cranelift: Add Bswap instruction (#1092) (#5147)

Adds Bswap to the Cranelift IR. Implements the Bswap instruction
in the x64 and aarch64 codegen backends. Cranelift users can now:
```
builder.ins().bswap(value)
```
to get a native byteswap instruction.

* x64: implements the 32- and 64-bit bswap instruction, following
the pattern set by similar unary instrutions (Neg and Not) - it
only operates on a dst register, but is parameterized with both
a src and dst which are expected to be the same register.

As x64 bswap instruction is only for 32- or 64-bit registers,
the 16-bit swap is implemented as a rotate left by 8.

Updated x64 RexFlags type to support emitting for single-operand
instructions like bswap

* aarch64: Bswap gets emitted as aarch64 rev16, rev32,
or rev64 instruction as appropriate.

* s390x: Bswap was already supported in backend, just had to add
a bit of plumbing

* For completeness, added bswap to the interpreter as well.

* added filetests and runtests for each ISA

* added bswap to fuzzgen, thanks to afonso360 for the code there

* 128-bit swaps are not yet implemented, that can be done later
This commit is contained in:
11evan
2022-10-31 12:30:00 -07:00
committed by GitHub
parent 95ecb7e4d4
commit 4ca9e82bd1
24 changed files with 455 additions and 0 deletions

View File

@@ -1023,6 +1023,10 @@
(RBit)
(Clz)
(Cls)
;; Byte reverse
(Rev16)
(Rev32)
(Rev64)
))
(type MemLabel extern (enum))
@@ -2579,6 +2583,17 @@
(decl a64_cls (Type Reg) Reg)
(rule (a64_cls ty x) (bit_rr (BitOp.Cls) ty x))
;; Helpers for generating `rev` instructions
(decl a64_rev16 (Type Reg) Reg)
(rule (a64_rev16 ty x) (bit_rr (BitOp.Rev16) ty x))
(decl a64_rev32 (Type Reg) Reg)
(rule (a64_rev32 ty x) (bit_rr (BitOp.Rev32) ty x))
(decl a64_rev64 (Type Reg) Reg)
(rule (a64_rev64 ty x) (bit_rr (BitOp.Rev64) ty x))
;; Helpers for generating `eon` instructions.
(decl eon (Type Reg Reg) Reg)

View File

@@ -934,6 +934,9 @@ impl MachInstEmit for Inst {
BitOp::RBit => (0b00000, 0b000000),
BitOp::Clz => (0b00000, 0b000100),
BitOp::Cls => (0b00000, 0b000101),
BitOp::Rev16 => (0b00000, 0b000001),
BitOp::Rev32 => (0b00000, 0b000010),
BitOp::Rev64 => (0b00000, 0b000011),
};
sink.put4(enc_bit_rr(size.sf_bit(), op1, op2, rn, rd))
}

View File

@@ -1375,6 +1375,61 @@ fn test_aarch64_binemit() {
"cls x21, x16",
));
insns.push((
Inst::BitRR {
op: BitOp::Rev16,
size: OperandSize::Size64,
rd: writable_xreg(2),
rn: xreg(11),
},
"6205C0DA",
"rev16 x2, x11",
));
insns.push((
Inst::BitRR {
op: BitOp::Rev16,
size: OperandSize::Size32,
rd: writable_xreg(3),
rn: xreg(21),
},
"A306C05A",
"rev16 w3, w21",
));
insns.push((
Inst::BitRR {
op: BitOp::Rev32,
size: OperandSize::Size64,
rd: writable_xreg(2),
rn: xreg(11),
},
"6209C0DA",
"rev32 x2, x11",
));
insns.push((
Inst::BitRR {
op: BitOp::Rev32,
size: OperandSize::Size32,
rd: writable_xreg(3),
rn: xreg(21),
},
"A30AC05A",
"rev32 w3, w21",
));
insns.push((
Inst::BitRR {
op: BitOp::Rev64,
size: OperandSize::Size64,
rd: writable_xreg(1),
rn: xreg(10),
},
"410DC0DA",
"rev64 x1, x10",
));
insns.push((
Inst::ULoad8 {
rd: writable_xreg(1),

View File

@@ -67,6 +67,9 @@ impl BitOp {
BitOp::RBit => "rbit",
BitOp::Clz => "clz",
BitOp::Cls => "cls",
BitOp::Rev16 => "rev16",
BitOp::Rev32 => "rev32",
BitOp::Rev64 => "rev64",
}
}
}

View File

@@ -1517,6 +1517,17 @@
(rule -1 (lower (has_type ty (cls x)))
(a64_cls ty x))
;;;; Rules for `bswap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(rule (lower (has_type $I16 (bswap x)))
(a64_rev16 $I16 x))
(rule (lower (has_type $I32 (bswap x)))
(a64_rev32 $I32 x))
(rule (lower (has_type $I64 (bswap x)))
(a64_rev64 $I64 x))
;;;; Rules for `bmask` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Bmask tests the value against zero, and uses `csetm` to assert the result.

View File

@@ -96,6 +96,8 @@ pub(crate) fn lower_insn_to_regs(
Opcode::Bitrev | Opcode::Clz | Opcode::Cls | Opcode::Ctz => implemented_in_isle(ctx),
Opcode::Bswap => implemented_in_isle(ctx),
Opcode::Popcnt => implemented_in_isle(ctx),
Opcode::Load