stage2 提供近乎完整的功能但出现严重性能问题 添加stage0的原生性能记录 stage1添加更多内置函数 nob重构代码使其更加健壮
This commit is contained in:
1 parent
b5a7a811b3
commit
e02b0c633a
35 files changed
+14018
-216
No files matched your search
+426
@@ -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, ®_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();
|
||||
}
|
||||
Reference in new issue
Block a user