@scriptc/compiler 0.0.25 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +15 -7
  2. package/dist/backend/cc.d.ts +3 -0
  3. package/dist/backend/cc.js +37 -4
  4. package/dist/backend/cc.js.map +1 -1
  5. package/dist/backend/emission/emit-exprs.js +81 -10
  6. package/dist/backend/emission/emit-exprs.js.map +1 -1
  7. package/dist/backend/emission/emitter.d.ts +14 -1
  8. package/dist/backend/emission/emitter.js +117 -11
  9. package/dist/backend/emission/emitter.js.map +1 -1
  10. package/dist/backend/emission/may-throw.js +11 -1
  11. package/dist/backend/emission/may-throw.js.map +1 -1
  12. package/dist/backend/ffi-callbacks.d.ts +11 -0
  13. package/dist/backend/ffi-callbacks.js +33 -0
  14. package/dist/backend/ffi-callbacks.js.map +1 -0
  15. package/dist/backend/llvm/classes.js +21 -21
  16. package/dist/backend/llvm/classes.js.map +1 -1
  17. package/dist/backend/llvm/dyn.d.ts +7 -4
  18. package/dist/backend/llvm/dyn.js +145 -141
  19. package/dist/backend/llvm/dyn.js.map +1 -1
  20. package/dist/backend/llvm/emitter.d.ts +7 -1
  21. package/dist/backend/llvm/emitter.js +950 -300
  22. package/dist/backend/llvm/emitter.js.map +1 -1
  23. package/dist/backend/llvm/shapes.d.ts +4 -0
  24. package/dist/backend/llvm/shapes.js +26 -24
  25. package/dist/backend/llvm/shapes.js.map +1 -1
  26. package/dist/backend/llvm/unsupported.js +1 -1
  27. package/dist/backend/llvm/unsupported.js.map +1 -1
  28. package/dist/backend/llvm/walkers.d.ts +2 -0
  29. package/dist/backend/llvm/walkers.js +50 -46
  30. package/dist/backend/llvm/walkers.js.map +1 -1
  31. package/dist/coverage/surface-manifest.js +1 -1
  32. package/dist/coverage/surface-manifest.js.map +1 -1
  33. package/dist/diagnostics/diagnostic.d.ts +1 -1
  34. package/dist/diagnostics/diagnostic.js +3 -2
  35. package/dist/diagnostics/diagnostic.js.map +1 -1
  36. package/dist/ffi/profile.d.ts +20 -2
  37. package/dist/ffi/profile.js +130 -6
  38. package/dist/ffi/profile.js.map +1 -1
  39. package/dist/frontend/lowering/lower-calls.js +58 -22
  40. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  41. package/dist/frontend/lowering/lower-exprs.js +17 -7
  42. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  43. package/dist/frontend/lowering/lower-server.d.ts +5 -0
  44. package/dist/frontend/lowering/lower-server.js +129 -37
  45. package/dist/frontend/lowering/lower-server.js.map +1 -1
  46. package/dist/frontend/lowering/lower-stmts.js +8 -1
  47. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  48. package/dist/frontend/lowering/surfaces.d.ts +3 -2
  49. package/dist/frontend/lowering/surfaces.js +4 -3
  50. package/dist/frontend/lowering/surfaces.js.map +1 -1
  51. package/dist/frontend/npm.d.ts +16 -0
  52. package/dist/frontend/npm.js +257 -0
  53. package/dist/frontend/npm.js.map +1 -1
  54. package/dist/index.d.ts +7 -4
  55. package/dist/index.js +152 -5
  56. package/dist/index.js.map +1 -1
  57. package/dist/ir/nodes.d.ts +48 -13
  58. package/dist/ir/nodes.js +49 -7
  59. package/dist/ir/nodes.js.map +1 -1
  60. package/dist/ir/validate.js +10 -22
  61. package/dist/ir/validate.js.map +1 -1
  62. package/dist/wasi-paths.d.ts +5 -0
  63. package/dist/wasi-paths.js +22 -0
  64. package/dist/wasi-paths.js.map +1 -0
  65. package/package.json +2 -2
  66. package/surface-manifest.json +2 -2
@@ -1,5 +1,69 @@
1
- import { canMarshalFuncIntoIsland, CAUGHT, DYN, F64, islandCallbackRet, islandPromisePayloadTag, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, moduleUsesDynInvoke, moduleUsesFetch, moduleUsesFsWatch, moduleUsesHttpServer, moduleUsesNet, moduleUsesProcessEvents, moduleUsesStream, moduleUsesTls, moduleUsesTlsCa, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, typeEquals, typeKey, VOID } from "../../ir/nodes.js";
1
+ /* IR → LLVM IR text (.ll). The LLVM backend consumes the SAME in-memory
2
+ * IrModule the C backend does (never the JSON dump — see the -0 lesson in
3
+ * the survey) and produces a textual module that rides compileC's
4
+ * program-TU seat: clang compiles .ll on the exact command line that
5
+ * compiles the .c, linking the same scr_* runtime with the same C ABI.
6
+ *
7
+ * Phase 1 was the TRIVIAL TIER: f64/bool/string locals and params, the
8
+ * scalar operator set, structured control flow, direct calls, interned
9
+ * string literals, and the console protocol. Phase 2 adds the VOLUME
10
+ * TIER: module globals and locals of every in-tier ref kind (arrays,
11
+ * record shapes, unions, function values/closures), the full array and
12
+ * string intrinsic surfaces, per-record-shape RC helpers (cycle headers
13
+ * included — the C emitter's fixpoint, ported in shapes.ts), tagged-union
14
+ * construction/narrowing/equality with interned immortal unit instances,
15
+ * capture boxes and the closure calling convention, switch/for-of
16
+ * lowering, and the non-throwing slice of the libCall table. EVERYTHING
17
+ * ELSE REFUSES loudly at the first unhandled node (LlvmUnsupportedError
18
+ * naming the kind) — this backend never guesses and never emits wrong
19
+ * code for a construct it does not model. compile() surfaces the refusal
20
+ * as diagnostic SC3001.
21
+ *
22
+ * RC ownership discipline: the frame/scope release-point machinery is
23
+ * ported from CEmitter (docs/ir.md) — every refcounted temp holds an owned
24
+ * +1 reference; varDecl/assign/return/call-argument MOVE that ownership;
25
+ * each statement releases its remaining refcounted temps when it ends;
26
+ * each scope releases the refcounted locals declared in it when it exits;
27
+ * callees own their params; return/break/continue release everything the
28
+ * jump bypasses (releaseForJump). Releases are type-directed through
29
+ * shapes.ts (the releaseCallC table's LLVM twin); frame entries can be
30
+ * SLOT-based (the entry names a pointer whose CURRENT value releases —
31
+ * conditional results like optional chains need that indirection).
32
+ *
33
+ * Exceptions (phase 4): the pending-flag unwind protocol, ported from the
34
+ * C emitter. `throw` moves its payload into the runtime's exception cell
35
+ * (scr_throw_*) and unwinds; after every call that can raise (per the
36
+ * SAME computeMayThrow analysis the C backend runs) a pending check tests
37
+ * scr_exc_pending() and unwinds — releasing frames/scopes down to the
38
+ * innermost try handler's depths and branching to its label, or releasing
39
+ * everything and returning a dummy value (never read: callers of a
40
+ * may-throw function test the flag before using the result). No
41
+ * setjmp/longjmp: longjmp would skip the emitted RC releases. try/catch
42
+ * follows emit-stmts.ts's shape exactly — a compile-time tryStack entry
43
+ * per region, the catch block taking the exception (scr_exc_take into the
44
+ * binding's snapshot box, or scr_exc_clear for the bindingless form), the
45
+ * finally body emitted once per path (normal, exception-with-stash,
46
+ * pending-return) with fresh temps each time. Catch bindings ride
47
+ * ScrCaught snapshot boxes (caughtTest/caughtNarrow/caughtCheck read
48
+ * them); TDZ reads test the box's payload slot and throw Node's
49
+ * ReferenceError. main() gains the uncaught epilogue when the entry
50
+ * function may throw.
51
+ *
52
+ * The dyn surface (phase 5): ScrDyn dyn values are in the tier — dyn.ts
53
+ * ports emit-walkers.ts's dyn slice (match/check/toDyn walkers, the
54
+ * String(unknown)/caught→dyn/keyed-read singletons, the checked-dynamic
55
+ * function boundary's thunk/box/adapter triple) and the emitter lowers
56
+ * the dyn expression kinds (dynFrom/dynCall/dynInvoke/dynTest/dynKeyGet/
57
+ * dynCheck/destructuring), the JSON.parse family, dyn record fields and
58
+ * overflow maps, dyn capture boxes, and generator unknown channels.
59
+ * The island surface (jsval/jsExit and embedded npm tables) is in the
60
+ * tier too; the module text and resolution tables use the same compressed,
61
+ * lazy-inflate representation as the C debugging backend.
62
+ */
63
+ import { deflateRawSync } from "node:zlib";
64
+ import { canMarshalFuncIntoIsland, CAUGHT, DYN, F64, ffiCallbackType, islandCallbackRet, islandPromisePayloadTag, isFfiCallbackParam, isFfiContextParam, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, moduleEmbedsBuiltin, moduleEmbedsCompressedNpm, moduleUsesDynInvoke, moduleUsesFetch, moduleUsesFsWatch, moduleUsesHttpServer, moduleUsesNet, moduleUsesNodeTest, moduleUsesProcessEvents, moduleUsesStream, moduleUsesTls, moduleUsesTlsCa, NPM_COMPRESS_MIN, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, typeEquals, typeKey, VOID } from "../../ir/nodes.js";
2
65
  import { matchIntegerBytesForLoop } from "../../ir/integer-loops.js";
66
+ import { allocateFfiCallbackAdapters } from "../ffi-callbacks.js";
3
67
  import { computeMayThrow } from "../emission/may-throw.js";
4
68
  import { mangleArgPack, mangleAsyncSpawn, mangleClassNew, mangleClassObj, mangleClassRetain, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenResThunk, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordNew, mangleRecordStruct, mangleResolveThunk, mangleTrampoline, mangleVtStruct, mangleWrapper } from "../mangle.js";
5
69
  import { BlockBuilder } from "./blocks.js";
@@ -7,10 +71,41 @@ import { buildClassGraph, classFieldIndex, classStructSym, emitClassObjDefs, emi
7
71
  import { DK, LlDyn } from "./dyn.js";
8
72
  import { LlvmUnsupportedError } from "./unsupported.js";
9
73
  import { LlWalkers } from "./walkers.js";
74
+ function ffiNativeTypeLl(cls) {
75
+ switch (cls) {
76
+ case "f64":
77
+ return "double";
78
+ case "bool":
79
+ case "u8":
80
+ return "i8";
81
+ case "u32":
82
+ case "i32":
83
+ return "i32";
84
+ case "string":
85
+ case "bytes":
86
+ throw new Error(`llvm emitter bug: span class '${cls}' has no scalar LLVM type`);
87
+ case "void":
88
+ return "void";
89
+ }
90
+ }
91
+ function ffiCallbackDummyLl(callback) {
92
+ switch (callback.returns) {
93
+ case "void":
94
+ return "void";
95
+ case "f64":
96
+ return "double 0.0";
97
+ case "bool":
98
+ case "u8":
99
+ return "i8 0";
100
+ case "u32":
101
+ case "i32":
102
+ return "i32 0";
103
+ }
104
+ }
10
105
  import { arrNewCall, boxAccess, boxNewCall, computeTraced, elemAccess, emitRecordShapes, FN_ATTRS, llFieldType, mapKeyAccess, mapKeyKindNum, mapValKindNum, releaseSym, retainSym, traceAdapter, traceArg, vAdapters, } from "./shapes.js";
11
106
  export { LlvmUnsupportedError } from "./unsupported.js";
12
- export function emitLlvmModule(mod) {
13
- return new LlEmitter(mod).emit();
107
+ export function emitLlvmModule(mod, options = {}) {
108
+ return new LlEmitter(mod, options).emit();
14
109
  }
15
110
  /** Exact double literal: LLVM's 16-digit hex form round-trips every f64
16
111
  * bit pattern (−0 and the full denormal range included). */
@@ -22,8 +117,7 @@ function f64Lit(n) {
22
117
  const F64_INF = f64Lit(Infinity);
23
118
  /** LLVM c"..." payload for a UTF-8 literal, NUL-terminated like the C
24
119
  * emitter's flexible-array-member initializer. */
25
- function llStrBytes(text) {
26
- const bytes = Buffer.from(text, "utf8");
120
+ function llBytes(bytes) {
27
121
  let s = "";
28
122
  for (const b of bytes) {
29
123
  s +=
@@ -33,6 +127,9 @@ function llStrBytes(text) {
33
127
  }
34
128
  return `${s}\\00`;
35
129
  }
130
+ function llStrBytes(text) {
131
+ return llBytes(Buffer.from(text, "utf8"));
132
+ }
36
133
  /** The non-throwing libCall slice the tier claims: IrLibFn → runtime
37
134
  * symbol, called generically (LLVM arg/result types derive from the call
38
135
  * site's IR types, which is exactly the contract the C prototypes pin).
@@ -549,6 +646,9 @@ const LIB_FN_SYMS = {
549
646
  "http.resWriteHeadPairs": "scr_http_res_write_head_pairs",
550
647
  "http.resWriteHeadDyn": "scr_http_res_write_head_dyn",
551
648
  "http.serverJoinDupHeaders": "scr_http_server_join_duplicate_headers",
649
+ "http.serverTimeoutGet": "scr_net_server_timeout_get",
650
+ "http.serverTimeoutSet": "scr_net_server_timeout_set",
651
+ "http.serverTimeoutOptionSet": "scr_net_server_timeout_option_set",
552
652
  "http.clientWrite": "scr_http_client_write_str",
553
653
  "http.clientWriteBytes": "scr_http_client_write_bytes",
554
654
  "http.clientEnd": "scr_http_client_end",
@@ -771,6 +871,9 @@ const DV_SET_KIND = {
771
871
  };
772
872
  class LlEmitter {
773
873
  mod;
874
+ sizeType;
875
+ cycleColorOffset;
876
+ wasi;
774
877
  /** Interned string literals: UTF-8 text → { symbol, byte length } —
775
878
  * first-use order, the C emitter's determinism discipline. */
776
879
  literals = new Map();
@@ -811,6 +914,9 @@ class LlEmitter {
811
914
  fnByName = new Map();
812
915
  /** Manifest-bound native imports, used by ffiCall emission. */
813
916
  ffiByName = new Map();
917
+ /** C-ABI callback trampolines and (for raw/no-userdata callbacks) their
918
+ * distinct call-scoped TLS closure slots. */
919
+ ffiCallbackAdapters;
814
920
  globalTypes = new Map();
815
921
  /** May-throw analysis (the C emitter's computeMayThrow, shared): pending
816
922
  * checks are emitted only after calls that can actually raise. */
@@ -873,7 +979,7 @@ class LlEmitter {
873
979
  jumpTargets = [];
874
980
  currentLocals = new Map();
875
981
  captureIds = new Set();
876
- /** Active canonical byte-loop induction bindings: local id → i64 slot. */
982
+ /** Active canonical byte-loop induction bindings: local id → size_t slot. */
877
983
  integerLoopBindings = new Map();
878
984
  /** Enclosing try-with-FINALLY regions, innermost last: a `return`
879
985
  * inside one runs every crossed finally (innermost first) before the
@@ -894,6 +1000,9 @@ class LlEmitter {
894
1000
  /** Return type of the function being emitted — the unwind path returns
895
1001
  * a dummy of this type (never read: callers check the flag first). */
896
1002
  currentReturnType = VOID;
1003
+ /** Active only while emitting a wasm32 async body lowered with LLVM's
1004
+ * switched-coroutine intrinsics. */
1005
+ currentWasiCoro = null;
897
1006
  /** The generator channels of the function being emitted (null outside
898
1007
  * generator bodies): yieldExpr emission reads them, and emitTryCatch's
899
1008
  * catch prologue emits the GENRET sentinel re-unwind exactly here. */
@@ -901,12 +1010,20 @@ class LlEmitter {
901
1010
  /** Active optional-chain bind slots, by chain id (chainRecv reads). */
902
1011
  chainSlots = new Map();
903
1012
  logArgSlots = 0;
904
- constructor(mod) {
1013
+ constructor(mod, options) {
905
1014
  this.mod = mod;
1015
+ this.sizeType = options.pointerBits === 32 ? "i32" : "i64";
1016
+ this.wasi = options.wasi === true;
1017
+ // ScrCycHdr is { ptr trace; ptr free; i32 color; i16 buffered;
1018
+ // i16 gen; size_t buf_index }. The object follows it, so color is 12
1019
+ // bytes behind a wasm32 object and 16 bytes behind a 64-bit object.
1020
+ this.cycleColorOffset = options.pointerBits === 32 ? 12 : 16;
1021
+ this.ffiCallbackAdapters = allocateFfiCallbackAdapters(mod.ffiImports ?? []);
906
1022
  for (const fn of mod.functions)
907
1023
  this.fnByName.set(fn.name, fn);
908
- for (const entry of mod.ffiImports ?? [])
1024
+ for (const entry of mod.ffiImports ?? []) {
909
1025
  this.ffiByName.set(entry.name, entry);
1026
+ }
910
1027
  const mt = computeMayThrow(mod);
911
1028
  this.mayThrow = mt.fns;
912
1029
  this.indirectMayThrow = mt.indirect;
@@ -978,9 +1095,9 @@ class LlEmitter {
978
1095
  lib: rec.lib,
979
1096
  });
980
1097
  }
981
- if (mod.embedded !== undefined && mod.embedded.modules.length > 0) {
982
- throw new LlvmUnsupportedError("npmEmbedding");
983
- }
1098
+ }
1099
+ abiOffset(native64, wasm32) {
1100
+ return this.sizeType === "i32" ? wasm32 : native64;
984
1101
  }
985
1102
  // ── types ───────────────────────────────────────────────────────────────
986
1103
  llType(t) {
@@ -1037,6 +1154,93 @@ class LlEmitter {
1037
1154
  }
1038
1155
  }
1039
1156
  // ── module assembly ─────────────────────────────────────────────────────
1157
+ ffiCallbackAdapter(binding, id) {
1158
+ const adapter = this.ffiCallbackAdapters.get(`${binding}:${id}`);
1159
+ if (!adapter)
1160
+ throw new Error(`llvm emitter bug: no callback adapter for ${binding}:${id}`);
1161
+ return adapter;
1162
+ }
1163
+ /** C-callable scalar callback trampolines. A callback with an explicit
1164
+ * context entry receives the closure at that exact ABI position. A raw
1165
+ * callback loads it from a call-scoped TLS slot installed around the
1166
+ * outer native call. */
1167
+ emitFfiCallbackDefs() {
1168
+ const globals = [];
1169
+ const defs = [];
1170
+ if (this.ffiCallbackAdapters.size === 0)
1171
+ return { globals, defs };
1172
+ this.declare(`declare zeroext i1 @scr_exc_pending()`);
1173
+ this.declare(`declare void @scr_trap(ptr)`);
1174
+ const expired = this.cstr("scriptc: native callback invoked outside its call-scoped lifetime\n");
1175
+ for (const adapter of this.ffiCallbackAdapters.values()) {
1176
+ const cb = adapter.callback;
1177
+ if (adapter.tls !== null)
1178
+ globals.push(`@${adapter.tls} = internal thread_local global ptr null`);
1179
+ const params = cb.params.map((param, i) => isFfiContextParam(param) ? `ptr %ctx` : `${ffiNativeTypeLl(param)} %a${i}`);
1180
+ const ret = ffiNativeTypeLl(cb.returns);
1181
+ defs.push(`define internal ${ret} @${adapter.symbol}(${params.join(", ")}) ${FN_ATTRS} {`, `entry:`, adapter.tls === null ? ` %cb = getelementptr inbounds i8, ptr %ctx, i64 0` : ` %cb = load ptr, ptr @${adapter.tls}`, ` %missing = icmp eq ptr %cb, null`, ` br i1 %missing, label %expired, label %ready`, `expired:`, ` call void @scr_trap(ptr ${expired})`, ` unreachable`, `ready:`, ` %pending = call zeroext i1 @scr_exc_pending()`, ` br i1 %pending, label %skip, label %invoke`, `skip:`, ` ret ${ffiCallbackDummyLl(cb)}`, `invoke:`, ` %fnp = getelementptr inbounds %ScrClosure, ptr %cb, i64 0, i32 1`, ` %fn = load ptr, ptr %fnp`);
1182
+ const scriptArgs = [];
1183
+ for (let i = 0; i < cb.params.length; i++) {
1184
+ const param = cb.params[i];
1185
+ if (isFfiContextParam(param))
1186
+ continue;
1187
+ switch (param) {
1188
+ case "f64":
1189
+ scriptArgs.push(`double %a${i}`);
1190
+ break;
1191
+ case "bool":
1192
+ defs.push(` %s${i} = icmp ne i8 %a${i}, 0`);
1193
+ scriptArgs.push(`i1 %s${i}`);
1194
+ break;
1195
+ case "u8":
1196
+ defs.push(` %s${i} = uitofp i8 %a${i} to double`);
1197
+ scriptArgs.push(`double %s${i}`);
1198
+ break;
1199
+ case "u32":
1200
+ defs.push(` %s${i} = uitofp i32 %a${i} to double`);
1201
+ scriptArgs.push(`double %s${i}`);
1202
+ break;
1203
+ case "i32":
1204
+ defs.push(` %s${i} = sitofp i32 %a${i} to double`);
1205
+ scriptArgs.push(`double %s${i}`);
1206
+ break;
1207
+ }
1208
+ }
1209
+ const ft = ffiCallbackType(cb);
1210
+ const internalRet = this.llType(ft.ret);
1211
+ const callArgs = [`ptr %cb`, ...scriptArgs].join(", ");
1212
+ if (cb.returns === "void") {
1213
+ defs.push(` call void %fn(${callArgs})`, ` ret void`, `}`, ``);
1214
+ continue;
1215
+ }
1216
+ defs.push(` %result = call ${internalRet} %fn(${callArgs})`);
1217
+ switch (cb.returns) {
1218
+ case "f64":
1219
+ defs.push(` ret double %result`);
1220
+ break;
1221
+ case "bool":
1222
+ defs.push(` %out = zext i1 %result to i8`, ` ret i8 %out`);
1223
+ break;
1224
+ case "u8":
1225
+ case "u32":
1226
+ this.declare(`declare double @scr_bit_ushr(double, double)`);
1227
+ defs.push(` %coerced = call double @scr_bit_ushr(double %result, double ${f64Lit(0)})`, ` %wide = fptoui double %coerced to i32`);
1228
+ if (cb.returns === "u8") {
1229
+ defs.push(` %out = trunc i32 %wide to i8`, ` ret i8 %out`);
1230
+ }
1231
+ else {
1232
+ defs.push(` ret i32 %wide`);
1233
+ }
1234
+ break;
1235
+ case "i32":
1236
+ this.declare(`declare double @scr_bit_or(double, double)`);
1237
+ defs.push(` %coerced = call double @scr_bit_or(double %result, double ${f64Lit(0)})`, ` %out = fptosi double %coerced to i32`, ` ret i32 %out`);
1238
+ break;
1239
+ }
1240
+ defs.push(`}`, ``);
1241
+ }
1242
+ return { globals, defs };
1243
+ }
1040
1244
  emit() {
1041
1245
  // Function bodies first (the literal/unit/fn-value tables fill as they
1042
1246
  // emit), then the file assembles around them — the C emitter's order.
@@ -1048,6 +1252,24 @@ class LlEmitter {
1048
1252
  const classObjDefs = emitClassObjDefs(this, this.classMeta, this.classObjs, this.fnByName, (t) => this.llType(t));
1049
1253
  const wrappers = this.emitFnValueDefs();
1050
1254
  const asyncDefs = this.emitAsyncScaffolding();
1255
+ const ffiCallbacks = this.emitFfiCallbackDefs();
1256
+ const embedded = this.mod.embedded;
1257
+ const usesIsland = embedded !== undefined && embedded.modules.length > 0;
1258
+ const storeNpmText = (text) => {
1259
+ const plain = Buffer.from(text, "utf8");
1260
+ if (text.length < NPM_COMPRESS_MIN)
1261
+ return { bytes: plain, raw: 0 };
1262
+ const deflated = deflateRawSync(plain, { level: 9 });
1263
+ return deflated.length < plain.length
1264
+ ? { bytes: deflated, raw: plain.length }
1265
+ : { bytes: plain, raw: 0 };
1266
+ };
1267
+ const npmStored = usesIsland
1268
+ ? embedded.modules.map((m) => ({
1269
+ src: storeNpmText(m.source),
1270
+ esm: m.esm === undefined ? null : storeNpmText(m.esm),
1271
+ }))
1272
+ : [];
1051
1273
  // Module globals, FIRST OCCURRENCE per id: a class-expression static
1052
1274
  // instantiated through several mixin applications registers one global
1053
1275
  // id several times (2041-mixin-values) — C's tentative definitions
@@ -1107,8 +1329,20 @@ class LlEmitter {
1107
1329
  // any island entry (the engine's lazy boot consults it) — cc.ts
1108
1330
  // compiles scr_fetch.c on the same predicate.
1109
1331
  const usesFetch = moduleUsesFetch(this.mod);
1110
- const snapshotsTlsCa = moduleUsesTls(this.mod) || moduleUsesTlsCa(this.mod);
1332
+ const embedsZlib = moduleEmbedsBuiltin(this.mod, "node:zlib");
1333
+ const embedsNet = moduleEmbedsBuiltin(this.mod, "node:http") ||
1334
+ moduleEmbedsBuiltin(this.mod, "node:https") ||
1335
+ moduleEmbedsBuiltin(this.mod, "node:net") ||
1336
+ moduleEmbedsBuiltin(this.mod, "node:tls");
1337
+ const snapshotsTlsCa = moduleUsesTls(this.mod) || moduleUsesTlsCa(this.mod) ||
1338
+ moduleEmbedsBuiltin(this.mod, "node:https") ||
1339
+ moduleEmbedsBuiltin(this.mod, "node:tls");
1111
1340
  const hasRefGlobals = globals.some((g) => isRefCounted(g.type)) || fnValueProps.length > 0;
1341
+ // The process verdict has the same precedence as the C reference
1342
+ // emitter: node:test owns the final status when present; otherwise an
1343
+ // embedded process.exitCode owns it; ordinary programs return zero.
1344
+ const usesNodeTest = moduleUsesNodeTest(this.mod);
1345
+ const programExitUsesIsland = !usesNodeTest && usesIsland;
1112
1346
  // Declared NOW — the extern block flushes before main assembles.
1113
1347
  if (usesEvents)
1114
1348
  this.declare(`declare void @scr_events_install()`);
@@ -1124,6 +1358,20 @@ class LlEmitter {
1124
1358
  this.declare(`declare void @scr_http_dyn_install()`);
1125
1359
  if (usesFetch)
1126
1360
  this.declare(`declare void @scr_fetch_install()`);
1361
+ if (embedsZlib)
1362
+ this.declare(`declare void @scr_zlib_island_install()`);
1363
+ if (embedsNet)
1364
+ this.declare(`declare void @scr_net_island_install()`);
1365
+ if (usesIsland) {
1366
+ this.declare(`declare void @scr_island_modules(ptr, ${this.sizeType}, ptr, ${this.sizeType})`);
1367
+ this.declare(`declare i32 @scr_island_exit_code()`);
1368
+ if (moduleEmbedsCompressedNpm(this.mod)) {
1369
+ this.declare(`declare void @scr_island_set_inflate(ptr)`);
1370
+ this.declare(`declare zeroext i1 @scr_zlib_inflate_exact(ptr, ${this.sizeType}, ptr, ${this.sizeType})`);
1371
+ }
1372
+ }
1373
+ if (usesNodeTest)
1374
+ this.declare(`declare i32 @scr_test_exit_code()`);
1127
1375
  if (snapshotsTlsCa) {
1128
1376
  this.declare(`declare void @scr_tls_ca_install()`);
1129
1377
  }
@@ -1145,11 +1393,12 @@ class LlEmitter {
1145
1393
  const entryMayThrow = this.mayThrow.has(this.mod.entry);
1146
1394
  // The event loop runs when timers appeared OR any async/generator
1147
1395
  // function exists (the C main's hasAsync || hasGenerators ||
1148
- // usesTimers gate; the island stays refused). Generator programs run
1396
+ // usesTimers gate). Generator programs run
1149
1397
  // the loop too: its exit accounting notes still-suspended generator
1150
1398
  // fibers as abandoned, so the RC audit downgrades exactly like the
1151
1399
  // async loop-exhaustion story.
1152
1400
  const runsLoop = this.usesTimers ||
1401
+ usesIsland ||
1153
1402
  this.mod.functions.some((f) => f.async === true || f.generator !== undefined);
1154
1403
  const uncaughtReleases = entryMayThrow && !asyncEntry ? globalReleaseLines("gu") : [];
1155
1404
  const loopReleasesU = runsLoop ? globalReleaseLines("gl") : [];
@@ -1171,7 +1420,40 @@ class LlEmitter {
1171
1420
  this.declare(`declare i32 @scr_promise_finish_top_level(ptr)`);
1172
1421
  this.declare(`declare void @scr_promise_rethrow_top_level(ptr)`);
1173
1422
  this.declare(`declare void @scr_promise_release(ptr)`);
1423
+ this.declare(`declare void @scr_exit_code_note(i32)`);
1424
+ if (programExitUsesIsland && usesEvents && hasRefGlobals) {
1425
+ this.declare(`declare ${this.sizeType} @scr_island_exit_code_version()`);
1426
+ }
1174
1427
  }
1428
+ const topPendingExitLines = () => {
1429
+ if (!asyncEntry)
1430
+ return [];
1431
+ const lines = [];
1432
+ if (usesNodeTest) {
1433
+ lines.push(` %tla_program_exit = call i32 @scr_test_exit_code()`);
1434
+ }
1435
+ else if (usesIsland) {
1436
+ lines.push(` %tla_program_exit = call i32 @scr_island_exit_code()`);
1437
+ }
1438
+ if (usesNodeTest || usesIsland) {
1439
+ lines.push(` %tla_program_exit_zero = icmp eq i32 %tla_program_exit, 0`, ` %tla_exit_status = select i1 %tla_program_exit_zero, i32 %tla_status, i32 %tla_program_exit`);
1440
+ }
1441
+ const exitStatus = usesNodeTest || usesIsland ? "%tla_exit_status" : "%tla_status";
1442
+ const tracksIslandExit = programExitUsesIsland && usesEvents && hasRefGlobals;
1443
+ if (tracksIslandExit) {
1444
+ lines.push(` %tla_exit_version = call ${this.sizeType} @scr_island_exit_code_version()`);
1445
+ }
1446
+ // finish_top_level initially notes 13. Replace that hint before exit
1447
+ // listeners run when a higher-priority verdict was already selected.
1448
+ lines.push(` call void @scr_exit_code_note(i32 ${exitStatus})`);
1449
+ lines.push(...exitListenerLines("xp"));
1450
+ if (tracksIslandExit) {
1451
+ lines.push(` %tla_exit_version_after = call ${this.sizeType} @scr_island_exit_code_version()`, ` %tla_exit_changed = icmp ne ${this.sizeType} %tla_exit_version_after, %tla_exit_version`, ` br i1 %tla_exit_changed, label %tla_exit_updated, label %tla_exit_unchanged`, `tla_exit_updated:`, ` %tla_listener_exit = call i32 @scr_island_exit_code()`, ` call void @scr_exit_code_note(i32 %tla_listener_exit)`, ` br label %tla_exit_done`, `tla_exit_unchanged:`, ` br label %tla_exit_done`, `tla_exit_done:`, ` %tla_final_exit = phi i32 [ %tla_listener_exit, %tla_exit_updated ], [ ${exitStatus}, %tla_exit_unchanged ]`);
1452
+ }
1453
+ lines.push(...topPendingReleases);
1454
+ lines.push(` ret i32 ${tracksIslandExit ? "%tla_final_exit" : exitStatus}`);
1455
+ return lines;
1456
+ };
1175
1457
  // LIBRARY mode: the runtime entry points the generated library
1176
1458
  // symbols delegate to — declared before the extern block flushes.
1177
1459
  if (this.mod.lib !== undefined) {
@@ -1182,10 +1464,10 @@ class LlEmitter {
1182
1464
  this.declare(`declare void @scr_library_arena_reset()`);
1183
1465
  this.declare(`declare void @scr_library_collect()`);
1184
1466
  if (this.mod.lib.exports.some((e) => e.params.includes("string"))) {
1185
- this.declare(`declare ptr @scr_library_str_in(ptr, i64)`);
1467
+ this.declare(`declare ptr @scr_library_str_in(ptr, ${this.sizeType})`);
1186
1468
  }
1187
1469
  if (this.mod.lib.exports.some((e) => e.params.includes("bytes"))) {
1188
- this.declare(`declare ptr @scr_library_bytes_in(ptr, i64, ptr)`);
1470
+ this.declare(`declare ptr @scr_library_bytes_in(ptr, ${this.sizeType}, ptr)`);
1189
1471
  }
1190
1472
  if (this.mod.lib.exports.some((e) => e.params.includes("i64"))) {
1191
1473
  this.declare(`declare double @scr_library_i64_in(i64, ptr)`);
@@ -1212,47 +1494,49 @@ class LlEmitter {
1212
1494
  // the C layout (4 bytes padding after the tag). ScrUnion/ScrClosure
1213
1495
  // mirror scr_runtime.h field-for-field (tag reads, slot peeks, the
1214
1496
  // fn pointer, and the caps[] tail all address through them).
1215
- `%ScrStr = type { i64, i64, i64 }`,
1497
+ `%ScrStr = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType} }`,
1216
1498
  `%ScrLogArg = type { i32, i64 }`,
1217
- `%ScrVt = type { i64, i64, ptr }`,
1218
- `%ScrUnion = type { i64, i32, ptr, ptr, ptr, i64 }`,
1219
- `%ScrClosure = type { i64, ptr, i64, ptr }`,
1220
- `%ScrRegex = type { i64, ptr, ptr, ptr }`,
1499
+ `%ScrVt = type { ${this.sizeType}, ${this.sizeType}, ptr }`,
1500
+ `%ScrUnion = type { ${this.sizeType}, i32, ptr, ptr, ptr, i64 }`,
1501
+ `%ScrClosure = type { ${this.sizeType}, ptr, ${this.sizeType}, ptr }`,
1502
+ `%ScrRegex = type { ${this.sizeType}, ptr, ptr, ptr }`,
1221
1503
  // ScrArr mirror { rc, len, cap, elem(i32+pad), elem_retain,
1222
1504
  // elem_release, elem_trace, data } — the immortal tagged-template
1223
1505
  // strings objects lay out through it (nothing GEPs into live heap
1224
1506
  // arrays; those stay behind the runtime's own entry points).
1225
- `%ScrArr = type { i64, i64, i64, i32, ptr, ptr, ptr, ptr }`,
1507
+ `%ScrArr = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, ptr, ptr }`,
1226
1508
  // The runtime error prefix { rc, vt, name, message, code } and the
1227
1509
  // class-object shape { rc, pre, post, ctor, name } — field reads on
1228
1510
  // builtin errors and classval loads GEP through these.
1229
- `%ScrError = type { i64, ptr, ptr, ptr, ptr }`,
1230
- `%ScrClassObj = type { i64, i64, i64, ptr, ptr }`,
1511
+ `%ScrError = type { ${this.sizeType}, ptr, ptr, ptr, ptr }`,
1512
+ `%ScrClassObj = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, ptr, ptr }`,
1231
1513
  // The runtime emitter prefix { rc, vt, reg, cls } — user subclasses
1232
1514
  // embed it (classes.ts), and bare-emitter GEPs address through it.
1233
- `%ScrEmitter = type { i64, ptr, ptr, ptr }`,
1515
+ `%ScrEmitter = type { ${this.sizeType}, ptr, ptr, ptr }`,
1234
1516
  // The runtime stream layout { rc, vt, reg, cls, st } — one struct
1235
1517
  // for all five stream classes; stream subclasses embed it.
1236
- `%ScrStream = type { i64, ptr, ptr, ptr, ptr }`,
1518
+ `%ScrStream = type { ${this.sizeType}, ptr, ptr, ptr, ptr }`,
1237
1519
  // The catch-binding snapshot box { rc, kind, f64, b, payload,
1238
1520
  // retain_fn, release_fn, trace_fn } — caughtTest kind reads and
1239
1521
  // caughtNarrow payload extraction GEP through it (offsets match
1240
1522
  // scr_runtime.h's natural alignment: kind at 8, f64 at 16, b at 24,
1241
1523
  // payload at 32).
1242
- `%ScrCaught = type { i64, i32, double, i8, ptr, ptr, ptr, ptr }`,
1524
+ `%ScrCaught = type { ${this.sizeType}, i32, double, i8, ptr, ptr, ptr, ptr }`,
1243
1525
  // ScrBytes { rc, len, elem(i32+pad), data, backing }. Indexed
1244
1526
  // typed-array access GEPs through this directly: the IR type already
1245
1527
  // fixes elem, so the hot path needs neither a runtime kind load nor
1246
1528
  // the generic scr_bytes_get/set call.
1247
- `%ScrBytes = type { i64, i64, i32, ptr, ptr }`,
1529
+ `%ScrBytes = type { ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr }`,
1248
1530
  // The capture box { rc, kind, obj_retain, obj_release, obj_trace,
1249
1531
  // slot } — TDZ reads peek the payload slot (offset 40) directly.
1250
- `%ScrBox = type { i64, i32, ptr, ptr, ptr, i64 }`,
1532
+ `%ScrBox = type { ${this.sizeType}, i32, ptr, ptr, ptr, i64 }`,
1251
1533
  // The stack buffer of the emitted JSON serializers { data, len, cap }.
1252
- `%ScrJsonBuf = type { ptr, i64, i64, ptr, i64, i64 }`,
1534
+ `%ScrJsonBuf = type { ptr, ${this.sizeType}, ${this.sizeType}, ptr, ${this.sizeType}, ${this.sizeType} }`,
1253
1535
  // The dynCheck error-path spine { parent, key, index } — the emitted
1254
1536
  // builders stack-allocate one per recursion level (dyn.ts).
1255
- `%ScrDynPath = type { ptr, ptr, i64 }`,
1537
+ `%ScrDynPath = type { ptr, ptr, ${this.sizeType} }`,
1538
+ `%ScrIslandModule = type { ptr, ptr, ${this.sizeType}, ${this.sizeType}, i32, ptr, ${this.sizeType}, ${this.sizeType} }`,
1539
+ `%ScrIslandEdge = type { ptr, ptr, ptr, i32 }`,
1256
1540
  ];
1257
1541
  out.push(...shapes.typeDefs);
1258
1542
  out.push(...classShapes.typeDefs);
@@ -1263,8 +1547,8 @@ class LlEmitter {
1263
1547
  for (const [text, lit] of this.literals) {
1264
1548
  // Immortal interned ScrStr: { rc = SIZE_MAX, len, cap = len, bytes\0 } —
1265
1549
  // the C emitter's static table, retain/release skip rc == SIZE_MAX.
1266
- out.push(`@${lit.sym} = internal global { i64, i64, i64, [${lit.len + 1} x i8] } ` +
1267
- `{ i64 -1, i64 ${lit.len}, i64 ${lit.len}, [${lit.len + 1} x i8] c"${llStrBytes(text)}" }`);
1550
+ out.push(`@${lit.sym} = internal global { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, [${lit.len + 1} x i8] } ` +
1551
+ `{ ${this.sizeType} -1, ${this.sizeType} ${lit.len}, ${this.sizeType} ${lit.len}, [${lit.len + 1} x i8] c"${llStrBytes(text)}" }`);
1268
1552
  }
1269
1553
  if (this.literals.size > 0)
1270
1554
  out.push(``);
@@ -1273,7 +1557,7 @@ class LlEmitter {
1273
1557
  // slot and RC entry points zero — retain/release/collector all skip
1274
1558
  // rc == SIZE_MAX, so these never join the RC audit or a trace walk.
1275
1559
  const [unionId, tag] = key.split(":");
1276
- out.push(`@${sym} = internal global %ScrUnion { i64 -1, i32 ${tag}, ptr null, ptr null, ptr null, i64 0 } ; ${unionId} unit arm`);
1560
+ out.push(`@${sym} = internal global %ScrUnion { ${this.sizeType} -1, i32 ${tag}, ptr null, ptr null, ptr null, i64 0 } ; ${unionId} unit arm`);
1277
1561
  }
1278
1562
  if (this.unitInstances.size > 0)
1279
1563
  out.push(``);
@@ -1281,7 +1565,7 @@ class LlEmitter {
1281
1565
  // One immortal ScrRegex per (pattern, flags) literal, pointing at
1282
1566
  // the interned source/flags strings. The bc slot starts null (lazy
1283
1567
  // compile, cached by the runtime) — a mutable global, not constant.
1284
- out.push(`@${re.sym} = internal global %ScrRegex { i64 -1, ptr ${re.src}, ptr ${re.fl}, ptr null } ; ${key.replace(/\n/g, "\\n")}`);
1568
+ out.push(`@${re.sym} = internal global %ScrRegex { ${this.sizeType} -1, ptr ${re.src}, ptr ${re.fl}, ptr null } ; ${key.replace(/\n/g, "\\n")}`);
1285
1569
  }
1286
1570
  if (this.regexInstances.size > 0)
1287
1571
  out.push(``);
@@ -1291,7 +1575,7 @@ class LlEmitter {
1291
1575
  // over it (rc == SIZE_MAX, len == cap, SCR_ELEM_STR = 2, no REF
1292
1576
  // entry points). Reads retain immortal strings — a no-op.
1293
1577
  const n = inst.slots.length;
1294
- out.push(`@${inst.sym}_data = internal constant [${n} x ptr] [ ${inst.slots.map((s) => `ptr ${s}`).join(", ")} ]`, `@${inst.sym} = internal global %ScrArr { i64 -1, i64 ${n}, i64 ${n}, i32 2, ptr null, ptr null, ptr null, ptr @${inst.sym}_data }`);
1578
+ out.push(`@${inst.sym}_data = internal constant [${n} x ptr] [ ${inst.slots.map((s) => `ptr ${s}`).join(", ")} ]`, `@${inst.sym} = internal global %ScrArr { ${this.sizeType} -1, ${this.sizeType} ${n}, ${this.sizeType} ${n}, i32 2, ptr null, ptr null, ptr null, ptr @${inst.sym}_data }`);
1295
1579
  }
1296
1580
  if (this.templateStringsInstances.size > 0)
1297
1581
  out.push(``);
@@ -1302,6 +1586,43 @@ class LlEmitter {
1302
1586
  }
1303
1587
  if (this.cstrs.size > 0)
1304
1588
  out.push(``);
1589
+ if (usesIsland) {
1590
+ const fmt = { esm: 0, cjs: 1, json: 2 };
1591
+ const edgeKind = { any: 0, import: 1, require: 2 };
1592
+ embedded.modules.forEach((m, i) => {
1593
+ const stored = npmStored[i];
1594
+ const key = Buffer.from(m.key, "utf8");
1595
+ out.push(`@sc_npm_key_${i} = internal constant [${key.length + 1} x i8] c"${llBytes(key)}"`, `@sc_npm_src_${i} = internal constant [${stored.src.bytes.length + 1} x i8] c"${llBytes(stored.src.bytes)}"`);
1596
+ if (stored.esm !== null) {
1597
+ out.push(`@sc_npm_esm_${i} = internal constant [${stored.esm.bytes.length + 1} x i8] c"${llBytes(stored.esm.bytes)}"`);
1598
+ }
1599
+ });
1600
+ const moduleRows = embedded.modules.map((m, i) => {
1601
+ const stored = npmStored[i];
1602
+ const esm = stored.esm === null
1603
+ ? `ptr null, ${this.sizeType} 0, ${this.sizeType} 0`
1604
+ : `ptr @sc_npm_esm_${i}, ${this.sizeType} ${stored.esm.bytes.length}, ${this.sizeType} ${stored.esm.raw}`;
1605
+ return `%ScrIslandModule { ptr @sc_npm_key_${i}, ptr @sc_npm_src_${i}, ` +
1606
+ `${this.sizeType} ${stored.src.bytes.length}, ${this.sizeType} ${stored.src.raw}, ` +
1607
+ `i32 ${fmt[m.format]}, ${esm} }`;
1608
+ });
1609
+ out.push(`@sc_npm_modules = internal constant [${moduleRows.length} x %ScrIslandModule] [ ${moduleRows.join(", ")} ]`);
1610
+ embedded.edges.forEach((edge, i) => {
1611
+ for (const [part, text] of [["from", edge.from], ["spec", edge.specifier], ["to", edge.to]]) {
1612
+ const bytes = Buffer.from(text, "utf8");
1613
+ out.push(`@sc_npm_edge_${i}_${part} = internal constant [${bytes.length + 1} x i8] c"${llBytes(bytes)}"`);
1614
+ }
1615
+ });
1616
+ if (embedded.edges.length > 0) {
1617
+ const edgeRows = embedded.edges.map((edge, i) => `%ScrIslandEdge { ptr @sc_npm_edge_${i}_from, ptr @sc_npm_edge_${i}_spec, ` +
1618
+ `ptr @sc_npm_edge_${i}_to, i32 ${edgeKind[edge.kind]} }`);
1619
+ out.push(`@sc_npm_edges = internal constant [${edgeRows.length} x %ScrIslandEdge] [ ${edgeRows.join(", ")} ]`);
1620
+ }
1621
+ out.push(``);
1622
+ }
1623
+ out.push(...ffiCallbacks.globals);
1624
+ if (ffiCallbacks.globals.length > 0)
1625
+ out.push(``);
1305
1626
  for (const g of globals) {
1306
1627
  const ty = this.llType(g.type);
1307
1628
  const zero = ty === "double" ? f64Lit(0) : ty === "ptr" ? "null" : "false";
@@ -1310,6 +1631,7 @@ class LlEmitter {
1310
1631
  if (globals.length > 0)
1311
1632
  out.push(``);
1312
1633
  out.push(...helpers);
1634
+ out.push(...ffiCallbacks.defs);
1313
1635
  out.push(...shapes.defs);
1314
1636
  out.push(...classShapes.defs);
1315
1637
  out.push(...classObjDefs);
@@ -1327,7 +1649,7 @@ class LlEmitter {
1327
1649
  const stamps = [];
1328
1650
  for (const iv of this.errorIntervals) {
1329
1651
  for (const [field, value] of [[0, iv.pre], [1, iv.post]]) {
1330
- stamps.push(` store i64 ${value}, ptr getelementptr inbounds ([5 x %ScrVt], ptr @scr_error_vts, i64 0, i64 ${iv.kind}, i32 ${field})${field === 1 ? ` ; ${iv.lib}` : ""}`);
1652
+ stamps.push(` store ${this.sizeType} ${value}, ptr getelementptr inbounds ([5 x %ScrVt], ptr @scr_error_vts, i64 0, i64 ${iv.kind}, i32 ${field})${field === 1 ? ` ; ${iv.lib}` : ""}`);
1331
1653
  }
1332
1654
  }
1333
1655
  if (this.emitterInterval !== null) {
@@ -1335,14 +1657,14 @@ class LlEmitter {
1335
1657
  // EventEmitter instances answer instanceof and dispatch dynamic
1336
1658
  // teardown under THIS module's preorder numbering.
1337
1659
  out.push(`@scr_emitter_vt = external global %ScrVt`, ``);
1338
- stamps.push(` store i64 ${this.emitterInterval.pre}, ptr getelementptr inbounds (%ScrVt, ptr @scr_emitter_vt, i64 0, i32 0)`, ` store i64 ${this.emitterInterval.post}, ptr getelementptr inbounds (%ScrVt, ptr @scr_emitter_vt, i64 0, i32 1) ; EventEmitter`);
1660
+ stamps.push(` store ${this.sizeType} ${this.emitterInterval.pre}, ptr getelementptr inbounds (%ScrVt, ptr @scr_emitter_vt, i64 0, i32 0)`, ` store ${this.sizeType} ${this.emitterInterval.post}, ptr getelementptr inbounds (%ScrVt, ptr @scr_emitter_vt, i64 0, i32 1) ; EventEmitter`);
1339
1661
  }
1340
1662
  for (const iv of this.streamIntervals) {
1341
1663
  // The runtime stream vtables' intervals (streamVtStampLines) — the
1342
1664
  // emitter story: instanceof and dynamic teardown dispatch through
1343
1665
  // them.
1344
1666
  out.push(`@${iv.vt} = external global %ScrVt`);
1345
- stamps.push(` store i64 ${iv.pre}, ptr getelementptr inbounds (%ScrVt, ptr @${iv.vt}, i64 0, i32 0)`, ` store i64 ${iv.post}, ptr getelementptr inbounds (%ScrVt, ptr @${iv.vt}, i64 0, i32 1) ; ${iv.lib}`);
1667
+ stamps.push(` store ${this.sizeType} ${iv.pre}, ptr getelementptr inbounds (%ScrVt, ptr @${iv.vt}, i64 0, i32 0)`, ` store ${this.sizeType} ${iv.post}, ptr getelementptr inbounds (%ScrVt, ptr @${iv.vt}, i64 0, i32 1) ; ${iv.lib}`);
1346
1668
  }
1347
1669
  if (this.streamIntervals.length > 0)
1348
1670
  out.push(``);
@@ -1366,17 +1688,28 @@ class LlEmitter {
1366
1688
  // LIBRARY mode: no @main — the profile-declared external
1367
1689
  // symbols instead, from the same IR facts the C emission consumes.
1368
1690
  out.push(...this.emitLibDefs(globals, globalReleaseLines, stamps));
1369
- out.push(`attributes #0 = { sanitize_address }`, ``);
1691
+ out.push(`attributes #0 = { sanitize_address }`);
1692
+ if (this.wasi)
1693
+ out.push(`attributes #1 = { sanitize_address presplitcoroutine }`);
1694
+ out.push(``);
1370
1695
  return out.join("\n");
1371
1696
  }
1372
- out.push(`define i32 @main(i32 %argc, ptr %argv) ${FN_ATTRS} {`, `entry:`, ` call void @scr_init()`, ...stamps,
1697
+ out.push(`define i32 @${this.wasi ? "__main_argc_argv" : "main"}(i32 %argc, ptr %argv) ${FN_ATTRS} {`, `entry:`, ` call void @scr_init()`, ...stamps,
1373
1698
  // Event-surface programs (signal/exit listeners) fill the loop's
1374
1699
  // nullable event hooks before %main — scr_events.c links only when
1375
1700
  // this line is emitted (cc.ts gates on the same predicate).
1376
1701
  ...(usesEvents ? [` call void @scr_events_install()`] : []),
1377
1702
  // fs.watch programs fill the loop's watch hooks the same way —
1378
1703
  // scr_watch.c links only when this line is emitted.
1379
- ...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(snapshotsTlsCa ? [` call void @scr_tls_ca_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(usesNet ? [` call void @scr_net_install()`, ` call void @scr_net_dyn_install()`] : []), ...(usesHttp ? [` call void @scr_http_dyn_install()`] : []), ...(usesStream ? [` call void @scr_stream_install()`] : []), ` call void @scr_lib_init(i32 %argc, ptr %argv)`, ...(asyncEntry
1704
+ ...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(snapshotsTlsCa ? [` call void @scr_tls_ca_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(embedsZlib ? [` call void @scr_zlib_island_install()`] : []), ...(embedsNet ? [` call void @scr_net_island_install()`] : []), ...(usesNet ? [` call void @scr_net_install()`, ` call void @scr_net_dyn_install()`] : []), ...(usesHttp ? [` call void @scr_http_dyn_install()`] : []), ...(usesStream ? [` call void @scr_stream_install()`] : []), ` call void @scr_lib_init(i32 %argc, ptr %argv)`, ...(usesIsland
1705
+ ? [
1706
+ ...(moduleEmbedsCompressedNpm(this.mod)
1707
+ ? [` call void @scr_island_set_inflate(ptr @scr_zlib_inflate_exact)`]
1708
+ : []),
1709
+ ` call void @scr_island_modules(ptr @sc_npm_modules, ${this.sizeType} ${embedded.modules.length}, ` +
1710
+ `ptr ${embedded.edges.length > 0 ? "@sc_npm_edges" : "null"}, ${this.sizeType} ${embedded.edges.length})`,
1711
+ ]
1712
+ : []), ...(asyncEntry
1380
1713
  ? [` %top = call ptr @${mangleAsyncSpawn(this.mod.entry)}()`]
1381
1714
  : [` call void @${mangleFunction(this.mod.entry)}()`]),
1382
1715
  // Uncaught exception from top-level code: Node exits 1.
@@ -1447,18 +1780,20 @@ class LlEmitter {
1447
1780
  ` %tla_pending = icmp eq i32 %tla_status, 13`,
1448
1781
  ` br i1 %tla_pending, label %tla_stuck, label %tla_ok`,
1449
1782
  `tla_stuck:`,
1450
- ...exitListenerLines("xp"),
1451
- ...topPendingReleases,
1452
- ` ret i32 13`,
1783
+ ...topPendingExitLines(),
1453
1784
  `tla_ok:`,
1454
1785
  ]
1455
1786
  : []),
1456
1787
  ]
1457
- : []), ...exitListenerLines("xn"), ...globalReleases, ` ret i32 0`, `}`, ``,
1788
+ : []), ...exitListenerLines("xn"), ...globalReleases, ...(usesNodeTest
1789
+ ? [` %test_exit = call i32 @scr_test_exit_code()`, ` ret i32 %test_exit`]
1790
+ : usesIsland
1791
+ ? [` %island_exit = call i32 @scr_island_exit_code()`, ` ret i32 %island_exit`]
1792
+ : [` ret i32 0`]), `}`, ``,
1458
1793
  // sanitize_address is inert under the plain pipeline; the sanitized
1459
1794
  // lane's -fsanitize=address link activates instrumentation over the
1460
1795
  // emitted functions too (the runtime TUs get theirs from clang).
1461
- `attributes #0 = { sanitize_address }`, ``);
1796
+ `attributes #0 = { sanitize_address }`, ...(this.wasi ? [`attributes #1 = { sanitize_address presplitcoroutine }`] : []), ``);
1462
1797
  return out.join("\n");
1463
1798
  }
1464
1799
  /** LIBRARY mode: the profile-declared external definitions — the
@@ -1510,7 +1845,7 @@ class LlEmitter {
1510
1845
  });
1511
1846
  out.push(ovlCells.length === 0
1512
1847
  ? `@scr_library_trap_overlays = constant [1 x ptr] zeroinitializer`
1513
- : `@scr_library_trap_overlays = constant [${ovlCells.length} x ptr] [${ovlCells.join(", ")}]`, `@scr_library_trap_overlays_len = constant i64 ${lib.trapOverlays.length}`, ``);
1848
+ : `@scr_library_trap_overlays = constant [${ovlCells.length} x ptr] [${ovlCells.join(", ")}]`, `@scr_library_trap_overlays_len = constant ${this.sizeType} ${lib.trapOverlays.length}`, ``);
1514
1849
  // The init entry: full deterministic reset-and-reevaluate. Program
1515
1850
  // globals release and zero first (run-once guards included), then the
1516
1851
  // runtime session reset, the error-vt interval stamps verbatim from
@@ -1596,13 +1931,13 @@ class LlEmitter {
1596
1931
  args.push(`double %c${i}`);
1597
1932
  break;
1598
1933
  case "string":
1599
- params.push(`ptr %a${i}_ptr`, `i64 %a${i}_len`);
1600
- body.push(` %c${i} = call ptr @scr_library_str_in(ptr %a${i}_ptr, i64 %a${i}_len)`);
1934
+ params.push(`ptr %a${i}_ptr`, `${this.sizeType} %a${i}_len`);
1935
+ body.push(` %c${i} = call ptr @scr_library_str_in(ptr %a${i}_ptr, ${this.sizeType} %a${i}_len)`);
1601
1936
  args.push(`ptr %c${i}`);
1602
1937
  break;
1603
1938
  case "bytes":
1604
- params.push(`ptr %a${i}_ptr`, `i64 %a${i}_len`);
1605
- body.push(` %c${i} = call ptr @scr_library_bytes_in(ptr %a${i}_ptr, i64 %a${i}_len, ptr @sc_lib_bytes_trap_${e.symbol})`);
1939
+ params.push(`ptr %a${i}_ptr`, `${this.sizeType} %a${i}_len`);
1940
+ body.push(` %c${i} = call ptr @scr_library_bytes_in(ptr %a${i}_ptr, ${this.sizeType} %a${i}_len, ptr @sc_lib_bytes_trap_${e.symbol})`);
1606
1941
  args.push(`ptr %c${i}`);
1607
1942
  break;
1608
1943
  }
@@ -1676,7 +2011,7 @@ class LlEmitter {
1676
2011
  // scr_box_retain is a static inline (increment-unless-immortal, then
1677
2012
  // mark the cycle header live — every box is collector-headered);
1678
2013
  // emitted once with internal linkage, like the record retains.
1679
- defs.push(`define internal ptr @sc_retain_box(ptr %b) ${FN_ATTRS} {`, `entry:`, ` %rc = load i64, ptr %b`, ` %imm = icmp eq i64 %rc, -1`, ` br i1 %imm, label %done, label %inc`, `inc:`, ` %n = add i64 %rc, 1`, ` store i64 %n, ptr %b`, ` %colorp = getelementptr i8, ptr %b, i64 -16`, ` store i32 0, ptr %colorp ; mark live`, ` br label %done`, `done:`, ` ret ptr %b`, `}`, ``);
2014
+ defs.push(`define internal ptr @sc_retain_box(ptr %b) ${FN_ATTRS} {`, `entry:`, ` %rc = load ${this.sizeType}, ptr %b`, ` %imm = icmp eq ${this.sizeType} %rc, -1`, ` br i1 %imm, label %done, label %inc`, `inc:`, ` %n = add ${this.sizeType} %rc, 1`, ` store ${this.sizeType} %n, ptr %b`, ` %colorp = getelementptr i8, ptr %b, ${this.sizeType} -${this.cycleColorOffset}`, ` store i32 0, ptr %colorp ; mark live`, ` br label %done`, `done:`, ` ret ptr %b`, `}`, ``);
1680
2015
  }
1681
2016
  // The declarations these helpers added must land in the extern block,
1682
2017
  // which already flushed — append here instead (LLVM is order-free).
@@ -1696,7 +2031,7 @@ class LlEmitter {
1696
2031
  // never the inner return type.
1697
2032
  const ret = fn.async === true || fn.generator !== undefined ? "ptr" : this.llType(fn.returnType);
1698
2033
  const call = `call ${ret} @${this.callTarget(name)}(${args})`;
1699
- out.push(`define internal ${ret} @${mangleWrapper(name)}(ptr %env${params.length ? ", " + params.join(", ") : ""}) ${FN_ATTRS} { ; ${name} as a value`, `entry:`, ret === "void" ? ` ${call}` : ` %r = ${call}`, ret === "void" ? ` ret void` : ` ret ${ret} %r`, `}`, `@${mangleFnClosure(name)} = internal global %ScrClosure { i64 -1, ptr @${mangleWrapper(name)}, i64 0, ptr null }`, ``);
2034
+ out.push(`define internal ${ret} @${mangleWrapper(name)}(ptr %env${params.length ? ", " + params.join(", ") : ""}) ${FN_ATTRS} { ; ${name} as a value`, `entry:`, ret === "void" ? ` ${call}` : ` %r = ${call}`, ret === "void" ? ` ret void` : ` ret ${ret} %r`, `}`, `@${mangleFnClosure(name)} = internal global %ScrClosure { ${this.sizeType} -1, ptr @${mangleWrapper(name)}, ${this.sizeType} 0, ptr null }`, ``);
1700
2035
  }
1701
2036
  return out;
1702
2037
  }
@@ -1709,6 +2044,11 @@ class LlEmitter {
1709
2044
  * fiber eagerly to its first suspension and returns the promise). */
1710
2045
  emitAsyncScaffolding() {
1711
2046
  const out = [];
2047
+ if (this.wasi) {
2048
+ this.declare(`declare void @llvm.coro.resume(ptr)`);
2049
+ this.declare(`declare void @llvm.coro.destroy(ptr)`);
2050
+ out.push(`define void @scr_wasi_coro_resume(ptr %handle) ${FN_ATTRS} {`, `entry:`, ` call void @llvm.coro.resume(ptr %handle)`, ` ret void`, `}`, `define void @scr_wasi_coro_destroy(ptr %handle) ${FN_ATTRS} {`, `entry:`, ` call void @llvm.coro.destroy(ptr %handle)`, ` ret void`, `}`, ``);
2051
+ }
1712
2052
  for (const fn of this.mod.functions) {
1713
2053
  if (fn.async !== true)
1714
2054
  continue;
@@ -1716,9 +2056,9 @@ class LlEmitter {
1716
2056
  const lifted = fn.captures !== undefined;
1717
2057
  const fieldTys = [...(lifted ? ["ptr"] : []), ...fn.params.map((p) => this.llType(p.type))];
1718
2058
  out.push(`%${pack} = type { ${fieldTys.join(", ") || "i8"} } ; ${fn.name} args`);
1719
- const sizeOf = `ptrtoint (ptr getelementptr (%${pack}, ptr null, i32 1) to i64)`;
2059
+ const sizeOf = `ptrtoint (ptr getelementptr (%${pack}, ptr null, i32 1) to ${this.sizeType})`;
1720
2060
  this.declare(`declare void @free(ptr)`);
1721
- this.declare(`declare ptr @malloc(i64)`);
2061
+ this.declare(`declare ptr @malloc(${this.sizeType})`);
1722
2062
  this.declare(`declare zeroext i1 @scr_exc_pending()`);
1723
2063
  this.declare(`declare ptr @scr_fiber_promise(ptr)`);
1724
2064
  this.declare(`declare ptr @scr_async_spawn(ptr, ptr)`);
@@ -1738,41 +2078,49 @@ class LlEmitter {
1738
2078
  const retTy = this.llType(ret);
1739
2079
  const bodyCall = `call ${retTy} @${mangleFunction(fn.name)}(${loads.join(", ")})`;
1740
2080
  tr.push(retTy === "void" ? ` ${bodyCall}` : ` %r = ${bodyCall}`);
1741
- if (lifted) {
1742
- this.declare(`declare void @scr_closure_release(ptr)`);
1743
- tr.push(` call void @scr_closure_release(ptr %a0)`);
2081
+ if (this.wasi) {
2082
+ // The coroutine body settles its own promise and owns the retained
2083
+ // lifted environment until final suspension. Its initial call
2084
+ // returns here at the first suspend (or final suspend).
2085
+ tr.push(` ret void`, `}`, ``);
1744
2086
  }
1745
- tr.push(` %pend = call zeroext i1 @scr_exc_pending()`, ` br i1 %pend, label %thrown, label %clean`, `clean:`, ` %pr = call ptr @scr_fiber_promise(ptr %self)`);
1746
- switch (ret.kind) {
1747
- case "void":
1748
- this.declare(`declare void @scr_promise_fulfill_void(ptr)`);
1749
- tr.push(` call void @scr_promise_fulfill_void(ptr %pr)`);
1750
- break;
1751
- case "f64":
1752
- case "date":
1753
- this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
1754
- tr.push(` call void @scr_promise_fulfill_f64(ptr %pr, double %r)`);
1755
- break;
1756
- case "bool":
1757
- this.declare(`declare void @scr_promise_fulfill_bool(ptr, i1 zeroext)`);
1758
- tr.push(` call void @scr_promise_fulfill_bool(ptr %pr, i1 %r)`);
1759
- break;
1760
- case "string":
1761
- this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
1762
- tr.push(` call void @scr_promise_fulfill_str(ptr %pr, ptr %r) ; moves in`);
1763
- break;
1764
- default: {
1765
- const v = vAdapters(this, ret);
1766
- this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
1767
- tr.push(` call void @scr_promise_fulfill_ref(ptr %pr, ptr %r, ptr ${v.retain}, ptr ${v.release}, ptr ${traceArg(this, ret)})`);
2087
+ else {
2088
+ if (lifted) {
2089
+ this.declare(`declare void @scr_closure_release(ptr)`);
2090
+ tr.push(` call void @scr_closure_release(ptr %a0)`);
1768
2091
  }
2092
+ tr.push(` %pend = call zeroext i1 @scr_exc_pending()`, ` br i1 %pend, label %thrown, label %clean`, `clean:`, ` %pr = call ptr @scr_fiber_promise(ptr %self)`);
2093
+ switch (ret.kind) {
2094
+ case "void":
2095
+ this.declare(`declare void @scr_promise_fulfill_void(ptr)`);
2096
+ tr.push(` call void @scr_promise_fulfill_void(ptr %pr)`);
2097
+ break;
2098
+ case "f64":
2099
+ case "date":
2100
+ this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
2101
+ tr.push(` call void @scr_promise_fulfill_f64(ptr %pr, double %r)`);
2102
+ break;
2103
+ case "bool":
2104
+ this.declare(`declare void @scr_promise_fulfill_bool(ptr, i1 zeroext)`);
2105
+ tr.push(` call void @scr_promise_fulfill_bool(ptr %pr, i1 %r)`);
2106
+ break;
2107
+ case "string":
2108
+ this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
2109
+ tr.push(` call void @scr_promise_fulfill_str(ptr %pr, ptr %r) ; moves in`);
2110
+ break;
2111
+ default: {
2112
+ const v = vAdapters(this, ret);
2113
+ this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
2114
+ tr.push(` call void @scr_promise_fulfill_ref(ptr %pr, ptr %r, ptr ${v.retain}, ptr ${v.release}, ptr ${traceArg(this, ret)})`);
2115
+ }
2116
+ }
2117
+ tr.push(` ret void`, `thrown:`);
2118
+ if (ret.kind !== "void" && isRefCounted(ret)) {
2119
+ // An escaping throw means %r is the never-read dummy (NULL).
2120
+ tr.push(` call void ${releaseSym(this, ret)}(ptr %r)`);
2121
+ }
2122
+ tr.push(` ret void`, `}`, ``);
1769
2123
  }
1770
- tr.push(` ret void`, `thrown:`);
1771
- if (ret.kind !== "void" && isRefCounted(ret)) {
1772
- // An escaping throw means %r is the never-read dummy (NULL).
1773
- tr.push(` call void ${releaseSym(this, ret)}(ptr %r)`);
1774
- }
1775
- tr.push(` ret void`, `}`, ``);
1776
2124
  out.push(...tr);
1777
2125
  // Spawn wrapper: pack the args (+1 moves in), spawn the fiber.
1778
2126
  const params = fieldTys.map((ty, i) => `${ty} %a${i}`);
@@ -1799,7 +2147,7 @@ class LlEmitter {
1799
2147
  `cache_miss:`,
1800
2148
  ]
1801
2149
  : []),
1802
- ` %ap = call ptr @malloc(i64 ${sizeOf})`,
2150
+ ` %ap = call ptr @malloc(${this.sizeType} ${sizeOf})`,
1803
2151
  ` %isnull = icmp eq ptr %ap, null`,
1804
2152
  ` br i1 %isnull, label %oom, label %ok`,
1805
2153
  `oom:`,
@@ -1867,9 +2215,9 @@ class LlEmitter {
1867
2215
  const lifted = fn.captures !== undefined;
1868
2216
  const fieldTys = [...(lifted ? ["ptr"] : []), ...fn.params.map((p) => this.llType(p.type))];
1869
2217
  out.push(`%${pack} = type { ${fieldTys.join(", ") || "i8"} } ; ${fn.name} args`);
1870
- const sizeOf = `ptrtoint (ptr getelementptr (%${pack}, ptr null, i32 1) to i64)`;
2218
+ const sizeOf = `ptrtoint (ptr getelementptr (%${pack}, ptr null, i32 1) to ${this.sizeType})`;
1871
2219
  this.declare(`declare void @free(ptr)`);
1872
- this.declare(`declare ptr @malloc(i64)`);
2220
+ this.declare(`declare ptr @malloc(${this.sizeType})`);
1873
2221
  this.declare(`declare zeroext i1 @scr_exc_pending()`);
1874
2222
  this.declare(`declare zeroext i1 @scr_exc_genret_pending()`);
1875
2223
  this.declare(`declare void @scr_exc_clear()`);
@@ -1891,7 +2239,7 @@ class LlEmitter {
1891
2239
  const retTy = this.llType(ret);
1892
2240
  const bodyCall = `call ${retTy} @${mangleFunction(fn.name)}(${loads.join(", ")})`;
1893
2241
  tr.push(retTy === "void" ? ` ${bodyCall}` : ` %r = ${bodyCall}`);
1894
- if (lifted) {
2242
+ if (lifted && !this.wasi) {
1895
2243
  this.declare(`declare void @scr_closure_release(ptr)`);
1896
2244
  tr.push(` call void @scr_closure_release(ptr %a0)`);
1897
2245
  }
@@ -1899,31 +2247,36 @@ class LlEmitter {
1899
2247
  // with the NONE slot — JS's undefined done-value. A GENRET unwind
1900
2248
  // consumes the sentinel and promotes the parked .return value; a
1901
2249
  // real exception stays pending (the consumer-side resume moves it).
1902
- tr.push(` %g = call ptr @scr_gen_of_fiber(ptr %self)`, ` %pend = call zeroext i1 @scr_exc_pending()`, ` br i1 %pend, label %thrown, label %clean`, `clean:`);
1903
- switch (ret.kind) {
1904
- case "void":
1905
- tr.push(` br label %done ; void body: the done value is undefined (NONE)`);
1906
- break;
1907
- case "f64":
1908
- case "date":
1909
- this.declare(`declare void @scr_gen_out_f64(ptr, double)`);
1910
- tr.push(` call void @scr_gen_out_f64(ptr %g, double %r)`, ` br label %done`);
1911
- break;
1912
- case "bool":
1913
- this.declare(`declare void @scr_gen_out_bool(ptr, i1 zeroext)`);
1914
- tr.push(` call void @scr_gen_out_bool(ptr %g, i1 %r)`, ` br label %done`);
1915
- break;
1916
- default: {
1917
- const v = vAdapters(this, ret);
1918
- this.declare(`declare void @scr_gen_out_ref(ptr, ptr, ptr)`);
1919
- tr.push(` call void @scr_gen_out_ref(ptr %g, ptr %r, ptr ${v.release})`, ` br label %done`);
1920
- }
2250
+ if (this.wasi) {
2251
+ tr.push(` ret void`, `}`, ``);
1921
2252
  }
1922
- tr.push(`thrown:`, ` %genret = call zeroext i1 @scr_exc_genret_pending()`, ` br i1 %genret, label %promote, label %dropdummy`, `promote:`, ` call void @scr_exc_clear()`, ` call void @scr_gen_ret_to_out(ptr %g)`, ` br label %dropdummy`, `dropdummy:`);
1923
- if (ret.kind !== "void" && isRefCounted(ret)) {
1924
- tr.push(` call void ${releaseSym(this, ret)}(ptr %r) ; unwound: the never-read dummy`);
2253
+ else {
2254
+ tr.push(` %g = call ptr @scr_gen_of_fiber(ptr %self)`, ` %pend = call zeroext i1 @scr_exc_pending()`, ` br i1 %pend, label %thrown, label %clean`, `clean:`);
2255
+ switch (ret.kind) {
2256
+ case "void":
2257
+ tr.push(` br label %done ; void body: the done value is undefined (NONE)`);
2258
+ break;
2259
+ case "f64":
2260
+ case "date":
2261
+ this.declare(`declare void @scr_gen_out_f64(ptr, double)`);
2262
+ tr.push(` call void @scr_gen_out_f64(ptr %g, double %r)`, ` br label %done`);
2263
+ break;
2264
+ case "bool":
2265
+ this.declare(`declare void @scr_gen_out_bool(ptr, i1 zeroext)`);
2266
+ tr.push(` call void @scr_gen_out_bool(ptr %g, i1 %r)`, ` br label %done`);
2267
+ break;
2268
+ default: {
2269
+ const v = vAdapters(this, ret);
2270
+ this.declare(`declare void @scr_gen_out_ref(ptr, ptr, ptr)`);
2271
+ tr.push(` call void @scr_gen_out_ref(ptr %g, ptr %r, ptr ${v.release})`, ` br label %done`);
2272
+ }
2273
+ }
2274
+ tr.push(`thrown:`, ` %genret = call zeroext i1 @scr_exc_genret_pending()`, ` br i1 %genret, label %promote, label %dropdummy`, `promote:`, ` call void @scr_exc_clear()`, ` call void @scr_gen_ret_to_out(ptr %g)`, ` br label %dropdummy`, `dropdummy:`);
2275
+ if (ret.kind !== "void" && isRefCounted(ret)) {
2276
+ tr.push(` call void ${releaseSym(this, ret)}(ptr %r) ; unwound: the never-read dummy`);
2277
+ }
2278
+ tr.push(` br label %done`, `done:`, ` ret void`, `}`, ``);
1925
2279
  }
1926
- tr.push(` br label %done`, `done:`, ` ret void`, `}`, ``);
1927
2280
  out.push(...tr);
1928
2281
  // The never-started teardown: drop the packed (+1) arguments.
1929
2282
  const dr = [
@@ -1952,7 +2305,7 @@ class LlEmitter {
1952
2305
  const sp = [
1953
2306
  `define internal ptr @${mangleGenSpawn(fn.name)}(${params.join(", ")}) ${FN_ATTRS} { ; gen spawn ${fn.name}`,
1954
2307
  `entry:`,
1955
- ` %ap = call ptr @malloc(i64 ${sizeOf})`,
2308
+ ` %ap = call ptr @malloc(${this.sizeType} ${sizeOf})`,
1956
2309
  ` %isnull = icmp eq ptr %ap, null`,
1957
2310
  ` br i1 %isnull, label %oom, label %ok`,
1958
2311
  `oom:`,
@@ -2124,6 +2477,10 @@ class LlEmitter {
2124
2477
  return;
2125
2478
  }
2126
2479
  this.releaseForJump(0, 0);
2480
+ if (this.currentWasiCoro !== null) {
2481
+ this.B.terminate(`br label %${this.currentWasiCoro.finalLabel}`);
2482
+ return;
2483
+ }
2127
2484
  const t = this.currentReturnType;
2128
2485
  if (t.kind === "void")
2129
2486
  this.B.terminate("ret void");
@@ -2207,8 +2564,8 @@ class LlEmitter {
2207
2564
  const len = B.tmp();
2208
2565
  const t = B.tmp();
2209
2566
  B.line(`${lenp} = getelementptr inbounds %ScrStr, ptr ${v.name}, i64 0, i32 1`);
2210
- B.line(`${len} = load i64, ptr ${lenp}`);
2211
- B.line(`${t} = icmp ne i64 ${len}, 0`);
2567
+ B.line(`${len} = load ${this.sizeType}, ptr ${lenp}`);
2568
+ B.line(`${t} = icmp ne ${this.sizeType} ${len}, 0`);
2212
2569
  return t;
2213
2570
  }
2214
2571
  case "date":
@@ -2303,8 +2660,8 @@ class LlEmitter {
2303
2660
  const len = B.tmp();
2304
2661
  const t = B.tmp();
2305
2662
  B.line(`${lenp} = getelementptr inbounds %ScrStr, ptr ${p}, i64 0, i32 1`);
2306
- B.line(`${len} = load i64, ptr ${lenp}`);
2307
- B.line(`${t} = icmp ne i64 ${len}, 0`);
2663
+ B.line(`${len} = load ${this.sizeType}, ptr ${lenp}`);
2664
+ B.line(`${t} = icmp ne ${this.sizeType} ${len}, 0`);
2308
2665
  B.line(`store i1 ${t}, ptr ${slot}`);
2309
2666
  break;
2310
2667
  }
@@ -2459,7 +2816,7 @@ class LlEmitter {
2459
2816
  B.line(`${vtp} = getelementptr inbounds %${classStructSym(staticClassName)}, ptr ${objName}, i64 0, i32 1`);
2460
2817
  B.line(`${vt} = load ptr, ptr ${vtp}`);
2461
2818
  B.line(`${prep} = getelementptr inbounds %ScrVt, ptr ${vt}, i64 0, i32 0`);
2462
- B.line(`${pre} = load i64, ptr ${prep}`);
2819
+ B.line(`${pre} = load ${this.sizeType}, ptr ${prep}`);
2463
2820
  return pre;
2464
2821
  }
2465
2822
  /** The class object's static symbol (classes as values), registering it
@@ -2652,6 +3009,93 @@ class LlEmitter {
2652
3009
  return mangleGenSpawn(fnName);
2653
3010
  return mangleFunction(fnName);
2654
3011
  }
3012
+ /** Queue the current wasm coroutine and suspend it. The runtime decides
3013
+ * whether the promise waiter list or the ready FIFO owns the continuation;
3014
+ * LLVM keeps all live locals in the coroutine frame. */
3015
+ emitWasiSuspend(promise) {
3016
+ const coro = this.currentWasiCoro;
3017
+ if (coro === null)
3018
+ throw new Error("llvm emitter bug: wasm suspension outside async body");
3019
+ if (promise === null) {
3020
+ this.declare(`declare void @scr_wasi_await_hop_prepare(ptr)`);
3021
+ this.B.line(`call void @scr_wasi_await_hop_prepare(ptr ${coro.self})`);
3022
+ }
3023
+ else {
3024
+ this.declare(`declare void @scr_wasi_await_prepare(ptr, ptr)`);
3025
+ this.B.line(`call void @scr_wasi_await_prepare(ptr ${coro.self}, ptr ${promise})`);
3026
+ }
3027
+ this.emitWasiSuspendPrepared();
3028
+ }
3029
+ /** Suspend after a target-specific runtime helper already queued the
3030
+ * continuation (used by module awaits, which skip the hop when settled). */
3031
+ emitWasiSuspendPrepared() {
3032
+ const coro = this.currentWasiCoro;
3033
+ if (coro === null)
3034
+ throw new Error("llvm emitter bug: wasm suspension outside async body");
3035
+ const save = this.B.tmp();
3036
+ const state = this.B.tmp();
3037
+ const resume = this.B.newLabel("coro.resume");
3038
+ this.B.line(`${save} = call token @llvm.coro.save(ptr ${coro.handle})`);
3039
+ this.B.line(`${state} = call i8 @llvm.coro.suspend(token ${save}, i1 false)`);
3040
+ this.B.terminate(`switch i8 ${state}, label %${coro.suspendLabel} [ i8 0, label %${resume} i8 1, label %${coro.cleanupLabel} ]`);
3041
+ this.B.startBlock(resume);
3042
+ }
3043
+ /** Move a clean async return value into the current fiber's promise. */
3044
+ emitWasiFulfill(v) {
3045
+ const coro = this.currentWasiCoro;
3046
+ if (coro === null)
3047
+ throw new Error("llvm emitter bug: wasm fulfillment outside async body");
3048
+ const ret = this.currentReturnType;
3049
+ if (coro.kind === "generator") {
3050
+ this.declare(`declare ptr @scr_gen_of_fiber(ptr)`);
3051
+ const gen = this.B.tmp();
3052
+ this.B.line(`${gen} = call ptr @scr_gen_of_fiber(ptr ${coro.self})`);
3053
+ switch (ret.kind) {
3054
+ case "void":
3055
+ return;
3056
+ case "f64":
3057
+ case "date":
3058
+ this.declare(`declare void @scr_gen_out_f64(ptr, double)`);
3059
+ this.B.line(`call void @scr_gen_out_f64(ptr ${gen}, double ${v.name})`);
3060
+ return;
3061
+ case "bool":
3062
+ this.declare(`declare void @scr_gen_out_bool(ptr, i1 zeroext)`);
3063
+ this.B.line(`call void @scr_gen_out_bool(ptr ${gen}, i1 ${v.name})`);
3064
+ return;
3065
+ default:
3066
+ this.declare(`declare void @scr_gen_out_ref(ptr, ptr, ptr)`);
3067
+ this.B.line(`call void @scr_gen_out_ref(ptr ${gen}, ptr ${v.name}, ptr ${vAdapters(this, ret).release})`);
3068
+ return;
3069
+ }
3070
+ }
3071
+ this.declare(`declare ptr @scr_fiber_promise(ptr)`);
3072
+ const pr = this.B.tmp();
3073
+ this.B.line(`${pr} = call ptr @scr_fiber_promise(ptr ${coro.self})`);
3074
+ switch (ret.kind) {
3075
+ case "void":
3076
+ this.declare(`declare void @scr_promise_fulfill_void(ptr)`);
3077
+ this.B.line(`call void @scr_promise_fulfill_void(ptr ${pr})`);
3078
+ break;
3079
+ case "f64":
3080
+ case "date":
3081
+ this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
3082
+ this.B.line(`call void @scr_promise_fulfill_f64(ptr ${pr}, double ${v.name})`);
3083
+ break;
3084
+ case "bool":
3085
+ this.declare(`declare void @scr_promise_fulfill_bool(ptr, i1 zeroext)`);
3086
+ this.B.line(`call void @scr_promise_fulfill_bool(ptr ${pr}, i1 ${v.name})`);
3087
+ break;
3088
+ case "string":
3089
+ this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
3090
+ this.B.line(`call void @scr_promise_fulfill_str(ptr ${pr}, ptr ${v.name}) ; moves in`);
3091
+ break;
3092
+ default: {
3093
+ const rc = vAdapters(this, ret);
3094
+ this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
3095
+ this.B.line(`call void @scr_promise_fulfill_ref(ptr ${pr}, ptr ${v.name}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${traceArg(this, ret)})`);
3096
+ }
3097
+ }
3098
+ }
2655
3099
  emitFunction(fn) {
2656
3100
  const B = new BlockBuilder();
2657
3101
  this.B = B;
@@ -2666,7 +3110,41 @@ class LlEmitter {
2666
3110
  this.tryStack = [];
2667
3111
  this.currentReturnType = fn.returnType;
2668
3112
  this.currentGenerator = fn.generator ?? null;
3113
+ this.currentWasiCoro = null;
2669
3114
  this.logArgSlots = 0;
3115
+ if (this.wasi && (fn.async === true || fn.generator !== undefined)) {
3116
+ this.declare(`declare token @llvm.coro.id(i32, ptr, ptr, ptr)`);
3117
+ this.declare(`declare ${this.sizeType} @llvm.coro.size.${this.sizeType}()`);
3118
+ this.declare(`declare ptr @llvm.coro.begin(token, ptr)`);
3119
+ this.declare(`declare token @llvm.coro.save(ptr)`);
3120
+ this.declare(`declare i8 @llvm.coro.suspend(token, i1)`);
3121
+ this.declare(`declare ptr @llvm.coro.free(token, ptr)`);
3122
+ this.declare(`declare i1 @llvm.coro.end(ptr, i1, token)`);
3123
+ this.declare(`declare ptr @malloc(${this.sizeType})`);
3124
+ this.declare(`declare void @free(ptr)`);
3125
+ this.declare(`declare void @scr_wasi_coro_started(ptr)`);
3126
+ this.declare(`declare ptr @scr_fiber_self()`);
3127
+ const id = B.tmp();
3128
+ const size = B.tmp();
3129
+ const mem = B.tmp();
3130
+ const handle = B.tmp();
3131
+ const self = B.tmp();
3132
+ B.line(`${id} = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)`);
3133
+ B.line(`${size} = call ${this.sizeType} @llvm.coro.size.${this.sizeType}()`);
3134
+ B.line(`${mem} = call ptr @malloc(${this.sizeType} ${size})`);
3135
+ B.line(`${handle} = call ptr @llvm.coro.begin(token ${id}, ptr ${mem})`);
3136
+ B.line(`call void @scr_wasi_coro_started(ptr ${handle})`);
3137
+ B.line(`${self} = call ptr @scr_fiber_self()`);
3138
+ this.currentWasiCoro = {
3139
+ kind: fn.async === true ? "async" : "generator",
3140
+ id,
3141
+ handle,
3142
+ self,
3143
+ finalLabel: B.newLabel("coro.final"),
3144
+ cleanupLabel: B.newLabel("coro.cleanup"),
3145
+ suspendLabel: B.newLabel("coro.suspend"),
3146
+ };
3147
+ }
2670
3148
  const paramIds = new Set(fn.params.map((p) => p.localId));
2671
3149
  for (const local of fn.locals) {
2672
3150
  // Boxed locals' slots hold their capture BOX (a ptr); captured
@@ -2688,9 +3166,11 @@ class LlEmitter {
2688
3166
  // Captured bindings come in through the environment — borrowed for the
2689
3167
  // whole call (the closure owns them): bound here, never released here.
2690
3168
  (fn.captures ?? []).forEach((c, i) => {
3169
+ const caps = B.tmp();
2691
3170
  const p = B.tmp();
2692
3171
  const box = B.tmp();
2693
- B.line(`${p} = getelementptr inbounds i8, ptr %sc_env, i64 ${32 + 8 * i} ; caps[${i}]`);
3172
+ B.line(`${caps} = getelementptr inbounds %ScrClosure, ptr %sc_env, i64 1 ; caps`);
3173
+ B.line(`${p} = getelementptr inbounds ptr, ptr ${caps}, ${this.sizeType} ${i} ; caps[${i}]`);
2694
3174
  B.line(`${box} = load ptr, ptr ${p}`);
2695
3175
  B.line(`store ptr ${box}, ptr %${mangleLocal(c.localId)} ; captured ${c.name}`);
2696
3176
  });
@@ -2720,9 +3200,51 @@ class LlEmitter {
2720
3200
  // whose unwind released everything down to depth 0).
2721
3201
  if (fn.returnType.kind === "void" && !B.isTerminated()) {
2722
3202
  this.releaseScope(this.scopes[0]);
2723
- B.terminate("ret void");
3203
+ if (this.currentWasiCoro !== null) {
3204
+ this.emitWasiFulfill(null);
3205
+ B.terminate(`br label %${this.currentWasiCoro.finalLabel}`);
3206
+ }
3207
+ else {
3208
+ B.terminate("ret void");
3209
+ }
2724
3210
  }
2725
3211
  this.scopes.pop();
3212
+ const coro = this.currentWasiCoro;
3213
+ if (coro !== null) {
3214
+ B.startBlock(coro.finalLabel);
3215
+ if (fn.captures !== undefined) {
3216
+ this.declare(`declare void @scr_closure_release(ptr)`);
3217
+ B.line(`call void @scr_closure_release(ptr %sc_env)`);
3218
+ }
3219
+ if (coro.kind === "generator") {
3220
+ this.declare(`declare void @scr_wasi_gen_finish(ptr)`);
3221
+ B.line(`call void @scr_wasi_gen_finish(ptr ${coro.self})`);
3222
+ }
3223
+ else {
3224
+ this.declare(`declare void @scr_wasi_async_finish(ptr)`);
3225
+ B.line(`call void @scr_wasi_async_finish(ptr ${coro.self})`);
3226
+ }
3227
+ const finalState = B.tmp();
3228
+ B.line(`${finalState} = call i8 @llvm.coro.suspend(token none, i1 true)`);
3229
+ B.terminate(`switch i8 ${finalState}, label %${coro.suspendLabel} [ i8 1, label %${coro.cleanupLabel} ]`);
3230
+ B.startBlock(coro.cleanupLabel);
3231
+ const frame = B.tmp();
3232
+ B.line(`${frame} = call ptr @llvm.coro.free(token ${coro.id}, ptr ${coro.handle})`);
3233
+ B.line(`call void @free(ptr ${frame})`);
3234
+ B.br(coro.suspendLabel);
3235
+ B.startBlock(coro.suspendLabel);
3236
+ const ended = B.tmp();
3237
+ B.line(`${ended} = call i1 @llvm.coro.end(ptr ${coro.handle}, i1 false, token none)`);
3238
+ const ret = this.llType(fn.returnType);
3239
+ if (ret === "void")
3240
+ B.terminate(`ret void`);
3241
+ else if (ret === "double")
3242
+ B.terminate(`ret double ${f64Lit(0)}`);
3243
+ else if (ret === "i1")
3244
+ B.terminate(`ret i1 false`);
3245
+ else
3246
+ B.terminate(`ret ptr null`);
3247
+ }
2726
3248
  if (this.logArgSlots > 0) {
2727
3249
  B.entryAllocas.push(`%logargs = alloca [${this.logArgSlots} x %ScrLogArg]`);
2728
3250
  }
@@ -2731,7 +3253,8 @@ class LlEmitter {
2731
3253
  if (fn.captures !== undefined)
2732
3254
  params.unshift("ptr %sc_env");
2733
3255
  const ret = this.llType(fn.returnType);
2734
- return `define internal ${ret} @${mangleFunction(fn.name)}(${params.join(", ")}) ${FN_ATTRS} { ; ${fn.name}\n${B.render()}\n}`;
3256
+ const attrs = coro !== null ? "#1" : FN_ATTRS;
3257
+ return `define internal ${ret} @${mangleFunction(fn.name)}(${params.join(", ")}) ${attrs} { ; ${fn.name}\n${B.render()}\n}`;
2735
3258
  }
2736
3259
  // ── statements ──────────────────────────────────────────────────────────
2737
3260
  emitStmts(stmts) {
@@ -2960,7 +3483,7 @@ class LlEmitter {
2960
3483
  B.line(`${kp} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 1`);
2961
3484
  B.line(`store ptr ${this.cstr(f.name)}, ptr ${kp}`);
2962
3485
  B.line(`${ip} = getelementptr inbounds %ScrDynPath, ptr ${pathSlot}, i64 0, i32 2`);
2963
- B.line(`store i64 0, ptr ${ip}`);
3486
+ B.line(`store ${this.sizeType} 0, ptr ${ip}`);
2964
3487
  const helper = this.dyn.dynCheckHelper(f.type);
2965
3488
  const fty = this.llType(f.type);
2966
3489
  const nv = B.tmp();
@@ -3129,8 +3652,8 @@ class LlEmitter {
3129
3652
  let integerSlot = null;
3130
3653
  if (integerLoop) {
3131
3654
  integerSlot = B.slot();
3132
- B.entryAllocas.push(`${integerSlot} = alloca i64 ; integer induction ${this.currentLocals.get(integerLoop.localId).name}`);
3133
- B.line(`store i64 0, ptr ${integerSlot}`);
3655
+ B.entryAllocas.push(`${integerSlot} = alloca ${this.sizeType} ; integer induction ${this.currentLocals.get(integerLoop.localId).name}`);
3656
+ B.line(`store ${this.sizeType} 0, ptr ${integerSlot}`);
3134
3657
  this.integerLoopBindings.set(integerLoop.localId, integerSlot);
3135
3658
  }
3136
3659
  else if (s.init) {
@@ -3156,9 +3679,9 @@ class LlEmitter {
3156
3679
  const index = B.tmp();
3157
3680
  const inBounds = B.tmp();
3158
3681
  B.line(`${lenPtr} = getelementptr inbounds %ScrBytes, ptr ${receiver.name}, i64 0, i32 1`);
3159
- B.line(`${len} = load i64, ptr ${lenPtr}`);
3160
- B.line(`${index} = load i64, ptr ${integerSlot}`);
3161
- B.line(`${inBounds} = icmp ult i64 ${index}, ${len}`);
3682
+ B.line(`${len} = load ${this.sizeType}, ptr ${lenPtr}`);
3683
+ B.line(`${index} = load ${this.sizeType}, ptr ${integerSlot}`);
3684
+ B.line(`${inBounds} = icmp ult ${this.sizeType} ${index}, ${len}`);
3162
3685
  B.condBr(inBounds, lb, le);
3163
3686
  }
3164
3687
  else if (s.cond)
@@ -3196,9 +3719,9 @@ class LlEmitter {
3196
3719
  if (integerLoop && integerSlot) {
3197
3720
  const old = B.tmp();
3198
3721
  const next = B.tmp();
3199
- B.line(`${old} = load i64, ptr ${integerSlot}`);
3200
- B.line(`${next} = add nuw i64 ${old}, 1`);
3201
- B.line(`store i64 ${next}, ptr ${integerSlot}`);
3722
+ B.line(`${old} = load ${this.sizeType}, ptr ${integerSlot}`);
3723
+ B.line(`${next} = add nuw ${this.sizeType} ${old}, 1`);
3724
+ B.line(`store ${this.sizeType} ${next}, ptr ${integerSlot}`);
3202
3725
  }
3203
3726
  else if (s.update)
3204
3727
  this.emitStmt(s.update);
@@ -3376,10 +3899,16 @@ class LlEmitter {
3376
3899
  else {
3377
3900
  this.releaseForJump(0, 0);
3378
3901
  }
3379
- if (v === null)
3380
- B.terminate("ret void");
3381
- else
3382
- B.terminate(`ret ${this.llType(s.value.type)} ${v.name}`);
3902
+ if (!B.isTerminated()) {
3903
+ if (this.currentWasiCoro !== null) {
3904
+ this.emitWasiFulfill(v);
3905
+ B.terminate(`br label %${this.currentWasiCoro.finalLabel}`);
3906
+ }
3907
+ else if (v === null)
3908
+ B.terminate("ret void");
3909
+ else
3910
+ B.terminate(`ret ${this.llType(s.value.type)} ${v.name}`);
3911
+ }
3383
3912
  break;
3384
3913
  }
3385
3914
  case "throw": {
@@ -3408,8 +3937,8 @@ class LlEmitter {
3408
3937
  // the construct (message) with the SC code stamped on `code`,
3409
3938
  // then unwind exactly like `throw`. SCR_ERR_ERROR = 0.
3410
3939
  const bytes = Buffer.byteLength(s.message, "utf8");
3411
- this.declare(`declare void @scr_throw_error_msg_code(i32, ptr, i64, ptr)`);
3412
- B.line(`call void @scr_throw_error_msg_code(i32 0, ptr ${this.cstr(s.message)}, i64 ${bytes}, ptr ${this.cstr(s.code)})`);
3940
+ this.declare(`declare void @scr_throw_error_msg_code(i32, ptr, ${this.sizeType}, ptr)`);
3941
+ B.line(`call void @scr_throw_error_msg_code(i32 0, ptr ${this.cstr(s.message)}, ${this.sizeType} ${bytes}, ptr ${this.cstr(s.code)})`);
3413
3942
  this.emitUnwind();
3414
3943
  break;
3415
3944
  }
@@ -3689,8 +4218,8 @@ class LlEmitter {
3689
4218
  if (integerSlot !== undefined) {
3690
4219
  const integer = B.tmp();
3691
4220
  const number = B.tmp();
3692
- B.line(`${integer} = load i64, ptr ${integerSlot}`);
3693
- B.line(`${number} = uitofp i64 ${integer} to double`);
4221
+ B.line(`${integer} = load ${this.sizeType}, ptr ${integerSlot}`);
4222
+ B.line(`${number} = uitofp ${this.sizeType} ${integer} to double`);
3694
4223
  return { name: number, type: e.type };
3695
4224
  }
3696
4225
  const b = this.binding(e.localId);
@@ -4817,15 +5346,17 @@ class LlEmitter {
4817
5346
  }
4818
5347
  // Lifted async lambdas enter through their spawn wrapper (which
4819
5348
  // takes sc_env first, like every lifted function).
4820
- this.declare(`declare ptr @scr_closure_new(ptr, i64)`);
5349
+ this.declare(`declare ptr @scr_closure_new(ptr, ${this.sizeType})`);
4821
5350
  const c = B.tmp();
4822
- B.line(`${c} = call ptr @scr_closure_new(ptr @${this.callTarget(e.fnName)}, i64 ${e.captures.length})`);
5351
+ B.line(`${c} = call ptr @scr_closure_new(ptr @${this.callTarget(e.fnName)}, ${this.sizeType} ${e.captures.length})`);
4823
5352
  const out = this.own({ name: c, type: e.type });
4824
5353
  e.captures.forEach((localId, i) => {
4825
5354
  const box = this.loadBox(`%${mangleLocal(localId)}`);
4826
5355
  const retained = this.retainBox(box);
5356
+ const caps = B.tmp();
4827
5357
  const capp = B.tmp();
4828
- B.line(`${capp} = getelementptr inbounds i8, ptr ${c}, i64 ${32 + 8 * i} ; caps[${i}]`);
5358
+ B.line(`${caps} = getelementptr inbounds %ScrClosure, ptr ${c}, i64 1 ; caps`);
5359
+ B.line(`${capp} = getelementptr inbounds ptr, ptr ${caps}, ${this.sizeType} ${i} ; caps[${i}]`);
4829
5360
  B.line(`store ptr ${retained}, ptr ${capp}`);
4830
5361
  });
4831
5362
  return out;
@@ -4904,11 +5435,49 @@ class LlEmitter {
4904
5435
  if (!entry)
4905
5436
  throw new Error(`llvm emitter bug: unknown FFI import ${e.import}`);
4906
5437
  const args = e.args.map((arg) => this.emitExpr(arg));
5438
+ const sourceArgs = new Map();
5439
+ const callbackArgs = new Map();
5440
+ let sourceIndex = 0;
5441
+ entry.params.forEach((param, abiIndex) => {
5442
+ if (isFfiContextParam(param))
5443
+ return;
5444
+ const arg = args[sourceIndex++];
5445
+ sourceArgs.set(abiIndex, arg);
5446
+ if (isFfiCallbackParam(param))
5447
+ callbackArgs.set(param.callback.id, arg);
5448
+ });
5449
+ const rawContexts = [];
5450
+ for (const param of entry.params) {
5451
+ if (!isFfiCallbackParam(param))
5452
+ continue;
5453
+ const adapter = this.ffiCallbackAdapter(entry.name, param.callback.id);
5454
+ if (adapter.tls === null)
5455
+ continue;
5456
+ const callback = callbackArgs.get(param.callback.id);
5457
+ const previous = B.tmp();
5458
+ B.line(`${previous} = load ptr, ptr @${adapter.tls}`);
5459
+ B.line(`store ptr ${callback.name}, ptr @${adapter.tls}`);
5460
+ rawContexts.push({ tls: adapter.tls, previous });
5461
+ }
4907
5462
  const nativeArgs = [];
4908
5463
  const nativeParamTypes = [];
4909
- entry.params.forEach((cls, i) => {
4910
- const arg = args[i];
4911
- switch (cls) {
5464
+ entry.params.forEach((param, i) => {
5465
+ if (isFfiCallbackParam(param)) {
5466
+ const adapter = this.ffiCallbackAdapter(entry.name, param.callback.id);
5467
+ nativeParamTypes.push("ptr");
5468
+ nativeArgs.push(`ptr @${adapter.symbol}`);
5469
+ return;
5470
+ }
5471
+ if (isFfiContextParam(param)) {
5472
+ const callback = callbackArgs.get(param.context);
5473
+ if (!callback)
5474
+ throw new Error(`llvm emitter bug: FFI context '${param.context}' has no callback arg`);
5475
+ nativeParamTypes.push("ptr");
5476
+ nativeArgs.push(`ptr ${callback.name}`);
5477
+ return;
5478
+ }
5479
+ const arg = sourceArgs.get(i);
5480
+ switch (param) {
4912
5481
  case "f64":
4913
5482
  nativeParamTypes.push("double");
4914
5483
  nativeArgs.push(`double ${arg.name}`);
@@ -4927,7 +5496,7 @@ class LlEmitter {
4927
5496
  const asU32 = B.tmp();
4928
5497
  B.line(`${asDouble} = call double @scr_bit_ushr(double ${arg.name}, double ${f64Lit(0)})`);
4929
5498
  B.line(`${asU32} = fptoui double ${asDouble} to i32`);
4930
- if (cls === "u8") {
5499
+ if (param === "u8") {
4931
5500
  const asU8 = B.tmp();
4932
5501
  B.line(`${asU8} = trunc i32 ${asU32} to i8`);
4933
5502
  nativeParamTypes.push("i8");
@@ -4975,29 +5544,47 @@ class LlEmitter {
4975
5544
  }
4976
5545
  }
4977
5546
  });
4978
- const retTy = entry.returns === "f64" ? "double"
4979
- : entry.returns === "bool" || entry.returns === "u8" ? "i8"
4980
- : entry.returns === "u32" || entry.returns === "i32" ? "i32"
4981
- : "void";
5547
+ const retTy = ffiNativeTypeLl(entry.returns);
4982
5548
  this.declare(`declare ${retTy} @${entry.symbol}(${nativeParamTypes.join(", ")})`);
4983
5549
  const call = `call ${retTy} @${entry.symbol}(${nativeArgs.join(", ")})`;
5550
+ const restoreRawContexts = () => {
5551
+ for (let i = rawContexts.length - 1; i >= 0; i--) {
5552
+ const saved = rawContexts[i];
5553
+ B.line(`store ptr ${saved.previous}, ptr @${saved.tls}`);
5554
+ }
5555
+ };
5556
+ const callbacksMayThrow = callbackArgs.size > 0;
4984
5557
  if (entry.returns === "void") {
4985
5558
  B.line(call);
5559
+ restoreRawContexts();
5560
+ if (callbacksMayThrow)
5561
+ this.emitPendingCheck();
4986
5562
  return { name: "", type: e.type };
4987
5563
  }
4988
5564
  const raw = B.tmp();
4989
5565
  B.line(`${raw} = ${call}`);
4990
- if (entry.returns === "f64")
4991
- return { name: raw, type: e.type };
5566
+ restoreRawContexts();
5567
+ if (entry.returns === "f64") {
5568
+ const result = { name: raw, type: e.type };
5569
+ if (callbacksMayThrow)
5570
+ this.emitPendingCheck();
5571
+ return result;
5572
+ }
4992
5573
  if (entry.returns === "bool") {
4993
5574
  const value = B.tmp();
4994
5575
  B.line(`${value} = icmp ne i8 ${raw}, 0`);
4995
- return { name: value, type: e.type };
5576
+ const result = { name: value, type: e.type };
5577
+ if (callbacksMayThrow)
5578
+ this.emitPendingCheck();
5579
+ return result;
4996
5580
  }
4997
5581
  const value = B.tmp();
4998
5582
  const op = entry.returns === "i32" ? "sitofp" : "uitofp";
4999
5583
  B.line(`${value} = ${op} ${retTy} ${raw} to double`);
5000
- return { name: value, type: e.type };
5584
+ const result = { name: value, type: e.type };
5585
+ if (callbacksMayThrow)
5586
+ this.emitPendingCheck();
5587
+ return result;
5001
5588
  }
5002
5589
  case "new": {
5003
5590
  // Allocate (fields zeroed, vt stamped), then run the ctor. The
@@ -5116,8 +5703,8 @@ class LlEmitter {
5116
5703
  const ge = B.tmp();
5117
5704
  const le = B.tmp();
5118
5705
  const t = B.tmp();
5119
- B.line(`${ge} = icmp sge i64 ${pre}, ${target.pre}`);
5120
- B.line(`${le} = icmp sle i64 ${pre}, ${target.post}`);
5706
+ B.line(`${ge} = icmp sge ${this.sizeType} ${pre}, ${target.pre}`);
5707
+ B.line(`${le} = icmp sle ${this.sizeType} ${pre}, ${target.post}`);
5121
5708
  B.line(`${t} = and i1 ${ge}, ${le} ; instanceof ${e.className}`);
5122
5709
  return { name: t, type: e.type };
5123
5710
  }
@@ -5135,14 +5722,14 @@ class LlEmitter {
5135
5722
  const tpostp = B.tmp();
5136
5723
  const tpost = B.tmp();
5137
5724
  B.line(`${tprep} = getelementptr inbounds %ScrClassObj, ptr ${target.name}, i64 0, i32 1`);
5138
- B.line(`${tpre} = load i64, ptr ${tprep}`);
5725
+ B.line(`${tpre} = load ${this.sizeType}, ptr ${tprep}`);
5139
5726
  B.line(`${tpostp} = getelementptr inbounds %ScrClassObj, ptr ${target.name}, i64 0, i32 2`);
5140
- B.line(`${tpost} = load i64, ptr ${tpostp}`);
5727
+ B.line(`${tpost} = load ${this.sizeType}, ptr ${tpostp}`);
5141
5728
  const ge = B.tmp();
5142
5729
  const le = B.tmp();
5143
5730
  const t = B.tmp();
5144
- B.line(`${ge} = icmp sge i64 ${pre}, ${tpre}`);
5145
- B.line(`${le} = icmp sle i64 ${pre}, ${tpost}`);
5731
+ B.line(`${ge} = icmp sge ${this.sizeType} ${pre}, ${tpre}`);
5732
+ B.line(`${le} = icmp sle ${this.sizeType} ${pre}, ${tpost}`);
5146
5733
  B.line(`${t} = and i1 ${ge}, ${le}`);
5147
5734
  return { name: t, type: e.type };
5148
5735
  }
@@ -5154,9 +5741,9 @@ class LlEmitter {
5154
5741
  const c = this.emitExpr(e.value);
5155
5742
  if (e.test === "instanceof") {
5156
5743
  const target = this.classMetaOf(e.className);
5157
- this.declare(`declare zeroext i1 @scr_caught_instanceof(ptr, i64, i64)`);
5744
+ this.declare(`declare zeroext i1 @scr_caught_instanceof(ptr, ${this.sizeType}, ${this.sizeType})`);
5158
5745
  const t = B.tmp();
5159
- B.line(`${t} = call zeroext i1 @scr_caught_instanceof(ptr ${c.name}, i64 ${target.pre}, i64 ${target.post})`);
5746
+ B.line(`${t} = call zeroext i1 @scr_caught_instanceof(ptr ${c.name}, ${this.sizeType} ${target.pre}, ${this.sizeType} ${target.post})`);
5160
5747
  if (e.negated !== true)
5161
5748
  return { name: t, type: e.type };
5162
5749
  const n = B.tmp();
@@ -5221,9 +5808,9 @@ class LlEmitter {
5221
5808
  const c = this.emitExpr(e.value);
5222
5809
  const target = this.classMetaOf(e.className);
5223
5810
  const display = e.className.startsWith("%") ? e.className.slice(1) : e.className;
5224
- this.declare(`declare ptr @scr_caught_check_obj(ptr, i64, i64, ptr)`);
5811
+ this.declare(`declare ptr @scr_caught_check_obj(ptr, ${this.sizeType}, ${this.sizeType}, ptr)`);
5225
5812
  const t = B.tmp();
5226
- B.line(`${t} = call ptr @scr_caught_check_obj(ptr ${c.name}, i64 ${target.pre}, i64 ${target.post}, ptr ${this.cstr(display)})`);
5813
+ B.line(`${t} = call ptr @scr_caught_check_obj(ptr ${c.name}, ${this.sizeType} ${target.pre}, ${this.sizeType} ${target.post}, ptr ${this.cstr(display)})`);
5227
5814
  const out = this.own({ name: t, type: e.type });
5228
5815
  this.emitPendingCheck();
5229
5816
  return out;
@@ -5334,7 +5921,7 @@ class LlEmitter {
5334
5921
  return compact;
5335
5922
  const rewriter = this.walkers.jsonIndentHelper();
5336
5923
  const t2 = B.tmp();
5337
- B.line(`${t2} = call ptr @${rewriter}(ptr ${compact.name}, ptr ${this.cstr(indent)}, i64 ${Buffer.byteLength(indent, "utf8")})`);
5924
+ B.line(`${t2} = call ptr @${rewriter}(ptr ${compact.name}, ptr ${this.cstr(indent)}, ${this.sizeType} ${Buffer.byteLength(indent, "utf8")})`);
5338
5925
  return this.own({ name: t2, type: e.type });
5339
5926
  }
5340
5927
  case "bytesNew": {
@@ -5389,18 +5976,50 @@ class LlEmitter {
5389
5976
  const v = this.emitExpr(e.value);
5390
5977
  const yt = e.value.type;
5391
5978
  if (yt.kind === "f64" || yt.kind === "date") {
5392
- this.declare(`declare void @scr_gen_yield_f64(double)`);
5393
- B.line(`call void @scr_gen_yield_f64(double ${v.name})`);
5979
+ if (this.wasi) {
5980
+ const coro = this.currentWasiCoro;
5981
+ this.declare(`declare ptr @scr_gen_of_fiber(ptr)`);
5982
+ this.declare(`declare void @scr_gen_out_f64(ptr, double)`);
5983
+ const g = B.tmp();
5984
+ B.line(`${g} = call ptr @scr_gen_of_fiber(ptr ${coro.self})`);
5985
+ B.line(`call void @scr_gen_out_f64(ptr ${g}, double ${v.name})`);
5986
+ }
5987
+ else {
5988
+ this.declare(`declare void @scr_gen_yield_f64(double)`);
5989
+ B.line(`call void @scr_gen_yield_f64(double ${v.name})`);
5990
+ }
5394
5991
  }
5395
5992
  else if (yt.kind === "bool") {
5396
- this.declare(`declare void @scr_gen_yield_bool(i1 zeroext)`);
5397
- B.line(`call void @scr_gen_yield_bool(i1 ${v.name})`);
5993
+ if (this.wasi) {
5994
+ const coro = this.currentWasiCoro;
5995
+ this.declare(`declare ptr @scr_gen_of_fiber(ptr)`);
5996
+ this.declare(`declare void @scr_gen_out_bool(ptr, i1 zeroext)`);
5997
+ const g = B.tmp();
5998
+ B.line(`${g} = call ptr @scr_gen_of_fiber(ptr ${coro.self})`);
5999
+ B.line(`call void @scr_gen_out_bool(ptr ${g}, i1 ${v.name})`);
6000
+ }
6001
+ else {
6002
+ this.declare(`declare void @scr_gen_yield_bool(i1 zeroext)`);
6003
+ B.line(`call void @scr_gen_yield_bool(i1 ${v.name})`);
6004
+ }
5398
6005
  }
5399
6006
  else {
5400
6007
  this.moveTemp(v); // the OUT slot takes ownership
5401
- this.declare(`declare void @scr_gen_yield_ref(ptr, ptr)`);
5402
- B.line(`call void @scr_gen_yield_ref(ptr ${v.name}, ptr ${vAdapters(this, yt).release})`);
6008
+ if (this.wasi) {
6009
+ const coro = this.currentWasiCoro;
6010
+ this.declare(`declare ptr @scr_gen_of_fiber(ptr)`);
6011
+ this.declare(`declare void @scr_gen_out_ref(ptr, ptr, ptr)`);
6012
+ const g = B.tmp();
6013
+ B.line(`${g} = call ptr @scr_gen_of_fiber(ptr ${coro.self})`);
6014
+ B.line(`call void @scr_gen_out_ref(ptr ${g}, ptr ${v.name}, ptr ${vAdapters(this, yt).release})`);
6015
+ }
6016
+ else {
6017
+ this.declare(`declare void @scr_gen_yield_ref(ptr, ptr)`);
6018
+ B.line(`call void @scr_gen_yield_ref(ptr ${v.name}, ptr ${vAdapters(this, yt).release})`);
6019
+ }
5403
6020
  }
6021
+ if (this.wasi)
6022
+ this.emitWasiSuspendPrepared();
5404
6023
  this.emitPendingCheck();
5405
6024
  if (e.type.kind === "void") {
5406
6025
  // An undefined next-channel: nothing to read (the frontend
@@ -5532,6 +6151,8 @@ class LlEmitter {
5532
6151
  // refcounted results arrive +1 and join the frame pre-check so an
5533
6152
  // unwind releases the dummy (NULL) harmlessly.
5534
6153
  const pr = this.emitExpr(e.value);
6154
+ if (this.wasi)
6155
+ this.emitWasiSuspend(pr.name);
5535
6156
  if (e.type.kind === "void") {
5536
6157
  this.declare(`declare void @scr_await_void(ptr)`);
5537
6158
  B.line(`call void @scr_await_void(ptr ${pr.name})`);
@@ -5586,10 +6207,18 @@ class LlEmitter {
5586
6207
  B.condBr(isP, lp, lh);
5587
6208
  B.startBlock(lp);
5588
6209
  this.declare(`declare void @scr_await_void(ptr)`);
5589
- B.line(`call void @scr_await_void(ptr ${this.unionPeek(u.name)})`);
6210
+ {
6211
+ const promise = this.unionPeek(u.name);
6212
+ if (this.wasi)
6213
+ this.emitWasiSuspend(promise);
6214
+ B.line(`call void @scr_await_void(ptr ${promise})`);
6215
+ }
5590
6216
  B.br(lj);
5591
6217
  B.startBlock(lh);
5592
- B.line(`call void @scr_await_hop()`);
6218
+ if (this.wasi)
6219
+ this.emitWasiSuspend(null);
6220
+ else
6221
+ B.line(`call void @scr_await_hop()`);
5593
6222
  B.br(lj);
5594
6223
  B.startBlock(lj);
5595
6224
  this.emitPendingCheck();
@@ -5618,6 +6247,8 @@ class LlEmitter {
5618
6247
  B.condBr(isP, lp, lh);
5619
6248
  B.startBlock(lp);
5620
6249
  const peek = this.unionPeek(u.name);
6250
+ if (this.wasi)
6251
+ this.emitWasiSuspend(peek);
5621
6252
  let awaited;
5622
6253
  if (inner.kind === "f64" || inner.kind === "date") {
5623
6254
  this.declare(`declare double @scr_await_f64(ptr)`);
@@ -5646,7 +6277,10 @@ class LlEmitter {
5646
6277
  B.line(`store ptr ${this.unionNewOwned(innerTag, awaited)}, ptr ${slot}`);
5647
6278
  B.br(lj);
5648
6279
  B.startBlock(lh);
5649
- B.line(`call void @scr_await_hop()`);
6280
+ if (this.wasi)
6281
+ this.emitWasiSuspend(null);
6282
+ else
6283
+ B.line(`call void @scr_await_hop()`);
5650
6284
  const unitTags = def.arms.flatMap((a, i) => (isUnitType(a) ? [i] : []));
5651
6285
  if (unitTags.length === 1) {
5652
6286
  B.line(`store ptr ${this.unitInstanceRef(resUnionId, resTagOf(def.arms[unitTags[0]]))}, ptr ${slot}`);
@@ -5772,8 +6406,27 @@ class LlEmitter {
5772
6406
  // Internal ESM dependency wait: pending promises park the module
5773
6407
  // fiber, while settled ones continue synchronously.
5774
6408
  const p = this.emitExpr(e.args[0]);
5775
- this.declare(`declare void @scr_module_await(ptr)`);
5776
- B.line(`call void @scr_module_await(ptr ${p.name})`);
6409
+ if (this.wasi) {
6410
+ const coro = this.currentWasiCoro;
6411
+ if (coro === null)
6412
+ throw new Error("llvm emitter bug: module await outside wasm coroutine");
6413
+ this.declare(`declare zeroext i1 @scr_wasi_module_await_prepare(ptr, ptr)`);
6414
+ const needsSuspend = B.tmp();
6415
+ const suspend = B.newLabel("ma.suspend");
6416
+ const ready = B.newLabel("ma.ready");
6417
+ B.line(`${needsSuspend} = call zeroext i1 @scr_wasi_module_await_prepare(ptr ${coro.self}, ptr ${p.name})`);
6418
+ B.condBr(needsSuspend, suspend, ready);
6419
+ B.startBlock(suspend);
6420
+ this.emitWasiSuspendPrepared();
6421
+ B.br(ready);
6422
+ B.startBlock(ready);
6423
+ this.declare(`declare void @scr_module_await(ptr)`);
6424
+ B.line(`call void @scr_module_await(ptr ${p.name})`);
6425
+ }
6426
+ else {
6427
+ this.declare(`declare void @scr_module_await(ptr)`);
6428
+ B.line(`call void @scr_module_await(ptr ${p.name})`);
6429
+ }
5777
6430
  this.emitPendingCheck();
5778
6431
  return { name: "", type: e.type };
5779
6432
  }
@@ -5807,7 +6460,7 @@ class LlEmitter {
5807
6460
  const len = B.tmp();
5808
6461
  const cap = B.tmp();
5809
6462
  B.line(`${len} = call double @scr_arr_len(ptr ${ps.name})`);
5810
- B.line(`${cap} = fptoui double ${len} to i64`);
6463
+ B.line(`${cap} = fptoui double ${len} to ${this.sizeType}`);
5811
6464
  const vals = B.tmp();
5812
6465
  B.line(`${vals} = ${arrNewCall(this, elem, cap)}`);
5813
6466
  this.own({ name: vals, type: e.type.inner });
@@ -5929,8 +6582,8 @@ class LlEmitter {
5929
6582
  }
5930
6583
  });
5931
6584
  const fn = e.name === "console.error" ? "scr_console_error" : "scr_console_log";
5932
- this.declare(`declare void @${fn}(i64, ptr)`);
5933
- B.line(`call void @${fn}(i64 ${args.length}, ptr %logargs)`);
6585
+ this.declare(`declare void @${fn}(${this.sizeType}, ptr)`);
6586
+ B.line(`call void @${fn}(${this.sizeType} ${args.length}, ptr %logargs)`);
5934
6587
  return { name: "", type: e.type };
5935
6588
  }
5936
6589
  case "dynFrom": {
@@ -5972,9 +6625,9 @@ class LlEmitter {
5972
6625
  this.liveDynRefAdapters.set(key, adapter);
5973
6626
  }
5974
6627
  const rc = vAdapters(this, v.type);
5975
- this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
6628
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, ${this.sizeType}, ptr, ptr)`);
5976
6629
  const boxed = B.tmp();
5977
- B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${v.name}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(key)}, i64 ${Buffer.byteLength(key, "utf8")}, ptr @${adapter.snapshot}, ptr ${adapter.commit})`);
6630
+ B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${v.name}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(key)}, ${this.sizeType} ${Buffer.byteLength(key, "utf8")}, ptr @${adapter.snapshot}, ptr ${adapter.commit})`);
5978
6631
  return this.own({ name: boxed, type: e.type });
5979
6632
  }
5980
6633
  if (v.type.kind === "func") {
@@ -6075,14 +6728,14 @@ class LlEmitter {
6075
6728
  B.entryAllocas.push(`${arr} = alloca [${args.length} x ptr]`);
6076
6729
  args.forEach((a, i) => {
6077
6730
  const p = B.tmp();
6078
- B.line(`${p} = getelementptr inbounds [${args.length} x ptr], ptr ${arr}, i64 0, i64 ${i}`);
6731
+ B.line(`${p} = getelementptr inbounds [${args.length} x ptr], ptr ${arr}, i64 0, ${this.sizeType} ${i}`);
6079
6732
  B.line(`store ptr ${a.name}, ptr ${p}`);
6080
6733
  });
6081
6734
  argsPtr = arr;
6082
6735
  }
6083
- this.declare(`declare ptr @scr_dyn_call(ptr, ptr, i64, ptr)`);
6736
+ this.declare(`declare ptr @scr_dyn_call(ptr, ptr, ${this.sizeType}, ptr)`);
6084
6737
  const t = B.tmp();
6085
- B.line(`${t} = call ptr @scr_dyn_call(ptr ${callee.name}, ptr ${argsPtr}, i64 ${args.length}, ptr ${this.cstr(e.calleeName)})`);
6738
+ B.line(`${t} = call ptr @scr_dyn_call(ptr ${callee.name}, ptr ${argsPtr}, ${this.sizeType} ${args.length}, ptr ${this.cstr(e.calleeName)})`);
6086
6739
  const out = this.own({ name: t, type: e.type });
6087
6740
  this.emitPendingCheck();
6088
6741
  return out;
@@ -6099,14 +6752,14 @@ class LlEmitter {
6099
6752
  B.entryAllocas.push(`${arr} = alloca [${args.length} x ptr]`);
6100
6753
  args.forEach((a, i) => {
6101
6754
  const p = B.tmp();
6102
- B.line(`${p} = getelementptr inbounds [${args.length} x ptr], ptr ${arr}, i64 0, i64 ${i}`);
6755
+ B.line(`${p} = getelementptr inbounds [${args.length} x ptr], ptr ${arr}, i64 0, ${this.sizeType} ${i}`);
6103
6756
  B.line(`store ptr ${a.name}, ptr ${p}`);
6104
6757
  });
6105
6758
  argsPtr = arr;
6106
6759
  }
6107
- this.declare(`declare ptr @scr_dyn_invoke(ptr, ptr, ptr, i64, ptr)`);
6760
+ this.declare(`declare ptr @scr_dyn_invoke(ptr, ptr, ptr, ${this.sizeType}, ptr)`);
6108
6761
  const t = B.tmp();
6109
- B.line(`${t} = call ptr @scr_dyn_invoke(ptr ${recv.name}, ptr ${this.cstr(e.method)}, ptr ${argsPtr}, i64 ${args.length}, ptr ${this.cstr(e.calleeName)})`);
6762
+ B.line(`${t} = call ptr @scr_dyn_invoke(ptr ${recv.name}, ptr ${this.cstr(e.method)}, ptr ${argsPtr}, ${this.sizeType} ${args.length}, ptr ${this.cstr(e.calleeName)})`);
6110
6763
  const out = this.own({ name: t, type: e.type });
6111
6764
  this.emitPendingCheck();
6112
6765
  return out;
@@ -6173,11 +6826,11 @@ class LlEmitter {
6173
6826
  B.line(`${isObj} = icmp eq i32 ${kd}, ${DK.OBJ}`);
6174
6827
  B.condBr(isObj, lObj, lNotObj);
6175
6828
  B.startBlock(lObj);
6176
- this.declare(`declare ptr @scr_dyn_obj_get(ptr, ptr, i64)`);
6829
+ this.declare(`declare ptr @scr_dyn_obj_get(ptr, ptr, ${this.sizeType})`);
6177
6830
  const keyBytes = Buffer.byteLength(e.key, "utf8");
6178
6831
  const m = B.tmp();
6179
6832
  const has = B.tmp();
6180
- B.line(`${m} = call ptr @scr_dyn_obj_get(ptr ${d.name}, ptr ${this.cstr(e.key)}, i64 ${keyBytes})`);
6833
+ B.line(`${m} = call ptr @scr_dyn_obj_get(ptr ${d.name}, ptr ${this.cstr(e.key)}, ${this.sizeType} ${keyBytes})`);
6181
6834
  B.line(`${has} = icmp ne ptr ${m}, null`);
6182
6835
  B.line(`store i1 ${has}, ptr ${slot}`);
6183
6836
  B.br(lj);
@@ -6195,8 +6848,8 @@ class LlEmitter {
6195
6848
  const len = B.tmp();
6196
6849
  const inR = B.tmp();
6197
6850
  B.line(`${lenp} = getelementptr inbounds i8, ptr ${d.name}, i64 16 ; ->v.arr.len`);
6198
- B.line(`${len} = load i64, ptr ${lenp}`);
6199
- B.line(`${inR} = icmp ugt i64 ${len}, ${e.key}`);
6851
+ B.line(`${len} = load ${this.sizeType}, ptr ${lenp}`);
6852
+ B.line(`${inR} = icmp ugt ${this.sizeType} ${len}, ${e.key}`);
6200
6853
  B.line(`store i1 ${inR}, ptr ${slot}`);
6201
6854
  }
6202
6855
  B.br(lj);
@@ -6309,10 +6962,10 @@ class LlEmitter {
6309
6962
  const lj = B.newLabel("dts.j");
6310
6963
  B.condBr(isObj, lObj, lj);
6311
6964
  B.startBlock(lObj);
6312
- this.declare(`declare ptr @scr_dyn_obj_get(ptr, ptr, i64)`);
6965
+ this.declare(`declare ptr @scr_dyn_obj_get(ptr, ptr, ${this.sizeType})`);
6313
6966
  const m = B.tmp();
6314
6967
  const has = B.tmp();
6315
- B.line(`${m} = call ptr @scr_dyn_obj_get(ptr ${d.name}, ptr ${this.cstr("%error")}, i64 6)`);
6968
+ B.line(`${m} = call ptr @scr_dyn_obj_get(ptr ${d.name}, ptr ${this.cstr("%error")}, ${this.sizeType} 6)`);
6316
6969
  B.line(`${has} = icmp ne ptr ${m}, null`);
6317
6970
  B.line(`store i1 ${has}, ptr ${slot}`);
6318
6971
  B.br(lj);
@@ -6420,7 +7073,7 @@ class LlEmitter {
6420
7073
  }
6421
7074
  const helper = this.dyn.dynIterNHelper();
6422
7075
  const t = B.tmp();
6423
- B.line(`${t} = call ptr @${helper}(ptr ${v.name}, i64 ${e.count})`);
7076
+ B.line(`${t} = call ptr @${helper}(ptr ${v.name}, ${this.sizeType} ${e.count})`);
6424
7077
  const out = this.own({ name: t, type: e.type });
6425
7078
  this.emitPendingCheck();
6426
7079
  return out;
@@ -7174,13 +7827,13 @@ class LlEmitter {
7174
7827
  this.resolveThunkDefs.push(...d);
7175
7828
  return sym;
7176
7829
  }
7177
- /** Loads a dyn node's kind tag (i32 at +8) — the dyn expression tests'
7830
+ /** Loads a dyn node's kind tag after its size_t RC word — the dyn expression tests'
7178
7831
  * shared read. */
7179
7832
  dynKind(d) {
7180
7833
  const B = this.B;
7181
7834
  const p = B.tmp();
7182
7835
  const k = B.tmp();
7183
- B.line(`${p} = getelementptr inbounds i8, ptr ${d}, i64 8 ; ->kind`);
7836
+ B.line(`${p} = getelementptr inbounds i8, ptr ${d}, i64 ${this.sizeType === "i32" ? 4 : 8} ; ->kind`);
7184
7837
  B.line(`${k} = load i32, ptr ${p}`);
7185
7838
  return k;
7186
7839
  }
@@ -7486,11 +8139,11 @@ class LlEmitter {
7486
8139
  }
7487
8140
  this.declare(`declare ptr @scr_union_new_f64(i32, double)`);
7488
8141
  this.declare(`declare ptr @scr_union_new_ref(i32, ptr, ptr, ptr, ptr)`);
7489
- this.declare(`declare ptr @scr_str_new(ptr, i64)`);
7490
- this.declare(`declare i64 @strlen(ptr)`);
8142
+ this.declare(`declare ptr @scr_str_new(ptr, ${this.sizeType})`);
8143
+ this.declare(`declare ${this.sizeType} @strlen(ptr)`);
7491
8144
  this.declare(`declare ptr @scr_str_retain_v(ptr)`);
7492
8145
  this.declare(`declare void @scr_str_release_v(ptr)`);
7493
- this.resolveThunkDefs.push(`define internal void @${sym}(ptr %cb, i1 zeroext %has, double %code, ptr %sig) ${FN_ATTRS} { ; child exit (code, signal)`, `entry:`, ` %uslot = alloca ptr`, ` %sslot = alloca ptr`, ` br i1 %has, label %num, label %nonum`, `num:`, ` %u1 = call ptr @scr_union_new_f64(i32 ${f64Tag}, double %code)`, ` store ptr %u1, ptr %uslot`, ` br label %sigq`, `nonum:`, ` store ptr ${this.unitInstanceRef(codeParam.unionId, codeNullTag)}, ptr %uslot`, ` br label %sigq`, `sigq:`, ` %hassig = icmp ne ptr %sig, null`, ` br i1 %hassig, label %sigs, label %signull`, `sigs:`, ` %len = call i64 @strlen(ptr %sig)`, ` %ss = call ptr @scr_str_new(ptr %sig, i64 %len)`, ` %su = call ptr @scr_union_new_ref(i32 ${strTag}, ptr %ss, ptr @scr_str_retain_v, ptr @scr_str_release_v, ptr null)`, ` store ptr %su, ptr %sslot`, ` br label %go`, `signull:`, ` store ptr ${this.unitInstanceRef(sigParam.unionId, sigNullTag)}, ptr %sslot`, ` br label %go`, `go:`, ` %u = load ptr, ptr %uslot`, ` %s = load ptr, ptr %sslot`, ` %fnp = getelementptr inbounds %ScrClosure, ptr %cb, i64 0, i32 1`, ` %fn = load ptr, ptr %fnp`, ` call void %fn(ptr %cb, ptr %u, ptr %s)`, ` ret void`, `}`, ``);
8146
+ this.resolveThunkDefs.push(`define internal void @${sym}(ptr %cb, i1 zeroext %has, double %code, ptr %sig) ${FN_ATTRS} { ; child exit (code, signal)`, `entry:`, ` %uslot = alloca ptr`, ` %sslot = alloca ptr`, ` br i1 %has, label %num, label %nonum`, `num:`, ` %u1 = call ptr @scr_union_new_f64(i32 ${f64Tag}, double %code)`, ` store ptr %u1, ptr %uslot`, ` br label %sigq`, `nonum:`, ` store ptr ${this.unitInstanceRef(codeParam.unionId, codeNullTag)}, ptr %uslot`, ` br label %sigq`, `sigq:`, ` %hassig = icmp ne ptr %sig, null`, ` br i1 %hassig, label %sigs, label %signull`, `sigs:`, ` %len = call ${this.sizeType} @strlen(ptr %sig)`, ` %ss = call ptr @scr_str_new(ptr %sig, ${this.sizeType} %len)`, ` %su = call ptr @scr_union_new_ref(i32 ${strTag}, ptr %ss, ptr @scr_str_retain_v, ptr @scr_str_release_v, ptr null)`, ` store ptr %su, ptr %sslot`, ` br label %go`, `signull:`, ` store ptr ${this.unitInstanceRef(sigParam.unionId, sigNullTag)}, ptr %sslot`, ` br label %go`, `go:`, ` %u = load ptr, ptr %uslot`, ` %s = load ptr, ptr %sslot`, ` %fnp = getelementptr inbounds %ScrClosure, ptr %cb, i64 0, i32 1`, ` %fn = load ptr, ptr %fnp`, ` call void %fn(ptr %cb, ptr %u, ptr %s)`, ` ret void`, `}`, ``);
7494
8147
  return sym;
7495
8148
  }
7496
8149
  /** Interned per-union child-stream data adapter — emit-async.ts's
@@ -7521,8 +8174,8 @@ class LlEmitter {
7521
8174
  * POINTER-SIZE slots off the emit tuple's va_list (textual LLVM IR
7522
8175
  * cannot va_arg portably) and calls this function behind the wrapper
7523
8176
  * closure's fn. It re-types each slot to the listener's declared
7524
- * parameter (f64 from its i64 bit pattern, bool from the zero-extended
7525
- * slot, refcounted values retained — the callee owns +1 per the
8177
+ * parameter (f64/bool scalars load from the emit site's stack slot,
8178
+ * refcounted values retain the direct pointer — the callee owns +1 per the
7526
8179
  * universal convention) and calls the ORIGINAL listener held by the
7527
8180
  * wrapper's one capture box. A non-void result is discarded (refcounted
7528
8181
  * ones released) — Node ignores listener return values. Interned per
@@ -7556,11 +8209,11 @@ class LlEmitter {
7556
8209
  cbT.params.forEach((p, i) => {
7557
8210
  const ty = this.llType(p);
7558
8211
  if (ty === "double") {
7559
- d.push(` %x${i} = ptrtoint ptr %a${i} to i64`, ` %d${i} = bitcast i64 %x${i} to double`);
8212
+ d.push(` %d${i} = load double, ptr %a${i}`);
7560
8213
  passed.push(`double %d${i}`);
7561
8214
  }
7562
8215
  else if (ty === "i1") {
7563
- d.push(` %x${i} = ptrtoint ptr %a${i} to i64`, ` %b${i} = trunc i64 %x${i} to i1`);
8216
+ d.push(` %b${i} = load i1, ptr %a${i}`);
7564
8217
  passed.push(`i1 %b${i}`);
7565
8218
  }
7566
8219
  else if (isRefCounted(p)) {
@@ -7592,11 +8245,11 @@ class LlEmitter {
7592
8245
  * registration call it feeds always moves it into the registry. */
7593
8246
  wrapEmitterListener(target, adapterFn) {
7594
8247
  const B = this.B;
7595
- this.declare(`declare ptr @scr_closure_new(ptr, i64)`);
8248
+ this.declare(`declare ptr @scr_closure_new(ptr, ${this.sizeType})`);
7596
8249
  this.declare(`declare ptr @scr_box_new(i32)`);
7597
8250
  this.declare(`declare void @scr_box_set_ref(ptr, ptr)`);
7598
8251
  const ad = B.tmp();
7599
- B.line(`${ad} = call ptr @scr_closure_new(ptr @${adapterFn}, i64 1)`);
8252
+ B.line(`${ad} = call ptr @scr_closure_new(ptr @${adapterFn}, ${this.sizeType} 1)`);
7600
8253
  const bx = B.tmp();
7601
8254
  B.line(`${bx} = call ptr @scr_box_new(i32 4) ; SCR_BOX_FUNC`);
7602
8255
  const capsp = B.tmp();
@@ -7672,7 +8325,7 @@ class LlEmitter {
7672
8325
  throw new Error("llvm emitter bug: bound-close err union lacks its undefined arm");
7673
8326
  trampoline = `${sym}_cb`;
7674
8327
  this.resolveThunkDefs.push(`define internal void @${trampoline}(ptr %self) ${FN_ATTRS} { ; close cb: fire with no error`, `entry:`, ` %capsp = getelementptr inbounds %ScrClosure, ptr %self, i64 1`, ` %bx = load ptr, ptr %capsp`, ` %inner = call ptr @scr_box_get_ref(ptr %bx) ; +1`, ` %fnp = getelementptr inbounds %ScrClosure, ptr %inner, i64 0, i32 1`, ` %fn = load ptr, ptr %fnp`, ` call void %fn(ptr %inner, ptr ${this.unitInstanceRef(errParam.unionId, undefTag)})`, ` call void @scr_closure_release(ptr %inner)`, ` ret void`, `}`, ``);
7675
- this.declare(`declare ptr @scr_closure_new(ptr, i64)`);
8328
+ this.declare(`declare ptr @scr_closure_new(ptr, ${this.sizeType})`);
7676
8329
  this.declare(`declare ptr @scr_box_new(i32)`);
7677
8330
  this.declare(`declare void @scr_box_set_ref(ptr, ptr)`);
7678
8331
  }
@@ -7695,7 +8348,7 @@ class LlEmitter {
7695
8348
  ` %pv = load ptr, ptr %pp`,
7696
8349
  ];
7697
8350
  if (oneParam) {
7698
- d.push(` %reg = call ptr @scr_closure_new(ptr @${trampoline}, i64 1)`, ` %rbx = call ptr @scr_box_new(i32 4) ; SCR_BOX_FUNC`, ` %rcp = getelementptr inbounds %ScrClosure, ptr %reg, i64 1`, ` store ptr %rbx, ptr %rcp`, ` %pr = call ptr @scr_closure_retain_v(ptr %pv)`, ` call void @scr_box_set_ref(ptr %rbx, ptr %pr)`, ` store ptr %reg, ptr %regslot`);
8351
+ d.push(` %reg = call ptr @scr_closure_new(ptr @${trampoline}, ${this.sizeType} 1)`, ` %rbx = call ptr @scr_box_new(i32 4) ; SCR_BOX_FUNC`, ` %rcp = getelementptr inbounds %ScrClosure, ptr %reg, i64 1`, ` store ptr %rbx, ptr %rcp`, ` %pr = call ptr @scr_closure_retain_v(ptr %pv)`, ` call void @scr_box_set_ref(ptr %rbx, ptr %pr)`, ` store ptr %reg, ptr %regslot`);
7699
8352
  }
7700
8353
  else {
7701
8354
  d.push(` %reg = call ptr @scr_closure_retain_v(ptr %pv)`, ` store ptr %reg, ptr %regslot`);
@@ -7791,8 +8444,8 @@ class LlEmitter {
7791
8444
  ? "a 'data' listener declaring a Buffer chunk received a string (the stream has an encoding set)"
7792
8445
  : "a 'data' listener declaring a string chunk received a Buffer (call setEncoding, or declare the chunk as a Buffer)";
7793
8446
  const slot = p.kind === "bytes" ? "%a0" : "%a1";
7794
- this.declare(`declare void @scr_throw_error_msg(i32, ptr, i64)`);
7795
- d.push(` %miss = icmp eq ptr ${slot}, null`, ` br i1 %miss, label %bad, label %ok`, `bad:`, ` call void @scr_throw_error_msg(i32 1, ptr ${this.cstr(msg)}, i64 ${Buffer.byteLength(msg, "utf8")})`, ` call void @scr_closure_release(ptr %orig)`, ` ret void`, `ok:`, ` %r0 = call ptr ${retainSym(this, p)}(ptr ${slot})`);
8447
+ this.declare(`declare void @scr_throw_error_msg(i32, ptr, ${this.sizeType})`);
8448
+ d.push(` %miss = icmp eq ptr ${slot}, null`, ` br i1 %miss, label %bad, label %ok`, `bad:`, ` call void @scr_throw_error_msg(i32 1, ptr ${this.cstr(msg)}, ${this.sizeType} ${Buffer.byteLength(msg, "utf8")})`, ` call void @scr_closure_release(ptr %orig)`, ` ret void`, `ok:`, ` %r0 = call ptr ${retainSym(this, p)}(ptr ${slot})`);
7796
8449
  finish("%r0");
7797
8450
  d.push(`}`, ``);
7798
8451
  }
@@ -7988,13 +8641,13 @@ class LlEmitter {
7988
8641
  // The stream-owning completion closure (a 1-cap closure boxing the
7989
8642
  // retained stream) — shared by the typed and dyn done shapes.
7990
8643
  const doneClosure = (fnRef, tag) => {
7991
- this.declare(`declare ptr @scr_closure_new(ptr, i64)`);
8644
+ this.declare(`declare ptr @scr_closure_new(ptr, ${this.sizeType})`);
7992
8645
  this.declare(`declare ptr @scr_box_new_obj(ptr, ptr, ptr)`);
7993
8646
  this.declare(`declare void @scr_box_set_ref(ptr, ptr)`);
7994
8647
  this.declare(`declare ptr @scr_stream_retain_v(ptr)`);
7995
8648
  this.declare(`declare void @scr_stream_release_v(ptr)`);
7996
8649
  this.declare(`declare void @scr_stream_trace(ptr, ptr, ptr)`);
7997
- d.push(` %${tag}clo = call ptr @scr_closure_new(ptr ${fnRef}, i64 1)`, ` %${tag}bx = call ptr @scr_box_new_obj(ptr @scr_stream_retain_v, ptr @scr_stream_release_v, ptr @scr_stream_trace)`, ` %${tag}cp = getelementptr inbounds %ScrClosure, ptr %${tag}clo, i64 1`, ` store ptr %${tag}bx, ptr %${tag}cp`, ` %${tag}sr = call ptr @scr_stream_retain_v(ptr %s)`, ` call void @scr_box_set_ref(ptr %${tag}bx, ptr %${tag}sr)`);
8650
+ d.push(` %${tag}clo = call ptr @scr_closure_new(ptr ${fnRef}, ${this.sizeType} 1)`, ` %${tag}bx = call ptr @scr_box_new_obj(ptr @scr_stream_retain_v, ptr @scr_stream_release_v, ptr @scr_stream_trace)`, ` %${tag}cp = getelementptr inbounds %ScrClosure, ptr %${tag}clo, i64 1`, ` store ptr %${tag}bx, ptr %${tag}cp`, ` %${tag}sr = call ptr @scr_stream_retain_v(ptr %s)`, ` call void @scr_box_set_ref(ptr %${tag}bx, ptr %${tag}sr)`);
7998
8651
  return `%${tag}clo`;
7999
8652
  };
8000
8653
  for (let i = off; i < declared.length; i++) {
@@ -8023,10 +8676,10 @@ class LlEmitter {
8023
8676
  passed.push(`ptr %dc${i}`);
8024
8677
  }
8025
8678
  else if (isEncPos) {
8026
- this.declare(`declare ptr @scr_str_new(ptr, i64)`);
8679
+ this.declare(`declare ptr @scr_str_new(ptr, ${this.sizeType})`);
8027
8680
  this.declare(`declare ptr @scr_dyn_new_str(ptr)`);
8028
8681
  this.declare(`declare void @scr_str_release(ptr)`);
8029
- d.push(` %es${i} = call ptr @scr_str_new(ptr ${this.cstr("buffer")}, i64 6)`, ` %ed${i} = call ptr @scr_dyn_new_str(ptr %es${i})`, ` call void @scr_str_release(ptr %es${i})`);
8682
+ d.push(` %es${i} = call ptr @scr_str_new(ptr ${this.cstr("buffer")}, ${this.sizeType} 6)`, ` %ed${i} = call ptr @scr_dyn_new_str(ptr %es${i})`, ` call void @scr_str_release(ptr %es${i})`);
8030
8683
  passed.push(`ptr %ed${i}`);
8031
8684
  }
8032
8685
  else if (isErrPos) {
@@ -8048,7 +8701,7 @@ class LlEmitter {
8048
8701
  }
8049
8702
  else if (isDonePos) {
8050
8703
  const glue = `scr_stream_done_dyn_${kind}`;
8051
- this.declare(`declare ptr @${glue}(ptr, ptr, i64)`);
8704
+ this.declare(`declare ptr @${glue}(ptr, ptr, ${this.sizeType})`);
8052
8705
  this.declare(`declare ptr @scr_dyn_new_func(ptr, ptr, i32, ptr, ptr)`);
8053
8706
  const clo = doneClosure(`@${glue}`, `dd${i}`);
8054
8707
  const arity = kind === "t" || kind === "l" ? 2 : 1;
@@ -8068,8 +8721,8 @@ class LlEmitter {
8068
8721
  }
8069
8722
  else if (isEncPos) {
8070
8723
  // Node's encoding for decoded (Buffer) chunks is 'buffer'.
8071
- this.declare(`declare ptr @scr_str_new(ptr, i64)`);
8072
- d.push(` %en${i} = call ptr @scr_str_new(ptr ${this.cstr("buffer")}, i64 6)`);
8724
+ this.declare(`declare ptr @scr_str_new(ptr, ${this.sizeType})`);
8725
+ d.push(` %en${i} = call ptr @scr_str_new(ptr ${this.cstr("buffer")}, ${this.sizeType} 6)`);
8073
8726
  passed.push(`ptr %en${i}`);
8074
8727
  }
8075
8728
  else if (isErrPos) {
@@ -8872,7 +9525,7 @@ class LlEmitter {
8872
9525
  if (slot === undefined)
8873
9526
  return null;
8874
9527
  const index = this.B.tmp();
8875
- this.B.line(`${index} = load i64, ptr ${slot}`);
9528
+ this.B.line(`${index} = load ${this.sizeType}, ptr ${slot}`);
8876
9529
  return index;
8877
9530
  }
8878
9531
  /** Bounds-check a typed-array element index without an out-of-line call
@@ -8884,16 +9537,16 @@ class LlEmitter {
8884
9537
  const lenPtr = B.tmp();
8885
9538
  const len = B.tmp();
8886
9539
  B.line(`${lenPtr} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
8887
- B.line(`${len} = load i64, ptr ${lenPtr}`);
9540
+ B.line(`${len} = load ${this.sizeType}, ptr ${lenPtr}`);
8888
9541
  if (integerIndex) {
8889
9542
  const inRange = B.tmp();
8890
- B.line(`${inRange} = icmp ult i64 ${index}, ${len}`);
9543
+ B.line(`${inRange} = icmp ult ${this.sizeType} ${index}, ${len}`);
8891
9544
  const invalid = B.newLabel("bytes.index.invalid");
8892
9545
  const valid = B.newLabel("bytes.index.valid");
8893
9546
  B.condBr(inRange, valid, invalid);
8894
9547
  B.startBlock(invalid);
8895
9548
  const indexF64 = B.tmp();
8896
- B.line(`${indexF64} = uitofp i64 ${index} to double`);
9549
+ B.line(`${indexF64} = uitofp ${this.sizeType} ${index} to double`);
8897
9550
  this.declare(`declare double @scr_bytes_get(ptr, double)`);
8898
9551
  B.line(`call double @scr_bytes_get(ptr ${receiver}, double ${indexF64})`);
8899
9552
  B.terminate("unreachable");
@@ -8904,7 +9557,7 @@ class LlEmitter {
8904
9557
  const nonnegative = B.tmp();
8905
9558
  const belowLen = B.tmp();
8906
9559
  const inRange = B.tmp();
8907
- B.line(`${lenF64} = uitofp i64 ${len} to double`);
9560
+ B.line(`${lenF64} = uitofp ${this.sizeType} ${len} to double`);
8908
9561
  B.line(`${nonnegative} = fcmp oge double ${index}, ${f64Lit(0)}`);
8909
9562
  B.line(`${belowLen} = fcmp olt double ${index}, ${lenF64}`);
8910
9563
  B.line(`${inRange} = and i1 ${nonnegative}, ${belowLen}`);
@@ -8916,8 +9569,8 @@ class LlEmitter {
8916
9569
  const idx = B.tmp();
8917
9570
  const roundTrip = B.tmp();
8918
9571
  const integral = B.tmp();
8919
- B.line(`${idx} = fptoui double ${index} to i64`);
8920
- B.line(`${roundTrip} = uitofp i64 ${idx} to double`);
9572
+ B.line(`${idx} = fptoui double ${index} to ${this.sizeType}`);
9573
+ B.line(`${roundTrip} = uitofp ${this.sizeType} ${idx} to double`);
8921
9574
  B.line(`${integral} = fcmp oeq double ${roundTrip}, ${index}`);
8922
9575
  B.condBr(integral, valid, invalid);
8923
9576
  B.startBlock(invalid);
@@ -8941,12 +9594,12 @@ class LlEmitter {
8941
9594
  const p = B.tmp();
8942
9595
  const len = B.tmp();
8943
9596
  B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
8944
- B.line(`${len} = load i64, ptr ${p}`);
9597
+ B.line(`${len} = load ${this.sizeType}, ptr ${p}`);
8945
9598
  const count = bytes && elem !== "u8" ? B.tmp() : len;
8946
9599
  if (count !== len)
8947
- B.line(`${count} = shl i64 ${len}, 2`);
9600
+ B.line(`${count} = shl ${this.sizeType} ${len}, 2`);
8948
9601
  const out = B.tmp();
8949
- B.line(`${out} = uitofp i64 ${count} to double`);
9602
+ B.line(`${out} = uitofp ${this.sizeType} ${count} to double`);
8950
9603
  return { name: out, type: F64 };
8951
9604
  }
8952
9605
  /** Kind-specialized typed-array load. */
@@ -8959,7 +9612,7 @@ class LlEmitter {
8959
9612
  const raw = B.tmp();
8960
9613
  const wide = B.tmp();
8961
9614
  const out = B.tmp();
8962
- B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
9615
+ B.line(`${p} = getelementptr inbounds i8, ptr ${data}, ${this.sizeType} ${idx}`);
8963
9616
  B.line(`${raw} = load i8, ptr ${p}, align 1`);
8964
9617
  B.line(`${wide} = zext i8 ${raw} to i32`);
8965
9618
  B.line(`${out} = uitofp i32 ${wide} to double`);
@@ -8968,14 +9621,14 @@ class LlEmitter {
8968
9621
  if (elem === "f32") {
8969
9622
  const raw = B.tmp();
8970
9623
  const out = B.tmp();
8971
- B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
9624
+ B.line(`${p} = getelementptr inbounds float, ptr ${data}, ${this.sizeType} ${idx}`);
8972
9625
  B.line(`${raw} = load float, ptr ${p}, align 1`);
8973
9626
  B.line(`${out} = fpext float ${raw} to double`);
8974
9627
  return { name: out, type: F64 };
8975
9628
  }
8976
9629
  const raw = B.tmp();
8977
9630
  const out = B.tmp();
8978
- B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
9631
+ B.line(`${p} = getelementptr inbounds i32, ptr ${data}, ${this.sizeType} ${idx}`);
8979
9632
  B.line(`${raw} = load i32, ptr ${p}, align 1`);
8980
9633
  B.line(`${out} = ${elem === "i32" ? "sitofp" : "uitofp"} i32 ${raw} to double`);
8981
9634
  return { name: out, type: F64 };
@@ -9053,18 +9706,18 @@ class LlEmitter {
9053
9706
  if (elem === "u8") {
9054
9707
  const byte = B.tmp();
9055
9708
  B.line(`${byte} = trunc i32 ${stored} to i8`);
9056
- B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
9709
+ B.line(`${p} = getelementptr inbounds i8, ptr ${data}, ${this.sizeType} ${idx}`);
9057
9710
  B.line(`store i8 ${byte}, ptr ${p}, align 1`);
9058
9711
  return;
9059
9712
  }
9060
9713
  if (elem === "f32") {
9061
9714
  const narrowed = B.tmp();
9062
9715
  B.line(`${narrowed} = fptrunc double ${value} to float`);
9063
- B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
9716
+ B.line(`${p} = getelementptr inbounds float, ptr ${data}, ${this.sizeType} ${idx}`);
9064
9717
  B.line(`store float ${narrowed}, ptr ${p}, align 1`);
9065
9718
  return;
9066
9719
  }
9067
- B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
9720
+ B.line(`${p} = getelementptr inbounds i32, ptr ${data}, ${this.sizeType} ${idx}`);
9068
9721
  B.line(`store i32 ${stored}, ptr ${p}, align 1`);
9069
9722
  }
9070
9723
  /** The bytesIntrinsic surface (emit-exprs.ts's switch, .ll flavored).
@@ -9698,7 +10351,7 @@ class LlEmitter {
9698
10351
  const commit = `${snapshot}_commit`;
9699
10352
  const check = this.dyn.dynCheckHelper(t);
9700
10353
  this.declare(`declare void @scr_arr_release(ptr)`);
9701
- this.resolveThunkDefs.push(`define internal void @${commit}(ptr %target, ptr %d) ${FN_ATTRS} { ; commit live stream element ${typeKey(t)}`, `entry:`, ` %next = call ptr @${check}(ptr %d, ptr null)`, ` %missing = icmp eq ptr %next, null`, ` br i1 %missing, label %done, label %swap`, `swap:`, ` %target_len_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 1`, ` %next_len_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 1`, ` %target_len = load i64, ptr %target_len_ptr`, ` %next_len = load i64, ptr %next_len_ptr`, ` store i64 %next_len, ptr %target_len_ptr`, ` store i64 %target_len, ptr %next_len_ptr`, ` %target_cap_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 2`, ` %next_cap_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 2`, ` %target_cap = load i64, ptr %target_cap_ptr`, ` %next_cap = load i64, ptr %next_cap_ptr`, ` store i64 %next_cap, ptr %target_cap_ptr`, ` store i64 %target_cap, ptr %next_cap_ptr`, ` %target_data_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 7`, ` %next_data_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 7`, ` %target_data = load ptr, ptr %target_data_ptr`, ` %next_data = load ptr, ptr %next_data_ptr`, ` store ptr %next_data, ptr %target_data_ptr`, ` store ptr %target_data, ptr %next_data_ptr`, ` call void @scr_arr_release(ptr %next)`, ` br label %done`, `done:`, ` ret void`, `}`, ``);
10354
+ this.resolveThunkDefs.push(`define internal void @${commit}(ptr %target, ptr %d) ${FN_ATTRS} { ; commit live stream element ${typeKey(t)}`, `entry:`, ` %next = call ptr @${check}(ptr %d, ptr null)`, ` %missing = icmp eq ptr %next, null`, ` br i1 %missing, label %done, label %swap`, `swap:`, ` %target_len_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 1`, ` %next_len_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 1`, ` %target_len = load ${this.sizeType}, ptr %target_len_ptr`, ` %next_len = load ${this.sizeType}, ptr %next_len_ptr`, ` store ${this.sizeType} %next_len, ptr %target_len_ptr`, ` store ${this.sizeType} %target_len, ptr %next_len_ptr`, ` %target_cap_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 2`, ` %next_cap_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 2`, ` %target_cap = load ${this.sizeType}, ptr %target_cap_ptr`, ` %next_cap = load ${this.sizeType}, ptr %next_cap_ptr`, ` store ${this.sizeType} %next_cap, ptr %target_cap_ptr`, ` store ${this.sizeType} %target_cap, ptr %next_cap_ptr`, ` %target_data_ptr = getelementptr inbounds %ScrArr, ptr %target, i64 0, i32 7`, ` %next_data_ptr = getelementptr inbounds %ScrArr, ptr %next, i64 0, i32 7`, ` %target_data = load ptr, ptr %target_data_ptr`, ` %next_data = load ptr, ptr %next_data_ptr`, ` store ptr %next_data, ptr %target_data_ptr`, ` store ptr %target_data, ptr %next_data_ptr`, ` call void @scr_arr_release(ptr %next)`, ` br label %done`, `done:`, ` ret void`, `}`, ``);
9702
10355
  return `@${commit}`;
9703
10356
  }
9704
10357
  if (t.kind !== "record")
@@ -9758,7 +10411,7 @@ class LlEmitter {
9758
10411
  }
9759
10412
  const sym = `sc_ldu_${this.liveDynUnionRefAdapters.size}`;
9760
10413
  this.liveDynUnionRefAdapters.set(key, sym);
9761
- this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
10414
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, ${this.sizeType}, ptr, ptr)`);
9762
10415
  const adapters = new Map();
9763
10416
  for (const { arm, tag } of mutableArms) {
9764
10417
  const prefix = `${sym}_${tag}`;
@@ -9782,7 +10435,7 @@ class LlEmitter {
9782
10435
  const boxed = B.tmp();
9783
10436
  B.line(`${payloadPtr} = getelementptr inbounds %ScrUnion, ptr %u, i64 0, i32 5`);
9784
10437
  B.line(`${payload} = load ptr, ptr ${payloadPtr}`);
9785
- B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${payload}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(armKey)}, i64 ${Buffer.byteLength(armKey, "utf8")}, ptr @${adapter.snapshot}, ptr ${adapter.commit})`);
10438
+ B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${payload}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(armKey)}, ${this.sizeType} ${Buffer.byteLength(armKey, "utf8")}, ptr @${adapter.snapshot}, ptr ${adapter.commit})`);
9786
10439
  B.terminate(`ret ptr ${boxed}`);
9787
10440
  });
9788
10441
  B.startBlock(fallback);
@@ -9809,8 +10462,8 @@ class LlEmitter {
9809
10462
  const nested = this.streamTypedRefMaterializeAdapter(t, ctx);
9810
10463
  const rc = vAdapters(this, t);
9811
10464
  const key = typeKey(t);
9812
- this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
9813
- B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${value}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(key)}, i64 ${Buffer.byteLength(key, "utf8")}, ptr @${nested.snapshot}, ptr ${nested.commit})`);
10465
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, ${this.sizeType}, ptr, ptr)`);
10466
+ B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${value}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(key)}, ${this.sizeType} ${Buffer.byteLength(key, "utf8")}, ptr @${nested.snapshot}, ptr ${nested.commit})`);
9814
10467
  return boxed;
9815
10468
  }
9816
10469
  /** LLVM twin of the C stream materializer: mutable reference children
@@ -9865,7 +10518,7 @@ class LlEmitter {
9865
10518
  }
9866
10519
  else {
9867
10520
  this.declare(`declare ptr @scr_dyn_new_obj()`);
9868
- this.declare(`declare void @scr_dyn_obj_set(ptr, ptr, i64, ptr)`);
10521
+ this.declare(`declare void @scr_dyn_obj_set(ptr, ptr, ${this.sizeType}, ptr)`);
9869
10522
  const out = B.tmp();
9870
10523
  B.line(`${out} = call ptr @scr_dyn_new_obj()`);
9871
10524
  const byName = new Map(shape.fields.map((field) => [field.name, field]));
@@ -9887,7 +10540,7 @@ class LlEmitter {
9887
10540
  fieldValue = boolValue;
9888
10541
  }
9889
10542
  const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
9890
- B.line(`call void @scr_dyn_obj_set(ptr ${out}, ptr ${this.cstr(field.name)}, i64 ${Buffer.byteLength(field.name, "utf8")}, ptr ${boxed})`);
10543
+ B.line(`call void @scr_dyn_obj_set(ptr ${out}, ptr ${this.cstr(field.name)}, ${this.sizeType} ${Buffer.byteLength(field.name, "utf8")}, ptr ${boxed})`);
9891
10544
  }
9892
10545
  B.terminate(`ret ptr ${out}`);
9893
10546
  }
@@ -9994,7 +10647,7 @@ class LlEmitter {
9994
10647
  B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(elem)}(ptr ${value})`);
9995
10648
  }
9996
10649
  else {
9997
- this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
10650
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, ${this.sizeType}, ptr, ptr)`);
9998
10651
  const boxedSlot = B.slot();
9999
10652
  B.entryAllocas.push(`${boxedSlot} = alloca ptr`);
10000
10653
  const tagPtr = B.tmp();
@@ -10016,7 +10669,7 @@ class LlEmitter {
10016
10669
  const armBoxed = B.tmp();
10017
10670
  B.line(`${payloadPtr} = getelementptr inbounds %ScrUnion, ptr ${value}, i64 0, i32 5`);
10018
10671
  B.line(`${payload} = load ptr, ptr ${payloadPtr}`);
10019
- B.line(`${armBoxed} = call ptr @scr_dyn_new_typed_ref(ptr ${payload}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(armKey)}, i64 ${Buffer.byteLength(armKey, "utf8")}, ptr @${armSnapshot}, ptr ${armCommit})`);
10672
+ B.line(`${armBoxed} = call ptr @scr_dyn_new_typed_ref(ptr ${payload}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${this.cstr(armKey)}, ${this.sizeType} ${Buffer.byteLength(armKey, "utf8")}, ptr @${armSnapshot}, ptr ${armCommit})`);
10020
10673
  B.line(`store ptr ${armBoxed}, ptr ${boxedSlot}`);
10021
10674
  B.br(join);
10022
10675
  this.resolveThunkDefs.push(`define internal ptr @${armSnapshot}(ptr %p) ${FN_ATTRS} { ; materialize stream union arm ${armKey}`, `entry:`, ` %d = call ptr @${this.dyn.toDynHelper(arm)}(ptr %p)`, ` ret ptr %d`, `}`, ``);
@@ -10043,8 +10696,8 @@ class LlEmitter {
10043
10696
  commit = this.streamTypedRefCommitAdapter(elem, snapshot);
10044
10697
  this.resolveThunkDefs.push(`define internal ptr @${snapshot}(ptr %p) ${FN_ATTRS} { ; materialize stream element ${key}`, `entry:`, ` %d = call ptr @${this.dyn.toDynHelper(elem)}(ptr %p)`, ` ret ptr %d`, `}`, ``);
10045
10698
  }
10046
- this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
10047
- B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${value}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${keyPtr}, i64 ${Buffer.byteLength(key, "utf8")}, ptr @${snapshot}, ptr ${commit})`);
10699
+ this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, ${this.sizeType}, ptr, ptr)`);
10700
+ B.line(`${boxed} = call ptr @scr_dyn_new_typed_ref(ptr ${value}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${keyPtr}, ${this.sizeType} ${Buffer.byteLength(key, "utf8")}, ptr @${snapshot}, ptr ${commit})`);
10048
10701
  }
10049
10702
  else {
10050
10703
  boxed = B.tmp();
@@ -10785,16 +11438,16 @@ class LlEmitter {
10785
11438
  const tag6 = infoDef.arms.findIndex((a) => a.kind === "record" && this.recordsById.get(a.shapeId)?.fields.find((f) => f.name === "scopeid")?.type.kind === "f64");
10786
11439
  const tag4 = 1 - tag6;
10787
11440
  this.declare(`declare ptr @scr_os_ifaddrs()`);
10788
- this.declare(`declare i64 @scr_os_ifaddrs_count(ptr)`);
10789
- this.declare(`declare ptr @scr_os_ifaddrs_name(ptr, i64)`);
10790
- this.declare(`declare ptr @scr_os_ifaddrs_address(ptr, i64)`);
10791
- this.declare(`declare ptr @scr_os_ifaddrs_netmask(ptr, i64)`);
10792
- this.declare(`declare ptr @scr_os_ifaddrs_family(ptr, i64)`);
10793
- this.declare(`declare ptr @scr_os_ifaddrs_mac(ptr, i64)`);
10794
- this.declare(`declare zeroext i1 @scr_os_ifaddrs_internal(ptr, i64)`);
10795
- this.declare(`declare zeroext i1 @scr_os_ifaddrs_ipv6(ptr, i64)`);
10796
- this.declare(`declare ptr @scr_os_ifaddrs_cidr(ptr, i64)`);
10797
- this.declare(`declare double @scr_os_ifaddrs_scopeid(ptr, i64)`);
11441
+ this.declare(`declare ${this.sizeType} @scr_os_ifaddrs_count(ptr)`);
11442
+ this.declare(`declare ptr @scr_os_ifaddrs_name(ptr, ${this.sizeType})`);
11443
+ this.declare(`declare ptr @scr_os_ifaddrs_address(ptr, ${this.sizeType})`);
11444
+ this.declare(`declare ptr @scr_os_ifaddrs_netmask(ptr, ${this.sizeType})`);
11445
+ this.declare(`declare ptr @scr_os_ifaddrs_family(ptr, ${this.sizeType})`);
11446
+ this.declare(`declare ptr @scr_os_ifaddrs_mac(ptr, ${this.sizeType})`);
11447
+ this.declare(`declare zeroext i1 @scr_os_ifaddrs_internal(ptr, ${this.sizeType})`);
11448
+ this.declare(`declare zeroext i1 @scr_os_ifaddrs_ipv6(ptr, ${this.sizeType})`);
11449
+ this.declare(`declare ptr @scr_os_ifaddrs_cidr(ptr, ${this.sizeType})`);
11450
+ this.declare(`declare double @scr_os_ifaddrs_scopeid(ptr, ${this.sizeType})`);
10798
11451
  this.declare(`declare void @scr_os_ifaddrs_free(ptr)`);
10799
11452
  this.declare(`declare ptr @scr_union_new_ref(i32, ptr, ptr, ptr, ptr)`);
10800
11453
  this.declare(`declare ptr @scr_union_retain_v(ptr)`);
@@ -10813,11 +11466,11 @@ class LlEmitter {
10813
11466
  const snap = B.tmp();
10814
11467
  const cnt = B.tmp();
10815
11468
  B.line(`${snap} = call ptr @scr_os_ifaddrs()`);
10816
- B.line(`${cnt} = call i64 @scr_os_ifaddrs_count(ptr ${snap})`);
11469
+ B.line(`${cnt} = call ${this.sizeType} @scr_os_ifaddrs_count(ptr ${snap})`);
10817
11470
  const iSlot = B.slot();
10818
11471
  const rowSlot = B.slot();
10819
- B.entryAllocas.push(`${iSlot} = alloca i64`, `${rowSlot} = alloca ptr`);
10820
- B.line(`store i64 0, ptr ${iSlot}`);
11472
+ B.entryAllocas.push(`${iSlot} = alloca ${this.sizeType}`, `${rowSlot} = alloca ptr`);
11473
+ B.line(`store ${this.sizeType} 0, ptr ${iSlot}`);
10821
11474
  const lc = B.newLabel("ni.c");
10822
11475
  const lb = B.newLabel("ni.b");
10823
11476
  const le = B.newLabel("ni.e");
@@ -10825,12 +11478,12 @@ class LlEmitter {
10825
11478
  B.startBlock(lc);
10826
11479
  const i = B.tmp();
10827
11480
  const cont = B.tmp();
10828
- B.line(`${i} = load i64, ptr ${iSlot}`);
10829
- B.line(`${cont} = icmp ult i64 ${i}, ${cnt}`);
11481
+ B.line(`${i} = load ${this.sizeType}, ptr ${iSlot}`);
11482
+ B.line(`${cont} = icmp ult ${this.sizeType} ${i}, ${cnt}`);
10830
11483
  B.condBr(cont, lb, le);
10831
11484
  B.startBlock(lb);
10832
11485
  const isV6 = B.tmp();
10833
- B.line(`${isV6} = call zeroext i1 @scr_os_ifaddrs_ipv6(ptr ${snap}, i64 ${i})`);
11486
+ B.line(`${isV6} = call zeroext i1 @scr_os_ifaddrs_ipv6(ptr ${snap}, ${this.sizeType} ${i})`);
10834
11487
  const l6 = B.newLabel("ni.v6");
10835
11488
  const l4 = B.newLabel("ni.v4");
10836
11489
  const lRow = B.newLabel("ni.r");
@@ -10857,14 +11510,14 @@ class LlEmitter {
10857
11510
  ["mac", "scr_os_ifaddrs_mac"],
10858
11511
  ]) {
10859
11512
  const v = B.tmp();
10860
- B.line(`${v} = call ptr @${sym}(ptr ${snap}, i64 ${i}) ; +1`);
11513
+ B.line(`${v} = call ptr @${sym}(ptr ${snap}, ${this.sizeType} ${i}) ; +1`);
10861
11514
  B.line(`store ptr ${v}, ptr ${this.recordFieldPtr(r, t.shapeId, field).ptr}`);
10862
11515
  }
10863
11516
  const internal = B.tmp();
10864
- B.line(`${internal} = call zeroext i1 @scr_os_ifaddrs_internal(ptr ${snap}, i64 ${i})`);
11517
+ B.line(`${internal} = call zeroext i1 @scr_os_ifaddrs_internal(ptr ${snap}, ${this.sizeType} ${i})`);
10865
11518
  this.storeField(this.recordFieldPtr(r, t.shapeId, "internal").ptr, { kind: "bool" }, internal);
10866
11519
  const cs = B.tmp();
10867
- B.line(`${cs} = call ptr @scr_os_ifaddrs_cidr(ptr ${snap}, i64 ${i}) ; +1 or null`);
11520
+ B.line(`${cs} = call ptr @scr_os_ifaddrs_cidr(ptr ${snap}, ${this.sizeType} ${i}) ; +1 or null`);
10868
11521
  const hasCidr = B.tmp();
10869
11522
  B.line(`${hasCidr} = icmp ne ptr ${cs}, null`);
10870
11523
  const lcs = B.newLabel("ni.cs");
@@ -10889,7 +11542,7 @@ class LlEmitter {
10889
11542
  B.line(`store ptr ${cv}, ptr ${this.recordFieldPtr(r, t.shapeId, "cidr").ptr}`);
10890
11543
  if (v6) {
10891
11544
  const sc = B.tmp();
10892
- B.line(`${sc} = call double @scr_os_ifaddrs_scopeid(ptr ${snap}, i64 ${i})`);
11545
+ B.line(`${sc} = call double @scr_os_ifaddrs_scopeid(ptr ${snap}, ${this.sizeType} ${i})`);
10893
11546
  this.storeField(this.recordFieldPtr(r, t.shapeId, "scopeid").ptr, F64, sc);
10894
11547
  }
10895
11548
  else {
@@ -10915,7 +11568,7 @@ class LlEmitter {
10915
11568
  const row = B.tmp();
10916
11569
  B.line(`${row} = load ptr, ptr ${rowSlot}`);
10917
11570
  const nm = B.tmp();
10918
- B.line(`${nm} = call ptr @scr_os_ifaddrs_name(ptr ${snap}, i64 ${i}) ; +1`);
11571
+ B.line(`${nm} = call ptr @scr_os_ifaddrs_name(ptr ${snap}, ${this.sizeType} ${i}) ; +1`);
10919
11572
  const cell = B.tmp();
10920
11573
  B.line(`${cell} = call ptr @scr_map_get_str_ref(ptr ${ovf}, ptr ${nm})`);
10921
11574
  const hasCell = B.tmp();
@@ -10951,8 +11604,8 @@ class LlEmitter {
10951
11604
  B.line(`call void @scr_arr_release(ptr ${rows})`);
10952
11605
  B.line(`call void @scr_str_release(ptr ${nm})`);
10953
11606
  const i2 = B.tmp();
10954
- B.line(`${i2} = add i64 ${i}, 1`);
10955
- B.line(`store i64 ${i2}, ptr ${iSlot}`);
11607
+ B.line(`${i2} = add ${this.sizeType} ${i}, 1`);
11608
+ B.line(`store ${this.sizeType} ${i2}, ptr ${iSlot}`);
10956
11609
  B.br(lc);
10957
11610
  B.startBlock(le);
10958
11611
  B.line(`call void @scr_os_ifaddrs_free(ptr ${snap})`);
@@ -10969,21 +11622,21 @@ class LlEmitter {
10969
11622
  const recT = e.type.elem;
10970
11623
  const path = this.emitExpr(e.args[0]);
10971
11624
  this.declare(`declare ptr @scr_fs_scandir(ptr)`);
10972
- this.declare(`declare i64 @scr_fs_scandir_count(ptr)`);
10973
- this.declare(`declare ptr @scr_fs_scandir_name(ptr, i64)`);
10974
- this.declare(`declare double @scr_fs_scandir_type(ptr, i64)`);
11625
+ this.declare(`declare ${this.sizeType} @scr_fs_scandir_count(ptr)`);
11626
+ this.declare(`declare ptr @scr_fs_scandir_name(ptr, ${this.sizeType})`);
11627
+ this.declare(`declare double @scr_fs_scandir_type(ptr, ${this.sizeType})`);
10975
11628
  this.declare(`declare void @scr_fs_scandir_free(ptr)`);
10976
11629
  const snap = B.tmp();
10977
11630
  B.line(`${snap} = call ptr @scr_fs_scandir(ptr ${path.name})`);
10978
11631
  this.emitPendingCheck();
10979
11632
  const cnt = B.tmp();
10980
- B.line(`${cnt} = call i64 @scr_fs_scandir_count(ptr ${snap})`);
11633
+ B.line(`${cnt} = call ${this.sizeType} @scr_fs_scandir_count(ptr ${snap})`);
10981
11634
  const arr = B.tmp();
10982
11635
  B.line(`${arr} = ${arrNewCall(this, recT, cnt)}`);
10983
11636
  const out = this.own({ name: arr, type: e.type });
10984
11637
  const iSlot = B.slot();
10985
- B.entryAllocas.push(`${iSlot} = alloca i64`);
10986
- B.line(`store i64 0, ptr ${iSlot}`);
11638
+ B.entryAllocas.push(`${iSlot} = alloca ${this.sizeType}`);
11639
+ B.line(`store ${this.sizeType} 0, ptr ${iSlot}`);
10987
11640
  const lc = B.newLabel("sd.c");
10988
11641
  const lb = B.newLabel("sd.b");
10989
11642
  const le = B.newLabel("sd.e");
@@ -10991,23 +11644,23 @@ class LlEmitter {
10991
11644
  B.startBlock(lc);
10992
11645
  const i = B.tmp();
10993
11646
  const cont = B.tmp();
10994
- B.line(`${i} = load i64, ptr ${iSlot}`);
10995
- B.line(`${cont} = icmp ult i64 ${i}, ${cnt}`);
11647
+ B.line(`${i} = load ${this.sizeType}, ptr ${iSlot}`);
11648
+ B.line(`${cont} = icmp ult ${this.sizeType} ${i}, ${cnt}`);
10996
11649
  B.condBr(cont, lb, le);
10997
11650
  B.startBlock(lb);
10998
11651
  const row = B.tmp();
10999
11652
  B.line(`${row} = call ptr @${mangleRecordNew(recT.shapeId)}()`);
11000
11653
  const dt = B.tmp();
11001
- B.line(`${dt} = call double @scr_fs_scandir_type(ptr ${snap}, i64 ${i})`);
11654
+ B.line(`${dt} = call double @scr_fs_scandir_type(ptr ${snap}, ${this.sizeType} ${i})`);
11002
11655
  this.storeField(this.recordFieldPtr(row, recT.shapeId, "%dtype").ptr, F64, dt);
11003
11656
  const nm = B.tmp();
11004
- B.line(`${nm} = call ptr @scr_fs_scandir_name(ptr ${snap}, i64 ${i}) ; +1`);
11657
+ B.line(`${nm} = call ptr @scr_fs_scandir_name(ptr ${snap}, ${this.sizeType} ${i}) ; +1`);
11005
11658
  B.line(`store ptr ${nm}, ptr ${this.recordFieldPtr(row, recT.shapeId, "name").ptr}`);
11006
11659
  B.line(`store ptr ${this.retainValue(path.name, STRING)}, ptr ${this.recordFieldPtr(row, recT.shapeId, "parentPath").ptr}`);
11007
11660
  this.arrPush(arr, "ref", row); // push takes ownership of the row
11008
11661
  const i2 = B.tmp();
11009
- B.line(`${i2} = add i64 ${i}, 1`);
11010
- B.line(`store i64 ${i2}, ptr ${iSlot}`);
11662
+ B.line(`${i2} = add ${this.sizeType} ${i}, 1`);
11663
+ B.line(`store ${this.sizeType} ${i2}, ptr ${iSlot}`);
11011
11664
  B.br(lc);
11012
11665
  B.startBlock(le);
11013
11666
  B.line(`call void @scr_fs_scandir_free(ptr ${snap})`);
@@ -11077,22 +11730,19 @@ class LlEmitter {
11077
11730
  }
11078
11731
  if (e.fn === "emitter.emit") {
11079
11732
  // The variadic dispatch: the event's unified tuple rides the C
11080
- // variadic tail POINTER-CLASSED (f64 as its i64 bit pattern, bool
11081
- // zero-extended — scr_runtime.h's fixed-shim contract; the emitted
11082
- // adapters re-type on the way out), every argument borrowed. May
11733
+ // variadic tail POINTER-CLASSED. Scalar values ride pointers to
11734
+ // call-lived stack slots, while reference values ride directly; the
11735
+ // fixed shim can therefore read one pointer-width slot per argument
11736
+ // on both wasm32 and native targets. Every argument is borrowed. May
11083
11737
  // throw (listeners run inside).
11084
11738
  const args = e.args.map((a) => this.emitExpr(a));
11085
11739
  const tuple = args.slice(2).map((a) => {
11086
11740
  const ty = this.llType(a.type);
11087
- if (ty === "double") {
11088
- const x = B.tmp();
11089
- B.line(`${x} = bitcast double ${a.name} to i64`);
11090
- return `i64 ${x}`;
11091
- }
11092
- if (ty === "i1") {
11093
- const x = B.tmp();
11094
- B.line(`${x} = zext i1 ${a.name} to i64`);
11095
- return `i64 ${x}`;
11741
+ if (ty === "double" || ty === "i1") {
11742
+ const slot = B.slot();
11743
+ B.entryAllocas.push(`${slot} = alloca ${ty} ; EventEmitter scalar argument`);
11744
+ B.line(`store ${ty} ${a.name}, ptr ${slot}`);
11745
+ return `ptr ${slot}`;
11096
11746
  }
11097
11747
  return `ptr ${a.name}`;
11098
11748
  });
@@ -11681,13 +12331,13 @@ class LlEmitter {
11681
12331
  throw new Error("llvm emitter bug: net.serverCloseBind result not a func");
11682
12332
  const args = e.args.map((a) => this.emitExpr(a));
11683
12333
  const fnSym = this.closeBindThunkFor(e.type.params[0], e.type.ret.kind === "netServer");
11684
- this.declare(`declare ptr @scr_closure_new(ptr, i64)`);
12334
+ this.declare(`declare ptr @scr_closure_new(ptr, ${this.sizeType})`);
11685
12335
  this.declare(`declare ptr @scr_box_new_obj(ptr, ptr, ptr)`);
11686
12336
  this.declare(`declare void @scr_box_set_ref(ptr, ptr)`);
11687
12337
  this.declare(`declare ptr @scr_net_server_retain_v(ptr)`);
11688
12338
  this.declare(`declare void @scr_net_server_release_v(ptr)`);
11689
12339
  const bound = B.tmp();
11690
- B.line(`${bound} = call ptr @scr_closure_new(ptr @${fnSym}, i64 1)`);
12340
+ B.line(`${bound} = call ptr @scr_closure_new(ptr @${fnSym}, ${this.sizeType} 1)`);
11691
12341
  const bx = B.tmp();
11692
12342
  B.line(`${bx} = call ptr @scr_box_new_obj(ptr @scr_net_server_retain_v, ptr @scr_net_server_release_v, ptr null)`);
11693
12343
  const capp = B.tmp();
@@ -11988,22 +12638,22 @@ class LlEmitter {
11988
12638
  if (ca.type.kind === "string") {
11989
12639
  caData = B.tmp();
11990
12640
  B.line(`${caLenPtr} = getelementptr inbounds %ScrStr, ptr ${ca.name}, i64 0, i32 1`);
11991
- B.line(`${caLen} = load i64, ptr ${caLenPtr}`);
11992
- B.line(`${caData} = getelementptr inbounds i8, ptr ${ca.name}, i64 24`);
12641
+ B.line(`${caLen} = load ${this.sizeType}, ptr ${caLenPtr}`);
12642
+ B.line(`${caData} = getelementptr inbounds i8, ptr ${ca.name}, i64 ${this.abiOffset(24, 12)}`);
11993
12643
  }
11994
12644
  else if (ca.type.kind === "bytes" && ca.type.elem === "u8") {
11995
12645
  const caDataPtr = B.tmp();
11996
12646
  caData = B.tmp();
11997
- B.line(`${caLenPtr} = getelementptr inbounds i8, ptr ${ca.name}, i64 8`);
11998
- B.line(`${caLen} = load i64, ptr ${caLenPtr}`);
11999
- B.line(`${caDataPtr} = getelementptr inbounds i8, ptr ${ca.name}, i64 24`);
12647
+ B.line(`${caLenPtr} = getelementptr inbounds i8, ptr ${ca.name}, i64 ${this.abiOffset(8, 4)}`);
12648
+ B.line(`${caLen} = load ${this.sizeType}, ptr ${caLenPtr}`);
12649
+ B.line(`${caDataPtr} = getelementptr inbounds i8, ptr ${ca.name}, i64 ${this.abiOffset(24, 12)}`);
12000
12650
  B.line(`${caData} = load ptr, ptr ${caDataPtr}`);
12001
12651
  }
12002
12652
  else {
12003
12653
  throw new Error(`llvm emitter bug: ${e.fn} CA is not a string or Buffer`);
12004
12654
  }
12005
- callArgs = [...callArgs.slice(0, 8), `ptr ${caData}`, `i64 ${caLen}`];
12006
- this.declare(`declare ptr @scr_https_request(ptr, double, ptr, ptr, double, ptr, i1 zeroext, i1 zeroext, ptr, i64, ptr, ptr)`);
12655
+ callArgs = [...callArgs.slice(0, 8), `ptr ${caData}`, `${this.sizeType} ${caLen}`];
12656
+ this.declare(`declare ptr @scr_https_request(ptr, double, ptr, ptr, double, ptr, i1 zeroext, i1 zeroext, ptr, ${this.sizeType}, ptr, ptr)`);
12007
12657
  }
12008
12658
  else {
12009
12659
  const decls = head.map((a) => (this.llType(a.type) === "i1" ? "i1 zeroext" : this.llType(a.type)));