Path: blob/main/cranelift/fuzzgen/src/passes/fcvt.rs
1692 views
use crate::FuzzGen;1use anyhow::Result;2use cranelift::codegen::cursor::{Cursor, FuncCursor};3use cranelift::codegen::ir::{Function, Inst, Opcode};4use cranelift::prelude::{types::*, *};56pub fn do_fcvt_trap_pass(fuzz: &mut FuzzGen, func: &mut Function) -> Result<()> {7let ratio = fuzz.config.allowed_fcvt_traps_ratio;8let insert_seq = !fuzz.u.ratio(ratio.0, ratio.1)?;9if !insert_seq {10return Ok(());11}1213let mut pos = FuncCursor::new(func);14while let Some(_block) = pos.next_block() {15while let Some(inst) = pos.next_inst() {16if can_fcvt_trap(&pos, inst) {17insert_fcvt_sequence(&mut pos, inst);18}19}20}21Ok(())22}2324/// Returns true/false if this instruction can trap25fn can_fcvt_trap(pos: &FuncCursor, inst: Inst) -> bool {26let opcode = pos.func.dfg.insts[inst].opcode();2728matches!(opcode, Opcode::FcvtToUint | Opcode::FcvtToSint)29}3031/// Gets the max and min float values for this integer type32/// Inserts fconst instructions with these values.33//34// When converting to integers, floats are truncated. This means that the maximum float value35// that can be converted into an i8 is 127.99999. And surprisingly the minimum float for an36// u8 is -0.99999! So get the limits of this type as a float value by adding or subtracting37// 1.0 from its min and max integer values.38fn float_limits(39pos: &mut FuncCursor,40float_ty: Type,41int_ty: Type,42is_signed: bool,43) -> (Value, Value) {44let (min_int, max_int) = int_ty.bounds(is_signed);4546if float_ty == F32 {47let (min, max) = if is_signed {48((min_int as i128) as f32, (max_int as i128) as f32)49} else {50(min_int as f32, max_int as f32)51};5253(pos.ins().f32const(min - 1.0), pos.ins().f32const(max + 1.0))54} else {55let (min, max) = if is_signed {56((min_int as i128) as f64, (max_int as i128) as f64)57} else {58(min_int as f64, max_int as f64)59};6061(pos.ins().f64const(min - 1.0), pos.ins().f64const(max + 1.0))62}63}6465/// Prepend instructions to inst to avoid traps66fn insert_fcvt_sequence(pos: &mut FuncCursor, inst: Inst) {67let dfg = &pos.func.dfg;68let opcode = dfg.insts[inst].opcode();69let arg = dfg.inst_args(inst)[0];70let float_ty = dfg.value_type(arg);71let int_ty = dfg.value_type(dfg.first_result(inst));7273// These instructions trap on NaN74let is_nan = pos.ins().fcmp(FloatCC::NotEqual, arg, arg);7576// They also trap if the value is larger or smaller than what the integer type can represent. So77// we generate the maximum and minimum float value that would make this trap, and compare against78// those limits.79let is_signed = opcode == Opcode::FcvtToSint;80let (min, max) = float_limits(pos, float_ty, int_ty, is_signed);81let underflows = pos.ins().fcmp(FloatCC::LessThanOrEqual, arg, min);82let overflows = pos.ins().fcmp(FloatCC::GreaterThanOrEqual, arg, max);8384// Check the previous conditions and replace with a 1.0 if this instruction would trap85let overflows_int = pos.ins().bor(underflows, overflows);86let is_invalid = pos.ins().bor(is_nan, overflows_int);8788let one = if float_ty == F32 {89pos.ins().f32const(1.0)90} else {91pos.ins().f64const(1.0)92};93let new_arg = pos.ins().select(is_invalid, one, arg);9495// Replace the previous arg with the new one96pos.func.dfg.inst_args_mut(inst)[0] = new_arg;97}9899100