Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::wasm::indices::{FuncIndex, GlobalIndex, TypeIndex};
9use anyhow::bail;
10use smallvec::SmallVec;
11
12/// A constant expression.
13///
14/// These are used to initialize globals, table elements, etc...
15#[derive(Clone, Debug, Eq, PartialEq, Hash)]
16pub struct ConstExpr {
17 ops: SmallVec<[ConstOp; 2]>,
18}
19
20impl ConstExpr {
21 /// Create a new const expression from a `wasmparser` const expression.
22 ///
23 /// Returns the new const expression as well as the escaping function
24 /// indices that appeared in `ref.func` instructions, if any.
25 pub fn from_wasmparser(
26 expr: &wasmparser::ConstExpr<'_>,
27 ) -> crate::Result<(Self, SmallVec<[FuncIndex; 1]>)> {
28 let mut iter = expr
29 .get_operators_reader()
30 .into_iter_with_offsets()
31 .peekable();
32
33 let mut ops = SmallVec::<[ConstOp; 2]>::new();
34 let mut escaped = SmallVec::<[FuncIndex; 1]>::new();
35 while let Some(res) = iter.next() {
36 let (op, offset) = res?;
37
38 // If we reach an `end` instruction, and there are no more
39 // instructions after that, then we are done reading this const
40 // expression.
41 if matches!(op, wasmparser::Operator::End) && iter.peek().is_none() {
42 break;
43 }
44
45 // Track any functions that appear in `ref.func` so that callers can
46 // make sure to flag them as escaping.
47 if let wasmparser::Operator::RefFunc { function_index } = &op {
48 escaped.push(FuncIndex::from_u32(*function_index));
49 }
50
51 ops.push(ConstOp::from_wasmparser(op, offset)?);
52 }
53 Ok((Self { ops }, escaped))
54 }
55
56 pub fn ops(&self) -> impl ExactSizeIterator<Item = ConstOp> + use<'_> {
57 self.ops.iter().copied()
58 }
59}
60
61/// The subset of Wasm opcodes that are constant.
62#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
63pub enum ConstOp {
64 I32Const(i32),
65 I64Const(i64),
66 F32Const(u32),
67 F64Const(u64),
68 V128Const(u128),
69 GlobalGet(GlobalIndex),
70 RefI31,
71 RefNull,
72 RefFunc(FuncIndex),
73 I32Add,
74 I32Sub,
75 I32Mul,
76 I64Add,
77 I64Sub,
78 I64Mul,
79 StructNew {
80 struct_type_index: TypeIndex,
81 },
82 StructNewDefault {
83 struct_type_index: TypeIndex,
84 },
85 ArrayNew {
86 array_type_index: TypeIndex,
87 },
88 ArrayNewDefault {
89 array_type_index: TypeIndex,
90 },
91 ArrayNewFixed {
92 array_type_index: TypeIndex,
93 array_size: u32,
94 },
95}
96
97impl ConstOp {
98 /// Convert a `wasmparser::Operator` to a `ConstOp`.
99 pub fn from_wasmparser(op: wasmparser::Operator<'_>, offset: usize) -> crate::Result<Self> {
100 use wasmparser::Operator as O;
101 Ok(match op {
102 O::I32Const { value } => Self::I32Const(value),
103 O::I64Const { value } => Self::I64Const(value),
104 O::F32Const { value } => Self::F32Const(value.bits()),
105 O::F64Const { value } => Self::F64Const(value.bits()),
106 O::V128Const { value } => Self::V128Const(u128::from_le_bytes(*value.bytes())),
107 O::RefNull { hty: _ } => Self::RefNull,
108 O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
109 O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
110 O::RefI31 => Self::RefI31,
111 O::I32Add => Self::I32Add,
112 O::I32Sub => Self::I32Sub,
113 O::I32Mul => Self::I32Mul,
114 O::I64Add => Self::I64Add,
115 O::I64Sub => Self::I64Sub,
116 O::I64Mul => Self::I64Mul,
117 O::StructNew { struct_type_index } => Self::StructNew {
118 struct_type_index: TypeIndex::from_u32(struct_type_index),
119 },
120 O::StructNewDefault { struct_type_index } => Self::StructNewDefault {
121 struct_type_index: TypeIndex::from_u32(struct_type_index),
122 },
123 O::ArrayNew { array_type_index } => Self::ArrayNew {
124 array_type_index: TypeIndex::from_u32(array_type_index),
125 },
126 O::ArrayNewDefault { array_type_index } => Self::ArrayNewDefault {
127 array_type_index: TypeIndex::from_u32(array_type_index),
128 },
129 O::ArrayNewFixed {
130 array_type_index,
131 array_size,
132 } => Self::ArrayNewFixed {
133 array_type_index: TypeIndex::from_u32(array_type_index),
134 array_size,
135 },
136 op => {
137 bail!("unsupported opcode in const expression at offset {offset:#x}: {op:?}");
138 }
139 })
140 }
141}