ruby-changes:73244
From: Takashi <ko1@a...>
Date: Tue, 30 Aug 2022 01:04:58 +0900 (JST)
Subject: [ruby-changes:73244] fe172aac04 (master): Convert getinstancevariable to new backend IR (https://github.com/Shopify/ruby/pull/352)
https://git.ruby-lang.org/ruby.git/commit/?id=fe172aac04 From fe172aac0465160ec5a02c687ab1dc6ade2c090a Mon Sep 17 00:00:00 2001 From: Takashi Kokubun <takashikkbn@g...> Date: Thu, 4 Aug 2022 09:02:09 -0700 Subject: Convert getinstancevariable to new backend IR (https://github.com/Shopify/ruby/pull/352) * Convert getinstancevariable to new backend IR * Support mem-based mem * Use more into() * Add tests for getivar * Just load obj_opnd to a register * Apply another into() * Flip the nil-out condition * Fix duplicated counts of side_exit --- yjit/src/backend/ir.rs | 9 +- yjit/src/codegen.rs | 315 ++++++++++++++++++++++++------------------------- 2 files changed, 160 insertions(+), 164 deletions(-) diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 99a084ff02..41842c9704 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -340,7 +340,14 @@ impl Target https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L340 pub fn unwrap_label_idx(&self) -> usize { match self { Target::Label(idx) => *idx, - _ => unreachable!() + _ => unreachable!("trying to unwrap {:?} into label", self) + } + } + + pub fn unwrap_code_ptr(&self) -> CodePtr { + match self { + Target::CodePtr(ptr) => *ptr, + _ => unreachable!("trying to unwrap {:?} into code ptr", self) } } } diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index ef7ccf9a65..f08b073e34 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -213,7 +213,7 @@ macro_rules! gen_counter_incr { https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L213 let counter_opnd = Opnd::mem(64, ptr_reg, 0); // Increment and store the updated value - $asm.incr_counter(counter_opnd, 1.into()); + $asm.incr_counter(counter_opnd, Opnd::UImm(1)); } }; } @@ -236,11 +236,14 @@ macro_rules! counted_exit { https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L236 let ocb = $ocb.unwrap(); let code_ptr = ocb.get_write_ptr(); + let mut ocb_asm = Assembler::new(); + // Increment the counter - gen_counter_incr!(ocb, $counter_name); + gen_counter_incr!(ocb_asm, $counter_name); // Jump to the existing side exit - jmp_ptr(ocb, $existing_side_exit); + ocb_asm.jmp($existing_side_exit.into()); + ocb_asm.compile(ocb); // Pointer to the side-exit code code_ptr @@ -1805,40 +1808,39 @@ fn gen_checkkeyword( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1808 KeepCompiling } -/* fn gen_jnz_to_target0( - cb: &mut CodeBlock, + asm: &mut Assembler, target0: CodePtr, _target1: Option<CodePtr>, shape: BranchShape, ) { match shape { BranchShape::Next0 | BranchShape::Next1 => unreachable!(), - BranchShape::Default => jnz_ptr(cb, target0), + BranchShape::Default => asm.jnz(target0.into()), } } fn gen_jz_to_target0( - cb: &mut CodeBlock, + asm: &mut Assembler, target0: CodePtr, _target1: Option<CodePtr>, shape: BranchShape, ) { match shape { BranchShape::Next0 | BranchShape::Next1 => unreachable!(), - BranchShape::Default => jz_ptr(cb, target0), + BranchShape::Default => asm.jz(Target::CodePtr(target0)), } } fn gen_jbe_to_target0( - cb: &mut CodeBlock, + asm: &mut Assembler, target0: CodePtr, _target1: Option<CodePtr>, shape: BranchShape, ) { match shape { BranchShape::Next0 | BranchShape::Next1 => unreachable!(), - BranchShape::Default => jbe_ptr(cb, target0), + BranchShape::Default => asm.jbe(Target::CodePtr(target0)), } } @@ -1848,7 +1850,7 @@ fn jit_chain_guard( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1850 jcc: JCCKinds, jit: &JITState, ctx: &Context, - cb: &mut CodeBlock, + asm: &mut Assembler, ocb: &mut OutlinedCb, depth_limit: i32, side_exit: CodePtr, @@ -1867,15 +1869,16 @@ fn jit_chain_guard( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1869 idx: jit.insn_idx, }; - gen_branch(jit, ctx, cb, ocb, bid, &deeper, None, None, target0_gen_fn); + gen_branch(jit, ctx, asm, ocb, bid, &deeper, None, None, target0_gen_fn); } else { - target0_gen_fn(cb, side_exit, None, BranchShape::Default); + target0_gen_fn(asm, side_exit, None, BranchShape::Default); } } // up to 5 different classes, and embedded or not for each pub const GET_IVAR_MAX_DEPTH: i32 = 10; +/* // hashes and arrays pub const OPT_AREF_MAX_CHAIN_DEPTH: i32 = 2; @@ -1921,26 +1924,33 @@ fn gen_set_ivar( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1924 KeepCompiling } -/* + + // Codegen for getting an instance variable. // Preconditions: -// - receiver is in REG0 // - receiver has the same class as CLASS_OF(comptime_receiver) // - no stack push or pops to ctx since the entry to the codegen of the instruction being compiled fn gen_get_ivar( jit: &mut JITState, ctx: &mut Context, - cb: &mut CodeBlock, + asm: &mut Assembler, ocb: &mut OutlinedCb, max_chain_depth: i32, comptime_receiver: VALUE, ivar_name: ID, - reg0_opnd: InsnOpnd, + recv: Opnd, + recv_opnd: InsnOpnd, side_exit: CodePtr, ) -> CodegenStatus { let comptime_val_klass = comptime_receiver.class_of(); let starting_context = *ctx; // make a copy for use with jit_chain_guard + // If recv isn't already a register, load it. + let recv = match recv { + Opnd::Reg(_) => recv, + _ => asm.load(recv), + }; + // Check if the comptime class uses a custom allocator let custom_allocator = unsafe { rb_get_alloc_func(comptime_val_klass) }; let uses_custom_allocator = match custom_allocator { @@ -1961,46 +1971,26 @@ fn gen_get_ivar( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1971 if !receiver_t_object || uses_custom_allocator { // General case. Call rb_ivar_get(). // VALUE rb_ivar_get(VALUE obj, ID id) - add_comment(cb, "call rb_ivar_get()"); + asm.comment("call rb_ivar_get()"); // The function could raise exceptions. - jit_prepare_routine_call(jit, ctx, cb, REG1); + jit_prepare_routine_call(jit, ctx, asm); - mov(cb, C_ARG_REGS[0], REG0); - mov(cb, C_ARG_REGS[1], uimm_opnd(ivar_name)); - call_ptr(cb, REG1, rb_ivar_get as *const u8); + let ivar_val = asm.ccall(rb_ivar_get as *const u8, vec![recv, Opnd::UImm(ivar_name)]); - if reg0_opnd != SelfOpnd { + if recv_opnd != SelfOpnd { ctx.stack_pop(1); } + // Push the ivar on the stack let out_opnd = ctx.stack_push(Type::Unknown); - mov(cb, out_opnd, RAX); + asm.mov(out_opnd, ivar_val); // Jump to next instruction. This allows guard chains to share the same successor. - jump_to_next_insn(jit, ctx, cb, ocb); + jump_to_next_insn(jit, ctx, asm, ocb); return EndBlock; } - /* - // FIXME: - // This check was added because of a failure in a test involving the - // Nokogiri Document class where we see a T_DATA that still has the default - // allocator. - // Aaron Patterson argues that this is a bug in the C extension, because - // people could call .allocate() on the class and still get a T_OBJECT - // For now I added an extra dynamic check that the receiver is T_OBJECT - // so we can safely pass all the tests in Shopify Core. - // - // Guard that the receiver is T_OBJECT - // #define RB_BUILTIN_TYPE(x) (int)(((struct RBasic*)(x))->flags & RUBY_T_MASK) - add_comment(cb, "guard receiver is T_OBJECT"); - mov(cb, REG1, member_opnd(REG0, struct RBasic, flags)); - and(cb, REG1, imm_opnd(RUBY_T_MASK)); - cmp(cb, REG1, imm_opnd(T_OBJECT)); - jit_chain_guard(JCC_JNE, jit, &starting_context, cb, ocb, max_chain_depth, side_exit); - */ - // FIXME: Mapping the index could fail when there is too many ivar names. If we're // compiling for a branch stub that can cause the exception to be thrown from the // wrong PC. @@ -2008,16 +1998,16 @@ fn gen_get_ivar( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L1998 unsafe { rb_obj_ensure_iv_index_mapping(comptime_receiver, ivar_name) }.as_usize(); // Pop receiver if it's on the temp stack - if reg0_opnd != SelfOpnd { + if recv_opnd != SelfOpnd { ctx.stack_pop(1); } if USE_RVARGC != 0 { // Check that the ivar table is big enough // Check that the slot is inside the ivar table (num_slots > index) - let num_slots = mem_opnd(32, REG0, ROBJECT_OFFSET_NUMIV); - cmp(cb, num_slots, uimm_opnd(ivar_index as u64)); - jle_ptr(cb, counted_exit!(ocb, side_exit, getivar_idx_out_of_range)); + let num_slots = Opnd::mem(32, recv, ROBJECT_OFFSET_NUMIV); + asm.cmp(num_slots, Opnd::UImm(ivar_index as u64)); + asm.jbe(counted_exit!(ocb, side_exit, getivar_idx_out_of_range).into()); } // Compile time self is embedded and the ivar index lands within the object @@ -2027,15 +2017,15 @@ fn gen_get_ivar( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L2017 // Guard that self is embedded // TODO: BT and JC is shorter - add_comment(cb, "guard embedded getivar"); - let flags_opnd = mem_opnd(64, REG0, RUBY_OFFSET_RBASIC_FLAGS); - test(cb, flags_opnd, uimm_opnd(ROBJECT_EMBED as u64)); + asm.comment("guard embedded getivar"); + let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); + asm.test(flags_opnd, Opnd::UImm(ROBJECT_EMBED as u64)); let side_exit = counted_exit!(ocb, side_exit, (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/