1 use std::collections::HashMap;
2 use std::ops::RangeInclusive;
3 
4 /// Holds the range of acceptable values to use during the generation of testcases
5 pub struct Config {
6     /// Maximum allowed test case inputs.
7     /// We build test case inputs from the rest of the bytes that the fuzzer provides us
8     /// so we allow the fuzzer to control this by feeding us more or less bytes.
9     /// The upper bound here is to prevent too many inputs that cause long test times
10     pub max_test_case_inputs: usize,
11     // Number of functions that we generate per testcase
12     pub testcase_funcs: RangeInclusive<usize>,
13     pub signature_params: RangeInclusive<usize>,
14     pub signature_rets: RangeInclusive<usize>,
15     pub instructions_per_block: RangeInclusive<usize>,
16     /// Number of variables that we allocate per function
17     /// This value does not include the signature params
18     pub vars_per_function: RangeInclusive<usize>,
19     /// Number of blocks that we generate per function.
20     /// This value does not include the entry block
21     pub blocks_per_function: RangeInclusive<usize>,
22     /// Number of params a block should take
23     /// This value does not apply to block0 which takes the function params
24     /// and is thus governed by `signature_params`
25     pub block_signature_params: RangeInclusive<usize>,
26     /// Max number of jump tables entries to generate
27     pub jump_table_entries: RangeInclusive<usize>,
28 
29     /// The Switch API specializes either individual blocks or contiguous ranges.
30     /// In `switch_cases` we decide to produce either a single block or a range.
31     /// The size of the range is controlled by `switch_max_range_size`.
32     pub switch_cases: RangeInclusive<usize>,
33     pub switch_max_range_size: RangeInclusive<usize>,
34 
35     /// Stack slots.
36     /// The combination of these two determines stack usage per function
37     pub static_stack_slots_per_function: RangeInclusive<usize>,
38     /// Size in bytes
39     pub static_stack_slot_size: RangeInclusive<usize>,
40     /// Stack slot alignment as a power of 2
41     pub stack_slot_alignment_log2: RangeInclusive<usize>,
42     /// Allowed stack probe sizes
43     pub stack_probe_size_log2: RangeInclusive<usize>,
44 
45     /// Determines how often we generate a backwards branch
46     /// Backwards branches are prone to infinite loops, and thus cause timeouts.
47     pub backwards_branch_ratio: (usize, usize),
48 
49     /// How often should we allow integer division by zero traps.
50     ///
51     /// Some instructions such as Srem and Udiv can cause a `int_divz` trap
52     /// under some inputs. We almost always insert a sequence of instructions
53     /// that avoids these issues. However we can allow some `int_divz` traps
54     /// by controlling this config.
55     pub allowed_int_divz_ratio: (usize, usize),
56 
57     /// How often should we allow fcvt related traps.
58     ///
59     /// `Fcvt*` instructions fail under some inputs, most commonly NaN's.
60     /// We insert a checking sequence to guarantee that those inputs never make
61     /// it to the instruction, but sometimes we want to allow them.
62     pub allowed_fcvt_traps_ratio: (usize, usize),
63 
64     /// Some flags really impact compile performance, we still want to test
65     /// them, but probably at a lower rate, so that overall execution time isn't
66     /// impacted as much
67     pub compile_flag_ratio: HashMap<&'static str, (usize, usize)>,
68 
69     /// Range of values for the padding between basic blocks. Larger values will
70     /// generate larger functions.
71     pub bb_padding_log2_size: RangeInclusive<usize>,
72 }
73 
74 impl Default for Config {
default() -> Self75     fn default() -> Self {
76         Config {
77             max_test_case_inputs: 100,
78             testcase_funcs: 1..=8,
79             signature_params: 0..=16,
80             signature_rets: 0..=16,
81             instructions_per_block: 0..=64,
82             vars_per_function: 0..=16,
83             blocks_per_function: 0..=16,
84             block_signature_params: 0..=16,
85             jump_table_entries: 0..=16,
86             switch_cases: 0..=64,
87             // Ranges smaller than 2 don't make sense.
88             switch_max_range_size: 2..=32,
89             static_stack_slots_per_function: 0..=8,
90             static_stack_slot_size: 0..=128,
91             stack_slot_alignment_log2: 0..=10,
92             // We need the mix of sizes that allows us to:
93             //  * not generates any stack probes
94             //  * generate unrolled stack probes
95             //  * generate loop stack probes
96             //
97             // This depends on the total amount of stack space that we have for this function
98             // (controlled by `static_stack_slots_per_function` and `static_stack_slot_size`)
99             //
100             // 1<<6 = 64 and 1<<14 = 16384
101             //
102             // This range allows us to generate all 3 cases within the current allowed
103             // stack size range.
104             stack_probe_size_log2: 6..=14,
105             // 0.1% allows us to explore this, while not causing enough timeouts to significantly
106             // impact execs/s
107             backwards_branch_ratio: (1, 1000),
108             allowed_int_divz_ratio: (1, 1_000_000),
109             allowed_fcvt_traps_ratio: (1, 1_000_000),
110             compile_flag_ratio: [("regalloc_checker", (1usize, 1000))].into_iter().collect(),
111             // Generate up to 4KiB of padding between basic blocks. Although we only
112             // explicitly generate up to 16 blocks, after SSA construction we can
113             // end up with way more blocks than that (Seeing 400 blocks is not uncommon).
114             // At 4KiB we end up at around 1.5MiB of padding per function, which seems reasonable.
115             bb_padding_log2_size: 0..=12,
116         }
117     }
118 }
119