From 598915cc6170c0dcf6792a720ea70f763201c167 Mon Sep 17 00:00:00 2001 From: bigmerge Date: Sat, 15 Aug 2026 19:50:17 -0500 Subject: [PATCH] runtime: restore the three builtins that made elc unrebuildable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The committed elc binary could not be refreshed from its own source. Rebuilding failed with three implicit-declaration errors: el_mem_check, stdout_to_file, stdout_restore. The compiler's own source calls all three (compiler.el:472,479,574 and codegen.el:4248) and two are registered in codegen.el's builtin_arity table — but none were defined in this runtime. They were found intact in ui/examples/native-hello-ios/NativeHello/el_runtime.c, a divergent private copy of this runtime that still carried them. Ported verbatim. Consequence of them being missing: the canonical elc binary was frozen. Source gained @route dispatch codegen (emit_route_dispatch, codegen.el:3948) and the @manager boundary-beat seam, but no rebuilt binary could carry them, so neuron's soul — whose routes.el now calls the compiler-synthesized el_route_dispatch — could not be built at all. Verified after the fix: - elc rebuilds from current source, clean. - Self-hosting fixpoint byte-identical (stage3 == stage2). - The rebuilt elc emits el_route_dispatch (2 occurrences in the soul amalgam, previously 0) and injects engram_boundary_beat at @manager boundaries, i.e. the decorator seam is live rather than inert. el_mem_check is itself the compiler's memory guard (ELC_MAX_MEM_MB, default 512MB, self-terminates before the OS OOM-killer fires) — so the runtime was missing the very guard that would have surfaced the compiler's memory blowup as a clean error instead of a 27GB host-killer. --- lang/runtime/el_runtime.c | 67 +++++++++++++++++++++++++++++++++++++++ lang/runtime/el_runtime.h | 6 ++++ 2 files changed, 73 insertions(+) diff --git a/lang/runtime/el_runtime.c b/lang/runtime/el_runtime.c index 173f98e..a9ae6e3 100644 --- a/lang/runtime/el_runtime.c +++ b/lang/runtime/el_runtime.c @@ -18204,3 +18204,70 @@ el_val_t __http_do(el_val_t m, el_val_t u, el_val_t b, el_val_t h, el_val_t t) { el_val_t __http_do_map(el_val_t m, el_val_t u, el_val_t b, el_val_t h, el_val_t t) { (void)m; (void)u; (void)b; (void)h; (void)t; return _no_curl_err(); } el_val_t __http_do_map_to_file(el_val_t m, el_val_t u, el_val_t b, el_val_t h, el_val_t p) { (void)m; (void)u; (void)b; (void)h; (void)p; return _no_curl_err(); } #endif /* !HAVE_CURL */ + +/* ── Compiler-support builtins ─────────────────────────────────────────────── + * stdout_to_file / stdout_restore / el_mem_check are called by the El compiler's + * own source (compiler.el:472,479,574 and codegen.el:4248) and are registered in + * codegen.el's builtin_arity table, but were missing from this runtime — so + * rebuilding elc from source failed with three implicit-declaration errors and + * the committed elc binary could never be refreshed. The definitions below are + * ported verbatim from ui/examples/native-hello-ios/NativeHello/el_runtime.c, + * a divergent private copy of this runtime that still carried them. + * ──────────────────────────────────────────────────────────────────────────── */ + +#include + +static int _el_saved_stdout_fd = -1; + +/* Redirect process stdout to a file; used by the compiler's JS post-processing + * pipeline to capture codegen output before piping it onward. */ +el_val_t stdout_to_file(el_val_t pathv) { + const char* path = EL_CSTR(pathv); + if (!path) return (el_val_t)(int64_t)-1; + fflush(stdout); + _el_saved_stdout_fd = dup(STDOUT_FILENO); + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) return (el_val_t)(int64_t)-1; + dup2(fd, STDOUT_FILENO); + close(fd); + return (el_val_t)(int64_t)0; +} + +el_val_t stdout_restore(void) { + if (_el_saved_stdout_fd >= 0) { + fflush(stdout); + dup2(_el_saved_stdout_fd, STDOUT_FILENO); + close(_el_saved_stdout_fd); + _el_saved_stdout_fd = -1; + } + return (el_val_t)(int64_t)0; +} + +/* el_mem_check — self-terminating memory guard for long-running compiler runs. + * Called periodically by the compiler to catch runaway growth before the OS + * OOM-killer fires. Limit comes from ELC_MAX_MEM_MB (default 512 MB). + * macOS reports ru_maxrss in bytes, Linux in kilobytes; normalised to MB. */ +el_val_t el_mem_check(void) { + long limit_mb = 512; + const char* env_val = getenv("ELC_MAX_MEM_MB"); + if (env_val && *env_val) { + long v = atol(env_val); + if (v > 0) limit_mb = v; + } + + struct rusage ru; + if (getrusage(RUSAGE_SELF, &ru) != 0) return 0; /* can't read — skip check */ + + long rss_mb; +#if defined(__APPLE__) || defined(__MACH__) + rss_mb = (long)(ru.ru_maxrss / (1024L * 1024L)); +#else + rss_mb = (long)(ru.ru_maxrss / 1024L); +#endif + + if (rss_mb >= limit_mb) { + fprintf(stderr, "elc: memory limit exceeded (%ldMB), aborting\n", limit_mb); + exit(1); + } + return 0; +} diff --git a/lang/runtime/el_runtime.h b/lang/runtime/el_runtime.h index 14f1592..7a445b2 100644 --- a/lang/runtime/el_runtime.h +++ b/lang/runtime/el_runtime.h @@ -1011,6 +1011,12 @@ el_val_t __uuid_v4(void); /* Args */ el_val_t __args_json(void); +/* Compiler-support builtins — called by the El compiler's own source + * (compiler.el, codegen.el) and registered in codegen.el's builtin_arity. */ +el_val_t stdout_to_file(el_val_t path); +el_val_t stdout_restore(void); +el_val_t el_mem_check(void); + #ifdef __cplusplus } #endif