Skip to main content

cranelift_codegen/isa/pulley_shared/inst/
mod.rs

1//! This module defines Pulley-specific machine instruction types.
2
3use core::marker::PhantomData;
4
5use crate::binemit::{Addend, CodeOffset, Reloc};
6use crate::ir::types::{self, F32, F64, I8, I8X16, I16, I32, I64, I128};
7use crate::ir::{self, MemFlagsData, Type};
8use crate::isa::FunctionAlignment;
9use crate::isa::pulley_shared::abi::PulleyMachineDeps;
10use crate::{CodegenError, CodegenResult, settings};
11use crate::{machinst::*, trace};
12use alloc::string::{String, ToString};
13use alloc::vec;
14use alloc::vec::Vec;
15use regalloc2::RegClass;
16use smallvec::SmallVec;
17
18pub mod regs;
19pub use self::regs::*;
20pub mod args;
21pub use self::args::*;
22pub mod emit;
23pub use self::emit::*;
24
25//=============================================================================
26// Instructions (top level): definition
27
28pub use crate::isa::pulley_shared::lower::isle::generated_code::MInst as Inst;
29pub use crate::isa::pulley_shared::lower::isle::generated_code::RawInst;
30
31impl From<RawInst> for Inst {
32    fn from(raw: RawInst) -> Inst {
33        Inst::Raw { raw }
34    }
35}
36
37use super::PulleyTargetKind;
38
39mod generated {
40    use super::*;
41    use crate::isa::pulley_shared::lower::isle::generated_code::RawInst;
42
43    pub struct RawInstDisplay<'a>(pub &'a RawInst);
44
45    include!(concat!(env!("OUT_DIR"), "/pulley_inst_gen.rs"));
46}
47
48/// Out-of-line data for return-calls, to keep the size of `Inst` down.
49#[derive(Clone, Debug)]
50pub struct ReturnCallInfo<T> {
51    /// Where this call is going.
52    pub dest: T,
53
54    /// The size of the argument area for this return-call, potentially smaller
55    /// than that of the caller, but never larger.
56    pub new_stack_arg_size: u32,
57
58    /// The in-register arguments and their constraints.
59    pub uses: CallArgList,
60}
61
62impl Inst {
63    /// Generic constructor for a load (zero-extending where appropriate).
64    pub fn gen_load(dst: Writable<Reg>, mem: Amode, ty: Type, flags: MemFlagsData) -> Inst {
65        if ty.is_vector() {
66            assert_eq!(ty.bytes(), 16);
67            Inst::VLoad {
68                dst: dst.map(|r| VReg::new(r).unwrap()),
69                mem,
70                ty,
71                flags,
72            }
73        } else if ty.is_int() {
74            assert!(ty.bytes() <= 8);
75            Inst::XLoad {
76                dst: dst.map(|r| XReg::new(r).unwrap()),
77                mem,
78                ty,
79                flags,
80            }
81        } else {
82            Inst::FLoad {
83                dst: dst.map(|r| FReg::new(r).unwrap()),
84                mem,
85                ty,
86                flags,
87            }
88        }
89    }
90
91    /// Generic constructor for a store.
92    pub fn gen_store(mem: Amode, from_reg: Reg, ty: Type, flags: MemFlagsData) -> Inst {
93        if ty.is_vector() {
94            assert_eq!(ty.bytes(), 16);
95            Inst::VStore {
96                mem,
97                src: VReg::new(from_reg).unwrap(),
98                ty,
99                flags,
100            }
101        } else if ty.is_int() {
102            assert!(ty.bytes() <= 8);
103            Inst::XStore {
104                mem,
105                src: XReg::new(from_reg).unwrap(),
106                ty,
107                flags,
108            }
109        } else {
110            Inst::FStore {
111                mem,
112                src: FReg::new(from_reg).unwrap(),
113                ty,
114                flags,
115            }
116        }
117    }
118}
119
120fn pulley_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
121    match inst {
122        Inst::Args { args } => {
123            for ArgPair { vreg, preg } in args {
124                collector.reg_fixed_def(vreg, *preg);
125            }
126        }
127        Inst::Rets { rets } => {
128            for RetPair { vreg, preg } in rets {
129                collector.reg_fixed_use(vreg, *preg);
130            }
131        }
132
133        Inst::DummyUse { reg } => {
134            collector.reg_use(reg);
135        }
136
137        Inst::Nop => {}
138
139        Inst::TrapIf { cond, code: _ } => {
140            cond.get_operands(collector);
141        }
142
143        Inst::GetSpecial { dst, reg } => {
144            collector.reg_def(dst);
145            // Note that this is explicitly ignored as this is only used for
146            // special registers that don't participate in register allocation
147            // such as the stack pointer, frame pointer, etc.
148            assert!(reg.is_special());
149        }
150
151        Inst::LoadExtNameNear { dst, .. } | Inst::LoadExtNameFar { dst, .. } => {
152            collector.reg_def(dst);
153        }
154
155        Inst::Call { info } => {
156            let CallInfo {
157                uses,
158                defs,
159                dest,
160                try_call_info,
161                clobbers,
162                ..
163            } = &mut **info;
164
165            // Pulley supports having the first few integer arguments in any
166            // register, so flag that with `reg_use` here.
167            let PulleyCall { args, .. } = dest;
168            for arg in args {
169                collector.reg_use(arg);
170            }
171
172            // Remaining arguments (and return values) are all in fixed
173            // registers according to Pulley's ABI, however.
174            for CallArgPair { vreg, preg } in uses {
175                collector.reg_fixed_use(vreg, *preg);
176            }
177            for CallRetPair { vreg, location } in defs {
178                match location {
179                    RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
180                    RetLocation::Stack(..) => collector.any_def(vreg),
181                }
182            }
183            collector.reg_clobbers(*clobbers);
184            if let Some(try_call_info) = try_call_info {
185                try_call_info.collect_operands(collector);
186            }
187        }
188        Inst::IndirectCallHost { info } => {
189            let CallInfo {
190                uses,
191                defs,
192                try_call_info,
193                clobbers,
194                ..
195            } = &mut **info;
196            for CallArgPair { vreg, preg } in uses {
197                collector.reg_fixed_use(vreg, *preg);
198            }
199            for CallRetPair { vreg, location } in defs {
200                match location {
201                    RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
202                    RetLocation::Stack(..) => collector.any_def(vreg),
203                }
204            }
205            collector.reg_clobbers(*clobbers);
206            if let Some(try_call_info) = try_call_info {
207                try_call_info.collect_operands(collector);
208            }
209        }
210        Inst::IndirectCall { info } => {
211            collector.reg_use(&mut info.dest);
212            let CallInfo {
213                uses,
214                defs,
215                try_call_info,
216                clobbers,
217                ..
218            } = &mut **info;
219            for CallArgPair { vreg, preg } in uses {
220                collector.reg_fixed_use(vreg, *preg);
221            }
222            for CallRetPair { vreg, location } in defs {
223                match location {
224                    RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
225                    RetLocation::Stack(..) => collector.any_def(vreg),
226                }
227            }
228            collector.reg_clobbers(*clobbers);
229            if let Some(try_call_info) = try_call_info {
230                try_call_info.collect_operands(collector);
231            }
232        }
233        Inst::ReturnCall { info } => {
234            for CallArgPair { vreg, preg } in &mut info.uses {
235                collector.reg_fixed_use(vreg, *preg);
236            }
237        }
238        Inst::ReturnIndirectCall { info } => {
239            // Use a fixed location of where to store the value to
240            // return-call-to. Using a fixed location prevents this register
241            // from being allocated to a callee-saved register which will get
242            // clobbered during the register restores just before the
243            // return-call.
244            //
245            // Also note that `x15` is specifically the last caller-saved
246            // register and, at this time, the only non-argument caller-saved
247            // register. This register allocation constraint is why it's not an
248            // argument register.
249            collector.reg_fixed_use(&mut info.dest, regs::x15());
250
251            for CallArgPair { vreg, preg } in &mut info.uses {
252                collector.reg_fixed_use(vreg, *preg);
253            }
254        }
255
256        Inst::Jump { .. } => {}
257
258        Inst::BrIf {
259            cond,
260            taken: _,
261            not_taken: _,
262        } => {
263            cond.get_operands(collector);
264        }
265
266        Inst::LoadAddr { dst, mem } => {
267            collector.reg_def(dst);
268            mem.get_operands(collector);
269        }
270
271        Inst::XLoad {
272            dst,
273            mem,
274            ty: _,
275            flags: _,
276        } => {
277            collector.reg_def(dst);
278            mem.get_operands(collector);
279        }
280
281        Inst::XStore {
282            mem,
283            src,
284            ty: _,
285            flags: _,
286        } => {
287            mem.get_operands(collector);
288            collector.reg_use(src);
289        }
290
291        Inst::FLoad {
292            dst,
293            mem,
294            ty: _,
295            flags: _,
296        } => {
297            collector.reg_def(dst);
298            mem.get_operands(collector);
299        }
300
301        Inst::FStore {
302            mem,
303            src,
304            ty: _,
305            flags: _,
306        } => {
307            mem.get_operands(collector);
308            collector.reg_use(src);
309        }
310
311        Inst::VLoad {
312            dst,
313            mem,
314            ty: _,
315            flags: _,
316        } => {
317            collector.reg_def(dst);
318            mem.get_operands(collector);
319        }
320
321        Inst::VStore {
322            mem,
323            src,
324            ty: _,
325            flags: _,
326        } => {
327            mem.get_operands(collector);
328            collector.reg_use(src);
329        }
330
331        Inst::BrTable { idx, .. } => {
332            collector.reg_use(idx);
333        }
334
335        Inst::Raw { raw } => generated::get_operands(raw, collector),
336
337        Inst::EmitIsland { .. } => {}
338
339        Inst::LabelAddress { dst, label: _ } => {
340            collector.reg_def(dst);
341        }
342
343        Inst::SequencePoint { .. } => {}
344    }
345}
346
347/// A newtype over a Pulley instruction that also carries a phantom type
348/// parameter describing whether we are targeting 32- or 64-bit Pulley bytecode.
349///
350/// Implements `Deref`, `DerefMut`, and `From`/`Into` for `Inst` to allow for
351/// seamless conversion between `Inst` and `InstAndKind`.
352#[derive(Clone, Debug)]
353pub struct InstAndKind<P>
354where
355    P: PulleyTargetKind,
356{
357    inst: Inst,
358    kind: PhantomData<P>,
359}
360
361impl<P> From<Inst> for InstAndKind<P>
362where
363    P: PulleyTargetKind,
364{
365    fn from(inst: Inst) -> Self {
366        Self {
367            inst,
368            kind: PhantomData,
369        }
370    }
371}
372
373impl<P> From<RawInst> for InstAndKind<P>
374where
375    P: PulleyTargetKind,
376{
377    fn from(inst: RawInst) -> Self {
378        Self {
379            inst: inst.into(),
380            kind: PhantomData,
381        }
382    }
383}
384
385impl<P> From<InstAndKind<P>> for Inst
386where
387    P: PulleyTargetKind,
388{
389    fn from(inst: InstAndKind<P>) -> Self {
390        inst.inst
391    }
392}
393
394impl<P> core::ops::Deref for InstAndKind<P>
395where
396    P: PulleyTargetKind,
397{
398    type Target = Inst;
399
400    fn deref(&self) -> &Self::Target {
401        &self.inst
402    }
403}
404
405impl<P> core::ops::DerefMut for InstAndKind<P>
406where
407    P: PulleyTargetKind,
408{
409    fn deref_mut(&mut self) -> &mut Self::Target {
410        &mut self.inst
411    }
412}
413
414impl<P> MachInst for InstAndKind<P>
415where
416    P: PulleyTargetKind,
417{
418    type LabelUse = LabelUse;
419    type ABIMachineSpec = PulleyMachineDeps<P>;
420
421    const TRAP_OPCODE: &'static [u8] = TRAP_OPCODE;
422
423    fn gen_dummy_use(reg: Reg) -> Self {
424        Inst::DummyUse { reg }.into()
425    }
426
427    fn canonical_type_for_rc(rc: RegClass) -> Type {
428        match rc {
429            regalloc2::RegClass::Int => I64,
430            regalloc2::RegClass::Float => F64,
431            regalloc2::RegClass::Vector => I8X16,
432        }
433    }
434
435    fn is_safepoint(&self) -> bool {
436        match self.inst {
437            Inst::Raw {
438                raw: RawInst::Trap { .. },
439            }
440            | Inst::Call { .. }
441            | Inst::IndirectCall { .. }
442            | Inst::IndirectCallHost { .. } => true,
443            _ => false,
444        }
445    }
446
447    fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
448        pulley_get_operands(self, collector);
449    }
450
451    fn is_move(&self) -> Option<(Writable<Reg>, Reg)> {
452        match self.inst {
453            Inst::Raw {
454                raw: RawInst::Xmov { dst, src },
455            } => Some((Writable::from_reg(*dst.to_reg()), *src)),
456            _ => None,
457        }
458    }
459
460    fn is_included_in_clobbers(&self) -> bool {
461        !self.is_args()
462    }
463
464    fn is_trap(&self) -> bool {
465        match self.inst {
466            Inst::Raw {
467                raw: RawInst::Trap { .. },
468            } => true,
469            _ => false,
470        }
471    }
472
473    fn is_args(&self) -> bool {
474        match self.inst {
475            Inst::Args { .. } => true,
476            _ => false,
477        }
478    }
479
480    fn is_term(&self) -> MachTerminator {
481        match &self.inst {
482            Inst::Raw {
483                raw: RawInst::Ret { .. },
484            }
485            | Inst::Rets { .. } => MachTerminator::Ret,
486            Inst::Jump { .. } => MachTerminator::Branch,
487            Inst::BrIf { .. } => MachTerminator::Branch,
488            Inst::BrTable { .. } => MachTerminator::Branch,
489            Inst::ReturnCall { .. } | Inst::ReturnIndirectCall { .. } => MachTerminator::RetCall,
490            Inst::Call { info } if info.try_call_info.is_some() => MachTerminator::Branch,
491            Inst::IndirectCall { info } if info.try_call_info.is_some() => MachTerminator::Branch,
492            Inst::IndirectCallHost { info } if info.try_call_info.is_some() => {
493                MachTerminator::Branch
494            }
495            _ => MachTerminator::None,
496        }
497    }
498
499    fn is_mem_access(&self) -> bool {
500        todo!()
501    }
502
503    fn call_type(&self) -> CallType {
504        match &self.inst {
505            Inst::Call { .. } | Inst::IndirectCall { .. } | Inst::IndirectCallHost { .. } => {
506                CallType::Regular
507            }
508
509            Inst::ReturnCall { .. } | Inst::ReturnIndirectCall { .. } => CallType::TailCall,
510
511            _ => CallType::None,
512        }
513    }
514
515    fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self {
516        match ty {
517            ir::types::I8 | ir::types::I16 | ir::types::I32 | ir::types::I64 => RawInst::Xmov {
518                dst: WritableXReg::try_from(to_reg).unwrap(),
519                src: XReg::new(from_reg).unwrap(),
520            }
521            .into(),
522            ir::types::F32 | ir::types::F64 => RawInst::Fmov {
523                dst: WritableFReg::try_from(to_reg).unwrap(),
524                src: FReg::new(from_reg).unwrap(),
525            }
526            .into(),
527            _ if ty.is_vector() => RawInst::Vmov {
528                dst: WritableVReg::try_from(to_reg).unwrap(),
529                src: VReg::new(from_reg).unwrap(),
530            }
531            .into(),
532            _ => panic!("don't know how to generate a move for type {ty}"),
533        }
534    }
535
536    fn gen_nop(_preferred_size: usize) -> Self {
537        todo!()
538    }
539
540    fn gen_nop_units() -> Vec<Vec<u8>> {
541        let mut bytes = vec![];
542        let nop = pulley_interpreter::op::Nop {};
543        nop.encode(&mut bytes);
544        // NOP needs to be a 1-byte opcode so it can be used to
545        // overwrite a callsite of any length.
546        assert_eq!(bytes.len(), 1);
547        vec![bytes]
548    }
549
550    fn rc_for_type(ty: &Type) -> CodegenResult<(&[RegClass], &[Type])> {
551        match *ty {
552            I8 | I16 | I32 | I64 => Ok((&[RegClass::Int], core::slice::from_ref(ty))),
553            F32 | F64 => Ok((&[RegClass::Float], core::slice::from_ref(ty))),
554            I128 => Ok((&[RegClass::Int, RegClass::Int], &[I64, I64])),
555            _ if ty.is_vector() => {
556                debug_assert!(ty.bits() <= 512);
557
558                // Here we only need to return a SIMD type with the same size as `ty`.
559                // We use these types for spills and reloads, so prefer types with lanes <= 31
560                // since that fits in the immediate field of `vsetivli`.
561                const SIMD_TYPES: [[Type; 1]; 6] = [
562                    [types::I8X2],
563                    [types::I8X4],
564                    [types::I8X8],
565                    [types::I8X16],
566                    [types::I16X16],
567                    [types::I32X16],
568                ];
569                let idx = (ty.bytes().ilog2() - 1) as usize;
570                let ty = &SIMD_TYPES[idx][..];
571
572                Ok((&[RegClass::Vector], ty))
573            }
574            _ => Err(CodegenError::Unsupported(format!(
575                "Unexpected SSA-value type: {ty}"
576            ))),
577        }
578    }
579
580    fn gen_jump(label: MachLabel) -> Self {
581        Inst::Jump { label }.into()
582    }
583
584    fn worst_case_size() -> CodeOffset {
585        // `VShuffle { dst, src1, src2, imm }` is 22 bytes:
586        // 3-byte opcode
587        // dst, src1, src2
588        // 16-byte immediate
589        22
590    }
591
592    fn worst_case_island_growth() -> CodeOffset {
593        // A single instruction may add an embedded constant, a deferred
594        // trap, and a few fixup records. Pulley's label-uses all have
595        // ~2 GiB range and don't support veneers, so this just covers
596        // constants and trap bytes; we pick a conservative bound.
597        32
598    }
599
600    fn function_alignment() -> FunctionAlignment {
601        FunctionAlignment {
602            minimum: 1,
603            preferred: 1,
604        }
605    }
606}
607
608const TRAP_OPCODE: &'static [u8] = &[
609    pulley_interpreter::opcode::Opcode::ExtendedOp as u8,
610    ((pulley_interpreter::opcode::ExtendedOpcode::Trap as u16) >> 0) as u8,
611    ((pulley_interpreter::opcode::ExtendedOpcode::Trap as u16) >> 8) as u8,
612];
613
614#[test]
615fn test_trap_encoding() {
616    let mut dst = alloc::vec::Vec::new();
617    pulley_interpreter::encode::trap(&mut dst);
618    assert_eq!(dst, TRAP_OPCODE);
619}
620
621//=============================================================================
622// Pretty-printing of instructions.
623
624pub struct RegNameDisplay(Reg);
625
626impl std::fmt::Display for RegNameDisplay {
627    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
628        let reg = self.0;
629        match reg.to_real_reg() {
630            Some(real) => {
631                let n = real.hw_enc();
632                match (real.class(), n) {
633                    (RegClass::Int, 63) => f.write_str("sp"),
634                    (RegClass::Int, 62) => f.write_str("lr"),
635                    (RegClass::Int, 61) => f.write_str("fp"),
636                    (RegClass::Int, 60) => f.write_str("tmp0"),
637                    (RegClass::Int, 59) => f.write_str("tmp1"),
638
639                    (RegClass::Int, _) => write!(f, "x{n}"),
640                    (RegClass::Float, _) => write!(f, "f{n}"),
641                    (RegClass::Vector, _) => write!(f, "v{n}"),
642                }
643            }
644            None => {
645                write!(f, "{reg:?}")
646            }
647        }
648    }
649}
650
651fn pretty_print_try_call(info: &TryCallInfo) -> String {
652    format!(
653        "; jump {:?}; catch [{}]",
654        info.continuation,
655        info.pretty_print_dests()
656    )
657}
658
659impl Inst {
660    fn print_with_state<P>(&self, _state: &mut EmitState<P>) -> String
661    where
662        P: PulleyTargetKind,
663    {
664        use core::fmt::Write;
665
666        match self {
667            Inst::Args { args } => {
668                let mut s = "args".to_string();
669                for arg in args {
670                    let preg = RegNameDisplay(arg.preg);
671                    let def = RegNameDisplay(arg.vreg.to_reg());
672                    write!(&mut s, " {def}={preg}").unwrap();
673                }
674                s
675            }
676            Inst::Rets { rets } => {
677                let mut s = "rets".to_string();
678                for ret in rets {
679                    let preg = RegNameDisplay(ret.preg);
680                    let vreg = RegNameDisplay(ret.vreg);
681                    write!(&mut s, " {vreg}={preg}").unwrap();
682                }
683                s
684            }
685
686            Inst::DummyUse { reg } => {
687                let reg = RegNameDisplay(*reg);
688                format!("dummy_use {reg}")
689            }
690
691            Inst::TrapIf { cond, code } => {
692                format!("trap_{cond} // code = {code:?}")
693            }
694
695            Inst::Nop => format!("nop"),
696
697            Inst::GetSpecial { dst, reg } => {
698                let dst = RegNameDisplay(*dst.to_reg());
699                let reg = RegNameDisplay(**reg);
700                format!("xmov {dst}, {reg}")
701            }
702
703            Inst::LoadExtNameNear { dst, name, offset } => {
704                let dst = RegNameDisplay(*dst.to_reg());
705                format!("{dst} = load_ext_name_near {name:?}, {offset}")
706            }
707
708            Inst::LoadExtNameFar { dst, name, offset } => {
709                let dst = RegNameDisplay(*dst.to_reg());
710                format!("{dst} = load_ext_name_far {name:?}, {offset}")
711            }
712
713            Inst::Call { info } => {
714                let try_call = info
715                    .try_call_info
716                    .as_ref()
717                    .map(|tci| pretty_print_try_call(tci))
718                    .unwrap_or_default();
719                format!("call {info:?}{try_call}")
720            }
721
722            Inst::IndirectCall { info } => {
723                let callee = RegNameDisplay(*info.dest);
724                let try_call = info
725                    .try_call_info
726                    .as_ref()
727                    .map(|tci| pretty_print_try_call(tci))
728                    .unwrap_or_default();
729                format!("indirect_call {callee}, {info:?}{try_call}")
730            }
731
732            Inst::ReturnCall { info } => {
733                format!("return_call {info:?}")
734            }
735
736            Inst::ReturnIndirectCall { info } => {
737                let callee = RegNameDisplay(*info.dest);
738                format!("return_indirect_call {callee}, {info:?}")
739            }
740
741            Inst::IndirectCallHost { info } => {
742                let try_call = info
743                    .try_call_info
744                    .as_ref()
745                    .map(|tci| pretty_print_try_call(tci))
746                    .unwrap_or_default();
747                format!("indirect_call_host {info:?}{try_call}")
748            }
749
750            Inst::Jump { label } => format!("jump {}", label.to_string()),
751
752            Inst::BrIf {
753                cond,
754                taken,
755                not_taken,
756            } => {
757                let taken = taken.to_string();
758                let not_taken = not_taken.to_string();
759                format!("br_{cond}, {taken}; jump {not_taken}")
760            }
761
762            Inst::LoadAddr { dst, mem } => {
763                let dst = RegNameDisplay(*dst.to_reg());
764                let mem = mem.to_string();
765                format!("{dst} = load_addr {mem}")
766            }
767
768            Inst::XLoad {
769                dst,
770                mem,
771                ty,
772                flags,
773            } => {
774                let dst = RegNameDisplay(*dst.to_reg());
775                let ty = ty.bits();
776                let mem = mem.to_string();
777                format!("{dst} = xload{ty} {mem} // flags ={flags}")
778            }
779
780            Inst::XStore {
781                mem,
782                src,
783                ty,
784                flags,
785            } => {
786                let ty = ty.bits();
787                let mem = mem.to_string();
788                let src = RegNameDisplay(**src);
789                format!("xstore{ty} {mem}, {src} // flags = {flags}")
790            }
791
792            Inst::FLoad {
793                dst,
794                mem,
795                ty,
796                flags,
797            } => {
798                let dst = RegNameDisplay(*dst.to_reg());
799                let ty = ty.bits();
800                let mem = mem.to_string();
801                format!("{dst} = fload{ty} {mem} // flags ={flags}")
802            }
803
804            Inst::FStore {
805                mem,
806                src,
807                ty,
808                flags,
809            } => {
810                let ty = ty.bits();
811                let mem = mem.to_string();
812                let src = RegNameDisplay(**src);
813                format!("fstore{ty} {mem}, {src} // flags = {flags}")
814            }
815
816            Inst::VLoad {
817                dst,
818                mem,
819                ty,
820                flags,
821            } => {
822                let dst = RegNameDisplay(*dst.to_reg());
823                let ty = ty.bits();
824                let mem = mem.to_string();
825                format!("{dst} = vload{ty} {mem} // flags ={flags}")
826            }
827
828            Inst::VStore {
829                mem,
830                src,
831                ty,
832                flags,
833            } => {
834                let ty = ty.bits();
835                let mem = mem.to_string();
836                let src = RegNameDisplay(**src);
837                format!("vstore{ty} {mem}, {src} // flags = {flags}")
838            }
839
840            Inst::BrTable {
841                idx,
842                default,
843                targets,
844            } => {
845                let idx = RegNameDisplay(**idx);
846                format!("br_table {idx} {default:?} {targets:?}")
847            }
848            Inst::Raw { raw } => format!("{}", generated::RawInstDisplay(raw)),
849
850            Inst::EmitIsland { space_needed } => format!("emit_island {space_needed}"),
851
852            Inst::LabelAddress { dst, label } => {
853                let dst = RegNameDisplay(dst.to_reg().to_reg());
854                format!("label_address {dst}, {label:?}")
855            }
856
857            Inst::SequencePoint {} => {
858                format!("sequence_point")
859            }
860        }
861    }
862}
863
864/// Different forms of label references for different instruction formats.
865#[derive(Clone, Copy, Debug, PartialEq, Eq)]
866pub enum LabelUse {
867    /// A PC-relative `jump`/`call`/etc... instruction with an `i32` relative
868    /// target.
869    ///
870    /// The relative distance to the destination is added to the 4 bytes at the
871    /// label site.
872    PcRel,
873}
874
875impl MachInstLabelUse for LabelUse {
876    /// Alignment for veneer code. Pulley instructions don't require any
877    /// particular alignment.
878    const ALIGN: CodeOffset = 1;
879
880    /// Maximum PC-relative range (positive), inclusive.
881    fn max_pos_range(self) -> CodeOffset {
882        match self {
883            Self::PcRel => 0x7fff_ffff,
884        }
885    }
886
887    /// Maximum PC-relative range (negative).
888    fn max_neg_range(self) -> CodeOffset {
889        match self {
890            Self::PcRel => 0x8000_0000,
891        }
892    }
893
894    /// Size of window into code needed to do the patch.
895    fn patch_size(self) -> CodeOffset {
896        match self {
897            Self::PcRel => 4,
898        }
899    }
900
901    /// Perform the patch.
902    fn patch(self, buffer: &mut [u8], use_offset: CodeOffset, label_offset: CodeOffset) {
903        let use_relative = (label_offset as i64) - (use_offset as i64);
904        debug_assert!(use_relative <= self.max_pos_range() as i64);
905        debug_assert!(use_relative >= -(self.max_neg_range() as i64));
906        let pc_rel = i32::try_from(use_relative).unwrap() as u32;
907        match self {
908            Self::PcRel => {
909                let buf: &mut [u8; 4] = buffer.try_into().unwrap();
910                let addend = u32::from_le_bytes(*buf);
911                trace!(
912                    "patching label use @ {use_offset:#x} \
913                     to label {label_offset:#x} via \
914                     PC-relative offset {pc_rel:#x} \
915                     adding in {addend:#x}"
916                );
917                let value = pc_rel.wrapping_add(addend);
918                *buf = value.to_le_bytes();
919            }
920        }
921    }
922
923    /// Is a veneer supported for this label reference type?
924    fn supports_veneer(self) -> bool {
925        match self {
926            Self::PcRel => false,
927        }
928    }
929
930    /// How large is the veneer, if supported?
931    fn veneer_size(self) -> CodeOffset {
932        match self {
933            Self::PcRel => 0,
934        }
935    }
936
937    fn worst_case_veneer_size() -> CodeOffset {
938        0
939    }
940
941    /// Generate a veneer into the buffer, given that this veneer is at `veneer_offset`, and return
942    /// an offset and label-use for the veneer's use of the original label.
943    fn generate_veneer(
944        self,
945        _buffer: &mut [u8],
946        _veneer_offset: CodeOffset,
947    ) -> (CodeOffset, LabelUse) {
948        match self {
949            Self::PcRel => panic!("veneer not supported for {self:?}"),
950        }
951    }
952
953    fn from_reloc(reloc: Reloc, addend: Addend) -> Option<LabelUse> {
954        match (reloc, addend) {
955            (Reloc::PulleyPcRel, 0) => Some(LabelUse::PcRel),
956            _ => None,
957        }
958    }
959}