Skip to main content

cranelift_codegen/ir/
instructions.rs

1//! Instruction formats and opcodes.
2//!
3//! The `instructions` module contains definitions for instruction formats, opcodes, and the
4//! in-memory representation of IR instructions.
5//!
6//! A large part of this module is auto-generated from the instruction descriptions in the meta
7//! directory.
8
9use crate::constant_hash::Table;
10use alloc::vec::Vec;
11use core::fmt::{self, Display, Formatter};
12use core::ops::{Deref, DerefMut};
13use core::str::FromStr;
14
15#[cfg(feature = "enable-serde")]
16use serde_derive::{Deserialize, Serialize};
17
18use crate::bitset::ScalarBitSet;
19use crate::entity;
20use crate::ir::{
21    self, Block, ExceptionTable, ExceptionTables, FuncRef, MemFlags, SigRef, StackSlot, Type,
22    Value,
23    condcodes::{FloatCC, IntCC},
24    trapcode::TrapCode,
25    types,
26};
27
28/// Some instructions use an external list of argument values because there is not enough space in
29/// the 16-byte `InstructionData` struct. These value lists are stored in a memory pool in
30/// `dfg.value_lists`.
31pub type ValueList = entity::EntityList<Value>;
32
33/// Memory pool for holding value lists. See `ValueList`.
34pub type ValueListPool = entity::ListPool<Value>;
35
36/// A pair of a Block and its arguments, stored in a single EntityList internally.
37///
38/// Block arguments are semantically a `BlockArg`.
39///
40/// NOTE: We don't expose either value_to_block or block_to_value outside of this module because
41/// this operation is not generally safe. However, as the two share the same underlying layout,
42/// they can be stored in the same value pool.
43///
44/// BlockCall makes use of this shared layout by storing all of its contents (a block and its
45/// argument) in a single EntityList. This is a bit better than introducing a new entity type for
46/// the pair of a block name and the arguments entity list, as we don't pay any indirection penalty
47/// to get to the argument values -- they're stored in-line with the block in the same list.
48///
49/// The BlockCall::new function guarantees this layout by requiring a block argument that's written
50/// in as the first element of the EntityList. Any subsequent entries are always assumed to be real
51/// Values.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
54pub struct BlockCall {
55    /// The underlying storage for the BlockCall. The first element of the values EntityList is
56    /// guaranteed to always be a Block encoded as a Value via BlockCall::block_to_value.
57    /// Consequently, the values entity list is never empty.
58    values: entity::EntityList<Value>,
59}
60
61impl BlockCall {
62    // NOTE: the only uses of this function should be internal to BlockCall. See the block comment
63    // on BlockCall for more context.
64    fn value_to_block(val: Value) -> Block {
65        Block::from_u32(val.as_u32())
66    }
67
68    // NOTE: the only uses of this function should be internal to BlockCall. See the block comment
69    // on BlockCall for more context.
70    fn block_to_value(block: Block) -> Value {
71        Value::from_u32(block.as_u32())
72    }
73
74    /// Construct a BlockCall with the given block and arguments.
75    pub fn new(
76        block: Block,
77        args: impl IntoIterator<Item = BlockArg>,
78        pool: &mut ValueListPool,
79    ) -> Self {
80        let mut values = ValueList::default();
81        values.push(Self::block_to_value(block), pool);
82        values.extend(args.into_iter().map(|arg| arg.encode_as_value()), pool);
83        Self { values }
84    }
85
86    /// Return the block for this BlockCall.
87    pub fn block(&self, pool: &ValueListPool) -> Block {
88        let val = self.values.first(pool).unwrap();
89        Self::value_to_block(val)
90    }
91
92    /// Replace the block for this BlockCall.
93    pub fn set_block(&mut self, block: Block, pool: &mut ValueListPool) {
94        *self.values.get_mut(0, pool).unwrap() = Self::block_to_value(block);
95    }
96
97    /// Append an argument to the block args.
98    pub fn append_argument(&mut self, arg: impl Into<BlockArg>, pool: &mut ValueListPool) {
99        self.values.push(arg.into().encode_as_value(), pool);
100    }
101
102    /// Return the length of the argument list.
103    pub fn len(&self, pool: &ValueListPool) -> usize {
104        self.values.len(pool) - 1
105    }
106
107    /// Return an iterator over the arguments of this block.
108    pub fn args<'a>(
109        &self,
110        pool: &'a ValueListPool,
111    ) -> impl ExactSizeIterator<Item = BlockArg> + DoubleEndedIterator<Item = BlockArg> + use<'a>
112    {
113        self.values.as_slice(pool)[1..]
114            .iter()
115            .map(|value| BlockArg::decode_from_value(*value))
116    }
117
118    /// Traverse the arguments with a closure that can mutate them.
119    pub fn update_args<F: FnMut(BlockArg) -> BlockArg>(
120        &mut self,
121        pool: &mut ValueListPool,
122        mut f: F,
123    ) {
124        for raw in self.values.as_mut_slice(pool)[1..].iter_mut() {
125            let new = f(BlockArg::decode_from_value(*raw));
126            *raw = new.encode_as_value();
127        }
128    }
129
130    /// Remove the argument at ix from the argument list.
131    pub fn remove(&mut self, ix: usize, pool: &mut ValueListPool) {
132        self.values.remove(1 + ix, pool)
133    }
134
135    /// Clear out the arguments list.
136    pub fn clear(&mut self, pool: &mut ValueListPool) {
137        self.values.truncate(1, pool)
138    }
139
140    /// Appends multiple elements to the arguments.
141    pub fn extend<I, T>(&mut self, elements: I, pool: &mut ValueListPool)
142    where
143        I: IntoIterator<Item = T>,
144        T: Into<BlockArg>,
145    {
146        self.values.extend(
147            elements
148                .into_iter()
149                .map(|elem| elem.into().encode_as_value()),
150            pool,
151        )
152    }
153
154    /// Return a value that can display this block call.
155    pub fn display<'a>(&self, pool: &'a ValueListPool) -> DisplayBlockCall<'a> {
156        DisplayBlockCall { block: *self, pool }
157    }
158
159    /// Deep-clone the underlying list in the same pool. The returned
160    /// list will have identical contents but changes to this list
161    /// will not change its contents or vice-versa.
162    pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {
163        Self {
164            values: self.values.deep_clone(pool),
165        }
166    }
167}
168
169/// Wrapper for the context needed to display a [BlockCall] value.
170pub struct DisplayBlockCall<'a> {
171    block: BlockCall,
172    pool: &'a ValueListPool,
173}
174
175impl<'a> Display for DisplayBlockCall<'a> {
176    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
177        write!(f, "{}", self.block.block(&self.pool))?;
178        if self.block.len(self.pool) > 0 {
179            write!(f, "(")?;
180            for (ix, arg) in self.block.args(self.pool).enumerate() {
181                if ix > 0 {
182                    write!(f, ", ")?;
183                }
184                write!(f, "{arg}")?;
185            }
186            write!(f, ")")?;
187        }
188        Ok(())
189    }
190}
191
192/// A `BlockArg` is a sum type of `Value`, `TryCallRet`, and
193/// `TryCallExn`. The latter two are values that are generated "on the
194/// edge" out of a `try_call` instruction into a successor block. We
195/// use special arguments rather than special values for these because
196/// they are not definable as SSA values at a certain program point --
197/// only when the `BlockCall` is executed.
198#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
199pub enum BlockArg {
200    /// An ordinary value, usable at the branch instruction using this
201    /// `BlockArg`, whose value is passed as an argument.
202    Value(Value),
203
204    /// A return value of a `try_call`'s called function. Signatures
205    /// allow multiple return values, so this carries an index. This
206    /// may be used only on the normal (non-exceptional) `BlockCall`
207    /// out of a `try_call` or `try_call_indirect` instruction.
208    TryCallRet(u32),
209
210    /// An exception payload value of a `try_call`. Some ABIs may
211    /// allow multiple payload values, so this carries an index. Its
212    /// type is defined by the ABI of the called function. This may be
213    /// used only on an exceptional `BlockCall` out of a `try_call` or
214    /// `try_call_indirect` instruction.
215    TryCallExn(u32),
216}
217
218impl BlockArg {
219    /// Encode this block argument as a `Value` for storage in the
220    /// value pool. Internal to `BlockCall`, must not be used
221    /// elsewhere to avoid exposing the raw bit encoding.
222    fn encode_as_value(&self) -> Value {
223        let (tag, payload) = match *self {
224            BlockArg::Value(v) => (0, v.as_bits()),
225            BlockArg::TryCallRet(i) => (1, i),
226            BlockArg::TryCallExn(i) => (2, i),
227        };
228        assert!(payload < (1 << 30));
229        let raw = (tag << 30) | payload;
230        Value::from_bits(raw)
231    }
232
233    /// Decode a raw `Value` encoding of this block argument.
234    fn decode_from_value(v: Value) -> Self {
235        let raw = v.as_u32();
236        let tag = raw >> 30;
237        let payload = raw & ((1 << 30) - 1);
238        match tag {
239            0 => BlockArg::Value(Value::from_bits(payload)),
240            1 => BlockArg::TryCallRet(payload),
241            2 => BlockArg::TryCallExn(payload),
242            _ => unreachable!(),
243        }
244    }
245
246    /// Return this argument as a `Value`, if it is one, or `None`
247    /// otherwise.
248    pub fn as_value(&self) -> Option<Value> {
249        match *self {
250            BlockArg::Value(v) => Some(v),
251            _ => None,
252        }
253    }
254
255    /// Update the contained value, if any.
256    pub fn map_value<F: FnMut(Value) -> Value>(&self, mut f: F) -> Self {
257        match *self {
258            BlockArg::Value(v) => BlockArg::Value(f(v)),
259            other => other,
260        }
261    }
262}
263
264impl Display for BlockArg {
265    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
266        match self {
267            BlockArg::Value(v) => write!(f, "{v}"),
268            BlockArg::TryCallRet(i) => write!(f, "ret{i}"),
269            BlockArg::TryCallExn(i) => write!(f, "exn{i}"),
270        }
271    }
272}
273
274impl From<Value> for BlockArg {
275    fn from(value: Value) -> BlockArg {
276        BlockArg::Value(value)
277    }
278}
279
280// Include code generated by `cranelift-codegen/meta/src/gen_inst.rs`. This file contains:
281//
282// - The `pub enum InstructionFormat` enum with all the instruction formats.
283// - The `pub enum InstructionData` enum with all the instruction data fields.
284// - The `pub enum Opcode` definition with all known opcodes,
285// - The `const OPCODE_FORMAT: [InstructionFormat; N]` table.
286// - The private `fn opcode_name(Opcode) -> &'static str` function, and
287// - The hash table `const OPCODE_HASH_TABLE: [Opcode; N]`.
288//
289// For value type constraints:
290//
291// - The `const OPCODE_CONSTRAINTS : [OpcodeConstraints; N]` table.
292// - The `const TYPE_SETS : [ValueTypeSet; N]` table.
293// - The `const OPERAND_CONSTRAINTS : [OperandConstraint; N]` table.
294//
295include!(concat!(env!("OUT_DIR"), "/opcodes.rs"));
296
297impl Display for Opcode {
298    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
299        write!(f, "{}", opcode_name(*self))
300    }
301}
302
303impl Opcode {
304    /// Get the instruction format for this opcode.
305    pub fn format(self) -> InstructionFormat {
306        OPCODE_FORMAT[self as usize - 1]
307    }
308
309    /// Get the constraint descriptor for this opcode.
310    /// Panic if this is called on `NotAnOpcode`.
311    pub fn constraints(self) -> OpcodeConstraints {
312        OPCODE_CONSTRAINTS[self as usize - 1]
313    }
314
315    /// Is this instruction a GC safepoint?
316    ///
317    /// Safepoints are all kinds of calls, except for tail calls.
318    #[inline]
319    pub fn is_safepoint(self) -> bool {
320        self.is_call() && !self.is_return()
321    }
322}
323
324// This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since
325// it critically depends on the `opcode_name()` function which is needed here anyway, it lives in
326// this module. This also saves us from running the build script twice to generate code for the two
327// separate crates.
328impl FromStr for Opcode {
329    type Err = &'static str;
330
331    /// Parse an Opcode name from a string.
332    fn from_str(s: &str) -> Result<Self, &'static str> {
333        use crate::constant_hash::{probe, simple_hash};
334
335        match probe::<&str, [Option<Self>]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) {
336            Err(_) => Err("Unknown opcode"),
337            // We unwrap here because probe() should have ensured that the entry
338            // at this index is not None.
339            Ok(i) => Ok(OPCODE_HASH_TABLE[i].unwrap()),
340        }
341    }
342}
343
344impl<'a> Table<&'a str> for [Option<Opcode>] {
345    fn len(&self) -> usize {
346        self.len()
347    }
348
349    fn key(&self, idx: usize) -> Option<&'a str> {
350        self[idx].map(opcode_name)
351    }
352}
353
354/// A variable list of `Value` operands used for function call arguments and passing arguments to
355/// basic blocks.
356#[derive(Clone, Debug)]
357pub struct VariableArgs(Vec<Value>);
358
359impl VariableArgs {
360    /// Create an empty argument list.
361    pub fn new() -> Self {
362        Self(Vec::new())
363    }
364
365    /// Add an argument to the end.
366    pub fn push(&mut self, v: Value) {
367        self.0.push(v)
368    }
369
370    /// Check if the list is empty.
371    pub fn is_empty(&self) -> bool {
372        self.0.is_empty()
373    }
374
375    /// Convert this to a value list in `pool` with `fixed` prepended.
376    pub fn into_value_list(self, fixed: &[Value], pool: &mut ValueListPool) -> ValueList {
377        let mut vlist = ValueList::default();
378        vlist.extend(fixed.iter().cloned(), pool);
379        vlist.extend(self.0, pool);
380        vlist
381    }
382}
383
384// Coerce `VariableArgs` into a `&[Value]` slice.
385impl Deref for VariableArgs {
386    type Target = [Value];
387
388    fn deref(&self) -> &[Value] {
389        &self.0
390    }
391}
392
393impl DerefMut for VariableArgs {
394    fn deref_mut(&mut self) -> &mut [Value] {
395        &mut self.0
396    }
397}
398
399impl Display for VariableArgs {
400    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
401        for (i, val) in self.0.iter().enumerate() {
402            if i == 0 {
403                write!(fmt, "{val}")?;
404            } else {
405                write!(fmt, ", {val}")?;
406            }
407        }
408        Ok(())
409    }
410}
411
412impl Default for VariableArgs {
413    fn default() -> Self {
414        Self::new()
415    }
416}
417
418/// Analyzing an instruction.
419///
420/// Avoid large matches on instruction formats by using the methods defined here to examine
421/// instructions.
422impl InstructionData {
423    /// Get the destinations of this instruction, if it's a branch.
424    ///
425    /// `br_table` returns the empty slice.
426    pub fn branch_destination<'a>(
427        &'a self,
428        jump_tables: &'a ir::JumpTables,
429        exception_tables: &'a ir::ExceptionTables,
430    ) -> &'a [BlockCall] {
431        match self {
432            Self::Jump { destination, .. } => core::slice::from_ref(destination),
433            Self::Brif { blocks, .. } => blocks.as_slice(),
434            Self::BranchTable { table, .. } => jump_tables.get(*table).unwrap().all_branches(),
435            Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
436                exception_tables.get(*exception).unwrap().all_branches()
437            }
438            _ => {
439                debug_assert!(!self.opcode().is_branch());
440                &[]
441            }
442        }
443    }
444
445    /// Get a mutable slice of the destinations of this instruction, if it's a branch.
446    ///
447    /// `br_table` returns the empty slice.
448    pub fn branch_destination_mut<'a>(
449        &'a mut self,
450        jump_tables: &'a mut ir::JumpTables,
451        exception_tables: &'a mut ir::ExceptionTables,
452    ) -> &'a mut [BlockCall] {
453        match self {
454            Self::Jump { destination, .. } => core::slice::from_mut(destination),
455            Self::Brif { blocks, .. } => blocks.as_mut_slice(),
456            Self::BranchTable { table, .. } => {
457                jump_tables.get_mut(*table).unwrap().all_branches_mut()
458            }
459            Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
460                exception_tables
461                    .get_mut(*exception)
462                    .unwrap()
463                    .all_branches_mut()
464            }
465            _ => {
466                debug_assert!(!self.opcode().is_branch());
467                &mut []
468            }
469        }
470    }
471
472    /// Replace the values used in this instruction according to the given
473    /// function.
474    pub fn map_values(
475        &mut self,
476        pool: &mut ValueListPool,
477        jump_tables: &mut ir::JumpTables,
478        exception_tables: &mut ir::ExceptionTables,
479        mut f: impl FnMut(Value) -> Value,
480    ) {
481        // Map all normal operator args.
482        for arg in self.arguments_mut(pool) {
483            *arg = f(*arg);
484        }
485
486        // Map all BlockCall args.
487        for block in self.branch_destination_mut(jump_tables, exception_tables) {
488            block.update_args(pool, |arg| arg.map_value(|val| f(val)));
489        }
490
491        // Map all context items.
492        if let Some(et) = self.exception_table() {
493            for ctx in exception_tables[et].contexts_mut() {
494                *ctx = f(*ctx);
495            }
496        }
497    }
498
499    /// If this is a trapping instruction, get its trap code. Otherwise, return
500    /// `None`.
501    pub fn trap_code(&self) -> Option<TrapCode> {
502        match *self {
503            Self::CondTrap { code, .. }
504            | Self::IntAddTrap { code, .. }
505            | Self::Trap { code, .. } => Some(code),
506            _ => None,
507        }
508    }
509
510    /// If this is a control-flow instruction depending on an integer condition, gets its
511    /// condition.  Otherwise, return `None`.
512    pub fn cond_code(&self) -> Option<IntCC> {
513        match self {
514            &InstructionData::IntCompare { cond, .. } => Some(cond),
515            _ => None,
516        }
517    }
518
519    /// If this is a control-flow instruction depending on a floating-point condition, gets its
520    /// condition.  Otherwise, return `None`.
521    pub fn fp_cond_code(&self) -> Option<FloatCC> {
522        match self {
523            &InstructionData::FloatCompare { cond, .. } => Some(cond),
524            _ => None,
525        }
526    }
527
528    /// If this is a trapping instruction, get an exclusive reference to its
529    /// trap code. Otherwise, return `None`.
530    pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> {
531        match self {
532            Self::CondTrap { code, .. }
533            | Self::IntAddTrap { code, .. }
534            | Self::Trap { code, .. } => Some(code),
535            _ => None,
536        }
537    }
538
539    /// If this is an atomic read/modify/write instruction, return its subopcode.
540    pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> {
541        match self {
542            &InstructionData::AtomicRmw { op, .. } => Some(op),
543            _ => None,
544        }
545    }
546
547    /// If this is a load/store instruction, returns its immediate offset.
548    pub fn load_store_offset(&self) -> Option<i32> {
549        match self {
550            &InstructionData::Load { offset, .. }
551            | &InstructionData::StackAddr { offset, .. }
552            | &InstructionData::Store { offset, .. } => Some(offset.into()),
553            _ => None,
554        }
555    }
556
557    /// If this is a load/store instruction, return its memory flags.
558    pub fn memflags(&self) -> Option<MemFlags> {
559        match self {
560            &InstructionData::Load { flags, .. }
561            | &InstructionData::LoadNoOffset { flags, .. }
562            | &InstructionData::Store { flags, .. }
563            | &InstructionData::StoreNoOffset { flags, .. }
564            | &InstructionData::AtomicCas { flags, .. }
565            | &InstructionData::AtomicRmw { flags, .. } => Some(flags),
566            _ => None,
567        }
568    }
569
570    /// If this is a load/store instruction, return its memory flags.
571    pub fn memflags_mut(&mut self) -> Option<&mut MemFlags> {
572        match self {
573            InstructionData::Load { flags, .. }
574            | InstructionData::LoadNoOffset { flags, .. }
575            | InstructionData::Store { flags, .. }
576            | InstructionData::StoreNoOffset { flags, .. }
577            | InstructionData::AtomicCas { flags, .. }
578            | InstructionData::AtomicRmw { flags, .. } => Some(flags),
579            _ => None,
580        }
581    }
582
583    /// If this is a load/store instruction, resolve its memory flags to data
584    /// through the DFG.
585    pub fn memflags_data(&self, dfg: &super::dfg::DataFlowGraph) -> Option<super::MemFlagsData> {
586        self.memflags().map(|f| dfg.mem_flags[f])
587    }
588
589    /// Get this load/store instruction's trap code, if any.
590    ///
591    /// Returns `None` when this is not a load/store instruction, or if it is a
592    /// load/store instruction but does not have a trap code.
593    pub fn memflags_trap_code(&self, dfg: &super::dfg::DataFlowGraph) -> Option<TrapCode> {
594        self.memflags_data(dfg)?.trap_code()
595    }
596
597    /// If this is a load/store instruction, get its alias region.
598    pub fn alias_region(&self, dfg: &super::dfg::DataFlowGraph) -> Option<super::AliasRegion> {
599        let flags = self.memflags_data(dfg)?;
600        flags.alias_region()
601    }
602
603    /// If this instruction references a stack slot, return it
604    pub fn stack_slot(&self) -> Option<StackSlot> {
605        match self {
606            &InstructionData::StackAddr { stack_slot, .. } => Some(stack_slot),
607            _ => None,
608        }
609    }
610
611    /// Return information about a call instruction.
612    ///
613    /// Any instruction that can call another function reveals its call signature here.
614    pub fn analyze_call<'a>(
615        &'a self,
616        pool: &'a ValueListPool,
617        exception_tables: &ExceptionTables,
618    ) -> CallInfo<'a> {
619        match *self {
620            Self::Call {
621                func_ref, ref args, ..
622            } => CallInfo::Direct(func_ref, args.as_slice(pool)),
623            Self::CallIndirect {
624                sig_ref, ref args, ..
625            } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
626            Self::TryCall {
627                func_ref,
628                ref args,
629                exception,
630                ..
631            } => {
632                let exdata = &exception_tables[exception];
633                CallInfo::DirectWithSig(func_ref, exdata.signature(), args.as_slice(pool))
634            }
635            Self::TryCallIndirect {
636                exception,
637                ref args,
638                ..
639            } => {
640                let exdata = &exception_tables[exception];
641                CallInfo::Indirect(exdata.signature(), &args.as_slice(pool)[1..])
642            }
643            Self::Ternary {
644                opcode: Opcode::StackSwitch,
645                ..
646            }
647            | Self::IntAddTrap {
648                opcode: Opcode::InterruptPoll,
649                ..
650            } => {
651                // These instructions aren't actually calls, but they have the
652                // .call() side effect, as they continue execution elsewhere.
653                CallInfo::NotACall
654            }
655            _ => {
656                debug_assert!(!self.opcode().is_call());
657                CallInfo::NotACall
658            }
659        }
660    }
661
662    #[inline]
663    pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {
664        if ctrl_typevar.is_invalid() {
665            return;
666        }
667
668        let bit_width = ctrl_typevar.bits();
669
670        match self {
671            Self::UnaryImm { opcode: _, imm } => {
672                *imm = imm.mask_to_width(bit_width);
673            }
674            _ => {}
675        }
676    }
677
678    /// Get the exception table, if any, associated with this instruction.
679    pub fn exception_table(&self) -> Option<ExceptionTable> {
680        match self {
681            Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
682                Some(*exception)
683            }
684            _ => None,
685        }
686    }
687}
688
689/// Information about call instructions.
690pub enum CallInfo<'a> {
691    /// This is not a call instruction.
692    NotACall,
693
694    /// This is a direct call to an external function declared in the preamble. See
695    /// `DataFlowGraph.ext_funcs`.
696    Direct(FuncRef, &'a [Value]),
697
698    /// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`.
699    Indirect(SigRef, &'a [Value]),
700
701    /// This is a direct call to an external function declared in the
702    /// preamble, but the signature is also known by other means:
703    /// e.g., from an exception table entry.
704    DirectWithSig(FuncRef, SigRef, &'a [Value]),
705}
706
707/// Value type constraints for a given opcode.
708///
709/// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and
710/// results are not determined by the format. Every `Opcode` has an associated
711/// `OpcodeConstraints` object that provides the missing details.
712#[derive(Clone, Copy)]
713pub struct OpcodeConstraints {
714    /// Flags for this opcode encoded as a bit field:
715    ///
716    /// Bits 0-2:
717    ///     Number of fixed result values. This does not include `variable_args` results as are
718    ///     produced by call instructions.
719    ///
720    /// Bit 3:
721    ///     This opcode is polymorphic and the controlling type variable can be inferred from the
722    ///     designated input operand. This is the `typevar_operand` index given to the
723    ///     `InstructionFormat` meta language object. When this bit is not set, the controlling
724    ///     type variable must be the first output value instead.
725    ///
726    /// Bit 4:
727    ///     This opcode is polymorphic and the controlling type variable does *not* appear as the
728    ///     first result type.
729    ///
730    /// Bits 5-7:
731    ///     Number of fixed value arguments. The minimum required number of value operands.
732    flags: u8,
733
734    /// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`.
735    typeset_offset: u8,
736
737    /// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first
738    /// `num_fixed_results()` entries describe the result constraints, then follows constraints for
739    /// the fixed `Value` input operands. (`num_fixed_value_arguments()` of them).
740    constraint_offset: u16,
741}
742
743impl OpcodeConstraints {
744    /// Can the controlling type variable for this opcode be inferred from the designated value
745    /// input operand?
746    /// This also implies that this opcode is polymorphic.
747    pub fn use_typevar_operand(self) -> bool {
748        (self.flags & 0x8) != 0
749    }
750
751    /// Is it necessary to look at the designated value input operand in order to determine the
752    /// controlling type variable, or is it good enough to use the first return type?
753    ///
754    /// Most polymorphic instructions produce a single result with the type of the controlling type
755    /// variable. A few polymorphic instructions either don't produce any results, or produce
756    /// results with a fixed type. These instructions return `true`.
757    pub fn requires_typevar_operand(self) -> bool {
758        (self.flags & 0x10) != 0
759    }
760
761    /// Get the number of *fixed* result values produced by this opcode.
762    /// This does not include `variable_args` produced by calls.
763    pub fn num_fixed_results(self) -> usize {
764        (self.flags & 0x7) as usize
765    }
766
767    /// Get the number of *fixed* input values required by this opcode.
768    ///
769    /// This does not include `variable_args` arguments on call and branch instructions.
770    ///
771    /// The number of fixed input values is usually implied by the instruction format, but
772    /// instruction formats that use a `ValueList` put both fixed and variable arguments in the
773    /// list. This method returns the *minimum* number of values required in the value list.
774    pub fn num_fixed_value_arguments(self) -> usize {
775        ((self.flags >> 5) & 0x7) as usize
776    }
777
778    /// Get the offset into `TYPE_SETS` for the controlling type variable.
779    /// Returns `None` if the instruction is not polymorphic.
780    fn typeset_offset(self) -> Option<usize> {
781        let offset = usize::from(self.typeset_offset);
782        if offset < TYPE_SETS.len() {
783            Some(offset)
784        } else {
785            None
786        }
787    }
788
789    /// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin.
790    fn constraint_offset(self) -> usize {
791        self.constraint_offset as usize
792    }
793
794    /// Get the value type of result number `n`, having resolved the controlling type variable to
795    /// `ctrl_type`.
796    pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
797        debug_assert!(n < self.num_fixed_results(), "Invalid result index");
798        match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {
799            ResolvedConstraint::Bound(t) => t,
800            ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"),
801        }
802    }
803
804    /// Get the value type of input value number `n`, having resolved the controlling type variable
805    /// to `ctrl_type`.
806    ///
807    /// Unlike results, it is possible for some input values to vary freely within a specific
808    /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant.
809    pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
810        debug_assert!(
811            n < self.num_fixed_value_arguments(),
812            "Invalid value argument index"
813        );
814        let offset = self.constraint_offset() + self.num_fixed_results();
815        OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
816    }
817
818    /// Get the typeset of allowed types for the controlling type variable in a polymorphic
819    /// instruction.
820    pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
821        self.typeset_offset().map(|offset| TYPE_SETS[offset])
822    }
823
824    /// Is this instruction polymorphic?
825    pub fn is_polymorphic(self) -> bool {
826        self.ctrl_typeset().is_some()
827    }
828}
829
830type BitSet8 = ScalarBitSet<u8>;
831type BitSet16 = ScalarBitSet<u16>;
832
833/// A value type set describes the permitted set of types for a type variable.
834#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
835pub struct ValueTypeSet {
836    /// Allowed lane sizes
837    pub lanes: BitSet16,
838    /// Allowed int widths
839    pub ints: BitSet8,
840    /// Allowed float widths
841    pub floats: BitSet8,
842    /// Allowed dynamic vectors minimum lane sizes
843    pub dynamic_lanes: BitSet16,
844}
845
846impl ValueTypeSet {
847    /// Is `scalar` part of the base type set?
848    ///
849    /// Note that the base type set does not have to be included in the type set proper.
850    fn is_base_type(self, scalar: Type) -> bool {
851        let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap();
852        if scalar.is_int() {
853            self.ints.contains(l2b)
854        } else if scalar.is_float() {
855            self.floats.contains(l2b)
856        } else {
857            false
858        }
859    }
860
861    /// Does `typ` belong to this set?
862    pub fn contains(self, typ: Type) -> bool {
863        if typ.is_dynamic_vector() {
864            let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap();
865            self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())
866        } else {
867            let l2l = u8::try_from(typ.log2_lane_count()).unwrap();
868            self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
869        }
870    }
871
872    /// Get an example member of this type set.
873    ///
874    /// This is used for error messages to avoid suggesting invalid types.
875    pub fn example(self) -> Type {
876        let t = if self.ints.max().unwrap_or(0) > 5 {
877            types::I32
878        } else if self.floats.max().unwrap_or(0) > 5 {
879            types::F32
880        } else {
881            types::I8
882        };
883        t.by(1 << self.lanes.min().unwrap()).unwrap()
884    }
885}
886
887/// Operand constraints. This describes the value type constraints on a single `Value` operand.
888enum OperandConstraint {
889    /// This operand has a concrete value type.
890    Concrete(Type),
891
892    /// This operand can vary freely within the given type set.
893    /// The type set is identified by its index into the TYPE_SETS constant table.
894    Free(u8),
895
896    /// This operand is the same type as the controlling type variable.
897    Same,
898
899    /// This operand is `ctrlType.lane_of()`.
900    LaneOf,
901
902    /// This operand is `ctrlType.as_truthy()`.
903    AsTruthy,
904
905    /// This operand is `ctrlType.half_width()`.
906    HalfWidth,
907
908    /// This operand is `ctrlType.double_width()`.
909    DoubleWidth,
910
911    /// This operand is `ctrlType.split_lanes()`.
912    SplitLanes,
913
914    /// This operand is `ctrlType.merge_lanes()`.
915    MergeLanes,
916
917    /// This operands is `ctrlType.dynamic_to_vector()`.
918    DynamicToVector,
919
920    /// This operand is `ctrlType.narrower()`.
921    Narrower,
922
923    /// This operand is `ctrlType.wider()`.
924    Wider,
925}
926
927impl OperandConstraint {
928    /// Resolve this operand constraint into a concrete value type, given the value of the
929    /// controlling type variable.
930    pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
931        use self::OperandConstraint::*;
932        use self::ResolvedConstraint::Bound;
933        match *self {
934            Concrete(t) => Bound(t),
935            Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
936            Same => Bound(ctrl_type),
937            LaneOf => Bound(ctrl_type.lane_of()),
938            AsTruthy => Bound(ctrl_type.as_truthy()),
939            HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
940            DoubleWidth => Bound(
941                ctrl_type
942                    .double_width()
943                    .expect("invalid type for double_width"),
944            ),
945            SplitLanes => {
946                if ctrl_type.is_dynamic_vector() {
947                    Bound(
948                        ctrl_type
949                            .dynamic_to_vector()
950                            .expect("invalid type for dynamic_to_vector")
951                            .split_lanes()
952                            .expect("invalid type for split_lanes")
953                            .vector_to_dynamic()
954                            .expect("invalid dynamic type"),
955                    )
956                } else {
957                    Bound(
958                        ctrl_type
959                            .split_lanes()
960                            .expect("invalid type for split_lanes"),
961                    )
962                }
963            }
964            MergeLanes => {
965                if ctrl_type.is_dynamic_vector() {
966                    Bound(
967                        ctrl_type
968                            .dynamic_to_vector()
969                            .expect("invalid type for dynamic_to_vector")
970                            .merge_lanes()
971                            .expect("invalid type for merge_lanes")
972                            .vector_to_dynamic()
973                            .expect("invalid dynamic type"),
974                    )
975                } else {
976                    Bound(
977                        ctrl_type
978                            .merge_lanes()
979                            .expect("invalid type for merge_lanes"),
980                    )
981                }
982            }
983            DynamicToVector => Bound(
984                ctrl_type
985                    .dynamic_to_vector()
986                    .expect("invalid type for dynamic_to_vector"),
987            ),
988            Narrower => {
989                let ctrl_type_bits = ctrl_type.log2_lane_bits();
990                let mut tys = ValueTypeSet::default();
991
992                // We're testing scalar values, only.
993                tys.lanes = ScalarBitSet::from_range(0, 1);
994
995                if ctrl_type.is_int() {
996                    // The upper bound in from_range is exclusive, and we want to exclude the
997                    // control type to construct the interval of [I8, ctrl_type).
998                    tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);
999                } else if ctrl_type.is_float() {
1000                    // The upper bound in from_range is exclusive, and we want to exclude the
1001                    // control type to construct the interval of [F16, ctrl_type).
1002                    tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8);
1003                } else {
1004                    panic!(
1005                        "The Narrower constraint only operates on floats or ints, got {ctrl_type:?}"
1006                    );
1007                }
1008                ResolvedConstraint::Free(tys)
1009            }
1010            Wider => {
1011                let ctrl_type_bits = ctrl_type.log2_lane_bits();
1012                let mut tys = ValueTypeSet::default();
1013
1014                // We're testing scalar values, only.
1015                tys.lanes = ScalarBitSet::from_range(0, 1);
1016
1017                if ctrl_type.is_int() {
1018                    let lower_bound = ctrl_type_bits as u8 + 1;
1019                    // The largest integer type we can represent in `BitSet8` is I128, which is
1020                    // represented by bit 7 in the bit set. Adding one to exclude I128 from the
1021                    // lower bound would overflow as 2^8 doesn't fit in a u8, but this would
1022                    // already describe the empty set so instead we leave `ints` in its default
1023                    // empty state.
1024                    if lower_bound < BitSet8::capacity() {
1025                        // The interval should include all types wider than `ctrl_type`, so we use
1026                        // `2^8` as the upper bound, and add one to the bits of `ctrl_type` to define
1027                        // the interval `(ctrl_type, I128]`.
1028                        tys.ints = BitSet8::from_range(lower_bound, 8);
1029                    }
1030                } else if ctrl_type.is_float() {
1031                    // Same as above but for `tys.floats`, as the largest float type is F128.
1032                    let lower_bound = ctrl_type_bits as u8 + 1;
1033                    if lower_bound < BitSet8::capacity() {
1034                        tys.floats = BitSet8::from_range(lower_bound, 8);
1035                    }
1036                } else {
1037                    panic!(
1038                        "The Wider constraint only operates on floats or ints, got {ctrl_type:?}"
1039                    );
1040                }
1041
1042                ResolvedConstraint::Free(tys)
1043            }
1044        }
1045    }
1046}
1047
1048/// The type constraint on a value argument once the controlling type variable is known.
1049#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1050pub enum ResolvedConstraint {
1051    /// The operand is bound to a known type.
1052    Bound(Type),
1053    /// The operand type can vary freely within the given set.
1054    Free(ValueTypeSet),
1055}
1056
1057/// A trait to map some functions over each of the entities within an
1058/// instruction, when paired with `InstructionData::map`.
1059pub trait InstructionMapper {
1060    /// Map a function over a `Value`.
1061    fn map_value(&mut self, value: Value) -> Value;
1062
1063    /// Map a function over a `ValueList`.
1064    fn map_value_list(&mut self, value_list: ValueList) -> ValueList;
1065
1066    /// Map a function over a `GlobalValue`.
1067    fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue;
1068
1069    /// Map a function over a `JumpTable`.
1070    fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable;
1071
1072    /// Map a function over an `ExceptionTable`.
1073    fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable;
1074
1075    /// Map a function over a `BlockCall`.
1076    fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall;
1077
1078    /// Map a function over a `Block`.
1079    fn map_block(&mut self, block: Block) -> Block;
1080
1081    /// Map a function over a `FuncRef`.
1082    fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;
1083
1084    /// Map a function over a `SigRef`.
1085    fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;
1086
1087    /// Map a function over a `StackSlot`.
1088    fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;
1089
1090    /// Map a function over a `DynamicStackSlot`.
1091    fn map_dynamic_stack_slot(
1092        &mut self,
1093        dynamic_stack_slot: ir::DynamicStackSlot,
1094    ) -> ir::DynamicStackSlot;
1095
1096    /// Map a function over a `Constant`.
1097    fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;
1098
1099    /// Map a function over an `Immediate`.
1100    fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;
1101
1102    /// Map a function over a `MemFlags` entity.
1103    ///
1104    /// The default implementation returns the flags unchanged, which is correct
1105    /// for mappers within a single function. Override this when mapping between
1106    /// functions (e.g. inlining) to re-insert the flags data into the target DFG.
1107    fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1108        flags
1109    }
1110}
1111
1112impl<'a, T> InstructionMapper for &'a mut T
1113where
1114    T: InstructionMapper,
1115{
1116    fn map_value(&mut self, value: Value) -> Value {
1117        (**self).map_value(value)
1118    }
1119
1120    fn map_value_list(&mut self, value_list: ValueList) -> ValueList {
1121        (**self).map_value_list(value_list)
1122    }
1123
1124    fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1125        (**self).map_global_value(global_value)
1126    }
1127
1128    fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1129        (**self).map_jump_table(jump_table)
1130    }
1131
1132    fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1133        (**self).map_exception_table(exception_table)
1134    }
1135
1136    fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {
1137        (**self).map_block_call(block_call)
1138    }
1139
1140    fn map_block(&mut self, block: Block) -> Block {
1141        (**self).map_block(block)
1142    }
1143
1144    fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1145        (**self).map_func_ref(func_ref)
1146    }
1147
1148    fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1149        (**self).map_sig_ref(sig_ref)
1150    }
1151
1152    fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1153        (**self).map_stack_slot(stack_slot)
1154    }
1155
1156    fn map_dynamic_stack_slot(
1157        &mut self,
1158        dynamic_stack_slot: ir::DynamicStackSlot,
1159    ) -> ir::DynamicStackSlot {
1160        (**self).map_dynamic_stack_slot(dynamic_stack_slot)
1161    }
1162
1163    fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1164        (**self).map_constant(constant)
1165    }
1166
1167    fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1168        (**self).map_immediate(immediate)
1169    }
1170
1171    fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1172        (**self).map_mem_flags(flags)
1173    }
1174}
1175
1176#[cfg(test)]
1177mod tests {
1178    use super::*;
1179    use alloc::string::ToString;
1180    use ir::{DynamicStackSlot, GlobalValue, JumpTable};
1181
1182    #[test]
1183    fn inst_data_is_copy() {
1184        fn is_copy<T: Copy>() {}
1185        is_copy::<InstructionData>();
1186    }
1187
1188    #[test]
1189    fn inst_data_size() {
1190        // The size of `InstructionData` is performance sensitive, so make sure
1191        // we don't regress it unintentionally.
1192        assert_eq!(core::mem::size_of::<InstructionData>(), 16);
1193    }
1194
1195    #[test]
1196    fn opcodes() {
1197        use core::mem;
1198
1199        let x = Opcode::Iadd;
1200        let mut y = Opcode::Isub;
1201
1202        assert!(x != y);
1203        y = Opcode::Iadd;
1204        assert_eq!(x, y);
1205        assert_eq!(x.format(), InstructionFormat::Binary);
1206
1207        assert_eq!(format!("{:?}", Opcode::StackAddr), "StackAddr");
1208        assert_eq!(Opcode::StackAddr.to_string(), "stack_addr");
1209
1210        // Check the matcher.
1211        assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
1212        assert_eq!("stack_addr".parse::<Opcode>(), Ok(Opcode::StackAddr));
1213        assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
1214        assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
1215        assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
1216
1217        // Opcode is a single byte, and because Option<Opcode> originally came to 2 bytes, early on
1218        // Opcode included a variant NotAnOpcode to avoid the unnecessary bloat. Since then the Rust
1219        // compiler has brought in NonZero optimization, meaning that an enum not using the 0 value
1220        // can be optional for no size cost. We want to ensure Option<Opcode> remains small.
1221        assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
1222    }
1223
1224    #[test]
1225    fn instruction_data() {
1226        use core::mem;
1227        // The size of the `InstructionData` enum is important for performance. It should not
1228        // exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that
1229        // require more space than that. It would be fine with a data structure smaller than 16
1230        // bytes, but what are the odds of that?
1231        assert_eq!(mem::size_of::<InstructionData>(), 16);
1232    }
1233
1234    #[test]
1235    fn constraints() {
1236        let a = Opcode::Iadd.constraints();
1237        assert!(a.use_typevar_operand());
1238        assert!(!a.requires_typevar_operand());
1239        assert_eq!(a.num_fixed_results(), 1);
1240        assert_eq!(a.num_fixed_value_arguments(), 2);
1241        assert_eq!(a.result_type(0, types::I32), types::I32);
1242        assert_eq!(a.result_type(0, types::I8), types::I8);
1243        assert_eq!(
1244            a.value_argument_constraint(0, types::I32),
1245            ResolvedConstraint::Bound(types::I32)
1246        );
1247        assert_eq!(
1248            a.value_argument_constraint(1, types::I32),
1249            ResolvedConstraint::Bound(types::I32)
1250        );
1251
1252        let b = Opcode::Bitcast.constraints();
1253        assert!(!b.use_typevar_operand());
1254        assert!(!b.requires_typevar_operand());
1255        assert_eq!(b.num_fixed_results(), 1);
1256        assert_eq!(b.num_fixed_value_arguments(), 1);
1257        assert_eq!(b.result_type(0, types::I32), types::I32);
1258        assert_eq!(b.result_type(0, types::I8), types::I8);
1259        match b.value_argument_constraint(0, types::I32) {
1260            ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
1261            _ => panic!("Unexpected constraint from value_argument_constraint"),
1262        }
1263
1264        let c = Opcode::Call.constraints();
1265        assert_eq!(c.num_fixed_results(), 0);
1266        assert_eq!(c.num_fixed_value_arguments(), 0);
1267
1268        let i = Opcode::CallIndirect.constraints();
1269        assert_eq!(i.num_fixed_results(), 0);
1270        assert_eq!(i.num_fixed_value_arguments(), 1);
1271
1272        let cmp = Opcode::Icmp.constraints();
1273        assert!(cmp.use_typevar_operand());
1274        assert!(cmp.requires_typevar_operand());
1275        assert_eq!(cmp.num_fixed_results(), 1);
1276        assert_eq!(cmp.num_fixed_value_arguments(), 2);
1277        assert_eq!(cmp.result_type(0, types::I64), types::I8);
1278    }
1279
1280    #[test]
1281    fn value_set() {
1282        use crate::ir::types::*;
1283
1284        let vts = ValueTypeSet {
1285            lanes: BitSet16::from_range(0, 8),
1286            ints: BitSet8::from_range(4, 7),
1287            floats: BitSet8::from_range(0, 0),
1288            dynamic_lanes: BitSet16::from_range(0, 4),
1289        };
1290        assert!(!vts.contains(I8));
1291        assert!(vts.contains(I32));
1292        assert!(vts.contains(I64));
1293        assert!(vts.contains(I32X4));
1294        assert!(vts.contains(I32X4XN));
1295        assert!(!vts.contains(F16));
1296        assert!(!vts.contains(F32));
1297        assert!(!vts.contains(F128));
1298        assert_eq!(vts.example().to_string(), "i32");
1299
1300        let vts = ValueTypeSet {
1301            lanes: BitSet16::from_range(0, 8),
1302            ints: BitSet8::from_range(0, 0),
1303            floats: BitSet8::from_range(5, 7),
1304            dynamic_lanes: BitSet16::from_range(0, 8),
1305        };
1306        assert_eq!(vts.example().to_string(), "f32");
1307
1308        let vts = ValueTypeSet {
1309            lanes: BitSet16::from_range(1, 8),
1310            ints: BitSet8::from_range(0, 0),
1311            floats: BitSet8::from_range(5, 7),
1312            dynamic_lanes: BitSet16::from_range(0, 8),
1313        };
1314        assert_eq!(vts.example().to_string(), "f32x2");
1315
1316        let vts = ValueTypeSet {
1317            lanes: BitSet16::from_range(2, 8),
1318            ints: BitSet8::from_range(3, 7),
1319            floats: BitSet8::from_range(0, 0),
1320            dynamic_lanes: BitSet16::from_range(0, 8),
1321        };
1322        assert_eq!(vts.example().to_string(), "i32x4");
1323
1324        let vts = ValueTypeSet {
1325            // TypeSet(lanes=(1, 256), ints=(8, 64))
1326            lanes: BitSet16::from_range(0, 9),
1327            ints: BitSet8::from_range(3, 7),
1328            floats: BitSet8::from_range(0, 0),
1329            dynamic_lanes: BitSet16::from_range(0, 8),
1330        };
1331        assert!(vts.contains(I32));
1332        assert!(vts.contains(I32X4));
1333    }
1334
1335    #[test]
1336    fn instruction_data_map() {
1337        struct TestMapper;
1338
1339        impl InstructionMapper for TestMapper {
1340            fn map_value(&mut self, value: Value) -> Value {
1341                Value::from_u32(value.as_u32() + 1)
1342            }
1343
1344            fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {
1345                ValueList::new()
1346            }
1347
1348            fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1349                GlobalValue::from_u32(global_value.as_u32() + 1)
1350            }
1351
1352            fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1353                JumpTable::from_u32(jump_table.as_u32() + 1)
1354            }
1355
1356            fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1357                ExceptionTable::from_u32(exception_table.as_u32() + 1)
1358            }
1359
1360            fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {
1361                let block = Block::from_u32(42);
1362                let mut pool = ValueListPool::new();
1363                BlockCall::new(block, [], &mut pool)
1364            }
1365
1366            fn map_block(&mut self, block: Block) -> Block {
1367                Block::from_u32(block.as_u32() + 1)
1368            }
1369
1370            fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1371                FuncRef::from_u32(func_ref.as_u32() + 1)
1372            }
1373
1374            fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1375                SigRef::from_u32(sig_ref.as_u32() + 1)
1376            }
1377
1378            fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1379                StackSlot::from_u32(stack_slot.as_u32() + 1)
1380            }
1381
1382            fn map_dynamic_stack_slot(
1383                &mut self,
1384                dynamic_stack_slot: ir::DynamicStackSlot,
1385            ) -> ir::DynamicStackSlot {
1386                DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)
1387            }
1388
1389            fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1390                ir::Constant::from_u32(constant.as_u32() + 1)
1391            }
1392
1393            fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1394                ir::Immediate::from_u32(immediate.as_u32() + 1)
1395            }
1396        }
1397
1398        let mut pool = ValueListPool::new();
1399        let map = |inst: InstructionData| inst.map(TestMapper);
1400
1401        // Mapping `Value`s.
1402        assert_eq!(
1403            map(InstructionData::Binary {
1404                opcode: Opcode::Iadd,
1405                args: [Value::from_u32(10), Value::from_u32(20)]
1406            }),
1407            InstructionData::Binary {
1408                opcode: Opcode::Iadd,
1409                args: [Value::from_u32(11), Value::from_u32(21)]
1410            }
1411        );
1412
1413        // Mapping `ValueList`s and `FuncRef`s.
1414        let mut args = ValueList::new();
1415        args.push(Value::from_u32(42), &mut pool);
1416        let func_ref = FuncRef::from_u32(99);
1417        let inst = map(InstructionData::Call {
1418            opcode: Opcode::Call,
1419            args,
1420            func_ref,
1421        });
1422        let InstructionData::Call {
1423            opcode: Opcode::Call,
1424            args,
1425            func_ref,
1426        } = inst
1427        else {
1428            panic!()
1429        };
1430        assert!(args.is_empty());
1431        assert_eq!(func_ref, FuncRef::from_u32(100));
1432
1433        // Mapping `GlobalValue`s.
1434        assert_eq!(
1435            map(InstructionData::UnaryGlobalValue {
1436                opcode: Opcode::SymbolValue,
1437                global_value: GlobalValue::from_u32(4),
1438            }),
1439            InstructionData::UnaryGlobalValue {
1440                opcode: Opcode::SymbolValue,
1441                global_value: GlobalValue::from_u32(5),
1442            }
1443        );
1444
1445        // Mapping `JumpTable`s.
1446        assert_eq!(
1447            map(InstructionData::BranchTable {
1448                opcode: Opcode::BrTable,
1449                arg: Value::from_u32(0),
1450                table: JumpTable::from_u32(1),
1451            }),
1452            InstructionData::BranchTable {
1453                opcode: Opcode::BrTable,
1454                arg: Value::from_u32(1),
1455                table: JumpTable::from_u32(2),
1456            }
1457        );
1458
1459        // Mapping `ExceptionTable`s.
1460        assert_eq!(
1461            map(InstructionData::TryCall {
1462                opcode: Opcode::TryCall,
1463                args,
1464                func_ref: FuncRef::from_u32(0),
1465                exception: ExceptionTable::from_u32(1),
1466            }),
1467            InstructionData::TryCall {
1468                opcode: Opcode::TryCall,
1469                args,
1470                func_ref: FuncRef::from_u32(1),
1471                exception: ExceptionTable::from_u32(2),
1472            }
1473        );
1474
1475        // Mapping `BlockCall`s.
1476        assert_eq!(
1477            map(InstructionData::Jump {
1478                opcode: Opcode::Jump,
1479                destination: BlockCall::new(Block::from_u32(99), [], &mut pool),
1480            }),
1481            map(InstructionData::Jump {
1482                opcode: Opcode::Jump,
1483                destination: BlockCall::new(Block::from_u32(42), [], &mut pool),
1484            })
1485        );
1486
1487        // Mapping `Block`s.
1488        assert_eq!(
1489            map(InstructionData::ExceptionHandlerAddress {
1490                opcode: Opcode::GetExceptionHandlerAddress,
1491                block: Block::from_u32(1),
1492                imm: 0.into(),
1493            }),
1494            InstructionData::ExceptionHandlerAddress {
1495                opcode: Opcode::GetExceptionHandlerAddress,
1496                block: Block::from_u32(2),
1497                imm: 0.into(),
1498            },
1499        );
1500
1501        // Mapping `SigRef`s.
1502        assert_eq!(
1503            map(InstructionData::CallIndirect {
1504                opcode: Opcode::CallIndirect,
1505                args,
1506                sig_ref: SigRef::from_u32(11)
1507            }),
1508            InstructionData::CallIndirect {
1509                opcode: Opcode::CallIndirect,
1510                args: ValueList::new(),
1511                sig_ref: SigRef::from_u32(12)
1512            }
1513        );
1514
1515        // Mapping `StackSlot`s.
1516        assert_eq!(
1517            map(InstructionData::StackAddr {
1518                opcode: Opcode::StackAddr,
1519                stack_slot: StackSlot::from_u32(0),
1520                offset: 0.into()
1521            }),
1522            InstructionData::StackAddr {
1523                opcode: Opcode::StackAddr,
1524                stack_slot: StackSlot::from_u32(1),
1525                offset: 0.into()
1526            },
1527        );
1528
1529        // Mapping `DynamicStackSlot`s.
1530        assert_eq!(
1531            map(InstructionData::DynamicStackAddr {
1532                opcode: Opcode::DynamicStackAddr,
1533                dynamic_stack_slot: DynamicStackSlot::from_u32(0),
1534            }),
1535            InstructionData::DynamicStackAddr {
1536                opcode: Opcode::DynamicStackAddr,
1537                dynamic_stack_slot: DynamicStackSlot::from_u32(1),
1538            },
1539        );
1540
1541        // Mapping `Constant`s
1542        assert_eq!(
1543            map(InstructionData::UnaryConst {
1544                opcode: ir::Opcode::Vconst,
1545                constant_handle: ir::Constant::from_u32(2)
1546            }),
1547            InstructionData::UnaryConst {
1548                opcode: ir::Opcode::Vconst,
1549                constant_handle: ir::Constant::from_u32(3)
1550            },
1551        );
1552
1553        // Mapping `Immediate`s
1554        assert_eq!(
1555            map(InstructionData::Shuffle {
1556                opcode: ir::Opcode::Shuffle,
1557                args: [Value::from_u32(0), Value::from_u32(1)],
1558                imm: ir::Immediate::from_u32(41),
1559            }),
1560            InstructionData::Shuffle {
1561                opcode: ir::Opcode::Shuffle,
1562                args: [Value::from_u32(1), Value::from_u32(2)],
1563                imm: ir::Immediate::from_u32(42),
1564            },
1565        );
1566    }
1567}