1
1
use crate :: config:: Config ;
2
2
use crate :: function_generator:: FunctionGenerator ;
3
+ use crate :: settings:: { Flags , OptLevel } ;
3
4
use anyhow:: Result ;
4
5
use arbitrary:: { Arbitrary , Unstructured } ;
5
6
use cranelift:: codegen:: data_value:: DataValue ;
@@ -30,6 +31,9 @@ impl<'a> Arbitrary<'a> for SingleFunction {
30
31
}
31
32
32
33
pub struct TestCase {
34
+ /// [Flags] to use when compiling this test case
35
+ pub flags : Flags ,
36
+ /// Function under test
33
37
pub func : Function ,
34
38
/// Generate multiple test inputs for each test case.
35
39
/// This allows us to get more coverage per compilation, which may be somewhat expensive.
@@ -38,19 +42,24 @@ pub struct TestCase {
38
42
39
43
impl fmt:: Debug for TestCase {
40
44
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
41
- write ! (
42
- f ,
43
- r#";; Fuzzgen test case
45
+ writeln ! ( f , ";; Fuzzgen test case \n " ) ? ;
46
+ writeln ! ( f , "test interpret" ) ? ;
47
+ writeln ! ( f , " test run" ) ? ;
44
48
45
- test interpret
46
- test run
47
- set enable_llvm_abi_extensions
48
- target aarch64
49
- target s390x
50
- target x86_64
49
+ // Print only non default flags
50
+ let default_flags = Flags :: new ( settings:: builder ( ) ) ;
51
+ for ( default, flag) in default_flags. iter ( ) . zip ( self . flags . iter ( ) ) {
52
+ assert_eq ! ( default . name, flag. name) ;
51
53
52
- "#
53
- ) ?;
54
+ if default. value_string ( ) != flag. value_string ( ) {
55
+ writeln ! ( f, "set {}={}" , flag. name, flag. value_string( ) ) ?;
56
+ }
57
+ }
58
+
59
+ writeln ! ( f, "target aarch64" ) ?;
60
+ writeln ! ( f, "target s390x" ) ?;
61
+ writeln ! ( f, "target riscv64" ) ?;
62
+ writeln ! ( f, "target x86_64\n " ) ?;
54
63
55
64
writeln ! ( f, "{}" , self . func) ?;
56
65
@@ -140,7 +149,10 @@ where
140
149
fn generate_test_inputs ( mut self , signature : & Signature ) -> Result < Vec < TestCaseInput > > {
141
150
let mut inputs = Vec :: new ( ) ;
142
151
143
- loop {
152
+ // Generate up to "max_test_case_inputs" inputs, we need an upper bound here since
153
+ // the fuzzer at some point starts trying to feed us way too many inputs. (I found one
154
+ // test case with 130k inputs!)
155
+ for _ in 0 ..self . config . max_test_case_inputs {
144
156
let last_len = self . u . len ( ) ;
145
157
146
158
let test_args = signature
@@ -217,14 +229,82 @@ where
217
229
self . run_func_passes ( func)
218
230
}
219
231
232
+ /// Generate a random set of cranelift flags.
233
+ /// Only semantics preserving flags are considered
234
+ fn generate_flags ( & mut self ) -> Result < Flags > {
235
+ let mut builder = settings:: builder ( ) ;
236
+
237
+ let opt = self . u . choose ( OptLevel :: all ( ) ) ?;
238
+ builder. set ( "opt_level" , & format ! ( "{}" , opt) [ ..] ) ?;
239
+
240
+ // Boolean flags
241
+ // TODO: probestack is semantics preserving, but only works inline and on x64
242
+ // TODO: enable_pinned_reg does not work with our current trampolines. See: #4376
243
+ // TODO: is_pic has issues:
244
+ // x86: https://github.com/bytecodealliance/wasmtime/issues/5005
245
+ // aarch64: https://github.com/bytecodealliance/wasmtime/issues/2735
246
+ let bool_settings = [
247
+ "enable_alias_analysis" ,
248
+ "enable_safepoints" ,
249
+ "unwind_info" ,
250
+ "preserve_frame_pointers" ,
251
+ "enable_jump_tables" ,
252
+ "enable_heap_access_spectre_mitigation" ,
253
+ "enable_table_access_spectre_mitigation" ,
254
+ "enable_incremental_compilation_cache_checks" ,
255
+ "regalloc_checker" ,
256
+ "enable_llvm_abi_extensions" ,
257
+ "use_egraphs" ,
258
+ ] ;
259
+ for flag_name in bool_settings {
260
+ let enabled = self
261
+ . config
262
+ . compile_flag_ratio
263
+ . get ( & flag_name)
264
+ . map ( |& ( num, denum) | self . u . ratio ( num, denum) )
265
+ . unwrap_or_else ( || bool:: arbitrary ( self . u ) ) ?;
266
+
267
+ let value = format ! ( "{}" , enabled) ;
268
+ builder. set ( flag_name, value. as_str ( ) ) ?;
269
+ }
270
+
271
+ // Fixed settings
272
+
273
+ // We need llvm ABI extensions for i128 values on x86, so enable it regardless of
274
+ // what we picked above.
275
+ if cfg ! ( target_arch = "x86_64" ) {
276
+ builder. enable ( "enable_llvm_abi_extensions" ) ?;
277
+ }
278
+
279
+ // This is the default, but we should ensure that it wasn't accidentally turned off anywhere.
280
+ builder. enable ( "enable_verifier" ) ?;
281
+
282
+ // These settings just panic when they're not enabled and we try to use their respective functionality
283
+ // so they aren't very interesting to be automatically generated.
284
+ builder. enable ( "enable_atomics" ) ?;
285
+ builder. enable ( "enable_float" ) ?;
286
+ builder. enable ( "enable_simd" ) ?;
287
+
288
+ // `machine_code_cfg_info` generates additional metadata for the embedder but this doesn't feed back
289
+ // into compilation anywhere, we leave it on unconditionally to make sure the generation doesn't panic.
290
+ builder. enable ( "machine_code_cfg_info" ) ?;
291
+
292
+ Ok ( Flags :: new ( builder) )
293
+ }
294
+
220
295
pub fn generate_test ( mut self ) -> Result < TestCase > {
221
296
// If we're generating test inputs as well as a function, then we're planning to execute
222
297
// this function. That means that any function references in it need to exist. We don't yet
223
298
// have infrastructure for generating multiple functions, so just don't generate funcrefs.
224
299
self . config . funcrefs_per_function = 0 ..=0 ;
225
300
301
+ let flags = self . generate_flags ( ) ?;
226
302
let func = self . generate_func ( ) ?;
227
303
let inputs = self . generate_test_inputs ( & func. signature ) ?;
228
- Ok ( TestCase { func, inputs } )
304
+ Ok ( TestCase {
305
+ flags,
306
+ func,
307
+ inputs,
308
+ } )
229
309
}
230
310
}
0 commit comments