We'll need to pick a spill candidate from a set and allow for the search
to fail to find anything.
This also allows slightly better panic messages when we run out of
registers.
A priory, an EBB argument value only gets an affinity if it is used
directly by a non-ghost instruction. A use by a branch passing arguments
to an EBB doesn't count.
When an EBB argument value does have an affinity, the values passed by
all the predecessors must also have affinities. This can cause EBB
argument values to get affinities recursively.
- Add a second pass to the liveness computation for propagating EBB
argument affinities, possibly recursively.
- Verify EBB argument affinities correctly: A value passed to a branch
must have an affinity only if the corresponding EBB argument value in
the destination has an affinity.
When an EBB argument value is used only as a return value, it still
needs to be given a register affinity. Otherwise it would appear as a
ghost value with no affinity.
Do the same to call arguments.
A function parameter in an incoming_arg stack slot should not be
coalesced into any virtual registers. We don't want to force the whole
virtual register to spill to the incoming_arg slot.
Function arguments that don't fit in registers are passed on the stack.
Create "incoming_arg" stack slots representing the stack arguments, and
assign them to the value arguments during spilling.
The offset is relative to the stack pointer in the calling function, so
it excludes the return address pushed by the call instruction itself on
Intel ISAs.
Change the ArgumentLoc::Stack offset to an i32, so it matches the stack
slot offsets.
When coloring registers for a branch instruction, also make sure that
the values passed as EBB arguments are in the registers expected by the
EBB.
The first time a branch to an EBB is processed, assign the EBB arguments
to the registers where the branch arguments already reside so no
regmoves are needed.
Ghost instructions don't generate code, but they can keep registers
alive. The coloring pass needs to process values killed by ghost
instructions so it knows when the registers are freed up.
Also track register pressure changes from ghost kills in the spiller.
When the spiller decides to spill a value, bring along all of the values
in its virtual register. This ensures that we won't have problems with
computing register pressure around EBB arguments. They will always be
register-to-register or stack-to-stack with related values using the
same stack slot.
This also means that the reloading pass won't have to deal with spilled
EBB arguments.
* Convert TypeSet fields to sets; Add BitSet<T> type to rust; Encode ValueTypeSets using BitSet; (still need mypy cleanup)
* nits
* cleanup nits
* forgot mypy type annotations
* rustfmt fixes
* Round 1 comments: filer b2, b4; doc comments in python; move bitset in its own toplevel module; Use Into<u32>
* fixes
* Revert comment to appease rustfmt
Coalescing means creating virtual registers and transforming the code
into conventional SSA form. This means that every value used as a branch
argument will belong to the same virtual register as the corresponding
EBB argument value.
Conventional SSA form makes it easy to avoid memory-memory copies when
spilling values, and the virtual registers can be used as hints when
picking registers too. This reduces the number of register moves needed
for EBB arguments.
Add a VirtRegs collection which tracks virtual registers.
A virtual register is a set of related SSA values whose live ranges
don't interfere. It is advantageous to use the same register or spill
slot for al the values in a virtual register. It reduces copies for EBB
arguments.
Ghost instructions don't have an encoding, and don't appear in the
output. The values they define do not need to be assigned to registers,
so they can be skipped.
The overlaps_def() method tests if a definition would conflict with the
live range.
The reaches_use() method tests if a live range is live at an
instruction.
* Clarify that extended basic blocks are abbreviated as EBB.
* Fix typo.
* Fix a typo.
* Fix typos.
* Use the same phrase to indicate scalar-only as other places in the doc.
* Mention that `band_imm` and friends are scalar-only.
And mention that they're equivalent to their respective
non-immediate-form counterparts.
The EntityRef trait is used by more than just the EntityMap now, so it
should live in its own module.
Also move the entity_impl! macro into the new module so it can be used
for defining new entity references anywhere.
* Replace a single-character string literal with a character literal.
* Use is_some() instead of comparing with Some(_).
* Add code-quotes around type names in comments.
* Use !...is_empty() instead of len() != 0.
* Tidy up redundant returns.
* Remove redundant .clone() calls.
* Remove unnecessary explicit lifetime parameters.
* Tidy up unnecessary '&'s.
* Add parens to make operator precedence explicit.
* Use debug_assert_eq instead of debug_assert with ==.
* Replace a &Vec argument with a &[...].
* Replace `a = a op b` with `a op= b`.
* Avoid unnecessary closures.
* Avoid .iter() and .iter_mut() for iterating over containers.
* Remove unneeded qualification.
* Implement an iterator over encodings
* Implement TargetIsa::legal_encodings
* Exclude non-boolean settings of isa flags bytes
* Address flake8 long line error
As soon as a value is spilled, also assign it to a spill slot.
For now, create a new spill slot for each spilled value. In the future,
values will be sharing spill slots of they are phi-related.
Use a new StackSlots struct to keep track of a function's stack slots
instead of just an entity map. This let's us build more internal data
structures for tracking the stack slots if necessary.
Start by adding a make_spill_slot() function that will be used by the
register allocator.
Add a StackSlotKind enumeration to help keep track of the different
kinds of stack slots supported:
- Incoming and outgoing function arguments on the stack.
- Spill slots and locals.
Change the text format syntax for declaring a stack slot to use a kind
keyword rather than just 'stack_slot'.
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.
Add a Stack() class for specifying operand constraints for values on the
stack.
Add encoding recipes for RISC-V spill and fill instructions. Don't
implement the encoding recipe functions yet since we don't have the
stack slot layout yet.
This is now a generic function that can test arbitrary combinations of
instructions and EBBs for dominance.
It can handle anything that converts into an expanded program point,
including a ValueDef.
Also fix a bug if the earlier dominates() function which didn't properly
handle block layouts that were not topologically ordered.
When comparing instructions in the same EBB, behave like the RPO visits
instructions in program order.
- Add a Layout::pp_ebb() method for convenience. It gets the EBB
containing any program point.
- Add a conversion from ValueDef to ExpandedProgramPoint so it can be
used with the rpo_cmp method.
* Function names should start with %
* Create FunctionName from string
* Implement displaying of FunctionName as %nnnn with fallback to #xxxx
* Run rustfmt and fix FunctionName::with_string in parser
* Implement FunctionName::new as a generic function
* Binary function names should start with #
* Implement NameRepr for function name
* Fix examples in docs to reflect that function names start with %
* Rebase and fix filecheck tests
Switch to the new domtree.cfg_postorder() which returns a reference to a
pre-computed post-order instead of allocating memory and computing a new
post-order.
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.