@scriptc/compiler 0.2.2 → 0.2.3
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-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/call-lifetimes.d.ts +20 -0
- package/dist/backend/llvm/call-lifetimes.js +179 -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 +205 -0
- package/dist/backend/llvm/call-lifetimes.test.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 +33 -0
- package/dist/backend/llvm/emitter.js +189 -16
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +20 -1
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +1 -1
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js +42 -5
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +70 -13
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +1 -1
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +6 -5
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +1 -1
- 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/lib-shared.js +35 -1
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +44 -0
- package/dist/backend/llvm/local-array-reads.js +313 -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 +123 -0
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -0
- package/dist/backend/llvm/shapes.js +1 -0
- 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 +241 -0
- package/dist/backend/llvm/stack-unions.test.js.map +1 -0
- package/dist/backend/llvm/string-lifetimes.d.ts +12 -0
- package/dist/backend/llvm/string-lifetimes.js +70 -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 +230 -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 +215 -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/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 +150 -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 +4 -4
- 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 +40 -3
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +35 -41
- 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 +50 -22
- 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 +6 -0
- 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 +1 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/integer-loops.d.ts +7 -0
- package/dist/ir/integer-loops.js +23 -0
- 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 +41 -0
- package/dist/ir/integer-loops.test.js.map +1 -0
- package/dist/ir/ir.d.ts +21 -17
- package/dist/ir/ir.js +43 -9
- 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 +161 -76
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +227 -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/package.json +20 -20
- package/surface-manifest.json +271 -17
|
@@ -2,7 +2,10 @@ import type { IrBytesElem, IrExpr, IrFfiImport, IrFunction, IrLocal, IrModule, I
|
|
|
2
2
|
import { type IntegerRanges } from "../../ir/integer-ranges.js";
|
|
3
3
|
import { type ConstantNumericTable } from "../../ir/constant-tables.js";
|
|
4
4
|
import { type FfiCallbackAdapter } from "../ffi-callbacks.js";
|
|
5
|
+
import { type CallLifetimes } from "./call-lifetimes.js";
|
|
5
6
|
import { BlockBuilder } from "./blocks.js";
|
|
7
|
+
import { type LocalArrayRead } from "./local-array-reads.js";
|
|
8
|
+
import { type MapReadLifetimes } from "./map-read-lifetimes.js";
|
|
6
9
|
import { type LlClassMeta } from "./classes.js";
|
|
7
10
|
import { LlDyn, type DynHost } from "./dyn.js";
|
|
8
11
|
import { LlWalkers } from "./walkers.js";
|
|
@@ -29,6 +32,19 @@ export declare function emitLlvmModule(mod: IrModule, options?: LlvmTargetOption
|
|
|
29
32
|
export declare function emitLlvmModuleSource(mod: IrModule, options?: LlvmTargetOptions): string | readonly string[];
|
|
30
33
|
export declare class LlEmitter {
|
|
31
34
|
readonly mod: IrModule;
|
|
35
|
+
localArrayReads: Map<string, LocalArrayRead>;
|
|
36
|
+
private localStackUnions;
|
|
37
|
+
integerArrayBindings: Set<string>;
|
|
38
|
+
private readonly fieldAliasTags;
|
|
39
|
+
private readonly fieldPointerTags;
|
|
40
|
+
/** Native class fields with different property names cannot occupy the
|
|
41
|
+
* same slot of one object. Inherited views of the same property share a
|
|
42
|
+
* tag. Array storage has its own disjoint categories; untagged runtime
|
|
43
|
+
* operations and record views conservatively alias all of them. */
|
|
44
|
+
private markFieldPointer;
|
|
45
|
+
markMemoryPointer(ptr: string, field: string): void;
|
|
46
|
+
fieldAliasAttachment(ptr: string): string;
|
|
47
|
+
private fieldAliasMetadata;
|
|
32
48
|
private readonly debug;
|
|
33
49
|
private debugScope;
|
|
34
50
|
readonly sizeType: "i32" | "i64";
|
|
@@ -84,6 +100,12 @@ export declare class LlEmitter {
|
|
|
84
100
|
needsBadKey: boolean;
|
|
85
101
|
private needsRetainBox;
|
|
86
102
|
readonly fnByName: Map<string, IrFunction>;
|
|
103
|
+
private readonly arrayPreservingFunctions;
|
|
104
|
+
private readonly optionalArrayReads;
|
|
105
|
+
callArrayReads: Map<IrExpr, LocalArrayRead>;
|
|
106
|
+
mapReadLifetimes: MapReadLifetimes;
|
|
107
|
+
readonly callLifetimes: CallLifetimes;
|
|
108
|
+
private borrowedParameters;
|
|
87
109
|
/** Manifest-bound native imports, used by ffiCall emission. */
|
|
88
110
|
readonly ffiByName: Map<string, IrFfiImport>;
|
|
89
111
|
/** C-ABI callback trampolines and (for raw/no-userdata callbacks) their
|
|
@@ -412,6 +434,16 @@ export declare class LlEmitter {
|
|
|
412
434
|
/** Move a clean async return value into the current fiber's promise. */
|
|
413
435
|
private emitWasiFulfill;
|
|
414
436
|
private emitFunction;
|
|
437
|
+
/** Keep the ordinary owned ABI for closures, virtual dispatch, generated
|
|
438
|
+
* runtime adapters and library exports. Only direct IR calls select the
|
|
439
|
+
* borrowing body. Ownership of other parameters moves into that body;
|
|
440
|
+
* borrowed parameters are released here on normal and exceptional exits.
|
|
441
|
+
* The pending exception remains for the adapter's caller to handle. */
|
|
442
|
+
private emitOwnedCallAdapter;
|
|
443
|
+
/** A local immutable binding keeps its value alive while later arguments
|
|
444
|
+
* and the callee execute. Global, captured, TDZ and mutable bindings need
|
|
445
|
+
* an owned snapshot, even when the callee only inspects the value. */
|
|
446
|
+
canBorrowCallArgument(value: IrExpr): boolean;
|
|
415
447
|
private emitStmts;
|
|
416
448
|
/** Emits a block in its own lexical scope (refcounted locals released at
|
|
417
449
|
* end). `setup` runs after the
|
|
@@ -487,6 +519,7 @@ export declare class LlEmitter {
|
|
|
487
519
|
* their ordinary statement-frame ownership. The caller must retain any
|
|
488
520
|
* reference result before evaluating another expression. */
|
|
489
521
|
emitReadReceiver(e: IrExpr): LlValue;
|
|
522
|
+
canBorrowReceiver(e: IrExpr): boolean;
|
|
490
523
|
emitExpr(e: IrExpr): LlValue;
|
|
491
524
|
emitJsMarshal(e: IrExpr & {
|
|
492
525
|
kind: "jsMarshal";
|
|
@@ -22,7 +22,7 @@ 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 } from "../../ir/integer-loops.js";
|
|
26
26
|
import { scalarizeNumericRecords } from "../../ir/scalar-records.js";
|
|
27
27
|
import { everyStmtList } from "../../ir/traverse.js";
|
|
28
28
|
import { analyzeIntegerRanges } from "../../ir/integer-ranges.js";
|
|
@@ -30,8 +30,13 @@ import { findConstantNumericTables } from "../../ir/constant-tables.js";
|
|
|
30
30
|
import { allocateFfiCallbackAdapters, hasForeignFfiCallback, hasRetainedFfiCallback } from "../ffi-callbacks.js";
|
|
31
31
|
import { RUNTIME_ABI_MARKER } from "../runtime-abi.js";
|
|
32
32
|
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";
|
|
33
|
+
import { mangleArgPack, mangleAsyncSpawn, mangleBorrowedFunction, mangleClassObj, mangleClassStruct, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordStruct, mangleTrampoline, mangleWrapper, mangleVtInstance } from "../mangle.js";
|
|
34
|
+
import { analyzeCallLifetimes } from "./call-lifetimes.js";
|
|
35
|
+
import { emitStackUnion, findLocalStackUnions } from "./stack-unions.js";
|
|
34
36
|
import { BlockBuilder } from "./blocks.js";
|
|
37
|
+
import { emitLocalArrayRead, findArrayPreservingFunctions, findLocalArrayReads, findCallArrayReads, OptionalArrayReads } from "./local-array-reads.js";
|
|
38
|
+
import { emitStackMapRead, findMapReadLifetimes } from "./map-read-lifetimes.js";
|
|
39
|
+
import { emitBorrowedFieldSequence } from "./borrowed-receivers.js";
|
|
35
40
|
import { LlvmDebugInfo } from "./debug-info.js";
|
|
36
41
|
import { f64Lit, ffiNativeTypeLl, ffiNativeParamLl, ffiNativeReturnLl, llvmCommentText } from "./common.js";
|
|
37
42
|
import { ffiExtendsNarrowIntegers } from "../targets.js";
|
|
@@ -101,6 +106,41 @@ function llStrBytes(text) {
|
|
|
101
106
|
}
|
|
102
107
|
export class LlEmitter {
|
|
103
108
|
mod;
|
|
109
|
+
localArrayReads = new Map();
|
|
110
|
+
localStackUnions = new Map();
|
|
111
|
+
integerArrayBindings = new Set();
|
|
112
|
+
fieldAliasTags = new Map();
|
|
113
|
+
fieldPointerTags = new Map();
|
|
114
|
+
/** Native class fields with different property names cannot occupy the
|
|
115
|
+
* same slot of one object. Inherited views of the same property share a
|
|
116
|
+
* tag. Array storage has its own disjoint categories; untagged runtime
|
|
117
|
+
* operations and record views conservatively alias all of them. */
|
|
118
|
+
markFieldPointer(ptr, field) {
|
|
119
|
+
this.markMemoryPointer(ptr, `field:${field}`);
|
|
120
|
+
}
|
|
121
|
+
markMemoryPointer(ptr, field) {
|
|
122
|
+
if (this.debug !== null)
|
|
123
|
+
return;
|
|
124
|
+
let tag = this.fieldAliasTags.get(field);
|
|
125
|
+
if (tag === undefined) {
|
|
126
|
+
tag = 2 + this.fieldAliasTags.size * 2;
|
|
127
|
+
this.fieldAliasTags.set(field, tag);
|
|
128
|
+
}
|
|
129
|
+
this.fieldPointerTags.set(ptr, tag);
|
|
130
|
+
}
|
|
131
|
+
fieldAliasAttachment(ptr) {
|
|
132
|
+
const tag = this.fieldPointerTags.get(ptr);
|
|
133
|
+
return tag === undefined ? "" : `, !tbaa !${tag}`;
|
|
134
|
+
}
|
|
135
|
+
fieldAliasMetadata() {
|
|
136
|
+
if (this.fieldAliasTags.size === 0)
|
|
137
|
+
return [];
|
|
138
|
+
const lines = ['!0 = !{!"scriptc native fields"}'];
|
|
139
|
+
for (const tag of this.fieldAliasTags.values()) {
|
|
140
|
+
lines.push(`!${tag - 1} = !{!"field.${tag}", !0, i64 0}`, `!${tag} = !{!${tag - 1}, !${tag - 1}, i64 0}`);
|
|
141
|
+
}
|
|
142
|
+
return lines;
|
|
143
|
+
}
|
|
104
144
|
debug;
|
|
105
145
|
debugScope = null;
|
|
106
146
|
sizeType;
|
|
@@ -149,6 +189,12 @@ export class LlEmitter {
|
|
|
149
189
|
needsBadKey = false;
|
|
150
190
|
needsRetainBox = false;
|
|
151
191
|
fnByName = new Map();
|
|
192
|
+
arrayPreservingFunctions;
|
|
193
|
+
optionalArrayReads;
|
|
194
|
+
callArrayReads = new Map();
|
|
195
|
+
mapReadLifetimes = { locals: new Map(), arguments: new Map() };
|
|
196
|
+
callLifetimes;
|
|
197
|
+
borrowedParameters = new Set();
|
|
152
198
|
/** Manifest-bound native imports, used by ffiCall emission. */
|
|
153
199
|
ffiByName = new Map();
|
|
154
200
|
/** C-ABI callback trampolines and (for raw/no-userdata callbacks) their
|
|
@@ -298,6 +344,9 @@ export class LlEmitter {
|
|
|
298
344
|
}
|
|
299
345
|
for (const u of mod.unions ?? [])
|
|
300
346
|
this.unionsById.set(u.id, u);
|
|
347
|
+
this.optionalArrayReads = new OptionalArrayReads(this.fnByName, this.unionsById);
|
|
348
|
+
this.arrayPreservingFunctions = findArrayPreservingFunctions(this.fnByName, this.unionsById, this.optionalArrayReads);
|
|
349
|
+
this.callLifetimes = analyzeCallLifetimes(this.fnByName);
|
|
301
350
|
for (const r of mod.records ?? [])
|
|
302
351
|
this.recordsById.set(r.id, r);
|
|
303
352
|
const traced = computeTraced(mod);
|
|
@@ -849,6 +898,7 @@ export class LlEmitter {
|
|
|
849
898
|
this.declare(`declare void @scr_net_island_install()`);
|
|
850
899
|
if (usesIsland) {
|
|
851
900
|
this.declare(`declare void @scr_island_modules(ptr, ${this.sizeType}, ptr, ${this.sizeType})`);
|
|
901
|
+
this.declare(`declare void @scr_island_entry_module(ptr)`);
|
|
852
902
|
this.declare(`declare i32 @scr_island_exit_code()`);
|
|
853
903
|
if (moduleEmbedsCompressedNpm(this.mod)) {
|
|
854
904
|
this.declare(`declare void @scr_island_set_inflate(ptr)`);
|
|
@@ -1116,6 +1166,7 @@ export class LlEmitter {
|
|
|
1116
1166
|
}
|
|
1117
1167
|
if (this.templateStringsInstances.size > 0)
|
|
1118
1168
|
out.push(``);
|
|
1169
|
+
const islandEntryPath = usesIsland ? this.cstr(this.mod.sourceFile) : null;
|
|
1119
1170
|
for (const [text, c] of this.cstrs) {
|
|
1120
1171
|
// NUL-terminated byte-array constants for scr_jb_puts and indentation.
|
|
1121
1172
|
out.push(`@${c.sym} = internal constant [${c.len + 1} x i8] c"${llStrBytes(text)}"`);
|
|
@@ -1247,6 +1298,7 @@ export class LlEmitter {
|
|
|
1247
1298
|
if (hasNoInlineRecordClone)
|
|
1248
1299
|
out.push(`attributes #2 = { noinline sanitize_address }`);
|
|
1249
1300
|
out.push(``);
|
|
1301
|
+
out.push(...this.fieldAliasMetadata());
|
|
1250
1302
|
if (this.debug !== null)
|
|
1251
1303
|
out.push(this.debug.render());
|
|
1252
1304
|
return out;
|
|
@@ -1260,6 +1312,7 @@ export class LlEmitter {
|
|
|
1260
1312
|
// scr_watch.c links only when this line is emitted.
|
|
1261
1313
|
...(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
1314
|
? [
|
|
1315
|
+
` call void @scr_island_entry_module(ptr ${islandEntryPath})`,
|
|
1263
1316
|
...(moduleEmbedsCompressedNpm(this.mod)
|
|
1264
1317
|
? [` call void @scr_island_set_inflate(ptr @scr_zlib_inflate_exact)`]
|
|
1265
1318
|
: []),
|
|
@@ -1359,6 +1412,7 @@ export class LlEmitter {
|
|
|
1359
1412
|
// lane's -fsanitize=address link activates instrumentation over the
|
|
1360
1413
|
// emitted functions too (the runtime TUs get theirs from clang).
|
|
1361
1414
|
`attributes #0 = { sanitize_address }`, ...(this.wasi ? [`attributes #1 = { sanitize_address presplitcoroutine }`] : []), ...(hasNoInlineRecordClone ? [`attributes #2 = { noinline sanitize_address }`] : []), ``);
|
|
1415
|
+
out.push(...this.fieldAliasMetadata());
|
|
1362
1416
|
if (this.debug !== null)
|
|
1363
1417
|
out.push(this.debug.render());
|
|
1364
1418
|
return out;
|
|
@@ -2418,7 +2472,7 @@ export class LlEmitter {
|
|
|
2418
2472
|
* a retained peek. */
|
|
2419
2473
|
unionExtract(uName, arm) {
|
|
2420
2474
|
const B = this.B;
|
|
2421
|
-
if (arm.kind === "f64") {
|
|
2475
|
+
if (arm.kind === "f64" || arm.kind === "procStream") {
|
|
2422
2476
|
const t = B.tmp();
|
|
2423
2477
|
this.declare(`declare double @scr_union_get_f64(ptr)`);
|
|
2424
2478
|
B.line(`${t} = call double @scr_union_get_f64(ptr ${uName})`);
|
|
@@ -2438,7 +2492,7 @@ export class LlEmitter {
|
|
|
2438
2492
|
unionNewOwned(tag, v) {
|
|
2439
2493
|
const B = this.B;
|
|
2440
2494
|
const t = B.tmp();
|
|
2441
|
-
if (v.type.kind === "f64") {
|
|
2495
|
+
if (v.type.kind === "f64" || v.type.kind === "procStream") {
|
|
2442
2496
|
this.declare(`declare ptr @scr_union_new_f64(i32, double)`);
|
|
2443
2497
|
B.line(`${t} = call ptr @scr_union_new_f64(i32 ${tag}, double ${v.name})`);
|
|
2444
2498
|
return t;
|
|
@@ -2469,6 +2523,7 @@ export class LlEmitter {
|
|
|
2469
2523
|
const { index, type } = classFieldIndex(meta, field);
|
|
2470
2524
|
const p = this.B.tmp();
|
|
2471
2525
|
this.B.line(`${p} = getelementptr inbounds %${classStructSym(className)}, ptr ${objName}, i64 0, i32 ${index}`);
|
|
2526
|
+
this.markFieldPointer(p, field);
|
|
2472
2527
|
return { ptr: p, type };
|
|
2473
2528
|
}
|
|
2474
2529
|
/** `o->vt->pre` — the dynamic class's preorder number (borrowed object;
|
|
@@ -2559,7 +2614,7 @@ export class LlEmitter {
|
|
|
2559
2614
|
const B = this.B;
|
|
2560
2615
|
const fieldTy = llFieldType(t);
|
|
2561
2616
|
const raw = B.tmp();
|
|
2562
|
-
B.line(`${raw} = load ${fieldTy}, ptr ${ptr}`);
|
|
2617
|
+
B.line(`${raw} = load ${fieldTy}, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
2563
2618
|
if (fieldTy !== "i8")
|
|
2564
2619
|
return raw;
|
|
2565
2620
|
const b = B.tmp();
|
|
@@ -2571,12 +2626,12 @@ export class LlEmitter {
|
|
|
2571
2626
|
const B = this.B;
|
|
2572
2627
|
const fieldTy = llFieldType(t);
|
|
2573
2628
|
if (fieldTy !== "i8") {
|
|
2574
|
-
B.line(`store ${fieldTy} ${value}, ptr ${ptr}`);
|
|
2629
|
+
B.line(`store ${fieldTy} ${value}, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
2575
2630
|
return;
|
|
2576
2631
|
}
|
|
2577
2632
|
const z = B.tmp();
|
|
2578
2633
|
B.line(`${z} = zext i1 ${value} to i8`);
|
|
2579
|
-
B.line(`store i8 ${z}, ptr ${ptr}`);
|
|
2634
|
+
B.line(`store i8 ${z}, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
2580
2635
|
}
|
|
2581
2636
|
// ── bindings ────────────────────────────────────────────────────────────
|
|
2582
2637
|
/** A binding's storage: a module global, a plain local slot, or a boxed
|
|
@@ -2856,8 +2911,20 @@ export class LlEmitter {
|
|
|
2856
2911
|
this.unwindCleanups.clear();
|
|
2857
2912
|
this.jumpTargets = [];
|
|
2858
2913
|
this.currentLocals = new Map(fn.locals.map((l) => [l.id, l]));
|
|
2914
|
+
this.borrowedParameters.clear();
|
|
2915
|
+
const borrowedParameterIndexes = this.callLifetimes.parameters.get(fn.name);
|
|
2916
|
+
if (borrowedParameterIndexes) {
|
|
2917
|
+
for (const index of borrowedParameterIndexes)
|
|
2918
|
+
this.borrowedParameters.add(fn.params[index].localId);
|
|
2919
|
+
}
|
|
2859
2920
|
this.captureIds = new Set([...(fn.captures ?? []), ...(fn.classCaptures ?? [])].map((c) => c.localId));
|
|
2860
2921
|
this.integerLoopBindings.clear();
|
|
2922
|
+
this.fieldPointerTags.clear();
|
|
2923
|
+
this.integerArrayBindings.clear();
|
|
2924
|
+
this.localArrayReads = findLocalArrayReads(fn, this.fnByName, this.unionsById, this.arrayPreservingFunctions, this.callLifetimes, this.optionalArrayReads);
|
|
2925
|
+
this.callArrayReads = findCallArrayReads(fn, this.optionalArrayReads, this.arrayPreservingFunctions, this.callLifetimes);
|
|
2926
|
+
this.mapReadLifetimes = findMapReadLifetimes(fn, this.unionsById, this.callLifetimes);
|
|
2927
|
+
this.localStackUnions = findLocalStackUnions(fn, this.callLifetimes, this.unionsById);
|
|
2861
2928
|
this.integerRanges = analyzeIntegerRanges(fn);
|
|
2862
2929
|
this.chainSlots.clear();
|
|
2863
2930
|
this.finallyStack = [];
|
|
@@ -2949,7 +3016,8 @@ export class LlEmitter {
|
|
|
2949
3016
|
// params (callees own their params — callers passed +1). Boxed params
|
|
2950
3017
|
// allocate the shared binding and move the raw value in.
|
|
2951
3018
|
const fnScope = [];
|
|
2952
|
-
|
|
3019
|
+
const borrowed = this.callLifetimes.parameters.get(fn.name);
|
|
3020
|
+
for (const [index, p] of fn.params.entries()) {
|
|
2953
3021
|
const local = this.currentLocals.get(p.localId);
|
|
2954
3022
|
const slot = `%${mangleLocal(p.localId)}`;
|
|
2955
3023
|
if (local.boxed) {
|
|
@@ -2961,7 +3029,7 @@ export class LlEmitter {
|
|
|
2961
3029
|
continue;
|
|
2962
3030
|
}
|
|
2963
3031
|
B.line(`store ${this.llType(p.type)} %p_${mangleLocal(p.localId)}, ptr ${slot}`);
|
|
2964
|
-
if (isRefCounted(p.type))
|
|
3032
|
+
if (isRefCounted(p.type) && !borrowed?.has(index))
|
|
2965
3033
|
fnScope.push({ slot, type: p.type });
|
|
2966
3034
|
}
|
|
2967
3035
|
this.scopes.push(fnScope);
|
|
@@ -3043,7 +3111,38 @@ export class LlEmitter {
|
|
|
3043
3111
|
const ret = this.llType(fn.returnType);
|
|
3044
3112
|
const attrs = coro !== null ? "#1" : FN_ATTRS;
|
|
3045
3113
|
const debug = this.debugScope === null ? "" : ` !dbg ${this.debugScope}`;
|
|
3046
|
-
|
|
3114
|
+
const symbol = borrowed ? mangleBorrowedFunction(fn.name) : mangleFunction(fn.name);
|
|
3115
|
+
const body = `define internal ${ret} @${symbol}(${params.join(", ")}) ${attrs}${debug} { ; ${fn.name}\n${B.render()}\n}`;
|
|
3116
|
+
return borrowed ? body + "\n" + this.emitOwnedCallAdapter(fn, borrowed) : body;
|
|
3117
|
+
}
|
|
3118
|
+
/** Keep the ordinary owned ABI for closures, virtual dispatch, generated
|
|
3119
|
+
* runtime adapters and library exports. Only direct IR calls select the
|
|
3120
|
+
* borrowing body. Ownership of other parameters moves into that body;
|
|
3121
|
+
* borrowed parameters are released here on normal and exceptional exits.
|
|
3122
|
+
* The pending exception remains for the adapter's caller to handle. */
|
|
3123
|
+
emitOwnedCallAdapter(fn, borrowed) {
|
|
3124
|
+
const B = new BlockBuilder();
|
|
3125
|
+
this.B = B;
|
|
3126
|
+
const params = fn.params.map((param, index) => `${this.llType(param.type)} %p${index}`);
|
|
3127
|
+
const ret = this.llType(fn.returnType);
|
|
3128
|
+
const call = `call ${ret} @${mangleBorrowedFunction(fn.name)}(${params.join(", ")})`;
|
|
3129
|
+
B.line(ret === "void" ? call : `%result = ${call}`);
|
|
3130
|
+
for (const index of borrowed)
|
|
3131
|
+
this.releaseValue(`%p${index}`, fn.params[index].type);
|
|
3132
|
+
B.terminate(ret === "void" ? "ret void" : `ret ${ret} %result`);
|
|
3133
|
+
return `define internal ${ret} @${mangleFunction(fn.name)}(${params.join(", ")}) ${FN_ATTRS} {\n${B.render()}\n}`;
|
|
3134
|
+
}
|
|
3135
|
+
/** A local immutable binding keeps its value alive while later arguments
|
|
3136
|
+
* and the callee execute. Global, captured, TDZ and mutable bindings need
|
|
3137
|
+
* an owned snapshot, even when the callee only inspects the value. */
|
|
3138
|
+
canBorrowCallArgument(value) {
|
|
3139
|
+
if (value.kind === "strLit")
|
|
3140
|
+
return true;
|
|
3141
|
+
if (value.kind !== "varRef")
|
|
3142
|
+
return false;
|
|
3143
|
+
const binding = this.binding(value.localId);
|
|
3144
|
+
return binding.kind === "local" && binding.local !== undefined &&
|
|
3145
|
+
(!binding.local.mutable || this.borrowedParameters.has(value.localId)) && !binding.local.boxed && !binding.local.tdz;
|
|
3047
3146
|
}
|
|
3048
3147
|
// ── statements ──────────────────────────────────────────────────────────
|
|
3049
3148
|
emitStmts(stmts) {
|
|
@@ -3087,6 +3186,31 @@ export class LlEmitter {
|
|
|
3087
3186
|
switch (s.kind) {
|
|
3088
3187
|
case "varDecl": {
|
|
3089
3188
|
const b = this.binding(s.localId);
|
|
3189
|
+
const localUnion = this.localStackUnions.get(s.localId);
|
|
3190
|
+
if (localUnion) {
|
|
3191
|
+
const union = emitStackUnion(this, localUnion);
|
|
3192
|
+
B.line(`store ptr ${union.value.name}, ptr ${b.slot}`);
|
|
3193
|
+
if (union.payload) {
|
|
3194
|
+
this.moveTemp(union.payload);
|
|
3195
|
+
this.scopes[this.scopes.length - 1].push({ slot: union.payloadSlot, type: union.payload.type });
|
|
3196
|
+
}
|
|
3197
|
+
break;
|
|
3198
|
+
}
|
|
3199
|
+
const localRead = this.localArrayReads.get(s.localId);
|
|
3200
|
+
if (localRead) {
|
|
3201
|
+
const owner = emitLocalArrayRead(this, localRead, b.slot);
|
|
3202
|
+
if (owner)
|
|
3203
|
+
this.scopes[this.scopes.length - 1].push(owner);
|
|
3204
|
+
break;
|
|
3205
|
+
}
|
|
3206
|
+
const mapRead = this.mapReadLifetimes.locals.get(s.localId);
|
|
3207
|
+
if (mapRead) {
|
|
3208
|
+
const result = emitStackMapRead(this, mapRead);
|
|
3209
|
+
B.line(`store ptr ${result.value.name}, ptr ${b.slot}`);
|
|
3210
|
+
if (result.owner)
|
|
3211
|
+
this.scopes[this.scopes.length - 1].push(result.owner);
|
|
3212
|
+
break;
|
|
3213
|
+
}
|
|
3090
3214
|
if (b.kind === "boxed") {
|
|
3091
3215
|
// Box FIRST, then evaluate the initializer: a named function
|
|
3092
3216
|
// expression's closure captures this box during init evaluation.
|
|
@@ -3225,7 +3349,7 @@ export class LlEmitter {
|
|
|
3225
3349
|
// release — a release can trigger a cycle collection, which must
|
|
3226
3350
|
// never see a heap edge whose count was already given up).
|
|
3227
3351
|
// Classes and records share the struct layout, so one emission.
|
|
3228
|
-
const obj = this.emitExpr(s.obj);
|
|
3352
|
+
const obj = isRefCounted(s.value.type) ? this.emitExpr(s.obj) : this.emitStableReceiver(s.obj, [s.value]);
|
|
3229
3353
|
const v = this.emitExpr(s.value);
|
|
3230
3354
|
const { ptr, type } = s.kind === "fieldSet"
|
|
3231
3355
|
? this.classFieldPtr(obj.name, s.className, s.field)
|
|
@@ -3486,13 +3610,21 @@ export class LlEmitter {
|
|
|
3486
3610
|
case "for": {
|
|
3487
3611
|
// The init's scope wraps the whole loop (break/continue must NOT
|
|
3488
3612
|
// release it — scopeDepth captured after the push, C parity).
|
|
3489
|
-
const
|
|
3613
|
+
const integerArrayLoop = this.debug === null ? matchIntegerArrayForLoop(s, this.currentLocals, this.integerArrayBindings) : null;
|
|
3614
|
+
const integerLoop = integerArrayLoop ?? (this.debug === null ? matchIntegerBytesForLoop(s, this.currentLocals) : null);
|
|
3490
3615
|
this.scopes.push([]);
|
|
3491
3616
|
let integerSlot = null;
|
|
3492
3617
|
if (integerLoop) {
|
|
3493
3618
|
integerSlot = B.slot();
|
|
3494
3619
|
B.entryAllocas.push(`${integerSlot} = alloca ${this.sizeType} ; integer induction ${this.currentLocals.get(integerLoop.localId).name}`);
|
|
3495
|
-
|
|
3620
|
+
let start = "0";
|
|
3621
|
+
if (integerArrayLoop && s.init?.kind === "varDecl" && s.init.init) {
|
|
3622
|
+
const value = this.emitExpr(s.init.init);
|
|
3623
|
+
start = B.tmp();
|
|
3624
|
+
B.line(`${start} = fptoui double ${value.name} to ${this.sizeType}`);
|
|
3625
|
+
this.integerArrayBindings.add(integerLoop.localId);
|
|
3626
|
+
}
|
|
3627
|
+
B.line(`store ${this.sizeType} ${start}, ptr ${integerSlot}`);
|
|
3496
3628
|
this.integerLoopBindings.set(integerLoop.localId, integerSlot);
|
|
3497
3629
|
}
|
|
3498
3630
|
else if (s.init) {
|
|
@@ -3540,8 +3672,10 @@ export class LlEmitter {
|
|
|
3540
3672
|
const len = B.tmp();
|
|
3541
3673
|
const index = B.tmp();
|
|
3542
3674
|
const inBounds = B.tmp();
|
|
3543
|
-
B.line(`${lenPtr} = getelementptr inbounds
|
|
3544
|
-
|
|
3675
|
+
B.line(`${lenPtr} = getelementptr inbounds %${integerArrayLoop ? "ScrArr" : "ScrBytes"}, ptr ${receiver.name}, i64 0, i32 1`);
|
|
3676
|
+
if (integerArrayLoop)
|
|
3677
|
+
this.markMemoryPointer(lenPtr, "array:header");
|
|
3678
|
+
B.line(`${len} = load ${this.sizeType}, ptr ${lenPtr}${this.fieldAliasAttachment(lenPtr)}`);
|
|
3545
3679
|
B.line(`${index} = load ${this.sizeType}, ptr ${integerSlot}`);
|
|
3546
3680
|
B.line(`${inBounds} = icmp ult ${this.sizeType} ${index}, ${len}`);
|
|
3547
3681
|
B.condBr(inBounds, lb, le);
|
|
@@ -3581,6 +3715,8 @@ export class LlEmitter {
|
|
|
3581
3715
|
this.releaseScope(this.scopes.pop());
|
|
3582
3716
|
if (integerLoop)
|
|
3583
3717
|
this.integerLoopBindings.delete(integerLoop.localId);
|
|
3718
|
+
if (integerArrayLoop)
|
|
3719
|
+
this.integerArrayBindings.delete(integerArrayLoop.localId);
|
|
3584
3720
|
break;
|
|
3585
3721
|
}
|
|
3586
3722
|
case "forOf": {
|
|
@@ -4083,7 +4219,7 @@ export class LlEmitter {
|
|
|
4083
4219
|
// to this expression's frame so they survive those reads and still
|
|
4084
4220
|
// die on the path that created them, including inside lazy branches.
|
|
4085
4221
|
this.scopes.push([]);
|
|
4086
|
-
const result = emitOperatorExpr(this, e);
|
|
4222
|
+
const result = emitBorrowedFieldSequence(this, e) ?? emitOperatorExpr(this, e);
|
|
4087
4223
|
for (const local of this.scopes.pop()) {
|
|
4088
4224
|
this.currentFrame().push({ name: local.slot, type: local.type, slot: true, ...(local.boxed ? { boxed: true } : {}) });
|
|
4089
4225
|
}
|
|
@@ -4170,6 +4306,8 @@ export class LlEmitter {
|
|
|
4170
4306
|
emitReadReceiver(e) {
|
|
4171
4307
|
if (!isRefCounted(e.type))
|
|
4172
4308
|
return this.emitExpr(e);
|
|
4309
|
+
if (e.kind === "strLit")
|
|
4310
|
+
return { name: this.internLiteral(e.value), type: e.type };
|
|
4173
4311
|
if (e.kind === "varRef") {
|
|
4174
4312
|
const binding = this.binding(e.localId);
|
|
4175
4313
|
// Capture boxes can carry TDZ and caught-value conversion semantics.
|
|
@@ -4192,8 +4330,43 @@ export class LlEmitter {
|
|
|
4192
4330
|
: this.classFieldPtr(receiver.name, e.className, e.field);
|
|
4193
4331
|
return { name: this.loadField(ptr, type), type: e.type };
|
|
4194
4332
|
}
|
|
4333
|
+
if (e.kind === "ternary" && this.canBorrowReceiver(e.then) && this.canBorrowReceiver(e.else_)) {
|
|
4334
|
+
// Checked projections lower to a ternary with an always-throwing
|
|
4335
|
+
// arm. Keep that check at the use, but borrow the successful arm.
|
|
4336
|
+
const B = this.B;
|
|
4337
|
+
const cond = this.emitExpr(e.cond);
|
|
4338
|
+
const slot = B.slot();
|
|
4339
|
+
B.entryAllocas.push(`${slot} = alloca ptr`);
|
|
4340
|
+
const yes = B.newLabel("read.then"), no = B.newLabel("read.else"), join = B.newLabel("read.join");
|
|
4341
|
+
B.condBr(cond.name, yes, no);
|
|
4342
|
+
for (const [label, value] of [[yes, e.then], [no, e.else_]]) {
|
|
4343
|
+
B.startBlock(label);
|
|
4344
|
+
this.frames.push([]);
|
|
4345
|
+
const result = this.emitReadReceiver(value);
|
|
4346
|
+
B.line(`store ptr ${result.name}, ptr ${slot}`);
|
|
4347
|
+
this.releaseFrame(this.frames.pop());
|
|
4348
|
+
B.br(join);
|
|
4349
|
+
}
|
|
4350
|
+
B.startBlock(join);
|
|
4351
|
+
const value = B.tmp();
|
|
4352
|
+
B.line(`${value} = load ptr, ptr ${slot}`);
|
|
4353
|
+
return { name: value, type: e.type };
|
|
4354
|
+
}
|
|
4195
4355
|
return this.emitExpr(e);
|
|
4196
4356
|
}
|
|
4357
|
+
canBorrowReceiver(e) {
|
|
4358
|
+
switch (e.kind) {
|
|
4359
|
+
case "varRef": return this.binding(e.localId).kind !== "boxed";
|
|
4360
|
+
case "unionNarrow": return this.canBorrowReceiver(e.value);
|
|
4361
|
+
case "fieldGet":
|
|
4362
|
+
case "recordGet": return this.canBorrowReceiver(e.obj);
|
|
4363
|
+
case "ternary": return this.canBorrowReceiver(e.then) && this.canBorrowReceiver(e.else_);
|
|
4364
|
+
// The runtime always sets a pending exception; its unreachable
|
|
4365
|
+
// typed dummy is null and owns no receiver.
|
|
4366
|
+
case "libCall": return e.fn === "error.nodeThrow";
|
|
4367
|
+
default: return false;
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4197
4370
|
emitExpr(e) {
|
|
4198
4371
|
return emitExpr(this, e);
|
|
4199
4372
|
}
|