1use std::fmt;
43
44use rucc_base::Interner;
45use rucc_target::TargetInfo;
46
47use crate::func::Func;
48use crate::inst::{Abi, Block, CallInfo, 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 varargs(
314 &mut self,
315 func: &'a Func,
316 info: &CallInfo,
317 passed: usize,
318 arg: impl Fn(usize) -> Type,
319 ) {
320 let varargs = &func[info.varargs];
321 if varargs.is_empty() {
322 return;
323 }
324 let named = func[info.signature].params.len();
325 if !func[info.signature].variadic {
326 self.error("this call is not variadic and says how a variadic argument travels");
327 return;
328 }
329 if varargs.len() != passed.saturating_sub(named) {
330 self.error(format!(
331 "the call passes {} arguments the signature does not name and says how {} travel",
332 passed.saturating_sub(named),
333 varargs.len()
334 ));
335 return;
336 }
337 for (index, &abi) in varargs.iter().enumerate() {
338 let at = format!("argument {}", named + index + 1);
339 if matches!(abi, Abi::Sret { .. }) {
340 self.error(format!("{at} is an sret and only a parameter can be one"));
341 continue;
342 }
343 self.abi(&at, &Param { ty: arg(index + named), abi });
344 }
345 }
346
347 fn abi(&mut self, at: &str, param: &Param) {
349 match param.abi {
350 Abi::Plain => {}
351 Abi::Sext | Abi::Zext => {
352 if !param.ty.is_int() || param.ty.is_vector() {
353 self.error(format!("{at} is extended and {} is not an integer", param.ty));
354 }
355 }
356 Abi::ByVal { size, align } | Abi::Sret { size, align } => {
357 if !param.ty.is_ptr() {
358 self.error(format!(
359 "{at} travels indirectly and {} is not a pointer",
360 param.ty
361 ));
362 }
363 if !align.is_power_of_two() {
364 self.error(format!("an alignment is a power of two and this is {align}"));
365 }
366 if size == 0 {
367 self.error(format!("{at} travels indirectly and has no size"));
368 }
369 }
370 }
371 }
372
373 fn bounds(&mut self, func: &'a Func) -> bool {
379 let counts = func.counts();
380 let before = self.errors.len();
381 for block in func.blocks() {
382 self.block = Some(block);
383 for &value in &func[block].params {
384 if value.index() >= counts.values {
385 self.error(format!(
386 "parameter %{} is not a value of this function",
387 value.raw()
388 ));
389 }
390 }
391 for inst in func.insts(block) {
392 self.inst = Some(inst);
393 for &value in &func[func[inst].args] {
394 if value.index() >= counts.values {
395 self.error(format!("%{} is not a value of this function", value.raw()));
396 }
397 }
398 for value in func[inst].results() {
399 if value.index() >= counts.values {
400 self.error(format!("%{} is not a value of this function", value.raw()));
401 }
402 }
403 for call in func.successors(inst) {
404 if call.block.index() >= counts.blocks {
405 self.error(format!(
406 "block{} is not a block of this function",
407 call.block.raw()
408 ));
409 }
410 for &value in &func[call.args] {
411 if value.index() >= counts.values {
412 self.error(format!("%{} is not a value of this function", value.raw()));
413 }
414 }
415 }
416 }
417 self.inst = None;
418 }
419 self.block = None;
420 self.errors.len() == before
421 }
422
423 fn block(&mut self, func: &'a Func, block: Block, doms: &Doms, layout: &Layout) {
424 if !doms.reaches(block) {
425 self.error("this block is not reachable and has not been deleted");
426 }
427 if block == func.entry().expect("checked in func") {
428 for other in func.blocks() {
429 let last = func[other].last;
430 if last.is_some_and(|inst| func.successors(inst).any(|call| call.block == block)) {
431 self.error("the entry block is branched to, and it takes the arguments");
432 }
433 }
434 }
435
436 let mut seen_terminator = false;
437 for inst in func.insts(block) {
438 self.inst = Some(inst);
439 if seen_terminator {
440 self.error("this comes after the block's terminator");
441 }
442 seen_terminator |= func.is_terminator(inst);
443 self.inst(func, inst, doms, layout);
444 }
445 self.inst = None;
446 if !seen_terminator {
447 self.error("this block does not end in a terminator");
448 }
449 }
450
451 fn inst(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
452 let data = &func[inst];
453 let opcode = data.opcode;
454
455 let stray = data.flags.without(crate::Flags::legal_on(opcode));
456 if !stray.is_empty() {
457 let names: Vec<&str> = stray.iter().map(|(_, name)| name).collect();
458 self.error(format!("{} does not read `{}`", opcode.name(), names.join("`, `")));
459 }
460 if data.extra.kind() != opcode.extra_kind() {
461 self.error(format!(
464 "{} carries {} and this one carries {}",
465 opcode.name(),
466 opcode.extra_kind().name(),
467 data.extra.kind().name()
468 ));
469 }
470 if let Some(want) = opcode.results() {
471 if want != data.results {
472 self.error(format!(
473 "{} produces {want} values and this one produces {}",
474 opcode.name(),
475 data.results
476 ));
477 }
478 }
479
480 self.uses(func, inst, doms, layout);
481 self.branches(func, inst);
482 self.memory(func, inst);
483 self.shape(func, inst);
484 }
485
486 fn uses(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
488 let block = layout.block_of(inst).expect("walking the blocks");
489 let check = |verifier: &mut Self, value: Value| match func[value].def {
490 Def::Param { block: def, .. } => {
491 if !doms.dominates(def, block) {
492 verifier.error(format!(
493 "%{} arrives at block{} and does not reach here",
494 value.raw(),
495 def.raw()
496 ));
497 }
498 }
499 Def::Result { inst: def, .. } => {
500 let Some(def_block) = layout.block_of(def) else {
501 verifier.error(format!(
502 "%{} is produced by an instruction that is not in the function",
503 value.raw()
504 ));
505 return;
506 };
507 let reaches = if def_block == block {
508 layout.position(def) < layout.position(inst)
509 } else {
510 doms.dominates(def_block, block)
511 };
512 if !reaches {
513 verifier.error(format!(
514 "%{} is produced in block{} and does not reach here",
515 value.raw(),
516 def_block.raw()
517 ));
518 }
519 }
520 };
521 for &value in &func[func[inst].args] {
522 check(self, value);
523 }
524 for call in func.successors(inst) {
525 for &value in &func[call.args] {
526 check(self, value);
527 }
528 }
529 }
530
531 fn branches(&mut self, func: &'a Func, inst: Inst) {
533 if func[inst].opcode == Opcode::BlockAddr {
534 return;
538 }
539 for call in func.successors(inst) {
540 let params = &func[call.block].params;
541 let args = &func[call.args];
542 if params.len() != args.len() {
543 self.error(format!(
544 "block{} takes {} arguments and this branch passes {}",
545 call.block.raw(),
546 params.len(),
547 args.len()
548 ));
549 continue;
550 }
551 for (index, (¶m, &arg)) in params.iter().zip(args).enumerate() {
552 let (want, got) = (func[param].ty, func[arg].ty);
553 if want != got {
554 self.error(format!(
555 "argument {} to block{} is {want} and this one is {got}",
556 index + 1,
557 call.block.raw()
558 ));
559 }
560 }
561 }
562 if let Extra::Switch(info) = func[inst].extra {
563 let switch = &func[info];
564 let targets = &func[switch.targets];
565 let cases = &func[switch.cases];
566 if targets.len() != cases.len() + 1 {
567 self.error(format!(
568 "a switch has one target per case and a default, and this one has {} targets for {} cases",
569 targets.len(),
570 cases.len()
571 ));
572 }
573 for (index, case) in cases.iter().enumerate() {
574 if cases[..index].contains(case) {
575 self.error("two cases of this switch have the same value");
576 }
577 }
578 }
579 }
580
581 fn memory(&mut self, func: &'a Func, inst: Inst) {
583 let opcode = func[inst].opcode;
584 let info = match func[inst].extra {
585 Extra::Mem(at) => func[at],
586 Extra::Rmw(_, at) => func[at],
587 Extra::Order(order) => {
588 if !order.is_valid_for_rmw() {
589 self.error("a fence is not a fence unless it orders something");
590 }
591 return;
592 }
593 _ => return,
594 };
595 if !info.align.is_power_of_two() {
596 self.error(format!("an alignment is a power of two and this is {}", info.align));
597 }
598 if let Some(tbaa) = info.tbaa {
599 if tbaa.index() >= self.module.counts().metadata {
600 self.error(format!("!{} is not a metadata node of this module", tbaa.raw()));
601 }
602 }
603 let ok = match opcode {
604 Opcode::AtomicLoad => info.order.is_valid_for_load(),
605 Opcode::AtomicStore => info.order.is_valid_for_store(),
606 Opcode::AtomicRmw | Opcode::Cmpxchg => info.order.is_valid_for_rmw(),
607 _ => info.order == MemOrder::NotAtomic,
610 };
611 if !ok {
612 self.error(format!("{} cannot be asked for {}", opcode.name(), info.order));
613 }
614 if matches!(opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) && info.size == 0 {
615 self.error(format!("{} moves no bytes", opcode.name()));
616 }
617 }
618
619 #[expect(clippy::too_many_lines, reason = "one arm per group of opcodes, and they differ")]
625 fn shape(&mut self, func: &'a Func, inst: Inst) {
626 let data = &func[inst];
627 let opcode = data.opcode;
628 let args = &func[data.args];
629 let arity = args.len();
630 let arg = |n: usize| func[args[n]].ty;
631 let results = usize::from(data.results);
632 let res = |n: usize| func[data.results().nth(n).expect("within the count")].ty;
633
634 match opcode {
635 Opcode::IConst => {
637 if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_int() {
638 self.error(format!(
639 "iconst produces an integer and this one produces {}",
640 res(0)
641 ));
642 }
643 }
644 Opcode::FConst => {
645 if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_float() {
646 self.error(format!(
647 "fconst produces a floating point value and this one produces {}",
648 res(0)
649 ));
650 }
651 }
652 Opcode::Splat => {
653 if self.takes(opcode, arity, 0) && results == 1 && !res(0).is_vector() {
654 self.error(format!("splat produces a vector and this one produces {}", res(0)));
655 }
656 }
657 Opcode::GlobalAddr
658 | Opcode::StackSave
659 | Opcode::FrameAddress
660 | Opcode::ReturnAddress => {
661 if results == 1 && !res(0).is_ptr() {
662 self.error(format!(
663 "{} produces a pointer and this one produces {}",
664 opcode.name(),
665 res(0)
666 ));
667 }
668 }
669
670 Opcode::Add
672 | Opcode::Sub
673 | Opcode::Mul
674 | Opcode::SDiv
675 | Opcode::UDiv
676 | Opcode::SRem
677 | Opcode::URem
678 | Opcode::And
679 | Opcode::Or
680 | Opcode::Xor
681 | Opcode::Shl
682 | Opcode::LShr
683 | Opcode::AShr => {
684 if self.takes(opcode, arity, 2) {
685 self.integer(opcode, arg(0), 0);
686 self.agree(opcode, arg(0), arg(1));
687 if results == 1 {
688 self.produces(opcode, res(0), arg(0));
689 }
690 }
691 }
692
693 Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv | Opcode::FRem => {
695 if self.takes(opcode, arity, 2) {
696 self.floating(opcode, arg(0), 0);
697 self.agree(opcode, arg(0), arg(1));
698 if results == 1 {
699 self.produces(opcode, res(0), arg(0));
700 }
701 }
702 }
703 Opcode::FNeg => {
704 if self.takes(opcode, arity, 1) {
705 self.floating(opcode, arg(0), 0);
706 if results == 1 {
707 self.produces(opcode, res(0), arg(0));
708 }
709 }
710 }
711 Opcode::Fma => {
712 if self.takes(opcode, arity, 3) {
713 self.floating(opcode, arg(0), 0);
714 self.agree(opcode, arg(0), arg(1));
715 self.agree(opcode, arg(0), arg(2));
716 if results == 1 {
717 self.produces(opcode, res(0), arg(0));
718 }
719 }
720 }
721
722 Opcode::ICmp | Opcode::FCmp => {
724 if self.takes(opcode, arity, 2) {
725 if opcode == Opcode::FCmp {
726 self.floating(opcode, arg(0), 0);
727 } else if !arg(0).lane().is_int() && !arg(0).is_ptr() {
728 self.error(format!(
729 "operand 1 of icmp is an integer or a pointer and this one is {}",
730 arg(0)
731 ));
732 }
733 self.agree(opcode, arg(0), arg(1));
734 if results == 1 {
735 self.produces(opcode, res(0), arg(0).with_lane(Type::I1));
736 }
737 }
738 }
739
740 Opcode::Trunc | Opcode::SExt | Opcode::ZExt => {
742 if self.takes(opcode, arity, 1) && results == 1 {
743 self.integer(opcode, arg(0), 0);
744 self.lanes(opcode, res(0), arg(0));
745 self.widens(opcode, res(0), arg(0), opcode != Opcode::Trunc);
746 }
747 }
748 Opcode::FPTrunc | Opcode::FPExt => {
749 if self.takes(opcode, arity, 1) && results == 1 {
750 self.floating(opcode, arg(0), 0);
751 self.lanes(opcode, res(0), arg(0));
752 self.widens(opcode, res(0), arg(0), opcode == Opcode::FPExt);
753 }
754 }
755 Opcode::FPToSI | Opcode::FPToUI => {
756 if self.takes(opcode, arity, 1) && results == 1 {
757 self.floating(opcode, arg(0), 0);
758 self.lanes(opcode, res(0), arg(0));
759 if !res(0).lane().is_int() {
760 self.error(format!(
761 "{} produces an integer and this one produces {}",
762 opcode.name(),
763 res(0)
764 ));
765 }
766 }
767 }
768 Opcode::SIToFP | Opcode::UIToFP => {
769 if self.takes(opcode, arity, 1) && results == 1 {
770 self.integer(opcode, arg(0), 0);
771 self.lanes(opcode, res(0), arg(0));
772 if !res(0).lane().is_float() {
773 self.error(format!(
774 "{} produces a floating point value and this one produces {}",
775 opcode.name(),
776 res(0)
777 ));
778 }
779 }
780 }
781 Opcode::PtrToInt => {
782 if self.takes(opcode, arity, 1) && results == 1 {
783 self.pointer(opcode, arg(0), 0);
784 self.integer(opcode, res(0), 0);
785 }
786 }
787 Opcode::IntToPtr => {
788 if self.takes(opcode, arity, 1) && results == 1 {
789 self.integer(opcode, arg(0), 0);
790 if !res(0).is_ptr() {
791 self.error(format!(
792 "inttoptr produces a pointer and this one produces {}",
793 res(0)
794 ));
795 }
796 }
797 }
798 Opcode::Bitcast => {
799 if self.takes(opcode, arity, 1) && results == 1 {
800 let (from, to) = (arg(0), res(0));
801 if from.is_ptr() != to.is_ptr() {
802 self.error(
805 "a bitcast between a pointer and a number is ptrtoint or inttoptr",
806 );
807 } else if width(from) != width(to) {
808 self.error(format!("a bitcast keeps the width and {from} and {to} differ"));
809 }
810 }
811 }
812
813 Opcode::Alloca => {
815 if arity > 1 {
816 self.takes(opcode, arity, 1);
817 } else if arity == 1 {
818 self.integer(opcode, arg(0), 0);
819 } else if func.block_of(inst) != func.entry() {
820 self.error("an alloca of a fixed size belongs in the entry block");
823 }
824 if results == 1 && !res(0).is_ptr() {
825 self.error(format!(
826 "alloca produces a pointer and this one produces {}",
827 res(0)
828 ));
829 }
830 }
831 Opcode::Load | Opcode::AtomicLoad => {
832 if self.takes(opcode, arity, 1) {
833 self.pointer(opcode, arg(0), 0);
834 }
835 if results == 1 && res(0).is_void() {
836 self.error(format!("{} reads a value and void is not one", opcode.name()));
837 }
838 }
839 Opcode::Store | Opcode::AtomicStore => {
840 if self.takes(opcode, arity, 2) {
841 if arg(0).is_void() {
842 self.error(format!("{} writes a value and void is not one", opcode.name()));
843 }
844 self.pointer(opcode, arg(1), 1);
845 }
846 }
847 Opcode::PtrAdd => {
848 if self.takes(opcode, arity, 2) {
849 self.pointer(opcode, arg(0), 0);
850 self.integer(opcode, arg(1), 1);
851 if results == 1 && !res(0).is_ptr() {
852 self.error(format!(
853 "ptr_add produces a pointer and this one produces {}",
854 res(0)
855 ));
856 }
857 }
858 }
859 Opcode::Memcpy | Opcode::Memmove => {
860 if self.takes(opcode, arity, 2) {
861 self.pointer(opcode, arg(0), 0);
862 self.pointer(opcode, arg(1), 1);
863 }
864 }
865 Opcode::Memset => {
866 if self.takes(opcode, arity, 2) {
867 self.pointer(opcode, arg(0), 0);
868 self.integer(opcode, arg(1), 1);
869 }
870 }
871 Opcode::AtomicRmw => {
872 if self.takes(opcode, arity, 2) {
873 self.pointer(opcode, arg(0), 0);
874 self.integer(opcode, arg(1), 1);
875 if results == 1 {
876 self.produces(opcode, res(0), arg(1));
877 }
878 }
879 }
880 Opcode::Cmpxchg => {
881 if self.takes(opcode, arity, 3) {
882 self.pointer(opcode, arg(0), 0);
883 self.agree(opcode, arg(1), arg(2));
884 if results == 2 {
885 self.produces(opcode, res(0), arg(1));
886 self.produces(opcode, res(1), arg(1).with_lane(Type::I1));
887 }
888 }
889 }
890 Opcode::Fence | Opcode::Unreachable | Opcode::UnreachableHint => {
891 self.takes(opcode, arity, 0);
892 }
893 Opcode::Prefetch | Opcode::StackRestore | Opcode::VaStart | Opcode::VaEnd => {
894 if self.takes(opcode, arity, 1) {
895 self.pointer(opcode, arg(0), 0);
896 }
897 }
898 Opcode::VaCopy => {
899 if self.takes(opcode, arity, 2) {
900 self.pointer(opcode, arg(0), 0);
901 self.pointer(opcode, arg(1), 1);
902 }
903 }
904 Opcode::VaArg => {
905 if self.takes(opcode, arity, 1) {
906 self.pointer(opcode, arg(0), 0);
907 }
908 if results == 1 && res(0).is_void() {
909 self.error("va_arg reads a value and void is not one");
910 }
911 }
912 Opcode::VaObject => {
913 if self.takes(opcode, arity, 1) {
914 self.pointer(opcode, arg(0), 0);
915 }
916 if results == 1 && res(0) != Type::PTR {
917 self.error(format!(
918 "va_object answers where the object is and {} is not an address",
919 res(0)
920 ));
921 }
922 }
923
924 Opcode::Jump => {
926 self.takes(opcode, arity, 0);
927 self.targets(func, inst, 1);
928 }
929 Opcode::BrIf => {
930 if self.takes(opcode, arity, 1) && arg(0) != Type::I1 {
931 self.error(format!("br_if branches on an i1 and this one on {}", arg(0)));
932 }
933 self.targets(func, inst, 2);
934 }
935 Opcode::Switch => {
936 if self.takes(opcode, arity, 1) {
937 self.integer(opcode, arg(0), 0);
938 }
939 }
940 Opcode::BlockAddr => {
941 self.takes(opcode, arity, 0);
942 self.targets(func, inst, 1);
943 if results == 1 && !res(0).is_ptr() {
944 self.error(format!(
945 "block_addr produces a pointer and this one produces {}",
946 res(0)
947 ));
948 }
949 if func.successors(inst).any(|call| !func[call.args].is_empty()) {
950 self.error("block_addr names a block and passes it arguments");
953 }
954 }
955 Opcode::IndirectBr => {
956 if self.takes(opcode, arity, 1) {
957 self.pointer(opcode, arg(0), 0);
958 }
959 }
962 Opcode::Return => {
963 let want = &func.signature().returns;
964 if arity != want.len() {
965 self.error(format!(
966 "the signature returns {} and this returns {arity}",
967 want.len()
968 ));
969 } else {
970 for (index, ty) in want.iter().map(|param| param.ty).enumerate() {
971 if arg(index) != ty {
972 self.error(format!(
973 "result {} of the signature is {ty} and this returns {}",
974 index + 1,
975 arg(index)
976 ));
977 }
978 }
979 }
980 }
981
982 Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => {
984 let Extra::Call(at) = data.extra else { return };
985 let info = func[at];
986 let signature = &func[info.signature];
987 let indirect = usize::from(opcode == Opcode::CallIndirect);
988 if indirect == 1 {
989 if arity == 0 {
990 self.error("call_indirect calls through a pointer and has no operands");
991 return;
992 }
993 self.pointer(opcode, arg(0), 0);
994 }
995 let passed = arity - indirect;
996 let enough = if signature.variadic {
997 passed >= signature.params.len()
998 } else {
999 passed == signature.params.len()
1000 };
1001 if enough {
1002 for (index, ty) in signature.param_types().enumerate() {
1003 if arg(index + indirect) != ty {
1004 self.error(format!(
1005 "parameter {} of the signature is {ty} and this argument is {}",
1006 index + 1,
1007 arg(index + indirect)
1008 ));
1009 }
1010 }
1011 } else {
1012 self.error(format!(
1013 "the signature takes {}{} and this call passes {passed}",
1014 signature.params.len(),
1015 if signature.variadic { " or more" } else { "" }
1016 ));
1017 }
1018 self.varargs(func, &info, passed, |n| arg(n + indirect));
1019 if results != signature.returns.len() {
1020 self.error(format!(
1021 "the signature returns {} and this call produces {results}",
1022 signature.returns.len()
1023 ));
1024 } else {
1025 for (index, ty) in signature.return_types().enumerate() {
1026 if res(index) != ty {
1027 self.error(format!(
1028 "result {} of the signature is {ty} and this call produces {}",
1029 index + 1,
1030 res(index)
1031 ));
1032 }
1033 }
1034 }
1035 if let Some(callee) = info.callee {
1038 if let Some(SymbolRef::Func(id)) = self.module.lookup(callee) {
1039 if self.module[id].signature() != signature {
1040 self.error(format!(
1041 "@{} is declared here with another signature",
1042 self.names.resolve(callee)
1043 ));
1044 }
1045 }
1046 }
1047 }
1048
1049 Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop | Opcode::Bswap | Opcode::Bitreverse => {
1051 if self.takes(opcode, arity, 1) {
1052 self.integer(opcode, arg(0), 0);
1053 if results == 1 {
1054 self.produces(opcode, res(0), arg(0));
1055 }
1056 }
1057 }
1058
1059 Opcode::SAddOverflow
1061 | Opcode::UAddOverflow
1062 | Opcode::SSubOverflow
1063 | Opcode::USubOverflow
1064 | Opcode::SMulOverflow
1065 | Opcode::UMulOverflow => {
1066 if self.takes(opcode, arity, 2) {
1067 self.integer(opcode, arg(0), 0);
1068 self.agree(opcode, arg(0), arg(1));
1069 if results == 2 {
1070 self.produces(opcode, res(0), arg(0));
1071 self.produces(opcode, res(1), arg(0).with_lane(Type::I1));
1072 }
1073 }
1074 }
1075 Opcode::Expect => {
1076 if self.takes(opcode, arity, 2) {
1077 self.agree(opcode, arg(0), arg(1));
1078 if results == 1 {
1079 self.produces(opcode, res(0), arg(0));
1080 }
1081 }
1082 }
1083
1084 Opcode::SetjmpMarker
1088 | Opcode::LongjmpMarker
1089 | Opcode::InlineAsm
1090 | Opcode::TargetIntrinsic => {}
1091 }
1092 }
1093
1094 fn takes(&mut self, opcode: Opcode, got: usize, want: usize) -> bool {
1098 if got == want {
1099 return true;
1100 }
1101 self.error(format!("{} takes {want} operands and this one has {got}", opcode.name()));
1102 false
1103 }
1104
1105 fn targets(&mut self, func: &'a Func, inst: Inst, want: usize) {
1107 let got = func.successors(inst).count();
1108 if got != want {
1109 self.error(format!(
1110 "{} branches to {want} blocks and this one to {got}",
1111 func[inst].opcode.name()
1112 ));
1113 }
1114 }
1115
1116 fn integer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1117 if !ty.lane().is_int() {
1118 self.error(format!(
1119 "operand {} of {} is an integer and this one is {ty}",
1120 n + 1,
1121 opcode.name()
1122 ));
1123 }
1124 }
1125
1126 fn floating(&mut self, opcode: Opcode, ty: Type, n: usize) {
1127 if !ty.lane().is_float() {
1128 self.error(format!(
1129 "operand {} of {} is a floating point value and this one is {ty}",
1130 n + 1,
1131 opcode.name()
1132 ));
1133 }
1134 }
1135
1136 fn pointer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1137 if !ty.is_ptr() {
1138 self.error(format!(
1139 "operand {} of {} is a pointer and this one is {ty}",
1140 n + 1,
1141 opcode.name()
1142 ));
1143 }
1144 }
1145
1146 fn agree(&mut self, opcode: Opcode, first: Type, second: Type) {
1148 if first != second {
1149 self.error(format!(
1150 "the operands of {} have one type and these are {first} and {second}",
1151 opcode.name()
1152 ));
1153 }
1154 }
1155
1156 fn produces(&mut self, opcode: Opcode, got: Type, want: Type) {
1158 if got != want {
1159 self.error(format!(
1160 "{} produces {want} here and this one produces {got}",
1161 opcode.name()
1162 ));
1163 }
1164 }
1165
1166 fn lanes(&mut self, opcode: Opcode, to: Type, from: Type) {
1168 if to.lanes() != from.lanes() {
1169 self.error(format!(
1170 "{} keeps the lane count and {from} has {} and {to} has {}",
1171 opcode.name(),
1172 from.lanes(),
1173 to.lanes()
1174 ));
1175 }
1176 }
1177
1178 fn widens(&mut self, opcode: Opcode, to: Type, from: Type, wider: bool) {
1180 let (a, b) = (to.lane().bits(), from.lane().bits());
1181 let ok = if wider { a > b } else { a < b };
1182 if !ok {
1183 let way = if wider { "wider" } else { "narrower" };
1184 self.error(format!(
1185 "{} produces something {way} and {from} to {to} is not",
1186 opcode.name()
1187 ));
1188 }
1189 }
1190
1191 fn error(&mut self, message: impl Into<String>) {
1194 let at = self.locate();
1195 self.at(at, message);
1196 }
1197
1198 fn at(&mut self, at: String, message: impl Into<String>) {
1199 self.errors.push(VerifyError { at, message: message.into() });
1200 }
1201
1202 fn locate(&self) -> String {
1208 use fmt::Write as _;
1209 let mut at = String::new();
1210 if let Some(func) = self.func {
1211 let _ = write!(at, "@{}", self.names.resolve(func.name));
1212 if let Some(block) = self.block {
1213 let _ = write!(at, " block{}", block.raw());
1214 }
1215 if let Some(inst) = self.inst {
1216 let _ = write!(at, " {}", func[inst].opcode.name());
1217 }
1218 }
1219 at
1220 }
1221}
1222
1223struct Layout {
1226 block: Vec<Option<Block>>,
1227 position: Vec<u32>,
1228}
1229
1230impl Layout {
1231 fn new(func: &Func) -> Self {
1232 let counts = func.counts();
1233 let mut layout =
1234 Layout { block: vec![None; counts.insts], position: vec![0; counts.insts] };
1235 for block in func.blocks() {
1236 for (position, inst) in func.insts(block).enumerate() {
1237 layout.block[inst.index()] = Some(block);
1238 layout.position[inst.index()] = position as u32;
1239 }
1240 }
1241 layout
1242 }
1243
1244 fn block_of(&self, inst: Inst) -> Option<Block> {
1245 self.block[inst.index()]
1246 }
1247
1248 fn position(&self, inst: Inst) -> u32 {
1249 self.position[inst.index()]
1250 }
1251}
1252
1253struct Doms {
1260 rank: Vec<Option<u32>>,
1263 idom: Vec<u32>,
1265}
1266
1267impl Doms {
1268 fn new(func: &Func) -> Self {
1269 let counts = func.counts();
1270 let entry = func.entry().expect("a function with blocks has a first one");
1271
1272 let mut succs: Vec<Vec<Block>> = vec![Vec::new(); counts.blocks];
1280 for block in func.blocks() {
1281 for inst in func.insts(block) {
1282 succs[block.index()].extend(func.successors(inst).map(|call| call.block));
1283 }
1284 }
1285
1286 let mut order = Vec::new();
1289 let mut seen = vec![false; counts.blocks];
1290 let mut stack = vec![(entry, 0usize)];
1291 seen[entry.index()] = true;
1292 while let Some((block, next)) = stack.pop() {
1293 match succs[block.index()].get(next) {
1294 Some(&target) => {
1295 stack.push((block, next + 1));
1296 if !seen[target.index()] {
1297 seen[target.index()] = true;
1298 stack.push((target, 0));
1299 }
1300 }
1301 None => order.push(block),
1302 }
1303 }
1304 order.reverse();
1305
1306 let mut rank = vec![None; counts.blocks];
1307 for (index, &block) in order.iter().enumerate() {
1308 rank[block.index()] = Some(index as u32);
1309 }
1310 let mut preds: Vec<Vec<u32>> = vec![Vec::new(); order.len()];
1311 for (index, &block) in order.iter().enumerate() {
1312 for &target in &succs[block.index()] {
1313 if let Some(target) = rank[target.index()] {
1314 preds[target as usize].push(index as u32);
1315 }
1316 }
1317 }
1318
1319 const NONE: u32 = u32::MAX;
1323 let mut idom = vec![NONE; order.len()];
1324 if !order.is_empty() {
1325 idom[0] = 0;
1326 }
1327 let mut changed = true;
1328 while changed {
1329 changed = false;
1330 for index in 1..order.len() {
1331 let mut new = NONE;
1332 for &pred in &preds[index] {
1333 if idom[pred as usize] == NONE {
1334 continue;
1335 }
1336 new = if new == NONE { pred } else { meet(&idom, new, pred) };
1337 }
1338 if new != NONE && idom[index] != new {
1339 idom[index] = new;
1340 changed = true;
1341 }
1342 }
1343 }
1344 Doms { rank, idom }
1345 }
1346
1347 fn reaches(&self, block: Block) -> bool {
1349 self.rank[block.index()].is_some()
1350 }
1351
1352 fn dominates(&self, of: Block, block: Block) -> bool {
1358 let (Some(a), Some(b)) = (self.rank[of.index()], self.rank[block.index()]) else {
1359 return true;
1360 };
1361 let mut walk = b;
1362 while walk > a {
1363 walk = self.idom[walk as usize];
1364 }
1365 walk == a
1366 }
1367}
1368
1369fn meet(idom: &[u32], mut a: u32, mut b: u32) -> u32 {
1371 while a != b {
1372 while a > b {
1373 a = idom[a as usize];
1374 }
1375 while b > a {
1376 b = idom[b as usize];
1377 }
1378 }
1379 a
1380}
1381
1382fn width(ty: Type) -> u64 {
1384 u64::from(ty.bits()) * u64::from(ty.lanes())
1385}
1386
1387fn types(list: &[Type]) -> String {
1389 if list.is_empty() {
1390 return "nothing".to_string();
1391 }
1392 list.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
1393}
1394
1395#[cfg(test)]
1396mod tests {
1397 use rucc_base::{Idx, Interner, Symbol};
1398 use rucc_diag::Span;
1399 use rucc_target::{Arch, Env, Os, Triple};
1400
1401 use super::*;
1402 use crate::fixtures::{EXAMPLE, SYMBOLS, ZOO};
1403 use crate::func::Builder;
1404 use crate::inst::{InstData, MetaNode, Signature};
1405 use crate::{Flags, IntPred, parse};
1406
1407 fn target() -> TargetInfo {
1408 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1409 }
1410
1411 fn errors(text: &str) -> Vec<String> {
1414 let mut names = Interner::new();
1415 let module = match parse(text, &mut names) {
1416 Ok(module) => module,
1417 Err(error) => panic!("{error}"),
1418 };
1419 match verify(&module, &names) {
1420 Ok(()) => Vec::new(),
1421 Err(errors) => errors.iter().map(ToString::to_string).collect(),
1422 }
1423 }
1424
1425 fn only(text: &str) -> String {
1427 let found = errors(text);
1428 assert_eq!(found.len(), 1, "{found:#?}");
1429 found.into_iter().next().expect("just counted one")
1430 }
1431
1432 const HEADER: &str = "\
1433; ModuleID = 'bad.c'
1434; format 0
1435target triple = \"x86_64-unknown-linux-gnu\"
1436target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
1437";
1438
1439 fn wrap(signature: &str, body: &str) -> String {
1441 format!("{HEADER}\nfunc @f{signature}, linkage(external) {{\n{body}}}\n")
1442 }
1443
1444 #[test]
1445 fn the_three_fixtures_are_modules_the_compiler_may_believe() {
1446 for text in [EXAMPLE, ZOO, SYMBOLS] {
1447 assert_eq!(errors(text), Vec::<String>::new());
1448 }
1449 }
1450
1451 #[test]
1452 fn a_use_that_its_definition_does_not_reach_is_reported() {
1453 let text = wrap(
1454 "(i1) -> i32",
1455 "block0(%0: i1):
1456 br_if %0, block1, block2
1457
1458block1:
1459 %1 = iconst.i32 7
1460 jump block2
1461
1462block2:
1463 return %1
1464",
1465 );
1466 assert_eq!(
1467 only(&text),
1468 "@f block2 return: %1 is produced in block1 and does not reach here"
1469 );
1470 }
1471
1472 #[test]
1473 fn a_use_before_its_definition_in_the_same_block_is_reported() {
1474 let text = wrap(
1475 "() -> i32",
1476 "block0:
1477 %0 = iconst.i32 1
1478 %1 = add %2, %0
1479 %2 = iconst.i32 2
1480 return %1
1481",
1482 );
1483 assert_eq!(only(&text), "@f block0 add: %2 is produced in block0 and does not reach here");
1484 }
1485
1486 #[test]
1487 fn a_call_that_takes_its_arguments_the_way_the_abi_says_is_believed() {
1488 let text = wrap(
1489 "(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext)",
1490 "block0(%0: ptr, %1: ptr, %2: i8):
1491 return
1492",
1493 );
1494 assert_eq!(errors(&text), Vec::<String>::new());
1495 }
1496
1497 #[test]
1498 fn a_call_that_says_how_an_argument_past_its_parameter_list_travels_is_believed() {
1499 let text = wrap(
1500 "(ptr)",
1501 "block0(%0: ptr):
1502 call @p(%0, %0 byval(24, align 8)) : (ptr, ...)
1503 return
1504",
1505 );
1506 assert_eq!(errors(&text), Vec::<String>::new());
1507 }
1508
1509 #[test]
1510 fn a_call_that_says_it_of_something_that_is_not_a_pointer_is_reported() {
1511 let text = wrap(
1512 "(ptr)",
1513 "block0(%0: ptr):
1514 %1 = iconst.i32 1
1515 call @p(%0, %1 byval(4, align 4)) : (ptr, ...)
1516 return
1517",
1518 );
1519 assert_eq!(
1520 only(&text),
1521 "@f block0 call: argument 2 travels indirectly and i32 is not a pointer"
1522 );
1523 }
1524
1525 #[test]
1526 fn a_call_that_says_an_argument_past_its_parameter_list_is_an_sret_is_reported() {
1527 let text = wrap(
1528 "(ptr)",
1529 "block0(%0: ptr):
1530 call @p(%0, %0 sret(24, align 8)) : (ptr, ...)
1531 return
1532",
1533 );
1534 assert_eq!(
1535 only(&text),
1536 "@f block0 call: argument 2 is an sret and only a parameter can be one"
1537 );
1538 }
1539
1540 #[test]
1541 fn a_call_that_is_not_variadic_says_nothing_about_a_variadic_argument() {
1542 let text = wrap(
1543 "(ptr)",
1544 "block0(%0: ptr):
1545 call @p(%0, %0 byval(24, align 8)) : (ptr)
1546 return
1547",
1548 );
1549 assert_eq!(
1550 errors(&text),
1551 vec![
1552 "@f block0 call: the signature takes 1 and this call passes 2",
1553 "@f block0 call: this call is not variadic and says how a variadic argument \
1554 travels",
1555 ]
1556 );
1557 }
1558
1559 #[test]
1560 fn an_sret_that_is_not_the_first_parameter_is_reported() {
1561 let text = wrap(
1562 "(ptr byval(8, align 8), ptr sret(16, align 8))",
1563 "block0(%0: ptr, %1: ptr):
1564 return
1565",
1566 );
1567 assert_eq!(only(&text), "@f: sret is the first parameter and this one is not");
1568 }
1569
1570 #[test]
1571 fn a_function_returning_through_sret_returns_nothing_else() {
1572 let text = wrap(
1573 "(ptr sret(8, align 8)) -> i32",
1574 "block0(%0: ptr):
1575 %1 = iconst.i32 7
1576 return %1
1577",
1578 );
1579 assert_eq!(only(&text), "@f: a signature returning through sret returns nothing else");
1580 }
1581
1582 #[test]
1583 fn an_object_that_travels_indirectly_travels_behind_a_pointer() {
1584 let text = wrap(
1585 "(i32 byval(4, align 4))",
1586 "block0(%0: i32):
1587 return
1588",
1589 );
1590 assert_eq!(only(&text), "@f: parameter 1 travels indirectly and i32 is not a pointer");
1591 }
1592
1593 #[test]
1594 fn an_alignment_a_parameter_could_not_have_is_reported() {
1595 let text = wrap(
1596 "(ptr byval(24, align 3))",
1597 "block0(%0: ptr):
1598 return
1599",
1600 );
1601 assert_eq!(only(&text), "@f: an alignment is a power of two and this is 3");
1602 }
1603
1604 #[test]
1605 fn a_result_does_not_travel_indirectly() {
1606 let text = wrap(
1609 "() -> ptr byval(16, align 8)",
1610 "block0:
1611 %0 = iconst.i64 0
1612 %1 = inttoptr.ptr %0
1613 return %1
1614",
1615 );
1616 assert_eq!(only(&text), "@f: result 1 travels indirectly and a result cannot");
1617 }
1618
1619 #[test]
1620 fn an_extension_is_asked_of_an_integer_and_not_of_anything_else() {
1621 let text = wrap(
1622 "(ptr zext)",
1623 "block0(%0: ptr):
1624 return
1625",
1626 );
1627 assert_eq!(only(&text), "@f: parameter 1 is extended and ptr is not an integer");
1628 }
1629
1630 #[test]
1631 fn a_branch_that_passes_the_wrong_number_of_arguments_is_reported() {
1632 let text = wrap(
1633 "(i32)",
1634 "block0(%0: i32):
1635 jump block1(%0)
1636
1637block1:
1638 return
1639",
1640 );
1641 assert_eq!(
1642 only(&text),
1643 "@f block0 jump: block1 takes 0 arguments and this branch passes 1"
1644 );
1645 }
1646
1647 #[test]
1648 fn a_branch_that_passes_the_wrong_type_is_reported() {
1649 let text = wrap(
1650 "(i32) -> i32",
1651 "block0(%0: i32):
1652 %1 = sext.i64 %0
1653 jump block1(%1)
1654
1655block1(%2: i32):
1656 return %2
1657",
1658 );
1659 assert_eq!(only(&text), "@f block0 jump: argument 1 to block1 is i32 and this one is i64");
1660 }
1661
1662 #[test]
1663 fn a_block_that_does_not_end_in_a_terminator_is_reported() {
1664 let text = wrap("()", "block0:\n %0 = iconst.i32 1\n");
1665 assert_eq!(only(&text), "@f block0: this block does not end in a terminator");
1666 }
1667
1668 #[test]
1669 fn an_instruction_after_the_terminator_is_reported() {
1670 let text = wrap("()", "block0:\n return\n %0 = iconst.i32 1\n");
1671 assert_eq!(only(&text), "@f block0 iconst: this comes after the block's terminator");
1672 }
1673
1674 #[test]
1675 fn an_unreachable_block_is_reported() {
1676 let text = wrap("()", "block0:\n return\n\nblock1:\n return\n");
1677 assert_eq!(only(&text), "@f block1: this block is not reachable and has not been deleted");
1678 }
1679
1680 #[test]
1681 fn a_block_a_jump_to_an_address_arrives_at_is_an_ordinary_target() {
1682 let text = wrap(
1683 "(ptr) -> i32",
1684 "block0(%0: ptr):
1685 %1 = block_addr block1
1686 indirect_br %0, block1
1687
1688block1:
1689 %2 = iconst.i32 1
1690 return %2
1691",
1692 );
1693 assert_eq!(errors(&text), Vec::<String>::new());
1694 }
1695
1696 #[test]
1697 fn a_block_whose_address_is_taken_is_reached_by_the_taking_of_it() {
1698 let text = wrap(
1701 "() -> ptr",
1702 "block0:
1703 %0 = block_addr block1
1704 return %0
1705
1706block1:
1707 unreachable
1708",
1709 );
1710 assert_eq!(errors(&text), Vec::<String>::new());
1711 }
1712
1713 #[test]
1714 fn taking_the_address_of_a_block_and_passing_it_arguments_is_reported() {
1715 let text = wrap(
1718 "(i32) -> ptr",
1719 "block0(%0: i32):
1720 %1 = block_addr block1(%0)
1721 return %1
1722
1723block1(%2: i32):
1724 unreachable
1725",
1726 );
1727 assert_eq!(
1728 only(&text),
1729 "@f block0 block_addr: block_addr names a block and passes it arguments"
1730 );
1731 }
1732
1733 #[test]
1734 fn a_jump_to_something_that_is_not_an_address_is_reported() {
1735 let text = wrap(
1736 "(i32)",
1737 "block0(%0: i32):
1738 indirect_br %0, block1
1739
1740block1:
1741 return
1742",
1743 );
1744 assert_eq!(
1745 only(&text),
1746 "@f block0 indirect_br: operand 1 of indirect_br is a pointer and this one is i32"
1747 );
1748 }
1749
1750 #[test]
1751 fn a_branch_back_to_the_entry_block_is_reported() {
1752 let text = wrap(
1755 "(i32)",
1756 "block0(%0: i32):
1757 jump block1
1758
1759block1:
1760 jump block0(%0)
1761",
1762 );
1763 assert_eq!(
1764 only(&text),
1765 "@f block0: the entry block is branched to, and it takes the arguments"
1766 );
1767 }
1768
1769 #[test]
1770 fn an_entry_block_that_does_not_take_the_arguments_is_reported() {
1771 let text = wrap("(i32)", "block0(%0: i64):\n return\n");
1772 assert_eq!(only(&text), "@f: the entry block takes i64 and the signature says i32");
1773 }
1774
1775 #[test]
1776 fn a_flag_the_opcode_does_not_read_is_reported() {
1777 let text =
1778 wrap("(i32) -> i32", "block0(%0: i32):\n %1 = add.exact %0, %0\n return %1\n");
1779 assert_eq!(only(&text), "@f block0 add: add does not read `exact`");
1780 }
1781
1782 #[test]
1783 fn an_ordering_the_operation_cannot_be_asked_for_is_reported() {
1784 let text = wrap(
1785 "(ptr) -> i32",
1786 "block0(%0: ptr):\n %1 = atomic_load.i32 %0, align 4, release\n return %1\n",
1787 );
1788 assert_eq!(only(&text), "@f block0 atomic_load: atomic_load cannot be asked for release");
1789 }
1790
1791 #[test]
1792 fn an_ordering_on_the_non_atomic_form_is_reported() {
1793 let text = wrap(
1794 "(ptr) -> i32",
1795 "block0(%0: ptr):\n %1 = load.i32 %0, align 4, acquire\n return %1\n",
1796 );
1797 assert_eq!(only(&text), "@f block0 load: load cannot be asked for acquire");
1798 }
1799
1800 #[test]
1801 fn a_va_object_that_answers_anything_but_an_address_is_reported() {
1802 let text = wrap(
1805 "(ptr) -> i64",
1806 "block0(%0: ptr):
1807 %1 = va_object.i64 %0, size 16, align 8
1808 return %1
1809",
1810 );
1811 assert_eq!(
1812 only(&text),
1813 "@f block0 va_object: va_object answers where the object is and i64 is not an address"
1814 );
1815 }
1816
1817 #[test]
1818 fn an_alloca_of_a_fixed_size_outside_the_entry_block_is_reported() {
1819 let text = wrap(
1820 "()",
1821 "block0:
1822 jump block1
1823
1824block1:
1825 %0 = alloca, size 16, align 8
1826 return
1827",
1828 );
1829 assert_eq!(
1830 only(&text),
1831 "@f block1 alloca: an alloca of a fixed size belongs in the entry block"
1832 );
1833 }
1834
1835 #[test]
1836 fn a_dynamic_alloca_may_be_anywhere() {
1837 let text = wrap(
1838 "(i64)",
1839 "block0(%0: i64):
1840 jump block1
1841
1842block1:
1843 %1 = alloca %0, align 8
1844 return
1845",
1846 );
1847 assert_eq!(errors(&text), Vec::<String>::new());
1848 }
1849
1850 #[test]
1851 fn two_cases_of_a_switch_with_the_same_value_are_reported() {
1852 let text = wrap(
1853 "(i32)",
1854 "block0(%0: i32):
1855 switch %0, block1, [7 => block1, 7 => block1]
1856
1857block1:
1858 return
1859",
1860 );
1861 assert_eq!(only(&text), "@f block0 switch: two cases of this switch have the same value");
1862 }
1863
1864 #[test]
1865 fn operands_that_do_not_agree_are_reported() {
1866 let text = wrap(
1867 "(i32, i64) -> i32",
1868 "block0(%0: i32, %1: i64):\n %2 = add %0, %1\n return %2\n",
1869 );
1870 assert_eq!(
1871 only(&text),
1872 "@f block0 add: the operands of add have one type and these are i32 and i64"
1873 );
1874 }
1875
1876 #[test]
1877 fn a_conversion_that_goes_the_wrong_way_is_reported() {
1878 let text = wrap("(i32) -> i64", "block0(%0: i32):\n %1 = trunc.i64 %0\n return %1\n");
1879 assert_eq!(
1880 only(&text),
1881 "@f block0 trunc: trunc produces something narrower and i32 to i64 is not"
1882 );
1883 }
1884
1885 #[test]
1886 fn a_bitcast_between_an_address_and_a_number_is_reported() {
1887 let text =
1888 wrap("(ptr) -> i64", "block0(%0: ptr):\n %1 = bitcast.i64 %0\n return %1\n");
1889 assert_eq!(
1890 only(&text),
1891 "@f block0 bitcast: a bitcast between a pointer and a number is ptrtoint or inttoptr"
1892 );
1893 }
1894
1895 #[test]
1896 fn an_operand_of_the_wrong_kind_is_reported() {
1897 let text = wrap("(i32) -> i32", "block0(%0: i32):\n %1 = fadd %0, %0\n return %1\n");
1898 assert_eq!(
1899 only(&text),
1900 "@f block0 fadd: operand 1 of fadd is a floating point value and this one is i32"
1901 );
1902 }
1903
1904 #[test]
1905 fn a_condition_that_is_not_one_bit_is_reported() {
1906 let text = wrap(
1907 "(i32)",
1908 "block0(%0: i32):
1909 br_if %0, block1, block1
1910
1911block1:
1912 return
1913",
1914 );
1915 assert_eq!(only(&text), "@f block0 br_if: br_if branches on an i1 and this one on i32");
1916 }
1917
1918 #[test]
1919 fn a_return_that_does_not_match_the_signature_is_reported() {
1920 let text = wrap("() -> i32", "block0:\n return\n");
1921 assert_eq!(only(&text), "@f block0 return: the signature returns 1 and this returns 0");
1922 }
1923
1924 #[test]
1925 fn a_call_that_disagrees_with_the_declaration_is_reported() {
1926 let text = format!(
1927 "{HEADER}
1928func @g(i32, ...) -> i32, linkage(external);
1929
1930func @f(i32) -> i32, linkage(external) {{
1931block0(%0: i32):
1932 %1 = call @g(%0) : (i32) -> i32
1933 return %1
1934}}
1935"
1936 );
1937 assert_eq!(only(&text), "@f block0 call: @g is declared here with another signature");
1938 }
1939
1940 #[test]
1941 fn a_global_whose_image_is_not_its_size_is_reported() {
1942 let text =
1943 format!("{HEADER}\nglobal @x : bytes 8 = {{ i32 7 }}, align 4, linkage(external)\n");
1944 assert_eq!(only(&text), "@x: the image is 4 bytes and the global is 8");
1945 }
1946
1947 #[test]
1948 fn a_pointer_in_an_image_has_no_width_and_is_reported() {
1949 let text = format!(
1953 "{HEADER}\nglobal @x : bytes 8 = {{ ptr 0x0, zero 8 }}, align 8, linkage(external)\n"
1954 );
1955 assert_eq!(only(&text), "@x: a scalar in an image has a width and ptr has none");
1956 }
1957
1958 #[test]
1959 fn a_declaration_of_something_another_module_defines_may_be_constant() {
1960 let text = format!("{HEADER}\nglobal @x : bytes 4, align 4, linkage(external), constant\n");
1963 assert!(errors(&text).is_empty(), "{:?}", errors(&text));
1964 }
1965
1966 #[test]
1967 fn an_alias_to_itself_is_reported() {
1968 let text = format!("{HEADER}\nalias @a = @a, linkage(external)\n");
1969 assert_eq!(only(&text), "@a: an alias to itself");
1970 }
1971
1972 #[test]
1973 fn an_ifunc_that_does_not_resolve_through_a_function_is_reported() {
1974 let text = format!(
1975 "{HEADER}
1976global @g : i32 = 0, align 4, linkage(external)
1977
1978ifunc @f = @g, linkage(external)
1979"
1980 );
1981 assert_eq!(
1982 only(&text),
1983 "@f: an ifunc resolves through a function and this target is not one"
1984 );
1985 }
1986
1987 #[test]
1988 fn attributes_that_contradict_each_other_are_reported() {
1989 let text =
1990 format!("{HEADER}\nfunc @f(), linkage(external), attrs(always_inline, noinline);\n");
1991 assert_eq!(
1992 only(&text),
1993 "@f: `always_inline` and `noinline` cannot both be true of a function"
1994 );
1995 }
1996
1997 fn one_error(module: &Module, func: &Func, names: &Interner) -> String {
2001 match verify_func(module, func, names) {
2002 Ok(()) => panic!("that was expected to be turned down"),
2003 Err(errors) => {
2004 assert_eq!(errors.len(), 1, "{errors:#?}");
2005 errors[0].to_string()
2006 }
2007 }
2008 }
2009
2010 #[test]
2011 fn a_value_the_function_does_not_have_is_reported() {
2012 let mut names = Interner::new();
2013 let module = Module::new(names.intern("built.c"), &target());
2014 let mut func = Func::new(names.intern("f"), Signature::new());
2015 let block = func.create_block();
2016 let args = func.push_values(&[Value::from_usize(9)]);
2017 let inst =
2018 func.create_inst(InstData { args, ..InstData::new(Opcode::Return) }, &[], Span::DUMMY);
2019 func.append_inst(block, inst);
2020 assert_eq!(
2021 one_error(&module, &func, &names),
2022 "@f block0 return: %9 is not a value of this function"
2023 );
2024 }
2025
2026 #[test]
2027 fn a_value_whose_definition_has_been_taken_out_is_reported() {
2028 let mut names = Interner::new();
2029 let module = Module::new(names.intern("built.c"), &target());
2030 let i32_ = Type::int(32);
2031 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
2032 let block = func.create_block();
2033 let mut b = Builder::new(&mut func, block);
2034 let value = b.iconst(i32_, 7);
2035 b.ret(&[value]);
2036 let Def::Result { inst, .. } = func[value].def else { unreachable!("a constant") };
2037 func.remove_inst(inst);
2038 assert_eq!(
2039 one_error(&module, &func, &names),
2040 "@f block0 return: %0 is produced by an instruction that is not in the function"
2041 );
2042 }
2043
2044 #[test]
2045 fn an_instruction_carrying_another_opcodes_payload_is_reported() {
2046 let mut names = Interner::new();
2049 let module = Module::new(names.intern("built.c"), &target());
2050 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
2051 let block = func.create_block();
2052 let param = func.append_param(block, Type::int(32));
2053 let args = func.push_values(&[param, param]);
2054 let inst = func.create_inst(
2055 InstData { args, extra: Extra::IntPred(IntPred::Eq), ..InstData::new(Opcode::Add) },
2056 &[Type::int(32)],
2057 Span::DUMMY,
2058 );
2059 func.append_inst(block, inst);
2060 let ret = func.create_inst(InstData::new(Opcode::Return), &[], Span::DUMMY);
2061 func.append_inst(block, ret);
2062 assert_eq!(
2063 one_error(&module, &func, &names),
2064 "@f block0 add: add carries nothing and this one carries an integer comparison"
2065 );
2066 }
2067
2068 #[test]
2069 fn a_metadata_node_that_is_its_own_parent_is_reported() {
2070 let mut names = Interner::new();
2071 let mut module = Module::new(names.intern("built.c"), &target());
2072 module.add_meta(MetaNode {
2073 name: names.intern("int"),
2074 parent: Some(Idx::from_usize(0)),
2075 offset: 0,
2076 });
2077 let found = match verify(&module, &names) {
2078 Ok(()) => panic!("that was expected to be turned down"),
2079 Err(errors) => errors,
2080 };
2081 assert_eq!(found.len(), 1, "{found:#?}");
2082 assert_eq!(
2083 found[0].to_string(),
2084 "!0: a metadata node's parent comes before it and this one is !0"
2085 );
2086 }
2087
2088 #[test]
2089 fn a_datalayout_the_target_does_not_imply_is_reported() {
2090 let mut names = Interner::new();
2091 let mut module = Module::new(names.intern("built.c"), &target());
2092 module.datalayout = DataLayout::parse("e-p:32:32-i64:64-f80:32-S64").expect("a layout");
2093 let found = match verify(&module, &names) {
2094 Ok(()) => panic!("that was expected to be turned down"),
2095 Err(errors) => errors,
2096 };
2097 assert_eq!(found.len(), 1, "{found:#?}");
2098 assert!(found[0].to_string().starts_with("@built.c: the datalayout is "), "{}", found[0]);
2099 }
2100
2101 #[test]
2102 fn a_flag_riding_along_where_it_is_read_is_not_reported() {
2103 let mut names = Interner::new();
2104 let module = Module::new(names.intern("built.c"), &target());
2105 let i32_ = Type::int(32);
2106 let mut func = Func::new(
2107 names.intern("f"),
2108 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
2109 );
2110 let block = func.create_block();
2111 let param = func.append_param(block, i32_);
2112 let mut b = Builder::new(&mut func, block);
2113 let sum = b.binary(Opcode::Add, param, param, Flags::NSW);
2114 b.ret(&[sum]);
2115 assert!(verify_func(&module, &func, &names).is_ok());
2116 }
2117
2118 #[test]
2119 fn a_declaration_is_checked_and_has_nothing_else_to_check() {
2120 let mut names = Interner::new();
2121 let module = Module::new(names.intern("built.c"), &target());
2122 let func = Func::new(Symbol::from_raw(0), Signature::new());
2123 assert!(func.is_declaration());
2124 assert!(verify_func(&module, &func, &names).is_ok());
2125 }
2126}