@scriptc/compiler 0.0.21 → 0.0.23
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.
- package/ambient/scriptc-node-fallback.d.ts +117 -37
- package/dist/backend/cc-cache.test.d.ts +1 -0
- package/dist/backend/cc-cache.test.js +2507 -0
- package/dist/backend/cc-cache.test.js.map +1 -0
- package/dist/backend/cc.d.ts +69 -11
- package/dist/backend/cc.js +1937 -222
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-async.js +3 -1
- package/dist/backend/emission/emit-async.js.map +1 -1
- package/dist/backend/emission/emit-exprs.d.ts +10 -0
- package/dist/backend/emission/emit-exprs.js +482 -18
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-stmts.js +13 -5
- package/dist/backend/emission/emit-stmts.js.map +1 -1
- package/dist/backend/emission/emit-types.js +4 -1
- package/dist/backend/emission/emit-types.js.map +1 -1
- package/dist/backend/emission/emit-walkers.js +108 -7
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +27 -1
- package/dist/backend/emission/emitter.js +84 -3
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +1 -0
- package/dist/backend/llvm/dyn.js +305 -8
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +831 -23
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.js +3 -2
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/compat/fetch-profile.d.ts +118 -0
- package/dist/compat/fetch-profile.js +531 -0
- package/dist/compat/fetch-profile.js.map +1 -0
- package/dist/coverage/surface-manifest.js +69 -0
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +10 -0
- package/dist/diagnostics/diagnostic.js +27 -1
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +24 -15
- package/dist/frontend/lowering/lower-builtins.js +217 -69
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +41 -0
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +32 -11
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +3 -3
- package/dist/frontend/lowering/lower-exprs.js +87 -15
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.js +7 -1
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +123 -7
- package/dist/frontend/lowering/lower-island.js +1976 -26
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +17 -1
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +33 -9
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +40 -0
- package/dist/frontend/lowering/lowerer.js +290 -18
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +55 -12
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/program.d.ts +20 -4
- package/dist/frontend/program.js +391 -49
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/resolve.d.ts +10 -0
- package/dist/frontend/resolve.js +37 -0
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/ts7/enums.d.ts +9 -0
- package/dist/frontend/ts7/enums.js +8 -6
- package/dist/frontend/ts7/enums.js.map +1 -1
- package/dist/frontend/ts7/program.js +4 -1
- package/dist/frontend/ts7/program.js.map +1 -1
- package/dist/frontend/types.d.ts +16 -9
- package/dist/frontend/types.js +119 -38
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +70 -14
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +55 -26
- package/dist/ir/nodes.js +42 -29
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +106 -9
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/int-infer.js +7 -1
- package/dist/library/int-infer.js.map +1 -1
- package/dist/library/int-infer.test.js +80 -0
- package/dist/library/int-infer.test.js.map +1 -1
- package/dist/library/sidecar-validate.js +5 -0
- package/dist/library/sidecar-validate.js.map +1 -1
- package/dist/library/sidecar.d.ts +3 -0
- package/dist/library/sidecar.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +1097 -130
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canMarshalFuncIntoIsland, CAUGHT, DYN, F64, islandCallbackRet, islandPromisePayloadTag, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, moduleUsesDynInvoke, moduleUsesFetch, moduleUsesFsWatch, moduleUsesHttpServer, moduleUsesNet, moduleUsesProcessEvents, moduleUsesStream, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, STRING, typeEquals, typeKey, VOID } from "../../ir/nodes.js";
|
|
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";
|
|
2
2
|
import { computeMayThrow } from "../emission/may-throw.js";
|
|
3
3
|
import { mangleArgPack, mangleAsyncSpawn, mangleClassNew, mangleClassObj, mangleClassRetain, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenResThunk, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordNew, mangleRecordStruct, mangleResolveThunk, mangleTrampoline, mangleVtStruct, mangleWrapper } from "../mangle.js";
|
|
4
4
|
import { BlockBuilder } from "./blocks.js";
|
|
@@ -129,13 +129,36 @@ const LIB_FN_SYMS = {
|
|
|
129
129
|
"process.stderrWrite": "scr_process_stderr_write",
|
|
130
130
|
"process.isTTY": "scr_process_is_tty",
|
|
131
131
|
"date.now": "scr_date_now",
|
|
132
|
+
"date.newNow": "scr_date_now",
|
|
133
|
+
"date.newMs": "scr_date_new_ms",
|
|
134
|
+
"date.newString": "scr_date_parse_get_time",
|
|
135
|
+
"date.getTime": "scr_date_get_time",
|
|
136
|
+
"date.valueOf": "scr_date_get_time",
|
|
132
137
|
"date.parseGetTime": "scr_date_parse_get_time",
|
|
133
138
|
"date.utc": "scr_date_utc",
|
|
139
|
+
"date.getFullYear": "scr_date_get_full_year_local",
|
|
140
|
+
"date.getUTCFullYear": "scr_date_get_full_year_utc",
|
|
141
|
+
"date.getMonth": "scr_date_get_month_local",
|
|
142
|
+
"date.getUTCMonth": "scr_date_get_month_utc",
|
|
143
|
+
"date.getDate": "scr_date_get_date_local",
|
|
144
|
+
"date.getUTCDate": "scr_date_get_date_utc",
|
|
145
|
+
"date.getDay": "scr_date_get_day_local",
|
|
146
|
+
"date.getUTCDay": "scr_date_get_day_utc",
|
|
147
|
+
"date.getHours": "scr_date_get_hours_local",
|
|
148
|
+
"date.getUTCHours": "scr_date_get_hours_utc",
|
|
149
|
+
"date.getMinutes": "scr_date_get_minutes_local",
|
|
150
|
+
"date.getUTCMinutes": "scr_date_get_minutes_utc",
|
|
151
|
+
"date.getSeconds": "scr_date_get_seconds_local",
|
|
152
|
+
"date.getUTCSeconds": "scr_date_get_seconds_utc",
|
|
153
|
+
"date.getMilliseconds": "scr_date_get_milliseconds",
|
|
154
|
+
"date.getUTCMilliseconds": "scr_date_get_milliseconds",
|
|
155
|
+
"date.getTimezoneOffset": "scr_date_get_timezone_offset",
|
|
134
156
|
"fs.existsSync": "scr_fs_exists",
|
|
135
157
|
// ── the throwing slice (MAY_THROW_LIB_FNS members): the generic path
|
|
136
158
|
// emits the standard pending check after each — emit-exprs.ts's finish.
|
|
137
159
|
"process.cpuPrevValidate": "scr_cpu_prev_validate",
|
|
138
160
|
"date.toISOString": "scr_date_to_iso",
|
|
161
|
+
"date.toISOStringValue": "scr_date_to_iso",
|
|
139
162
|
"process.chdir": "scr_process_chdir",
|
|
140
163
|
"fs.writeFileSync": "scr_fs_write_file",
|
|
141
164
|
"fs.appendFileSync": "scr_fs_append_file",
|
|
@@ -351,6 +374,18 @@ const LIB_FN_SYMS = {
|
|
|
351
374
|
// defineProps throw Node's TypeErrors (all in the may-throw seed set —
|
|
352
375
|
// the generic path emits the standard pending check). typeof and the
|
|
353
376
|
// ambient-this read never throw. The fs dyn read is the sync-fs story.
|
|
377
|
+
"fetch.start": "scr_fetch_static",
|
|
378
|
+
"fetch.responseNew": "scr_fetch_response_new",
|
|
379
|
+
"fetch.responseJson": "scr_fetch_response_json",
|
|
380
|
+
"fetch.responseText": "scr_fetch_response_text",
|
|
381
|
+
"fetch.responseBytes": "scr_fetch_response_bytes",
|
|
382
|
+
"fetch.abortControllerNew": "scr_fetch_abort_controller_new",
|
|
383
|
+
"fetch.abortTimeout": "scr_fetch_abort_timeout",
|
|
384
|
+
"fetch.abortNow": "scr_fetch_abort_now",
|
|
385
|
+
"fetch.abortAny": "scr_fetch_abort_any",
|
|
386
|
+
"fetch.streamNew": "scr_fetch_stream_new",
|
|
387
|
+
"fetch.streamFrom": "scr_fetch_stream_from",
|
|
388
|
+
"fetch.readerRead": "scr_fetch_reader_read",
|
|
354
389
|
"json.parse": "scr_json_parse",
|
|
355
390
|
"dyn.keySet": "scr_dyn_key_set",
|
|
356
391
|
"dyn.iterPack": "scr_dyn_iter_pack",
|
|
@@ -626,6 +661,8 @@ const STREAM_CANONICAL_CBS = {
|
|
|
626
661
|
* the stream/emitter slice of emit-exprs.ts's markings, applied before
|
|
627
662
|
* dispatch so generic and special shapes share one table. */
|
|
628
663
|
const USES_TIMERS_LIB_FNS = new Set([
|
|
664
|
+
"fetch.start",
|
|
665
|
+
"fetch.abortTimeout", "fetch.streamNew", "fetch.streamFrom",
|
|
629
666
|
"readable.new", "writable.new", "duplex.new", "transform.new", "passthrough.new",
|
|
630
667
|
"readable.init", "writable.init", "duplex.init", "transform.init", "passthrough.init",
|
|
631
668
|
"readable.newDyn", "writable.newDyn", "duplex.newDyn", "transform.newDyn", "passthrough.newDyn",
|
|
@@ -770,6 +807,15 @@ class LlEmitter {
|
|
|
770
807
|
* typeKey → thunk symbol (CEmitter.resolveThunks). */
|
|
771
808
|
resolveThunks = new Map();
|
|
772
809
|
resolveThunkDefs = [];
|
|
810
|
+
/** ReadableStream.from adapters keep typed arrays by reference and box
|
|
811
|
+
* one current element per pull. */
|
|
812
|
+
streamFromArrayAdapters = new Map();
|
|
813
|
+
/** Identity-preserving static→dyn capsules used by Web APIs whose values
|
|
814
|
+
* remain directly observable (stream chunks and AbortSignal reasons). */
|
|
815
|
+
liveDynRefAdapters = new Map();
|
|
816
|
+
/** Runtime-arm dispatchers for live union values. */
|
|
817
|
+
liveDynUnionRefAdapters = new Map();
|
|
818
|
+
dynPromiseAdapters = new Map();
|
|
773
819
|
unionsById = new Map();
|
|
774
820
|
recordsById = new Map();
|
|
775
821
|
tracedShapes;
|
|
@@ -919,6 +965,7 @@ class LlEmitter {
|
|
|
919
965
|
llType(t) {
|
|
920
966
|
switch (t.kind) {
|
|
921
967
|
case "f64":
|
|
968
|
+
case "date":
|
|
922
969
|
return "double";
|
|
923
970
|
case "bool":
|
|
924
971
|
return "i1";
|
|
@@ -1038,6 +1085,7 @@ class LlEmitter {
|
|
|
1038
1085
|
// any island entry (the engine's lazy boot consults it) — cc.ts
|
|
1039
1086
|
// compiles scr_fetch.c on the same predicate.
|
|
1040
1087
|
const usesFetch = moduleUsesFetch(this.mod);
|
|
1088
|
+
const snapshotsTlsCa = moduleUsesTls(this.mod) || moduleUsesTlsCa(this.mod);
|
|
1041
1089
|
const hasRefGlobals = globals.some((g) => isRefCounted(g.type)) || fnValueProps.length > 0;
|
|
1042
1090
|
// Declared NOW — the extern block flushes before main assembles.
|
|
1043
1091
|
if (usesEvents)
|
|
@@ -1054,6 +1102,9 @@ class LlEmitter {
|
|
|
1054
1102
|
this.declare(`declare void @scr_http_dyn_install()`);
|
|
1055
1103
|
if (usesFetch)
|
|
1056
1104
|
this.declare(`declare void @scr_fetch_install()`);
|
|
1105
|
+
if (snapshotsTlsCa) {
|
|
1106
|
+
this.declare(`declare void @scr_tls_ca_install()`);
|
|
1107
|
+
}
|
|
1057
1108
|
if (usesEvents && hasRefGlobals) {
|
|
1058
1109
|
this.declare(`declare void @scr_run_exit_listeners(double)`);
|
|
1059
1110
|
this.declare(`declare i32 @scr_exit_code_hint_get()`);
|
|
@@ -1167,6 +1218,11 @@ class LlEmitter {
|
|
|
1167
1218
|
// scr_runtime.h's natural alignment: kind at 8, f64 at 16, b at 24,
|
|
1168
1219
|
// payload at 32).
|
|
1169
1220
|
`%ScrCaught = type { i64, i32, double, i8, ptr, ptr, ptr, ptr }`,
|
|
1221
|
+
// ScrBytes { rc, len, elem(i32+pad), data, backing }. Indexed
|
|
1222
|
+
// typed-array access GEPs through this directly: the IR type already
|
|
1223
|
+
// fixes elem, so the hot path needs neither a runtime kind load nor
|
|
1224
|
+
// the generic scr_bytes_get/set call.
|
|
1225
|
+
`%ScrBytes = type { i64, i64, i32, ptr, ptr }`,
|
|
1170
1226
|
// The capture box { rc, kind, obj_retain, obj_release, obj_trace,
|
|
1171
1227
|
// slot } — TDZ reads peek the payload slot (offset 40) directly.
|
|
1172
1228
|
`%ScrBox = type { i64, i32, ptr, ptr, ptr, i64 }`,
|
|
@@ -1298,7 +1354,7 @@ class LlEmitter {
|
|
|
1298
1354
|
...(usesEvents ? [` call void @scr_events_install()`] : []),
|
|
1299
1355
|
// fs.watch programs fill the loop's watch hooks the same way —
|
|
1300
1356
|
// scr_watch.c links only when this line is emitted.
|
|
1301
|
-
...(usesFsWatch ? [` call void @scr_watch_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
|
|
1357
|
+
...(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
|
|
1302
1358
|
? [` %top = call ptr @${mangleAsyncSpawn(this.mod.entry)}()`]
|
|
1303
1359
|
: [` call void @${mangleFunction(this.mod.entry)}()`]),
|
|
1304
1360
|
// Uncaught exception from top-level code: Node exits 1.
|
|
@@ -1671,6 +1727,7 @@ class LlEmitter {
|
|
|
1671
1727
|
tr.push(` call void @scr_promise_fulfill_void(ptr %pr)`);
|
|
1672
1728
|
break;
|
|
1673
1729
|
case "f64":
|
|
1730
|
+
case "date":
|
|
1674
1731
|
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
1675
1732
|
tr.push(` call void @scr_promise_fulfill_f64(ptr %pr, double %r)`);
|
|
1676
1733
|
break;
|
|
@@ -1826,6 +1883,7 @@ class LlEmitter {
|
|
|
1826
1883
|
tr.push(` br label %done ; void body: the done value is undefined (NONE)`);
|
|
1827
1884
|
break;
|
|
1828
1885
|
case "f64":
|
|
1886
|
+
case "date":
|
|
1829
1887
|
this.declare(`declare void @scr_gen_out_f64(ptr, double)`);
|
|
1830
1888
|
tr.push(` call void @scr_gen_out_f64(ptr %g, double %r)`, ` br label %done`);
|
|
1831
1889
|
break;
|
|
@@ -2047,7 +2105,7 @@ class LlEmitter {
|
|
|
2047
2105
|
const t = this.currentReturnType;
|
|
2048
2106
|
if (t.kind === "void")
|
|
2049
2107
|
this.B.terminate("ret void");
|
|
2050
|
-
else if (t.kind === "f64")
|
|
2108
|
+
else if (t.kind === "f64" || t.kind === "date")
|
|
2051
2109
|
this.B.terminate(`ret double ${f64Lit(0)}`);
|
|
2052
2110
|
else if (t.kind === "bool")
|
|
2053
2111
|
this.B.terminate("ret i1 false");
|
|
@@ -2079,7 +2137,10 @@ class LlEmitter {
|
|
|
2079
2137
|
emitThrowValue(v) {
|
|
2080
2138
|
const B = this.B;
|
|
2081
2139
|
const t = v.type;
|
|
2082
|
-
if (t.kind === "
|
|
2140
|
+
if (t.kind === "date") {
|
|
2141
|
+
throw new Error("LLVM emitter bug: Date throw reached backend");
|
|
2142
|
+
}
|
|
2143
|
+
else if (t.kind === "f64") {
|
|
2083
2144
|
this.declare(`declare void @scr_throw_f64(double)`);
|
|
2084
2145
|
B.line(`call void @scr_throw_f64(double ${v.name})`);
|
|
2085
2146
|
}
|
|
@@ -2128,6 +2189,8 @@ class LlEmitter {
|
|
|
2128
2189
|
B.line(`${t} = icmp ne i64 ${len}, 0`);
|
|
2129
2190
|
return t;
|
|
2130
2191
|
}
|
|
2192
|
+
case "date":
|
|
2193
|
+
return "true";
|
|
2131
2194
|
case "array":
|
|
2132
2195
|
case "record":
|
|
2133
2196
|
case "object":
|
|
@@ -2222,6 +2285,9 @@ class LlEmitter {
|
|
|
2222
2285
|
B.line(`store i1 ${t}, ptr ${slot}`);
|
|
2223
2286
|
break;
|
|
2224
2287
|
}
|
|
2288
|
+
case "date":
|
|
2289
|
+
B.line(`store i1 true, ptr ${slot} ; Date objects are truthy`);
|
|
2290
|
+
break;
|
|
2225
2291
|
case "array":
|
|
2226
2292
|
case "record":
|
|
2227
2293
|
case "object":
|
|
@@ -2766,13 +2832,16 @@ class LlEmitter {
|
|
|
2766
2832
|
}
|
|
2767
2833
|
case "bytesSet": {
|
|
2768
2834
|
// Typed-array element write: same evaluation order as arraySet;
|
|
2769
|
-
// the value is a scalar (the
|
|
2770
|
-
// ownership moves. Any invalid index traps — no
|
|
2771
|
-
|
|
2835
|
+
// the value is a scalar (the kind-specific inline path coerces
|
|
2836
|
+
// JS-exactly), so no ownership moves. Any invalid index traps — no
|
|
2837
|
+
// append. IrBytesElem is static, so never rediscover it through the
|
|
2838
|
+
// generic runtime switch in a hot loop.
|
|
2839
|
+
const arr = this.emitBytesReceiver(s.arr, [s.index, s.value]);
|
|
2772
2840
|
const idx = this.emitExpr(s.index);
|
|
2773
2841
|
const v = this.emitExpr(s.value);
|
|
2774
|
-
|
|
2775
|
-
|
|
2842
|
+
if (s.arr.type.kind !== "bytes")
|
|
2843
|
+
throw new Error("llvm emitter bug: bytesSet on non-bytes");
|
|
2844
|
+
this.emitBytesSet(s.arr.type.elem, arr.name, idx.name, v.name);
|
|
2776
2845
|
break;
|
|
2777
2846
|
}
|
|
2778
2847
|
case "fieldSet":
|
|
@@ -5255,7 +5324,7 @@ class LlEmitter {
|
|
|
5255
5324
|
throw new Error("llvm emitter bug: yieldExpr with no operand (frontend fills undefined)");
|
|
5256
5325
|
const v = this.emitExpr(e.value);
|
|
5257
5326
|
const yt = e.value.type;
|
|
5258
|
-
if (yt.kind === "f64") {
|
|
5327
|
+
if (yt.kind === "f64" || yt.kind === "date") {
|
|
5259
5328
|
this.declare(`declare void @scr_gen_yield_f64(double)`);
|
|
5260
5329
|
B.line(`call void @scr_gen_yield_f64(double ${v.name})`);
|
|
5261
5330
|
}
|
|
@@ -5275,7 +5344,7 @@ class LlEmitter {
|
|
|
5275
5344
|
return { name: "", type: e.type };
|
|
5276
5345
|
}
|
|
5277
5346
|
const t = B.tmp();
|
|
5278
|
-
if (e.type.kind === "f64") {
|
|
5347
|
+
if (e.type.kind === "f64" || e.type.kind === "date") {
|
|
5279
5348
|
this.declare(`declare double @scr_gen_take_in_f64()`);
|
|
5280
5349
|
B.line(`${t} = call double @scr_gen_take_in_f64()`);
|
|
5281
5350
|
return { name: t, type: e.type };
|
|
@@ -5307,7 +5376,7 @@ class LlEmitter {
|
|
|
5307
5376
|
store(a.name, e.arg.type);
|
|
5308
5377
|
};
|
|
5309
5378
|
const parkIn = (name, t) => {
|
|
5310
|
-
if (t.kind === "f64") {
|
|
5379
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5311
5380
|
this.declare(`declare void @scr_gen_in_f64(ptr, double)`);
|
|
5312
5381
|
B.line(`call void @scr_gen_in_f64(ptr ${g.name}, double ${name})`);
|
|
5313
5382
|
}
|
|
@@ -5353,7 +5422,7 @@ class LlEmitter {
|
|
|
5353
5422
|
}
|
|
5354
5423
|
else {
|
|
5355
5424
|
sendArg((name, t) => {
|
|
5356
|
-
if (t.kind === "f64") {
|
|
5425
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5357
5426
|
this.declare(`declare void @scr_gen_ret_f64(ptr, double)`);
|
|
5358
5427
|
B.line(`call void @scr_gen_ret_f64(ptr ${g.name}, double ${name})`);
|
|
5359
5428
|
}
|
|
@@ -5406,7 +5475,7 @@ class LlEmitter {
|
|
|
5406
5475
|
return { name: "", type: e.type };
|
|
5407
5476
|
}
|
|
5408
5477
|
const t = B.tmp();
|
|
5409
|
-
if (e.type.kind === "f64") {
|
|
5478
|
+
if (e.type.kind === "f64" || e.type.kind === "date") {
|
|
5410
5479
|
this.declare(`declare double @scr_await_f64(ptr)`);
|
|
5411
5480
|
B.line(`${t} = call double @scr_await_f64(ptr ${pr.name})`);
|
|
5412
5481
|
}
|
|
@@ -5486,7 +5555,7 @@ class LlEmitter {
|
|
|
5486
5555
|
B.startBlock(lp);
|
|
5487
5556
|
const peek = this.unionPeek(u.name);
|
|
5488
5557
|
let awaited;
|
|
5489
|
-
if (inner.kind === "f64") {
|
|
5558
|
+
if (inner.kind === "f64" || inner.kind === "date") {
|
|
5490
5559
|
this.declare(`declare double @scr_await_f64(ptr)`);
|
|
5491
5560
|
const x = B.tmp();
|
|
5492
5561
|
B.line(`${x} = call double @scr_await_f64(ptr ${peek})`);
|
|
@@ -5563,7 +5632,7 @@ class LlEmitter {
|
|
|
5563
5632
|
return out;
|
|
5564
5633
|
}
|
|
5565
5634
|
let resolve;
|
|
5566
|
-
const kindNums = { f64: 0, bool: 1, string: 2, void: 3 };
|
|
5635
|
+
const kindNums = { f64: 0, date: 0, bool: 1, string: 2, void: 3 };
|
|
5567
5636
|
const kindNum = kindNums[inner.kind];
|
|
5568
5637
|
if (kindNum !== undefined) {
|
|
5569
5638
|
this.declare(`declare ptr @scr_make_resolve(ptr, i32)`);
|
|
@@ -5609,7 +5678,7 @@ class LlEmitter {
|
|
|
5609
5678
|
this.declare(`declare ptr @scr_make_reject(ptr)`);
|
|
5610
5679
|
const p = B.tmp();
|
|
5611
5680
|
B.line(`${p} = call ptr @scr_promise_new()`);
|
|
5612
|
-
const kindNums = { f64: 0, bool: 1, string: 2, void: 3 };
|
|
5681
|
+
const kindNums = { f64: 0, date: 0, bool: 1, string: 2, void: 3 };
|
|
5613
5682
|
const kindNum = kindNums[inner.kind];
|
|
5614
5683
|
const resolve = B.tmp();
|
|
5615
5684
|
if (kindNum !== undefined) {
|
|
@@ -5724,7 +5793,7 @@ class LlEmitter {
|
|
|
5724
5793
|
}
|
|
5725
5794
|
const v = this.emitExpr(e.args[0]);
|
|
5726
5795
|
const t = e.args[0].type;
|
|
5727
|
-
if (t.kind === "f64") {
|
|
5796
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5728
5797
|
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
5729
5798
|
B.line(`call void @scr_promise_fulfill_f64(ptr ${p}, double ${v.name})`);
|
|
5730
5799
|
}
|
|
@@ -5821,6 +5890,29 @@ class LlEmitter {
|
|
|
5821
5890
|
return this.own({ name: t, type: e.type });
|
|
5822
5891
|
}
|
|
5823
5892
|
const v = this.emitExpr(e.value);
|
|
5893
|
+
if (e.liveRef) {
|
|
5894
|
+
if (v.type.kind === "union") {
|
|
5895
|
+
const adapter = this.liveDynUnionRefAdapter(v.type);
|
|
5896
|
+
const boxed = B.tmp();
|
|
5897
|
+
B.line(`${boxed} = call ptr @${adapter}(ptr ${v.name})`);
|
|
5898
|
+
return this.own({ name: boxed, type: e.type });
|
|
5899
|
+
}
|
|
5900
|
+
if (!this.streamTypedRefEligible(v.type)) {
|
|
5901
|
+
throw new Error(`llvm emitter bug: live dyn ref of ${typeKey(v.type)}`);
|
|
5902
|
+
}
|
|
5903
|
+
const key = typeKey(v.type);
|
|
5904
|
+
let adapter = this.liveDynRefAdapters.get(key);
|
|
5905
|
+
if (!adapter) {
|
|
5906
|
+
const prefix = `sc_ldr_${this.liveDynRefAdapters.size}`;
|
|
5907
|
+
adapter = this.streamTypedRefMaterializeAdapter(v.type, { prefix, adapters: new Map() }, `${prefix}_materialize`);
|
|
5908
|
+
this.liveDynRefAdapters.set(key, adapter);
|
|
5909
|
+
}
|
|
5910
|
+
const rc = vAdapters(this, v.type);
|
|
5911
|
+
this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
|
|
5912
|
+
const boxed = B.tmp();
|
|
5913
|
+
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})`);
|
|
5914
|
+
return this.own({ name: boxed, type: e.type });
|
|
5915
|
+
}
|
|
5824
5916
|
if (v.type.kind === "func") {
|
|
5825
5917
|
// A closure boxes as the checked-dynamic tree's function kind: retained closure +
|
|
5826
5918
|
// the per-signature call thunk + the interned signature key. The
|
|
@@ -6560,6 +6652,15 @@ class LlEmitter {
|
|
|
6560
6652
|
return t;
|
|
6561
6653
|
});
|
|
6562
6654
|
}
|
|
6655
|
+
case "callFnThis": {
|
|
6656
|
+
const pack = argPack(args.slice(2).map((x) => x.name));
|
|
6657
|
+
this.declare(`declare ptr @scr_jsval_call_this(ptr, ptr, i32, ptr)`);
|
|
6658
|
+
return fallible(() => {
|
|
6659
|
+
const t = B.tmp();
|
|
6660
|
+
B.line(`${t} = call ptr @scr_jsval_call_this(ptr ${a(0)}, ptr ${a(1)}, i32 ${args.length - 2}, ptr ${pack})`);
|
|
6661
|
+
return t;
|
|
6662
|
+
});
|
|
6663
|
+
}
|
|
6563
6664
|
case "callSpread": {
|
|
6564
6665
|
// Spread application (`f(...pre, ...s)`): the prelude helper's
|
|
6565
6666
|
// real spread syntax — iterator protocols are the engine's own,
|
|
@@ -8568,6 +8669,233 @@ class LlEmitter {
|
|
|
8568
8669
|
}
|
|
8569
8670
|
}
|
|
8570
8671
|
}
|
|
8672
|
+
/** Whether an index/value expression can overwrite the bytes receiver
|
|
8673
|
+
* binding. Kept deliberately conservative; uncertain or calling shapes
|
|
8674
|
+
* retain the ordinary owned-receiver snapshot. */
|
|
8675
|
+
isStableBytesOperand(e, receiverLocalId) {
|
|
8676
|
+
switch (e.kind) {
|
|
8677
|
+
case "numLit":
|
|
8678
|
+
case "boolLit":
|
|
8679
|
+
case "varRef":
|
|
8680
|
+
case "incDec":
|
|
8681
|
+
return true;
|
|
8682
|
+
case "assignExpr":
|
|
8683
|
+
// A bytes assignment nested under truthiness/ternary can still
|
|
8684
|
+
// produce the numeric index/value expected by the outer access.
|
|
8685
|
+
return (e.localId !== receiverLocalId &&
|
|
8686
|
+
this.isStableBytesOperand(e.value, receiverLocalId));
|
|
8687
|
+
case "bin":
|
|
8688
|
+
return (this.isStableBytesOperand(e.left, receiverLocalId) &&
|
|
8689
|
+
this.isStableBytesOperand(e.right, receiverLocalId));
|
|
8690
|
+
case "unary":
|
|
8691
|
+
case "toBool":
|
|
8692
|
+
return this.isStableBytesOperand(e.operand, receiverLocalId);
|
|
8693
|
+
case "logical":
|
|
8694
|
+
return (this.isStableBytesOperand(e.left, receiverLocalId) &&
|
|
8695
|
+
this.isStableBytesOperand(e.right, receiverLocalId));
|
|
8696
|
+
case "ternary":
|
|
8697
|
+
return (this.isStableBytesOperand(e.cond, receiverLocalId) &&
|
|
8698
|
+
this.isStableBytesOperand(e.then, receiverLocalId) &&
|
|
8699
|
+
this.isStableBytesOperand(e.else_, receiverLocalId));
|
|
8700
|
+
case "bytesIntrinsic":
|
|
8701
|
+
return ((e.method === "get" || e.method === "length" || e.method === "byteLength") &&
|
|
8702
|
+
e.receiver.kind === "varRef" &&
|
|
8703
|
+
e.args.every((arg) => this.isStableBytesOperand(arg, receiverLocalId)));
|
|
8704
|
+
default:
|
|
8705
|
+
return false;
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
/** Borrow a direct, unboxed bytes binding when later operands are stable.
|
|
8709
|
+
* Its scope/global owner keeps it alive through the access. */
|
|
8710
|
+
emitBytesReceiver(receiver, following) {
|
|
8711
|
+
if (receiver.kind === "varRef" &&
|
|
8712
|
+
following.every((operand) => this.isStableBytesOperand(operand, receiver.localId))) {
|
|
8713
|
+
const b = this.binding(receiver.localId);
|
|
8714
|
+
if (b.kind !== "boxed") {
|
|
8715
|
+
const value = this.B.tmp();
|
|
8716
|
+
this.B.line(`${value} = load ptr, ptr ${b.slot}`);
|
|
8717
|
+
return { name: value, type: receiver.type };
|
|
8718
|
+
}
|
|
8719
|
+
}
|
|
8720
|
+
return this.emitExpr(receiver);
|
|
8721
|
+
}
|
|
8722
|
+
/** Bounds-check a typed-array element index without an out-of-line call
|
|
8723
|
+
* on the valid path. The range test rejects NaN/infinity before fptoui;
|
|
8724
|
+
* the integer round trip then rejects fractions. The error block retains
|
|
8725
|
+
* the runtime's exact trap text. Leaves the builder in the valid block. */
|
|
8726
|
+
emitBytesIndex(receiver, index) {
|
|
8727
|
+
const B = this.B;
|
|
8728
|
+
const lenPtr = B.tmp();
|
|
8729
|
+
const len = B.tmp();
|
|
8730
|
+
const lenF64 = B.tmp();
|
|
8731
|
+
const nonnegative = B.tmp();
|
|
8732
|
+
const belowLen = B.tmp();
|
|
8733
|
+
const inRange = B.tmp();
|
|
8734
|
+
B.line(`${lenPtr} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
|
|
8735
|
+
B.line(`${len} = load i64, ptr ${lenPtr}`);
|
|
8736
|
+
B.line(`${lenF64} = uitofp i64 ${len} to double`);
|
|
8737
|
+
B.line(`${nonnegative} = fcmp oge double ${index}, ${f64Lit(0)}`);
|
|
8738
|
+
B.line(`${belowLen} = fcmp olt double ${index}, ${lenF64}`);
|
|
8739
|
+
B.line(`${inRange} = and i1 ${nonnegative}, ${belowLen}`);
|
|
8740
|
+
const rangeOk = B.newLabel("bytes.index.range");
|
|
8741
|
+
const invalid = B.newLabel("bytes.index.invalid");
|
|
8742
|
+
const valid = B.newLabel("bytes.index.valid");
|
|
8743
|
+
B.condBr(inRange, rangeOk, invalid);
|
|
8744
|
+
B.startBlock(rangeOk);
|
|
8745
|
+
const idx = B.tmp();
|
|
8746
|
+
const roundTrip = B.tmp();
|
|
8747
|
+
const integral = B.tmp();
|
|
8748
|
+
B.line(`${idx} = fptoui double ${index} to i64`);
|
|
8749
|
+
B.line(`${roundTrip} = uitofp i64 ${idx} to double`);
|
|
8750
|
+
B.line(`${integral} = fcmp oeq double ${roundTrip}, ${index}`);
|
|
8751
|
+
B.condBr(integral, valid, invalid);
|
|
8752
|
+
B.startBlock(invalid);
|
|
8753
|
+
this.declare(`declare double @scr_bytes_get(ptr, double)`);
|
|
8754
|
+
B.line(`call double @scr_bytes_get(ptr ${receiver}, double ${index})`);
|
|
8755
|
+
B.terminate("unreachable");
|
|
8756
|
+
B.startBlock(valid);
|
|
8757
|
+
return idx;
|
|
8758
|
+
}
|
|
8759
|
+
/** Load the raw data pointer from ScrBytes after the bounds check. */
|
|
8760
|
+
emitBytesData(receiver) {
|
|
8761
|
+
const p = this.B.tmp();
|
|
8762
|
+
const data = this.B.tmp();
|
|
8763
|
+
this.B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 3`);
|
|
8764
|
+
this.B.line(`${data} = load ptr, ptr ${p}`);
|
|
8765
|
+
return data;
|
|
8766
|
+
}
|
|
8767
|
+
/** Static typed-array length/byteLength load. */
|
|
8768
|
+
emitBytesLength(elem, receiver, bytes) {
|
|
8769
|
+
const B = this.B;
|
|
8770
|
+
const p = B.tmp();
|
|
8771
|
+
const len = B.tmp();
|
|
8772
|
+
B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
|
|
8773
|
+
B.line(`${len} = load i64, ptr ${p}`);
|
|
8774
|
+
const count = bytes && elem !== "u8" ? B.tmp() : len;
|
|
8775
|
+
if (count !== len)
|
|
8776
|
+
B.line(`${count} = shl i64 ${len}, 2`);
|
|
8777
|
+
const out = B.tmp();
|
|
8778
|
+
B.line(`${out} = uitofp i64 ${count} to double`);
|
|
8779
|
+
return { name: out, type: F64 };
|
|
8780
|
+
}
|
|
8781
|
+
/** Kind-specialized typed-array load. */
|
|
8782
|
+
emitBytesGet(elem, receiver, index) {
|
|
8783
|
+
const B = this.B;
|
|
8784
|
+
const idx = this.emitBytesIndex(receiver, index);
|
|
8785
|
+
const data = this.emitBytesData(receiver);
|
|
8786
|
+
const p = B.tmp();
|
|
8787
|
+
if (elem === "u8") {
|
|
8788
|
+
const raw = B.tmp();
|
|
8789
|
+
const wide = B.tmp();
|
|
8790
|
+
const out = B.tmp();
|
|
8791
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
|
|
8792
|
+
B.line(`${raw} = load i8, ptr ${p}, align 1`);
|
|
8793
|
+
B.line(`${wide} = zext i8 ${raw} to i32`);
|
|
8794
|
+
B.line(`${out} = uitofp i32 ${wide} to double`);
|
|
8795
|
+
return { name: out, type: F64 };
|
|
8796
|
+
}
|
|
8797
|
+
if (elem === "f32") {
|
|
8798
|
+
const raw = B.tmp();
|
|
8799
|
+
const out = B.tmp();
|
|
8800
|
+
B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
|
|
8801
|
+
B.line(`${raw} = load float, ptr ${p}, align 1`);
|
|
8802
|
+
B.line(`${out} = fpext float ${raw} to double`);
|
|
8803
|
+
return { name: out, type: F64 };
|
|
8804
|
+
}
|
|
8805
|
+
const raw = B.tmp();
|
|
8806
|
+
const out = B.tmp();
|
|
8807
|
+
B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
|
|
8808
|
+
B.line(`${raw} = load i32, ptr ${p}, align 1`);
|
|
8809
|
+
B.line(`${out} = ${elem === "i32" ? "sitofp" : "uitofp"} i32 ${raw} to double`);
|
|
8810
|
+
return { name: out, type: F64 };
|
|
8811
|
+
}
|
|
8812
|
+
/** ToUint32/ToInt32 coercion with the overwhelmingly common finite,
|
|
8813
|
+
* exactly-representable JS-number range on a cast-only path. Exceptional
|
|
8814
|
+
* and huge values remain inline in the program TU so unrelated runtime
|
|
8815
|
+
* binaries do not acquire another exported helper. */
|
|
8816
|
+
emitBytesU32(value) {
|
|
8817
|
+
const B = this.B;
|
|
8818
|
+
const aboveMin = B.tmp();
|
|
8819
|
+
const belowMax = B.tmp();
|
|
8820
|
+
const fast = B.tmp();
|
|
8821
|
+
B.line(`${aboveMin} = fcmp oge double ${value}, ${f64Lit(-9007199254740992)}`);
|
|
8822
|
+
B.line(`${belowMax} = fcmp ole double ${value}, ${f64Lit(9007199254740992)}`);
|
|
8823
|
+
B.line(`${fast} = and i1 ${aboveMin}, ${belowMax}`);
|
|
8824
|
+
const fastLabel = B.newLabel("bytes.coerce.fast");
|
|
8825
|
+
const slowLabel = B.newLabel("bytes.coerce.slow");
|
|
8826
|
+
const done = B.newLabel("bytes.coerce.done");
|
|
8827
|
+
B.condBr(fast, fastLabel, slowLabel);
|
|
8828
|
+
B.startBlock(fastLabel);
|
|
8829
|
+
const signed = B.tmp();
|
|
8830
|
+
const fastU32 = B.tmp();
|
|
8831
|
+
B.line(`${signed} = fptosi double ${value} to i64`);
|
|
8832
|
+
B.line(`${fastU32} = trunc i64 ${signed} to i32`);
|
|
8833
|
+
B.br(done);
|
|
8834
|
+
B.startBlock(slowLabel);
|
|
8835
|
+
const ordered = B.tmp();
|
|
8836
|
+
const belowInf = B.tmp();
|
|
8837
|
+
const aboveNegInf = B.tmp();
|
|
8838
|
+
const finiteRange = B.tmp();
|
|
8839
|
+
const finite = B.tmp();
|
|
8840
|
+
B.line(`${ordered} = fcmp ord double ${value}, ${value}`);
|
|
8841
|
+
B.line(`${belowInf} = fcmp olt double ${value}, ${F64_INF}`);
|
|
8842
|
+
B.line(`${aboveNegInf} = fcmp ogt double ${value}, ${f64Lit(-Infinity)}`);
|
|
8843
|
+
B.line(`${finiteRange} = and i1 ${belowInf}, ${aboveNegInf}`);
|
|
8844
|
+
B.line(`${finite} = and i1 ${ordered}, ${finiteRange}`);
|
|
8845
|
+
const finiteLabel = B.newLabel("bytes.coerce.finite");
|
|
8846
|
+
const nonfiniteLabel = B.newLabel("bytes.coerce.nonfinite");
|
|
8847
|
+
const slowDone = B.newLabel("bytes.coerce.slow.done");
|
|
8848
|
+
B.condBr(finite, finiteLabel, nonfiniteLabel);
|
|
8849
|
+
B.startBlock(finiteLabel);
|
|
8850
|
+
this.declare(`declare double @llvm.trunc.f64(double)`);
|
|
8851
|
+
const truncated = B.tmp();
|
|
8852
|
+
const residue = B.tmp();
|
|
8853
|
+
const negative = B.tmp();
|
|
8854
|
+
const wrapped = B.tmp();
|
|
8855
|
+
const normalized = B.tmp();
|
|
8856
|
+
const finiteU32 = B.tmp();
|
|
8857
|
+
B.line(`${truncated} = call double @llvm.trunc.f64(double ${value})`);
|
|
8858
|
+
B.line(`${residue} = frem double ${truncated}, ${f64Lit(4294967296)}`);
|
|
8859
|
+
B.line(`${negative} = fcmp olt double ${residue}, ${f64Lit(0)}`);
|
|
8860
|
+
B.line(`${wrapped} = fadd double ${residue}, ${f64Lit(4294967296)}`);
|
|
8861
|
+
B.line(`${normalized} = select i1 ${negative}, double ${wrapped}, double ${residue}`);
|
|
8862
|
+
B.line(`${finiteU32} = fptoui double ${normalized} to i32`);
|
|
8863
|
+
B.br(slowDone);
|
|
8864
|
+
B.startBlock(nonfiniteLabel);
|
|
8865
|
+
B.br(slowDone);
|
|
8866
|
+
B.startBlock(slowDone);
|
|
8867
|
+
const slowU32 = B.tmp();
|
|
8868
|
+
B.line(`${slowU32} = phi i32 [ ${finiteU32}, %${finiteLabel} ], [ 0, %${nonfiniteLabel} ]`);
|
|
8869
|
+
B.br(done);
|
|
8870
|
+
B.startBlock(done);
|
|
8871
|
+
const out = B.tmp();
|
|
8872
|
+
B.line(`${out} = phi i32 [ ${fastU32}, %${fastLabel} ], [ ${slowU32}, %${slowDone} ]`);
|
|
8873
|
+
return out;
|
|
8874
|
+
}
|
|
8875
|
+
/** Kind-specialized typed-array store. */
|
|
8876
|
+
emitBytesSet(elem, receiver, index, value) {
|
|
8877
|
+
const B = this.B;
|
|
8878
|
+
const idx = this.emitBytesIndex(receiver, index);
|
|
8879
|
+
const stored = elem === "f32" ? null : this.emitBytesU32(value);
|
|
8880
|
+
const data = this.emitBytesData(receiver);
|
|
8881
|
+
const p = B.tmp();
|
|
8882
|
+
if (elem === "u8") {
|
|
8883
|
+
const byte = B.tmp();
|
|
8884
|
+
B.line(`${byte} = trunc i32 ${stored} to i8`);
|
|
8885
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
|
|
8886
|
+
B.line(`store i8 ${byte}, ptr ${p}, align 1`);
|
|
8887
|
+
return;
|
|
8888
|
+
}
|
|
8889
|
+
if (elem === "f32") {
|
|
8890
|
+
const narrowed = B.tmp();
|
|
8891
|
+
B.line(`${narrowed} = fptrunc double ${value} to float`);
|
|
8892
|
+
B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
|
|
8893
|
+
B.line(`store float ${narrowed}, ptr ${p}, align 1`);
|
|
8894
|
+
return;
|
|
8895
|
+
}
|
|
8896
|
+
B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
|
|
8897
|
+
B.line(`store i32 ${stored}, ptr ${p}, align 1`);
|
|
8898
|
+
}
|
|
8571
8899
|
/** The bytesIntrinsic surface (emit-exprs.ts's switch, .ll flavored).
|
|
8572
8900
|
* Receiver and args are borrowed frame temps; string/bytes results come
|
|
8573
8901
|
* back +1. The MAY_THROW methods get the standard pending check after
|
|
@@ -8615,18 +8943,30 @@ class LlEmitter {
|
|
|
8615
8943
|
? call("scr_bytes_read_var", "double (ptr, double, double, i1 zeroext, i1 zeroext)", `ptr ${r0.name}, double ${rest[0].name}, double ${rest[1].name}, i1 ${spec.sign}, i1 ${spec.le}`, false, true)
|
|
8616
8944
|
: call("scr_bytes_write_var", "double (ptr, double, double, double, i1 zeroext, i1 zeroext)", `ptr ${r0.name}, double ${rest[0].name}, double ${rest[1].name}, double ${rest[2].name}, i1 ${spec.sign}, i1 ${spec.le}`, false, true);
|
|
8617
8945
|
}
|
|
8618
|
-
const r = this.emitExpr(e.receiver);
|
|
8619
|
-
const args = e.args.map((a) => this.emitExpr(a));
|
|
8620
8946
|
const method = e.method;
|
|
8947
|
+
const directElementAccess = method === "length" || method === "byteLength" || method === "get";
|
|
8948
|
+
const r = directElementAccess
|
|
8949
|
+
? this.emitBytesReceiver(e.receiver, e.args)
|
|
8950
|
+
: this.emitExpr(e.receiver);
|
|
8951
|
+
const args = e.args.map((a) => this.emitExpr(a));
|
|
8621
8952
|
const NAN = f64Lit(NaN);
|
|
8622
8953
|
switch (method) {
|
|
8623
8954
|
case "length":
|
|
8624
|
-
|
|
8955
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
8956
|
+
throw new Error("llvm emitter bug: bytesIntrinsic length on non-bytes");
|
|
8957
|
+
}
|
|
8958
|
+
return this.emitBytesLength(e.receiver.type.elem, r.name, false);
|
|
8625
8959
|
case "byteLength":
|
|
8626
|
-
|
|
8960
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
8961
|
+
throw new Error("llvm emitter bug: bytesIntrinsic byteLength on non-bytes");
|
|
8962
|
+
}
|
|
8963
|
+
return this.emitBytesLength(e.receiver.type.elem, r.name, true);
|
|
8627
8964
|
case "get":
|
|
8628
8965
|
// Any invalid index traps (the array runtime's discipline).
|
|
8629
|
-
|
|
8966
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
8967
|
+
throw new Error("llvm emitter bug: bytesIntrinsic get on non-bytes");
|
|
8968
|
+
}
|
|
8969
|
+
return this.emitBytesGet(e.receiver.type.elem, r.name, args[0].name);
|
|
8630
8970
|
case "slice":
|
|
8631
8971
|
return call("scr_bytes_slice", "ptr (ptr, double, double)", `ptr ${r.name}, double ${args[0]?.name ?? f64Lit(0)}, double ${args[1]?.name ?? F64_INF}`, true, false);
|
|
8632
8972
|
case "subarray":
|
|
@@ -9119,6 +9459,410 @@ class LlEmitter {
|
|
|
9119
9459
|
B.line(`call void @sc_bad_key()`);
|
|
9120
9460
|
B.terminate(`unreachable`);
|
|
9121
9461
|
}
|
|
9462
|
+
dynPromiseAdapter(inner) {
|
|
9463
|
+
if (!isRefCounted(inner) || inner.kind === "dyn") {
|
|
9464
|
+
throw new Error(`dynamic promise adapter requires a concrete reference type, got ${typeKey(inner)}`);
|
|
9465
|
+
}
|
|
9466
|
+
const key = typeKey(inner);
|
|
9467
|
+
const existing = this.dynPromiseAdapters.get(key);
|
|
9468
|
+
if (existing)
|
|
9469
|
+
return existing;
|
|
9470
|
+
const sym = `sc_dpa_${this.dynPromiseAdapters.size}`;
|
|
9471
|
+
this.dynPromiseAdapters.set(key, sym);
|
|
9472
|
+
this.declare(`declare ptr @scr_promise_payload_ref(ptr)`);
|
|
9473
|
+
this.declare(`declare void @scr_dyn_release_v(ptr)`);
|
|
9474
|
+
this.declare(`declare zeroext i1 @scr_exc_pending()`);
|
|
9475
|
+
this.declare(`declare void @scr_promise_reject_pending(ptr)`);
|
|
9476
|
+
const B = new BlockBuilder();
|
|
9477
|
+
const dyn = B.tmp();
|
|
9478
|
+
const value = B.tmp();
|
|
9479
|
+
const pending = B.tmp();
|
|
9480
|
+
B.line(`${dyn} = call ptr @scr_promise_payload_ref(ptr %src)`);
|
|
9481
|
+
B.line(`${value} = call ptr @${this.dyn.dynCheckHelper(inner)}(ptr ${dyn}, ptr null)`);
|
|
9482
|
+
B.line(`call void @scr_dyn_release_v(ptr ${dyn})`);
|
|
9483
|
+
B.line(`${pending} = call zeroext i1 @scr_exc_pending()`);
|
|
9484
|
+
const fail = B.newLabel("sra.fail");
|
|
9485
|
+
const ok = B.newLabel("sra.ok");
|
|
9486
|
+
B.condBr(pending, fail, ok);
|
|
9487
|
+
B.startBlock(fail);
|
|
9488
|
+
B.line(`call void @scr_promise_reject_pending(ptr %dst)`);
|
|
9489
|
+
B.terminate(`ret void`);
|
|
9490
|
+
B.startBlock(ok);
|
|
9491
|
+
if (inner.kind === "string") {
|
|
9492
|
+
this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
|
|
9493
|
+
B.line(`call void @scr_promise_fulfill_str(ptr %dst, ptr ${value})`);
|
|
9494
|
+
}
|
|
9495
|
+
else {
|
|
9496
|
+
const rc = vAdapters(this, inner);
|
|
9497
|
+
this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
|
|
9498
|
+
B.line(`call void @scr_promise_fulfill_ref(ptr %dst, ptr ${value}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${traceArg(this, inner)})`);
|
|
9499
|
+
}
|
|
9500
|
+
B.terminate(`ret void`);
|
|
9501
|
+
this.resolveThunkDefs.push(`define internal void @${sym}(ptr %dst, ptr %src) ${FN_ATTRS} { ; checked-dynamic promise exit ${key}`, B.render(), `}`, ``);
|
|
9502
|
+
return sym;
|
|
9503
|
+
}
|
|
9504
|
+
streamTypedRefCommitAdapter(t, snapshot) {
|
|
9505
|
+
if (t.kind === "bytes") {
|
|
9506
|
+
const commit = `${snapshot}_commit`;
|
|
9507
|
+
const check = this.dyn.dynCheckHelper(t);
|
|
9508
|
+
this.declare(`declare void @scr_bytes_copy_contents(ptr, ptr)`);
|
|
9509
|
+
this.declare(`declare void @scr_bytes_release(ptr)`);
|
|
9510
|
+
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 %copy`, `copy:`, ` call void @scr_bytes_copy_contents(ptr %target, ptr %next)`, ` call void @scr_bytes_release(ptr %next)`, ` br label %done`, `done:`, ` ret void`, `}`, ``);
|
|
9511
|
+
return `@${commit}`;
|
|
9512
|
+
}
|
|
9513
|
+
if (t.kind === "array") {
|
|
9514
|
+
const commit = `${snapshot}_commit`;
|
|
9515
|
+
const check = this.dyn.dynCheckHelper(t);
|
|
9516
|
+
this.declare(`declare void @scr_arr_release(ptr)`);
|
|
9517
|
+
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`, `}`, ``);
|
|
9518
|
+
return `@${commit}`;
|
|
9519
|
+
}
|
|
9520
|
+
if (t.kind !== "record")
|
|
9521
|
+
return "null";
|
|
9522
|
+
const shape = this.recordsById.get(t.shapeId);
|
|
9523
|
+
if (!shape) {
|
|
9524
|
+
throw new Error(`llvm emitter bug: stream typed-ref commit of unknown shape ${t.shapeId}`);
|
|
9525
|
+
}
|
|
9526
|
+
const commit = `${snapshot}_commit`;
|
|
9527
|
+
const check = this.dyn.dynCheckHelper(t);
|
|
9528
|
+
const lines = [
|
|
9529
|
+
`define internal void @${commit}(ptr %target, ptr %d) ${FN_ATTRS} { ; commit live stream element ${typeKey(t)}`,
|
|
9530
|
+
`entry:`,
|
|
9531
|
+
` %next = call ptr @${check}(ptr %d, ptr null)`,
|
|
9532
|
+
` %missing = icmp eq ptr %next, null`,
|
|
9533
|
+
` br i1 %missing, label %done, label %swap`,
|
|
9534
|
+
`swap:`,
|
|
9535
|
+
];
|
|
9536
|
+
const members = [
|
|
9537
|
+
...shape.fields.map((field, index) => ({
|
|
9538
|
+
index: index + 1,
|
|
9539
|
+
type: llFieldType(field.type),
|
|
9540
|
+
name: field.name,
|
|
9541
|
+
})),
|
|
9542
|
+
...(shape.indexValue
|
|
9543
|
+
? [{
|
|
9544
|
+
index: shape.fields.length + 1,
|
|
9545
|
+
type: "ptr",
|
|
9546
|
+
name: "[key: string] overflow",
|
|
9547
|
+
}]
|
|
9548
|
+
: []),
|
|
9549
|
+
];
|
|
9550
|
+
members.forEach((member, index) => {
|
|
9551
|
+
lines.push(` %tp${index} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %target, i64 0, i32 ${member.index}`, ` %np${index} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %next, i64 0, i32 ${member.index}`, ` %old${index} = load ${member.type}, ptr %tp${index} ; ${member.name}`, ` %new${index} = load ${member.type}, ptr %np${index}`, ` store ${member.type} %new${index}, ptr %tp${index}`, ` store ${member.type} %old${index}, ptr %np${index}`);
|
|
9552
|
+
});
|
|
9553
|
+
lines.push(` call void ${releaseSym(this, t)}(ptr %next)`, ` br label %done`, `done:`, ` ret void`, `}`, ``);
|
|
9554
|
+
this.resolveThunkDefs.push(...lines);
|
|
9555
|
+
return `@${commit}`;
|
|
9556
|
+
}
|
|
9557
|
+
/** Box the mutable payload of a union directly, preserving the identity
|
|
9558
|
+
* that AbortSignal reasons and stream chunks expose. Scalar/unit arms use
|
|
9559
|
+
* the ordinary union-to-dyn converter. */
|
|
9560
|
+
liveDynUnionRefAdapter(t) {
|
|
9561
|
+
const key = typeKey(t);
|
|
9562
|
+
const existing = this.liveDynUnionRefAdapters.get(key);
|
|
9563
|
+
if (existing)
|
|
9564
|
+
return existing;
|
|
9565
|
+
const union = this.unionsById.get(t.unionId);
|
|
9566
|
+
if (!union) {
|
|
9567
|
+
throw new Error(`llvm emitter bug: live dyn ref of unknown union ${t.unionId}`);
|
|
9568
|
+
}
|
|
9569
|
+
const mutableArms = union.arms
|
|
9570
|
+
.map((arm, tag) => ({ arm, tag }))
|
|
9571
|
+
.filter(({ arm }) => this.streamTypedRefEligible(arm));
|
|
9572
|
+
if (mutableArms.length === 0) {
|
|
9573
|
+
throw new Error(`llvm emitter bug: live dyn ref of immutable union ${key}`);
|
|
9574
|
+
}
|
|
9575
|
+
const sym = `sc_ldu_${this.liveDynUnionRefAdapters.size}`;
|
|
9576
|
+
this.liveDynUnionRefAdapters.set(key, sym);
|
|
9577
|
+
this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
|
|
9578
|
+
const adapters = new Map();
|
|
9579
|
+
for (const { arm, tag } of mutableArms) {
|
|
9580
|
+
const prefix = `${sym}_${tag}`;
|
|
9581
|
+
adapters.set(tag, this.streamTypedRefMaterializeAdapter(arm, { prefix, adapters: new Map() }, `${prefix}_materialize`));
|
|
9582
|
+
}
|
|
9583
|
+
const B = new BlockBuilder();
|
|
9584
|
+
const tagPtr = B.tmp();
|
|
9585
|
+
const tagValue = B.tmp();
|
|
9586
|
+
B.line(`${tagPtr} = getelementptr inbounds %ScrUnion, ptr %u, i64 0, i32 1`);
|
|
9587
|
+
B.line(`${tagValue} = load i32, ptr ${tagPtr}`);
|
|
9588
|
+
const fallback = B.newLabel("ldu.dyn");
|
|
9589
|
+
const armLabels = mutableArms.map(() => B.newLabel("ldu.ref"));
|
|
9590
|
+
B.terminate(`switch i32 ${tagValue}, label %${fallback} [ ${mutableArms.map(({ tag }, index) => `i32 ${tag}, label %${armLabels[index]}`).join(" ")} ]`);
|
|
9591
|
+
mutableArms.forEach(({ arm, tag }, index) => {
|
|
9592
|
+
const adapter = adapters.get(tag);
|
|
9593
|
+
const rc = vAdapters(this, arm);
|
|
9594
|
+
const armKey = typeKey(arm);
|
|
9595
|
+
B.startBlock(armLabels[index]);
|
|
9596
|
+
const payloadPtr = B.tmp();
|
|
9597
|
+
const payload = B.tmp();
|
|
9598
|
+
const boxed = B.tmp();
|
|
9599
|
+
B.line(`${payloadPtr} = getelementptr inbounds %ScrUnion, ptr %u, i64 0, i32 5`);
|
|
9600
|
+
B.line(`${payload} = load ptr, ptr ${payloadPtr}`);
|
|
9601
|
+
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})`);
|
|
9602
|
+
B.terminate(`ret ptr ${boxed}`);
|
|
9603
|
+
});
|
|
9604
|
+
B.startBlock(fallback);
|
|
9605
|
+
const boxed = B.tmp();
|
|
9606
|
+
B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(t)}(ptr %u)`);
|
|
9607
|
+
B.terminate(`ret ptr ${boxed}`);
|
|
9608
|
+
this.resolveThunkDefs.push(`define internal ptr @${sym}(ptr %u) ${FN_ATTRS} { ; materialize live union value ${key}`, B.render(), `}`, ``);
|
|
9609
|
+
return sym;
|
|
9610
|
+
}
|
|
9611
|
+
streamTypedRefEligible(t) {
|
|
9612
|
+
return t.kind === "record" || t.kind === "array" || t.kind === "bytes";
|
|
9613
|
+
}
|
|
9614
|
+
streamTypedRefBoxValue(B, t, value, ctx) {
|
|
9615
|
+
const boxed = B.tmp();
|
|
9616
|
+
if (!this.streamTypedRefEligible(t)) {
|
|
9617
|
+
const valueTy = t.kind === "f64"
|
|
9618
|
+
? "double"
|
|
9619
|
+
: t.kind === "bool"
|
|
9620
|
+
? "i1"
|
|
9621
|
+
: "ptr";
|
|
9622
|
+
B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(t)}(${valueTy} ${value})`);
|
|
9623
|
+
return boxed;
|
|
9624
|
+
}
|
|
9625
|
+
const nested = this.streamTypedRefMaterializeAdapter(t, ctx);
|
|
9626
|
+
const rc = vAdapters(this, t);
|
|
9627
|
+
const key = typeKey(t);
|
|
9628
|
+
this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
|
|
9629
|
+
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})`);
|
|
9630
|
+
return boxed;
|
|
9631
|
+
}
|
|
9632
|
+
/** LLVM twin of the C stream materializer: mutable reference children
|
|
9633
|
+
* are their own typed capsules, so writes through a nested alias commit
|
|
9634
|
+
* to the nested static source immediately. */
|
|
9635
|
+
streamTypedRefMaterializeAdapter(t, ctx, preferredSnapshot) {
|
|
9636
|
+
const key = typeKey(t);
|
|
9637
|
+
const existing = ctx.adapters.get(key);
|
|
9638
|
+
if (existing)
|
|
9639
|
+
return existing;
|
|
9640
|
+
const snapshot = preferredSnapshot ??
|
|
9641
|
+
`${ctx.prefix}_nested_${ctx.adapters.size}`;
|
|
9642
|
+
const adapter = { snapshot, commit: "null" };
|
|
9643
|
+
ctx.adapters.set(key, adapter);
|
|
9644
|
+
adapter.commit = this.streamTypedRefCommitAdapter(t, snapshot);
|
|
9645
|
+
const B = new BlockBuilder();
|
|
9646
|
+
if (t.kind === "record") {
|
|
9647
|
+
const shape = this.recordsById.get(t.shapeId);
|
|
9648
|
+
if (!shape) {
|
|
9649
|
+
throw new Error(`llvm emitter bug: stream typed-ref materialize of unknown shape ${t.shapeId}`);
|
|
9650
|
+
}
|
|
9651
|
+
/* Keep the ordinary converter for index-signature/listener records;
|
|
9652
|
+
* their source-identity and overflow walks have extra contracts.
|
|
9653
|
+
* Declared-field records and tuples cover the live stream values. */
|
|
9654
|
+
const ordinary = shape.indexValue || shape.fields.some((field) => field.name === "handleEvent" && field.type.kind === "func");
|
|
9655
|
+
if (ordinary) {
|
|
9656
|
+
const out = B.tmp();
|
|
9657
|
+
B.line(`${out} = call ptr @${this.dyn.toDynHelper(t)}(ptr %p)`);
|
|
9658
|
+
B.terminate(`ret ptr ${out}`);
|
|
9659
|
+
}
|
|
9660
|
+
else if (shape.tuple) {
|
|
9661
|
+
this.declare(`declare ptr @scr_dyn_new_arr()`);
|
|
9662
|
+
this.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
|
|
9663
|
+
const out = B.tmp();
|
|
9664
|
+
B.line(`${out} = call ptr @scr_dyn_new_arr()`);
|
|
9665
|
+
const fields = [...shape.fields].sort((a, b) => Number(a.name) - Number(b.name));
|
|
9666
|
+
for (const field of fields) {
|
|
9667
|
+
const index = shape.fields.indexOf(field) + 1;
|
|
9668
|
+
const fieldPtr = B.tmp();
|
|
9669
|
+
const fieldValue = B.tmp();
|
|
9670
|
+
B.line(`${fieldPtr} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %p, i64 0, i32 ${index}`);
|
|
9671
|
+
B.line(`${fieldValue} = load ${llFieldType(field.type)}, ptr ${fieldPtr}`);
|
|
9672
|
+
const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
|
|
9673
|
+
B.line(`call void @scr_dyn_arr_push(ptr ${out}, ptr ${boxed})`);
|
|
9674
|
+
}
|
|
9675
|
+
B.terminate(`ret ptr ${out}`);
|
|
9676
|
+
}
|
|
9677
|
+
else {
|
|
9678
|
+
this.declare(`declare ptr @scr_dyn_new_obj()`);
|
|
9679
|
+
this.declare(`declare void @scr_dyn_obj_set(ptr, ptr, i64, ptr)`);
|
|
9680
|
+
const out = B.tmp();
|
|
9681
|
+
B.line(`${out} = call ptr @scr_dyn_new_obj()`);
|
|
9682
|
+
const byName = new Map(shape.fields.map((field) => [field.name, field]));
|
|
9683
|
+
const order = shape.declaredOrder ?? shape.fields.map((field) => field.name);
|
|
9684
|
+
const inOrder = new Set(order);
|
|
9685
|
+
const fields = [
|
|
9686
|
+
...order.map((name) => byName.get(name)).filter((field) => field !== undefined),
|
|
9687
|
+
...shape.fields.filter((field) => !inOrder.has(field.name)),
|
|
9688
|
+
];
|
|
9689
|
+
for (const field of fields) {
|
|
9690
|
+
const index = shape.fields.indexOf(field) + 1;
|
|
9691
|
+
const fieldPtr = B.tmp();
|
|
9692
|
+
const fieldValue = B.tmp();
|
|
9693
|
+
B.line(`${fieldPtr} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %p, i64 0, i32 ${index}`);
|
|
9694
|
+
B.line(`${fieldValue} = load ${llFieldType(field.type)}, ptr ${fieldPtr}`);
|
|
9695
|
+
const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
|
|
9696
|
+
B.line(`call void @scr_dyn_obj_set(ptr ${out}, ptr ${this.cstr(field.name)}, i64 ${Buffer.byteLength(field.name, "utf8")}, ptr ${boxed})`);
|
|
9697
|
+
}
|
|
9698
|
+
B.terminate(`ret ptr ${out}`);
|
|
9699
|
+
}
|
|
9700
|
+
}
|
|
9701
|
+
else if (t.kind === "array") {
|
|
9702
|
+
const elem = t.elem;
|
|
9703
|
+
this.declare(`declare ptr @scr_dyn_new_arr()`);
|
|
9704
|
+
this.declare(`declare void @scr_dyn_arr_push(ptr, ptr)`);
|
|
9705
|
+
this.declare(`declare double @scr_arr_len(ptr)`);
|
|
9706
|
+
const out = B.tmp();
|
|
9707
|
+
B.line(`${out} = call ptr @scr_dyn_new_arr()`);
|
|
9708
|
+
const len = B.tmp();
|
|
9709
|
+
B.line(`${len} = call double @scr_arr_len(ptr %p)`);
|
|
9710
|
+
const iSlot = B.slot();
|
|
9711
|
+
B.entryAllocas.push(`${iSlot} = alloca double`);
|
|
9712
|
+
B.line(`store double ${f64Lit(0)}, ptr ${iSlot}`);
|
|
9713
|
+
const condition = B.newLabel("strm.c");
|
|
9714
|
+
const body = B.newLabel("strm.b");
|
|
9715
|
+
const done = B.newLabel("strm.e");
|
|
9716
|
+
B.br(condition);
|
|
9717
|
+
B.startBlock(condition);
|
|
9718
|
+
const index = B.tmp();
|
|
9719
|
+
const more = B.tmp();
|
|
9720
|
+
B.line(`${index} = load double, ptr ${iSlot}`);
|
|
9721
|
+
B.line(`${more} = fcmp olt double ${index}, ${len}`);
|
|
9722
|
+
B.condBr(more, body, done);
|
|
9723
|
+
B.startBlock(body);
|
|
9724
|
+
let value;
|
|
9725
|
+
if (elem.kind === "f64" || elem.kind === "bool") {
|
|
9726
|
+
const valueTy = elem.kind === "f64" ? "double" : "i1";
|
|
9727
|
+
this.declare(`declare ${elem.kind === "bool" ? "zeroext i1" : valueTy} @scr_arr_get_${elem.kind}(ptr, double)`);
|
|
9728
|
+
value = B.tmp();
|
|
9729
|
+
B.line(`${value} = call ${valueTy} @scr_arr_get_${elem.kind}(ptr %p, double ${index})`);
|
|
9730
|
+
}
|
|
9731
|
+
else {
|
|
9732
|
+
this.declare(`declare ptr @scr_arr_get_ref(ptr, double)`);
|
|
9733
|
+
value = B.tmp();
|
|
9734
|
+
B.line(`${value} = call ptr @scr_arr_get_ref(ptr %p, double ${index}) ; +1`);
|
|
9735
|
+
}
|
|
9736
|
+
const boxed = this.streamTypedRefBoxValue(B, elem, value, ctx);
|
|
9737
|
+
B.line(`call void @scr_dyn_arr_push(ptr ${out}, ptr ${boxed})`);
|
|
9738
|
+
if (isRefCounted(elem)) {
|
|
9739
|
+
B.line(`call void ${releaseSym(this, elem)}(ptr ${value})`);
|
|
9740
|
+
}
|
|
9741
|
+
const next = B.tmp();
|
|
9742
|
+
B.line(`${next} = fadd double ${index}, ${f64Lit(1)}`);
|
|
9743
|
+
B.line(`store double ${next}, ptr ${iSlot}`);
|
|
9744
|
+
B.br(condition);
|
|
9745
|
+
B.startBlock(done);
|
|
9746
|
+
B.terminate(`ret ptr ${out}`);
|
|
9747
|
+
}
|
|
9748
|
+
else {
|
|
9749
|
+
const out = B.tmp();
|
|
9750
|
+
B.line(`${out} = call ptr @${this.dyn.toDynHelper(t)}(ptr %p)`);
|
|
9751
|
+
B.terminate(`ret ptr ${out}`);
|
|
9752
|
+
}
|
|
9753
|
+
this.resolveThunkDefs.push(`define internal ptr @${snapshot}(ptr %p) ${FN_ATTRS} { ; materialize live stream value ${key}`, B.render(), `}`, ``);
|
|
9754
|
+
return adapter;
|
|
9755
|
+
}
|
|
9756
|
+
streamFromArrayAdapter(t) {
|
|
9757
|
+
const elem = t.elem;
|
|
9758
|
+
const key = typeKey(elem);
|
|
9759
|
+
const existing = this.streamFromArrayAdapters.get(key);
|
|
9760
|
+
if (existing)
|
|
9761
|
+
return existing;
|
|
9762
|
+
const sym = `sc_sfa_${this.streamFromArrayAdapters.size}`;
|
|
9763
|
+
this.streamFromArrayAdapters.set(key, sym);
|
|
9764
|
+
const B = new BlockBuilder();
|
|
9765
|
+
const unionDef = elem.kind === "union"
|
|
9766
|
+
? this.unionsById.get(elem.unionId)
|
|
9767
|
+
: undefined;
|
|
9768
|
+
if (elem.kind === "union" && !unionDef) {
|
|
9769
|
+
throw new Error(`llvm emitter bug: streamFrom of unknown union ${elem.unionId}`);
|
|
9770
|
+
}
|
|
9771
|
+
const unionRefArms = unionDef?.arms
|
|
9772
|
+
.map((arm, tag) => ({ arm, tag }))
|
|
9773
|
+
.filter(({ arm }) => isRefCounted(arm) && arm.kind !== "dyn" && arm.kind !== "string") ?? [];
|
|
9774
|
+
const typedRef = isRefCounted(elem) &&
|
|
9775
|
+
elem.kind !== "dyn" &&
|
|
9776
|
+
elem.kind !== "string" &&
|
|
9777
|
+
elem.kind !== "union";
|
|
9778
|
+
const snapshot = `${sym}_materialize`;
|
|
9779
|
+
let value;
|
|
9780
|
+
if (elem.kind === "f64") {
|
|
9781
|
+
this.declare(`declare double @scr_arr_get_f64(ptr, double)`);
|
|
9782
|
+
value = B.tmp();
|
|
9783
|
+
B.line(`${value} = call double @scr_arr_get_f64(ptr %a, double %i)`);
|
|
9784
|
+
}
|
|
9785
|
+
else if (elem.kind === "bool") {
|
|
9786
|
+
this.declare(`declare zeroext i1 @scr_arr_get_bool(ptr, double)`);
|
|
9787
|
+
value = B.tmp();
|
|
9788
|
+
B.line(`${value} = call i1 @scr_arr_get_bool(ptr %a, double %i)`);
|
|
9789
|
+
}
|
|
9790
|
+
else {
|
|
9791
|
+
this.declare(`declare ptr @scr_arr_get_ref(ptr, double)`);
|
|
9792
|
+
value = B.tmp();
|
|
9793
|
+
B.line(`${value} = call ptr @scr_arr_get_ref(ptr %a, double %i) ; +1`);
|
|
9794
|
+
}
|
|
9795
|
+
let boxed;
|
|
9796
|
+
const valueTy = elem.kind === "f64" ? "double" : elem.kind === "bool" ? "i1" : "ptr";
|
|
9797
|
+
if (elem.kind === "union") {
|
|
9798
|
+
if (unionRefArms.length === 0) {
|
|
9799
|
+
boxed = B.tmp();
|
|
9800
|
+
B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(elem)}(ptr ${value})`);
|
|
9801
|
+
}
|
|
9802
|
+
else {
|
|
9803
|
+
this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
|
|
9804
|
+
const boxedSlot = B.slot();
|
|
9805
|
+
B.entryAllocas.push(`${boxedSlot} = alloca ptr`);
|
|
9806
|
+
const tagPtr = B.tmp();
|
|
9807
|
+
const tagValue = B.tmp();
|
|
9808
|
+
B.line(`${tagPtr} = getelementptr inbounds %ScrUnion, ptr ${value}, i64 0, i32 1`);
|
|
9809
|
+
B.line(`${tagValue} = load i32, ptr ${tagPtr}`);
|
|
9810
|
+
const fallback = B.newLabel("sfa.union.dyn");
|
|
9811
|
+
const join = B.newLabel("sfa.union.join");
|
|
9812
|
+
const armLabels = unionRefArms.map(() => B.newLabel("sfa.union.ref"));
|
|
9813
|
+
B.terminate(`switch i32 ${tagValue}, label %${fallback} [ ${unionRefArms.map(({ tag }, i) => `i32 ${tag}, label %${armLabels[i]}`).join(" ")} ]`);
|
|
9814
|
+
unionRefArms.forEach(({ arm, tag }, i) => {
|
|
9815
|
+
const armKey = typeKey(arm);
|
|
9816
|
+
const armSnapshot = `${snapshot}_${tag}`;
|
|
9817
|
+
const armCommit = this.streamTypedRefCommitAdapter(arm, armSnapshot);
|
|
9818
|
+
const rc = vAdapters(this, arm);
|
|
9819
|
+
B.startBlock(armLabels[i]);
|
|
9820
|
+
const payloadPtr = B.tmp();
|
|
9821
|
+
const payload = B.tmp();
|
|
9822
|
+
const armBoxed = B.tmp();
|
|
9823
|
+
B.line(`${payloadPtr} = getelementptr inbounds %ScrUnion, ptr ${value}, i64 0, i32 5`);
|
|
9824
|
+
B.line(`${payload} = load ptr, ptr ${payloadPtr}`);
|
|
9825
|
+
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})`);
|
|
9826
|
+
B.line(`store ptr ${armBoxed}, ptr ${boxedSlot}`);
|
|
9827
|
+
B.br(join);
|
|
9828
|
+
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`, `}`, ``);
|
|
9829
|
+
});
|
|
9830
|
+
B.startBlock(fallback);
|
|
9831
|
+
const dynBoxed = B.tmp();
|
|
9832
|
+
B.line(`${dynBoxed} = call ptr @${this.dyn.toDynHelper(elem)}(ptr ${value})`);
|
|
9833
|
+
B.line(`store ptr ${dynBoxed}, ptr ${boxedSlot}`);
|
|
9834
|
+
B.br(join);
|
|
9835
|
+
B.startBlock(join);
|
|
9836
|
+
boxed = B.tmp();
|
|
9837
|
+
B.line(`${boxed} = load ptr, ptr ${boxedSlot}`);
|
|
9838
|
+
}
|
|
9839
|
+
}
|
|
9840
|
+
else if (typedRef) {
|
|
9841
|
+
boxed = B.tmp();
|
|
9842
|
+
const rc = vAdapters(this, elem);
|
|
9843
|
+
const keyPtr = this.cstr(key);
|
|
9844
|
+
let commit;
|
|
9845
|
+
if (this.streamTypedRefEligible(elem)) {
|
|
9846
|
+
commit = this.streamTypedRefMaterializeAdapter(elem, { prefix: snapshot, adapters: new Map() }, snapshot).commit;
|
|
9847
|
+
}
|
|
9848
|
+
else {
|
|
9849
|
+
commit = this.streamTypedRefCommitAdapter(elem, snapshot);
|
|
9850
|
+
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`, `}`, ``);
|
|
9851
|
+
}
|
|
9852
|
+
this.declare(`declare ptr @scr_dyn_new_typed_ref(ptr, ptr, ptr, ptr, i64, ptr, ptr)`);
|
|
9853
|
+
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})`);
|
|
9854
|
+
}
|
|
9855
|
+
else {
|
|
9856
|
+
boxed = B.tmp();
|
|
9857
|
+
B.line(`${boxed} = call ptr @${this.dyn.toDynHelper(elem)}(${valueTy} ${value})`);
|
|
9858
|
+
}
|
|
9859
|
+
if (isRefCounted(elem)) {
|
|
9860
|
+
B.line(`call void ${releaseSym(this, elem)}(ptr ${value})`);
|
|
9861
|
+
}
|
|
9862
|
+
B.terminate(`ret ptr ${boxed}`);
|
|
9863
|
+
this.resolveThunkDefs.push(`define internal ptr @${sym}(ptr %a, double %i) ${FN_ATTRS} { ; ReadableStream.from array<${key}>`, B.render(), `}`, ``);
|
|
9864
|
+
return sym;
|
|
9865
|
+
}
|
|
9122
9866
|
/** The claimed libCall slice: args are BORROWED (owned temps of the
|
|
9123
9867
|
* current frame), refcounted results come back +1. LLVM types derive
|
|
9124
9868
|
* from the call site's IR types (exactly the contract the C prototypes
|
|
@@ -9131,6 +9875,70 @@ class LlEmitter {
|
|
|
9131
9875
|
// Loop liveness first (one table for generic and special shapes).
|
|
9132
9876
|
if (USES_TIMERS_LIB_FNS.has(e.fn))
|
|
9133
9877
|
this.usesTimers = true;
|
|
9878
|
+
if (e.fn === "fetch.responseText" || e.fn === "fetch.responseBytes") {
|
|
9879
|
+
if (e.type.kind !== "promise") {
|
|
9880
|
+
throw new Error(`llvm emitter bug: ${e.fn} result`);
|
|
9881
|
+
}
|
|
9882
|
+
const response = this.emitExpr(e.args[0]);
|
|
9883
|
+
const runtimeFn = e.fn === "fetch.responseText"
|
|
9884
|
+
? "scr_fetch_response_text"
|
|
9885
|
+
: "scr_fetch_response_bytes";
|
|
9886
|
+
this.declare(`declare ptr @${runtimeFn}(ptr)`);
|
|
9887
|
+
this.declare(`declare ptr @scr_promise_new()`);
|
|
9888
|
+
this.declare(`declare void @scr_promise_race_add(ptr, ptr, ptr)`);
|
|
9889
|
+
const sourceRaw = B.tmp();
|
|
9890
|
+
B.line(`${sourceRaw} = call ptr @${runtimeFn}(ptr ${response.name})`);
|
|
9891
|
+
const source = this.own({
|
|
9892
|
+
name: sourceRaw,
|
|
9893
|
+
type: { kind: "promise", inner: DYN },
|
|
9894
|
+
});
|
|
9895
|
+
const resultRaw = B.tmp();
|
|
9896
|
+
B.line(`${resultRaw} = call ptr @scr_promise_new()`);
|
|
9897
|
+
const result = this.own({ name: resultRaw, type: e.type });
|
|
9898
|
+
B.line(`call void @scr_promise_race_add(ptr ${result.name}, ptr ${source.name}, ptr @${this.dynPromiseAdapter(e.type.inner)})`);
|
|
9899
|
+
return result;
|
|
9900
|
+
}
|
|
9901
|
+
if (e.fn === "fetch.readerRead") {
|
|
9902
|
+
if (e.type.kind !== "promise" || e.type.inner.kind !== "record") {
|
|
9903
|
+
throw new Error("llvm emitter bug: fetch.readerRead result");
|
|
9904
|
+
}
|
|
9905
|
+
const reader = this.emitExpr(e.args[0]);
|
|
9906
|
+
this.declare(`declare ptr @scr_fetch_reader_read(ptr)`);
|
|
9907
|
+
this.declare(`declare ptr @scr_promise_new()`);
|
|
9908
|
+
this.declare(`declare void @scr_promise_race_add(ptr, ptr, ptr)`);
|
|
9909
|
+
const sourceRaw = B.tmp();
|
|
9910
|
+
B.line(`${sourceRaw} = call ptr @scr_fetch_reader_read(ptr ${reader.name})`);
|
|
9911
|
+
const source = this.own({
|
|
9912
|
+
name: sourceRaw,
|
|
9913
|
+
type: { kind: "promise", inner: DYN },
|
|
9914
|
+
});
|
|
9915
|
+
const resultRaw = B.tmp();
|
|
9916
|
+
B.line(`${resultRaw} = call ptr @scr_promise_new()`);
|
|
9917
|
+
const result = this.own({ name: resultRaw, type: e.type });
|
|
9918
|
+
B.line(`call void @scr_promise_race_add(ptr ${result.name}, ptr ${source.name}, ptr @${this.dynPromiseAdapter(e.type.inner)})`);
|
|
9919
|
+
return result;
|
|
9920
|
+
}
|
|
9921
|
+
if (e.fn === "fetch.streamFrom") {
|
|
9922
|
+
const source = this.emitExpr(e.args[0]);
|
|
9923
|
+
let sym = "scr_fetch_stream_from";
|
|
9924
|
+
let extra = "";
|
|
9925
|
+
if (source.type.kind === "array") {
|
|
9926
|
+
sym = "scr_fetch_stream_from_array";
|
|
9927
|
+
extra = `, ptr @${this.streamFromArrayAdapter(source.type)}`;
|
|
9928
|
+
}
|
|
9929
|
+
else if (source.type.kind === "bytes") {
|
|
9930
|
+
sym = "scr_fetch_stream_from_bytes";
|
|
9931
|
+
}
|
|
9932
|
+
else if (source.type.kind === "string") {
|
|
9933
|
+
sym = "scr_fetch_stream_from_string";
|
|
9934
|
+
}
|
|
9935
|
+
this.declare(`declare ptr @${sym}(ptr${source.type.kind === "array" ? ", ptr" : ""})`);
|
|
9936
|
+
const raw = B.tmp();
|
|
9937
|
+
B.line(`${raw} = call ptr @${sym}(ptr ${source.name}${extra})`);
|
|
9938
|
+
const out = this.own({ name: raw, type: e.type });
|
|
9939
|
+
this.emitPendingCheck();
|
|
9940
|
+
return out;
|
|
9941
|
+
}
|
|
9134
9942
|
// The handful with non-generic shapes first.
|
|
9135
9943
|
if (e.fn === "error.argTypeThrow") {
|
|
9136
9944
|
// Always throws with the runtime-rendered Received tail (the
|