From 02620441c3f50e5cb409196ff076c71d1114dc59 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Thu, 27 Oct 2022 09:43:15 -0700 Subject: [PATCH] Add uadd_overflow_trap (#5123) Add a new instruction uadd_overflow_trap, which is a fused version of iadd_ifcout and trapif. Adding this instruction removes a dependency on the iflags type, and would allow us to move closer to removing it entirely. The instruction is defined for the i32 and i64 types only, and is currently only used in the legalization of heap_addr. --- cranelift/codegen/meta/src/shared/formats.rs | 7 ++ .../codegen/meta/src/shared/instructions.rs | 28 +++++ cranelift/codegen/src/isa/aarch64/inst.isle | 8 ++ cranelift/codegen/src/isa/aarch64/lower.isle | 5 + .../codegen/src/isa/aarch64/lower_inst.rs | 2 + cranelift/codegen/src/isa/riscv64/inst.isle | 20 ++-- cranelift/codegen/src/isa/riscv64/lower.isle | 11 +- cranelift/codegen/src/isa/s390x/inst.isle | 48 ++++++++ cranelift/codegen/src/isa/s390x/lower.isle | 54 +++++++++ cranelift/codegen/src/isa/s390x/lower.rs | 1 + cranelift/codegen/src/isa/x64/lower.isle | 35 ++++++ cranelift/codegen/src/isa/x64/lower.rs | 1 + cranelift/codegen/src/legalizer/heap.rs | 9 +- cranelift/codegen/src/prelude_lower.isle | 8 ++ cranelift/codegen/src/verifier/mod.rs | 1 + cranelift/codegen/src/write.rs | 1 + .../isa/aarch64/uadd_overflow_trap.clif | 77 +++++++++++++ .../isa/riscv64/uadd_overflow_trap.clif | 97 +++++++++++++++++ .../isa/s390x/uadd_overflow_trap.clif | 73 +++++++++++++ .../filetests/isa/x64/uadd_overflow_trap.clif | 103 ++++++++++++++++++ .../runtests/uadd_overflow_trap.clif | 68 ++++++++++++ cranelift/interpreter/src/step.rs | 9 ++ cranelift/reader/src/parser.rs | 12 ++ 23 files changed, 660 insertions(+), 18 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/aarch64/uadd_overflow_trap.clif create mode 100644 cranelift/filetests/filetests/isa/riscv64/uadd_overflow_trap.clif create mode 100644 cranelift/filetests/filetests/isa/s390x/uadd_overflow_trap.clif create mode 100644 cranelift/filetests/filetests/isa/x64/uadd_overflow_trap.clif create mode 100644 cranelift/filetests/filetests/runtests/uadd_overflow_trap.clif 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) }