@scriptc/compiler 0.0.23 → 0.0.25
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 +100 -11
- 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 +31 -1
- package/dist/backend/emission/emit-async.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +147 -14
- 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 +24 -5
- package/dist/backend/emission/emit-stmts.js.map +1 -1
- package/dist/backend/emission/emit-types.js +10 -0
- package/dist/backend/emission/emit-types.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +10 -2
- package/dist/backend/emission/emitter.js +49 -30
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.js +284 -25
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.js +9 -1
- 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/frontend/lowering/lower-builtins.d.ts +14 -9
- package/dist/frontend/lowering/lower-builtins.js +737 -62
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +2 -1
- package/dist/frontend/lowering/lower-calls.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.js +53 -2
- package/dist/frontend/lowering/lower-exprs.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-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 +45 -10
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +2 -2
- package/dist/frontend/lowering/lowerer.js +11 -6
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +65 -13
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/types.d.ts +3 -0
- package/dist/frontend/types.js +78 -0
- 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 +84 -21
- package/dist/ir/nodes.js +84 -1
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +92 -18
- 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 +50 -2
|
@@ -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",
|
|
@@ -174,6 +176,7 @@ const LIB_FN_SYMS = {
|
|
|
174
176
|
"fs.unlinkSync": "scr_fs_unlink",
|
|
175
177
|
"fs.chmodSync": "scr_fs_chmod",
|
|
176
178
|
"fs.copyFileSync": "scr_fs_copyfile",
|
|
179
|
+
"fs.renameSync": "scr_fs_rename",
|
|
177
180
|
"fs.accessSync": "scr_fs_access",
|
|
178
181
|
// node:assert (scr_assert.c): all args borrowed; failures throw the
|
|
179
182
|
// catchable AssertionError. The never-throwing members ride along.
|
|
@@ -236,6 +239,8 @@ const LIB_FN_SYMS = {
|
|
|
236
239
|
"fs.lstatSync": "scr_fs_lstat",
|
|
237
240
|
"fs.openSync": "scr_fs_open",
|
|
238
241
|
"fs.readSync": "scr_fs_read_sync",
|
|
242
|
+
"fs.writeSync": "scr_fs_write_sync",
|
|
243
|
+
"fs.writeStrSync": "scr_fs_write_str_sync",
|
|
239
244
|
"fs.closeSync": "scr_fs_close",
|
|
240
245
|
// string_decoder (scr_bytes.c): stateless helpers over (enc, pending,
|
|
241
246
|
// chunk) — never throw.
|
|
@@ -246,8 +251,12 @@ const LIB_FN_SYMS = {
|
|
|
246
251
|
"stats.isDirectory": "scr_stats_is_dir",
|
|
247
252
|
"stats.isSymbolicLink": "scr_stats_is_symlink",
|
|
248
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",
|
|
249
257
|
"stats.mtimeMs": "scr_stats_mtime_ms",
|
|
250
258
|
"text.decode": "scr_text_decode",
|
|
259
|
+
"text.decodeLegacy": "scr_text_decode_legacy",
|
|
251
260
|
"zlib.deflateSync": "scr_zlib_deflate",
|
|
252
261
|
"zlib.inflateSync": "scr_zlib_inflate",
|
|
253
262
|
// The CA-store unit. get/set throw (an unknown type name, a
|
|
@@ -355,15 +364,25 @@ const LIB_FN_SYMS = {
|
|
|
355
364
|
// so it is special-cased in emitLibCall (the fs.readFileSync pattern).
|
|
356
365
|
"fsp.readFileBytes": "scr_fsp_read_file_bytes",
|
|
357
366
|
"fsp.writeFile": "scr_fsp_write_file",
|
|
367
|
+
"fsp.writeFileMode": "scr_fsp_write_file_mode",
|
|
358
368
|
"fsp.mkdir": "scr_fsp_mkdir",
|
|
359
369
|
"fsp.mkdirMode": "scr_fsp_mkdir_mode",
|
|
360
370
|
"fsp.mkdirRecursive": "scr_fsp_mkdir_recursive",
|
|
361
371
|
"fsp.mkdirRecursiveMode": "scr_fsp_mkdir_recursive_mode",
|
|
362
372
|
"fsp.unlink": "scr_fsp_unlink",
|
|
363
373
|
"fsp.chmod": "scr_fsp_chmod",
|
|
374
|
+
"fsp.rename": "scr_fsp_rename",
|
|
364
375
|
"fsp.readdir": "scr_fsp_readdir",
|
|
365
376
|
"fsp.rm": "scr_fsp_rm",
|
|
366
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",
|
|
367
386
|
"sym.new": "scr_sym_new",
|
|
368
387
|
"sym.for": "scr_sym_for",
|
|
369
388
|
"sym.toString": "scr_sym_to_string",
|
|
@@ -854,6 +873,8 @@ class LlEmitter {
|
|
|
854
873
|
jumpTargets = [];
|
|
855
874
|
currentLocals = new Map();
|
|
856
875
|
captureIds = new Set();
|
|
876
|
+
/** Active canonical byte-loop induction bindings: local id → i64 slot. */
|
|
877
|
+
integerLoopBindings = new Map();
|
|
857
878
|
/** Enclosing try-with-FINALLY regions, innermost last: a `return`
|
|
858
879
|
* inside one runs every crossed finally (innermost first) before the
|
|
859
880
|
* actual ret — the C emitter's pending-return path, with the finally
|
|
@@ -983,6 +1004,7 @@ class LlEmitter {
|
|
|
983
1004
|
case "url":
|
|
984
1005
|
case "searchParams":
|
|
985
1006
|
case "stats":
|
|
1007
|
+
case "fileHandle":
|
|
986
1008
|
case "spawnRes":
|
|
987
1009
|
case "child":
|
|
988
1010
|
case "childStream":
|
|
@@ -2205,6 +2227,7 @@ class LlEmitter {
|
|
|
2205
2227
|
case "url":
|
|
2206
2228
|
case "searchParams":
|
|
2207
2229
|
case "stats":
|
|
2230
|
+
case "fileHandle":
|
|
2208
2231
|
case "spawnRes":
|
|
2209
2232
|
case "child":
|
|
2210
2233
|
case "childStream":
|
|
@@ -2302,6 +2325,7 @@ class LlEmitter {
|
|
|
2302
2325
|
case "url":
|
|
2303
2326
|
case "searchParams":
|
|
2304
2327
|
case "stats":
|
|
2328
|
+
case "fileHandle":
|
|
2305
2329
|
case "spawnRes":
|
|
2306
2330
|
case "child":
|
|
2307
2331
|
case "childStream":
|
|
@@ -2636,6 +2660,7 @@ class LlEmitter {
|
|
|
2636
2660
|
this.jumpTargets = [];
|
|
2637
2661
|
this.currentLocals = new Map(fn.locals.map((l) => [l.id, l]));
|
|
2638
2662
|
this.captureIds = new Set((fn.captures ?? []).map((c) => c.localId));
|
|
2663
|
+
this.integerLoopBindings.clear();
|
|
2639
2664
|
this.chainSlots.clear();
|
|
2640
2665
|
this.finallyStack = [];
|
|
2641
2666
|
this.tryStack = [];
|
|
@@ -2837,11 +2862,12 @@ class LlEmitter {
|
|
|
2837
2862
|
// append. IrBytesElem is static, so never rediscover it through the
|
|
2838
2863
|
// generic runtime switch in a hot loop.
|
|
2839
2864
|
const arr = this.emitBytesReceiver(s.arr, [s.index, s.value]);
|
|
2840
|
-
const
|
|
2865
|
+
const integerIndex = this.emitIntegerLoopIndex(s.index);
|
|
2866
|
+
const idx = integerIndex ?? this.emitExpr(s.index).name;
|
|
2841
2867
|
const v = this.emitExpr(s.value);
|
|
2842
2868
|
if (s.arr.type.kind !== "bytes")
|
|
2843
2869
|
throw new Error("llvm emitter bug: bytesSet on non-bytes");
|
|
2844
|
-
this.emitBytesSet(s.arr.type.elem, arr.name, idx
|
|
2870
|
+
this.emitBytesSet(s.arr.type.elem, arr.name, idx, v.name, integerIndex !== null);
|
|
2845
2871
|
break;
|
|
2846
2872
|
}
|
|
2847
2873
|
case "fieldSet":
|
|
@@ -3098,9 +3124,18 @@ class LlEmitter {
|
|
|
3098
3124
|
case "for": {
|
|
3099
3125
|
// The init's scope wraps the whole loop (break/continue must NOT
|
|
3100
3126
|
// release it — scopeDepth captured after the push, C parity).
|
|
3127
|
+
const integerLoop = matchIntegerBytesForLoop(s, this.currentLocals);
|
|
3101
3128
|
this.scopes.push([]);
|
|
3102
|
-
|
|
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) {
|
|
3103
3137
|
this.emitStmt(s.init);
|
|
3138
|
+
}
|
|
3104
3139
|
const lc = B.newLabel("loop.c");
|
|
3105
3140
|
const lb = B.newLabel("loop.b");
|
|
3106
3141
|
const le = B.newLabel("loop.e");
|
|
@@ -3114,7 +3149,19 @@ class LlEmitter {
|
|
|
3114
3149
|
const lu = s.update || freshens ? B.newLabel("loop.u") : lc;
|
|
3115
3150
|
B.br(lc);
|
|
3116
3151
|
B.startBlock(lc);
|
|
3117
|
-
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)
|
|
3118
3165
|
B.condBr(this.emitCondition(s.cond), lb, le);
|
|
3119
3166
|
else
|
|
3120
3167
|
B.br(lb);
|
|
@@ -3146,12 +3193,21 @@ class LlEmitter {
|
|
|
3146
3193
|
// The wrapper scope's entry releases whatever the slot points
|
|
3147
3194
|
// to at loop exit — now the freshest binding. Nothing to fix.
|
|
3148
3195
|
}
|
|
3149
|
-
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)
|
|
3150
3204
|
this.emitStmt(s.update);
|
|
3151
3205
|
B.br(lc);
|
|
3152
3206
|
}
|
|
3153
3207
|
B.startBlock(le);
|
|
3154
3208
|
this.releaseScope(this.scopes.pop());
|
|
3209
|
+
if (integerLoop)
|
|
3210
|
+
this.integerLoopBindings.delete(integerLoop.localId);
|
|
3155
3211
|
break;
|
|
3156
3212
|
}
|
|
3157
3213
|
case "forOf": {
|
|
@@ -3629,6 +3685,14 @@ class LlEmitter {
|
|
|
3629
3685
|
// tag-only); one reaching the generic dispatch escaped its wrap.
|
|
3630
3686
|
throw new Error(`llvm emitter bug: bare unitLit '${e.unit}'`);
|
|
3631
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
|
+
}
|
|
3632
3696
|
const b = this.binding(e.localId);
|
|
3633
3697
|
if (b.kind === "boxed") {
|
|
3634
3698
|
// Reads go through the shared binding; ref kinds come out +1.
|
|
@@ -7834,6 +7898,52 @@ class LlEmitter {
|
|
|
7834
7898
|
this.resolveThunkDefs.push(...d);
|
|
7835
7899
|
return sym;
|
|
7836
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
|
+
}
|
|
7837
7947
|
/** The stream option-callback invoke adapter for one (kind, callback
|
|
7838
7948
|
* type) — emit-async.ts's streamCbThunkFor: the runtime calls the
|
|
7839
7949
|
* user's read/write/final/destroy/transform/flush (or the finished/
|
|
@@ -8139,7 +8249,7 @@ class LlEmitter {
|
|
|
8139
8249
|
case "trimEnd":
|
|
8140
8250
|
return call("scr_str_trim_end", "ptr (ptr)", `ptr ${r.name}`, "ptr", true);
|
|
8141
8251
|
case "split":
|
|
8142
|
-
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);
|
|
8143
8253
|
case "padStart":
|
|
8144
8254
|
return call("scr_str_pad_start", "ptr (ptr, double, ptr)", `ptr ${r.name}, double ${args[0].name}, ptr ${args[1].name}`, "ptr", true);
|
|
8145
8255
|
case "padEnd":
|
|
@@ -8211,6 +8321,33 @@ class LlEmitter {
|
|
|
8211
8321
|
B.line(`${t} = call double @scr_arr_len(ptr ${r.name})`);
|
|
8212
8322
|
return { name: t, type: e.type };
|
|
8213
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
|
+
}
|
|
8214
8351
|
case "pop": {
|
|
8215
8352
|
// Ownership of a refcounted element moves OUT of the array to
|
|
8216
8353
|
// this temp (+1 to us, the runtime does not release it).
|
|
@@ -8270,6 +8407,13 @@ class LlEmitter {
|
|
|
8270
8407
|
B.line(`${t} = call ptr @scr_arr_to_reversed(ptr ${r.name})`);
|
|
8271
8408
|
return this.own({ name: t, type: e.type });
|
|
8272
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
|
+
}
|
|
8273
8417
|
case "toSpliced": {
|
|
8274
8418
|
const start = this.emitExpr(e.args[0]);
|
|
8275
8419
|
const count = this.emitExpr(e.args[1]);
|
|
@@ -8719,20 +8863,47 @@ class LlEmitter {
|
|
|
8719
8863
|
}
|
|
8720
8864
|
return this.emitExpr(receiver);
|
|
8721
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
|
+
}
|
|
8722
8878
|
/** Bounds-check a typed-array element index without an out-of-line call
|
|
8723
8879
|
* on the valid path. The range test rejects NaN/infinity before fptoui;
|
|
8724
8880
|
* the integer round trip then rejects fractions. The error block retains
|
|
8725
8881
|
* the runtime's exact trap text. Leaves the builder in the valid block. */
|
|
8726
|
-
emitBytesIndex(receiver, index) {
|
|
8882
|
+
emitBytesIndex(receiver, index, integerIndex = false) {
|
|
8727
8883
|
const B = this.B;
|
|
8728
8884
|
const lenPtr = B.tmp();
|
|
8729
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
|
+
}
|
|
8730
8903
|
const lenF64 = B.tmp();
|
|
8731
8904
|
const nonnegative = B.tmp();
|
|
8732
8905
|
const belowLen = B.tmp();
|
|
8733
8906
|
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
8907
|
B.line(`${lenF64} = uitofp i64 ${len} to double`);
|
|
8737
8908
|
B.line(`${nonnegative} = fcmp oge double ${index}, ${f64Lit(0)}`);
|
|
8738
8909
|
B.line(`${belowLen} = fcmp olt double ${index}, ${lenF64}`);
|
|
@@ -8779,9 +8950,9 @@ class LlEmitter {
|
|
|
8779
8950
|
return { name: out, type: F64 };
|
|
8780
8951
|
}
|
|
8781
8952
|
/** Kind-specialized typed-array load. */
|
|
8782
|
-
emitBytesGet(elem, receiver, index) {
|
|
8953
|
+
emitBytesGet(elem, receiver, index, integerIndex = false) {
|
|
8783
8954
|
const B = this.B;
|
|
8784
|
-
const idx = this.emitBytesIndex(receiver, index);
|
|
8955
|
+
const idx = this.emitBytesIndex(receiver, index, integerIndex);
|
|
8785
8956
|
const data = this.emitBytesData(receiver);
|
|
8786
8957
|
const p = B.tmp();
|
|
8787
8958
|
if (elem === "u8") {
|
|
@@ -8873,9 +9044,9 @@ class LlEmitter {
|
|
|
8873
9044
|
return out;
|
|
8874
9045
|
}
|
|
8875
9046
|
/** Kind-specialized typed-array store. */
|
|
8876
|
-
emitBytesSet(elem, receiver, index, value) {
|
|
9047
|
+
emitBytesSet(elem, receiver, index, value, integerIndex = false) {
|
|
8877
9048
|
const B = this.B;
|
|
8878
|
-
const idx = this.emitBytesIndex(receiver, index);
|
|
9049
|
+
const idx = this.emitBytesIndex(receiver, index, integerIndex);
|
|
8879
9050
|
const stored = elem === "f32" ? null : this.emitBytesU32(value);
|
|
8880
9051
|
const data = this.emitBytesData(receiver);
|
|
8881
9052
|
const p = B.tmp();
|
|
@@ -8948,7 +9119,8 @@ class LlEmitter {
|
|
|
8948
9119
|
const r = directElementAccess
|
|
8949
9120
|
? this.emitBytesReceiver(e.receiver, e.args)
|
|
8950
9121
|
: this.emitExpr(e.receiver);
|
|
8951
|
-
const
|
|
9122
|
+
const integerIndex = method === "get" ? this.emitIntegerLoopIndex(e.args[0]) : null;
|
|
9123
|
+
const args = integerIndex === null ? e.args.map((a) => this.emitExpr(a)) : [];
|
|
8952
9124
|
const NAN = f64Lit(NaN);
|
|
8953
9125
|
switch (method) {
|
|
8954
9126
|
case "length":
|
|
@@ -8966,7 +9138,7 @@ class LlEmitter {
|
|
|
8966
9138
|
if (e.receiver.type.kind !== "bytes") {
|
|
8967
9139
|
throw new Error("llvm emitter bug: bytesIntrinsic get on non-bytes");
|
|
8968
9140
|
}
|
|
8969
|
-
return this.emitBytesGet(e.receiver.type.elem, r.name, args[0].name);
|
|
9141
|
+
return this.emitBytesGet(e.receiver.type.elem, r.name, integerIndex ?? args[0].name, integerIndex !== null);
|
|
8970
9142
|
case "slice":
|
|
8971
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);
|
|
8972
9144
|
case "subarray":
|
|
@@ -8985,21 +9157,26 @@ class LlEmitter {
|
|
|
8985
9157
|
// dst.set(src, offset?) — void; throws Node's RangeError on
|
|
8986
9158
|
// overflow.
|
|
8987
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);
|
|
8988
|
-
case "toString":
|
|
9160
|
+
case "toString":
|
|
9161
|
+
case "toStringVar": {
|
|
8989
9162
|
// The encoding arg is always present (the frontend completes an
|
|
8990
|
-
// omitted one to "utf8").
|
|
8991
|
-
//
|
|
8992
|
-
//
|
|
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";
|
|
8993
9170
|
if (e.args.length === 3) {
|
|
8994
|
-
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);
|
|
8995
9172
|
}
|
|
8996
9173
|
if (e.args.length === 2) {
|
|
8997
9174
|
this.declare(`declare double @scr_bytes_len(ptr)`);
|
|
8998
9175
|
const len = B.tmp();
|
|
8999
9176
|
B.line(`${len} = call double @scr_bytes_len(ptr ${r.name})`);
|
|
9000
|
-
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);
|
|
9001
9178
|
}
|
|
9002
|
-
return call(
|
|
9179
|
+
return call(stem, "ptr (ptr, ptr)", `ptr ${r.name}, ptr ${args[0].name}`, true, checked);
|
|
9003
9180
|
}
|
|
9004
9181
|
case "equals":
|
|
9005
9182
|
return call("scr_bytes_equals", "zeroext i1 (ptr, ptr)", `ptr ${r.name}, ptr ${args[0].name}`, false, false);
|
|
@@ -9128,7 +9305,14 @@ class LlEmitter {
|
|
|
9128
9305
|
case "replaceAll":
|
|
9129
9306
|
return fallible("scr_regex_replace_all", `ptr ${r.name}, ptr ${args[0].name}, ptr ${args[1].name}`);
|
|
9130
9307
|
case "split":
|
|
9131
|
-
|
|
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
|
+
}
|
|
9132
9316
|
case "test": {
|
|
9133
9317
|
this.declare(`declare zeroext i1 @scr_regex_test(ptr, ptr)`);
|
|
9134
9318
|
const t = B.tmp();
|
|
@@ -9666,9 +9850,14 @@ class LlEmitter {
|
|
|
9666
9850
|
for (const field of fields) {
|
|
9667
9851
|
const index = shape.fields.indexOf(field) + 1;
|
|
9668
9852
|
const fieldPtr = B.tmp();
|
|
9669
|
-
|
|
9853
|
+
let fieldValue = B.tmp();
|
|
9670
9854
|
B.line(`${fieldPtr} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %p, i64 0, i32 ${index}`);
|
|
9671
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
|
+
}
|
|
9672
9861
|
const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
|
|
9673
9862
|
B.line(`call void @scr_dyn_arr_push(ptr ${out}, ptr ${boxed})`);
|
|
9674
9863
|
}
|
|
@@ -9689,9 +9878,14 @@ class LlEmitter {
|
|
|
9689
9878
|
for (const field of fields) {
|
|
9690
9879
|
const index = shape.fields.indexOf(field) + 1;
|
|
9691
9880
|
const fieldPtr = B.tmp();
|
|
9692
|
-
|
|
9881
|
+
let fieldValue = B.tmp();
|
|
9693
9882
|
B.line(`${fieldPtr} = getelementptr inbounds %${mangleRecordStruct(t.shapeId)}, ptr %p, i64 0, i32 ${index}`);
|
|
9694
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
|
+
}
|
|
9695
9889
|
const boxed = this.streamTypedRefBoxValue(B, field.type, fieldValue, ctx);
|
|
9696
9890
|
B.line(`call void @scr_dyn_obj_set(ptr ${out}, ptr ${this.cstr(field.name)}, i64 ${Buffer.byteLength(field.name, "utf8")}, ptr ${boxed})`);
|
|
9697
9891
|
}
|
|
@@ -9875,6 +10069,71 @@ class LlEmitter {
|
|
|
9875
10069
|
// Loop liveness first (one table for generic and special shapes).
|
|
9876
10070
|
if (USES_TIMERS_LIB_FNS.has(e.fn))
|
|
9877
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
|
+
}
|
|
9878
10137
|
if (e.fn === "fetch.responseText" || e.fn === "fetch.responseBytes") {
|
|
9879
10138
|
if (e.type.kind !== "promise") {
|
|
9880
10139
|
throw new Error(`llvm emitter bug: ${e.fn} result`);
|