Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fuzz/fuzz_targets/fastalloc_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl Arbitrary<'_> for TestCase {
fixed_nonallocatable: true,
clobbers: true,
reftypes: false,
callsite_ish_constraints: true,
},
)?,
})
Expand Down
1 change: 1 addition & 0 deletions fuzz/fuzz_targets/ion_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl Arbitrary<'_> for TestCase {
fixed_nonallocatable: true,
clobbers: true,
reftypes: true,
callsite_ish_constraints: true,
},
)?,
})
Expand Down
1 change: 1 addition & 0 deletions fuzz/fuzz_targets/ssagen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl Arbitrary<'_> for TestCase {
fixed_nonallocatable: true,
clobbers: true,
reftypes: true,
callsite_ish_constraints: true,
},
)?,
})
Expand Down
61 changes: 53 additions & 8 deletions src/fuzzing/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ pub struct Options {
pub fixed_nonallocatable: bool,
pub clobbers: bool,
pub reftypes: bool,
pub callsite_ish_constraints: bool,
}

impl core::default::Default for Options {
Expand All @@ -274,6 +275,7 @@ impl core::default::Default for Options {
fixed_nonallocatable: false,
clobbers: false,
reftypes: false,
callsite_ish_constraints: false,
}
}
}
Expand All @@ -289,7 +291,7 @@ impl Func {
// General strategy:
// 1. Create an arbitrary CFG.
// 2. Create a list of vregs to define in each block.
// 3. Define some of those vregs in each block as blockparams.f.
// 3. Define some of those vregs in each block as blockparams.
// 4. Populate blocks with ops that define the rest of the vregs.
// - For each use, choose an available vreg: either one
// already defined (via blockparam or inst) in this block,
Expand Down Expand Up @@ -344,14 +346,19 @@ impl Func {

builder.compute_doms();

let alloc_vreg = |builder: &mut FuncBuilder, u: &mut Unstructured| {
let vreg = VReg::new(builder.f.num_vregs, RegClass::arbitrary(u)?);
builder.f.num_vregs += 1;
Ok(vreg)
};

let mut vregs_by_block = vec![];
let mut vregs_by_block_to_be_defined = vec![];
let mut block_params = vec![vec![]; num_blocks];
for block in 0..num_blocks {
let mut vregs = vec![];
for _ in 0..u.int_in_range(5..=15)? {
let vreg = VReg::new(builder.f.num_vregs, RegClass::arbitrary(u)?);
builder.f.num_vregs += 1;
let vreg = alloc_vreg(&mut builder, u)?;
vregs.push(vreg);
if opts.reftypes && bool::arbitrary(u)? {
builder.f.reftype_vregs.push(vreg);
Expand Down Expand Up @@ -408,7 +415,7 @@ impl Func {
def_pos,
)];
let mut allocations = vec![Allocation::none()];
for _ in 0..u.int_in_range(0..=3)? {
for _ in 0..u.int_in_range(0..=10)? {
let vreg = if avail.len() > 0
&& (remaining_nonlocal_uses == 0 || bool::arbitrary(u)?)
{
Expand Down Expand Up @@ -471,22 +478,22 @@ impl Func {
// Early-defs with fixed constraints conflict with
// any other fixed uses of the same preg.
if fixed_late.contains(&fixed_reg) {
break;
continue;
}
}
if op.kind() == OperandKind::Use && op.pos() == OperandPos::Late {
// Late-use with fixed constraints conflict with
// any other fixed uses of the same preg.
if fixed_early.contains(&fixed_reg) {
break;
continue;
}
}
let fixed_list = match op.pos() {
OperandPos::Early => &mut fixed_early,
OperandPos::Late => &mut fixed_late,
};
if fixed_list.contains(&fixed_reg) {
break;
continue;
}
fixed_list.push(fixed_reg);
operands[i] = Operand::new(
Expand All @@ -496,11 +503,49 @@ impl Func {
op.pos(),
);
}

if opts.callsite_ish_constraints && bool::arbitrary(u)? {
// Define some new vregs with `any`
// constraints.
for _ in 0..u.int_in_range(0..=20)? {
let vreg = alloc_vreg(&mut builder, u)?;
operands.push(Operand::new(
vreg,
OperandConstraint::Any,
OperandKind::Def,
OperandPos::Late,
));
}

// Create some clobbers, avoiding regs named
// by operand constraints. Note that the sum
// of the maximum clobber count here (10) and
// maximum operand count above (10) is less
// than the number of registers in any single
// class, so the resulting problem is always
// allocatable.
for _ in 0..u.int_in_range(0..=10)? {
let reg = u.int_in_range(0..=30)?;
let preg = PReg::new(reg, RegClass::arbitrary(u)?);
if operands
.iter()
.any(|op| match (op.kind(), op.constraint()) {
(OperandKind::Def, OperandConstraint::FixedReg(fixed)) => {
fixed == preg
}
_ => false,
})
{
continue;
}
clobbers.push(preg);
}
}
} else if opts.clobbers && bool::arbitrary(u)? {
for _ in 0..u.int_in_range(0..=5)? {
let reg = u.int_in_range(0..=30)?;
if clobbers.iter().any(|r| r.hw_enc() == reg) {
break;
continue;
}
clobbers.push(PReg::new(reg, RegClass::arbitrary(u)?));
}
Expand Down