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:
@@ -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, &[]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
89
lib/cretonne/src/ir/trapcode.rs
Normal file
89
lib/cretonne/src/ir/trapcode.rs
Normal 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(()));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -337,7 +337,9 @@ impl<'a> Verifier<'a> {
|
||||
FloatCompare { .. } |
|
||||
Load { .. } |
|
||||
Store { .. } |
|
||||
RegMove { .. } => {}
|
||||
RegMove { .. } |
|
||||
Trap { .. } |
|
||||
CondTrap { .. } => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user