/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 | } |