Path: blob/main/cranelift/codegen/src/ir/instructions.rs
1693 views
//! Instruction formats and opcodes.1//!2//! The `instructions` module contains definitions for instruction formats, opcodes, and the3//! in-memory representation of IR instructions.4//!5//! A large part of this module is auto-generated from the instruction descriptions in the meta6//! directory.78use crate::constant_hash::Table;9use alloc::vec::Vec;10use core::fmt::{self, Display, Formatter};11use core::ops::{Deref, DerefMut};12use core::str::FromStr;1314#[cfg(feature = "enable-serde")]15use serde_derive::{Deserialize, Serialize};1617use crate::bitset::ScalarBitSet;18use crate::entity;19use crate::ir::{20self, Block, ExceptionTable, ExceptionTables, FuncRef, MemFlags, SigRef, StackSlot, Type,21Value,22condcodes::{FloatCC, IntCC},23trapcode::TrapCode,24types,25};2627/// Some instructions use an external list of argument values because there is not enough space in28/// the 16-byte `InstructionData` struct. These value lists are stored in a memory pool in29/// `dfg.value_lists`.30pub type ValueList = entity::EntityList<Value>;3132/// Memory pool for holding value lists. See `ValueList`.33pub type ValueListPool = entity::ListPool<Value>;3435/// A pair of a Block and its arguments, stored in a single EntityList internally.36///37/// Block arguments are semantically a `BlockArg`.38///39/// NOTE: We don't expose either value_to_block or block_to_value outside of this module because40/// this operation is not generally safe. However, as the two share the same underlying layout,41/// they can be stored in the same value pool.42///43/// BlockCall makes use of this shared layout by storing all of its contents (a block and its44/// argument) in a single EntityList. This is a bit better than introducing a new entity type for45/// the pair of a block name and the arguments entity list, as we don't pay any indirection penalty46/// to get to the argument values -- they're stored in-line with the block in the same list.47///48/// The BlockCall::new function guarantees this layout by requiring a block argument that's written49/// in as the first element of the EntityList. Any subsequent entries are always assumed to be real50/// Values.51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]52#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]53pub struct BlockCall {54/// The underlying storage for the BlockCall. The first element of the values EntityList is55/// guaranteed to always be a Block encoded as a Value via BlockCall::block_to_value.56/// Consequently, the values entity list is never empty.57values: entity::EntityList<Value>,58}5960impl BlockCall {61// NOTE: the only uses of this function should be internal to BlockCall. See the block comment62// on BlockCall for more context.63fn value_to_block(val: Value) -> Block {64Block::from_u32(val.as_u32())65}6667// NOTE: the only uses of this function should be internal to BlockCall. See the block comment68// on BlockCall for more context.69fn block_to_value(block: Block) -> Value {70Value::from_u32(block.as_u32())71}7273/// Construct a BlockCall with the given block and arguments.74pub fn new(75block: Block,76args: impl IntoIterator<Item = BlockArg>,77pool: &mut ValueListPool,78) -> Self {79let mut values = ValueList::default();80values.push(Self::block_to_value(block), pool);81values.extend(args.into_iter().map(|arg| arg.encode_as_value()), pool);82Self { values }83}8485/// Return the block for this BlockCall.86pub fn block(&self, pool: &ValueListPool) -> Block {87let val = self.values.first(pool).unwrap();88Self::value_to_block(val)89}9091/// Replace the block for this BlockCall.92pub fn set_block(&mut self, block: Block, pool: &mut ValueListPool) {93*self.values.get_mut(0, pool).unwrap() = Self::block_to_value(block);94}9596/// Append an argument to the block args.97pub fn append_argument(&mut self, arg: impl Into<BlockArg>, pool: &mut ValueListPool) {98self.values.push(arg.into().encode_as_value(), pool);99}100101/// Return the length of the argument list.102pub fn len(&self, pool: &ValueListPool) -> usize {103self.values.len(pool) - 1104}105106/// Return an iterator over the arguments of this block.107pub fn args<'a>(108&self,109pool: &'a ValueListPool,110) -> impl ExactSizeIterator<Item = BlockArg> + DoubleEndedIterator<Item = BlockArg> + use<'a>111{112self.values.as_slice(pool)[1..]113.iter()114.map(|value| BlockArg::decode_from_value(*value))115}116117/// Traverse the arguments with a closure that can mutate them.118pub fn update_args<F: FnMut(BlockArg) -> BlockArg>(119&mut self,120pool: &mut ValueListPool,121mut f: F,122) {123for raw in self.values.as_mut_slice(pool)[1..].iter_mut() {124let new = f(BlockArg::decode_from_value(*raw));125*raw = new.encode_as_value();126}127}128129/// Remove the argument at ix from the argument list.130pub fn remove(&mut self, ix: usize, pool: &mut ValueListPool) {131self.values.remove(1 + ix, pool)132}133134/// Clear out the arguments list.135pub fn clear(&mut self, pool: &mut ValueListPool) {136self.values.truncate(1, pool)137}138139/// Appends multiple elements to the arguments.140pub fn extend<I, T>(&mut self, elements: I, pool: &mut ValueListPool)141where142I: IntoIterator<Item = T>,143T: Into<BlockArg>,144{145self.values.extend(146elements147.into_iter()148.map(|elem| elem.into().encode_as_value()),149pool,150)151}152153/// Return a value that can display this block call.154pub fn display<'a>(&self, pool: &'a ValueListPool) -> DisplayBlockCall<'a> {155DisplayBlockCall { block: *self, pool }156}157158/// Deep-clone the underlying list in the same pool. The returned159/// list will have identical contents but changes to this list160/// will not change its contents or vice-versa.161pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {162Self {163values: self.values.deep_clone(pool),164}165}166}167168/// Wrapper for the context needed to display a [BlockCall] value.169pub struct DisplayBlockCall<'a> {170block: BlockCall,171pool: &'a ValueListPool,172}173174impl<'a> Display for DisplayBlockCall<'a> {175fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {176write!(f, "{}", self.block.block(&self.pool))?;177if self.block.len(self.pool) > 0 {178write!(f, "(")?;179for (ix, arg) in self.block.args(self.pool).enumerate() {180if ix > 0 {181write!(f, ", ")?;182}183write!(f, "{arg}")?;184}185write!(f, ")")?;186}187Ok(())188}189}190191/// A `BlockArg` is a sum type of `Value`, `TryCallRet`, and192/// `TryCallExn`. The latter two are values that are generated "on the193/// edge" out of a `try_call` instruction into a successor block. We194/// use special arguments rather than special values for these because195/// they are not definable as SSA values at a certain program point --196/// only when the `BlockCall` is executed.197#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]198pub enum BlockArg {199/// An ordinary value, usable at the branch instruction using this200/// `BlockArg`, whose value is passed as an argument.201Value(Value),202203/// A return value of a `try_call`'s called function. Signatures204/// allow multiple return values, so this carries an index. This205/// may be used only on the normal (non-exceptional) `BlockCall`206/// out of a `try_call` or `try_call_indirect` instruction.207TryCallRet(u32),208209/// An exception payload value of a `try_call`. Some ABIs may210/// allow multiple payload values, so this carries an index. Its211/// type is defined by the ABI of the called function. This may be212/// used only on an exceptional `BlockCall` out of a `try_call` or213/// `try_call_indirect` instruction.214TryCallExn(u32),215}216217impl BlockArg {218/// Encode this block argument as a `Value` for storage in the219/// value pool. Internal to `BlockCall`, must not be used220/// elsewhere to avoid exposing the raw bit encoding.221fn encode_as_value(&self) -> Value {222let (tag, payload) = match *self {223BlockArg::Value(v) => (0, v.as_bits()),224BlockArg::TryCallRet(i) => (1, i),225BlockArg::TryCallExn(i) => (2, i),226};227assert!(payload < (1 << 30));228let raw = (tag << 30) | payload;229Value::from_bits(raw)230}231232/// Decode a raw `Value` encoding of this block argument.233fn decode_from_value(v: Value) -> Self {234let raw = v.as_u32();235let tag = raw >> 30;236let payload = raw & ((1 << 30) - 1);237match tag {2380 => BlockArg::Value(Value::from_bits(payload)),2391 => BlockArg::TryCallRet(payload),2402 => BlockArg::TryCallExn(payload),241_ => unreachable!(),242}243}244245/// Return this argument as a `Value`, if it is one, or `None`246/// otherwise.247pub fn as_value(&self) -> Option<Value> {248match *self {249BlockArg::Value(v) => Some(v),250_ => None,251}252}253254/// Update the contained value, if any.255pub fn map_value<F: FnMut(Value) -> Value>(&self, mut f: F) -> Self {256match *self {257BlockArg::Value(v) => BlockArg::Value(f(v)),258other => other,259}260}261}262263impl Display for BlockArg {264fn fmt(&self, f: &mut Formatter) -> fmt::Result {265match self {266BlockArg::Value(v) => write!(f, "{v}"),267BlockArg::TryCallRet(i) => write!(f, "ret{i}"),268BlockArg::TryCallExn(i) => write!(f, "exn{i}"),269}270}271}272273impl From<Value> for BlockArg {274fn from(value: Value) -> BlockArg {275BlockArg::Value(value)276}277}278279// Include code generated by `cranelift-codegen/meta/src/gen_inst.rs`. This file contains:280//281// - The `pub enum InstructionFormat` enum with all the instruction formats.282// - The `pub enum InstructionData` enum with all the instruction data fields.283// - The `pub enum Opcode` definition with all known opcodes,284// - The `const OPCODE_FORMAT: [InstructionFormat; N]` table.285// - The private `fn opcode_name(Opcode) -> &'static str` function, and286// - The hash table `const OPCODE_HASH_TABLE: [Opcode; N]`.287//288// For value type constraints:289//290// - The `const OPCODE_CONSTRAINTS : [OpcodeConstraints; N]` table.291// - The `const TYPE_SETS : [ValueTypeSet; N]` table.292// - The `const OPERAND_CONSTRAINTS : [OperandConstraint; N]` table.293//294include!(concat!(env!("OUT_DIR"), "/opcodes.rs"));295296impl Display for Opcode {297fn fmt(&self, f: &mut Formatter) -> fmt::Result {298write!(f, "{}", opcode_name(*self))299}300}301302impl Opcode {303/// Get the instruction format for this opcode.304pub fn format(self) -> InstructionFormat {305OPCODE_FORMAT[self as usize - 1]306}307308/// Get the constraint descriptor for this opcode.309/// Panic if this is called on `NotAnOpcode`.310pub fn constraints(self) -> OpcodeConstraints {311OPCODE_CONSTRAINTS[self as usize - 1]312}313314/// Is this instruction a GC safepoint?315///316/// Safepoints are all kinds of calls, except for tail calls.317#[inline]318pub fn is_safepoint(self) -> bool {319self.is_call() && !self.is_return()320}321}322323// This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since324// it critically depends on the `opcode_name()` function which is needed here anyway, it lives in325// this module. This also saves us from running the build script twice to generate code for the two326// separate crates.327impl FromStr for Opcode {328type Err = &'static str;329330/// Parse an Opcode name from a string.331fn from_str(s: &str) -> Result<Self, &'static str> {332use crate::constant_hash::{probe, simple_hash};333334match probe::<&str, [Option<Self>]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) {335Err(_) => Err("Unknown opcode"),336// We unwrap here because probe() should have ensured that the entry337// at this index is not None.338Ok(i) => Ok(OPCODE_HASH_TABLE[i].unwrap()),339}340}341}342343impl<'a> Table<&'a str> for [Option<Opcode>] {344fn len(&self) -> usize {345self.len()346}347348fn key(&self, idx: usize) -> Option<&'a str> {349self[idx].map(opcode_name)350}351}352353/// A variable list of `Value` operands used for function call arguments and passing arguments to354/// basic blocks.355#[derive(Clone, Debug)]356pub struct VariableArgs(Vec<Value>);357358impl VariableArgs {359/// Create an empty argument list.360pub fn new() -> Self {361Self(Vec::new())362}363364/// Add an argument to the end.365pub fn push(&mut self, v: Value) {366self.0.push(v)367}368369/// Check if the list is empty.370pub fn is_empty(&self) -> bool {371self.0.is_empty()372}373374/// Convert this to a value list in `pool` with `fixed` prepended.375pub fn into_value_list(self, fixed: &[Value], pool: &mut ValueListPool) -> ValueList {376let mut vlist = ValueList::default();377vlist.extend(fixed.iter().cloned(), pool);378vlist.extend(self.0, pool);379vlist380}381}382383// Coerce `VariableArgs` into a `&[Value]` slice.384impl Deref for VariableArgs {385type Target = [Value];386387fn deref(&self) -> &[Value] {388&self.0389}390}391392impl DerefMut for VariableArgs {393fn deref_mut(&mut self) -> &mut [Value] {394&mut self.0395}396}397398impl Display for VariableArgs {399fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {400for (i, val) in self.0.iter().enumerate() {401if i == 0 {402write!(fmt, "{val}")?;403} else {404write!(fmt, ", {val}")?;405}406}407Ok(())408}409}410411impl Default for VariableArgs {412fn default() -> Self {413Self::new()414}415}416417/// Analyzing an instruction.418///419/// Avoid large matches on instruction formats by using the methods defined here to examine420/// instructions.421impl InstructionData {422/// Get the destinations of this instruction, if it's a branch.423///424/// `br_table` returns the empty slice.425pub fn branch_destination<'a>(426&'a self,427jump_tables: &'a ir::JumpTables,428exception_tables: &'a ir::ExceptionTables,429) -> &'a [BlockCall] {430match self {431Self::Jump { destination, .. } => std::slice::from_ref(destination),432Self::Brif { blocks, .. } => blocks.as_slice(),433Self::BranchTable { table, .. } => jump_tables.get(*table).unwrap().all_branches(),434Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {435exception_tables.get(*exception).unwrap().all_branches()436}437_ => {438debug_assert!(!self.opcode().is_branch());439&[]440}441}442}443444/// Get a mutable slice of the destinations of this instruction, if it's a branch.445///446/// `br_table` returns the empty slice.447pub fn branch_destination_mut<'a>(448&'a mut self,449jump_tables: &'a mut ir::JumpTables,450exception_tables: &'a mut ir::ExceptionTables,451) -> &'a mut [BlockCall] {452match self {453Self::Jump { destination, .. } => std::slice::from_mut(destination),454Self::Brif { blocks, .. } => blocks.as_mut_slice(),455Self::BranchTable { table, .. } => {456jump_tables.get_mut(*table).unwrap().all_branches_mut()457}458Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {459exception_tables460.get_mut(*exception)461.unwrap()462.all_branches_mut()463}464_ => {465debug_assert!(!self.opcode().is_branch());466&mut []467}468}469}470471/// Replace the values used in this instruction according to the given472/// function.473pub fn map_values(474&mut self,475pool: &mut ValueListPool,476jump_tables: &mut ir::JumpTables,477exception_tables: &mut ir::ExceptionTables,478mut f: impl FnMut(Value) -> Value,479) {480// Map all normal operator args.481for arg in self.arguments_mut(pool) {482*arg = f(*arg);483}484485// Map all BlockCall args.486for block in self.branch_destination_mut(jump_tables, exception_tables) {487block.update_args(pool, |arg| arg.map_value(|val| f(val)));488}489490// Map all context items.491if let Some(et) = self.exception_table() {492for ctx in exception_tables[et].contexts_mut() {493*ctx = f(*ctx);494}495}496}497498/// If this is a trapping instruction, get its trap code. Otherwise, return499/// `None`.500pub fn trap_code(&self) -> Option<TrapCode> {501match *self {502Self::CondTrap { code, .. }503| Self::IntAddTrap { code, .. }504| Self::Trap { code, .. } => Some(code),505_ => None,506}507}508509/// If this is a control-flow instruction depending on an integer condition, gets its510/// condition. Otherwise, return `None`.511pub fn cond_code(&self) -> Option<IntCC> {512match self {513&InstructionData::IntCompare { cond, .. }514| &InstructionData::IntCompareImm { cond, .. } => Some(cond),515_ => None,516}517}518519/// If this is a control-flow instruction depending on a floating-point condition, gets its520/// condition. Otherwise, return `None`.521pub fn fp_cond_code(&self) -> Option<FloatCC> {522match self {523&InstructionData::FloatCompare { cond, .. } => Some(cond),524_ => None,525}526}527528/// If this is a trapping instruction, get an exclusive reference to its529/// trap code. Otherwise, return `None`.530pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> {531match self {532Self::CondTrap { code, .. }533| Self::IntAddTrap { code, .. }534| Self::Trap { code, .. } => Some(code),535_ => None,536}537}538539/// If this is an atomic read/modify/write instruction, return its subopcode.540pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> {541match self {542&InstructionData::AtomicRmw { op, .. } => Some(op),543_ => None,544}545}546547/// If this is a load/store instruction, returns its immediate offset.548pub fn load_store_offset(&self) -> Option<i32> {549match self {550&InstructionData::Load { offset, .. }551| &InstructionData::StackLoad { offset, .. }552| &InstructionData::Store { offset, .. }553| &InstructionData::StackStore { offset, .. } => Some(offset.into()),554_ => None,555}556}557558/// If this is a load/store instruction, return its memory flags.559pub fn memflags(&self) -> Option<MemFlags> {560match self {561&InstructionData::Load { flags, .. }562| &InstructionData::LoadNoOffset { flags, .. }563| &InstructionData::Store { flags, .. }564| &InstructionData::StoreNoOffset { flags, .. }565| &InstructionData::AtomicCas { flags, .. }566| &InstructionData::AtomicRmw { flags, .. } => Some(flags),567_ => None,568}569}570571/// If this instruction references a stack slot, return it572pub fn stack_slot(&self) -> Option<StackSlot> {573match self {574&InstructionData::StackStore { stack_slot, .. }575| &InstructionData::StackLoad { stack_slot, .. } => Some(stack_slot),576_ => None,577}578}579580/// Return information about a call instruction.581///582/// Any instruction that can call another function reveals its call signature here.583pub fn analyze_call<'a>(584&'a self,585pool: &'a ValueListPool,586exception_tables: &ExceptionTables,587) -> CallInfo<'a> {588match *self {589Self::Call {590func_ref, ref args, ..591} => CallInfo::Direct(func_ref, args.as_slice(pool)),592Self::CallIndirect {593sig_ref, ref args, ..594} => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),595Self::TryCall {596func_ref,597ref args,598exception,599..600} => {601let exdata = &exception_tables[exception];602CallInfo::DirectWithSig(func_ref, exdata.signature(), args.as_slice(pool))603}604Self::TryCallIndirect {605exception,606ref args,607..608} => {609let exdata = &exception_tables[exception];610CallInfo::Indirect(exdata.signature(), &args.as_slice(pool)[1..])611}612Self::Ternary {613opcode: Opcode::StackSwitch,614..615} => {616// `StackSwitch` is not actually a call, but has the .call() side617// effect as it continues execution elsewhere.618CallInfo::NotACall619}620_ => {621debug_assert!(!self.opcode().is_call());622CallInfo::NotACall623}624}625}626627#[inline]628pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {629if ctrl_typevar.is_invalid() {630return;631}632633let bit_width = ctrl_typevar.bits();634635match self {636Self::UnaryImm { opcode: _, imm } => {637*imm = imm.mask_to_width(bit_width);638}639Self::BinaryImm64 {640opcode,641arg: _,642imm,643} => {644if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm {645*imm = imm.mask_to_width(bit_width);646}647}648Self::IntCompareImm {649opcode,650arg: _,651cond,652imm,653} => {654debug_assert_eq!(*opcode, Opcode::IcmpImm);655if cond.unsigned() != *cond {656*imm = imm.mask_to_width(bit_width);657}658}659_ => {}660}661}662663/// Get the exception table, if any, associated with this instruction.664pub fn exception_table(&self) -> Option<ExceptionTable> {665match self {666Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {667Some(*exception)668}669_ => None,670}671}672}673674/// Information about call instructions.675pub enum CallInfo<'a> {676/// This is not a call instruction.677NotACall,678679/// This is a direct call to an external function declared in the preamble. See680/// `DataFlowGraph.ext_funcs`.681Direct(FuncRef, &'a [Value]),682683/// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`.684Indirect(SigRef, &'a [Value]),685686/// This is a direct call to an external function declared in the687/// preamble, but the signature is also known by other means:688/// e.g., from an exception table entry.689DirectWithSig(FuncRef, SigRef, &'a [Value]),690}691692/// Value type constraints for a given opcode.693///694/// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and695/// results are not determined by the format. Every `Opcode` has an associated696/// `OpcodeConstraints` object that provides the missing details.697#[derive(Clone, Copy)]698pub struct OpcodeConstraints {699/// Flags for this opcode encoded as a bit field:700///701/// Bits 0-2:702/// Number of fixed result values. This does not include `variable_args` results as are703/// produced by call instructions.704///705/// Bit 3:706/// This opcode is polymorphic and the controlling type variable can be inferred from the707/// designated input operand. This is the `typevar_operand` index given to the708/// `InstructionFormat` meta language object. When this bit is not set, the controlling709/// type variable must be the first output value instead.710///711/// Bit 4:712/// This opcode is polymorphic and the controlling type variable does *not* appear as the713/// first result type.714///715/// Bits 5-7:716/// Number of fixed value arguments. The minimum required number of value operands.717flags: u8,718719/// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`.720typeset_offset: u8,721722/// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first723/// `num_fixed_results()` entries describe the result constraints, then follows constraints for724/// the fixed `Value` input operands. (`num_fixed_value_arguments()` of them).725constraint_offset: u16,726}727728impl OpcodeConstraints {729/// Can the controlling type variable for this opcode be inferred from the designated value730/// input operand?731/// This also implies that this opcode is polymorphic.732pub fn use_typevar_operand(self) -> bool {733(self.flags & 0x8) != 0734}735736/// Is it necessary to look at the designated value input operand in order to determine the737/// controlling type variable, or is it good enough to use the first return type?738///739/// Most polymorphic instructions produce a single result with the type of the controlling type740/// variable. A few polymorphic instructions either don't produce any results, or produce741/// results with a fixed type. These instructions return `true`.742pub fn requires_typevar_operand(self) -> bool {743(self.flags & 0x10) != 0744}745746/// Get the number of *fixed* result values produced by this opcode.747/// This does not include `variable_args` produced by calls.748pub fn num_fixed_results(self) -> usize {749(self.flags & 0x7) as usize750}751752/// Get the number of *fixed* input values required by this opcode.753///754/// This does not include `variable_args` arguments on call and branch instructions.755///756/// The number of fixed input values is usually implied by the instruction format, but757/// instruction formats that use a `ValueList` put both fixed and variable arguments in the758/// list. This method returns the *minimum* number of values required in the value list.759pub fn num_fixed_value_arguments(self) -> usize {760((self.flags >> 5) & 0x7) as usize761}762763/// Get the offset into `TYPE_SETS` for the controlling type variable.764/// Returns `None` if the instruction is not polymorphic.765fn typeset_offset(self) -> Option<usize> {766let offset = usize::from(self.typeset_offset);767if offset < TYPE_SETS.len() {768Some(offset)769} else {770None771}772}773774/// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin.775fn constraint_offset(self) -> usize {776self.constraint_offset as usize777}778779/// Get the value type of result number `n`, having resolved the controlling type variable to780/// `ctrl_type`.781pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {782debug_assert!(n < self.num_fixed_results(), "Invalid result index");783match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {784ResolvedConstraint::Bound(t) => t,785ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"),786}787}788789/// Get the value type of input value number `n`, having resolved the controlling type variable790/// to `ctrl_type`.791///792/// Unlike results, it is possible for some input values to vary freely within a specific793/// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant.794pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {795debug_assert!(796n < self.num_fixed_value_arguments(),797"Invalid value argument index"798);799let offset = self.constraint_offset() + self.num_fixed_results();800OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)801}802803/// Get the typeset of allowed types for the controlling type variable in a polymorphic804/// instruction.805pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {806self.typeset_offset().map(|offset| TYPE_SETS[offset])807}808809/// Is this instruction polymorphic?810pub fn is_polymorphic(self) -> bool {811self.ctrl_typeset().is_some()812}813}814815type BitSet8 = ScalarBitSet<u8>;816type BitSet16 = ScalarBitSet<u16>;817818/// A value type set describes the permitted set of types for a type variable.819#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]820pub struct ValueTypeSet {821/// Allowed lane sizes822pub lanes: BitSet16,823/// Allowed int widths824pub ints: BitSet8,825/// Allowed float widths826pub floats: BitSet8,827/// Allowed dynamic vectors minimum lane sizes828pub dynamic_lanes: BitSet16,829}830831impl ValueTypeSet {832/// Is `scalar` part of the base type set?833///834/// Note that the base type set does not have to be included in the type set proper.835fn is_base_type(self, scalar: Type) -> bool {836let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap();837if scalar.is_int() {838self.ints.contains(l2b)839} else if scalar.is_float() {840self.floats.contains(l2b)841} else {842false843}844}845846/// Does `typ` belong to this set?847pub fn contains(self, typ: Type) -> bool {848if typ.is_dynamic_vector() {849let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap();850self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())851} else {852let l2l = u8::try_from(typ.log2_lane_count()).unwrap();853self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())854}855}856857/// Get an example member of this type set.858///859/// This is used for error messages to avoid suggesting invalid types.860pub fn example(self) -> Type {861let t = if self.ints.max().unwrap_or(0) > 5 {862types::I32863} else if self.floats.max().unwrap_or(0) > 5 {864types::F32865} else {866types::I8867};868t.by(1 << self.lanes.min().unwrap()).unwrap()869}870}871872/// Operand constraints. This describes the value type constraints on a single `Value` operand.873enum OperandConstraint {874/// This operand has a concrete value type.875Concrete(Type),876877/// This operand can vary freely within the given type set.878/// The type set is identified by its index into the TYPE_SETS constant table.879Free(u8),880881/// This operand is the same type as the controlling type variable.882Same,883884/// This operand is `ctrlType.lane_of()`.885LaneOf,886887/// This operand is `ctrlType.as_truthy()`.888AsTruthy,889890/// This operand is `ctrlType.half_width()`.891HalfWidth,892893/// This operand is `ctrlType.double_width()`.894DoubleWidth,895896/// This operand is `ctrlType.split_lanes()`.897SplitLanes,898899/// This operand is `ctrlType.merge_lanes()`.900MergeLanes,901902/// This operands is `ctrlType.dynamic_to_vector()`.903DynamicToVector,904905/// This operand is `ctrlType.narrower()`.906Narrower,907908/// This operand is `ctrlType.wider()`.909Wider,910}911912impl OperandConstraint {913/// Resolve this operand constraint into a concrete value type, given the value of the914/// controlling type variable.915pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {916use self::OperandConstraint::*;917use self::ResolvedConstraint::Bound;918match *self {919Concrete(t) => Bound(t),920Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),921Same => Bound(ctrl_type),922LaneOf => Bound(ctrl_type.lane_of()),923AsTruthy => Bound(ctrl_type.as_truthy()),924HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),925DoubleWidth => Bound(926ctrl_type927.double_width()928.expect("invalid type for double_width"),929),930SplitLanes => {931if ctrl_type.is_dynamic_vector() {932Bound(933ctrl_type934.dynamic_to_vector()935.expect("invalid type for dynamic_to_vector")936.split_lanes()937.expect("invalid type for split_lanes")938.vector_to_dynamic()939.expect("invalid dynamic type"),940)941} else {942Bound(943ctrl_type944.split_lanes()945.expect("invalid type for split_lanes"),946)947}948}949MergeLanes => {950if ctrl_type.is_dynamic_vector() {951Bound(952ctrl_type953.dynamic_to_vector()954.expect("invalid type for dynamic_to_vector")955.merge_lanes()956.expect("invalid type for merge_lanes")957.vector_to_dynamic()958.expect("invalid dynamic type"),959)960} else {961Bound(962ctrl_type963.merge_lanes()964.expect("invalid type for merge_lanes"),965)966}967}968DynamicToVector => Bound(969ctrl_type970.dynamic_to_vector()971.expect("invalid type for dynamic_to_vector"),972),973Narrower => {974let ctrl_type_bits = ctrl_type.log2_lane_bits();975let mut tys = ValueTypeSet::default();976977// We're testing scalar values, only.978tys.lanes = ScalarBitSet::from_range(0, 1);979980if ctrl_type.is_int() {981// The upper bound in from_range is exclusive, and we want to exclude the982// control type to construct the interval of [I8, ctrl_type).983tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);984} else if ctrl_type.is_float() {985// The upper bound in from_range is exclusive, and we want to exclude the986// control type to construct the interval of [F16, ctrl_type).987tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8);988} else {989panic!(990"The Narrower constraint only operates on floats or ints, got {ctrl_type:?}"991);992}993ResolvedConstraint::Free(tys)994}995Wider => {996let ctrl_type_bits = ctrl_type.log2_lane_bits();997let mut tys = ValueTypeSet::default();998999// We're testing scalar values, only.1000tys.lanes = ScalarBitSet::from_range(0, 1);10011002if ctrl_type.is_int() {1003let lower_bound = ctrl_type_bits as u8 + 1;1004// The largest integer type we can represent in `BitSet8` is I128, which is1005// represented by bit 7 in the bit set. Adding one to exclude I128 from the1006// lower bound would overflow as 2^8 doesn't fit in a u8, but this would1007// already describe the empty set so instead we leave `ints` in its default1008// empty state.1009if lower_bound < BitSet8::capacity() {1010// The interval should include all types wider than `ctrl_type`, so we use1011// `2^8` as the upper bound, and add one to the bits of `ctrl_type` to define1012// the interval `(ctrl_type, I128]`.1013tys.ints = BitSet8::from_range(lower_bound, 8);1014}1015} else if ctrl_type.is_float() {1016// Same as above but for `tys.floats`, as the largest float type is F128.1017let lower_bound = ctrl_type_bits as u8 + 1;1018if lower_bound < BitSet8::capacity() {1019tys.floats = BitSet8::from_range(lower_bound, 8);1020}1021} else {1022panic!(1023"The Wider constraint only operates on floats or ints, got {ctrl_type:?}"1024);1025}10261027ResolvedConstraint::Free(tys)1028}1029}1030}1031}10321033/// The type constraint on a value argument once the controlling type variable is known.1034#[derive(Copy, Clone, Debug, PartialEq, Eq)]1035pub enum ResolvedConstraint {1036/// The operand is bound to a known type.1037Bound(Type),1038/// The operand type can vary freely within the given set.1039Free(ValueTypeSet),1040}10411042/// A trait to map some functions over each of the entities within an1043/// instruction, when paired with `InstructionData::map`.1044pub trait InstructionMapper {1045/// Map a function over a `Value`.1046fn map_value(&mut self, value: Value) -> Value;10471048/// Map a function over a `ValueList`.1049fn map_value_list(&mut self, value_list: ValueList) -> ValueList;10501051/// Map a function over a `GlobalValue`.1052fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue;10531054/// Map a function over a `JumpTable`.1055fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable;10561057/// Map a function over an `ExceptionTable`.1058fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable;10591060/// Map a function over a `BlockCall`.1061fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall;10621063/// Map a function over a `Block`.1064fn map_block(&mut self, block: Block) -> Block;10651066/// Map a function over a `FuncRef`.1067fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;10681069/// Map a function over a `SigRef`.1070fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;10711072/// Map a function over a `StackSlot`.1073fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;10741075/// Map a function over a `DynamicStackSlot`.1076fn map_dynamic_stack_slot(1077&mut self,1078dynamic_stack_slot: ir::DynamicStackSlot,1079) -> ir::DynamicStackSlot;10801081/// Map a function over a `Constant`.1082fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;10831084/// Map a function over an `Immediate`.1085fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;1086}10871088impl<'a, T> InstructionMapper for &'a mut T1089where1090T: InstructionMapper,1091{1092fn map_value(&mut self, value: Value) -> Value {1093(**self).map_value(value)1094}10951096fn map_value_list(&mut self, value_list: ValueList) -> ValueList {1097(**self).map_value_list(value_list)1098}10991100fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {1101(**self).map_global_value(global_value)1102}11031104fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {1105(**self).map_jump_table(jump_table)1106}11071108fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {1109(**self).map_exception_table(exception_table)1110}11111112fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {1113(**self).map_block_call(block_call)1114}11151116fn map_block(&mut self, block: Block) -> Block {1117(**self).map_block(block)1118}11191120fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {1121(**self).map_func_ref(func_ref)1122}11231124fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {1125(**self).map_sig_ref(sig_ref)1126}11271128fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {1129(**self).map_stack_slot(stack_slot)1130}11311132fn map_dynamic_stack_slot(1133&mut self,1134dynamic_stack_slot: ir::DynamicStackSlot,1135) -> ir::DynamicStackSlot {1136(**self).map_dynamic_stack_slot(dynamic_stack_slot)1137}11381139fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {1140(**self).map_constant(constant)1141}11421143fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {1144(**self).map_immediate(immediate)1145}1146}11471148#[cfg(test)]1149mod tests {1150use super::*;1151use alloc::string::ToString;1152use ir::{DynamicStackSlot, GlobalValue, JumpTable};11531154#[test]1155fn inst_data_is_copy() {1156fn is_copy<T: Copy>() {}1157is_copy::<InstructionData>();1158}11591160#[test]1161fn inst_data_size() {1162// The size of `InstructionData` is performance sensitive, so make sure1163// we don't regress it unintentionally.1164assert_eq!(std::mem::size_of::<InstructionData>(), 16);1165}11661167#[test]1168fn opcodes() {1169use core::mem;11701171let x = Opcode::Iadd;1172let mut y = Opcode::Isub;11731174assert!(x != y);1175y = Opcode::Iadd;1176assert_eq!(x, y);1177assert_eq!(x.format(), InstructionFormat::Binary);11781179assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm");1180assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm");11811182// Check the matcher.1183assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));1184assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm));1185assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));1186assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));1187assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));11881189// Opcode is a single byte, and because Option<Opcode> originally came to 2 bytes, early on1190// Opcode included a variant NotAnOpcode to avoid the unnecessary bloat. Since then the Rust1191// compiler has brought in NonZero optimization, meaning that an enum not using the 0 value1192// can be optional for no size cost. We want to ensure Option<Opcode> remains small.1193assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());1194}11951196#[test]1197fn instruction_data() {1198use core::mem;1199// The size of the `InstructionData` enum is important for performance. It should not1200// exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that1201// require more space than that. It would be fine with a data structure smaller than 161202// bytes, but what are the odds of that?1203assert_eq!(mem::size_of::<InstructionData>(), 16);1204}12051206#[test]1207fn constraints() {1208let a = Opcode::Iadd.constraints();1209assert!(a.use_typevar_operand());1210assert!(!a.requires_typevar_operand());1211assert_eq!(a.num_fixed_results(), 1);1212assert_eq!(a.num_fixed_value_arguments(), 2);1213assert_eq!(a.result_type(0, types::I32), types::I32);1214assert_eq!(a.result_type(0, types::I8), types::I8);1215assert_eq!(1216a.value_argument_constraint(0, types::I32),1217ResolvedConstraint::Bound(types::I32)1218);1219assert_eq!(1220a.value_argument_constraint(1, types::I32),1221ResolvedConstraint::Bound(types::I32)1222);12231224let b = Opcode::Bitcast.constraints();1225assert!(!b.use_typevar_operand());1226assert!(!b.requires_typevar_operand());1227assert_eq!(b.num_fixed_results(), 1);1228assert_eq!(b.num_fixed_value_arguments(), 1);1229assert_eq!(b.result_type(0, types::I32), types::I32);1230assert_eq!(b.result_type(0, types::I8), types::I8);1231match b.value_argument_constraint(0, types::I32) {1232ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),1233_ => panic!("Unexpected constraint from value_argument_constraint"),1234}12351236let c = Opcode::Call.constraints();1237assert_eq!(c.num_fixed_results(), 0);1238assert_eq!(c.num_fixed_value_arguments(), 0);12391240let i = Opcode::CallIndirect.constraints();1241assert_eq!(i.num_fixed_results(), 0);1242assert_eq!(i.num_fixed_value_arguments(), 1);12431244let cmp = Opcode::Icmp.constraints();1245assert!(cmp.use_typevar_operand());1246assert!(cmp.requires_typevar_operand());1247assert_eq!(cmp.num_fixed_results(), 1);1248assert_eq!(cmp.num_fixed_value_arguments(), 2);1249assert_eq!(cmp.result_type(0, types::I64), types::I8);1250}12511252#[test]1253fn value_set() {1254use crate::ir::types::*;12551256let vts = ValueTypeSet {1257lanes: BitSet16::from_range(0, 8),1258ints: BitSet8::from_range(4, 7),1259floats: BitSet8::from_range(0, 0),1260dynamic_lanes: BitSet16::from_range(0, 4),1261};1262assert!(!vts.contains(I8));1263assert!(vts.contains(I32));1264assert!(vts.contains(I64));1265assert!(vts.contains(I32X4));1266assert!(vts.contains(I32X4XN));1267assert!(!vts.contains(F16));1268assert!(!vts.contains(F32));1269assert!(!vts.contains(F128));1270assert_eq!(vts.example().to_string(), "i32");12711272let vts = ValueTypeSet {1273lanes: BitSet16::from_range(0, 8),1274ints: BitSet8::from_range(0, 0),1275floats: BitSet8::from_range(5, 7),1276dynamic_lanes: BitSet16::from_range(0, 8),1277};1278assert_eq!(vts.example().to_string(), "f32");12791280let vts = ValueTypeSet {1281lanes: BitSet16::from_range(1, 8),1282ints: BitSet8::from_range(0, 0),1283floats: BitSet8::from_range(5, 7),1284dynamic_lanes: BitSet16::from_range(0, 8),1285};1286assert_eq!(vts.example().to_string(), "f32x2");12871288let vts = ValueTypeSet {1289lanes: BitSet16::from_range(2, 8),1290ints: BitSet8::from_range(3, 7),1291floats: BitSet8::from_range(0, 0),1292dynamic_lanes: BitSet16::from_range(0, 8),1293};1294assert_eq!(vts.example().to_string(), "i32x4");12951296let vts = ValueTypeSet {1297// TypeSet(lanes=(1, 256), ints=(8, 64))1298lanes: BitSet16::from_range(0, 9),1299ints: BitSet8::from_range(3, 7),1300floats: BitSet8::from_range(0, 0),1301dynamic_lanes: BitSet16::from_range(0, 8),1302};1303assert!(vts.contains(I32));1304assert!(vts.contains(I32X4));1305}13061307#[test]1308fn instruction_data_map() {1309struct TestMapper;13101311impl InstructionMapper for TestMapper {1312fn map_value(&mut self, value: Value) -> Value {1313Value::from_u32(value.as_u32() + 1)1314}13151316fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {1317ValueList::new()1318}13191320fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {1321GlobalValue::from_u32(global_value.as_u32() + 1)1322}13231324fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {1325JumpTable::from_u32(jump_table.as_u32() + 1)1326}13271328fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {1329ExceptionTable::from_u32(exception_table.as_u32() + 1)1330}13311332fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {1333let block = Block::from_u32(42);1334let mut pool = ValueListPool::new();1335BlockCall::new(block, [], &mut pool)1336}13371338fn map_block(&mut self, block: Block) -> Block {1339Block::from_u32(block.as_u32() + 1)1340}13411342fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {1343FuncRef::from_u32(func_ref.as_u32() + 1)1344}13451346fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {1347SigRef::from_u32(sig_ref.as_u32() + 1)1348}13491350fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {1351StackSlot::from_u32(stack_slot.as_u32() + 1)1352}13531354fn map_dynamic_stack_slot(1355&mut self,1356dynamic_stack_slot: ir::DynamicStackSlot,1357) -> ir::DynamicStackSlot {1358DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)1359}13601361fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {1362ir::Constant::from_u32(constant.as_u32() + 1)1363}13641365fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {1366ir::Immediate::from_u32(immediate.as_u32() + 1)1367}1368}13691370let mut pool = ValueListPool::new();1371let map = |inst: InstructionData| inst.map(TestMapper);13721373// Mapping `Value`s.1374assert_eq!(1375map(InstructionData::Binary {1376opcode: Opcode::Iadd,1377args: [Value::from_u32(10), Value::from_u32(20)]1378}),1379InstructionData::Binary {1380opcode: Opcode::Iadd,1381args: [Value::from_u32(11), Value::from_u32(21)]1382}1383);13841385// Mapping `ValueList`s and `FuncRef`s.1386let mut args = ValueList::new();1387args.push(Value::from_u32(42), &mut pool);1388let func_ref = FuncRef::from_u32(99);1389let inst = map(InstructionData::Call {1390opcode: Opcode::Call,1391args,1392func_ref,1393});1394let InstructionData::Call {1395opcode: Opcode::Call,1396args,1397func_ref,1398} = inst1399else {1400panic!()1401};1402assert!(args.is_empty());1403assert_eq!(func_ref, FuncRef::from_u32(100));14041405// Mapping `GlobalValue`s.1406assert_eq!(1407map(InstructionData::UnaryGlobalValue {1408opcode: Opcode::GlobalValue,1409global_value: GlobalValue::from_u32(4),1410}),1411InstructionData::UnaryGlobalValue {1412opcode: Opcode::GlobalValue,1413global_value: GlobalValue::from_u32(5),1414}1415);14161417// Mapping `JumpTable`s.1418assert_eq!(1419map(InstructionData::BranchTable {1420opcode: Opcode::BrTable,1421arg: Value::from_u32(0),1422table: JumpTable::from_u32(1),1423}),1424InstructionData::BranchTable {1425opcode: Opcode::BrTable,1426arg: Value::from_u32(1),1427table: JumpTable::from_u32(2),1428}1429);14301431// Mapping `ExceptionTable`s.1432assert_eq!(1433map(InstructionData::TryCall {1434opcode: Opcode::TryCall,1435args,1436func_ref: FuncRef::from_u32(0),1437exception: ExceptionTable::from_u32(1),1438}),1439InstructionData::TryCall {1440opcode: Opcode::TryCall,1441args,1442func_ref: FuncRef::from_u32(1),1443exception: ExceptionTable::from_u32(2),1444}1445);14461447// Mapping `BlockCall`s.1448assert_eq!(1449map(InstructionData::Jump {1450opcode: Opcode::Jump,1451destination: BlockCall::new(Block::from_u32(99), [], &mut pool),1452}),1453map(InstructionData::Jump {1454opcode: Opcode::Jump,1455destination: BlockCall::new(Block::from_u32(42), [], &mut pool),1456})1457);14581459// Mapping `Block`s.1460assert_eq!(1461map(InstructionData::ExceptionHandlerAddress {1462opcode: Opcode::GetExceptionHandlerAddress,1463block: Block::from_u32(1),1464imm: 0.into(),1465}),1466InstructionData::ExceptionHandlerAddress {1467opcode: Opcode::GetExceptionHandlerAddress,1468block: Block::from_u32(2),1469imm: 0.into(),1470},1471);14721473// Mapping `SigRef`s.1474assert_eq!(1475map(InstructionData::CallIndirect {1476opcode: Opcode::CallIndirect,1477args,1478sig_ref: SigRef::from_u32(11)1479}),1480InstructionData::CallIndirect {1481opcode: Opcode::CallIndirect,1482args: ValueList::new(),1483sig_ref: SigRef::from_u32(12)1484}1485);14861487// Mapping `StackSlot`s.1488assert_eq!(1489map(InstructionData::StackLoad {1490opcode: Opcode::StackLoad,1491stack_slot: StackSlot::from_u32(0),1492offset: 0.into()1493}),1494InstructionData::StackLoad {1495opcode: Opcode::StackLoad,1496stack_slot: StackSlot::from_u32(1),1497offset: 0.into()1498},1499);15001501// Mapping `DynamicStackSlot`s.1502assert_eq!(1503map(InstructionData::DynamicStackLoad {1504opcode: Opcode::DynamicStackLoad,1505dynamic_stack_slot: DynamicStackSlot::from_u32(0),1506}),1507InstructionData::DynamicStackLoad {1508opcode: Opcode::DynamicStackLoad,1509dynamic_stack_slot: DynamicStackSlot::from_u32(1),1510},1511);15121513// Mapping `Constant`s1514assert_eq!(1515map(InstructionData::UnaryConst {1516opcode: ir::Opcode::Vconst,1517constant_handle: ir::Constant::from_u32(2)1518}),1519InstructionData::UnaryConst {1520opcode: ir::Opcode::Vconst,1521constant_handle: ir::Constant::from_u32(3)1522},1523);15241525// Mapping `Immediate`s1526assert_eq!(1527map(InstructionData::Shuffle {1528opcode: ir::Opcode::Shuffle,1529args: [Value::from_u32(0), Value::from_u32(1)],1530imm: ir::Immediate::from_u32(41),1531}),1532InstructionData::Shuffle {1533opcode: ir::Opcode::Shuffle,1534args: [Value::from_u32(1), Value::from_u32(2)],1535imm: ir::Immediate::from_u32(42),1536},1537);1538}1539}154015411542