fix(selfhost): green fixed-point buxc2→buxc3→buxc4

Raise scope/HIR buffer caps, parenthesize unary operands, fix fat-func
typedef and param access so selfhost-built compilers recompile src
identically (path-normalized C + stripped ELF).
This commit is contained in:
2026-07-20 00:21:39 +03:00
parent cb6df5443a
commit e63dacfdbf
8 changed files with 122 additions and 75 deletions
+49 -36
View File
@@ -385,10 +385,12 @@ module CBackend {
return;
}
// Unary
// Unary — always parenthesize operand so `!(a && b)` is not `!a && b`
if kind == hUnary {
StringBuilder_Append(&cbe.sb, CBackend_OpToC(node.intValue));
StringBuilder_Append(&cbe.sb, "(");
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ")");
return;
}
@@ -938,15 +940,8 @@ module CBackend {
var p: int = 0;
while p < mod.funcs[i].paramCount {
var ptype: String = "";
if p == 0 { ptype = mod.funcs[i].param0.typeName; }
else if p == 1 { ptype = mod.funcs[i].param1.typeName; }
else if p == 2 { ptype = mod.funcs[i].param2.typeName; }
else if p == 3 { ptype = mod.funcs[i].param3.typeName; }
else if p == 4 { ptype = mod.funcs[i].param4.typeName; }
else if p == 5 { ptype = mod.funcs[i].param5.typeName; }
else if p == 6 { ptype = mod.funcs[i].param6.typeName; }
else if p == 7 { ptype = mod.funcs[i].param7.typeName; }
else if p == 8 { ptype = mod.funcs[i].param8.typeName; }
let hp: *HirParam = CBE_FuncParam(&mod.funcs[i], p);
if hp != null as *HirParam { ptype = hp.typeName; }
CBE_MaybeEmitExtraFat(cbe, ptype);
p = p + 1;
}
@@ -961,6 +956,22 @@ module CBackend {
CBE_EmitOneFatTypedef(cbe, name);
}
/// Load param pointer by index (avoids nested `.paramN.name` on *HirParam —
/// selfhost CBE may emit `.` instead of `->` for chained pointer fields).
func CBE_FuncParam(f: *HirFunc, idx: int) -> *HirParam {
if f == null as *HirFunc { return null as *HirParam; }
if idx == 0 { return f.param0; }
if idx == 1 { return f.param1; }
if idx == 2 { return f.param2; }
if idx == 3 { return f.param3; }
if idx == 4 { return f.param4; }
if idx == 5 { return f.param5; }
if idx == 6 { return f.param6; }
if idx == 7 { return f.param7; }
if idx == 8 { return f.param8; }
return null as *HirParam;
}
func CBE_CollectBuxFn(names: *String, count: *int, name: String) {
if String_Eq(name, "") { return; }
if !String_StartsWith(name, "BuxFn_") { return; }
@@ -1001,7 +1012,15 @@ module CBackend {
var pi: uint = 1;
while pi < partCount {
let pPart: String = String_SplitPart(rest, "_", pi);
if !(pi == 1 && String_Eq(pPart, "void") && partCount == 2) {
// Skip sole `void` param (BuxFn_ret_void); always emit real params.
// Written without nested `!` so older selfhost CBE stays correct.
var skipVoid: bool = false;
if pi == 1 {
if String_Eq(pPart, "void") {
if partCount == 2 { skipVoid = true; }
}
}
if !skipVoid {
StringBuilder_Append(&cbe.sb, ", ");
StringBuilder_Append(&cbe.sb, CBE_FatPartToC(pPart));
}
@@ -1148,21 +1167,17 @@ module CBackend {
StringBuilder_Append(&cbe.sb, retC);
StringBuilder_Append(&cbe.sb, " __adapt_");
StringBuilder_Append(&cbe.sb, fname);
StringBuilder_Append(&cbe.sb, "(void* env");
// Use __fat_env so we never clash with a user param named `env` (CtfeEnv, etc.)
StringBuilder_Append(&cbe.sb, "(void* __fat_env");
var p: int = 0;
while p < mod.funcs[i].paramCount {
// Skip if first param is already __env (shouldn't for named funcs)
var pname: String = "";
var ptype: String = "int";
if p == 0 { pname = mod.funcs[i].param0.name; ptype = mod.funcs[i].param0.typeName; }
else if p == 1 { pname = mod.funcs[i].param1.name; ptype = mod.funcs[i].param1.typeName; }
else if p == 2 { pname = mod.funcs[i].param2.name; ptype = mod.funcs[i].param2.typeName; }
else if p == 3 { pname = mod.funcs[i].param3.name; ptype = mod.funcs[i].param3.typeName; }
else if p == 4 { pname = mod.funcs[i].param4.name; ptype = mod.funcs[i].param4.typeName; }
else if p == 5 { pname = mod.funcs[i].param5.name; ptype = mod.funcs[i].param5.typeName; }
else if p == 6 { pname = mod.funcs[i].param6.name; ptype = mod.funcs[i].param6.typeName; }
else if p == 7 { pname = mod.funcs[i].param7.name; ptype = mod.funcs[i].param7.typeName; }
else if p == 8 { pname = mod.funcs[i].param8.name; ptype = mod.funcs[i].param8.typeName; }
let hp: *HirParam = CBE_FuncParam(&mod.funcs[i], p);
if hp != null as *HirParam {
pname = hp.name;
ptype = hp.typeName;
}
if String_Eq(ptype, "") { ptype = "int"; }
StringBuilder_Append(&cbe.sb, ", ");
StringBuilder_Append(&cbe.sb, ptype);
@@ -1170,7 +1185,7 @@ module CBackend {
StringBuilder_Append(&cbe.sb, pname);
p = p + 1;
}
StringBuilder_Append(&cbe.sb, ") {\n (void)env;\n");
StringBuilder_Append(&cbe.sb, ") {\n (void)__fat_env;\n");
if String_Eq(retC, "void") {
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, fname);
@@ -1184,15 +1199,8 @@ module CBackend {
while p < mod.funcs[i].paramCount {
if p > 0 { StringBuilder_Append(&cbe.sb, ", "); }
var pname: String = "";
if p == 0 { pname = mod.funcs[i].param0.name; }
else if p == 1 { pname = mod.funcs[i].param1.name; }
else if p == 2 { pname = mod.funcs[i].param2.name; }
else if p == 3 { pname = mod.funcs[i].param3.name; }
else if p == 4 { pname = mod.funcs[i].param4.name; }
else if p == 5 { pname = mod.funcs[i].param5.name; }
else if p == 6 { pname = mod.funcs[i].param6.name; }
else if p == 7 { pname = mod.funcs[i].param7.name; }
else if p == 8 { pname = mod.funcs[i].param8.name; }
let hp2: *HirParam = CBE_FuncParam(&mod.funcs[i], p);
if hp2 != null as *HirParam { pname = hp2.name; }
StringBuilder_Append(&cbe.sb, pname);
p = p + 1;
}
@@ -1386,7 +1394,8 @@ module CBackend {
if node.typeName != null as String { return node.typeName; }
return "";
}
if node.kind == hFieldPtr {
// Field access (both hFieldPtr and hFieldAccess): resolve field type from structs
if node.kind == hFieldPtr || node.kind == hFieldAccess {
let baseType: String = CBE_GetExprTypeName(mod, node.child1);
var structName: String = baseType;
if String_EndsWith(baseType, "*") {
@@ -1396,7 +1405,10 @@ module CBackend {
}
}
if !String_Eq(structName, "") {
return CBE_LookupFieldType(mod, structName, node.strValue);
let ft: String = CBE_LookupFieldType(mod, structName, node.strValue);
if ft != null as String && !String_Eq(ft, "") {
return ft;
}
}
}
if node.typeName != null as String { return node.typeName; }
@@ -1409,7 +1421,7 @@ module CBackend {
func CBackend_Generate(mod: *HirModule) -> String {
let cbe: *CEmitter = bux_alloc(sizeof(CEmitter)) as *CEmitter;
cbe.sb = StringBuilder_NewCap(8192);
cbe.sb = StringBuilder_NewCap(262144);
cbe.indent = 0;
cbe.mod = mod;
cbe.deferCount = 0;
@@ -1737,7 +1749,8 @@ module CBackend {
StringBuilder_Append(&cbe.sb, "*)__env);\n");
} else if mod.funcs[i].paramCount > 0 {
// Capture-less closure still has __env
if String_Eq(mod.funcs[i].param0.name, "__env") {
let envP: *HirParam = CBE_FuncParam(&mod.funcs[i], 0);
if envP != null as *HirParam && String_Eq(envP.name, "__env") {
StringBuilder_Append(&cbe.sb, " (void)__env;\n");
}
}
+10 -8
View File
@@ -4038,15 +4038,17 @@ module HirLower {
let ctx: *LowerCtx = bux_alloc(sizeof(LowerCtx)) as *LowerCtx;
ctx.module = mod;
ctx.scope = sema.scope;
ctx.funcs = bux_alloc(512 as uint * sizeof(HirFunc)) as *HirFunc;
// Capacities sized for full selfhost compile (~600 funcs, ~120 structs).
// Undersized buffers overflowed heap → corrupt main.c / buxc2→buxc3 fail.
ctx.funcs = bux_alloc(4096 as uint * sizeof(HirFunc)) as *HirFunc;
ctx.funcCount = 0;
ctx.externFuncs = bux_alloc(512 as uint * sizeof(HirFunc)) as *HirFunc;
ctx.externFuncs = bux_alloc(1024 as uint * sizeof(HirFunc)) as *HirFunc;
ctx.externCount = 0;
ctx.varCounter = 0;
ctx.genFuncCount = 0;
ctx.genFuncs = bux_alloc(256 as uint * sizeof(Decl)) as *Decl;
ctx.genFuncs = bux_alloc(1024 as uint * sizeof(Decl)) as *Decl;
ctx.genStructCount = 0;
ctx.genStructs = bux_alloc(256 as uint * sizeof(Decl)) as *Decl;
ctx.genStructs = bux_alloc(1024 as uint * sizeof(Decl)) as *Decl;
ctx.substParam0 = "";
ctx.substArg0 = "";
ctx.substParam1 = "";
@@ -4056,11 +4058,11 @@ module HirLower {
hm.funcCount = 0;
hm.funcs = ctx.funcs;
hm.structCount = 0;
hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct;
hm.structs = bux_alloc(512 as uint * sizeof(HirStruct)) as *HirStruct;
hm.enumCount = 0;
hm.enums = bux_alloc(64 as uint * sizeof(HirEnum)) as *HirEnum;
hm.enums = bux_alloc(256 as uint * sizeof(HirEnum)) as *HirEnum;
hm.constCount = 0;
hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst;
hm.consts = bux_alloc(2048 as uint * sizeof(HirConst)) as *HirConst;
ctx.hm = hm;
// First pass: count structs (to allocate field arrays later)
@@ -4175,7 +4177,7 @@ module HirLower {
ctx.externCount = ctx.externCount + 1;
}
// Pass 1: collect const names (expressions evaluated in Pass 2)
if decl.kind == dkConst && hm.constCount < 512 {
if decl.kind == dkConst && hm.constCount < 2048 {
let ci: int = hm.constCount;
hm.consts[ci].name = decl.strValue;
hm.consts[ci].value = 0;
+13 -9
View File
@@ -1721,7 +1721,7 @@ module Sema {
// Interface
if dk == dkInterface {
if sema.interfaceCount < 64 {
if sema.interfaceCount < 256 {
sema.interfaceTable[sema.interfaceCount].name = decl.strValue;
sema.interfaceTable[sema.interfaceCount].decl = decl;
sema.interfaceCount = sema.interfaceCount + 1;
@@ -1734,7 +1734,7 @@ module Sema {
var m: *Decl = decl.childDecl1;
while m != null as *Decl {
if m.kind == dkFunc {
if sema.methodCount < 256 {
if sema.methodCount < 2048 {
sema.methodEntries[sema.methodCount].typeName = implTypeName;
sema.methodEntries[sema.methodCount].methodName = m.strValue;
sema.methodEntries[sema.methodCount].decl = m;
@@ -1865,7 +1865,7 @@ module Sema {
}
if !String_Eq(typeName, "") {
let methodName: String = bux_str_slice(decl.strValue, (i + 1) as uint, bux_strlen(decl.strValue) - (i + 1) as uint);
if sema.methodCount < 256 {
if sema.methodCount < 2048 {
sema.methodEntries[sema.methodCount].typeName = typeName;
sema.methodEntries[sema.methodCount].methodName = methodName;
sema.methodEntries[sema.methodCount].decl = decl;
@@ -2225,19 +2225,23 @@ module Sema {
func Sema_Analyze(mod: *Module) -> *Sema {
let s: *Sema = bux_alloc(sizeof(Sema)) as *Sema;
s.module = mod;
// Global scope must use Scope_New capacity (maxSymbols=8192).
// Allocating only 1024 slots while Scope_Define allows 8192 overflowed
// the heap when analyzing full compiler sources (~1200 decls) — buxc2 crash.
s.scope = bux_alloc(sizeof(Scope)) as *Scope;
s.scope.symbols = bux_alloc(1024 as uint * sizeof(Symbol)) as *Symbol;
s.scope.count = 0;
s.scope.parent = null as *Scope;
let globalScope: Scope = Scope_New();
s.scope.symbols = globalScope.symbols;
s.scope.count = globalScope.count;
s.scope.parent = globalScope.parent;
s.hasError = false;
s.diagCount = 0;
s.diags = bux_alloc(256 as uint * sizeof(SemaDiag)) as *SemaDiag;
s.diags = bux_alloc(1024 as uint * sizeof(SemaDiag)) as *SemaDiag;
s.typeTable = null as *void;
s.methodTable = null as *void;
s.currentRetType = tyVoid;
s.interfaceTable = bux_alloc(64 as uint * sizeof(InterfaceEntry)) as *InterfaceEntry;
s.interfaceTable = bux_alloc(256 as uint * sizeof(InterfaceEntry)) as *InterfaceEntry;
s.interfaceCount = 0;
s.methodEntries = bux_alloc(256 as uint * sizeof(MethodEntry)) as *MethodEntry;
s.methodEntries = bux_alloc(2048 as uint * sizeof(MethodEntry)) as *MethodEntry;
s.methodCount = 0;
// First pass: collect globals