Skip to content

Commit b995300

Browse files
authored
Fuzzing: add "callsite-ish constraints". (#216)
This is a followup from #214. We have use-cases in Cranelift now where we generate constraints on call instructions that have: - A number of fixed-reg uses at "early"; - A number of fixed-reg defs at "late"; - A number of register clobbers, potentially overlapping with uses; - A number of "any"-constrained defs at "late", to add register pressure. The existing fuzzing function generator would create at most 3 operands, and would add *either* fixed-reg constraints *or* clobbers, not both. This PR adds a "callsite-ish constraints" branch as a subcase of the fixed-reg choice that also adds the "any" defs to add register pressure, and adds new clobbers that don't interfere with existing constraints. It also loosens the logic around constructing fixed-reg constraints: if a conflict is found with some arbitrarily-chosen constraint, it keeps going to the next operand and tries to make it fixed, rather than giving up on further operands.
1 parent eba60dc commit b995300

File tree

4 files changed

+56
-8
lines changed

4 files changed

+56
-8
lines changed

fuzz/fuzz_targets/fastalloc_checker.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ impl Arbitrary<'_> for TestCase {
2525
fixed_nonallocatable: true,
2626
clobbers: true,
2727
reftypes: false,
28+
callsite_ish_constraints: true,
2829
},
2930
)?,
3031
})

fuzz/fuzz_targets/ion_checker.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ impl Arbitrary<'_> for TestCase {
2525
fixed_nonallocatable: true,
2626
clobbers: true,
2727
reftypes: true,
28+
callsite_ish_constraints: true,
2829
},
2930
)?,
3031
})

fuzz/fuzz_targets/ssagen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ impl Arbitrary<'_> for TestCase {
2626
fixed_nonallocatable: true,
2727
clobbers: true,
2828
reftypes: true,
29+
callsite_ish_constraints: true,
2930
},
3031
)?,
3132
})

src/fuzzing/func.rs

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ pub struct Options {
264264
pub fixed_nonallocatable: bool,
265265
pub clobbers: bool,
266266
pub reftypes: bool,
267+
pub callsite_ish_constraints: bool,
267268
}
268269

269270
impl core::default::Default for Options {
@@ -274,6 +275,7 @@ impl core::default::Default for Options {
274275
fixed_nonallocatable: false,
275276
clobbers: false,
276277
reftypes: false,
278+
callsite_ish_constraints: false,
277279
}
278280
}
279281
}
@@ -289,7 +291,7 @@ impl Func {
289291
// General strategy:
290292
// 1. Create an arbitrary CFG.
291293
// 2. Create a list of vregs to define in each block.
292-
// 3. Define some of those vregs in each block as blockparams.f.
294+
// 3. Define some of those vregs in each block as blockparams.
293295
// 4. Populate blocks with ops that define the rest of the vregs.
294296
// - For each use, choose an available vreg: either one
295297
// already defined (via blockparam or inst) in this block,
@@ -344,14 +346,19 @@ impl Func {
344346

345347
builder.compute_doms();
346348

349+
let alloc_vreg = |builder: &mut FuncBuilder, u: &mut Unstructured| {
350+
let vreg = VReg::new(builder.f.num_vregs, RegClass::arbitrary(u)?);
351+
builder.f.num_vregs += 1;
352+
Ok(vreg)
353+
};
354+
347355
let mut vregs_by_block = vec![];
348356
let mut vregs_by_block_to_be_defined = vec![];
349357
let mut block_params = vec![vec![]; num_blocks];
350358
for block in 0..num_blocks {
351359
let mut vregs = vec![];
352360
for _ in 0..u.int_in_range(5..=15)? {
353-
let vreg = VReg::new(builder.f.num_vregs, RegClass::arbitrary(u)?);
354-
builder.f.num_vregs += 1;
361+
let vreg = alloc_vreg(&mut builder, u)?;
355362
vregs.push(vreg);
356363
if opts.reftypes && bool::arbitrary(u)? {
357364
builder.f.reftype_vregs.push(vreg);
@@ -408,7 +415,7 @@ impl Func {
408415
def_pos,
409416
)];
410417
let mut allocations = vec![Allocation::none()];
411-
for _ in 0..u.int_in_range(0..=3)? {
418+
for _ in 0..u.int_in_range(0..=10)? {
412419
let vreg = if avail.len() > 0
413420
&& (remaining_nonlocal_uses == 0 || bool::arbitrary(u)?)
414421
{
@@ -471,22 +478,22 @@ impl Func {
471478
// Early-defs with fixed constraints conflict with
472479
// any other fixed uses of the same preg.
473480
if fixed_late.contains(&fixed_reg) {
474-
break;
481+
continue;
475482
}
476483
}
477484
if op.kind() == OperandKind::Use && op.pos() == OperandPos::Late {
478485
// Late-use with fixed constraints conflict with
479486
// any other fixed uses of the same preg.
480487
if fixed_early.contains(&fixed_reg) {
481-
break;
488+
continue;
482489
}
483490
}
484491
let fixed_list = match op.pos() {
485492
OperandPos::Early => &mut fixed_early,
486493
OperandPos::Late => &mut fixed_late,
487494
};
488495
if fixed_list.contains(&fixed_reg) {
489-
break;
496+
continue;
490497
}
491498
fixed_list.push(fixed_reg);
492499
operands[i] = Operand::new(
@@ -496,11 +503,49 @@ impl Func {
496503
op.pos(),
497504
);
498505
}
506+
507+
if opts.callsite_ish_constraints && bool::arbitrary(u)? {
508+
// Define some new vregs with `any`
509+
// constraints.
510+
for _ in 0..u.int_in_range(0..=20)? {
511+
let vreg = alloc_vreg(&mut builder, u)?;
512+
operands.push(Operand::new(
513+
vreg,
514+
OperandConstraint::Any,
515+
OperandKind::Def,
516+
OperandPos::Late,
517+
));
518+
}
519+
520+
// Create some clobbers, avoiding regs named
521+
// by operand constraints. Note that the sum
522+
// of the maximum clobber count here (10) and
523+
// maximum operand count above (10) is less
524+
// than the number of registers in any single
525+
// class, so the resulting problem is always
526+
// allocatable.
527+
for _ in 0..u.int_in_range(0..=10)? {
528+
let reg = u.int_in_range(0..=30)?;
529+
let preg = PReg::new(reg, RegClass::arbitrary(u)?);
530+
if operands
531+
.iter()
532+
.any(|op| match (op.kind(), op.constraint()) {
533+
(OperandKind::Def, OperandConstraint::FixedReg(fixed)) => {
534+
fixed == preg
535+
}
536+
_ => false,
537+
})
538+
{
539+
continue;
540+
}
541+
clobbers.push(preg);
542+
}
543+
}
499544
} else if opts.clobbers && bool::arbitrary(u)? {
500545
for _ in 0..u.int_in_range(0..=5)? {
501546
let reg = u.int_in_range(0..=30)?;
502547
if clobbers.iter().any(|r| r.hw_enc() == reg) {
503-
break;
548+
continue;
504549
}
505550
clobbers.push(PReg::new(reg, RegClass::arbitrary(u)?));
506551
}

0 commit comments

Comments
 (0)