diff options
Diffstat (limited to 'yjit/src/backend/ir.rs')
-rw-r--r-- | yjit/src/backend/ir.rs | 98 |
1 files changed, 78 insertions, 20 deletions
diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index d14e3485aa..1f6307db9e 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -105,12 +105,20 @@ pub enum Op Breakpoint, } +// Memory operand base +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum MemBase +{ + Reg(u8), + InsnOut(usize), +} + // Memory location #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Mem { - // Base register - pub(super) base_reg: Reg, + // Base register number or instruction index + pub(super) base: MemBase, // Offset relative to the base pointer pub(super) disp: i32, @@ -148,11 +156,20 @@ impl Opnd Opnd::Reg(base_reg) => { assert!(base_reg.num_bits == 64); Opnd::Mem(Mem { + base: MemBase::Reg(base_reg.reg_no), + disp: disp, num_bits: num_bits, - base_reg: base_reg, + }) + }, + + Opnd::InsnOut(idx) => { + Opnd::Mem(Mem { + base: MemBase::InsnOut(idx), disp: disp, + num_bits: num_bits, }) }, + _ => unreachable!("memory operand with non-register base") } } @@ -161,6 +178,13 @@ impl Opnd pub fn const_ptr(ptr: *const u8) -> Self { Opnd::UImm(ptr as u64) } + + pub fn unwrap_reg(&self) -> Reg { + match self { + Opnd::Reg(reg) => *reg, + _ => unreachable!("trying to unwrap {:?} into reg", self) + } + } } impl From<usize> for Opnd { @@ -264,8 +288,14 @@ impl Assembler // one. let insn_idx = self.insns.len(); for opnd in &opnds { - if let Opnd::InsnOut(idx) = opnd { - self.live_ranges[*idx] = insn_idx; + match opnd { + Opnd::InsnOut(idx) => { + self.live_ranges[*idx] = insn_idx; + } + Opnd::Mem( Mem { base: MemBase::InsnOut(idx), .. }) => { + self.live_ranges[*idx] = insn_idx; + } + _ => {} } } @@ -483,22 +513,26 @@ impl Assembler // spans more than one instruction. In that case, return the // allocated register to the pool. for opnd in &opnds { - if let Opnd::InsnOut(idx) = opnd { - // Since we have an InsnOut, we know it spans more that one - // instruction. - let start_index = *idx; - assert!(start_index < index); - - // We're going to check if this is the last instruction that - // uses this operand. If it is, we can return the allocated - // register to the pool. - if live_ranges[start_index] == index { - if let Opnd::Reg(reg) = asm.insns[start_index].out { - dealloc_reg(&mut pool, ®s, ®); - } else { - unreachable!("no register allocated for insn"); + match opnd { + Opnd::InsnOut(idx) | Opnd::Mem( Mem { base: MemBase::InsnOut(idx), .. }) => { + // Since we have an InsnOut, we know it spans more that one + // instruction. + let start_index = *idx; + assert!(start_index < index); + + // We're going to check if this is the last instruction that + // uses this operand. If it is, we can return the allocated + // register to the pool. + if live_ranges[start_index] == index { + if let Opnd::Reg(reg) = asm.insns[start_index].out { + dealloc_reg(&mut pool, ®s, ®); + } else { + unreachable!("no register allocated for insn"); + } } } + + _ => {} } } @@ -541,7 +575,15 @@ impl Assembler // Replace InsnOut operands by their corresponding register let reg_opnds = opnds.into_iter().map(|opnd| match opnd { - Opnd::InsnOut(idx) => asm.insns[idx].out, + Opnd::InsnOut(idx) => asm.insns[idx].out, + Opnd::Mem(Mem { base: MemBase::InsnOut(idx), disp, num_bits }) => { + let out_reg = asm.insns[idx].out.unwrap_reg(); + Opnd::Mem(Mem { + base: MemBase::Reg(out_reg.reg_no), + disp, + num_bits + }) + } _ => opnd, } ).collect(); @@ -864,6 +906,22 @@ mod tests { asm.compile_with_regs(&mut cb, regs); } + // Use instruction output as base register for memory operand + #[test] + fn test_base_insn_out() + { + let (mut asm, mut cb, regs) = setup_asm(1); + + // Load the pointer into a register + let ptr_reg = asm.load(Opnd::const_ptr(0 as *const u8)); + let counter_opnd = Opnd::mem(64, ptr_reg, 0); + + // Increment and store the updated value + asm.incr_counter(counter_opnd, 1.into() ); + + asm.compile_with_regs(&mut cb, regs); + } + #[test] fn test_c_call() { |