Commit 26de5491 authored by qinsoon's avatar qinsoon

ccall with extern symbol

parent b904f808
......@@ -104,6 +104,11 @@ pub enum Instruction_ {
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
Load{
is_ptr: bool,
......@@ -267,6 +272,10 @@ impl Instruction_ {
let abort = select_value!(is_abort, "ABORT_ON_EXN", "RETHROW");
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} => {
let ptr = select_value!(is_ptr, "PTR", "");
format!("LOAD {} {:?} {}", ptr, order, ops[mem_loc])
......@@ -338,7 +347,7 @@ impl Instruction_ {
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_::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} => {
format!("SWAPSTACK {} {} {} {}", ops[stack], is_exception, op_vector_str(args, ops), resume.debug_str(ops))
},
......
......@@ -19,6 +19,8 @@ pub type CName = MuName;
#[allow(non_snake_case)]
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;
......@@ -435,6 +437,7 @@ impl BlockContent {
vec_utils::append_unique(&mut ret, &mut live_outs);
}
Instruction_::Call{ref resume, ..}
| Instruction_::CCall{ref resume, ..}
| Instruction_::SwapStack{ref resume, ..}
| Instruction_::ExnInstruction{ref resume, ..} => {
let mut live_outs = vec![];
......@@ -736,10 +739,10 @@ pub enum Constant {
Double(f64),
// IRef(Address),
FuncRef(MuID),
UFuncRef(MuID),
Vector(Vec<Constant>),
//Pointer(Address),
NullRef,
ExternSym(CName)
}
impl fmt::Display for Constant {
......@@ -750,7 +753,6 @@ impl fmt::Display for Constant {
&Constant::Double(v) => write!(f, "{}", v),
// &Constant::IRef(v) => write!(f, "{}", v),
&Constant::FuncRef(v) => write!(f, "{}", v),
&Constant::UFuncRef(v) => write!(f, "{}", v),
&Constant::Vector(ref v) => {
write!(f, "[").unwrap();
for i in 0..v.len() {
......@@ -762,6 +764,7 @@ impl fmt::Display for Constant {
write!(f, "]")
}
&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 {
| &CmpOp(_, _, _)
| &ConvOp{..}
| &ExprCall{..}
| &ExprCCall{..}
| &Load{..}
| &Store{..}
| &CmpXchg{..}
......@@ -52,6 +53,7 @@ pub fn has_side_effect(inst: &Instruction_) -> bool {
&CmpOp(_, _, _) => false,
&ConvOp{..} => false,
&ExprCall{..} => true,
&ExprCCall{..} => true,
&Load{..} => true,
&Store{..} => true,
&CmpXchg{..} => true,
......
......@@ -39,6 +39,7 @@ pub enum OpCode {
AtomicRMW(AtomicRMWOp),
ExprCall,
ExprCCall,
Load,
Store,
CmpXchg,
......@@ -247,6 +248,7 @@ pub fn pick_op_code_for_inst(inst: &Instruction) -> OpCode {
Instruction_::ConvOp{operation, ..} => OpCode::Conversion(operation),
Instruction_::AtomicRMW{op, ..} => OpCode::AtomicRMW(op),
Instruction_::ExprCall{..} => OpCode::ExprCall,
Instruction_::ExprCCall{..} => OpCode::ExprCCall,
Instruction_::Load{..} => OpCode::Load,
Instruction_::Store{..} => OpCode::Store,
Instruction_::CmpXchg{..} => OpCode::CmpXchg,
......
......@@ -259,6 +259,18 @@ impl <'a> InstructionSelection {
Some(resume),
node,
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(_) => {
......@@ -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
......@@ -1314,7 +1326,7 @@ impl <'a> InstructionSelection {
// if ret is Some, return values will put stored in given temporaries
// otherwise create temporaries
// always returns result temporaries (given or created)
fn emit_c_call (
fn emit_c_call_internal(
&mut self,
func_name: CName,
sig: P<CFuncSig>,
......@@ -1345,6 +1357,69 @@ impl <'a> InstructionSelection {
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(
&mut self,
......@@ -1726,10 +1801,15 @@ impl <'a> InstructionSelection {
let tmp = self.make_temporary(f_context, pv.ty.clone(), vm);
match c {
&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::UFuncRef(_) => {
&Constant::FuncRef(_) => {
unimplemented!()
},
&Constant::NullRef => {
......@@ -1855,11 +1935,18 @@ impl <'a> InstructionSelection {
fn match_funcref_const(&mut self, op: &P<TreeNode>) -> bool {
match op.v {
TreeNode_::Value(ref pv) => {
match pv.v {
Value_::Constant(Constant::FuncRef(_)) => true,
Value_::Constant(Constant::UFuncRef(_)) => true,
let is_const = match pv.v {
Value_::Constant(_) => true,
_ => false
}
};
let is_func = match &pv.ty.v {
&MuType_::FuncRef(_)
| &MuType_::UFuncPtr(_) => true,
_ => false
};
is_const && is_func
},
_ => false
}
......@@ -1869,12 +1956,11 @@ impl <'a> InstructionSelection {
match op.v {
TreeNode_::Value(ref pv) => {
match pv.v {
Value_::Constant(Constant::FuncRef(id))
| Value_::Constant(Constant::UFuncRef(id)) => id,
_ => panic!("expected a (u)funcref const")
Value_::Constant(Constant::FuncRef(id)) => id,
_ => panic!("expected a funcref const")
}
},
_ => panic!("expected a (u)funcref const")
_ => panic!("expected a funcref const")
}
}
......
use testutil::get_test_clang_path;
use testutil::exec;
use testutil::*;
use ast::ir::MuName;
use runtime;
use compiler::backend;
use std::path::PathBuf;
use std::process::Command;
use std::process::Output;
fn link_executable_internal (files: Vec<PathBuf>, out: PathBuf) -> PathBuf {
let mut gcc = Command::new(get_test_clang_path());
......@@ -118,9 +119,14 @@ pub fn link_primordial (funcs: Vec<MuName>, out: &str) -> PathBuf {
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());
assert!(exec(run).status.success());
exec_nocheck(run)
}
pub fn link_dylib (funcs: Vec<MuName>, out: &str) -> PathBuf {
......
......@@ -21,6 +21,14 @@ pub fn get_test_clang_path() -> String {
}
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);
let output = match cmd.output() {
Ok(res) => res,
......@@ -32,8 +40,6 @@ pub fn exec (mut cmd: Command) -> Output {
println!("---err---");
println!("{}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success());
output
}
......
......@@ -9,4 +9,5 @@ mod test_thread;
mod test_floatingpoint;
mod test_int;
mod test_binop;
mod test_controlflow;
\ No newline at end of file
mod test_controlflow;
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