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
1693 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::vec::Vec;
13
use core::marker::PhantomData;
14
use cranelift_bitset::ScalarBitSet;
15
use regalloc2::{MachineEnv, PReg, PRegSet};
16
use smallvec::{SmallVec, smallvec};
17
use std::borrow::ToOwned;
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 = std::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 {
489
DEFAULT_CLOBBERS
490
}
491
}
492
493
fn compute_frame_layout(
494
_call_conv: isa::CallConv,
495
flags: &settings::Flags,
496
_sig: &Signature,
497
regs: &[Writable<RealReg>],
498
function_calls: FunctionCalls,
499
incoming_args_size: u32,
500
tail_args_size: u32,
501
stackslots_size: u32,
502
fixed_frame_storage_size: u32,
503
outgoing_args_size: u32,
504
) -> FrameLayout {
505
let mut regs: Vec<Writable<RealReg>> = regs
506
.iter()
507
.cloned()
508
.filter(|r| DEFAULT_CALLEE_SAVES.contains(r.to_reg().into()))
509
.collect();
510
511
regs.sort_unstable();
512
513
// Compute clobber size.
514
let clobber_size = compute_clobber_size(&regs);
515
516
// Compute linkage frame size.
517
let setup_area_size = if flags.preserve_frame_pointers()
518
|| function_calls != FunctionCalls::None
519
// The function arguments that are passed on the stack are addressed
520
// relative to the Frame Pointer.
521
|| incoming_args_size > 0
522
|| clobber_size > 0
523
|| fixed_frame_storage_size > 0
524
{
525
P::pointer_width().bytes() * 2 // FP, LR
526
} else {
527
0
528
};
529
530
FrameLayout {
531
word_bytes: u32::from(P::pointer_width().bytes()),
532
incoming_args_size,
533
tail_args_size,
534
setup_area_size: setup_area_size.into(),
535
clobber_size,
536
fixed_frame_storage_size,
537
stackslots_size,
538
outgoing_args_size,
539
clobbered_callee_saves: regs,
540
function_calls,
541
}
542
}
543
544
fn gen_inline_probestack(
545
_insts: &mut SmallInstVec<Self::I>,
546
_call_conv: isa::CallConv,
547
_frame_size: u32,
548
_guard_size: u32,
549
) {
550
// Pulley doesn't need inline probestacks because it always checks stack
551
// decrements.
552
}
553
554
fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
555
// Use x15 as a temp if needed: clobbered, not a
556
// retval.
557
Writable::from_reg(regs::x_reg(15))
558
}
559
560
fn exception_payload_regs(_call_conv: isa::CallConv) -> &'static [Reg] {
561
const PAYLOAD_REGS: &'static [Reg] = &[
562
Reg::from_real_reg(regs::px_reg(0)),
563
Reg::from_real_reg(regs::px_reg(1)),
564
];
565
PAYLOAD_REGS
566
}
567
}
568
569
/// Different styles of management of fp/lr and clobbered registers.
570
///
571
/// This helps decide, depending on Cranelift settings and frame layout, what
572
/// macro instruction is used to setup the pulley frame.
573
enum FrameStyle {
574
/// No management is happening, fp/lr aren't saved by Pulley or Cranelift.
575
/// No stack is being allocated either.
576
None,
577
578
/// Pulley saves the fp/lr combo and then stack adjustments/clobbers are
579
/// handled manually.
580
PulleyBasicSetup { frame_size: u32 },
581
582
/// Pulley is managing the fp/lr combo, the stack size, and clobbered
583
/// X-class registers.
584
///
585
/// Note that `saved_by_pulley` is not the exhaustive set of clobbered
586
/// registers. It's only those that are part of the `PushFrameSave`
587
/// instruction.
588
PulleySetupAndSaveClobbers {
589
/// The size of the frame, including clobbers, that's being allocated.
590
frame_size: u16,
591
/// Registers that pulley is saving/restoring.
592
saved_by_pulley: ScalarBitSet<u16>,
593
},
594
595
/// Cranelift is manually managing everything, both clobbers and stack
596
/// increments/decrements.
597
///
598
/// Note that fp/lr are not saved in this mode.
599
Manual {
600
/// The size of the stack being allocated.
601
frame_size: u32,
602
},
603
}
604
605
/// Pulley-specific helpers when dealing with ABI code.
606
impl FrameLayout {
607
/// Whether or not this frame saves fp/lr.
608
fn setup_frame(&self) -> bool {
609
self.setup_area_size > 0
610
}
611
612
/// Returns the stack size allocated by this function, excluding incoming
613
/// tail args or the optional "setup area" of fp/lr.
614
fn stack_size(&self) -> u32 {
615
self.clobber_size + self.fixed_frame_storage_size + self.outgoing_args_size
616
}
617
618
/// Returns the style of frame being used for this function.
619
///
620
/// See `FrameStyle` for more information.
621
fn pulley_frame_style(&self) -> FrameStyle {
622
let saved_by_pulley = self.clobbered_xregs_saved_by_pulley();
623
match (
624
self.stack_size(),
625
self.setup_frame(),
626
saved_by_pulley.is_empty(),
627
) {
628
// No stack allocated, not saving fp/lr, no clobbers, nothing to do
629
(0, false, true) => FrameStyle::None,
630
631
// No stack allocated, saving fp/lr, no clobbers, so this is
632
// pulley-managed via push/pop_frame.
633
(0, true, true) => FrameStyle::PulleyBasicSetup { frame_size: 0 },
634
635
// Some stack is being allocated and pulley is managing fp/lr. Let
636
// pulley manage clobbered registers as well, regardless if they're
637
// present or not.
638
//
639
// If the stack is too large then `PulleyBasicSetup` is used
640
// otherwise we'll be pushing `PushFrameSave` and `PopFrameRestore`.
641
(frame_size, true, _) => match frame_size.try_into() {
642
Ok(frame_size) => FrameStyle::PulleySetupAndSaveClobbers {
643
frame_size,
644
saved_by_pulley,
645
},
646
Err(_) => FrameStyle::PulleyBasicSetup { frame_size },
647
},
648
649
// Some stack is being allocated, but pulley isn't managing fp/lr,
650
// so we're manually doing everything.
651
(frame_size, false, true) => FrameStyle::Manual { frame_size },
652
653
// If there's no frame setup and there's clobbered registers this
654
// technically should have already hit a case above, so panic here.
655
(_, false, false) => unreachable!(),
656
}
657
}
658
659
/// Returns the set of clobbered registers that Pulley is managing via its
660
/// macro instructions rather than the generated code.
661
fn clobbered_xregs_saved_by_pulley(&self) -> ScalarBitSet<u16> {
662
let mut clobbered: ScalarBitSet<u16> = ScalarBitSet::new();
663
// Pulley only manages clobbers if it's also managing fp/lr.
664
if !self.setup_frame() {
665
return clobbered;
666
}
667
let mut found_manual_clobber = false;
668
for reg in self.clobbered_callee_saves.iter() {
669
let r_reg = reg.to_reg();
670
// Pulley can only manage clobbers of integer registers at this
671
// time, float registers are managed manually.
672
//
673
// Also assert that all pulley-managed clobbers come first,
674
// otherwise the loop below in `manually_managed_clobbers` is
675
// incorrect.
676
if r_reg.class() == RegClass::Int {
677
assert!(!found_manual_clobber);
678
if let Some(offset) = r_reg.hw_enc().checked_sub(16) {
679
clobbered.insert(offset);
680
}
681
} else {
682
found_manual_clobber = true;
683
}
684
}
685
clobbered
686
}
687
688
/// Returns an iterator over the clobbers that Cranelift is managing, not
689
/// Pulley.
690
///
691
/// If this frame has clobbers then they're either saved by Pulley with
692
/// `FrameStyle::PulleySetupAndSaveClobbers`. Cranelift might need to manage
693
/// these registers depending on Cranelift settings. Cranelift also always
694
/// manages floating-point registers.
695
fn manually_managed_clobbers<'a>(
696
&'a self,
697
style: &'a FrameStyle,
698
) -> impl Iterator<Item = (i32, Type, Reg)> + 'a {
699
let mut offset = self.stack_size();
700
self.clobbered_callee_saves.iter().filter_map(move |reg| {
701
// Allocate space for this clobber no matter what. If pulley is
702
// managing this then we're just accounting for the pulley-saved
703
// registers as well. Note that all pulley-managed registers come
704
// first in the list here.
705
offset -= 8;
706
let r_reg = reg.to_reg();
707
let ty = match r_reg.class() {
708
RegClass::Int => {
709
// If this register is saved by pulley, skip this clobber.
710
if let FrameStyle::PulleySetupAndSaveClobbers {
711
saved_by_pulley, ..
712
} = style
713
{
714
if let Some(reg) = r_reg.hw_enc().checked_sub(16) {
715
if saved_by_pulley.contains(reg) {
716
return None;
717
}
718
}
719
}
720
I64
721
}
722
RegClass::Float => F64,
723
RegClass::Vector => unreachable!("no vector registers are callee-save"),
724
};
725
let offset = i32::try_from(offset).unwrap();
726
Some((offset, ty, Reg::from(reg.to_reg())))
727
})
728
}
729
}
730
731
const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()
732
// Integer registers.
733
.with(px_reg(16))
734
.with(px_reg(17))
735
.with(px_reg(18))
736
.with(px_reg(19))
737
.with(px_reg(20))
738
.with(px_reg(21))
739
.with(px_reg(22))
740
.with(px_reg(23))
741
.with(px_reg(24))
742
.with(px_reg(25))
743
.with(px_reg(26))
744
.with(px_reg(27))
745
.with(px_reg(28))
746
.with(px_reg(29))
747
.with(px_reg(30))
748
.with(px_reg(31))
749
// Note: no float/vector registers are callee-saved.
750
;
751
752
fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
753
let mut clobbered_size = 0;
754
for reg in clobbers {
755
match reg.to_reg().class() {
756
RegClass::Int => {
757
clobbered_size += 8;
758
}
759
RegClass::Float => {
760
clobbered_size += 8;
761
}
762
RegClass::Vector => unimplemented!("Vector Size Clobbered"),
763
}
764
}
765
align_to(clobbered_size, 16)
766
}
767
768
const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()
769
// Integer registers: the first 16 get clobbered.
770
.with(px_reg(0))
771
.with(px_reg(1))
772
.with(px_reg(2))
773
.with(px_reg(3))
774
.with(px_reg(4))
775
.with(px_reg(5))
776
.with(px_reg(6))
777
.with(px_reg(7))
778
.with(px_reg(8))
779
.with(px_reg(9))
780
.with(px_reg(10))
781
.with(px_reg(11))
782
.with(px_reg(12))
783
.with(px_reg(13))
784
.with(px_reg(14))
785
.with(px_reg(15))
786
// All float registers get clobbered.
787
.with(pf_reg(0))
788
.with(pf_reg(1))
789
.with(pf_reg(2))
790
.with(pf_reg(3))
791
.with(pf_reg(4))
792
.with(pf_reg(5))
793
.with(pf_reg(6))
794
.with(pf_reg(7))
795
.with(pf_reg(8))
796
.with(pf_reg(9))
797
.with(pf_reg(10))
798
.with(pf_reg(11))
799
.with(pf_reg(12))
800
.with(pf_reg(13))
801
.with(pf_reg(14))
802
.with(pf_reg(15))
803
.with(pf_reg(16))
804
.with(pf_reg(17))
805
.with(pf_reg(18))
806
.with(pf_reg(19))
807
.with(pf_reg(20))
808
.with(pf_reg(21))
809
.with(pf_reg(22))
810
.with(pf_reg(23))
811
.with(pf_reg(24))
812
.with(pf_reg(25))
813
.with(pf_reg(26))
814
.with(pf_reg(27))
815
.with(pf_reg(28))
816
.with(pf_reg(29))
817
.with(pf_reg(30))
818
.with(pf_reg(31))
819
// All vector registers get clobbered.
820
.with(pv_reg(0))
821
.with(pv_reg(1))
822
.with(pv_reg(2))
823
.with(pv_reg(3))
824
.with(pv_reg(4))
825
.with(pv_reg(5))
826
.with(pv_reg(6))
827
.with(pv_reg(7))
828
.with(pv_reg(8))
829
.with(pv_reg(9))
830
.with(pv_reg(10))
831
.with(pv_reg(11))
832
.with(pv_reg(12))
833
.with(pv_reg(13))
834
.with(pv_reg(14))
835
.with(pv_reg(15))
836
.with(pv_reg(16))
837
.with(pv_reg(17))
838
.with(pv_reg(18))
839
.with(pv_reg(19))
840
.with(pv_reg(20))
841
.with(pv_reg(21))
842
.with(pv_reg(22))
843
.with(pv_reg(23))
844
.with(pv_reg(24))
845
.with(pv_reg(25))
846
.with(pv_reg(26))
847
.with(pv_reg(27))
848
.with(pv_reg(28))
849
.with(pv_reg(29))
850
.with(pv_reg(30))
851
.with(pv_reg(31));
852
853
const ALL_CLOBBERS: PRegSet = PRegSet::empty()
854
.with(px_reg(0))
855
.with(px_reg(1))
856
.with(px_reg(2))
857
.with(px_reg(3))
858
.with(px_reg(4))
859
.with(px_reg(5))
860
.with(px_reg(6))
861
.with(px_reg(7))
862
.with(px_reg(8))
863
.with(px_reg(9))
864
.with(px_reg(10))
865
.with(px_reg(11))
866
.with(px_reg(12))
867
.with(px_reg(13))
868
.with(px_reg(14))
869
.with(px_reg(15))
870
.with(px_reg(16))
871
.with(px_reg(17))
872
.with(px_reg(18))
873
.with(px_reg(19))
874
.with(px_reg(20))
875
.with(px_reg(21))
876
.with(px_reg(22))
877
.with(px_reg(23))
878
.with(px_reg(24))
879
.with(px_reg(25))
880
.with(px_reg(26))
881
.with(px_reg(27))
882
.with(px_reg(28))
883
.with(px_reg(29))
884
.with(px_reg(30))
885
.with(px_reg(31))
886
.with(pf_reg(0))
887
.with(pf_reg(1))
888
.with(pf_reg(2))
889
.with(pf_reg(3))
890
.with(pf_reg(4))
891
.with(pf_reg(5))
892
.with(pf_reg(6))
893
.with(pf_reg(7))
894
.with(pf_reg(8))
895
.with(pf_reg(9))
896
.with(pf_reg(10))
897
.with(pf_reg(11))
898
.with(pf_reg(12))
899
.with(pf_reg(13))
900
.with(pf_reg(14))
901
.with(pf_reg(15))
902
.with(pf_reg(16))
903
.with(pf_reg(17))
904
.with(pf_reg(18))
905
.with(pf_reg(19))
906
.with(pf_reg(20))
907
.with(pf_reg(21))
908
.with(pf_reg(22))
909
.with(pf_reg(23))
910
.with(pf_reg(24))
911
.with(pf_reg(25))
912
.with(pf_reg(26))
913
.with(pf_reg(27))
914
.with(pf_reg(28))
915
.with(pf_reg(29))
916
.with(pf_reg(30))
917
.with(pf_reg(31))
918
.with(pv_reg(0))
919
.with(pv_reg(1))
920
.with(pv_reg(2))
921
.with(pv_reg(3))
922
.with(pv_reg(4))
923
.with(pv_reg(5))
924
.with(pv_reg(6))
925
.with(pv_reg(7))
926
.with(pv_reg(8))
927
.with(pv_reg(9))
928
.with(pv_reg(10))
929
.with(pv_reg(11))
930
.with(pv_reg(12))
931
.with(pv_reg(13))
932
.with(pv_reg(14))
933
.with(pv_reg(15))
934
.with(pv_reg(16))
935
.with(pv_reg(17))
936
.with(pv_reg(18))
937
.with(pv_reg(19))
938
.with(pv_reg(20))
939
.with(pv_reg(21))
940
.with(pv_reg(22))
941
.with(pv_reg(23))
942
.with(pv_reg(24))
943
.with(pv_reg(25))
944
.with(pv_reg(26))
945
.with(pv_reg(27))
946
.with(pv_reg(28))
947
.with(pv_reg(29))
948
.with(pv_reg(30))
949
.with(pv_reg(31));
950
951
fn create_reg_environment() -> MachineEnv {
952
// Prefer caller-saved registers over callee-saved registers, because that
953
// way we don't need to emit code to save and restore them if we don't
954
// mutate them.
955
956
let preferred_regs_by_class: [Vec<PReg>; 3] = {
957
let x_registers: Vec<PReg> = (0..16).map(|x| px_reg(x)).collect();
958
let f_registers: Vec<PReg> = (0..32).map(|x| pf_reg(x)).collect();
959
let v_registers: Vec<PReg> = (0..32).map(|x| pv_reg(x)).collect();
960
[x_registers, f_registers, v_registers]
961
};
962
963
let non_preferred_regs_by_class: [Vec<PReg>; 3] = {
964
let x_registers: Vec<PReg> = (16..XReg::SPECIAL_START)
965
.map(|x| px_reg(x.into()))
966
.collect();
967
let f_registers: Vec<PReg> = vec![];
968
let v_registers: Vec<PReg> = vec![];
969
[x_registers, f_registers, v_registers]
970
};
971
972
MachineEnv {
973
preferred_regs_by_class,
974
non_preferred_regs_by_class,
975
fixed_stack_slots: vec![],
976
scratch_by_class: [None, None, None],
977
}
978
}
979
980