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