#pragma once #include #include #include typedef enum Op { Exit, // Pop value from the stack and return as exit code. Return 0 if the // stack is empty. Push, Pop, Add, Sub, Mul, Div, Dec, // Decrement the top of the stack by 1. Empty, // Check whether the stack is empty. Pushes a bool. Cmp, // Pop the top of the stack and compare it with the payload. Pushes a // bool. /* Blocks */ End, // Marks the end of a block. Break, // Exit the current block. Loop, // Push a loop block. Payload (i32): label. /* Branches */ Br, // Branch. Payload (i64): [(i32) conditional? | (i32) label]. // A condtional branch pops a bool from the stack and branches if true. // The condition can also be negated. See br_if(). /* Functions */ Func, Arg, Call, /* Locals */ Local, // Create a local variable. LocalRd, // Load a local variable into the top of the stack. LocalWr, // Pop the top of the stack and store it in a local variable. } Op; typedef enum Type { I32, F32, } Type; // Label type for blocks and locals. typedef uint32_t Label; typedef struct Branch { Label label; bool conditional : 1; // True for conditional branches. bool expected : 1; // Comparison value for conditional branches. } Branch; typedef struct Function { Label label; } Function; typedef struct Value { union { uint64_t u64; int32_t i32; float f32; Branch branch; Label label; }; } Value; typedef struct Inst { Op op : 5; Type type : 2; Value payload; } Inst; typedef struct Vm Vm; // ----------------------------------------------------------------------------- // VM API /// Create a new virtual machine. Vm* vm_new(); /// Destroy the virtual machine. void vm_del(Vm**); /// Execute code on the virtual machine. /// /// Returns the program exit code if an exit operation is executed, 0 otherwise. int vm_run(Vm*, const Inst[], size_t count); /// Prints the virtual machine's stack to stdout. void vm_print_stack(const Vm*); // ----------------------------------------------------------------------------- // Programming API /// Exit the program. static inline Inst vmExit() { return (Inst){.op = Exit}; } /// Push a value. static inline Inst vmPushI32(int32_t value) { return (Inst){.op = Push, .type = I32, .payload = (Value){.i32 = value}}; } /// Pop a value. static inline Inst vmPop(Type type) { return (Inst){.op = Pop, .type = type}; } /// Add two values. static inline Inst vmAdd(Type type) { return (Inst){.op = Add, .type = type}; } /// Decrement a value. static inline Inst vmDec(Type type) { return (Inst){.op = Dec, .type = type}; } /// Compare a value. static inline Inst vmCmpI32(int32_t value) { return (Inst){.op = Cmp, .type = I32, .payload = (Value){.i32 = value}}; } /// End the current block. static inline Inst vmEnd() { return (Inst){.op = End}; } /// Create a loop. static inline Inst vmLoop(Label label) { return (Inst){.op = Loop, .payload = (Value){.label = label}}; } /// Create the payload of a conditional branch. static inline Inst vmBr_if(bool value, Label label) { return (Inst){ .op = Br, .payload = (Value){ .branch = { .label = label, .conditional = 1, .expected = value, }}}; } /// Create a function. static inline Inst vmFunc(Label label) { return (Inst){.op = Func, .payload = (Value){.label = label}}; } /// Create a function argument. static inline Inst vmArg(Type type, Label label) { return (Inst){.op = Arg, .type = type, .payload = (Value){.label = label}}; } /// Call a function. static inline Inst vmCall(Label label) { return (Inst){.op = Call, .payload = (Value){.label = label}}; } /// Create a local variable. static inline Inst vmLocal(Type type, Label label) { return (Inst){.op = Local, .type = type, .payload = (Value){.label = label}}; } /// Read a local variable. static inline Inst vmLocalRd(Type type, Label label) { return (Inst){ .op = LocalRd, .type = type, .payload = (Value){.label = label}}; } /// Write a local variable. static inline Inst vmLocalWr(Type type, Label label) { return (Inst){ .op = LocalWr, .type = type, .payload = (Value){.label = label}}; }