Files
spl/stage2/ir2c.spl
T

1332 lines
42 KiB
Plaintext
Raw 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 -> C99 后端(P1a 子集)。
//
// 设计:每个 IR ref 一个 C 局部变量(C 编译器 -O2 会做 mem2reg),标签用 goto;
// 类型直接由 IR 的 tid 决定,生成期无运行时分发。
//
// 每个非 extern 函数的 C 名为 `spl_f<fid>`;extern 函数直接用其 SPL 名
// (由 runtime 提供)。暂不支持聚合/切片/全局数据。
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, .append_u64 } = @import("utils/fmt.spl");
type TypeTable = @import("./type_table.spl");
type LayoutM = @import("./layout.spl");
type .{ .Layout } = @import("./layout.spl");
type AttrM = @import("./attr.spl");
type .{ .Attr } = @import("./attr.spl");
type IrM = @import("./ir.spl");
type .{ .Ir, .IrFunc, .IrEntry, .Global } = @import("./ir.spl");
type Ir2C = @self;
ir: *Ir,
ty: *TypeTable,
layout: *Layout,
attr: *Attr,
out: *ByteBuffer,
gdata_n: usize,
failed: bool,
fn usize_at(v: *Vec, i: usize): usize {
ret (v.data as *usize)[i];
}
fn put(self: *Ir2C, s: []u8) {
match ByteBuffer.append(self.out, s) {
.Ok => {},
.Err(error) => { self.failed = true; },
}
}
fn put_byte(self: *Ir2C, b: u8) {
match ByteBuffer.append_byte(self.out, b) {
.Ok => {},
.Err(error) => { self.failed = true; },
}
}
fn put_u(self: *Ir2C, v: usize) {
match append_usize(self.out, v, 10, false) {
.Ok => {},
.Err(error) => { self.failed = true; },
}
}
fn put_i(self: *Ir2C, v: i64) {
match append_i64(self.out, v, 10, false) {
.Ok => {},
.Err(error) => { self.failed = true; },
}
}
fn put_u64(self: *Ir2C, v: u64) {
match append_u64(self.out, v, 10, false) {
.Ok => {},
.Err(error) => { self.failed = true; },
}
}
fn put_bv(self: *Ir2C, v: ByteView) {
match ByteBuffer.append(self.out, ByteView.as_slice(v)) {
.Ok => {},
.Err(error) => { self.failed = true; },
}
}
fn put_unsupported(self: *Ir2C, what: []u8) {
self.failed = true;
put(self, "/* unsupported: ");
put(self, what);
put(self, " */\n");
}
// ---- 类型查询 ----
fn info_of(self: *Ir2C, tid: usize): *LayoutM.LayoutInfo {
ret LayoutM.info_of(self.layout, tid);
}
fn type_size(self: *Ir2C, tid: usize): usize {
var info = info_of(self, tid);
if info == null { ret 0; }
ret (info.size_bits + 7) / 8;
}
fn type_align(self: *Ir2C, tid: usize): usize {
var info = info_of(self, tid);
if info == null { ret 1; }
var a = info.align_bits / 8;
if a == 0 { a = 1; }
ret a;
}
fn is_aggregate(self: *Ir2C, tid: usize): bool {
var te = TypeTable.node(self.ty, tid);
if te == null { ret false; }
match te.node {
.Array => { ret true; },
.Struct => { ret true; },
.Union => { ret true; },
.Enum => { ret true; },
.Shape => { ret true; },
_ => {},
}
ret false;
}
fn tid_is_signed(self: *Ir2C, tid: usize): bool {
var te = TypeTable.node(self.ty, tid);
if te == null { ret false; }
match te.node {
.Int(x) => { ret x.is_signed; },
_ => {},
}
ret false;
}
fn tid_int_bits(self: *Ir2C, tid: usize): usize {
var te = TypeTable.node(self.ty, tid);
if te == null { ret 0; }
match te.node {
.Int(x) => { ret x.bits; },
_ => {},
}
ret 0;
}
fn tid_is_bool(self: *Ir2C, tid: usize): bool {
var te = TypeTable.node(self.ty, tid);
if te == null { ret false; }
match te.node {
.Bool => { ret true; },
_ => {},
}
ret false;
}
fn tid_is_unit(self: *Ir2C, tid: usize): bool {
if tid == 0 { ret true; }
var te = TypeTable.node(self.ty, tid);
if te == null { ret true; }
match te.node {
.Unit => { ret true; },
_ => {},
}
ret false;
}
fn fn_ret_type(self: *Ir2C, fn_tid: usize): usize {
var te = TypeTable.node(self.ty, fn_tid);
if te == null { ret 0; }
match te.node {
.Fn(f) => { ret f.ret_type; },
_ => {},
}
ret 0;
}
// ---- C 类型 ----
fn put_type(self: *Ir2C, tid: usize): bool {
var te = TypeTable.node(self.ty, tid);
if te == null { ret false; }
match te.node {
.Unit => { put(self, "void"); ret true; },
.Never => { put(self, "void"); ret true; },
.Bool => { put(self, "u8"); ret true; },
.Int(x) => {
if x.bits == 8 {
if x.is_signed { put(self, "i8"); } else { put(self, "u8"); }
} else if x.bits == 16 {
if x.is_signed { put(self, "i16"); } else { put(self, "u16"); }
} else if x.bits == 32 {
if x.is_signed { put(self, "i32"); } else { put(self, "u32"); }
} else {
if x.is_signed { put(self, "i64"); } else { put(self, "u64"); }
}
ret true;
},
.Float(x) => {
if x.bits == 32 { put(self, "float"); } else { put(self, "double"); }
ret true;
},
.Ptr(x) => { put(self, "void*"); ret true; },
.Fn(f) => { put(self, "void*"); ret true; },
// 聚合值统一以「指向存储的指针」表示
.Array => { put(self, "void*"); ret true; },
.Struct => { put(self, "void*"); ret true; },
.Union => { put(self, "void*"); ret true; },
.Enum => { put(self, "void*"); ret true; },
.Shape => { put(self, "void*"); ret true; },
_ => {},
}
ret false;
}
fn fn_is_extern(self: *Ir2C, f: *IrFunc): bool {
var i: usize = 0;
while i < f.attrs.len {
var a = AttrM.node(self.attr, usize_at(&f.attrs, i));
if a != null {
var info = AttrM.info_at(self.attr, a.info_index);
if info != null {
if ByteView.eq(info.name, ByteView.from_slice("extern")) { ret true; }
}
}
i = i + 1;
}
ret false;
}
fn c_fn_name(self: *Ir2C, fid: usize) {
var f = IrM.func(self.ir, fid);
if f != null && fn_is_extern(self, f) {
put_bv(self, f.name);
ret;
}
put(self, "f");
put_u(self, fid);
}
// ---- 常量解析 ----
fn const_node(self: *Ir2C, f: *IrFunc, ref: usize): usize {
if ref == 0 || ref >= f.nodes.len { ret 0; }
var n = (f.nodes.data as *IrEntry) + ref;
match n.body {
.Const(c) => {
match c.lit {
.Int(v) => { ret v; },
.Char(v) => { ret v as usize; },
.Ptr(v) => { ret v; },
_ => {},
}
},
_ => {},
}
ret 0;
}
// ---- 聚合字段 ----
fn field_tid(self: *Ir2C, tid: usize, idx: usize): usize {
var te = TypeTable.node(self.ty, tid);
if te == null { ret 0; }
match te.node {
.Enum(agg) => {
if idx == 0 { ret agg.tag_type; }
var vidx = idx - 1;
if vidx < agg.variants.len {
var m = TypeTable.member_at(&agg.variants, vidx);
if m != null { ret m.type_id; }
}
ret 0;
},
.Struct(agg) => {
if idx < agg.variants.len {
var m = TypeTable.member_at(&agg.variants, idx);
if m != null { ret m.type_id; }
}
ret 0;
},
.Union(agg) => {
if idx < agg.variants.len {
var m = TypeTable.member_at(&agg.variants, idx);
if m != null { ret m.type_id; }
}
ret 0;
},
.Shape(agg) => {
if idx < agg.variants.len {
var m = TypeTable.member_at(&agg.variants, idx);
if m != null { ret m.type_id; }
}
ret 0;
},
.Array(a) => { ret a.element; },
_ => {},
}
ret 0;
}
fn field_off(self: *Ir2C, tid: usize, idx: usize): usize {
var te = TypeTable.node(self.ty, tid);
if te == null { ret 0; }
match te.node {
.Array(a) => { ret idx * type_size(self, a.element); },
_ => {},
}
var info = info_of(self, tid);
if info == null || idx >= info.field_bit_offsets.len { ret 0; }
ret usize_at(&info.field_bit_offsets, idx) / 8;
}
// 聚合值统一表示为「指向存储的指针」:声明一个 8 字节对齐的缓冲并把指针存入 v<ref>。
fn emit_agg_local(self: *Ir2C, ref: usize, tid: usize) {
var size = type_size(self, tid);
var words = (size + 7) / 8;
if words == 0 { words = 1; }
put(self, "u64 s");
put_u(self, ref);
put(self, "[");
put_u(self, words);
put(self, "]; void* v");
put_u(self, ref);
put(self, " = (void*)s");
put_u(self, ref);
put(self, ";\n");
}
fn emit_agg_construct(self: *Ir2C, ref: usize, n: *IrEntry) {
match n.body {
.AggConstruct(a) => {
if !is_aggregate(self, a.tid) { put_unsupported(self, "agg construct non-agg"); ret; }
bind_agg(self, ref);
var i: usize = 0;
while i < a.fields.len {
var fref = usize_at(&a.fields, i);
var ftid = field_tid(self, a.tid, i);
var foff = field_off(self, a.tid, i);
if ftid != 0 {
if is_aggregate(self, ftid) {
var fsz = type_size(self, ftid);
put(self, "memcpy((u8*)v");
put_u(self, ref);
put(self, " + ");
put_u(self, foff);
put(self, ", v");
put_u(self, fref);
put(self, ", ");
put_u(self, fsz);
put(self, ");\n");
} else {
put(self, "*(");
put_type(self, ftid);
put(self, "*)((u8*)v");
put_u(self, ref);
put(self, " + ");
put_u(self, foff);
put(self, ") = v");
put_u(self, fref);
put(self, ";\n");
}
}
i = i + 1;
}
},
_ => {},
}
}
fn emit_agg_extract(self: *Ir2C, ref: usize, n: *IrEntry) {
match n.body {
.AggExtract(a) => {
var foff = field_off(self, a.tid, a.field_idx);
if is_aggregate(self, a.field_tid) {
var fsz = type_size(self, a.field_tid);
bind_agg(self, ref);
put(self, "memcpy(v");
put_u(self, ref);
put(self, ", (u8*)v");
put_u(self, a.val);
put(self, " + ");
put_u(self, foff);
put(self, ", ");
put_u(self, fsz);
put(self, ");\n");
} else {
put(self, "v");
put_u(self, ref);
put(self, " = *(");
put_type(self, a.field_tid);
put(self, "*)((u8*)v");
put_u(self, a.val);
put(self, " + ");
put_u(self, foff);
put(self, ");\n");
}
},
_ => {},
}
}
fn emit_agg_insert(self: *Ir2C, ref: usize, n: *IrEntry) {
match n.body {
.AggInsert(a) => {
bind_agg(self, ref);
var sz = type_size(self, a.tid);
put(self, "memcpy(v");
put_u(self, ref);
put(self, ", v");
put_u(self, a.agg);
put(self, ", ");
put_u(self, sz);
put(self, ");\n");
var ftid = field_tid(self, a.tid, a.field_idx);
var foff = field_off(self, a.tid, a.field_idx);
if ftid != 0 {
if is_aggregate(self, ftid) {
var fsz = type_size(self, ftid);
put(self, "memcpy((u8*)v");
put_u(self, ref);
put(self, " + ");
put_u(self, foff);
put(self, ", v");
put_u(self, a.field);
put(self, ", ");
put_u(self, fsz);
put(self, ");\n");
} else {
put(self, "*(");
put_type(self, ftid);
put(self, "*)((u8*)v");
put_u(self, ref);
put(self, " + ");
put_u(self, foff);
put(self, ") = v");
put_u(self, a.field);
put(self, ";\n");
}
}
},
_ => {},
}
}
// ---- 节点发射 ----
fn emit_arith(self: *Ir2C, ref: usize, n: *IrEntry) {
match n.body {
.Arith(a) => {
put(self, "v");
put_u(self, ref);
put(self, " = ");
match n.kind {
.ArithAdd => { put(self, "(v"); put_u(self, a.left); put(self, " + v"); put_u(self, a.right); put(self, ")"); },
.ArithSub => { put(self, "(v"); put_u(self, a.left); put(self, " - v"); put_u(self, a.right); put(self, ")"); },
.ArithMul => { put(self, "(v"); put_u(self, a.left); put(self, " * v"); put_u(self, a.right); put(self, ")"); },
.ArithDiv => { put(self, "(v"); put_u(self, a.left); put(self, " / v"); put_u(self, a.right); put(self, ")"); },
.ArithRem => { put(self, "(v"); put_u(self, a.left); put(self, " % v"); put_u(self, a.right); put(self, ")"); },
.ArithAnd => { put(self, "(v"); put_u(self, a.left); put(self, " & v"); put_u(self, a.right); put(self, ")"); },
.ArithOr => { put(self, "(v"); put_u(self, a.left); put(self, " | v"); put_u(self, a.right); put(self, ")"); },
.ArithXor => { put(self, "(v"); put_u(self, a.left); put(self, " ^ v"); put_u(self, a.right); put(self, ")"); },
.ArithShl => { put(self, "(v"); put_u(self, a.left); put(self, " << v"); put_u(self, a.right); put(self, ")"); },
.ArithShr => { put(self, "(v"); put_u(self, a.left); put(self, " >> v"); put_u(self, a.right); put(self, ")"); },
.ArithNeg => { put(self, "(-v"); put_u(self, a.left); put(self, ")"); },
.ArithNot => {
if tid_is_bool(self, a.tid) {
put(self, "(!v");
} else {
put(self, "(~v");
}
put_u(self, a.left);
put(self, ")");
},
.ArithAbs => {
put(self, "(v");
put_u(self, a.left);
put(self, " < 0 ? -v");
put_u(self, a.left);
put(self, " : v");
put_u(self, a.left);
put(self, ")");
},
_ => { put(self, "0"); },
}
put(self, ";\n");
},
_ => {},
}
}
fn emit_cmp(self: *Ir2C, ref: usize, n: *IrEntry) {
match n.body {
.Cmp(c) => {
put(self, "v");
put_u(self, ref);
put(self, " = (v");
put_u(self, c.a);
match n.kind {
.CmpEq => { put(self, " == v"); },
.CmpNe => { put(self, " != v"); },
.CmpLt => { put(self, " < v"); },
.CmpLe => { put(self, " <= v"); },
.CmpGt => { put(self, " > v"); },
.CmpGe => { put(self, " >= v"); },
_ => { put(self, " == v"); },
}
put_u(self, c.b);
put(self, ");\n");
},
_ => {},
}
}
fn emit_cast(self: *Ir2C, ref: usize, n: *IrEntry) {
match n.body {
.Cast(c) => {
put(self, "v");
put_u(self, ref);
put(self, " = ");
var from_is_ptr = false;
var from_is_bool = false;
var to_is_ptr = false;
var te = TypeTable.node(self.ty, c.from_tid);
if te != null {
match te.node {
.Ptr(p) => { from_is_ptr = true; },
.Bool => { from_is_bool = true; },
_ => {},
}
}
var tte = TypeTable.node(self.ty, c.to_tid);
if tte != null {
match tte.node { .Ptr(p) => { to_is_ptr = true; }, _ => {} }
}
if to_is_ptr {
put(self, "(void*)(usize)v");
put_u(self, c.val);
} else if from_is_ptr {
put(self, "(usize)v");
put_u(self, c.val);
} else if from_is_bool {
put(self, "(v");
put_u(self, c.val);
put(self, " ? 1 : 0)");
} else {
put(self, "v");
put_u(self, c.val);
}
put(self, ";\n");
},
_ => {},
}
}
fn emit_const(self: *Ir2C, ref: usize, n: *IrEntry) {
match n.body {
.Const(c) => {
if tid_is_unit(self, c.tid) { ret; }
put(self, "v");
put_u(self, ref);
put(self, " = ");
match c.lit {
.Bool(b) => { if b { put(self, "1"); } else { put(self, "0"); } },
.Char(v) => { put_u(self, v as usize); },
.Int(v) => {
if tid_is_signed(self, c.tid) {
var iv = v as i64;
if iv == (-9223372036854775807 - 1) {
put(self, "(-9223372036854775807 - 1)");
} else {
put_i(self, iv);
}
} else {
put_u(self, v);
if tid_int_bits(self, c.tid) >= 64 { put(self, "ull"); }
}
},
.Ptr(v) => { put(self, "(void*)"); put_u(self, v); },
.Global(g) => { put(self, "(void*)spl_g_"); put_u(self, g); },
.Float(v) => { put(self, "0.0"); },
.Fn(fid) => { put(self, "(void*)&"); c_fn_name(self, fid); },
}
put(self, ";\n");
},
_ => {},
}
}
fn emit_mem(self: *Ir2C, f: *IrFunc, ref: usize, n: *IrEntry) {
match n.body {
.MemAlloc(m) => {
put(self, "v");
put_u(self, ref);
put(self, " = (void*)s");
put_u(self, ref);
put(self, ";\n");
},
.MemLoad(m) => {
if is_aggregate(self, m.tid) {
var sz = type_size(self, m.tid);
bind_agg(self, ref);
put(self, "memcpy(v");
put_u(self, ref);
put(self, ", v");
put_u(self, m.ptr);
put(self, ", ");
put_u(self, sz);
put(self, ");\n");
} else {
put(self, "v");
put_u(self, ref);
put(self, " = *(");
put_type(self, m.tid);
put(self, "*)v");
put_u(self, m.ptr);
put(self, ";\n");
}
},
.MemStore(m) => {
if is_aggregate(self, m.tid) {
var sz = type_size(self, m.tid);
put(self, "memcpy(v");
put_u(self, m.ptr);
put(self, ", v");
put_u(self, m.val);
put(self, ", ");
put_u(self, sz);
put(self, ");\n");
} else {
put(self, "*(");
put_type(self, m.tid);
put(self, "*)v");
put_u(self, m.ptr);
put(self, " = v");
put_u(self, m.val);
put(self, ";\n");
}
},
.MemOffset(m) => {
var size = type_size(self, m.tid);
if size == 0 { size = 1; }
put(self, "v");
put_u(self, ref);
put(self, " = (void*)((u8*)v");
put_u(self, m.ptr);
put(self, " + (u64)v");
put_u(self, m.offset);
put(self, " * ");
put_u(self, size);
put(self, ");\n");
},
.MemFieldPtr(m) => {
var foff = field_off(self, m.tid, m.field_idx);
put(self, "v");
put_u(self, ref);
put(self, " = (void*)((u8*)v");
put_u(self, m.agg);
put(self, " + ");
put_u(self, foff);
put(self, ");\n");
},
.MemGlobalAlloc(m) => {
var ctid: usize = 0;
var pv: usize = 0;
var found = false;
if m.const_node > 0 && m.const_node < f.nodes.len {
var cn = (f.nodes.data as *IrEntry) + m.const_node;
match cn.body {
.Const(c) => {
ctid = c.tid;
match c.lit { .Ptr(v) => { pv = v; found = true; }, _ => {} }
},
_ => {},
}
}
if !found { put(self, "v"); put_u(self, ref); put(self, " = (void*)0;\n"); ret; }
var size = type_size(self, ctid);
if size == 0 { size = 8; }
var gid = self.gdata_n;
self.gdata_n = self.gdata_n + 1;
var words = (size + 7) / 8;
if words == 0 { words = 1; }
put(self, "static const u64 g");
put_u(self, gid);
put(self, "[");
put_u(self, words);
put(self, "] = {");
var j: usize = 0;
while j < words {
if j > 0 { put(self, ", "); }
var w: u64 = 0;
var k: usize = 0;
while k < 8 {
var idx = j * 8 + k;
var b: u64 = 0;
if idx < size { b = ((pv + idx) as *u8).* as u64; }
w = w | (b << (k as u64 * 8));
k = k + 1;
}
put_u64(self, w);
put(self, "ull");
j = j + 1;
}
put(self, "};\nv");
put_u(self, ref);
put(self, " = (void*)g");
put_u(self, gid);
put(self, ";\n");
},
_ => {},
}
}
fn emit_typeop(self: *Ir2C, ref: usize, n: *IrEntry) {
match n.body {
.TypeOp(t) => {
var v: usize = 0;
match n.kind {
.TypeSizeof => { v = type_size(self, t.tid); },
.TypeBitsizeof => {
var info = info_of(self, t.tid);
if info != null { v = info.size_bits; }
},
.TypeAlignof => { v = type_align(self, t.tid); },
.TypeFieldCount => {
var te = TypeTable.node(self.ty, t.tid);
if te != null {
match te.node {
.Struct(a) => { v = a.variants.len; },
.Union(a) => { v = a.variants.len; },
.Array(a) => { v = a.len; },
_ => {},
}
}
},
_ => {},
}
put(self, "v");
put_u(self, ref);
put(self, " = ");
put_u(self, v);
put(self, ";\n");
},
_ => {},
}
}
// 节点的「值类型」;0 表示无结果。
fn result_tid(self: *Ir2C, n: *IrEntry): usize {
match n.body {
.Arith(a) => { ret a.tid; },
.Cmp(c) => { ret TypeTable.make_bool(self.ty); },
.Cast(c) => { ret c.to_tid; },
.MemAlloc(m) => { ret TypeTable.make_ptr(self.ty, m.tid); },
.MemGlobalAlloc(m) => { ret TypeTable.make_ptr(self.ty, m.tid); },
.MemLoad(m) => { ret m.tid; },
.MemOffset(m) => { ret TypeTable.make_ptr(self.ty, m.tid); },
.MemFieldPtr(m) => {
var ft = field_tid(self, m.tid, m.field_idx);
ret TypeTable.make_ptr(self.ty, ft);
},
.Const(c) => { ret c.tid; },
.TypeOp(t) => { ret TypeTable.make_int(self.ty, 64, false); },
.AggConstruct(a) => { ret a.tid; },
.AggExtract(a) => { ret a.field_tid; },
.AggInsert(a) => { ret a.tid; },
.Select(s) => { ret s.tid; },
.Call(c) => { ret c.tid; },
.Param(p) => { ret p.tid; },
_ => {},
}
ret 0;
}
fn res_tid_ref(self: *Ir2C, f: *IrFunc, ref: usize): usize {
if ref == 0 || ref >= f.nodes.len { ret 0; }
ret result_tid(self, (f.nodes.data as *IrEntry) + ref);
}
fn emit_call(self: *Ir2C, f: *IrFunc, ref: usize, n: *IrEntry) {
match n.body {
.Call(c) => {
var callee: usize = 0;
if c.func > 0 && c.func < f.nodes.len {
var fnode = (f.nodes.data as *IrEntry) + c.func;
match fnode.body {
.Const(cc) => {
match cc.lit { .Fn(nf) => { callee = nf; }, _ => {} }
},
_ => {},
}
}
var ret_agg = is_aggregate(self, c.tid);
if callee != 0 {
if ret_agg {
bind_agg(self, ref);
} else if !tid_is_unit(self, c.tid) {
put(self, "v");
put_u(self, ref);
put(self, " = ");
}
c_fn_name(self, callee);
put(self, "(");
if ret_agg {
put(self, "v");
put_u(self, ref);
}
var k: usize = 0;
while k < c.params.len {
if k > 0 || ret_agg { put(self, ", "); }
put(self, "v");
put_u(self, usize_at(&c.params, k));
k = k + 1;
}
put(self, ");\n");
ret;
}
// 间接调用:从 func 值的类型构造函数指针类型
var func_tid = res_tid_ref(self, f, c.func);
var ftnode = TypeTable.node(self.ty, func_tid);
if ftnode == null {
put_unsupported(self, "indirect call type");
ret;
}
var params_tid: usize = 0;
match ftnode.node { .Fn(fd) => { params_tid = func_tid; }, _ => {} }
if ret_agg {
bind_agg(self, ref);
} else if !tid_is_unit(self, c.tid) {
put(self, "v");
put_u(self, ref);
put(self, " = ");
}
put(self, "((");
if ret_agg || tid_is_unit(self, c.tid) {
put(self, "void");
} else {
if !put_type(self, c.tid) { put(self, "void"); }
}
put(self, " (*)(");
var need_comma = false;
if ret_agg {
put(self, "void*");
need_comma = true;
}
var np: usize = 0;
var te = TypeTable.node(self.ty, params_tid);
if te != null {
match te.node {
.Fn(fd) => {
np = fd.params.len;
var kp: usize = 0;
while kp < np {
if need_comma { put(self, ", "); }
var pt = usize_at(&fd.params, kp);
if is_aggregate(self, pt) {
put(self, "void*");
} else {
if !put_type(self, pt) { put(self, "void*"); }
}
need_comma = true;
kp = kp + 1;
}
},
_ => {},
}
}
if !need_comma { put(self, "void"); }
put(self, "))v");
put_u(self, c.func);
put(self, ")(");
if ret_agg {
put(self, "v");
put_u(self, ref);
}
var k2: usize = 0;
while k2 < c.params.len {
if k2 > 0 || ret_agg { put(self, ", "); }
put(self, "v");
put_u(self, usize_at(&c.params, k2));
k2 = k2 + 1;
}
put(self, ");\n");
},
_ => {},
}
}
fn emit_control(self: *Ir2C, f: *IrFunc, ref: usize, n: *IrEntry, ret_tid: usize) {
match n.body {
.Param(p) => {
var idx = const_node(self, f, p.idx);
if is_aggregate(self, p.tid) {
put(self, "v");
put_u(self, ref);
put(self, " = a");
put_u(self, idx);
put(self, ";\n");
} else {
put(self, "v");
put_u(self, ref);
put(self, " = (");
put_type(self, p.tid);
put(self, ")a");
put_u(self, idx);
put(self, ";\n");
}
},
.Ret(r) => {
if is_aggregate(self, ret_tid) {
if r.val != 0 {
var sz = type_size(self, ret_tid);
put(self, "memcpy(ret__, v");
put_u(self, r.val);
put(self, ", ");
put_u(self, sz);
put(self, ");\n");
}
put(self, "return;\n");
} else if tid_is_unit(self, ret_tid) {
put(self, "return;\n");
} else {
put(self, "return v");
put_u(self, r.val);
put(self, ";\n");
}
},
.Br(b) => {
put(self, "if (v");
put_u(self, b.cond);
put(self, ") goto L");
put_u(self, b.true_label);
put(self, ";\ngoto L");
put_u(self, b.false_label);
put(self, ";\n");
},
.Jmp(j) => {
put(self, "goto L");
put_u(self, j.label);
put(self, ";\n");
},
.Call(c) => { emit_call(self, f, ref, n); },
_ => {},
}
}
fn hex_digit(v: u8): []u8 {
if v == 0 { ret "0"; }
if v == 1 { ret "1"; }
if v == 2 { ret "2"; }
if v == 3 { ret "3"; }
if v == 4 { ret "4"; }
if v == 5 { ret "5"; }
if v == 6 { ret "6"; }
if v == 7 { ret "7"; }
if v == 8 { ret "8"; }
if v == 9 { ret "9"; }
if v == 10 { ret "a"; }
if v == 11 { ret "b"; }
if v == 12 { ret "c"; }
if v == 13 { ret "d"; }
if v == 14 { ret "e"; }
ret "f";
}
fn emit_c_string(self: *Ir2C, msg: ByteView) {
put(self, "\"");
var i: usize = 0;
while i < msg.len {
var b = msg.ptr[i];
if b == 34 { put(self, "\\\""); }
else if b == 92 { put(self, "\\\\"); }
else if b == 10 { put(self, "\\n"); }
else if b == 13 { put(self, "\\r"); }
else if b == 9 { put(self, "\\t"); }
else if b >= 32 && b < 127 { put_byte(self, b); }
else {
put(self, "\\x");
put(self, hex_digit((b >> 4) & 15));
put(self, hex_digit(b & 15));
}
i = i + 1;
}
put(self, "\"");
}
fn emit_node(self: *Ir2C, fid: usize, f: *IrFunc, ref: usize, n: *IrEntry, ret_tid: usize) {
match n.kind {
.TypeConst => { emit_const(self, ref, n); },
.ArithAdd => { emit_arith(self, ref, n); },
.ArithSub => { emit_arith(self, ref, n); },
.ArithMul => { emit_arith(self, ref, n); },
.ArithDiv => { emit_arith(self, ref, n); },
.ArithRem => { emit_arith(self, ref, n); },
.ArithAnd => { emit_arith(self, ref, n); },
.ArithOr => { emit_arith(self, ref, n); },
.ArithXor => { emit_arith(self, ref, n); },
.ArithShl => { emit_arith(self, ref, n); },
.ArithShr => { emit_arith(self, ref, n); },
.ArithNeg => { emit_arith(self, ref, n); },
.ArithNot => { emit_arith(self, ref, n); },
.ArithAbs => { emit_arith(self, ref, n); },
.CmpEq => { emit_cmp(self, ref, n); },
.CmpNe => { emit_cmp(self, ref, n); },
.CmpLt => { emit_cmp(self, ref, n); },
.CmpLe => { emit_cmp(self, ref, n); },
.CmpGt => { emit_cmp(self, ref, n); },
.CmpGe => { emit_cmp(self, ref, n); },
.CastTrunc => { emit_cast(self, ref, n); },
.CastZext => { emit_cast(self, ref, n); },
.CastSext => { emit_cast(self, ref, n); },
.CastBitcast => { emit_cast(self, ref, n); },
.CastPtr2int => { emit_cast(self, ref, n); },
.CastInt2ptr => { emit_cast(self, ref, n); },
.CastBool2int => { emit_cast(self, ref, n); },
.MemAlloca => { emit_mem(self, f, ref, n); },
.MemLoad => { emit_mem(self, f, ref, n); },
.MemStore => { emit_mem(self, f, ref, n); },
.MemOffset => { emit_mem(self, f, ref, n); },
.MemFieldPtr => { emit_mem(self, f, ref, n); },
.MemGlobalAlloc => { emit_mem(self, f, ref, n); },
.AggConstruct => { emit_agg_construct(self, ref, n); },
.AggExtract => { emit_agg_extract(self, ref, n); },
.AggInsert => { emit_agg_insert(self, ref, n); },
.TypeSizeof => { emit_typeop(self, ref, n); },
.TypeBitsizeof => { emit_typeop(self, ref, n); },
.TypeAlignof => { emit_typeop(self, ref, n); },
.TypeFieldCount => { emit_typeop(self, ref, n); },
.ControlParam => { emit_control(self, f, ref, n, ret_tid); },
.ControlRet => { emit_control(self, f, ref, n, ret_tid); },
.ControlBr => { emit_control(self, f, ref, n, ret_tid); },
.ControlJmp => { emit_control(self, f, ref, n, ret_tid); },
.ControlCall => { emit_control(self, f, ref, n, ret_tid); },
.ControlSelect => {
match n.body {
.Select(s) => {
if is_aggregate(self, s.tid) {
put(self, "v");
put_u(self, ref);
put(self, " = v");
put_u(self, s.cond);
put(self, " ? v");
put_u(self, s.true_val);
put(self, " : v");
put_u(self, s.false_val);
put(self, ";\n");
} else {
put(self, "v");
put_u(self, ref);
put(self, " = v");
put_u(self, s.cond);
put(self, " ? v");
put_u(self, s.true_val);
put(self, " : v");
put_u(self, s.false_val);
put(self, ";\n");
}
},
_ => {},
}
},
.ControlTrap => { put(self, "spl_trap();\n"); },
.ControlUnreachable => { put(self, "spl_trap();\n"); },
.DbgBreakpoint => {},
.DbgPanic => {
match n.body {
.Panic(p) => {
put(self, "spl_panic(");
emit_c_string(self, p.msg);
put(self, ");\n");
},
_ => {},
}
},
_ => { put_unsupported(self, "node kind"); },
}
}
fn emit_fn_sig(self: *Ir2C, fid: usize, with_names: bool) {
var f = IrM.func(self.ir, fid);
var ret_tid = fn_ret_type(self, f.fn_tid);
var ret_agg = is_aggregate(self, ret_tid);
if ret_agg {
put(self, "void");
} else if !put_type(self, ret_tid) {
put(self, "void");
}
put(self, " ");
c_fn_name(self, fid);
put(self, "(");
var need = false;
if ret_agg {
put(self, "void* ret__");
need = true;
}
var nargs: usize = 0;
var te = TypeTable.node(self.ty, f.fn_tid);
if te != null {
match te.node {
.Fn(fd) => {
nargs = fd.params.len;
var k: usize = 0;
while k < nargs {
if need { put(self, ", "); }
var pt = usize_at(&fd.params, k);
if is_aggregate(self, pt) {
put(self, "void*");
} else {
if !put_type(self, pt) { put(self, "void*"); }
}
if with_names {
put(self, " a");
put_u(self, k);
}
need = true;
k = k + 1;
}
},
_ => {},
}
}
if !need { put(self, "void"); }
put(self, ")");
}
fn emit_proto(self: *Ir2C, fid: usize) {
emit_fn_sig(self, fid, false);
put(self, ";\n");
}
// 预声明一个 ref 的存储与值变量。
fn declare_ref(self: *Ir2C, ref: usize, tid: usize) {
if is_aggregate(self, tid) {
var size = type_size(self, tid);
var words = (size + 7) / 8;
if words == 0 { words = 1; }
put(self, "u64 s");
put_u(self, ref);
put(self, "[");
put_u(self, words);
put(self, "]; void* v");
put_u(self, ref);
put(self, ";\n");
ret;
}
if !put_type(self, tid) { put(self, "void*"); }
put(self, " v");
put_u(self, ref);
put(self, ";\n");
}
fn declare_all(self: *Ir2C, f: *IrFunc) {
var i: usize = 1;
while i < f.nodes.len {
var n = (f.nodes.data as *IrEntry) + i;
var is_alloc = false;
var count: usize = 1;
var pointee: usize = 0;
match n.body {
.MemAlloc(m) => {
is_alloc = true;
count = const_node(self, f, m.count);
if count == 0 { count = 1; }
pointee = m.tid;
},
_ => {},
}
if is_alloc {
if is_aggregate(self, pointee) {
var esz = type_size(self, pointee);
var words = (esz * count + 7) / 8;
if words == 0 { words = 1; }
put(self, "u64 s");
put_u(self, i);
put(self, "[");
put_u(self, words);
put(self, "]; void* v");
put_u(self, i);
put(self, ";\n");
} else {
if !put_type(self, pointee) { put(self, "void*"); }
put(self, " s");
put_u(self, i);
put(self, "[");
put_u(self, count);
put(self, "]; void* v");
put_u(self, i);
put(self, ";\n");
}
} else {
var tid = result_tid(self, n);
if tid != 0 && !tid_is_unit(self, tid) {
declare_ref(self, i, tid);
}
}
i = i + 1;
}
}
fn bind_agg(self: *Ir2C, ref: usize) {
put(self, "v");
put_u(self, ref);
put(self, " = (void*)s");
put_u(self, ref);
put(self, ";\n");
}
fn emit_def(self: *Ir2C, fid: usize) {
var f = IrM.func(self.ir, fid);
var ret_tid = fn_ret_type(self, f.fn_tid);
emit_fn_sig(self, fid, true);
put(self, " {\n");
declare_all(self, f);
var i: usize = 1;
while i < f.nodes.len {
put(self, "L");
put_u(self, i);
put(self, ": ;\n");
var n = (f.nodes.data as *IrEntry) + i;
emit_node(self, fid, f, i, n, ret_tid);
i = i + 1;
}
put(self, "spl_trap();\n}\n");
}
// ---- driver ----
fn find_main(self: *Ir2C): usize {
var i: usize = 1;
while i < self.ir.funcs.len {
var f = IrM.func(self.ir, i);
if f != null && !fn_is_extern(self, f) {
if ByteView.eq(f.name, ByteView.from_slice("main")) { ret i; }
}
i = i + 1;
}
ret 0;
}
fn run(self: *Ir2C) {
put(self, "#include \"spl_rt.h\"\n");
// 模块级全局变量(Zero → BSS:无初始化式)。
var gi: usize = 0;
while gi < self.ir.globals.len {
var g = (self.ir.globals.data as *Global) + gi;
var sz = type_size(self, g.tid);
if sz == 0 { sz = 1; }
put(self, "static unsigned char spl_g_");
put_u(self, gi);
put(self, "[");
put_u(self, sz);
put(self, "]");
if !g.zero {
put(self, " = {");
var k: usize = 0;
while k < g.data.len {
if k > 0 { put(self, ", "); }
put_u(self, ((g.data.data as *u8) + k).* as usize);
k = k + 1;
}
if g.data.len == 0 { put(self, "0"); }
put(self, "}");
}
put(self, ";\n");
gi = gi + 1;
}
var i: usize = 1;
while i < self.ir.funcs.len {
var f = IrM.func(self.ir, i);
if f != null && !fn_is_extern(self, f) {
emit_proto(self, i);
}
i = i + 1;
}
i = 1;
while i < self.ir.funcs.len {
var f = IrM.func(self.ir, i);
if f != null && !fn_is_extern(self, f) {
emit_def(self, i);
}
i = i + 1;
}
var m = find_main(self);
if m != 0 {
var f = IrM.func(self.ir, m);
var nargs: usize = 0;
var te = TypeTable.node(self.ty, f.fn_tid);
if te != null {
match te.node { .Fn(fd) => { nargs = fd.params.len; }, _ => {} }
}
if nargs == 0 {
put(self, "int main(void) { f");
put_u(self, m);
put(self, "(); return 0; }\n");
} else {
put(self, "int main(int argc, char** argv) { ");
if !tid_is_unit(self, fn_ret_type(self, f.fn_tid)) {
put(self, "return (int)f");
put_u(self, m);
put(self, "(argc, (void*)argv); }\n");
} else {
put(self, "f");
put_u(self, m);
put(self, "(argc, (void*)argv); return 0; }\n");
}
}
}
}
fn new(ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr, out: *ByteBuffer): Ir2C {
ret Ir2C {
.ir = ir,
.ty = ty,
.layout = layout,
.attr = attr,
.out = out,
.gdata_n = 1,
.failed = false,
};
}
fn compile(ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr, out: *ByteBuffer): bool {
var c = new(ir, ty, layout, attr, out);
run(&c);
ret !c.failed;
}
fn main() {}