Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/pulley_shared/abi.rs
3092 views
1
//! Implementation of a standard Pulley ABI.
2
3
use super::{PulleyFlags, PulleyTargetKind, inst::*};
4
use crate::isa::pulley_shared::PointerWidth;
5
use crate::{
6
CodegenResult,
7
ir::{self, MemFlags, Signature, types::*},
8
isa,
9
machinst::*,
10
settings,
11
};
12
use alloc::borrow::ToOwned;
13
use alloc::vec::Vec;
14
use core::marker::PhantomData;
15
use cranelift_bitset::ScalarBitSet;
16
use regalloc2::{MachineEnv, PReg, PRegSet};
17
use smallvec::{SmallVec, smallvec};
18
use std::sync::OnceLock;
19
20
/// Support for the Pulley ABI from the callee side (within a function body).
21
pub(crate) type PulleyCallee<P> = Callee<PulleyMachineDeps<P>>;
22
23
/// Pulley-specific ABI behavior. This struct just serves as an implementation
24
/// point for the trait; it is never actually instantiated.
25
pub struct PulleyMachineDeps<P>
26
where
27
P: PulleyTargetKind,
28
{
29
_phantom: PhantomData<P>,
30
}
31
32
impl<P> ABIMachineSpec for PulleyMachineDeps<P>
33
where
34
P: PulleyTargetKind,
35
{
36
type I = InstAndKind<P>;
37
type F = PulleyFlags;
38
39
/// This is the limit for the size of argument and return-value areas on the
40
/// stack. We place a reasonable limit here to avoid integer overflow issues
41
/// with 32-bit arithmetic: for now, 128 MB.
42
const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
43
44
fn word_bits() -> u32 {
45
P::pointer_width().bits().into()
46
}
47
48
/// Return required stack alignment in bytes.
49
fn stack_align(_call_conv: isa::CallConv) -> u32 {
50
16
51
}
52
53
fn compute_arg_locs(
54
call_conv: isa::CallConv,
55
flags: &settings::Flags,
56
params: &[ir::AbiParam],
57
args_or_rets: ArgsOrRets,
58
add_ret_area_ptr: bool,
59
mut args: ArgsAccumulator,
60
) -> CodegenResult<(u32, Option<usize>)> {
61
// NB: make sure this method stays in sync with
62
// `cranelift_pulley::interp::Vm::call`.
63
//
64
// In general we use the first half of all register banks as argument
65
// passing registers because, well, why not for now. Currently the only
66
// exception is x15 which is reserved as a single caller-saved register
67
// not used for arguments. This is used in `ReturnCallIndirect` to hold
68
// the location of where we're jumping to.
69
70
let x_end = 14;
71
let f_end = 15;
72
let v_end = 15;
73
74
let mut next_x_reg = 0;
75
let mut next_f_reg = 0;
76
let mut next_v_reg = 0;
77
let mut next_stack: u32 = 0;
78
79
let ret_area_ptr = if add_ret_area_ptr {
80
debug_assert_eq!(args_or_rets, ArgsOrRets::Args);
81
next_x_reg += 1;
82
Some(ABIArg::reg(
83
x_reg(next_x_reg - 1).to_real_reg().unwrap(),
84
I64,
85
ir::ArgumentExtension::None,
86
ir::ArgumentPurpose::Normal,
87
))
88
} else {
89
None
90
};
91
92
for param in params {
93
// Find the regclass(es) of the register(s) used to store a value of
94
// this type.
95
let (rcs, reg_tys) = Self::I::rc_for_type(param.value_type)?;
96
97
let mut slots = ABIArgSlotVec::new();
98
for (rc, reg_ty) in rcs.iter().zip(reg_tys.iter()) {
99
let next_reg = if (next_x_reg <= x_end) && *rc == RegClass::Int {
100
let x = Some(x_reg(next_x_reg));
101
next_x_reg += 1;
102
x
103
} else if (next_f_reg <= f_end) && *rc == RegClass::Float {
104
let f = Some(f_reg(next_f_reg));
105
next_f_reg += 1;
106
f
107
} else if (next_v_reg <= v_end) && *rc == RegClass::Vector {
108
let v = Some(v_reg(next_v_reg));
109
next_v_reg += 1;
110
v
111
} else {
112
None
113
};
114
115
if let Some(reg) = next_reg {
116
slots.push(ABIArgSlot::Reg {
117
reg: reg.to_real_reg().unwrap(),
118
ty: *reg_ty,
119
extension: param.extension,
120
});
121
} else {
122
if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {
123
return Err(crate::CodegenError::Unsupported(
124
"Too many return values to fit in registers. \
125
Use a StructReturn argument instead. (#9510)"
126
.to_owned(),
127
));
128
}
129
130
// Compute size and 16-byte stack alignment happens
131
// separately after all args.
132
let size = reg_ty.bits() / 8;
133
let size = core::cmp::max(size, 8);
134
135
// Align.
136
debug_assert!(size.is_power_of_two());
137
next_stack = align_to(next_stack, size);
138
139
slots.push(ABIArgSlot::Stack {
140
offset: i64::from(next_stack),
141
ty: *reg_ty,
142
extension: param.extension,
143
});
144
145
next_stack += size;
146
}
147
}
148
149
args.push(ABIArg::Slots {
150
slots,
151
purpose: param.purpose,
152
});
153
}
154
155
let pos = if let Some(ret_area_ptr) = ret_area_ptr {
156
args.push_non_formal(ret_area_ptr);
157
Some(args.args().len() - 1)
158
} else {
159
None
160
};
161
162
next_stack = align_to(next_stack, Self::stack_align(call_conv));
163
164
Ok((next_stack, pos))
165
}
166
167
fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Self::I {
168
let mut flags = MemFlags::trusted();
169
// Stack loads/stores of vectors always use little-endianess to avoid
170
// implementing a byte-swap of vectors on big-endian platforms.
171
if ty.is_vector() {
172
flags.set_endianness(ir::Endianness::Little);
173
}
174
Inst::gen_load(into_reg, mem.into(), ty, flags).into()
175
}
176
177
fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Self::I {
178
let mut flags = MemFlags::trusted();
179
// Stack loads/stores of vectors always use little-endianess to avoid
180
// implementing a byte-swap of vectors on big-endian platforms.
181
if ty.is_vector() {
182
flags.set_endianness(ir::Endianness::Little);
183
}
184
Inst::gen_store(mem.into(), from_reg, ty, flags).into()
185
}
186
187
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self::I {
188
Self::I::gen_move(to_reg, from_reg, ty)
189
}
190
191
fn gen_extend(
192
dst: Writable<Reg>,
193
src: Reg,
194
signed: bool,
195
from_bits: u8,
196
to_bits: u8,
197
) -> Self::I {
198
assert!(from_bits < to_bits);
199
let src = XReg::new(src).unwrap();
200
let dst = dst.try_into().unwrap();
201
match (signed, from_bits) {
202
(true, 8) => RawInst::Sext8 { dst, src }.into(),
203
(true, 16) => RawInst::Sext16 { dst, src }.into(),
204
(true, 32) => RawInst::Sext32 { dst, src }.into(),
205
(false, 8) => RawInst::Zext8 { dst, src }.into(),
206
(false, 16) => RawInst::Zext16 { dst, src }.into(),
207
(false, 32) => RawInst::Zext32 { dst, src }.into(),
208
_ => unimplemented!("extend {from_bits} to {to_bits} as signed? {signed}"),
209
}
210
}
211
212
fn get_ext_mode(
213
_call_conv: isa::CallConv,
214
specified: ir::ArgumentExtension,
215
) -> ir::ArgumentExtension {
216
specified
217
}
218
219
fn gen_args(args: Vec<ArgPair>) -> Self::I {
220
Inst::Args { args }.into()
221
}
222
223
fn gen_rets(rets: Vec<RetPair>) -> Self::I {
224
Inst::Rets { rets }.into()
225
}
226
227
fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {
228
spilltmp_reg()
229
}
230
231
fn gen_add_imm(
232
_call_conv: isa::CallConv,
233
into_reg: Writable<Reg>,
234
from_reg: Reg,
235
imm: u32,
236
) -> SmallInstVec<Self::I> {
237
let dst = into_reg.try_into().unwrap();
238
let imm = imm as i32;
239
smallvec![
240
RawInst::Xconst32 { dst, imm }.into(),
241
RawInst::Xadd32 {
242
dst,
243
src1: from_reg.try_into().unwrap(),
244
src2: dst.to_reg(),
245
}
246
.into()
247
]
248
}
249
250
fn gen_stack_lower_bound_trap(_limit_reg: Reg) -> SmallInstVec<Self::I> {
251
unimplemented!("pulley shouldn't need stack bound checks")
252
}
253
254
fn gen_get_stack_addr(mem: StackAMode, dst: Writable<Reg>) -> Self::I {
255
let dst = dst.to_reg();
256
let dst = XReg::new(dst).unwrap();
257
let dst = WritableXReg::from_reg(dst);
258
let mem = mem.into();
259
Inst::LoadAddr { dst, mem }.into()
260
}
261
262
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Self::I {
263
let base = XReg::try_from(base).unwrap();
264
let mem = Amode::RegOffset { base, offset };
265
Inst::gen_load(into_reg, mem, ty, MemFlags::trusted()).into()
266
}
267
268
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Self::I {
269
let base = XReg::try_from(base).unwrap();
270
let mem = Amode::RegOffset { base, offset };
271
Inst::gen_store(mem, from_reg, ty, MemFlags::trusted()).into()
272
}
273
274
fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Self::I> {
275
if amount == 0 {
276
return smallvec![];
277
}
278
279
let inst = if amount < 0 {
280
let amount = amount.checked_neg().unwrap();
281
if let Ok(amt) = u32::try_from(amount) {
282
RawInst::StackAlloc32 { amt }
283
} else {
284
unreachable!()
285
}
286
} else {
287
if let Ok(amt) = u32::try_from(amount) {
288
RawInst::StackFree32 { amt }
289
} else {
290
unreachable!()
291
}
292
};
293
smallvec![inst.into()]
294
}
295
296
/// Generates the entire prologue for the function.
297
///
298
/// Note that this is different from other backends where it's not spread
299
/// out among a few individual functions. That's because the goal here is to
300
/// generate a single macro-instruction for the entire prologue in the most
301
/// common cases and we don't want to spread the logic over multiple
302
/// functions.
303
///
304
/// The general machinst methods are split to accommodate stack checks and
305
/// things like stack probes, all of which are empty on Pulley because
306
/// Pulley has its own stack check mechanism.
307
fn gen_prologue_frame_setup(
308
_call_conv: isa::CallConv,
309
_flags: &settings::Flags,
310
_isa_flags: &PulleyFlags,
311
frame_layout: &FrameLayout,
312
) -> SmallInstVec<Self::I> {
313
let mut insts = SmallVec::new();
314
315
let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;
316
if incoming_args_diff > 0 {
317
// Decrement SP by the amount of additional incoming argument space
318
// we need
319
insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));
320
}
321
322
let style = frame_layout.pulley_frame_style();
323
324
match &style {
325
FrameStyle::None => {}
326
FrameStyle::PulleyBasicSetup { frame_size } => {
327
insts.push(RawInst::PushFrame.into());
328
insts.extend(Self::gen_sp_reg_adjust(
329
-i32::try_from(*frame_size).unwrap(),
330
));
331
}
332
FrameStyle::PulleySetupAndSaveClobbers {
333
frame_size,
334
saved_by_pulley,
335
} => insts.push(
336
RawInst::PushFrameSave {
337
amt: *frame_size,
338
regs: pulley_interpreter::UpperRegSet::from_bitset(*saved_by_pulley),
339
}
340
.into(),
341
),
342
FrameStyle::Manual { frame_size } => insts.extend(Self::gen_sp_reg_adjust(
343
-i32::try_from(*frame_size).unwrap(),
344
)),
345
}
346
347
for (offset, ty, reg) in frame_layout.manually_managed_clobbers(&style) {
348
insts.push(
349
Inst::gen_store(Amode::SpOffset { offset }, reg, ty, MemFlags::trusted()).into(),
350
);
351
}
352
353
insts
354
}
355
356
/// Reverse of `gen_prologue_frame_setup`.
357
fn gen_epilogue_frame_restore(
358
_call_conv: isa::CallConv,
359
_flags: &settings::Flags,
360
_isa_flags: &PulleyFlags,
361
frame_layout: &FrameLayout,
362
) -> SmallInstVec<Self::I> {
363
let mut insts = SmallVec::new();
364
365
let style = frame_layout.pulley_frame_style();
366
367
// Restore clobbered registers that are manually managed in Cranelift.
368
for (offset, ty, reg) in frame_layout.manually_managed_clobbers(&style) {
369
insts.push(
370
Inst::gen_load(
371
Writable::from_reg(reg),
372
Amode::SpOffset { offset },
373
ty,
374
MemFlags::trusted(),
375
)
376
.into(),
377
);
378
}
379
380
// Perform the inverse of `gen_prologue_frame_setup`.
381
match &style {
382
FrameStyle::None => {}
383
FrameStyle::PulleyBasicSetup { frame_size } => {
384
insts.extend(Self::gen_sp_reg_adjust(i32::try_from(*frame_size).unwrap()));
385
insts.push(RawInst::PopFrame.into());
386
}
387
FrameStyle::PulleySetupAndSaveClobbers {
388
frame_size,
389
saved_by_pulley,
390
} => insts.push(
391
RawInst::PopFrameRestore {
392
amt: *frame_size,
393
regs: pulley_interpreter::UpperRegSet::from_bitset(*saved_by_pulley),
394
}
395
.into(),
396
),
397
FrameStyle::Manual { frame_size } => {
398
insts.extend(Self::gen_sp_reg_adjust(i32::try_from(*frame_size).unwrap()))
399
}
400
}
401
402
insts
403
}
404
405
fn gen_return(
406
call_conv: isa::CallConv,
407
_isa_flags: &PulleyFlags,
408
frame_layout: &FrameLayout,
409
) -> SmallInstVec<Self::I> {
410
let mut insts = SmallVec::new();
411
412
// Handle final stack adjustments for the tail-call ABI.
413
if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 {
414
insts.extend(Self::gen_sp_reg_adjust(
415
frame_layout.tail_args_size.try_into().unwrap(),
416
));
417
}
418
insts.push(RawInst::Ret {}.into());
419
420
insts
421
}
422
423
fn gen_probestack(_insts: &mut SmallInstVec<Self::I>, _frame_size: u32) {
424
// Pulley doesn't implement stack probes since all stack pointer
425
// decrements are checked already.
426
}
427
428
fn gen_clobber_save(
429
_call_conv: isa::CallConv,
430
_flags: &settings::Flags,
431
_frame_layout: &FrameLayout,
432
) -> SmallVec<[Self::I; 16]> {
433
// Note that this is intentionally empty because everything necessary
434
// was already done in `gen_prologue_frame_setup`.
435
SmallVec::new()
436
}
437
438
fn gen_clobber_restore(
439
_call_conv: isa::CallConv,
440
_flags: &settings::Flags,
441
_frame_layout: &FrameLayout,
442
) -> SmallVec<[Self::I; 16]> {
443
// Intentionally empty as restores happen for Pulley in `gen_return`.
444
SmallVec::new()
445
}
446
447
fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
448
_call_conv: isa::CallConv,
449
_dst: Reg,
450
_src: Reg,
451
_size: usize,
452
_alloc_tmp: F,
453
) -> SmallVec<[Self::I; 8]> {
454
todo!()
455
}
456
457
fn get_number_of_spillslots_for_value(
458
rc: RegClass,
459
_target_vector_bytes: u32,
460
_isa_flags: &PulleyFlags,
461
) -> u32 {
462
// Spill slots are the size of a "word" or a pointer, but Pulley
463
// registers are 8-byte for integers/floats regardless of pointer size.
464
// Calculate the number of slots necessary to store 8 bytes.
465
let slots_for_8bytes = match P::pointer_width() {
466
PointerWidth::PointerWidth32 => 2,
467
PointerWidth::PointerWidth64 => 1,
468
};
469
match rc {
470
// Int/float registers are 8-bytes
471
RegClass::Int | RegClass::Float => slots_for_8bytes,
472
// Vector registers are 16 bytes
473
RegClass::Vector => 2 * slots_for_8bytes,
474
}
475
}
476
477
fn get_machine_env(_flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
478
static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
479
MACHINE_ENV.get_or_init(create_reg_environment)
480
}
481
482
fn get_regs_clobbered_by_call(
483
call_conv_of_callee: isa::CallConv,
484
is_exception: bool,
485
) -> PRegSet {
486
if is_exception {
487
ALL_CLOBBERS
488
} else if call_conv_of_callee == isa::CallConv::PreserveAll {
489
NO_CLOBBERS
490
} else {
491
DEFAULT_CLOBBERS
492
}
493
}
494
495
fn compute_frame_layout(
496
call_conv: isa::CallConv,
497
flags: &settings::Flags,
498
_sig: &Signature,
499
regs: &[Writable<RealReg>],
500
function_calls: FunctionCalls,
501
incoming_args_size: u32,
502
tail_args_size: u32,
503
stackslots_size: u32,
504
fixed_frame_storage_size: u32,
505
outgoing_args_size: u32,
506
) -> FrameLayout {
507
let is_callee_save = |reg: &Writable<RealReg>| match call_conv {
508
isa::CallConv::PreserveAll => true,
509
_ => DEFAULT_CALLEE_SAVES.contains(reg.to_reg().into()),
510
};
511
let mut regs: Vec<Writable<RealReg>> =
512
regs.iter().cloned().filter(is_callee_save).collect();
513
514
regs.sort_unstable();
515
516
// Compute clobber size.
517
let clobber_size = compute_clobber_size(&regs);
518
519
// Compute linkage frame size.
520
let setup_area_size = if flags.preserve_frame_pointers()
521
|| function_calls != FunctionCalls::None
522
// The function arguments that are passed on the stack are addressed
523
// relative to the Frame Pointer.
524
|| incoming_args_size > 0
525
|| clobber_size > 0
526
|| fixed_frame_storage_size > 0
527
{
528
P::pointer_width().bytes() * 2 // FP, LR
529
} else {
530
0
531
};
532
533
FrameLayout {
534
word_bytes: u32::from(P::pointer_width().bytes()),
535
incoming_args_size,
536
tail_args_size,
537
setup_area_size: setup_area_size.into(),
538
clobber_size,
539
fixed_frame_storage_size,
540
stackslots_size,
541
outgoing_args_size,
542
clobbered_callee_saves: regs,
543
function_calls,
544
}
545
}
546
547
fn gen_inline_probestack(
548
_insts: &mut SmallInstVec<Self::I>,
549
_call_conv: isa::CallConv,
550
_frame_size: u32,
551
_guard_size: u32,
552
) {
553
// Pulley doesn't need inline probestacks because it always checks stack
554
// decrements.
555
}
556
557
fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
558
// Use x15 as a temp if needed: clobbered, not a
559
// retval.
560
Writable::from_reg(regs::x_reg(15))
561
}
562
563
fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
564
const PAYLOAD_REGS: &'static [Reg] = &[
565
Reg::from_real_reg(regs::px_reg(0)),
566
Reg::from_real_reg(regs::px_reg(1)),
567
];
568
match call_conv {
569
isa::CallConv::SystemV | isa::CallConv::Tail | isa::CallConv::PreserveAll => {
570
PAYLOAD_REGS
571
}
572
isa::CallConv::Fast
573
| isa::CallConv::WindowsFastcall
574
| isa::CallConv::AppleAarch64
575
| isa::CallConv::Probestack
576
| isa::CallConv::Winch => &[],
577
}
578
}
579
}
580
581
/// Different styles of management of fp/lr and clobbered registers.
582
///
583
/// This helps decide, depending on Cranelift settings and frame layout, what
584
/// macro instruction is used to setup the pulley frame.
585
enum FrameStyle {
586
/// No management is happening, fp/lr aren't saved by Pulley or Cranelift.
587
/// No stack is being allocated either.
588
None,
589
590
/// Pulley saves the fp/lr combo and then stack adjustments/clobbers are
591
/// handled manually.
592
PulleyBasicSetup { frame_size: u32 },
593
594
/// Pulley is managing the fp/lr combo, the stack size, and clobbered
595
/// X-class registers.
596
///
597
/// Note that `saved_by_pulley` is not the exhaustive set of clobbered
598
/// registers. It's only those that are part of the `PushFrameSave`
599
/// instruction.
600
PulleySetupAndSaveClobbers {
601
/// The size of the frame, including clobbers, that's being allocated.
602
frame_size: u16,
603
/// Registers that pulley is saving/restoring.
604
saved_by_pulley: ScalarBitSet<u16>,
605
},
606
607
/// Cranelift is manually managing everything, both clobbers and stack
608
/// increments/decrements.
609
///
610
/// Note that fp/lr are not saved in this mode.
611
Manual {
612
/// The size of the stack being allocated.
613
frame_size: u32,
614
},
615
}
616
617
/// Pulley-specific helpers when dealing with ABI code.
618
impl FrameLayout {
619
/// Whether or not this frame saves fp/lr.
620
fn setup_frame(&self) -> bool {
621
self.setup_area_size > 0
622
}
623
624
/// Returns the stack size allocated by this function, excluding incoming
625
/// tail args or the optional "setup area" of fp/lr.
626
fn stack_size(&self) -> u32 {
627
self.clobber_size + self.fixed_frame_storage_size + self.outgoing_args_size
628
}
629
630
/// Returns the style of frame being used for this function.
631
///
632
/// See `FrameStyle` for more information.
633
fn pulley_frame_style(&self) -> FrameStyle {
634
let saved_by_pulley = self.clobbered_xregs_saved_by_pulley();
635
match (
636
self.stack_size(),
637
self.setup_frame(),
638
saved_by_pulley.is_empty(),
639
) {
640
// No stack allocated, not saving fp/lr, no clobbers, nothing to do
641
(0, false, true) => FrameStyle::None,
642
643
// No stack allocated, saving fp/lr, no clobbers, so this is
644
// pulley-managed via push/pop_frame.
645
(0, true, true) => FrameStyle::PulleyBasicSetup { frame_size: 0 },
646
647
// Some stack is being allocated and pulley is managing fp/lr. Let
648
// pulley manage clobbered registers as well, regardless if they're
649
// present or not.
650
//
651
// If the stack is too large then `PulleyBasicSetup` is used
652
// otherwise we'll be pushing `PushFrameSave` and `PopFrameRestore`.
653
(frame_size, true, _) => match frame_size.try_into() {
654
Ok(frame_size) => FrameStyle::PulleySetupAndSaveClobbers {
655
frame_size,
656
saved_by_pulley,
657
},
658
Err(_) => FrameStyle::PulleyBasicSetup { frame_size },
659
},
660
661
// Some stack is being allocated, but pulley isn't managing fp/lr,
662
// so we're manually doing everything.
663
(frame_size, false, true) => FrameStyle::Manual { frame_size },
664
665
// If there's no frame setup and there's clobbered registers this
666
// technically should have already hit a case above, so panic here.
667
(_, false, false) => unreachable!(),
668
}
669
}
670
671
/// Returns the set of clobbered registers that Pulley is managing via its
672
/// macro instructions rather than the generated code.
673
fn clobbered_xregs_saved_by_pulley(&self) -> ScalarBitSet<u16> {
674
let mut clobbered: ScalarBitSet<u16> = ScalarBitSet::new();
675
// Pulley only manages clobbers if it's also managing fp/lr.
676
if !self.setup_frame() {
677
return clobbered;
678
}
679
let mut found_manual_clobber = false;
680
for reg in self.clobbered_callee_saves.iter() {
681
let r_reg = reg.to_reg();
682
// Pulley can only manage clobbers of integer registers at this
683
// time, float registers are managed manually.
684
//
685
// Also assert that all pulley-managed clobbers come first,
686
// otherwise the loop below in `manually_managed_clobbers` is
687
// incorrect.
688
if r_reg.class() == RegClass::Int {
689
assert!(!found_manual_clobber);
690
if let Some(offset) = r_reg.hw_enc().checked_sub(16) {
691
clobbered.insert(offset);
692
}
693
} else {
694
found_manual_clobber = true;
695
}
696
}
697
clobbered
698
}
699
700
/// Returns an iterator over the clobbers that Cranelift is managing, not
701
/// Pulley.
702
///
703
/// If this frame has clobbers then they're either saved by Pulley with
704
/// `FrameStyle::PulleySetupAndSaveClobbers`. Cranelift might need to manage
705
/// these registers depending on Cranelift settings. Cranelift also always
706
/// manages floating-point registers.
707
fn manually_managed_clobbers<'a>(
708
&'a self,
709
style: &'a FrameStyle,
710
) -> impl Iterator<Item = (i32, Type, Reg)> + 'a {
711
let mut offset = self.stack_size();
712
self.clobbered_callee_saves.iter().filter_map(move |reg| {
713
// Allocate space for this clobber no matter what. If pulley is
714
// managing this then we're just accounting for the pulley-saved
715
// registers as well. Note that all pulley-managed registers come
716
// first in the list here.
717
offset -= 8;
718
let r_reg = reg.to_reg();
719
let ty = match r_reg.class() {
720
RegClass::Int => {
721
// If this register is saved by pulley, skip this clobber.
722
if let FrameStyle::PulleySetupAndSaveClobbers {
723
saved_by_pulley, ..
724
} = style
725
{
726
if let Some(reg) = r_reg.hw_enc().checked_sub(16) {
727
if saved_by_pulley.contains(reg) {
728
return None;
729
}
730
}
731
}
732
I64
733
}
734
RegClass::Float => F64,
735
RegClass::Vector => I8X16,
736
};
737
let offset = i32::try_from(offset).unwrap();
738
Some((offset, ty, Reg::from(reg.to_reg())))
739
})
740
}
741
}
742
743
const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()
744
// Integer registers.
745
.with(px_reg(16))
746
.with(px_reg(17))
747
.with(px_reg(18))
748
.with(px_reg(19))
749
.with(px_reg(20))
750
.with(px_reg(21))
751
.with(px_reg(22))
752
.with(px_reg(23))
753
.with(px_reg(24))
754
.with(px_reg(25))
755
.with(px_reg(26))
756
.with(px_reg(27))
757
.with(px_reg(28))
758
.with(px_reg(29))
759
.with(px_reg(30))
760
.with(px_reg(31))
761
// Note: no float/vector registers are callee-saved.
762
;
763
764
fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
765
let mut clobbered_size = 0;
766
for reg in clobbers {
767
match reg.to_reg().class() {
768
RegClass::Int => {
769
clobbered_size += 8;
770
}
771
RegClass::Float => {
772
clobbered_size += 8;
773
}
774
RegClass::Vector => {
775
// No alignment concerns: the Pulley virtual CPU
776
// supports unaligned vector load/stores.
777
clobbered_size += 16;
778
}
779
}
780
}
781
align_to(clobbered_size, 16)
782
}
783
784
const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()
785
// Integer registers: the first 16 get clobbered.
786
.with(px_reg(0))
787
.with(px_reg(1))
788
.with(px_reg(2))
789
.with(px_reg(3))
790
.with(px_reg(4))
791
.with(px_reg(5))
792
.with(px_reg(6))
793
.with(px_reg(7))
794
.with(px_reg(8))
795
.with(px_reg(9))
796
.with(px_reg(10))
797
.with(px_reg(11))
798
.with(px_reg(12))
799
.with(px_reg(13))
800
.with(px_reg(14))
801
.with(px_reg(15))
802
// All float registers get clobbered.
803
.with(pf_reg(0))
804
.with(pf_reg(1))
805
.with(pf_reg(2))
806
.with(pf_reg(3))
807
.with(pf_reg(4))
808
.with(pf_reg(5))
809
.with(pf_reg(6))
810
.with(pf_reg(7))
811
.with(pf_reg(8))
812
.with(pf_reg(9))
813
.with(pf_reg(10))
814
.with(pf_reg(11))
815
.with(pf_reg(12))
816
.with(pf_reg(13))
817
.with(pf_reg(14))
818
.with(pf_reg(15))
819
.with(pf_reg(16))
820
.with(pf_reg(17))
821
.with(pf_reg(18))
822
.with(pf_reg(19))
823
.with(pf_reg(20))
824
.with(pf_reg(21))
825
.with(pf_reg(22))
826
.with(pf_reg(23))
827
.with(pf_reg(24))
828
.with(pf_reg(25))
829
.with(pf_reg(26))
830
.with(pf_reg(27))
831
.with(pf_reg(28))
832
.with(pf_reg(29))
833
.with(pf_reg(30))
834
.with(pf_reg(31))
835
// All vector registers get clobbered.
836
.with(pv_reg(0))
837
.with(pv_reg(1))
838
.with(pv_reg(2))
839
.with(pv_reg(3))
840
.with(pv_reg(4))
841
.with(pv_reg(5))
842
.with(pv_reg(6))
843
.with(pv_reg(7))
844
.with(pv_reg(8))
845
.with(pv_reg(9))
846
.with(pv_reg(10))
847
.with(pv_reg(11))
848
.with(pv_reg(12))
849
.with(pv_reg(13))
850
.with(pv_reg(14))
851
.with(pv_reg(15))
852
.with(pv_reg(16))
853
.with(pv_reg(17))
854
.with(pv_reg(18))
855
.with(pv_reg(19))
856
.with(pv_reg(20))
857
.with(pv_reg(21))
858
.with(pv_reg(22))
859
.with(pv_reg(23))
860
.with(pv_reg(24))
861
.with(pv_reg(25))
862
.with(pv_reg(26))
863
.with(pv_reg(27))
864
.with(pv_reg(28))
865
.with(pv_reg(29))
866
.with(pv_reg(30))
867
.with(pv_reg(31));
868
869
const ALL_CLOBBERS: PRegSet = PRegSet::empty()
870
.with(px_reg(0))
871
.with(px_reg(1))
872
.with(px_reg(2))
873
.with(px_reg(3))
874
.with(px_reg(4))
875
.with(px_reg(5))
876
.with(px_reg(6))
877
.with(px_reg(7))
878
.with(px_reg(8))
879
.with(px_reg(9))
880
.with(px_reg(10))
881
.with(px_reg(11))
882
.with(px_reg(12))
883
.with(px_reg(13))
884
.with(px_reg(14))
885
.with(px_reg(15))
886
.with(px_reg(16))
887
.with(px_reg(17))
888
.with(px_reg(18))
889
.with(px_reg(19))
890
.with(px_reg(20))
891
.with(px_reg(21))
892
.with(px_reg(22))
893
.with(px_reg(23))
894
.with(px_reg(24))
895
.with(px_reg(25))
896
.with(px_reg(26))
897
.with(px_reg(27))
898
.with(px_reg(28))
899
.with(px_reg(29))
900
.with(px_reg(30))
901
.with(px_reg(31))
902
.with(pf_reg(0))
903
.with(pf_reg(1))
904
.with(pf_reg(2))
905
.with(pf_reg(3))
906
.with(pf_reg(4))
907
.with(pf_reg(5))
908
.with(pf_reg(6))
909
.with(pf_reg(7))
910
.with(pf_reg(8))
911
.with(pf_reg(9))
912
.with(pf_reg(10))
913
.with(pf_reg(11))
914
.with(pf_reg(12))
915
.with(pf_reg(13))
916
.with(pf_reg(14))
917
.with(pf_reg(15))
918
.with(pf_reg(16))
919
.with(pf_reg(17))
920
.with(pf_reg(18))
921
.with(pf_reg(19))
922
.with(pf_reg(20))
923
.with(pf_reg(21))
924
.with(pf_reg(22))
925
.with(pf_reg(23))
926
.with(pf_reg(24))
927
.with(pf_reg(25))
928
.with(pf_reg(26))
929
.with(pf_reg(27))
930
.with(pf_reg(28))
931
.with(pf_reg(29))
932
.with(pf_reg(30))
933
.with(pf_reg(31))
934
.with(pv_reg(0))
935
.with(pv_reg(1))
936
.with(pv_reg(2))
937
.with(pv_reg(3))
938
.with(pv_reg(4))
939
.with(pv_reg(5))
940
.with(pv_reg(6))
941
.with(pv_reg(7))
942
.with(pv_reg(8))
943
.with(pv_reg(9))
944
.with(pv_reg(10))
945
.with(pv_reg(11))
946
.with(pv_reg(12))
947
.with(pv_reg(13))
948
.with(pv_reg(14))
949
.with(pv_reg(15))
950
.with(pv_reg(16))
951
.with(pv_reg(17))
952
.with(pv_reg(18))
953
.with(pv_reg(19))
954
.with(pv_reg(20))
955
.with(pv_reg(21))
956
.with(pv_reg(22))
957
.with(pv_reg(23))
958
.with(pv_reg(24))
959
.with(pv_reg(25))
960
.with(pv_reg(26))
961
.with(pv_reg(27))
962
.with(pv_reg(28))
963
.with(pv_reg(29))
964
.with(pv_reg(30))
965
.with(pv_reg(31));
966
967
const NO_CLOBBERS: PRegSet = PRegSet::empty();
968
969
fn create_reg_environment() -> MachineEnv {
970
// Prefer caller-saved registers over callee-saved registers, because that
971
// way we don't need to emit code to save and restore them if we don't
972
// mutate them.
973
974
let preferred_regs_by_class: [Vec<PReg>; 3] = {
975
let x_registers: Vec<PReg> = (0..16).map(|x| px_reg(x)).collect();
976
let f_registers: Vec<PReg> = (0..32).map(|x| pf_reg(x)).collect();
977
let v_registers: Vec<PReg> = (0..32).map(|x| pv_reg(x)).collect();
978
[x_registers, f_registers, v_registers]
979
};
980
981
let non_preferred_regs_by_class: [Vec<PReg>; 3] = {
982
let x_registers: Vec<PReg> = (16..XReg::SPECIAL_START)
983
.map(|x| px_reg(x.into()))
984
.collect();
985
let f_registers: Vec<PReg> = vec![];
986
let v_registers: Vec<PReg> = vec![];
987
[x_registers, f_registers, v_registers]
988
};
989
990
MachineEnv {
991
preferred_regs_by_class,
992
non_preferred_regs_by_class,
993
fixed_stack_slots: vec![],
994
scratch_by_class: [None, None, None],
995
}
996
}
997
998