Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cranelift: Add Bswap instruction (#1092) #5147

Merged
merged 1 commit into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions cranelift/codegen/meta/src/shared/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,12 @@ pub(crate) fn define(
TypeSetBuilder::new().ints(Interval::All).build(),
);

let iSwappable = &TypeVar::new(
"iSwappable",
"A multi byte scalar integer type",
TypeSetBuilder::new().ints(16..128).build(),
);

let iAddr = &TypeVar::new(
"iAddr",
"An integer address type",
Expand Down Expand Up @@ -2699,6 +2705,23 @@ pub(crate) fn define(
.operands_out(vec![a]),
);

let x = &Operand::new("x", iSwappable);
let a = &Operand::new("a", iSwappable);

ig.push(
Inst::new(
"bswap",
r#"
Reverse the byte order of an integer.

Reverses the bytes in ``x``.
"#,
&formats.unary,
)
.operands_in(vec![x])
.operands_out(vec![a]),
);

let x = &Operand::new("x", Int);
let a = &Operand::new("a", Int);

Expand Down
15 changes: 15 additions & 0 deletions cranelift/codegen/src/isa/aarch64/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,10 @@
(RBit)
(Clz)
(Cls)
;; Byte reverse
(Rev16)
(Rev32)
(Rev64)
))

(type MemLabel extern (enum))
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions cranelift/codegen/src/isa/aarch64/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down
55 changes: 55 additions & 0 deletions cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
3 changes: 3 additions & 0 deletions cranelift/codegen/src/isa/aarch64/inst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ impl BitOp {
BitOp::RBit => "rbit",
BitOp::Clz => "clz",
BitOp::Cls => "cls",
BitOp::Rev16 => "rev16",
BitOp::Rev32 => "rev32",
BitOp::Rev64 => "rev64",
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions cranelift/codegen/src/isa/aarch64/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions cranelift/codegen/src/isa/aarch64/lower_inst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions cranelift/codegen/src/isa/s390x/lower.isle
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,18 @@
7 6 5 4 3 2 1 0))))


;;;; Rules for `bswap` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(rule (lower (has_type $I16 (bswap x)))
(lshr_imm $I32 (bswap_reg $I32 x) 16))

(rule (lower (has_type $I32 (bswap x)))
(bswap_reg $I32 x))

(rule (lower (has_type $I64 (bswap x)))
(bswap_reg $I64 x))


;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; The FLOGR hardware instruction always operates on the full 64-bit register.
Expand Down
1 change: 1 addition & 0 deletions cranelift/codegen/src/isa/s390x/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ impl LowerBackend for S390xBackend {
| Opcode::Vselect
| Opcode::Bmask
| Opcode::Bitrev
| Opcode::Bswap
| Opcode::Clz
| Opcode::Cls
| Opcode::Ctz
Expand Down
15 changes: 15 additions & 0 deletions cranelift/codegen/src/isa/x64/encoding/rex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,21 @@ impl RexFlags {
(self.0 & 2) != 0
}

#[inline(always)]
pub(crate) fn emit_one_op(&self, sink: &mut MachBuffer<Inst>, enc_e: u8) {
// Register Operand coded in Opcode Byte
// REX.R and REX.X unused
// REX.B == 1 accesses r8-r15
let w = if self.must_clear_w() { 0 } else { 1 };
let r = 0;
let x = 0;
let b = (enc_e >> 3) & 1;
let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
if rex != 0x40 || self.must_always_emit() {
sink.put1(rex);
}
}

#[inline(always)]
pub(crate) fn emit_two_op(&self, sink: &mut MachBuffer<Inst>, enc_g: u8, enc_e: u8) {
let w = if self.must_clear_w() { 0 } else { 1 };
Expand Down
15 changes: 15 additions & 0 deletions cranelift/codegen/src/isa/x64/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@
(Setcc (cc CC)
(dst WritableGpr))

;; Swaps byte order in register
(Bswap (size OperandSize) ;; 4 or 8
(src Gpr)
(dst WritableGpr))

;; =========================================
;; Conditional moves.

Expand Down Expand Up @@ -1959,6 +1964,16 @@
(rule (x64_sar ty src1 src2)
(shift_r ty (ShiftKind.ShiftRightArithmetic) src1 src2))

;; Helper for creating byteswap instructions.
;; In x64, 32- and 64-bit registers use BSWAP instruction, and
;; for 16-bit registers one must instead use xchg or rol/ror
(decl x64_bswap (Type Gpr) Gpr)
(rule (x64_bswap ty src)
(let ((dst WritableGpr (temp_writable_gpr))
(size OperandSize (operand_size_of_type_32_64 ty))
(_ Unit (emit (MInst.Bswap size src dst))))
dst))

;; Helper for creating `MInst.CmpRmiR` instructions.
(decl cmp_rmi_r (OperandSize CmpOpcode GprMemImm Gpr) ProducesFlags)
(rule (cmp_rmi_r size opcode src1 src2)
Expand Down
15 changes: 15 additions & 0 deletions cranelift/codegen/src/isa/x64/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,21 @@ pub(crate) fn emit(
);
}

Inst::Bswap { size, src, dst } => {
let src = allocs.next(src.to_reg());
let dst = allocs.next(dst.to_reg().to_reg());
debug_assert_eq!(src, dst);
let enc_reg = int_reg_enc(dst);

// BSWAP reg32 is (REX.W==0) 0F C8
// BSWAP reg64 is (REX.W==1) 0F C8
let rex_flags = RexFlags::from(*size);
rex_flags.emit_one_op(sink, enc_reg);

sink.put1(0x0F);
sink.put1(0xC8 | (enc_reg & 7));
}

Inst::Cmove {
size,
cc,
Expand Down
56 changes: 56 additions & 0 deletions cranelift/codegen/src/isa/x64/inst/emit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ impl Inst {
Inst::Setcc { cc, dst }
}

fn bswap(size: OperandSize, dst: Writable<Reg>) -> Inst {
debug_assert!(dst.to_reg().class() == RegClass::Int);
let src = Gpr::new(dst.to_reg()).unwrap();
let dst = WritableGpr::from_writable_reg(dst).unwrap();
Inst::Bswap { size, src, dst }
}

fn xmm_rm_r_imm(
op: SseOpcode,
src: RegMem,
Expand Down Expand Up @@ -3505,6 +3512,55 @@ fn test_x64_emit() {
insns.push((Inst::setcc(CC::LE, w_r14), "410F9EC6", "setle %r14b"));
insns.push((Inst::setcc(CC::P, w_r9), "410F9AC1", "setp %r9b"));
insns.push((Inst::setcc(CC::NP, w_r8), "410F9BC0", "setnp %r8b"));

// ========================================================
// Bswap
insns.push((
Inst::bswap(OperandSize::Size64, w_rax),
"480FC8",
"bswapq %rax, %rax",
));
insns.push((
Inst::bswap(OperandSize::Size64, w_r8),
"490FC8",
"bswapq %r8, %r8",
));
insns.push((
Inst::bswap(OperandSize::Size32, w_rax),
"0FC8",
"bswapl %eax, %eax",
));
insns.push((
Inst::bswap(OperandSize::Size64, w_rcx),
"480FC9",
"bswapq %rcx, %rcx",
));
insns.push((
Inst::bswap(OperandSize::Size32, w_rcx),
"0FC9",
"bswapl %ecx, %ecx",
));
insns.push((
Inst::bswap(OperandSize::Size64, w_r11),
"490FCB",
"bswapq %r11, %r11",
));
insns.push((
Inst::bswap(OperandSize::Size32, w_r11),
"410FCB",
"bswapl %r11d, %r11d",
));
insns.push((
Inst::bswap(OperandSize::Size64, w_r14),
"490FCE",
"bswapq %r14, %r14",
));
insns.push((
Inst::bswap(OperandSize::Size32, w_r14),
"410FCE",
"bswapl %r14d, %r14d",
));

// ========================================================
// Cmove
insns.push((
Expand Down
16 changes: 16 additions & 0 deletions cranelift/codegen/src/isa/x64/inst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl Inst {
Inst::AluRmiR { .. }
| Inst::AluRM { .. }
| Inst::AtomicRmwSeq { .. }
| Inst::Bswap { .. }
| Inst::CallKnown { .. }
| Inst::CallUnknown { .. }
| Inst::CheckedDivOrRemSeq { .. }
Expand Down Expand Up @@ -1373,6 +1374,17 @@ impl PrettyPrint for Inst {
format!("{} {}", ljustify2("set".to_string(), cc.to_string()), dst)
}

Inst::Bswap { size, src, dst } => {
let src = pretty_print_reg(src.to_reg(), size.to_bytes(), allocs);
let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes(), allocs);
format!(
"{} {}, {}",
ljustify2("bswap".to_string(), suffix_bwlq(*size)),
src,
dst
)
}

Inst::Cmove {
size,
cc,
Expand Down Expand Up @@ -1953,6 +1965,10 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
Inst::Setcc { dst, .. } => {
collector.reg_def(dst.to_writable_reg());
}
Inst::Bswap { src, dst, .. } => {
collector.reg_use(src.to_reg());
collector.reg_reuse_def(dst.to_writable_reg(), 0);
}
Inst::Cmove {
consequent,
alternative,
Expand Down
Loading