diff --git a/cranelift/codegen/meta/src/shared/formats.rs b/cranelift/codegen/meta/src/shared/formats.rs index 179a6b8508..2d94d1e3c3 100644 --- a/cranelift/codegen/meta/src/shared/formats.rs +++ b/cranelift/codegen/meta/src/shared/formats.rs @@ -20,6 +20,7 @@ pub(crate) struct Formats { pub(crate) int_compare: Rc, pub(crate) int_compare_imm: Rc, pub(crate) int_cond_trap: Rc, + pub(crate) int_add_trap: Rc, pub(crate) jump: Rc, pub(crate) load: Rc, pub(crate) load_no_offset: Rc, @@ -223,6 +224,12 @@ impl Formats { .imm(&imm.trapcode) .build(), + int_add_trap: Builder::new("IntAddTrap") + .value() + .value() + .imm(&imm.trapcode) + .build(), + float_cond_trap: Builder::new("FloatCondTrap") .imm(&imm.floatcc) .value() diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs index 3213759b59..b9f717b3a0 100644 --- a/cranelift/codegen/meta/src/shared/instructions.rs +++ b/cranelift/codegen/meta/src/shared/instructions.rs @@ -2153,6 +2153,34 @@ pub(crate) fn define( .operands_out(vec![a, c_if_out]), ); + { + let code = &Operand::new("code", &imm.trapcode); + + let i32_64 = &TypeVar::new( + "i32_64", + "A 32 or 64-bit scalar integer type", + TypeSetBuilder::new().ints(32..64).build(), + ); + + let a = &Operand::new("a", i32_64); + let x = &Operand::new("x", i32_64); + let y = &Operand::new("y", i32_64); + ig.push( + Inst::new( + "uadd_overflow_trap", + r#" + Unsigned addition of x and y, trapping if the result overflows. + + Accepts 32 or 64-bit integers, and does not support vector types. + "#, + &formats.int_add_trap, + ) + .operands_in(vec![x, y, code]) + .operands_out(vec![a]) + .can_trap(true), + ); + } + ig.push( Inst::new( "isub_bin", diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index 9f96eceed3..b1b1a561a5 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -2772,6 +2772,14 @@ ) x)) +;; Check for unsigned overflow. +(decl trap_if_overflow (ProducesFlags TrapCode) Reg) +(rule (trap_if_overflow producer tc) + (with_flags_reg + producer + (ConsumesFlags.ConsumesFlagsSideEffect + (MInst.TrapIf (cond_br_cond (Cond.Hs)) tc)))) + (decl sink_atomic_load (Inst) Reg) (rule (sink_atomic_load x @ (atomic_load _ addr)) (let ((_ Unit (sink_inst x))) diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle index a91c47ff30..464cf6f7aa 100644 --- a/cranelift/codegen/src/isa/aarch64/lower.isle +++ b/cranelift/codegen/src/isa/aarch64/lower.isle @@ -2373,6 +2373,11 @@ (add_with_flags ty a b) (invalid_reg))) +;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule (lower (has_type (fits_in_64 ty) (uadd_overflow_trap a b tc))) + (trap_if_overflow (add_with_flags_paired ty a b) tc)) + ;;; Rules for `tls_value` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rule (lower (tls_value (symbol_value_data name _ _))) diff --git a/cranelift/codegen/src/isa/aarch64/lower_inst.rs b/cranelift/codegen/src/isa/aarch64/lower_inst.rs index e320234eb9..a045d85c52 100644 --- a/cranelift/codegen/src/isa/aarch64/lower_inst.rs +++ b/cranelift/codegen/src/isa/aarch64/lower_inst.rs @@ -257,6 +257,8 @@ pub(crate) fn lower_insn_to_regs( Opcode::IaddIfcout => implemented_in_isle(ctx), + Opcode::UaddOverflowTrap => implemented_in_isle(ctx), + Opcode::IaddImm | Opcode::ImulImm | Opcode::UdivImm diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle index 653f342385..404231482a 100644 --- a/cranelift/codegen/src/isa/riscv64/inst.isle +++ b/cranelift/codegen/src/isa/riscv64/inst.isle @@ -1847,22 +1847,22 @@ (gen_fabs x ty) (fpu_rrr (fabs_copy_sign ty) ty x x)) -;;; right now only return if overflow. -(decl lower_uadd_overflow (Reg Reg Type) Reg) +;;; Returns the sum in the first register, and the overflow test in the second. +(decl lower_uadd_overflow (Reg Reg Type) ValueRegs) (rule 1 (lower_uadd_overflow x y $I64) - (let - ((tmp Reg (alu_add x y))) - (gen_icmp (IntCC.UnsignedLessThan) tmp x $I64))) + (let ((tmp Reg (alu_add x y)) + (test Reg (gen_icmp (IntCC.UnsignedLessThan) tmp x $I64))) + (value_regs tmp test))) (rule (lower_uadd_overflow x y (fits_in_32 ty)) - (let - ((tmp_x Reg (ext_int_if_need $false x ty)) - (tmp_y Reg (ext_int_if_need $false y ty)) - (sum Reg (alu_add tmp_x tmp_y))) - (alu_srli sum (ty_bits ty)))) + (let ((tmp_x Reg (ext_int_if_need $false x ty)) + (tmp_y Reg (ext_int_if_need $false y ty)) + (sum Reg (alu_add tmp_x tmp_y)) + (test Reg (alu_srli sum (ty_bits ty)))) + (value_regs sum test))) (decl inst_output_get (InstOutput u8) ValueRegs) (extern constructor inst_output_get inst_output_get) diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle index 3e8a0ad89e..edc3da22f1 100644 --- a/cranelift/codegen/src/isa/riscv64/lower.isle +++ b/cranelift/codegen/src/isa/riscv64/lower.isle @@ -48,6 +48,13 @@ (lower (has_type (fits_in_64 ty) (iadd_ifcout x y))) (output_ifcout (alu_add x y))) +;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;; +(rule + (lower (has_type (fits_in_64 ty) (uadd_overflow_trap x y tc))) + (let ((res ValueRegs (lower_uadd_overflow x y ty)) + (_ InstOutput (gen_trapif (value_regs_get res 1) tc))) + (value_regs_get res 0))) + ;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Base case, simply subtracting things in registers. @@ -853,8 +860,8 @@ (rule (lower (trapif _ (iadd_ifcout a @ (value_type ty) b) trap_code)) (let - ((test Reg (lower_uadd_overflow a b ty))) - (gen_trapif test trap_code))) + ((res ValueRegs (lower_uadd_overflow a b ty))) + (gen_trapif (value_regs_get res 1) trap_code))) ;;;;; Rules for `trapff`;;;;;;;;; diff --git a/cranelift/codegen/src/isa/s390x/inst.isle b/cranelift/codegen/src/isa/s390x/inst.isle index 6f0a4605b7..74d7f2942e 100644 --- a/cranelift/codegen/src/isa/s390x/inst.isle +++ b/cranelift/codegen/src/isa/s390x/inst.isle @@ -2022,6 +2022,13 @@ (_ Unit (emit (MInst.AluRRR op dst src1 src2)))) dst)) +;; Helper for emitting `MInst.AluRRR` instructions as flag producers. +(decl alu_rrr_with_flags_paired (Type ALUOp Reg Reg) ProducesFlags) +(rule (alu_rrr_with_flags_paired ty op src1 src2) + (let ((dst WritableReg (temp_writable_reg ty))) + (ProducesFlags.ProducesFlagsReturnsResultWithConsumer + (MInst.AluRRR op dst src1 src2) dst))) + ;; Helper for emitting `MInst.AluRRSImm16` instructions. (decl alu_rrsimm16 (Type ALUOp Reg i16) Reg) (rule (alu_rrsimm16 ty op src imm) @@ -2036,6 +2043,13 @@ (_ Unit (emit (MInst.AluRR op dst src1 src2)))) dst)) +;; Helper for emitting `MInst.AluRR` instructions as flag producers. +(decl alu_rr_with_flags_paired (Type ALUOp Reg Reg) ProducesFlags) +(rule (alu_rr_with_flags_paired ty op src1 src2) + (let ((dst WritableReg (temp_writable_reg ty))) + (ProducesFlags.ProducesFlagsReturnsResultWithConsumer + (MInst.AluRR op dst src1 src2) dst))) + ;; Helper for emitting `MInst.AluRX` instructions. (decl alu_rx (Type ALUOp Reg MemArg) Reg) (rule (alu_rx ty op src mem) @@ -2043,6 +2057,13 @@ (_ Unit (emit (MInst.AluRX op dst src mem)))) dst)) +;; Helper for emitting `MInst.AluRX` instructions as flags producers. +(decl alu_rx_with_flags_paired (Type ALUOp Reg MemArg) ProducesFlags) +(rule (alu_rx_with_flags_paired ty op src mem) + (let ((dst WritableReg (temp_writable_reg ty))) + (ProducesFlags.ProducesFlagsReturnsResultWithConsumer + (MInst.AluRX op dst src mem) dst))) + ;; Helper for emitting `MInst.AluRSImm16` instructions. (decl alu_rsimm16 (Type ALUOp Reg i16) Reg) (rule (alu_rsimm16 ty op src imm) @@ -2064,6 +2085,13 @@ (_ Unit (emit (MInst.AluRUImm32 op dst src imm)))) dst)) +;; Helper for emitting `MInst.AluRUImm32` instructions as flag producers. +(decl alu_ruimm32_with_flags_paired (Type ALUOp Reg u32) ProducesFlags) +(rule (alu_ruimm32_with_flags_paired ty op src imm) + (let ((dst WritableReg (temp_writable_reg ty))) + (ProducesFlags.ProducesFlagsReturnsResultWithConsumer + (MInst.AluRUImm32 op dst src imm) dst))) + ;; Helper for emitting `MInst.AluRUImm16Shifted` instructions. (decl alu_ruimm16shifted (Type ALUOp Reg UImm16Shifted) Reg) (rule (alu_ruimm16shifted ty op src imm) @@ -3877,18 +3905,38 @@ (decl add_logical_reg (Type Reg Reg) Reg) (rule (add_logical_reg ty x y) (alu_rrr ty (aluop_add_logical ty) x y)) +(decl add_logical_reg_with_flags_paired (Type Reg Reg) ProducesFlags) +(rule (add_logical_reg_with_flags_paired ty x y) + (alu_rrr_with_flags_paired ty (aluop_add_logical ty) x y)) + (decl add_logical_reg_zext32 (Type Reg Reg) Reg) (rule (add_logical_reg_zext32 ty x y) (alu_rr ty (aluop_add_logical_zext32 ty) x y)) +(decl add_logical_reg_zext32_with_flags_paired (Type Reg Reg) ProducesFlags) +(rule (add_logical_reg_zext32_with_flags_paired ty x y) + (alu_rr_with_flags_paired ty (aluop_add_logical_zext32 ty) x y)) + (decl add_logical_zimm32 (Type Reg u32) Reg) (rule (add_logical_zimm32 ty x y) (alu_ruimm32 ty (aluop_add_logical ty) x y)) +(decl add_logical_zimm32_with_flags_paired (Type Reg u32) ProducesFlags) +(rule (add_logical_zimm32_with_flags_paired ty x y) + (alu_ruimm32_with_flags_paired ty (aluop_add_logical ty) x y)) + (decl add_logical_mem (Type Reg MemArg) Reg) (rule (add_logical_mem ty x y) (alu_rx ty (aluop_add_logical ty) x y)) +(decl add_logical_mem_with_flags_paired (Type Reg MemArg) ProducesFlags) +(rule (add_logical_mem_with_flags_paired ty x y) + (alu_rx_with_flags_paired ty (aluop_add_logical ty) x y)) + (decl add_logical_mem_zext32 (Type Reg MemArg) Reg) (rule (add_logical_mem_zext32 ty x y) (alu_rx ty (aluop_add_logical_zext32 ty) x y)) +(decl add_logical_mem_zext32_with_flags_paired (Type Reg MemArg) ProducesFlags) +(rule (add_logical_mem_zext32_with_flags_paired ty x y) + (alu_rx_with_flags_paired ty (aluop_add_logical_zext32 ty) x y)) + ;; Helpers for generating `sub` instructions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/codegen/src/isa/s390x/lower.isle b/cranelift/codegen/src/isa/s390x/lower.isle index 568e3d1fab..55aaa28f8c 100644 --- a/cranelift/codegen/src/isa/s390x/lower.isle +++ b/cranelift/codegen/src/isa/s390x/lower.isle @@ -3870,6 +3870,60 @@ (trap_if_bool (bool (flags_to_producesflags flags) (mask_as_cond 3)) trap_code))) +;;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule 0 (lower (has_type (fits_in_64 ty) (uadd_overflow_trap x y tc))) + (with_flags + (add_logical_reg_with_flags_paired ty x y) + (trap_if_impl (mask_as_cond 3) tc))) + +;; Add a register an a zero-extended register. +(rule 4 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap x (zext32_value y) tc))) + (with_flags + (add_logical_reg_zext32_with_flags_paired ty x y) + (trap_if_impl (mask_as_cond 3) tc))) +(rule 8 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap (zext32_value x) y tc))) + (with_flags + (add_logical_reg_zext32_with_flags_paired ty y x) + (trap_if_impl (mask_as_cond 3) tc))) + +;; Add a register and an immediate +(rule 3 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap x (u32_from_value y) tc))) + (with_flags + (add_logical_zimm32_with_flags_paired ty x y) + (trap_if_impl (mask_as_cond 3) tc))) +(rule 7 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap (u32_from_value x) y tc))) + (with_flags + (add_logical_zimm32_with_flags_paired ty y x) + (trap_if_impl (mask_as_cond 3) tc))) + +;; Add a register and memory (32/64-bit types). +(rule 2 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap x (sinkable_load_32_64 y) tc))) + (with_flags + (add_logical_mem_with_flags_paired ty x (sink_load y)) + (trap_if_impl (mask_as_cond 3) tc))) +(rule 6 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap (sinkable_load_32_64 x) y tc))) + (with_flags + (add_logical_mem_with_flags_paired ty y (sink_load x)) + (trap_if_impl (mask_as_cond 3) tc))) + +;; Add a register and zero-extended memory. +(rule 1 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap x (sinkable_uload32 y) tc))) + (with_flags + (add_logical_mem_zext32_with_flags_paired ty x (sink_uload32 y)) + (trap_if_impl (mask_as_cond 3) tc))) +(rule 5 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap (sinkable_uload32 x) y tc))) + (with_flags + (add_logical_mem_zext32_with_flags_paired ty y (sink_uload32 x)) + (trap_if_impl (mask_as_cond 3) tc))) ;;;; Rules for `return` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/cranelift/codegen/src/isa/s390x/lower.rs b/cranelift/codegen/src/isa/s390x/lower.rs index 9fc4efbc34..8279404d58 100644 --- a/cranelift/codegen/src/isa/s390x/lower.rs +++ b/cranelift/codegen/src/isa/s390x/lower.rs @@ -240,6 +240,7 @@ impl LowerBackend for S390xBackend { | Opcode::IaddCout | Opcode::IaddCarry | Opcode::IaddIfcarry + | Opcode::UaddOverflowTrap | Opcode::IsubBin | Opcode::IsubIfbin | Opcode::IsubBout diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle index ffe221e7c4..0c2267225e 100644 --- a/cranelift/codegen/src/isa/x64/lower.isle +++ b/cranelift/codegen/src/isa/x64/lower.isle @@ -1352,6 +1352,41 @@ (rule (lower (trap code)) (side_effect (x64_ud2 code))) +;;;; Rules for `uadd_overflow_trap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(rule (lower (has_type (fits_in_64 ty) (uadd_overflow_trap a b tc))) + (with_flags + (x64_add_with_flags_paired ty a b) + (trap_if (CC.B) tc))) + +;; Add a register and an immediate. + +(rule 1 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap a (simm32_from_value b) tc))) + (with_flags + (x64_add_with_flags_paired ty a b) + (trap_if (CC.B) tc))) + +(rule 2 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap (simm32_from_value a) b tc))) + (with_flags + (x64_add_with_flags_paired ty b a) + (trap_if (CC.B) tc))) + +;; Add a register and memory. + +(rule 3 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap a (sinkable_load b) tc))) + (with_flags + (x64_add_with_flags_paired ty a (sink_load_to_gpr_mem_imm b)) + (trap_if (CC.B) tc))) + +(rule 4 (lower (has_type (fits_in_64 ty) + (uadd_overflow_trap (sinkable_load a) b tc))) + (with_flags + (x64_add_with_flags_paired ty b (sink_load_to_gpr_mem_imm a)) + (trap_if (CC.B) tc))) + ;;;; Rules for `trapif` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The flags must not have been clobbered by any other instruction between the diff --git a/cranelift/codegen/src/isa/x64/lower.rs b/cranelift/codegen/src/isa/x64/lower.rs index fd434d7ced..759eb5e589 100644 --- a/cranelift/codegen/src/isa/x64/lower.rs +++ b/cranelift/codegen/src/isa/x64/lower.rs @@ -527,6 +527,7 @@ fn lower_insn_to_regs( | Opcode::IsubIfbout | Opcode::IsubBorrow | Opcode::IsubIfborrow + | Opcode::UaddOverflowTrap | Opcode::BandImm | Opcode::BorImm | Opcode::BxorImm diff --git a/cranelift/codegen/src/legalizer/heap.rs b/cranelift/codegen/src/legalizer/heap.rs index 3d04faacf1..34ef3b34de 100644 --- a/cranelift/codegen/src/legalizer/heap.rs +++ b/cranelift/codegen/src/legalizer/heap.rs @@ -74,12 +74,9 @@ fn dynamic_addr( } else { // We need an overflow check for the adjusted offset. let access_size_val = pos.ins().iconst(addr_ty, access_size as i64); - let (adj_offset, overflow) = pos.ins().iadd_ifcout(offset, access_size_val); - pos.ins().trapif( - isa.unsigned_add_overflow_condition(), - overflow, - ir::TrapCode::HeapOutOfBounds, - ); + let adj_offset = + pos.ins() + .uadd_overflow_trap(offset, access_size_val, ir::TrapCode::HeapOutOfBounds); (IntCC::UnsignedGreaterThan, adj_offset, bound) }; let oob = pos.ins().icmp(cc, lhs, bound); diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle index a081ca6a63..e88a4d5931 100644 --- a/cranelift/codegen/src/prelude_lower.isle +++ b/cranelift/codegen/src/prelude_lower.isle @@ -425,6 +425,14 @@ (_y Unit (emit consumer_inst))) (value_regs producer_result consumer_result))) +;; A flag-producer that also produces a result, paired with a consumer that has +;; no results. +(rule (with_flags (ProducesFlags.ProducesFlagsReturnsResultWithConsumer producer_inst producer_result) + (ConsumesFlags.ConsumesFlagsSideEffect consumer_inst)) + (let ((_ Unit (emit producer_inst)) + (_ Unit (emit consumer_inst))) + (value_reg producer_result))) + (rule (with_flags (ProducesFlags.ProducesFlagsSideEffect producer_inst) (ConsumesFlags.ConsumesFlagsReturnsReg consumer_inst consumer_result)) (let ((_x Unit (emit producer_inst)) diff --git a/cranelift/codegen/src/verifier/mod.rs b/cranelift/codegen/src/verifier/mod.rs index 5fb2a54d01..7e24011db9 100644 --- a/cranelift/codegen/src/verifier/mod.rs +++ b/cranelift/codegen/src/verifier/mod.rs @@ -759,6 +759,7 @@ impl<'a> Verifier<'a> { | Ternary { .. } | TernaryImm8 { .. } | Shuffle { .. } + | IntAddTrap { .. } | IntCompare { .. } | IntCompareImm { .. } | FloatCompare { .. } diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs index c51adb288c..79fae3e96f 100644 --- a/cranelift/codegen/src/write.rs +++ b/cranelift/codegen/src/write.rs @@ -418,6 +418,7 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt } IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), + IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code), FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), Jump { destination, diff --git a/cranelift/filetests/filetests/isa/aarch64/uadd_overflow_trap.clif b/cranelift/filetests/filetests/isa/aarch64/uadd_overflow_trap.clif new file mode 100644 index 0000000000..4d4ad4d876 --- /dev/null +++ b/cranelift/filetests/filetests/isa/aarch64/uadd_overflow_trap.clif @@ -0,0 +1,77 @@ +test compile precise-output +target aarch64 + +function %f0(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 127 + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; movz x3, #127 +; adds w0, w0, w3 +; b.lo 8 ; udf +; ret + +function %f1(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 127 + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; block0: +; movz x3, #127 +; adds w0, w3, w0 +; b.lo 8 ; udf +; ret + +function %f2(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; adds w0, w0, w1 +; b.lo 8 ; udf +; ret + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 127 + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; movz x3, #127 +; adds x0, x0, x3 +; b.lo 8 ; udf +; ret + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 127 + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; block0: +; movz x3, #127 +; adds x0, x3, x0 +; b.lo 8 ; udf +; ret + +function %f4(i64, i64) -> i64 { +block0(v0: i64, v1: i64): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; adds x0, x0, x1 +; b.lo 8 ; udf +; ret + diff --git a/cranelift/filetests/filetests/isa/riscv64/uadd_overflow_trap.clif b/cranelift/filetests/filetests/isa/riscv64/uadd_overflow_trap.clif new file mode 100644 index 0000000000..231f066fe4 --- /dev/null +++ b/cranelift/filetests/filetests/isa/riscv64/uadd_overflow_trap.clif @@ -0,0 +1,97 @@ +test compile precise-output +target riscv64 + +function %f0(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 127 + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; mv a6,a0 +; li a0,127 +; uext.w a2,a6 +; uext.w a4,a0 +; add a0,a2,a4 +; srli t3,a0,32 +; trap_if t3,user0 +; ret + +function %f1(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 127 + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; block0: +; li a1,127 +; uext.w a2,a1 +; uext.w a4,a0 +; add a0,a2,a4 +; srli t3,a0,32 +; trap_if t3,user0 +; ret + +function %f2(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; mv a6,a1 +; uext.w a1,a0 +; uext.w a3,a6 +; add a0,a1,a3 +; srli a7,a0,32 +; trap_if a7,user0 +; ret + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 127 + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; li a1,127 +; add a2,a0,a1 +; mv a5,a2 +; ult a4,a5,a0##ty=i64 +; mv a2,a5 +; trap_if a4,user0 +; mv a0,a2 +; ret + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 127 + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; block0: +; li a1,127 +; add a0,a1,a0 +; ult a4,a0,a1##ty=i64 +; trap_if a4,user0 +; ret + +function %f4(i64, i64) -> i64 { +block0(v0: i64, v1: i64): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; add a1,a0,a1 +; mv a4,a1 +; ult a3,a4,a0##ty=i64 +; mv a1,a4 +; trap_if a3,user0 +; mv a0,a1 +; ret + diff --git a/cranelift/filetests/filetests/isa/s390x/uadd_overflow_trap.clif b/cranelift/filetests/filetests/isa/s390x/uadd_overflow_trap.clif new file mode 100644 index 0000000000..12c2a83143 --- /dev/null +++ b/cranelift/filetests/filetests/isa/s390x/uadd_overflow_trap.clif @@ -0,0 +1,73 @@ +test compile precise-output +target s390x + +function %f0(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 127 + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; alfi %r2, 127 +; jle 6 ; trap +; br %r14 + +function %f1(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 127 + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; block0: +; alfi %r2, 127 +; jle 6 ; trap +; br %r14 + +function %f2(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; alr %r2, %r3 +; jle 6 ; trap +; br %r14 + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 127 + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; algfi %r2, 127 +; jle 6 ; trap +; br %r14 + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 127 + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; block0: +; algfi %r2, 127 +; jle 6 ; trap +; br %r14 + +function %f4(i64, i64) -> i64 { +block0(v0: i64, v1: i64): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; block0: +; algr %r2, %r3 +; jle 6 ; trap +; br %r14 + diff --git a/cranelift/filetests/filetests/isa/x64/uadd_overflow_trap.clif b/cranelift/filetests/filetests/isa/x64/uadd_overflow_trap.clif new file mode 100644 index 0000000000..390a580184 --- /dev/null +++ b/cranelift/filetests/filetests/isa/x64/uadd_overflow_trap.clif @@ -0,0 +1,103 @@ +test compile precise-output +target x86_64 + +function %f0(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 127 + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; pushq %rbp +; movq %rsp, %rbp +; block0: +; movq %rdi, %rax +; addl %eax, $127, %eax +; jnb ; ud2 user0 ; +; movq %rbp, %rsp +; popq %rbp +; ret + +function %f1(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 127 + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; pushq %rbp +; movq %rsp, %rbp +; block0: +; movq %rdi, %rax +; addl %eax, $127, %eax +; jnb ; ud2 user0 ; +; movq %rbp, %rsp +; popq %rbp +; ret + +function %f2(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; pushq %rbp +; movq %rsp, %rbp +; block0: +; movq %rdi, %rax +; addl %eax, %esi, %eax +; jnb ; ud2 user0 ; +; movq %rbp, %rsp +; popq %rbp +; ret + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 127 + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; pushq %rbp +; movq %rsp, %rbp +; block0: +; movq %rdi, %rax +; addq %rax, $127, %rax +; jnb ; ud2 user0 ; +; movq %rbp, %rsp +; popq %rbp +; ret + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 127 + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; pushq %rbp +; movq %rsp, %rbp +; block0: +; movq %rdi, %rax +; addq %rax, $127, %rax +; jnb ; ud2 user0 ; +; movq %rbp, %rsp +; popq %rbp +; ret + +function %f4(i64, i64) -> i64 { +block0(v0: i64, v1: i64): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; pushq %rbp +; movq %rsp, %rbp +; block0: +; movq %rdi, %rax +; addq %rax, %rsi, %rax +; jnb ; ud2 user0 ; +; movq %rbp, %rsp +; popq %rbp +; ret + diff --git a/cranelift/filetests/filetests/runtests/uadd_overflow_trap.clif b/cranelift/filetests/filetests/runtests/uadd_overflow_trap.clif new file mode 100644 index 0000000000..d38b134aac --- /dev/null +++ b/cranelift/filetests/filetests/runtests/uadd_overflow_trap.clif @@ -0,0 +1,68 @@ +test run +test interpret +target x86_64 +target aarch64 +target riscv64 +target s390x + +; NOTE: we don't currently have infrastructure for testing for traps, so these +; tests can only test the happy path. Once we eventually have annotations for +; expected traps, the cases here should be expanded. + +function %f0(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 0x7f + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; run: %f0(0) == 0x7f +; run: %f0(0x80) == 0xff + +function %f1(i32) -> i32 { +block0(v0: i32): + v1 = iconst.i32 0x7f + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; run: %f0(0) == 0x7f +; run: %f0(0x80) == 0xff + +function %f2(i32, i32) -> i32 { +block0(v0: i32, v1: i32): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; run: %f2(0, 0) == 0x0 +; run: %f2(0x80, 0x7f) == 0xff + +function %f3(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 0x7f + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; run: %f3(0) == 0x7f +; run: %f3(0x80) == 0xff + +function %f4(i64) -> i64 { +block0(v0: i64): + v1 = iconst.i64 0x7f + v2 = uadd_overflow_trap v1, v0, user0 + return v2 +} + +; run: %f4(0) == 0x7f +; run: %f4(0x80) == 0xff + +function %f5(i64, i64) -> i64 { +block0(v0: i64, v1: i64): + v2 = uadd_overflow_trap v0, v1, user0 + return v2 +} + +; run: %f5(0, 0) == 0x0 +; run: %f5(0x80, 0x7f) == 0xff diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs index 4b886cb56f..68d7a53630 100644 --- a/cranelift/interpreter/src/step.rs +++ b/cranelift/interpreter/src/step.rs @@ -753,6 +753,15 @@ where assign_multiple(&[sum, Value::bool(carry, false, types::I8)?]) } Opcode::IaddIfcarry => unimplemented!("IaddIfcarry"), + Opcode::UaddOverflowTrap => { + let sum = Value::add(arg(0)?, arg(1)?)?; + let carry = Value::lt(&sum, &arg(0)?)? && Value::lt(&sum, &arg(1)?)?; + if carry { + ControlFlow::Trap(CraneliftTrap::User(trap_code())) + } else { + assign(sum) + } + } Opcode::IsubBin => choose( Value::into_bool(arg(2)?)?, Value::sub(arg(0)?, Value::add(arg(1)?, Value::int(1, ctrl_ty)?)?)?, diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index e025ef6e7c..794648ce2a 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -3091,6 +3091,18 @@ impl<'a> Parser<'a> { args: [arg, addr], } } + InstructionFormat::IntAddTrap => { + let a = self.match_value("expected SSA value operand")?; + self.match_token(Token::Comma, "expected ',' between operands")?; + let b = self.match_value("expected SSA value operand")?; + self.match_token(Token::Comma, "expected ',' between operands")?; + let code = self.match_enum("expected trap code")?; + InstructionData::IntAddTrap { + opcode, + args: [a, b], + code, + } + } }; Ok(idata) }