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::StackAddr { offset, .. }
552 | &InstructionData::Store { offset, .. } => Some(offset.into()),
553 _ => None,
554 }
555 }
556
557 pub fn memflags(&self) -> Option<MemFlags> {
559 match self {
560 &InstructionData::Load { flags, .. }
561 | &InstructionData::LoadNoOffset { flags, .. }
562 | &InstructionData::Store { flags, .. }
563 | &InstructionData::StoreNoOffset { flags, .. }
564 | &InstructionData::AtomicCas { flags, .. }
565 | &InstructionData::AtomicRmw { flags, .. } => Some(flags),
566 _ => None,
567 }
568 }
569
570 pub fn memflags_data(&self, dfg: &super::dfg::DataFlowGraph) -> Option<super::MemFlagsData> {
573 self.memflags().map(|f| dfg.mem_flags[f])
574 }
575
576 pub fn stack_slot(&self) -> Option<StackSlot> {
578 match self {
579 &InstructionData::StackAddr { stack_slot, .. } => Some(stack_slot),
580 _ => None,
581 }
582 }
583
584 pub fn analyze_call<'a>(
588 &'a self,
589 pool: &'a ValueListPool,
590 exception_tables: &ExceptionTables,
591 ) -> CallInfo<'a> {
592 match *self {
593 Self::Call {
594 func_ref, ref args, ..
595 } => CallInfo::Direct(func_ref, args.as_slice(pool)),
596 Self::CallIndirect {
597 sig_ref, ref args, ..
598 } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
599 Self::TryCall {
600 func_ref,
601 ref args,
602 exception,
603 ..
604 } => {
605 let exdata = &exception_tables[exception];
606 CallInfo::DirectWithSig(func_ref, exdata.signature(), args.as_slice(pool))
607 }
608 Self::TryCallIndirect {
609 exception,
610 ref args,
611 ..
612 } => {
613 let exdata = &exception_tables[exception];
614 CallInfo::Indirect(exdata.signature(), &args.as_slice(pool)[1..])
615 }
616 Self::Ternary {
617 opcode: Opcode::StackSwitch,
618 ..
619 } => {
620 CallInfo::NotACall
623 }
624 _ => {
625 debug_assert!(!self.opcode().is_call());
626 CallInfo::NotACall
627 }
628 }
629 }
630
631 #[inline]
632 pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {
633 if ctrl_typevar.is_invalid() {
634 return;
635 }
636
637 let bit_width = ctrl_typevar.bits();
638
639 match self {
640 Self::UnaryImm { opcode: _, imm } => {
641 *imm = imm.mask_to_width(bit_width);
642 }
643 _ => {}
644 }
645 }
646
647 pub fn exception_table(&self) -> Option<ExceptionTable> {
649 match self {
650 Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
651 Some(*exception)
652 }
653 _ => None,
654 }
655 }
656}
657
658pub enum CallInfo<'a> {
660 NotACall,
662
663 Direct(FuncRef, &'a [Value]),
666
667 Indirect(SigRef, &'a [Value]),
669
670 DirectWithSig(FuncRef, SigRef, &'a [Value]),
674}
675
676#[derive(Clone, Copy)]
682pub struct OpcodeConstraints {
683 flags: u8,
702
703 typeset_offset: u8,
705
706 constraint_offset: u16,
710}
711
712impl OpcodeConstraints {
713 pub fn use_typevar_operand(self) -> bool {
717 (self.flags & 0x8) != 0
718 }
719
720 pub fn requires_typevar_operand(self) -> bool {
727 (self.flags & 0x10) != 0
728 }
729
730 pub fn num_fixed_results(self) -> usize {
733 (self.flags & 0x7) as usize
734 }
735
736 pub fn num_fixed_value_arguments(self) -> usize {
744 ((self.flags >> 5) & 0x7) as usize
745 }
746
747 fn typeset_offset(self) -> Option<usize> {
750 let offset = usize::from(self.typeset_offset);
751 if offset < TYPE_SETS.len() {
752 Some(offset)
753 } else {
754 None
755 }
756 }
757
758 fn constraint_offset(self) -> usize {
760 self.constraint_offset as usize
761 }
762
763 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
766 debug_assert!(n < self.num_fixed_results(), "Invalid result index");
767 match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {
768 ResolvedConstraint::Bound(t) => t,
769 ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"),
770 }
771 }
772
773 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
779 debug_assert!(
780 n < self.num_fixed_value_arguments(),
781 "Invalid value argument index"
782 );
783 let offset = self.constraint_offset() + self.num_fixed_results();
784 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
785 }
786
787 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
790 self.typeset_offset().map(|offset| TYPE_SETS[offset])
791 }
792
793 pub fn is_polymorphic(self) -> bool {
795 self.ctrl_typeset().is_some()
796 }
797}
798
799type BitSet8 = ScalarBitSet<u8>;
800type BitSet16 = ScalarBitSet<u16>;
801
802#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
804pub struct ValueTypeSet {
805 pub lanes: BitSet16,
807 pub ints: BitSet8,
809 pub floats: BitSet8,
811 pub dynamic_lanes: BitSet16,
813}
814
815impl ValueTypeSet {
816 fn is_base_type(self, scalar: Type) -> bool {
820 let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap();
821 if scalar.is_int() {
822 self.ints.contains(l2b)
823 } else if scalar.is_float() {
824 self.floats.contains(l2b)
825 } else {
826 false
827 }
828 }
829
830 pub fn contains(self, typ: Type) -> bool {
832 if typ.is_dynamic_vector() {
833 let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap();
834 self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())
835 } else {
836 let l2l = u8::try_from(typ.log2_lane_count()).unwrap();
837 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
838 }
839 }
840
841 pub fn example(self) -> Type {
845 let t = if self.ints.max().unwrap_or(0) > 5 {
846 types::I32
847 } else if self.floats.max().unwrap_or(0) > 5 {
848 types::F32
849 } else {
850 types::I8
851 };
852 t.by(1 << self.lanes.min().unwrap()).unwrap()
853 }
854}
855
856enum OperandConstraint {
858 Concrete(Type),
860
861 Free(u8),
864
865 Same,
867
868 LaneOf,
870
871 AsTruthy,
873
874 HalfWidth,
876
877 DoubleWidth,
879
880 SplitLanes,
882
883 MergeLanes,
885
886 DynamicToVector,
888
889 Narrower,
891
892 Wider,
894}
895
896impl OperandConstraint {
897 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
900 use self::OperandConstraint::*;
901 use self::ResolvedConstraint::Bound;
902 match *self {
903 Concrete(t) => Bound(t),
904 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
905 Same => Bound(ctrl_type),
906 LaneOf => Bound(ctrl_type.lane_of()),
907 AsTruthy => Bound(ctrl_type.as_truthy()),
908 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
909 DoubleWidth => Bound(
910 ctrl_type
911 .double_width()
912 .expect("invalid type for double_width"),
913 ),
914 SplitLanes => {
915 if ctrl_type.is_dynamic_vector() {
916 Bound(
917 ctrl_type
918 .dynamic_to_vector()
919 .expect("invalid type for dynamic_to_vector")
920 .split_lanes()
921 .expect("invalid type for split_lanes")
922 .vector_to_dynamic()
923 .expect("invalid dynamic type"),
924 )
925 } else {
926 Bound(
927 ctrl_type
928 .split_lanes()
929 .expect("invalid type for split_lanes"),
930 )
931 }
932 }
933 MergeLanes => {
934 if ctrl_type.is_dynamic_vector() {
935 Bound(
936 ctrl_type
937 .dynamic_to_vector()
938 .expect("invalid type for dynamic_to_vector")
939 .merge_lanes()
940 .expect("invalid type for merge_lanes")
941 .vector_to_dynamic()
942 .expect("invalid dynamic type"),
943 )
944 } else {
945 Bound(
946 ctrl_type
947 .merge_lanes()
948 .expect("invalid type for merge_lanes"),
949 )
950 }
951 }
952 DynamicToVector => Bound(
953 ctrl_type
954 .dynamic_to_vector()
955 .expect("invalid type for dynamic_to_vector"),
956 ),
957 Narrower => {
958 let ctrl_type_bits = ctrl_type.log2_lane_bits();
959 let mut tys = ValueTypeSet::default();
960
961 tys.lanes = ScalarBitSet::from_range(0, 1);
963
964 if ctrl_type.is_int() {
965 tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);
968 } else if ctrl_type.is_float() {
969 tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8);
972 } else {
973 panic!(
974 "The Narrower constraint only operates on floats or ints, got {ctrl_type:?}"
975 );
976 }
977 ResolvedConstraint::Free(tys)
978 }
979 Wider => {
980 let ctrl_type_bits = ctrl_type.log2_lane_bits();
981 let mut tys = ValueTypeSet::default();
982
983 tys.lanes = ScalarBitSet::from_range(0, 1);
985
986 if ctrl_type.is_int() {
987 let lower_bound = ctrl_type_bits as u8 + 1;
988 if lower_bound < BitSet8::capacity() {
994 tys.ints = BitSet8::from_range(lower_bound, 8);
998 }
999 } else if ctrl_type.is_float() {
1000 let lower_bound = ctrl_type_bits as u8 + 1;
1002 if lower_bound < BitSet8::capacity() {
1003 tys.floats = BitSet8::from_range(lower_bound, 8);
1004 }
1005 } else {
1006 panic!(
1007 "The Wider constraint only operates on floats or ints, got {ctrl_type:?}"
1008 );
1009 }
1010
1011 ResolvedConstraint::Free(tys)
1012 }
1013 }
1014 }
1015}
1016
1017#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1019pub enum ResolvedConstraint {
1020 Bound(Type),
1022 Free(ValueTypeSet),
1024}
1025
1026pub trait InstructionMapper {
1029 fn map_value(&mut self, value: Value) -> Value;
1031
1032 fn map_value_list(&mut self, value_list: ValueList) -> ValueList;
1034
1035 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue;
1037
1038 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable;
1040
1041 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable;
1043
1044 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall;
1046
1047 fn map_block(&mut self, block: Block) -> Block;
1049
1050 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;
1052
1053 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;
1055
1056 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;
1058
1059 fn map_dynamic_stack_slot(
1061 &mut self,
1062 dynamic_stack_slot: ir::DynamicStackSlot,
1063 ) -> ir::DynamicStackSlot;
1064
1065 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;
1067
1068 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;
1070
1071 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1077 flags
1078 }
1079}
1080
1081impl<'a, T> InstructionMapper for &'a mut T
1082where
1083 T: InstructionMapper,
1084{
1085 fn map_value(&mut self, value: Value) -> Value {
1086 (**self).map_value(value)
1087 }
1088
1089 fn map_value_list(&mut self, value_list: ValueList) -> ValueList {
1090 (**self).map_value_list(value_list)
1091 }
1092
1093 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1094 (**self).map_global_value(global_value)
1095 }
1096
1097 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1098 (**self).map_jump_table(jump_table)
1099 }
1100
1101 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1102 (**self).map_exception_table(exception_table)
1103 }
1104
1105 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {
1106 (**self).map_block_call(block_call)
1107 }
1108
1109 fn map_block(&mut self, block: Block) -> Block {
1110 (**self).map_block(block)
1111 }
1112
1113 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1114 (**self).map_func_ref(func_ref)
1115 }
1116
1117 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1118 (**self).map_sig_ref(sig_ref)
1119 }
1120
1121 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1122 (**self).map_stack_slot(stack_slot)
1123 }
1124
1125 fn map_dynamic_stack_slot(
1126 &mut self,
1127 dynamic_stack_slot: ir::DynamicStackSlot,
1128 ) -> ir::DynamicStackSlot {
1129 (**self).map_dynamic_stack_slot(dynamic_stack_slot)
1130 }
1131
1132 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1133 (**self).map_constant(constant)
1134 }
1135
1136 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1137 (**self).map_immediate(immediate)
1138 }
1139
1140 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1141 (**self).map_mem_flags(flags)
1142 }
1143}
1144
1145#[cfg(test)]
1146mod tests {
1147 use super::*;
1148 use alloc::string::ToString;
1149 use ir::{DynamicStackSlot, GlobalValue, JumpTable};
1150
1151 #[test]
1152 fn inst_data_is_copy() {
1153 fn is_copy<T: Copy>() {}
1154 is_copy::<InstructionData>();
1155 }
1156
1157 #[test]
1158 fn inst_data_size() {
1159 assert_eq!(core::mem::size_of::<InstructionData>(), 16);
1162 }
1163
1164 #[test]
1165 fn opcodes() {
1166 use core::mem;
1167
1168 let x = Opcode::Iadd;
1169 let mut y = Opcode::Isub;
1170
1171 assert!(x != y);
1172 y = Opcode::Iadd;
1173 assert_eq!(x, y);
1174 assert_eq!(x.format(), InstructionFormat::Binary);
1175
1176 assert_eq!(format!("{:?}", Opcode::StackAddr), "StackAddr");
1177 assert_eq!(Opcode::StackAddr.to_string(), "stack_addr");
1178
1179 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
1181 assert_eq!("stack_addr".parse::<Opcode>(), Ok(Opcode::StackAddr));
1182 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
1183 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
1184 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
1185
1186 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
1191 }
1192
1193 #[test]
1194 fn instruction_data() {
1195 use core::mem;
1196 assert_eq!(mem::size_of::<InstructionData>(), 16);
1201 }
1202
1203 #[test]
1204 fn constraints() {
1205 let a = Opcode::Iadd.constraints();
1206 assert!(a.use_typevar_operand());
1207 assert!(!a.requires_typevar_operand());
1208 assert_eq!(a.num_fixed_results(), 1);
1209 assert_eq!(a.num_fixed_value_arguments(), 2);
1210 assert_eq!(a.result_type(0, types::I32), types::I32);
1211 assert_eq!(a.result_type(0, types::I8), types::I8);
1212 assert_eq!(
1213 a.value_argument_constraint(0, types::I32),
1214 ResolvedConstraint::Bound(types::I32)
1215 );
1216 assert_eq!(
1217 a.value_argument_constraint(1, types::I32),
1218 ResolvedConstraint::Bound(types::I32)
1219 );
1220
1221 let b = Opcode::Bitcast.constraints();
1222 assert!(!b.use_typevar_operand());
1223 assert!(!b.requires_typevar_operand());
1224 assert_eq!(b.num_fixed_results(), 1);
1225 assert_eq!(b.num_fixed_value_arguments(), 1);
1226 assert_eq!(b.result_type(0, types::I32), types::I32);
1227 assert_eq!(b.result_type(0, types::I8), types::I8);
1228 match b.value_argument_constraint(0, types::I32) {
1229 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
1230 _ => panic!("Unexpected constraint from value_argument_constraint"),
1231 }
1232
1233 let c = Opcode::Call.constraints();
1234 assert_eq!(c.num_fixed_results(), 0);
1235 assert_eq!(c.num_fixed_value_arguments(), 0);
1236
1237 let i = Opcode::CallIndirect.constraints();
1238 assert_eq!(i.num_fixed_results(), 0);
1239 assert_eq!(i.num_fixed_value_arguments(), 1);
1240
1241 let cmp = Opcode::Icmp.constraints();
1242 assert!(cmp.use_typevar_operand());
1243 assert!(cmp.requires_typevar_operand());
1244 assert_eq!(cmp.num_fixed_results(), 1);
1245 assert_eq!(cmp.num_fixed_value_arguments(), 2);
1246 assert_eq!(cmp.result_type(0, types::I64), types::I8);
1247 }
1248
1249 #[test]
1250 fn value_set() {
1251 use crate::ir::types::*;
1252
1253 let vts = ValueTypeSet {
1254 lanes: BitSet16::from_range(0, 8),
1255 ints: BitSet8::from_range(4, 7),
1256 floats: BitSet8::from_range(0, 0),
1257 dynamic_lanes: BitSet16::from_range(0, 4),
1258 };
1259 assert!(!vts.contains(I8));
1260 assert!(vts.contains(I32));
1261 assert!(vts.contains(I64));
1262 assert!(vts.contains(I32X4));
1263 assert!(vts.contains(I32X4XN));
1264 assert!(!vts.contains(F16));
1265 assert!(!vts.contains(F32));
1266 assert!(!vts.contains(F128));
1267 assert_eq!(vts.example().to_string(), "i32");
1268
1269 let vts = ValueTypeSet {
1270 lanes: BitSet16::from_range(0, 8),
1271 ints: BitSet8::from_range(0, 0),
1272 floats: BitSet8::from_range(5, 7),
1273 dynamic_lanes: BitSet16::from_range(0, 8),
1274 };
1275 assert_eq!(vts.example().to_string(), "f32");
1276
1277 let vts = ValueTypeSet {
1278 lanes: BitSet16::from_range(1, 8),
1279 ints: BitSet8::from_range(0, 0),
1280 floats: BitSet8::from_range(5, 7),
1281 dynamic_lanes: BitSet16::from_range(0, 8),
1282 };
1283 assert_eq!(vts.example().to_string(), "f32x2");
1284
1285 let vts = ValueTypeSet {
1286 lanes: BitSet16::from_range(2, 8),
1287 ints: BitSet8::from_range(3, 7),
1288 floats: BitSet8::from_range(0, 0),
1289 dynamic_lanes: BitSet16::from_range(0, 8),
1290 };
1291 assert_eq!(vts.example().to_string(), "i32x4");
1292
1293 let vts = ValueTypeSet {
1294 lanes: BitSet16::from_range(0, 9),
1296 ints: BitSet8::from_range(3, 7),
1297 floats: BitSet8::from_range(0, 0),
1298 dynamic_lanes: BitSet16::from_range(0, 8),
1299 };
1300 assert!(vts.contains(I32));
1301 assert!(vts.contains(I32X4));
1302 }
1303
1304 #[test]
1305 fn instruction_data_map() {
1306 struct TestMapper;
1307
1308 impl InstructionMapper for TestMapper {
1309 fn map_value(&mut self, value: Value) -> Value {
1310 Value::from_u32(value.as_u32() + 1)
1311 }
1312
1313 fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {
1314 ValueList::new()
1315 }
1316
1317 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1318 GlobalValue::from_u32(global_value.as_u32() + 1)
1319 }
1320
1321 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1322 JumpTable::from_u32(jump_table.as_u32() + 1)
1323 }
1324
1325 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1326 ExceptionTable::from_u32(exception_table.as_u32() + 1)
1327 }
1328
1329 fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {
1330 let block = Block::from_u32(42);
1331 let mut pool = ValueListPool::new();
1332 BlockCall::new(block, [], &mut pool)
1333 }
1334
1335 fn map_block(&mut self, block: Block) -> Block {
1336 Block::from_u32(block.as_u32() + 1)
1337 }
1338
1339 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1340 FuncRef::from_u32(func_ref.as_u32() + 1)
1341 }
1342
1343 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1344 SigRef::from_u32(sig_ref.as_u32() + 1)
1345 }
1346
1347 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1348 StackSlot::from_u32(stack_slot.as_u32() + 1)
1349 }
1350
1351 fn map_dynamic_stack_slot(
1352 &mut self,
1353 dynamic_stack_slot: ir::DynamicStackSlot,
1354 ) -> ir::DynamicStackSlot {
1355 DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)
1356 }
1357
1358 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1359 ir::Constant::from_u32(constant.as_u32() + 1)
1360 }
1361
1362 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1363 ir::Immediate::from_u32(immediate.as_u32() + 1)
1364 }
1365 }
1366
1367 let mut pool = ValueListPool::new();
1368 let map = |inst: InstructionData| inst.map(TestMapper);
1369
1370 assert_eq!(
1372 map(InstructionData::Binary {
1373 opcode: Opcode::Iadd,
1374 args: [Value::from_u32(10), Value::from_u32(20)]
1375 }),
1376 InstructionData::Binary {
1377 opcode: Opcode::Iadd,
1378 args: [Value::from_u32(11), Value::from_u32(21)]
1379 }
1380 );
1381
1382 let mut args = ValueList::new();
1384 args.push(Value::from_u32(42), &mut pool);
1385 let func_ref = FuncRef::from_u32(99);
1386 let inst = map(InstructionData::Call {
1387 opcode: Opcode::Call,
1388 args,
1389 func_ref,
1390 });
1391 let InstructionData::Call {
1392 opcode: Opcode::Call,
1393 args,
1394 func_ref,
1395 } = inst
1396 else {
1397 panic!()
1398 };
1399 assert!(args.is_empty());
1400 assert_eq!(func_ref, FuncRef::from_u32(100));
1401
1402 assert_eq!(
1404 map(InstructionData::UnaryGlobalValue {
1405 opcode: Opcode::SymbolValue,
1406 global_value: GlobalValue::from_u32(4),
1407 }),
1408 InstructionData::UnaryGlobalValue {
1409 opcode: Opcode::SymbolValue,
1410 global_value: GlobalValue::from_u32(5),
1411 }
1412 );
1413
1414 assert_eq!(
1416 map(InstructionData::BranchTable {
1417 opcode: Opcode::BrTable,
1418 arg: Value::from_u32(0),
1419 table: JumpTable::from_u32(1),
1420 }),
1421 InstructionData::BranchTable {
1422 opcode: Opcode::BrTable,
1423 arg: Value::from_u32(1),
1424 table: JumpTable::from_u32(2),
1425 }
1426 );
1427
1428 assert_eq!(
1430 map(InstructionData::TryCall {
1431 opcode: Opcode::TryCall,
1432 args,
1433 func_ref: FuncRef::from_u32(0),
1434 exception: ExceptionTable::from_u32(1),
1435 }),
1436 InstructionData::TryCall {
1437 opcode: Opcode::TryCall,
1438 args,
1439 func_ref: FuncRef::from_u32(1),
1440 exception: ExceptionTable::from_u32(2),
1441 }
1442 );
1443
1444 assert_eq!(
1446 map(InstructionData::Jump {
1447 opcode: Opcode::Jump,
1448 destination: BlockCall::new(Block::from_u32(99), [], &mut pool),
1449 }),
1450 map(InstructionData::Jump {
1451 opcode: Opcode::Jump,
1452 destination: BlockCall::new(Block::from_u32(42), [], &mut pool),
1453 })
1454 );
1455
1456 assert_eq!(
1458 map(InstructionData::ExceptionHandlerAddress {
1459 opcode: Opcode::GetExceptionHandlerAddress,
1460 block: Block::from_u32(1),
1461 imm: 0.into(),
1462 }),
1463 InstructionData::ExceptionHandlerAddress {
1464 opcode: Opcode::GetExceptionHandlerAddress,
1465 block: Block::from_u32(2),
1466 imm: 0.into(),
1467 },
1468 );
1469
1470 assert_eq!(
1472 map(InstructionData::CallIndirect {
1473 opcode: Opcode::CallIndirect,
1474 args,
1475 sig_ref: SigRef::from_u32(11)
1476 }),
1477 InstructionData::CallIndirect {
1478 opcode: Opcode::CallIndirect,
1479 args: ValueList::new(),
1480 sig_ref: SigRef::from_u32(12)
1481 }
1482 );
1483
1484 assert_eq!(
1486 map(InstructionData::StackAddr {
1487 opcode: Opcode::StackAddr,
1488 stack_slot: StackSlot::from_u32(0),
1489 offset: 0.into()
1490 }),
1491 InstructionData::StackAddr {
1492 opcode: Opcode::StackAddr,
1493 stack_slot: StackSlot::from_u32(1),
1494 offset: 0.into()
1495 },
1496 );
1497
1498 assert_eq!(
1500 map(InstructionData::DynamicStackAddr {
1501 opcode: Opcode::DynamicStackAddr,
1502 dynamic_stack_slot: DynamicStackSlot::from_u32(0),
1503 }),
1504 InstructionData::DynamicStackAddr {
1505 opcode: Opcode::DynamicStackAddr,
1506 dynamic_stack_slot: DynamicStackSlot::from_u32(1),
1507 },
1508 );
1509
1510 assert_eq!(
1512 map(InstructionData::UnaryConst {
1513 opcode: ir::Opcode::Vconst,
1514 constant_handle: ir::Constant::from_u32(2)
1515 }),
1516 InstructionData::UnaryConst {
1517 opcode: ir::Opcode::Vconst,
1518 constant_handle: ir::Constant::from_u32(3)
1519 },
1520 );
1521
1522 assert_eq!(
1524 map(InstructionData::Shuffle {
1525 opcode: ir::Opcode::Shuffle,
1526 args: [Value::from_u32(0), Value::from_u32(1)],
1527 imm: ir::Immediate::from_u32(41),
1528 }),
1529 InstructionData::Shuffle {
1530 opcode: ir::Opcode::Shuffle,
1531 args: [Value::from_u32(1), Value::from_u32(2)],
1532 imm: ir::Immediate::from_u32(42),
1533 },
1534 );
1535 }
1536}