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