Ensure that the set of register classes is closed under intersection.
Provide a RegClass::intersect() method which finds the register class
representing the intersection of two classes.
Generate a bit-mask of subclasses for each register class to be used by
the intersect() method.
Ensure that register classes are sorted topologically. This is also used
by the intersect() method.
Every encoding recipe must specify register constraints on input and
output values.
Generate recipe constraint tables along with the other encoding tables.
This set of available register units also manages register aliasing in
an efficient way.
Detect if the units in a register straddles mask words. The algorithm
for allocating multi-unit registers expect the whole register to be
inside a single mask word. We could handle this if necessary, but so far
no ISAs need it.
Also rework the algorithm to be more robust against unreachable blocks.
- Add an is_reachable(ebb) method.
- Change idom(ebb) to just return an instruction.
- Make idom() return None for the entry block as well as unreachable
blocks.
- Remove NO_VALUE and ExpandedValue::None.
- Remove the Default implelmentation for Value.
- InstructionData::second_result() returns an Option<Value>.
- InstructionData::second_result() returns a reference to the packed
option.
This was only used for the comment rewrite mechanism, and we can just
predict the next allocated instruction number instead. See the other
uses of next_key() for gather_comments().
This is simply the slice iterator for the dense vector.
- map.values() returns an iterator with references to the values.
- for i in &map iterates over references to the values.
This implements the classic Briggs/Torczon sparse set construct.
Adapt it to our existing EntityRef infrastructure so we can use types
keys instead of just integers like the original paper does.
Also provide a SparseSet<T> type alias which implements a sparse set of
entity refeences. This is actually closer to what the original paper
describes.
We will track live ranges separately for each SSA value, rather than per
virtual register like LLVM does.
This is the basis for a register allocator, so place it in a new
regalloc module.
The ProgramOrder::cmp() comparison is often used where one or both
arguments are statically known to be an Inst or Ebb. Give the compiler a
better chance to discover this via inlining and other optimizations.
- Make cmp() generic with Into<ExpandedProgramPoint> bounds.
- Implement the natural From<T> traits for ExpandedProgramPoint.
- Make Layout::pp_seq() generic with the same bound.
Now, with inlining and constant folding, passing an Inst argument to
PO::cmp() will result in a call to a monomorphized Layout::seq::<Inst>()
which can avoid the dynamic match to select a table for looking up the
sequence number.
The result is that comparing two program points of statically known type
results in two direct table lookups and a sequence number comparison.
This all uses ExpandedProgramPoint because it is more likely to be
transparent to the constant folder than the bit-packed ProgramPoint
type.
Assign sequence numbers to both instructions and EBB headers such that
their relative position can be determined in constant time simply by
comparing sequence numbers.
Implement the sequence numbers with a scheme similar to the line numbers
used in BASIC programs. Start out with 10, 20, 30, ... and pick numbers
in the gaps as long as possible. Renumber locally when needed.
for InstructionData. Use generated `is_terminator()` for `Opcode`
instead. `is_terminator`, `can_trap` and `is_branch` functions are now
public.
fix syntax error
The intel, arm32, and arm32 targets were only defined in the meta
language previously. Add Rust implementations too.
This is mostly boilerplate, except for the unit tests in the
registers.rs files.
If an instruction uses any values that are aliases of other values,
print out the alias mappings on lines preceding the instruction. This is
necessary to reconstruct the data flow graph.
We don't make any attempt to only write out each alias mapping once.
The parser does not yet support value aliases.
When the extended_values table is empty, the value to resolve is
definitely not an alias, but we still need as least one trip in the loop
to determine that.
Provide a generic way of accessing the value arguments on an
instruction. This is provided as two slice references. One for the fixed
arguments and one for any VariableArgs.
The arguments() methods return an array of two slices which is a bit
awkward. Also provide an each_arg() method which passes each argument
value to a closure.
When an illegal instruction is replaced with other instructions, back up
and revisit the expanded instructions. The new instructions need to have
encodings assigned too.
This also allows for expansions to contain illegal instructions that
need to be legalized themselves.
Make it possible to move a cursor to a new position.
In the current implementation of Layout and Cursor, this is a trivial
operation, but if we switch to a B-tree based function layout, this
involves navigating the tree.
Begin emitting legalization patterns in the form of two functions,
'expand' and 'narrow' that are included in legalizer.rs.
The generated code compiles, but it is not fully working yet. We need to
deal with the special cases of instructions producing multiple results.
This is a work in progress. The 'legalizer.rs' file generated by
gen_legalizer.py is not used for anything yet.
Add PEP 484 type annotations to a bunch of Python code.
A extended value can now be changed to a third form: An alias of another
value. This is like a copy instruction, but implicit in the value table.
Value aliases are used in lieu of use-def chains which would be used to
implement replace-all-uses-with.
Added new DFG methods:
- change_to_alias() changes an existing extended value into an alias.
Primay values can't be changed, replace their definition with a copy
instruction instead.
- resolve_aliases() find the original non-alias value.
- resolve_copies() like resolve_aliases(), but also sees through
copy/spill/fill instructions.
Polymorphic single-result instructions don't always return the
controlling type variable as their first result. They may use a derived
type variable, as for example icmp does.
All scalar types are mapped to b1 which is usually what you want for a
scalar. Vector types have their lanes mapped to the wider boolean types.
Add an as_bool_pedantic() methos that produces the scalar sized boolean
types as before.
Also add a friendlier Debug implementation for Type.
The DataFlowGraph::replace(inst) method returns an instruction builder
that will replace an instruction in-place.
This will be used when transforming instructions, replacing an old
instruction with a new (legal) way of computing its primary value. Since
primary result values are essentially instruction pointers, this is the
only way of replacing the definition of a value.
If secondary result values match the old instruction in both number and
types, they can be reused. If not, added a detach_secondary_results()
method for detaching old secondary values.
All the InstrBuilder methods now consume the builder, and the non-leaf
methods return the dfg mutable reference they were holding.
This makes it possible to construct instruction builders that are only
safe to use once because they are doing more advanced value rewriting.
Rewrite Builder uses in test cases to use this method and construct a
new builder for each instruction. This pattern allows us to change the
InstBuilder trait to a one-shot implementation that can only create a
single instruction.
Don't re-export the Builder struct, it is less important than the
InstBuilder trait, and we may get more implementations.
Distinguish the lifetime of the Cursor and its referenced function
layout.
Use two separate function lifetimes: 'fc and 'fd. The borrow checker
seems to get confused if we don't.