Commit e0ebe575 authored by Isaac Oscar Gariano's avatar Isaac Oscar Gariano

Don't inline calls with exception blocks that may be reached

parent cf60ddf1
Pipeline #1196 failed with stages
in 18 minutes and 51 seconds
......@@ -340,6 +340,80 @@ impl Instruction {
}
}
/// can this instruction throw exception?
/// (whether or not it containjs a ctach for it)
pub fn is_potentially_throwing(&self) -> bool {
use inst::Instruction_::*;
match self.v {
// Note: commented out ones are ones where we haven't implemented exceptions yet
Watchpoint { .. } |
Call { .. } |
CCall { .. } |
SwapStackExc { .. } |
SwapStackExpr { .. } |
ExnInstruction { .. } |
ExprCall { .. } |
ExprCCall { .. } |
//Load { .. } |
//Store { .. } |
//CmpXchg { .. } |
//AtomicRMW { .. } |
//New(_) |
//NewHybrid(_, _) |
Throw(_) => true,
// BinOp(op, _, _) |
// BinOpWithStatus(op, _, _, _) if op.may_throw() => true,
BinOp(_, _, _) |
BinOpWithStatus(_, _, _, _) |
CmpOp(_, _, _) |
ConvOp { .. } |
AllocA(_) |
AllocAHybrid(_, _) |
NewStack(_) |
NewThread { .. } |
NewFrameCursor(_) |
GetIRef(_) |
GetFieldIRef { .. } |
GetElementIRef { .. } |
ShiftIRef { .. } |
GetVarPartIRef { .. } |
Fence(_) |
Return(_) |
ThreadExit |
TailCall(_) |
Branch1(_) |
Branch2 { .. } |
Select { .. } |
WPBranch { .. } |
Switch { .. } |
CommonInst_GetThreadLocal |
CommonInst_SetThreadLocal(_) |
CommonInst_Pin(_) |
CommonInst_Unpin(_) |
CommonInst_GetAddr(_) |
CommonInst_Tr64IsFp(_) |
CommonInst_Tr64IsInt(_) |
CommonInst_Tr64IsRef(_) |
CommonInst_Tr64FromFp(_) |
CommonInst_Tr64FromInt(_) |
CommonInst_Tr64FromRef(_, _) |
CommonInst_Tr64ToFp(_) |
CommonInst_Tr64ToInt(_) |
CommonInst_Tr64ToRef(_) |
CommonInst_Tr64ToTag(_) |
Move(_) |
PrintHex(_) |
SetRetval(_) |
KillStack(_) |
CurrentStack |
SwapStackKill { .. } => false,
_ => false
}
}
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
self.v.debug_str(ops)
}
......
......@@ -304,8 +304,8 @@ impl MuFunctionVersion {
}
/// gets call outedges in this function
/// returns Map(CallSiteID -> FuncID)
pub fn get_static_call_edges(&self) -> LinkedHashMap<MuID, MuID> {
/// returns Map(CallSiteID -> (FuncID, has exception clause))
pub fn get_static_call_edges(&self) -> LinkedHashMap<MuID, (MuID, bool)> {
let mut ret = LinkedHashMap::new();
let f_content = self.content.as_ref().unwrap();
......@@ -317,7 +317,6 @@ impl MuFunctionVersion {
match inst.v {
TreeNode_::Instruction(ref inst) => {
let ref ops = inst.ops;
match inst.v {
Instruction_::ExprCall { ref data, .. } |
Instruction_::ExprCCall { ref data, .. } |
......@@ -330,7 +329,10 @@ impl MuFunctionVersion {
TreeNode_::Value(ref pv) => {
match pv.v {
Value_::Constant(Constant::FuncRef(id)) => {
ret.insert(inst.id(), id);
ret.insert(
inst.id(),
(id, inst.has_exception_clause())
);
}
_ => {}
}
......@@ -352,7 +354,7 @@ impl MuFunctionVersion {
// TODO: It may be more efficient to compute this when the instructions
// are added to the function version and store the result in a field
pub fn has_throw(&self) -> bool {
pub fn could_throw(&self) -> bool {
let f_content = self.content.as_ref().unwrap();
for (_, block) in f_content.blocks.iter() {
......@@ -361,13 +363,11 @@ impl MuFunctionVersion {
for inst in block_content.body.iter() {
match inst.v {
TreeNode_::Instruction(ref inst) => {
match inst.v {
Instruction_::Throw(_) => {
return true;
}
_ => {
// do nothing
}
if inst.is_potentially_throwing() {
// TODO: Do some smarter checking
// (e.g. if this is a CALL to a function where !could_throw,
// or a division where the divisor can't possibly be zero..)
return true;
}
}
_ => unreachable!()
......
......@@ -69,10 +69,11 @@ impl Inlining {
let mut inline_something = false;
// check each call from this function
for func_id in func.get_static_call_edges().values() {
for (func_id, (_, has_exc)) in func.get_static_call_edges() {
// check a single callsite, whether it should be inlined
// the result is returned as boolean, and also written into 'should_inline'
let should_inline_this = self.check_should_inline_func(*func_id, func.func_id, vm);
let should_inline_this =
self.check_should_inline_func(has_exc, func_id, func.func_id, vm);
inline_something = inline_something || should_inline_this;
}
......@@ -80,7 +81,13 @@ impl Inlining {
}
/// checks whether we should inline the caller into the callee
fn check_should_inline_func(&mut self, callee: MuID, caller: MuID, vm: &VM) -> bool {
fn check_should_inline_func(
&mut self,
has_exc: bool,
callee: MuID,
caller: MuID,
vm: &VM
) -> bool {
// recursive call, do not inline
if callee == caller {
return false;
......@@ -123,19 +130,21 @@ impl Inlining {
// some heuristics here to decide if we should inline the function
let n_insts = estimate_insts(&fv);
let out_calls = fv.get_static_call_edges();
let has_throw = fv.has_throw();
let could_throw = fv.could_throw();
let has_tailcall = fv.has_tailcall();
// simple heuristic here:
// * estimated machine insts are fewer than 10 insts
// * leaf in call graph (no out calls)
// * no throw (otherwise we will need to rearrange catch)
let should_inline = n_insts <= 25 && out_calls.len() == 0 && !has_throw && !has_tailcall;
let should_inline = n_insts <= 25 && !(has_exc && could_throw) && !has_tailcall;
trace!("func {} has {} insts (estimated)", callee, n_insts);
trace!(" has {} out calls", out_calls.len());
trace!(" has throws? {}", has_throw);
trace!(
" has exception clause? {}, could throw? {}",
has_exc,
could_throw
);
trace!("SO func should be inlined? {}", should_inline);
self.should_inline.insert(callee, should_inline);
......@@ -166,7 +175,7 @@ impl Inlining {
trace!("check inst: {}", inst);
let inst_id = inst.id();
if call_edges.contains_key(&inst_id) {
let call_target = call_edges.get(&inst_id).unwrap();
let call_target = &call_edges.get(&inst_id).unwrap().0;
if self.should_inline.contains_key(call_target) &&
*self.should_inline.get(call_target).unwrap()
{
......@@ -178,7 +187,7 @@ impl Inlining {
// inline expansion starts here
// getting the function being inlined
let inlined_func = *call_edges.get(&inst.id()).unwrap();
let inlined_func = call_edges.get(&inst.id()).unwrap().0;
trace!("function being inlined is {}", inlined_func);
let inlined_fvid = match vm.get_cur_version_for_func(inlined_func) {
Some(fvid) => fvid,
......
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