ruby-changes:73108
From: Maxime <ko1@a...>
Date: Tue, 30 Aug 2022 00:51:32 +0900 (JST)
Subject: [ruby-changes:73108] 75c995b0d1 (master): Bias register allocator to reuse first operand
https://git.ruby-lang.org/ruby.git/commit/?id=75c995b0d1 From 75c995b0d10515568ccfe8f67be1bd3bbcbb4b69 Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@s...> Date: Wed, 18 May 2022 16:00:45 -0400 Subject: Bias register allocator to reuse first operand --- yjit/src/backend/ir.rs | 53 +++++++++++++++++++++++++++++++----------- yjit/src/backend/x86_64/mod.rs | 1 + 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 41eef8c60b..7f6a20c191 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -434,7 +434,7 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L434 match op { // Check for Add, Sub, And, Mov, with two memory operands. // Load one operand into memory. - Op::Add | Op::Sub | Op::And => { + Op::Add | Op::Sub | Op::And | Op::Mov => { match opnds.as_slice() { [Opnd::Mem(_), Opnd::Mem(_)] => { // We load opnd1 because for mov, opnd0 is the output @@ -508,27 +508,42 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L508 } } + // 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 { + // 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 + if opnds.len() > 0 { + 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])) + } + } + } + } + + if out_reg == Opnd::None { + // Allocate a new register for this instruction + out_reg = Opnd::Reg(alloc_reg(&mut pool, ®s)) + } + } + // Replace InsnOut operands by their corresponding register - let opnds = opnds.into_iter().map(|opnd| + let reg_opnds = opnds.into_iter().map(|opnd| match opnd { Opnd::InsnOut(idx) => asm.insns[idx].out, _ => opnd, } ).collect(); - asm.push_insn(op, opnds, target); + asm.push_insn(op, reg_opnds, target); + // Set the output register for this instruction let num_insns = asm.insns.len(); - if live_ranges[index] != index { - // This instruction is used by another instruction, so we need - // to allocate a register for it. - asm.insns[num_insns - 1].out = Opnd::Reg(alloc_reg(&mut pool, ®s)); - } - else - { - // Nobody is using the output of this instruction - asm.insns[num_insns - 1].out = Opnd::None; - } + asm.insns[num_insns - 1].out = out_reg; }); assert_eq!(pool, 0, "Expected all registers to be returned to the pool"); @@ -732,7 +747,7 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L747 fn test_compile() { let mut asm = Assembler::new(); - let mut cb = CodeBlock::new_dummy(64 * 1024); + let mut cb = CodeBlock::new_dummy(1024); let regs = Assembler::get_scrach_regs(); let out = asm.add(Opnd::Reg(regs[0]), Opnd::UImm(2)); @@ -740,4 +755,14 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L755 asm.compile(&mut cb); } + + // Test full codegen pipeline + #[test] + fn test_mov_mem2mem() + { + let mut asm = Assembler::new(); + let mut cb = CodeBlock::new_dummy(1024); + asm.mov(Opnd::mem(64, SP, 0), Opnd::mem(64, SP, 8)); + asm.compile(&mut cb); + } } diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index 2eb12e3d27..00b9998b69 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -69,6 +69,7 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L69 Store, */ + Op::Load => add(cb, insn.out.into(), insn.opnds[0].into()), Op::Mov => add(cb, insn.opnds[0].into(), insn.opnds[1].into()), // Test and set flags -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/