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:
2026-06-23 03:39:22 +02:00
parent 35959784de
commit d53f92482d
12 changed files with 289 additions and 296 deletions
+37 -44
View File
@@ -190,29 +190,27 @@ class WCProgram extends HTMLElement {
return { boolDefs, scalarDefs, vectorDefs, stringDefs, arrayDefs };
}
_generateIncludes(proccalls, hasStrings) {
const libNames = new Set();
for (const p of proccalls) {
const qname = p._selectedProc;
if (qname) {
const lib = qname.split("/")[0];
if (lib) libNames.add(lib);
_generateLLVMDecls(proccalls) {
var seen = new Set();
var lines = [];
for (var pi = 0; pi < proccalls.length; pi++) {
var qname = proccalls[pi]._selectedProc;
if (!qname || seen.has(qname)) continue;
seen.add(qname);
var decl = LLVM_DECL_MAP[qname];
if (decl) {
lines.push(decl);
} else {
var fn = qname.split("/")[1] || qname;
lines.push("declare i32 @" + fn + "(...)");
}
}
let includes = Array.from(libNames).map(l => {
const path = INCLUDE_MAP[l] || (l + ".h");
return `#include <${path}>`;
}).join("\n");
if (hasStrings) {
const hdr = "#include <stdlib.h>\n#include <string.h>";
includes = includes ? includes + "\n" + hdr : hdr;
}
return includes;
return lines.join("\n");
}
generateCode() {
const outputEl = $id("c-code");
if (!outputEl) { showError("Missing #c-code output element"); return; }
const outputEl = $id("ir-code");
if (!outputEl) { showError("Missing #ir-code output element"); return; }
const { usedQuantities, usedUnits } = this._gatherUsedTypes();
@@ -220,51 +218,46 @@ class WCProgram extends HTMLElement {
const unitDefs = this._generateUnitDefs(usedUnits);
const { boolDefs, scalarDefs, vectorDefs, stringDefs, arrayDefs } = this._generateVariableDeclarations();
const hasStrings = $id("variable-area").querySelectorAll("wc-string").length > 0;
const stringStruct = hasStrings
? "typedef struct { char *data; long int length; } string;\n\n"
: "";
CodeGen.resetStringPool();
const firstChild = this.firstElementChild;
const body = firstChild && typeof CodeGen.emitFlat === "function"
? CodeGen.emitFlat(firstChild)
: "\n// (no flowchart body)\n";
const proccalls = this.querySelectorAll("wc-proccall");
const includes = this._generateIncludes(proccalls, hasStrings);
: "; no flowchart body\n";
// Collect external function declarations from used libraries
const proccalls = this.querySelectorAll("wc-proccall");
const decls = this._generateLLVMDecls(proccalls);
// Collect string globals
const strPool = CodeGen.getStringPool();
const globals = strPool.map(function (_, i) { return CodeGen.stringGlobal(i); }).join("\n");
// Variable declarations -> alloca instructions in the prologue
const dataSection = (boolDefs || scalarDefs || vectorDefs || stringDefs || arrayDefs)
? [boolDefs, scalarDefs, vectorDefs, stringDefs, arrayDefs]
.filter(Boolean).map(s => s + "\n").join("")
.filter(Boolean).map(function (s) { return s + "\n"; }).join("")
: "";
const hasReturnOrExit =
body.includes("\n return ") || body.includes("\n exit(");
const raw =
"// --- WiseCode\u00ae ---\n" +
(includes ? includes + "\n\n" : "") +
"int main() {\n" +
(typeDefs ? typeDefs + "\n\n" : "") +
(unitDefs ? unitDefs + "\n\n" : "") +
stringStruct + dataSection + body +
(hasReturnOrExit ? "" : "return 0;\n") +
"}\n// --- END ---";
const raw = CodeGen.generateIRFile(firstChild, decls, globals, dataSection);
outputEl.textContent = raw;
if (typeof hljs !== "undefined") hljs.highlightElement(outputEl);
outputEl.parentElement.dataset.language = "c";
outputEl.parentElement.dataset.language = "llvm";
}
}
/**
* Maps library names to their C #include paths.
* Libraries not listed here use the default `<{name}.h>` convention.
* Maps library/function names to their LLVM IR `declare` signature.
* Key is the proc qname, value is the declare line.
* Functions not listed get a generic `declare i32 @name(...)`.
*/
const INCLUDE_MAP = {
"sdl3": "SDL3/SDL.h",
"glfw": "GLFW/glfw3.h",
"gl": "GL/gl.h",
const LLVM_DECL_MAP = {
"stdio/printf": "declare i32 @printf(i8*, ...)",
"stdlib/exit": "declare void @exit(i32)",
};
// ═══════════════════════════════════════════════════════════════════════════════
// Block registry
// ═══════════════════════════════════════════════════════════════════════════════