Coverage Report

Created: 2026-04-09 08:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/regalloc2/src/ssa.rs
Line
Count
Source
1
/*
2
 * Released under the terms of the Apache 2.0 license with LLVM
3
 * exception. See `LICENSE` for details.
4
 */
5
6
//! SSA-related utilities.
7
8
use alloc::vec;
9
10
use crate::cfg::CFGInfo;
11
use crate::{Block, Function, FxHashSet, Inst, OperandKind, RegAllocError, VReg};
12
13
0
pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllocError> {
14
    // For every block param and inst def, check that this is the only def.
15
0
    let mut defined_in = vec![Block::invalid(); f.num_vregs()];
16
0
    for block in 0..f.num_blocks() {
17
0
        let block = Block::new(block);
18
0
        let mut def = |vreg: VReg, inst| {
19
0
            if vreg.vreg() >= defined_in.len() {
20
0
                trace!("VRegs not numbered consecutively {:?}", vreg);
21
0
                return Err(RegAllocError::SSA(vreg, inst));
22
0
            }
23
0
            if defined_in[vreg.vreg()].is_valid() {
24
0
                trace!("Multiple def constraints for {:?}", vreg);
25
0
                Err(RegAllocError::SSA(vreg, inst))
26
            } else {
27
0
                defined_in[vreg.vreg()] = block;
28
0
                Ok(())
29
            }
30
0
        };
31
0
        for &param in f.block_params(block) {
32
0
            def(param, Inst::invalid())?;
33
        }
34
0
        for inst in f.block_insns(block).iter() {
35
0
            for operand in f.inst_operands(inst) {
36
0
                if let OperandKind::Def = operand.kind() {
37
0
                    def(operand.vreg(), inst)?;
38
0
                }
39
            }
40
        }
41
    }
42
43
    // Walk the blocks in arbitrary order. Check, for every use, that
44
    // the def is either in the same block in an earlier inst, or is
45
    // defined (by inst or blockparam) in some other block that
46
    // dominates this one.
47
0
    let mut local = FxHashSet::default();
48
0
    for block in 0..f.num_blocks() {
49
0
        let block = Block::new(block);
50
0
        local.clear();
51
0
        local.extend(f.block_params(block));
52
53
0
        for iix in f.block_insns(block).iter() {
54
0
            let operands = f.inst_operands(iix);
55
0
            for operand in operands {
56
                // Fixed registers uses will likely not be SSA, but they also
57
                // won't receive assignments.
58
0
                if operand.as_fixed_nonallocatable().is_some() {
59
0
                    continue;
60
0
                }
61
62
0
                match operand.kind() {
63
                    OperandKind::Use => {
64
0
                        let def_block = defined_in[operand.vreg().vreg()];
65
0
                        let okay = def_block.is_valid()
66
0
                            && if def_block == block {
67
0
                                local.contains(&operand.vreg())
68
                            } else {
69
0
                                cfginfo.dominates(def_block, block)
70
                            };
71
0
                        if !okay {
72
0
                            trace!("Invalid use {:?}", operand.vreg());
73
0
                            return Err(RegAllocError::SSA(operand.vreg(), iix));
74
0
                        }
75
                    }
76
0
                    OperandKind::Def => {
77
0
                        // Check all the uses in this instruction
78
0
                        // first, before recording its defs below.
79
0
                    }
80
                }
81
            }
82
83
            // In SSA form, an instruction can't use a VReg that it
84
            // also defines. So only record this instruction's defs
85
            // after its uses have been checked.
86
0
            for operand in operands {
87
0
                if let OperandKind::Def = operand.kind() {
88
0
                    local.insert(operand.vreg());
89
0
                }
90
            }
91
        }
92
    }
93
94
    // Check that the length of branch args matches the sum of the
95
    // number of blockparams in their succs, and that the end of every
96
    // block ends in this branch or in a ret, and that there are no
97
    // other branches or rets in the middle of the block.
98
0
    for block in 0..f.num_blocks() {
99
0
        let block = Block::new(block);
100
0
        let insns = f.block_insns(block);
101
0
        for insn in insns.iter() {
102
0
            if insn == insns.last() {
103
0
                if !(f.is_branch(insn) || f.is_ret(insn)) {
104
0
                    trace!("block {:?} is not terminated by a branch or ret!", block);
105
0
                    return Err(RegAllocError::BB(block));
106
0
                }
107
0
                if f.is_branch(insn) {
108
0
                    for (i, &succ) in f.block_succs(block).iter().enumerate() {
109
0
                        let blockparams_in = f.block_params(succ);
110
0
                        let blockparams_out = f.branch_blockparams(block, insn, i);
111
0
                        if blockparams_in.len() != blockparams_out.len() {
112
0
                            trace!(
113
                                "Mismatch on block params, found {} expected {}",
114
0
                                blockparams_out.len(),
115
0
                                blockparams_in.len()
116
                            );
117
0
                            return Err(RegAllocError::Branch(insn));
118
0
                        }
119
                    }
120
0
                }
121
            } else {
122
0
                if f.is_branch(insn) || f.is_ret(insn) {
123
0
                    trace!("Block terminator found in the middle of a block");
124
0
                    return Err(RegAllocError::BB(block));
125
0
                }
126
            }
127
        }
128
    }
129
130
    // Check that the entry block has no block args: otherwise it is
131
    // undefined what their value would be.
132
0
    if f.block_params(f.entry_block()).len() > 0 {
133
0
        trace!("Entry block contains block args");
134
0
        return Err(RegAllocError::BB(f.entry_block()));
135
0
    }
136
137
0
    Ok(())
138
0
}