use crate::bitset::BitSet;
use crate::cursor::{Cursor, FuncCursor};
use crate::flowgraph::ControlFlowGraph;
use crate::ir::types::I32;
use crate::ir::{self, InstBuilder, MemFlags};
use crate::isa::TargetIsa;
use crate::timing;
mod boundary;
mod call;
mod globalvalue;
mod heap;
mod libcall;
mod split;
mod table;
use self::call::expand_call;
use self::globalvalue::expand_global_value;
use self::heap::expand_heap_addr;
use self::libcall::expand_as_libcall;
use self::table::expand_table_addr;
fn legalize_inst(
inst: ir::Inst,
pos: &mut FuncCursor,
cfg: &mut ControlFlowGraph,
isa: &dyn TargetIsa,
) -> bool {
let opcode = pos.func.dfg[inst].opcode();
if opcode.is_call() {
if boundary::handle_call_abi(inst, pos.func, cfg) {
return true;
}
} else if opcode.is_return() {
if boundary::handle_return_abi(inst, pos.func, cfg) {
return true;
}
} else if opcode.is_branch() {
split::simplify_branch_arguments(&mut pos.func.dfg, inst);
}
match pos.func.update_encoding(inst, isa) {
Ok(()) => false,
Err(action) => {
if action(inst, pos.func, cfg, isa) {
return true;
}
expand_as_libcall(inst, pos.func, isa)
}
}
}
pub fn legalize_function(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) {
let _tt = timing::legalize();
debug_assert!(cfg.is_valid());
boundary::legalize_signatures(func, isa);
func.encodings.resize(func.dfg.num_insts());
let mut pos = FuncCursor::new(func);
while let Some(_ebb) = pos.next_ebb() {
let mut prev_pos = pos.position();
while let Some(inst) = pos.next_inst() {
if legalize_inst(inst, &mut pos, cfg, isa) {
pos.set_position(prev_pos);
} else {
prev_pos = pos.position();
}
}
}
if !isa.flags().jump_tables_enabled() {
pos.func.jump_tables.clear();
}
}
include!(concat!(env!("OUT_DIR"), "/legalizer.rs"));
fn expand_cond_trap(
inst: ir::Inst,
func: &mut ir::Function,
cfg: &mut ControlFlowGraph,
_isa: &dyn TargetIsa,
) {
let trapz;
let (arg, code) = match func.dfg[inst] {
ir::InstructionData::CondTrap { opcode, arg, code } => {
trapz = match opcode {
ir::Opcode::Trapz => true,
ir::Opcode::Trapnz => false,
_ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
};
(arg, code)
}
_ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
};
let old_ebb = func.layout.pp_ebb(inst);
let new_ebb = func.dfg.make_ebb();
if trapz {
func.dfg.replace(inst).brnz(arg, new_ebb, &[]);
} else {
func.dfg.replace(inst).brz(arg, new_ebb, &[]);
}
let mut pos = FuncCursor::new(func).after_inst(inst);
pos.use_srcloc(inst);
pos.ins().trap(code);
pos.insert_ebb(new_ebb);
cfg.recompute_ebb(pos.func, old_ebb);
cfg.recompute_ebb(pos.func, new_ebb);
}
fn expand_br_table(
inst: ir::Inst,
func: &mut ir::Function,
cfg: &mut ControlFlowGraph,
isa: &dyn TargetIsa,
) {
if isa.flags().jump_tables_enabled() {
expand_br_table_jt(inst, func, cfg, isa);
} else {
expand_br_table_conds(inst, func, cfg, isa);
}
}
fn expand_br_table_jt(
inst: ir::Inst,
func: &mut ir::Function,
cfg: &mut ControlFlowGraph,
isa: &dyn TargetIsa,
) {
use crate::ir::condcodes::IntCC;
let (arg, default_ebb, table) = match func.dfg[inst] {
ir::InstructionData::BranchTable {
opcode: ir::Opcode::BrTable,
arg,
destination,
table,
} => (arg, destination, table),
_ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)),
};
let table_size = func.jump_tables[table].len();
let addr_ty = isa.pointer_type();
let entry_ty = I32;
let mut pos = FuncCursor::new(func).at_inst(inst);
pos.use_srcloc(inst);
let oob = pos
.ins()
.icmp_imm(IntCC::UnsignedGreaterThanOrEqual, arg, table_size as i64);
pos.ins().brnz(oob, default_ebb, &[]);
let base_addr = pos.ins().jump_table_base(addr_ty, table);
let entry = pos
.ins()
.jump_table_entry(addr_ty, arg, base_addr, entry_ty.bytes() as u8, table);
let addr = pos.ins().iadd(base_addr, entry);
pos.ins().indirect_jump_table_br(addr, table);
let ebb = pos.current_ebb().unwrap();
pos.remove_inst();
cfg.recompute_ebb(pos.func, ebb);
}
fn expand_br_table_conds(
inst: ir::Inst,
func: &mut ir::Function,
cfg: &mut ControlFlowGraph,
_isa: &dyn TargetIsa,
) {
use crate::ir::condcodes::IntCC;
let (arg, default_ebb, table) = match func.dfg[inst] {
ir::InstructionData::BranchTable {
opcode: ir::Opcode::BrTable,
arg,
destination,
table,
} => (arg, destination, table),
_ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)),
};
let table_size = func.jump_tables[table].len();
let mut pos = FuncCursor::new(func).at_inst(inst);
pos.use_srcloc(inst);
for i in 0..table_size {
let dest = pos.func.jump_tables[table].as_slice()[i];
let t = pos.ins().icmp_imm(IntCC::Equal, arg, i as i64);
pos.ins().brnz(t, dest, &[]);
}
pos.ins().jump(default_ebb, &[]);
let ebb = pos.current_ebb().unwrap();
pos.remove_inst();
cfg.recompute_ebb(pos.func, ebb);
}
fn expand_select(
inst: ir::Inst,
func: &mut ir::Function,
cfg: &mut ControlFlowGraph,
_isa: &dyn TargetIsa,
) {
let (ctrl, tval, fval) = match func.dfg[inst] {
ir::InstructionData::Ternary {
opcode: ir::Opcode::Select,
args,
} => (args[0], args[1], args[2]),
_ => panic!("Expected select: {}", func.dfg.display_inst(inst, None)),
};
let old_ebb = func.layout.pp_ebb(inst);
let result = func.dfg.first_result(inst);
func.dfg.clear_results(inst);
let new_ebb = func.dfg.make_ebb();
func.dfg.attach_ebb_param(new_ebb, result);
func.dfg.replace(inst).brnz(ctrl, new_ebb, &[tval]);
let mut pos = FuncCursor::new(func).after_inst(inst);
pos.use_srcloc(inst);
pos.ins().jump(new_ebb, &[fval]);
pos.insert_ebb(new_ebb);
cfg.recompute_ebb(pos.func, new_ebb);
cfg.recompute_ebb(pos.func, old_ebb);
}
fn expand_br_icmp(
inst: ir::Inst,
func: &mut ir::Function,
cfg: &mut ControlFlowGraph,
_isa: &dyn TargetIsa,
) {
let (cond, a, b, destination, ebb_args) = match func.dfg[inst] {
ir::InstructionData::BranchIcmp {
cond,
destination,
ref args,
..
} => (
cond,
args.get(0, &func.dfg.value_lists).unwrap(),
args.get(1, &func.dfg.value_lists).unwrap(),
destination,
args.as_slice(&func.dfg.value_lists)[2..].to_vec(),
),
_ => panic!("Expected br_icmp {}", func.dfg.display_inst(inst, None)),
};
let old_ebb = func.layout.pp_ebb(inst);
func.dfg.clear_results(inst);
let icmp_res = func.dfg.replace(inst).icmp(cond, a, b);
let mut pos = FuncCursor::new(func).after_inst(inst);
pos.use_srcloc(inst);
pos.ins().brnz(icmp_res, destination, &ebb_args);
cfg.recompute_ebb(pos.func, destination);
cfg.recompute_ebb(pos.func, old_ebb);
}
fn expand_fconst(
inst: ir::Inst,
func: &mut ir::Function,
_cfg: &mut ControlFlowGraph,
_isa: &dyn TargetIsa,
) {
let ty = func.dfg.value_type(func.dfg.first_result(inst));
debug_assert!(!ty.is_vector(), "Only scalar fconst supported: {}", ty);
let mut pos = FuncCursor::new(func).at_inst(inst);
pos.use_srcloc(inst);
let ival = match pos.func.dfg[inst] {
ir::InstructionData::UnaryIeee32 {
opcode: ir::Opcode::F32const,
imm,
} => pos.ins().iconst(ir::types::I32, i64::from(imm.bits())),
ir::InstructionData::UnaryIeee64 {
opcode: ir::Opcode::F64const,
imm,
} => pos.ins().iconst(ir::types::I64, imm.bits() as i64),
_ => panic!("Expected fconst: {}", pos.func.dfg.display_inst(inst, None)),
};
pos.func.dfg.replace(inst).bitcast(ty, ival);
}
fn expand_stack_load(
inst: ir::Inst,
func: &mut ir::Function,
_cfg: &mut ControlFlowGraph,
isa: &dyn TargetIsa,
) {
let ty = func.dfg.value_type(func.dfg.first_result(inst));
let addr_ty = isa.pointer_type();
let mut pos = FuncCursor::new(func).at_inst(inst);
pos.use_srcloc(inst);
let (stack_slot, offset) = match pos.func.dfg[inst] {
ir::InstructionData::StackLoad {
opcode: _opcode,
stack_slot,
offset,
} => (stack_slot, offset),
_ => panic!(
"Expected stack_load: {}",
pos.func.dfg.display_inst(inst, None)
),
};
let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
let mflags = MemFlags::trusted();
pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);
}
fn expand_stack_store(
inst: ir::Inst,
func: &mut ir::Function,
_cfg: &mut ControlFlowGraph,
isa: &dyn TargetIsa,
) {
let addr_ty = isa.pointer_type();
let mut pos = FuncCursor::new(func).at_inst(inst);
pos.use_srcloc(inst);
let (val, stack_slot, offset) = match pos.func.dfg[inst] {
ir::InstructionData::StackStore {
opcode: _opcode,
arg,
stack_slot,
offset,
} => (arg, stack_slot, offset),
_ => panic!(
"Expected stack_store: {}",
pos.func.dfg.display_inst(inst, None)
),
};
let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
let mut mflags = MemFlags::new();
mflags.set_notrap();
mflags.set_aligned();
pos.func.dfg.replace(inst).store(mflags, val, addr, 0);
}