Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/fuzzgen/src/passes/fcvt.rs
1692 views
1
use crate::FuzzGen;
2
use anyhow::Result;
3
use cranelift::codegen::cursor::{Cursor, FuncCursor};
4
use cranelift::codegen::ir::{Function, Inst, Opcode};
5
use cranelift::prelude::{types::*, *};
6
7
pub fn do_fcvt_trap_pass(fuzz: &mut FuzzGen, func: &mut Function) -> Result<()> {
8
let ratio = fuzz.config.allowed_fcvt_traps_ratio;
9
let insert_seq = !fuzz.u.ratio(ratio.0, ratio.1)?;
10
if !insert_seq {
11
return Ok(());
12
}
13
14
let mut pos = FuncCursor::new(func);
15
while let Some(_block) = pos.next_block() {
16
while let Some(inst) = pos.next_inst() {
17
if can_fcvt_trap(&pos, inst) {
18
insert_fcvt_sequence(&mut pos, inst);
19
}
20
}
21
}
22
Ok(())
23
}
24
25
/// Returns true/false if this instruction can trap
26
fn can_fcvt_trap(pos: &FuncCursor, inst: Inst) -> bool {
27
let opcode = pos.func.dfg.insts[inst].opcode();
28
29
matches!(opcode, Opcode::FcvtToUint | Opcode::FcvtToSint)
30
}
31
32
/// Gets the max and min float values for this integer type
33
/// Inserts fconst instructions with these values.
34
//
35
// When converting to integers, floats are truncated. This means that the maximum float value
36
// that can be converted into an i8 is 127.99999. And surprisingly the minimum float for an
37
// u8 is -0.99999! So get the limits of this type as a float value by adding or subtracting
38
// 1.0 from its min and max integer values.
39
fn float_limits(
40
pos: &mut FuncCursor,
41
float_ty: Type,
42
int_ty: Type,
43
is_signed: bool,
44
) -> (Value, Value) {
45
let (min_int, max_int) = int_ty.bounds(is_signed);
46
47
if float_ty == F32 {
48
let (min, max) = if is_signed {
49
((min_int as i128) as f32, (max_int as i128) as f32)
50
} else {
51
(min_int as f32, max_int as f32)
52
};
53
54
(pos.ins().f32const(min - 1.0), pos.ins().f32const(max + 1.0))
55
} else {
56
let (min, max) = if is_signed {
57
((min_int as i128) as f64, (max_int as i128) as f64)
58
} else {
59
(min_int as f64, max_int as f64)
60
};
61
62
(pos.ins().f64const(min - 1.0), pos.ins().f64const(max + 1.0))
63
}
64
}
65
66
/// Prepend instructions to inst to avoid traps
67
fn insert_fcvt_sequence(pos: &mut FuncCursor, inst: Inst) {
68
let dfg = &pos.func.dfg;
69
let opcode = dfg.insts[inst].opcode();
70
let arg = dfg.inst_args(inst)[0];
71
let float_ty = dfg.value_type(arg);
72
let int_ty = dfg.value_type(dfg.first_result(inst));
73
74
// These instructions trap on NaN
75
let is_nan = pos.ins().fcmp(FloatCC::NotEqual, arg, arg);
76
77
// They also trap if the value is larger or smaller than what the integer type can represent. So
78
// we generate the maximum and minimum float value that would make this trap, and compare against
79
// those limits.
80
let is_signed = opcode == Opcode::FcvtToSint;
81
let (min, max) = float_limits(pos, float_ty, int_ty, is_signed);
82
let underflows = pos.ins().fcmp(FloatCC::LessThanOrEqual, arg, min);
83
let overflows = pos.ins().fcmp(FloatCC::GreaterThanOrEqual, arg, max);
84
85
// Check the previous conditions and replace with a 1.0 if this instruction would trap
86
let overflows_int = pos.ins().bor(underflows, overflows);
87
let is_invalid = pos.ins().bor(is_nan, overflows_int);
88
89
let one = if float_ty == F32 {
90
pos.ins().f32const(1.0)
91
} else {
92
pos.ins().f64const(1.0)
93
};
94
let new_arg = pos.ins().select(is_invalid, one, arg);
95
96
// Replace the previous arg with the new one
97
pos.func.dfg.inst_args_mut(inst)[0] = new_arg;
98
}
99
100