Files
spl/stage2/intrin.spl
T

327 lines
11 KiB
Plaintext

// Expect retcode = 0
// 内建注册表:按 Intrin 查描述信息,按名字/字段/`as` 规则反查 Intrin。新增内建只改
// info_at,不改 sema 逻辑(SPL.md 的 @/# 扩展点)。
type .{ .ByteView } = @import("utils/byte_view.spl");
type TypeTable = @import("./type_table.spl");
type .{ .TypeNode, .TypeEntry } = @import("./type_table.spl");
type .{ .LangKind } = @import("./lang_type.spl");
// 文件命名空间(供跨模块用 IntrinOps.foo 调用)。
type IntrinOps = @self;
type Intrin = enum {
None,
BitSizeof,
Sizeof,
Alignof,
Assert,
Breakpoint,
Len,
IntCast,
Truncate,
BitCast,
FloatCast,
Float2Int,
Int2Float,
Bool2Int,
Ptr2Int,
Int2Ptr,
PtrCast,
Enum2Int,
OffsetOf,
FieldCount,
}
type IntrinForm = enum {
Call,
Field,
}
type IntrinResult = enum {
Unit,
Bool,
Usize,
}
type IntrinInfo = struct {
name: ByteView,
kind: Intrin,
form: IntrinForm,
min_args: i32,
max_args: i32, // -1 = variadic
result: IntrinResult,
}
fn form_is(a: IntrinForm, b: IntrinForm): bool {
ret @enum2int(a) == @enum2int(b);
}
fn kind_is(a: LangKind, b: LangKind): bool {
ret @enum2int(a) == @enum2int(b);
}
// 枚举顺序即 kind 数值(与 splc0 表一致)。
fn info_at(index: usize): IntrinInfo {
match index {
1 => {
ret IntrinInfo { .name = ByteView.from_slice("@bitsizeof"), .kind = .BitSizeof,
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
},
2 => {
ret IntrinInfo { .name = ByteView.from_slice("@sizeof"), .kind = .Sizeof,
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
},
3 => {
ret IntrinInfo { .name = ByteView.from_slice("@alignof"), .kind = .Alignof,
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
},
4 => {
ret IntrinInfo { .name = ByteView.from_slice("@assert"), .kind = .Assert,
.form = .Call, .min_args = 1, .max_args = 2, .result = .Unit };
},
5 => {
ret IntrinInfo { .name = ByteView.from_slice("@breakpoint"), .kind = .Breakpoint,
.form = .Call, .min_args = 0, .max_args = 0, .result = .Unit };
},
6 => {
ret IntrinInfo { .name = ByteView.from_slice("len"), .kind = .Len,
.form = .Field, .min_args = 0, .max_args = 0, .result = .Usize };
},
7 => {
ret IntrinInfo { .name = ByteView.from_slice("@int_cast"), .kind = .IntCast,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
},
8 => {
ret IntrinInfo { .name = ByteView.from_slice("@truncate"), .kind = .Truncate,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
},
9 => {
ret IntrinInfo { .name = ByteView.from_slice("@bit_cast"), .kind = .BitCast,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
},
10 => {
ret IntrinInfo { .name = ByteView.from_slice("@float_cast"), .kind = .FloatCast,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
},
11 => {
ret IntrinInfo { .name = ByteView.from_slice("@float2int"), .kind = .Float2Int,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
},
12 => {
ret IntrinInfo { .name = ByteView.from_slice("@int2float"), .kind = .Int2Float,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
},
13 => {
ret IntrinInfo { .name = ByteView.from_slice("@bool2int"), .kind = .Bool2Int,
.form = .Call, .min_args = 1, .max_args = 2, .result = .Unit };
},
14 => {
ret IntrinInfo { .name = ByteView.from_slice("@ptr2int"), .kind = .Ptr2Int,
.form = .Call, .min_args = 1, .max_args = 2, .result = .Unit };
},
15 => {
ret IntrinInfo { .name = ByteView.from_slice("@int2ptr"), .kind = .Int2Ptr,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
},
16 => {
ret IntrinInfo { .name = ByteView.from_slice("@ptr_cast"), .kind = .PtrCast,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
},
17 => {
ret IntrinInfo { .name = ByteView.from_slice("@enum2int"), .kind = .Enum2Int,
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
},
18 => {
ret IntrinInfo { .name = ByteView.from_slice("@offsetof"), .kind = .OffsetOf,
.form = .Call, .min_args = 2, .max_args = 2, .result = .Usize };
},
19 => {
ret IntrinInfo { .name = ByteView.from_slice("@field_count"), .kind = .FieldCount,
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
},
_ => {
ret IntrinInfo { .name = ByteView.empty(), .kind = .None, .form = .Call,
.min_args = 0, .max_args = 0, .result = .Unit };
},
}
ret IntrinInfo { .name = ByteView.empty(), .kind = .None, .form = .Call,
.min_args = 0, .max_args = 0, .result = .Unit };
}
fn info(kind: Intrin): IntrinInfo {
ret info_at(@enum2int(kind) as usize);
}
fn table_count(): usize {
ret 20;
}
fn strip_at(name: ByteView): ByteView {
if name.len > 0 && name.ptr[0] == '@' {
ret ByteView { .ptr = name.ptr + 1, .len = name.len - 1 };
}
ret name;
}
// 按名查找 CALL 形态的内建;忽略前导 '@'。
fn lookup_call(name: ByteView): Intrin {
var want = strip_at(name);
var i: usize = 1;
while i < table_count() {
var d = info_at(i);
if form_is(d.form, .Call) {
var dn = strip_at(d.name);
if ByteView.eq(dn, want) { ret d.kind; }
}
i = i + 1;
}
ret .None;
}
// 按接收者类型 + 字段名查找 FIELD 形态内建。
fn lookup_field(recv: LangKind, field: ByteView): Intrin {
if ByteView.eq(field, ByteView.from_slice("len")) {
if kind_is(recv, .Array) || kind_is(recv, .Slice) { ret .Len; }
}
ret .None;
}
fn is_int(e: *TypeEntry): bool {
match e.node {
.Int(x) => { ret true; },
_ => { ret false; },
}
ret false;
}
fn is_float(e: *TypeEntry): bool {
match e.node {
.Float(x) => { ret true; },
_ => { ret false; },
}
ret false;
}
fn is_bool(e: *TypeEntry): bool {
match e.node {
.Bool => { ret true; },
_ => { ret false; },
}
ret false;
}
fn is_ptr(e: *TypeEntry): bool {
match e.node {
.Ptr(x) => { ret true; },
_ => { ret false; },
}
ret false;
}
fn int_bits(e: *TypeEntry): usize {
match e.node {
.Int(x) => { ret x.bits; },
_ => { ret 0; },
}
ret 0;
}
// `as` 的展开规则表(改 as 语义只改这里)。
fn lookup_cast(ir: *TypeTable, ft: usize, tt: usize): Intrin {
var f = TypeTable.node(ir, ft);
var t = TypeTable.node(ir, tt);
if f == null || t == null { ret .None; }
if is_int(f) && is_int(t) {
var fb = int_bits(f);
var tb = int_bits(t);
if fb == tb { ret .BitCast; }
if fb > tb { ret .Truncate; }
ret .IntCast;
}
if is_float(f) && is_float(t) { ret .FloatCast; }
if is_bool(f) && is_int(t) { ret .Bool2Int; }
if is_ptr(f) && is_int(t) { ret .Ptr2Int; }
if is_int(f) && is_ptr(t) { ret .Int2Ptr; }
if is_ptr(f) && is_ptr(t) { ret .PtrCast; }
ret .None;
}
fn test_info_table() {
var d = info(.Sizeof);
@assert(ByteView.eq(d.name, ByteView.from_slice("@sizeof")), "sizeof name");
@assert(d.min_args == 1 && d.max_args == 1, "sizeof arity");
@assert(form_is(d.form, .Call), "sizeof form");
var l = info(.Len);
@assert(ByteView.eq(l.name, ByteView.from_slice("len")), "len name");
@assert(form_is(l.form, .Field), "len form");
}
fn test_lookup_call() {
@assert(@enum2int(lookup_call(ByteView.from_slice("sizeof"))) == @enum2int(Intrin.Sizeof),
"sizeof by bare name");
@assert(@enum2int(lookup_call(ByteView.from_slice("@sizeof"))) == @enum2int(Intrin.Sizeof),
"sizeof by @name");
@assert(@enum2int(lookup_call(ByteView.from_slice("assert"))) == @enum2int(Intrin.Assert),
"assert");
@assert(@enum2int(lookup_call(ByteView.from_slice("len"))) == @enum2int(Intrin.None),
"len is field, not call");
@assert(@enum2int(lookup_call(ByteView.from_slice("nope"))) == @enum2int(Intrin.None),
"unknown");
}
fn test_lookup_field() {
@assert(@enum2int(lookup_field(.Slice, ByteView.from_slice("len"))) ==
@enum2int(Intrin.Len),
"slice len");
@assert(@enum2int(lookup_field(.Array, ByteView.from_slice("len"))) ==
@enum2int(Intrin.Len),
"array len");
@assert(@enum2int(lookup_field(.Struct, ByteView.from_slice("len"))) ==
@enum2int(Intrin.None),
"struct len");
@assert(@enum2int(lookup_field(.Slice, ByteView.from_slice("ptr"))) ==
@enum2int(Intrin.None),
"slice ptr");
}
fn test_lookup_cast() {
var ir: TypeTable = undefined;
match TypeTable.new() {
.Ok(v) => { ir = v; },
.Err(error) => { @assert(false, "type table new failed"); },
}
defer { TypeTable.drop(&ir); }
var i32t = TypeTable.make_int(&ir, 32, true);
var i64t = TypeTable.make_int(&ir, 64, true);
var f64t = TypeTable.make_float(&ir, 64);
var boolt = TypeTable.make_bool(&ir);
var pt = TypeTable.make_ptr(&ir, i32t);
@assert(@enum2int(lookup_cast(&ir, i64t, i32t)) == @enum2int(Intrin.Truncate),
"truncate");
@assert(@enum2int(lookup_cast(&ir, i32t, i64t)) == @enum2int(Intrin.IntCast), "int cast");
@assert(@enum2int(lookup_cast(&ir, i32t, i32t)) == @enum2int(Intrin.BitCast), "bit cast");
@assert(@enum2int(lookup_cast(&ir, f64t, f64t)) == @enum2int(Intrin.FloatCast),
"float cast");
@assert(@enum2int(lookup_cast(&ir, boolt, i32t)) == @enum2int(Intrin.Bool2Int),
"bool2int");
@assert(@enum2int(lookup_cast(&ir, pt, i32t)) == @enum2int(Intrin.Ptr2Int), "ptr2int");
@assert(@enum2int(lookup_cast(&ir, i32t, pt)) == @enum2int(Intrin.Int2Ptr), "int2ptr");
@assert(@enum2int(lookup_cast(&ir, pt, pt)) == @enum2int(Intrin.PtrCast), "ptr cast");
@assert(@enum2int(lookup_cast(&ir, i32t, f64t)) == @enum2int(Intrin.None),
"int->float forbidden");
}
fn main() {
test_info_table();
test_lookup_call();
test_lookup_field();
test_lookup_cast();
}