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