EXPERIMENT: emit the wrapper unconditionally, so exit binds at runtime too
ISHIKAWA: why did exit injection still need compile-time knowledge? Because the
body-helper wrapper was only emitted when codegen already knew an exit
construct existed. The wrapper being conditional was the cause, not the wrapper
being necessary.
PREDICTIONS AND RESULTS
P1 exit becomes runtime-bindable TRUE returns 14, bound
after the build
P2 codegen shrinks TRUE 5094 -> 5044
P3 cost 5-15% from a call frame on every fn FALSE 0.37s -> 0.38s, ~3%
P4 fixpoint holds TRUE
Every fn now gets a body helper and a wrapper. It has to be unconditional:
early returns must route through something for an exit construct to observe
them, and codegen cannot know which fns will be bound after the binary exists.
Removed with the machinery: declare_exit, decorator_exit, cg_exit_target,
cg_exit_construct, and the injects_at_exit scanner branch.
Two controls failed and were rewritten rather than repaired --
no-exit-construct-emits-no-wrapper asserted the optimisation this removes, so
it is now inverted. The integration harness gained a seventh assertion: an exit
construct declared after the build replaces the result.
99/99 native, 7/7 integration, fixpoint gen2==gen3.
This commit is contained in:
@@ -3214,22 +3214,6 @@ fn cg_entry_seam(stmt: Map<String, Any>, fn_name: String) -> Void {
|
||||
|
||||
// cg_exit_target / cg_exit_construct — the first construct on this fn that
|
||||
// injects at exit, or "" if none.
|
||||
fn cg_exit_target(stmt: Map<String, Any>) -> String {
|
||||
let xdl = stmt["decorators"]
|
||||
let n_xdl: Int = native_list_len(xdl)
|
||||
let xi = 0
|
||||
let found: String = ""
|
||||
while xi < n_xdl {
|
||||
if str_eq(found, "") {
|
||||
let xd = native_list_get(xdl, xi)
|
||||
let xdn: String = xd["name"]
|
||||
let xt: String = decorator_exit(xdn)
|
||||
if !str_eq(xt, "") { let found = xt }
|
||||
}
|
||||
let xi = xi + 1
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
fn cg_wrap_target(stmt: Map<String, Any>) -> String {
|
||||
let wdl = stmt["decorators"]
|
||||
@@ -3309,22 +3293,6 @@ fn params_to_env_args(params: [Any]) -> String {
|
||||
out
|
||||
}
|
||||
|
||||
fn cg_exit_construct(stmt: Map<String, Any>) -> String {
|
||||
let xdl = stmt["decorators"]
|
||||
let n_xdl: Int = native_list_len(xdl)
|
||||
let xi = 0
|
||||
let found: String = ""
|
||||
while xi < n_xdl {
|
||||
if str_eq(found, "") {
|
||||
let xd = native_list_get(xdl, xi)
|
||||
let xdn: String = xd["name"]
|
||||
let xt: String = decorator_exit(xdn)
|
||||
if !str_eq(xt, "") { let found = xdn }
|
||||
}
|
||||
let xi = xi + 1
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
// params_to_call_args — "a, b, c" from the param list, for the wrapper's call
|
||||
// into the body helper.
|
||||
@@ -3390,12 +3358,10 @@ fn cg_fn(stmt: Map<String, Any>) -> Void {
|
||||
// silently miss every early return — which is precisely the class of
|
||||
// failure this seam exists to remove. Fns with no exit construct emit
|
||||
// exactly as before, byte for byte.
|
||||
let exit_target: String = cg_exit_target(stmt)
|
||||
let exit_construct: String = cg_exit_construct(stmt)
|
||||
let wrap_target: String = cg_wrap_target(stmt)
|
||||
let wrap_construct: String = cg_wrap_construct(stmt)
|
||||
let has_wrap: Bool = !str_eq(wrap_target, "")
|
||||
let has_exit: Bool = !str_eq(exit_target, "") || has_wrap
|
||||
let has_exit: Bool = true
|
||||
if has_exit {
|
||||
emit_line("static el_val_t __el_body_" + fn_name + "(" + params_c + ") {")
|
||||
} else {
|
||||
@@ -3448,9 +3414,7 @@ fn cg_fn(stmt: Map<String, Any>) -> Void {
|
||||
} else {
|
||||
emit_line(" el_val_t __r = __el_body_" + fn_name + "(" + params_to_call_args(params) + ");")
|
||||
}
|
||||
if !str_eq(exit_target, "") {
|
||||
emit_line(" " + exit_target + "(EL_STR(" + c_str_lit(fn_name) + "), EL_STR(" + c_str_lit(exit_construct) + "), __r);")
|
||||
}
|
||||
emit_line(" __r = el_seam_run(EL_STR(" + c_str_lit(fn_name) + "), 1, __r);")
|
||||
emit_line(" return __r;")
|
||||
emit_line("}")
|
||||
}
|
||||
@@ -4312,13 +4276,7 @@ fn program_has_routes(recs: [Map<String, Any>]) -> Bool {
|
||||
// persist-after-mutate sites and 9-of-9 failed index-after-append sites — every
|
||||
// one an obligation that decayed into "remember to do this after you mutate."
|
||||
// An obligation a human must remember is not an obligation.
|
||||
fn declare_exit(name: String, exits: String) -> Void {
|
||||
state_set("__dec_exit_" + name, exits)
|
||||
}
|
||||
|
||||
fn decorator_exit(name: String) -> String {
|
||||
state_get("__dec_exit_" + name)
|
||||
}
|
||||
|
||||
// A WRAP construct receives the body as a CLOSURE and decides how, whether, and
|
||||
// how many times to invoke it.
|
||||
@@ -4382,8 +4340,6 @@ fn prohibiting_constructs() -> String {
|
||||
fn scan_declared_decorators(tokens: [Any]) -> Void {
|
||||
declare_prohibition("manager", "dharma_emit,dharma_field")
|
||||
let total: Int = native_list_len(tokens) / 2
|
||||
let has_pending_x: Bool = false
|
||||
let pending_exit: String = ""
|
||||
let has_pending_w: Bool = false
|
||||
let pending_wrap: String = ""
|
||||
let has_pending_p: Bool = false
|
||||
@@ -4426,10 +4382,6 @@ fn scan_declared_decorators(tokens: [Any]) -> Void {
|
||||
if str_eq(dname, "decorator") {
|
||||
if native_list_len(args) >= 2 {
|
||||
let dkind: String = native_list_get(args, 0)
|
||||
if str_eq(dkind, "injects_at_exit") {
|
||||
let has_pending_x = true
|
||||
let pending_exit = native_list_get(args, 1)
|
||||
}
|
||||
if str_eq(dkind, "wraps_body") {
|
||||
let has_pending_w = true
|
||||
let pending_wrap = native_list_get(args, 1)
|
||||
@@ -4444,10 +4396,6 @@ fn scan_declared_decorators(tokens: [Any]) -> Void {
|
||||
} else {
|
||||
if str_eq(k, "Fn") {
|
||||
let fname: String = tok_value(tokens, pos + 1)
|
||||
if has_pending_x {
|
||||
declare_exit(fname, pending_exit)
|
||||
let has_pending_x = false
|
||||
}
|
||||
if has_pending_w {
|
||||
declare_wrap(fname, pending_wrap)
|
||||
let has_pending_w = false
|
||||
|
||||
Reference in New Issue
Block a user