1use crate::constant_hash::Table;
10use alloc::vec::Vec;
11use core::fmt::{self, Display, Formatter};
12use core::ops::{Deref, DerefMut};
13use core::str::FromStr;
14
15#[cfg(feature = "enable-serde")]
16use serde_derive::{Deserialize, Serialize};
17
18use crate::bitset::ScalarBitSet;
19use crate::entity;
20use crate::ir::{
21 self, Block, ExceptionTable, ExceptionTables, FuncRef, MemFlags, SigRef, StackSlot, Type,
22 Value,
23 condcodes::{FloatCC, IntCC},
24 trapcode::TrapCode,
25 types,
26};
27
28pub type ValueList = entity::EntityList<Value>;
32
33pub type ValueListPool = entity::ListPool<Value>;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
54pub struct BlockCall {
55 values: entity::EntityList<Value>,
59}
60
61impl BlockCall {
62 fn value_to_block(val: Value) -> Block {
65 Block::from_u32(val.as_u32())
66 }
67
68 fn block_to_value(block: Block) -> Value {
71 Value::from_u32(block.as_u32())
72 }
73
74 pub fn new(
76 block: Block,
77 args: impl IntoIterator<Item = BlockArg>,
78 pool: &mut ValueListPool,
79 ) -> Self {
80 let mut values = ValueList::default();
81 values.push(Self::block_to_value(block), pool);
82 values.extend(args.into_iter().map(|arg| arg.encode_as_value()), pool);
83 Self { values }
84 }
85
86 pub fn block(&self, pool: &ValueListPool) -> Block {
88 let val = self.values.first(pool).unwrap();
89 Self::value_to_block(val)
90 }
91
92 pub fn set_block(&mut self, block: Block, pool: &mut ValueListPool) {
94 *self.values.get_mut(0, pool).unwrap() = Self::block_to_value(block);
95 }
96
97 pub fn append_argument(&mut self, arg: impl Into<BlockArg>, pool: &mut ValueListPool) {
99 self.values.push(arg.into().encode_as_value(), pool);
100 }
101
102 pub fn len(&self, pool: &ValueListPool) -> usize {
104 self.values.len(pool) - 1
105 }
106
107 pub fn args<'a>(
109 &self,
110 pool: &'a ValueListPool,
111 ) -> impl ExactSizeIterator<Item = BlockArg> + DoubleEndedIterator<Item = BlockArg> + use<'a>
112 {
113 self.values.as_slice(pool)[1..]
114 .iter()
115 .map(|value| BlockArg::decode_from_value(*value))
116 }
117
118 pub fn update_args<F: FnMut(BlockArg) -> BlockArg>(
120 &mut self,
121 pool: &mut ValueListPool,
122 mut f: F,
123 ) {
124 for raw in self.values.as_mut_slice(pool)[1..].iter_mut() {
125 let new = f(BlockArg::decode_from_value(*raw));
126 *raw = new.encode_as_value();
127 }
128 }
129
130 pub fn remove(&mut self, ix: usize, pool: &mut ValueListPool) {
132 self.values.remove(1 + ix, pool)
133 }
134
135 pub fn clear(&mut self, pool: &mut ValueListPool) {
137 self.values.truncate(1, pool)
138 }
139
140 pub fn extend<I, T>(&mut self, elements: I, pool: &mut ValueListPool)
142 where
143 I: IntoIterator<Item = T>,
144 T: Into<BlockArg>,
145 {
146 self.values.extend(
147 elements
148 .into_iter()
149 .map(|elem| elem.into().encode_as_value()),
150 pool,
151 )
152 }
153
154 pub fn display<'a>(&self, pool: &'a ValueListPool) -> DisplayBlockCall<'a> {
156 DisplayBlockCall { block: *self, pool }
157 }
158
159 pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {
163 Self {
164 values: self.values.deep_clone(pool),
165 }
166 }
167}
168
169pub struct DisplayBlockCall<'a> {
171 block: BlockCall,
172 pool: &'a ValueListPool,
173}
174
175impl<'a> Display for DisplayBlockCall<'a> {
176 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
177 write!(f, "{}", self.block.block(&self.pool))?;
178 if self.block.len(self.pool) > 0 {
179 write!(f, "(")?;
180 for (ix, arg) in self.block.args(self.pool).enumerate() {
181 if ix > 0 {
182 write!(f, ", ")?;
183 }
184 write!(f, "{arg}")?;
185 }
186 write!(f, ")")?;
187 }
188 Ok(())
189 }
190}
191
192#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
199pub enum BlockArg {
200 Value(Value),
203
204 TryCallRet(u32),
209
210 TryCallExn(u32),
216}
217
218impl BlockArg {
219 fn encode_as_value(&self) -> Value {
223 let (tag, payload) = match *self {
224 BlockArg::Value(v) => (0, v.as_bits()),
225 BlockArg::TryCallRet(i) => (1, i),
226 BlockArg::TryCallExn(i) => (2, i),
227 };
228 assert!(payload < (1 << 30));
229 let raw = (tag << 30) | payload;
230 Value::from_bits(raw)
231 }
232
233 fn decode_from_value(v: Value) -> Self {
235 let raw = v.as_u32();
236 let tag = raw >> 30;
237 let payload = raw & ((1 << 30) - 1);
238 match tag {
239 0 => BlockArg::Value(Value::from_bits(payload)),
240 1 => BlockArg::TryCallRet(payload),
241 2 => BlockArg::TryCallExn(payload),
242 _ => unreachable!(),
243 }
244 }
245
246 pub fn as_value(&self) -> Option<Value> {
249 match *self {
250 BlockArg::Value(v) => Some(v),
251 _ => None,
252 }
253 }
254
255 pub fn map_value<F: FnMut(Value) -> Value>(&self, mut f: F) -> Self {
257 match *self {
258 BlockArg::Value(v) => BlockArg::Value(f(v)),
259 other => other,
260 }
261 }
262}
263
264impl Display for BlockArg {
265 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
266 match self {
267 BlockArg::Value(v) => write!(f, "{v}"),
268 BlockArg::TryCallRet(i) => write!(f, "ret{i}"),
269 BlockArg::TryCallExn(i) => write!(f, "exn{i}"),
270 }
271 }
272}
273
274impl From<Value> for BlockArg {
275 fn from(value: Value) -> BlockArg {
276 BlockArg::Value(value)
277 }
278}
279
280include!(concat!(env!("OUT_DIR"), "/opcodes.rs"));
296
297impl Display for Opcode {
298 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
299 write!(f, "{}", opcode_name(*self))
300 }
301}
302
303impl Opcode {
304 pub fn format(self) -> InstructionFormat {
306 OPCODE_FORMAT[self as usize - 1]
307 }
308
309 pub fn constraints(self) -> OpcodeConstraints {
312 OPCODE_CONSTRAINTS[self as usize - 1]
313 }
314
315 #[inline]
319 pub fn is_safepoint(self) -> bool {
320 self.is_call() && !self.is_return()
321 }
322}
323
324impl FromStr for Opcode {
329 type Err = &'static str;
330
331 fn from_str(s: &str) -> Result<Self, &'static str> {
333 use crate::constant_hash::{probe, simple_hash};
334
335 match probe::<&str, [Option<Self>]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) {
336 Err(_) => Err("Unknown opcode"),
337 Ok(i) => Ok(OPCODE_HASH_TABLE[i].unwrap()),
340 }
341 }
342}
343
344impl<'a> Table<&'a str> for [Option<Opcode>] {
345 fn len(&self) -> usize {
346 self.len()
347 }
348
349 fn key(&self, idx: usize) -> Option<&'a str> {
350 self[idx].map(opcode_name)
351 }
352}
353
354#[derive(Clone, Debug)]
357pub struct VariableArgs(Vec<Value>);
358
359impl VariableArgs {
360 pub fn new() -> Self {
362 Self(Vec::new())
363 }
364
365 pub fn push(&mut self, v: Value) {
367 self.0.push(v)
368 }
369
370 pub fn is_empty(&self) -> bool {
372 self.0.is_empty()
373 }
374
375 pub fn into_value_list(self, fixed: &[Value], pool: &mut ValueListPool) -> ValueList {
377 let mut vlist = ValueList::default();
378 vlist.extend(fixed.iter().cloned(), pool);
379 vlist.extend(self.0, pool);
380 vlist
381 }
382}
383
384impl Deref for VariableArgs {
386 type Target = [Value];
387
388 fn deref(&self) -> &[Value] {
389 &self.0
390 }
391}
392
393impl DerefMut for VariableArgs {
394 fn deref_mut(&mut self) -> &mut [Value] {
395 &mut self.0
396 }
397}
398
399impl Display for VariableArgs {
400 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
401 for (i, val) in self.0.iter().enumerate() {
402 if i == 0 {
403 write!(fmt, "{val}")?;
404 } else {
405 write!(fmt, ", {val}")?;
406 }
407 }
408 Ok(())
409 }
410}
411
412impl Default for VariableArgs {
413 fn default() -> Self {
414 Self::new()
415 }
416}
417
418impl InstructionData {
423 pub fn branch_destination<'a>(
427 &'a self,
428 jump_tables: &'a ir::JumpTables,
429 exception_tables: &'a ir::ExceptionTables,
430 ) -> &'a [BlockCall] {
431 match self {
432 Self::Jump { destination, .. } => core::slice::from_ref(destination),
433 Self::Brif { blocks, .. } => blocks.as_slice(),
434 Self::BranchTable { table, .. } => jump_tables.get(*table).unwrap().all_branches(),
435 Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
436 exception_tables.get(*exception).unwrap().all_branches()
437 }
438 _ => {
439 debug_assert!(!self.opcode().is_branch());
440 &[]
441 }
442 }
443 }
444
445 pub fn branch_destination_mut<'a>(
449 &'a mut self,
450 jump_tables: &'a mut ir::JumpTables,
451 exception_tables: &'a mut ir::ExceptionTables,
452 ) -> &'a mut [BlockCall] {
453 match self {
454 Self::Jump { destination, .. } => core::slice::from_mut(destination),
455 Self::Brif { blocks, .. } => blocks.as_mut_slice(),
456 Self::BranchTable { table, .. } => {
457 jump_tables.get_mut(*table).unwrap().all_branches_mut()
458 }
459 Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
460 exception_tables
461 .get_mut(*exception)
462 .unwrap()
463 .all_branches_mut()
464 }
465 _ => {
466 debug_assert!(!self.opcode().is_branch());
467 &mut []
468 }
469 }
470 }
471
472 pub fn map_values(
475 &mut self,
476 pool: &mut ValueListPool,
477 jump_tables: &mut ir::JumpTables,
478 exception_tables: &mut ir::ExceptionTables,
479 mut f: impl FnMut(Value) -> Value,
480 ) {
481 for arg in self.arguments_mut(pool) {
483 *arg = f(*arg);
484 }
485
486 for block in self.branch_destination_mut(jump_tables, exception_tables) {
488 block.update_args(pool, |arg| arg.map_value(|val| f(val)));
489 }
490
491 if let Some(et) = self.exception_table() {
493 for ctx in exception_tables[et].contexts_mut() {
494 *ctx = f(*ctx);
495 }
496 }
497 }
498
499 pub fn trap_code(&self) -> Option<TrapCode> {
502 match *self {
503 Self::CondTrap { code, .. }
504 | Self::IntAddTrap { code, .. }
505 | Self::Trap { code, .. } => Some(code),
506 _ => None,
507 }
508 }
509
510 pub fn cond_code(&self) -> Option<IntCC> {
513 match self {
514 &InstructionData::IntCompare { cond, .. } => Some(cond),
515 _ => None,
516 }
517 }
518
519 pub fn fp_cond_code(&self) -> Option<FloatCC> {
522 match self {
523 &InstructionData::FloatCompare { cond, .. } => Some(cond),
524 _ => None,
525 }
526 }
527
528 pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> {
531 match self {
532 Self::CondTrap { code, .. }
533 | Self::IntAddTrap { code, .. }
534 | Self::Trap { code, .. } => Some(code),
535 _ => None,
536 }
537 }
538
539 pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> {
541 match self {
542 &InstructionData::AtomicRmw { op, .. } => Some(op),
543 _ => None,
544 }
545 }
546
547 pub fn load_store_offset(&self) -> Option<i32> {
549 match self {
550 &InstructionData::Load { offset, .. }
551 | &InstructionData::StackAddr { offset, .. }
552 | &InstructionData::Store { offset, .. } => Some(offset.into()),
553 _ => None,
554 }
555 }
556
557 pub fn memflags(&self) -> Option<MemFlags> {
559 match self {
560 &InstructionData::Load { flags, .. }
561 | &InstructionData::LoadNoOffset { flags, .. }
562 | &InstructionData::Store { flags, .. }
563 | &InstructionData::StoreNoOffset { flags, .. }
564 | &InstructionData::AtomicCas { flags, .. }
565 | &InstructionData::AtomicRmw { flags, .. } => Some(flags),
566 _ => None,
567 }
568 }
569
570 pub fn memflags_mut(&mut self) -> Option<&mut MemFlags> {
572 match self {
573 InstructionData::Load { flags, .. }
574 | InstructionData::LoadNoOffset { flags, .. }
575 | InstructionData::Store { flags, .. }
576 | InstructionData::StoreNoOffset { flags, .. }
577 | InstructionData::AtomicCas { flags, .. }
578 | InstructionData::AtomicRmw { flags, .. } => Some(flags),
579 _ => None,
580 }
581 }
582
583 pub fn memflags_data(&self, dfg: &super::dfg::DataFlowGraph) -> Option<super::MemFlagsData> {
586 self.memflags().map(|f| dfg.mem_flags[f])
587 }
588
589 pub fn memflags_trap_code(&self, dfg: &super::dfg::DataFlowGraph) -> Option<TrapCode> {
594 self.memflags_data(dfg)?.trap_code()
595 }
596
597 pub fn alias_region(&self, dfg: &super::dfg::DataFlowGraph) -> Option<super::AliasRegion> {
599 let flags = self.memflags_data(dfg)?;
600 flags.alias_region()
601 }
602
603 pub fn stack_slot(&self) -> Option<StackSlot> {
605 match self {
606 &InstructionData::StackAddr { stack_slot, .. } => Some(stack_slot),
607 _ => None,
608 }
609 }
610
611 pub fn analyze_call<'a>(
615 &'a self,
616 pool: &'a ValueListPool,
617 exception_tables: &ExceptionTables,
618 ) -> CallInfo<'a> {
619 match *self {
620 Self::Call {
621 func_ref, ref args, ..
622 } => CallInfo::Direct(func_ref, args.as_slice(pool)),
623 Self::CallIndirect {
624 sig_ref, ref args, ..
625 } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
626 Self::TryCall {
627 func_ref,
628 ref args,
629 exception,
630 ..
631 } => {
632 let exdata = &exception_tables[exception];
633 CallInfo::DirectWithSig(func_ref, exdata.signature(), args.as_slice(pool))
634 }
635 Self::TryCallIndirect {
636 exception,
637 ref args,
638 ..
639 } => {
640 let exdata = &exception_tables[exception];
641 CallInfo::Indirect(exdata.signature(), &args.as_slice(pool)[1..])
642 }
643 Self::Ternary {
644 opcode: Opcode::StackSwitch,
645 ..
646 }
647 | Self::IntAddTrap {
648 opcode: Opcode::InterruptPoll,
649 ..
650 } => {
651 CallInfo::NotACall
654 }
655 _ => {
656 debug_assert!(!self.opcode().is_call());
657 CallInfo::NotACall
658 }
659 }
660 }
661
662 #[inline]
663 pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {
664 if ctrl_typevar.is_invalid() {
665 return;
666 }
667
668 let bit_width = ctrl_typevar.bits();
669
670 match self {
671 Self::UnaryImm { opcode: _, imm } => {
672 *imm = imm.mask_to_width(bit_width);
673 }
674 _ => {}
675 }
676 }
677
678 pub fn exception_table(&self) -> Option<ExceptionTable> {
680 match self {
681 Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
682 Some(*exception)
683 }
684 _ => None,
685 }
686 }
687}
688
689pub enum CallInfo<'a> {
691 NotACall,
693
694 Direct(FuncRef, &'a [Value]),
697
698 Indirect(SigRef, &'a [Value]),
700
701 DirectWithSig(FuncRef, SigRef, &'a [Value]),
705}
706
707#[derive(Clone, Copy)]
713pub struct OpcodeConstraints {
714 flags: u8,
733
734 typeset_offset: u8,
736
737 constraint_offset: u16,
741}
742
743impl OpcodeConstraints {
744 pub fn use_typevar_operand(self) -> bool {
748 (self.flags & 0x8) != 0
749 }
750
751 pub fn requires_typevar_operand(self) -> bool {
758 (self.flags & 0x10) != 0
759 }
760
761 pub fn num_fixed_results(self) -> usize {
764 (self.flags & 0x7) as usize
765 }
766
767 pub fn num_fixed_value_arguments(self) -> usize {
775 ((self.flags >> 5) & 0x7) as usize
776 }
777
778 fn typeset_offset(self) -> Option<usize> {
781 let offset = usize::from(self.typeset_offset);
782 if offset < TYPE_SETS.len() {
783 Some(offset)
784 } else {
785 None
786 }
787 }
788
789 fn constraint_offset(self) -> usize {
791 self.constraint_offset as usize
792 }
793
794 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
797 debug_assert!(n < self.num_fixed_results(), "Invalid result index");
798 match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {
799 ResolvedConstraint::Bound(t) => t,
800 ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"),
801 }
802 }
803
804 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
810 debug_assert!(
811 n < self.num_fixed_value_arguments(),
812 "Invalid value argument index"
813 );
814 let offset = self.constraint_offset() + self.num_fixed_results();
815 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
816 }
817
818 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
821 self.typeset_offset().map(|offset| TYPE_SETS[offset])
822 }
823
824 pub fn is_polymorphic(self) -> bool {
826 self.ctrl_typeset().is_some()
827 }
828}
829
830type BitSet8 = ScalarBitSet<u8>;
831type BitSet16 = ScalarBitSet<u16>;
832
833#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
835pub struct ValueTypeSet {
836 pub lanes: BitSet16,
838 pub ints: BitSet8,
840 pub floats: BitSet8,
842 pub dynamic_lanes: BitSet16,
844}
845
846impl ValueTypeSet {
847 fn is_base_type(self, scalar: Type) -> bool {
851 let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap();
852 if scalar.is_int() {
853 self.ints.contains(l2b)
854 } else if scalar.is_float() {
855 self.floats.contains(l2b)
856 } else {
857 false
858 }
859 }
860
861 pub fn contains(self, typ: Type) -> bool {
863 if typ.is_dynamic_vector() {
864 let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap();
865 self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())
866 } else {
867 let l2l = u8::try_from(typ.log2_lane_count()).unwrap();
868 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
869 }
870 }
871
872 pub fn example(self) -> Type {
876 let t = if self.ints.max().unwrap_or(0) > 5 {
877 types::I32
878 } else if self.floats.max().unwrap_or(0) > 5 {
879 types::F32
880 } else {
881 types::I8
882 };
883 t.by(1 << self.lanes.min().unwrap()).unwrap()
884 }
885}
886
887enum OperandConstraint {
889 Concrete(Type),
891
892 Free(u8),
895
896 Same,
898
899 LaneOf,
901
902 AsTruthy,
904
905 HalfWidth,
907
908 DoubleWidth,
910
911 SplitLanes,
913
914 MergeLanes,
916
917 DynamicToVector,
919
920 Narrower,
922
923 Wider,
925}
926
927impl OperandConstraint {
928 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
931 use self::OperandConstraint::*;
932 use self::ResolvedConstraint::Bound;
933 match *self {
934 Concrete(t) => Bound(t),
935 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
936 Same => Bound(ctrl_type),
937 LaneOf => Bound(ctrl_type.lane_of()),
938 AsTruthy => Bound(ctrl_type.as_truthy()),
939 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
940 DoubleWidth => Bound(
941 ctrl_type
942 .double_width()
943 .expect("invalid type for double_width"),
944 ),
945 SplitLanes => {
946 if ctrl_type.is_dynamic_vector() {
947 Bound(
948 ctrl_type
949 .dynamic_to_vector()
950 .expect("invalid type for dynamic_to_vector")
951 .split_lanes()
952 .expect("invalid type for split_lanes")
953 .vector_to_dynamic()
954 .expect("invalid dynamic type"),
955 )
956 } else {
957 Bound(
958 ctrl_type
959 .split_lanes()
960 .expect("invalid type for split_lanes"),
961 )
962 }
963 }
964 MergeLanes => {
965 if ctrl_type.is_dynamic_vector() {
966 Bound(
967 ctrl_type
968 .dynamic_to_vector()
969 .expect("invalid type for dynamic_to_vector")
970 .merge_lanes()
971 .expect("invalid type for merge_lanes")
972 .vector_to_dynamic()
973 .expect("invalid dynamic type"),
974 )
975 } else {
976 Bound(
977 ctrl_type
978 .merge_lanes()
979 .expect("invalid type for merge_lanes"),
980 )
981 }
982 }
983 DynamicToVector => Bound(
984 ctrl_type
985 .dynamic_to_vector()
986 .expect("invalid type for dynamic_to_vector"),
987 ),
988 Narrower => {
989 let ctrl_type_bits = ctrl_type.log2_lane_bits();
990 let mut tys = ValueTypeSet::default();
991
992 tys.lanes = ScalarBitSet::from_range(0, 1);
994
995 if ctrl_type.is_int() {
996 tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);
999 } else if ctrl_type.is_float() {
1000 tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8);
1003 } else {
1004 panic!(
1005 "The Narrower constraint only operates on floats or ints, got {ctrl_type:?}"
1006 );
1007 }
1008 ResolvedConstraint::Free(tys)
1009 }
1010 Wider => {
1011 let ctrl_type_bits = ctrl_type.log2_lane_bits();
1012 let mut tys = ValueTypeSet::default();
1013
1014 tys.lanes = ScalarBitSet::from_range(0, 1);
1016
1017 if ctrl_type.is_int() {
1018 let lower_bound = ctrl_type_bits as u8 + 1;
1019 if lower_bound < BitSet8::capacity() {
1025 tys.ints = BitSet8::from_range(lower_bound, 8);
1029 }
1030 } else if ctrl_type.is_float() {
1031 let lower_bound = ctrl_type_bits as u8 + 1;
1033 if lower_bound < BitSet8::capacity() {
1034 tys.floats = BitSet8::from_range(lower_bound, 8);
1035 }
1036 } else {
1037 panic!(
1038 "The Wider constraint only operates on floats or ints, got {ctrl_type:?}"
1039 );
1040 }
1041
1042 ResolvedConstraint::Free(tys)
1043 }
1044 }
1045 }
1046}
1047
1048#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1050pub enum ResolvedConstraint {
1051 Bound(Type),
1053 Free(ValueTypeSet),
1055}
1056
1057pub trait InstructionMapper {
1060 fn map_value(&mut self, value: Value) -> Value;
1062
1063 fn map_value_list(&mut self, value_list: ValueList) -> ValueList;
1065
1066 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue;
1068
1069 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable;
1071
1072 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable;
1074
1075 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall;
1077
1078 fn map_block(&mut self, block: Block) -> Block;
1080
1081 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;
1083
1084 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;
1086
1087 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;
1089
1090 fn map_dynamic_stack_slot(
1092 &mut self,
1093 dynamic_stack_slot: ir::DynamicStackSlot,
1094 ) -> ir::DynamicStackSlot;
1095
1096 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;
1098
1099 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;
1101
1102 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1108 flags
1109 }
1110}
1111
1112impl<'a, T> InstructionMapper for &'a mut T
1113where
1114 T: InstructionMapper,
1115{
1116 fn map_value(&mut self, value: Value) -> Value {
1117 (**self).map_value(value)
1118 }
1119
1120 fn map_value_list(&mut self, value_list: ValueList) -> ValueList {
1121 (**self).map_value_list(value_list)
1122 }
1123
1124 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1125 (**self).map_global_value(global_value)
1126 }
1127
1128 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1129 (**self).map_jump_table(jump_table)
1130 }
1131
1132 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1133 (**self).map_exception_table(exception_table)
1134 }
1135
1136 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {
1137 (**self).map_block_call(block_call)
1138 }
1139
1140 fn map_block(&mut self, block: Block) -> Block {
1141 (**self).map_block(block)
1142 }
1143
1144 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1145 (**self).map_func_ref(func_ref)
1146 }
1147
1148 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1149 (**self).map_sig_ref(sig_ref)
1150 }
1151
1152 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1153 (**self).map_stack_slot(stack_slot)
1154 }
1155
1156 fn map_dynamic_stack_slot(
1157 &mut self,
1158 dynamic_stack_slot: ir::DynamicStackSlot,
1159 ) -> ir::DynamicStackSlot {
1160 (**self).map_dynamic_stack_slot(dynamic_stack_slot)
1161 }
1162
1163 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1164 (**self).map_constant(constant)
1165 }
1166
1167 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1168 (**self).map_immediate(immediate)
1169 }
1170
1171 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1172 (**self).map_mem_flags(flags)
1173 }
1174}
1175
1176#[cfg(test)]
1177mod tests {
1178 use super::*;
1179 use alloc::string::ToString;
1180 use ir::{DynamicStackSlot, GlobalValue, JumpTable};
1181
1182 #[test]
1183 fn inst_data_is_copy() {
1184 fn is_copy<T: Copy>() {}
1185 is_copy::<InstructionData>();
1186 }
1187
1188 #[test]
1189 fn inst_data_size() {
1190 assert_eq!(core::mem::size_of::<InstructionData>(), 16);
1193 }
1194
1195 #[test]
1196 fn opcodes() {
1197 use core::mem;
1198
1199 let x = Opcode::Iadd;
1200 let mut y = Opcode::Isub;
1201
1202 assert!(x != y);
1203 y = Opcode::Iadd;
1204 assert_eq!(x, y);
1205 assert_eq!(x.format(), InstructionFormat::Binary);
1206
1207 assert_eq!(format!("{:?}", Opcode::StackAddr), "StackAddr");
1208 assert_eq!(Opcode::StackAddr.to_string(), "stack_addr");
1209
1210 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
1212 assert_eq!("stack_addr".parse::<Opcode>(), Ok(Opcode::StackAddr));
1213 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
1214 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
1215 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
1216
1217 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
1222 }
1223
1224 #[test]
1225 fn instruction_data() {
1226 use core::mem;
1227 assert_eq!(mem::size_of::<InstructionData>(), 16);
1232 }
1233
1234 #[test]
1235 fn constraints() {
1236 let a = Opcode::Iadd.constraints();
1237 assert!(a.use_typevar_operand());
1238 assert!(!a.requires_typevar_operand());
1239 assert_eq!(a.num_fixed_results(), 1);
1240 assert_eq!(a.num_fixed_value_arguments(), 2);
1241 assert_eq!(a.result_type(0, types::I32), types::I32);
1242 assert_eq!(a.result_type(0, types::I8), types::I8);
1243 assert_eq!(
1244 a.value_argument_constraint(0, types::I32),
1245 ResolvedConstraint::Bound(types::I32)
1246 );
1247 assert_eq!(
1248 a.value_argument_constraint(1, types::I32),
1249 ResolvedConstraint::Bound(types::I32)
1250 );
1251
1252 let b = Opcode::Bitcast.constraints();
1253 assert!(!b.use_typevar_operand());
1254 assert!(!b.requires_typevar_operand());
1255 assert_eq!(b.num_fixed_results(), 1);
1256 assert_eq!(b.num_fixed_value_arguments(), 1);
1257 assert_eq!(b.result_type(0, types::I32), types::I32);
1258 assert_eq!(b.result_type(0, types::I8), types::I8);
1259 match b.value_argument_constraint(0, types::I32) {
1260 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
1261 _ => panic!("Unexpected constraint from value_argument_constraint"),
1262 }
1263
1264 let c = Opcode::Call.constraints();
1265 assert_eq!(c.num_fixed_results(), 0);
1266 assert_eq!(c.num_fixed_value_arguments(), 0);
1267
1268 let i = Opcode::CallIndirect.constraints();
1269 assert_eq!(i.num_fixed_results(), 0);
1270 assert_eq!(i.num_fixed_value_arguments(), 1);
1271
1272 let cmp = Opcode::Icmp.constraints();
1273 assert!(cmp.use_typevar_operand());
1274 assert!(cmp.requires_typevar_operand());
1275 assert_eq!(cmp.num_fixed_results(), 1);
1276 assert_eq!(cmp.num_fixed_value_arguments(), 2);
1277 assert_eq!(cmp.result_type(0, types::I64), types::I8);
1278 }
1279
1280 #[test]
1281 fn value_set() {
1282 use crate::ir::types::*;
1283
1284 let vts = ValueTypeSet {
1285 lanes: BitSet16::from_range(0, 8),
1286 ints: BitSet8::from_range(4, 7),
1287 floats: BitSet8::from_range(0, 0),
1288 dynamic_lanes: BitSet16::from_range(0, 4),
1289 };
1290 assert!(!vts.contains(I8));
1291 assert!(vts.contains(I32));
1292 assert!(vts.contains(I64));
1293 assert!(vts.contains(I32X4));
1294 assert!(vts.contains(I32X4XN));
1295 assert!(!vts.contains(F16));
1296 assert!(!vts.contains(F32));
1297 assert!(!vts.contains(F128));
1298 assert_eq!(vts.example().to_string(), "i32");
1299
1300 let vts = ValueTypeSet {
1301 lanes: BitSet16::from_range(0, 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(), "f32");
1307
1308 let vts = ValueTypeSet {
1309 lanes: BitSet16::from_range(1, 8),
1310 ints: BitSet8::from_range(0, 0),
1311 floats: BitSet8::from_range(5, 7),
1312 dynamic_lanes: BitSet16::from_range(0, 8),
1313 };
1314 assert_eq!(vts.example().to_string(), "f32x2");
1315
1316 let vts = ValueTypeSet {
1317 lanes: BitSet16::from_range(2, 8),
1318 ints: BitSet8::from_range(3, 7),
1319 floats: BitSet8::from_range(0, 0),
1320 dynamic_lanes: BitSet16::from_range(0, 8),
1321 };
1322 assert_eq!(vts.example().to_string(), "i32x4");
1323
1324 let vts = ValueTypeSet {
1325 lanes: BitSet16::from_range(0, 9),
1327 ints: BitSet8::from_range(3, 7),
1328 floats: BitSet8::from_range(0, 0),
1329 dynamic_lanes: BitSet16::from_range(0, 8),
1330 };
1331 assert!(vts.contains(I32));
1332 assert!(vts.contains(I32X4));
1333 }
1334
1335 #[test]
1336 fn instruction_data_map() {
1337 struct TestMapper;
1338
1339 impl InstructionMapper for TestMapper {
1340 fn map_value(&mut self, value: Value) -> Value {
1341 Value::from_u32(value.as_u32() + 1)
1342 }
1343
1344 fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {
1345 ValueList::new()
1346 }
1347
1348 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1349 GlobalValue::from_u32(global_value.as_u32() + 1)
1350 }
1351
1352 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1353 JumpTable::from_u32(jump_table.as_u32() + 1)
1354 }
1355
1356 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1357 ExceptionTable::from_u32(exception_table.as_u32() + 1)
1358 }
1359
1360 fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {
1361 let block = Block::from_u32(42);
1362 let mut pool = ValueListPool::new();
1363 BlockCall::new(block, [], &mut pool)
1364 }
1365
1366 fn map_block(&mut self, block: Block) -> Block {
1367 Block::from_u32(block.as_u32() + 1)
1368 }
1369
1370 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1371 FuncRef::from_u32(func_ref.as_u32() + 1)
1372 }
1373
1374 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1375 SigRef::from_u32(sig_ref.as_u32() + 1)
1376 }
1377
1378 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1379 StackSlot::from_u32(stack_slot.as_u32() + 1)
1380 }
1381
1382 fn map_dynamic_stack_slot(
1383 &mut self,
1384 dynamic_stack_slot: ir::DynamicStackSlot,
1385 ) -> ir::DynamicStackSlot {
1386 DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)
1387 }
1388
1389 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1390 ir::Constant::from_u32(constant.as_u32() + 1)
1391 }
1392
1393 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1394 ir::Immediate::from_u32(immediate.as_u32() + 1)
1395 }
1396 }
1397
1398 let mut pool = ValueListPool::new();
1399 let map = |inst: InstructionData| inst.map(TestMapper);
1400
1401 assert_eq!(
1403 map(InstructionData::Binary {
1404 opcode: Opcode::Iadd,
1405 args: [Value::from_u32(10), Value::from_u32(20)]
1406 }),
1407 InstructionData::Binary {
1408 opcode: Opcode::Iadd,
1409 args: [Value::from_u32(11), Value::from_u32(21)]
1410 }
1411 );
1412
1413 let mut args = ValueList::new();
1415 args.push(Value::from_u32(42), &mut pool);
1416 let func_ref = FuncRef::from_u32(99);
1417 let inst = map(InstructionData::Call {
1418 opcode: Opcode::Call,
1419 args,
1420 func_ref,
1421 });
1422 let InstructionData::Call {
1423 opcode: Opcode::Call,
1424 args,
1425 func_ref,
1426 } = inst
1427 else {
1428 panic!()
1429 };
1430 assert!(args.is_empty());
1431 assert_eq!(func_ref, FuncRef::from_u32(100));
1432
1433 assert_eq!(
1435 map(InstructionData::UnaryGlobalValue {
1436 opcode: Opcode::SymbolValue,
1437 global_value: GlobalValue::from_u32(4),
1438 }),
1439 InstructionData::UnaryGlobalValue {
1440 opcode: Opcode::SymbolValue,
1441 global_value: GlobalValue::from_u32(5),
1442 }
1443 );
1444
1445 assert_eq!(
1447 map(InstructionData::BranchTable {
1448 opcode: Opcode::BrTable,
1449 arg: Value::from_u32(0),
1450 table: JumpTable::from_u32(1),
1451 }),
1452 InstructionData::BranchTable {
1453 opcode: Opcode::BrTable,
1454 arg: Value::from_u32(1),
1455 table: JumpTable::from_u32(2),
1456 }
1457 );
1458
1459 assert_eq!(
1461 map(InstructionData::TryCall {
1462 opcode: Opcode::TryCall,
1463 args,
1464 func_ref: FuncRef::from_u32(0),
1465 exception: ExceptionTable::from_u32(1),
1466 }),
1467 InstructionData::TryCall {
1468 opcode: Opcode::TryCall,
1469 args,
1470 func_ref: FuncRef::from_u32(1),
1471 exception: ExceptionTable::from_u32(2),
1472 }
1473 );
1474
1475 assert_eq!(
1477 map(InstructionData::Jump {
1478 opcode: Opcode::Jump,
1479 destination: BlockCall::new(Block::from_u32(99), [], &mut pool),
1480 }),
1481 map(InstructionData::Jump {
1482 opcode: Opcode::Jump,
1483 destination: BlockCall::new(Block::from_u32(42), [], &mut pool),
1484 })
1485 );
1486
1487 assert_eq!(
1489 map(InstructionData::ExceptionHandlerAddress {
1490 opcode: Opcode::GetExceptionHandlerAddress,
1491 block: Block::from_u32(1),
1492 imm: 0.into(),
1493 }),
1494 InstructionData::ExceptionHandlerAddress {
1495 opcode: Opcode::GetExceptionHandlerAddress,
1496 block: Block::from_u32(2),
1497 imm: 0.into(),
1498 },
1499 );
1500
1501 assert_eq!(
1503 map(InstructionData::CallIndirect {
1504 opcode: Opcode::CallIndirect,
1505 args,
1506 sig_ref: SigRef::from_u32(11)
1507 }),
1508 InstructionData::CallIndirect {
1509 opcode: Opcode::CallIndirect,
1510 args: ValueList::new(),
1511 sig_ref: SigRef::from_u32(12)
1512 }
1513 );
1514
1515 assert_eq!(
1517 map(InstructionData::StackAddr {
1518 opcode: Opcode::StackAddr,
1519 stack_slot: StackSlot::from_u32(0),
1520 offset: 0.into()
1521 }),
1522 InstructionData::StackAddr {
1523 opcode: Opcode::StackAddr,
1524 stack_slot: StackSlot::from_u32(1),
1525 offset: 0.into()
1526 },
1527 );
1528
1529 assert_eq!(
1531 map(InstructionData::DynamicStackAddr {
1532 opcode: Opcode::DynamicStackAddr,
1533 dynamic_stack_slot: DynamicStackSlot::from_u32(0),
1534 }),
1535 InstructionData::DynamicStackAddr {
1536 opcode: Opcode::DynamicStackAddr,
1537 dynamic_stack_slot: DynamicStackSlot::from_u32(1),
1538 },
1539 );
1540
1541 assert_eq!(
1543 map(InstructionData::UnaryConst {
1544 opcode: ir::Opcode::Vconst,
1545 constant_handle: ir::Constant::from_u32(2)
1546 }),
1547 InstructionData::UnaryConst {
1548 opcode: ir::Opcode::Vconst,
1549 constant_handle: ir::Constant::from_u32(3)
1550 },
1551 );
1552
1553 assert_eq!(
1555 map(InstructionData::Shuffle {
1556 opcode: ir::Opcode::Shuffle,
1557 args: [Value::from_u32(0), Value::from_u32(1)],
1558 imm: ir::Immediate::from_u32(41),
1559 }),
1560 InstructionData::Shuffle {
1561 opcode: ir::Opcode::Shuffle,
1562 args: [Value::from_u32(1), Value::from_u32(2)],
1563 imm: ir::Immediate::from_u32(42),
1564 },
1565 );
1566 }
1567}