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

ruby-changes:73173

From: Kevin <ko1@a...>
Date: Tue, 30 Aug 2022 00:57:59 +0900 (JST)
Subject: [ruby-changes:73173] 6773832ab9 (master): More Arm64 lowering/backend work (https://github.com/Shopify/ruby/pull/307)

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

From 6773832ab9cad3c7bcb3b93ef85a4bcfc9b3a4e3 Mon Sep 17 00:00:00 2001
From: Kevin Newton <kddnewton@g...>
Date: Fri, 8 Jul 2022 13:01:21 -0400
Subject: More Arm64 lowering/backend work
 (https://github.com/Shopify/ruby/pull/307)

* More Arm64 lowering/backend work

* We now have encoding support for the LDR instruction for loading a PC-relative memory location
* You can now call add/adds/sub/subs with signed immediates, which switches appropriately based on sign
* We can now load immediates into registers appropriately, attempting to keep the minimal number of instructions:
  * If it fits into 16 bytes, we use just a single movz.
  * Else if it can be encoded into a bitmask immediate, we use a single mov.
  * Otherwise we use a movz, a movk, and then optionally another one or two movks.
* Fixed a bunch of code to do with the Op::Load opcode.
* We now handle GC-offsets properly for Op::Load by skipping around them with a jump instruction. (This will be made better by constant pools in the future.)
* Op::Lea is doing what it's supposed to do now.
* Fixed a bug in the backend tests to do with not using the result of an Op::Add.

* Fix the remaining tests for Arm64

* Move split loads logic into each backend
---
 yjit/src/asm/arm64/inst/load_literal.rs |  89 +++++++++++++++++
 yjit/src/asm/arm64/inst/mod.rs          |   2 +
 yjit/src/asm/arm64/mod.rs               | 131 ++++++++++++++++++++----
 yjit/src/asm/arm64/opnd.rs              |   1 +
 yjit/src/asm/mod.rs                     |   2 +-
 yjit/src/backend/arm64/mod.rs           | 172 +++++++++++++++++++++++++-------
 yjit/src/backend/ir.rs                  |  65 ------------
 yjit/src/backend/tests.rs               |  27 ++---
 yjit/src/backend/x86_64/mod.rs          | 105 ++++++++++++++-----
 9 files changed, 431 insertions(+), 163 deletions(-)
 create mode 100644 yjit/src/asm/arm64/inst/load_literal.rs

diff --git a/yjit/src/asm/arm64/inst/load_literal.rs b/yjit/src/asm/arm64/inst/load_literal.rs
new file mode 100644
index 0000000000..a49130c3eb
--- /dev/null
+++ b/yjit/src/asm/arm64/inst/load_literal.rs
@@ -0,0 +1,89 @@ https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/load_literal.rs#L1
+/// The size of the operands being operated on.
+enum Opc {
+    Size32 = 0b00,
+    Size64 = 0b01,
+}
+
+/// A convenience function so that we can convert the number of bits of an
+/// register operand directly into an Sf enum variant.
+impl From<u8> for Opc {
+    fn from(num_bits: u8) -> Self {
+        match num_bits {
+            64 => Opc::Size64,
+            32 => Opc::Size32,
+            _ => panic!("Invalid number of bits: {}", num_bits)
+        }
+    }
+}
+
+/// The struct that represents an A64 load literal 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 |
+/// |        0  1    1  0  0  0                                                                                     |
+/// | opc..                       imm19........................................................... rt.............. |
+/// +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
+///
+pub struct LoadLiteral {
+    /// The number of the register to load the value into.
+    rt: u8,
+
+    /// The PC-relative number of instructions to load the value from.
+    imm19: i32,
+
+    /// The size of the operands being operated on.
+    opc: Opc
+}
+
+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 {
+        Self { rt, imm19, opc: 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<LoadLiteral> for u32 {
+    /// Convert an instruction into a 32-bit value.
+    fn from(inst: LoadLiteral) -> Self {
+        let imm19 = (inst.imm19 as u32) & ((1 << 19) - 1);
+
+        0
+        | ((inst.opc as u32) << 30)
+        | (1 << 28)
+        | (FAMILY << 25)
+        | (imm19 << 5)
+        | (inst.rt as u32)
+    }
+}
+
+impl From<LoadLiteral> for [u8; 4] {
+    /// Convert an instruction into a 4 byte array.
+    fn from(inst: LoadLiteral) -> [u8; 4] {
+        let result: u32 = inst.into();
+        result.to_le_bytes()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_ldr_positive() {
+        let inst = LoadLiteral::ldr(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 result: u32 = inst.into();
+        assert_eq!(0x58ffff60, result);
+    }
+}
diff --git a/yjit/src/asm/arm64/inst/mod.rs b/yjit/src/asm/arm64/inst/mod.rs
index ae589ca564..f402f6765a 100644
--- a/yjit/src/asm/arm64/inst/mod.rs
+++ b/yjit/src/asm/arm64/inst/mod.rs
@@ -9,6 +9,7 @@ mod call; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/mod.rs#L9
 mod data_imm;
 mod data_reg;
 mod load;
+mod load_literal;
 mod logical_imm;
 mod logical_reg;
 mod mov;
@@ -24,6 +25,7 @@ pub use call::Call; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/mod.rs#L25
 pub use data_imm::DataImm;
 pub use data_reg::DataReg;
 pub use load::Load;
+pub use load_literal::LoadLiteral;
 pub use logical_imm::LogicalImm;
 pub use logical_reg::LogicalReg;
 pub use mov::Mov;
diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs
index ced8b262c5..2dc5aa9388 100644
--- a/yjit/src/asm/arm64/mod.rs
+++ b/yjit/src/asm/arm64/mod.rs
@@ -39,11 +39,21 @@ pub fn add(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L39
 
             DataReg::add(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into()
         },
-        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(imm12)) => {
+        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(uimm12)) => {
             assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size.");
-            assert!(uimm_fits_bits(imm12, 12), "The immediate operand must be 12 bits or less.");
+            assert!(uimm_fits_bits(uimm12, 12), "The immediate operand must be 12 bits or less.");
 
-            DataImm::add(rd.reg_no, rn.reg_no, imm12 as u16, rd.num_bits).into()
+            DataImm::add(rd.reg_no, rn.reg_no, uimm12 as u16, rd.num_bits).into()
+        },
+        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Imm(imm12)) => {
+            assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size.");
+            assert!(imm_fits_bits(imm12, 12), "The immediate operand must be 12 bits or less.");
+
+            if imm12 < 0 {
+                DataImm::sub(rd.reg_no, rn.reg_no, -imm12 as u16, rd.num_bits).into()
+            } else {
+                DataImm::add(rd.reg_no, rn.reg_no, imm12 as u16, rd.num_bits).into()
+            }
         },
         _ => panic!("Invalid operand combination to add instruction."),
     };
@@ -68,6 +78,16 @@ pub fn adds(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L78
 
             DataImm::adds(rd.reg_no, rn.reg_no, imm12 as u16, rd.num_bits).into()
         },
+        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Imm(imm12)) => {
+            assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size.");
+            assert!(imm_fits_bits(imm12, 12), "The immediate operand must be 12 bits or less.");
+
+            if imm12 < 0 {
+                DataImm::subs(rd.reg_no, rn.reg_no, -imm12 as u16, rd.num_bits).into()
+            } else {
+                DataImm::adds(rd.reg_no, rn.reg_no, imm12 as u16, rd.num_bits).into()
+            }
+        },
         _ => panic!("Invalid operand combination to adds instruction."),
     };
 
@@ -237,6 +257,18 @@ pub fn ldaddal(cb: &mut CodeBlock, rs: A64Opnd, rt: A64Opnd, rn: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L257
     cb.write_bytes(&bytes);
 }
 
+/// LDR - load a PC-relative memory address into a register
+pub fn ldr(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()
+        },
+        _ => panic!("Invalid operand combination to ldr instruction."),
+    };
+
+    cb.write_bytes(&bytes);
+}
+
 /// LDUR - load a memory address into a register
 pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) {
     let bytes: [u8; 4] = match (rt, rn) {
@@ -415,11 +447,21 @@ pub fn sub(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L447
 
             DataReg::sub(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into()
         },
-        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(imm12)) => {
+        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(uimm12)) => {
             assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size.");
-            assert!(uimm_fits_bits(imm12, 12), "The immediate operand must be 12 bits or less.");
+            assert!(uimm_fits_bits(uimm12, 12), "The immediate operand must be 12 bits or less.");
 
-            DataImm::sub(rd.reg_no, rn.reg_no, imm12 as u16, rd.num_bits).into()
+            DataImm::sub(rd.reg_no, rn.reg_no, uimm12 as u16, rd.num_bits).into()
+        },
+        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Imm(imm12)) => {
+            assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the  (... truncated)

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

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