[codegen] Don't simplify an operation if it would result in unnecessary legalization;
Converting something like iadd.i64 on a 32-bits architecture into a iadd_imm.i64 will result in the instruction being legalized back to an iadd.i64 later on, creating unnecessary churn. This commit implements avoid doing so, and changes the target ISA to a 64-bits platform for tests than ran into this, as well as making sure this won't happen on 32-bits platforms.
This commit is contained in:
@@ -243,7 +243,7 @@ impl Context {
|
|||||||
|
|
||||||
/// Perform pre-legalization rewrites on the function.
|
/// Perform pre-legalization rewrites on the function.
|
||||||
pub fn preopt(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
|
pub fn preopt(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
|
||||||
do_preopt(&mut self.func, &mut self.cfg);
|
do_preopt(&mut self.func, &mut self.cfg, isa);
|
||||||
self.verify_if(isa)?;
|
self.verify_if(isa)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::ir::{
|
|||||||
types::{I16, I32, I64, I8},
|
types::{I16, I32, I64, I8},
|
||||||
DataFlowGraph, Ebb, Function, Inst, InstBuilder, InstructionData, Type, Value,
|
DataFlowGraph, Ebb, Function, Inst, InstBuilder, InstructionData, Type, Value,
|
||||||
};
|
};
|
||||||
|
use crate::isa::TargetIsa;
|
||||||
use crate::timing;
|
use crate::timing;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -533,9 +534,14 @@ fn try_fold_extended_move(
|
|||||||
|
|
||||||
/// Apply basic simplifications.
|
/// Apply basic simplifications.
|
||||||
///
|
///
|
||||||
/// This folds constants with arithmetic to form `_imm` instructions, and other
|
/// This folds constants with arithmetic to form `_imm` instructions, and other minor
|
||||||
/// minor simplifications.
|
/// simplifications.
|
||||||
fn simplify(pos: &mut FuncCursor, inst: Inst) {
|
///
|
||||||
|
/// Doesn't apply some simplifications if the native word width (in bytes) is smaller than the
|
||||||
|
/// controlling type's width of the instruction. This would result in an illegal instruction that
|
||||||
|
/// would likely be expanded back into an instruction on smaller types with the same initial
|
||||||
|
/// opcode, creating unnecessary churn.
|
||||||
|
fn simplify(pos: &mut FuncCursor, inst: Inst, native_word_width: u32) {
|
||||||
match pos.func.dfg[inst] {
|
match pos.func.dfg[inst] {
|
||||||
InstructionData::Binary { opcode, args } => {
|
InstructionData::Binary { opcode, args } => {
|
||||||
if let Some(mut imm) = resolve_imm64_value(&pos.func.dfg, args[1]) {
|
if let Some(mut imm) = resolve_imm64_value(&pos.func.dfg, args[1]) {
|
||||||
@@ -562,13 +568,15 @@ fn simplify(pos: &mut FuncCursor, inst: Inst) {
|
|||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let ty = pos.func.dfg.ctrl_typevar(inst);
|
let ty = pos.func.dfg.ctrl_typevar(inst);
|
||||||
|
if ty.bytes() <= native_word_width {
|
||||||
pos.func
|
pos.func
|
||||||
.dfg
|
.dfg
|
||||||
.replace(inst)
|
.replace(inst)
|
||||||
.BinaryImm(new_opcode, ty, imm, args[0]);
|
.BinaryImm(new_opcode, ty, imm, args[0]);
|
||||||
|
|
||||||
// Repeat for BinaryImm simplification.
|
// Repeat for BinaryImm simplification.
|
||||||
simplify(pos, inst);
|
simplify(pos, inst, native_word_width);
|
||||||
|
}
|
||||||
} else if let Some(imm) = resolve_imm64_value(&pos.func.dfg, args[0]) {
|
} else if let Some(imm) = resolve_imm64_value(&pos.func.dfg, args[0]) {
|
||||||
let new_opcode = match opcode {
|
let new_opcode = match opcode {
|
||||||
Opcode::Iadd => Opcode::IaddImm,
|
Opcode::Iadd => Opcode::IaddImm,
|
||||||
@@ -580,12 +588,14 @@ fn simplify(pos: &mut FuncCursor, inst: Inst) {
|
|||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let ty = pos.func.dfg.ctrl_typevar(inst);
|
let ty = pos.func.dfg.ctrl_typevar(inst);
|
||||||
|
if ty.bytes() <= native_word_width {
|
||||||
pos.func
|
pos.func
|
||||||
.dfg
|
.dfg
|
||||||
.replace(inst)
|
.replace(inst)
|
||||||
.BinaryImm(new_opcode, ty, imm, args[1]);
|
.BinaryImm(new_opcode, ty, imm, args[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InstructionData::Unary { opcode, arg } => match opcode {
|
InstructionData::Unary { opcode, arg } => match opcode {
|
||||||
Opcode::AdjustSpDown => {
|
Opcode::AdjustSpDown => {
|
||||||
@@ -643,7 +653,9 @@ fn simplify(pos: &mut FuncCursor, inst: Inst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opcode::UshrImm | Opcode::SshrImm => {
|
Opcode::UshrImm | Opcode::SshrImm => {
|
||||||
if try_fold_extended_move(pos, inst, opcode, arg, imm) {
|
if pos.func.dfg.ctrl_typevar(inst).bytes() <= native_word_width
|
||||||
|
&& try_fold_extended_move(pos, inst, opcode, arg, imm)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -686,9 +698,11 @@ fn simplify(pos: &mut FuncCursor, inst: Inst) {
|
|||||||
InstructionData::IntCompare { opcode, cond, args } => {
|
InstructionData::IntCompare { opcode, cond, args } => {
|
||||||
debug_assert_eq!(opcode, Opcode::Icmp);
|
debug_assert_eq!(opcode, Opcode::Icmp);
|
||||||
if let Some(imm) = resolve_imm64_value(&pos.func.dfg, args[1]) {
|
if let Some(imm) = resolve_imm64_value(&pos.func.dfg, args[1]) {
|
||||||
|
if pos.func.dfg.ctrl_typevar(inst).bytes() <= native_word_width {
|
||||||
pos.func.dfg.replace(inst).icmp_imm(cond, args[0], imm);
|
pos.func.dfg.replace(inst).icmp_imm(cond, args[0], imm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InstructionData::CondTrap { .. }
|
InstructionData::CondTrap { .. }
|
||||||
| InstructionData::Branch { .. }
|
| InstructionData::Branch { .. }
|
||||||
@@ -937,13 +951,14 @@ fn branch_order(pos: &mut FuncCursor, cfg: &mut ControlFlowGraph, ebb: Ebb, inst
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The main pre-opt pass.
|
/// The main pre-opt pass.
|
||||||
pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph) {
|
pub fn do_preopt(func: &mut Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) {
|
||||||
let _tt = timing::preopt();
|
let _tt = timing::preopt();
|
||||||
let mut pos = FuncCursor::new(func);
|
let mut pos = FuncCursor::new(func);
|
||||||
|
let native_word_width = isa.pointer_bytes();
|
||||||
while let Some(ebb) = pos.next_ebb() {
|
while let Some(ebb) = pos.next_ebb() {
|
||||||
while let Some(inst) = pos.next_inst() {
|
while let Some(inst) = pos.next_inst() {
|
||||||
// Apply basic simplifications.
|
// Apply basic simplifications.
|
||||||
simplify(&mut pos, inst);
|
simplify(&mut pos, inst, native_word_width as u32);
|
||||||
|
|
||||||
// Try to transform divide-by-constant into simpler operations.
|
// Try to transform divide-by-constant into simpler operations.
|
||||||
if let Some(divrem_info) = get_div_info(inst, &pos.func.dfg) {
|
if let Some(divrem_info) = get_div_info(inst, &pos.func.dfg) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
test simple_preopt
|
test simple_preopt
|
||||||
target i686 baseline
|
target x86_64 baseline
|
||||||
|
|
||||||
; Cases where the denominator is created by an iconst
|
; Cases where the denominator is created by an iconst
|
||||||
|
|
||||||
|
|||||||
61
cranelift/filetests/filetests/simple_preopt/simplify32.clif
Normal file
61
cranelift/filetests/filetests/simple_preopt/simplify32.clif
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
test simple_preopt
|
||||||
|
target i686
|
||||||
|
|
||||||
|
;; 32-bits platforms.
|
||||||
|
|
||||||
|
function %iadd_imm(i32) -> i32 {
|
||||||
|
ebb0(v0: i32):
|
||||||
|
v1 = iconst.i32 2
|
||||||
|
v2 = iadd v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; sameln: function %iadd_imm
|
||||||
|
; nextln: ebb0(v0: i32):
|
||||||
|
; nextln: v1 = iconst.i32 2
|
||||||
|
; nextln: v2 = iadd_imm v0, 2
|
||||||
|
; nextln: return v2
|
||||||
|
; nextln: }
|
||||||
|
|
||||||
|
function %isub_imm(i32) -> i32 {
|
||||||
|
ebb0(v0: i32):
|
||||||
|
v1 = iconst.i32 2
|
||||||
|
v2 = isub v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; sameln: function %isub_imm
|
||||||
|
; nextln: ebb0(v0: i32):
|
||||||
|
; nextln: v1 = iconst.i32 2
|
||||||
|
; nextln: v2 = iadd_imm v0, -2
|
||||||
|
; nextln: return v2
|
||||||
|
; nextln: }
|
||||||
|
|
||||||
|
function %icmp_imm(i32) -> i32 {
|
||||||
|
ebb0(v0: i32):
|
||||||
|
v1 = iconst.i32 2
|
||||||
|
v2 = icmp slt v0, v1
|
||||||
|
v3 = bint.i32 v2
|
||||||
|
return v3
|
||||||
|
}
|
||||||
|
; sameln: function %icmp_imm
|
||||||
|
; nextln: ebb0(v0: i32):
|
||||||
|
; nextln: v1 = iconst.i32 2
|
||||||
|
; nextln: v2 = icmp_imm slt v0, 2
|
||||||
|
; nextln: v3 = bint.i32 v2
|
||||||
|
; nextln: return v3
|
||||||
|
; nextln: }
|
||||||
|
|
||||||
|
;; Don't simplify operations that would get illegal because of lack of native
|
||||||
|
;; support.
|
||||||
|
function %iadd_imm(i64) -> i64 {
|
||||||
|
ebb0(v0: i64):
|
||||||
|
v1 = iconst.i64 2
|
||||||
|
v2 = iadd v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; sameln: function %iadd_imm
|
||||||
|
; nextln: ebb0(v0: i64):
|
||||||
|
; nextln: v1 = iconst.i64 2
|
||||||
|
; nextln: v2 = iadd v0, v1
|
||||||
|
; nextln: return v2
|
||||||
|
; nextln: }
|
||||||
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
test simple_preopt
|
test simple_preopt
|
||||||
target i686
|
target x86_64
|
||||||
|
|
||||||
|
;; 64-bits platforms.
|
||||||
|
|
||||||
function %iadd_imm(i32) -> i32 {
|
function %iadd_imm(i32) -> i32 {
|
||||||
ebb0(v0: i32):
|
ebb0(v0: i32):
|
||||||
Reference in New Issue
Block a user