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