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}