We have some operations defined on DataFlowGraph purely to work around borrow-checker issues with InstructionData and other data on DataFlowGraph. Part of the problem is that indexing the DFG directly hides the fact that we're only indexing the insts field of the DFG. This PR makes the insts field of the DFG public, but wraps it in a newtype that only allows indexing. This means that the borrow checker is better able to tell when operations on memory held by the DFG won't conflict, which comes up frequently when mutating ValueLists held by InstructionData.
99 lines
3.4 KiB
Rust
99 lines
3.4 KiB
Rust
use crate::{FuzzGen, Type};
|
|
use anyhow::Result;
|
|
use cranelift::codegen::cursor::{Cursor, FuncCursor};
|
|
use cranelift::codegen::ir::{Function, Inst, Opcode};
|
|
use cranelift::prelude::{types::*, *};
|
|
|
|
pub fn do_fcvt_trap_pass(fuzz: &mut FuzzGen, func: &mut Function) -> Result<()> {
|
|
let ratio = fuzz.config.allowed_fcvt_traps_ratio;
|
|
let insert_seq = !fuzz.u.ratio(ratio.0, ratio.1)?;
|
|
if !insert_seq {
|
|
return Ok(());
|
|
}
|
|
|
|
let mut pos = FuncCursor::new(func);
|
|
while let Some(_block) = pos.next_block() {
|
|
while let Some(inst) = pos.next_inst() {
|
|
if can_fcvt_trap(&pos, inst) {
|
|
insert_fcvt_sequence(&mut pos, inst);
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Returns true/false if this instruction can trap
|
|
fn can_fcvt_trap(pos: &FuncCursor, inst: Inst) -> bool {
|
|
let opcode = pos.func.dfg.insts[inst].opcode();
|
|
|
|
matches!(opcode, Opcode::FcvtToUint | Opcode::FcvtToSint)
|
|
}
|
|
|
|
/// Gets the max and min float values for this integer type
|
|
/// Inserts fconst instructions with these values.
|
|
//
|
|
// When converting to integers, floats are truncated. This means that the maximum float value
|
|
// that can be converted into an i8 is 127.99999. And surprisingly the minimum float for an
|
|
// u8 is -0.99999! So get the limits of this type as a float value by adding or subtracting
|
|
// 1.0 from its min and max integer values.
|
|
fn float_limits(
|
|
pos: &mut FuncCursor,
|
|
float_ty: Type,
|
|
int_ty: Type,
|
|
is_signed: bool,
|
|
) -> (Value, Value) {
|
|
let (min_int, max_int) = int_ty.bounds(is_signed);
|
|
|
|
if float_ty == F32 {
|
|
let (min, max) = if is_signed {
|
|
((min_int as i128) as f32, (max_int as i128) as f32)
|
|
} else {
|
|
(min_int as f32, max_int as f32)
|
|
};
|
|
|
|
(pos.ins().f32const(min - 1.0), pos.ins().f32const(max + 1.0))
|
|
} else {
|
|
let (min, max) = if is_signed {
|
|
((min_int as i128) as f64, (max_int as i128) as f64)
|
|
} else {
|
|
(min_int as f64, max_int as f64)
|
|
};
|
|
|
|
(pos.ins().f64const(min - 1.0), pos.ins().f64const(max + 1.0))
|
|
}
|
|
}
|
|
|
|
/// Prepend instructions to inst to avoid traps
|
|
fn insert_fcvt_sequence(pos: &mut FuncCursor, inst: Inst) {
|
|
let dfg = &pos.func.dfg;
|
|
let opcode = dfg.insts[inst].opcode();
|
|
let arg = dfg.inst_args(inst)[0];
|
|
let float_ty = dfg.value_type(arg);
|
|
let int_ty = dfg.value_type(dfg.first_result(inst));
|
|
|
|
// These instructions trap on NaN
|
|
let is_nan = pos.ins().fcmp(FloatCC::NotEqual, arg, arg);
|
|
|
|
// They also trap if the value is larger or smaller than what the integer type can represent. So
|
|
// we generate the maximum and minimum float value that would make this trap, and compare against
|
|
// those limits.
|
|
let is_signed = opcode == Opcode::FcvtToSint;
|
|
let (min, max) = float_limits(pos, float_ty, int_ty, is_signed);
|
|
let underflows = pos.ins().fcmp(FloatCC::LessThanOrEqual, arg, min);
|
|
let overflows = pos.ins().fcmp(FloatCC::GreaterThanOrEqual, arg, max);
|
|
|
|
// Check the previous conditions and replace with a 1.0 if this instruction would trap
|
|
let overflows_int = pos.ins().bor(underflows, overflows);
|
|
let is_invalid = pos.ins().bor(is_nan, overflows_int);
|
|
|
|
let one = if float_ty == F32 {
|
|
pos.ins().f32const(1.0)
|
|
} else {
|
|
pos.ins().f64const(1.0)
|
|
};
|
|
let new_arg = pos.ins().select(is_invalid, one, arg);
|
|
|
|
// Replace the previous arg with the new one
|
|
pos.func.dfg.inst_args_mut(inst)[0] = new_arg;
|
|
}
|