fix(qbe): use extraData instead of child3 for hIf else-branch

Block lowering overwrites child3 for statement chaining.
Else block is stored in extraData by hir_lower (matches c_backend).
Fixes QBE parse error 'last block misses jump' in if-without-else chains.
This commit is contained in:
2026-06-02 18:07:35 +03:00
parent eb8179b6d0
commit ec4e748584
10 changed files with 756 additions and 72 deletions
+254 -14
View File
@@ -192,6 +192,14 @@ func Cli_Check(srcPath: String) -> int {
// Phase 4: HIR lowering // Phase 4: HIR lowering
let hirMod: *HirModule = HirLower_LowerModule(mod, sema); let hirMod: *HirModule = HirLower_LowerModule(mod, sema);
var dc: *Decl = mod.firstItem;
while dc != null as *Decl {
if dc.kind == dkFunc {
Print("check func ");
PrintLine(dc.strValue);
}
dc = dc.childDecl2;
}
PrintLine("Check passed"); PrintLine("Check passed");
return 0; return 0;
} }
@@ -232,13 +240,144 @@ func Cli_CompileSource(source: String, sourceName: String) -> *HirModule {
return hirMod; return hirMod;
} }
// ---------------------------------------------------------------------------
// Stdlib discovery and merging
// ---------------------------------------------------------------------------
func Cli_FindStdlibDir(projectDir: String) -> String {
var path: String = bux_path_join(projectDir, "stdlib");
if DirExists(path) { return path; }
path = bux_path_join(projectDir, "../stdlib");
if DirExists(path) { return path; }
path = "/home/ziko/z-git/bux/bux/stdlib";
if DirExists(path) { return path; }
return "";
}
func Cli_DeclName(decl: *Decl) -> String {
if decl == null as *Decl { return ""; }
return decl.strValue;
}
func Cli_NameInList(name: String, names: *String, count: int) -> bool {
if String_Eq(name, "") { return false; }
var i: int = 0;
while i < count {
if String_Eq(names[i], name) {
return true;
}
i = i + 1;
}
return false;
}
func Cli_CollectNames(mod: *Module, names: *String, maxCount: int) -> int {
if mod == null as *Module { return 0; }
var count: int = 0;
var decl: *Decl = mod.firstItem;
while decl != null as *Decl && count < maxCount {
let next: *Decl = decl.childDecl2;
if decl.kind == dkModule {
var inner: *Decl = decl.childDecl1;
while inner != null as *Decl && count < maxCount {
let iname: String = Cli_DeclName(inner);
if !String_Eq(iname, "") {
names[count] = iname;
count = count + 1;
}
inner = inner.childDecl2;
}
} else {
let dname: String = Cli_DeclName(decl);
if !String_Eq(dname, "") {
names[count] = dname;
count = count + 1;
}
}
decl = next;
}
return count;
}
func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int {
Print("DEBUG: MergeFileInto ");
PrintLine(path);
let source: String = ReadFile(path);
if String_Eq(source, "") { return 0; }
let lex: *Lexer = Lexer_Tokenize(source);
if Lexer_DiagCount(lex) > 0 { return 0; }
let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount);
if mod == null as *Module { return 0; }
var added: int = 0;
var decl: *Decl = mod.firstItem;
while decl != null as *Decl {
let next: *Decl = decl.childDecl2;
decl.childDecl2 = null as *Decl;
if decl.kind == dkModule {
var inner: *Decl = decl.childDecl1;
while inner != null as *Decl {
let innerNext: *Decl = inner.childDecl2;
let iname: String = Cli_DeclName(inner);
if !String_Eq(iname, "") && Cli_NameInList(iname, skipNames, skipCount) {
// Skip shadowed stdlib decl
} else {
inner.childDecl2 = target.firstItem;
target.firstItem = inner;
target.itemCount = target.itemCount + 1;
added = added + 1;
}
inner = innerNext;
}
} else {
let dname: String = Cli_DeclName(decl);
if !String_Eq(dname, "") && Cli_NameInList(dname, skipNames, skipCount) {
// Skip shadowed stdlib decl
} else {
decl.childDecl2 = target.firstItem;
target.firstItem = decl;
target.itemCount = target.itemCount + 1;
added = added + 1;
}
}
decl = next;
}
Print("DEBUG: MergeFileInto done, added=");
PrintInt(added);
PrintLine("");
return added;
}
func Cli_CopyModuleDecls(target: *Module, source: *Module) {
if source == null as *Module || source.firstItem == null as *Decl { return; }
Print("DEBUG: copy start, source.itemCount=");
PrintInt(source.itemCount);
PrintLine("");
var decl: *Decl = source.firstItem;
var limit: int = 0;
while decl != null as *Decl && limit < 10000 {
let next: *Decl = decl.childDecl2;
decl.childDecl2 = target.firstItem;
target.firstItem = decl;
target.itemCount = target.itemCount + 1;
decl = next;
limit = limit + 1;
}
source.firstItem = null as *Decl;
source.itemCount = 0;
PrintLine("DEBUG: copy end");
}
func Cli_BuildProject(projectDir: String) -> int { func Cli_BuildProject(projectDir: String) -> int {
PrintLine("DEBUG: Cli_BuildProject start");
let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml")); let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml"));
PrintLine("DEBUG: manifest loaded");
var outName: String = man.name; var outName: String = man.name;
if String_Eq(outName, "") { if String_Eq(outName, "") {
outName = "bux_out"; outName = "bux_out";
} }
let srcDir: String = bux_path_join(projectDir, "src"); let srcDir: String = bux_path_join(projectDir, "src");
PrintLine("DEBUG: srcDir built");
if !DirExists(srcDir) { if !DirExists(srcDir) {
Print("Error: no src/ directory in "); Print("Error: no src/ directory in ");
PrintLine(projectDir); PrintLine(projectDir);
@@ -260,14 +399,14 @@ func Cli_BuildProject(projectDir: String) -> int {
PrintInt(fileCount); PrintInt(fileCount);
PrintLine(" source files"); PrintLine(" source files");
// Create merged module // Create user merged module (collect user decls first)
let merged: *Module = bux_alloc(sizeof(Module)) as *Module; let userMerged: *Module = bux_alloc(sizeof(Module)) as *Module;
merged.name = "main"; userMerged.name = "main";
merged.path = ""; userMerged.path = "";
merged.itemCount = 0; userMerged.itemCount = 0;
merged.firstItem = null as *Decl; userMerged.firstItem = null as *Decl;
// Parse each file and merge declarations into merged module // Parse each file and merge declarations into userMerged module
var i: int = 0; var i: int = 0;
while i < fileCount { while i < fileCount {
let path: String = files[i]; let path: String = files[i];
@@ -291,7 +430,7 @@ func Cli_BuildProject(projectDir: String) -> int {
PrintLine(path); PrintLine(path);
return 1; return 1;
} }
// Merge declarations from this module into merged // Merge declarations from this module into userMerged
var decl: *Decl = mod.firstItem; var decl: *Decl = mod.firstItem;
while decl != null as *Decl { while decl != null as *Decl {
let next: *Decl = decl.childDecl2; let next: *Decl = decl.childDecl2;
@@ -301,26 +440,111 @@ func Cli_BuildProject(projectDir: String) -> int {
var inner: *Decl = decl.childDecl1; var inner: *Decl = decl.childDecl1;
while inner != null as *Decl { while inner != null as *Decl {
let innerNext: *Decl = inner.childDecl2; let innerNext: *Decl = inner.childDecl2;
inner.childDecl2 = merged.firstItem; inner.childDecl2 = userMerged.firstItem;
merged.firstItem = inner; userMerged.firstItem = inner;
merged.itemCount = merged.itemCount + 1; userMerged.itemCount = userMerged.itemCount + 1;
inner = innerNext; inner = innerNext;
} }
} else { } else {
decl.childDecl2 = merged.firstItem; decl.childDecl2 = userMerged.firstItem;
merged.firstItem = decl; userMerged.firstItem = decl;
merged.itemCount = merged.itemCount + 1; userMerged.itemCount = userMerged.itemCount + 1;
} }
decl = next; decl = next;
} }
i = i + 1; i = i + 1;
} }
Print("DEBUG: userMerged.itemCount=");
PrintInt(userMerged.itemCount);
PrintLine("");
// Collect user declaration names for shadow detection
let maxNames: int = 2048;
let userNames: *String = bux_alloc(maxNames * 8) as *String;
let userNameCount: int = Cli_CollectNames(userMerged, userNames, maxNames);
// Create final merged module
let merged: *Module = bux_alloc(sizeof(Module)) as *Module;
merged.name = "main";
merged.path = "";
merged.itemCount = 0;
merged.firstItem = null as *Decl;
// Find and merge stdlib declarations
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
if !String_Eq(stdlibDir, "") {
Print("Stdlib found: ");
PrintLine(stdlibDir);
let stdSubdir: String = bux_path_join(stdlibDir, "Std");
if DirExists(stdSubdir) {
var stdFileCount: int = 0;
let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount);
Print("DEBUG: stdFileCount=");
PrintInt(stdFileCount);
PrintLine("");
if stdFileCount > 0 {
Print("Merging ");
PrintInt(stdFileCount);
PrintLine(" stdlib files");
var si: int = 0;
var stdAdded: int = 0;
while si < stdFileCount {
Print("DEBUG: merging stdlib file ");
PrintInt(si);
PrintLine("");
// Skip Channel.bux for debugging
if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") {
PrintLine("DEBUG: skipping Channel.bux");
si = si + 1;
continue;
}
let added: int = Cli_MergeFileInto(merged, stdFiles[si], userNames, userNameCount);
stdAdded = stdAdded + added;
si = si + 1;
}
Print("Stdlib declarations added: ");
PrintInt(stdAdded);
PrintLine("");
}
}
}
// Copy user declarations into merged (user shadows stdlib)
PrintLine("DEBUG: before copy");
var countTest: int = 0;
var dtest: *Decl = userMerged.firstItem;
while dtest != null as *Decl && countTest < 10000 {
dtest = dtest.childDecl2;
countTest = countTest + 1;
}
Print("userMerged actual count=");
PrintInt(countTest);
PrintLine("");
Print("userMerged.itemCount=");
PrintInt(userMerged.itemCount);
PrintLine("");
var du: *Decl = userMerged.firstItem;
while du != null as *Decl {
if du.kind == dkFunc {
Print("user func ");
PrintLine(du.strValue);
}
du = du.childDecl2;
}
Cli_CopyModuleDecls(merged, userMerged);
PrintLine("DEBUG: copy done");
Print("merged.itemCount=");
PrintInt(merged.itemCount);
PrintLine("");
PrintLine("DEBUG: copy done");
Print("Merged "); Print("Merged ");
PrintInt(merged.itemCount); PrintInt(merged.itemCount);
PrintLine(" declarations"); PrintLine(" declarations");
// Semantic analysis // Semantic analysis
PrintLine("DEBUG: about to run sema");
PrintLine("Running sema..."); PrintLine("Running sema...");
let sema: *Sema = Sema_Analyze(merged); let sema: *Sema = Sema_Analyze(merged);
if Sema_HasError(sema) { if Sema_HasError(sema) {
@@ -330,10 +554,24 @@ func Cli_BuildProject(projectDir: String) -> int {
// HIR lowering // HIR lowering
PrintLine("Lowering to HIR..."); PrintLine("Lowering to HIR...");
var di: *Decl = merged.firstItem;
while di != null as *Decl {
if di.kind == dkFunc {
Print("decl func ");
PrintLine(di.strValue);
}
di = di.childDecl2;
}
let hirMod: *HirModule = HirLower_LowerModule(merged, sema); let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
// QBE SSA code generation // QBE SSA code generation
PrintLine("Generating QBE SSA..."); PrintLine("Generating QBE SSA...");
var fi: int = 0;
while fi < hirMod.funcCount {
Print("func ");
PrintLine(hirMod.funcs[fi].name);
fi = fi + 1;
}
let qbeCode: String = QbeBackend_Generate(hirMod); let qbeCode: String = QbeBackend_Generate(hirMod);
// Create build directory // Create build directory
@@ -418,6 +656,7 @@ func Cli_BuildProject(projectDir: String) -> int {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func Cli_RunProject(projectDir: String) -> int { func Cli_RunProject(projectDir: String) -> int {
PrintLine("DEBUG: Cli_RunProject start");
let rc: int = Cli_BuildProject(projectDir); let rc: int = Cli_BuildProject(projectDir);
if rc != 0 { if rc != 0 {
return rc; return rc;
@@ -487,6 +726,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
} }
if String_Eq(cmd, "run") { if String_Eq(cmd, "run") {
PrintLine("DEBUG: run command");
let dir: String = "."; let dir: String = ".";
if argCount >= 3 { dir = args[2]; } if argCount >= 3 { dir = args[2]; }
return Cli_RunProject(dir); return Cli_RunProject(dir);
+2
View File
@@ -103,6 +103,8 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
if kind == ekIdent { if kind == ekIdent {
n.kind = hVar; n.kind = hVar;
n.strValue = expr.strValue; n.strValue = expr.strValue;
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
n.typeKind = sym.typeKind;
return n; return n;
} }
+4
View File
@@ -298,6 +298,10 @@ func parserParsePrimary(p: *Parser) -> *Expr {
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func dummyFunc() -> int {
return 0;
}
// Postfix: call, index, field access, as, is, ? // Postfix: call, index, field access, as, is, ?
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+117 -21
View File
@@ -66,12 +66,16 @@ struct QbeEmitter {
// Const table for inline substitution // Const table for inline substitution
constCount: int; constCount: int;
consts: *HirConst; consts: *HirConst;
// Current function for param type lookup
currentFunc: *HirFunc;
// Module reference for function return type lookup
mod: *HirModule;
} }
func QBE_Init() -> QbeEmitter { func QBE_Init() -> QbeEmitter {
var dsb: StringBuilder = StringBuilder_NewCap(4096); var dsb: StringBuilder = StringBuilder_NewCap(4096);
var csb: StringBuilder = StringBuilder_NewCap(16384); var csb: StringBuilder = StringBuilder_NewCap(16384);
return QbeEmitter { dataSb: dsb, codeSb: csb, tempId: 0, labelId: 0, strId: 0, indent: 0, inBlock: false, constCount: 0, consts: null as *HirConst }; return QbeEmitter { dataSb: dsb, codeSb: csb, tempId: 0, labelId: 0, strId: 0, indent: 0, inBlock: false, constCount: 0, consts: null as *HirConst, currentFunc: null as *HirFunc, mod: null as *HirModule };
} }
func QBE_FreshTemp(qbe: *QbeEmitter) -> String { func QBE_FreshTemp(qbe: *QbeEmitter) -> String {
@@ -262,8 +266,8 @@ func QBE_IsTerminator(node: *HirNode) -> bool {
if node.kind == hBreak { return true; } if node.kind == hBreak { return true; }
if node.kind == hContinue { return true; } if node.kind == hContinue { return true; }
if node.kind == hIf { if node.kind == hIf {
if node.child3 != null as *HirNode { if node.extraData != null as *HirNode {
return QBE_IsTerminator(node.child2) && QBE_IsTerminator(node.child3); return QBE_IsTerminator(node.child2) && QBE_IsTerminator(node.extraData as *HirNode);
} }
return false; return false;
} }
@@ -280,6 +284,56 @@ func QBE_IsTerminator(node: *HirNode) -> bool {
return false; return false;
} }
// ---------------------------------------------------------------------------
// Helper: find function by name in module
// ---------------------------------------------------------------------------
func QBE_FindFunc(qbe: *QbeEmitter, name: String) -> *HirFunc {
if qbe.mod == null as *HirModule { return null as *HirFunc; }
var i: int = 0;
while i < qbe.mod.funcCount {
if String_Eq(qbe.mod.funcs[i].name, name) {
return &qbe.mod.funcs[i];
}
i = i + 1;
}
i = 0;
while i < qbe.mod.externCount {
if String_Eq(qbe.mod.externFuncs[i].name, name) {
return &qbe.mod.externFuncs[i];
}
i = i + 1;
}
return null as *HirFunc;
}
// ---------------------------------------------------------------------------
// Helper: determine QBE type for any HIR node
// ---------------------------------------------------------------------------
func QBE_NodeType(qbe: *QbeEmitter, node: *HirNode) -> String {
if node == null as *HirNode { return "l"; }
// For variables, check param type first
if node.kind == hVar && qbe.currentFunc != null as *HirFunc {
var pi2: int = 0;
var pnames2: *HirParam = &qbe.currentFunc.param0;
while pi2 < qbe.currentFunc.paramCount {
if String_Eq(pnames2[pi2].name, node.strValue) {
return QbeBackend_TypeWord(pnames2[pi2].typeKind);
}
pi2 = pi2 + 1;
}
}
// For calls, look up callee return type
if node.kind == hCall && qbe.mod != null as *HirModule {
let callee: *HirFunc = QBE_FindFunc(qbe, node.strValue);
if callee != null as *HirFunc {
return QbeBackend_TypeWord(callee.retTypeKind);
}
}
return QbeBackend_TypeWord(node.typeKind);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Expression emission — returns the QBE value string (e.g. "%t3", "42", "$foo") // Expression emission — returns the QBE value string (e.g. "%t3", "42", "$foo")
// For expressions that need multiple instructions, emits them inline. // For expressions that need multiple instructions, emits them inline.
@@ -325,8 +379,21 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
return "0"; return "0";
} }
} }
// Determine type: use param type if available, else node.typeKind
var varTy: int = node.typeKind;
if qbe.currentFunc != null as *HirFunc {
var pi2: int = 0;
var pnames2: *HirParam = &qbe.currentFunc.param0;
while pi2 < qbe.currentFunc.paramCount {
if String_Eq(pnames2[pi2].name, node.strValue) {
varTy = pnames2[pi2].typeKind;
break;
}
pi2 = pi2 + 1;
}
}
let tmp: String = QBE_FreshTemp(qbe); let tmp: String = QBE_FreshTemp(qbe);
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QbeBackend_TypeWord(varTy);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_Emit(qbe, " ="); QBE_Emit(qbe, " =");
@@ -350,7 +417,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
} }
let val: String = QBE_EmitExpr(qbe, node.child2); let val: String = QBE_EmitExpr(qbe, node.child2);
if String_Eq(val, "") { val = "0"; } if String_Eq(val, "") { val = "0"; }
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QBE_NodeType(qbe, node.child2);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, "store"); QBE_Emit(qbe, "store");
QBE_Emit(qbe, ty); QBE_Emit(qbe, ty);
@@ -366,12 +433,12 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
let op: int = node.intValue; let op: int = node.intValue;
let left: String = QBE_EmitExpr(qbe, node.child1); let left: String = QBE_EmitExpr(qbe, node.child1);
let right: String = QBE_EmitExpr(qbe, node.child2); let right: String = QBE_EmitExpr(qbe, node.child2);
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QBE_NodeType(qbe, node.child1);
// Comparison operators // Comparison operators
// QBE comparison ops: ceqw/ceql, cnew/cnel, csltw/csltl, cslew/cslel, csgtw/csgtl, csgew/csgel // QBE comparison ops: ceqw/ceql, cnew/cnel, csltw/csltl, cslew/cslel, csgtw/csgtl, csgew/csgel
var cmpOp: String = ""; var cmpOp: String = "";
var cmpTy: String = ty; let cmpTy: String = QBE_NodeType(qbe, node.child1);
if op == tkEq { cmpOp = "ceq"; } if op == tkEq { cmpOp = "ceq"; }
else if op == tkNe { cmpOp = "cne"; } else if op == tkNe { cmpOp = "cne"; }
else if op == tkLt { cmpOp = "cslt"; } else if op == tkLt { cmpOp = "cslt"; }
@@ -385,7 +452,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
QBE_Emit(qbe, cmpTmp); QBE_Emit(qbe, cmpTmp);
QBE_Emit(qbe, " =w "); QBE_Emit(qbe, " =w ");
QBE_Emit(qbe, cmpOp); QBE_Emit(qbe, cmpOp);
QBE_Emit(qbe, ty); QBE_Emit(qbe, cmpTy);
QBE_Emit(qbe, " "); QBE_Emit(qbe, " ");
QBE_Emit(qbe, QBE_EnsureLocal(left)); QBE_Emit(qbe, QBE_EnsureLocal(left));
QBE_Emit(qbe, ", "); QBE_Emit(qbe, ", ");
@@ -452,7 +519,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
let rVal: String = QBE_EmitExpr(qbe, node.child2); let rVal: String = QBE_EmitExpr(qbe, node.child2);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_Emit(qbe, " =l copy "); QBE_Emit(qbe, " =w copy ");
QBE_EmitLine(qbe, rVal); QBE_EmitLine(qbe, rVal);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, "jmp "); QBE_Emit(qbe, "jmp ");
@@ -460,7 +527,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
QBE_EmitLine(qbe, lFalse); QBE_EmitLine(qbe, lFalse);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_EmitLine(qbe, " =l copy 0"); QBE_EmitLine(qbe, " =w copy 0");
QBE_EmitLine(qbe, lEnd); QBE_EmitLine(qbe, lEnd);
return tmp; return tmp;
} }
@@ -479,7 +546,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
QBE_EmitLine(qbe, lTrue); QBE_EmitLine(qbe, lTrue);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_EmitLine(qbe, " =l copy 1"); QBE_EmitLine(qbe, " =w copy 1");
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, "jmp "); QBE_Emit(qbe, "jmp ");
QBE_EmitLine(qbe, lEnd); QBE_EmitLine(qbe, lEnd);
@@ -487,7 +554,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
let rVal: String = QBE_EmitExpr(qbe, node.child2); let rVal: String = QBE_EmitExpr(qbe, node.child2);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_Emit(qbe, " =l copy "); QBE_Emit(qbe, " =w copy ");
QBE_EmitLine(qbe, rVal); QBE_EmitLine(qbe, rVal);
QBE_EmitLine(qbe, lEnd); QBE_EmitLine(qbe, lEnd);
return tmp; return tmp;
@@ -500,7 +567,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
if kind == hUnary { if kind == hUnary {
let op: int = node.intValue; let op: int = node.intValue;
let operand: String = QBE_EmitExpr(qbe, node.child1); let operand: String = QBE_EmitExpr(qbe, node.child1);
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QBE_NodeType(qbe, node.child1);
if op == tkBang || op == tkMinus { if op == tkBang || op == tkMinus {
let tmp: String = QBE_FreshTemp(qbe); let tmp: String = QBE_FreshTemp(qbe);
@@ -535,7 +602,14 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
if kind == hCall { if kind == hCall {
// Pre-evaluate arguments to avoid emitting instructions inside the call // Pre-evaluate arguments to avoid emitting instructions inside the call
let tmp: String = QBE_FreshTemp(qbe); let tmp: String = QBE_FreshTemp(qbe);
let retTy: String = QbeBackend_TypeWord(node.typeKind); var retTy: String = QbeBackend_TypeWord(node.typeKind);
// Look up callee return type from module if typeKind is unknown
if String_Eq(retTy, "l") && qbe.mod != null as *HirModule {
let callee: *HirFunc = QBE_FindFunc(qbe, node.strValue);
if callee != null as *HirFunc {
retTy = QbeBackend_TypeWord(callee.retTypeKind);
}
}
var arg1: String = ""; var arg1: String = "";
var arg2: String = ""; var arg2: String = "";
if node.child1 != null as *HirNode { if node.child1 != null as *HirNode {
@@ -552,13 +626,13 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
QBE_Emit(qbe, QBE_EscapeIdent(node.strValue)); QBE_Emit(qbe, QBE_EscapeIdent(node.strValue));
QBE_Emit(qbe, "("); QBE_Emit(qbe, "(");
if node.child1 != null as *HirNode { if node.child1 != null as *HirNode {
let a1ty: String = QbeBackend_TypeWord(node.child1.typeKind); let a1ty: String = QBE_NodeType(qbe, node.child1);
QBE_Emit(qbe, a1ty); QBE_Emit(qbe, a1ty);
QBE_Emit(qbe, " "); QBE_Emit(qbe, " ");
QBE_Emit(qbe, QBE_EnsureLocal(arg1)); QBE_Emit(qbe, QBE_EnsureLocal(arg1));
} }
if node.child2 != null as *HirNode { if node.child2 != null as *HirNode {
let a2ty: String = QbeBackend_TypeWord(node.child2.typeKind); let a2ty: String = QBE_NodeType(qbe, node.child2);
QBE_Emit(qbe, ", "); QBE_Emit(qbe, ", ");
QBE_Emit(qbe, a2ty); QBE_Emit(qbe, a2ty);
QBE_Emit(qbe, " "); QBE_Emit(qbe, " ");
@@ -599,6 +673,20 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
return tmp; return tmp;
} }
// Index pointer: &arr[idx] (for pointer indexing)
if kind == hIndexPtr {
let base: String = QBE_EmitExpr(qbe, node.child1);
let idx: String = QBE_EmitExpr(qbe, node.child2);
let tmp: String = QBE_FreshTemp(qbe);
QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp);
QBE_Emit(qbe, " =l add ");
QBE_Emit(qbe, QBE_EnsureLocal(base));
QBE_Emit(qbe, ", ");
QBE_EmitLine(qbe, QBE_EnsureLocal(idx));
return tmp;
}
// Cast — QBE doesn't need same-width casts; passthrough // Cast — QBE doesn't need same-width casts; passthrough
if kind == hCast { if kind == hCast {
return QBE_EmitExpr(qbe, node.child1); return QBE_EmitExpr(qbe, node.child1);
@@ -626,6 +714,12 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
if node == null as *HirNode { return; } if node == null as *HirNode { return; }
let kind: int = node.kind; let kind: int = node.kind;
// Assign statement
if kind == hAssign {
discard QBE_EmitExpr(qbe, node);
return;
}
// Store: declaration or assignment // Store: declaration or assignment
if kind == hStore { if kind == hStore {
if node.child1 != null as *HirNode && node.child1.kind == hAlloca { if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
@@ -653,7 +747,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
// Plain assignment // Plain assignment
let target: String = QBE_EmitExpr(qbe, node.child1); let target: String = QBE_EmitExpr(qbe, node.child1);
let val: String = QBE_EmitExpr(qbe, node.child2); let val: String = QBE_EmitExpr(qbe, node.child2);
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QBE_NodeType(qbe, node.child2);
if String_Eq(val, "") { val = "0"; } if String_Eq(val, "") { val = "0"; }
if String_Eq(target, "") { return; } if String_Eq(target, "") { return; }
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
@@ -692,7 +786,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
QBE_Emit(qbe, ", "); QBE_Emit(qbe, ", ");
QBE_Emit(qbe, lThen); QBE_Emit(qbe, lThen);
QBE_Emit(qbe, ", "); QBE_Emit(qbe, ", ");
var hasElse: bool = node.child3 != null as *HirNode; var hasElse: bool = node.extraData != null as *HirNode;
if hasElse { if hasElse {
QBE_EmitLine(qbe, lElse); QBE_EmitLine(qbe, lElse);
} else { } else {
@@ -709,8 +803,8 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
if hasElse { if hasElse {
QBE_EmitLine(qbe, lElse); QBE_EmitLine(qbe, lElse);
QBE_EmitStmt(qbe, node.child3); QBE_EmitStmt(qbe, node.extraData as *HirNode);
if !QBE_IsTerminator(node.child3) { if !QBE_IsTerminator(node.extraData as *HirNode) {
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, "jmp "); QBE_Emit(qbe, "jmp ");
QBE_EmitLine(qbe, lEnd); QBE_EmitLine(qbe, lEnd);
@@ -719,7 +813,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
// Only emit @lEnd if at least one branch falls through // Only emit @lEnd if at least one branch falls through
var needEnd: bool = !QBE_IsTerminator(node.child2); var needEnd: bool = !QBE_IsTerminator(node.child2);
if hasElse && !QBE_IsTerminator(node.child3) { if hasElse && !QBE_IsTerminator(node.extraData as *HirNode) {
needEnd = true; needEnd = true;
} }
if needEnd { if needEnd {
@@ -828,6 +922,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
func QBE_EmitFunc(qbe: *QbeEmitter, f: *HirFunc) { func QBE_EmitFunc(qbe: *QbeEmitter, f: *HirFunc) {
if f.body == null as *HirNode { return; } if f.body == null as *HirNode { return; }
qbe.currentFunc = f;
QBE_Emit(qbe, "export function "); QBE_Emit(qbe, "export function ");
let retTy: String = QbeBackend_TypeWord(f.retTypeKind); let retTy: String = QbeBackend_TypeWord(f.retTypeKind);
@@ -929,6 +1024,7 @@ func QbeBackend_Generate(mod: *HirModule) -> String {
qbe.inBlock = false; qbe.inBlock = false;
qbe.constCount = mod.constCount; qbe.constCount = mod.constCount;
qbe.consts = mod.consts; qbe.consts = mod.consts;
qbe.mod = mod;
// Header // Header
QBE_EmitLine(qbe, "# Generated by Bux QBE Backend"); QBE_EmitLine(qbe, "# Generated by Bux QBE Backend");
+1 -1
View File
@@ -312,7 +312,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
let s: *Sema = bux_alloc(sizeof(Sema)) as *Sema; let s: *Sema = bux_alloc(sizeof(Sema)) as *Sema;
s.module = mod; s.module = mod;
s.scope = bux_alloc(sizeof(Scope)) as *Scope; s.scope = bux_alloc(sizeof(Scope)) as *Scope;
s.scope.symbols = bux_alloc(256 as uint * sizeof(Symbol)) as *Symbol; s.scope.symbols = bux_alloc(1024 as uint * sizeof(Symbol)) as *Symbol;
s.scope.count = 0; s.scope.count = 0;
s.scope.parent = null as *Scope; s.scope.parent = null as *Scope;
s.hasError = false; s.hasError = false;
+254 -14
View File
@@ -192,6 +192,14 @@ func Cli_Check(srcPath: String) -> int {
// Phase 4: HIR lowering // Phase 4: HIR lowering
let hirMod: *HirModule = HirLower_LowerModule(mod, sema); let hirMod: *HirModule = HirLower_LowerModule(mod, sema);
var dc: *Decl = mod.firstItem;
while dc != null as *Decl {
if dc.kind == dkFunc {
Print("check func ");
PrintLine(dc.strValue);
}
dc = dc.childDecl2;
}
PrintLine("Check passed"); PrintLine("Check passed");
return 0; return 0;
} }
@@ -232,13 +240,144 @@ func Cli_CompileSource(source: String, sourceName: String) -> *HirModule {
return hirMod; return hirMod;
} }
// ---------------------------------------------------------------------------
// Stdlib discovery and merging
// ---------------------------------------------------------------------------
func Cli_FindStdlibDir(projectDir: String) -> String {
var path: String = bux_path_join(projectDir, "stdlib");
if DirExists(path) { return path; }
path = bux_path_join(projectDir, "../stdlib");
if DirExists(path) { return path; }
path = "/home/ziko/z-git/bux/bux/stdlib";
if DirExists(path) { return path; }
return "";
}
func Cli_DeclName(decl: *Decl) -> String {
if decl == null as *Decl { return ""; }
return decl.strValue;
}
func Cli_NameInList(name: String, names: *String, count: int) -> bool {
if String_Eq(name, "") { return false; }
var i: int = 0;
while i < count {
if String_Eq(names[i], name) {
return true;
}
i = i + 1;
}
return false;
}
func Cli_CollectNames(mod: *Module, names: *String, maxCount: int) -> int {
if mod == null as *Module { return 0; }
var count: int = 0;
var decl: *Decl = mod.firstItem;
while decl != null as *Decl && count < maxCount {
let next: *Decl = decl.childDecl2;
if decl.kind == dkModule {
var inner: *Decl = decl.childDecl1;
while inner != null as *Decl && count < maxCount {
let iname: String = Cli_DeclName(inner);
if !String_Eq(iname, "") {
names[count] = iname;
count = count + 1;
}
inner = inner.childDecl2;
}
} else {
let dname: String = Cli_DeclName(decl);
if !String_Eq(dname, "") {
names[count] = dname;
count = count + 1;
}
}
decl = next;
}
return count;
}
func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int {
Print("DEBUG: MergeFileInto ");
PrintLine(path);
let source: String = ReadFile(path);
if String_Eq(source, "") { return 0; }
let lex: *Lexer = Lexer_Tokenize(source);
if Lexer_DiagCount(lex) > 0 { return 0; }
let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount);
if mod == null as *Module { return 0; }
var added: int = 0;
var decl: *Decl = mod.firstItem;
while decl != null as *Decl {
let next: *Decl = decl.childDecl2;
decl.childDecl2 = null as *Decl;
if decl.kind == dkModule {
var inner: *Decl = decl.childDecl1;
while inner != null as *Decl {
let innerNext: *Decl = inner.childDecl2;
let iname: String = Cli_DeclName(inner);
if !String_Eq(iname, "") && Cli_NameInList(iname, skipNames, skipCount) {
// Skip shadowed stdlib decl
} else {
inner.childDecl2 = target.firstItem;
target.firstItem = inner;
target.itemCount = target.itemCount + 1;
added = added + 1;
}
inner = innerNext;
}
} else {
let dname: String = Cli_DeclName(decl);
if !String_Eq(dname, "") && Cli_NameInList(dname, skipNames, skipCount) {
// Skip shadowed stdlib decl
} else {
decl.childDecl2 = target.firstItem;
target.firstItem = decl;
target.itemCount = target.itemCount + 1;
added = added + 1;
}
}
decl = next;
}
Print("DEBUG: MergeFileInto done, added=");
PrintInt(added);
PrintLine("");
return added;
}
func Cli_CopyModuleDecls(target: *Module, source: *Module) {
if source == null as *Module || source.firstItem == null as *Decl { return; }
Print("DEBUG: copy start, source.itemCount=");
PrintInt(source.itemCount);
PrintLine("");
var decl: *Decl = source.firstItem;
var limit: int = 0;
while decl != null as *Decl && limit < 10000 {
let next: *Decl = decl.childDecl2;
decl.childDecl2 = target.firstItem;
target.firstItem = decl;
target.itemCount = target.itemCount + 1;
decl = next;
limit = limit + 1;
}
source.firstItem = null as *Decl;
source.itemCount = 0;
PrintLine("DEBUG: copy end");
}
func Cli_BuildProject(projectDir: String) -> int { func Cli_BuildProject(projectDir: String) -> int {
PrintLine("DEBUG: Cli_BuildProject start");
let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml")); let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml"));
PrintLine("DEBUG: manifest loaded");
var outName: String = man.name; var outName: String = man.name;
if String_Eq(outName, "") { if String_Eq(outName, "") {
outName = "bux_out"; outName = "bux_out";
} }
let srcDir: String = bux_path_join(projectDir, "src"); let srcDir: String = bux_path_join(projectDir, "src");
PrintLine("DEBUG: srcDir built");
if !DirExists(srcDir) { if !DirExists(srcDir) {
Print("Error: no src/ directory in "); Print("Error: no src/ directory in ");
PrintLine(projectDir); PrintLine(projectDir);
@@ -260,14 +399,14 @@ func Cli_BuildProject(projectDir: String) -> int {
PrintInt(fileCount); PrintInt(fileCount);
PrintLine(" source files"); PrintLine(" source files");
// Create merged module // Create user merged module (collect user decls first)
let merged: *Module = bux_alloc(sizeof(Module)) as *Module; let userMerged: *Module = bux_alloc(sizeof(Module)) as *Module;
merged.name = "main"; userMerged.name = "main";
merged.path = ""; userMerged.path = "";
merged.itemCount = 0; userMerged.itemCount = 0;
merged.firstItem = null as *Decl; userMerged.firstItem = null as *Decl;
// Parse each file and merge declarations into merged module // Parse each file and merge declarations into userMerged module
var i: int = 0; var i: int = 0;
while i < fileCount { while i < fileCount {
let path: String = files[i]; let path: String = files[i];
@@ -291,7 +430,7 @@ func Cli_BuildProject(projectDir: String) -> int {
PrintLine(path); PrintLine(path);
return 1; return 1;
} }
// Merge declarations from this module into merged // Merge declarations from this module into userMerged
var decl: *Decl = mod.firstItem; var decl: *Decl = mod.firstItem;
while decl != null as *Decl { while decl != null as *Decl {
let next: *Decl = decl.childDecl2; let next: *Decl = decl.childDecl2;
@@ -301,26 +440,111 @@ func Cli_BuildProject(projectDir: String) -> int {
var inner: *Decl = decl.childDecl1; var inner: *Decl = decl.childDecl1;
while inner != null as *Decl { while inner != null as *Decl {
let innerNext: *Decl = inner.childDecl2; let innerNext: *Decl = inner.childDecl2;
inner.childDecl2 = merged.firstItem; inner.childDecl2 = userMerged.firstItem;
merged.firstItem = inner; userMerged.firstItem = inner;
merged.itemCount = merged.itemCount + 1; userMerged.itemCount = userMerged.itemCount + 1;
inner = innerNext; inner = innerNext;
} }
} else { } else {
decl.childDecl2 = merged.firstItem; decl.childDecl2 = userMerged.firstItem;
merged.firstItem = decl; userMerged.firstItem = decl;
merged.itemCount = merged.itemCount + 1; userMerged.itemCount = userMerged.itemCount + 1;
} }
decl = next; decl = next;
} }
i = i + 1; i = i + 1;
} }
Print("DEBUG: userMerged.itemCount=");
PrintInt(userMerged.itemCount);
PrintLine("");
// Collect user declaration names for shadow detection
let maxNames: int = 2048;
let userNames: *String = bux_alloc(maxNames * 8) as *String;
let userNameCount: int = Cli_CollectNames(userMerged, userNames, maxNames);
// Create final merged module
let merged: *Module = bux_alloc(sizeof(Module)) as *Module;
merged.name = "main";
merged.path = "";
merged.itemCount = 0;
merged.firstItem = null as *Decl;
// Find and merge stdlib declarations
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
if !String_Eq(stdlibDir, "") {
Print("Stdlib found: ");
PrintLine(stdlibDir);
let stdSubdir: String = bux_path_join(stdlibDir, "Std");
if DirExists(stdSubdir) {
var stdFileCount: int = 0;
let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount);
Print("DEBUG: stdFileCount=");
PrintInt(stdFileCount);
PrintLine("");
if stdFileCount > 0 {
Print("Merging ");
PrintInt(stdFileCount);
PrintLine(" stdlib files");
var si: int = 0;
var stdAdded: int = 0;
while si < stdFileCount {
Print("DEBUG: merging stdlib file ");
PrintInt(si);
PrintLine("");
// Skip Channel.bux for debugging
if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") {
PrintLine("DEBUG: skipping Channel.bux");
si = si + 1;
continue;
}
let added: int = Cli_MergeFileInto(merged, stdFiles[si], userNames, userNameCount);
stdAdded = stdAdded + added;
si = si + 1;
}
Print("Stdlib declarations added: ");
PrintInt(stdAdded);
PrintLine("");
}
}
}
// Copy user declarations into merged (user shadows stdlib)
PrintLine("DEBUG: before copy");
var countTest: int = 0;
var dtest: *Decl = userMerged.firstItem;
while dtest != null as *Decl && countTest < 10000 {
dtest = dtest.childDecl2;
countTest = countTest + 1;
}
Print("userMerged actual count=");
PrintInt(countTest);
PrintLine("");
Print("userMerged.itemCount=");
PrintInt(userMerged.itemCount);
PrintLine("");
var du: *Decl = userMerged.firstItem;
while du != null as *Decl {
if du.kind == dkFunc {
Print("user func ");
PrintLine(du.strValue);
}
du = du.childDecl2;
}
Cli_CopyModuleDecls(merged, userMerged);
PrintLine("DEBUG: copy done");
Print("merged.itemCount=");
PrintInt(merged.itemCount);
PrintLine("");
PrintLine("DEBUG: copy done");
Print("Merged "); Print("Merged ");
PrintInt(merged.itemCount); PrintInt(merged.itemCount);
PrintLine(" declarations"); PrintLine(" declarations");
// Semantic analysis // Semantic analysis
PrintLine("DEBUG: about to run sema");
PrintLine("Running sema..."); PrintLine("Running sema...");
let sema: *Sema = Sema_Analyze(merged); let sema: *Sema = Sema_Analyze(merged);
if Sema_HasError(sema) { if Sema_HasError(sema) {
@@ -330,10 +554,24 @@ func Cli_BuildProject(projectDir: String) -> int {
// HIR lowering // HIR lowering
PrintLine("Lowering to HIR..."); PrintLine("Lowering to HIR...");
var di: *Decl = merged.firstItem;
while di != null as *Decl {
if di.kind == dkFunc {
Print("decl func ");
PrintLine(di.strValue);
}
di = di.childDecl2;
}
let hirMod: *HirModule = HirLower_LowerModule(merged, sema); let hirMod: *HirModule = HirLower_LowerModule(merged, sema);
// QBE SSA code generation // QBE SSA code generation
PrintLine("Generating QBE SSA..."); PrintLine("Generating QBE SSA...");
var fi: int = 0;
while fi < hirMod.funcCount {
Print("func ");
PrintLine(hirMod.funcs[fi].name);
fi = fi + 1;
}
let qbeCode: String = QbeBackend_Generate(hirMod); let qbeCode: String = QbeBackend_Generate(hirMod);
// Create build directory // Create build directory
@@ -418,6 +656,7 @@ func Cli_BuildProject(projectDir: String) -> int {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func Cli_RunProject(projectDir: String) -> int { func Cli_RunProject(projectDir: String) -> int {
PrintLine("DEBUG: Cli_RunProject start");
let rc: int = Cli_BuildProject(projectDir); let rc: int = Cli_BuildProject(projectDir);
if rc != 0 { if rc != 0 {
return rc; return rc;
@@ -487,6 +726,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
} }
if String_Eq(cmd, "run") { if String_Eq(cmd, "run") {
PrintLine("DEBUG: run command");
let dir: String = "."; let dir: String = ".";
if argCount >= 3 { dir = args[2]; } if argCount >= 3 { dir = args[2]; }
return Cli_RunProject(dir); return Cli_RunProject(dir);
+2
View File
@@ -103,6 +103,8 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
if kind == ekIdent { if kind == ekIdent {
n.kind = hVar; n.kind = hVar;
n.strValue = expr.strValue; n.strValue = expr.strValue;
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
n.typeKind = sym.typeKind;
return n; return n;
} }
+4
View File
@@ -298,6 +298,10 @@ func parserParsePrimary(p: *Parser) -> *Expr {
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func dummyFunc() -> int {
return 0;
}
// Postfix: call, index, field access, as, is, ? // Postfix: call, index, field access, as, is, ?
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+117 -21
View File
@@ -66,12 +66,16 @@ struct QbeEmitter {
// Const table for inline substitution // Const table for inline substitution
constCount: int; constCount: int;
consts: *HirConst; consts: *HirConst;
// Current function for param type lookup
currentFunc: *HirFunc;
// Module reference for function return type lookup
mod: *HirModule;
} }
func QBE_Init() -> QbeEmitter { func QBE_Init() -> QbeEmitter {
var dsb: StringBuilder = StringBuilder_NewCap(4096); var dsb: StringBuilder = StringBuilder_NewCap(4096);
var csb: StringBuilder = StringBuilder_NewCap(16384); var csb: StringBuilder = StringBuilder_NewCap(16384);
return QbeEmitter { dataSb: dsb, codeSb: csb, tempId: 0, labelId: 0, strId: 0, indent: 0, inBlock: false, constCount: 0, consts: null as *HirConst }; return QbeEmitter { dataSb: dsb, codeSb: csb, tempId: 0, labelId: 0, strId: 0, indent: 0, inBlock: false, constCount: 0, consts: null as *HirConst, currentFunc: null as *HirFunc, mod: null as *HirModule };
} }
func QBE_FreshTemp(qbe: *QbeEmitter) -> String { func QBE_FreshTemp(qbe: *QbeEmitter) -> String {
@@ -262,8 +266,8 @@ func QBE_IsTerminator(node: *HirNode) -> bool {
if node.kind == hBreak { return true; } if node.kind == hBreak { return true; }
if node.kind == hContinue { return true; } if node.kind == hContinue { return true; }
if node.kind == hIf { if node.kind == hIf {
if node.child3 != null as *HirNode { if node.extraData != null as *HirNode {
return QBE_IsTerminator(node.child2) && QBE_IsTerminator(node.child3); return QBE_IsTerminator(node.child2) && QBE_IsTerminator(node.extraData as *HirNode);
} }
return false; return false;
} }
@@ -280,6 +284,56 @@ func QBE_IsTerminator(node: *HirNode) -> bool {
return false; return false;
} }
// ---------------------------------------------------------------------------
// Helper: find function by name in module
// ---------------------------------------------------------------------------
func QBE_FindFunc(qbe: *QbeEmitter, name: String) -> *HirFunc {
if qbe.mod == null as *HirModule { return null as *HirFunc; }
var i: int = 0;
while i < qbe.mod.funcCount {
if String_Eq(qbe.mod.funcs[i].name, name) {
return &qbe.mod.funcs[i];
}
i = i + 1;
}
i = 0;
while i < qbe.mod.externCount {
if String_Eq(qbe.mod.externFuncs[i].name, name) {
return &qbe.mod.externFuncs[i];
}
i = i + 1;
}
return null as *HirFunc;
}
// ---------------------------------------------------------------------------
// Helper: determine QBE type for any HIR node
// ---------------------------------------------------------------------------
func QBE_NodeType(qbe: *QbeEmitter, node: *HirNode) -> String {
if node == null as *HirNode { return "l"; }
// For variables, check param type first
if node.kind == hVar && qbe.currentFunc != null as *HirFunc {
var pi2: int = 0;
var pnames2: *HirParam = &qbe.currentFunc.param0;
while pi2 < qbe.currentFunc.paramCount {
if String_Eq(pnames2[pi2].name, node.strValue) {
return QbeBackend_TypeWord(pnames2[pi2].typeKind);
}
pi2 = pi2 + 1;
}
}
// For calls, look up callee return type
if node.kind == hCall && qbe.mod != null as *HirModule {
let callee: *HirFunc = QBE_FindFunc(qbe, node.strValue);
if callee != null as *HirFunc {
return QbeBackend_TypeWord(callee.retTypeKind);
}
}
return QbeBackend_TypeWord(node.typeKind);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Expression emission — returns the QBE value string (e.g. "%t3", "42", "$foo") // Expression emission — returns the QBE value string (e.g. "%t3", "42", "$foo")
// For expressions that need multiple instructions, emits them inline. // For expressions that need multiple instructions, emits them inline.
@@ -325,8 +379,21 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
return "0"; return "0";
} }
} }
// Determine type: use param type if available, else node.typeKind
var varTy: int = node.typeKind;
if qbe.currentFunc != null as *HirFunc {
var pi2: int = 0;
var pnames2: *HirParam = &qbe.currentFunc.param0;
while pi2 < qbe.currentFunc.paramCount {
if String_Eq(pnames2[pi2].name, node.strValue) {
varTy = pnames2[pi2].typeKind;
break;
}
pi2 = pi2 + 1;
}
}
let tmp: String = QBE_FreshTemp(qbe); let tmp: String = QBE_FreshTemp(qbe);
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QbeBackend_TypeWord(varTy);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_Emit(qbe, " ="); QBE_Emit(qbe, " =");
@@ -350,7 +417,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
} }
let val: String = QBE_EmitExpr(qbe, node.child2); let val: String = QBE_EmitExpr(qbe, node.child2);
if String_Eq(val, "") { val = "0"; } if String_Eq(val, "") { val = "0"; }
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QBE_NodeType(qbe, node.child2);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, "store"); QBE_Emit(qbe, "store");
QBE_Emit(qbe, ty); QBE_Emit(qbe, ty);
@@ -366,12 +433,12 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
let op: int = node.intValue; let op: int = node.intValue;
let left: String = QBE_EmitExpr(qbe, node.child1); let left: String = QBE_EmitExpr(qbe, node.child1);
let right: String = QBE_EmitExpr(qbe, node.child2); let right: String = QBE_EmitExpr(qbe, node.child2);
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QBE_NodeType(qbe, node.child1);
// Comparison operators // Comparison operators
// QBE comparison ops: ceqw/ceql, cnew/cnel, csltw/csltl, cslew/cslel, csgtw/csgtl, csgew/csgel // QBE comparison ops: ceqw/ceql, cnew/cnel, csltw/csltl, cslew/cslel, csgtw/csgtl, csgew/csgel
var cmpOp: String = ""; var cmpOp: String = "";
var cmpTy: String = ty; let cmpTy: String = QBE_NodeType(qbe, node.child1);
if op == tkEq { cmpOp = "ceq"; } if op == tkEq { cmpOp = "ceq"; }
else if op == tkNe { cmpOp = "cne"; } else if op == tkNe { cmpOp = "cne"; }
else if op == tkLt { cmpOp = "cslt"; } else if op == tkLt { cmpOp = "cslt"; }
@@ -385,7 +452,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
QBE_Emit(qbe, cmpTmp); QBE_Emit(qbe, cmpTmp);
QBE_Emit(qbe, " =w "); QBE_Emit(qbe, " =w ");
QBE_Emit(qbe, cmpOp); QBE_Emit(qbe, cmpOp);
QBE_Emit(qbe, ty); QBE_Emit(qbe, cmpTy);
QBE_Emit(qbe, " "); QBE_Emit(qbe, " ");
QBE_Emit(qbe, QBE_EnsureLocal(left)); QBE_Emit(qbe, QBE_EnsureLocal(left));
QBE_Emit(qbe, ", "); QBE_Emit(qbe, ", ");
@@ -452,7 +519,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
let rVal: String = QBE_EmitExpr(qbe, node.child2); let rVal: String = QBE_EmitExpr(qbe, node.child2);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_Emit(qbe, " =l copy "); QBE_Emit(qbe, " =w copy ");
QBE_EmitLine(qbe, rVal); QBE_EmitLine(qbe, rVal);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, "jmp "); QBE_Emit(qbe, "jmp ");
@@ -460,7 +527,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
QBE_EmitLine(qbe, lFalse); QBE_EmitLine(qbe, lFalse);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_EmitLine(qbe, " =l copy 0"); QBE_EmitLine(qbe, " =w copy 0");
QBE_EmitLine(qbe, lEnd); QBE_EmitLine(qbe, lEnd);
return tmp; return tmp;
} }
@@ -479,7 +546,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
QBE_EmitLine(qbe, lTrue); QBE_EmitLine(qbe, lTrue);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_EmitLine(qbe, " =l copy 1"); QBE_EmitLine(qbe, " =w copy 1");
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, "jmp "); QBE_Emit(qbe, "jmp ");
QBE_EmitLine(qbe, lEnd); QBE_EmitLine(qbe, lEnd);
@@ -487,7 +554,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
let rVal: String = QBE_EmitExpr(qbe, node.child2); let rVal: String = QBE_EmitExpr(qbe, node.child2);
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp); QBE_Emit(qbe, tmp);
QBE_Emit(qbe, " =l copy "); QBE_Emit(qbe, " =w copy ");
QBE_EmitLine(qbe, rVal); QBE_EmitLine(qbe, rVal);
QBE_EmitLine(qbe, lEnd); QBE_EmitLine(qbe, lEnd);
return tmp; return tmp;
@@ -500,7 +567,7 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
if kind == hUnary { if kind == hUnary {
let op: int = node.intValue; let op: int = node.intValue;
let operand: String = QBE_EmitExpr(qbe, node.child1); let operand: String = QBE_EmitExpr(qbe, node.child1);
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QBE_NodeType(qbe, node.child1);
if op == tkBang || op == tkMinus { if op == tkBang || op == tkMinus {
let tmp: String = QBE_FreshTemp(qbe); let tmp: String = QBE_FreshTemp(qbe);
@@ -535,7 +602,14 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
if kind == hCall { if kind == hCall {
// Pre-evaluate arguments to avoid emitting instructions inside the call // Pre-evaluate arguments to avoid emitting instructions inside the call
let tmp: String = QBE_FreshTemp(qbe); let tmp: String = QBE_FreshTemp(qbe);
let retTy: String = QbeBackend_TypeWord(node.typeKind); var retTy: String = QbeBackend_TypeWord(node.typeKind);
// Look up callee return type from module if typeKind is unknown
if String_Eq(retTy, "l") && qbe.mod != null as *HirModule {
let callee: *HirFunc = QBE_FindFunc(qbe, node.strValue);
if callee != null as *HirFunc {
retTy = QbeBackend_TypeWord(callee.retTypeKind);
}
}
var arg1: String = ""; var arg1: String = "";
var arg2: String = ""; var arg2: String = "";
if node.child1 != null as *HirNode { if node.child1 != null as *HirNode {
@@ -552,13 +626,13 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
QBE_Emit(qbe, QBE_EscapeIdent(node.strValue)); QBE_Emit(qbe, QBE_EscapeIdent(node.strValue));
QBE_Emit(qbe, "("); QBE_Emit(qbe, "(");
if node.child1 != null as *HirNode { if node.child1 != null as *HirNode {
let a1ty: String = QbeBackend_TypeWord(node.child1.typeKind); let a1ty: String = QBE_NodeType(qbe, node.child1);
QBE_Emit(qbe, a1ty); QBE_Emit(qbe, a1ty);
QBE_Emit(qbe, " "); QBE_Emit(qbe, " ");
QBE_Emit(qbe, QBE_EnsureLocal(arg1)); QBE_Emit(qbe, QBE_EnsureLocal(arg1));
} }
if node.child2 != null as *HirNode { if node.child2 != null as *HirNode {
let a2ty: String = QbeBackend_TypeWord(node.child2.typeKind); let a2ty: String = QBE_NodeType(qbe, node.child2);
QBE_Emit(qbe, ", "); QBE_Emit(qbe, ", ");
QBE_Emit(qbe, a2ty); QBE_Emit(qbe, a2ty);
QBE_Emit(qbe, " "); QBE_Emit(qbe, " ");
@@ -599,6 +673,20 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
return tmp; return tmp;
} }
// Index pointer: &arr[idx] (for pointer indexing)
if kind == hIndexPtr {
let base: String = QBE_EmitExpr(qbe, node.child1);
let idx: String = QBE_EmitExpr(qbe, node.child2);
let tmp: String = QBE_FreshTemp(qbe);
QBE_EmitIndent(qbe);
QBE_Emit(qbe, tmp);
QBE_Emit(qbe, " =l add ");
QBE_Emit(qbe, QBE_EnsureLocal(base));
QBE_Emit(qbe, ", ");
QBE_EmitLine(qbe, QBE_EnsureLocal(idx));
return tmp;
}
// Cast — QBE doesn't need same-width casts; passthrough // Cast — QBE doesn't need same-width casts; passthrough
if kind == hCast { if kind == hCast {
return QBE_EmitExpr(qbe, node.child1); return QBE_EmitExpr(qbe, node.child1);
@@ -626,6 +714,12 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
if node == null as *HirNode { return; } if node == null as *HirNode { return; }
let kind: int = node.kind; let kind: int = node.kind;
// Assign statement
if kind == hAssign {
discard QBE_EmitExpr(qbe, node);
return;
}
// Store: declaration or assignment // Store: declaration or assignment
if kind == hStore { if kind == hStore {
if node.child1 != null as *HirNode && node.child1.kind == hAlloca { if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
@@ -653,7 +747,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
// Plain assignment // Plain assignment
let target: String = QBE_EmitExpr(qbe, node.child1); let target: String = QBE_EmitExpr(qbe, node.child1);
let val: String = QBE_EmitExpr(qbe, node.child2); let val: String = QBE_EmitExpr(qbe, node.child2);
let ty: String = QbeBackend_TypeWord(node.typeKind); let ty: String = QBE_NodeType(qbe, node.child2);
if String_Eq(val, "") { val = "0"; } if String_Eq(val, "") { val = "0"; }
if String_Eq(target, "") { return; } if String_Eq(target, "") { return; }
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
@@ -692,7 +786,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
QBE_Emit(qbe, ", "); QBE_Emit(qbe, ", ");
QBE_Emit(qbe, lThen); QBE_Emit(qbe, lThen);
QBE_Emit(qbe, ", "); QBE_Emit(qbe, ", ");
var hasElse: bool = node.child3 != null as *HirNode; var hasElse: bool = node.extraData != null as *HirNode;
if hasElse { if hasElse {
QBE_EmitLine(qbe, lElse); QBE_EmitLine(qbe, lElse);
} else { } else {
@@ -709,8 +803,8 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
if hasElse { if hasElse {
QBE_EmitLine(qbe, lElse); QBE_EmitLine(qbe, lElse);
QBE_EmitStmt(qbe, node.child3); QBE_EmitStmt(qbe, node.extraData as *HirNode);
if !QBE_IsTerminator(node.child3) { if !QBE_IsTerminator(node.extraData as *HirNode) {
QBE_EmitIndent(qbe); QBE_EmitIndent(qbe);
QBE_Emit(qbe, "jmp "); QBE_Emit(qbe, "jmp ");
QBE_EmitLine(qbe, lEnd); QBE_EmitLine(qbe, lEnd);
@@ -719,7 +813,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
// Only emit @lEnd if at least one branch falls through // Only emit @lEnd if at least one branch falls through
var needEnd: bool = !QBE_IsTerminator(node.child2); var needEnd: bool = !QBE_IsTerminator(node.child2);
if hasElse && !QBE_IsTerminator(node.child3) { if hasElse && !QBE_IsTerminator(node.extraData as *HirNode) {
needEnd = true; needEnd = true;
} }
if needEnd { if needEnd {
@@ -828,6 +922,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
func QBE_EmitFunc(qbe: *QbeEmitter, f: *HirFunc) { func QBE_EmitFunc(qbe: *QbeEmitter, f: *HirFunc) {
if f.body == null as *HirNode { return; } if f.body == null as *HirNode { return; }
qbe.currentFunc = f;
QBE_Emit(qbe, "export function "); QBE_Emit(qbe, "export function ");
let retTy: String = QbeBackend_TypeWord(f.retTypeKind); let retTy: String = QbeBackend_TypeWord(f.retTypeKind);
@@ -929,6 +1024,7 @@ func QbeBackend_Generate(mod: *HirModule) -> String {
qbe.inBlock = false; qbe.inBlock = false;
qbe.constCount = mod.constCount; qbe.constCount = mod.constCount;
qbe.consts = mod.consts; qbe.consts = mod.consts;
qbe.mod = mod;
// Header // Header
QBE_EmitLine(qbe, "# Generated by Bux QBE Backend"); QBE_EmitLine(qbe, "# Generated by Bux QBE Backend");
+1 -1
View File
@@ -312,7 +312,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
let s: *Sema = bux_alloc(sizeof(Sema)) as *Sema; let s: *Sema = bux_alloc(sizeof(Sema)) as *Sema;
s.module = mod; s.module = mod;
s.scope = bux_alloc(sizeof(Scope)) as *Scope; s.scope = bux_alloc(sizeof(Scope)) as *Scope;
s.scope.symbols = bux_alloc(256 as uint * sizeof(Symbol)) as *Symbol; s.scope.symbols = bux_alloc(1024 as uint * sizeof(Symbol)) as *Symbol;
s.scope.count = 0; s.scope.count = 0;
s.scope.parent = null as *Scope; s.scope.parent = null as *Scope;
s.hasError = false; s.hasError = false;