1use 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
25pub 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#[derive(Clone, Debug)]
48pub struct ReturnCallInfo<T> {
49 pub dest: T,
51
52 pub new_stack_arg_size: u32,
55
56 pub uses: CallArgList,
58}
59
60impl Inst {
61 pub fn gen_load(dst: Writable<Reg>, mem: Amode, ty: Type, flags: MemFlagsData) -> 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 pub fn gen_store(mem: Amode, from_reg: Reg, ty: Type, flags: MemFlagsData) -> 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 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 let PulleyCall { args, .. } = dest;
166 for arg in args {
167 collector.reg_use(arg);
168 }
169
170 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 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#[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 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 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 22
592 }
593
594 fn worst_case_island_growth() -> CodeOffset {
595 32
600 }
601
602 fn ref_type_regclass(_settings: &settings::Flags) -> RegClass {
603 RegClass::Int
604 }
605
606 fn function_alignment() -> FunctionAlignment {
607 FunctionAlignment {
608 minimum: 1,
609 preferred: 1,
610 }
611 }
612}
613
614const TRAP_OPCODE: &'static [u8] = &[
615 pulley_interpreter::opcode::Opcode::ExtendedOp as u8,
616 ((pulley_interpreter::opcode::ExtendedOpcode::Trap as u16) >> 0) as u8,
617 ((pulley_interpreter::opcode::ExtendedOpcode::Trap as u16) >> 8) as u8,
618];
619
620#[test]
621fn test_trap_encoding() {
622 let mut dst = alloc::vec::Vec::new();
623 pulley_interpreter::encode::trap(&mut dst);
624 assert_eq!(dst, TRAP_OPCODE);
625}
626
627pub fn reg_name(reg: Reg) -> String {
631 match reg.to_real_reg() {
632 Some(real) => {
633 let n = real.hw_enc();
634 match (real.class(), n) {
635 (RegClass::Int, 63) => format!("sp"),
636 (RegClass::Int, 62) => format!("lr"),
637 (RegClass::Int, 61) => format!("fp"),
638 (RegClass::Int, 60) => format!("tmp0"),
639 (RegClass::Int, 59) => format!("tmp1"),
640
641 (RegClass::Int, _) => format!("x{n}"),
642 (RegClass::Float, _) => format!("f{n}"),
643 (RegClass::Vector, _) => format!("v{n}"),
644 }
645 }
646 None => {
647 format!("{reg:?}")
648 }
649 }
650}
651
652fn pretty_print_try_call(info: &TryCallInfo) -> String {
653 format!(
654 "; jump {:?}; catch [{}]",
655 info.continuation,
656 info.pretty_print_dests()
657 )
658}
659
660impl Inst {
661 fn print_with_state<P>(&self, _state: &mut EmitState<P>) -> String
662 where
663 P: PulleyTargetKind,
664 {
665 use core::fmt::Write;
666
667 let format_reg = |reg: Reg| -> String { reg_name(reg) };
668
669 match self {
670 Inst::Args { args } => {
671 let mut s = "args".to_string();
672 for arg in args {
673 let preg = format_reg(arg.preg);
674 let def = format_reg(arg.vreg.to_reg());
675 write!(&mut s, " {def}={preg}").unwrap();
676 }
677 s
678 }
679 Inst::Rets { rets } => {
680 let mut s = "rets".to_string();
681 for ret in rets {
682 let preg = format_reg(ret.preg);
683 let vreg = format_reg(ret.vreg);
684 write!(&mut s, " {vreg}={preg}").unwrap();
685 }
686 s
687 }
688
689 Inst::DummyUse { reg } => {
690 let reg = format_reg(*reg);
691 format!("dummy_use {reg}")
692 }
693
694 Inst::TrapIf { cond, code } => {
695 format!("trap_{cond} // code = {code:?}")
696 }
697
698 Inst::Nop => format!("nop"),
699
700 Inst::GetSpecial { dst, reg } => {
701 let dst = format_reg(*dst.to_reg());
702 let reg = format_reg(**reg);
703 format!("xmov {dst}, {reg}")
704 }
705
706 Inst::LoadExtNameNear { dst, name, offset } => {
707 let dst = format_reg(*dst.to_reg());
708 format!("{dst} = load_ext_name_near {name:?}, {offset}")
709 }
710
711 Inst::LoadExtNameFar { dst, name, offset } => {
712 let dst = format_reg(*dst.to_reg());
713 format!("{dst} = load_ext_name_far {name:?}, {offset}")
714 }
715
716 Inst::Call { info } => {
717 let try_call = info
718 .try_call_info
719 .as_ref()
720 .map(|tci| pretty_print_try_call(tci))
721 .unwrap_or_default();
722 format!("call {info:?}{try_call}")
723 }
724
725 Inst::IndirectCall { info } => {
726 let callee = format_reg(*info.dest);
727 let try_call = info
728 .try_call_info
729 .as_ref()
730 .map(|tci| pretty_print_try_call(tci))
731 .unwrap_or_default();
732 format!("indirect_call {callee}, {info:?}{try_call}")
733 }
734
735 Inst::ReturnCall { info } => {
736 format!("return_call {info:?}")
737 }
738
739 Inst::ReturnIndirectCall { info } => {
740 let callee = format_reg(*info.dest);
741 format!("return_indirect_call {callee}, {info:?}")
742 }
743
744 Inst::IndirectCallHost { info } => {
745 let try_call = info
746 .try_call_info
747 .as_ref()
748 .map(|tci| pretty_print_try_call(tci))
749 .unwrap_or_default();
750 format!("indirect_call_host {info:?}{try_call}")
751 }
752
753 Inst::Jump { label } => format!("jump {}", label.to_string()),
754
755 Inst::BrIf {
756 cond,
757 taken,
758 not_taken,
759 } => {
760 let taken = taken.to_string();
761 let not_taken = not_taken.to_string();
762 format!("br_{cond}, {taken}; jump {not_taken}")
763 }
764
765 Inst::LoadAddr { dst, mem } => {
766 let dst = format_reg(*dst.to_reg());
767 let mem = mem.to_string();
768 format!("{dst} = load_addr {mem}")
769 }
770
771 Inst::XLoad {
772 dst,
773 mem,
774 ty,
775 flags,
776 } => {
777 let dst = format_reg(*dst.to_reg());
778 let ty = ty.bits();
779 let mem = mem.to_string();
780 format!("{dst} = xload{ty} {mem} // flags ={flags}")
781 }
782
783 Inst::XStore {
784 mem,
785 src,
786 ty,
787 flags,
788 } => {
789 let ty = ty.bits();
790 let mem = mem.to_string();
791 let src = format_reg(**src);
792 format!("xstore{ty} {mem}, {src} // flags = {flags}")
793 }
794
795 Inst::FLoad {
796 dst,
797 mem,
798 ty,
799 flags,
800 } => {
801 let dst = format_reg(*dst.to_reg());
802 let ty = ty.bits();
803 let mem = mem.to_string();
804 format!("{dst} = fload{ty} {mem} // flags ={flags}")
805 }
806
807 Inst::FStore {
808 mem,
809 src,
810 ty,
811 flags,
812 } => {
813 let ty = ty.bits();
814 let mem = mem.to_string();
815 let src = format_reg(**src);
816 format!("fstore{ty} {mem}, {src} // flags = {flags}")
817 }
818
819 Inst::VLoad {
820 dst,
821 mem,
822 ty,
823 flags,
824 } => {
825 let dst = format_reg(*dst.to_reg());
826 let ty = ty.bits();
827 let mem = mem.to_string();
828 format!("{dst} = vload{ty} {mem} // flags ={flags}")
829 }
830
831 Inst::VStore {
832 mem,
833 src,
834 ty,
835 flags,
836 } => {
837 let ty = ty.bits();
838 let mem = mem.to_string();
839 let src = format_reg(**src);
840 format!("vstore{ty} {mem}, {src} // flags = {flags}")
841 }
842
843 Inst::BrTable {
844 idx,
845 default,
846 targets,
847 } => {
848 let idx = format_reg(**idx);
849 format!("br_table {idx} {default:?} {targets:?}")
850 }
851 Inst::Raw { raw } => generated::print(raw),
852
853 Inst::EmitIsland { space_needed } => format!("emit_island {space_needed}"),
854
855 Inst::LabelAddress { dst, label } => {
856 let dst = format_reg(dst.to_reg().to_reg());
857 format!("label_address {dst}, {label:?}")
858 }
859
860 Inst::SequencePoint {} => {
861 format!("sequence_point")
862 }
863 }
864 }
865}
866
867#[derive(Clone, Copy, Debug, PartialEq, Eq)]
869pub enum LabelUse {
870 PcRel,
876}
877
878impl MachInstLabelUse for LabelUse {
879 const ALIGN: CodeOffset = 1;
882
883 fn max_pos_range(self) -> CodeOffset {
885 match self {
886 Self::PcRel => 0x7fff_ffff,
887 }
888 }
889
890 fn max_neg_range(self) -> CodeOffset {
892 match self {
893 Self::PcRel => 0x8000_0000,
894 }
895 }
896
897 fn patch_size(self) -> CodeOffset {
899 match self {
900 Self::PcRel => 4,
901 }
902 }
903
904 fn patch(self, buffer: &mut [u8], use_offset: CodeOffset, label_offset: CodeOffset) {
906 let use_relative = (label_offset as i64) - (use_offset as i64);
907 debug_assert!(use_relative <= self.max_pos_range() as i64);
908 debug_assert!(use_relative >= -(self.max_neg_range() as i64));
909 let pc_rel = i32::try_from(use_relative).unwrap() as u32;
910 match self {
911 Self::PcRel => {
912 let buf: &mut [u8; 4] = buffer.try_into().unwrap();
913 let addend = u32::from_le_bytes(*buf);
914 trace!(
915 "patching label use @ {use_offset:#x} \
916 to label {label_offset:#x} via \
917 PC-relative offset {pc_rel:#x} \
918 adding in {addend:#x}"
919 );
920 let value = pc_rel.wrapping_add(addend);
921 *buf = value.to_le_bytes();
922 }
923 }
924 }
925
926 fn supports_veneer(self) -> bool {
928 match self {
929 Self::PcRel => false,
930 }
931 }
932
933 fn veneer_size(self) -> CodeOffset {
935 match self {
936 Self::PcRel => 0,
937 }
938 }
939
940 fn worst_case_veneer_size() -> CodeOffset {
941 0
942 }
943
944 fn generate_veneer(
947 self,
948 _buffer: &mut [u8],
949 _veneer_offset: CodeOffset,
950 ) -> (CodeOffset, LabelUse) {
951 match self {
952 Self::PcRel => panic!("veneer not supported for {self:?}"),
953 }
954 }
955
956 fn from_reloc(reloc: Reloc, addend: Addend) -> Option<LabelUse> {
957 match (reloc, addend) {
958 (Reloc::PulleyPcRel, 0) => Some(LabelUse::PcRel),
959 _ => None,
960 }
961 }
962}