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