GitLab will be upgraded on 30 Jan 2023 from 2.00 pm (AEDT) to 3.00 pm (AEDT). During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to us at N110 (b) CSIT building.

aot.rs 9.06 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");
106
        cc.arg("-lz");
107
108
109
110
111
112
113
114
115
116
    } else if cfg!(target_os = "macos") {
        cc.arg("-liconv");
        cc.arg("-framework");
        cc.arg("Security");
        cc.arg("-framework");
        cc.arg("CoreFoundation");
        cc.arg("-lz");
        cc.arg("-lSystem");
        cc.arg("-lc");
        cc.arg("-lm");
qinsoon's avatar
qinsoon committed
117
    }
118

qinsoon's avatar
qinsoon committed
119
    // all the source code
120
121
122
123
124
    for file in files {
        info!("link with {:?}", file.as_path());
        cc.arg(file.as_path());
    }

qinsoon's avatar
qinsoon committed
125
    // flag to allow find symbols in the executable
126
    cc.arg("-rdynamic");
qinsoon's avatar
qinsoon committed
127
128

    // specified output
129
130
    cc.arg("-o");
    cc.arg(out.as_os_str());
qinsoon's avatar
qinsoon committed
131

qinsoon's avatar
qinsoon committed
132
133
    // execute and check results
    assert!(exec_cmd(cc).status.success(), "failed to link code");
qinsoon's avatar
qinsoon committed
134
135
136
    out
}

qinsoon's avatar
qinsoon committed
137
138
/// links generated code for the given functions to produce a dynamic
/// library of the given name
139
pub fn link_dylib(funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
140
141
142
143
144
    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
145
146
147
148
pub fn link_dylib_with_extra_srcs(
    funcs: Vec<MuName>,
    srcs: Vec<String>,
    out: &str,
149
    vm: &VM
150
) -> PathBuf {
qinsoon's avatar
qinsoon committed
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
    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);

170
171
172
173
    link_dylib_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
174
        out_path
175
    )
qinsoon's avatar
qinsoon committed
176
177
178
}

/// invokes the C compiler to link code into a dynamic library
179
180
181
182
fn link_dylib_internal(
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
183
    out: PathBuf
184
185
) -> PathBuf {
    let mut object_files: Vec<PathBuf> = vec![];
qinsoon's avatar
qinsoon committed
186

qinsoon's avatar
qinsoon committed
187
    // compile each single source file
qinsoon's avatar
qinsoon committed
188
    for file in files {
qinsoon's avatar
qinsoon committed
189
        let mut cc = Command::new(get_c_compiler());
qinsoon's avatar
qinsoon committed
190

qinsoon's avatar
qinsoon committed
191
        // output object file
192
        cc.arg("-c");
qinsoon's avatar
qinsoon committed
193
        // position independent code
194
        cc.arg("-fPIC");
qinsoon's avatar
qinsoon committed
195
196
197
198

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

199
200
201
        cc.arg(file.as_os_str());
        cc.arg("-o");
        cc.arg(out.as_os_str());
qinsoon's avatar
qinsoon committed
202
203

        object_files.push(out);
qinsoon's avatar
qinsoon committed
204
        exec_cmd(cc);
205
206
    }

qinsoon's avatar
qinsoon committed
207
208
    // link object files into a dynamic library
    let mut cc = Command::new(get_c_compiler());
209
210
211
212
213
214
215

    // 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
216
217
    }

qinsoon's avatar
qinsoon committed
218
219
    // options:
    // shared library
220
    cc.arg("-shared");
qinsoon's avatar
qinsoon committed
221
    // position independent code
222
    cc.arg("-fPIC");
qinsoon's avatar
qinsoon committed
223
    // allow undefined symbol
224
225
    cc.arg("-Wl");
    cc.arg("-undefined");
qinsoon's avatar
qinsoon committed
226
    // allow dynamic lookup symbols
227
    cc.arg("dynamic_lookup");
qinsoon's avatar
qinsoon committed
228
229

    // all object files
qinsoon's avatar
qinsoon committed
230
    for obj in object_files {
231
        cc.arg(obj.as_os_str());
qinsoon's avatar
qinsoon committed
232
    }
233

qinsoon's avatar
qinsoon committed
234
    // output
235
236
    cc.arg("-o");
    cc.arg(out.as_os_str());
qinsoon's avatar
qinsoon committed
237

qinsoon's avatar
qinsoon committed
238
    exec_cmd(cc);
qinsoon's avatar
qinsoon committed
239
240
241
242

    out
}

qinsoon's avatar
qinsoon committed
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/// 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(),
257
            None => panic!("cannot find function {}", fnc_name)
qinsoon's avatar
qinsoon committed
258
259
260
261
        };

        let cur_ver = match func.cur_ver {
            Some(v) => v,
262
263
264
265
266
267
            None => {
                panic!(
                    "function {} does not have a defined current version",
                    fnc_name
                )
            }
qinsoon's avatar
qinsoon committed
268
269
270
271
272
        };

        let func_vers = vm.func_vers().read().unwrap();
        let mut func_ver = match func_vers.get(&cur_ver) {
            Some(fv) => fv.write().unwrap(),
273
            None => panic!("cannot find function version {}", cur_ver)
qinsoon's avatar
qinsoon committed
274
275
276
277
278
279
280
281
282
283
284
285
286
        };
        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)
287
288
289
pub fn compile_fncs<'a>(
    entry: &'static str,
    fnc_names: Vec<&'static str>,
290
    build_fnc: &'a Fn() -> VM
291
) -> ll::Library {
qinsoon's avatar
qinsoon committed
292
293
294
295
296
297
298
299
300
301
    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();
302
303
304
305
306
        let mut func_ver = func_vers
            .get(&func.cur_ver.unwrap())
            .unwrap()
            .write()
            .unwrap();
qinsoon's avatar
qinsoon committed
307
308
309
310
311
312
313
314
315
316
317
        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
318
fn get_path_for_mu_func(f: MuName, vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
319
    let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
320
    ret.push(f + ".S");
qinsoon's avatar
qinsoon committed
321
322
323
324

    ret
}

qinsoon's avatar
qinsoon committed
325
/// gets the path for generated Mu context (persisted VM/heap)
326
fn get_path_for_mu_context(vm: &VM) -> PathBuf {
qinsoon's avatar
qinsoon committed
327
    let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
qinsoon's avatar
qinsoon committed
328
329
    ret.push(backend::AOT_EMIT_CONTEXT_FILE);
    ret
330
}