@scriptc/compiler 0.0.22 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +39 -3
- package/dist/backend/emission/emit-async.js +3 -1
- package/dist/backend/emission/emit-async.js.map +1 -1
- package/dist/backend/emission/emit-exprs.d.ts +10 -0
- package/dist/backend/emission/emit-exprs.js +127 -10
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-stmts.js +13 -5
- package/dist/backend/emission/emit-stmts.js.map +1 -1
- package/dist/backend/emission/emit-types.js +4 -1
- package/dist/backend/emission/emit-types.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +11 -1
- package/dist/backend/emission/emitter.js +61 -1
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.js +304 -21
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/shapes.js +3 -2
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/compat/fetch-profile.d.ts +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 +3 -7
- package/dist/frontend/lowering/lower-builtins.js +80 -55
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +1 -0
- 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-exprs.d.ts +1 -1
- package/dist/frontend/lowering/lower-exprs.js +25 -4
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.js +7 -1
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +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-stmts.js +8 -4
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +1 -0
- package/dist/frontend/lowering/lowerer.js +6 -2
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.js +43 -12
- 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 +4 -4
- package/dist/frontend/types.js +25 -8
- package/dist/frontend/types.js.map +1 -1
- package/dist/ir/nodes.d.ts +24 -11
- package/dist/ir/nodes.js +6 -1
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +45 -8
- package/dist/ir/validate.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +137 -28
|
@@ -129,13 +129,36 @@ const LIB_FN_SYMS = {
|
|
|
129
129
|
"process.stderrWrite": "scr_process_stderr_write",
|
|
130
130
|
"process.isTTY": "scr_process_is_tty",
|
|
131
131
|
"date.now": "scr_date_now",
|
|
132
|
+
"date.newNow": "scr_date_now",
|
|
133
|
+
"date.newMs": "scr_date_new_ms",
|
|
134
|
+
"date.newString": "scr_date_parse_get_time",
|
|
135
|
+
"date.getTime": "scr_date_get_time",
|
|
136
|
+
"date.valueOf": "scr_date_get_time",
|
|
132
137
|
"date.parseGetTime": "scr_date_parse_get_time",
|
|
133
138
|
"date.utc": "scr_date_utc",
|
|
139
|
+
"date.getFullYear": "scr_date_get_full_year_local",
|
|
140
|
+
"date.getUTCFullYear": "scr_date_get_full_year_utc",
|
|
141
|
+
"date.getMonth": "scr_date_get_month_local",
|
|
142
|
+
"date.getUTCMonth": "scr_date_get_month_utc",
|
|
143
|
+
"date.getDate": "scr_date_get_date_local",
|
|
144
|
+
"date.getUTCDate": "scr_date_get_date_utc",
|
|
145
|
+
"date.getDay": "scr_date_get_day_local",
|
|
146
|
+
"date.getUTCDay": "scr_date_get_day_utc",
|
|
147
|
+
"date.getHours": "scr_date_get_hours_local",
|
|
148
|
+
"date.getUTCHours": "scr_date_get_hours_utc",
|
|
149
|
+
"date.getMinutes": "scr_date_get_minutes_local",
|
|
150
|
+
"date.getUTCMinutes": "scr_date_get_minutes_utc",
|
|
151
|
+
"date.getSeconds": "scr_date_get_seconds_local",
|
|
152
|
+
"date.getUTCSeconds": "scr_date_get_seconds_utc",
|
|
153
|
+
"date.getMilliseconds": "scr_date_get_milliseconds",
|
|
154
|
+
"date.getUTCMilliseconds": "scr_date_get_milliseconds",
|
|
155
|
+
"date.getTimezoneOffset": "scr_date_get_timezone_offset",
|
|
134
156
|
"fs.existsSync": "scr_fs_exists",
|
|
135
157
|
// ── the throwing slice (MAY_THROW_LIB_FNS members): the generic path
|
|
136
158
|
// emits the standard pending check after each — emit-exprs.ts's finish.
|
|
137
159
|
"process.cpuPrevValidate": "scr_cpu_prev_validate",
|
|
138
160
|
"date.toISOString": "scr_date_to_iso",
|
|
161
|
+
"date.toISOStringValue": "scr_date_to_iso",
|
|
139
162
|
"process.chdir": "scr_process_chdir",
|
|
140
163
|
"fs.writeFileSync": "scr_fs_write_file",
|
|
141
164
|
"fs.appendFileSync": "scr_fs_append_file",
|
|
@@ -352,9 +375,11 @@ const LIB_FN_SYMS = {
|
|
|
352
375
|
// the generic path emits the standard pending check). typeof and the
|
|
353
376
|
// ambient-this read never throw. The fs dyn read is the sync-fs story.
|
|
354
377
|
"fetch.start": "scr_fetch_static",
|
|
378
|
+
"fetch.responseNew": "scr_fetch_response_new",
|
|
355
379
|
"fetch.responseJson": "scr_fetch_response_json",
|
|
356
380
|
"fetch.responseText": "scr_fetch_response_text",
|
|
357
381
|
"fetch.responseBytes": "scr_fetch_response_bytes",
|
|
382
|
+
"fetch.abortControllerNew": "scr_fetch_abort_controller_new",
|
|
358
383
|
"fetch.abortTimeout": "scr_fetch_abort_timeout",
|
|
359
384
|
"fetch.abortNow": "scr_fetch_abort_now",
|
|
360
385
|
"fetch.abortAny": "scr_fetch_abort_any",
|
|
@@ -940,6 +965,7 @@ class LlEmitter {
|
|
|
940
965
|
llType(t) {
|
|
941
966
|
switch (t.kind) {
|
|
942
967
|
case "f64":
|
|
968
|
+
case "date":
|
|
943
969
|
return "double";
|
|
944
970
|
case "bool":
|
|
945
971
|
return "i1";
|
|
@@ -1192,6 +1218,11 @@ class LlEmitter {
|
|
|
1192
1218
|
// scr_runtime.h's natural alignment: kind at 8, f64 at 16, b at 24,
|
|
1193
1219
|
// payload at 32).
|
|
1194
1220
|
`%ScrCaught = type { i64, i32, double, i8, ptr, ptr, ptr, ptr }`,
|
|
1221
|
+
// ScrBytes { rc, len, elem(i32+pad), data, backing }. Indexed
|
|
1222
|
+
// typed-array access GEPs through this directly: the IR type already
|
|
1223
|
+
// fixes elem, so the hot path needs neither a runtime kind load nor
|
|
1224
|
+
// the generic scr_bytes_get/set call.
|
|
1225
|
+
`%ScrBytes = type { i64, i64, i32, ptr, ptr }`,
|
|
1195
1226
|
// The capture box { rc, kind, obj_retain, obj_release, obj_trace,
|
|
1196
1227
|
// slot } — TDZ reads peek the payload slot (offset 40) directly.
|
|
1197
1228
|
`%ScrBox = type { i64, i32, ptr, ptr, ptr, i64 }`,
|
|
@@ -1696,6 +1727,7 @@ class LlEmitter {
|
|
|
1696
1727
|
tr.push(` call void @scr_promise_fulfill_void(ptr %pr)`);
|
|
1697
1728
|
break;
|
|
1698
1729
|
case "f64":
|
|
1730
|
+
case "date":
|
|
1699
1731
|
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
1700
1732
|
tr.push(` call void @scr_promise_fulfill_f64(ptr %pr, double %r)`);
|
|
1701
1733
|
break;
|
|
@@ -1851,6 +1883,7 @@ class LlEmitter {
|
|
|
1851
1883
|
tr.push(` br label %done ; void body: the done value is undefined (NONE)`);
|
|
1852
1884
|
break;
|
|
1853
1885
|
case "f64":
|
|
1886
|
+
case "date":
|
|
1854
1887
|
this.declare(`declare void @scr_gen_out_f64(ptr, double)`);
|
|
1855
1888
|
tr.push(` call void @scr_gen_out_f64(ptr %g, double %r)`, ` br label %done`);
|
|
1856
1889
|
break;
|
|
@@ -2072,7 +2105,7 @@ class LlEmitter {
|
|
|
2072
2105
|
const t = this.currentReturnType;
|
|
2073
2106
|
if (t.kind === "void")
|
|
2074
2107
|
this.B.terminate("ret void");
|
|
2075
|
-
else if (t.kind === "f64")
|
|
2108
|
+
else if (t.kind === "f64" || t.kind === "date")
|
|
2076
2109
|
this.B.terminate(`ret double ${f64Lit(0)}`);
|
|
2077
2110
|
else if (t.kind === "bool")
|
|
2078
2111
|
this.B.terminate("ret i1 false");
|
|
@@ -2104,7 +2137,10 @@ class LlEmitter {
|
|
|
2104
2137
|
emitThrowValue(v) {
|
|
2105
2138
|
const B = this.B;
|
|
2106
2139
|
const t = v.type;
|
|
2107
|
-
if (t.kind === "
|
|
2140
|
+
if (t.kind === "date") {
|
|
2141
|
+
throw new Error("LLVM emitter bug: Date throw reached backend");
|
|
2142
|
+
}
|
|
2143
|
+
else if (t.kind === "f64") {
|
|
2108
2144
|
this.declare(`declare void @scr_throw_f64(double)`);
|
|
2109
2145
|
B.line(`call void @scr_throw_f64(double ${v.name})`);
|
|
2110
2146
|
}
|
|
@@ -2153,6 +2189,8 @@ class LlEmitter {
|
|
|
2153
2189
|
B.line(`${t} = icmp ne i64 ${len}, 0`);
|
|
2154
2190
|
return t;
|
|
2155
2191
|
}
|
|
2192
|
+
case "date":
|
|
2193
|
+
return "true";
|
|
2156
2194
|
case "array":
|
|
2157
2195
|
case "record":
|
|
2158
2196
|
case "object":
|
|
@@ -2247,6 +2285,9 @@ class LlEmitter {
|
|
|
2247
2285
|
B.line(`store i1 ${t}, ptr ${slot}`);
|
|
2248
2286
|
break;
|
|
2249
2287
|
}
|
|
2288
|
+
case "date":
|
|
2289
|
+
B.line(`store i1 true, ptr ${slot} ; Date objects are truthy`);
|
|
2290
|
+
break;
|
|
2250
2291
|
case "array":
|
|
2251
2292
|
case "record":
|
|
2252
2293
|
case "object":
|
|
@@ -2791,13 +2832,16 @@ class LlEmitter {
|
|
|
2791
2832
|
}
|
|
2792
2833
|
case "bytesSet": {
|
|
2793
2834
|
// 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
|
-
|
|
2835
|
+
// the value is a scalar (the kind-specific inline path coerces
|
|
2836
|
+
// JS-exactly), so no ownership moves. Any invalid index traps — no
|
|
2837
|
+
// append. IrBytesElem is static, so never rediscover it through the
|
|
2838
|
+
// generic runtime switch in a hot loop.
|
|
2839
|
+
const arr = this.emitBytesReceiver(s.arr, [s.index, s.value]);
|
|
2797
2840
|
const idx = this.emitExpr(s.index);
|
|
2798
2841
|
const v = this.emitExpr(s.value);
|
|
2799
|
-
|
|
2800
|
-
|
|
2842
|
+
if (s.arr.type.kind !== "bytes")
|
|
2843
|
+
throw new Error("llvm emitter bug: bytesSet on non-bytes");
|
|
2844
|
+
this.emitBytesSet(s.arr.type.elem, arr.name, idx.name, v.name);
|
|
2801
2845
|
break;
|
|
2802
2846
|
}
|
|
2803
2847
|
case "fieldSet":
|
|
@@ -5280,7 +5324,7 @@ class LlEmitter {
|
|
|
5280
5324
|
throw new Error("llvm emitter bug: yieldExpr with no operand (frontend fills undefined)");
|
|
5281
5325
|
const v = this.emitExpr(e.value);
|
|
5282
5326
|
const yt = e.value.type;
|
|
5283
|
-
if (yt.kind === "f64") {
|
|
5327
|
+
if (yt.kind === "f64" || yt.kind === "date") {
|
|
5284
5328
|
this.declare(`declare void @scr_gen_yield_f64(double)`);
|
|
5285
5329
|
B.line(`call void @scr_gen_yield_f64(double ${v.name})`);
|
|
5286
5330
|
}
|
|
@@ -5300,7 +5344,7 @@ class LlEmitter {
|
|
|
5300
5344
|
return { name: "", type: e.type };
|
|
5301
5345
|
}
|
|
5302
5346
|
const t = B.tmp();
|
|
5303
|
-
if (e.type.kind === "f64") {
|
|
5347
|
+
if (e.type.kind === "f64" || e.type.kind === "date") {
|
|
5304
5348
|
this.declare(`declare double @scr_gen_take_in_f64()`);
|
|
5305
5349
|
B.line(`${t} = call double @scr_gen_take_in_f64()`);
|
|
5306
5350
|
return { name: t, type: e.type };
|
|
@@ -5332,7 +5376,7 @@ class LlEmitter {
|
|
|
5332
5376
|
store(a.name, e.arg.type);
|
|
5333
5377
|
};
|
|
5334
5378
|
const parkIn = (name, t) => {
|
|
5335
|
-
if (t.kind === "f64") {
|
|
5379
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5336
5380
|
this.declare(`declare void @scr_gen_in_f64(ptr, double)`);
|
|
5337
5381
|
B.line(`call void @scr_gen_in_f64(ptr ${g.name}, double ${name})`);
|
|
5338
5382
|
}
|
|
@@ -5378,7 +5422,7 @@ class LlEmitter {
|
|
|
5378
5422
|
}
|
|
5379
5423
|
else {
|
|
5380
5424
|
sendArg((name, t) => {
|
|
5381
|
-
if (t.kind === "f64") {
|
|
5425
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5382
5426
|
this.declare(`declare void @scr_gen_ret_f64(ptr, double)`);
|
|
5383
5427
|
B.line(`call void @scr_gen_ret_f64(ptr ${g.name}, double ${name})`);
|
|
5384
5428
|
}
|
|
@@ -5431,7 +5475,7 @@ class LlEmitter {
|
|
|
5431
5475
|
return { name: "", type: e.type };
|
|
5432
5476
|
}
|
|
5433
5477
|
const t = B.tmp();
|
|
5434
|
-
if (e.type.kind === "f64") {
|
|
5478
|
+
if (e.type.kind === "f64" || e.type.kind === "date") {
|
|
5435
5479
|
this.declare(`declare double @scr_await_f64(ptr)`);
|
|
5436
5480
|
B.line(`${t} = call double @scr_await_f64(ptr ${pr.name})`);
|
|
5437
5481
|
}
|
|
@@ -5511,7 +5555,7 @@ class LlEmitter {
|
|
|
5511
5555
|
B.startBlock(lp);
|
|
5512
5556
|
const peek = this.unionPeek(u.name);
|
|
5513
5557
|
let awaited;
|
|
5514
|
-
if (inner.kind === "f64") {
|
|
5558
|
+
if (inner.kind === "f64" || inner.kind === "date") {
|
|
5515
5559
|
this.declare(`declare double @scr_await_f64(ptr)`);
|
|
5516
5560
|
const x = B.tmp();
|
|
5517
5561
|
B.line(`${x} = call double @scr_await_f64(ptr ${peek})`);
|
|
@@ -5588,7 +5632,7 @@ class LlEmitter {
|
|
|
5588
5632
|
return out;
|
|
5589
5633
|
}
|
|
5590
5634
|
let resolve;
|
|
5591
|
-
const kindNums = { f64: 0, bool: 1, string: 2, void: 3 };
|
|
5635
|
+
const kindNums = { f64: 0, date: 0, bool: 1, string: 2, void: 3 };
|
|
5592
5636
|
const kindNum = kindNums[inner.kind];
|
|
5593
5637
|
if (kindNum !== undefined) {
|
|
5594
5638
|
this.declare(`declare ptr @scr_make_resolve(ptr, i32)`);
|
|
@@ -5634,7 +5678,7 @@ class LlEmitter {
|
|
|
5634
5678
|
this.declare(`declare ptr @scr_make_reject(ptr)`);
|
|
5635
5679
|
const p = B.tmp();
|
|
5636
5680
|
B.line(`${p} = call ptr @scr_promise_new()`);
|
|
5637
|
-
const kindNums = { f64: 0, bool: 1, string: 2, void: 3 };
|
|
5681
|
+
const kindNums = { f64: 0, date: 0, bool: 1, string: 2, void: 3 };
|
|
5638
5682
|
const kindNum = kindNums[inner.kind];
|
|
5639
5683
|
const resolve = B.tmp();
|
|
5640
5684
|
if (kindNum !== undefined) {
|
|
@@ -5749,7 +5793,7 @@ class LlEmitter {
|
|
|
5749
5793
|
}
|
|
5750
5794
|
const v = this.emitExpr(e.args[0]);
|
|
5751
5795
|
const t = e.args[0].type;
|
|
5752
|
-
if (t.kind === "f64") {
|
|
5796
|
+
if (t.kind === "f64" || t.kind === "date") {
|
|
5753
5797
|
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
5754
5798
|
B.line(`call void @scr_promise_fulfill_f64(ptr ${p}, double ${v.name})`);
|
|
5755
5799
|
}
|
|
@@ -8625,6 +8669,233 @@ class LlEmitter {
|
|
|
8625
8669
|
}
|
|
8626
8670
|
}
|
|
8627
8671
|
}
|
|
8672
|
+
/** Whether an index/value expression can overwrite the bytes receiver
|
|
8673
|
+
* binding. Kept deliberately conservative; uncertain or calling shapes
|
|
8674
|
+
* retain the ordinary owned-receiver snapshot. */
|
|
8675
|
+
isStableBytesOperand(e, receiverLocalId) {
|
|
8676
|
+
switch (e.kind) {
|
|
8677
|
+
case "numLit":
|
|
8678
|
+
case "boolLit":
|
|
8679
|
+
case "varRef":
|
|
8680
|
+
case "incDec":
|
|
8681
|
+
return true;
|
|
8682
|
+
case "assignExpr":
|
|
8683
|
+
// A bytes assignment nested under truthiness/ternary can still
|
|
8684
|
+
// produce the numeric index/value expected by the outer access.
|
|
8685
|
+
return (e.localId !== receiverLocalId &&
|
|
8686
|
+
this.isStableBytesOperand(e.value, receiverLocalId));
|
|
8687
|
+
case "bin":
|
|
8688
|
+
return (this.isStableBytesOperand(e.left, receiverLocalId) &&
|
|
8689
|
+
this.isStableBytesOperand(e.right, receiverLocalId));
|
|
8690
|
+
case "unary":
|
|
8691
|
+
case "toBool":
|
|
8692
|
+
return this.isStableBytesOperand(e.operand, receiverLocalId);
|
|
8693
|
+
case "logical":
|
|
8694
|
+
return (this.isStableBytesOperand(e.left, receiverLocalId) &&
|
|
8695
|
+
this.isStableBytesOperand(e.right, receiverLocalId));
|
|
8696
|
+
case "ternary":
|
|
8697
|
+
return (this.isStableBytesOperand(e.cond, receiverLocalId) &&
|
|
8698
|
+
this.isStableBytesOperand(e.then, receiverLocalId) &&
|
|
8699
|
+
this.isStableBytesOperand(e.else_, receiverLocalId));
|
|
8700
|
+
case "bytesIntrinsic":
|
|
8701
|
+
return ((e.method === "get" || e.method === "length" || e.method === "byteLength") &&
|
|
8702
|
+
e.receiver.kind === "varRef" &&
|
|
8703
|
+
e.args.every((arg) => this.isStableBytesOperand(arg, receiverLocalId)));
|
|
8704
|
+
default:
|
|
8705
|
+
return false;
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
/** Borrow a direct, unboxed bytes binding when later operands are stable.
|
|
8709
|
+
* Its scope/global owner keeps it alive through the access. */
|
|
8710
|
+
emitBytesReceiver(receiver, following) {
|
|
8711
|
+
if (receiver.kind === "varRef" &&
|
|
8712
|
+
following.every((operand) => this.isStableBytesOperand(operand, receiver.localId))) {
|
|
8713
|
+
const b = this.binding(receiver.localId);
|
|
8714
|
+
if (b.kind !== "boxed") {
|
|
8715
|
+
const value = this.B.tmp();
|
|
8716
|
+
this.B.line(`${value} = load ptr, ptr ${b.slot}`);
|
|
8717
|
+
return { name: value, type: receiver.type };
|
|
8718
|
+
}
|
|
8719
|
+
}
|
|
8720
|
+
return this.emitExpr(receiver);
|
|
8721
|
+
}
|
|
8722
|
+
/** Bounds-check a typed-array element index without an out-of-line call
|
|
8723
|
+
* on the valid path. The range test rejects NaN/infinity before fptoui;
|
|
8724
|
+
* the integer round trip then rejects fractions. The error block retains
|
|
8725
|
+
* the runtime's exact trap text. Leaves the builder in the valid block. */
|
|
8726
|
+
emitBytesIndex(receiver, index) {
|
|
8727
|
+
const B = this.B;
|
|
8728
|
+
const lenPtr = B.tmp();
|
|
8729
|
+
const len = B.tmp();
|
|
8730
|
+
const lenF64 = B.tmp();
|
|
8731
|
+
const nonnegative = B.tmp();
|
|
8732
|
+
const belowLen = B.tmp();
|
|
8733
|
+
const inRange = B.tmp();
|
|
8734
|
+
B.line(`${lenPtr} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
|
|
8735
|
+
B.line(`${len} = load i64, ptr ${lenPtr}`);
|
|
8736
|
+
B.line(`${lenF64} = uitofp i64 ${len} to double`);
|
|
8737
|
+
B.line(`${nonnegative} = fcmp oge double ${index}, ${f64Lit(0)}`);
|
|
8738
|
+
B.line(`${belowLen} = fcmp olt double ${index}, ${lenF64}`);
|
|
8739
|
+
B.line(`${inRange} = and i1 ${nonnegative}, ${belowLen}`);
|
|
8740
|
+
const rangeOk = B.newLabel("bytes.index.range");
|
|
8741
|
+
const invalid = B.newLabel("bytes.index.invalid");
|
|
8742
|
+
const valid = B.newLabel("bytes.index.valid");
|
|
8743
|
+
B.condBr(inRange, rangeOk, invalid);
|
|
8744
|
+
B.startBlock(rangeOk);
|
|
8745
|
+
const idx = B.tmp();
|
|
8746
|
+
const roundTrip = B.tmp();
|
|
8747
|
+
const integral = B.tmp();
|
|
8748
|
+
B.line(`${idx} = fptoui double ${index} to i64`);
|
|
8749
|
+
B.line(`${roundTrip} = uitofp i64 ${idx} to double`);
|
|
8750
|
+
B.line(`${integral} = fcmp oeq double ${roundTrip}, ${index}`);
|
|
8751
|
+
B.condBr(integral, valid, invalid);
|
|
8752
|
+
B.startBlock(invalid);
|
|
8753
|
+
this.declare(`declare double @scr_bytes_get(ptr, double)`);
|
|
8754
|
+
B.line(`call double @scr_bytes_get(ptr ${receiver}, double ${index})`);
|
|
8755
|
+
B.terminate("unreachable");
|
|
8756
|
+
B.startBlock(valid);
|
|
8757
|
+
return idx;
|
|
8758
|
+
}
|
|
8759
|
+
/** Load the raw data pointer from ScrBytes after the bounds check. */
|
|
8760
|
+
emitBytesData(receiver) {
|
|
8761
|
+
const p = this.B.tmp();
|
|
8762
|
+
const data = this.B.tmp();
|
|
8763
|
+
this.B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 3`);
|
|
8764
|
+
this.B.line(`${data} = load ptr, ptr ${p}`);
|
|
8765
|
+
return data;
|
|
8766
|
+
}
|
|
8767
|
+
/** Static typed-array length/byteLength load. */
|
|
8768
|
+
emitBytesLength(elem, receiver, bytes) {
|
|
8769
|
+
const B = this.B;
|
|
8770
|
+
const p = B.tmp();
|
|
8771
|
+
const len = B.tmp();
|
|
8772
|
+
B.line(`${p} = getelementptr inbounds %ScrBytes, ptr ${receiver}, i64 0, i32 1`);
|
|
8773
|
+
B.line(`${len} = load i64, ptr ${p}`);
|
|
8774
|
+
const count = bytes && elem !== "u8" ? B.tmp() : len;
|
|
8775
|
+
if (count !== len)
|
|
8776
|
+
B.line(`${count} = shl i64 ${len}, 2`);
|
|
8777
|
+
const out = B.tmp();
|
|
8778
|
+
B.line(`${out} = uitofp i64 ${count} to double`);
|
|
8779
|
+
return { name: out, type: F64 };
|
|
8780
|
+
}
|
|
8781
|
+
/** Kind-specialized typed-array load. */
|
|
8782
|
+
emitBytesGet(elem, receiver, index) {
|
|
8783
|
+
const B = this.B;
|
|
8784
|
+
const idx = this.emitBytesIndex(receiver, index);
|
|
8785
|
+
const data = this.emitBytesData(receiver);
|
|
8786
|
+
const p = B.tmp();
|
|
8787
|
+
if (elem === "u8") {
|
|
8788
|
+
const raw = B.tmp();
|
|
8789
|
+
const wide = B.tmp();
|
|
8790
|
+
const out = B.tmp();
|
|
8791
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
|
|
8792
|
+
B.line(`${raw} = load i8, ptr ${p}, align 1`);
|
|
8793
|
+
B.line(`${wide} = zext i8 ${raw} to i32`);
|
|
8794
|
+
B.line(`${out} = uitofp i32 ${wide} to double`);
|
|
8795
|
+
return { name: out, type: F64 };
|
|
8796
|
+
}
|
|
8797
|
+
if (elem === "f32") {
|
|
8798
|
+
const raw = B.tmp();
|
|
8799
|
+
const out = B.tmp();
|
|
8800
|
+
B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
|
|
8801
|
+
B.line(`${raw} = load float, ptr ${p}, align 1`);
|
|
8802
|
+
B.line(`${out} = fpext float ${raw} to double`);
|
|
8803
|
+
return { name: out, type: F64 };
|
|
8804
|
+
}
|
|
8805
|
+
const raw = B.tmp();
|
|
8806
|
+
const out = B.tmp();
|
|
8807
|
+
B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
|
|
8808
|
+
B.line(`${raw} = load i32, ptr ${p}, align 1`);
|
|
8809
|
+
B.line(`${out} = ${elem === "i32" ? "sitofp" : "uitofp"} i32 ${raw} to double`);
|
|
8810
|
+
return { name: out, type: F64 };
|
|
8811
|
+
}
|
|
8812
|
+
/** ToUint32/ToInt32 coercion with the overwhelmingly common finite,
|
|
8813
|
+
* exactly-representable JS-number range on a cast-only path. Exceptional
|
|
8814
|
+
* and huge values remain inline in the program TU so unrelated runtime
|
|
8815
|
+
* binaries do not acquire another exported helper. */
|
|
8816
|
+
emitBytesU32(value) {
|
|
8817
|
+
const B = this.B;
|
|
8818
|
+
const aboveMin = B.tmp();
|
|
8819
|
+
const belowMax = B.tmp();
|
|
8820
|
+
const fast = B.tmp();
|
|
8821
|
+
B.line(`${aboveMin} = fcmp oge double ${value}, ${f64Lit(-9007199254740992)}`);
|
|
8822
|
+
B.line(`${belowMax} = fcmp ole double ${value}, ${f64Lit(9007199254740992)}`);
|
|
8823
|
+
B.line(`${fast} = and i1 ${aboveMin}, ${belowMax}`);
|
|
8824
|
+
const fastLabel = B.newLabel("bytes.coerce.fast");
|
|
8825
|
+
const slowLabel = B.newLabel("bytes.coerce.slow");
|
|
8826
|
+
const done = B.newLabel("bytes.coerce.done");
|
|
8827
|
+
B.condBr(fast, fastLabel, slowLabel);
|
|
8828
|
+
B.startBlock(fastLabel);
|
|
8829
|
+
const signed = B.tmp();
|
|
8830
|
+
const fastU32 = B.tmp();
|
|
8831
|
+
B.line(`${signed} = fptosi double ${value} to i64`);
|
|
8832
|
+
B.line(`${fastU32} = trunc i64 ${signed} to i32`);
|
|
8833
|
+
B.br(done);
|
|
8834
|
+
B.startBlock(slowLabel);
|
|
8835
|
+
const ordered = B.tmp();
|
|
8836
|
+
const belowInf = B.tmp();
|
|
8837
|
+
const aboveNegInf = B.tmp();
|
|
8838
|
+
const finiteRange = B.tmp();
|
|
8839
|
+
const finite = B.tmp();
|
|
8840
|
+
B.line(`${ordered} = fcmp ord double ${value}, ${value}`);
|
|
8841
|
+
B.line(`${belowInf} = fcmp olt double ${value}, ${F64_INF}`);
|
|
8842
|
+
B.line(`${aboveNegInf} = fcmp ogt double ${value}, ${f64Lit(-Infinity)}`);
|
|
8843
|
+
B.line(`${finiteRange} = and i1 ${belowInf}, ${aboveNegInf}`);
|
|
8844
|
+
B.line(`${finite} = and i1 ${ordered}, ${finiteRange}`);
|
|
8845
|
+
const finiteLabel = B.newLabel("bytes.coerce.finite");
|
|
8846
|
+
const nonfiniteLabel = B.newLabel("bytes.coerce.nonfinite");
|
|
8847
|
+
const slowDone = B.newLabel("bytes.coerce.slow.done");
|
|
8848
|
+
B.condBr(finite, finiteLabel, nonfiniteLabel);
|
|
8849
|
+
B.startBlock(finiteLabel);
|
|
8850
|
+
this.declare(`declare double @llvm.trunc.f64(double)`);
|
|
8851
|
+
const truncated = B.tmp();
|
|
8852
|
+
const residue = B.tmp();
|
|
8853
|
+
const negative = B.tmp();
|
|
8854
|
+
const wrapped = B.tmp();
|
|
8855
|
+
const normalized = B.tmp();
|
|
8856
|
+
const finiteU32 = B.tmp();
|
|
8857
|
+
B.line(`${truncated} = call double @llvm.trunc.f64(double ${value})`);
|
|
8858
|
+
B.line(`${residue} = frem double ${truncated}, ${f64Lit(4294967296)}`);
|
|
8859
|
+
B.line(`${negative} = fcmp olt double ${residue}, ${f64Lit(0)}`);
|
|
8860
|
+
B.line(`${wrapped} = fadd double ${residue}, ${f64Lit(4294967296)}`);
|
|
8861
|
+
B.line(`${normalized} = select i1 ${negative}, double ${wrapped}, double ${residue}`);
|
|
8862
|
+
B.line(`${finiteU32} = fptoui double ${normalized} to i32`);
|
|
8863
|
+
B.br(slowDone);
|
|
8864
|
+
B.startBlock(nonfiniteLabel);
|
|
8865
|
+
B.br(slowDone);
|
|
8866
|
+
B.startBlock(slowDone);
|
|
8867
|
+
const slowU32 = B.tmp();
|
|
8868
|
+
B.line(`${slowU32} = phi i32 [ ${finiteU32}, %${finiteLabel} ], [ 0, %${nonfiniteLabel} ]`);
|
|
8869
|
+
B.br(done);
|
|
8870
|
+
B.startBlock(done);
|
|
8871
|
+
const out = B.tmp();
|
|
8872
|
+
B.line(`${out} = phi i32 [ ${fastU32}, %${fastLabel} ], [ ${slowU32}, %${slowDone} ]`);
|
|
8873
|
+
return out;
|
|
8874
|
+
}
|
|
8875
|
+
/** Kind-specialized typed-array store. */
|
|
8876
|
+
emitBytesSet(elem, receiver, index, value) {
|
|
8877
|
+
const B = this.B;
|
|
8878
|
+
const idx = this.emitBytesIndex(receiver, index);
|
|
8879
|
+
const stored = elem === "f32" ? null : this.emitBytesU32(value);
|
|
8880
|
+
const data = this.emitBytesData(receiver);
|
|
8881
|
+
const p = B.tmp();
|
|
8882
|
+
if (elem === "u8") {
|
|
8883
|
+
const byte = B.tmp();
|
|
8884
|
+
B.line(`${byte} = trunc i32 ${stored} to i8`);
|
|
8885
|
+
B.line(`${p} = getelementptr inbounds i8, ptr ${data}, i64 ${idx}`);
|
|
8886
|
+
B.line(`store i8 ${byte}, ptr ${p}, align 1`);
|
|
8887
|
+
return;
|
|
8888
|
+
}
|
|
8889
|
+
if (elem === "f32") {
|
|
8890
|
+
const narrowed = B.tmp();
|
|
8891
|
+
B.line(`${narrowed} = fptrunc double ${value} to float`);
|
|
8892
|
+
B.line(`${p} = getelementptr inbounds float, ptr ${data}, i64 ${idx}`);
|
|
8893
|
+
B.line(`store float ${narrowed}, ptr ${p}, align 1`);
|
|
8894
|
+
return;
|
|
8895
|
+
}
|
|
8896
|
+
B.line(`${p} = getelementptr inbounds i32, ptr ${data}, i64 ${idx}`);
|
|
8897
|
+
B.line(`store i32 ${stored}, ptr ${p}, align 1`);
|
|
8898
|
+
}
|
|
8628
8899
|
/** The bytesIntrinsic surface (emit-exprs.ts's switch, .ll flavored).
|
|
8629
8900
|
* Receiver and args are borrowed frame temps; string/bytes results come
|
|
8630
8901
|
* back +1. The MAY_THROW methods get the standard pending check after
|
|
@@ -8672,18 +8943,30 @@ class LlEmitter {
|
|
|
8672
8943
|
? call("scr_bytes_read_var", "double (ptr, double, double, i1 zeroext, i1 zeroext)", `ptr ${r0.name}, double ${rest[0].name}, double ${rest[1].name}, i1 ${spec.sign}, i1 ${spec.le}`, false, true)
|
|
8673
8944
|
: call("scr_bytes_write_var", "double (ptr, double, double, double, i1 zeroext, i1 zeroext)", `ptr ${r0.name}, double ${rest[0].name}, double ${rest[1].name}, double ${rest[2].name}, i1 ${spec.sign}, i1 ${spec.le}`, false, true);
|
|
8674
8945
|
}
|
|
8675
|
-
const r = this.emitExpr(e.receiver);
|
|
8676
|
-
const args = e.args.map((a) => this.emitExpr(a));
|
|
8677
8946
|
const method = e.method;
|
|
8947
|
+
const directElementAccess = method === "length" || method === "byteLength" || method === "get";
|
|
8948
|
+
const r = directElementAccess
|
|
8949
|
+
? this.emitBytesReceiver(e.receiver, e.args)
|
|
8950
|
+
: this.emitExpr(e.receiver);
|
|
8951
|
+
const args = e.args.map((a) => this.emitExpr(a));
|
|
8678
8952
|
const NAN = f64Lit(NaN);
|
|
8679
8953
|
switch (method) {
|
|
8680
8954
|
case "length":
|
|
8681
|
-
|
|
8955
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
8956
|
+
throw new Error("llvm emitter bug: bytesIntrinsic length on non-bytes");
|
|
8957
|
+
}
|
|
8958
|
+
return this.emitBytesLength(e.receiver.type.elem, r.name, false);
|
|
8682
8959
|
case "byteLength":
|
|
8683
|
-
|
|
8960
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
8961
|
+
throw new Error("llvm emitter bug: bytesIntrinsic byteLength on non-bytes");
|
|
8962
|
+
}
|
|
8963
|
+
return this.emitBytesLength(e.receiver.type.elem, r.name, true);
|
|
8684
8964
|
case "get":
|
|
8685
8965
|
// Any invalid index traps (the array runtime's discipline).
|
|
8686
|
-
|
|
8966
|
+
if (e.receiver.type.kind !== "bytes") {
|
|
8967
|
+
throw new Error("llvm emitter bug: bytesIntrinsic get on non-bytes");
|
|
8968
|
+
}
|
|
8969
|
+
return this.emitBytesGet(e.receiver.type.elem, r.name, args[0].name);
|
|
8687
8970
|
case "slice":
|
|
8688
8971
|
return call("scr_bytes_slice", "ptr (ptr, double, double)", `ptr ${r.name}, double ${args[0]?.name ?? f64Lit(0)}, double ${args[1]?.name ?? F64_INF}`, true, false);
|
|
8689
8972
|
case "subarray":
|