@scriptc/compiler 0.2.2 → 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/ambient/scriptc-node-fallback.d.ts +99 -27
- package/dist/backend/cycle-analysis.js +63 -50
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +51 -0
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- 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/borrowed-receivers.d.ts +9 -0
- package/dist/backend/llvm/borrowed-receivers.js +58 -0
- package/dist/backend/llvm/borrowed-receivers.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 +27 -0
- package/dist/backend/llvm/call-lifetimes.js +199 -0
- package/dist/backend/llvm/call-lifetimes.js.map +1 -0
- package/dist/backend/llvm/call-lifetimes.test.d.ts +1 -0
- package/dist/backend/llvm/call-lifetimes.test.js +259 -0
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -0
- 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/dyn.d.ts +2 -2
- package/dist/backend/llvm/dyn.js +79 -35
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +56 -7
- package/dist/backend/llvm/emitter.js +310 -37
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +24 -5
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +4 -3
- 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 +50 -5
- 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 +136 -57
- 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 +33 -31
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +61 -27
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +9 -3
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +5 -10
- package/dist/backend/llvm/expr-stream-bridges.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/lib-shared.js +35 -1
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +42 -0
- package/dist/backend/llvm/local-array-reads.js +226 -0
- package/dist/backend/llvm/local-array-reads.js.map +1 -0
- package/dist/backend/llvm/local-array-reads.test.d.ts +1 -0
- package/dist/backend/llvm/local-array-reads.test.js +446 -0
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -0
- package/dist/backend/llvm/map-read-lifetimes.d.ts +36 -0
- package/dist/backend/llvm/map-read-lifetimes.js +128 -0
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -0
- package/dist/backend/llvm/map-read-lifetimes.test.d.ts +1 -0
- package/dist/backend/llvm/map-read-lifetimes.test.js +127 -0
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -0
- 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 +24 -6
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/split.js +15 -5
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/split.test.js +17 -0
- package/dist/backend/llvm/split.test.js.map +1 -1
- package/dist/backend/llvm/stack-unions.d.ts +20 -0
- package/dist/backend/llvm/stack-unions.js +61 -0
- package/dist/backend/llvm/stack-unions.js.map +1 -0
- package/dist/backend/llvm/stack-unions.test.d.ts +1 -0
- package/dist/backend/llvm/stack-unions.test.js +262 -0
- package/dist/backend/llvm/stack-unions.test.js.map +1 -0
- 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 +12 -0
- package/dist/backend/llvm/string-lifetimes.js +72 -0
- package/dist/backend/llvm/string-lifetimes.js.map +1 -0
- package/dist/backend/llvm/string-lifetimes.test.d.ts +1 -0
- package/dist/backend/llvm/string-lifetimes.test.js +239 -0
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -0
- package/dist/backend/mangle.d.ts +2 -0
- package/dist/backend/mangle.js +4 -0
- package/dist/backend/mangle.js.map +1 -1
- package/dist/backend/may-throw.js +67 -29
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +104 -1
- package/dist/backend/may-throw.test.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +2 -2
- package/dist/backend/native-toolchain.js +2 -0
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/frontend/input-tracker.js +3 -3
- package/dist/frontend/input-tracker.js.map +1 -1
- package/dist/frontend/input-tracker.test.js +38 -2
- package/dist/frontend/input-tracker.test.js.map +1 -1
- package/dist/frontend/lowering/collection-ingestion.d.ts +31 -0
- package/dist/frontend/lowering/collection-ingestion.js +224 -0
- package/dist/frontend/lowering/collection-ingestion.js.map +1 -0
- package/dist/frontend/lowering/containers/bytes.js +2 -0
- package/dist/frontend/lowering/containers/bytes.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/import-meta.d.ts +10 -0
- package/dist/frontend/lowering/import-meta.js +52 -0
- package/dist/frontend/lowering/import-meta.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.js +105 -41
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.d.ts +2 -0
- package/dist/frontend/lowering/lower-builtin-values.js +22 -1
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +3 -13
- package/dist/frontend/lowering/lower-builtins.js +152 -46
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +6 -3
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +7 -7
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +5 -1
- package/dist/frontend/lowering/lower-containers.js +52 -37
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +47 -43
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +43 -0
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +72 -68
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-system-errors.d.ts +6 -0
- package/dist/frontend/lowering/lower-system-errors.js +29 -0
- package/dist/frontend/lowering/lower-system-errors.js.map +1 -0
- package/dist/frontend/lowering/lowerer.d.ts +4 -3
- package/dist/frontend/lowering/lowerer.js +60 -25
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +2 -2
- package/dist/frontend/lowering/surfaces.js +69 -21
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/module-syntax.d.ts +1 -1
- package/dist/frontend/module-syntax.js +8 -1
- package/dist/frontend/module-syntax.js.map +1 -1
- package/dist/frontend/module-syntax.test.js +10 -0
- package/dist/frontend/module-syntax.test.js.map +1 -1
- package/dist/frontend/npm.d.ts +4 -0
- package/dist/frontend/npm.js +86 -33
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.js +103 -0
- package/dist/frontend/npm.test.js.map +1 -1
- package/dist/frontend/pipeline-native.js +2 -1
- package/dist/frontend/pipeline-native.js.map +1 -1
- package/dist/frontend/services-node.js +3 -2
- package/dist/frontend/services-node.js.map +1 -1
- package/dist/frontend/services.d.ts +5 -1
- package/dist/frontend/services.js +9 -1
- package/dist/frontend/services.js.map +1 -1
- package/dist/frontend/services.test.js +21 -0
- package/dist/frontend/services.test.js.map +1 -1
- package/dist/frontend/ts7/ast-node.d.ts +2 -0
- package/dist/frontend/ts7/ast-node.js +30 -4
- package/dist/frontend/ts7/ast-node.js.map +1 -1
- package/dist/frontend/ts7/ast-node.test.js +60 -1
- package/dist/frontend/ts7/ast-node.test.js.map +1 -1
- package/dist/frontend/type-mapper.js +8 -2
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.js +4 -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 +26 -0
- package/dist/ir/integer-loops.js +130 -1
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-loops.test.d.ts +1 -0
- package/dist/ir/integer-loops.test.js +91 -0
- package/dist/ir/integer-loops.test.js.map +1 -0
- 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 +29 -22
- package/dist/ir/ir.js +59 -14
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +18 -0
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/validate.js +208 -105
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +247 -0
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/fence-eval.js +9 -2
- package/dist/library/fence-eval.js.map +1 -1
- package/dist/library/fence-eval.test.d.ts +1 -0
- package/dist/library/fence-eval.test.js +21 -0
- package/dist/library/fence-eval.test.js.map +1 -0
- package/dist/native/runtime-typescript.d.ts +4 -0
- package/dist/native/runtime-typescript.js +61 -0
- package/dist/native/runtime-typescript.js.map +1 -0
- package/dist/native/runtime-typescript.test.d.ts +1 -0
- package/dist/native/runtime-typescript.test.js +41 -0
- package/dist/native/runtime-typescript.test.js.map +1 -0
- 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 +271 -17
|
@@ -22,16 +22,26 @@ 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 { 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";
|
|
32
35
|
import { computeMayThrow } from "../may-throw.js";
|
|
33
|
-
import { mangleArgPack, mangleAsyncSpawn, mangleClassObj, mangleClassStruct, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordStruct, mangleTrampoline, mangleWrapper, mangleVtInstance } from "../mangle.js";
|
|
36
|
+
import { mangleArgPack, mangleAsyncSpawn, mangleBorrowedFunction, mangleClassObj, mangleClassStruct, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordStruct, mangleTrampoline, mangleWrapper, mangleVtInstance } from "../mangle.js";
|
|
37
|
+
import { analyzeCallLifetimes } from "./call-lifetimes.js";
|
|
38
|
+
import { emitStackUnion, findLocalStackUnions } from "./stack-unions.js";
|
|
34
39
|
import { BlockBuilder } from "./blocks.js";
|
|
40
|
+
import { emitLocalArrayRead, findLocalArrayReads, findCallArrayReads, OptionalArrayReads } from "./local-array-reads.js";
|
|
41
|
+
import { emitStackMapRead, findMapReadLifetimes } from "./map-read-lifetimes.js";
|
|
42
|
+
import { emitBorrowedFieldSequence } from "./borrowed-receivers.js";
|
|
43
|
+
import { emitBorrowedInput } from "./borrowed-inputs.js";
|
|
44
|
+
import { ReferenceEffects } from "./reference-effects.js";
|
|
35
45
|
import { LlvmDebugInfo } from "./debug-info.js";
|
|
36
46
|
import { f64Lit, ffiNativeTypeLl, ffiNativeParamLl, ffiNativeReturnLl, llvmCommentText } from "./common.js";
|
|
37
47
|
import { ffiExtendsNarrowIntegers } from "../targets.js";
|
|
@@ -101,6 +111,41 @@ function llStrBytes(text) {
|
|
|
101
111
|
}
|
|
102
112
|
export class LlEmitter {
|
|
103
113
|
mod;
|
|
114
|
+
localArrayReads = new Map();
|
|
115
|
+
localStackUnions = new Map();
|
|
116
|
+
integerArrayBindings = new Set();
|
|
117
|
+
fieldAliasTags = new Map();
|
|
118
|
+
fieldPointerTags = new Map();
|
|
119
|
+
/** Native class fields with different property names cannot occupy the
|
|
120
|
+
* same slot of one object. Inherited views of the same property share a
|
|
121
|
+
* tag. Array storage has its own disjoint categories; untagged runtime
|
|
122
|
+
* operations and record views conservatively alias all of them. */
|
|
123
|
+
markFieldPointer(ptr, field) {
|
|
124
|
+
this.markMemoryPointer(ptr, `field:${field}`);
|
|
125
|
+
}
|
|
126
|
+
markMemoryPointer(ptr, field) {
|
|
127
|
+
if (this.debug !== null)
|
|
128
|
+
return;
|
|
129
|
+
let tag = this.fieldAliasTags.get(field);
|
|
130
|
+
if (tag === undefined) {
|
|
131
|
+
tag = 2 + this.fieldAliasTags.size * 2;
|
|
132
|
+
this.fieldAliasTags.set(field, tag);
|
|
133
|
+
}
|
|
134
|
+
this.fieldPointerTags.set(ptr, tag);
|
|
135
|
+
}
|
|
136
|
+
fieldAliasAttachment(ptr) {
|
|
137
|
+
const tag = this.fieldPointerTags.get(ptr);
|
|
138
|
+
return tag === undefined ? "" : `, !tbaa !${tag}`;
|
|
139
|
+
}
|
|
140
|
+
fieldAliasMetadata() {
|
|
141
|
+
if (this.fieldAliasTags.size === 0)
|
|
142
|
+
return [];
|
|
143
|
+
const lines = ['!0 = !{!"scriptc native fields"}'];
|
|
144
|
+
for (const tag of this.fieldAliasTags.values()) {
|
|
145
|
+
lines.push(`!${tag - 1} = !{!"field.${tag}", !0, i64 0}`, `!${tag} = !{!${tag - 1}, !${tag - 1}, i64 0}`);
|
|
146
|
+
}
|
|
147
|
+
return lines;
|
|
148
|
+
}
|
|
104
149
|
debug;
|
|
105
150
|
debugScope = null;
|
|
106
151
|
sizeType;
|
|
@@ -116,6 +161,7 @@ export class LlEmitter {
|
|
|
116
161
|
* immortal (rc == SIZE_MAX) static per (union, unit tag).
|
|
117
162
|
* RC entry points and the collector skip immortals. */
|
|
118
163
|
unitInstances = new Map();
|
|
164
|
+
immortalValues = new Set();
|
|
119
165
|
/** Regex literal templates: "<flags>/<pattern>" → { symbol, interned
|
|
120
166
|
* source/flags literal refs } — one immortal ScrRegex template per distinct
|
|
121
167
|
* (pattern, flags) pair; the bytecode slot starts null and the runtime
|
|
@@ -149,6 +195,13 @@ export class LlEmitter {
|
|
|
149
195
|
needsBadKey = false;
|
|
150
196
|
needsRetainBox = false;
|
|
151
197
|
fnByName = new Map();
|
|
198
|
+
referenceEffects;
|
|
199
|
+
optionalArrayReads;
|
|
200
|
+
callArrayReads = new Map();
|
|
201
|
+
mapReadLifetimes = { locals: new Map(), arguments: new Map() };
|
|
202
|
+
callLifetimes;
|
|
203
|
+
borrowedParameters = new Set();
|
|
204
|
+
stableCallBindings = new Set();
|
|
152
205
|
/** Manifest-bound native imports, used by ffiCall emission. */
|
|
153
206
|
ffiByName = new Map();
|
|
154
207
|
/** C-ABI callback trampolines and (for raw/no-userdata callbacks) their
|
|
@@ -230,9 +283,12 @@ export class LlEmitter {
|
|
|
230
283
|
jumpTargets = [];
|
|
231
284
|
currentLocals = new Map();
|
|
232
285
|
captureIds = new Set();
|
|
233
|
-
/** Active
|
|
286
|
+
/** Active induction bindings retain their width independently of size_t. */
|
|
234
287
|
integerLoopBindings = new Map();
|
|
288
|
+
countedLoopsEnabled = false;
|
|
235
289
|
integerRanges = new Map();
|
|
290
|
+
bytesBounds = new Set();
|
|
291
|
+
integerViews = new Map();
|
|
236
292
|
/** Enclosing try-with-FINALLY regions, innermost last: a `return`
|
|
237
293
|
* inside one runs every crossed finally (innermost first) before the
|
|
238
294
|
* actual ret — the runtime ABI’s pending-return path, with the finally
|
|
@@ -298,6 +354,9 @@ export class LlEmitter {
|
|
|
298
354
|
}
|
|
299
355
|
for (const u of mod.unions ?? [])
|
|
300
356
|
this.unionsById.set(u.id, u);
|
|
357
|
+
this.optionalArrayReads = new OptionalArrayReads(this.fnByName, this.unionsById);
|
|
358
|
+
this.referenceEffects = new ReferenceEffects(this.fnByName, (call) => this.optionalArrayReads.get(call) !== null);
|
|
359
|
+
this.callLifetimes = analyzeCallLifetimes(this.fnByName);
|
|
301
360
|
for (const r of mod.records ?? [])
|
|
302
361
|
this.recordsById.set(r.id, r);
|
|
303
362
|
const traced = computeTraced(mod);
|
|
@@ -849,6 +908,7 @@ export class LlEmitter {
|
|
|
849
908
|
this.declare(`declare void @scr_net_island_install()`);
|
|
850
909
|
if (usesIsland) {
|
|
851
910
|
this.declare(`declare void @scr_island_modules(ptr, ${this.sizeType}, ptr, ${this.sizeType})`);
|
|
911
|
+
this.declare(`declare void @scr_island_entry_module(ptr)`);
|
|
852
912
|
this.declare(`declare i32 @scr_island_exit_code()`);
|
|
853
913
|
if (moduleEmbedsCompressedNpm(this.mod)) {
|
|
854
914
|
this.declare(`declare void @scr_island_set_inflate(ptr)`);
|
|
@@ -1116,6 +1176,7 @@ export class LlEmitter {
|
|
|
1116
1176
|
}
|
|
1117
1177
|
if (this.templateStringsInstances.size > 0)
|
|
1118
1178
|
out.push(``);
|
|
1179
|
+
const islandEntryPath = usesIsland ? this.cstr(this.mod.sourceFile) : null;
|
|
1119
1180
|
for (const [text, c] of this.cstrs) {
|
|
1120
1181
|
// NUL-terminated byte-array constants for scr_jb_puts and indentation.
|
|
1121
1182
|
out.push(`@${c.sym} = internal constant [${c.len + 1} x i8] c"${llStrBytes(text)}"`);
|
|
@@ -1247,6 +1308,7 @@ export class LlEmitter {
|
|
|
1247
1308
|
if (hasNoInlineRecordClone)
|
|
1248
1309
|
out.push(`attributes #2 = { noinline sanitize_address }`);
|
|
1249
1310
|
out.push(``);
|
|
1311
|
+
out.push(...this.fieldAliasMetadata());
|
|
1250
1312
|
if (this.debug !== null)
|
|
1251
1313
|
out.push(this.debug.render());
|
|
1252
1314
|
return out;
|
|
@@ -1260,6 +1322,7 @@ export class LlEmitter {
|
|
|
1260
1322
|
// scr_watch.c links only when this line is emitted.
|
|
1261
1323
|
...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(this.ffiHasForeignCallback ? [` call void @scr_ffi_install()`] : []), ...(snapshotsTlsCa ? [` call void @scr_tls_ca_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(embedsZlib ? [` call void @scr_zlib_island_install()`] : []), ...(embedsNet ? [` call void @scr_net_island_install()`] : []), ...(usesNet ? [` call void @scr_net_install()`, ` call void @scr_net_dyn_install()`] : []), ...(usesHttp ? [` call void @scr_http_dyn_install()`] : []), ...(usesDgram ? [` call void @scr_dgram_install()`] : []), ...(usesHttp2 ? [` call void @scr_http2_dyn_install()`] : []), ...(usesStream ? [` call void @scr_stream_install()`] : []), ` call void @scr_lib_init(i32 %argc, ptr %argv)`, ...(usesIsland
|
|
1262
1324
|
? [
|
|
1325
|
+
` call void @scr_island_entry_module(ptr ${islandEntryPath})`,
|
|
1263
1326
|
...(moduleEmbedsCompressedNpm(this.mod)
|
|
1264
1327
|
? [` call void @scr_island_set_inflate(ptr @scr_zlib_inflate_exact)`]
|
|
1265
1328
|
: []),
|
|
@@ -1359,6 +1422,7 @@ export class LlEmitter {
|
|
|
1359
1422
|
// lane's -fsanitize=address link activates instrumentation over the
|
|
1360
1423
|
// emitted functions too (the runtime TUs get theirs from clang).
|
|
1361
1424
|
`attributes #0 = { sanitize_address }`, ...(this.wasi ? [`attributes #1 = { sanitize_address presplitcoroutine }`] : []), ...(hasNoInlineRecordClone ? [`attributes #2 = { noinline sanitize_address }`] : []), ``);
|
|
1425
|
+
out.push(...this.fieldAliasMetadata());
|
|
1362
1426
|
if (this.debug !== null)
|
|
1363
1427
|
out.push(this.debug.render());
|
|
1364
1428
|
return out;
|
|
@@ -1941,7 +2005,9 @@ export class LlEmitter {
|
|
|
1941
2005
|
lit = { sym: `sc_lit_${this.literals.size}`, len: Buffer.byteLength(text, "utf8") };
|
|
1942
2006
|
this.literals.set(text, lit);
|
|
1943
2007
|
}
|
|
1944
|
-
|
|
2008
|
+
const name = `@${lit.sym}`;
|
|
2009
|
+
this.immortalValues.add(name);
|
|
2010
|
+
return name;
|
|
1945
2011
|
}
|
|
1946
2012
|
/** Interned NUL-terminated C-string constant (the scr_jb_puts /
|
|
1947
2013
|
* stringify-indent currency) — `@`-ref, first-use order. */
|
|
@@ -1971,7 +2037,9 @@ export class LlEmitter {
|
|
|
1971
2037
|
sym = `sc_unit_${this.unitInstances.size}`;
|
|
1972
2038
|
this.unitInstances.set(key, sym);
|
|
1973
2039
|
}
|
|
1974
|
-
|
|
2040
|
+
const name = `@${sym}`;
|
|
2041
|
+
this.immortalValues.add(name);
|
|
2042
|
+
return name;
|
|
1975
2043
|
}
|
|
1976
2044
|
declare(decl) {
|
|
1977
2045
|
this.decls.add(decl);
|
|
@@ -1987,7 +2055,7 @@ export class LlEmitter {
|
|
|
1987
2055
|
}
|
|
1988
2056
|
/** Registers an owned refcounted value on the current statement frame. */
|
|
1989
2057
|
own(v) {
|
|
1990
|
-
if (isRefCounted(v.type))
|
|
2058
|
+
if (isRefCounted(v.type) && !this.immortalValues.has(v.name))
|
|
1991
2059
|
this.currentFrame().push(v);
|
|
1992
2060
|
return v;
|
|
1993
2061
|
}
|
|
@@ -1999,7 +2067,7 @@ export class LlEmitter {
|
|
|
1999
2067
|
}
|
|
2000
2068
|
/** Strike a refcounted temp from its frame: ownership is being moved. */
|
|
2001
2069
|
moveTemp(v) {
|
|
2002
|
-
if (!isRefCounted(v.type))
|
|
2070
|
+
if (!isRefCounted(v.type) || this.immortalValues.has(v.name))
|
|
2003
2071
|
return;
|
|
2004
2072
|
for (let i = this.frames.length - 1; i >= 0; i--) {
|
|
2005
2073
|
const idx = this.frames[i].findIndex((e) => e.name === v.name);
|
|
@@ -2013,6 +2081,8 @@ export class LlEmitter {
|
|
|
2013
2081
|
/** The retained (+1) read of a refcounted value — type-directed through
|
|
2014
2082
|
* the `_v` table (immortals skip, exactly the C retain calls). */
|
|
2015
2083
|
retainValue(name, type) {
|
|
2084
|
+
if (this.immortalValues.has(name))
|
|
2085
|
+
return name;
|
|
2016
2086
|
const t = this.B.tmp();
|
|
2017
2087
|
this.B.line(`${t} = call ptr ${retainSym(this.shapeHost, type)}(ptr ${name})`);
|
|
2018
2088
|
return t;
|
|
@@ -2020,6 +2090,8 @@ export class LlEmitter {
|
|
|
2020
2090
|
/** The release call for one owned refcounted value — type-directed like
|
|
2021
2091
|
* releaseCallC (all runtime releases are NULL-tolerant). */
|
|
2022
2092
|
releaseValue(name, type) {
|
|
2093
|
+
if (this.immortalValues.has(name))
|
|
2094
|
+
return;
|
|
2023
2095
|
this.B.line(`call void ${releaseSym(this.shapeHost, type)}(ptr ${name})`);
|
|
2024
2096
|
}
|
|
2025
2097
|
releaseFrame(frame) {
|
|
@@ -2418,7 +2490,7 @@ export class LlEmitter {
|
|
|
2418
2490
|
* a retained peek. */
|
|
2419
2491
|
unionExtract(uName, arm) {
|
|
2420
2492
|
const B = this.B;
|
|
2421
|
-
if (arm.kind === "f64") {
|
|
2493
|
+
if (arm.kind === "f64" || arm.kind === "procStream") {
|
|
2422
2494
|
const t = B.tmp();
|
|
2423
2495
|
this.declare(`declare double @scr_union_get_f64(ptr)`);
|
|
2424
2496
|
B.line(`${t} = call double @scr_union_get_f64(ptr ${uName})`);
|
|
@@ -2438,7 +2510,7 @@ export class LlEmitter {
|
|
|
2438
2510
|
unionNewOwned(tag, v) {
|
|
2439
2511
|
const B = this.B;
|
|
2440
2512
|
const t = B.tmp();
|
|
2441
|
-
if (v.type.kind === "f64") {
|
|
2513
|
+
if (v.type.kind === "f64" || v.type.kind === "procStream") {
|
|
2442
2514
|
this.declare(`declare ptr @scr_union_new_f64(i32, double)`);
|
|
2443
2515
|
B.line(`${t} = call ptr @scr_union_new_f64(i32 ${tag}, double ${v.name})`);
|
|
2444
2516
|
return t;
|
|
@@ -2469,6 +2541,7 @@ export class LlEmitter {
|
|
|
2469
2541
|
const { index, type } = classFieldIndex(meta, field);
|
|
2470
2542
|
const p = this.B.tmp();
|
|
2471
2543
|
this.B.line(`${p} = getelementptr inbounds %${classStructSym(className)}, ptr ${objName}, i64 0, i32 ${index}`);
|
|
2544
|
+
this.markFieldPointer(p, field);
|
|
2472
2545
|
return { ptr: p, type };
|
|
2473
2546
|
}
|
|
2474
2547
|
/** `o->vt->pre` — the dynamic class's preorder number (borrowed object;
|
|
@@ -2559,7 +2632,7 @@ export class LlEmitter {
|
|
|
2559
2632
|
const B = this.B;
|
|
2560
2633
|
const fieldTy = llFieldType(t);
|
|
2561
2634
|
const raw = B.tmp();
|
|
2562
|
-
B.line(`${raw} = load ${fieldTy}, ptr ${ptr}`);
|
|
2635
|
+
B.line(`${raw} = load ${fieldTy}, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
2563
2636
|
if (fieldTy !== "i8")
|
|
2564
2637
|
return raw;
|
|
2565
2638
|
const b = B.tmp();
|
|
@@ -2571,12 +2644,12 @@ export class LlEmitter {
|
|
|
2571
2644
|
const B = this.B;
|
|
2572
2645
|
const fieldTy = llFieldType(t);
|
|
2573
2646
|
if (fieldTy !== "i8") {
|
|
2574
|
-
B.line(`store ${fieldTy} ${value}, ptr ${ptr}`);
|
|
2647
|
+
B.line(`store ${fieldTy} ${value}, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
2575
2648
|
return;
|
|
2576
2649
|
}
|
|
2577
2650
|
const z = B.tmp();
|
|
2578
2651
|
B.line(`${z} = zext i1 ${value} to i8`);
|
|
2579
|
-
B.line(`store i8 ${z}, ptr ${ptr}`);
|
|
2652
|
+
B.line(`store i8 ${z}, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
2580
2653
|
}
|
|
2581
2654
|
// ── bindings ────────────────────────────────────────────────────────────
|
|
2582
2655
|
/** A binding's storage: a module global, a plain local slot, or a boxed
|
|
@@ -2856,9 +2929,25 @@ export class LlEmitter {
|
|
|
2856
2929
|
this.unwindCleanups.clear();
|
|
2857
2930
|
this.jumpTargets = [];
|
|
2858
2931
|
this.currentLocals = new Map(fn.locals.map((l) => [l.id, l]));
|
|
2932
|
+
this.borrowedParameters.clear();
|
|
2933
|
+
this.stableCallBindings = this.callLifetimes.bindings.get(fn.name) ?? new Set();
|
|
2934
|
+
const borrowedParameterIndexes = this.callLifetimes.borrowed.get(fn.name);
|
|
2935
|
+
if (borrowedParameterIndexes) {
|
|
2936
|
+
for (const index of borrowedParameterIndexes)
|
|
2937
|
+
this.borrowedParameters.add(fn.params[index].localId);
|
|
2938
|
+
}
|
|
2859
2939
|
this.captureIds = new Set([...(fn.captures ?? []), ...(fn.classCaptures ?? [])].map((c) => c.localId));
|
|
2860
2940
|
this.integerLoopBindings.clear();
|
|
2941
|
+
this.countedLoopsEnabled = this.debug === null && !fn.async && !fn.generator;
|
|
2942
|
+
this.integerViews.clear();
|
|
2943
|
+
this.fieldPointerTags.clear();
|
|
2944
|
+
this.integerArrayBindings.clear();
|
|
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);
|
|
2947
|
+
this.mapReadLifetimes = findMapReadLifetimes(fn, this.unionsById, this.callLifetimes);
|
|
2948
|
+
this.localStackUnions = findLocalStackUnions(fn, this.callLifetimes, this.unionsById);
|
|
2861
2949
|
this.integerRanges = analyzeIntegerRanges(fn);
|
|
2950
|
+
this.bytesBounds = findBytesBounds(fn, this.integerRanges);
|
|
2862
2951
|
this.chainSlots.clear();
|
|
2863
2952
|
this.finallyStack = [];
|
|
2864
2953
|
this.tryStack = [];
|
|
@@ -2900,6 +2989,13 @@ export class LlEmitter {
|
|
|
2900
2989
|
};
|
|
2901
2990
|
}
|
|
2902
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
|
+
}
|
|
2903
2999
|
for (const local of fn.locals) {
|
|
2904
3000
|
// Boxed locals' slots hold their capture BOX (a ptr); captured
|
|
2905
3001
|
// (env-borrowed) locals bind the incoming box below. A caught-typed
|
|
@@ -2949,7 +3045,8 @@ export class LlEmitter {
|
|
|
2949
3045
|
// params (callees own their params — callers passed +1). Boxed params
|
|
2950
3046
|
// allocate the shared binding and move the raw value in.
|
|
2951
3047
|
const fnScope = [];
|
|
2952
|
-
|
|
3048
|
+
const borrowed = this.callLifetimes.borrowed.get(fn.name);
|
|
3049
|
+
for (const [index, p] of fn.params.entries()) {
|
|
2953
3050
|
const local = this.currentLocals.get(p.localId);
|
|
2954
3051
|
const slot = `%${mangleLocal(p.localId)}`;
|
|
2955
3052
|
if (local.boxed) {
|
|
@@ -2961,7 +3058,8 @@ export class LlEmitter {
|
|
|
2961
3058
|
continue;
|
|
2962
3059
|
}
|
|
2963
3060
|
B.line(`store ${this.llType(p.type)} %p_${mangleLocal(p.localId)}, ptr ${slot}`);
|
|
2964
|
-
|
|
3061
|
+
this.storeIntegerView(p.localId, { name: `%p_${mangleLocal(p.localId)}`, type: p.type });
|
|
3062
|
+
if (isRefCounted(p.type) && !borrowed?.has(index))
|
|
2965
3063
|
fnScope.push({ slot, type: p.type });
|
|
2966
3064
|
}
|
|
2967
3065
|
this.scopes.push(fnScope);
|
|
@@ -3043,7 +3141,38 @@ export class LlEmitter {
|
|
|
3043
3141
|
const ret = this.llType(fn.returnType);
|
|
3044
3142
|
const attrs = coro !== null ? "#1" : FN_ATTRS;
|
|
3045
3143
|
const debug = this.debugScope === null ? "" : ` !dbg ${this.debugScope}`;
|
|
3046
|
-
|
|
3144
|
+
const symbol = borrowed ? mangleBorrowedFunction(fn.name) : mangleFunction(fn.name);
|
|
3145
|
+
const body = `define internal ${ret} @${symbol}(${params.join(", ")}) ${attrs}${debug} { ; ${fn.name}\n${B.render()}\n}`;
|
|
3146
|
+
return borrowed ? body + "\n" + this.emitOwnedCallAdapter(fn, borrowed) : body;
|
|
3147
|
+
}
|
|
3148
|
+
/** Keep the ordinary owned ABI for closures, virtual dispatch, generated
|
|
3149
|
+
* runtime adapters and library exports. Only direct IR calls select the
|
|
3150
|
+
* borrowing body. Ownership of other parameters moves into that body;
|
|
3151
|
+
* borrowed parameters are released here on normal and exceptional exits.
|
|
3152
|
+
* The pending exception remains for the adapter's caller to handle. */
|
|
3153
|
+
emitOwnedCallAdapter(fn, borrowed) {
|
|
3154
|
+
const B = new BlockBuilder();
|
|
3155
|
+
this.B = B;
|
|
3156
|
+
const params = fn.params.map((param, index) => `${this.llType(param.type)} %p${index}`);
|
|
3157
|
+
const ret = this.llType(fn.returnType);
|
|
3158
|
+
const call = `call ${ret} @${mangleBorrowedFunction(fn.name)}(${params.join(", ")})`;
|
|
3159
|
+
B.line(ret === "void" ? call : `%result = ${call}`);
|
|
3160
|
+
for (const index of borrowed)
|
|
3161
|
+
this.releaseValue(`%p${index}`, fn.params[index].type);
|
|
3162
|
+
B.terminate(ret === "void" ? "ret void" : `ret ${ret} %result`);
|
|
3163
|
+
return `define internal ${ret} @${mangleFunction(fn.name)}(${params.join(", ")}) ${FN_ATTRS} {\n${B.render()}\n}`;
|
|
3164
|
+
}
|
|
3165
|
+
/** A local stable binding keeps its value alive while later arguments
|
|
3166
|
+
* and the callee execute. Global, captured, TDZ and mutable bindings need
|
|
3167
|
+
* an owned snapshot, even when the callee only inspects the value. */
|
|
3168
|
+
canBorrowCallArgument(value) {
|
|
3169
|
+
if (value.kind === "strLit")
|
|
3170
|
+
return true;
|
|
3171
|
+
if (value.kind !== "varRef")
|
|
3172
|
+
return false;
|
|
3173
|
+
const binding = this.binding(value.localId);
|
|
3174
|
+
return binding.kind === "local" && binding.local !== undefined &&
|
|
3175
|
+
(!binding.local.mutable || this.stableCallBindings.has(value.localId) || this.borrowedParameters.has(value.localId)) && !binding.local.boxed && !binding.local.tdz;
|
|
3047
3176
|
}
|
|
3048
3177
|
// ── statements ──────────────────────────────────────────────────────────
|
|
3049
3178
|
emitStmts(stmts) {
|
|
@@ -3087,6 +3216,31 @@ export class LlEmitter {
|
|
|
3087
3216
|
switch (s.kind) {
|
|
3088
3217
|
case "varDecl": {
|
|
3089
3218
|
const b = this.binding(s.localId);
|
|
3219
|
+
const localUnion = this.localStackUnions.get(s.localId);
|
|
3220
|
+
if (localUnion) {
|
|
3221
|
+
const union = emitStackUnion(this, localUnion);
|
|
3222
|
+
B.line(`store ptr ${union.value.name}, ptr ${b.slot}`);
|
|
3223
|
+
if (union.payload) {
|
|
3224
|
+
this.moveTemp(union.payload);
|
|
3225
|
+
this.scopes[this.scopes.length - 1].push({ slot: union.payloadSlot, type: union.payload.type });
|
|
3226
|
+
}
|
|
3227
|
+
break;
|
|
3228
|
+
}
|
|
3229
|
+
const localRead = this.localArrayReads.get(s.localId);
|
|
3230
|
+
if (localRead) {
|
|
3231
|
+
const owner = emitLocalArrayRead(this, localRead, b.slot);
|
|
3232
|
+
if (owner)
|
|
3233
|
+
this.scopes[this.scopes.length - 1].push(owner);
|
|
3234
|
+
break;
|
|
3235
|
+
}
|
|
3236
|
+
const mapRead = this.mapReadLifetimes.locals.get(s.localId);
|
|
3237
|
+
if (mapRead) {
|
|
3238
|
+
const result = emitStackMapRead(this, mapRead);
|
|
3239
|
+
B.line(`store ptr ${result.value.name}, ptr ${b.slot}`);
|
|
3240
|
+
if (result.owner)
|
|
3241
|
+
this.scopes[this.scopes.length - 1].push(result.owner);
|
|
3242
|
+
break;
|
|
3243
|
+
}
|
|
3090
3244
|
if (b.kind === "boxed") {
|
|
3091
3245
|
// Box FIRST, then evaluate the initializer: a named function
|
|
3092
3246
|
// expression's closure captures this box during init evaluation.
|
|
@@ -3124,9 +3278,20 @@ export class LlEmitter {
|
|
|
3124
3278
|
}
|
|
3125
3279
|
break;
|
|
3126
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
|
+
}
|
|
3127
3291
|
const v = this.emitExpr(s.init);
|
|
3128
3292
|
this.moveTemp(v);
|
|
3129
3293
|
B.line(`store ${this.llType(b.type)} ${v.name}, ptr ${b.slot}`);
|
|
3294
|
+
this.storeIntegerView(s.localId, v, s.init);
|
|
3130
3295
|
if (isRefCounted(b.type)) {
|
|
3131
3296
|
this.scopes[this.scopes.length - 1].push({ slot: b.slot, type: b.type });
|
|
3132
3297
|
}
|
|
@@ -3157,6 +3322,7 @@ export class LlEmitter {
|
|
|
3157
3322
|
this.releaseValue(old, b.type);
|
|
3158
3323
|
}
|
|
3159
3324
|
B.line(`store ${this.llType(b.type)} ${v.name}, ptr ${b.slot}`);
|
|
3325
|
+
this.storeIntegerView(s.localId, v, s.value);
|
|
3160
3326
|
break;
|
|
3161
3327
|
}
|
|
3162
3328
|
case "exprStmt":
|
|
@@ -3166,7 +3332,7 @@ export class LlEmitter {
|
|
|
3166
3332
|
// Evaluation order matches JS: array, index, then value. Ownership
|
|
3167
3333
|
// of a refcounted value moves into the array (the runtime releases
|
|
3168
3334
|
// the replaced element itself).
|
|
3169
|
-
const arr = this
|
|
3335
|
+
const arr = emitBorrowedInput(this, s.arr);
|
|
3170
3336
|
const idx = this.emitExpr(s.index);
|
|
3171
3337
|
const v = this.emitExpr(s.value);
|
|
3172
3338
|
if (s.arr.type.kind !== "array")
|
|
@@ -3180,7 +3346,7 @@ export class LlEmitter {
|
|
|
3180
3346
|
break;
|
|
3181
3347
|
}
|
|
3182
3348
|
case "arraySetLength": {
|
|
3183
|
-
const arr = this
|
|
3349
|
+
const arr = emitBorrowedInput(this, s.arr);
|
|
3184
3350
|
const length = this.emitExpr(s.length);
|
|
3185
3351
|
if (s.arr.type.kind !== "array")
|
|
3186
3352
|
throw new InternalCompilerError("llvm emitter bug: arraySetLength on non-array");
|
|
@@ -3191,7 +3357,7 @@ export class LlEmitter {
|
|
|
3191
3357
|
}
|
|
3192
3358
|
case "arraySetUndefined":
|
|
3193
3359
|
case "arrayDelete": {
|
|
3194
|
-
const arr = this
|
|
3360
|
+
const arr = emitBorrowedInput(this, s.arr);
|
|
3195
3361
|
const idx = this.emitExpr(s.index);
|
|
3196
3362
|
if (s.arr.type.kind !== "array")
|
|
3197
3363
|
throw new InternalCompilerError(`llvm emitter bug: ${s.kind} on non-array`);
|
|
@@ -3210,12 +3376,11 @@ export class LlEmitter {
|
|
|
3210
3376
|
// IrBytesElem is static, so never rediscover it through the
|
|
3211
3377
|
// generic runtime switch in a hot loop.
|
|
3212
3378
|
const arr = this.emitStableReceiver(s.arr, [s.index, s.value]);
|
|
3213
|
-
const
|
|
3214
|
-
const idx = integerIndex ?? this.emitExpr(s.index).name;
|
|
3379
|
+
const idx = this.emitExpr(s.index);
|
|
3215
3380
|
const v = this.emitExpr(s.value);
|
|
3216
3381
|
if (s.arr.type.kind !== "bytes")
|
|
3217
3382
|
throw new InternalCompilerError("llvm emitter bug: bytesSet on non-bytes");
|
|
3218
|
-
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));
|
|
3219
3384
|
break;
|
|
3220
3385
|
}
|
|
3221
3386
|
case "fieldSet":
|
|
@@ -3225,7 +3390,7 @@ export class LlEmitter {
|
|
|
3225
3390
|
// release — a release can trigger a cycle collection, which must
|
|
3226
3391
|
// never see a heap edge whose count was already given up).
|
|
3227
3392
|
// Classes and records share the struct layout, so one emission.
|
|
3228
|
-
const obj = this.emitExpr(s.obj);
|
|
3393
|
+
const obj = isRefCounted(s.value.type) ? this.emitExpr(s.obj) : this.emitStableReceiver(s.obj, [s.value]);
|
|
3229
3394
|
const v = this.emitExpr(s.value);
|
|
3230
3395
|
const { ptr, type } = s.kind === "fieldSet"
|
|
3231
3396
|
? this.classFieldPtr(obj.name, s.className, s.field)
|
|
@@ -3486,14 +3651,51 @@ export class LlEmitter {
|
|
|
3486
3651
|
case "for": {
|
|
3487
3652
|
// The init's scope wraps the whole loop (break/continue must NOT
|
|
3488
3653
|
// release it — scopeDepth captured after the push, C parity).
|
|
3489
|
-
const
|
|
3654
|
+
const integerArrayLoop = this.debug === null ? matchIntegerArrayForLoop(s, this.currentLocals, this.integerArrayBindings) : null;
|
|
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
|
+
}
|
|
3490
3673
|
this.scopes.push([]);
|
|
3491
3674
|
let integerSlot = null;
|
|
3675
|
+
let countedLimit = null;
|
|
3492
3676
|
if (integerLoop) {
|
|
3493
3677
|
integerSlot = B.slot();
|
|
3494
3678
|
B.entryAllocas.push(`${integerSlot} = alloca ${this.sizeType} ; integer induction ${this.currentLocals.get(integerLoop.localId).name}`);
|
|
3495
|
-
|
|
3496
|
-
|
|
3679
|
+
let start = "0";
|
|
3680
|
+
if (integerArrayLoop && s.init?.kind === "varDecl" && s.init.init) {
|
|
3681
|
+
const value = this.emitExpr(s.init.init);
|
|
3682
|
+
start = B.tmp();
|
|
3683
|
+
B.line(`${start} = fptoui double ${value.name} to ${this.sizeType}`);
|
|
3684
|
+
this.integerArrayBindings.add(integerLoop.localId);
|
|
3685
|
+
}
|
|
3686
|
+
B.line(`store ${this.sizeType} ${start}, ptr ${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);
|
|
3497
3699
|
}
|
|
3498
3700
|
else if (s.init) {
|
|
3499
3701
|
// A multi-declarator head shares the loop's scope. Emitting its
|
|
@@ -3540,12 +3742,21 @@ export class LlEmitter {
|
|
|
3540
3742
|
const len = B.tmp();
|
|
3541
3743
|
const index = B.tmp();
|
|
3542
3744
|
const inBounds = B.tmp();
|
|
3543
|
-
B.line(`${lenPtr} = getelementptr inbounds
|
|
3544
|
-
|
|
3745
|
+
B.line(`${lenPtr} = getelementptr inbounds %${integerArrayLoop ? "ScrArr" : "ScrBytes"}, ptr ${receiver.name}, i64 0, i32 1`);
|
|
3746
|
+
if (integerArrayLoop)
|
|
3747
|
+
this.markMemoryPointer(lenPtr, "array:header");
|
|
3748
|
+
B.line(`${len} = load ${this.sizeType}, ptr ${lenPtr}${this.fieldAliasAttachment(lenPtr)}`);
|
|
3545
3749
|
B.line(`${index} = load ${this.sizeType}, ptr ${integerSlot}`);
|
|
3546
3750
|
B.line(`${inBounds} = icmp ult ${this.sizeType} ${index}, ${len}`);
|
|
3547
3751
|
B.condBr(inBounds, lb, le);
|
|
3548
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
|
+
}
|
|
3549
3760
|
else if (s.cond)
|
|
3550
3761
|
B.condBr(this.emitCondition(s.cond), lb, le);
|
|
3551
3762
|
else
|
|
@@ -3573,6 +3784,13 @@ export class LlEmitter {
|
|
|
3573
3784
|
B.line(`${next} = add nuw ${this.sizeType} ${old}, 1`);
|
|
3574
3785
|
B.line(`store ${this.sizeType} ${next}, ptr ${integerSlot}`);
|
|
3575
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
|
+
}
|
|
3576
3794
|
else if (s.update)
|
|
3577
3795
|
this.emitStmt(s.update);
|
|
3578
3796
|
B.br(lc);
|
|
@@ -3581,6 +3799,14 @@ export class LlEmitter {
|
|
|
3581
3799
|
this.releaseScope(this.scopes.pop());
|
|
3582
3800
|
if (integerLoop)
|
|
3583
3801
|
this.integerLoopBindings.delete(integerLoop.localId);
|
|
3802
|
+
if (countedLoop)
|
|
3803
|
+
this.integerLoopBindings.delete(countedLoop.localId);
|
|
3804
|
+
if (integerArrayLoop)
|
|
3805
|
+
this.integerArrayBindings.delete(integerArrayLoop.localId);
|
|
3806
|
+
if (countedJoin) {
|
|
3807
|
+
B.br(countedJoin);
|
|
3808
|
+
B.startBlock(countedJoin);
|
|
3809
|
+
}
|
|
3584
3810
|
break;
|
|
3585
3811
|
}
|
|
3586
3812
|
case "forOf": {
|
|
@@ -4066,12 +4292,15 @@ export class LlEmitter {
|
|
|
4066
4292
|
* branch and moves the result into `slot`: the chosen value's ownership
|
|
4067
4293
|
* transfers, every other temp the arm allocated releases inside the
|
|
4068
4294
|
* branch. The shared core of ternary/logical. */
|
|
4069
|
-
emitBranchInto(slot, expr) {
|
|
4295
|
+
emitBranchInto(slot, expr, integerSlot) {
|
|
4070
4296
|
this.frames.push([]);
|
|
4071
4297
|
const v = this.emitExpr(expr);
|
|
4072
4298
|
this.moveTemp(v);
|
|
4073
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}`);
|
|
4074
4302
|
this.releaseFrame(this.frames.pop());
|
|
4303
|
+
return v.uint32 !== undefined;
|
|
4075
4304
|
}
|
|
4076
4305
|
// ── expressions ─────────────────────────────────────────────────────────
|
|
4077
4306
|
emitLiteralExpr(e) {
|
|
@@ -4083,7 +4312,7 @@ export class LlEmitter {
|
|
|
4083
4312
|
// to this expression's frame so they survive those reads and still
|
|
4084
4313
|
// die on the path that created them, including inside lazy branches.
|
|
4085
4314
|
this.scopes.push([]);
|
|
4086
|
-
const result = emitOperatorExpr(this, e);
|
|
4315
|
+
const result = emitBorrowedFieldSequence(this, e) ?? emitOperatorExpr(this, e);
|
|
4087
4316
|
for (const local of this.scopes.pop()) {
|
|
4088
4317
|
this.currentFrame().push({ name: local.slot, type: local.type, slot: true, ...(local.boxed ? { boxed: true } : {}) });
|
|
4089
4318
|
}
|
|
@@ -4170,6 +4399,8 @@ export class LlEmitter {
|
|
|
4170
4399
|
emitReadReceiver(e) {
|
|
4171
4400
|
if (!isRefCounted(e.type))
|
|
4172
4401
|
return this.emitExpr(e);
|
|
4402
|
+
if (e.kind === "strLit")
|
|
4403
|
+
return { name: this.internLiteral(e.value), type: e.type };
|
|
4173
4404
|
if (e.kind === "varRef") {
|
|
4174
4405
|
const binding = this.binding(e.localId);
|
|
4175
4406
|
// Capture boxes can carry TDZ and caught-value conversion semantics.
|
|
@@ -4192,8 +4423,43 @@ export class LlEmitter {
|
|
|
4192
4423
|
: this.classFieldPtr(receiver.name, e.className, e.field);
|
|
4193
4424
|
return { name: this.loadField(ptr, type), type: e.type };
|
|
4194
4425
|
}
|
|
4426
|
+
if (e.kind === "ternary" && this.canBorrowReceiver(e.then) && this.canBorrowReceiver(e.else_)) {
|
|
4427
|
+
// Checked projections lower to a ternary with an always-throwing
|
|
4428
|
+
// arm. Keep that check at the use, but borrow the successful arm.
|
|
4429
|
+
const B = this.B;
|
|
4430
|
+
const cond = this.emitExpr(e.cond);
|
|
4431
|
+
const slot = B.slot();
|
|
4432
|
+
B.entryAllocas.push(`${slot} = alloca ptr`);
|
|
4433
|
+
const yes = B.newLabel("read.then"), no = B.newLabel("read.else"), join = B.newLabel("read.join");
|
|
4434
|
+
B.condBr(cond.name, yes, no);
|
|
4435
|
+
for (const [label, value] of [[yes, e.then], [no, e.else_]]) {
|
|
4436
|
+
B.startBlock(label);
|
|
4437
|
+
this.frames.push([]);
|
|
4438
|
+
const result = this.emitReadReceiver(value);
|
|
4439
|
+
B.line(`store ptr ${result.name}, ptr ${slot}`);
|
|
4440
|
+
this.releaseFrame(this.frames.pop());
|
|
4441
|
+
B.br(join);
|
|
4442
|
+
}
|
|
4443
|
+
B.startBlock(join);
|
|
4444
|
+
const value = B.tmp();
|
|
4445
|
+
B.line(`${value} = load ptr, ptr ${slot}`);
|
|
4446
|
+
return { name: value, type: e.type };
|
|
4447
|
+
}
|
|
4195
4448
|
return this.emitExpr(e);
|
|
4196
4449
|
}
|
|
4450
|
+
canBorrowReceiver(e) {
|
|
4451
|
+
switch (e.kind) {
|
|
4452
|
+
case "varRef": return this.binding(e.localId).kind !== "boxed";
|
|
4453
|
+
case "unionNarrow": return this.canBorrowReceiver(e.value);
|
|
4454
|
+
case "fieldGet":
|
|
4455
|
+
case "recordGet": return this.canBorrowReceiver(e.obj);
|
|
4456
|
+
case "ternary": return this.canBorrowReceiver(e.then) && this.canBorrowReceiver(e.else_);
|
|
4457
|
+
// The runtime always sets a pending exception; its unreachable
|
|
4458
|
+
// typed dummy is null and owns no receiver.
|
|
4459
|
+
case "libCall": return e.fn === "error.nodeThrow";
|
|
4460
|
+
default: return false;
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4197
4463
|
emitExpr(e) {
|
|
4198
4464
|
return emitExpr(this, e);
|
|
4199
4465
|
}
|
|
@@ -4311,8 +4577,8 @@ export class LlEmitter {
|
|
|
4311
4577
|
emitIntegerLoopIndex(expr) {
|
|
4312
4578
|
return emitIntegerLoopIndex(this, expr);
|
|
4313
4579
|
}
|
|
4314
|
-
emitBytesIndex(receiver, index,
|
|
4315
|
-
return emitBytesIndex(this, receiver, index,
|
|
4580
|
+
emitBytesIndex(receiver, index, expr, inBounds = false) {
|
|
4581
|
+
return emitBytesIndex(this, receiver, index, expr, undefined, inBounds);
|
|
4316
4582
|
}
|
|
4317
4583
|
emitBytesData(receiver) {
|
|
4318
4584
|
return emitBytesData(this, receiver);
|
|
@@ -4320,14 +4586,21 @@ export class LlEmitter {
|
|
|
4320
4586
|
emitBytesLength(elem, receiver, bytes) {
|
|
4321
4587
|
return emitBytesLength(this, elem, receiver, bytes);
|
|
4322
4588
|
}
|
|
4323
|
-
emitBytesGet(elem, receiver, index,
|
|
4324
|
-
return emitBytesGet(this, elem, receiver, index,
|
|
4589
|
+
emitBytesGet(elem, receiver, index, expr, inBounds = false) {
|
|
4590
|
+
return emitBytesGet(this, elem, receiver, index, expr, inBounds);
|
|
4325
4591
|
}
|
|
4326
|
-
emitToUint32(value, expr) {
|
|
4327
|
-
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}`);
|
|
4328
4601
|
}
|
|
4329
|
-
emitBytesSet(elem, receiver, index, value,
|
|
4330
|
-
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);
|
|
4331
4604
|
}
|
|
4332
4605
|
emitBytesIntrinsic(e) {
|
|
4333
4606
|
return emitBytesIntrinsic(this, e);
|