Commit 9358449f authored by Isaac's avatar Isaac

You can now use runtime features in shared library boot images

parent d85c082d
......@@ -131,6 +131,11 @@ test_muc:binop:
script:
- pytest tests/test_muc/test_binop.py -v --color=yes
test_extra:tests_library_runtime:
stage: test
script:
- cd tests/test_library_runtime; ./test.sh
test_jit:milestones:
stage: test
script:
......
......@@ -746,27 +746,31 @@ impl fmt::Display for MuType_ {
}
}
pub type StructTagMap = Option<AtomicPtr<RwLock<HashMap<StructTag, StructType_>>>>;
pub static mut STRUCT_TAG_MAP_LOC: StructTagMap = None;
pub type StructTagMap = AtomicPtr<RwLock<HashMap<StructTag, StructType_>>>;
pub static mut STRUCT_TAG_MAP_LOC: StructTagMap = AtomicPtr::new(ptr::null_mut());
pub type HybridTagMap = Option<AtomicPtr<RwLock<HashMap<HybridTag, HybridType_>>>>;
pub static mut HYBRID_TAG_MAP_LOC: HybridTagMap = None;
pub type HybridTagMap = AtomicPtr<RwLock<HashMap<HybridTag, HybridType_>>>;
pub static mut HYBRID_TAG_MAP_LOC: HybridTagMap = AtomicPtr::new(ptr::null_mut());
lazy_static! {
/// storing a map from MuName to StructType_
pub static ref STRUCT_TAG_MAP : RwLock<HashMap<StructTag, StructType_>> =
unsafe {
match &STRUCT_TAG_MAP_LOC {
&Some(ref ptr) => ptr::read(ptr.load(Ordering::Relaxed)),
&None => RwLock::new(HashMap::new())
let ptr = STRUCT_TAG_MAP_LOC.load(Ordering::Relaxed);
if ptr.is_null() {
RwLock::new(HashMap::new())
} else {
ptr.read()
}
};
/// storing a map from MuName to HybridType_
pub static ref HYBRID_TAG_MAP : RwLock<HashMap<HybridTag, HybridType_>> =
unsafe {
match &HYBRID_TAG_MAP_LOC {
&Some(ref ptr) => ptr::read(ptr.load(Ordering::Relaxed)),
&None => RwLock::new(HashMap::new())
let ptr = HYBRID_TAG_MAP_LOC.load(Ordering::Relaxed);
if ptr.is_null() {
RwLock::new(HashMap::new())
} else {
ptr.read()
}
};
}
......
......@@ -322,6 +322,14 @@ fn link_dylib_internal(
// allow dynamic lookup symbols
cc.arg("dynamic_lookup");
cc.arg(format!("-L{}",
get_path_under_zebu(if cfg!(debug_assertions) {
"target/debug/deps/"
} else {
"target/release/deps/"
}).to_str().unwrap()));
cc.arg("-lmu");
// all object files
for obj in object_files {
cc.arg(obj.as_os_str());
......@@ -375,7 +383,10 @@ pub fn compile_fnc<'a>(
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()
// ll::Library dosn't load using the RTLD_GLOBAL flag, which we need for dlsym calls to work
// So I'm using this hack to fix this (the transmute is needed because ll::Library has a
// private constructor).
unsafe { std::mem::transmute(ll::os::unix::Library::open(Some(dylib.as_os_str()), libc::RTLD_NOW | libc::RTLD_GLOBAL).unwrap()) }
}
/// builds a bundle (that contains several functions), compiles them,
......
......@@ -121,10 +121,9 @@ pub fn resolve_symbol(symbol: MuName) -> Address {
pub fn resolve_symbol_unmangled(symbol: MuName) -> Address {
use std::ptr;
let c_symbol = CString::new((*symbol).clone()).unwrap();
let c_symbol = CString::new(mangle_name(symbol.clone())).unwrap();
let rtld_default = unsafe { dlopen(ptr::null(), 0) };
let ret = unsafe { dlsym(rtld_default, c_symbol.as_ptr()) };
let ret = unsafe { dlsym(libc::RTLD_DEFAULT, c_symbol.as_ptr()) };
let error = unsafe { dlerror() };
if !error.is_null() {
......@@ -295,6 +294,10 @@ pub static mut LAST_TIME: c_ulong = 0;
// RTMU mu_main needs changes
fn as_addr<T>(a: &T)->rodal::Address {
unsafe { std::mem::transmute::<&T, *const rodal::Address>(a).read() }}
/// a function to resume a mu VM dumped as a shared library boot image, this function will be called
/// from C, and returns a MuVM* that works with the C API
#[no_mangle]
......@@ -303,6 +306,7 @@ pub extern "C" fn mu_resume(
edata: *const (),
dumped_vm: *mut Arc<VM>,
) -> *mut CMuVM {
trace!("mu_resume: {}, {}, {}, {}", as_addr(&stm), as_addr(&htm), as_addr(&edata), as_addr(&dumped_vm));
// resume the VM and wrap it into a C API object
to_CMuVM(resume_runtime(stm, htm, edata, dumped_vm))
}
......
......@@ -27,6 +27,7 @@ extern void* STRUCT_TAG_MAP;
extern void* HYBRID_TAG_MAP;
void* resume_mu() { // Returns a MuVM* (see muapi.h)
fprintf(stderr, "resume_mu: %p, %p, %p, %p\n", &STRUCT_TAG_MAP, &HYBRID_TAG_MAP, &RODAL_END, &vm);
return mu_resume(&STRUCT_TAG_MAP, &HYBRID_TAG_MAP, &RODAL_END, &vm);
}
......
#!/bin/bash
{
echo -e '#include "test_common.c"'
$MUC -c test_runtime.uir emit/test_runtime.so | head -n -1;
echo -e '\n\tload_test(); run_test(mvm);\n}'
} > test_build.c
clang -L$MU_ZEBU/target/$ZEBU_BUILD/deps -lmu -ldl test_build.c -o test_build
clang -L$MU_ZEBU/target/$ZEBU_BUILD/deps -lmu -ldl test_load.c -o test_load
echo "Testing in same process"
./test_build
echo "testing in diferent process"
./test_load
......@@ -8,24 +8,24 @@
static void* lib = NULL;
void load_lib() {
void *lib = dlopen("./emit/test_mu_new.so", RTLD_GLOBAL);
void load_test() {
void *lib = dlopen("./emit/test_runtime.so", RTLD_NOW | RTLD_GLOBAL);
if (lib == NULL) {
puts(dlerror());
}
assert(lib != NULL);
}
void run_current(MuVM* vm) {
void run_test(MuVM* vm) {
vm->current_thread_as_mu_thread(vm, NULL);
uint64_t (*test_mu_new)(uint64_t) = dlsym(lib, "test_mu_new");
assert(test_mu_new != NULL);
assert(test_mu_new(3) == 3);
uint64_t (*test_runtime)(uint64_t) = dlsym(lib, "test_runtime");
assert(test_runtime != NULL);
assert(test_runtime(3) == 3);
}
void run_resume() {
void resume_test() {
MuVM* (*resume_mu)() = dlsym(lib, "resume_mu");
assert(resume_mu != NULL);
run_current(resume_mu());
run_test(resume_mu());
}
#include "test_common.c"
int main() {
load_test();
resume_test();
}
.typedef i64 = int<64>
.funcdef trivial_thread<()->()>
{
entry():
COMMINST uvm.thread_exit()
}
.funcdef throw_back<(ref<i64>)->()>
{
entry(<ref<i64>> arg):
THROW arg
}
// Should simply return it's argument
.funcdef test_runtime<(i64)->(i64)>
{
entry(<i64> arg):
// Test thread spawning
stack = COMMINST uvm.new_stack<[()->()]>(trivial_thread)
thread = NEWTHREAD stack PASS_VALUES<>()
// Test NEW
obj = NEW <i64>
iobj = GETIREF <i64> obj
STORE <i64> iobj arg
// Test exception handling
CALL<(ref<i64>)->()> throw_back(obj) EXC(no_exception() handler())
handler()[exception]:
// Load the value we just threw back
e = REFCAST <ref<void> ref<int<64>>> exception
evi = GETIREF <int<64>> e
ev = LOAD <int<64>> evi
RET ev
no_exception(): // should be unreachable
RET <i64>0
}
# Copyright 2017 The Australian National University
#
# 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
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
from util import execute, compile_bundle, load_bundle, get_function;
import pytest;
import ctypes;
def test_new():
lib = load_bundle(
"""
.typedef i64 = int<64>
.funcdef test_new<(i64)->(i64)>
{
entry(<i64> arg):
obj = NEW <i64>
iobj = GETIREF <i64> obj
STORE <i64> iobj arg
res = LOAD <i64> iobj
RET res
}
""", "test_new");
get_function(lib.init, [], None)() # call init function
func = get_function(lib.test_new, [ctypes.c_int64], ctypes.c_int64)
assert(func(10) == 10)
def test_new_future():
lib = load_bundle(
"""
.typedef i64 = int<64>
.global future <i64>
.funcdef test_new_thread<(i64)->()>
{
entry(<i64> arg):
obj = NEW <i64>
iobj = GETIREF <i64> obj
STORE <i64> iobj arg
res = LOAD <i64> iobj
STORE SEQ_CST <i64> future res // store result into future
COMMINST uvm.thread_exit()
}
.funcdef test_new <(i64)->(i64)>
{
entry(<i64>arg):
STORE SEQ_CST <i64> future <i64>0 // store value indicating future is not read
s = COMMINST uvm.new_stack<[(i64)->()]>(test_new_thread)
t = NEWTHREAD s PASS_VALUES<i64>(arg)
BRANCH wait1()
wait1():
future_val = LOAD SEQ_CST <i64> future
test_result = NE <i64> future_val <i64>0
BRANCH2 test_result done(future_val) wait2()
wait2(): // this trivial block is a workaround for a bug in Zebu
BRANCH wait1()
done(<i64> res):
RET res
}
""", "test_new_future");
get_function(lib.init, [], None)() # call init function
func = get_function(lib.test_new, [ctypes.c_int64], ctypes.c_int64)
assert(func(10) == 10)
#include "test_lib.h"
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include "muapi.h"
#include "mu-fastimpl.h"
#define G(id, ...) global_ ## id
#define L(id, ...) local_ ## id
int main() {
MuVM* mvm = mu_fastimpl_new_with_opts("init_mu --aot-emit-dir=emit");
MuCtx* ctx = mvm->new_context(mvm);
MuIRBuilder* irbuilder;
MuID G(0, i64);
MuID G(1, test_mu_new);
MuID G(2);
{
irbuilder = ctx->new_ir_builder(ctx);
G(0, i64) = irbuilder->gen_sym(irbuilder, "@i64");
irbuilder->new_type_int(irbuilder, G(0, i64), 64);
G(1, test_mu_new) = irbuilder->gen_sym(irbuilder, "@test_mu_new");
G(2) = irbuilder->gen_sym(irbuilder, NULL);
irbuilder->new_funcsig(irbuilder, G(2), (MuTypeNode[]){G(0, i64)}, 1, (MuTypeNode[]){G(0, i64)}, 1);
irbuilder->new_func(irbuilder, G(1, test_mu_new), G(2));
MuID L(0) = irbuilder->gen_sym(irbuilder, NULL);
MuID L(1, test_mu_new.__3.entry) = irbuilder->gen_sym(irbuilder, "@test_mu_new.__3.entry");
MuID L(2, test_mu_new.__3.entry.arg) = irbuilder->gen_sym(irbuilder, "@test_mu_new.__3.entry.arg");
MuID L(3) = irbuilder->gen_sym(irbuilder, NULL);
MuID L(4, test_mu_new.__3.entry.obj) = irbuilder->gen_sym(irbuilder, "@test_mu_new.__3.entry.obj");
irbuilder->new_new(irbuilder, L(3), L(4, test_mu_new.__3.entry.obj), G(0, i64), MU_NO_ID);
MuID L(5) = irbuilder->gen_sym(irbuilder, NULL);
MuID L(6, test_mu_new.__3.entry.iobj) = irbuilder->gen_sym(irbuilder, "@test_mu_new.__3.entry.iobj");
irbuilder->new_getiref(irbuilder, L(5), L(6, test_mu_new.__3.entry.iobj), G(0, i64), L(4, test_mu_new.__3.entry.obj));
MuID L(7) = irbuilder->gen_sym(irbuilder, NULL);
irbuilder->new_store(irbuilder, L(7), 0, MU_ORD_NOT_ATOMIC, G(0, i64), L(6, test_mu_new.__3.entry.iobj), L(2, test_mu_new.__3.entry.arg), MU_NO_ID);
MuID L(8) = irbuilder->gen_sym(irbuilder, NULL);
MuID L(9, test_mu_new.__3.entry.res) = irbuilder->gen_sym(irbuilder, "@test_mu_new.__3.entry.res");
irbuilder->new_load(irbuilder, L(8), L(9, test_mu_new.__3.entry.res), 0, MU_ORD_NOT_ATOMIC, G(0, i64), L(6, test_mu_new.__3.entry.iobj), MU_NO_ID);
MuID L(10) = irbuilder->gen_sym(irbuilder, NULL);
irbuilder->new_ret(irbuilder, L(10), (MuVarNode[]){L(9, test_mu_new.__3.entry.res)}, 1);
irbuilder->new_bb(irbuilder, L(1, test_mu_new.__3.entry), (MuID[]){L(2, test_mu_new.__3.entry.arg)}, (MuTypeNode[]){G(0, i64)}, 1, MU_NO_ID, (MuInstNode[]){L(3), L(5), L(7), L(8), L(10)}, 5);
irbuilder->new_func_ver(irbuilder, L(0), G(1, test_mu_new), (MuBBNode[]){L(1, test_mu_new.__3.entry)}, 1);
irbuilder->load(irbuilder);
}
ctx->make_boot_image(ctx,
(MuID[]){G(0, i64), G(1, test_mu_new)}, 2,
NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, 0,
"test_mu_new.so");
load_lib(); run_current(mvm);
}
#include "test_lib.h"
int main() {
load_lib();
run_resume();
}
#!/bin/bash
{
echo -e '#include "test_lib.h"'
$MUC -c test_mu_new.uir emit/test_mu_new.so | head -n -1;
echo -e '\n\tload_lib(); run_current(mvm);\n}'
} > main_build.c
gcc -L$MU_ZEBU/target/$ZEBU_BUILD/deps -lmu -ldl main_build.c test_lib.c -o main_build
gcc -L$MU_ZEBU/target/$ZEBU_BUILD/deps -lmu -ldl main_load.c test_lib.c -o main_load
echo "Testing in same process"
./main_build
echo "testing in diferent process"
./main_load
#include "muapi.h"
extern void load_lib();
extern void run_resume();
extern void run_current(MuVM* vm);
.typedef i64 = int<64>
.funcdef test_mu_new<(i64)->(i64)>
{
entry(<i64> arg):
obj = NEW <i64>
iobj = GETIREF <i64> obj
STORE <i64> iobj arg
res = LOAD <i64> iobj
RET res
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment