Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/winch/codegen/src/abi/mod.rs
1692 views
1
//!
2
//! The Default ABI
3
//!
4
//! Winch uses a default ABI, for all internal functions. This allows
5
//! us to push the complexity of system ABI compliance to the trampolines. The
6
//! default ABI treats all allocatable registers as caller saved, which means
7
//! that (i) all register values in the Wasm value stack (which are normally
8
//! referred to as "live"), must be saved onto the machine stack (ii) function
9
//! prologues and epilogues don't store/restore other registers more than the
10
//! non-allocatable ones (e.g. rsp/rbp in x86_64).
11
//!
12
//! The calling convention in the default ABI, uses registers to a certain fixed
13
//! count for arguments and return values, and then the stack is used for all
14
//! additional arguments and return values. Aside from the parameters declared
15
//! in each WebAssembly function, Winch's ABI declares two extra parameters, to
16
//! hold the callee and caller `VMContext` pointers. A well-known `LocalSlot` is
17
//! reserved for the callee VMContext pointer and also a particular pinned
18
//! register is used to hold the value of the callee `VMContext`, which is
19
//! available throughout the lifetime of the function.
20
//!
21
//!
22
//! Generally the stack layout looks like:
23
//! +-------------------------------+
24
//! | |
25
//! | |
26
//! | Stack Args |
27
//! | |
28
//! | |
29
//! +-------------------------------+----> SP @ function entry
30
//! | Ret addr |
31
//! +-------------------------------+
32
//! | SP |
33
//! +-------------------------------+----> SP @ Function prologue
34
//! | |
35
//! +-------------------------------+----> VMContext slot
36
//! | |
37
//! | |
38
//! | Stack slots |
39
//! | + dynamic space |
40
//! | |
41
//! | |
42
//! | |
43
//! +-------------------------------+----> SP @ callsite (after)
44
//! | alignment |
45
//! | + arguments |
46
//! | | ----> Space allocated for calls
47
//! | |
48
use crate::codegen::ptr_type_from_ptr_size;
49
use crate::isa::{CallingConvention, reg::Reg};
50
use crate::masm::SPOffset;
51
use anyhow::Result;
52
use smallvec::SmallVec;
53
use std::collections::HashSet;
54
use std::ops::{Add, BitAnd, Not, Sub};
55
use wasmtime_environ::{WasmFuncType, WasmValType};
56
57
pub(crate) mod local;
58
pub(crate) use local::*;
59
60
/// Internal classification for params or returns,
61
/// mainly used for params and return register assignment.
62
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
63
pub(super) enum ParamsOrReturns {
64
Params,
65
Returns,
66
}
67
68
/// Macro to get the pinned register holding the [VMContext].
69
macro_rules! vmctx {
70
($m:ident) => {
71
<$m::ABI as $crate::abi::ABI>::vmctx_reg()
72
};
73
}
74
75
pub(crate) use vmctx;
76
77
/// Constructs an [ABISig] using Winch's ABI.
78
pub(crate) fn wasm_sig<A: ABI>(ty: &WasmFuncType) -> Result<ABISig> {
79
// 6 is used semi-arbitrarily here, we can modify as we see fit.
80
let mut params: SmallVec<[WasmValType; 6]> = SmallVec::new();
81
params.extend_from_slice(&vmctx_types::<A>());
82
params.extend_from_slice(ty.params());
83
84
A::sig_from(&params, ty.returns(), &CallingConvention::Default)
85
}
86
87
/// Returns the callee and caller [VMContext] types.
88
pub(crate) fn vmctx_types<A: ABI>() -> [WasmValType; 2] {
89
[A::ptr_type(), A::ptr_type()]
90
}
91
92
/// Trait implemented by a specific ISA and used to provide
93
/// information about alignment, parameter passing, usage of
94
/// specific registers, etc.
95
pub(crate) trait ABI {
96
/// The required stack alignment.
97
fn stack_align() -> u8;
98
99
/// The required stack alignment for calls.
100
fn call_stack_align() -> u8;
101
102
/// The offset to the argument base, relative to the frame pointer.
103
fn arg_base_offset() -> u8;
104
105
/// The initial size in bytes of the function's frame.
106
///
107
/// This amount is constant and accounts for all the stack space allocated
108
/// at the frame setup.
109
fn initial_frame_size() -> u8;
110
111
/// Construct the ABI-specific signature from a WebAssembly
112
/// function type.
113
#[cfg(test)]
114
fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> Result<ABISig> {
115
Self::sig_from(wasm_sig.params(), wasm_sig.returns(), call_conv)
116
}
117
118
/// Construct an ABI signature from WasmType params and returns.
119
fn sig_from(
120
params: &[WasmValType],
121
returns: &[WasmValType],
122
call_conv: &CallingConvention,
123
) -> Result<ABISig>;
124
125
/// Construct [`ABIResults`] from a slice of [`WasmType`].
126
fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> Result<ABIResults>;
127
128
/// Returns the number of bits in a word.
129
fn word_bits() -> u8;
130
131
/// Returns the number of bytes in a word.
132
fn word_bytes() -> u8 {
133
Self::word_bits() / 8
134
}
135
136
/// Returns the pinned register used to hold
137
/// the `VMContext`.
138
fn vmctx_reg() -> Reg;
139
140
/// The size, in bytes, of each stack slot used for stack parameter passing.
141
fn stack_slot_size() -> u8;
142
143
/// Returns the size in bytes of the given [`WasmType`].
144
fn sizeof(ty: &WasmValType) -> u8;
145
146
/// The target pointer size represented as [WasmValType].
147
fn ptr_type() -> WasmValType {
148
// Defaulting to 64, since we currently only support 64-bit
149
// architectures.
150
WasmValType::I64
151
}
152
}
153
154
/// ABI-specific representation of function argument or result.
155
#[derive(Clone, Debug)]
156
pub enum ABIOperand {
157
/// A register [`ABIOperand`].
158
Reg {
159
/// The type of the [`ABIOperand`].
160
ty: WasmValType,
161
/// Register holding the [`ABIOperand`].
162
reg: Reg,
163
/// The size of the [`ABIOperand`], in bytes.
164
size: u32,
165
},
166
/// A stack [`ABIOperand`].
167
Stack {
168
/// The type of the [`ABIOperand`].
169
ty: WasmValType,
170
/// Offset of the operand referenced through FP by the callee and
171
/// through SP by the caller.
172
offset: u32,
173
/// The size of the [`ABIOperand`], in bytes.
174
size: u32,
175
},
176
}
177
178
impl ABIOperand {
179
/// Allocate a new register [`ABIOperand`].
180
pub fn reg(reg: Reg, ty: WasmValType, size: u32) -> Self {
181
Self::Reg { reg, ty, size }
182
}
183
184
/// Allocate a new stack [`ABIOperand`].
185
pub fn stack_offset(offset: u32, ty: WasmValType, size: u32) -> Self {
186
Self::Stack { ty, offset, size }
187
}
188
189
/// Is this [`ABIOperand`] in a register.
190
pub fn is_reg(&self) -> bool {
191
match *self {
192
ABIOperand::Reg { .. } => true,
193
_ => false,
194
}
195
}
196
197
/// Unwraps the underlying register if it is one.
198
///
199
/// # Panics
200
/// This function panics if the [`ABIOperand`] is not a register.
201
pub fn unwrap_reg(&self) -> Reg {
202
match self {
203
ABIOperand::Reg { reg, .. } => *reg,
204
_ => unreachable!(),
205
}
206
}
207
}
208
209
/// Information about the [`ABIOperand`] information used in [`ABISig`].
210
#[derive(Clone, Debug)]
211
pub(crate) struct ABIOperands {
212
/// All the operands.
213
pub inner: SmallVec<[ABIOperand; 6]>,
214
/// All the registers used as operands.
215
pub regs: HashSet<Reg>,
216
/// Stack bytes used by the operands.
217
pub bytes: u32,
218
}
219
220
impl Default for ABIOperands {
221
fn default() -> Self {
222
Self {
223
inner: Default::default(),
224
regs: HashSet::with_capacity(0),
225
bytes: 0,
226
}
227
}
228
}
229
230
/// Machine stack location of the stack results.
231
#[derive(Debug, Copy, Clone)]
232
pub(crate) enum RetArea {
233
/// Addressed from the stack pointer at the given offset.
234
SP(SPOffset),
235
/// The address of the results base is stored at a particular,
236
/// well known [LocalSlot].
237
Slot(LocalSlot),
238
/// The return area cannot be fully resolved ahead-of-time.
239
/// If there are results on the stack, this is the default state to which
240
/// all return areas get initialized to until they can be fully resolved to
241
/// either a [RetArea::SP] or [RetArea::Slot].
242
///
243
/// This allows a more explicit differentiation between the existence of
244
/// a return area versus no return area at all.
245
Uninit,
246
}
247
248
impl Default for RetArea {
249
fn default() -> Self {
250
Self::Uninit
251
}
252
}
253
254
impl RetArea {
255
/// Create a [RetArea] addressed from SP at the given offset.
256
pub fn sp(offs: SPOffset) -> Self {
257
Self::SP(offs)
258
}
259
260
/// Create a [RetArea] addressed stored at the given [LocalSlot].
261
pub fn slot(local: LocalSlot) -> Self {
262
Self::Slot(local)
263
}
264
265
/// Returns the [SPOffset] used as the base of the return area.
266
///
267
/// # Panics
268
/// This function panics if the return area doesn't hold a [SPOffset].
269
pub fn unwrap_sp(&self) -> SPOffset {
270
match self {
271
Self::SP(offs) => *offs,
272
_ => unreachable!(),
273
}
274
}
275
276
/// Returns true if the return area is addressed via the stack pointer.
277
pub fn is_sp(&self) -> bool {
278
match self {
279
Self::SP(_) => true,
280
_ => false,
281
}
282
}
283
284
/// Returns true if the return area is uninitialized.
285
pub fn is_uninit(&self) -> bool {
286
match self {
287
Self::Uninit => true,
288
_ => false,
289
}
290
}
291
}
292
293
/// ABI-specific representation of an [`ABISig`].
294
#[derive(Clone, Debug, Default)]
295
pub(crate) struct ABIResults {
296
/// The result operands.
297
operands: ABIOperands,
298
/// The return area, if there are results on the stack.
299
ret_area: Option<RetArea>,
300
}
301
302
impl ABIResults {
303
/// Creates [`ABIResults`] from a slice of `WasmType`.
304
/// This function maps the given return types to their ABI specific
305
/// representation. It does so, by iterating over them and applying the
306
/// given `map` closure. The map closure takes a [WasmValType], maps its ABI
307
/// representation, according to the calling convention. In the case of
308
/// results, one result is stored in registers and the rest at particular
309
/// offsets in the stack.
310
pub fn from<F>(
311
returns: &[WasmValType],
312
call_conv: &CallingConvention,
313
mut map: F,
314
) -> Result<Self>
315
where
316
F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,
317
{
318
if returns.len() == 0 {
319
return Ok(Self::default());
320
}
321
322
type FoldTuple = (SmallVec<[ABIOperand; 6]>, HashSet<Reg>, u32);
323
type FoldTupleResult = Result<FoldTuple>;
324
325
let fold_impl =
326
|(mut operands, mut regs, stack_bytes): FoldTuple, arg| -> FoldTupleResult {
327
let (operand, bytes) = map(arg, stack_bytes)?;
328
if operand.is_reg() {
329
regs.insert(operand.unwrap_reg());
330
}
331
operands.push(operand);
332
Ok((operands, regs, bytes))
333
};
334
335
// When dealing with multiple results, Winch's calling convention stores the
336
// last return value in a register rather than the first one. In that
337
// sense, Winch's return values in the ABI signature are "reversed" in
338
// terms of storage. This technique is particularly helpful to ensure that
339
// the following invariants are maintained:
340
// * Spilled memory values always precede register values
341
// * Spilled values are stored from oldest to newest, matching their
342
// respective locations on the machine stack.
343
let (mut operands, regs, bytes) = if call_conv.is_default() {
344
returns
345
.iter()
346
.rev()
347
.try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?
348
} else {
349
returns
350
.iter()
351
.try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?
352
};
353
354
// Similar to above, we reverse the result of the operands calculation
355
// to ensure that they match the declared order.
356
if call_conv.is_default() {
357
operands.reverse();
358
}
359
360
Ok(Self::new(ABIOperands {
361
inner: operands,
362
regs,
363
bytes,
364
}))
365
}
366
367
/// Create a new [`ABIResults`] from [`ABIOperands`].
368
pub fn new(operands: ABIOperands) -> Self {
369
let ret_area = (operands.bytes > 0).then(|| RetArea::default());
370
Self { operands, ret_area }
371
}
372
373
/// Returns a reference to a [HashSet<Reg>], which includes
374
/// all the registers used to hold function results.
375
pub fn regs(&self) -> &HashSet<Reg> {
376
&self.operands.regs
377
}
378
379
/// Get a slice over all the result [`ABIOperand`]s.
380
pub fn operands(&self) -> &[ABIOperand] {
381
&self.operands.inner
382
}
383
384
/// Returns the length of the result.
385
pub fn len(&self) -> usize {
386
self.operands.inner.len()
387
}
388
389
/// Returns the length of results on the stack.
390
pub fn stack_operands_len(&self) -> usize {
391
self.operands().len() - self.regs().len()
392
}
393
394
/// Get the [`ABIOperand`] result in the nth position.
395
#[cfg(test)]
396
pub fn get(&self, n: usize) -> Option<&ABIOperand> {
397
self.operands.inner.get(n)
398
}
399
400
/// Returns the first [`ABIOperand`].
401
/// Useful in situations where the function signature is known to
402
/// have a single return.
403
///
404
/// # Panics
405
/// This function panics if the function signature contains more
406
pub fn unwrap_singleton(&self) -> &ABIOperand {
407
debug_assert_eq!(self.len(), 1);
408
&self.operands.inner[0]
409
}
410
411
/// Returns the size, in bytes of all the [`ABIOperand`]s in the stack.
412
pub fn size(&self) -> u32 {
413
self.operands.bytes
414
}
415
416
/// Returns true if the [`ABIResults`] require space on the machine stack
417
/// for results.
418
pub fn on_stack(&self) -> bool {
419
self.operands.bytes > 0
420
}
421
422
/// Set the return area of the signature.
423
///
424
/// # Panics
425
///
426
/// This function will panic if trying to set a return area if there are
427
/// no results on the stack or if trying to set an uninitialize return area.
428
/// This method must only be used when the return area can be fully
429
/// materialized.
430
pub fn set_ret_area(&mut self, area: RetArea) {
431
debug_assert!(self.on_stack());
432
debug_assert!(!area.is_uninit());
433
self.ret_area = Some(area);
434
}
435
436
/// Returns a reference to the return area, if any.
437
pub fn ret_area(&self) -> Option<&RetArea> {
438
self.ret_area.as_ref()
439
}
440
}
441
442
/// ABI-specific representation of an [`ABISig`].
443
#[derive(Debug, Clone, Default)]
444
pub(crate) struct ABIParams {
445
/// The param operands.
446
operands: ABIOperands,
447
/// Whether [`ABIParams`] contains an extra parameter for the stack
448
/// result area.
449
has_retptr: bool,
450
}
451
452
impl ABIParams {
453
/// Creates [`ABIParams`] from a slice of `WasmType`.
454
/// This function maps the given param types to their ABI specific
455
/// representation. It does so, by iterating over them and applying the
456
/// given `map` closure. The map closure takes a [WasmType], maps its ABI
457
/// representation, according to the calling convention. In the case of
458
/// params, multiple params may be passed in registers and the rest on the
459
/// stack depending on the calling convention.
460
pub fn from<F, A: ABI>(
461
params: &[WasmValType],
462
initial_bytes: u32,
463
needs_stack_results: bool,
464
mut map: F,
465
) -> Result<Self>
466
where
467
F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,
468
{
469
if params.len() == 0 && !needs_stack_results {
470
return Ok(Self::with_bytes(initial_bytes));
471
}
472
473
let register_capacity = params.len().min(6);
474
let mut operands = SmallVec::new();
475
let mut regs = HashSet::with_capacity(register_capacity);
476
let mut stack_bytes = initial_bytes;
477
478
let ptr_type = ptr_type_from_ptr_size(<A as ABI>::word_bytes());
479
// Handle stack results by specifying an extra, implicit first argument.
480
let stack_results = if needs_stack_results {
481
let (operand, bytes) = map(&ptr_type, stack_bytes)?;
482
if operand.is_reg() {
483
regs.insert(operand.unwrap_reg());
484
}
485
stack_bytes = bytes;
486
Some(operand)
487
} else {
488
None
489
};
490
491
for arg in params.iter() {
492
let (operand, bytes) = map(arg, stack_bytes)?;
493
if operand.is_reg() {
494
regs.insert(operand.unwrap_reg());
495
}
496
operands.push(operand);
497
stack_bytes = bytes;
498
}
499
500
if let Some(operand) = stack_results {
501
// But still push the operand for stack results last as that is what
502
// the rest of the code expects.
503
operands.push(operand);
504
}
505
506
Ok(Self {
507
operands: ABIOperands {
508
inner: operands,
509
regs,
510
bytes: stack_bytes,
511
},
512
has_retptr: needs_stack_results,
513
})
514
}
515
516
/// Creates new [`ABIParams`], with the specified amount of stack bytes.
517
pub fn with_bytes(bytes: u32) -> Self {
518
let mut params = Self::default();
519
params.operands.bytes = bytes;
520
params
521
}
522
523
/// Get the [`ABIOperand`] param in the nth position.
524
#[cfg(test)]
525
pub fn get(&self, n: usize) -> Option<&ABIOperand> {
526
self.operands.inner.get(n)
527
}
528
529
/// Get a slice over all the parameter [`ABIOperand`]s.
530
pub fn operands(&self) -> &[ABIOperand] {
531
&self.operands.inner
532
}
533
534
/// Returns the length of the params, including the return pointer,
535
/// if any.
536
pub fn len(&self) -> usize {
537
self.operands.inner.len()
538
}
539
540
/// Returns the length of the params, excluding the return pointer,
541
/// if any.
542
pub fn len_without_retptr(&self) -> usize {
543
if self.has_retptr {
544
self.len() - 1
545
} else {
546
self.len()
547
}
548
}
549
550
/// Returns true if the [ABISig] has an extra parameter for stack results.
551
pub fn has_retptr(&self) -> bool {
552
self.has_retptr
553
}
554
555
/// Returns the last [ABIOperand] used as the pointer to the
556
/// stack results area.
557
///
558
/// # Panics
559
/// This function panics if the [ABIParams] doesn't have a stack results
560
/// parameter.
561
pub fn unwrap_results_area_operand(&self) -> &ABIOperand {
562
debug_assert!(self.has_retptr);
563
self.operands.inner.last().unwrap()
564
}
565
}
566
567
/// An ABI-specific representation of a function signature.
568
#[derive(Debug, Clone)]
569
pub(crate) struct ABISig {
570
/// Function parameters.
571
pub params: ABIParams,
572
/// Function result.
573
pub results: ABIResults,
574
/// A unique set of registers used in the entire [`ABISig`].
575
pub regs: HashSet<Reg>,
576
/// Calling convention used.
577
pub call_conv: CallingConvention,
578
}
579
580
impl Default for ABISig {
581
fn default() -> Self {
582
Self {
583
params: Default::default(),
584
results: Default::default(),
585
regs: Default::default(),
586
call_conv: CallingConvention::Default,
587
}
588
}
589
}
590
591
impl ABISig {
592
/// Create a new ABI signature.
593
pub fn new(cc: CallingConvention, params: ABIParams, results: ABIResults) -> Self {
594
let regs = params
595
.operands
596
.regs
597
.union(&results.operands.regs)
598
.copied()
599
.collect();
600
Self {
601
params,
602
results,
603
regs,
604
call_conv: cc,
605
}
606
}
607
608
/// Returns an iterator over all the parameter operands.
609
pub fn params(&self) -> &[ABIOperand] {
610
self.params.operands()
611
}
612
613
/// Returns an iterator over all the result operands.
614
pub fn results(&self) -> &[ABIOperand] {
615
self.results.operands()
616
}
617
618
/// Returns a slice over the signature params, excluding the results
619
/// base parameter, if any.
620
pub fn params_without_retptr(&self) -> &[ABIOperand] {
621
if self.params.has_retptr() {
622
&self.params()[0..(self.params.len() - 1)]
623
} else {
624
self.params()
625
}
626
}
627
628
/// Returns the stack size, in bytes, needed for arguments on the stack.
629
pub fn params_stack_size(&self) -> u32 {
630
self.params.operands.bytes
631
}
632
633
/// Returns the stack size, in bytes, needed for results on the stack.
634
pub fn results_stack_size(&self) -> u32 {
635
self.results.operands.bytes
636
}
637
638
/// Returns true if the signature has results on the stack.
639
pub fn has_stack_results(&self) -> bool {
640
self.results.on_stack()
641
}
642
}
643
644
/// Align a value up to the given power-of-two-alignment.
645
// See https://sites.google.com/site/theoryofoperatingsystems/labs/malloc/align8
646
pub(crate) fn align_to<N>(value: N, alignment: N) -> N
647
where
648
N: Not<Output = N>
649
+ BitAnd<N, Output = N>
650
+ Add<N, Output = N>
651
+ Sub<N, Output = N>
652
+ From<u8>
653
+ Copy,
654
{
655
let alignment_mask = alignment - 1.into();
656
(value + alignment_mask) & !alignment_mask
657
}
658
659
/// Calculates the delta needed to adjust a function's frame plus some
660
/// addend to a given alignment.
661
pub(crate) fn calculate_frame_adjustment(frame_size: u32, addend: u32, alignment: u32) -> u32 {
662
let total = frame_size + addend;
663
(alignment - (total % alignment)) % alignment
664
}
665
666