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, .. } => core::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, .. } => core::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, .. } => Some(cond),
515 _ => None,
516 }
517 }
518
519 pub fn fp_cond_code(&self) -> Option<FloatCC> {
522 match self {
523 &InstructionData::FloatCompare { cond, .. } => Some(cond),
524 _ => None,
525 }
526 }
527
528 pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> {
531 match self {
532 Self::CondTrap { code, .. }
533 | Self::IntAddTrap { code, .. }
534 | Self::Trap { code, .. } => Some(code),
535 _ => None,
536 }
537 }
538
539 pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> {
541 match self {
542 &InstructionData::AtomicRmw { op, .. } => Some(op),
543 _ => None,
544 }
545 }
546
547 pub fn load_store_offset(&self) -> Option<i32> {
549 match self {
550 &InstructionData::Load { offset, .. }
551 | &InstructionData::StackLoad { offset, .. }
552 | &InstructionData::Store { offset, .. }
553 | &InstructionData::StackStore { offset, .. } => Some(offset.into()),
554 _ => None,
555 }
556 }
557
558 pub fn memflags(&self) -> Option<MemFlags> {
560 match 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 }
570
571 pub fn memflags_data(&self, dfg: &super::dfg::DataFlowGraph) -> Option<super::MemFlagsData> {
574 self.memflags().map(|f| dfg.mem_flags[f])
575 }
576
577 pub fn stack_slot(&self) -> Option<StackSlot> {
579 match self {
580 &InstructionData::StackStore { stack_slot, .. }
581 | &InstructionData::StackLoad { stack_slot, .. } => Some(stack_slot),
582 _ => None,
583 }
584 }
585
586 pub fn analyze_call<'a>(
590 &'a self,
591 pool: &'a ValueListPool,
592 exception_tables: &ExceptionTables,
593 ) -> CallInfo<'a> {
594 match *self {
595 Self::Call {
596 func_ref, ref args, ..
597 } => CallInfo::Direct(func_ref, args.as_slice(pool)),
598 Self::CallIndirect {
599 sig_ref, ref args, ..
600 } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
601 Self::TryCall {
602 func_ref,
603 ref args,
604 exception,
605 ..
606 } => {
607 let exdata = &exception_tables[exception];
608 CallInfo::DirectWithSig(func_ref, exdata.signature(), args.as_slice(pool))
609 }
610 Self::TryCallIndirect {
611 exception,
612 ref args,
613 ..
614 } => {
615 let exdata = &exception_tables[exception];
616 CallInfo::Indirect(exdata.signature(), &args.as_slice(pool)[1..])
617 }
618 Self::Ternary {
619 opcode: Opcode::StackSwitch,
620 ..
621 } => {
622 CallInfo::NotACall
625 }
626 _ => {
627 debug_assert!(!self.opcode().is_call());
628 CallInfo::NotACall
629 }
630 }
631 }
632
633 #[inline]
634 pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {
635 if ctrl_typevar.is_invalid() {
636 return;
637 }
638
639 let bit_width = ctrl_typevar.bits();
640
641 match self {
642 Self::UnaryImm { opcode: _, imm } => {
643 *imm = imm.mask_to_width(bit_width);
644 }
645 _ => {}
646 }
647 }
648
649 pub fn exception_table(&self) -> Option<ExceptionTable> {
651 match self {
652 Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
653 Some(*exception)
654 }
655 _ => None,
656 }
657 }
658}
659
660pub enum CallInfo<'a> {
662 NotACall,
664
665 Direct(FuncRef, &'a [Value]),
668
669 Indirect(SigRef, &'a [Value]),
671
672 DirectWithSig(FuncRef, SigRef, &'a [Value]),
676}
677
678#[derive(Clone, Copy)]
684pub struct OpcodeConstraints {
685 flags: u8,
704
705 typeset_offset: u8,
707
708 constraint_offset: u16,
712}
713
714impl OpcodeConstraints {
715 pub fn use_typevar_operand(self) -> bool {
719 (self.flags & 0x8) != 0
720 }
721
722 pub fn requires_typevar_operand(self) -> bool {
729 (self.flags & 0x10) != 0
730 }
731
732 pub fn num_fixed_results(self) -> usize {
735 (self.flags & 0x7) as usize
736 }
737
738 pub fn num_fixed_value_arguments(self) -> usize {
746 ((self.flags >> 5) & 0x7) as usize
747 }
748
749 fn typeset_offset(self) -> Option<usize> {
752 let offset = usize::from(self.typeset_offset);
753 if offset < TYPE_SETS.len() {
754 Some(offset)
755 } else {
756 None
757 }
758 }
759
760 fn constraint_offset(self) -> usize {
762 self.constraint_offset as usize
763 }
764
765 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
768 debug_assert!(n < self.num_fixed_results(), "Invalid result index");
769 match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {
770 ResolvedConstraint::Bound(t) => t,
771 ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"),
772 }
773 }
774
775 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
781 debug_assert!(
782 n < self.num_fixed_value_arguments(),
783 "Invalid value argument index"
784 );
785 let offset = self.constraint_offset() + self.num_fixed_results();
786 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
787 }
788
789 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
792 self.typeset_offset().map(|offset| TYPE_SETS[offset])
793 }
794
795 pub fn is_polymorphic(self) -> bool {
797 self.ctrl_typeset().is_some()
798 }
799}
800
801type BitSet8 = ScalarBitSet<u8>;
802type BitSet16 = ScalarBitSet<u16>;
803
804#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
806pub struct ValueTypeSet {
807 pub lanes: BitSet16,
809 pub ints: BitSet8,
811 pub floats: BitSet8,
813 pub dynamic_lanes: BitSet16,
815}
816
817impl ValueTypeSet {
818 fn is_base_type(self, scalar: Type) -> bool {
822 let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap();
823 if scalar.is_int() {
824 self.ints.contains(l2b)
825 } else if scalar.is_float() {
826 self.floats.contains(l2b)
827 } else {
828 false
829 }
830 }
831
832 pub fn contains(self, typ: Type) -> bool {
834 if typ.is_dynamic_vector() {
835 let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap();
836 self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())
837 } else {
838 let l2l = u8::try_from(typ.log2_lane_count()).unwrap();
839 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
840 }
841 }
842
843 pub fn example(self) -> Type {
847 let t = if self.ints.max().unwrap_or(0) > 5 {
848 types::I32
849 } else if self.floats.max().unwrap_or(0) > 5 {
850 types::F32
851 } else {
852 types::I8
853 };
854 t.by(1 << self.lanes.min().unwrap()).unwrap()
855 }
856}
857
858enum OperandConstraint {
860 Concrete(Type),
862
863 Free(u8),
866
867 Same,
869
870 LaneOf,
872
873 AsTruthy,
875
876 HalfWidth,
878
879 DoubleWidth,
881
882 SplitLanes,
884
885 MergeLanes,
887
888 DynamicToVector,
890
891 Narrower,
893
894 Wider,
896}
897
898impl OperandConstraint {
899 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
902 use self::OperandConstraint::*;
903 use self::ResolvedConstraint::Bound;
904 match *self {
905 Concrete(t) => Bound(t),
906 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
907 Same => Bound(ctrl_type),
908 LaneOf => Bound(ctrl_type.lane_of()),
909 AsTruthy => Bound(ctrl_type.as_truthy()),
910 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
911 DoubleWidth => Bound(
912 ctrl_type
913 .double_width()
914 .expect("invalid type for double_width"),
915 ),
916 SplitLanes => {
917 if ctrl_type.is_dynamic_vector() {
918 Bound(
919 ctrl_type
920 .dynamic_to_vector()
921 .expect("invalid type for dynamic_to_vector")
922 .split_lanes()
923 .expect("invalid type for split_lanes")
924 .vector_to_dynamic()
925 .expect("invalid dynamic type"),
926 )
927 } else {
928 Bound(
929 ctrl_type
930 .split_lanes()
931 .expect("invalid type for split_lanes"),
932 )
933 }
934 }
935 MergeLanes => {
936 if ctrl_type.is_dynamic_vector() {
937 Bound(
938 ctrl_type
939 .dynamic_to_vector()
940 .expect("invalid type for dynamic_to_vector")
941 .merge_lanes()
942 .expect("invalid type for merge_lanes")
943 .vector_to_dynamic()
944 .expect("invalid dynamic type"),
945 )
946 } else {
947 Bound(
948 ctrl_type
949 .merge_lanes()
950 .expect("invalid type for merge_lanes"),
951 )
952 }
953 }
954 DynamicToVector => Bound(
955 ctrl_type
956 .dynamic_to_vector()
957 .expect("invalid type for dynamic_to_vector"),
958 ),
959 Narrower => {
960 let ctrl_type_bits = ctrl_type.log2_lane_bits();
961 let mut tys = ValueTypeSet::default();
962
963 tys.lanes = ScalarBitSet::from_range(0, 1);
965
966 if ctrl_type.is_int() {
967 tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);
970 } else if ctrl_type.is_float() {
971 tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8);
974 } else {
975 panic!(
976 "The Narrower constraint only operates on floats or ints, got {ctrl_type:?}"
977 );
978 }
979 ResolvedConstraint::Free(tys)
980 }
981 Wider => {
982 let ctrl_type_bits = ctrl_type.log2_lane_bits();
983 let mut tys = ValueTypeSet::default();
984
985 tys.lanes = ScalarBitSet::from_range(0, 1);
987
988 if ctrl_type.is_int() {
989 let lower_bound = ctrl_type_bits as u8 + 1;
990 if lower_bound < BitSet8::capacity() {
996 tys.ints = BitSet8::from_range(lower_bound, 8);
1000 }
1001 } else if ctrl_type.is_float() {
1002 let lower_bound = ctrl_type_bits as u8 + 1;
1004 if lower_bound < BitSet8::capacity() {
1005 tys.floats = BitSet8::from_range(lower_bound, 8);
1006 }
1007 } else {
1008 panic!(
1009 "The Wider constraint only operates on floats or ints, got {ctrl_type:?}"
1010 );
1011 }
1012
1013 ResolvedConstraint::Free(tys)
1014 }
1015 }
1016 }
1017}
1018
1019#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1021pub enum ResolvedConstraint {
1022 Bound(Type),
1024 Free(ValueTypeSet),
1026}
1027
1028pub trait InstructionMapper {
1031 fn map_value(&mut self, value: Value) -> Value;
1033
1034 fn map_value_list(&mut self, value_list: ValueList) -> ValueList;
1036
1037 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue;
1039
1040 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable;
1042
1043 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable;
1045
1046 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall;
1048
1049 fn map_block(&mut self, block: Block) -> Block;
1051
1052 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;
1054
1055 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;
1057
1058 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;
1060
1061 fn map_dynamic_stack_slot(
1063 &mut self,
1064 dynamic_stack_slot: ir::DynamicStackSlot,
1065 ) -> ir::DynamicStackSlot;
1066
1067 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;
1069
1070 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;
1072
1073 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1079 flags
1080 }
1081}
1082
1083impl<'a, T> InstructionMapper for &'a mut T
1084where
1085 T: InstructionMapper,
1086{
1087 fn map_value(&mut self, value: Value) -> Value {
1088 (**self).map_value(value)
1089 }
1090
1091 fn map_value_list(&mut self, value_list: ValueList) -> ValueList {
1092 (**self).map_value_list(value_list)
1093 }
1094
1095 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1096 (**self).map_global_value(global_value)
1097 }
1098
1099 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1100 (**self).map_jump_table(jump_table)
1101 }
1102
1103 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1104 (**self).map_exception_table(exception_table)
1105 }
1106
1107 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {
1108 (**self).map_block_call(block_call)
1109 }
1110
1111 fn map_block(&mut self, block: Block) -> Block {
1112 (**self).map_block(block)
1113 }
1114
1115 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1116 (**self).map_func_ref(func_ref)
1117 }
1118
1119 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1120 (**self).map_sig_ref(sig_ref)
1121 }
1122
1123 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1124 (**self).map_stack_slot(stack_slot)
1125 }
1126
1127 fn map_dynamic_stack_slot(
1128 &mut self,
1129 dynamic_stack_slot: ir::DynamicStackSlot,
1130 ) -> ir::DynamicStackSlot {
1131 (**self).map_dynamic_stack_slot(dynamic_stack_slot)
1132 }
1133
1134 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1135 (**self).map_constant(constant)
1136 }
1137
1138 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1139 (**self).map_immediate(immediate)
1140 }
1141
1142 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1143 (**self).map_mem_flags(flags)
1144 }
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149 use super::*;
1150 use alloc::string::ToString;
1151 use ir::{DynamicStackSlot, GlobalValue, JumpTable};
1152
1153 #[test]
1154 fn inst_data_is_copy() {
1155 fn is_copy<T: Copy>() {}
1156 is_copy::<InstructionData>();
1157 }
1158
1159 #[test]
1160 fn inst_data_size() {
1161 assert_eq!(core::mem::size_of::<InstructionData>(), 16);
1164 }
1165
1166 #[test]
1167 fn opcodes() {
1168 use core::mem;
1169
1170 let x = Opcode::Iadd;
1171 let mut y = Opcode::Isub;
1172
1173 assert!(x != y);
1174 y = Opcode::Iadd;
1175 assert_eq!(x, y);
1176 assert_eq!(x.format(), InstructionFormat::Binary);
1177
1178 assert_eq!(format!("{:?}", Opcode::BandNot), "BandNot");
1179 assert_eq!(Opcode::BandNot.to_string(), "band_not");
1180
1181 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
1183 assert_eq!("band_not".parse::<Opcode>(), Ok(Opcode::BandNot));
1184 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
1185 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
1186 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
1187
1188 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
1193 }
1194
1195 #[test]
1196 fn instruction_data() {
1197 use core::mem;
1198 assert_eq!(mem::size_of::<InstructionData>(), 16);
1203 }
1204
1205 #[test]
1206 fn constraints() {
1207 let a = Opcode::Iadd.constraints();
1208 assert!(a.use_typevar_operand());
1209 assert!(!a.requires_typevar_operand());
1210 assert_eq!(a.num_fixed_results(), 1);
1211 assert_eq!(a.num_fixed_value_arguments(), 2);
1212 assert_eq!(a.result_type(0, types::I32), types::I32);
1213 assert_eq!(a.result_type(0, types::I8), types::I8);
1214 assert_eq!(
1215 a.value_argument_constraint(0, types::I32),
1216 ResolvedConstraint::Bound(types::I32)
1217 );
1218 assert_eq!(
1219 a.value_argument_constraint(1, types::I32),
1220 ResolvedConstraint::Bound(types::I32)
1221 );
1222
1223 let b = Opcode::Bitcast.constraints();
1224 assert!(!b.use_typevar_operand());
1225 assert!(!b.requires_typevar_operand());
1226 assert_eq!(b.num_fixed_results(), 1);
1227 assert_eq!(b.num_fixed_value_arguments(), 1);
1228 assert_eq!(b.result_type(0, types::I32), types::I32);
1229 assert_eq!(b.result_type(0, types::I8), types::I8);
1230 match b.value_argument_constraint(0, types::I32) {
1231 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
1232 _ => panic!("Unexpected constraint from value_argument_constraint"),
1233 }
1234
1235 let c = Opcode::Call.constraints();
1236 assert_eq!(c.num_fixed_results(), 0);
1237 assert_eq!(c.num_fixed_value_arguments(), 0);
1238
1239 let i = Opcode::CallIndirect.constraints();
1240 assert_eq!(i.num_fixed_results(), 0);
1241 assert_eq!(i.num_fixed_value_arguments(), 1);
1242
1243 let cmp = Opcode::Icmp.constraints();
1244 assert!(cmp.use_typevar_operand());
1245 assert!(cmp.requires_typevar_operand());
1246 assert_eq!(cmp.num_fixed_results(), 1);
1247 assert_eq!(cmp.num_fixed_value_arguments(), 2);
1248 assert_eq!(cmp.result_type(0, types::I64), types::I8);
1249 }
1250
1251 #[test]
1252 fn value_set() {
1253 use crate::ir::types::*;
1254
1255 let vts = ValueTypeSet {
1256 lanes: BitSet16::from_range(0, 8),
1257 ints: BitSet8::from_range(4, 7),
1258 floats: BitSet8::from_range(0, 0),
1259 dynamic_lanes: BitSet16::from_range(0, 4),
1260 };
1261 assert!(!vts.contains(I8));
1262 assert!(vts.contains(I32));
1263 assert!(vts.contains(I64));
1264 assert!(vts.contains(I32X4));
1265 assert!(vts.contains(I32X4XN));
1266 assert!(!vts.contains(F16));
1267 assert!(!vts.contains(F32));
1268 assert!(!vts.contains(F128));
1269 assert_eq!(vts.example().to_string(), "i32");
1270
1271 let vts = ValueTypeSet {
1272 lanes: BitSet16::from_range(0, 8),
1273 ints: BitSet8::from_range(0, 0),
1274 floats: BitSet8::from_range(5, 7),
1275 dynamic_lanes: BitSet16::from_range(0, 8),
1276 };
1277 assert_eq!(vts.example().to_string(), "f32");
1278
1279 let vts = ValueTypeSet {
1280 lanes: BitSet16::from_range(1, 8),
1281 ints: BitSet8::from_range(0, 0),
1282 floats: BitSet8::from_range(5, 7),
1283 dynamic_lanes: BitSet16::from_range(0, 8),
1284 };
1285 assert_eq!(vts.example().to_string(), "f32x2");
1286
1287 let vts = ValueTypeSet {
1288 lanes: BitSet16::from_range(2, 8),
1289 ints: BitSet8::from_range(3, 7),
1290 floats: BitSet8::from_range(0, 0),
1291 dynamic_lanes: BitSet16::from_range(0, 8),
1292 };
1293 assert_eq!(vts.example().to_string(), "i32x4");
1294
1295 let vts = ValueTypeSet {
1296 lanes: BitSet16::from_range(0, 9),
1298 ints: BitSet8::from_range(3, 7),
1299 floats: BitSet8::from_range(0, 0),
1300 dynamic_lanes: BitSet16::from_range(0, 8),
1301 };
1302 assert!(vts.contains(I32));
1303 assert!(vts.contains(I32X4));
1304 }
1305
1306 #[test]
1307 fn instruction_data_map() {
1308 struct TestMapper;
1309
1310 impl InstructionMapper for TestMapper {
1311 fn map_value(&mut self, value: Value) -> Value {
1312 Value::from_u32(value.as_u32() + 1)
1313 }
1314
1315 fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {
1316 ValueList::new()
1317 }
1318
1319 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1320 GlobalValue::from_u32(global_value.as_u32() + 1)
1321 }
1322
1323 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1324 JumpTable::from_u32(jump_table.as_u32() + 1)
1325 }
1326
1327 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1328 ExceptionTable::from_u32(exception_table.as_u32() + 1)
1329 }
1330
1331 fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {
1332 let block = Block::from_u32(42);
1333 let mut pool = ValueListPool::new();
1334 BlockCall::new(block, [], &mut pool)
1335 }
1336
1337 fn map_block(&mut self, block: Block) -> Block {
1338 Block::from_u32(block.as_u32() + 1)
1339 }
1340
1341 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1342 FuncRef::from_u32(func_ref.as_u32() + 1)
1343 }
1344
1345 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1346 SigRef::from_u32(sig_ref.as_u32() + 1)
1347 }
1348
1349 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1350 StackSlot::from_u32(stack_slot.as_u32() + 1)
1351 }
1352
1353 fn map_dynamic_stack_slot(
1354 &mut self,
1355 dynamic_stack_slot: ir::DynamicStackSlot,
1356 ) -> ir::DynamicStackSlot {
1357 DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)
1358 }
1359
1360 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1361 ir::Constant::from_u32(constant.as_u32() + 1)
1362 }
1363
1364 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1365 ir::Immediate::from_u32(immediate.as_u32() + 1)
1366 }
1367 }
1368
1369 let mut pool = ValueListPool::new();
1370 let map = |inst: InstructionData| inst.map(TestMapper);
1371
1372 assert_eq!(
1374 map(InstructionData::Binary {
1375 opcode: Opcode::Iadd,
1376 args: [Value::from_u32(10), Value::from_u32(20)]
1377 }),
1378 InstructionData::Binary {
1379 opcode: Opcode::Iadd,
1380 args: [Value::from_u32(11), Value::from_u32(21)]
1381 }
1382 );
1383
1384 let mut args = ValueList::new();
1386 args.push(Value::from_u32(42), &mut pool);
1387 let func_ref = FuncRef::from_u32(99);
1388 let inst = map(InstructionData::Call {
1389 opcode: Opcode::Call,
1390 args,
1391 func_ref,
1392 });
1393 let InstructionData::Call {
1394 opcode: Opcode::Call,
1395 args,
1396 func_ref,
1397 } = inst
1398 else {
1399 panic!()
1400 };
1401 assert!(args.is_empty());
1402 assert_eq!(func_ref, FuncRef::from_u32(100));
1403
1404 assert_eq!(
1406 map(InstructionData::UnaryGlobalValue {
1407 opcode: Opcode::GlobalValue,
1408 global_value: GlobalValue::from_u32(4),
1409 }),
1410 InstructionData::UnaryGlobalValue {
1411 opcode: Opcode::GlobalValue,
1412 global_value: GlobalValue::from_u32(5),
1413 }
1414 );
1415
1416 assert_eq!(
1418 map(InstructionData::BranchTable {
1419 opcode: Opcode::BrTable,
1420 arg: Value::from_u32(0),
1421 table: JumpTable::from_u32(1),
1422 }),
1423 InstructionData::BranchTable {
1424 opcode: Opcode::BrTable,
1425 arg: Value::from_u32(1),
1426 table: JumpTable::from_u32(2),
1427 }
1428 );
1429
1430 assert_eq!(
1432 map(InstructionData::TryCall {
1433 opcode: Opcode::TryCall,
1434 args,
1435 func_ref: FuncRef::from_u32(0),
1436 exception: ExceptionTable::from_u32(1),
1437 }),
1438 InstructionData::TryCall {
1439 opcode: Opcode::TryCall,
1440 args,
1441 func_ref: FuncRef::from_u32(1),
1442 exception: ExceptionTable::from_u32(2),
1443 }
1444 );
1445
1446 assert_eq!(
1448 map(InstructionData::Jump {
1449 opcode: Opcode::Jump,
1450 destination: BlockCall::new(Block::from_u32(99), [], &mut pool),
1451 }),
1452 map(InstructionData::Jump {
1453 opcode: Opcode::Jump,
1454 destination: BlockCall::new(Block::from_u32(42), [], &mut pool),
1455 })
1456 );
1457
1458 assert_eq!(
1460 map(InstructionData::ExceptionHandlerAddress {
1461 opcode: Opcode::GetExceptionHandlerAddress,
1462 block: Block::from_u32(1),
1463 imm: 0.into(),
1464 }),
1465 InstructionData::ExceptionHandlerAddress {
1466 opcode: Opcode::GetExceptionHandlerAddress,
1467 block: Block::from_u32(2),
1468 imm: 0.into(),
1469 },
1470 );
1471
1472 assert_eq!(
1474 map(InstructionData::CallIndirect {
1475 opcode: Opcode::CallIndirect,
1476 args,
1477 sig_ref: SigRef::from_u32(11)
1478 }),
1479 InstructionData::CallIndirect {
1480 opcode: Opcode::CallIndirect,
1481 args: ValueList::new(),
1482 sig_ref: SigRef::from_u32(12)
1483 }
1484 );
1485
1486 assert_eq!(
1488 map(InstructionData::StackLoad {
1489 opcode: Opcode::StackLoad,
1490 stack_slot: StackSlot::from_u32(0),
1491 offset: 0.into()
1492 }),
1493 InstructionData::StackLoad {
1494 opcode: Opcode::StackLoad,
1495 stack_slot: StackSlot::from_u32(1),
1496 offset: 0.into()
1497 },
1498 );
1499
1500 assert_eq!(
1502 map(InstructionData::DynamicStackLoad {
1503 opcode: Opcode::DynamicStackLoad,
1504 dynamic_stack_slot: DynamicStackSlot::from_u32(0),
1505 }),
1506 InstructionData::DynamicStackLoad {
1507 opcode: Opcode::DynamicStackLoad,
1508 dynamic_stack_slot: DynamicStackSlot::from_u32(1),
1509 },
1510 );
1511
1512 assert_eq!(
1514 map(InstructionData::UnaryConst {
1515 opcode: ir::Opcode::Vconst,
1516 constant_handle: ir::Constant::from_u32(2)
1517 }),
1518 InstructionData::UnaryConst {
1519 opcode: ir::Opcode::Vconst,
1520 constant_handle: ir::Constant::from_u32(3)
1521 },
1522 );
1523
1524 assert_eq!(
1526 map(InstructionData::Shuffle {
1527 opcode: ir::Opcode::Shuffle,
1528 args: [Value::from_u32(0), Value::from_u32(1)],
1529 imm: ir::Immediate::from_u32(41),
1530 }),
1531 InstructionData::Shuffle {
1532 opcode: ir::Opcode::Shuffle,
1533 args: [Value::from_u32(1), Value::from_u32(2)],
1534 imm: ir::Immediate::from_u32(42),
1535 },
1536 );
1537 }
1538}