Files
spl/stage0/spl_mcode.h

221 lines
8.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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_vm_kind_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_vm_kind_t type);
void spl_vm_ins_dump(spl_vm_ins_t *ins, spl_vm_val_t addr);
#endif /* __SPL_MCODE_H__ */