From 54f074e50758499fbb6d9bfc0e05ca895f719932 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Sat, 4 Mar 2023 23:49:10 +0100 Subject: [PATCH] Re-introduce optional dedicated scratch registers (#117) * Re-introduce optional dedicated scratch registers Dedicated scratch registers used for resolving move cycles were removed in #51 and replaced with an algorithm to automatically allocate a scratch register as needed. However in many cases, a client will already have a non-allocatable scratch register available for things like extended jumps (see #91). It makes sense to re-use this register for regalloc than potentially spilling an existing register. * Clarify comment --- src/fuzzing/func.rs | 2 ++ src/ion/moves.rs | 3 +++ src/lib.rs | 26 +++++++++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/fuzzing/func.rs b/src/fuzzing/func.rs index fda2742..08daae1 100644 --- a/src/fuzzing/func.rs +++ b/src/fuzzing/func.rs @@ -662,11 +662,13 @@ pub fn machine_env() -> MachineEnv { } let preferred_regs_by_class: [Vec; 2] = [regs(0..24), vec![]]; let non_preferred_regs_by_class: [Vec; 2] = [regs(24..32), vec![]]; + let scratch_by_class: [Option; 2] = [None, None]; let fixed_stack_slots = regs(32..63); // Register 63 is reserved for use as a fixed non-allocatable register. MachineEnv { preferred_regs_by_class, non_preferred_regs_by_class, + scratch_by_class, fixed_stack_slots, } } diff --git a/src/ion/moves.rs b/src/ion/moves.rs index 0bead13..fc4905c 100644 --- a/src/ion/moves.rs +++ b/src/ion/moves.rs @@ -986,6 +986,9 @@ impl<'a, F: Function> Env<'a, F> { to: pos_prio.pos.next(), }); let get_reg = || { + if let Some(reg) = self.env.scratch_by_class[regclass as usize] { + return Some(Allocation::reg(reg)); + } while let Some(preg) = scratch_iter.next() { if !self.pregs[preg.index()] .allocations diff --git a/src/lib.rs b/src/lib.rs index 6c29e1b..6a7e2d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1326,19 +1326,43 @@ impl<'a> Iterator for OutputIter<'a> { pub struct MachineEnv { /// Preferred physical registers for each class. These are the /// registers that will be allocated first, if free. + /// + /// If an explicit scratch register is provided in `scratch_by_class` then + /// it must not appear in this list. pub preferred_regs_by_class: [Vec; 2], /// Non-preferred physical registers for each class. These are the /// registers that will be allocated if a preferred register is /// not available; using one of these is considered suboptimal, /// but still better than spilling. + /// + /// If an explicit scratch register is provided in `scratch_by_class` then + /// it must not appear in this list. pub non_preferred_regs_by_class: [Vec; 2], + /// Optional dedicated scratch register per class. This is needed to perform + /// moves between registers when cyclic move patterns occur. The + /// register should not be placed in either the preferred or + /// non-preferred list (i.e., it is not otherwise allocatable). + /// + /// Note that the register allocator will freely use this register + /// between instructions, but *within* the machine code generated + /// by a single (regalloc-level) instruction, the client is free + /// to use the scratch register. E.g., if one "instruction" causes + /// the emission of two machine-code instructions, this lowering + /// can use the scratch register between them. + /// + /// If a scratch register is not provided then the register allocator will + /// automatically allocate one as needed, spilling a value to the stack if + /// necessary. + pub scratch_by_class: [Option; 2], + /// Some `PReg`s can be designated as locations on the stack rather than /// actual registers. These can be used to tell the register allocator about /// pre-defined stack slots used for function arguments and return values. /// - /// `PReg`s in this list cannot be used as an allocatable register. + /// `PReg`s in this list cannot be used as an allocatable or scratch + /// register. pub fixed_stack_slots: Vec, }