WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.7% of users enabled 2FA.

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

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