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

Improved swapstack implementation (WIP)

parent c24b8896
...@@ -176,8 +176,7 @@ impl Instruction { ...@@ -176,8 +176,7 @@ impl Instruction {
CommonInst_GetAddr(_) | CommonInst_GetAddr(_) |
PrintHex(_) | PrintHex(_) |
SetRetval(_) | SetRetval(_) |
KillStack(_) | KillStack(_) => true,
CurrentStack => true,
BinOp(_, _, _) | BinOp(_, _, _) |
BinOpWithStatus(_, _, _, _) | BinOpWithStatus(_, _, _, _) |
CmpOp(_, _, _) | CmpOp(_, _, _) |
...@@ -198,7 +197,8 @@ impl Instruction { ...@@ -198,7 +197,8 @@ impl Instruction {
CommonInst_Tr64ToInt(_) | CommonInst_Tr64ToInt(_) |
CommonInst_Tr64ToRef(_) | CommonInst_Tr64ToRef(_) |
CommonInst_Tr64ToTag(_) | CommonInst_Tr64ToTag(_) |
Move(_) => false Move(_) |
CurrentStack => false
} }
} }
......
...@@ -1037,52 +1037,6 @@ impl ASMCodeGen { ...@@ -1037,52 +1037,6 @@ impl ASMCodeGen {
self.cur_mut().code.push(ASMInst::symbolic(code)); 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( fn add_asm_inst(
&mut self, &mut self,
code: String, code: String,
...@@ -2162,6 +2116,48 @@ impl ASMCodeGen { ...@@ -2162,6 +2116,48 @@ impl ASMCodeGen {
self.add_asm_inst(asm, ignore_zero_register(id1, vec![loc1]), uses, false) 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) { fn emit_ldr_spill(&mut self, dest: Reg, src: Mem) {
self.internal_load("LDR", dest, src, false, true, false); self.internal_load("LDR", dest, src, false, true, false);
} }
...@@ -2395,12 +2391,13 @@ impl CodeGenerator for ASMCodeGen { ...@@ -2395,12 +2391,13 @@ impl CodeGenerator for ASMCodeGen {
fn emit_bl( fn emit_bl(
&mut self, &mut self,
callsite: String, callsite: Option<String>,
func: MuName, func: MuName,
pe: Option<MuName>, pe: Option<MuName>,
args: Vec<P<Value>>, args: Vec<P<Value>>,
ret: Vec<P<Value>>,
is_native: bool is_native: bool
) -> ValueLocation { ) -> Option<ValueLocation> {
if is_native { if is_native {
trace_emit!("\tBL /*C*/ {}({:?})", func, args); trace_emit!("\tBL /*C*/ {}({:?})", func, args);
} else { } else {
...@@ -2413,34 +2410,27 @@ impl CodeGenerator for ASMCodeGen { ...@@ -2413,34 +2410,27 @@ impl CodeGenerator for ASMCodeGen {
mangle_name(func) mangle_name(func)
}; };
let mut ret = ret;
ret.push(LR.clone());
let asm = format!("BL {}", func); let asm = format!("BL {}", func);
self.add_asm_call(asm, pe, args, None); self.internal_call(callsite, asm, pe, args, ret, None, true)
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)
} }
fn emit_blr( fn emit_blr(
&mut self, &mut self,
callsite: String, callsite: Option<String>,
func: Reg, func: Reg,
pe: Option<MuName>, pe: Option<MuName>,
args: Vec<P<Value>> args: Vec<P<Value>>,
) -> ValueLocation { ret: Vec<P<Value>>
) -> Option<ValueLocation> {
trace_emit!("\tBLR {}({:?})", func, args); trace_emit!("\tBLR {}({:?})", func, args);
let mut ret = ret;
ret.push(LR.clone());
let (reg1, id1, loc1) = self.prepare_reg(func, 3 + 1); let (reg1, id1, loc1) = self.prepare_reg(func, 3 + 1);
let asm = format!("BLR {}", reg1); let asm = format!("BLR {}", reg1);
self.add_asm_call(asm, pe, args, Some((id1, loc1))); self.internal_call(callsite, asm, pe, args, ret, Some((id1, loc1)), true)
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)
} }
...@@ -2458,23 +2448,35 @@ impl CodeGenerator for ASMCodeGen { ...@@ -2458,23 +2448,35 @@ impl CodeGenerator for ASMCodeGen {
None 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())); fn emit_b_call(
let mut uses: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new(); &mut self,
for arg in args { callsite: Option<String>,
uses.insert(arg.id(), vec![]); 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, let func = if is_native {
linked_hashmap!{}, "/*C*/".to_string() + func.as_str()
uses, } else {
false, mangle_name(func)
ASMBranchTarget::Return, };
None
); 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) { fn emit_b_cond(&mut self, cond: &str, dest_name: MuName) {
trace_emit!("\tB.{} {}", cond, dest_name); trace_emit!("\tB.{} {}", cond, dest_name);
...@@ -2503,35 +2505,25 @@ impl CodeGenerator for ASMCodeGen { ...@@ -2503,35 +2505,25 @@ impl CodeGenerator for ASMCodeGen {
None 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 (reg1, id1, loc1) = self.prepare_reg(func, 3 + 1);
let mut uses: LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new(); let asm = format!("BR {}", reg1);
for arg in args { self.internal_call(callsite, asm, pe, args, ret, Some((id1, loc1)), may_return)
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
);
} }
fn emit_cbnz(&mut self, src: Reg, dest_name: MuName) { fn emit_cbnz(&mut self, src: Reg, dest_name: MuName) {
self.internal_branch_op("CBNZ", src, dest_name); self.internal_branch_op("CBNZ", src, dest_name);
} }
......
...@@ -120,26 +120,45 @@ pub trait CodeGenerator { ...@@ -120,26 +120,45 @@ pub trait CodeGenerator {
// Calls // Calls
fn emit_bl( fn emit_bl(
&mut self, &mut self,
callsite: String, callsite: Option<String>,
func: MuName, func: MuName,
pe: Option<MuName>, pe: Option<MuName>,
args: Vec<P<Value>>, args: Vec<P<Value>>,
ret: Vec<P<Value>>,
is_native: bool is_native: bool
) -> ValueLocation; ) -> Option<ValueLocation>;
fn emit_blr( fn emit_blr(
&mut self, &mut self,
callsite: String, callsite: Option<String>,
func: Reg, func: Reg,
pe: Option<MuName>, pe: Option<MuName>,
args: Vec<P<Value>> args: Vec<P<Value>>,
) -> ValueLocation; ret: Vec<P<Value>>,
) -> Option<ValueLocation>;
// Branches // Branches
fn emit_b(&mut self, dest_name: MuName); 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_b_cond(&mut self, cond: &str, dest_name: MuName);
fn emit_br(&mut self, dest_address: Reg); 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_ret(&mut self, src: Reg);
fn emit_cbnz(&mut self, src: Reg, dest_name: MuName); fn emit_cbnz(&mut self, src: Reg, dest_name: MuName);
......
...@@ -674,6 +674,54 @@ lazy_static! { ...@@ -674,6 +674,54 @@ lazy_static! {
D15.clone() 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(),
D30.clone(),
D31.clone()
];
pub static ref ALL_USABLE_GPRS : Vec<P<Value>> = vec![ pub static ref ALL_USABLE_GPRS : Vec<P<Value>> = vec![
X0.clone(), X0.clone(),
X1.clone(), X1.clone(),
......
...@@ -67,8 +67,7 @@ lazy_static! { ...@@ -67,8 +67,7 @@ lazy_static! {
String::from("muentry_thread_exit")), String::from("muentry_thread_exit")),
jit: RwLock::new(None), jit: RwLock::new(None),
}; };
}
lazy_static! {
// impl/decl: thread.rs // impl/decl: thread.rs
pub static ref NEW_STACK: RuntimeEntrypoint = RuntimeEntrypoint { pub static ref NEW_STACK: RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig{ sig: P(MuFuncSig{
...@@ -180,6 +179,16 @@ lazy_static! { ...@@ -180,6 +179,16 @@ lazy_static! {
jit: RwLock::new(None), jit: RwLock::new(None),
}; };
pub static ref THROW_EXCEPTION_INTERNAL : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig {
hdr: MuEntityHeader::unnamed(ir::new_internal_id()),
ret_tys: vec![],
arg_tys: vec![ADDRESS_TYPE.clone(), ADDRESS_TYPE.clone()]
}),
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_throw_exception_internal")),
jit: RwLock::new(None),
};
// impl/decl: math.rs // impl/decl: math.rs
pub static ref FREM32 : RuntimeEntrypoint = RuntimeEntrypoint { pub static ref FREM32 : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig{ sig: P(MuFuncSig{
...@@ -342,48 +351,4 @@ lazy_static! { ...@@ -342,48 +351,4 @@ lazy_static! {
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_mem_zero")), aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_mem_zero")),
jit: RwLock::new(None) jit: RwLock::new(None)
}; };
// impl/decl: thread.rs
pub static ref PREPARE_SWAPSTACK_RET : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig {
hdr: MuEntityHeader::unnamed(ir::new_internal_id()),
ret_tys: vec![ADDRESS_TYPE.clone(), ADDRESS_TYPE.clone()],
arg_tys: vec![STACKREF_TYPE.clone()]
}),
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_prepare_swapstack_ret")),
jit: RwLock::new(None)
};
// impl/decl: thread.rs
pub static ref PREPARE_SWAPSTACK_KILL : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig {
hdr: MuEntityHeader::unnamed(ir::new_internal_id()),
ret_tys: vec![ADDRESS_TYPE.clone(), STACKREF_TYPE.clone()],
arg_tys: vec![STACKREF_TYPE.clone()]
}),
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_prepare_swapstack_kill")),
jit: RwLock::new(None)
};
// impl/decl: thread.rs
pub static ref SWAPSTACK_RET_THROW : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig {
hdr: MuEntityHeader::unnamed(ir::new_internal_id()),
ret_tys: vec![],
arg_tys: vec![REF_VOID_TYPE.clone(), ADDRESS_TYPE.clone(), ADDRESS_TYPE.clone()]
}),
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_swapstack_ret_throw")),
jit: RwLock::new(None)
};
// impl/decl: thread.rs
pub static ref SWAPSTACK_KILL_THROW : RuntimeEntrypoint = RuntimeEntrypoint {
sig: P(MuFuncSig {
hdr: MuEntityHeader::unnamed(ir::new_internal_id()),
ret_tys: vec![],
arg_tys: vec![REF_VOID_TYPE.clone(), ADDRESS_TYPE.clone(), STACKREF_TYPE.clone()]
}),
aot: ValueLocation::Relocatable(RegGroup::GPR, String::from("muentry_swapstack_kill_throw")),
jit: RwLock::new(None)
};
} }
...@@ -90,7 +90,6 @@ pub extern "C" fn throw_exception_internal(exception_obj: Address, frame_cursor: ...@@ -90,7 +90,6 @@ pub extern "C" fn throw_exception_internal(exception_obj: Address, frame_cursor:
}; };
// Check for a catch block at this callsite // Check for a catch block at this callsite
// (there won't be one on the first iteration of this loop)
if callsite_info.exceptional_destination.is_some() { if callsite_info.exceptional_destination.is_some() {
catch_address = callsite_info.exceptional_destination.unwrap(); catch_address = callsite_info.exceptional_destination.unwrap();
trace!("Found catch block: 0x{:x}", catch_address); trace!("Found catch block: 0x{:x}", catch_address);
......
...@@ -45,59 +45,46 @@ begin_func exception_restore ...@@ -45,59 +45,46 @@ begin_func exception_restore
BR X0 BR X0
end_func exception_restore end_func exception_restore
# Swapstack internals # starts a muthread that passes values to the target
.macro stack_pass # muthread_start_normal(new_sp: Address, old_sp_loc: Address)
# On the new stack, reverse the above # X0 , X1
pop_callee_saved begin_func muthread_start_normal
exit_frame
RET
.endm
.macro stack_throw
# The new stack will have the same layout as the stack when muentry_throw_exception
# calls throw_exception_internal, so we can do that directly here
# Add the total size pushed by 'push_callee_saved' to get the FP for the new stack
ADD FP, SP, 144
MOV X1, FP
BL throw_exception_internal
.endm
.macro stack_ret old_sp
enter_frame enter_frame
push_callee_saved push_callee_saved
// Save the current SP to old_sp_loc
MOV X11, SP MOV X11, SP
STR X11, [\old_sp] STR X11, [X1]
.endm
.macro stack_kill old_stack // Swap to the new stack
mov_args_to_callee_saved MOV SP, X0
MOV X0, \old_stack
BL muentry_kill_stack
mov_callee_saved_to_args
.endm
# starts a muthread that passes values to the target (with the given memory area containg values of argument registers) // Load the argument registers from the new stack
# (new_sp: Address, old_sp_loc: Address) load_arguments SP
# X0 X1,
begin_func muthread_start_pass // Jump to the new stack
stack_ret X1 exit_frame
MOV X9, X0 # X1 will be overriden by the next instructions BR LR
load_arguments X9 end_func muthread_start_normal
MOV SP, X9
stack_pass
end_func muthread_start_pass
# Same as muentry_swapstack_ret_throw # starts a muthread that passes values to the target
# muthread_start_throw(exception: Address, new_sp: Address, old_sp_loc: &mut Adress) # muthread_start_throw(exception: Address, new_sp: Address, old_sp_loc: &mut Adress)
# X0 X1 X2 # X0 X1 X2
begin_func muthread_start_throw begin_func muthread_start_exceptional
stack_ret X2 enter_frame
push_callee_saved
// Save the current SP to old_sp_loc
MOV X11, SP
STR X11, [X1]
// Swap to the new stack
MOV SP, X1 MOV SP, X1
stack_throw SUB SP, SP, #144 // Alocate space for callee saved registers
end_func muthread_start_throw B throw_exception_internal
// We won't be coming back...
end_func muthread_start_exceptional
# restores the thread # restores the thread
# (new_sp: Address) # (new_sp: Address)
...@@ -105,41 +92,9 @@ end_func muthread_start_throw ...@@ -105,41 +92,9 @@ end_func muthread_start_throw
begin_func muentry_thread_exit begin_func muentry_thread_exit
# Rust code will be responsible for actually killing the stack # Rust code will be responsible for actually killing the stack
MOV SP, X0 MOV SP, X0
stack_pass
end_func muentry_thread_exit
# swap to the new stack whilst passing values and saving the old stack
# muentry_swapstack_ret_pass(new_stack args..., new_sp: Address, old_sp_loc: &mut Adress)
# X0 ... X7 [x8] X9 X10
begin_func muentry_swapstack_ret_pass
stack_ret X10
MOV SP, X9
stack_pass
end_func muentry_swapstack_ret_pass
# Same as swapstack_ret_pass except will throw an exception to the new stack instead of passing values
# muentry_swapstack_ret_throw(exception: Address, new_sp: Address, old_sp_loc: &mut Adress)
# X0 X1 X2
begin_func muentry_swapstack_ret_throw
stack_ret X2
MOV SP, X1
stack_throw
end_func muentry_swapstack_ret_throw
# swap to the new stack whilst passing values and killing the old stack // Do the inverse of 'muthread_*'
# muentry_swapstack_kill_pass(new_stack args..., new_sp: Address, old_stack: *mut MuStack) pop_callee_saved
# X0 ... X7 [x8] X9 X10 exit_frame
begin_func muentry_swapstack_kill_pass BR LR
MOV SP, X9 end_func muentry_thread_exit
stack_kill X10 \ No newline at end of file
stack_pass
end_func muentry_swapstack_kill_pass
# Same as muentry_swapstack_kill_pass except will throw an exception to the new stack instead of passing values
# muentry_swapstack_kill_throw(exception: Address, new_sp: Address, old_stack: *mut MuStack)
# X0 X1 X2
begin_func muentry_swapstack_kill_throw
MOV SP, X1
stack_kill X2
stack_throw
end_func muentry_swapstack_kill_throw
...@@ -18,13 +18,11 @@ use ast::types::*; ...@@ -18,13 +18,11 @@ use ast::types::*;
use vm::VM; use vm::VM;
use runtime::ValueLocation; use runtime::ValueLocation;