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