1 //! Pulley binary code emission.
2 
3 use super::*;
4 use crate::ir::{self, Endianness};
5 use crate::isa;
6 use crate::isa::pulley_shared::PointerWidth;
7 use crate::isa::pulley_shared::abi::PulleyMachineDeps;
8 use core::marker::PhantomData;
9 use cranelift_control::ControlPlane;
10 use pulley_interpreter::encode as enc;
11 use pulley_interpreter::regs::BinaryOperands;
12 
13 pub struct EmitInfo {
14     call_conv: isa::CallConv,
15     shared_flags: settings::Flags,
16     isa_flags: crate::isa::pulley_shared::settings::Flags,
17 }
18 
19 impl EmitInfo {
new( call_conv: isa::CallConv, shared_flags: settings::Flags, isa_flags: crate::isa::pulley_shared::settings::Flags, ) -> Self20     pub(crate) fn new(
21         call_conv: isa::CallConv,
22         shared_flags: settings::Flags,
23         isa_flags: crate::isa::pulley_shared::settings::Flags,
24     ) -> Self {
25         Self {
26             call_conv,
27             shared_flags,
28             isa_flags,
29         }
30     }
31 
endianness(&self, flags: MemFlags) -> Endianness32     fn endianness(&self, flags: MemFlags) -> Endianness {
33         flags.endianness(self.isa_flags.endianness())
34     }
35 }
36 
37 /// State carried between emissions of a sequence of instructions.
38 #[derive(Default, Clone, Debug)]
39 pub struct EmitState<P>
40 where
41     P: PulleyTargetKind,
42 {
43     _phantom: PhantomData<P>,
44     ctrl_plane: ControlPlane,
45     user_stack_map: Option<ir::UserStackMap>,
46     frame_layout: FrameLayout,
47 }
48 
49 impl<P> EmitState<P>
50 where
51     P: PulleyTargetKind,
52 {
take_stack_map(&mut self) -> Option<ir::UserStackMap>53     fn take_stack_map(&mut self) -> Option<ir::UserStackMap> {
54         self.user_stack_map.take()
55     }
56 }
57 
58 impl<P> MachInstEmitState<InstAndKind<P>> for EmitState<P>
59 where
60     P: PulleyTargetKind,
61 {
new(abi: &Callee<PulleyMachineDeps<P>>, ctrl_plane: ControlPlane) -> Self62     fn new(abi: &Callee<PulleyMachineDeps<P>>, ctrl_plane: ControlPlane) -> Self {
63         EmitState {
64             _phantom: PhantomData,
65             ctrl_plane,
66             user_stack_map: None,
67             frame_layout: abi.frame_layout().clone(),
68         }
69     }
70 
pre_safepoint(&mut self, user_stack_map: Option<ir::UserStackMap>)71     fn pre_safepoint(&mut self, user_stack_map: Option<ir::UserStackMap>) {
72         self.user_stack_map = user_stack_map;
73     }
74 
ctrl_plane_mut(&mut self) -> &mut ControlPlane75     fn ctrl_plane_mut(&mut self) -> &mut ControlPlane {
76         &mut self.ctrl_plane
77     }
78 
take_ctrl_plane(self) -> ControlPlane79     fn take_ctrl_plane(self) -> ControlPlane {
80         self.ctrl_plane
81     }
82 
frame_layout(&self) -> &FrameLayout83     fn frame_layout(&self) -> &FrameLayout {
84         &self.frame_layout
85     }
86 }
87 
88 impl<P> MachInstEmit for InstAndKind<P>
89 where
90     P: PulleyTargetKind,
91 {
92     type State = EmitState<P>;
93     type Info = EmitInfo;
94 
emit(&self, sink: &mut MachBuffer<Self>, emit_info: &Self::Info, state: &mut Self::State)95     fn emit(&self, sink: &mut MachBuffer<Self>, emit_info: &Self::Info, state: &mut Self::State) {
96         // N.B.: we *must* not exceed the "worst-case size" used to compute
97         // where to insert islands, except when islands are explicitly triggered
98         // (with an `EmitIsland`). We check this in debug builds. This is `mut`
99         // to allow disabling the check for `JTSequence`, which is always
100         // emitted following an `EmitIsland`.
101         let mut start = sink.cur_offset();
102         pulley_emit(self, sink, emit_info, state, &mut start);
103 
104         let end = sink.cur_offset();
105         assert!(
106             (end - start) <= InstAndKind::<P>::worst_case_size(),
107             "encoded inst {self:?} longer than worst-case size: length: {}, Inst::worst_case_size() = {}",
108             end - start,
109             InstAndKind::<P>::worst_case_size()
110         );
111     }
112 
pretty_print_inst(&self, state: &mut Self::State) -> String113     fn pretty_print_inst(&self, state: &mut Self::State) -> String {
114         self.print_with_state(state)
115     }
116 }
117 
pulley_emit<P>( inst: &Inst, sink: &mut MachBuffer<InstAndKind<P>>, emit_info: &EmitInfo, state: &mut EmitState<P>, start_offset: &mut u32, ) where P: PulleyTargetKind,118 fn pulley_emit<P>(
119     inst: &Inst,
120     sink: &mut MachBuffer<InstAndKind<P>>,
121     emit_info: &EmitInfo,
122     state: &mut EmitState<P>,
123     start_offset: &mut u32,
124 ) where
125     P: PulleyTargetKind,
126 {
127     match inst {
128         // Pseudo-instructions that don't actually encode to anything.
129         Inst::Args { .. } | Inst::Rets { .. } | Inst::DummyUse { .. } => {}
130 
131         Inst::TrapIf { cond, code } => {
132             let trap = sink.defer_trap(*code);
133             let not_trap = sink.get_label();
134 
135             <InstAndKind<P>>::from(Inst::BrIf {
136                 cond: cond.clone(),
137                 taken: trap,
138                 not_taken: not_trap,
139             })
140             .emit(sink, emit_info, state);
141             sink.bind_label(not_trap, &mut state.ctrl_plane);
142         }
143 
144         Inst::Nop => todo!(),
145 
146         Inst::GetSpecial { dst, reg } => enc::xmov(sink, dst, reg),
147 
148         Inst::LoadExtNameNear { dst, name, offset } => {
149             patch_pc_rel_offset(sink, |sink| enc::xpcadd(sink, dst, 0));
150             let end = sink.cur_offset();
151             sink.add_reloc_at_offset(end - 4, Reloc::PulleyPcRel, &**name, *offset);
152         }
153 
154         Inst::LoadExtNameFar { dst, name, offset } => {
155             let size = match P::pointer_width() {
156                 PointerWidth::PointerWidth32 => {
157                     enc::xconst32(sink, dst, 0);
158                     4
159                 }
160                 PointerWidth::PointerWidth64 => {
161                     enc::xconst64(sink, dst, 0);
162                     8
163                 }
164             };
165             let end = sink.cur_offset();
166             sink.add_reloc_at_offset(end - size, Reloc::Abs8, &**name, *offset);
167         }
168 
169         Inst::Call { info } => {
170             let start = sink.cur_offset();
171 
172             // If arguments happen to already be in the right register for the
173             // ABI then remove them from this list. Otherwise emit the
174             // appropriate `Call` instruction depending on how many arguments we
175             // have that aren't already in their correct register according to
176             // ABI conventions.
177             let mut args = &info.dest.args[..];
178             while !args.is_empty() && args.last().copied() == XReg::new(x_reg(args.len() - 1)) {
179                 args = &args[..args.len() - 1];
180             }
181             patch_pc_rel_offset(sink, |sink| match args {
182                 [] => enc::call(sink, 0),
183                 [x0] => enc::call1(sink, x0, 0),
184                 [x0, x1] => enc::call2(sink, x0, x1, 0),
185                 [x0, x1, x2] => enc::call3(sink, x0, x1, x2, 0),
186                 [x0, x1, x2, x3] => enc::call4(sink, x0, x1, x2, x3, 0),
187                 _ => unreachable!(),
188             });
189             let end = sink.cur_offset();
190             sink.add_reloc_at_offset(end - 4, Reloc::PulleyPcRel, &info.dest.name, 0);
191             if let Some(s) = state.take_stack_map() {
192                 let offset = sink.cur_offset();
193                 sink.push_user_stack_map(state, offset, s);
194             }
195 
196             if let Some(try_call) = info.try_call_info.as_ref() {
197                 sink.add_try_call_site(
198                     Some(state.frame_layout.sp_to_fp()),
199                     try_call.exception_handlers(&state.frame_layout),
200                 );
201             } else {
202                 sink.add_call_site();
203             }
204 
205             if info.patchable {
206                 sink.add_patchable_call_site(sink.cur_offset() - start);
207             } else {
208                 let adjust = -i32::try_from(info.callee_pop_size).unwrap();
209                 for i in PulleyMachineDeps::<P>::gen_sp_reg_adjust(adjust) {
210                     i.emit(sink, emit_info, state);
211                 }
212 
213                 // Load any stack-carried return values.
214                 info.emit_retval_loads::<PulleyMachineDeps<P>, _, _>(
215                     state.frame_layout().stackslots_size,
216                     |inst| inst.emit(sink, emit_info, state),
217                     |space_needed| Some(<InstAndKind<P>>::from(Inst::EmitIsland { space_needed })),
218                 );
219             }
220 
221             // If this is a try-call, jump to the continuation
222             // (normal-return) block.
223             if let Some(try_call) = info.try_call_info.as_ref() {
224                 let jmp = InstAndKind::<P>::from(Inst::Jump {
225                     label: try_call.continuation,
226                 });
227                 jmp.emit(sink, emit_info, state);
228             }
229 
230             // We produce an island above if needed, so disable
231             // the worst-case-size check in this case.
232             *start_offset = sink.cur_offset();
233         }
234 
235         Inst::IndirectCall { info } => {
236             enc::call_indirect(sink, info.dest);
237 
238             if let Some(s) = state.take_stack_map() {
239                 let offset = sink.cur_offset();
240                 sink.push_user_stack_map(state, offset, s);
241             }
242 
243             if let Some(try_call) = info.try_call_info.as_ref() {
244                 sink.add_try_call_site(
245                     Some(state.frame_layout.sp_to_fp()),
246                     try_call.exception_handlers(&state.frame_layout),
247                 );
248             } else {
249                 sink.add_call_site();
250             }
251 
252             let adjust = -i32::try_from(info.callee_pop_size).unwrap();
253             for i in PulleyMachineDeps::<P>::gen_sp_reg_adjust(adjust) {
254                 i.emit(sink, emit_info, state);
255             }
256 
257             // Load any stack-carried return values.
258             info.emit_retval_loads::<PulleyMachineDeps<P>, _, _>(
259                 state.frame_layout().stackslots_size,
260                 |inst| inst.emit(sink, emit_info, state),
261                 |space_needed| Some(<InstAndKind<P>>::from(Inst::EmitIsland { space_needed })),
262             );
263 
264             // If this is a try-call, jump to the continuation
265             // (normal-return) block.
266             if let Some(try_call) = info.try_call_info.as_ref() {
267                 let jmp = InstAndKind::<P>::from(Inst::Jump {
268                     label: try_call.continuation,
269                 });
270                 jmp.emit(sink, emit_info, state);
271             }
272 
273             // We produce an island above if needed, so disable
274             // the worst-case-size check in this case.
275             *start_offset = sink.cur_offset();
276         }
277 
278         Inst::ReturnCall { info } => {
279             emit_return_call_common_sequence(sink, emit_info, state, &info);
280 
281             // Emit an unconditional jump which is quite similar to `Inst::Call`
282             // except that a `jump` opcode is used instead of a `call` opcode.
283             sink.put1(pulley_interpreter::Opcode::Jump as u8);
284             sink.add_reloc(Reloc::PulleyPcRel, &info.dest, 0);
285             sink.put4(1);
286 
287             // Islands were manually handled in
288             // `emit_return_call_common_sequence`.
289             *start_offset = sink.cur_offset();
290         }
291 
292         Inst::ReturnIndirectCall { info } => {
293             emit_return_call_common_sequence(sink, emit_info, state, &info);
294             enc::xjump(sink, info.dest);
295 
296             // Islands were manually handled in
297             // `emit_return_call_common_sequence`.
298             *start_offset = sink.cur_offset();
299         }
300 
301         Inst::IndirectCallHost { info } => {
302             // Emit a relocation to fill in the actual immediate argument here
303             // in `call_indirect_host`.
304             sink.add_reloc(Reloc::PulleyCallIndirectHost, &info.dest, 0);
305             enc::call_indirect_host(sink, 0_u8);
306 
307             if let Some(s) = state.take_stack_map() {
308                 let offset = sink.cur_offset();
309                 sink.push_user_stack_map(state, offset, s);
310             }
311 
312             if let Some(try_call) = info.try_call_info.as_ref() {
313                 sink.add_try_call_site(
314                     Some(state.frame_layout.sp_to_fp()),
315                     try_call.exception_handlers(&state.frame_layout),
316                 );
317             } else {
318                 sink.add_call_site();
319             }
320 
321             // If a callee pop is happening here that means that something has
322             // messed up, these are expected to be "very simple" signatures.
323             assert!(info.callee_pop_size == 0);
324         }
325 
326         Inst::Jump { label } => {
327             sink.use_label_at_offset(*start_offset + 1, *label, LabelUse::PcRel);
328             sink.add_uncond_branch(*start_offset, *start_offset + 5, *label);
329             patch_pc_rel_offset(sink, |sink| enc::jump(sink, 0));
330         }
331 
332         Inst::BrIf {
333             cond,
334             taken,
335             not_taken,
336         } => {
337             // Encode the inverted form of the branch. Branches always have
338             // their trailing 4 bytes as the relative offset which is what we're
339             // going to target here within the `MachBuffer`.
340             let mut inverted = SmallVec::<[u8; 16]>::new();
341             cond.invert().encode(&mut inverted, 0);
342             let len = inverted.len() as u32;
343             inverted.clear();
344             cond.invert()
345                 .encode(&mut inverted, i32::try_from(len - 4).unwrap());
346             assert!(len > 4);
347 
348             // Use the `taken` label 4 bytes before the end of the instruction
349             // we're about to emit as that's the base of `PcRelOffset`. Note
350             // that the `Jump` here factors in the offset from the start of the
351             // instruction to the start of the relative offset, hence `len - 4`
352             // as the factor to adjust by.
353             let taken_end = *start_offset + len;
354             sink.use_label_at_offset(taken_end - 4, *taken, LabelUse::PcRel);
355             sink.add_cond_branch(*start_offset, taken_end, *taken, &inverted);
356             patch_pc_rel_offset(sink, |sink| cond.encode(sink, 0));
357             debug_assert_eq!(sink.cur_offset(), taken_end);
358 
359             // For the not-taken branch use an unconditional jump to the
360             // relevant label, and we know that the jump instruction is 5 bytes
361             // long where the final 4 bytes are the offset to jump by.
362             let not_taken_start = taken_end + 1;
363             let not_taken_end = not_taken_start + 4;
364             sink.use_label_at_offset(not_taken_start, *not_taken, LabelUse::PcRel);
365             sink.add_uncond_branch(taken_end, not_taken_end, *not_taken);
366             patch_pc_rel_offset(sink, |sink| enc::jump(sink, 0));
367             assert_eq!(sink.cur_offset(), not_taken_end);
368         }
369 
370         Inst::LoadAddr { dst, mem } => {
371             let base = mem.get_base_register();
372             let offset = mem.get_offset_with_state(state);
373 
374             if let Some(base) = base {
375                 if offset == 0 {
376                     enc::xmov(sink, dst, base);
377                 } else {
378                     if let Ok(offset) = i8::try_from(offset) {
379                         enc::xconst8(sink, dst, offset);
380                     } else if let Ok(offset) = i16::try_from(offset) {
381                         enc::xconst16(sink, dst, offset);
382                     } else {
383                         enc::xconst32(sink, dst, offset);
384                     }
385 
386                     match P::pointer_width() {
387                         PointerWidth::PointerWidth32 => {
388                             enc::xadd32(sink, BinaryOperands::new(dst, base, dst))
389                         }
390                         PointerWidth::PointerWidth64 => {
391                             enc::xadd64(sink, BinaryOperands::new(dst, base, dst))
392                         }
393                     }
394                 }
395             } else {
396                 unreachable!("all pulley amodes have a base register right now")
397             }
398         }
399 
400         Inst::XLoad {
401             dst,
402             mem,
403             ty,
404             flags,
405         } => {
406             use Endianness as E;
407             assert!(flags.trap_code().is_none());
408             let addr = AddrO32::Base {
409                 addr: mem.get_base_register().unwrap(),
410                 offset: mem.get_offset_with_state(state),
411             };
412             let endian = emit_info.endianness(*flags);
413             match *ty {
414                 I8 => enc::xload8_u32_o32(sink, dst, addr),
415                 I16 => match endian {
416                     E::Little => enc::xload16le_s32_o32(sink, dst, addr),
417                     E::Big => enc::xload16be_s32_o32(sink, dst, addr),
418                 },
419                 I32 => match endian {
420                     E::Little => enc::xload32le_o32(sink, dst, addr),
421                     E::Big => enc::xload32be_o32(sink, dst, addr),
422                 },
423                 I64 => match endian {
424                     E::Little => enc::xload64le_o32(sink, dst, addr),
425                     E::Big => enc::xload64be_o32(sink, dst, addr),
426                 },
427                 _ => unimplemented!("xload ty={ty:?}"),
428             }
429         }
430 
431         Inst::FLoad {
432             dst,
433             mem,
434             ty,
435             flags,
436         } => {
437             use Endianness as E;
438             assert!(flags.trap_code().is_none());
439             let addr = AddrO32::Base {
440                 addr: mem.get_base_register().unwrap(),
441                 offset: mem.get_offset_with_state(state),
442             };
443             let endian = emit_info.endianness(*flags);
444             match *ty {
445                 F32 => match endian {
446                     E::Little => enc::fload32le_o32(sink, dst, addr),
447                     E::Big => enc::fload32be_o32(sink, dst, addr),
448                 },
449                 F64 => match endian {
450                     E::Little => enc::fload64le_o32(sink, dst, addr),
451                     E::Big => enc::fload64be_o32(sink, dst, addr),
452                 },
453                 _ => unimplemented!("fload ty={ty:?}"),
454             }
455         }
456 
457         Inst::VLoad {
458             dst,
459             mem,
460             ty,
461             flags,
462         } => {
463             assert!(flags.trap_code().is_none());
464             let addr = AddrO32::Base {
465                 addr: mem.get_base_register().unwrap(),
466                 offset: mem.get_offset_with_state(state),
467             };
468             let endian = emit_info.endianness(*flags);
469             assert_eq!(endian, Endianness::Little);
470             assert_eq!(ty.bytes(), 16);
471             enc::vload128le_o32(sink, dst, addr);
472         }
473 
474         Inst::XStore {
475             mem,
476             src,
477             ty,
478             flags,
479         } => {
480             use Endianness as E;
481             assert!(flags.trap_code().is_none());
482             let addr = AddrO32::Base {
483                 addr: mem.get_base_register().unwrap(),
484                 offset: mem.get_offset_with_state(state),
485             };
486             let endian = emit_info.endianness(*flags);
487             match *ty {
488                 I8 => enc::xstore8_o32(sink, addr, src),
489                 I16 => match endian {
490                     E::Little => enc::xstore16le_o32(sink, addr, src),
491                     E::Big => enc::xstore16be_o32(sink, addr, src),
492                 },
493                 I32 => match endian {
494                     E::Little => enc::xstore32le_o32(sink, addr, src),
495                     E::Big => enc::xstore32be_o32(sink, addr, src),
496                 },
497                 I64 => match endian {
498                     E::Little => enc::xstore64le_o32(sink, addr, src),
499                     E::Big => enc::xstore64be_o32(sink, addr, src),
500                 },
501                 _ => unimplemented!("xstore ty={ty:?}"),
502             }
503         }
504 
505         Inst::FStore {
506             mem,
507             src,
508             ty,
509             flags,
510         } => {
511             use Endianness as E;
512             assert!(flags.trap_code().is_none());
513             let addr = AddrO32::Base {
514                 addr: mem.get_base_register().unwrap(),
515                 offset: mem.get_offset_with_state(state),
516             };
517             let endian = emit_info.endianness(*flags);
518             match *ty {
519                 F32 => match endian {
520                     E::Little => enc::fstore32le_o32(sink, addr, src),
521                     E::Big => enc::fstore32be_o32(sink, addr, src),
522                 },
523                 F64 => match endian {
524                     E::Little => enc::fstore64le_o32(sink, addr, src),
525                     E::Big => enc::fstore64be_o32(sink, addr, src),
526                 },
527                 _ => unimplemented!("fstore ty={ty:?}"),
528             }
529         }
530 
531         Inst::VStore {
532             mem,
533             src,
534             ty,
535             flags,
536         } => {
537             assert!(flags.trap_code().is_none());
538             let addr = AddrO32::Base {
539                 addr: mem.get_base_register().unwrap(),
540                 offset: mem.get_offset_with_state(state),
541             };
542             let endian = emit_info.endianness(*flags);
543             assert_eq!(endian, Endianness::Little);
544             assert_eq!(ty.bytes(), 16);
545             enc::vstore128le_o32(sink, addr, src);
546         }
547 
548         Inst::BrTable {
549             idx,
550             default,
551             targets,
552         } => {
553             // Encode the `br_table32` instruction directly which expects the
554             // next `amt` 4-byte integers to all be relative offsets. Each
555             // offset is the pc-relative offset of the branch destination.
556             //
557             // Pulley clamps the branch targets to the `amt` specified so the
558             // final branch target is the default jump target.
559             //
560             // Note that this instruction may have many branch targets so it
561             // manually checks to see if an island is needed. If so we emit a
562             // jump around the island before the `br_table32` itself gets
563             // emitted.
564             let amt = u32::try_from(targets.len() + 1).expect("too many branch targets");
565             let br_table_size = amt * 4 + 6;
566             if sink.island_needed(br_table_size) {
567                 let label = sink.get_label();
568                 <InstAndKind<P>>::from(Inst::Jump { label }).emit(sink, emit_info, state);
569                 sink.emit_island(br_table_size, &mut state.ctrl_plane);
570                 sink.bind_label(label, &mut state.ctrl_plane);
571             }
572             enc::br_table32(sink, *idx, amt);
573             for target in targets.iter() {
574                 let offset = sink.cur_offset();
575                 sink.use_label_at_offset(offset, *target, LabelUse::PcRel);
576                 sink.put4(0);
577             }
578             let offset = sink.cur_offset();
579             sink.use_label_at_offset(offset, *default, LabelUse::PcRel);
580             sink.put4(0);
581 
582             // We manually handled `emit_island` above when dealing with
583             // `island_needed` so update the starting offset to the current
584             // offset so this instruction doesn't accidentally trigger
585             // the assertion that we're always under worst-case-size.
586             *start_offset = sink.cur_offset();
587         }
588 
589         Inst::Raw { raw } => super::generated::emit(raw, sink),
590 
591         Inst::EmitIsland { space_needed } => {
592             if sink.island_needed(*space_needed) {
593                 let label = sink.get_label();
594                 <InstAndKind<P>>::from(Inst::Jump { label }).emit(sink, emit_info, state);
595                 sink.emit_island(space_needed + 8, &mut state.ctrl_plane);
596                 sink.bind_label(label, &mut state.ctrl_plane);
597             }
598         }
599 
600         Inst::LabelAddress { dst, label } => {
601             patch_pc_rel_offset(sink, |sink| enc::xpcadd(sink, dst, 0));
602             let end = sink.cur_offset();
603             sink.use_label_at_offset(end - 4, *label, LabelUse::PcRel);
604         }
605 
606         Inst::SequencePoint { .. } => {
607             // Nothing.
608         }
609     }
610 }
611 
emit_return_call_common_sequence<T, P>( sink: &mut MachBuffer<InstAndKind<P>>, emit_info: &EmitInfo, state: &mut EmitState<P>, info: &ReturnCallInfo<T>, ) where P: PulleyTargetKind,612 fn emit_return_call_common_sequence<T, P>(
613     sink: &mut MachBuffer<InstAndKind<P>>,
614     emit_info: &EmitInfo,
615     state: &mut EmitState<P>,
616     info: &ReturnCallInfo<T>,
617 ) where
618     P: PulleyTargetKind,
619 {
620     // The return call sequence can potentially emit a lot of instructions, so
621     // lets emit an island here if we need it.
622     //
623     // It is difficult to calculate exactly how many instructions are going to
624     // be emitted, so we calculate it by emitting it into a disposable buffer,
625     // and then checking how many instructions were actually emitted.
626     let mut buffer = MachBuffer::new();
627     let mut fake_emit_state = state.clone();
628 
629     return_call_emit_impl(&mut buffer, emit_info, &mut fake_emit_state, info);
630 
631     // Finalize the buffer and get the number of bytes emitted.
632     let buffer = buffer.finish(&Default::default(), &mut Default::default());
633     let length = buffer.data().len() as u32;
634 
635     // And now emit the island inline with this instruction.
636     if sink.island_needed(length) {
637         let jump_around_label = sink.get_label();
638         <InstAndKind<P>>::gen_jump(jump_around_label).emit(sink, emit_info, state);
639         sink.emit_island(length + 4, &mut state.ctrl_plane);
640         sink.bind_label(jump_around_label, &mut state.ctrl_plane);
641     }
642 
643     // Now that we're done, emit the *actual* return sequence.
644     return_call_emit_impl(sink, emit_info, state, info);
645 }
646 
647 /// This should not be called directly, Instead prefer to call [emit_return_call_common_sequence].
return_call_emit_impl<T, P>( sink: &mut MachBuffer<InstAndKind<P>>, emit_info: &EmitInfo, state: &mut EmitState<P>, info: &ReturnCallInfo<T>, ) where P: PulleyTargetKind,648 fn return_call_emit_impl<T, P>(
649     sink: &mut MachBuffer<InstAndKind<P>>,
650     emit_info: &EmitInfo,
651     state: &mut EmitState<P>,
652     info: &ReturnCallInfo<T>,
653 ) where
654     P: PulleyTargetKind,
655 {
656     let epilogue = <PulleyMachineDeps<P>>::gen_epilogue_frame_restore(
657         emit_info.call_conv,
658         &emit_info.shared_flags,
659         &emit_info.isa_flags,
660         &state.frame_layout,
661     );
662 
663     for inst in epilogue {
664         inst.emit(sink, emit_info, state);
665     }
666 
667     // Now that `sp` is restored to what it was on function entry it may need to
668     // be adjusted if the stack arguments of our own function differ from the
669     // stack arguments of the callee. Perform any necessary adjustment here.
670     //
671     // Note that this means that there's a brief window where stack arguments
672     // might be below `sp` in the case that the callee has more stack arguments
673     // than ourselves. That's in theory ok though as we're inventing the pulley
674     // ABI and nothing like async signals are happening that we have to worry
675     // about.
676     let incoming_args_diff =
677         i64::from(state.frame_layout().tail_args_size - info.new_stack_arg_size);
678 
679     if incoming_args_diff != 0 {
680         let amt = i32::try_from(incoming_args_diff).unwrap();
681         for inst in PulleyMachineDeps::<P>::gen_sp_reg_adjust(amt) {
682             inst.emit(sink, emit_info, state);
683         }
684     }
685 }
686 
687 /// Invokes `f` with `sink` and assumes that a single instruction is emitted
688 /// which ends with a Pulley `PcRelOffset`.
689 ///
690 /// The offset at that location is patched to include the size of the
691 /// instruction before the relative offset since relocations will be applied to
692 /// the address of the offset and added to the contents at the offset. The
693 /// Pulley interpreter, however, will calculate the offset from the start of the
694 /// instruction, so this extra offset is required.
patch_pc_rel_offset<P>( sink: &mut MachBuffer<InstAndKind<P>>, f: impl FnOnce(&mut MachBuffer<InstAndKind<P>>), ) where P: PulleyTargetKind,695 fn patch_pc_rel_offset<P>(
696     sink: &mut MachBuffer<InstAndKind<P>>,
697     f: impl FnOnce(&mut MachBuffer<InstAndKind<P>>),
698 ) where
699     P: PulleyTargetKind,
700 {
701     let patch = sink.start_patchable();
702     let start = sink.cur_offset();
703     f(sink);
704     let end = sink.cur_offset();
705     let region = sink.end_patchable(patch).patch(sink);
706     let chunk = region.last_chunk_mut::<4>().unwrap();
707     assert_eq!(*chunk, [0, 0, 0, 0]);
708     *chunk = (end - start - 4).to_le_bytes();
709 }
710