Add trap codes to the Cretonne IL.

The trap and trapz/trapnz instructions now take a trap code immediate
operand which indicates the reason for trapping.
This commit is contained in:
Jakob Stoklund Olesen
2017-09-20 13:58:57 -07:00
parent 0f21fd342a
commit e8723be33f
28 changed files with 199 additions and 62 deletions

View File

@@ -428,7 +428,7 @@ mod test {
use cursor::{Cursor, FuncCursor};
use flowgraph::ControlFlowGraph;
use ir::types::*;
use ir::{Function, InstBuilder, types};
use ir::{Function, InstBuilder, types, TrapCode};
use settings;
use super::*;
use verifier::verify_context;
@@ -506,7 +506,7 @@ mod test {
let jmp02 = cur.ins().jump(ebb2, &[]);
cur.insert_ebb(ebb1);
let trap = cur.ins().trap();
let trap = cur.ins().trap(TrapCode::User(5));
cur.insert_ebb(ebb2);
let jmp21 = cur.ins().jump(ebb1, &[]);

View File

@@ -214,6 +214,12 @@ pub enum InstructionData {
src: RegUnit,
dst: RegUnit,
},
Trap { opcode: Opcode, code: ir::TrapCode },
CondTrap {
opcode: Opcode,
arg: Value,
code: ir::TrapCode,
},
}
/// A variable list of `Value` operands used for function call arguments and passing arguments to

View File

@@ -17,6 +17,7 @@ mod globalvar;
mod heap;
mod memflags;
mod progpoint;
mod trapcode;
mod valueloc;
pub use ir::builder::{InstBuilder, InstBuilderBase, InstInserterBase, InsertBuilder};
@@ -34,6 +35,7 @@ pub use ir::layout::{Layout, CursorBase, Cursor};
pub use ir::memflags::MemFlags;
pub use ir::progpoint::{ProgramPoint, ProgramOrder, ExpandedProgramPoint};
pub use ir::stackslot::{StackSlots, StackSlotKind, StackSlotData};
pub use ir::trapcode::TrapCode;
pub use ir::types::Type;
pub use ir::valueloc::{ValueLoc, ArgumentLoc};

View File

@@ -0,0 +1,89 @@
//! Trap codes describing the reason for a trap.
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
/// A trap code describing the reason for a trap.
///
/// All trap instructions have an explicit trap code.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum TrapCode {
/// The current stack space was exhausted.
///
/// On some platforms, a stack overflow may also be indicated by a segmentation fault from the
/// stack guard page.
StackOverflow,
/// A `heap_addr` instruction detected an out-of-bounds error.
///
/// Some out-of-bounds heap accesses are detected by a segmentation fault on the heap guard
/// pages.
HeapOutOfBounds,
/// An integer arithmetic operation caused an overflow.
IntegerOverflow,
/// An integer division by zero.
IntegerDivisionByZero,
/// A user-defined trap code.
User(u16),
}
impl Display for TrapCode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use self::TrapCode::*;
let identifier = match *self {
StackOverflow => "stk_ovf",
HeapOutOfBounds => "heap_oob",
IntegerOverflow => "int_ovf",
IntegerDivisionByZero => "int_divz",
User(x) => return write!(f, "user{}", x),
};
f.write_str(identifier)
}
}
impl FromStr for TrapCode {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::TrapCode::*;
match s {
"stk_ovf" => Ok(StackOverflow),
"heap_oob" => Ok(HeapOutOfBounds),
"int_ovf" => Ok(IntegerOverflow),
"int_divz" => Ok(IntegerDivisionByZero),
_ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
_ => Err(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
// Everything but user-defined codes.
const CODES: [TrapCode; 4] = [
TrapCode::StackOverflow,
TrapCode::HeapOutOfBounds,
TrapCode::IntegerOverflow,
TrapCode::IntegerDivisionByZero,
];
#[test]
fn display() {
for r in &CODES {
let tc = *r;
assert_eq!(tc.to_string().parse(), Ok(tc));
}
assert_eq!("bogus".parse::<TrapCode>(), Err(()));
assert_eq!(TrapCode::User(17).to_string(), "user17");
assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
assert_eq!("user".parse::<TrapCode>(), Err(()));
assert_eq!("user-1".parse::<TrapCode>(), Err(()));
assert_eq!("users".parse::<TrapCode>(), Err(()));
}
}

View File

@@ -74,14 +74,14 @@ fn dynamic_addr(
// We need an overflow check for the adjusted offset.
let size_val = pos.ins().iconst(offset_ty, size);
let (adj_offset, overflow) = pos.ins().iadd_cout(offset, size_val);
pos.ins().trapnz(overflow);
pos.ins().trapnz(overflow, ir::TrapCode::HeapOutOfBounds);
oob = pos.ins().icmp(
IntCC::UnsignedGreaterThan,
adj_offset,
bound,
);
}
pos.ins().trapnz(oob);
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
offset_addr(inst, heap, addr_ty, offset, offset_ty, pos.func);
}
@@ -103,7 +103,7 @@ fn static_addr(
// Start with the bounds check. Trap if `offset + size > bound`.
if size > bound {
// This will simply always trap since `offset >= 0`.
pos.ins().trap();
pos.ins().trap(ir::TrapCode::HeapOutOfBounds);
pos.func.dfg.replace(inst).iconst(addr_ty, 0);
return;
}
@@ -129,7 +129,7 @@ fn static_addr(
limit,
)
};
pos.ins().trapnz(oob);
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
}
offset_addr(inst, heap, addr_ty, offset, offset_ty, pos.func);

View File

@@ -106,15 +106,15 @@ include!(concat!(env!("OUT_DIR"), "/legalizer.rs"));
fn expand_cond_trap(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph) {
// Parse the instruction.
let trapz;
let arg = match func.dfg[inst] {
ir::InstructionData::Unary { opcode, arg } => {
let (arg, code) = match func.dfg[inst] {
ir::InstructionData::CondTrap { opcode, arg, code } => {
// We want to branch *over* an unconditional trap.
trapz = match opcode {
ir::Opcode::Trapz => true,
ir::Opcode::Trapnz => false,
_ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
};
arg
(arg, code)
}
_ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
};
@@ -138,7 +138,7 @@ fn expand_cond_trap(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFl
}
let mut pos = FuncCursor::new(func).after_inst(inst);
pos.ins().trap();
pos.ins().trap(code);
pos.insert_ebb(new_ebb);
// Finally update the CFG.

View File

@@ -337,7 +337,9 @@ impl<'a> Verifier<'a> {
FloatCompare { .. } |
Load { .. } |
Store { .. } |
RegMove { .. } => {}
RegMove { .. } |
Trap { .. } |
CondTrap { .. } => {}
}
Ok(())

View File

@@ -376,6 +376,8 @@ pub fn write_operands(
write!(w, " {}, %{} -> %{}", arg, src, dst)
}
}
Trap { code, .. } => write!(w, " {}", code),
CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
}
}