fuzzgen: Add fcvt_* ops (#4958)
This commit is contained in:
@@ -47,6 +47,13 @@ pub struct Config {
|
|||||||
/// that avoids these issues. However we can allow some `int_divz` traps
|
/// that avoids these issues. However we can allow some `int_divz` traps
|
||||||
/// by controlling this config.
|
/// by controlling this config.
|
||||||
pub allowed_int_divz_ratio: (usize, usize),
|
pub allowed_int_divz_ratio: (usize, usize),
|
||||||
|
|
||||||
|
/// How often should we allow fcvt related traps.
|
||||||
|
///
|
||||||
|
/// `Fcvt*` instructions fail under some inputs, most commonly NaN's.
|
||||||
|
/// We insert a checking sequence to guarantee that those inputs never make
|
||||||
|
/// it to the instruction, but sometimes we want to allow them.
|
||||||
|
pub allowed_fcvt_traps_ratio: (usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -71,6 +78,7 @@ impl Default for Config {
|
|||||||
// impact execs/s
|
// impact execs/s
|
||||||
backwards_branch_ratio: (1, 1000),
|
backwards_branch_ratio: (1, 1000),
|
||||||
allowed_int_divz_ratio: (1, 1_000_000),
|
allowed_int_divz_ratio: (1, 1_000_000),
|
||||||
|
allowed_fcvt_traps_ratio: (1, 1_000_000),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -486,6 +486,122 @@ const OPCODE_SIGNATURES: &'static [(
|
|||||||
// Nearest
|
// Nearest
|
||||||
(Opcode::Nearest, &[F32], &[F32], insert_opcode),
|
(Opcode::Nearest, &[F32], &[F32], insert_opcode),
|
||||||
(Opcode::Nearest, &[F64], &[F64], insert_opcode),
|
(Opcode::Nearest, &[F64], &[F64], insert_opcode),
|
||||||
|
// FcvtToUint
|
||||||
|
// TODO: Some ops disabled:
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4897
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4899
|
||||||
|
// aarch64: https://github.com/bytecodealliance/wasmtime/issues/4934
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToUint, &[F32], &[I8], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToUint, &[F32], &[I16], insert_opcode),
|
||||||
|
(Opcode::FcvtToUint, &[F32], &[I32], insert_opcode),
|
||||||
|
(Opcode::FcvtToUint, &[F32], &[I64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtToUint, &[F32], &[I128], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToUint, &[F64], &[I8], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToUint, &[F64], &[I16], insert_opcode),
|
||||||
|
(Opcode::FcvtToUint, &[F64], &[I32], insert_opcode),
|
||||||
|
(Opcode::FcvtToUint, &[F64], &[I64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtToUint, &[F64], &[I128], insert_opcode),
|
||||||
|
// FcvtToUintSat
|
||||||
|
// TODO: Some ops disabled:
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4897
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4899
|
||||||
|
// aarch64: https://github.com/bytecodealliance/wasmtime/issues/4934
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToUintSat, &[F32], &[I8], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToUintSat, &[F32], &[I16], insert_opcode),
|
||||||
|
(Opcode::FcvtToUintSat, &[F32], &[I32], insert_opcode),
|
||||||
|
(Opcode::FcvtToUintSat, &[F32], &[I64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtToUintSat, &[F32], &[I128], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToUintSat, &[F64], &[I8], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToUintSat, &[F64], &[I16], insert_opcode),
|
||||||
|
(Opcode::FcvtToUintSat, &[F64], &[I32], insert_opcode),
|
||||||
|
(Opcode::FcvtToUintSat, &[F64], &[I64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtToUintSat, &[F64], &[I128], insert_opcode),
|
||||||
|
// FcvtToSint
|
||||||
|
// TODO: Some ops disabled:
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4897
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4899
|
||||||
|
// aarch64: https://github.com/bytecodealliance/wasmtime/issues/4934
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToSint, &[F32], &[I8], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToSint, &[F32], &[I16], insert_opcode),
|
||||||
|
(Opcode::FcvtToSint, &[F32], &[I32], insert_opcode),
|
||||||
|
(Opcode::FcvtToSint, &[F32], &[I64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtToSint, &[F32], &[I128], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToSint, &[F64], &[I8], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToSint, &[F64], &[I16], insert_opcode),
|
||||||
|
(Opcode::FcvtToSint, &[F64], &[I32], insert_opcode),
|
||||||
|
(Opcode::FcvtToSint, &[F64], &[I64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtToSint, &[F64], &[I128], insert_opcode),
|
||||||
|
// FcvtToSintSat
|
||||||
|
// TODO: Some ops disabled:
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4897
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4899
|
||||||
|
// aarch64: https://github.com/bytecodealliance/wasmtime/issues/4934
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToSintSat, &[F32], &[I8], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToSintSat, &[F32], &[I16], insert_opcode),
|
||||||
|
(Opcode::FcvtToSintSat, &[F32], &[I32], insert_opcode),
|
||||||
|
(Opcode::FcvtToSintSat, &[F32], &[I64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtToSintSat, &[F32], &[I128], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToSintSat, &[F64], &[I8], insert_opcode),
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
(Opcode::FcvtToSintSat, &[F64], &[I16], insert_opcode),
|
||||||
|
(Opcode::FcvtToSintSat, &[F64], &[I32], insert_opcode),
|
||||||
|
(Opcode::FcvtToSintSat, &[F64], &[I64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtToSintSat, &[F64], &[I128], insert_opcode),
|
||||||
|
// FcvtFromUint
|
||||||
|
// TODO: Some ops disabled:
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4900
|
||||||
|
// aarch64: https://github.com/bytecodealliance/wasmtime/issues/4933
|
||||||
|
(Opcode::FcvtFromUint, &[I8], &[F32], insert_opcode),
|
||||||
|
(Opcode::FcvtFromUint, &[I16], &[F32], insert_opcode),
|
||||||
|
(Opcode::FcvtFromUint, &[I32], &[F32], insert_opcode),
|
||||||
|
(Opcode::FcvtFromUint, &[I64], &[F32], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtFromUint, &[I128], &[F32], insert_opcode),
|
||||||
|
(Opcode::FcvtFromUint, &[I8], &[F64], insert_opcode),
|
||||||
|
(Opcode::FcvtFromUint, &[I16], &[F64], insert_opcode),
|
||||||
|
(Opcode::FcvtFromUint, &[I32], &[F64], insert_opcode),
|
||||||
|
(Opcode::FcvtFromUint, &[I64], &[F64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtFromUint, &[I128], &[F64], insert_opcode),
|
||||||
|
// FcvtFromSint
|
||||||
|
// TODO: Some ops disabled:
|
||||||
|
// x64: https://github.com/bytecodealliance/wasmtime/issues/4900
|
||||||
|
// aarch64: https://github.com/bytecodealliance/wasmtime/issues/4933
|
||||||
|
(Opcode::FcvtFromSint, &[I8], &[F32], insert_opcode),
|
||||||
|
(Opcode::FcvtFromSint, &[I16], &[F32], insert_opcode),
|
||||||
|
(Opcode::FcvtFromSint, &[I32], &[F32], insert_opcode),
|
||||||
|
(Opcode::FcvtFromSint, &[I64], &[F32], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtFromSint, &[I128], &[F32], insert_opcode),
|
||||||
|
(Opcode::FcvtFromSint, &[I8], &[F64], insert_opcode),
|
||||||
|
(Opcode::FcvtFromSint, &[I16], &[F64], insert_opcode),
|
||||||
|
(Opcode::FcvtFromSint, &[I32], &[F64], insert_opcode),
|
||||||
|
(Opcode::FcvtFromSint, &[I64], &[F64], insert_opcode),
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
(Opcode::FcvtFromSint, &[I128], &[F64], insert_opcode),
|
||||||
// Fcmp
|
// Fcmp
|
||||||
(Opcode::Fcmp, &[F32, F32], &[B1], insert_cmp),
|
(Opcode::Fcmp, &[F32, F32], &[B1], insert_cmp),
|
||||||
(Opcode::Fcmp, &[F64, F64], &[B1], insert_cmp),
|
(Opcode::Fcmp, &[F64, F64], &[B1], insert_cmp),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use std::fmt;
|
|||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod function_generator;
|
mod function_generator;
|
||||||
mod pass;
|
mod passes;
|
||||||
|
|
||||||
pub type TestCaseInput = Vec<DataValue>;
|
pub type TestCaseInput = Vec<DataValue>;
|
||||||
|
|
||||||
@@ -205,7 +205,10 @@ where
|
|||||||
// Run the int_divz pass
|
// Run the int_divz pass
|
||||||
//
|
//
|
||||||
// This pass replaces divs and rems with sequences that do not trap
|
// This pass replaces divs and rems with sequences that do not trap
|
||||||
pass::do_int_divz_pass(self, &mut ctx.func)?;
|
passes::do_int_divz_pass(self, &mut ctx.func)?;
|
||||||
|
|
||||||
|
// This pass replaces fcvt* instructions with sequences that do not trap
|
||||||
|
passes::do_fcvt_trap_pass(self, &mut ctx.func)?;
|
||||||
|
|
||||||
Ok(ctx.func)
|
Ok(ctx.func)
|
||||||
}
|
}
|
||||||
|
|||||||
98
cranelift/fuzzgen/src/passes/fcvt.rs
Normal file
98
cranelift/fuzzgen/src/passes/fcvt.rs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
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[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[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;
|
||||||
|
}
|
||||||
5
cranelift/fuzzgen/src/passes/mod.rs
Normal file
5
cranelift/fuzzgen/src/passes/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
mod fcvt;
|
||||||
|
mod int_divz;
|
||||||
|
|
||||||
|
pub use fcvt::do_fcvt_trap_pass;
|
||||||
|
pub use int_divz::do_int_divz_pass;
|
||||||
Reference in New Issue
Block a user