use cursor::{Cursor, FuncCursor};
use ir::condcodes::FloatCC;
use ir::immediates::{Ieee32, Ieee64};
use ir::types;
use ir::types::Type;
use ir::{Function, Inst, InstBuilder, InstructionData, Opcode, Value};
use timing;
static CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000;
static CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000;
pub fn do_nan_canonicalization(func: &mut Function) {
let _tt = timing::canonicalize_nans();
let mut pos = FuncCursor::new(func);
while let Some(_ebb) = pos.next_ebb() {
while let Some(inst) = pos.next_inst() {
if is_fp_arith(&mut pos, inst) {
add_nan_canon_seq(&mut pos, inst);
}
}
}
}
fn is_fp_arith(pos: &mut FuncCursor, inst: Inst) -> bool {
match pos.func.dfg[inst] {
InstructionData::Unary { opcode, .. } => {
opcode == Opcode::Ceil
|| opcode == Opcode::Floor
|| opcode == Opcode::Nearest
|| opcode == Opcode::Sqrt
|| opcode == Opcode::Trunc
}
InstructionData::Binary { opcode, .. } => {
opcode == Opcode::Fadd
|| opcode == Opcode::Fdiv
|| opcode == Opcode::Fmax
|| opcode == Opcode::Fmin
|| opcode == Opcode::Fmul
|| opcode == Opcode::Fsub
}
InstructionData::Ternary { opcode, .. } => opcode == Opcode::Fma,
_ => false,
}
}
fn add_nan_canon_seq(pos: &mut FuncCursor, inst: Inst) {
let val = pos.func.dfg.first_result(inst);
let val_type = pos.func.dfg.value_type(val);
let new_res = pos.func.dfg.replace_result(val, val_type);
let _next_inst = pos.next_inst().expect("EBB missing terminator!");
let is_nan = pos.ins().fcmp(FloatCC::NotEqual, new_res, new_res);
let canon_nan = insert_nan_const(pos, val_type);
pos.ins()
.with_result(val)
.select(is_nan, canon_nan, new_res);
pos.prev_inst(); }
fn insert_nan_const(pos: &mut FuncCursor, nan_type: Type) -> Value {
match nan_type {
types::F32 => pos.ins().f32const(Ieee32::with_bits(CANON_32BIT_NAN)),
types::F64 => pos.ins().f64const(Ieee64::with_bits(CANON_64BIT_NAN)),
_ => {
panic!("Could not canonicalize NaN: Unexpected result type found.");
}
}
}