Files

350 lines
11 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Expect retcode = 0
// 目标布局:由 IR 类型表计算 size/align/字段位偏移,供 ir2vm 使用。默认 64 位平台,
// 聚合按最大字段对齐且不重排。
type .{ .Error } = @import("utils/error.spl");
type .{ .Vec } = @import("utils/vec.spl");
type TypeTable = @import("./type_table.spl");
type .{ .TypeNode, .Agg, .AggKind, .Member } = @import("./type_table.spl");
type .{ .node, .member_at, .make_int, .make_array, .make_agg, .push_member } = @import("./type_table.spl");
type LayoutInfo = struct {
size_bits: usize,
align_bits: usize,
field_bit_offsets: Vec, // usize
field_bit_sizes: Vec, // usize
resolved: bool,
}
type Layout = @self;
infos: Vec, // LayoutInfo
ptr_size: usize,
ptr_align: usize,
type LayoutResult = enum {
Ok: Layout,
Err: Error,
}
fn blank_usizes(): Vec {
ret Vec.blank(@sizeof(usize), @alignof(usize));
}
fn new(): LayoutResult {
ret .Ok(Layout {
.infos = Vec.blank(@sizeof(LayoutInfo), @alignof(LayoutInfo)),
.ptr_size = @sizeof(usize),
.ptr_align = @alignof(usize),
});
}
fn info_at(self: *Layout, id: usize): *LayoutInfo {
if id == 0 || id >= self.infos.len { ret null; }
ret (self.infos.data as *LayoutInfo) + id;
}
// 未解析或越界返回 null。
fn info_of(self: *Layout, id: usize): *LayoutInfo {
var info = info_at(self, id);
if info == null || !info.resolved { ret null; }
ret info;
}
fn drop(self: *Layout) {
var i: usize = 0;
while i < self.infos.len {
var info = info_at(self, i);
if info != null {
Vec.drop(&info.field_bit_offsets);
Vec.drop(&info.field_bit_sizes);
}
i = i + 1;
}
Vec.drop(&self.infos);
}
fn ensure(self: *Layout, n: usize) {
while self.infos.len < n {
var info = LayoutInfo {
.size_bits = 0,
.align_bits = 0,
.field_bit_offsets = blank_usizes(),
.field_bit_sizes = blank_usizes(),
.resolved = false,
};
match Vec.push(&self.infos, &info as *u8) {
.Ok => {},
.Err(error) => { ret; },
}
}
}
fn push_usize(v: *Vec, x: usize) {
Vec.push(v, &x as *u8);
}
fn usize_at(v: *Vec, i: usize): usize {
match Vec.get(v, i) {
.Ok(ptr) => { ret (ptr as *usize).*; },
.Err(error) => { ret 0; },
}
ret 0;
}
// 标量对齐(字节);0 表示没有匹配的标量(即未落地)。
fn scalar_align(size_bytes: usize): usize {
if size_bytes == 1 { ret 1; }
if size_bytes == 2 { ret 2; }
if size_bytes == 4 { ret 4; }
if size_bytes == 8 { ret 8; }
ret 0;
}
fn round_up_bits(x: usize, align_bits: usize): usize {
if align_bits <= 1 { ret x; }
ret (x + align_bits - 1) / align_bits * align_bits;
}
fn layout_scalar(info: *LayoutInfo, size_bits: usize) {
var size_bytes = (size_bits + 7) / 8;
var a = scalar_align(size_bytes);
if a == 0 { ret; }
info.size_bits = size_bytes * 8;
info.align_bits = a * 8;
}
fn layout_ptr(self: *Layout, info: *LayoutInfo) {
if self.ptr_size == 0 { ret; }
info.size_bits = self.ptr_size * 8;
info.align_bits = self.ptr_align * 8;
}
// enum 与 struct/union/shape 的布局。字段索引约定(与 ir2vm 一致):
// enum: 0 = tag,1..n = 各变体载荷(同一偏移);其余按声明顺序。
fn layout_agg(self: *Layout, is_enum: bool, is_union: bool, agg: Agg, info: *LayoutInfo) {
var n = agg.variants.len;
if is_enum {
if agg.tag_type == 0 { ret; }
var tag_info = info_at(self, agg.tag_type);
if tag_info == null || !tag_info.resolved { ret; }
var tag_align_bits = tag_info.align_bits;
if tag_align_bits == 0 { tag_align_bits = 8; }
var payload_align_bits: usize = 8;
var payload_size_bits: usize = 0;
var i: usize = 0;
while i < n {
var m = member_at(&agg.variants, i);
if m != null && m.type_id != 0 {
var fi = info_at(self, m.type_id);
if fi == null || !fi.resolved { ret; }
var fa = fi.align_bits;
if fa == 0 { fa = 8; }
if fa > payload_align_bits { payload_align_bits = fa; }
if fi.size_bits > payload_size_bits { payload_size_bits = fi.size_bits; }
}
i = i + 1;
}
var payload_off = round_up_bits(tag_info.size_bits, payload_align_bits);
var final_align = tag_align_bits;
if payload_align_bits > final_align { final_align = payload_align_bits; }
info.size_bits = round_up_bits(payload_off + payload_size_bits, final_align);
info.align_bits = final_align;
push_usize(&info.field_bit_offsets, 0);
push_usize(&info.field_bit_sizes, tag_info.size_bits);
i = 0;
while i < n {
var m = member_at(&agg.variants, i);
var fsize: usize = 0;
if m != null && m.type_id != 0 {
var fi = info_at(self, m.type_id);
if fi != null { fsize = fi.size_bits; }
}
push_usize(&info.field_bit_offsets, payload_off);
push_usize(&info.field_bit_sizes, fsize);
i = i + 1;
}
ret;
}
if n == 0 {
info.size_bits = 0;
info.align_bits = 8;
ret;
}
var max_align_bits: usize = 8;
var offset_bits: usize = 0;
var i: usize = 0;
while i < n {
var m = member_at(&agg.variants, i);
var fsize_bits: usize = 0;
var fsize_align: usize = 8;
if m != null && m.type_id != 0 {
var fi = info_at(self, m.type_id);
if fi == null || !fi.resolved { ret; }
fsize_bits = fi.size_bits;
fsize_align = fi.align_bits;
if fsize_align == 0 { fsize_align = 8; }
}
if is_union {
push_usize(&info.field_bit_offsets, 0);
push_usize(&info.field_bit_sizes, fsize_bits);
if fsize_bits > info.size_bits { info.size_bits = fsize_bits; }
} else {
offset_bits = round_up_bits(offset_bits, fsize_align);
push_usize(&info.field_bit_offsets, offset_bits);
push_usize(&info.field_bit_sizes, fsize_bits);
offset_bits = offset_bits + fsize_bits;
}
if fsize_align > max_align_bits { max_align_bits = fsize_align; }
i = i + 1;
}
if is_union {
info.size_bits = round_up_bits(info.size_bits, max_align_bits);
} else {
info.size_bits = round_up_bits(offset_bits, max_align_bits);
}
info.align_bits = max_align_bits;
}
fn resolve_members(self: *Layout, tt: *TypeTable, agg: Agg) {
var i: usize = 0;
while i < agg.variants.len {
var m = member_at(&agg.variants, i);
if m != null && m.type_id != 0 { resolve_one(self, tt, m.type_id); }
i = i + 1;
}
}
fn resolve_one(self: *Layout, tt: *TypeTable, id: usize) {
var info = info_at(self, id);
if info == null || info.resolved { ret; }
var e = node(tt, id);
if e == null { ret; }
info.resolved = true;
match e.node {
.Error => { info.size_bits = 0; info.align_bits = 8; },
.Infer => { info.size_bits = 0; info.align_bits = 8; },
.Forall => { info.size_bits = 0; info.align_bits = 8; },
.Exists => { info.size_bits = 0; info.align_bits = 8; },
.Never => { info.size_bits = 0; info.align_bits = 8; },
.Unit => { info.size_bits = 0; info.align_bits = 8; },
.Any => { layout_ptr(self, info); },
.Bool => { layout_scalar(info, 8); },
.Int(x) => { layout_scalar(info, x.bits); },
.Float(x) => { layout_scalar(info, x.bits); },
.Ptr(x) => { layout_ptr(self, info); },
.Array(a) => {
resolve_one(self, tt, a.element);
var elem = info_at(self, a.element);
if elem == null || !elem.resolved { ret; }
info.size_bits = elem.size_bits * a.len;
info.align_bits = elem.align_bits;
},
.Struct(agg) => {
resolve_members(self, tt, agg);
layout_agg(self, false, false, agg, info);
},
.Union(agg) => {
resolve_members(self, tt, agg);
layout_agg(self, false, true, agg, info);
},
.Enum(agg) => {
if agg.tag_type != 0 { resolve_one(self, tt, agg.tag_type); }
resolve_members(self, tt, agg);
layout_agg(self, true, false, agg, info);
},
.Shape(agg) => {
resolve_members(self, tt, agg);
layout_agg(self, false, false, agg, info);
},
.Fn(f) => {
var i: usize = 0;
while i < f.params.len {
resolve_one(self, tt, usize_at(&f.params, i));
i = i + 1;
}
resolve_one(self, tt, f.ret_type);
layout_ptr(self, info);
},
}
}
fn resolve(self: *Layout, tt: *TypeTable) {
ensure(self, tt.table.len);
var i: usize = 1;
while i < tt.table.len {
resolve_one(self, tt, i);
i = i + 1;
}
}
fn main() {
var tt: TypeTable = undefined;
match TypeTable.new() {
.Ok(v) => { tt = v; },
.Err(error) => { @assert(false, "layout test: tt init"); ret; },
}
defer { TypeTable.drop(&tt); }
var i32t = make_int(&tt, 32, true);
var u8t = make_int(&tt, 8, false);
var members = Vec.blank(@sizeof(Member), @alignof(Member));
push_member(&members, "a", i32t);
push_member(&members, "b", u8t);
var agg = Agg { .mode = .Auto, .fixed_align_bits = 0, .tag_type = 0, .variants = members };
var sid = make_agg(&tt, .Struct, agg);
var layout: Layout = undefined;
match new() {
.Ok(v) => { layout = v; },
.Err(error) => { @assert(false, "layout test: layout init"); ret; },
}
defer { drop(&layout); }
resolve(&layout, &tt);
var info = info_of(&layout, sid);
if info == null {
@assert(false, "struct layout missing");
ret;
}
@assert(info.size_bits == 64, "struct size_bits");
@assert(info.align_bits == 32, "struct align_bits");
@assert(usize_at(&info.field_bit_offsets, 0) == 0, "field0 off");
@assert(usize_at(&info.field_bit_offsets, 1) == 32, "field1 off");
var arr = make_array(&tt, i32t, 3);
resolve(&layout, &tt);
var ainfo = info_of(&layout, arr);
if ainfo == null {
@assert(false, "array layout missing");
ret;
}
@assert(ainfo.size_bits == 96, "array size_bits");
@assert(ainfo.align_bits == 32, "array align_bits");
var pt = TypeTable.make_ptr(&tt, u8t);
resolve(&layout, &tt);
var pinfo = info_of(&layout, pt);
if pinfo == null {
@assert(false, "ptr layout missing");
ret;
}
@assert(pinfo.size_bits == @sizeof(usize) * 8, "ptr size_bits");
ret;
}