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