mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
Unwind directly after runtime throws that always leave an exception
The compiler-resolved Node throws (error.nodeThrow, the argument and property type throws, undeclared global reads) always leave an exception pending, yet each was followed by the generic pending test and a branch. tsc-ts has 12,177 such sites, mostly the mismatch arms of checked casts and member reads of possibly-undefined values. Unwind unconditionally instead; emission continues in a block without predecessors so the caller's remaining instructions stay well-formed, and LLVM deletes it.
This commit is contained in:
@@ -411,7 +411,8 @@ test("checked field receivers borrow only the successful projection", () => {
|
||||
};
|
||||
const llvm = work(text(child(checked)), union);
|
||||
expect(llvm).toContain("@scr_throw_node_coded");
|
||||
expect(llvm).toMatch(PENDING_CHECK);
|
||||
// The throw always leaves an exception pending: it unwinds directly.
|
||||
expect(llvm).not.toMatch(PENDING_CHECK);
|
||||
expect(llvm).not.toContain("@scr_union_retain_v");
|
||||
expect(llvm).not.toMatch(/call ptr @sc_rretain_/);
|
||||
const captured = work(text(child(checked)), union, true);
|
||||
|
||||
@@ -3626,6 +3626,18 @@ export class LlEmitter {
|
||||
B.startBlock(lk);
|
||||
}
|
||||
|
||||
/** After a runtime call that ALWAYS leaves an exception pending (the
|
||||
* compiler-resolved Node throws): the pending test would always answer
|
||||
* true, so unwind unconditionally. Emission continues in a block with no
|
||||
* predecessors so the caller's remaining instructions stay well-formed;
|
||||
* LLVM deletes it. */
|
||||
emitAlwaysPendingUnwind(): void {
|
||||
const B = this.B;
|
||||
if (B.isTerminated()) return;
|
||||
this.emitUnwind();
|
||||
B.startBlock(B.newLabel("exc.dead"));
|
||||
}
|
||||
|
||||
private workerLoopBudget(): string | null {
|
||||
if (!this.mod.workers) return null;
|
||||
const slot = this.B.slot();
|
||||
|
||||
@@ -114,7 +114,7 @@ export function emitDynamicLibCall(host: LlvmEmitterContext, e: LibCallExpr): Ll
|
||||
const ty = host.llType(e.type);
|
||||
const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
|
||||
const out = host.own({ name: dummy, type: e.type });
|
||||
host.emitPendingCheck();
|
||||
host.emitAlwaysPendingUnwind();
|
||||
return out;
|
||||
}
|
||||
return host.emitGenericLibCall(e);
|
||||
|
||||
@@ -636,12 +636,12 @@ export function emitErrorsEventsLibCall(host: LlvmEmitterContext, e: LibCallExpr
|
||||
B.line(`call void @scr_throw_arg_type(ptr ${an.name}, ptr ${ex.name}, ptr ${got.name})`);
|
||||
const ty = host.llType(e.type);
|
||||
if (ty === "void") {
|
||||
host.emitPendingCheck();
|
||||
host.emitAlwaysPendingUnwind();
|
||||
return { name: "", type: e.type };
|
||||
}
|
||||
const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
|
||||
const out = host.own({ name: dummy, type: e.type });
|
||||
host.emitPendingCheck();
|
||||
host.emitAlwaysPendingUnwind();
|
||||
return out;
|
||||
}
|
||||
if (e.fn === "error.propTypeThrow") {
|
||||
@@ -653,12 +653,12 @@ export function emitErrorsEventsLibCall(host: LlvmEmitterContext, e: LibCallExpr
|
||||
B.line(`call void @scr_throw_prop_type(ptr ${an.name}, ptr ${ex.name}, ptr ${got.name})`);
|
||||
const ty = host.llType(e.type);
|
||||
if (ty === "void") {
|
||||
host.emitPendingCheck();
|
||||
host.emitAlwaysPendingUnwind();
|
||||
return { name: "", type: e.type };
|
||||
}
|
||||
const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
|
||||
const out = host.own({ name: dummy, type: e.type });
|
||||
host.emitPendingCheck();
|
||||
host.emitAlwaysPendingUnwind();
|
||||
return out;
|
||||
}
|
||||
if (e.fn === "error.nodeThrow") {
|
||||
@@ -673,12 +673,12 @@ export function emitErrorsEventsLibCall(host: LlvmEmitterContext, e: LibCallExpr
|
||||
);
|
||||
const ty = host.llType(e.type);
|
||||
if (ty === "void") {
|
||||
host.emitPendingCheck();
|
||||
host.emitAlwaysPendingUnwind();
|
||||
return { name: "", type: e.type };
|
||||
}
|
||||
const dummy = ty === "double" ? f64Lit(0) : ty === "i1" ? "false" : "null";
|
||||
const out = host.own({ name: dummy, type: e.type });
|
||||
host.emitPendingCheck();
|
||||
host.emitAlwaysPendingUnwind();
|
||||
return out;
|
||||
}
|
||||
if (e.fn === "emitter.on") {
|
||||
|
||||
Reference in New Issue
Block a user