Coverage Report

Created: 2026-01-22 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/regalloc2/src/fuzzing/func.rs
Line
Count
Source
1
/*
2
 * Released under the terms of the Apache 2.0 license with LLVM
3
 * exception. See `LICENSE` for details.
4
 */
5
6
use crate::{
7
    domtree, postorder, Allocation, Block, Function, Inst, InstRange, MachineEnv, Operand,
8
    OperandConstraint, OperandKind, OperandPos, PReg, PRegSet, RegClass, VReg,
9
};
10
11
use alloc::vec::Vec;
12
use alloc::{format, vec};
13
use core::ops::RangeInclusive;
14
15
use arbitrary::Result as ArbitraryResult;
16
use arbitrary::{Arbitrary, Unstructured};
17
18
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19
pub enum InstOpcode {
20
    Op,
21
    Ret,
22
    Branch,
23
}
24
25
#[derive(Clone, Debug)]
26
pub struct InstData {
27
    op: InstOpcode,
28
    operands: Vec<Operand>,
29
    clobbers: Vec<PReg>,
30
}
31
32
impl InstData {
33
484k
    pub fn branch() -> InstData {
34
484k
        InstData {
35
484k
            op: InstOpcode::Branch,
36
484k
            operands: vec![],
37
484k
            clobbers: vec![],
38
484k
        }
39
484k
    }
40
11.3k
    pub fn ret() -> InstData {
41
11.3k
        InstData {
42
11.3k
            op: InstOpcode::Ret,
43
11.3k
            operands: vec![],
44
11.3k
            clobbers: vec![],
45
11.3k
        }
46
11.3k
    }
47
}
48
49
#[derive(Clone)]
50
pub struct Func {
51
    insts: Vec<InstData>,
52
    blocks: Vec<InstRange>,
53
    block_preds: Vec<Vec<Block>>,
54
    block_succs: Vec<Vec<Block>>,
55
    block_params_in: Vec<Vec<VReg>>,
56
    block_params_out: Vec<Vec<Vec<VReg>>>,
57
    num_vregs: usize,
58
    reftype_vregs: Vec<VReg>,
59
    debug_value_labels: Vec<(VReg, Inst, Inst, u32)>,
60
}
61
62
impl Function for Func {
63
90.5k
    fn num_insts(&self) -> usize {
64
90.5k
        self.insts.len()
65
90.5k
    }
66
67
55.5M
    fn num_blocks(&self) -> usize {
68
55.5M
        self.blocks.len()
69
55.5M
    }
70
71
6.10M
    fn entry_block(&self) -> Block {
72
6.10M
        debug_assert!(self.blocks.len() > 0);
73
6.10M
        Block::new(0)
74
6.10M
    }
75
76
14.0M
    fn block_insns(&self, block: Block) -> InstRange {
77
14.0M
        self.blocks[block.index()]
78
14.0M
    }
79
80
10.9M
    fn block_succs(&self, block: Block) -> &[Block] {
81
10.9M
        &self.block_succs[block.index()][..]
82
10.9M
    }
83
84
24.4M
    fn block_preds(&self, block: Block) -> &[Block] {
85
24.4M
        &self.block_preds[block.index()][..]
86
24.4M
    }
87
88
3.69M
    fn block_params(&self, block: Block) -> &[VReg] {
89
3.69M
        &self.block_params_in[block.index()][..]
90
3.69M
    }
91
92
5.77M
    fn is_ret(&self, insn: Inst) -> bool {
93
5.77M
        self.insts[insn.index()].op == InstOpcode::Ret
94
5.77M
    }
95
96
7.44M
    fn is_branch(&self, insn: Inst) -> bool {
97
7.44M
        self.insts[insn.index()].op == InstOpcode::Branch
98
7.44M
    }
99
100
3.00M
    fn branch_blockparams(&self, block: Block, _: Inst, succ: usize) -> &[VReg] {
101
3.00M
        &self.block_params_out[block.index()][succ][..]
102
3.00M
    }
103
104
11.3k
    fn debug_value_labels(&self) -> &[(VReg, Inst, Inst, u32)] {
105
11.3k
        &self.debug_value_labels[..]
106
11.3k
    }
107
108
106M
    fn inst_operands(&self, insn: Inst) -> &[Operand] {
109
106M
        &self.insts[insn.index()].operands[..]
110
106M
    }
111
112
11.2M
    fn inst_clobbers(&self, insn: Inst) -> PRegSet {
113
11.2M
        let mut set = PRegSet::default();
114
11.2M
        for &preg in &self.insts[insn.index()].clobbers {
115
4.56M
            set = set.with(preg);
116
4.56M
        }
117
11.2M
        set
118
11.2M
    }
119
120
23.7k
    fn num_vregs(&self) -> usize {
121
23.7k
        self.num_vregs
122
23.7k
    }
123
124
244k
    fn spillslot_size(&self, regclass: RegClass) -> usize {
125
244k
        match regclass {
126
            // Test the case where 2 classes share the same
127
113k
            RegClass::Int => 1,
128
30.4k
            RegClass::Float => 1,
129
100k
            RegClass::Vector => 2,
130
        }
131
244k
    }
132
}
133
134
struct FuncBuilder {
135
    postorder: Vec<Block>,
136
    idom: Vec<Block>,
137
    f: Func,
138
    insts_per_block: Vec<Vec<InstData>>,
139
}
140
141
impl FuncBuilder {
142
11.3k
    fn new() -> Self {
143
11.3k
        FuncBuilder {
144
11.3k
            postorder: vec![],
145
11.3k
            idom: vec![],
146
11.3k
            f: Func {
147
11.3k
                block_preds: vec![],
148
11.3k
                block_succs: vec![],
149
11.3k
                block_params_in: vec![],
150
11.3k
                block_params_out: vec![],
151
11.3k
                insts: vec![],
152
11.3k
                blocks: vec![],
153
11.3k
                num_vregs: 0,
154
11.3k
                reftype_vregs: vec![],
155
11.3k
                debug_value_labels: vec![],
156
11.3k
            },
157
11.3k
            insts_per_block: vec![],
158
11.3k
        }
159
11.3k
    }
160
161
496k
    pub fn add_block(&mut self) -> Block {
162
496k
        let b = Block::new(self.f.blocks.len());
163
496k
        self.f
164
496k
            .blocks
165
496k
            .push(InstRange::new(Inst::new(0), Inst::new(0)));
166
496k
        self.f.block_preds.push(vec![]);
167
496k
        self.f.block_succs.push(vec![]);
168
496k
        self.f.block_params_in.push(vec![]);
169
496k
        self.f.block_params_out.push(vec![]);
170
496k
        self.insts_per_block.push(vec![]);
171
496k
        b
172
496k
    }
173
174
4.66M
    pub fn add_inst(&mut self, block: Block, data: InstData) {
175
4.66M
        self.insts_per_block[block.index()].push(data);
176
4.66M
    }
177
178
610k
    pub fn add_edge(&mut self, from: Block, to: Block) {
179
610k
        self.f.block_succs[from.index()].push(to);
180
610k
        self.f.block_preds[to.index()].push(from);
181
610k
    }
182
183
496k
    pub fn set_block_params_in(&mut self, block: Block, params: &[VReg]) {
184
496k
        self.f.block_params_in[block.index()] = params.iter().cloned().collect();
185
496k
    }
186
187
484k
    pub fn set_block_params_out(&mut self, block: Block, params: Vec<Vec<VReg>>) {
188
484k
        self.f.block_params_out[block.index()] = params;
189
484k
    }
190
191
11.3k
    fn compute_doms(&mut self) {
192
11.3k
        let f = &self.f;
193
11.3k
        let _ = postorder::calculate(
194
11.3k
            self.f.blocks.len(),
195
11.3k
            Block::new(0),
196
11.3k
            &mut vec![],
197
11.3k
            &mut self.postorder,
198
496k
            |block| &f.block_succs[block.index()][..],
199
        );
200
11.3k
        domtree::calculate(
201
11.3k
            self.f.blocks.len(),
202
2.19M
            |block| &f.block_preds[block.index()][..],
203
11.3k
            &self.postorder[..],
204
11.3k
            &mut vec![],
205
11.3k
            &mut self.idom,
206
11.3k
            Block::new(0),
207
        );
208
11.3k
    }
209
210
551k
    fn add_arbitrary_debug_labels(
211
551k
        &mut self,
212
551k
        u: &mut Unstructured<'_>,
213
551k
        num_blocks: usize,
214
551k
        vreg: VReg,
215
551k
    ) -> ArbitraryResult<()> {
216
551k
        let assumed_end_inst = 10 * num_blocks;
217
551k
        let mut start = u.int_in_range::<usize>(0..=assumed_end_inst)?;
218
4.79M
        Ok(for _ in 0..10 {
219
4.46M
            if start >= assumed_end_inst {
220
227k
                break;
221
4.23M
            }
222
4.23M
            let end = u.int_in_range::<usize>(start..=assumed_end_inst)?;
223
4.23M
            let label = u.int_in_range::<u32>(0..=100)?;
224
4.23M
            self.f
225
4.23M
                .debug_value_labels
226
4.23M
                .push((vreg, Inst::new(start), Inst::new(end), label));
227
4.23M
            start = end;
228
        })
229
551k
    }
230
231
11.3k
    fn finalize(mut self) -> Func {
232
495k
        for (blocknum, blockrange) in self.f.blocks.iter_mut().enumerate() {
233
495k
            let begin_inst = self.f.insts.len();
234
4.65M
            for inst in &self.insts_per_block[blocknum] {
235
4.65M
                self.f.insts.push(inst.clone());
236
4.65M
            }
237
495k
            let end_inst = self.f.insts.len();
238
495k
            *blockrange = InstRange::new(Inst::new(begin_inst), Inst::new(end_inst));
239
        }
240
241
11.3k
        self.f
242
11.3k
    }
243
}
244
245
impl Arbitrary<'_> for RegClass {
246
9.69M
    fn arbitrary(u: &mut Unstructured) -> ArbitraryResult<Self> {
247
9.69M
        Ok(*u.choose(&[RegClass::Int, RegClass::Float, RegClass::Vector])?)
248
9.69M
    }
249
}
250
251
impl Arbitrary<'_> for OperandPos {
252
4.16M
    fn arbitrary(u: &mut Unstructured) -> ArbitraryResult<Self> {
253
4.16M
        Ok(*u.choose(&[OperandPos::Early, OperandPos::Late])?)
254
4.16M
    }
255
}
256
257
impl Arbitrary<'_> for OperandConstraint {
258
12.7M
    fn arbitrary(u: &mut Unstructured) -> ArbitraryResult<Self> {
259
12.7M
        Ok(*u.choose(&[OperandConstraint::Any, OperandConstraint::Reg])?)
260
12.7M
    }
261
}
262
263
3.11M
fn choose_dominating_block(
264
3.11M
    idom: &[Block],
265
3.11M
    mut block: Block,
266
3.11M
    allow_self: bool,
267
3.11M
    u: &mut Unstructured,
268
3.11M
) -> ArbitraryResult<Block> {
269
3.11M
    debug_assert!(block.is_valid());
270
3.11M
    let orig_block = block;
271
    loop {
272
24.2M
        if (allow_self || block != orig_block) && bool::arbitrary(u)? {
273
2.10M
            break;
274
22.1M
        }
275
22.1M
        if idom[block.index()].is_invalid() {
276
1.00M
            break;
277
21.1M
        }
278
21.1M
        block = idom[block.index()];
279
    }
280
3.11M
    let block = if block != orig_block || allow_self {
281
3.08M
        block
282
    } else {
283
26.4k
        Block::invalid()
284
    };
285
3.11M
    Ok(block)
286
3.11M
}
287
288
851k
fn convert_def_to_reuse(u: &mut Unstructured<'_>, operands: &mut [Operand]) -> ArbitraryResult<()> {
289
851k
    let op = operands[0];
290
851k
    debug_assert_eq!(op.kind(), OperandKind::Def);
291
292
851k
    let reused = u.int_in_range(1..=(operands.len() - 1))?;
293
851k
    Ok(if op.class() == operands[reused].class() {
294
591k
        // Replace the def with a reuse of an existing input.
295
591k
        operands[0] = Operand::new(
296
591k
            op.vreg(),
297
591k
            OperandConstraint::Reuse(reused),
298
591k
            op.kind(),
299
591k
            OperandPos::Late,
300
591k
        );
301
591k
302
591k
        // Make sure reused input is a register.
303
591k
        let op = operands[reused];
304
591k
        operands[reused] = Operand::new(
305
591k
            op.vreg(),
306
591k
            OperandConstraint::Reg,
307
591k
            op.kind(),
308
591k
            OperandPos::Early,
309
591k
        );
310
591k
    })
311
851k
}
312
313
1.07M
fn convert_op_to_fixed(
314
1.07M
    u: &mut Unstructured<'_>,
315
1.07M
    op: &mut Operand,
316
1.07M
    fixed_early: &mut Vec<PReg>,
317
1.07M
    fixed_late: &mut Vec<PReg>,
318
1.07M
) -> ArbitraryResult<()> {
319
1.07M
    let fixed_reg = PReg::new(u.int_in_range(0..=62)?, op.class());
320
321
1.07M
    if op.kind() == OperandKind::Def && op.pos() == OperandPos::Early {
322
        // Early-defs with fixed constraints conflict with
323
        // any other fixed uses of the same preg.
324
45.0k
        if fixed_late.contains(&fixed_reg) {
325
0
            return Ok(());
326
45.0k
        }
327
1.02M
    }
328
329
1.07M
    if op.kind() == OperandKind::Use && op.pos() == OperandPos::Late {
330
        // Late-use with fixed constraints conflict with
331
        // any other fixed uses of the same preg.
332
0
        if fixed_early.contains(&fixed_reg) {
333
0
            return Ok(());
334
0
        }
335
1.07M
    }
336
337
    // Check that it is not already fixed.
338
1.07M
    let fixed_list = match op.pos() {
339
986k
        OperandPos::Early => fixed_early,
340
84.7k
        OperandPos::Late => fixed_late,
341
    };
342
1.07M
    if fixed_list.contains(&fixed_reg) {
343
435k
        return Ok(());
344
636k
    }
345
346
636k
    fixed_list.push(fixed_reg);
347
636k
    *op = Operand::new(
348
636k
        op.vreg(),
349
636k
        OperandConstraint::FixedReg(fixed_reg),
350
636k
        op.kind(),
351
636k
        op.pos(),
352
636k
    );
353
354
636k
    Ok(())
355
1.07M
}
356
357
840k
fn has_fixed_def_with(preg: PReg) -> impl Fn(&Operand) -> bool {
358
12.3M
    move |op| match (op.kind(), op.constraint()) {
359
119k
        (OperandKind::Def, OperandConstraint::FixedReg(fixed)) => fixed == preg,
360
12.2M
        _ => false,
361
12.3M
    }
362
840k
}
363
364
#[derive(Clone, Debug)]
365
pub struct Options {
366
    pub reused_inputs: bool,
367
    pub fixed_regs: bool,
368
    pub fixed_nonallocatable: bool,
369
    pub clobbers: bool,
370
    pub reftypes: bool,
371
    pub callsite_ish_constraints: bool,
372
    pub num_blocks: RangeInclusive<usize>,
373
    pub num_vregs_per_block: RangeInclusive<usize>,
374
    pub num_uses_per_inst: RangeInclusive<usize>,
375
    pub num_callsite_ish_vregs_per_inst: RangeInclusive<usize>,
376
    pub num_clobbers_per_inst: RangeInclusive<usize>,
377
}
378
379
impl Options {
380
    /// Default options for generating functions.
381
    pub const DEFAULT: Self = Self {
382
        reused_inputs: false,
383
        fixed_regs: false,
384
        fixed_nonallocatable: false,
385
        clobbers: false,
386
        reftypes: false,
387
        callsite_ish_constraints: false,
388
        num_blocks: 1..=100,
389
        num_vregs_per_block: 5..=15,
390
        num_uses_per_inst: 0..=10,
391
        num_callsite_ish_vregs_per_inst: 0..=20,
392
        num_clobbers_per_inst: 0..=10,
393
    };
394
}
395
396
impl Arbitrary<'_> for Func {
397
0
    fn arbitrary(u: &mut Unstructured) -> ArbitraryResult<Func> {
398
0
        Func::arbitrary_with_options(u, &Options::DEFAULT)
399
0
    }
400
}
401
402
impl Func {
403
11.3k
    pub fn arbitrary_with_options(u: &mut Unstructured, opts: &Options) -> ArbitraryResult<Func> {
404
        // General strategy:
405
        // 1. Create an arbitrary CFG.
406
        // 2. Create a list of vregs to define in each block.
407
        // 3. Define some of those vregs in each block as blockparams.
408
        // 4. Populate blocks with ops that define the rest of the vregs.
409
        //    - For each use, choose an available vreg: either one
410
        //      already defined (via blockparam or inst) in this block,
411
        //      or one defined in a dominating block.
412
413
11.3k
        let mut builder = FuncBuilder::new();
414
496k
        for _ in 0..u.int_in_range(opts.num_blocks.clone())? {
415
496k
            builder.add_block();
416
496k
        }
417
11.3k
        let num_blocks = builder.f.blocks.len();
418
419
        // Generate a CFG. Create a "spine" of either single blocks,
420
        // with links to the next; or fork patterns, with the left
421
        // fork linking to the next and the right fork in `out_blocks`
422
        // to be connected below. This creates an arbitrary CFG with
423
        // split critical edges, which is a property that we require
424
        // for the regalloc.
425
11.3k
        let mut from = 0;
426
11.3k
        let mut out_blocks = vec![];
427
11.3k
        let mut in_blocks = vec![];
428
256k
        while from < num_blocks {
429
245k
            in_blocks.push(from);
430
245k
            if num_blocks > 3 && from < num_blocks - 3 && bool::arbitrary(u)? {
431
125k
                // To avoid critical edges, we use from+1 as an edge
432
125k
                // block, and advance `from` an extra block; `from+2`
433
125k
                // will be the next normal iteration.
434
125k
                builder.add_edge(Block::new(from), Block::new(from + 1));
435
125k
                builder.add_edge(Block::new(from), Block::new(from + 2));
436
125k
                builder.add_edge(Block::new(from + 2), Block::new(from + 3));
437
125k
                out_blocks.push(from + 1);
438
125k
                from += 2;
439
125k
            } else if from < num_blocks - 1 {
440
108k
                builder.add_edge(Block::new(from), Block::new(from + 1));
441
108k
            }
442
245k
            from += 1;
443
        }
444
136k
        for pred in out_blocks {
445
125k
            let succ = *u.choose(&in_blocks[..])?;
446
125k
            builder.add_edge(Block::new(pred), Block::new(succ));
447
        }
448
11.3k
        builder.compute_doms();
449
450
8.28M
        let alloc_vreg = |builder: &mut FuncBuilder, u: &mut Unstructured| {
451
8.28M
            let vreg = VReg::new(builder.f.num_vregs, RegClass::arbitrary(u)?);
452
8.28M
            builder.f.num_vregs += 1;
453
8.28M
            Ok(vreg)
454
8.28M
        };
455
456
11.3k
        let mut vregs_by_block = vec![];
457
11.3k
        let mut vregs_by_block_to_be_defined = vec![];
458
11.3k
        let mut block_params = vec![vec![]; num_blocks];
459
496k
        for block in 0..num_blocks {
460
            // Calculate the available vregs for this block.
461
496k
            let mut vregs = vec![];
462
496k
            for _ in 0..u.int_in_range(opts.num_vregs_per_block.clone())? {
463
4.34M
                let vreg = alloc_vreg(&mut builder, u)?;
464
4.34M
                vregs.push(vreg);
465
4.34M
                if opts.reftypes && bool::arbitrary(u)? {
466
735k
                    builder.f.reftype_vregs.push(vreg);
467
3.60M
                }
468
4.34M
                if bool::arbitrary(u)? {
469
551k
                    builder.add_arbitrary_debug_labels(u, num_blocks, vreg)?;
470
3.79M
                }
471
            }
472
496k
            vregs_by_block.push(vregs.clone());
473
474
            // Choose some of the vregs to be block parameters.
475
496k
            let mut vregs_to_be_defined = vec![];
476
496k
            let mut max_block_params = u.int_in_range(0..=core::cmp::min(3, vregs.len() / 3))?;
477
4.83M
            for &vreg in &vregs {
478
4.34M
                if block > 0 && bool::arbitrary(u)? && max_block_params > 0 {
479
175k
                    block_params[block].push(vreg);
480
175k
                    max_block_params -= 1;
481
4.16M
                } else {
482
4.16M
                    vregs_to_be_defined.push(vreg);
483
4.16M
                }
484
            }
485
496k
            vregs_to_be_defined.reverse();
486
496k
            vregs_by_block_to_be_defined.push(vregs_to_be_defined);
487
496k
            builder.set_block_params_in(Block::new(block), &block_params[block][..]);
488
        }
489
490
495k
        for block in 0..num_blocks {
491
495k
            let mut avail = block_params[block].clone();
492
495k
            let mut remaining_nonlocal_uses = u.int_in_range(0..=3)?;
493
4.66M
            while let Some(vreg) = vregs_by_block_to_be_defined[block].pop() {
494
                // Start with a written-to vreg (`def`).
495
4.16M
                let mut operands = vec![Operand::new(
496
4.16M
                    vreg,
497
4.16M
                    OperandConstraint::arbitrary(u)?,
498
4.16M
                    OperandKind::Def,
499
4.16M
                    OperandPos::arbitrary(u)?,
500
                )];
501
502
                // Then add some read-from vregs (`uses`).
503
4.16M
                let mut allocations = vec![Allocation::none()];
504
4.16M
                for _ in 0..u.int_in_range(opts.num_uses_per_inst.clone())? {
505
8.57M
                    let vreg = if avail.len() > 0
506
7.89M
                        && (remaining_nonlocal_uses == 0 || bool::arbitrary(u)?)
507
                    {
508
5.69M
                        *u.choose(&avail[..])?
509
                    } else {
510
2.88M
                        let def_block = choose_dominating_block(
511
2.88M
                            &builder.idom[..],
512
2.88M
                            Block::new(block),
513
                            /* allow_self = */ false,
514
2.88M
                            u,
515
0
                        )?;
516
2.88M
                        if !def_block.is_valid() {
517
                            // No vregs already defined, and no pred blocks that dominate us
518
                            // (perhaps we are the entry block): just stop generating inputs.
519
19.0k
                            break;
520
2.86M
                        }
521
2.86M
                        remaining_nonlocal_uses -= 1;
522
2.86M
                        *u.choose(&vregs_by_block[def_block.index()])?
523
                    };
524
8.55M
                    operands.push(Operand::new(
525
8.55M
                        vreg,
526
8.55M
                        OperandConstraint::arbitrary(u)?,
527
8.55M
                        OperandKind::Use,
528
8.55M
                        OperandPos::Early,
529
                    ));
530
8.55M
                    allocations.push(Allocation::none());
531
                }
532
533
                // Convert some of the operands to have special constraints:
534
                // reuses, fixed, clobbers, etc.
535
4.16M
                let mut clobbers: Vec<PReg> = vec![];
536
4.16M
                if operands.len() > 1 && opts.reused_inputs && bool::arbitrary(u)? {
537
851k
                    convert_def_to_reuse(u, &mut operands)?;
538
3.31M
                } else if opts.fixed_regs && bool::arbitrary(u)? {
539
497k
                    let mut fixed_early = vec![];
540
497k
                    let mut fixed_late = vec![];
541
497k
                    for _ in 0..u.int_in_range(0..=operands.len() - 1)? {
542
                        // Pick an operand and make it a fixed reg.
543
1.07M
                        let i = u.int_in_range(0..=(operands.len() - 1))?;
544
1.07M
                        let op = &mut operands[i];
545
1.07M
                        convert_op_to_fixed(u, op, &mut fixed_early, &mut fixed_late)?;
546
                    }
547
548
497k
                    if opts.callsite_ish_constraints && bool::arbitrary(u)? {
549
                        // Define some new vregs with `any` constraints.
550
348k
                        for _ in 0..u.int_in_range(opts.num_callsite_ish_vregs_per_inst.clone())? {
551
3.93M
                            let vreg = alloc_vreg(&mut builder, u)?;
552
3.93M
                            operands.push(Operand::any_def(vreg));
553
                        }
554
555
                        // Create some clobbers, avoiding regs named by operand
556
                        // constraints. Note that the sum of the maximum clobber
557
                        // count here (10) and maximum operand count above (10)
558
                        // is less than the number of registers in any single
559
                        // class, so the resulting problem is always
560
                        // allocatable.
561
348k
                        for _ in 0..u.int_in_range(opts.num_clobbers_per_inst.clone())? {
562
840k
                            let reg = u.int_in_range(0..=30)?;
563
840k
                            let preg = PReg::new(reg, RegClass::arbitrary(u)?);
564
840k
                            if operands.iter().any(has_fixed_def_with(preg)) {
565
7.31k
                                continue;
566
833k
                            }
567
833k
                            clobbers.push(preg);
568
                        }
569
148k
                    }
570
2.81M
                } else if opts.clobbers && bool::arbitrary(u)? {
571
145k
                    for _ in 0..u.int_in_range(opts.num_clobbers_per_inst.clone())? {
572
657k
                        let reg = u.int_in_range(0..=30)?;
573
1.42M
                        if clobbers.iter().any(|r| r.hw_enc() == reg) {
574
168k
                            continue;
575
489k
                        }
576
489k
                        clobbers.push(PReg::new(reg, RegClass::arbitrary(u)?));
577
                    }
578
2.67M
                } else if opts.fixed_nonallocatable && bool::arbitrary(u)? {
579
87.0k
                    operands.push(Operand::fixed_nonallocatable(PReg::new(
580
                        63,
581
87.0k
                        RegClass::arbitrary(u)?,
582
                    )));
583
2.58M
                }
584
585
4.16M
                builder.add_inst(
586
4.16M
                    Block::new(block),
587
4.16M
                    InstData {
588
4.16M
                        op: InstOpcode::Op,
589
4.16M
                        operands,
590
4.16M
                        clobbers,
591
4.16M
                    },
592
                );
593
4.16M
                avail.push(vreg);
594
            }
595
596
            // Define the branch with blockparam args that must end
597
            // the block.
598
495k
            if builder.f.block_succs[block].len() > 0 {
599
484k
                let mut params = vec![];
600
609k
                for &succ in &builder.f.block_succs[block] {
601
609k
                    let mut args = vec![];
602
609k
                    for i in 0..builder.f.block_params_in[succ.index()].len() {
603
225k
                        let dom_block = choose_dominating_block(
604
225k
                            &builder.idom[..],
605
225k
                            Block::new(block),
606
                            false,
607
225k
                            u,
608
0
                        )?;
609
610
                        // Look for a vreg with a suitable class. If no
611
                        // suitable vreg is available then we error out, which
612
                        // causes the fuzzer to skip this function.
613
225k
                        let vregs = if dom_block.is_valid() && bool::arbitrary(u)? {
614
66.9k
                            &vregs_by_block[dom_block.index()][..]
615
                        } else {
616
158k
                            &avail[..]
617
                        };
618
225k
                        let suitable_vregs: Vec<_> = vregs
619
225k
                            .iter()
620
2.33M
                            .filter(|vreg| {
621
2.33M
                                vreg.class() == builder.f.block_params_in[succ.index()][i].class()
622
2.33M
                            })
623
225k
                            .copied()
624
225k
                            .collect();
625
225k
                        let vreg = u.choose(&suitable_vregs)?;
626
225k
                        args.push(*vreg);
627
                    }
628
609k
                    params.push(args);
629
                }
630
484k
                builder.set_block_params_out(Block::new(block), params);
631
484k
                builder.add_inst(Block::new(block), InstData::branch());
632
11.3k
            } else {
633
11.3k
                builder.add_inst(Block::new(block), InstData::ret());
634
11.3k
            }
635
        }
636
637
11.3k
        builder.f.debug_value_labels.sort_unstable();
638
639
11.3k
        Ok(builder.finalize())
640
11.3k
    }
641
}
642
643
impl core::fmt::Debug for Func {
644
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
645
0
        write!(f, "{{\n")?;
646
0
        for (i, blockrange) in self.blocks.iter().enumerate() {
647
0
            let succs = self.block_succs[i]
648
0
                .iter()
649
0
                .map(|b| b.index())
650
0
                .collect::<Vec<_>>();
651
0
            let preds = self.block_preds[i]
652
0
                .iter()
653
0
                .map(|b| b.index())
654
0
                .collect::<Vec<_>>();
655
0
            let params_in = self.block_params_in[i]
656
0
                .iter()
657
0
                .map(|v| format!("v{}", v.vreg()))
658
0
                .collect::<Vec<_>>()
659
0
                .join(", ");
660
0
            let params_out = self.block_params_out[i]
661
0
                .iter()
662
0
                .enumerate()
663
0
                .map(|(succ_idx, vec)| {
664
0
                    let succ = self.block_succs[i][succ_idx];
665
0
                    let params = vec
666
0
                        .iter()
667
0
                        .map(|v| format!("v{}", v.vreg()))
668
0
                        .collect::<Vec<_>>()
669
0
                        .join(", ");
670
0
                    format!("block{}({})", succ.index(), params)
671
0
                })
672
0
                .collect::<Vec<_>>()
673
0
                .join(", ");
674
0
            write!(
675
0
                f,
676
0
                "  block{}({}): # succs:{:?} preds:{:?}\n",
677
                i, params_in, succs, preds
678
0
            )?;
679
0
            for inst in blockrange.iter() {
680
0
                write!(
681
0
                    f,
682
0
                    "    inst{}: {:?} ops:{:?} clobber:{:?}\n",
683
0
                    inst.index(),
684
0
                    self.insts[inst.index()].op,
685
0
                    self.insts[inst.index()].operands,
686
0
                    self.insts[inst.index()].clobbers
687
0
                )?;
688
0
                if let InstOpcode::Branch = self.insts[inst.index()].op {
689
0
                    write!(f, "    params: {}\n", params_out)?;
690
0
                }
691
            }
692
        }
693
0
        write!(f, "}}\n")?;
694
0
        Ok(())
695
0
    }
696
}
697
698
11.3k
pub fn machine_env() -> MachineEnv {
699
67.9k
    fn regs(r: core::ops::Range<usize>, c: RegClass) -> PRegSet {
700
1.08M
        r.map(|i| PReg::new(i, c)).collect()
701
67.9k
    }
702
11.3k
    let preferred_regs_by_class = [
703
11.3k
        regs(0..24, RegClass::Int),
704
11.3k
        regs(0..24, RegClass::Float),
705
11.3k
        regs(0..24, RegClass::Vector),
706
11.3k
    ];
707
11.3k
    let non_preferred_regs_by_class = [
708
11.3k
        regs(24..32, RegClass::Int),
709
11.3k
        regs(24..32, RegClass::Float),
710
11.3k
        regs(24..32, RegClass::Vector),
711
11.3k
    ];
712
11.3k
    let scratch_by_class = [None, None, None];
713
11.3k
    let fixed_stack_slots = (32..63)
714
350k
        .flat_map(|i| {
715
350k
            [
716
350k
                PReg::new(i, RegClass::Int),
717
350k
                PReg::new(i, RegClass::Float),
718
350k
                PReg::new(i, RegClass::Vector),
719
350k
            ]
720
350k
        })
721
11.3k
        .collect();
722
    // Register 63 is reserved for use as a fixed non-allocatable register.
723
11.3k
    MachineEnv {
724
11.3k
        preferred_regs_by_class,
725
11.3k
        non_preferred_regs_by_class,
726
11.3k
        scratch_by_class,
727
11.3k
        fixed_stack_slots,
728
11.3k
    }
729
11.3k
}