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