From e8723be33f9367718498395c1710b1c953e1782c Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Wed, 20 Sep 2017 13:58:57 -0700 Subject: [PATCH] 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. --- cranelift/filetests/cfg/traps_early.cton | 2 +- cranelift/filetests/cfg/unused_node.cton | 2 +- cranelift/filetests/isa/intel/binary32.cton | 2 +- cranelift/filetests/isa/intel/binary64.cton | 2 +- .../filetests/isa/intel/legalize-custom.cton | 16 ++-- .../filetests/isa/intel/legalize-memory.cton | 2 +- cranelift/filetests/parser/branch.cton | 16 ++-- .../parser/instruction_encoding.cton | 4 +- cranelift/filetests/parser/rewrite.cton | 4 +- cranelift/filetests/parser/tiny.cton | 4 +- .../filetests/verifier/return_at_end.cton | 6 +- cranelift/filetests/wasm/control.cton | 2 +- cranelift/tests/cfg_traversal.rs | 10 +-- lib/cretonne/meta/base/formats.py | 5 +- lib/cretonne/meta/base/immediates.py | 16 ++++ lib/cretonne/meta/base/instructions.py | 8 +- lib/cretonne/meta/isa/intel/encodings.py | 4 +- lib/cretonne/meta/isa/intel/recipes.py | 6 +- lib/cretonne/src/dominator_tree.rs | 4 +- lib/cretonne/src/ir/instructions.rs | 6 ++ lib/cretonne/src/ir/mod.rs | 2 + lib/cretonne/src/ir/trapcode.rs | 89 +++++++++++++++++++ lib/cretonne/src/legalizer/heap.rs | 8 +- lib/cretonne/src/legalizer/mod.rs | 8 +- lib/cretonne/src/verifier/mod.rs | 4 +- lib/cretonne/src/write.rs | 2 + lib/reader/src/parser.rs | 23 +++-- lib/wasm/src/code_translator.rs | 4 +- 28 files changed, 199 insertions(+), 62 deletions(-) create mode 100644 lib/cretonne/src/ir/trapcode.rs diff --git a/cranelift/filetests/cfg/traps_early.cton b/cranelift/filetests/cfg/traps_early.cton index 814e251f51..e12c3c65ee 100644 --- a/cranelift/filetests/cfg/traps_early.cton +++ b/cranelift/filetests/cfg/traps_early.cton @@ -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 diff --git a/cranelift/filetests/cfg/unused_node.cton b/cranelift/filetests/cfg/unused_node.cton index cbde9757bc..d339e5ea83 100644 --- a/cranelift/filetests/cfg/unused_node.cton +++ b/cranelift/filetests/cfg/unused_node.cton @@ -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 diff --git a/cranelift/filetests/isa/intel/binary32.cton b/cranelift/filetests/isa/intel/binary32.cton index b531dfdfd7..16f8131cef 100644 --- a/cranelift/filetests/isa/intel/binary32.cton +++ b/cranelift/filetests/isa/intel/binary32.cton @@ -369,5 +369,5 @@ ebb1: ; asm: ebb2: ebb2: - trap ; bin: 0f 0b + trap user0 ; bin: 0f 0b } diff --git a/cranelift/filetests/isa/intel/binary64.cton b/cranelift/filetests/isa/intel/binary64.cton index 680da4273e..e67f630753 100644 --- a/cranelift/filetests/isa/intel/binary64.cton +++ b/cranelift/filetests/isa/intel/binary64.cton @@ -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 } diff --git a/cranelift/filetests/isa/intel/legalize-custom.cton b/cranelift/filetests/isa/intel/legalize-custom.cton index 4c8488173b..39b92c6306 100644 --- a/cranelift/filetests/isa/intel/legalize-custom.cton +++ b/cranelift/filetests/isa/intel/legalize-custom.cton @@ -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 } diff --git a/cranelift/filetests/isa/intel/legalize-memory.cton b/cranelift/filetests/isa/intel/legalize-memory.cton index 5db903aa52..c7d5ed3617 100644 --- a/cranelift/filetests/isa/intel/legalize-memory.cton +++ b/cranelift/filetests/isa/intel/legalize-memory.cton @@ -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 diff --git a/cranelift/filetests/parser/branch.cton b/cranelift/filetests/parser/branch.cton index 283dd9a03a..7b3ca787b6 100644 --- a/cranelift/filetests/parser/branch.cton +++ b/cranelift/filetests/parser/branch.cton @@ -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: } diff --git a/cranelift/filetests/parser/instruction_encoding.cton b/cranelift/filetests/parser/instruction_encoding.cton index c9c668dc3f..d6dda30da5 100644 --- a/cranelift/filetests/parser/instruction_encoding.cton +++ b/cranelift/filetests/parser/instruction_encoding.cton @@ -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 diff --git a/cranelift/filetests/parser/rewrite.cton b/cranelift/filetests/parser/rewrite.cton index c6a04a9e9c..1601002b27 100644 --- a/cranelift/filetests/parser/rewrite.cton +++ b/cranelift/filetests/parser/rewrite.cton @@ -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. diff --git a/cranelift/filetests/parser/tiny.cton b/cranelift/filetests/parser/tiny.cton index 56dfc53973..e538b5acaa 100644 --- a/cranelift/filetests/parser/tiny.cton +++ b/cranelift/filetests/parser/tiny.cton @@ -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. diff --git a/cranelift/filetests/verifier/return_at_end.cton b/cranelift/filetests/verifier/return_at_end.cton index 7acc8b8fad..fcbae57070 100644 --- a/cranelift/filetests/verifier/return_at_end.cton +++ b/cranelift/filetests/verifier/return_at_end.cton @@ -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 } diff --git a/cranelift/filetests/wasm/control.cton b/cranelift/filetests/wasm/control.cton index c8e37a536f..9d46a3577b 100644 --- a/cranelift/filetests/wasm/control.cton +++ b/cranelift/filetests/wasm/control.cton @@ -46,5 +46,5 @@ ebb1(v2: i32): function %undefined() { ebb0: - trap + trap user0 } diff --git a/cranelift/tests/cfg_traversal.rs b/cranelift/tests/cfg_traversal.rs index 9154349bc7..90fb900dfe 100644 --- a/cranelift/tests/cfg_traversal.rs +++ b/cranelift/tests/cfg_traversal.rs @@ -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], diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index 9d6315ddcf..1746217958 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -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()) diff --git a/lib/cretonne/meta/base/immediates.py b/lib/cretonne/meta/base/immediates.py index dd4a5c7760..53293acc77 100644 --- a/lib/cretonne/meta/base/immediates.py +++ b/lib/cretonne/meta/base/immediates.py @@ -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', + }) diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index 735ec742bc..f49a532a3f 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -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') diff --git a/lib/cretonne/meta/isa/intel/encodings.py b/lib/cretonne/meta/isa/intel/encodings.py index bf1b91c0dc..355c755e7c 100644 --- a/lib/cretonne/meta/isa/intel/encodings.py +++ b/lib/cretonne/meta/isa/intel/encodings.py @@ -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 diff --git a/lib/cretonne/meta/isa/intel/recipes.py b/lib/cretonne/meta/isa/intel/recipes.py index 06aea4b01f..8d07f06de1 100644 --- a/lib/cretonne/meta/isa/intel/recipes.py +++ b/lib/cretonne/meta/isa/intel/recipes.py @@ -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 diff --git a/lib/cretonne/src/dominator_tree.rs b/lib/cretonne/src/dominator_tree.rs index cf8ecc8cb9..5f342baf89 100644 --- a/lib/cretonne/src/dominator_tree.rs +++ b/lib/cretonne/src/dominator_tree.rs @@ -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, &[]); diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index 6dd5eafde7..0b3c671cc6 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -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 diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index b65174245f..077db6537b 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -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}; diff --git a/lib/cretonne/src/ir/trapcode.rs b/lib/cretonne/src/ir/trapcode.rs new file mode 100644 index 0000000000..c9312cdff0 --- /dev/null +++ b/lib/cretonne/src/ir/trapcode.rs @@ -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 { + 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::(), Err(())); + + assert_eq!(TrapCode::User(17).to_string(), "user17"); + assert_eq!("user22".parse(), Ok(TrapCode::User(22))); + assert_eq!("user".parse::(), Err(())); + assert_eq!("user-1".parse::(), Err(())); + assert_eq!("users".parse::(), Err(())); + } +} diff --git a/lib/cretonne/src/legalizer/heap.rs b/lib/cretonne/src/legalizer/heap.rs index 0a14f5f8ee..ab2ce444af 100644 --- a/lib/cretonne/src/legalizer/heap.rs +++ b/lib/cretonne/src/legalizer/heap.rs @@ -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); diff --git a/lib/cretonne/src/legalizer/mod.rs b/lib/cretonne/src/legalizer/mod.rs index 6dbeb15d04..5cb1c27819 100644 --- a/lib/cretonne/src/legalizer/mod.rs +++ b/lib/cretonne/src/legalizer/mod.rs @@ -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. diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index f387c0d295..3fd6685133 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -337,7 +337,9 @@ impl<'a> Verifier<'a> { FloatCompare { .. } | Load { .. } | Store { .. } | - RegMove { .. } => {} + RegMove { .. } | + Trap { .. } | + CondTrap { .. } => {} } Ok(()) diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index f225e0aa7d..6dd6f333df 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -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), } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 94cf785fb4..8ce55d4445 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -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() diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index b3d202aa2d..c66f9cea79 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -101,7 +101,9 @@ pub fn translate_operator( // 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 **********************************