WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

Commit c9b13533 authored by qinsoon's avatar qinsoon
Browse files

linkutils (previously as testutils)

parent ca0d77bf
......@@ -37,4 +37,4 @@ pub extern crate gc;
pub mod vm;
pub mod compiler;
pub mod runtime;
pub mod testutil;
\ No newline at end of file
pub mod linkutils;
\ No newline at end of file
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use testutil::*;
use linkutils::*;
use ast::ir::MuName;
use runtime;
use vm::VM;
......@@ -20,10 +20,67 @@ use compiler::backend;
use std::path::PathBuf;
use std::process::Command;
use std::process::Output;
/// links generated code for the given functions, static library of Zebu,
/// and a main function to produce an executable of the given name
pub fn link_primordial (funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
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
let files : Vec<PathBuf> = {
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
let source = get_path_under_zebu(runtime::PRIMORDIAL_ENTRY);
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()) {
Ok(_) => {},
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);
link_executable_internal(files,
&vm.vm_options.flag_bootimage_external_lib,
&vm.vm_options.flag_bootimage_external_libpath,
out_path)
}
/// invokes the C compiler to link code into an executable
fn link_executable_internal (files: Vec<PathBuf>, lib: &Vec<String>, libpath: &Vec<String>, out: PathBuf) -> PathBuf {
let mut cc = Command::new(get_test_clang_path());
info!("output as {:?}", out.as_path());
let mut cc = Command::new(get_c_compiler());
// external libs
for path in libpath.iter() {
......@@ -33,7 +90,7 @@ fn link_executable_internal (files: Vec<PathBuf>, lib: &Vec<String>, libpath: &V
cc.arg(format!("-l{}", l));
}
info!("output as {:?}", out.as_path());
// dylibs used for linux
if cfg!(target_os = "linux") {
cc.arg("-ldl");
cc.arg("-lrt");
......@@ -41,29 +98,69 @@ fn link_executable_internal (files: Vec<PathBuf>, lib: &Vec<String>, libpath: &V
cc.arg("-lpthread");
}
// This needs to be at the bottom due to the linkage of librodall_alloc.a
// all the source code
for file in files {
info!("link with {:?}", file.as_path());
cc.arg(file.as_path());
}
// so we can find symbols in itself
// flag to allow find symbols in the executable
cc.arg("-rdynamic");
// specified output
cc.arg("-o");
cc.arg(out.as_os_str());
assert!(exec(cc).status.success());
// execute and check results
assert!(exec_cmd(cc).status.success(), "failed to link code");
out
}
/// links generated code for the given functions to produce a dynamic
/// library of the given name
pub fn link_dylib (funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
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
pub fn link_dylib_with_extra_srcs(funcs: Vec<MuName>, srcs: Vec<String>, out: &str, vm: &VM) -> PathBuf{
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);
link_dylib_internal(files,
&vm.vm_options.flag_bootimage_external_lib,
&vm.vm_options.flag_bootimage_external_libpath,
out_path)
}
/// invokes the C compiler to link code into a dynamic library
fn link_dylib_internal (files: Vec<PathBuf>, lib: &Vec<String>, libpath: &Vec<String>, out: PathBuf) -> PathBuf {
let mut object_files : Vec<PathBuf> = vec![];
// compile each single source file
for file in files {
let mut cc = Command::new(get_test_clang_path());
let mut cc = Command::new(get_c_compiler());
// output object file
cc.arg("-c");
// position independent code
cc.arg("-fPIC");
let mut out = file.clone();
......@@ -74,10 +171,11 @@ fn link_dylib_internal (files: Vec<PathBuf>, lib: &Vec<String>, libpath: &Vec<St
cc.arg(out.as_os_str());
object_files.push(out);
exec(cc);
exec_cmd(cc);
}
let mut cc = Command::new(get_test_clang_path());
// link object files into a dynamic library
let mut cc = Command::new(get_c_compiler());
// external libs
for path in libpath.iter() {
......@@ -87,11 +185,15 @@ fn link_dylib_internal (files: Vec<PathBuf>, lib: &Vec<String>, libpath: &Vec<St
cc.arg(format!("-l{}", l));
}
// options
// options:
// shared library
cc.arg("-shared");
// position independent code
cc.arg("-fPIC");
// allow undefined symbol
cc.arg("-Wl");
cc.arg("-undefined");
// allow dynamic lookup symbols
cc.arg("dynamic_lookup");
// all object files
......@@ -103,11 +205,73 @@ fn link_dylib_internal (files: Vec<PathBuf>, lib: &Vec<String>, libpath: &Vec<St
cc.arg("-o");
cc.arg(out.as_os_str());
exec(cc);
exec_cmd(cc);
out
}
/// 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,
None => panic!("function {} does not have a defined current version", fnc_name)
};
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)
pub fn compile_fncs<'a>(entry: &'static str, fnc_names: Vec<&'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);
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();
let mut func_ver = func_vers.get(&func.cur_ver.unwrap()).unwrap().write().unwrap();
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
fn get_path_for_mu_func (f: MuName, vm: &VM) -> PathBuf {
let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
ret.push(f);
......@@ -116,126 +280,9 @@ fn get_path_for_mu_func (f: MuName, vm: &VM) -> PathBuf {
ret
}
/// gets the path for generated Mu context (persisted VM/heap)
fn get_path_for_mu_context (vm: &VM) -> PathBuf {
let mut ret = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
ret.push(backend::AOT_EMIT_CONTEXT_FILE);
ret
}
pub fn link_primordial (funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
let emit_dir = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
let files : Vec<PathBuf> = {
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
let source = get_path_under_mu(runtime::PRIMORDIAL_ENTRY);
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()) {
Ok(_) => {},
Err(e) => panic!("failed to copy: {}", e)
}
// include the primordial C main
ret.push(dest);
// include mu static lib
ret.push(get_path_under_mu(if cfg!(debug_assertions) {
"target/debug/libmu.a"
} else {
"target/release/libmu.a"
}));
/*// include rodal alloc static lib (it overrides free and realloc so it should be the last thing linked)
ret.push(get_path_under_mu(if cfg!(debug_assertions) {
"target/debug/deps/librodal_alloc.a"
} else {
"target/release/deps/librodal_alloc.a"
}));*/
ret
};
let mut out_path = emit_dir.clone();
out_path.push(out);
link_executable_internal(files,
&vm.vm_options.flag_bootimage_external_lib,
&vm.vm_options.flag_bootimage_external_libpath,
out_path)
}
pub fn execute(executable: PathBuf) -> Output {
let run = Command::new(executable.as_os_str());
exec(run)
}
pub fn execute_nocheck(executable: PathBuf) -> Output {
let run = Command::new(executable.as_os_str());
exec_nocheck(run)
}
pub fn link_dylib (funcs: Vec<MuName>, out: &str, vm: &VM) -> PathBuf {
let files = {
let mut ret = vec![];
for func in funcs {
ret.push(get_path_for_mu_func(func, vm));
}
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);
link_dylib_internal(files,
&vm.vm_options.flag_bootimage_external_lib,
&vm.vm_options.flag_bootimage_external_libpath,
out_path)
}
pub fn link_dylib_with_extra_srcs(funcs: Vec<MuName>, srcs: Vec<String>, out: &str, vm: &VM) -> PathBuf{
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);
link_dylib_internal(files,
&vm.vm_options.flag_bootimage_external_lib,
&vm.vm_options.flag_bootimage_external_libpath,
out_path)
}
}
\ No newline at end of file
......@@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! This module contains utility functions for linking generated code and
//! running tests for Zebu.
extern crate libloading as ll;
use compiler::*;
use ast::ir::*;
use vm::*;
use std::sync::Arc;
use std::path::PathBuf;
......@@ -24,9 +26,13 @@ use std::process::Command;
use std::process::Output;
use std::os::unix::process::ExitStatusExt;
/// linking utilities for ahead-of-time compilation
pub mod aot;
pub fn get_test_clang_path() -> String {
/// gets a C compiler (for assembling and linking generated assembly code)
/// This function will check CC environment variable and return it.
/// Otherwise, it returns the default C compiler (clang).
fn get_c_compiler() -> String {
use std::env;
match env::var("CC") {
......@@ -35,15 +41,45 @@ pub fn get_test_clang_path() -> String {
}
}
pub fn exec (cmd: Command) -> Output {
let output = exec_nocheck(cmd);
/// concatenates the given path related to Zebu root path
/// This function will check MU_ZEBU environment variable and use it as Zebu root path.
/// Otherwise, it uses the current directory as Zebu root path.
fn get_path_under_zebu(str: &'static str) -> PathBuf {
use std::env;
assert!(output.status.success());
match env::var("MU_ZEBU") {
Ok(v) => {
let mut ret = PathBuf::from(v);
ret.push(str);
ret
}
Err(_) => PathBuf::from(str)
}
}
/// executes the executable of the given path, checks its exit status
/// panics if this executable does not finish normally
pub fn exec_path(executable: PathBuf) -> Output {
let run = Command::new(executable.as_os_str());
exec_cmd(run)
}
/// executes the executable of the given path, does not check exit status
pub fn exec_path_nocheck(executable: PathBuf) -> Output {
let run = Command::new(executable.as_os_str());
exec_cmd_nocheck(run)
}
/// executes the given command, checks its exit status,
/// panics if this command does not finish normally
fn exec_cmd(cmd: Command) -> Output {
let output = exec_cmd_nocheck(cmd);
assert!(output.status.success());
output
}
pub fn exec_nocheck (mut cmd: Command) -> Output {
/// executes the given command, does not check exit status
fn exec_cmd_nocheck(mut cmd: Command) -> Output {
info!("executing: {:?}", cmd);
let output = match cmd.output() {
Ok(res) => res,
......@@ -62,79 +98,14 @@ pub fn exec_nocheck (mut cmd: Command) -> Output {
output
}
pub fn get_path_under_mu(str: &'static str) -> PathBuf {
use std::env;
match env::var("MU_ZEBU") {
Ok(v) => {
let mut ret = PathBuf::from(v);
ret.push(str);
ret
}
Err(_) => PathBuf::from(str)
}
}
/// returns a name for dynamic library
#[cfg(target_os = "macos")]
pub fn get_dylib_name(name: &'static str) -> String {
format!("lib{}.dylib", name)
}
/// returns a name for dynamic library
#[cfg(target_os = "linux")]
pub fn get_dylib_name(name: &'static str) -> String {
format!("lib{}.so", name)
}
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,
None => panic!("function {} does not have a defined current version", fnc_name)
};
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()
}
pub fn compile_fncs<'a>(entry: &'static str, fnc_names: Vec<&'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);
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();
let mut func_ver = func_vers.get(&func.cur_ver.unwrap()).unwrap().write().unwrap();
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()
}
}
\ No newline at end of file
......@@ -92,7 +92,7 @@ impl MuVM {
extern crate libloading as ll;
use compiler::*;
use testutil::aot;
use linkutils::aot;
let funcs : Vec<MuID> = {
let funcs = self.vm.funcs().read().unwrap();
......
......@@ -978,7 +978,7 @@ impl <'a> VM {
/// has dylib extension, otherwise generates an executable)
#[cfg(feature = "aot")]
fn link_boot_image(&self, funcs: Vec<MuID>, extra_srcs: Vec<String>, output_file: String) {
use testutil;
use linkutils;
info!("Linking boot image...");
......@@ -993,13 +993,13 @@ impl <'a> VM {
if output_file.ends_with("dylib") || output_file.ends_with("so") {
// compile as dynamic library
testutil::aot::link_dylib_with_extra_srcs(func_names, extra_srcs, &output_file, self);
linkutils::aot::link_dylib_with_extra_srcs(func_names, extra_srcs, &output_file, self);
} else {
if extra_srcs.len() != 0 {
panic!("trying to create an executable with linking extern sources, unimplemented");
}
// compile as executable
testutil::aot::link_primordial(func_names, &output_file, self);
linkutils::aot::link_primordial(func_names, &output_file, self);
}
trace!("Done!");
......
......@@ -26,8 +26,8 @@ use self::mu::utils::Address;
use self::mu::utils::LinkedHashMap;
use std::sync::Arc;
use self::mu::testutil;
use self::mu::testutil::aot;
use self::mu::linkutils;
use self::mu::linkutils::aot;
#[test]
fn test_allocation_fastpath() {
......@@ -51,7 +51,7 @@ fn test_allocation_fastpath() {
backend::emit_context(&vm);