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, .. }
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 memflags_data(&self, dfg: &super::dfg::DataFlowGraph) -> Option<super::MemFlagsData> {
575 self.memflags().map(|f| dfg.mem_flags[f])
576 }
577
578 pub fn stack_slot(&self) -> Option<StackSlot> {
580 match self {
581 &InstructionData::StackStore { stack_slot, .. }
582 | &InstructionData::StackLoad { stack_slot, .. } => Some(stack_slot),
583 _ => None,
584 }
585 }
586
587 pub fn analyze_call<'a>(
591 &'a self,
592 pool: &'a ValueListPool,
593 exception_tables: &ExceptionTables,
594 ) -> CallInfo<'a> {
595 match *self {
596 Self::Call {
597 func_ref, ref args, ..
598 } => CallInfo::Direct(func_ref, args.as_slice(pool)),
599 Self::CallIndirect {
600 sig_ref, ref args, ..
601 } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
602 Self::TryCall {
603 func_ref,
604 ref args,
605 exception,
606 ..
607 } => {
608 let exdata = &exception_tables[exception];
609 CallInfo::DirectWithSig(func_ref, exdata.signature(), args.as_slice(pool))
610 }
611 Self::TryCallIndirect {
612 exception,
613 ref args,
614 ..
615 } => {
616 let exdata = &exception_tables[exception];
617 CallInfo::Indirect(exdata.signature(), &args.as_slice(pool)[1..])
618 }
619 Self::Ternary {
620 opcode: Opcode::StackSwitch,
621 ..
622 } => {
623 CallInfo::NotACall
626 }
627 _ => {
628 debug_assert!(!self.opcode().is_call());
629 CallInfo::NotACall
630 }
631 }
632 }
633
634 #[inline]
635 pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {
636 if ctrl_typevar.is_invalid() {
637 return;
638 }
639
640 let bit_width = ctrl_typevar.bits();
641
642 match self {
643 Self::UnaryImm { opcode: _, imm } => {
644 *imm = imm.mask_to_width(bit_width);
645 }
646 Self::BinaryImm64 {
647 opcode,
648 arg: _,
649 imm,
650 } => {
651 if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm {
652 *imm = imm.mask_to_width(bit_width);
653 }
654 }
655 Self::IntCompareImm {
656 opcode,
657 arg: _,
658 cond,
659 imm,
660 } => {
661 debug_assert_eq!(*opcode, Opcode::IcmpImm);
662 if cond.unsigned() != *cond {
663 *imm = imm.mask_to_width(bit_width);
664 }
665 }
666 _ => {}
667 }
668 }
669
670 pub fn exception_table(&self) -> Option<ExceptionTable> {
672 match self {
673 Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
674 Some(*exception)
675 }
676 _ => None,
677 }
678 }
679}
680
681pub enum CallInfo<'a> {
683 NotACall,
685
686 Direct(FuncRef, &'a [Value]),
689
690 Indirect(SigRef, &'a [Value]),
692
693 DirectWithSig(FuncRef, SigRef, &'a [Value]),
697}
698
699#[derive(Clone, Copy)]
705pub struct OpcodeConstraints {
706 flags: u8,
725
726 typeset_offset: u8,
728
729 constraint_offset: u16,
733}
734
735impl OpcodeConstraints {
736 pub fn use_typevar_operand(self) -> bool {
740 (self.flags & 0x8) != 0
741 }
742
743 pub fn requires_typevar_operand(self) -> bool {
750 (self.flags & 0x10) != 0
751 }
752
753 pub fn num_fixed_results(self) -> usize {
756 (self.flags & 0x7) as usize
757 }
758
759 pub fn num_fixed_value_arguments(self) -> usize {
767 ((self.flags >> 5) & 0x7) as usize
768 }
769
770 fn typeset_offset(self) -> Option<usize> {
773 let offset = usize::from(self.typeset_offset);
774 if offset < TYPE_SETS.len() {
775 Some(offset)
776 } else {
777 None
778 }
779 }
780
781 fn constraint_offset(self) -> usize {
783 self.constraint_offset as usize
784 }
785
786 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
789 debug_assert!(n < self.num_fixed_results(), "Invalid result index");
790 match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {
791 ResolvedConstraint::Bound(t) => t,
792 ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"),
793 }
794 }
795
796 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
802 debug_assert!(
803 n < self.num_fixed_value_arguments(),
804 "Invalid value argument index"
805 );
806 let offset = self.constraint_offset() + self.num_fixed_results();
807 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
808 }
809
810 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
813 self.typeset_offset().map(|offset| TYPE_SETS[offset])
814 }
815
816 pub fn is_polymorphic(self) -> bool {
818 self.ctrl_typeset().is_some()
819 }
820}
821
822type BitSet8 = ScalarBitSet<u8>;
823type BitSet16 = ScalarBitSet<u16>;
824
825#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
827pub struct ValueTypeSet {
828 pub lanes: BitSet16,
830 pub ints: BitSet8,
832 pub floats: BitSet8,
834 pub dynamic_lanes: BitSet16,
836}
837
838impl ValueTypeSet {
839 fn is_base_type(self, scalar: Type) -> bool {
843 let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap();
844 if scalar.is_int() {
845 self.ints.contains(l2b)
846 } else if scalar.is_float() {
847 self.floats.contains(l2b)
848 } else {
849 false
850 }
851 }
852
853 pub fn contains(self, typ: Type) -> bool {
855 if typ.is_dynamic_vector() {
856 let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap();
857 self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())
858 } else {
859 let l2l = u8::try_from(typ.log2_lane_count()).unwrap();
860 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
861 }
862 }
863
864 pub fn example(self) -> Type {
868 let t = if self.ints.max().unwrap_or(0) > 5 {
869 types::I32
870 } else if self.floats.max().unwrap_or(0) > 5 {
871 types::F32
872 } else {
873 types::I8
874 };
875 t.by(1 << self.lanes.min().unwrap()).unwrap()
876 }
877}
878
879enum OperandConstraint {
881 Concrete(Type),
883
884 Free(u8),
887
888 Same,
890
891 LaneOf,
893
894 AsTruthy,
896
897 HalfWidth,
899
900 DoubleWidth,
902
903 SplitLanes,
905
906 MergeLanes,
908
909 DynamicToVector,
911
912 Narrower,
914
915 Wider,
917}
918
919impl OperandConstraint {
920 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
923 use self::OperandConstraint::*;
924 use self::ResolvedConstraint::Bound;
925 match *self {
926 Concrete(t) => Bound(t),
927 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
928 Same => Bound(ctrl_type),
929 LaneOf => Bound(ctrl_type.lane_of()),
930 AsTruthy => Bound(ctrl_type.as_truthy()),
931 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
932 DoubleWidth => Bound(
933 ctrl_type
934 .double_width()
935 .expect("invalid type for double_width"),
936 ),
937 SplitLanes => {
938 if ctrl_type.is_dynamic_vector() {
939 Bound(
940 ctrl_type
941 .dynamic_to_vector()
942 .expect("invalid type for dynamic_to_vector")
943 .split_lanes()
944 .expect("invalid type for split_lanes")
945 .vector_to_dynamic()
946 .expect("invalid dynamic type"),
947 )
948 } else {
949 Bound(
950 ctrl_type
951 .split_lanes()
952 .expect("invalid type for split_lanes"),
953 )
954 }
955 }
956 MergeLanes => {
957 if ctrl_type.is_dynamic_vector() {
958 Bound(
959 ctrl_type
960 .dynamic_to_vector()
961 .expect("invalid type for dynamic_to_vector")
962 .merge_lanes()
963 .expect("invalid type for merge_lanes")
964 .vector_to_dynamic()
965 .expect("invalid dynamic type"),
966 )
967 } else {
968 Bound(
969 ctrl_type
970 .merge_lanes()
971 .expect("invalid type for merge_lanes"),
972 )
973 }
974 }
975 DynamicToVector => Bound(
976 ctrl_type
977 .dynamic_to_vector()
978 .expect("invalid type for dynamic_to_vector"),
979 ),
980 Narrower => {
981 let ctrl_type_bits = ctrl_type.log2_lane_bits();
982 let mut tys = ValueTypeSet::default();
983
984 tys.lanes = ScalarBitSet::from_range(0, 1);
986
987 if ctrl_type.is_int() {
988 tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);
991 } else if ctrl_type.is_float() {
992 tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8);
995 } else {
996 panic!(
997 "The Narrower constraint only operates on floats or ints, got {ctrl_type:?}"
998 );
999 }
1000 ResolvedConstraint::Free(tys)
1001 }
1002 Wider => {
1003 let ctrl_type_bits = ctrl_type.log2_lane_bits();
1004 let mut tys = ValueTypeSet::default();
1005
1006 tys.lanes = ScalarBitSet::from_range(0, 1);
1008
1009 if ctrl_type.is_int() {
1010 let lower_bound = ctrl_type_bits as u8 + 1;
1011 if lower_bound < BitSet8::capacity() {
1017 tys.ints = BitSet8::from_range(lower_bound, 8);
1021 }
1022 } else if ctrl_type.is_float() {
1023 let lower_bound = ctrl_type_bits as u8 + 1;
1025 if lower_bound < BitSet8::capacity() {
1026 tys.floats = BitSet8::from_range(lower_bound, 8);
1027 }
1028 } else {
1029 panic!(
1030 "The Wider constraint only operates on floats or ints, got {ctrl_type:?}"
1031 );
1032 }
1033
1034 ResolvedConstraint::Free(tys)
1035 }
1036 }
1037 }
1038}
1039
1040#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1042pub enum ResolvedConstraint {
1043 Bound(Type),
1045 Free(ValueTypeSet),
1047}
1048
1049pub trait InstructionMapper {
1052 fn map_value(&mut self, value: Value) -> Value;
1054
1055 fn map_value_list(&mut self, value_list: ValueList) -> ValueList;
1057
1058 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue;
1060
1061 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable;
1063
1064 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable;
1066
1067 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall;
1069
1070 fn map_block(&mut self, block: Block) -> Block;
1072
1073 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;
1075
1076 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;
1078
1079 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;
1081
1082 fn map_dynamic_stack_slot(
1084 &mut self,
1085 dynamic_stack_slot: ir::DynamicStackSlot,
1086 ) -> ir::DynamicStackSlot;
1087
1088 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;
1090
1091 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;
1093
1094 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1100 flags
1101 }
1102}
1103
1104impl<'a, T> InstructionMapper for &'a mut T
1105where
1106 T: InstructionMapper,
1107{
1108 fn map_value(&mut self, value: Value) -> Value {
1109 (**self).map_value(value)
1110 }
1111
1112 fn map_value_list(&mut self, value_list: ValueList) -> ValueList {
1113 (**self).map_value_list(value_list)
1114 }
1115
1116 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1117 (**self).map_global_value(global_value)
1118 }
1119
1120 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1121 (**self).map_jump_table(jump_table)
1122 }
1123
1124 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1125 (**self).map_exception_table(exception_table)
1126 }
1127
1128 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {
1129 (**self).map_block_call(block_call)
1130 }
1131
1132 fn map_block(&mut self, block: Block) -> Block {
1133 (**self).map_block(block)
1134 }
1135
1136 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1137 (**self).map_func_ref(func_ref)
1138 }
1139
1140 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1141 (**self).map_sig_ref(sig_ref)
1142 }
1143
1144 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1145 (**self).map_stack_slot(stack_slot)
1146 }
1147
1148 fn map_dynamic_stack_slot(
1149 &mut self,
1150 dynamic_stack_slot: ir::DynamicStackSlot,
1151 ) -> ir::DynamicStackSlot {
1152 (**self).map_dynamic_stack_slot(dynamic_stack_slot)
1153 }
1154
1155 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1156 (**self).map_constant(constant)
1157 }
1158
1159 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1160 (**self).map_immediate(immediate)
1161 }
1162
1163 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1164 (**self).map_mem_flags(flags)
1165 }
1166}
1167
1168#[cfg(test)]
1169mod tests {
1170 use super::*;
1171 use alloc::string::ToString;
1172 use ir::{DynamicStackSlot, GlobalValue, JumpTable};
1173
1174 #[test]
1175 fn inst_data_is_copy() {
1176 fn is_copy<T: Copy>() {}
1177 is_copy::<InstructionData>();
1178 }
1179
1180 #[test]
1181 fn inst_data_size() {
1182 assert_eq!(core::mem::size_of::<InstructionData>(), 16);
1185 }
1186
1187 #[test]
1188 fn opcodes() {
1189 use core::mem;
1190
1191 let x = Opcode::Iadd;
1192 let mut y = Opcode::Isub;
1193
1194 assert!(x != y);
1195 y = Opcode::Iadd;
1196 assert_eq!(x, y);
1197 assert_eq!(x.format(), InstructionFormat::Binary);
1198
1199 assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm");
1200 assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm");
1201
1202 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
1204 assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm));
1205 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
1206 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
1207 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
1208
1209 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
1214 }
1215
1216 #[test]
1217 fn instruction_data() {
1218 use core::mem;
1219 assert_eq!(mem::size_of::<InstructionData>(), 16);
1224 }
1225
1226 #[test]
1227 fn constraints() {
1228 let a = Opcode::Iadd.constraints();
1229 assert!(a.use_typevar_operand());
1230 assert!(!a.requires_typevar_operand());
1231 assert_eq!(a.num_fixed_results(), 1);
1232 assert_eq!(a.num_fixed_value_arguments(), 2);
1233 assert_eq!(a.result_type(0, types::I32), types::I32);
1234 assert_eq!(a.result_type(0, types::I8), types::I8);
1235 assert_eq!(
1236 a.value_argument_constraint(0, types::I32),
1237 ResolvedConstraint::Bound(types::I32)
1238 );
1239 assert_eq!(
1240 a.value_argument_constraint(1, types::I32),
1241 ResolvedConstraint::Bound(types::I32)
1242 );
1243
1244 let b = Opcode::Bitcast.constraints();
1245 assert!(!b.use_typevar_operand());
1246 assert!(!b.requires_typevar_operand());
1247 assert_eq!(b.num_fixed_results(), 1);
1248 assert_eq!(b.num_fixed_value_arguments(), 1);
1249 assert_eq!(b.result_type(0, types::I32), types::I32);
1250 assert_eq!(b.result_type(0, types::I8), types::I8);
1251 match b.value_argument_constraint(0, types::I32) {
1252 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
1253 _ => panic!("Unexpected constraint from value_argument_constraint"),
1254 }
1255
1256 let c = Opcode::Call.constraints();
1257 assert_eq!(c.num_fixed_results(), 0);
1258 assert_eq!(c.num_fixed_value_arguments(), 0);
1259
1260 let i = Opcode::CallIndirect.constraints();
1261 assert_eq!(i.num_fixed_results(), 0);
1262 assert_eq!(i.num_fixed_value_arguments(), 1);
1263
1264 let cmp = Opcode::Icmp.constraints();
1265 assert!(cmp.use_typevar_operand());
1266 assert!(cmp.requires_typevar_operand());
1267 assert_eq!(cmp.num_fixed_results(), 1);
1268 assert_eq!(cmp.num_fixed_value_arguments(), 2);
1269 assert_eq!(cmp.result_type(0, types::I64), types::I8);
1270 }
1271
1272 #[test]
1273 fn value_set() {
1274 use crate::ir::types::*;
1275
1276 let vts = ValueTypeSet {
1277 lanes: BitSet16::from_range(0, 8),
1278 ints: BitSet8::from_range(4, 7),
1279 floats: BitSet8::from_range(0, 0),
1280 dynamic_lanes: BitSet16::from_range(0, 4),
1281 };
1282 assert!(!vts.contains(I8));
1283 assert!(vts.contains(I32));
1284 assert!(vts.contains(I64));
1285 assert!(vts.contains(I32X4));
1286 assert!(vts.contains(I32X4XN));
1287 assert!(!vts.contains(F16));
1288 assert!(!vts.contains(F32));
1289 assert!(!vts.contains(F128));
1290 assert_eq!(vts.example().to_string(), "i32");
1291
1292 let vts = ValueTypeSet {
1293 lanes: BitSet16::from_range(0, 8),
1294 ints: BitSet8::from_range(0, 0),
1295 floats: BitSet8::from_range(5, 7),
1296 dynamic_lanes: BitSet16::from_range(0, 8),
1297 };
1298 assert_eq!(vts.example().to_string(), "f32");
1299
1300 let vts = ValueTypeSet {
1301 lanes: BitSet16::from_range(1, 8),
1302 ints: BitSet8::from_range(0, 0),
1303 floats: BitSet8::from_range(5, 7),
1304 dynamic_lanes: BitSet16::from_range(0, 8),
1305 };
1306 assert_eq!(vts.example().to_string(), "f32x2");
1307
1308 let vts = ValueTypeSet {
1309 lanes: BitSet16::from_range(2, 8),
1310 ints: BitSet8::from_range(3, 7),
1311 floats: BitSet8::from_range(0, 0),
1312 dynamic_lanes: BitSet16::from_range(0, 8),
1313 };
1314 assert_eq!(vts.example().to_string(), "i32x4");
1315
1316 let vts = ValueTypeSet {
1317 lanes: BitSet16::from_range(0, 9),
1319 ints: BitSet8::from_range(3, 7),
1320 floats: BitSet8::from_range(0, 0),
1321 dynamic_lanes: BitSet16::from_range(0, 8),
1322 };
1323 assert!(vts.contains(I32));
1324 assert!(vts.contains(I32X4));
1325 }
1326
1327 #[test]
1328 fn instruction_data_map() {
1329 struct TestMapper;
1330
1331 impl InstructionMapper for TestMapper {
1332 fn map_value(&mut self, value: Value) -> Value {
1333 Value::from_u32(value.as_u32() + 1)
1334 }
1335
1336 fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {
1337 ValueList::new()
1338 }
1339
1340 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1341 GlobalValue::from_u32(global_value.as_u32() + 1)
1342 }
1343
1344 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1345 JumpTable::from_u32(jump_table.as_u32() + 1)
1346 }
1347
1348 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1349 ExceptionTable::from_u32(exception_table.as_u32() + 1)
1350 }
1351
1352 fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {
1353 let block = Block::from_u32(42);
1354 let mut pool = ValueListPool::new();
1355 BlockCall::new(block, [], &mut pool)
1356 }
1357
1358 fn map_block(&mut self, block: Block) -> Block {
1359 Block::from_u32(block.as_u32() + 1)
1360 }
1361
1362 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1363 FuncRef::from_u32(func_ref.as_u32() + 1)
1364 }
1365
1366 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1367 SigRef::from_u32(sig_ref.as_u32() + 1)
1368 }
1369
1370 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1371 StackSlot::from_u32(stack_slot.as_u32() + 1)
1372 }
1373
1374 fn map_dynamic_stack_slot(
1375 &mut self,
1376 dynamic_stack_slot: ir::DynamicStackSlot,
1377 ) -> ir::DynamicStackSlot {
1378 DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)
1379 }
1380
1381 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1382 ir::Constant::from_u32(constant.as_u32() + 1)
1383 }
1384
1385 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1386 ir::Immediate::from_u32(immediate.as_u32() + 1)
1387 }
1388 }
1389
1390 let mut pool = ValueListPool::new();
1391 let map = |inst: InstructionData| inst.map(TestMapper);
1392
1393 assert_eq!(
1395 map(InstructionData::Binary {
1396 opcode: Opcode::Iadd,
1397 args: [Value::from_u32(10), Value::from_u32(20)]
1398 }),
1399 InstructionData::Binary {
1400 opcode: Opcode::Iadd,
1401 args: [Value::from_u32(11), Value::from_u32(21)]
1402 }
1403 );
1404
1405 let mut args = ValueList::new();
1407 args.push(Value::from_u32(42), &mut pool);
1408 let func_ref = FuncRef::from_u32(99);
1409 let inst = map(InstructionData::Call {
1410 opcode: Opcode::Call,
1411 args,
1412 func_ref,
1413 });
1414 let InstructionData::Call {
1415 opcode: Opcode::Call,
1416 args,
1417 func_ref,
1418 } = inst
1419 else {
1420 panic!()
1421 };
1422 assert!(args.is_empty());
1423 assert_eq!(func_ref, FuncRef::from_u32(100));
1424
1425 assert_eq!(
1427 map(InstructionData::UnaryGlobalValue {
1428 opcode: Opcode::GlobalValue,
1429 global_value: GlobalValue::from_u32(4),
1430 }),
1431 InstructionData::UnaryGlobalValue {
1432 opcode: Opcode::GlobalValue,
1433 global_value: GlobalValue::from_u32(5),
1434 }
1435 );
1436
1437 assert_eq!(
1439 map(InstructionData::BranchTable {
1440 opcode: Opcode::BrTable,
1441 arg: Value::from_u32(0),
1442 table: JumpTable::from_u32(1),
1443 }),
1444 InstructionData::BranchTable {
1445 opcode: Opcode::BrTable,
1446 arg: Value::from_u32(1),
1447 table: JumpTable::from_u32(2),
1448 }
1449 );
1450
1451 assert_eq!(
1453 map(InstructionData::TryCall {
1454 opcode: Opcode::TryCall,
1455 args,
1456 func_ref: FuncRef::from_u32(0),
1457 exception: ExceptionTable::from_u32(1),
1458 }),
1459 InstructionData::TryCall {
1460 opcode: Opcode::TryCall,
1461 args,
1462 func_ref: FuncRef::from_u32(1),
1463 exception: ExceptionTable::from_u32(2),
1464 }
1465 );
1466
1467 assert_eq!(
1469 map(InstructionData::Jump {
1470 opcode: Opcode::Jump,
1471 destination: BlockCall::new(Block::from_u32(99), [], &mut pool),
1472 }),
1473 map(InstructionData::Jump {
1474 opcode: Opcode::Jump,
1475 destination: BlockCall::new(Block::from_u32(42), [], &mut pool),
1476 })
1477 );
1478
1479 assert_eq!(
1481 map(InstructionData::ExceptionHandlerAddress {
1482 opcode: Opcode::GetExceptionHandlerAddress,
1483 block: Block::from_u32(1),
1484 imm: 0.into(),
1485 }),
1486 InstructionData::ExceptionHandlerAddress {
1487 opcode: Opcode::GetExceptionHandlerAddress,
1488 block: Block::from_u32(2),
1489 imm: 0.into(),
1490 },
1491 );
1492
1493 assert_eq!(
1495 map(InstructionData::CallIndirect {
1496 opcode: Opcode::CallIndirect,
1497 args,
1498 sig_ref: SigRef::from_u32(11)
1499 }),
1500 InstructionData::CallIndirect {
1501 opcode: Opcode::CallIndirect,
1502 args: ValueList::new(),
1503 sig_ref: SigRef::from_u32(12)
1504 }
1505 );
1506
1507 assert_eq!(
1509 map(InstructionData::StackLoad {
1510 opcode: Opcode::StackLoad,
1511 stack_slot: StackSlot::from_u32(0),
1512 offset: 0.into()
1513 }),
1514 InstructionData::StackLoad {
1515 opcode: Opcode::StackLoad,
1516 stack_slot: StackSlot::from_u32(1),
1517 offset: 0.into()
1518 },
1519 );
1520
1521 assert_eq!(
1523 map(InstructionData::DynamicStackLoad {
1524 opcode: Opcode::DynamicStackLoad,
1525 dynamic_stack_slot: DynamicStackSlot::from_u32(0),
1526 }),
1527 InstructionData::DynamicStackLoad {
1528 opcode: Opcode::DynamicStackLoad,
1529 dynamic_stack_slot: DynamicStackSlot::from_u32(1),
1530 },
1531 );
1532
1533 assert_eq!(
1535 map(InstructionData::UnaryConst {
1536 opcode: ir::Opcode::Vconst,
1537 constant_handle: ir::Constant::from_u32(2)
1538 }),
1539 InstructionData::UnaryConst {
1540 opcode: ir::Opcode::Vconst,
1541 constant_handle: ir::Constant::from_u32(3)
1542 },
1543 );
1544
1545 assert_eq!(
1547 map(InstructionData::Shuffle {
1548 opcode: ir::Opcode::Shuffle,
1549 args: [Value::from_u32(0), Value::from_u32(1)],
1550 imm: ir::Immediate::from_u32(41),
1551 }),
1552 InstructionData::Shuffle {
1553 opcode: ir::Opcode::Shuffle,
1554 args: [Value::from_u32(1), Value::from_u32(2)],
1555 imm: ir::Immediate::from_u32(42),
1556 },
1557 );
1558 }
1559}