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