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:
@@ -7,7 +7,7 @@ function %nonsense(i32) {
|
||||
; check: digraph %nonsense {
|
||||
|
||||
ebb0(v1: i32):
|
||||
trap ; error: terminator instruction was encountered before the end
|
||||
trap user0 ; error: terminator instruction was encountered before the end
|
||||
brnz v1, ebb2 ; unordered: ebb0:inst1 -> ebb2
|
||||
jump ebb1 ; unordered: ebb0:inst2 -> ebb1
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ function %not_reached(i32) -> i32 {
|
||||
|
||||
ebb0(v0: i32):
|
||||
brnz v0, ebb2 ; unordered: ebb0:inst0 -> ebb2
|
||||
trap
|
||||
trap user0
|
||||
|
||||
ebb1:
|
||||
v1 = iconst.i32 1
|
||||
|
||||
@@ -369,5 +369,5 @@ ebb1:
|
||||
|
||||
; asm: ebb2:
|
||||
ebb2:
|
||||
trap ; bin: 0f 0b
|
||||
trap user0 ; bin: 0f 0b
|
||||
}
|
||||
|
||||
@@ -861,5 +861,5 @@ ebb0:
|
||||
; asm: movl %r10d, %ecx
|
||||
[-,%rcx] v32 = uextend.i64 v13 ; bin: 44 89 d1
|
||||
|
||||
trap ; bin: 0f 0b
|
||||
trap user0 ; bin: 0f 0b
|
||||
}
|
||||
|
||||
@@ -9,22 +9,22 @@ isa intel
|
||||
|
||||
function %cond_trap(i32) {
|
||||
ebb0(v1: i32):
|
||||
trapz v1
|
||||
trapz v1, user67
|
||||
return
|
||||
; check: $ebb0($v1: i32):
|
||||
; nextln: brnz $v1, $(new=$EBB)
|
||||
; nextln: trap
|
||||
; nextln: trap user67
|
||||
; check: $new:
|
||||
; nextln: return
|
||||
}
|
||||
|
||||
function %cond_trap2(i32) {
|
||||
ebb0(v1: i32):
|
||||
trapnz v1
|
||||
trapnz v1, int_ovf
|
||||
return
|
||||
; check: $ebb0($v1: i32):
|
||||
; nextln: brz $v1, $(new=$EBB)
|
||||
; nextln: trap
|
||||
; nextln: trap int_ovf
|
||||
; check: $new:
|
||||
; nextln: return
|
||||
}
|
||||
@@ -32,11 +32,11 @@ ebb0(v1: i32):
|
||||
function %cond_trap_b1(i32) {
|
||||
ebb0(v1: i32):
|
||||
v2 = icmp_imm eq v1, 6
|
||||
trapz v2
|
||||
trapz v2, user7
|
||||
return
|
||||
; check: $ebb0($v1: i32):
|
||||
; check: brnz $v2, $(new=$EBB)
|
||||
; nextln: trap
|
||||
; nextln: trap user7
|
||||
; check: $new:
|
||||
; nextln: return
|
||||
}
|
||||
@@ -44,11 +44,11 @@ ebb0(v1: i32):
|
||||
function %cond_trap2_b1(i32) {
|
||||
ebb0(v1: i32):
|
||||
v2 = icmp_imm eq v1, 6
|
||||
trapnz v2
|
||||
trapnz v2, user9
|
||||
return
|
||||
; check: $ebb0($v1: i32):
|
||||
; check: brz $v2, $(new=$EBB)
|
||||
; nextln: trap
|
||||
; nextln: trap user9
|
||||
; check: $new:
|
||||
; nextln: return
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ ebb0(v0: i32, v999: i64):
|
||||
; Boundscheck code
|
||||
; check: $(oob=$V) = icmp
|
||||
; nextln: brz $oob, $(ok=$EBB)
|
||||
; nextln: trap
|
||||
; nextln: trap heap_oob
|
||||
; check: $ok:
|
||||
; Checks here are assuming that no pipehole opts fold the load offsets.
|
||||
; nextln: $(xoff=$V) = uextend.i64 $v0
|
||||
|
||||
@@ -86,13 +86,13 @@ function %jumptable(i32) {
|
||||
|
||||
ebb10(v3: i32):
|
||||
br_table v3, jt2
|
||||
trap
|
||||
trap user1
|
||||
ebb20:
|
||||
trap
|
||||
trap user2
|
||||
ebb30:
|
||||
trap
|
||||
trap user3
|
||||
ebb40:
|
||||
trap
|
||||
trap user4
|
||||
}
|
||||
; sameln: function %jumptable(i32) native {
|
||||
; nextln: jt0 = jump_table 0
|
||||
@@ -100,14 +100,14 @@ ebb40:
|
||||
; nextln:
|
||||
; nextln: ebb0($v3: i32):
|
||||
; nextln: br_table $v3, jt1
|
||||
; nextln: trap
|
||||
; nextln: trap user1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: trap
|
||||
; nextln: trap user2
|
||||
; nextln:
|
||||
; nextln: ebb2:
|
||||
; nextln: trap
|
||||
; nextln: trap user3
|
||||
; nextln:
|
||||
; nextln: ebb3:
|
||||
; nextln: trap
|
||||
; nextln: trap user4
|
||||
; nextln: }
|
||||
|
||||
@@ -7,7 +7,7 @@ isa riscv
|
||||
function %foo(i32, i32) {
|
||||
ebb1(v0: i32 [%x8], v1: i32):
|
||||
[-,-] v2 = iadd v0, v1
|
||||
[-] trap
|
||||
[-] trap heap_oob
|
||||
[R#1234, %x5, %x11] v6, v7 = iadd_cout v2, v0
|
||||
[Rshamt#beef, %x25] v8 = ishl_imm v6, 2
|
||||
v9 = iadd v8, v7
|
||||
@@ -16,7 +16,7 @@ ebb1(v0: i32 [%x8], v1: i32):
|
||||
; sameln: function %foo(i32, i32) native {
|
||||
; nextln: $ebb1($v0: i32 [%x8], $v1: i32):
|
||||
; nextln: [-,-]$WS $v2 = iadd $v0, $v1
|
||||
; nextln: [-]$WS trap
|
||||
; nextln: [-]$WS trap heap_oob
|
||||
; nextln: [R#1234,%x5,%x11]$WS $v6, $v7 = iadd_cout $v2, $v0
|
||||
; nextln: [Rshamt#beef,%x25]$WS $v8 = ishl_imm $v6, 2
|
||||
; nextln: [-,-]$WS $v9 = iadd $v8, $v7
|
||||
|
||||
@@ -13,13 +13,13 @@ function %defs() {
|
||||
ebb100(v20: i32):
|
||||
v1000 = iconst.i32x8 5
|
||||
v9200 = f64const 0x4.0p0
|
||||
trap
|
||||
trap user4
|
||||
}
|
||||
; sameln: function %defs() native {
|
||||
; nextln: $ebb100($v20: i32):
|
||||
; nextln: $v1000 = iconst.i32x8 5
|
||||
; nextln: $v9200 = f64const 0x1.0000000000000p2
|
||||
; nextln: trap
|
||||
; nextln: trap user4
|
||||
; nextln: }
|
||||
|
||||
; Using values.
|
||||
|
||||
@@ -3,11 +3,11 @@ test cat
|
||||
; The smallest possible function.
|
||||
function %minimal() {
|
||||
ebb0:
|
||||
trap
|
||||
trap user0
|
||||
}
|
||||
; sameln: function %minimal() native {
|
||||
; nextln: ebb0:
|
||||
; nextln: trap
|
||||
; nextln: trap user0
|
||||
; nextln: }
|
||||
|
||||
; Create and use values.
|
||||
|
||||
@@ -4,10 +4,10 @@ set return_at_end
|
||||
function %ok(i32) {
|
||||
ebb0(v0: i32):
|
||||
brnz v0, ebb1
|
||||
trap
|
||||
trap int_divz
|
||||
|
||||
ebb1:
|
||||
trapz v0
|
||||
trapz v0, user5
|
||||
return
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@ ebb0(v0: i32):
|
||||
return ; error: Internal return not allowed
|
||||
|
||||
ebb1:
|
||||
trapz v0
|
||||
trapz v0, user6
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@ ebb1(v2: i32):
|
||||
|
||||
function %undefined() {
|
||||
ebb0:
|
||||
trap
|
||||
trap user0
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ fn simple_traversal() {
|
||||
brz v4, ebb4
|
||||
jump ebb5
|
||||
ebb3:
|
||||
trap
|
||||
trap user0
|
||||
ebb4:
|
||||
trap
|
||||
trap user0
|
||||
ebb5:
|
||||
trap
|
||||
trap user0
|
||||
}
|
||||
",
|
||||
vec![0, 1, 3, 2, 4, 5],
|
||||
@@ -123,7 +123,7 @@ fn loops_three() {
|
||||
jump ebb6
|
||||
ebb5:
|
||||
brz v0, ebb4
|
||||
trap
|
||||
trap user0
|
||||
ebb6:
|
||||
jump ebb7
|
||||
ebb7:
|
||||
@@ -152,7 +152,7 @@ fn back_edge_one() {
|
||||
brnz v0, ebb0
|
||||
return
|
||||
ebb4:
|
||||
trap
|
||||
trap user0
|
||||
}
|
||||
",
|
||||
vec![0, 1, 3, 2, 4],
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 **********************************
|
||||
|
||||
Reference in New Issue
Block a user