@scriptc/compiler 0.2.3 → 0.2.4
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/dist/backend/llvm/borrowed-inputs.d.ts +15 -0
- package/dist/backend/llvm/borrowed-inputs.js +28 -0
- package/dist/backend/llvm/borrowed-inputs.js.map +1 -0
- package/dist/backend/llvm/byte-numbers.d.ts +10 -0
- package/dist/backend/llvm/byte-numbers.js +235 -0
- package/dist/backend/llvm/byte-numbers.js.map +1 -0
- package/dist/backend/llvm/bytes-bounds.d.ts +9 -0
- package/dist/backend/llvm/bytes-bounds.js +189 -0
- package/dist/backend/llvm/bytes-bounds.js.map +1 -0
- package/dist/backend/llvm/call-lifetimes.d.ts +7 -0
- package/dist/backend/llvm/call-lifetimes.js +27 -7
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +54 -0
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/checked-value-lifetimes.d.ts +9 -0
- package/dist/backend/llvm/checked-value-lifetimes.js +35 -0
- package/dist/backend/llvm/checked-value-lifetimes.js.map +1 -0
- package/dist/backend/llvm/counted-loops.d.ts +6 -0
- package/dist/backend/llvm/counted-loops.js +28 -0
- package/dist/backend/llvm/counted-loops.js.map +1 -0
- package/dist/backend/llvm/emitter.d.ts +25 -9
- package/dist/backend/llvm/emitter.js +131 -31
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +4 -4
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +3 -2
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.d.ts +4 -4
- package/dist/backend/llvm/expr-bytes.js +68 -35
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js +21 -13
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.d.ts +2 -1
- package/dist/backend/llvm/expr-containers.js +68 -46
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +11 -0
- package/dist/backend/llvm/expr-control.js +26 -5
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +32 -30
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +55 -22
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +8 -2
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/integer-values.d.ts +10 -0
- package/dist/backend/llvm/integer-values.js +41 -0
- package/dist/backend/llvm/integer-values.js.map +1 -0
- package/dist/backend/llvm/integer-views.d.ts +7 -0
- package/dist/backend/llvm/integer-views.js +67 -0
- package/dist/backend/llvm/integer-views.js.map +1 -0
- package/dist/backend/llvm/integer-views.test.d.ts +1 -0
- package/dist/backend/llvm/integer-views.test.js +34 -0
- package/dist/backend/llvm/integer-views.test.js.map +1 -0
- package/dist/backend/llvm/lib-dispatch.js +3 -1
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +5 -5
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +2 -4
- package/dist/backend/llvm/local-array-reads.js +4 -91
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +11 -11
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.js +2 -2
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +5 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/reference-effects.d.ts +22 -0
- package/dist/backend/llvm/reference-effects.js +148 -0
- package/dist/backend/llvm/reference-effects.js.map +1 -0
- package/dist/backend/llvm/reference-effects.test.d.ts +1 -0
- package/dist/backend/llvm/reference-effects.test.js +74 -0
- package/dist/backend/llvm/reference-effects.test.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +7 -4
- package/dist/backend/llvm/shapes.js +23 -6
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/stack-unions.test.js +23 -2
- package/dist/backend/llvm/stack-unions.test.js.map +1 -1
- package/dist/backend/llvm/string-construction.d.ts +10 -0
- package/dist/backend/llvm/string-construction.js +38 -0
- package/dist/backend/llvm/string-construction.js.map +1 -0
- package/dist/backend/llvm/string-construction.test.d.ts +1 -0
- package/dist/backend/llvm/string-construction.test.js +65 -0
- package/dist/backend/llvm/string-construction.test.js.map +1 -0
- package/dist/backend/llvm/string-lifetimes.d.ts +2 -2
- package/dist/backend/llvm/string-lifetimes.js +5 -3
- package/dist/backend/llvm/string-lifetimes.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.test.js +19 -10
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/frontend/lowering/collection-ingestion.js +9 -0
- package/dist/frontend/lowering/collection-ingestion.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +6 -0
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.js +2 -0
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +3 -3
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.js +12 -34
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +12 -2
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lowerer.js +10 -3
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/type-mapper.js +2 -2
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/ir/analysis.js +3 -1
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/byte-numbers.d.ts +20 -0
- package/dist/ir/byte-numbers.js +65 -0
- package/dist/ir/byte-numbers.js.map +1 -0
- package/dist/ir/byte-numbers.test.d.ts +1 -0
- package/dist/ir/byte-numbers.test.js +42 -0
- package/dist/ir/byte-numbers.test.js.map +1 -0
- package/dist/ir/integer-loops.d.ts +19 -0
- package/dist/ir/integer-loops.js +107 -1
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-loops.test.js +51 -1
- package/dist/ir/integer-loops.test.js.map +1 -1
- package/dist/ir/integer-ranges.d.ts +8 -6
- package/dist/ir/integer-ranges.js +343 -89
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +54 -3
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/ir.d.ts +8 -5
- package/dist/ir/ir.js +16 -5
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/validate.js +47 -29
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +20 -0
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/native/toolchain.js +6 -3
- package/dist/native/toolchain.js.map +1 -1
- package/dist/native/toolchain.test.js +43 -5
- package/dist/native/toolchain.test.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +1 -1
|
@@ -22,10 +22,13 @@ import { deflateRawSync } from "node:zlib";
|
|
|
22
22
|
import { endsWithJump, matchStringSelfConcat, streamTypedRefEligible } from "../../ir/analysis.js";
|
|
23
23
|
import { emitLibraryIdentityLines } from "../library-identity-markers.js";
|
|
24
24
|
import { CAUGHT, ffiCallbackType, isDynTypedRefType, isFfiContextParam, isRefCounted, isUnitType, moduleRuntimeFeatures, moduleEmbedsBuiltin, moduleEmbedsCompressedNpm, NPM_COMPRESS_MIN, POINTER_KINDS, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, typeKey, VOID } from "../../ir/ir.js";
|
|
25
|
-
import { matchIntegerArrayForLoop, matchIntegerBytesForLoop } from "../../ir/integer-loops.js";
|
|
25
|
+
import { matchIntegerArrayForLoop, matchIntegerBytesForLoop, matchIntegerCountedForLoop } from "../../ir/integer-loops.js";
|
|
26
|
+
import { emitCountedLoopLimit } from "./counted-loops.js";
|
|
27
|
+
import { findBytesBounds } from "./bytes-bounds.js";
|
|
26
28
|
import { scalarizeNumericRecords } from "../../ir/scalar-records.js";
|
|
27
29
|
import { everyStmtList } from "../../ir/traverse.js";
|
|
28
30
|
import { analyzeIntegerRanges } from "../../ir/integer-ranges.js";
|
|
31
|
+
import { findIntegerViews } from "./integer-views.js";
|
|
29
32
|
import { findConstantNumericTables } from "../../ir/constant-tables.js";
|
|
30
33
|
import { allocateFfiCallbackAdapters, hasForeignFfiCallback, hasRetainedFfiCallback } from "../ffi-callbacks.js";
|
|
31
34
|
import { RUNTIME_ABI_MARKER } from "../runtime-abi.js";
|
|
@@ -34,9 +37,11 @@ import { mangleArgPack, mangleAsyncSpawn, mangleBorrowedFunction, mangleClassObj
|
|
|
34
37
|
import { analyzeCallLifetimes } from "./call-lifetimes.js";
|
|
35
38
|
import { emitStackUnion, findLocalStackUnions } from "./stack-unions.js";
|
|
36
39
|
import { BlockBuilder } from "./blocks.js";
|
|
37
|
-
import { emitLocalArrayRead,
|
|
40
|
+
import { emitLocalArrayRead, findLocalArrayReads, findCallArrayReads, OptionalArrayReads } from "./local-array-reads.js";
|
|
38
41
|
import { emitStackMapRead, findMapReadLifetimes } from "./map-read-lifetimes.js";
|
|
39
42
|
import { emitBorrowedFieldSequence } from "./borrowed-receivers.js";
|
|
43
|
+
import { emitBorrowedInput } from "./borrowed-inputs.js";
|
|
44
|
+
import { ReferenceEffects } from "./reference-effects.js";
|
|
40
45
|
import { LlvmDebugInfo } from "./debug-info.js";
|
|
41
46
|
import { f64Lit, ffiNativeTypeLl, ffiNativeParamLl, ffiNativeReturnLl, llvmCommentText } from "./common.js";
|
|
42
47
|
import { ffiExtendsNarrowIntegers } from "../targets.js";
|
|
@@ -156,6 +161,7 @@ export class LlEmitter {
|
|
|
156
161
|
* immortal (rc == SIZE_MAX) static per (union, unit tag).
|
|
157
162
|
* RC entry points and the collector skip immortals. */
|
|
158
163
|
unitInstances = new Map();
|
|
164
|
+
immortalValues = new Set();
|
|
159
165
|
/** Regex literal templates: "<flags>/<pattern>" → { symbol, interned
|
|
160
166
|
* source/flags literal refs } — one immortal ScrRegex template per distinct
|
|
161
167
|
* (pattern, flags) pair; the bytecode slot starts null and the runtime
|
|
@@ -189,12 +195,13 @@ export class LlEmitter {
|
|
|
189
195
|
needsBadKey = false;
|
|
190
196
|
needsRetainBox = false;
|
|
191
197
|
fnByName = new Map();
|
|
192
|
-
|
|
198
|
+
referenceEffects;
|
|
193
199
|
optionalArrayReads;
|
|
194
200
|
callArrayReads = new Map();
|
|
195
201
|
mapReadLifetimes = { locals: new Map(), arguments: new Map() };
|
|
196
202
|
callLifetimes;
|
|
197
203
|
borrowedParameters = new Set();
|
|
204
|
+
stableCallBindings = new Set();
|
|
198
205
|
/** Manifest-bound native imports, used by ffiCall emission. */
|
|
199
206
|
ffiByName = new Map();
|
|
200
207
|
/** C-ABI callback trampolines and (for raw/no-userdata callbacks) their
|
|
@@ -276,9 +283,12 @@ export class LlEmitter {
|
|
|
276
283
|
jumpTargets = [];
|
|
277
284
|
currentLocals = new Map();
|
|
278
285
|
captureIds = new Set();
|
|
279
|
-
/** Active
|
|
286
|
+
/** Active induction bindings retain their width independently of size_t. */
|
|
280
287
|
integerLoopBindings = new Map();
|
|
288
|
+
countedLoopsEnabled = false;
|
|
281
289
|
integerRanges = new Map();
|
|
290
|
+
bytesBounds = new Set();
|
|
291
|
+
integerViews = new Map();
|
|
282
292
|
/** Enclosing try-with-FINALLY regions, innermost last: a `return`
|
|
283
293
|
* inside one runs every crossed finally (innermost first) before the
|
|
284
294
|
* actual ret — the runtime ABI’s pending-return path, with the finally
|
|
@@ -345,7 +355,7 @@ export class LlEmitter {
|
|
|
345
355
|
for (const u of mod.unions ?? [])
|
|
346
356
|
this.unionsById.set(u.id, u);
|
|
347
357
|
this.optionalArrayReads = new OptionalArrayReads(this.fnByName, this.unionsById);
|
|
348
|
-
this.
|
|
358
|
+
this.referenceEffects = new ReferenceEffects(this.fnByName, (call) => this.optionalArrayReads.get(call) !== null);
|
|
349
359
|
this.callLifetimes = analyzeCallLifetimes(this.fnByName);
|
|
350
360
|
for (const r of mod.records ?? [])
|
|
351
361
|
this.recordsById.set(r.id, r);
|
|
@@ -1995,7 +2005,9 @@ export class LlEmitter {
|
|
|
1995
2005
|
lit = { sym: `sc_lit_${this.literals.size}`, len: Buffer.byteLength(text, "utf8") };
|
|
1996
2006
|
this.literals.set(text, lit);
|
|
1997
2007
|
}
|
|
1998
|
-
|
|
2008
|
+
const name = `@${lit.sym}`;
|
|
2009
|
+
this.immortalValues.add(name);
|
|
2010
|
+
return name;
|
|
1999
2011
|
}
|
|
2000
2012
|
/** Interned NUL-terminated C-string constant (the scr_jb_puts /
|
|
2001
2013
|
* stringify-indent currency) — `@`-ref, first-use order. */
|
|
@@ -2025,7 +2037,9 @@ export class LlEmitter {
|
|
|
2025
2037
|
sym = `sc_unit_${this.unitInstances.size}`;
|
|
2026
2038
|
this.unitInstances.set(key, sym);
|
|
2027
2039
|
}
|
|
2028
|
-
|
|
2040
|
+
const name = `@${sym}`;
|
|
2041
|
+
this.immortalValues.add(name);
|
|
2042
|
+
return name;
|
|
2029
2043
|
}
|
|
2030
2044
|
declare(decl) {
|
|
2031
2045
|
this.decls.add(decl);
|
|
@@ -2041,7 +2055,7 @@ export class LlEmitter {
|
|
|
2041
2055
|
}
|
|
2042
2056
|
/** Registers an owned refcounted value on the current statement frame. */
|
|
2043
2057
|
own(v) {
|
|
2044
|
-
if (isRefCounted(v.type))
|
|
2058
|
+
if (isRefCounted(v.type) && !this.immortalValues.has(v.name))
|
|
2045
2059
|
this.currentFrame().push(v);
|
|
2046
2060
|
return v;
|
|
2047
2061
|
}
|
|
@@ -2053,7 +2067,7 @@ export class LlEmitter {
|
|
|
2053
2067
|
}
|
|
2054
2068
|
/** Strike a refcounted temp from its frame: ownership is being moved. */
|
|
2055
2069
|
moveTemp(v) {
|
|
2056
|
-
if (!isRefCounted(v.type))
|
|
2070
|
+
if (!isRefCounted(v.type) || this.immortalValues.has(v.name))
|
|
2057
2071
|
return;
|
|
2058
2072
|
for (let i = this.frames.length - 1; i >= 0; i--) {
|
|
2059
2073
|
const idx = this.frames[i].findIndex((e) => e.name === v.name);
|
|
@@ -2067,6 +2081,8 @@ export class LlEmitter {
|
|
|
2067
2081
|
/** The retained (+1) read of a refcounted value — type-directed through
|
|
2068
2082
|
* the `_v` table (immortals skip, exactly the C retain calls). */
|
|
2069
2083
|
retainValue(name, type) {
|
|
2084
|
+
if (this.immortalValues.has(name))
|
|
2085
|
+
return name;
|
|
2070
2086
|
const t = this.B.tmp();
|
|
2071
2087
|
this.B.line(`${t} = call ptr ${retainSym(this.shapeHost, type)}(ptr ${name})`);
|
|
2072
2088
|
return t;
|
|
@@ -2074,6 +2090,8 @@ export class LlEmitter {
|
|
|
2074
2090
|
/** The release call for one owned refcounted value — type-directed like
|
|
2075
2091
|
* releaseCallC (all runtime releases are NULL-tolerant). */
|
|
2076
2092
|
releaseValue(name, type) {
|
|
2093
|
+
if (this.immortalValues.has(name))
|
|
2094
|
+
return;
|
|
2077
2095
|
this.B.line(`call void ${releaseSym(this.shapeHost, type)}(ptr ${name})`);
|
|
2078
2096
|
}
|
|
2079
2097
|
releaseFrame(frame) {
|
|
@@ -2912,20 +2930,24 @@ export class LlEmitter {
|
|
|
2912
2930
|
this.jumpTargets = [];
|
|
2913
2931
|
this.currentLocals = new Map(fn.locals.map((l) => [l.id, l]));
|
|
2914
2932
|
this.borrowedParameters.clear();
|
|
2915
|
-
|
|
2933
|
+
this.stableCallBindings = this.callLifetimes.bindings.get(fn.name) ?? new Set();
|
|
2934
|
+
const borrowedParameterIndexes = this.callLifetimes.borrowed.get(fn.name);
|
|
2916
2935
|
if (borrowedParameterIndexes) {
|
|
2917
2936
|
for (const index of borrowedParameterIndexes)
|
|
2918
2937
|
this.borrowedParameters.add(fn.params[index].localId);
|
|
2919
2938
|
}
|
|
2920
2939
|
this.captureIds = new Set([...(fn.captures ?? []), ...(fn.classCaptures ?? [])].map((c) => c.localId));
|
|
2921
2940
|
this.integerLoopBindings.clear();
|
|
2941
|
+
this.countedLoopsEnabled = this.debug === null && !fn.async && !fn.generator;
|
|
2942
|
+
this.integerViews.clear();
|
|
2922
2943
|
this.fieldPointerTags.clear();
|
|
2923
2944
|
this.integerArrayBindings.clear();
|
|
2924
|
-
this.localArrayReads = findLocalArrayReads(fn, this.fnByName, this.unionsById, this.
|
|
2925
|
-
this.callArrayReads = findCallArrayReads(fn, this.optionalArrayReads, this.
|
|
2945
|
+
this.localArrayReads = findLocalArrayReads(fn, this.fnByName, this.unionsById, this.referenceEffects.functions, this.callLifetimes, this.optionalArrayReads);
|
|
2946
|
+
this.callArrayReads = findCallArrayReads(fn, this.optionalArrayReads, this.referenceEffects.functions, this.callLifetimes);
|
|
2926
2947
|
this.mapReadLifetimes = findMapReadLifetimes(fn, this.unionsById, this.callLifetimes);
|
|
2927
2948
|
this.localStackUnions = findLocalStackUnions(fn, this.callLifetimes, this.unionsById);
|
|
2928
2949
|
this.integerRanges = analyzeIntegerRanges(fn);
|
|
2950
|
+
this.bytesBounds = findBytesBounds(fn, this.integerRanges);
|
|
2929
2951
|
this.chainSlots.clear();
|
|
2930
2952
|
this.finallyStack = [];
|
|
2931
2953
|
this.tryStack = [];
|
|
@@ -2967,6 +2989,13 @@ export class LlEmitter {
|
|
|
2967
2989
|
};
|
|
2968
2990
|
}
|
|
2969
2991
|
const paramIds = new Set(fn.params.map((p) => p.localId));
|
|
2992
|
+
if (this.debug === null) {
|
|
2993
|
+
for (const id of findIntegerViews(fn)) {
|
|
2994
|
+
const slot = B.slot();
|
|
2995
|
+
B.entryAllocas.push(`${slot} = alloca i32 ; integer view ${this.currentLocals.get(id).name}`);
|
|
2996
|
+
this.integerViews.set(id, slot);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2970
2999
|
for (const local of fn.locals) {
|
|
2971
3000
|
// Boxed locals' slots hold their capture BOX (a ptr); captured
|
|
2972
3001
|
// (env-borrowed) locals bind the incoming box below. A caught-typed
|
|
@@ -3016,7 +3045,7 @@ export class LlEmitter {
|
|
|
3016
3045
|
// params (callees own their params — callers passed +1). Boxed params
|
|
3017
3046
|
// allocate the shared binding and move the raw value in.
|
|
3018
3047
|
const fnScope = [];
|
|
3019
|
-
const borrowed = this.callLifetimes.
|
|
3048
|
+
const borrowed = this.callLifetimes.borrowed.get(fn.name);
|
|
3020
3049
|
for (const [index, p] of fn.params.entries()) {
|
|
3021
3050
|
const local = this.currentLocals.get(p.localId);
|
|
3022
3051
|
const slot = `%${mangleLocal(p.localId)}`;
|
|
@@ -3029,6 +3058,7 @@ export class LlEmitter {
|
|
|
3029
3058
|
continue;
|
|
3030
3059
|
}
|
|
3031
3060
|
B.line(`store ${this.llType(p.type)} %p_${mangleLocal(p.localId)}, ptr ${slot}`);
|
|
3061
|
+
this.storeIntegerView(p.localId, { name: `%p_${mangleLocal(p.localId)}`, type: p.type });
|
|
3032
3062
|
if (isRefCounted(p.type) && !borrowed?.has(index))
|
|
3033
3063
|
fnScope.push({ slot, type: p.type });
|
|
3034
3064
|
}
|
|
@@ -3132,7 +3162,7 @@ export class LlEmitter {
|
|
|
3132
3162
|
B.terminate(ret === "void" ? "ret void" : `ret ${ret} %result`);
|
|
3133
3163
|
return `define internal ${ret} @${mangleFunction(fn.name)}(${params.join(", ")}) ${FN_ATTRS} {\n${B.render()}\n}`;
|
|
3134
3164
|
}
|
|
3135
|
-
/** A local
|
|
3165
|
+
/** A local stable binding keeps its value alive while later arguments
|
|
3136
3166
|
* and the callee execute. Global, captured, TDZ and mutable bindings need
|
|
3137
3167
|
* an owned snapshot, even when the callee only inspects the value. */
|
|
3138
3168
|
canBorrowCallArgument(value) {
|
|
@@ -3142,7 +3172,7 @@ export class LlEmitter {
|
|
|
3142
3172
|
return false;
|
|
3143
3173
|
const binding = this.binding(value.localId);
|
|
3144
3174
|
return binding.kind === "local" && binding.local !== undefined &&
|
|
3145
|
-
(!binding.local.mutable || this.borrowedParameters.has(value.localId)) && !binding.local.boxed && !binding.local.tdz;
|
|
3175
|
+
(!binding.local.mutable || this.stableCallBindings.has(value.localId) || this.borrowedParameters.has(value.localId)) && !binding.local.boxed && !binding.local.tdz;
|
|
3146
3176
|
}
|
|
3147
3177
|
// ── statements ──────────────────────────────────────────────────────────
|
|
3148
3178
|
emitStmts(stmts) {
|
|
@@ -3248,9 +3278,20 @@ export class LlEmitter {
|
|
|
3248
3278
|
}
|
|
3249
3279
|
break;
|
|
3250
3280
|
}
|
|
3281
|
+
// A lexical const alias cannot outlive its unchanged local owner.
|
|
3282
|
+
// Whole-value uses still retain their own copies, so returning or
|
|
3283
|
+
// storing the alias preserves the ordinary heap representation.
|
|
3284
|
+
// Boxed captures, reassigned sources and projections need ownership.
|
|
3285
|
+
if (b.kind === "local" && b.local && !b.local.mutable && isRefCounted(b.type) &&
|
|
3286
|
+
this.stableCallBindings.has(s.localId) && this.canBorrowCallArgument(s.init)) {
|
|
3287
|
+
const v = this.emitReadReceiver(s.init);
|
|
3288
|
+
B.line(`store ptr ${v.name}, ptr ${b.slot}`);
|
|
3289
|
+
break;
|
|
3290
|
+
}
|
|
3251
3291
|
const v = this.emitExpr(s.init);
|
|
3252
3292
|
this.moveTemp(v);
|
|
3253
3293
|
B.line(`store ${this.llType(b.type)} ${v.name}, ptr ${b.slot}`);
|
|
3294
|
+
this.storeIntegerView(s.localId, v, s.init);
|
|
3254
3295
|
if (isRefCounted(b.type)) {
|
|
3255
3296
|
this.scopes[this.scopes.length - 1].push({ slot: b.slot, type: b.type });
|
|
3256
3297
|
}
|
|
@@ -3281,6 +3322,7 @@ export class LlEmitter {
|
|
|
3281
3322
|
this.releaseValue(old, b.type);
|
|
3282
3323
|
}
|
|
3283
3324
|
B.line(`store ${this.llType(b.type)} ${v.name}, ptr ${b.slot}`);
|
|
3325
|
+
this.storeIntegerView(s.localId, v, s.value);
|
|
3284
3326
|
break;
|
|
3285
3327
|
}
|
|
3286
3328
|
case "exprStmt":
|
|
@@ -3290,7 +3332,7 @@ export class LlEmitter {
|
|
|
3290
3332
|
// Evaluation order matches JS: array, index, then value. Ownership
|
|
3291
3333
|
// of a refcounted value moves into the array (the runtime releases
|
|
3292
3334
|
// the replaced element itself).
|
|
3293
|
-
const arr = this
|
|
3335
|
+
const arr = emitBorrowedInput(this, s.arr);
|
|
3294
3336
|
const idx = this.emitExpr(s.index);
|
|
3295
3337
|
const v = this.emitExpr(s.value);
|
|
3296
3338
|
if (s.arr.type.kind !== "array")
|
|
@@ -3304,7 +3346,7 @@ export class LlEmitter {
|
|
|
3304
3346
|
break;
|
|
3305
3347
|
}
|
|
3306
3348
|
case "arraySetLength": {
|
|
3307
|
-
const arr = this
|
|
3349
|
+
const arr = emitBorrowedInput(this, s.arr);
|
|
3308
3350
|
const length = this.emitExpr(s.length);
|
|
3309
3351
|
if (s.arr.type.kind !== "array")
|
|
3310
3352
|
throw new InternalCompilerError("llvm emitter bug: arraySetLength on non-array");
|
|
@@ -3315,7 +3357,7 @@ export class LlEmitter {
|
|
|
3315
3357
|
}
|
|
3316
3358
|
case "arraySetUndefined":
|
|
3317
3359
|
case "arrayDelete": {
|
|
3318
|
-
const arr = this
|
|
3360
|
+
const arr = emitBorrowedInput(this, s.arr);
|
|
3319
3361
|
const idx = this.emitExpr(s.index);
|
|
3320
3362
|
if (s.arr.type.kind !== "array")
|
|
3321
3363
|
throw new InternalCompilerError(`llvm emitter bug: ${s.kind} on non-array`);
|
|
@@ -3334,12 +3376,11 @@ export class LlEmitter {
|
|
|
3334
3376
|
// IrBytesElem is static, so never rediscover it through the
|
|
3335
3377
|
// generic runtime switch in a hot loop.
|
|
3336
3378
|
const arr = this.emitStableReceiver(s.arr, [s.index, s.value]);
|
|
3337
|
-
const
|
|
3338
|
-
const idx = integerIndex ?? this.emitExpr(s.index).name;
|
|
3379
|
+
const idx = this.emitExpr(s.index);
|
|
3339
3380
|
const v = this.emitExpr(s.value);
|
|
3340
3381
|
if (s.arr.type.kind !== "bytes")
|
|
3341
3382
|
throw new InternalCompilerError("llvm emitter bug: bytesSet on non-bytes");
|
|
3342
|
-
this.emitBytesSet(s.arr.type.elem, arr.name, idx, v.
|
|
3383
|
+
this.emitBytesSet(s.arr.type.elem, arr.name, idx, v, s.index, s.value, this.bytesBounds.has(s));
|
|
3343
3384
|
break;
|
|
3344
3385
|
}
|
|
3345
3386
|
case "fieldSet":
|
|
@@ -3612,8 +3653,26 @@ export class LlEmitter {
|
|
|
3612
3653
|
// release it — scopeDepth captured after the push, C parity).
|
|
3613
3654
|
const integerArrayLoop = this.debug === null ? matchIntegerArrayForLoop(s, this.currentLocals, this.integerArrayBindings) : null;
|
|
3614
3655
|
const integerLoop = integerArrayLoop ?? (this.debug === null ? matchIntegerBytesForLoop(s, this.currentLocals) : null);
|
|
3656
|
+
const countedLoop = !integerLoop && this.countedLoopsEnabled ? matchIntegerCountedForLoop(s, this.currentLocals, this.integerRanges) : null;
|
|
3657
|
+
let countedJoin = null;
|
|
3658
|
+
if (countedLoop?.guarded) {
|
|
3659
|
+
const bound = this.emitExpr(countedLoop.limit);
|
|
3660
|
+
const safe = B.tmp();
|
|
3661
|
+
const fast = B.newLabel("counted.fast");
|
|
3662
|
+
const slow = B.newLabel("counted.slow");
|
|
3663
|
+
countedJoin = B.newLabel("counted.done");
|
|
3664
|
+
B.line(`${safe} = fcmp ${countedLoop.step > 0 ? "ole" : "oge"} double ${bound.name}, ${f64Lit(countedLoop.guardLimit)}`);
|
|
3665
|
+
B.condBr(safe, fast, slow);
|
|
3666
|
+
B.startBlock(slow);
|
|
3667
|
+
this.countedLoopsEnabled = false;
|
|
3668
|
+
this.emitStmt(s);
|
|
3669
|
+
this.countedLoopsEnabled = true;
|
|
3670
|
+
B.br(countedJoin);
|
|
3671
|
+
B.startBlock(fast);
|
|
3672
|
+
}
|
|
3615
3673
|
this.scopes.push([]);
|
|
3616
3674
|
let integerSlot = null;
|
|
3675
|
+
let countedLimit = null;
|
|
3617
3676
|
if (integerLoop) {
|
|
3618
3677
|
integerSlot = B.slot();
|
|
3619
3678
|
B.entryAllocas.push(`${integerSlot} = alloca ${this.sizeType} ; integer induction ${this.currentLocals.get(integerLoop.localId).name}`);
|
|
@@ -3625,7 +3684,18 @@ export class LlEmitter {
|
|
|
3625
3684
|
this.integerArrayBindings.add(integerLoop.localId);
|
|
3626
3685
|
}
|
|
3627
3686
|
B.line(`store ${this.sizeType} ${start}, ptr ${integerSlot}`);
|
|
3628
|
-
this.integerLoopBindings.set(integerLoop.localId, integerSlot
|
|
3687
|
+
this.integerLoopBindings.set(integerLoop.localId, { slot: integerSlot, type: this.sizeType,
|
|
3688
|
+
range: { min: 0, max: integerArrayLoop ? 4294967294 : Number.MAX_SAFE_INTEGER - 1 } });
|
|
3689
|
+
}
|
|
3690
|
+
else if (countedLoop) {
|
|
3691
|
+
integerSlot = B.slot();
|
|
3692
|
+
B.entryAllocas.push(`${integerSlot} = alloca i64 ; integer induction ${this.currentLocals.get(countedLoop.localId).name}`);
|
|
3693
|
+
const startNumber = this.emitExpr(countedLoop.start);
|
|
3694
|
+
const start = B.tmp();
|
|
3695
|
+
B.line(`${start} = fptosi double ${startNumber.name} to i64`);
|
|
3696
|
+
B.line(`store i64 ${start}, ptr ${integerSlot}`);
|
|
3697
|
+
this.integerLoopBindings.set(countedLoop.localId, { slot: integerSlot, type: "i64", signed: true, range: countedLoop.range });
|
|
3698
|
+
countedLimit = emitCountedLoopLimit(this, countedLoop);
|
|
3629
3699
|
}
|
|
3630
3700
|
else if (s.init) {
|
|
3631
3701
|
// A multi-declarator head shares the loop's scope. Emitting its
|
|
@@ -3680,6 +3750,13 @@ export class LlEmitter {
|
|
|
3680
3750
|
B.line(`${inBounds} = icmp ult ${this.sizeType} ${index}, ${len}`);
|
|
3681
3751
|
B.condBr(inBounds, lb, le);
|
|
3682
3752
|
}
|
|
3753
|
+
else if (countedLoop && integerSlot && countedLimit) {
|
|
3754
|
+
const index = B.tmp();
|
|
3755
|
+
const inBounds = B.tmp();
|
|
3756
|
+
B.line(`${index} = load i64, ptr ${integerSlot}`);
|
|
3757
|
+
B.line(`${inBounds} = icmp ${countedLoop.step > 0 ? "slt" : "sgt"} i64 ${index}, ${countedLimit}`);
|
|
3758
|
+
B.condBr(inBounds, lb, le);
|
|
3759
|
+
}
|
|
3683
3760
|
else if (s.cond)
|
|
3684
3761
|
B.condBr(this.emitCondition(s.cond), lb, le);
|
|
3685
3762
|
else
|
|
@@ -3707,6 +3784,13 @@ export class LlEmitter {
|
|
|
3707
3784
|
B.line(`${next} = add nuw ${this.sizeType} ${old}, 1`);
|
|
3708
3785
|
B.line(`store ${this.sizeType} ${next}, ptr ${integerSlot}`);
|
|
3709
3786
|
}
|
|
3787
|
+
else if (countedLoop && integerSlot) {
|
|
3788
|
+
const old = B.tmp();
|
|
3789
|
+
const next = B.tmp();
|
|
3790
|
+
B.line(`${old} = load i64, ptr ${integerSlot}`);
|
|
3791
|
+
B.line(`${next} = add nsw i64 ${old}, ${countedLoop.step}`);
|
|
3792
|
+
B.line(`store i64 ${next}, ptr ${integerSlot}`);
|
|
3793
|
+
}
|
|
3710
3794
|
else if (s.update)
|
|
3711
3795
|
this.emitStmt(s.update);
|
|
3712
3796
|
B.br(lc);
|
|
@@ -3715,8 +3799,14 @@ export class LlEmitter {
|
|
|
3715
3799
|
this.releaseScope(this.scopes.pop());
|
|
3716
3800
|
if (integerLoop)
|
|
3717
3801
|
this.integerLoopBindings.delete(integerLoop.localId);
|
|
3802
|
+
if (countedLoop)
|
|
3803
|
+
this.integerLoopBindings.delete(countedLoop.localId);
|
|
3718
3804
|
if (integerArrayLoop)
|
|
3719
3805
|
this.integerArrayBindings.delete(integerArrayLoop.localId);
|
|
3806
|
+
if (countedJoin) {
|
|
3807
|
+
B.br(countedJoin);
|
|
3808
|
+
B.startBlock(countedJoin);
|
|
3809
|
+
}
|
|
3720
3810
|
break;
|
|
3721
3811
|
}
|
|
3722
3812
|
case "forOf": {
|
|
@@ -4202,12 +4292,15 @@ export class LlEmitter {
|
|
|
4202
4292
|
* branch and moves the result into `slot`: the chosen value's ownership
|
|
4203
4293
|
* transfers, every other temp the arm allocated releases inside the
|
|
4204
4294
|
* branch. The shared core of ternary/logical. */
|
|
4205
|
-
emitBranchInto(slot, expr) {
|
|
4295
|
+
emitBranchInto(slot, expr, integerSlot) {
|
|
4206
4296
|
this.frames.push([]);
|
|
4207
4297
|
const v = this.emitExpr(expr);
|
|
4208
4298
|
this.moveTemp(v);
|
|
4209
4299
|
this.B.line(`store ${this.llType(expr.type)} ${v.name}, ptr ${slot}`);
|
|
4300
|
+
if (integerSlot !== undefined && v.uint32 !== undefined)
|
|
4301
|
+
this.B.line(`store i32 ${v.uint32}, ptr ${integerSlot}`);
|
|
4210
4302
|
this.releaseFrame(this.frames.pop());
|
|
4303
|
+
return v.uint32 !== undefined;
|
|
4211
4304
|
}
|
|
4212
4305
|
// ── expressions ─────────────────────────────────────────────────────────
|
|
4213
4306
|
emitLiteralExpr(e) {
|
|
@@ -4484,8 +4577,8 @@ export class LlEmitter {
|
|
|
4484
4577
|
emitIntegerLoopIndex(expr) {
|
|
4485
4578
|
return emitIntegerLoopIndex(this, expr);
|
|
4486
4579
|
}
|
|
4487
|
-
emitBytesIndex(receiver, index,
|
|
4488
|
-
return emitBytesIndex(this, receiver, index,
|
|
4580
|
+
emitBytesIndex(receiver, index, expr, inBounds = false) {
|
|
4581
|
+
return emitBytesIndex(this, receiver, index, expr, undefined, inBounds);
|
|
4489
4582
|
}
|
|
4490
4583
|
emitBytesData(receiver) {
|
|
4491
4584
|
return emitBytesData(this, receiver);
|
|
@@ -4493,14 +4586,21 @@ export class LlEmitter {
|
|
|
4493
4586
|
emitBytesLength(elem, receiver, bytes) {
|
|
4494
4587
|
return emitBytesLength(this, elem, receiver, bytes);
|
|
4495
4588
|
}
|
|
4496
|
-
emitBytesGet(elem, receiver, index,
|
|
4497
|
-
return emitBytesGet(this, elem, receiver, index,
|
|
4589
|
+
emitBytesGet(elem, receiver, index, expr, inBounds = false) {
|
|
4590
|
+
return emitBytesGet(this, elem, receiver, index, expr, inBounds);
|
|
4498
4591
|
}
|
|
4499
|
-
emitToUint32(value, expr) {
|
|
4500
|
-
return emitToUint32(this, value, expr);
|
|
4592
|
+
emitToUint32(value, expr, uint32) {
|
|
4593
|
+
return emitToUint32(this, value, expr, uint32);
|
|
4594
|
+
}
|
|
4595
|
+
storeIntegerView(localId, value, expr) {
|
|
4596
|
+
const slot = this.integerViews.get(localId);
|
|
4597
|
+
if (slot === undefined)
|
|
4598
|
+
return;
|
|
4599
|
+
const integer = this.emitToUint32(value.name, expr, value.uint32);
|
|
4600
|
+
this.B.line(`store i32 ${integer}, ptr ${slot}`);
|
|
4501
4601
|
}
|
|
4502
|
-
emitBytesSet(elem, receiver, index, value,
|
|
4503
|
-
return emitBytesSet(this, elem, receiver, index, value,
|
|
4602
|
+
emitBytesSet(elem, receiver, index, value, indexExpr, expr, inBounds = false) {
|
|
4603
|
+
return emitBytesSet(this, elem, receiver, index, value, indexExpr, expr, inBounds);
|
|
4504
4604
|
}
|
|
4505
4605
|
emitBytesIntrinsic(e) {
|
|
4506
4606
|
return emitBytesIntrinsic(this, e);
|