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

@@ -9,7 +9,7 @@ from __future__ import absolute_import
from cdsl.formats import InstructionFormat
from cdsl.operands import VALUE, VARIABLE_ARGS
from .immediates import imm64, uimm8, uimm32, ieee32, ieee64, offset32
from .immediates import boolean, intcc, floatcc, memflags, regunit
from .immediates import boolean, intcc, floatcc, memflags, regunit, trapcode
from . import entities
from .entities import ebb, sig_ref, func_ref, stack_slot, heap
@@ -61,5 +61,8 @@ HeapAddr = InstructionFormat(heap, VALUE, uimm32)
RegMove = InstructionFormat(VALUE, ('src', regunit), ('dst', regunit))
Trap = InstructionFormat(trapcode)
CondTrap = InstructionFormat(VALUE, trapcode)
# Finally extract the names of global variables in this module.
InstructionFormat.extract_names(globals())

View File

@@ -105,3 +105,19 @@ regunit = ImmediateKind(
'regunit',
'A register unit in the target ISA',
rust_type='isa::RegUnit')
#: A trap code indicating the reason for trapping.
#:
#: The Rust enum type also has a `User(u16)` variant for user-provided trap
#: codes.
trapcode = ImmediateKind(
'trapcode',
'A trap reason code.',
default_member='code',
rust_type='ir::TrapCode',
values={
"stk_ovf": 'StackOverflow',
"heap_oob": 'HeapOutOfBounds',
"int_ovf": 'IntegerOverflow',
"int_divz": 'IntegerDivisionByZero',
})

View File

@@ -11,6 +11,7 @@ from cdsl.instructions import Instruction, InstructionGroup
from base.types import f32, f64, b1
from base.immediates import imm64, uimm8, uimm32, ieee32, ieee64, offset32
from base.immediates import boolean, intcc, floatcc, memflags, regunit
from base.immediates import trapcode
from base import entities
from cdsl.ti import WiderOrEq
import base.formats # noqa
@@ -126,11 +127,12 @@ br_table = Instruction(
""",
ins=(x, JT), is_branch=True)
code = Operand('code', trapcode)
trap = Instruction(
'trap', r"""
Terminate execution unconditionally.
""",
is_terminator=True, can_trap=True)
ins=code, is_terminator=True, can_trap=True)
trapz = Instruction(
'trapz', r"""
@@ -138,7 +140,7 @@ trapz = Instruction(
if ``c`` is non-zero, execution continues at the following instruction.
""",
ins=c, can_trap=True)
ins=(c, code), can_trap=True)
trapnz = Instruction(
'trapnz', r"""
@@ -146,7 +148,7 @@ trapnz = Instruction(
if ``c`` is zero, execution continues at the following instruction.
""",
ins=c, can_trap=True)
ins=(c, code), can_trap=True)
rvals = Operand('rvals', VARIABLE_ARGS, doc='return values')

View File

@@ -287,8 +287,8 @@ I64.enc(base.brnz.b1, *r.t8jccb_abcd(0x75))
#
# Trap as ud2
#
I32.enc(base.trap, *r.noop(0x0f, 0x0b))
I64.enc(base.trap, *r.noop(0x0f, 0x0b))
I32.enc(base.trap, *r.trap(0x0f, 0x0b))
I64.enc(base.trap, *r.trap(0x0f, 0x0b))
#
# Comparisons

View File

@@ -5,7 +5,7 @@ from __future__ import absolute_import
from cdsl.isa import EncRecipe
from cdsl.predicates import IsSignedInt, IsEqual
from base.formats import Unary, UnaryImm, Binary, BinaryImm, MultiAry
from base.formats import Nullary, Call, IndirectCall, Store, Load
from base.formats import Trap, Call, IndirectCall, Store, Load
from base.formats import IntCompare
from base.formats import RegMove, Ternary, Jump, Branch, FuncAddr
from .registers import GPR, ABCD, FPR
@@ -195,8 +195,8 @@ class TailRecipe:
null = EncRecipe('null', Unary, size=0, ins=GPR, outs=0, emit='')
# XX opcode, no ModR/M.
noop = TailRecipe(
'noop', Nullary, size=0, ins=(), outs=(),
trap = TailRecipe(
'trap', Trap, size=0, ins=(), outs=(),
emit='PUT_OP(bits, BASE_REX, sink);')
# XX /r

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),
}
}

View File

@@ -2192,6 +2192,19 @@ impl<'a> Parser<'a> {
dst,
}
}
InstructionFormat::Trap => {
let code = self.match_enum("expected trap code")?;
InstructionData::Trap { opcode, code }
}
InstructionFormat::CondTrap => {
let arg = self.match_value("expected SSA value operand")?;
self.match_token(
Token::Comma,
"expected ',' between operands",
)?;
let code = self.match_enum("expected trap code")?;
InstructionData::CondTrap { opcode, arg, code }
}
};
Ok(idata)
}
@@ -2365,7 +2378,7 @@ mod tests {
jt10 = jump_table ebb0
; Jumptable
ebb0: ; Basic block
trap ; Instruction
trap user42; Instruction
} ; Trailing.
; More trailing.",
).parse_function(None)
@@ -2459,7 +2472,7 @@ mod tests {
let func = Parser::new(
"function #1234567890AbCdEf() native {
ebb0:
trap
trap int_divz
}",
).parse_function(None)
.unwrap()
@@ -2470,7 +2483,7 @@ mod tests {
let mut parser = Parser::new(
"function #12ww() native {
ebb0:
trap
trap stk_ovf
}",
);
assert!(parser.parse_function(None).is_err());
@@ -2479,7 +2492,7 @@ mod tests {
let mut parser = Parser::new(
"function #1() native {
ebb0:
trap
trap user0
}",
);
assert!(parser.parse_function(None).is_err());
@@ -2488,7 +2501,7 @@ mod tests {
let func = Parser::new(
"function #() native {
ebb0:
trap
trap int_ovf
}",
).parse_function(None)
.unwrap()

View File

@@ -101,7 +101,9 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
// We do nothing
}
Operator::Unreachable => {
builder.ins().trap();
// We use `trap user0` to indicate a user-generated trap.
// We could make the trap code configurable if need be.
builder.ins().trap(ir::TrapCode::User(0));
state.real_unreachable_stack_depth = 1;
}
/***************************** Control flow blocks **********************************