Commit 26de5491 authored by qinsoon's avatar qinsoon

ccall with extern symbol

parent b904f808
...@@ -104,6 +104,11 @@ pub enum Instruction_ { ...@@ -104,6 +104,11 @@ pub enum Instruction_ {
is_abort: bool, // T to abort, F to rethrow - FIXME: current, always rethrow for now is_abort: bool, // T to abort, F to rethrow - FIXME: current, always rethrow for now
}, },
ExprCCall{
data: CallData,
is_abort: bool
},
// yields the memory value // yields the memory value
Load{ Load{
is_ptr: bool, is_ptr: bool,
...@@ -267,6 +272,10 @@ impl Instruction_ { ...@@ -267,6 +272,10 @@ impl Instruction_ {
let abort = select_value!(is_abort, "ABORT_ON_EXN", "RETHROW"); let abort = select_value!(is_abort, "ABORT_ON_EXN", "RETHROW");
format!("CALL {} {}", data.debug_str(ops), abort) format!("CALL {} {}", data.debug_str(ops), abort)
}, },
&Instruction_::ExprCCall{ref data, is_abort} => {
let abort = select_value!(is_abort, "ABORT_ON_EXN", "RETHROW");
format!("CCALL {} {}", data.debug_str(ops), abort)
}
&Instruction_::Load{is_ptr, mem_loc, order} => { &Instruction_::Load{is_ptr, mem_loc, order} => {
let ptr = select_value!(is_ptr, "PTR", ""); let ptr = select_value!(is_ptr, "PTR", "");
format!("LOAD {} {:?} {}", ptr, order, ops[mem_loc]) format!("LOAD {} {:?} {}", ptr, order, ops[mem_loc])
...@@ -338,7 +347,7 @@ impl Instruction_ { ...@@ -338,7 +347,7 @@ impl Instruction_ {
format!("WPBRANCH {} {} {}", wp, disable_dest.debug_str(ops), enable_dest.debug_str(ops)) format!("WPBRANCH {} {} {}", wp, disable_dest.debug_str(ops), enable_dest.debug_str(ops))
}, },
&Instruction_::Call{ref data, ref resume} => format!("CALL {} {}", data.debug_str(ops), resume.debug_str(ops)), &Instruction_::Call{ref data, ref resume} => format!("CALL {} {}", data.debug_str(ops), resume.debug_str(ops)),
&Instruction_::CCall{ref data, ref resume} => format!("CALL {} {}", data.debug_str(ops), resume.debug_str(ops)), &Instruction_::CCall{ref data, ref resume} => format!("CCALL {} {}", data.debug_str(ops), resume.debug_str(ops)),
&Instruction_::SwapStack{stack, is_exception, ref args, ref resume} => { &Instruction_::SwapStack{stack, is_exception, ref args, ref resume} => {
format!("SWAPSTACK {} {} {} {}", ops[stack], is_exception, op_vector_str(args, ops), resume.debug_str(ops)) format!("SWAPSTACK {} {} {} {}", ops[stack], is_exception, op_vector_str(args, ops), resume.debug_str(ops))
}, },
......
...@@ -19,6 +19,8 @@ pub type CName = MuName; ...@@ -19,6 +19,8 @@ pub type CName = MuName;
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn Mu(str: &'static str) -> MuName {str.to_string()} pub fn Mu(str: &'static str) -> MuName {str.to_string()}
#[allow(non_snake_case)]
pub fn C(str: &'static str) -> CName {str.to_string()}
pub type OpIndex = usize; pub type OpIndex = usize;
...@@ -435,6 +437,7 @@ impl BlockContent { ...@@ -435,6 +437,7 @@ impl BlockContent {
vec_utils::append_unique(&mut ret, &mut live_outs); vec_utils::append_unique(&mut ret, &mut live_outs);
} }
Instruction_::Call{ref resume, ..} Instruction_::Call{ref resume, ..}
| Instruction_::CCall{ref resume, ..}
| Instruction_::SwapStack{ref resume, ..} | Instruction_::SwapStack{ref resume, ..}
| Instruction_::ExnInstruction{ref resume, ..} => { | Instruction_::ExnInstruction{ref resume, ..} => {
let mut live_outs = vec![]; let mut live_outs = vec![];
...@@ -736,10 +739,10 @@ pub enum Constant { ...@@ -736,10 +739,10 @@ pub enum Constant {
Double(f64), Double(f64),
// IRef(Address), // IRef(Address),
FuncRef(MuID), FuncRef(MuID),
UFuncRef(MuID),
Vector(Vec<Constant>), Vector(Vec<Constant>),
//Pointer(Address), //Pointer(Address),
NullRef, NullRef,
ExternSym(CName)
} }
impl fmt::Display for Constant { impl fmt::Display for Constant {
...@@ -750,7 +753,6 @@ impl fmt::Display for Constant { ...@@ -750,7 +753,6 @@ impl fmt::Display for Constant {
&Constant::Double(v) => write!(f, "{}", v), &Constant::Double(v) => write!(f, "{}", v),
// &Constant::IRef(v) => write!(f, "{}", v), // &Constant::IRef(v) => write!(f, "{}", v),
&Constant::FuncRef(v) => write!(f, "{}", v), &Constant::FuncRef(v) => write!(f, "{}", v),
&Constant::UFuncRef(v) => write!(f, "{}", v),
&Constant::Vector(ref v) => { &Constant::Vector(ref v) => {
write!(f, "[").unwrap(); write!(f, "[").unwrap();
for i in 0..v.len() { for i in 0..v.len() {
...@@ -762,6 +764,7 @@ impl fmt::Display for Constant { ...@@ -762,6 +764,7 @@ impl fmt::Display for Constant {
write!(f, "]") write!(f, "]")
} }
&Constant::NullRef => write!(f, "NullRef"), &Constant::NullRef => write!(f, "NullRef"),
&Constant::ExternSym(ref name) => write!(f, "ExternSym({})", name)
} }
} }
} }
......
...@@ -7,6 +7,7 @@ pub fn is_terminal_inst(inst: &Instruction_) -> bool { ...@@ -7,6 +7,7 @@ pub fn is_terminal_inst(inst: &Instruction_) -> bool {
| &CmpOp(_, _, _) | &CmpOp(_, _, _)
| &ConvOp{..} | &ConvOp{..}
| &ExprCall{..} | &ExprCall{..}
| &ExprCCall{..}
| &Load{..} | &Load{..}
| &Store{..} | &Store{..}
| &CmpXchg{..} | &CmpXchg{..}
...@@ -52,6 +53,7 @@ pub fn has_side_effect(inst: &Instruction_) -> bool { ...@@ -52,6 +53,7 @@ pub fn has_side_effect(inst: &Instruction_) -> bool {
&CmpOp(_, _, _) => false, &CmpOp(_, _, _) => false,
&ConvOp{..} => false, &ConvOp{..} => false,
&ExprCall{..} => true, &ExprCall{..} => true,
&ExprCCall{..} => true,
&Load{..} => true, &Load{..} => true,
&Store{..} => true, &Store{..} => true,
&CmpXchg{..} => true, &CmpXchg{..} => true,
......
...@@ -39,6 +39,7 @@ pub enum OpCode { ...@@ -39,6 +39,7 @@ pub enum OpCode {
AtomicRMW(AtomicRMWOp), AtomicRMW(AtomicRMWOp),
ExprCall, ExprCall,
ExprCCall,
Load, Load,
Store, Store,
CmpXchg, CmpXchg,
...@@ -247,6 +248,7 @@ pub fn pick_op_code_for_inst(inst: &Instruction) -> OpCode { ...@@ -247,6 +248,7 @@ pub fn pick_op_code_for_inst(inst: &Instruction) -> OpCode {
Instruction_::ConvOp{operation, ..} => OpCode::Conversion(operation), Instruction_::ConvOp{operation, ..} => OpCode::Conversion(operation),
Instruction_::AtomicRMW{op, ..} => OpCode::AtomicRMW(op), Instruction_::AtomicRMW{op, ..} => OpCode::AtomicRMW(op),
Instruction_::ExprCall{..} => OpCode::ExprCall, Instruction_::ExprCall{..} => OpCode::ExprCall,
Instruction_::ExprCCall{..} => OpCode::ExprCCall,
Instruction_::Load{..} => OpCode::Load, Instruction_::Load{..} => OpCode::Load,
Instruction_::Store{..} => OpCode::Store, Instruction_::Store{..} => OpCode::Store,
Instruction_::CmpXchg{..} => OpCode::CmpXchg, Instruction_::CmpXchg{..} => OpCode::CmpXchg,
......
...@@ -259,6 +259,18 @@ impl <'a> InstructionSelection { ...@@ -259,6 +259,18 @@ impl <'a> InstructionSelection {
Some(resume), Some(resume),
node, node,
f_content, f_context, vm); f_content, f_context, vm);
},
Instruction_::ExprCCall{ref data, is_abort} => {
if is_abort {
unimplemented!()
}
self.emit_c_call_ir(inst, data, None, node, f_content, f_context, vm);
}
Instruction_::CCall{ref data, ref resume} => {
self.emit_c_call_ir(inst, data, Some(resume), node, f_content, f_context, vm);
} }
Instruction_::Return(_) => { Instruction_::Return(_) => {
...@@ -1170,7 +1182,7 @@ impl <'a> InstructionSelection { ...@@ -1170,7 +1182,7 @@ impl <'a> InstructionSelection {
} }
}; };
self.emit_c_call(entry_name, sig, args, rets, cur_node, f_content, f_context, vm) self.emit_c_call_internal(entry_name, sig, args, rets, cur_node, f_content, f_context, vm)
} }
// returns the stack arg offset - we will need this to collapse stack after the call // returns the stack arg offset - we will need this to collapse stack after the call
...@@ -1314,7 +1326,7 @@ impl <'a> InstructionSelection { ...@@ -1314,7 +1326,7 @@ impl <'a> InstructionSelection {
// if ret is Some, return values will put stored in given temporaries // if ret is Some, return values will put stored in given temporaries
// otherwise create temporaries // otherwise create temporaries
// always returns result temporaries (given or created) // always returns result temporaries (given or created)
fn emit_c_call ( fn emit_c_call_internal(
&mut self, &mut self,
func_name: CName, func_name: CName,
sig: P<CFuncSig>, sig: P<CFuncSig>,
...@@ -1345,6 +1357,69 @@ impl <'a> InstructionSelection { ...@@ -1345,6 +1357,69 @@ impl <'a> InstructionSelection {
self.emit_postcall_convention(&sig, &rets, stack_arg_size, f_context, vm) self.emit_postcall_convention(&sig, &rets, stack_arg_size, f_context, vm)
} }
fn emit_c_call_ir(
&mut self,
inst: &Instruction,
calldata: &CallData,
resumption: Option<&ResumptionData>,
cur_node: &TreeNode,
f_content: &FunctionContent,
f_context: &mut FunctionContext,
vm: &VM)
{
let ops = inst.ops.read().unwrap();
// prepare args (they could be instructions, we need to emit inst and get value)
let mut arg_values = vec![];
for arg_index in calldata.args.iter() {
let ref arg = ops[*arg_index];
if self.match_ireg(arg) {
let arg = self.emit_ireg(arg, f_content, f_context, vm);
arg_values.push(arg);
} else if self.match_iimm(arg) {
let arg = self.node_iimm_to_value(arg);
arg_values.push(arg);
} else {
unimplemented!();
}
}
let args = arg_values;
trace!("generating ccall");
let ref func = ops[calldata.func];
if self.match_funcref_const(func) {
match func.v {
TreeNode_::Value(ref pv) => {
let sig = match pv.ty.v {
MuType_::UFuncPtr(ref sig) => sig.clone(),
_ => panic!("expected ufuncptr type with ccall, found {}", pv)
};
let rets = inst.value.clone();
match pv.v {
Value_::Constant(Constant::Int(addr)) => unimplemented!(),
Value_::Constant(Constant::ExternSym(ref func_name)) => {
self.emit_c_call_internal(
func_name.clone(), //func_name: CName,
sig, // sig: P<CFuncSig>,
args, // args: Vec<P<Value>>,
rets, // Option<Vec<P<Value>>>,
Some(cur_node), // Option<&TreeNode>,
f_content, // &FunctionContent,
f_context, // &mut FunctionContext,
vm);
},
_ => panic!("expect a ufuncptr to be either address constant, or symbol constant, we have {}", pv)
}
},
_ => unimplemented!()
}
}
}
fn emit_mu_call( fn emit_mu_call(
&mut self, &mut self,
...@@ -1726,10 +1801,15 @@ impl <'a> InstructionSelection { ...@@ -1726,10 +1801,15 @@ impl <'a> InstructionSelection {
let tmp = self.make_temporary(f_context, pv.ty.clone(), vm); let tmp = self.make_temporary(f_context, pv.ty.clone(), vm);
match c { match c {
&Constant::Int(val) => { &Constant::Int(val) => {
self.backend.emit_mov_r64_imm64(&tmp, val as i64); if x86_64::is_valid_x86_imm(pv) {
let val = self.value_iimm_to_i32(&pv);
self.backend.emit_mov_r64_imm32(&tmp, val)
} else {
self.backend.emit_mov_r64_imm64(&tmp, val as i64);
}
}, },
&Constant::FuncRef(_) &Constant::FuncRef(_) => {
| &Constant::UFuncRef(_) => {
unimplemented!() unimplemented!()
}, },
&Constant::NullRef => { &Constant::NullRef => {
...@@ -1855,11 +1935,18 @@ impl <'a> InstructionSelection { ...@@ -1855,11 +1935,18 @@ impl <'a> InstructionSelection {
fn match_funcref_const(&mut self, op: &P<TreeNode>) -> bool { fn match_funcref_const(&mut self, op: &P<TreeNode>) -> bool {
match op.v { match op.v {
TreeNode_::Value(ref pv) => { TreeNode_::Value(ref pv) => {
match pv.v { let is_const = match pv.v {
Value_::Constant(Constant::FuncRef(_)) => true, Value_::Constant(_) => true,
Value_::Constant(Constant::UFuncRef(_)) => true,
_ => false _ => false
} };
let is_func = match &pv.ty.v {
&MuType_::FuncRef(_)
| &MuType_::UFuncPtr(_) => true,
_ => false
};
is_const && is_func
}, },
_ => false _ => false
} }
...@@ -1869,12 +1956,11 @@ impl <'a> InstructionSelection { ...@@ -1869,12 +1956,11 @@ impl <'a> InstructionSelection {
match op.v { match op.v {
TreeNode_::Value(ref pv) => { TreeNode_::Value(ref pv) => {
match pv.v { match pv.v {
Value_::Constant(Constant::FuncRef(id)) Value_::Constant(Constant::FuncRef(id)) => id,
| Value_::Constant(Constant::UFuncRef(id)) => id, _ => panic!("expected a funcref const")
_ => panic!("expected a (u)funcref const")
} }
}, },
_ => panic!("expected a (u)funcref const") _ => panic!("expected a funcref const")
} }
} }
......
use testutil::get_test_clang_path; use testutil::*;
use testutil::exec;
use ast::ir::MuName; use ast::ir::MuName;
use runtime; use runtime;
use compiler::backend; use compiler::backend;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::process::Output;
fn link_executable_internal (files: Vec<PathBuf>, out: PathBuf) -> PathBuf { fn link_executable_internal (files: Vec<PathBuf>, out: PathBuf) -> PathBuf {
let mut gcc = Command::new(get_test_clang_path()); let mut gcc = Command::new(get_test_clang_path());
...@@ -118,9 +119,14 @@ pub fn link_primordial (funcs: Vec<MuName>, out: &str) -> PathBuf { ...@@ -118,9 +119,14 @@ pub fn link_primordial (funcs: Vec<MuName>, out: &str) -> PathBuf {
link_executable_internal(files, out_path) link_executable_internal(files, out_path)
} }
pub fn execute(executable: PathBuf) { 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()); let run = Command::new(executable.as_os_str());
assert!(exec(run).status.success()); exec_nocheck(run)
} }
pub fn link_dylib (funcs: Vec<MuName>, out: &str) -> PathBuf { pub fn link_dylib (funcs: Vec<MuName>, out: &str) -> PathBuf {
......
...@@ -21,6 +21,14 @@ pub fn get_test_clang_path() -> String { ...@@ -21,6 +21,14 @@ pub fn get_test_clang_path() -> String {
} }
pub fn exec (mut cmd: Command) -> Output { pub fn exec (mut cmd: Command) -> Output {
let output = exec_nocheck(cmd);
assert!(output.status.success());
output
}
pub fn exec_nocheck (mut cmd: Command) -> Output {
println!("executing: {:?}", cmd); println!("executing: {:?}", cmd);
let output = match cmd.output() { let output = match cmd.output() {
Ok(res) => res, Ok(res) => res,
...@@ -32,8 +40,6 @@ pub fn exec (mut cmd: Command) -> Output { ...@@ -32,8 +40,6 @@ pub fn exec (mut cmd: Command) -> Output {
println!("---err---"); println!("---err---");
println!("{}", String::from_utf8_lossy(&output.stderr)); println!("{}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success());
output output
} }
......
...@@ -9,4 +9,5 @@ mod test_thread; ...@@ -9,4 +9,5 @@ mod test_thread;
mod test_floatingpoint; mod test_floatingpoint;
mod test_int; mod test_int;
mod test_binop; mod test_binop;
mod test_controlflow; mod test_controlflow;
\ No newline at end of file mod test_call;
\ No newline at end of file
use mu::ast::types::*;
use mu::ast::ir::*;
use mu::ast::inst::*;
use mu::ast::op::*;
use mu::vm::*;
use mu::compiler::*;
use mu::testutil;
use std::sync::RwLock;
use std::sync::Arc;
use mu::testutil::aot;
#[test]
fn test_ccall_exit() {
VM::start_logging_trace();
let vm = Arc::new(ccall_exit());
let compiler = Compiler::new(CompilerPolicy::default(), vm.clone());
let func_id = vm.id_of("ccall_exit");
{
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);
}
vm.make_primordial_thread(func_id, vec![]);
backend::emit_context(&vm);
let executable = aot::link_primordial(vec!["ccall_exit".to_string()], "ccall_exit_test");
let output = aot::execute_nocheck(executable);
assert!(output.status.code().is_some());
let ret_code = output.status.code().unwrap();
println!("return code: {}", ret_code);
assert!(ret_code == 10);
}
fn ccall_exit() -> VM {
let vm = VM::new();
// .typedef @int32 = int<32>
let type_def_int32 = vm.declare_type(vm.next_id(), MuType_::int(32));
vm.set_name(type_def_int32.as_entity(), Mu("int32"));
// .typedef @exit_sig = (@int32) -> !
let exit_sig = vm.declare_func_sig(vm.next_id(), vec![], vec![type_def_int32.clone()]);
vm.set_name(exit_sig.as_entity(), Mu("exit_sig"));
// .typedef @ufp_exit = ufuncptr(@exit_sig)
let type_def_ufp_exit = vm.declare_type(vm.next_id(), MuType_::UFuncPtr(exit_sig.clone()));
vm.set_name(type_def_ufp_exit.as_entity(), Mu("ufp_exit"));
// .const @exit = EXTERN SYMBOL "exit"
let const_exit = vm.declare_const(vm.next_id(), type_def_ufp_exit.clone(), Constant::ExternSym(C("exit")));
vm.set_name(const_exit.as_entity(), Mu("exit"));
// .const @int32_10 = 10
let const_int32_10 = vm.declare_const(vm.next_id(), type_def_int32.clone(), Constant::Int(10));
vm.set_name(const_int32_10.as_entity(), Mu("const_int32_10"));
// .const @int32_0 = 0
let const_int32_0 = vm.declare_const(vm.next_id(), type_def_int32.clone(), Constant::Int(0));
vm.set_name(const_int32_0.as_entity(), Mu("const_int32_0"));
// .funcsig @ccall_exit_sig = () -> !
let ccall_exit_sig = vm.declare_func_sig(vm.next_id(), vec![], vec![]);
vm.set_name(ccall_exit_sig.as_entity(), Mu("ccall_exit_sig"));
// .funcdecl @ccall_exit <@ccall_exit_sig>
let func_id = vm.next_id();
let func = MuFunction::new(func_id, ccall_exit_sig.clone());
vm.set_name(func.as_entity(), Mu("ccall_exit"));
vm.declare_func(func);
// .funcdef @ccall_exit VERSION @ccall_exit_v1 <@ccall_exit_sig>
let mut func_ver = MuFunctionVersion::new(vm.next_id(), func_id, ccall_exit_sig.clone());
vm.set_name(func_ver.as_entity(), Mu("ccall_exit_v1"));
// %entry():
let mut blk_entry = Block::new(vm.next_id());
vm.set_name(blk_entry.as_entity(), Mu("entry"));
// exprCCALL %const_exit (%const_int32_10) normal: %end(), exception: %end()
let blk_end_id = vm.next_id();
let const_exit_local = func_ver.new_constant(const_exit.clone());
let const_int32_10_local = func_ver.new_constant(const_int32_10.clone());
let blk_entry_ccall = func_ver.new_inst(Instruction{
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: RwLock::new(vec![const_exit_local, const_int32_10_local]),
v: Instruction_::ExprCCall {
data: CallData {
func: 0,
args: vec![1],
convention: CallConvention::Foreign(ForeignFFI::C)
},
is_abort: false
}
});
// RET %const_int32_0
let const_int32_0_local = func_ver.new_constant(const_int32_0.clone());
let blk_entry_ret = func_ver.new_inst(Instruction{
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: RwLock::new(vec![const_int32_0_local]),
v: Instruction_::Return(vec![0])
});
blk_entry.content = Some(BlockContent {
args: vec![],
exn_arg: None,
body: vec![blk_entry_ccall, blk_entry_ret],
keepalives: None
});
func_ver.define(FunctionContent{
entry: blk_entry.id(),
blocks: hashmap!{
blk_entry.id() => blk_entry
}
});
vm.define_func_version(func_ver);
vm
}
\ No newline at end of file
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