stage0 更改名称spl_ir -> spl_mcode spl_cli提供debug模式 提供临时语言规范
This commit is contained in:
@@ -4,23 +4,32 @@
|
||||
* Built-in syscalls are auto-registered via spl_syscall_register().
|
||||
*
|
||||
* Usage:
|
||||
* spl_cli <file.sir> [entry_point]
|
||||
* spl_cli [-d] <file.sir> [entry_point]
|
||||
*/
|
||||
|
||||
#include "spl_ir.h"
|
||||
#include "spl_mcode.h"
|
||||
#include "spl_syscall.h"
|
||||
#include "spl_vm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: spl_cli <file.sir> [entry_point]\n");
|
||||
int debug_mode = 0;
|
||||
int arg_idx = 1;
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "-d") == 0) {
|
||||
debug_mode = 1;
|
||||
arg_idx = 2;
|
||||
}
|
||||
|
||||
if (arg_idx >= argc) {
|
||||
fprintf(stderr, "Usage: spl_cli [-d] <file.sir> [entry_point]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *path = argv[1];
|
||||
const char *entry = argc >= 3 ? argv[2] : "main";
|
||||
const char *path = argv[arg_idx];
|
||||
const char *entry = argc >= arg_idx + 2 ? argv[arg_idx + 1] : "main";
|
||||
|
||||
spl_prog_t prog;
|
||||
if (spl_prog_load_from_file(path, &prog) != 0) {
|
||||
@@ -32,6 +41,7 @@ int main(int argc, const char **argv) {
|
||||
|
||||
spl_vm_t vm;
|
||||
spl_vm_init(&vm);
|
||||
if (debug_mode) spl_vm_set_debug(&vm, 1);
|
||||
if (spl_vm_load_prog(&vm, &prog) != 0) {
|
||||
fprintf(stderr, "vm: prog '%s' not found\n", entry);
|
||||
spl_prog_drop(&prog);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Usage: spl_disasm <file.sir>
|
||||
*/
|
||||
|
||||
#include "spl_ir.h"
|
||||
#include "spl_mcode.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* spl_ir.c — SIR binary serialization, deserialization, and utilities
|
||||
/* spl_mcode.c — SPL VM machine code binary serialization, deserialization, and utilities
|
||||
*
|
||||
* Binary format (all metadata fields are spl_val_t = uint64_t LE):
|
||||
* [HEADER] magic(8) nfuncs(8) ninsns(8) nnatives(8) nstrs(8) ndata(8)
|
||||
@@ -9,7 +9,7 @@
|
||||
* [STRTAB] each: slen(8) str(slen bytes, padded to 8)
|
||||
*/
|
||||
|
||||
#include "spl_ir.h"
|
||||
#include "spl_mcode.h"
|
||||
|
||||
void spl_prog_init(spl_prog_t *prog) {
|
||||
if (!prog)
|
||||
@@ -445,12 +445,10 @@ const char *opcode_name[] = {
|
||||
const char *spl_opcode_name(spl_opcode_t opcode) { return opcode_name[opcode]; }
|
||||
const char *spl_type_tag_name(spl_type_t type) {
|
||||
switch (type) {
|
||||
case SPL_VOID:
|
||||
return "void";
|
||||
case SPL_I8:
|
||||
return "i8";
|
||||
case SPL_U8:
|
||||
return "u8";
|
||||
case SPL_VOID: return "void";
|
||||
case SPL_BOOL: return "bool";
|
||||
case SPL_I8: return "i8";
|
||||
case SPL_U8: return "u8";
|
||||
case SPL_I16:
|
||||
return "i16";
|
||||
case SPL_U16:
|
||||
@@ -1,8 +1,8 @@
|
||||
/* spl_ir.h - SPL Intermediate Representation: instruction set and binary format
|
||||
/* spl_mcode.h - SPL VM Machine Code: instruction set and binary format
|
||||
*/
|
||||
|
||||
#ifndef __SPL_IR_H__
|
||||
#define __SPL_IR_H__
|
||||
#ifndef __SPL_MCODE_H__
|
||||
#define __SPL_MCODE_H__
|
||||
|
||||
#include "include/core_map.h"
|
||||
#include "include/core_vec.h"
|
||||
@@ -14,6 +14,7 @@ typedef intptr_t isize;
|
||||
|
||||
typedef enum {
|
||||
SPL_VOID,
|
||||
SPL_BOOL,
|
||||
SPL_I8,
|
||||
SPL_U8,
|
||||
SPL_I16,
|
||||
@@ -220,4 +221,4 @@ const char *spl_type_tag_name(spl_type_t type);
|
||||
|
||||
void spl_ins_dump(spl_ins_t *ins, spl_val_t addr);
|
||||
|
||||
#endif /* __SPL_IR_H__ */
|
||||
#endif /* __SPL_MCODE_H__ */
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "spl_syscall.h"
|
||||
#include "include/core_map.h"
|
||||
#include "include/core_vec.h"
|
||||
#include "spl_ir.h"
|
||||
#include "spl_mcode.h"
|
||||
#include "spl_vm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -128,7 +128,7 @@ static spl_val_t vm_read_file(int nargs, spl_val_t *args) {
|
||||
const char *path = (const char *)(uintptr_t)args[0];
|
||||
if (path == nullptr) {
|
||||
fprintf(stderr, "filepath can't be null");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#ifndef __SPL_SYSCALL_H__
|
||||
#define __SPL_SYSCALL_H__
|
||||
|
||||
#include "spl_ir.h"
|
||||
#include "spl_mcode.h"
|
||||
|
||||
/* Register all known built-in syscalls into prog->natives[].
|
||||
* Entries whose name matches a known syscall get their impl_fn set;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "spl_vm.h"
|
||||
#include "spl_ir.h"
|
||||
#include "spl_mcode.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -45,6 +45,7 @@ static int spl_type_size(spl_type_t t) {
|
||||
switch (t) {
|
||||
case SPL_VOID:
|
||||
return 0;
|
||||
case SPL_BOOL:
|
||||
case SPL_I8:
|
||||
case SPL_U8:
|
||||
return 1;
|
||||
@@ -80,6 +81,16 @@ static int spl_type_size(spl_type_t t) {
|
||||
return -1; \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_ADDR(addr, label) do { \
|
||||
if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \
|
||||
fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", \
|
||||
label, vm->ip - 1, (void*)(uintptr_t)(addr), vm->sp, vm->fp); \
|
||||
spl_vm_stackdump(vm, vm->sp); \
|
||||
spl_vm_backtrace(vm, vm->fp); \
|
||||
vm->exit_code = 1; return -1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* ================================================================
|
||||
* Stack push/pop (stacks.data is pre-allocated in init)
|
||||
* ================================================================ */
|
||||
@@ -547,6 +558,7 @@ void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
|
||||
vm->prog = NULL;
|
||||
vm->trace = 0;
|
||||
vm->debug = 1;
|
||||
vm->debug_addr = 0;
|
||||
vm->exit_code = 0;
|
||||
}
|
||||
|
||||
@@ -584,6 +596,7 @@ void spl_vm_set_debug(spl_vm_t *vm, int enabled) {
|
||||
if (!vm)
|
||||
return;
|
||||
vm->debug = enabled ? 1 : 0;
|
||||
vm->debug_addr = enabled ? 1 : 0;
|
||||
}
|
||||
|
||||
#define STACK_CANARY(vm) (vm)->stacks.data[(vm)->fp - 1]
|
||||
@@ -922,6 +935,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
/* ========== Indirect Memory (load/store with types) ========== */
|
||||
case SPL_LOAD: {
|
||||
void *_addr = (void *)POP();
|
||||
CHECK_ADDR(_addr, "LOAD");
|
||||
spl_val_t _v = 0;
|
||||
usize _sz = spl_type_size(ins->type);
|
||||
memcpy(&_v, _addr, _sz);
|
||||
@@ -936,6 +950,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
case SPL_STORE: {
|
||||
spl_val_t _v = POP();
|
||||
void *_addr = (void *)POP();
|
||||
CHECK_ADDR(_addr, "STORE");
|
||||
memcpy(_addr, &_v, spl_type_size(ins->type));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define __SPL_VM_H__
|
||||
|
||||
#include "include/core_vec.h"
|
||||
#include "spl_ir.h"
|
||||
#include "spl_mcode.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define SPL_STACK_CANARY ((spl_val_t)0xDEADBEEFCAFEBABEull)
|
||||
@@ -30,6 +30,7 @@ typedef struct {
|
||||
int exit_code;
|
||||
int trace; /* non-zero to print each instruction */
|
||||
int debug; /* non-zero to enable canary checks */
|
||||
int debug_addr; /* non-zero to check for low-address memory access */
|
||||
spl_prog_t *prog;
|
||||
char error_msg[1024];
|
||||
struct {
|
||||
|
||||
452
stage1/spl.md
452
stage1/spl.md
@@ -1,452 +0,0 @@
|
||||
# SPL — 语法与语义
|
||||
|
||||
## 概述
|
||||
|
||||
类 C 语法的系统编程语言,受 Rust/Zig 启发。编译为 SIR(SPL 中间表示),一种基于栈的字节码。多阶段引导:
|
||||
|
||||
```
|
||||
C → splc0(C) → splc1(SPL) → splc2(SPL) → ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 词法结构
|
||||
|
||||
### 注释
|
||||
```
|
||||
// 行注释
|
||||
/* 块注释 */
|
||||
```
|
||||
|
||||
### 标识符
|
||||
`[a-zA-Z_][a-zA-Z0-9_]*`
|
||||
|
||||
### 关键字
|
||||
```
|
||||
as asm bool break catch comptime const continue
|
||||
defer else enum errdefer false fn for
|
||||
if loop match null ret struct
|
||||
test true try type union var void
|
||||
while _
|
||||
```
|
||||
|
||||
### 字面量
|
||||
|
||||
| 种类 | 示例 | 类型 |
|
||||
|-------------|---------------------------------|---------|
|
||||
| 整数 | `42` `0xFF` `0b1010` `0o77` | i32 |
|
||||
| 字符 | `'A'` `'\n'` `'\\'` | i32 |
|
||||
| 字符串 | `"hello"` `"line\n"` | i8* |
|
||||
| 布尔 | `true` `false` | bool |
|
||||
| 空 | `null` | null/0/undefinded |
|
||||
|
||||
### 运算符
|
||||
|
||||
| 类别 | 符号 |
|
||||
|------------|------------------------------------------------------------|
|
||||
| 算术 | `+` `-` `*` `/` `%` |
|
||||
| 位运算 | `&` `\|` `^` `~` `<<` `>>` |
|
||||
| 比较 | `==` `!=` `<` `<=` `>` `>=` |
|
||||
| 逻辑 | `&&` `\|\|` `!` |
|
||||
| 赋值 | `=` `+=` `-=` `*=` `/=` `%=` `&=` `\|=` `^=` `<<=` `>>=` |
|
||||
| 其他 | `&`(取地址) `*`(解引用) `.` `->` `<-` `..` `...` `:` `:=` `?` |
|
||||
|
||||
---
|
||||
|
||||
## 类型系统
|
||||
|
||||
### 基本类型
|
||||
|
||||
| 名称 | SIR 类型 | 大小(字节) |
|
||||
|--------|-------------|-------------|
|
||||
| void | SPL_VOID | 0 |
|
||||
| bool | SPL_I32 | 4 |
|
||||
| i8 | SPL_I8 | 1 |
|
||||
| u8 | SPL_U8 | 1 |
|
||||
| i16 | SPL_I16 | 2 |
|
||||
| u16 | SPL_U16 | 2 |
|
||||
| i32 | SPL_I32 | 4 |
|
||||
| u32 | SPL_U32 | 4 |
|
||||
| i64 | SPL_I64 | 8 |
|
||||
| u64 | SPL_U64 | 8 |
|
||||
| isize | SPL_ISIZE | sizeof(ptr) |
|
||||
| usize | SPL_USIZE | sizeof(ptr) |
|
||||
| f32 | SPL_F32 | 4 |
|
||||
| f64 | SPL_F64 | 8 |
|
||||
| ptr | SPL_PTR | 8 |
|
||||
| _ | SPL_ ... | (推断) |
|
||||
|
||||
`_` 是通配符类型——用作类型推断的占位符。在大多数声明上下文中无效。
|
||||
|
||||
### 指针类型
|
||||
写作 `*T`。示例:`*i32`、`*u8`、`*void`。
|
||||
|
||||
同类型指针会去重。
|
||||
|
||||
### 数组类型
|
||||
写作 `[N]T`。示例:`[10]i32`。数组是值类型(存在于栈槽或全局数据中)。`T` 可以是任意类型。
|
||||
|
||||
数组/复合类型 在VM堆上申请内存 `N * sizeof(T)` 。
|
||||
|
||||
### 切片类型
|
||||
写作 `[]T`。示例:`[]i32`、`[]u8`。切片是数组的一段连续视图,底层实现为胖指针:
|
||||
|
||||
```
|
||||
[]T = struct { ptr: *T, len: i32 }
|
||||
```
|
||||
|
||||
栈上占 **2 个槽位**(指针 + 长度)。通过切片表达式从数组创建:
|
||||
|
||||
```
|
||||
var arr: [10]i32 = ...;
|
||||
var slice: []i32 = arr[3..7]; // 取 arr[3..7) 视图
|
||||
var full: []i32 = arr[0..]; // 省略结束值 = 到末尾
|
||||
```
|
||||
|
||||
此外,切片也可以从另一个切片再切片得到,长度限制为原切片的 len。
|
||||
|
||||
### 聚合类型
|
||||
```
|
||||
type Name = struct { field: Type, ... };
|
||||
type Name = union { field: Type, ... };
|
||||
type Name = enum { A, B, C, ... };
|
||||
```
|
||||
|
||||
- **struct**:字段按顺序排列(默认由 SPL 定义布局,可通过 `#[extern("vm")]` 覆盖)
|
||||
- **union**:所有字段共享同一偏移(sizeof = 最大字段大小)
|
||||
- **enum**:无字段,值为从 0 开始的整数常量
|
||||
|
||||
聚合类型在堆上分配,如果需要栈分配那么栈上占用 `size` 个槽位(struct 为各字段大小之和,union 为最大字段大小,enum 为 1)。
|
||||
|
||||
`type Name = ExistingType;` 形式用于定义简单类型别名,但目前仅支持聚合类型的别名定义。
|
||||
|
||||
### 聚合类型拓展与match
|
||||
```
|
||||
type Expr = enum {
|
||||
Int: i32,
|
||||
Add: struct { left: *Expr, right: *Expr },
|
||||
fn eval(self: *Expr) i32 {
|
||||
match self {
|
||||
.Int(val) => ret val,
|
||||
.Add(left, right) => ret eval(left) + eval(right),
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
}
|
||||
|
||||
type Point = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
|
||||
type Test = enum {
|
||||
TestEnum0,
|
||||
TestEnum1
|
||||
}
|
||||
|
||||
fn init(x: i32, y: i32) Point {
|
||||
ret Point { .x = x, .y = y };
|
||||
}
|
||||
|
||||
fn dump(self: *Point) void {
|
||||
vm_printf("Point: %d %d", self.x, self.y);
|
||||
}
|
||||
}
|
||||
```
|
||||
这里面包括聚合类型声明除了成员以外任何东西,即整个编译模块也属于聚合类型,将类型抽象化。
|
||||
其中这里面还包括调用时支持第一个参数self匹配类型时自动填充。
|
||||
暂时默认全部pub即全部暴露没有不暴露的。
|
||||
|
||||
复合enum支持match等语法解包
|
||||
```
|
||||
match self {
|
||||
.Int(val) => ret val,
|
||||
.Add(left, right) => ret eval(left) + eval(right),
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 声明
|
||||
|
||||
### 函数
|
||||
```
|
||||
fn name(param: Type, ...) ReturnType {
|
||||
body...
|
||||
}
|
||||
|
||||
fn name(param: Type, ...) ReturnType; // 前向声明一般来说不需要
|
||||
```
|
||||
|
||||
函数参数在栈上按声明顺序从左到右排列。参数通过 `LADDR fp+idx` 访问。
|
||||
|
||||
### 原生函数(VM 互操作)
|
||||
```
|
||||
#[extern("vm")]
|
||||
fn vm_function(arg: Type, ...) ReturnType;
|
||||
```
|
||||
声明一个可通过 NCALL 调用的外部 VM 函数。运行时须链接或通过 `spl_syscall_register()` 注册实现。
|
||||
|
||||
### 类型别名
|
||||
```
|
||||
type Name = struct/union/enum { ... };
|
||||
type Name = ExistingType;
|
||||
```
|
||||
|
||||
### 变量
|
||||
```
|
||||
var name: Type = expr;
|
||||
```
|
||||
|
||||
变量在当前函数栈帧上分配空间。声明时若带 `= expr` 则将表达式值存入栈槽。
|
||||
|
||||
### 常量
|
||||
```
|
||||
const name: Type = expr;
|
||||
```
|
||||
|
||||
在 splc0 中常量也被分配栈空间(行为与 var 相同)。编译时可求值的常量会被记录到 `consts` 映射表,允许在局部作用域中引用。
|
||||
|
||||
### 短声明
|
||||
```
|
||||
var name := expr; // 类型推断为 expr 的 type
|
||||
const name := const_expr; // 类型推断为 expr 的 type
|
||||
```
|
||||
语法糖:声明变量/常量并立即赋初始值,类型从表达式推断。
|
||||
|
||||
---
|
||||
|
||||
## 内置指令(@ 前缀)
|
||||
|
||||
以 `@` 开头的标识符用于编译器内置操作:
|
||||
|
||||
### @import
|
||||
```
|
||||
@import("path")
|
||||
```
|
||||
在编译时导入另一个 SPL 源文件。路径相对于当前源文件目录。用于模块化编译。
|
||||
|
||||
### @sizeof
|
||||
```
|
||||
@sizeof(T)
|
||||
```
|
||||
返回类型 `T` 的大小(字节),编译期常量。可用于分配内存、I/O 缓冲区。splc0 中暂未实现。
|
||||
|
||||
### @offsetof
|
||||
```
|
||||
@offsetof(T, field)
|
||||
```
|
||||
返回结构体 `T` 中字段 `field` 的字节偏移。实现泛型/运行时反射时有用。splc0 中暂未实现。
|
||||
|
||||
### @panic
|
||||
```
|
||||
@panic("message")
|
||||
```
|
||||
编译期中断,输出错误信息并终止编译。splc0 中暂未实现。
|
||||
|
||||
### @assert
|
||||
```
|
||||
@assert(expr)
|
||||
```
|
||||
编译期断言——若 `expr` 为假则中断编译。splc0 中暂未实现。
|
||||
|
||||
### @embed
|
||||
```
|
||||
@embed("file")
|
||||
```
|
||||
在编译时将文件内容作为字节数组嵌入程序。splc0 中暂未实现。
|
||||
|
||||
---
|
||||
|
||||
## 语句
|
||||
|
||||
### 块
|
||||
```
|
||||
{ statement; statement; ... }
|
||||
```
|
||||
创建新作用域——局部声明的变量在退出时被丢弃(符号表弹出),且defer也是基于块作用域的,
|
||||
块可以返回值,只需要最后一个表达式没有分号。
|
||||
|
||||
### 表达式语句
|
||||
```
|
||||
expr;
|
||||
```
|
||||
|
||||
### 赋值
|
||||
```
|
||||
name = expr;
|
||||
name += expr;
|
||||
name.field = expr;
|
||||
name[expr] = expr;
|
||||
```
|
||||
|
||||
支持 `=`、`+=`、`-=`、`*=`、`/=`、`%=`、`&=`、`|=`、`^=`、`<<=`、`>>=`。
|
||||
|
||||
### 返回
|
||||
```
|
||||
ret expr;
|
||||
ret; // void 返回(仅限 void 函数)
|
||||
```
|
||||
|
||||
RET 指令会根据函数返回类型携带或不带返回值。
|
||||
|
||||
### If / Else
|
||||
```
|
||||
if expr statement
|
||||
if expr statement else statement
|
||||
```
|
||||
`expr` 必须求值为 bool(i32),或者成功语义。`statement` 可以是块 `{ }`。
|
||||
|
||||
实现:`BZ` 跳转到 else 分支,`JMP` 跳过 else 分支。
|
||||
|
||||
### While
|
||||
```
|
||||
while expr { ... }
|
||||
```
|
||||
实现:在循环顶部求值 `expr`,`BZ` 跳转到循环结束,循环体末尾 `JMP` 跳回顶部。
|
||||
|
||||
### Loop
|
||||
```
|
||||
loop { ... }
|
||||
```
|
||||
无限循环。使用 `break` 退出,`continue` 重新开始。
|
||||
|
||||
### Break / Continue
|
||||
```
|
||||
break;
|
||||
continue;
|
||||
```
|
||||
break 通过链表记录所有跳出位置,在循环结束后统一回填目标地址。
|
||||
|
||||
### Defer
|
||||
```
|
||||
defer { ... }
|
||||
defer statement;
|
||||
```
|
||||
作用域退出时延迟执行
|
||||
|
||||
### For Range
|
||||
```
|
||||
for 0..N as i { body }
|
||||
for slice as val { body }
|
||||
for slice, 0.. as val, idx { body }
|
||||
```
|
||||
Range for 循环。支持三种形式:
|
||||
|
||||
1. **数值区间** `for begin..end as i`:`i` 从 `begin` 到 `end-1`,步长 1
|
||||
2. **切片遍历** `for slice as val`:遍历切片的每个元素
|
||||
3. **带索引的切片遍历** `for slice, 0.. as val, idx`:同时获得元素值和索引
|
||||
|
||||
展开实现:
|
||||
```
|
||||
// for 0..N as i { body }
|
||||
var i: i32 = 0;
|
||||
while i < N {
|
||||
// body...
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
// for slice, 0.. as val, idx { body }
|
||||
var idx: i32 = 0;
|
||||
var _len: i32 = slice.len;
|
||||
var _ptr: *T = slice.ptr;
|
||||
while idx < _len {
|
||||
var val: T = _ptr[idx];
|
||||
// body...
|
||||
idx = idx + 1;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 表达式(优先级爬升)
|
||||
|
||||
### 运算符优先级表
|
||||
|
||||
| 优先级 | 运算符 | 结合性 |
|
||||
|--------|-------------------------|----------|
|
||||
| 1 | `\|\|` | 左结合 |
|
||||
| 2 | `&&` | 左结合 |
|
||||
| 3 | `\|` | 左结合 |
|
||||
| 4 | `^` | 左结合 |
|
||||
| 5 | `&` | 左结合 |
|
||||
| 6 | `==` `!=` | 左结合 |
|
||||
| 7 | `<` `<=` `>` `>=` | 左结合 |
|
||||
| 8 | `<<` `>>` | 左结合 |
|
||||
| 9 | `+` `-` | 左结合 |
|
||||
| 10 | `*` `/` `%` | 左结合 |
|
||||
| 前缀 | `-` `!` `~` `&` `*` | 右结合 |
|
||||
| 后缀 | `.field` `[expr]` | 左结合 |
|
||||
|
||||
**`||` 和 `&&` 的短路求值**:`||` 用 `BNZ` 左侧为真时跳过右侧;`&&` 用 `BZ` 左侧为假时跳过右侧。
|
||||
|
||||
### 主要表达式
|
||||
```
|
||||
字面量 → PUSH 立即数
|
||||
标识符 → LADDR 局部变量地址(可选 LD64 取值)
|
||||
标识符(args) → 函数调用
|
||||
(expr) → 分组
|
||||
```
|
||||
|
||||
- 标识符引用局部变量时:数组/聚合类型压入地址,其他类型压入值(LADDR + LD64)
|
||||
- 函数调用时 push 参数(从左到右),push 函数地址,CALL nargs
|
||||
|
||||
### 后缀表达式
|
||||
```
|
||||
expr.field → 结构体成员访问(计算字节偏移)(可以单层解引用即 推断c语言的 -> 但是只能单层)
|
||||
expr[expr] → 数组/指针索引
|
||||
expr[begin..end] → 切片表达式(从数组/切片创建视图)
|
||||
expr.* → 解引用(LD64)
|
||||
```
|
||||
|
||||
- **`.field`**:根据类型布局计算字节偏移。结构体:地址 + 偏移,嵌套结构体保留地址。指针指向的结构体:先 LD64 解引用获取堆地址,再加字段偏移。
|
||||
- **`[expr]`**:指针索引用元素字节大小做乘法,数组索引用槽位大小做乘法。最后 ADD 得到元素地址,LD* 加载值。
|
||||
|
||||
### 前缀表达式
|
||||
```
|
||||
-expr → 算术取反(NEG)
|
||||
!expr → 逻辑非(expr == 0 → EQ + PUSH 0)
|
||||
~expr → 按位取反(NOT)
|
||||
&expr → 取地址(LADDR,仅限标识符)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 调用约定
|
||||
|
||||
> 见 ../stage0/spl_ir.h
|
||||
|
||||
---
|
||||
|
||||
## SIR 指令集
|
||||
|
||||
> 见 ../stage0/spl_ir.h
|
||||
|
||||
---
|
||||
|
||||
## 内存模型
|
||||
|
||||
- **栈**:向上增长。每个槽位为 `spl_val_t`(uintptr_t,8 字节)。
|
||||
- **帧指针**(fp):当前函数参数的基址。
|
||||
- **栈指针**(sp):栈顶。
|
||||
- **金丝雀**:存储在 `data[fp-1]`(若 fp > 0)——对编译后的代码不可见。
|
||||
- **全局数据**(gdata):按索引引用的字节块。字符串、静态数据等。
|
||||
|
||||
---
|
||||
|
||||
## 编译期执行(Comptime)
|
||||
|
||||
```
|
||||
comptime { ... }
|
||||
```
|
||||
在编译时执行代码,通过将编译后的 .sir 文件加载到子 VM 中。通过以下系统调用实现:
|
||||
|
||||
```
|
||||
vm_new() → ptr // 创建子 VM
|
||||
vm_drop(vm) // 销毁子 VM
|
||||
vm_load(vm, path) → ptr // 加载 .sir,返回 prog
|
||||
vm_push(vm, val) // 将参数压入子 VM 栈
|
||||
vm_call(vm, name, nargs) // 调用函数
|
||||
vm_run(vm) → i32 // 运行子 VM,返回退出码
|
||||
```
|
||||
|
||||
(splc0 中尚未实现。)
|
||||
Reference in New Issue
Block a user