@scriptc/compiler 0.2.6 → 0.2.7
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 +5 -18
- package/dist/backend/cycle-analysis.d.ts +21 -9
- package/dist/backend/cycle-analysis.js +139 -67
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +283 -3
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- package/dist/backend/cycle-mutability.d.ts +39 -0
- package/dist/backend/cycle-mutability.js +205 -0
- package/dist/backend/cycle-mutability.js.map +1 -0
- package/dist/backend/link-plan-core.d.ts +2 -1
- package/dist/backend/link-plan-core.js +15 -3
- package/dist/backend/link-plan-core.js.map +1 -1
- package/dist/backend/link-plan.d.ts +2 -1
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/llvm/alloc.d.ts +40 -0
- package/dist/backend/llvm/alloc.js +205 -0
- package/dist/backend/llvm/alloc.js.map +1 -0
- package/dist/backend/llvm/alloc.test.d.ts +1 -0
- package/dist/backend/llvm/alloc.test.js +102 -0
- package/dist/backend/llvm/alloc.test.js.map +1 -0
- package/dist/backend/llvm/call-lifetimes.d.ts +15 -3
- package/dist/backend/llvm/call-lifetimes.js +42 -18
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +170 -0
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/classes.d.ts +4 -1
- package/dist/backend/llvm/classes.js +35 -32
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +36 -3
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +13 -0
- package/dist/backend/llvm/common.js +30 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.d.ts +19 -3
- package/dist/backend/llvm/constant-callbacks.js +289 -52
- package/dist/backend/llvm/constant-callbacks.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.test.js +140 -3
- package/dist/backend/llvm/constant-callbacks.test.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.d.ts +29 -0
- package/dist/backend/llvm/dense-array-access.js +335 -0
- package/dist/backend/llvm/dense-array-access.js.map +1 -0
- package/dist/backend/llvm/dense-array-access.test.d.ts +1 -0
- package/dist/backend/llvm/dense-array-access.test.js +149 -0
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +34 -0
- package/dist/backend/llvm/dyn.js +189 -1
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +124 -4
- package/dist/backend/llvm/emitter.js +623 -105
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +68 -6
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +58 -4
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-async.test.d.ts +1 -0
- package/dist/backend/llvm/expr-async.test.js +62 -0
- package/dist/backend/llvm/expr-async.test.js.map +1 -0
- package/dist/backend/llvm/expr-calls.js +64 -6
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +16 -5
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-control.js +92 -2
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.js +2 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +174 -25
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +1 -1
- package/dist/backend/llvm/expr-primitives.js +52 -24
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +1 -1
- package/dist/backend/llvm/expr-records.js +3 -3
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +59 -2
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.test.js +1 -1
- package/dist/backend/llvm/integer-remainder.d.ts +11 -0
- package/dist/backend/llvm/integer-remainder.js +91 -0
- package/dist/backend/llvm/integer-remainder.js.map +1 -1
- package/dist/backend/llvm/layouts.d.ts +2 -1
- package/dist/backend/llvm/layouts.js +3 -2
- package/dist/backend/llvm/layouts.js.map +1 -1
- package/dist/backend/llvm/lazy-captures.d.ts +16 -0
- package/dist/backend/llvm/lazy-captures.js +96 -0
- package/dist/backend/llvm/lazy-captures.js.map +1 -0
- package/dist/backend/llvm/lazy-captures.test.d.ts +1 -0
- package/dist/backend/llvm/lazy-captures.test.js +78 -0
- package/dist/backend/llvm/lazy-captures.test.js.map +1 -0
- package/dist/backend/llvm/lib-filesystem.js +3 -4
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +5 -1
- package/dist/backend/llvm/local-array-reads.js +78 -17
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +78 -1
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.d.ts +5 -1
- package/dist/backend/llvm/local-union-storage.js +48 -6
- package/dist/backend/llvm/local-union-storage.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.test.js +91 -4
- package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +1 -1
- package/dist/backend/llvm/nullable-fields.d.ts +28 -0
- package/dist/backend/llvm/nullable-fields.js +45 -0
- package/dist/backend/llvm/nullable-fields.js.map +1 -0
- package/dist/backend/llvm/nullable-fields.test.d.ts +1 -0
- package/dist/backend/llvm/nullable-fields.test.js +48 -0
- package/dist/backend/llvm/nullable-fields.test.js.map +1 -0
- package/dist/backend/llvm/serialized-ir-emission.test.d.ts +1 -0
- package/dist/backend/llvm/serialized-ir-emission.test.js +52 -0
- package/dist/backend/llvm/serialized-ir-emission.test.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +69 -2
- package/dist/backend/llvm/shapes.js +259 -27
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +114 -5
- package/dist/backend/llvm/shapes.test.js.map +1 -1
- package/dist/backend/llvm/stack-callbacks.js +2 -3
- package/dist/backend/llvm/stack-callbacks.js.map +1 -1
- package/dist/backend/llvm/stack-captures.d.ts +3 -1
- package/dist/backend/llvm/stack-captures.js +5 -3
- package/dist/backend/llvm/stack-captures.js.map +1 -1
- package/dist/backend/llvm/stack-unions.test.js +38 -1
- package/dist/backend/llvm/stack-unions.test.js.map +1 -1
- package/dist/backend/llvm/string-construction.test.js +1 -1
- package/dist/backend/llvm/string-key-hash.d.ts +8 -0
- package/dist/backend/llvm/string-key-hash.js +59 -0
- package/dist/backend/llvm/string-key-hash.js.map +1 -0
- package/dist/backend/llvm/string-key-hash.test.d.ts +1 -0
- package/dist/backend/llvm/string-key-hash.test.js +34 -0
- package/dist/backend/llvm/string-key-hash.test.js.map +1 -0
- package/dist/backend/llvm/string-lifetimes.test.js +41 -4
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/switch-dispatch.js +1 -1
- package/dist/backend/llvm/switch-dispatch.js.map +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js +2 -2
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
- package/dist/backend/may-throw.js +3 -0
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +1 -1
- package/dist/backend/native/cache-warm.d.ts +2 -1
- package/dist/backend/native/cache-warm.js.map +1 -1
- package/dist/backend/native/contracts.d.ts +3 -2
- package/dist/backend/native/contracts.js.map +1 -1
- package/dist/backend/native/runtime-inputs.d.ts +1 -1
- package/dist/backend/native/runtime-inputs.js +1 -0
- package/dist/backend/native/runtime-inputs.js.map +1 -1
- package/dist/backend/native-codegen-core.d.ts +2 -1
- package/dist/backend/native-codegen-core.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +5 -0
- package/dist/backend/native-codegen.js +4 -0
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +23 -0
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info-core.d.ts +3 -2
- package/dist/backend/native-link-info-core.js.map +1 -1
- package/dist/backend/native-link-info.d.ts +2 -1
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-link-info.test.js +2 -2
- package/dist/backend/native-link-info.test.js.map +1 -1
- package/dist/backend/native-tools.d.ts +3 -1
- package/dist/backend/native-tools.js +0 -1
- package/dist/backend/native-tools.js.map +1 -1
- package/dist/backend/optimization.d.ts +25 -0
- package/dist/backend/optimization.js +21 -0
- package/dist/backend/optimization.js.map +1 -0
- package/dist/backend/optimization.test.d.ts +1 -0
- package/dist/backend/optimization.test.js +24 -0
- package/dist/backend/optimization.test.js.map +1 -0
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-core.d.ts +15 -4
- package/dist/backend/runtime-pack-core.js +23 -12
- package/dist/backend/runtime-pack-core.js.map +1 -1
- package/dist/backend/runtime-pack-native.d.ts +7 -3
- package/dist/backend/runtime-pack-native.js +0 -3
- package/dist/backend/runtime-pack-native.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.d.ts +20 -2
- package/dist/backend/runtime-pack.js +23 -0
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +115 -2
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/target-diagnostics.test.js +1 -1
- package/dist/backend/targets.d.ts +2 -1
- package/dist/backend/targets.js +1 -1
- package/dist/backend/targets.js.map +1 -1
- package/dist/cli/command.js +5 -4
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/host.d.ts +2 -1
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/usage.js +5 -2
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +5 -2
- package/dist/diagnostics/diagnostic.js +10 -8
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +2 -2
- package/dist/executable/executable-cache.js +7 -4
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +20 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/frontend/lowering/abstract-equality.js +5 -49
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +23 -0
- package/dist/frontend/lowering/array-values.js +129 -0
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/builtins/errors.js +17 -2
- package/dist/frontend/lowering/builtins/errors.js.map +1 -1
- package/dist/frontend/lowering/builtins/process.d.ts +6 -17
- package/dist/frontend/lowering/builtins/process.js +56 -29
- package/dist/frontend/lowering/builtins/process.js.map +1 -1
- package/dist/frontend/lowering/caught-value-dispatch.d.ts +11 -0
- package/dist/frontend/lowering/caught-value-dispatch.js +87 -0
- package/dist/frontend/lowering/caught-value-dispatch.js.map +1 -0
- package/dist/frontend/lowering/class-assertions.d.ts +4 -0
- package/dist/frontend/lowering/class-assertions.js +12 -4
- package/dist/frontend/lowering/class-assertions.js.map +1 -1
- package/dist/frontend/lowering/class-callbacks.d.ts +3 -3
- package/dist/frontend/lowering/class-callbacks.js +18 -5
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +1 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js +4 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-method-mutations.d.ts +5 -0
- package/dist/frontend/lowering/class-method-mutations.js +472 -0
- package/dist/frontend/lowering/class-method-mutations.js.map +1 -0
- package/dist/frontend/lowering/class-method-values.js +25 -5
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-prototypes.js +8 -2
- package/dist/frontend/lowering/class-prototypes.js.map +1 -1
- package/dist/frontend/lowering/class-unions.d.ts +16 -0
- package/dist/frontend/lowering/class-unions.js +127 -0
- package/dist/frontend/lowering/class-unions.js.map +1 -0
- package/dist/frontend/lowering/coercions/builders.js +8 -1
- package/dist/frontend/lowering/coercions/builders.js.map +1 -1
- package/dist/frontend/lowering/coercions/function-adapters.js +2 -0
- package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
- package/dist/frontend/lowering/coercions/identity.d.ts +7 -0
- package/dist/frontend/lowering/coercions/identity.js +26 -0
- package/dist/frontend/lowering/coercions/identity.js.map +1 -0
- package/dist/frontend/lowering/coercions/structural.js +9 -6
- package/dist/frontend/lowering/coercions/structural.js.map +1 -1
- package/dist/frontend/lowering/collection-narrowing.d.ts +6 -0
- package/dist/frontend/lowering/collection-narrowing.js +31 -0
- package/dist/frontend/lowering/collection-narrowing.js.map +1 -0
- package/dist/frontend/lowering/containers/array-callback-call.d.ts +7 -0
- package/dist/frontend/lowering/containers/array-callback-call.js +52 -0
- package/dist/frontend/lowering/containers/array-callback-call.js.map +1 -0
- package/dist/frontend/lowering/containers/array-callbacks.d.ts +1 -1
- package/dist/frontend/lowering/containers/array-callbacks.js +363 -49
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/array-concat.d.ts +7 -0
- package/dist/frontend/lowering/containers/array-concat.js +130 -0
- package/dist/frontend/lowering/containers/array-concat.js.map +1 -0
- package/dist/frontend/lowering/containers/array-construction.d.ts +2 -0
- package/dist/frontend/lowering/containers/array-construction.js +95 -3
- package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
- package/dist/frontend/lowering/containers/array-methods.d.ts +1 -11
- package/dist/frontend/lowering/containers/array-methods.js +43 -132
- package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/collection-methods.js +10 -1
- package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/indexed-objects.js +56 -70
- package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
- package/dist/frontend/lowering/cycle-initialization.d.ts +41 -0
- package/dist/frontend/lowering/cycle-initialization.js +277 -0
- package/dist/frontend/lowering/cycle-initialization.js.map +1 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js +4 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +138 -32
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-field-write.d.ts +4 -4
- package/dist/frontend/lowering/expressions/union-field-write.js +54 -44
- package/dist/frontend/lowering/expressions/union-field-write.js.map +1 -1
- package/dist/frontend/lowering/iterator-adapters.d.ts +20 -0
- package/dist/frontend/lowering/iterator-adapters.js +605 -0
- package/dist/frontend/lowering/iterator-adapters.js.map +1 -0
- package/dist/frontend/lowering/lower-array-sort.d.ts +1 -1
- package/dist/frontend/lowering/lower-array-sort.js +44 -5
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
- package/dist/frontend/lowering/lower-array-sort.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-array-sort.test.js +31 -0
- package/dist/frontend/lowering/lower-array-sort.test.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.js +7 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.js +1 -0
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +3 -3
- package/dist/frontend/lowering/lower-calls.js +162 -121
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +2 -0
- package/dist/frontend/lowering/lower-classes.js +45 -43
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +9 -2
- package/dist/frontend/lowering/lower-exprs.js +390 -157
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-functions.js +32 -19
- package/dist/frontend/lowering/lower-functions.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +26 -2
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +10 -2
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +46 -58
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +37 -0
- package/dist/frontend/lowering/lowerer.js +283 -18
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/native-ffi.js +3 -1
- package/dist/frontend/lowering/native-ffi.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-provenance.d.ts +15 -0
- package/dist/frontend/lowering/runtime-optional-provenance.js +40 -0
- package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -0
- package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
- package/dist/frontend/lowering/strict-equality.d.ts +9 -0
- package/dist/frontend/lowering/strict-equality.js +116 -0
- package/dist/frontend/lowering/strict-equality.js.map +1 -0
- package/dist/frontend/lowering/surfaces.js +3 -0
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/union-comparison.d.ts +5 -0
- package/dist/frontend/lowering/union-comparison.js +47 -0
- package/dist/frontend/lowering/union-comparison.js.map +1 -0
- package/dist/frontend/program.d.ts +38 -2
- package/dist/frontend/program.js +440 -59
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +59 -6
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +1 -1
- package/dist/frontend/type-mapper.js +89 -14
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +15 -3
- package/dist/frontend/type-mapper.test.js.map +1 -1
- package/dist/index.js +25 -9
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.d.ts +3 -1
- package/dist/ir/analysis.js +4 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/builtin-signatures.js +2 -3
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/constant-tables.test.js +1 -1
- package/dist/ir/initializer-bindings.test.js +1 -1
- package/dist/ir/ir.d.ts +78 -15
- package/dist/ir/ir.js +20 -6
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/scalar-records.test.js +1 -1
- package/dist/ir/serialize.d.ts +1 -1
- package/dist/ir/serialize.js +3 -3
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/traverse.js +8 -0
- package/dist/ir/traverse.js.map +1 -1
- package/dist/ir/validate.js +118 -20
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +269 -8
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/fence-eval.test.js +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/library-cache.test.js +5 -5
- package/dist/library/library-profile.d.ts +2 -1
- package/dist/library/library-profile.js +4 -3
- package/dist/library/library-profile.js.map +1 -1
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/compiler.test.js +2 -2
- package/dist/native/library.d.ts +2 -1
- package/dist/native/library.js.map +1 -1
- package/dist/native/prepare.js +27 -3
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/sanitizer.d.ts +3 -2
- package/dist/native/sanitizer.js +3 -13
- package/dist/native/sanitizer.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +8 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { emitLlvmLayouts } from "./layouts.js";
|
|
2
|
+
import { inlineAllocSupported } from "./alloc.js";
|
|
2
3
|
import { llvmBytes as llBytes } from "../literals.js";
|
|
3
4
|
import { InternalCompilerError } from "../../errors.js";
|
|
4
5
|
/** Lower typed IR to LLVM, using the runtime's C ABI.
|
|
@@ -19,9 +20,9 @@ import { InternalCompilerError } from "../../errors.js";
|
|
|
19
20
|
* compressed source tables that the runtime inflates on demand.
|
|
20
21
|
*/
|
|
21
22
|
import { deflateRawSync } from "node:zlib";
|
|
22
|
-
import { endsWithJump, matchStringSelfConcat, streamTypedRefEligible } from "../../ir/analysis.js";
|
|
23
|
+
import { endsWithJump, matchStringSelfConcat, streamTypedRefEligible, undefinedArmTag, } from "../../ir/analysis.js";
|
|
23
24
|
import { emitLibraryIdentityLines } from "../library-identity-markers.js";
|
|
24
|
-
import { CAUGHT, ffiCallbackType, isDynTypedRefType, isFfiContextParam, isRefCounted, isUnitType, POINTER_KINDS, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, typeKey, STRING, VOID, } from "../../ir/ir.js";
|
|
25
|
+
import { CAUGHT, ffiCallbackType, isDynTypedRefType, isFfiContextParam, isRefCounted, isUnitType, POINTER_KINDS, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, typeEquals, typeKey, STRING, VOID, } from "../../ir/ir.js";
|
|
25
26
|
import { moduleRuntimeFeatures, moduleEmbedsBuiltin, moduleEmbedsCompressedNpm, NPM_COMPRESS_MIN, } from "../../ir/runtime-features.js";
|
|
26
27
|
import { matchIntegerArrayForLoop, matchIntegerBytesForLoop, matchIntegerCountedForLoop, } from "../../ir/integer-loops.js";
|
|
27
28
|
import { emitCountedLoopLimit } from "./counted-loops.js";
|
|
@@ -44,7 +45,9 @@ import { analyzeCallLifetimes } from "./call-lifetimes.js";
|
|
|
44
45
|
import { canStackUnion, emitStackUnion, findLocalStackUnions } from "./stack-unions.js";
|
|
45
46
|
import { allocateLocalUnion, findLocalUnionStorage, storeLocalUnion, } from "./local-union-storage.js";
|
|
46
47
|
import { BlockBuilder } from "./blocks.js";
|
|
47
|
-
import {
|
|
48
|
+
import { NullableRefFields } from "./nullable-fields.js";
|
|
49
|
+
import { emitLocalArrayRead, emitProjectedArrayRead, emitBorrowedArrayRead, findLocalArrayReads, findCallArrayReads, OptionalArrayReads, } from "./local-array-reads.js";
|
|
50
|
+
import { emitDenseArrayGet, emitDenseArraySet } from "./dense-array-access.js";
|
|
48
51
|
import { emitStackMapRead, findMapReadLifetimes, matchMapRead, } from "./map-read-lifetimes.js";
|
|
49
52
|
import { emitBorrowedFieldSequence } from "./borrowed-receivers.js";
|
|
50
53
|
import { emitBorrowedInput } from "./borrowed-inputs.js";
|
|
@@ -52,10 +55,11 @@ import { ReferenceEffects } from "./reference-effects.js";
|
|
|
52
55
|
import { LlvmDebugInfo } from "./debug-info.js";
|
|
53
56
|
import { StackCallbacks } from "./stack-callbacks.js";
|
|
54
57
|
import { findLoopArrayBorrows } from "./loop-array-borrows.js";
|
|
55
|
-
import { findConstantCallbacks } from "./constant-callbacks.js";
|
|
58
|
+
import { AmbientReceiverReaders, findConstantCallbacks } from "./constant-callbacks.js";
|
|
56
59
|
import { findScalarStringSlices, emitStringSliceSnapshot, } from "./string-slices.js";
|
|
60
|
+
import { LazyCaptures } from "./lazy-captures.js";
|
|
57
61
|
import { StackCaptures } from "./stack-captures.js";
|
|
58
|
-
import { f64Lit, ffiNativeTypeLl, ffiNativeParamLl, ffiNativeReturnLl, llvmCommentText, } from "./common.js";
|
|
62
|
+
import { ABSENT_FIELD_PAYLOAD, emitFieldAbsentTest, f64Lit, ffiNativeTypeLl, ffiNativeParamLl, ffiNativeReturnLl, llvmCommentText, } from "./common.js";
|
|
59
63
|
import { ffiExtendsNarrowIntegers } from "../targets.js";
|
|
60
64
|
import { emitLiteralExpr, emitOperatorExpr, emitStringExpr, emitContainerExpr, emitRecordExpr, } from "./expr-primitives.js";
|
|
61
65
|
import { emitControlExpr } from "./expr-control.js";
|
|
@@ -79,7 +83,8 @@ import { buildClassGraph, classMembershipIntervals, emitClassMembershipHelper, c
|
|
|
79
83
|
import { LlDyn } from "./dyn.js";
|
|
80
84
|
import { LlvmUnsupportedError } from "./unsupported.js";
|
|
81
85
|
import { LlWalkers } from "./walkers.js";
|
|
82
|
-
import {
|
|
86
|
+
import { literalCapWord } from "./string-key-hash.js";
|
|
87
|
+
import { arrNewCall, boxAccess, boxNewCall, boxReleaseSym, computeTraced, elemAccess, emitInlineRcHelpers, FN_ATTRS, inlineRcDecls, llFieldType, releaseSym, retainSym, traceArg, typedRefConstructor, vAdapters, } from "./shapes.js";
|
|
83
88
|
export { LlvmUnsupportedError } from "./unsupported.js";
|
|
84
89
|
function ffiCallbackDummyLl(callback) {
|
|
85
90
|
switch (callback.returns) {
|
|
@@ -182,6 +187,8 @@ export class LlEmitter {
|
|
|
182
187
|
* immortal (rc == SIZE_MAX) static per (union, unit tag).
|
|
183
188
|
* RC entry points and the collector skip immortals. */
|
|
184
189
|
unitInstances = new Map();
|
|
190
|
+
/** unionId → symbol of the union's immortal ABSENT field-slot instance. */
|
|
191
|
+
absentInstances = new Map();
|
|
185
192
|
immortalValues = new Set();
|
|
186
193
|
/** Regex literal templates: "<flags>/<pattern>" → { symbol, interned
|
|
187
194
|
* source/flags literal refs } — one immortal ScrRegex template per distinct
|
|
@@ -215,7 +222,13 @@ export class LlEmitter {
|
|
|
215
222
|
needsBadTag = false;
|
|
216
223
|
needsBadKey = false;
|
|
217
224
|
needsRetainBox = false;
|
|
225
|
+
/** Inline RC fast-path helpers requested by call sites (shapes.ts); null
|
|
226
|
+
* when inline RC is off. */
|
|
227
|
+
rcHelpers;
|
|
218
228
|
fnByName = new Map();
|
|
229
|
+
/** Emitted LLVM text of every IR function body, by function name: the
|
|
230
|
+
* fiberless-async analysis scans it for suspension entry points. */
|
|
231
|
+
fnDefText = new Map();
|
|
219
232
|
referenceEffects;
|
|
220
233
|
optionalArrayReads;
|
|
221
234
|
callArrayReads = new Map();
|
|
@@ -224,8 +237,16 @@ export class LlEmitter {
|
|
|
224
237
|
constantCallbacks;
|
|
225
238
|
currentConstantCallbacks = new Map();
|
|
226
239
|
stackCallbacks;
|
|
240
|
+
receiverReaders;
|
|
227
241
|
stackCaptures;
|
|
242
|
+
lazyCaptures;
|
|
243
|
+
/** Unchanged captured parameters of the current function: each maps to
|
|
244
|
+
* the slot caching its box, NULL until the first closure needs it. */
|
|
245
|
+
lazyCaptureBoxes = new Map();
|
|
246
|
+
lazyStackBoxes = new Map();
|
|
228
247
|
borrowedParameters = new Set();
|
|
248
|
+
/** Parameters proven projection-only: callers may pass stack boxes. */
|
|
249
|
+
projectedParameters = new Set();
|
|
229
250
|
stableCallBindings = new Set();
|
|
230
251
|
/** Manifest-bound native imports, used by ffiCall emission. */
|
|
231
252
|
ffiByName = new Map();
|
|
@@ -265,6 +286,7 @@ export class LlEmitter {
|
|
|
265
286
|
liveDynUnionRefAdapters = new Map();
|
|
266
287
|
dynPromiseAdapters = new Map();
|
|
267
288
|
unionsById = new Map();
|
|
289
|
+
nullableFields;
|
|
268
290
|
recordsById = new Map();
|
|
269
291
|
recordCloneShapes = new Set();
|
|
270
292
|
tracedShapes;
|
|
@@ -370,6 +392,7 @@ export class LlEmitter {
|
|
|
370
392
|
this.wasi = options.wasi === true;
|
|
371
393
|
this.emitLibraryIdentity = options.emitLibraryIdentity !== false;
|
|
372
394
|
this.runtimeAbiMarker = options.runtimeAbiMarker === true;
|
|
395
|
+
this.rcHelpers = options.inlineRc === true ? new Set() : null;
|
|
373
396
|
// ScrCycHdr.color stays 12 bytes behind a wasm32 object and 16 bytes
|
|
374
397
|
// behind a 64-bit object. The wasm32 header pads before color so its
|
|
375
398
|
// payload remains double-aligned without changing this ABI offset.
|
|
@@ -396,12 +419,15 @@ export class LlEmitter {
|
|
|
396
419
|
}
|
|
397
420
|
for (const u of mod.unions ?? [])
|
|
398
421
|
this.unionsById.set(u.id, u);
|
|
422
|
+
this.nullableFields = new NullableRefFields(mod.classes ?? [], this.unionsById);
|
|
399
423
|
this.optionalArrayReads = new OptionalArrayReads(this.fnByName, this.unionsById);
|
|
400
424
|
this.referenceEffects = new ReferenceEffects(this.fnByName, (call) => this.optionalArrayReads.get(call) !== null);
|
|
401
|
-
this.callLifetimes = analyzeCallLifetimes(this.fnByName);
|
|
425
|
+
this.callLifetimes = analyzeCallLifetimes(this.fnByName, (className, field) => this.nullableFields.get(className, field) !== null);
|
|
402
426
|
this.constantCallbacks = findConstantCallbacks(mod, this.callLifetimes);
|
|
403
427
|
this.stackCallbacks = new StackCallbacks(this.fnByName);
|
|
428
|
+
this.receiverReaders = new AmbientReceiverReaders(this.fnByName);
|
|
404
429
|
this.stackCaptures = new StackCaptures(this.fnByName, this.callLifetimes, this.stackCallbacks);
|
|
430
|
+
this.lazyCaptures = new LazyCaptures(this.fnByName);
|
|
405
431
|
for (const r of mod.records ?? [])
|
|
406
432
|
this.recordsById.set(r.id, r);
|
|
407
433
|
const traced = computeTraced(mod);
|
|
@@ -414,6 +440,17 @@ export class LlEmitter {
|
|
|
414
440
|
tracedUnions: this.tracedUnions,
|
|
415
441
|
recordsById: this.recordsById,
|
|
416
442
|
recordCloneShapes: this.recordCloneShapes,
|
|
443
|
+
objectAudit: options.objectAudit !== false,
|
|
444
|
+
inlineAlloc: inlineAllocSupported({
|
|
445
|
+
pointerBits: options.pointerBits,
|
|
446
|
+
wasi: options.wasi,
|
|
447
|
+
targetTriple: options.targetTriple,
|
|
448
|
+
// Worker programs (SCR_WORKERS) make scr_cyc_live and
|
|
449
|
+
// scr_weak_dispose_hook thread-local and compile the allocator out,
|
|
450
|
+
// exactly like thread-instanced libraries.
|
|
451
|
+
threadInstances: mod.lib?.threadInstances === true || mod.workers === true,
|
|
452
|
+
}),
|
|
453
|
+
rcHelpers: this.rcHelpers,
|
|
417
454
|
unionsById: this.unionsById,
|
|
418
455
|
declare: (decl) => this.declare(decl),
|
|
419
456
|
needOom: () => this.needOom(),
|
|
@@ -421,6 +458,7 @@ export class LlEmitter {
|
|
|
421
458
|
internLiteral: (text) => this.internLiteral(text),
|
|
422
459
|
cstr: (text) => this.cstr(text),
|
|
423
460
|
unitInstanceRef: (unionId, tag) => this.unitInstanceRef(unionId, tag),
|
|
461
|
+
absentInstanceRef: (unionId) => this.absentInstanceRef(unionId),
|
|
424
462
|
liveDynRefAdapter: (type) => this.liveDynRefAdapter(type),
|
|
425
463
|
liveDynUnionRefAdapter: (type) => this.liveDynUnionRefAdapter(type),
|
|
426
464
|
dynPromiseAdapter: (type) => this.dynPromiseAdapter(type),
|
|
@@ -797,8 +835,11 @@ export class LlEmitter {
|
|
|
797
835
|
// Function bodies first (the literal/unit/fn-value tables fill as they
|
|
798
836
|
// emit), then the file assembles around them — the runtime ABI’s order.
|
|
799
837
|
const fnDefs = [];
|
|
800
|
-
for (const fn of this.mod.functions)
|
|
801
|
-
|
|
838
|
+
for (const fn of this.mod.functions) {
|
|
839
|
+
const def = this.emitFunction(fn);
|
|
840
|
+
fnDefs.push(def);
|
|
841
|
+
this.fnDefText.set(fn.name, def);
|
|
842
|
+
}
|
|
802
843
|
const errorViews = [];
|
|
803
844
|
for (const property of ["name", "message"])
|
|
804
845
|
if (this.mod.functions.some((fn) => fn.name === `%error.${property}.read`)) {
|
|
@@ -817,7 +858,7 @@ export class LlEmitter {
|
|
|
817
858
|
this.declare(`declare void @scr_error_register_dyn(ptr, ptr)`);
|
|
818
859
|
errorViews.push(` call void @scr_error_register_dyn(ptr @${mangleVtInstance(meta.def.name)}, ptr @${name})`);
|
|
819
860
|
}
|
|
820
|
-
const layouts = emitLlvmLayouts(this.shapeHost, this.mod, this.classMeta, this.classObjs, this.fnByName, (t) => this.llType(t));
|
|
861
|
+
const layouts = emitLlvmLayouts(this.shapeHost, this.mod, this.classMeta, this.classObjs, this.fnByName, (t) => this.llType(t), this.nullableFields);
|
|
821
862
|
const shapes = layouts.records;
|
|
822
863
|
const classShapes = layouts.classes;
|
|
823
864
|
const classObjDefs = layouts.classObjects;
|
|
@@ -1132,13 +1173,19 @@ export class LlEmitter {
|
|
|
1132
1173
|
`%ScrLogArg = type { i32, i64 }`,
|
|
1133
1174
|
`%ScrVt = type { ${this.sizeType}, ${this.sizeType}, ptr }`,
|
|
1134
1175
|
`%ScrUnion = type { ${this.sizeType}, i32, ptr, ptr, ptr, i64 }`,
|
|
1135
|
-
`%ScrClosure = type { ${this.sizeType}, ptr, ${this.sizeType}, ptr, i32 }`,
|
|
1176
|
+
`%ScrClosure = type { ${this.sizeType}, ptr, ${this.sizeType}, ptr, i32, i32, ptr }`,
|
|
1136
1177
|
`%ScrFfiTable = type { ptr, ${this.sizeType}, ${this.sizeType}, ptr, i8, ptr, ptr, ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, ptr, ptr }`,
|
|
1137
|
-
`%ScrRegex = type { ${this.sizeType}, ptr, ptr, ptr, double, ptr }`,
|
|
1178
|
+
`%ScrRegex = type { ${this.sizeType}, ptr, ptr, ptr, double, ptr, ptr }`,
|
|
1138
1179
|
// ScrArr mirrors scr_runtime.h field-for-field. Live dynamic stream
|
|
1139
1180
|
// commits swap its mutable dense, sparse, presence, and property
|
|
1140
1181
|
// storage while preserving the target object's identity.
|
|
1141
1182
|
`%ScrArr = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, ptr, ptr, ptr, ptr, ${this.sizeType}, ${this.sizeType}, ptr, ${this.sizeType}, ${this.sizeType}, ptr }`,
|
|
1183
|
+
// The ScrMap prefix { rc, key_kind, val_kind, val_retain, val_release,
|
|
1184
|
+
// val_trace, key_retain, key_release, key_trace } — the inline RC
|
|
1185
|
+
// fast paths test the two trace slots (shapes.ts).
|
|
1186
|
+
...(this.rcHelpers === null
|
|
1187
|
+
? []
|
|
1188
|
+
: [`%ScrMapRc = type { ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr }`]),
|
|
1142
1189
|
// The runtime error prefix { rc, vt, name, message, code, cause } and the
|
|
1143
1190
|
// class-object shape { rc, pre, post, ctor, name } — field reads on
|
|
1144
1191
|
// builtin errors and classval loads GEP through these.
|
|
@@ -1184,12 +1231,13 @@ export class LlEmitter {
|
|
|
1184
1231
|
// data (string literals, unit arms, template arrays, vtables) stays
|
|
1185
1232
|
// shared.
|
|
1186
1233
|
const tl = this.mod.workers === true || this.mod.lib?.threadInstances === true ? "thread_local " : "";
|
|
1187
|
-
out.push(``, `@scr_error_vts = external ${tl}global [${RUNTIME_ERROR_CLASSES.size} x %ScrVt]`, `declare void @scr_init()`, `declare void @scr_lib_init(i32, ptr)`, ...(this.mod.workers ? [`declare void @
|
|
1234
|
+
out.push(``, `@scr_error_vts = external ${tl}global [${RUNTIME_ERROR_CLASSES.size} x %ScrVt]`, `declare void @scr_init()`, `declare void @scr_lib_init(i32, ptr)`, ...(this.mod.workers ? [`declare void @scr_runtime_workers_v8()`] : []), ...(this.runtimeAbiMarker && this.mod.lib === undefined
|
|
1188
1235
|
? [`declare void @${RUNTIME_ABI_MARKER}()`]
|
|
1189
1236
|
: []));
|
|
1190
1237
|
for (const d of this.decls)
|
|
1191
1238
|
out.push(d);
|
|
1192
1239
|
out.push(``);
|
|
1240
|
+
const flushedDecls = new Set(this.decls);
|
|
1193
1241
|
for (const table of this.constantNumericTables.values()) {
|
|
1194
1242
|
const n = table.values.length;
|
|
1195
1243
|
out.push(`@${table.symbol} = private constant [${n} x double] [${table.values.map((v) => `double ${f64Lit(v)}`).join(", ")}]`, `define internal double @${table.symbol}_get(ptr %a, double %i) alwaysinline #0 {`, `entry:`, ` %initialized = icmp ne ptr %a, null`, ` %nonnegative = fcmp oge double %i, ${f64Lit(0)}`, ` %below = fcmp olt double %i, ${f64Lit(n)}`, ` %range = and i1 %nonnegative, %below`, ` %safe = and i1 %initialized, %range`, ` br i1 %safe, label %convert, label %fallback`, `convert:`,
|
|
@@ -1200,8 +1248,11 @@ export class LlEmitter {
|
|
|
1200
1248
|
for (const [text, lit] of this.literals) {
|
|
1201
1249
|
// Immortal interned ScrStr: { rc = SIZE_MAX, len, cap = len, bytes\0 } —
|
|
1202
1250
|
// the runtime ABI’s static table, retain/release skip rc == SIZE_MAX.
|
|
1251
|
+
// 64-bit (little-endian) targets split the capacity word: the high
|
|
1252
|
+
// half carries the precomputed Map key hash (SCR_STR_HASH_CACHE).
|
|
1253
|
+
const capWord = this.sizeType === "i64" ? literalCapWord(Buffer.from(text, "utf8")) : String(lit.len);
|
|
1203
1254
|
out.push(`@${lit.sym} = internal global { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, [${lit.len + 1} x i8] } ` +
|
|
1204
|
-
`{ ${this.sizeType} -1, ${this.sizeType} ${lit.len}, ${this.sizeType} ${
|
|
1255
|
+
`{ ${this.sizeType} -1, ${this.sizeType} ${lit.len}, ${this.sizeType} ${capWord}, [${lit.len + 1} x i8] c"${llStrBytes(text)}" }`);
|
|
1205
1256
|
}
|
|
1206
1257
|
if (this.literals.size > 0)
|
|
1207
1258
|
out.push(``);
|
|
@@ -1214,11 +1265,23 @@ export class LlEmitter {
|
|
|
1214
1265
|
}
|
|
1215
1266
|
if (this.unitInstances.size > 0)
|
|
1216
1267
|
out.push(``);
|
|
1268
|
+
for (const [unionId, sym] of this.absentInstances) {
|
|
1269
|
+
// One immortal ABSENT field-slot instance per undefined-armed union:
|
|
1270
|
+
// the undefined arm's tag with payload 1 (ordinary unit instances
|
|
1271
|
+
// carry 0). Every tag reader sees undefined; recordHas and the
|
|
1272
|
+
// field reads compare tag and payload, never the address, so the
|
|
1273
|
+
// per-object copies of separately compiled units agree.
|
|
1274
|
+
const tag = undefinedArmTag({ kind: "union", unionId }, this.unionsById);
|
|
1275
|
+
out.push(`@${sym} = internal global %ScrUnion { ${this.sizeType} -1, i32 ${tag}, ptr null, ptr null, ptr null, i64 ${ABSENT_FIELD_PAYLOAD} } ; ${unionId} absent field`);
|
|
1276
|
+
}
|
|
1277
|
+
if (this.absentInstances.size > 0)
|
|
1278
|
+
out.push(``);
|
|
1217
1279
|
for (const [key, re] of this.regexInstances) {
|
|
1218
1280
|
// One immortal ScrRegex per (pattern, flags) literal, pointing at
|
|
1219
|
-
// the interned source/flags strings. The bc
|
|
1220
|
-
// compile, cached by the runtime) — a mutable
|
|
1221
|
-
|
|
1281
|
+
// the interned source/flags strings. The bc and native-matcher slots
|
|
1282
|
+
// start null (lazy compile, cached by the runtime) — a mutable
|
|
1283
|
+
// global, not constant.
|
|
1284
|
+
out.push(`@${re.sym} = internal ${tl}global %ScrRegex { ${this.sizeType} -1, ptr ${re.src}, ptr ${re.fl}, ptr null, double 0.0, ptr null, ptr null } ; ${key.replace(/\n/g, "\\n")}`);
|
|
1222
1285
|
}
|
|
1223
1286
|
if (this.regexInstances.size > 0)
|
|
1224
1287
|
out.push(``);
|
|
@@ -1367,6 +1430,7 @@ export class LlEmitter {
|
|
|
1367
1430
|
// symbols specified by the library IR instead.
|
|
1368
1431
|
for (const line of this.emitLibDefs(globals, globalReleaseLines, stamps))
|
|
1369
1432
|
out.push(line);
|
|
1433
|
+
out.push(...this.inlineRcTail(flushedDecls));
|
|
1370
1434
|
out.push(`attributes #0 = { sanitize_address }`);
|
|
1371
1435
|
if (this.wasi)
|
|
1372
1436
|
out.push(`attributes #1 = { sanitize_address presplitcoroutine }`);
|
|
@@ -1378,7 +1442,7 @@ export class LlEmitter {
|
|
|
1378
1442
|
out.push(this.debug.render());
|
|
1379
1443
|
return out;
|
|
1380
1444
|
}
|
|
1381
|
-
out.push(`define i32 @${this.mod.workers ? "sc_context_entry" : this.wasi ? "__main_argc_argv" : "main"}(i32 %argc, ptr %argv) ${FN_ATTRS} {`, `entry:`, ...(this.runtimeAbiMarker ? [` call void @${RUNTIME_ABI_MARKER}()`] : []), ...(this.mod.workers ? [` call void @
|
|
1445
|
+
out.push(`define i32 @${this.mod.workers ? "sc_context_entry" : this.wasi ? "__main_argc_argv" : "main"}(i32 %argc, ptr %argv) ${FN_ATTRS} {`, `entry:`, ...(this.runtimeAbiMarker ? [` call void @${RUNTIME_ABI_MARKER}()`] : []), ...(this.mod.workers ? [` call void @scr_runtime_workers_v8()`] : []), ` call void @scr_init()`, ...stamps,
|
|
1382
1446
|
// Event-surface programs (signal/exit listeners) fill the loop's
|
|
1383
1447
|
// nullable event hooks before %main — scr_events.c links only when
|
|
1384
1448
|
// this line is emitted (native/executable.ts gates on the same predicate).
|
|
@@ -1486,7 +1550,7 @@ export class LlEmitter {
|
|
|
1486
1550
|
? [` %test_exit = call i32 @scr_test_exit_code()`, ` ret i32 %test_exit`]
|
|
1487
1551
|
: usesIsland
|
|
1488
1552
|
? [` %island_exit = call i32 @scr_island_exit_code()`, ` ret i32 %island_exit`]
|
|
1489
|
-
: [` %native_exit = call i32 @scr_exit_code_hint_get()`, ` ret i32 %native_exit`]), `}`, ``,
|
|
1553
|
+
: [` %native_exit = call i32 @scr_exit_code_hint_get()`, ` ret i32 %native_exit`]), `}`, ``, ...this.inlineRcTail(flushedDecls),
|
|
1490
1554
|
// sanitize_address is inert under the plain pipeline; the sanitized
|
|
1491
1555
|
// lane's -fsanitize=address link activates instrumentation over the
|
|
1492
1556
|
// emitted functions too (the runtime TUs get theirs from clang).
|
|
@@ -1743,10 +1807,62 @@ export class LlEmitter {
|
|
|
1743
1807
|
// emitted once with internal linkage, like the record retains.
|
|
1744
1808
|
defs.push(`define internal ptr @sc_retain_box(ptr %b) ${FN_ATTRS} {`, `entry:`, ` %rc = load ${this.sizeType}, ptr %b`, ` %imm = icmp eq ${this.sizeType} %rc, -1`, ` br i1 %imm, label %done, label %inc`, `inc:`, ` %n = add ${this.sizeType} %rc, 1`, ` store ${this.sizeType} %n, ptr %b`, ` %colorp = getelementptr i8, ptr %b, ${this.sizeType} -${this.cycleColorOffset}`, ` store i32 0, ptr %colorp ; mark live`, ` br label %done`, `done:`, ` ret ptr %b`, `}`, ``);
|
|
1745
1809
|
}
|
|
1810
|
+
// String comparisons enter through small internal wrappers that LLVM
|
|
1811
|
+
// inlines at each site: interned literals and shared values compare by
|
|
1812
|
+
// identity, and equality rejects different byte lengths, before any
|
|
1813
|
+
// runtime call (scr_str_eq/scr_str_cmp keep the complete semantics).
|
|
1814
|
+
if (this.decls.has(`declare zeroext i1 @scr_str_eq(ptr, ptr)`)) {
|
|
1815
|
+
defs.push(`define internal zeroext i1 @sc_str_eq(ptr %a, ptr %b) ${FN_ATTRS} {`, `entry:`, ` %same = icmp eq ptr %a, %b`, ` br i1 %same, label %yes, label %lengths`, `lengths:`, ` %alen.p = getelementptr inbounds %ScrStr, ptr %a, i32 0, i32 1`, ` %blen.p = getelementptr inbounds %ScrStr, ptr %b, i32 0, i32 1`, ` %alen = load ${this.sizeType}, ptr %alen.p`, ` %blen = load ${this.sizeType}, ptr %blen.p`, ` %samelen = icmp eq ${this.sizeType} %alen, %blen`, ` br i1 %samelen, label %bytes, label %no`, `bytes:`, ` %eq = call zeroext i1 @scr_str_eq(ptr %a, ptr %b)`, ` ret i1 %eq`, `yes:`, ` ret i1 true`, `no:`, ` ret i1 false`, `}`, ``);
|
|
1816
|
+
}
|
|
1817
|
+
// Ordering also decides inline when the first bytes differ: byte order
|
|
1818
|
+
// is the plain comparison's order, and the UTF-16 order whenever both
|
|
1819
|
+
// bytes are ASCII. Callers only test the sign of the result.
|
|
1820
|
+
for (const fn of ["scr_str_cmp", "scr_str_cmp_u16"]) {
|
|
1821
|
+
if (!this.decls.has(`declare i32 @${fn}(ptr, ptr)`))
|
|
1822
|
+
continue;
|
|
1823
|
+
this.declare("declare i64 @llvm.bswap.i64(i64)");
|
|
1824
|
+
const utf16 = fn === "scr_str_cmp_u16";
|
|
1825
|
+
defs.push(`define internal i32 @${fn.replace(/^scr_/, "sc_")}(ptr %a, ptr %b) ${FN_ATTRS} {`, `entry:`, ` %same = icmp eq ptr %a, %b`, ` br i1 %same, label %equal, label %lengths`, `lengths:`, ` %alen.p = getelementptr inbounds %ScrStr, ptr %a, i32 0, i32 1`, ` %blen.p = getelementptr inbounds %ScrStr, ptr %b, i32 0, i32 1`, ` %alen = load ${this.sizeType}, ptr %alen.p`, ` %blen = load ${this.sizeType}, ptr %blen.p`, ` %aempty = icmp eq ${this.sizeType} %alen, 0`, ` %bempty = icmp eq ${this.sizeType} %blen, 0`, ` %empty = or i1 %aempty, %bempty`, ` br i1 %empty, label %order, label %words`,
|
|
1826
|
+
// Both at least 8 bytes: the first differing byte of one word pair.
|
|
1827
|
+
// Byte-swapped little-endian words compare in byte order.
|
|
1828
|
+
`words:`, ` %along = icmp uge ${this.sizeType} %alen, 8`, ` %blong = icmp uge ${this.sizeType} %blen, 8`, ` %long = and i1 %along, %blong`, ` br i1 %long, label %word, label %first`, `word:`, ` %awp = getelementptr inbounds %ScrStr, ptr %a, i32 1`, ` %bwp = getelementptr inbounds %ScrStr, ptr %b, i32 1`, ` %aw = load i64, ptr %awp, align 1`, ` %bw = load i64, ptr %bwp, align 1`, ` %wdiffer = icmp ne i64 %aw, %bw`, ` br i1 %wdiffer, label %wdiff, label %wsame`, `wdiff:`, ...(utf16
|
|
1829
|
+
? [
|
|
1830
|
+
` %wbits = or i64 %aw, %bw`,
|
|
1831
|
+
` %whigh = and i64 %wbits, -9187201950435737472`,
|
|
1832
|
+
` %wascii = icmp eq i64 %whigh, 0`,
|
|
1833
|
+
` br i1 %wascii, label %wordorder, label %order`,
|
|
1834
|
+
]
|
|
1835
|
+
: [` br label %wordorder`]), `wordorder:`, ` %asw = call i64 @llvm.bswap.i64(i64 %aw)`, ` %bsw = call i64 @llvm.bswap.i64(i64 %bw)`, ` %wbelow = icmp ult i64 %asw, %bsw`, ` %wsign = select i1 %wbelow, i32 -1, i32 1`, ` ret i32 %wsign`,
|
|
1836
|
+
// Equal first words and one length of at most 16 bytes: the word
|
|
1837
|
+
// ending at that length covers the rest, and any difference lies
|
|
1838
|
+
// past the equal first word.
|
|
1839
|
+
`wsame:`, ` %samelen = icmp eq ${this.sizeType} %alen, %blen`, ` %short = icmp ule ${this.sizeType} %alen, 16`, ` %tailable = and i1 %samelen, %short`, ` br i1 %tailable, label %tail, label %order`, `tail:`, ` %toff = sub ${this.sizeType} %alen, 8`, ` %atp = getelementptr inbounds i8, ptr %awp, ${this.sizeType} %toff`, ` %btp = getelementptr inbounds i8, ptr %bwp, ${this.sizeType} %toff`, ` %at = load i64, ptr %atp, align 1`, ` %bt = load i64, ptr %btp, align 1`, ` %tdiffer = icmp ne i64 %at, %bt`, ` br i1 %tdiffer, label %tdiff, label %equal`, `tdiff:`, ...(utf16
|
|
1840
|
+
? [
|
|
1841
|
+
` %tbits = or i64 %at, %bt`,
|
|
1842
|
+
` %thigh = and i64 %tbits, -9187201950435737472`,
|
|
1843
|
+
` %tascii = icmp eq i64 %thigh, 0`,
|
|
1844
|
+
` br i1 %tascii, label %tailorder, label %order`,
|
|
1845
|
+
]
|
|
1846
|
+
: [` br label %tailorder`]), `tailorder:`, ` %ats = call i64 @llvm.bswap.i64(i64 %at)`, ` %bts = call i64 @llvm.bswap.i64(i64 %bt)`, ` %tbelow = icmp ult i64 %ats, %bts`, ` %tsign = select i1 %tbelow, i32 -1, i32 1`, ` ret i32 %tsign`, `first:`, ` %adata = getelementptr inbounds %ScrStr, ptr %a, i32 1`, ` %bdata = getelementptr inbounds %ScrStr, ptr %b, i32 1`, ` %x = load i8, ptr %adata`, ` %y = load i8, ptr %bdata`, ` %differ = icmp ne i8 %x, %y`, ...(utf16
|
|
1847
|
+
? [
|
|
1848
|
+
` %bits = or i8 %x, %y`,
|
|
1849
|
+
` %ascii = icmp sge i8 %bits, 0`,
|
|
1850
|
+
` %decide = and i1 %differ, %ascii`,
|
|
1851
|
+
]
|
|
1852
|
+
: [` %decide = and i1 %differ, true`]), ` br i1 %decide, label %byte, label %order`, `byte:`, ` %below = icmp ult i8 %x, %y`, ` %sign = select i1 %below, i32 -1, i32 1`, ` ret i32 %sign`, `order:`, ` %c = call i32 @${fn}(ptr %a, ptr %b)`, ` ret i32 %c`, `equal:`, ` ret i32 0`, `}`, ``);
|
|
1853
|
+
}
|
|
1746
1854
|
// The declarations these helpers added must land in the extern block,
|
|
1747
1855
|
// which already flushed — append here instead (LLVM is order-free).
|
|
1748
1856
|
return defs.length > 0 ? [...defs] : defs;
|
|
1749
1857
|
}
|
|
1858
|
+
/** The inline RC helpers every call site requested, emitted last so
|
|
1859
|
+
* requests from main/library epilogues are included. Slow-path
|
|
1860
|
+
* declarations a late request added after the extern block flushed land
|
|
1861
|
+
* here too. */
|
|
1862
|
+
inlineRcTail(flushedDecls) {
|
|
1863
|
+
const late = inlineRcDecls(this.shapeHost).filter((d) => !flushedDecls.has(d));
|
|
1864
|
+
return [...late, ...emitInlineRcHelpers(this.shapeHost)];
|
|
1865
|
+
}
|
|
1750
1866
|
/** Env-signature wrappers + interned immortal closures for declared
|
|
1751
1867
|
* functions used as values (the runtime ABI’s sc_w_/sc_fc_ pair): every
|
|
1752
1868
|
* mention of `f` yields the same pointer, so `f === f` holds. */
|
|
@@ -1761,7 +1877,7 @@ export class LlEmitter {
|
|
|
1761
1877
|
// never the inner return type.
|
|
1762
1878
|
const ret = fn.async === true || fn.generator !== undefined ? "ptr" : this.llType(fn.returnType);
|
|
1763
1879
|
const call = `call ${ret} @${this.callTarget(name)}(${args})`;
|
|
1764
|
-
out.push(`define internal ${ret} @${mangleWrapper(name)}(ptr %env${params.length ? ", " + params.join(", ") : ""}) ${FN_ATTRS} { ; ${name} as a value`, `entry:`, ret === "void" ? ` ${call}` : ` %r = ${call}`, ret === "void" ? ` ret void` : ` ret ${ret} %r`, `}`, `@${mangleFnClosure(name)} = internal ${this.mod.workers === true || this.mod.lib?.threadInstances === true ? "thread_local " : ""}global %ScrClosure { ${this.sizeType} -1, ptr @${mangleWrapper(name)}, ${this.sizeType} 0, ptr null, i32 ${(fn.generator ? 1 : 0) + (fn.async ? 2 : 0) + (fn.ownsPrototype ? 4 : 0)} }`, ``);
|
|
1880
|
+
out.push(`define internal ${ret} @${mangleWrapper(name)}(ptr %env${params.length ? ", " + params.join(", ") : ""}) ${FN_ATTRS} { ; ${name} as a value`, `entry:`, ret === "void" ? ` ${call}` : ` %r = ${call}`, ret === "void" ? ` ret void` : ` ret ${ret} %r`, `}`, `@${mangleFnClosure(name)} = internal ${this.mod.workers === true || this.mod.lib?.threadInstances === true ? "thread_local " : ""}global %ScrClosure { ${this.sizeType} -1, ptr @${mangleWrapper(name)}, ${this.sizeType} 0, ptr null, i32 ${(fn.generator ? 1 : 0) + (fn.async ? 2 : 0) + (fn.ownsPrototype ? 4 : 0)}, i32 0, ptr null }`, ``);
|
|
1765
1881
|
}
|
|
1766
1882
|
return out;
|
|
1767
1883
|
}
|
|
@@ -1815,6 +1931,154 @@ export class LlEmitter {
|
|
|
1815
1931
|
});
|
|
1816
1932
|
return { definitions, pack, lifted, fieldTys, ret, tr, spawnParams, argPackLines };
|
|
1817
1933
|
}
|
|
1934
|
+
/** Settles `promise` from a clean async body result `%r` (moved in). */
|
|
1935
|
+
asyncFulfillLines(ret, promise) {
|
|
1936
|
+
switch (ret.kind) {
|
|
1937
|
+
case "void":
|
|
1938
|
+
this.declare(`declare void @scr_promise_fulfill_void(ptr)`);
|
|
1939
|
+
return [` call void @scr_promise_fulfill_void(ptr ${promise})`];
|
|
1940
|
+
case "f64":
|
|
1941
|
+
case "date":
|
|
1942
|
+
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
1943
|
+
return [` call void @scr_promise_fulfill_f64(ptr ${promise}, double %r)`];
|
|
1944
|
+
case "bool":
|
|
1945
|
+
this.declare(`declare void @scr_promise_fulfill_bool(ptr, i1 zeroext)`);
|
|
1946
|
+
return [` call void @scr_promise_fulfill_bool(ptr ${promise}, i1 %r)`];
|
|
1947
|
+
case "string":
|
|
1948
|
+
this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
|
|
1949
|
+
return [` call void @scr_promise_fulfill_str(ptr ${promise}, ptr %r) ; moves in`];
|
|
1950
|
+
case "dyn":
|
|
1951
|
+
this.declare(`declare void @scr_promise_resolve_dyn(ptr, ptr)`);
|
|
1952
|
+
return [` call void @scr_promise_resolve_dyn(ptr ${promise}, ptr %r) ; moves in`];
|
|
1953
|
+
default: {
|
|
1954
|
+
const v = vAdapters(this.shapeHost, ret);
|
|
1955
|
+
this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
|
|
1956
|
+
return [
|
|
1957
|
+
` call void @scr_promise_fulfill_ref(ptr ${promise}, ptr %r, ptr ${v.retain}, ptr ${v.release}, ptr ${traceArg(this.shapeHost, ret)})`,
|
|
1958
|
+
];
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
/** Async functions whose calls can skip the fiber (scr_async_inline_*).
|
|
1963
|
+
* A body qualifies only when nothing it runs can park its execution
|
|
1964
|
+
* context: its emitted text, and the text of every module function it
|
|
1965
|
+
* mentions (transitively), contains no suspension entry point. Such a
|
|
1966
|
+
* body always completes before the call returns, which is exactly the
|
|
1967
|
+
* behavior of a fiber that never suspends. Excluded conservatively:
|
|
1968
|
+
* wasm32 (its coroutine lowering owns async bodies), programs embedding
|
|
1969
|
+
* the dynamic engine, module-evaluation bodies (their promise caches),
|
|
1970
|
+
* `dyn` results (resolution may adopt a thenable), and every program
|
|
1971
|
+
* that uses node:test subtests — `t.test()` runs a subtest inline and
|
|
1972
|
+
* awaits it on the CURRENT fiber even from synchronous code. Worker
|
|
1973
|
+
* programs keep fibers too: their fiber epilogue owns context
|
|
1974
|
+
* termination (a stopping worker rejects and observes the promise), which
|
|
1975
|
+
* the fiberless wrapper does not reproduce. A missed suspension still
|
|
1976
|
+
* cannot reorder silently: the runtime traps when a context parks while a
|
|
1977
|
+
* fiberless body is on its stack. */
|
|
1978
|
+
fiberlessAsyncFunctions() {
|
|
1979
|
+
const eligible = new Set();
|
|
1980
|
+
if (this.wasi || this.mod.workers === true)
|
|
1981
|
+
return eligible;
|
|
1982
|
+
const embedded = this.mod.embedded;
|
|
1983
|
+
if (embedded !== undefined && embedded.modules.length > 0)
|
|
1984
|
+
return eligible;
|
|
1985
|
+
const suspends = /@scr_(?:await_|module_await\b|promise_await_settled\b|test_sub\b|gen_yield_|async_gen_|fiber_)/;
|
|
1986
|
+
for (const decl of this.decls)
|
|
1987
|
+
if (/@scr_test_sub\b/.test(decl))
|
|
1988
|
+
return eligible;
|
|
1989
|
+
for (const text of this.fnDefText.values())
|
|
1990
|
+
if (/@scr_test_sub\b/.test(text))
|
|
1991
|
+
return eligible;
|
|
1992
|
+
const byMangled = new Map();
|
|
1993
|
+
for (const fn of this.mod.functions) {
|
|
1994
|
+
byMangled.set(mangleFunction(fn.name), fn.name);
|
|
1995
|
+
byMangled.set(mangleBorrowedFunction(fn.name), fn.name);
|
|
1996
|
+
}
|
|
1997
|
+
// Reverse mention edges, then propagate "may park" to every mentioner.
|
|
1998
|
+
const mentionedBy = new Map();
|
|
1999
|
+
const parks = new Set();
|
|
2000
|
+
const work = [];
|
|
2001
|
+
for (const [name, text] of this.fnDefText) {
|
|
2002
|
+
if (suspends.test(text)) {
|
|
2003
|
+
parks.add(name);
|
|
2004
|
+
work.push(name);
|
|
2005
|
+
}
|
|
2006
|
+
for (const match of text.matchAll(/@(sc_b?f_[A-Za-z0-9_]+)/g)) {
|
|
2007
|
+
const callee = byMangled.get(match[1]);
|
|
2008
|
+
if (callee === undefined || callee === name)
|
|
2009
|
+
continue;
|
|
2010
|
+
let callers = mentionedBy.get(callee);
|
|
2011
|
+
if (callers === undefined)
|
|
2012
|
+
mentionedBy.set(callee, (callers = new Set()));
|
|
2013
|
+
callers.add(name);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
while (work.length > 0) {
|
|
2017
|
+
const callers = mentionedBy.get(work.pop());
|
|
2018
|
+
if (callers === undefined)
|
|
2019
|
+
continue;
|
|
2020
|
+
for (const caller of callers) {
|
|
2021
|
+
if (parks.has(caller))
|
|
2022
|
+
continue;
|
|
2023
|
+
parks.add(caller);
|
|
2024
|
+
work.push(caller);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
for (const fn of this.mod.functions) {
|
|
2028
|
+
if (fn.async !== true || fn.generator !== undefined)
|
|
2029
|
+
continue;
|
|
2030
|
+
if (fn.asyncCacheGlobal !== undefined || fn.asyncCycleCacheGlobal !== undefined)
|
|
2031
|
+
continue;
|
|
2032
|
+
if (fn.returnType.kind === "dyn" || parks.has(fn.name))
|
|
2033
|
+
continue;
|
|
2034
|
+
if (!this.fnDefText.has(fn.name))
|
|
2035
|
+
continue;
|
|
2036
|
+
eligible.add(fn.name);
|
|
2037
|
+
}
|
|
2038
|
+
return eligible;
|
|
2039
|
+
}
|
|
2040
|
+
/** The spawn wrapper of a fiberless async function: runs the body on the
|
|
2041
|
+
* caller's stack between scr_async_inline_enter/leave and settles the
|
|
2042
|
+
* fresh promise from its outcome — the fiber trampoline's epilogue,
|
|
2043
|
+
* without the fiber. */
|
|
2044
|
+
emitFiberlessAsyncSpawn(fn, spawnParams) {
|
|
2045
|
+
const ret = fn.returnType;
|
|
2046
|
+
const retTy = this.llType(ret);
|
|
2047
|
+
const lifted = fn.captures !== undefined;
|
|
2048
|
+
this.declare(`declare void @scr_async_inline_enter(ptr)`);
|
|
2049
|
+
this.declare(`declare ptr @scr_async_inline_leave(ptr)`);
|
|
2050
|
+
this.declare(`declare void @scr_promise_reject_pending(ptr)`);
|
|
2051
|
+
this.declare(`declare zeroext i1 @scr_exc_pending()`);
|
|
2052
|
+
if (lifted) {
|
|
2053
|
+
this.declare(`declare ptr @scr_closure_retain_v(ptr)`);
|
|
2054
|
+
this.declare(`declare void @scr_closure_release(ptr)`);
|
|
2055
|
+
}
|
|
2056
|
+
const bodyCall = `call ${retTy} @${mangleFunction(fn.name)}(${spawnParams.join(", ")})`;
|
|
2057
|
+
const out = [
|
|
2058
|
+
`define internal ptr @${mangleAsyncSpawn(fn.name)}(${spawnParams.join(", ")}) ${FN_ATTRS} { ; fiberless ${fn.name}`,
|
|
2059
|
+
`entry:`,
|
|
2060
|
+
` %frame = alloca { ptr, ptr }`,
|
|
2061
|
+
// The fiber path holds +1 on a lifted environment for the body's
|
|
2062
|
+
// duration (packed, released by the trampoline); keep that lifetime.
|
|
2063
|
+
...(lifted ? [` %env = call ptr @scr_closure_retain_v(ptr %a0)`] : []),
|
|
2064
|
+
` call void @scr_async_inline_enter(ptr %frame)`,
|
|
2065
|
+
retTy === "void" ? ` ${bodyCall}` : ` %r = ${bodyCall}`,
|
|
2066
|
+
...(lifted ? [` call void @scr_closure_release(ptr %a0)`] : []),
|
|
2067
|
+
` %pend = call zeroext i1 @scr_exc_pending()`,
|
|
2068
|
+
` %p = call ptr @scr_async_inline_leave(ptr %frame)`,
|
|
2069
|
+
` br i1 %pend, label %thrown, label %clean`,
|
|
2070
|
+
`clean:`,
|
|
2071
|
+
...this.asyncFulfillLines(ret, "%p"),
|
|
2072
|
+
` ret ptr %p`,
|
|
2073
|
+
`thrown:`,
|
|
2074
|
+
];
|
|
2075
|
+
if (ret.kind !== "void" && isRefCounted(ret)) {
|
|
2076
|
+
// An escaping throw means %r is the never-read dummy (NULL).
|
|
2077
|
+
out.push(` call void ${releaseSym(this.shapeHost, ret)}(ptr %r)`);
|
|
2078
|
+
}
|
|
2079
|
+
out.push(` call void @scr_promise_reject_pending(ptr %p)`, ` ret ptr %p`, `}`, ``);
|
|
2080
|
+
return out;
|
|
2081
|
+
}
|
|
1818
2082
|
/** Per-async-function machinery — async.ts's scaffolding, .ll
|
|
1819
2083
|
* flavored: an argument-pack struct type, a fiber trampoline (unpacks,
|
|
1820
2084
|
* frees the pack, runs the ordinary compiled body, settles the
|
|
@@ -1829,9 +2093,20 @@ export class LlEmitter {
|
|
|
1829
2093
|
this.declare(`declare void @llvm.coro.destroy(ptr)`);
|
|
1830
2094
|
out.push(`define void @scr_wasi_coro_resume(ptr %handle) ${FN_ATTRS} {`, `entry:`, ` call void @llvm.coro.resume(ptr %handle)`, ` ret void`, `}`, `define void @scr_wasi_coro_destroy(ptr %handle) ${FN_ATTRS} {`, `entry:`, ` call void @llvm.coro.destroy(ptr %handle)`, ` ret void`, `}`, ``);
|
|
1831
2095
|
}
|
|
2096
|
+
const fiberless = this.fiberlessAsyncFunctions();
|
|
1832
2097
|
for (const fn of this.mod.functions) {
|
|
1833
2098
|
if (fn.async !== true || fn.generator !== undefined)
|
|
1834
2099
|
continue;
|
|
2100
|
+
if (fiberless.has(fn.name)) {
|
|
2101
|
+
const fieldTys = [
|
|
2102
|
+
...(fn.captures !== undefined ? ["ptr"] : []),
|
|
2103
|
+
...fn.params.map((p) => this.llType(p.type)),
|
|
2104
|
+
];
|
|
2105
|
+
const params = fieldTys.map((ty, i) => `${ty} %a${i}`);
|
|
2106
|
+
for (const line of this.emitFiberlessAsyncSpawn(fn, params))
|
|
2107
|
+
out.push(line);
|
|
2108
|
+
continue;
|
|
2109
|
+
}
|
|
1835
2110
|
const { definitions, ret, tr, spawnParams, argPackLines } = this.emitArgPackAndTrampolinePrologue(fn);
|
|
1836
2111
|
for (const line of definitions)
|
|
1837
2112
|
out.push(line);
|
|
@@ -1849,34 +2124,8 @@ export class LlEmitter {
|
|
|
1849
2124
|
this.declare(`declare void @scr_closure_release(ptr)`);
|
|
1850
2125
|
}
|
|
1851
2126
|
tr.push(` %pend = call zeroext i1 @scr_exc_pending()`, ` br i1 %pend, label %thrown, label %clean`, `clean:`, ` %pr = call ptr @scr_fiber_promise(ptr %self)`);
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
this.declare(`declare void @scr_promise_fulfill_void(ptr)`);
|
|
1855
|
-
tr.push(` call void @scr_promise_fulfill_void(ptr %pr)`);
|
|
1856
|
-
break;
|
|
1857
|
-
case "f64":
|
|
1858
|
-
case "date":
|
|
1859
|
-
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
1860
|
-
tr.push(` call void @scr_promise_fulfill_f64(ptr %pr, double %r)`);
|
|
1861
|
-
break;
|
|
1862
|
-
case "bool":
|
|
1863
|
-
this.declare(`declare void @scr_promise_fulfill_bool(ptr, i1 zeroext)`);
|
|
1864
|
-
tr.push(` call void @scr_promise_fulfill_bool(ptr %pr, i1 %r)`);
|
|
1865
|
-
break;
|
|
1866
|
-
case "string":
|
|
1867
|
-
this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
|
|
1868
|
-
tr.push(` call void @scr_promise_fulfill_str(ptr %pr, ptr %r) ; moves in`);
|
|
1869
|
-
break;
|
|
1870
|
-
case "dyn":
|
|
1871
|
-
this.declare(`declare void @scr_promise_resolve_dyn(ptr, ptr)`);
|
|
1872
|
-
tr.push(` call void @scr_promise_resolve_dyn(ptr %pr, ptr %r) ; moves in`);
|
|
1873
|
-
break;
|
|
1874
|
-
default: {
|
|
1875
|
-
const v = vAdapters(this.shapeHost, ret);
|
|
1876
|
-
this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
|
|
1877
|
-
tr.push(` call void @scr_promise_fulfill_ref(ptr %pr, ptr %r, ptr ${v.retain}, ptr ${v.release}, ptr ${traceArg(this.shapeHost, ret)})`);
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
2127
|
+
for (const line of this.asyncFulfillLines(ret, "%pr"))
|
|
2128
|
+
tr.push(line);
|
|
1880
2129
|
tr.push(` ret void`, `thrown:`);
|
|
1881
2130
|
if (ret.kind !== "void" && isRefCounted(ret)) {
|
|
1882
2131
|
// An escaping throw means %r is the never-read dummy (NULL).
|
|
@@ -2115,6 +2364,40 @@ export class LlEmitter {
|
|
|
2115
2364
|
this.immortalValues.add(name);
|
|
2116
2365
|
return name;
|
|
2117
2366
|
}
|
|
2367
|
+
/** The immortal ABSENT state of an undefined-armed record field slot
|
|
2368
|
+
* (IR fieldAbsent). */
|
|
2369
|
+
absentInstanceRef(unionId) {
|
|
2370
|
+
if (undefinedArmTag({ kind: "union", unionId }, this.unionsById) < 0) {
|
|
2371
|
+
throw new InternalCompilerError(`llvm emitter bug: absent state of ${unionId} without undefined`);
|
|
2372
|
+
}
|
|
2373
|
+
let sym = this.absentInstances.get(unionId);
|
|
2374
|
+
if (!sym) {
|
|
2375
|
+
sym = `sc_absent_${this.absentInstances.size}`;
|
|
2376
|
+
this.absentInstances.set(unionId, sym);
|
|
2377
|
+
}
|
|
2378
|
+
const name = `@${sym}`;
|
|
2379
|
+
this.immortalValues.add(name);
|
|
2380
|
+
return name;
|
|
2381
|
+
}
|
|
2382
|
+
/** i1: a union value loaded from a field slot is the ABSENT state — the
|
|
2383
|
+
* undefined arm's tag with the absent payload marker. */
|
|
2384
|
+
fieldAbsentTest(value, unionId) {
|
|
2385
|
+
return emitFieldAbsentTest(this.B, value, undefinedArmTag({ kind: "union", unionId }, this.unionsById));
|
|
2386
|
+
}
|
|
2387
|
+
/** Loads a record field slot as a VALUE: an undefined-armed union slot
|
|
2388
|
+
* holding the ABSENT state surfaces as the ordinary undefined arm, so
|
|
2389
|
+
* absence never travels beyond its slot (a later `{ f: r.f }` is an
|
|
2390
|
+
* explicit, present property). */
|
|
2391
|
+
loadRecordField(ptr, t) {
|
|
2392
|
+
const v = this.loadField(ptr, t);
|
|
2393
|
+
if (t.kind !== "union" || undefinedArmTag(t, this.unionsById) < 0)
|
|
2394
|
+
return v;
|
|
2395
|
+
const absent = this.fieldAbsentTest(v, t.unionId);
|
|
2396
|
+
const unit = this.unitInstanceRef(t.unionId, undefinedArmTag(t, this.unionsById));
|
|
2397
|
+
const out = this.B.tmp();
|
|
2398
|
+
this.B.line(`${out} = select i1 ${absent}, ptr ${unit}, ptr ${v}`);
|
|
2399
|
+
return out;
|
|
2400
|
+
}
|
|
2118
2401
|
declare(decl) {
|
|
2119
2402
|
this.decls.add(decl);
|
|
2120
2403
|
}
|
|
@@ -2174,8 +2457,7 @@ export class LlEmitter {
|
|
|
2174
2457
|
const t = this.B.tmp();
|
|
2175
2458
|
this.B.line(`${t} = load ptr, ptr ${v.name}`);
|
|
2176
2459
|
if (v.boxed) {
|
|
2177
|
-
this.
|
|
2178
|
-
this.B.line(`call void @scr_box_release(ptr ${t})`);
|
|
2460
|
+
this.B.line(`call void ${boxReleaseSym(this.shapeHost)}(ptr ${t})`);
|
|
2179
2461
|
}
|
|
2180
2462
|
else {
|
|
2181
2463
|
this.releaseValue(t, v.type);
|
|
@@ -2191,8 +2473,7 @@ export class LlEmitter {
|
|
|
2191
2473
|
const t = this.B.tmp();
|
|
2192
2474
|
this.B.line(`${t} = load ptr, ptr ${e.slot}`);
|
|
2193
2475
|
if (e.boxed) {
|
|
2194
|
-
this.
|
|
2195
|
-
this.B.line(`call void @scr_box_release(ptr ${t})`);
|
|
2476
|
+
this.B.line(`call void ${boxReleaseSym(this.shapeHost)}(ptr ${t})`);
|
|
2196
2477
|
}
|
|
2197
2478
|
else {
|
|
2198
2479
|
this.releaseValue(t, e.type); // runtime releases are NULL-tolerant
|
|
@@ -2272,13 +2553,13 @@ export class LlEmitter {
|
|
|
2272
2553
|
terminator = `br label %${this.currentWasiCoro.finalLabel}`;
|
|
2273
2554
|
}
|
|
2274
2555
|
else {
|
|
2275
|
-
const t = this.currentReturnType;
|
|
2556
|
+
const t = this.llType(this.currentReturnType);
|
|
2276
2557
|
terminator =
|
|
2277
|
-
t
|
|
2558
|
+
t === "void"
|
|
2278
2559
|
? "ret void"
|
|
2279
|
-
: t
|
|
2560
|
+
: t === "double"
|
|
2280
2561
|
? `ret double ${f64Lit(0)}`
|
|
2281
|
-
: t
|
|
2562
|
+
: t === "i1"
|
|
2282
2563
|
? "ret i1 false"
|
|
2283
2564
|
: "ret ptr null";
|
|
2284
2565
|
}
|
|
@@ -2424,14 +2705,10 @@ export class LlEmitter {
|
|
|
2424
2705
|
this.unionTagSwitch(v.name, def, (arm) => {
|
|
2425
2706
|
let valueName = "false";
|
|
2426
2707
|
if (arm.kind === "f64" || arm.kind === "procStream") {
|
|
2427
|
-
valueName =
|
|
2428
|
-
this.declare(`declare double @scr_union_get_f64(ptr)`);
|
|
2429
|
-
B.line(`${valueName} = call double @scr_union_get_f64(ptr ${v.name})`);
|
|
2708
|
+
valueName = this.unionGetF64(v.name);
|
|
2430
2709
|
}
|
|
2431
2710
|
else if (arm.kind === "bool") {
|
|
2432
|
-
valueName =
|
|
2433
|
-
this.declare(`declare zeroext i1 @scr_union_get_bool(ptr)`);
|
|
2434
|
-
B.line(`${valueName} = call zeroext i1 @scr_union_get_bool(ptr ${v.name})`);
|
|
2711
|
+
valueName = this.unionGetBool(v.name);
|
|
2435
2712
|
}
|
|
2436
2713
|
else if (arm.kind === "string") {
|
|
2437
2714
|
valueName = this.unionPeek(v.name);
|
|
@@ -2610,24 +2887,34 @@ export class LlEmitter {
|
|
|
2610
2887
|
B.terminate(`unreachable`);
|
|
2611
2888
|
}
|
|
2612
2889
|
/** The +1 extraction of a union's single narrowed arm (unionNarrow /
|
|
2613
|
-
* the nullish-family reads): scalars via
|
|
2890
|
+
* the nullish-family reads): scalars via inline payload loads, ref arms
|
|
2614
2891
|
* a retained peek. */
|
|
2615
2892
|
unionExtract(uName, arm) {
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
this.
|
|
2620
|
-
B.line(`${t} = call double @scr_union_get_f64(ptr ${uName})`);
|
|
2621
|
-
return t;
|
|
2622
|
-
}
|
|
2623
|
-
if (arm.kind === "bool") {
|
|
2624
|
-
const t = B.tmp();
|
|
2625
|
-
this.declare(`declare zeroext i1 @scr_union_get_bool(ptr)`);
|
|
2626
|
-
B.line(`${t} = call zeroext i1 @scr_union_get_bool(ptr ${uName})`);
|
|
2627
|
-
return t;
|
|
2628
|
-
}
|
|
2893
|
+
if (arm.kind === "f64" || arm.kind === "procStream")
|
|
2894
|
+
return this.unionGetF64(uName);
|
|
2895
|
+
if (arm.kind === "bool")
|
|
2896
|
+
return this.unionGetBool(uName);
|
|
2629
2897
|
return this.retainValue(this.unionPeek(uName), arm);
|
|
2630
2898
|
}
|
|
2899
|
+
/** A scalar f64 arm's payload (scr_union_get_f64 inlined). Inline loads
|
|
2900
|
+
* keep stack boxes free of escaping calls, so LLVM can scalarize them. */
|
|
2901
|
+
unionGetF64(uName) {
|
|
2902
|
+
const p = this.B.tmp();
|
|
2903
|
+
const t = this.B.tmp();
|
|
2904
|
+
this.B.line(`${p} = getelementptr inbounds %ScrUnion, ptr ${uName}, i64 0, i32 5`);
|
|
2905
|
+
this.B.line(`${t} = load double, ptr ${p}`);
|
|
2906
|
+
return t;
|
|
2907
|
+
}
|
|
2908
|
+
/** A bool arm's payload (scr_union_get_bool inlined: nonzero slot). */
|
|
2909
|
+
unionGetBool(uName) {
|
|
2910
|
+
const p = this.B.tmp();
|
|
2911
|
+
const bits = this.B.tmp();
|
|
2912
|
+
const t = this.B.tmp();
|
|
2913
|
+
this.B.line(`${p} = getelementptr inbounds %ScrUnion, ptr ${uName}, i64 0, i32 5`);
|
|
2914
|
+
this.B.line(`${bits} = load i64, ptr ${p}`);
|
|
2915
|
+
this.B.line(`${t} = icmp ne i64 ${bits}, 0`);
|
|
2916
|
+
return t;
|
|
2917
|
+
}
|
|
2631
2918
|
/** Constructs a union box around an OWNED (+1, already moved) value —
|
|
2632
2919
|
* the scr_union_new_* dispatch of unionWrap and the wrap-into-join
|
|
2633
2920
|
* sites (shift). */
|
|
@@ -2806,17 +3093,26 @@ export class LlEmitter {
|
|
|
2806
3093
|
this.B.line(`${b} = load ptr, ptr ${slot}`);
|
|
2807
3094
|
return b;
|
|
2808
3095
|
}
|
|
3096
|
+
/** The payload slot of a capture box (ScrBox.slot, field 5). */
|
|
3097
|
+
boxSlot(box) {
|
|
3098
|
+
const slot = this.B.tmp();
|
|
3099
|
+
this.B.line(`${slot} = getelementptr inbounds %ScrBox, ptr ${box}, i32 0, i32 5`);
|
|
3100
|
+
return slot;
|
|
3101
|
+
}
|
|
2809
3102
|
boxGet(box, t) {
|
|
2810
3103
|
const B = this.B;
|
|
2811
3104
|
const acc = boxAccess(t);
|
|
2812
3105
|
const r = B.tmp();
|
|
3106
|
+
// Scalar payloads are read in place (scr_box_get_f64/bool are a
|
|
3107
|
+
// memcpy and a nonzero test of the same slot), so LLVM can keep a
|
|
3108
|
+
// captured number in a register across a loop of calls.
|
|
2813
3109
|
if (acc === "f64") {
|
|
2814
|
-
|
|
2815
|
-
B.line(`${r} = call double @scr_box_get_f64(ptr ${box})`);
|
|
3110
|
+
B.line(`${r} = load double, ptr ${this.boxSlot(box)}`);
|
|
2816
3111
|
}
|
|
2817
3112
|
else if (acc === "bool") {
|
|
2818
|
-
|
|
2819
|
-
B.line(`${
|
|
3113
|
+
const raw = B.tmp();
|
|
3114
|
+
B.line(`${raw} = load i64, ptr ${this.boxSlot(box)}`);
|
|
3115
|
+
B.line(`${r} = icmp ne i64 ${raw}, 0`);
|
|
2820
3116
|
}
|
|
2821
3117
|
else {
|
|
2822
3118
|
this.declare(`declare ptr @scr_box_get_ref(ptr)`);
|
|
@@ -2829,18 +3125,45 @@ export class LlEmitter {
|
|
|
2829
3125
|
const B = this.B;
|
|
2830
3126
|
const acc = boxAccess(t);
|
|
2831
3127
|
if (acc === "f64") {
|
|
2832
|
-
|
|
2833
|
-
B.line(`call void @scr_box_set_f64(ptr ${box}, double ${value})`);
|
|
3128
|
+
B.line(`store double ${value}, ptr ${this.boxSlot(box)}`);
|
|
2834
3129
|
}
|
|
2835
3130
|
else if (acc === "bool") {
|
|
2836
|
-
|
|
2837
|
-
B.line(
|
|
3131
|
+
const raw = B.tmp();
|
|
3132
|
+
B.line(`${raw} = zext i1 ${value} to i64`);
|
|
3133
|
+
B.line(`store i64 ${raw}, ptr ${this.boxSlot(box)}`);
|
|
2838
3134
|
}
|
|
2839
3135
|
else {
|
|
2840
3136
|
this.declare(`declare void @scr_box_set_ref(ptr, ptr)`);
|
|
2841
3137
|
B.line(`call void @scr_box_set_ref(ptr ${box}, ptr ${value})`);
|
|
2842
3138
|
}
|
|
2843
3139
|
}
|
|
3140
|
+
/** The box a new environment captures for `localId` (borrowed). An
|
|
3141
|
+
* unchanged parameter gets one box on first capture, holding its own
|
|
3142
|
+
* reference to the value; the function scope releases the cached box. */
|
|
3143
|
+
captureBox(localId) {
|
|
3144
|
+
const cache = this.lazyCaptureBoxes.get(localId);
|
|
3145
|
+
if (cache === undefined)
|
|
3146
|
+
return this.loadBox(`%${mangleLocal(localId)}`);
|
|
3147
|
+
const B = this.B;
|
|
3148
|
+
const type = this.currentLocals.get(localId).type;
|
|
3149
|
+
const current = this.loadBox(cache);
|
|
3150
|
+
const missing = B.tmp();
|
|
3151
|
+
B.line(`${missing} = icmp eq ptr ${current}, null`);
|
|
3152
|
+
const create = B.newLabel("lazy.box"), join = B.newLabel("lazy.box.j");
|
|
3153
|
+
B.condBr(missing, create, join);
|
|
3154
|
+
B.startBlock(create);
|
|
3155
|
+
const stack = this.lazyStackBoxes.get(localId);
|
|
3156
|
+
const box = stack ?? B.tmp();
|
|
3157
|
+
if (!stack)
|
|
3158
|
+
B.line(`${box} = ${boxNewCall(this.shapeHost, type)}`);
|
|
3159
|
+
const value = B.tmp();
|
|
3160
|
+
B.line(`${value} = load ${this.llType(type)}, ptr %${mangleLocal(localId)}`);
|
|
3161
|
+
this.boxSet(box, type, isRefCounted(type) ? this.retainValue(value, type) : value);
|
|
3162
|
+
B.line(`store ptr ${box}, ptr ${cache}`);
|
|
3163
|
+
B.br(join);
|
|
3164
|
+
B.startBlock(join);
|
|
3165
|
+
return this.loadBox(cache);
|
|
3166
|
+
}
|
|
2844
3167
|
retainBox(box) {
|
|
2845
3168
|
this.needsRetainBox = true;
|
|
2846
3169
|
const t = this.B.tmp();
|
|
@@ -2885,6 +3208,12 @@ export class LlEmitter {
|
|
|
2885
3208
|
B.line(`${empty} = icmp eq ptr ${value}, null`);
|
|
2886
3209
|
this.throwIfUninitialized(empty, name);
|
|
2887
3210
|
}
|
|
3211
|
+
/** Throws the TDZ ReferenceError for `name` unless the i1 `flag` is set. */
|
|
3212
|
+
checkInitializedFlag(flag, name) {
|
|
3213
|
+
const empty = this.B.tmp();
|
|
3214
|
+
this.B.line(`${empty} = xor i1 ${flag}, true`);
|
|
3215
|
+
this.throwIfUninitialized(empty, name);
|
|
3216
|
+
}
|
|
2888
3217
|
throwIfUninitialized(empty, name) {
|
|
2889
3218
|
const B = this.B;
|
|
2890
3219
|
const lt = B.newLabel("tdz.t");
|
|
@@ -3053,13 +3382,23 @@ export class LlEmitter {
|
|
|
3053
3382
|
this.unwindCleanups.clear();
|
|
3054
3383
|
this.jumpTargets = [];
|
|
3055
3384
|
this.currentLocals = new Map(fn.locals.map((l) => [l.id, l]));
|
|
3385
|
+
this.lazyCaptureBoxes.clear();
|
|
3386
|
+
this.lazyStackBoxes.clear();
|
|
3387
|
+
// An unchanged captured parameter keeps its value in a plain slot; the
|
|
3388
|
+
// binding is boxed only for closures (captureBox).
|
|
3389
|
+
const lazyParameters = this.lazyCaptures.parameters(fn);
|
|
3390
|
+
for (const id of lazyParameters) {
|
|
3391
|
+
const { boxed: _boxed, ...plain } = this.currentLocals.get(id);
|
|
3392
|
+
this.currentLocals.set(id, plain);
|
|
3393
|
+
this.lazyCaptureBoxes.set(id, "");
|
|
3394
|
+
}
|
|
3056
3395
|
this.currentConstantCallbacks =
|
|
3057
3396
|
this.debug === null ? (this.constantCallbacks.get(fn.name) ?? new Map()) : new Map();
|
|
3058
3397
|
// Preserve concrete source bindings for debugger inspection. Suspended
|
|
3059
3398
|
// functions keep the established array lifetime across continuations.
|
|
3060
3399
|
this.streamingSplitsEnabled = this.debug === null && !fn.async && !fn.generator;
|
|
3061
3400
|
this.stackCallbacks.reset(this.streamingSplitsEnabled);
|
|
3062
|
-
this.stackCaptures.reset(fn, this.streamingSplitsEnabled);
|
|
3401
|
+
this.stackCaptures.reset(fn, this.streamingSplitsEnabled, lazyParameters);
|
|
3063
3402
|
this.privateSplitLocals = this.streamingSplitsEnabled ? findPrivateSplitLocals(fn) : new Map();
|
|
3064
3403
|
this.storedSplits.clear();
|
|
3065
3404
|
this.splitSpans.clear();
|
|
@@ -3068,6 +3407,11 @@ export class LlEmitter {
|
|
|
3068
3407
|
const numericFn = withInitializerBindings(fn, initializerBindings);
|
|
3069
3408
|
this.numericLocals = new Map(numericFn.locals.map((l) => [l.id, l]));
|
|
3070
3409
|
this.borrowedParameters.clear();
|
|
3410
|
+
this.projectedParameters.clear();
|
|
3411
|
+
const projectedParameterIndexes = this.callLifetimes.parameters.get(fn.name);
|
|
3412
|
+
if (projectedParameterIndexes)
|
|
3413
|
+
for (const index of projectedParameterIndexes)
|
|
3414
|
+
this.projectedParameters.add(fn.params[index].localId);
|
|
3071
3415
|
this.stableCallBindings = this.callLifetimes.bindings.get(fn.name) ?? new Set();
|
|
3072
3416
|
const borrowedParameterIndexes = this.callLifetimes.borrowed.get(fn.name);
|
|
3073
3417
|
if (borrowedParameterIndexes) {
|
|
@@ -3095,7 +3439,7 @@ export class LlEmitter {
|
|
|
3095
3439
|
this.debug === null ? findScalarStringSlices(fn, this.callLifetimes) : new Map();
|
|
3096
3440
|
this.mapReadLifetimes = findMapReadLifetimes(fn, this.unionsById, this.callLifetimes);
|
|
3097
3441
|
this.localStackUnions = findLocalStackUnions(fn, this.callLifetimes, this.unionsById);
|
|
3098
|
-
this.localUnionStorageProofs = findLocalUnionStorage(fn, this.callLifetimes, this.unionsById);
|
|
3442
|
+
this.localUnionStorageProofs = findLocalUnionStorage(fn, this.callLifetimes, this.unionsById, (e) => this.nullableFieldGet(e) !== null);
|
|
3099
3443
|
this.localUnionStorage = new Map();
|
|
3100
3444
|
this.integerRanges = analyzeIntegerRanges(numericFn);
|
|
3101
3445
|
this.bytesBounds = findBytesBounds(numericFn, this.integerRanges);
|
|
@@ -3154,7 +3498,7 @@ export class LlEmitter {
|
|
|
3154
3498
|
B.line(`${value} = load double, ptr @${mangleGlobal(global.id)}`);
|
|
3155
3499
|
this.storeIntegerView(global.id, { name: value, type: global.type });
|
|
3156
3500
|
}
|
|
3157
|
-
for (const local of
|
|
3501
|
+
for (const local of this.currentLocals.values()) {
|
|
3158
3502
|
// Boxed locals' slots hold their capture BOX (a ptr); captured
|
|
3159
3503
|
// (env-borrowed) locals bind the incoming box below. A caught-typed
|
|
3160
3504
|
// local is a catch binding: its slot holds the ScrCaught snapshot
|
|
@@ -3203,6 +3547,21 @@ export class LlEmitter {
|
|
|
3203
3547
|
// params (callees own their params — callers passed +1). Boxed params
|
|
3204
3548
|
// allocate the shared binding and move the raw value in.
|
|
3205
3549
|
const fnScope = [];
|
|
3550
|
+
for (const id of this.lazyCaptureBoxes.keys()) {
|
|
3551
|
+
const cache = B.slot();
|
|
3552
|
+
B.entryAllocas.push(`${cache} = alloca ptr ; lazy box ${this.currentLocals.get(id).name}`);
|
|
3553
|
+
B.line(`store ptr null, ptr ${cache}`);
|
|
3554
|
+
fnScope.push({ slot: cache, type: this.currentLocals.get(id).type, boxed: true });
|
|
3555
|
+
this.lazyCaptureBoxes.set(id, cache);
|
|
3556
|
+
// Environments that finish during the call can share an immortal
|
|
3557
|
+
// frame box. Its payload owner is released with the function scope.
|
|
3558
|
+
const stack = this.stackCaptures.emit(this, id, this.currentLocals.get(id).type);
|
|
3559
|
+
if (stack) {
|
|
3560
|
+
this.lazyStackBoxes.set(id, stack.box);
|
|
3561
|
+
if (stack.owner)
|
|
3562
|
+
fnScope.push(stack.owner);
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3206
3565
|
const borrowed = this.callLifetimes.borrowed.get(fn.name);
|
|
3207
3566
|
for (const [index, p] of fn.params.entries()) {
|
|
3208
3567
|
const local = this.currentLocals.get(p.localId);
|
|
@@ -3562,9 +3921,7 @@ export class LlEmitter {
|
|
|
3562
3921
|
const acc = elemAccess(s.arr.type.elem);
|
|
3563
3922
|
if (acc === "ref")
|
|
3564
3923
|
this.moveTemp(v);
|
|
3565
|
-
|
|
3566
|
-
this.declare(`declare void @scr_arr_set_${acc}(ptr, double, ${argTy === "i1" ? "i1 zeroext" : argTy})`);
|
|
3567
|
-
B.line(`call void @scr_arr_set_${acc}(ptr ${arr.name}, double ${idx.name}, ${argTy} ${v.name})`);
|
|
3924
|
+
emitDenseArraySet(this, arr.name, idx, s.index, acc, s.arr.type.elem, v.name);
|
|
3568
3925
|
break;
|
|
3569
3926
|
}
|
|
3570
3927
|
case "arraySetLength": {
|
|
@@ -3612,6 +3969,12 @@ export class LlEmitter {
|
|
|
3612
3969
|
// release — a release can trigger a cycle collection, which must
|
|
3613
3970
|
// never see a heap edge whose count was already given up).
|
|
3614
3971
|
// Classes and records share the struct layout, so one emission.
|
|
3972
|
+
const nullable = s.kind === "fieldSet" ? this.nullableFields.get(s.className, s.field) : null;
|
|
3973
|
+
if (nullable && s.kind === "fieldSet") {
|
|
3974
|
+
const obj = this.emitExpr(s.obj);
|
|
3975
|
+
this.emitNullableFieldStore(obj.name, s.className, s.field, nullable, s.value);
|
|
3976
|
+
break;
|
|
3977
|
+
}
|
|
3615
3978
|
const obj = isRefCounted(s.value.type)
|
|
3616
3979
|
? this.emitExpr(s.obj)
|
|
3617
3980
|
: this.emitStableReceiver(s.obj, [s.value]);
|
|
@@ -3695,7 +4058,7 @@ export class LlEmitter {
|
|
|
3695
4058
|
for (const f of shape.fields) {
|
|
3696
4059
|
const lit = this.internLiteral(f.name);
|
|
3697
4060
|
const hit = B.tmp();
|
|
3698
|
-
B.line(`${hit} = call zeroext i1 @
|
|
4061
|
+
B.line(`${hit} = call zeroext i1 @sc_str_eq(ptr ${key.name}, ptr ${lit}) ; ${llvmCommentText(f.name)}`);
|
|
3699
4062
|
const lh = B.newLabel("rks.h");
|
|
3700
4063
|
const ln = B.newLabel("rks.n");
|
|
3701
4064
|
B.condBr(hit, lh, ln);
|
|
@@ -3751,7 +4114,7 @@ export class LlEmitter {
|
|
|
3751
4114
|
// typeEquals(f.type, iv) — the frontend fences everything else.
|
|
3752
4115
|
const lit = this.internLiteral(f.name);
|
|
3753
4116
|
const hit = B.tmp();
|
|
3754
|
-
B.line(`${hit} = call zeroext i1 @
|
|
4117
|
+
B.line(`${hit} = call zeroext i1 @sc_str_eq(ptr ${key.name}, ptr ${lit}) ; ${llvmCommentText(f.name)}`);
|
|
3755
4118
|
const lh = B.newLabel("rks.h");
|
|
3756
4119
|
const ln = B.newLabel("rks.n");
|
|
3757
4120
|
B.condBr(hit, lh, ln);
|
|
@@ -4125,10 +4488,13 @@ export class LlEmitter {
|
|
|
4125
4488
|
B.line(`${inBounds} = icmp ne ptr ${cur}, null`);
|
|
4126
4489
|
}
|
|
4127
4490
|
else {
|
|
4128
|
-
const i = B.tmp(), len = B.tmp();
|
|
4129
|
-
this.declare(`declare double @scr_arr_len(ptr)`);
|
|
4491
|
+
const i = B.tmp(), lenPtr = B.tmp(), rawLen = B.tmp(), len = B.tmp();
|
|
4130
4492
|
B.line(`${i} = load double, ptr ${idxSlot}`);
|
|
4131
|
-
|
|
4493
|
+
// The inline form of scr_arr_len, like the `length` intrinsic.
|
|
4494
|
+
B.line(`${lenPtr} = getelementptr inbounds %ScrArr, ptr ${arr.name}, i32 0, i32 1`);
|
|
4495
|
+
this.markMemoryPointer(lenPtr, "array:header");
|
|
4496
|
+
B.line(`${rawLen} = load ${this.sizeType}, ptr ${lenPtr}${this.fieldAliasAttachment(lenPtr)}`);
|
|
4497
|
+
B.line(`${len} = uitofp ${this.sizeType} ${rawLen} to double`);
|
|
4132
4498
|
B.line(`${inBounds} = fcmp olt double ${i}, ${len}`);
|
|
4133
4499
|
cur = i;
|
|
4134
4500
|
}
|
|
@@ -4152,12 +4518,8 @@ export class LlEmitter {
|
|
|
4152
4518
|
const borrowedElement = !snapshot && this.loopArrayBorrows.has(s);
|
|
4153
4519
|
if (!snapshot) {
|
|
4154
4520
|
const acc = elemAccess(elem);
|
|
4155
|
-
const accTy = acc === "f64" ? "double" : acc === "bool" ? "i1" : "ptr";
|
|
4156
4521
|
const getter = borrowedElement ? "scr_arr_borrow_ref" : `scr_arr_get_${acc}`;
|
|
4157
|
-
this.
|
|
4158
|
-
const value = B.tmp();
|
|
4159
|
-
B.line(`${value} = call ${accTy} @${getter}(ptr ${arr.name}, double ${cur})`);
|
|
4160
|
-
cur = value;
|
|
4522
|
+
cur = emitDenseArrayGet(this, arr.name, { name: cur, type: { kind: "f64" } }, undefined, acc, elem, getter, !borrowedElement);
|
|
4161
4523
|
}
|
|
4162
4524
|
if (spanLength) {
|
|
4163
4525
|
const length = B.tmp();
|
|
@@ -4553,7 +4915,7 @@ export class LlEmitter {
|
|
|
4553
4915
|
const hit = B.tmp();
|
|
4554
4916
|
if (c.test.type.kind === "string") {
|
|
4555
4917
|
this.declare(`declare zeroext i1 @scr_str_eq(ptr, ptr)`);
|
|
4556
|
-
B.line(`${hit} = call zeroext i1 @
|
|
4918
|
+
B.line(`${hit} = call zeroext i1 @sc_str_eq(ptr ${disc.name}, ptr ${t.name})`);
|
|
4557
4919
|
}
|
|
4558
4920
|
else if (c.test.type.kind === "bool") {
|
|
4559
4921
|
B.line(`${hit} = icmp eq i1 ${disc.name}, ${t.name}`);
|
|
@@ -4751,17 +5113,26 @@ export class LlEmitter {
|
|
|
4751
5113
|
this.ownSlot(result.owner.slot, result.owner.type);
|
|
4752
5114
|
return result.value;
|
|
4753
5115
|
}
|
|
5116
|
+
const arrayRead = this.optionalArrayReads.get(e);
|
|
5117
|
+
if (arrayRead)
|
|
5118
|
+
return emitProjectedArrayRead(this, arrayRead);
|
|
4754
5119
|
if (canStackUnion(e, this.unionsById))
|
|
4755
5120
|
return emitStackUnion(this, e).value;
|
|
4756
5121
|
if (e.kind === "unionNarrow") {
|
|
4757
|
-
const union = this.
|
|
5122
|
+
const union = this.emitUnionProjection(e.value);
|
|
4758
5123
|
return { name: this.unionPeek(union.name), type: e.type };
|
|
4759
5124
|
}
|
|
5125
|
+
// A nullable-pointer field has no box to borrow. Projections use
|
|
5126
|
+
// emitUnionProjection; other borrowers get an owned heap box.
|
|
5127
|
+
if (this.nullableFieldGet(e))
|
|
5128
|
+
return this.emitExpr(e);
|
|
4760
5129
|
if (e.kind === "recordGet" || e.kind === "fieldGet") {
|
|
4761
5130
|
const receiver = this.emitReadReceiver(e.obj);
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
: this.
|
|
5131
|
+
if (e.kind === "recordGet") {
|
|
5132
|
+
const { ptr, type } = this.recordFieldPtr(receiver.name, e.shapeId, e.field);
|
|
5133
|
+
return { name: this.loadRecordField(ptr, type), type: e.type };
|
|
5134
|
+
}
|
|
5135
|
+
const { ptr, type } = this.classFieldPtr(receiver.name, e.className, e.field);
|
|
4765
5136
|
return { name: this.loadField(ptr, type), type: e.type };
|
|
4766
5137
|
}
|
|
4767
5138
|
if (e.kind === "ternary" && this.canBorrowReceiver(e.then) && this.canBorrowReceiver(e.else_)) {
|
|
@@ -4791,6 +5162,153 @@ export class LlEmitter {
|
|
|
4791
5162
|
}
|
|
4792
5163
|
return this.emitExpr(e);
|
|
4793
5164
|
}
|
|
5165
|
+
/** A private stack union source with an independently owned payload. */
|
|
5166
|
+
canStackReceiver(e) {
|
|
5167
|
+
return this.isStackUnionSource(e);
|
|
5168
|
+
}
|
|
5169
|
+
/** True for union sources that emitReadReceiver can produce as a private
|
|
5170
|
+
* stack box with an independently owned payload (no heap union). */
|
|
5171
|
+
isStackUnionSource(e) {
|
|
5172
|
+
if (e.type.kind !== "union")
|
|
5173
|
+
return false;
|
|
5174
|
+
return (canStackUnion(e, this.unionsById) ||
|
|
5175
|
+
matchMapRead(e, this.unionsById) !== null ||
|
|
5176
|
+
this.optionalArrayReads.get(e) !== null);
|
|
5177
|
+
}
|
|
5178
|
+
/** The nullable-pointer representation behind a union-typed class field
|
|
5179
|
+
* read, or null for every other expression. */
|
|
5180
|
+
nullableFieldGet(e) {
|
|
5181
|
+
if (e.kind !== "fieldGet" || e.type.kind !== "union")
|
|
5182
|
+
return null;
|
|
5183
|
+
return this.nullableFields.get(e.className, e.field);
|
|
5184
|
+
}
|
|
5185
|
+
/** A union operand consumed only by a projection (tag test, payload
|
|
5186
|
+
* extraction, arm compare, truthiness). A nullable-pointer field read
|
|
5187
|
+
* becomes a private stack box whose payload is borrowed from the field:
|
|
5188
|
+
* the consumer must finish before any later expression can store to the
|
|
5189
|
+
* field, and must never retain, release, or store the box. Every other
|
|
5190
|
+
* operand takes the ordinary borrowed-receiver path. */
|
|
5191
|
+
emitUnionProjection(e) {
|
|
5192
|
+
const nullable = this.nullableFieldGet(e);
|
|
5193
|
+
if (!nullable || e.kind !== "fieldGet")
|
|
5194
|
+
return this.emitReadReceiver(e);
|
|
5195
|
+
const B = this.B;
|
|
5196
|
+
const receiver = this.emitReadReceiver(e.obj);
|
|
5197
|
+
const { ptr } = this.classFieldPtr(receiver.name, e.className, e.field);
|
|
5198
|
+
const p = B.tmp();
|
|
5199
|
+
B.line(`${p} = load ptr, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
5200
|
+
return { name: this.stackNullableBox(p, nullable), type: e.type };
|
|
5201
|
+
}
|
|
5202
|
+
/** A private stack box over a nullable-pointer field read whose payload
|
|
5203
|
+
* is retained into the current frame: an independent owner, so the box
|
|
5204
|
+
* may outlive later stores to the field (call arguments, local copies).
|
|
5205
|
+
* The box itself must still never reach an RC entry point. */
|
|
5206
|
+
emitOwnedNullableStack(e) {
|
|
5207
|
+
const nullable = this.nullableFieldGet(e);
|
|
5208
|
+
if (!nullable || e.kind !== "fieldGet")
|
|
5209
|
+
throw new InternalCompilerError("llvm emitter bug: owned nullable stack of non-field");
|
|
5210
|
+
const receiver = this.emitReadReceiver(e.obj);
|
|
5211
|
+
const { ptr } = this.classFieldPtr(receiver.name, e.className, e.field);
|
|
5212
|
+
const p = this.B.tmp();
|
|
5213
|
+
this.B.line(`${p} = load ptr, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
5214
|
+
const owner = this.own({ name: this.retainValue(p, nullable.arm), type: nullable.arm });
|
|
5215
|
+
return { box: this.stackNullableBox(owner.name, nullable), owner };
|
|
5216
|
+
}
|
|
5217
|
+
/** A private stack box over a borrowed nullable pointer. */
|
|
5218
|
+
stackNullableBox(p, nullable) {
|
|
5219
|
+
const B = this.B;
|
|
5220
|
+
const box = B.slot();
|
|
5221
|
+
B.entryAllocas.push(`${box} = alloca %ScrUnion`);
|
|
5222
|
+
const isNull = B.tmp(), tag = B.tmp(), tagPtr = B.tmp(), slot = B.tmp();
|
|
5223
|
+
B.line(`${isNull} = icmp eq ptr ${p}, null`);
|
|
5224
|
+
B.line(`${tag} = select i1 ${isNull}, i32 ${nullable.unitTag}, i32 ${nullable.refTag}`);
|
|
5225
|
+
B.line(`${tagPtr} = getelementptr inbounds %ScrUnion, ptr ${box}, i64 0, i32 1`);
|
|
5226
|
+
B.line(`store i32 ${tag}, ptr ${tagPtr}`);
|
|
5227
|
+
B.line(`${slot} = getelementptr inbounds %ScrUnion, ptr ${box}, i64 0, i32 5`);
|
|
5228
|
+
B.line(`store i64 0, ptr ${slot}`);
|
|
5229
|
+
B.line(`store ptr ${p}, ptr ${slot}`);
|
|
5230
|
+
return box;
|
|
5231
|
+
}
|
|
5232
|
+
/** The owned (+1) union value of a nullable-pointer slot: the unit arm's
|
|
5233
|
+
* immortal instance, or a fresh heap box around a retained instance. */
|
|
5234
|
+
nullableToOwnedUnion(p, nullable) {
|
|
5235
|
+
const B = this.B;
|
|
5236
|
+
const slot = B.slot();
|
|
5237
|
+
B.entryAllocas.push(`${slot} = alloca ptr`);
|
|
5238
|
+
const isNull = B.tmp();
|
|
5239
|
+
const unit = B.newLabel("nf.u"), ref = B.newLabel("nf.r"), join = B.newLabel("nf.j");
|
|
5240
|
+
B.line(`${isNull} = icmp eq ptr ${p}, null`);
|
|
5241
|
+
B.condBr(isNull, unit, ref);
|
|
5242
|
+
B.startBlock(unit);
|
|
5243
|
+
B.line(`store ptr ${this.unitInstanceRef(nullable.unionId, nullable.unitTag)}, ptr ${slot}`);
|
|
5244
|
+
B.br(join);
|
|
5245
|
+
B.startBlock(ref);
|
|
5246
|
+
const owned = this.retainValue(p, nullable.arm);
|
|
5247
|
+
const box = this.unionNewOwned(nullable.refTag, { name: owned, type: nullable.arm });
|
|
5248
|
+
B.line(`store ptr ${box}, ptr ${slot}`);
|
|
5249
|
+
B.br(join);
|
|
5250
|
+
B.startBlock(join);
|
|
5251
|
+
const t = B.tmp();
|
|
5252
|
+
B.line(`${t} = load ptr, ptr ${slot}`);
|
|
5253
|
+
return t;
|
|
5254
|
+
}
|
|
5255
|
+
/** The owned (+1) instance pointer (NULL for the unit arm) of a union
|
|
5256
|
+
* box, which stays owned by its existing owner. */
|
|
5257
|
+
unionToNullable(u, nullable) {
|
|
5258
|
+
const B = this.B;
|
|
5259
|
+
const tag = this.unionTag(u);
|
|
5260
|
+
const isRef = B.tmp(), sel = B.tmp();
|
|
5261
|
+
B.line(`${isRef} = icmp eq i32 ${tag}, ${nullable.refTag}`);
|
|
5262
|
+
const payload = this.unionPeek(u);
|
|
5263
|
+
B.line(`${sel} = select i1 ${isRef}, ptr ${payload}, ptr null`);
|
|
5264
|
+
return this.retainValue(sel, nullable.arm);
|
|
5265
|
+
}
|
|
5266
|
+
/** `obj.f = value` for a nullable-pointer field: the new instance pointer
|
|
5267
|
+
* is produced without a union box where the value is a wrap, and as a
|
|
5268
|
+
* retained payload of a borrowed box otherwise. Unlink-then-release like
|
|
5269
|
+
* every field store. */
|
|
5270
|
+
emitNullableFieldStore(obj, className, field, nullable, value) {
|
|
5271
|
+
const B = this.B;
|
|
5272
|
+
let next;
|
|
5273
|
+
if (value.kind === "unionWrap" &&
|
|
5274
|
+
value.unionId === nullable.unionId &&
|
|
5275
|
+
(value.tag === nullable.refTag
|
|
5276
|
+
? typeEquals(value.value.type, nullable.arm)
|
|
5277
|
+
: value.tag === nullable.unitTag)) {
|
|
5278
|
+
if (value.tag === nullable.refTag) {
|
|
5279
|
+
const inner = this.emitExpr(value.value);
|
|
5280
|
+
this.moveTemp(inner);
|
|
5281
|
+
next = inner.name;
|
|
5282
|
+
}
|
|
5283
|
+
else {
|
|
5284
|
+
// Unit payloads carry nothing; a void payload runs for effects.
|
|
5285
|
+
if (!isUnitType(value.value.type))
|
|
5286
|
+
this.emitExpr(value.value);
|
|
5287
|
+
next = "null";
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
else if (value.kind === "varRef" && !this.canBorrowReceiver(value)) {
|
|
5291
|
+
next = this.unionToNullable(this.emitExpr(value).name, nullable);
|
|
5292
|
+
}
|
|
5293
|
+
else {
|
|
5294
|
+
next = this.unionToNullable(this.emitUnionProjection(value).name, nullable);
|
|
5295
|
+
}
|
|
5296
|
+
const { ptr } = this.classFieldPtr(obj, className, field);
|
|
5297
|
+
const old = B.tmp();
|
|
5298
|
+
B.line(`${old} = load ptr, ptr ${ptr}`);
|
|
5299
|
+
B.line(`store ptr ${next}, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
5300
|
+
this.releaseValue(old, nullable.arm);
|
|
5301
|
+
}
|
|
5302
|
+
/** A union binding whose slot may hold a private stack box: stack locals
|
|
5303
|
+
* and projection-only parameters (callers may pass stack boxes). Such a
|
|
5304
|
+
* box must only be projected: never retained, released, or stored. */
|
|
5305
|
+
isStackUnionLocal(localId) {
|
|
5306
|
+
return (this.projectedParameters.has(localId) ||
|
|
5307
|
+
this.localStackUnions.has(localId) ||
|
|
5308
|
+
this.localUnionStorage.has(localId) ||
|
|
5309
|
+
this.localArrayReads.has(localId) ||
|
|
5310
|
+
this.mapReadLifetimes.locals.has(localId));
|
|
5311
|
+
}
|
|
4794
5312
|
canBorrowReceiver(e) {
|
|
4795
5313
|
switch (e.kind) {
|
|
4796
5314
|
case "varRef": {
|