1use std::fmt;
43
44use rucc_base::Interner;
45use rucc_target::{Slot, TargetInfo};
46
47use crate::func::Func;
48use crate::inst::{Abi, Block, CallInfo, Def, Inst, Param, Signature, VaInfo, 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::VaObject(at) => {
588 let object = func[at];
589 self.slots(func, object);
590 func[object.mem]
591 }
592 Extra::Order(order) => {
593 if !order.is_valid_for_rmw() {
594 self.error("a fence is not a fence unless it orders something");
595 }
596 return;
597 }
598 _ => return,
599 };
600 if !info.align.is_power_of_two() {
601 self.error(format!("an alignment is a power of two and this is {}", info.align));
602 }
603 if let Some(tbaa) = info.tbaa {
604 if tbaa.index() >= self.module.counts().metadata {
605 self.error(format!("!{} is not a metadata node of this module", tbaa.raw()));
606 }
607 }
608 let ok = match opcode {
609 Opcode::AtomicLoad => info.order.is_valid_for_load(),
610 Opcode::AtomicStore => info.order.is_valid_for_store(),
611 Opcode::AtomicRmw | Opcode::Cmpxchg => info.order.is_valid_for_rmw(),
612 _ => info.order == MemOrder::NotAtomic,
615 };
616 if !ok {
617 self.error(format!("{} cannot be asked for {}", opcode.name(), info.order));
618 }
619 if matches!(opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) && info.size == 0 {
620 self.error(format!("{} moves no bytes", opcode.name()));
621 }
622 }
623
624 fn slots(&mut self, func: &'a Func, object: VaInfo) {
630 let size = func[object.mem].size;
631 for &slot in &func[object.slots] {
632 let width = match slot {
633 Slot::Integer { size, .. } => u64::from(size),
634 Slot::Float { format, .. } => u64::from(format.width()).div_ceil(8),
635 };
636 if slot.offset() + width > size {
637 self.error(format!(
638 "a slot holds bytes {} to {} of an object of {size} bytes",
639 slot.offset(),
640 slot.offset() + width
641 ));
642 }
643 }
644 }
645
646 #[expect(clippy::too_many_lines, reason = "one arm per group of opcodes, and they differ")]
652 fn shape(&mut self, func: &'a Func, inst: Inst) {
653 let data = &func[inst];
654 let opcode = data.opcode;
655 let args = &func[data.args];
656 let arity = args.len();
657 let arg = |n: usize| func[args[n]].ty;
658 let results = usize::from(data.results);
659 let res = |n: usize| func[data.results().nth(n).expect("within the count")].ty;
660
661 match opcode {
662 Opcode::IConst => {
664 if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_int() {
665 self.error(format!(
666 "iconst produces an integer and this one produces {}",
667 res(0)
668 ));
669 }
670 }
671 Opcode::FConst => {
672 if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_float() {
673 self.error(format!(
674 "fconst produces a floating point value and this one produces {}",
675 res(0)
676 ));
677 }
678 }
679 Opcode::Splat => {
680 if self.takes(opcode, arity, 0) && results == 1 && !res(0).is_vector() {
681 self.error(format!("splat produces a vector and this one produces {}", res(0)));
682 }
683 }
684 Opcode::GlobalAddr
685 | Opcode::StackSave
686 | Opcode::FrameAddress
687 | Opcode::ReturnAddress => {
688 if results == 1 && !res(0).is_ptr() {
689 self.error(format!(
690 "{} produces a pointer and this one produces {}",
691 opcode.name(),
692 res(0)
693 ));
694 }
695 }
696
697 Opcode::Add
699 | Opcode::Sub
700 | Opcode::Mul
701 | Opcode::SDiv
702 | Opcode::UDiv
703 | Opcode::SRem
704 | Opcode::URem
705 | Opcode::And
706 | Opcode::Or
707 | Opcode::Xor
708 | Opcode::Shl
709 | Opcode::LShr
710 | Opcode::AShr => {
711 if self.takes(opcode, arity, 2) {
712 self.integer(opcode, arg(0), 0);
713 self.agree(opcode, arg(0), arg(1));
714 if results == 1 {
715 self.produces(opcode, res(0), arg(0));
716 }
717 }
718 }
719
720 Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv | Opcode::FRem => {
722 if self.takes(opcode, arity, 2) {
723 self.floating(opcode, arg(0), 0);
724 self.agree(opcode, arg(0), arg(1));
725 if results == 1 {
726 self.produces(opcode, res(0), arg(0));
727 }
728 }
729 }
730 Opcode::FNeg => {
731 if self.takes(opcode, arity, 1) {
732 self.floating(opcode, arg(0), 0);
733 if results == 1 {
734 self.produces(opcode, res(0), arg(0));
735 }
736 }
737 }
738 Opcode::Fma => {
739 if self.takes(opcode, arity, 3) {
740 self.floating(opcode, arg(0), 0);
741 self.agree(opcode, arg(0), arg(1));
742 self.agree(opcode, arg(0), arg(2));
743 if results == 1 {
744 self.produces(opcode, res(0), arg(0));
745 }
746 }
747 }
748
749 Opcode::ICmp | Opcode::FCmp => {
751 if self.takes(opcode, arity, 2) {
752 if opcode == Opcode::FCmp {
753 self.floating(opcode, arg(0), 0);
754 } else if !arg(0).lane().is_int() && !arg(0).is_ptr() {
755 self.error(format!(
756 "operand 1 of icmp is an integer or a pointer and this one is {}",
757 arg(0)
758 ));
759 }
760 self.agree(opcode, arg(0), arg(1));
761 if results == 1 {
762 self.produces(opcode, res(0), arg(0).with_lane(Type::I1));
763 }
764 }
765 }
766
767 Opcode::Trunc | Opcode::SExt | Opcode::ZExt => {
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 self.widens(opcode, res(0), arg(0), opcode != Opcode::Trunc);
773 }
774 }
775 Opcode::FPTrunc | Opcode::FPExt => {
776 if self.takes(opcode, arity, 1) && results == 1 {
777 self.floating(opcode, arg(0), 0);
778 self.lanes(opcode, res(0), arg(0));
779 self.widens(opcode, res(0), arg(0), opcode == Opcode::FPExt);
780 }
781 }
782 Opcode::FPToSI | Opcode::FPToUI => {
783 if self.takes(opcode, arity, 1) && results == 1 {
784 self.floating(opcode, arg(0), 0);
785 self.lanes(opcode, res(0), arg(0));
786 if !res(0).lane().is_int() {
787 self.error(format!(
788 "{} produces an integer and this one produces {}",
789 opcode.name(),
790 res(0)
791 ));
792 }
793 }
794 }
795 Opcode::SIToFP | Opcode::UIToFP => {
796 if self.takes(opcode, arity, 1) && results == 1 {
797 self.integer(opcode, arg(0), 0);
798 self.lanes(opcode, res(0), arg(0));
799 if !res(0).lane().is_float() {
800 self.error(format!(
801 "{} produces a floating point value and this one produces {}",
802 opcode.name(),
803 res(0)
804 ));
805 }
806 }
807 }
808 Opcode::PtrToInt => {
809 if self.takes(opcode, arity, 1) && results == 1 {
810 self.pointer(opcode, arg(0), 0);
811 self.integer(opcode, res(0), 0);
812 }
813 }
814 Opcode::IntToPtr => {
815 if self.takes(opcode, arity, 1) && results == 1 {
816 self.integer(opcode, arg(0), 0);
817 if !res(0).is_ptr() {
818 self.error(format!(
819 "inttoptr produces a pointer and this one produces {}",
820 res(0)
821 ));
822 }
823 }
824 }
825 Opcode::Bitcast => {
826 if self.takes(opcode, arity, 1) && results == 1 {
827 let (from, to) = (arg(0), res(0));
828 if from.is_ptr() != to.is_ptr() {
829 self.error(
832 "a bitcast between a pointer and a number is ptrtoint or inttoptr",
833 );
834 } else if width(from) != width(to) {
835 self.error(format!("a bitcast keeps the width and {from} and {to} differ"));
836 }
837 }
838 }
839
840 Opcode::Alloca => {
842 if arity > 1 {
843 self.takes(opcode, arity, 1);
844 } else if arity == 1 {
845 self.integer(opcode, arg(0), 0);
846 } else if func.block_of(inst) != func.entry() {
847 self.error("an alloca of a fixed size belongs in the entry block");
850 }
851 if results == 1 && !res(0).is_ptr() {
852 self.error(format!(
853 "alloca produces a pointer and this one produces {}",
854 res(0)
855 ));
856 }
857 }
858 Opcode::Load | Opcode::AtomicLoad => {
859 if self.takes(opcode, arity, 1) {
860 self.pointer(opcode, arg(0), 0);
861 }
862 if results == 1 && res(0).is_void() {
863 self.error(format!("{} reads a value and void is not one", opcode.name()));
864 }
865 }
866 Opcode::Store | Opcode::AtomicStore => {
867 if self.takes(opcode, arity, 2) {
868 if arg(0).is_void() {
869 self.error(format!("{} writes a value and void is not one", opcode.name()));
870 }
871 self.pointer(opcode, arg(1), 1);
872 }
873 }
874 Opcode::PtrAdd => {
875 if self.takes(opcode, arity, 2) {
876 self.pointer(opcode, arg(0), 0);
877 self.integer(opcode, arg(1), 1);
878 if results == 1 && !res(0).is_ptr() {
879 self.error(format!(
880 "ptr_add produces a pointer and this one produces {}",
881 res(0)
882 ));
883 }
884 }
885 }
886 Opcode::Memcpy | Opcode::Memmove => {
887 if self.takes(opcode, arity, 2) {
888 self.pointer(opcode, arg(0), 0);
889 self.pointer(opcode, arg(1), 1);
890 }
891 }
892 Opcode::Memset => {
893 if self.takes(opcode, arity, 2) {
894 self.pointer(opcode, arg(0), 0);
895 self.integer(opcode, arg(1), 1);
896 }
897 }
898 Opcode::AtomicRmw => {
899 if self.takes(opcode, arity, 2) {
900 self.pointer(opcode, arg(0), 0);
901 self.integer(opcode, arg(1), 1);
902 if results == 1 {
903 self.produces(opcode, res(0), arg(1));
904 }
905 }
906 }
907 Opcode::Cmpxchg => {
908 if self.takes(opcode, arity, 3) {
909 self.pointer(opcode, arg(0), 0);
910 self.agree(opcode, arg(1), arg(2));
911 if results == 2 {
912 self.produces(opcode, res(0), arg(1));
913 self.produces(opcode, res(1), arg(1).with_lane(Type::I1));
914 }
915 }
916 }
917 Opcode::Fence | Opcode::Unreachable | Opcode::UnreachableHint => {
918 self.takes(opcode, arity, 0);
919 }
920 Opcode::Prefetch | Opcode::StackRestore | Opcode::VaStart | Opcode::VaEnd => {
921 if self.takes(opcode, arity, 1) {
922 self.pointer(opcode, arg(0), 0);
923 }
924 }
925 Opcode::VaCopy => {
926 if self.takes(opcode, arity, 2) {
927 self.pointer(opcode, arg(0), 0);
928 self.pointer(opcode, arg(1), 1);
929 }
930 }
931 Opcode::VaArg => {
932 if self.takes(opcode, arity, 1) {
933 self.pointer(opcode, arg(0), 0);
934 }
935 if results == 1 && res(0).is_void() {
936 self.error("va_arg reads a value and void is not one");
937 }
938 }
939 Opcode::VaObject => {
940 if self.takes(opcode, arity, 1) {
941 self.pointer(opcode, arg(0), 0);
942 }
943 if results == 1 && res(0) != Type::PTR {
944 self.error(format!(
945 "va_object answers where the object is and {} is not an address",
946 res(0)
947 ));
948 }
949 }
950
951 Opcode::Jump => {
953 self.takes(opcode, arity, 0);
954 self.targets(func, inst, 1);
955 }
956 Opcode::BrIf => {
957 if self.takes(opcode, arity, 1) && arg(0) != Type::I1 {
958 self.error(format!("br_if branches on an i1 and this one on {}", arg(0)));
959 }
960 self.targets(func, inst, 2);
961 }
962 Opcode::Switch => {
963 if self.takes(opcode, arity, 1) {
964 self.integer(opcode, arg(0), 0);
965 }
966 }
967 Opcode::BlockAddr => {
968 self.takes(opcode, arity, 0);
969 self.targets(func, inst, 1);
970 if results == 1 && !res(0).is_ptr() {
971 self.error(format!(
972 "block_addr produces a pointer and this one produces {}",
973 res(0)
974 ));
975 }
976 if func.successors(inst).any(|call| !func[call.args].is_empty()) {
977 self.error("block_addr names a block and passes it arguments");
980 }
981 }
982 Opcode::IndirectBr => {
983 if self.takes(opcode, arity, 1) {
984 self.pointer(opcode, arg(0), 0);
985 }
986 }
989 Opcode::Return => {
990 let want = &func.signature().returns;
991 if arity != want.len() {
992 self.error(format!(
993 "the signature returns {} and this returns {arity}",
994 want.len()
995 ));
996 } else {
997 for (index, ty) in want.iter().map(|param| param.ty).enumerate() {
998 if arg(index) != ty {
999 self.error(format!(
1000 "result {} of the signature is {ty} and this returns {}",
1001 index + 1,
1002 arg(index)
1003 ));
1004 }
1005 }
1006 }
1007 }
1008
1009 Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => {
1011 let Extra::Call(at) = data.extra else { return };
1012 let info = func[at];
1013 let signature = &func[info.signature];
1014 let indirect = usize::from(opcode == Opcode::CallIndirect);
1015 if indirect == 1 {
1016 if arity == 0 {
1017 self.error("call_indirect calls through a pointer and has no operands");
1018 return;
1019 }
1020 self.pointer(opcode, arg(0), 0);
1021 }
1022 let passed = arity - indirect;
1023 let enough = if signature.variadic {
1024 passed >= signature.params.len()
1025 } else {
1026 passed == signature.params.len()
1027 };
1028 if enough {
1029 for (index, ty) in signature.param_types().enumerate() {
1030 if arg(index + indirect) != ty {
1031 self.error(format!(
1032 "parameter {} of the signature is {ty} and this argument is {}",
1033 index + 1,
1034 arg(index + indirect)
1035 ));
1036 }
1037 }
1038 } else {
1039 self.error(format!(
1040 "the signature takes {}{} and this call passes {passed}",
1041 signature.params.len(),
1042 if signature.variadic { " or more" } else { "" }
1043 ));
1044 }
1045 self.varargs(func, &info, passed, |n| arg(n + indirect));
1046 if results != signature.returns.len() {
1047 self.error(format!(
1048 "the signature returns {} and this call produces {results}",
1049 signature.returns.len()
1050 ));
1051 } else {
1052 for (index, ty) in signature.return_types().enumerate() {
1053 if res(index) != ty {
1054 self.error(format!(
1055 "result {} of the signature is {ty} and this call produces {}",
1056 index + 1,
1057 res(index)
1058 ));
1059 }
1060 }
1061 }
1062 if let Some(callee) = info.callee {
1065 if let Some(SymbolRef::Func(id)) = self.module.lookup(callee) {
1066 if self.module[id].signature() != signature {
1067 self.error(format!(
1068 "@{} is declared here with another signature",
1069 self.names.resolve(callee)
1070 ));
1071 }
1072 }
1073 }
1074 }
1075
1076 Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop | Opcode::Bswap | Opcode::Bitreverse => {
1078 if self.takes(opcode, arity, 1) {
1079 self.integer(opcode, arg(0), 0);
1080 if results == 1 {
1081 self.produces(opcode, res(0), arg(0));
1082 }
1083 }
1084 }
1085
1086 Opcode::SAddOverflow
1088 | Opcode::UAddOverflow
1089 | Opcode::SSubOverflow
1090 | Opcode::USubOverflow
1091 | Opcode::SMulOverflow
1092 | Opcode::UMulOverflow => {
1093 if self.takes(opcode, arity, 2) {
1094 self.integer(opcode, arg(0), 0);
1095 self.agree(opcode, arg(0), arg(1));
1096 if results == 2 {
1097 self.produces(opcode, res(0), arg(0));
1098 self.produces(opcode, res(1), arg(0).with_lane(Type::I1));
1099 }
1100 }
1101 }
1102 Opcode::Expect => {
1103 if self.takes(opcode, arity, 2) {
1104 self.agree(opcode, arg(0), arg(1));
1105 if results == 1 {
1106 self.produces(opcode, res(0), arg(0));
1107 }
1108 }
1109 }
1110
1111 Opcode::SetjmpMarker
1115 | Opcode::LongjmpMarker
1116 | Opcode::InlineAsm
1117 | Opcode::TargetIntrinsic => {}
1118 }
1119 }
1120
1121 fn takes(&mut self, opcode: Opcode, got: usize, want: usize) -> bool {
1125 if got == want {
1126 return true;
1127 }
1128 self.error(format!("{} takes {want} operands and this one has {got}", opcode.name()));
1129 false
1130 }
1131
1132 fn targets(&mut self, func: &'a Func, inst: Inst, want: usize) {
1134 let got = func.successors(inst).count();
1135 if got != want {
1136 self.error(format!(
1137 "{} branches to {want} blocks and this one to {got}",
1138 func[inst].opcode.name()
1139 ));
1140 }
1141 }
1142
1143 fn integer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1144 if !ty.lane().is_int() {
1145 self.error(format!(
1146 "operand {} of {} is an integer and this one is {ty}",
1147 n + 1,
1148 opcode.name()
1149 ));
1150 }
1151 }
1152
1153 fn floating(&mut self, opcode: Opcode, ty: Type, n: usize) {
1154 if !ty.lane().is_float() {
1155 self.error(format!(
1156 "operand {} of {} is a floating point value and this one is {ty}",
1157 n + 1,
1158 opcode.name()
1159 ));
1160 }
1161 }
1162
1163 fn pointer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1164 if !ty.is_ptr() {
1165 self.error(format!(
1166 "operand {} of {} is a pointer and this one is {ty}",
1167 n + 1,
1168 opcode.name()
1169 ));
1170 }
1171 }
1172
1173 fn agree(&mut self, opcode: Opcode, first: Type, second: Type) {
1175 if first != second {
1176 self.error(format!(
1177 "the operands of {} have one type and these are {first} and {second}",
1178 opcode.name()
1179 ));
1180 }
1181 }
1182
1183 fn produces(&mut self, opcode: Opcode, got: Type, want: Type) {
1185 if got != want {
1186 self.error(format!(
1187 "{} produces {want} here and this one produces {got}",
1188 opcode.name()
1189 ));
1190 }
1191 }
1192
1193 fn lanes(&mut self, opcode: Opcode, to: Type, from: Type) {
1195 if to.lanes() != from.lanes() {
1196 self.error(format!(
1197 "{} keeps the lane count and {from} has {} and {to} has {}",
1198 opcode.name(),
1199 from.lanes(),
1200 to.lanes()
1201 ));
1202 }
1203 }
1204
1205 fn widens(&mut self, opcode: Opcode, to: Type, from: Type, wider: bool) {
1207 let (a, b) = (to.lane().bits(), from.lane().bits());
1208 let ok = if wider { a > b } else { a < b };
1209 if !ok {
1210 let way = if wider { "wider" } else { "narrower" };
1211 self.error(format!(
1212 "{} produces something {way} and {from} to {to} is not",
1213 opcode.name()
1214 ));
1215 }
1216 }
1217
1218 fn error(&mut self, message: impl Into<String>) {
1221 let at = self.locate();
1222 self.at(at, message);
1223 }
1224
1225 fn at(&mut self, at: String, message: impl Into<String>) {
1226 self.errors.push(VerifyError { at, message: message.into() });
1227 }
1228
1229 fn locate(&self) -> String {
1235 use fmt::Write as _;
1236 let mut at = String::new();
1237 if let Some(func) = self.func {
1238 let _ = write!(at, "@{}", self.names.resolve(func.name));
1239 if let Some(block) = self.block {
1240 let _ = write!(at, " block{}", block.raw());
1241 }
1242 if let Some(inst) = self.inst {
1243 let _ = write!(at, " {}", func[inst].opcode.name());
1244 }
1245 }
1246 at
1247 }
1248}
1249
1250struct Layout {
1253 block: Vec<Option<Block>>,
1254 position: Vec<u32>,
1255}
1256
1257impl Layout {
1258 fn new(func: &Func) -> Self {
1259 let counts = func.counts();
1260 let mut layout =
1261 Layout { block: vec![None; counts.insts], position: vec![0; counts.insts] };
1262 for block in func.blocks() {
1263 for (position, inst) in func.insts(block).enumerate() {
1264 layout.block[inst.index()] = Some(block);
1265 layout.position[inst.index()] = position as u32;
1266 }
1267 }
1268 layout
1269 }
1270
1271 fn block_of(&self, inst: Inst) -> Option<Block> {
1272 self.block[inst.index()]
1273 }
1274
1275 fn position(&self, inst: Inst) -> u32 {
1276 self.position[inst.index()]
1277 }
1278}
1279
1280struct Doms {
1287 rank: Vec<Option<u32>>,
1290 idom: Vec<u32>,
1292}
1293
1294impl Doms {
1295 fn new(func: &Func) -> Self {
1296 let counts = func.counts();
1297 let entry = func.entry().expect("a function with blocks has a first one");
1298
1299 let mut succs: Vec<Vec<Block>> = vec![Vec::new(); counts.blocks];
1307 for block in func.blocks() {
1308 for inst in func.insts(block) {
1309 succs[block.index()].extend(func.successors(inst).map(|call| call.block));
1310 }
1311 }
1312
1313 let mut order = Vec::new();
1316 let mut seen = vec![false; counts.blocks];
1317 let mut stack = vec![(entry, 0usize)];
1318 seen[entry.index()] = true;
1319 while let Some((block, next)) = stack.pop() {
1320 match succs[block.index()].get(next) {
1321 Some(&target) => {
1322 stack.push((block, next + 1));
1323 if !seen[target.index()] {
1324 seen[target.index()] = true;
1325 stack.push((target, 0));
1326 }
1327 }
1328 None => order.push(block),
1329 }
1330 }
1331 order.reverse();
1332
1333 let mut rank = vec![None; counts.blocks];
1334 for (index, &block) in order.iter().enumerate() {
1335 rank[block.index()] = Some(index as u32);
1336 }
1337 let mut preds: Vec<Vec<u32>> = vec![Vec::new(); order.len()];
1338 for (index, &block) in order.iter().enumerate() {
1339 for &target in &succs[block.index()] {
1340 if let Some(target) = rank[target.index()] {
1341 preds[target as usize].push(index as u32);
1342 }
1343 }
1344 }
1345
1346 const NONE: u32 = u32::MAX;
1350 let mut idom = vec![NONE; order.len()];
1351 if !order.is_empty() {
1352 idom[0] = 0;
1353 }
1354 let mut changed = true;
1355 while changed {
1356 changed = false;
1357 for index in 1..order.len() {
1358 let mut new = NONE;
1359 for &pred in &preds[index] {
1360 if idom[pred as usize] == NONE {
1361 continue;
1362 }
1363 new = if new == NONE { pred } else { meet(&idom, new, pred) };
1364 }
1365 if new != NONE && idom[index] != new {
1366 idom[index] = new;
1367 changed = true;
1368 }
1369 }
1370 }
1371 Doms { rank, idom }
1372 }
1373
1374 fn reaches(&self, block: Block) -> bool {
1376 self.rank[block.index()].is_some()
1377 }
1378
1379 fn dominates(&self, of: Block, block: Block) -> bool {
1385 let (Some(a), Some(b)) = (self.rank[of.index()], self.rank[block.index()]) else {
1386 return true;
1387 };
1388 let mut walk = b;
1389 while walk > a {
1390 walk = self.idom[walk as usize];
1391 }
1392 walk == a
1393 }
1394}
1395
1396fn meet(idom: &[u32], mut a: u32, mut b: u32) -> u32 {
1398 while a != b {
1399 while a > b {
1400 a = idom[a as usize];
1401 }
1402 while b > a {
1403 b = idom[b as usize];
1404 }
1405 }
1406 a
1407}
1408
1409fn width(ty: Type) -> u64 {
1411 u64::from(ty.bits()) * u64::from(ty.lanes())
1412}
1413
1414fn types(list: &[Type]) -> String {
1416 if list.is_empty() {
1417 return "nothing".to_string();
1418 }
1419 list.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
1420}
1421
1422#[cfg(test)]
1423mod tests {
1424 use rucc_base::{Idx, Interner, Symbol};
1425 use rucc_diag::Span;
1426 use rucc_target::{Arch, Env, Os, Triple};
1427
1428 use super::*;
1429 use crate::fixtures::{EXAMPLE, SYMBOLS, ZOO};
1430 use crate::func::Builder;
1431 use crate::inst::{InstData, MetaNode, Signature};
1432 use crate::{Flags, IntPred, parse};
1433
1434 fn target() -> TargetInfo {
1435 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1436 }
1437
1438 fn errors(text: &str) -> Vec<String> {
1441 let mut names = Interner::new();
1442 let module = match parse(text, &mut names) {
1443 Ok(module) => module,
1444 Err(error) => panic!("{error}"),
1445 };
1446 match verify(&module, &names) {
1447 Ok(()) => Vec::new(),
1448 Err(errors) => errors.iter().map(ToString::to_string).collect(),
1449 }
1450 }
1451
1452 fn only(text: &str) -> String {
1454 let found = errors(text);
1455 assert_eq!(found.len(), 1, "{found:#?}");
1456 found.into_iter().next().expect("just counted one")
1457 }
1458
1459 const HEADER: &str = "\
1460; ModuleID = 'bad.c'
1461; format 0
1462target triple = \"x86_64-unknown-linux-gnu\"
1463target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
1464";
1465
1466 fn wrap(signature: &str, body: &str) -> String {
1468 format!("{HEADER}\nfunc @f{signature}, linkage(external) {{\n{body}}}\n")
1469 }
1470
1471 #[test]
1472 fn the_three_fixtures_are_modules_the_compiler_may_believe() {
1473 for text in [EXAMPLE, ZOO, SYMBOLS] {
1474 assert_eq!(errors(text), Vec::<String>::new());
1475 }
1476 }
1477
1478 #[test]
1479 fn a_use_that_its_definition_does_not_reach_is_reported() {
1480 let text = wrap(
1481 "(i1) -> i32",
1482 "block0(%0: i1):
1483 br_if %0, block1, block2
1484
1485block1:
1486 %1 = iconst.i32 7
1487 jump block2
1488
1489block2:
1490 return %1
1491",
1492 );
1493 assert_eq!(
1494 only(&text),
1495 "@f block2 return: %1 is produced in block1 and does not reach here"
1496 );
1497 }
1498
1499 #[test]
1500 fn a_use_before_its_definition_in_the_same_block_is_reported() {
1501 let text = wrap(
1502 "() -> i32",
1503 "block0:
1504 %0 = iconst.i32 1
1505 %1 = add %2, %0
1506 %2 = iconst.i32 2
1507 return %1
1508",
1509 );
1510 assert_eq!(only(&text), "@f block0 add: %2 is produced in block0 and does not reach here");
1511 }
1512
1513 #[test]
1514 fn a_call_that_takes_its_arguments_the_way_the_abi_says_is_believed() {
1515 let text = wrap(
1516 "(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext)",
1517 "block0(%0: ptr, %1: ptr, %2: i8):
1518 return
1519",
1520 );
1521 assert_eq!(errors(&text), Vec::<String>::new());
1522 }
1523
1524 #[test]
1525 fn a_call_that_says_how_an_argument_past_its_parameter_list_travels_is_believed() {
1526 let text = wrap(
1527 "(ptr)",
1528 "block0(%0: ptr):
1529 call @p(%0, %0 byval(24, align 8)) : (ptr, ...)
1530 return
1531",
1532 );
1533 assert_eq!(errors(&text), Vec::<String>::new());
1534 }
1535
1536 #[test]
1537 fn a_call_that_says_it_of_something_that_is_not_a_pointer_is_reported() {
1538 let text = wrap(
1539 "(ptr)",
1540 "block0(%0: ptr):
1541 %1 = iconst.i32 1
1542 call @p(%0, %1 byval(4, align 4)) : (ptr, ...)
1543 return
1544",
1545 );
1546 assert_eq!(
1547 only(&text),
1548 "@f block0 call: argument 2 travels indirectly and i32 is not a pointer"
1549 );
1550 }
1551
1552 #[test]
1553 fn a_call_that_says_an_argument_past_its_parameter_list_is_an_sret_is_reported() {
1554 let text = wrap(
1555 "(ptr)",
1556 "block0(%0: ptr):
1557 call @p(%0, %0 sret(24, align 8)) : (ptr, ...)
1558 return
1559",
1560 );
1561 assert_eq!(
1562 only(&text),
1563 "@f block0 call: argument 2 is an sret and only a parameter can be one"
1564 );
1565 }
1566
1567 #[test]
1568 fn a_call_that_is_not_variadic_says_nothing_about_a_variadic_argument() {
1569 let text = wrap(
1570 "(ptr)",
1571 "block0(%0: ptr):
1572 call @p(%0, %0 byval(24, align 8)) : (ptr)
1573 return
1574",
1575 );
1576 assert_eq!(
1577 errors(&text),
1578 vec![
1579 "@f block0 call: the signature takes 1 and this call passes 2",
1580 "@f block0 call: this call is not variadic and says how a variadic argument \
1581 travels",
1582 ]
1583 );
1584 }
1585
1586 #[test]
1587 fn an_sret_that_is_not_the_first_parameter_is_reported() {
1588 let text = wrap(
1589 "(ptr byval(8, align 8), ptr sret(16, align 8))",
1590 "block0(%0: ptr, %1: ptr):
1591 return
1592",
1593 );
1594 assert_eq!(only(&text), "@f: sret is the first parameter and this one is not");
1595 }
1596
1597 #[test]
1598 fn a_function_returning_through_sret_returns_nothing_else() {
1599 let text = wrap(
1600 "(ptr sret(8, align 8)) -> i32",
1601 "block0(%0: ptr):
1602 %1 = iconst.i32 7
1603 return %1
1604",
1605 );
1606 assert_eq!(only(&text), "@f: a signature returning through sret returns nothing else");
1607 }
1608
1609 #[test]
1610 fn an_object_that_travels_indirectly_travels_behind_a_pointer() {
1611 let text = wrap(
1612 "(i32 byval(4, align 4))",
1613 "block0(%0: i32):
1614 return
1615",
1616 );
1617 assert_eq!(only(&text), "@f: parameter 1 travels indirectly and i32 is not a pointer");
1618 }
1619
1620 #[test]
1621 fn an_alignment_a_parameter_could_not_have_is_reported() {
1622 let text = wrap(
1623 "(ptr byval(24, align 3))",
1624 "block0(%0: ptr):
1625 return
1626",
1627 );
1628 assert_eq!(only(&text), "@f: an alignment is a power of two and this is 3");
1629 }
1630
1631 #[test]
1635 fn a_slot_holding_bytes_the_object_does_not_have_is_reported() {
1636 let text = wrap(
1637 "(ptr) -> ptr",
1638 "block0(%0: ptr):
1639 %1 = va_object %0, size 12, align 8, in(int 8 at 0, int 8 at 8)
1640 return %1
1641",
1642 );
1643 assert_eq!(
1644 only(&text),
1645 "@f block0 va_object: a slot holds bytes 8 to 16 of an object of 12 bytes"
1646 );
1647 }
1648
1649 #[test]
1650 fn a_result_does_not_travel_indirectly() {
1651 let text = wrap(
1654 "() -> ptr byval(16, align 8)",
1655 "block0:
1656 %0 = iconst.i64 0
1657 %1 = inttoptr.ptr %0
1658 return %1
1659",
1660 );
1661 assert_eq!(only(&text), "@f: result 1 travels indirectly and a result cannot");
1662 }
1663
1664 #[test]
1665 fn an_extension_is_asked_of_an_integer_and_not_of_anything_else() {
1666 let text = wrap(
1667 "(ptr zext)",
1668 "block0(%0: ptr):
1669 return
1670",
1671 );
1672 assert_eq!(only(&text), "@f: parameter 1 is extended and ptr is not an integer");
1673 }
1674
1675 #[test]
1676 fn a_branch_that_passes_the_wrong_number_of_arguments_is_reported() {
1677 let text = wrap(
1678 "(i32)",
1679 "block0(%0: i32):
1680 jump block1(%0)
1681
1682block1:
1683 return
1684",
1685 );
1686 assert_eq!(
1687 only(&text),
1688 "@f block0 jump: block1 takes 0 arguments and this branch passes 1"
1689 );
1690 }
1691
1692 #[test]
1693 fn a_branch_that_passes_the_wrong_type_is_reported() {
1694 let text = wrap(
1695 "(i32) -> i32",
1696 "block0(%0: i32):
1697 %1 = sext.i64 %0
1698 jump block1(%1)
1699
1700block1(%2: i32):
1701 return %2
1702",
1703 );
1704 assert_eq!(only(&text), "@f block0 jump: argument 1 to block1 is i32 and this one is i64");
1705 }
1706
1707 #[test]
1708 fn a_block_that_does_not_end_in_a_terminator_is_reported() {
1709 let text = wrap("()", "block0:\n %0 = iconst.i32 1\n");
1710 assert_eq!(only(&text), "@f block0: this block does not end in a terminator");
1711 }
1712
1713 #[test]
1714 fn an_instruction_after_the_terminator_is_reported() {
1715 let text = wrap("()", "block0:\n return\n %0 = iconst.i32 1\n");
1716 assert_eq!(only(&text), "@f block0 iconst: this comes after the block's terminator");
1717 }
1718
1719 #[test]
1720 fn an_unreachable_block_is_reported() {
1721 let text = wrap("()", "block0:\n return\n\nblock1:\n return\n");
1722 assert_eq!(only(&text), "@f block1: this block is not reachable and has not been deleted");
1723 }
1724
1725 #[test]
1726 fn a_block_a_jump_to_an_address_arrives_at_is_an_ordinary_target() {
1727 let text = wrap(
1728 "(ptr) -> i32",
1729 "block0(%0: ptr):
1730 %1 = block_addr block1
1731 indirect_br %0, block1
1732
1733block1:
1734 %2 = iconst.i32 1
1735 return %2
1736",
1737 );
1738 assert_eq!(errors(&text), Vec::<String>::new());
1739 }
1740
1741 #[test]
1742 fn a_block_whose_address_is_taken_is_reached_by_the_taking_of_it() {
1743 let text = wrap(
1746 "() -> ptr",
1747 "block0:
1748 %0 = block_addr block1
1749 return %0
1750
1751block1:
1752 unreachable
1753",
1754 );
1755 assert_eq!(errors(&text), Vec::<String>::new());
1756 }
1757
1758 #[test]
1759 fn taking_the_address_of_a_block_and_passing_it_arguments_is_reported() {
1760 let text = wrap(
1763 "(i32) -> ptr",
1764 "block0(%0: i32):
1765 %1 = block_addr block1(%0)
1766 return %1
1767
1768block1(%2: i32):
1769 unreachable
1770",
1771 );
1772 assert_eq!(
1773 only(&text),
1774 "@f block0 block_addr: block_addr names a block and passes it arguments"
1775 );
1776 }
1777
1778 #[test]
1779 fn a_jump_to_something_that_is_not_an_address_is_reported() {
1780 let text = wrap(
1781 "(i32)",
1782 "block0(%0: i32):
1783 indirect_br %0, block1
1784
1785block1:
1786 return
1787",
1788 );
1789 assert_eq!(
1790 only(&text),
1791 "@f block0 indirect_br: operand 1 of indirect_br is a pointer and this one is i32"
1792 );
1793 }
1794
1795 #[test]
1796 fn a_branch_back_to_the_entry_block_is_reported() {
1797 let text = wrap(
1800 "(i32)",
1801 "block0(%0: i32):
1802 jump block1
1803
1804block1:
1805 jump block0(%0)
1806",
1807 );
1808 assert_eq!(
1809 only(&text),
1810 "@f block0: the entry block is branched to, and it takes the arguments"
1811 );
1812 }
1813
1814 #[test]
1815 fn an_entry_block_that_does_not_take_the_arguments_is_reported() {
1816 let text = wrap("(i32)", "block0(%0: i64):\n return\n");
1817 assert_eq!(only(&text), "@f: the entry block takes i64 and the signature says i32");
1818 }
1819
1820 #[test]
1821 fn a_flag_the_opcode_does_not_read_is_reported() {
1822 let text =
1823 wrap("(i32) -> i32", "block0(%0: i32):\n %1 = add.exact %0, %0\n return %1\n");
1824 assert_eq!(only(&text), "@f block0 add: add does not read `exact`");
1825 }
1826
1827 #[test]
1828 fn an_ordering_the_operation_cannot_be_asked_for_is_reported() {
1829 let text = wrap(
1830 "(ptr) -> i32",
1831 "block0(%0: ptr):\n %1 = atomic_load.i32 %0, align 4, release\n return %1\n",
1832 );
1833 assert_eq!(only(&text), "@f block0 atomic_load: atomic_load cannot be asked for release");
1834 }
1835
1836 #[test]
1837 fn an_ordering_on_the_non_atomic_form_is_reported() {
1838 let text = wrap(
1839 "(ptr) -> i32",
1840 "block0(%0: ptr):\n %1 = load.i32 %0, align 4, acquire\n return %1\n",
1841 );
1842 assert_eq!(only(&text), "@f block0 load: load cannot be asked for acquire");
1843 }
1844
1845 #[test]
1846 fn a_va_object_that_answers_anything_but_an_address_is_reported() {
1847 let text = wrap(
1850 "(ptr) -> i64",
1851 "block0(%0: ptr):
1852 %1 = va_object.i64 %0, size 16, align 8
1853 return %1
1854",
1855 );
1856 assert_eq!(
1857 only(&text),
1858 "@f block0 va_object: va_object answers where the object is and i64 is not an address"
1859 );
1860 }
1861
1862 #[test]
1863 fn an_alloca_of_a_fixed_size_outside_the_entry_block_is_reported() {
1864 let text = wrap(
1865 "()",
1866 "block0:
1867 jump block1
1868
1869block1:
1870 %0 = alloca, size 16, align 8
1871 return
1872",
1873 );
1874 assert_eq!(
1875 only(&text),
1876 "@f block1 alloca: an alloca of a fixed size belongs in the entry block"
1877 );
1878 }
1879
1880 #[test]
1881 fn a_dynamic_alloca_may_be_anywhere() {
1882 let text = wrap(
1883 "(i64)",
1884 "block0(%0: i64):
1885 jump block1
1886
1887block1:
1888 %1 = alloca %0, align 8
1889 return
1890",
1891 );
1892 assert_eq!(errors(&text), Vec::<String>::new());
1893 }
1894
1895 #[test]
1896 fn two_cases_of_a_switch_with_the_same_value_are_reported() {
1897 let text = wrap(
1898 "(i32)",
1899 "block0(%0: i32):
1900 switch %0, block1, [7 => block1, 7 => block1]
1901
1902block1:
1903 return
1904",
1905 );
1906 assert_eq!(only(&text), "@f block0 switch: two cases of this switch have the same value");
1907 }
1908
1909 #[test]
1910 fn operands_that_do_not_agree_are_reported() {
1911 let text = wrap(
1912 "(i32, i64) -> i32",
1913 "block0(%0: i32, %1: i64):\n %2 = add %0, %1\n return %2\n",
1914 );
1915 assert_eq!(
1916 only(&text),
1917 "@f block0 add: the operands of add have one type and these are i32 and i64"
1918 );
1919 }
1920
1921 #[test]
1922 fn a_conversion_that_goes_the_wrong_way_is_reported() {
1923 let text = wrap("(i32) -> i64", "block0(%0: i32):\n %1 = trunc.i64 %0\n return %1\n");
1924 assert_eq!(
1925 only(&text),
1926 "@f block0 trunc: trunc produces something narrower and i32 to i64 is not"
1927 );
1928 }
1929
1930 #[test]
1931 fn a_bitcast_between_an_address_and_a_number_is_reported() {
1932 let text =
1933 wrap("(ptr) -> i64", "block0(%0: ptr):\n %1 = bitcast.i64 %0\n return %1\n");
1934 assert_eq!(
1935 only(&text),
1936 "@f block0 bitcast: a bitcast between a pointer and a number is ptrtoint or inttoptr"
1937 );
1938 }
1939
1940 #[test]
1941 fn an_operand_of_the_wrong_kind_is_reported() {
1942 let text = wrap("(i32) -> i32", "block0(%0: i32):\n %1 = fadd %0, %0\n return %1\n");
1943 assert_eq!(
1944 only(&text),
1945 "@f block0 fadd: operand 1 of fadd is a floating point value and this one is i32"
1946 );
1947 }
1948
1949 #[test]
1950 fn a_condition_that_is_not_one_bit_is_reported() {
1951 let text = wrap(
1952 "(i32)",
1953 "block0(%0: i32):
1954 br_if %0, block1, block1
1955
1956block1:
1957 return
1958",
1959 );
1960 assert_eq!(only(&text), "@f block0 br_if: br_if branches on an i1 and this one on i32");
1961 }
1962
1963 #[test]
1964 fn a_return_that_does_not_match_the_signature_is_reported() {
1965 let text = wrap("() -> i32", "block0:\n return\n");
1966 assert_eq!(only(&text), "@f block0 return: the signature returns 1 and this returns 0");
1967 }
1968
1969 #[test]
1970 fn a_call_that_disagrees_with_the_declaration_is_reported() {
1971 let text = format!(
1972 "{HEADER}
1973func @g(i32, ...) -> i32, linkage(external);
1974
1975func @f(i32) -> i32, linkage(external) {{
1976block0(%0: i32):
1977 %1 = call @g(%0) : (i32) -> i32
1978 return %1
1979}}
1980"
1981 );
1982 assert_eq!(only(&text), "@f block0 call: @g is declared here with another signature");
1983 }
1984
1985 #[test]
1986 fn a_global_whose_image_is_not_its_size_is_reported() {
1987 let text =
1988 format!("{HEADER}\nglobal @x : bytes 8 = {{ i32 7 }}, align 4, linkage(external)\n");
1989 assert_eq!(only(&text), "@x: the image is 4 bytes and the global is 8");
1990 }
1991
1992 #[test]
1993 fn a_pointer_in_an_image_has_no_width_and_is_reported() {
1994 let text = format!(
1998 "{HEADER}\nglobal @x : bytes 8 = {{ ptr 0x0, zero 8 }}, align 8, linkage(external)\n"
1999 );
2000 assert_eq!(only(&text), "@x: a scalar in an image has a width and ptr has none");
2001 }
2002
2003 #[test]
2004 fn a_declaration_of_something_another_module_defines_may_be_constant() {
2005 let text = format!("{HEADER}\nglobal @x : bytes 4, align 4, linkage(external), constant\n");
2008 assert!(errors(&text).is_empty(), "{:?}", errors(&text));
2009 }
2010
2011 #[test]
2012 fn an_alias_to_itself_is_reported() {
2013 let text = format!("{HEADER}\nalias @a = @a, linkage(external)\n");
2014 assert_eq!(only(&text), "@a: an alias to itself");
2015 }
2016
2017 #[test]
2018 fn an_ifunc_that_does_not_resolve_through_a_function_is_reported() {
2019 let text = format!(
2020 "{HEADER}
2021global @g : i32 = 0, align 4, linkage(external)
2022
2023ifunc @f = @g, linkage(external)
2024"
2025 );
2026 assert_eq!(
2027 only(&text),
2028 "@f: an ifunc resolves through a function and this target is not one"
2029 );
2030 }
2031
2032 #[test]
2033 fn attributes_that_contradict_each_other_are_reported() {
2034 let text =
2035 format!("{HEADER}\nfunc @f(), linkage(external), attrs(always_inline, noinline);\n");
2036 assert_eq!(
2037 only(&text),
2038 "@f: `always_inline` and `noinline` cannot both be true of a function"
2039 );
2040 }
2041
2042 fn one_error(module: &Module, func: &Func, names: &Interner) -> String {
2046 match verify_func(module, func, names) {
2047 Ok(()) => panic!("that was expected to be turned down"),
2048 Err(errors) => {
2049 assert_eq!(errors.len(), 1, "{errors:#?}");
2050 errors[0].to_string()
2051 }
2052 }
2053 }
2054
2055 #[test]
2056 fn a_value_the_function_does_not_have_is_reported() {
2057 let mut names = Interner::new();
2058 let module = Module::new(names.intern("built.c"), &target());
2059 let mut func = Func::new(names.intern("f"), Signature::new());
2060 let block = func.create_block();
2061 let args = func.push_values(&[Value::from_usize(9)]);
2062 let inst =
2063 func.create_inst(InstData { args, ..InstData::new(Opcode::Return) }, &[], Span::DUMMY);
2064 func.append_inst(block, inst);
2065 assert_eq!(
2066 one_error(&module, &func, &names),
2067 "@f block0 return: %9 is not a value of this function"
2068 );
2069 }
2070
2071 #[test]
2072 fn a_value_whose_definition_has_been_taken_out_is_reported() {
2073 let mut names = Interner::new();
2074 let module = Module::new(names.intern("built.c"), &target());
2075 let i32_ = Type::int(32);
2076 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
2077 let block = func.create_block();
2078 let mut b = Builder::new(&mut func, block);
2079 let value = b.iconst(i32_, 7);
2080 b.ret(&[value]);
2081 let Def::Result { inst, .. } = func[value].def else { unreachable!("a constant") };
2082 func.remove_inst(inst);
2083 assert_eq!(
2084 one_error(&module, &func, &names),
2085 "@f block0 return: %0 is produced by an instruction that is not in the function"
2086 );
2087 }
2088
2089 #[test]
2090 fn an_instruction_carrying_another_opcodes_payload_is_reported() {
2091 let mut names = Interner::new();
2094 let module = Module::new(names.intern("built.c"), &target());
2095 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
2096 let block = func.create_block();
2097 let param = func.append_param(block, Type::int(32));
2098 let args = func.push_values(&[param, param]);
2099 let inst = func.create_inst(
2100 InstData { args, extra: Extra::IntPred(IntPred::Eq), ..InstData::new(Opcode::Add) },
2101 &[Type::int(32)],
2102 Span::DUMMY,
2103 );
2104 func.append_inst(block, inst);
2105 let ret = func.create_inst(InstData::new(Opcode::Return), &[], Span::DUMMY);
2106 func.append_inst(block, ret);
2107 assert_eq!(
2108 one_error(&module, &func, &names),
2109 "@f block0 add: add carries nothing and this one carries an integer comparison"
2110 );
2111 }
2112
2113 #[test]
2114 fn a_metadata_node_that_is_its_own_parent_is_reported() {
2115 let mut names = Interner::new();
2116 let mut module = Module::new(names.intern("built.c"), &target());
2117 module.add_meta(MetaNode {
2118 name: names.intern("int"),
2119 parent: Some(Idx::from_usize(0)),
2120 offset: 0,
2121 });
2122 let found = match verify(&module, &names) {
2123 Ok(()) => panic!("that was expected to be turned down"),
2124 Err(errors) => errors,
2125 };
2126 assert_eq!(found.len(), 1, "{found:#?}");
2127 assert_eq!(
2128 found[0].to_string(),
2129 "!0: a metadata node's parent comes before it and this one is !0"
2130 );
2131 }
2132
2133 #[test]
2134 fn a_datalayout_the_target_does_not_imply_is_reported() {
2135 let mut names = Interner::new();
2136 let mut module = Module::new(names.intern("built.c"), &target());
2137 module.datalayout = DataLayout::parse("e-p:32:32-i64:64-f80:32-S64").expect("a layout");
2138 let found = match verify(&module, &names) {
2139 Ok(()) => panic!("that was expected to be turned down"),
2140 Err(errors) => errors,
2141 };
2142 assert_eq!(found.len(), 1, "{found:#?}");
2143 assert!(found[0].to_string().starts_with("@built.c: the datalayout is "), "{}", found[0]);
2144 }
2145
2146 #[test]
2147 fn a_flag_riding_along_where_it_is_read_is_not_reported() {
2148 let mut names = Interner::new();
2149 let module = Module::new(names.intern("built.c"), &target());
2150 let i32_ = Type::int(32);
2151 let mut func = Func::new(
2152 names.intern("f"),
2153 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
2154 );
2155 let block = func.create_block();
2156 let param = func.append_param(block, i32_);
2157 let mut b = Builder::new(&mut func, block);
2158 let sum = b.binary(Opcode::Add, param, param, Flags::NSW);
2159 b.ret(&[sum]);
2160 assert!(verify_func(&module, &func, &names).is_ok());
2161 }
2162
2163 #[test]
2164 fn a_declaration_is_checked_and_has_nothing_else_to_check() {
2165 let mut names = Interner::new();
2166 let module = Module::new(names.intern("built.c"), &target());
2167 let func = Func::new(Symbol::from_raw(0), Signature::new());
2168 assert!(func.is_declaration());
2169 assert!(verify_func(&module, &func, &names).is_ok());
2170 }
2171}