1 //! Pulley instruction arguments.
2 
3 use super::*;
4 use crate::ir::ExternalName;
5 use crate::machinst::abi::StackAMode;
6 use pulley_interpreter::encode;
7 use pulley_interpreter::regs::Reg as _;
8 use std::fmt;
9 
10 /// A macro for defining a newtype of `Reg` that enforces some invariant about
11 /// the wrapped `Reg` (such as that it is of a particular register class).
12 macro_rules! newtype_of_reg {
13     (
14         $newtype_reg:ident,
15         $newtype_writable_reg:ident,
16         $class:expr
17     ) => {
18         /// A newtype wrapper around `Reg`.
19         #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20         pub struct $newtype_reg(Reg);
21 
22         impl PartialEq<Reg> for $newtype_reg {
23             fn eq(&self, other: &Reg) -> bool {
24                 self.0 == *other
25             }
26         }
27 
28         impl From<$newtype_reg> for Reg {
29             fn from(r: $newtype_reg) -> Self {
30                 r.0
31             }
32         }
33 
34         impl TryFrom<Reg> for $newtype_reg {
35             type Error = ();
36             fn try_from(r: Reg) -> Result<Self, Self::Error> {
37                 Self::new(r).ok_or(())
38             }
39         }
40 
41         impl $newtype_reg {
42             /// Create this newtype from the given register, or return `None` if the register
43             /// is not a valid instance of this newtype.
44             pub fn new(reg: Reg) -> Option<Self> {
45                 if reg.class() == $class {
46                     Some(Self(reg))
47                 } else {
48                     None
49                 }
50             }
51 
52             /// Get this newtype's underlying `Reg`.
53             pub fn to_reg(self) -> Reg {
54                 self.0
55             }
56         }
57 
58         // Convenience impl so that people working with this newtype can use it
59         // "just like" a plain `Reg`.
60         //
61         // NB: We cannot implement `DerefMut` because that would let people do
62         // nasty stuff like `*my_xreg.deref_mut() = some_freg`, breaking the
63         // invariants that `XReg` provides.
64         impl std::ops::Deref for $newtype_reg {
65             type Target = Reg;
66 
67             fn deref(&self) -> &Reg {
68                 &self.0
69             }
70         }
71 
72         /// If you know what you're doing, you can explicitly mutably borrow the
73         /// underlying `Reg`. Don't make it point to the wrong type of register
74         /// please.
75         impl AsMut<Reg> for $newtype_reg {
76             fn as_mut(&mut self) -> &mut Reg {
77                 &mut self.0
78             }
79         }
80 
81         /// Writable Reg.
82         pub type $newtype_writable_reg = Writable<$newtype_reg>;
83 
84         impl From<pulley_interpreter::regs::$newtype_reg> for $newtype_reg {
85             fn from(r: pulley_interpreter::regs::$newtype_reg) -> Self {
86                 Self::new(regalloc2::PReg::new(usize::from(r as u8), $class).into()).unwrap()
87             }
88         }
89         impl From<$newtype_reg> for pulley_interpreter::regs::$newtype_reg {
90             fn from(r: $newtype_reg) -> Self {
91                 Self::new(r.to_real_reg().unwrap().hw_enc()).unwrap()
92             }
93         }
94         impl<'a> From<&'a $newtype_reg> for pulley_interpreter::regs::$newtype_reg {
95             fn from(r: &'a $newtype_reg) -> Self {
96                 Self::new(r.to_real_reg().unwrap().hw_enc()).unwrap()
97             }
98         }
99         impl From<$newtype_writable_reg> for pulley_interpreter::regs::$newtype_reg {
100             fn from(r: $newtype_writable_reg) -> Self {
101                 Self::new(r.to_reg().to_real_reg().unwrap().hw_enc()).unwrap()
102             }
103         }
104         impl<'a> From<&'a $newtype_writable_reg> for pulley_interpreter::regs::$newtype_reg {
105             fn from(r: &'a $newtype_writable_reg) -> Self {
106                 Self::new(r.to_reg().to_real_reg().unwrap().hw_enc()).unwrap()
107             }
108         }
109 
110         impl TryFrom<Writable<Reg>> for $newtype_writable_reg {
111             type Error = ();
112             fn try_from(r: Writable<Reg>) -> Result<Self, Self::Error> {
113                 let r = r.to_reg();
114                 match $newtype_reg::new(r) {
115                     Some(r) => Ok(Writable::from_reg(r)),
116                     None => Err(()),
117                 }
118             }
119         }
120     };
121 }
122 
123 // Newtypes for registers classes.
124 newtype_of_reg!(XReg, WritableXReg, RegClass::Int);
125 newtype_of_reg!(FReg, WritableFReg, RegClass::Float);
126 newtype_of_reg!(VReg, WritableVReg, RegClass::Vector);
127 
128 impl XReg {
129     /// Index of the first "special" register, or the end of which registers
130     /// regalloc is allowed to use.
131     pub const SPECIAL_START: u8 = pulley_interpreter::regs::XReg::SPECIAL_START;
132 
133     /// Returns whether this is a "special" physical register for pulley.
134     pub fn is_special(&self) -> bool {
135         match self.as_pulley() {
136             Some(reg) => reg.is_special(),
137             None => false,
138         }
139     }
140 
141     /// Returns the pulley-typed register, if this is a physical register.
142     pub fn as_pulley(&self) -> Option<pulley_interpreter::XReg> {
143         let enc = self.to_real_reg()?.hw_enc();
144         Some(pulley_interpreter::XReg::new(enc).unwrap())
145     }
146 }
147 
148 pub use super::super::lower::isle::generated_code::Amode;
149 
150 impl Amode {
151     /// Add the registers referenced by this Amode to `collector`.
152     pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
153         match self {
154             Amode::RegOffset { base, offset: _ } => collector.reg_use(base),
155             // Registers used in these modes aren't allocatable.
156             Amode::SpOffset { .. } | Amode::Stack { .. } => {}
157         }
158     }
159 
160     pub(crate) fn get_base_register(&self) -> Option<XReg> {
161         match self {
162             Amode::RegOffset { base, offset: _ } => Some(*base),
163             Amode::SpOffset { .. } | Amode::Stack { .. } => Some(XReg::new(stack_reg()).unwrap()),
164         }
165     }
166 
167     pub(crate) fn get_offset_with_state<P>(&self, state: &EmitState<P>) -> i32
168     where
169         P: PulleyTargetKind,
170     {
171         match self {
172             Amode::RegOffset { base: _, offset } | Amode::SpOffset { offset } => *offset,
173             Amode::Stack { amode } => {
174                 let offset64 = match amode {
175                     StackAMode::IncomingArg(offset, stack_args_size) => {
176                         let offset = i64::from(*stack_args_size) - *offset;
177                         let frame_layout = state.frame_layout();
178                         let sp_offset = frame_layout.tail_args_size
179                             + frame_layout.setup_area_size
180                             + frame_layout.clobber_size
181                             + frame_layout.fixed_frame_storage_size
182                             + frame_layout.outgoing_args_size;
183                         i64::from(sp_offset) - offset
184                     }
185                     StackAMode::Slot(offset) => {
186                         offset + i64::from(state.frame_layout().outgoing_args_size)
187                     }
188                     StackAMode::OutgoingArg(offset) => *offset,
189                 };
190                 i32::try_from(offset64).unwrap()
191             }
192         }
193     }
194 }
195 
196 impl core::fmt::Display for Amode {
197     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
198         match self {
199             Amode::SpOffset { offset } => {
200                 if *offset >= 0 {
201                     write!(f, "sp+{offset}")
202                 } else {
203                     write!(f, "sp{offset}")
204                 }
205             }
206             Amode::RegOffset { base, offset } => {
207                 let name = reg_name(**base);
208                 if *offset >= 0 {
209                     write!(f, "{name}+{offset}")
210                 } else {
211                     write!(f, "{name}{offset}")
212                 }
213             }
214             Amode::Stack { amode } => core::fmt::Debug::fmt(amode, f),
215         }
216     }
217 }
218 
219 impl From<StackAMode> for Amode {
220     fn from(amode: StackAMode) -> Self {
221         Amode::Stack { amode }
222     }
223 }
224 
225 /// The size of an operand or operation.
226 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
227 pub enum OperandSize {
228     /// 32 bits.
229     Size32,
230     /// 64 bits.
231     Size64,
232 }
233 
234 pub use crate::isa::pulley_shared::lower::isle::generated_code::Cond;
235 
236 impl Cond {
237     /// Collect register operands within `collector` for register allocation.
238     pub fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
239         match self {
240             Cond::If32 { reg } | Cond::IfNot32 { reg } => collector.reg_use(reg),
241 
242             Cond::IfXeq32 { src1, src2 }
243             | Cond::IfXneq32 { src1, src2 }
244             | Cond::IfXslt32 { src1, src2 }
245             | Cond::IfXslteq32 { src1, src2 }
246             | Cond::IfXult32 { src1, src2 }
247             | Cond::IfXulteq32 { src1, src2 }
248             | Cond::IfXeq64 { src1, src2 }
249             | Cond::IfXneq64 { src1, src2 }
250             | Cond::IfXslt64 { src1, src2 }
251             | Cond::IfXslteq64 { src1, src2 }
252             | Cond::IfXult64 { src1, src2 }
253             | Cond::IfXulteq64 { src1, src2 } => {
254                 collector.reg_use(src1);
255                 collector.reg_use(src2);
256             }
257 
258             Cond::IfXeq32I32 { src1, src2 }
259             | Cond::IfXneq32I32 { src1, src2 }
260             | Cond::IfXslt32I32 { src1, src2 }
261             | Cond::IfXslteq32I32 { src1, src2 }
262             | Cond::IfXsgt32I32 { src1, src2 }
263             | Cond::IfXsgteq32I32 { src1, src2 }
264             | Cond::IfXeq64I32 { src1, src2 }
265             | Cond::IfXneq64I32 { src1, src2 }
266             | Cond::IfXslt64I32 { src1, src2 }
267             | Cond::IfXslteq64I32 { src1, src2 }
268             | Cond::IfXsgt64I32 { src1, src2 }
269             | Cond::IfXsgteq64I32 { src1, src2 } => {
270                 collector.reg_use(src1);
271                 let _: &mut i32 = src2;
272             }
273 
274             Cond::IfXult32I32 { src1, src2 }
275             | Cond::IfXulteq32I32 { src1, src2 }
276             | Cond::IfXugt32I32 { src1, src2 }
277             | Cond::IfXugteq32I32 { src1, src2 }
278             | Cond::IfXult64I32 { src1, src2 }
279             | Cond::IfXulteq64I32 { src1, src2 }
280             | Cond::IfXugt64I32 { src1, src2 }
281             | Cond::IfXugteq64I32 { src1, src2 } => {
282                 collector.reg_use(src1);
283                 let _: &mut u32 = src2;
284             }
285         }
286     }
287 
288     /// Encode this condition as a branch into `sink`.
289     ///
290     /// Note that the offset encoded to jump by is filled in as 0 and it's
291     /// assumed `MachBuffer` will come back and clean it up.
292     pub fn encode(&self, sink: &mut impl Extend<u8>, rel: i32) {
293         match *self {
294             Cond::If32 { reg } => encode::br_if32(sink, reg, rel),
295             Cond::IfNot32 { reg } => encode::br_if_not32(sink, reg, rel),
296             Cond::IfXeq32 { src1, src2 } => encode::br_if_xeq32(sink, src1, src2, rel),
297             Cond::IfXneq32 { src1, src2 } => encode::br_if_xneq32(sink, src1, src2, rel),
298             Cond::IfXslt32 { src1, src2 } => encode::br_if_xslt32(sink, src1, src2, rel),
299             Cond::IfXslteq32 { src1, src2 } => encode::br_if_xslteq32(sink, src1, src2, rel),
300             Cond::IfXult32 { src1, src2 } => encode::br_if_xult32(sink, src1, src2, rel),
301             Cond::IfXulteq32 { src1, src2 } => encode::br_if_xulteq32(sink, src1, src2, rel),
302             Cond::IfXeq64 { src1, src2 } => encode::br_if_xeq64(sink, src1, src2, rel),
303             Cond::IfXneq64 { src1, src2 } => encode::br_if_xneq64(sink, src1, src2, rel),
304             Cond::IfXslt64 { src1, src2 } => encode::br_if_xslt64(sink, src1, src2, rel),
305             Cond::IfXslteq64 { src1, src2 } => encode::br_if_xslteq64(sink, src1, src2, rel),
306             Cond::IfXult64 { src1, src2 } => encode::br_if_xult64(sink, src1, src2, rel),
307             Cond::IfXulteq64 { src1, src2 } => encode::br_if_xulteq64(sink, src1, src2, rel),
308 
309             Cond::IfXeq32I32 { src1, src2 } => match i8::try_from(src2) {
310                 Ok(src2) => encode::br_if_xeq32_i8(sink, src1, src2, rel),
311                 Err(_) => encode::br_if_xeq32_i32(sink, src1, src2, rel),
312             },
313             Cond::IfXneq32I32 { src1, src2 } => match i8::try_from(src2) {
314                 Ok(src2) => encode::br_if_xneq32_i8(sink, src1, src2, rel),
315                 Err(_) => encode::br_if_xneq32_i32(sink, src1, src2, rel),
316             },
317             Cond::IfXslt32I32 { src1, src2 } => match i8::try_from(src2) {
318                 Ok(src2) => encode::br_if_xslt32_i8(sink, src1, src2, rel),
319                 Err(_) => encode::br_if_xslt32_i32(sink, src1, src2, rel),
320             },
321             Cond::IfXslteq32I32 { src1, src2 } => match i8::try_from(src2) {
322                 Ok(src2) => encode::br_if_xslteq32_i8(sink, src1, src2, rel),
323                 Err(_) => encode::br_if_xslteq32_i32(sink, src1, src2, rel),
324             },
325             Cond::IfXsgt32I32 { src1, src2 } => match i8::try_from(src2) {
326                 Ok(src2) => encode::br_if_xsgt32_i8(sink, src1, src2, rel),
327                 Err(_) => encode::br_if_xsgt32_i32(sink, src1, src2, rel),
328             },
329             Cond::IfXsgteq32I32 { src1, src2 } => match i8::try_from(src2) {
330                 Ok(src2) => encode::br_if_xsgteq32_i8(sink, src1, src2, rel),
331                 Err(_) => encode::br_if_xsgteq32_i32(sink, src1, src2, rel),
332             },
333             Cond::IfXult32I32 { src1, src2 } => match u8::try_from(src2) {
334                 Ok(src2) => encode::br_if_xult32_u8(sink, src1, src2, rel),
335                 Err(_) => encode::br_if_xult32_u32(sink, src1, src2, rel),
336             },
337             Cond::IfXulteq32I32 { src1, src2 } => match u8::try_from(src2) {
338                 Ok(src2) => encode::br_if_xulteq32_u8(sink, src1, src2, rel),
339                 Err(_) => encode::br_if_xulteq32_u32(sink, src1, src2, rel),
340             },
341             Cond::IfXugt32I32 { src1, src2 } => match u8::try_from(src2) {
342                 Ok(src2) => encode::br_if_xugt32_u8(sink, src1, src2, rel),
343                 Err(_) => encode::br_if_xugt32_u32(sink, src1, src2, rel),
344             },
345             Cond::IfXugteq32I32 { src1, src2 } => match u8::try_from(src2) {
346                 Ok(src2) => encode::br_if_xugteq32_u8(sink, src1, src2, rel),
347                 Err(_) => encode::br_if_xugteq32_u32(sink, src1, src2, rel),
348             },
349 
350             Cond::IfXeq64I32 { src1, src2 } => match i8::try_from(src2) {
351                 Ok(src2) => encode::br_if_xeq64_i8(sink, src1, src2, rel),
352                 Err(_) => encode::br_if_xeq64_i32(sink, src1, src2, rel),
353             },
354             Cond::IfXneq64I32 { src1, src2 } => match i8::try_from(src2) {
355                 Ok(src2) => encode::br_if_xneq64_i8(sink, src1, src2, rel),
356                 Err(_) => encode::br_if_xneq64_i32(sink, src1, src2, rel),
357             },
358             Cond::IfXslt64I32 { src1, src2 } => match i8::try_from(src2) {
359                 Ok(src2) => encode::br_if_xslt64_i8(sink, src1, src2, rel),
360                 Err(_) => encode::br_if_xslt64_i32(sink, src1, src2, rel),
361             },
362             Cond::IfXslteq64I32 { src1, src2 } => match i8::try_from(src2) {
363                 Ok(src2) => encode::br_if_xslteq64_i8(sink, src1, src2, rel),
364                 Err(_) => encode::br_if_xslteq64_i32(sink, src1, src2, rel),
365             },
366             Cond::IfXsgt64I32 { src1, src2 } => match i8::try_from(src2) {
367                 Ok(src2) => encode::br_if_xsgt64_i8(sink, src1, src2, rel),
368                 Err(_) => encode::br_if_xsgt64_i32(sink, src1, src2, rel),
369             },
370             Cond::IfXsgteq64I32 { src1, src2 } => match i8::try_from(src2) {
371                 Ok(src2) => encode::br_if_xsgteq64_i8(sink, src1, src2, rel),
372                 Err(_) => encode::br_if_xsgteq64_i32(sink, src1, src2, rel),
373             },
374             Cond::IfXult64I32 { src1, src2 } => match u8::try_from(src2) {
375                 Ok(src2) => encode::br_if_xult64_u8(sink, src1, src2, rel),
376                 Err(_) => encode::br_if_xult64_u32(sink, src1, src2, rel),
377             },
378             Cond::IfXulteq64I32 { src1, src2 } => match u8::try_from(src2) {
379                 Ok(src2) => encode::br_if_xulteq64_u8(sink, src1, src2, rel),
380                 Err(_) => encode::br_if_xulteq64_u32(sink, src1, src2, rel),
381             },
382             Cond::IfXugt64I32 { src1, src2 } => match u8::try_from(src2) {
383                 Ok(src2) => encode::br_if_xugt64_u8(sink, src1, src2, rel),
384                 Err(_) => encode::br_if_xugt64_u32(sink, src1, src2, rel),
385             },
386             Cond::IfXugteq64I32 { src1, src2 } => match u8::try_from(src2) {
387                 Ok(src2) => encode::br_if_xugteq64_u8(sink, src1, src2, rel),
388                 Err(_) => encode::br_if_xugteq64_u32(sink, src1, src2, rel),
389             },
390         }
391     }
392 
393     /// Inverts this conditional.
394     pub fn invert(&self) -> Cond {
395         match *self {
396             Cond::If32 { reg } => Cond::IfNot32 { reg },
397             Cond::IfNot32 { reg } => Cond::If32 { reg },
398             Cond::IfXeq32 { src1, src2 } => Cond::IfXneq32 { src1, src2 },
399             Cond::IfXneq32 { src1, src2 } => Cond::IfXeq32 { src1, src2 },
400             Cond::IfXeq64 { src1, src2 } => Cond::IfXneq64 { src1, src2 },
401             Cond::IfXneq64 { src1, src2 } => Cond::IfXeq64 { src1, src2 },
402 
403             // Note that for below the condition changes but the operands are
404             // also swapped.
405             Cond::IfXslt32 { src1, src2 } => Cond::IfXslteq32 {
406                 src1: src2,
407                 src2: src1,
408             },
409             Cond::IfXslteq32 { src1, src2 } => Cond::IfXslt32 {
410                 src1: src2,
411                 src2: src1,
412             },
413             Cond::IfXult32 { src1, src2 } => Cond::IfXulteq32 {
414                 src1: src2,
415                 src2: src1,
416             },
417             Cond::IfXulteq32 { src1, src2 } => Cond::IfXult32 {
418                 src1: src2,
419                 src2: src1,
420             },
421             Cond::IfXslt64 { src1, src2 } => Cond::IfXslteq64 {
422                 src1: src2,
423                 src2: src1,
424             },
425             Cond::IfXslteq64 { src1, src2 } => Cond::IfXslt64 {
426                 src1: src2,
427                 src2: src1,
428             },
429             Cond::IfXult64 { src1, src2 } => Cond::IfXulteq64 {
430                 src1: src2,
431                 src2: src1,
432             },
433             Cond::IfXulteq64 { src1, src2 } => Cond::IfXult64 {
434                 src1: src2,
435                 src2: src1,
436             },
437 
438             Cond::IfXeq32I32 { src1, src2 } => Cond::IfXneq32I32 { src1, src2 },
439             Cond::IfXneq32I32 { src1, src2 } => Cond::IfXeq32I32 { src1, src2 },
440             Cond::IfXslt32I32 { src1, src2 } => Cond::IfXsgteq32I32 { src1, src2 },
441             Cond::IfXslteq32I32 { src1, src2 } => Cond::IfXsgt32I32 { src1, src2 },
442             Cond::IfXult32I32 { src1, src2 } => Cond::IfXugteq32I32 { src1, src2 },
443             Cond::IfXulteq32I32 { src1, src2 } => Cond::IfXugt32I32 { src1, src2 },
444             Cond::IfXsgt32I32 { src1, src2 } => Cond::IfXslteq32I32 { src1, src2 },
445             Cond::IfXsgteq32I32 { src1, src2 } => Cond::IfXslt32I32 { src1, src2 },
446             Cond::IfXugt32I32 { src1, src2 } => Cond::IfXulteq32I32 { src1, src2 },
447             Cond::IfXugteq32I32 { src1, src2 } => Cond::IfXult32I32 { src1, src2 },
448 
449             Cond::IfXeq64I32 { src1, src2 } => Cond::IfXneq64I32 { src1, src2 },
450             Cond::IfXneq64I32 { src1, src2 } => Cond::IfXeq64I32 { src1, src2 },
451             Cond::IfXslt64I32 { src1, src2 } => Cond::IfXsgteq64I32 { src1, src2 },
452             Cond::IfXslteq64I32 { src1, src2 } => Cond::IfXsgt64I32 { src1, src2 },
453             Cond::IfXult64I32 { src1, src2 } => Cond::IfXugteq64I32 { src1, src2 },
454             Cond::IfXulteq64I32 { src1, src2 } => Cond::IfXugt64I32 { src1, src2 },
455             Cond::IfXsgt64I32 { src1, src2 } => Cond::IfXslteq64I32 { src1, src2 },
456             Cond::IfXsgteq64I32 { src1, src2 } => Cond::IfXslt64I32 { src1, src2 },
457             Cond::IfXugt64I32 { src1, src2 } => Cond::IfXulteq64I32 { src1, src2 },
458             Cond::IfXugteq64I32 { src1, src2 } => Cond::IfXult64I32 { src1, src2 },
459         }
460     }
461 }
462 
463 impl fmt::Display for Cond {
464     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465         match self {
466             Cond::If32 { reg } => write!(f, "if32 {}", reg_name(**reg)),
467             Cond::IfNot32 { reg } => write!(f, "if_not32 {}", reg_name(**reg)),
468             Cond::IfXeq32 { src1, src2 } => {
469                 write!(f, "if_xeq32 {}, {}", reg_name(**src1), reg_name(**src2))
470             }
471             Cond::IfXneq32 { src1, src2 } => {
472                 write!(f, "if_xneq32 {}, {}", reg_name(**src1), reg_name(**src2))
473             }
474             Cond::IfXslt32 { src1, src2 } => {
475                 write!(f, "if_xslt32 {}, {}", reg_name(**src1), reg_name(**src2))
476             }
477             Cond::IfXslteq32 { src1, src2 } => {
478                 write!(f, "if_xslteq32 {}, {}", reg_name(**src1), reg_name(**src2))
479             }
480             Cond::IfXult32 { src1, src2 } => {
481                 write!(f, "if_xult32 {}, {}", reg_name(**src1), reg_name(**src2))
482             }
483             Cond::IfXulteq32 { src1, src2 } => {
484                 write!(f, "if_xulteq32 {}, {}", reg_name(**src1), reg_name(**src2))
485             }
486             Cond::IfXeq64 { src1, src2 } => {
487                 write!(f, "if_xeq64 {}, {}", reg_name(**src1), reg_name(**src2))
488             }
489             Cond::IfXneq64 { src1, src2 } => {
490                 write!(f, "if_xneq64 {}, {}", reg_name(**src1), reg_name(**src2))
491             }
492             Cond::IfXslt64 { src1, src2 } => {
493                 write!(f, "if_xslt64 {}, {}", reg_name(**src1), reg_name(**src2))
494             }
495             Cond::IfXslteq64 { src1, src2 } => {
496                 write!(f, "if_xslteq64 {}, {}", reg_name(**src1), reg_name(**src2))
497             }
498             Cond::IfXult64 { src1, src2 } => {
499                 write!(f, "if_xult64 {}, {}", reg_name(**src1), reg_name(**src2))
500             }
501             Cond::IfXulteq64 { src1, src2 } => {
502                 write!(f, "if_xulteq64 {}, {}", reg_name(**src1), reg_name(**src2))
503             }
504             Cond::IfXeq32I32 { src1, src2 } => {
505                 write!(f, "if_xeq32_i32 {}, {src2}", reg_name(**src1))
506             }
507             Cond::IfXneq32I32 { src1, src2 } => {
508                 write!(f, "if_xneq32_i32 {}, {src2}", reg_name(**src1))
509             }
510             Cond::IfXslt32I32 { src1, src2 } => {
511                 write!(f, "if_xslt32_i32 {}, {src2}", reg_name(**src1))
512             }
513             Cond::IfXslteq32I32 { src1, src2 } => {
514                 write!(f, "if_xslteq32_i32 {}, {src2}", reg_name(**src1))
515             }
516             Cond::IfXsgt32I32 { src1, src2 } => {
517                 write!(f, "if_xsgt32_i32 {}, {src2}", reg_name(**src1))
518             }
519             Cond::IfXsgteq32I32 { src1, src2 } => {
520                 write!(f, "if_xsgteq32_i32 {}, {src2}", reg_name(**src1))
521             }
522             Cond::IfXult32I32 { src1, src2 } => {
523                 write!(f, "if_xult32_i32 {}, {src2}", reg_name(**src1))
524             }
525             Cond::IfXulteq32I32 { src1, src2 } => {
526                 write!(f, "if_xulteq32_i32 {}, {src2}", reg_name(**src1))
527             }
528             Cond::IfXugt32I32 { src1, src2 } => {
529                 write!(f, "if_xugt32_i32 {}, {src2}", reg_name(**src1))
530             }
531             Cond::IfXugteq32I32 { src1, src2 } => {
532                 write!(f, "if_xugteq32_i32 {}, {src2}", reg_name(**src1))
533             }
534             Cond::IfXeq64I32 { src1, src2 } => {
535                 write!(f, "if_xeq64_i32 {}, {src2}", reg_name(**src1))
536             }
537             Cond::IfXneq64I32 { src1, src2 } => {
538                 write!(f, "if_xneq64_i32 {}, {src2}", reg_name(**src1))
539             }
540             Cond::IfXslt64I32 { src1, src2 } => {
541                 write!(f, "if_xslt64_i32 {}, {src2}", reg_name(**src1))
542             }
543             Cond::IfXslteq64I32 { src1, src2 } => {
544                 write!(f, "if_xslteq64_i32 {}, {src2}", reg_name(**src1))
545             }
546             Cond::IfXsgt64I32 { src1, src2 } => {
547                 write!(f, "if_xsgt64_i32 {}, {src2}", reg_name(**src1))
548             }
549             Cond::IfXsgteq64I32 { src1, src2 } => {
550                 write!(f, "if_xsgteq64_i32 {}, {src2}", reg_name(**src1))
551             }
552             Cond::IfXult64I32 { src1, src2 } => {
553                 write!(f, "if_xult64_i32 {}, {src2}", reg_name(**src1))
554             }
555             Cond::IfXulteq64I32 { src1, src2 } => {
556                 write!(f, "if_xulteq64_i32 {}, {src2}", reg_name(**src1))
557             }
558             Cond::IfXugt64I32 { src1, src2 } => {
559                 write!(f, "if_xugt64_i32 {}, {src2}", reg_name(**src1))
560             }
561             Cond::IfXugteq64I32 { src1, src2 } => {
562                 write!(f, "if_xugteq64_i32 {}, {src2}", reg_name(**src1))
563             }
564         }
565     }
566 }
567 
568 /// Payload of `CallInfo` for call instructions
569 #[derive(Clone, Debug)]
570 pub struct PulleyCall {
571     /// The external name that's being called, or the Cranelift-generated
572     /// function that's being invoked.
573     pub name: ExternalName,
574     /// Arguments tracked in this call invocation which aren't assigned fixed
575     /// registers. This tracks up to 4 registers and all remaining registers
576     /// will be present and tracked in `CallInfo<T>` fields.
577     pub args: SmallVec<[XReg; 4]>,
578 }
579 
580 pub use super::super::lower::isle::generated_code::AddrO32;
581 
582 impl Copy for AddrO32 {}
583 
584 impl AddrO32 {
585     /// Implementation of regalloc for this addressing mode.
586     pub fn collect_operands(&mut self, collector: &mut impl OperandVisitor) {
587         match self {
588             AddrO32::Base { addr, offset: _ } => {
589                 collector.reg_use(addr);
590             }
591         }
592     }
593 }
594 
595 impl From<AddrO32> for pulley_interpreter::AddrO32 {
596     fn from(addr: AddrO32) -> Self {
597         match addr {
598             AddrO32::Base { addr, offset } => Self {
599                 addr: addr.into(),
600                 offset,
601             },
602         }
603     }
604 }
605 
606 impl fmt::Display for AddrO32 {
607     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
608         match self {
609             AddrO32::Base { addr, offset } => {
610                 let addr = reg_name(**addr);
611                 write!(f, "{addr}, {offset}")
612             }
613         }
614     }
615 }
616 
617 pub use super::super::lower::isle::generated_code::AddrZ;
618 
619 impl Copy for AddrZ {}
620 
621 impl AddrZ {
622     /// Implementation of regalloc for this addressing mode.
623     pub fn collect_operands(&mut self, collector: &mut impl OperandVisitor) {
624         match self {
625             AddrZ::Base { addr, offset: _ } => {
626                 collector.reg_use(addr);
627             }
628         }
629     }
630 }
631 
632 impl From<AddrZ> for pulley_interpreter::AddrZ {
633     fn from(addr: AddrZ) -> Self {
634         match addr {
635             AddrZ::Base { addr, offset } => Self {
636                 addr: addr.into(),
637                 offset,
638             },
639         }
640     }
641 }
642 
643 impl fmt::Display for AddrZ {
644     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
645         match self {
646             AddrZ::Base { addr, offset } => {
647                 let addr = reg_name(**addr);
648                 write!(f, "{addr}, {offset}")
649             }
650         }
651     }
652 }
653 
654 pub use super::super::lower::isle::generated_code::AddrG32;
655 
656 impl Copy for AddrG32 {}
657 
658 impl AddrG32 {
659     /// Implementation of regalloc for this addressing mode.
660     pub fn collect_operands(&mut self, collector: &mut impl OperandVisitor) {
661         match self {
662             AddrG32::RegisterBound {
663                 host_heap_base,
664                 host_heap_bound,
665                 wasm_addr,
666                 offset: _,
667             } => {
668                 collector.reg_use(host_heap_base);
669                 collector.reg_use(host_heap_bound);
670                 collector.reg_use(wasm_addr);
671             }
672         }
673     }
674 }
675 
676 impl From<AddrG32> for pulley_interpreter::AddrG32 {
677     fn from(addr: AddrG32) -> Self {
678         match addr {
679             AddrG32::RegisterBound {
680                 host_heap_base,
681                 host_heap_bound,
682                 wasm_addr,
683                 offset,
684             } => Self {
685                 host_heap_base: host_heap_base.into(),
686                 host_heap_bound: host_heap_bound.into(),
687                 wasm_addr: wasm_addr.into(),
688                 offset,
689             },
690         }
691     }
692 }
693 
694 impl fmt::Display for AddrG32 {
695     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
696         match self {
697             AddrG32::RegisterBound {
698                 host_heap_base,
699                 host_heap_bound,
700                 wasm_addr,
701                 offset,
702             } => {
703                 let host_heap_base = reg_name(**host_heap_base);
704                 let host_heap_bound = reg_name(**host_heap_bound);
705                 let wasm_addr = reg_name(**wasm_addr);
706                 write!(
707                     f,
708                     "{host_heap_base}, {host_heap_bound}, {wasm_addr}, {offset}",
709                 )
710             }
711         }
712     }
713 }
714 
715 pub use super::super::lower::isle::generated_code::AddrG32Bne;
716 
717 impl Copy for AddrG32Bne {}
718 
719 impl AddrG32Bne {
720     /// Implementation of regalloc for this addressing mode.
721     pub fn collect_operands(&mut self, collector: &mut impl OperandVisitor) {
722         match self {
723             AddrG32Bne::BoundNe {
724                 host_heap_base,
725                 host_heap_bound_addr,
726                 host_heap_bound_offset: _,
727                 wasm_addr,
728                 offset: _,
729             } => {
730                 collector.reg_use(host_heap_base);
731                 collector.reg_use(host_heap_bound_addr);
732                 collector.reg_use(wasm_addr);
733             }
734         }
735     }
736 }
737 
738 impl From<AddrG32Bne> for pulley_interpreter::AddrG32Bne {
739     fn from(addr: AddrG32Bne) -> Self {
740         match addr {
741             AddrG32Bne::BoundNe {
742                 host_heap_base,
743                 host_heap_bound_addr,
744                 host_heap_bound_offset,
745                 wasm_addr,
746                 offset,
747             } => Self {
748                 host_heap_base: host_heap_base.into(),
749                 host_heap_bound_addr: host_heap_bound_addr.into(),
750                 host_heap_bound_offset,
751                 wasm_addr: wasm_addr.into(),
752                 offset,
753             },
754         }
755     }
756 }
757 
758 impl fmt::Display for AddrG32Bne {
759     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
760         match self {
761             AddrG32Bne::BoundNe {
762                 host_heap_base,
763                 host_heap_bound_addr,
764                 host_heap_bound_offset,
765                 wasm_addr,
766                 offset,
767             } => {
768                 let host_heap_base = reg_name(**host_heap_base);
769                 let host_heap_bound_addr = reg_name(**host_heap_bound_addr);
770                 let wasm_addr = reg_name(**wasm_addr);
771                 write!(
772                     f,
773                     "{host_heap_base}, \
774                      *[{host_heap_bound_addr} + {host_heap_bound_offset}], \
775                      {wasm_addr}, \
776                      {offset}",
777                 )
778             }
779         }
780     }
781 }
782