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_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;
1066
1067 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;
1069
1070 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;
1072
1073 fn map_dynamic_stack_slot(
1075 &mut self,
1076 dynamic_stack_slot: ir::DynamicStackSlot,
1077 ) -> ir::DynamicStackSlot;
1078
1079 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;
1081
1082 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;
1084}
1085
1086impl<'a, T> InstructionMapper for &'a mut T
1087where
1088 T: InstructionMapper,
1089{
1090 fn map_value(&mut self, value: Value) -> Value {
1091 (**self).map_value(value)
1092 }
1093
1094 fn map_value_list(&mut self, value_list: ValueList) -> ValueList {
1095 (**self).map_value_list(value_list)
1096 }
1097
1098 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1099 (**self).map_global_value(global_value)
1100 }
1101
1102 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1103 (**self).map_jump_table(jump_table)
1104 }
1105
1106 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1107 (**self).map_exception_table(exception_table)
1108 }
1109
1110 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {
1111 (**self).map_block_call(block_call)
1112 }
1113
1114 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1115 (**self).map_func_ref(func_ref)
1116 }
1117
1118 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1119 (**self).map_sig_ref(sig_ref)
1120 }
1121
1122 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1123 (**self).map_stack_slot(stack_slot)
1124 }
1125
1126 fn map_dynamic_stack_slot(
1127 &mut self,
1128 dynamic_stack_slot: ir::DynamicStackSlot,
1129 ) -> ir::DynamicStackSlot {
1130 (**self).map_dynamic_stack_slot(dynamic_stack_slot)
1131 }
1132
1133 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1134 (**self).map_constant(constant)
1135 }
1136
1137 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1138 (**self).map_immediate(immediate)
1139 }
1140}
1141
1142#[cfg(test)]
1143mod tests {
1144 use super::*;
1145 use alloc::string::ToString;
1146 use ir::{DynamicStackSlot, GlobalValue, JumpTable};
1147
1148 #[test]
1149 fn inst_data_is_copy() {
1150 fn is_copy<T: Copy>() {}
1151 is_copy::<InstructionData>();
1152 }
1153
1154 #[test]
1155 fn inst_data_size() {
1156 assert_eq!(std::mem::size_of::<InstructionData>(), 16);
1159 }
1160
1161 #[test]
1162 fn opcodes() {
1163 use core::mem;
1164
1165 let x = Opcode::Iadd;
1166 let mut y = Opcode::Isub;
1167
1168 assert!(x != y);
1169 y = Opcode::Iadd;
1170 assert_eq!(x, y);
1171 assert_eq!(x.format(), InstructionFormat::Binary);
1172
1173 assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm");
1174 assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm");
1175
1176 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
1178 assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm));
1179 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
1180 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
1181 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
1182
1183 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
1188 }
1189
1190 #[test]
1191 fn instruction_data() {
1192 use core::mem;
1193 assert_eq!(mem::size_of::<InstructionData>(), 16);
1198 }
1199
1200 #[test]
1201 fn constraints() {
1202 let a = Opcode::Iadd.constraints();
1203 assert!(a.use_typevar_operand());
1204 assert!(!a.requires_typevar_operand());
1205 assert_eq!(a.num_fixed_results(), 1);
1206 assert_eq!(a.num_fixed_value_arguments(), 2);
1207 assert_eq!(a.result_type(0, types::I32), types::I32);
1208 assert_eq!(a.result_type(0, types::I8), types::I8);
1209 assert_eq!(
1210 a.value_argument_constraint(0, types::I32),
1211 ResolvedConstraint::Bound(types::I32)
1212 );
1213 assert_eq!(
1214 a.value_argument_constraint(1, types::I32),
1215 ResolvedConstraint::Bound(types::I32)
1216 );
1217
1218 let b = Opcode::Bitcast.constraints();
1219 assert!(!b.use_typevar_operand());
1220 assert!(!b.requires_typevar_operand());
1221 assert_eq!(b.num_fixed_results(), 1);
1222 assert_eq!(b.num_fixed_value_arguments(), 1);
1223 assert_eq!(b.result_type(0, types::I32), types::I32);
1224 assert_eq!(b.result_type(0, types::I8), types::I8);
1225 match b.value_argument_constraint(0, types::I32) {
1226 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
1227 _ => panic!("Unexpected constraint from value_argument_constraint"),
1228 }
1229
1230 let c = Opcode::Call.constraints();
1231 assert_eq!(c.num_fixed_results(), 0);
1232 assert_eq!(c.num_fixed_value_arguments(), 0);
1233
1234 let i = Opcode::CallIndirect.constraints();
1235 assert_eq!(i.num_fixed_results(), 0);
1236 assert_eq!(i.num_fixed_value_arguments(), 1);
1237
1238 let cmp = Opcode::Icmp.constraints();
1239 assert!(cmp.use_typevar_operand());
1240 assert!(cmp.requires_typevar_operand());
1241 assert_eq!(cmp.num_fixed_results(), 1);
1242 assert_eq!(cmp.num_fixed_value_arguments(), 2);
1243 assert_eq!(cmp.result_type(0, types::I64), types::I8);
1244 }
1245
1246 #[test]
1247 fn value_set() {
1248 use crate::ir::types::*;
1249
1250 let vts = ValueTypeSet {
1251 lanes: BitSet16::from_range(0, 8),
1252 ints: BitSet8::from_range(4, 7),
1253 floats: BitSet8::from_range(0, 0),
1254 dynamic_lanes: BitSet16::from_range(0, 4),
1255 };
1256 assert!(!vts.contains(I8));
1257 assert!(vts.contains(I32));
1258 assert!(vts.contains(I64));
1259 assert!(vts.contains(I32X4));
1260 assert!(vts.contains(I32X4XN));
1261 assert!(!vts.contains(F16));
1262 assert!(!vts.contains(F32));
1263 assert!(!vts.contains(F128));
1264 assert_eq!(vts.example().to_string(), "i32");
1265
1266 let vts = ValueTypeSet {
1267 lanes: BitSet16::from_range(0, 8),
1268 ints: BitSet8::from_range(0, 0),
1269 floats: BitSet8::from_range(5, 7),
1270 dynamic_lanes: BitSet16::from_range(0, 8),
1271 };
1272 assert_eq!(vts.example().to_string(), "f32");
1273
1274 let vts = ValueTypeSet {
1275 lanes: BitSet16::from_range(1, 8),
1276 ints: BitSet8::from_range(0, 0),
1277 floats: BitSet8::from_range(5, 7),
1278 dynamic_lanes: BitSet16::from_range(0, 8),
1279 };
1280 assert_eq!(vts.example().to_string(), "f32x2");
1281
1282 let vts = ValueTypeSet {
1283 lanes: BitSet16::from_range(2, 8),
1284 ints: BitSet8::from_range(3, 7),
1285 floats: BitSet8::from_range(0, 0),
1286 dynamic_lanes: BitSet16::from_range(0, 8),
1287 };
1288 assert_eq!(vts.example().to_string(), "i32x4");
1289
1290 let vts = ValueTypeSet {
1291 lanes: BitSet16::from_range(0, 9),
1293 ints: BitSet8::from_range(3, 7),
1294 floats: BitSet8::from_range(0, 0),
1295 dynamic_lanes: BitSet16::from_range(0, 8),
1296 };
1297 assert!(vts.contains(I32));
1298 assert!(vts.contains(I32X4));
1299 }
1300
1301 #[test]
1302 fn instruction_data_map() {
1303 struct TestMapper;
1304
1305 impl InstructionMapper for TestMapper {
1306 fn map_value(&mut self, value: Value) -> Value {
1307 Value::from_u32(value.as_u32() + 1)
1308 }
1309
1310 fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {
1311 ValueList::new()
1312 }
1313
1314 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1315 GlobalValue::from_u32(global_value.as_u32() + 1)
1316 }
1317
1318 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1319 JumpTable::from_u32(jump_table.as_u32() + 1)
1320 }
1321
1322 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1323 ExceptionTable::from_u32(exception_table.as_u32() + 1)
1324 }
1325
1326 fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {
1327 let block = Block::from_u32(42);
1328 let mut pool = ValueListPool::new();
1329 BlockCall::new(block, [], &mut pool)
1330 }
1331
1332 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1333 FuncRef::from_u32(func_ref.as_u32() + 1)
1334 }
1335
1336 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1337 SigRef::from_u32(sig_ref.as_u32() + 1)
1338 }
1339
1340 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1341 StackSlot::from_u32(stack_slot.as_u32() + 1)
1342 }
1343
1344 fn map_dynamic_stack_slot(
1345 &mut self,
1346 dynamic_stack_slot: ir::DynamicStackSlot,
1347 ) -> ir::DynamicStackSlot {
1348 DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)
1349 }
1350
1351 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1352 ir::Constant::from_u32(constant.as_u32() + 1)
1353 }
1354
1355 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1356 ir::Immediate::from_u32(immediate.as_u32() + 1)
1357 }
1358 }
1359
1360 let mut pool = ValueListPool::new();
1361 let map = |inst: InstructionData| inst.map(TestMapper);
1362
1363 assert_eq!(
1365 map(InstructionData::Binary {
1366 opcode: Opcode::Iadd,
1367 args: [Value::from_u32(10), Value::from_u32(20)]
1368 }),
1369 InstructionData::Binary {
1370 opcode: Opcode::Iadd,
1371 args: [Value::from_u32(11), Value::from_u32(21)]
1372 }
1373 );
1374
1375 let mut args = ValueList::new();
1377 args.push(Value::from_u32(42), &mut pool);
1378 let func_ref = FuncRef::from_u32(99);
1379 let inst = map(InstructionData::Call {
1380 opcode: Opcode::Call,
1381 args,
1382 func_ref,
1383 });
1384 let InstructionData::Call {
1385 opcode: Opcode::Call,
1386 args,
1387 func_ref,
1388 } = inst
1389 else {
1390 panic!()
1391 };
1392 assert!(args.is_empty());
1393 assert_eq!(func_ref, FuncRef::from_u32(100));
1394
1395 assert_eq!(
1397 map(InstructionData::UnaryGlobalValue {
1398 opcode: Opcode::GlobalValue,
1399 global_value: GlobalValue::from_u32(4),
1400 }),
1401 InstructionData::UnaryGlobalValue {
1402 opcode: Opcode::GlobalValue,
1403 global_value: GlobalValue::from_u32(5),
1404 }
1405 );
1406
1407 assert_eq!(
1409 map(InstructionData::BranchTable {
1410 opcode: Opcode::BrTable,
1411 arg: Value::from_u32(0),
1412 table: JumpTable::from_u32(1),
1413 }),
1414 InstructionData::BranchTable {
1415 opcode: Opcode::BrTable,
1416 arg: Value::from_u32(1),
1417 table: JumpTable::from_u32(2),
1418 }
1419 );
1420
1421 assert_eq!(
1423 map(InstructionData::TryCall {
1424 opcode: Opcode::TryCall,
1425 args,
1426 func_ref: FuncRef::from_u32(0),
1427 exception: ExceptionTable::from_u32(1),
1428 }),
1429 InstructionData::TryCall {
1430 opcode: Opcode::TryCall,
1431 args,
1432 func_ref: FuncRef::from_u32(1),
1433 exception: ExceptionTable::from_u32(2),
1434 }
1435 );
1436
1437 assert_eq!(
1439 map(InstructionData::Jump {
1440 opcode: Opcode::Jump,
1441 destination: BlockCall::new(Block::from_u32(99), [], &mut pool),
1442 }),
1443 map(InstructionData::Jump {
1444 opcode: Opcode::Jump,
1445 destination: BlockCall::new(Block::from_u32(42), [], &mut pool),
1446 })
1447 );
1448
1449 assert_eq!(
1451 map(InstructionData::CallIndirect {
1452 opcode: Opcode::CallIndirect,
1453 args,
1454 sig_ref: SigRef::from_u32(11)
1455 }),
1456 InstructionData::CallIndirect {
1457 opcode: Opcode::CallIndirect,
1458 args: ValueList::new(),
1459 sig_ref: SigRef::from_u32(12)
1460 }
1461 );
1462
1463 assert_eq!(
1465 map(InstructionData::StackLoad {
1466 opcode: Opcode::StackLoad,
1467 stack_slot: StackSlot::from_u32(0),
1468 offset: 0.into()
1469 }),
1470 InstructionData::StackLoad {
1471 opcode: Opcode::StackLoad,
1472 stack_slot: StackSlot::from_u32(1),
1473 offset: 0.into()
1474 },
1475 );
1476
1477 assert_eq!(
1479 map(InstructionData::DynamicStackLoad {
1480 opcode: Opcode::DynamicStackLoad,
1481 dynamic_stack_slot: DynamicStackSlot::from_u32(0),
1482 }),
1483 InstructionData::DynamicStackLoad {
1484 opcode: Opcode::DynamicStackLoad,
1485 dynamic_stack_slot: DynamicStackSlot::from_u32(1),
1486 },
1487 );
1488
1489 assert_eq!(
1491 map(InstructionData::UnaryConst {
1492 opcode: ir::Opcode::Vconst,
1493 constant_handle: ir::Constant::from_u32(2)
1494 }),
1495 InstructionData::UnaryConst {
1496 opcode: ir::Opcode::Vconst,
1497 constant_handle: ir::Constant::from_u32(3)
1498 },
1499 );
1500
1501 assert_eq!(
1503 map(InstructionData::Shuffle {
1504 opcode: ir::Opcode::Shuffle,
1505 args: [Value::from_u32(0), Value::from_u32(1)],
1506 imm: ir::Immediate::from_u32(41),
1507 }),
1508 InstructionData::Shuffle {
1509 opcode: ir::Opcode::Shuffle,
1510 args: [Value::from_u32(1), Value::from_u32(2)],
1511 imm: ir::Immediate::from_u32(42),
1512 },
1513 );
1514 }
1515}