Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/winch/codegen/src/stack.rs
1692 views
1
use crate::{codegen::CodeGenError, isa::reg::Reg, masm::StackSlot};
2
use anyhow::{Result, anyhow};
3
use smallvec::SmallVec;
4
use wasmparser::{Ieee32, Ieee64};
5
use wasmtime_environ::WasmValType;
6
7
/// A typed register value used to track register values in the value
8
/// stack.
9
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
10
pub struct TypedReg {
11
/// The physical register.
12
pub reg: Reg,
13
/// The type associated to the physical register.
14
pub ty: WasmValType,
15
}
16
17
impl TypedReg {
18
/// Create a new [`TypedReg`].
19
pub fn new(ty: WasmValType, reg: Reg) -> Self {
20
Self { ty, reg }
21
}
22
23
/// Create an i64 [`TypedReg`].
24
pub fn i64(reg: Reg) -> Self {
25
Self {
26
ty: WasmValType::I64,
27
reg,
28
}
29
}
30
31
/// Create an i32 [`TypedReg`].
32
pub fn i32(reg: Reg) -> Self {
33
Self {
34
ty: WasmValType::I32,
35
reg,
36
}
37
}
38
39
/// Create an f64 [`TypedReg`].
40
pub fn f64(reg: Reg) -> Self {
41
Self {
42
ty: WasmValType::F64,
43
reg,
44
}
45
}
46
47
/// Create an f32 [`TypedReg`].
48
pub fn f32(reg: Reg) -> Self {
49
Self {
50
ty: WasmValType::F32,
51
reg,
52
}
53
}
54
55
/// Create a v128 [`TypedReg`].
56
pub fn v128(reg: Reg) -> Self {
57
Self {
58
ty: WasmValType::V128,
59
reg,
60
}
61
}
62
}
63
64
impl From<TypedReg> for Reg {
65
fn from(tr: TypedReg) -> Self {
66
tr.reg
67
}
68
}
69
70
/// A local value.
71
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
72
pub struct Local {
73
/// The index of the local.
74
pub index: u32,
75
/// The type of the local.
76
pub ty: WasmValType,
77
}
78
79
/// A memory value.
80
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
81
pub struct Memory {
82
/// The type associated with the memory offset.
83
pub ty: WasmValType,
84
/// The stack slot corresponding to the memory value.
85
pub slot: StackSlot,
86
}
87
88
/// Value definition to be used within the shadow stack.
89
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
90
pub(crate) enum Val {
91
/// I32 Constant.
92
I32(i32),
93
/// I64 Constant.
94
I64(i64),
95
/// F32 Constant.
96
F32(Ieee32),
97
/// F64 Constant.
98
F64(Ieee64),
99
/// V128 Constant.
100
V128(i128),
101
/// A register value.
102
Reg(TypedReg),
103
/// A local slot.
104
Local(Local),
105
/// Offset to a memory location.
106
Memory(Memory),
107
}
108
109
impl From<TypedReg> for Val {
110
fn from(tr: TypedReg) -> Self {
111
Val::Reg(tr)
112
}
113
}
114
115
impl From<Local> for Val {
116
fn from(local: Local) -> Self {
117
Val::Local(local)
118
}
119
}
120
121
impl From<Memory> for Val {
122
fn from(mem: Memory) -> Self {
123
Val::Memory(mem)
124
}
125
}
126
127
impl TryFrom<u32> for Val {
128
type Error = anyhow::Error;
129
fn try_from(value: u32) -> Result<Self, Self::Error> {
130
i32::try_from(value).map(Val::i32).map_err(Into::into)
131
}
132
}
133
134
impl Val {
135
/// Create a new I32 constant value.
136
pub fn i32(v: i32) -> Self {
137
Self::I32(v)
138
}
139
140
/// Create a new I64 constant value.
141
pub fn i64(v: i64) -> Self {
142
Self::I64(v)
143
}
144
145
/// Create a new F32 constant value.
146
pub fn f32(v: Ieee32) -> Self {
147
Self::F32(v)
148
}
149
150
pub fn f64(v: Ieee64) -> Self {
151
Self::F64(v)
152
}
153
154
/// Create a new V128 constant value.
155
pub fn v128(v: i128) -> Self {
156
Self::V128(v)
157
}
158
159
/// Create a new Reg value.
160
pub fn reg(reg: Reg, ty: WasmValType) -> Self {
161
Self::Reg(TypedReg { reg, ty })
162
}
163
164
/// Create a new Local value.
165
pub fn local(index: u32, ty: WasmValType) -> Self {
166
Self::Local(Local { index, ty })
167
}
168
169
/// Create a Memory value.
170
pub fn mem(ty: WasmValType, slot: StackSlot) -> Self {
171
Self::Memory(Memory { ty, slot })
172
}
173
174
/// Check whether the value is a register.
175
pub fn is_reg(&self) -> bool {
176
match *self {
177
Self::Reg(_) => true,
178
_ => false,
179
}
180
}
181
182
/// Check whether the value is a memory offset.
183
pub fn is_mem(&self) -> bool {
184
match *self {
185
Self::Memory(_) => true,
186
_ => false,
187
}
188
}
189
190
/// Check whether the value is a constant.
191
pub fn is_const(&self) -> bool {
192
match *self {
193
Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true,
194
_ => false,
195
}
196
}
197
198
/// Check whether the value is local with a particular index.
199
pub fn is_local_at_index(&self, index: u32) -> bool {
200
match *self {
201
Self::Local(Local { index: i, .. }) if i == index => true,
202
_ => false,
203
}
204
}
205
206
/// Get the register representation of the value.
207
///
208
/// # Panics
209
/// This method will panic if the value is not a register.
210
pub fn unwrap_reg(&self) -> TypedReg {
211
match self {
212
Self::Reg(tr) => *tr,
213
v => panic!("expected value {v:?} to be a register"),
214
}
215
}
216
217
/// Get the integer representation of the value.
218
///
219
/// # Panics
220
/// This method will panic if the value is not an i32.
221
pub fn unwrap_i32(&self) -> i32 {
222
match self {
223
Self::I32(v) => *v,
224
v => panic!("expected value {v:?} to be i32"),
225
}
226
}
227
228
/// Get the integer representation of the value.
229
///
230
/// # Panics
231
/// This method will panic if the value is not an i64.
232
pub fn unwrap_i64(&self) -> i64 {
233
match self {
234
Self::I64(v) => *v,
235
v => panic!("expected value {v:?} to be i64"),
236
}
237
}
238
239
/// Get the float representation of the value.
240
///
241
/// # Panics
242
/// This method will panic if the value is not an f32.
243
pub fn unwrap_f32(&self) -> Ieee32 {
244
match self {
245
Self::F32(v) => *v,
246
v => panic!("expected value {v:?} to be f32"),
247
}
248
}
249
250
/// Get the float representation of the value.
251
///
252
/// # Panics
253
/// This method will panic if the value is not an f64.
254
pub fn unwrap_f64(&self) -> Ieee64 {
255
match self {
256
Self::F64(v) => *v,
257
v => panic!("expected value {v:?} to be f64"),
258
}
259
}
260
261
/// Returns the underlying memory value if it is one, panics otherwise.
262
pub fn unwrap_mem(&self) -> Memory {
263
match self {
264
Self::Memory(m) => *m,
265
v => panic!("expected value {v:?} to be a Memory"),
266
}
267
}
268
269
/// Check whether the value is an i32 constant.
270
pub fn is_i32_const(&self) -> bool {
271
match *self {
272
Self::I32(_) => true,
273
_ => false,
274
}
275
}
276
277
/// Check whether the value is an i64 constant.
278
pub fn is_i64_const(&self) -> bool {
279
match *self {
280
Self::I64(_) => true,
281
_ => false,
282
}
283
}
284
285
/// Check whether the value is an f32 constant.
286
pub fn is_f32_const(&self) -> bool {
287
match *self {
288
Self::F32(_) => true,
289
_ => false,
290
}
291
}
292
293
/// Check whether the value is an f64 constant.
294
pub fn is_f64_const(&self) -> bool {
295
match *self {
296
Self::F64(_) => true,
297
_ => false,
298
}
299
}
300
301
/// Get the type of the value.
302
pub fn ty(&self) -> WasmValType {
303
match self {
304
Val::I32(_) => WasmValType::I32,
305
Val::I64(_) => WasmValType::I64,
306
Val::F32(_) => WasmValType::F32,
307
Val::F64(_) => WasmValType::F64,
308
Val::V128(_) => WasmValType::V128,
309
Val::Reg(r) => r.ty,
310
Val::Memory(m) => m.ty,
311
Val::Local(l) => l.ty,
312
}
313
}
314
}
315
316
/// The shadow stack used for compilation.
317
#[derive(Default, Debug)]
318
pub(crate) struct Stack {
319
// NB: The 64 is chosen arbitrarily. We can adjust as we see fit.
320
inner: SmallVec<[Val; 64]>,
321
}
322
323
impl Stack {
324
/// Allocate a new stack.
325
pub fn new() -> Self {
326
Self {
327
inner: Default::default(),
328
}
329
}
330
331
/// Ensures that there are at least `n` elements in the value stack,
332
/// and returns the index calculated by: stack length minus `n`.
333
pub fn ensure_index_at(&self, n: usize) -> Result<usize> {
334
if self.len() >= n {
335
Ok(self.len() - n)
336
} else {
337
Err(anyhow!(CodeGenError::missing_values_in_stack()))
338
}
339
}
340
341
/// Returns true if the stack contains a local with the provided index
342
/// except if the only time the local appears is the top element.
343
pub fn contains_latent_local(&self, index: u32) -> bool {
344
self.inner
345
.iter()
346
// Iterate top-to-bottom so we can skip the top element and stop
347
// when we see a memory element.
348
.rev()
349
// The local is not latent if it's the top element because the top
350
// element will be popped next which materializes the local.
351
.skip(1)
352
// Stop when we see a memory element because that marks where we
353
// spilled up to so there will not be any locals past this point.
354
.take_while(|v| !v.is_mem())
355
.any(|v| v.is_local_at_index(index))
356
}
357
358
/// Extend the stack with the given elements.
359
pub fn extend(&mut self, values: impl IntoIterator<Item = Val>) {
360
self.inner.extend(values);
361
}
362
363
/// Inserts many values at the given index.
364
pub fn insert_many(&mut self, at: usize, values: &[Val]) {
365
debug_assert!(at <= self.len());
366
367
if at == self.len() {
368
self.inner.extend_from_slice(values);
369
} else {
370
self.inner.insert_from_slice(at, values);
371
}
372
}
373
374
/// Get the length of the stack.
375
pub fn len(&self) -> usize {
376
self.inner.len()
377
}
378
379
/// Push a value to the stack.
380
pub fn push(&mut self, val: Val) {
381
self.inner.push(val);
382
}
383
384
/// Peek into the top in the stack.
385
pub fn peek(&self) -> Option<&Val> {
386
self.inner.last()
387
}
388
389
/// Returns an iterator referencing the last n items of the stack,
390
/// in bottom-most to top-most order.
391
pub fn peekn(&self, n: usize) -> impl Iterator<Item = &Val> + '_ {
392
let len = self.len();
393
assert!(n <= len);
394
395
let partition = len - n;
396
self.inner[partition..].into_iter()
397
}
398
399
/// Pops the top element of the stack, if any.
400
pub fn pop(&mut self) -> Option<Val> {
401
self.inner.pop()
402
}
403
404
/// Pops the element at the top of the stack if it is an i32 const;
405
/// returns `None` otherwise.
406
pub fn pop_i32_const(&mut self) -> Option<i32> {
407
match self.peek() {
408
Some(v) => v.is_i32_const().then(|| self.pop().unwrap().unwrap_i32()),
409
_ => None,
410
}
411
}
412
413
/// Pops the element at the top of the stack if it is an i64 const;
414
/// returns `None` otherwise.
415
pub fn pop_i64_const(&mut self) -> Option<i64> {
416
match self.peek() {
417
Some(v) => v.is_i64_const().then(|| self.pop().unwrap().unwrap_i64()),
418
_ => None,
419
}
420
}
421
422
/// Pops the element at the top of the stack if it is an f32 const;
423
/// returns `None` otherwise.
424
pub fn pop_f32_const(&mut self) -> Option<Ieee32> {
425
match self.peek() {
426
Some(v) => v.is_f32_const().then(|| self.pop().unwrap().unwrap_f32()),
427
_ => None,
428
}
429
}
430
431
/// Pops the element at the top of the stack if it is an f64 const;
432
/// returns `None` otherwise.
433
pub fn pop_f64_const(&mut self) -> Option<Ieee64> {
434
match self.peek() {
435
Some(v) => v.is_f64_const().then(|| self.pop().unwrap().unwrap_f64()),
436
_ => None,
437
}
438
}
439
440
/// Pops the element at the top of the stack if it is a register;
441
/// returns `None` otherwise.
442
pub fn pop_reg(&mut self) -> Option<TypedReg> {
443
match self.peek() {
444
Some(v) => v.is_reg().then(|| self.pop().unwrap().unwrap_reg()),
445
_ => None,
446
}
447
}
448
449
/// Pops the given register if it is at the top of the stack;
450
/// returns `None` otherwise.
451
pub fn pop_named_reg(&mut self, reg: Reg) -> Option<TypedReg> {
452
match self.peek() {
453
Some(v) => {
454
(v.is_reg() && v.unwrap_reg().reg == reg).then(|| self.pop().unwrap().unwrap_reg())
455
}
456
_ => None,
457
}
458
}
459
460
/// Get a mutable reference to the inner stack representation.
461
pub fn inner_mut(&mut self) -> &mut SmallVec<[Val; 64]> {
462
&mut self.inner
463
}
464
465
/// Get a reference to the inner stack representation.
466
pub fn inner(&self) -> &SmallVec<[Val; 64]> {
467
&self.inner
468
}
469
470
/// Calculates the size of, in bytes, of the top n [Memory] entries
471
/// in the value stack.
472
pub fn sizeof(&self, top: usize) -> u32 {
473
self.peekn(top).fold(0, |acc, v| {
474
if v.is_mem() {
475
acc + v.unwrap_mem().slot.size
476
} else {
477
acc
478
}
479
})
480
}
481
}
482
483
#[cfg(test)]
484
mod tests {
485
use super::{Stack, Val};
486
use crate::isa::reg::Reg;
487
use wasmtime_environ::WasmValType;
488
489
#[test]
490
fn test_pop_i32_const() {
491
let mut stack = Stack::new();
492
stack.push(Val::i32(33i32));
493
assert_eq!(33, stack.pop_i32_const().unwrap());
494
495
stack.push(Val::local(10, WasmValType::I32));
496
assert!(stack.pop_i32_const().is_none());
497
}
498
499
#[test]
500
fn test_pop_reg() {
501
let mut stack = Stack::new();
502
let reg = Reg::int(2usize);
503
stack.push(Val::reg(reg, WasmValType::I32));
504
stack.push(Val::i32(4));
505
506
assert_eq!(None, stack.pop_reg());
507
let _ = stack.pop().unwrap();
508
assert_eq!(reg, stack.pop_reg().unwrap().reg);
509
}
510
511
#[test]
512
fn test_pop_named_reg() {
513
let mut stack = Stack::new();
514
let reg = Reg::int(2usize);
515
stack.push(Val::reg(reg, WasmValType::I32));
516
stack.push(Val::reg(Reg::int(4), WasmValType::I32));
517
518
assert_eq!(None, stack.pop_named_reg(reg));
519
let _ = stack.pop().unwrap();
520
assert_eq!(reg, stack.pop_named_reg(reg).unwrap().reg);
521
}
522
}
523
524