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::Store { offset, .. }
308             | &InstructionData::StackLoad { offset, .. }
309             | &InstructionData::StackStore { offset, .. }
310             | &InstructionData::TableAddr { offset, .. } => Some(DataValue::from(offset)),
311             // 64-bit.
312             &InstructionData::UnaryImm { imm, .. }
313             | &InstructionData::BinaryImm64 { imm, .. }
314             | &InstructionData::IntCompareImm { imm, .. } => Some(DataValue::from(imm.bits())),
315             &InstructionData::UnaryIeee64 { imm, .. } => Some(DataValue::from(imm)),
316             // 128-bit; though these immediates are present logically in the IR they are not
317             // included in the `InstructionData` for memory-size reasons. This case, returning
318             // `None`, is left here to alert users of this method that they should retrieve the
319             // value using the `DataFlowGraph`.
320             &InstructionData::Shuffle { imm: _, .. } => None,
321             _ => None,
322         }
323     }
324 
325     /// If this is a trapping instruction, get its trap code. Otherwise, return
326     /// `None`.
327     pub fn trap_code(&self) -> Option<TrapCode> {
328         match *self {
329             Self::CondTrap { code, .. }
330             | Self::FloatCondTrap { code, .. }
331             | Self::IntCondTrap { code, .. }
332             | Self::Trap { code, .. } => Some(code),
333             _ => None,
334         }
335     }
336 
337     /// If this is a control-flow instruction depending on an integer condition, gets its
338     /// condition.  Otherwise, return `None`.
339     pub fn cond_code(&self) -> Option<IntCC> {
340         match self {
341             &InstructionData::IntCond { cond, .. }
342             | &InstructionData::BranchIcmp { cond, .. }
343             | &InstructionData::IntCompare { cond, .. }
344             | &InstructionData::IntCondTrap { cond, .. }
345             | &InstructionData::BranchInt { cond, .. }
346             | &InstructionData::IntSelect { cond, .. }
347             | &InstructionData::IntCompareImm { cond, .. } => Some(cond),
348             _ => None,
349         }
350     }
351 
352     /// If this is a control-flow instruction depending on a floating-point condition, gets its
353     /// condition.  Otherwise, return `None`.
354     pub fn fp_cond_code(&self) -> Option<FloatCC> {
355         match self {
356             &InstructionData::BranchFloat { cond, .. }
357             | &InstructionData::FloatCompare { cond, .. }
358             | &InstructionData::FloatCond { cond, .. }
359             | &InstructionData::FloatCondTrap { cond, .. } => Some(cond),
360             _ => None,
361         }
362     }
363 
364     /// If this is a trapping instruction, get an exclusive reference to its
365     /// trap code. Otherwise, return `None`.
366     pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> {
367         match self {
368             Self::CondTrap { code, .. }
369             | Self::FloatCondTrap { code, .. }
370             | Self::IntCondTrap { code, .. }
371             | Self::Trap { code, .. } => Some(code),
372             _ => None,
373         }
374     }
375 
376     /// If this is an atomic read/modify/write instruction, return its subopcode.
377     pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> {
378         match self {
379             &InstructionData::AtomicRmw { op, .. } => Some(op),
380             _ => None,
381         }
382     }
383 
384     /// If this is a load/store instruction, returns its immediate offset.
385     pub fn load_store_offset(&self) -> Option<i32> {
386         match self {
387             &InstructionData::Load { offset, .. }
388             | &InstructionData::StackLoad { offset, .. }
389             | &InstructionData::Store { offset, .. }
390             | &InstructionData::StackStore { offset, .. } => Some(offset.into()),
391             _ => None,
392         }
393     }
394 
395     /// If this is a load/store instruction, return its memory flags.
396     pub fn memflags(&self) -> Option<MemFlags> {
397         match self {
398             &InstructionData::Load { flags, .. }
399             | &InstructionData::LoadNoOffset { flags, .. }
400             | &InstructionData::Store { flags, .. }
401             | &InstructionData::StoreNoOffset { flags, .. } => Some(flags),
402             _ => None,
403         }
404     }
405 
406     /// If this instruction references a stack slot, return it
407     pub fn stack_slot(&self) -> Option<StackSlot> {
408         match self {
409             &InstructionData::StackStore { stack_slot, .. }
410             | &InstructionData::StackLoad { stack_slot, .. } => Some(stack_slot),
411             _ => None,
412         }
413     }
414 
415     /// Return information about a call instruction.
416     ///
417     /// Any instruction that can call another function reveals its call signature here.
418     pub fn analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a> {
419         match *self {
420             Self::Call {
421                 func_ref, ref args, ..
422             } => CallInfo::Direct(func_ref, args.as_slice(pool)),
423             Self::CallIndirect {
424                 sig_ref, ref args, ..
425             } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
426             _ => {
427                 debug_assert!(!self.opcode().is_call());
428                 CallInfo::NotACall
429             }
430         }
431     }
432 
433     #[inline]
434     pub(crate) fn sign_extend_immediates(&mut self, ctrl_typevar: Type) {
435         if ctrl_typevar.is_invalid() {
436             return;
437         }
438 
439         let bit_width = ctrl_typevar.bits();
440 
441         match self {
442             Self::BinaryImm64 {
443                 opcode,
444                 arg: _,
445                 imm,
446             } => {
447                 if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm {
448                     imm.sign_extend_from_width(bit_width);
449                 }
450             }
451             Self::IntCompareImm {
452                 opcode,
453                 arg: _,
454                 cond,
455                 imm,
456             } => {
457                 debug_assert_eq!(*opcode, Opcode::IcmpImm);
458                 if cond.unsigned() != *cond {
459                     imm.sign_extend_from_width(bit_width);
460                 }
461             }
462             _ => {}
463         }
464     }
465 }
466 
467 /// Information about branch and jump instructions.
468 pub enum BranchInfo<'a> {
469     /// This is not a branch or jump instruction.
470     /// This instruction will not transfer control to another block in the function, but it may still
471     /// affect control flow by returning or trapping.
472     NotABranch,
473 
474     /// This is a branch or jump to a single destination block, possibly taking value arguments.
475     SingleDest(Block, &'a [Value]),
476 
477     /// This is a jump table branch which can have many destination blocks and maybe one default block.
478     Table(JumpTable, Option<Block>),
479 }
480 
481 /// Information about call instructions.
482 pub enum CallInfo<'a> {
483     /// This is not a call instruction.
484     NotACall,
485 
486     /// This is a direct call to an external function declared in the preamble. See
487     /// `DataFlowGraph.ext_funcs`.
488     Direct(FuncRef, &'a [Value]),
489 
490     /// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`.
491     Indirect(SigRef, &'a [Value]),
492 }
493 
494 /// Value type constraints for a given opcode.
495 ///
496 /// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and
497 /// results are not determined by the format. Every `Opcode` has an associated
498 /// `OpcodeConstraints` object that provides the missing details.
499 #[derive(Clone, Copy)]
500 pub struct OpcodeConstraints {
501     /// Flags for this opcode encoded as a bit field:
502     ///
503     /// Bits 0-2:
504     ///     Number of fixed result values. This does not include `variable_args` results as are
505     ///     produced by call instructions.
506     ///
507     /// Bit 3:
508     ///     This opcode is polymorphic and the controlling type variable can be inferred from the
509     ///     designated input operand. This is the `typevar_operand` index given to the
510     ///     `InstructionFormat` meta language object. When this bit is not set, the controlling
511     ///     type variable must be the first output value instead.
512     ///
513     /// Bit 4:
514     ///     This opcode is polymorphic and the controlling type variable does *not* appear as the
515     ///     first result type.
516     ///
517     /// Bits 5-7:
518     ///     Number of fixed value arguments. The minimum required number of value operands.
519     flags: u8,
520 
521     /// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`.
522     typeset_offset: u8,
523 
524     /// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first
525     /// `num_fixed_results()` entries describe the result constraints, then follows constraints for
526     /// the fixed `Value` input operands. (`num_fixed_value_arguments()` of them).
527     constraint_offset: u16,
528 }
529 
530 impl OpcodeConstraints {
531     /// Can the controlling type variable for this opcode be inferred from the designated value
532     /// input operand?
533     /// This also implies that this opcode is polymorphic.
534     pub fn use_typevar_operand(self) -> bool {
535         (self.flags & 0x8) != 0
536     }
537 
538     /// Is it necessary to look at the designated value input operand in order to determine the
539     /// controlling type variable, or is it good enough to use the first return type?
540     ///
541     /// Most polymorphic instructions produce a single result with the type of the controlling type
542     /// variable. A few polymorphic instructions either don't produce any results, or produce
543     /// results with a fixed type. These instructions return `true`.
544     pub fn requires_typevar_operand(self) -> bool {
545         (self.flags & 0x10) != 0
546     }
547 
548     /// Get the number of *fixed* result values produced by this opcode.
549     /// This does not include `variable_args` produced by calls.
550     pub fn num_fixed_results(self) -> usize {
551         (self.flags & 0x7) as usize
552     }
553 
554     /// Get the number of *fixed* input values required by this opcode.
555     ///
556     /// This does not include `variable_args` arguments on call and branch instructions.
557     ///
558     /// The number of fixed input values is usually implied by the instruction format, but
559     /// instruction formats that use a `ValueList` put both fixed and variable arguments in the
560     /// list. This method returns the *minimum* number of values required in the value list.
561     pub fn num_fixed_value_arguments(self) -> usize {
562         ((self.flags >> 5) & 0x7) as usize
563     }
564 
565     /// Get the offset into `TYPE_SETS` for the controlling type variable.
566     /// Returns `None` if the instruction is not polymorphic.
567     fn typeset_offset(self) -> Option<usize> {
568         let offset = usize::from(self.typeset_offset);
569         if offset < TYPE_SETS.len() {
570             Some(offset)
571         } else {
572             None
573         }
574     }
575 
576     /// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin.
577     fn constraint_offset(self) -> usize {
578         self.constraint_offset as usize
579     }
580 
581     /// Get the value type of result number `n`, having resolved the controlling type variable to
582     /// `ctrl_type`.
583     pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
584         debug_assert!(n < self.num_fixed_results(), "Invalid result index");
585         if let ResolvedConstraint::Bound(t) =
586             OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type)
587         {
588             t
589         } else {
590             panic!("Result constraints can't be free");
591         }
592     }
593 
594     /// Get the value type of input value number `n`, having resolved the controlling type variable
595     /// to `ctrl_type`.
596     ///
597     /// Unlike results, it is possible for some input values to vary freely within a specific
598     /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant.
599     pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
600         debug_assert!(
601             n < self.num_fixed_value_arguments(),
602             "Invalid value argument index"
603         );
604         let offset = self.constraint_offset() + self.num_fixed_results();
605         OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
606     }
607 
608     /// Get the typeset of allowed types for the controlling type variable in a polymorphic
609     /// instruction.
610     pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
611         self.typeset_offset().map(|offset| TYPE_SETS[offset])
612     }
613 
614     /// Is this instruction polymorphic?
615     pub fn is_polymorphic(self) -> bool {
616         self.ctrl_typeset().is_some()
617     }
618 }
619 
620 type BitSet8 = BitSet<u8>;
621 type BitSet16 = BitSet<u16>;
622 
623 /// A value type set describes the permitted set of types for a type variable.
624 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
625 pub struct ValueTypeSet {
626     /// Allowed lane sizes
627     pub lanes: BitSet16,
628     /// Allowed int widths
629     pub ints: BitSet8,
630     /// Allowed float widths
631     pub floats: BitSet8,
632     /// Allowed bool widths
633     pub bools: BitSet8,
634     /// Allowed ref widths
635     pub refs: BitSet8,
636 }
637 
638 impl ValueTypeSet {
639     /// Is `scalar` part of the base type set?
640     ///
641     /// Note that the base type set does not have to be included in the type set proper.
642     fn is_base_type(self, scalar: Type) -> bool {
643         let l2b = scalar.log2_lane_bits();
644         if scalar.is_int() {
645             self.ints.contains(l2b)
646         } else if scalar.is_float() {
647             self.floats.contains(l2b)
648         } else if scalar.is_bool() {
649             self.bools.contains(l2b)
650         } else if scalar.is_ref() {
651             self.refs.contains(l2b)
652         } else {
653             false
654         }
655     }
656 
657     /// Does `typ` belong to this set?
658     pub fn contains(self, typ: Type) -> bool {
659         let l2l = typ.log2_lane_count();
660         self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
661     }
662 
663     /// Get an example member of this type set.
664     ///
665     /// This is used for error messages to avoid suggesting invalid types.
666     pub fn example(self) -> Type {
667         let t = if self.ints.max().unwrap_or(0) > 5 {
668             types::I32
669         } else if self.floats.max().unwrap_or(0) > 5 {
670             types::F32
671         } else if self.bools.max().unwrap_or(0) > 5 {
672             types::B32
673         } else {
674             types::B1
675         };
676         t.by(1 << self.lanes.min().unwrap()).unwrap()
677     }
678 }
679 
680 /// Operand constraints. This describes the value type constraints on a single `Value` operand.
681 enum OperandConstraint {
682     /// This operand has a concrete value type.
683     Concrete(Type),
684 
685     /// This operand can vary freely within the given type set.
686     /// The type set is identified by its index into the TYPE_SETS constant table.
687     Free(u8),
688 
689     /// This operand is the same type as the controlling type variable.
690     Same,
691 
692     /// This operand is `ctrlType.lane_of()`.
693     LaneOf,
694 
695     /// This operand is `ctrlType.as_bool()`.
696     AsBool,
697 
698     /// This operand is `ctrlType.half_width()`.
699     HalfWidth,
700 
701     /// This operand is `ctrlType.double_width()`.
702     DoubleWidth,
703 
704     /// This operand is `ctrlType.half_vector()`.
705     HalfVector,
706 
707     /// This operand is `ctrlType.double_vector()`.
708     DoubleVector,
709 
710     /// This operand is `ctrlType.split_lanes()`.
711     SplitLanes,
712 
713     /// This operand is `ctrlType.merge_lanes()`.
714     MergeLanes,
715 }
716 
717 impl OperandConstraint {
718     /// Resolve this operand constraint into a concrete value type, given the value of the
719     /// controlling type variable.
720     pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
721         use self::OperandConstraint::*;
722         use self::ResolvedConstraint::Bound;
723         match *self {
724             Concrete(t) => Bound(t),
725             Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
726             Same => Bound(ctrl_type),
727             LaneOf => Bound(ctrl_type.lane_of()),
728             AsBool => Bound(ctrl_type.as_bool()),
729             HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
730             DoubleWidth => Bound(
731                 ctrl_type
732                     .double_width()
733                     .expect("invalid type for double_width"),
734             ),
735             HalfVector => Bound(
736                 ctrl_type
737                     .half_vector()
738                     .expect("invalid type for half_vector"),
739             ),
740             DoubleVector => Bound(ctrl_type.by(2).expect("invalid type for double_vector")),
741             SplitLanes => Bound(
742                 ctrl_type
743                     .split_lanes()
744                     .expect("invalid type for split_lanes"),
745             ),
746             MergeLanes => Bound(
747                 ctrl_type
748                     .merge_lanes()
749                     .expect("invalid type for merge_lanes"),
750             ),
751         }
752     }
753 }
754 
755 /// The type constraint on a value argument once the controlling type variable is known.
756 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
757 pub enum ResolvedConstraint {
758     /// The operand is bound to a known type.
759     Bound(Type),
760     /// The operand type can vary freely within the given set.
761     Free(ValueTypeSet),
762 }
763 
764 #[cfg(test)]
765 mod tests {
766     use super::*;
767     use alloc::string::ToString;
768 
769     #[test]
770     fn opcodes() {
771         use core::mem;
772 
773         let x = Opcode::Iadd;
774         let mut y = Opcode::Isub;
775 
776         assert!(x != y);
777         y = Opcode::Iadd;
778         assert_eq!(x, y);
779         assert_eq!(x.format(), InstructionFormat::Binary);
780 
781         assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm");
782         assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm");
783 
784         // Check the matcher.
785         assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
786         assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm));
787         assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
788         assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
789         assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
790 
791         // Opcode is a single byte, and because Option<Opcode> originally came to 2 bytes, early on
792         // Opcode included a variant NotAnOpcode to avoid the unnecessary bloat. Since then the Rust
793         // compiler has brought in NonZero optimization, meaning that an enum not using the 0 value
794         // can be optional for no size cost. We want to ensure Option<Opcode> remains small.
795         assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
796     }
797 
798     #[test]
799     fn instruction_data() {
800         use core::mem;
801         // The size of the `InstructionData` enum is important for performance. It should not
802         // exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that
803         // require more space than that. It would be fine with a data structure smaller than 16
804         // bytes, but what are the odds of that?
805         assert_eq!(mem::size_of::<InstructionData>(), 16);
806     }
807 
808     #[test]
809     fn constraints() {
810         let a = Opcode::Iadd.constraints();
811         assert!(a.use_typevar_operand());
812         assert!(!a.requires_typevar_operand());
813         assert_eq!(a.num_fixed_results(), 1);
814         assert_eq!(a.num_fixed_value_arguments(), 2);
815         assert_eq!(a.result_type(0, types::I32), types::I32);
816         assert_eq!(a.result_type(0, types::I8), types::I8);
817         assert_eq!(
818             a.value_argument_constraint(0, types::I32),
819             ResolvedConstraint::Bound(types::I32)
820         );
821         assert_eq!(
822             a.value_argument_constraint(1, types::I32),
823             ResolvedConstraint::Bound(types::I32)
824         );
825 
826         let b = Opcode::Bitcast.constraints();
827         assert!(!b.use_typevar_operand());
828         assert!(!b.requires_typevar_operand());
829         assert_eq!(b.num_fixed_results(), 1);
830         assert_eq!(b.num_fixed_value_arguments(), 1);
831         assert_eq!(b.result_type(0, types::I32), types::I32);
832         assert_eq!(b.result_type(0, types::I8), types::I8);
833         match b.value_argument_constraint(0, types::I32) {
834             ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
835             _ => panic!("Unexpected constraint from value_argument_constraint"),
836         }
837 
838         let c = Opcode::Call.constraints();
839         assert_eq!(c.num_fixed_results(), 0);
840         assert_eq!(c.num_fixed_value_arguments(), 0);
841 
842         let i = Opcode::CallIndirect.constraints();
843         assert_eq!(i.num_fixed_results(), 0);
844         assert_eq!(i.num_fixed_value_arguments(), 1);
845 
846         let cmp = Opcode::Icmp.constraints();
847         assert!(cmp.use_typevar_operand());
848         assert!(cmp.requires_typevar_operand());
849         assert_eq!(cmp.num_fixed_results(), 1);
850         assert_eq!(cmp.num_fixed_value_arguments(), 2);
851     }
852 
853     #[test]
854     fn value_set() {
855         use crate::ir::types::*;
856 
857         let vts = ValueTypeSet {
858             lanes: BitSet16::from_range(0, 8),
859             ints: BitSet8::from_range(4, 7),
860             floats: BitSet8::from_range(0, 0),
861             bools: BitSet8::from_range(3, 7),
862             refs: BitSet8::from_range(5, 7),
863         };
864         assert!(!vts.contains(I8));
865         assert!(vts.contains(I32));
866         assert!(vts.contains(I64));
867         assert!(vts.contains(I32X4));
868         assert!(!vts.contains(F32));
869         assert!(!vts.contains(B1));
870         assert!(vts.contains(B8));
871         assert!(vts.contains(B64));
872         assert!(vts.contains(R32));
873         assert!(vts.contains(R64));
874         assert_eq!(vts.example().to_string(), "i32");
875 
876         let vts = ValueTypeSet {
877             lanes: BitSet16::from_range(0, 8),
878             ints: BitSet8::from_range(0, 0),
879             floats: BitSet8::from_range(5, 7),
880             bools: BitSet8::from_range(3, 7),
881             refs: BitSet8::from_range(0, 0),
882         };
883         assert_eq!(vts.example().to_string(), "f32");
884 
885         let vts = ValueTypeSet {
886             lanes: BitSet16::from_range(1, 8),
887             ints: BitSet8::from_range(0, 0),
888             floats: BitSet8::from_range(5, 7),
889             bools: BitSet8::from_range(3, 7),
890             refs: BitSet8::from_range(0, 0),
891         };
892         assert_eq!(vts.example().to_string(), "f32x2");
893 
894         let vts = ValueTypeSet {
895             lanes: BitSet16::from_range(2, 8),
896             ints: BitSet8::from_range(0, 0),
897             floats: BitSet8::from_range(0, 0),
898             bools: BitSet8::from_range(3, 7),
899             refs: BitSet8::from_range(0, 0),
900         };
901         assert!(!vts.contains(B32X2));
902         assert!(vts.contains(B32X4));
903         assert_eq!(vts.example().to_string(), "b32x4");
904 
905         let vts = ValueTypeSet {
906             // TypeSet(lanes=(1, 256), ints=(8, 64))
907             lanes: BitSet16::from_range(0, 9),
908             ints: BitSet8::from_range(3, 7),
909             floats: BitSet8::from_range(0, 0),
910             bools: BitSet8::from_range(0, 0),
911             refs: BitSet8::from_range(0, 0),
912         };
913         assert!(vts.contains(I32));
914         assert!(vts.contains(I32X4));
915         assert!(!vts.contains(R32));
916         assert!(!vts.contains(R64));
917     }
918 }
919