Coverage Report

Created: 2024-10-16 07:58

/rust/registry/src/index.crates.io-6f17d22bba15001f/regalloc2-0.5.1/src/checker.rs
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * The following code is derived from `lib/src/checker.rs` in the
3
 * regalloc.rs project
4
 * (https://github.com/bytecodealliance/regalloc.rs). regalloc.rs is
5
 * also licensed under Apache-2.0 with the LLVM exception, as the rest
6
 * of regalloc2 is.
7
 */
8
9
//! Checker: verifies that spills/reloads/moves retain equivalent
10
//! dataflow to original, VReg-based code.
11
//!
12
//! The basic idea is that we track symbolic values as they flow
13
//! through spills and reloads.  The symbolic values represent
14
//! particular virtual registers in the original function body
15
//! presented to the register allocator. Any instruction in the
16
//! original function body (i.e., not added by the allocator)
17
//! conceptually generates a symbolic value "Vn" when storing to (or
18
//! modifying) a virtual register.
19
//!
20
//! A symbolic value is logically a *set of virtual registers*,
21
//! representing all virtual registers equal to the value in the given
22
//! storage slot at a given program point. This representation (as
23
//! opposed to tracking just one virtual register) is necessary
24
//! because the regalloc may implement moves in the source program
25
//! (via move instructions or blockparam assignments on edges) in
26
//! "intelligent" ways, taking advantage of values that are already in
27
//! the right place, so we need to know *all* names for a value.
28
//!
29
//! These symbolic values are precise but partial: in other words, if
30
//! a physical register is described as containing a virtual register
31
//! at a program point, it must actually contain the value of this
32
//! register (modulo any analysis bugs); but it may describe fewer
33
//! virtual registers even in cases where one *could* statically prove
34
//! that it contains a certain register, because the analysis is not
35
//! perfectly path-sensitive or value-sensitive. However, all
36
//! assignments *produced by our register allocator* should be
37
//! analyzed fully precisely. (This last point is important and bears
38
//! repeating: we only need to verify the programs that we produce,
39
//! not arbitrary programs.)
40
//!
41
//! Operand constraints (fixed register, register, any) are also checked
42
//! at each operand.
43
//!
44
//! ## Formal Definition
45
//!
46
//! The analysis lattice consists of the elements of 𝒫(V), the
47
//! powerset (set of all subsets) of V (the set of all virtual
48
//! registers). The ⊤ (top) value in the lattice is V itself, and the
49
//! ⊥ (bottom) value in the lattice is ∅ (the empty set). The lattice
50
//! ordering relation is the subset relation: S ≤ U iff S ⊆ U. These
51
//! definitions imply that the lattice meet-function (greatest lower
52
//! bound) is set-intersection.
53
//!
54
//! (For efficiency, we represent ⊤ not by actually listing out all
55
//! virtual registers, but by representing a special "top" value, but
56
//! the semantics are the same.)
57
//!
58
//! The dataflow analysis state at each program point (each point
59
//! before or after an instruction) is:
60
//!
61
//!   - map of: Allocation -> lattice value
62
//!
63
//! And the transfer functions for instructions are (where `A` is the
64
//! above map from allocated physical registers to lattice values):
65
//!
66
//!   - `Edit::Move` inserted by RA:       [ alloc_d := alloc_s ]
67
//!
68
//!       A' = A[alloc_d → A[alloc_s]]
69
//!
70
//!   - statement in pre-regalloc function [ V_i := op V_j, V_k, ... ]
71
//!     with allocated form                [ A_i := op A_j, A_k, ... ]
72
//!
73
//!       A' = { A_k → A[A_k] \ { V_i } for k ≠ i } ∪
74
//!            { A_i -> { V_i } }
75
//!
76
//!     In other words, a statement, even after allocation, generates
77
//!     a symbol that corresponds to its original virtual-register
78
//!     def. Simultaneously, that same virtual register symbol is removed
79
//!     from all other allocs: they no longer carry the current value.
80
//!
81
//!   - Parallel moves or blockparam-assignments in original program
82
//!                                       [ V_d1 := V_s1, V_d2 := V_s2, ... ]
83
//!
84
//!       A' = { A_k → subst(A[A_k]) for all k }
85
//!            where subst(S) removes symbols for overwritten virtual
86
//!            registers (V_d1 .. V_dn) and then adds V_di whenever
87
//!            V_si appeared prior to the removals.
88
//!
89
//! To check correctness, we first find the dataflow fixpoint with the
90
//! above lattice and transfer/meet functions. Then, at each op, we
91
//! examine the dataflow solution at the preceding program point, and
92
//! check that the allocation for each op arg (input/use) contains the
93
//! symbol corresponding to the original virtual register specified
94
//! for this arg.
95
96
#![allow(dead_code)]
97
98
use crate::{
99
    Allocation, AllocationKind, Block, Edit, Function, Inst, InstOrEdit, InstPosition, MachineEnv,
100
    Operand, OperandConstraint, OperandKind, OperandPos, Output, PReg, PRegSet, VReg,
101
};
102
use fxhash::{FxHashMap, FxHashSet};
103
use smallvec::{smallvec, SmallVec};
104
use std::default::Default;
105
use std::hash::Hash;
106
use std::result::Result;
107
108
/// A set of errors detected by the regalloc checker.
109
#[derive(Clone, Debug)]
110
pub struct CheckerErrors {
111
    errors: Vec<CheckerError>,
112
}
113
114
/// A single error detected by the regalloc checker.
115
#[derive(Clone, Debug)]
116
pub enum CheckerError {
117
    MissingAllocation {
118
        inst: Inst,
119
        op: Operand,
120
    },
121
    UnknownValueInAllocation {
122
        inst: Inst,
123
        op: Operand,
124
        alloc: Allocation,
125
    },
126
    ConflictedValueInAllocation {
127
        inst: Inst,
128
        op: Operand,
129
        alloc: Allocation,
130
    },
131
    IncorrectValuesInAllocation {
132
        inst: Inst,
133
        op: Operand,
134
        alloc: Allocation,
135
        actual: FxHashSet<VReg>,
136
    },
137
    ConstraintViolated {
138
        inst: Inst,
139
        op: Operand,
140
        alloc: Allocation,
141
    },
142
    AllocationIsNotReg {
143
        inst: Inst,
144
        op: Operand,
145
        alloc: Allocation,
146
    },
147
    AllocationIsNotFixedReg {
148
        inst: Inst,
149
        op: Operand,
150
        alloc: Allocation,
151
    },
152
    AllocationIsNotReuse {
153
        inst: Inst,
154
        op: Operand,
155
        alloc: Allocation,
156
        expected_alloc: Allocation,
157
    },
158
    AllocationIsNotStack {
159
        inst: Inst,
160
        op: Operand,
161
        alloc: Allocation,
162
    },
163
    ConflictedValueInStackmap {
164
        inst: Inst,
165
        alloc: Allocation,
166
    },
167
    NonRefValuesInStackmap {
168
        inst: Inst,
169
        alloc: Allocation,
170
        vregs: FxHashSet<VReg>,
171
    },
172
    StackToStackMove {
173
        into: Allocation,
174
        from: Allocation,
175
    },
176
}
177
178
/// Abstract state for an allocation.
179
///
180
/// Equivalent to a set of virtual register names, with the
181
/// universe-set as top and empty set as bottom lattice element. The
182
/// meet-function is thus set intersection.
183
#[derive(Clone, Debug, PartialEq, Eq)]
184
enum CheckerValue {
185
    /// The lattice top-value: this value could be equivalent to any
186
    /// vreg (i.e., the universe set).
187
    Universe,
188
    /// The set of VRegs that this value is equal to.
189
    VRegs(FxHashSet<VReg>),
190
}
191
192
impl CheckerValue {
193
0
    fn vregs(&self) -> Option<&FxHashSet<VReg>> {
194
0
        match self {
195
0
            CheckerValue::Universe => None,
196
0
            CheckerValue::VRegs(vregs) => Some(vregs),
197
        }
198
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerValue>::vregs
Unexecuted instantiation: <regalloc2::checker::CheckerValue>::vregs
199
200
0
    fn vregs_mut(&mut self) -> Option<&mut FxHashSet<VReg>> {
201
0
        match self {
202
0
            CheckerValue::Universe => None,
203
0
            CheckerValue::VRegs(vregs) => Some(vregs),
204
        }
205
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerValue>::vregs_mut
Unexecuted instantiation: <regalloc2::checker::CheckerValue>::vregs_mut
206
}
207
208
impl Default for CheckerValue {
209
0
    fn default() -> CheckerValue {
210
0
        CheckerValue::Universe
211
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerValue as core::default::Default>::default
Unexecuted instantiation: <regalloc2::checker::CheckerValue as core::default::Default>::default
212
}
213
214
impl CheckerValue {
215
    /// Meet function of the abstract-interpretation value
216
    /// lattice. Returns a boolean value indicating whether `self` was
217
    /// changed.
218
0
    fn meet_with(&mut self, other: &CheckerValue) {
219
0
        match (self, other) {
220
0
            (_, CheckerValue::Universe) => {
221
0
                // Nothing.
222
0
            }
223
0
            (this @ CheckerValue::Universe, _) => {
224
0
                *this = other.clone();
225
0
            }
226
0
            (CheckerValue::VRegs(my_vregs), CheckerValue::VRegs(other_vregs)) => {
227
0
                my_vregs.retain(|vreg| other_vregs.contains(vreg));
228
0
            }
229
        }
230
0
    }
231
232
0
    fn from_reg(reg: VReg) -> CheckerValue {
233
0
        CheckerValue::VRegs(std::iter::once(reg).collect())
234
0
    }
235
236
0
    fn remove_vreg(&mut self, reg: VReg) {
237
0
        match self {
238
            CheckerValue::Universe => {
239
0
                panic!("Cannot remove VReg from Universe set (we do not have the full list of vregs available");
240
            }
241
0
            CheckerValue::VRegs(vregs) => {
242
0
                vregs.remove(&reg);
243
0
            }
244
0
        }
245
0
    }
246
247
0
    fn copy_vreg(&mut self, src: VReg, dst: VReg) {
248
0
        match self {
249
0
            CheckerValue::Universe => {
250
0
                // Nothing.
251
0
            }
252
0
            CheckerValue::VRegs(vregs) => {
253
0
                if vregs.contains(&src) {
254
0
                    vregs.insert(dst);
255
0
                }
256
            }
257
        }
258
0
    }
259
260
0
    fn empty() -> CheckerValue {
261
0
        CheckerValue::VRegs(FxHashSet::default())
262
0
    }
263
}
264
265
0
fn visit_all_vregs<F: Function, V: FnMut(VReg)>(f: &F, mut v: V) {
266
0
    for block in 0..f.num_blocks() {
267
0
        let block = Block::new(block);
268
0
        for inst in f.block_insns(block).iter() {
269
0
            for op in f.inst_operands(inst) {
270
0
                v(op.vreg());
271
0
            }
272
0
            if let Some((src, dst)) = f.is_move(inst) {
273
0
                v(src.vreg());
274
0
                v(dst.vreg());
275
0
            }
276
0
            if f.is_branch(inst) {
277
0
                for succ_idx in 0..f.block_succs(block).len() {
278
0
                    for &param in f.branch_blockparams(block, inst, succ_idx) {
279
0
                        v(param);
280
0
                    }
281
                }
282
0
            }
283
        }
284
0
        for &vreg in f.block_params(block) {
285
0
            v(vreg);
286
0
        }
287
    }
288
0
}
Unexecuted instantiation: regalloc2::checker::visit_all_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>, <regalloc2::checker::CheckerState>::initial_with_pinned_vregs<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>::{closure#0}>
Unexecuted instantiation: regalloc2::checker::visit_all_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>, <regalloc2::checker::CheckerState>::initial_with_pinned_vregs<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>::{closure#0}>
Unexecuted instantiation: regalloc2::checker::visit_all_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>, <regalloc2::checker::CheckerState>::initial_with_pinned_vregs<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>::{closure#0}>
Unexecuted instantiation: regalloc2::checker::visit_all_vregs::<_, _>
289
290
/// State that steps through program points as we scan over the instruction stream.
291
#[derive(Clone, Debug, PartialEq, Eq)]
292
enum CheckerState {
293
    Top,
294
    Allocations(FxHashMap<Allocation, CheckerValue>),
295
}
296
297
impl CheckerState {
298
0
    fn get_value(&self, alloc: &Allocation) -> Option<&CheckerValue> {
299
0
        match self {
300
0
            CheckerState::Top => None,
301
0
            CheckerState::Allocations(allocs) => allocs.get(alloc),
302
        }
303
0
    }
304
305
0
    fn get_values_mut(&mut self) -> impl Iterator<Item = &mut CheckerValue> {
306
0
        match self {
307
0
            CheckerState::Top => panic!("Cannot get mutable values iterator on Top state"),
308
0
            CheckerState::Allocations(allocs) => allocs.values_mut(),
309
0
        }
310
0
    }
311
312
0
    fn get_mappings(&self) -> impl Iterator<Item = (&Allocation, &CheckerValue)> {
313
0
        match self {
314
0
            CheckerState::Top => panic!("Cannot get mappings iterator on Top state"),
315
0
            CheckerState::Allocations(allocs) => allocs.iter(),
316
0
        }
317
0
    }
318
319
0
    fn get_mappings_mut(&mut self) -> impl Iterator<Item = (&Allocation, &mut CheckerValue)> {
320
0
        match self {
321
0
            CheckerState::Top => panic!("Cannot get mutable mappings iterator on Top state"),
322
0
            CheckerState::Allocations(allocs) => allocs.iter_mut(),
323
0
        }
324
0
    }
325
326
    /// Transition from a "top" (undefined/unanalyzed) state to an empty set of allocations.
327
0
    fn become_defined(&mut self) {
328
0
        match self {
329
0
            CheckerState::Top => *self = CheckerState::Allocations(FxHashMap::default()),
330
0
            _ => {}
331
        }
332
0
    }
333
334
0
    fn set_value(&mut self, alloc: Allocation, value: CheckerValue) {
335
0
        match self {
336
            CheckerState::Top => {
337
0
                panic!("Cannot set value on Top state");
338
            }
339
0
            CheckerState::Allocations(allocs) => {
340
0
                allocs.insert(alloc, value);
341
0
            }
342
0
        }
343
0
    }
344
345
0
    fn copy_vreg(&mut self, src: VReg, dst: VReg) {
346
0
        match self {
347
0
            CheckerState::Top => {
348
0
                // Nothing.
349
0
            }
350
0
            CheckerState::Allocations(allocs) => {
351
0
                for value in allocs.values_mut() {
352
0
                    value.copy_vreg(src, dst);
353
0
                }
354
            }
355
        }
356
0
    }
357
358
0
    fn remove_value(&mut self, alloc: &Allocation) {
359
0
        match self {
360
            CheckerState::Top => {
361
0
                panic!("Cannot remove value on Top state");
362
            }
363
0
            CheckerState::Allocations(allocs) => {
364
0
                allocs.remove(alloc);
365
0
            }
366
0
        }
367
0
    }
368
369
0
    fn initial_with_pinned_vregs<F: Function>(f: &F) -> CheckerState {
370
0
        // Scan the function, looking for all vregs that are pinned
371
0
        // vregs, gathering them with their PRegs.
372
0
        let mut pinned_vregs: FxHashMap<VReg, PReg> = FxHashMap::default();
373
0
        visit_all_vregs(f, |vreg: VReg| {
374
0
            if let Some(preg) = f.is_pinned_vreg(vreg) {
375
0
                pinned_vregs.insert(vreg, preg);
376
0
            }
377
0
        });
Unexecuted instantiation: <regalloc2::checker::CheckerState>::initial_with_pinned_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::initial_with_pinned_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::initial_with_pinned_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::initial_with_pinned_vregs::<_>::{closure#0}
378
0
379
0
        let mut allocs = FxHashMap::default();
380
0
        for (vreg, preg) in pinned_vregs {
381
0
            allocs.insert(
382
0
                Allocation::reg(preg),
383
0
                CheckerValue::VRegs(std::iter::once(vreg).collect()),
384
0
            );
385
0
        }
386
387
0
        CheckerState::Allocations(allocs)
388
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerState>::initial_with_pinned_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::initial_with_pinned_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::initial_with_pinned_vregs::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::initial_with_pinned_vregs::<_>
389
}
390
391
impl Default for CheckerState {
392
0
    fn default() -> CheckerState {
393
0
        CheckerState::Top
394
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerState as core::default::Default>::default
Unexecuted instantiation: <regalloc2::checker::CheckerState as core::default::Default>::default
395
}
396
397
impl std::fmt::Display for CheckerValue {
398
0
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
399
0
        match self {
400
            CheckerValue::Universe => {
401
0
                write!(f, "top")
402
            }
403
0
            CheckerValue::VRegs(vregs) => {
404
0
                write!(f, "{{ ")?;
405
0
                for vreg in vregs {
406
0
                    write!(f, "{} ", vreg)?;
407
                }
408
0
                write!(f, "}}")?;
409
0
                Ok(())
410
            }
411
        }
412
0
    }
413
}
414
415
/// Meet function for analysis value: meet individual values at
416
/// matching allocations, and intersect keys (remove key-value pairs
417
/// only on one side). Returns boolean flag indicating whether `into`
418
/// changed.
419
0
fn merge_map<K: Copy + Clone + PartialEq + Eq + Hash>(
420
0
    into: &mut FxHashMap<K, CheckerValue>,
421
0
    from: &FxHashMap<K, CheckerValue>,
422
0
) {
423
0
    into.retain(|k, _| from.contains_key(k));
424
0
    for (k, into_v) in into.iter_mut() {
425
0
        let from_v = from.get(k).unwrap();
426
0
        into_v.meet_with(from_v);
427
0
    }
428
0
}
429
430
impl CheckerState {
431
    /// Create a new checker state.
432
0
    fn new() -> CheckerState {
433
0
        Default::default()
434
0
    }
435
436
    /// Merge this checker state with another at a CFG join-point.
437
0
    fn meet_with(&mut self, other: &CheckerState) {
438
0
        match (self, other) {
439
0
            (_, CheckerState::Top) => {
440
0
                // Nothing.
441
0
            }
442
0
            (this @ CheckerState::Top, _) => {
443
0
                *this = other.clone();
444
0
            }
445
            (
446
0
                CheckerState::Allocations(my_allocations),
447
0
                CheckerState::Allocations(other_allocations),
448
0
            ) => {
449
0
                merge_map(my_allocations, other_allocations);
450
0
            }
451
        }
452
0
    }
453
454
0
    fn check_val<'a, F: Function>(
455
0
        &self,
456
0
        inst: Inst,
457
0
        op: Operand,
458
0
        alloc: Allocation,
459
0
        val: &CheckerValue,
460
0
        allocs: &[Allocation],
461
0
        checker: &Checker<'a, F>,
462
0
    ) -> Result<(), CheckerError> {
463
0
        if alloc == Allocation::none() {
464
0
            return Err(CheckerError::MissingAllocation { inst, op });
465
0
        }
466
0
467
0
        if op.as_fixed_nonallocatable().is_none() {
468
0
            match val {
469
                CheckerValue::Universe => {
470
0
                    return Err(CheckerError::UnknownValueInAllocation { inst, op, alloc });
471
                }
472
0
                CheckerValue::VRegs(vregs) if !vregs.contains(&op.vreg()) => {
473
0
                    return Err(CheckerError::IncorrectValuesInAllocation {
474
0
                        inst,
475
0
                        op,
476
0
                        alloc,
477
0
                        actual: vregs.clone(),
478
0
                    });
479
                }
480
0
                _ => {}
481
            }
482
0
        }
483
484
0
        self.check_constraint(inst, op, alloc, allocs, checker)?;
485
486
0
        Ok(())
487
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check_val::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check_val::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check_val::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check_val::<_>
488
489
    /// Check an instruction against this state. This must be called
490
    /// twice: once with `InstPosition::Before`, and once with
491
    /// `InstPosition::After` (after updating state with defs).
492
0
    fn check<'a, F: Function>(
493
0
        &self,
494
0
        pos: InstPosition,
495
0
        checkinst: &CheckerInst,
496
0
        checker: &Checker<'a, F>,
497
0
    ) -> Result<(), CheckerError> {
498
0
        let default_val = Default::default();
499
0
        match checkinst {
500
            &CheckerInst::Op {
501
0
                inst,
502
0
                ref operands,
503
0
                ref allocs,
504
0
                ..
505
0
            } => {
506
0
                // Skip Use-checks at the After point if there are any
507
0
                // reused inputs: the Def which reuses the input
508
0
                // happens early.
509
0
                let has_reused_input = operands
510
0
                    .iter()
511
0
                    .any(|op| matches!(op.constraint(), OperandConstraint::Reuse(_)));
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<_>::{closure#0}
512
0
                if has_reused_input && pos == InstPosition::After {
513
0
                    return Ok(());
514
0
                }
515
516
                // For each operand, check (i) that the allocation
517
                // contains the expected vreg, and (ii) that it meets
518
                // the requirements of the OperandConstraint.
519
0
                for (op, alloc) in operands.iter().zip(allocs.iter()) {
520
0
                    let is_here = match (op.pos(), pos) {
521
0
                        (OperandPos::Early, InstPosition::Before) => true,
522
0
                        (OperandPos::Late, InstPosition::After) => true,
523
0
                        _ => false,
524
                    };
525
0
                    if !is_here {
526
0
                        continue;
527
0
                    }
528
0
                    if op.kind() == OperandKind::Def {
529
0
                        continue;
530
0
                    }
531
0
532
0
                    let val = self.get_value(alloc).unwrap_or(&default_val);
533
0
                    trace!(
534
0
                        "checker: checkinst {:?}: op {:?}, alloc {:?}, checker value {:?}",
535
                        checkinst,
536
                        op,
537
                        alloc,
538
                        val
539
                    );
540
0
                    self.check_val(inst, *op, *alloc, val, allocs, checker)?;
541
                }
542
            }
543
0
            &CheckerInst::Safepoint { inst, ref allocs } => {
544
0
                for &alloc in allocs {
545
0
                    let val = self.get_value(&alloc).unwrap_or(&default_val);
546
0
                    trace!(
547
0
                        "checker: checkinst {:?}: safepoint slot {}, checker value {:?}",
548
                        checkinst,
549
                        alloc,
550
                        val
551
                    );
552
553
0
                    let reffy = val
554
0
                        .vregs()
555
0
                        .expect("checker value should not be Universe set")
556
0
                        .iter()
557
0
                        .any(|vreg| checker.reftyped_vregs.contains(vreg));
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>::{closure#1}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>::{closure#1}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>::{closure#1}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<_>::{closure#1}
558
0
                    if !reffy {
559
0
                        return Err(CheckerError::NonRefValuesInStackmap {
560
0
                            inst,
561
0
                            alloc,
562
0
                            vregs: val.vregs().unwrap().clone(),
563
0
                        });
564
0
                    }
565
                }
566
            }
567
0
            &CheckerInst::Move { into, from } => {
568
0
                // Ensure that the allocator never returns stack-to-stack moves.
569
0
                let is_stack = |alloc: Allocation| {
570
0
                    if let Some(reg) = alloc.as_reg() {
571
0
                        checker.stack_pregs.contains(reg)
572
                    } else {
573
0
                        alloc.is_stack()
574
                    }
575
0
                };
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>::{closure#2}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>::{closure#2}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>::{closure#2}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<_>::{closure#2}
576
0
                if is_stack(into) && is_stack(from) {
577
0
                    return Err(CheckerError::StackToStackMove { into, from });
578
0
                }
579
            }
580
0
            &CheckerInst::ParallelMove { .. } => {
581
0
                // This doesn't need verification; we just update
582
0
                // according to the move semantics in the step
583
0
                // function below.
584
0
            }
585
0
            &CheckerInst::ProgramMove { inst, src, dst: _ } => {
586
                // Validate that the fixed-reg constraint, if any, on
587
                // `src` is satisfied.
588
0
                if let OperandConstraint::FixedReg(preg) = src.constraint() {
589
0
                    let alloc = Allocation::reg(preg);
590
0
                    let val = self.get_value(&alloc).unwrap_or(&default_val);
591
0
                    trace!(
592
0
                        "checker: checkinst {:?}: cheker value in {:?} is {:?}",
593
                        checkinst,
594
                        alloc,
595
                        val
596
                    );
597
0
                    self.check_val(inst, src, alloc, val, &[alloc], checker)?;
598
0
                }
599
                // Note that we don't do anything with `dst`
600
                // here. That is implicitly checked whenever `dst` is
601
                // used; the `update()` step below adds the symbolic
602
                // vreg for `dst` into wherever `src` may be stored.
603
            }
604
        }
605
0
        Ok(())
606
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check::<_>
607
608
    /// Update according to instruction.
609
0
    fn update<'a, F: Function>(&mut self, checkinst: &CheckerInst, checker: &Checker<'a, F>) {
610
0
        self.become_defined();
611
0
612
0
        match checkinst {
613
0
            &CheckerInst::Move { into, from } => {
614
                // Value may not be present if this move is part of
615
                // the parallel move resolver's fallback sequence that
616
                // saves a victim register elsewhere. (In other words,
617
                // that sequence saves an undefined value and restores
618
                // it, so has no effect.) The checker needs to avoid
619
                // putting Universe lattice values into the map.
620
0
                if let Some(val) = self.get_value(&from).cloned() {
621
0
                    trace!(
622
0
                        "checker: checkinst {:?} updating: move {:?} -> {:?} val {:?}",
623
                        checkinst,
624
                        from,
625
                        into,
626
                        val
627
                    );
628
0
                    self.set_value(into, val);
629
0
                }
630
            }
631
0
            &CheckerInst::ParallelMove { ref moves } => {
632
0
                // First, build map of actions for each vreg in an
633
0
                // alloc. If an alloc has a reg V_i before a parallel
634
0
                // move, then for each use of V_i as a source (V_i ->
635
0
                // V_j), we might add a new V_j wherever V_i appears;
636
0
                // and if V_i is used as a dest (at most once), then
637
0
                // it must be removed first from allocs' vreg sets.
638
0
                let mut additions: FxHashMap<VReg, SmallVec<[VReg; 2]>> = FxHashMap::default();
639
0
                let mut deletions: FxHashSet<VReg> = FxHashSet::default();
640
641
0
                for &(dest, src) in moves {
642
0
                    deletions.insert(dest);
643
0
                    additions
644
0
                        .entry(src)
645
0
                        .or_insert_with(|| smallvec![])
Unexecuted instantiation: <regalloc2::checker::CheckerState>::update::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::update::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::update::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>::{closure#0}
Unexecuted instantiation: <regalloc2::checker::CheckerState>::update::<_>::{closure#0}
646
0
                        .push(dest);
647
0
                }
648
649
                // Now process each allocation's set of vreg labels,
650
                // first deleting those labels that were updated by
651
                // this parallel move, then adding back labels
652
                // redefined by the move.
653
0
                for value in self.get_values_mut() {
654
0
                    if let Some(vregs) = value.vregs_mut() {
655
0
                        let mut insertions: SmallVec<[VReg; 2]> = smallvec![];
656
0
                        for &vreg in vregs.iter() {
657
0
                            if let Some(additions) = additions.get(&vreg) {
658
0
                                insertions.extend(additions.iter().cloned());
659
0
                            }
660
                        }
661
0
                        for &d in &deletions {
662
0
                            vregs.remove(&d);
663
0
                        }
664
0
                        vregs.extend(insertions);
665
0
                    }
666
                }
667
            }
668
            &CheckerInst::Op {
669
0
                ref operands,
670
0
                ref allocs,
671
0
                ref clobbers,
672
                ..
673
            } => {
674
                // For each def, (i) update alloc to reflect defined
675
                // vreg (and only that vreg), and (ii) update all
676
                // other allocs in the checker state by removing this
677
                // vreg, if defined (other defs are now stale).
678
0
                for (op, alloc) in operands.iter().zip(allocs.iter()) {
679
0
                    if op.kind() != OperandKind::Def {
680
0
                        continue;
681
0
                    }
682
0
                    self.remove_vreg(op.vreg());
683
0
                    self.set_value(*alloc, CheckerValue::from_reg(op.vreg()));
684
                }
685
0
                for clobber in clobbers {
686
0
                    self.remove_value(&Allocation::reg(*clobber));
687
0
                }
688
            }
689
0
            &CheckerInst::Safepoint { ref allocs, .. } => {
690
0
                for (alloc, value) in self.get_mappings_mut() {
691
0
                    if alloc.is_reg() {
692
0
                        continue;
693
0
                    }
694
0
                    if !allocs.contains(&alloc) {
695
0
                        // Remove all reftyped vregs as labels.
696
0
                        let new_vregs = value
697
0
                            .vregs()
698
0
                            .unwrap()
699
0
                            .difference(&checker.reftyped_vregs)
700
0
                            .cloned()
701
0
                            .collect();
702
0
                        *value = CheckerValue::VRegs(new_vregs);
703
0
                    }
704
                }
705
            }
706
0
            &CheckerInst::ProgramMove { inst: _, src, dst } => {
707
0
                // Remove all earlier instances of `dst`: this vreg is
708
0
                // now stale (it is being overwritten).
709
0
                self.remove_vreg(dst.vreg());
710
                // Define `dst` wherever `src` occurs.
711
0
                for (_, value) in self.get_mappings_mut() {
712
0
                    value.copy_vreg(src.vreg(), dst.vreg());
713
0
                }
714
            }
715
        }
716
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerState>::update::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::update::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::update::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::update::<_>
717
718
0
    fn remove_vreg(&mut self, vreg: VReg) {
719
0
        for (_, value) in self.get_mappings_mut() {
720
0
            value.remove_vreg(vreg);
721
0
        }
722
0
    }
723
724
0
    fn check_constraint<'a, F: Function>(
725
0
        &self,
726
0
        inst: Inst,
727
0
        op: Operand,
728
0
        alloc: Allocation,
729
0
        allocs: &[Allocation],
730
0
        checker: &Checker<'a, F>,
731
0
    ) -> Result<(), CheckerError> {
732
0
        match op.constraint() {
733
0
            OperandConstraint::Any => {}
734
            OperandConstraint::Reg => {
735
0
                if let Some(preg) = alloc.as_reg() {
736
                    // Reject pregs that represent a fixed stack slot.
737
0
                    if !checker.machine_env.fixed_stack_slots.contains(&preg) {
738
0
                        return Ok(());
739
0
                    }
740
0
                }
741
0
                return Err(CheckerError::AllocationIsNotReg { inst, op, alloc });
742
            }
743
            OperandConstraint::Stack => {
744
0
                if alloc.kind() != AllocationKind::Stack {
745
                    // Accept pregs that represent a fixed stack slot.
746
0
                    if let Some(preg) = alloc.as_reg() {
747
0
                        if checker.machine_env.fixed_stack_slots.contains(&preg) {
748
0
                            return Ok(());
749
0
                        }
750
0
                    }
751
0
                    return Err(CheckerError::AllocationIsNotStack { inst, op, alloc });
752
0
                }
753
            }
754
0
            OperandConstraint::FixedReg(preg) => {
755
0
                if alloc != Allocation::reg(preg) {
756
0
                    return Err(CheckerError::AllocationIsNotFixedReg { inst, op, alloc });
757
0
                }
758
            }
759
0
            OperandConstraint::Reuse(idx) => {
760
0
                if alloc.kind() != AllocationKind::Reg {
761
0
                    return Err(CheckerError::AllocationIsNotReg { inst, op, alloc });
762
0
                }
763
0
                if alloc != allocs[idx] {
764
0
                    return Err(CheckerError::AllocationIsNotReuse {
765
0
                        inst,
766
0
                        op,
767
0
                        alloc,
768
0
                        expected_alloc: allocs[idx],
769
0
                    });
770
0
                }
771
            }
772
        }
773
0
        Ok(())
774
0
    }
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check_constraint::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check_constraint::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check_constraint::<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>
Unexecuted instantiation: <regalloc2::checker::CheckerState>::check_constraint::<_>
775
}
776
777
/// An instruction representation in the checker's BB summary.
778
#[derive(Clone, Debug)]
779
pub(crate) enum CheckerInst {
780
    /// A move between allocations (these could be registers or
781
    /// spillslots).
782
    Move { into: Allocation, from: Allocation },
783
784
    /// A parallel move in the original program. Simultaneously moves
785
    /// from all source vregs to all corresponding dest vregs,
786
    /// permitting overlap in the src and dest sets and doing all
787
    /// reads before any writes.
788
    ParallelMove {
789
        /// Vector of (dest, src) moves.
790
        moves: Vec<(VReg, VReg)>,
791
    },
792
793
    /// A regular instruction with fixed use and def slots. Contains
794
    /// both the original operands (as given to the regalloc) and the
795
    /// allocation results.
796
    Op {
797
        inst: Inst,
798
        operands: Vec<Operand>,
799
        allocs: Vec<Allocation>,
800
        clobbers: Vec<PReg>,
801
    },
802
803
    /// A safepoint, with the given Allocations specified as containing
804
    /// reftyped values. All other reftyped values become invalid.
805
    Safepoint { inst: Inst, allocs: Vec<Allocation> },
806
807
    /// An op with one source operand, and one dest operand, that
808
    /// copies any symbolic values from the source to the dest, in
809
    /// addition to adding the symbolic value of the dest vreg to the
810
    /// set. This "program move" is distinguished from the above
811
    /// `Move` by being semantically relevant in the original
812
    /// (pre-regalloc) program.
813
    ///
814
    /// We transform checker values as follows: for any vreg-set that
815
    /// contains `dst`'s vreg, we first delete that vreg (because it
816
    /// is being redefined). Then, for any vreg-set with `src`
817
    /// present, we add `dst`.
818
    ProgramMove {
819
        inst: Inst,
820
        src: Operand,
821
        dst: Operand,
822
    },
823
}
824
825
#[derive(Debug)]
826
pub struct Checker<'a, F: Function> {
827
    f: &'a F,
828
    bb_in: FxHashMap<Block, CheckerState>,
829
    bb_insts: FxHashMap<Block, Vec<CheckerInst>>,
830
    edge_insts: FxHashMap<(Block, Block), Vec<CheckerInst>>,
831
    reftyped_vregs: FxHashSet<VReg>,
832
    machine_env: &'a MachineEnv,
833
    stack_pregs: PRegSet,
834
}
835
836
impl<'a, F: Function> Checker<'a, F> {
837
    /// Create a new checker for the given function, initializing CFG
838
    /// info immediately.  The client should call the `add_*()`
839
    /// methods to add abstract instructions to each BB before
840
    /// invoking `run()` to check for errors.
841
0
    pub fn new(f: &'a F, machine_env: &'a MachineEnv) -> Checker<'a, F> {
842
0
        let mut bb_in = FxHashMap::default();
843
0
        let mut bb_insts = FxHashMap::default();
844
0
        let mut edge_insts = FxHashMap::default();
845
0
        let mut reftyped_vregs = FxHashSet::default();
846
847
0
        for block in 0..f.num_blocks() {
848
0
            let block = Block::new(block);
849
0
            bb_in.insert(block, Default::default());
850
0
            bb_insts.insert(block, vec![]);
851
0
            for &succ in f.block_succs(block) {
852
0
                edge_insts.insert((block, succ), vec![]);
853
0
            }
854
        }
855
856
0
        for &vreg in f.reftype_vregs() {
857
0
            reftyped_vregs.insert(vreg);
858
0
        }
859
860
0
        bb_in.insert(f.entry_block(), CheckerState::initial_with_pinned_vregs(f));
861
0
862
0
        let mut stack_pregs = PRegSet::empty();
863
0
        for &preg in &machine_env.fixed_stack_slots {
864
0
            stack_pregs.add(preg);
865
0
        }
866
867
0
        Checker {
868
0
            f,
869
0
            bb_in,
870
0
            bb_insts,
871
0
            edge_insts,
872
0
            reftyped_vregs,
873
0
            machine_env,
874
0
            stack_pregs,
875
0
        }
876
0
    }
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::new
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::new
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::new
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::new
877
878
    /// Build the list of checker instructions based on the given func
879
    /// and allocation results.
880
0
    pub fn prepare(&mut self, out: &Output) {
881
0
        trace!("checker: out = {:?}", out);
882
        // Preprocess safepoint stack-maps into per-inst vecs.
883
0
        let mut safepoint_slots: FxHashMap<Inst, Vec<Allocation>> = FxHashMap::default();
884
0
        for &(progpoint, slot) in &out.safepoint_slots {
885
0
            safepoint_slots
886
0
                .entry(progpoint.inst())
887
0
                .or_insert_with(|| vec![])
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::prepare::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::prepare::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::prepare::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::prepare::{closure#0}
888
0
                .push(slot);
889
0
        }
890
891
0
        let mut last_inst = None;
892
0
        for block in 0..self.f.num_blocks() {
893
0
            let block = Block::new(block);
894
0
            for inst_or_edit in out.block_insts_and_edits(self.f, block) {
895
0
                match inst_or_edit {
896
0
                    InstOrEdit::Inst(inst) => {
897
0
                        debug_assert!(last_inst.is_none() || inst > last_inst.unwrap());
898
0
                        last_inst = Some(inst);
899
0
                        self.handle_inst(block, inst, &mut safepoint_slots, out);
900
                    }
901
0
                    InstOrEdit::Edit(edit) => self.handle_edit(block, edit),
902
                }
903
            }
904
        }
905
0
    }
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::prepare
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::prepare
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::prepare
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::prepare
906
907
    /// For each original instruction, create an `Op`.
908
0
    fn handle_inst(
909
0
        &mut self,
910
0
        block: Block,
911
0
        inst: Inst,
912
0
        safepoint_slots: &mut FxHashMap<Inst, Vec<Allocation>>,
913
0
        out: &Output,
914
0
    ) {
915
0
        // If this is a safepoint, then check the spillslots at this point.
916
0
        if self.f.requires_refs_on_stack(inst) {
917
0
            let allocs = safepoint_slots.remove(&inst).unwrap_or_else(|| vec![]);
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::handle_inst::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::handle_inst::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::handle_inst::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::handle_inst::{closure#0}
918
0
919
0
            let checkinst = CheckerInst::Safepoint { inst, allocs };
920
0
            self.bb_insts.get_mut(&block).unwrap().push(checkinst);
921
0
        }
922
923
        // If this is a move, handle specially. Note that the
924
        // regalloc2-inserted moves are not semantically present in
925
        // the original program and so do not modify the sets of
926
        // symbolic values at all, but rather just move them around;
927
        // but "program moves" *are* present, and have the following
928
        // semantics: they define the destination vreg, but also
929
        // retain any symbolic values in the source.
930
        //
931
        // regalloc2 reifies all moves into edits in its unified
932
        // move/edit framework, so we don't get allocs for these moves
933
        // in the post-regalloc output, and the embedder is not
934
        // supposed to emit the moves. But we *do* want to check the
935
        // semantic implications, namely definition of new vregs and,
936
        // for moves to/from pinned vregs, the implied register
937
        // constraints. So we emit `ProgramMove` ops that do just
938
        // this.
939
0
        if let Some((src, dst)) = self.f.is_move(inst) {
940
0
            let src_preg = self.f.is_pinned_vreg(src.vreg());
941
0
            let src_op = match src_preg {
942
0
                Some(preg) => Operand::reg_fixed_use(src.vreg(), preg),
943
0
                None => Operand::any_use(src.vreg()),
944
            };
945
0
            let dst_preg = self.f.is_pinned_vreg(dst.vreg());
946
0
            let dst_op = match dst_preg {
947
0
                Some(preg) => Operand::reg_fixed_def(dst.vreg(), preg),
948
0
                None => Operand::any_def(dst.vreg()),
949
            };
950
0
            let checkinst = CheckerInst::ProgramMove {
951
0
                inst,
952
0
                src: src_op,
953
0
                dst: dst_op,
954
0
            };
955
0
            trace!("checker: adding inst {:?}", checkinst);
956
0
            self.bb_insts.get_mut(&block).unwrap().push(checkinst);
957
        }
958
        // Skip normal checks if this is a branch: the blockparams do
959
        // not exist in post-regalloc code, and the edge-moves have to
960
        // be inserted before the branch rather than after.
961
0
        else if !self.f.is_branch(inst) {
962
0
            let operands: Vec<_> = self.f.inst_operands(inst).iter().cloned().collect();
963
0
            let allocs: Vec<_> = out.inst_allocs(inst).iter().cloned().collect();
964
0
            let clobbers: Vec<_> = self.f.inst_clobbers(inst).into_iter().collect();
965
0
            let checkinst = CheckerInst::Op {
966
0
                inst,
967
0
                operands,
968
0
                allocs,
969
0
                clobbers,
970
0
            };
971
0
            trace!("checker: adding inst {:?}", checkinst);
972
0
            self.bb_insts.get_mut(&block).unwrap().push(checkinst);
973
        }
974
        // Instead, if this is a branch, emit a ParallelMove on each
975
        // outgoing edge as necessary to handle blockparams.
976
        else {
977
0
            for (i, &succ) in self.f.block_succs(block).iter().enumerate() {
978
0
                let args = self.f.branch_blockparams(block, inst, i);
979
0
                let params = self.f.block_params(succ);
980
0
                assert_eq!(
981
0
                    args.len(),
982
0
                    params.len(),
983
0
                    "block{} has succ block{}; gave {} args for {} params",
984
0
                    block.index(),
985
0
                    succ.index(),
986
0
                    args.len(),
987
0
                    params.len()
988
                );
989
0
                if args.len() > 0 {
990
0
                    let moves = params.iter().cloned().zip(args.iter().cloned()).collect();
991
0
                    self.edge_insts
992
0
                        .get_mut(&(block, succ))
993
0
                        .unwrap()
994
0
                        .push(CheckerInst::ParallelMove { moves });
995
0
                }
996
            }
997
        }
998
0
    }
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::handle_inst
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::handle_inst
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::handle_inst
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::handle_inst
999
1000
0
    fn handle_edit(&mut self, block: Block, edit: &Edit) {
1001
0
        trace!("checker: adding edit {:?}", edit);
1002
0
        match edit {
1003
0
            &Edit::Move { from, to } => {
1004
0
                self.bb_insts
1005
0
                    .get_mut(&block)
1006
0
                    .unwrap()
1007
0
                    .push(CheckerInst::Move { into: to, from });
1008
0
            }
1009
0
        }
1010
0
    }
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::handle_edit
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::handle_edit
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::handle_edit
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::handle_edit
1011
1012
    /// Perform the dataflow analysis to compute checker state at each BB entry.
1013
0
    fn analyze(&mut self) {
1014
0
        let mut queue = Vec::new();
1015
0
        let mut queue_set = FxHashSet::default();
1016
0
1017
0
        queue.push(self.f.entry_block());
1018
0
        queue_set.insert(self.f.entry_block());
1019
1020
0
        while !queue.is_empty() {
1021
0
            let block = queue.pop().unwrap();
1022
0
            queue_set.remove(&block);
1023
0
            let mut state = self.bb_in.get(&block).cloned().unwrap();
1024
0
            trace!("analyze: block {} has state {:?}", block.index(), state);
1025
0
            for inst in self.bb_insts.get(&block).unwrap() {
1026
0
                state.update(inst, self);
1027
0
                trace!("analyze: inst {:?} -> state {:?}", inst, state);
1028
            }
1029
1030
0
            for &succ in self.f.block_succs(block) {
1031
0
                let mut new_state = state.clone();
1032
0
                for edge_inst in self.edge_insts.get(&(block, succ)).unwrap() {
1033
0
                    new_state.update(edge_inst, self);
1034
0
                    trace!(
1035
0
                        "analyze: succ {:?}: inst {:?} -> state {:?}",
1036
                        succ,
1037
                        edge_inst,
1038
                        new_state
1039
                    );
1040
                }
1041
1042
0
                let cur_succ_in = self.bb_in.get(&succ).unwrap();
1043
0
                trace!(
1044
0
                    "meeting state {:?} for block {} with state {:?} for block {}",
1045
0
                    new_state,
1046
0
                    block.index(),
1047
0
                    cur_succ_in,
1048
0
                    succ.index()
1049
                );
1050
0
                new_state.meet_with(cur_succ_in);
1051
0
                let changed = &new_state != cur_succ_in;
1052
0
                trace!(" -> {:?}, changed {}", new_state, changed);
1053
1054
0
                if changed {
1055
0
                    trace!(
1056
0
                        "analyze: block {} state changed from {:?} to {:?}; pushing onto queue",
1057
0
                        succ.index(),
1058
                        cur_succ_in,
1059
                        new_state
1060
                    );
1061
0
                    self.bb_in.insert(succ, new_state);
1062
0
                    if !queue_set.contains(&succ) {
1063
0
                        queue.push(succ);
1064
0
                        queue_set.insert(succ);
1065
0
                    }
1066
0
                }
1067
            }
1068
        }
1069
0
    }
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::analyze
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::analyze
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::analyze
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::analyze
1070
1071
    /// Using BB-start state computed by `analyze()`, step the checker state
1072
    /// through each BB and check each instruction's register allocations
1073
    /// for errors.
1074
0
    fn find_errors(&self) -> Result<(), CheckerErrors> {
1075
0
        let mut errors = vec![];
1076
0
        for (block, input) in &self.bb_in {
1077
0
            let mut state = input.clone();
1078
0
            for inst in self.bb_insts.get(block).unwrap() {
1079
0
                if let Err(e) = state.check(InstPosition::Before, inst, self) {
1080
0
                    trace!("Checker error: {:?}", e);
1081
0
                    errors.push(e);
1082
0
                }
1083
0
                state.update(inst, self);
1084
0
                if let Err(e) = state.check(InstPosition::After, inst, self) {
1085
0
                    trace!("Checker error: {:?}", e);
1086
0
                    errors.push(e);
1087
0
                }
1088
            }
1089
        }
1090
1091
0
        if errors.is_empty() {
1092
0
            Ok(())
1093
        } else {
1094
0
            Err(CheckerErrors { errors })
1095
        }
1096
0
    }
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::find_errors
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::find_errors
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::find_errors
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::find_errors
1097
1098
    /// Find any errors, returning `Err(CheckerErrors)` with all errors found
1099
    /// or `Ok(())` otherwise.
1100
0
    pub fn run(mut self) -> Result<(), CheckerErrors> {
1101
0
        self.analyze();
1102
0
        let result = self.find_errors();
1103
0
1104
0
        trace!("=== CHECKER RESULT ===");
1105
0
        fn print_state(state: &CheckerState) {
1106
0
            if let CheckerState::Allocations(allocs) = state {
1107
0
                let mut s = vec![];
1108
0
                for (alloc, state) in allocs {
1109
0
                    s.push(format!("{} := {}", alloc, state));
1110
0
                }
1111
0
                trace!("    {{ {} }}", s.join(", "))
1112
0
            }
1113
0
        }
1114
0
        for vreg in self.f.reftype_vregs() {
1115
0
            trace!("  REF: {}", vreg);
1116
        }
1117
0
        for bb in 0..self.f.num_blocks() {
1118
0
            let bb = Block::new(bb);
1119
0
            trace!("block{}:", bb.index());
1120
0
            let insts = self.bb_insts.get(&bb).unwrap();
1121
0
            let mut state = self.bb_in.get(&bb).unwrap().clone();
1122
0
            print_state(&state);
1123
0
            for inst in insts {
1124
0
                match inst {
1125
                    &CheckerInst::Op {
1126
0
                        inst,
1127
0
                        ref operands,
1128
0
                        ref allocs,
1129
0
                        ref clobbers,
1130
0
                    } => {
1131
0
                        trace!(
1132
0
                            "  inst{}: {:?} ({:?}) clobbers:{:?}",
1133
0
                            inst.index(),
1134
                            operands,
1135
                            allocs,
1136
                            clobbers
1137
                        );
1138
                    }
1139
0
                    &CheckerInst::Move { from, into } => {
1140
0
                        trace!("    {} -> {}", from, into);
1141
                    }
1142
0
                    &CheckerInst::Safepoint { ref allocs, .. } => {
1143
0
                        let mut slotargs = vec![];
1144
0
                        for &slot in allocs {
1145
0
                            slotargs.push(format!("{}", slot));
1146
0
                        }
1147
0
                        trace!("    safepoint: {}", slotargs.join(", "));
1148
                    }
1149
0
                    &CheckerInst::ProgramMove { inst, src, dst } => {
1150
0
                        trace!("    inst{}: prog_move {} -> {}", inst.index(), src, dst);
1151
                    }
1152
                    &CheckerInst::ParallelMove { .. } => {
1153
0
                        panic!("unexpected parallel_move in body (non-edge)")
1154
                    }
1155
                }
1156
0
                state.update(inst, &self);
1157
0
                print_state(&state);
1158
            }
1159
1160
0
            for &succ in self.f.block_succs(bb) {
1161
0
                trace!("  succ {:?}:", succ);
1162
0
                let mut state = state.clone();
1163
0
                for edge_inst in self.edge_insts.get(&(bb, succ)).unwrap() {
1164
0
                    match edge_inst {
1165
0
                        &CheckerInst::ParallelMove { ref moves } => {
1166
0
                            let moves = moves
1167
0
                                .iter()
1168
0
                                .map(|(dest, src)| format!("{} -> {}", src, dest))
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::run::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::run::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::run::{closure#0}
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::run::{closure#0}
1169
0
                                .collect::<Vec<_>>();
1170
0
                            trace!("    parallel_move {}", moves.join(", "));
1171
                        }
1172
0
                        _ => panic!("unexpected edge_inst: not a parallel move"),
1173
                    }
1174
0
                    state.update(edge_inst, &self);
1175
0
                    print_state(&state);
1176
                }
1177
            }
1178
        }
1179
1180
0
        result
1181
0
    }
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::x64::lower::isle::generated_code::MInst>>>::run
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::aarch64::lower::isle::generated_code::MInst>>>::run
Unexecuted instantiation: <regalloc2::checker::Checker<cranelift_codegen::machinst::vcode::VCode<cranelift_codegen::isa::riscv64::lower::isle::generated_code::MInst>>>::run
Unexecuted instantiation: <regalloc2::checker::Checker<_>>::run
1182
}