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:
+64
-98
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user