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