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 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(_) => {}
qinsoon's avatar
qinsoon committed
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
            Err(e) => panic!("failed to copy: {}", e)
        }

        // 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
77
78
    link_executable_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
        out_path
    )
qinsoon's avatar
qinsoon committed
79
80
81
}

/// invokes the C compiler to link code into an executable
82
83
84
85
86
87
fn link_executable_internal(
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
    out: PathBuf
) -> 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
138
139
pub fn link_dylib_with_extra_srcs(
    funcs: Vec<MuName>,
    srcs: Vec<String>,
    out: &str,
    vm: &VM
) -> 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
163
164
    link_dylib_internal(
        files,
        &vm.vm_options.flag_bootimage_external_lib,
        &vm.vm_options.flag_bootimage_external_libpath,
        out_path
    )
qinsoon's avatar
qinsoon committed
165
166
167
}

/// invokes the C compiler to link code into a dynamic library
168
169
170
171
172
173
174
fn link_dylib_internal(
    files: Vec<PathBuf>,
    lib: &Vec<String>,
    libpath: &Vec<String>,
    out: PathBuf
) -> 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
246
247
248
249
250
/// 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(),
            None => panic!("cannot find function {}", fnc_name)
        };

        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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
        };

        let func_vers = vm.func_vers().read().unwrap();
        let mut func_ver = match func_vers.get(&cur_ver) {
            Some(fv) => fv.write().unwrap(),
            None => panic!("cannot find function version {}", cur_ver)
        };
        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
279
280
pub fn compile_fncs<'a>(
    entry: &'static str,
    fnc_names: Vec<&'static str>,
    build_fnc: &'a Fn() -> VM
) -> 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
}