Commit c6bdd727 authored by qinsoon's avatar qinsoon

[wip] let backend know about exception flow. also add one more test to see if…

[wip] let backend know about exception flow. also add one more test to see if callee-saved register is restored
during catching exception, the test failed (and I marked it as ignored).
parent 46a10665
Pipeline #319 failed with stage
in 41 minutes and 19 seconds
......@@ -2,6 +2,7 @@ use ir::*;
use ptr::*;
use types::*;
use op::*;
use ir_semantics;
use utils::vec_utils;
......@@ -68,6 +69,63 @@ impl Clone for Instruction {
}
impl Instruction {
pub fn has_exception_clause(&self) -> bool {
ir_semantics::is_potentially_excepting_instruction(&self.v)
}
pub fn get_exception_target(&self) -> Option<MuID> {
use inst::Instruction_::*;
match self.v {
Watchpoint {ref resume, ..}
| Call {ref resume, ..}
| CCall {ref resume, ..}
| SwapStack {ref resume, ..}
| ExnInstruction {ref resume, ..} => {
Some(resume.exn_dest.target)
},
BinOp(_, _, _)
| BinOpWithStatus(_, _, _, _)
| CmpOp(_, _, _)
| ConvOp{..}
| ExprCall{..}
| ExprCCall{..}
| Load{..}
| Store{..}
| CmpXchg{..}
| AtomicRMW{..}
| New(_)
| AllocA(_)
| NewHybrid(_, _)
| AllocAHybrid(_, _)
| NewStack(_)
| NewThread(_, _)
| NewThreadExn(_, _)
| NewFrameCursor(_)
| GetIRef(_)
| GetFieldIRef{..}
| GetElementIRef{..}
| ShiftIRef{..}
| GetVarPartIRef{..}
| Fence(_)
| Return(_)
| ThreadExit
| Throw(_)
| TailCall(_)
| Branch1(_)
| Branch2{..}
| Select{..}
| WPBranch{..}
| Switch{..}
| CommonInst_GetThreadLocal
| CommonInst_SetThreadLocal(_)
| CommonInst_Pin(_)
| CommonInst_Unpin(_)
| Move(_)
| PrintHex(_) => None
}
}
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
self.v.debug_str(ops)
}
......
......@@ -103,3 +103,53 @@ pub fn has_side_effect(inst: &Instruction_) -> bool {
&PrintHex(_) => true
}
}
pub fn is_potentially_excepting_instruction(inst: &Instruction_) -> bool {
match inst {
&Watchpoint{..}
| &Call{..}
| &CCall{..}
| &SwapStack{..}
| &ExnInstruction{..} => true,
&BinOp(_, _, _)
| &BinOpWithStatus(_, _, _, _)
| &CmpOp(_, _, _)
| &ConvOp{..}
| &ExprCall{..}
| &ExprCCall{..}
| &Load{..}
| &Store{..}
| &CmpXchg{..}
| &AtomicRMW{..}
| &New(_)
| &AllocA(_)
| &NewHybrid(_, _)
| &AllocAHybrid(_, _)
| &NewStack(_)
| &NewThread(_, _)
| &NewThreadExn(_, _)
| &NewFrameCursor(_)
| &GetIRef(_)
| &GetFieldIRef{..}
| &GetElementIRef{..}
| &ShiftIRef{..}
| &GetVarPartIRef{..}
| &Fence(_)
| &Return(_)
| &ThreadExit
| &Throw(_)
| &TailCall(_)
| &Branch1(_)
| &Branch2{..}
| &Select{..}
| &WPBranch{..}
| &Switch{..}
| &CommonInst_GetThreadLocal
| &CommonInst_SetThreadLocal(_)
| &CommonInst_Pin(_)
| &CommonInst_Unpin(_)
| &Move(_)
| &PrintHex(_) => false
}
}
\ No newline at end of file
......@@ -376,6 +376,35 @@ impl ASMCode {
panic!("conditional branch does not have a fallthrough target");
}
},
ASMBranchTarget::PotentiallyExcepting(ref target) => {
// may trigger exception and jump to target - similar as conditional branch
let target_n = self.blocks.get(target).unwrap().start_inst;
// cur inst's succ is target
asm[i].succs.push(target_n);
if TRACE_CFA {
trace!("inst {}: is potentially excepting to {}", i, target);
trace!("inst {}: excepting target index is {}", i, target_n);
trace!("inst {}: set SUCCS as excepting target {}", i, target_n);
}
asm[target_n].preds.push(i);
if let Some(next_inst) = ASMCode::find_next_inst(i, asm) {
// cur succ is next inst
asm[i].succs.push(next_inst);
// next inst's pred is cur
asm[next_inst].preds.push(i);
if TRACE_CFA {
trace!("inst {}: SET SUCCS as PEI fallthrough target {}", i, next_inst);
}
} else {
panic!("PEI does not have a fallthrough target");
}
},
ASMBranchTarget::Return => {
if TRACE_CFA {
trace!("inst {}: is a return", i);
......@@ -436,6 +465,27 @@ impl ASMCode {
}
}
fn find_last_inst(i: usize, asm: &Vec<ASMInst>) -> Option<usize> {
if i == 0 {
None
} else {
let mut cur = i;
while cur >= 0 {
if !asm[cur].is_symbol {
return Some(cur);
}
if cur == 0 {
return None;
} else {
cur -= 1;
}
}
None
}
}
fn add_frame_size_patchpoint(&mut self, patchpoint: ASMLocation) {
self.frame_size_patchpoints.push(patchpoint);
}
......@@ -680,6 +730,10 @@ impl MachineCode for ASMCode {
let mut ret = vec![];
for inst in self.code.iter() {
if !inst.is_symbol {
ret.append(&mut "\t".to_string().into_bytes());
}
ret.append(&mut inst.code.clone().into_bytes());
ret.append(&mut "\n".to_string().into_bytes());
}
......@@ -744,6 +798,14 @@ impl MachineCode for ASMCode {
None => None
}
}
fn get_next_inst(&self, index: usize) -> Option<usize> {
ASMCode::find_next_inst(index, &self.code)
}
fn get_last_inst(&self, index: usize) -> Option<usize> {
ASMCode::find_last_inst(index, &self.code)
}
}
#[derive(Clone, Debug)]
......@@ -751,6 +813,7 @@ enum ASMBranchTarget {
None,
Conditional(MuName),
Unconditional(MuName),
PotentiallyExcepting(MuName),
Return
}
......@@ -930,7 +993,7 @@ impl ASMCodeGen {
regs.map(|x| self.prepare_machine_reg(x)).collect()
}
fn add_asm_call(&mut self, code: String) {
fn add_asm_call(&mut self, code: String, potentially_excepting: Option<MuName>) {
// a call instruction will use all the argument registers
// do not need
let uses : LinkedHashMap<MuID, Vec<ASMLocation>> = LinkedHashMap::new();
......@@ -960,7 +1023,13 @@ impl ASMCodeGen {
}
}
self.add_asm_inst(code, defines, uses, false);
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_ret(&mut self, code: String) {
......@@ -2703,11 +2772,11 @@ impl CodeGenerator for ASMCodeGen {
}
#[cfg(target_os = "macos")]
fn emit_call_near_rel32(&mut self, callsite: String, func: MuName) -> ValueLocation {
fn emit_call_near_rel32(&mut self, callsite: String, func: MuName, pe: Option<MuName>) -> ValueLocation {
trace!("emit: call {}", func);
let asm = format!("call {}", symbol(func));
self.add_asm_call(asm);
self.add_asm_call(asm, pe);
let callsite_symbol = symbol(callsite.clone());
self.add_asm_symbolic(directive_globl(callsite_symbol.clone()));
......@@ -2718,13 +2787,13 @@ impl CodeGenerator for ASMCodeGen {
#[cfg(target_os = "linux")]
// generating Position-Independent Code using PLT
fn emit_call_near_rel32(&mut self, callsite: String, func: MuName) -> ValueLocation {
fn emit_call_near_rel32(&mut self, callsite: String, func: MuName, pe: Option<MuName>) -> ValueLocation {
trace!("emit: call {}", func);
let func = func + "@PLT";
let asm = format!("call {}", symbol(func));
self.add_asm_call(asm);
self.add_asm_call(asm, pe);
let callsite_symbol = symbol(callsite.clone());
self.add_asm_symbolic(directive_globl(callsite_symbol.clone()));
......@@ -2733,12 +2802,12 @@ impl CodeGenerator for ASMCodeGen {
ValueLocation::Relocatable(RegGroup::GPR, callsite)
}
fn emit_call_near_r64(&mut self, callsite: String, func: &P<Value>) -> ValueLocation {
fn emit_call_near_r64(&mut self, callsite: String, func: &P<Value>, pe: Option<MuName>) -> ValueLocation {
trace!("emit: call {}", func);
unimplemented!()
}
fn emit_call_near_mem64(&mut self, callsite: String, func: &P<Value>) -> ValueLocation {
fn emit_call_near_mem64(&mut self, callsite: String, func: &P<Value>, pe: Option<MuName>) -> ValueLocation {
trace!("emit: call {}", func);
unimplemented!()
}
......
......@@ -163,9 +163,9 @@ pub trait CodeGenerator {
fn emit_jl(&mut self, dest: MuName);
fn emit_jle(&mut self, dest: MuName);
fn emit_call_near_rel32(&mut self, callsite: String, func: MuName) -> ValueLocation;
fn emit_call_near_r64(&mut self, callsite: String, func: &P<Value>) -> ValueLocation;
fn emit_call_near_mem64(&mut self, callsite: String, func: &P<Value>) -> ValueLocation;
fn emit_call_near_rel32(&mut self, callsite: String, func: MuName, pe: Option<MuName>) -> ValueLocation;
fn emit_call_near_r64 (&mut self, callsite: String, func: &P<Value>, pe: Option<MuName>) -> ValueLocation;
fn emit_call_near_mem64(&mut self, callsite: String, func: &P<Value>, pe: Option<MuName>) -> ValueLocation;
fn emit_ret(&mut self);
......
......@@ -2384,7 +2384,7 @@ impl <'a> InstructionSelection {
unimplemented!()
} else {
let callsite = self.new_callsite_label(cur_node);
self.backend.emit_call_near_rel32(callsite, func_name);
self.backend.emit_call_near_rel32(callsite, func_name, None); // assume ccall wont throw exception
// record exception block (CCall may have an exception block)
if cur_node.is_some() {
......@@ -2512,6 +2512,16 @@ impl <'a> InstructionSelection {
}
}
let stack_arg_size = self.emit_precall_convention(&arg_values, vm);
// check if this call has exception clause - need to tell backend about this
let potentially_excepting = {
if resumption.is_some() {
let target_id = resumption.unwrap().exn_dest.target;
Some(f_content.get_block(target_id).name().unwrap())
} else {
None
}
};
trace!("generating call inst");
// check direct call or indirect
......@@ -2525,18 +2535,18 @@ impl <'a> InstructionSelection {
unimplemented!()
} else {
let callsite = self.new_callsite_label(Some(cur_node));
self.backend.emit_call_near_rel32(callsite, target.name().unwrap())
self.backend.emit_call_near_rel32(callsite, target.name().unwrap(), potentially_excepting)
}
} else if self.match_ireg(func) {
let target = self.emit_ireg(func, f_content, f_context, vm);
let callsite = self.new_callsite_label(Some(cur_node));
self.backend.emit_call_near_r64(callsite, &target)
self.backend.emit_call_near_r64(callsite, &target, potentially_excepting)
} else if self.match_mem(func) {
let target = self.emit_mem(func, vm);
let callsite = self.new_callsite_label(Some(cur_node));
self.backend.emit_call_near_mem64(callsite, &target)
self.backend.emit_call_near_mem64(callsite, &target, potentially_excepting)
} else {
unimplemented!()
}
......
......@@ -271,9 +271,15 @@ fn local_liveness_analysis (cf: &mut CompiledFunction) -> LinkedHashMap<String,
None => panic!("cannot find range for block {}", block)
};
trace!("Block {}: start_inst={}, end_inst(inclusive)={}", block, range.start, range.end-1);
start_inst_map.insert(range.start, block);
end_inst_map.insert(range.end - 1, block);
let first_inst = range.start;
let last_inst = match mc.get_last_inst(range.end) {
Some(last) => last,
None => panic!("cannot find last instruction in block {}, this block contains no instruction?", block)
};
trace!("Block {}: start_inst={}, end_inst(inclusive)={}", block, first_inst, last_inst);
start_inst_map.insert(first_inst, block);
end_inst_map.insert(last_inst, block);
}
// local liveness analysis
......@@ -324,7 +330,7 @@ fn local_liveness_analysis (cf: &mut CompiledFunction) -> LinkedHashMap<String,
let succs : Vec<String> = {
let mut ret = vec![];
for succ in mc.get_succs(end - 1).into_iter() {
for succ in mc.get_succs(mc.get_last_inst(end).unwrap()).into_iter() {
match start_inst_map.get(succ) {
Some(str) => ret.push(String::from(*str)),
None => {}
......
......@@ -159,6 +159,9 @@ pub trait MachineCode {
fn get_succs(&self, index: usize) -> &Vec<usize>;
fn get_preds(&self, index: usize) -> &Vec<usize>;
fn get_next_inst(&self, index: usize) -> Option<usize>;
fn get_last_inst(&self, index: usize) -> Option<usize>;
fn get_inst_reg_uses(&self, index: usize) -> Vec<MuID>;
fn get_inst_reg_defines(&self, index: usize) -> Vec<MuID>;
......
......@@ -121,6 +121,15 @@ macro_rules! define_block {
body: vec![$($inst), *],
keepalives: None
});
};
(($vm: expr, $fv: ident) $name: ident ($($arg: ident), *) [$exn_arg: ident] {$($inst: ident), *}) => {
$name.content = Some(BlockContent{
args: vec![$($arg.clone_value()), *],
exn_arg: Some($exn_arg.clone_value()),
body: vec![$($inst), *],
keepalives: None
});
}
}
......@@ -383,7 +392,7 @@ macro_rules! inst {
}
});
};
// CALL
// CALL (1 return result)
(($vm: expr, $fv: ident) $name: ident:
$res: ident = CALL ($($op: ident), *) FUNC($func: expr) ($args: expr) $cc: expr,
normal: $norm_dest: ident ($norm_args: expr),
......@@ -411,6 +420,35 @@ macro_rules! inst {
}
});
};
// CALL (no return value)
(($vm: expr, $fv: ident) $name: ident:
CALL ($($op: ident), *) FUNC($func: expr) ($args: expr) $cc: expr,
normal: $norm_dest: ident ($norm_args: expr),
exc: $exc_dest: ident ($exc_args: expr)) => {
let $name = $fv.new_inst(Instruction {
hdr : MuEntityHeader::unnamed($vm.next_id()),
value: None,
ops : RwLock::new(vec![$($op.clone()),*]),
v : Instruction_::Call {
data: CallData {
func: $func,
args: $args,
convention: $cc
},
resume: ResumptionData {
normal_dest: Destination {
target: $norm_dest.id(),
args : $norm_args
},
exn_dest: Destination {
target: $exc_dest.id(),
args : $exc_args
}
}
}
});
};
// RET
(($vm: expr, $fv: ident) $name: ident: RET ($($val: ident), +)) => {
......
This diff is collapsed.
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