- Replace all C code generation with LLVM IR (flat DFA model with bb_ prefix labels to avoid SSA value name collisions) - Remove c.min.js, add llvm.min.js highlight.js language definition - Update shell.nix: gcc -> clang, remove sdl3/mesa deps - Download LLVM Language Reference to docs/llvm/LangRef.html - Rename #c-code to #ir-code in HTML, JS, and CSS - Replace _generateIncludes with _generateLLVMDecls for extern function declarations using LLVM_DECL_MAP - Add string pool (CodeGen.allocString, stringGlobal, llvmStringArg) for LLVM IR global string constants with getelementptr references
149 lines
5.2 KiB
JavaScript
149 lines
5.2 KiB
JavaScript
// ── Pure LLVM IR code generation — zero-DOM, flat DFA model ───────────
|
|
//
|
|
// Every block is an LLVM IR label. Every edge is a `br` instruction.
|
|
// Decisions become `br i1 %cond, label %yes, label %no`.
|
|
// Error handling becomes `br i1 %result, label %err, label %ok`.
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
var CodeGen = {};
|
|
|
|
// ── String pool ─────────────────────────────────────────────────────
|
|
|
|
var _strPool = [];
|
|
CodeGen.resetStringPool = function () { _strPool = []; };
|
|
CodeGen.getStringPool = function () { return _strPool; };
|
|
|
|
/**
|
|
* Registers a string literal and returns its unique index.
|
|
*/
|
|
CodeGen.allocString = function (s) {
|
|
var idx = _strPool.indexOf(s);
|
|
if (idx === -1) { idx = _strPool.length; _strPool.push(s); }
|
|
return idx;
|
|
};
|
|
|
|
/**
|
|
* Escapes a JS string for LLVM IR `c"..."` constant, and produces
|
|
* the full `getelementptr` reference for use as a call argument.
|
|
*/
|
|
CodeGen.llvmStringArg = function (str, idx) {
|
|
var escaped = "";
|
|
for (var i = 0; i < str.length; i++) {
|
|
var ch = str.charCodeAt(i);
|
|
if (ch === 0x22) escaped += "\\22";
|
|
else if (ch === 0x5C) escaped += "\\5C";
|
|
else if (ch === 0x0A) escaped += "\\0A";
|
|
else if (ch === 0x0D) escaped += "\\0D";
|
|
else if (ch === 0x09) escaped += "\\09";
|
|
else if (ch < 0x20 || ch > 0x7E) {
|
|
escaped += "\\" + ch.toString(16).toUpperCase().padStart(2, "0");
|
|
} else {
|
|
escaped += str[i];
|
|
}
|
|
}
|
|
var byteLen = str.length + 1; // includes null terminator
|
|
return "i8* getelementptr inbounds ([" + byteLen + " x i8], [" + byteLen + " x i8]* @.str." + idx + ", i32 0, i32 0)";
|
|
};
|
|
|
|
/**
|
|
* Generates the LLVM IR global constant for a string pool entry.
|
|
*/
|
|
CodeGen.stringGlobal = function (idx) {
|
|
var str = _strPool[idx];
|
|
var escaped = "", byteLen = 0;
|
|
for (var i = 0; i < str.length; i++) {
|
|
var ch = str.charCodeAt(i);
|
|
byteLen++;
|
|
if (ch === 0x22) escaped += "\\22";
|
|
else if (ch === 0x5C) escaped += "\\5C";
|
|
else if (ch === 0x0A) escaped += "\\0A";
|
|
else if (ch === 0x0D) escaped += "\\0D";
|
|
else if (ch === 0x09) escaped += "\\09";
|
|
else if (ch < 0x20 || ch > 0x7E) {
|
|
var h = ch.toString(16).toUpperCase();
|
|
escaped += "\\" + (h.length === 1 ? "0" : "") + h;
|
|
} else {
|
|
escaped += str[i];
|
|
}
|
|
}
|
|
byteLen++; // null terminator
|
|
return "@.str." + idx + " = private unnamed_addr constant [" + byteLen + " x i8] c\"" + escaped + "\\00\"";
|
|
};
|
|
|
|
// ── Flat DFA emit ───────────────────────────────────────────────────
|
|
|
|
CodeGen.emitFlat = function (entry) {
|
|
var visited = new Set();
|
|
var parts = [];
|
|
|
|
function bb(block) { return "bb_" + block.id; }
|
|
|
|
function emit(block) {
|
|
if (visited.has(block)) {
|
|
parts.push(" br label %" + bb(block) + "\n");
|
|
return;
|
|
}
|
|
visited.add(block);
|
|
parts.push("\n" + bb(block) + ":");
|
|
|
|
var own = block.generateOwnCode();
|
|
if (own) parts.push(own);
|
|
|
|
var flow = block.wcNextFlow || block.nextFlow || [];
|
|
var fc = "";
|
|
if (typeof block.generateFlowCode === "function") {
|
|
fc = block.generateFlowCode();
|
|
// Replace bare label references with bb_ prefixed versions
|
|
var replaced = "";
|
|
var pos = 0, m;
|
|
while ((m = /label\s+%([\w.]+)/g).lastIndex = pos, m = m.exec(fc)) {
|
|
replaced += fc.slice(pos, m.index) + "label %bb_" + m[1];
|
|
pos = m.index + m[0].length;
|
|
}
|
|
replaced += fc.slice(pos);
|
|
fc = replaced;
|
|
} else {
|
|
for (var i = 0; i < flow.length; i++) {
|
|
var t = flow[i].toElement;
|
|
if (t) fc += " br label %" + bb(t) + "\n";
|
|
}
|
|
}
|
|
if (fc) parts.push("\n" + fc);
|
|
|
|
for (var i = 0; i < flow.length; i++) {
|
|
var t = flow[i].toElement;
|
|
if (t) emit(t);
|
|
}
|
|
}
|
|
|
|
emit(entry);
|
|
return parts.join("");
|
|
};
|
|
|
|
// ── Full module assembly ────────────────────────────────────────────
|
|
|
|
CodeGen.generateIRFile = function (entryBlock, declarations, globals, prologue) {
|
|
var lines = ["; ModuleID = \"wisecode\"", "target triple = \"x86_64-pc-linux-gnu\"", ""];
|
|
if (declarations) lines.push(declarations);
|
|
if (globals) lines.push(globals);
|
|
lines.push("");
|
|
lines.push("define i32 @main() {");
|
|
if (prologue) lines.push("entry:\n" + prologue + "\n br label %bb_" + entryBlock.id);
|
|
lines.push(CodeGen.emitFlat(entryBlock));
|
|
lines.push("}");
|
|
lines.push("");
|
|
lines.push("; --- END ---");
|
|
return lines.join("\n");
|
|
};
|
|
|
|
// ── Export ──────────────────────────────────────────────────────────
|
|
if (typeof module !== "undefined" && module.exports) {
|
|
module.exports = CodeGen;
|
|
}
|
|
if (typeof window !== "undefined") {
|
|
window.CodeGen = CodeGen;
|
|
}
|
|
})();
|