Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs
3103 views
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 {
20
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
32
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
{
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
{
62
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
71
fn pre_safepoint(&mut self, user_stack_map: Option<ir::UserStackMap>) {
72
self.user_stack_map = user_stack_map;
73
}
74
75
fn ctrl_plane_mut(&mut self) -> &mut ControlPlane {
76
&mut self.ctrl_plane
77
}
78
79
fn take_ctrl_plane(self) -> ControlPlane {
80
self.ctrl_plane
81
}
82
83
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
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
113
fn pretty_print_inst(&self, state: &mut Self::State) -> String {
114
self.print_with_state(state)
115
}
116
}
117
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
// Pseduo-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
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].
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.
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
711