/src/regalloc.rs/bin/test_framework.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! As part of this set of test cases, we define a mini IR and implement the |
2 | | //! `Function` trait for it so that we can use the regalloc public interface. |
3 | | |
4 | | use arbitrary::Arbitrary; |
5 | | use regalloc::*; |
6 | | use std::collections::HashSet; |
7 | | |
8 | | use std::{borrow::Cow, fmt}; |
9 | | |
10 | | use crate::{ |
11 | | parser::REFTYPE_START, |
12 | | validator::{validate, Context as ValidatorContext, RegRef}, |
13 | | }; |
14 | | |
15 | | use log::debug; |
16 | | |
17 | | //============================================================================= |
18 | | // Definition of: Label, RI (reg-or-immediate operands), AM (address modes), |
19 | | // and Inst (instructions). Also the get-regs and map-regs operations for |
20 | | // them. Destinations are on the left. |
21 | | |
22 | 51.2k | #[derive(Clone)] <minira::test_framework::Label as core::clone::Clone>::clone Line | Count | Source | 22 | 51.2k | #[derive(Clone)] |
|
23 | | pub enum Label { |
24 | | Unresolved { name: String }, |
25 | | Resolved { name: String, bix: BlockIx }, |
26 | | } |
27 | | |
28 | | impl Label { |
29 | | pub fn new_unresolved(name: String) -> Label { |
30 | | Label::Unresolved { name } |
31 | | } |
32 | 46.9k | pub fn get_block_ix(&self) -> BlockIx { |
33 | 46.9k | match self { |
34 | 46.9k | Label::Resolved { name: _, bix } => *bix, |
35 | 0 | Label::Unresolved { .. } => panic!("Label::getBlockIx: unresolved label!"), |
36 | | } |
37 | 46.9k | } |
38 | 0 | pub fn type_checks(&self, cx: &ValidatorContext) -> bool { |
39 | 0 | match self { |
40 | 0 | Label::Unresolved { .. } => false, |
41 | 0 | Label::Resolved { bix, .. } => { |
42 | 0 | let bix_u32: u32 = (*bix).into(); |
43 | 0 | bix_u32 < cx.num_blocks |
44 | | } |
45 | | } |
46 | 0 | } |
47 | | } |
48 | | |
49 | | impl fmt::Debug for Label { |
50 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
51 | 0 | match self { |
52 | 0 | Label::Unresolved { name } => write!(fmt, "??:{}", &name), |
53 | 0 | Label::Resolved { name, bix } => write!(fmt, "{:?}:{}", bix, name), |
54 | | } |
55 | 0 | } |
56 | | } |
57 | | |
58 | 6.49k | #[derive(Copy, Clone)] |
59 | | pub enum RI { |
60 | | Reg { reg: Reg }, |
61 | | Imm { imm: u32 }, |
62 | | } |
63 | | |
64 | | #[allow(non_snake_case)] |
65 | | pub fn RI_R(reg: Reg) -> RI { |
66 | | debug_assert!(reg.get_class() == RegClass::I32); |
67 | | RI::Reg { reg } |
68 | | } |
69 | | |
70 | | #[allow(non_snake_case)] |
71 | | pub fn RI_I(imm: u32) -> RI { |
72 | | RI::Imm { imm } |
73 | | } |
74 | | |
75 | | impl fmt::Debug for RI { |
76 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
77 | 0 | match self { |
78 | 0 | RI::Reg { reg } => reg.fmt(fmt), |
79 | 0 | RI::Imm { imm } => write!(fmt, "{}", imm), |
80 | | } |
81 | 0 | } |
82 | | } |
83 | | impl RI { |
84 | 8.73k | fn add_reg_reads_to(&self, collector: &mut RegUsageCollector) { |
85 | 8.73k | match self { |
86 | 5.61k | RI::Reg { reg } => collector.add_use(*reg), |
87 | 3.11k | RI::Imm { .. } => {} |
88 | | } |
89 | 8.73k | } |
90 | 2.84k | fn apply_uses<RUM: RegUsageMapper>(&mut self, mapper: &RUM) { |
91 | 2.84k | match self { |
92 | 1.83k | RI::Reg { ref mut reg } => { |
93 | 1.83k | reg.apply_uses(mapper); |
94 | 1.83k | } |
95 | 1.01k | RI::Imm { .. } => {} |
96 | | } |
97 | 2.84k | } Unexecuted instantiation: <minira::test_framework::RI>::apply_uses::<regalloc::reg_maps::MentionRegUsageMapper> <minira::test_framework::RI>::apply_uses::<regalloc::reg_maps::VrangeRegUsageMapper> Line | Count | Source | 90 | 2.84k | fn apply_uses<RUM: RegUsageMapper>(&mut self, mapper: &RUM) { | 91 | 2.84k | match self { | 92 | 1.83k | RI::Reg { ref mut reg } => { | 93 | 1.83k | reg.apply_uses(mapper); | 94 | 1.83k | } | 95 | 1.01k | RI::Imm { .. } => {} | 96 | | } | 97 | 2.84k | } |
|
98 | 0 | fn type_checks(&self, cx: &mut ValidatorContext) -> bool { |
99 | 0 | match self { |
100 | 0 | RI::Reg { reg } => cx.check_reg_rc(reg, RegRef::Use, RegClass::I32), |
101 | 0 | RI::Imm { .. } => true, |
102 | | } |
103 | 0 | } |
104 | | } |
105 | | |
106 | 12.9k | #[derive(Copy, Clone)] |
107 | | pub enum AM { |
108 | | RI { base: Reg, offset: u32 }, |
109 | | RR { base: Reg, offset: Reg }, |
110 | | } |
111 | | |
112 | | #[allow(non_snake_case)] |
113 | | pub fn AM_R(base: Reg) -> AM { |
114 | | debug_assert!(base.get_class() == RegClass::I32); |
115 | | AM::RI { base, offset: 0 } |
116 | | } |
117 | | |
118 | | #[allow(non_snake_case)] |
119 | | pub fn AM_RI(base: Reg, offset: u32) -> AM { |
120 | | debug_assert!(base.get_class() == RegClass::I32); |
121 | | AM::RI { base, offset } |
122 | | } |
123 | | |
124 | | #[allow(non_snake_case)] |
125 | | pub fn AM_RR(base: Reg, offset: Reg) -> AM { |
126 | | debug_assert!(base.get_class() == RegClass::I32); |
127 | | debug_assert!(offset.get_class() == RegClass::I32); |
128 | | AM::RR { base, offset } |
129 | | } |
130 | | |
131 | | impl fmt::Debug for AM { |
132 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
133 | 0 | match self { |
134 | 0 | AM::RI { base, offset } => write!(fmt, "[{:?}, {:?}]", base, offset), |
135 | 0 | AM::RR { base, offset } => write!(fmt, "[{:?}, {:?}]", base, offset), |
136 | | } |
137 | 0 | } |
138 | | } |
139 | | |
140 | | impl AM { |
141 | 17.1k | fn add_reg_reads_to(&self, collector: &mut RegUsageCollector) { |
142 | 17.1k | match self { |
143 | 8.87k | AM::RI { base, .. } => collector.add_use(*base), |
144 | 8.23k | AM::RR { base, offset } => { |
145 | 8.23k | collector.add_use(*base); |
146 | 8.23k | collector.add_use(*offset); |
147 | 8.23k | } |
148 | | } |
149 | 17.1k | } |
150 | | |
151 | 5.37k | fn apply_uses<RUM: RegUsageMapper>(&mut self, mapper: &RUM) { |
152 | 5.37k | match self { |
153 | 2.83k | AM::RI { ref mut base, .. } => { |
154 | 2.83k | base.apply_uses(mapper); |
155 | 2.83k | } |
156 | | AM::RR { |
157 | 2.53k | ref mut base, |
158 | 2.53k | ref mut offset, |
159 | 2.53k | } => { |
160 | 2.53k | base.apply_uses(mapper); |
161 | 2.53k | offset.apply_uses(mapper); |
162 | 2.53k | } |
163 | | } |
164 | 5.37k | } <minira::test_framework::AM>::apply_uses::<regalloc::reg_maps::VrangeRegUsageMapper> Line | Count | Source | 151 | 5.37k | fn apply_uses<RUM: RegUsageMapper>(&mut self, mapper: &RUM) { | 152 | 5.37k | match self { | 153 | 2.83k | AM::RI { ref mut base, .. } => { | 154 | 2.83k | base.apply_uses(mapper); | 155 | 2.83k | } | 156 | | AM::RR { | 157 | 2.53k | ref mut base, | 158 | 2.53k | ref mut offset, | 159 | 2.53k | } => { | 160 | 2.53k | base.apply_uses(mapper); | 161 | 2.53k | offset.apply_uses(mapper); | 162 | 2.53k | } | 163 | | } | 164 | 5.37k | } |
Unexecuted instantiation: <minira::test_framework::AM>::apply_uses::<regalloc::reg_maps::MentionRegUsageMapper> |
165 | | |
166 | 0 | fn type_checks(&self, cx: &mut ValidatorContext) -> bool { |
167 | 0 | use RegClass::*; |
168 | 0 | match self { |
169 | 0 | AM::RI { base, .. } => cx.check_reg_rc(base, RegRef::Use, I32), |
170 | 0 | AM::RR { base, offset } => { |
171 | 0 | cx.check_reg_rc(base, RegRef::Use, I32) && cx.check_reg_rc(offset, RegRef::Use, I32) |
172 | | } |
173 | | } |
174 | 0 | } |
175 | | } |
176 | | |
177 | 6.49k | #[derive(Copy, Clone, Arbitrary)] |
178 | | pub enum BinOp { |
179 | | Add, |
180 | | Sub, |
181 | | Mul, |
182 | | Mod, |
183 | | Shr, |
184 | | And, |
185 | | CmpEQ, |
186 | | CmpLT, |
187 | | CmpLE, |
188 | | CmpGE, |
189 | | CmpGT, |
190 | | } |
191 | | |
192 | | impl fmt::Debug for BinOp { |
193 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
194 | | write!( |
195 | 0 | fmt, |
196 | 0 | "{}", |
197 | 0 | match self { |
198 | 0 | BinOp::Add => "add", |
199 | 0 | BinOp::Sub => "sub", |
200 | 0 | BinOp::Mul => "mul", |
201 | 0 | BinOp::Mod => "mod", |
202 | 0 | BinOp::Shr => "shr", |
203 | 0 | BinOp::And => "and", |
204 | 0 | BinOp::CmpEQ => "cmp_eq", |
205 | 0 | BinOp::CmpLT => "cmp_lt", |
206 | 0 | BinOp::CmpLE => "cmp_le", |
207 | 0 | BinOp::CmpGE => "cmp_ge", |
208 | 0 | BinOp::CmpGT => "cmp_gt", |
209 | | } |
210 | | ) |
211 | 0 | } |
212 | | } |
213 | | |
214 | | impl fmt::Display for BinOp { |
215 | | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
216 | | (self as &dyn fmt::Debug).fmt(fmt) |
217 | | } |
218 | | } |
219 | | |
220 | | impl BinOp { |
221 | 0 | pub fn calc(self, arg_left: u32, arg_right: u32) -> IResult<u32> { |
222 | 0 | Ok(match self { |
223 | 0 | BinOp::Add => u32::wrapping_add(arg_left, arg_right), |
224 | 0 | BinOp::Sub => u32::wrapping_sub(arg_left, arg_right), |
225 | 0 | BinOp::Mul => u32::wrapping_mul(arg_left, arg_right), |
226 | | BinOp::Mod => { |
227 | 0 | if arg_right == 0 { |
228 | 0 | return Err("modulo by 0".into()); |
229 | 0 | } |
230 | 0 | arg_left % arg_right |
231 | | } |
232 | 0 | BinOp::Shr => arg_left >> (arg_right & 31), |
233 | 0 | BinOp::And => arg_left & arg_right, |
234 | | BinOp::CmpEQ => { |
235 | 0 | if arg_left == arg_right { |
236 | 0 | 1 |
237 | | } else { |
238 | 0 | 0 |
239 | | } |
240 | | } |
241 | | BinOp::CmpLT => { |
242 | 0 | if arg_left < arg_right { |
243 | 0 | 1 |
244 | | } else { |
245 | 0 | 0 |
246 | | } |
247 | | } |
248 | | BinOp::CmpLE => { |
249 | 0 | if arg_left <= arg_right { |
250 | 0 | 1 |
251 | | } else { |
252 | 0 | 0 |
253 | | } |
254 | | } |
255 | | BinOp::CmpGE => { |
256 | 0 | if arg_left >= arg_right { |
257 | 0 | 1 |
258 | | } else { |
259 | 0 | 0 |
260 | | } |
261 | | } |
262 | | BinOp::CmpGT => { |
263 | 0 | if arg_left > arg_right { |
264 | 0 | 1 |
265 | | } else { |
266 | 0 | 0 |
267 | | } |
268 | | } |
269 | | }) |
270 | 0 | } |
271 | | } |
272 | | |
273 | 661 | #[derive(Copy, Clone, Arbitrary)] |
274 | | pub enum BinOpF { |
275 | | FAdd, |
276 | | FSub, |
277 | | FMul, |
278 | | FDiv, |
279 | | } |
280 | | |
281 | | impl fmt::Debug for BinOpF { |
282 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
283 | | write!( |
284 | 0 | fmt, |
285 | 0 | "{}", |
286 | 0 | match self { |
287 | 0 | BinOpF::FAdd => "fadd".to_string(), |
288 | 0 | BinOpF::FSub => "fsub".to_string(), |
289 | 0 | BinOpF::FMul => "fmul".to_string(), |
290 | 0 | BinOpF::FDiv => "fdiv".to_string(), |
291 | | } |
292 | | ) |
293 | 0 | } |
294 | | } |
295 | | |
296 | | impl fmt::Display for BinOpF { |
297 | | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
298 | | (self as &dyn fmt::Debug).fmt(fmt) |
299 | | } |
300 | | } |
301 | | |
302 | | impl BinOpF { |
303 | 0 | pub fn calc(self, arg_left: f32, arg_right: f32) -> IResult<f32> { |
304 | 0 | Ok(match self { |
305 | 0 | BinOpF::FAdd => arg_left + arg_right, |
306 | 0 | BinOpF::FSub => arg_left - arg_right, |
307 | 0 | BinOpF::FMul => arg_left * arg_right, |
308 | | BinOpF::FDiv => { |
309 | 0 | if arg_right == 0.0 { |
310 | 0 | return Err("division by zero".into()); |
311 | 0 | } |
312 | 0 | arg_left / arg_right |
313 | | } |
314 | | }) |
315 | 0 | } |
316 | | } |
317 | | |
318 | 221k | #[derive(Clone)] |
319 | | pub enum Inst { |
320 | | NopZ {}, |
321 | | Imm { |
322 | | dst: Reg, |
323 | | imm: u32, |
324 | | }, |
325 | | ImmF { |
326 | | dst: Reg, |
327 | | imm: f32, |
328 | | }, |
329 | | Copy { |
330 | | dst: Reg, |
331 | | src: Reg, |
332 | | }, |
333 | | CopyF { |
334 | | dst: Reg, |
335 | | src: Reg, |
336 | | }, |
337 | | BinOp { |
338 | | op: BinOp, |
339 | | dst: Reg, |
340 | | src_left: Reg, |
341 | | src_right: RI, |
342 | | }, |
343 | | BinOpM { |
344 | | op: BinOp, |
345 | | dst: Reg, |
346 | | src_right: RI, |
347 | | }, // "mod" semantics for `dst` |
348 | | BinOpF { |
349 | | op: BinOpF, |
350 | | dst: Reg, |
351 | | src_left: Reg, |
352 | | src_right: Reg, |
353 | | }, |
354 | | Load { |
355 | | dst: Reg, |
356 | | addr: AM, |
357 | | }, |
358 | | LoadF { |
359 | | dst: Reg, |
360 | | addr: AM, |
361 | | }, |
362 | | Store { |
363 | | addr: AM, |
364 | | src: Reg, |
365 | | }, |
366 | | StoreF { |
367 | | addr: AM, |
368 | | src: Reg, |
369 | | }, |
370 | | MakeRef { |
371 | | dst: Reg, |
372 | | src: Reg, |
373 | | }, |
374 | | UseRef { |
375 | | dst: Reg, |
376 | | src: Reg, |
377 | | }, |
378 | | Safepoint, |
379 | | Spill { |
380 | | dst: SpillSlot, |
381 | | src: RealReg, |
382 | | }, |
383 | | SpillF { |
384 | | dst: SpillSlot, |
385 | | src: RealReg, |
386 | | }, |
387 | | Reload { |
388 | | dst: RealReg, |
389 | | src: SpillSlot, |
390 | | }, |
391 | | ReloadF { |
392 | | dst: RealReg, |
393 | | src: SpillSlot, |
394 | | }, |
395 | | Goto { |
396 | | target: Label, |
397 | | }, |
398 | | GotoCTF { |
399 | | cond: Reg, |
400 | | target_true: Label, |
401 | | target_false: Label, |
402 | | }, |
403 | | PrintS { |
404 | | str: String, |
405 | | }, |
406 | | PrintI { |
407 | | reg: Reg, |
408 | | }, |
409 | | PrintF { |
410 | | reg: Reg, |
411 | | }, |
412 | | Finish { |
413 | | reg: Option<Reg>, |
414 | | }, |
415 | | } |
416 | | |
417 | | pub fn i_imm(dst: Reg, imm: u32) -> Inst { |
418 | | debug_assert!(dst.get_class() == RegClass::I32); |
419 | | Inst::Imm { dst, imm } |
420 | | } |
421 | | pub fn i_immf(dst: Reg, imm: f32) -> Inst { |
422 | | debug_assert!(dst.get_class() == RegClass::F32); |
423 | | Inst::ImmF { dst, imm } |
424 | | } |
425 | | pub fn i_copy(dst: Reg, src: Reg) -> Inst { |
426 | | debug_assert!(dst.get_class() == RegClass::I32); |
427 | | debug_assert!(src.get_class() == RegClass::I32); |
428 | | Inst::Copy { dst, src } |
429 | | } |
430 | | pub fn i_copyf(dst: Reg, src: Reg) -> Inst { |
431 | | debug_assert!(dst.get_class() == RegClass::F32); |
432 | | debug_assert!(src.get_class() == RegClass::F32); |
433 | | Inst::CopyF { dst, src } |
434 | | } |
435 | | // For BinOp variants see below |
436 | | |
437 | | pub fn i_load(dst: Reg, addr: AM) -> Inst { |
438 | | debug_assert!(dst.get_class() == RegClass::I32); |
439 | | Inst::Load { dst, addr } |
440 | | } |
441 | | pub fn i_loadf(dst: Reg, addr: AM) -> Inst { |
442 | | debug_assert!(dst.get_class() == RegClass::F32); |
443 | | Inst::LoadF { dst, addr } |
444 | | } |
445 | | pub fn i_store(addr: AM, src: Reg) -> Inst { |
446 | | debug_assert!(src.get_class() == RegClass::I32); |
447 | | Inst::Store { addr, src } |
448 | | } |
449 | | pub fn i_storef(addr: AM, src: Reg) -> Inst { |
450 | | debug_assert!(src.get_class() == RegClass::F32); |
451 | | Inst::StoreF { addr, src } |
452 | | } |
453 | 11.2k | fn i_spill(dst: SpillSlot, src: RealReg) -> Inst { |
454 | | debug_assert!(src.get_class() == RegClass::I32); |
455 | 11.2k | Inst::Spill { dst, src } |
456 | 11.2k | } |
457 | 102 | fn i_spillf(dst: SpillSlot, src: RealReg) -> Inst { |
458 | | debug_assert!(src.get_class() == RegClass::F32); |
459 | 102 | Inst::SpillF { dst, src } |
460 | 102 | } |
461 | 16.0k | fn i_reload(dst: RealReg, src: SpillSlot) -> Inst { |
462 | | debug_assert!(dst.get_class() == RegClass::I32); |
463 | 16.0k | Inst::Reload { dst, src } |
464 | 16.0k | } |
465 | 288 | fn i_reloadf(dst: RealReg, src: SpillSlot) -> Inst { |
466 | | debug_assert!(dst.get_class() == RegClass::F32); |
467 | 288 | Inst::ReloadF { dst, src } |
468 | 288 | } |
469 | | pub fn i_goto<'a>(target: &'a str) -> Inst { |
470 | | Inst::Goto { |
471 | | target: Label::new_unresolved(target.to_string()), |
472 | | } |
473 | | } |
474 | | pub fn i_goto_ctf<'a>(cond: Reg, target_true: &'a str, target_false: &'a str) -> Inst { |
475 | | debug_assert!(cond.get_class() == RegClass::I32); |
476 | | Inst::GotoCTF { |
477 | | cond, |
478 | | target_true: Label::new_unresolved(target_true.to_string()), |
479 | | target_false: Label::new_unresolved(target_false.to_string()), |
480 | | } |
481 | | } |
482 | | pub fn i_print_s<'a>(str: &'a str) -> Inst { |
483 | | Inst::PrintS { |
484 | | str: str.to_string(), |
485 | | } |
486 | | } |
487 | | pub fn i_print_i(reg: Reg) -> Inst { |
488 | | debug_assert!(reg.get_class() == RegClass::I32); |
489 | | Inst::PrintI { reg } |
490 | | } |
491 | 0 | pub fn i_print_f(reg: Reg) -> Inst { |
492 | | debug_assert!(reg.get_class() == RegClass::F32); |
493 | 0 | Inst::PrintF { reg } |
494 | 0 | } |
495 | | pub fn i_finish(reg: Option<Reg>) -> Inst { |
496 | | Inst::Finish { reg } |
497 | | } |
498 | | |
499 | | pub fn i_add(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
500 | | debug_assert!(dst.get_class() == RegClass::I32); |
501 | | debug_assert!(src_left.get_class() == RegClass::I32); |
502 | | Inst::BinOp { |
503 | | op: BinOp::Add, |
504 | | dst, |
505 | | src_left, |
506 | | src_right, |
507 | | } |
508 | | } |
509 | | pub fn i_sub(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
510 | | debug_assert!(dst.get_class() == RegClass::I32); |
511 | | debug_assert!(src_left.get_class() == RegClass::I32); |
512 | | Inst::BinOp { |
513 | | op: BinOp::Sub, |
514 | | dst, |
515 | | src_left, |
516 | | src_right, |
517 | | } |
518 | | } |
519 | | pub fn i_mul(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
520 | | debug_assert!(dst.get_class() == RegClass::I32); |
521 | | debug_assert!(src_left.get_class() == RegClass::I32); |
522 | | Inst::BinOp { |
523 | | op: BinOp::Mul, |
524 | | dst, |
525 | | src_left, |
526 | | src_right, |
527 | | } |
528 | | } |
529 | | pub fn i_mod(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
530 | | debug_assert!(dst.get_class() == RegClass::I32); |
531 | | debug_assert!(src_left.get_class() == RegClass::I32); |
532 | | Inst::BinOp { |
533 | | op: BinOp::Mod, |
534 | | dst, |
535 | | src_left, |
536 | | src_right, |
537 | | } |
538 | | } |
539 | | pub fn i_shr(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
540 | | debug_assert!(dst.get_class() == RegClass::I32); |
541 | | debug_assert!(src_left.get_class() == RegClass::I32); |
542 | | Inst::BinOp { |
543 | | op: BinOp::Shr, |
544 | | dst, |
545 | | src_left, |
546 | | src_right, |
547 | | } |
548 | | } |
549 | | pub fn i_and(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
550 | | debug_assert!(dst.get_class() == RegClass::I32); |
551 | | debug_assert!(src_left.get_class() == RegClass::I32); |
552 | | Inst::BinOp { |
553 | | op: BinOp::And, |
554 | | dst, |
555 | | src_left, |
556 | | src_right, |
557 | | } |
558 | | } |
559 | | pub fn i_cmp_eq(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
560 | | debug_assert!(dst.get_class() == RegClass::I32); |
561 | | debug_assert!(src_left.get_class() == RegClass::I32); |
562 | | Inst::BinOp { |
563 | | op: BinOp::CmpEQ, |
564 | | dst, |
565 | | src_left, |
566 | | src_right, |
567 | | } |
568 | | } |
569 | | pub fn i_cmp_lt(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
570 | | debug_assert!(dst.get_class() == RegClass::I32); |
571 | | debug_assert!(src_left.get_class() == RegClass::I32); |
572 | | Inst::BinOp { |
573 | | op: BinOp::CmpLT, |
574 | | dst, |
575 | | src_left, |
576 | | src_right, |
577 | | } |
578 | | } |
579 | | pub fn i_cmp_le(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
580 | | debug_assert!(dst.get_class() == RegClass::I32); |
581 | | debug_assert!(src_left.get_class() == RegClass::I32); |
582 | | Inst::BinOp { |
583 | | op: BinOp::CmpLE, |
584 | | dst, |
585 | | src_left, |
586 | | src_right, |
587 | | } |
588 | | } |
589 | | pub fn i_cmp_ge(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
590 | | debug_assert!(dst.get_class() == RegClass::I32); |
591 | | debug_assert!(src_left.get_class() == RegClass::I32); |
592 | | Inst::BinOp { |
593 | | op: BinOp::CmpGE, |
594 | | dst, |
595 | | src_left, |
596 | | src_right, |
597 | | } |
598 | | } |
599 | | pub fn i_cmp_gt(dst: Reg, src_left: Reg, src_right: RI) -> Inst { |
600 | | debug_assert!(dst.get_class() == RegClass::I32); |
601 | | debug_assert!(src_left.get_class() == RegClass::I32); |
602 | | Inst::BinOp { |
603 | | op: BinOp::CmpGT, |
604 | | dst, |
605 | | src_left, |
606 | | src_right, |
607 | | } |
608 | | } |
609 | | |
610 | | // 2-operand versions of i_add and i_sub, for experimentation |
611 | | pub fn i_addm(dst: Reg, src_right: RI) -> Inst { |
612 | | debug_assert!(dst.get_class() == RegClass::I32); |
613 | | Inst::BinOpM { |
614 | | op: BinOp::Add, |
615 | | dst, |
616 | | src_right, |
617 | | } |
618 | | } |
619 | | pub fn i_andm(dst: Reg, src_right: RI) -> Inst { |
620 | | debug_assert!(dst.get_class() == RegClass::I32); |
621 | | Inst::BinOpM { |
622 | | op: BinOp::And, |
623 | | dst, |
624 | | src_right, |
625 | | } |
626 | | } |
627 | | pub fn i_modm(dst: Reg, src_right: RI) -> Inst { |
628 | | debug_assert!(dst.get_class() == RegClass::I32); |
629 | | Inst::BinOpM { |
630 | | op: BinOp::Mod, |
631 | | dst, |
632 | | src_right, |
633 | | } |
634 | | } |
635 | | pub fn i_mulm(dst: Reg, src_right: RI) -> Inst { |
636 | | debug_assert!(dst.get_class() == RegClass::I32); |
637 | | Inst::BinOpM { |
638 | | op: BinOp::Mul, |
639 | | dst, |
640 | | src_right, |
641 | | } |
642 | | } |
643 | | pub fn i_shrm(dst: Reg, src_right: RI) -> Inst { |
644 | | debug_assert!(dst.get_class() == RegClass::I32); |
645 | | Inst::BinOpM { |
646 | | op: BinOp::Shr, |
647 | | dst, |
648 | | src_right, |
649 | | } |
650 | | } |
651 | | pub fn i_subm(dst: Reg, src_right: RI) -> Inst { |
652 | | debug_assert!(dst.get_class() == RegClass::I32); |
653 | | Inst::BinOpM { |
654 | | op: BinOp::Sub, |
655 | | dst, |
656 | | src_right, |
657 | | } |
658 | | } |
659 | | pub fn i_cmp_eqm(dst: Reg, src_right: RI) -> Inst { |
660 | | debug_assert!(dst.get_class() == RegClass::I32); |
661 | | Inst::BinOpM { |
662 | | op: BinOp::CmpEQ, |
663 | | dst, |
664 | | src_right, |
665 | | } |
666 | | } |
667 | | pub fn i_cmp_gem(dst: Reg, src_right: RI) -> Inst { |
668 | | debug_assert!(dst.get_class() == RegClass::I32); |
669 | | Inst::BinOpM { |
670 | | op: BinOp::CmpGE, |
671 | | dst, |
672 | | src_right, |
673 | | } |
674 | | } |
675 | | pub fn i_cmp_gtm(dst: Reg, src_right: RI) -> Inst { |
676 | | debug_assert!(dst.get_class() == RegClass::I32); |
677 | | Inst::BinOpM { |
678 | | op: BinOp::CmpGT, |
679 | | dst, |
680 | | src_right, |
681 | | } |
682 | | } |
683 | | pub fn i_cmp_lem(dst: Reg, src_right: RI) -> Inst { |
684 | | debug_assert!(dst.get_class() == RegClass::I32); |
685 | | Inst::BinOpM { |
686 | | op: BinOp::CmpLE, |
687 | | dst, |
688 | | src_right, |
689 | | } |
690 | | } |
691 | | pub fn i_cmp_ltm(dst: Reg, src_right: RI) -> Inst { |
692 | | debug_assert!(dst.get_class() == RegClass::I32); |
693 | | Inst::BinOpM { |
694 | | op: BinOp::CmpLT, |
695 | | dst, |
696 | | src_right, |
697 | | } |
698 | | } |
699 | | |
700 | | pub fn i_fadd(dst: Reg, src_left: Reg, src_right: Reg) -> Inst { |
701 | | debug_assert!(dst.get_class() == RegClass::F32); |
702 | | debug_assert!(src_left.get_class() == RegClass::F32); |
703 | | debug_assert!(src_right.get_class() == RegClass::F32); |
704 | | Inst::BinOpF { |
705 | | op: BinOpF::FAdd, |
706 | | dst, |
707 | | src_left, |
708 | | src_right, |
709 | | } |
710 | | } |
711 | | pub fn i_fsub(dst: Reg, src_left: Reg, src_right: Reg) -> Inst { |
712 | | debug_assert!(dst.get_class() == RegClass::F32); |
713 | | debug_assert!(src_left.get_class() == RegClass::F32); |
714 | | debug_assert!(src_right.get_class() == RegClass::F32); |
715 | | Inst::BinOpF { |
716 | | op: BinOpF::FSub, |
717 | | dst, |
718 | | src_left, |
719 | | src_right, |
720 | | } |
721 | | } |
722 | | pub fn i_fmul(dst: Reg, src_left: Reg, src_right: Reg) -> Inst { |
723 | | debug_assert!(dst.get_class() == RegClass::F32); |
724 | | debug_assert!(src_left.get_class() == RegClass::F32); |
725 | | debug_assert!(src_right.get_class() == RegClass::F32); |
726 | | Inst::BinOpF { |
727 | | op: BinOpF::FMul, |
728 | | dst, |
729 | | src_left, |
730 | | src_right, |
731 | | } |
732 | | } |
733 | | pub fn i_fdiv(dst: Reg, src_left: Reg, src_right: Reg) -> Inst { |
734 | | debug_assert!(dst.get_class() == RegClass::F32); |
735 | | debug_assert!(src_left.get_class() == RegClass::F32); |
736 | | debug_assert!(src_right.get_class() == RegClass::F32); |
737 | | Inst::BinOpF { |
738 | | op: BinOpF::FDiv, |
739 | | dst, |
740 | | src_left, |
741 | | src_right, |
742 | | } |
743 | | } |
744 | | |
745 | | impl fmt::Debug for Inst { |
746 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
747 | 0 | fn ljustify(s: String, w: usize) -> String { |
748 | 0 | if s.len() >= w { |
749 | 0 | s |
750 | 0 | } else { |
751 | 0 | // BEGIN hack |
752 | 0 | let mut need = w - s.len(); |
753 | 0 | if need > 5 { |
754 | 0 | need = 5; |
755 | 0 | } |
756 | 0 | let extra = [" ", " ", " ", " ", " "][need - 1]; |
757 | 0 | // END hack |
758 | 0 | s + &extra.to_string() |
759 | 0 | } |
760 | 0 | } |
761 | 0 |
|
762 | 0 | match self { |
763 | 0 | Inst::NopZ {} => write!(fmt, "nopz"), |
764 | 0 | Inst::Imm { dst, imm } => write!(fmt, "imm {:?}, {:?}", dst, imm), |
765 | 0 | Inst::ImmF { dst, imm } => write!(fmt, "immf {:?}, {:?}", dst, imm), |
766 | 0 | Inst::Copy { dst, src } => write!(fmt, "copy {:?}, {:?}", dst, src), |
767 | 0 | Inst::CopyF { dst, src } => write!(fmt, "copyf {:?}, {:?}", dst, src), |
768 | | Inst::BinOp { |
769 | 0 | op, |
770 | 0 | dst, |
771 | 0 | src_left, |
772 | 0 | src_right, |
773 | 0 | } => write!( |
774 | 0 | fmt, |
775 | 0 | "{} {:?}, {:?}, {:?}", |
776 | 0 | ljustify(op.to_string(), 7), |
777 | 0 | dst, |
778 | 0 | src_left, |
779 | 0 | src_right |
780 | 0 | ), |
781 | 0 | Inst::BinOpM { op, dst, src_right } => write!( |
782 | 0 | fmt, |
783 | 0 | "{} {:?}, {:?}", |
784 | 0 | ljustify(op.to_string() + &"m".to_string(), 7), |
785 | 0 | dst, |
786 | 0 | src_right |
787 | 0 | ), |
788 | | Inst::BinOpF { |
789 | 0 | op, |
790 | 0 | dst, |
791 | 0 | src_left, |
792 | 0 | src_right, |
793 | 0 | } => write!( |
794 | 0 | fmt, |
795 | 0 | "{} {:?}, {:?}, {:?}", |
796 | 0 | ljustify(op.to_string(), 7), |
797 | 0 | dst, |
798 | 0 | src_left, |
799 | 0 | src_right |
800 | 0 | ), |
801 | 0 | Inst::Load { dst, addr } => write!(fmt, "load {:?}, {:?}", dst, addr), |
802 | 0 | Inst::LoadF { dst, addr } => write!(fmt, "loadf {:?}, {:?}", dst, addr), |
803 | 0 | Inst::Store { addr, src } => write!(fmt, "store {:?}, {:?}", addr, src), |
804 | 0 | Inst::StoreF { addr, src } => write!(fmt, "storef {:?}, {:?}", addr, src), |
805 | 0 | Inst::MakeRef { dst, src } => write!(fmt, "makeref {:?}, {:?}", dst, src), |
806 | 0 | Inst::UseRef { dst, src } => write!(fmt, "useref {:?}, {:?}", dst, src), |
807 | 0 | Inst::Safepoint => write!(fmt, "safepoint"), |
808 | 0 | Inst::Spill { dst, src } => write!(fmt, "SPILL {:?}, {:?}", dst, src), |
809 | 0 | Inst::SpillF { dst, src } => write!(fmt, "SPILLF {:?}, {:?}", dst, src), |
810 | 0 | Inst::Reload { dst, src } => write!(fmt, "RELOAD {:?}, {:?}", dst, src), |
811 | 0 | Inst::ReloadF { dst, src } => write!(fmt, "RELOAD {:?}, {:?}", dst, src), |
812 | 0 | Inst::Goto { target } => write!(fmt, "goto {:?}", target), |
813 | | Inst::GotoCTF { |
814 | 0 | cond, |
815 | 0 | target_true, |
816 | 0 | target_false, |
817 | 0 | } => write!( |
818 | 0 | fmt, |
819 | 0 | "if_then_else {:?}, {:?}, {:?}", |
820 | 0 | cond, target_true, target_false |
821 | 0 | ), |
822 | 0 | Inst::PrintS { str } => { |
823 | 0 | let mut res = "prints '".to_string(); |
824 | 0 | for c in str.chars() { |
825 | 0 | res += &(if c == '\n' { |
826 | 0 | "\\n".to_string() |
827 | | } else { |
828 | 0 | c.to_string() |
829 | | }); |
830 | | } |
831 | 0 | write!(fmt, "{}'", res) |
832 | | } |
833 | 0 | Inst::PrintI { reg } => write!(fmt, "printi {:?}", reg), |
834 | 0 | Inst::PrintF { reg } => write!(fmt, "printf {:?}", reg), |
835 | 0 | Inst::Finish { reg } => { |
836 | 0 | if let Some(reg) = reg { |
837 | 0 | write!(fmt, "finish {:?}", reg) |
838 | | } else { |
839 | 0 | write!(fmt, "finish") |
840 | | } |
841 | | } |
842 | | } |
843 | 0 | } |
844 | | } |
845 | | |
846 | | impl Inst { |
847 | | // Returns a vector of BlockIxs, being those that this insn might jump to. |
848 | | // This might contain duplicates (although it would be pretty strange if |
849 | | // it did). This function should not be applied to non-control-flow |
850 | | // instructions. The labels are assumed all to be "resolved". |
851 | 38.2k | pub fn get_targets(&self) -> Vec<BlockIx> { |
852 | 38.2k | match self { |
853 | 25.7k | Inst::Goto { target } => vec![target.get_block_ix()], |
854 | | Inst::GotoCTF { |
855 | | cond: _, |
856 | 10.5k | target_true, |
857 | 10.5k | target_false, |
858 | 10.5k | } => vec![target_true.get_block_ix(), target_false.get_block_ix()], |
859 | 1.88k | Inst::Finish { reg: _ } => vec![], |
860 | 0 | _other => panic!("Inst::getTargets: incorrectly applied to: {:?}", self), |
861 | | } |
862 | 38.2k | } |
863 | | |
864 | | // Returns three sets of regs, (def, mod, use), being those def'd (written), |
865 | | // those mod'd (modified) and those use'd (read) by the instruction, |
866 | | // respectively. |
867 | | // |
868 | | // Be careful here. If an instruction really modifies a register -- as is |
869 | | // typical for x86 -- that register needs to be in the `mod` set, and not in |
870 | | // the `def` and `use` sets. *Any* mistake in describing register uses here |
871 | | // will almost certainly lead to incorrect register allocations. |
872 | | // |
873 | | // Also the following must hold: the union of `def` and `use` must be |
874 | | // disjoint from `mod`. |
875 | 328k | pub fn get_reg_usage(&self, collector: &mut RegUsageCollector) { |
876 | 328k | match self { |
877 | 4.36k | Inst::NopZ {} => {} |
878 | 83.6k | Inst::Imm { dst, imm: _ } => { |
879 | 83.6k | collector.add_def(Writable::from_reg(*dst)); |
880 | 83.6k | } |
881 | 10.7k | Inst::ImmF { dst, imm: _ } => { |
882 | 10.7k | collector.add_def(Writable::from_reg(*dst)); |
883 | 10.7k | } |
884 | 62.1k | Inst::Copy { dst, src } => { |
885 | 62.1k | collector.add_def(Writable::from_reg(*dst)); |
886 | 62.1k | collector.add_use(*src); |
887 | 62.1k | } |
888 | 4.87k | Inst::CopyF { dst, src } => { |
889 | 4.87k | collector.add_def(Writable::from_reg(*dst)); |
890 | 4.87k | collector.add_use(*src); |
891 | 4.87k | } |
892 | | Inst::BinOp { |
893 | | op: _, |
894 | 1.76k | dst, |
895 | 1.76k | src_left, |
896 | 1.76k | src_right, |
897 | 1.76k | } => { |
898 | 1.76k | collector.add_def(Writable::from_reg(*dst)); |
899 | 1.76k | collector.add_use(*src_left); |
900 | 1.76k | src_right.add_reg_reads_to(collector); |
901 | 1.76k | } |
902 | | Inst::BinOpM { |
903 | | op: _, |
904 | 6.96k | dst, |
905 | 6.96k | src_right, |
906 | 6.96k | } => { |
907 | 6.96k | collector.add_mod(Writable::from_reg(*dst)); |
908 | 6.96k | src_right.add_reg_reads_to(collector); |
909 | 6.96k | } |
910 | | Inst::BinOpF { |
911 | | op: _, |
912 | 771 | dst, |
913 | 771 | src_left, |
914 | 771 | src_right, |
915 | 771 | } => { |
916 | 771 | collector.add_def(Writable::from_reg(*dst)); |
917 | 771 | collector.add_use(*src_left); |
918 | 771 | collector.add_use(*src_right); |
919 | 771 | } |
920 | 4.32k | Inst::Store { addr, src } => { |
921 | 4.32k | addr.add_reg_reads_to(collector); |
922 | 4.32k | collector.add_use(*src); |
923 | 4.32k | } |
924 | 6.71k | Inst::StoreF { addr, src } => { |
925 | 6.71k | addr.add_reg_reads_to(collector); |
926 | 6.71k | collector.add_use(*src); |
927 | 6.71k | } |
928 | 14.1k | Inst::MakeRef { dst, src } => { |
929 | 14.1k | collector.add_def(Writable::from_reg(*dst)); |
930 | 14.1k | collector.add_use(*src); |
931 | 14.1k | } |
932 | 6.05k | Inst::UseRef { dst, src } => { |
933 | 6.05k | collector.add_def(Writable::from_reg(*dst)); |
934 | 6.05k | collector.add_use(*src); |
935 | 6.05k | } |
936 | 37.0k | Inst::Safepoint => {} |
937 | 2.29k | Inst::Load { dst, addr } => { |
938 | 2.29k | collector.add_def(Writable::from_reg(*dst)); |
939 | 2.29k | addr.add_reg_reads_to(collector); |
940 | 2.29k | } |
941 | 3.77k | Inst::LoadF { dst, addr } => { |
942 | 3.77k | collector.add_def(Writable::from_reg(*dst)); |
943 | 3.77k | addr.add_reg_reads_to(collector); |
944 | 3.77k | } |
945 | 33.8k | Inst::Goto { .. } => {} |
946 | | Inst::GotoCTF { |
947 | 14.8k | cond, |
948 | 14.8k | target_true: _, |
949 | 14.8k | target_false: _, |
950 | 14.8k | } => { |
951 | 14.8k | collector.add_use(*cond); |
952 | 14.8k | } |
953 | 0 | Inst::PrintS { .. } => {} |
954 | 0 | Inst::PrintI { reg } => { |
955 | 0 | collector.add_use(*reg); |
956 | 0 | } |
957 | 0 | Inst::PrintF { reg } => { |
958 | 0 | collector.add_use(*reg); |
959 | 0 | } |
960 | 2.04k | Inst::Finish { reg } => { |
961 | 2.04k | if let Some(reg) = reg { |
962 | 597 | collector.add_use(*reg); |
963 | 1.44k | } |
964 | | } |
965 | | // Spill and Reload are seen here during the final pass over insts that |
966 | | // computes clobbered regs. |
967 | 11.3k | Inst::Spill { src, .. } | Inst::SpillF { src, .. } => { |
968 | 11.3k | collector.add_use(src.to_reg()); |
969 | 11.3k | } |
970 | 16.3k | Inst::Reload { dst, .. } | Inst::ReloadF { dst, .. } => { |
971 | 16.3k | collector.add_def(Writable::from_reg(dst.to_reg())); |
972 | 16.3k | } |
973 | | } |
974 | 328k | } |
975 | | |
976 | | /// Apply the specified VirtualReg->RealReg mappings to the instruction, |
977 | 94.0k | pub fn map_regs<RUM: RegUsageMapper>(&mut self, mapper: &RUM) { |
978 | 94.0k | match self { |
979 | 0 | Inst::NopZ {} => {} |
980 | 27.1k | Inst::Imm { dst, imm: _ } => { |
981 | 27.1k | dst.apply_defs(mapper); |
982 | 27.1k | } |
983 | 3.43k | Inst::ImmF { dst, imm: _ } => { |
984 | 3.43k | dst.apply_defs(mapper); |
985 | 3.43k | } |
986 | 18.6k | Inst::Copy { dst, src } => { |
987 | 18.6k | dst.apply_defs(mapper); |
988 | 18.6k | src.apply_uses(mapper); |
989 | 18.6k | } |
990 | 1.57k | Inst::CopyF { dst, src } => { |
991 | 1.57k | dst.apply_defs(mapper); |
992 | 1.57k | src.apply_uses(mapper); |
993 | 1.57k | } |
994 | | Inst::BinOp { |
995 | | op: _, |
996 | 563 | dst, |
997 | 563 | src_left, |
998 | 563 | src_right, |
999 | 563 | } => { |
1000 | 563 | dst.apply_defs(mapper); |
1001 | 563 | src_left.apply_uses(mapper); |
1002 | 563 | src_right.apply_uses(mapper); |
1003 | 563 | } |
1004 | | Inst::BinOpM { |
1005 | | op: _, |
1006 | 2.28k | dst, |
1007 | 2.28k | src_right, |
1008 | 2.28k | } => { |
1009 | 2.28k | dst.apply_mods(mapper); |
1010 | 2.28k | src_right.apply_uses(mapper); |
1011 | 2.28k | } |
1012 | | Inst::BinOpF { |
1013 | | op: _, |
1014 | 242 | dst, |
1015 | 242 | src_left, |
1016 | 242 | src_right, |
1017 | 242 | } => { |
1018 | 242 | dst.apply_defs(mapper); |
1019 | 242 | src_left.apply_uses(mapper); |
1020 | 242 | src_right.apply_uses(mapper); |
1021 | 242 | } |
1022 | 4.58k | Inst::MakeRef { dst, src } => { |
1023 | 4.58k | dst.apply_defs(mapper); |
1024 | 4.58k | src.apply_uses(mapper); |
1025 | 4.58k | } |
1026 | 1.98k | Inst::UseRef { dst, src } => { |
1027 | 1.98k | dst.apply_defs(mapper); |
1028 | 1.98k | src.apply_uses(mapper); |
1029 | 1.98k | } |
1030 | 11.8k | Inst::Safepoint => {} |
1031 | 1.32k | Inst::Store { addr, src } => { |
1032 | 1.32k | addr.apply_uses(mapper); |
1033 | 1.32k | src.apply_uses(mapper); |
1034 | 1.32k | } |
1035 | 2.10k | Inst::StoreF { addr, src } => { |
1036 | 2.10k | addr.apply_uses(mapper); |
1037 | 2.10k | src.apply_uses(mapper); |
1038 | 2.10k | } |
1039 | 724 | Inst::Load { dst, addr } => { |
1040 | 724 | dst.apply_defs(mapper); |
1041 | 724 | addr.apply_uses(mapper); |
1042 | 724 | } |
1043 | 1.21k | Inst::LoadF { dst, addr } => { |
1044 | 1.21k | dst.apply_defs(mapper); |
1045 | 1.21k | addr.apply_uses(mapper); |
1046 | 1.21k | } |
1047 | 10.9k | Inst::Goto { .. } => {} |
1048 | | Inst::GotoCTF { |
1049 | 4.71k | cond, |
1050 | 4.71k | target_true: _, |
1051 | 4.71k | target_false: _, |
1052 | 4.71k | } => { |
1053 | 4.71k | cond.apply_uses(mapper); |
1054 | 4.71k | } |
1055 | 0 | Inst::PrintS { .. } => {} |
1056 | 0 | Inst::PrintI { reg } => { |
1057 | 0 | reg.apply_uses(mapper); |
1058 | 0 | } |
1059 | 0 | Inst::PrintF { reg } => { |
1060 | 0 | reg.apply_uses(mapper); |
1061 | 0 | } |
1062 | 663 | Inst::Finish { reg } => { |
1063 | 663 | if let Some(reg) = reg { |
1064 | 188 | reg.apply_uses(mapper); |
1065 | 475 | } |
1066 | | } |
1067 | | Inst::Spill { .. } |
1068 | | | Inst::SpillF { .. } |
1069 | | | Inst::Reload { .. } |
1070 | | | Inst::ReloadF { .. } => { |
1071 | 0 | unreachable!("no need to fill in instructions inserted by regalloc") |
1072 | | } |
1073 | | } |
1074 | 94.0k | } <minira::test_framework::Inst>::map_regs::<regalloc::reg_maps::VrangeRegUsageMapper> Line | Count | Source | 977 | 94.0k | pub fn map_regs<RUM: RegUsageMapper>(&mut self, mapper: &RUM) { | 978 | 94.0k | match self { | 979 | 0 | Inst::NopZ {} => {} | 980 | 27.1k | Inst::Imm { dst, imm: _ } => { | 981 | 27.1k | dst.apply_defs(mapper); | 982 | 27.1k | } | 983 | 3.43k | Inst::ImmF { dst, imm: _ } => { | 984 | 3.43k | dst.apply_defs(mapper); | 985 | 3.43k | } | 986 | 18.6k | Inst::Copy { dst, src } => { | 987 | 18.6k | dst.apply_defs(mapper); | 988 | 18.6k | src.apply_uses(mapper); | 989 | 18.6k | } | 990 | 1.57k | Inst::CopyF { dst, src } => { | 991 | 1.57k | dst.apply_defs(mapper); | 992 | 1.57k | src.apply_uses(mapper); | 993 | 1.57k | } | 994 | | Inst::BinOp { | 995 | | op: _, | 996 | 563 | dst, | 997 | 563 | src_left, | 998 | 563 | src_right, | 999 | 563 | } => { | 1000 | 563 | dst.apply_defs(mapper); | 1001 | 563 | src_left.apply_uses(mapper); | 1002 | 563 | src_right.apply_uses(mapper); | 1003 | 563 | } | 1004 | | Inst::BinOpM { | 1005 | | op: _, | 1006 | 2.28k | dst, | 1007 | 2.28k | src_right, | 1008 | 2.28k | } => { | 1009 | 2.28k | dst.apply_mods(mapper); | 1010 | 2.28k | src_right.apply_uses(mapper); | 1011 | 2.28k | } | 1012 | | Inst::BinOpF { | 1013 | | op: _, | 1014 | 242 | dst, | 1015 | 242 | src_left, | 1016 | 242 | src_right, | 1017 | 242 | } => { | 1018 | 242 | dst.apply_defs(mapper); | 1019 | 242 | src_left.apply_uses(mapper); | 1020 | 242 | src_right.apply_uses(mapper); | 1021 | 242 | } | 1022 | 4.58k | Inst::MakeRef { dst, src } => { | 1023 | 4.58k | dst.apply_defs(mapper); | 1024 | 4.58k | src.apply_uses(mapper); | 1025 | 4.58k | } | 1026 | 1.98k | Inst::UseRef { dst, src } => { | 1027 | 1.98k | dst.apply_defs(mapper); | 1028 | 1.98k | src.apply_uses(mapper); | 1029 | 1.98k | } | 1030 | 11.8k | Inst::Safepoint => {} | 1031 | 1.32k | Inst::Store { addr, src } => { | 1032 | 1.32k | addr.apply_uses(mapper); | 1033 | 1.32k | src.apply_uses(mapper); | 1034 | 1.32k | } | 1035 | 2.10k | Inst::StoreF { addr, src } => { | 1036 | 2.10k | addr.apply_uses(mapper); | 1037 | 2.10k | src.apply_uses(mapper); | 1038 | 2.10k | } | 1039 | 724 | Inst::Load { dst, addr } => { | 1040 | 724 | dst.apply_defs(mapper); | 1041 | 724 | addr.apply_uses(mapper); | 1042 | 724 | } | 1043 | 1.21k | Inst::LoadF { dst, addr } => { | 1044 | 1.21k | dst.apply_defs(mapper); | 1045 | 1.21k | addr.apply_uses(mapper); | 1046 | 1.21k | } | 1047 | 10.9k | Inst::Goto { .. } => {} | 1048 | | Inst::GotoCTF { | 1049 | 4.71k | cond, | 1050 | 4.71k | target_true: _, | 1051 | 4.71k | target_false: _, | 1052 | 4.71k | } => { | 1053 | 4.71k | cond.apply_uses(mapper); | 1054 | 4.71k | } | 1055 | 0 | Inst::PrintS { .. } => {} | 1056 | 0 | Inst::PrintI { reg } => { | 1057 | 0 | reg.apply_uses(mapper); | 1058 | 0 | } | 1059 | 0 | Inst::PrintF { reg } => { | 1060 | 0 | reg.apply_uses(mapper); | 1061 | 0 | } | 1062 | 663 | Inst::Finish { reg } => { | 1063 | 663 | if let Some(reg) = reg { | 1064 | 188 | reg.apply_uses(mapper); | 1065 | 475 | } | 1066 | | } | 1067 | | Inst::Spill { .. } | 1068 | | | Inst::SpillF { .. } | 1069 | | | Inst::Reload { .. } | 1070 | | | Inst::ReloadF { .. } => { | 1071 | 0 | unreachable!("no need to fill in instructions inserted by regalloc") | 1072 | | } | 1073 | | } | 1074 | 94.0k | } |
Unexecuted instantiation: <minira::test_framework::Inst>::map_regs::<regalloc::reg_maps::MentionRegUsageMapper> |
1075 | | |
1076 | 108k | pub fn is_control_flow(&self) -> bool { |
1077 | 108k | match self { |
1078 | 21.8k | Inst::Goto { .. } | Inst::GotoCTF { .. } | Inst::Finish { reg: _ } => true, |
1079 | 86.4k | _ => false, |
1080 | | } |
1081 | 108k | } |
1082 | | |
1083 | | /// Is this instruction a user instruction, or is it internal to regalloc? |
1084 | | pub fn is_user(&self) -> bool { |
1085 | | match self { |
1086 | | Inst::Spill { .. } |
1087 | | | Inst::SpillF { .. } |
1088 | | | Inst::Reload { .. } |
1089 | | | Inst::ReloadF { .. } => false, |
1090 | | _ => true, |
1091 | | } |
1092 | | } |
1093 | | |
1094 | | pub fn type_checks(&self, cx: &mut ValidatorContext) -> bool { |
1095 | | use RegClass::*; |
1096 | | // Always check uses before defs. |
1097 | | match self { |
1098 | | Inst::NopZ {} => true, |
1099 | | Inst::Imm { dst, imm: _ } => cx.check_reg_rc(dst, RegRef::Def, I32), |
1100 | | Inst::ImmF { dst, imm: _ } => cx.check_reg_rc(dst, RegRef::Def, F32), |
1101 | | // MakeRef and UseRef are like copy instructions; the typecheck only |
1102 | | // reasons about register classes, not reffyness, so the |
1103 | | // distinctions between the three are irrelevant here. |
1104 | | Inst::Copy { dst, src } | Inst::MakeRef { dst, src } | Inst::UseRef { dst, src } => { |
1105 | | cx.check_reg_rc(src, RegRef::Use, I32) && cx.check_reg_rc(dst, RegRef::Def, I32) |
1106 | | } |
1107 | | Inst::CopyF { dst, src } => { |
1108 | | cx.check_reg_rc(src, RegRef::Use, F32) && cx.check_reg_rc(dst, RegRef::Def, F32) |
1109 | | } |
1110 | | Inst::Load { dst, addr } => { |
1111 | | addr.type_checks(cx) && cx.check_reg_rc(dst, RegRef::Def, I32) |
1112 | | } |
1113 | | Inst::LoadF { dst, addr } => { |
1114 | | addr.type_checks(cx) && cx.check_reg_rc(dst, RegRef::Def, F32) |
1115 | | } |
1116 | | Inst::Store { addr, src } => { |
1117 | | cx.check_reg_rc(src, RegRef::Use, I32) && addr.type_checks(cx) |
1118 | | } |
1119 | | Inst::StoreF { addr, src } => { |
1120 | | cx.check_reg_rc(src, RegRef::Use, F32) && addr.type_checks(cx) |
1121 | | } |
1122 | | Inst::Goto { target } => target.type_checks(cx), |
1123 | | Inst::GotoCTF { |
1124 | | cond, |
1125 | | target_true, |
1126 | | target_false, |
1127 | | } => { |
1128 | | cx.check_reg_rc(cond, RegRef::Use, I32) |
1129 | | && target_true.type_checks(cx) |
1130 | | && target_false.type_checks(cx) |
1131 | | } |
1132 | | Inst::PrintS { .. } => true, |
1133 | | Inst::PrintI { reg } => cx.check_reg_rc(reg, RegRef::Use, I32), |
1134 | | Inst::PrintF { reg } => cx.check_reg_rc(reg, RegRef::Use, F32), |
1135 | | Inst::Finish { reg } => reg.map_or(true, |reg| cx.check_reg(reg, RegRef::Use)), |
1136 | | Inst::BinOp { |
1137 | | op: _, |
1138 | | dst, |
1139 | | src_left, |
1140 | | src_right, |
1141 | | } => { |
1142 | | cx.check_reg_rc(src_left, RegRef::Use, I32) |
1143 | | && src_right.type_checks(cx) |
1144 | | && cx.check_reg_rc(dst, RegRef::Def, I32) |
1145 | | } |
1146 | | Inst::BinOpM { |
1147 | | op: _, |
1148 | | dst, |
1149 | | src_right, |
1150 | | } => { |
1151 | | cx.check_reg_rc(dst, RegRef::Use, I32) |
1152 | | && src_right.type_checks(cx) |
1153 | | && cx.check_reg_rc(dst, RegRef::Def, I32) |
1154 | | } |
1155 | | Inst::BinOpF { |
1156 | | op: _, |
1157 | | dst, |
1158 | | src_left, |
1159 | | src_right, |
1160 | | } => { |
1161 | | cx.check_reg_rc(src_left, RegRef::Use, F32) |
1162 | | && cx.check_reg_rc(src_right, RegRef::Use, F32) |
1163 | | && cx.check_reg_rc(dst, RegRef::Def, F32) |
1164 | | } |
1165 | | Inst::Safepoint => true, |
1166 | | |
1167 | | // These are not user instructions. |
1168 | | Inst::Spill { .. } |
1169 | | | Inst::SpillF { .. } |
1170 | | | Inst::Reload { .. } |
1171 | | | Inst::ReloadF { .. } => { |
1172 | | panic!("unexpected spill/spillf/reload/reloadf in type_checks") |
1173 | | } |
1174 | | } |
1175 | | } |
1176 | | } |
1177 | | |
1178 | | //============================================================================= |
1179 | | // The interpreter |
1180 | | |
1181 | | #[derive(Copy, Clone)] |
1182 | | pub enum Value { |
1183 | | U32(u32), |
1184 | | F32(f32), |
1185 | | Ref(u32), |
1186 | | } |
1187 | | |
1188 | | impl PartialEq for Value { |
1189 | 0 | fn eq(&self, other: &Self) -> bool { |
1190 | 0 | match self { |
1191 | 0 | Value::U32(x) => match other { |
1192 | 0 | Value::U32(y) => x == y, |
1193 | 0 | _ => false, |
1194 | | }, |
1195 | | |
1196 | 0 | Value::F32(x) => match other { |
1197 | 0 | Value::F32(y) => (x.is_nan() && y.is_nan()) || x == y, |
1198 | 0 | _ => false, |
1199 | | }, |
1200 | | |
1201 | 0 | Value::Ref(x) => match other { |
1202 | 0 | Value::Ref(y) => x == y, |
1203 | 0 | _ => false, |
1204 | | }, |
1205 | | } |
1206 | 0 | } |
1207 | | } |
1208 | | |
1209 | | impl Value { |
1210 | 0 | fn to_u32(self) -> u32 { |
1211 | 0 | match self { |
1212 | 0 | Value::U32(n) => n, |
1213 | 0 | Value::F32(_) => panic!("Value::toU32: this is a F32"), |
1214 | 0 | Value::Ref(_) => panic!("Value::toU32: this is a ref"), |
1215 | | } |
1216 | 0 | } |
1217 | 0 | fn to_f32(self) -> f32 { |
1218 | 0 | match self { |
1219 | 0 | Value::U32(_) => panic!("Value::toF32: this is a U32"), |
1220 | 0 | Value::Ref(_) => panic!("Value::toF32: this is a ref"), |
1221 | 0 | Value::F32(n) => n, |
1222 | 0 | } |
1223 | 0 | } |
1224 | 0 | fn to_ref(self) -> u32 { |
1225 | 0 | match self { |
1226 | 0 | Value::Ref(n) => n, |
1227 | 0 | _ => panic!("Value::to_ref: this is not a ref"), |
1228 | | } |
1229 | 0 | } |
1230 | 0 | fn cast_to_u32(self) -> u32 { |
1231 | 0 | match self { |
1232 | 0 | Value::U32(n) => n, |
1233 | 0 | Value::F32(f) => f as u32, |
1234 | 0 | Value::Ref(n) => n, |
1235 | | } |
1236 | 0 | } |
1237 | 0 | fn cast_to_f32(self) -> f32 { |
1238 | 0 | match self { |
1239 | 0 | Value::U32(n) => n as f32, |
1240 | 0 | Value::F32(f) => f, |
1241 | 0 | Value::Ref(n) => n as f32, |
1242 | | } |
1243 | 0 | } |
1244 | | } |
1245 | | |
1246 | | impl fmt::Debug for Value { |
1247 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
1248 | 0 | match self { |
1249 | 0 | Value::U32(n) => write!(fmt, "{}", n), |
1250 | 0 | Value::F32(n) => write!(fmt, "{}", n), |
1251 | 0 | Value::Ref(n) => write!(fmt, "@{}", n), |
1252 | | } |
1253 | 0 | } |
1254 | | } |
1255 | | |
1256 | | #[derive(PartialEq)] |
1257 | | pub enum RunStage { |
1258 | | BeforeRegalloc, |
1259 | | AfterRegalloc, |
1260 | | } |
1261 | | |
1262 | | struct IState<'a> { |
1263 | | func: &'a Func, |
1264 | | nia: InstIx, // Program counter ("next instruction address") |
1265 | | vregs: Vec<Option<Value>>, // unlimited |
1266 | | rregs: Vec<Option<Value>>, // [0 .. maxRealRegs) |
1267 | | mem: Vec<Option<Value>>, // [0 .. maxMem) |
1268 | | slots: Vec<Option<Value>>, // [0..] Spill slots, no upper limit |
1269 | | num_insts: usize, // Stats: number of insns executed |
1270 | | num_spills: usize, // Stats: .. of which are spills |
1271 | | num_reloads: usize, // Stats: .. of which are reloads |
1272 | | run_stage: RunStage, |
1273 | | ret_value: Option<Value>, |
1274 | | stdout: String, // Everything that's printed out. |
1275 | | } |
1276 | | |
1277 | | type IResult<T> = Result<T, String>; |
1278 | | |
1279 | | impl<'a> IState<'a> { |
1280 | | fn new(func: &'a Func, max_real_regs: usize, max_mem: usize, run_stage: RunStage) -> Self { |
1281 | | let mut state = IState { |
1282 | | func, |
1283 | | nia: func.blocks[func |
1284 | | .entry |
1285 | | .as_ref() |
1286 | | .expect("missing entry block") |
1287 | | .get_block_ix()] |
1288 | | .start, |
1289 | | vregs: Vec::new(), |
1290 | | rregs: Vec::new(), |
1291 | | mem: Vec::new(), |
1292 | | slots: Vec::new(), |
1293 | | num_insts: 0, |
1294 | | num_spills: 0, |
1295 | | num_reloads: 0, |
1296 | | run_stage, |
1297 | | ret_value: None, |
1298 | | stdout: String::new(), |
1299 | | }; |
1300 | | state.rregs.resize(max_real_regs, None); |
1301 | | state.mem.resize(max_mem, None); |
1302 | | state |
1303 | | } |
1304 | | |
1305 | 0 | fn get_real_reg(&self, rreg: RealReg) -> IResult<Value> { |
1306 | | // No automatic resizing. If the rreg doesn't exist, just fail. |
1307 | 0 | match self.rregs.get(rreg.get_index()) { |
1308 | 0 | None => panic!("IState::get_real_reg: invalid rreg {:?}", rreg), |
1309 | 0 | Some(None) => { |
1310 | 0 | return Err(format!( |
1311 | 0 | "IState::get_real_reg: read of uninit rreg {:?} at nia {:?}", |
1312 | 0 | rreg, self.nia |
1313 | 0 | ))? |
1314 | | } |
1315 | 0 | Some(Some(val)) => Ok(*val), |
1316 | | } |
1317 | 0 | } |
1318 | | |
1319 | 0 | fn set_real_reg(&mut self, rreg: RealReg, val: Value) { |
1320 | | // No automatic resizing. If the rreg doesn't exist, just fail. |
1321 | 0 | match self.rregs.get_mut(rreg.get_index()) { |
1322 | 0 | None => panic!("IState::setRealReg: invalid rreg {:?}", rreg), |
1323 | 0 | Some(val_p) => *val_p = Some(val), |
1324 | 0 | } |
1325 | 0 | } |
1326 | | |
1327 | | fn get_virtual_reg(&self, vreg: VirtualReg) -> IResult<Value> { |
1328 | | debug_assert!( |
1329 | | self.run_stage != RunStage::AfterRegalloc, |
1330 | | "trying to get a vreg after regalloc" |
1331 | | ); |
1332 | | // The vector might be too small. But in that case we'd be |
1333 | | // reading the vreg uninitialised anyway, so just complain. |
1334 | | match self.vregs.get(vreg.get_index()) { |
1335 | | None | Some(None) => { |
1336 | | // entry either present or absent, but has never been written in both |
1337 | | // cases. |
1338 | | return Err(format!( |
1339 | | "IState::get_virtual_reg: read of uninit vreg {:?}", |
1340 | | vreg |
1341 | | ))?; |
1342 | | } |
1343 | | Some(Some(val)) => Ok(*val), |
1344 | | } |
1345 | | } |
1346 | | |
1347 | | fn set_virtual_reg(&mut self, vreg: VirtualReg, val: Value) { |
1348 | | debug_assert!( |
1349 | | self.run_stage != RunStage::AfterRegalloc, |
1350 | | "trying to set a vreg after regalloc" |
1351 | | ); |
1352 | | // Auto-resize the vector if necessary |
1353 | | let ix = vreg.get_index(); |
1354 | | if ix >= self.vregs.len() { |
1355 | | self.vregs.resize(ix + 1, None); |
1356 | | } |
1357 | | debug_assert!(ix < self.vregs.len()); |
1358 | | self.vregs[ix] = Some(val); |
1359 | | } |
1360 | | |
1361 | 0 | fn get_spill_slot(&self, slot: SpillSlot) -> Value { |
1362 | | // The vector might be too small. But in that case we'd be |
1363 | | // reading the slot uninitialised anyway, so just complain. |
1364 | 0 | match self.slots.get(slot.get_usize()) { |
1365 | 0 | None | // indexing error |
1366 | 0 | Some(None) => // entry present, but has never been written |
1367 | 0 | panic!("IState::getSpillSlot: read of uninit slot # {}", |
1368 | 0 | slot.get()), |
1369 | 0 | Some(Some(val)) |
1370 | 0 | => *val |
1371 | 0 | } |
1372 | 0 | } |
1373 | | |
1374 | | fn set_spill_slot_u32(&mut self, slot: SpillSlot, val: Value) { |
1375 | | // Auto-resize the vector if necessary |
1376 | | let ix = slot.get_usize(); |
1377 | | if ix >= self.slots.len() { |
1378 | | self.slots.resize(ix + 1, None); |
1379 | | } |
1380 | | debug_assert!(ix < self.slots.len()); |
1381 | | debug_assert!(matches!(val, Value::Ref(_) | Value::U32(_))); |
1382 | | self.slots[ix] = Some(val); |
1383 | | } |
1384 | | |
1385 | 0 | fn set_spill_slot_f32(&mut self, slot: SpillSlot, val: f32) { |
1386 | | // Auto-resize the vector if necessary |
1387 | 0 | let ix = slot.get_usize(); |
1388 | 0 | if ix >= self.slots.len() { |
1389 | 0 | self.slots.resize(ix + 1, None); |
1390 | 0 | } |
1391 | | debug_assert!(ix < self.slots.len()); |
1392 | 0 | self.slots[ix] = Some(Value::F32(val)); |
1393 | 0 | } |
1394 | | |
1395 | 0 | fn get_reg(&self, reg: Reg) -> IResult<Value> { |
1396 | 0 | if reg.is_virtual() { |
1397 | 0 | self.get_virtual_reg(reg.to_virtual_reg()) |
1398 | | } else { |
1399 | 0 | self.get_real_reg(reg.to_real_reg()) |
1400 | | } |
1401 | 0 | } |
1402 | | |
1403 | | fn set_reg_u32(&mut self, reg: Reg, val: u32) { |
1404 | | self.set_reg(reg, Value::U32(val)) |
1405 | | } |
1406 | | |
1407 | | fn set_reg_f32(&mut self, reg: Reg, val: f32) { |
1408 | | self.set_reg(reg, Value::F32(val)) |
1409 | | } |
1410 | | |
1411 | | fn set_reg_ref(&mut self, reg: Reg, val: u32) { |
1412 | | self.set_reg(reg, Value::Ref(val)) |
1413 | | } |
1414 | | |
1415 | 0 | fn set_reg(&mut self, reg: Reg, val: Value) { |
1416 | 0 | if reg.is_virtual() { |
1417 | 0 | self.set_virtual_reg(reg.to_virtual_reg(), val); |
1418 | 0 | } else { |
1419 | 0 | self.set_real_reg(reg.to_real_reg(), val); |
1420 | 0 | } |
1421 | 0 | } |
1422 | | |
1423 | 0 | fn get_mem(&self, addr: u32) -> IResult<Value> { |
1424 | | // No auto resizing of the memory. |
1425 | 0 | Ok(match self.mem.get(addr as usize) { |
1426 | 0 | None => return Err(format!("IState::getMem: invalid addr {}", addr))?, |
1427 | 0 | Some(None) => { |
1428 | 0 | return Err(format!( |
1429 | 0 | "IState::getMem: read of uninit mem at addr {}", |
1430 | 0 | addr |
1431 | 0 | ))? |
1432 | | } |
1433 | 0 | Some(Some(val)) => *val, |
1434 | | }) |
1435 | 0 | } |
1436 | | |
1437 | 0 | fn set_mem_u32(&mut self, addr: u32, val: u32) -> IResult<()> { |
1438 | 0 | // No auto resizing of the memory |
1439 | 0 | match self.mem.get_mut(addr as usize) { |
1440 | 0 | None => return Err(format!("IState::set_mem_u32: invalid addr {}", addr))?, |
1441 | 0 | Some(val_p) => *val_p = Some(Value::U32(val)), |
1442 | 0 | } |
1443 | 0 | Ok(()) |
1444 | 0 | } |
1445 | | |
1446 | 0 | fn set_mem_f32(&mut self, addr: u32, val: f32) -> IResult<()> { |
1447 | 0 | // No auto resizing of the memory |
1448 | 0 | match self.mem.get_mut(addr as usize) { |
1449 | 0 | None => return Err(format!("IState::set_mem_f32: invalid addr {}", addr))?, |
1450 | 0 | Some(val_p) => *val_p = Some(Value::F32(val)), |
1451 | 0 | } |
1452 | 0 | Ok(()) |
1453 | 0 | } |
1454 | | |
1455 | | #[allow(non_snake_case)] |
1456 | 0 | fn get_RI(&self, ri: &RI) -> IResult<u32> { |
1457 | 0 | Ok(match ri { |
1458 | 0 | RI::Reg { reg } => self.get_reg(*reg)?.to_u32(), |
1459 | 0 | RI::Imm { imm } => *imm, |
1460 | | }) |
1461 | 0 | } |
1462 | | |
1463 | | #[allow(non_snake_case)] |
1464 | 0 | fn get_AM(&self, am: &AM) -> IResult<u32> { |
1465 | | // Base + offset can overflow and wraparound, because why not? |
1466 | 0 | Ok(match am { |
1467 | 0 | AM::RI { base, offset } => self.get_reg(*base)?.to_u32().wrapping_add(*offset), |
1468 | 0 | AM::RR { base, offset } => self |
1469 | 0 | .get_reg(*base)? |
1470 | | .to_u32() |
1471 | 0 | .wrapping_add(self.get_reg(*offset)?.to_u32()), |
1472 | | }) |
1473 | 0 | } |
1474 | | |
1475 | | // Move the interpreter one step forward |
1476 | 0 | fn step(&mut self) -> IResult<bool> { |
1477 | 0 | let mut done = false; |
1478 | 0 |
|
1479 | 0 | let iix = self.nia; |
1480 | 0 | self.nia = iix.plus(1); |
1481 | 0 | self.num_insts += 1; |
1482 | 0 |
|
1483 | 0 | let insn = &self.func.insns[iix]; |
1484 | 0 | match insn { |
1485 | 0 | Inst::NopZ {} => { |
1486 | 0 | self.num_insts -= 1; |
1487 | 0 | } |
1488 | 0 | Inst::Imm { dst, imm } => self.set_reg_u32(*dst, *imm), |
1489 | 0 | Inst::ImmF { dst, imm } => self.set_reg_f32(*dst, *imm), |
1490 | 0 | Inst::Copy { dst, src } => self.set_reg(*dst, self.get_reg(*src)?), |
1491 | 0 | Inst::CopyF { dst, src } => self.set_reg_f32(*dst, self.get_reg(*src)?.to_f32()), |
1492 | | Inst::BinOp { |
1493 | 0 | op, |
1494 | 0 | dst, |
1495 | 0 | src_left, |
1496 | 0 | src_right, |
1497 | | } => { |
1498 | 0 | let src_left_v = self.get_reg(*src_left)?.to_u32(); |
1499 | 0 | let src_right_v = self.get_RI(src_right)?; |
1500 | 0 | let dst_v = op.calc(src_left_v, src_right_v)?; |
1501 | 0 | self.set_reg_u32(*dst, dst_v); |
1502 | | } |
1503 | 0 | Inst::BinOpM { op, dst, src_right } => { |
1504 | 0 | let mut dst_v = self.get_reg(*dst)?.to_u32(); |
1505 | 0 | let src_right_v = self.get_RI(src_right)?; |
1506 | 0 | dst_v = op.calc(dst_v, src_right_v)?; |
1507 | 0 | self.set_reg_u32(*dst, dst_v); |
1508 | | } |
1509 | | Inst::BinOpF { |
1510 | 0 | op, |
1511 | 0 | dst, |
1512 | 0 | src_left, |
1513 | 0 | src_right, |
1514 | | } => { |
1515 | 0 | let src_left_v = self.get_reg(*src_left)?.to_f32(); |
1516 | 0 | let src_right_v = self.get_reg(*src_right)?.to_f32(); |
1517 | 0 | let dst_v = op.calc(src_left_v, src_right_v)?; |
1518 | 0 | self.set_reg_f32(*dst, dst_v); |
1519 | | } |
1520 | 0 | Inst::MakeRef { dst, src } => { |
1521 | 0 | let src_v = self.get_reg(*src)?.to_u32(); |
1522 | 0 | self.set_reg_ref(*dst, src_v); |
1523 | | } |
1524 | 0 | Inst::UseRef { dst, src } => { |
1525 | 0 | let src_v = self.get_reg(*src)?.to_ref(); |
1526 | 0 | self.set_reg_u32(*dst, src_v); |
1527 | | } |
1528 | 0 | Inst::Safepoint => {} |
1529 | 0 | Inst::Load { dst, addr } => { |
1530 | 0 | let addr_v = self.get_AM(addr)?; |
1531 | 0 | let dst_v = self.get_mem(addr_v)?.cast_to_u32(); |
1532 | 0 | self.set_reg_u32(*dst, dst_v); |
1533 | | } |
1534 | 0 | Inst::LoadF { dst, addr } => { |
1535 | 0 | let addr_v = self.get_AM(addr)?; |
1536 | 0 | let dst_v = self.get_mem(addr_v)?.cast_to_f32(); |
1537 | 0 | self.set_reg_f32(*dst, dst_v); |
1538 | | } |
1539 | 0 | Inst::Store { addr, src } => { |
1540 | 0 | let addr_v = self.get_AM(addr)?; |
1541 | 0 | let src_v = self.get_reg(*src)?.to_u32(); |
1542 | 0 | self.set_mem_u32(addr_v, src_v)?; |
1543 | | } |
1544 | 0 | Inst::StoreF { addr, src } => { |
1545 | 0 | let addr_v = self.get_AM(addr)?; |
1546 | 0 | let src_v = self.get_reg(*src)?.to_f32(); |
1547 | 0 | self.set_mem_f32(addr_v, src_v)?; |
1548 | | } |
1549 | 0 | Inst::Spill { dst, src } => { |
1550 | 0 | let src_v = self.get_real_reg(*src)?; |
1551 | 0 | self.set_spill_slot_u32(*dst, src_v); |
1552 | 0 | self.num_spills += 1; |
1553 | | } |
1554 | 0 | Inst::SpillF { dst, src } => { |
1555 | 0 | let src_v = self.get_real_reg(*src)?.to_f32(); |
1556 | 0 | self.set_spill_slot_f32(*dst, src_v); |
1557 | 0 | self.num_spills += 1; |
1558 | | } |
1559 | 0 | Inst::Reload { dst, src } => { |
1560 | 0 | let src_v = self.get_spill_slot(*src); |
1561 | 0 | match src_v { |
1562 | 0 | Value::Ref(n) => self.set_reg_ref(dst.to_reg(), n), |
1563 | 0 | Value::U32(n) => self.set_reg_u32(dst.to_reg(), n), |
1564 | 0 | _ => panic!("must be a ref or u32"), |
1565 | | } |
1566 | 0 | self.num_reloads += 1; |
1567 | | } |
1568 | 0 | Inst::ReloadF { dst, src } => { |
1569 | 0 | let src_v = self.get_spill_slot(*src).to_f32(); |
1570 | 0 | self.set_reg_f32(dst.to_reg(), src_v); |
1571 | 0 | self.num_reloads += 1; |
1572 | 0 | } |
1573 | 0 | Inst::Goto { target } => self.nia = self.func.blocks[target.get_block_ix()].start, |
1574 | | Inst::GotoCTF { |
1575 | 0 | cond, |
1576 | 0 | target_true, |
1577 | 0 | target_false, |
1578 | 0 | } => { |
1579 | 0 | let target = if self.get_reg(*cond)?.to_u32() != 0 { |
1580 | 0 | target_true |
1581 | | } else { |
1582 | 0 | target_false |
1583 | | }; |
1584 | 0 | self.nia = self.func.blocks[target.get_block_ix()].start; |
1585 | | } |
1586 | 0 | Inst::PrintS { str } => { |
1587 | 0 | self.stdout += str; |
1588 | 0 | print!("{}", str); |
1589 | 0 | } |
1590 | 0 | Inst::PrintI { reg } => { |
1591 | 0 | let str = format!("{:?}", self.get_reg(*reg)?.to_u32()); |
1592 | 0 | print!("{}", str); |
1593 | 0 | self.stdout += &str; |
1594 | | } |
1595 | 0 | Inst::PrintF { reg } => { |
1596 | 0 | let str = format!("{:?}", self.get_reg(*reg)?.to_f32()); |
1597 | 0 | print!("{}", str); |
1598 | 0 | self.stdout += &str; |
1599 | | } |
1600 | 0 | Inst::Finish { reg } => { |
1601 | 0 | self.ret_value = if let Some(reg) = *reg { |
1602 | 0 | Some(self.get_reg(reg)?) |
1603 | | } else { |
1604 | 0 | None |
1605 | | }; |
1606 | 0 | done = true; |
1607 | | } |
1608 | | } |
1609 | 0 | Ok(done) |
1610 | 0 | } |
1611 | | } |
1612 | | |
1613 | | /// Number of dynamic steps allowed in a test program. Useful to detect infinite |
1614 | | /// loops during testing. |
1615 | | const MAX_NUM_STEPS: u32 = 1000000; |
1616 | | |
1617 | | #[derive(Debug)] |
1618 | | pub struct RunResult { |
1619 | | /// Return value. |
1620 | | pub ret_value: Option<Value>, |
1621 | | |
1622 | | /// Number of dynamically executed steps. |
1623 | | pub num_steps: usize, |
1624 | | |
1625 | | /// Number of dynamically executed reload instructions. |
1626 | | pub num_reloads: usize, |
1627 | | |
1628 | | /// Everything that's been printed out. |
1629 | | pub stdout: String, |
1630 | | } |
1631 | | |
1632 | 0 | pub fn run_func( |
1633 | 0 | f: &Func, |
1634 | 0 | who: &str, |
1635 | 0 | reg_universe: &RealRegUniverse, |
1636 | 0 | run_stage: RunStage, |
1637 | 0 | ) -> Result<RunResult, String> { |
1638 | 0 | println!(""); |
1639 | 0 | println!( |
1640 | 0 | "Running stage '{}': Func: name='{}' entry='{:?}'", |
1641 | 0 | who, f.name, f.entry |
1642 | 0 | ); |
1643 | 0 |
|
1644 | 0 | if run_stage == RunStage::BeforeRegalloc { |
1645 | 0 | if let Err(err) = validate(f, reg_universe) { |
1646 | 0 | return Err(format!("validation error: {}", err)); |
1647 | 0 | } |
1648 | 0 | } |
1649 | | |
1650 | 0 | let mut istate = IState::new( |
1651 | 0 | f, |
1652 | 0 | reg_universe.regs.len(), |
1653 | 0 | /* max_mem */ 1000, |
1654 | 0 | run_stage, |
1655 | 0 | ); |
1656 | 0 | let mut done = false; |
1657 | 0 | let mut allowed_steps = MAX_NUM_STEPS; |
1658 | 0 | while allowed_steps > 0 && !done { |
1659 | 0 | done = istate.step()?; |
1660 | 0 | allowed_steps -= 1; |
1661 | | } |
1662 | | |
1663 | 0 | if allowed_steps == 0 { |
1664 | 0 | return Err("too many dynamic steps. Maybe running an infinite loop?".into()); |
1665 | 0 | } |
1666 | 0 |
|
1667 | 0 | println!( |
1668 | 0 | "Running stage '{}': done. {} insns, {} spills, {} reloads", |
1669 | 0 | who, istate.num_insts, istate.num_spills, istate.num_reloads |
1670 | 0 | ); |
1671 | 0 |
|
1672 | 0 | Ok(RunResult { |
1673 | 0 | ret_value: istate.ret_value, |
1674 | 0 | num_steps: istate.num_insts, |
1675 | 0 | num_reloads: istate.num_reloads, |
1676 | 0 | stdout: istate.stdout, |
1677 | 0 | }) |
1678 | 0 | } |
1679 | | |
1680 | | //============================================================================= |
1681 | | // Definition of Block and Func, and printing thereof. |
1682 | | |
1683 | 21.8k | #[derive(Debug, Clone)] |
1684 | | pub struct Block { |
1685 | | pub name: String, |
1686 | | pub start: InstIx, |
1687 | | pub len: u32, |
1688 | | pub estimated_execution_frequency: u16, |
1689 | | } |
1690 | | |
1691 | | impl Block { |
1692 | | pub fn new(name: String, start: InstIx, len: u32) -> Self { |
1693 | | Self { |
1694 | | name, |
1695 | | start, |
1696 | | len, |
1697 | | estimated_execution_frequency: 1, |
1698 | | } |
1699 | | } |
1700 | | } |
1701 | | |
1702 | 4.26k | #[derive(Debug, Clone)] |
1703 | | pub struct Func { |
1704 | | pub name: String, |
1705 | | pub entry: Option<Label>, |
1706 | | pub num_virtual_regs: u32, |
1707 | | pub reftype_reg_start: Option<u32>, // all vregs >= this index are reftyped. |
1708 | | pub insns: TypedIxVec<InstIx, Inst>, // indexed by InstIx |
1709 | | |
1710 | | // Note that `blocks` must be in order of increasing `Block::start` |
1711 | | // fields. Code that wants to traverse the blocks in some other order |
1712 | | // must represent the ordering some other way; rearranging Func::blocks is |
1713 | | // not allowed. |
1714 | | pub blocks: TypedIxVec<BlockIx, Block>, // indexed by BlockIx |
1715 | | } |
1716 | | |
1717 | | // Find a block Ix for a block name |
1718 | 0 | fn lookup(blocks: &TypedIxVec<BlockIx, Block>, name: String) -> BlockIx { |
1719 | 0 | let mut bix = 0; |
1720 | 0 | for b in blocks.iter() { |
1721 | 0 | if b.name == name { |
1722 | 0 | return BlockIx::new(bix); |
1723 | 0 | } |
1724 | 0 | bix += 1; |
1725 | | } |
1726 | 0 | panic!("Func::lookup: can't resolve label name '{}'", name); |
1727 | 0 | } |
1728 | | |
1729 | | impl Func { |
1730 | | pub fn new<'a>(name: &'a str) -> Self { |
1731 | | Func { |
1732 | | name: name.to_string(), |
1733 | | entry: None, |
1734 | | num_virtual_regs: 0, |
1735 | | reftype_reg_start: None, |
1736 | | insns: TypedIxVec::<InstIx, Inst>::new(), |
1737 | | blocks: TypedIxVec::<BlockIx, Block>::new(), |
1738 | | } |
1739 | | } |
1740 | | |
1741 | | pub fn set_entry(&mut self, entry: &str) { |
1742 | | self.entry = Some(Label::Unresolved { |
1743 | | name: entry.to_string(), |
1744 | | }); |
1745 | | } |
1746 | | |
1747 | 0 | pub fn print(&self, who: &str, mb_block_anns: &Option<TypedIxVec<BlockIx, Vec<String>>>) { |
1748 | 0 | println!(""); |
1749 | 0 | println!( |
1750 | 0 | "Func {}: name='{}' entry='{:?}' {{", |
1751 | 0 | who, self.name, self.entry |
1752 | 0 | ); |
1753 | 0 | let mut ix = 0; |
1754 | 0 | for b in self.blocks.iter() { |
1755 | 0 | if ix > 0 { |
1756 | 0 | println!(""); |
1757 | 0 | } |
1758 | 0 | println!(" {:?}:{}", BlockIx::new(ix), b.name); |
1759 | | |
1760 | 0 | if let Some(anns_map) = mb_block_anns { |
1761 | 0 | for ann in &anns_map[BlockIx::new(ix)] { |
1762 | 0 | println!(" ;; {}", ann); |
1763 | 0 | } |
1764 | 0 | } |
1765 | | |
1766 | 0 | for i in b.start.get()..b.start.get() + b.len { |
1767 | 0 | let ix = InstIx::new(i); |
1768 | 0 | println!(" {:<3?} {:?}", ix, self.insns[ix]); |
1769 | 0 | } |
1770 | 0 | ix += 1; |
1771 | | } |
1772 | 0 | println!("}}"); |
1773 | 0 | } |
1774 | | |
1775 | | /// Prints the function in a way the parser can comprehend. This is used by |
1776 | | /// the fuzzer, but not otherwise, so allow dead code here. |
1777 | | #[allow(dead_code)] |
1778 | 0 | pub fn render(&self, who: &str, fmt: &mut String) -> fmt::Result { |
1779 | | use std::fmt::Write; |
1780 | | use std::iter::FromIterator; |
1781 | | |
1782 | 0 | let mut used_vregs = HashSet::new(); |
1783 | 0 | let mut used_rregs = HashSet::new(); |
1784 | | |
1785 | 0 | for b in self.blocks.iter() { |
1786 | 0 | for i in b.start.get()..b.start.get() + b.len { |
1787 | 0 | let iix = InstIx::new(i); |
1788 | 0 |
|
1789 | 0 | // Get the use, def and mod sets for `self.insns[iix]`. This is a bit |
1790 | 0 | // awkward since we don't have the library's internal convenience |
1791 | 0 | // functions available here. |
1792 | 0 | let mut ru_vecs = RegUsageCollector::get_empty_reg_vecs_test_framework_only(false); |
1793 | 0 | let mut ru_collector = RegUsageCollector::new(&mut ru_vecs); |
1794 | 0 |
|
1795 | 0 | self.insns[iix].get_reg_usage(&mut ru_collector); |
1796 | 0 | assert!(!ru_collector.reg_vecs.is_sanitized()); |
1797 | | |
1798 | 0 | let (used_vec, defined_vec, modified_vec) = |
1799 | 0 | ru_collector.get_use_def_mod_vecs_test_framework_only(); |
1800 | 0 | let used = Set::from_vec(used_vec); |
1801 | 0 | let defined = Set::from_vec(defined_vec); |
1802 | 0 | let modified = Set::from_vec(modified_vec); |
1803 | | |
1804 | 0 | for ® in defined.iter().chain(modified.iter()) { |
1805 | 0 | if reg.is_virtual() { |
1806 | 0 | used_vregs.insert(reg); |
1807 | 0 | } else { |
1808 | 0 | used_rregs.insert(reg); |
1809 | 0 | } |
1810 | | } |
1811 | 0 | for ® in used.iter() { |
1812 | 0 | if reg.is_virtual() { |
1813 | 0 | used_vregs.insert(reg); |
1814 | 0 | } else { |
1815 | 0 | used_rregs.insert(reg); |
1816 | 0 | } |
1817 | | } |
1818 | | } |
1819 | | } |
1820 | | |
1821 | 0 | let mut used_vregs = Vec::from_iter(used_vregs.into_iter()); |
1822 | 0 | used_vregs.sort(); |
1823 | 0 | let mut used_rregs = Vec::from_iter(used_rregs.into_iter()); |
1824 | 0 | used_rregs.sort(); |
1825 | | |
1826 | 0 | writeln!(fmt, "; {}", who)?; |
1827 | 0 | if let Some(start) = self.reftype_reg_start { |
1828 | 0 | writeln!(fmt, "{} = {}", REFTYPE_START, start)?; |
1829 | 0 | } |
1830 | 0 | for vreg in used_vregs { |
1831 | 0 | writeln!(fmt, "{:?} = {:?}", vreg, vreg.get_class())?; |
1832 | | } |
1833 | 0 | for rreg in used_rregs { |
1834 | 0 | writeln!( |
1835 | 0 | fmt, |
1836 | 0 | "{:?} = real {:?} {:?}", |
1837 | 0 | rreg, |
1838 | 0 | rreg.get_class(), |
1839 | 0 | rreg.get_index() |
1840 | 0 | )?; |
1841 | | } |
1842 | 0 | writeln!(fmt, "")?; |
1843 | | |
1844 | 0 | let mut ix = 0; |
1845 | 0 | for b in self.blocks.iter() { |
1846 | 0 | if ix > 0 { |
1847 | 0 | writeln!(fmt, "")?; |
1848 | 0 | } |
1849 | 0 | writeln!(fmt, "{:?}:", BlockIx::new(ix))?; |
1850 | 0 | for i in b.start.get()..b.start.get() + b.len { |
1851 | 0 | let ix = InstIx::new(i); |
1852 | 0 | writeln!(fmt, " {:?}", self.insns[ix])?; |
1853 | | } |
1854 | 0 | ix += 1; |
1855 | | } |
1856 | 0 | writeln!(fmt, "")?; |
1857 | | |
1858 | 0 | Ok(()) |
1859 | 0 | } |
1860 | | |
1861 | | // Get a new VirtualReg name |
1862 | | pub fn new_virtual_reg(&mut self, rc: RegClass) -> Reg { |
1863 | | let v = Reg::new_virtual(rc, self.num_virtual_regs); |
1864 | | self.num_virtual_regs += 1; |
1865 | | v |
1866 | | } |
1867 | | |
1868 | | // Add a block to the Func |
1869 | | pub fn block<'a>(&mut self, name: &'a str, insns: Vec<Inst>) { |
1870 | | let mut insns = TypedIxVec::from_vec(insns); |
1871 | | let start = self.insns.len(); |
1872 | | let len = insns.len() as u32; |
1873 | | self.insns.append(&mut insns); |
1874 | | let b = Block::new(name.to_string(), InstIx::new(start), len); |
1875 | | self.blocks.push(b); |
1876 | | } |
1877 | | |
1878 | | // All blocks have been added. Resolve labels and we're good to go. |
1879 | | /* .finish(): check |
1880 | | - all blocks nonempty |
1881 | | - all blocks end in i_finish, i_goto or i_goto_ctf |
1882 | | - no blocks have those insns before the end |
1883 | | - blocks are in increasing order of ::start fields |
1884 | | - all referenced blocks actually exist |
1885 | | - convert references to block numbers |
1886 | | */ |
1887 | 0 | pub fn finish(&mut self) { |
1888 | 0 | for bix in BlockIx::new(0).dotdot(BlockIx::new(self.blocks.len())) { |
1889 | 0 | let b = &self.blocks[bix]; |
1890 | 0 | if b.len == 0 { |
1891 | 0 | panic!("Func::done: a block is empty"); |
1892 | 0 | } |
1893 | 0 | if bix > BlockIx::new(0) && self.blocks[bix.minus(1)].start >= self.blocks[bix].start { |
1894 | 0 | panic!("Func: blocks are not in increasing order of InstIx"); |
1895 | 0 | } |
1896 | 0 | for i in 0..b.len { |
1897 | 0 | let iix = b.start.plus(i); |
1898 | 0 | if i == b.len - 1 && !self.insns[iix].is_control_flow() { |
1899 | 0 | panic!("Func: block must end in control flow insn"); |
1900 | 0 | } |
1901 | 0 | if i != b.len - 1 && self.insns[iix].is_control_flow() { |
1902 | 0 | panic!("Func: block contains control flow insn not at end"); |
1903 | 0 | } |
1904 | | } |
1905 | | } |
1906 | | |
1907 | | // Resolve all labels |
1908 | 0 | let blocks = &self.blocks; |
1909 | 0 | for i in self.insns.iter_mut() { |
1910 | 0 | resolve_inst(i, |name| lookup(blocks, name)); |
1911 | 0 | } |
1912 | 0 | resolve_label(self.entry.as_mut().unwrap(), |name| lookup(blocks, name)); |
1913 | 0 | } |
1914 | | |
1915 | 3.40k | pub fn update_from_alloc(&mut self, result: regalloc::RegAllocResult<Func>) { |
1916 | 3.40k | self.insns = TypedIxVec::from_vec(result.insns); |
1917 | 3.40k | let num_blocks = self.blocks.len(); |
1918 | 3.40k | let mut i = 0; |
1919 | 16.3k | for bix in self.blocks.range() { |
1920 | 16.3k | let block = &mut self.blocks[bix]; |
1921 | 16.3k | block.start = result.target_map[bix]; |
1922 | 16.3k | block.len = if i + 1 < num_blocks { |
1923 | 12.9k | result.target_map[BlockIx::new(i + 1)].get() |
1924 | | } else { |
1925 | 3.40k | self.insns.len() |
1926 | 16.3k | } - block.start.get(); |
1927 | 16.3k | i += 1; |
1928 | | } |
1929 | 3.40k | } |
1930 | | |
1931 | 4.26k | pub fn get_stackmap_request(&self) -> Option<StackmapRequestInfo> { |
1932 | 4.26k | self.reftype_reg_start.and_then(|reftype_reg_start| { |
1933 | 4.26k | if reftype_reg_start == self.num_virtual_regs { |
1934 | 0 | None |
1935 | | } else { |
1936 | 4.26k | let reftyped_vregs = (reftype_reg_start..self.num_virtual_regs) |
1937 | 29.3k | .map(|index| Reg::new_virtual(RegClass::I32, index).to_virtual_reg()) |
1938 | 4.26k | .collect::<Vec<_>>(); |
1939 | 4.26k | let mut safepoint_insns = vec![]; |
1940 | 123k | for iix in self.insns.range() { |
1941 | 123k | match self.insns[iix] { |
1942 | 14.7k | Inst::Safepoint => { |
1943 | 14.7k | safepoint_insns.push(iix); |
1944 | 14.7k | } |
1945 | 108k | _ => {} |
1946 | | } |
1947 | | } |
1948 | | debug!( |
1949 | 0 | "SRI: reftyped_vregs = {:?}, safepoint_insns = {:?}", |
1950 | 0 | reftyped_vregs, safepoint_insns |
1951 | | ); |
1952 | 4.26k | Some(StackmapRequestInfo { |
1953 | 4.26k | reftype_class: RegClass::I32, |
1954 | 4.26k | reftyped_vregs, |
1955 | 4.26k | safepoint_insns, |
1956 | 4.26k | }) |
1957 | | } |
1958 | 4.26k | }) |
1959 | 4.26k | } |
1960 | | } |
1961 | | |
1962 | 0 | fn resolve_label<F>(label: &mut Label, lookup: F) |
1963 | 0 | where |
1964 | 0 | F: Fn(String) -> BlockIx, |
1965 | 0 | { |
1966 | 0 | let resolved = match label { |
1967 | 0 | Label::Unresolved { name } => Label::Resolved { |
1968 | 0 | name: name.clone(), |
1969 | 0 | bix: lookup(name.clone()), |
1970 | 0 | }, |
1971 | 0 | Label::Resolved { .. } => panic!("resolveLabel: is already resolved!"), |
1972 | | }; |
1973 | 0 | *label = resolved; |
1974 | 0 | } Unexecuted instantiation: minira::test_framework::resolve_label::<<minira::test_framework::Func>::finish::{closure#0}>Unexecuted instantiation: minira::test_framework::resolve_label::<<minira::test_framework::Func>::finish::{closure#1}> |
1975 | | |
1976 | 0 | fn resolve_inst<F>(insn: &mut Inst, lookup: F) |
1977 | 0 | where |
1978 | 0 | F: Copy + Fn(String) -> BlockIx, |
1979 | 0 | { |
1980 | 0 | match insn { |
1981 | 0 | Inst::Goto { ref mut target } => resolve_label(target, lookup), |
1982 | | Inst::GotoCTF { |
1983 | | cond: _, |
1984 | 0 | ref mut target_true, |
1985 | 0 | ref mut target_false, |
1986 | 0 | } => { |
1987 | 0 | resolve_label(target_true, lookup); |
1988 | 0 | resolve_label(target_false, lookup); |
1989 | 0 | } |
1990 | 0 | _ => (), |
1991 | | } |
1992 | 0 | } |
1993 | | |
1994 | | pub enum Stmt { |
1995 | | Vanilla { |
1996 | | insn: Inst, |
1997 | | }, |
1998 | | IfThenElse { |
1999 | | cond: Reg, |
2000 | | stmts_t: Vec<Stmt>, |
2001 | | stmts_e: Vec<Stmt>, |
2002 | | }, |
2003 | | RepeatUntil { |
2004 | | stmts: Vec<Stmt>, |
2005 | | cond: Reg, |
2006 | | }, |
2007 | | WhileDo { |
2008 | | cond: Reg, |
2009 | | stmts: Vec<Stmt>, |
2010 | | }, |
2011 | | } |
2012 | | |
2013 | | // Various handy wrappers, mostly wrappings of i_* functions |
2014 | | pub fn s_if_then_else(cond: Reg, stmts_t: Vec<Stmt>, stmts_e: Vec<Stmt>) -> Stmt { |
2015 | | Stmt::IfThenElse { |
2016 | | cond, |
2017 | | stmts_t, |
2018 | | stmts_e, |
2019 | | } |
2020 | | } |
2021 | | pub fn s_if_then(cond: Reg, stmts_t: Vec<Stmt>) -> Stmt { |
2022 | | Stmt::IfThenElse { |
2023 | | cond, |
2024 | | stmts_t, |
2025 | | stmts_e: vec![], |
2026 | | } |
2027 | | } |
2028 | | pub fn s_repeat_until(stmts: Vec<Stmt>, cond: Reg) -> Stmt { |
2029 | | Stmt::RepeatUntil { stmts, cond } |
2030 | | } |
2031 | | pub fn s_while_do(cond: Reg, stmts: Vec<Stmt>) -> Stmt { |
2032 | | Stmt::WhileDo { cond, stmts } |
2033 | | } |
2034 | | |
2035 | | fn s_vanilla(insn: Inst) -> Stmt { |
2036 | | Stmt::Vanilla { insn } |
2037 | | } |
2038 | | |
2039 | | pub fn s_imm(dst: Reg, imm: u32) -> Stmt { |
2040 | | s_vanilla(i_imm(dst, imm)) |
2041 | | } |
2042 | | pub fn s_immf(dst: Reg, imm: f32) -> Stmt { |
2043 | | s_vanilla(i_immf(dst, imm)) |
2044 | | } |
2045 | | pub fn s_copy(dst: Reg, src: Reg) -> Stmt { |
2046 | | s_vanilla(i_copy(dst, src)) |
2047 | | } |
2048 | | pub fn s_load(dst: Reg, addr: AM) -> Stmt { |
2049 | | s_vanilla(i_load(dst, addr)) |
2050 | | } |
2051 | | pub fn s_loadf(dst: Reg, addr: AM) -> Stmt { |
2052 | | s_vanilla(i_loadf(dst, addr)) |
2053 | | } |
2054 | | pub fn s_store(addr: AM, src: Reg) -> Stmt { |
2055 | | s_vanilla(i_store(addr, src)) |
2056 | | } |
2057 | | pub fn s_storef(addr: AM, src: Reg) -> Stmt { |
2058 | | s_vanilla(i_storef(addr, src)) |
2059 | | } |
2060 | | pub fn s_print_s<'a>(str: &'a str) -> Stmt { |
2061 | | s_vanilla(i_print_s(str)) |
2062 | | } |
2063 | | pub fn s_print_i(reg: Reg) -> Stmt { |
2064 | | s_vanilla(i_print_i(reg)) |
2065 | | } |
2066 | | pub fn s_print_f(reg: Reg) -> Stmt { |
2067 | | s_vanilla(i_print_f(reg)) |
2068 | | } |
2069 | | |
2070 | | pub fn s_add(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2071 | | s_vanilla(i_add(dst, src_left, src_right)) |
2072 | | } |
2073 | | pub fn s_sub(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2074 | | s_vanilla(i_sub(dst, src_left, src_right)) |
2075 | | } |
2076 | | pub fn s_mul(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2077 | | s_vanilla(i_mul(dst, src_left, src_right)) |
2078 | | } |
2079 | | pub fn s_mod(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2080 | | s_vanilla(i_mod(dst, src_left, src_right)) |
2081 | | } |
2082 | | pub fn s_shr(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2083 | | s_vanilla(i_shr(dst, src_left, src_right)) |
2084 | | } |
2085 | | pub fn s_and(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2086 | | s_vanilla(i_and(dst, src_left, src_right)) |
2087 | | } |
2088 | | pub fn s_cmp_eq(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2089 | | s_vanilla(i_cmp_eq(dst, src_left, src_right)) |
2090 | | } |
2091 | | pub fn s_cmp_lt(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2092 | | s_vanilla(i_cmp_lt(dst, src_left, src_right)) |
2093 | | } |
2094 | | pub fn s_cmp_le(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2095 | | s_vanilla(i_cmp_le(dst, src_left, src_right)) |
2096 | | } |
2097 | | pub fn s_cmp_ge(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2098 | | s_vanilla(i_cmp_ge(dst, src_left, src_right)) |
2099 | | } |
2100 | | pub fn s_cmp_gt(dst: Reg, src_left: Reg, src_right: RI) -> Stmt { |
2101 | | s_vanilla(i_cmp_gt(dst, src_left, src_right)) |
2102 | | } |
2103 | | |
2104 | | pub fn s_addm(dst: Reg, src_right: RI) -> Stmt { |
2105 | | s_vanilla(i_addm(dst, src_right)) |
2106 | | } |
2107 | | //fn s_subm(dst: Reg, src_right: RI) -> Stmt { |
2108 | | // s_vanilla(i_subm(dst, src_right)) |
2109 | | //} |
2110 | | |
2111 | | pub fn s_fadd(dst: Reg, src_left: Reg, src_right: Reg) -> Stmt { |
2112 | | s_vanilla(i_fadd(dst, src_left, src_right)) |
2113 | | } |
2114 | | pub fn s_fsub(dst: Reg, src_left: Reg, src_right: Reg) -> Stmt { |
2115 | | s_vanilla(i_fsub(dst, src_left, src_right)) |
2116 | | } |
2117 | | pub fn s_fmul(dst: Reg, src_left: Reg, src_right: Reg) -> Stmt { |
2118 | | s_vanilla(i_fmul(dst, src_left, src_right)) |
2119 | | } |
2120 | | pub fn s_fdiv(dst: Reg, src_left: Reg, src_right: Reg) -> Stmt { |
2121 | | s_vanilla(i_fdiv(dst, src_left, src_right)) |
2122 | | } |
2123 | | |
2124 | | //============================================================================= |
2125 | | // The "blockifier". This is just to make it easier to write test cases, by |
2126 | | // allowing direct use of if-then-else, do-while and repeat-until. It is |
2127 | | // otherwise entirely unrelated to the register allocator proper. |
2128 | | |
2129 | | pub struct Blockifier { |
2130 | | name: String, |
2131 | | blocks: Vec<Vec<Inst>>, |
2132 | | num_virtual_regs: u32, |
2133 | | } |
2134 | | |
2135 | | fn make_text_label_str(n: usize) -> String { |
2136 | | "L".to_string() + &n.to_string() |
2137 | | } |
2138 | | |
2139 | | impl Blockifier { |
2140 | | pub fn new<'a>(name: &'a str) -> Self { |
2141 | | Self { |
2142 | | name: name.to_string(), |
2143 | | blocks: vec![], |
2144 | | num_virtual_regs: 0, |
2145 | | } |
2146 | | } |
2147 | | |
2148 | | // Get a new VirtualReg name |
2149 | | pub fn new_virtual_reg(&mut self, rc: RegClass) -> Reg { |
2150 | | let v = Reg::new_virtual(rc, self.num_virtual_regs); |
2151 | | self.num_virtual_regs += 1; |
2152 | | v |
2153 | | } |
2154 | | |
2155 | | /// Recursive worker function, which flattens out the control flow, producing |
2156 | | /// a set of blocks. |
2157 | 0 | fn blockify(&mut self, stmts: Vec<Stmt>) -> (usize, usize) { |
2158 | 0 | let entry_block = self.blocks.len(); |
2159 | 0 | let mut cur_block = entry_block; |
2160 | 0 | self.blocks.push(Vec::new()); |
2161 | 0 | for s in stmts { |
2162 | 0 | match s { |
2163 | 0 | Stmt::Vanilla { insn } => { |
2164 | 0 | self.blocks[cur_block].push(insn); |
2165 | 0 | } |
2166 | | Stmt::IfThenElse { |
2167 | 0 | cond, |
2168 | 0 | stmts_t, |
2169 | 0 | stmts_e, |
2170 | 0 | } => { |
2171 | 0 | let (t_ent, t_exit) = self.blockify(stmts_t); |
2172 | 0 | let (e_ent, e_exit) = self.blockify(stmts_e); |
2173 | 0 | let cont = self.blocks.len(); |
2174 | 0 | self.blocks.push(Vec::new()); |
2175 | 0 | self.blocks[t_exit].push(i_goto(&make_text_label_str(cont))); |
2176 | 0 | self.blocks[e_exit].push(i_goto(&make_text_label_str(cont))); |
2177 | 0 | self.blocks[cur_block].push(i_goto_ctf( |
2178 | 0 | cond, |
2179 | 0 | &make_text_label_str(t_ent), |
2180 | 0 | &make_text_label_str(e_ent), |
2181 | 0 | )); |
2182 | 0 | cur_block = cont; |
2183 | 0 | } |
2184 | 0 | Stmt::RepeatUntil { stmts, cond } => { |
2185 | 0 | let (s_ent, s_exit) = self.blockify(stmts); |
2186 | 0 |
|
2187 | 0 | // Don't create critical edges by creating the following loop |
2188 | 0 | // structure: |
2189 | 0 | // |
2190 | 0 | // current -> loop_header -> s_ent -> s_exit -> after_loop |
2191 | 0 | // ^ | |
2192 | 0 | // |-------- loop_continue < |
2193 | 0 |
|
2194 | 0 | let loop_header = self.blocks.len(); |
2195 | 0 | self.blocks.push(vec![i_goto(&make_text_label_str(s_ent))]); |
2196 | 0 |
|
2197 | 0 | self.blocks[cur_block].push(i_goto(&make_text_label_str(loop_header))); |
2198 | 0 |
|
2199 | 0 | let loop_continue = self.blocks.len(); |
2200 | 0 | self.blocks |
2201 | 0 | .push(vec![i_goto(&make_text_label_str(loop_header))]); |
2202 | 0 |
|
2203 | 0 | let after_loop = self.blocks.len(); |
2204 | 0 | self.blocks.push(Vec::new()); |
2205 | 0 |
|
2206 | 0 | self.blocks[s_exit].push(i_goto_ctf( |
2207 | 0 | cond, |
2208 | 0 | &make_text_label_str(after_loop), |
2209 | 0 | &make_text_label_str(loop_continue), |
2210 | 0 | )); |
2211 | 0 |
|
2212 | 0 | cur_block = after_loop; |
2213 | 0 | } |
2214 | 0 | Stmt::WhileDo { cond, stmts } => { |
2215 | 0 | let condblock = self.blocks.len(); |
2216 | 0 | self.blocks.push(Vec::new()); |
2217 | 0 | self.blocks[cur_block].push(i_goto(&make_text_label_str(condblock))); |
2218 | 0 | let (s_ent, s_exit) = self.blockify(stmts); |
2219 | 0 | self.blocks[s_exit].push(i_goto(&make_text_label_str(condblock))); |
2220 | 0 | let cont = self.blocks.len(); |
2221 | 0 | self.blocks.push(Vec::new()); |
2222 | 0 | self.blocks[condblock].push(i_goto_ctf( |
2223 | 0 | cond, |
2224 | 0 | &make_text_label_str(s_ent), |
2225 | 0 | &make_text_label_str(cont), |
2226 | 0 | )); |
2227 | 0 | cur_block = cont; |
2228 | 0 | } |
2229 | | } |
2230 | | } |
2231 | 0 | (entry_block, cur_block) |
2232 | 0 | } |
2233 | | |
2234 | | // The main external function. Convert the given statements, into a Func. |
2235 | 0 | pub fn finish(mut self, stmts: Vec<Stmt>, ret_value: Option<Reg>) -> Func { |
2236 | 0 | let (ent_bno, exit_bno) = self.blockify(stmts); |
2237 | 0 | self.blocks[exit_bno].push(i_finish(ret_value)); |
2238 | 0 |
|
2239 | 0 | // Convert (ent_bno, exit_bno, cleanedUp) into a Func |
2240 | 0 | let mut func = Func::new(&self.name); |
2241 | 0 | func.set_entry(&make_text_label_str(ent_bno)); |
2242 | 0 | func.num_virtual_regs = self.num_virtual_regs; |
2243 | 0 | let mut n = 0; |
2244 | 0 | for ivec in self.blocks { |
2245 | 0 | func.block(&make_text_label_str(n), ivec); |
2246 | 0 | n += 1; |
2247 | 0 | } |
2248 | | |
2249 | 0 | func.finish(); |
2250 | 0 | func |
2251 | 0 | } |
2252 | | } |
2253 | | |
2254 | | // -------------------------------------------------- |
2255 | | // Implementation of `Function` trait for test cases. |
2256 | | |
2257 | | impl regalloc::Function for Func { |
2258 | | type Inst = Inst; |
2259 | | |
2260 | 33.0k | fn insns(&self) -> &[Inst] { |
2261 | 33.0k | self.insns.elems() |
2262 | 33.0k | } |
2263 | | |
2264 | | fn insns_mut(&mut self) -> &mut [Inst] { |
2265 | | self.insns.elems_mut() |
2266 | | } |
2267 | | |
2268 | 320k | fn get_insn(&self, iix: InstIx) -> &Inst { |
2269 | 320k | &self.insns[iix] |
2270 | 320k | } |
2271 | | |
2272 | 98.4k | fn get_insn_mut(&mut self, iix: InstIx) -> &mut Inst { |
2273 | 98.4k | &mut self.insns[iix] |
2274 | 98.4k | } |
2275 | | |
2276 | 18.5k | fn entry_block(&self) -> BlockIx { |
2277 | 18.5k | BlockIx::new(0) |
2278 | 18.5k | } |
2279 | | |
2280 | 58.4k | fn blocks(&self) -> Range<BlockIx> { |
2281 | 58.4k | self.blocks.range() |
2282 | 58.4k | } |
2283 | | |
2284 | | /// Provide the range of instruction indices contained in each block. |
2285 | 563k | fn block_insns(&self, block: BlockIx) -> Range<InstIx> { |
2286 | 563k | Range::new(self.blocks[block].start, self.blocks[block].len as usize) |
2287 | 563k | } |
2288 | | |
2289 | | /// Get CFG successors: indexed by block, provide a list of successor blocks. |
2290 | 38.2k | fn block_succs(&self, block: BlockIx) -> Cow<[BlockIx]> { |
2291 | 38.2k | let last_insn = self.blocks[block].start.plus(self.blocks[block].len - 1); |
2292 | 38.2k | Cow::Owned(self.insns[last_insn].get_targets()) |
2293 | 38.2k | } |
2294 | | |
2295 | 16.7k | fn is_ret(&self, insn: InstIx) -> bool { |
2296 | 16.7k | match &self.insns[insn] { |
2297 | 682 | &Inst::Finish { .. } => true, |
2298 | 16.0k | _ => false, |
2299 | | } |
2300 | 16.7k | } |
2301 | | |
2302 | | /// Provide the defined, used, and modified registers for an instruction. |
2303 | 328k | fn get_regs(insn: &Self::Inst, collector: &mut RegUsageCollector) { |
2304 | 328k | insn.get_reg_usage(collector); |
2305 | 328k | } |
2306 | | |
2307 | | /// Map each register slot through a virt -> phys mapping indexed |
2308 | | /// by virtual register. The two separate maps provide the |
2309 | | /// mapping to use for uses (which semantically occur just prior |
2310 | | /// to the instruction's effect) and defs (which semantically occur |
2311 | | /// just after the instruction's effect). Regs that were "modified" |
2312 | | /// can use either map; the vreg should be the same in both. |
2313 | 94.0k | fn map_regs<RUM: RegUsageMapper>(insn: &mut Self::Inst, maps: &RUM) { |
2314 | 94.0k | insn.map_regs(maps); |
2315 | 94.0k | } Unexecuted instantiation: minira::test_framework::i_print_f |
2316 | | |
2317 | | /// Allow the regalloc to query whether this is a move. |
2318 | 127k | fn is_move(&self, insn: &Self::Inst) -> Option<(Writable<Reg>, Reg)> { |
2319 | 127k | match insn { |
2320 | 35.6k | &Inst::Copy { dst, src } => Some((Writable::from_reg(dst), src)), |
2321 | 91.9k | _ => None, |
2322 | | } |
2323 | 127k | } |
2324 | | |
2325 | 14.5k | fn get_num_vregs(&self) -> usize { |
2326 | 14.5k | self.num_virtual_regs as usize |
2327 | 14.5k | } |
2328 | | |
2329 | | /// How many logical spill slots does the given regclass require? E.g., on a |
2330 | | /// 64-bit machine, spill slots may nominally be 64-bit words, but a 128-bit |
2331 | | /// vector value will require two slots. The regalloc will always align on |
2332 | | /// this size. |
2333 | 4.89k | fn get_spillslot_size(&self, _regclass: RegClass, _for_vreg: VirtualReg) -> u32 { |
2334 | 4.89k | // For our simple test ISA, every value occupies one spill slot. |
2335 | 4.89k | 1 |
2336 | 4.89k | } |
2337 | | |
2338 | | /// Generate a spill instruction for insertion into the instruction sequence. |
2339 | 11.3k | fn gen_spill( |
2340 | 11.3k | &self, |
2341 | 11.3k | to_slot: SpillSlot, |
2342 | 11.3k | from_reg: RealReg, |
2343 | 11.3k | _for_vreg: Option<VirtualReg>, |
2344 | 11.3k | ) -> Self::Inst { |
2345 | 11.3k | match from_reg.get_class() { |
2346 | 11.2k | RegClass::I32 => i_spill(to_slot, from_reg), |
2347 | 102 | RegClass::F32 => i_spillf(to_slot, from_reg), |
2348 | 0 | _ => panic!("Unused register class in test ISA was used"), |
2349 | | } |
2350 | 11.3k | } |
2351 | | |
2352 | | /// Generate a reload instruction for insertion into the instruction sequence. |
2353 | 16.3k | fn gen_reload( |
2354 | 16.3k | &self, |
2355 | 16.3k | to_reg: Writable<RealReg>, |
2356 | 16.3k | from_slot: SpillSlot, |
2357 | 16.3k | _for_vreg: Option<VirtualReg>, |
2358 | 16.3k | ) -> Self::Inst { |
2359 | 16.3k | match to_reg.to_reg().get_class() { |
2360 | 16.0k | RegClass::I32 => i_reload(to_reg.to_reg(), from_slot), |
2361 | 288 | RegClass::F32 => i_reloadf(to_reg.to_reg(), from_slot), |
2362 | 0 | _ => panic!("Unused register class in test ISA was used"), |
2363 | | } |
2364 | 16.3k | } |
2365 | | |
2366 | | /// Generate a register-to-register move for insertion into the instruction |
2367 | | /// sequence. |
2368 | 0 | fn gen_move( |
2369 | 0 | &self, |
2370 | 0 | to_reg: Writable<RealReg>, |
2371 | 0 | from_reg: RealReg, |
2372 | 0 | _for_vreg: VirtualReg, |
2373 | 0 | ) -> Self::Inst { |
2374 | 0 | match to_reg.to_reg().get_class() { |
2375 | 0 | RegClass::I32 => Inst::Copy { |
2376 | 0 | src: from_reg.to_reg(), |
2377 | 0 | dst: to_reg.to_reg().to_reg(), |
2378 | 0 | }, |
2379 | 0 | RegClass::F32 => Inst::CopyF { |
2380 | 0 | src: from_reg.to_reg(), |
2381 | 0 | dst: to_reg.to_reg().to_reg(), |
2382 | 0 | }, |
2383 | 0 | _ => unimplemented!("gen_move for non i32/f32"), |
2384 | | } |
2385 | 0 | } |
2386 | | |
2387 | | /// Generate an instruction which is a no-op and has zero length. |
2388 | 4.36k | fn gen_zero_len_nop(&self) -> Self::Inst { |
2389 | 4.36k | Inst::NopZ {} |
2390 | 4.36k | } |
2391 | | |
2392 | | /// Try to alter an existing instruction to use a value directly in a |
2393 | | /// spillslot (accessing memory directly) instead of the given register. May |
2394 | | /// be useful on ISAs that have mem/reg ops, like x86. |
2395 | | /// |
2396 | | /// Note that this is not *quite* just fusing a load with the op; if the |
2397 | | /// value is def'd or modified, it should be written back to the spill slot |
2398 | | /// as well. In other words, it is just using the spillslot as if it were a |
2399 | | /// real register, for reads and/or writes. |
2400 | | fn maybe_direct_reload( |
2401 | | &self, |
2402 | | _insn: &Self::Inst, |
2403 | | _reg: VirtualReg, |
2404 | | _slot: SpillSlot, |
2405 | | ) -> Option<Self::Inst> { |
2406 | | // test ISA does not have register-memory ALU instruction forms. |
2407 | | None |
2408 | | } |
2409 | | |
2410 | 3.64k | fn func_liveins(&self) -> Set<RealReg> { |
2411 | 3.64k | Set::empty() |
2412 | 3.64k | } |
2413 | | |
2414 | 3.45k | fn func_liveouts(&self) -> Set<RealReg> { |
2415 | 3.45k | Set::empty() |
2416 | 3.45k | } |
2417 | | } |
2418 | | |
2419 | | /// Create a universe for testing, with nI32 `I32` class regs and nF32 `F32` |
2420 | | /// class regs. |
2421 | 4.26k | pub fn make_universe(num_i32: usize, num_f32: usize) -> RealRegUniverse { |
2422 | 4.26k | let total_regs = num_i32 + num_f32; |
2423 | 4.26k | if total_regs >= 256 { |
2424 | 0 | panic!("make_universe: too many regs, cannot represent"); |
2425 | 4.26k | } |
2426 | 4.26k | |
2427 | 4.26k | let mut regs = Vec::<(RealReg, String)>::new(); |
2428 | 4.26k | let mut allocable_by_class = [None; NUM_REG_CLASSES]; |
2429 | 4.26k | let mut index = 0u8; |
2430 | 4.26k | |
2431 | 4.26k | if num_i32 > 0 { |
2432 | 4.26k | let first = index as usize; |
2433 | 17.0k | for i in 0..num_i32 { |
2434 | 17.0k | let name = format!("R{}", i).to_string(); |
2435 | 17.0k | let reg = Reg::new_real(RegClass::I32, /*enc=*/ 0, index).to_real_reg(); |
2436 | 17.0k | regs.push((reg, name)); |
2437 | 17.0k | index += 1; |
2438 | 17.0k | } |
2439 | 4.26k | let last = index as usize - 1; |
2440 | 4.26k | allocable_by_class[RegClass::I32.rc_to_usize()] = Some(RegClassInfo { |
2441 | 4.26k | first, |
2442 | 4.26k | last, |
2443 | 4.26k | suggested_scratch: Some(last), |
2444 | 4.26k | }); |
2445 | 0 | } |
2446 | | |
2447 | 4.26k | if num_f32 > 0 { |
2448 | 4.26k | let first = index as usize; |
2449 | 17.0k | for i in 0..num_f32 { |
2450 | 17.0k | let name = format!("F{}", i).to_string(); |
2451 | 17.0k | let reg = Reg::new_real(RegClass::F32, /*enc=*/ 0, index).to_real_reg(); |
2452 | 17.0k | regs.push((reg, name)); |
2453 | 17.0k | index += 1; |
2454 | 17.0k | } |
2455 | 4.26k | let last = index as usize - 1; |
2456 | 4.26k | allocable_by_class[RegClass::F32.rc_to_usize()] = Some(RegClassInfo { |
2457 | 4.26k | first, |
2458 | 4.26k | last, |
2459 | 4.26k | suggested_scratch: Some(last), |
2460 | 4.26k | }); |
2461 | 0 | } |
2462 | | |
2463 | | debug_assert!(index as usize == total_regs); |
2464 | | |
2465 | 4.26k | let allocable = regs.len(); |
2466 | 4.26k | let univ = RealRegUniverse { |
2467 | 4.26k | regs, |
2468 | 4.26k | // for this example, all regs are allocable |
2469 | 4.26k | allocable, |
2470 | 4.26k | allocable_by_class, |
2471 | 4.26k | }; |
2472 | 4.26k | univ.check_is_sane(); |
2473 | 4.26k | |
2474 | 4.26k | univ |
2475 | 4.26k | } |