Coverage Report

Created: 2021-03-22 08:29

/src/regalloc.rs/bin/validator.rs
Line
Count
Source (jump to first uncovered line)
1
use crate::test_framework::*;
2
use regalloc::*;
3
use std::collections::{HashMap, HashSet};
4
5
#[derive(PartialEq, Debug)]
6
pub enum RegRef {
7
    Use,
8
    Def,
9
}
10
11
pub struct Context<'rru> {
12
    pub num_vregs: usize,
13
    pub num_blocks: u32,
14
    real_reg_universe: &'rru RealRegUniverse,
15
    vreg_types: HashMap<usize, RegClass>,
16
    used_regs: HashSet<Reg>,
17
}
18
19
impl<'rru> Context<'rru> {
20
    fn new(func: &Func, real_reg_universe: &'rru RealRegUniverse) -> Self {
21
        Self {
22
            num_vregs: func.num_virtual_regs as usize,
23
            real_reg_universe,
24
            num_blocks: func.blocks.len(),
25
            vreg_types: HashMap::new(),
26
            used_regs: HashSet::new(),
27
        }
28
    }
29
30
0
    pub fn check_reg(&mut self, reg: Reg, reg_ref: RegRef) -> bool {
31
0
        let rc = reg.get_class();
32
0
        let index = reg.get_index();
33
0
34
0
        if !self.used_regs.contains(&reg) {
35
0
            if reg_ref == RegRef::Use {
36
                // Use before def.
37
0
                return false;
38
0
            }
39
0
            self.used_regs.insert(reg);
40
0
        }
41
42
0
        if reg.is_virtual() {
43
            // If the register has been mentioned earlier, check that it didn't change type in the
44
            // meanwhile.
45
0
            if let Some(prev_rc) = self.vreg_types.insert(index, rc) {
46
0
                if prev_rc != rc {
47
0
                    return false;
48
0
                }
49
0
            }
50
51
            // If it's virtual, it must be in range.
52
0
            index < self.num_vregs
53
        } else {
54
            debug_assert!(reg.is_real());
55
            // If it's real, it must:
56
            // - exist in the array of real registers,
57
            // - have the same encoding as in the real register universe,
58
            // - be in the range of its register class.
59
0
            if index >= self.real_reg_universe.regs.len() {
60
0
                return false;
61
0
            }
62
0
            if self.real_reg_universe.regs[index].0 != reg.as_real_reg().unwrap() {
63
0
                return false;
64
0
            }
65
0
            match self.real_reg_universe.allocable_by_class[rc as usize] {
66
0
                Some(ref reg_info) => index >= reg_info.first && index <= reg_info.last,
67
0
                None => false,
68
            }
69
        }
70
0
    }
71
72
    pub fn check_reg_rc(&mut self, reg: &Reg, reg_ref: RegRef, expected_class: RegClass) -> bool {
73
        reg.get_class() == expected_class && self.check_reg(*reg, reg_ref)
74
    }
75
}
76
77
0
pub fn validate(func: &Func, real_reg_universe: &RealRegUniverse) -> Result<(), String> {
78
0
    let mut cx = Context::new(func, real_reg_universe);
79
0
80
0
    // Function entry must exist and point to a valid block.
81
0
    match &func.entry {
82
0
        None => {
83
0
            return Err("missing entry label".into());
84
        }
85
0
        Some(label) => {
86
0
            if !label.type_checks(&cx) {
87
0
                return Err("invalid or unresolved entry label".into());
88
0
            }
89
        }
90
    };
91
92
    // Blocks must not be empty.
93
0
    for b in func.blocks.iter() {
94
0
        if b.len == 0 {
95
0
            return Err(format!("block {} is empty", b.name));
96
0
        }
97
0
        if b.start.get().checked_add(b.len).is_none() {
98
0
            return Err(format!("too many instructions in block {}", b.name));
99
0
        }
100
    }
101
102
0
    if func.blocks[BlockIx::new(0)].start.get() != 0 {
103
0
        return Err(format!("first block must start at first instruction"));
104
0
    }
105
0
106
0
    let last_block = &func.blocks[BlockIx::new(func.blocks.len() - 1)];
107
0
    if func.insns.len() != last_block.start.get() + last_block.len {
108
0
        return Err(format!("unused instructions"));
109
0
    }
110
111
    // Check that blocks are ordered in increasing block start.
112
0
    for i in 1..func.blocks.len() {
113
0
        let prev = BlockIx::new(i - 1);
114
0
        let cur = BlockIx::new(i);
115
0
116
0
        let prev_block = &func.blocks[prev];
117
0
        if prev_block.start >= func.blocks[cur].start {
118
0
            return Err(format!(
119
0
                "blocks {:?} and {:?} not ordered in strictly increasing start",
120
0
                prev, cur
121
0
            ));
122
0
        }
123
0
124
0
        let prev_start: u32 = prev_block.start.into();
125
0
        let cur_start: u32 = func.blocks[cur].start.into();
126
0
        if prev_start + prev_block.len != cur_start {
127
0
            return Err(format!("block {:?} is incorrectly specified", prev));
128
0
        }
129
    }
130
131
    // Check instructions.
132
0
    for b in func.blocks.iter() {
133
0
        if b.start.get().checked_add(b.len).is_none() {
134
0
            return Err("too many block instructions".into());
135
0
        }
136
0
        for i in b.start.dotdot(b.start.plus(b.len)) {
137
0
            if i.get() >= func.insns.len() {
138
0
                return Err(format!(
139
0
                    "invalid instruction number {:?} in block {}",
140
0
                    i, b.name
141
0
                ));
142
0
            }
143
0
144
0
            let inst = &func.insns[i];
145
0
146
0
            if !inst.is_user() {
147
0
                return Err(format!(
148
0
                    "unexpected regalloc inst {:?} in block {}",
149
0
                    inst, b.name
150
0
                ));
151
0
            }
152
0
153
0
            // This is a poor man's liveness analysis, since it doesn't take control flow into account.
154
0
            if !inst.type_checks(&mut cx) {
155
0
                return Err(format!(
156
0
                    "inst {:?} in block {} does not type check",
157
0
                    inst, b.name
158
0
                ));
159
0
            }
160
0
161
0
            // No control flow instructions in the middle, but it must be one at the end.
162
0
            if i == b.start.plus(b.len - 1) {
163
0
                if !inst.is_control_flow() {
164
0
                    return Err(format!(
165
0
                        "final inst {:?} of block {} must be a control flow inst",
166
0
                        inst, b.name
167
0
                    ));
168
0
                }
169
            } else {
170
0
                if inst.is_control_flow() {
171
0
                    return Err(format!(
172
0
                        "control flow inst {:?} in the middle of block {}",
173
0
                        inst, b.name
174
0
                    ));
175
0
                }
176
            }
177
        }
178
    }
179
180
0
    if let Err(err) = regalloc::analysis_main::run_analysis(
181
0
        func,
182
0
        real_reg_universe,
183
0
        // The next four params merely ensure that we get all possible analysis results from
184
0
        // `run_analysis`.  There's no implied claim about which algorithm we're using or
185
0
        // validating.
186
0
        AlgorithmWithDefaults::Backtracking,
187
0
        /*client_wants_stackmaps=*/ true,
188
0
        /*reftype_class=*/ RegClass::I64,
189
0
        /*reftyped_vregs=*/ &vec![],
190
0
    ) {
191
0
        return Err(err.to_string());
192
0
    }
193
0
194
0
    Ok(())
195
0
}
196
197
0
pub fn check_results(
198
0
    before_regalloc_result: &Result<RunResult, String>,
199
0
    after_regalloc_result: &Result<RunResult, String>,
200
0
) {
201
0
    match before_regalloc_result {
202
0
        Ok(before_regalloc_result) => {
203
0
            let after_regalloc_result = after_regalloc_result
204
0
                .as_ref()
205
0
                .expect("code after regalloc should have succeeded");
206
207
            // The interpreted result number of dynamic steps is a lower bound on the
208
            // number of dynamic steps executed after regalloc.
209
            assert!(
210
0
                before_regalloc_result.num_steps <= after_regalloc_result.num_steps,
211
                "inconsistent trace"
212
            );
213
214
            assert_eq!(
215
                before_regalloc_result.ret_value, after_regalloc_result.ret_value,
216
0
                "Incorrect interpreter result: expected {:?}, observed {:?}",
217
0
                before_regalloc_result.ret_value, after_regalloc_result.ret_value
218
            );
219
220
            assert_eq!(
221
                before_regalloc_result.stdout, after_regalloc_result.stdout,
222
0
                r#"Different stdout values before/after regalloc:
223
0
- before:
224
0
{}
225
0
-after:
226
0
{}
227
0
        "#,
228
0
                before_regalloc_result.stdout, after_regalloc_result.stdout
229
            );
230
        }
231
232
0
        Err(err) => {
233
0
            assert_eq!(err, after_regalloc_result.as_ref().unwrap_err());
234
        }
235
    }
236
0
}