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
+64 -98
View File
@@ -477,127 +477,93 @@ class WCProcCall extends WCBlock {
return block.dataType === "string" ? name + ".data" : name;
}
generateOwnCode() {
const qname = this._selectedProc;
if (!qname) return "";
const parts = qname.split("/");
const procName = parts[1] || qname;
let returnStyle = this._cachedReturnStyle || "pointer";
const inputs = [];
const outputs = [];
for (const row of this._el.inputs.querySelectorAll(".proccall-arg-row")) {
const hook = row.querySelector(".hook");
const inp = row.querySelector(".proccall-arg-input");
const source = hook ? findDataSource(hook) : null;
_buildLLVMArgs() {
var inputs = [];
var outputs = [];
for (var ri = 0; ri < this._el.inputs.querySelectorAll(".proccall-arg-row").length; ri++) {
var row = this._el.inputs.querySelectorAll(".proccall-arg-row")[ri];
var hook = row.querySelector(".hook");
var inp = row.querySelector(".proccall-arg-input");
var source = hook ? findDataSource(hook) : null;
if (source) {
inputs.push(this._sourceName(source));
inputs.push("%" + this._sourceName(source));
} else {
let val = inp.value || inp.placeholder;
if (inp.dataset.paramType === "string") val = `"${escapeString(val)}"`;
var val = inp.value || inp.placeholder;
if (inp.dataset.paramType === "string") {
var idx = CodeGen.allocString(val);
val = CodeGen.llvmStringArg(val, idx);
} else if (inp.dataset.paramType === "int" || inp.dataset.paramType === "i32") {
val = "i32 " + val;
} else if (inp.dataset.paramType === "float" || inp.dataset.paramType === "double") {
val = "double " + val;
} else {
val = "i32 " + val; // default integer
}
inputs.push(val);
}
}
for (const row of this._el.outputs.querySelectorAll(".proccall-arg-row")) {
const inp = row.querySelector(".proccall-arg-input");
outputs.push("&" + (inp.value || inp.placeholder));
}
if (this._fmtProc) {
for (const row of this._el.fmtargsList.querySelectorAll(".proccall-arg-row")) {
const hook = row.querySelector(".hook");
const inp = row.querySelector(".proccall-arg-input");
const source = hook ? findDataSource(hook) : null;
if (source) {
inputs.push(this._sourceName(source));
} else {
let val = inp.value || inp.placeholder;
if (hook.dataset.type === "string") val = `"${escapeString(val)}"`;
inputs.push(val);
}
}
for (var rj = 0; rj < this._el.outputs.querySelectorAll(".proccall-arg-row").length; rj++) {
var row2 = this._el.outputs.querySelectorAll(".proccall-arg-row")[rj];
var inp2 = row2.querySelector(".proccall-arg-input");
outputs.push(inp2.value || inp2.placeholder);
}
return { inputs: inputs, outputs: outputs };
}
const hasErrPath = this._el.errHook && this._el.errHook.classList.contains("visible") &&
Array.from(this.wcNextFlow).some(p => p.fromHook === this._el.errHook);
// For error-capable calls, the call is emitted inside generateFlowCode
// (as part of the if-condition) to avoid double execution.
if (returnStyle === "error" && hasErrPath) return "";
if (returnStyle === "value" && outputs.length === 1 && hasErrPath) {
const outVar = outputs[0].replace(/^&/, "");
return ` ${outVar} = ${procName}(${inputs.join(", ")});\n`;
}
generateOwnCode() {
var qname = this._selectedProc;
if (!qname) return "";
var sp = qname.split("/");
var procName = sp[1] || qname;
var returnStyle = this._cachedReturnStyle || "pointer";
var io = this._buildLLVMArgs();
var callArgs = io.inputs.join(", ");
var hasErrPath = this._el.errHook && this._el.errHook.classList.contains("visible") &&
Array.from(this.wcNextFlow).some(function (p) { return p.fromHook === this._el.errHook; }.bind(this));
var rn = "%_r_" + this.id, vn = "%_v_" + this.id;
if (returnStyle === "error") {
return ` ${procName}(${inputs.join(", ")});\n`;
if (hasErrPath) return " " + rn + " = call i32 @" + procName + "(" + callArgs + ")\n";
return " call void @" + procName + "(" + callArgs + ")\n";
}
if (returnStyle === "value" && outputs.length === 1) {
return ` ${procName}(${inputs.join(", ")});\n`;
if (returnStyle === "value" && io.outputs.length === 1) {
if (hasErrPath) return " " + vn + " = call i32 @" + procName + "(" + callArgs + ")\n";
return " call void @" + procName + "(" + callArgs + ")\n";
}
const args = [...inputs, ...outputs].join(", ");
return ` ${procName}(${args});\n`;
return " call void @" + procName + "(" + callArgs + ")\n";
}
generateFlowCode() {
const hasErrPath = this._el.errHook && this._el.errHook.classList.contains("visible") &&
Array.from(this.wcNextFlow).some(p => p.fromHook === this._el.errHook);
var hasErrPath = this._el.errHook && this._el.errHook.classList.contains("visible") &&
Array.from(this.wcNextFlow).some(function (p) { return p.fromHook === this._el.errHook; }.bind(this));
if (!hasErrPath) return WCBlock.prototype.generateFlowCode.call(this);
const qname = this._selectedProc;
var qname = this._selectedProc;
if (!qname) return "";
const parts = qname.split("/");
const procName = parts[1] || qname;
var sp = qname.split("/");
var procName = sp[1] || qname;
var returnStyle = this._cachedReturnStyle || "pointer";
var io = this._buildLLVMArgs();
let returnStyle = this._cachedReturnStyle || "pointer";
const inputs = [];
const outputs = [];
for (const row of this._el.inputs.querySelectorAll(".proccall-arg-row")) {
const hook = row.querySelector(".hook");
const inp = row.querySelector(".proccall-arg-input");
const source = hook ? findDataSource(hook) : null;
if (source) {
inputs.push(this._sourceName(source));
} else {
inputs.push(inp.value || inp.placeholder);
}
}
for (const row of this._el.outputs.querySelectorAll(".proccall-arg-row")) {
const inp = row.querySelector(".proccall-arg-input");
outputs.push("&" + (inp.value || inp.placeholder));
}
if (this._fmtProc) {
for (const row of this._el.fmtargsList.querySelectorAll(".proccall-arg-row")) {
const hook = row.querySelector(".hook");
const inp = row.querySelector(".proccall-arg-input");
const source = hook ? findDataSource(hook) : null;
if (source) {
inputs.push(this._sourceName(source));
} else {
inputs.push(inp.value || inp.placeholder);
}
}
var errTarget = null, okTarget = null;
var flows = Array.from(this.wcNextFlow);
for (var fi = 0; fi < flows.length; fi++) {
var p = flows[fi];
if (p.fromHook === this._el.errHook) errTarget = p.toElement;
else okTarget = p.toElement;
}
const args = inputs.join(", ");
const errTarget = Array.from(this.wcNextFlow)
.filter(p => p.fromHook === this._el.errHook)
.map(p => p.toElement)[0];
const okTarget = Array.from(this.wcNextFlow)
.filter(p => p.fromHook !== this._el.errHook)
.map(p => p.toElement)[0];
let code = "";
var rn = "%_r_" + this.id, vn = "%_v_" + this.id, cn = "%_cmp_" + this.id;
var code = "";
if (returnStyle === "error") {
code += ` if (${procName}(${args}) != 0) goto ${errTarget.id};\n`;
} else if (returnStyle === "value" && outputs.length === 1) {
const outVar = outputs[0].replace(/^&/, "");
code += ` if (${outVar} != 0) goto ${errTarget.id};\n`;
code += " " + cn + " = icmp ne i32 " + rn + ", 0\n";
if (errTarget) code += " br i1 " + cn + ", label %" + errTarget.id + ", label %" + (okTarget ? okTarget.id : "") + "\n";
} else if (returnStyle === "value" && io.outputs.length === 1) {
code += " " + cn + " = icmp ne i32 " + vn + ", 0\n";
if (errTarget) code += " br i1 " + cn + ", label %" + errTarget.id + ", label %" + (okTarget ? okTarget.id : "") + "\n";
}
if (okTarget) code += ` goto ${okTarget.id};\n`;
return code;
}