223 lines
8.5 KiB
C
223 lines
8.5 KiB
C
/* spl_ir.h - SPL Intermediate Representation: instruction set and binary format
|
|
*/
|
|
|
|
#ifndef __SPL_IR_H__
|
|
#define __SPL_IR_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 = 0,
|
|
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") \
|
|
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, 1, 1, "print top of stack as hex")
|
|
|
|
/* clang-format on */
|
|
|
|
typedef enum {
|
|
#define X(opcode, name, argc, pop, push, desc) opcode,
|
|
SPL_OPCODES(X)
|
|
#undef X
|
|
} spl_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_val_t;
|
|
|
|
typedef struct spl_ins {
|
|
uint8_t opcode;
|
|
uint8_t type;
|
|
spl_val_t imm;
|
|
} spl_ins_t;
|
|
typedef VEC(spl_ins_t) spl_ins_vec_t;
|
|
|
|
typedef struct spl_func {
|
|
char *name;
|
|
spl_val_t idx_of_strtab;
|
|
spl_val_t nargs;
|
|
spl_val_t ninsns;
|
|
spl_val_t address;
|
|
} spl_func_t;
|
|
typedef VEC(spl_func_t) spl_func_vec_t;
|
|
|
|
/* Native function pointer type */
|
|
typedef spl_val_t (*spl_fn_t)(int nargs, spl_val_t *args);
|
|
typedef struct spl_native {
|
|
char *name;
|
|
spl_val_t idx_of_strtab;
|
|
spl_fn_t impl_fn;
|
|
} spl_native_t;
|
|
typedef VEC(spl_native_t) spl_native_vec_t;
|
|
|
|
typedef struct {
|
|
unsigned char *data;
|
|
usize size;
|
|
} spl_gdata_t;
|
|
typedef VEC(spl_gdata_t) spl_data_t;
|
|
typedef VEC(const char *) spl_strtab_t;
|
|
typedef MAP(const char *, const char *) spl_symtab_t;
|
|
/* Opaque handle for loaded program */
|
|
typedef struct spl_prog {
|
|
spl_ins_vec_t insns;
|
|
spl_func_vec_t funcs;
|
|
spl_native_vec_t natives;
|
|
spl_data_t gdata;
|
|
spl_strtab_t strtab;
|
|
spl_symtab_t symtab;
|
|
} 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_data(spl_prog_t *prog, void *ptr, usize size);
|
|
int spl_prog_add_func(spl_prog_t *prog, spl_func_t *func);
|
|
int spl_prog_add_native(spl_prog_t *prog, spl_native_t *native);
|
|
|
|
spl_func_t *spl_prog_get_func(spl_prog_t *prog, const char *name);
|
|
spl_native_t *spl_prog_get_native(spl_prog_t *prog, const char *name);
|
|
|
|
/* High-level program construction helpers */
|
|
spl_val_t spl_prog_emit(spl_prog_t *prog, uint16_t opcode, uint16_t type, spl_val_t imm);
|
|
int spl_prog_add_func_simple(spl_prog_t *prog, const char *name, spl_val_t nargs);
|
|
void spl_prog_end_func(spl_prog_t *prog, int func_idx);
|
|
int spl_prog_add_str(spl_prog_t *prog, const char *str);
|
|
|
|
/* Opcode name lookup for debugging/dumping */
|
|
const char *spl_opcode_name(spl_opcode_t opcode);
|
|
const char *spl_type_name(spl_type_t type);
|
|
|
|
void spl_ins_dump(spl_ins_t *ins, spl_val_t addr);
|
|
|
|
#endif /* __SPL_IR_H__ */
|