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