221 lines
8.6 KiB
C
221 lines
8.6 KiB
C
/* spl_mcode.h - SPL VM Machine Code: instruction set and binary format
|
||
*/
|
||
|
||
#ifndef __SPL_MCODE_H__
|
||
#define __SPL_MCODE_H__
|
||
|
||
#include "include/core_map.h"
|
||
#include "include/core_vec.h"
|
||
#include <stddef.h>
|
||
#include <stdint.h>
|
||
|
||
typedef uintptr_t usize;
|
||
typedef intptr_t isize;
|
||
|
||
typedef enum {
|
||
SPL_VOID,
|
||
SPL_BOOL,
|
||
SPL_I8,
|
||
SPL_U8,
|
||
SPL_I16,
|
||
SPL_U16,
|
||
SPL_I32,
|
||
SPL_U32,
|
||
SPL_I64,
|
||
SPL_U64,
|
||
SPL_ISIZE,
|
||
SPL_USIZE,
|
||
SPL_F32,
|
||
SPL_F64,
|
||
SPL_PTR,
|
||
SPL_TYPE_COUNT,
|
||
} spl_type_t;
|
||
|
||
/* clang-format off */
|
||
#define SPL_OPCODES(X) \
|
||
/* opcode, name, argc, pop, push, desc */ \
|
||
X(SPL_ERROR, "error", 0, 0, 0, "invalid opcode / internal error") \
|
||
/* 栈操作 */ \
|
||
X(SPL_PUSH, "push", 1, 0, 1, "push immediate") \
|
||
X(SPL_DUP, "dup", 0, 0, 1, "duplicate top of stack") \
|
||
X(SPL_DROP, "drop", 0, 1, 0, "discard top of stack") \
|
||
X(SPL_SWAP, "swap", 0, 2, 2, "swap top two elements") \
|
||
X(SPL_PICK, "pick", 1, 0, 1, "push copy of stack[imm] (0=TOS)") \
|
||
X(SPL_ROT, "rot", 0, 3, 3, "rotate top three (a b c -- b c a)") \
|
||
/* 算术 */ \
|
||
X(SPL_ADD, "add", 0, 2, 1, "integer addition") \
|
||
X(SPL_SUB, "sub", 0, 2, 1, "integer subtraction") \
|
||
X(SPL_MUL, "mul", 0, 2, 1, "integer multiplication") \
|
||
X(SPL_DIV_S, "div_s", 0, 2, 1, "signed division") \
|
||
X(SPL_DIV_U, "div_u", 0, 2, 1, "unsigned division") \
|
||
X(SPL_REM_S, "rem_s", 0, 2, 1, "signed remainder") \
|
||
X(SPL_REM_U, "rem_u", 0, 2, 1, "unsigned remainder") \
|
||
X(SPL_NEG, "neg", 0, 1, 1, "two's complement negation") \
|
||
/* 位运算 */ \
|
||
X(SPL_AND, "and", 0, 2, 1, "bitwise AND") \
|
||
X(SPL_OR, "or", 0, 2, 1, "bitwise OR") \
|
||
X(SPL_XOR, "xor", 0, 2, 1, "bitwise XOR") \
|
||
X(SPL_NOT, "not", 0, 1, 1, "bitwise NOT") \
|
||
X(SPL_SHL, "shl", 0, 2, 1, "left shift") \
|
||
X(SPL_SHR_U, "shr_u", 0, 2, 1, "logical right shift (zero-fill)") \
|
||
X(SPL_SHR_S, "shr_s", 0, 2, 1, "arithmetic right shift (sign-fill)") \
|
||
/* 比较 */ \
|
||
X(SPL_EQ, "eq", 0, 2, 1, "equal") \
|
||
X(SPL_NE, "ne", 0, 2, 1, "not equal") \
|
||
X(SPL_SLT, "slt", 0, 2, 1, "signed less than") \
|
||
X(SPL_SLE, "sle", 0, 2, 1, "signed less or equal") \
|
||
X(SPL_ULT, "ult", 0, 2, 1, "unsigned less than") \
|
||
X(SPL_ULE, "ule", 0, 2, 1, "unsigned less or equal") \
|
||
X(SPL_SGT, "sgt", 0, 2, 1, "signed greater than") \
|
||
X(SPL_SGE, "sge", 0, 2, 1, "signed greater or equal") \
|
||
X(SPL_UGT, "ugt", 0, 2, 1, "unsigned greater than") \
|
||
X(SPL_UGE, "uge", 0, 2, 1, "unsigned greater or equal") \
|
||
/* 类型转换 */ \
|
||
X(SPL_TRUNC, "trunc", 1, 1, 1, "truncate to low imm bits") \
|
||
X(SPL_SEXT, "sext", 1, 1, 1, "sign-extend from bit imm") \
|
||
X(SPL_ZEXT, "zext", 1, 1, 1, "zero-extend from bit imm") \
|
||
/* 控制流 */ \
|
||
X(SPL_JMP, "jmp", 1, 0, 0, "unconditional jump (offset isize)") \
|
||
X(SPL_BZ, "bz", 1, 1, 0, "pop; jump if zero (offset isize)") \
|
||
X(SPL_BNZ, "bnz", 1, 1, 0, "pop; jump if non-zero (offset isize)") \
|
||
X(SPL_HALT, "halt", 0, 0, 0, "stop execution") \
|
||
/* 函数调用 */ \
|
||
X(SPL_CALL, "call", 1, 0, 0, "call function pop function offset, call it") \
|
||
X(SPL_CALLI, "calli", 0, 1, 0, "indirect call: pop function address, call it") \
|
||
X(SPL_RET, "ret", 0, 0, 0, "return from function") \
|
||
/* 栈帧局部变量 */ \
|
||
X(SPL_ALLOC, "alloc", 1, 0, 0, "allocate imm zero-slots on stack") \
|
||
X(SPL_LADDR, "laddr", 1, 0, 1, "push address of local at fp + imm bytes") \
|
||
X(SPL_GADDR, "gaddr", 1, 0, 1, "push address of global at gp + imm") \
|
||
/* 间接内存访问 */ \
|
||
X(SPL_LOAD, "load", 0, 1, 1, "load sizeof(type)-bits zero-extended") \
|
||
X(SPL_STORE, "store", 0, 2, 0, "store low sizeof(type)-bits") \
|
||
/* 原生接口 */ \
|
||
X(SPL_NCALL, "ncall", 1, 0, 1, "call native function by index") \
|
||
X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \
|
||
/* 调试 */ \
|
||
X(SPL_BK, "breakpoint", 0, 0, 0, "break point when exec will stop run") \
|
||
X(SPL_DBG, "dbg", 0, 0, 0, "print VM debug info (stack, backtrace, locals)")
|
||
|
||
/* clang-format on */
|
||
|
||
typedef enum {
|
||
#define X(opcode, name, argc, pop, push, desc) opcode,
|
||
SPL_OPCODES(X)
|
||
#undef X
|
||
} spl_vm_opcode_t;
|
||
|
||
/*
|
||
* Binary format (all metadata fields spl_val_t LE):
|
||
* magic[8] = "SPLBIN\0\0"
|
||
* nfuncs, ninsns, nnatives, nstrs, ndata
|
||
* [func table] each: name_len, name(pad8), idx_of_strtab, nargs, ninsns, address
|
||
* [insns] each: opcode(2) type(2) imm(8) = 12 bytes
|
||
* [natives] each: name_len, name(pad8), idx_of_strtab
|
||
* [strtab] each: slen, str(pad8)
|
||
* [gdata] each: dsize(8), data(dsize bytes, padded to 8)
|
||
*
|
||
* === Calling Convention ===
|
||
*
|
||
* Before CALL:
|
||
* - args pushed left-to-right
|
||
* - target address pushed last
|
||
*
|
||
* CALL (imm = nargs):
|
||
* 1. pop target address
|
||
* 2. callstack[cp++] = {saved_fp, saved_ip, nargs}
|
||
* 3. fp = sp - nargs (fp points to arg0)
|
||
* 4. ip = target address
|
||
*
|
||
* ALLOC k:
|
||
* sp += k (slots zeroed; local[j] = stacks.data[fp + nargs + j])
|
||
*
|
||
* LADDR imm:
|
||
* push &stacks.data[fp + imm]
|
||
* (imm < nargs accesses args; imm >= nargs accesses locals)
|
||
*
|
||
* GADDR imm:
|
||
* push prog->gdata[imm] (pointer to global data blob)
|
||
*
|
||
* RET:
|
||
* 1. pop retval if non-void type
|
||
* 2. sp = fp
|
||
* 3. pop frame; fp = saved_fp, ip = saved_ip
|
||
* 4. push retval if non-void type
|
||
*
|
||
* CALLI (indirect call):
|
||
* - stack before: ..., arg0, ..., argN-1, nargs, func_addr
|
||
* 1. pop func_addr, then pop nargs
|
||
* 2. same as CALL steps 2-4
|
||
*/
|
||
|
||
#define SPL_BINFMT_MAGIC "SPLBIN\0\0"
|
||
// All SIR stack values are ptr-bit unsigned integers
|
||
typedef usize spl_vm_val_t;
|
||
|
||
typedef struct spl_vm_ins {
|
||
uint8_t opcode;
|
||
uint8_t type;
|
||
spl_vm_val_t imm;
|
||
} spl_vm_ins_t;
|
||
typedef VEC(spl_vm_ins_t) spl_vm_ins_vec_t;
|
||
|
||
typedef struct spl_vm_func {
|
||
char *name;
|
||
spl_vm_val_t idx_of_strtab;
|
||
spl_vm_val_t nargs;
|
||
spl_vm_val_t ninsns;
|
||
spl_vm_val_t address;
|
||
} spl_vm_func_t;
|
||
typedef VEC(spl_vm_func_t) spl_vm_func_vec_t;
|
||
|
||
/* Native function pointer type */
|
||
typedef spl_vm_val_t (*spl_vm_fn_t)(int nargs, spl_vm_val_t *args);
|
||
typedef struct spl_vm_native {
|
||
char *name;
|
||
spl_vm_val_t idx_of_strtab;
|
||
spl_vm_fn_t impl_fn;
|
||
} spl_vm_native_t;
|
||
typedef VEC(spl_vm_native_t) spl_vm_native_vec_t;
|
||
|
||
typedef struct {
|
||
unsigned char *data;
|
||
usize size;
|
||
} spl_vm_gdata_t;
|
||
typedef VEC(spl_vm_gdata_t) spl_vm_data_t;
|
||
typedef VEC(const char *) spl_vm_strtab_t;
|
||
typedef MAP(const char *, const char *) spl_vm_symtab_t;
|
||
/* Opaque handle for loaded program */
|
||
typedef struct spl_prog {
|
||
spl_vm_ins_vec_t insns;
|
||
spl_vm_func_vec_t funcs;
|
||
spl_vm_native_vec_t natives;
|
||
spl_vm_data_t gdata;
|
||
spl_vm_strtab_t strtab;
|
||
spl_vm_symtab_t symtab;
|
||
char *debug; /* 文件尾部 debug 段(splc0 -g 追加的文本),无则 NULL */
|
||
usize debug_size;
|
||
} spl_prog_t;
|
||
|
||
void spl_prog_init(spl_prog_t *prog);
|
||
void spl_prog_drop(spl_prog_t *prog);
|
||
|
||
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog);
|
||
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog);
|
||
|
||
int spl_prog_add_instr(spl_prog_t *prog, uint8_t opcode, uint8_t type, spl_vm_val_t imm);
|
||
int spl_prog_add_data(spl_prog_t *prog, void *ptr, usize size);
|
||
int spl_prog_add_func(spl_prog_t *prog, spl_vm_func_t *func);
|
||
int spl_prog_add_native(spl_prog_t *prog, spl_vm_native_t *native);
|
||
|
||
spl_vm_func_t *spl_prog_get_func(spl_prog_t *prog, const char *name);
|
||
spl_vm_native_t *spl_prog_get_native(spl_prog_t *prog, const char *name);
|
||
|
||
/* Opcode name lookup for debugging/dumping */
|
||
const char *spl_vm_opcode_name(spl_vm_opcode_t opcode);
|
||
const char *spl_vm_type_kind_name(spl_type_t type);
|
||
|
||
void spl_vm_ins_dump(spl_vm_ins_t *ins, spl_vm_val_t addr);
|
||
|
||
#endif /* __SPL_MCODE_H__ */
|