@scriptc/compiler 0.0.22 → 0.0.24
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 +139 -14
- package/dist/backend/cc.d.ts +13 -0
- package/dist/backend/cc.js +11 -0
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-async.d.ts +4 -0
- package/dist/backend/emission/emit-async.js +34 -2
- 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 +272 -22
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-shapes.js +1 -0
- package/dist/backend/emission/emit-shapes.js.map +1 -1
- package/dist/backend/emission/emit-stmts.js +36 -9
- package/dist/backend/emission/emit-stmts.js.map +1 -1
- package/dist/backend/emission/emit-types.js +14 -1
- package/dist/backend/emission/emit-types.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +19 -1
- package/dist/backend/emission/emitter.js +81 -2
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.js +578 -36
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.js +12 -3
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/mangle.d.ts +3 -0
- package/dist/backend/mangle.js +5 -0
- package/dist/backend/mangle.js.map +1 -1
- package/dist/compat/fetch-profile.d.ts +10 -0
- package/dist/compat/fetch-profile.js +55 -6
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/coverage/surface-manifest.js +16 -1
- 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 +17 -16
- package/dist/frontend/lowering/lower-builtins.js +817 -117
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +3 -1
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +28 -8
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +2 -1
- package/dist/frontend/lowering/lower-containers.js +99 -32
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +1 -1
- package/dist/frontend/lowering/lower-exprs.js +78 -6
- 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-inspect.js +59 -1
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +21 -0
- package/dist/frontend/lowering/lower-island.js +284 -10
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +5 -0
- package/dist/frontend/lowering/lower-server.js +1 -1
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +6 -2
- package/dist/frontend/lowering/lower-stmts.js +53 -14
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +3 -2
- package/dist/frontend/lowering/lowerer.js +17 -8
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +108 -25
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/program.d.ts +4 -4
- package/dist/frontend/program.js +264 -46
- 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 +7 -4
- package/dist/frontend/types.js +103 -8
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/ir/integer-loops.d.ts +23 -0
- package/dist/ir/integer-loops.js +82 -0
- package/dist/ir/integer-loops.js.map +1 -0
- package/dist/ir/nodes.d.ts +104 -28
- package/dist/ir/nodes.js +89 -1
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +136 -25
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/fence-eval.js +4 -3
- package/dist/library/fence-eval.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +186 -29
|
@@ -1,4 +1,5 @@
|
|
|
1
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
|
+
import { matchIntegerBytesForLoop } from "../../ir/integer-loops.js";
|
|
2
3
|
import { computeMayThrow } from "../emission/may-throw.js";
|
|
3
4
|
import { mangleArgPack, mangleAsyncSpawn, mangleClassNew, mangleClassObj, mangleClassRetain, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenResThunk, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordNew, mangleRecordStruct, mangleResolveThunk, mangleTrampoline, mangleVtStruct, mangleWrapper } from "../mangle.js";
|
|
4
5
|
import { BlockBuilder } from "./blocks.js";
|
|
@@ -38,6 +39,7 @@ function llStrBytes(text) {
|
|
|
38
39
|
* Throwing members (MAY_THROW_LIB_FNS) and everything unlisted refuse by
|
|
39
40
|
* name. */
|
|
40
41
|
const LIB_FN_SYMS = {
|
|
42
|
+
"util.parseArgs": "scr_util_parse_args",
|
|
41
43
|
"math.maxArr": "scr_math_max_arr",
|
|
42
44
|
"math.minArr": "scr_math_min_arr",
|
|
43
45
|
"math.min": "scr_math_min",
|
|
@@ -129,13 +131,36 @@ const LIB_FN_SYMS = {
|
|
|
129
131
|
"process.stderrWrite": "scr_process_stderr_write",
|
|
130
132
|
"process.isTTY": "scr_process_is_tty",
|
|
131
133
|
"date.now": "scr_date_now",
|
|
134
|
+
"date.newNow": "scr_date_now",
|
|
135
|
+
"date.newMs": "scr_date_new_ms",
|
|
136
|
+
"date.newString": "scr_date_parse_get_time",
|
|
137
|
+
"date.getTime": "scr_date_get_time",
|
|
138
|
+
"date.valueOf": "scr_date_get_time",
|
|
132
139
|
"date.parseGetTime": "scr_date_parse_get_time",
|
|
133
140
|
"date.utc": "scr_date_utc",
|
|
141
|
+
"date.getFullYear": "scr_date_get_full_year_local",
|
|
142
|
+
"date.getUTCFullYear": "scr_date_get_full_year_utc",
|
|
143
|
+
"date.getMonth": "scr_date_get_month_local",
|
|
144
|
+
"date.getUTCMonth": "scr_date_get_month_utc",
|
|
145
|
+
"date.getDate": "scr_date_get_date_local",
|
|
146
|
+
"date.getUTCDate": "scr_date_get_date_utc",
|
|
147
|
+
"date.getDay": "scr_date_get_day_local",
|
|
148
|
+
"date.getUTCDay": "scr_date_get_day_utc",
|
|
149
|
+
"date.getHours": "scr_date_get_hours_local",
|
|
150
|
+
"date.getUTCHours": "scr_date_get_hours_utc",
|
|
151
|
+
"date.getMinutes": "scr_date_get_minutes_local",
|
|
152
|
+
"date.getUTCMinutes": "scr_date_get_minutes_utc",
|
|
153
|
+
"date.getSeconds": "scr_date_get_seconds_local",
|
|
154
|
+
"date.getUTCSeconds": "scr_date_get_seconds_utc",
|
|
155
|
+
"date.getMilliseconds": "scr_date_get_milliseconds",
|
|
156
|
+
"date.getUTCMilliseconds": "scr_date_get_milliseconds",
|
|
157
|
+
"date.getTimezoneOffset": "scr_date_get_timezone_offset",
|
|
134
158
|
"fs.existsSync": "scr_fs_exists",
|
|
135
159
|
// ── the throwing slice (MAY_THROW_LIB_FNS members): the generic path
|
|
136
160
|
// emits the standard pending check after each — emit-exprs.ts's finish.
|
|
137
161
|
"process.cpuPrevValidate": "scr_cpu_prev_validate",
|
|
138
162
|
"date.toISOString": "scr_date_to_iso",
|
|
163
|
+
"date.toISOStringValue": "scr_date_to_iso",
|
|
139
164
|
"process.chdir": "scr_process_chdir",
|
|
140
165
|
"fs.writeFileSync": "scr_fs_write_file",
|
|
141
166
|
"fs.appendFileSync": "scr_fs_append_file",
|
|
@@ -151,6 +176,7 @@ const LIB_FN_SYMS = {
|
|
|
151
176
|
"fs.unlinkSync": "scr_fs_unlink",
|
|
152
177
|
"fs.chmodSync": "scr_fs_chmod",
|
|
153
178
|
"fs.copyFileSync": "scr_fs_copyfile",
|
|
179
|
+
"fs.renameSync": "scr_fs_rename",
|
|
154
180
|
"fs.accessSync": "scr_fs_access",
|
|
155
181
|
// node:assert (scr_assert.c): all args borrowed; failures throw the
|
|
156
182
|
// catchable AssertionError. The never-throwing members ride along.
|
|
@@ -213,6 +239,8 @@ const LIB_FN_SYMS = {
|
|
|
213
239
|
"fs.lstatSync": "scr_fs_lstat",
|
|
214
240
|
"fs.openSync": "scr_fs_open",
|
|
215
241
|
"fs.readSync": "scr_fs_read_sync",
|
|
242
|
+
"fs.writeSync": "scr_fs_write_sync",
|
|
243
|
+
"fs.writeStrSync": "scr_fs_write_str_sync",
|
|
216
244
|
"fs.closeSync": "scr_fs_close",
|
|
217
245
|
// string_decoder (scr_bytes.c): stateless helpers over (enc, pending,
|
|
218
246
|
// chunk) — never throw.
|
|
@@ -223,8 +251,12 @@ const LIB_FN_SYMS = {
|
|
|
223
251
|
"stats.isDirectory": "scr_stats_is_dir",
|
|
224
252
|
"stats.isSymbolicLink": "scr_stats_is_symlink",
|
|
225
253
|
"stats.size": "scr_stats_size",
|
|
254
|
+
"stats.blocks": "scr_stats_blocks",
|
|
255
|
+
"stats.nlink": "scr_stats_nlink",
|
|
256
|
+
"stats.atimeMs": "scr_stats_atime_ms",
|
|
226
257
|
"stats.mtimeMs": "scr_stats_mtime_ms",
|
|
227
258
|
"text.decode": "scr_text_decode",
|
|
259
|
+
"text.decodeLegacy": "scr_text_decode_legacy",
|
|
228
260
|
"zlib.deflateSync": "scr_zlib_deflate",
|
|
229
261
|
"zlib.inflateSync": "scr_zlib_inflate",
|
|
230
262
|
// The CA-store unit. get/set throw (an unknown type name, a
|
|
@@ -332,15 +364,25 @@ const LIB_FN_SYMS = {
|
|
|
332
364
|
// so it is special-cased in emitLibCall (the fs.readFileSync pattern).
|
|
333
365
|
"fsp.readFileBytes": "scr_fsp_read_file_bytes",
|
|
334
366
|
"fsp.writeFile": "scr_fsp_write_file",
|
|
367
|
+
"fsp.writeFileMode": "scr_fsp_write_file_mode",
|
|
335
368
|
"fsp.mkdir": "scr_fsp_mkdir",
|
|
336
369
|
"fsp.mkdirMode": "scr_fsp_mkdir_mode",
|
|
337
370
|
"fsp.mkdirRecursive": "scr_fsp_mkdir_recursive",
|
|
338
371
|
"fsp.mkdirRecursiveMode": "scr_fsp_mkdir_recursive_mode",
|
|
339
372
|
"fsp.unlink": "scr_fsp_unlink",
|
|
340
373
|
"fsp.chmod": "scr_fsp_chmod",
|
|
374
|
+
"fsp.rename": "scr_fsp_rename",
|
|
341
375
|
"fsp.readdir": "scr_fsp_readdir",
|
|
342
376
|
"fsp.rm": "scr_fsp_rm",
|
|
343
377
|
"fsp.stat": "scr_fsp_stat",
|
|
378
|
+
"fsp.open": "scr_fsp_open",
|
|
379
|
+
"fileHandle.fd": "scr_file_handle_fd",
|
|
380
|
+
"fileHandle.close": "scr_file_handle_close_promise",
|
|
381
|
+
"fileHandle.readFile": "scr_file_handle_read_file_promise",
|
|
382
|
+
"fileHandle.readFileBytes": "scr_file_handle_read_file_bytes_promise",
|
|
383
|
+
"fileHandle.writeFile": "scr_file_handle_write_file_promise",
|
|
384
|
+
"fileHandle.writeFileBytes": "scr_file_handle_write_file_bytes_promise",
|
|
385
|
+
"fileHandle.stat": "scr_file_handle_stat_promise",
|
|
344
386
|
"sym.new": "scr_sym_new",
|
|
345
387
|
"sym.for": "scr_sym_for",
|
|
346
388
|
"sym.toString": "scr_sym_to_string",
|
|
@@ -352,9 +394,11 @@ const LIB_FN_SYMS = {
|
|
|
352
394
|
// the generic path emits the standard pending check). typeof and the
|
|
353
395
|
// ambient-this read never throw. The fs dyn read is the sync-fs story.
|
|
354
396
|
"fetch.start": "scr_fetch_static",
|
|
397
|
+
"fetch.responseNew": "scr_fetch_response_new",
|
|
355
398
|
"fetch.responseJson": "scr_fetch_response_json",
|
|
356
399
|
"fetch.responseText": "scr_fetch_response_text",
|
|
357
400
|
"fetch.responseBytes": "scr_fetch_response_bytes",
|
|
401
|
+
"fetch.abortControllerNew": "scr_fetch_abort_controller_new",
|
|
358
402
|
"fetch.abortTimeout": "scr_fetch_abort_timeout",
|
|
359
403
|
"fetch.abortNow": "scr_fetch_abort_now",
|
|
360
404
|
"fetch.abortAny": "scr_fetch_abort_any",
|
|
@@ -829,6 +873,8 @@ class LlEmitter {
|
|
|
829
873
|
jumpTargets = [];
|
|
830
874
|
currentLocals = new Map();
|
|
831
875
|
captureIds = new Set();
|
|
876
|
+
/** Active canonical byte-loop induction bindings: local id → i64 slot. */
|
|
877
|
+
integerLoopBindings = new Map();
|
|
832
878
|
/** Enclosing try-with-FINALLY regions, innermost last: a `return`
|
|
833
879
|
* inside one runs every crossed finally (innermost first) before the
|
|
834
880
|
* actual ret — the C emitter's pending-return path, with the finally
|
|
@@ -940,6 +986,7 @@ class LlEmitter {
|
|
|
940
986
|
llType(t) {
|
|
941
987
|
switch (t.kind) {
|
|
942
988
|
case "f64":
|
|
989
|
+
case "date":
|
|
943
990
|
return "double";
|
|
944
991
|
case "bool":
|
|
945
992
|
return "i1";
|
|
@@ -957,6 +1004,7 @@ class LlEmitter {
|
|
|
957
1004
|
case "url":
|
|
958
1005
|
case "searchParams":
|
|
959
1006
|
case "stats":
|
|
1007
|
+
case "fileHandle":
|
|
960
1008
|
case "spawnRes":
|
|
961
1009
|
case "child":
|
|
962
1010
|
case "childStream":
|
|
@@ -1192,6 +1240,11 @@ class LlEmitter {
|
|
|
1192
1240
|
// scr_runtime.h's natural alignment: kind at 8, f64 at 16, b at 24,
|
|
1193
1241
|
// payload at 32).
|
|
1194
1242
|
`%ScrCaught = type { i64, i32, double, i8, ptr, ptr, ptr, ptr }`,
|
|
1243
|
+
// ScrBytes { rc, len, elem(i32+pad), data, backing }. Indexed
|
|
1244
|
+
// typed-array access GEPs through this directly: the IR type already
|
|
1245
|
+
// fixes elem, so the hot path needs neither a runtime kind load nor
|
|
1246
|
+
// the generic scr_bytes_get/set call.
|
|
1247
|
+
`%ScrBytes = type { i64, i64, i32, ptr, ptr }`,
|
|
1195
1248
|
// The capture box { rc, kind, obj_retain, obj_release, obj_trace,
|
|
1196
1249
|
// slot } — TDZ reads peek the payload slot (offset 40) directly.
|
|
1197
1250
|
`%ScrBox = type { i64, i32, ptr, ptr, ptr, i64 }`,
|
|
@@ -1696,6 +1749,7 @@ class LlEmitter {
|
|
|
1696
1749
|
tr.push(` call void @scr_promise_fulfill_void(ptr %pr)`);
|
|
1697
1750
|
break;
|
|
1698
1751
|
case "f64":
|
|
1752
|
+
case "date":
|
|
1699
1753
|
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
1700
1754
|
tr.push(` call void @scr_promise_fulfill_f64(ptr %pr, double %r)`);
|
|
1701
1755
|
break;
|
|
@@ -1851,6 +1905,7 @@ class LlEmitter {
|
|
|
1851
1905
|
tr.push(` br label %done ; void body: the done value is undefined (NONE)`);
|
|
1852
1906
|
break;
|
|
1853
1907
|
case "f64":
|
|
1908
|
+
case "date":
|
|
1854
1909
|
this.declare(`declare void @scr_gen_out_f64(ptr, double)`);
|
|
1855
1910
|
tr.push(` call void @scr_gen_out_f64(ptr %g, double %r)`, ` br label %done`);
|
|
1856
1911
|
break;
|
|
@@ -2072,7 +2127,7 @@ class LlEmitter {
|
|
|
2072
2127
|
const t = this.currentReturnType;
|
|
2073
2128
|
if (t.kind === "void")
|
|
2074
2129
|
this.B.terminate("ret void");
|
|
2075
|
-
else if (t.kind === "f64")
|
|
2130
|
+
else if (t.kind === "f64" || t.kind === "date")
|
|
2076
2131
|
this.B.terminate(`ret double ${f64Lit(0)}`);
|
|
2077
2132
|
else if (t.kind === "bool")
|
|
2078
2133
|
this.B.terminate("ret i1 false");
|
|
@@ -2104,7 +2159,10 @@ class LlEmitter {
|
|
|
2104
2159
|
emitThrowValue(v) {
|
|
2105
2160
|
const B = this.B;
|
|
2106
2161
|
const t = v.type;
|
|
2107
|
-
if (t.kind === "
|
|
2162
|
+
if (t.kind === "date") {
|
|
2163
|
+
throw new Error("LLVM emitter bug: Date throw reached backend");
|
|
2164
|
+
}
|
|
2165
|
+
else if (t.kind === "f64") {
|
|
2108
2166
|
this.declare(`declare void @scr_throw_f64(double)`);
|
|
2109
2167
|
B.line(`call void @scr_throw_f64(double ${v.name})`);
|
|
2110
2168
|
}
|
|
@@ -2153,6 +2211,8 @@ class LlEmitter {
|
|
|
2153
2211
|
B.line(`${t} = icmp ne i64 ${len}, 0`);
|
|
2154
2212
|
return t;
|
|
2155
2213
|
}
|
|
2214
|
+
case "date":
|
|
2215
|
+
return "true";
|
|
2156
2216
|
case "array":
|
|
2157
2217
|
case "record":
|
|
2158
2218
|
case "object":
|
|
@@ -2167,6 +2227,7 @@ class LlEmitter {
|
|
|
2167
2227
|
case "url":
|
|
2168
2228
|
case "searchParams":
|
|
2169
2229
|
case "stats":
|
|
2230
|
+
case "fileHandle":
|
|
2170
2231
|
case "spawnRes":
|
|
2171
2232
|
case "child":
|
|
2172
2233
|
case "childStream":
|
|
@@ -2247,6 +2308,9 @@ class LlEmitter {
|
|
|
2247
2308
|
B.line(`store i1 ${t}, ptr ${slot}`);
|
|
2248
2309
|
break;
|
|
2249
2310
|
}
|
|
2311
|
+
case "date":
|
|
2312
|
+
B.line(`store i1 true, ptr ${slot} ; Date objects are truthy`);
|
|
2313
|
+
break;
|
|
2250
2314
|
case "array":
|
|
2251
2315
|
case "record":
|
|
2252
2316
|
case "object":
|
|
@@ -2261,6 +2325,7 @@ class LlEmitter {
|
|
|
2261
2325
|
case "url":
|
|
2262
2326
|
case "searchParams":
|
|
2263
2327
|
case "stats":
|
|
2328
|
+
case "fileHandle":
|
|
2264
2329
|
case "spawnRes":
|
|
2265
2330
|
case "child":
|
|
2266
2331
|
case "childStream":
|
|
@@ -2595,6 +2660,7 @@ class LlEmitter {
|
|
|
2595
2660
|
this.jumpTargets = [];
|
|
2596
2661
|
this.currentLocals = new Map(fn.locals.map((l) => [l.id, l]));
|
|
2597
2662
|
this.captureIds = new Set((fn.captures ?? []).map((c) => c.localId));
|
|
2663
|
+
this.integerLoopBindings.clear();
|
|
2598
2664
|
this.chainSlots.clear();
|
|
2599
2665
|
this.finallyStack = [];
|
|
2600
2666
|
this.tryStack = [];
|
|
@@ -2791,13 +2857,17 @@ class LlEmitter {
|
|
|
2791
2857
|
}
|
|
2792
2858
|
case "bytesSet": {
|
|
2793
2859
|
// Typed-array element write: same evaluation order as arraySet;
|
|
2794
|
-
// the value is a scalar (the
|
|
2795
|
-
// ownership moves. Any invalid index traps — no
|
|
2796
|
-
|
|
2797
|
-
|
|
2860
|
+
// the value is a scalar (the kind-specific inline path coerces
|
|
2861
|
+
// JS-exactly), so no ownership moves. Any invalid index traps — no
|
|
2862
|
+
// append. IrBytesElem is static, so never rediscover it through the
|
|
2863
|
+
// generic runtime switch in a hot loop.
|
|
2864
|
+
const arr = this.emitBytesReceiver(s.arr, [s.index, s.value]);
|
|
2865
|
+
const integerIndex = this.emitIntegerLoopIndex(s.index);
|
|
2866
|
+
const idx = integerIndex ?? this.emitExpr(s.index).name;
|
|
2798
2867
|
const v = this.emitExpr(s.value);
|
|
2799
|
-
|
|
2800
|
-
|
|
2868
|
+
if (s.arr.type.kind !== "bytes")
|
|
2869
|
+
throw new Error("llvm emitter bug: bytesSet on non-bytes");
|
|
2870
|
+
this.emitBytesSet(s.arr.type.elem, arr.name, idx, v.name, integerIndex !== null);
|
|
2801
2871
|
break;
|
|
2802
2872
|
}
|
|
2803
2873
|
case "fieldSet":
|
|
@@ -3054,9 +3124,18 @@ class LlEmitter {
|
|
|
3054
3124
|
case "for": {
|
|
3055
3125
|
// The init's scope wraps the whole loop (break/continue must NOT
|
|
3056
3126
|
// release it — scopeDepth captured after the push, C parity).
|
|
3127
|
+
const integerLoop = matchIntegerBytesForLoop(s, this.currentLocals);
|
|
3057
3128
|
this.scopes.push([]);
|
|
3058
|
-
|
|
3129
|
+
let integerSlot = null;
|
|
3130
|
+
if (integerLoop) {
|
|
3131
|
+
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}`);
|
|
3134
|
+
this.integerLoopBindings.set(integerLoop.localId, integerSlot);
|
|
3135
|
+
}
|
|
3136
|
+
else if (s.init) {
|
|
3059
3137
|
this.emitStmt(s.init);
|
|
3138
|
+
}
|
|
3060
3139
|
const lc = B.newLabel("loop.c");
|
|
3061
3140
|
const lb = B.newLabel("loop.b");
|
|
3062
3141
|
const le = B.newLabel("loop.e");
|
|
@@ -3070,7 +3149,19 @@ class LlEmitter {
|
|
|
3070
3149
|
const lu = s.update || freshens ? B.newLabel("loop.u") : lc;
|
|
3071
3150
|
B.br(lc);
|
|
3072
3151
|
B.startBlock(lc);
|
|
3073
|
-
if (
|
|
3152
|
+
if (integerLoop && integerSlot) {
|
|
3153
|
+
const receiver = this.emitBytesReceiver(integerLoop.limitReceiver, []);
|
|
3154
|
+
const lenPtr = B.tmp();
|
|
3155
|
+
const len = B.tmp();
|
|
3156
|
+
const index = B.tmp();
|
|
3157
|
+
const inBounds = B.tmp();
|
|
3158
|
+
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}`);
|
|
3162
|
+
B.condBr(inBounds, lb, le);
|
|
3163
|
+
}
|
|
3164
|
+
else if (s.cond)
|
|
3074
3165
|
B.condBr(this.emitCondition(s.cond), lb, le);
|
|
3075
3166
|
else
|
|
3076
3167
|
B.br(lb);
|
|
@@ -3102,12 +3193,21 @@ class LlEmitter {
|
|
|
3102
3193
|
// The wrapper scope's entry releases whatever the slot points
|
|
3103
3194
|
// to at loop exit — now the freshest binding. Nothing to fix.
|
|
3104
3195
|
}
|
|
3105
|
-
if (
|
|
3196
|
+
if (integerLoop && integerSlot) {
|
|
3197
|
+
const old = B.tmp();
|
|
3198
|
+
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}`);
|
|
3202
|
+
}
|
|
3203
|
+
else if (s.update)
|
|
3106
3204
|
this.emitStmt(s.update);
|
|
3107
3205
|
B.br(lc);
|
|
3108
3206
|
}
|
|
3109
3207
|
B.startBlock(le);
|
|
3110
3208
|
this.releaseScope(this.scopes.pop());
|
|
3209
|
+
if (integerLoop)
|
|
3210
|
+
this.integerLoopBindings.delete(integerLoop.localId);
|
|
3111
3211
|
break;
|
|
3112
3212
|
}
|
|
3113
3213
|
case "forOf": {
|
|
@@ -3585,6 +3685,14 @@ class LlEmitter {
|
|
|
3585
3685
|
// tag-only); one reaching the generic dispatch escaped its wrap.
|
|
3586
3686
|
throw new Error(`llvm emitter bug: bare unitLit '${e.unit}'`);
|
|
3587
3687
|
case "varRef": {
|
|
3688
|
+
const integerSlot = this.integerLoopBindings.get(e.localId);
|
|
3689
|
+
if (integerSlot !== undefined) {
|
|
3690
|
+
const integer = B.tmp();
|
|
3691
|
+
const number = B.tmp();
|
|
3692
|
+
B.line(`${integer} = load i64, ptr ${integerSlot}`);
|
|
3693
|
+
B.line(`${number} = uitofp i64 ${integer} to double`);
|
|
3694
|
+
return { name: number, type: e.type };
|
|
3695
|
+
}
|
|
3588
3696
|
const b = this.binding(e.localId);
|
|
3589
3697
|
if (b.kind === "boxed") {
|
|
3590
3698
|
// Reads go through the shared binding; ref kinds come out +1.
|
|
@@ -5280,7 +5388,7 @@ class LlEmitter {
|
|
|
5280
5388
|
throw new Error("llvm emitter bug: yieldExpr with no operand (frontend fills undefined)");
|
|
5281
5389
|
const v = this.emitExpr(e.value);
|
|
5282
5390
|
const yt = e.value.type;
|
|
5283
|
-
if (yt.kind === "f64") {
|
|
5391
|
+
if (yt.kind === "f64" || yt.kind === "date") {
|
|
5284
5392
|
this.declare(`declare void @scr_gen_yield_f64(double)`);
|
|
5285
5393
|
B.line(`call void @scr_gen_yield_f64(double ${v.name})`);
|
|
5286
5394
|
}
|
|
@@ -5300,7 +5408,7 @@ class LlEmitter {
|
|
|
5300
5408
|
return { name: "", type: e.type };
|
|
5301
5409
|
}
|
|
5302
5410
|
const t = B.tmp();
|
|
5303
|
-
if (e.type.kind === "f64") {
|
|
5411
|
+
if (e.type.kind === "f64" || e.type.kind === "date") {
|
|
5304
5412
|
this.declare(`declare double @scr_gen_take_in_f64()`);
|
|
5305
5413
|
B.line(`${t} = call double @scr_gen_take_in_f64()`);
|
|
5306
5414
|
return { name: t, type: e.type };
|
|
@@ -5332,7 +5440,7 @@ class LlEmitter {
|
|
|
5332
5440
|
store(a.name, e.arg.type);
|
|
5333
5441
|
};
|
|
5334
5442
|
const parkIn = (name, t) => {
|
|
5335
|
-
if (t.kind === "f64") {
|
|
5443
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5336
5444
|
this.declare(`declare void @scr_gen_in_f64(ptr, double)`);
|
|
5337
5445
|
B.line(`call void @scr_gen_in_f64(ptr ${g.name}, double ${name})`);
|
|
5338
5446
|
}
|
|
@@ -5378,7 +5486,7 @@ class LlEmitter {
|
|
|
5378
5486
|
}
|
|
5379
5487
|
else {
|
|
5380
5488
|
sendArg((name, t) => {
|
|
5381
|
-
if (t.kind === "f64") {
|
|
5489
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5382
5490
|
this.declare(`declare void @scr_gen_ret_f64(ptr, double)`);
|
|
5383
5491
|
B.line(`call void @scr_gen_ret_f64(ptr ${g.name}, double ${name})`);
|
|
5384
5492
|
}
|
|
@@ -5431,7 +5539,7 @@ class LlEmitter {
|
|
|
5431
5539
|
return { name: "", type: e.type };
|
|
5432
5540
|
}
|
|
5433
5541
|
const t = B.tmp();
|
|
5434
|
-
if (e.type.kind === "f64") {
|
|
5542
|
+
if (e.type.kind === "f64" || e.type.kind === "date") {
|
|
5435
5543
|
this.declare(`declare double @scr_await_f64(ptr)`);
|
|
5436
5544
|
B.line(`${t} = call double @scr_await_f64(ptr ${pr.name})`);
|
|
5437
5545
|
}
|
|
@@ -5511,7 +5619,7 @@ class LlEmitter {
|
|
|
5511
5619
|
B.startBlock(lp);
|
|
5512
5620
|
const peek = this.unionPeek(u.name);
|
|
5513
5621
|
let awaited;
|
|
5514
|
-
if (inner.kind === "f64") {
|
|
5622
|
+
if (inner.kind === "f64" || inner.kind === "date") {
|
|
5515
5623
|
this.declare(`declare double @scr_await_f64(ptr)`);
|
|
5516
5624
|
const x = B.tmp();
|
|
5517
5625
|
B.line(`${x} = call double @scr_await_f64(ptr ${peek})`);
|
|
@@ -5588,7 +5696,7 @@ class LlEmitter {
|
|
|
5588
5696
|
return out;
|
|
5589
5697
|
}
|
|
5590
5698
|
let resolve;
|
|
5591
|
-
const kindNums = { f64: 0, bool: 1, string: 2, void: 3 };
|
|
5699
|
+
const kindNums = { f64: 0, date: 0, bool: 1, string: 2, void: 3 };
|
|
5592
5700
|
const kindNum = kindNums[inner.kind];
|
|
5593
5701
|
if (kindNum !== undefined) {
|
|
5594
5702
|
this.declare(`declare ptr @scr_make_resolve(ptr, i32)`);
|
|
@@ -5634,7 +5742,7 @@ class LlEmitter {
|
|
|
5634
5742
|
this.declare(`declare ptr @scr_make_reject(ptr)`);
|
|
5635
5743
|
const p = B.tmp();
|
|
5636
5744
|
B.line(`${p} = call ptr @scr_promise_new()`);
|
|
5637
|
-
const kindNums = { f64: 0, bool: 1, string: 2, void: 3 };
|
|
5745
|
+
const kindNums = { f64: 0, date: 0, bool: 1, string: 2, void: 3 };
|
|
5638
5746
|
const kindNum = kindNums[inner.kind];
|
|
5639
5747
|
const resolve = B.tmp();
|
|
5640
5748
|
if (kindNum !== undefined) {
|
|
@@ -5749,7 +5857,7 @@ class LlEmitter {
|
|
|
5749
5857
|
}
|
|
5750
5858
|
const v = this.emitExpr(e.args[0]);
|
|
5751
5859
|
const t = e.args[0].type;
|
|
5752
|
-
if (t.kind === "f64") {
|
|
5860
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5753
5861
|
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
5754
5862
|
B.line(`call void @scr_promise_fulfill_f64(ptr ${p}, double ${v.name})`);
|
|
5755
5863
|
}
|
|
@@ -7790,6 +7898,52 @@ class LlEmitter {
|
|
|
7790
7898
|
this.resolveThunkDefs.push(...d);
|
|
7791
7899
|
return sym;
|
|
7792
7900
|
}
|
|
7901
|
+
/** The fs.rename callback adapter: the runtime passes a borrowed Error
|
|
7902
|
+
* or NULL and the thunk constructs the callback's owned, program-shaped
|
|
7903
|
+
* Error | null union (or a dyn error/null in checkJs programs). */
|
|
7904
|
+
fsRenameThunkFor(cbT) {
|
|
7905
|
+
if (cbT.params.length === 0) {
|
|
7906
|
+
this.declare(`declare void @scr_fs_rename_thunk0(ptr, ptr)`);
|
|
7907
|
+
return "scr_fs_rename_thunk0";
|
|
7908
|
+
}
|
|
7909
|
+
const key = `fsren:${typeKey(cbT)}`;
|
|
7910
|
+
let sym = this.resolveThunks.get(key);
|
|
7911
|
+
if (sym)
|
|
7912
|
+
return sym;
|
|
7913
|
+
sym = `sc_fsren_${this.resolveThunks.size}`;
|
|
7914
|
+
this.resolveThunks.set(key, sym);
|
|
7915
|
+
const param = cbT.params[0];
|
|
7916
|
+
const d = [
|
|
7917
|
+
`define internal void @${sym}(ptr %cb, ptr %err) ${FN_ATTRS} { ; fs.rename callback ${typeKey(cbT)}`,
|
|
7918
|
+
`entry:`,
|
|
7919
|
+
` %slot = alloca ptr`,
|
|
7920
|
+
` %has = icmp ne ptr %err, null`,
|
|
7921
|
+
` br i1 %has, label %yes, label %no`,
|
|
7922
|
+
`yes:`,
|
|
7923
|
+
];
|
|
7924
|
+
if (param.kind === "dyn") {
|
|
7925
|
+
this.declare(`declare ptr @scr_dyn_from_error(ptr)`);
|
|
7926
|
+
this.declare(`declare ptr @scr_dyn_new_null()`);
|
|
7927
|
+
d.push(` %de = call ptr @scr_dyn_from_error(ptr %err)`, ` store ptr %de, ptr %slot`, ` br label %go`, `no:`, ` %dn = call ptr @scr_dyn_new_null()`, ` store ptr %dn, ptr %slot`);
|
|
7928
|
+
}
|
|
7929
|
+
else {
|
|
7930
|
+
if (param.kind !== "union")
|
|
7931
|
+
throw new Error("llvm emitter bug: fs.rename error param not a union");
|
|
7932
|
+
const def = this.unionsById.get(param.unionId);
|
|
7933
|
+
const errTag = def ? def.arms.findIndex((a) => a.kind === "object" && a.className === "%Error") : -1;
|
|
7934
|
+
const nullTag = def ? def.arms.findIndex((a) => a.kind === "nullT") : -1;
|
|
7935
|
+
if (errTag < 0 || nullTag < 0)
|
|
7936
|
+
throw new Error("llvm emitter bug: fs.rename error union lacks its arms");
|
|
7937
|
+
this.declare(`declare ptr @scr_error_retain(ptr)`);
|
|
7938
|
+
this.declare(`declare ptr @scr_error_retain_v(ptr)`);
|
|
7939
|
+
this.declare(`declare void @scr_error_release_v(ptr)`);
|
|
7940
|
+
this.declare(`declare ptr @scr_union_new_ref(i32, ptr, ptr, ptr, ptr)`);
|
|
7941
|
+
d.push(` %er = call ptr @scr_error_retain(ptr %err)`, ` %eu = call ptr @scr_union_new_ref(i32 ${errTag}, ptr %er, ptr @scr_error_retain_v, ptr @scr_error_release_v, ptr null)`, ` store ptr %eu, ptr %slot`, ` br label %go`, `no:`, ` store ptr ${this.unitInstanceRef(param.unionId, nullTag)}, ptr %slot`);
|
|
7942
|
+
}
|
|
7943
|
+
d.push(` br label %go`, `go:`, ` %arg = load ptr, ptr %slot`, ` %fnp = getelementptr inbounds %ScrClosure, ptr %cb, i64 0, i32 1`, ` %fn = load ptr, ptr %fnp`, ` call void %fn(ptr %cb, ptr %arg)`, ` ret void`, `}`, ``);
|
|
7944
|
+
this.resolveThunkDefs.push(...d);
|
|
7945
|
+
return sym;
|
|
7946
|
+
}
|
|
7793
7947
|
/** The stream option-callback invoke adapter for one (kind, callback
|
|
7794
7948
|
* type) — emit-async.ts's streamCbThunkFor: the runtime calls the
|
|
7795
7949
|
* user's read/write/final/destroy/transform/flush (or the finished/
|
|
@@ -8095,7 +8249,7 @@ class LlEmitter {
|
|
|
8095
8249
|
case "trimEnd":
|
|
8096
8250
|
return call("scr_str_trim_end", "ptr (ptr)", `ptr ${r.name}`, "ptr", true);
|
|
8097
8251
|
case "split":
|
|
8098
|
-
return call("
|
|
8252
|
+
return call("scr_str_split_limit", "ptr (ptr, ptr, double)", `ptr ${r.name}, ptr ${args[0].name}, double ${args[1].name}`, "ptr", true);
|
|
8099
8253
|
case "padStart":
|
|
8100
8254
|
return call("scr_str_pad_start", "ptr (ptr, double, ptr)", `ptr ${r.name}, double ${args[0].name}, ptr ${args[1].name}`, "ptr", true);
|
|
8101
8255
|
case "padEnd":
|
|
@@ -8167,6 +8321,33 @@ class LlEmitter {
|
|
|
8167
8321
|
B.line(`${t} = call double @scr_arr_len(ptr ${r.name})`);
|
|
8168
8322
|
return { name: t, type: e.type };
|
|
8169
8323
|
}
|
|
8324
|
+
case "unshift": {
|
|
8325
|
+
// Evaluate every argument before the first mutation, then insert
|
|
8326
|
+
// from right to left so the final front order is source order.
|
|
8327
|
+
const vs = e.args.map((a) => this.emitExpr(a));
|
|
8328
|
+
if (acc === "ref")
|
|
8329
|
+
vs.forEach((v) => this.moveTemp(v));
|
|
8330
|
+
this.declare(`declare double @scr_arr_unshift_${acc}(ptr, ${accArg})`);
|
|
8331
|
+
let last = "";
|
|
8332
|
+
for (let i = vs.length - 1; i >= 0; i--) {
|
|
8333
|
+
last = B.tmp();
|
|
8334
|
+
B.line(`${last} = call double @scr_arr_unshift_${acc}(ptr ${r.name}, ${accTy} ${vs[i].name})`);
|
|
8335
|
+
}
|
|
8336
|
+
if (last !== "")
|
|
8337
|
+
return { name: last, type: e.type };
|
|
8338
|
+
this.declare(`declare double @scr_arr_len(ptr)`);
|
|
8339
|
+
const t = B.tmp();
|
|
8340
|
+
B.line(`${t} = call double @scr_arr_len(ptr ${r.name})`);
|
|
8341
|
+
return { name: t, type: e.type };
|
|
8342
|
+
}
|
|
8343
|
+
case "unshiftSpread": {
|
|
8344
|
+
// The runtime snapshots the borrowed source and handles self-spread.
|
|
8345
|
+
const src = this.emitExpr(e.args[0]);
|
|
8346
|
+
this.declare(`declare double @scr_arr_unshift_spread(ptr, ptr)`);
|
|
8347
|
+
const t = B.tmp();
|
|
8348
|
+
B.line(`${t} = call double @scr_arr_unshift_spread(ptr ${r.name}, ptr ${src.name})`);
|
|
8349
|
+
return { name: t, type: e.type };
|
|
8350
|
+
}
|
|
8170
8351
|
case "pop": {
|
|
8171
8352
|
// Ownership of a refcounted element moves OUT of the array to
|
|
8172
8353
|
// this temp (+1 to us, the runtime does not release it).
|
|
@@ -8226,6 +8407,13 @@ class LlEmitter {
|
|
|
8226
8407
|
B.line(`${t} = call ptr @scr_arr_to_reversed(ptr ${r.name})`);
|
|
8227
8408
|
return this.own({ name: t, type: e.type });
|
|
8228
8409
|
}
|
|
8410
|
+
case "reverse": {
|
|
8411
|
+
// Mutates in place and returns the same receiver as a fresh +1.
|
|
8412
|
+
this.declare(`declare ptr @scr_arr_reverse(ptr)`);
|
|
8413
|
+
const t = B.tmp();
|
|
8414
|
+
B.line(`${t} = call ptr @scr_arr_reverse(ptr ${r.name})`);
|
|
8415
|
+
return this.own({ name: t, type: e.type });
|
|
8416
|
+
}
|
|
8229
8417
|
case "toSpliced": {
|
|
8230
8418
|
const start = this.emitExpr(e.args[0]);
|
|
8231
8419
|
const count = this.emitExpr(e.args[1]);
|
|
@@ -8625,6 +8813,260 @@ class LlEmitter {
|
|
|
8625
8813
|
}
|
|
8626
8814
|
}
|
|
8627
8815
|
}
|
|
8816
|
+
/** Whether an index/value expression can overwrite the bytes receiver
|
|
8817
|
+
* binding. Kept deliberately conservative; uncertain or calling shapes
|
|
8818
|
+
* retain the ordinary owned-receiver snapshot. */
|
|
8819
|
+
isStableBytesOperand(e, receiverLocalId) {
|
|
8820
|
+
switch (e.kind) {
|
|
8821
|
+
case "numLit":
|
|
8822
|
+
case "boolLit":
|
|
8823
|
+
case "varRef":
|
|
8824
|
+
case "incDec":
|
|
8825
|
+
return true;
|
|
8826
|
+
case "assignExpr":
|
|
8827
|
+
// A bytes assignment nested under truthiness/ternary can still
|
|
8828
|
+
// produce the numeric index/value expected by the outer access.
|
|
8829
|
+
return (e.localId !== receiverLocalId &&
|
|
8830
|
+
this.isStableBytesOperand(e.value, receiverLocalId));
|
|
8831
|
+
case "bin":
|
|
8832
|
+
return (this.isStableBytesOperand(e.left, receiverLocalId) &&
|
|
8833
|
+
this.isStableBytesOperand(e.right, receiverLocalId));
|
|
8834
|
+
case "unary":
|
|
8835
|
+
case "toBool":
|
|
8836
|
+
return this.isStableBytesOperand(e.operand, receiverLocalId);
|
|
8837
|
+
case "logical":
|
|
8838
|
+
return (this.isStableBytesOperand(e.left, receiverLocalId) &&
|
|
8839
|
+
this.isStableBytesOperand(e.right, receiverLocalId));
|
|
8840
|
+
case "ternary":
|
|
8841
|
+
return (this.isStableBytesOperand(e.cond, receiverLocalId) &&
|
|
8842
|
+
this.isStableBytesOperand(e.then, receiverLocalId) &&
|
|
8843
|
+
this.isStableBytesOperand(e.else_, receiverLocalId));
|
|
8844
|
+
case "bytesIntrinsic":
|
|
8845
|
+
return ((e.method === "get" || e.method === "length" || e.method === "byteLength") &&
|
|
8846
|
+
e.receiver.kind === "varRef" &&
|
|
8847
|
+
e.args.every((arg) => this.isStableBytesOperand(arg, receiverLocalId)));
|
|
8848
|
+
default:
|
|
8849
|
+
return false;
|
|
8850
|
+
}
|
|
8851
|
+
}
|
|
8852
|
+
/** Borrow a direct, unboxed bytes binding when later operands are stable.
|
|
8853
|
+
* Its scope/global owner keeps it alive through the access. */
|
|
8854
|
+
emitBytesReceiver(receiver, following) {
|
|
8855
|
+
if (receiver.kind === "varRef" &&
|
|
8856
|
+
following.every((operand) => this.isStableBytesOperand(operand, receiver.localId))) {
|
|
8857
|
+
const b = this.binding(receiver.localId);
|
|
8858
|
+
if (b.kind !== "boxed") {
|
|
8859
|
+
const value = this.B.tmp();
|
|
8860
|
+
this.B.line(`${value} = load ptr, ptr ${b.slot}`);
|
|
8861
|
+
return { name: value, type: receiver.type };
|
|
8862
|
+
}
|
|
8863
|
+
}
|
|
8864
|
+
return this.emitExpr(receiver);
|
|
8865
|
+
}
|
|
8866
|
+
/** Load a canonical integer-loop induction value for a direct index use.
|
|
8867
|
+
* Other expression shapes retain ordinary f64 evaluation. */
|
|
8868
|
+
emitIntegerLoopIndex(expr) {
|
|
8869
|
+
if (expr.kind !== "varRef")
|
|
8870
|
+
return null;
|
|
8871
|
+
const slot = this.integerLoopBindings.get(expr.localId);
|
|
8872
|
+
if (slot === undefined)
|
|
8873
|
+
return null;
|
|
8874
|
+
const index = this.B.tmp();
|
|
8875
|
+
this.B.line(`${index} = load i64, ptr ${slot}`);
|
|
8876
|
+
return index;
|
|
8877
|
+
}
|
|
8878
|
+
/** Bounds-check a typed-array element index without an out-of-line call
|
|
8879
|
+
* on the valid path. The range test rejects NaN/infinity before fptoui;
|
|
8880
|
+
* the integer round trip then rejects fractions. The error block retains
|
|
8881
|
+
* the runtime's exact trap text. Leaves the builder in the valid block. */
|
|
8882
|
+
emitBytesIndex(receiver, index, integerIndex = false) {
|
|
8883
|
+
const B = this.B;
|
|
8884
|
+
const lenPtr = B.tmp();
|
|
8885
|
+
const len = B.tmp();
|
|
8886
|
+
B.line(`${lenPtr} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
|
|
8887
|
+
B.line(`${len} = load i64, ptr ${lenPtr}`);
|
|
8888
|
+
if (integerIndex) {
|
|
8889
|
+
const inRange = B.tmp();
|
|
8890
|
+
B.line(`${inRange} = icmp ult i64 ${index}, ${len}`);
|
|
8891
|
+
const invalid = B.newLabel("bytes.index.invalid");
|
|
8892
|
+
const valid = B.newLabel("bytes.index.valid");
|
|
8893
|
+
B.condBr(inRange, valid, invalid);
|
|
8894
|
+
B.startBlock(invalid);
|
|
8895
|
+
const indexF64 = B.tmp();
|
|
8896
|
+
B.line(`${indexF64} = uitofp i64 ${index} to double`);
|
|
8897
|
+
this.declare(`declare double @scr_bytes_get(ptr, double)`);
|
|
8898
|
+
B.line(`call double @scr_bytes_get(ptr ${receiver}, double ${indexF64})`);
|
|
8899
|
+
B.terminate("unreachable");
|
|
8900
|
+
B.startBlock(valid);
|
|
8901
|
+
return index;
|
|
8902
|
+
}
|
|
8903
|
+
const lenF64 = B.tmp();
|
|
8904
|
+
const nonnegative = B.tmp();
|
|
8905
|
+
const belowLen = B.tmp();
|
|
8906
|
+
const inRange = B.tmp();
|
|
8907
|
+
B.line(`${lenF64} = uitofp i64 ${len} to double`);
|
|
8908
|
+
B.line(`${nonnegative} = fcmp oge double ${index}, ${f64Lit(0)}`);
|
|
8909
|
+
B.line(`${belowLen} = fcmp olt double ${index}, ${lenF64}`);
|
|
8910
|
+
B.line(`${inRange} = and i1 ${nonnegative}, ${belowLen}`);
|
|
8911
|
+
const rangeOk = B.newLabel("bytes.index.range");
|
|
8912
|
+
const invalid = B.newLabel("bytes.index.invalid");
|
|
8913
|
+
const valid = B.newLabel("bytes.index.valid");
|
|
8914
|
+
B.condBr(inRange, rangeOk, invalid);
|
|
8915
|
+
B.startBlock(rangeOk);
|
|
8916
|
+
const idx = B.tmp();
|
|
8917
|
+
const roundTrip = B.tmp();
|
|
8918
|
+
const integral = B.tmp();
|
|
8919
|
+
B.line(`${idx} = fptoui double ${index} to i64`);
|
|
8920
|
+
B.line(`${roundTrip} = uitofp i64 ${idx} to double`);
|
|
8921
|
+
B.line(`${integral} = fcmp oeq double ${roundTrip}, ${index}`);
|
|
8922
|
+
B.condBr(integral, valid, invalid);
|
|
8923
|
+
B.startBlock(invalid);
|
|
8924
|
+
this.declare(`declare double @scr_bytes_get(ptr, double)`);
|
|
8925
|
+
B.line(`call double @scr_bytes_get(ptr ${receiver}, double ${index})`);
|
|
8926
|
+
B.terminate("unreachable");
|
|
8927
|
+
B.startBlock(valid);
|
|
8928
|
+
return idx;
|
|
8929
|
+
}
|
|
8930
|
+
/** Load the raw data pointer from ScrBytes after the bounds check. */
|
|
8931
|
+
emitBytesData(receiver) {
|
|
8932
|
+
const p = this.B.tmp();
|
|
8933
|
+
const data = this.B.tmp();
|
|
8934
|
+
this.B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 3`);
|
|
8935
|
+
this.B.line(`${data} = load ptr, ptr ${p}`);
|
|
8936
|
+
return data;
|
|
8937
|
+
}
|
|
8938
|
+
/** Static typed-array length/byteLength load. */
|
|
8939
|
+
emitBytesLength(elem, receiver, bytes) {
|
|
8940
|
+
const B = this.B;
|
|
8941
|
+
const p = B.tmp();
|
|
8942
|
+
const len = B.tmp();
|
|
8943
|
+
B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
|
|
8944
|
+
B.line(`${len} = load i64, ptr ${p}`);
|
|
8945
|
+
const count = bytes && elem !== "u8" ? B.tmp() : len;
|
|
8946
|
+
if (count !== len)
|
|
8947
|
+
B.line(`${count} = shl i64 ${len}, 2`);
|
|
8948
|
+
const out = B.tmp();
|
|
8949
|
+
B.line(`${out} = uitofp i64 ${count} to double`);
|
|
8950
|
+
return { name: out, type: F64 };
|
|
8951
|
+
}
|
|
8952
|
+
/** Kind-specialized typed-array load. */
|
|
8953
|
+
emitBytesGet(elem, receiver, index, integerIndex = false) {
|
|
8954
|
+
const B = this.B;
|
|
8955
|
+
const idx = this.emitBytesIndex(receiver, index, integerIndex);
|
|
8956
|
+
const data = this.emitBytesData(receiver);
|
|
8957
|
+
const p = B.tmp();
|
|
8958
|
+
if (elem === "u8") {
|
|
8959
|
+
const raw = B.tmp();
|
|
8960
|
+
const wide = B.tmp();
|
|
8961
|
+
const out = B.tmp();
|
|
8962
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
|
|
8963
|
+
B.line(`${raw} = load i8, ptr ${p}, align 1`);
|
|
8964
|
+
B.line(`${wide} = zext i8 ${raw} to i32`);
|
|
8965
|
+
B.line(`${out} = uitofp i32 ${wide} to double`);
|
|
8966
|
+
return { name: out, type: F64 };
|
|
8967
|
+
}
|
|
8968
|
+
if (elem === "f32") {
|
|
8969
|
+
const raw = B.tmp();
|
|
8970
|
+
const out = B.tmp();
|
|
8971
|
+
B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
|
|
8972
|
+
B.line(`${raw} = load float, ptr ${p}, align 1`);
|
|
8973
|
+
B.line(`${out} = fpext float ${raw} to double`);
|
|
8974
|
+
return { name: out, type: F64 };
|
|
8975
|
+
}
|
|
8976
|
+
const raw = B.tmp();
|
|
8977
|
+
const out = B.tmp();
|
|
8978
|
+
B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
|
|
8979
|
+
B.line(`${raw} = load i32, ptr ${p}, align 1`);
|
|
8980
|
+
B.line(`${out} = ${elem === "i32" ? "sitofp" : "uitofp"} i32 ${raw} to double`);
|
|
8981
|
+
return { name: out, type: F64 };
|
|
8982
|
+
}
|
|
8983
|
+
/** ToUint32/ToInt32 coercion with the overwhelmingly common finite,
|
|
8984
|
+
* exactly-representable JS-number range on a cast-only path. Exceptional
|
|
8985
|
+
* and huge values remain inline in the program TU so unrelated runtime
|
|
8986
|
+
* binaries do not acquire another exported helper. */
|
|
8987
|
+
emitBytesU32(value) {
|
|
8988
|
+
const B = this.B;
|
|
8989
|
+
const aboveMin = B.tmp();
|
|
8990
|
+
const belowMax = B.tmp();
|
|
8991
|
+
const fast = B.tmp();
|
|
8992
|
+
B.line(`${aboveMin} = fcmp oge double ${value}, ${f64Lit(-9007199254740992)}`);
|
|
8993
|
+
B.line(`${belowMax} = fcmp ole double ${value}, ${f64Lit(9007199254740992)}`);
|
|
8994
|
+
B.line(`${fast} = and i1 ${aboveMin}, ${belowMax}`);
|
|
8995
|
+
const fastLabel = B.newLabel("bytes.coerce.fast");
|
|
8996
|
+
const slowLabel = B.newLabel("bytes.coerce.slow");
|
|
8997
|
+
const done = B.newLabel("bytes.coerce.done");
|
|
8998
|
+
B.condBr(fast, fastLabel, slowLabel);
|
|
8999
|
+
B.startBlock(fastLabel);
|
|
9000
|
+
const signed = B.tmp();
|
|
9001
|
+
const fastU32 = B.tmp();
|
|
9002
|
+
B.line(`${signed} = fptosi double ${value} to i64`);
|
|
9003
|
+
B.line(`${fastU32} = trunc i64 ${signed} to i32`);
|
|
9004
|
+
B.br(done);
|
|
9005
|
+
B.startBlock(slowLabel);
|
|
9006
|
+
const ordered = B.tmp();
|
|
9007
|
+
const belowInf = B.tmp();
|
|
9008
|
+
const aboveNegInf = B.tmp();
|
|
9009
|
+
const finiteRange = B.tmp();
|
|
9010
|
+
const finite = B.tmp();
|
|
9011
|
+
B.line(`${ordered} = fcmp ord double ${value}, ${value}`);
|
|
9012
|
+
B.line(`${belowInf} = fcmp olt double ${value}, ${F64_INF}`);
|
|
9013
|
+
B.line(`${aboveNegInf} = fcmp ogt double ${value}, ${f64Lit(-Infinity)}`);
|
|
9014
|
+
B.line(`${finiteRange} = and i1 ${belowInf}, ${aboveNegInf}`);
|
|
9015
|
+
B.line(`${finite} = and i1 ${ordered}, ${finiteRange}`);
|
|
9016
|
+
const finiteLabel = B.newLabel("bytes.coerce.finite");
|
|
9017
|
+
const nonfiniteLabel = B.newLabel("bytes.coerce.nonfinite");
|
|
9018
|
+
const slowDone = B.newLabel("bytes.coerce.slow.done");
|
|
9019
|
+
B.condBr(finite, finiteLabel, nonfiniteLabel);
|
|
9020
|
+
B.startBlock(finiteLabel);
|
|
9021
|
+
this.declare(`declare double @llvm.trunc.f64(double)`);
|
|
9022
|
+
const truncated = B.tmp();
|
|
9023
|
+
const residue = B.tmp();
|
|
9024
|
+
const negative = B.tmp();
|
|
9025
|
+
const wrapped = B.tmp();
|
|
9026
|
+
const normalized = B.tmp();
|
|
9027
|
+
const finiteU32 = B.tmp();
|
|
9028
|
+
B.line(`${truncated} = call double @llvm.trunc.f64(double ${value})`);
|
|
9029
|
+
B.line(`${residue} = frem double ${truncated}, ${f64Lit(4294967296)}`);
|
|
9030
|
+
B.line(`${negative} = fcmp olt double ${residue}, ${f64Lit(0)}`);
|
|
9031
|
+
B.line(`${wrapped} = fadd double ${residue}, ${f64Lit(4294967296)}`);
|
|
9032
|
+
B.line(`${normalized} = select i1 ${negative}, double ${wrapped}, double ${residue}`);
|
|
9033
|
+
B.line(`${finiteU32} = fptoui double ${normalized} to i32`);
|
|
9034
|
+
B.br(slowDone);
|
|
9035
|
+
B.startBlock(nonfiniteLabel);
|
|
9036
|
+
B.br(slowDone);
|
|
9037
|
+
B.startBlock(slowDone);
|
|
9038
|
+
const slowU32 = B.tmp();
|
|
9039
|
+
B.line(`${slowU32} = phi i32 [ ${finiteU32}, %${finiteLabel} ], [ 0, %${nonfiniteLabel} ]`);
|
|
9040
|
+
B.br(done);
|
|
9041
|
+
B.startBlock(done);
|
|
9042
|
+
const out = B.tmp();
|
|
9043
|
+
B.line(`${out} = phi i32 [ ${fastU32}, %${fastLabel} ], [ ${slowU32}, %${slowDone} ]`);
|
|
9044
|
+
return out;
|
|
9045
|
+
}
|
|
9046
|
+
/** Kind-specialized typed-array store. */
|
|
9047
|
+
emitBytesSet(elem, receiver, index, value, integerIndex = false) {
|
|
9048
|
+
const B = this.B;
|
|
9049
|
+
const idx = this.emitBytesIndex(receiver, index, integerIndex);
|
|
9050
|
+
const stored = elem === "f32" ? null : this.emitBytesU32(value);
|
|
9051
|
+
const data = this.emitBytesData(receiver);
|
|
9052
|
+
const p = B.tmp();
|
|
9053
|
+
if (elem === "u8") {
|
|
9054
|
+
const byte = B.tmp();
|
|
9055
|
+
B.line(`${byte} = trunc i32 ${stored} to i8`);
|
|
9056
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
|
|
9057
|
+
B.line(`store i8 ${byte}, ptr ${p}, align 1`);
|
|
9058
|
+
return;
|
|
9059
|
+
}
|
|
9060
|
+
if (elem === "f32") {
|
|
9061
|
+
const narrowed = B.tmp();
|
|
9062
|
+
B.line(`${narrowed} = fptrunc double ${value} to float`);
|
|
9063
|
+
B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
|
|
9064
|
+
B.line(`store float ${narrowed}, ptr ${p}, align 1`);
|
|
9065
|
+
return;
|
|
9066
|
+
}
|
|
9067
|
+
B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
|
|
9068
|
+
B.line(`store i32 ${stored}, ptr ${p}, align 1`);
|
|
9069
|
+
}
|
|
8628
9070
|
/** The bytesIntrinsic surface (emit-exprs.ts's switch, .ll flavored).
|
|
8629
9071
|
* Receiver and args are borrowed frame temps; string/bytes results come
|
|
8630
9072
|
* back +1. The MAY_THROW methods get the standard pending check after
|
|
@@ -8672,18 +9114,31 @@ class LlEmitter {
|
|
|
8672
9114
|
? 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)
|
|
8673
9115
|
: 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);
|
|
8674
9116
|
}
|
|
8675
|
-
const r = this.emitExpr(e.receiver);
|
|
8676
|
-
const args = e.args.map((a) => this.emitExpr(a));
|
|
8677
9117
|
const method = e.method;
|
|
9118
|
+
const directElementAccess = method === "length" || method === "byteLength" || method === "get";
|
|
9119
|
+
const r = directElementAccess
|
|
9120
|
+
? this.emitBytesReceiver(e.receiver, e.args)
|
|
9121
|
+
: this.emitExpr(e.receiver);
|
|
9122
|
+
const integerIndex = method === "get" ? this.emitIntegerLoopIndex(e.args[0]) : null;
|
|
9123
|
+
const args = integerIndex === null ? e.args.map((a) => this.emitExpr(a)) : [];
|
|
8678
9124
|
const NAN = f64Lit(NaN);
|
|
8679
9125
|
switch (method) {
|
|
8680
9126
|
case "length":
|
|
8681
|
-
|
|
9127
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
9128
|
+
throw new Error("llvm emitter bug: bytesIntrinsic length on non-bytes");
|
|
9129
|
+
}
|
|
9130
|
+
return this.emitBytesLength(e.receiver.type.elem, r.name, false);
|
|
8682
9131
|
case "byteLength":
|
|
8683
|
-
|
|
9132
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
9133
|
+
throw new Error("llvm emitter bug: bytesIntrinsic byteLength on non-bytes");
|
|
9134
|
+
}
|
|
9135
|
+
return this.emitBytesLength(e.receiver.type.elem, r.name, true);
|
|
8684
9136
|
case "get":
|
|
8685
9137
|
// Any invalid index traps (the array runtime's discipline).
|
|
8686
|
-
|
|
9138
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
9139
|
+
throw new Error("llvm emitter bug: bytesIntrinsic get on non-bytes");
|
|
9140
|
+
}
|
|
9141
|
+
return this.emitBytesGet(e.receiver.type.elem, r.name, integerIndex ?? args[0].name, integerIndex !== null);
|
|
8687
9142
|
case "slice":
|
|
8688
9143
|
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);
|
|
8689
9144
|
case "subarray":
|
|
@@ -8702,21 +9157,26 @@ class LlEmitter {
|
|
|
8702
9157
|
// dst.set(src, offset?) — void; throws Node's RangeError on
|
|
8703
9158
|
// overflow.
|
|
8704
9159
|
return call("scr_bytes_set_from", "void (ptr, ptr, double)", `ptr ${r.name}, ptr ${args[0].name}, double ${args[1]?.name ?? f64Lit(0)}`, false, true);
|
|
8705
|
-
case "toString":
|
|
9160
|
+
case "toString":
|
|
9161
|
+
case "toStringVar": {
|
|
8706
9162
|
// The encoding arg is always present (the frontend completes an
|
|
8707
|
-
// omitted one to "utf8").
|
|
8708
|
-
//
|
|
8709
|
-
//
|
|
9163
|
+
// omitted one to "utf8"). The runtime-valued form validates and
|
|
9164
|
+
// may throw ERR_UNKNOWN_ENCODING; the literal form is canonical
|
|
9165
|
+
// and cannot throw. Both return +1. Range forms: [enc, start]
|
|
9166
|
+
// decode to the buffer's end (the element count — r->len in the C
|
|
9167
|
+
// spelling); [enc, start, end] clamps.
|
|
9168
|
+
const checked = method === "toStringVar";
|
|
9169
|
+
const stem = checked ? "scr_bytes_to_str_checked" : "scr_bytes_to_str";
|
|
8710
9170
|
if (e.args.length === 3) {
|
|
8711
|
-
return call(
|
|
9171
|
+
return call(`${stem}_range`, "ptr (ptr, ptr, double, double)", `ptr ${r.name}, ptr ${args[0].name}, double ${args[1].name}, double ${args[2].name}`, true, checked);
|
|
8712
9172
|
}
|
|
8713
9173
|
if (e.args.length === 2) {
|
|
8714
9174
|
this.declare(`declare double @scr_bytes_len(ptr)`);
|
|
8715
9175
|
const len = B.tmp();
|
|
8716
9176
|
B.line(`${len} = call double @scr_bytes_len(ptr ${r.name})`);
|
|
8717
|
-
return call(
|
|
9177
|
+
return call(`${stem}_range`, "ptr (ptr, ptr, double, double)", `ptr ${r.name}, ptr ${args[0].name}, double ${args[1].name}, double ${len}`, true, checked);
|
|
8718
9178
|
}
|
|
8719
|
-
return call(
|
|
9179
|
+
return call(stem, "ptr (ptr, ptr)", `ptr ${r.name}, ptr ${args[0].name}`, true, checked);
|
|
8720
9180
|
}
|
|
8721
9181
|
case "equals":
|
|
8722
9182
|
return call("scr_bytes_equals", "zeroext i1 (ptr, ptr)", `ptr ${r.name}, ptr ${args[0].name}`, false, false);
|
|
@@ -8845,7 +9305,14 @@ class LlEmitter {
|
|
|
8845
9305
|
case "replaceAll":
|
|
8846
9306
|
return fallible("scr_regex_replace_all", `ptr ${r.name}, ptr ${args[0].name}, ptr ${args[1].name}`);
|
|
8847
9307
|
case "split":
|
|
8848
|
-
|
|
9308
|
+
this.declare(`declare ptr @scr_regex_split_limit(ptr, ptr, double)`);
|
|
9309
|
+
{
|
|
9310
|
+
const t = B.tmp();
|
|
9311
|
+
B.line(`${t} = call ptr @scr_regex_split_limit(ptr ${r.name}, ptr ${args[0].name}, double ${args[1].name})`);
|
|
9312
|
+
const out = this.own({ name: t, type: e.type });
|
|
9313
|
+
this.emitPendingCheck();
|
|
9314
|
+
return out;
|
|
9315
|
+
}
|
|
8849
9316
|
case "test": {
|
|
8850
9317
|
this.declare(`declare zeroext i1 @scr_regex_test(ptr, ptr)`);
|
|
8851
9318
|
const t = B.tmp();
|
|
@@ -9383,9 +9850,14 @@ class LlEmitter {
|
|
|
9383
9850
|
for (const field of fields) {
|
|
9384
9851
|
const index = shape.fields.indexOf(field) + 1;
|
|
9385
9852
|
const fieldPtr = B.tmp();
|
|
9386
|
-
|
|
9853
|
+
let fieldValue = B.tmp();
|
|
9387
9854
|
B.line(`${fieldPtr} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %p, i64 0, i32 ${index}`);
|
|
9388
9855
|
B.line(`${fieldValue} = load ${llFieldType(field.type)}, ptr ${fieldPtr}`);
|
|
9856
|
+
if (llFieldType(field.type) === "i8") {
|
|
9857
|
+
const boolValue = B.tmp();
|
|
9858
|
+
B.line(`${boolValue} = trunc i8 ${fieldValue} to i1`);
|
|
9859
|
+
fieldValue = boolValue;
|
|
9860
|
+
}
|
|
9389
9861
|
const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
|
|
9390
9862
|
B.line(`call void @scr_dyn_arr_push(ptr ${out}, ptr ${boxed})`);
|
|
9391
9863
|
}
|
|
@@ -9406,9 +9878,14 @@ class LlEmitter {
|
|
|
9406
9878
|
for (const field of fields) {
|
|
9407
9879
|
const index = shape.fields.indexOf(field) + 1;
|
|
9408
9880
|
const fieldPtr = B.tmp();
|
|
9409
|
-
|
|
9881
|
+
let fieldValue = B.tmp();
|
|
9410
9882
|
B.line(`${fieldPtr} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %p, i64 0, i32 ${index}`);
|
|
9411
9883
|
B.line(`${fieldValue} = load ${llFieldType(field.type)}, ptr ${fieldPtr}`);
|
|
9884
|
+
if (llFieldType(field.type) === "i8") {
|
|
9885
|
+
const boolValue = B.tmp();
|
|
9886
|
+
B.line(`${boolValue} = trunc i8 ${fieldValue} to i1`);
|
|
9887
|
+
fieldValue = boolValue;
|
|
9888
|
+
}
|
|
9412
9889
|
const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
|
|
9413
9890
|
B.line(`call void @scr_dyn_obj_set(ptr ${out}, ptr ${this.cstr(field.name)}, i64 ${Buffer.byteLength(field.name, "utf8")}, ptr ${boxed})`);
|
|
9414
9891
|
}
|
|
@@ -9592,6 +10069,71 @@ class LlEmitter {
|
|
|
9592
10069
|
// Loop liveness first (one table for generic and special shapes).
|
|
9593
10070
|
if (USES_TIMERS_LIB_FNS.has(e.fn))
|
|
9594
10071
|
this.usesTimers = true;
|
|
10072
|
+
if (e.fn === "process.stdoutWriteBytesCb" || e.fn === "process.stderrWriteBytesCb") {
|
|
10073
|
+
// All arguments evaluate before bytes are submitted. The callback
|
|
10074
|
+
// then moves into the next-tick entry and its error-first adapter
|
|
10075
|
+
// constructs Node's success `null` argument when the tick fires.
|
|
10076
|
+
this.usesTimers = true;
|
|
10077
|
+
const args = e.args.map((a) => this.emitExpr(a));
|
|
10078
|
+
const cbT = e.args[2].type;
|
|
10079
|
+
if (cbT.kind !== "func")
|
|
10080
|
+
throw new Error("llvm emitter bug: process write callback not a func");
|
|
10081
|
+
this.moveTemp(args[2]);
|
|
10082
|
+
const adapter = this.fsRenameThunkFor(cbT);
|
|
10083
|
+
const write = e.fn === "process.stdoutWriteBytesCb"
|
|
10084
|
+
? "scr_process_stdout_write_bytes"
|
|
10085
|
+
: "scr_process_stderr_write_bytes";
|
|
10086
|
+
this.declare(`declare zeroext i1 @${write}(ptr, ptr)`);
|
|
10087
|
+
this.declare(`declare void @scr_process_write_callback(ptr, ptr)`);
|
|
10088
|
+
const out = B.tmp();
|
|
10089
|
+
B.line(`${out} = call i1 @${write}(ptr ${args[0].name}, ptr ${args[1].name})`);
|
|
10090
|
+
B.line(`call void @scr_process_write_callback(ptr ${args[2].name}, ptr @${adapter})`);
|
|
10091
|
+
return { name: out, type: e.type };
|
|
10092
|
+
}
|
|
10093
|
+
if (e.fn === "fs.renameCb") {
|
|
10094
|
+
// The callback MOVES into the next-turn operation. Its emitted
|
|
10095
|
+
// adapter materializes the callback's Error | null union (or the
|
|
10096
|
+
// checkJs dyn argument) from the runtime's borrowed ScrError.
|
|
10097
|
+
this.usesTimers = true;
|
|
10098
|
+
const args = e.args.map((a) => this.emitExpr(a));
|
|
10099
|
+
const cbT = e.args[2].type;
|
|
10100
|
+
if (cbT.kind !== "func")
|
|
10101
|
+
throw new Error("llvm emitter bug: fs.rename callback not a func");
|
|
10102
|
+
this.moveTemp(args[2]);
|
|
10103
|
+
const adapter = this.fsRenameThunkFor(cbT);
|
|
10104
|
+
this.declare(`declare void @scr_fs_rename_async(ptr, ptr, ptr, ptr)`);
|
|
10105
|
+
B.line(`call void @scr_fs_rename_async(ptr ${args[0].name}, ptr ${args[1].name}, ptr ${args[2].name}, ptr @${adapter})`);
|
|
10106
|
+
return { name: "", type: e.type };
|
|
10107
|
+
}
|
|
10108
|
+
if (e.fn === "fileHandle.read" || e.fn === "fileHandle.writeBytes" || e.fn === "fileHandle.writeStr") {
|
|
10109
|
+
if (e.type.kind !== "promise" || e.type.inner.kind !== "record") {
|
|
10110
|
+
throw new Error(`llvm emitter bug: ${e.fn} result`);
|
|
10111
|
+
}
|
|
10112
|
+
const args = e.args.map((a) => this.emitExpr(a));
|
|
10113
|
+
const sym = e.fn === "fileHandle.read"
|
|
10114
|
+
? "scr_file_handle_read"
|
|
10115
|
+
: e.fn === "fileHandle.writeBytes"
|
|
10116
|
+
? "scr_file_handle_write_bytes"
|
|
10117
|
+
: "scr_file_handle_write_str";
|
|
10118
|
+
const tail = e.fn === "fileHandle.writeStr"
|
|
10119
|
+
? "ptr, ptr, double, ptr"
|
|
10120
|
+
: "ptr, ptr, double, double, double, i1 zeroext";
|
|
10121
|
+
this.declare(`declare double @${sym}(${tail})`);
|
|
10122
|
+
const count = B.tmp();
|
|
10123
|
+
B.line(`${count} = call double @${sym}(${args.map((a) => `${this.llType(a.type)} ${a.name}`).join(", ")})`);
|
|
10124
|
+
const inner = e.type.inner;
|
|
10125
|
+
const rec = B.tmp();
|
|
10126
|
+
B.line(`${rec} = call ptr @${mangleRecordNew(inner.shapeId)}()`);
|
|
10127
|
+
const countField = e.fn === "fileHandle.read" ? "bytesRead" : "bytesWritten";
|
|
10128
|
+
this.storeField(this.recordFieldPtr(rec, inner.shapeId, countField).ptr, F64, count);
|
|
10129
|
+
const payload = this.retainValue(args[1].name, e.args[1].type);
|
|
10130
|
+
B.line(`store ptr ${payload}, ptr ${this.recordFieldPtr(rec, inner.shapeId, "buffer").ptr}`);
|
|
10131
|
+
const rc = vAdapters(this, inner);
|
|
10132
|
+
this.declare(`declare ptr @scr_promise_settled_ref(ptr, ptr, ptr, ptr)`);
|
|
10133
|
+
const result = B.tmp();
|
|
10134
|
+
B.line(`${result} = call ptr @scr_promise_settled_ref(ptr ${rec}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${traceArg(this, inner)})`);
|
|
10135
|
+
return this.own({ name: result, type: e.type });
|
|
10136
|
+
}
|
|
9595
10137
|
if (e.fn === "fetch.responseText" || e.fn === "fetch.responseBytes") {
|
|
9596
10138
|
if (e.type.kind !== "promise") {
|
|
9597
10139
|
throw new Error(`llvm emitter bug: ${e.fn} result`);
|