Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/riscv64/abi.rs
1693 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 smallvec::{SmallVec, smallvec};
23
use std::borrow::ToOwned;
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 std::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 = std::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 = 8;
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 => unimplemented!("Vector Clobber Saves"),
499
};
500
insts.push(Inst::gen_store(
501
AMode::SPOffset((stack_size - cur_offset) as i64),
502
Reg::from(reg.to_reg()),
503
ty,
504
MemFlags::trusted(),
505
));
506
507
if flags.unwind_info() {
508
insts.push(Inst::Unwind {
509
inst: UnwindInst::SaveReg {
510
clobber_offset: frame_layout.clobber_size - cur_offset,
511
reg: r_reg,
512
},
513
});
514
}
515
516
cur_offset += 8
517
}
518
}
519
insts
520
}
521
522
fn gen_clobber_restore(
523
_call_conv: isa::CallConv,
524
_flags: &settings::Flags,
525
frame_layout: &FrameLayout,
526
) -> SmallVec<[Inst; 16]> {
527
let mut insts = SmallVec::new();
528
529
let stack_size = frame_layout.clobber_size
530
+ frame_layout.fixed_frame_storage_size
531
+ frame_layout.outgoing_args_size;
532
533
let mut cur_offset = 8;
534
for reg in &frame_layout.clobbered_callee_saves {
535
let rreg = reg.to_reg();
536
let ty = match rreg.class() {
537
RegClass::Int => I64,
538
RegClass::Float => F64,
539
RegClass::Vector => unimplemented!("Vector Clobber Restores"),
540
};
541
insts.push(Inst::gen_load(
542
reg.map(Reg::from),
543
AMode::SPOffset(i64::from(stack_size - cur_offset)),
544
ty,
545
MemFlags::trusted(),
546
));
547
cur_offset += 8
548
}
549
550
if stack_size > 0 {
551
insts.extend(Self::gen_sp_reg_adjust(stack_size as i32));
552
}
553
554
insts
555
}
556
557
fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
558
call_conv: isa::CallConv,
559
dst: Reg,
560
src: Reg,
561
size: usize,
562
mut alloc_tmp: F,
563
) -> SmallVec<[Self::I; 8]> {
564
let mut insts = SmallVec::new();
565
let arg0 = Writable::from_reg(x_reg(10));
566
let arg1 = Writable::from_reg(x_reg(11));
567
let arg2 = Writable::from_reg(x_reg(12));
568
let tmp = alloc_tmp(Self::word_type());
569
insts.extend(Inst::load_constant_u64(tmp, size as u64));
570
insts.push(Inst::Call {
571
info: Box::new(CallInfo {
572
dest: ExternalName::LibCall(LibCall::Memcpy),
573
uses: smallvec![
574
CallArgPair {
575
vreg: dst,
576
preg: arg0.to_reg()
577
},
578
CallArgPair {
579
vreg: src,
580
preg: arg1.to_reg()
581
},
582
CallArgPair {
583
vreg: tmp.to_reg(),
584
preg: arg2.to_reg()
585
}
586
],
587
defs: smallvec![],
588
clobbers: Self::get_regs_clobbered_by_call(call_conv, false),
589
caller_conv: call_conv,
590
callee_conv: call_conv,
591
callee_pop_size: 0,
592
try_call_info: None,
593
}),
594
});
595
insts
596
}
597
598
fn get_number_of_spillslots_for_value(
599
rc: RegClass,
600
_target_vector_bytes: u32,
601
isa_flags: &RiscvFlags,
602
) -> u32 {
603
// We allocate in terms of 8-byte slots.
604
match rc {
605
RegClass::Int => 1,
606
RegClass::Float => 1,
607
RegClass::Vector => (isa_flags.min_vec_reg_size() / 8) as u32,
608
}
609
}
610
611
fn get_machine_env(_flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
612
static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
613
MACHINE_ENV.get_or_init(create_reg_environment)
614
}
615
616
fn get_regs_clobbered_by_call(
617
call_conv_of_callee: isa::CallConv,
618
is_exception: bool,
619
) -> PRegSet {
620
match call_conv_of_callee {
621
isa::CallConv::Tail if is_exception => ALL_CLOBBERS,
622
_ => DEFAULT_CLOBBERS,
623
}
624
}
625
626
fn compute_frame_layout(
627
_call_conv: isa::CallConv,
628
flags: &settings::Flags,
629
_sig: &Signature,
630
regs: &[Writable<RealReg>],
631
function_calls: FunctionCalls,
632
incoming_args_size: u32,
633
tail_args_size: u32,
634
stackslots_size: u32,
635
fixed_frame_storage_size: u32,
636
outgoing_args_size: u32,
637
) -> FrameLayout {
638
let mut regs: Vec<Writable<RealReg>> = regs
639
.iter()
640
.cloned()
641
.filter(|r| DEFAULT_CALLEE_SAVES.contains(r.to_reg().into()))
642
.collect();
643
644
regs.sort_unstable();
645
646
// Compute clobber size.
647
let clobber_size = compute_clobber_size(&regs);
648
649
// Compute linkage frame size.
650
let setup_area_size = if flags.preserve_frame_pointers()
651
|| function_calls != FunctionCalls::None
652
// The function arguments that are passed on the stack are addressed
653
// relative to the Frame Pointer.
654
|| incoming_args_size > 0
655
|| clobber_size > 0
656
|| fixed_frame_storage_size > 0
657
{
658
16 // FP, LR
659
} else {
660
0
661
};
662
663
// Return FrameLayout structure.
664
FrameLayout {
665
word_bytes: 8,
666
incoming_args_size,
667
tail_args_size,
668
setup_area_size,
669
clobber_size,
670
fixed_frame_storage_size,
671
stackslots_size,
672
outgoing_args_size,
673
clobbered_callee_saves: regs,
674
function_calls,
675
}
676
}
677
678
fn gen_inline_probestack(
679
insts: &mut SmallInstVec<Self::I>,
680
_call_conv: isa::CallConv,
681
frame_size: u32,
682
guard_size: u32,
683
) {
684
// Unroll at most n consecutive probes, before falling back to using a loop
685
const PROBE_MAX_UNROLL: u32 = 3;
686
687
// Calculate how many probes we need to perform. Round down, as we only
688
// need to probe whole guard_size regions we'd otherwise skip over.
689
let probe_count = frame_size / guard_size;
690
if probe_count == 0 {
691
// No probe necessary
692
return;
693
}
694
695
// Must be a caller-saved register that is not an argument.
696
let tmp = Writable::from_reg(x_reg(28)); // t3
697
698
if probe_count <= PROBE_MAX_UNROLL {
699
Self::gen_probestack_unroll(insts, tmp, guard_size, probe_count)
700
} else {
701
insts.push(Inst::StackProbeLoop {
702
guard_size,
703
probe_count,
704
tmp,
705
});
706
}
707
}
708
709
fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
710
// Use x12 as a temp if needed: clobbered, not a
711
// retval.
712
Writable::from_reg(regs::x_reg(12))
713
}
714
715
fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
716
const PAYLOAD_REGS: &'static [Reg] = &[regs::a0(), regs::a1()];
717
match call_conv {
718
isa::CallConv::SystemV | isa::CallConv::Tail => PAYLOAD_REGS,
719
_ => &[],
720
}
721
}
722
}
723
724
// NOTE: no V regs are callee save.
725
const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()
726
// X Regs
727
.with(px_reg(2))
728
.with(px_reg(8))
729
.with(px_reg(9))
730
.with(px_reg(18))
731
.with(px_reg(19))
732
.with(px_reg(20))
733
.with(px_reg(21))
734
.with(px_reg(22))
735
.with(px_reg(23))
736
.with(px_reg(24))
737
.with(px_reg(25))
738
.with(px_reg(26))
739
.with(px_reg(27))
740
// F Regs
741
.with(pf_reg(8))
742
.with(pf_reg(18))
743
.with(pf_reg(19))
744
.with(pf_reg(20))
745
.with(pf_reg(21))
746
.with(pf_reg(22))
747
.with(pf_reg(23))
748
.with(pf_reg(24))
749
.with(pf_reg(25))
750
.with(pf_reg(26))
751
.with(pf_reg(27));
752
753
fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
754
let mut clobbered_size = 0;
755
for reg in clobbers {
756
match reg.to_reg().class() {
757
RegClass::Int => {
758
clobbered_size += 8;
759
}
760
RegClass::Float => {
761
clobbered_size += 8;
762
}
763
RegClass::Vector => unimplemented!("Vector Size Clobbered"),
764
}
765
}
766
align_to(clobbered_size, 16)
767
}
768
769
const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()
770
.with(px_reg(1))
771
.with(px_reg(5))
772
.with(px_reg(6))
773
.with(px_reg(7))
774
.with(px_reg(10))
775
.with(px_reg(11))
776
.with(px_reg(12))
777
.with(px_reg(13))
778
.with(px_reg(14))
779
.with(px_reg(15))
780
.with(px_reg(16))
781
.with(px_reg(17))
782
.with(px_reg(28))
783
.with(px_reg(29))
784
.with(px_reg(30))
785
.with(px_reg(31))
786
// F Regs
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(9))
796
.with(pf_reg(10))
797
.with(pf_reg(11))
798
.with(pf_reg(12))
799
.with(pf_reg(13))
800
.with(pf_reg(14))
801
.with(pf_reg(15))
802
.with(pf_reg(16))
803
.with(pf_reg(17))
804
.with(pf_reg(28))
805
.with(pf_reg(29))
806
.with(pf_reg(30))
807
.with(pf_reg(31))
808
// V Regs - All vector regs get clobbered
809
.with(pv_reg(0))
810
.with(pv_reg(1))
811
.with(pv_reg(2))
812
.with(pv_reg(3))
813
.with(pv_reg(4))
814
.with(pv_reg(5))
815
.with(pv_reg(6))
816
.with(pv_reg(7))
817
.with(pv_reg(8))
818
.with(pv_reg(9))
819
.with(pv_reg(10))
820
.with(pv_reg(11))
821
.with(pv_reg(12))
822
.with(pv_reg(13))
823
.with(pv_reg(14))
824
.with(pv_reg(15))
825
.with(pv_reg(16))
826
.with(pv_reg(17))
827
.with(pv_reg(18))
828
.with(pv_reg(19))
829
.with(pv_reg(20))
830
.with(pv_reg(21))
831
.with(pv_reg(22))
832
.with(pv_reg(23))
833
.with(pv_reg(24))
834
.with(pv_reg(25))
835
.with(pv_reg(26))
836
.with(pv_reg(27))
837
.with(pv_reg(28))
838
.with(pv_reg(29))
839
.with(pv_reg(30))
840
.with(pv_reg(31));
841
842
const ALL_CLOBBERS: PRegSet = PRegSet::empty()
843
// Specials: x0 is the zero register; x1 is the return address; x2 is SP.
844
.with(px_reg(3))
845
.with(px_reg(4))
846
.with(px_reg(5))
847
.with(px_reg(6))
848
.with(px_reg(7))
849
.with(px_reg(8))
850
.with(px_reg(9))
851
.with(px_reg(10))
852
.with(px_reg(11))
853
.with(px_reg(12))
854
.with(px_reg(13))
855
.with(px_reg(14))
856
.with(px_reg(15))
857
.with(px_reg(16))
858
.with(px_reg(17))
859
.with(px_reg(18))
860
.with(px_reg(19))
861
.with(px_reg(20))
862
.with(px_reg(21))
863
.with(px_reg(22))
864
.with(px_reg(23))
865
.with(px_reg(24))
866
.with(px_reg(25))
867
.with(px_reg(26))
868
.with(px_reg(27))
869
.with(px_reg(28))
870
.with(px_reg(29))
871
.with(px_reg(30))
872
.with(px_reg(31))
873
// F Regs
874
.with(pf_reg(0))
875
.with(pf_reg(1))
876
.with(pf_reg(2))
877
.with(pf_reg(3))
878
.with(pf_reg(4))
879
.with(pf_reg(5))
880
.with(pf_reg(6))
881
.with(pf_reg(7))
882
.with(pf_reg(8))
883
.with(pf_reg(9))
884
.with(pf_reg(10))
885
.with(pf_reg(11))
886
.with(pf_reg(12))
887
.with(pf_reg(13))
888
.with(pf_reg(14))
889
.with(pf_reg(15))
890
.with(pf_reg(16))
891
.with(pf_reg(17))
892
.with(pf_reg(18))
893
.with(pf_reg(19))
894
.with(pf_reg(20))
895
.with(pf_reg(21))
896
.with(pf_reg(22))
897
.with(pf_reg(23))
898
.with(pf_reg(24))
899
.with(pf_reg(25))
900
.with(pf_reg(26))
901
.with(pf_reg(27))
902
.with(pf_reg(28))
903
.with(pf_reg(29))
904
.with(pf_reg(30))
905
.with(pf_reg(31))
906
// V Regs
907
.with(pv_reg(0))
908
.with(pv_reg(1))
909
.with(pv_reg(2))
910
.with(pv_reg(3))
911
.with(pv_reg(4))
912
.with(pv_reg(5))
913
.with(pv_reg(6))
914
.with(pv_reg(7))
915
.with(pv_reg(8))
916
.with(pv_reg(9))
917
.with(pv_reg(10))
918
.with(pv_reg(11))
919
.with(pv_reg(12))
920
.with(pv_reg(13))
921
.with(pv_reg(14))
922
.with(pv_reg(15))
923
.with(pv_reg(16))
924
.with(pv_reg(17))
925
.with(pv_reg(18))
926
.with(pv_reg(19))
927
.with(pv_reg(20))
928
.with(pv_reg(21))
929
.with(pv_reg(22))
930
.with(pv_reg(23))
931
.with(pv_reg(24))
932
.with(pv_reg(25))
933
.with(pv_reg(26))
934
.with(pv_reg(27))
935
.with(pv_reg(28))
936
.with(pv_reg(29))
937
.with(pv_reg(30))
938
.with(pv_reg(31));
939
940
fn create_reg_environment() -> MachineEnv {
941
// Some C Extension instructions can only use a subset of the registers.
942
// x8 - x15, f8 - f15, v8 - v15 so we should prefer to use those since
943
// they allow us to emit C instructions more often.
944
//
945
// In general the order of preference is:
946
// 1. Compressible Caller Saved registers.
947
// 2. Non-Compressible Caller Saved registers.
948
// 3. Compressible Callee Saved registers.
949
// 4. Non-Compressible Callee Saved registers.
950
951
let preferred_regs_by_class: [Vec<PReg>; 3] = {
952
let x_registers: Vec<PReg> = (10..=15).map(px_reg).collect();
953
let f_registers: Vec<PReg> = (10..=15).map(pf_reg).collect();
954
let v_registers: Vec<PReg> = (8..=15).map(pv_reg).collect();
955
956
[x_registers, f_registers, v_registers]
957
};
958
959
let non_preferred_regs_by_class: [Vec<PReg>; 3] = {
960
// x0 - x4 are special registers, so we don't want to use them.
961
// Omit x30 and x31 since they are the spilltmp registers.
962
963
// Start with the Non-Compressible Caller Saved registers.
964
let x_registers: Vec<PReg> = (5..=7)
965
.chain(16..=17)
966
.chain(28..=29)
967
// The first Callee Saved register is x9 since its Compressible
968
// Omit x8 since it's the frame pointer.
969
.chain(9..=9)
970
// The rest of the Callee Saved registers are Non-Compressible
971
.chain(18..=27)
972
.map(px_reg)
973
.collect();
974
975
// Prefer Caller Saved registers.
976
let f_registers: Vec<PReg> = (0..=7)
977
.chain(16..=17)
978
.chain(28..=31)
979
// Once those are exhausted, we should prefer f8 and f9 since they are
980
// callee saved, but compressible.
981
.chain(8..=9)
982
.chain(18..=27)
983
.map(pf_reg)
984
.collect();
985
986
let v_registers = (0..=7).chain(16..=31).map(pv_reg).collect();
987
988
[x_registers, f_registers, v_registers]
989
};
990
991
MachineEnv {
992
preferred_regs_by_class,
993
non_preferred_regs_by_class,
994
fixed_stack_slots: vec![],
995
scratch_by_class: [None, None, None],
996
}
997
}
998
999
impl Riscv64MachineDeps {
1000
fn gen_probestack_unroll(
1001
insts: &mut SmallInstVec<Inst>,
1002
tmp: Writable<Reg>,
1003
guard_size: u32,
1004
probe_count: u32,
1005
) {
1006
// When manually unrolling adjust the stack pointer and then write a zero
1007
// to the stack at that offset.
1008
//
1009
// We do this because valgrind expects us to never write beyond the stack
1010
// pointer and associated redzone.
1011
// See: https://github.com/bytecodealliance/wasmtime/issues/7454
1012
1013
// Store the adjust amount in a register upfront, so we don't have to
1014
// reload it for each probe. It's worth loading this as a negative and
1015
// using an `add` instruction since we have compressed versions of `add`
1016
// but not the `sub` instruction.
1017
insts.extend(Inst::load_constant_u64(tmp, (-(guard_size as i64)) as u64));
1018
1019
for _ in 0..probe_count {
1020
insts.push(Inst::AluRRR {
1021
alu_op: AluOPRRR::Add,
1022
rd: writable_stack_reg(),
1023
rs1: stack_reg(),
1024
rs2: tmp.to_reg(),
1025
});
1026
1027
insts.push(Inst::gen_store(
1028
AMode::SPOffset(0),
1029
zero_reg(),
1030
I32,
1031
MemFlags::trusted(),
1032
));
1033
}
1034
1035
// Restore the stack pointer to its original value
1036
insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));
1037
}
1038
}
1039
1040