cranelift: Use GPR newtypes extensively in x64 lowering (#3798)

We already defined the `Gpr` newtype and used it in a few places, and we already
defined the `Xmm` newtype and used it extensively. This finishes the transition
to using the newtypes extensively in lowering by making use of `Gpr` in more
places.

Fixes #3685
This commit is contained in:
Nick Fitzgerald
2022-02-14 12:54:41 -08:00
committed by GitHub
parent 84b9c7bb8a
commit dc86e7a6dc
9 changed files with 1804 additions and 1482 deletions

View File

@@ -16,9 +16,9 @@
;; Integer arithmetic/bit-twiddling.
(AluRmiR (size OperandSize) ;; 4 or 8
(op AluRmiROpcode)
(src1 Reg)
(src2 RegMemImm)
(dst WritableReg))
(src1 Gpr)
(src2 GprMemImm)
(dst WritableGpr))
;; Instructions on general-purpose registers that only read src and
;; defines dst (dst is not modified). `bsr`, etc.
@@ -40,19 +40,19 @@
;; Integer quotient and remainder: (div idiv) $rax $rdx (reg addr)
(Div (size OperandSize) ;; 1, 2, 4, or 8
(signed bool)
(divisor RegMem)
(dividend Reg)
(dst_quotient WritableReg)
(dst_remainder WritableReg))
(divisor GprMem)
(dividend Gpr)
(dst_quotient WritableGpr)
(dst_remainder WritableGpr))
;; The high (and low) bits of a (un)signed multiply: `RDX:RAX := RAX *
;; rhs`.
(MulHi (size OperandSize)
(signed bool)
(src1 Reg)
(src2 RegMem)
(dst_lo WritableReg)
(dst_hi WritableReg))
(src1 Gpr)
(src2 GprMem)
(dst_lo WritableGpr)
(dst_hi WritableGpr))
;; A synthetic sequence to implement the right inline checks for
;; remainder and division, assuming the dividend is in %rax.
@@ -69,32 +69,32 @@
;; regalloc failures where %rdx is live before its first def!
(CheckedDivOrRemSeq (kind DivOrRemKind)
(size OperandSize)
(dividend Reg)
(dividend Gpr)
;; The divisor operand. Note it's marked as modified
;; so that it gets assigned a register different from
;; the temporary.
(divisor WritableReg)
(dst_quotient WritableReg)
(dst_remainder WritableReg)
(tmp OptionWritableReg))
(divisor WritableGpr)
(dst_quotient WritableGpr)
(dst_remainder WritableGpr)
(tmp OptionWritableGpr))
;; Do a sign-extend based on the sign of the value in rax into rdx: (cwd
;; cdq cqo) or al into ah: (cbw)
(SignExtendData (size OperandSize) ;; 1, 2, 4, or 8
(src Reg)
(dst WritableReg))
(src Gpr)
(dst WritableGpr))
;; Constant materialization: (imm32 imm64) reg.
;;
;; Either: movl $imm32, %reg32 or movabsq $imm64, %reg32.
(Imm (dst_size OperandSize) ;; 4 or 8
(simm64 u64)
(dst WritableReg))
(dst WritableGpr))
;; GPR to GPR move: mov (64 32) reg reg.
(MovRR (size OperandSize) ;; 4 or 8
(src Reg)
(dst WritableReg))
(src Gpr)
(dst WritableGpr))
;; Zero-extended loads, except for 64 bits: movz (bl bq wl wq lq) addr
;; reg.
@@ -103,12 +103,12 @@
;; zero-extend rule makes it unnecessary. For that case we emit the
;; equivalent "movl AM, reg32".
(MovzxRmR (ext_mode ExtMode)
(src RegMem)
(dst WritableReg))
(src GprMem)
(dst WritableGpr))
;; A plain 64-bit integer load, since MovZX_RM_R can't represent that.
(Mov64MR (src SyntheticAmode)
(dst WritableReg))
(dst WritableGpr))
;; Loads the memory address of addr into dst.
(LoadEffectiveAddress (addr SyntheticAmode)
@@ -116,22 +116,22 @@
;; Sign-extended loads and moves: movs (bl bq wl wq lq) addr reg.
(MovsxRmR (ext_mode ExtMode)
(src RegMem)
(dst WritableReg))
(src GprMem)
(dst WritableGpr))
;; Integer stores: mov (b w l q) reg addr.
(MovRM (size OperandSize) ;; 1, 2, 4, or 8
(src Reg)
(src Gpr)
(dst SyntheticAmode))
;; Arithmetic shifts: (shl shr sar) (b w l q) imm reg.
(ShiftR (size OperandSize) ;; 1, 2, 4, or 8
(kind ShiftKind)
(src Reg)
;; shift count: `Imm8Reg::Imm8(0 .. #bits-in-type - 1)` or
;; `Imm8Reg::Reg(r)` where `r` get's move mitosis'd into `%cl`.
(num_bits Imm8Reg)
(dst WritableReg))
(src Gpr)
;; shift count: `Imm8Gpr::Imm8(0 .. #bits-in-type - 1)` or
;; `Imm8Reg::Gpr(r)` where `r` get's move mitosis'd into `%cl`.
(num_bits Imm8Gpr)
(dst WritableGpr))
;; Arithmetic SIMD shifts.
(XmmRmiReg (opcode SseOpcode)
@@ -142,30 +142,30 @@
;; Integer comparisons/tests: cmp or test (b w l q) (reg addr imm) reg.
(CmpRmiR (size OperandSize) ;; 1, 2, 4, or 8
(opcode CmpOpcode)
(src RegMemImm)
(dst Reg))
(src GprMemImm)
(dst Gpr))
;; Materializes the requested condition code in the destinaton reg.
(Setcc (cc CC)
(dst WritableReg))
(dst WritableGpr))
;; Integer conditional move.
;;
;; Overwrites the destination register.
(Cmove (size OperandSize)
(cc CC)
(consequent RegMem)
(alternative Reg)
(dst WritableReg))
(consequent GprMem)
(alternative Gpr)
(dst WritableGpr))
;; =========================================
;; Stack manipulation.
;; pushq (reg addr imm)
(Push64 (src RegMemImm))
(Push64 (src GprMemImm))
;; popq reg
(Pop64 (dst WritableReg))
(Pop64 (dst WritableGpr))
;; =========================================
;; Floating-point operations.
@@ -221,7 +221,7 @@
;; XMM (scalar) unary op (from integer to float reg): movd, movq,
;; cvtsi2s{s,d}
(GprToXmm (op SseOpcode)
(src RegMem)
(src GprMem)
(dst WritableXmm)
(src_size OperandSize))
@@ -272,21 +272,21 @@
;; registers.
(XmmMinMaxSeq (size OperandSize)
(is_min bool)
(lhs Reg)
(rhs_dst WritableReg))
(lhs Xmm)
(rhs_dst WritableXmm))
;; XMM (scalar) conditional move.
;;
;; Overwrites the destination register if cc is set.
(XmmCmove (size OperandSize)
(cc CC)
(src RegMem)
(dst WritableReg))
(src XmmMem)
(dst WritableXmm))
;; Float comparisons/tests: cmp (b w l q) (reg addr imm) reg.
(XmmCmpRmR (op SseOpcode)
(src RegMem)
(dst Reg))
(src XmmMem)
(dst Xmm))
;; A binary XMM instruction with an 8-bit immediate: e.g. cmp (ps pd) imm
;; (reg addr) reg
@@ -780,8 +780,8 @@
;; As a side effect, this marks the value as used.
;;
;; This is used when lowering various shifts and rotates.
(decl put_masked_in_imm8_reg (Value Type) Imm8Reg)
(extern constructor put_masked_in_imm8_reg put_masked_in_imm8_reg)
(decl put_masked_in_imm8_gpr (Value Type) Imm8Gpr)
(extern constructor put_masked_in_imm8_gpr put_masked_in_imm8_gpr)
(type CC extern
(enum O
@@ -825,14 +825,21 @@
(type Gpr (primitive Gpr))
(type WritableGpr (primitive WritableGpr))
(type OptionWritableGpr (primitive OptionWritableGpr))
(type GprMem extern (enum))
(type GprMemImm extern (enum))
(type Imm8Gpr extern (enum))
(type Xmm (primitive Xmm))
(type WritableXmm (primitive WritableXmm))
(type OptionWritableXmm (primitive OptionWritableXmm))
(type XmmMem extern (enum))
(type XmmMemImm extern (enum))
;; Convert an `Imm8Reg` into an `Imm8Gpr`.
(decl imm8_reg_to_imm8_gpr (Imm8Reg) Imm8Gpr)
(extern constructor imm8_reg_to_imm8_gpr imm8_reg_to_imm8_gpr)
;; Convert a `WritableGpr` to a `WritableReg`.
(decl writable_gpr_to_reg (WritableGpr) WritableReg)
(extern constructor writable_gpr_to_reg writable_gpr_to_reg)
@@ -857,6 +864,14 @@
(decl gpr_to_reg (Gpr) Reg)
(extern constructor gpr_to_reg gpr_to_reg)
;; Convert an `Gpr` to a `GprMem`.
(decl gpr_to_gpr_mem (Gpr) GprMem)
(extern constructor gpr_to_gpr_mem gpr_to_gpr_mem)
;; Convert an `Gpr` to a `GprMemImm`.
(decl gpr_to_gpr_mem_imm (Gpr) GprMemImm)
(extern constructor gpr_to_gpr_mem_imm gpr_to_gpr_mem_imm)
;; Convert an `Xmm` to a `Reg`.
(decl xmm_to_reg (Xmm) Reg)
(extern constructor xmm_to_reg xmm_to_reg)
@@ -984,6 +999,25 @@
(rule (value_xmm x)
(value_reg (xmm_to_reg x)))
;; Get the `n`th reg in a `ValueRegs` and construct a GPR from it.
;;
;; Asserts that the register is a GPR.
(decl value_regs_get_gpr (ValueRegs usize) Gpr)
(rule (value_regs_get_gpr regs n)
(gpr_new (value_regs_get regs n)))
;; Convert a `Gpr` to an `Imm8Gpr`.
(decl gpr_to_imm8_gpr (Gpr) Imm8Gpr)
(extern constructor gpr_to_imm8_gpr gpr_to_imm8_gpr)
;; Convert an 8-bit immediate into an `Imm8Gpr`.
(decl imm8_to_imm8_gpr (u8) Imm8Gpr)
(extern constructor imm8_to_imm8_gpr imm8_to_imm8_gpr)
;; Get the low half of the given `Value` as a GPR.
(decl lo_gpr (Value) Gpr)
(rule (lo_gpr regs) (gpr_new (lo_reg regs)))
;;;; Helpers for Getting Particular Physical Registers ;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; These should only be used for legalization purposes, when we can't otherwise
@@ -1014,15 +1048,15 @@
;; `Imm8Reg.Imm8`. This is used for shifts and rotates, so that we don't try and
;; shift/rotate more bits than the type has available, per Cranelift's
;; semantics.
(decl const_to_type_masked_imm8 (u64 Type) Imm8Reg)
(decl const_to_type_masked_imm8 (u64 Type) Imm8Gpr)
(extern constructor const_to_type_masked_imm8 const_to_type_masked_imm8)
;; Extract a constant `RegMemImm.Imm` from a value operand.
(decl simm32_from_value (RegMemImm) Value)
;; Extract a constant `GprMemImm.Imm` from a value operand.
(decl simm32_from_value (GprMemImm) Value)
(extern extractor simm32_from_value simm32_from_value)
;; Extract a constant `RegMemImm.Imm` from an `Imm64` immediate.
(decl simm32_from_imm64 (RegMemImm) Imm64)
(decl simm32_from_imm64 (GprMemImm) Imm64)
(extern extractor simm32_from_imm64 simm32_from_imm64)
;; A load that can be sunk into another operation.
@@ -1041,6 +1075,10 @@
(decl sink_load (SinkableLoad) RegMemImm)
(extern constructor sink_load sink_load)
(decl sink_load_to_gpr_mem_imm (SinkableLoad) GprMemImm)
(rule (sink_load_to_gpr_mem_imm load)
(gpr_mem_imm_new (sink_load load)))
;;;; Helpers for Sign/Zero Extending ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(type ExtKind extern
@@ -1057,13 +1095,13 @@
(extern constructor ext_mode ext_mode)
;; Put the given value into a register, but extended as the given type.
(decl extend_to_reg (Value Type ExtendKind) Reg)
(decl extend_to_gpr (Value Type ExtendKind) Gpr)
;; If the value is already of the requested type, no extending is necessary.
(rule (extend_to_reg (and val (value_type ty)) =ty _kind)
(put_in_reg val))
(rule (extend_to_gpr (and val (value_type ty)) =ty _kind)
(put_in_gpr val))
(rule (extend_to_reg (and val (value_type from_ty))
(rule (extend_to_gpr (and val (value_type from_ty))
to_ty
kind)
(let ((from_bits u16 (ty_bits_u16 from_ty))
@@ -1073,10 +1111,10 @@
(extend kind
to_ty
(ext_mode from_bits to_bits)
(put_in_reg_mem val))))
(put_in_gpr_mem val))))
;; Do a sign or zero extension of the given `RegMem`.
(decl extend (ExtendKind Type ExtMode RegMem) Reg)
;; Do a sign or zero extension of the given `GprMem`.
(decl extend (ExtendKind Type ExtMode GprMem) Gpr)
;; Zero extending uses `movzx`.
(rule (extend (ExtendKind.Zero) ty mode src)
@@ -1166,14 +1204,14 @@
(decl x64_load (Type SyntheticAmode ExtKind) Reg)
(rule (x64_load (fits_in_32 ty) addr (ExtKind.SignExtend))
(movsx ty
(ext_mode (ty_bytes ty) 8)
(synthetic_amode_to_reg_mem addr)))
(gpr_to_reg (movsx ty
(ext_mode (ty_bytes ty) 8)
(reg_mem_to_gpr_mem (synthetic_amode_to_reg_mem addr)))))
(rule (x64_load $I64 addr _ext_kind)
(let ((dst WritableReg (temp_writable_reg $I64))
(let ((dst WritableGpr (temp_writable_gpr))
(_ Unit (emit (MInst.Mov64MR addr dst))))
(writable_reg_to_reg dst)))
(gpr_to_reg (writable_gpr_to_gpr dst))))
(rule (x64_load $F32 addr _ext_kind)
(xmm_to_reg (xmm_unary_rm_r (SseOpcode.Movss)
@@ -1202,15 +1240,15 @@
;; only gets defined the once.
;; Helper for emitting `MInst.AluRmiR` instructions.
(decl alu_rmi_r (Type AluRmiROpcode Reg RegMemImm) Reg)
(decl alu_rmi_r (Type AluRmiROpcode Gpr GprMemImm) Gpr)
(rule (alu_rmi_r ty opcode src1 src2)
(let ((dst WritableReg (temp_writable_reg ty))
(let ((dst WritableGpr (temp_writable_gpr))
(size OperandSize (operand_size_of_type_32_64 ty))
(_ Unit (emit (MInst.AluRmiR size opcode src1 src2 dst))))
(writable_reg_to_reg dst)))
(writable_gpr_to_gpr dst)))
;; Helper for emitting `add` instructions.
(decl add (Type Reg RegMemImm) Reg)
(decl add (Type Gpr GprMemImm) Gpr)
(rule (add ty src1 src2)
(alu_rmi_r ty
(AluRmiROpcode.Add)
@@ -1218,29 +1256,29 @@
src2))
;; Helper for creating `add` instructions whose flags are also used.
(decl add_with_flags (Type Reg RegMemImm) ProducesFlags)
(decl add_with_flags (Type Gpr GprMemImm) ProducesFlags)
(rule (add_with_flags ty src1 src2)
(let ((dst WritableReg (temp_writable_reg ty)))
(let ((dst WritableGpr (temp_writable_gpr)))
(ProducesFlags.ProducesFlags (MInst.AluRmiR (operand_size_of_type_32_64 ty)
(AluRmiROpcode.Add)
src1
src2
dst)
(writable_reg_to_reg dst))))
(gpr_to_reg (writable_gpr_to_gpr dst)))))
;; Helper for creating `adc` instructions.
(decl adc (Type Reg RegMemImm) ConsumesFlags)
(decl adc (Type Gpr GprMemImm) ConsumesFlags)
(rule (adc ty src1 src2)
(let ((dst WritableReg (temp_writable_reg ty)))
(let ((dst WritableGpr (temp_writable_gpr)))
(ConsumesFlags.ConsumesFlags (MInst.AluRmiR (operand_size_of_type_32_64 ty)
(AluRmiROpcode.Adc)
src1
src2
dst)
(writable_reg_to_reg dst))))
(gpr_to_reg (writable_gpr_to_gpr dst)))))
;; Helper for emitting `sub` instructions.
(decl sub (Type Reg RegMemImm) Reg)
(decl sub (Type Gpr GprMemImm) Gpr)
(rule (sub ty src1 src2)
(alu_rmi_r ty
(AluRmiROpcode.Sub)
@@ -1248,29 +1286,29 @@
src2))
;; Helper for creating `sub` instructions whose flags are also used.
(decl sub_with_flags (Type Reg RegMemImm) ProducesFlags)
(decl sub_with_flags (Type Gpr GprMemImm) ProducesFlags)
(rule (sub_with_flags ty src1 src2)
(let ((dst WritableReg (temp_writable_reg ty)))
(let ((dst WritableGpr (temp_writable_gpr)))
(ProducesFlags.ProducesFlags (MInst.AluRmiR (operand_size_of_type_32_64 ty)
(AluRmiROpcode.Sub)
src1
src2
dst)
(writable_reg_to_reg dst))))
(gpr_to_reg (writable_gpr_to_gpr dst)))))
;; Helper for creating `sbb` instructions.
(decl sbb (Type Reg RegMemImm) ConsumesFlags)
(decl sbb (Type Gpr GprMemImm) ConsumesFlags)
(rule (sbb ty src1 src2)
(let ((dst WritableReg (temp_writable_reg ty)))
(let ((dst WritableGpr (temp_writable_gpr)))
(ConsumesFlags.ConsumesFlags (MInst.AluRmiR (operand_size_of_type_32_64 ty)
(AluRmiROpcode.Sbb)
src1
src2
dst)
(writable_reg_to_reg dst))))
(gpr_to_reg (writable_gpr_to_gpr dst)))))
;; Helper for creating `mul` instructions.
(decl mul (Type Reg RegMemImm) Reg)
(decl mul (Type Gpr GprMemImm) Gpr)
(rule (mul ty src1 src2)
(alu_rmi_r ty
(AluRmiROpcode.Mul)
@@ -1278,10 +1316,7 @@
src2))
;; Helper for emitting `and` instructions.
;;
;; Use `m_` prefix (short for "mach inst") to disambiguate with the ISLE-builtin
;; `and` operator.
(decl x64_and (Type Reg RegMemImm) Reg)
(decl x64_and (Type Gpr GprMemImm) Gpr)
(rule (x64_and ty src1 src2)
(alu_rmi_r ty
(AluRmiROpcode.And)
@@ -1289,7 +1324,7 @@
src2))
;; Helper for emitting `or` instructions.
(decl or (Type Reg RegMemImm) Reg)
(decl or (Type Gpr GprMemImm) Gpr)
(rule (or ty src1 src2)
(alu_rmi_r ty
(AluRmiROpcode.Or)
@@ -1297,7 +1332,7 @@
src2))
;; Helper for emitting `xor` instructions.
(decl xor (Type Reg RegMemImm) Reg)
(decl xor (Type Gpr GprMemImm) Gpr)
(rule (xor ty src1 src2)
(alu_rmi_r ty
(AluRmiROpcode.Xor)
@@ -1309,10 +1344,10 @@
;; Integer immediates.
(rule (imm (fits_in_64 ty) simm64)
(let ((dst WritableReg (temp_writable_reg ty))
(let ((dst WritableGpr (temp_writable_gpr))
(size OperandSize (operand_size_of_type_32_64 ty))
(_ Unit (emit (MInst.Imm size simm64 dst))))
(writable_reg_to_reg dst)))
(gpr_to_reg (writable_gpr_to_gpr dst))))
;; `f32` immediates.
(rule (imm $F32 bits)
@@ -1332,21 +1367,21 @@
;; Special case for when a 64-bit immediate fits into 32-bits. We can use a
;; 32-bit move that zero-extends the value, which has a smaller encoding.
(rule (imm $I64 (nonzero_u64_fits_in_u32 x))
(let ((dst WritableReg (temp_writable_reg $I64))
(let ((dst WritableGpr (temp_writable_gpr))
(_ Unit (emit (MInst.Imm (OperandSize.Size32) x dst))))
(writable_reg_to_reg dst)))
(gpr_to_reg (writable_gpr_to_gpr dst))))
;; Special case for integer zero immediates: turn them into an `xor r, r`.
(rule (imm (fits_in_64 ty) 0)
(let ((wr WritableReg (temp_writable_reg ty))
(r Reg (writable_reg_to_reg wr))
(let ((wgpr WritableGpr (temp_writable_gpr))
(g Gpr (writable_gpr_to_gpr wgpr))
(size OperandSize (operand_size_of_type_32_64 ty))
(_ Unit (emit (MInst.AluRmiR size
(AluRmiROpcode.Xor)
r
(RegMemImm.Reg r)
wr))))
r))
g
(gpr_to_gpr_mem_imm g)
wgpr))))
(gpr_to_reg g)))
;; Special case for zero immediates with vector types, they turn into an xor
;; specific to the vector type.
@@ -1384,44 +1419,42 @@
;; TODO: use cmpeqpd for all 1s
;; Helper for creating `MInst.ShifR` instructions.
(decl shift_r (Type ShiftKind Reg Imm8Reg) Reg)
(decl shift_r (Type ShiftKind Gpr Imm8Gpr) Gpr)
(rule (shift_r ty kind src1 src2)
(let ((dst WritableReg (temp_writable_reg ty))
(let ((dst WritableGpr (temp_writable_gpr))
;; Use actual 8/16-bit instructions when appropriate: we
;; rely on their shift-amount-masking semantics.
(size OperandSize (raw_operand_size_of_type ty))
(_ Unit (emit (MInst.ShiftR size kind src1 src2 dst))))
(writable_reg_to_reg dst)))
(writable_gpr_to_gpr dst)))
;; Helper for creating `rotl` instructions (prefixed with "m_", short for "mach
;; inst", to disambiguate this from clif's `rotl`).
(decl x64_rotl (Type Reg Imm8Reg) Reg)
;; Helper for creating `rotl` instructions.
(decl x64_rotl (Type Gpr Imm8Gpr) Gpr)
(rule (x64_rotl ty src1 src2)
(shift_r ty (ShiftKind.RotateLeft) src1 src2))
;; Helper for creating `rotr` instructions (prefixed with "m_", short for "mach
;; inst", to disambiguate this from clif's `rotr`).
(decl x64_rotr (Type Reg Imm8Reg) Reg)
;; Helper for creating `rotr` instructions.
(decl x64_rotr (Type Gpr Imm8Gpr) Gpr)
(rule (x64_rotr ty src1 src2)
(shift_r ty (ShiftKind.RotateRight) src1 src2))
;; Helper for creating `shl` instructions.
(decl shl (Type Reg Imm8Reg) Reg)
(decl shl (Type Gpr Imm8Gpr) Gpr)
(rule (shl ty src1 src2)
(shift_r ty (ShiftKind.ShiftLeft) src1 src2))
;; Helper for creating logical shift-right instructions.
(decl shr (Type Reg Imm8Reg) Reg)
(decl shr (Type Gpr Imm8Gpr) Gpr)
(rule (shr ty src1 src2)
(shift_r ty (ShiftKind.ShiftRightLogical) src1 src2))
;; Helper for creating arithmetic shift-right instructions.
(decl sar (Type Reg Imm8Reg) Reg)
(decl sar (Type Gpr Imm8Gpr) Gpr)
(rule (sar ty src1 src2)
(shift_r ty (ShiftKind.ShiftRightArithmetic) src1 src2))
;; Helper for creating `MInst.CmpRmiR` instructions.
(decl cmp_rmi_r (OperandSize CmpOpcode RegMemImm Reg) ProducesFlags)
(decl cmp_rmi_r (OperandSize CmpOpcode GprMemImm Gpr) ProducesFlags)
(rule (cmp_rmi_r size opcode src1 src2)
(ProducesFlags.ProducesFlags (MInst.CmpRmiR size
opcode
@@ -1430,36 +1463,36 @@
(invalid_reg)))
;; Helper for creating `cmp` instructions.
(decl cmp (OperandSize RegMemImm Reg) ProducesFlags)
(decl cmp (OperandSize GprMemImm Gpr) ProducesFlags)
(rule (cmp size src1 src2)
(cmp_rmi_r size (CmpOpcode.Cmp) src1 src2))
;; Helper for creating `test` instructions.
(decl test (OperandSize RegMemImm Reg) ProducesFlags)
(decl test (OperandSize GprMemImm Gpr) ProducesFlags)
(rule (test size src1 src2)
(cmp_rmi_r size (CmpOpcode.Test) src1 src2))
;; Helper for creating `MInst.Cmove` instructions.
(decl cmove (Type CC RegMem Reg) ConsumesFlags)
(decl cmove (Type CC GprMem Gpr) ConsumesFlags)
(rule (cmove ty cc consequent alternative)
(let ((dst WritableReg (temp_writable_reg ty))
(let ((dst WritableGpr (temp_writable_gpr))
(size OperandSize (operand_size_of_type_32_64 ty)))
(ConsumesFlags.ConsumesFlags (MInst.Cmove size cc consequent alternative dst)
(writable_reg_to_reg dst))))
(gpr_to_reg (writable_gpr_to_gpr dst)))))
;; Helper for creating `MInst.MovzxRmR` instructions.
(decl movzx (Type ExtMode RegMem) Reg)
(decl movzx (Type ExtMode GprMem) Gpr)
(rule (movzx ty mode src)
(let ((dst WritableReg (temp_writable_reg ty))
(let ((dst WritableGpr (temp_writable_gpr))
(_ Unit (emit (MInst.MovzxRmR mode src dst))))
(writable_reg_to_reg dst)))
(writable_gpr_to_gpr dst)))
;; Helper for creating `MInst.MovsxRmR` instructions.
(decl movsx (Type ExtMode RegMem) Reg)
(decl movsx (Type ExtMode GprMem) Gpr)
(rule (movsx ty mode src)
(let ((dst WritableReg (temp_writable_reg ty))
(let ((dst WritableGpr (temp_writable_gpr))
(_ Unit (emit (MInst.MovsxRmR mode src dst))))
(writable_reg_to_reg dst)))
(writable_gpr_to_gpr dst)))
;; Helper for creating `MInst.XmmRmR` instructions.
(decl xmm_rm_r (Type SseOpcode Xmm XmmMem) Xmm)
@@ -1926,10 +1959,10 @@
;; Helper for creating `MInst.MulHi` instructions.
;;
;; Returns the (lo, hi) register halves of the multiplication.
(decl mul_hi (Type bool Reg RegMem) ValueRegs)
(decl mul_hi (Type bool Gpr GprMem) ValueRegs)
(rule (mul_hi ty signed src1 src2)
(let ((dst_lo WritableReg (temp_writable_reg ty))
(dst_hi WritableReg (temp_writable_reg ty))
(let ((dst_lo WritableGpr (temp_writable_gpr))
(dst_hi WritableGpr (temp_writable_gpr))
(size OperandSize (operand_size_of_type_32_64 ty))
(_ Unit (emit (MInst.MulHi size
signed
@@ -1937,12 +1970,12 @@
src2
dst_lo
dst_hi))))
(value_regs (writable_reg_to_reg dst_lo)
(writable_reg_to_reg dst_hi))))
(value_gprs (writable_gpr_to_gpr dst_lo)
(writable_gpr_to_gpr dst_hi))))
;; Helper for creating `mul` instructions that return both the lower and
;; (unsigned) higher halves of the result.
(decl mulhi_u (Type Reg RegMem) ValueRegs)
(decl mulhi_u (Type Gpr GprMem) ValueRegs)
(rule (mulhi_u ty src1 src2)
(mul_hi ty $false src1 src2))
@@ -2026,7 +2059,7 @@
(decl gpr_to_xmm (SseOpcode GprMem OperandSize) Xmm)
(rule (gpr_to_xmm op src size)
(let ((dst WritableXmm (temp_writable_xmm))
(_ Unit (emit (MInst.GprToXmm op (gpr_mem_to_reg_mem src) dst size))))
(_ Unit (emit (MInst.GprToXmm op src dst size))))
(writable_xmm_to_xmm dst)))
;; Helper for creating `not` instructions.

View File

@@ -48,8 +48,10 @@ macro_rules! newtype_of_reg {
(
$newtype_reg:ident,
$newtype_writable_reg:ident,
$newtype_option_writable_reg:ident,
$newtype_reg_mem:ident,
$newtype_reg_mem_imm:ident,
$newtype_imm8_reg:ident,
|$check_reg:ident| $check:expr
) => {
/// A newtype wrapper around `Reg`.
@@ -122,6 +124,9 @@ macro_rules! newtype_of_reg {
pub type $newtype_writable_reg = Writable<$newtype_reg>;
#[allow(dead_code)] // Used by some newtypes and not others.
pub type $newtype_option_writable_reg = Option<Writable<$newtype_reg>>;
impl ToWritableReg for $newtype_writable_reg {
fn to_writable_reg(&self) -> Writable<Reg> {
Writable::from_reg(self.to_reg().to_reg())
@@ -218,6 +223,11 @@ macro_rules! newtype_of_reg {
_ => true,
});
}
#[allow(dead_code)] // Used by some newtypes and not others.
pub fn get_regs_as_uses(&self, collector: &mut RegUsageCollector) {
self.0.get_regs_as_uses(collector);
}
}
impl PrettyPrint for $newtype_reg_mem {
@@ -290,6 +300,11 @@ macro_rules! newtype_of_reg {
_ => true,
});
}
#[allow(dead_code)] // Used by some newtypes and not others.
pub fn get_regs_as_uses(&self, collector: &mut RegUsageCollector) {
self.0.get_regs_as_uses(collector);
}
}
impl PrettyPrint for $newtype_reg_mem_imm {
@@ -303,18 +318,60 @@ macro_rules! newtype_of_reg {
self.0.show_rru_sized(mb_rru, size)
}
}
/// A newtype wrapper around `Imm8Reg`.
#[derive(Clone, Debug)]
#[allow(dead_code)] // Used by some newtypes and not others.
pub struct $newtype_imm8_reg(Imm8Reg);
impl From<$newtype_reg> for $newtype_imm8_reg {
fn from(r: $newtype_reg) -> Self {
Self(Imm8Reg::Reg { reg: r.to_reg() })
}
}
impl $newtype_imm8_reg {
/// Construct this newtype from the given `Imm8Reg`, or return
/// `None` if the `Imm8Reg` is not a valid instance of this newtype.
#[allow(dead_code)] // Used by some newtypes and not others.
pub fn new(imm8_reg: Imm8Reg) -> Option<Self> {
match imm8_reg {
Imm8Reg::Imm8 { .. } => Some(Self(imm8_reg)),
Imm8Reg::Reg { reg: $check_reg } if $check => Some(Self(imm8_reg)),
Imm8Reg::Reg { reg: _ } => None,
}
}
/// Convert this newtype into its underlying `Imm8Reg`.
#[allow(dead_code)] // Used by some newtypes and not others.
pub fn to_imm8_reg(self) -> Imm8Reg {
self.0
}
}
};
}
// Define a newtype of `Reg` for general-purpose registers.
newtype_of_reg!(Gpr, WritableGpr, GprMem, GprMemImm, |reg| {
reg.get_class() == RegClass::I64
});
newtype_of_reg!(
Gpr,
WritableGpr,
OptionWritableGpr,
GprMem,
GprMemImm,
Imm8Gpr,
|reg| reg.get_class() == RegClass::I64
);
// Define a newtype of `Reg` for XMM registers.
newtype_of_reg!(Xmm, WritableXmm, XmmMem, XmmMemImm, |reg| {
reg.get_class() == RegClass::V128
});
newtype_of_reg!(
Xmm,
WritableXmm,
OptionWritableXmm,
XmmMem,
XmmMemImm,
Imm8Xmm,
|reg| reg.get_class() == RegClass::V128
);
/// A possible addressing mode (amode) that can be used in instructions.
/// These denote a 64-bit value only.

View File

@@ -156,15 +156,15 @@ pub(crate) fn emit(
if *op == AluRmiROpcode::Mul {
// We kinda freeloaded Mul into RMI_R_Op, but it doesn't fit the usual pattern, so
// we have to special-case it.
match src2 {
match src2.clone().to_reg_mem_imm() {
RegMemImm::Reg { reg: reg_e } => {
emit_std_reg_reg(
sink,
LegacyPrefixes::None,
0x0FAF,
2,
reg_g.to_reg(),
*reg_e,
reg_g.to_reg().to_reg(),
reg_e,
rex,
);
}
@@ -178,14 +178,14 @@ pub(crate) fn emit(
LegacyPrefixes::None,
0x0FAF,
2,
reg_g.to_reg(),
reg_g.to_reg().to_reg(),
&amode,
rex,
);
}
RegMemImm::Imm { simm32 } => {
let use_imm8 = low8_will_sign_extend_to_32(*simm32);
let use_imm8 = low8_will_sign_extend_to_32(simm32);
let opcode = if use_imm8 { 0x6B } else { 0x69 };
// Yes, really, reg_g twice.
emit_std_reg_reg(
@@ -193,11 +193,11 @@ pub(crate) fn emit(
LegacyPrefixes::None,
opcode,
1,
reg_g.to_reg(),
reg_g.to_reg(),
reg_g.to_reg().to_reg(),
reg_g.to_reg().to_reg(),
rex,
);
emit_simm(sink, if use_imm8 { 1 } else { 4 }, *simm32);
emit_simm(sink, if use_imm8 { 1 } else { 4 }, simm32);
}
}
} else {
@@ -215,11 +215,11 @@ pub(crate) fn emit(
};
assert!(!(is_8bit && *size == OperandSize::Size64));
match src2 {
match src2.clone().to_reg_mem_imm() {
RegMemImm::Reg { reg: reg_e } => {
if is_8bit {
rex.always_emit_if_8bit_needed(*reg_e);
rex.always_emit_if_8bit_needed(reg_g.to_reg());
rex.always_emit_if_8bit_needed(reg_e);
rex.always_emit_if_8bit_needed(reg_g.to_reg().to_reg());
}
// GCC/llvm use the swapped operand encoding (viz., the R/RM vs RM/R
// duality). Do this too, so as to be able to compare generated machine
@@ -229,15 +229,15 @@ pub(crate) fn emit(
LegacyPrefixes::None,
opcode_r,
1,
*reg_e,
reg_g.to_reg(),
reg_e,
reg_g.to_reg().to_reg(),
rex,
);
}
RegMemImm::Mem { addr } => {
if is_8bit {
rex.always_emit_if_8bit_needed(reg_g.to_reg());
rex.always_emit_if_8bit_needed(reg_g.to_reg().to_reg());
}
// Here we revert to the "normal" G-E ordering.
let amode = addr.finalize(state, sink);
@@ -248,7 +248,7 @@ pub(crate) fn emit(
LegacyPrefixes::None,
opcode_m,
1,
reg_g.to_reg(),
reg_g.to_reg().to_reg(),
&amode,
rex,
);
@@ -256,10 +256,10 @@ pub(crate) fn emit(
RegMemImm::Imm { simm32 } => {
assert!(!is_8bit);
let use_imm8 = low8_will_sign_extend_to_32(*simm32);
let use_imm8 = low8_will_sign_extend_to_32(simm32);
let opcode = if use_imm8 { 0x83 } else { 0x81 };
// And also here we use the "normal" G-E ordering.
let enc_g = int_reg_enc(reg_g.to_reg());
let enc_g = int_reg_enc(reg_g.to_reg().to_reg());
emit_std_enc_enc(
sink,
LegacyPrefixes::None,
@@ -269,7 +269,7 @@ pub(crate) fn emit(
enc_g,
rex,
);
emit_simm(sink, if use_imm8 { 1 } else { 4 }, *simm32);
emit_simm(sink, if use_imm8 { 1 } else { 4 }, simm32);
}
}
}
@@ -377,9 +377,9 @@ pub(crate) fn emit(
sink.add_trap(loc, TrapCode::IntegerDivisionByZero);
let subopcode = if *signed { 7 } else { 6 };
match divisor {
match divisor.clone().to_reg_mem() {
RegMem::Reg { reg } => {
let src = int_reg_enc(*reg);
let src = int_reg_enc(reg);
emit_std_enc_enc(
sink,
prefix,
@@ -387,7 +387,7 @@ pub(crate) fn emit(
1,
subopcode,
src,
RexFlags::from((*size, *reg)),
RexFlags::from((*size, reg)),
)
}
RegMem::Mem { addr: src } => {
@@ -428,9 +428,9 @@ pub(crate) fn emit(
};
let subopcode = if *signed { 5 } else { 4 };
match src2 {
match src2.clone().to_reg_mem() {
RegMem::Reg { reg } => {
let src = int_reg_enc(*reg);
let src = int_reg_enc(reg);
emit_std_enc_enc(sink, prefix, 0xF7, 1, subopcode, src, rex_flags)
}
RegMem::Mem { addr: src } => {
@@ -504,7 +504,7 @@ pub(crate) fn emit(
// $done:
// Check if the divisor is zero, first.
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0), divisor.to_reg());
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0), divisor.to_reg().to_reg());
inst.emit(sink, info, state);
let inst = Inst::trap_if(CC::Z, TrapCode::IntegerDivisionByZero);
@@ -512,7 +512,8 @@ pub(crate) fn emit(
let (do_op, done_label) = if kind.is_signed() {
// Now check if the divisor is -1.
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0xffffffff), divisor.to_reg());
let inst =
Inst::cmp_rmi_r(*size, RegMemImm::imm(0xffffffff), divisor.to_reg().to_reg());
inst.emit(sink, info, state);
let do_op = sink.get_label();
@@ -537,12 +538,16 @@ pub(crate) fn emit(
if *size == OperandSize::Size64 {
let tmp = tmp.expect("temporary for i64 sdiv");
let inst = Inst::imm(OperandSize::Size64, 0x8000000000000000, tmp);
let inst = Inst::imm(
OperandSize::Size64,
0x8000000000000000,
tmp.to_writable_reg(),
);
inst.emit(sink, info, state);
let inst = Inst::cmp_rmi_r(
OperandSize::Size64,
RegMemImm::reg(tmp.to_reg()),
RegMemImm::reg(tmp.to_reg().to_reg()),
regs::rax(),
);
inst.emit(sink, info, state);
@@ -576,7 +581,11 @@ pub(crate) fn emit(
inst.emit(sink, info, state);
}
let inst = Inst::div(*size, kind.is_signed(), RegMem::reg(divisor.to_reg()));
let inst = Inst::div(
*size,
kind.is_signed(),
RegMem::reg(divisor.to_reg().to_reg()),
);
inst.emit(sink, info, state);
// Lowering takes care of moving the result back into the right register, see comment
@@ -626,8 +635,8 @@ pub(crate) fn emit(
LegacyPrefixes::None,
0x89,
1,
*src,
dst.to_reg(),
src.to_reg(),
dst.to_reg().to_reg(),
RexFlags::from(*size),
);
}
@@ -664,12 +673,12 @@ pub(crate) fn emit(
}
};
match src {
match src.clone().to_reg_mem() {
RegMem::Reg { reg: src } => {
match ext_mode {
ExtMode::BL | ExtMode::BQ => {
// A redundant REX prefix must be emitted for certain register inputs.
rex_flags.always_emit_if_8bit_needed(*src);
rex_flags.always_emit_if_8bit_needed(src);
}
_ => {}
}
@@ -678,8 +687,8 @@ pub(crate) fn emit(
LegacyPrefixes::None,
opcodes,
num_opcodes,
dst.to_reg(),
*src,
dst.to_reg().to_reg(),
src,
rex_flags,
)
}
@@ -694,7 +703,7 @@ pub(crate) fn emit(
LegacyPrefixes::None,
opcodes,
num_opcodes,
dst.to_reg(),
dst.to_reg().to_reg(),
src,
rex_flags,
)
@@ -712,7 +721,7 @@ pub(crate) fn emit(
LegacyPrefixes::None,
0x8B,
1,
dst.to_reg(),
dst.to_reg().to_reg(),
src,
RexFlags::set_w(),
)
@@ -758,12 +767,12 @@ pub(crate) fn emit(
}
};
match src {
match src.clone().to_reg_mem() {
RegMem::Reg { reg: src } => {
match ext_mode {
ExtMode::BL | ExtMode::BQ => {
// A redundant REX prefix must be emitted for certain register inputs.
rex_flags.always_emit_if_8bit_needed(*src);
rex_flags.always_emit_if_8bit_needed(src);
}
_ => {}
}
@@ -772,8 +781,8 @@ pub(crate) fn emit(
LegacyPrefixes::None,
opcodes,
num_opcodes,
dst.to_reg(),
*src,
dst.to_reg().to_reg(),
src,
rex_flags,
)
}
@@ -788,7 +797,7 @@ pub(crate) fn emit(
LegacyPrefixes::None,
opcodes,
num_opcodes,
dst.to_reg(),
dst.to_reg().to_reg(),
src,
rex_flags,
)
@@ -812,13 +821,13 @@ pub(crate) fn emit(
// This is one of the few places where the presence of a
// redundant REX prefix changes the meaning of the
// instruction.
let rex = RexFlags::from((*size, *src));
let rex = RexFlags::from((*size, src.to_reg()));
// 8-bit: MOV r8, r/m8 is (REX.W==0) 88 /r
// 16-bit: MOV r16, r/m16 is 66 (REX.W==0) 89 /r
// 32-bit: MOV r32, r/m32 is (REX.W==0) 89 /r
// 64-bit: MOV r64, r/m64 is (REX.W==1) 89 /r
emit_std_reg_mem(sink, state, info, prefix, opcode, 1, *src, dst, rex);
emit_std_reg_mem(sink, state, info, prefix, opcode, 1, src.to_reg(), dst, rex);
}
Inst::ShiftR {
@@ -837,10 +846,10 @@ pub(crate) fn emit(
ShiftKind::ShiftRightArithmetic => 7,
};
let enc_dst = int_reg_enc(dst.to_reg());
let rex_flags = RexFlags::from((*size, dst.to_reg()));
match num_bits {
let rex_flags = RexFlags::from((*size, dst.to_reg().to_reg()));
match num_bits.clone().to_imm8_reg() {
Imm8Reg::Reg { reg } => {
debug_assert_eq!(*reg, regs::rcx());
debug_assert_eq!(reg, regs::rcx());
let (opcode, prefix) = match size {
OperandSize::Size8 => (0xD2, LegacyPrefixes::None),
OperandSize::Size16 => (0xD3, LegacyPrefixes::_66),
@@ -870,7 +879,7 @@ pub(crate) fn emit(
// When the shift amount is 1, there's an even shorter encoding, but we don't
// bother with that nicety here.
emit_std_enc_enc(sink, prefix, opcode, 1, subopcode, enc_dst, rex_flags);
sink.put1(*num_bits);
sink.put1(num_bits);
}
}
}
@@ -963,13 +972,13 @@ pub(crate) fn emit(
prefix = LegacyPrefixes::_66;
}
// A redundant REX prefix can change the meaning of this instruction.
let mut rex = RexFlags::from((*size, *reg_g));
let mut rex = RexFlags::from((*size, reg_g.to_reg()));
match src_e {
match src_e.clone().to_reg_mem_imm() {
RegMemImm::Reg { reg: reg_e } => {
if *size == OperandSize::Size8 {
// Check whether the E register forces the use of a redundant REX.
rex.always_emit_if_8bit_needed(*reg_e);
rex.always_emit_if_8bit_needed(reg_e);
}
// Use the swapped operands encoding for CMP, to stay consistent with the output of
@@ -980,7 +989,7 @@ pub(crate) fn emit(
(OperandSize::Size8, false) => 0x84,
(_, false) => 0x85,
};
emit_std_reg_reg(sink, prefix, opcode, 1, *reg_e, *reg_g, rex);
emit_std_reg_reg(sink, prefix, opcode, 1, reg_e, reg_g.to_reg(), rex);
}
RegMemImm::Mem { addr } => {
@@ -992,13 +1001,23 @@ pub(crate) fn emit(
(OperandSize::Size8, false) => 0x84,
(_, false) => 0x85,
};
emit_std_reg_mem(sink, state, info, prefix, opcode, 1, *reg_g, addr, rex);
emit_std_reg_mem(
sink,
state,
info,
prefix,
opcode,
1,
reg_g.to_reg(),
addr,
rex,
);
}
RegMemImm::Imm { simm32 } => {
// FIXME JRS 2020Feb11: there are shorter encodings for
// cmp $imm, rax/eax/ax/al.
let use_imm8 = is_cmp && low8_will_sign_extend_to_32(*simm32);
let use_imm8 = is_cmp && low8_will_sign_extend_to_32(simm32);
// And also here we use the "normal" G-E ordering.
let opcode = if is_cmp {
@@ -1018,9 +1037,9 @@ pub(crate) fn emit(
};
let subopcode = if is_cmp { 7 } else { 0 };
let enc_g = int_reg_enc(*reg_g);
let enc_g = int_reg_enc(reg_g.to_reg());
emit_std_enc_enc(sink, prefix, opcode, 1, subopcode, enc_g, rex);
emit_simm(sink, if use_imm8 { 1 } else { size.to_bytes() }, *simm32);
emit_simm(sink, if use_imm8 { 1 } else { size.to_bytes() }, simm32);
}
}
}
@@ -1056,9 +1075,17 @@ pub(crate) fn emit(
_ => unreachable!("invalid size spec for cmove"),
};
let opcode = 0x0F40 + cc.get_enc() as u32;
match consequent {
match consequent.clone().to_reg_mem() {
RegMem::Reg { reg: reg_e } => {
emit_std_reg_reg(sink, prefix, opcode, 2, reg_g.to_reg(), *reg_e, rex_flags);
emit_std_reg_reg(
sink,
prefix,
opcode,
2,
reg_g.to_reg().to_reg(),
reg_e,
rex_flags,
);
}
RegMem::Mem { addr } => {
let addr = &addr.finalize(state, sink);
@@ -1069,7 +1096,7 @@ pub(crate) fn emit(
prefix,
opcode,
2,
reg_g.to_reg(),
reg_g.to_reg().to_reg(),
addr,
rex_flags,
);
@@ -1090,7 +1117,7 @@ pub(crate) fn emit(
} else {
SseOpcode::Movss
};
let inst = Inst::xmm_unary_rm_r(op, src.clone(), *dst);
let inst = Inst::xmm_unary_rm_r(op, src.clone().to_reg_mem(), dst.to_writable_reg());
inst.emit(sink, info, state);
sink.bind_label(next);
@@ -1101,9 +1128,9 @@ pub(crate) fn emit(
sink.add_trap(state.cur_srcloc(), TrapCode::StackOverflow);
}
match src {
match src.clone().to_reg_mem_imm() {
RegMemImm::Reg { reg } => {
let enc_reg = int_reg_enc(*reg);
let enc_reg = int_reg_enc(reg);
let rex = 0x40 | ((enc_reg >> 3) & 1);
if rex != 0x40 {
sink.put1(rex);
@@ -1127,12 +1154,12 @@ pub(crate) fn emit(
}
RegMemImm::Imm { simm32 } => {
if low8_will_sign_extend_to_64(*simm32) {
if low8_will_sign_extend_to_64(simm32) {
sink.put1(0x6A);
sink.put1(*simm32 as u8);
sink.put1(simm32 as u8);
} else {
sink.put1(0x68);
sink.put4(*simm32);
sink.put4(simm32);
}
}
}
@@ -1711,7 +1738,8 @@ pub(crate) fn emit(
_ => unreachable!(),
};
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(*lhs), rhs_dst.to_reg());
let inst =
Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(lhs.to_reg()), rhs_dst.to_reg().to_reg());
inst.emit(sink, info, state);
one_way_jmp(sink, CC::NZ, do_min_max);
@@ -1721,7 +1749,7 @@ pub(crate) fn emit(
// and negative zero. These instructions merge the sign bits in that
// case, and are no-ops otherwise.
let op = if *is_min { or_op } else { and_op };
let inst = Inst::xmm_rm_r(op, RegMem::reg(*lhs), *rhs_dst);
let inst = Inst::xmm_rm_r(op, RegMem::reg(lhs.to_reg()), rhs_dst.to_writable_reg());
inst.emit(sink, info, state);
let inst = Inst::jmp_known(done);
@@ -1731,13 +1759,17 @@ pub(crate) fn emit(
// read-only operand: perform an addition between the two operands, which has the
// desired NaN propagation effects.
sink.bind_label(propagate_nan);
let inst = Inst::xmm_rm_r(add_op, RegMem::reg(*lhs), *rhs_dst);
let inst = Inst::xmm_rm_r(add_op, RegMem::reg(lhs.to_reg()), rhs_dst.to_writable_reg());
inst.emit(sink, info, state);
one_way_jmp(sink, CC::P, done);
sink.bind_label(do_min_max);
let inst = Inst::xmm_rm_r(min_max_op, RegMem::reg(*lhs), *rhs_dst);
let inst = Inst::xmm_rm_r(
min_max_op,
RegMem::reg(lhs.to_reg()),
rhs_dst.to_writable_reg(),
);
inst.emit(sink, info, state);
sink.bind_label(done);
@@ -1890,17 +1922,9 @@ pub(crate) fn emit(
_ => panic!("unexpected opcode {:?}", op),
};
let rex = RexFlags::from(*src_size);
match src_e {
match src_e.clone().to_reg_mem() {
RegMem::Reg { reg: reg_e } => {
emit_std_reg_reg(
sink,
prefix,
opcode,
2,
reg_g.to_reg().to_reg(),
*reg_e,
rex,
);
emit_std_reg_reg(sink, prefix, opcode, 2, reg_g.to_reg().to_reg(), reg_e, rex);
}
RegMem::Mem { addr } => {
let addr = &addr.finalize(state, sink);
@@ -1928,13 +1952,23 @@ pub(crate) fn emit(
_ => unimplemented!("Emit xmm cmp rm r"),
};
match src {
match src.clone().to_reg_mem() {
RegMem::Reg { reg } => {
emit_std_reg_reg(sink, prefix, opcode, len, *dst, *reg, rex);
emit_std_reg_reg(sink, prefix, opcode, len, dst.to_reg(), reg, rex);
}
RegMem::Mem { addr } => {
let addr = &addr.finalize(state, sink);
emit_std_reg_mem(sink, state, info, prefix, opcode, len, *dst, addr, rex);
emit_std_reg_mem(
sink,
state,
info,
prefix,
opcode,
len,
dst.to_reg(),
addr,
rex,
);
}
}
}

View File

@@ -133,9 +133,9 @@ impl Inst {
Self::AluRmiR {
size,
op,
src1: dst.to_reg(),
src2: src,
dst,
src1: Gpr::new(dst.to_reg()).unwrap(),
src2: GprMemImm::new(src).unwrap(),
dst: WritableGpr::from_writable_reg(dst).unwrap(),
}
}
@@ -174,10 +174,10 @@ impl Inst {
Inst::Div {
size,
signed,
divisor,
dividend: regs::rax(),
dst_quotient: Writable::from_reg(regs::rax()),
dst_remainder: Writable::from_reg(regs::rdx()),
divisor: GprMem::new(divisor).unwrap(),
dividend: Gpr::new(regs::rax()).unwrap(),
dst_quotient: WritableGpr::from_reg(Gpr::new(regs::rax()).unwrap()),
dst_remainder: Writable::from_reg(Gpr::new(regs::rdx()).unwrap()),
}
}
@@ -191,10 +191,10 @@ impl Inst {
Inst::MulHi {
size,
signed,
src1: regs::rax(),
src2: rhs,
dst_lo: Writable::from_reg(regs::rax()),
dst_hi: Writable::from_reg(regs::rdx()),
src1: Gpr::new(regs::rax()).unwrap(),
src2: GprMem::new(rhs).unwrap(),
dst_lo: WritableGpr::from_reg(Gpr::new(regs::rax()).unwrap()),
dst_hi: WritableGpr::from_reg(Gpr::new(regs::rdx()).unwrap()),
}
}
@@ -211,19 +211,19 @@ impl Inst {
Inst::CheckedDivOrRemSeq {
kind,
size,
divisor,
dividend: regs::rax(),
dst_quotient: Writable::from_reg(regs::rax()),
dst_remainder: Writable::from_reg(regs::rdx()),
tmp,
divisor: WritableGpr::from_writable_reg(divisor).unwrap(),
dividend: Gpr::new(regs::rax()).unwrap(),
dst_quotient: Writable::from_reg(Gpr::new(regs::rax()).unwrap()),
dst_remainder: Writable::from_reg(Gpr::new(regs::rdx()).unwrap()),
tmp: tmp.map(|tmp| WritableGpr::from_writable_reg(tmp).unwrap()),
}
}
pub(crate) fn sign_extend_data(size: OperandSize) -> Inst {
Inst::SignExtendData {
size,
src: regs::rax(),
dst: Writable::from_reg(regs::rdx()),
src: Gpr::new(regs::rax()).unwrap(),
dst: Writable::from_reg(Gpr::new(regs::rdx()).unwrap()),
}
}
@@ -239,7 +239,7 @@ impl Inst {
Inst::Imm {
dst_size,
simm64,
dst,
dst: WritableGpr::from_writable_reg(dst).unwrap(),
}
}
@@ -247,6 +247,8 @@ impl Inst {
debug_assert!(size.is_one_of(&[OperandSize::Size32, OperandSize::Size64]));
debug_assert!(src.get_class() == RegClass::I64);
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
let src = Gpr::new(src).unwrap();
let dst = WritableGpr::from_writable_reg(dst).unwrap();
Inst::MovRR { size, src, dst }
}
@@ -360,7 +362,7 @@ impl Inst {
debug_assert!(dst.to_reg().get_class() == RegClass::V128);
Inst::GprToXmm {
op,
src,
src: GprMem::new(src).unwrap(),
dst: WritableXmm::from_writable_reg(dst).unwrap(),
src_size,
}
@@ -369,6 +371,8 @@ impl Inst {
pub(crate) fn xmm_cmp_rm_r(op: SseOpcode, src: RegMem, dst: Reg) -> Inst {
src.assert_regclass_is(RegClass::V128);
debug_assert!(dst.get_class() == RegClass::V128);
let src = XmmMem::new(src).unwrap();
let dst = Xmm::new(dst).unwrap();
Inst::XmmCmpRmR { op, src, dst }
}
@@ -457,8 +461,8 @@ impl Inst {
Inst::XmmMinMaxSeq {
size,
is_min,
lhs,
rhs_dst,
lhs: Xmm::new(lhs).unwrap(),
rhs_dst: WritableXmm::from_writable_reg(rhs_dst).unwrap(),
}
}
@@ -483,6 +487,8 @@ impl Inst {
pub(crate) fn movzx_rm_r(ext_mode: ExtMode, src: RegMem, dst: Writable<Reg>) -> Inst {
src.assert_regclass_is(RegClass::I64);
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
let src = GprMem::new(src).unwrap();
let dst = WritableGpr::from_writable_reg(dst).unwrap();
Inst::MovzxRmR { ext_mode, src, dst }
}
@@ -500,6 +506,8 @@ impl Inst {
pub(crate) fn movsx_rm_r(ext_mode: ExtMode, src: RegMem, dst: Writable<Reg>) -> Inst {
src.assert_regclass_is(RegClass::I64);
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
let src = GprMem::new(src).unwrap();
let dst = WritableGpr::from_writable_reg(dst).unwrap();
Inst::MovsxRmR { ext_mode, src, dst }
}
@@ -507,7 +515,7 @@ impl Inst {
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
Inst::Mov64MR {
src: src.into(),
dst,
dst: WritableGpr::from_writable_reg(dst).unwrap(),
}
}
@@ -524,7 +532,7 @@ impl Inst {
debug_assert!(src.get_class() == RegClass::I64);
Inst::MovRM {
size,
src,
src: Gpr::new(src).unwrap(),
dst: dst.into(),
}
}
@@ -552,12 +560,13 @@ impl Inst {
Inst::ShiftR {
size,
kind,
src: dst.to_reg(),
num_bits: match num_bits {
src: Gpr::new(dst.to_reg()).unwrap(),
num_bits: Imm8Gpr::new(match num_bits {
Some(imm) => Imm8Reg::Imm8 { imm },
None => Imm8Reg::Reg { reg: regs::rcx() },
},
dst,
})
.unwrap(),
dst: WritableGpr::from_writable_reg(dst).unwrap(),
}
}
@@ -568,8 +577,8 @@ impl Inst {
debug_assert_eq!(dst.get_class(), RegClass::I64);
Inst::CmpRmiR {
size,
src,
dst,
src: GprMemImm::new(src).unwrap(),
dst: Gpr::new(dst).unwrap(),
opcode: CmpOpcode::Cmp,
}
}
@@ -580,8 +589,8 @@ impl Inst {
debug_assert_eq!(dst.get_class(), RegClass::I64);
Inst::CmpRmiR {
size,
src,
dst,
src: GprMemImm::new(src).unwrap(),
dst: Gpr::new(dst).unwrap(),
opcode: CmpOpcode::Test,
}
}
@@ -594,6 +603,7 @@ impl Inst {
pub(crate) fn setcc(cc: CC, dst: Writable<Reg>) -> Inst {
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
let dst = WritableGpr::from_writable_reg(dst).unwrap();
Inst::Setcc { cc, dst }
}
@@ -607,9 +617,9 @@ impl Inst {
Inst::Cmove {
size,
cc,
consequent: src,
alternative: dst.to_reg(),
dst,
consequent: GprMem::new(src).unwrap(),
alternative: Gpr::new(dst.to_reg()).unwrap(),
dst: WritableGpr::from_writable_reg(dst).unwrap(),
}
}
@@ -617,16 +627,20 @@ impl Inst {
debug_assert!(size.is_one_of(&[OperandSize::Size32, OperandSize::Size64]));
src.assert_regclass_is(RegClass::V128);
debug_assert!(dst.to_reg().get_class() == RegClass::V128);
let src = XmmMem::new(src).unwrap();
let dst = WritableXmm::from_writable_reg(dst).unwrap();
Inst::XmmCmove { size, cc, src, dst }
}
pub(crate) fn push64(src: RegMemImm) -> Inst {
src.assert_regclass_is(RegClass::I64);
let src = GprMemImm::new(src).unwrap();
Inst::Push64 { src }
}
pub(crate) fn pop64(dst: Writable<Reg>) -> Inst {
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
let dst = WritableGpr::from_writable_reg(dst).unwrap();
Inst::Pop64 { dst }
}
@@ -780,7 +794,7 @@ impl Inst {
fn produces_const(&self) -> bool {
match self {
Self::AluRmiR { op, src2, dst, .. } => {
src2.to_reg() == Some(dst.to_reg())
src2.clone().to_reg_mem_imm().to_reg() == Some(dst.to_reg().to_reg())
&& (*op == AluRmiROpcode::Xor || *op == AluRmiROpcode::Sub)
}
@@ -838,7 +852,11 @@ impl Inst {
Inst::AluRmiR { src1, dst, .. } => {
if *src1 != dst.to_reg() {
debug_assert!(src1.is_virtual());
insts.push(Self::gen_move(*dst, *src1, types::I64));
insts.push(Self::gen_move(
dst.to_writable_reg(),
src1.to_reg(),
types::I64,
));
*src1 = dst.to_reg();
}
insts.push(self);
@@ -883,7 +901,11 @@ impl Inst {
} => {
if *alternative != dst.to_reg() {
debug_assert!(alternative.is_virtual());
insts.push(Self::mov_r_r(*size, *alternative, *dst));
insts.push(Self::mov_r_r(
*size,
alternative.to_reg(),
dst.to_writable_reg(),
));
*alternative = dst.to_reg();
}
insts.push(self);
@@ -916,22 +938,30 @@ impl Inst {
debug_assert!(dividend.is_virtual());
insts.push(Self::gen_move(
Writable::from_reg(regs::rax()),
*dividend,
dividend.to_reg(),
types::I64,
));
*dividend = regs::rax();
*dividend = Gpr::new(regs::rax()).unwrap();
}
let mut quotient_mov = None;
if dst_quotient.to_reg() != regs::rax() {
debug_assert!(dst_quotient.to_reg().is_virtual());
quotient_mov = Some(Self::gen_move(*dst_quotient, regs::rax(), types::I64));
*dst_quotient = Writable::from_reg(regs::rax());
quotient_mov = Some(Self::gen_move(
dst_quotient.to_writable_reg(),
regs::rax(),
types::I64,
));
*dst_quotient = Writable::from_reg(Gpr::new(regs::rax()).unwrap());
}
let mut remainder_mov = None;
if dst_remainder.to_reg() != regs::rdx() {
debug_assert!(dst_remainder.to_reg().is_virtual());
remainder_mov = Some(Self::gen_move(*dst_remainder, regs::rdx(), types::I64));
*dst_remainder = Writable::from_reg(regs::rdx());
remainder_mov = Some(Self::gen_move(
dst_remainder.to_writable_reg(),
regs::rdx(),
types::I64,
));
*dst_remainder = Writable::from_reg(Gpr::new(regs::rdx()).unwrap());
}
insts.push(self);
insts.extend(quotient_mov);
@@ -947,22 +977,30 @@ impl Inst {
debug_assert!(src1.is_virtual());
insts.push(Self::gen_move(
Writable::from_reg(regs::rax()),
*src1,
src1.to_reg(),
types::I64,
));
*src1 = regs::rax();
*src1 = Gpr::new(regs::rax()).unwrap();
}
let mut dst_lo_mov = None;
if dst_lo.to_reg() != regs::rax() {
debug_assert!(dst_lo.to_reg().is_virtual());
dst_lo_mov = Some(Self::gen_move(*dst_lo, regs::rax(), types::I64));
*dst_lo = Writable::from_reg(regs::rax());
dst_lo_mov = Some(Self::gen_move(
dst_lo.to_writable_reg(),
regs::rax(),
types::I64,
));
*dst_lo = Writable::from_reg(Gpr::new(regs::rax()).unwrap());
}
let mut dst_hi_mov = None;
if dst_hi.to_reg() != regs::rdx() {
debug_assert!(dst_hi.to_reg().is_virtual());
dst_hi_mov = Some(Self::gen_move(*dst_hi, regs::rdx(), types::I64));
*dst_hi = Writable::from_reg(regs::rdx());
dst_hi_mov = Some(Self::gen_move(
dst_hi.to_writable_reg(),
regs::rdx(),
types::I64,
));
*dst_hi = Writable::from_reg(Gpr::new(regs::rdx()).unwrap());
}
insts.push(self);
insts.extend(dst_lo_mov);
@@ -973,16 +1011,20 @@ impl Inst {
debug_assert!(src.is_virtual());
insts.push(Self::gen_move(
Writable::from_reg(regs::rax()),
*src,
src.to_reg(),
types::I64,
));
*src = regs::rax();
*src = Gpr::new(regs::rax()).unwrap();
}
let mut dst_mov = None;
if dst.to_reg() != regs::rax() {
debug_assert!(dst.to_reg().is_virtual());
dst_mov = Some(Self::gen_move(*dst, dst.to_reg(), types::I64));
*dst = Writable::from_reg(regs::rax());
dst_mov = Some(Self::gen_move(
dst.to_writable_reg(),
dst.to_reg().to_reg(),
types::I64,
));
*dst = Writable::from_reg(Gpr::new(regs::rax()).unwrap());
}
insts.push(self);
insts.extend(dst_mov);
@@ -992,18 +1034,22 @@ impl Inst {
} => {
if *src != dst.to_reg() {
debug_assert!(src.is_virtual());
insts.push(Self::gen_move(*dst, *src, types::I64));
insts.push(Self::gen_move(
dst.to_writable_reg(),
src.to_reg(),
types::I64,
));
*src = dst.to_reg();
}
if let Imm8Reg::Reg { reg } = num_bits {
if *reg != regs::rcx() {
if let Imm8Reg::Reg { reg } = num_bits.clone().to_imm8_reg() {
if reg != regs::rcx() {
debug_assert!(reg.is_virtual());
insts.push(Self::gen_move(
Writable::from_reg(regs::rcx()),
*reg,
reg,
types::I64,
));
*reg = regs::rcx();
*num_bits = Imm8Gpr::new(Imm8Reg::Reg { reg: regs::rcx() }).unwrap();
}
}
insts.push(self);
@@ -1146,7 +1192,7 @@ impl PrettyPrint for Inst {
"{} {}, {}",
ljustify2(op.to_string(), suffix_lqb(*size, op.is_8bit())),
src2.show_rru_sized(mb_rru, size_lqb(*size, op.is_8bit())),
show_ireg_sized(dst.to_reg(), mb_rru, size_lqb(*size, op.is_8bit())),
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, size_lqb(*size, op.is_8bit())),
),
Inst::UnaryRmR { src, dst, op, size } => format!(
@@ -1208,7 +1254,7 @@ impl PrettyPrint for Inst {
DivOrRemKind::SignedRem => "srem",
DivOrRemKind::UnsignedRem => "urem",
},
show_ireg_sized(divisor.to_reg(), mb_rru, size.to_bytes()),
show_ireg_sized(divisor.to_reg().to_reg(), mb_rru, size.to_bytes()),
),
Inst::SignExtendData { size, .. } => match size {
@@ -1276,8 +1322,8 @@ impl PrettyPrint for Inst {
},
format!("f{}", size.to_bits())
),
show_ireg_sized(*lhs, mb_rru, 8),
show_ireg_sized(rhs_dst.to_reg(), mb_rru, 8),
show_ireg_sized(lhs.to_reg(), mb_rru, 8),
show_ireg_sized(rhs_dst.to_reg().to_reg(), mb_rru, 8),
),
Inst::XmmRmRImm {
@@ -1342,7 +1388,7 @@ impl PrettyPrint for Inst {
"{} {}, {}",
ljustify(op.to_string()),
src.show_rru_sized(mb_rru, 8),
show_ireg_sized(*dst, mb_rru, 8),
show_ireg_sized(dst.to_reg(), mb_rru, 8),
),
Inst::CvtUint64ToFloatSeq {
@@ -1405,14 +1451,14 @@ impl PrettyPrint for Inst {
"{} ${}, {}",
ljustify("movabsq".to_string()),
*simm64 as i64,
show_ireg_sized(dst.to_reg(), mb_rru, 8)
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, 8)
)
} else {
format!(
"{} ${}, {}",
ljustify("movl".to_string()),
(*simm64 as u32) as i32,
show_ireg_sized(dst.to_reg(), mb_rru, 4)
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, 4)
)
}
}
@@ -1420,8 +1466,8 @@ impl PrettyPrint for Inst {
Inst::MovRR { size, src, dst } => format!(
"{} {}, {}",
ljustify2("mov".to_string(), suffix_lq(*size)),
show_ireg_sized(*src, mb_rru, size.to_bytes()),
show_ireg_sized(dst.to_reg(), mb_rru, size.to_bytes())
show_ireg_sized(src.to_reg(), mb_rru, size.to_bytes()),
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, size.to_bytes())
),
Inst::MovzxRmR {
@@ -1432,14 +1478,14 @@ impl PrettyPrint for Inst {
"{} {}, {}",
ljustify("movl".to_string()),
src.show_rru_sized(mb_rru, ext_mode.src_size()),
show_ireg_sized(dst.to_reg(), mb_rru, 4)
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, 4)
)
} else {
format!(
"{} {}, {}",
ljustify2("movz".to_string(), ext_mode.to_string()),
src.show_rru_sized(mb_rru, ext_mode.src_size()),
show_ireg_sized(dst.to_reg(), mb_rru, ext_mode.dst_size())
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, ext_mode.dst_size())
)
}
}
@@ -1464,13 +1510,13 @@ impl PrettyPrint for Inst {
"{} {}, {}",
ljustify2("movs".to_string(), ext_mode.to_string()),
src.show_rru_sized(mb_rru, ext_mode.src_size()),
show_ireg_sized(dst.to_reg(), mb_rru, ext_mode.dst_size())
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, ext_mode.dst_size())
),
Inst::MovRM { size, src, dst, .. } => format!(
"{} {}, {}",
ljustify2("mov".to_string(), suffix_bwlq(*size)),
show_ireg_sized(*src, mb_rru, size.to_bytes()),
show_ireg_sized(src.to_reg(), mb_rru, size.to_bytes()),
dst.show_rru(mb_rru)
),
@@ -1480,19 +1526,19 @@ impl PrettyPrint for Inst {
num_bits,
dst,
..
} => match num_bits {
} => match num_bits.clone().to_imm8_reg() {
Imm8Reg::Reg { reg } => format!(
"{} {}, {}",
ljustify2(kind.to_string(), suffix_bwlq(*size)),
show_ireg_sized(*reg, mb_rru, 1),
show_ireg_sized(dst.to_reg(), mb_rru, size.to_bytes())
show_ireg_sized(reg, mb_rru, 1),
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, size.to_bytes())
),
Imm8Reg::Imm8 { imm: num_bits } => format!(
"{} ${}, {}",
ljustify2(kind.to_string(), suffix_bwlq(*size)),
num_bits,
show_ireg_sized(dst.to_reg(), mb_rru, size.to_bytes())
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, size.to_bytes())
),
},
@@ -1519,14 +1565,14 @@ impl PrettyPrint for Inst {
"{} {}, {}",
ljustify2(op.to_string(), suffix_bwlq(*size)),
src.show_rru_sized(mb_rru, size.to_bytes()),
show_ireg_sized(*dst, mb_rru, size.to_bytes())
show_ireg_sized(dst.to_reg(), mb_rru, size.to_bytes())
)
}
Inst::Setcc { cc, dst } => format!(
"{} {}",
ljustify2("set".to_string(), cc.to_string()),
show_ireg_sized(dst.to_reg(), mb_rru, 1)
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, 1)
),
Inst::Cmove {
@@ -1539,7 +1585,7 @@ impl PrettyPrint for Inst {
"{} {}, {}",
ljustify(format!("cmov{}{}", cc.to_string(), suffix_bwlq(*size))),
src.show_rru_sized(mb_rru, size.to_bytes()),
show_ireg_sized(dst.to_reg(), mb_rru, size.to_bytes())
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, size.to_bytes())
),
Inst::XmmCmove { size, cc, src, dst } => {
@@ -1552,7 +1598,7 @@ impl PrettyPrint for Inst {
"ss"
},
src.show_rru_sized(mb_rru, size.to_bytes()),
show_ireg_sized(dst.to_reg(), mb_rru, size.to_bytes())
show_ireg_sized(dst.to_reg().to_reg(), mb_rru, size.to_bytes())
)
}
@@ -1693,10 +1739,10 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
debug_assert_eq!(*src1, dst.to_reg());
if inst.produces_const() {
// No need to account for src2, since src2 == dst.
collector.add_def(*dst);
collector.add_def(dst.to_writable_reg());
} else {
src2.get_regs_as_uses(collector);
collector.add_mod(*dst);
collector.add_mod(dst.to_writable_reg());
}
}
Inst::Not { src, dst, .. } => {
@@ -1760,9 +1806,9 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
// the rdx register *before* the instruction, which is not too bad.
collector.add_mod(Writable::from_reg(regs::rax()));
collector.add_mod(Writable::from_reg(regs::rdx()));
collector.add_mod(*divisor);
collector.add_mod(divisor.to_writable_reg());
if let Some(tmp) = tmp {
collector.add_def(*tmp);
collector.add_def(tmp.to_writable_reg());
}
}
Inst::SignExtendData { size, src, dst } => {
@@ -1852,8 +1898,8 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
Inst::XmmUninitializedValue { dst } => collector.add_def(dst.to_writable_reg()),
Inst::XmmLoadConst { dst, .. } => collector.add_def(*dst),
Inst::XmmMinMaxSeq { lhs, rhs_dst, .. } => {
collector.add_use(*lhs);
collector.add_mod(*rhs_dst);
collector.add_use(lhs.to_reg());
collector.add_mod(rhs_dst.to_writable_reg());
}
Inst::XmmRmiReg {
src1, src2, dst, ..
@@ -1868,14 +1914,14 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
}
Inst::XmmCmpRmR { src, dst, .. } => {
src.get_regs_as_uses(collector);
collector.add_use(*dst);
collector.add_use(dst.to_reg());
}
Inst::Imm { dst, .. } => {
collector.add_def(*dst);
collector.add_def(dst.to_writable_reg());
}
Inst::MovRR { src, dst, .. } => {
collector.add_use(*src);
collector.add_def(*dst);
collector.add_use(src.to_reg());
collector.add_def(dst.to_writable_reg());
}
Inst::XmmToGpr { src, dst, .. } => {
collector.add_use(src.to_reg());
@@ -1918,11 +1964,11 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
}
Inst::MovzxRmR { src, dst, .. } => {
src.get_regs_as_uses(collector);
collector.add_def(*dst);
collector.add_def(dst.to_writable_reg());
}
Inst::Mov64MR { src, dst, .. } => {
src.get_regs_as_uses(collector);
collector.add_def(*dst)
collector.add_def(dst.to_writable_reg())
}
Inst::LoadEffectiveAddress { addr: src, dst } => {
src.get_regs_as_uses(collector);
@@ -1930,41 +1976,44 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
}
Inst::MovsxRmR { src, dst, .. } => {
src.get_regs_as_uses(collector);
collector.add_def(*dst);
collector.add_def(dst.to_writable_reg());
}
Inst::MovRM { src, dst, .. } => {
collector.add_use(*src);
collector.add_use(src.to_reg());
dst.get_regs_as_uses(collector);
}
Inst::ShiftR { num_bits, dst, .. } => {
if let Imm8Reg::Reg { reg } = num_bits {
debug_assert_eq!(*reg, regs::rcx());
if let Imm8Reg::Reg { reg } = num_bits.clone().to_imm8_reg() {
debug_assert_eq!(reg, regs::rcx());
collector.add_use(regs::rcx());
}
collector.add_mod(*dst);
collector.add_mod(dst.to_writable_reg());
}
Inst::CmpRmiR { src, dst, .. } => {
src.get_regs_as_uses(collector);
collector.add_use(*dst); // yes, really `add_use`
collector.add_use(dst.to_reg()); // yes, really `add_use`
}
Inst::Setcc { dst, .. } => {
collector.add_def(*dst);
collector.add_def(dst.to_writable_reg());
}
Inst::Cmove {
consequent: src,
dst,
..
}
| Inst::XmmCmove { src, dst, .. } => {
} => {
src.get_regs_as_uses(collector);
collector.add_mod(*dst);
collector.add_mod(dst.to_writable_reg());
}
Inst::XmmCmove { src, dst, .. } => {
src.get_regs_as_uses(collector);
collector.add_mod(dst.to_writable_reg());
}
Inst::Push64 { src } => {
src.get_regs_as_uses(collector);
collector.add_mod(Writable::from_reg(regs::rsp()));
}
Inst::Pop64 { dst } => {
collector.add_def(*dst);
collector.add_def(dst.to_writable_reg());
}
Inst::CallKnown {
@@ -2149,11 +2198,11 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
debug_assert_eq!(*src1, dst.to_reg());
if produces_const {
src2.map_as_def(mapper);
mapper.map_def(dst);
dst.map_def(mapper);
*src1 = dst.to_reg();
} else {
src2.map_uses(mapper);
mapper.map_mod(dst);
dst.map_mod(mapper);
*src1 = dst.to_reg();
}
}
@@ -2165,9 +2214,9 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
Inst::Div { divisor, .. } => divisor.map_uses(mapper),
Inst::MulHi { src2, .. } => src2.map_uses(mapper),
Inst::CheckedDivOrRemSeq { divisor, tmp, .. } => {
mapper.map_mod(divisor);
divisor.map_mod(mapper);
if let Some(tmp) = tmp {
mapper.map_def(tmp)
tmp.map_def(mapper)
}
}
Inst::SignExtendData { .. } => {}
@@ -2275,8 +2324,8 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
ref mut rhs_dst,
..
} => {
mapper.map_use(lhs);
mapper.map_mod(rhs_dst);
lhs.map_use(mapper);
rhs_dst.map_mod(mapper);
}
Inst::XmmMovRM {
ref mut src,
@@ -2292,16 +2341,16 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
..
} => {
src.map_uses(mapper);
mapper.map_use(dst);
dst.map_use(mapper);
}
Inst::Imm { ref mut dst, .. } => mapper.map_def(dst),
Inst::Imm { ref mut dst, .. } => dst.map_def(mapper),
Inst::MovRR {
ref mut src,
ref mut dst,
..
} => {
mapper.map_use(src);
mapper.map_def(dst);
src.map_use(mapper);
dst.map_def(mapper);
}
Inst::XmmToGpr {
ref mut src,
@@ -2356,11 +2405,11 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
..
} => {
src.map_uses(mapper);
mapper.map_def(dst);
dst.map_def(mapper);
}
Inst::Mov64MR { src, dst, .. } => {
src.map_uses(mapper);
mapper.map_def(dst);
dst.map_def(mapper);
}
Inst::LoadEffectiveAddress { addr: src, dst } => {
src.map_uses(mapper);
@@ -2372,14 +2421,14 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
..
} => {
src.map_uses(mapper);
mapper.map_def(dst);
dst.map_def(mapper);
}
Inst::MovRM {
ref mut src,
ref mut dst,
..
} => {
mapper.map_use(src);
src.map_use(mapper);
dst.map_uses(mapper);
}
Inst::ShiftR {
@@ -2388,7 +2437,7 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
..
} => {
debug_assert_eq!(*src, dst.to_reg());
mapper.map_mod(dst);
dst.map_mod(mapper);
*src = dst.to_reg();
}
Inst::CmpRmiR {
@@ -2397,9 +2446,9 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
..
} => {
src.map_uses(mapper);
mapper.map_use(dst);
dst.map_use(mapper);
}
Inst::Setcc { ref mut dst, .. } => mapper.map_def(dst),
Inst::Setcc { ref mut dst, .. } => dst.map_def(mapper),
Inst::Cmove {
consequent: ref mut src,
ref mut dst,
@@ -2407,7 +2456,7 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
..
} => {
src.map_uses(mapper);
mapper.map_mod(dst);
dst.map_mod(mapper);
*alternative = dst.to_reg();
}
Inst::XmmCmove {
@@ -2416,11 +2465,11 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
..
} => {
src.map_uses(mapper);
mapper.map_mod(dst);
dst.map_mod(mapper);
}
Inst::Push64 { ref mut src } => src.map_uses(mapper),
Inst::Pop64 { ref mut dst } => {
mapper.map_def(dst);
dst.map_def(mapper);
}
Inst::CallKnown {
@@ -2520,7 +2569,7 @@ impl MachInst for Inst {
// conceivably use `movl %reg, %reg` to zero out the top 32 bits of
// %reg.
Self::MovRR { size, src, dst, .. } if *size == OperandSize::Size64 => {
Some((*dst, *src))
Some((dst.to_writable_reg(), src.to_reg()))
}
// Note as well that MOVS[S|D] when used in the `XmmUnaryRmR` context are pure moves of
// scalar floating-point values (and annotate `dst` as `def`s to the register allocator)
@@ -2578,12 +2627,15 @@ impl MachInst for Inst {
size: OperandSize::Size8,
src,
dst: SyntheticAmode::NominalSPOffset { simm32 },
} => Some(MachInstStackOpInfo::StoreNomSPOff(*src, *simm32 as i64)),
} => Some(MachInstStackOpInfo::StoreNomSPOff(
src.to_reg(),
*simm32 as i64,
)),
Self::Mov64MR {
src: SyntheticAmode::NominalSPOffset { simm32 },
dst,
} => Some(MachInstStackOpInfo::LoadNomSPOff(
dst.to_reg(),
dst.to_reg().to_reg(),
*simm32 as i64,
)),
_ => None,

View File

@@ -63,33 +63,33 @@
;; Add two registers.
(rule (lower (has_type (fits_in_64 ty)
(iadd x y)))
(value_reg (add ty
(put_in_reg x)
(RegMemImm.Reg (put_in_reg y)))))
(value_gpr (add ty
(put_in_gpr x)
(gpr_to_gpr_mem_imm (put_in_gpr y)))))
;; Add a register and an immediate.
(rule (lower (has_type (fits_in_64 ty)
(iadd x (simm32_from_value y))))
(value_reg (add ty (put_in_reg x) y)))
(value_gpr (add ty (put_in_gpr x) y)))
(rule (lower (has_type (fits_in_64 ty)
(iadd (simm32_from_value x) y)))
(value_reg (add ty (put_in_reg y) x)))
(value_gpr (add ty (put_in_gpr y) x)))
;; Add a register and memory.
(rule (lower (has_type (fits_in_64 ty)
(iadd x (sinkable_load y))))
(value_reg (add ty
(put_in_reg x)
(sink_load y))))
(value_gpr (add ty
(put_in_gpr x)
(sink_load_to_gpr_mem_imm y))))
(rule (lower (has_type (fits_in_64 ty)
(iadd (sinkable_load x) y)))
(value_reg (add ty
(put_in_reg y)
(sink_load x))))
(value_gpr (add ty
(put_in_gpr y)
(sink_load_to_gpr_mem_imm x))))
;; SSE.
@@ -117,15 +117,15 @@
(rule (lower (has_type $I128 (iadd x y)))
;; Get the high/low registers for `x`.
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Reg (value_regs_get x_regs 0))
(x_hi Reg (value_regs_get x_regs 1)))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1)))
;; Get the high/low registers for `y`.
(let ((y_regs ValueRegs (put_in_regs y))
(y_lo Reg (value_regs_get y_regs 0))
(y_hi Reg (value_regs_get y_regs 1)))
(y_lo Gpr (value_regs_get_gpr y_regs 0))
(y_hi Gpr (value_regs_get_gpr y_regs 1)))
;; Do an add followed by an add-with-carry.
(with_flags (add_with_flags $I64 x_lo (RegMemImm.Reg y_lo))
(adc $I64 x_hi (RegMemImm.Reg y_hi))))))
(with_flags (add_with_flags $I64 x_lo (gpr_to_gpr_mem_imm y_lo))
(adc $I64 x_hi (gpr_to_gpr_mem_imm y_hi))))))
;;;; Rules for `sadd_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -166,42 +166,42 @@
;; Add two registers.
(rule (lower (has_type (fits_in_64 ty)
(iadd_ifcout x y)))
(let ((unused_iflags Reg (writable_reg_to_reg (temp_writable_reg $I64))))
(value_regs (add ty
(put_in_reg x)
(RegMemImm.Reg (put_in_reg y)))
unused_iflags)))
(let ((unused_iflags Gpr (writable_gpr_to_gpr (temp_writable_gpr))))
(value_gprs (add ty
(put_in_gpr x)
(put_in_gpr_mem_imm y))
unused_iflags)))
;; Add a register and an immediate.
(rule (lower (has_type (fits_in_64 ty)
(iadd_ifcout x (simm32_from_value y))))
(let ((unused_iflags Reg (writable_reg_to_reg (temp_writable_reg $I64))))
(value_regs (add ty (put_in_reg x) y)
(let ((unused_iflags Gpr (writable_gpr_to_gpr (temp_writable_gpr))))
(value_gprs (add ty (put_in_gpr x) y)
unused_iflags)))
(rule (lower (has_type (fits_in_64 ty)
(iadd_ifcout (simm32_from_value x) y)))
(let ((unused_iflags Reg (writable_reg_to_reg (temp_writable_reg $I64))))
(value_regs (add ty (put_in_reg y) x)
(let ((unused_iflags Gpr (writable_gpr_to_gpr (temp_writable_gpr))))
(value_gprs (add ty (put_in_gpr y) x)
unused_iflags)))
;; Add a register and memory.
(rule (lower (has_type (fits_in_64 ty)
(iadd_ifcout x (sinkable_load y))))
(let ((unused_iflags Reg (writable_reg_to_reg (temp_writable_reg $I64))))
(value_regs (add ty
(put_in_reg x)
(sink_load y))
(let ((unused_iflags Gpr (writable_gpr_to_gpr (temp_writable_gpr))))
(value_gprs (add ty
(put_in_gpr x)
(sink_load_to_gpr_mem_imm y))
unused_iflags)))
(rule (lower (has_type (fits_in_64 ty)
(iadd_ifcout (sinkable_load x) y)))
(let ((unused_iflags Reg (writable_reg_to_reg (temp_writable_reg $I64))))
(value_regs (add ty
(put_in_reg y)
(sink_load x))
(let ((unused_iflags Gpr (writable_gpr_to_gpr (temp_writable_gpr))))
(value_gprs (add ty
(put_in_gpr y)
(sink_load_to_gpr_mem_imm x))
unused_iflags)))
;; (No `iadd_ifcout` for `i128`.)
@@ -212,30 +212,30 @@
;; When the immediate fits in a `RegMemImm.Imm`, use that.
(rule (lower (has_type (fits_in_64 ty) (iadd_imm y (simm32_from_imm64 x))))
(value_reg (add ty (put_in_reg y) x)))
(value_gpr (add ty (put_in_gpr y) x)))
;; Otherwise, put the immediate into a register.
(rule (lower (has_type (fits_in_64 ty) (iadd_imm y (u64_from_imm64 x))))
(value_reg (add ty (put_in_reg y) (RegMemImm.Reg (imm ty x)))))
(value_gpr (add ty (put_in_gpr y) (gpr_to_gpr_mem_imm (gpr_new (imm ty x))))))
;; `i128`
;; When the immediate fits in a `RegMemImm.Imm`, use that.
(rule (lower (has_type $I128 (iadd_imm y (simm32_from_imm64 x))))
(let ((y_regs ValueRegs (put_in_regs y))
(y_lo Reg (value_regs_get y_regs 0))
(y_hi Reg (value_regs_get y_regs 1)))
(y_lo Gpr (value_regs_get_gpr y_regs 0))
(y_hi Gpr (value_regs_get_gpr y_regs 1)))
(with_flags (add_with_flags $I64 y_lo x)
(adc $I64 y_hi (RegMemImm.Imm 0)))))
(adc $I64 y_hi (gpr_mem_imm_new (RegMemImm.Imm 0))))))
;; Otherwise, put the immediate into a register.
(rule (lower (has_type $I128 (iadd_imm y (u64_from_imm64 x))))
(let ((y_regs ValueRegs (put_in_regs y))
(y_lo Reg (value_regs_get y_regs 0))
(y_hi Reg (value_regs_get y_regs 1))
(x_lo Reg (imm $I64 x)))
(with_flags (add_with_flags $I64 y_lo (RegMemImm.Reg x_lo))
(adc $I64 y_hi (RegMemImm.Imm 0)))))
(y_lo Gpr (value_regs_get_gpr y_regs 0))
(y_hi Gpr (value_regs_get_gpr y_regs 1))
(x_lo Gpr (gpr_new (imm $I64 x))))
(with_flags (add_with_flags $I64 y_lo (gpr_to_gpr_mem_imm x_lo))
(adc $I64 y_hi (gpr_mem_imm_new (RegMemImm.Imm 0))))))
;;;; Rules for `isub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -244,21 +244,21 @@
;; Sub two registers.
(rule (lower (has_type (fits_in_64 ty)
(isub x y)))
(value_reg (sub ty
(put_in_reg x)
(RegMemImm.Reg (put_in_reg y)))))
(value_gpr (sub ty
(put_in_gpr x)
(put_in_gpr_mem_imm y))))
;; Sub a register and an immediate.
(rule (lower (has_type (fits_in_64 ty)
(isub x (simm32_from_value y))))
(value_reg (sub ty (put_in_reg x) y)))
(value_gpr (sub ty (put_in_gpr x) y)))
;; Sub a register and memory.
(rule (lower (has_type (fits_in_64 ty)
(isub x (sinkable_load y))))
(value_reg (sub ty
(put_in_reg x)
(sink_load y))))
(value_gpr (sub ty
(put_in_gpr x)
(sink_load_to_gpr_mem_imm y))))
;; SSE.
@@ -286,15 +286,15 @@
(rule (lower (has_type $I128 (isub x y)))
;; Get the high/low registers for `x`.
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Reg (value_regs_get x_regs 0))
(x_hi Reg (value_regs_get x_regs 1)))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1)))
;; Get the high/low registers for `y`.
(let ((y_regs ValueRegs (put_in_regs y))
(y_lo Reg (value_regs_get y_regs 0))
(y_hi Reg (value_regs_get y_regs 1)))
(y_lo Gpr (value_regs_get_gpr y_regs 0))
(y_hi Gpr (value_regs_get_gpr y_regs 1)))
;; Do a sub followed by an sub-with-borrow.
(with_flags (sub_with_flags $I64 x_lo (RegMemImm.Reg y_lo))
(sbb $I64 x_hi (RegMemImm.Reg y_hi))))))
(with_flags (sub_with_flags $I64 x_lo (gpr_to_gpr_mem_imm y_lo))
(sbb $I64 x_hi (gpr_to_gpr_mem_imm y_hi))))))
;;;; Rules for `ssub_sat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -326,36 +326,36 @@
;; And two registers.
(rule (lower (has_type (fits_in_64 ty) (band x y)))
(value_reg (x64_and ty
(put_in_reg x)
(RegMemImm.Reg (put_in_reg y)))))
(value_gpr (x64_and ty
(put_in_gpr x)
(put_in_gpr_mem_imm y))))
;; And with a memory operand.
(rule (lower (has_type (fits_in_64 ty)
(band x (sinkable_load y))))
(value_reg (x64_and ty
(put_in_reg x)
(sink_load y))))
(value_gpr (x64_and ty
(put_in_gpr x)
(sink_load_to_gpr_mem_imm y))))
(rule (lower (has_type (fits_in_64 ty)
(band (sinkable_load x) y)))
(value_reg (x64_and ty
(put_in_reg y)
(sink_load x))))
(value_gpr (x64_and ty
(put_in_gpr y)
(sink_load_to_gpr_mem_imm x))))
;; And with an immediate.
(rule (lower (has_type (fits_in_64 ty)
(band x (simm32_from_value y))))
(value_reg (x64_and ty
(put_in_reg x)
(value_gpr (x64_and ty
(put_in_gpr x)
y)))
(rule (lower (has_type (fits_in_64 ty)
(band (simm32_from_value x) y)))
(value_reg (x64_and ty
(put_in_reg y)
(value_gpr (x64_and ty
(put_in_gpr y)
x)))
;; SSE.
@@ -375,23 +375,23 @@
(rule (lower (has_type $I128 (band x y)))
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Reg (value_regs_get x_regs 0))
(x_hi Reg (value_regs_get x_regs 1))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1))
(y_regs ValueRegs (put_in_regs y))
(y_lo Reg (value_regs_get y_regs 0))
(y_hi Reg (value_regs_get y_regs 1)))
(value_regs (x64_and $I64 x_lo (RegMemImm.Reg y_lo))
(x64_and $I64 x_hi (RegMemImm.Reg y_hi)))))
(y_lo Gpr (value_regs_get_gpr y_regs 0))
(y_hi Gpr (value_regs_get_gpr y_regs 1)))
(value_gprs (x64_and $I64 x_lo (gpr_to_gpr_mem_imm y_lo))
(x64_and $I64 x_hi (gpr_to_gpr_mem_imm y_hi)))))
(rule (lower (has_type $B128 (band x y)))
;; Booleans are always `0` or `1`, so we only need to do the `and` on the
;; low half. The high half is always zero but, rather than generate a new
;; zero, we just reuse `x`'s high half which is already zero.
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Reg (value_regs_get x_regs 0))
(x_hi Reg (value_regs_get x_regs 1))
(y_lo Reg (lo_reg y)))
(value_regs (x64_and $I64 x_lo (RegMemImm.Reg y_lo))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1))
(y_lo Gpr (lo_gpr y)))
(value_gprs (x64_and $I64 x_lo (gpr_to_gpr_mem_imm y_lo))
x_hi)))
;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -400,36 +400,36 @@
;; Or two registers.
(rule (lower (has_type (fits_in_64 ty) (bor x y)))
(value_reg (or ty
(put_in_reg x)
(RegMemImm.Reg (put_in_reg y)))))
(value_gpr (or ty
(put_in_gpr x)
(put_in_gpr_mem_imm y))))
;; Or with a memory operand.
(rule (lower (has_type (fits_in_64 ty)
(bor x (sinkable_load y))))
(value_reg (or ty
(put_in_reg x)
(sink_load y))))
(value_gpr (or ty
(put_in_gpr x)
(sink_load_to_gpr_mem_imm y))))
(rule (lower (has_type (fits_in_64 ty)
(bor (sinkable_load x) y)))
(value_reg (or ty
(put_in_reg y)
(sink_load x))))
(value_gpr (or ty
(put_in_gpr y)
(sink_load_to_gpr_mem_imm x))))
;; Or with an immediate.
(rule (lower (has_type (fits_in_64 ty)
(bor x (simm32_from_value y))))
(value_reg (or ty
(put_in_reg x)
(value_gpr (or ty
(put_in_gpr x)
y)))
(rule (lower (has_type (fits_in_64 ty)
(bor (simm32_from_value x) y)))
(value_reg (or ty
(put_in_reg y)
(value_gpr (or ty
(put_in_gpr y)
x)))
;; SSE.
@@ -449,12 +449,12 @@
(decl or_i128 (ValueRegs ValueRegs) ValueRegs)
(rule (or_i128 x y)
(let ((x_lo Reg (value_regs_get x 0))
(x_hi Reg (value_regs_get x 1))
(y_lo Reg (value_regs_get y 0))
(y_hi Reg (value_regs_get y 1)))
(value_regs (or $I64 x_lo (RegMemImm.Reg y_lo))
(or $I64 x_hi (RegMemImm.Reg y_hi)))))
(let ((x_lo Gpr (value_regs_get_gpr x 0))
(x_hi Gpr (value_regs_get_gpr x 1))
(y_lo Gpr (value_regs_get_gpr y 0))
(y_hi Gpr (value_regs_get_gpr y 1)))
(value_gprs (or $I64 x_lo (gpr_to_gpr_mem_imm y_lo))
(or $I64 x_hi (gpr_to_gpr_mem_imm y_hi)))))
(rule (lower (has_type $I128 (bor x y)))
(or_i128 (put_in_regs x) (put_in_regs y)))
@@ -464,10 +464,10 @@
;; low half. The high half is always zero but, rather than generate a new
;; zero, we just reuse `x`'s high half which is already zero.
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Reg (value_regs_get x_regs 0))
(x_hi Reg (value_regs_get x_regs 1))
(y_lo Reg (lo_reg y)))
(value_regs (or $I64 x_lo (RegMemImm.Reg y_lo))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1))
(y_lo Gpr (lo_gpr y)))
(value_gprs (or $I64 x_lo (gpr_to_gpr_mem_imm y_lo))
x_hi)))
;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -476,36 +476,36 @@
;; Xor two registers.
(rule (lower (has_type (fits_in_64 ty) (bxor x y)))
(value_reg (xor ty
(put_in_reg x)
(RegMemImm.Reg (put_in_reg y)))))
(value_gpr (xor ty
(put_in_gpr x)
(put_in_gpr_mem_imm y))))
;; Xor with a memory operand.
(rule (lower (has_type (fits_in_64 ty)
(bxor x (sinkable_load y))))
(value_reg (xor ty
(put_in_reg x)
(sink_load y))))
(value_gpr (xor ty
(put_in_gpr x)
(sink_load_to_gpr_mem_imm y))))
(rule (lower (has_type (fits_in_64 ty)
(bxor (sinkable_load x) y)))
(value_reg (xor ty
(put_in_reg y)
(sink_load x))))
(value_gpr (xor ty
(put_in_gpr y)
(sink_load_to_gpr_mem_imm x))))
;; Xor with an immediate.
(rule (lower (has_type (fits_in_64 ty)
(bxor x (simm32_from_value y))))
(value_reg (xor ty
(put_in_reg x)
(value_gpr (xor ty
(put_in_gpr x)
y)))
(rule (lower (has_type (fits_in_64 ty)
(bxor (simm32_from_value x) y)))
(value_reg (xor ty
(put_in_reg y)
(value_gpr (xor ty
(put_in_gpr y)
x)))
;; SSE.
@@ -517,23 +517,23 @@
(rule (lower (has_type $I128 (bxor x y)))
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Reg (value_regs_get x_regs 0))
(x_hi Reg (value_regs_get x_regs 1))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1))
(y_regs ValueRegs (put_in_regs y))
(y_lo Reg (value_regs_get y_regs 0))
(y_hi Reg (value_regs_get y_regs 1)))
(value_regs (xor $I64 x_lo (RegMemImm.Reg y_lo))
(xor $I64 x_hi (RegMemImm.Reg y_hi)))))
(y_lo Gpr (value_regs_get_gpr y_regs 0))
(y_hi Gpr (value_regs_get_gpr y_regs 1)))
(value_gprs (xor $I64 x_lo (gpr_to_gpr_mem_imm y_lo))
(xor $I64 x_hi (gpr_to_gpr_mem_imm y_hi)))))
(rule (lower (has_type $B128 (bxor x y)))
;; Booleans are always `0` or `1`, so we only need to do the `xor` on the
;; low half. The high half is always zero but, rather than generate a new
;; zero, we just reuse `x`'s high half which is already zero.
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Reg (value_regs_get x_regs 0))
(x_hi Reg (value_regs_get x_regs 1))
(y_lo Reg (lo_reg y)))
(value_regs (xor $I64 x_lo (RegMemImm.Reg y_lo))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1))
(y_lo Gpr (lo_gpr y)))
(value_gprs (xor $I64 x_lo (gpr_to_gpr_mem_imm y_lo))
x_hi)))
;;;; Rules for `ishl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -541,38 +541,49 @@
;; `i64` and smaller.
(rule (lower (has_type (fits_in_64 ty) (ishl src amt)))
(value_reg (shl ty (put_in_reg src) (put_masked_in_imm8_reg amt ty))))
(value_gpr (shl ty (put_in_gpr src) (put_masked_in_imm8_gpr amt ty))))
;; `i128`.
(decl shl_i128 (ValueRegs Reg) ValueRegs)
(decl shl_i128 (ValueRegs Gpr) ValueRegs)
(rule (shl_i128 src amt)
;; Unpack the registers that make up the 128-bit value being shifted.
(let ((src_lo Reg (value_regs_get src 0))
(src_hi Reg (value_regs_get src 1))
(let ((src_lo Gpr (value_regs_get_gpr src 0))
(src_hi Gpr (value_regs_get_gpr src 1))
;; Do two 64-bit shifts.
(lo_shifted Reg (shl $I64 src_lo (Imm8Reg.Reg amt)))
(hi_shifted Reg (shl $I64 src_hi (Imm8Reg.Reg amt)))
(lo_shifted Gpr (shl $I64 src_lo (gpr_to_imm8_gpr amt)))
(hi_shifted Gpr (shl $I64 src_hi (gpr_to_imm8_gpr amt)))
;; `src_lo >> (64 - amt)` are the bits to carry over from the lo
;; into the hi.
(carry Reg (shr $I64 src_lo (Imm8Reg.Reg (sub $I64 (imm $I64 64) (RegMemImm.Reg amt)))))
(zero Reg (imm $I64 0))
(carry Gpr (shr $I64
src_lo
(gpr_to_imm8_gpr (sub $I64
(gpr_new (imm $I64 64))
(gpr_to_gpr_mem_imm amt)))))
(zero Gpr (gpr_new (imm $I64 0)))
;; Nullify the carry if we are shifting in by a multiple of 128.
(carry_ Reg (with_flags_1 (test (OperandSize.Size64) (RegMemImm.Imm 127) amt)
(cmove $I64 (CC.Z) (RegMem.Reg zero) carry)))
(carry_ Gpr (gpr_new (with_flags_1 (test (OperandSize.Size64)
(gpr_mem_imm_new (RegMemImm.Imm 127))
amt)
(cmove $I64
(CC.Z)
(gpr_to_gpr_mem zero)
carry))))
;; Add the carry into the high half.
(hi_shifted_ Reg (or $I64 carry_ (RegMemImm.Reg hi_shifted))))
(hi_shifted_ Gpr (or $I64 carry_ (gpr_to_gpr_mem_imm hi_shifted))))
;; Combine the two shifted halves. However, if we are shifting by >= 64
;; (modulo 128), then the low bits are zero and the high bits are our
;; low bits.
(with_flags_2 (test (OperandSize.Size64) (RegMemImm.Imm 64) amt)
(cmove $I64 (CC.Z) (RegMem.Reg lo_shifted) zero)
(cmove $I64 (CC.Z) (RegMem.Reg hi_shifted_) lo_shifted))))
(with_flags_2 (test (OperandSize.Size64)
(gpr_mem_imm_new (RegMemImm.Imm 64))
amt)
(cmove $I64 (CC.Z) (gpr_to_gpr_mem lo_shifted) zero)
(cmove $I64 (CC.Z) (gpr_to_gpr_mem hi_shifted_) lo_shifted))))
(rule (lower (has_type $I128 (ishl src amt)))
;; NB: Only the low bits of `amt` matter since we logically mask the shift
;; amount to the value's bit width.
(let ((amt_ Reg (lo_reg amt)))
(let ((amt_ Gpr (lo_gpr amt)))
(shl_i128 (put_in_regs src) amt_)))
;; SSE.
@@ -613,10 +624,12 @@
(rule (ishl_i8x16_mask (RegMemImm.Reg amt))
(let ((mask_table SyntheticAmode (ishl_i8x16_mask_table))
(base_mask_addr Gpr (lea mask_table))
(mask_offset Reg (shl $I64 amt (Imm8Reg.Imm8 4))))
(mask_offset Gpr (shl $I64
(gpr_new amt)
(imm8_to_imm8_gpr 4))))
(amode_to_synthetic_amode (amode_imm_reg_reg_shift 0
base_mask_addr
(gpr_new mask_offset)
mask_offset
0))))
(rule (ishl_i8x16_mask (RegMemImm.Mem amt))
(ishl_i8x16_mask (RegMemImm.Reg (x64_load $I64 amt (ExtKind.None)))))
@@ -640,38 +653,49 @@
;; `i64` and smaller.
(rule (lower (has_type (fits_in_64 ty) (ushr src amt)))
(let ((src_ Reg (extend_to_reg src ty (ExtendKind.Zero))))
(value_reg (shr ty src_ (put_masked_in_imm8_reg amt ty)))))
(let ((src_ Gpr (extend_to_gpr src ty (ExtendKind.Zero))))
(value_gpr (shr ty src_ (put_masked_in_imm8_gpr amt ty)))))
;; `i128`.
(decl shr_i128 (ValueRegs Reg) ValueRegs)
(decl shr_i128 (ValueRegs Gpr) ValueRegs)
(rule (shr_i128 src amt)
;; Unpack the lo/hi halves of `src`.
(let ((src_lo Reg (value_regs_get src 0))
(src_hi Reg (value_regs_get src 1))
(let ((src_lo Gpr (value_regs_get_gpr src 0))
(src_hi Gpr (value_regs_get_gpr src 1))
;; Do a shift on each half.
(lo_shifted Reg (shr $I64 src_lo (Imm8Reg.Reg amt)))
(hi_shifted Reg (shr $I64 src_hi (Imm8Reg.Reg amt)))
(lo_shifted Gpr (shr $I64 src_lo (gpr_to_imm8_gpr amt)))
(hi_shifted Gpr (shr $I64 src_hi (gpr_to_imm8_gpr amt)))
;; `src_hi << (64 - amt)` are the bits to carry over from the hi
;; into the lo.
(carry Reg (shl $I64 src_hi (Imm8Reg.Reg (sub $I64 (imm $I64 64) (RegMemImm.Reg amt)))))
(carry Gpr (shl $I64
src_hi
(gpr_to_imm8_gpr (sub $I64
(gpr_new (imm $I64 64))
(gpr_to_gpr_mem_imm amt)))))
;; Nullify the carry if we are shifting by a multiple of 128.
(carry_ Reg (with_flags_1 (test (OperandSize.Size64) (RegMemImm.Imm 127) amt)
(cmove $I64 (CC.Z) (RegMem.Reg (imm $I64 0)) carry)))
(carry_ Gpr (gpr_new (with_flags_1 (test (OperandSize.Size64)
(gpr_mem_imm_new (RegMemImm.Imm 127))
amt)
(cmove $I64
(CC.Z)
(gpr_to_gpr_mem (gpr_new (imm $I64 0)))
carry))))
;; Add the carry bits into the lo.
(lo_shifted_ Reg (or $I64 carry_ (RegMemImm.Reg lo_shifted))))
(lo_shifted_ Gpr (or $I64 carry_ (gpr_to_gpr_mem_imm lo_shifted))))
;; Combine the two shifted halves. However, if we are shifting by >= 64
;; (modulo 128), then the hi bits are zero and the lo bits are what
;; would otherwise be our hi bits.
(with_flags_2 (test (OperandSize.Size64) (RegMemImm.Imm 64) amt)
(cmove $I64 (CC.Z) (RegMem.Reg lo_shifted_) hi_shifted)
(cmove $I64 (CC.Z) (RegMem.Reg hi_shifted) (imm $I64 0)))))
(with_flags_2 (test (OperandSize.Size64)
(gpr_mem_imm_new (RegMemImm.Imm 64))
amt)
(cmove $I64 (CC.Z) (gpr_to_gpr_mem lo_shifted_) hi_shifted)
(cmove $I64 (CC.Z) (gpr_to_gpr_mem hi_shifted) (gpr_new (imm $I64 0))))))
(rule (lower (has_type $I128 (ushr src amt)))
;; NB: Only the low bits of `amt` matter since we logically mask the shift
;; amount to the value's bit width.
(let ((amt_ Reg (lo_reg amt)))
(let ((amt_ Gpr (lo_gpr amt)))
(shr_i128 (put_in_regs src) amt_)))
;; SSE.
@@ -712,10 +736,12 @@
(rule (ushr_i8x16_mask (RegMemImm.Reg amt))
(let ((mask_table SyntheticAmode (ushr_i8x16_mask_table))
(base_mask_addr Gpr (lea mask_table))
(mask_offset Reg (shl $I64 amt (Imm8Reg.Imm8 4))))
(mask_offset Gpr (shl $I64
(gpr_new amt)
(imm8_to_imm8_gpr 4))))
(amode_to_synthetic_amode (amode_imm_reg_reg_shift 0
base_mask_addr
(gpr_new mask_offset)
mask_offset
0))))
(rule (ushr_i8x16_mask (RegMemImm.Mem amt))
(ushr_i8x16_mask (RegMemImm.Reg (x64_load $I64 amt (ExtKind.None)))))
@@ -739,41 +765,52 @@
;; `i64` and smaller.
(rule (lower (has_type (fits_in_64 ty) (sshr src amt)))
(let ((src_ Reg (extend_to_reg src ty (ExtendKind.Sign))))
(value_reg (sar ty src_ (put_masked_in_imm8_reg amt ty)))))
(let ((src_ Gpr (extend_to_gpr src ty (ExtendKind.Sign))))
(value_gpr (sar ty src_ (put_masked_in_imm8_gpr amt ty)))))
;; `i128`.
(decl sar_i128 (ValueRegs Reg) ValueRegs)
(decl sar_i128 (ValueRegs Gpr) ValueRegs)
(rule (sar_i128 src amt)
;; Unpack the low/high halves of `src`.
(let ((src_lo Reg (value_regs_get src 0))
(src_hi Reg (value_regs_get src 1))
(let ((src_lo Gpr (value_regs_get_gpr src 0))
(src_hi Gpr (value_regs_get_gpr src 1))
;; Do a shift of each half. NB: the low half uses an unsigned shift
;; because its MSB is not a sign bit.
(lo_shifted Reg (shr $I64 src_lo (Imm8Reg.Reg amt)))
(hi_shifted Reg (sar $I64 src_hi (Imm8Reg.Reg amt)))
(lo_shifted Gpr (shr $I64 src_lo (gpr_to_imm8_gpr amt)))
(hi_shifted Gpr (sar $I64 src_hi (gpr_to_imm8_gpr amt)))
;; `src_hi << (64 - amt)` are the bits to carry over from the low
;; half to the high half.
(carry Reg (shl $I64 src_hi (Imm8Reg.Reg (sub $I64 (imm $I64 64) (RegMemImm.Reg amt)))))
(carry Gpr (shl $I64
src_hi
(gpr_to_imm8_gpr (sub $I64
(gpr_new (imm $I64 64))
(gpr_to_gpr_mem_imm amt)))))
;; Nullify the carry if we are shifting by a multiple of 128.
(carry_ Reg (with_flags_1 (test (OperandSize.Size64) (RegMemImm.Imm 127) amt)
(cmove $I64 (CC.Z) (RegMem.Reg (imm $I64 0)) carry)))
(carry_ Gpr (gpr_new (with_flags_1 (test (OperandSize.Size64)
(gpr_mem_imm_new (RegMemImm.Imm 127))
amt)
(cmove $I64
(CC.Z)
(gpr_to_gpr_mem (gpr_new (imm $I64 0)))
carry))))
;; Add the carry into the low half.
(lo_shifted_ Reg (or $I64 lo_shifted (RegMemImm.Reg carry_)))
(lo_shifted_ Gpr (or $I64 lo_shifted (gpr_to_gpr_mem_imm carry_)))
;; Get all sign bits.
(sign_bits Reg (sar $I64 src_hi (Imm8Reg.Imm8 63))))
(sign_bits Gpr (sar $I64 src_hi (imm8_to_imm8_gpr 63))))
;; Combine the two shifted halves. However, if we are shifting by >= 64
;; (modulo 128), then the hi bits are all sign bits and the lo bits are
;; what would otherwise be our hi bits.
(with_flags_2 (test (OperandSize.Size64) (RegMemImm.Imm 64) amt)
(cmove $I64 (CC.Z) (RegMem.Reg lo_shifted_) hi_shifted)
(cmove $I64 (CC.Z) (RegMem.Reg hi_shifted) sign_bits))))
(with_flags_2 (test (OperandSize.Size64)
(gpr_mem_imm_new (RegMemImm.Imm 64))
amt)
(cmove $I64 (CC.Z) (gpr_to_gpr_mem lo_shifted_) hi_shifted)
(cmove $I64 (CC.Z) (gpr_to_gpr_mem hi_shifted) sign_bits))))
(rule (lower (has_type $I128 (sshr src amt)))
;; NB: Only the low bits of `amt` matter since we logically mask the shift
;; amount to the value's bit width.
(let ((amt_ Reg (lo_reg amt)))
(let ((amt_ Gpr (lo_gpr amt)))
(sar_i128 (put_in_regs src) amt_)))
;; SSE.
@@ -807,9 +844,13 @@
(rule (sshr_i8x16_bigger_shift _ty (RegMemImm.Imm i))
(xmm_mem_imm_new (RegMemImm.Imm (u32_add i 8))))
(rule (sshr_i8x16_bigger_shift ty (RegMemImm.Reg r))
(mov_rmi_to_xmm (RegMemImm.Reg (add ty r (RegMemImm.Imm 8)))))
(mov_rmi_to_xmm (RegMemImm.Reg (gpr_to_reg (add ty
(gpr_new r)
(gpr_mem_imm_new (RegMemImm.Imm 8)))))))
(rule (sshr_i8x16_bigger_shift ty rmi @ (RegMemImm.Mem _m))
(mov_rmi_to_xmm (RegMemImm.Reg (add ty (imm ty 8) rmi))))
(mov_rmi_to_xmm (RegMemImm.Reg (gpr_to_reg (add ty
(gpr_new (imm ty 8))
(gpr_mem_imm_new rmi))))))
;; `sshr.{i16x8,i32x4}` can be a simple `psra{w,d}`, we just have to make sure
;; that if the shift amount is in a register, it is in an XMM register.
@@ -834,11 +875,11 @@
(let ((src_ Xmm (put_in_xmm src))
(lo Gpr (pextrd $I64 src_ 0))
(hi Gpr (pextrd $I64 src_ 1))
(amt_ Imm8Reg (put_masked_in_imm8_reg amt $I64))
(shifted_lo Reg (sar $I64 (gpr_to_reg lo) amt_))
(shifted_hi Reg (sar $I64 (gpr_to_reg hi) amt_)))
(value_xmm (make_i64x2_from_lanes (reg_mem_to_gpr_mem (RegMem.Reg shifted_lo))
(reg_mem_to_gpr_mem (RegMem.Reg shifted_hi))))))
(amt_ Imm8Gpr (put_masked_in_imm8_gpr amt $I64))
(shifted_lo Gpr (sar $I64 lo amt_))
(shifted_hi Gpr (sar $I64 hi amt_)))
(value_xmm (make_i64x2_from_lanes (gpr_to_gpr_mem shifted_lo)
(gpr_to_gpr_mem shifted_hi)))))
;;;; Rules for `rotl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -846,13 +887,13 @@
;; constant.
(rule (lower (has_type (ty_8_or_16 ty) (rotl src amt)))
(let ((amt_ Reg (extend_to_reg amt $I32 (ExtendKind.Zero))))
(value_reg (x64_rotl ty (put_in_reg src) (Imm8Reg.Reg amt_)))))
(let ((amt_ Gpr (extend_to_gpr amt $I32 (ExtendKind.Zero))))
(value_gpr (x64_rotl ty (put_in_gpr src) (gpr_to_imm8_gpr amt_)))))
(rule (lower (has_type (ty_8_or_16 ty)
(rotl src (u64_from_iconst amt))))
(value_reg (x64_rotl ty
(put_in_reg src)
(value_gpr (x64_rotl ty
(put_in_gpr src)
(const_to_type_masked_imm8 amt ty))))
;; `i64` and `i32`: we can rely on x86's rotate-amount masking since
@@ -861,13 +902,13 @@
(rule (lower (has_type (ty_32_or_64 ty) (rotl src amt)))
;; NB: Only the low bits of `amt` matter since we logically mask the
;; shift amount to the value's bit width.
(let ((amt_ Reg (lo_reg amt)))
(value_reg (x64_rotl ty (put_in_reg src) (Imm8Reg.Reg amt_)))))
(let ((amt_ Gpr (lo_gpr amt)))
(value_gpr (x64_rotl ty (put_in_gpr src) (gpr_to_imm8_gpr amt_)))))
(rule (lower (has_type (ty_32_or_64 ty)
(rotl src (u64_from_iconst amt))))
(value_reg (x64_rotl ty
(put_in_reg src)
(value_gpr (x64_rotl ty
(put_in_gpr src)
(const_to_type_masked_imm8 amt ty))))
;; `i128`.
@@ -876,9 +917,11 @@
(let ((src_ ValueRegs (put_in_regs src))
;; NB: Only the low bits of `amt` matter since we logically mask the
;; rotation amount to the value's bit width.
(amt_ Reg (lo_reg amt)))
(amt_ Gpr (lo_gpr amt)))
(or_i128 (shl_i128 src_ amt_)
(shr_i128 src_ (sub $I64 (imm $I64 128) (RegMemImm.Reg amt_))))))
(shr_i128 src_ (sub $I64
(gpr_new (imm $I64 128))
(gpr_to_gpr_mem_imm amt_))))))
;;;; Rules for `rotr` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -886,13 +929,13 @@
;; constant.
(rule (lower (has_type (ty_8_or_16 ty) (rotr src amt)))
(let ((amt_ Reg (extend_to_reg amt $I32 (ExtendKind.Zero))))
(value_reg (x64_rotr ty (put_in_reg src) (Imm8Reg.Reg amt_)))))
(let ((amt_ Gpr (extend_to_gpr amt $I32 (ExtendKind.Zero))))
(value_gpr (x64_rotr ty (put_in_gpr src) (gpr_to_imm8_gpr amt_)))))
(rule (lower (has_type (ty_8_or_16 ty)
(rotr src (u64_from_iconst amt))))
(value_reg (x64_rotr ty
(put_in_reg src)
(value_gpr (x64_rotr ty
(put_in_gpr src)
(const_to_type_masked_imm8 amt ty))))
;; `i64` and `i32`: we can rely on x86's rotate-amount masking since
@@ -901,13 +944,13 @@
(rule (lower (has_type (ty_32_or_64 ty) (rotr src amt)))
;; NB: Only the low bits of `amt` matter since we logically mask the
;; shift amount to the value's bit width.
(let ((amt_ Reg (lo_reg amt)))
(value_reg (x64_rotr ty (put_in_reg src) (Imm8Reg.Reg amt_)))))
(let ((amt_ Gpr (lo_gpr amt)))
(value_gpr (x64_rotr ty (put_in_gpr src) (gpr_to_imm8_gpr amt_)))))
(rule (lower (has_type (ty_32_or_64 ty)
(rotr src (u64_from_iconst amt))))
(value_reg (x64_rotr ty
(put_in_reg src)
(value_gpr (x64_rotr ty
(put_in_gpr src)
(const_to_type_masked_imm8 amt ty))))
;; `i128`.
@@ -916,9 +959,11 @@
(let ((src_ ValueRegs (put_in_regs src))
;; NB: Only the low bits of `amt` matter since we logically mask the
;; rotation amount to the value's bit width.
(amt_ Reg (lo_reg amt)))
(amt_ Gpr (lo_gpr amt)))
(or_i128 (shr_i128 src_ amt_)
(shl_i128 src_ (sub $I64 (imm $I64 128) (RegMemImm.Reg amt_))))))
(shl_i128 src_ (sub $I64
(gpr_new (imm $I64 128))
(gpr_to_gpr_mem_imm amt_))))))
;;;; Rules for `ineg` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -961,33 +1006,33 @@
;; Multiply two registers.
(rule (lower (has_type (fits_in_64 ty) (imul x y)))
(value_reg (mul ty
(put_in_reg x)
(RegMemImm.Reg (put_in_reg y)))))
(value_gpr (mul ty
(put_in_gpr x)
(put_in_gpr_mem_imm y))))
;; Multiply a register and an immediate.
(rule (lower (has_type (fits_in_64 ty)
(imul x (simm32_from_value y))))
(value_reg (mul ty (put_in_reg x) y)))
(value_gpr (mul ty (put_in_gpr x) y)))
(rule (lower (has_type (fits_in_64 ty)
(imul (simm32_from_value x) y)))
(value_reg (mul ty (put_in_reg y) x)))
(value_gpr (mul ty (put_in_gpr y) x)))
;; Multiply a register and a memory load.
(rule (lower (has_type (fits_in_64 ty)
(imul x (sinkable_load y))))
(value_reg (mul ty
(put_in_reg x)
(sink_load y))))
(value_gpr (mul ty
(put_in_gpr x)
(sink_load_to_gpr_mem_imm y))))
(rule (lower (has_type (fits_in_64 ty)
(imul (sinkable_load x) y)))
(value_reg (mul ty
(put_in_reg y)
(sink_load x))))
(value_gpr (mul ty
(put_in_gpr y)
(sink_load_to_gpr_mem_imm x))))
;; `i128`.
@@ -1007,25 +1052,25 @@
(rule (lower (has_type $I128 (imul x y)))
;; Put `x` into registers and unpack its hi/lo halves.
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Reg (value_regs_get x_regs 0))
(x_hi Reg (value_regs_get x_regs 1))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1))
;; Put `y` into registers and unpack its hi/lo halves.
(y_regs ValueRegs (put_in_regs y))
(y_lo Reg (value_regs_get y_regs 0))
(y_hi Reg (value_regs_get y_regs 1))
(y_lo Gpr (value_regs_get_gpr y_regs 0))
(y_hi Gpr (value_regs_get_gpr y_regs 1))
;; lo_hi = mul x_lo, y_hi
(lo_hi Reg (mul $I64 x_lo (RegMemImm.Reg y_hi)))
(lo_hi Gpr (mul $I64 x_lo (gpr_to_gpr_mem_imm y_hi)))
;; hi_lo = mul x_hi, y_lo
(hi_lo Reg (mul $I64 x_hi (RegMemImm.Reg y_lo)))
(hi_lo Gpr (mul $I64 x_hi (gpr_to_gpr_mem_imm y_lo)))
;; hilo_hilo = add lo_hi, hi_lo
(hilo_hilo Reg (add $I64 lo_hi (RegMemImm.Reg hi_lo)))
(hilo_hilo Gpr (add $I64 lo_hi (gpr_to_gpr_mem_imm hi_lo)))
;; dst_lo:hi_lolo = mulhi_u x_lo, y_lo
(mul_regs ValueRegs (mulhi_u $I64 x_lo (RegMem.Reg y_lo)))
(dst_lo Reg (value_regs_get mul_regs 0))
(hi_lolo Reg (value_regs_get mul_regs 1))
(mul_regs ValueRegs (mulhi_u $I64 x_lo (gpr_to_gpr_mem y_lo)))
(dst_lo Gpr (value_regs_get_gpr mul_regs 0))
(hi_lolo Gpr (value_regs_get_gpr mul_regs 1))
;; dst_hi = add hilo_hilo, hi_lolo
(dst_hi Reg (add $I64 hilo_hilo (RegMemImm.Reg hi_lolo))))
(value_regs dst_lo dst_hi)))
(dst_hi Gpr (add $I64 hilo_hilo (gpr_to_gpr_mem_imm hi_lolo))))
(value_gprs dst_lo dst_hi)))
;; SSE.
@@ -1310,8 +1355,8 @@
(decl i128_not (Value) ValueRegs)
(rule (i128_not x)
(let ((x_regs ValueRegs (put_in_regs x))
(x_lo Gpr (gpr_new (value_regs_get x_regs 0)))
(x_hi Gpr (gpr_new (value_regs_get x_regs 1))))
(x_lo Gpr (value_regs_get_gpr x_regs 0))
(x_hi Gpr (value_regs_get_gpr x_regs 1)))
(value_gprs (not $I64 x_lo)
(not $I64 x_hi))))
@@ -1420,11 +1465,11 @@
(decl cmp_and_choose (Type CC Value Value) ValueRegs)
(rule (cmp_and_choose (fits_in_64 ty) cc x y)
(let ((x_reg Reg (put_in_reg x))
(y_reg Reg (put_in_reg y))
(let ((x_reg Gpr (put_in_gpr x))
(y_reg Gpr (put_in_gpr y))
(size OperandSize (raw_operand_size_of_type ty)))
(value_reg (with_flags_1 (cmp size (RegMemImm.Reg x_reg) y_reg)
(cmove ty cc (RegMem.Reg y_reg) x_reg)))))
(value_reg (with_flags_1 (cmp size (gpr_to_gpr_mem_imm x_reg) y_reg)
(cmove ty cc (gpr_to_gpr_mem y_reg) x_reg)))))
(rule (lower (has_type (fits_in_64 ty) (umin x y)))
(cmp_and_choose ty (CC.B) x y))

View File

@@ -79,7 +79,7 @@ where
if let Some(c) = inputs.constant {
if let Some(imm) = to_simm32(c as i64) {
return imm;
return imm.to_reg_mem_imm();
}
// Generate constants fresh at each use to minimize long-range
@@ -120,21 +120,23 @@ where
RegMem::reg(self.put_in_reg(val))
}
fn put_masked_in_imm8_reg(&mut self, val: Value, ty: Type) -> Imm8Reg {
fn put_masked_in_imm8_gpr(&mut self, val: Value, ty: Type) -> Imm8Gpr {
let inputs = self.lower_ctx.get_value_as_source_or_const(val);
if let Some(c) = inputs.constant {
let mask = 1_u64
.checked_shl(ty.bits() as u32)
.map_or(u64::MAX, |x| x - 1);
return Imm8Reg::Imm8 {
return Imm8Gpr::new(Imm8Reg::Imm8 {
imm: (c & mask) as u8,
};
})
.unwrap();
}
Imm8Reg::Reg {
Imm8Gpr::new(Imm8Reg::Reg {
reg: self.put_in_regs(val).regs()[0],
}
})
.unwrap()
}
#[inline]
@@ -178,17 +180,18 @@ where
}
#[inline]
fn const_to_type_masked_imm8(&mut self, c: u64, ty: Type) -> Imm8Reg {
fn const_to_type_masked_imm8(&mut self, c: u64, ty: Type) -> Imm8Gpr {
let mask = 1_u64
.checked_shl(ty.bits() as u32)
.map_or(u64::MAX, |x| x - 1);
Imm8Reg::Imm8 {
Imm8Gpr::new(Imm8Reg::Imm8 {
imm: (c & mask) as u8,
}
})
.unwrap()
}
#[inline]
fn simm32_from_value(&mut self, val: Value) -> Option<RegMemImm> {
fn simm32_from_value(&mut self, val: Value) -> Option<GprMemImm> {
let inst = self.lower_ctx.dfg().value_def(val).inst()?;
let constant: u64 = self.lower_ctx.get_constant(inst)?;
let constant = constant as i64;
@@ -196,7 +199,7 @@ where
}
#[inline]
fn simm32_from_imm64(&mut self, imm: Imm64) -> Option<RegMemImm> {
fn simm32_from_imm64(&mut self, imm: Imm64) -> Option<GprMemImm> {
to_simm32(imm.bits())
}
@@ -412,6 +415,31 @@ where
fn reg_to_gpr_mem(&mut self, r: Reg) -> GprMem {
GprMem::new(RegMem::reg(r)).unwrap()
}
#[inline]
fn imm8_reg_to_imm8_gpr(&mut self, ir: &Imm8Reg) -> Imm8Gpr {
Imm8Gpr::new(ir.clone()).unwrap()
}
#[inline]
fn gpr_to_gpr_mem(&mut self, gpr: Gpr) -> GprMem {
GprMem::from(gpr)
}
#[inline]
fn gpr_to_gpr_mem_imm(&mut self, gpr: Gpr) -> GprMemImm {
GprMemImm::from(gpr)
}
#[inline]
fn gpr_to_imm8_gpr(&mut self, gpr: Gpr) -> Imm8Gpr {
Imm8Gpr::from(gpr)
}
#[inline]
fn imm8_to_imm8_gpr(&mut self, imm: u8) -> Imm8Gpr {
Imm8Gpr::new(Imm8Reg::Imm8 { imm }).unwrap()
}
}
// Since x64 doesn't have 8x16 shifts and we must use a 16x8 shift instead, we
@@ -446,11 +474,14 @@ const I8X16_USHR_MASKS: [u8; 128] = [
];
#[inline]
fn to_simm32(constant: i64) -> Option<RegMemImm> {
fn to_simm32(constant: i64) -> Option<GprMemImm> {
if constant == ((constant << 32) >> 32) {
Some(RegMemImm::Imm {
simm32: constant as u32,
})
Some(
GprMemImm::new(RegMemImm::Imm {
simm32: constant as u32,
})
.unwrap(),
)
} else {
None
}

View File

@@ -1,4 +1,4 @@
src/clif.isle 9ea75a6f790b5c03
src/prelude.isle 73285cd431346d53
src/isa/x64/inst.isle 7513533d16948249
src/isa/x64/lower.isle 802b6e750d407100
src/isa/x64/inst.isle 301db31d5f1118ae
src/isa/x64/lower.isle cdc94aec26c0bc5b

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,6 @@ pub type ValueSlice<'a> = &'a [Value];
pub type ValueArray2 = [Value; 2];
pub type ValueArray3 = [Value; 3];
pub type WritableReg = Writable<Reg>;
pub type OptionWritableReg = Option<WritableReg>;
pub type VecReg = Vec<Reg>;
pub type VecWritableReg = Vec<WritableReg>;
pub type ValueRegs = crate::machinst::ValueRegs<Reg>;