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};
73use crate::ir::{
74 types, ArgumentPurpose, Block, Constant, DynamicStackSlot, FuncRef, Function, GlobalValue,
75 Inst, JumpTable, MemFlags, MemoryTypeData, Opcode, SigRef, StackSlot, Type, Value, ValueDef,
76 ValueList,
77};
78use crate::isa::TargetIsa;
79use crate::print_errors::pretty_verifier_error;
80use crate::settings::FlagsOrIsa;
81use crate::timing;
82use alloc::collections::BTreeSet;
83use alloc::string::{String, ToString};
84use alloc::vec::Vec;
85use core::fmt::{self, Display, Formatter};
86
87#[derive(Debug, PartialEq, Eq, Clone)]
89pub struct VerifierError {
90 pub location: AnyEntity,
92 pub context: Option<String>,
95 pub message: String,
97}
98
99impl std::error::Error for VerifierError {}
102
103impl Display for VerifierError {
104 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
105 match &self.context {
106 None => write!(f, "{}: {}", self.location, self.message),
107 Some(context) => write!(f, "{} ({}): {}", self.location, context, self.message),
108 }
109 }
110}
111
112impl<L, C, M> From<(L, C, M)> for VerifierError
123where
124 L: Into<AnyEntity>,
125 C: Into<String>,
126 M: Into<String>,
127{
128 fn from(items: (L, C, M)) -> Self {
129 let (location, context, message) = items;
130 Self {
131 location: location.into(),
132 context: Some(context.into()),
133 message: message.into(),
134 }
135 }
136}
137
138impl<L, M> From<(L, M)> for VerifierError
142where
143 L: Into<AnyEntity>,
144 M: Into<String>,
145{
146 fn from(items: (L, M)) -> Self {
147 let (location, message) = items;
148 Self {
149 location: location.into(),
150 context: None,
151 message: message.into(),
152 }
153 }
154}
155
156pub type VerifierStepResult = Result<(), ()>;
167
168pub type VerifierResult<T> = Result<T, VerifierErrors>;
173
174#[derive(Debug, Default, PartialEq, Eq, Clone)]
176pub struct VerifierErrors(pub Vec<VerifierError>);
177
178impl std::error::Error for VerifierErrors {}
181
182impl VerifierErrors {
183 #[inline]
185 pub fn new() -> Self {
186 Self(Vec::new())
187 }
188
189 #[inline]
191 pub fn is_empty(&self) -> bool {
192 self.0.is_empty()
193 }
194
195 #[inline]
197 pub fn has_error(&self) -> bool {
198 !self.0.is_empty()
199 }
200
201 #[inline]
204 pub fn as_result(&self) -> VerifierStepResult {
205 if self.is_empty() {
206 Ok(())
207 } else {
208 Err(())
209 }
210 }
211
212 pub fn report(&mut self, error: impl Into<VerifierError>) {
214 self.0.push(error.into());
215 }
216
217 pub fn fatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {
219 self.report(error);
220 Err(())
221 }
222
223 pub fn nonfatal(&mut self, error: impl Into<VerifierError>) -> VerifierStepResult {
225 self.report(error);
226 Ok(())
227 }
228}
229
230impl From<Vec<VerifierError>> for VerifierErrors {
231 fn from(v: Vec<VerifierError>) -> Self {
232 Self(v)
233 }
234}
235
236impl Into<Vec<VerifierError>> for VerifierErrors {
237 fn into(self) -> Vec<VerifierError> {
238 self.0
239 }
240}
241
242impl Into<VerifierResult<()>> for VerifierErrors {
243 fn into(self) -> VerifierResult<()> {
244 if self.is_empty() {
245 Ok(())
246 } else {
247 Err(self)
248 }
249 }
250}
251
252impl Display for VerifierErrors {
253 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
254 for err in &self.0 {
255 writeln!(f, "- {err}")?;
256 }
257 Ok(())
258 }
259}
260
261pub fn verify_function<'a, FOI: Into<FlagsOrIsa<'a>>>(
263 func: &Function,
264 fisa: FOI,
265) -> VerifierResult<()> {
266 let _tt = timing::verifier();
267 let mut errors = VerifierErrors::default();
268 let verifier = Verifier::new(func, fisa.into());
269 let result = verifier.run(&mut errors);
270 if errors.is_empty() {
271 result.unwrap();
272 Ok(())
273 } else {
274 Err(errors)
275 }
276}
277
278pub fn verify_context<'a, FOI: Into<FlagsOrIsa<'a>>>(
281 func: &Function,
282 cfg: &ControlFlowGraph,
283 domtree: &DominatorTree,
284 fisa: FOI,
285 errors: &mut VerifierErrors,
286) -> VerifierStepResult {
287 let _tt = timing::verifier();
288 let verifier = Verifier::new(func, fisa.into());
289 if cfg.is_valid() {
290 verifier.cfg_integrity(cfg, errors)?;
291 }
292 if domtree.is_valid() {
293 verifier.domtree_integrity(domtree, errors)?;
294 }
295 verifier.run(errors)
296}
297
298struct Verifier<'a> {
299 func: &'a Function,
300 expected_cfg: ControlFlowGraph,
301 expected_domtree: DominatorTree,
302 isa: Option<&'a dyn TargetIsa>,
303}
304
305impl<'a> Verifier<'a> {
306 pub fn new(func: &'a Function, fisa: FlagsOrIsa<'a>) -> Self {
307 let expected_cfg = ControlFlowGraph::with_function(func);
308 let expected_domtree = DominatorTree::with_function(func, &expected_cfg);
309 Self {
310 func,
311 expected_cfg,
312 expected_domtree,
313 isa: fisa.isa,
314 }
315 }
316
317 #[inline]
319 fn context(&self, inst: Inst) -> String {
320 self.func.dfg.display_inst(inst).to_string()
321 }
322
323 fn verify_global_values(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
327 let mut cycle_seen = false;
328 let mut seen = SparseSet::new();
329
330 'gvs: for gv in self.func.global_values.keys() {
331 seen.clear();
332 seen.insert(gv);
333
334 let mut cur = gv;
335 loop {
336 match self.func.global_values[cur] {
337 ir::GlobalValueData::Load { base, .. }
338 | ir::GlobalValueData::IAddImm { base, .. } => {
339 if seen.insert(base).is_some() {
340 if !cycle_seen {
341 errors.report((
342 gv,
343 format!("global value cycle: {}", DisplayList(seen.as_slice())),
344 ));
345 cycle_seen = true;
347 }
348 continue 'gvs;
349 }
350
351 cur = base;
352 }
353 _ => break,
354 }
355 }
356
357 match self.func.global_values[gv] {
358 ir::GlobalValueData::VMContext { .. } => {
359 if self
360 .func
361 .special_param(ir::ArgumentPurpose::VMContext)
362 .is_none()
363 {
364 errors.report((gv, format!("undeclared vmctx reference {gv}")));
365 }
366 }
367 ir::GlobalValueData::IAddImm {
368 base, global_type, ..
369 } => {
370 if !global_type.is_int() {
371 errors.report((
372 gv,
373 format!("iadd_imm global value with non-int type {global_type}"),
374 ));
375 } else if let Some(isa) = self.isa {
376 let base_type = self.func.global_values[base].global_type(isa);
377 if global_type != base_type {
378 errors.report((
379 gv,
380 format!(
381 "iadd_imm type {global_type} differs from operand type {base_type}"
382 ),
383 ));
384 }
385 }
386 }
387 ir::GlobalValueData::Load { base, .. } => {
388 if let Some(isa) = self.isa {
389 let base_type = self.func.global_values[base].global_type(isa);
390 let pointer_type = isa.pointer_type();
391 if base_type != pointer_type {
392 errors.report((
393 gv,
394 format!(
395 "base {base} has type {base_type}, which is not the pointer type {pointer_type}"
396 ),
397 ));
398 }
399 }
400 }
401 _ => {}
402 }
403 }
404
405 Ok(())
407 }
408
409 fn verify_memory_types(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
410 for (mt, mt_data) in &self.func.memory_types {
413 match mt_data {
414 MemoryTypeData::Struct { size, fields } => {
415 let mut last_offset = 0;
416 for field in fields {
417 if field.offset < last_offset {
418 errors.report((
419 mt,
420 format!(
421 "memory type {} has a field at offset {}, which is out-of-order",
422 mt, field.offset
423 ),
424 ));
425 }
426 last_offset = match field.offset.checked_add(u64::from(field.ty.bytes())) {
427 Some(o) => o,
428 None => {
429 errors.report((
430 mt,
431 format!(
432 "memory type {} has a field at offset {} of size {}; offset plus size overflows a u64",
433 mt, field.offset, field.ty.bytes()),
434 ));
435 break;
436 }
437 };
438
439 if last_offset > *size {
440 errors.report((
441 mt,
442 format!(
443 "memory type {} has a field at offset {} of size {} that overflows the struct size {}",
444 mt, field.offset, field.ty.bytes(), *size),
445 ));
446 }
447 }
448 }
449 _ => {}
450 }
451 }
452
453 Ok(())
454 }
455
456 fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult {
459 match self.func.is_block_basic(block) {
460 Ok(()) => Ok(()),
461 Err((inst, message)) => errors.fatal((inst, self.context(inst), message)),
462 }
463 }
464
465 fn block_integrity(
466 &self,
467 block: Block,
468 inst: Inst,
469 errors: &mut VerifierErrors,
470 ) -> VerifierStepResult {
471 let is_terminator = self.func.dfg.insts[inst].opcode().is_terminator();
472 let is_last_inst = self.func.layout.last_inst(block) == Some(inst);
473
474 if is_terminator && !is_last_inst {
475 return errors.fatal((
477 inst,
478 self.context(inst),
479 format!("a terminator instruction was encountered before the end of {block}"),
480 ));
481 }
482 if is_last_inst && !is_terminator {
483 return errors.fatal((block, "block does not end in a terminator instruction"));
484 }
485
486 let inst_block = self.func.layout.inst_block(inst);
488 if inst_block != Some(block) {
489 return errors.fatal((
490 inst,
491 self.context(inst),
492 format!("should belong to {block} not {inst_block:?}"),
493 ));
494 }
495
496 for &arg in self.func.dfg.block_params(block) {
498 match self.func.dfg.value_def(arg) {
499 ValueDef::Param(arg_block, _) => {
500 if block != arg_block {
501 return errors.fatal((arg, format!("does not belong to {block}")));
502 }
503 }
504 _ => {
505 return errors.fatal((arg, "expected an argument, found a result"));
506 }
507 }
508 }
509
510 Ok(())
511 }
512
513 fn instruction_integrity(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
514 let inst_data = &self.func.dfg.insts[inst];
515 let dfg = &self.func.dfg;
516
517 if inst_data.opcode().format() != InstructionFormat::from(inst_data) {
519 return errors.fatal((
520 inst,
521 self.context(inst),
522 "instruction opcode doesn't match instruction format",
523 ));
524 }
525
526 let expected_num_results = dfg.num_expected_results_for_verifier(inst);
527
528 let got_results = dfg.inst_results(inst).len();
530 if got_results != expected_num_results {
531 return errors.fatal((
532 inst,
533 self.context(inst),
534 format!("expected {expected_num_results} result values, found {got_results}"),
535 ));
536 }
537
538 self.verify_entity_references(inst, errors)
539 }
540
541 fn verify_entity_references(
542 &self,
543 inst: Inst,
544 errors: &mut VerifierErrors,
545 ) -> VerifierStepResult {
546 use crate::ir::instructions::InstructionData::*;
547
548 for arg in self.func.dfg.inst_values(inst) {
549 self.verify_inst_arg(inst, arg, errors)?;
550
551 let original = self.func.dfg.resolve_aliases(arg);
553 if !self.func.dfg.value_is_attached(original) {
554 errors.report((
555 inst,
556 self.context(inst),
557 format!("argument {arg} -> {original} is not attached"),
558 ));
559 }
560 }
561
562 for &res in self.func.dfg.inst_results(inst) {
563 self.verify_inst_result(inst, res, errors)?;
564 }
565
566 match self.func.dfg.insts[inst] {
567 MultiAry { ref args, .. } => {
568 self.verify_value_list(inst, args, errors)?;
569 }
570 Jump { destination, .. } => {
571 self.verify_block(inst, destination.block(&self.func.dfg.value_lists), errors)?;
572 }
573 Brif {
574 arg,
575 blocks: [block_then, block_else],
576 ..
577 } => {
578 self.verify_value(inst, arg, errors)?;
579 self.verify_block(inst, block_then.block(&self.func.dfg.value_lists), errors)?;
580 self.verify_block(inst, block_else.block(&self.func.dfg.value_lists), errors)?;
581 }
582 BranchTable { table, .. } => {
583 self.verify_jump_table(inst, table, errors)?;
584 }
585 Call {
586 func_ref, ref args, ..
587 } => {
588 self.verify_func_ref(inst, func_ref, errors)?;
589 self.verify_value_list(inst, args, errors)?;
590 }
591 CallIndirect {
592 sig_ref, ref args, ..
593 } => {
594 self.verify_sig_ref(inst, sig_ref, errors)?;
595 self.verify_value_list(inst, args, errors)?;
596 }
597 FuncAddr { func_ref, .. } => {
598 self.verify_func_ref(inst, func_ref, errors)?;
599 }
600 StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => {
601 self.verify_stack_slot(inst, stack_slot, errors)?;
602 }
603 DynamicStackLoad {
604 dynamic_stack_slot, ..
605 }
606 | DynamicStackStore {
607 dynamic_stack_slot, ..
608 } => {
609 self.verify_dynamic_stack_slot(inst, dynamic_stack_slot, errors)?;
610 }
611 UnaryGlobalValue { global_value, .. } => {
612 self.verify_global_value(inst, global_value, errors)?;
613 }
614 NullAry {
615 opcode: Opcode::GetPinnedReg,
616 }
617 | Unary {
618 opcode: Opcode::SetPinnedReg,
619 ..
620 } => {
621 if let Some(isa) = &self.isa {
622 if !isa.flags().enable_pinned_reg() {
623 return errors.fatal((
624 inst,
625 self.context(inst),
626 "GetPinnedReg/SetPinnedReg cannot be used without enable_pinned_reg",
627 ));
628 }
629 } else {
630 return errors.fatal((
631 inst,
632 self.context(inst),
633 "GetPinnedReg/SetPinnedReg need an ISA!",
634 ));
635 }
636 }
637 NullAry {
638 opcode: Opcode::GetFramePointer | Opcode::GetReturnAddress,
639 } => {
640 if let Some(isa) = &self.isa {
641 if !isa.flags().preserve_frame_pointers() {
644 return errors.fatal((
645 inst,
646 self.context(inst),
647 "`get_frame_pointer`/`get_return_address` cannot be used without \
648 enabling `preserve_frame_pointers`",
649 ));
650 }
651 } else {
652 return errors.fatal((
653 inst,
654 self.context(inst),
655 "`get_frame_pointer`/`get_return_address` require an ISA!",
656 ));
657 }
658 }
659 LoadNoOffset {
660 opcode: Opcode::Bitcast,
661 flags,
662 arg,
663 } => {
664 self.verify_bitcast(inst, flags, arg, errors)?;
665 }
666 UnaryConst {
667 opcode: opcode @ (Opcode::Vconst | Opcode::F128const),
668 constant_handle,
669 ..
670 } => {
671 self.verify_constant_size(inst, opcode, constant_handle, errors)?;
672 }
673
674 AtomicCas { .. }
676 | AtomicRmw { .. }
677 | LoadNoOffset { .. }
678 | StoreNoOffset { .. }
679 | Unary { .. }
680 | UnaryConst { .. }
681 | UnaryImm { .. }
682 | UnaryIeee16 { .. }
683 | UnaryIeee32 { .. }
684 | UnaryIeee64 { .. }
685 | Binary { .. }
686 | BinaryImm8 { .. }
687 | BinaryImm64 { .. }
688 | Ternary { .. }
689 | TernaryImm8 { .. }
690 | Shuffle { .. }
691 | IntAddTrap { .. }
692 | IntCompare { .. }
693 | IntCompareImm { .. }
694 | FloatCompare { .. }
695 | Load { .. }
696 | Store { .. }
697 | Trap { .. }
698 | CondTrap { .. }
699 | NullAry { .. } => {}
700 }
701
702 Ok(())
703 }
704
705 fn verify_block(
706 &self,
707 loc: impl Into<AnyEntity>,
708 e: Block,
709 errors: &mut VerifierErrors,
710 ) -> VerifierStepResult {
711 if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) {
712 return errors.fatal((loc, format!("invalid block reference {e}")));
713 }
714 if let Some(entry_block) = self.func.layout.entry_block() {
715 if e == entry_block {
716 return errors.fatal((loc, format!("invalid reference to entry block {e}")));
717 }
718 }
719 Ok(())
720 }
721
722 fn verify_sig_ref(
723 &self,
724 inst: Inst,
725 s: SigRef,
726 errors: &mut VerifierErrors,
727 ) -> VerifierStepResult {
728 if !self.func.dfg.signatures.is_valid(s) {
729 errors.fatal((
730 inst,
731 self.context(inst),
732 format!("invalid signature reference {s}"),
733 ))
734 } else {
735 Ok(())
736 }
737 }
738
739 fn verify_func_ref(
740 &self,
741 inst: Inst,
742 f: FuncRef,
743 errors: &mut VerifierErrors,
744 ) -> VerifierStepResult {
745 if !self.func.dfg.ext_funcs.is_valid(f) {
746 errors.nonfatal((
747 inst,
748 self.context(inst),
749 format!("invalid function reference {f}"),
750 ))
751 } else {
752 Ok(())
753 }
754 }
755
756 fn verify_stack_slot(
757 &self,
758 inst: Inst,
759 ss: StackSlot,
760 errors: &mut VerifierErrors,
761 ) -> VerifierStepResult {
762 if !self.func.sized_stack_slots.is_valid(ss) {
763 errors.nonfatal((inst, self.context(inst), format!("invalid stack slot {ss}")))
764 } else {
765 Ok(())
766 }
767 }
768
769 fn verify_dynamic_stack_slot(
770 &self,
771 inst: Inst,
772 ss: DynamicStackSlot,
773 errors: &mut VerifierErrors,
774 ) -> VerifierStepResult {
775 if !self.func.dynamic_stack_slots.is_valid(ss) {
776 errors.nonfatal((
777 inst,
778 self.context(inst),
779 format!("invalid dynamic stack slot {ss}"),
780 ))
781 } else {
782 Ok(())
783 }
784 }
785
786 fn verify_global_value(
787 &self,
788 inst: Inst,
789 gv: GlobalValue,
790 errors: &mut VerifierErrors,
791 ) -> VerifierStepResult {
792 if !self.func.global_values.is_valid(gv) {
793 errors.nonfatal((
794 inst,
795 self.context(inst),
796 format!("invalid global value {gv}"),
797 ))
798 } else {
799 Ok(())
800 }
801 }
802
803 fn verify_value_list(
804 &self,
805 inst: Inst,
806 l: &ValueList,
807 errors: &mut VerifierErrors,
808 ) -> VerifierStepResult {
809 if !l.is_valid(&self.func.dfg.value_lists) {
810 errors.nonfatal((
811 inst,
812 self.context(inst),
813 format!("invalid value list reference {l:?}"),
814 ))
815 } else {
816 Ok(())
817 }
818 }
819
820 fn verify_jump_table(
821 &self,
822 inst: Inst,
823 j: JumpTable,
824 errors: &mut VerifierErrors,
825 ) -> VerifierStepResult {
826 if !self.func.stencil.dfg.jump_tables.is_valid(j) {
827 errors.nonfatal((
828 inst,
829 self.context(inst),
830 format!("invalid jump table reference {j}"),
831 ))
832 } else {
833 let pool = &self.func.stencil.dfg.value_lists;
834 for block in self.func.stencil.dfg.jump_tables[j].all_branches() {
835 self.verify_block(inst, block.block(pool), errors)?;
836 }
837 Ok(())
838 }
839 }
840
841 fn verify_value(
842 &self,
843 loc_inst: Inst,
844 v: Value,
845 errors: &mut VerifierErrors,
846 ) -> VerifierStepResult {
847 let dfg = &self.func.dfg;
848 if !dfg.value_is_valid(v) {
849 errors.nonfatal((
850 loc_inst,
851 self.context(loc_inst),
852 format!("invalid value reference {v}"),
853 ))
854 } else {
855 Ok(())
856 }
857 }
858
859 fn verify_inst_arg(
860 &self,
861 loc_inst: Inst,
862 v: Value,
863 errors: &mut VerifierErrors,
864 ) -> VerifierStepResult {
865 self.verify_value(loc_inst, v, errors)?;
866
867 let dfg = &self.func.dfg;
868 let loc_block = self
869 .func
870 .layout
871 .inst_block(loc_inst)
872 .expect("Instruction not in layout.");
873 let is_reachable = self.expected_domtree.is_reachable(loc_block);
874
875 match dfg.value_def(v) {
877 ValueDef::Result(def_inst, _) => {
878 if !dfg.inst_is_valid(def_inst) {
880 return errors.fatal((
881 loc_inst,
882 self.context(loc_inst),
883 format!("{v} is defined by invalid instruction {def_inst}"),
884 ));
885 }
886 if self.func.layout.inst_block(def_inst) == None {
888 return errors.fatal((
889 loc_inst,
890 self.context(loc_inst),
891 format!("{v} is defined by {def_inst} which has no block"),
892 ));
893 }
894 if is_reachable {
896 if !self
897 .expected_domtree
898 .dominates(def_inst, loc_inst, &self.func.layout)
899 {
900 return errors.fatal((
901 loc_inst,
902 self.context(loc_inst),
903 format!("uses value {v} from non-dominating {def_inst}"),
904 ));
905 }
906 if def_inst == loc_inst {
907 return errors.fatal((
908 loc_inst,
909 self.context(loc_inst),
910 format!("uses value {v} from itself"),
911 ));
912 }
913 }
914 }
915 ValueDef::Param(block, _) => {
916 if !dfg.block_is_valid(block) {
918 return errors.fatal((
919 loc_inst,
920 self.context(loc_inst),
921 format!("{v} is defined by invalid block {block}"),
922 ));
923 }
924 if !self.func.layout.is_block_inserted(block) {
926 return errors.fatal((
927 loc_inst,
928 self.context(loc_inst),
929 format!("{v} is defined by {block} which is not in the layout"),
930 ));
931 }
932 if is_reachable
934 && !self
935 .expected_domtree
936 .dominates(block, loc_inst, &self.func.layout)
937 {
938 return errors.fatal((
939 loc_inst,
940 self.context(loc_inst),
941 format!("uses value arg from non-dominating {block}"),
942 ));
943 }
944 }
945 ValueDef::Union(_, _) => {
946 }
949 }
950 Ok(())
951 }
952
953 fn verify_inst_result(
954 &self,
955 loc_inst: Inst,
956 v: Value,
957 errors: &mut VerifierErrors,
958 ) -> VerifierStepResult {
959 self.verify_value(loc_inst, v, errors)?;
960
961 match self.func.dfg.value_def(v) {
962 ValueDef::Result(def_inst, _) => {
963 if def_inst != loc_inst {
964 errors.fatal((
965 loc_inst,
966 self.context(loc_inst),
967 format!("instruction result {v} is not defined by the instruction"),
968 ))
969 } else {
970 Ok(())
971 }
972 }
973 ValueDef::Param(_, _) => errors.fatal((
974 loc_inst,
975 self.context(loc_inst),
976 format!("instruction result {v} is not defined by the instruction"),
977 )),
978 ValueDef::Union(_, _) => errors.fatal((
979 loc_inst,
980 self.context(loc_inst),
981 format!("instruction result {v} is a union node"),
982 )),
983 }
984 }
985
986 fn verify_bitcast(
987 &self,
988 inst: Inst,
989 flags: MemFlags,
990 arg: Value,
991 errors: &mut VerifierErrors,
992 ) -> VerifierStepResult {
993 let typ = self.func.dfg.ctrl_typevar(inst);
994 let value_type = self.func.dfg.value_type(arg);
995
996 if typ.bits() != value_type.bits() {
997 errors.fatal((
998 inst,
999 format!(
1000 "The bitcast argument {} has a type of {} bits, which doesn't match an expected type of {} bits",
1001 arg,
1002 value_type.bits(),
1003 typ.bits()
1004 ),
1005 ))
1006 } else if flags != MemFlags::new()
1007 && flags != MemFlags::new().with_endianness(ir::Endianness::Little)
1008 && flags != MemFlags::new().with_endianness(ir::Endianness::Big)
1009 {
1010 errors.fatal((
1011 inst,
1012 "The bitcast instruction only accepts the `big` or `little` memory flags",
1013 ))
1014 } else if flags == MemFlags::new() && typ.lane_count() != value_type.lane_count() {
1015 errors.fatal((
1016 inst,
1017 "Byte order specifier required for bitcast instruction changing lane count",
1018 ))
1019 } else {
1020 Ok(())
1021 }
1022 }
1023
1024 fn verify_constant_size(
1025 &self,
1026 inst: Inst,
1027 opcode: Opcode,
1028 constant: Constant,
1029 errors: &mut VerifierErrors,
1030 ) -> VerifierStepResult {
1031 let type_size = match opcode {
1032 Opcode::F128const => types::F128.bytes(),
1033 Opcode::Vconst => self.func.dfg.ctrl_typevar(inst).bytes(),
1034 _ => unreachable!("unexpected opcode {opcode:?}"),
1035 } as usize;
1036 let constant_size = self.func.dfg.constants.get(constant).len();
1037 if type_size != constant_size {
1038 errors.fatal((
1039 inst,
1040 format!(
1041 "The instruction expects {constant} to have a size of {type_size} bytes but it has {constant_size}"
1042 ),
1043 ))
1044 } else {
1045 Ok(())
1046 }
1047 }
1048
1049 fn domtree_integrity(
1050 &self,
1051 domtree: &DominatorTree,
1052 errors: &mut VerifierErrors,
1053 ) -> VerifierStepResult {
1054 for block in self.func.layout.blocks() {
1058 let expected = self.expected_domtree.idom(block);
1059 let got = domtree.idom(block);
1060 if got != expected {
1061 return errors.fatal((
1062 block,
1063 format!("invalid domtree, expected idom({block}) = {expected:?}, got {got:?}"),
1064 ));
1065 }
1066 }
1067 if domtree.cfg_postorder().len() != self.expected_domtree.cfg_postorder().len() {
1069 return errors.fatal((
1070 AnyEntity::Function,
1071 "incorrect number of Blocks in postorder traversal",
1072 ));
1073 }
1074 for (index, (&test_block, &true_block)) in domtree
1075 .cfg_postorder()
1076 .iter()
1077 .zip(self.expected_domtree.cfg_postorder().iter())
1078 .enumerate()
1079 {
1080 if test_block != true_block {
1081 return errors.fatal((
1082 test_block,
1083 format!(
1084 "invalid domtree, postorder block number {index} should be {true_block}, got {test_block}"
1085 ),
1086 ));
1087 }
1088 }
1089 Ok(())
1090 }
1091
1092 fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1093 if let Some(block) = self.func.layout.entry_block() {
1094 let expected_types = &self.func.signature.params;
1095 let block_param_count = self.func.dfg.num_block_params(block);
1096
1097 if block_param_count != expected_types.len() {
1098 return errors.fatal((
1099 block,
1100 format!(
1101 "entry block parameters ({}) must match function signature ({})",
1102 block_param_count,
1103 expected_types.len()
1104 ),
1105 ));
1106 }
1107
1108 for (i, &arg) in self.func.dfg.block_params(block).iter().enumerate() {
1109 let arg_type = self.func.dfg.value_type(arg);
1110 if arg_type != expected_types[i].value_type {
1111 errors.report((
1112 block,
1113 format!(
1114 "entry block parameter {} expected to have type {}, got {}",
1115 i, expected_types[i], arg_type
1116 ),
1117 ));
1118 }
1119 }
1120 }
1121
1122 errors.as_result()
1123 }
1124
1125 fn check_entry_not_cold(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1126 if let Some(entry_block) = self.func.layout.entry_block() {
1127 if self.func.layout.is_cold(entry_block) {
1128 return errors
1129 .fatal((entry_block, format!("entry block cannot be marked as cold")));
1130 }
1131 }
1132 errors.as_result()
1133 }
1134
1135 fn typecheck(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1136 let inst_data = &self.func.dfg.insts[inst];
1137 let constraints = inst_data.opcode().constraints();
1138
1139 let ctrl_type = if let Some(value_typeset) = constraints.ctrl_typeset() {
1140 let ctrl_type = self.func.dfg.ctrl_typevar(inst);
1142
1143 if !value_typeset.contains(ctrl_type) {
1144 errors.report((
1145 inst,
1146 self.context(inst),
1147 format!(
1148 "has an invalid controlling type {ctrl_type} (allowed set is {value_typeset:?})"
1149 ),
1150 ));
1151 }
1152
1153 ctrl_type
1154 } else {
1155 types::INVALID
1158 };
1159
1160 let _ = self.typecheck_results(inst, ctrl_type, errors);
1162 let _ = self.typecheck_fixed_args(inst, ctrl_type, errors);
1163 let _ = self.typecheck_variable_args(inst, errors);
1164 let _ = self.typecheck_return(inst, errors);
1165 let _ = self.typecheck_special(inst, errors);
1166
1167 Ok(())
1168 }
1169
1170 fn typecheck_results(
1171 &self,
1172 inst: Inst,
1173 ctrl_type: Type,
1174 errors: &mut VerifierErrors,
1175 ) -> VerifierStepResult {
1176 let mut i = 0;
1177 for &result in self.func.dfg.inst_results(inst) {
1178 let result_type = self.func.dfg.value_type(result);
1179 let expected_type = self.func.dfg.compute_result_type(inst, i, ctrl_type);
1180 if let Some(expected_type) = expected_type {
1181 if result_type != expected_type {
1182 errors.report((
1183 inst,
1184 self.context(inst),
1185 format!(
1186 "expected result {i} ({result}) to have type {expected_type}, found {result_type}"
1187 ),
1188 ));
1189 }
1190 } else {
1191 return errors.nonfatal((
1192 inst,
1193 self.context(inst),
1194 "has more result values than expected",
1195 ));
1196 }
1197 i += 1;
1198 }
1199
1200 if self.func.dfg.compute_result_type(inst, i, ctrl_type) != None {
1202 return errors.nonfatal((
1203 inst,
1204 self.context(inst),
1205 "has fewer result values than expected",
1206 ));
1207 }
1208 Ok(())
1209 }
1210
1211 fn typecheck_fixed_args(
1212 &self,
1213 inst: Inst,
1214 ctrl_type: Type,
1215 errors: &mut VerifierErrors,
1216 ) -> VerifierStepResult {
1217 let constraints = self.func.dfg.insts[inst].opcode().constraints();
1218
1219 for (i, &arg) in self.func.dfg.inst_fixed_args(inst).iter().enumerate() {
1220 let arg_type = self.func.dfg.value_type(arg);
1221 match constraints.value_argument_constraint(i, ctrl_type) {
1222 ResolvedConstraint::Bound(expected_type) => {
1223 if arg_type != expected_type {
1224 errors.report((
1225 inst,
1226 self.context(inst),
1227 format!(
1228 "arg {i} ({arg}) has type {arg_type}, expected {expected_type}"
1229 ),
1230 ));
1231 }
1232 }
1233 ResolvedConstraint::Free(type_set) => {
1234 if !type_set.contains(arg_type) {
1235 errors.report((
1236 inst,
1237 self.context(inst),
1238 format!(
1239 "arg {i} ({arg}) with type {arg_type} failed to satisfy type set {type_set:?}"
1240 ),
1241 ));
1242 }
1243 }
1244 }
1245 }
1246 Ok(())
1247 }
1248
1249 fn typecheck_variable_args(
1252 &self,
1253 inst: Inst,
1254 errors: &mut VerifierErrors,
1255 ) -> VerifierStepResult {
1256 match &self.func.dfg.insts[inst] {
1257 ir::InstructionData::Jump { destination, .. } => {
1258 self.typecheck_block_call(inst, destination, errors)?;
1259 }
1260 ir::InstructionData::Brif {
1261 blocks: [block_then, block_else],
1262 ..
1263 } => {
1264 self.typecheck_block_call(inst, block_then, errors)?;
1265 self.typecheck_block_call(inst, block_else, errors)?;
1266 }
1267 ir::InstructionData::BranchTable { table, .. } => {
1268 for block in self.func.stencil.dfg.jump_tables[*table].all_branches() {
1269 self.typecheck_block_call(inst, block, errors)?;
1270 }
1271 }
1272 inst => debug_assert!(!inst.opcode().is_branch()),
1273 }
1274
1275 match self.func.dfg.insts[inst].analyze_call(&self.func.dfg.value_lists) {
1276 CallInfo::Direct(func_ref, args) => {
1277 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1278 let arg_types = self.func.dfg.signatures[sig_ref]
1279 .params
1280 .iter()
1281 .map(|a| a.value_type);
1282 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1283 }
1284 CallInfo::Indirect(sig_ref, args) => {
1285 let arg_types = self.func.dfg.signatures[sig_ref]
1286 .params
1287 .iter()
1288 .map(|a| a.value_type);
1289 self.typecheck_variable_args_iterator(inst, arg_types, args, errors)?;
1290 }
1291 CallInfo::NotACall => {}
1292 }
1293 Ok(())
1294 }
1295
1296 fn typecheck_block_call(
1297 &self,
1298 inst: Inst,
1299 block: &ir::BlockCall,
1300 errors: &mut VerifierErrors,
1301 ) -> VerifierStepResult {
1302 let pool = &self.func.dfg.value_lists;
1303 let iter = self
1304 .func
1305 .dfg
1306 .block_params(block.block(pool))
1307 .iter()
1308 .map(|&v| self.func.dfg.value_type(v));
1309 let args = block.args_slice(pool);
1310 self.typecheck_variable_args_iterator(inst, iter, args, errors)
1311 }
1312
1313 fn typecheck_variable_args_iterator<I: Iterator<Item = Type>>(
1314 &self,
1315 inst: Inst,
1316 iter: I,
1317 variable_args: &[Value],
1318 errors: &mut VerifierErrors,
1319 ) -> VerifierStepResult {
1320 let mut i = 0;
1321
1322 for expected_type in iter {
1323 if i >= variable_args.len() {
1324 i += 1;
1326 continue;
1327 }
1328 let arg = variable_args[i];
1329 let arg_type = self.func.dfg.value_type(arg);
1330 if expected_type != arg_type {
1331 errors.report((
1332 inst,
1333 self.context(inst),
1334 format!(
1335 "arg {} ({}) has type {}, expected {}",
1336 i, variable_args[i], arg_type, expected_type
1337 ),
1338 ));
1339 }
1340 i += 1;
1341 }
1342 if i != variable_args.len() {
1343 return errors.nonfatal((
1344 inst,
1345 self.context(inst),
1346 format!(
1347 "mismatched argument count for `{}`: got {}, expected {}",
1348 self.func.dfg.display_inst(inst),
1349 variable_args.len(),
1350 i,
1351 ),
1352 ));
1353 }
1354 Ok(())
1355 }
1356
1357 fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1358 match self.func.dfg.insts[inst] {
1359 ir::InstructionData::MultiAry {
1360 opcode: Opcode::Return,
1361 args,
1362 } => {
1363 let types = args
1364 .as_slice(&self.func.dfg.value_lists)
1365 .iter()
1366 .map(|v| self.func.dfg.value_type(*v));
1367 self.typecheck_return_types(
1368 inst,
1369 types,
1370 errors,
1371 "arguments of return must match function signature",
1372 )?;
1373 }
1374 ir::InstructionData::Call {
1375 opcode: Opcode::ReturnCall,
1376 func_ref,
1377 ..
1378 } => {
1379 let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
1380 self.typecheck_tail_call(inst, sig_ref, errors)?;
1381 }
1382 ir::InstructionData::CallIndirect {
1383 opcode: Opcode::ReturnCallIndirect,
1384 sig_ref,
1385 ..
1386 } => {
1387 self.typecheck_tail_call(inst, sig_ref, errors)?;
1388 }
1389 inst => debug_assert!(!inst.opcode().is_return()),
1390 }
1391 Ok(())
1392 }
1393
1394 fn typecheck_tail_call(
1395 &self,
1396 inst: Inst,
1397 sig_ref: SigRef,
1398 errors: &mut VerifierErrors,
1399 ) -> VerifierStepResult {
1400 let signature = &self.func.dfg.signatures[sig_ref];
1401 let cc = signature.call_conv;
1402 if !cc.supports_tail_calls() {
1403 errors.report((
1404 inst,
1405 self.context(inst),
1406 format!("calling convention `{cc}` does not support tail calls"),
1407 ));
1408 }
1409 if cc != self.func.signature.call_conv {
1410 errors.report((
1411 inst,
1412 self.context(inst),
1413 "callee's calling convention must match caller",
1414 ));
1415 }
1416 let types = signature.returns.iter().map(|param| param.value_type);
1417 self.typecheck_return_types(inst, types, errors, "results of callee must match caller")?;
1418 Ok(())
1419 }
1420
1421 fn typecheck_return_types(
1422 &self,
1423 inst: Inst,
1424 actual_types: impl ExactSizeIterator<Item = Type>,
1425 errors: &mut VerifierErrors,
1426 message: &str,
1427 ) -> VerifierStepResult {
1428 let expected_types = &self.func.signature.returns;
1429 if actual_types.len() != expected_types.len() {
1430 return errors.nonfatal((inst, self.context(inst), message));
1431 }
1432 for (i, (actual_type, &expected_type)) in actual_types.zip(expected_types).enumerate() {
1433 if actual_type != expected_type.value_type {
1434 errors.report((
1435 inst,
1436 self.context(inst),
1437 format!(
1438 "result {i} has type {actual_type}, must match function signature of \
1439 {expected_type}"
1440 ),
1441 ));
1442 }
1443 }
1444 Ok(())
1445 }
1446
1447 fn typecheck_special(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1450 match self.func.dfg.insts[inst] {
1451 ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
1452 if let Some(isa) = self.isa {
1453 let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
1454 let global_type = self.func.global_values[global_value].global_type(isa);
1455 if inst_type != global_type {
1456 return errors.nonfatal((
1457 inst, self.context(inst),
1458 format!(
1459 "global_value instruction with type {inst_type} references global value with type {global_type}"
1460 )),
1461 );
1462 }
1463 }
1464 }
1465 _ => {}
1466 }
1467 Ok(())
1468 }
1469
1470 fn cfg_integrity(
1471 &self,
1472 cfg: &ControlFlowGraph,
1473 errors: &mut VerifierErrors,
1474 ) -> VerifierStepResult {
1475 let mut expected_succs = BTreeSet::<Block>::new();
1476 let mut got_succs = BTreeSet::<Block>::new();
1477 let mut expected_preds = BTreeSet::<Inst>::new();
1478 let mut got_preds = BTreeSet::<Inst>::new();
1479
1480 for block in self.func.layout.blocks() {
1481 expected_succs.extend(self.expected_cfg.succ_iter(block));
1482 got_succs.extend(cfg.succ_iter(block));
1483
1484 let missing_succs: Vec<Block> =
1485 expected_succs.difference(&got_succs).cloned().collect();
1486 if !missing_succs.is_empty() {
1487 errors.report((
1488 block,
1489 format!("cfg lacked the following successor(s) {missing_succs:?}"),
1490 ));
1491 continue;
1492 }
1493
1494 let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();
1495 if !excess_succs.is_empty() {
1496 errors.report((
1497 block,
1498 format!("cfg had unexpected successor(s) {excess_succs:?}"),
1499 ));
1500 continue;
1501 }
1502
1503 expected_preds.extend(
1504 self.expected_cfg
1505 .pred_iter(block)
1506 .map(|BlockPredecessor { inst, .. }| inst),
1507 );
1508 got_preds.extend(
1509 cfg.pred_iter(block)
1510 .map(|BlockPredecessor { inst, .. }| inst),
1511 );
1512
1513 let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
1514 if !missing_preds.is_empty() {
1515 errors.report((
1516 block,
1517 format!("cfg lacked the following predecessor(s) {missing_preds:?}"),
1518 ));
1519 continue;
1520 }
1521
1522 let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
1523 if !excess_preds.is_empty() {
1524 errors.report((
1525 block,
1526 format!("cfg had unexpected predecessor(s) {excess_preds:?}"),
1527 ));
1528 continue;
1529 }
1530
1531 expected_succs.clear();
1532 got_succs.clear();
1533 expected_preds.clear();
1534 got_preds.clear();
1535 }
1536 errors.as_result()
1537 }
1538
1539 fn immediate_constraints(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1540 let inst_data = &self.func.dfg.insts[inst];
1541
1542 match *inst_data {
1543 ir::InstructionData::Store { flags, .. } => {
1544 if flags.readonly() {
1545 errors.fatal((
1546 inst,
1547 self.context(inst),
1548 "A store instruction cannot have the `readonly` MemFlag",
1549 ))
1550 } else {
1551 Ok(())
1552 }
1553 }
1554 ir::InstructionData::BinaryImm8 {
1555 opcode: ir::instructions::Opcode::Extractlane,
1556 imm: lane,
1557 arg,
1558 ..
1559 }
1560 | ir::InstructionData::TernaryImm8 {
1561 opcode: ir::instructions::Opcode::Insertlane,
1562 imm: lane,
1563 args: [arg, _],
1564 ..
1565 } => {
1566 let ty = self.func.dfg.value_type(arg);
1569 if lane as u32 >= ty.lane_count() {
1570 errors.fatal((
1571 inst,
1572 self.context(inst),
1573 format!("The lane {lane} does not index into the type {ty}",),
1574 ))
1575 } else {
1576 Ok(())
1577 }
1578 }
1579 ir::InstructionData::Shuffle {
1580 opcode: ir::instructions::Opcode::Shuffle,
1581 imm,
1582 ..
1583 } => {
1584 let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();
1585 if imm.len() != 16 {
1586 errors.fatal((
1587 inst,
1588 self.context(inst),
1589 format!("the shuffle immediate wasn't 16-bytes long"),
1590 ))
1591 } else if let Some(i) = imm.iter().find(|i| **i >= 32) {
1592 errors.fatal((
1593 inst,
1594 self.context(inst),
1595 format!("shuffle immediate index {i} is larger than the maximum 31"),
1596 ))
1597 } else {
1598 Ok(())
1599 }
1600 }
1601 _ => Ok(()),
1602 }
1603 }
1604
1605 fn iconst_bounds(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult {
1606 use crate::ir::instructions::InstructionData::UnaryImm;
1607
1608 let inst_data = &self.func.dfg.insts[inst];
1609 if let UnaryImm {
1610 opcode: Opcode::Iconst,
1611 imm,
1612 } = inst_data
1613 {
1614 let ctrl_typevar = self.func.dfg.ctrl_typevar(inst);
1615 let bounds_mask = match ctrl_typevar {
1616 types::I8 => u8::MAX.into(),
1617 types::I16 => u16::MAX.into(),
1618 types::I32 => u32::MAX.into(),
1619 types::I64 => u64::MAX,
1620 _ => unreachable!(),
1621 };
1622
1623 let value = imm.bits() as u64;
1624 if value & bounds_mask != value {
1625 errors.fatal((
1626 inst,
1627 self.context(inst),
1628 "constant immediate is out of bounds",
1629 ))
1630 } else {
1631 Ok(())
1632 }
1633 } else {
1634 Ok(())
1635 }
1636 }
1637
1638 fn typecheck_function_signature(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1639 let params = self
1640 .func
1641 .signature
1642 .params
1643 .iter()
1644 .enumerate()
1645 .map(|p| (true, p));
1646 let returns = self
1647 .func
1648 .signature
1649 .returns
1650 .iter()
1651 .enumerate()
1652 .map(|p| (false, p));
1653
1654 for (is_argument, (i, param)) in params.chain(returns) {
1655 let is_return = !is_argument;
1656 let item = if is_argument {
1657 "Parameter"
1658 } else {
1659 "Return value"
1660 };
1661
1662 if param.value_type == types::INVALID {
1663 errors.report((
1664 AnyEntity::Function,
1665 format!("{item} at position {i} has an invalid type"),
1666 ));
1667 }
1668
1669 if let ArgumentPurpose::StructArgument(_) = param.purpose {
1670 if is_return {
1671 errors.report((
1672 AnyEntity::Function,
1673 format!("{item} at position {i} can't be an struct argument"),
1674 ))
1675 }
1676 }
1677
1678 let ty_allows_extension = param.value_type.is_int();
1679 let has_extension = param.extension != ArgumentExtension::None;
1680 if !ty_allows_extension && has_extension {
1681 errors.report((
1682 AnyEntity::Function,
1683 format!(
1684 "{} at position {} has invalid extension {:?}",
1685 item, i, param.extension
1686 ),
1687 ));
1688 }
1689 }
1690
1691 if errors.has_error() {
1692 Err(())
1693 } else {
1694 Ok(())
1695 }
1696 }
1697
1698 pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult {
1699 self.verify_global_values(errors)?;
1700 self.verify_memory_types(errors)?;
1701 self.typecheck_entry_block_params(errors)?;
1702 self.check_entry_not_cold(errors)?;
1703 self.typecheck_function_signature(errors)?;
1704
1705 for block in self.func.layout.blocks() {
1706 if self.func.layout.first_inst(block).is_none() {
1707 return errors.fatal((block, format!("{block} cannot be empty")));
1708 }
1709 for inst in self.func.layout.block_insts(block) {
1710 self.block_integrity(block, inst, errors)?;
1711 self.instruction_integrity(inst, errors)?;
1712 self.typecheck(inst, errors)?;
1713 self.immediate_constraints(inst, errors)?;
1714 self.iconst_bounds(inst, errors)?;
1715 }
1716
1717 self.encodable_as_bb(block, errors)?;
1718 }
1719
1720 if !errors.is_empty() {
1721 log::warn!(
1722 "Found verifier errors in function:\n{}",
1723 pretty_verifier_error(self.func, None, errors.clone())
1724 );
1725 }
1726
1727 Ok(())
1728 }
1729}
1730
1731#[cfg(test)]
1732mod tests {
1733 use super::{Verifier, VerifierError, VerifierErrors};
1734 use crate::ir::instructions::{InstructionData, Opcode};
1735 use crate::ir::{types, AbiParam, Function, Type};
1736 use crate::settings;
1737
1738 macro_rules! assert_err_with_msg {
1739 ($e:expr, $msg:expr) => {
1740 match $e.0.get(0) {
1741 None => panic!("Expected an error"),
1742 Some(&VerifierError { ref message, .. }) => {
1743 if !message.contains($msg) {
1744 #[cfg(feature = "std")]
1745 panic!("'{}' did not contain the substring '{}'", message, $msg);
1746 #[cfg(not(feature = "std"))]
1747 panic!("error message did not contain the expected substring");
1748 }
1749 }
1750 }
1751 };
1752 }
1753
1754 #[test]
1755 fn empty() {
1756 let func = Function::new();
1757 let flags = &settings::Flags::new(settings::builder());
1758 let verifier = Verifier::new(&func, flags.into());
1759 let mut errors = VerifierErrors::default();
1760
1761 assert_eq!(verifier.run(&mut errors), Ok(()));
1762 assert!(errors.0.is_empty());
1763 }
1764
1765 #[test]
1766 fn bad_instruction_format() {
1767 let mut func = Function::new();
1768 let block0 = func.dfg.make_block();
1769 func.layout.append_block(block0);
1770 let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {
1771 opcode: Opcode::F32const,
1772 imm: 0.into(),
1773 });
1774 func.layout.append_inst(nullary_with_bad_opcode, block0);
1775 let destination = func.dfg.block_call(block0, &[]);
1776 func.stencil.layout.append_inst(
1777 func.stencil.dfg.make_inst(InstructionData::Jump {
1778 opcode: Opcode::Jump,
1779 destination,
1780 }),
1781 block0,
1782 );
1783 let flags = &settings::Flags::new(settings::builder());
1784 let verifier = Verifier::new(&func, flags.into());
1785 let mut errors = VerifierErrors::default();
1786
1787 let _ = verifier.run(&mut errors);
1788
1789 assert_err_with_msg!(errors, "instruction format");
1790 }
1791
1792 fn test_iconst_bounds(immediate: i64, ctrl_typevar: Type) -> VerifierErrors {
1793 let mut func = Function::new();
1794 let block0 = func.dfg.make_block();
1795 func.layout.append_block(block0);
1796
1797 let test_inst = func.dfg.make_inst(InstructionData::UnaryImm {
1798 opcode: Opcode::Iconst,
1799 imm: immediate.into(),
1800 });
1801
1802 let end_inst = func.dfg.make_inst(InstructionData::MultiAry {
1803 opcode: Opcode::Return,
1804 args: Default::default(),
1805 });
1806
1807 func.dfg.make_inst_results(test_inst, ctrl_typevar);
1808 func.layout.append_inst(test_inst, block0);
1809 func.layout.append_inst(end_inst, block0);
1810
1811 let flags = &settings::Flags::new(settings::builder());
1812 let verifier = Verifier::new(&func, flags.into());
1813 let mut errors = VerifierErrors::default();
1814
1815 let _ = verifier.run(&mut errors);
1816 errors
1817 }
1818
1819 fn test_iconst_bounds_err(immediate: i64, ctrl_typevar: Type) {
1820 assert_err_with_msg!(
1821 test_iconst_bounds(immediate, ctrl_typevar),
1822 "constant immediate is out of bounds"
1823 );
1824 }
1825
1826 fn test_iconst_bounds_ok(immediate: i64, ctrl_typevar: Type) {
1827 assert!(test_iconst_bounds(immediate, ctrl_typevar).is_empty());
1828 }
1829
1830 #[test]
1831 fn negative_iconst_8() {
1832 test_iconst_bounds_err(-10, types::I8);
1833 }
1834
1835 #[test]
1836 fn negative_iconst_32() {
1837 test_iconst_bounds_err(-1, types::I32);
1838 }
1839
1840 #[test]
1841 fn large_iconst_8() {
1842 test_iconst_bounds_err(1 + u8::MAX as i64, types::I8);
1843 }
1844
1845 #[test]
1846 fn large_iconst_16() {
1847 test_iconst_bounds_err(10 + u16::MAX as i64, types::I16);
1848 }
1849
1850 #[test]
1851 fn valid_iconst_8() {
1852 test_iconst_bounds_ok(10, types::I8);
1853 }
1854
1855 #[test]
1856 fn valid_iconst_32() {
1857 test_iconst_bounds_ok(u32::MAX as i64, types::I32);
1858 }
1859
1860 #[test]
1861 fn test_function_invalid_param() {
1862 let mut func = Function::new();
1863 func.signature.params.push(AbiParam::new(types::INVALID));
1864
1865 let mut errors = VerifierErrors::default();
1866 let flags = &settings::Flags::new(settings::builder());
1867 let verifier = Verifier::new(&func, flags.into());
1868
1869 let _ = verifier.typecheck_function_signature(&mut errors);
1870 assert_err_with_msg!(errors, "Parameter at position 0 has an invalid type");
1871 }
1872
1873 #[test]
1874 fn test_function_invalid_return_value() {
1875 let mut func = Function::new();
1876 func.signature.returns.push(AbiParam::new(types::INVALID));
1877
1878 let mut errors = VerifierErrors::default();
1879 let flags = &settings::Flags::new(settings::builder());
1880 let verifier = Verifier::new(&func, flags.into());
1881
1882 let _ = verifier.typecheck_function_signature(&mut errors);
1883 assert_err_with_msg!(errors, "Return value at position 0 has an invalid type");
1884 }
1885
1886 #[test]
1887 fn test_printing_contextual_errors() {
1888 let mut func = Function::new();
1890 let block0 = func.dfg.make_block();
1891 func.layout.append_block(block0);
1892
1893 let inst = func.dfg.make_inst(InstructionData::UnaryIeee64 {
1895 opcode: Opcode::F64const,
1896 imm: 0.0.into(),
1897 });
1898 func.layout.append_inst(inst, block0);
1899
1900 let mut errors = VerifierErrors::default();
1902 let flags = &settings::Flags::new(settings::builder());
1903 let verifier = Verifier::new(&func, flags.into());
1904
1905 let _ = verifier.typecheck_results(inst, types::I32, &mut errors);
1908 assert_eq!(
1909 format!("{}", errors.0[0]),
1910 "inst0 (f64const 0.0): has fewer result values than expected"
1911 )
1912 }
1913
1914 #[test]
1915 fn test_empty_block() {
1916 let mut func = Function::new();
1917 let block0 = func.dfg.make_block();
1918 func.layout.append_block(block0);
1919
1920 let flags = &settings::Flags::new(settings::builder());
1921 let verifier = Verifier::new(&func, flags.into());
1922 let mut errors = VerifierErrors::default();
1923 let _ = verifier.run(&mut errors);
1924
1925 assert_err_with_msg!(errors, "block0 cannot be empty");
1926 }
1927}