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

ruby-changes:73258

From: Kevin <ko1@a...>
Date: Tue, 30 Aug 2022 01:05:14 +0900 (JST)
Subject: [ruby-changes:73258] 8278d72290 (master): Left and right shift for IR (https://github.com/Shopify/ruby/pull/374)

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

From 8278d722907dc134e9a3436d5542d7dc168d8925 Mon Sep 17 00:00:00 2001
From: Kevin Newton <kddnewton@g...>
Date: Fri, 5 Aug 2022 16:52:23 -0400
Subject: Left and right shift for IR
 (https://github.com/Shopify/ruby/pull/374)

* Left and right shift for IR

* Update yjit/src/backend/x86_64/mod.rs

Co-authored-by: Alan Wu <XrXr@u...>
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@g...>
---
 yjit/src/asm/arm64/inst/sbfm.rs | 33 +++++++++++++++++++++++++++++--
 yjit/src/asm/arm64/mod.rs       | 21 ++++++++++++++++++++
 yjit/src/backend/arm64/mod.rs   | 36 ++++++++++++++++++++++++++++++++++
 yjit/src/backend/ir.rs          | 12 ++++++++++++
 yjit/src/backend/x86_64/mod.rs  | 43 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 143 insertions(+), 2 deletions(-)

diff --git a/yjit/src/asm/arm64/inst/sbfm.rs b/yjit/src/asm/arm64/inst/sbfm.rs
index 4fbb567ed0..6f69e58043 100644
--- a/yjit/src/asm/arm64/inst/sbfm.rs
+++ b/yjit/src/asm/arm64/inst/sbfm.rs
@@ -31,6 +31,18 @@ pub struct SBFM { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/sbfm.rs#L31
 }
 
 impl SBFM {
+    /// ASR
+    /// https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/ASR--immediate---Arithmetic-Shift-Right--immediate---an-alias-of-SBFM-?lang=en
+    pub fn asr(rd: u8, rn: u8, shift: u8, num_bits: u8) -> Self {
+        let (imms, n) = if num_bits == 64 {
+            (0b111111, true)
+        } else {
+            (0b011111, false)
+        };
+
+        Self { rd, rn, immr: shift, imms, n, sf: num_bits.into() }
+    }
+
     /// SXTW
     /// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/SXTW--Sign-Extend-Word--an-alias-of-SBFM-?lang=en
     pub fn sxtw(rd: u8, rn: u8) -> Self {
@@ -44,13 +56,16 @@ const FAMILY: u32 = 0b1001; https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/sbfm.rs#L56
 impl From<SBFM> for u32 {
     /// Convert an instruction into a 32-bit value.
     fn from(inst: SBFM) -> Self {
+        let immr = (inst.immr as u32) & ((1 << 6) - 1);
+        let imms = (inst.imms as u32) & ((1 << 6) - 1);
+
         0
         | ((inst.sf as u32) << 31)
         | (FAMILY << 25)
         | (1 << 24)
         | ((inst.n as u32) << 22)
-        | ((inst.immr as u32) << 16)
-        | ((inst.imms as u32) << 10)
+        | (immr << 16)
+        | (imms << 10)
         | ((inst.rn as u32) << 5)
         | inst.rd as u32
     }
@@ -68,6 +83,20 @@ impl From<SBFM> for [u8; 4] { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/inst/sbfm.rs#L83
 mod tests {
     use super::*;
 
+    #[test]
+    fn test_asr_32_bits() {
+        let inst = SBFM::asr(0, 1, 2, 32);
+        let result: u32 = inst.into();
+        assert_eq!(0x13027c20, result);
+    }
+
+    #[test]
+    fn test_asr_64_bits() {
+        let inst = SBFM::asr(10, 11, 5, 64);
+        let result: u32 = inst.into();
+        assert_eq!(0x9345fd6a, result);
+    }
+
     #[test]
     fn test_sxtw() {
         let inst = SBFM::sxtw(0, 1);
diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs
index d114f64a22..68be36c256 100644
--- a/yjit/src/asm/arm64/mod.rs
+++ b/yjit/src/asm/arm64/mod.rs
@@ -166,6 +166,22 @@ pub fn ands(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L166
     cb.write_bytes(&bytes);
 }
 
+/// ASR - arithmetic shift right rn by shift, put the result in rd, don't update
+/// flags
+pub fn asr(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, shift: A64Opnd) {
+    let bytes: [u8; 4] = match (rd, rn, shift) {
+        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(shift)) => {
+            assert!(rd.num_bits == rn.num_bits, "rd and rn must be of the same size.");
+            assert!(uimm_fits_bits(shift, 6), "The shift operand must be 6 bits or less.");
+
+            SBFM::asr(rd.reg_no, rn.reg_no, shift.try_into().unwrap(), rd.num_bits).into()
+        },
+        _ => panic!("Invalid operand combination to asr instruction."),
+    };
+
+    cb.write_bytes(&bytes);
+}
+
 /// Whether or not the offset between two instructions fits into the branch with
 /// or without link instruction. If it doesn't, then we have to load the value
 /// into a register first.
@@ -903,6 +919,11 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/arm64/mod.rs#L919
         check_bytes("200840f2", |cb| ands(cb, X0, X1, A64Opnd::new_uimm(7)));
     }
 
+    #[test]
+    fn test_asr() {
+        check_bytes("b4fe4a93", |cb| asr(cb, X20, X21, A64Opnd::new_uimm(10)));
+    }
+
     #[test]
     fn test_bcond() {
         check_bytes("01200054", |cb| bcond(cb, Condition::NE, A64Opnd::new_imm(0x400)));
diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs
index 2cddf55756..2e8c2068af 100644
--- a/yjit/src/backend/arm64/mod.rs
+++ b/yjit/src/backend/arm64/mod.rs
@@ -576,6 +576,15 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/arm64/mod.rs#L576
                 Op::Not => {
                     mvn(cb, insn.out.into(), insn.opnds[0].into());
                 },
+                Op::RShift => {
+                    asr(cb, insn.out.into(), insn.opnds[0].into(), insn.opnds[1].into());
+                },
+                Op::URShift => {
+                    lsr(cb, insn.out.into(), insn.opnds[0].into(), insn.opnds[1].into());
+                },
+                Op::LShift => {
+                    lsl(cb, insn.out.into(), insn.opnds[0].into(), insn.opnds[1].into());
+                },
                 Op::Store => {
                     // This order may be surprising but it is correct. The way
                     // the Arm64 assembler works, the register that is going to
@@ -901,6 +910,33 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/arm64/mod.rs#L910
         asm.compile_with_num_regs(&mut cb, 1);
     }
 
+    #[test]
+    fn test_emit_lshift() {
+        let (mut asm, mut cb) = setup_asm();
+
+        let opnd = asm.lshift(Opnd::Reg(X0_REG), Opnd::UImm(5));
+        asm.store(Opnd::mem(64, Opnd::Reg(X2_REG), 0), opnd);
+        asm.compile_with_num_regs(&mut cb, 1);
+    }
+
+    #[test]
+    fn test_emit_rshift() {
+        let (mut asm, mut cb) = setup_asm();
+
+        let opnd = asm.rshift(Opnd::Reg(X0_REG), Opnd::UImm(5));
+        asm.store(Opnd::mem(64, Opnd::Reg(X2_REG), 0), opnd);
+        asm.compile_with_num_regs(&mut cb, 1);
+    }
+
+    #[test]
+    fn test_emit_urshift() {
+        let (mut asm, mut cb) = setup_asm();
+
+        let opnd = asm.urshift(Opnd::Reg(X0_REG), Opnd::UImm(5));
+        asm.store(Opnd::mem(64, Opnd::Reg(X2_REG), 0), opnd);
+        asm.compile_with_num_regs(&mut cb, 1);
+    }
+
     #[test]
     fn test_emit_test() {
         let (mut asm, mut cb) = setup_asm();
diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs
index a23b27dda2..5eee61b228 100644
--- a/yjit/src/backend/ir.rs
+++ b/yjit/src/backend/ir.rs
@@ -66,6 +66,15 @@ pub enum Op https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L66
     // instruction.
     Not,
 
+    /// Shift a value right by a certain amount (signed).
+    RShift,
+
+    /// Shift a value right by a certain amount (unsigned).
+    URShift,
+
+    /// Shift a value left by a certain amount.
+    LShift,
+
     //
     // Low-level instructions
     //
@@ -912,6 +921,9 @@ def_push_2_opnd!(sub, Op::Sub); https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L921
 def_push_2_opnd!(and, Op::And);
 def_push_2_opnd!(or, Op::Or);
 def_push_1_opnd!(not, Op::Not);
+def_push_2_opnd!(lshift, Op::LShift);
+def_push_2_opnd!(rshift, Op::RShift);
+def_push_2_opnd!(urshift, Op::URShift);
 def_push_1_opnd_no_out!(cpush, Op::CPush);
 def_push_0_opnd!(cpop, Op::CPop);
 def_push_1_opnd_no_out!(cpop_into, Op::CPopInto);
diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs
index 8d45230e91..4ba849b239 100644
--- a/yjit/src/backend/x86_64/mod.rs
+++ b/yjit/src/backend/x86_64/mod.rs
@@ -164,6 +164,37 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L164
 
                     asm.push_insn(op, vec![opnd0, opnd1], target, text, pos_marker);
                 },
+                // These instructions modify their input operand in-place, so we
+                // may need to load the input value to preserve it
+                Op::LShift | Op::RShift | Op::URShift => {
+                    let (opnd0, opnd1) = match (opnds[0], opnds[1]) {
+                        // Instruction output whose live range spans beyond this instruction
+                        (Opnd::InsnOut { .. }, _) => {
+                            let idx = match original_opnds[0] {
+                                Opnd::InsnOut { idx, .. } => {
+                                    idx
+                                },
+                                _ => unreachable!()
+                            };
+
+                            // Our input must be from a previous instruction!
+                            assert!(idx < index);
+
+                            if live_ranges[idx] > index {
+                                (asm.load(opnds[0]), opnds[1])
+                            } else {
+                                (opnds[0], opnds[1])
+                            }
+                        },
+                        // We have to load memory operands to avoid corrupting them
+                        (Opnd::Mem(_) | Opnd::Reg(_), _) => {
+                            (asm.load(opnds[0]), opnds[1])
+                        },
+                        _ => (opnds[0], opnds[1])
+                    };
+
+                    asm.push_insn(op, vec![opnd0, opnd1], target, text, pos_marker);
+                },
                 Op::CSelZ | Op::CSelNZ | Op::CSelE | Op::CSelNE |
                 Op::CSelL | Op::CSelLE | Op::CSelG | Op::CSelGE => {
                     let ne (... truncated)

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

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