# Ideas
This is to document some of my crazy ideas for this project.
## Threaded interpreter
The normal ways (computed gotos, tail-calls) to optimize VM opcode execution
[do not work in Rust](https://pliniker.github.io/post/dispatchers/).
Crazy ideas:
1. Each opcode as a closure/function. Still using a loop to call each opcode.
2. Each opcode as a closure that also has a reference to the next opcode.
3. Compile opcodes to nested closures. All opcodes have a static number of inputs and outputs.
### Compile opcodes into closures
A closure can be made for each opcode of a function to capture the opcode's parameters
(jump target, local/global idx, mem offset, etc..).
It could also allow merging multiple opcodes into a single closure.
Convert (2 push, 2 pop, 1 push):
* GetLocal(0) - push local onto stack.
* I64Const(1234) - push constant '1234' onto stack.
* I64Add - pop two i64 values, push results (a + b)
```rust
/// opcode functions for i64 opcodes.
mod i64_ops {
fn op_add_local_const(ctx: &Frame, local: u32, const_val: i64) -> Trap<()> {
let left = ctx.get_local(local);
let right = const_val;
let res = left.wrapping_add(right);
ctx.push(res)
Ok(())
}
/// many more "merged" opcode functions.....
}
```
The compiler would make a closure:
```rust
let local_idx = 0; // decoded from 'GetLocal(0)' op
let const_val = 1234; // decoded from 'I64Const(1234)' op
let merged_op = move |ctx: &Frame| -> Trap<()> {
i64_ops::op_add_local_const(ctx, local_idx, const_val)
};
```
## Structure
* Module - immutable
* Function - immutable
* CodeBlock - immutable
* Instruction - immutable
* VM - mutable
### Module
An Module defines the static code for a program/script. It contains a list of Functions.
Can have an optional `start` Function that is executed before any exported functions can
be called.
Layout:
* Meta data{version, total size, # globals, # functions, min size required to start execution}
* Export/globals
* One or more Functions. Allow stream execution.
### Function
* Type
* Locals
* Body - Intructions.
### CodeBlock
* Meta{# locals, # instructions}
### Instruction
* ... many standard ops: Add,Sub,Mul,Load,Store,etc...
* Call - Call another Function inside the Module.
* TailCall - Optimized version of `Call`.
* RustCall - Call non-async Rust function. Easy to use for API bindings.
* AsyncCall - Async call.
#### Call/TailCall
These are optimizations to avoid creating a boxed (heap allocated) future.
#### RustCall
For simple library functions. Don't allow it to call functions in the Module.
#### AsyncCall
Need to used a `BoxFuture` to avoid recursive async calls.
```rust
use futures::future::{BoxFuture, FutureExt};
fn recursive() -> BoxFuture<'static, ()> {
async move {
recursive().await;
recursive().await;
}.boxed()
}
```
### CallStack
Rust's async support could be used to make the VM stackless without the need for
an internal callstack or C-style coroutine (task/stacklet/etc...).
Stack of:
* FunctionCtx - VM Function context(function idx, pc, locals)
* Future - Rust Future
### VM
The execution context for a Module. The VM holds an immutable reference to the Module.
### Stack free execution
Loading/executing the Module in a VM should be async.
Load:
* Create a Module by compiling/loading it from a file. Should support async I/O.
* Create a VM from the Module. (VM is mutable, Module is immutable). doesn't block.
Call Function: Rust -> VM
* VM.call(name). async. Can yield from