1use crate::constant_hash::Table;
10use alloc::vec::Vec;
11use core::fmt::{self, Display, Formatter};
12use core::ops::{Deref, DerefMut};
13use core::str::FromStr;
14
15#[cfg(feature = "enable-serde")]
16use serde_derive::{Deserialize, Serialize};
17
18use crate::bitset::ScalarBitSet;
19use crate::entity;
20use 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
28pub type ValueList = entity::EntityList<Value>;
32
33pub type ValueListPool = entity::ListPool<Value>;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
54pub struct BlockCall {
55    values: entity::EntityList<Value>,
59}
60
61impl BlockCall {
62    fn value_to_block(val: Value) -> Block {
65        Block::from_u32(val.as_u32())
66    }
67
68    fn block_to_value(block: Block) -> Value {
71        Value::from_u32(block.as_u32())
72    }
73
74    pub fn new(
76        block: Block,
77        args: impl IntoIterator<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.into_iter().map(|arg| arg.encode_as_value()), pool);
83        Self { values }
84    }
85
86    pub fn block(&self, pool: &ValueListPool) -> Block {
88        let val = self.values.first(pool).unwrap();
89        Self::value_to_block(val)
90    }
91
92    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    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    pub fn len(&self, pool: &ValueListPool) -> usize {
104        self.values.len(pool) - 1
105    }
106
107    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    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    pub fn remove(&mut self, ix: usize, pool: &mut ValueListPool) {
132        self.values.remove(1 + ix, pool)
133    }
134
135    pub fn clear(&mut self, pool: &mut ValueListPool) {
137        self.values.truncate(1, pool)
138    }
139
140    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    pub fn display<'a>(&self, pool: &'a ValueListPool) -> DisplayBlockCall<'a> {
156        DisplayBlockCall { block: *self, pool }
157    }
158
159    pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {
163        Self {
164            values: self.values.deep_clone(pool),
165        }
166    }
167}
168
169pub struct DisplayBlockCall<'a> {
171    block: BlockCall,
172    pool: &'a ValueListPool,
173}
174
175impl<'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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
199pub enum BlockArg {
200    Value(Value),
203
204    TryCallRet(u32),
209
210    TryCallExn(u32),
216}
217
218impl BlockArg {
219    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    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    pub fn as_value(&self) -> Option<Value> {
249        match *self {
250            BlockArg::Value(v) => Some(v),
251            _ => None,
252        }
253    }
254
255    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
264impl 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
274impl From<Value> for BlockArg {
275    fn from(value: Value) -> BlockArg {
276        BlockArg::Value(value)
277    }
278}
279
280include!(concat!(env!("OUT_DIR"), "/opcodes.rs"));
296
297impl Display for Opcode {
298    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
299        write!(f, "{}", opcode_name(*self))
300    }
301}
302
303impl Opcode {
304    pub fn format(self) -> InstructionFormat {
306        OPCODE_FORMAT[self as usize - 1]
307    }
308
309    pub fn constraints(self) -> OpcodeConstraints {
312        OPCODE_CONSTRAINTS[self as usize - 1]
313    }
314
315    #[inline]
319    pub fn is_safepoint(self) -> bool {
320        self.is_call() && !self.is_return()
321    }
322}
323
324impl FromStr for Opcode {
329    type Err = &'static str;
330
331    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            Ok(i) => Ok(OPCODE_HASH_TABLE[i].unwrap()),
340        }
341    }
342}
343
344impl<'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#[derive(Clone, Debug)]
357pub struct VariableArgs(Vec<Value>);
358
359impl VariableArgs {
360    pub fn new() -> Self {
362        Self(Vec::new())
363    }
364
365    pub fn push(&mut self, v: Value) {
367        self.0.push(v)
368    }
369
370    pub fn is_empty(&self) -> bool {
372        self.0.is_empty()
373    }
374
375    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
384impl Deref for VariableArgs {
386    type Target = [Value];
387
388    fn deref(&self) -> &[Value] {
389        &self.0
390    }
391}
392
393impl DerefMut for VariableArgs {
394    fn deref_mut(&mut self) -> &mut [Value] {
395        &mut self.0
396    }
397}
398
399impl 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
412impl Default for VariableArgs {
413    fn default() -> Self {
414        Self::new()
415    }
416}
417
418impl InstructionData {
423    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    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    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) {
483            *arg = f(*arg);
484        }
485
486        for block in self.branch_destination_mut(jump_tables, exception_tables) {
488            block.update_args(pool, |arg| arg.map_value(|val| f(val)));
489        }
490
491        if let Some(et) = self.exception_table() {
493            for ctx in exception_tables[et].contexts_mut() {
494                *ctx = f(*ctx);
495            }
496        }
497    }
498
499    pub fn trap_code(&self) -> Option<TrapCode> {
502        match *self {
503            Self::CondTrap { code, .. }
504            | Self::IntAddTrap { code, .. }
505            | Self::Trap { code, .. } => Some(code),
506            _ => None,
507        }
508    }
509
510    pub fn cond_code(&self) -> Option<IntCC> {
513        match self {
514            &InstructionData::IntCompare { cond, .. }
515            | &InstructionData::IntCompareImm { cond, .. } => Some(cond),
516            _ => None,
517        }
518    }
519
520    pub fn fp_cond_code(&self) -> Option<FloatCC> {
523        match self {
524            &InstructionData::FloatCompare { cond, .. } => Some(cond),
525            _ => None,
526        }
527    }
528
529    pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> {
532        match self {
533            Self::CondTrap { code, .. }
534            | Self::IntAddTrap { code, .. }
535            | Self::Trap { code, .. } => Some(code),
536            _ => None,
537        }
538    }
539
540    pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> {
542        match self {
543            &InstructionData::AtomicRmw { op, .. } => Some(op),
544            _ => None,
545        }
546    }
547
548    pub fn load_store_offset(&self) -> Option<i32> {
550        match self {
551            &InstructionData::Load { offset, .. }
552            | &InstructionData::StackLoad { offset, .. }
553            | &InstructionData::Store { offset, .. }
554            | &InstructionData::StackStore { offset, .. } => Some(offset.into()),
555            _ => None,
556        }
557    }
558
559    pub fn memflags(&self) -> Option<MemFlags> {
561        match self {
562            &InstructionData::Load { flags, .. }
563            | &InstructionData::LoadNoOffset { flags, .. }
564            | &InstructionData::Store { flags, .. }
565            | &InstructionData::StoreNoOffset { flags, .. }
566            | &InstructionData::AtomicCas { flags, .. }
567            | &InstructionData::AtomicRmw { flags, .. } => Some(flags),
568            _ => None,
569        }
570    }
571
572    pub fn stack_slot(&self) -> Option<StackSlot> {
574        match self {
575            &InstructionData::StackStore { stack_slot, .. }
576            | &InstructionData::StackLoad { stack_slot, .. } => Some(stack_slot),
577            _ => None,
578        }
579    }
580
581    pub fn analyze_call<'a>(
585        &'a self,
586        pool: &'a ValueListPool,
587        exception_tables: &ExceptionTables,
588    ) -> CallInfo<'a> {
589        match *self {
590            Self::Call {
591                func_ref, ref args, ..
592            } => CallInfo::Direct(func_ref, args.as_slice(pool)),
593            Self::CallIndirect {
594                sig_ref, ref args, ..
595            } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
596            Self::TryCall {
597                func_ref,
598                ref args,
599                exception,
600                ..
601            } => {
602                let exdata = &exception_tables[exception];
603                CallInfo::DirectWithSig(func_ref, exdata.signature(), args.as_slice(pool))
604            }
605            Self::TryCallIndirect {
606                exception,
607                ref args,
608                ..
609            } => {
610                let exdata = &exception_tables[exception];
611                CallInfo::Indirect(exdata.signature(), &args.as_slice(pool)[1..])
612            }
613            Self::Ternary {
614                opcode: Opcode::StackSwitch,
615                ..
616            } => {
617                CallInfo::NotACall
620            }
621            _ => {
622                debug_assert!(!self.opcode().is_call());
623                CallInfo::NotACall
624            }
625        }
626    }
627
628    #[inline]
629    pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {
630        if ctrl_typevar.is_invalid() {
631            return;
632        }
633
634        let bit_width = ctrl_typevar.bits();
635
636        match self {
637            Self::UnaryImm { opcode: _, imm } => {
638                *imm = imm.mask_to_width(bit_width);
639            }
640            Self::BinaryImm64 {
641                opcode,
642                arg: _,
643                imm,
644            } => {
645                if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm {
646                    *imm = imm.mask_to_width(bit_width);
647                }
648            }
649            Self::IntCompareImm {
650                opcode,
651                arg: _,
652                cond,
653                imm,
654            } => {
655                debug_assert_eq!(*opcode, Opcode::IcmpImm);
656                if cond.unsigned() != *cond {
657                    *imm = imm.mask_to_width(bit_width);
658                }
659            }
660            _ => {}
661        }
662    }
663
664    pub fn exception_table(&self) -> Option<ExceptionTable> {
666        match self {
667            Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
668                Some(*exception)
669            }
670            _ => None,
671        }
672    }
673}
674
675pub enum CallInfo<'a> {
677    NotACall,
679
680    Direct(FuncRef, &'a [Value]),
683
684    Indirect(SigRef, &'a [Value]),
686
687    DirectWithSig(FuncRef, SigRef, &'a [Value]),
691}
692
693#[derive(Clone, Copy)]
699pub struct OpcodeConstraints {
700    flags: u8,
719
720    typeset_offset: u8,
722
723    constraint_offset: u16,
727}
728
729impl OpcodeConstraints {
730    pub fn use_typevar_operand(self) -> bool {
734        (self.flags & 0x8) != 0
735    }
736
737    pub fn requires_typevar_operand(self) -> bool {
744        (self.flags & 0x10) != 0
745    }
746
747    pub fn num_fixed_results(self) -> usize {
750        (self.flags & 0x7) as usize
751    }
752
753    pub fn num_fixed_value_arguments(self) -> usize {
761        ((self.flags >> 5) & 0x7) as usize
762    }
763
764    fn typeset_offset(self) -> Option<usize> {
767        let offset = usize::from(self.typeset_offset);
768        if offset < TYPE_SETS.len() {
769            Some(offset)
770        } else {
771            None
772        }
773    }
774
775    fn constraint_offset(self) -> usize {
777        self.constraint_offset as usize
778    }
779
780    pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
783        debug_assert!(n < self.num_fixed_results(), "Invalid result index");
784        match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {
785            ResolvedConstraint::Bound(t) => t,
786            ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"),
787        }
788    }
789
790    pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
796        debug_assert!(
797            n < self.num_fixed_value_arguments(),
798            "Invalid value argument index"
799        );
800        let offset = self.constraint_offset() + self.num_fixed_results();
801        OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
802    }
803
804    pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
807        self.typeset_offset().map(|offset| TYPE_SETS[offset])
808    }
809
810    pub fn is_polymorphic(self) -> bool {
812        self.ctrl_typeset().is_some()
813    }
814}
815
816type BitSet8 = ScalarBitSet<u8>;
817type BitSet16 = ScalarBitSet<u16>;
818
819#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
821pub struct ValueTypeSet {
822    pub lanes: BitSet16,
824    pub ints: BitSet8,
826    pub floats: BitSet8,
828    pub dynamic_lanes: BitSet16,
830}
831
832impl ValueTypeSet {
833    fn is_base_type(self, scalar: Type) -> bool {
837        let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap();
838        if scalar.is_int() {
839            self.ints.contains(l2b)
840        } else if scalar.is_float() {
841            self.floats.contains(l2b)
842        } else {
843            false
844        }
845    }
846
847    pub fn contains(self, typ: Type) -> bool {
849        if typ.is_dynamic_vector() {
850            let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap();
851            self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())
852        } else {
853            let l2l = u8::try_from(typ.log2_lane_count()).unwrap();
854            self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
855        }
856    }
857
858    pub fn example(self) -> Type {
862        let t = if self.ints.max().unwrap_or(0) > 5 {
863            types::I32
864        } else if self.floats.max().unwrap_or(0) > 5 {
865            types::F32
866        } else {
867            types::I8
868        };
869        t.by(1 << self.lanes.min().unwrap()).unwrap()
870    }
871}
872
873enum OperandConstraint {
875    Concrete(Type),
877
878    Free(u8),
881
882    Same,
884
885    LaneOf,
887
888    AsTruthy,
890
891    HalfWidth,
893
894    DoubleWidth,
896
897    SplitLanes,
899
900    MergeLanes,
902
903    DynamicToVector,
905
906    Narrower,
908
909    Wider,
911}
912
913impl OperandConstraint {
914    pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
917        use self::OperandConstraint::*;
918        use self::ResolvedConstraint::Bound;
919        match *self {
920            Concrete(t) => Bound(t),
921            Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
922            Same => Bound(ctrl_type),
923            LaneOf => Bound(ctrl_type.lane_of()),
924            AsTruthy => Bound(ctrl_type.as_truthy()),
925            HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
926            DoubleWidth => Bound(
927                ctrl_type
928                    .double_width()
929                    .expect("invalid type for double_width"),
930            ),
931            SplitLanes => {
932                if ctrl_type.is_dynamic_vector() {
933                    Bound(
934                        ctrl_type
935                            .dynamic_to_vector()
936                            .expect("invalid type for dynamic_to_vector")
937                            .split_lanes()
938                            .expect("invalid type for split_lanes")
939                            .vector_to_dynamic()
940                            .expect("invalid dynamic type"),
941                    )
942                } else {
943                    Bound(
944                        ctrl_type
945                            .split_lanes()
946                            .expect("invalid type for split_lanes"),
947                    )
948                }
949            }
950            MergeLanes => {
951                if ctrl_type.is_dynamic_vector() {
952                    Bound(
953                        ctrl_type
954                            .dynamic_to_vector()
955                            .expect("invalid type for dynamic_to_vector")
956                            .merge_lanes()
957                            .expect("invalid type for merge_lanes")
958                            .vector_to_dynamic()
959                            .expect("invalid dynamic type"),
960                    )
961                } else {
962                    Bound(
963                        ctrl_type
964                            .merge_lanes()
965                            .expect("invalid type for merge_lanes"),
966                    )
967                }
968            }
969            DynamicToVector => Bound(
970                ctrl_type
971                    .dynamic_to_vector()
972                    .expect("invalid type for dynamic_to_vector"),
973            ),
974            Narrower => {
975                let ctrl_type_bits = ctrl_type.log2_lane_bits();
976                let mut tys = ValueTypeSet::default();
977
978                tys.lanes = ScalarBitSet::from_range(0, 1);
980
981                if ctrl_type.is_int() {
982                    tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);
985                } else if ctrl_type.is_float() {
986                    tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8);
989                } else {
990                    panic!(
991                        "The Narrower constraint only operates on floats or ints, got {ctrl_type:?}"
992                    );
993                }
994                ResolvedConstraint::Free(tys)
995            }
996            Wider => {
997                let ctrl_type_bits = ctrl_type.log2_lane_bits();
998                let mut tys = ValueTypeSet::default();
999
1000                tys.lanes = ScalarBitSet::from_range(0, 1);
1002
1003                if ctrl_type.is_int() {
1004                    let lower_bound = ctrl_type_bits as u8 + 1;
1005                    if lower_bound < BitSet8::capacity() {
1011                        tys.ints = BitSet8::from_range(lower_bound, 8);
1015                    }
1016                } else if ctrl_type.is_float() {
1017                    let lower_bound = ctrl_type_bits as u8 + 1;
1019                    if lower_bound < BitSet8::capacity() {
1020                        tys.floats = BitSet8::from_range(lower_bound, 8);
1021                    }
1022                } else {
1023                    panic!(
1024                        "The Wider constraint only operates on floats or ints, got {ctrl_type:?}"
1025                    );
1026                }
1027
1028                ResolvedConstraint::Free(tys)
1029            }
1030        }
1031    }
1032}
1033
1034#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1036pub enum ResolvedConstraint {
1037    Bound(Type),
1039    Free(ValueTypeSet),
1041}
1042
1043pub trait InstructionMapper {
1046    fn map_value(&mut self, value: Value) -> Value;
1048
1049    fn map_value_list(&mut self, value_list: ValueList) -> ValueList;
1051
1052    fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue;
1054
1055    fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable;
1057
1058    fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable;
1060
1061    fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall;
1063
1064    fn map_block(&mut self, block: Block) -> Block;
1066
1067    fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;
1069
1070    fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;
1072
1073    fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;
1075
1076    fn map_dynamic_stack_slot(
1078        &mut self,
1079        dynamic_stack_slot: ir::DynamicStackSlot,
1080    ) -> ir::DynamicStackSlot;
1081
1082    fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;
1084
1085    fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;
1087}
1088
1089impl<'a, T> InstructionMapper for &'a mut T
1090where
1091    T: InstructionMapper,
1092{
1093    fn map_value(&mut self, value: Value) -> Value {
1094        (**self).map_value(value)
1095    }
1096
1097    fn map_value_list(&mut self, value_list: ValueList) -> ValueList {
1098        (**self).map_value_list(value_list)
1099    }
1100
1101    fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1102        (**self).map_global_value(global_value)
1103    }
1104
1105    fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1106        (**self).map_jump_table(jump_table)
1107    }
1108
1109    fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1110        (**self).map_exception_table(exception_table)
1111    }
1112
1113    fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {
1114        (**self).map_block_call(block_call)
1115    }
1116
1117    fn map_block(&mut self, block: Block) -> Block {
1118        (**self).map_block(block)
1119    }
1120
1121    fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1122        (**self).map_func_ref(func_ref)
1123    }
1124
1125    fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1126        (**self).map_sig_ref(sig_ref)
1127    }
1128
1129    fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1130        (**self).map_stack_slot(stack_slot)
1131    }
1132
1133    fn map_dynamic_stack_slot(
1134        &mut self,
1135        dynamic_stack_slot: ir::DynamicStackSlot,
1136    ) -> ir::DynamicStackSlot {
1137        (**self).map_dynamic_stack_slot(dynamic_stack_slot)
1138    }
1139
1140    fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1141        (**self).map_constant(constant)
1142    }
1143
1144    fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1145        (**self).map_immediate(immediate)
1146    }
1147}
1148
1149#[cfg(test)]
1150mod tests {
1151    use super::*;
1152    use alloc::string::ToString;
1153    use ir::{DynamicStackSlot, GlobalValue, JumpTable};
1154
1155    #[test]
1156    fn inst_data_is_copy() {
1157        fn is_copy<T: Copy>() {}
1158        is_copy::<InstructionData>();
1159    }
1160
1161    #[test]
1162    fn inst_data_size() {
1163        assert_eq!(std::mem::size_of::<InstructionData>(), 16);
1166    }
1167
1168    #[test]
1169    fn opcodes() {
1170        use core::mem;
1171
1172        let x = Opcode::Iadd;
1173        let mut y = Opcode::Isub;
1174
1175        assert!(x != y);
1176        y = Opcode::Iadd;
1177        assert_eq!(x, y);
1178        assert_eq!(x.format(), InstructionFormat::Binary);
1179
1180        assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm");
1181        assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm");
1182
1183        assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
1185        assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm));
1186        assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
1187        assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
1188        assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
1189
1190        assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
1195    }
1196
1197    #[test]
1198    fn instruction_data() {
1199        use core::mem;
1200        assert_eq!(mem::size_of::<InstructionData>(), 16);
1205    }
1206
1207    #[test]
1208    fn constraints() {
1209        let a = Opcode::Iadd.constraints();
1210        assert!(a.use_typevar_operand());
1211        assert!(!a.requires_typevar_operand());
1212        assert_eq!(a.num_fixed_results(), 1);
1213        assert_eq!(a.num_fixed_value_arguments(), 2);
1214        assert_eq!(a.result_type(0, types::I32), types::I32);
1215        assert_eq!(a.result_type(0, types::I8), types::I8);
1216        assert_eq!(
1217            a.value_argument_constraint(0, types::I32),
1218            ResolvedConstraint::Bound(types::I32)
1219        );
1220        assert_eq!(
1221            a.value_argument_constraint(1, types::I32),
1222            ResolvedConstraint::Bound(types::I32)
1223        );
1224
1225        let b = Opcode::Bitcast.constraints();
1226        assert!(!b.use_typevar_operand());
1227        assert!(!b.requires_typevar_operand());
1228        assert_eq!(b.num_fixed_results(), 1);
1229        assert_eq!(b.num_fixed_value_arguments(), 1);
1230        assert_eq!(b.result_type(0, types::I32), types::I32);
1231        assert_eq!(b.result_type(0, types::I8), types::I8);
1232        match b.value_argument_constraint(0, types::I32) {
1233            ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
1234            _ => panic!("Unexpected constraint from value_argument_constraint"),
1235        }
1236
1237        let c = Opcode::Call.constraints();
1238        assert_eq!(c.num_fixed_results(), 0);
1239        assert_eq!(c.num_fixed_value_arguments(), 0);
1240
1241        let i = Opcode::CallIndirect.constraints();
1242        assert_eq!(i.num_fixed_results(), 0);
1243        assert_eq!(i.num_fixed_value_arguments(), 1);
1244
1245        let cmp = Opcode::Icmp.constraints();
1246        assert!(cmp.use_typevar_operand());
1247        assert!(cmp.requires_typevar_operand());
1248        assert_eq!(cmp.num_fixed_results(), 1);
1249        assert_eq!(cmp.num_fixed_value_arguments(), 2);
1250        assert_eq!(cmp.result_type(0, types::I64), types::I8);
1251    }
1252
1253    #[test]
1254    fn value_set() {
1255        use crate::ir::types::*;
1256
1257        let vts = ValueTypeSet {
1258            lanes: BitSet16::from_range(0, 8),
1259            ints: BitSet8::from_range(4, 7),
1260            floats: BitSet8::from_range(0, 0),
1261            dynamic_lanes: BitSet16::from_range(0, 4),
1262        };
1263        assert!(!vts.contains(I8));
1264        assert!(vts.contains(I32));
1265        assert!(vts.contains(I64));
1266        assert!(vts.contains(I32X4));
1267        assert!(vts.contains(I32X4XN));
1268        assert!(!vts.contains(F16));
1269        assert!(!vts.contains(F32));
1270        assert!(!vts.contains(F128));
1271        assert_eq!(vts.example().to_string(), "i32");
1272
1273        let vts = ValueTypeSet {
1274            lanes: BitSet16::from_range(0, 8),
1275            ints: BitSet8::from_range(0, 0),
1276            floats: BitSet8::from_range(5, 7),
1277            dynamic_lanes: BitSet16::from_range(0, 8),
1278        };
1279        assert_eq!(vts.example().to_string(), "f32");
1280
1281        let vts = ValueTypeSet {
1282            lanes: BitSet16::from_range(1, 8),
1283            ints: BitSet8::from_range(0, 0),
1284            floats: BitSet8::from_range(5, 7),
1285            dynamic_lanes: BitSet16::from_range(0, 8),
1286        };
1287        assert_eq!(vts.example().to_string(), "f32x2");
1288
1289        let vts = ValueTypeSet {
1290            lanes: BitSet16::from_range(2, 8),
1291            ints: BitSet8::from_range(3, 7),
1292            floats: BitSet8::from_range(0, 0),
1293            dynamic_lanes: BitSet16::from_range(0, 8),
1294        };
1295        assert_eq!(vts.example().to_string(), "i32x4");
1296
1297        let vts = ValueTypeSet {
1298            lanes: BitSet16::from_range(0, 9),
1300            ints: BitSet8::from_range(3, 7),
1301            floats: BitSet8::from_range(0, 0),
1302            dynamic_lanes: BitSet16::from_range(0, 8),
1303        };
1304        assert!(vts.contains(I32));
1305        assert!(vts.contains(I32X4));
1306    }
1307
1308    #[test]
1309    fn instruction_data_map() {
1310        struct TestMapper;
1311
1312        impl InstructionMapper for TestMapper {
1313            fn map_value(&mut self, value: Value) -> Value {
1314                Value::from_u32(value.as_u32() + 1)
1315            }
1316
1317            fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {
1318                ValueList::new()
1319            }
1320
1321            fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1322                GlobalValue::from_u32(global_value.as_u32() + 1)
1323            }
1324
1325            fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1326                JumpTable::from_u32(jump_table.as_u32() + 1)
1327            }
1328
1329            fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1330                ExceptionTable::from_u32(exception_table.as_u32() + 1)
1331            }
1332
1333            fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {
1334                let block = Block::from_u32(42);
1335                let mut pool = ValueListPool::new();
1336                BlockCall::new(block, [], &mut pool)
1337            }
1338
1339            fn map_block(&mut self, block: Block) -> Block {
1340                Block::from_u32(block.as_u32() + 1)
1341            }
1342
1343            fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1344                FuncRef::from_u32(func_ref.as_u32() + 1)
1345            }
1346
1347            fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1348                SigRef::from_u32(sig_ref.as_u32() + 1)
1349            }
1350
1351            fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1352                StackSlot::from_u32(stack_slot.as_u32() + 1)
1353            }
1354
1355            fn map_dynamic_stack_slot(
1356                &mut self,
1357                dynamic_stack_slot: ir::DynamicStackSlot,
1358            ) -> ir::DynamicStackSlot {
1359                DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)
1360            }
1361
1362            fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1363                ir::Constant::from_u32(constant.as_u32() + 1)
1364            }
1365
1366            fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1367                ir::Immediate::from_u32(immediate.as_u32() + 1)
1368            }
1369        }
1370
1371        let mut pool = ValueListPool::new();
1372        let map = |inst: InstructionData| inst.map(TestMapper);
1373
1374        assert_eq!(
1376            map(InstructionData::Binary {
1377                opcode: Opcode::Iadd,
1378                args: [Value::from_u32(10), Value::from_u32(20)]
1379            }),
1380            InstructionData::Binary {
1381                opcode: Opcode::Iadd,
1382                args: [Value::from_u32(11), Value::from_u32(21)]
1383            }
1384        );
1385
1386        let mut args = ValueList::new();
1388        args.push(Value::from_u32(42), &mut pool);
1389        let func_ref = FuncRef::from_u32(99);
1390        let inst = map(InstructionData::Call {
1391            opcode: Opcode::Call,
1392            args,
1393            func_ref,
1394        });
1395        let InstructionData::Call {
1396            opcode: Opcode::Call,
1397            args,
1398            func_ref,
1399        } = inst
1400        else {
1401            panic!()
1402        };
1403        assert!(args.is_empty());
1404        assert_eq!(func_ref, FuncRef::from_u32(100));
1405
1406        assert_eq!(
1408            map(InstructionData::UnaryGlobalValue {
1409                opcode: Opcode::GlobalValue,
1410                global_value: GlobalValue::from_u32(4),
1411            }),
1412            InstructionData::UnaryGlobalValue {
1413                opcode: Opcode::GlobalValue,
1414                global_value: GlobalValue::from_u32(5),
1415            }
1416        );
1417
1418        assert_eq!(
1420            map(InstructionData::BranchTable {
1421                opcode: Opcode::BrTable,
1422                arg: Value::from_u32(0),
1423                table: JumpTable::from_u32(1),
1424            }),
1425            InstructionData::BranchTable {
1426                opcode: Opcode::BrTable,
1427                arg: Value::from_u32(1),
1428                table: JumpTable::from_u32(2),
1429            }
1430        );
1431
1432        assert_eq!(
1434            map(InstructionData::TryCall {
1435                opcode: Opcode::TryCall,
1436                args,
1437                func_ref: FuncRef::from_u32(0),
1438                exception: ExceptionTable::from_u32(1),
1439            }),
1440            InstructionData::TryCall {
1441                opcode: Opcode::TryCall,
1442                args,
1443                func_ref: FuncRef::from_u32(1),
1444                exception: ExceptionTable::from_u32(2),
1445            }
1446        );
1447
1448        assert_eq!(
1450            map(InstructionData::Jump {
1451                opcode: Opcode::Jump,
1452                destination: BlockCall::new(Block::from_u32(99), [], &mut pool),
1453            }),
1454            map(InstructionData::Jump {
1455                opcode: Opcode::Jump,
1456                destination: BlockCall::new(Block::from_u32(42), [], &mut pool),
1457            })
1458        );
1459
1460        assert_eq!(
1462            map(InstructionData::ExceptionHandlerAddress {
1463                opcode: Opcode::GetExceptionHandlerAddress,
1464                block: Block::from_u32(1),
1465                imm: 0.into(),
1466            }),
1467            InstructionData::ExceptionHandlerAddress {
1468                opcode: Opcode::GetExceptionHandlerAddress,
1469                block: Block::from_u32(2),
1470                imm: 0.into(),
1471            },
1472        );
1473
1474        assert_eq!(
1476            map(InstructionData::CallIndirect {
1477                opcode: Opcode::CallIndirect,
1478                args,
1479                sig_ref: SigRef::from_u32(11)
1480            }),
1481            InstructionData::CallIndirect {
1482                opcode: Opcode::CallIndirect,
1483                args: ValueList::new(),
1484                sig_ref: SigRef::from_u32(12)
1485            }
1486        );
1487
1488        assert_eq!(
1490            map(InstructionData::StackLoad {
1491                opcode: Opcode::StackLoad,
1492                stack_slot: StackSlot::from_u32(0),
1493                offset: 0.into()
1494            }),
1495            InstructionData::StackLoad {
1496                opcode: Opcode::StackLoad,
1497                stack_slot: StackSlot::from_u32(1),
1498                offset: 0.into()
1499            },
1500        );
1501
1502        assert_eq!(
1504            map(InstructionData::DynamicStackLoad {
1505                opcode: Opcode::DynamicStackLoad,
1506                dynamic_stack_slot: DynamicStackSlot::from_u32(0),
1507            }),
1508            InstructionData::DynamicStackLoad {
1509                opcode: Opcode::DynamicStackLoad,
1510                dynamic_stack_slot: DynamicStackSlot::from_u32(1),
1511            },
1512        );
1513
1514        assert_eq!(
1516            map(InstructionData::UnaryConst {
1517                opcode: ir::Opcode::Vconst,
1518                constant_handle: ir::Constant::from_u32(2)
1519            }),
1520            InstructionData::UnaryConst {
1521                opcode: ir::Opcode::Vconst,
1522                constant_handle: ir::Constant::from_u32(3)
1523            },
1524        );
1525
1526        assert_eq!(
1528            map(InstructionData::Shuffle {
1529                opcode: ir::Opcode::Shuffle,
1530                args: [Value::from_u32(0), Value::from_u32(1)],
1531                imm: ir::Immediate::from_u32(41),
1532            }),
1533            InstructionData::Shuffle {
1534                opcode: ir::Opcode::Shuffle,
1535                args: [Value::from_u32(1), Value::from_u32(2)],
1536                imm: ir::Immediate::from_u32(42),
1537            },
1538        );
1539    }
1540}