Revisit expanded instructions for legalization.
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.
This commit is contained in:
@@ -11,8 +11,10 @@ ebb0(v1: i64, v2: i64):
|
|||||||
; regex: VX=vx\d+
|
; regex: VX=vx\d+
|
||||||
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
|
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
|
||||||
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
|
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
|
||||||
; check: $(v3l=$V) = band $v1l, $v2l
|
; check: [R#ec
|
||||||
; check: $(v3h=$V) = band $v1h, $v2h
|
; sameln: $(v3l=$V) = band $v1l, $v2l
|
||||||
|
; check: [R#ec
|
||||||
|
; sameln: $(v3h=$V) = band $v1h, $v2h
|
||||||
; check: $v3 = iconcat_lohi $v3l, $v3h
|
; check: $v3 = iconcat_lohi $v3l, $v3h
|
||||||
|
|
||||||
function bitwise_or(i64, i64) -> i64 {
|
function bitwise_or(i64, i64) -> i64 {
|
||||||
@@ -24,8 +26,10 @@ ebb0(v1: i64, v2: i64):
|
|||||||
; regex: VX=vx\d+
|
; regex: VX=vx\d+
|
||||||
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
|
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
|
||||||
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
|
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
|
||||||
; check: $(v3l=$V) = bor $v1l, $v2l
|
; check: [R#cc
|
||||||
; check: $(v3h=$V) = bor $v1h, $v2h
|
; sameln: $(v3l=$V) = bor $v1l, $v2l
|
||||||
|
; check: [R#cc
|
||||||
|
; sameln: $(v3h=$V) = bor $v1h, $v2h
|
||||||
; check: $v3 = iconcat_lohi $v3l, $v3h
|
; check: $v3 = iconcat_lohi $v3l, $v3h
|
||||||
|
|
||||||
function bitwise_xor(i64, i64) -> i64 {
|
function bitwise_xor(i64, i64) -> i64 {
|
||||||
@@ -37,6 +41,8 @@ ebb0(v1: i64, v2: i64):
|
|||||||
; regex: VX=vx\d+
|
; regex: VX=vx\d+
|
||||||
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
|
; check: $(v1l=$V), $(v1h=$VX) = isplit_lohi $v1
|
||||||
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
|
; check: $(v2l=$V), $(v2h=$VX) = isplit_lohi $v2
|
||||||
; check: $(v3l=$V) = bxor $v1l, $v2l
|
; check: [R#8c
|
||||||
; check: $(v3h=$V) = bxor $v1h, $v2h
|
; sameln: $(v3l=$V) = bxor $v1l, $v2l
|
||||||
|
; check: [R#8c
|
||||||
|
; sameln: $(v3h=$V) = bxor $v1h, $v2h
|
||||||
; check: $v3 = iconcat_lohi $v3l, $v3h
|
; check: $v3 = iconcat_lohi $v3l, $v3h
|
||||||
|
|||||||
@@ -139,8 +139,6 @@ def gen_xform(xform, fmt):
|
|||||||
`inst: Inst` is the variable to be replaced. It is pointed to by `pos:
|
`inst: Inst` is the variable to be replaced. It is pointed to by `pos:
|
||||||
Cursor`.
|
Cursor`.
|
||||||
`dfg: DataFlowGraph` is available and mutable.
|
`dfg: DataFlowGraph` is available and mutable.
|
||||||
|
|
||||||
Produce an `Option<Inst>` result at the end.
|
|
||||||
"""
|
"""
|
||||||
# Unwrap the source instruction, create local variables for the input
|
# Unwrap the source instruction, create local variables for the input
|
||||||
# variables.
|
# variables.
|
||||||
@@ -150,9 +148,6 @@ def gen_xform(xform, fmt):
|
|||||||
for dst in xform.dst.rtl:
|
for dst in xform.dst.rtl:
|
||||||
emit_dst_inst(dst, fmt)
|
emit_dst_inst(dst, fmt)
|
||||||
|
|
||||||
# TODO: Return the first replacement instruction.
|
|
||||||
fmt.line('None')
|
|
||||||
|
|
||||||
|
|
||||||
def gen_xform_group(xgrp, fmt):
|
def gen_xform_group(xgrp, fmt):
|
||||||
# type: (XFormGroup, Formatter) -> None
|
# type: (XFormGroup, Formatter) -> None
|
||||||
@@ -165,8 +160,7 @@ def gen_xform_group(xgrp, fmt):
|
|||||||
fmt.line('#[allow(unused_variables,unused_assignments)]')
|
fmt.line('#[allow(unused_variables,unused_assignments)]')
|
||||||
with fmt.indented(
|
with fmt.indented(
|
||||||
'fn ' + xgrp.name +
|
'fn ' + xgrp.name +
|
||||||
'(pos: &mut Cursor, dfg: &mut DataFlowGraph) -> ' +
|
'(pos: &mut Cursor, dfg: &mut DataFlowGraph) -> bool {',
|
||||||
'Option<Inst> {',
|
|
||||||
'}'):
|
'}'):
|
||||||
# Gen the instruction to be legalized. The cursor we're passed must be
|
# Gen the instruction to be legalized. The cursor we're passed must be
|
||||||
# pointing at an instruction.
|
# pointing at an instruction.
|
||||||
@@ -179,7 +173,8 @@ def gen_xform_group(xgrp, fmt):
|
|||||||
'Opcode::{} => {{'.format(inst.camel_name), '}'):
|
'Opcode::{} => {{'.format(inst.camel_name), '}'):
|
||||||
gen_xform(xform, fmt)
|
gen_xform(xform, fmt)
|
||||||
# We'll assume there are uncovered opcodes.
|
# We'll assume there are uncovered opcodes.
|
||||||
fmt.line('_ => None,')
|
fmt.line('_ => return false,')
|
||||||
|
fmt.line('true')
|
||||||
|
|
||||||
|
|
||||||
def generate(isas, out_dir):
|
def generate(isas, out_dir):
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
//! The legalizer does not deal with register allocation constraints. These constraints are derived
|
//! The legalizer does not deal with register allocation constraints. These constraints are derived
|
||||||
//! from the encoding recipes, and solved later by the register allocator.
|
//! from the encoding recipes, and solved later by the register allocator.
|
||||||
|
|
||||||
use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, Inst, InstBuilder};
|
use ir::{Function, Cursor, DataFlowGraph, InstructionData, Opcode, InstBuilder};
|
||||||
use ir::condcodes::IntCC;
|
use ir::condcodes::IntCC;
|
||||||
use isa::{TargetIsa, Legalize};
|
use isa::{TargetIsa, Legalize};
|
||||||
|
|
||||||
@@ -27,36 +27,46 @@ pub fn legalize_function(func: &mut Function, isa: &TargetIsa) {
|
|||||||
func.encodings.resize(func.dfg.num_insts());
|
func.encodings.resize(func.dfg.num_insts());
|
||||||
let mut pos = Cursor::new(&mut func.layout);
|
let mut pos = Cursor::new(&mut func.layout);
|
||||||
while let Some(_ebb) = pos.next_ebb() {
|
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() {
|
while let Some(inst) = pos.next_inst() {
|
||||||
match isa.encode(&func.dfg, &func.dfg[inst]) {
|
match isa.encode(&func.dfg, &func.dfg[inst]) {
|
||||||
Ok(encoding) => func.encodings[inst] = encoding,
|
Ok(encoding) => *func.encodings.ensure(inst) = encoding,
|
||||||
Err(Legalize::Expand) => {
|
Err(action) => {
|
||||||
expand(&mut pos, &mut func.dfg);
|
// We should transform the instruction into legal equivalents.
|
||||||
}
|
|
||||||
Err(Legalize::Narrow) => {
|
|
||||||
narrow(&mut pos, &mut func.dfg);
|
|
||||||
}
|
|
||||||
// TODO: We should transform the instruction into legal equivalents.
|
|
||||||
// Possible strategies are:
|
// Possible strategies are:
|
||||||
// 1. Expand instruction into sequence of legal instructions. Possibly
|
// 1. Legalize::Expand: Expand instruction into sequence of legal instructions.
|
||||||
// iteratively.
|
// Possibly iteratively. ()
|
||||||
// 2. Split the controlling type variable into high and low parts. This applies
|
// 2. Legalize::Narrow: Split the controlling type variable into high and low
|
||||||
// both to SIMD vector types which can be halved and to integer types such
|
// parts. This applies both to SIMD vector types which can be halved and to
|
||||||
// as `i64` used on a 32-bit ISA.
|
// integer types such as `i64` used on a 32-bit ISA. ().
|
||||||
// 3. Promote the controlling type variable to a larger type. This typically
|
// 3. TODO: Promote the controlling type variable to a larger type. This
|
||||||
// means expressing `i8` and `i16` arithmetic in terms if `i32` operations
|
// typically means expressing `i8` and `i16` arithmetic in terms if `i32`
|
||||||
// on RISC targets. (It may or may not be beneficial to promote small vector
|
// operations on RISC targets. (It may or may not be beneficial to promote
|
||||||
// types versus splitting them.)
|
// small vector types versus splitting them.)
|
||||||
// 4. Convert to library calls. For example, floating point operations on an
|
// 4. TODO: Convert to library calls. For example, floating point operations on
|
||||||
// ISA with no IEEE 754 support.
|
// an ISA with no IEEE 754 support.
|
||||||
//
|
let changed = match action {
|
||||||
// The iteration scheme used here is not going to cut it. Transforming
|
Legalize::Expand => expand(&mut pos, &mut func.dfg),
|
||||||
// instructions involves changing `function.layout` which is impossiblr while
|
Legalize::Narrow => narrow(&mut pos, &mut func.dfg),
|
||||||
// it is referenced by the two iterators. We need a layout cursor that can
|
};
|
||||||
// maintain a position *and* permit inserting and replacing instructions.
|
// 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
|
// Include legalization patterns that were generated by gen_legalizer.py from the XForms in
|
||||||
|
|||||||
Reference in New Issue
Block a user