Legalize b{and,or,xor}_not into component instructions (#5709)

* Remove trailing whitespace in `lower.isle` files

* Legalize the `band_not` instruction into simpler form

This commit legalizes the `band_not` instruction into `band`-of-`bnot`,
or two instructions. This is intended to assist with egraph-based
optimizations where the `band_not` instruction doesn't have to be
specifically included in other bit-operation-patterns.

Lowerings of the `band_not` instruction have been moved to a
specialization of the `band` instruction.

* Legalize `bor_not` into components

Same as prior commit, but for the `bor_not` instruction.

* Legalize bxor_not into bxor-of-bnot

Same as prior commits. I think this also ended up fixing a bug in the
s390x backend where `bxor_not x y` was actually translated as `bnot
(bxor x y)` by accident given the test update changes.

* Simplify not-fused operands for riscv64

Looks like some delegated-to rules have special-cases for "if this
feature is enabled use the fused instruction" so move the clause for
testing the feature up to the lowering phase to help trigger other rules
if the feature isn't enabled. This should make the riscv64 backend more
consistent with how other backends are implemented.

* Remove B{and,or,xor}Not from cost of egraph metrics

These shouldn't ever reach egraphs now that they're legalized away.

* Add an egraph optimization for `x^-1 => ~x`

This adds a simplification node to translate xor-against-minus-1 to a
`bnot` instruction. This helps trigger various other optimizations in
the egraph implementation and also various backend lowering rules for
instructions. This is chiefly useful as wasm doesn't have a `bnot`
equivalent, so it's encoded as `x^-1`.

* Add a wasm test for end-to-end bitwise lowerings

Test that end-to-end various optimizations are being applied for input
wasm modules.

* Specifically don't self-update rustup on CI

I forget why this was here originally, but this is failing on Windows
CI. In general there's no need to update rustup, so leave it as-is.

* Cleanup some aarch64 lowering rules

Previously a 32/64 split was necessary due to the `ALUOp` being different
but that's been refactored away no so there's no longer any need for
duplicate rules.

* Narrow a x64 lowering rule

This previously made more sense when it was `band_not` and rarely used,
but be more specific in the type-filter on this rule that it's only
applicable to SIMD types with lanes.

* Simplify xor-against-minus-1 rule

No need to have the commutative version since constants are already
shuffled right for egraphs

* Optimize band-of-bnot when bnot is on the left

Use some more rules in the egraph algebraic optimizations to
canonicalize band/bor/bxor with a `bnot` operand to put the operand on
the right. That way the lowerings in the backends only have to list the
rule once, with the operand on the right, to optimize both styles of
input.

* Add commutative lowering rules

* Update cranelift/codegen/src/isa/x64/lower.isle

Co-authored-by: Jamey Sharp <jamey@minilop.net>

---------

Co-authored-by: Jamey Sharp <jamey@minilop.net>
This commit is contained in:
Alex Crichton
2023-02-06 13:53:40 -06:00
committed by GitHub
parent 99c3936616
commit de0e0bea3f
17 changed files with 506 additions and 277 deletions

View File

@@ -18,11 +18,6 @@ runs:
- name: Install Rust - name: Install Rust
shell: bash shell: bash
run: | run: |
if [[ "${{ runner.os }}" = "Windows" ]]; then
rustup self update
fi
rustup set profile minimal rustup set profile minimal
rustup update "${{ inputs.toolchain }}" --no-self-update rustup update "${{ inputs.toolchain }}" --no-self-update
rustup default "${{ inputs.toolchain }}" rustup default "${{ inputs.toolchain }}"

View File

@@ -85,11 +85,8 @@ pub(crate) fn pure_op_cost(op: Opcode) -> Cost {
Opcode::Iadd Opcode::Iadd
| Opcode::Isub | Opcode::Isub
| Opcode::Band | Opcode::Band
| Opcode::BandNot
| Opcode::Bor | Opcode::Bor
| Opcode::BorNot
| Opcode::Bxor | Opcode::Bxor
| Opcode::BxorNot
| Opcode::Bnot | Opcode::Bnot
| Opcode::Ishl | Opcode::Ishl
| Opcode::Ushr | Opcode::Ushr

View File

@@ -1054,75 +1054,74 @@
;;;; Rules for `band` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `band` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule -1 (lower (has_type (fits_in_32 ty) (band x y))) (rule -1 (lower (has_type (fits_in_64 ty) (band x y)))
(alu_rs_imm_logic_commutative (ALUOp.And) ty x y)) (alu_rs_imm_logic_commutative (ALUOp.And) ty x y))
(rule (lower (has_type $I64 (band x y)))
(alu_rs_imm_logic_commutative (ALUOp.And) $I64 x y))
(rule (lower (has_type $I128 (band x y))) (i128_alu_bitop (ALUOp.And) $I64 x y)) (rule (lower (has_type $I128 (band x y))) (i128_alu_bitop (ALUOp.And) $I64 x y))
(rule -2 (lower (has_type (ty_vec128 ty) (band x y))) (rule -2 (lower (has_type (ty_vec128 ty) (band x y)))
(and_vec x y (vector_size ty))) (and_vec x y (vector_size ty)))
;; Specialized lowerings for `(band x (bnot y))` which is additionally produced
;; by Cranelift's `band_not` instruction that is legalized into the simpler
;; forms early on.
(rule 1 (lower (has_type (fits_in_64 ty) (band x (bnot y))))
(alu_rs_imm_logic (ALUOp.AndNot) ty x y))
(rule 2 (lower (has_type (fits_in_64 ty) (band (bnot y) x)))
(alu_rs_imm_logic (ALUOp.AndNot) ty x y))
(rule 3 (lower (has_type $I128 (band x (bnot y)))) (i128_alu_bitop (ALUOp.AndNot) $I64 x y))
(rule 4 (lower (has_type $I128 (band (bnot y) x))) (i128_alu_bitop (ALUOp.AndNot) $I64 x y))
(rule 5 (lower (has_type (ty_vec128 ty) (band x (bnot y))))
(bic_vec x y (vector_size ty)))
(rule 6 (lower (has_type (ty_vec128 ty) (band (bnot y) x)))
(bic_vec x y (vector_size ty)))
;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule -1 (lower (has_type (fits_in_32 ty) (bor x y))) (rule -1 (lower (has_type (fits_in_64 ty) (bor x y)))
(alu_rs_imm_logic_commutative (ALUOp.Orr) ty x y)) (alu_rs_imm_logic_commutative (ALUOp.Orr) ty x y))
(rule (lower (has_type $I64 (bor x y)))
(alu_rs_imm_logic_commutative (ALUOp.Orr) $I64 x y))
(rule (lower (has_type $I128 (bor x y))) (i128_alu_bitop (ALUOp.Orr) $I64 x y)) (rule (lower (has_type $I128 (bor x y))) (i128_alu_bitop (ALUOp.Orr) $I64 x y))
(rule -2 (lower (has_type (ty_vec128 ty) (bor x y))) (rule -2 (lower (has_type (ty_vec128 ty) (bor x y)))
(orr_vec x y (vector_size ty))) (orr_vec x y (vector_size ty)))
;; Specialized lowerings for `(bor x (bnot y))` which is additionally produced
;; by Cranelift's `bor_not` instruction that is legalized into the simpler
;; forms early on.
(rule 1 (lower (has_type (fits_in_64 ty) (bor x (bnot y))))
(alu_rs_imm_logic (ALUOp.OrrNot) ty x y))
(rule 2 (lower (has_type (fits_in_64 ty) (bor (bnot y) x)))
(alu_rs_imm_logic (ALUOp.OrrNot) ty x y))
(rule 3 (lower (has_type $I128 (bor x (bnot y)))) (i128_alu_bitop (ALUOp.OrrNot) $I64 x y))
(rule 4 (lower (has_type $I128 (bor (bnot y) x))) (i128_alu_bitop (ALUOp.OrrNot) $I64 x y))
;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule -1 (lower (has_type (fits_in_32 ty) (bxor x y))) (rule -1 (lower (has_type (fits_in_64 ty) (bxor x y)))
(alu_rs_imm_logic_commutative (ALUOp.Eor) ty x y)) (alu_rs_imm_logic_commutative (ALUOp.Eor) ty x y))
(rule (lower (has_type $I64 (bxor x y)))
(alu_rs_imm_logic_commutative (ALUOp.Eor) $I64 x y))
(rule (lower (has_type $I128 (bxor x y))) (i128_alu_bitop (ALUOp.Eor) $I64 x y)) (rule (lower (has_type $I128 (bxor x y))) (i128_alu_bitop (ALUOp.Eor) $I64 x y))
(rule -2 (lower (has_type (ty_vec128 ty) (bxor x y))) (rule -2 (lower (has_type (ty_vec128 ty) (bxor x y)))
(eor_vec x y (vector_size ty))) (eor_vec x y (vector_size ty)))
;;;; Rules for `band_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Specialized lowerings for `(bxor x (bnot y))` which is additionally produced
;; by Cranelift's `bxor_not` instruction that is legalized into the simpler
;; forms early on.
(rule -1 (lower (has_type (fits_in_32 ty) (band_not x y))) (rule 1 (lower (has_type (fits_in_64 ty) (bxor x (bnot y))))
(alu_rs_imm_logic (ALUOp.AndNot) ty x y)) (alu_rs_imm_logic (ALUOp.EorNot) ty x y))
(rule 2 (lower (has_type (fits_in_64 ty) (bxor (bnot y) x)))
(alu_rs_imm_logic (ALUOp.EorNot) ty x y))
(rule (lower (has_type $I64 (band_not x y))) (rule 3 (lower (has_type $I128 (bxor x (bnot y)))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y))
(alu_rs_imm_logic (ALUOp.AndNot) $I64 x y)) (rule 4 (lower (has_type $I128 (bxor (bnot y) x))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y))
(rule (lower (has_type $I128 (band_not x y))) (i128_alu_bitop (ALUOp.AndNot) $I64 x y))
(rule -2 (lower (has_type (ty_vec128 ty) (band_not x y)))
(bic_vec x y (vector_size ty)))
;;;; Rules for `bor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule -1 (lower (has_type (fits_in_32 ty) (bor_not x y)))
(alu_rs_imm_logic (ALUOp.OrrNot) ty x y))
(rule (lower (has_type $I64 (bor_not x y)))
(alu_rs_imm_logic (ALUOp.OrrNot) $I64 x y))
(rule (lower (has_type $I128 (bor_not x y))) (i128_alu_bitop (ALUOp.OrrNot) $I64 x y))
;;;; Rules for `bxor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule -1 (lower (has_type (fits_in_32 ty) (bxor_not x y)))
(alu_rs_imm_logic (ALUOp.EorNot) $I32 x y))
(rule (lower (has_type $I64 (bxor_not x y)))
(alu_rs_imm_logic (ALUOp.EorNot) $I64 x y))
(rule (lower (has_type $I128 (bxor_not x y))) (i128_alu_bitop (ALUOp.EorNot) $I64 x y))
;;;; Rules for `ishl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `ishl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -1950,32 +1950,14 @@
;;; ;;;
(decl gen_andn (Reg Reg) Reg) (decl gen_andn (Reg Reg) Reg)
(rule 1 (rule 1 (gen_andn rs1 rs2)
(gen_andn rs1 rs2)
(if-let $true (has_b))
(alu_rrr (AluOPRRR.Andn) rs1 rs2)) (alu_rrr (AluOPRRR.Andn) rs1 rs2))
(rule
(gen_andn rs1 rs2)
(if-let $false (has_b))
(let
((tmp Reg (gen_bit_not rs2)))
(alu_and rs1 tmp)))
;;; ;;;
(decl gen_orn (Reg Reg) Reg) (decl gen_orn (Reg Reg) Reg)
(rule 1 (rule 1 (gen_orn rs1 rs2)
(gen_orn rs1 rs2 )
(if-let $true (has_b))
(alu_rrr (AluOPRRR.Orn) rs1 rs2)) (alu_rrr (AluOPRRR.Orn) rs1 rs2))
(rule
(gen_orn rs1 rs2)
(if-let $false (has_b))
(let
((tmp Reg (gen_bit_not rs2)))
(alu_rrr (AluOPRRR.Or) rs1 tmp)))
(decl gen_rev8 (Reg) Reg) (decl gen_rev8 (Reg) Reg)
(rule 1 (rule 1
(gen_rev8 rs) (gen_rev8 rs)
@@ -2014,14 +1996,6 @@
(_ Unit (emit (MInst.Brev8 rs ty step tmp tmp2 rd)))) (_ Unit (emit (MInst.Brev8 rs ty step tmp tmp2 rd))))
(writable_reg_to_reg rd))) (writable_reg_to_reg rd)))
;;; x ^ ~y
(decl gen_xor_not (Reg Reg) Reg)
(rule
(gen_xor_not x y)
(let
((tmp Reg (gen_bit_not y)))
(alu_rrr (AluOPRRR.Xor) x tmp)))
;; Negates x ;; Negates x
;; Equivalent to 0 - x ;; Equivalent to 0 - x
(decl neg (Type ValueRegs) ValueRegs) (decl neg (Type ValueRegs) ValueRegs)

View File

@@ -204,6 +204,29 @@
(rule (lower (has_type $F64 (band x y))) (rule (lower (has_type $F64 (band x y)))
(lower_float_binary (AluOPRRR.And) x y $F64)) (lower_float_binary (AluOPRRR.And) x y $F64))
;; Specialized lowerings for `(band x (bnot y))` which is additionally produced
;; by Cranelift's `band_not` instruction that is legalized into the simpler
;; forms early on.
(rule 3 (lower (has_type (fits_in_64 ty) (band x (bnot y))))
(if-let $true (has_b))
(gen_andn x y))
(rule 4 (lower (has_type (fits_in_64 ty) (band (bnot y) x)))
(if-let $true (has_b))
(gen_andn x y))
(rule 5 (lower (has_type $I128 (band x (bnot y))))
(if-let $true (has_b))
(let
((low Reg (gen_andn (value_regs_get x 0) (value_regs_get y 0)))
(high Reg (gen_andn (value_regs_get x 1) (value_regs_get y 1))))
(value_regs low high)))
(rule 6 (lower (has_type $I128 (band (bnot y) x)))
(if-let $true (has_b))
(let
((low Reg (gen_andn (value_regs_get x 0) (value_regs_get y 0)))
(high Reg (gen_andn (value_regs_get x 1) (value_regs_get y 1))))
(value_regs low high)))
;;;; Rules for `or` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `or` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule -1 (lower (has_type (fits_in_64 ty) (bor x y))) (rule -1 (lower (has_type (fits_in_64 ty) (bor x y)))
@@ -222,6 +245,30 @@
(rule (lower (has_type $F64 (bor x y))) (rule (lower (has_type $F64 (bor x y)))
(lower_float_binary (AluOPRRR.Or) x y $F64)) (lower_float_binary (AluOPRRR.Or) x y $F64))
;; Specialized lowerings for `(bor x (bnot y))` which is additionally produced
;; by Cranelift's `bor_not` instruction that is legalized into the simpler
;; forms early on.
(rule 3 (lower (has_type (fits_in_64 ty) (bor x (bnot y))))
(if-let $true (has_b))
(gen_orn x y))
(rule 4 (lower (has_type (fits_in_64 ty) (bor (bnot y) x)))
(if-let $true (has_b))
(gen_orn x y))
(rule 5 (lower (has_type $I128 (bor x (bnot y))))
(if-let $true (has_b))
(let
((low Reg (gen_orn (value_regs_get x 0) (value_regs_get y 0)))
(high Reg (gen_orn (value_regs_get x 1) (value_regs_get y 1))))
(value_regs low high)))
(rule 6 (lower (has_type $I128 (bor (bnot y) x)))
(if-let $true (has_b))
(let
((low Reg (gen_orn (value_regs_get x 0) (value_regs_get y 0)))
(high Reg (gen_orn (value_regs_get x 1) (value_regs_get y 1))))
(value_regs low high)))
;;;; Rules for `xor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `xor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule -1 (lower (has_type (fits_in_64 ty) (bxor x y))) (rule -1 (lower (has_type (fits_in_64 ty) (bxor x y)))
@@ -289,15 +336,6 @@
(lower_extend x $true (ty_bits in) (ty_bits out))) (lower_extend x $true (ty_bits in) (ty_bits out)))
;;;; Rules for `band_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (has_type (fits_in_64 ty) (band_not x y)))
(gen_andn x y))
(rule 1 (lower (has_type $I128 (band_not x y)))
(let
((low Reg (gen_andn (value_regs_get x 0) (value_regs_get y 0)))
(high Reg (gen_andn (value_regs_get x 1) (value_regs_get y 1))))
(value_regs low high)))
;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `popcnt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (has_type (fits_in_64 ty) (popcnt x))) (rule (lower (has_type (fits_in_64 ty) (popcnt x)))
(lower_popcnt x ty)) (lower_popcnt x ty))
@@ -397,29 +435,6 @@
(lower_i128_rotr x y)) (lower_i128_rotr x y))
;;;; Rules for `bxor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; notice x y order!!!
(rule (lower (has_type (fits_in_64 ty) (bxor_not x y)))
(gen_xor_not x y))
(rule 1 (lower (has_type $I128 (bxor_not x y)))
(let
((low Reg (gen_xor_not (value_regs_get x 0) (value_regs_get y 0)))
(high Reg (gen_xor_not (value_regs_get x 1) (value_regs_get y 1))))
(value_regs low high)
)
)
;;;; Rules for `bor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (has_type (fits_in_64 ty) (bor_not x y)))
(gen_orn x y))
(rule 1 (lower (has_type $I128 (bor_not x y)))
(let
((low Reg (gen_orn (value_regs_get x 0) (value_regs_get y 0)))
(high Reg (gen_orn (value_regs_get x 1) (value_regs_get y 1))))
(value_regs low high)))
;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (has_type (fits_in_64 ty) (cls x))) (rule (lower (has_type (fits_in_64 ty) (cls x)))
(lower_cls x ty)) (lower_cls x ty))

View File

@@ -983,6 +983,22 @@
(rule 0 (lower (has_type (vr128_ty ty) (band x y))) (rule 0 (lower (has_type (vr128_ty ty) (band x y)))
(vec_and ty x y)) (vec_and ty x y))
;; Specialized lowerings for `(band x (bnot y))` which is additionally produced
;; by Cranelift's `band_not` instruction that is legalized into the simpler
;; forms early on.
;; z15 version using a single instruction.
(rule 7 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (band x (bnot y))))
(and_not_reg ty x y))
(rule 8 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (band (bnot y) x)))
(and_not_reg ty x y))
;; And-not two vector registers.
(rule 9 (lower (has_type (vr128_ty ty) (band x (bnot y))))
(vec_and_not ty x y))
(rule 10 (lower (has_type (vr128_ty ty) (band (bnot y) x)))
(vec_and_not ty x y))
;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Or two registers. ;; Or two registers.
@@ -1009,6 +1025,22 @@
(rule 0 (lower (has_type (vr128_ty ty) (bor x y))) (rule 0 (lower (has_type (vr128_ty ty) (bor x y)))
(vec_or ty x y)) (vec_or ty x y))
;; Specialized lowerings for `(bor x (bnot y))` which is additionally produced
;; by Cranelift's `bor_not` instruction that is legalized into the simpler
;; forms early on.
;; z15 version using a single instruction.
(rule 7 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (bor x (bnot y))))
(or_not_reg ty x y))
(rule 8 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (bor (bnot y) x)))
(or_not_reg ty x y))
;; Or-not two vector registers.
(rule 9 (lower (has_type (vr128_ty ty) (bor x (bnot y))))
(vec_or_not ty x y))
(rule 10 (lower (has_type (vr128_ty ty) (bor (bnot y) x)))
(vec_or_not ty x y))
;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `bxor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1032,49 +1064,20 @@
(rule 0 (lower (has_type (vr128_ty ty) (bxor x y))) (rule 0 (lower (has_type (vr128_ty ty) (bxor x y)))
(vec_xor ty x y)) (vec_xor ty x y))
;; Specialized lowerings for `(bxor x (bnot y))` which is additionally produced
;;;; Rules for `band_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; by Cranelift's `bxor_not` instruction that is legalized into the simpler
;; forms early on.
;; z15 version using a single instruction. ;; z15 version using a single instruction.
(rule 2 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (band_not x y))) (rule 5 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (bxor x (bnot y))))
(and_not_reg ty x y)) (not_xor_reg ty x y))
(rule 6 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (bxor (bnot y) x)))
;; z14 version using XOR with -1.
(rule 1 (lower (has_type (and (mie2_disabled) (fits_in_64 ty)) (band_not x y)))
(and_reg ty x (not_reg ty y)))
;; And-not two vector registers.
(rule (lower (has_type (vr128_ty ty) (band_not x y)))
(vec_and_not ty x y))
;;;; Rules for `bor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; z15 version using a single instruction.
(rule 2 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (bor_not x y)))
(or_not_reg ty x y))
;; z14 version using XOR with -1.
(rule 1 (lower (has_type (and (mie2_disabled) (fits_in_64 ty)) (bor_not x y)))
(or_reg ty x (not_reg ty y)))
;; Or-not two vector registers.
(rule (lower (has_type (vr128_ty ty) (bor_not x y)))
(vec_or_not ty x y))
;;;; Rules for `bxor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; z15 version using a single instruction.
(rule 2 (lower (has_type (and (mie2_enabled) (fits_in_64 ty)) (bxor_not x y)))
(not_xor_reg ty x y)) (not_xor_reg ty x y))
;; z14 version using XOR with -1.
(rule 1 (lower (has_type (and (mie2_disabled) (fits_in_64 ty)) (bxor_not x y)))
(not_reg ty (xor_reg ty x y)))
;; Xor-not two vector registers. ;; Xor-not two vector registers.
(rule (lower (has_type (vr128_ty ty) (bxor_not x y))) (rule 7 (lower (has_type (vr128_ty ty) (bxor x (bnot y))))
(vec_not_xor ty x y))
(rule 8 (lower (has_type (vr128_ty ty) (bxor (bnot y) x)))
(vec_not_xor ty x y)) (vec_not_xor ty x y))

View File

@@ -269,6 +269,36 @@
(value_gprs (x64_and $I64 x_lo y_lo) (value_gprs (x64_and $I64 x_lo y_lo)
(x64_and $I64 x_hi y_hi)))) (x64_and $I64 x_hi y_hi))))
;; Specialized lowerings for `(band x (bnot y))` which is additionally produced
;; by Cranelift's `band_not` instruction that is legalized into the simpler
;; forms early on.
(decl sse_and_not (Type Xmm XmmMem) Xmm)
(rule (sse_and_not $F32X4 x y) (x64_andnps x y))
(rule (sse_and_not $F64X2 x y) (x64_andnpd x y))
(rule -1 (sse_and_not (multi_lane _bits _lanes) x y) (x64_pandn x y))
;; Note the flipping of operands below as we're match
;;
;; (band x (bnot y))
;;
;; while x86 does
;;
;; pandn(x, y) = and(not(x), y)
(rule 8 (lower (has_type ty @ (multi_lane _bits _lane) (band x (bnot y))))
(sse_and_not ty y x))
(rule 9 (lower (has_type ty @ (multi_lane _bits _lane) (band (bnot y) x)))
(sse_and_not ty y x))
(rule 10 (lower (has_type ty @ (use_bmi1 $true) (band x (bnot y))))
(if (ty_int_ref_scalar_64 ty))
;; the first argument is the one that gets inverted with andn
(x64_andn ty y x))
(rule 11 (lower (has_type ty @ (use_bmi1 $true) (band (bnot y) x)))
(if (ty_int_ref_scalar_64 ty))
(x64_andn ty y x))
;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `bor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; `{i,b}64` and smaller. ;; `{i,b}64` and smaller.
@@ -1085,52 +1115,6 @@
(OperandSize.Size32)))) (OperandSize.Size32))))
(x64_pmuludq x2 y2))) (x64_pmuludq x2 y2)))
;;;; Rules for `band_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl sse_and_not (Type Xmm XmmMem) Xmm)
(rule (sse_and_not $F32X4 x y) (x64_andnps x y))
(rule (sse_and_not $F64X2 x y) (x64_andnpd x y))
(rule -1 (sse_and_not (multi_lane _bits _lanes) x y) (x64_pandn x y))
;; Note the flipping of operands below. CLIF specifies
;;
;; band_not(x, y) = and(x, not(y))
;;
;; while x86 does
;;
;; pandn(x, y) = and(not(x), y)
(rule 0 (lower (has_type ty (band_not x y)))
(sse_and_not ty y x))
(rule 1 (lower (has_type ty @ (use_bmi1 $false) (band_not x y)))
(if (ty_int_ref_scalar_64 ty))
(x64_and ty
x
(x64_not ty y)))
(rule 1 (lower (has_type ty @ (use_bmi1 $true) (band_not x y)))
(if (ty_int_ref_scalar_64 ty))
;; the first argument is the one that gets inverted with andn
(x64_andn ty y x))
;;;; Rules for `bxor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule 0 (lower (has_type ty (bxor_not x y)))
(if (ty_int_ref_scalar_64 ty))
(x64_xor ty
x
(x64_not ty y)))
;;;; Rules for `bor_not` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule 0 (lower (has_type ty (bor_not x y)))
(if (ty_int_ref_scalar_64 ty))
(x64_or ty
x
(x64_not ty y)))
;;;; Rules for `iabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Rules for `iabs` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (has_type $I8X16 (iabs x))) (rule (lower (has_type $I8X16 (iabs x)))

View File

@@ -224,6 +224,28 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
pos.func.dfg.replace(inst).icmp(cond, arg, imm); pos.func.dfg.replace(inst).icmp(cond, arg, imm);
} }
// Legalize the fused bitwise-plus-not instructions into simpler
// instructions to assist with optimizations. Lowering will
// pattern match this sequence regardless when architectures
// support the instruction natively.
InstructionData::Binary { opcode, args } => {
match opcode {
ir::Opcode::BandNot => {
let neg = pos.ins().bnot(args[1]);
pos.func.dfg.replace(inst).band(args[0], neg);
}
ir::Opcode::BorNot => {
let neg = pos.ins().bnot(args[1]);
pos.func.dfg.replace(inst).bor(args[0], neg);
}
ir::Opcode::BxorNot => {
let neg = pos.ins().bnot(args[1]);
pos.func.dfg.replace(inst).bxor(args[0], neg);
}
_ => prev_pos = pos.position(),
};
}
_ => { _ => {
prev_pos = pos.position(); prev_pos = pos.position();
continue; continue;

View File

@@ -281,3 +281,8 @@
(rule (simplify (rule (simplify
(icmp (ty_int ty) (IntCC.SignedLessThanOrEqual) x x)) (icmp (ty_int ty) (IntCC.SignedLessThanOrEqual) x x))
(iconst ty (imm64 1))) (iconst ty (imm64 1)))
;; (x ^ -1) can be replaced with the `bnot` instruction
(rule (simplify (bxor ty x (iconst ty k)))
(if-let -1 (i64_sextend_imm64 ty k))
(bnot ty x))

View File

@@ -221,3 +221,33 @@ block0(v1: i8):
; check: v3 = iconst.i8 0 ; check: v3 = iconst.i8 0
; check: return v3 ; check: return v3
function %bnot1(i8) -> i8 {
block0(v1: i8):
v2 = iconst.i8 -1
v3 = bxor v1, v2
return v3
}
; check: v4 = bnot v1
; check: return v4
function %bnot2(i64) -> i64 {
block0(v1: i64):
v2 = iconst.i64 -1
v3 = bxor v1, v2
return v3
}
; check: v4 = bnot v1
; check: return v4
function %bnot3(i64) -> i64 {
block0(v1: i64):
v2 = iconst.i64 -1
v3 = bxor v2, v1
return v3
}
; check: v5 = bnot v1
; check: return v5

View File

@@ -0,0 +1,37 @@
test compile precise-output
set unwind_info=false
set opt_level=speed
target aarch64
function %band_not_i32_reversed(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bnot v0
v3 = band v2, v1
return v3
}
; block0:
; bic w0, w1, w0
; ret
function %bor_not_i32_reversed(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bnot v0
v3 = bor v2, v1
return v3
}
; block0:
; orn w0, w1, w0
; ret
function %bxor_not_i32_reversed(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bnot v0
v3 = bxor v2, v1
return v3
}
; block0:
; eon w0, w1, w0
; ret

View File

@@ -0,0 +1,45 @@
test compile precise-output
set opt_level=speed
target riscv64 has_b
function %band_not_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = band_not.i32 v0, v1
return v2
}
; block0:
; andn a0,a0,a1
; ret
function %band_not_i32_reversed(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bnot v0
v3 = band v2, v1
return v3
}
; block0:
; andn a0,a1,a0
; ret
function %bor_not_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bor_not.i32 v0, v1
return v2
}
; block0:
; orn a0,a0,a1
; ret
function %bor_not_i32_reversed(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bnot v0
v3 = bor v2, v1
return v3
}
; block0:
; orn a0,a1,a0
; ret

View File

@@ -631,9 +631,9 @@ block0(v0: i128, v1: i128):
} }
; block0: ; block0:
; not a2,a2 ; not a4,a2
; and a0,a0,a2
; not a6,a3 ; not a6,a3
; and a0,a0,a4
; and a1,a1,a6 ; and a1,a1,a6
; ret ; ret
@@ -645,9 +645,9 @@ block0(v0: i64):
} }
; block0: ; block0:
; li t2,4 ; li a1,4
; not a1,t2 ; not a2,a1
; and a0,a0,a1 ; and a0,a0,a2
; ret ; ret
function %band_not_i64_constant_shift(i64, i64) -> i64 { function %band_not_i64_constant_shift(i64, i64) -> i64 {
@@ -660,8 +660,8 @@ block0(v0: i64, v1: i64):
; block0: ; block0:
; slli a2,a1,4 ; slli a2,a1,4
; not a1,a2 ; not a2,a2
; and a0,a0,a1 ; and a0,a0,a2
; ret ; ret
function %bor_not_i32(i32, i32) -> i32 { function %bor_not_i32(i32, i32) -> i32 {
@@ -693,9 +693,9 @@ block0(v0: i128, v1: i128):
} }
; block0: ; block0:
; not a2,a2 ; not a4,a2
; or a0,a0,a2
; not a6,a3 ; not a6,a3
; or a0,a0,a4
; or a1,a1,a6 ; or a1,a1,a6
; ret ; ret
@@ -707,9 +707,9 @@ block0(v0: i64):
} }
; block0: ; block0:
; li t2,4 ; li a1,4
; not a1,t2 ; not a2,a1
; or a0,a0,a1 ; or a0,a0,a2
; ret ; ret
function %bor_not_i64_constant_shift(i64, i64) -> i64 { function %bor_not_i64_constant_shift(i64, i64) -> i64 {
@@ -722,8 +722,8 @@ block0(v0: i64, v1: i64):
; block0: ; block0:
; slli a2,a1,4 ; slli a2,a1,4
; not a1,a2 ; not a2,a2
; or a0,a0,a1 ; or a0,a0,a2
; ret ; ret
function %bxor_not_i32(i32, i32) -> i32 { function %bxor_not_i32(i32, i32) -> i32 {
@@ -755,9 +755,9 @@ block0(v0: i128, v1: i128):
} }
; block0: ; block0:
; not a2,a2 ; not a4,a2
; xor a0,a0,a2
; not a6,a3 ; not a6,a3
; xor a0,a0,a4
; xor a1,a1,a6 ; xor a1,a1,a6
; ret ; ret
@@ -769,9 +769,9 @@ block0(v0: i64):
} }
; block0: ; block0:
; li t2,4 ; li a1,4
; not a1,t2 ; not a2,a1
; xor a0,a0,a1 ; xor a0,a0,a2
; ret ; ret
function %bxor_not_i64_constant_shift(i64, i64) -> i64 { function %bxor_not_i64_constant_shift(i64, i64) -> i64 {
@@ -784,8 +784,8 @@ block0(v0: i64, v1: i64):
; block0: ; block0:
; slli a2,a1,4 ; slli a2,a1,4
; not a1,a2 ; not a2,a2
; xor a0,a0,a1 ; xor a0,a0,a2
; ret ; ret
function %ishl_i128_i8(i128, i8) -> i128 { function %ishl_i128_i8(i128, i8) -> i128 {

View File

@@ -0,0 +1,66 @@
test compile precise-output
set opt_level=speed
target s390x has_mie2
function %band_not_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = band_not.i32 v0, v1
return v2
}
; block0:
; ncrk %r2, %r2, %r3
; br %r14
function %band_not_i32_reversed(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bnot v0
v3 = band v2, v1
return v3
}
; block0:
; ncrk %r2, %r3, %r2
; br %r14
function %bor_not_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bor_not.i32 v0, v1
return v2
}
; block0:
; ocrk %r2, %r2, %r3
; br %r14
function %bor_not_i32_reversed(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bnot v0
v3 = bor v2, v1
return v3
}
; block0:
; ocrk %r2, %r3, %r2
; br %r14
function %bxor_not_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bxor_not.i32 v0, v1
return v2
}
; block0:
; nxrk %r2, %r2, %r3
; br %r14
function %bxor_not_i32_reversed(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bnot v0
v3 = bxor v2, v1
return v3
}
; block0:
; nxrk %r2, %r3, %r2
; br %r14

View File

@@ -366,9 +366,8 @@ block0(v0: i32, v1: i32):
} }
; block0: ; block0:
; lgr %r5, %r3 ; xilf %r3, 4294967295
; xilf %r5, 4294967295 ; nr %r2, %r3
; nr %r2, %r5
; br %r14 ; br %r14
function %band_not_i16(i16, i16) -> i16 { function %band_not_i16(i16, i16) -> i16 {
@@ -378,9 +377,8 @@ block0(v0: i16, v1: i16):
} }
; block0: ; block0:
; lgr %r5, %r3 ; xilf %r3, 4294967295
; xilf %r5, 4294967295 ; nr %r2, %r3
; nr %r2, %r5
; br %r14 ; br %r14
function %band_not_i8(i8, i8) -> i8 { function %band_not_i8(i8, i8) -> i8 {
@@ -390,9 +388,8 @@ block0(v0: i8, v1: i8):
} }
; block0: ; block0:
; lgr %r5, %r3 ; xilf %r3, 4294967295
; xilf %r5, 4294967295 ; nr %r2, %r3
; nr %r2, %r5
; br %r14 ; br %r14
function %bor_not_i128(i128, i128) -> i128 { function %bor_not_i128(i128, i128) -> i128 {
@@ -427,9 +424,8 @@ block0(v0: i32, v1: i32):
} }
; block0: ; block0:
; lgr %r5, %r3 ; xilf %r3, 4294967295
; xilf %r5, 4294967295 ; or %r2, %r3
; or %r2, %r5
; br %r14 ; br %r14
function %bor_not_i16(i16, i16) -> i16 { function %bor_not_i16(i16, i16) -> i16 {
@@ -439,9 +435,8 @@ block0(v0: i16, v1: i16):
} }
; block0: ; block0:
; lgr %r5, %r3 ; xilf %r3, 4294967295
; xilf %r5, 4294967295 ; or %r2, %r3
; or %r2, %r5
; br %r14 ; br %r14
function %bor_not_i8(i8, i8) -> i8 { function %bor_not_i8(i8, i8) -> i8 {
@@ -451,9 +446,8 @@ block0(v0: i8, v1: i8):
} }
; block0: ; block0:
; lgr %r5, %r3 ; xilf %r3, 4294967295
; xilf %r5, 4294967295 ; or %r2, %r3
; or %r2, %r5
; br %r14 ; br %r14
function %bxor_not_i128(i128, i128) -> i128 { function %bxor_not_i128(i128, i128) -> i128 {
@@ -476,9 +470,9 @@ block0(v0: i64, v1: i64):
} }
; block0: ; block0:
; xilf %r3, 4294967295
; xihf %r3, 4294967295
; xgr %r2, %r3 ; xgr %r2, %r3
; xilf %r2, 4294967295
; xihf %r2, 4294967295
; br %r14 ; br %r14
function %bxor_not_i32(i32, i32) -> i32 { function %bxor_not_i32(i32, i32) -> i32 {
@@ -488,8 +482,8 @@ block0(v0: i32, v1: i32):
} }
; block0: ; block0:
; xilf %r3, 4294967295
; xr %r2, %r3 ; xr %r2, %r3
; xilf %r2, 4294967295
; br %r14 ; br %r14
function %bxor_not_i16(i16, i16) -> i16 { function %bxor_not_i16(i16, i16) -> i16 {
@@ -499,8 +493,8 @@ block0(v0: i16, v1: i16):
} }
; block0: ; block0:
; xilf %r3, 4294967295
; xr %r2, %r3 ; xr %r2, %r3
; xilf %r2, 4294967295
; br %r14 ; br %r14
function %bxor_not_i8(i8, i8) -> i8 { function %bxor_not_i8(i8, i8) -> i8 {
@@ -510,8 +504,8 @@ block0(v0: i8, v1: i8):
} }
; block0: ; block0:
; xilf %r3, 4294967295
; xr %r2, %r3 ; xr %r2, %r3
; xilf %r2, 4294967295
; br %r14 ; br %r14
function %bnot_i128(i128) -> i128 { function %bnot_i128(i128) -> i128 {

View File

@@ -1,4 +1,5 @@
test compile precise-output test compile precise-output
set opt_level=speed
target x86_64 has_bmi1 target x86_64 has_bmi1
function %f1(i8, i8) -> i8 { function %f1(i8, i8) -> i8 {
@@ -15,3 +16,19 @@ block0(v0: i8, v1: i8):
; popq %rbp ; popq %rbp
; ret ; ret
function %reversed_operands(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = bnot v0
v3 = band v2, v1
return v3
}
; pushq %rbp
; movq %rsp, %rbp
; block0:
; andn %eax, %edi, %esi
; movq %rbp, %rsp
; popq %rbp
; ret

View File

@@ -0,0 +1,46 @@
;;!target = "x86_64"
;;!compile = true
;;!settings = ["opt_level=speed", "has_bmi1=true"]
(module
;; this should get optimized to a `bnot` in clif
(func (param i32) (result i32)
i32.const -1
local.get 0
i32.xor)
;; this should get optimized to a single `andn` instruction
(func (param i32 i32) (result i32)
local.get 0
i32.const -1
local.get 1
i32.xor
i32.and)
)
;; function u0:0:
;; pushq %rbp
;; unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
;; movq %rsp, %rbp
;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
;; block0:
;; jmp label1
;; block1:
;; movq %rdi, %rax
;; notl %eax, %eax
;; movq %rbp, %rsp
;; popq %rbp
;; ret
;;
;; function u0:1:
;; pushq %rbp
;; unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
;; movq %rsp, %rbp
;; unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
;; block0:
;; jmp label1
;; block1:
;; andn %eax, %esi, %edi
;; movq %rbp, %rsp
;; popq %rbp
;; ret