stage2 大量优化代码使其自举编译时间大幅缩短
This commit is contained in:
1 parent
e02b0c633a
commit
846f441e3a
38 files changed
+2418
-835
No files matched your search
+2
-5
@@ -272,11 +272,8 @@ fn drop(self: *Ast) {
|
||||
}
|
||||
|
||||
fn node(self: *Ast, ref: usize): *Node {
|
||||
match Vec.get(&self.nodes, ref) {
|
||||
.Ok(ptr) => { ret ptr as *Node; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
if ref >= self.nodes.len { ret null; }
|
||||
ret (self.nodes.data as *Node) + ref;
|
||||
}
|
||||
|
||||
// 追加节点,返回其引用(失败返回 0)。
|
||||
|
||||
+4
-4
@@ -6,7 +6,7 @@
|
||||
|
||||
type .{ .Error } = @import("utils/error.spl");
|
||||
type .{ .Vec } = @import("utils/vec.spl");
|
||||
type .{ .Map } = @import("utils/map.spl");
|
||||
type .{ .Map, .hash_kind_usize } = @import("utils/map.spl");
|
||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||
type .{ .hash_usize, .eq_usize } = @import("utils/hash.spl");
|
||||
@@ -54,8 +54,8 @@ owned: Vec, // ByteBuffer:为 dbg.panic 消息提供稳定存储
|
||||
// ---- 小工具 ----
|
||||
|
||||
fn list_at(v: Vec, i: usize): usize {
|
||||
match Vec.get(&v, i) { .Ok(p) => { ret (p as *usize).*; }, .Err(e) => { ret 0; } }
|
||||
ret 0;
|
||||
if i >= v.len { ret 0; }
|
||||
ret ((v.data as *usize) + i).*;
|
||||
}
|
||||
|
||||
fn push_u(v: *Vec, x: usize) {
|
||||
@@ -2069,7 +2069,7 @@ fn new(builder: *IrBuilder, sema: *Sema, diag: *Diag): Ast2Ir {
|
||||
.ir = builder,
|
||||
.ast = sema.ast,
|
||||
.def2ir_map = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
|
||||
hash_usize, eq_usize),
|
||||
hash_kind_usize()),
|
||||
.loop_stack = Vec.blank(@sizeof(LoopCtx), @alignof(LoopCtx)),
|
||||
.defer_stack = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||
.cur_ret_lang = 0,
|
||||
|
||||
+1
-5
@@ -132,11 +132,7 @@ fn drop(self: *Attr) {
|
||||
|
||||
fn node(self: *Attr, id: usize): *AttrNode {
|
||||
if id == 0 || id >= self.table.len { ret null; }
|
||||
match Vec.get(&self.table, id) {
|
||||
.Ok(ptr) => { ret ptr as *AttrNode; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
ret (self.table.data as *AttrNode) + id;
|
||||
}
|
||||
|
||||
fn info_at(self: *Attr, index: usize): *AttrInfo {
|
||||
|
||||
+1
-5
@@ -87,11 +87,7 @@ fn drop(self: *DefTable) {
|
||||
// id 0 与越界均返回 null(对应 stage1 spl_def_node)。
|
||||
fn node(self: *DefTable, id: usize): *DefNode {
|
||||
if id == 0 || id >= self.table.len { ret null; }
|
||||
match Vec.get(&self.table, id) {
|
||||
.Ok(ptr) => { ret ptr as *DefNode; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
ret (self.table.data as *DefNode) + id;
|
||||
}
|
||||
|
||||
fn count(self: *DefTable): usize {
|
||||
|
||||
+1
-4
@@ -161,10 +161,7 @@ fn table_count(): usize {
|
||||
|
||||
fn strip_at(name: ByteView): ByteView {
|
||||
if name.len > 0 && name.ptr[0] == '@' {
|
||||
match ByteView.slice(name, 1, name.len) {
|
||||
.Ok(rest) => { ret rest; },
|
||||
.Err(error) => { ret name; },
|
||||
}
|
||||
ret ByteView { .ptr = name.ptr + 1, .len = name.len - 1 };
|
||||
}
|
||||
ret name;
|
||||
}
|
||||
|
||||
+17
-41
@@ -167,26 +167,16 @@ fn drop_body(body: IrBody) {
|
||||
fn drop(self: *Ir) {
|
||||
var i: usize = 0;
|
||||
while i < self.funcs.len {
|
||||
match Vec.get(&self.funcs, i) {
|
||||
.Ok(ptr) => {
|
||||
var f = ptr as *IrFunc;
|
||||
var j: usize = 0;
|
||||
while j < f.nodes.len {
|
||||
match Vec.get(&f.nodes, j) {
|
||||
.Ok(np) => {
|
||||
var e = np as *IrEntry;
|
||||
Vec.drop(&e.attrs);
|
||||
drop_body(e.body);
|
||||
},
|
||||
.Err(error) => {},
|
||||
}
|
||||
j = j + 1;
|
||||
}
|
||||
Vec.drop(&f.attrs);
|
||||
Vec.drop(&f.nodes);
|
||||
},
|
||||
.Err(error) => {},
|
||||
var f = (self.funcs.data as *IrFunc) + i;
|
||||
var j: usize = 0;
|
||||
while j < f.nodes.len {
|
||||
var e = (f.nodes.data as *IrEntry) + j;
|
||||
Vec.drop(&e.attrs);
|
||||
drop_body(e.body);
|
||||
j = j + 1;
|
||||
}
|
||||
Vec.drop(&f.attrs);
|
||||
Vec.drop(&f.nodes);
|
||||
i = i + 1;
|
||||
}
|
||||
Vec.drop(&self.funcs);
|
||||
@@ -207,36 +197,22 @@ fn alloc_fn(self: *Ir): usize {
|
||||
|
||||
fn alloc_node(self: *Ir, fn_id: usize): usize {
|
||||
if fn_id == 0 || fn_id >= self.funcs.len { ret 0; }
|
||||
match Vec.get(&self.funcs, fn_id) {
|
||||
.Ok(ptr) => {
|
||||
var f = ptr as *IrFunc;
|
||||
var n = new_entry();
|
||||
match Vec.push(&f.nodes, &n as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||||
ret f.nodes.len - 1;
|
||||
},
|
||||
.Err(error) => {},
|
||||
}
|
||||
ret 0;
|
||||
var f = (self.funcs.data as *IrFunc) + fn_id;
|
||||
var n = new_entry();
|
||||
match Vec.push(&f.nodes, &n as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||||
ret f.nodes.len - 1;
|
||||
}
|
||||
|
||||
fn func(self: *Ir, fn_id: usize): *IrFunc {
|
||||
if fn_id == 0 || fn_id >= self.funcs.len { ret null; }
|
||||
match Vec.get(&self.funcs, fn_id) {
|
||||
.Ok(ptr) => { ret ptr as *IrFunc; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
ret (self.funcs.data as *IrFunc) + fn_id;
|
||||
}
|
||||
|
||||
fn node(self: *Ir, fn_id: usize, node_id: usize): *IrEntry {
|
||||
var f = func(self, fn_id);
|
||||
if f == null { ret null; }
|
||||
if node_id == 0 || node_id >= f.nodes.len { ret null; }
|
||||
match Vec.get(&f.nodes, node_id) {
|
||||
.Ok(ptr) => { ret ptr as *IrEntry; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
ret (f.nodes.data as *IrEntry) + node_id;
|
||||
}
|
||||
|
||||
fn kind_name(kind: IrKind): []u8 {
|
||||
@@ -345,8 +321,8 @@ fn dump_attrs(out: *ByteBuffer, attr: *Attr, ids: Vec): bool {
|
||||
}
|
||||
|
||||
fn list_at(v: Vec, i: usize): usize {
|
||||
match Vec.get(&v, i) { .Ok(p) => { ret (p as *usize).*; }, .Err(e) => { ret 0; } }
|
||||
ret 0;
|
||||
if i >= v.len { ret 0; }
|
||||
ret ((v.data as *usize) + i).*;
|
||||
}
|
||||
|
||||
fn dump_body(out: *ByteBuffer, ty: *TypeTable, e: *IrEntry): bool {
|
||||
|
||||
+12
-2
@@ -362,7 +362,7 @@ fn frame_build(fl: *FrameLayout, ctx: *Ir2Vm, fid: usize) {
|
||||
var i: usize = 0;
|
||||
while i < n {
|
||||
var blank: Slot = .{ .result_off = 0, .data_off = 0 };
|
||||
match Vec.push(&fl.slots, &blank as *u8) { .Ok => {}, .Err(error) => {} }
|
||||
ignore_bool(Vec.push_raw(&fl.slots, &blank as *u8));
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
@@ -421,7 +421,17 @@ fixups: *Vec,
|
||||
terminated: bool,
|
||||
|
||||
fn emit(cg: *Cgen, op: Op, ty: VmKind, imm: usize) {
|
||||
ignore_usize(Mcode.add_instr(&cg.ctx.prog, op, ty, imm));
|
||||
var prog = &cg.ctx.prog;
|
||||
var i = prog.insns.len;
|
||||
if i < prog.insns.cap {
|
||||
var p = (prog.insns.data as *VmIns) + i;
|
||||
p.opcode = op;
|
||||
p.ty = ty;
|
||||
p.imm = imm;
|
||||
prog.insns.len = i + 1;
|
||||
ret;
|
||||
}
|
||||
ignore_usize(Mcode.add_instr(prog, op, ty, imm));
|
||||
}
|
||||
|
||||
fn ip_of(cg: *Cgen): usize {
|
||||
|
||||
@@ -14,8 +14,8 @@ type Ir = @import("./ir.spl");
|
||||
type .{ .IrKind, .ConstLit, .IrEntry, .IrFunc, .IrBody } = @import("./ir.spl");
|
||||
|
||||
fn list_at(v: Vec, i: usize): usize {
|
||||
match Vec.get(&v, i) { .Ok(p) => { ret (p as *usize).*; }, .Err(e) => { ret 0; } }
|
||||
ret 0;
|
||||
if i >= v.len { ret 0; }
|
||||
ret ((v.data as *usize) + i).*;
|
||||
}
|
||||
|
||||
// ---- builder ----
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
type .{ .Error } = @import("utils/error.spl");
|
||||
type .{ .Vec } = @import("utils/vec.spl");
|
||||
type .{ .Map } = @import("utils/map.spl");
|
||||
type .{ .Map, .hash_kind_usize } = @import("utils/map.spl");
|
||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||
type .{ .append_usize } = @import("utils/fmt.spl");
|
||||
@@ -183,7 +183,7 @@ fn new(): LangTypeResult {
|
||||
.ir = ir,
|
||||
.lang_table = lang_table,
|
||||
.fold = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
|
||||
hash_usize, eq_usize),
|
||||
hash_kind_usize()),
|
||||
.defs = defs,
|
||||
.owned_names = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)),
|
||||
};
|
||||
@@ -218,11 +218,7 @@ fn drop(self: *LangType) {
|
||||
// id 0 与越界返回 null(对应 stage1 spl_lang_type_node)。
|
||||
fn lang_node(self: *LangType, id: usize): *LangEntry {
|
||||
if id == 0 || id >= self.lang_table.len { ret null; }
|
||||
match Vec.get(&self.lang_table, id) {
|
||||
.Ok(ptr) => { ret ptr as *LangEntry; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
ret (self.lang_table.data as *LangEntry) + id;
|
||||
}
|
||||
|
||||
fn ir_type(self: *LangType, lang_id: usize): usize {
|
||||
|
||||
+1
-5
@@ -44,11 +44,7 @@ fn new(): LayoutResult {
|
||||
|
||||
fn info_at(self: *Layout, id: usize): *LayoutInfo {
|
||||
if id == 0 || id >= self.infos.len { ret null; }
|
||||
match Vec.get(&self.infos, id) {
|
||||
.Ok(ptr) => { ret ptr as *LayoutInfo; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
ret (self.infos.data as *LayoutInfo) + id;
|
||||
}
|
||||
|
||||
// 未解析或越界返回 null(对应 stage1 spl_layout_info_of)。
|
||||
|
||||
+2
-5
@@ -92,11 +92,8 @@ fn has3(self: *Lexer, ch1: u8, ch2: u8): bool {
|
||||
}
|
||||
|
||||
fn sub(self: *Lexer, start: usize, end: usize): ByteView {
|
||||
match ByteView.slice(self.src, start, end) {
|
||||
.Ok(v) => { ret v; },
|
||||
.Err(error) => { ret ByteView.empty(); },
|
||||
}
|
||||
ret ByteView.empty();
|
||||
if start >= end { ret ByteView.empty(); }
|
||||
ret ByteView { .ptr = self.src.ptr + start, .len = end - start };
|
||||
}
|
||||
|
||||
// 产出 token 并把 pos/col 推进到 end。Vec.push 失败视为致命(同 vec.make_vec 风格)。
|
||||
|
||||
+71
-26
@@ -265,6 +265,15 @@ fn drop(self: *Prog) {
|
||||
// ---- 构建 ----
|
||||
|
||||
fn add_instr(self: *Prog, op: Op, ty: VmKind, imm: usize): usize {
|
||||
var i = self.insns.len;
|
||||
if i < self.insns.cap {
|
||||
var p = (self.insns.data as *VmIns) + i;
|
||||
p.opcode = op;
|
||||
p.ty = ty;
|
||||
p.imm = imm;
|
||||
self.insns.len = i + 1;
|
||||
ret i + 1;
|
||||
}
|
||||
var ins = VmIns { .opcode = op, .ty = ty, .imm = imm };
|
||||
if !Vec.push_raw(&self.insns, &ins as *u8) { ret 0; }
|
||||
ret self.insns.len;
|
||||
@@ -462,11 +471,8 @@ fn dump(self: *Prog, out: *ByteBuffer): bool {
|
||||
}
|
||||
|
||||
fn ins_at(self: *Prog, i: usize): VmIns {
|
||||
match Vec.get(&self.insns, i) {
|
||||
.Ok(ptr) => { ret (ptr as *VmIns).*; },
|
||||
.Err(error) => { ret VmIns { .opcode = .Error, .ty = .Void, .imm = 0 }; },
|
||||
}
|
||||
ret VmIns { .opcode = .Error, .ty = .Void, .imm = 0 };
|
||||
if i >= self.insns.len { ret VmIns { .opcode = .Error, .ty = .Void, .imm = 0 }; }
|
||||
ret ((self.insns.data as *VmIns) + i).*;
|
||||
}
|
||||
|
||||
fn func_at(self: *Prog, i: usize): *VmFunc {
|
||||
@@ -491,41 +497,43 @@ fn align8(n: usize): usize {
|
||||
ret (n + 7) & (~(7 as usize));
|
||||
}
|
||||
|
||||
// 说明:以下 put_* 直接写入已预留容量的 ByteBuffer 存储(store 已按精确总大小 reserve),
|
||||
// 跳过 ByteBuffer.append → Vec.append 的校验/拷贝路径。若容量不足会越界,故 store 先校验容量。
|
||||
|
||||
fn put_u8(out: *ByteBuffer, b: u8): bool {
|
||||
match ByteBuffer.append_byte(out, b) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||
ret false;
|
||||
out.storage.data[out.storage.len] = b;
|
||||
out.storage.len = out.storage.len + 1;
|
||||
ret true;
|
||||
}
|
||||
|
||||
// 直接按小端整型写(x86 小端;VM 的 STORE 用 memcpy,未对齐也安全)。
|
||||
fn put_u16(out: *ByteBuffer, v: usize): bool {
|
||||
if !put_u8(out, (v & 255) as u8) { ret false; }
|
||||
ret put_u8(out, ((v >> 8) & 255) as u8);
|
||||
var d = out.storage.data + out.storage.len;
|
||||
(d as *u16).* = v as u16;
|
||||
out.storage.len = out.storage.len + 2;
|
||||
ret true;
|
||||
}
|
||||
|
||||
fn put_u64(out: *ByteBuffer, v: usize): bool {
|
||||
var i: usize = 0;
|
||||
while i < 8 {
|
||||
if !put_u8(out, ((v >> (i * 8)) & 255) as u8) { ret false; }
|
||||
i = i + 1;
|
||||
}
|
||||
var d = out.storage.data + out.storage.len;
|
||||
(d as *u64).* = v as u64;
|
||||
out.storage.len = out.storage.len + 8;
|
||||
ret true;
|
||||
}
|
||||
|
||||
fn put_zeros(out: *ByteBuffer, n: usize): bool {
|
||||
var i: usize = 0;
|
||||
while i < n {
|
||||
if !put_u8(out, 0) { ret false; }
|
||||
i = i + 1;
|
||||
if n != 0 {
|
||||
ignore_ptr(Mem.set((out.storage.data + out.storage.len) as *any, 0, n));
|
||||
out.storage.len = out.storage.len + n;
|
||||
}
|
||||
ret true;
|
||||
}
|
||||
|
||||
fn put_view(out: *ByteBuffer, v: ByteView): bool {
|
||||
if v.len == 0 { ret true; }
|
||||
match ByteBuffer.append(out, ByteView.as_slice(v)) {
|
||||
.Ok => { ret true; },
|
||||
.Err(error) => { ret false; },
|
||||
}
|
||||
ret false;
|
||||
Mem.copy((out.storage.data + out.storage.len) as *any, v.ptr as *any, v.len);
|
||||
out.storage.len = out.storage.len + v.len;
|
||||
ret true;
|
||||
}
|
||||
|
||||
// 写入「带结尾 NUL、再按 8 对齐」的名称字段(对应 C 侧 nlen = strlen+1)。
|
||||
@@ -541,6 +549,41 @@ fn store(self: *Prog, path: ByteView): Status {
|
||||
var out = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&out); }
|
||||
|
||||
// 精确预估输出大小并一次性预留,避免大文件逐字节增长导致的重复拷贝。
|
||||
var total: usize = 48;
|
||||
var fi: usize = 0;
|
||||
while fi < self.funcs.len {
|
||||
var f = func_at(self, fi);
|
||||
if f != null {
|
||||
var nlen = f.name.len + 1;
|
||||
total = total + 8 + align8(nlen) + 32;
|
||||
}
|
||||
fi = fi + 1;
|
||||
}
|
||||
total = total + self.insns.len * 12;
|
||||
fi = 0;
|
||||
while fi < self.natives.len {
|
||||
var n = native_at(self, fi);
|
||||
if n != null {
|
||||
var nlen = n.name.len + 1;
|
||||
total = total + 8 + align8(nlen) + 8;
|
||||
}
|
||||
fi = fi + 1;
|
||||
}
|
||||
fi = 0;
|
||||
while fi < self.strtab.len {
|
||||
total = total + 8 + align8(str_at(self, fi).len);
|
||||
fi = fi + 1;
|
||||
}
|
||||
fi = 1;
|
||||
while fi < self.gdata.len {
|
||||
var e = gdata_at(self, fi);
|
||||
if e != null { total = total + 8 + align8(e.storage.len); }
|
||||
fi = fi + 1;
|
||||
}
|
||||
match ByteBuffer.reserve(&out, total) { .Ok => {}, .Err(error) => {} }
|
||||
if out.storage.cap < total || out.storage.data == null { ret .Err(make(.Io)); }
|
||||
|
||||
if !put_view(&out, ByteView.from_slice("SPLBIN")) { ret .Err(make(.Io)); }
|
||||
if !put_zeros(&out, 2) { ret .Err(make(.Io)); }
|
||||
|
||||
@@ -572,9 +615,11 @@ fn store(self: *Prog, path: ByteView): Status {
|
||||
i = 0;
|
||||
while i < ninsns {
|
||||
var ins = ins_at(self, i);
|
||||
if !put_u16(&out, @enum2int(ins.opcode) as usize) { ret .Err(make(.Io)); }
|
||||
if !put_u16(&out, @enum2int(ins.ty) as usize) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, ins.imm) { ret .Err(make(.Io)); }
|
||||
var d = out.storage.data + out.storage.len;
|
||||
(d as *u16).* = @enum2int(ins.opcode) as u16;
|
||||
((d + 2) as *u16).* = @enum2int(ins.ty) as u16;
|
||||
((d + 4) as *u64).* = ins.imm as u64;
|
||||
out.storage.len = out.storage.len + 12;
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
|
||||
+7
-9
@@ -42,11 +42,8 @@ fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser {
|
||||
// ---- token 访问 ----
|
||||
|
||||
fn token_ptr(self: *Parser, index: usize): *Token {
|
||||
match Vec.get(&self.toks, index) {
|
||||
.Ok(ptr) => { ret ptr as *Token; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
if index >= self.toks.len { ret null; }
|
||||
ret (self.toks.data as *Token) + index;
|
||||
}
|
||||
|
||||
fn is_trivia(kind: TokenKind): bool {
|
||||
@@ -61,16 +58,17 @@ fn is_trivia(kind: TokenKind): bool {
|
||||
// 跳过 trivia;返回当前 token(含 EOF)。越界返回 null。
|
||||
fn peek(self: *Parser): *Token {
|
||||
var n = self.toks.len;
|
||||
var base = self.toks.data as *Token;
|
||||
var i = self.pos;
|
||||
while i < n {
|
||||
var t = token_ptr(self, i);
|
||||
if t == null { break; }
|
||||
if base == null { break; }
|
||||
var t = base + i;
|
||||
if !is_trivia(t.kind) { break; }
|
||||
i = i + 1;
|
||||
}
|
||||
self.pos = i;
|
||||
if i >= n { ret null; }
|
||||
ret token_ptr(self, i);
|
||||
if i >= n || base == null { ret null; }
|
||||
ret base + i;
|
||||
}
|
||||
|
||||
fn advance(self: *Parser): *Token {
|
||||
|
||||
+3
-7
@@ -7,7 +7,7 @@
|
||||
|
||||
type .{ .Error } = @import("utils/error.spl");
|
||||
type .{ .Vec } = @import("utils/vec.spl");
|
||||
type .{ .Map } = @import("utils/map.spl");
|
||||
type .{ .Map, .hash_kind_view } = @import("utils/map.spl");
|
||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||
type .{ .hash_view, .eq_view } = @import("utils/hash.spl");
|
||||
|
||||
@@ -43,7 +43,7 @@ type ScopeResult = enum {
|
||||
|
||||
fn new_map(): Map {
|
||||
ret Map.new(@sizeof(ByteView), @alignof(ByteView), @sizeof(Symbol), @alignof(Symbol),
|
||||
hash_view, eq_view);
|
||||
hash_kind_view());
|
||||
}
|
||||
|
||||
fn new(): ScopeResult {
|
||||
@@ -81,11 +81,7 @@ fn drop(self: *Scope) {
|
||||
// id 0 与越界返回 null。
|
||||
fn node(self: *Scope, id: usize): *ScopeNode {
|
||||
if id == 0 || id >= self.scopes.len { ret null; }
|
||||
match Vec.get(&self.scopes, id) {
|
||||
.Ok(ptr) => { ret ptr as *ScopeNode; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
ret (self.scopes.data as *ScopeNode) + id;
|
||||
}
|
||||
|
||||
// 新作用域 parent = 当前作用域;不自动切换 current(调用方负责,见 sema)。
|
||||
|
||||
+4
-7
@@ -9,7 +9,7 @@
|
||||
|
||||
type .{ .Error, .Status } = @import("utils/error.spl");
|
||||
type .{ .Vec } = @import("utils/vec.spl");
|
||||
type .{ .Map } = @import("utils/map.spl");
|
||||
type .{ .Map, .hash_kind_usize } = @import("utils/map.spl");
|
||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||
type .{ .append_usize } = @import("utils/fmt.spl");
|
||||
@@ -69,11 +69,8 @@ type SemaResult = enum {
|
||||
// ---- 小工具 ----
|
||||
|
||||
fn list_at(v: Vec, i: usize): usize {
|
||||
match Vec.get(&v, i) {
|
||||
.Ok(ptr) => { ret (ptr as *usize).*; },
|
||||
.Err(error) => { ret 0; },
|
||||
}
|
||||
ret 0;
|
||||
if i >= v.len { ret 0; }
|
||||
ret ((v.data as *usize) + i).*;
|
||||
}
|
||||
|
||||
fn push_u(v: *Vec, x: usize) {
|
||||
@@ -234,7 +231,7 @@ fn new(ast: *Ast, diag: *Diag): SemaResult {
|
||||
.current_loop_break_tid = 0,
|
||||
.label_stack = Vec.blank(@sizeof(ByteView), @alignof(ByteView)),
|
||||
.ir_to_lang = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
|
||||
hash_usize, eq_usize),
|
||||
hash_kind_usize()),
|
||||
};
|
||||
ret .Ok(s);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ fn steps(): usize {
|
||||
ret vm_timestamp(3);
|
||||
}
|
||||
|
||||
fn calls(): usize {
|
||||
ret vm_timestamp(5);
|
||||
}
|
||||
|
||||
fn now_ns(): usize {
|
||||
ret vm_timestamp(4);
|
||||
}
|
||||
|
||||
+58
-30
@@ -66,37 +66,65 @@ fn is_trivia(kind: TokenKind): bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 先按长度与首字节分派,再做完整比较,避免对每个标识符做全表串比较。
|
||||
fn keyword_of(text: ByteView): KeywordResult {
|
||||
if is(text, "any") { ret .Yes(.Any); }
|
||||
if is(text, "as") { ret .Yes(.As); }
|
||||
if is(text, "bool") { ret .Yes(.Bool); }
|
||||
if is(text, "break") { ret .Yes(.Break); }
|
||||
if is(text, "catch") { ret .Yes(.Catch); }
|
||||
if is(text, "comptime") { ret .Yes(.Comptime); }
|
||||
if is(text, "const") { ret .Yes(.Const); }
|
||||
if is(text, "continue") { ret .Yes(.Continue); }
|
||||
if is(text, "defer") { ret .Yes(.Defer); }
|
||||
if is(text, "else") { ret .Yes(.Else); }
|
||||
if is(text, "enum") { ret .Yes(.Enum); }
|
||||
if is(text, "errdefer") { ret .Yes(.Errdefer); }
|
||||
if is(text, "false") { ret .Yes(.False); }
|
||||
if is(text, "fn") { ret .Yes(.Fn); }
|
||||
if is(text, "for") { ret .Yes(.For); }
|
||||
if is(text, "if") { ret .Yes(.If); }
|
||||
if is(text, "loop") { ret .Yes(.Loop); }
|
||||
if is(text, "match") { ret .Yes(.Match); }
|
||||
if is(text, "null") { ret .Yes(.Null); }
|
||||
if is(text, "ret") { ret .Yes(.Ret); }
|
||||
if is(text, "shape") { ret .Yes(.Shape); }
|
||||
if is(text, "struct") { ret .Yes(.Struct); }
|
||||
if is(text, "true") { ret .Yes(.True); }
|
||||
if is(text, "try") { ret .Yes(.Try); }
|
||||
if is(text, "type") { ret .Yes(.Type); }
|
||||
if is(text, "undefined") { ret .Yes(.Undefined); }
|
||||
if is(text, "union") { ret .Yes(.Union); }
|
||||
if is(text, "var") { ret .Yes(.Var); }
|
||||
if is(text, "while") { ret .Yes(.While); }
|
||||
if is(text, "_") { ret .Yes(.Underscore); }
|
||||
var n = text.len;
|
||||
if n == 0 { ret .No; }
|
||||
var c0 = text.ptr[0];
|
||||
if n == 2 {
|
||||
if c0 == 'a' && is(text, "as") { ret .Yes(.As); }
|
||||
if c0 == 'f' && is(text, "fn") { ret .Yes(.Fn); }
|
||||
if c0 == 'i' && is(text, "if") { ret .Yes(.If); }
|
||||
ret .No;
|
||||
}
|
||||
if n == 3 {
|
||||
if c0 == 'a' && is(text, "any") { ret .Yes(.Any); }
|
||||
if c0 == 'f' && is(text, "for") { ret .Yes(.For); }
|
||||
if c0 == 'r' && is(text, "ret") { ret .Yes(.Ret); }
|
||||
if c0 == 't' && is(text, "try") { ret .Yes(.Try); }
|
||||
if c0 == 'v' && is(text, "var") { ret .Yes(.Var); }
|
||||
ret .No;
|
||||
}
|
||||
if n == 4 {
|
||||
if c0 == 'b' && is(text, "bool") { ret .Yes(.Bool); }
|
||||
if c0 == 'e' && is(text, "else") { ret .Yes(.Else); }
|
||||
if c0 == 'e' && is(text, "enum") { ret .Yes(.Enum); }
|
||||
if c0 == 'l' && is(text, "loop") { ret .Yes(.Loop); }
|
||||
if c0 == 'n' && is(text, "null") { ret .Yes(.Null); }
|
||||
if c0 == 't' && is(text, "true") { ret .Yes(.True); }
|
||||
if c0 == 't' && is(text, "type") { ret .Yes(.Type); }
|
||||
ret .No;
|
||||
}
|
||||
if n == 5 {
|
||||
if c0 == 'b' && is(text, "break") { ret .Yes(.Break); }
|
||||
if c0 == 'c' && is(text, "catch") { ret .Yes(.Catch); }
|
||||
if c0 == 'c' && is(text, "const") { ret .Yes(.Const); }
|
||||
if c0 == 'd' && is(text, "defer") { ret .Yes(.Defer); }
|
||||
if c0 == 'f' && is(text, "false") { ret .Yes(.False); }
|
||||
if c0 == 'm' && is(text, "match") { ret .Yes(.Match); }
|
||||
if c0 == 's' && is(text, "shape") { ret .Yes(.Shape); }
|
||||
if c0 == 'u' && is(text, "union") { ret .Yes(.Union); }
|
||||
if c0 == 'w' && is(text, "while") { ret .Yes(.While); }
|
||||
ret .No;
|
||||
}
|
||||
if n == 6 {
|
||||
if c0 == 's' && is(text, "struct") { ret .Yes(.Struct); }
|
||||
ret .No;
|
||||
}
|
||||
if n == 8 {
|
||||
if c0 == 'c' && is(text, "comptime") { ret .Yes(.Comptime); }
|
||||
if c0 == 'c' && is(text, "continue") { ret .Yes(.Continue); }
|
||||
if c0 == 'e' && is(text, "errdefer") { ret .Yes(.Errdefer); }
|
||||
ret .No;
|
||||
}
|
||||
if n == 9 {
|
||||
if c0 == 'u' && is(text, "undefined") { ret .Yes(.Undefined); }
|
||||
ret .No;
|
||||
}
|
||||
if n == 1 {
|
||||
if c0 == '_' && is(text, "_") { ret .Yes(.Underscore); }
|
||||
ret .No;
|
||||
}
|
||||
ret .No;
|
||||
}
|
||||
|
||||
|
||||
+5
-12
@@ -9,7 +9,7 @@
|
||||
|
||||
type .{ .Error } = @import("utils/error.spl");
|
||||
type .{ .Vec } = @import("utils/vec.spl");
|
||||
type .{ .Map } = @import("utils/map.spl");
|
||||
type .{ .Map, .hash_kind_usize } = @import("utils/map.spl");
|
||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||
type .{ .append_usize } = @import("utils/fmt.spl");
|
||||
@@ -301,11 +301,8 @@ fn usize_at(v: Vec, i: usize): usize {
|
||||
}
|
||||
|
||||
fn member_at(v: *Vec, i: usize): *Member {
|
||||
match Vec.get(v, i) {
|
||||
.Ok(ptr) => { ret ptr as *Member; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
if i >= v.len { ret null; }
|
||||
ret (v.data as *Member) + i;
|
||||
}
|
||||
|
||||
fn push_member(v: *Vec, name: []u8, type_id: usize) {
|
||||
@@ -323,11 +320,7 @@ fn push_member(v: *Vec, name: []u8, type_id: usize) {
|
||||
// id 0 与越界均返回 null(对应 stage1 spl_type_node)。
|
||||
fn node(self: *TypeTable, id: usize): *TypeEntry {
|
||||
if id == 0 || id >= self.table.len { ret null; }
|
||||
match Vec.get(&self.table, id) {
|
||||
.Ok(ptr) => { ret ptr as *TypeEntry; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
ret (self.table.data as *TypeEntry) + id;
|
||||
}
|
||||
|
||||
// ---- 生命周期 ----
|
||||
@@ -336,7 +329,7 @@ fn new(): TypeTableResult {
|
||||
var tt = TypeTable {
|
||||
.table = Vec.blank(@sizeof(TypeEntry), @alignof(TypeEntry)),
|
||||
.fold = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
|
||||
hash_usize, eq_usize),
|
||||
hash_kind_usize()),
|
||||
};
|
||||
var sentinel = TypeEntry { .hash_next = 0, .node = .Error };
|
||||
match Vec.push(&tt.table, &sentinel as *u8) {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
type .{ .Error, .make } = @import("error.spl");
|
||||
|
||||
#extern(vm) fn vm_bytes_eq(a: *any, alen: usize, b: *any, blen: usize): i32;
|
||||
|
||||
type ByteView = @self;
|
||||
|
||||
ptr: *u8,
|
||||
@@ -62,13 +64,7 @@ fn byte_at(self: ByteView, index: usize): ByteResult {
|
||||
}
|
||||
|
||||
fn eq(a: ByteView, b: ByteView): bool {
|
||||
if a.len != b.len { ret false; }
|
||||
var i: usize = 0;
|
||||
while i < a.len {
|
||||
if a.ptr[i] != b.ptr[i] { ret false; }
|
||||
i = i + 1;
|
||||
}
|
||||
ret true;
|
||||
ret vm_bytes_eq(a.ptr as *any, a.len, b.ptr as *any, b.len) != 0;
|
||||
}
|
||||
|
||||
// 按无符号字节进行字典序比较,结果只承诺小于、等于或大于零。
|
||||
|
||||
+45
-190
@@ -7,9 +7,14 @@
|
||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||
type .{ .Vec } = @import("vec.spl");
|
||||
type .{ .ByteView } = @import("byte_view.spl");
|
||||
type .{ .hash_usize, .eq_usize, .hash_view, .eq_view } = @import("hash.spl");
|
||||
type .{ .checked_add, .checked_mul, .grow_capacity } = @import("memory.spl");
|
||||
type .{ .copy } = @import("../sys/mem.spl");
|
||||
|
||||
#extern(vm) fn vm_map_valid(m: *any): i32;
|
||||
#extern(vm) fn vm_map_reserve(m: *any, need: usize): i32;
|
||||
#extern(vm) fn vm_map_put(m: *any, key: *any, val: *any): i32;
|
||||
#extern(vm) fn vm_map_get(m: *any, key: *any, out: *any): i32;
|
||||
#extern(vm) fn vm_map_del(m: *any, key: *any): i32;
|
||||
#extern(vm) fn vm_map_clear(m: *any): void;
|
||||
#extern(vm) fn vm_map_drop(m: *any): void;
|
||||
|
||||
// 槽位状态:Empty/Occupied/Deleted(墓碑,仅在扩容重哈希时清除)。
|
||||
// 负载因子 70/128。
|
||||
@@ -20,11 +25,6 @@ type SlotState = enum {
|
||||
Deleted,
|
||||
}
|
||||
|
||||
fn state_code(state: SlotState): u8 {
|
||||
var s: SlotState = state;
|
||||
ret @enum2int(s) as u8;
|
||||
}
|
||||
|
||||
// 查找/删除结果:显式区分命中与缺失,替代 bool 哨兵。
|
||||
type LookupResult = enum {
|
||||
Found,
|
||||
@@ -39,11 +39,15 @@ states: Vec,
|
||||
size: usize,
|
||||
key_size: usize,
|
||||
val_size: usize,
|
||||
hash_fn: fn(*u8): usize,
|
||||
eq_fn: fn(*u8, *u8): bool,
|
||||
hash_kind: usize,
|
||||
|
||||
// 键解释:0 = usize;1 = ByteView(按内容哈希/比较)。
|
||||
fn hash_kind_usize(): usize { ret 0; }
|
||||
fn hash_kind_view(): usize { ret 1; }
|
||||
fn hash_kind_constant(): usize { ret 2; }
|
||||
|
||||
fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
||||
hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map {
|
||||
hash_kind: usize): Map {
|
||||
ret Map {
|
||||
.keys = Vec.blank(key_size, key_align),
|
||||
.vals = Vec.blank(val_size, val_align),
|
||||
@@ -51,23 +55,30 @@ fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
||||
.size = 0,
|
||||
.key_size = key_size,
|
||||
.val_size = val_size,
|
||||
.hash_fn = hash_fn,
|
||||
.eq_fn = eq_fn,
|
||||
.hash_kind = hash_kind,
|
||||
};
|
||||
}
|
||||
|
||||
fn new(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
||||
hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map {
|
||||
ret blank(key_size, key_align, val_size, val_align, hash_fn, eq_fn);
|
||||
hash_kind: usize): Map {
|
||||
ret blank(key_size, key_align, val_size, val_align, hash_kind);
|
||||
}
|
||||
|
||||
fn valid(self: Map): bool {
|
||||
if self.key_size == 0 || self.val_size == 0 { ret false; }
|
||||
if self.states.len != self.keys.len || self.states.len != self.vals.len {
|
||||
ret false;
|
||||
}
|
||||
if self.size > self.states.len { ret false; }
|
||||
ret true;
|
||||
fn status_from_code(code: i32): Status {
|
||||
if code == 0 { ret .Ok; }
|
||||
if code == 2 { ret .Err(make(.OutOfRange)); }
|
||||
if code == 3 { ret .Err(make(.Overflow)); }
|
||||
if code == 4 { ret .Err(make(.InvalidAlignment)); }
|
||||
ret .Err(make(.InvalidArgument));
|
||||
}
|
||||
|
||||
fn lookup_from_code(code: i32): LookupResult {
|
||||
if code == 0 { ret .Found; }
|
||||
ret .NotFound;
|
||||
}
|
||||
|
||||
fn valid(self: *Map): bool {
|
||||
ret vm_map_valid(self as *any) != 0;
|
||||
}
|
||||
|
||||
fn size(self: Map): usize {
|
||||
@@ -85,10 +96,8 @@ fn slot_count(self: Map): usize {
|
||||
|
||||
fn slot_state(self: Map, index: usize): SlotState {
|
||||
var code = self.states.data[index];
|
||||
var occupied: SlotState = .Occupied;
|
||||
var deleted_state: SlotState = .Deleted;
|
||||
if code == state_code(occupied) { ret .Occupied; }
|
||||
if code == state_code(deleted_state) { ret .Deleted; }
|
||||
if code == 1 { ret .Occupied; }
|
||||
if code == 2 { ret .Deleted; }
|
||||
ret .Empty;
|
||||
}
|
||||
|
||||
@@ -109,186 +118,32 @@ fn val_at(self: *Map, index: usize): *u8 {
|
||||
ret self.vals.data + index * self.val_size;
|
||||
}
|
||||
|
||||
// 返回占用键的槽位;未找到返回 ~0。容量为 0 时直接未找到。
|
||||
fn find_slot(self: *Map, key: *u8): usize {
|
||||
if self.states.cap == 0 { ret ~(0 as usize); }
|
||||
var mask = self.states.cap - 1;
|
||||
var i = self.hash_fn(key) & mask;
|
||||
var empty: SlotState = .Empty;
|
||||
var occupied: SlotState = .Occupied;
|
||||
var probes: usize = 0;
|
||||
while probes < self.states.cap && self.states.data[i] != state_code(empty) {
|
||||
if self.states.data[i] == state_code(occupied) &&
|
||||
self.eq_fn(self.keys.data + i * self.key_size, key) {
|
||||
ret i;
|
||||
}
|
||||
i = (i + 1) & mask;
|
||||
probes = probes + 1;
|
||||
}
|
||||
ret ~(0 as usize);
|
||||
}
|
||||
|
||||
// 要求容量非零且键不存在。优先复用探测链中的第一个墓碑。
|
||||
fn place(self: *Map, key: *u8, val: *u8) {
|
||||
var mask = self.states.cap - 1;
|
||||
var i = self.hash_fn(key) & mask;
|
||||
var empty: SlotState = .Empty;
|
||||
var deleted: SlotState = .Deleted;
|
||||
var occupied: SlotState = .Occupied;
|
||||
var first_deleted = ~(0 as usize);
|
||||
var probes: usize = 0;
|
||||
while probes < self.states.cap && self.states.data[i] != state_code(empty) {
|
||||
if self.states.data[i] == state_code(deleted) && first_deleted == ~(0 as usize) {
|
||||
first_deleted = i;
|
||||
}
|
||||
i = (i + 1) & mask;
|
||||
probes = probes + 1;
|
||||
}
|
||||
if first_deleted != ~(0 as usize) {
|
||||
i = first_deleted;
|
||||
}
|
||||
copy((self.keys.data + i * self.key_size) as *any, key as *any, self.key_size);
|
||||
copy((self.vals.data + i * self.val_size) as *any, val as *any, self.val_size);
|
||||
self.states.data[i] = state_code(occupied);
|
||||
self.size = self.size + 1;
|
||||
}
|
||||
|
||||
// 保证至少 need 个槽位。所有检查在旧表改动前完成,失败保持原表不变。
|
||||
fn reserve(self: *Map, need: usize): Status {
|
||||
if !valid(self.*) { ret .Err(make(.InvalidArgument)); }
|
||||
if need <= self.states.cap { ret .Ok; }
|
||||
|
||||
var new_cap = grow_capacity(self.states.cap, 8, need);
|
||||
|
||||
var fresh = self.*;
|
||||
fresh.keys = Vec.blank(self.key_size, self.keys.elem_align);
|
||||
fresh.vals = Vec.blank(self.val_size, self.vals.elem_align);
|
||||
fresh.states = Vec.blank(1, 1);
|
||||
fresh.size = 0;
|
||||
|
||||
match Vec.reserve(&fresh.keys, new_cap) {
|
||||
.Ok => {},
|
||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
||||
}
|
||||
match Vec.reserve(&fresh.vals, new_cap) {
|
||||
.Ok => {},
|
||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
||||
}
|
||||
match Vec.reserve(&fresh.states, new_cap) {
|
||||
.Ok => {},
|
||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
||||
}
|
||||
fresh.keys.len = new_cap;
|
||||
fresh.vals.len = new_cap;
|
||||
fresh.states.len = new_cap;
|
||||
|
||||
var i: usize = 0;
|
||||
var empty: SlotState = .Empty;
|
||||
var occupied: SlotState = .Occupied;
|
||||
while i < new_cap {
|
||||
fresh.states.data[i] = state_code(empty);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while i < self.states.len {
|
||||
if self.states.data[i] == state_code(occupied) {
|
||||
place(&fresh, self.keys.data + i * self.key_size,
|
||||
self.vals.data + i * self.val_size);
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
var old_keys = self.keys;
|
||||
var old_vals = self.vals;
|
||||
var old_states = self.states;
|
||||
self.* = fresh;
|
||||
Vec.drop(&old_keys);
|
||||
Vec.drop(&old_vals);
|
||||
Vec.drop(&old_states);
|
||||
ret .Ok;
|
||||
ret status_from_code(vm_map_reserve(self as *any, need));
|
||||
}
|
||||
|
||||
// 覆盖已存在的键;否则插入并可能扩容。
|
||||
fn put(self: *Map, key: *u8, val: *u8): Status {
|
||||
if !valid(self.*) || key == null || val == null {
|
||||
ret .Err(make(.InvalidArgument));
|
||||
}
|
||||
|
||||
var existing = find_slot(self, key);
|
||||
if existing != ~(0 as usize) {
|
||||
copy((self.vals.data + existing * self.val_size) as *any, val as *any, self.val_size);
|
||||
ret .Ok;
|
||||
}
|
||||
|
||||
if self.states.cap == 0 {
|
||||
match reserve(self, 8) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
} else {
|
||||
var next_size: usize = 0;
|
||||
match checked_add(self.size, 1) {
|
||||
.Ok(value) => { next_size = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
var scaled: usize = 0;
|
||||
match checked_mul(next_size, 128) {
|
||||
.Ok(value) => { scaled = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
if scaled / self.states.cap >= 70 {
|
||||
var need: usize = 0;
|
||||
match checked_mul(self.states.cap, 2) {
|
||||
.Ok(value) => { need = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
match reserve(self, need) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
place(self, key, val);
|
||||
ret .Ok;
|
||||
ret status_from_code(vm_map_put(self as *any, key as *any, val as *any));
|
||||
}
|
||||
|
||||
// 找到键则把值复制到 out 并返回 Found;否则 out 不变并返回 NotFound。
|
||||
fn get(self: *Map, key: *u8, out: *u8): LookupResult {
|
||||
if !valid(self.*) || key == null || out == null { ret .NotFound; }
|
||||
var i = find_slot(self, key);
|
||||
if i == ~(0 as usize) { ret .NotFound; }
|
||||
copy(out as *any, (self.vals.data + i * self.val_size) as *any, self.val_size);
|
||||
ret .Found;
|
||||
ret lookup_from_code(vm_map_get(self as *any, key as *any, out as *any));
|
||||
}
|
||||
|
||||
fn del(self: *Map, key: *u8): LookupResult {
|
||||
if !valid(self.*) || key == null { ret .NotFound; }
|
||||
var i = find_slot(self, key);
|
||||
if i == ~(0 as usize) { ret .NotFound; }
|
||||
var deleted_state: SlotState = .Deleted;
|
||||
self.states.data[i] = state_code(deleted_state);
|
||||
self.size = self.size - 1;
|
||||
ret .Found;
|
||||
ret lookup_from_code(vm_map_del(self as *any, key as *any));
|
||||
}
|
||||
|
||||
// 清空内容但保留容量。墓碑与负载计数一并复位。
|
||||
fn clear(self: *Map) {
|
||||
var i: usize = 0;
|
||||
var empty: SlotState = .Empty;
|
||||
while i < self.states.len {
|
||||
self.states.data[i] = state_code(empty);
|
||||
i = i + 1;
|
||||
}
|
||||
self.size = 0;
|
||||
vm_map_clear(self as *any);
|
||||
}
|
||||
|
||||
fn drop(self: *Map) {
|
||||
Vec.drop(&self.keys);
|
||||
Vec.drop(&self.vals);
|
||||
Vec.drop(&self.states);
|
||||
self.size = 0;
|
||||
vm_map_drop(self as *any);
|
||||
}
|
||||
|
||||
fn take(source: *Map): Map {
|
||||
@@ -348,13 +203,13 @@ fn deleted(m: *Map, key: *u8): bool {
|
||||
}
|
||||
|
||||
fn make_int_map(): Map {
|
||||
ret new(@sizeof(usize), @alignof(usize), @sizeof(i32), @alignof(i32), hash_usize, eq_usize);
|
||||
ret new(@sizeof(usize), @alignof(usize), @sizeof(i32), @alignof(i32), hash_kind_usize());
|
||||
}
|
||||
|
||||
fn test_put_get_update() {
|
||||
var m = make_int_map();
|
||||
@assert(size(m) == 0 && capacity(m) == 0, "empty map");
|
||||
@assert(valid(m));
|
||||
@assert(valid(&m));
|
||||
|
||||
var key: usize = 0;
|
||||
var value: i32 = 0;
|
||||
@@ -367,7 +222,7 @@ fn test_put_get_update() {
|
||||
key = 7;
|
||||
value = 70;
|
||||
expect_ok(put(&m, &key as *u8, &value as *u8));
|
||||
@assert(size(m) == 1 && valid(m));
|
||||
@assert(size(m) == 1 && valid(&m));
|
||||
|
||||
out = -1;
|
||||
@assert(found(&m, &key as *u8, &out as *u8) && out == 70, "insert then get");
|
||||
@@ -463,7 +318,7 @@ fn test_take_and_self_put() {
|
||||
|
||||
fn test_byte_view_keys() {
|
||||
var m = new(@sizeof(ByteView), @alignof(ByteView), @sizeof(i32), @alignof(i32),
|
||||
hash_view, eq_view);
|
||||
hash_kind_view());
|
||||
defer { drop(&m); }
|
||||
|
||||
var alpha = ByteView.from_slice("alpha");
|
||||
|
||||
+3
-25
@@ -5,6 +5,7 @@
|
||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||
|
||||
#extern(vm) fn vm_memcpy(dst: *any, src: *any, n: usize): *any;
|
||||
#extern(vm) fn vm_memmove(dst: *any, src: *any, n: usize): *any;
|
||||
|
||||
fn ignore_ptr(p: *any) {}
|
||||
|
||||
@@ -73,31 +74,8 @@ fn move(dst: []u8, src: []u8): Status {
|
||||
}
|
||||
if dst_addr == src_addr { ret .Ok; }
|
||||
|
||||
// 非重叠且较大时走原生 memcpy(Vec 扩容复制是常见场景)。
|
||||
var non_overlap = false;
|
||||
if dst_addr < src_addr {
|
||||
if src_addr - dst_addr >= count { non_overlap = true; }
|
||||
} else {
|
||||
if dst_addr - src_addr >= count { non_overlap = true; }
|
||||
}
|
||||
if non_overlap && count >= 16 {
|
||||
ignore_ptr(vm_memcpy(dst.ptr as *any, src.ptr as *any, count));
|
||||
ret .Ok;
|
||||
}
|
||||
|
||||
if dst_addr < src_addr {
|
||||
var i: usize = 0;
|
||||
while i < count {
|
||||
dst.ptr[i] = src.ptr[i];
|
||||
i = i + 1;
|
||||
}
|
||||
} else {
|
||||
var i = count;
|
||||
while i > 0 {
|
||||
i = i - 1;
|
||||
dst.ptr[i] = src.ptr[i];
|
||||
}
|
||||
}
|
||||
// 重叠安全,统一走原生 memmove。
|
||||
ignore_ptr(vm_memmove(dst.ptr as *any, src.ptr as *any, count));
|
||||
ret .Ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// 开放寻址集合。键按字节复制到集合自己的存储中;集合只释放自身存储,
|
||||
// 不释放键值内部引用的资源。哈希与相等回调的约定和 Map 一致。
|
||||
type .{ .Status } = @import("error.spl");
|
||||
type .{ .Map, .LookupResult } = @import("map.spl");
|
||||
type .{ .Map, .LookupResult, .hash_kind_usize, .hash_kind_view, .hash_kind_constant } =
|
||||
@import("map.spl");
|
||||
type .{ .ByteView } = @import("byte_view.spl");
|
||||
type .{ .hash_usize, .eq_usize, .hash_view, .eq_view } = @import("hash.spl");
|
||||
|
||||
@@ -11,15 +12,14 @@ type Set = @self;
|
||||
|
||||
map: Map,
|
||||
|
||||
fn new(key_size: usize, key_align: usize,
|
||||
hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Set {
|
||||
fn new(key_size: usize, key_align: usize, hash_kind: usize): Set {
|
||||
ret Set {
|
||||
.map = Map.new(key_size, key_align, 1, 1, hash_fn, eq_fn),
|
||||
.map = Map.new(key_size, key_align, 1, 1, hash_kind),
|
||||
};
|
||||
}
|
||||
|
||||
fn valid(self: Set): bool {
|
||||
ret Map.valid(self.map) && self.map.val_size == 1;
|
||||
ret Map.valid(&self.map) && self.map.val_size == 1;
|
||||
}
|
||||
|
||||
fn size(self: Set): usize {
|
||||
@@ -104,7 +104,7 @@ fn constant_hash(key: *u8): usize {
|
||||
}
|
||||
|
||||
fn test_insert_find_remove() {
|
||||
var set = new(@sizeof(usize), @alignof(usize), hash_usize, eq_usize);
|
||||
var set = new(@sizeof(usize), @alignof(usize), hash_kind_usize());
|
||||
defer { drop(&set); }
|
||||
@assert(valid(set));
|
||||
@assert(size(set) == 0 && capacity(set) == 0);
|
||||
@@ -169,7 +169,7 @@ fn test_insert_find_remove() {
|
||||
}
|
||||
|
||||
fn test_collisions_and_take() {
|
||||
var set = new(@sizeof(usize), @alignof(usize), constant_hash, eq_usize);
|
||||
var set = new(@sizeof(usize), @alignof(usize), hash_kind_constant());
|
||||
defer { drop(&set); }
|
||||
|
||||
var i: usize = 0;
|
||||
@@ -213,7 +213,7 @@ fn test_collisions_and_take() {
|
||||
}
|
||||
|
||||
fn test_byte_view_keys() {
|
||||
var set = new(@sizeof(ByteView), @alignof(ByteView), hash_view, eq_view);
|
||||
var set = new(@sizeof(ByteView), @alignof(ByteView), hash_kind_view());
|
||||
defer { drop(&set); }
|
||||
|
||||
var alpha = ByteView.from_slice("alpha");
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||
type .{ .ByteView, .FindResult } = @import("byte_view.spl");
|
||||
type .{ .Vec } = @import("vec.spl");
|
||||
type .{ .Map } = @import("map.spl");
|
||||
type .{ .Map, .hash_kind_view } = @import("map.spl");
|
||||
type .{ .hash_view, .eq_view } = @import("hash.spl");
|
||||
|
||||
type Position = struct {
|
||||
@@ -134,7 +134,7 @@ fn set_new(): Sources {
|
||||
ret Sources {
|
||||
.items = Vec.blank(@sizeof(Source), 8),
|
||||
.by_name = Map.blank(@sizeof(ByteView), @alignof(ByteView), @sizeof(usize),
|
||||
@alignof(usize), hash_view, eq_view),
|
||||
@alignof(usize), hash_kind_view()),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+44
-172
@@ -7,6 +7,18 @@ type .{ .Error, .Kind, .Status } = @import("error.spl");
|
||||
type .{ .SizeResult, .checked_add, .checked_mul, .grow_capacity, .move } = @import("memory.spl");
|
||||
type .{ .alloc, .copy, .free } = @import("../sys/mem.spl");
|
||||
|
||||
#extern(vm) fn vm_vec_valid(v: *any): i32;
|
||||
#extern(vm) fn vm_vec_push_raw(v: *any, elem: *any): i32;
|
||||
#extern(vm) fn vm_vec_push(v: *any, elem: *any): i32;
|
||||
#extern(vm) fn vm_vec_get(v: *any, index: usize, out: *any): i32;
|
||||
#extern(vm) fn vm_vec_allocate(out: *any, capacity: usize, elem_size: usize, elem_align: usize): i32;
|
||||
#extern(vm) fn vm_vec_reserve_raw(v: *any, need: usize): i32;
|
||||
#extern(vm) fn vm_vec_append(v: *any, elems: *any, count: usize): i32;
|
||||
#extern(vm) fn vm_vec_set(v: *any, index: usize, elem: *any): i32;
|
||||
#extern(vm) fn vm_vec_pop(v: *any, out: *any): i32;
|
||||
#extern(vm) fn vm_vec_clone(out: *any, src: *any): i32;
|
||||
#extern(vm) fn vm_vec_drop(v: *any): void;
|
||||
|
||||
type Vec = @self;
|
||||
|
||||
raw: *u8,
|
||||
@@ -51,95 +63,41 @@ fn new(elem_size: usize, elem_align: usize): VecResult {
|
||||
|
||||
// 按指针接收,避免每次调用复制整个 Vec;内联对齐检查,省一次函数调用。
|
||||
fn valid(self: *Vec): bool {
|
||||
if self.elem_size == 0 { ret false; }
|
||||
var a = self.elem_align;
|
||||
if a == 0 || (a & (a - 1)) != 0 { ret false; }
|
||||
if self.len > self.cap { ret false; }
|
||||
if self.cap == 0 {
|
||||
ret self.raw == null && self.data == null && self.len == 0;
|
||||
}
|
||||
if self.raw == null || self.data == null { ret false; }
|
||||
ret (self.data as usize & (a - 1)) == 0;
|
||||
ret vm_vec_valid(self as *any) != 0;
|
||||
}
|
||||
|
||||
fn status_error(kind: Kind): Status {
|
||||
ret .Err(Error.make(kind));
|
||||
}
|
||||
|
||||
// native 错误码 -> Error.Kind(0 ok / 1 InvalidArgument / 2 OutOfRange / 3 Overflow / 4 InvalidAlignment)
|
||||
fn kind_from_code(code: i32): Kind {
|
||||
if code == 2 { ret .OutOfRange; }
|
||||
if code == 3 { ret .Overflow; }
|
||||
if code == 4 { ret .InvalidAlignment; }
|
||||
ret .InvalidArgument;
|
||||
}
|
||||
|
||||
fn status_from_code(code: i32): Status {
|
||||
if code == 0 { ret .Ok; }
|
||||
ret status_error(kind_from_code(code));
|
||||
}
|
||||
|
||||
// 计算元素区字节数。错误时不修改任何 Vec。
|
||||
fn byte_count(count: usize, elem_size: usize): SizeResult {
|
||||
ret checked_mul(count, elem_size);
|
||||
}
|
||||
|
||||
fn allocate(capacity: usize, elem_size: usize, elem_align: usize): VecResult {
|
||||
if elem_size == 0 { ret .Err(Error.make(.InvalidArgument)); }
|
||||
if !valid_alignment(elem_align) {
|
||||
ret .Err(Error.make(.InvalidAlignment));
|
||||
}
|
||||
if capacity == 0 { ret .Ok(blank(elem_size, elem_align)); }
|
||||
|
||||
var bytes: usize = 0;
|
||||
match byte_count(capacity, elem_size) {
|
||||
.Ok(value) => { bytes = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
|
||||
var total: usize = 0;
|
||||
match checked_add(bytes, elem_align - 1) {
|
||||
.Ok(value) => { total = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
|
||||
var raw = alloc(total) as *u8;
|
||||
if raw == null {
|
||||
ret .Err(Error.make(.Overflow));
|
||||
}
|
||||
var address = raw as usize;
|
||||
var mask = elem_align - 1;
|
||||
if address > (~(0 as usize)) - mask {
|
||||
free(raw as *any);
|
||||
ret .Err(Error.make(.Overflow));
|
||||
}
|
||||
var aligned = (address + mask) & ~mask;
|
||||
ret .Ok(Vec {
|
||||
.raw = raw,
|
||||
.data = aligned as *u8,
|
||||
.len = 0,
|
||||
.cap = capacity,
|
||||
.elem_size = elem_size,
|
||||
.elem_align = elem_align,
|
||||
});
|
||||
var v: Vec = undefined;
|
||||
var rc = vm_vec_allocate(&v as *any, capacity, elem_size, elem_align);
|
||||
if rc == 0 { ret .Ok(v); }
|
||||
ret .Err(Error.make(kind_from_code(rc)));
|
||||
}
|
||||
|
||||
// 与 reserve 相同,但假定调用方已校验 self(内部热路径用,省去重复 valid)。
|
||||
fn reserve_raw(self: *Vec, need: usize): Status {
|
||||
if need <= self.cap { ret .Ok; }
|
||||
|
||||
var new_cap = grow_capacity(self.cap, 8, need);
|
||||
|
||||
var fresh: Vec = undefined;
|
||||
match allocate(new_cap, self.elem_size, self.elem_align) {
|
||||
.Ok(value) => { fresh = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
|
||||
var used_bytes: usize = 0;
|
||||
match byte_count(self.len, self.elem_size) {
|
||||
.Ok(value) => { used_bytes = value; },
|
||||
.Err(error) => {
|
||||
drop(&fresh);
|
||||
ret .Err(error);
|
||||
},
|
||||
}
|
||||
if used_bytes != 0 {
|
||||
copy(fresh.data as *any, self.data as *any, used_bytes);
|
||||
}
|
||||
fresh.len = self.len;
|
||||
|
||||
var old_raw = self.raw;
|
||||
self.* = fresh;
|
||||
free(old_raw as *any);
|
||||
ret .Ok;
|
||||
ret status_from_code(vm_vec_reserve_raw(self as *any, need));
|
||||
}
|
||||
|
||||
// 保证至少可容纳 need 个元素(外部入口,含校验)。
|
||||
@@ -154,81 +112,30 @@ fn with_capacity(elem_size: usize, elem_align: usize, capacity: usize): VecResul
|
||||
}
|
||||
|
||||
fn get(self: *Vec, index: usize): PtrResult {
|
||||
if !valid(self) { ret .Err(Error.make(.InvalidArgument)); }
|
||||
if index >= self.len { ret .Err(Error.make(.OutOfRange)); }
|
||||
ret .Ok(self.data + index * self.elem_size);
|
||||
var out: *u8 = null;
|
||||
var rc = vm_vec_get(self as *any, index, &out as *any);
|
||||
if rc == 0 { ret .Ok(out); }
|
||||
if rc == 2 { ret .Err(Error.make(.OutOfRange)); }
|
||||
ret .Err(Error.make(.InvalidArgument));
|
||||
}
|
||||
|
||||
fn set(self: *Vec, index: usize, elem: *u8): Status {
|
||||
if !valid(self) || elem == null { ret status_error(.InvalidArgument); }
|
||||
if index >= self.len { ret status_error(.OutOfRange); }
|
||||
var dst = self.data + index * self.elem_size;
|
||||
ret move(dst[0..self.elem_size], elem[0..self.elem_size]);
|
||||
ret status_from_code(vm_vec_set(self as *any, index, elem as *any));
|
||||
}
|
||||
|
||||
// 追加 count 个连续元素。elems 可以指向 self 当前已初始化的元素区。
|
||||
fn append(self: *Vec, elems: *u8, count: usize): Status {
|
||||
if !valid(self) { ret status_error(.InvalidArgument); }
|
||||
if count == 0 { ret .Ok; }
|
||||
if elems == null { ret status_error(.InvalidArgument); }
|
||||
|
||||
var copy_bytes: usize = 0;
|
||||
match byte_count(count, self.elem_size) {
|
||||
.Ok(value) => { copy_bytes = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
var new_len: usize = 0;
|
||||
match checked_add(self.len, count) {
|
||||
.Ok(value) => { new_len = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
|
||||
var used_bytes: usize = 0;
|
||||
match byte_count(self.len, self.elem_size) {
|
||||
.Ok(value) => { used_bytes = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
|
||||
var source_inside = false;
|
||||
var source_offset: usize = 0;
|
||||
if used_bytes != 0 {
|
||||
var base = self.data as usize;
|
||||
var source = elems as usize;
|
||||
if source >= base && source - base < used_bytes {
|
||||
source_offset = source - base;
|
||||
if copy_bytes > used_bytes - source_offset {
|
||||
ret status_error(.OutOfRange);
|
||||
}
|
||||
source_inside = true;
|
||||
}
|
||||
}
|
||||
|
||||
match reserve_raw(self, new_len) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
if source_inside { elems = self.data + source_offset; }
|
||||
|
||||
var dst = self.data + used_bytes;
|
||||
match move(dst[0..copy_bytes], elems[0..copy_bytes]) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
self.len = new_len;
|
||||
ret .Ok;
|
||||
ret status_from_code(vm_vec_append(self as *any, elems as *any, count));
|
||||
}
|
||||
|
||||
fn push(self: *Vec, elem: *u8): Status {
|
||||
ret append(self, elem, 1);
|
||||
ret status_from_code(vm_vec_push(self as *any, elem as *any));
|
||||
}
|
||||
|
||||
// 热路径:假定 self 有效且已有容量时直接写入;容量不足才回退到 append。
|
||||
fn push_raw(self: *Vec, elem: *u8): bool {
|
||||
if self.len < self.cap {
|
||||
copy(self.data + self.len * self.elem_size, elem, self.elem_size);
|
||||
self.len = self.len + 1;
|
||||
ret true;
|
||||
}
|
||||
if elem == null { ret false; }
|
||||
if vm_vec_push_raw(self as *any, elem as *any) != 0 { ret true; }
|
||||
match append(self, elem, 1) {
|
||||
.Ok => { ret true; },
|
||||
.Err(error) => { ret false; },
|
||||
@@ -238,15 +145,7 @@ fn push_raw(self: *Vec, elem: *u8): bool {
|
||||
|
||||
// 将末元素复制到 out,然后缩短数组。out 可以与元素区重叠。
|
||||
fn pop(self: *Vec, out: *u8): Status {
|
||||
if !valid(self) || out == null { ret status_error(.InvalidArgument); }
|
||||
if self.len == 0 { ret status_error(.OutOfRange); }
|
||||
var source = self.data + (self.len - 1) * self.elem_size;
|
||||
match move(out[0..self.elem_size], source[0..self.elem_size]) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
self.len = self.len - 1;
|
||||
ret .Ok;
|
||||
ret status_from_code(vm_vec_pop(self as *any, out as *any));
|
||||
}
|
||||
|
||||
fn clear(self: *Vec) {
|
||||
@@ -256,31 +155,7 @@ fn clear(self: *Vec) {
|
||||
// 成功时先释放 out 原有存储再提交副本;失败保持 out 不变。
|
||||
// out 与 src 指向同一个 Vec 也允许。
|
||||
fn clone(out: *Vec, src: Vec): Status {
|
||||
if !valid(out) || !valid(&src) { ret status_error(.InvalidArgument); }
|
||||
if out.elem_size != src.elem_size || out.elem_align != src.elem_align {
|
||||
ret status_error(.InvalidArgument);
|
||||
}
|
||||
|
||||
var fresh = blank(src.elem_size, src.elem_align);
|
||||
match reserve(&fresh, src.len) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
var bytes: usize = 0;
|
||||
match byte_count(src.len, src.elem_size) {
|
||||
.Ok(value) => { bytes = value; },
|
||||
.Err(error) => {
|
||||
drop(&fresh);
|
||||
ret .Err(error);
|
||||
},
|
||||
}
|
||||
if bytes != 0 {
|
||||
copy(fresh.data as *any, src.data as *any, bytes);
|
||||
}
|
||||
fresh.len = src.len;
|
||||
drop(out);
|
||||
out.* = fresh;
|
||||
ret .Ok;
|
||||
ret status_from_code(vm_vec_clone(out as *any, &src as *any));
|
||||
}
|
||||
|
||||
// 显式转移所有权,返回值必须由接收方最终 drop。
|
||||
@@ -291,10 +166,7 @@ fn take(source: *Vec): Vec {
|
||||
}
|
||||
|
||||
fn drop(self: *Vec) {
|
||||
var elem_size = self.elem_size;
|
||||
var elem_align = self.elem_align;
|
||||
free(self.raw as *any);
|
||||
self.* = blank(elem_size, elem_align);
|
||||
vm_vec_drop(self as *any);
|
||||
}
|
||||
|
||||
fn expect_ok(result: Status) {
|
||||
|
||||
Reference in new issue
Block a user