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