Skip to content

Commit da7313a

Browse files
committed
fuzz: refactor several large blocks into separate functions
This is simply a readability refactor: the internal logic of `Func::arbitrary_with_options` is quite complex already, so moving some of the smaller pieces out into separate functions makes things clearer.
1 parent 98e8862 commit da7313a

File tree

1 file changed

+112
-85
lines changed

1 file changed

+112
-85
lines changed

src/fuzzing/func.rs

Lines changed: 112 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,27 @@ impl FuncBuilder {
207207
);
208208
}
209209

210+
fn add_arbitrary_debug_labels(
211+
&mut self,
212+
u: &mut Unstructured<'_>,
213+
num_blocks: usize,
214+
vreg: VReg,
215+
) -> ArbitraryResult<()> {
216+
let assumed_end_inst = 10 * num_blocks;
217+
let mut start = u.int_in_range::<usize>(0..=assumed_end_inst)?;
218+
Ok(for _ in 0..10 {
219+
if start >= assumed_end_inst {
220+
break;
221+
}
222+
let end = u.int_in_range::<usize>(start..=assumed_end_inst)?;
223+
let label = u.int_in_range::<u32>(0..=100)?;
224+
self.f
225+
.debug_value_labels
226+
.push((vreg, Inst::new(start), Inst::new(end), label));
227+
start = end;
228+
})
229+
}
230+
210231
fn finalize(mut self) -> Func {
211232
for (blocknum, blockrange) in self.f.blocks.iter_mut().enumerate() {
212233
let begin_inst = self.f.insts.len();
@@ -227,6 +248,12 @@ impl Arbitrary<'_> for RegClass {
227248
}
228249
}
229250

251+
impl Arbitrary<'_> for OperandPos {
252+
fn arbitrary(u: &mut Unstructured) -> ArbitraryResult<Self> {
253+
Ok(*u.choose(&[OperandPos::Early, OperandPos::Late])?)
254+
}
255+
}
256+
230257
impl Arbitrary<'_> for OperandConstraint {
231258
fn arbitrary(u: &mut Unstructured) -> ArbitraryResult<Self> {
232259
Ok(*u.choose(&[OperandConstraint::Any, OperandConstraint::Reg])?)
@@ -258,6 +285,82 @@ fn choose_dominating_block(
258285
Ok(block)
259286
}
260287

288+
fn convert_def_to_reuse(u: &mut Unstructured<'_>, operands: &mut [Operand]) -> ArbitraryResult<()> {
289+
let op = operands[0];
290+
debug_assert_eq!(op.kind(), OperandKind::Def);
291+
292+
let reused = u.int_in_range(1..=(operands.len() - 1))?;
293+
Ok(if op.class() == operands[reused].class() {
294+
// Replace the def with a reuse of an existing input.
295+
operands[0] = Operand::new(
296+
op.vreg(),
297+
OperandConstraint::Reuse(reused),
298+
op.kind(),
299+
OperandPos::Late,
300+
);
301+
302+
// Make sure reused input is a register.
303+
let op = operands[reused];
304+
operands[reused] = Operand::new(
305+
op.vreg(),
306+
OperandConstraint::Reg,
307+
op.kind(),
308+
OperandPos::Early,
309+
);
310+
})
311+
}
312+
313+
fn convert_op_to_fixed(
314+
u: &mut Unstructured<'_>,
315+
op: &mut Operand,
316+
fixed_early: &mut Vec<PReg>,
317+
fixed_late: &mut Vec<PReg>,
318+
) -> ArbitraryResult<()> {
319+
let fixed_reg = PReg::new(u.int_in_range(0..=62)?, op.class());
320+
321+
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+
if fixed_late.contains(&fixed_reg) {
325+
return Ok(());
326+
}
327+
}
328+
329+
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+
if fixed_early.contains(&fixed_reg) {
333+
return Ok(());
334+
}
335+
}
336+
337+
// Check that it is not already fixed.
338+
let fixed_list = match op.pos() {
339+
OperandPos::Early => fixed_early,
340+
OperandPos::Late => fixed_late,
341+
};
342+
if fixed_list.contains(&fixed_reg) {
343+
return Ok(());
344+
}
345+
346+
fixed_list.push(fixed_reg);
347+
*op = Operand::new(
348+
op.vreg(),
349+
OperandConstraint::FixedReg(fixed_reg),
350+
op.kind(),
351+
op.pos(),
352+
);
353+
354+
Ok(())
355+
}
356+
357+
fn has_fixed_def_with(preg: PReg) -> impl Fn(&Operand) -> bool {
358+
move |op| match (op.kind(), op.constraint()) {
359+
(OperandKind::Def, OperandConstraint::FixedReg(fixed)) => fixed == preg,
360+
_ => false,
361+
}
362+
}
363+
261364
#[derive(Clone, Debug)]
262365
pub struct Options {
263366
pub reused_inputs: bool,
@@ -371,22 +474,7 @@ impl Func {
371474
builder.f.reftype_vregs.push(vreg);
372475
}
373476
if bool::arbitrary(u)? {
374-
let assumed_end_inst = 10 * num_blocks;
375-
let mut start = u.int_in_range::<usize>(0..=assumed_end_inst)?;
376-
for _ in 0..10 {
377-
if start >= assumed_end_inst {
378-
break;
379-
}
380-
let end = u.int_in_range::<usize>(start..=assumed_end_inst)?;
381-
let label = u.int_in_range::<u32>(0..=100)?;
382-
builder.f.debug_value_labels.push((
383-
vreg,
384-
Inst::new(start),
385-
Inst::new(end),
386-
label,
387-
));
388-
start = end;
389-
}
477+
builder.add_arbitrary_debug_labels(u, num_blocks, vreg)?;
390478
}
391479
}
392480
vregs_by_block.push(vregs.clone());
@@ -417,9 +505,9 @@ impl Func {
417505
};
418506
let mut operands = vec![Operand::new(
419507
vreg,
420-
def_constraint,
508+
OperandConstraint::arbitrary(u)?,
421509
OperandKind::Def,
422-
def_pos,
510+
OperandPos::arbitrary(u)?,
423511
)];
424512
let mut allocations = vec![Allocation::none()];
425513
for _ in 0..u.int_in_range(opts.num_uses_per_inst.clone())? {
@@ -442,86 +530,33 @@ impl Func {
442530
remaining_nonlocal_uses -= 1;
443531
*u.choose(&vregs_by_block[def_block.index()])?
444532
};
445-
let use_constraint = OperandConstraint::arbitrary(u)?;
446533
operands.push(Operand::new(
447534
vreg,
448-
use_constraint,
535+
OperandConstraint::arbitrary(u)?,
449536
OperandKind::Use,
450537
OperandPos::Early,
451538
));
452539
allocations.push(Allocation::none());
453540
}
454541
let mut clobbers: Vec<PReg> = vec![];
455542
if operands.len() > 1 && opts.reused_inputs && bool::arbitrary(u)? {
456-
// Make the def a reused input.
457-
let op = operands[0];
458-
debug_assert_eq!(op.kind(), OperandKind::Def);
459-
let reused = u.int_in_range(1..=(operands.len() - 1))?;
460-
if op.class() == operands[reused].class() {
461-
operands[0] = Operand::new(
462-
op.vreg(),
463-
OperandConstraint::Reuse(reused),
464-
op.kind(),
465-
OperandPos::Late,
466-
);
467-
// Make sure reused input is a Reg.
468-
let op = operands[reused];
469-
operands[reused] = Operand::new(
470-
op.vreg(),
471-
OperandConstraint::Reg,
472-
op.kind(),
473-
OperandPos::Early,
474-
);
475-
}
543+
convert_def_to_reuse(u, &mut operands)?;
476544
} else if opts.fixed_regs && bool::arbitrary(u)? {
477545
let mut fixed_early = vec![];
478546
let mut fixed_late = vec![];
479547
for _ in 0..u.int_in_range(0..=operands.len() - 1)? {
480548
// Pick an operand and make it a fixed reg.
481549
let i = u.int_in_range(0..=(operands.len() - 1))?;
482-
let op = operands[i];
483-
let fixed_reg = PReg::new(u.int_in_range(0..=62)?, op.class());
484-
if op.kind() == OperandKind::Def && op.pos() == OperandPos::Early {
485-
// Early-defs with fixed constraints conflict with
486-
// any other fixed uses of the same preg.
487-
if fixed_late.contains(&fixed_reg) {
488-
continue;
489-
}
490-
}
491-
if op.kind() == OperandKind::Use && op.pos() == OperandPos::Late {
492-
// Late-use with fixed constraints conflict with
493-
// any other fixed uses of the same preg.
494-
if fixed_early.contains(&fixed_reg) {
495-
continue;
496-
}
497-
}
498-
let fixed_list = match op.pos() {
499-
OperandPos::Early => &mut fixed_early,
500-
OperandPos::Late => &mut fixed_late,
501-
};
502-
if fixed_list.contains(&fixed_reg) {
503-
continue;
504-
}
505-
fixed_list.push(fixed_reg);
506-
operands[i] = Operand::new(
507-
op.vreg(),
508-
OperandConstraint::FixedReg(fixed_reg),
509-
op.kind(),
510-
op.pos(),
511-
);
550+
let op = &mut operands[i];
551+
convert_op_to_fixed(u, op, &mut fixed_early, &mut fixed_late)?;
512552
}
513553

514554
if opts.callsite_ish_constraints && bool::arbitrary(u)? {
515555
// Define some new vregs with `any`
516556
// constraints.
517557
for _ in 0..u.int_in_range(opts.num_callsite_ish_vregs_per_inst.clone())? {
518558
let vreg = alloc_vreg(&mut builder, u)?;
519-
operands.push(Operand::new(
520-
vreg,
521-
OperandConstraint::Any,
522-
OperandKind::Def,
523-
OperandPos::Late,
524-
));
559+
operands.push(Operand::any_def(vreg));
525560
}
526561

527562
// Create some clobbers, avoiding regs named
@@ -534,15 +569,7 @@ impl Func {
534569
for _ in 0..u.int_in_range(opts.num_clobbers_per_inst.clone())? {
535570
let reg = u.int_in_range(0..=30)?;
536571
let preg = PReg::new(reg, RegClass::arbitrary(u)?);
537-
if operands
538-
.iter()
539-
.any(|op| match (op.kind(), op.constraint()) {
540-
(OperandKind::Def, OperandConstraint::FixedReg(fixed)) => {
541-
fixed == preg
542-
}
543-
_ => false,
544-
})
545-
{
572+
if operands.iter().any(has_fixed_def_with(preg)) {
546573
continue;
547574
}
548575
clobbers.push(preg);

0 commit comments

Comments
 (0)