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 CallInfo::NotACall
650 }
651 _ => {
652 debug_assert!(!self.opcode().is_call());
653 CallInfo::NotACall
654 }
655 }
656 }
657
658 #[inline]
659 pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) {
660 if ctrl_typevar.is_invalid() {
661 return;
662 }
663
664 let bit_width = ctrl_typevar.bits();
665
666 match self {
667 Self::UnaryImm { opcode: _, imm } => {
668 *imm = imm.mask_to_width(bit_width);
669 }
670 _ => {}
671 }
672 }
673
674 pub fn exception_table(&self) -> Option<ExceptionTable> {
676 match self {
677 Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => {
678 Some(*exception)
679 }
680 _ => None,
681 }
682 }
683}
684
685pub enum CallInfo<'a> {
687 NotACall,
689
690 Direct(FuncRef, &'a [Value]),
693
694 Indirect(SigRef, &'a [Value]),
696
697 DirectWithSig(FuncRef, SigRef, &'a [Value]),
701}
702
703#[derive(Clone, Copy)]
709pub struct OpcodeConstraints {
710 flags: u8,
729
730 typeset_offset: u8,
732
733 constraint_offset: u16,
737}
738
739impl OpcodeConstraints {
740 pub fn use_typevar_operand(self) -> bool {
744 (self.flags & 0x8) != 0
745 }
746
747 pub fn requires_typevar_operand(self) -> bool {
754 (self.flags & 0x10) != 0
755 }
756
757 pub fn num_fixed_results(self) -> usize {
760 (self.flags & 0x7) as usize
761 }
762
763 pub fn num_fixed_value_arguments(self) -> usize {
771 ((self.flags >> 5) & 0x7) as usize
772 }
773
774 fn typeset_offset(self) -> Option<usize> {
777 let offset = usize::from(self.typeset_offset);
778 if offset < TYPE_SETS.len() {
779 Some(offset)
780 } else {
781 None
782 }
783 }
784
785 fn constraint_offset(self) -> usize {
787 self.constraint_offset as usize
788 }
789
790 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
793 debug_assert!(n < self.num_fixed_results(), "Invalid result index");
794 match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) {
795 ResolvedConstraint::Bound(t) => t,
796 ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"),
797 }
798 }
799
800 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint {
806 debug_assert!(
807 n < self.num_fixed_value_arguments(),
808 "Invalid value argument index"
809 );
810 let offset = self.constraint_offset() + self.num_fixed_results();
811 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type)
812 }
813
814 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
817 self.typeset_offset().map(|offset| TYPE_SETS[offset])
818 }
819
820 pub fn is_polymorphic(self) -> bool {
822 self.ctrl_typeset().is_some()
823 }
824}
825
826type BitSet8 = ScalarBitSet<u8>;
827type BitSet16 = ScalarBitSet<u16>;
828
829#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
831pub struct ValueTypeSet {
832 pub lanes: BitSet16,
834 pub ints: BitSet8,
836 pub floats: BitSet8,
838 pub dynamic_lanes: BitSet16,
840}
841
842impl ValueTypeSet {
843 fn is_base_type(self, scalar: Type) -> bool {
847 let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap();
848 if scalar.is_int() {
849 self.ints.contains(l2b)
850 } else if scalar.is_float() {
851 self.floats.contains(l2b)
852 } else {
853 false
854 }
855 }
856
857 pub fn contains(self, typ: Type) -> bool {
859 if typ.is_dynamic_vector() {
860 let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap();
861 self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type())
862 } else {
863 let l2l = u8::try_from(typ.log2_lane_count()).unwrap();
864 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type())
865 }
866 }
867
868 pub fn example(self) -> Type {
872 let t = if self.ints.max().unwrap_or(0) > 5 {
873 types::I32
874 } else if self.floats.max().unwrap_or(0) > 5 {
875 types::F32
876 } else {
877 types::I8
878 };
879 t.by(1 << self.lanes.min().unwrap()).unwrap()
880 }
881}
882
883enum OperandConstraint {
885 Concrete(Type),
887
888 Free(u8),
891
892 Same,
894
895 LaneOf,
897
898 AsTruthy,
900
901 HalfWidth,
903
904 DoubleWidth,
906
907 SplitLanes,
909
910 MergeLanes,
912
913 DynamicToVector,
915
916 Narrower,
918
919 Wider,
921}
922
923impl OperandConstraint {
924 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint {
927 use self::OperandConstraint::*;
928 use self::ResolvedConstraint::Bound;
929 match *self {
930 Concrete(t) => Bound(t),
931 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]),
932 Same => Bound(ctrl_type),
933 LaneOf => Bound(ctrl_type.lane_of()),
934 AsTruthy => Bound(ctrl_type.as_truthy()),
935 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
936 DoubleWidth => Bound(
937 ctrl_type
938 .double_width()
939 .expect("invalid type for double_width"),
940 ),
941 SplitLanes => {
942 if ctrl_type.is_dynamic_vector() {
943 Bound(
944 ctrl_type
945 .dynamic_to_vector()
946 .expect("invalid type for dynamic_to_vector")
947 .split_lanes()
948 .expect("invalid type for split_lanes")
949 .vector_to_dynamic()
950 .expect("invalid dynamic type"),
951 )
952 } else {
953 Bound(
954 ctrl_type
955 .split_lanes()
956 .expect("invalid type for split_lanes"),
957 )
958 }
959 }
960 MergeLanes => {
961 if ctrl_type.is_dynamic_vector() {
962 Bound(
963 ctrl_type
964 .dynamic_to_vector()
965 .expect("invalid type for dynamic_to_vector")
966 .merge_lanes()
967 .expect("invalid type for merge_lanes")
968 .vector_to_dynamic()
969 .expect("invalid dynamic type"),
970 )
971 } else {
972 Bound(
973 ctrl_type
974 .merge_lanes()
975 .expect("invalid type for merge_lanes"),
976 )
977 }
978 }
979 DynamicToVector => Bound(
980 ctrl_type
981 .dynamic_to_vector()
982 .expect("invalid type for dynamic_to_vector"),
983 ),
984 Narrower => {
985 let ctrl_type_bits = ctrl_type.log2_lane_bits();
986 let mut tys = ValueTypeSet::default();
987
988 tys.lanes = ScalarBitSet::from_range(0, 1);
990
991 if ctrl_type.is_int() {
992 tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8);
995 } else if ctrl_type.is_float() {
996 tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8);
999 } else {
1000 panic!(
1001 "The Narrower constraint only operates on floats or ints, got {ctrl_type:?}"
1002 );
1003 }
1004 ResolvedConstraint::Free(tys)
1005 }
1006 Wider => {
1007 let ctrl_type_bits = ctrl_type.log2_lane_bits();
1008 let mut tys = ValueTypeSet::default();
1009
1010 tys.lanes = ScalarBitSet::from_range(0, 1);
1012
1013 if ctrl_type.is_int() {
1014 let lower_bound = ctrl_type_bits as u8 + 1;
1015 if lower_bound < BitSet8::capacity() {
1021 tys.ints = BitSet8::from_range(lower_bound, 8);
1025 }
1026 } else if ctrl_type.is_float() {
1027 let lower_bound = ctrl_type_bits as u8 + 1;
1029 if lower_bound < BitSet8::capacity() {
1030 tys.floats = BitSet8::from_range(lower_bound, 8);
1031 }
1032 } else {
1033 panic!(
1034 "The Wider constraint only operates on floats or ints, got {ctrl_type:?}"
1035 );
1036 }
1037
1038 ResolvedConstraint::Free(tys)
1039 }
1040 }
1041 }
1042}
1043
1044#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1046pub enum ResolvedConstraint {
1047 Bound(Type),
1049 Free(ValueTypeSet),
1051}
1052
1053pub trait InstructionMapper {
1056 fn map_value(&mut self, value: Value) -> Value;
1058
1059 fn map_value_list(&mut self, value_list: ValueList) -> ValueList;
1061
1062 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue;
1064
1065 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable;
1067
1068 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable;
1070
1071 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall;
1073
1074 fn map_block(&mut self, block: Block) -> Block;
1076
1077 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef;
1079
1080 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef;
1082
1083 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot;
1085
1086 fn map_dynamic_stack_slot(
1088 &mut self,
1089 dynamic_stack_slot: ir::DynamicStackSlot,
1090 ) -> ir::DynamicStackSlot;
1091
1092 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant;
1094
1095 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate;
1097
1098 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1104 flags
1105 }
1106}
1107
1108impl<'a, T> InstructionMapper for &'a mut T
1109where
1110 T: InstructionMapper,
1111{
1112 fn map_value(&mut self, value: Value) -> Value {
1113 (**self).map_value(value)
1114 }
1115
1116 fn map_value_list(&mut self, value_list: ValueList) -> ValueList {
1117 (**self).map_value_list(value_list)
1118 }
1119
1120 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1121 (**self).map_global_value(global_value)
1122 }
1123
1124 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1125 (**self).map_jump_table(jump_table)
1126 }
1127
1128 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1129 (**self).map_exception_table(exception_table)
1130 }
1131
1132 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall {
1133 (**self).map_block_call(block_call)
1134 }
1135
1136 fn map_block(&mut self, block: Block) -> Block {
1137 (**self).map_block(block)
1138 }
1139
1140 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1141 (**self).map_func_ref(func_ref)
1142 }
1143
1144 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1145 (**self).map_sig_ref(sig_ref)
1146 }
1147
1148 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1149 (**self).map_stack_slot(stack_slot)
1150 }
1151
1152 fn map_dynamic_stack_slot(
1153 &mut self,
1154 dynamic_stack_slot: ir::DynamicStackSlot,
1155 ) -> ir::DynamicStackSlot {
1156 (**self).map_dynamic_stack_slot(dynamic_stack_slot)
1157 }
1158
1159 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1160 (**self).map_constant(constant)
1161 }
1162
1163 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1164 (**self).map_immediate(immediate)
1165 }
1166
1167 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1168 (**self).map_mem_flags(flags)
1169 }
1170}
1171
1172#[cfg(test)]
1173mod tests {
1174 use super::*;
1175 use alloc::string::ToString;
1176 use ir::{DynamicStackSlot, GlobalValue, JumpTable};
1177
1178 #[test]
1179 fn inst_data_is_copy() {
1180 fn is_copy<T: Copy>() {}
1181 is_copy::<InstructionData>();
1182 }
1183
1184 #[test]
1185 fn inst_data_size() {
1186 assert_eq!(core::mem::size_of::<InstructionData>(), 16);
1189 }
1190
1191 #[test]
1192 fn opcodes() {
1193 use core::mem;
1194
1195 let x = Opcode::Iadd;
1196 let mut y = Opcode::Isub;
1197
1198 assert!(x != y);
1199 y = Opcode::Iadd;
1200 assert_eq!(x, y);
1201 assert_eq!(x.format(), InstructionFormat::Binary);
1202
1203 assert_eq!(format!("{:?}", Opcode::StackAddr), "StackAddr");
1204 assert_eq!(Opcode::StackAddr.to_string(), "stack_addr");
1205
1206 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
1208 assert_eq!("stack_addr".parse::<Opcode>(), Ok(Opcode::StackAddr));
1209 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
1210 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
1211 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
1212
1213 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>());
1218 }
1219
1220 #[test]
1221 fn instruction_data() {
1222 use core::mem;
1223 assert_eq!(mem::size_of::<InstructionData>(), 16);
1228 }
1229
1230 #[test]
1231 fn constraints() {
1232 let a = Opcode::Iadd.constraints();
1233 assert!(a.use_typevar_operand());
1234 assert!(!a.requires_typevar_operand());
1235 assert_eq!(a.num_fixed_results(), 1);
1236 assert_eq!(a.num_fixed_value_arguments(), 2);
1237 assert_eq!(a.result_type(0, types::I32), types::I32);
1238 assert_eq!(a.result_type(0, types::I8), types::I8);
1239 assert_eq!(
1240 a.value_argument_constraint(0, types::I32),
1241 ResolvedConstraint::Bound(types::I32)
1242 );
1243 assert_eq!(
1244 a.value_argument_constraint(1, types::I32),
1245 ResolvedConstraint::Bound(types::I32)
1246 );
1247
1248 let b = Opcode::Bitcast.constraints();
1249 assert!(!b.use_typevar_operand());
1250 assert!(!b.requires_typevar_operand());
1251 assert_eq!(b.num_fixed_results(), 1);
1252 assert_eq!(b.num_fixed_value_arguments(), 1);
1253 assert_eq!(b.result_type(0, types::I32), types::I32);
1254 assert_eq!(b.result_type(0, types::I8), types::I8);
1255 match b.value_argument_constraint(0, types::I32) {
1256 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)),
1257 _ => panic!("Unexpected constraint from value_argument_constraint"),
1258 }
1259
1260 let c = Opcode::Call.constraints();
1261 assert_eq!(c.num_fixed_results(), 0);
1262 assert_eq!(c.num_fixed_value_arguments(), 0);
1263
1264 let i = Opcode::CallIndirect.constraints();
1265 assert_eq!(i.num_fixed_results(), 0);
1266 assert_eq!(i.num_fixed_value_arguments(), 1);
1267
1268 let cmp = Opcode::Icmp.constraints();
1269 assert!(cmp.use_typevar_operand());
1270 assert!(cmp.requires_typevar_operand());
1271 assert_eq!(cmp.num_fixed_results(), 1);
1272 assert_eq!(cmp.num_fixed_value_arguments(), 2);
1273 assert_eq!(cmp.result_type(0, types::I64), types::I8);
1274 }
1275
1276 #[test]
1277 fn value_set() {
1278 use crate::ir::types::*;
1279
1280 let vts = ValueTypeSet {
1281 lanes: BitSet16::from_range(0, 8),
1282 ints: BitSet8::from_range(4, 7),
1283 floats: BitSet8::from_range(0, 0),
1284 dynamic_lanes: BitSet16::from_range(0, 4),
1285 };
1286 assert!(!vts.contains(I8));
1287 assert!(vts.contains(I32));
1288 assert!(vts.contains(I64));
1289 assert!(vts.contains(I32X4));
1290 assert!(vts.contains(I32X4XN));
1291 assert!(!vts.contains(F16));
1292 assert!(!vts.contains(F32));
1293 assert!(!vts.contains(F128));
1294 assert_eq!(vts.example().to_string(), "i32");
1295
1296 let vts = ValueTypeSet {
1297 lanes: BitSet16::from_range(0, 8),
1298 ints: BitSet8::from_range(0, 0),
1299 floats: BitSet8::from_range(5, 7),
1300 dynamic_lanes: BitSet16::from_range(0, 8),
1301 };
1302 assert_eq!(vts.example().to_string(), "f32");
1303
1304 let vts = ValueTypeSet {
1305 lanes: BitSet16::from_range(1, 8),
1306 ints: BitSet8::from_range(0, 0),
1307 floats: BitSet8::from_range(5, 7),
1308 dynamic_lanes: BitSet16::from_range(0, 8),
1309 };
1310 assert_eq!(vts.example().to_string(), "f32x2");
1311
1312 let vts = ValueTypeSet {
1313 lanes: BitSet16::from_range(2, 8),
1314 ints: BitSet8::from_range(3, 7),
1315 floats: BitSet8::from_range(0, 0),
1316 dynamic_lanes: BitSet16::from_range(0, 8),
1317 };
1318 assert_eq!(vts.example().to_string(), "i32x4");
1319
1320 let vts = ValueTypeSet {
1321 lanes: BitSet16::from_range(0, 9),
1323 ints: BitSet8::from_range(3, 7),
1324 floats: BitSet8::from_range(0, 0),
1325 dynamic_lanes: BitSet16::from_range(0, 8),
1326 };
1327 assert!(vts.contains(I32));
1328 assert!(vts.contains(I32X4));
1329 }
1330
1331 #[test]
1332 fn instruction_data_map() {
1333 struct TestMapper;
1334
1335 impl InstructionMapper for TestMapper {
1336 fn map_value(&mut self, value: Value) -> Value {
1337 Value::from_u32(value.as_u32() + 1)
1338 }
1339
1340 fn map_value_list(&mut self, _value_list: ValueList) -> ValueList {
1341 ValueList::new()
1342 }
1343
1344 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
1345 GlobalValue::from_u32(global_value.as_u32() + 1)
1346 }
1347
1348 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
1349 JumpTable::from_u32(jump_table.as_u32() + 1)
1350 }
1351
1352 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable {
1353 ExceptionTable::from_u32(exception_table.as_u32() + 1)
1354 }
1355
1356 fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall {
1357 let block = Block::from_u32(42);
1358 let mut pool = ValueListPool::new();
1359 BlockCall::new(block, [], &mut pool)
1360 }
1361
1362 fn map_block(&mut self, block: Block) -> Block {
1363 Block::from_u32(block.as_u32() + 1)
1364 }
1365
1366 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef {
1367 FuncRef::from_u32(func_ref.as_u32() + 1)
1368 }
1369
1370 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef {
1371 SigRef::from_u32(sig_ref.as_u32() + 1)
1372 }
1373
1374 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot {
1375 StackSlot::from_u32(stack_slot.as_u32() + 1)
1376 }
1377
1378 fn map_dynamic_stack_slot(
1379 &mut self,
1380 dynamic_stack_slot: ir::DynamicStackSlot,
1381 ) -> ir::DynamicStackSlot {
1382 DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1)
1383 }
1384
1385 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1386 ir::Constant::from_u32(constant.as_u32() + 1)
1387 }
1388
1389 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1390 ir::Immediate::from_u32(immediate.as_u32() + 1)
1391 }
1392 }
1393
1394 let mut pool = ValueListPool::new();
1395 let map = |inst: InstructionData| inst.map(TestMapper);
1396
1397 assert_eq!(
1399 map(InstructionData::Binary {
1400 opcode: Opcode::Iadd,
1401 args: [Value::from_u32(10), Value::from_u32(20)]
1402 }),
1403 InstructionData::Binary {
1404 opcode: Opcode::Iadd,
1405 args: [Value::from_u32(11), Value::from_u32(21)]
1406 }
1407 );
1408
1409 let mut args = ValueList::new();
1411 args.push(Value::from_u32(42), &mut pool);
1412 let func_ref = FuncRef::from_u32(99);
1413 let inst = map(InstructionData::Call {
1414 opcode: Opcode::Call,
1415 args,
1416 func_ref,
1417 });
1418 let InstructionData::Call {
1419 opcode: Opcode::Call,
1420 args,
1421 func_ref,
1422 } = inst
1423 else {
1424 panic!()
1425 };
1426 assert!(args.is_empty());
1427 assert_eq!(func_ref, FuncRef::from_u32(100));
1428
1429 assert_eq!(
1431 map(InstructionData::UnaryGlobalValue {
1432 opcode: Opcode::SymbolValue,
1433 global_value: GlobalValue::from_u32(4),
1434 }),
1435 InstructionData::UnaryGlobalValue {
1436 opcode: Opcode::SymbolValue,
1437 global_value: GlobalValue::from_u32(5),
1438 }
1439 );
1440
1441 assert_eq!(
1443 map(InstructionData::BranchTable {
1444 opcode: Opcode::BrTable,
1445 arg: Value::from_u32(0),
1446 table: JumpTable::from_u32(1),
1447 }),
1448 InstructionData::BranchTable {
1449 opcode: Opcode::BrTable,
1450 arg: Value::from_u32(1),
1451 table: JumpTable::from_u32(2),
1452 }
1453 );
1454
1455 assert_eq!(
1457 map(InstructionData::TryCall {
1458 opcode: Opcode::TryCall,
1459 args,
1460 func_ref: FuncRef::from_u32(0),
1461 exception: ExceptionTable::from_u32(1),
1462 }),
1463 InstructionData::TryCall {
1464 opcode: Opcode::TryCall,
1465 args,
1466 func_ref: FuncRef::from_u32(1),
1467 exception: ExceptionTable::from_u32(2),
1468 }
1469 );
1470
1471 assert_eq!(
1473 map(InstructionData::Jump {
1474 opcode: Opcode::Jump,
1475 destination: BlockCall::new(Block::from_u32(99), [], &mut pool),
1476 }),
1477 map(InstructionData::Jump {
1478 opcode: Opcode::Jump,
1479 destination: BlockCall::new(Block::from_u32(42), [], &mut pool),
1480 })
1481 );
1482
1483 assert_eq!(
1485 map(InstructionData::ExceptionHandlerAddress {
1486 opcode: Opcode::GetExceptionHandlerAddress,
1487 block: Block::from_u32(1),
1488 imm: 0.into(),
1489 }),
1490 InstructionData::ExceptionHandlerAddress {
1491 opcode: Opcode::GetExceptionHandlerAddress,
1492 block: Block::from_u32(2),
1493 imm: 0.into(),
1494 },
1495 );
1496
1497 assert_eq!(
1499 map(InstructionData::CallIndirect {
1500 opcode: Opcode::CallIndirect,
1501 args,
1502 sig_ref: SigRef::from_u32(11)
1503 }),
1504 InstructionData::CallIndirect {
1505 opcode: Opcode::CallIndirect,
1506 args: ValueList::new(),
1507 sig_ref: SigRef::from_u32(12)
1508 }
1509 );
1510
1511 assert_eq!(
1513 map(InstructionData::StackAddr {
1514 opcode: Opcode::StackAddr,
1515 stack_slot: StackSlot::from_u32(0),
1516 offset: 0.into()
1517 }),
1518 InstructionData::StackAddr {
1519 opcode: Opcode::StackAddr,
1520 stack_slot: StackSlot::from_u32(1),
1521 offset: 0.into()
1522 },
1523 );
1524
1525 assert_eq!(
1527 map(InstructionData::DynamicStackAddr {
1528 opcode: Opcode::DynamicStackAddr,
1529 dynamic_stack_slot: DynamicStackSlot::from_u32(0),
1530 }),
1531 InstructionData::DynamicStackAddr {
1532 opcode: Opcode::DynamicStackAddr,
1533 dynamic_stack_slot: DynamicStackSlot::from_u32(1),
1534 },
1535 );
1536
1537 assert_eq!(
1539 map(InstructionData::UnaryConst {
1540 opcode: ir::Opcode::Vconst,
1541 constant_handle: ir::Constant::from_u32(2)
1542 }),
1543 InstructionData::UnaryConst {
1544 opcode: ir::Opcode::Vconst,
1545 constant_handle: ir::Constant::from_u32(3)
1546 },
1547 );
1548
1549 assert_eq!(
1551 map(InstructionData::Shuffle {
1552 opcode: ir::Opcode::Shuffle,
1553 args: [Value::from_u32(0), Value::from_u32(1)],
1554 imm: ir::Immediate::from_u32(41),
1555 }),
1556 InstructionData::Shuffle {
1557 opcode: ir::Opcode::Shuffle,
1558 args: [Value::from_u32(1), Value::from_u32(2)],
1559 imm: ir::Immediate::from_u32(42),
1560 },
1561 );
1562 }
1563}