Commit 8f5cfd09 authored by qinsoon's avatar qinsoon

simple threadlocal impl. launching thread works

parent 6efaae9b
......@@ -5,7 +5,7 @@ authors = [ "Your name <you@example.com>" ]
build = "build.rs"
[lib]
crate-type = ["cdylib", "rlib"]
crate-type = ["staticlib", "rlib"]
[build-dependencies.gcc]
git = "https://github.com/alexcrichton/gcc-rs"
......@@ -23,4 +23,4 @@ rustc-serialize = "*"
time = "0.1.34"
aligned_alloc = "0.1.2"
crossbeam = "0.2.8"
byteorder = "0.5.3"
\ No newline at end of file
byteorder = "0.5.3"
......@@ -14,6 +14,7 @@ use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
pub type WPID = usize;
pub type MuID = usize;
pub type MuName = String;
pub type CName = MuName;
pub type OpIndex = usize;
......@@ -203,6 +204,20 @@ impl FunctionContext {
values: HashMap::new()
}
}
pub fn make_temporary(&mut self, id: MuID, ty: P<MuType>) -> P<TreeNode> {
self.values.insert(id, SSAVarEntry::new(id, ty.clone()));
P(TreeNode {
hdr: MuEntityHeader::unnamed(id),
op: pick_op_code_for_ssa(&ty),
v: TreeNode_::Value(P(Value{
hdr: MuEntityHeader::unnamed(id),
ty: ty,
v: Value_::SSAVar(id)
}))
})
}
pub fn get_value(&self, id: MuID) -> Option<&SSAVarEntry> {
self.values.get(&id)
......@@ -479,6 +494,18 @@ impl Value {
_ => false
}
}
pub fn extract_int_const(&self) -> u64 {
match self.v {
Value_::Constant(ref c) => {
match c {
&Constant::Int(val) => val,
_ => panic!("expect int const")
}
},
_ => panic!("expect int const")
}
}
pub fn extract_ssa_id(&self) -> Option<MuID> {
match self.v {
......
......@@ -323,6 +323,8 @@ macro_rules! is_type (
)
);
pub type CFuncSig = MuFuncSig;
#[derive(PartialEq, Debug, RustcEncodable, RustcDecodable)]
pub struct MuFuncSig {
pub hdr: MuEntityHeader,
......
......@@ -844,9 +844,9 @@ impl CodeGenerator for ASMCodeGen {
fn emit_add_r64_imm32(&mut self, dest: &P<Value>, src: u32) {
trace!("emit: add {}, {} -> {}", dest, src, dest);
let (reg1, id1, loc1) = self.prepare_reg(dest, 4 + 1);
let (reg1, id1, loc1) = self.prepare_reg(dest, 4 + 1 + 1 + src.to_string().len() + 1);
let asm = format!("addq {},${}", src, reg1);
let asm = format!("addq ${},{}", src, reg1);
self.add_asm_inst(
asm,
......
......@@ -10,6 +10,10 @@ use ast::types;
use ast::types::*;
use vm::VM;
use vm::CompiledFunction;
use runtime::ValueLocation;
use runtime::thread;
use runtime::entrypoints;
use runtime::entrypoints::RuntimeEntrypoint;
use compiler::CompilerPass;
use compiler::backend::x86_64;
......@@ -38,7 +42,7 @@ impl <'a> InstructionSelection {
// 3. we need to backup/restore all the callee-saved registers
// if any of these assumption breaks, we will need to re-emit the code
#[allow(unused_variables)]
fn instruction_select(&mut self, node: &'a TreeNode, cur_func: &MuFunctionVersion, vm: &VM) {
fn instruction_select(&mut self, node: &'a TreeNode, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) {
trace!("instsel on node {}", node);
match node.v {
......@@ -57,16 +61,16 @@ impl <'a> InstructionSelection {
let ops = inst.ops.read().unwrap();
self.process_dest(&ops, fallthrough_dest, cur_func, vm);
self.process_dest(&ops, branch_dest, cur_func, vm);
self.process_dest(&ops, fallthrough_dest, f_content, f_context, vm);
self.process_dest(&ops, branch_dest, f_content, f_context, vm);
let branch_target = cur_func.content.as_ref().unwrap().get_block(branch_dest.target);
let branch_target = f_content.get_block(branch_dest.target);
let ref cond = ops[cond];
if self.match_cmp_res(cond) {
trace!("emit cmp_eq-branch2");
match self.emit_cmp_res(cond, cur_func, vm) {
match self.emit_cmp_res(cond, f_content, f_context, vm) {
op::CmpOp::EQ => self.backend.emit_je(branch_target),
op::CmpOp::NE => self.backend.emit_jne(branch_target),
op::CmpOp::UGE => self.backend.emit_jae(branch_target),
......@@ -82,7 +86,7 @@ impl <'a> InstructionSelection {
} else if self.match_ireg(cond) {
trace!("emit ireg-branch2");
let cond_reg = self.emit_ireg(cond, cur_func, vm);
let cond_reg = self.emit_ireg(cond, f_content, f_context, vm);
// emit: cmp cond_reg 1
self.backend.emit_cmp_r64_imm32(&cond_reg, 1);
......@@ -96,9 +100,9 @@ impl <'a> InstructionSelection {
Instruction_::Branch1(ref dest) => {
let ops = inst.ops.read().unwrap();
self.process_dest(&ops, dest, cur_func, vm);
self.process_dest(&ops, dest, f_content, f_context, vm);
let target = cur_func.content.as_ref().unwrap().get_block(dest.target);
let target = f_content.get_block(dest.target);
trace!("emit branch1");
// jmp
......@@ -133,7 +137,7 @@ impl <'a> InstructionSelection {
trace!("arg {}", arg);
if self.match_ireg(arg) {
let arg = self.emit_ireg(arg, cur_func, vm);
let arg = self.emit_ireg(arg, f_content, f_context, vm);
if gpr_arg_count < x86_64::ARGUMENT_GPRs.len() {
self.backend.emit_mov_r64_r64(&x86_64::ARGUMENT_GPRs[gpr_arg_count], &arg);
......@@ -169,7 +173,7 @@ impl <'a> InstructionSelection {
self.backend.emit_call_near_rel32(target.name().unwrap());
}
} else if self.match_ireg(func) {
let target = self.emit_ireg(func, cur_func, vm);
let target = self.emit_ireg(func, f_content, f_context, vm);
self.backend.emit_call_near_r64(&target);
} else if self.match_mem(func) {
......@@ -200,7 +204,7 @@ impl <'a> InstructionSelection {
},
Instruction_::Return(_) => {
self.emit_common_epilogue(inst, cur_func, vm);
self.emit_common_epilogue(inst, f_content, f_context, vm);
self.backend.emit_ret();
},
......@@ -213,8 +217,8 @@ impl <'a> InstructionSelection {
if self.match_ireg(&ops[op1]) && self.match_ireg(&ops[op2]) {
trace!("emit add-ireg-ireg");
let reg_op1 = self.emit_ireg(&ops[op1], cur_func, vm);
let reg_op2 = self.emit_ireg(&ops[op2], cur_func, vm);
let reg_op1 = self.emit_ireg(&ops[op1], f_content, f_context, vm);
let reg_op2 = self.emit_ireg(&ops[op2], f_content, f_context, vm);
let res_tmp = self.emit_get_result(node);
// mov op1, res
......@@ -224,7 +228,7 @@ impl <'a> InstructionSelection {
} else if self.match_ireg(&ops[op1]) && self.match_iimm(&ops[op2]) {
trace!("emit add-ireg-imm");
let reg_op1 = self.emit_ireg(&ops[op1], cur_func, vm);
let reg_op1 = self.emit_ireg(&ops[op1], f_content, f_context, vm);
let reg_op2 = self.emit_get_iimm(&ops[op2]);
let res_tmp = self.emit_get_result(node);
......@@ -238,7 +242,7 @@ impl <'a> InstructionSelection {
} else if self.match_ireg(&ops[op1]) && self.match_mem(&ops[op2]) {
trace!("emit add-ireg-mem");
let reg_op1 = self.emit_ireg(&ops[op1], cur_func, vm);
let reg_op1 = self.emit_ireg(&ops[op1], f_content, f_context, vm);
let reg_op2 = self.emit_mem(&ops[op2]);
let res_tmp = self.emit_get_result(node);
......@@ -257,8 +261,8 @@ impl <'a> InstructionSelection {
if self.match_ireg(&ops[op1]) && self.match_ireg(&ops[op2]) {
trace!("emit sub-ireg-ireg");
let reg_op1 = self.emit_ireg(&ops[op1], cur_func, vm);
let reg_op2 = self.emit_ireg(&ops[op2], cur_func, vm);
let reg_op1 = self.emit_ireg(&ops[op1], f_content, f_context, vm);
let reg_op2 = self.emit_ireg(&ops[op2], f_content, f_context, vm);
let res_tmp = self.emit_get_result(node);
// mov op1, res
......@@ -268,7 +272,7 @@ impl <'a> InstructionSelection {
} else if self.match_ireg(&ops[op1]) && self.match_iimm(&ops[op2]) {
trace!("emit sub-ireg-imm");
let reg_op1 = self.emit_ireg(&ops[op1], cur_func, vm);
let reg_op1 = self.emit_ireg(&ops[op1], f_content, f_context, vm);
let imm_op2 = self.emit_get_iimm(&ops[op2]);
let res_tmp = self.emit_get_result(node);
......@@ -282,7 +286,7 @@ impl <'a> InstructionSelection {
} else if self.match_ireg(&ops[op1]) && self.match_mem(&ops[op2]) {
trace!("emit sub-ireg-mem");
let reg_op1 = self.emit_ireg(&ops[op1], cur_func, vm);
let reg_op1 = self.emit_ireg(&ops[op1], f_content, f_context, vm);
let mem_op2 = self.emit_mem(&ops[op2]);
let res_tmp = self.emit_get_result(node);
......@@ -302,7 +306,7 @@ impl <'a> InstructionSelection {
let rax = x86_64::RAX.clone();
let op1 = &ops[op1];
if self.match_ireg(op1) {
let reg_op1 = self.emit_ireg(op1, cur_func, vm);
let reg_op1 = self.emit_ireg(op1, f_content, f_context, vm);
self.backend.emit_mov_r64_r64(&rax, &reg_op1);
} else if self.match_iimm(op1) {
......@@ -320,7 +324,7 @@ impl <'a> InstructionSelection {
// mul op2 -> rax
let op2 = &ops[op2];
if self.match_ireg(op2) {
let reg_op2 = self.emit_ireg(op2, cur_func, vm);
let reg_op2 = self.emit_ireg(op2, f_content, f_context, vm);
self.backend.emit_mul_r64(&reg_op2);
} else if self.match_iimm(op2) {
......@@ -392,7 +396,7 @@ impl <'a> InstructionSelection {
let resolved_loc = self.emit_get_mem(loc_op, vm);
if self.match_ireg(val_op) {
let val = self.emit_ireg(val_op, cur_func, vm);
let val = self.emit_ireg(val_op, f_content, f_context, vm);
if generate_plain_mov {
self.backend.emit_mov_mem64_r64(&resolved_loc, &val);
} else {
......@@ -413,6 +417,12 @@ impl <'a> InstructionSelection {
Instruction_::ThreadExit => {
// emit a call to swap_back_to_native_stack(sp_loc: Address)
// get thread local and add offset to get sp_loc
let tl = self.emit_get_threadlocal(f_content, f_context, vm);
self.backend.emit_add_r64_imm32(&tl, *thread::NATIVE_SP_LOC_OFFSET as u32);
self.emit_runtime_entry(&entrypoints::SWAP_BACK_TO_NATIVE_STACK, vec![tl.clone()], f_content, f_context, vm);
}
_ => unimplemented!()
......@@ -420,13 +430,114 @@ impl <'a> InstructionSelection {
},
TreeNode_::Value(ref p) => {
}
}
}
fn emit_get_threadlocal (&mut self, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) -> P<Value> {
let mut rets = self.emit_runtime_entry(&entrypoints::GET_THREAD_LOCAL, vec![], f_content, f_context, vm);
rets.pop().unwrap()
}
fn emit_runtime_entry (&mut self, entry: &RuntimeEntrypoint, args: Vec<P<Value>>, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) -> Vec<P<Value>> {
let sig = entry.sig.clone();
let entry_name = {
if vm.is_running() {
unimplemented!()
} else {
let ref entry_loc = entry.aot;
match entry_loc {
&ValueLocation::Relocatable(_, ref name) => name.clone(),
_ => panic!("expecting a relocatable value")
}
}
};
self.emit_c_call(entry_name, sig, args, None, f_content, f_context, vm)
}
#[allow(unused_variables)]
fn process_dest(&mut self, ops: &Vec<P<TreeNode>>, dest: &Destination, cur_func: &MuFunctionVersion, vm: &VM) {
fn emit_c_call (&mut self, func_name: CName, sig: P<CFuncSig>, args: Vec<P<Value>>, rets: Option<Vec<P<Value>>>,
f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) -> Vec<P<Value>>
{
let mut gpr_arg_count = 0;
for arg in args.iter() {
if arg.is_int_reg() {
if gpr_arg_count < x86_64::ARGUMENT_GPRs.len() {
self.backend.emit_mov_r64_r64(&x86_64::ARGUMENT_GPRs[gpr_arg_count], &arg);
gpr_arg_count += 1;
} else {
// use stack to pass argument
unimplemented!()
}
} else if arg.is_int_const() {
if x86_64::is_valid_x86_imm(arg) {
let int_const = arg.extract_int_const() as u32;
if gpr_arg_count < x86_64::ARGUMENT_GPRs.len() {
self.backend.emit_mov_r64_imm32(&x86_64::ARGUMENT_GPRs[gpr_arg_count], int_const);
gpr_arg_count += 1;
} else {
// use stack to pass argument
unimplemented!()
}
} else {
// put the constant to memory
unimplemented!()
}
} else {
// floating point
unimplemented!()
}
}
// make call
if vm.is_running() {
unimplemented!()
} else {
self.backend.emit_call_near_rel32(func_name);
}
// deal with ret vals
let mut return_vals = vec![];
let mut gpr_ret_count = 0;
for ret_index in 0..sig.ret_tys.len() {
let ref ty = sig.ret_tys[ret_index];
let ret_val = match rets {
Some(ref rets) => rets[ret_index].clone(),
None => {
let tmp_node = f_context.make_temporary(vm.next_id(), ty.clone());
tmp_node.clone_value()
}
};
if ret_val.is_int_reg() {
if gpr_ret_count < x86_64::RETURN_GPRs.len() {
self.backend.emit_mov_r64_r64(&ret_val, &x86_64::RETURN_GPRs[gpr_ret_count]);
gpr_ret_count += 1;
} else {
// get return value by stack
unimplemented!()
}
} else {
// floating point register
unimplemented!()
}
return_vals.push(ret_val);
}
return_vals
}
#[allow(unused_variables)]
fn process_dest(&mut self, ops: &Vec<P<TreeNode>>, dest: &Destination, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) {
for i in 0..dest.args.len() {
let ref dest_arg = dest.args[i];
match dest_arg {
......@@ -448,10 +559,10 @@ impl <'a> InstructionSelection {
// }
// }
//
let ref target_args = cur_func.content.as_ref().unwrap().get_block(dest.target).content.as_ref().unwrap().args;
let ref target_args = f_content.get_block(dest.target).content.as_ref().unwrap().args;
let ref target_arg = target_args[i];
self.emit_general_move(&arg, target_arg, cur_func, vm);
self.emit_general_move(&arg, target_arg, f_content, f_context, vm);
},
&DestArg::Freshbound(_) => unimplemented!()
}
......@@ -503,7 +614,7 @@ impl <'a> InstructionSelection {
self.backend.end_block(block_name);
}
fn emit_common_epilogue(&mut self, ret_inst: &Instruction, cur_func: &MuFunctionVersion, vm: &VM) {
fn emit_common_epilogue(&mut self, ret_inst: &Instruction, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) {
// epilogue is not a block (its a few instruction inserted before return)
// FIXME: this may change in the future
......@@ -519,7 +630,7 @@ impl <'a> InstructionSelection {
for i in ret_val_indices {
let ref ret_val = ops[*i];
if self.match_ireg(ret_val) {
let reg_ret_val = self.emit_ireg(ret_val, cur_func, vm);
let reg_ret_val = self.emit_ireg(ret_val, f_content, f_context, vm);
self.backend.emit_mov_r64_r64(&x86_64::RETURN_GPRs[gpr_ret_count], &reg_ret_val);
gpr_ret_count += 1;
......@@ -557,7 +668,7 @@ impl <'a> InstructionSelection {
}
}
fn emit_cmp_res(&mut self, cond: &P<TreeNode>, cur_func: &MuFunctionVersion, vm: &VM) -> op::CmpOp {
fn emit_cmp_res(&mut self, cond: &P<TreeNode>, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) -> op::CmpOp {
match cond.v {
TreeNode_::Instruction(ref inst) => {
let ops = inst.ops.read().unwrap();
......@@ -569,12 +680,12 @@ impl <'a> InstructionSelection {
if op::is_int_cmp(op) {
if self.match_ireg(op1) && self.match_ireg(op2) {
let reg_op1 = self.emit_ireg(op1, cur_func, vm);
let reg_op2 = self.emit_ireg(op2, cur_func, vm);
let reg_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let reg_op2 = self.emit_ireg(op2, f_content, f_context, vm);
self.backend.emit_cmp_r64_r64(&reg_op1, &reg_op2);
} else if self.match_ireg(op1) && self.match_iimm(op2) {
let reg_op1 = self.emit_ireg(op1, cur_func, vm);
let reg_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let iimm_op2 = self.emit_get_iimm(op2);
self.backend.emit_cmp_r64_imm32(&reg_op1, iimm_op2);
......@@ -621,10 +732,10 @@ impl <'a> InstructionSelection {
}
}
fn emit_ireg(&mut self, op: &P<TreeNode>, cur_func: &MuFunctionVersion, vm: &VM) -> P<Value> {
fn emit_ireg(&mut self, op: &P<TreeNode>, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) -> P<Value> {
match op.v {
TreeNode_::Instruction(_) => {
self.instruction_select(op, cur_func, vm);
self.instruction_select(op, f_content, f_context, vm);
self.emit_get_result(op)
},
......@@ -763,12 +874,12 @@ impl <'a> InstructionSelection {
}
}
fn emit_general_move(&mut self, src: &P<TreeNode>, dest: &P<Value>, cur_func: &MuFunctionVersion, vm: &VM) {
fn emit_general_move(&mut self, src: &P<TreeNode>, dest: &P<Value>, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) {
let ref dst_ty = dest.ty;
if !types::is_fp(dst_ty) && types::is_scalar(dst_ty) {
if self.match_ireg(src) {
let src_reg = self.emit_ireg(src, cur_func, vm);
let src_reg = self.emit_ireg(src, f_content, f_context, vm);
self.backend.emit_mov_r64_r64(dest, &src_reg);
} else if self.match_iimm(src) {
let src_imm = self.emit_get_iimm(src);
......@@ -805,8 +916,10 @@ impl CompilerPass for InstructionSelection {
#[allow(unused_variables)]
fn visit_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
let f_content = func.content.as_ref().unwrap();
for block_id in func.block_trace.as_ref().unwrap() {
let block = func.content.as_ref().unwrap().get_block(*block_id);
let block = f_content.get_block(*block_id);
let block_label = block.name().unwrap();
self.backend.start_block(block_label.clone());
......@@ -821,7 +934,7 @@ impl CompilerPass for InstructionSelection {
self.backend.set_block_liveout(block_label.clone(), &live_out);
for inst in block_content.body.iter() {
self.instruction_select(&inst, func, vm);
self.instruction_select(&inst, f_content, &mut func.context, vm);
}
self.backend.end_block(block_label);
......
......@@ -12,12 +12,22 @@ use std::sync::RwLock;
pub type EntryFuncSig = MuFuncSig;
pub struct RuntimeEntrypoint {
sig: P<MuFuncSig>,
aot: ValueLocation,
jit: RwLock<Option<ValueLocation>>
pub sig: P<MuFuncSig>,
pub aot: ValueLocation,
pub jit: RwLock<Option<ValueLocation>>
}
lazy_static! {
pub static ref GET_THREAD_LOCAL : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig {
hdr: MuEntityHeader::unnamed(ir::new_internal_id()),
ret_tys: vec![runtime::ADDRESS_TYPE.clone()],
arg_tys: vec![]
}),
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("get_thread_local")),
jit: RwLock::new(None),
};
pub static ref SWAP_BACK_TO_NATIVE_STACK : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig{
hdr: MuEntityHeader::unnamed(ir::new_internal_id()),
......
......@@ -5,10 +5,14 @@
__thread void* mu_tls;
void* init_thread_local(void* thread) {
void set_thread_local(void* thread) {
printf("setting mu_tls to %p\n", thread);
mu_tls = thread;
}
return &mu_tls;
void* get_thread_local() {
printf("getting mu_tls as %p\n", mu_tls);
return mu_tls;
}
void* resolve_symbol(const char* sym) {
......
......@@ -210,12 +210,20 @@ pub struct MuThread {
user_tls: Option<Address>
}
// this depends on the layout of MuThread
lazy_static! {
pub static ref NATIVE_SP_LOC_OFFSET : usize = mem::size_of::<MuEntityHeader>()
+ mem::size_of::<Box<mm::Mutator>>()
+ mem::size_of::<Option<Box<MuStack>>>();
}
#[cfg(target_arch = "x86_64")]
#[cfg(target_os = "macos")]
#[link(name = "runtime")]
extern "C" {
#[allow(improper_ctypes)]
fn init_thread_local(thread: *mut MuThread) -> Address;
fn set_thread_local(thread: *mut MuThread);
pub fn get_thread_local() -> Address;
}
#[cfg(target_arch = "x86_64")]
......@@ -248,10 +256,15 @@ impl MuThread {
let muthread : *mut MuThread = Box::into_raw(Box::new(MuThread::new(id, mm::new_mutator(), stack, user_tls)));
// set thread local
let addr = unsafe {init_thread_local(muthread)};
let sp_threadlocal_loc = addr.plus(mem::size_of::<MuEntityHeader>())
.plus(mem::size_of::<Box<mm::Mutator>>())
.plus(mem::size_of::<Option<Box<MuStack>>>());
unsafe {set_thread_local(muthread)};
let addr = unsafe {get_thread_local()};
unsafe {get_thread_local()};
unsafe {get_thread_local()};
unsafe {get_thread_local()};
unsafe {get_thread_local()};
unsafe {get_thread_local()};
let sp_threadlocal_loc = addr.plus(*NATIVE_SP_LOC_OFFSET);
debug!("new sp: 0x{:x}", new_sp);
debug!("sp_store: 0x{:x}", sp_threadlocal_loc);
......@@ -259,6 +272,8 @@ impl MuThread {
unsafe {
swap_to_mu_stack(new_sp, entry, sp_threadlocal_loc);
}
debug!("returned to Rust stack. Going to quit");
}) {
Ok(handle) => handle,
Err(_) => panic!("failed to create a thread")
......
......@@ -19,6 +19,8 @@ use std::collections::HashMap;
#[test]
fn test_thread_create() {
simple_logger::init_with_level(log::LogLevel::Trace).ok();
let vm = Arc::new(primordial_main());
let compiler = Compiler::new(CompilerPolicy::default(), vm.clone());
......
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