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.
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.
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 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.
Include the test file preamble comments when building a filecheck
instance for every function in the file.
This makes it possible to define common regex variables in the preamble
and use these definitions for all the functions.