cranelift_codegen/
nan_canonicalization.rs1use crate::cursor::{Cursor, FuncCursor};
6use crate::ir::condcodes::FloatCC;
7use crate::ir::immediates::{Ieee32, Ieee64};
8use crate::ir::types;
9use crate::ir::{Function, Inst, InstBuilder, InstructionData, Opcode, Value};
10use crate::opts::MemFlags;
11use crate::timing;
12
13static CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000;
15static CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000;
16
17pub fn do_nan_canonicalization(func: &mut Function) {
19 let _tt = timing::canonicalize_nans();
20 let mut pos = FuncCursor::new(func);
21 while let Some(_block) = pos.next_block() {
22 while let Some(inst) = pos.next_inst() {
23 if is_fp_arith(&mut pos, inst) {
24 add_nan_canon_seq(&mut pos, inst);
25 }
26 }
27 }
28}
29
30fn is_fp_arith(pos: &mut FuncCursor, inst: Inst) -> bool {
34 match pos.func.dfg.insts[inst] {
35 InstructionData::Unary { opcode, .. } => {
36 opcode == Opcode::Ceil
37 || opcode == Opcode::Floor
38 || opcode == Opcode::Nearest
39 || opcode == Opcode::Sqrt
40 || opcode == Opcode::Trunc
41 }
42 InstructionData::Binary { opcode, .. } => {
43 opcode == Opcode::Fadd
44 || opcode == Opcode::Fdiv
45 || opcode == Opcode::Fmax
46 || opcode == Opcode::Fmin
47 || opcode == Opcode::Fmul
48 || opcode == Opcode::Fsub
49 }
50 InstructionData::Ternary { opcode, .. } => opcode == Opcode::Fma,
51 _ => false,
52 }
53}
54
55fn add_nan_canon_seq(pos: &mut FuncCursor, inst: Inst) {
57 let val = pos.func.dfg.first_result(inst);
60 let val_type = pos.func.dfg.value_type(val);
61 let new_res = pos.func.dfg.replace_result(val, val_type);
62 let _next_inst = pos.next_inst().expect("block missing terminator!");
63
64 let is_nan = pos.ins().fcmp(FloatCC::NotEqual, new_res, new_res);
67
68 let scalar_select = |pos: &mut FuncCursor, canon_nan: Value| {
69 pos.ins()
70 .with_result(val)
71 .select(is_nan, canon_nan, new_res);
72 };
73 let vector_select = |pos: &mut FuncCursor, canon_nan: Value| {
74 let is_nan = pos.ins().bitcast(val_type, MemFlags::new(), is_nan);
75 pos.ins()
76 .with_result(val)
77 .bitselect(is_nan, canon_nan, new_res);
78 };
79
80 match val_type {
81 types::F32 => {
82 let canon_nan = pos.ins().f32const(Ieee32::with_bits(CANON_32BIT_NAN));
83 scalar_select(pos, canon_nan);
84 }
85 types::F64 => {
86 let canon_nan = pos.ins().f64const(Ieee64::with_bits(CANON_64BIT_NAN));
87 scalar_select(pos, canon_nan);
88 }
89 types::F32X4 => {
90 let canon_nan = pos.ins().f32const(Ieee32::with_bits(CANON_32BIT_NAN));
91 let canon_nan = pos.ins().splat(types::F32X4, canon_nan);
92 vector_select(pos, canon_nan);
93 }
94 types::F64X2 => {
95 let canon_nan = pos.ins().f64const(Ieee64::with_bits(CANON_64BIT_NAN));
96 let canon_nan = pos.ins().splat(types::F64X2, canon_nan);
97 vector_select(pos, canon_nan);
98 }
99 _ => {
100 panic!("Could not canonicalize NaN: Unexpected result type found.");
102 }
103 }
104
105 pos.prev_inst(); }