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