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.
- 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.
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.
Add a RegBank class for describing CPU register banks.
Define register banks for all the ISA stubs. The ARM32 floating point
bank in particular requires attention.
The Intel ISA handles both 32-bit and 64-bit code.
ARM is split into separate arm32 and arm64 ISAs since the architectures
have little in common in instruction encodings and register files.
Use the inferred type variables to construct a type argument for builder
methods. This is for those instructions where the result types cannot be
computed from the result types.
Each instruction used in a pattern has constraints on the types of its
operands. These constraints are expressed as symbolic type variables.
Compute type variables for each variable used in a transformation
pattern. Some are free type variables, and some are derived from the
free type variables.
The type variables associated with variables can be used for computing
the result types of replacement instructions that don't support simple
forward type inference from their inputs.
The type sets computed by this patch are conservatively too large, so
they can't yet be used to type check patterns.
Add TypeVar constants representing the available type functions, and a
TypeVar.derived() static method which creates a derived TypeVar.
Keep the existing non-parametric methods for creating derived type
variables.
Add a method for converting a free type variable to a derived one.
A few operands have a fixed type assigned. Create a singleton type
variable for these exceptions. Most instructions are polymorphic, so
this is a little overhead.
Eliminate the Operand.typ field and replace it with an Operand.typevar
field which is always a TypeVar, but which only exists in VALUE
operands.
This method caused lots of import cycles when type checking.
Use isinstance() in the Operand constructor instead to decipher the
OperandSpec union type.
We want to separate the Python classes that make up the DSL used to
define the Cretonne language from the concrete definitions.
- cdsl.types defines the ValueType class hierarchy.
- base.types defines the concrete types.
Add an assertion for the value placements that we don't support yet.
1. A primary result in the source pattern becomes a secondary result in
the destination.
2. A secondary result becomes a secondary result, and the destination
instruction is not exactly matching the source.
Since we're deconstructing an instruction anyway, go ahead and resolve
any value aliases on its arguments before we construct the replacement
instructions.
If a secondary value in the source pattern becomes a primary value in
the destination pattern, it is not possible to overwrite the definition
of the source value.
Instead, change the original source value to an alias to the new promary
value.
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 expanding iadd_cout, the original instruction is replaced with an
iadd, and an icmp is inserted after the iadd.
Make sure we advance the insertion position after replacing iadd_cout so
the icmp gets inserted *after* iadd.
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.
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.
When a Var is used in an XForm, it can be defined in the src or dst or
both patterns, and it is classified accordingly. When a Var is defined,
it is also useful to be able to find the `Def` that defined it.
Add src_def and dst_def reference members to Var, and initialize them in
the private Var copies that XForm creates for itself.
These two members also replace the defctx bitmask.
There's 4 classes of variables, depending on whether they have defs in
the source and destination patterns.
Add more XForm verification: In a legalize XForm, all source defs must
be outputs.
Fix a legalize pattern bug caught by this.