* Added Intel x86-64 encodings for 64bit loads and store instructions
* Using GPR registers instead of ABCD for istore8 with REX prefix
Fixed testing of 64bit intel encoding
* Emit REX and REX-less encodings for optional REX prefix
Value renumbering in binary64.cton
Replace the isa::Legalize enumeration with a function pointer. This
allows an ISA to define its own specific legalization actions instead of
relying on the default two.
Generate a LEGALIZE_ACTIONS table for each ISA which contains
legalization function pointers indexed by the legalization codes that
are already in the encoding tables. Include this table in
isa/*/enc_tables.rs.
Give the `Encodings` iterator a reference to the action table and change
its `legalize()` method to return a function pointer instead of an
ISA-specific code.
The Result<> returned from TargetIsa::encode() no longer implements
Debug, so eliminate uses of unwrap and expect on that type.
The encoding tables are keyed by the controlling type variable only. We
need to distinguish different encodings for instructions with multiple
type variables.
Add a TypePredicate instruction predicate which can check the type of an
instruction value operand. Combine type checks into the instruction
predicate for instructions with more than one type variable.
Add Intel encodings for fcvt_from_sint.f32.i64 which can now be
distinguished from fcvt_from_sint.f32.i32.
The new encoding format allows entries that mean "stop with this
legalization code" which makes it possible to configure legalization
actions per instruction, instead of only per controlling type variable.
This patch adds the Rust side of the legalization codes:
- Add an `Encodings::legalize()` method on the encoding iterator which
can be called after the iterator has returned `None`. The returned
code is either the default legalization action for the type, or a
specific code encountered in the encoding list.
- Change `lookup_enclist` to return a full iterator instead of just an
offset. The two-phase lookup can bail at multiple points, each time
with a default legalization code from the level 1 table. This default
legalization code is stored in the returned iterator.
- Change all the implementations of legal_encodings() in the ISA
implementations.
This change means that we don't need to return a Result any longer. The
`Encodings` iterator can be empty with an associated legalization code.
Encodings has a 16-bit "recipe" field, but even Intel only has 57
recipes currently, so it is unlikely that we will ever need to full
range. Use this to represent encoding lists more compactly.
Change the encoding list to a format that:
- Doesn't need a predicate entry before every encoding entry.
- Doesn't need a terminator after the list for each instruction.
- Supports multiple "stop codes" for configurable guidance of the
legalizer.
The encoding scheme has these limits:
- 2*NR + NS <= 0x1000
- INSTP + ISAP <= 0x1000
Where:
- NR is the number of recipes in an ISA,
- NS is the number of stop codes (legalization actions).
- INSTP is the number of instruction predicates.
- ISAP is the number of discrete ISA predicates.
Instead of generating a single `check_instp()` function, create an array
of individual function pointers for checking instruction predicates.
This makes explicit the jump table in the old check_instp() method and
it gives us a way of determining the number of instruction predicates
that exists.
It turns out that most encoding predicates are expressed as recipe
predicates. This means that the encoding tables can be more compact
since we can check the recipe predicate separately from individual
instruction predicates, and the recipe number is already present in the
table.
- Don't combine recipe and encoding-specific predicates when creating an
Encoding. Keep them separate.
- Generate a table of recipe predicates with function pointers. Many of
these are null.
- Check any recipe predicate before accepting a recipe+bits pair.
This has the effect of making almost all instruction predicates
CODE_ALWAYS.
When an instruction doesn't have a valid encoding for the target ISA, it
needs to be legalized. Different legalization strategies can be
expressed as separate XFormGroup objects.
Make the choice of XFormGroup configurable per CPU mode, rather than
depending on a hard-coded default.
Add a CPUMode.legalize_type() method which assigns an XFormGroup to
controlling type variables and lets you set a default.
Add a `legalize` field to Level1Entry so the first-level hash table
lookup gives us the configured default legalization action for the
instruction's controlling type variable.
The encoding tables contain references to numbered ISA predicates.
- Give the ISA Flags types a predicate_view() method which returns a
PredicateView.
- Delete the old predicate_bytes() method which returned a raw &[u8].
- Use a 'static lifetime for the encoding list slice in the Encodings
iterator, and a single 'a lifetime for everything else.
Register locations can change throughout an EBB. Make sure the
emit_inst() function considers this when encoding instructions and
update the register diversion tracker.
This function will emit the binary machine code into contiguous raw
memory while sending relocations to a RelocSink.
Add a MemoryCodeSink for generating machine code directly into memory
efficiently. Allow the TargetIsa to provide emit_function
implementations that are specialized to the MemoryCodeSink type to avoid
needless small virtual callbacks to put1() et etc.
Fixes#11.
Presets are groups of settings and values applied at once. This is used
as a shorthand in test files, so for example "isa intel nehalem" enables
all of the CPUID bits that the Nehalem micro-architecture provides.
Change the result type for the bit-counting instructions from a fixed i8
to the iB type variable which is the type of the input. This matches the
convention in WebAssembly, and at least Intel's instructions will set a
full register's worth of count result, even if it is always < 64.
Duplicate the Intel 'ur' encoding recipe into 'umr' and 'urm' variants
corresponding to the RM and MR encoding variants. The difference is
which register is encoded as 'reg' and which is 'r/m' in the ModR/M
byte. A 'mov' register copy uses the MR variant, a unary popcnt uses the
RM variant.
Add a TailRecipe.rex() method which creates an encoding recipe with a
REX prefix.
Define I64 encodings with REX.W for i64 operations and with/without REX
for i32 ops. Only test the with-REX encodings for now. We don't yet have
an instruction shrinking pass that can select the non-REX encodings.
Use a PUT_OP macro in the TailRecipe Python class to replace the code
snippet that emits the prefixes + opcode part of the instruction encoding.
Prepare for the addition of REX prefixes by giving the PUT_OP functions
a third argument representing the REX prefix. For the non-REX encodings,
verify that no REX bits wold be needed.
Generate code to:
- Unwrap the instruction and generate an error if the instruction format
doesn't match the recipe.
- Look up the value locations of register and stack arguments.
The recipe_* functions in the ISA binemit modules now take these
unwrapped items as arguments.
Also add an optional `emit` argument to the EncRecipe constructor which
makes it possible to provide inline Rust code snippets for code
emission. This requires a lot less boilerplate than recipe_* functions.
This is just a rough sketch to get us started. There are bound to be
some issues.
This also legalizes signatures for x86-32, but probably not correctly.
It's basically implementing the x86-64 ABI for 32-bit.
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.
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
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.
Avoid spreading u32 as a bitmask of register classes throughout the
code.
Enforce the limit of 32 register classes total. This could easily be
raised if needed.
The MAX_TOPRCS constant is the highest possible number of top-level
register classes in an ISA. The RegClassData.toprc field is always
smaller than this limit.
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.
We don't support the full set of Intel addressing modes yet. So far we
have:
- Register indirect, no displacement.
- Register indirect, 8-bit signed displacement.
- Register indirect, 32-bit signed displacement.
The SIB addressing modes will need new Cretonne instruction formats to
represent.
We'll arrange encoding lists such that the first suitable encoding is
the best choice for the legalizer. This is the most intuitive way of
generating the encodings.
After register allocation, we may choose a different encoding, but that
will require looking at the whole list.
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.
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.
These instructions have a fixed register constraint; the shift amount is
passed in CL.
Add meta language syntax so a fixed register can be specified as
"GPR.rcx".
Tabulate the Intel opcode representations and implement an OP() function
which computes the encoding bits.
Implement the single-byte opcode with a reg-reg ModR/M byte.
Most instructions don't have any fixed register constraints. Add boolean
summaries that can be used to check if it is worthwhile to scan the
constraint lists when looking for a fixed register constraint.
Also add a tied_ops summary bool which indicates that the instruction
has tied operand constraints.
The register constraint for an output operand can be specified as an
integer indicating the input operand number to tie. The tied operands
must use the same register.
Generate operand constraints using ConstraintKind::Tied(n) for both the
tied operands. The n index refers to the opposite array. The input
operand refers to the outs array and vice versa.