ruby-changes:73156
From: Kevin <ko1@a...>
Date: Tue, 30 Aug 2022 00:57:23 +0900 (JST)
Subject: [ruby-changes:73156] 27dd43bbc5 (master): TST, CMP, AND/ANDS with registers (https://github.com/Shopify/ruby/pull/301)
https://git.ruby-lang.org/ruby.git/commit/?id=27dd43bbc5 From 27dd43bbc52eb2040d46370fb0170d4d420223e1 Mon Sep 17 00:00:00 2001 From: Kevin Newton <kddnewton@g...> Date: Fri, 17 Jun 2022 17:26:13 -0400 Subject: TST, CMP, AND/ANDS with registers (https://github.com/Shopify/ruby/pull/301) * Add TST instruction and AND/ANDS entrypoints for immediates * TST/AND/ANDS for registers * CMP instruction --- yjit/src/asm/arm64/inst/data_imm.rs | 13 ++++ yjit/src/asm/arm64/inst/data_reg.rs | 13 ++++ yjit/src/asm/arm64/inst/logical_imm.rs | 13 ++++ yjit/src/asm/arm64/inst/logical_reg.rs | 125 ++++++++++++++++++++++++++++++++ yjit/src/asm/arm64/inst/mod.rs | 126 +++++++++++++++++++++++++++++++++ 5 files changed, 290 insertions(+) create mode 100644 yjit/src/asm/arm64/inst/logical_reg.rs diff --git a/yjit/src/asm/arm64/inst/data_imm.rs b/yjit/src/asm/arm64/inst/data_imm.rs index 0d0a6ff325..950cf3421e 100644 --- a/yjit/src/asm/arm64/inst/data_imm.rs +++ b/yjit/src/asm/arm64/inst/data_imm.rs @@ -80,6 +80,12 @@ impl DataImm { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/data_imm.rs#L80 } } + /// CMP (immediate) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/CMP--immediate---Compare--immediate---an-alias-of-SUBS--immediate--?lang=en + pub fn cmp(rn: u8, imm12: u16, num_bits: u8) -> Self { + Self::subs(31, rn, imm12, num_bits) + } + /// SUB (immediate) /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/SUB--immediate---Subtract--immediate--?lang=en pub fn sub(rd: u8, rn: u8, imm12: u16, num_bits: u8) -> Self { @@ -156,6 +162,13 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/data_imm.rs#L162 assert_eq!(0xb1001c20, result); } + #[test] + fn test_cmp() { + let inst = DataImm::cmp(0, 7, 64); + let result: u32 = inst.into(); + assert_eq!(0xf1001c1f, result); + } + #[test] fn test_sub() { let inst = DataImm::sub(0, 1, 7, 64); diff --git a/yjit/src/asm/arm64/inst/data_reg.rs b/yjit/src/asm/arm64/inst/data_reg.rs index 8635ab804b..40f026d1fd 100644 --- a/yjit/src/asm/arm64/inst/data_reg.rs +++ b/yjit/src/asm/arm64/inst/data_reg.rs @@ -86,6 +86,12 @@ impl DataReg { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/data_reg.rs#L86 } } + /// CMP (shifted register) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/CMP--shifted-register---Compare--shifted-register---an-alias-of-SUBS--shifted-register--?lang=en + pub fn cmp(rn: u8, rm: u8, num_bits: u8) -> Self { + Self::subs(31, rn, rm, num_bits) + } + /// SUB (shifted register) /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/SUB--shifted-register---Subtract--shifted-register--?lang=en pub fn sub(rd: u8, rn: u8, rm: u8, num_bits: u8) -> Self { @@ -165,6 +171,13 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/data_reg.rs#L171 assert_eq!(0xab020020, result); } + #[test] + fn test_cmp() { + let inst = DataReg::cmp(0, 1, 64); + let result: u32 = inst.into(); + assert_eq!(0xeb01001f, result); + } + #[test] fn test_sub() { let inst = DataReg::sub(0, 1, 2, 64); diff --git a/yjit/src/asm/arm64/inst/logical_imm.rs b/yjit/src/asm/arm64/inst/logical_imm.rs index 63a4556d85..88de8ba4a1 100644 --- a/yjit/src/asm/arm64/inst/logical_imm.rs +++ b/yjit/src/asm/arm64/inst/logical_imm.rs @@ -49,6 +49,12 @@ impl LogicalImm { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/logical_imm.rs#L49 pub fn ands(rd: u8, rn: u8, imm: BitmaskImmediate, num_bits: u8) -> Self { Self { rd, rn, imm, opc: Opc::Ands, sf: num_bits.into() } } + + /// TST (immediate) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/TST--immediate---Test-bits--immediate---an-alias-of-ANDS--immediate--?lang=en + pub fn tst(rn: u8, imm: BitmaskImmediate, num_bits: u8) -> Self { + Self::ands(31, rn, imm, num_bits) + } } /// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Data-Processing----Immediate?lang=en#log_imm @@ -94,4 +100,11 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/logical_imm.rs#L100 let result: u32 = inst.into(); assert_eq!(0xf2400820, result); } + + #[test] + fn test_tst() { + let inst = LogicalImm::tst(1, 7.try_into().unwrap(), 64); + let result: u32 = inst.into(); + assert_eq!(0xf240083f, result); + } } diff --git a/yjit/src/asm/arm64/inst/logical_reg.rs b/yjit/src/asm/arm64/inst/logical_reg.rs new file mode 100644 index 0000000000..929d80b1a7 --- /dev/null +++ b/yjit/src/asm/arm64/inst/logical_reg.rs @@ -0,0 +1,125 @@ https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/logical_reg.rs#L1 +use super::sf::Sf; + +/// The type of shift to perform on the second operand register. +enum Shift { + LSL = 0b00, // logical shift left (unsigned) + LSR = 0b01, // logical shift right (unsigned) + ASR = 0b10, // arithmetic shift right (signed) + ROR = 0b11 // rotate right (unsigned) +} + +// Which operation to perform. +enum Opc { + /// The AND operation. + And = 0b00, + + /// The ANDS operation. + Ands = 0b11 +} + +/// The struct that represents an A64 logical register instruction that can be +/// encoded. +/// +/// AND/ANDS (shifted register) +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// | 31 30 29 28 | 27 26 25 24 | 23 22 21 20 | 19 18 17 16 | 15 14 13 12 | 11 10 09 08 | 07 06 05 04 | 03 02 01 00 | +/// | 0 1 0 1 0 0 | +/// | sf opc.. shift rm.............. imm6............... rn.............. rd.............. | +/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +/// +pub struct LogicalReg { + /// The register number of the destination register. + rd: u8, + + /// The register number of the first operand register. + rn: u8, + + /// The amount to shift the second operand register. + imm6: u8, + + /// The register number of the second operand register. + rm: u8, + + /// The type of shift to perform on the second operand register. + shift: Shift, + + /// The opcode for this instruction. + opc: Opc, + + /// Whether or not this instruction is operating on 64-bit operands. + sf: Sf +} + +impl LogicalReg { + /// AND (shifted register) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/AND--shifted-register---Bitwise-AND--shifted-register--?lang=en + pub fn and(rd: u8, rn: u8, rm: u8, num_bits: u8) -> Self { + Self { rd, rn, imm6: 0, rm, shift: Shift::LSL, opc: Opc::And, sf: num_bits.into() } + } + + /// ANDS (shifted register) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/ANDS--shifted-register---Bitwise-AND--shifted-register---setting-flags-?lang=en + pub fn ands(rd: u8, rn: u8, rm: u8, num_bits: u8) -> Self { + Self { rd, rn, imm6: 0, rm, shift: Shift::LSL, opc: Opc::Ands, sf: num_bits.into() } + } + + /// TST (shifted register) + /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/TST--shifted-register---Test--shifted-register---an-alias-of-ANDS--shifted-register--?lang=en + pub fn tst(rn: u8, rm: u8, num_bits: u8) -> Self { + Self { rd: 31, rn, imm6: 0, rm, shift: Shift::LSL, opc: Opc::Ands, sf: num_bits.into() } + } +} + +/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Data-Processing----Register?lang=en +const FAMILY: u32 = 0b0101; + +impl From<LogicalReg> for u32 { + /// Convert an instruction into a 32-bit value. + fn from(inst: LogicalReg) -> Self { + let imm6 = (inst.imm6 as u32) & ((1 << 6) - 1); + + 0 + | ((inst.sf as u32) << 31) + | ((inst.opc as u32) << 29) + | (FAMILY << 25) + | ((inst.shift as u32) << 22) + | ((inst.rm as u32) << 16) + | (imm6 << 10) + | ((inst.rn as u32) << 5) + | inst.rd as u32 + } +} + +impl From<LogicalReg> for [u8; 4] { + /// Convert an instruction into a 4 byte array. + fn from(inst: LogicalReg) -> [u8; 4] { + let result: u32 = inst.into(); + result.to_le_bytes() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_and() { + let inst = LogicalReg::and(0, 1, 2, 64); + let result: u32 = inst.into(); + assert_eq!(0x8a020020, result); + } + + #[test] + fn test_ands() { + let inst = LogicalReg::ands(0, 1, 2, 64); + let result: u32 = inst.into(); + assert_eq!(0xea020020, result); + } + + #[test] + fn test_tst() { + let inst = LogicalReg::tst(0, 1, 64); + let result: u32 = inst.into(); + assert_eq!(0xea01001f, result); + } +} diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs index 83cdd26d1d..7d05f28604 100644 --- a/yjit/src/asm/arm64/inst/mod.rs +++ b/yjit/src/asm/arm64/inst/mod.rs @@ -6,6 +6,7 @@ mod data_imm; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/mod.rs#L6 mod data_reg; mod load; mod logical_imm; +mod logical_reg; mod mov; mod sf; mod store; @@ -18,6 +19,8 @@ use call::Call; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/mod.rs#L19 use data_imm::DataImm; use data_reg::DataReg; use load::Load; +use logical_imm::LogicalImm; +use logical_reg::LogicalReg; use mov::Mov; use store::Store; @@ -85,6 +88,50 @@ pub fn adds(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { https://github.com/ruby/ruby (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/