Commit 856903b6 authored by Isaac Oscar Gariano's avatar Isaac Oscar Gariano

Improved swapstack implementation (WIP)

parent c24b8896
......@@ -176,8 +176,7 @@ impl Instruction {
CommonInst_GetAddr(_) |
PrintHex(_) |
SetRetval(_) |
KillStack(_) |
CurrentStack => true,
KillStack(_) => true,
BinOp(_, _, _) |
BinOpWithStatus(_, _, _, _) |
CmpOp(_, _, _) |
......@@ -198,7 +197,8 @@ impl Instruction {
CommonInst_Tr64ToInt(_) |
CommonInst_Tr64ToRef(_) |
CommonInst_Tr64ToTag(_) |
Move(_) => false
Move(_) |
CurrentStack => false
}
}
......
......@@ -1037,52 +1037,6 @@ impl ASMCodeGen {
self.cur_mut().code.push(ASMInst::symbolic(code));
}
fn add_asm_call(
&mut self,
code: String,
potentially_excepting: Option<MuName>,
arguments: Vec<P<Value>>,
target: Option<(MuID, ASMLocation)>
) {
// a call instruction will use all the argument registers
// do not need
let mut uses: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
if target.is_some() {
let (id, loc) = target.unwrap();
uses.insert(id, vec![loc]);
}
for arg in arguments {
uses.insert(arg.id(), vec![]);
}
let mut defines: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
for reg in CALLER_SAVED_GPRS.iter() {
if !defines.contains_key(&reg.id()) {
defines.insert(reg.id(), vec![]);
}
}
for reg in CALLER_SAVED_FPRS.iter() {
if !defines.contains_key(&reg.id()) {
defines.insert(reg.id(), vec![]);
}
}
self.add_asm_inst_internal(
code,
defines,
uses,
false,
{
if potentially_excepting.is_some() {
ASMBranchTarget::PotentiallyExcepting(potentially_excepting.unwrap())
} else {
ASMBranchTarget::None
}
},
None
)
}
fn add_asm_inst(
&mut self,
code: String,
......@@ -2162,6 +2116,48 @@ impl ASMCodeGen {
self.add_asm_inst(asm, ignore_zero_register(id1, vec![loc1]), uses, false)
}
fn internal_call(&mut self, callsite: Option<String>, code: String, pe: Option<MuName>, args: Vec<P<Value>>, ret: Vec<P<Value>>, target: Option<(MuID, ASMLocation)>, may_return: bool) -> Option<ValueLocation> {
let mut uses: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
if target.is_some() {
let (id, loc) = target.unwrap();
uses.insert(id, vec![loc]);
}
for arg in args {
uses.insert(arg.id(), vec![]);
}
let mut defines: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
for ret in ret.iter() {
defines.insert(ret.id(), vec![]);
}
self.add_asm_inst_internal(
code,
defines,
uses,
false,
{
if pe.is_some() {
ASMBranchTarget::PotentiallyExcepting(pe.unwrap())
} else if may_return {
ASMBranchTarget::None
} else {
ASMBranchTarget::Return
}
},
None
);
if callsite.is_some() {
let callsite_symbol = mangle_name(callsite.as_ref().unwrap().clone());
self.add_asm_symbolic(directive_globl(callsite_symbol.clone()));
self.add_asm_symbolic(format!("{}:", callsite_symbol.clone()));
Some(ValueLocation::Relocatable(RegGroup::GPR, callsite.unwrap()))
} else {
None
}
}
fn emit_ldr_spill(&mut self, dest: Reg, src: Mem) {
self.internal_load("LDR", dest, src, false, true, false);
}
......@@ -2395,12 +2391,13 @@ impl CodeGenerator for ASMCodeGen {
fn emit_bl(
&mut self,
callsite: String,
callsite: Option<String>,
func: MuName,
pe: Option<MuName>,
args: Vec<P<Value>>,
ret: Vec<P<Value>>,
is_native: bool
) -> ValueLocation {
) -> Option<ValueLocation> {
if is_native {
trace_emit!("\tBL /*C*/ {}({:?})", func, args);
} else {
......@@ -2413,34 +2410,27 @@ impl CodeGenerator for ASMCodeGen {
mangle_name(func)
};
let mut ret = ret;
ret.push(LR.clone());
let asm = format!("BL {}", func);
self.add_asm_call(asm, pe, args, None);
let callsite_symbol = mangle_name(callsite.clone());
self.add_asm_symbolic(directive_globl(callsite_symbol.clone()));
self.add_asm_symbolic(format!("{}:", callsite_symbol.clone()));
ValueLocation::Relocatable(RegGroup::GPR, callsite)
self.internal_call(callsite, asm, pe, args, ret, None, true)
}
fn emit_blr(
&mut self,
callsite: String,
callsite: Option<String>,
func: Reg,
pe: Option<MuName>,
args: Vec<P<Value>>
) -> ValueLocation {
args: Vec<P<Value>>,
ret: Vec<P<Value>>
) -> Option<ValueLocation> {
trace_emit!("\tBLR {}({:?})", func, args);
let mut ret = ret;
ret.push(LR.clone());
let (reg1, id1, loc1) = self.prepare_reg(func, 3 + 1);
let asm = format!("BLR {}", reg1);
self.add_asm_call(asm, pe, args, Some((id1, loc1)));
let callsite_symbol = mangle_name(callsite.clone());
self.add_asm_symbolic(directive_globl(callsite_symbol.clone()));
self.add_asm_symbolic(format!("{}:", callsite_symbol.clone()));
ValueLocation::Relocatable(RegGroup::GPR, callsite)
self.internal_call(callsite, asm, pe, args, ret, Some((id1, loc1)), true)
}
......@@ -2458,23 +2448,35 @@ impl CodeGenerator for ASMCodeGen {
None
);
}
fn emit_b_func(&mut self, func_name: MuName, args: Vec<P<Value>>) {
trace_emit!("\tB {}({:?})", func_name, args);
let asm = format!("/*TAILCALL*/ B {}", mangle_name(func_name.clone()));
let mut uses: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
for arg in args {
uses.insert(arg.id(), vec![]);
fn emit_b_call(
&mut self,
callsite: Option<String>,
func: MuName,
pe: Option<MuName>,
args: Vec<P<Value>>,
ret: Vec<P<Value>>,
is_native: bool,
may_return: bool
) -> Option<ValueLocation> {
if is_native {
trace_emit!("\tB /*C*/ {}({:?})", func, args);
} else {
trace_emit!("\tB {}({:?})", func, args);
}
self.add_asm_inst_internal(
asm,
linked_hashmap!{},
uses,
false,
ASMBranchTarget::Return,
None
);
let func = if is_native {
"/*C*/".to_string() + func.as_str()
} else {
mangle_name(func)
};
let mut ret = ret;
ret.push(LR.clone());
let asm = format!("B {}", func);
self.internal_call(callsite, asm, pe, args, ret, None, may_return)
}
fn emit_b_cond(&mut self, cond: &str, dest_name: MuName) {
trace_emit!("\tB.{} {}", cond, dest_name);
......@@ -2503,35 +2505,25 @@ impl CodeGenerator for ASMCodeGen {
None
);
}
fn emit_br_func(&mut self, func_address: Reg, args: Vec<P<Value>>) {
trace_emit!("\tBR {}({:?})", func_address, args);
let (reg1, id1, loc1) = self.prepare_reg(func_address, 2 + 1);
let asm = format!("/*TAILCALL*/ BR {}", reg1);
fn emit_br_call(
&mut self,
callsite: Option<String>,
func: Reg,
pe: Option<MuName>,
args: Vec<P<Value>>,
ret: Vec<P<Value>>,
may_return: bool
) -> Option<ValueLocation> {
trace_emit!("\tBR {}({:?})", func, args);
let mut ret = ret;
ret.push(LR.clone());
let mut added_id1 = false;
let mut uses: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
for arg in args {
if arg.id() == id1 {
uses.insert(arg.id(), vec![loc1.clone()]);
added_id1 = true;
} else {
uses.insert(arg.id(), vec![]);
}
}
if !added_id1 {
uses.insert(id1, vec![loc1]);
}
self.add_asm_inst_internal(
asm,
linked_hashmap!{},
uses,
false,
ASMBranchTarget::Return,
None
);
let (reg1, id1, loc1) = self.prepare_reg(func, 3 + 1);
let asm = format!("BR {}", reg1);
self.internal_call(callsite, asm, pe, args, ret, Some((id1, loc1)), may_return)
}
fn emit_cbnz(&mut self, src: Reg, dest_name: MuName) {
self.internal_branch_op("CBNZ", src, dest_name);
}
......
......@@ -120,26 +120,45 @@ pub trait CodeGenerator {
// Calls
fn emit_bl(
&mut self,
callsite: String,
callsite: Option<String>,
func: MuName,
pe: Option<MuName>,
args: Vec<P<Value>>,
ret: Vec<P<Value>>,
is_native: bool
) -> ValueLocation;
) -> Option<ValueLocation>;
fn emit_blr(
&mut self,
callsite: String,
callsite: Option<String>,
func: Reg,
pe: Option<MuName>,
args: Vec<P<Value>>
) -> ValueLocation;
// Branches
args: Vec<P<Value>>,
ret: Vec<P<Value>>,
) -> Option<ValueLocation>;
// Branches
fn emit_b(&mut self, dest_name: MuName);
fn emit_b_func(&mut self, func: MuName, args: Vec<P<Value>>); // For tail calls
fn emit_b_cond(&mut self, cond: &str, dest_name: MuName);
fn emit_br(&mut self, dest_address: Reg);
fn emit_br_func(&mut self, func_address: Reg, args: Vec<P<Value>>); // For tail calls
fn emit_b_call(
&mut self,
callsite: Option<String>,
func: MuName,
pe: Option<MuName>,
args: Vec<P<Value>>,
ret: Vec<P<Value>>,
is_native: bool,
may_return: bool
) -> Option<ValueLocation>;
fn emit_br_call(
&mut self,
callsite: Option<String>,
func: Reg,
pe: Option<MuName>,
args: Vec<P<Value>>,
ret: Vec<P<Value>>,
may_return: bool
) -> Option<ValueLocation>;
fn emit_ret(&mut self, src: Reg);
fn emit_cbnz(&mut self, src: Reg, dest_name: MuName);
......
......@@ -23,6 +23,7 @@ use ast::op::*;
use ast::types::*;
use utils::math::align_up;
use utils::POINTER_SIZE;
use utils::WORD_SIZE;
use vm::VM;
use runtime::mm;
use runtime::mm::OBJECT_HEADER_SIZE;
......@@ -1478,7 +1479,6 @@ impl<'a> InstructionSelection {
let tmp_res = self.get_result_value(node, 0);
// load [tl + STACK_OFFSET] -> tmp_res
// WARNING: This assumes that an Option<Box<MuStack>> is actually just a pointer to a MuStack
emit_load_base_offset(
self.backend.as_mut(),
&tmp_res,
......@@ -4216,15 +4216,8 @@ impl<'a> InstructionSelection {
if vm.is_doing_jit() {
unimplemented!()
} else {
let callsite = self.new_callsite_label(cur_node);
self.backend
.emit_bl(callsite.clone(), func_name, None, arg_regs, true);
// assume ccall wont throw exception
// TODO: What if theres an exception block?
self.current_callsites
.push_back((callsite, 0, stack_arg_size));
self.backend.emit_bl(None, func_name, None, arg_regs, CALLER_SAVED_REGS.to_vec(), true);
// record exception block (CCall may have an exception block)
if cur_node.is_some() {
......@@ -4336,80 +4329,130 @@ impl<'a> InstructionSelection {
vm: &VM
) {
let ref ops = inst.ops;
let swapee = self.emit_ireg(&ops[swapee], f_content, f_context, vm);
let new_sp = make_temporary(f_context, ADDRESS_TYPE.clone(), vm);
let cur_stack = make_temporary(
f_context,
if is_kill {
STACKREF_TYPE.clone()
// Calsite label, that will be used to mark the resumption pointer when
// the current stack is swapped back
let callsite_label = if !is_kill {
Some(self.new_callsite_label(Some(node)))
} else {
None
};
// Compute all the arguments...
let mut arg_values = vec![];
let arg_nodes = args.iter().map(|a| ops[*a].clone()).collect::<Vec<_>>();
for ref arg in &arg_nodes {
if match_node_imm(arg) {
arg_values.push(node_imm_to_value(arg))
} else if self.match_reg(arg) {
arg_values.push(self.emit_reg(arg, f_content, f_context, vm))
} else {
ADDRESS_TYPE.clone()
},
unimplemented!()
};
}
let tl = self.emit_get_threadlocal(f_context, vm);
let cur_stackref = make_temporary(f_context, STACKREF_TYPE.clone(), vm);
// Load the current stackref
emit_load_base_offset(
self.backend.as_mut(),
&cur_stackref,
&tl,
*thread::STACK_OFFSET as i64,
f_context,
vm
);
// Prepare for stack swapping
self.emit_runtime_entry(
if is_kill {
&entrypoints::PREPARE_SWAPSTACK_KILL
} else {
&entrypoints::PREPARE_SWAPSTACK_RET
},
vec![swapee.clone()],
Some(vec![new_sp.clone(), cur_stack.clone()]),
Some(node),
// Store the new stackref
let swapee = self.emit_ireg(&ops[swapee], f_content, f_context, vm);
emit_store_base_offset(
self.backend.as_mut(),
&tl,
*thread::STACK_OFFSET as i64,
&swapee,
f_context,
vm
);
// Compute the locations of return values, and how much space needs to be added to the stack
let res_tys = match inst.value {
Some(ref values) => values.iter().map(|v| v.ty.clone()).collect::<Vec<_>>(),
None => vec![]
};
let (_, res_locs, res_stack_size) = compute_argument_locations(&res_tys, &SP, 0, &vm);
if !is_kill {
// Load the callsite's address into LR
let callsite_value = make_value_symbolic(callsite_label.as_ref().unwrap().clone(),
false, &VOID_TYPE, vm);
self.backend.emit_adr(&LR, &callsite_value);
// Reserve space on the stack for the return values of the swap stack
emit_sub_u64(self.backend.as_mut(), &SP, &SP, res_stack_size as u64);
self.backend.emit_push_pair(&LR, &FP, &SP);
let cur_sp = make_temporary(f_context, STACKREF_TYPE.clone(), vm);
self.backend.emit_mov(&cur_sp, &SP);
// Save the current SP
emit_store_base_offset(
self.backend.as_mut(),
&cur_stackref,
*thread::MUSTACK_SP_OFFSET as i64,
&cur_sp,
f_context,
vm
);
}
if is_exception {
assert!(args.len() == 1);
// Load the new sp from the swapee
// (Note: we cant load directly into the SP, so we have to use a temporary)
let new_sp = make_temporary(f_context, ADDRESS_TYPE.clone(), vm);
emit_load_base_offset(
self.backend.as_mut(),
&new_sp,
&swapee,
*thread::MUSTACK_SP_OFFSET as i64,
f_context,
vm
);
// Swap to the new stack
self.backend.emit_mov(&SP, &new_sp);
// TODO: MAKE SURE THE REGISTER ALLOCATOR DOSN'T DO SPILLING AFTER THIS POINT
if is_kill {
// Kill the old stack
self.emit_runtime_entry(
&entrypoints::KILL_STACK,
vec![cur_stackref],
None,
Some(node),
f_context,
vm
);
}
let exc = self.emit_ireg(&ops[args[0]], f_content, f_context, vm);
if is_exception {
assert!(arg_values.len() == 1);
//Reserve temporary space for the exception throwing routine
emit_sub_u64(self.backend.as_mut(), &SP, &SP, (WORD_SIZE*CALLEE_SAVED_COUNT) as u64);
// Swap the stack and throw the given exception
// Throw the exception
self.emit_runtime_entry(
// TODO: Handle exception resumption from this functioncall
if is_kill {
&entrypoints::SWAPSTACK_KILL_THROW
} else {
&entrypoints::SWAPSTACK_RET_THROW
},
vec![exc.clone(), new_sp.clone(), cur_stack.clone()],
&entrypoints::THROW_EXCEPTION_INTERNAL,
vec![arg_values[0].clone(), new_sp.clone()],
Some(vec![]),
Some(node),
f_context,
vm
);
} else {
// Prepare arguments
let mut arg_values = vec![];
let arg_nodes = args.iter().map(|a| ops[*a].clone()).collect::<Vec<_>>();
for ref arg in &arg_nodes {
if match_node_imm(arg) {
arg_values.push(node_imm_to_value(arg))
} else if self.match_reg(arg) {
arg_values.push(self.emit_reg(arg, f_content, f_context, vm))
} else {
unimplemented!()
};
}
self.backend.emit_pop_pair(&FP, &LR, &SP);
// Emit precall convention
let arg_tys = arg_nodes.iter().map(|a| a.ty()).collect::<Vec<_>>();
let (stack_arg_size, mut arg_regs) = self.emit_precall_convention(
let (stack_arg_size, arg_regs) = self.emit_precall_convention(
&new_sp,
// The frame contains space for all callee saved registers (including the FP and LR)
((CALLEE_SAVED_COUNT + 2) * POINTER_SIZE) as isize,
......@@ -4421,12 +4464,6 @@ impl<'a> InstructionSelection {
vm
);
// Pass new_sp and cur_stack in X9 and X10 respectivley
self.backend.emit_mov(&X9, &new_sp);
self.backend.emit_mov(&X10, &cur_stack);
arg_regs.push(X9.clone());
arg_regs.push(X10.clone());
let potentially_excepting = {
if resumption.is_some() {
let target_id = resumption.unwrap().exn_dest.target;
......@@ -4441,18 +4478,13 @@ impl<'a> InstructionSelection {
if vm.is_doing_jit() {
unimplemented!()
} else {
let callsite = self.new_callsite_label(Some(node));
self.backend.emit_bl(
callsite,
if is_kill {
"muentry_swapstack_kill_pass".to_string()
} else {
"muentry_swapstack_ret_pass".to_string()
},
potentially_excepting,
arg_regs,
true
)
let caller_saved = if is_kill {
vec![]
} else {
ALL_USABLE_MACHINE_REGS.to_vec()
};
self.backend.emit_br_call(callsite_label, &LR, potentially_excepting, arg_regs, caller_saved, false)
}
};
......@@ -4460,11 +4492,9 @@ impl<'a> InstructionSelection {
let ref exn_dest = resumption.as_ref().unwrap().exn_dest;
let target_block = exn_dest.target;
self.current_callsites
.push_back((callsite.to_relocatable(), target_block, stack_arg_size));
} else {
self.current_callsites
.push_back((callsite.to_relocatable(), 0, stack_arg_size));
self.current_callsites.push_back((callsite.unwrap().to_relocatable(), target_block, stack_arg_size));
} else if !is_kill {
self.current_callsites.push_back((callsite.unwrap().to_relocatable(), 0, stack_arg_size));
}
}
......@@ -4567,10 +4597,10 @@ impl<'a> InstructionSelection {
let target_id = self.node_funcref_const_to_id(func);
let funcs = vm.funcs().read().unwrap();
let target = funcs.get(&target_id).unwrap().read().unwrap();
self.backend.emit_b_func(target.name(), arg_regs);
self.backend.emit_b_call(None, target.name(), None, arg_regs, vec![], false, false);
} else {
let target = self.emit_ireg(func, f_content, f_context, vm);
self.backend.emit_br_func(&target, arg_regs);
self.backend.emit_br_call(None, &target, None, arg_regs, vec![], false);
}
} else {
// Emit a branch with link (i.e. a call)
......@@ -4585,18 +4615,18 @@ impl<'a> InstructionSelection {
} else {
let callsite = self.new_callsite_label(Some(cur_node));
self.backend.emit_bl(
callsite,
Some(callsite),
target.name(),
potentially_excepting,
arg_regs,
CALLER_SAVED_REGS.to_vec(),
false
)
).unwrap()
}
} else {
let target = self.emit_ireg(func, f_content, f_context, vm);
let callsite = self.new_callsite_label(Some(cur_node));
self.backend
.emit_blr(callsite, &target, potentially_excepting, arg_regs)
self.backend.emit_blr(Some(callsite), &target, potentially_excepting, arg_regs, CALLER_SAVED_REGS.to_vec()).unwrap()
}
};
......
......@@ -674,6 +674,54 @@ lazy_static! {
D15.clone()
];
pub static ref CALLER_SAVED_REGS : [P<Value>; 42] = [
X0.clone(),
X1.clone(),
X2.clone(),
X3.clone(),
X4.clone(),
X5.clone(),
X6.clone(),
X7.clone(),
X8.clone(),
X9.clone(),
X10.clone(),
X11.clone(),
X12.clone(),
X13.clone(),
X14.clone(),
X15.clone(),
X16.clone(),
X17.clone(),
//X18.clone(), // Platform Register
D0.clone(),
D1.clone(),
D2.clone(),
D3.clone(),
D4.clone(),
D5.clone(),
D6.clone(),
D7.clone(),
D16.clone(),
D17.clone(),
D18.clone(),
D19.clone(),
D20.clone(),
D21.clone(),
D22.clone(),
D23.clone(),
D24.clone(),
D25.clone(),
D26.clone(),
D27.clone(),
D28.clone(),
D29.clone(),