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.
77 lines
3.9 KiB
Rust
77 lines
3.9 KiB
Rust
//! Legalize instructions.
|
|
//!
|
|
//! A legal instruction is one that can be mapped directly to a machine code instruction for the
|
|
//! target ISA. The `legalize_function()` function takes as input any function and transforms it
|
|
//! into an equivalent function using only legal instructions.
|
|
//!
|
|
//! The characteristics of legal instructions depend on the target ISA, so any given instruction
|
|
//! can be legal for one ISA and illegal for another.
|
|
//!
|
|
//! Besides transforming instructions, the legalizer also fills out the `function.encodings` map
|
|
//! which provides a legal encoding recipe for every instruction.
|
|
//!
|
|
//! The legalizer does not deal with register allocation constraints. These constraints are derived
|
|
//! from the encoding recipes, and solved later by the register allocator.
|
|
|
|
use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, InstBuilder};
|
|
use ir::condcodes::IntCC;
|
|
use isa::{TargetIsa, Legalize};
|
|
|
|
/// Legalize `func` for `isa`.
|
|
///
|
|
/// - Transform any instructions that don't have a legal representation in `isa`.
|
|
/// - Fill out `func.encodings`.
|
|
///
|
|
pub fn legalize_function(func: &mut Function, isa: &TargetIsa) {
|
|
// TODO: This is very simplified and incomplete.
|
|
func.encodings.resize(func.dfg.num_insts());
|
|
let mut pos = Cursor::new(&mut func.layout);
|
|
while let Some(_ebb) = pos.next_ebb() {
|
|
// Keep track of the cursor position before the instruction being processed, so we can
|
|
// double back when replacing instructions.
|
|
let mut prev_pos = pos.position();
|
|
|
|
while let Some(inst) = pos.next_inst() {
|
|
match isa.encode(&func.dfg, &func.dfg[inst]) {
|
|
Ok(encoding) => *func.encodings.ensure(inst) = encoding,
|
|
Err(action) => {
|
|
// We should transform the instruction into legal equivalents.
|
|
// Possible strategies are:
|
|
// 1. Legalize::Expand: Expand instruction into sequence of legal instructions.
|
|
// Possibly iteratively. ()
|
|
// 2. Legalize::Narrow: Split the controlling type variable into high and low
|
|
// parts. This applies both to SIMD vector types which can be halved and to
|
|
// integer types such as `i64` used on a 32-bit ISA. ().
|
|
// 3. TODO: Promote the controlling type variable to a larger type. This
|
|
// typically means expressing `i8` and `i16` arithmetic in terms if `i32`
|
|
// operations on RISC targets. (It may or may not be beneficial to promote
|
|
// small vector types versus splitting them.)
|
|
// 4. TODO: Convert to library calls. For example, floating point operations on
|
|
// an ISA with no IEEE 754 support.
|
|
let changed = match action {
|
|
Legalize::Expand => expand(&mut pos, &mut func.dfg),
|
|
Legalize::Narrow => narrow(&mut pos, &mut func.dfg),
|
|
};
|
|
// If the current instruction was replaced, we need to double back and revisit
|
|
// the expanded sequence. This is both to assign encodings and possible to
|
|
// expand further.
|
|
// There's a risk of infinite looping here if the legalization patterns are
|
|
// unsound. Should we attempt to detect that?
|
|
if changed {
|
|
pos.set_position(prev_pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remember this position in case we need to double back.
|
|
prev_pos = pos.position();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Include legalization patterns that were generated by gen_legalizer.py from the XForms in
|
|
// meta/cretonne/legalize.py.
|
|
//
|
|
// Concretely, this defines private functions `narrow()`, and `expand()`.
|
|
include!(concat!(env!("OUT_DIR"), "/legalizer.rs"));
|