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