stage2 提供近乎完整的功能但出现严重性能问题 添加stage0的原生性能记录 stage1添加更多内置函数 nob重构代码使其更加健壮

This commit is contained in:
zzy committed 2026-09-19 18:08:43 +08:00
1 parent b5a7a811b3
commit e02b0c633a
35 files changed
+14018 -216

No files matched your search

+426
View File
@@ -0,0 +1,426 @@
// Expect retcode = 0
// 属性注册表与属性节点(移植自 stage1/spl_attr.c)。注册表是数据:内置属性
// 在 register_builtins 里登记,查找按 (ns, name)。节点记录命中信息 + 实参。
// 新增属性只加注册项,不改 sema 逻辑(对应 SPL.md 的 @/# 扩展点)。
type .{ .Error } = @import("utils/error.spl");
type .{ .Vec } = @import("utils/vec.spl");
type .{ .ByteView } = @import("utils/byte_view.spl");
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
type .{ .append_usize, .append_i64 } = @import("utils/fmt.spl");
type AttrStage = enum {
Sema,
Layout,
Ir,
Opt,
Backend,
}
type AttrTarget = enum {
Decl,
Type,
Expr,
Stmt,
}
type AttrDemand = enum {
Optional,
Required,
}
type AttrInfo = struct {
ns: ByteView,
name: ByteView,
stage: AttrStage,
target: AttrTarget,
demand: AttrDemand,
min_args: i32,
max_args: i32, // -1 = variadic
}
type AttrArgKind = enum {
Int,
Str,
Def,
Type,
}
type AttrArg = enum {
Int: i64,
Str: ByteView,
Def: usize,
Type: usize,
}
type AttrNode = struct {
info_index: usize, // 注册表下标;0 = 无
args: Vec, // AttrArg
consumed_mask: u32,
}
type Attr = @self;
table: Vec, // AttrNode
registry: Vec, // AttrInfo
type AttrResult = enum {
Ok: Attr,
Err: Error,
}
fn empty_info(): AttrInfo {
ret AttrInfo {
.ns = ByteView.empty(),
.name = ByteView.empty(),
.stage = .Sema,
.target = .Decl,
.demand = .Optional,
.min_args = 0,
.max_args = 0,
};
}
fn new_node(): AttrNode {
ret AttrNode {
.info_index = 0,
.args = Vec.blank(@sizeof(AttrArg), @alignof(AttrArg)),
.consumed_mask = 0,
};
}
fn new(): AttrResult {
var at = Attr {
.table = Vec.blank(@sizeof(AttrNode), @alignof(AttrNode)),
.registry = Vec.blank(@sizeof(AttrInfo), @alignof(AttrInfo)),
};
var sentinel = new_node();
match Vec.push(&at.table, &sentinel as *u8) {
.Ok => {},
.Err(error) => {
Vec.drop(&at.table);
Vec.drop(&at.registry);
ret .Err(error);
},
}
var reg_sentinel = empty_info();
match Vec.push(&at.registry, &reg_sentinel as *u8) {
.Ok => {},
.Err(error) => {
Vec.drop(&at.table);
Vec.drop(&at.registry);
ret .Err(error);
},
}
register_builtins(&at);
ret .Ok(at);
}
fn drop(self: *Attr) {
var i: usize = 1;
while i < self.table.len {
var a = node(self, i);
if a != null {
Vec.drop(&a.args);
}
i = i + 1;
}
Vec.drop(&self.table);
Vec.drop(&self.registry);
}
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;
}
fn info_at(self: *Attr, index: usize): *AttrInfo {
if index == 0 || index >= self.registry.len { ret null; }
match Vec.get(&self.registry, index) {
.Ok(ptr) => { ret ptr as *AttrInfo; },
.Err(error) => { ret null; },
}
ret null;
}
fn node_info(self: *Attr, id: usize): *AttrInfo {
var a = node(self, id);
if a == null { ret null; }
ret info_at(self, a.info_index);
}
// 追加属性节点(args 所有权转移)。
fn alloc(self: *Attr, info_index: usize, args: Vec): usize {
var a = new_node();
a.info_index = info_index;
a.args = args;
match Vec.push(&self.table, &a as *u8) {
.Ok => {},
.Err(error) => { ret 0; },
}
ret self.table.len - 1;
}
fn registry_add(self: *Attr, info: AttrInfo) {
match Vec.push(&self.registry, &info as *u8) {
.Ok => {},
.Err(error) => {},
}
}
// 返回注册表下标(>=1)或 0。
fn lookup(self: *Attr, ns: ByteView, name: ByteView): usize {
var i: usize = 1;
while i < self.registry.len {
var info = info_at(self, i);
if info != null && ByteView.eq(info.ns, ns) && ByteView.eq(info.name, name) {
ret i;
}
i = i + 1;
}
ret 0;
}
fn make_info(ns: []u8, name: []u8, stage: AttrStage, target: AttrTarget, demand: AttrDemand,
min_args: i32, max_args: i32): AttrInfo {
ret AttrInfo {
.ns = ByteView.from_slice(ns),
.name = ByteView.from_slice(name),
.stage = stage,
.target = target,
.demand = demand,
.min_args = min_args,
.max_args = max_args,
};
}
fn register_builtins(self: *Attr) {
registry_add(self, make_info("", "extern", .Ir, .Decl, .Required, 1, 1));
registry_add(self, make_info("", "inline", .Opt, .Decl, .Optional, 0, 0));
registry_add(self, make_info("", "noinline", .Opt, .Decl, .Optional, 0, 0));
registry_add(self, make_info("", "align", .Layout, .Type, .Required, 1, 1));
registry_add(self, make_info("", "volatile", .Ir, .Expr, .Required, 0, 0));
registry_add(self, make_info("", "atomic", .Ir, .Expr, .Required, 0, 1));
}
// ---- dump(镜像 stage1 spl_attr_dump / spl_attr_dump_id)----
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
match ByteBuffer.append(buf, bytes) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
fn push_usize(buf: *ByteBuffer, value: usize): bool {
match append_usize(buf, value, 10, false) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
fn push_hex(buf: *ByteBuffer, value: u32): bool {
if !push_out(buf, "0x") { ret false; }
match append_usize(buf, value as usize, 16, false) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
fn stage_name(s: AttrStage): []u8 {
match s {
.Sema => { ret "SEMA"; },
.Layout => { ret "LAYOUT"; },
.Ir => { ret "IR"; },
.Opt => { ret "OPT"; },
.Backend => { ret "BACKEND"; },
}
ret "?";
}
fn target_name(t: AttrTarget): []u8 {
match t {
.Decl => { ret "DECL"; },
.Type => { ret "TYPE"; },
.Expr => { ret "EXPR"; },
.Stmt => { ret "STMT"; },
}
ret "?";
}
fn demand_name(d: AttrDemand): []u8 {
match d {
.Required => { ret "REQUIRED"; },
.Optional => { ret "OPTIONAL"; },
}
ret "?";
}
fn dump_id(self: *Attr, id: usize, out: *ByteBuffer): bool {
if id == 0 {
ret push_out(out, "?");
}
var a = node(self, id);
if a == null { ret push_out(out, "?"); }
var info = info_at(self, a.info_index);
if info == null { ret push_out(out, "?"); }
if !push_out(out, ByteView.as_slice(info.name)) { ret false; }
if a.args.len == 0 { ret true; }
if !push_out(out, "(") { ret false; }
var i: usize = 0;
while i < a.args.len {
if i > 0 && !push_out(out, ", ") { ret false; }
var arg = arg_at(&a.args, i);
if arg == null { ret false; }
match arg.* {
.Int(v) => {
match append_i64(out, v, 10, false) {
.Ok => {},
.Err(error) => { ret false; },
}
},
.Str(v) => {
if !push_out(out, "\"") { ret false; }
if !push_out(out, ByteView.as_slice(v)) { ret false; }
if !push_out(out, "\"") { ret false; }
},
.Def(v) => {
if !push_out(out, "def#") { ret false; }
if !push_usize(out, v) { ret false; }
},
.Type(v) => {
if !push_out(out, "type#") { ret false; }
if !push_usize(out, v) { ret false; }
},
}
i = i + 1;
}
ret push_out(out, ")");
}
fn arg_at(v: *Vec, i: usize): *AttrArg {
match Vec.get(v, i) {
.Ok(ptr) => { ret ptr as *AttrArg; },
.Err(error) => { ret null; },
}
ret null;
}
fn dump(self: *Attr, out: *ByteBuffer): bool {
if !push_out(out, "AttrTable:\n") { ret false; }
var i: usize = 1;
while i < self.table.len {
var a = node(self, i);
if !push_out(out, " #") { ret false; }
if !push_usize(out, i) { ret false; }
if !push_out(out, " ") { ret false; }
if !dump_id(self, i, out) { ret false; }
if a != null {
var info = info_at(self, a.info_index);
if info != null {
if !push_out(out, " [stage=") { ret false; }
if !push_out(out, stage_name(info.stage)) { ret false; }
if !push_out(out, " target=") { ret false; }
if !push_out(out, target_name(info.target)) { ret false; }
if !push_out(out, " demand=") { ret false; }
if !push_out(out, demand_name(info.demand)) { ret false; }
if !push_out(out, " consumed=") { ret false; }
if !push_hex(out, a.consumed_mask) { ret false; }
if !push_out(out, "]") { ret false; }
}
}
if !push_out(out, "\n") { ret false; }
i = i + 1;
}
ret true;
}
// ---- 测试 ----
fn expect_text(buf: ByteBuffer, expected: []u8) {
var got = ByteBuffer.as_slice(buf);
@assert(got.len == expected.len, "dump length");
var i: usize = 0;
while i < expected.len {
@assert(got.ptr[i] == expected.ptr[i], "dump byte");
i = i + 1;
}
}
fn test_registry() {
var at: Attr = undefined;
match new() {
.Ok(v) => { at = v; },
.Err(error) => { @assert(false, "attr new failed"); },
}
defer { drop(&at); }
@assert(at.registry.len == 7, "1 sentinel + 6 builtins");
var i = lookup(&at, ByteView.empty(), ByteView.from_slice("extern"));
@assert(i != 0, "extern found");
var info = info_at(&at, i);
@assert(info != null, "extern info");
@assert(info.min_args == 1 && info.max_args == 1, "extern arity");
var j = lookup(&at, ByteView.empty(), ByteView.from_slice("atomic"));
@assert(j != 0, "atomic found");
var jinfo = info_at(&at, j);
@assert(jinfo.min_args == 0 && jinfo.max_args == 1, "atomic arity");
@assert(lookup(&at, ByteView.empty(), ByteView.from_slice("nope")) == 0, "unknown attr");
}
fn test_alloc_and_dump_id() {
var at: Attr = undefined;
match new() {
.Ok(v) => { at = v; },
.Err(error) => { @assert(false, "attr new failed"); },
}
defer { drop(&at); }
var idx = lookup(&at, ByteView.empty(), ByteView.from_slice("extern"));
var args = Vec.blank(@sizeof(AttrArg), @alignof(AttrArg));
var s: AttrArg = .Str(ByteView.from_slice("vm"));
match Vec.push(&args, &s as *u8) { .Ok => {}, .Err(e) => { @assert(false, "push str"); } }
var n: AttrArg = .Int(3);
match Vec.push(&args, &n as *u8) { .Ok => {}, .Err(e) => { @assert(false, "push int"); } }
var d: AttrArg = .Def(9);
match Vec.push(&args, &d as *u8) { .Ok => {}, .Err(e) => { @assert(false, "push def"); } }
var id = alloc(&at, idx, args);
@assert(id == 1, "first attr id");
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
if !dump_id(&at, id, &out) { @assert(false, "dump_id ok"); }
expect_text(out, "extern(\"vm\", 3, def#9)");
var out2 = ByteBuffer.new();
defer { ByteBuffer.drop(&out2); }
if !dump_id(&at, 0, &out2) { @assert(false, "dump_id none ok"); }
expect_text(out2, "?");
var out3 = ByteBuffer.new();
defer { ByteBuffer.drop(&out3); }
var l = lookup(&at, ByteView.empty(), ByteView.from_slice("inline"));
var id2 = alloc(&at, l, Vec.blank(@sizeof(AttrArg), @alignof(AttrArg)));
if !dump(&at, &out3) { @assert(false, "dump ok"); }
// 只校验首尾关键片段,避免依赖完整表格排版
var got = ByteBuffer.as_slice(out3);
@assert(got.len > 0, "dump nonempty");
}
fn main() {
test_registry();
test_alloc_and_dump_id();
}