An instruction may have fixed operand constraints that make it
impossibly to use a single register value to satisfy two at a time.
Detect when the same value is used for multiple fixed register operands
and insert copies during the spilling pass.
Even if an argument is already in the correct register, make sure that
we detect conflicts by registering the no-op move. This also means that
the ABI argument register won't be turned into a variable for the
solver.
Add a spilling pass which lowers register pressure by assigning SSA
values to the stack. Important missing features:
- Resolve conflicts where an instruction uses the same value more than
once in incompatible ways.
- Deal with EBB arguments.
Fix bugs in the reload pass exposed by the first test case:
- Create live ranges for temporary registers.
- Set encodings on created spill and fill instructions.
The register pressure tracker now has to separate register counts: base
and transient.
The transient counts are used to track spikes of register pressure, such
as dead defs or temporary registers needed to satisfy instruction
constraints.
The base counts represent long-lived variables.
The reload pass inserts spill and fill instructions as needed so
instructions that operate on registers will never see a value with stack
affinity.
This is a very basic implementation, and we can't write good test cases
until we have a spilling pass.
The create_dead() methods can create a live range for a new value, and
extend_local() can extend a live range within an EBB where it is already
live.
This is enough to update liveness for new values as long as they stay
local to their EBB.
The spilling and reload passes need to ensure that the set of live
ranges with register affinity can always be assigned registers. The
register pressure tracker can count how many registers are in use for
each top-level register class and give guidance on the type of
registers that need to be spilled when limits are exceeded.
Pressure tracking is extra complicated for the arm32 floating point
register bank because there are multiple top-level register classes (S,
D, Q) competing for the same register units.
A top-level register class is one that has no sub-classes. It is
possible to have multiple top-level register classes in the same
register bank. For example, ARM's FPR bank has both D and Q top-level
register classes.
Number register classes such that all top-level register classes appear
as a contiguous sequence starting from 0. This will be used by the
register allocator when counting used registers per top-level register
class.
After finding a register solution, it need to be executed as a sequence
of regmove instructions. This often requires a topological ordering of
the moves so they don't conflict.
When the solution contains cycles, try to grab an available scratch
register to implement the copies. Panic if that fails (later, we'll
implement emergency spilling in this case).
Make sure we handle odd aliasing in the arm32 floating point register
bank. Not everything is a simple cycle in that case, so make sure we
don't assume so.
The register constraint solver has two kinds of variables:
1. Live values that were already in a register, and
2. Values defined by the instruction.
Make a record of the original register holding the first kind of value.
It is not necessary to to a second pass over the live values to update
the set of available registers. The color_args() and color_entry_args()
functions can do that in a single pass.
The live value tracker expects them to be there.
We may eventually delete dead arguments from internal EBBs, but at least
the entry block needs to be able to handle dead function arguments.
Provide a drop_dead_args() function which deletes them instead.
We still need to assign a register to dead EBB arguments, so they can't
just be ignored.
The live value tracker goes through the trouble of looking up the live
range for each value it tracks. We can cache a few more interesting
properties from the live range in the LiveValue struct.
Most of the time, register coloring is almost trivial: just pick
available registers for the values defined by the current instruction.
However, some instructions have register operand constraints, and it may
be necessary to move live registers around to satisfy the constraints.
Sometimes the instruction's own operands can interfere with each other
in a way that you can't just pick a register assignment for each output
in order.
This is complicated enough that it is worthwhile to represent as a
constraint satisfaction problem in a separate solver module. The
representation is chosen to be very fast in the common case where the
constraints are trivial to solve.
The current implementation is still incomplete, but as functional as the
code it's replacing. Missing features:
- Handle tied operand constraints.
- Handle ABI constraints on calls and return instructions.
- Execute a constraint solution by emitting regmove instructions.
- Handling register diversions before leaving the EBB.
The arguments to the entry block arrive in registers determined by the
ABI. This information is stored in the signature.
Use a separate function for coloring entry block arguments using the
signature information. We can't handle stack arguments yet.
This value affinity doesn't mean "whatever", it means that the value
does not interact with any real instructions, where "real" means
instructions that have a legal encoding.
Update the liveness verifier to check this property:
- Encoded instructions can only use real values.
- Encoded instructions define real values.
- Ghost instructions define ghost values.
This means that we can verify the basics with verify_context before
moving on to verifying the liveness information.
Live ranges are now verified immediately after computing them and after
register allocation is complete.
The liveness verifier will check that the live ranges are consistent
with the function. It runs as part of the register allocation pipeline
when enable_verifier is set.
The initial implementation checks the live ranges, but not the
ISA-specific constraints and affinities.
Now we can access instruction results and arguments as well as EBB
arguments as slices.
Delete the Values iterator which was traversing the linked lists of
values. It is no longer needed.
This is the first step of the value list refactoring which will replace
linked lists of values with value lists.
- Keep a ValueList in the EbbData struct containing all the EBB
arguments.
- Change dfg.ebb_args() to return a slice instead of an iterator.
This leaves us in a temporary hybrid state where we maintain both a
linked list and a ValueList vector of the EBB arguments.
The tables returned by recipe_names() and recipe_constraints() are now
collected into an EncInfo struct that is available from
TargetIsa::encoding_info(). This is equivalent to the register bank
tables available fro TargetIsa::register_info().
This cleans of the TargetIsa interface and makes it easier to add
encoding-related information.
Now that variable arguments are always stored in a value list with the
fixed arguments, we no longer need the arcane [&[Value]; 2] return type.
Arguments are always stored contiguously, so just return a &[Value]
slice.
Also remove the each_arg() methods which were just trying to make it
easier to work with the old slice pair.
The Branch format also stores its fixed argument in the value list. This
requires the value pool to be passed to a few more functions.
Note that this actually makes the Branch and Jump variants of
InstructionData identical. The instruction format hashing does not yet
understand that all value operands are stored in the value list. We'll
fix that in a later patch.
Also convert IndirectCall, noting that Call and IndirectCall remain
separate instruction formats because they have different immediate
fields.
Add a new kind of instruction format that keeps all of its value
arguments in a value list. These value lists are all allocated out of
the dfg.value_lists memory pool.
Instruction formats with the value_list property set store *all* of
their value arguments in a single value list. There is no distinction
between fixed arguments and variable arguments.
Change the Call instruction format to use the value list representation
for its arguments.
This change is only the beginning. The intent is to eliminate the
boxed_storage instruction formats completely. Value lists use less
memory, and when the transition is complete, InstructionData will have a
trivial Drop implementation.
Add an abi module with code that is probably useful to all ISAs when
implementing this function.
Add a unit() method to RegClassData which can be used to index the
register units in a class.
This is a bare-bones outline of the SSA coloring pass. Many features are
missing, including:
- Handling instruction operand constraints beyond simple register
classes.
- Handling ABI requirements for function arguments and return values.
- Generating shuffle code for EBB arguments.
When the liveness pass implements dead code elimination, missing live
ranges can be used to indicate unused values that it may be possible to
remove. But even then, we may have to keep dead defs around if the
instruction has side effects or other live defs.