1use std::fmt;
43
44use rucc_base::Interner;
45use rucc_target::TargetInfo;
46
47use crate::func::Func;
48use crate::inst::{Abi, Block, Def, Inst, Param, Signature, Value};
49use crate::module::{Alias, AliasKind, DataLayout, Datum, Global, Module, SymbolRef};
50use crate::{Extra, MemOrder, Opcode, Type};
51
52#[derive(Clone, Debug, PartialEq, Eq)]
54pub struct VerifyError {
55 pub at: String,
58 pub message: String,
60}
61
62impl fmt::Display for VerifyError {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 write!(f, "{}: {}", self.at, self.message)
65 }
66}
67
68impl std::error::Error for VerifyError {}
69
70pub fn verify(module: &Module, names: &Interner) -> Result<(), Vec<VerifyError>> {
77 let mut verifier = Verifier::new(module, names);
78 verifier.module();
79 verifier.finish()
80}
81
82pub fn verify_func<'a>(
91 module: &'a Module,
92 func: &'a Func,
93 names: &'a Interner,
94) -> Result<(), Vec<VerifyError>> {
95 let mut verifier = Verifier::new(module, names);
96 verifier.func(func);
97 verifier.finish()
98}
99
100struct Verifier<'a> {
102 module: &'a Module,
103 names: &'a Interner,
104 errors: Vec<VerifyError>,
105 func: Option<&'a Func>,
108 block: Option<Block>,
109 inst: Option<Inst>,
110}
111
112impl<'a> Verifier<'a> {
113 fn new(module: &'a Module, names: &'a Interner) -> Self {
114 Verifier { module, names, errors: Vec::new(), func: None, block: None, inst: None }
115 }
116
117 fn finish(self) -> Result<(), Vec<VerifyError>> {
118 if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
119 }
120
121 fn module(&mut self) {
124 let implied = DataLayout::for_target(&TargetInfo::new(self.module.triple));
125 if self.module.datalayout != implied {
126 self.at(
127 format!("@{}", self.names.resolve(self.module.name)),
128 format!(
129 "the datalayout is `{}` and {} implies `{implied}`",
130 self.module.datalayout, self.module.triple
131 ),
132 );
133 }
134
135 for id in self.module.globals() {
136 self.global(&self.module[id]);
137 }
138 for id in self.module.aliases() {
139 self.alias(&self.module[id]);
140 }
141 for id in self.module.funcs() {
142 self.func(&self.module[id]);
143 }
144 self.metadata();
145 }
146
147 fn global(&mut self, global: &Global) {
148 let at = format!("@{}", self.names.resolve(global.name));
149 if !global.align.is_power_of_two() {
150 self.at(
151 at.clone(),
152 format!("an alignment is a power of two and this is {}", global.align),
153 );
154 }
155 let Some(init) = global.init else { return };
161 let mut size = 0;
162 for &datum in &self.module[init] {
163 size += datum.size(self.module);
164 if let Datum::Scalar { ty, .. } = datum {
170 if ty.bits() == 0 {
171 self.at(
172 at.clone(),
173 format!("a scalar in an image has a width and {ty} has none"),
174 );
175 }
176 }
177 if let Datum::Addr(reloc) = datum {
178 let bytes = self.module[reloc].size;
179 if !matches!(bytes, 1 | 2 | 4 | 8) {
180 self.at(
181 at.clone(),
182 format!(
183 "an address is written as 1, 2, 4 or 8 bytes and this one as {bytes}"
184 ),
185 );
186 }
187 }
188 }
189 if size != global.size {
190 self.at(at, format!("the image is {size} bytes and the global is {}", global.size));
191 }
192 }
193
194 fn alias(&mut self, alias: &Alias) {
195 let at = format!("@{}", self.names.resolve(alias.name));
196 if alias.name == alias.target {
197 self.at(at, "an alias to itself");
198 return;
199 }
200 let Some(found) = self.module.lookup(alias.target) else { return };
203 if alias.kind == AliasKind::IFunc && !matches!(found, SymbolRef::Func(_)) {
204 self.at(at, "an ifunc resolves through a function and this target is not one");
205 }
206 }
207
208 fn metadata(&mut self) {
209 for node in self.module.metadata() {
210 let Some(parent) = self.module[node].parent else { continue };
211 if parent.raw() >= node.raw() {
212 self.at(
215 format!("!{}", node.raw()),
216 format!(
217 "a metadata node's parent comes before it and this one is !{}",
218 parent.raw()
219 ),
220 );
221 }
222 }
223 }
224
225 fn func(&mut self, func: &'a Func) {
228 self.func = Some(func);
229 self.block = None;
230 self.inst = None;
231
232 if let Some((one, other)) = func.attrs.conflict() {
233 self.error(format!("`{one}` and `{other}` cannot both be true of a function"));
234 }
235 if func.is_declaration() {
236 self.func = None;
237 return;
238 }
239 if !self.bounds(func) {
242 self.func = None;
243 return;
244 }
245
246 for signature in func.signatures() {
247 self.signature(signature);
248 }
249
250 let entry = func.entry().expect("a function with blocks has a first one");
251 let params: Vec<Type> = func[entry].params.iter().map(|&value| func[value].ty).collect();
252 let want: Vec<Type> = func.signature().param_types().collect();
253 if params != want {
254 self.error(format!(
255 "the entry block takes {} and the signature says {}",
256 types(¶ms),
257 types(&want)
258 ));
259 }
260
261 let doms = Doms::new(func);
262 let layout = Layout::new(func);
263 for block in func.blocks() {
264 self.block = Some(block);
265 self.block(func, block, &doms, &layout);
266 }
267 self.block = None;
268 self.inst = None;
269 self.func = None;
270 }
271
272 fn signature(&mut self, signature: &Signature) {
279 for (index, param) in signature.params.iter().enumerate() {
280 let at = format!("parameter {}", index + 1);
281 self.abi(&at, param);
282 match param.abi {
283 Abi::Sret { .. } if index > 0 => {
284 self.error("sret is the first parameter and this one is not");
288 }
289 Abi::Sret { .. } if !signature.returns.is_empty() => {
290 self.error("a signature returning through sret returns nothing else");
291 }
292 _ => {}
293 }
294 }
295 for (index, param) in signature.returns.iter().enumerate() {
296 let at = format!("result {}", index + 1);
297 self.abi(&at, param);
298 if param.abi.indirect() {
299 self.error(format!("{at} travels indirectly and a result cannot"));
302 }
303 }
304 }
305
306 fn abi(&mut self, at: &str, param: &Param) {
308 match param.abi {
309 Abi::Plain => {}
310 Abi::Sext | Abi::Zext => {
311 if !param.ty.is_int() || param.ty.is_vector() {
312 self.error(format!("{at} is extended and {} is not an integer", param.ty));
313 }
314 }
315 Abi::ByVal { size, align } | Abi::Sret { size, align } => {
316 if !param.ty.is_ptr() {
317 self.error(format!(
318 "{at} travels indirectly and {} is not a pointer",
319 param.ty
320 ));
321 }
322 if !align.is_power_of_two() {
323 self.error(format!("an alignment is a power of two and this is {align}"));
324 }
325 if size == 0 {
326 self.error(format!("{at} travels indirectly and has no size"));
327 }
328 }
329 }
330 }
331
332 fn bounds(&mut self, func: &'a Func) -> bool {
338 let counts = func.counts();
339 let before = self.errors.len();
340 for block in func.blocks() {
341 self.block = Some(block);
342 for &value in &func[block].params {
343 if value.index() >= counts.values {
344 self.error(format!(
345 "parameter %{} is not a value of this function",
346 value.raw()
347 ));
348 }
349 }
350 for inst in func.insts(block) {
351 self.inst = Some(inst);
352 for &value in &func[func[inst].args] {
353 if value.index() >= counts.values {
354 self.error(format!("%{} is not a value of this function", value.raw()));
355 }
356 }
357 for value in func[inst].results() {
358 if value.index() >= counts.values {
359 self.error(format!("%{} is not a value of this function", value.raw()));
360 }
361 }
362 for call in func.successors(inst) {
363 if call.block.index() >= counts.blocks {
364 self.error(format!(
365 "block{} is not a block of this function",
366 call.block.raw()
367 ));
368 }
369 for &value in &func[call.args] {
370 if value.index() >= counts.values {
371 self.error(format!("%{} is not a value of this function", value.raw()));
372 }
373 }
374 }
375 }
376 self.inst = None;
377 }
378 self.block = None;
379 self.errors.len() == before
380 }
381
382 fn block(&mut self, func: &'a Func, block: Block, doms: &Doms, layout: &Layout) {
383 if !doms.reaches(block) {
384 self.error("this block is not reachable and has not been deleted");
385 }
386 if block == func.entry().expect("checked in func") {
387 for other in func.blocks() {
388 let last = func[other].last;
389 if last.is_some_and(|inst| func.successors(inst).any(|call| call.block == block)) {
390 self.error("the entry block is branched to, and it takes the arguments");
391 }
392 }
393 }
394
395 let mut seen_terminator = false;
396 for inst in func.insts(block) {
397 self.inst = Some(inst);
398 if seen_terminator {
399 self.error("this comes after the block's terminator");
400 }
401 seen_terminator |= func.is_terminator(inst);
402 self.inst(func, inst, doms, layout);
403 }
404 self.inst = None;
405 if !seen_terminator {
406 self.error("this block does not end in a terminator");
407 }
408 }
409
410 fn inst(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
411 let data = &func[inst];
412 let opcode = data.opcode;
413
414 let stray = data.flags.without(crate::Flags::legal_on(opcode));
415 if !stray.is_empty() {
416 let names: Vec<&str> = stray.iter().map(|(_, name)| name).collect();
417 self.error(format!("{} does not read `{}`", opcode.name(), names.join("`, `")));
418 }
419 if data.extra.kind() != opcode.extra_kind() {
420 self.error(format!(
423 "{} carries {} and this one carries {}",
424 opcode.name(),
425 opcode.extra_kind().name(),
426 data.extra.kind().name()
427 ));
428 }
429 if let Some(want) = opcode.results() {
430 if want != data.results {
431 self.error(format!(
432 "{} produces {want} values and this one produces {}",
433 opcode.name(),
434 data.results
435 ));
436 }
437 }
438
439 self.uses(func, inst, doms, layout);
440 self.branches(func, inst);
441 self.memory(func, inst);
442 self.shape(func, inst);
443 }
444
445 fn uses(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
447 let block = layout.block_of(inst).expect("walking the blocks");
448 let check = |verifier: &mut Self, value: Value| match func[value].def {
449 Def::Param { block: def, .. } => {
450 if !doms.dominates(def, block) {
451 verifier.error(format!(
452 "%{} arrives at block{} and does not reach here",
453 value.raw(),
454 def.raw()
455 ));
456 }
457 }
458 Def::Result { inst: def, .. } => {
459 let Some(def_block) = layout.block_of(def) else {
460 verifier.error(format!(
461 "%{} is produced by an instruction that is not in the function",
462 value.raw()
463 ));
464 return;
465 };
466 let reaches = if def_block == block {
467 layout.position(def) < layout.position(inst)
468 } else {
469 doms.dominates(def_block, block)
470 };
471 if !reaches {
472 verifier.error(format!(
473 "%{} is produced in block{} and does not reach here",
474 value.raw(),
475 def_block.raw()
476 ));
477 }
478 }
479 };
480 for &value in &func[func[inst].args] {
481 check(self, value);
482 }
483 for call in func.successors(inst) {
484 for &value in &func[call.args] {
485 check(self, value);
486 }
487 }
488 }
489
490 fn branches(&mut self, func: &'a Func, inst: Inst) {
492 if func[inst].opcode == Opcode::BlockAddr {
493 return;
497 }
498 for call in func.successors(inst) {
499 let params = &func[call.block].params;
500 let args = &func[call.args];
501 if params.len() != args.len() {
502 self.error(format!(
503 "block{} takes {} arguments and this branch passes {}",
504 call.block.raw(),
505 params.len(),
506 args.len()
507 ));
508 continue;
509 }
510 for (index, (¶m, &arg)) in params.iter().zip(args).enumerate() {
511 let (want, got) = (func[param].ty, func[arg].ty);
512 if want != got {
513 self.error(format!(
514 "argument {} to block{} is {want} and this one is {got}",
515 index + 1,
516 call.block.raw()
517 ));
518 }
519 }
520 }
521 if let Extra::Switch(info) = func[inst].extra {
522 let switch = &func[info];
523 let targets = &func[switch.targets];
524 let cases = &func[switch.cases];
525 if targets.len() != cases.len() + 1 {
526 self.error(format!(
527 "a switch has one target per case and a default, and this one has {} targets for {} cases",
528 targets.len(),
529 cases.len()
530 ));
531 }
532 for (index, case) in cases.iter().enumerate() {
533 if cases[..index].contains(case) {
534 self.error("two cases of this switch have the same value");
535 }
536 }
537 }
538 }
539
540 fn memory(&mut self, func: &'a Func, inst: Inst) {
542 let opcode = func[inst].opcode;
543 let info = match func[inst].extra {
544 Extra::Mem(at) => func[at],
545 Extra::Rmw(_, at) => func[at],
546 Extra::Order(order) => {
547 if !order.is_valid_for_rmw() {
548 self.error("a fence is not a fence unless it orders something");
549 }
550 return;
551 }
552 _ => return,
553 };
554 if !info.align.is_power_of_two() {
555 self.error(format!("an alignment is a power of two and this is {}", info.align));
556 }
557 if let Some(tbaa) = info.tbaa {
558 if tbaa.index() >= self.module.counts().metadata {
559 self.error(format!("!{} is not a metadata node of this module", tbaa.raw()));
560 }
561 }
562 let ok = match opcode {
563 Opcode::AtomicLoad => info.order.is_valid_for_load(),
564 Opcode::AtomicStore => info.order.is_valid_for_store(),
565 Opcode::AtomicRmw | Opcode::Cmpxchg => info.order.is_valid_for_rmw(),
566 _ => info.order == MemOrder::NotAtomic,
569 };
570 if !ok {
571 self.error(format!("{} cannot be asked for {}", opcode.name(), info.order));
572 }
573 if matches!(opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) && info.size == 0 {
574 self.error(format!("{} moves no bytes", opcode.name()));
575 }
576 }
577
578 #[expect(clippy::too_many_lines, reason = "one arm per group of opcodes, and they differ")]
584 fn shape(&mut self, func: &'a Func, inst: Inst) {
585 let data = &func[inst];
586 let opcode = data.opcode;
587 let args = &func[data.args];
588 let arity = args.len();
589 let arg = |n: usize| func[args[n]].ty;
590 let results = usize::from(data.results);
591 let res = |n: usize| func[data.results().nth(n).expect("within the count")].ty;
592
593 match opcode {
594 Opcode::IConst => {
596 if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_int() {
597 self.error(format!(
598 "iconst produces an integer and this one produces {}",
599 res(0)
600 ));
601 }
602 }
603 Opcode::FConst => {
604 if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_float() {
605 self.error(format!(
606 "fconst produces a floating point value and this one produces {}",
607 res(0)
608 ));
609 }
610 }
611 Opcode::Splat => {
612 if self.takes(opcode, arity, 0) && results == 1 && !res(0).is_vector() {
613 self.error(format!("splat produces a vector and this one produces {}", res(0)));
614 }
615 }
616 Opcode::GlobalAddr
617 | Opcode::StackSave
618 | Opcode::FrameAddress
619 | Opcode::ReturnAddress => {
620 if results == 1 && !res(0).is_ptr() {
621 self.error(format!(
622 "{} produces a pointer and this one produces {}",
623 opcode.name(),
624 res(0)
625 ));
626 }
627 }
628
629 Opcode::Add
631 | Opcode::Sub
632 | Opcode::Mul
633 | Opcode::SDiv
634 | Opcode::UDiv
635 | Opcode::SRem
636 | Opcode::URem
637 | Opcode::And
638 | Opcode::Or
639 | Opcode::Xor
640 | Opcode::Shl
641 | Opcode::LShr
642 | Opcode::AShr => {
643 if self.takes(opcode, arity, 2) {
644 self.integer(opcode, arg(0), 0);
645 self.agree(opcode, arg(0), arg(1));
646 if results == 1 {
647 self.produces(opcode, res(0), arg(0));
648 }
649 }
650 }
651
652 Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv | Opcode::FRem => {
654 if self.takes(opcode, arity, 2) {
655 self.floating(opcode, arg(0), 0);
656 self.agree(opcode, arg(0), arg(1));
657 if results == 1 {
658 self.produces(opcode, res(0), arg(0));
659 }
660 }
661 }
662 Opcode::FNeg => {
663 if self.takes(opcode, arity, 1) {
664 self.floating(opcode, arg(0), 0);
665 if results == 1 {
666 self.produces(opcode, res(0), arg(0));
667 }
668 }
669 }
670 Opcode::Fma => {
671 if self.takes(opcode, arity, 3) {
672 self.floating(opcode, arg(0), 0);
673 self.agree(opcode, arg(0), arg(1));
674 self.agree(opcode, arg(0), arg(2));
675 if results == 1 {
676 self.produces(opcode, res(0), arg(0));
677 }
678 }
679 }
680
681 Opcode::ICmp | Opcode::FCmp => {
683 if self.takes(opcode, arity, 2) {
684 if opcode == Opcode::FCmp {
685 self.floating(opcode, arg(0), 0);
686 } else if !arg(0).lane().is_int() && !arg(0).is_ptr() {
687 self.error(format!(
688 "operand 1 of icmp is an integer or a pointer and this one is {}",
689 arg(0)
690 ));
691 }
692 self.agree(opcode, arg(0), arg(1));
693 if results == 1 {
694 self.produces(opcode, res(0), arg(0).with_lane(Type::I1));
695 }
696 }
697 }
698
699 Opcode::Trunc | Opcode::SExt | Opcode::ZExt => {
701 if self.takes(opcode, arity, 1) && results == 1 {
702 self.integer(opcode, arg(0), 0);
703 self.lanes(opcode, res(0), arg(0));
704 self.widens(opcode, res(0), arg(0), opcode != Opcode::Trunc);
705 }
706 }
707 Opcode::FPTrunc | Opcode::FPExt => {
708 if self.takes(opcode, arity, 1) && results == 1 {
709 self.floating(opcode, arg(0), 0);
710 self.lanes(opcode, res(0), arg(0));
711 self.widens(opcode, res(0), arg(0), opcode == Opcode::FPExt);
712 }
713 }
714 Opcode::FPToSI | Opcode::FPToUI => {
715 if self.takes(opcode, arity, 1) && results == 1 {
716 self.floating(opcode, arg(0), 0);
717 self.lanes(opcode, res(0), arg(0));
718 if !res(0).lane().is_int() {
719 self.error(format!(
720 "{} produces an integer and this one produces {}",
721 opcode.name(),
722 res(0)
723 ));
724 }
725 }
726 }
727 Opcode::SIToFP | Opcode::UIToFP => {
728 if self.takes(opcode, arity, 1) && results == 1 {
729 self.integer(opcode, arg(0), 0);
730 self.lanes(opcode, res(0), arg(0));
731 if !res(0).lane().is_float() {
732 self.error(format!(
733 "{} produces a floating point value and this one produces {}",
734 opcode.name(),
735 res(0)
736 ));
737 }
738 }
739 }
740 Opcode::PtrToInt => {
741 if self.takes(opcode, arity, 1) && results == 1 {
742 self.pointer(opcode, arg(0), 0);
743 self.integer(opcode, res(0), 0);
744 }
745 }
746 Opcode::IntToPtr => {
747 if self.takes(opcode, arity, 1) && results == 1 {
748 self.integer(opcode, arg(0), 0);
749 if !res(0).is_ptr() {
750 self.error(format!(
751 "inttoptr produces a pointer and this one produces {}",
752 res(0)
753 ));
754 }
755 }
756 }
757 Opcode::Bitcast => {
758 if self.takes(opcode, arity, 1) && results == 1 {
759 let (from, to) = (arg(0), res(0));
760 if from.is_ptr() != to.is_ptr() {
761 self.error(
764 "a bitcast between a pointer and a number is ptrtoint or inttoptr",
765 );
766 } else if width(from) != width(to) {
767 self.error(format!("a bitcast keeps the width and {from} and {to} differ"));
768 }
769 }
770 }
771
772 Opcode::Alloca => {
774 if arity > 1 {
775 self.takes(opcode, arity, 1);
776 } else if arity == 1 {
777 self.integer(opcode, arg(0), 0);
778 } else if func.block_of(inst) != func.entry() {
779 self.error("an alloca of a fixed size belongs in the entry block");
782 }
783 if results == 1 && !res(0).is_ptr() {
784 self.error(format!(
785 "alloca produces a pointer and this one produces {}",
786 res(0)
787 ));
788 }
789 }
790 Opcode::Load | Opcode::AtomicLoad => {
791 if self.takes(opcode, arity, 1) {
792 self.pointer(opcode, arg(0), 0);
793 }
794 if results == 1 && res(0).is_void() {
795 self.error(format!("{} reads a value and void is not one", opcode.name()));
796 }
797 }
798 Opcode::Store | Opcode::AtomicStore => {
799 if self.takes(opcode, arity, 2) {
800 if arg(0).is_void() {
801 self.error(format!("{} writes a value and void is not one", opcode.name()));
802 }
803 self.pointer(opcode, arg(1), 1);
804 }
805 }
806 Opcode::PtrAdd => {
807 if self.takes(opcode, arity, 2) {
808 self.pointer(opcode, arg(0), 0);
809 self.integer(opcode, arg(1), 1);
810 if results == 1 && !res(0).is_ptr() {
811 self.error(format!(
812 "ptr_add produces a pointer and this one produces {}",
813 res(0)
814 ));
815 }
816 }
817 }
818 Opcode::Memcpy | Opcode::Memmove => {
819 if self.takes(opcode, arity, 2) {
820 self.pointer(opcode, arg(0), 0);
821 self.pointer(opcode, arg(1), 1);
822 }
823 }
824 Opcode::Memset => {
825 if self.takes(opcode, arity, 2) {
826 self.pointer(opcode, arg(0), 0);
827 self.integer(opcode, arg(1), 1);
828 }
829 }
830 Opcode::AtomicRmw => {
831 if self.takes(opcode, arity, 2) {
832 self.pointer(opcode, arg(0), 0);
833 self.integer(opcode, arg(1), 1);
834 if results == 1 {
835 self.produces(opcode, res(0), arg(1));
836 }
837 }
838 }
839 Opcode::Cmpxchg => {
840 if self.takes(opcode, arity, 3) {
841 self.pointer(opcode, arg(0), 0);
842 self.agree(opcode, arg(1), arg(2));
843 if results == 2 {
844 self.produces(opcode, res(0), arg(1));
845 self.produces(opcode, res(1), arg(1).with_lane(Type::I1));
846 }
847 }
848 }
849 Opcode::Fence | Opcode::Unreachable | Opcode::UnreachableHint => {
850 self.takes(opcode, arity, 0);
851 }
852 Opcode::Prefetch | Opcode::StackRestore | Opcode::VaStart | Opcode::VaEnd => {
853 if self.takes(opcode, arity, 1) {
854 self.pointer(opcode, arg(0), 0);
855 }
856 }
857 Opcode::VaCopy => {
858 if self.takes(opcode, arity, 2) {
859 self.pointer(opcode, arg(0), 0);
860 self.pointer(opcode, arg(1), 1);
861 }
862 }
863 Opcode::VaArg => {
864 if self.takes(opcode, arity, 1) {
865 self.pointer(opcode, arg(0), 0);
866 }
867 if results == 1 && res(0).is_void() {
868 self.error("va_arg reads a value and void is not one");
869 }
870 }
871
872 Opcode::Jump => {
874 self.takes(opcode, arity, 0);
875 self.targets(func, inst, 1);
876 }
877 Opcode::BrIf => {
878 if self.takes(opcode, arity, 1) && arg(0) != Type::I1 {
879 self.error(format!("br_if branches on an i1 and this one on {}", arg(0)));
880 }
881 self.targets(func, inst, 2);
882 }
883 Opcode::Switch => {
884 if self.takes(opcode, arity, 1) {
885 self.integer(opcode, arg(0), 0);
886 }
887 }
888 Opcode::BlockAddr => {
889 self.takes(opcode, arity, 0);
890 self.targets(func, inst, 1);
891 if results == 1 && !res(0).is_ptr() {
892 self.error(format!(
893 "block_addr produces a pointer and this one produces {}",
894 res(0)
895 ));
896 }
897 if func.successors(inst).any(|call| !func[call.args].is_empty()) {
898 self.error("block_addr names a block and passes it arguments");
901 }
902 }
903 Opcode::IndirectBr => {
904 if self.takes(opcode, arity, 1) {
905 self.pointer(opcode, arg(0), 0);
906 }
907 }
910 Opcode::Return => {
911 let want = &func.signature().returns;
912 if arity != want.len() {
913 self.error(format!(
914 "the signature returns {} and this returns {arity}",
915 want.len()
916 ));
917 } else {
918 for (index, ty) in want.iter().map(|param| param.ty).enumerate() {
919 if arg(index) != ty {
920 self.error(format!(
921 "result {} of the signature is {ty} and this returns {}",
922 index + 1,
923 arg(index)
924 ));
925 }
926 }
927 }
928 }
929
930 Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => {
932 let Extra::Call(at) = data.extra else { return };
933 let info = func[at];
934 let signature = &func[info.signature];
935 let indirect = usize::from(opcode == Opcode::CallIndirect);
936 if indirect == 1 {
937 if arity == 0 {
938 self.error("call_indirect calls through a pointer and has no operands");
939 return;
940 }
941 self.pointer(opcode, arg(0), 0);
942 }
943 let passed = arity - indirect;
944 let enough = if signature.variadic {
945 passed >= signature.params.len()
946 } else {
947 passed == signature.params.len()
948 };
949 if enough {
950 for (index, ty) in signature.param_types().enumerate() {
951 if arg(index + indirect) != ty {
952 self.error(format!(
953 "parameter {} of the signature is {ty} and this argument is {}",
954 index + 1,
955 arg(index + indirect)
956 ));
957 }
958 }
959 } else {
960 self.error(format!(
961 "the signature takes {}{} and this call passes {passed}",
962 signature.params.len(),
963 if signature.variadic { " or more" } else { "" }
964 ));
965 }
966 if results != signature.returns.len() {
967 self.error(format!(
968 "the signature returns {} and this call produces {results}",
969 signature.returns.len()
970 ));
971 } else {
972 for (index, ty) in signature.return_types().enumerate() {
973 if res(index) != ty {
974 self.error(format!(
975 "result {} of the signature is {ty} and this call produces {}",
976 index + 1,
977 res(index)
978 ));
979 }
980 }
981 }
982 if let Some(callee) = info.callee {
985 if let Some(SymbolRef::Func(id)) = self.module.lookup(callee) {
986 if self.module[id].signature() != signature {
987 self.error(format!(
988 "@{} is declared here with another signature",
989 self.names.resolve(callee)
990 ));
991 }
992 }
993 }
994 }
995
996 Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop | Opcode::Bswap | Opcode::Bitreverse => {
998 if self.takes(opcode, arity, 1) {
999 self.integer(opcode, arg(0), 0);
1000 if results == 1 {
1001 self.produces(opcode, res(0), arg(0));
1002 }
1003 }
1004 }
1005
1006 Opcode::SAddOverflow
1008 | Opcode::UAddOverflow
1009 | Opcode::SSubOverflow
1010 | Opcode::USubOverflow
1011 | Opcode::SMulOverflow
1012 | Opcode::UMulOverflow => {
1013 if self.takes(opcode, arity, 2) {
1014 self.integer(opcode, arg(0), 0);
1015 self.agree(opcode, arg(0), arg(1));
1016 if results == 2 {
1017 self.produces(opcode, res(0), arg(0));
1018 self.produces(opcode, res(1), arg(0).with_lane(Type::I1));
1019 }
1020 }
1021 }
1022 Opcode::Expect => {
1023 if self.takes(opcode, arity, 2) {
1024 self.agree(opcode, arg(0), arg(1));
1025 if results == 1 {
1026 self.produces(opcode, res(0), arg(0));
1027 }
1028 }
1029 }
1030
1031 Opcode::SetjmpMarker
1035 | Opcode::LongjmpMarker
1036 | Opcode::InlineAsm
1037 | Opcode::TargetIntrinsic => {}
1038 }
1039 }
1040
1041 fn takes(&mut self, opcode: Opcode, got: usize, want: usize) -> bool {
1045 if got == want {
1046 return true;
1047 }
1048 self.error(format!("{} takes {want} operands and this one has {got}", opcode.name()));
1049 false
1050 }
1051
1052 fn targets(&mut self, func: &'a Func, inst: Inst, want: usize) {
1054 let got = func.successors(inst).count();
1055 if got != want {
1056 self.error(format!(
1057 "{} branches to {want} blocks and this one to {got}",
1058 func[inst].opcode.name()
1059 ));
1060 }
1061 }
1062
1063 fn integer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1064 if !ty.lane().is_int() {
1065 self.error(format!(
1066 "operand {} of {} is an integer and this one is {ty}",
1067 n + 1,
1068 opcode.name()
1069 ));
1070 }
1071 }
1072
1073 fn floating(&mut self, opcode: Opcode, ty: Type, n: usize) {
1074 if !ty.lane().is_float() {
1075 self.error(format!(
1076 "operand {} of {} is a floating point value and this one is {ty}",
1077 n + 1,
1078 opcode.name()
1079 ));
1080 }
1081 }
1082
1083 fn pointer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1084 if !ty.is_ptr() {
1085 self.error(format!(
1086 "operand {} of {} is a pointer and this one is {ty}",
1087 n + 1,
1088 opcode.name()
1089 ));
1090 }
1091 }
1092
1093 fn agree(&mut self, opcode: Opcode, first: Type, second: Type) {
1095 if first != second {
1096 self.error(format!(
1097 "the operands of {} have one type and these are {first} and {second}",
1098 opcode.name()
1099 ));
1100 }
1101 }
1102
1103 fn produces(&mut self, opcode: Opcode, got: Type, want: Type) {
1105 if got != want {
1106 self.error(format!(
1107 "{} produces {want} here and this one produces {got}",
1108 opcode.name()
1109 ));
1110 }
1111 }
1112
1113 fn lanes(&mut self, opcode: Opcode, to: Type, from: Type) {
1115 if to.lanes() != from.lanes() {
1116 self.error(format!(
1117 "{} keeps the lane count and {from} has {} and {to} has {}",
1118 opcode.name(),
1119 from.lanes(),
1120 to.lanes()
1121 ));
1122 }
1123 }
1124
1125 fn widens(&mut self, opcode: Opcode, to: Type, from: Type, wider: bool) {
1127 let (a, b) = (to.lane().bits(), from.lane().bits());
1128 let ok = if wider { a > b } else { a < b };
1129 if !ok {
1130 let way = if wider { "wider" } else { "narrower" };
1131 self.error(format!(
1132 "{} produces something {way} and {from} to {to} is not",
1133 opcode.name()
1134 ));
1135 }
1136 }
1137
1138 fn error(&mut self, message: impl Into<String>) {
1141 let at = self.locate();
1142 self.at(at, message);
1143 }
1144
1145 fn at(&mut self, at: String, message: impl Into<String>) {
1146 self.errors.push(VerifyError { at, message: message.into() });
1147 }
1148
1149 fn locate(&self) -> String {
1155 use fmt::Write as _;
1156 let mut at = String::new();
1157 if let Some(func) = self.func {
1158 let _ = write!(at, "@{}", self.names.resolve(func.name));
1159 if let Some(block) = self.block {
1160 let _ = write!(at, " block{}", block.raw());
1161 }
1162 if let Some(inst) = self.inst {
1163 let _ = write!(at, " {}", func[inst].opcode.name());
1164 }
1165 }
1166 at
1167 }
1168}
1169
1170struct Layout {
1173 block: Vec<Option<Block>>,
1174 position: Vec<u32>,
1175}
1176
1177impl Layout {
1178 fn new(func: &Func) -> Self {
1179 let counts = func.counts();
1180 let mut layout =
1181 Layout { block: vec![None; counts.insts], position: vec![0; counts.insts] };
1182 for block in func.blocks() {
1183 for (position, inst) in func.insts(block).enumerate() {
1184 layout.block[inst.index()] = Some(block);
1185 layout.position[inst.index()] = position as u32;
1186 }
1187 }
1188 layout
1189 }
1190
1191 fn block_of(&self, inst: Inst) -> Option<Block> {
1192 self.block[inst.index()]
1193 }
1194
1195 fn position(&self, inst: Inst) -> u32 {
1196 self.position[inst.index()]
1197 }
1198}
1199
1200struct Doms {
1207 rank: Vec<Option<u32>>,
1210 idom: Vec<u32>,
1212}
1213
1214impl Doms {
1215 fn new(func: &Func) -> Self {
1216 let counts = func.counts();
1217 let entry = func.entry().expect("a function with blocks has a first one");
1218
1219 let mut succs: Vec<Vec<Block>> = vec![Vec::new(); counts.blocks];
1227 for block in func.blocks() {
1228 for inst in func.insts(block) {
1229 succs[block.index()].extend(func.successors(inst).map(|call| call.block));
1230 }
1231 }
1232
1233 let mut order = Vec::new();
1236 let mut seen = vec![false; counts.blocks];
1237 let mut stack = vec![(entry, 0usize)];
1238 seen[entry.index()] = true;
1239 while let Some((block, next)) = stack.pop() {
1240 match succs[block.index()].get(next) {
1241 Some(&target) => {
1242 stack.push((block, next + 1));
1243 if !seen[target.index()] {
1244 seen[target.index()] = true;
1245 stack.push((target, 0));
1246 }
1247 }
1248 None => order.push(block),
1249 }
1250 }
1251 order.reverse();
1252
1253 let mut rank = vec![None; counts.blocks];
1254 for (index, &block) in order.iter().enumerate() {
1255 rank[block.index()] = Some(index as u32);
1256 }
1257 let mut preds: Vec<Vec<u32>> = vec![Vec::new(); order.len()];
1258 for (index, &block) in order.iter().enumerate() {
1259 for &target in &succs[block.index()] {
1260 if let Some(target) = rank[target.index()] {
1261 preds[target as usize].push(index as u32);
1262 }
1263 }
1264 }
1265
1266 const NONE: u32 = u32::MAX;
1270 let mut idom = vec![NONE; order.len()];
1271 if !order.is_empty() {
1272 idom[0] = 0;
1273 }
1274 let mut changed = true;
1275 while changed {
1276 changed = false;
1277 for index in 1..order.len() {
1278 let mut new = NONE;
1279 for &pred in &preds[index] {
1280 if idom[pred as usize] == NONE {
1281 continue;
1282 }
1283 new = if new == NONE { pred } else { meet(&idom, new, pred) };
1284 }
1285 if new != NONE && idom[index] != new {
1286 idom[index] = new;
1287 changed = true;
1288 }
1289 }
1290 }
1291 Doms { rank, idom }
1292 }
1293
1294 fn reaches(&self, block: Block) -> bool {
1296 self.rank[block.index()].is_some()
1297 }
1298
1299 fn dominates(&self, of: Block, block: Block) -> bool {
1305 let (Some(a), Some(b)) = (self.rank[of.index()], self.rank[block.index()]) else {
1306 return true;
1307 };
1308 let mut walk = b;
1309 while walk > a {
1310 walk = self.idom[walk as usize];
1311 }
1312 walk == a
1313 }
1314}
1315
1316fn meet(idom: &[u32], mut a: u32, mut b: u32) -> u32 {
1318 while a != b {
1319 while a > b {
1320 a = idom[a as usize];
1321 }
1322 while b > a {
1323 b = idom[b as usize];
1324 }
1325 }
1326 a
1327}
1328
1329fn width(ty: Type) -> u64 {
1331 u64::from(ty.bits()) * u64::from(ty.lanes())
1332}
1333
1334fn types(list: &[Type]) -> String {
1336 if list.is_empty() {
1337 return "nothing".to_string();
1338 }
1339 list.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
1340}
1341
1342#[cfg(test)]
1343mod tests {
1344 use rucc_base::{Idx, Interner, Symbol};
1345 use rucc_diag::Span;
1346 use rucc_target::{Arch, Env, Os, Triple};
1347
1348 use super::*;
1349 use crate::fixtures::{EXAMPLE, SYMBOLS, ZOO};
1350 use crate::func::Builder;
1351 use crate::inst::{InstData, MetaNode, Signature};
1352 use crate::{Flags, IntPred, parse};
1353
1354 fn target() -> TargetInfo {
1355 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1356 }
1357
1358 fn errors(text: &str) -> Vec<String> {
1361 let mut names = Interner::new();
1362 let module = match parse(text, &mut names) {
1363 Ok(module) => module,
1364 Err(error) => panic!("{error}"),
1365 };
1366 match verify(&module, &names) {
1367 Ok(()) => Vec::new(),
1368 Err(errors) => errors.iter().map(ToString::to_string).collect(),
1369 }
1370 }
1371
1372 fn only(text: &str) -> String {
1374 let found = errors(text);
1375 assert_eq!(found.len(), 1, "{found:#?}");
1376 found.into_iter().next().expect("just counted one")
1377 }
1378
1379 const HEADER: &str = "\
1380; ModuleID = 'bad.c'
1381; format 0
1382target triple = \"x86_64-unknown-linux-gnu\"
1383target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
1384";
1385
1386 fn wrap(signature: &str, body: &str) -> String {
1388 format!("{HEADER}\nfunc @f{signature}, linkage(external) {{\n{body}}}\n")
1389 }
1390
1391 #[test]
1392 fn the_three_fixtures_are_modules_the_compiler_may_believe() {
1393 for text in [EXAMPLE, ZOO, SYMBOLS] {
1394 assert_eq!(errors(text), Vec::<String>::new());
1395 }
1396 }
1397
1398 #[test]
1399 fn a_use_that_its_definition_does_not_reach_is_reported() {
1400 let text = wrap(
1401 "(i1) -> i32",
1402 "block0(%0: i1):
1403 br_if %0, block1, block2
1404
1405block1:
1406 %1 = iconst.i32 7
1407 jump block2
1408
1409block2:
1410 return %1
1411",
1412 );
1413 assert_eq!(
1414 only(&text),
1415 "@f block2 return: %1 is produced in block1 and does not reach here"
1416 );
1417 }
1418
1419 #[test]
1420 fn a_use_before_its_definition_in_the_same_block_is_reported() {
1421 let text = wrap(
1422 "() -> i32",
1423 "block0:
1424 %0 = iconst.i32 1
1425 %1 = add %2, %0
1426 %2 = iconst.i32 2
1427 return %1
1428",
1429 );
1430 assert_eq!(only(&text), "@f block0 add: %2 is produced in block0 and does not reach here");
1431 }
1432
1433 #[test]
1434 fn a_call_that_takes_its_arguments_the_way_the_abi_says_is_believed() {
1435 let text = wrap(
1436 "(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext)",
1437 "block0(%0: ptr, %1: ptr, %2: i8):
1438 return
1439",
1440 );
1441 assert_eq!(errors(&text), Vec::<String>::new());
1442 }
1443
1444 #[test]
1445 fn an_sret_that_is_not_the_first_parameter_is_reported() {
1446 let text = wrap(
1447 "(ptr byval(8, align 8), ptr sret(16, align 8))",
1448 "block0(%0: ptr, %1: ptr):
1449 return
1450",
1451 );
1452 assert_eq!(only(&text), "@f: sret is the first parameter and this one is not");
1453 }
1454
1455 #[test]
1456 fn a_function_returning_through_sret_returns_nothing_else() {
1457 let text = wrap(
1458 "(ptr sret(8, align 8)) -> i32",
1459 "block0(%0: ptr):
1460 %1 = iconst.i32 7
1461 return %1
1462",
1463 );
1464 assert_eq!(only(&text), "@f: a signature returning through sret returns nothing else");
1465 }
1466
1467 #[test]
1468 fn an_object_that_travels_indirectly_travels_behind_a_pointer() {
1469 let text = wrap(
1470 "(i32 byval(4, align 4))",
1471 "block0(%0: i32):
1472 return
1473",
1474 );
1475 assert_eq!(only(&text), "@f: parameter 1 travels indirectly and i32 is not a pointer");
1476 }
1477
1478 #[test]
1479 fn an_alignment_a_parameter_could_not_have_is_reported() {
1480 let text = wrap(
1481 "(ptr byval(24, align 3))",
1482 "block0(%0: ptr):
1483 return
1484",
1485 );
1486 assert_eq!(only(&text), "@f: an alignment is a power of two and this is 3");
1487 }
1488
1489 #[test]
1490 fn a_result_does_not_travel_indirectly() {
1491 let text = wrap(
1494 "() -> ptr byval(16, align 8)",
1495 "block0:
1496 %0 = iconst.i64 0
1497 %1 = inttoptr.ptr %0
1498 return %1
1499",
1500 );
1501 assert_eq!(only(&text), "@f: result 1 travels indirectly and a result cannot");
1502 }
1503
1504 #[test]
1505 fn an_extension_is_asked_of_an_integer_and_not_of_anything_else() {
1506 let text = wrap(
1507 "(ptr zext)",
1508 "block0(%0: ptr):
1509 return
1510",
1511 );
1512 assert_eq!(only(&text), "@f: parameter 1 is extended and ptr is not an integer");
1513 }
1514
1515 #[test]
1516 fn a_branch_that_passes_the_wrong_number_of_arguments_is_reported() {
1517 let text = wrap(
1518 "(i32)",
1519 "block0(%0: i32):
1520 jump block1(%0)
1521
1522block1:
1523 return
1524",
1525 );
1526 assert_eq!(
1527 only(&text),
1528 "@f block0 jump: block1 takes 0 arguments and this branch passes 1"
1529 );
1530 }
1531
1532 #[test]
1533 fn a_branch_that_passes_the_wrong_type_is_reported() {
1534 let text = wrap(
1535 "(i32) -> i32",
1536 "block0(%0: i32):
1537 %1 = sext.i64 %0
1538 jump block1(%1)
1539
1540block1(%2: i32):
1541 return %2
1542",
1543 );
1544 assert_eq!(only(&text), "@f block0 jump: argument 1 to block1 is i32 and this one is i64");
1545 }
1546
1547 #[test]
1548 fn a_block_that_does_not_end_in_a_terminator_is_reported() {
1549 let text = wrap("()", "block0:\n %0 = iconst.i32 1\n");
1550 assert_eq!(only(&text), "@f block0: this block does not end in a terminator");
1551 }
1552
1553 #[test]
1554 fn an_instruction_after_the_terminator_is_reported() {
1555 let text = wrap("()", "block0:\n return\n %0 = iconst.i32 1\n");
1556 assert_eq!(only(&text), "@f block0 iconst: this comes after the block's terminator");
1557 }
1558
1559 #[test]
1560 fn an_unreachable_block_is_reported() {
1561 let text = wrap("()", "block0:\n return\n\nblock1:\n return\n");
1562 assert_eq!(only(&text), "@f block1: this block is not reachable and has not been deleted");
1563 }
1564
1565 #[test]
1566 fn a_block_a_jump_to_an_address_arrives_at_is_an_ordinary_target() {
1567 let text = wrap(
1568 "(ptr) -> i32",
1569 "block0(%0: ptr):
1570 %1 = block_addr block1
1571 indirect_br %0, block1
1572
1573block1:
1574 %2 = iconst.i32 1
1575 return %2
1576",
1577 );
1578 assert_eq!(errors(&text), Vec::<String>::new());
1579 }
1580
1581 #[test]
1582 fn a_block_whose_address_is_taken_is_reached_by_the_taking_of_it() {
1583 let text = wrap(
1586 "() -> ptr",
1587 "block0:
1588 %0 = block_addr block1
1589 return %0
1590
1591block1:
1592 unreachable
1593",
1594 );
1595 assert_eq!(errors(&text), Vec::<String>::new());
1596 }
1597
1598 #[test]
1599 fn taking_the_address_of_a_block_and_passing_it_arguments_is_reported() {
1600 let text = wrap(
1603 "(i32) -> ptr",
1604 "block0(%0: i32):
1605 %1 = block_addr block1(%0)
1606 return %1
1607
1608block1(%2: i32):
1609 unreachable
1610",
1611 );
1612 assert_eq!(
1613 only(&text),
1614 "@f block0 block_addr: block_addr names a block and passes it arguments"
1615 );
1616 }
1617
1618 #[test]
1619 fn a_jump_to_something_that_is_not_an_address_is_reported() {
1620 let text = wrap(
1621 "(i32)",
1622 "block0(%0: i32):
1623 indirect_br %0, block1
1624
1625block1:
1626 return
1627",
1628 );
1629 assert_eq!(
1630 only(&text),
1631 "@f block0 indirect_br: operand 1 of indirect_br is a pointer and this one is i32"
1632 );
1633 }
1634
1635 #[test]
1636 fn a_branch_back_to_the_entry_block_is_reported() {
1637 let text = wrap(
1640 "(i32)",
1641 "block0(%0: i32):
1642 jump block1
1643
1644block1:
1645 jump block0(%0)
1646",
1647 );
1648 assert_eq!(
1649 only(&text),
1650 "@f block0: the entry block is branched to, and it takes the arguments"
1651 );
1652 }
1653
1654 #[test]
1655 fn an_entry_block_that_does_not_take_the_arguments_is_reported() {
1656 let text = wrap("(i32)", "block0(%0: i64):\n return\n");
1657 assert_eq!(only(&text), "@f: the entry block takes i64 and the signature says i32");
1658 }
1659
1660 #[test]
1661 fn a_flag_the_opcode_does_not_read_is_reported() {
1662 let text =
1663 wrap("(i32) -> i32", "block0(%0: i32):\n %1 = add.exact %0, %0\n return %1\n");
1664 assert_eq!(only(&text), "@f block0 add: add does not read `exact`");
1665 }
1666
1667 #[test]
1668 fn an_ordering_the_operation_cannot_be_asked_for_is_reported() {
1669 let text = wrap(
1670 "(ptr) -> i32",
1671 "block0(%0: ptr):\n %1 = atomic_load.i32 %0, align 4, release\n return %1\n",
1672 );
1673 assert_eq!(only(&text), "@f block0 atomic_load: atomic_load cannot be asked for release");
1674 }
1675
1676 #[test]
1677 fn an_ordering_on_the_non_atomic_form_is_reported() {
1678 let text = wrap(
1679 "(ptr) -> i32",
1680 "block0(%0: ptr):\n %1 = load.i32 %0, align 4, acquire\n return %1\n",
1681 );
1682 assert_eq!(only(&text), "@f block0 load: load cannot be asked for acquire");
1683 }
1684
1685 #[test]
1686 fn an_alloca_of_a_fixed_size_outside_the_entry_block_is_reported() {
1687 let text = wrap(
1688 "()",
1689 "block0:
1690 jump block1
1691
1692block1:
1693 %0 = alloca, size 16, align 8
1694 return
1695",
1696 );
1697 assert_eq!(
1698 only(&text),
1699 "@f block1 alloca: an alloca of a fixed size belongs in the entry block"
1700 );
1701 }
1702
1703 #[test]
1704 fn a_dynamic_alloca_may_be_anywhere() {
1705 let text = wrap(
1706 "(i64)",
1707 "block0(%0: i64):
1708 jump block1
1709
1710block1:
1711 %1 = alloca %0, align 8
1712 return
1713",
1714 );
1715 assert_eq!(errors(&text), Vec::<String>::new());
1716 }
1717
1718 #[test]
1719 fn two_cases_of_a_switch_with_the_same_value_are_reported() {
1720 let text = wrap(
1721 "(i32)",
1722 "block0(%0: i32):
1723 switch %0, block1, [7 => block1, 7 => block1]
1724
1725block1:
1726 return
1727",
1728 );
1729 assert_eq!(only(&text), "@f block0 switch: two cases of this switch have the same value");
1730 }
1731
1732 #[test]
1733 fn operands_that_do_not_agree_are_reported() {
1734 let text = wrap(
1735 "(i32, i64) -> i32",
1736 "block0(%0: i32, %1: i64):\n %2 = add %0, %1\n return %2\n",
1737 );
1738 assert_eq!(
1739 only(&text),
1740 "@f block0 add: the operands of add have one type and these are i32 and i64"
1741 );
1742 }
1743
1744 #[test]
1745 fn a_conversion_that_goes_the_wrong_way_is_reported() {
1746 let text = wrap("(i32) -> i64", "block0(%0: i32):\n %1 = trunc.i64 %0\n return %1\n");
1747 assert_eq!(
1748 only(&text),
1749 "@f block0 trunc: trunc produces something narrower and i32 to i64 is not"
1750 );
1751 }
1752
1753 #[test]
1754 fn a_bitcast_between_an_address_and_a_number_is_reported() {
1755 let text =
1756 wrap("(ptr) -> i64", "block0(%0: ptr):\n %1 = bitcast.i64 %0\n return %1\n");
1757 assert_eq!(
1758 only(&text),
1759 "@f block0 bitcast: a bitcast between a pointer and a number is ptrtoint or inttoptr"
1760 );
1761 }
1762
1763 #[test]
1764 fn an_operand_of_the_wrong_kind_is_reported() {
1765 let text = wrap("(i32) -> i32", "block0(%0: i32):\n %1 = fadd %0, %0\n return %1\n");
1766 assert_eq!(
1767 only(&text),
1768 "@f block0 fadd: operand 1 of fadd is a floating point value and this one is i32"
1769 );
1770 }
1771
1772 #[test]
1773 fn a_condition_that_is_not_one_bit_is_reported() {
1774 let text = wrap(
1775 "(i32)",
1776 "block0(%0: i32):
1777 br_if %0, block1, block1
1778
1779block1:
1780 return
1781",
1782 );
1783 assert_eq!(only(&text), "@f block0 br_if: br_if branches on an i1 and this one on i32");
1784 }
1785
1786 #[test]
1787 fn a_return_that_does_not_match_the_signature_is_reported() {
1788 let text = wrap("() -> i32", "block0:\n return\n");
1789 assert_eq!(only(&text), "@f block0 return: the signature returns 1 and this returns 0");
1790 }
1791
1792 #[test]
1793 fn a_call_that_disagrees_with_the_declaration_is_reported() {
1794 let text = format!(
1795 "{HEADER}
1796func @g(i32, ...) -> i32, linkage(external);
1797
1798func @f(i32) -> i32, linkage(external) {{
1799block0(%0: i32):
1800 %1 = call @g(%0) : (i32) -> i32
1801 return %1
1802}}
1803"
1804 );
1805 assert_eq!(only(&text), "@f block0 call: @g is declared here with another signature");
1806 }
1807
1808 #[test]
1809 fn a_global_whose_image_is_not_its_size_is_reported() {
1810 let text =
1811 format!("{HEADER}\nglobal @x : bytes 8 = {{ i32 7 }}, align 4, linkage(external)\n");
1812 assert_eq!(only(&text), "@x: the image is 4 bytes and the global is 8");
1813 }
1814
1815 #[test]
1816 fn a_pointer_in_an_image_has_no_width_and_is_reported() {
1817 let text = format!(
1821 "{HEADER}\nglobal @x : bytes 8 = {{ ptr 0x0, zero 8 }}, align 8, linkage(external)\n"
1822 );
1823 assert_eq!(only(&text), "@x: a scalar in an image has a width and ptr has none");
1824 }
1825
1826 #[test]
1827 fn a_declaration_of_something_another_module_defines_may_be_constant() {
1828 let text = format!("{HEADER}\nglobal @x : bytes 4, align 4, linkage(external), constant\n");
1831 assert!(errors(&text).is_empty(), "{:?}", errors(&text));
1832 }
1833
1834 #[test]
1835 fn an_alias_to_itself_is_reported() {
1836 let text = format!("{HEADER}\nalias @a = @a, linkage(external)\n");
1837 assert_eq!(only(&text), "@a: an alias to itself");
1838 }
1839
1840 #[test]
1841 fn an_ifunc_that_does_not_resolve_through_a_function_is_reported() {
1842 let text = format!(
1843 "{HEADER}
1844global @g : i32 = 0, align 4, linkage(external)
1845
1846ifunc @f = @g, linkage(external)
1847"
1848 );
1849 assert_eq!(
1850 only(&text),
1851 "@f: an ifunc resolves through a function and this target is not one"
1852 );
1853 }
1854
1855 #[test]
1856 fn attributes_that_contradict_each_other_are_reported() {
1857 let text =
1858 format!("{HEADER}\nfunc @f(), linkage(external), attrs(always_inline, noinline);\n");
1859 assert_eq!(
1860 only(&text),
1861 "@f: `always_inline` and `noinline` cannot both be true of a function"
1862 );
1863 }
1864
1865 fn one_error(module: &Module, func: &Func, names: &Interner) -> String {
1869 match verify_func(module, func, names) {
1870 Ok(()) => panic!("that was expected to be turned down"),
1871 Err(errors) => {
1872 assert_eq!(errors.len(), 1, "{errors:#?}");
1873 errors[0].to_string()
1874 }
1875 }
1876 }
1877
1878 #[test]
1879 fn a_value_the_function_does_not_have_is_reported() {
1880 let mut names = Interner::new();
1881 let module = Module::new(names.intern("built.c"), &target());
1882 let mut func = Func::new(names.intern("f"), Signature::new());
1883 let block = func.create_block();
1884 let args = func.push_values(&[Value::from_usize(9)]);
1885 let inst =
1886 func.create_inst(InstData { args, ..InstData::new(Opcode::Return) }, &[], Span::DUMMY);
1887 func.append_inst(block, inst);
1888 assert_eq!(
1889 one_error(&module, &func, &names),
1890 "@f block0 return: %9 is not a value of this function"
1891 );
1892 }
1893
1894 #[test]
1895 fn a_value_whose_definition_has_been_taken_out_is_reported() {
1896 let mut names = Interner::new();
1897 let module = Module::new(names.intern("built.c"), &target());
1898 let i32_ = Type::int(32);
1899 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
1900 let block = func.create_block();
1901 let mut b = Builder::new(&mut func, block);
1902 let value = b.iconst(i32_, 7);
1903 b.ret(&[value]);
1904 let Def::Result { inst, .. } = func[value].def else { unreachable!("a constant") };
1905 func.remove_inst(inst);
1906 assert_eq!(
1907 one_error(&module, &func, &names),
1908 "@f block0 return: %0 is produced by an instruction that is not in the function"
1909 );
1910 }
1911
1912 #[test]
1913 fn an_instruction_carrying_another_opcodes_payload_is_reported() {
1914 let mut names = Interner::new();
1917 let module = Module::new(names.intern("built.c"), &target());
1918 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1919 let block = func.create_block();
1920 let param = func.append_param(block, Type::int(32));
1921 let args = func.push_values(&[param, param]);
1922 let inst = func.create_inst(
1923 InstData { args, extra: Extra::IntPred(IntPred::Eq), ..InstData::new(Opcode::Add) },
1924 &[Type::int(32)],
1925 Span::DUMMY,
1926 );
1927 func.append_inst(block, inst);
1928 let ret = func.create_inst(InstData::new(Opcode::Return), &[], Span::DUMMY);
1929 func.append_inst(block, ret);
1930 assert_eq!(
1931 one_error(&module, &func, &names),
1932 "@f block0 add: add carries nothing and this one carries an integer comparison"
1933 );
1934 }
1935
1936 #[test]
1937 fn a_metadata_node_that_is_its_own_parent_is_reported() {
1938 let mut names = Interner::new();
1939 let mut module = Module::new(names.intern("built.c"), &target());
1940 module.add_meta(MetaNode {
1941 name: names.intern("int"),
1942 parent: Some(Idx::from_usize(0)),
1943 offset: 0,
1944 });
1945 let found = match verify(&module, &names) {
1946 Ok(()) => panic!("that was expected to be turned down"),
1947 Err(errors) => errors,
1948 };
1949 assert_eq!(found.len(), 1, "{found:#?}");
1950 assert_eq!(
1951 found[0].to_string(),
1952 "!0: a metadata node's parent comes before it and this one is !0"
1953 );
1954 }
1955
1956 #[test]
1957 fn a_datalayout_the_target_does_not_imply_is_reported() {
1958 let mut names = Interner::new();
1959 let mut module = Module::new(names.intern("built.c"), &target());
1960 module.datalayout = DataLayout::parse("e-p:32:32-i64:64-f80:32-S64").expect("a layout");
1961 let found = match verify(&module, &names) {
1962 Ok(()) => panic!("that was expected to be turned down"),
1963 Err(errors) => errors,
1964 };
1965 assert_eq!(found.len(), 1, "{found:#?}");
1966 assert!(found[0].to_string().starts_with("@built.c: the datalayout is "), "{}", found[0]);
1967 }
1968
1969 #[test]
1970 fn a_flag_riding_along_where_it_is_read_is_not_reported() {
1971 let mut names = Interner::new();
1972 let module = Module::new(names.intern("built.c"), &target());
1973 let i32_ = Type::int(32);
1974 let mut func = Func::new(
1975 names.intern("f"),
1976 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
1977 );
1978 let block = func.create_block();
1979 let param = func.append_param(block, i32_);
1980 let mut b = Builder::new(&mut func, block);
1981 let sum = b.binary(Opcode::Add, param, param, Flags::NSW);
1982 b.ret(&[sum]);
1983 assert!(verify_func(&module, &func, &names).is_ok());
1984 }
1985
1986 #[test]
1987 fn a_declaration_is_checked_and_has_nothing_else_to_check() {
1988 let mut names = Interner::new();
1989 let module = Module::new(names.intern("built.c"), &target());
1990 let func = Func::new(Symbol::from_raw(0), Signature::new());
1991 assert!(func.is_declaration());
1992 assert!(verify_func(&module, &func, &names).is_ok());
1993 }
1994}