Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/riscv64/abi.rs
3090 views
1
//! Implementation of a standard Riscv64 ABI.
2
3
use crate::ir;
4
use crate::ir::types::*;
5
6
use crate::isa;
7
8
use crate::isa::CallConv;
9
use crate::isa::riscv64::inst::*;
10
use crate::machinst::*;
11
12
use crate::CodegenResult;
13
use crate::ir::LibCall;
14
use crate::ir::Signature;
15
use crate::isa::riscv64::settings::Flags as RiscvFlags;
16
use crate::isa::unwind::UnwindInst;
17
use crate::settings;
18
use alloc::boxed::Box;
19
use alloc::vec::Vec;
20
use regalloc2::{MachineEnv, PReg, PRegSet};
21
22
use alloc::borrow::ToOwned;
23
use smallvec::{SmallVec, smallvec};
24
use std::sync::OnceLock;
25
26
/// Support for the Riscv64 ABI from the callee side (within a function body).
27
pub(crate) type Riscv64Callee = Callee<Riscv64MachineDeps>;
28
29
/// Riscv64-specific ABI behavior. This struct just serves as an implementation
30
/// point for the trait; it is never actually instantiated.
31
pub struct Riscv64MachineDeps;
32
33
impl IsaFlags for RiscvFlags {}
34
35
impl RiscvFlags {
36
pub(crate) fn min_vec_reg_size(&self) -> u64 {
37
let entries = [
38
(self.has_zvl65536b(), 65536),
39
(self.has_zvl32768b(), 32768),
40
(self.has_zvl16384b(), 16384),
41
(self.has_zvl8192b(), 8192),
42
(self.has_zvl4096b(), 4096),
43
(self.has_zvl2048b(), 2048),
44
(self.has_zvl1024b(), 1024),
45
(self.has_zvl512b(), 512),
46
(self.has_zvl256b(), 256),
47
// In order to claim the Application Profile V extension, a minimum
48
// register size of 128 is required. i.e. V implies Zvl128b.
49
(self.has_v(), 128),
50
(self.has_zvl128b(), 128),
51
(self.has_zvl64b(), 64),
52
(self.has_zvl32b(), 32),
53
];
54
55
for (has_flag, size) in entries.into_iter() {
56
if !has_flag {
57
continue;
58
}
59
60
// Due to a limitation in regalloc2, we can't support types
61
// larger than 1024 bytes. So limit that here.
62
return core::cmp::min(size, 1024);
63
}
64
65
return 0;
66
}
67
}
68
69
impl ABIMachineSpec for Riscv64MachineDeps {
70
type I = Inst;
71
type F = RiscvFlags;
72
73
/// This is the limit for the size of argument and return-value areas on the
74
/// stack. We place a reasonable limit here to avoid integer overflow issues
75
/// with 32-bit arithmetic: for now, 128 MB.
76
const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
77
78
fn word_bits() -> u32 {
79
64
80
}
81
82
/// Return required stack alignment in bytes.
83
fn stack_align(_call_conv: isa::CallConv) -> u32 {
84
16
85
}
86
87
fn compute_arg_locs(
88
call_conv: isa::CallConv,
89
flags: &settings::Flags,
90
params: &[ir::AbiParam],
91
args_or_rets: ArgsOrRets,
92
add_ret_area_ptr: bool,
93
mut args: ArgsAccumulator,
94
) -> CodegenResult<(u32, Option<usize>)> {
95
// This implements the LP64D RISC-V ABI.
96
97
assert_ne!(
98
call_conv,
99
isa::CallConv::Winch,
100
"riscv64 does not support the 'winch' calling convention yet"
101
);
102
103
// All registers that can be used as parameters or rets.
104
// both start and end are included.
105
let (x_start, x_end, f_start, f_end) = match args_or_rets {
106
ArgsOrRets::Args => (10, 17, 10, 17),
107
ArgsOrRets::Rets => (10, 11, 10, 11),
108
};
109
let mut next_x_reg = x_start;
110
let mut next_f_reg = f_start;
111
// Stack space.
112
let mut next_stack: u32 = 0;
113
114
let ret_area_ptr = if add_ret_area_ptr {
115
assert!(ArgsOrRets::Args == args_or_rets);
116
next_x_reg += 1;
117
Some(ABIArg::reg(
118
x_reg(x_start).to_real_reg().unwrap(),
119
I64,
120
ir::ArgumentExtension::None,
121
ir::ArgumentPurpose::Normal,
122
))
123
} else {
124
None
125
};
126
127
for param in params {
128
if let ir::ArgumentPurpose::StructArgument(_) = param.purpose {
129
panic!(
130
"StructArgument parameters are not supported on riscv64. \
131
Use regular pointer arguments instead."
132
);
133
}
134
135
// Find regclass(es) of the register(s) used to store a value of this type.
136
let (rcs, reg_tys) = Inst::rc_for_type(param.value_type)?;
137
let mut slots = ABIArgSlotVec::new();
138
for (rc, reg_ty) in rcs.iter().zip(reg_tys.iter()) {
139
let next_reg = if (next_x_reg <= x_end) && *rc == RegClass::Int {
140
let x = Some(x_reg(next_x_reg));
141
next_x_reg += 1;
142
x
143
} else if (next_f_reg <= f_end) && *rc == RegClass::Float {
144
let x = Some(f_reg(next_f_reg));
145
next_f_reg += 1;
146
x
147
} else {
148
None
149
};
150
if let Some(reg) = next_reg {
151
slots.push(ABIArgSlot::Reg {
152
reg: reg.to_real_reg().unwrap(),
153
ty: *reg_ty,
154
extension: param.extension,
155
});
156
} else {
157
if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {
158
return Err(crate::CodegenError::Unsupported(
159
"Too many return values to fit in registers. \
160
Use a StructReturn argument instead. (#9510)"
161
.to_owned(),
162
));
163
}
164
165
// Compute size and 16-byte stack alignment happens
166
// separately after all args.
167
let size = reg_ty.bits() / 8;
168
let size = core::cmp::max(size, 8);
169
// Align.
170
debug_assert!(size.is_power_of_two());
171
next_stack = align_to(next_stack, size);
172
slots.push(ABIArgSlot::Stack {
173
offset: next_stack as i64,
174
ty: *reg_ty,
175
extension: param.extension,
176
});
177
next_stack += size;
178
}
179
}
180
args.push(ABIArg::Slots {
181
slots,
182
purpose: param.purpose,
183
});
184
}
185
let pos = if let Some(ret_area_ptr) = ret_area_ptr {
186
args.push_non_formal(ret_area_ptr);
187
Some(args.args().len() - 1)
188
} else {
189
None
190
};
191
192
next_stack = align_to(next_stack, Self::stack_align(call_conv));
193
194
Ok((next_stack, pos))
195
}
196
197
fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
198
Inst::gen_load(into_reg, mem.into(), ty, MemFlags::trusted())
199
}
200
201
fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
202
Inst::gen_store(mem.into(), from_reg, ty, MemFlags::trusted())
203
}
204
205
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
206
Inst::gen_move(to_reg, from_reg, ty)
207
}
208
209
fn gen_extend(
210
to_reg: Writable<Reg>,
211
from_reg: Reg,
212
signed: bool,
213
from_bits: u8,
214
to_bits: u8,
215
) -> Inst {
216
assert!(from_bits < to_bits);
217
Inst::Extend {
218
rd: to_reg,
219
rn: from_reg,
220
signed,
221
from_bits,
222
to_bits,
223
}
224
}
225
226
fn get_ext_mode(
227
_call_conv: isa::CallConv,
228
specified: ir::ArgumentExtension,
229
) -> ir::ArgumentExtension {
230
specified
231
}
232
233
fn gen_args(args: Vec<ArgPair>) -> Inst {
234
Inst::Args { args }
235
}
236
237
fn gen_rets(rets: Vec<RetPair>) -> Inst {
238
Inst::Rets { rets }
239
}
240
241
fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {
242
spilltmp_reg()
243
}
244
245
fn gen_add_imm(
246
_call_conv: isa::CallConv,
247
into_reg: Writable<Reg>,
248
from_reg: Reg,
249
imm: u32,
250
) -> SmallInstVec<Inst> {
251
let mut insts = SmallInstVec::new();
252
if let Some(imm12) = Imm12::maybe_from_u64(imm as u64) {
253
insts.push(Inst::AluRRImm12 {
254
alu_op: AluOPRRI::Addi,
255
rd: into_reg,
256
rs: from_reg,
257
imm12,
258
});
259
} else {
260
insts.extend(Inst::load_constant_u32(
261
writable_spilltmp_reg2(),
262
imm as u64,
263
));
264
insts.push(Inst::AluRRR {
265
alu_op: AluOPRRR::Add,
266
rd: into_reg,
267
rs1: spilltmp_reg2(),
268
rs2: from_reg,
269
});
270
}
271
insts
272
}
273
274
fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {
275
let mut insts = SmallVec::new();
276
insts.push(Inst::TrapIf {
277
cc: IntCC::UnsignedLessThan,
278
rs1: stack_reg(),
279
rs2: limit_reg,
280
trap_code: ir::TrapCode::STACK_OVERFLOW,
281
});
282
insts
283
}
284
285
fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>) -> Inst {
286
Inst::LoadAddr {
287
rd: into_reg,
288
mem: mem.into(),
289
}
290
}
291
292
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
293
let mem = AMode::RegOffset(base, offset as i64);
294
Inst::gen_load(into_reg, mem, ty, MemFlags::trusted())
295
}
296
297
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
298
let mem = AMode::RegOffset(base, offset as i64);
299
Inst::gen_store(mem, from_reg, ty, MemFlags::trusted())
300
}
301
302
fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Inst> {
303
let mut insts = SmallVec::new();
304
305
if amount == 0 {
306
return insts;
307
}
308
309
if let Some(imm) = Imm12::maybe_from_i64(amount as i64) {
310
insts.push(Inst::AluRRImm12 {
311
alu_op: AluOPRRI::Addi,
312
rd: writable_stack_reg(),
313
rs: stack_reg(),
314
imm12: imm,
315
})
316
} else {
317
let tmp = writable_spilltmp_reg();
318
insts.extend(Inst::load_constant_u64(tmp, amount as i64 as u64));
319
insts.push(Inst::AluRRR {
320
alu_op: AluOPRRR::Add,
321
rd: writable_stack_reg(),
322
rs1: stack_reg(),
323
rs2: tmp.to_reg(),
324
});
325
}
326
327
insts
328
}
329
330
fn gen_prologue_frame_setup(
331
_call_conv: isa::CallConv,
332
flags: &settings::Flags,
333
_isa_flags: &RiscvFlags,
334
frame_layout: &FrameLayout,
335
) -> SmallInstVec<Inst> {
336
let mut insts = SmallVec::new();
337
338
if frame_layout.setup_area_size > 0 {
339
// add sp,sp,-16 ;; alloc stack space for fp.
340
// sd ra,8(sp) ;; save ra.
341
// sd fp,0(sp) ;; store old fp.
342
// mv fp,sp ;; set fp to sp.
343
insts.extend(Self::gen_sp_reg_adjust(-16));
344
insts.push(Inst::gen_store(
345
AMode::SPOffset(8),
346
link_reg(),
347
I64,
348
MemFlags::trusted(),
349
));
350
insts.push(Inst::gen_store(
351
AMode::SPOffset(0),
352
fp_reg(),
353
I64,
354
MemFlags::trusted(),
355
));
356
357
if flags.unwind_info() {
358
insts.push(Inst::Unwind {
359
inst: UnwindInst::PushFrameRegs {
360
offset_upward_to_caller_sp: frame_layout.setup_area_size,
361
},
362
});
363
}
364
insts.push(Inst::Mov {
365
rd: writable_fp_reg(),
366
rm: stack_reg(),
367
ty: I64,
368
});
369
}
370
371
insts
372
}
373
/// reverse of gen_prologue_frame_setup.
374
fn gen_epilogue_frame_restore(
375
call_conv: isa::CallConv,
376
_flags: &settings::Flags,
377
_isa_flags: &RiscvFlags,
378
frame_layout: &FrameLayout,
379
) -> SmallInstVec<Inst> {
380
let mut insts = SmallVec::new();
381
382
if frame_layout.setup_area_size > 0 {
383
insts.push(Inst::gen_load(
384
writable_link_reg(),
385
AMode::SPOffset(8),
386
I64,
387
MemFlags::trusted(),
388
));
389
insts.push(Inst::gen_load(
390
writable_fp_reg(),
391
AMode::SPOffset(0),
392
I64,
393
MemFlags::trusted(),
394
));
395
insts.extend(Self::gen_sp_reg_adjust(16));
396
}
397
398
if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 {
399
insts.extend(Self::gen_sp_reg_adjust(
400
frame_layout.tail_args_size.try_into().unwrap(),
401
));
402
}
403
404
insts
405
}
406
407
fn gen_return(
408
_call_conv: isa::CallConv,
409
_isa_flags: &RiscvFlags,
410
_frame_layout: &FrameLayout,
411
) -> SmallInstVec<Inst> {
412
smallvec![Inst::Ret {}]
413
}
414
415
fn gen_probestack(insts: &mut SmallInstVec<Self::I>, frame_size: u32) {
416
insts.extend(Inst::load_constant_u32(writable_a0(), frame_size as u64));
417
let mut info = CallInfo::empty(
418
ExternalName::LibCall(LibCall::Probestack),
419
CallConv::SystemV,
420
);
421
info.uses.push(CallArgPair {
422
vreg: a0(),
423
preg: a0(),
424
});
425
insts.push(Inst::Call {
426
info: Box::new(info),
427
});
428
}
429
430
fn gen_clobber_save(
431
_call_conv: isa::CallConv,
432
flags: &settings::Flags,
433
frame_layout: &FrameLayout,
434
) -> SmallVec<[Inst; 16]> {
435
let mut insts = SmallVec::new();
436
let setup_frame = frame_layout.setup_area_size > 0;
437
438
let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;
439
if incoming_args_diff > 0 {
440
// Decrement SP by the amount of additional incoming argument space we need
441
insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));
442
443
if setup_frame {
444
// Write the lr position on the stack again, as it hasn't changed since it was
445
// pushed in `gen_prologue_frame_setup`
446
insts.push(Inst::gen_store(
447
AMode::SPOffset(8),
448
link_reg(),
449
I64,
450
MemFlags::trusted(),
451
));
452
insts.push(Inst::gen_load(
453
writable_fp_reg(),
454
AMode::SPOffset(i64::from(incoming_args_diff)),
455
I64,
456
MemFlags::trusted(),
457
));
458
insts.push(Inst::gen_store(
459
AMode::SPOffset(0),
460
fp_reg(),
461
I64,
462
MemFlags::trusted(),
463
));
464
465
// Finally, sync the frame pointer with SP
466
insts.push(Inst::gen_move(writable_fp_reg(), stack_reg(), I64));
467
}
468
}
469
470
if flags.unwind_info() && setup_frame {
471
// The *unwind* frame (but not the actual frame) starts at the
472
// clobbers, just below the saved FP/LR pair.
473
insts.push(Inst::Unwind {
474
inst: UnwindInst::DefineNewFrame {
475
offset_downward_to_clobbers: frame_layout.clobber_size,
476
offset_upward_to_caller_sp: frame_layout.setup_area_size,
477
},
478
});
479
}
480
481
// Adjust the stack pointer downward for clobbers, the function fixed
482
// frame (spillslots and storage slots), and outgoing arguments.
483
let stack_size = frame_layout.clobber_size
484
+ frame_layout.fixed_frame_storage_size
485
+ frame_layout.outgoing_args_size;
486
487
// Store each clobbered register in order at offsets from SP,
488
// placing them above the fixed frame slots.
489
if stack_size > 0 {
490
insts.extend(Self::gen_sp_reg_adjust(-(stack_size as i32)));
491
492
let mut cur_offset = 0;
493
for reg in &frame_layout.clobbered_callee_saves {
494
let r_reg = reg.to_reg();
495
let ty = match r_reg.class() {
496
RegClass::Int => I64,
497
RegClass::Float => F64,
498
RegClass::Vector => I8X16,
499
};
500
cur_offset = align_to(cur_offset, ty.bytes());
501
insts.push(Inst::gen_store(
502
AMode::SPOffset(i64::from(stack_size - cur_offset - ty.bytes())),
503
Reg::from(reg.to_reg()),
504
ty,
505
MemFlags::trusted(),
506
));
507
508
if flags.unwind_info() {
509
insts.push(Inst::Unwind {
510
inst: UnwindInst::SaveReg {
511
clobber_offset: frame_layout.clobber_size - cur_offset - ty.bytes(),
512
reg: r_reg,
513
},
514
});
515
}
516
517
cur_offset += ty.bytes();
518
assert!(cur_offset <= stack_size);
519
}
520
}
521
insts
522
}
523
524
fn gen_clobber_restore(
525
_call_conv: isa::CallConv,
526
_flags: &settings::Flags,
527
frame_layout: &FrameLayout,
528
) -> SmallVec<[Inst; 16]> {
529
let mut insts = SmallVec::new();
530
531
let stack_size = frame_layout.clobber_size
532
+ frame_layout.fixed_frame_storage_size
533
+ frame_layout.outgoing_args_size;
534
let mut cur_offset = 0;
535
536
for reg in &frame_layout.clobbered_callee_saves {
537
let rreg = reg.to_reg();
538
let ty = match rreg.class() {
539
RegClass::Int => I64,
540
RegClass::Float => F64,
541
RegClass::Vector => I8X16,
542
};
543
cur_offset = align_to(cur_offset, ty.bytes());
544
insts.push(Inst::gen_load(
545
reg.map(Reg::from),
546
AMode::SPOffset(i64::from(stack_size - cur_offset - ty.bytes())),
547
ty,
548
MemFlags::trusted(),
549
));
550
cur_offset += ty.bytes();
551
}
552
553
if stack_size > 0 {
554
insts.extend(Self::gen_sp_reg_adjust(stack_size as i32));
555
}
556
557
insts
558
}
559
560
fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
561
call_conv: isa::CallConv,
562
dst: Reg,
563
src: Reg,
564
size: usize,
565
mut alloc_tmp: F,
566
) -> SmallVec<[Self::I; 8]> {
567
let mut insts = SmallVec::new();
568
let arg0 = Writable::from_reg(x_reg(10));
569
let arg1 = Writable::from_reg(x_reg(11));
570
let arg2 = Writable::from_reg(x_reg(12));
571
let tmp = alloc_tmp(Self::word_type());
572
insts.extend(Inst::load_constant_u64(tmp, size as u64));
573
insts.push(Inst::Call {
574
info: Box::new(CallInfo {
575
dest: ExternalName::LibCall(LibCall::Memcpy),
576
uses: smallvec![
577
CallArgPair {
578
vreg: dst,
579
preg: arg0.to_reg()
580
},
581
CallArgPair {
582
vreg: src,
583
preg: arg1.to_reg()
584
},
585
CallArgPair {
586
vreg: tmp.to_reg(),
587
preg: arg2.to_reg()
588
}
589
],
590
defs: smallvec![],
591
clobbers: Self::get_regs_clobbered_by_call(call_conv, false),
592
caller_conv: call_conv,
593
callee_conv: call_conv,
594
callee_pop_size: 0,
595
try_call_info: None,
596
patchable: false,
597
}),
598
});
599
insts
600
}
601
602
fn get_number_of_spillslots_for_value(
603
rc: RegClass,
604
_target_vector_bytes: u32,
605
isa_flags: &RiscvFlags,
606
) -> u32 {
607
// We allocate in terms of 8-byte slots.
608
match rc {
609
RegClass::Int => 1,
610
RegClass::Float => 1,
611
RegClass::Vector => (isa_flags.min_vec_reg_size() / 8) as u32,
612
}
613
}
614
615
fn get_machine_env(_flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
616
static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
617
MACHINE_ENV.get_or_init(create_reg_environment)
618
}
619
620
fn get_regs_clobbered_by_call(
621
call_conv_of_callee: isa::CallConv,
622
is_exception: bool,
623
) -> PRegSet {
624
match call_conv_of_callee {
625
isa::CallConv::Tail if is_exception => ALL_CLOBBERS,
626
// Note that "PreserveAll" actually preserves nothing at
627
// the callsite if used for a `try_call`, because the
628
// unwinder ABI for `try_call`s is still "no clobbered
629
// register restores" for this ABI (so as to work with
630
// Wasmtime).
631
isa::CallConv::PreserveAll if is_exception => ALL_CLOBBERS,
632
isa::CallConv::PreserveAll => NO_CLOBBERS,
633
_ => DEFAULT_CLOBBERS,
634
}
635
}
636
637
fn compute_frame_layout(
638
call_conv: isa::CallConv,
639
flags: &settings::Flags,
640
_sig: &Signature,
641
regs: &[Writable<RealReg>],
642
function_calls: FunctionCalls,
643
incoming_args_size: u32,
644
tail_args_size: u32,
645
stackslots_size: u32,
646
fixed_frame_storage_size: u32,
647
outgoing_args_size: u32,
648
) -> FrameLayout {
649
let is_callee_saved = |reg: &Writable<RealReg>| match call_conv {
650
isa::CallConv::PreserveAll => true,
651
_ => DEFAULT_CALLEE_SAVES.contains(reg.to_reg().into()),
652
};
653
let mut regs: Vec<Writable<RealReg>> =
654
regs.iter().cloned().filter(is_callee_saved).collect();
655
656
regs.sort_unstable();
657
658
// Compute clobber size.
659
let clobber_size = compute_clobber_size(&regs);
660
661
// Compute linkage frame size.
662
let setup_area_size = if flags.preserve_frame_pointers()
663
|| function_calls != FunctionCalls::None
664
// The function arguments that are passed on the stack are addressed
665
// relative to the Frame Pointer.
666
|| incoming_args_size > 0
667
|| clobber_size > 0
668
|| fixed_frame_storage_size > 0
669
{
670
16 // FP, LR
671
} else {
672
0
673
};
674
675
// Return FrameLayout structure.
676
FrameLayout {
677
word_bytes: 8,
678
incoming_args_size,
679
tail_args_size,
680
setup_area_size,
681
clobber_size,
682
fixed_frame_storage_size,
683
stackslots_size,
684
outgoing_args_size,
685
clobbered_callee_saves: regs,
686
function_calls,
687
}
688
}
689
690
fn gen_inline_probestack(
691
insts: &mut SmallInstVec<Self::I>,
692
_call_conv: isa::CallConv,
693
frame_size: u32,
694
guard_size: u32,
695
) {
696
// Unroll at most n consecutive probes, before falling back to using a loop
697
const PROBE_MAX_UNROLL: u32 = 3;
698
699
// Calculate how many probes we need to perform. Round down, as we only
700
// need to probe whole guard_size regions we'd otherwise skip over.
701
let probe_count = frame_size / guard_size;
702
if probe_count == 0 {
703
// No probe necessary
704
return;
705
}
706
707
// Must be a caller-saved register that is not an argument.
708
let tmp = Writable::from_reg(x_reg(28)); // t3
709
710
if probe_count <= PROBE_MAX_UNROLL {
711
Self::gen_probestack_unroll(insts, tmp, guard_size, probe_count)
712
} else {
713
insts.push(Inst::StackProbeLoop {
714
guard_size,
715
probe_count,
716
tmp,
717
});
718
}
719
}
720
721
fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
722
// Use x12 as a temp if needed: clobbered, not a
723
// retval.
724
Writable::from_reg(regs::x_reg(12))
725
}
726
727
fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
728
const PAYLOAD_REGS: &'static [Reg] = &[regs::a0(), regs::a1()];
729
match call_conv {
730
isa::CallConv::SystemV | isa::CallConv::Tail | isa::CallConv::PreserveAll => {
731
PAYLOAD_REGS
732
}
733
_ => &[],
734
}
735
}
736
}
737
738
// NOTE: no V regs are callee save.
739
const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()
740
// X Regs
741
.with(px_reg(2))
742
.with(px_reg(8))
743
.with(px_reg(9))
744
.with(px_reg(18))
745
.with(px_reg(19))
746
.with(px_reg(20))
747
.with(px_reg(21))
748
.with(px_reg(22))
749
.with(px_reg(23))
750
.with(px_reg(24))
751
.with(px_reg(25))
752
.with(px_reg(26))
753
.with(px_reg(27))
754
// F Regs
755
.with(pf_reg(8))
756
.with(pf_reg(18))
757
.with(pf_reg(19))
758
.with(pf_reg(20))
759
.with(pf_reg(21))
760
.with(pf_reg(22))
761
.with(pf_reg(23))
762
.with(pf_reg(24))
763
.with(pf_reg(25))
764
.with(pf_reg(26))
765
.with(pf_reg(27));
766
767
fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
768
let mut clobbered_size = 0;
769
for reg in clobbers {
770
match reg.to_reg().class() {
771
RegClass::Int => {
772
clobbered_size += 8;
773
}
774
RegClass::Float => {
775
clobbered_size += 8;
776
}
777
RegClass::Vector => {
778
clobbered_size = align_to(clobbered_size, 16);
779
clobbered_size += 16;
780
}
781
}
782
}
783
align_to(clobbered_size, 16)
784
}
785
786
const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()
787
.with(px_reg(1))
788
.with(px_reg(5))
789
.with(px_reg(6))
790
.with(px_reg(7))
791
.with(px_reg(10))
792
.with(px_reg(11))
793
.with(px_reg(12))
794
.with(px_reg(13))
795
.with(px_reg(14))
796
.with(px_reg(15))
797
.with(px_reg(16))
798
.with(px_reg(17))
799
.with(px_reg(28))
800
.with(px_reg(29))
801
.with(px_reg(30))
802
.with(px_reg(31))
803
// F Regs
804
.with(pf_reg(0))
805
.with(pf_reg(1))
806
.with(pf_reg(2))
807
.with(pf_reg(3))
808
.with(pf_reg(4))
809
.with(pf_reg(5))
810
.with(pf_reg(6))
811
.with(pf_reg(7))
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(28))
822
.with(pf_reg(29))
823
.with(pf_reg(30))
824
.with(pf_reg(31))
825
// V Regs - All vector regs get clobbered
826
.with(pv_reg(0))
827
.with(pv_reg(1))
828
.with(pv_reg(2))
829
.with(pv_reg(3))
830
.with(pv_reg(4))
831
.with(pv_reg(5))
832
.with(pv_reg(6))
833
.with(pv_reg(7))
834
.with(pv_reg(8))
835
.with(pv_reg(9))
836
.with(pv_reg(10))
837
.with(pv_reg(11))
838
.with(pv_reg(12))
839
.with(pv_reg(13))
840
.with(pv_reg(14))
841
.with(pv_reg(15))
842
.with(pv_reg(16))
843
.with(pv_reg(17))
844
.with(pv_reg(18))
845
.with(pv_reg(19))
846
.with(pv_reg(20))
847
.with(pv_reg(21))
848
.with(pv_reg(22))
849
.with(pv_reg(23))
850
.with(pv_reg(24))
851
.with(pv_reg(25))
852
.with(pv_reg(26))
853
.with(pv_reg(27))
854
.with(pv_reg(28))
855
.with(pv_reg(29))
856
.with(pv_reg(30))
857
.with(pv_reg(31));
858
859
const ALL_CLOBBERS: PRegSet = PRegSet::empty()
860
// Specials: x0 is the zero register; x1 is the return address; x2 is SP.
861
.with(px_reg(3))
862
.with(px_reg(4))
863
.with(px_reg(5))
864
.with(px_reg(6))
865
.with(px_reg(7))
866
.with(px_reg(8))
867
.with(px_reg(9))
868
.with(px_reg(10))
869
.with(px_reg(11))
870
.with(px_reg(12))
871
.with(px_reg(13))
872
.with(px_reg(14))
873
.with(px_reg(15))
874
.with(px_reg(16))
875
.with(px_reg(17))
876
.with(px_reg(18))
877
.with(px_reg(19))
878
.with(px_reg(20))
879
.with(px_reg(21))
880
.with(px_reg(22))
881
.with(px_reg(23))
882
.with(px_reg(24))
883
.with(px_reg(25))
884
.with(px_reg(26))
885
.with(px_reg(27))
886
.with(px_reg(28))
887
.with(px_reg(29))
888
.with(px_reg(30))
889
.with(px_reg(31))
890
// F Regs
891
.with(pf_reg(0))
892
.with(pf_reg(1))
893
.with(pf_reg(2))
894
.with(pf_reg(3))
895
.with(pf_reg(4))
896
.with(pf_reg(5))
897
.with(pf_reg(6))
898
.with(pf_reg(7))
899
.with(pf_reg(8))
900
.with(pf_reg(9))
901
.with(pf_reg(10))
902
.with(pf_reg(11))
903
.with(pf_reg(12))
904
.with(pf_reg(13))
905
.with(pf_reg(14))
906
.with(pf_reg(15))
907
.with(pf_reg(16))
908
.with(pf_reg(17))
909
.with(pf_reg(18))
910
.with(pf_reg(19))
911
.with(pf_reg(20))
912
.with(pf_reg(21))
913
.with(pf_reg(22))
914
.with(pf_reg(23))
915
.with(pf_reg(24))
916
.with(pf_reg(25))
917
.with(pf_reg(26))
918
.with(pf_reg(27))
919
.with(pf_reg(28))
920
.with(pf_reg(29))
921
.with(pf_reg(30))
922
.with(pf_reg(31))
923
// V Regs
924
.with(pv_reg(0))
925
.with(pv_reg(1))
926
.with(pv_reg(2))
927
.with(pv_reg(3))
928
.with(pv_reg(4))
929
.with(pv_reg(5))
930
.with(pv_reg(6))
931
.with(pv_reg(7))
932
.with(pv_reg(8))
933
.with(pv_reg(9))
934
.with(pv_reg(10))
935
.with(pv_reg(11))
936
.with(pv_reg(12))
937
.with(pv_reg(13))
938
.with(pv_reg(14))
939
.with(pv_reg(15))
940
.with(pv_reg(16))
941
.with(pv_reg(17))
942
.with(pv_reg(18))
943
.with(pv_reg(19))
944
.with(pv_reg(20))
945
.with(pv_reg(21))
946
.with(pv_reg(22))
947
.with(pv_reg(23))
948
.with(pv_reg(24))
949
.with(pv_reg(25))
950
.with(pv_reg(26))
951
.with(pv_reg(27))
952
.with(pv_reg(28))
953
.with(pv_reg(29))
954
.with(pv_reg(30))
955
.with(pv_reg(31));
956
957
const NO_CLOBBERS: PRegSet = PRegSet::empty();
958
959
fn create_reg_environment() -> MachineEnv {
960
// Some C Extension instructions can only use a subset of the registers.
961
// x8 - x15, f8 - f15, v8 - v15 so we should prefer to use those since
962
// they allow us to emit C instructions more often.
963
//
964
// In general the order of preference is:
965
// 1. Compressible Caller Saved registers.
966
// 2. Non-Compressible Caller Saved registers.
967
// 3. Compressible Callee Saved registers.
968
// 4. Non-Compressible Callee Saved registers.
969
970
let preferred_regs_by_class: [Vec<PReg>; 3] = {
971
let x_registers: Vec<PReg> = (10..=15).map(px_reg).collect();
972
let f_registers: Vec<PReg> = (10..=15).map(pf_reg).collect();
973
let v_registers: Vec<PReg> = (8..=15).map(pv_reg).collect();
974
975
[x_registers, f_registers, v_registers]
976
};
977
978
let non_preferred_regs_by_class: [Vec<PReg>; 3] = {
979
// x0 - x4 are special registers, so we don't want to use them.
980
// Omit x30 and x31 since they are the spilltmp registers.
981
982
// Start with the Non-Compressible Caller Saved registers.
983
let x_registers: Vec<PReg> = (5..=7)
984
.chain(16..=17)
985
.chain(28..=29)
986
// The first Callee Saved register is x9 since its Compressible
987
// Omit x8 since it's the frame pointer.
988
.chain(9..=9)
989
// The rest of the Callee Saved registers are Non-Compressible
990
.chain(18..=27)
991
.map(px_reg)
992
.collect();
993
994
// Prefer Caller Saved registers.
995
let f_registers: Vec<PReg> = (0..=7)
996
.chain(16..=17)
997
.chain(28..=31)
998
// Once those are exhausted, we should prefer f8 and f9 since they are
999
// callee saved, but compressible.
1000
.chain(8..=9)
1001
.chain(18..=27)
1002
.map(pf_reg)
1003
.collect();
1004
1005
let v_registers = (0..=7).chain(16..=31).map(pv_reg).collect();
1006
1007
[x_registers, f_registers, v_registers]
1008
};
1009
1010
MachineEnv {
1011
preferred_regs_by_class,
1012
non_preferred_regs_by_class,
1013
fixed_stack_slots: vec![],
1014
scratch_by_class: [None, None, None],
1015
}
1016
}
1017
1018
impl Riscv64MachineDeps {
1019
fn gen_probestack_unroll(
1020
insts: &mut SmallInstVec<Inst>,
1021
tmp: Writable<Reg>,
1022
guard_size: u32,
1023
probe_count: u32,
1024
) {
1025
// When manually unrolling adjust the stack pointer and then write a zero
1026
// to the stack at that offset.
1027
//
1028
// We do this because valgrind expects us to never write beyond the stack
1029
// pointer and associated redzone.
1030
// See: https://github.com/bytecodealliance/wasmtime/issues/7454
1031
1032
// Store the adjust amount in a register upfront, so we don't have to
1033
// reload it for each probe. It's worth loading this as a negative and
1034
// using an `add` instruction since we have compressed versions of `add`
1035
// but not the `sub` instruction.
1036
insts.extend(Inst::load_constant_u64(tmp, (-(guard_size as i64)) as u64));
1037
1038
for _ in 0..probe_count {
1039
insts.push(Inst::AluRRR {
1040
alu_op: AluOPRRR::Add,
1041
rd: writable_stack_reg(),
1042
rs1: stack_reg(),
1043
rs2: tmp.to_reg(),
1044
});
1045
1046
insts.push(Inst::gen_store(
1047
AMode::SPOffset(0),
1048
zero_reg(),
1049
I32,
1050
MemFlags::trusted(),
1051
));
1052
}
1053
1054
// Restore the stack pointer to its original value
1055
insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));
1056
}
1057
}
1058
1059