Path: blob/main/cranelift/codegen/src/nan_canonicalization.rs
3069 views
//! A NaN-canonicalizing rewriting pass. Patch floating point arithmetic1//! instructions that may return a NaN result with a sequence of operations2//! that will replace nondeterministic NaN's with a single canonical NaN value.34use crate::cursor::{Cursor, FuncCursor};5use crate::ir::condcodes::FloatCC;6use crate::ir::immediates::{Ieee16, Ieee32, Ieee64, Ieee128};7use crate::ir::types::{self};8use crate::ir::{Function, Inst, InstBuilder, InstructionData, Opcode, Value};9use crate::opts::MemFlags;10use crate::timing;1112/// Perform the NaN canonicalization pass.13pub fn do_nan_canonicalization(func: &mut Function, has_vector_support: bool) {14let _tt = timing::canonicalize_nans();15let mut pos = FuncCursor::new(func);16while let Some(_block) = pos.next_block() {17while let Some(inst) = pos.next_inst() {18if is_fp_arith(&mut pos, inst) {19add_nan_canon_seq(&mut pos, inst, has_vector_support);20}21}22}23}2425/// Returns true/false based on whether the instruction is a floating-point26/// arithmetic operation. This ignores operations like `fneg`, `fabs`, or27/// `fcopysign` that only operate on the sign bit of a floating point value.28fn is_fp_arith(pos: &mut FuncCursor, inst: Inst) -> bool {29match pos.func.dfg.insts[inst] {30InstructionData::Unary { opcode, .. } => {31opcode == Opcode::Ceil32|| opcode == Opcode::Floor33|| opcode == Opcode::Nearest34|| opcode == Opcode::Sqrt35|| opcode == Opcode::Trunc36|| opcode == Opcode::Fdemote37|| opcode == Opcode::Fpromote38|| opcode == Opcode::FvpromoteLow39|| opcode == Opcode::Fvdemote40}41InstructionData::Binary { opcode, .. } => {42opcode == Opcode::Fadd43|| opcode == Opcode::Fdiv44|| opcode == Opcode::Fmax45|| opcode == Opcode::Fmin46|| opcode == Opcode::Fmul47|| opcode == Opcode::Fsub48}49InstructionData::Ternary { opcode, .. } => opcode == Opcode::Fma,50_ => false,51}52}5354/// Append a sequence of canonicalizing instructions after the given instruction.55fn add_nan_canon_seq(pos: &mut FuncCursor, inst: Inst, has_vector_support: bool) {56// Select the instruction result, result type. Replace the instruction57// result and step forward before inserting the canonicalization sequence.58let val = pos.func.dfg.first_result(inst);59let val_type = pos.func.dfg.value_type(val);60let new_res = pos.func.dfg.replace_result(val, val_type);61let _next_inst = pos.next_inst().expect("block missing terminator!");6263// Insert a comparison instruction, to check if `inst_res` is NaN (comparing64// against NaN is always unordered). Select the canonical NaN value if `val`65// is NaN, assign the result to `inst`.66let comparison = FloatCC::Unordered;6768let vectorized_scalar_select = |pos: &mut FuncCursor, canon_nan: Value, ty: types::Type| {69let canon_nan = pos.ins().scalar_to_vector(ty, canon_nan);70let new_res = pos.ins().scalar_to_vector(ty, new_res);71let is_nan = pos.ins().fcmp(comparison, new_res, new_res);72let is_nan = pos.ins().bitcast(ty, MemFlags::new(), is_nan);73let simd_result = pos.ins().bitselect(is_nan, canon_nan, new_res);74pos.ins().with_result(val).extractlane(simd_result, 0);75};76let scalar_select = |pos: &mut FuncCursor, canon_nan: Value| {77let is_nan = pos.ins().fcmp(comparison, new_res, new_res);78pos.ins()79.with_result(val)80.select(is_nan, canon_nan, new_res);81};8283let vector_select = |pos: &mut FuncCursor, canon_nan: Value| {84let is_nan = pos.ins().fcmp(comparison, new_res, new_res);85let is_nan = pos.ins().bitcast(val_type, MemFlags::new(), is_nan);86pos.ins()87.with_result(val)88.bitselect(is_nan, canon_nan, new_res);89};9091match val_type {92types::F16 => {93let canon_nan = pos.ins().f16const(Ieee16::NAN);94scalar_select(pos, canon_nan);95}96types::F32 => {97let canon_nan = pos.ins().f32const(Ieee32::NAN);98if has_vector_support {99vectorized_scalar_select(pos, canon_nan, types::F32X4);100} else {101scalar_select(pos, canon_nan);102}103}104types::F64 => {105let canon_nan = pos.ins().f64const(Ieee64::NAN);106if has_vector_support {107vectorized_scalar_select(pos, canon_nan, types::F64X2);108} else {109scalar_select(pos, canon_nan);110}111}112types::F32X4 => {113let canon_nan = pos.ins().f32const(Ieee32::NAN);114let canon_nan = pos.ins().splat(types::F32X4, canon_nan);115vector_select(pos, canon_nan);116}117types::F64X2 => {118let canon_nan = pos.ins().f64const(Ieee64::NAN);119let canon_nan = pos.ins().splat(types::F64X2, canon_nan);120vector_select(pos, canon_nan);121}122types::F128 => {123let nan_const = pos.func.dfg.constants.insert(Ieee128::NAN.into());124let canon_nan = pos.ins().f128const(nan_const);125scalar_select(pos, canon_nan);126}127_ => {128// Panic if the type given was not an IEEE floating point type.129panic!("Could not canonicalize NaN: Unexpected result type found.");130}131}132133pos.prev_inst(); // Step backwards so the pass does not skip instructions.134}135136137