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