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 alloc::vec::Vec;
10use core::fmt::{self, Display, Formatter};
11use core::ops::{Deref, DerefMut};
12use core::str::FromStr;
13
14#[cfg(feature = "enable-serde")]
15use serde::{Deserialize, Serialize};
16
17use crate::bitset::BitSet;
18use crate::entity;
19use crate::ir::{
20    self,
21    condcodes::{FloatCC, IntCC},
22    trapcode::TrapCode,
23    types, Block, FuncRef, MemFlags, SigRef, StackSlot, Type, Value,
24};
25
26/// Some instructions use an external list of argument values because there is not enough space in
27/// the 16-byte `InstructionData` struct. These value lists are stored in a memory pool in
28/// `dfg.value_lists`.
29pub type ValueList = entity::EntityList<Value>;
30
31/// Memory pool for holding value lists. See `ValueList`.
32pub type ValueListPool = entity::ListPool<Value>;
33
34/// A pair of a Block and its arguments, stored in a single EntityList internally.
35///
36/// NOTE: We don't expose either value_to_block or block_to_value outside of this module because
37/// this operation is not generally safe. However, as the two share the same underlying layout,
38/// they can be stored in the same value pool.
39///
40/// BlockCall makes use of this shared layout by storing all of its contents (a block and its
41/// argument) in a single EntityList. This is a bit better than introducing a new entity type for
42/// the pair of a block name and the arguments entity list, as we don't pay any indirection penalty
43/// to get to the argument values -- they're stored in-line with the block in the same list.
44///
45/// The BlockCall::new function guarantees this layout by requiring a block argument that's written
46/// in as the first element of the EntityList. Any subsequent entries are always assumed to be real
47/// Values.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
50pub struct BlockCall {
51    /// The underlying storage for the BlockCall. The first element of the values EntityList is
52    /// guaranteed to always be a Block encoded as a Value via BlockCall::block_to_value.
53    /// Consequently, the values entity list is never empty.
54    values: entity::EntityList<Value>,
55}
56
57impl BlockCall {
58    // NOTE: the only uses of this function should be internal to BlockCall. See the block comment
59    // on BlockCall for more context.
60    fn value_to_block(val: Value) -> Block {
61        Block::from_u32(val.as_u32())
62    }
63
64    // NOTE: the only uses of this function should be internal to BlockCall. See the block comment
65    // on BlockCall for more context.
66    fn block_to_value(block: Block) -> Value {
67        Value::from_u32(block.as_u32())
68    }
69
70    /// Construct a BlockCall with the given block and arguments.
71    pub fn new(block: Block, args: &[Value], pool: &mut ValueListPool) -> Self {
72        let mut values = ValueList::default();
73        values.push(Self::block_to_value(block), pool);
74        values.extend(args.iter().copied(), pool);
75        Self { values }
76    }
77
78    /// Return the block for this BlockCall.
79    pub fn block(&self, pool: &ValueListPool) -> Block {
80        let val = self.values.first(pool).unwrap();
81        Self::value_to_block(val)
82    }
83
84    /// Replace the block for this BlockCall.
85    pub fn set_block(&mut self, block: Block, pool: &mut ValueListPool) {
86        *self.values.get_mut(0, pool).unwrap() = Self::block_to_value(block);
87    }
88
89    /// Append an argument to the block args.
90    pub fn append_argument(&mut self, arg: Value, pool: &mut ValueListPool) {
91        self.values.push(arg, pool);
92    }
93
94    /// Return a slice for the arguments of this block.
95    pub fn args_slice<'a>(&self, pool: &'a ValueListPool) -> &'a [Value] {
96        &self.values.as_slice(pool)[1..]
97    }
98
99    /// Return a slice for the arguments of this block.
100    pub fn args_slice_mut<'a>(&'a mut self, pool: &'a mut ValueListPool) -> &'a mut [Value] {
101        &mut self.values.as_mut_slice(pool)[1..]
102    }
103
104    /// Remove the argument at ix from the argument list.
105    pub fn remove(&mut self, ix: usize, pool: &mut ValueListPool) {
106        self.values.remove(1 + ix, pool)
107    }
108
109    /// Clear out the arguments list.
110    pub fn clear(&mut self, pool: &mut ValueListPool) {
111        self.values.truncate(1, pool)
112    }
113
114    /// Appends multiple elements to the arguments.
115    pub fn extend<I>(&mut self, elements: I, pool: &mut ValueListPool)
116    where
117        I: IntoIterator<Item = Value>,
118    {
119        self.values.extend(elements, pool)
120    }
121
122    /// Return a value that can display this block call.
123    pub fn display<'a>(&self, pool: &'a ValueListPool) -> DisplayBlockCall<'a> {
124        DisplayBlockCall { block: *self, pool }
125    }
126
127    /// Deep-clone the underlying list in the same pool. The returned
128    /// list will have identical contents but changes to this list
129    /// will not change its contents or vice-versa.
130    pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {
131        Self {
132            values: self.values.deep_clone(pool),
133        }
134    }
135}
136
137/// Wrapper for the context needed to display a [BlockCall] value.
138pub struct DisplayBlockCall<'a> {
139    block: BlockCall,
140    pool: &'a ValueListPool,
141}
142
143impl<'a> Display for DisplayBlockCall<'a> {
144    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
145        write!(f, "{}", self.block.block(&self.pool))?;
146        let args = self.block.args_slice(&self.pool);
147        if !args.is_empty() {
148            write!(f, "(")?;
149            for (ix, arg) in args.iter().enumerate() {
150                if ix > 0 {
151                    write!(f, ", ")?;
152                }
153                write!(f, "{}", arg)?;
154            }
155            write!(f, ")")?;
156        }
157        Ok(())
158    }
159}
160
161// Include code generated by `cranelift-codegen/meta/src/gen_inst.rs`. This file contains:
162//
163// - The `pub enum InstructionFormat` enum with all the instruction formats.
164// - The `pub enum InstructionData` enum with all the instruction data fields.
165// - The `pub enum Opcode` definition with all known opcodes,
166// - The `const OPCODE_FORMAT: [InstructionFormat; N]` table.
167// - The private `fn opcode_name(Opcode) -> &'static str` function, and
168// - The hash table `const OPCODE_HASH_TABLE: [Opcode; N]`.
169//
170// For value type constraints:
171//
172// - The `const OPCODE_CONSTRAINTS : [OpcodeConstraints; N]` table.
173// - The `const TYPE_SETS : [ValueTypeSet; N]` table.
174// - The `const OPERAND_CONSTRAINTS : [OperandConstraint; N]` table.
175//
176include!(concat!(env!("OUT_DIR"), "/opcodes.rs"));
177
178impl Display for Opcode {
179    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
180        write!(f, "{}", opcode_name(*self))
181    }
182}
183
184impl Opcode {
185    /// Get the instruction format for this opcode.
186    pub fn format(self) -> InstructionFormat {
187        OPCODE_FORMAT[self as usize - 1]
188    }
189
190    /// Get the constraint descriptor for this opcode.
191    /// Panic if this is called on `NotAnOpcode`.
192    pub fn constraints(self) -> OpcodeConstraints {
193        OPCODE_CONSTRAINTS[self as usize - 1]
194    }
195
196    /// Returns true if the instruction is a resumable trap.
197    pub fn is_resumable_trap(&self) -> bool {
198        match self {
199            Opcode::ResumableTrap | Opcode::ResumableTrapnz => true,
200            _ => false,
201        }
202    }
203}
204
205// This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since
206// it critically depends on the `opcode_name()` function which is needed here anyway, it lives in
207// this module. This also saves us from running the build script twice to generate code for the two
208// separate crates.
209impl FromStr for Opcode {
210    type Err = &'static str;
211
212    /// Parse an Opcode name from a string.
213    fn from_str(s: &str) -> Result<Self, &'static str> {
214        use crate::constant_hash::{probe, simple_hash, Table};
215
216        impl<'a> Table<&'a str> for [Option<Opcode>] {
217            fn len(&self) -> usize {
218                self.len()
219            }
220
221            fn key(&self, idx: usize) -> Option<&'a str> {
222                self[idx].map(opcode_name)
223            }
224        }
225
226        match probe::<&str, [Option<Self>]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) {
227            Err(_) => Err("Unknown opcode"),
228            // We unwrap here because probe() should have ensured that the entry
229            // at this index is not None.
230            Ok(i) => Ok(OPCODE_HASH_TABLE[i].unwrap()),
231        }
232    }
233}
234
235/// A variable list of `Value` operands used for function call arguments and passing arguments to
236/// basic blocks.
237#[derive(Clone, Debug)]
238pub struct VariableArgs(Vec<Value>);
239
240impl VariableArgs {
241    /// Create an empty argument list.
242    pub fn new() -> Self {
243        Self(Vec::new())
244    }
245
246    /// Add an argument to the end.
247    pub fn push(&mut self, v: Value) {
248        self.0.push(v)
249    }
250
251    /// Check if the list is empty.
252    pub fn is_empty(&self) -> bool {
253        self.0.is_empty()
254    }
255
256    /// Convert this to a value list in `pool` with `fixed` prepended.
257    pub fn into_value_list(self, fixed: &[Value], pool: &mut ValueListPool) -> ValueList {
258        let mut vlist = ValueList::default();
259        vlist.extend(fixed.iter().cloned(), pool);
260        vlist.extend(self.0, pool);
261        vlist
262    }
263}
264
265// Coerce `VariableArgs` into a `&[Value]` slice.
266impl Deref for VariableArgs {
267    type Target = [Value];
268
269    fn deref(&self) -> &[Value] {
270        &self.0
271    }
272}
273
274impl DerefMut for VariableArgs {
275    fn deref_mut(&mut self) -> &mut [Value] {
276        &mut self.0
277    }
278}
279
280impl Display for VariableArgs {
281    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
282        for (i, val) in self.0.iter().enumerate() {
283            if i == 0 {
284                write!(fmt, "{}", val)?;
285            } else {
286                write!(fmt, ", {}", val)?;
287            }
288        }
289        Ok(())
290    }
291}
292
293impl Default for VariableArgs {
294    fn default() -> Self {
295        Self::new()
296    }
297}
298
299/// Analyzing an instruction.
300///
301/// Avoid large matches on instruction formats by using the methods defined here to examine
302/// instructions.
303impl InstructionData {
304    /// Get the destinations of this instruction, if it's a branch.
305    ///
306    /// `br_table` returns the empty slice.
307    pub fn branch_destination<'a>(&'a self, jump_tables: &'a ir::JumpTables) -> &[BlockCall] {
308        match self {
309            Self::Jump {
310                ref destination, ..
311            } => std::slice::from_ref(destination),
312            Self::Brif { blocks, .. } => blocks.as_slice(),
313            Self::BranchTable { table, .. } => jump_tables.get(*table).unwrap().all_branches(),
314            _ => {
315                debug_assert!(!self.opcode().is_branch());
316                &[]
317            }
318        }
319    }
320
321    /// Get a mutable slice of the destinations of this instruction, if it's a branch.
322    ///
323    /// `br_table` returns the empty slice.
324    pub fn branch_destination_mut<'a>(
325        &'a mut self,
326        jump_tables: &'a mut ir::JumpTables,
327    ) -> &mut [BlockCall] {
328        match self {
329            Self::Jump {
330                ref mut destination,
331                ..
332            } => std::slice::from_mut(destination),
333            Self::Brif { blocks, .. } => blocks.as_mut_slice(),
334            Self::BranchTable { table, .. } => {
335                jump_tables.get_mut(*table).unwrap().all_branches_mut()
336            }
337            _ => {
338                debug_assert!(!self.opcode().is_branch());
339                &mut []
340            }
341        }
342    }
343
344    /// If this is a trapping instruction, get its trap code. Otherwise, return
345    /// `None`.
346    pub fn trap_code(&self) -> Option<TrapCode> {
347        match *self {
348            Self::CondTrap { code, .. } | Self::Trap { code, .. } => Some(code),
349            _ => None,
350        }
351    }
352
353    /// If this is a control-flow instruction depending on an integer condition, gets its
354    /// condition.  Otherwise, return `None`.
355    pub fn cond_code(&self) -> Option<IntCC> {
356        match self {
357            &InstructionData::IntCompare { cond, .. }
358            | &InstructionData::IntCompareImm { cond, .. } => Some(cond),
359            _ => None,
360        }
361    }
362
363    /// If this is a control-flow instruction depending on a floating-point condition, gets its
364    /// condition.  Otherwise, return `None`.
365    pub fn fp_cond_code(&self) -> Option<FloatCC> {
366        match self {
367            &InstructionData::FloatCompare { cond, .. } => Some(cond),
368            _ => None,
369        }
370    }
371
372    /// If this is a trapping instruction, get an exclusive reference to its
373    /// trap code. Otherwise, return `None`.
374    pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> {
375        match self {
376            Self::CondTrap { code, .. } | Self::Trap { code, .. } => Some(code),
377            _ => None,
378        }
379    }
380
381    /// If this is an atomic read/modify/write instruction, return its subopcode.
382    pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> {
383        match self {
384            &InstructionData::AtomicRmw { op, .. } => Some(op),
385            _ => None,
386        }
387    }
388
389    /// If this is a load/store instruction, returns its immediate offset.
390    pub fn load_store_offset(&self) -> Option<i32> {
391        match self {
392            &InstructionData::Load { offset, .. }
393            | &InstructionData::StackLoad { offset, .. }
394            | &InstructionData::Store { offset, .. }
395            | &InstructionData::StackStore { offset, .. } => Some(offset.into()),
396            _ => None,
397        }
398    }
399
400    /// If this is a load/store instruction, return its memory flags.
401    pub fn memflags(&self) -> Option<MemFlags> {
402        match self {
403            &InstructionData::Load { flags, .. }
404            | &InstructionData::LoadNoOffset { flags, .. }
405            | &InstructionData::Store { flags, .. }
406            | &InstructionData::StoreNoOffset { flags, .. }
407            | &InstructionData::AtomicCas { flags, .. }
408            | &InstructionData::AtomicRmw { flags, .. } => Some(flags),
409            _ => None,
410        }
411    }
412
413    /// If this instruction references a stack slot, return it
414    pub fn stack_slot(&self) -> Option<StackSlot> {
415        match self {
416            &InstructionData::StackStore { stack_slot, .. }
417            | &InstructionData::StackLoad { stack_slot, .. } => Some(stack_slot),
418            _ => None,
419        }
420    }
421
422    /// Return information about a call instruction.
423    ///
424    /// Any instruction that can call another function reveals its call signature here.
425    pub fn analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a> {
426        match *self {
427            Self::Call {
428                func_ref, ref args, ..
429            } => CallInfo::Direct(func_ref, args.as_slice(pool)),
430            Self::CallIndirect {
431                sig_ref, ref args, ..
432            } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
433            _ => {
434                debug_assert!(!self.opcode().is_call());
435                CallInfo::NotACall
436            }
437        }
438    }
439
440    #[inline]
441    pub(crate) fn sign_extend_immediates(&mut self, ctrl_typevar: Type) {
442        if ctrl_typevar.is_invalid() {
443            return;
444        }
445
446        let bit_width = ctrl_typevar.bits();
447
448        match self {
449            Self::BinaryImm64 {
450                opcode,
451                arg: _,
452                imm,
453            } => {
454                if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm {
455                    imm.sign_extend_from_width(bit_width);
456                }
457            }
458            Self::IntCompareImm {
459                opcode,
460                arg: _,
461                cond,
462                imm,
463            } => {
464                debug_assert_eq!(*opcode, Opcode::IcmpImm);
465                if cond.unsigned() != *cond {
466                    imm.sign_extend_from_width(bit_width);
467                }
468            }
469            _ => {}
470        }
471    }
472}
473
474/// Information about call instructions.
475pub enum CallInfo<'a> {
476    /// This is not a call instruction.
477    NotACall,
478
479    /// This is a direct call to an external function declared in the preamble. See
480    /// `DataFlowGraph.ext_funcs`.
481    Direct(FuncRef, &'a [Value]),
482
483    /// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`.
484    Indirect(SigRef, &'a [Value]),
485}
486
487/// Value type constraints for a given opcode.
488///
489/// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and
490/// results are not determined by the format. Every `Opcode` has an associated
491/// `OpcodeConstraints` object that provides the missing details.
492#[derive(Clone, Copy)]
493pub struct OpcodeConstraints {
494    /// Flags for this opcode encoded as a bit field:
495    ///
496    /// Bits 0-2:
497    ///     Number of fixed result values. This does not include `variable_args` results as are
498    ///     produced by call instructions.
499    ///
500    /// Bit 3:
501    ///     This opcode is polymorphic and the controlling type variable can be inferred from the
502    ///     designated input operand. This is the `typevar_operand` index given to the
503    ///     `InstructionFormat` meta language object. When this bit is not set, the controlling
504    ///     type variable must be the first output value instead.
505    ///
506    /// Bit 4:
507    ///     This opcode is polymorphic and the controlling type variable does *not* appear as the
508    ///     first result type.
509    ///
510    /// Bits 5-7:
511    ///     Number of fixed value arguments. The minimum required number of value operands.
512    flags: u8,
513
514    /// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`.
515    typeset_offset: u8,
516
517    /// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first
518    /// `num_fixed_results()` entries describe the result constraints, then follows constraints for
519    /// the fixed `Value` input operands. (`num_fixed_value_arguments()` of them).
520    constraint_offset: u16,
521}
522
523impl OpcodeConstraints {
524    /// Can the controlling type variable for this opcode be inferred from the designated value
525    /// input operand?
526    /// This also implies that this opcode is polymorphic.
527    pub fn use_typevar_operand(self) -> bool {
528        (self.flags & 0x8) != 0
529    }
530
531    /// Is it necessary to look at the designated value input operand in order to determine the
532    /// controlling type variable, or is it good enough to use the first return type?
533    ///
534    /// Most polymorphic instructions produce a single result with the type of the controlling type
535    /// variable. A few polymorphic instructions either don't produce any results, or produce
536    /// results with a fixed type. These instructions return `true`.
537    pub fn requires_typevar_operand(self) -> bool {
538        (self.flags & 0x10) != 0
539    }
540
541    /// Get the number of *fixed* result values produced by this opcode.
542    /// This does not include `variable_args` produced by calls.
543    pub fn num_fixed_results(self) -> usize {
544        (self.flags & 0x7) as usize
545    }
546
547    /// Get the number of *fixed* input values required by this opcode.
548    ///
549    /// This does not include `variable_args` arguments on call and branch instructions.
550    ///
551    /// The number of fixed input values is usually implied by the instruction format, but
552    /// instruction formats that use a `ValueList` put both fixed and variable arguments in the
553    /// list. This method returns the *minimum* number of values required in the value list.
554    pub fn num_fixed_value_arguments(self) -> usize {
555        ((self.flags >> 5) & 0x7) as usize
556    }
557
558    /// Get the offset into `TYPE_SETS` for the controlling type variable.
559    /// Returns `None` if the instruction is not polymorphic.
560    fn typeset_offset(self) -> Option<usize> {
561        let offset = usize::from(self.typeset_offset);
562        if offset < TYPE_SETS.len() {
563            Some(offset)
564        } else {
565            None
566        }
567    }
568
569    /// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin.
570    fn constraint_offset(self) -> usize {
571        self.constraint_offset as usize
572    }
573
574    /// Get the value type of result number `n`, having resolved the controlling type variable to
575    /// `ctrl_type`.
576    pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
577        debug_assert!(n < self.num_fixed_results(), "Invalid result index");
578        match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {
579            ResolvedConstraint::Bound(t) => t,
580            ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {:?}", ts),
581        }
582    }
583
584    /// Get the value type of input value number `n`, having resolved the controlling type variable
585    /// to `ctrl_type`.
586    ///
587    /// Unlike results, it is possible for some input values to vary freely within a specific
588    /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant.
589    pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
590        debug_assert!(
591            n < self.num_fixed_value_arguments(),
592            "Invalid value argument index"
593        );
594        let offset = self.constraint_offset() + self.num_fixed_results();
595        OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
596    }
597
598    /// Get the typeset of allowed types for the controlling type variable in a polymorphic
599    /// instruction.
600    pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
601        self.typeset_offset().map(|offset| TYPE_SETS[offset])
602    }
603
604    /// Is this instruction polymorphic?
605    pub fn is_polymorphic(self) -> bool {
606        self.ctrl_typeset().is_some()
607    }
608}
609
610type BitSet8 = BitSet<u8>;
611type BitSet16 = BitSet<u16>;
612
613/// A value type set describes the permitted set of types for a type variable.
614#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
615pub struct ValueTypeSet {
616    /// Allowed lane sizes
617    pub lanes: BitSet16,
618    /// Allowed int widths
619    pub ints: BitSet8,
620    /// Allowed float widths
621    pub floats: BitSet8,
622    /// Allowed ref widths
623    pub refs: BitSet8,
624    /// Allowed dynamic vectors minimum lane sizes
625    pub dynamic_lanes: BitSet16,
626}
627
628impl ValueTypeSet {
629    /// Is `scalar` part of the base type set?
630    ///
631    /// Note that the base type set does not have to be included in the type set proper.
632    fn is_base_type(self, scalar: Type) -> bool {
633        let l2b = scalar.log2_lane_bits();
634        if scalar.is_int() {
635            self.ints.contains(l2b)
636        } else if scalar.is_float() {
637            self.floats.contains(l2b)
638        } else if scalar.is_ref() {
639            self.refs.contains(l2b)
640        } else {
641            false
642        }
643    }
644
645    /// Does `typ` belong to this set?
646    pub fn contains(self, typ: Type) -> bool {
647        if typ.is_dynamic_vector() {
648            let l2l = typ.log2_min_lane_count();
649            self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())
650        } else {
651            let l2l = typ.log2_lane_count();
652            self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
653        }
654    }
655
656    /// Get an example member of this type set.
657    ///
658    /// This is used for error messages to avoid suggesting invalid types.
659    pub fn example(self) -> Type {
660        let t = if self.ints.max().unwrap_or(0) > 5 {
661            types::I32
662        } else if self.floats.max().unwrap_or(0) > 5 {
663            types::F32
664        } else {
665            types::I8
666        };
667        t.by(1 << self.lanes.min().unwrap()).unwrap()
668    }
669}
670
671/// Operand constraints. This describes the value type constraints on a single `Value` operand.
672enum OperandConstraint {
673    /// This operand has a concrete value type.
674    Concrete(Type),
675
676    /// This operand can vary freely within the given type set.
677    /// The type set is identified by its index into the TYPE_SETS constant table.
678    Free(u8),
679
680    /// This operand is the same type as the controlling type variable.
681    Same,
682
683    /// This operand is `ctrlType.lane_of()`.
684    LaneOf,
685
686    /// This operand is `ctrlType.as_truthy()`.
687    AsTruthy,
688
689    /// This operand is `ctrlType.half_width()`.
690    HalfWidth,
691
692    /// This operand is `ctrlType.double_width()`.
693    DoubleWidth,
694
695    /// This operand is `ctrlType.split_lanes()`.
696    SplitLanes,
697
698    /// This operand is `ctrlType.merge_lanes()`.
699    MergeLanes,
700
701    /// This operands is `ctrlType.dynamic_to_vector()`.
702    DynamicToVector,
703
704    /// This operand is `ctrlType.narrower()`.
705    Narrower,
706
707    /// This operand is `ctrlType.wider()`.
708    Wider,
709}
710
711impl OperandConstraint {
712    /// Resolve this operand constraint into a concrete value type, given the value of the
713    /// controlling type variable.
714    pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
715        use self::OperandConstraint::*;
716        use self::ResolvedConstraint::Bound;
717        match *self {
718            Concrete(t) => Bound(t),
719            Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
720            Same => Bound(ctrl_type),
721            LaneOf => Bound(ctrl_type.lane_of()),
722            AsTruthy => Bound(ctrl_type.as_truthy()),
723            HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
724            DoubleWidth => Bound(
725                ctrl_type
726                    .double_width()
727                    .expect("invalid type for double_width"),
728            ),
729            SplitLanes => {
730                if ctrl_type.is_dynamic_vector() {
731                    Bound(
732                        ctrl_type
733                            .dynamic_to_vector()
734                            .expect("invalid type for dynamic_to_vector")
735                            .split_lanes()
736                            .expect("invalid type for split_lanes")
737                            .vector_to_dynamic()
738                            .expect("invalid dynamic type"),
739                    )
740                } else {
741                    Bound(
742                        ctrl_type
743                            .split_lanes()
744                            .expect("invalid type for split_lanes"),
745                    )
746                }
747            }
748            MergeLanes => {
749                if ctrl_type.is_dynamic_vector() {
750                    Bound(
751                        ctrl_type
752                            .dynamic_to_vector()
753                            .expect("invalid type for dynamic_to_vector")
754                            .merge_lanes()
755                            .expect("invalid type for merge_lanes")
756                            .vector_to_dynamic()
757                            .expect("invalid dynamic type"),
758                    )
759                } else {
760                    Bound(
761                        ctrl_type
762                            .merge_lanes()
763                            .expect("invalid type for merge_lanes"),
764                    )
765                }
766            }
767            DynamicToVector => Bound(
768                ctrl_type
769                    .dynamic_to_vector()
770                    .expect("invalid type for dynamic_to_vector"),
771            ),
772            Narrower => {
773                let ctrl_type_bits = ctrl_type.log2_lane_bits();
774                let mut tys = ValueTypeSet::default();
775
776                // We're testing scalar values, only.
777                tys.lanes = BitSet::from_range(0, 1);
778
779                if ctrl_type.is_int() {
780                    // The upper bound in from_range is exclusive, and we want to exclude the
781                    // control type to construct the interval of [I8, ctrl_type).
782                    tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);
783                } else if ctrl_type.is_float() {
784                    // The upper bound in from_range is exclusive, and we want to exclude the
785                    // control type to construct the interval of [F32, ctrl_type).
786                    tys.floats = BitSet8::from_range(5, ctrl_type_bits as u8);
787                } else {
788                    panic!("The Narrower constraint only operates on floats or ints");
789                }
790                ResolvedConstraint::Free(tys)
791            }
792            Wider => {
793                let ctrl_type_bits = ctrl_type.log2_lane_bits();
794                let mut tys = ValueTypeSet::default();
795
796                // We're testing scalar values, only.
797                tys.lanes = BitSet::from_range(0, 1);
798
799                if ctrl_type.is_int() {
800                    let lower_bound = ctrl_type_bits as u8 + 1;
801                    // The largest integer type we can represent in `BitSet8` is I128, which is
802                    // represented by bit 7 in the bit set. Adding one to exclude I128 from the
803                    // lower bound would overflow as 2^8 doesn't fit in a u8, but this would
804                    // already describe the empty set so instead we leave `ints` in its default
805                    // empty state.
806                    if lower_bound < BitSet8::bits() as u8 {
807                        // The interval should include all types wider than `ctrl_type`, so we use
808                        // `2^8` as the upper bound, and add one to the bits of `ctrl_type` to define
809                        // the interval `(ctrl_type, I128]`.
810                        tys.ints = BitSet8::from_range(lower_bound, 8);
811                    }
812                } else if ctrl_type.is_float() {
813                    // The interval should include all float types wider than `ctrl_type`, so we
814                    // use `2^7` as the upper bound, and add one to the bits of `ctrl_type` to
815                    // define the interval `(ctrl_type, F64]`.
816                    tys.floats = BitSet8::from_range(ctrl_type_bits as u8 + 1, 7);
817                } else {
818                    panic!("The Wider constraint only operates on floats or ints");
819                }
820
821                ResolvedConstraint::Free(tys)
822            }
823        }
824    }
825}
826
827/// The type constraint on a value argument once the controlling type variable is known.
828#[derive(Copy, Clone, Debug, PartialEq, Eq)]
829pub enum ResolvedConstraint {
830    /// The operand is bound to a known type.
831    Bound(Type),
832    /// The operand type can vary freely within the given set.
833    Free(ValueTypeSet),
834}
835
836#[cfg(test)]
837mod tests {
838    use super::*;
839    use alloc::string::ToString;
840
841    #[test]
842    fn inst_data_is_copy() {
843        fn is_copy<T: Copy>() {}
844        is_copy::<InstructionData>();
845    }
846
847    #[test]
848    fn inst_data_size() {
849        // The size of `InstructionData` is performance sensitive, so make sure
850        // we don't regress it unintentionally.
851        assert_eq!(std::mem::size_of::<InstructionData>(), 16);
852    }
853
854    #[test]
855    fn opcodes() {
856        use core::mem;
857
858        let x = Opcode::Iadd;
859        let mut y = Opcode::Isub;
860
861        assert!(x != y);
862        y = Opcode::Iadd;
863        assert_eq!(x, y);
864        assert_eq!(x.format(), InstructionFormat::Binary);
865
866        assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm");
867        assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm");
868
869        // Check the matcher.
870        assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
871        assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm));
872        assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
873        assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
874        assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
875
876        // Opcode is a single byte, and because Option<Opcode> originally came to 2 bytes, early on
877        // Opcode included a variant NotAnOpcode to avoid the unnecessary bloat. Since then the Rust
878        // compiler has brought in NonZero optimization, meaning that an enum not using the 0 value
879        // can be optional for no size cost. We want to ensure Option<Opcode> remains small.
880        assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
881    }
882
883    #[test]
884    fn instruction_data() {
885        use core::mem;
886        // The size of the `InstructionData` enum is important for performance. It should not
887        // exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that
888        // require more space than that. It would be fine with a data structure smaller than 16
889        // bytes, but what are the odds of that?
890        assert_eq!(mem::size_of::<InstructionData>(), 16);
891    }
892
893    #[test]
894    fn constraints() {
895        let a = Opcode::Iadd.constraints();
896        assert!(a.use_typevar_operand());
897        assert!(!a.requires_typevar_operand());
898        assert_eq!(a.num_fixed_results(), 1);
899        assert_eq!(a.num_fixed_value_arguments(), 2);
900        assert_eq!(a.result_type(0, types::I32), types::I32);
901        assert_eq!(a.result_type(0, types::I8), types::I8);
902        assert_eq!(
903            a.value_argument_constraint(0, types::I32),
904            ResolvedConstraint::Bound(types::I32)
905        );
906        assert_eq!(
907            a.value_argument_constraint(1, types::I32),
908            ResolvedConstraint::Bound(types::I32)
909        );
910
911        let b = Opcode::Bitcast.constraints();
912        assert!(!b.use_typevar_operand());
913        assert!(!b.requires_typevar_operand());
914        assert_eq!(b.num_fixed_results(), 1);
915        assert_eq!(b.num_fixed_value_arguments(), 1);
916        assert_eq!(b.result_type(0, types::I32), types::I32);
917        assert_eq!(b.result_type(0, types::I8), types::I8);
918        match b.value_argument_constraint(0, types::I32) {
919            ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
920            _ => panic!("Unexpected constraint from value_argument_constraint"),
921        }
922
923        let c = Opcode::Call.constraints();
924        assert_eq!(c.num_fixed_results(), 0);
925        assert_eq!(c.num_fixed_value_arguments(), 0);
926
927        let i = Opcode::CallIndirect.constraints();
928        assert_eq!(i.num_fixed_results(), 0);
929        assert_eq!(i.num_fixed_value_arguments(), 1);
930
931        let cmp = Opcode::Icmp.constraints();
932        assert!(cmp.use_typevar_operand());
933        assert!(cmp.requires_typevar_operand());
934        assert_eq!(cmp.num_fixed_results(), 1);
935        assert_eq!(cmp.num_fixed_value_arguments(), 2);
936    }
937
938    #[test]
939    fn value_set() {
940        use crate::ir::types::*;
941
942        let vts = ValueTypeSet {
943            lanes: BitSet16::from_range(0, 8),
944            ints: BitSet8::from_range(4, 7),
945            floats: BitSet8::from_range(0, 0),
946            refs: BitSet8::from_range(5, 7),
947            dynamic_lanes: BitSet16::from_range(0, 4),
948        };
949        assert!(!vts.contains(I8));
950        assert!(vts.contains(I32));
951        assert!(vts.contains(I64));
952        assert!(vts.contains(I32X4));
953        assert!(vts.contains(I32X4XN));
954        assert!(!vts.contains(F32));
955        assert!(vts.contains(R32));
956        assert!(vts.contains(R64));
957        assert_eq!(vts.example().to_string(), "i32");
958
959        let vts = ValueTypeSet {
960            lanes: BitSet16::from_range(0, 8),
961            ints: BitSet8::from_range(0, 0),
962            floats: BitSet8::from_range(5, 7),
963            refs: BitSet8::from_range(0, 0),
964            dynamic_lanes: BitSet16::from_range(0, 8),
965        };
966        assert_eq!(vts.example().to_string(), "f32");
967
968        let vts = ValueTypeSet {
969            lanes: BitSet16::from_range(1, 8),
970            ints: BitSet8::from_range(0, 0),
971            floats: BitSet8::from_range(5, 7),
972            refs: BitSet8::from_range(0, 0),
973            dynamic_lanes: BitSet16::from_range(0, 8),
974        };
975        assert_eq!(vts.example().to_string(), "f32x2");
976
977        let vts = ValueTypeSet {
978            lanes: BitSet16::from_range(2, 8),
979            ints: BitSet8::from_range(3, 7),
980            floats: BitSet8::from_range(0, 0),
981            refs: BitSet8::from_range(0, 0),
982            dynamic_lanes: BitSet16::from_range(0, 8),
983        };
984        assert_eq!(vts.example().to_string(), "i32x4");
985
986        let vts = ValueTypeSet {
987            // TypeSet(lanes=(1, 256), ints=(8, 64))
988            lanes: BitSet16::from_range(0, 9),
989            ints: BitSet8::from_range(3, 7),
990            floats: BitSet8::from_range(0, 0),
991            refs: BitSet8::from_range(0, 0),
992            dynamic_lanes: BitSet16::from_range(0, 8),
993        };
994        assert!(vts.contains(I32));
995        assert!(vts.contains(I32X4));
996        assert!(!vts.contains(R32));
997        assert!(!vts.contains(R64));
998    }
999}