/* 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 #include 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, 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_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__ */