1use alloc::vec;
5use alloc::vec::Vec;
6use core::fmt;
7
8use super::entity::{Block, Inst, StackSlot, Symbol};
9use super::function::Function;
10use super::operand::{BlockCall, Operand};
11use crate::regs::{PReg, Reg, RegClass, VReg};
12use crate::target::{InstKind, Opcode, OperandRule};
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34#[non_exhaustive]
35pub enum MirError {
36 UnknownOpcode {
38 inst: Inst,
40 opcode: Opcode,
42 },
43 OperandCount {
46 inst: Inst,
48 expected: usize,
50 found: usize,
52 },
53 OperandShape {
58 inst: Inst,
60 index: usize,
62 },
63 RegClass {
66 inst: Inst,
68 index: usize,
70 },
71 ImmediateRange {
74 inst: Inst,
76 index: usize,
78 value: i64,
80 },
81 TooManyMemOperands {
84 inst: Inst,
86 },
87 ArgCount {
90 inst: Inst,
92 block: Block,
94 expected: usize,
96 found: usize,
98 },
99 ArgClass {
101 inst: Inst,
103 block: Block,
105 index: usize,
107 },
108 DuplicateDestination {
110 inst: Inst,
112 },
113 BadParam {
115 block: Block,
117 },
118 BadArg {
121 block: Block,
123 },
124 UnknownVReg(VReg),
126 UnknownPReg(PReg),
128 UnknownSlot(StackSlot),
130 UnknownBlock(Block),
133 UnknownSymbol(Symbol),
135 UnknownClass(RegClass),
137 MultipleDefs {
140 vreg: VReg,
142 },
143 MissingTerminator {
146 block: Block,
148 },
149 MisplacedTerminator {
151 inst: Inst,
153 },
154 NoBlocks,
156 Limit,
160}
161
162impl fmt::Display for MirError {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 match self {
165 MirError::UnknownOpcode { inst, opcode } => {
166 write!(f, "{inst}: opcode {} is not in the target", opcode.index())
167 }
168 MirError::OperandCount {
169 inst,
170 expected,
171 found,
172 } => write!(f, "{inst}: takes {expected} operands, {found} given"),
173 MirError::OperandShape { inst, index } => {
174 write!(
175 f,
176 "{inst}: operand {index} has a shape this position does not take"
177 )
178 }
179 MirError::RegClass { inst, index } => {
180 write!(
181 f,
182 "{inst}: operand {index} is a register of the wrong class"
183 )
184 }
185 MirError::ImmediateRange { inst, index, value } => write!(
186 f,
187 "{inst}: immediate {value} in operand {index} is out of range; load it into a register"
188 ),
189 MirError::TooManyMemOperands { inst } => {
190 write!(f, "{inst}: too many memory operands for one instruction")
191 }
192 MirError::ArgCount {
193 inst,
194 block,
195 expected,
196 found,
197 } => write!(
198 f,
199 "{inst}: {block} has {expected} parameters but {found} arguments are passed"
200 ),
201 MirError::ArgClass { inst, block, index } => write!(
202 f,
203 "{inst}: argument {index} to {block} is not of its parameter's class"
204 ),
205 MirError::DuplicateDestination { inst } => {
206 write!(f, "{inst}: a parallel copy writes one destination twice")
207 }
208 MirError::BadParam { block } => write!(
209 f,
210 "{block}: a parameter must be a register or a whole stack slot"
211 ),
212 MirError::BadArg { block } => write!(
213 f,
214 "an argument to {block} must be a register, a whole stack slot, or an immediate"
215 ),
216 MirError::UnknownVReg(v) => write!(f, "virtual register {v:?} does not exist"),
217 MirError::UnknownPReg(p) => {
218 write!(f, "physical register {} is not in the target", p.index())
219 }
220 MirError::UnknownSlot(s) => write!(f, "stack slot {s} does not exist"),
221 MirError::UnknownBlock(b) => write!(f, "{b} does not exist"),
222 MirError::UnknownSymbol(s) => write!(f, "symbol {s} does not exist"),
223 MirError::UnknownClass(c) => {
224 write!(f, "register class {} is not in the target", c.index())
225 }
226 MirError::MultipleDefs { vreg } => {
227 write!(f, "{vreg:?} is defined more than once")
228 }
229 MirError::MissingTerminator { block } => {
230 write!(
231 f,
232 "{block} does not end with a jump, branch, return, or trap"
233 )
234 }
235 MirError::MisplacedTerminator { inst } => {
236 write!(
237 f,
238 "{inst}: a terminator must be the last instruction of its block"
239 )
240 }
241 MirError::NoBlocks => f.write_str("the function has no blocks"),
242 MirError::Limit => f.write_str("the function exceeds its 32-bit tables"),
243 }
244 }
245}
246
247impl core::error::Error for MirError {}
248
249fn loc_key(op: &Operand) -> Option<u64> {
252 match op {
253 Operand::Reg(r) => Some(match (r.as_phys(), r.as_virt()) {
254 (Some(p), _) => p.index() as u64,
255 (_, Some(v)) => (1 << 32) | v.index() as u64,
256 _ => return None,
257 }),
258 Operand::Slot { slot, .. } => Some((2 << 32) | slot.index() as u64),
259 _ => None,
260 }
261}
262
263impl Function {
264 pub(crate) fn check_inst(
266 &self,
267 inst: Inst,
268 opcode: Opcode,
269 operands: &[Operand],
270 ) -> Result<(), MirError> {
271 let target = self.target();
272 let desc = target
273 .inst(opcode)
274 .ok_or(MirError::UnknownOpcode { inst, opcode })?;
275 match desc.kind() {
276 InstKind::Copy => {
277 if operands.len() != 2 {
278 return Err(MirError::OperandCount {
279 inst,
280 expected: 2,
281 found: operands.len(),
282 });
283 }
284 self.check_copy_pair(inst, operands, 0, 1)
285 }
286 InstKind::ParallelCopy => {
287 if operands.len() % 2 != 0 {
288 return Err(MirError::OperandCount {
289 inst,
290 expected: operands.len() + 1,
291 found: operands.len(),
292 });
293 }
294 let n = operands.len() / 2;
295 for i in 0..n {
296 self.check_copy_pair(inst, operands, i, n + i)?;
297 }
298 let mut keys: Vec<u64> = operands[..n].iter().filter_map(loc_key).collect();
299 keys.sort_unstable();
300 if keys.windows(2).any(|w| w[0] == w[1]) {
301 return Err(MirError::DuplicateDestination { inst });
302 }
303 Ok(())
304 }
305 _ => {
306 let rules = desc.operands();
307 if rules.len() != operands.len() {
308 return Err(MirError::OperandCount {
309 inst,
310 expected: rules.len(),
311 found: operands.len(),
312 });
313 }
314 let mut mems = 0usize;
315 for (index, (rule, op)) in rules.iter().zip(operands).enumerate() {
316 self.check_operand(inst, index, *rule, op, rules, &mut mems)?;
317 }
318 if mems > usize::from(target.max_mem_operands()) {
319 return Err(MirError::TooManyMemOperands { inst });
320 }
321 Ok(())
322 }
323 }
324 }
325
326 fn check_copy_pair(
329 &self,
330 inst: Inst,
331 ops: &[Operand],
332 dst: usize,
333 src: usize,
334 ) -> Result<(), MirError> {
335 let shape = |index| MirError::OperandShape { inst, index };
336 let (d, s) = (ops[dst], ops[src]);
337 match d {
338 Operand::Reg(r) => self.check_reg(r)?,
339 Operand::Slot { slot, offset: 0 } => self.check_slot(slot)?,
340 _ => return Err(shape(dst)),
341 }
342 match s {
343 Operand::Reg(r) => self.check_reg(r)?,
344 Operand::Slot { slot, offset: 0 } => self.check_slot(slot)?,
345 Operand::Imm(_) => {}
346 _ => return Err(shape(src)),
347 }
348 if let (Some(a), Some(b)) = (d.reg(), s.reg()) {
349 if self.reg_class(a) != self.reg_class(b) {
350 return Err(MirError::RegClass { inst, index: src });
351 }
352 }
353 Ok(())
354 }
355
356 fn reg_in(&self, inst: Inst, index: usize, reg: Reg, class: RegClass) -> Result<(), MirError> {
357 self.check_reg(reg)?;
358 if self.reg_class(reg) == Some(class) {
359 Ok(())
360 } else {
361 Err(MirError::RegClass { inst, index })
362 }
363 }
364
365 fn check_operand(
366 &self,
367 inst: Inst,
368 index: usize,
369 rule: OperandRule,
370 op: &Operand,
371 rules: &[OperandRule],
372 mems: &mut usize,
373 ) -> Result<(), MirError> {
374 let target = self.target();
375 let shape = MirError::OperandShape { inst, index };
376 match rule {
377 OperandRule::Def(class) => match op {
378 Operand::Reg(r) => self.reg_in(inst, index, *r, class),
379 _ => Err(shape),
380 },
381 OperandRule::Tied(def) => {
382 let class = match rules.get(usize::from(def)) {
383 Some(OperandRule::Def(c)) => *c,
384 _ => return Err(shape),
385 };
386 match op {
387 Operand::Reg(r) => self.reg_in(inst, index, *r, class),
388 _ => Err(shape),
389 }
390 }
391 OperandRule::Use(class, accept) => match *op {
392 Operand::Reg(r) if accept.reg() => self.reg_in(inst, index, r, class),
393 Operand::Imm(value) if accept.imm() != crate::target::ImmRange::None => {
394 if accept.imm().fits(value) {
395 Ok(())
396 } else {
397 Err(MirError::ImmediateRange { inst, index, value })
398 }
399 }
400 Operand::Mem(m) if accept.mem() => {
401 *mems += 1;
402 for r in m.regs() {
403 self.reg_in(inst, index, r, target.addr_class())?;
404 }
405 Ok(())
406 }
407 Operand::Slot { slot, .. } if accept.mem() => {
408 *mems += 1;
409 self.check_slot(slot)
410 }
411 Operand::Symbol(s) if accept.symbol() => {
412 if s.index() < self.symbols.len() {
413 Ok(())
414 } else {
415 Err(MirError::UnknownSymbol(s))
416 }
417 }
418 _ => Err(shape),
419 },
420 OperandRule::Cond => match op {
421 Operand::Cond(c) if c.code() < target.conds() => Ok(()),
422 _ => Err(shape),
423 },
424 OperandRule::Block => match op {
425 Operand::Block(call) => self.check_block_call(inst, call),
426 _ => Err(shape),
427 },
428 }
429 }
430
431 fn check_block_call(&self, inst: Inst, call: &BlockCall) -> Result<(), MirError> {
432 let block = call.block();
433 let params = self
434 .blocks
435 .get(block.index())
436 .ok_or(MirError::UnknownBlock(block))?
437 .params
438 .as_slice();
439 let args = self
441 .args
442 .get(call.range())
443 .ok_or(MirError::UnknownBlock(block))?;
444 if args.len() != params.len() {
445 return Err(MirError::ArgCount {
446 inst,
447 block,
448 expected: params.len(),
449 found: args.len(),
450 });
451 }
452 for (index, (a, p)) in args.iter().zip(params).enumerate() {
453 if let (Some(a), Some(p)) = (a.reg(), p.reg()) {
454 if self.reg_class(a) != self.reg_class(p) {
455 return Err(MirError::ArgClass { inst, block, index });
456 }
457 }
458 }
459 Ok(())
460 }
461
462 pub fn verify(&self) -> Result<(), MirError> {
493 if self.blocks.is_empty() {
494 return Err(MirError::NoBlocks);
495 }
496 let target = self.target();
497 let mut defined = vec![false; self.vregs.len()];
498 let mut define = |r: Option<Reg>| -> Result<(), MirError> {
499 if let Some(v) = r.and_then(Reg::as_virt) {
500 let seen = defined.get_mut(v.index()).ok_or(MirError::UnknownVReg(v))?;
501 if *seen {
502 return Err(MirError::MultipleDefs { vreg: v });
503 }
504 *seen = true;
505 }
506 Ok(())
507 };
508 for (b, data) in self.blocks.iter().enumerate() {
509 let block = Block::from_index(b);
510 for p in &data.params {
511 define(p.reg())?;
512 }
513 let Some(&last) = data.insts.last() else {
514 return Err(MirError::MissingTerminator { block });
515 };
516 for &inst in data.insts.iter() {
517 let d = self.insts.get(inst.index()).ok_or(MirError::Limit)?;
518 let ops = self.operands(inst);
519 self.check_inst(inst, d.opcode, ops)?;
520 let desc = target.inst(d.opcode).ok_or(MirError::UnknownOpcode {
521 inst,
522 opcode: d.opcode,
523 })?;
524 let defs = match desc.kind() {
525 InstKind::Copy => 1,
526 InstKind::ParallelCopy => ops.len() / 2,
527 _ => desc.defs(),
528 };
529 for op in ops.iter().take(defs) {
530 define(op.reg())?;
531 }
532 let terminator = desc.kind().is_terminator();
533 if terminator && inst != last {
534 return Err(MirError::MisplacedTerminator { inst });
535 }
536 if !terminator && inst == last {
537 return Err(MirError::MissingTerminator { block });
538 }
539 }
540 }
541 Ok(())
542 }
543}