1use crate::dbg::DisplayList;
67use crate::dominator_tree::DominatorTree;
68use crate::entity::SparseSet;
69use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
70use crate::ir::ExceptionTableItem;
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: DominatorTree,
306 isa: Option<&'a dyn TargetIsa>,
307}
308
309impl<'a> Verifier<'a> {
310 pub fn new(func: &'a Function, fisa: FlagsOrIsa<'a>) -> Self {
311 let expected_cfg = ControlFlowGraph::with_function(func);
312 let expected_domtree = DominatorTree::with_function(func, &expected_cfg);
313 Self {
314 func,
315 expected_cfg,
316 expected_domtree,
317 isa: fisa.isa,
318 }
319 }
320
321 #[inline]
323 fn context(&self, inst: Inst) -> String {
324 self.func.dfg.display_inst(inst).to_string()
325 }
326
327 fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
331 let mut cycle_seen = false;
332 let mut seen = SparseSet::new();
333
334 'gvs: for gv in self.func.global_values.keys() {
335 seen.clear();
336 seen.insert(gv);
337
338 let mut cur = gv;
339 loop {
340 match self.func.global_values[cur] {
341 ir::GlobalValueData::Load { base, .. }
342 | ir::GlobalValueData::IAddImm { base, .. } => {
343 if seen.insert(base).is_some() {
344 if !cycle_seen {
345 errors.report((
346 gv,
347 format!("global value cycle: {}", DisplayList(seen.as_slice())),
348 ));
349 cycle_seen = true;
351 }
352 continue 'gvs;
353 }
354
355 cur = base;
356 }
357 _ => break,
358 }
359 }
360
361 match self.func.global_values[gv] {
362 ir::GlobalValueData::VMContext { .. } => {
363 if self
364 .func
365 .special_param(ir::ArgumentPurpose::VMContext)
366 .is_none()
367 {
368 errors.report((gv, format!("undeclared vmctx reference {gv}")));
369 }
370 }
371 ir::GlobalValueData::IAddImm {
372 base, global_type, ..
373 } => {
374 if !global_type.is_int() {
375 errors.report((
376 gv,
377 format!("iadd_imm global value with non-int type {global_type}"),
378 ));
379 } else if let Some(isa) = self.isa {
380 let base_type = self.func.global_values[base].global_type(isa);
381 if global_type != base_type {
382 errors.report((
383 gv,
384 format!(
385 "iadd_imm type {global_type} differs from operand type {base_type}"
386 ),
387 ));
388 }
389 }
390 }
391 ir::GlobalValueData::Load { base, .. } => {
392 if let Some(isa) = self.isa {
393 let base_type = self.func.global_values[base].global_type(isa);
394 let pointer_type = isa.pointer_type();
395 if base_type != pointer_type {
396 errors.report((
397 gv,
398 format!(
399 "base {base} has type {base_type}, which is not the pointer type {pointer_type}"
400 ),
401 ));
402 }
403 }
404 }
405 _ => {}
406 }
407 }
408
409 Ok(())
411 }
412
413 fn verify_memory_types(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
414 for (mt, mt_data) in &self.func.memory_types {
417 match mt_data {
418 MemoryTypeData::Struct { size, fields } => {
419 let mut last_offset = 0;
420 for field in fields {
421 if field.offset < last_offset {
422 errors.report((
423 mt,
424 format!(
425 "memory type {} has a field at offset {}, which is out-of-order",
426 mt, field.offset
427 ),
428 ));
429 }
430 last_offset = match field.offset.checked_add(u64::from(field.ty.bytes())) {
431 Some(o) => o,
432 None => {
433 errors.report((
434 mt,
435 format!(
436 "memory type {} has a field at offset {} of size {}; offset plus size overflows a u64",
437 mt, field.offset, field.ty.bytes()),
438 ));
439 break;
440 }
441 };
442
443 if last_offset > *size {
444 errors.report((
445 mt,
446 format!(
447 "memory type {} has a field at offset {} of size {} that overflows the struct size {}",
448 mt, field.offset, field.ty.bytes(), *size),
449 ));
450 }
451 }
452 }
453 _ => {}
454 }
455 }
456
457 Ok(())
458 }
459
460 fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult {
463 match self.func.is_block_basic(block) {
464 Ok(()) => Ok(()),
465 Err((inst, message)) => errors.fatal((inst, self.context(inst), message)),
466 }
467 }
468
469 fn block_integrity(
470 &self,
471 block: Block,
472 inst: Inst,
473 errors: &mut VerifierErrors,
474 ) -> VerifierStepResult {
475 let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator();
476 let is_last_inst = self.func.layout.last_inst(block) == Some(inst);
477
478 if is_terminator && !is_last_inst {
479 return errors.fatal((
481 inst,
482 self.context(inst),
483 format!("a terminator instruction was encountered before the end of {block}"),
484 ));
485 }
486 if is_last_inst && !is_terminator {
487 return errors.fatal((block, "block does not end in a terminator instruction"));
488 }
489
490 let inst_block = self.func.layout.inst_block(inst);
492 if inst_block != Some(block) {
493 return errors.fatal((
494 inst,
495 self.context(inst),
496 format!("should belong to {block} not {inst_block:?}"),
497 ));
498 }
499
500 for &arg in self.func.dfg.block_params(block) {
502 match self.func.dfg.value_def(arg) {
503 ValueDef::Param(arg_block, _) => {
504 if block != arg_block {
505 return errors.fatal((arg, format!("does not belong to {block}")));
506 }
507 }
508 _ => {
509 return errors.fatal((arg, "expected an argument, found a result"));
510 }
511 }
512 }
513
514 Ok(())
515 }
516
517 fn instruction_integrity(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
518 let inst_data = &self.func.dfg.insts[inst];
519 let dfg = &self.func.dfg;
520
521 if inst_data.opcode().format() != InstructionFormat::from(inst_data) {
523 return errors.fatal((
524 inst,
525 self.context(inst),
526 "instruction opcode doesn't match instruction format",
527 ));
528 }
529
530 let expected_num_results = dfg.num_expected_results_for_verifier(inst);
531
532 let got_results = dfg.inst_results(inst).len();
534 if got_results != expected_num_results {
535 return errors.fatal((
536 inst,
537 self.context(inst),
538 format!("expected {expected_num_results} result values, found {got_results}"),
539 ));
540 }
541
542 self.verify_entity_references(inst, errors)
543 }
544
545 fn verify_entity_references(
546 &self,
547 inst: Inst,
548 errors: &mut VerifierErrors,
549 ) -> VerifierStepResult {
550 use crate::ir::instructions::InstructionData::*;
551
552 for arg in self.func.dfg.inst_values(inst) {
553 self.verify_inst_arg(inst, arg, errors)?;
554
555 let original = self.func.dfg.resolve_aliases(arg);
557 if !self.func.dfg.value_is_attached(original) {
558 errors.report((
559 inst,
560 self.context(inst),
561 format!("argument {arg} -> {original} is not attached"),
562 ));
563 }
564 }
565
566 for &res in self.func.dfg.inst_results(inst) {
567 self.verify_inst_result(inst, res, errors)?;
568 }
569
570 match self.func.dfg.insts[inst] {
571 MultiAry { ref args, .. } => {
572 self.verify_value_list(inst, args, errors)?;
573 }
574 Jump { destination, .. } => {
575 self.verify_block(inst, destination.block(&self.func.dfg.value_lists), errors)?;
576 }
577 Brif {
578 arg,
579 blocks: [block_then, block_else],
580 ..
581 } => {
582 self.verify_value(inst, arg, errors)?;
583 self.verify_block(inst, block_then.block(&self.func.dfg.value_lists), errors)?;
584 self.verify_block(inst, block_else.block(&self.func.dfg.value_lists), errors)?;
585 }
586 BranchTable { table, .. } => {
587 self.verify_jump_table(inst, table, errors)?;
588 }
589 Call {
590 func_ref, ref args, ..
591 } => {
592 self.verify_func_ref(inst, func_ref, errors)?;
593 self.verify_value_list(inst, args, errors)?;
594 }
595 CallIndirect {
596 sig_ref, ref args, ..
597 } => {
598 self.verify_sig_ref(inst, sig_ref, errors)?;
599 self.verify_value_list(inst, args, errors)?;
600 }
601 TryCall {
602 func_ref,
603 ref args,
604 exception,
605 ..
606 } => {
607 self.verify_func_ref(inst, func_ref, errors)?;
608 self.verify_value_list(inst, args, errors)?;
609 self.verify_exception_table(inst, exception, errors)?;
610 self.verify_exception_compatible_abi(inst, exception, errors)?;
611 }
612 TryCallIndirect {
613 ref args,
614 exception,
615 ..
616 } => {
617 self.verify_value_list(inst, args, errors)?;
618 self.verify_exception_table(inst, exception, errors)?;
619 self.verify_exception_compatible_abi(inst, exception, errors)?;
620 }
621 FuncAddr { func_ref, .. } => {
622 self.verify_func_ref(inst, func_ref, errors)?;
623 }
624 StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => {
625 self.verify_stack_slot(inst, stack_slot, errors)?;
626 }
627 DynamicStackLoad {
628 dynamic_stack_slot, ..
629 }
630 | DynamicStackStore {
631 dynamic_stack_slot, ..
632 } => {
633 self.verify_dynamic_stack_slot(inst, dynamic_stack_slot, errors)?;
634 }
635 UnaryGlobalValue { global_value, .. } => {
636 self.verify_global_value(inst, global_value, errors)?;
637 }
638 NullAry {
639 opcode: Opcode::GetPinnedReg,
640 }
641 | Unary {
642 opcode: Opcode::SetPinnedReg,
643 ..
644 } => {
645 if let Some(isa) = &self.isa {
646 if !isa.flags().enable_pinned_reg() {
647 return errors.fatal((
648 inst,
649 self.context(inst),
650 "GetPinnedReg/SetPinnedReg cannot be used without enable_pinned_reg",
651 ));
652 }
653 } else {
654 return errors.fatal((
655 inst,
656 self.context(inst),
657 "GetPinnedReg/SetPinnedReg need an ISA!",
658 ));
659 }
660 }
661 NullAry {
662 opcode: Opcode::GetFramePointer | Opcode::GetReturnAddress,
663 } => {
664 if let Some(isa) = &self.isa {
665 if !isa.flags().preserve_frame_pointers() {
668 return errors.fatal((
669 inst,
670 self.context(inst),
671 "`get_frame_pointer`/`get_return_address` cannot be used without \
672 enabling `preserve_frame_pointers`",
673 ));
674 }
675 } else {
676 return errors.fatal((
677 inst,
678 self.context(inst),
679 "`get_frame_pointer`/`get_return_address` require an ISA!",
680 ));
681 }
682 }
683 LoadNoOffset {
684 opcode: Opcode::Bitcast,
685 flags,
686 arg,
687 } => {
688 self.verify_bitcast(inst, flags, arg, errors)?;
689 }
690 LoadNoOffset { opcode, arg, .. } if opcode.can_load() => {
691 self.verify_is_address(inst, arg, errors)?;
692 }
693 Load { opcode, arg, .. } if opcode.can_load() => {
694 self.verify_is_address(inst, arg, errors)?;
695 }
696 AtomicCas {
697 opcode,
698 args: [p, _, _],
699 ..
700 } if opcode.can_load() || opcode.can_store() => {
701 self.verify_is_address(inst, p, errors)?;
702 }
703 AtomicRmw {
704 opcode,
705 args: [p, _],
706 ..
707 } if opcode.can_load() || opcode.can_store() => {
708 self.verify_is_address(inst, p, errors)?;
709 }
710 Store {
711 opcode,
712 args: [_, p],
713 ..
714 } if opcode.can_store() => {
715 self.verify_is_address(inst, p, errors)?;
716 }
717 StoreNoOffset {
718 opcode,
719 args: [_, p],
720 ..
721 } if opcode.can_store() => {
722 self.verify_is_address(inst, p, errors)?;
723 }
724 UnaryConst {
725 opcode: opcode @ (Opcode::Vconst | Opcode::F128const),
726 constant_handle,
727 ..
728 } => {
729 self.verify_constant_size(inst, opcode, constant_handle, errors)?;
730 }
731
732 ExceptionHandlerAddress { block, imm, .. } => {
733 self.verify_block(inst, block, errors)?;
734 self.verify_try_call_handler_index(inst, block, imm.into(), errors)?;
735 }
736
737 AtomicCas { .. }
739 | AtomicRmw { .. }
740 | LoadNoOffset { .. }
741 | StoreNoOffset { .. }
742 | Unary { .. }
743 | UnaryConst { .. }
744 | UnaryImm { .. }
745 | UnaryIeee16 { .. }
746 | UnaryIeee32 { .. }
747 | UnaryIeee64 { .. }
748 | Binary { .. }
749 | BinaryImm8 { .. }
750 | BinaryImm64 { .. }
751 | Ternary { .. }
752 | TernaryImm8 { .. }
753 | Shuffle { .. }
754 | IntAddTrap { .. }
755 | IntCompare { .. }
756 | IntCompareImm { .. }
757 | FloatCompare { .. }
758 | Load { .. }
759 | Store { .. }
760 | Trap { .. }
761 | CondTrap { .. }
762 | NullAry { .. } => {}
763 }
764
765 Ok(())
766 }
767
768 fn verify_block(
769 &self,
770 loc: impl Into<AnyEntity>,
771 e: Block,
772 errors: &mut VerifierErrors,
773 ) -> VerifierStepResult {
774 if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) {
775 return errors.fatal((loc, format!("invalid block reference {e}")));
776 }
777 if let Some(entry_block) = self.func.layout.entry_block() {
778 if e == entry_block {
779 return errors.fatal((loc, format!("invalid reference to entry block {e}")));
780 }
781 }
782 Ok(())
783 }
784
785 fn verify_sig_ref(
786 &self,
787 inst: Inst,
788 s: SigRef,
789 errors: &mut VerifierErrors,
790 ) -> VerifierStepResult {
791 if !self.func.dfg.signatures.is_valid(s) {
792 errors.fatal((
793 inst,
794 self.context(inst),
795 format!("invalid signature reference {s}"),
796 ))
797 } else {
798 Ok(())
799 }
800 }
801
802 fn verify_func_ref(
803 &self,
804 inst: Inst,
805 f: FuncRef,
806 errors: &mut VerifierErrors,
807 ) -> VerifierStepResult {
808 if !self.func.dfg.ext_funcs.is_valid(f) {
809 errors.nonfatal((
810 inst,
811 self.context(inst),
812 format!("invalid function reference {f}"),
813 ))
814 } else {
815 Ok(())
816 }
817 }
818
819 fn verify_stack_slot(
820 &self,
821 inst: Inst,
822 ss: StackSlot,
823 errors: &mut VerifierErrors,
824 ) -> VerifierStepResult {
825 if !self.func.sized_stack_slots.is_valid(ss) {
826 errors.nonfatal((inst, self.context(inst), format!("invalid stack slot {ss}")))
827 } else {
828 Ok(())
829 }
830 }
831
832 fn verify_dynamic_stack_slot(
833 &self,
834 inst: Inst,
835 ss: DynamicStackSlot,
836 errors: &mut VerifierErrors,
837 ) -> VerifierStepResult {
838 if !self.func.dynamic_stack_slots.is_valid(ss) {
839 errors.nonfatal((
840 inst,
841 self.context(inst),
842 format!("invalid dynamic stack slot {ss}"),
843 ))
844 } else {
845 Ok(())
846 }
847 }
848
849 fn verify_global_value(
850 &self,
851 inst: Inst,
852 gv: GlobalValue,
853 errors: &mut VerifierErrors,
854 ) -> VerifierStepResult {
855 if !self.func.global_values.is_valid(gv) {
856 errors.nonfatal((
857 inst,
858 self.context(inst),
859 format!("invalid global value {gv}"),
860 ))
861 } else {
862 Ok(())
863 }
864 }
865
866 fn verify_value_list(
867 &self,
868 inst: Inst,
869 l: &ValueList,
870 errors: &mut VerifierErrors,
871 ) -> VerifierStepResult {
872 if !l.is_valid(&self.func.dfg.value_lists) {
873 errors.nonfatal((
874 inst,
875 self.context(inst),
876 format!("invalid value list reference {l:?}"),
877 ))
878 } else {
879 Ok(())
880 }
881 }
882
883 fn verify_jump_table(
884 &self,
885 inst: Inst,
886 j: JumpTable,
887 errors: &mut VerifierErrors,
888 ) -> VerifierStepResult {
889 if !self.func.stencil.dfg.jump_tables.is_valid(j) {
890 errors.nonfatal((
891 inst,
892 self.context(inst),
893 format!("invalid jump table reference {j}"),
894 ))
895 } else {
896 let pool = &self.func.stencil.dfg.value_lists;
897 for block in self.func.stencil.dfg.jump_tables[j].all_branches() {
898 self.verify_block(inst, block.block(pool), errors)?;
899 }
900 Ok(())
901 }
902 }
903
904 fn verify_exception_table(
905 &self,
906 inst: Inst,
907 et: ExceptionTable,
908 errors: &mut VerifierErrors,
909 ) -> VerifierStepResult {
910 if !self.func.stencil.dfg.exception_tables.is_valid(et) {
912 errors.nonfatal((
913 inst,
914 self.context(inst),
915 format!("invalid exception table reference {et}"),
916 ))?;
917 }
918
919 let pool = &self.func.stencil.dfg.value_lists;
920 let exdata = &self.func.stencil.dfg.exception_tables[et];
921
922 self.verify_sig_ref(inst, exdata.signature(), errors)?;
925
926 for block in exdata.all_branches() {
928 self.verify_block(inst, block.block(pool), errors)?;
929 }
930 Ok(())
931 }
932
933 fn verify_exception_compatible_abi(
934 &self,
935 inst: Inst,
936 et: ExceptionTable,
937 errors: &mut VerifierErrors,
938 ) -> VerifierStepResult {
939 let callee_sig_ref = self.func.dfg.exception_tables[et].signature();
940 let callee_sig = &self.func.dfg.signatures[callee_sig_ref];
941 let callee_call_conv = callee_sig.call_conv;
942 if !callee_call_conv.supports_exceptions() {
943 errors.nonfatal((
944 inst,
945 self.context(inst),
946 format!(
947 "calling convention `{callee_call_conv}` of callee does not support exceptions"
948 ),
949 ))?;
950 }
951 Ok(())
952 }
953
954 fn verify_value(
955 &self,
956 loc_inst: Inst,
957 v: Value,
958 errors: &mut VerifierErrors,
959 ) -> VerifierStepResult {
960 let dfg = &self.func.dfg;
961 if !dfg.value_is_valid(v) {
962 errors.nonfatal((
963 loc_inst,
964 self.context(loc_inst),
965 format!("invalid value reference {v}"),
966 ))
967 } else {
968 Ok(())
969 }
970 }
971
972 fn verify_inst_arg(
973 &self,
974 loc_inst: Inst,
975 v: Value,
976 errors: &mut VerifierErrors,
977 ) -> VerifierStepResult {
978 self.verify_value(loc_inst, v, errors)?;
979
980 let dfg = &self.func.dfg;
981 let loc_block = self
982 .func
983 .layout
984 .inst_block(loc_inst)
985 .expect("Instruction not in layout.");
986 let is_reachable = self.expected_domtree.is_reachable(loc_block);
987
988 match dfg.value_def(v) {
990 ValueDef::Result(def_inst, _) => {
991 if !dfg.inst_is_valid(def_inst) {
993 return errors.fatal((
994 loc_inst,
995 self.context(loc_inst),
996 format!("{v} is defined by invalid instruction {def_inst}"),
997 ));
998 }
999 if self.func.layout.inst_block(def_inst) == None {
1001 return errors.fatal((
1002 loc_inst,
1003 self.context(loc_inst),
1004 format!("{v} is defined by {def_inst} which has no block"),
1005 ));
1006 }
1007 if is_reachable {
1009 if !self
1010 .expected_domtree
1011 .dominates(def_inst, loc_inst, &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.block_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 item in exdata.items() {
1417 match item {
1418 ExceptionTableItem::Tag(_, block_call)
1419 | ExceptionTableItem::Default(block_call) => {
1420 self.typecheck_block_call(
1421 inst,
1422 &block_call,
1423 BlockCallTargetType::Exception,
1424 errors,
1425 )?;
1426 }
1427 ExceptionTableItem::Context(_) => {}
1428 }
1429 }
1430 }
1431 inst => debug_assert!(!inst.opcode().is_branch()),
1432 }
1433
1434 match self.func.dfg.insts[inst]
1435 .analyze_call(&self.func.dfg.value_lists, &self.func.dfg.exception_tables)
1436 {
1437 CallInfo::Direct(func_ref, args) => {
1438 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1439 let arg_types = self.func.dfg.signatures[sig_ref]
1440 .params
1441 .iter()
1442 .map(|a| a.value_type);
1443 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1444 }
1445 CallInfo::DirectWithSig(func_ref, sig_ref, args) => {
1446 let expected_sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1447 let sigdata = &self.func.dfg.signatures;
1448 if sigdata[sig_ref] != sigdata[expected_sig_ref] {
1451 errors.nonfatal((
1452 inst,
1453 self.context(inst),
1454 format!(
1455 "exception table signature {sig_ref} did not match function {func_ref}'s signature {expected_sig_ref}"
1456 ),
1457 ))?;
1458 }
1459 let arg_types = self.func.dfg.signatures[sig_ref]
1460 .params
1461 .iter()
1462 .map(|a| a.value_type);
1463 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1464 }
1465 CallInfo::Indirect(sig_ref, args) => {
1466 let arg_types = self.func.dfg.signatures[sig_ref]
1467 .params
1468 .iter()
1469 .map(|a| a.value_type);
1470 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1471 }
1472 CallInfo::NotACall => {}
1473 }
1474 Ok(())
1475 }
1476
1477 fn typecheck_block_call(
1478 &self,
1479 inst: Inst,
1480 block: &ir::BlockCall,
1481 target_type: BlockCallTargetType,
1482 errors: &mut VerifierErrors,
1483 ) -> VerifierStepResult {
1484 let pool = &self.func.dfg.value_lists;
1485 let block_params = self.func.dfg.block_params(block.block(pool));
1486 let args = block.args(pool);
1487 if args.len() != block_params.len() {
1488 return errors.nonfatal((
1489 inst,
1490 self.context(inst),
1491 format!(
1492 "mismatched argument count for `{}`: got {}, expected {}",
1493 self.func.dfg.display_inst(inst),
1494 args.len(),
1495 block_params.len(),
1496 ),
1497 ));
1498 }
1499 for (arg, param) in args.zip(block_params.iter()) {
1500 let Some(arg_ty) = self.block_call_arg_ty(arg, inst, target_type, errors)? else {
1501 continue;
1502 };
1503 let param_ty = self.func.dfg.value_type(*param);
1504 if arg_ty != param_ty {
1505 errors.nonfatal((
1506 inst,
1507 self.context(inst),
1508 format!("arg {arg} has type {arg_ty}, expected {param_ty}"),
1509 ))?;
1510 }
1511 }
1512 Ok(())
1513 }
1514
1515 fn block_call_arg_ty(
1516 &self,
1517 arg: BlockArg,
1518 inst: Inst,
1519 target_type: BlockCallTargetType,
1520 errors: &mut VerifierErrors,
1521 ) -> Result<Option<Type>, ()> {
1522 match arg {
1523 BlockArg::Value(v) => Ok(Some(self.func.dfg.value_type(v))),
1524 BlockArg::TryCallRet(_) | BlockArg::TryCallExn(_) => {
1525 let et = match self.func.dfg.insts[inst].exception_table() {
1527 Some(et) => et,
1528 None => {
1529 errors.fatal((
1530 inst,
1531 self.context(inst),
1532 format!(
1533 "`retN` block argument in block-call not on `try_call` instruction"
1534 ),
1535 ))?;
1536 unreachable!()
1537 }
1538 };
1539 let exdata = &self.func.dfg.exception_tables[et];
1540 let sig = &self.func.dfg.signatures[exdata.signature()];
1541
1542 match (arg, target_type) {
1543 (BlockArg::TryCallRet(i), BlockCallTargetType::ExNormalRet)
1544 if (i as usize) < sig.returns.len() =>
1545 {
1546 Ok(Some(sig.returns[i as usize].value_type))
1547 }
1548 (BlockArg::TryCallRet(_), BlockCallTargetType::ExNormalRet) => {
1549 errors.fatal((
1550 inst,
1551 self.context(inst),
1552 format!("out-of-bounds `retN` block argument"),
1553 ))?;
1554 unreachable!()
1555 }
1556 (BlockArg::TryCallRet(_), _) => {
1557 errors.fatal((
1558 inst,
1559 self.context(inst),
1560 format!("`retN` block argument used outside normal-return target of `try_call`"),
1561 ))?;
1562 unreachable!()
1563 }
1564 (BlockArg::TryCallExn(i), BlockCallTargetType::Exception) => {
1565 if let Some(isa) = self.isa {
1566 match sig
1567 .call_conv
1568 .exception_payload_types(isa.pointer_type())
1569 .get(i as usize)
1570 {
1571 Some(ty) => Ok(Some(*ty)),
1572 None => {
1573 errors.fatal((
1574 inst,
1575 self.context(inst),
1576 format!("out-of-bounds `exnN` block argument"),
1577 ))?;
1578 unreachable!()
1579 }
1580 }
1581 } else {
1582 Ok(None)
1583 }
1584 }
1585 (BlockArg::TryCallExn(_), _) => {
1586 errors.fatal((
1587 inst,
1588 self.context(inst),
1589 format!("`exnN` block argument used outside normal-return target of `try_call`"),
1590 ))?;
1591 unreachable!()
1592 }
1593 _ => unreachable!(),
1594 }
1595 }
1596 }
1597 }
1598
1599 fn typecheck_variable_args_iterator(
1600 &self,
1601 inst: Inst,
1602 iter: impl ExactSizeIterator<Item = Type>,
1603 variable_args: &[Value],
1604 errors: &mut VerifierErrors,
1605 ) -> VerifierStepResult {
1606 let mut i = 0;
1607
1608 for expected_type in iter {
1609 if i >= variable_args.len() {
1610 i += 1;
1612 continue;
1613 }
1614 let arg = variable_args[i];
1615 let arg_type = self.func.dfg.value_type(arg);
1616 if expected_type != arg_type {
1617 errors.report((
1618 inst,
1619 self.context(inst),
1620 format!(
1621 "arg {} ({}) has type {}, expected {}",
1622 i, variable_args[i], arg_type, expected_type
1623 ),
1624 ));
1625 }
1626 i += 1;
1627 }
1628 if i != variable_args.len() {
1629 return errors.nonfatal((
1630 inst,
1631 self.context(inst),
1632 format!(
1633 "mismatched argument count for `{}`: got {}, expected {}",
1634 self.func.dfg.display_inst(inst),
1635 variable_args.len(),
1636 i,
1637 ),
1638 ));
1639 }
1640 Ok(())
1641 }
1642
1643 fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1644 match self.func.dfg.insts[inst] {
1645 ir::InstructionData::MultiAry {
1646 opcode: Opcode::Return,
1647 args,
1648 } => {
1649 let types = args
1650 .as_slice(&self.func.dfg.value_lists)
1651 .iter()
1652 .map(|v| self.func.dfg.value_type(*v));
1653 self.typecheck_return_types(
1654 inst,
1655 types,
1656 errors,
1657 "arguments of return must match function signature",
1658 )?;
1659 }
1660 ir::InstructionData::Call {
1661 opcode: Opcode::ReturnCall,
1662 func_ref,
1663 ..
1664 } => {
1665 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1666 self.typecheck_tail_call(inst, sig_ref, errors)?;
1667 }
1668 ir::InstructionData::CallIndirect {
1669 opcode: Opcode::ReturnCallIndirect,
1670 sig_ref,
1671 ..
1672 } => {
1673 self.typecheck_tail_call(inst, sig_ref, errors)?;
1674 }
1675 inst => debug_assert!(!inst.opcode().is_return()),
1676 }
1677 Ok(())
1678 }
1679
1680 fn typecheck_tail_call(
1681 &self,
1682 inst: Inst,
1683 sig_ref: SigRef,
1684 errors: &mut VerifierErrors,
1685 ) -> VerifierStepResult {
1686 let signature = &self.func.dfg.signatures[sig_ref];
1687 let cc = signature.call_conv;
1688 if !cc.supports_tail_calls() {
1689 errors.report((
1690 inst,
1691 self.context(inst),
1692 format!("calling convention `{cc}` does not support tail calls"),
1693 ));
1694 }
1695 if cc != self.func.signature.call_conv {
1696 errors.report((
1697 inst,
1698 self.context(inst),
1699 "callee's calling convention must match caller",
1700 ));
1701 }
1702 let types = signature.returns.iter().map(|param| param.value_type);
1703 self.typecheck_return_types(inst, types, errors, "results of callee must match caller")?;
1704 Ok(())
1705 }
1706
1707 fn typecheck_return_types(
1708 &self,
1709 inst: Inst,
1710 actual_types: impl ExactSizeIterator<Item = Type>,
1711 errors: &mut VerifierErrors,
1712 message: &str,
1713 ) -> VerifierStepResult {
1714 let expected_types = &self.func.signature.returns;
1715 if actual_types.len() != expected_types.len() {
1716 return errors.nonfatal((inst, self.context(inst), message));
1717 }
1718 for (i, (actual_type, &expected_type)) in actual_types.zip(expected_types).enumerate() {
1719 if actual_type != expected_type.value_type {
1720 errors.report((
1721 inst,
1722 self.context(inst),
1723 format!(
1724 "result {i} has type {actual_type}, must match function signature of \
1725 {expected_type}"
1726 ),
1727 ));
1728 }
1729 }
1730 Ok(())
1731 }
1732
1733 fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1736 match self.func.dfg.insts[inst] {
1737 ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
1738 if let Some(isa) = self.isa {
1739 let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
1740 let global_type = self.func.global_values[global_value].global_type(isa);
1741 if inst_type != global_type {
1742 return errors.nonfatal((
1743 inst, self.context(inst),
1744 format!(
1745 "global_value instruction with type {inst_type} references global value with type {global_type}"
1746 )),
1747 );
1748 }
1749 }
1750 }
1751 _ => {}
1752 }
1753 Ok(())
1754 }
1755
1756 fn cfg_integrity(
1757 &self,
1758 cfg: &ControlFlowGraph,
1759 errors: &mut VerifierErrors,
1760 ) -> VerifierStepResult {
1761 let mut expected_succs = BTreeSet::<Block>::new();
1762 let mut got_succs = BTreeSet::<Block>::new();
1763 let mut expected_preds = BTreeSet::<Inst>::new();
1764 let mut got_preds = BTreeSet::<Inst>::new();
1765
1766 for block in self.func.layout.blocks() {
1767 expected_succs.extend(self.expected_cfg.succ_iter(block));
1768 got_succs.extend(cfg.succ_iter(block));
1769
1770 let missing_succs: Vec<Block> =
1771 expected_succs.difference(&got_succs).cloned().collect();
1772 if !missing_succs.is_empty() {
1773 errors.report((
1774 block,
1775 format!("cfg lacked the following successor(s) {missing_succs:?}"),
1776 ));
1777 continue;
1778 }
1779
1780 let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();
1781 if !excess_succs.is_empty() {
1782 errors.report((
1783 block,
1784 format!("cfg had unexpected successor(s) {excess_succs:?}"),
1785 ));
1786 continue;
1787 }
1788
1789 expected_preds.extend(
1790 self.expected_cfg
1791 .pred_iter(block)
1792 .map(|BlockPredecessor { inst, .. }| inst),
1793 );
1794 got_preds.extend(
1795 cfg.pred_iter(block)
1796 .map(|BlockPredecessor { inst, .. }| inst),
1797 );
1798
1799 let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
1800 if !missing_preds.is_empty() {
1801 errors.report((
1802 block,
1803 format!("cfg lacked the following predecessor(s) {missing_preds:?}"),
1804 ));
1805 continue;
1806 }
1807
1808 let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
1809 if !excess_preds.is_empty() {
1810 errors.report((
1811 block,
1812 format!("cfg had unexpected predecessor(s) {excess_preds:?}"),
1813 ));
1814 continue;
1815 }
1816
1817 expected_succs.clear();
1818 got_succs.clear();
1819 expected_preds.clear();
1820 got_preds.clear();
1821 }
1822 errors.as_result()
1823 }
1824
1825 fn immediate_constraints(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1826 let inst_data = &self.func.dfg.insts[inst];
1827
1828 match *inst_data {
1829 ir::InstructionData::Store { flags, .. } => {
1830 if flags.readonly() {
1831 errors.fatal((
1832 inst,
1833 self.context(inst),
1834 "A store instruction cannot have the `readonly` MemFlag",
1835 ))
1836 } else {
1837 Ok(())
1838 }
1839 }
1840 ir::InstructionData::BinaryImm8 {
1841 opcode: ir::instructions::Opcode::Extractlane,
1842 imm: lane,
1843 arg,
1844 ..
1845 }
1846 | ir::InstructionData::TernaryImm8 {
1847 opcode: ir::instructions::Opcode::Insertlane,
1848 imm: lane,
1849 args: [arg, _],
1850 ..
1851 } => {
1852 let ty = self.func.dfg.value_type(arg);
1855 if lane as u32 >= ty.lane_count() {
1856 errors.fatal((
1857 inst,
1858 self.context(inst),
1859 format!("The lane {lane} does not index into the type {ty}",),
1860 ))
1861 } else {
1862 Ok(())
1863 }
1864 }
1865 ir::InstructionData::Shuffle {
1866 opcode: ir::instructions::Opcode::Shuffle,
1867 imm,
1868 ..
1869 } => {
1870 let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();
1871 if imm.len() != 16 {
1872 errors.fatal((
1873 inst,
1874 self.context(inst),
1875 format!("the shuffle immediate wasn't 16-bytes long"),
1876 ))
1877 } else if let Some(i) = imm.iter().find(|i| **i >= 32) {
1878 errors.fatal((
1879 inst,
1880 self.context(inst),
1881 format!("shuffle immediate index {i} is larger than the maximum 31"),
1882 ))
1883 } else {
1884 Ok(())
1885 }
1886 }
1887 _ => Ok(()),
1888 }
1889 }
1890
1891 fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1892 use crate::ir::instructions::InstructionData::UnaryImm;
1893
1894 let inst_data = &self.func.dfg.insts[inst];
1895 if let UnaryImm {
1896 opcode: Opcode::Iconst,
1897 imm,
1898 } = inst_data
1899 {
1900 let ctrl_typevar = self.func.dfg.ctrl_typevar(inst);
1901 let bounds_mask = match ctrl_typevar {
1902 types::I8 => u8::MAX.into(),
1903 types::I16 => u16::MAX.into(),
1904 types::I32 => u32::MAX.into(),
1905 types::I64 => u64::MAX,
1906 _ => unreachable!(),
1907 };
1908
1909 let value = imm.bits() as u64;
1910 if value & bounds_mask != value {
1911 errors.fatal((
1912 inst,
1913 self.context(inst),
1914 "constant immediate is out of bounds",
1915 ))
1916 } else {
1917 Ok(())
1918 }
1919 } else {
1920 Ok(())
1921 }
1922 }
1923
1924 fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1925 let params = self
1926 .func
1927 .signature
1928 .params
1929 .iter()
1930 .enumerate()
1931 .map(|p| (true, p));
1932 let returns = self
1933 .func
1934 .signature
1935 .returns
1936 .iter()
1937 .enumerate()
1938 .map(|p| (false, p));
1939
1940 for (is_argument, (i, param)) in params.chain(returns) {
1941 let is_return = !is_argument;
1942 let item = if is_argument {
1943 "Parameter"
1944 } else {
1945 "Return value"
1946 };
1947
1948 if param.value_type == types::INVALID {
1949 errors.report((
1950 AnyEntity::Function,
1951 format!("{item} at position {i} has an invalid type"),
1952 ));
1953 }
1954
1955 if let ArgumentPurpose::StructArgument(_) = param.purpose {
1956 if is_return {
1957 errors.report((
1958 AnyEntity::Function,
1959 format!("{item} at position {i} can't be an struct argument"),
1960 ))
1961 }
1962 }
1963
1964 let ty_allows_extension = param.value_type.is_int();
1965 let has_extension = param.extension != ArgumentExtension::None;
1966 if !ty_allows_extension && has_extension {
1967 errors.report((
1968 AnyEntity::Function,
1969 format!(
1970 "{} at position {} has invalid extension {:?}",
1971 item, i, param.extension
1972 ),
1973 ));
1974 }
1975 }
1976
1977 if errors.has_error() { Err(()) } else { Ok(()) }
1978 }
1979
1980 fn verify_try_call_handler_index(
1981 &self,
1982 inst: Inst,
1983 block: Block,
1984 index_imm: i64,
1985 errors: &mut VerifierErrors,
1986 ) -> VerifierStepResult {
1987 if index_imm < 0 {
1988 return errors.fatal((
1989 inst,
1990 format!("exception handler index {index_imm} cannot be negative"),
1991 ));
1992 }
1993 let Ok(index) = usize::try_from(index_imm) else {
1994 return errors.fatal((
1995 inst,
1996 format!("exception handler index {index_imm} is out-of-range"),
1997 ));
1998 };
1999 let Some(terminator) = self.func.layout.last_inst(block) else {
2000 return errors.fatal((
2001 inst,
2002 format!("referenced block {block} does not have a terminator"),
2003 ));
2004 };
2005 let Some(et) = self.func.dfg.insts[terminator].exception_table() else {
2006 return errors.fatal((
2007 inst,
2008 format!("referenced block {block} does not end in a try_call"),
2009 ));
2010 };
2011
2012 let etd = &self.func.dfg.exception_tables[et];
2013 let num_exceptional_edges = etd.all_branches().len() - 1;
2018 if index >= num_exceptional_edges {
2019 return errors.fatal((
2020 inst,
2021 format!("exception handler index {index_imm} is out-of-range"),
2022 ));
2023 }
2024
2025 Ok(())
2026 }
2027
2028 pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
2029 self.verify_global_values(errors)?;
2030 self.verify_memory_types(errors)?;
2031 self.typecheck_entry_block_params(errors)?;
2032 self.check_entry_not_cold(errors)?;
2033 self.typecheck_function_signature(errors)?;
2034
2035 for block in self.func.layout.blocks() {
2036 if self.func.layout.first_inst(block).is_none() {
2037 return errors.fatal((block, format!("{block} cannot be empty")));
2038 }
2039 for inst in self.func.layout.block_insts(block) {
2040 crate::trace!("verifying {inst:?}: {}", self.func.dfg.display_inst(inst));
2041 self.block_integrity(block, inst, errors)?;
2042 self.instruction_integrity(inst, errors)?;
2043 self.typecheck(inst, errors)?;
2044 self.immediate_constraints(inst, errors)?;
2045 self.iconst_bounds(inst, errors)?;
2046 }
2047
2048 self.encodable_as_bb(block, errors)?;
2049 }
2050
2051 if !errors.is_empty() {
2052 log::warn!(
2053 "Found verifier errors in function:\n{}",
2054 pretty_verifier_error(self.func, None, errors.clone())
2055 );
2056 }
2057
2058 Ok(())
2059 }
2060}
2061
2062#[cfg(test)]
2063mod tests {
2064 use super::{Verifier, VerifierError, VerifierErrors};
2065 use crate::ir::instructions::{InstructionData, Opcode};
2066 use crate::ir::{AbiParam, Function, Type, types};
2067 use crate::settings;
2068
2069 macro_rules! assert_err_with_msg {
2070 ($e:expr, $msg:expr) => {
2071 match $e.0.get(0) {
2072 None => panic!("Expected an error"),
2073 Some(&VerifierError { ref message, .. }) => {
2074 if !message.contains($msg) {
2075 #[cfg(feature = "std")]
2076 panic!("'{}' did not contain the substring '{}'", message, $msg);
2077 #[cfg(not(feature = "std"))]
2078 panic!("error message did not contain the expected substring");
2079 }
2080 }
2081 }
2082 };
2083 }
2084
2085 #[test]
2086 fn empty() {
2087 let func = Function::new();
2088 let flags = &settings::Flags::new(settings::builder());
2089 let verifier = Verifier::new(&func, flags.into());
2090 let mut errors = VerifierErrors::default();
2091
2092 assert_eq!(verifier.run(&mut errors), Ok(()));
2093 assert!(errors.0.is_empty());
2094 }
2095
2096 #[test]
2097 fn bad_instruction_format() {
2098 let mut func = Function::new();
2099 let block0 = func.dfg.make_block();
2100 func.layout.append_block(block0);
2101 let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {
2102 opcode: Opcode::F32const,
2103 imm: 0.into(),
2104 });
2105 func.layout.append_inst(nullary_with_bad_opcode, block0);
2106 let destination = func.dfg.block_call(block0, &[]);
2107 func.stencil.layout.append_inst(
2108 func.stencil.dfg.make_inst(InstructionData::Jump {
2109 opcode: Opcode::Jump,
2110 destination,
2111 }),
2112 block0,
2113 );
2114 let flags = &settings::Flags::new(settings::builder());
2115 let verifier = Verifier::new(&func, flags.into());
2116 let mut errors = VerifierErrors::default();
2117
2118 let _ = verifier.run(&mut errors);
2119
2120 assert_err_with_msg!(errors, "instruction format");
2121 }
2122
2123 fn test_iconst_bounds(immediate: i64, ctrl_typevar: Type) -> VerifierErrors {
2124 let mut func = Function::new();
2125 let block0 = func.dfg.make_block();
2126 func.layout.append_block(block0);
2127
2128 let test_inst = func.dfg.make_inst(InstructionData::UnaryImm {
2129 opcode: Opcode::Iconst,
2130 imm: immediate.into(),
2131 });
2132
2133 let end_inst = func.dfg.make_inst(InstructionData::MultiAry {
2134 opcode: Opcode::Return,
2135 args: Default::default(),
2136 });
2137
2138 func.dfg.make_inst_results(test_inst, ctrl_typevar);
2139 func.layout.append_inst(test_inst, block0);
2140 func.layout.append_inst(end_inst, block0);
2141
2142 let flags = &settings::Flags::new(settings::builder());
2143 let verifier = Verifier::new(&func, flags.into());
2144 let mut errors = VerifierErrors::default();
2145
2146 let _ = verifier.run(&mut errors);
2147 errors
2148 }
2149
2150 fn test_iconst_bounds_err(immediate: i64, ctrl_typevar: Type) {
2151 assert_err_with_msg!(
2152 test_iconst_bounds(immediate, ctrl_typevar),
2153 "constant immediate is out of bounds"
2154 );
2155 }
2156
2157 fn test_iconst_bounds_ok(immediate: i64, ctrl_typevar: Type) {
2158 assert!(test_iconst_bounds(immediate, ctrl_typevar).is_empty());
2159 }
2160
2161 #[test]
2162 fn negative_iconst_8() {
2163 test_iconst_bounds_err(-10, types::I8);
2164 }
2165
2166 #[test]
2167 fn negative_iconst_32() {
2168 test_iconst_bounds_err(-1, types::I32);
2169 }
2170
2171 #[test]
2172 fn large_iconst_8() {
2173 test_iconst_bounds_err(1 + u8::MAX as i64, types::I8);
2174 }
2175
2176 #[test]
2177 fn large_iconst_16() {
2178 test_iconst_bounds_err(10 + u16::MAX as i64, types::I16);
2179 }
2180
2181 #[test]
2182 fn valid_iconst_8() {
2183 test_iconst_bounds_ok(10, types::I8);
2184 }
2185
2186 #[test]
2187 fn valid_iconst_32() {
2188 test_iconst_bounds_ok(u32::MAX as i64, types::I32);
2189 }
2190
2191 #[test]
2192 fn test_function_invalid_param() {
2193 let mut func = Function::new();
2194 func.signature.params.push(AbiParam::new(types::INVALID));
2195
2196 let mut errors = VerifierErrors::default();
2197 let flags = &settings::Flags::new(settings::builder());
2198 let verifier = Verifier::new(&func, flags.into());
2199
2200 let _ = verifier.typecheck_function_signature(&mut errors);
2201 assert_err_with_msg!(errors, "Parameter at position 0 has an invalid type");
2202 }
2203
2204 #[test]
2205 fn test_function_invalid_return_value() {
2206 let mut func = Function::new();
2207 func.signature.returns.push(AbiParam::new(types::INVALID));
2208
2209 let mut errors = VerifierErrors::default();
2210 let flags = &settings::Flags::new(settings::builder());
2211 let verifier = Verifier::new(&func, flags.into());
2212
2213 let _ = verifier.typecheck_function_signature(&mut errors);
2214 assert_err_with_msg!(errors, "Return value at position 0 has an invalid type");
2215 }
2216
2217 #[test]
2218 fn test_printing_contextual_errors() {
2219 let mut func = Function::new();
2221 let block0 = func.dfg.make_block();
2222 func.layout.append_block(block0);
2223
2224 let inst = func.dfg.make_inst(InstructionData::UnaryIeee64 {
2226 opcode: Opcode::F64const,
2227 imm: 0.0.into(),
2228 });
2229 func.layout.append_inst(inst, block0);
2230
2231 let mut errors = VerifierErrors::default();
2233 let flags = &settings::Flags::new(settings::builder());
2234 let verifier = Verifier::new(&func, flags.into());
2235
2236 let _ = verifier.typecheck_results(inst, types::I32, &mut errors);
2239 assert_eq!(
2240 format!("{}", errors.0[0]),
2241 "inst0 (f64const 0.0): has fewer result values than expected"
2242 )
2243 }
2244
2245 #[test]
2246 fn test_empty_block() {
2247 let mut func = Function::new();
2248 let block0 = func.dfg.make_block();
2249 func.layout.append_block(block0);
2250
2251 let flags = &settings::Flags::new(settings::builder());
2252 let verifier = Verifier::new(&func, flags.into());
2253 let mut errors = VerifierErrors::default();
2254 let _ = verifier.run(&mut errors);
2255
2256 assert_err_with_msg!(errors, "block0 cannot be empty");
2257 }
2258}