1use wasm_gen;
2use wasm_gen::{FuncCode, Imm};
3
4use crate::{heap, Builtin, Symbol, PYTHON_STACK_POINTER};
5
6pub fn generate_push() -> wasm_gen::Func {
10 wasm_gen::Func {
11 sig: wasm_gen::FuncType {
12 params: vec![wasm_gen::I32], results: vec![],
14 },
15 locals: vec![],
16 code: vec![
17 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)),
18 FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackGrow as i32)),
19 FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(0)),
20 FuncCode::new2(wasm_gen::I32_STORE, Imm::I64(0x0), Imm::I64(0x0)),
21 ],
22 }
23}
24
25pub fn push(symbol: Symbol) -> Vec<FuncCode> {
26 match symbol {
27 Symbol::FuncElem(_, _) => {
28 panic!();
29 }
30 Symbol::Data(offset) => {
31 let mut code = vec![];
32
33 code.append(&mut vec![
35 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(offset as i32)),
36 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(heap::TAG_DATA as i32)),
37 FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::BoxValue as i32)),
38 ]);
39 code.append(&mut vec![FuncCode::new1(
41 wasm_gen::CALL,
42 Imm::I32(Builtin::PythonStackPush as i32),
43 )]);
44
45 code
46 }
47 Symbol::HeapObject(offset) => vec![
48 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(offset as i32)),
49 FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackPush as i32)),
50 ],
51 Symbol::WasmTopOfStack => vec![FuncCode::new1(
52 wasm_gen::CALL,
53 Imm::I32(Builtin::PythonStackPush as i32),
54 )],
55 Symbol::Global(idx) => vec![
56 FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(idx as i32)),
57 FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackPush as i32)),
58 ],
59 Symbol::None => vec![
60 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(33)),
61 FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackPush as i32)),
62 ],
63 Symbol::Intrinsic(code) => code,
64 Symbol::Int32(n) => vec![
65 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(n)),
66 FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackPush as i32)),
67 ],
68 e => unimplemented!("pushing symbol {:?}", e),
69 }
70}
71
72pub fn generate_grow() -> wasm_gen::Func {
78 let code = vec![
79 FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
81 FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
82 FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(0)),
83 FuncCode::new0(wasm_gen::I32_ADD),
84 FuncCode::new1(wasm_gen::GLOBAL_SET, Imm::I32(PYTHON_STACK_POINTER)),
85 ];
86 wasm_gen::Func {
87 sig: wasm_gen::FuncType {
88 params: vec![wasm_gen::I32], results: vec![wasm_gen::I32], },
91 locals: vec![],
92 code,
93 }
94}
95
96pub fn generate_stack_size() -> wasm_gen::Func {
98 let code = vec![
99 FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
100 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)), FuncCode::new0(wasm_gen::I32_DIV_S),
102 ];
103 wasm_gen::Func {
104 sig: wasm_gen::FuncType {
105 params: vec![wasm_gen::I32],
106 results: vec![wasm_gen::I32], },
108 locals: vec![],
109 code,
110 }
111}
112
113pub fn generate_shrink() -> wasm_gen::Func {
121 let code = vec![
122 FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
123 FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(0)),
124 FuncCode::new0(wasm_gen::I32_SUB),
125 FuncCode::new1(wasm_gen::LOCAL_TEE, Imm::I32(1)),
126 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(0)),
128 FuncCode::new0(wasm_gen::I32_GE_S),
129 FuncCode::new0(wasm_gen::I32_EQZ),
130 FuncCode::new_control(wasm_gen::IF, wasm_gen::NONE),
131 FuncCode::new1(
133 wasm_gen::CALL,
134 Imm::I32(Builtin::TrapPythonStackUnderflow as i32),
135 ),
136 FuncCode::new0(wasm_gen::END),
138 FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(1)),
140 FuncCode::new1(wasm_gen::GLOBAL_SET, Imm::I32(PYTHON_STACK_POINTER)),
141 ];
142
143 wasm_gen::Func {
144 sig: wasm_gen::FuncType {
145 params: vec![wasm_gen::I32], results: vec![],
147 },
148 locals: vec![
149 (1, wasm_gen::I32), ],
151 code,
152 }
153}
154
155pub fn generate_pop() -> wasm_gen::Func {
156 let mut code = vec![];
157 code.append(&mut vec![
158 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)),
159 FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackShrink as i32)),
160 FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
161 FuncCode::new2(wasm_gen::I32_LOAD, Imm::I64(0x0), Imm::I64(0x0)),
162 ]);
163
164 wasm_gen::Func {
165 sig: wasm_gen::FuncType {
166 params: vec![],
167 results: vec![wasm_gen::I32],
168 },
169 locals: vec![],
170 code,
171 }
172}
173
174pub fn generate_peak_last_n() -> wasm_gen::Func {
176 wasm_gen::Func {
177 sig: wasm_gen::FuncType {
178 params: vec![wasm_gen::I32], results: vec![wasm_gen::I32], },
181 locals: vec![],
182 code: vec![
183 FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
184 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)), FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(0)),
186 FuncCode::new0(wasm_gen::I32_MUL),
187 FuncCode::new0(wasm_gen::I32_SUB),
188 FuncCode::new2(wasm_gen::I32_LOAD, Imm::I64(0x0), Imm::I64(0x0)),
189 ],
190 }
191}
192
193pub fn generate_peak_last() -> wasm_gen::Func {
195 wasm_gen::Func {
196 sig: wasm_gen::FuncType {
197 params: vec![],
198 results: vec![wasm_gen::I32], },
200 locals: vec![],
201 code: vec![
202 FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
203 FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)), FuncCode::new0(wasm_gen::I32_SUB),
205 FuncCode::new2(wasm_gen::I32_LOAD, Imm::I64(0x0), Imm::I64(0x0)),
206 ],
207 }
208}
209
210pub fn dump() -> Vec<FuncCode> {
212 vec![
213 FuncCode::new1(wasm_gen::CALL, Imm::I64(0)), FuncCode::new0(wasm_gen::UNREACHABLE),
215 ]
216}
217
218#[cfg(test)]
219mod tests {
220 use std::collections::HashMap;
221 use wasmi::RuntimeValue;
222
223 use super::*;
224 use crate::{build_builtins, build_globals, build_imports, build_types, test_runner};
225 use wasm_gen::WasmCodeGen;
226
227 fn test_module() -> WasmCodeGen {
228 let mut wasm_module = WasmCodeGen::new();
229 wasm_module.add_table(wasm_gen::TableElemType::Funcref, 10, 10);
230 let mem = wasm_module.add_memory(10, 64 * 100);
231 wasm_module.add_export("mem".to_string(), mem, wasm_gen::ExportType::Mem);
232
233 let mut end_of_data_offset = 8;
235
236 build_types(&mut wasm_module);
237 build_globals(&mut wasm_module);
238 build_imports(
239 &mut wasm_module,
240 &mut end_of_data_offset,
241 &mut HashMap::new(),
242 );
243 build_builtins(&mut wasm_module);
244
245 wasm_module.add_export(
246 "push_data".to_string(),
247 Builtin::PythonStackPush as usize,
248 wasm_gen::ExportType::Func,
249 );
250 wasm_module.add_export(
251 "pop".to_string(),
252 Builtin::PythonStackPop as usize,
253 wasm_gen::ExportType::Func,
254 );
255 wasm_module.add_export(
256 "size".to_string(),
257 Builtin::PythonStackSize as usize,
258 wasm_gen::ExportType::Func,
259 );
260
261 wasm_module
262 }
263
264 #[test]
265 fn test_stack() {
266 let instance = test_runner::instantiate(test_module());
267
268 let v1 = RuntimeValue::I32(1);
269 test_runner::call(&instance, "push_data", &[v1]);
270 let v2 = RuntimeValue::I32(2);
271 test_runner::call(&instance, "push_data", &[v2]);
272
273 let r1 = test_runner::call(&instance, "pop", &[]);
274 assert_eq!(r1.unwrap(), v2);
275 let r2 = test_runner::call(&instance, "pop", &[]);
276 assert_eq!(r2.unwrap(), v1);
277 }
278
279 #[test]
280 fn test_stack_size() {
281 let instance = test_runner::instantiate(test_module());
282
283 let size0 = test_runner::call(&instance, "size", &[]);
284 assert_eq!(size0.unwrap(), RuntimeValue::I32(0));
285
286 test_runner::call(&instance, "push_data", &[RuntimeValue::I32(1)]);
287 let size1 = test_runner::call(&instance, "size", &[]);
288 assert_eq!(size1.unwrap(), RuntimeValue::I32(1));
289
290 test_runner::call(&instance, "push_data", &[RuntimeValue::I32(2)]);
291 let size2 = test_runner::call(&instance, "size", &[]);
292 assert_eq!(size2.unwrap(), RuntimeValue::I32(2));
293 }
294
295 #[test]
296 fn test_pop_stack_underflow() {
297 let instance = test_runner::instantiate(test_module());
298
299 test_runner::call(&instance, "push_data", &[RuntimeValue::I32(7)]);
300 test_runner::call(&instance, "pop", &[]);
301 let trap = test_runner::call_trap(&instance, "pop", &[]);
302
303 match trap.kind() {
304 wasmi::TrapKind::Unreachable => { }
305 _ => panic!(),
306 };
307 }
308
309 #[test]
310 fn test_push_stack_overflow() {
311 let instance = test_runner::instantiate(test_module());
312
313 test_runner::call(&instance, "push_data", &[RuntimeValue::I32(7)]);
314 test_runner::call(&instance, "push_data", &[RuntimeValue::I32(8)]);
315 let trap = test_runner::call_trap(&instance, "push_data", &[RuntimeValue::I32(9)]);
316
317 match trap.kind() {
318 wasmi::TrapKind::Unreachable => { }
319 _ => panic!(),
320 };
321 }
322}