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, ®s, &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, ®s, ®)) } } } @@ -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/