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