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 pub struct RawInstDisplay<'a>(pub &'a RawInst);
44
45 include!(concat!(env!("OUT_DIR"), "/pulley_inst_gen.rs"));
46}
47
48#[derive(Clone, Debug)]
50pub struct ReturnCallInfo<T> {
51 pub dest: T,
53
54 pub new_stack_arg_size: u32,
57
58 pub uses: CallArgList,
60}
61
62impl Inst {
63 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 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 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 let PulleyCall { args, .. } = dest;
168 for arg in args {
169 collector.reg_use(arg);
170 }
171
172 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 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#[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 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 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 22
590 }
591
592 fn worst_case_island_growth() -> CodeOffset {
593 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
621pub 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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
866pub enum LabelUse {
867 PcRel,
873}
874
875impl MachInstLabelUse for LabelUse {
876 const ALIGN: CodeOffset = 1;
879
880 fn max_pos_range(self) -> CodeOffset {
882 match self {
883 Self::PcRel => 0x7fff_ffff,
884 }
885 }
886
887 fn max_neg_range(self) -> CodeOffset {
889 match self {
890 Self::PcRel => 0x8000_0000,
891 }
892 }
893
894 fn patch_size(self) -> CodeOffset {
896 match self {
897 Self::PcRel => 4,
898 }
899 }
900
901 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 fn supports_veneer(self) -> bool {
925 match self {
926 Self::PcRel => false,
927 }
928 }
929
930 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 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}