aot.rs 8.76 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1
// Copyright 2017 The Australian National University
2
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
3
4
5
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
6
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
9
10
11
12
13
14
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

qinsoon's avatar
qinsoon committed
15
use linkutils::*;
qinsoon's avatar
qinsoon committed
16
17
use ast::ir::MuName;
use runtime;
qinsoon's avatar
qinsoon committed
18
use vm::VM;
qinsoon's avatar
qinsoon committed
19
use compiler::backend;
qinsoon's avatar
qinsoon committed
20

qinsoon's avatar
qinsoon committed
21
22
23
use std::path::PathBuf;
use std::process::Command;

qinsoon's avatar
qinsoon committed
24
25
/// links generated code for the given functions, static library of Zebu,
/// and a main function to produce an executable of the given name
26
pub fn link_primordial(funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
27
28
29
    let emit_dir = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);

    // prepare a list of files that need to be compiled and linked together
30
    let files: Vec<PathBuf> = {
qinsoon's avatar
qinsoon committed
31
32
33
34
35
36
37
38
39
40
41
42
43
        use std::fs;

        let mut ret = vec![];

        // all interested mu funcs
        for func in funcs {
            ret.push(get_path_for_mu_func(func, vm));
        }

        // mu context
        ret.push(get_path_for_mu_context(vm));

        // copy primoridal entry
44
        let source = get_path_under_zebu(runtime::PRIMORDIAL_ENTRY);
qinsoon's avatar
qinsoon committed
45
46
47
48
49
50
51
52
        let dest = {
            let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
            ret.push("main.c");

            ret
        };
        trace!("copying from {:?} to {:?}", source, dest);
        match fs::copy(source.as_path(), dest.as_path()) {
53
            Ok(_) => {}
54
            Err(e) => panic!("failed to copy: {}", e)
qinsoon's avatar
qinsoon committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
        }

        // include the primordial C main
        ret.push(dest);

        // include mu static lib
        ret.push(get_path_under_zebu(if cfg!(debug_assertions) {
            "target/debug/libmu.a"
        } else {
            "target/release/libmu.a"
        }));

        ret
    };

    let mut out_path = emit_dir.clone();
    out_path.push(out);

73
74
75
76
    link_executable_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
77
        out_path
78
    )
qinsoon's avatar
qinsoon committed
79
80
81
}

/// invokes the C compiler to link code into an executable
82
83
84
85
fn link_executable_internal(
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
86
    out: PathBuf
87
) -> PathBuf {
qinsoon's avatar
qinsoon committed
88
89
90
    info!("output as {:?}", out.as_path());

    let mut cc = Command::new(get_c_compiler());
qinsoon's avatar
qinsoon committed
91

92
93
94
95
96
97
    // external libs
    for path in libpath.iter() {
        cc.arg(format!("-L{}", path));
    }
    for l in lib.iter() {
        cc.arg(format!("-l{}", l));
qinsoon's avatar
qinsoon committed
98
99
    }

qinsoon's avatar
qinsoon committed
100
    // dylibs used for linux
qinsoon's avatar
qinsoon committed
101
    if cfg!(target_os = "linux") {
102
103
104
105
        cc.arg("-ldl");
        cc.arg("-lrt");
        cc.arg("-lm");
        cc.arg("-lpthread");
qinsoon's avatar
qinsoon committed
106
    }
107

qinsoon's avatar
qinsoon committed
108
    // all the source code
109
110
111
112
113
    for file in files {
        info!("link with {:?}", file.as_path());
        cc.arg(file.as_path());
    }

qinsoon's avatar
qinsoon committed
114
    // flag to allow find symbols in the executable
115
    cc.arg("-rdynamic");
qinsoon's avatar
qinsoon committed
116
117

    // specified output
118
119
    cc.arg("-o");
    cc.arg(out.as_os_str());
qinsoon's avatar
qinsoon committed
120

qinsoon's avatar
qinsoon committed
121
122
    // execute and check results
    assert!(exec_cmd(cc).status.success(), "failed to link code");
qinsoon's avatar
qinsoon committed
123
124
125
    out
}

qinsoon's avatar
qinsoon committed
126
127
/// links generated code for the given functions to produce a dynamic
/// library of the given name
128
pub fn link_dylib(funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
129
130
131
132
133
    link_dylib_with_extra_srcs(funcs, vec![], out, vm)
}

/// links generated code for the given functions with a few external sources
/// to produce a dynamic library of the given name
134
135
136
137
pub fn link_dylib_with_extra_srcs(
    funcs: Vec<MuName>,
    srcs: Vec<String>,
    out: &str,
138
    vm: &VM
139
) -> PathBuf {
qinsoon's avatar
qinsoon committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    let files = {
        let mut ret = vec![];

        for func in funcs {
            ret.push(get_path_for_mu_func(func, vm));
        }

        for src in srcs {
            ret.push(PathBuf::from(src));
        }

        ret.push(get_path_for_mu_context(vm));

        ret
    };

    let mut out_path = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
    out_path.push(out);

159
160
161
162
    link_dylib_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
163
        out_path
164
    )
qinsoon's avatar
qinsoon committed
165
166
167
}

/// invokes the C compiler to link code into a dynamic library
168
169
170
171
fn link_dylib_internal(
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
172
    out: PathBuf
173
174
) -> PathBuf {
    let mut object_files: Vec<PathBuf> = vec![];
qinsoon's avatar
qinsoon committed
175

qinsoon's avatar
qinsoon committed
176
    // compile each single source file
qinsoon's avatar
qinsoon committed
177
    for file in files {
qinsoon's avatar
qinsoon committed
178
        let mut cc = Command::new(get_c_compiler());
qinsoon's avatar
qinsoon committed
179

qinsoon's avatar
qinsoon committed
180
        // output object file
181
        cc.arg("-c");
qinsoon's avatar
qinsoon committed
182
        // position independent code
183
        cc.arg("-fPIC");
qinsoon's avatar
qinsoon committed
184
185
186
187

        let mut out = file.clone();
        out.set_extension("o");

188
189
190
        cc.arg(file.as_os_str());
        cc.arg("-o");
        cc.arg(out.as_os_str());
qinsoon's avatar
qinsoon committed
191
192

        object_files.push(out);
qinsoon's avatar
qinsoon committed
193
        exec_cmd(cc);
194
195
    }

qinsoon's avatar
qinsoon committed
196
197
    // link object files into a dynamic library
    let mut cc = Command::new(get_c_compiler());
198
199
200
201
202
203
204

    // external libs
    for path in libpath.iter() {
        cc.arg(format!("-L{}", path));
    }
    for l in lib.iter() {
        cc.arg(format!("-l{}", l));
qinsoon's avatar
qinsoon committed
205
206
    }

qinsoon's avatar
qinsoon committed
207
208
    // options:
    // shared library
209
    cc.arg("-shared");
qinsoon's avatar
qinsoon committed
210
    // position independent code
211
    cc.arg("-fPIC");
qinsoon's avatar
qinsoon committed
212
    // allow undefined symbol
213
214
    cc.arg("-Wl");
    cc.arg("-undefined");
qinsoon's avatar
qinsoon committed
215
    // allow dynamic lookup symbols
216
    cc.arg("dynamic_lookup");
qinsoon's avatar
qinsoon committed
217
218

    // all object files
qinsoon's avatar
qinsoon committed
219
    for obj in object_files {
220
        cc.arg(obj.as_os_str());
qinsoon's avatar
qinsoon committed
221
    }
222

qinsoon's avatar
qinsoon committed
223
    // output
224
225
    cc.arg("-o");
    cc.arg(out.as_os_str());
qinsoon's avatar
qinsoon committed
226

qinsoon's avatar
qinsoon committed
227
    exec_cmd(cc);
qinsoon's avatar
qinsoon committed
228
229
230
231

    out
}

qinsoon's avatar
qinsoon committed
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/// builds a bundle (that contains a function), compiles it,
/// links and loads it as a dynamic library
/// This function is used to test compiler.
//  TODO: should think about using make_boot_image() instead of this adhoc code (Issue #52)
pub fn compile_fnc<'a>(fnc_name: &'static str, build_fnc: &'a Fn() -> VM) -> ll::Library {
    VM::start_logging_trace();

    let vm = Arc::new(build_fnc());
    let compiler = Compiler::new(CompilerPolicy::default(), &vm);
    let func_id = vm.id_of(fnc_name);
    {
        let funcs = vm.funcs().read().unwrap();
        let func = match funcs.get(&func_id) {
            Some(func) => func.read().unwrap(),
246
            None => panic!("cannot find function {}", fnc_name)
qinsoon's avatar
qinsoon committed
247
248
249
250
        };

        let cur_ver = match func.cur_ver {
            Some(v) => v,
251
252
253
254
255
256
            None => {
                panic!(
                    "function {} does not have a defined current version",
                    fnc_name
                )
            }
qinsoon's avatar
qinsoon committed
257
258
259
260
261
        };

        let func_vers = vm.func_vers().read().unwrap();
        let mut func_ver = match func_vers.get(&cur_ver) {
            Some(fv) => fv.write().unwrap(),
262
            None => panic!("cannot find function version {}", cur_ver)
qinsoon's avatar
qinsoon committed
263
264
265
266
267
268
269
270
271
272
273
274
275
        };
        compiler.compile(&mut func_ver);
    }
    backend::emit_context(&vm);
    let libname = &get_dylib_name(fnc_name);
    let dylib = aot::link_dylib(vec![Mu(fnc_name)], libname, &vm);
    ll::Library::new(dylib.as_os_str()).unwrap()
}

/// builds a bundle (that contains several functions), compiles them,
/// links and loads it as a dynamic library
/// This function is used to test compiler.
//  TODO: should think about using make_boot_image() instead of this adhoc code (Issue #52)
276
277
278
pub fn compile_fncs<'a>(
    entry: &'static str,
    fnc_names: Vec<&'static str>,
279
    build_fnc: &'a Fn() -> VM
280
) -> ll::Library {
qinsoon's avatar
qinsoon committed
281
282
283
284
285
286
287
288
289
290
    VM::start_logging_trace();

    let vm = Arc::new(build_fnc());
    let compiler = Compiler::new(CompilerPolicy::default(), &vm);

    for func in fnc_names.iter() {
        let func_id = vm.id_of(func);
        let funcs = vm.funcs().read().unwrap();
        let func = funcs.get(&func_id).unwrap().read().unwrap();
        let func_vers = vm.func_vers().read().unwrap();
291
292
293
294
295
        let mut func_ver = func_vers
            .get(&func.cur_ver.unwrap())
            .unwrap()
            .write()
            .unwrap();
qinsoon's avatar
qinsoon committed
296
297
298
299
300
301
302
303
304
305
306
        compiler.compile(&mut func_ver);
    }

    backend::emit_context(&vm);

    let libname = &get_dylib_name(entry);
    let dylib = aot::link_dylib(fnc_names.iter().map(|x| Mu(x)).collect(), libname, &vm);
    ll::Library::new(dylib.as_os_str()).unwrap()
}

/// gets the path for the generated code of a Mu function
307
fn get_path_for_mu_func(f: MuName, vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
308
    let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
309
    ret.push(f + ".S");
qinsoon's avatar
qinsoon committed
310
311
312
313

    ret
}

qinsoon's avatar
qinsoon committed
314
/// gets the path for generated Mu context (persisted VM/heap)
315
fn get_path_for_mu_context(vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
316
    let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
qinsoon's avatar
qinsoon committed
317
318
    ret.push(backend::AOT_EMIT_CONTEXT_FILE);
    ret
319
}