[前][次][番号順一覧][スレッド一覧]

ruby-changes:73268

From: Kevin <ko1@a...>
Date: Tue, 30 Aug 2022 01:05:16 +0900 (JST)
Subject: [ruby-changes:73268] b8846dd2f8 (master): Load mem displacement when necessary on AArch64 (https://github.com/Shopify/ruby/pull/382)

https://git.ruby-lang.org/ruby.git/commit/?id=b8846dd2f8

From b8846dd2f8042fc13a0f5ae17e2e2a6f400074dd Mon Sep 17 00:00:00 2001
From: Kevin Newton <kddnewton@g...>
Date: Tue, 9 Aug 2022 10:27:21 -0400
Subject: Load mem displacement when necessary on AArch64
 (https://github.com/Shopify/ruby/pull/382)

* LDR instruction for AArch64

* Split loads in arm64_split when memory address displacements do not fit
---
 yjit/src/asm/arm64/inst/load_literal.rs  |   6 +-
 yjit/src/asm/arm64/inst/load_register.rs | 108 ++++++++++++++++++++++
 yjit/src/asm/arm64/inst/mod.rs           |   2 +
 yjit/src/asm/arm64/mod.rs                |  46 ++++++++--
 yjit/src/backend/arm64/mod.rs            | 152 +++++++++++++++++++++++--------
 5 files changed, 261 insertions(+), 53 deletions(-)
 create mode 100644 yjit/src/asm/arm64/inst/load_register.rs

diff --git a/yjit/src/asm/arm64/inst/load_literal.rs b/yjit/src/asm/arm64/inst/load_literal.rs
index a49130c3eb..d2a5d57eea 100644
--- a/yjit/src/asm/arm64/inst/load_literal.rs
+++ b/yjit/src/asm/arm64/inst/load_literal.rs
@@ -39,7 +39,7 @@ pub struct LoadLiteral { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/load_literal.rs#L39
 impl LoadLiteral {
     /// LDR (load literal)
     /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDR--literal---Load-Register--literal--?lang=en
-    pub fn ldr(rt: u8, imm19: i32, num_bits: u8) -> Self {
+    pub fn ldr_literal(rt: u8, imm19: i32, num_bits: u8) -> Self {
         Self { rt, imm19, opc: num_bits.into() }
     }
 }
@@ -75,14 +75,14 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/load_literal.rs#L75
 
     #[test]
     fn test_ldr_positive() {
-        let inst = LoadLiteral::ldr(0, 5, 64);
+        let inst = LoadLiteral::ldr_literal(0, 5, 64);
         let result: u32 = inst.into();
         assert_eq!(0x580000a0, result);
     }
 
     #[test]
     fn test_ldr_negative() {
-        let inst = LoadLiteral::ldr(0, -5, 64);
+        let inst = LoadLiteral::ldr_literal(0, -5, 64);
         let result: u32 = inst.into();
         assert_eq!(0x58ffff60, result);
     }
diff --git a/yjit/src/asm/arm64/inst/load_register.rs b/yjit/src/asm/arm64/inst/load_register.rs
new file mode 100644
index 0000000000..3426b9ba5f
--- /dev/null
+++ b/yjit/src/asm/arm64/inst/load_register.rs
@@ -0,0 +1,108 @@ https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/load_register.rs#L1
+/// Whether or not to shift the register.
+enum S {
+    Shift = 1,
+    NoShift = 0
+}
+
+/// The option for this instruction.
+enum Option {
+    UXTW = 0b010,
+    LSL = 0b011,
+    SXTW = 0b110,
+    SXTX = 0b111
+}
+
+/// The size of the operands of this instruction.
+enum Size {
+    Size32 = 0b10,
+    Size64 = 0b11
+}
+
+/// A convenience function so that we can convert the number of bits of an
+/// register operand directly into a Size enum variant.
+impl From<u8> for Size {
+    fn from(num_bits: u8) -> Self {
+        match num_bits {
+            64 => Size::Size64,
+            32 => Size::Size32,
+            _ => panic!("Invalid number of bits: {}", num_bits)
+        }
+    }
+}
+
+/// The struct that represents an A64 load instruction that can be encoded.
+///
+/// LDR
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+/// | 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 |
+/// |        1  1    1  0  0  0    0  1  1                                   1  0                                   |
+/// | size.                                rm..............   option.. S          rn.............. rt.............. |
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+///
+pub struct LoadRegister {
+    /// The number of the register to load the value into.
+    rt: u8,
+
+    /// The base register with which to form the address.
+    rn: u8,
+
+    /// Whether or not to shift the value of the register.
+    s: S,
+
+    /// The option associated with this instruction that controls the shift.
+    option: Option,
+
+    /// The number of the offset register.
+    rm: u8,
+
+    /// The size of the operands.
+    size: Size
+}
+
+impl LoadRegister {
+    /// LDR
+    /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDR--register---Load-Register--register--?lang=en
+    pub fn ldr(rt: u8, rn: u8, rm: u8, num_bits: u8) -> Self {
+        Self { rt, rn, s: S::NoShift, option: Option::LSL, rm, size: num_bits.into() }
+    }
+}
+
+/// https://developer.arm.com/documentation/ddi0602/2022-03/Index-by-Encoding/Loads-and-Stores?lang=en
+const FAMILY: u32 = 0b0100;
+
+impl From<LoadRegister> for u32 {
+    /// Convert an instruction into a 32-bit value.
+    fn from(inst: LoadRegister) -> Self {
+        0
+        | ((inst.size as u32) << 30)
+        | (0b11 << 28)
+        | (FAMILY << 25)
+        | (0b11 << 21)
+        | ((inst.rm as u32) << 16)
+        | ((inst.option as u32) << 13)
+        | ((inst.s as u32) << 12)
+        | (0b10 << 10)
+        | ((inst.rn as u32) << 5)
+        | (inst.rt as u32)
+    }
+}
+
+impl From<LoadRegister> for [u8; 4] {
+    /// Convert an instruction into a 4 byte array.
+    fn from(inst: LoadRegister) -> [u8; 4] {
+        let result: u32 = inst.into();
+        result.to_le_bytes()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_ldr() {
+        let inst = LoadRegister::ldr(0, 1, 2, 64);
+        let result: u32 = inst.into();
+        assert_eq!(0xf8626820, result);
+    }
+}
diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs
index 42df2d137a..ab41464013 100644
--- a/yjit/src/asm/arm64/inst/mod.rs
+++ b/yjit/src/asm/arm64/inst/mod.rs
@@ -10,6 +10,7 @@ mod conditional; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/mod.rs#L10
 mod data_imm;
 mod data_reg;
 mod load_literal;
+mod load_register;
 mod load_store;
 mod logical_imm;
 mod logical_reg;
@@ -30,6 +31,7 @@ pub use conditional::Conditional; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/mod.rs#L31
 pub use data_imm::DataImm;
 pub use data_reg::DataReg;
 pub use load_literal::LoadLiteral;
+pub use load_register::LoadRegister;
 pub use load_store::LoadStore;
 pub use logical_imm::LogicalImm;
 pub use logical_reg::LogicalReg;
diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs
index 68be36c256..93b44dba4b 100644
--- a/yjit/src/asm/arm64/mod.rs
+++ b/yjit/src/asm/arm64/mod.rs
@@ -374,11 +374,26 @@ pub fn ldp_post(cb: &mut CodeBlock, rt1: A64Opnd, rt2: A64Opnd, rn: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L374
     cb.write_bytes(&bytes);
 }
 
+/// LDR - load a memory address into a register with a register offset
+pub fn ldr(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd, rm: A64Opnd) {
+    let bytes: [u8; 4] = match (rt, rn, rm) {
+        (A64Opnd::Reg(rt), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => {
+            assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size");
+            assert!(rn.num_bits == rm.num_bits, "Expected registers to be the same size");
+
+            LoadRegister::ldr(rt.reg_no, rn.reg_no, rm.reg_no, rt.num_bits).into()
+        },
+        _ => panic!("Invalid operand combination to ldr instruction.")
+    };
+
+    cb.write_bytes(&bytes);
+}
+
 /// LDR - load a PC-relative memory address into a register
-pub fn ldr(cb: &mut CodeBlock, rt: A64Opnd, rn: i32) {
+pub fn ldr_literal(cb: &mut CodeBlock, rt: A64Opnd, rn: i32) {
     let bytes: [u8; 4] = match rt {
         A64Opnd::Reg(rt) => {
-            LoadLiteral::ldr(rt.reg_no, rn, rt.num_bits).into()
+            LoadLiteral::ldr_literal(rt.reg_no, rn, rt.num_bits).into()
         },
         _ => panic!("Invalid operand combination to ldr instruction."),
     };
@@ -386,12 +401,18 @@ pub fn ldr(cb: &mut CodeBlock, rt: A64Opnd, rn: i32) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L401
     cb.write_bytes(&bytes);
 }
 
+/// Whether or not a memory address displacement fits into the maximum number of
+/// bits such that it can be used without loading it into a register first.
+pub fn mem_disp_fits_bits(disp: i32) -> bool {
+    imm_fits_bits(disp.into(), 9)
+}
+
 /// LDR (post-index) - load a register from memory, update the base pointer after loading it
 pub fn ldr_post(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) {
     let bytes: [u8; 4] = match (rt, rn) {
         (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => {
             assert!(rt.num_bits == rn.num_bits, "All operands must be of the same size.");
-            assert!(imm_fits_bits(rn.disp.into(), 9), "The displacement must be 9 bits or less.");
+            assert!(mem_disp_fits_bits(rn.disp), "The displacement must be 9 bits or less.");
 
             LoadStore::ldr_post(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into()
         },
@@ -406,7 +427,7 @@ pub fn ldr_pre(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L427
     let bytes: [u8; 4] = match (rt, rn) {
         (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => {
             assert!(rt.num_bits == rn.num_bits, "All operands must be of the same size.");
-            assert!(imm_fits_bits(rn.disp.into(), 9), "The displacement must be 9 bits or less.");
+            assert!(mem_disp_fits_bits(rn.disp), "The displacement must be 9 bits or less.");
 
             LoadStore::ldr_pre(rt.reg_no, rn.base_reg_no, rn.disp as i16, rt.num_bits).into()
         },
@@ -426,7 +447,7 @@ pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L447
         },
         (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => {
             assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size");
-            assert!(imm_fits_bits(rn.di (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]