feat: switch from C to LLVM IR code generation
- 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
This commit is contained in:
+91
-34
@@ -1,61 +1,117 @@
|
||||
// ── Pure C code generation — zero-DOM, flat DFA model ─────────────────
|
||||
// ── Pure LLVM IR code generation — zero-DOM, flat DFA model ───────────
|
||||
//
|
||||
// Every block is a C label. Every flow edge is a `goto label;`.
|
||||
// There is zero nesting: no if/else chains, no branch-analysis, no
|
||||
// shared-block computation. Decisions become `if (cond) goto yes;
|
||||
// goto no;`. Error handling becomes `if (call != 0) goto err;
|
||||
// goto ok;`.
|
||||
//
|
||||
// Block interface:
|
||||
// { id: string,
|
||||
// generateOwnCode(): string, // C code for this one block
|
||||
// generateFlowCode?(): string, // flow-control (gotos). If absent,
|
||||
// // CodeGen walks wcNextFlow instead.
|
||||
// wcNextFlow?: [{toElement: block}],
|
||||
// nextFlow?: [{toElement: block}] }
|
||||
// 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; };
|
||||
|
||||
/**
|
||||
* Emits a complete C program as a flat sequence of labelled blocks
|
||||
* connected by gotos. Each block is emitted once (DFS order); already-
|
||||
* visited blocks become `goto label;` stubs.
|
||||
*
|
||||
* @param {Object} entry the first block (e.g. START)
|
||||
* @returns {string}
|
||||
* 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(" goto " + block.id + ";\n");
|
||||
parts.push(" br label %" + bb(block) + "\n");
|
||||
return;
|
||||
}
|
||||
visited.add(block);
|
||||
parts.push("\n" + block.id + ":");
|
||||
parts.push("\n" + bb(block) + ":");
|
||||
|
||||
var own = block.generateOwnCode();
|
||||
if (own) parts.push(own);
|
||||
|
||||
// Flow control: custom generateFlowCode, or auto from wcNextFlow
|
||||
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 += " goto " + t.id + ";\n";
|
||||
if (t) fc += " br label %" + bb(t) + "\n";
|
||||
}
|
||||
}
|
||||
if (fc) parts.push("\n" + fc);
|
||||
|
||||
// Recurse into successors (visited blocks are skipped at top)
|
||||
for (var i = 0; i < flow.length; i++) {
|
||||
var t = flow[i].toElement;
|
||||
if (t) emit(t);
|
||||
@@ -66,18 +122,19 @@
|
||||
return parts.join("");
|
||||
};
|
||||
|
||||
/**
|
||||
* Assembles a complete C source file.
|
||||
*/
|
||||
CodeGen.generateCFile = function (entryBlock, includes, declarations) {
|
||||
var lines = ["// --- WiseCode\u00ae ---"];
|
||||
for (var i = 0; i < includes.length; i++) lines.push(includes[i]);
|
||||
lines.push("");
|
||||
lines.push("int main() {");
|
||||
// ── 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("// --- END ---");
|
||||
lines.push("");
|
||||
lines.push("; --- END ---");
|
||||
return lines.join("\n");
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user