GitLab will continue to be upgraded from 11.4.5-ce.0 on November 25th 2019 at 4.00pm (AEDT) to 5.00pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available.

Commit 26de5491 authored by qinsoon's avatar qinsoon

ccall with extern symbol

parent b904f808
Pipeline #125 canceled with stage
......@@ -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