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