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