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

ruby-changes:73118

From: Maxime <ko1@a...>
Date: Tue, 30 Aug 2022 00:53:34 +0900 (JST)
Subject: [ruby-changes:73118] a88fc48b3a (master): Add CCall IR insn, implement gen_swap()

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

From a88fc48b3a61b63aa1c2f4b05981e0d8726e2b9e Mon Sep 17 00:00:00 2001
From: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@s...>
Date: Tue, 24 May 2022 15:44:46 -0400
Subject: Add CCall IR insn, implement gen_swap()

---
 yjit/src/backend/ir.rs         | 58 ++++++++++++++++++++++++++++++++++++++----
 yjit/src/backend/x86_64/mod.rs | 22 +++++++++++++---
 yjit/src/codegen.rs            | 46 ++++++++++++++++++++-------------
 3 files changed, 99 insertions(+), 27 deletions(-)

diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs
index 932ba9f0be..9ed8f34c3e 100644
--- a/yjit/src/backend/ir.rs
+++ b/yjit/src/backend/ir.rs
@@ -73,6 +73,9 @@ pub enum Op https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L73
     Jnz,
     Jbe,
 
+    // C function call with N arguments (variadic)
+    CCall,
+
     /*
     // The following are conditional jump instructions. They all accept as their
     // first operand an EIR_LABEL_NAME, which is used as the target of the jump.
@@ -269,9 +272,10 @@ impl From<X86Opnd> for Opnd { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L272
 #[derive(Clone, PartialEq, Eq, Debug)]
 pub enum Target
 {
-    CodePtr(CodePtr),   // Pointer to a piece of code (e.g. side-exit)
+    CodePtr(CodePtr),   // Pointer to a piece of YJIT-generated code (e.g. side-exit)
+    FunPtr(*const u8),  // Pointer to a C function
     LabelName(String),  // A label without an index in the output
-    LabelIdx(usize),      // A label that has been indexed
+    LabelIdx(usize),    // A label that has been indexed
 }
 
 /// YJIT IR instruction
@@ -466,16 +470,24 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L470
         // Mutate the pool bitmap to indicate that the register at that index
         // has been allocated and is live.
         fn alloc_reg(pool: &mut u32, regs: &Vec<Reg>) -> Reg {
-            for index in 0..regs.len() {
+            for (index, reg) in regs.iter().enumerate() {
                 if (*pool & (1 << index)) == 0 {
                     *pool |= 1 << index;
-                    return regs[index];
+                    return *reg;
                 }
             }
 
             unreachable!("Register spill not supported");
         }
 
+        // Allocate a specific register
+        fn take_reg(pool: &mut u32, regs: &Vec<Reg>, reg: &Reg) -> Reg {
+            let reg_index = regs.iter().position(|elem| elem == reg).unwrap();
+            assert_eq!(*pool & (1 << reg_index), 0);
+            *pool |= 1 << reg_index;
+            return regs[reg_index];
+        }
+
         // Mutate the pool bitmap to indicate that the given register is being
         // returned as it is no longer used by the instruction that previously
         // held it.
@@ -510,10 +522,21 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L522
                 }
             }
 
+            // C return values need to be mapped to the C return register
+            if op == Op::CCall {
+                assert_eq!(pool, 0, "register lives past C function call");
+            }
+
             // If this instruction is used by another instruction,
             // we need to allocate a register to it
             let mut out_reg = Opnd::None;
             if live_ranges[index] != index {
+
+                // C return values need to be mapped to the C return register
+                if op == Op::CCall {
+                    out_reg = Opnd::Reg(take_reg(&mut pool, &regs, &RET_REG))
+                }
+
                 // If this instruction's first operand maps to a register and
                 // this is the last use of the register, reuse the register
                 // We do this to improve register allocation on x86
@@ -523,7 +546,7 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L546
                     if let Opnd::InsnOut(idx) = opnds[0] {
                         if live_ranges[idx] == index {
                             if let Opnd::Reg(reg) = asm.insns[idx].out {
-                                out_reg = Opnd::Reg(alloc_reg(&mut pool, &vec![reg]))
+                                out_reg = Opnd::Reg(take_reg(&mut pool, &regs, &reg))
                             }
                         }
                     }
@@ -580,6 +603,12 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L603
     {
         self.push_insn(Op::Jbe, vec![], Some(target));
     }
+
+    pub fn ccall(&mut self, fptr: *const u8, opnds: Vec<Opnd>) -> Opnd
+    {
+        let target = Target::FunPtr(fptr);
+        self.push_insn(Op::CCall, opnds, Some(target))
+    }
 }
 
 macro_rules! def_push_1_opnd {
@@ -799,4 +828,23 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L828
 
         asm.compile_with_regs(&mut cb, vec![regs[0], regs[1]]);
     }
+
+    #[test]
+    fn test_c_call()
+    {
+        extern "sysv64" fn dummy_c_fun(v0: usize, v1: usize)
+        {
+        }
+
+        let mut asm = Assembler::new();
+        let mut cb = CodeBlock::new_dummy(1024);
+        let regs = Assembler::get_scratch_regs();
+
+        asm.ccall(
+            dummy_c_fun as *const u8,
+            vec![Opnd::mem(64, SP, 0), Opnd::UImm(1)]
+        );
+
+        asm.compile_with_regs(&mut cb, vec![regs[0], regs[1]]);
+    }
 }
diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs
index 17d542c3ca..f6a901f77d 100644
--- a/yjit/src/backend/x86_64/mod.rs
+++ b/yjit/src/backend/x86_64/mod.rs
@@ -14,6 +14,9 @@ pub const CFP: Opnd = Opnd::Reg(R13_REG); https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L14
 pub const EC: Opnd = Opnd::Reg(R12_REG);
 pub const SP: Opnd = Opnd::Reg(RBX_REG);
 
+// C return value register on this platform
+pub const RET_REG: Reg = RAX_REG;
+
 /// Map Opnd to X86Opnd
 impl From<Opnd> for X86Opnd {
     fn from(opnd: Opnd) -> Self {
@@ -54,7 +57,7 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L57
     }
 
     // Emit platform-specific machine code
-    fn target_split(mut self) -> Assembler
+    fn x86_split(mut self) -> Assembler
     {
         let live_ranges: Vec<usize> = std::mem::take(&mut self.live_ranges);
 
@@ -88,7 +91,7 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L91
     }
 
     // Emit platform-specific machine code
-    pub fn target_emit(&self, cb: &mut CodeBlock)
+    pub fn x86_emit(&self, cb: &mut CodeBlock)
     {
         // For each instruction
         for insn in &self.insns {
@@ -117,6 +120,17 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L120
                 Jbe,
                 */
 
+                // C function call
+                Op::CCall => {
+                    // Temporary
+                    assert!(insn.opnds.len() < C_ARG_REGS.len());
+
+                    // For each operand
+                    for (idx, opnd) in insn.opnds.iter().enumerate() {
+                        mov(cb, C_ARG_REGS[idx], insn.opnds[idx].into());
+                    }
+                },
+
                 _ => panic!("unsupported instruction passed to x86 backend")
             };
         }
@@ -126,9 +140,9 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L140
     pub fn compile_with_regs(self, cb: &mut CodeBlock, regs: Vec<Reg>)
     {
         self
-        .target_split()
+        .x86_split()
         .split_loads()
         .alloc_regs(regs)
-        .target_emit(cb);
+        .x86_emit(cb);
     }
 }
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index 834b75192a..f9b3d513dc 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -931,7 +931,6 @@ fn gen_dup( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L931
 
 
 
-
 /*
 // duplicate stack top n elements
 fn gen_dupn(
@@ -966,7 +965,6 @@ fn gen_dupn( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L965
 }
 */
 
-
 // duplicate stack top n elements
 fn gen_dupn(
     jit: &mut JITState,
@@ -1002,16 +1000,6 @@ fn gen_dupn( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1000
     KeepCompiling
 }
 
-
-
-
-
-
-
-
-
-
-
 // Swap top 2 stack entries
 fn gen_swap(
     _jit: &mut JITState,
@@ -1031,19 +1019,23 @@ fn stack_swap( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1019
     _reg0: X86Opnd,
     _reg1: X86Opnd,
 ) {
-    let opnd0 = ctx.stack_opnd(offset0 as i32);
-    let opnd1 = ctx.stack_opnd(offset1 as i32);
+    let mut asm = Assembler::new();
+
+    let stack0_mem = ctx.ir_stack_opnd(offset0 as i32);
+    let stack1_mem = ctx.ir_stack_opnd(offset1 as i32);
 
     let mapping0 = ctx.get_opnd_mapping(StackOpnd(offset0));
     let mapping1 = ctx.get_opnd_mapping(StackOpnd(offset1));
 
-    mov(cb, REG0, opnd0);
-    mov(cb, REG1, opnd1);
-    mov(cb, opnd0, REG1);
-    mov(cb, opnd1, REG0);
+    let stack0_reg = asm.load(stack0_mem);
+    let stack1_reg = asm.load(stack1_mem);
+    asm.mov(stack0_mem, stack1_reg);
+    asm.mov(stack1_mem, stack0_reg);
 
     ctx.set_opnd_mapping(StackOpnd(offset0), mapping1);
     ctx.set_opnd_mapping(StackOpnd(offset1), mapping0);
+
+    asm.compile(cb);
 }
 
 fn gen_putnil(
@@ -1277,6 +1269,7 @@ fn gen_newarray( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1269
     KeepCompiling
 }
 
+
 // dup array
 fn gen_duparray(
     jit: &mut JITState,
@@ -1299,6 +1292,23 @@ fn gen_duparray( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1292
     KeepCompiling
 }
 
+
+
+
+/*
+let mut asm = Assembler::new();
+
+//asm.ccall(rb_ary_resurrect as *const u8, vec![ary]);
+
+asm.compile(cb);
+*/
+
+
+
+
+
+
+
 // dup hash
 fn gen_duphash(
     jit: &mut JITState,
-- 
cgit v1.2.1


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

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