359 lines
11 KiB
Plaintext
359 lines
11 KiB
Plaintext
// Expect retcode = 0
|
||
|
||
// 目标布局(移植自 stage1/spl_layout.c)。输入是 IR 类型表 TypeTable;
|
||
// 为每个类型计算 size_bits / align_bits / 字段位偏移,供 ir2vm 使用。
|
||
// 默认 64 位平台:标量按 1/2/4/8 字节对齐,指针 = usize,聚合按最大字段对齐
|
||
// 且不允许重排(与 stage1 的默认策略一致)。
|
||
|
||
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; }
|
||
match Vec.get(&self.infos, id) {
|
||
.Ok(ptr) => { ret ptr as *LayoutInfo; },
|
||
.Err(error) => { ret null; },
|
||
}
|
||
ret null;
|
||
}
|
||
|
||
// 未解析或越界返回 null(对应 stage1 spl_layout_info_of)。
|
||
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) {
|
||
match Vec.push(v, &x as *u8) {
|
||
.Ok => {},
|
||
.Err(error) => {},
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|