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