1use crate::dbg::DisplayList;
67use crate::dominator_tree::DominatorTree;
68use crate::entity::SparseSet;
69use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
70use crate::ir::entities::AnyEntity;
71use crate::ir::instructions::{CallInfo, InstructionFormat, ResolvedConstraint};
72use crate::ir::{self, ArgumentExtension};
73use crate::ir::{
74 types, ArgumentPurpose, Block, Constant, DynamicStackSlot, FuncRef, Function, GlobalValue,
75 Inst, JumpTable, MemFlags, MemoryTypeData, Opcode, SigRef, StackSlot, Type, Value, ValueDef,
76 ValueList,
77};
78use crate::isa::TargetIsa;
79use crate::print_errors::pretty_verifier_error;
80use crate::settings::FlagsOrIsa;
81use crate::timing;
82use alloc::collections::BTreeSet;
83use alloc::string::{String, ToString};
84use alloc::vec::Vec;
85use core::fmt::{self, Display, Formatter};
86
87#[derive(Debug, PartialEq, Eq, Clone)]
89pub struct VerifierError {
90 pub location: AnyEntity,
92 pub context: Option<String>,
95 pub message: String,
97}
98
99impl std::error::Error for VerifierError {}
102
103impl Display for VerifierError {
104 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
105 match &self.context {
106 None => write!(f, "{}: {}", self.location, self.message),
107 Some(context) => write!(f, "{} ({}): {}", self.location, context, self.message),
108 }
109 }
110}
111
112impl<L, C, M> From<(L, C, M)> for VerifierError
123where
124 L: Into<AnyEntity>,
125 C: Into<String>,
126 M: Into<String>,
127{
128 fn from(items: (L, C, M)) -> Self {
129 let (location, context, message) = items;
130 Self {
131 location: location.into(),
132 context: Some(context.into()),
133 message: message.into(),
134 }
135 }
136}
137
138impl<L, M> From<(L, M)> for VerifierError
142where
143 L: Into<AnyEntity>,
144 M: Into<String>,
145{
146 fn from(items: (L, M)) -> Self {
147 let (location, message) = items;
148 Self {
149 location: location.into(),
150 context: None,
151 message: message.into(),
152 }
153 }
154}
155
156pub type VerifierStepResult = Result<(), ()>;
167
168pub type VerifierResult<T> = Result<T, VerifierErrors>;
173
174#[derive(Debug, Default, PartialEq, Eq, Clone)]
176pub struct VerifierErrors(pub Vec<VerifierError>);
177
178impl std::error::Error for VerifierErrors {}
181
182impl VerifierErrors {
183 #[inline]
185 pub fn new() -> Self {
186 Self(Vec::new())
187 }
188
189 #[inline]
191 pub fn is_empty(&self) -> bool {
192 self.0.is_empty()
193 }
194
195 #[inline]
197 pub fn has_error(&self) -> bool {
198 !self.0.is_empty()
199 }
200
201 #[inline]
204 pub fn as_result(&self) -> VerifierStepResult {
205 if self.is_empty() {
206 Ok(())
207 } else {
208 Err(())
209 }
210 }
211
212 pub fn report(&mut self, error: impl Into<VerifierError>) {
214 self.0.push(error.into());
215 }
216
217 pub fn fatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {
219 self.report(error);
220 Err(())
221 }
222
223 pub fn nonfatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {
225 self.report(error);
226 Ok(())
227 }
228}
229
230impl From<Vec<VerifierError>> for VerifierErrors {
231 fn from(v: Vec<VerifierError>) -> Self {
232 Self(v)
233 }
234}
235
236impl Into<Vec<VerifierError>> for VerifierErrors {
237 fn into(self) -> Vec<VerifierError> {
238 self.0
239 }
240}
241
242impl Into<VerifierResult<()>> for VerifierErrors {
243 fn into(self) -> VerifierResult<()> {
244 if self.is_empty() {
245 Ok(())
246 } else {
247 Err(self)
248 }
249 }
250}
251
252impl Display for VerifierErrors {
253 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
254 for err in &self.0 {
255 writeln!(f, "- {err}")?;
256 }
257 Ok(())
258 }
259}
260
261pub fn verify_function<'a, FOI: Into<FlagsOrIsa<'a>>>(
263 func: &Function,
264 fisa: FOI,
265) -> VerifierResult<()> {
266 let _tt = timing::verifier();
267 let mut errors = VerifierErrors::default();
268 let verifier = Verifier::new(func, fisa.into());
269 let result = verifier.run(&mut errors);
270 if errors.is_empty() {
271 result.unwrap();
272 Ok(())
273 } else {
274 Err(errors)
275 }
276}
277
278pub fn verify_context<'a, FOI: Into<FlagsOrIsa<'a>>>(
281 func: &Function,
282 cfg: &ControlFlowGraph,
283 domtree: &DominatorTree,
284 fisa: FOI,
285 errors: &mut VerifierErrors,
286) -> VerifierStepResult {
287 let _tt = timing::verifier();
288 let verifier = Verifier::new(func, fisa.into());
289 if cfg.is_valid() {
290 verifier.cfg_integrity(cfg, errors)?;
291 }
292 if domtree.is_valid() {
293 verifier.domtree_integrity(domtree, errors)?;
294 }
295 verifier.run(errors)
296}
297
298struct Verifier<'a> {
299 func: &'a Function,
300 expected_cfg: ControlFlowGraph,
301 expected_domtree: DominatorTree,
302 isa: Option<&'a dyn TargetIsa>,
303}
304
305impl<'a> Verifier<'a> {
306 pub fn new(func: &'a Function, fisa: FlagsOrIsa<'a>) -> Self {
307 let expected_cfg = ControlFlowGraph::with_function(func);
308 let expected_domtree = DominatorTree::with_function(func, &expected_cfg);
309 Self {
310 func,
311 expected_cfg,
312 expected_domtree,
313 isa: fisa.isa,
314 }
315 }
316
317 #[inline]
319 fn context(&self, inst: Inst) -> String {
320 self.func.dfg.display_inst(inst).to_string()
321 }
322
323 fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
327 let mut cycle_seen = false;
328 let mut seen = SparseSet::new();
329
330 'gvs: for gv in self.func.global_values.keys() {
331 seen.clear();
332 seen.insert(gv);
333
334 let mut cur = gv;
335 loop {
336 match self.func.global_values[cur] {
337 ir::GlobalValueData::Load { base, .. }
338 | ir::GlobalValueData::IAddImm { base, .. } => {
339 if seen.insert(base).is_some() {
340 if !cycle_seen {
341 errors.report((
342 gv,
343 format!("global value cycle: {}", DisplayList(seen.as_slice())),
344 ));
345 cycle_seen = true;
347 }
348 continue 'gvs;
349 }
350
351 cur = base;
352 }
353 _ => break,
354 }
355 }
356
357 match self.func.global_values[gv] {
358 ir::GlobalValueData::VMContext { .. } => {
359 if self
360 .func
361 .special_param(ir::ArgumentPurpose::VMContext)
362 .is_none()
363 {
364 errors.report((gv, format!("undeclared vmctx reference {gv}")));
365 }
366 }
367 ir::GlobalValueData::IAddImm {
368 base, global_type, ..
369 } => {
370 if !global_type.is_int() {
371 errors.report((
372 gv,
373 format!("iadd_imm global value with non-int type {global_type}"),
374 ));
375 } else if let Some(isa) = self.isa {
376 let base_type = self.func.global_values[base].global_type(isa);
377 if global_type != base_type {
378 errors.report((
379 gv,
380 format!(
381 "iadd_imm type {global_type} differs from operand type {base_type}"
382 ),
383 ));
384 }
385 }
386 }
387 ir::GlobalValueData::Load { base, .. } => {
388 if let Some(isa) = self.isa {
389 let base_type = self.func.global_values[base].global_type(isa);
390 let pointer_type = isa.pointer_type();
391 if base_type != pointer_type {
392 errors.report((
393 gv,
394 format!(
395 "base {base} has type {base_type}, which is not the pointer type {pointer_type}"
396 ),
397 ));
398 }
399 }
400 }
401 _ => {}
402 }
403 }
404
405 Ok(())
407 }
408
409 fn verify_memory_types(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
410 for (mt, mt_data) in &self.func.memory_types {
413 match mt_data {
414 MemoryTypeData::Struct { size, fields } => {
415 let mut last_offset = 0;
416 for field in fields {
417 if field.offset < last_offset {
418 errors.report((
419 mt,
420 format!(
421 "memory type {} has a field at offset {}, which is out-of-order",
422 mt, field.offset
423 ),
424 ));
425 }
426 last_offset = match field.offset.checked_add(u64::from(field.ty.bytes())) {
427 Some(o) => o,
428 None => {
429 errors.report((
430 mt,
431 format!(
432 "memory type {} has a field at offset {} of size {}; offset plus size overflows a u64",
433 mt, field.offset, field.ty.bytes()),
434 ));
435 break;
436 }
437 };
438
439 if last_offset > *size {
440 errors.report((
441 mt,
442 format!(
443 "memory type {} has a field at offset {} of size {} that overflows the struct size {}",
444 mt, field.offset, field.ty.bytes(), *size),
445 ));
446 }
447 }
448 }
449 _ => {}
450 }
451 }
452
453 Ok(())
454 }
455
456 fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult {
459 match self.func.is_block_basic(block) {
460 Ok(()) => Ok(()),
461 Err((inst, message)) => errors.fatal((inst, self.context(inst), message)),
462 }
463 }
464
465 fn block_integrity(
466 &self,
467 block: Block,
468 inst: Inst,
469 errors: &mut VerifierErrors,
470 ) -> VerifierStepResult {
471 let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator();
472 let is_last_inst = self.func.layout.last_inst(block) == Some(inst);
473
474 if is_terminator && !is_last_inst {
475 return errors.fatal((
477 inst,
478 self.context(inst),
479 format!("a terminator instruction was encountered before the end of {block}"),
480 ));
481 }
482 if is_last_inst && !is_terminator {
483 return errors.fatal((block, "block does not end in a terminator instruction"));
484 }
485
486 let inst_block = self.func.layout.inst_block(inst);
488 if inst_block != Some(block) {
489 return errors.fatal((
490 inst,
491 self.context(inst),
492 format!("should belong to {block} not {inst_block:?}"),
493 ));
494 }
495
496 for &arg in self.func.dfg.block_params(block) {
498 match self.func.dfg.value_def(arg) {
499 ValueDef::Param(arg_block, _) => {
500 if block != arg_block {
501 return errors.fatal((arg, format!("does not belong to {block}")));
502 }
503 }
504 _ => {
505 return errors.fatal((arg, "expected an argument, found a result"));
506 }
507 }
508 }
509
510 Ok(())
511 }
512
513 fn instruction_integrity(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
514 let inst_data = &self.func.dfg.insts[inst];
515 let dfg = &self.func.dfg;
516
517 if inst_data.opcode().format() != InstructionFormat::from(inst_data) {
519 return errors.fatal((
520 inst,
521 self.context(inst),
522 "instruction opcode doesn't match instruction format",
523 ));
524 }
525
526 let expected_num_results = dfg.num_expected_results_for_verifier(inst);
527
528 let got_results = dfg.inst_results(inst).len();
530 if got_results != expected_num_results {
531 return errors.fatal((
532 inst,
533 self.context(inst),
534 format!("expected {expected_num_results} result values, found {got_results}"),
535 ));
536 }
537
538 self.verify_entity_references(inst, errors)
539 }
540
541 fn verify_entity_references(
542 &self,
543 inst: Inst,
544 errors: &mut VerifierErrors,
545 ) -> VerifierStepResult {
546 use crate::ir::instructions::InstructionData::*;
547
548 for arg in self.func.dfg.inst_values(inst) {
549 self.verify_inst_arg(inst, arg, errors)?;
550
551 let original = self.func.dfg.resolve_aliases(arg);
553 if !self.func.dfg.value_is_attached(original) {
554 errors.report((
555 inst,
556 self.context(inst),
557 format!("argument {arg} -> {original} is not attached"),
558 ));
559 }
560 }
561
562 for &res in self.func.dfg.inst_results(inst) {
563 self.verify_inst_result(inst, res, errors)?;
564 }
565
566 match self.func.dfg.insts[inst] {
567 MultiAry { ref args, .. } => {
568 self.verify_value_list(inst, args, errors)?;
569 }
570 Jump { destination, .. } => {
571 self.verify_block(inst, destination.block(&self.func.dfg.value_lists), errors)?;
572 }
573 Brif {
574 arg,
575 blocks: [block_then, block_else],
576 ..
577 } => {
578 self.verify_value(inst, arg, errors)?;
579 self.verify_block(inst, block_then.block(&self.func.dfg.value_lists), errors)?;
580 self.verify_block(inst, block_else.block(&self.func.dfg.value_lists), errors)?;
581 }
582 BranchTable { table, .. } => {
583 self.verify_jump_table(inst, table, errors)?;
584 }
585 Call {
586 func_ref, ref args, ..
587 } => {
588 self.verify_func_ref(inst, func_ref, errors)?;
589 self.verify_value_list(inst, args, errors)?;
590 }
591 CallIndirect {
592 sig_ref, ref args, ..
593 } => {
594 self.verify_sig_ref(inst, sig_ref, errors)?;
595 self.verify_value_list(inst, args, errors)?;
596 }
597 FuncAddr { func_ref, .. } => {
598 self.verify_func_ref(inst, func_ref, errors)?;
599 }
600 StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => {
601 self.verify_stack_slot(inst, stack_slot, errors)?;
602 }
603 DynamicStackLoad {
604 dynamic_stack_slot, ..
605 }
606 | DynamicStackStore {
607 dynamic_stack_slot, ..
608 } => {
609 self.verify_dynamic_stack_slot(inst, dynamic_stack_slot, errors)?;
610 }
611 UnaryGlobalValue { global_value, .. } => {
612 self.verify_global_value(inst, global_value, errors)?;
613 }
614 NullAry {
615 opcode: Opcode::GetPinnedReg,
616 }
617 | Unary {
618 opcode: Opcode::SetPinnedReg,
619 ..
620 } => {
621 if let Some(isa) = &self.isa {
622 if !isa.flags().enable_pinned_reg() {
623 return errors.fatal((
624 inst,
625 self.context(inst),
626 "GetPinnedReg/SetPinnedReg cannot be used without enable_pinned_reg",
627 ));
628 }
629 } else {
630 return errors.fatal((
631 inst,
632 self.context(inst),
633 "GetPinnedReg/SetPinnedReg need an ISA!",
634 ));
635 }
636 }
637 NullAry {
638 opcode: Opcode::GetFramePointer | Opcode::GetReturnAddress,
639 } => {
640 if let Some(isa) = &self.isa {
641 if !isa.flags().preserve_frame_pointers() {
644 return errors.fatal((
645 inst,
646 self.context(inst),
647 "`get_frame_pointer`/`get_return_address` cannot be used without \
648 enabling `preserve_frame_pointers`",
649 ));
650 }
651 } else {
652 return errors.fatal((
653 inst,
654 self.context(inst),
655 "`get_frame_pointer`/`get_return_address` require an ISA!",
656 ));
657 }
658 }
659 LoadNoOffset {
660 opcode: Opcode::Bitcast,
661 flags,
662 arg,
663 } => {
664 self.verify_bitcast(inst, flags, arg, errors)?;
665 }
666 LoadNoOffset { opcode, arg, .. } if opcode.can_load() => {
667 self.verify_is_address(inst, arg, errors)?;
668 }
669 Load { opcode, arg, .. } if opcode.can_load() => {
670 self.verify_is_address(inst, arg, errors)?;
671 }
672 AtomicCas {
673 opcode,
674 args: [p, _, _],
675 ..
676 } if opcode.can_load() || opcode.can_store() => {
677 self.verify_is_address(inst, p, errors)?;
678 }
679 AtomicRmw {
680 opcode,
681 args: [p, _],
682 ..
683 } if opcode.can_load() || opcode.can_store() => {
684 self.verify_is_address(inst, p, errors)?;
685 }
686 Store {
687 opcode,
688 args: [_, p],
689 ..
690 } if opcode.can_store() => {
691 self.verify_is_address(inst, p, errors)?;
692 }
693 StoreNoOffset {
694 opcode,
695 args: [_, p],
696 ..
697 } if opcode.can_store() => {
698 self.verify_is_address(inst, p, errors)?;
699 }
700 UnaryConst {
701 opcode: opcode @ (Opcode::Vconst | Opcode::F128const),
702 constant_handle,
703 ..
704 } => {
705 self.verify_constant_size(inst, opcode, constant_handle, errors)?;
706 }
707
708 AtomicCas { .. }
710 | AtomicRmw { .. }
711 | LoadNoOffset { .. }
712 | StoreNoOffset { .. }
713 | Unary { .. }
714 | UnaryConst { .. }
715 | UnaryImm { .. }
716 | UnaryIeee16 { .. }
717 | UnaryIeee32 { .. }
718 | UnaryIeee64 { .. }
719 | Binary { .. }
720 | BinaryImm8 { .. }
721 | BinaryImm64 { .. }
722 | Ternary { .. }
723 | TernaryImm8 { .. }
724 | Shuffle { .. }
725 | IntAddTrap { .. }
726 | IntCompare { .. }
727 | IntCompareImm { .. }
728 | FloatCompare { .. }
729 | Load { .. }
730 | Store { .. }
731 | Trap { .. }
732 | CondTrap { .. }
733 | NullAry { .. } => {}
734 }
735
736 Ok(())
737 }
738
739 fn verify_block(
740 &self,
741 loc: impl Into<AnyEntity>,
742 e: Block,
743 errors: &mut VerifierErrors,
744 ) -> VerifierStepResult {
745 if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) {
746 return errors.fatal((loc, format!("invalid block reference {e}")));
747 }
748 if let Some(entry_block) = self.func.layout.entry_block() {
749 if e == entry_block {
750 return errors.fatal((loc, format!("invalid reference to entry block {e}")));
751 }
752 }
753 Ok(())
754 }
755
756 fn verify_sig_ref(
757 &self,
758 inst: Inst,
759 s: SigRef,
760 errors: &mut VerifierErrors,
761 ) -> VerifierStepResult {
762 if !self.func.dfg.signatures.is_valid(s) {
763 errors.fatal((
764 inst,
765 self.context(inst),
766 format!("invalid signature reference {s}"),
767 ))
768 } else {
769 Ok(())
770 }
771 }
772
773 fn verify_func_ref(
774 &self,
775 inst: Inst,
776 f: FuncRef,
777 errors: &mut VerifierErrors,
778 ) -> VerifierStepResult {
779 if !self.func.dfg.ext_funcs.is_valid(f) {
780 errors.nonfatal((
781 inst,
782 self.context(inst),
783 format!("invalid function reference {f}"),
784 ))
785 } else {
786 Ok(())
787 }
788 }
789
790 fn verify_stack_slot(
791 &self,
792 inst: Inst,
793 ss: StackSlot,
794 errors: &mut VerifierErrors,
795 ) -> VerifierStepResult {
796 if !self.func.sized_stack_slots.is_valid(ss) {
797 errors.nonfatal((inst, self.context(inst), format!("invalid stack slot {ss}")))
798 } else {
799 Ok(())
800 }
801 }
802
803 fn verify_dynamic_stack_slot(
804 &self,
805 inst: Inst,
806 ss: DynamicStackSlot,
807 errors: &mut VerifierErrors,
808 ) -> VerifierStepResult {
809 if !self.func.dynamic_stack_slots.is_valid(ss) {
810 errors.nonfatal((
811 inst,
812 self.context(inst),
813 format!("invalid dynamic stack slot {ss}"),
814 ))
815 } else {
816 Ok(())
817 }
818 }
819
820 fn verify_global_value(
821 &self,
822 inst: Inst,
823 gv: GlobalValue,
824 errors: &mut VerifierErrors,
825 ) -> VerifierStepResult {
826 if !self.func.global_values.is_valid(gv) {
827 errors.nonfatal((
828 inst,
829 self.context(inst),
830 format!("invalid global value {gv}"),
831 ))
832 } else {
833 Ok(())
834 }
835 }
836
837 fn verify_value_list(
838 &self,
839 inst: Inst,
840 l: &ValueList,
841 errors: &mut VerifierErrors,
842 ) -> VerifierStepResult {
843 if !l.is_valid(&self.func.dfg.value_lists) {
844 errors.nonfatal((
845 inst,
846 self.context(inst),
847 format!("invalid value list reference {l:?}"),
848 ))
849 } else {
850 Ok(())
851 }
852 }
853
854 fn verify_jump_table(
855 &self,
856 inst: Inst,
857 j: JumpTable,
858 errors: &mut VerifierErrors,
859 ) -> VerifierStepResult {
860 if !self.func.stencil.dfg.jump_tables.is_valid(j) {
861 errors.nonfatal((
862 inst,
863 self.context(inst),
864 format!("invalid jump table reference {j}"),
865 ))
866 } else {
867 let pool = &self.func.stencil.dfg.value_lists;
868 for block in self.func.stencil.dfg.jump_tables[j].all_branches() {
869 self.verify_block(inst, block.block(pool), errors)?;
870 }
871 Ok(())
872 }
873 }
874
875 fn verify_value(
876 &self,
877 loc_inst: Inst,
878 v: Value,
879 errors: &mut VerifierErrors,
880 ) -> VerifierStepResult {
881 let dfg = &self.func.dfg;
882 if !dfg.value_is_valid(v) {
883 errors.nonfatal((
884 loc_inst,
885 self.context(loc_inst),
886 format!("invalid value reference {v}"),
887 ))
888 } else {
889 Ok(())
890 }
891 }
892
893 fn verify_inst_arg(
894 &self,
895 loc_inst: Inst,
896 v: Value,
897 errors: &mut VerifierErrors,
898 ) -> VerifierStepResult {
899 self.verify_value(loc_inst, v, errors)?;
900
901 let dfg = &self.func.dfg;
902 let loc_block = self
903 .func
904 .layout
905 .inst_block(loc_inst)
906 .expect("Instruction not in layout.");
907 let is_reachable = self.expected_domtree.is_reachable(loc_block);
908
909 match dfg.value_def(v) {
911 ValueDef::Result(def_inst, _) => {
912 if !dfg.inst_is_valid(def_inst) {
914 return errors.fatal((
915 loc_inst,
916 self.context(loc_inst),
917 format!("{v} is defined by invalid instruction {def_inst}"),
918 ));
919 }
920 if self.func.layout.inst_block(def_inst) == None {
922 return errors.fatal((
923 loc_inst,
924 self.context(loc_inst),
925 format!("{v} is defined by {def_inst} which has no block"),
926 ));
927 }
928 if is_reachable {
930 if !self
931 .expected_domtree
932 .dominates(def_inst, loc_inst, &self.func.layout)
933 {
934 return errors.fatal((
935 loc_inst,
936 self.context(loc_inst),
937 format!("uses value {v} from non-dominating {def_inst}"),
938 ));
939 }
940 if def_inst == loc_inst {
941 return errors.fatal((
942 loc_inst,
943 self.context(loc_inst),
944 format!("uses value {v} from itself"),
945 ));
946 }
947 }
948 }
949 ValueDef::Param(block, _) => {
950 if !dfg.block_is_valid(block) {
952 return errors.fatal((
953 loc_inst,
954 self.context(loc_inst),
955 format!("{v} is defined by invalid block {block}"),
956 ));
957 }
958 if !self.func.layout.is_block_inserted(block) {
960 return errors.fatal((
961 loc_inst,
962 self.context(loc_inst),
963 format!("{v} is defined by {block} which is not in the layout"),
964 ));
965 }
966 if is_reachable
968 && !self
969 .expected_domtree
970 .dominates(block, loc_inst, &self.func.layout)
971 {
972 return errors.fatal((
973 loc_inst,
974 self.context(loc_inst),
975 format!("uses value arg from non-dominating {block}"),
976 ));
977 }
978 }
979 ValueDef::Union(_, _) => {
980 }
983 }
984 Ok(())
985 }
986
987 fn verify_inst_result(
988 &self,
989 loc_inst: Inst,
990 v: Value,
991 errors: &mut VerifierErrors,
992 ) -> VerifierStepResult {
993 self.verify_value(loc_inst, v, errors)?;
994
995 match self.func.dfg.value_def(v) {
996 ValueDef::Result(def_inst, _) => {
997 if def_inst != loc_inst {
998 errors.fatal((
999 loc_inst,
1000 self.context(loc_inst),
1001 format!("instruction result {v} is not defined by the instruction"),
1002 ))
1003 } else {
1004 Ok(())
1005 }
1006 }
1007 ValueDef::Param(_, _) => errors.fatal((
1008 loc_inst,
1009 self.context(loc_inst),
1010 format!("instruction result {v} is not defined by the instruction"),
1011 )),
1012 ValueDef::Union(_, _) => errors.fatal((
1013 loc_inst,
1014 self.context(loc_inst),
1015 format!("instruction result {v} is a union node"),
1016 )),
1017 }
1018 }
1019
1020 fn verify_bitcast(
1021 &self,
1022 inst: Inst,
1023 flags: MemFlags,
1024 arg: Value,
1025 errors: &mut VerifierErrors,
1026 ) -> VerifierStepResult {
1027 let typ = self.func.dfg.ctrl_typevar(inst);
1028 let value_type = self.func.dfg.value_type(arg);
1029
1030 if typ.bits() != value_type.bits() {
1031 errors.fatal((
1032 inst,
1033 format!(
1034 "The bitcast argument {} has a type of {} bits, which doesn't match an expected type of {} bits",
1035 arg,
1036 value_type.bits(),
1037 typ.bits()
1038 ),
1039 ))
1040 } else if flags != MemFlags::new()
1041 && flags != MemFlags::new().with_endianness(ir::Endianness::Little)
1042 && flags != MemFlags::new().with_endianness(ir::Endianness::Big)
1043 {
1044 errors.fatal((
1045 inst,
1046 "The bitcast instruction only accepts the `big` or `little` memory flags",
1047 ))
1048 } else if flags == MemFlags::new() && typ.lane_count() != value_type.lane_count() {
1049 errors.fatal((
1050 inst,
1051 "Byte order specifier required for bitcast instruction changing lane count",
1052 ))
1053 } else {
1054 Ok(())
1055 }
1056 }
1057
1058 fn verify_constant_size(
1059 &self,
1060 inst: Inst,
1061 opcode: Opcode,
1062 constant: Constant,
1063 errors: &mut VerifierErrors,
1064 ) -> VerifierStepResult {
1065 let type_size = match opcode {
1066 Opcode::F128const => types::F128.bytes(),
1067 Opcode::Vconst => self.func.dfg.ctrl_typevar(inst).bytes(),
1068 _ => unreachable!("unexpected opcode {opcode:?}"),
1069 } as usize;
1070 let constant_size = self.func.dfg.constants.get(constant).len();
1071 if type_size != constant_size {
1072 errors.fatal((
1073 inst,
1074 format!(
1075 "The instruction expects {constant} to have a size of {type_size} bytes but it has {constant_size}"
1076 ),
1077 ))
1078 } else {
1079 Ok(())
1080 }
1081 }
1082
1083 fn verify_is_address(
1084 &self,
1085 loc_inst: Inst,
1086 v: Value,
1087 errors: &mut VerifierErrors,
1088 ) -> VerifierStepResult {
1089 if let Some(isa) = self.isa {
1090 let pointer_width = isa.triple().pointer_width()?;
1091 let value_type = self.func.dfg.value_type(v);
1092 let expected_width = pointer_width.bits() as u32;
1093 let value_width = value_type.bits();
1094 if expected_width != value_width {
1095 errors.nonfatal((
1096 loc_inst,
1097 self.context(loc_inst),
1098 format!("invalid pointer width (got {value_width}, expected {expected_width}) encountered {v}"),
1099 ))
1100 } else {
1101 Ok(())
1102 }
1103 } else {
1104 Ok(())
1105 }
1106 }
1107
1108 fn domtree_integrity(
1109 &self,
1110 domtree: &DominatorTree,
1111 errors: &mut VerifierErrors,
1112 ) -> VerifierStepResult {
1113 for block in self.func.layout.blocks() {
1117 let expected = self.expected_domtree.idom(block);
1118 let got = domtree.idom(block);
1119 if got != expected {
1120 return errors.fatal((
1121 block,
1122 format!("invalid domtree, expected idom({block}) = {expected:?}, got {got:?}"),
1123 ));
1124 }
1125 }
1126 if domtree.cfg_postorder().len() != self.expected_domtree.cfg_postorder().len() {
1128 return errors.fatal((
1129 AnyEntity::Function,
1130 "incorrect number of Blocks in postorder traversal",
1131 ));
1132 }
1133 for (index, (&test_block, &true_block)) in domtree
1134 .cfg_postorder()
1135 .iter()
1136 .zip(self.expected_domtree.cfg_postorder().iter())
1137 .enumerate()
1138 {
1139 if test_block != true_block {
1140 return errors.fatal((
1141 test_block,
1142 format!(
1143 "invalid domtree, postorder block number {index} should be {true_block}, got {test_block}"
1144 ),
1145 ));
1146 }
1147 }
1148 Ok(())
1149 }
1150
1151 fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1152 if let Some(block) = self.func.layout.entry_block() {
1153 let expected_types = &self.func.signature.params;
1154 let block_param_count = self.func.dfg.num_block_params(block);
1155
1156 if block_param_count != expected_types.len() {
1157 return errors.fatal((
1158 block,
1159 format!(
1160 "entry block parameters ({}) must match function signature ({})",
1161 block_param_count,
1162 expected_types.len()
1163 ),
1164 ));
1165 }
1166
1167 for (i, &arg) in self.func.dfg.block_params(block).iter().enumerate() {
1168 let arg_type = self.func.dfg.value_type(arg);
1169 if arg_type != expected_types[i].value_type {
1170 errors.report((
1171 block,
1172 format!(
1173 "entry block parameter {} expected to have type {}, got {}",
1174 i, expected_types[i], arg_type
1175 ),
1176 ));
1177 }
1178 }
1179 }
1180
1181 errors.as_result()
1182 }
1183
1184 fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1185 if let Some(entry_block) = self.func.layout.entry_block() {
1186 if self.func.layout.is_cold(entry_block) {
1187 return errors
1188 .fatal((entry_block, format!("entry block cannot be marked as cold")));
1189 }
1190 }
1191 errors.as_result()
1192 }
1193
1194 fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1195 let inst_data = &self.func.dfg.insts[inst];
1196 let constraints = inst_data.opcode().constraints();
1197
1198 let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
1199 let ctrl_type = self.func.dfg.ctrl_typevar(inst);
1201
1202 if !value_typeset.contains(ctrl_type) {
1203 errors.report((
1204 inst,
1205 self.context(inst),
1206 format!(
1207 "has an invalid controlling type {ctrl_type} (allowed set is {value_typeset:?})"
1208 ),
1209 ));
1210 }
1211
1212 ctrl_type
1213 } else {
1214 types::INVALID
1217 };
1218
1219 let _ = self.typecheck_results(inst, ctrl_type, errors);
1221 let _ = self.typecheck_fixed_args(inst, ctrl_type, errors);
1222 let _ = self.typecheck_variable_args(inst, errors);
1223 let _ = self.typecheck_return(inst, errors);
1224 let _ = self.typecheck_special(inst, errors);
1225
1226 Ok(())
1227 }
1228
1229 fn typecheck_results(
1230 &self,
1231 inst: Inst,
1232 ctrl_type: Type,
1233 errors: &mut VerifierErrors,
1234 ) -> VerifierStepResult {
1235 let mut i = 0;
1236 for &result in self.func.dfg.inst_results(inst) {
1237 let result_type = self.func.dfg.value_type(result);
1238 let expected_type = self.func.dfg.compute_result_type(inst, i, ctrl_type);
1239 if let Some(expected_type) = expected_type {
1240 if result_type != expected_type {
1241 errors.report((
1242 inst,
1243 self.context(inst),
1244 format!(
1245 "expected result {i} ({result}) to have type {expected_type}, found {result_type}"
1246 ),
1247 ));
1248 }
1249 } else {
1250 return errors.nonfatal((
1251 inst,
1252 self.context(inst),
1253 "has more result values than expected",
1254 ));
1255 }
1256 i += 1;
1257 }
1258
1259 if self.func.dfg.compute_result_type(inst, i, ctrl_type) != None {
1261 return errors.nonfatal((
1262 inst,
1263 self.context(inst),
1264 "has fewer result values than expected",
1265 ));
1266 }
1267 Ok(())
1268 }
1269
1270 fn typecheck_fixed_args(
1271 &self,
1272 inst: Inst,
1273 ctrl_type: Type,
1274 errors: &mut VerifierErrors,
1275 ) -> VerifierStepResult {
1276 let constraints = self.func.dfg.insts[inst].opcode().constraints();
1277
1278 for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() {
1279 let arg_type = self.func.dfg.value_type(arg);
1280 match constraints.value_argument_constraint(i, ctrl_type) {
1281 ResolvedConstraint::Bound(expected_type) => {
1282 if arg_type != expected_type {
1283 errors.report((
1284 inst,
1285 self.context(inst),
1286 format!(
1287 "arg {i} ({arg}) has type {arg_type}, expected {expected_type}"
1288 ),
1289 ));
1290 }
1291 }
1292 ResolvedConstraint::Free(type_set) => {
1293 if !type_set.contains(arg_type) {
1294 errors.report((
1295 inst,
1296 self.context(inst),
1297 format!(
1298 "arg {i} ({arg}) with type {arg_type} failed to satisfy type set {type_set:?}"
1299 ),
1300 ));
1301 }
1302 }
1303 }
1304 }
1305 Ok(())
1306 }
1307
1308 fn typecheck_variable_args(
1311 &self,
1312 inst: Inst,
1313 errors: &mut VerifierErrors,
1314 ) -> VerifierStepResult {
1315 match &self.func.dfg.insts[inst] {
1316 ir::InstructionData::Jump { destination, .. } => {
1317 self.typecheck_block_call(inst, destination, errors)?;
1318 }
1319 ir::InstructionData::Brif {
1320 blocks: [block_then, block_else],
1321 ..
1322 } => {
1323 self.typecheck_block_call(inst, block_then, errors)?;
1324 self.typecheck_block_call(inst, block_else, errors)?;
1325 }
1326 ir::InstructionData::BranchTable { table, .. } => {
1327 for block in self.func.stencil.dfg.jump_tables[*table].all_branches() {
1328 self.typecheck_block_call(inst, block, errors)?;
1329 }
1330 }
1331 inst => debug_assert!(!inst.opcode().is_branch()),
1332 }
1333
1334 match self.func.dfg.insts[inst].analyze_call(&self.func.dfg.value_lists) {
1335 CallInfo::Direct(func_ref, args) => {
1336 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1337 let arg_types = self.func.dfg.signatures[sig_ref]
1338 .params
1339 .iter()
1340 .map(|a| a.value_type);
1341 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1342 }
1343 CallInfo::Indirect(sig_ref, args) => {
1344 let arg_types = self.func.dfg.signatures[sig_ref]
1345 .params
1346 .iter()
1347 .map(|a| a.value_type);
1348 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1349 }
1350 CallInfo::NotACall => {}
1351 }
1352 Ok(())
1353 }
1354
1355 fn typecheck_block_call(
1356 &self,
1357 inst: Inst,
1358 block: &ir::BlockCall,
1359 errors: &mut VerifierErrors,
1360 ) -> VerifierStepResult {
1361 let pool = &self.func.dfg.value_lists;
1362 let iter = self
1363 .func
1364 .dfg
1365 .block_params(block.block(pool))
1366 .iter()
1367 .map(|&v| self.func.dfg.value_type(v));
1368 let args = block.args_slice(pool);
1369 self.typecheck_variable_args_iterator(inst, iter, args, errors)
1370 }
1371
1372 fn typecheck_variable_args_iterator<I: Iterator<Item = Type>>(
1373 &self,
1374 inst: Inst,
1375 iter: I,
1376 variable_args: &[Value],
1377 errors: &mut VerifierErrors,
1378 ) -> VerifierStepResult {
1379 let mut i = 0;
1380
1381 for expected_type in iter {
1382 if i >= variable_args.len() {
1383 i += 1;
1385 continue;
1386 }
1387 let arg = variable_args[i];
1388 let arg_type = self.func.dfg.value_type(arg);
1389 if expected_type != arg_type {
1390 errors.report((
1391 inst,
1392 self.context(inst),
1393 format!(
1394 "arg {} ({}) has type {}, expected {}",
1395 i, variable_args[i], arg_type, expected_type
1396 ),
1397 ));
1398 }
1399 i += 1;
1400 }
1401 if i != variable_args.len() {
1402 return errors.nonfatal((
1403 inst,
1404 self.context(inst),
1405 format!(
1406 "mismatched argument count for `{}`: got {}, expected {}",
1407 self.func.dfg.display_inst(inst),
1408 variable_args.len(),
1409 i,
1410 ),
1411 ));
1412 }
1413 Ok(())
1414 }
1415
1416 fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1417 match self.func.dfg.insts[inst] {
1418 ir::InstructionData::MultiAry {
1419 opcode: Opcode::Return,
1420 args,
1421 } => {
1422 let types = args
1423 .as_slice(&self.func.dfg.value_lists)
1424 .iter()
1425 .map(|v| self.func.dfg.value_type(*v));
1426 self.typecheck_return_types(
1427 inst,
1428 types,
1429 errors,
1430 "arguments of return must match function signature",
1431 )?;
1432 }
1433 ir::InstructionData::Call {
1434 opcode: Opcode::ReturnCall,
1435 func_ref,
1436 ..
1437 } => {
1438 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1439 self.typecheck_tail_call(inst, sig_ref, errors)?;
1440 }
1441 ir::InstructionData::CallIndirect {
1442 opcode: Opcode::ReturnCallIndirect,
1443 sig_ref,
1444 ..
1445 } => {
1446 self.typecheck_tail_call(inst, sig_ref, errors)?;
1447 }
1448 inst => debug_assert!(!inst.opcode().is_return()),
1449 }
1450 Ok(())
1451 }
1452
1453 fn typecheck_tail_call(
1454 &self,
1455 inst: Inst,
1456 sig_ref: SigRef,
1457 errors: &mut VerifierErrors,
1458 ) -> VerifierStepResult {
1459 let signature = &self.func.dfg.signatures[sig_ref];
1460 let cc = signature.call_conv;
1461 if !cc.supports_tail_calls() {
1462 errors.report((
1463 inst,
1464 self.context(inst),
1465 format!("calling convention `{cc}` does not support tail calls"),
1466 ));
1467 }
1468 if cc != self.func.signature.call_conv {
1469 errors.report((
1470 inst,
1471 self.context(inst),
1472 "callee's calling convention must match caller",
1473 ));
1474 }
1475 let types = signature.returns.iter().map(|param| param.value_type);
1476 self.typecheck_return_types(inst, types, errors, "results of callee must match caller")?;
1477 Ok(())
1478 }
1479
1480 fn typecheck_return_types(
1481 &self,
1482 inst: Inst,
1483 actual_types: impl ExactSizeIterator<Item = Type>,
1484 errors: &mut VerifierErrors,
1485 message: &str,
1486 ) -> VerifierStepResult {
1487 let expected_types = &self.func.signature.returns;
1488 if actual_types.len() != expected_types.len() {
1489 return errors.nonfatal((inst, self.context(inst), message));
1490 }
1491 for (i, (actual_type, &expected_type)) in actual_types.zip(expected_types).enumerate() {
1492 if actual_type != expected_type.value_type {
1493 errors.report((
1494 inst,
1495 self.context(inst),
1496 format!(
1497 "result {i} has type {actual_type}, must match function signature of \
1498 {expected_type}"
1499 ),
1500 ));
1501 }
1502 }
1503 Ok(())
1504 }
1505
1506 fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1509 match self.func.dfg.insts[inst] {
1510 ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
1511 if let Some(isa) = self.isa {
1512 let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
1513 let global_type = self.func.global_values[global_value].global_type(isa);
1514 if inst_type != global_type {
1515 return errors.nonfatal((
1516 inst, self.context(inst),
1517 format!(
1518 "global_value instruction with type {inst_type} references global value with type {global_type}"
1519 )),
1520 );
1521 }
1522 }
1523 }
1524 _ => {}
1525 }
1526 Ok(())
1527 }
1528
1529 fn cfg_integrity(
1530 &self,
1531 cfg: &ControlFlowGraph,
1532 errors: &mut VerifierErrors,
1533 ) -> VerifierStepResult {
1534 let mut expected_succs = BTreeSet::<Block>::new();
1535 let mut got_succs = BTreeSet::<Block>::new();
1536 let mut expected_preds = BTreeSet::<Inst>::new();
1537 let mut got_preds = BTreeSet::<Inst>::new();
1538
1539 for block in self.func.layout.blocks() {
1540 expected_succs.extend(self.expected_cfg.succ_iter(block));
1541 got_succs.extend(cfg.succ_iter(block));
1542
1543 let missing_succs: Vec<Block> =
1544 expected_succs.difference(&got_succs).cloned().collect();
1545 if !missing_succs.is_empty() {
1546 errors.report((
1547 block,
1548 format!("cfg lacked the following successor(s) {missing_succs:?}"),
1549 ));
1550 continue;
1551 }
1552
1553 let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();
1554 if !excess_succs.is_empty() {
1555 errors.report((
1556 block,
1557 format!("cfg had unexpected successor(s) {excess_succs:?}"),
1558 ));
1559 continue;
1560 }
1561
1562 expected_preds.extend(
1563 self.expected_cfg
1564 .pred_iter(block)
1565 .map(|BlockPredecessor { inst, .. }| inst),
1566 );
1567 got_preds.extend(
1568 cfg.pred_iter(block)
1569 .map(|BlockPredecessor { inst, .. }| inst),
1570 );
1571
1572 let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
1573 if !missing_preds.is_empty() {
1574 errors.report((
1575 block,
1576 format!("cfg lacked the following predecessor(s) {missing_preds:?}"),
1577 ));
1578 continue;
1579 }
1580
1581 let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
1582 if !excess_preds.is_empty() {
1583 errors.report((
1584 block,
1585 format!("cfg had unexpected predecessor(s) {excess_preds:?}"),
1586 ));
1587 continue;
1588 }
1589
1590 expected_succs.clear();
1591 got_succs.clear();
1592 expected_preds.clear();
1593 got_preds.clear();
1594 }
1595 errors.as_result()
1596 }
1597
1598 fn immediate_constraints(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1599 let inst_data = &self.func.dfg.insts[inst];
1600
1601 match *inst_data {
1602 ir::InstructionData::Store { flags, .. } => {
1603 if flags.readonly() {
1604 errors.fatal((
1605 inst,
1606 self.context(inst),
1607 "A store instruction cannot have the `readonly` MemFlag",
1608 ))
1609 } else {
1610 Ok(())
1611 }
1612 }
1613 ir::InstructionData::BinaryImm8 {
1614 opcode: ir::instructions::Opcode::Extractlane,
1615 imm: lane,
1616 arg,
1617 ..
1618 }
1619 | ir::InstructionData::TernaryImm8 {
1620 opcode: ir::instructions::Opcode::Insertlane,
1621 imm: lane,
1622 args: [arg, _],
1623 ..
1624 } => {
1625 let ty = self.func.dfg.value_type(arg);
1628 if lane as u32 >= ty.lane_count() {
1629 errors.fatal((
1630 inst,
1631 self.context(inst),
1632 format!("The lane {lane} does not index into the type {ty}",),
1633 ))
1634 } else {
1635 Ok(())
1636 }
1637 }
1638 ir::InstructionData::Shuffle {
1639 opcode: ir::instructions::Opcode::Shuffle,
1640 imm,
1641 ..
1642 } => {
1643 let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();
1644 if imm.len() != 16 {
1645 errors.fatal((
1646 inst,
1647 self.context(inst),
1648 format!("the shuffle immediate wasn't 16-bytes long"),
1649 ))
1650 } else if let Some(i) = imm.iter().find(|i| **i >= 32) {
1651 errors.fatal((
1652 inst,
1653 self.context(inst),
1654 format!("shuffle immediate index {i} is larger than the maximum 31"),
1655 ))
1656 } else {
1657 Ok(())
1658 }
1659 }
1660 _ => Ok(()),
1661 }
1662 }
1663
1664 fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1665 use crate::ir::instructions::InstructionData::UnaryImm;
1666
1667 let inst_data = &self.func.dfg.insts[inst];
1668 if let UnaryImm {
1669 opcode: Opcode::Iconst,
1670 imm,
1671 } = inst_data
1672 {
1673 let ctrl_typevar = self.func.dfg.ctrl_typevar(inst);
1674 let bounds_mask = match ctrl_typevar {
1675 types::I8 => u8::MAX.into(),
1676 types::I16 => u16::MAX.into(),
1677 types::I32 => u32::MAX.into(),
1678 types::I64 => u64::MAX,
1679 _ => unreachable!(),
1680 };
1681
1682 let value = imm.bits() as u64;
1683 if value & bounds_mask != value {
1684 errors.fatal((
1685 inst,
1686 self.context(inst),
1687 "constant immediate is out of bounds",
1688 ))
1689 } else {
1690 Ok(())
1691 }
1692 } else {
1693 Ok(())
1694 }
1695 }
1696
1697 fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1698 let params = self
1699 .func
1700 .signature
1701 .params
1702 .iter()
1703 .enumerate()
1704 .map(|p| (true, p));
1705 let returns = self
1706 .func
1707 .signature
1708 .returns
1709 .iter()
1710 .enumerate()
1711 .map(|p| (false, p));
1712
1713 for (is_argument, (i, param)) in params.chain(returns) {
1714 let is_return = !is_argument;
1715 let item = if is_argument {
1716 "Parameter"
1717 } else {
1718 "Return value"
1719 };
1720
1721 if param.value_type == types::INVALID {
1722 errors.report((
1723 AnyEntity::Function,
1724 format!("{item} at position {i} has an invalid type"),
1725 ));
1726 }
1727
1728 if let ArgumentPurpose::StructArgument(_) = param.purpose {
1729 if is_return {
1730 errors.report((
1731 AnyEntity::Function,
1732 format!("{item} at position {i} can't be an struct argument"),
1733 ))
1734 }
1735 }
1736
1737 let ty_allows_extension = param.value_type.is_int();
1738 let has_extension = param.extension != ArgumentExtension::None;
1739 if !ty_allows_extension && has_extension {
1740 errors.report((
1741 AnyEntity::Function,
1742 format!(
1743 "{} at position {} has invalid extension {:?}",
1744 item, i, param.extension
1745 ),
1746 ));
1747 }
1748 }
1749
1750 if errors.has_error() {
1751 Err(())
1752 } else {
1753 Ok(())
1754 }
1755 }
1756
1757 pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1758 self.verify_global_values(errors)?;
1759 self.verify_memory_types(errors)?;
1760 self.typecheck_entry_block_params(errors)?;
1761 self.check_entry_not_cold(errors)?;
1762 self.typecheck_function_signature(errors)?;
1763
1764 for block in self.func.layout.blocks() {
1765 if self.func.layout.first_inst(block).is_none() {
1766 return errors.fatal((block, format!("{block} cannot be empty")));
1767 }
1768 for inst in self.func.layout.block_insts(block) {
1769 crate::trace!("verifying {inst:?}: {}", self.func.dfg.display_inst(inst));
1770 self.block_integrity(block, inst, errors)?;
1771 self.instruction_integrity(inst, errors)?;
1772 self.typecheck(inst, errors)?;
1773 self.immediate_constraints(inst, errors)?;
1774 self.iconst_bounds(inst, errors)?;
1775 }
1776
1777 self.encodable_as_bb(block, errors)?;
1778 }
1779
1780 if !errors.is_empty() {
1781 log::warn!(
1782 "Found verifier errors in function:\n{}",
1783 pretty_verifier_error(self.func, None, errors.clone())
1784 );
1785 }
1786
1787 Ok(())
1788 }
1789}
1790
1791#[cfg(test)]
1792mod tests {
1793 use super::{Verifier, VerifierError, VerifierErrors};
1794 use crate::ir::instructions::{InstructionData, Opcode};
1795 use crate::ir::{types, AbiParam, Function, Type};
1796 use crate::settings;
1797
1798 macro_rules! assert_err_with_msg {
1799 ($e:expr, $msg:expr) => {
1800 match $e.0.get(0) {
1801 None => panic!("Expected an error"),
1802 Some(&VerifierError { ref message, .. }) => {
1803 if !message.contains($msg) {
1804 #[cfg(feature = "std")]
1805 panic!("'{}' did not contain the substring '{}'", message, $msg);
1806 #[cfg(not(feature = "std"))]
1807 panic!("error message did not contain the expected substring");
1808 }
1809 }
1810 }
1811 };
1812 }
1813
1814 #[test]
1815 fn empty() {
1816 let func = Function::new();
1817 let flags = &settings::Flags::new(settings::builder());
1818 let verifier = Verifier::new(&func, flags.into());
1819 let mut errors = VerifierErrors::default();
1820
1821 assert_eq!(verifier.run(&mut errors), Ok(()));
1822 assert!(errors.0.is_empty());
1823 }
1824
1825 #[test]
1826 fn bad_instruction_format() {
1827 let mut func = Function::new();
1828 let block0 = func.dfg.make_block();
1829 func.layout.append_block(block0);
1830 let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {
1831 opcode: Opcode::F32const,
1832 imm: 0.into(),
1833 });
1834 func.layout.append_inst(nullary_with_bad_opcode, block0);
1835 let destination = func.dfg.block_call(block0, &[]);
1836 func.stencil.layout.append_inst(
1837 func.stencil.dfg.make_inst(InstructionData::Jump {
1838 opcode: Opcode::Jump,
1839 destination,
1840 }),
1841 block0,
1842 );
1843 let flags = &settings::Flags::new(settings::builder());
1844 let verifier = Verifier::new(&func, flags.into());
1845 let mut errors = VerifierErrors::default();
1846
1847 let _ = verifier.run(&mut errors);
1848
1849 assert_err_with_msg!(errors, "instruction format");
1850 }
1851
1852 fn test_iconst_bounds(immediate: i64, ctrl_typevar: Type) -> VerifierErrors {
1853 let mut func = Function::new();
1854 let block0 = func.dfg.make_block();
1855 func.layout.append_block(block0);
1856
1857 let test_inst = func.dfg.make_inst(InstructionData::UnaryImm {
1858 opcode: Opcode::Iconst,
1859 imm: immediate.into(),
1860 });
1861
1862 let end_inst = func.dfg.make_inst(InstructionData::MultiAry {
1863 opcode: Opcode::Return,
1864 args: Default::default(),
1865 });
1866
1867 func.dfg.make_inst_results(test_inst, ctrl_typevar);
1868 func.layout.append_inst(test_inst, block0);
1869 func.layout.append_inst(end_inst, block0);
1870
1871 let flags = &settings::Flags::new(settings::builder());
1872 let verifier = Verifier::new(&func, flags.into());
1873 let mut errors = VerifierErrors::default();
1874
1875 let _ = verifier.run(&mut errors);
1876 errors
1877 }
1878
1879 fn test_iconst_bounds_err(immediate: i64, ctrl_typevar: Type) {
1880 assert_err_with_msg!(
1881 test_iconst_bounds(immediate, ctrl_typevar),
1882 "constant immediate is out of bounds"
1883 );
1884 }
1885
1886 fn test_iconst_bounds_ok(immediate: i64, ctrl_typevar: Type) {
1887 assert!(test_iconst_bounds(immediate, ctrl_typevar).is_empty());
1888 }
1889
1890 #[test]
1891 fn negative_iconst_8() {
1892 test_iconst_bounds_err(-10, types::I8);
1893 }
1894
1895 #[test]
1896 fn negative_iconst_32() {
1897 test_iconst_bounds_err(-1, types::I32);
1898 }
1899
1900 #[test]
1901 fn large_iconst_8() {
1902 test_iconst_bounds_err(1 + u8::MAX as i64, types::I8);
1903 }
1904
1905 #[test]
1906 fn large_iconst_16() {
1907 test_iconst_bounds_err(10 + u16::MAX as i64, types::I16);
1908 }
1909
1910 #[test]
1911 fn valid_iconst_8() {
1912 test_iconst_bounds_ok(10, types::I8);
1913 }
1914
1915 #[test]
1916 fn valid_iconst_32() {
1917 test_iconst_bounds_ok(u32::MAX as i64, types::I32);
1918 }
1919
1920 #[test]
1921 fn test_function_invalid_param() {
1922 let mut func = Function::new();
1923 func.signature.params.push(AbiParam::new(types::INVALID));
1924
1925 let mut errors = VerifierErrors::default();
1926 let flags = &settings::Flags::new(settings::builder());
1927 let verifier = Verifier::new(&func, flags.into());
1928
1929 let _ = verifier.typecheck_function_signature(&mut errors);
1930 assert_err_with_msg!(errors, "Parameter at position 0 has an invalid type");
1931 }
1932
1933 #[test]
1934 fn test_function_invalid_return_value() {
1935 let mut func = Function::new();
1936 func.signature.returns.push(AbiParam::new(types::INVALID));
1937
1938 let mut errors = VerifierErrors::default();
1939 let flags = &settings::Flags::new(settings::builder());
1940 let verifier = Verifier::new(&func, flags.into());
1941
1942 let _ = verifier.typecheck_function_signature(&mut errors);
1943 assert_err_with_msg!(errors, "Return value at position 0 has an invalid type");
1944 }
1945
1946 #[test]
1947 fn test_printing_contextual_errors() {
1948 let mut func = Function::new();
1950 let block0 = func.dfg.make_block();
1951 func.layout.append_block(block0);
1952
1953 let inst = func.dfg.make_inst(InstructionData::UnaryIeee64 {
1955 opcode: Opcode::F64const,
1956 imm: 0.0.into(),
1957 });
1958 func.layout.append_inst(inst, block0);
1959
1960 let mut errors = VerifierErrors::default();
1962 let flags = &settings::Flags::new(settings::builder());
1963 let verifier = Verifier::new(&func, flags.into());
1964
1965 let _ = verifier.typecheck_results(inst, types::I32, &mut errors);
1968 assert_eq!(
1969 format!("{}", errors.0[0]),
1970 "inst0 (f64const 0.0): has fewer result values than expected"
1971 )
1972 }
1973
1974 #[test]
1975 fn test_empty_block() {
1976 let mut func = Function::new();
1977 let block0 = func.dfg.make_block();
1978 func.layout.append_block(block0);
1979
1980 let flags = &settings::Flags::new(settings::builder());
1981 let verifier = Verifier::new(&func, flags.into());
1982 let mut errors = VerifierErrors::default();
1983 let _ = verifier.run(&mut errors);
1984
1985 assert_err_with_msg!(errors, "block0 cannot be empty");
1986 }
1987}