use wasm_gen;
use wasm_gen::{FuncCode, Imm};
use crate::{heap, Builtin, Symbol, PYTHON_STACK_POINTER};
pub fn generate_push() -> wasm_gen::Func {
wasm_gen::Func {
sig: wasm_gen::FuncType {
params: vec![wasm_gen::I32],
results: vec![],
},
locals: vec![],
code: vec![
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)),
FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackGrow as i32)),
FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(0)),
FuncCode::new2(wasm_gen::I32_STORE, Imm::I64(0x0), Imm::I64(0x0)),
],
}
}
pub fn push(symbol: Symbol) -> Vec<FuncCode> {
match symbol {
Symbol::FuncElem(_, _) => {
panic!();
}
Symbol::Data(offset) => {
let mut code = vec![];
code.append(&mut vec![
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(offset as i32)),
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(heap::TAG_DATA as i32)),
FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::BoxValue as i32)),
]);
code.append(&mut vec![FuncCode::new1(
wasm_gen::CALL,
Imm::I32(Builtin::PythonStackPush as i32),
)]);
code
}
Symbol::HeapObject(offset) => vec![
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(offset as i32)),
FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackPush as i32)),
],
Symbol::WasmTopOfStack => vec![FuncCode::new1(
wasm_gen::CALL,
Imm::I32(Builtin::PythonStackPush as i32),
)],
Symbol::Global(idx) => vec![
FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(idx as i32)),
FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackPush as i32)),
],
Symbol::None => vec![
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(33)),
FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackPush as i32)),
],
Symbol::Intrinsic(code) => code,
Symbol::Int32(n) => vec![
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(n)),
FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackPush as i32)),
],
e => unimplemented!("pushing symbol {:?}", e),
}
}
pub fn generate_grow() -> wasm_gen::Func {
let code = vec![
FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(0)),
FuncCode::new0(wasm_gen::I32_ADD),
FuncCode::new1(wasm_gen::GLOBAL_SET, Imm::I32(PYTHON_STACK_POINTER)),
];
wasm_gen::Func {
sig: wasm_gen::FuncType {
params: vec![wasm_gen::I32],
results: vec![wasm_gen::I32],
},
locals: vec![],
code,
}
}
pub fn generate_stack_size() -> wasm_gen::Func {
let code = vec![
FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)),
FuncCode::new0(wasm_gen::I32_DIV_S),
];
wasm_gen::Func {
sig: wasm_gen::FuncType {
params: vec![wasm_gen::I32],
results: vec![wasm_gen::I32],
},
locals: vec![],
code,
}
}
pub fn generate_shrink() -> wasm_gen::Func {
let code = vec![
FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(0)),
FuncCode::new0(wasm_gen::I32_SUB),
FuncCode::new1(wasm_gen::LOCAL_TEE, Imm::I32(1)),
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(0)),
FuncCode::new0(wasm_gen::I32_GE_S),
FuncCode::new0(wasm_gen::I32_EQZ),
FuncCode::new_control(wasm_gen::IF, wasm_gen::NONE),
FuncCode::new1(
wasm_gen::CALL,
Imm::I32(Builtin::TrapPythonStackUnderflow as i32),
),
FuncCode::new0(wasm_gen::END),
FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(1)),
FuncCode::new1(wasm_gen::GLOBAL_SET, Imm::I32(PYTHON_STACK_POINTER)),
];
wasm_gen::Func {
sig: wasm_gen::FuncType {
params: vec![wasm_gen::I32],
results: vec![],
},
locals: vec![
(1, wasm_gen::I32),
],
code,
}
}
pub fn generate_pop() -> wasm_gen::Func {
let mut code = vec![];
code.append(&mut vec![
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)),
FuncCode::new1(wasm_gen::CALL, Imm::I32(Builtin::PythonStackShrink as i32)),
FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
FuncCode::new2(wasm_gen::I32_LOAD, Imm::I64(0x0), Imm::I64(0x0)),
]);
wasm_gen::Func {
sig: wasm_gen::FuncType {
params: vec![],
results: vec![wasm_gen::I32],
},
locals: vec![],
code,
}
}
pub fn generate_peak_last_n() -> wasm_gen::Func {
wasm_gen::Func {
sig: wasm_gen::FuncType {
params: vec![wasm_gen::I32],
results: vec![wasm_gen::I32],
},
locals: vec![],
code: vec![
FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)),
FuncCode::new1(wasm_gen::LOCAL_GET, Imm::I32(0)),
FuncCode::new0(wasm_gen::I32_MUL),
FuncCode::new0(wasm_gen::I32_SUB),
FuncCode::new2(wasm_gen::I32_LOAD, Imm::I64(0x0), Imm::I64(0x0)),
],
}
}
pub fn generate_peak_last() -> wasm_gen::Func {
wasm_gen::Func {
sig: wasm_gen::FuncType {
params: vec![],
results: vec![wasm_gen::I32],
},
locals: vec![],
code: vec![
FuncCode::new1(wasm_gen::GLOBAL_GET, Imm::I32(PYTHON_STACK_POINTER)),
FuncCode::new1(wasm_gen::I32_CONST, Imm::I32(4)),
FuncCode::new0(wasm_gen::I32_SUB),
FuncCode::new2(wasm_gen::I32_LOAD, Imm::I64(0x0), Imm::I64(0x0)),
],
}
}
pub fn dump() -> Vec<FuncCode> {
vec![
FuncCode::new1(wasm_gen::CALL, Imm::I64(0)),
FuncCode::new0(wasm_gen::UNREACHABLE),
]
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use wasmi::RuntimeValue;
use super::*;
use crate::{build_builtins, build_globals, build_imports, build_types, test_runner};
use wasm_gen::WasmCodeGen;
fn test_module() -> WasmCodeGen {
let mut wasm_module = WasmCodeGen::new();
wasm_module.add_table(wasm_gen::TableElemType::Funcref, 10, 10);
let mem = wasm_module.add_memory(10, 64 * 100);
wasm_module.add_export("mem".to_string(), mem, wasm_gen::ExportType::Mem);
let mut end_of_data_offset = 8;
build_types(&mut wasm_module);
build_globals(&mut wasm_module);
build_imports(
&mut wasm_module,
&mut end_of_data_offset,
&mut HashMap::new(),
);
build_builtins(&mut wasm_module);
wasm_module.add_export(
"push_data".to_string(),
Builtin::PythonStackPush as usize,
wasm_gen::ExportType::Func,
);
wasm_module.add_export(
"pop".to_string(),
Builtin::PythonStackPop as usize,
wasm_gen::ExportType::Func,
);
wasm_module.add_export(
"size".to_string(),
Builtin::PythonStackSize as usize,
wasm_gen::ExportType::Func,
);
wasm_module
}
#[test]
fn test_stack() {
let instance = test_runner::instantiate(test_module());
let v1 = RuntimeValue::I32(1);
test_runner::call(&instance, "push_data", &[v1]);
let v2 = RuntimeValue::I32(2);
test_runner::call(&instance, "push_data", &[v2]);
let r1 = test_runner::call(&instance, "pop", &[]);
assert_eq!(r1.unwrap(), v2);
let r2 = test_runner::call(&instance, "pop", &[]);
assert_eq!(r2.unwrap(), v1);
}
#[test]
fn test_stack_size() {
let instance = test_runner::instantiate(test_module());
let size0 = test_runner::call(&instance, "size", &[]);
assert_eq!(size0.unwrap(), RuntimeValue::I32(0));
test_runner::call(&instance, "push_data", &[RuntimeValue::I32(1)]);
let size1 = test_runner::call(&instance, "size", &[]);
assert_eq!(size1.unwrap(), RuntimeValue::I32(1));
test_runner::call(&instance, "push_data", &[RuntimeValue::I32(2)]);
let size2 = test_runner::call(&instance, "size", &[]);
assert_eq!(size2.unwrap(), RuntimeValue::I32(2));
}
#[test]
fn test_pop_stack_underflow() {
let instance = test_runner::instantiate(test_module());
test_runner::call(&instance, "push_data", &[RuntimeValue::I32(7)]);
test_runner::call(&instance, "pop", &[]);
let trap = test_runner::call_trap(&instance, "pop", &[]);
match trap.kind() {
wasmi::TrapKind::Unreachable => { }
_ => panic!(),
};
}
#[test]
fn test_push_stack_overflow() {
let instance = test_runner::instantiate(test_module());
test_runner::call(&instance, "push_data", &[RuntimeValue::I32(7)]);
test_runner::call(&instance, "push_data", &[RuntimeValue::I32(8)]);
let trap = test_runner::call_trap(&instance, "push_data", &[RuntimeValue::I32(9)]);
match trap.kind() {
wasmi::TrapKind::Unreachable => { }
_ => panic!(),
};
}
}