@scriptc/compiler 0.2.10 → 0.3.1
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 +7 -38
- package/ambient/scriptc-overrides.d.ts +1 -25
- package/ambient/scriptc.d.ts +1 -9
- package/dist/backend/cycle-analysis.test.js +1 -1
- package/dist/backend/cycle-mutability.d.ts +1 -1
- package/dist/backend/cycle-mutability.js +1 -18
- package/dist/backend/cycle-mutability.js.map +1 -1
- package/dist/backend/executable-features.d.ts +1 -1
- package/dist/backend/executable-features.js +3 -8
- package/dist/backend/executable-features.js.map +1 -1
- package/dist/backend/immutability.d.ts +1 -1
- package/dist/backend/immutability.js +1 -1
- package/dist/backend/immutability.test.js +1 -1
- package/dist/backend/library-pack.js +0 -2
- package/dist/backend/library-pack.js.map +1 -1
- package/dist/backend/linker.js +1 -1
- package/dist/backend/linker.js.map +1 -1
- package/dist/backend/llvm/bigint-wrap.d.ts +4 -0
- package/dist/backend/llvm/bigint-wrap.js +590 -0
- package/dist/backend/llvm/bigint-wrap.js.map +1 -0
- package/dist/backend/llvm/blocks.d.ts +2 -0
- package/dist/backend/llvm/blocks.js +4 -0
- package/dist/backend/llvm/blocks.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.js +15 -4
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +14 -3
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/checked-narrows.test.js +2 -2
- package/dist/backend/llvm/checked-narrows.test.js.map +1 -1
- package/dist/backend/llvm/classes.js +60 -21
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +11 -11
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +7 -5
- package/dist/backend/llvm/common.js +7 -5
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.d.ts +1 -1
- package/dist/backend/llvm/constant-callbacks.js +13 -31
- package/dist/backend/llvm/constant-callbacks.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.test.js +1 -1
- package/dist/backend/llvm/dense-array-access.js +133 -45
- package/dist/backend/llvm/dense-array-access.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.test.js +54 -2
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +2 -1
- package/dist/backend/llvm/dyn.js +4 -73
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +123 -23
- package/dist/backend/llvm/emitter.js +729 -258
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +17 -10
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +120 -43
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-async.test.js +2 -2
- package/dist/backend/llvm/expr-async.test.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.js +51 -36
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-callbacks.js +72 -3
- package/dist/backend/llvm/expr-callbacks.js.map +1 -1
- package/dist/backend/llvm/expr-calls.d.ts +1 -1
- package/dist/backend/llvm/expr-calls.js +159 -30
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-calls.test.js +132 -0
- package/dist/backend/llvm/expr-calls.test.js.map +1 -0
- package/dist/backend/llvm/expr-containers.js +25 -8
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-context.d.ts +2 -0
- package/dist/backend/llvm/expr-control.js +4 -107
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.d.ts +1 -2
- package/dist/backend/llvm/expr-dispatch.js +3 -41
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.d.ts +1 -1
- package/dist/backend/llvm/expr-dynamic.js +39 -31
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +7 -0
- package/dist/backend/llvm/expr-primitives.js +80 -22
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +97 -7
- package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
- package/dist/backend/llvm/expr-records.d.ts +9 -0
- package/dist/backend/llvm/expr-records.js +134 -16
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.d.ts +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +80 -1
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.test.js +51 -2
- package/dist/backend/llvm/expr-stream-bridges.test.js.map +1 -1
- package/dist/backend/llvm/int32-abi.d.ts +31 -0
- package/dist/backend/llvm/int32-abi.js +62 -0
- package/dist/backend/llvm/int32-abi.js.map +1 -0
- package/dist/backend/llvm/int32-abi.test.js +107 -0
- package/dist/backend/llvm/int32-abi.test.js.map +1 -0
- package/dist/backend/llvm/int32-locals.d.ts +30 -0
- package/dist/backend/llvm/int32-locals.js +95 -0
- package/dist/backend/llvm/int32-locals.js.map +1 -0
- package/dist/backend/llvm/integer-values.d.ts +4 -0
- package/dist/backend/llvm/integer-values.js +30 -1
- package/dist/backend/llvm/integer-values.js.map +1 -1
- package/dist/backend/llvm/integer-views.d.ts +8 -1
- package/dist/backend/llvm/integer-views.js +47 -2
- package/dist/backend/llvm/integer-views.js.map +1 -1
- package/dist/backend/llvm/lazy-captures.d.ts +16 -0
- package/dist/backend/llvm/lazy-captures.js +88 -2
- package/dist/backend/llvm/lazy-captures.js.map +1 -1
- package/dist/backend/llvm/lazy-captures.test.js +48 -0
- package/dist/backend/llvm/lazy-captures.test.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +14 -1
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +3 -13
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +7 -8
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.js +21 -2
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +4 -3
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.test.js +3 -3
- package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
- package/dist/backend/llvm/map-number-lookup.d.ts +7 -0
- package/dist/backend/llvm/map-number-lookup.js +16 -2
- package/dist/backend/llvm/map-number-lookup.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.js +7 -1
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +25 -8
- package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/reference-effects.js +2 -0
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/reference-writes.d.ts +3 -0
- package/dist/backend/llvm/reference-writes.js +10 -2
- package/dist/backend/llvm/reference-writes.js.map +1 -1
- package/dist/backend/llvm/regex-matchers.d.ts +39 -0
- package/dist/backend/llvm/regex-matchers.js +1649 -0
- package/dist/backend/llvm/regex-matchers.js.map +1 -0
- package/dist/backend/llvm/regex-matchers.test.js +96 -0
- package/dist/backend/llvm/regex-matchers.test.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +36 -1
- package/dist/backend/llvm/shapes.js +128 -16
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +3 -3
- package/dist/backend/llvm/stack-unions.test.js +2 -2
- package/dist/backend/llvm/stack-unions.test.js.map +1 -1
- package/dist/backend/llvm/string-construction.test.js +3 -3
- package/dist/backend/llvm/string-construction.test.js.map +1 -1
- package/dist/backend/llvm/string-lifetimes.test.js +6 -6
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/string-literal-equality.d.ts +22 -0
- package/dist/backend/llvm/string-literal-equality.js +78 -0
- package/dist/backend/llvm/string-literal-equality.js.map +1 -0
- package/dist/backend/llvm/switch-dispatch.js +59 -13
- package/dist/backend/llvm/switch-dispatch.js.map +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js +72 -7
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
- package/dist/backend/llvm/union-retype.test.d.ts +1 -0
- package/dist/backend/llvm/union-retype.test.js +229 -0
- package/dist/backend/llvm/union-retype.test.js.map +1 -0
- package/dist/backend/llvm/wrapping-sums.d.ts +17 -0
- package/dist/backend/llvm/wrapping-sums.js +113 -0
- package/dist/backend/llvm/wrapping-sums.js.map +1 -0
- package/dist/backend/loop-polls.js +1 -0
- package/dist/backend/loop-polls.js.map +1 -1
- package/dist/backend/mangle.d.ts +9 -0
- package/dist/backend/mangle.js +15 -0
- package/dist/backend/mangle.js.map +1 -1
- package/dist/backend/may-throw.js +6 -12
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +1 -1
- package/dist/backend/native/artifact-stamps.js +0 -2
- package/dist/backend/native/artifact-stamps.js.map +1 -1
- package/dist/backend/native/cache-warm.d.ts +1 -1
- package/dist/backend/native/cache-warm.js +2 -3
- package/dist/backend/native/cache-warm.js.map +1 -1
- package/dist/backend/native/compiler-fingerprint.d.ts +1 -1
- package/dist/backend/native/compiler-fingerprint.js +4 -7
- package/dist/backend/native/compiler-fingerprint.js.map +1 -1
- package/dist/backend/native/contracts.d.ts +3 -28
- package/dist/backend/native/driver.js +1 -3
- package/dist/backend/native/driver.js.map +1 -1
- package/dist/backend/native/executable.d.ts +0 -15
- package/dist/backend/native/executable.js +17 -172
- package/dist/backend/native/executable.js.map +1 -1
- package/dist/backend/native/tool-identity.js +0 -1
- package/dist/backend/native/tool-identity.js.map +1 -1
- package/dist/backend/native/vendor.d.ts +1 -1
- package/dist/backend/native/vendor.js +1 -1
- package/dist/backend/native/vendor.js.map +1 -1
- package/dist/backend/native-link-info-core.d.ts +0 -2
- package/dist/backend/native-link-info-core.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +23 -33
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/publication.test.js +1 -1
- 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 +1 -2
- package/dist/backend/runtime-pack-core.js +5 -14
- package/dist/backend/runtime-pack-core.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +3 -8
- package/dist/backend/runtime-pack-targets.test.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +22 -19
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/target-diagnostics.d.ts +0 -5
- package/dist/backend/target-diagnostics.js +1 -11
- package/dist/backend/target-diagnostics.js.map +1 -1
- package/dist/backend/target-diagnostics.test.js +1 -1
- package/dist/backend/vendor-archives.d.ts +1 -7
- package/dist/backend/vendor-archives.js +9 -189
- package/dist/backend/vendor-archives.js.map +1 -1
- package/dist/backend/vendor-inputs.d.ts +0 -1
- package/dist/backend/vendor-inputs.js +0 -1
- package/dist/backend/vendor-inputs.js.map +1 -1
- package/dist/backend/warm-cache.js +3 -5
- package/dist/backend/warm-cache.js.map +1 -1
- package/dist/cli/command.js +4 -8
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/host.d.ts +1 -1
- package/dist/cli/usage.d.ts +1 -5
- package/dist/cli/usage.js +4 -9
- package/dist/cli/usage.js.map +1 -1
- package/dist/compat/fetch-profile.d.ts +2 -2
- package/dist/compat/fetch-profile.js +16 -25
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/compile-types.d.ts +2 -17
- package/dist/contracts/check.js +6 -6
- package/dist/contracts/check.js.map +1 -1
- package/dist/coverage/report.d.ts +5 -22
- package/dist/coverage/report.js +12 -76
- package/dist/coverage/report.js.map +1 -1
- package/dist/coverage/surface-manifest.d.ts +3 -3
- package/dist/coverage/surface-manifest.js +22 -27
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/coverage/verdict-cache.d.ts +1 -3
- package/dist/coverage/verdict-cache.js +2 -7
- package/dist/coverage/verdict-cache.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +11 -60
- package/dist/diagnostics/diagnostic.js +39 -118
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/envelope.d.ts +3 -4
- package/dist/diagnostics/envelope.js +0 -9
- package/dist/diagnostics/envelope.js.map +1 -1
- package/dist/diagnostics/envelope.test.js +1 -1
- package/dist/diagnostics/envelope.test.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +0 -3
- package/dist/executable/executable-cache.js +2 -4
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +0 -4
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/executable/prepare.js +5 -2
- package/dist/executable/prepare.js.map +1 -1
- package/dist/frontend/analysis.js +10 -10
- package/dist/frontend/analysis.js.map +1 -1
- package/dist/frontend/cjs-syntax.js +0 -88
- package/dist/frontend/cjs-syntax.js.map +1 -1
- package/dist/frontend/dts-paths.d.ts +1 -1
- package/dist/frontend/dts-paths.js +1 -1
- package/dist/frontend/lowering/abstract-equality.js +1 -4
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +14 -0
- package/dist/frontend/lowering/array-values.js +33 -11
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/builtins/arguments.js +0 -13
- package/dist/frontend/lowering/builtins/arguments.js.map +1 -1
- package/dist/frontend/lowering/builtins/crypto.d.ts +0 -4
- package/dist/frontend/lowering/builtins/crypto.js +0 -4
- package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
- package/dist/frontend/lowering/builtins/json.js +1 -39
- package/dist/frontend/lowering/builtins/json.js.map +1 -1
- package/dist/frontend/lowering/builtins/module-resolution.d.ts +0 -15
- package/dist/frontend/lowering/builtins/module-resolution.js +5 -44
- package/dist/frontend/lowering/builtins/module-resolution.js.map +1 -1
- package/dist/frontend/lowering/builtins/numbers.d.ts +0 -6
- package/dist/frontend/lowering/builtins/numbers.js +6 -57
- package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
- package/dist/frontend/lowering/builtins/promises.js +4 -67
- package/dist/frontend/lowering/builtins/promises.js.map +1 -1
- package/dist/frontend/lowering/builtins/workers.js +1 -1
- package/dist/frontend/lowering/builtins/workers.js.map +1 -1
- package/dist/frontend/lowering/call-arguments.d.ts +1 -37
- package/dist/frontend/lowering/call-arguments.js +11 -94
- package/dist/frontend/lowering/call-arguments.js.map +1 -1
- package/dist/frontend/lowering/call-signatures.d.ts +1 -6
- package/dist/frontend/lowering/call-signatures.js +16 -68
- package/dist/frontend/lowering/call-signatures.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +19 -4
- package/dist/frontend/lowering/class-dynamic-dispatch.js +168 -98
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js +44 -4
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
- package/dist/frontend/lowering/class-method-mutations.js +0 -2
- package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
- package/dist/frontend/lowering/class-protocols.js +1 -1
- package/dist/frontend/lowering/class-protocols.js.map +1 -1
- package/dist/frontend/lowering/coercions/function-adapters.js +5 -35
- package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
- package/dist/frontend/lowering/coercions/state.d.ts +0 -1
- package/dist/frontend/lowering/coercions/state.js +0 -1
- package/dist/frontend/lowering/coercions/state.js.map +1 -1
- package/dist/frontend/lowering/coercions/structural-plans.js +2 -4
- package/dist/frontend/lowering/coercions/structural-plans.js.map +1 -1
- package/dist/frontend/lowering/coercions/structural.js +2 -12
- package/dist/frontend/lowering/coercions/structural.js.map +1 -1
- package/dist/frontend/lowering/coercions/unions.js +0 -1
- package/dist/frontend/lowering/coercions/unions.js.map +1 -1
- package/dist/frontend/lowering/containers/array-callbacks.js +49 -28
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/array-construction.js +2 -3
- package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
- package/dist/frontend/lowering/containers/array-methods.js +10 -54
- package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/array-pipelines.d.ts +54 -0
- package/dist/frontend/lowering/containers/array-pipelines.js +1827 -0
- package/dist/frontend/lowering/containers/array-pipelines.js.map +1 -0
- package/dist/frontend/lowering/containers/bytes.js +0 -1
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/callback-arguments.js +2 -10
- package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
- package/dist/frontend/lowering/containers/collection-methods.js +1 -6
- package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/exec-result-uses.d.ts +10 -0
- package/dist/frontend/lowering/containers/exec-result-uses.js +88 -0
- package/dist/frontend/lowering/containers/exec-result-uses.js.map +1 -0
- package/dist/frontend/lowering/containers/indexed-objects.js +0 -2
- package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.d.ts +0 -6
- package/dist/frontend/lowering/containers/string-and-regexp.js +17 -17
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/containers/string-replacement.d.ts +2 -1
- package/dist/frontend/lowering/containers/string-replacement.js +32 -136
- package/dist/frontend/lowering/containers/string-replacement.js.map +1 -1
- package/dist/frontend/lowering/devirtualize.d.ts +33 -0
- package/dist/frontend/lowering/devirtualize.js +199 -0
- package/dist/frontend/lowering/devirtualize.js.map +1 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js +0 -3
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
- package/dist/frontend/lowering/expressions/native-proxy.js +1 -1
- package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -1
- package/dist/frontend/lowering/expressions/nullish-assignment.js +55 -8
- package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.d.ts +0 -8
- package/dist/frontend/lowering/expressions/object-literals.js +8 -35
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +0 -4
- package/dist/frontend/lowering/expressions/optional-chains.js +3 -66
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/function-context.js +0 -1
- package/dist/frontend/lowering/function-context.js.map +1 -1
- package/dist/frontend/lowering/generic-functions.js +0 -1
- package/dist/frontend/lowering/generic-functions.js.map +1 -1
- package/dist/frontend/lowering/identifier-uses.d.ts +7 -0
- package/dist/frontend/lowering/identifier-uses.js +23 -0
- package/dist/frontend/lowering/identifier-uses.js.map +1 -0
- package/dist/frontend/lowering/iterator-adapters.js +51 -3
- package/dist/frontend/lowering/iterator-adapters.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.js +10 -77
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-array-sort.js +14 -10
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
- package/dist/frontend/lowering/lower-array-sort.test.js +2 -2
- package/dist/frontend/lowering/lower-array-sort.test.js.map +1 -1
- package/dist/frontend/lowering/lower-assert.js +2 -3
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-async-iteration.js +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +2 -27
- package/dist/frontend/lowering/lower-calls.js +111 -493
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +12 -60
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-comptime.js +2 -3
- package/dist/frontend/lowering/lower-comptime.js.map +1 -1
- package/dist/frontend/lowering/lower-console.d.ts +14 -0
- package/dist/frontend/lowering/lower-console.js +195 -0
- package/dist/frontend/lowering/lower-console.js.map +1 -0
- package/dist/frontend/lowering/lower-enums.js +5 -8
- package/dist/frontend/lowering/lower-enums.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +3 -4
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-expando.js +3 -5
- package/dist/frontend/lowering/lower-expando.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +0 -8
- package/dist/frontend/lowering/lower-exprs.js +180 -905
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-functions.js +9 -8
- package/dist/frontend/lowering/lower-functions.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.d.ts +10 -0
- package/dist/frontend/lowering/lower-generators.js +108 -41
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +1 -50
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +0 -43
- package/dist/frontend/lowering/lower-modules.js +29 -329
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.test.js +5 -5
- package/dist/frontend/lowering/lower-modules.test.js.map +1 -1
- package/dist/frontend/lowering/lower-server.js +0 -30
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stdlib.d.ts +98 -0
- package/dist/frontend/lowering/{lower-island.js → lower-stdlib.js} +97 -1160
- package/dist/frontend/lowering/lower-stdlib.js.map +1 -0
- package/dist/frontend/lowering/lower-stmts.d.ts +0 -16
- package/dist/frontend/lowering/lower-stmts.js +58 -1016
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.test.js +1 -1
- package/dist/frontend/lowering/lower-stmts.test.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +14 -13
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +42 -155
- package/dist/frontend/lowering/lowerer.js +192 -816
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/lowerer.test.js +4 -51
- package/dist/frontend/lowering/lowerer.test.js.map +1 -1
- package/dist/frontend/lowering/nan-coded-reads.d.ts +16 -0
- package/dist/frontend/lowering/nan-coded-reads.js +108 -1
- package/dist/frontend/lowering/nan-coded-reads.js.map +1 -1
- package/dist/frontend/lowering/narrowing-stability.js +1 -1
- package/dist/frontend/lowering/narrowing-stability.js.map +1 -1
- package/dist/frontend/lowering/optional-arguments.js +0 -2
- package/dist/frontend/lowering/optional-arguments.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.d.ts +4 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js +2 -0
- package/dist/frontend/lowering/runtime-optional-bounds.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-bounds.test.js +27 -0
- package/dist/frontend/lowering/runtime-optional-bounds.test.js.map +1 -1
- package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
- package/dist/frontend/lowering/split-consumers.js +2 -18
- package/dist/frontend/lowering/split-consumers.js.map +1 -1
- package/dist/frontend/lowering/strict-equality.js +1 -1
- package/dist/frontend/lowering/strict-equality.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +6 -45
- package/dist/frontend/lowering/surfaces.js +8 -73
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/union-retag.d.ts +6 -1
- package/dist/frontend/lowering/union-retag.js +64 -1
- package/dist/frontend/lowering/union-retag.js.map +1 -1
- package/dist/frontend/lowering/union-retag.test.js +46 -0
- package/dist/frontend/lowering/union-retag.test.js.map +1 -1
- package/dist/frontend/module-syntax.d.ts +0 -3
- package/dist/frontend/module-syntax.js.map +1 -1
- package/dist/frontend/npm-node.d.ts +0 -7
- package/dist/frontend/npm-node.js +0 -7
- package/dist/frontend/npm-node.js.map +1 -1
- package/dist/frontend/npm-static-rewrite-syntax.d.ts +1 -1
- package/dist/frontend/npm-static-rewrite-syntax.js +6 -82
- package/dist/frontend/npm-static-rewrite-syntax.js.map +1 -1
- package/dist/frontend/npm-static.d.ts +1 -1
- package/dist/frontend/npm-static.js +6 -71
- package/dist/frontend/npm-static.js.map +1 -1
- package/dist/frontend/npm.d.ts +3 -319
- package/dist/frontend/npm.js +6 -1007
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/npm.test.js +2 -197
- package/dist/frontend/npm.test.js.map +1 -1
- package/dist/frontend/pipeline-native.js +1 -2
- package/dist/frontend/pipeline-native.js.map +1 -1
- package/dist/frontend/pipeline.d.ts +3 -11
- package/dist/frontend/pipeline.js +37 -56
- package/dist/frontend/pipeline.js.map +1 -1
- package/dist/frontend/pipeline.test.js +12 -6
- package/dist/frontend/pipeline.test.js.map +1 -1
- package/dist/frontend/program.d.ts +0 -34
- package/dist/frontend/program.js +12 -97
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/provenance-core.d.ts +0 -4
- package/dist/frontend/provenance-core.js +4 -37
- package/dist/frontend/provenance-core.js.map +1 -1
- package/dist/frontend/provenance-registry.d.ts +0 -4
- package/dist/frontend/provenance-registry.js +0 -20
- package/dist/frontend/provenance-registry.js.map +1 -1
- package/dist/frontend/resolve.d.ts +0 -12
- package/dist/frontend/resolve.js +0 -34
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/runtime-resolve.js +2 -2
- package/dist/frontend/runtime-resolve.js.map +1 -1
- package/dist/frontend/services-node.js +2 -3
- package/dist/frontend/services-node.js.map +1 -1
- package/dist/frontend/services.d.ts +1 -8
- package/dist/frontend/services.js +2 -22
- package/dist/frontend/services.js.map +1 -1
- package/dist/frontend/services.test.js +0 -38
- package/dist/frontend/services.test.js.map +1 -1
- package/dist/frontend/tsc-codes.js +0 -11
- package/dist/frontend/tsc-codes.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +2 -27
- package/dist/frontend/type-mapper.js +58 -156
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -10
- package/dist/index.js.map +1 -1
- package/dist/ir/api.d.ts +1 -1
- package/dist/ir/api.js +1 -1
- package/dist/ir/api.js.map +1 -1
- package/dist/ir/builtin-effects.js +2 -5
- package/dist/ir/builtin-effects.js.map +1 -1
- package/dist/ir/builtin-signatures.d.ts +1 -1
- package/dist/ir/builtin-signatures.js +13 -56
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/constant-globals.test.js +1 -1
- package/dist/ir/constant-tables.test.js +1 -1
- package/dist/ir/final-fields.test.js +1 -1
- package/dist/ir/function-references.d.ts +12 -0
- package/dist/ir/function-references.js +29 -0
- package/dist/ir/function-references.js.map +1 -0
- package/dist/ir/function-references.test.d.ts +1 -0
- package/dist/ir/function-references.test.js +276 -0
- package/dist/ir/function-references.test.js.map +1 -0
- package/dist/ir/initializer-bindings.test.js +1 -1
- package/dist/ir/int32-slots.d.ts +10 -7
- package/dist/ir/int32-slots.js +219 -68
- package/dist/ir/int32-slots.js.map +1 -1
- package/dist/ir/int32-slots.test.js +39 -2
- package/dist/ir/int32-slots.test.js.map +1 -1
- package/dist/ir/integer-ranges.d.ts +4 -4
- package/dist/ir/integer-ranges.js +89 -64
- package/dist/ir/integer-ranges.js.map +1 -1
- package/dist/ir/integer-ranges.test.js +71 -9
- package/dist/ir/integer-ranges.test.js.map +1 -1
- package/dist/ir/intrinsic-signatures.js +1 -0
- package/dist/ir/intrinsic-signatures.js.map +1 -1
- package/dist/ir/ir.d.ts +64 -290
- package/dist/ir/ir.js +11 -259
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +2 -7
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/runtime-features.d.ts +3 -30
- package/dist/ir/runtime-features.js +8 -43
- package/dist/ir/runtime-features.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 +4 -21
- package/dist/ir/traverse.js.map +1 -1
- package/dist/ir/unshare.test.js +1 -1
- package/dist/ir/validate.js +66 -213
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +31 -13
- 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/prepare.js +5 -20
- package/dist/library/prepare.js.map +1 -1
- package/dist/native/cli.js +0 -2
- package/dist/native/cli.js.map +1 -1
- package/dist/native/compiler.test.js +2 -4
- package/dist/native/compiler.test.js.map +1 -1
- package/dist/native/prepare.js +2 -4
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/sanitizer.js +2 -16
- package/dist/native/sanitizer.js.map +1 -1
- package/dist/native/sanitizer.test.js +0 -3
- package/dist/native/sanitizer.test.js.map +1 -1
- package/dist/startup-cache.js +0 -2
- package/dist/startup-cache.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +80 -104
- package/dist/backend/llvm/expr-island.d.ts +0 -15
- package/dist/backend/llvm/expr-island.js +0 -709
- package/dist/backend/llvm/expr-island.js.map +0 -1
- package/dist/frontend/lowering/coercions/island.d.ts +0 -37
- package/dist/frontend/lowering/coercions/island.js +0 -350
- package/dist/frontend/lowering/coercions/island.js.map +0 -1
- package/dist/frontend/lowering/lower-island.d.ts +0 -229
- package/dist/frontend/lowering/lower-island.js.map +0 -1
- package/dist/frontend/lowering/lower-island.test.js +0 -56
- package/dist/frontend/lowering/lower-island.test.js.map +0 -1
- package/dist/frontend/npm-fetch-analysis.d.ts +0 -17
- package/dist/frontend/npm-fetch-analysis.js +0 -109
- package/dist/frontend/npm-fetch-analysis.js.map +0 -1
- package/dist/frontend/npm-fetch-analysis.test.js +0 -130
- package/dist/frontend/npm-fetch-analysis.test.js.map +0 -1
- package/dist/frontend/npm-fetch-node.d.ts +0 -2
- package/dist/frontend/npm-fetch-node.js +0 -12
- package/dist/frontend/npm-fetch-node.js.map +0 -1
- package/dist/frontend/npm-fetch-syntax.d.ts +0 -6
- package/dist/frontend/npm-fetch-syntax.js +0 -202
- package/dist/frontend/npm-fetch-syntax.js.map +0 -1
- package/dist/native/runtime-typescript.d.ts +0 -4
- package/dist/native/runtime-typescript.js +0 -74
- package/dist/native/runtime-typescript.js.map +0 -1
- package/dist/native/runtime-typescript.test.js +0 -41
- package/dist/native/runtime-typescript.test.js.map +0 -1
- /package/dist/{frontend/lowering/lower-island.test.d.ts → backend/llvm/expr-calls.test.d.ts} +0 -0
- /package/dist/{frontend/npm-fetch-analysis.test.d.ts → backend/llvm/int32-abi.test.d.ts} +0 -0
- /package/dist/{native/runtime-typescript.test.d.ts → backend/llvm/regex-matchers.test.d.ts} +0 -0
|
@@ -1,31 +1,14 @@
|
|
|
1
1
|
import { emitLlvmLayouts } from "./layouts.js";
|
|
2
|
+
import { compileRegexMatcher } from "./regex-matchers.js";
|
|
2
3
|
import { FIRST_EMITTED_CYC_KIND, inlineAllocSupported } from "./alloc.js";
|
|
3
4
|
import { llvmBytes as llBytes } from "../literals.js";
|
|
4
5
|
import { InternalCompilerError } from "../../errors.js";
|
|
5
|
-
/** Lower typed IR to LLVM, using the runtime's C ABI.
|
|
6
|
-
*
|
|
7
|
-
* Reference-counted temporaries own one reference. Declarations, assignments,
|
|
8
|
-
* returns and arguments move that ownership; frame and scope exits release
|
|
9
|
-
* everything remaining. Slot-backed entries release the slot's current value.
|
|
10
|
-
*
|
|
11
|
-
* Exceptions use a pending flag and explicit cleanup edges. Throwing calls
|
|
12
|
-
* test scr_exc_pending before consuming results, release the scopes crossed,
|
|
13
|
-
* and branch to the innermost handler or return to the caller. Catch bindings
|
|
14
|
-
* hold ScrCaught snapshots; finally runs on normal, throwing and returning
|
|
15
|
-
* paths. This keeps ownership cleanup intact without nonlocal jumps.
|
|
16
|
-
*
|
|
17
|
-
* Program objects are emitted by the packaged LLVM helper and linked with
|
|
18
|
-
* runtime packs. Sanitizer development builds use an external LLVM driver.
|
|
19
|
-
* Dynamic values and islands share the runtime ABI; embedded npm modules use
|
|
20
|
-
* compressed source tables that the runtime inflates on demand.
|
|
21
|
-
*/
|
|
22
6
|
import { createHash } from "node:crypto";
|
|
23
|
-
import { deflateRawSync } from "node:zlib";
|
|
24
7
|
import { endsWithJump, matchStringSelfConcat, streamTypedRefEligible, undefinedArmTag, } from "../../ir/analysis.js";
|
|
25
8
|
import { emitLibraryIdentityLines } from "../library-identity-markers.js";
|
|
26
9
|
import { loopTestsEveryIteration } from "../loop-polls.js";
|
|
27
10
|
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";
|
|
28
|
-
import { moduleRuntimeFeatures
|
|
11
|
+
import { moduleRuntimeFeatures } from "../../ir/runtime-features.js";
|
|
29
12
|
import { matchIntegerArrayForLoop, matchIntegerBytesForLoop, matchIntegerCountedForLoop, } from "../../ir/integer-loops.js";
|
|
30
13
|
import { emitCountedLoopLimit } from "./counted-loops.js";
|
|
31
14
|
import { emitLiteralSwitch } from "./switch-dispatch.js";
|
|
@@ -34,19 +17,22 @@ import { emitByteWindowGuard, findInitializedByteLoopBindings, matchByteWindow,
|
|
|
34
17
|
import { canDeferSplitPiece, emitSplitSpanNext, materializeSplitPiece, emitSplitCursor, emitSplitNext, emitSplitScratch, emitSplitSnapshot, findPrivateSplitLocals, loadSplitSnapshot, storeSplitSnapshot, stringSplit, } from "./split-loops.js";
|
|
35
18
|
import { scalarizeNumericRecords } from "../../ir/scalar-records.js";
|
|
36
19
|
import { specializeNumericCalls } from "../../ir/numeric-call-specialization.js";
|
|
37
|
-
import { everyExprChild, everyStmtChild, everyStmtList } from "../../ir/traverse.js";
|
|
20
|
+
import { everyExpr, everyExprChild, everyStmtChild, everyStmtList } from "../../ir/traverse.js";
|
|
38
21
|
import { analyzeIntegerRanges, INT32_RANGE } from "../../ir/integer-ranges.js";
|
|
39
22
|
import { unshareNodes } from "../../ir/unshare.js";
|
|
40
23
|
import { analyzeInt32Slots } from "../../ir/int32-slots.js";
|
|
41
24
|
import { analyzeFinalFields, constructorClass } from "../../ir/final-fields.js";
|
|
42
25
|
import { foldConstantGlobals } from "../../ir/constant-globals.js";
|
|
43
26
|
import { findIntegerViews } from "./integer-views.js";
|
|
27
|
+
import { findInt32Abis, INT32_ABI_SUFFIX } from "./int32-abi.js";
|
|
28
|
+
import { findInt32Locals } from "./int32-locals.js";
|
|
29
|
+
import { exactInteger, narrowInteger } from "./integer-values.js";
|
|
44
30
|
import { findInitializerBindings, withInitializerBindings } from "../../ir/initializer-bindings.js";
|
|
45
31
|
import { findConstantNumericTables } from "../../ir/constant-tables.js";
|
|
46
32
|
import { allocateFfiCallbackAdapters, hasForeignFfiCallback, hasRetainedFfiCallback, } from "../ffi-callbacks.js";
|
|
47
33
|
import { RUNTIME_ABI_MARKER } from "../runtime-abi.js";
|
|
48
34
|
import { computeMayThrow } from "../may-throw.js";
|
|
49
|
-
import { mangleArgPack, mangleAsyncSpawn, mangleBorrowedFunction, mangleClassObj, mangleClassStruct, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordStruct, mangleTrampoline, mangleWrapper, mangleVtInstance, } from "../mangle.js";
|
|
35
|
+
import { mangleArgPack, mangleAsyncAdoptCall, mangleAsyncAwaitCall, mangleAsyncSpawn, mangleBorrowedFunction, mangleClassObj, mangleClassStruct, mangleFnClosure, mangleFunction, mangleGenDrop, mangleGenSpawn, mangleGlobal, mangleLocal, mangleRecordStruct, mangleTrampoline, mangleWrapper, mangleBorrowingWrapper, mangleVtInstance, } from "../mangle.js";
|
|
50
36
|
import { analyzeCallLifetimes } from "./call-lifetimes.js";
|
|
51
37
|
import { canStackUnion, emitStackUnion, findLocalStackUnions } from "./stack-unions.js";
|
|
52
38
|
import { allocateLocalUnion, findLocalUnionStorage, storeLocalUnion, } from "./local-union-storage.js";
|
|
@@ -79,8 +65,7 @@ import { emitIntrinsicExpr, emitSerializationExpr, emitAsyncExpr } from "./expr-
|
|
|
79
65
|
import { PublishWalkers } from "./publish.js";
|
|
80
66
|
import { computeImmutableFields, stableGlobals } from "../immutability.js";
|
|
81
67
|
import { computePublishedTypes } from "../publication.js";
|
|
82
|
-
import {
|
|
83
|
-
import { emitJsMarshal, emitJsOp, emitJsExit, islandAdapter, islandTypedAdapter, } from "./expr-island.js";
|
|
68
|
+
import { emitExpr } from "./expr-dispatch.js";
|
|
84
69
|
import { dynKind, raceAdapterFor, genResultThunkFor, childExitThunkFor, childExitSignalThunkFor, childDataThunkFor, execFileThunkFor, ipcMessageThunkFor, ipcSendThunkFor, emitterFixedAdapter, wrapEmitterListener, unwrapNullableClosure, closeBindThunkFor, closeOverrideWrapFor, } from "./expr-callbacks.js";
|
|
85
70
|
import { streamDataAdapter, streamDoneFnFor, cryptoBytesThunkFor, fsRenameThunkFor, streamCbThunkFor, zlibBytesThunkFor, } from "./expr-stream-callbacks.js";
|
|
86
71
|
import { emitStableReceiver, resolveThunkFor, tagInSet, arrPush, emitArrayCopyLoop, emitStrIntrinsic, emitArrIntrinsic, emitDiscardedSplice, wrapNullable, emitMapNew, mapSet, emitMapLikeIntrinsic, emitSetNew, } from "./expr-containers.js";
|
|
@@ -104,6 +89,7 @@ import { NUMBER_MAP_ENTRY_DECL, STRING_MAP_ENTRY_DECL } from "./map-number-looku
|
|
|
104
89
|
import { STRING_READ_HELPERS } from "./string-reads.js";
|
|
105
90
|
import { emitConcatInputs, emitMixedConcat, numberPart, stringParts, } from "./string-construction.js";
|
|
106
91
|
import { arrNewCall, boxAccess, boxNewCall, boxReleaseSym, computeTraced, elemAccess, emitInlineRcHelpers, emitNullableRetainWrappers, FN_ATTRS, inlineRcDecls, llFieldType, releaseSym, retainSym, traceArg, typedRefConstructor, vAdapters, } from "./shapes.js";
|
|
92
|
+
import { INLINE_LITERAL_BYTES, literalEqualityDefinition } from "./string-literal-equality.js";
|
|
107
93
|
export { LlvmUnsupportedError } from "./unsupported.js";
|
|
108
94
|
function ffiCallbackDummyLl(callback) {
|
|
109
95
|
switch (callback.returns) {
|
|
@@ -239,6 +225,14 @@ export class LlEmitter {
|
|
|
239
225
|
* indices; see ShapeHost.nextCycKind). */
|
|
240
226
|
cycKinds = 0;
|
|
241
227
|
wasi;
|
|
228
|
+
/** Synchronous generator bodies compile to LLVM switched coroutines (a
|
|
229
|
+
* heap frame resumed by an ordinary call) instead of running on a fiber
|
|
230
|
+
* stack. Worker programs keep fibers (context shutdown unwinds suspended
|
|
231
|
+
* generator stacks) and so do debug builds (the debugger reads locals
|
|
232
|
+
* from the native frame). */
|
|
233
|
+
stacklessGenerators = false;
|
|
234
|
+
/** Some function in the module is lowered as a coroutine. */
|
|
235
|
+
usesCoroutines = false;
|
|
242
236
|
/** The target lacks a rounding instruction at its baseline CPU (x86-64
|
|
243
237
|
* before SSE4.1), so `llvm.trunc` would lower to a libm call: emit
|
|
244
238
|
* truncation inline through an integer round trip instead. */
|
|
@@ -258,6 +252,10 @@ export class LlEmitter {
|
|
|
258
252
|
/** Interned string literals: UTF-8 text → { symbol, byte length } —
|
|
259
253
|
* first-use order, the runtime ABI’s determinism discipline. */
|
|
260
254
|
literals = new Map();
|
|
255
|
+
/** Literal equality helpers: literal text → helper symbol (literalEquals). */
|
|
256
|
+
literalEqualities = new Map();
|
|
257
|
+
/** Interned bigint literals: hex magnitude with sign → { symbol, value }. */
|
|
258
|
+
bigintLiterals = new Map();
|
|
261
259
|
/** Symbols of interned constant data, named by content (contentSymbol). */
|
|
262
260
|
contentSymbols = new Set();
|
|
263
261
|
/** Interned unit-armed union instances: "unionId:tag" → symbol — one
|
|
@@ -266,6 +264,10 @@ export class LlEmitter {
|
|
|
266
264
|
unitInstances = new Map();
|
|
267
265
|
/** unionId → symbol of the union's immortal ABSENT field-slot instance. */
|
|
268
266
|
absentInstances = new Map();
|
|
267
|
+
/** unionId → symbol of a flag set once a checked-dynamic cast caches one
|
|
268
|
+
* of the union's boxes. A cached box is refreshed in place, possibly with
|
|
269
|
+
* a tag that another union numbers apart, so it is never shared. */
|
|
270
|
+
retypeGuards = new Map();
|
|
269
271
|
/** The nullable-union ABSENT sentinel was referenced (absentInstanceRef). */
|
|
270
272
|
needsNullableAbsent = false;
|
|
271
273
|
immortalValues = new Set();
|
|
@@ -331,6 +333,9 @@ export class LlEmitter {
|
|
|
331
333
|
lazyCaptureBoxes = new Map();
|
|
332
334
|
lazyStackBoxes = new Map();
|
|
333
335
|
borrowedParameters = new Set();
|
|
336
|
+
/** Captures of the current closure body that its environment holds
|
|
337
|
+
* unchanged for the closure's whole life (LazyCaptures.stableCaptures). */
|
|
338
|
+
stableCaptureIds = new Set();
|
|
334
339
|
/** Parameters proven projection-only: callers may pass stack boxes. */
|
|
335
340
|
projectedParameters = new Set();
|
|
336
341
|
stableCallBindings = new Set();
|
|
@@ -386,6 +391,21 @@ export class LlEmitter {
|
|
|
386
391
|
/** Method names with at least one may-throw implementation — the
|
|
387
392
|
* virtualCall pending check's key. */
|
|
388
393
|
mayThrowMethods = new Set();
|
|
394
|
+
/** Async functions awaited directly at some `await f(...)` site; each
|
|
395
|
+
* gets an await-call entry (emitAsyncAwaitCall). */
|
|
396
|
+
awaitCallees = new Set();
|
|
397
|
+
/** Async functions returned directly at some `return f(...)` site of an
|
|
398
|
+
* async function; each gets an adopting entry (emitAsyncAwaitCall). */
|
|
399
|
+
adoptCallees = new Set();
|
|
400
|
+
/** The promise a `return` emitted past its finally regions already
|
|
401
|
+
* produced for its adopting await (borrowed from the return's parked
|
|
402
|
+
* slot); that await takes it instead of evaluating its operand. */
|
|
403
|
+
adoptedOperand = null;
|
|
404
|
+
takeAdoptedOperand() {
|
|
405
|
+
const value = this.adoptedOperand;
|
|
406
|
+
this.adoptedOperand = null;
|
|
407
|
+
return value;
|
|
408
|
+
}
|
|
389
409
|
/** setTimeout and friends appeared somewhere: main must run the event
|
|
390
410
|
* loop even in programs with no async functions. */
|
|
391
411
|
usesTimers = false;
|
|
@@ -459,6 +479,8 @@ export class LlEmitter {
|
|
|
459
479
|
initializerBindings;
|
|
460
480
|
/** Whole-program int32 storage and parameter/result facts. */
|
|
461
481
|
int32Slots;
|
|
482
|
+
/** Functions whose direct calls use i32 parameters or results. */
|
|
483
|
+
int32Abis;
|
|
462
484
|
/** Immutable module constants (contract types) whose graphs become
|
|
463
485
|
* immortal once initialized: their reads need no reference counting. */
|
|
464
486
|
immortalGlobals;
|
|
@@ -476,10 +498,17 @@ export class LlEmitter {
|
|
|
476
498
|
* not a constructor (where they change), not async or a generator. */
|
|
477
499
|
finalFieldBorrows = false;
|
|
478
500
|
integerRanges = new Map();
|
|
501
|
+
/** Sums whose result only feeds 32-bit conversions (wrapping-sums.ts). */
|
|
502
|
+
wrappingSums = new Set();
|
|
479
503
|
byteWindowsEnabled = true;
|
|
480
504
|
byteWindowEntries = new Map();
|
|
481
505
|
bytesBounds = new Set();
|
|
482
506
|
integerViews = new Map();
|
|
507
|
+
/** Number locals of the current function stored only as an i32
|
|
508
|
+
* (int32-locals.ts); their slots are `alloca i32`. */
|
|
509
|
+
int32Locals = new Map();
|
|
510
|
+
/** The written values (and `++`/`--` nodes) the int32 proof covers. */
|
|
511
|
+
int32LocalWrites = new Set();
|
|
483
512
|
/** Enclosing try-with-FINALLY regions, innermost last: a `return`
|
|
484
513
|
* inside one runs every crossed finally (innermost first) before the
|
|
485
514
|
* actual ret — the runtime ABI’s pending-return path, with the finally
|
|
@@ -499,9 +528,12 @@ export class LlEmitter {
|
|
|
499
528
|
/** Return type of the function being emitted — the unwind path returns
|
|
500
529
|
* a dummy of this type (never read: callers check the flag first). */
|
|
501
530
|
currentReturnType = VOID;
|
|
502
|
-
/**
|
|
503
|
-
|
|
504
|
-
|
|
531
|
+
/** The current body returns its number result as i32. */
|
|
532
|
+
currentInt32Result = false;
|
|
533
|
+
/** Active only while emitting a body lowered with LLVM's
|
|
534
|
+
* switched-coroutine intrinsics (wasm32 async bodies and generators,
|
|
535
|
+
* native synchronous generators; see coroLowered). */
|
|
536
|
+
currentCoro = null;
|
|
505
537
|
/** The generator channels of the function being emitted (null outside
|
|
506
538
|
* generator bodies): yieldExpr emission reads them, and emitTryCatch's
|
|
507
539
|
* catch prologue emits the GENRET sentinel re-unwind exactly here. */
|
|
@@ -524,6 +556,7 @@ export class LlEmitter {
|
|
|
524
556
|
this.constantNumericTables = findConstantNumericTables(mod);
|
|
525
557
|
this.initializerBindings = findInitializerBindings(mod);
|
|
526
558
|
this.int32Slots = analyzeInt32Slots(mod);
|
|
559
|
+
this.int32Abis = this.debug === null ? findInt32Abis(mod, this.int32Slots) : new Map();
|
|
527
560
|
this.immortalGlobals = new Set(mod.contracts?.immortalGlobals ?? []);
|
|
528
561
|
this.programLifetime = new Set(mod.contracts?.programLifetime ?? []);
|
|
529
562
|
this.finalFields = analyzeFinalFields(mod);
|
|
@@ -531,6 +564,7 @@ export class LlEmitter {
|
|
|
531
564
|
this.ffiExtendNarrowIntegers =
|
|
532
565
|
options.wasi === true || ffiExtendsNarrowIntegers(options.targetTriple);
|
|
533
566
|
this.wasi = options.wasi === true;
|
|
567
|
+
this.stacklessGenerators = !this.wasi && mod.workers !== true && this.debug === null;
|
|
534
568
|
// An empty triple targets the host, as for executableTls below: the
|
|
535
569
|
// Node-hosted CLI passes none, the native CLI passes its toolchain's, and
|
|
536
570
|
// both must emit the same module for the same target.
|
|
@@ -682,6 +716,7 @@ export class LlEmitter {
|
|
|
682
716
|
internLiteral: (text) => this.internLiteral(text),
|
|
683
717
|
cstr: (text) => this.cstr(text),
|
|
684
718
|
unitInstanceRef: (unionId, tag) => this.unitInstanceRef(unionId, tag),
|
|
719
|
+
retypeGuardRef: (unionId) => this.retypeGuardRef(unionId),
|
|
685
720
|
fieldAbsentTestIn: (B, value, unionId) => this.fieldAbsentTestIn(B, value, unionId),
|
|
686
721
|
absentInstanceRef: (unionId) => this.absentInstanceRef(unionId),
|
|
687
722
|
liveDynRefAdapter: (type) => this.liveDynRefAdapter(type),
|
|
@@ -1125,23 +1160,6 @@ export class LlEmitter {
|
|
|
1125
1160
|
const asyncDefs = this.emitAsyncScaffolding();
|
|
1126
1161
|
const ffiCallbacks = this.emitFfiCallbackDefs();
|
|
1127
1162
|
const hasNoInlineRecordClone = [...this.recordCloneShapes].some((shapeId) => (this.recordsById.get(shapeId)?.fields.length ?? 0) >= 16);
|
|
1128
|
-
const embedded = this.mod.embedded;
|
|
1129
|
-
const usesIsland = embedded !== undefined && embedded.modules.length > 0;
|
|
1130
|
-
const storeNpmText = (text) => {
|
|
1131
|
-
const plain = Buffer.from(text, "utf8");
|
|
1132
|
-
if (text.length < NPM_COMPRESS_MIN)
|
|
1133
|
-
return { bytes: plain, raw: 0 };
|
|
1134
|
-
const deflated = deflateRawSync(plain, { level: 9 });
|
|
1135
|
-
return deflated.length < plain.length
|
|
1136
|
-
? { bytes: deflated, raw: plain.length }
|
|
1137
|
-
: { bytes: plain, raw: 0 };
|
|
1138
|
-
};
|
|
1139
|
-
const npmStored = usesIsland
|
|
1140
|
-
? embedded.modules.map((m) => ({
|
|
1141
|
-
src: storeNpmText(m.source),
|
|
1142
|
-
esm: m.esm === undefined ? null : storeNpmText(m.esm),
|
|
1143
|
-
}))
|
|
1144
|
-
: [];
|
|
1145
1163
|
// Module globals, FIRST OCCURRENCE per id: a class-expression static
|
|
1146
1164
|
// instantiated through several mixin applications registers one global
|
|
1147
1165
|
// id several times (2041-mixin-values) — C's tentative definitions
|
|
@@ -1205,24 +1223,14 @@ export class LlEmitter {
|
|
|
1205
1223
|
const usesDgram = runtimeFeatures.dgram;
|
|
1206
1224
|
const usesHttp2 = runtimeFeatures.http2;
|
|
1207
1225
|
const usesHttp = runtimeFeatures.http;
|
|
1208
|
-
// Fetch-referencing programs register
|
|
1209
|
-
//
|
|
1210
|
-
// compiles scr_fetch.c on the same predicate.
|
|
1226
|
+
// Fetch-referencing programs register native fetch before main.
|
|
1227
|
+
// Runtime selection compiles scr_fetch.c on the same predicate.
|
|
1211
1228
|
const usesFetch = runtimeFeatures.fetch;
|
|
1212
|
-
const
|
|
1213
|
-
const embedsNet = moduleEmbedsBuiltin(this.mod, "node:http") ||
|
|
1214
|
-
moduleEmbedsBuiltin(this.mod, "node:https") ||
|
|
1215
|
-
moduleEmbedsBuiltin(this.mod, "node:net") ||
|
|
1216
|
-
moduleEmbedsBuiltin(this.mod, "node:tls");
|
|
1217
|
-
const snapshotsTlsCa = runtimeFeatures.tls ||
|
|
1218
|
-
runtimeFeatures.tlsCa ||
|
|
1219
|
-
moduleEmbedsBuiltin(this.mod, "node:https") ||
|
|
1220
|
-
moduleEmbedsBuiltin(this.mod, "node:tls");
|
|
1229
|
+
const snapshotsTlsCa = runtimeFeatures.tls || runtimeFeatures.tlsCa;
|
|
1221
1230
|
// The process verdict has the same precedence as the C reference
|
|
1222
1231
|
// emitter: node:test owns the final status when present; otherwise an
|
|
1223
1232
|
// embedded process.exitCode owns it; ordinary programs return zero.
|
|
1224
1233
|
const usesNodeTest = runtimeFeatures.nodeTest;
|
|
1225
|
-
const programExitUsesIsland = !usesNodeTest && usesIsland;
|
|
1226
1234
|
// Declared NOW — the extern block flushes before main assembles.
|
|
1227
1235
|
if (usesEvents)
|
|
1228
1236
|
this.declare(`declare void @scr_events_install()`);
|
|
@@ -1259,19 +1267,6 @@ export class LlEmitter {
|
|
|
1259
1267
|
this.declare(`declare void @scr_http_dyn_install()`);
|
|
1260
1268
|
if (usesFetch)
|
|
1261
1269
|
this.declare(`declare void @scr_fetch_install()`);
|
|
1262
|
-
if (embedsZlib)
|
|
1263
|
-
this.declare(`declare void @scr_zlib_island_install()`);
|
|
1264
|
-
if (embedsNet)
|
|
1265
|
-
this.declare(`declare void @scr_net_island_install()`);
|
|
1266
|
-
if (usesIsland) {
|
|
1267
|
-
this.declare(`declare void @scr_island_modules(ptr, ${this.sizeType}, ptr, ${this.sizeType})`);
|
|
1268
|
-
this.declare(`declare void @scr_island_entry_module(ptr)`);
|
|
1269
|
-
this.declare(`declare i32 @scr_island_exit_code()`);
|
|
1270
|
-
if (moduleEmbedsCompressedNpm(this.mod)) {
|
|
1271
|
-
this.declare(`declare void @scr_island_set_inflate(ptr)`);
|
|
1272
|
-
this.declare(`declare zeroext i1 @scr_zlib_inflate_exact(ptr, ${this.sizeType}, ptr, ${this.sizeType})`);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
1270
|
if (usesNodeTest)
|
|
1276
1271
|
this.declare(`declare i32 @scr_test_exit_code()`);
|
|
1277
1272
|
if (snapshotsTlsCa) {
|
|
@@ -1310,7 +1305,6 @@ export class LlEmitter {
|
|
|
1310
1305
|
// fibers as abandoned, so the RC audit downgrades exactly like the
|
|
1311
1306
|
// async loop-exhaustion story.
|
|
1312
1307
|
const runsLoop = this.usesTimers ||
|
|
1313
|
-
usesIsland ||
|
|
1314
1308
|
this.ffiHasForeignCallback ||
|
|
1315
1309
|
this.mod.functions.some((f) => f.async === true || f.generator !== undefined);
|
|
1316
1310
|
const uncaughtReleases = entryMayThrow && !asyncEntry ? globalReleaseLines("gu") : [];
|
|
@@ -1337,9 +1331,6 @@ export class LlEmitter {
|
|
|
1337
1331
|
this.declare(`declare void @scr_promise_rethrow_top_level(ptr)`);
|
|
1338
1332
|
this.declare(`declare void @scr_promise_release(ptr)`);
|
|
1339
1333
|
this.declare(`declare void @scr_exit_code_note(i32)`);
|
|
1340
|
-
if (programExitUsesIsland && inlineExitListeners) {
|
|
1341
|
-
this.declare(`declare ${this.sizeType} @scr_island_exit_code_version()`);
|
|
1342
|
-
}
|
|
1343
1334
|
}
|
|
1344
1335
|
const topPendingExitLines = () => {
|
|
1345
1336
|
if (!asyncEntry)
|
|
@@ -1348,28 +1339,18 @@ export class LlEmitter {
|
|
|
1348
1339
|
if (usesNodeTest) {
|
|
1349
1340
|
lines.push(` %tla_program_exit = call i32 @scr_test_exit_code()`);
|
|
1350
1341
|
}
|
|
1351
|
-
|
|
1352
|
-
lines.push(` %tla_program_exit = call i32 @scr_island_exit_code()`);
|
|
1353
|
-
}
|
|
1354
|
-
if (usesNodeTest || usesIsland) {
|
|
1342
|
+
if (usesNodeTest) {
|
|
1355
1343
|
lines.push(` %tla_program_exit_zero = icmp eq i32 %tla_program_exit, 0`, ` %tla_exit_status = select i1 %tla_program_exit_zero, i32 %tla_status, i32 %tla_program_exit`);
|
|
1356
1344
|
}
|
|
1357
|
-
const exitStatus = usesNodeTest
|
|
1358
|
-
const tracksIslandExit = programExitUsesIsland && inlineExitListeners;
|
|
1359
|
-
if (tracksIslandExit) {
|
|
1360
|
-
lines.push(` %tla_exit_version = call ${this.sizeType} @scr_island_exit_code_version()`);
|
|
1361
|
-
}
|
|
1345
|
+
const exitStatus = usesNodeTest ? "%tla_exit_status" : "%tla_status";
|
|
1362
1346
|
// finish_top_level initially notes 13. Replace that hint before exit
|
|
1363
1347
|
// listeners run when a higher-priority verdict was already selected.
|
|
1364
1348
|
lines.push(` call void @scr_exit_code_note(i32 ${exitStatus})`);
|
|
1365
1349
|
for (const line of exitListenerLines("xp"))
|
|
1366
1350
|
lines.push(line);
|
|
1367
|
-
if (tracksIslandExit) {
|
|
1368
|
-
lines.push(` %tla_exit_version_after = call ${this.sizeType} @scr_island_exit_code_version()`, ` %tla_exit_changed = icmp ne ${this.sizeType} %tla_exit_version_after, %tla_exit_version`, ` br i1 %tla_exit_changed, label %tla_exit_updated, label %tla_exit_unchanged`, `tla_exit_updated:`, ` %tla_listener_exit = call i32 @scr_island_exit_code()`, ` call void @scr_exit_code_note(i32 %tla_listener_exit)`, ` br label %tla_exit_done`, `tla_exit_unchanged:`, ` br label %tla_exit_done`, `tla_exit_done:`, ` %tla_final_exit = phi i32 [ %tla_listener_exit, %tla_exit_updated ], [ ${exitStatus}, %tla_exit_unchanged ]`);
|
|
1369
|
-
}
|
|
1370
1351
|
for (const line of topPendingReleases)
|
|
1371
1352
|
lines.push(line);
|
|
1372
|
-
lines.push(` ret i32 ${
|
|
1353
|
+
lines.push(` ret i32 ${exitStatus}`);
|
|
1373
1354
|
return lines;
|
|
1374
1355
|
};
|
|
1375
1356
|
// LIBRARY mode: the runtime entry points the generated library
|
|
@@ -1424,6 +1405,16 @@ export class LlEmitter {
|
|
|
1424
1405
|
];
|
|
1425
1406
|
if (this.constantNumericTables.size > 0)
|
|
1426
1407
|
this.declare(`declare double @scr_arr_get_number(ptr, double)`);
|
|
1408
|
+
// Native matchers of the regex literals inside the compiled subset.
|
|
1409
|
+
const regexMatchers = new Map();
|
|
1410
|
+
for (const [key, re] of this.regexInstances) {
|
|
1411
|
+
const code = compileRegexMatcher(`${re.sym}_match`, re.pattern, re.flags, this.sizeType, !this.wasi);
|
|
1412
|
+
if (code === null)
|
|
1413
|
+
continue;
|
|
1414
|
+
regexMatchers.set(key, code);
|
|
1415
|
+
for (const decl of code.decls)
|
|
1416
|
+
this.declare(decl);
|
|
1417
|
+
}
|
|
1427
1418
|
const out = [
|
|
1428
1419
|
`; Generated by scriptc (LLVM backend) from ${this.mod.sourceFile}. Do not edit.`,
|
|
1429
1420
|
``,
|
|
@@ -1437,17 +1428,17 @@ export class LlEmitter {
|
|
|
1437
1428
|
`%ScrLogArg = type { i32, i64 }`,
|
|
1438
1429
|
`%ScrVt = type { ${this.sizeType}, ${this.sizeType}, ptr }`,
|
|
1439
1430
|
`%ScrUnion = type { ${this.sizeType}, i32, ptr, ptr, ptr, i64 }`,
|
|
1440
|
-
`%ScrClosure = type { ${this.sizeType}, ptr, ${this.sizeType}, ptr, i32, i32, ptr }`,
|
|
1431
|
+
`%ScrClosure = type { ${this.sizeType}, ptr, ${this.sizeType}, ptr, i32, i32, ptr, ptr }`,
|
|
1441
1432
|
`%ScrFfiTable = type { ptr, ${this.sizeType}, ${this.sizeType}, ptr, i8, ptr, ptr, ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, ptr, ptr }`,
|
|
1442
|
-
`%ScrRegex = type { ${this.sizeType}, ptr, ptr, ptr, double, ptr, ptr }`,
|
|
1433
|
+
`%ScrRegex = type { ${this.sizeType}, ptr, ptr, ptr, double, ptr, ptr, ptr }`,
|
|
1443
1434
|
// ScrArr mirrors scr_runtime.h field-for-field: { rc, len, cap, elem,
|
|
1444
|
-
// cyc, ops, data, present, ext } (field numbers in
|
|
1445
|
-
// where ops points at the element type's constant RC descriptor
|
|
1435
|
+
// cyc, spare, filled, ops, data, present, ext } (field numbers in
|
|
1436
|
+
// SCR_ARR_FIELD), where filled bounds the hole-free prefix, ops points at the element type's constant RC descriptor
|
|
1446
1437
|
// (%ScrArrOps, SCR_ELEM_REF only) and ext at the out-of-line sparse,
|
|
1447
1438
|
// property and metadata storage (NULL until first used). Live dynamic
|
|
1448
1439
|
// stream commits swap its dense, presence and out-of-line storage while
|
|
1449
1440
|
// preserving the target object's identity.
|
|
1450
|
-
`%ScrArr = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType},
|
|
1441
|
+
`%ScrArr = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, i8, i8, i16, i32, ptr, ptr, ptr, ptr }`,
|
|
1451
1442
|
`%ScrArrOps = type { ptr, ptr, ptr }`,
|
|
1452
1443
|
// The ScrMap prefix { rc, key_kind, val_kind, val_retain, val_release,
|
|
1453
1444
|
// val_trace, key_retain, key_release, key_trace } — the inline RC
|
|
@@ -1456,11 +1447,12 @@ export class LlEmitter {
|
|
|
1456
1447
|
? []
|
|
1457
1448
|
: [`%ScrMapRc = type { ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr }`]),
|
|
1458
1449
|
// The complete ScrMap and its { key, val, hash } entry, read by the
|
|
1459
|
-
// inline number-key lookup (sc_map_entry_f64):
|
|
1460
|
-
//
|
|
1461
|
-
|
|
1450
|
+
// inline number-key lookup (sc_map_entry_f64): field 11 is the i64
|
|
1451
|
+
// absent-key filter, entries is field 13, the direct index and its
|
|
1452
|
+
// length fields 19 and 20.
|
|
1453
|
+
...(this.decls.has(NUMBER_MAP_ENTRY_DECL) || this.decls.has(STRING_MAP_ENTRY_DECL)
|
|
1462
1454
|
? [
|
|
1463
|
-
`%ScrMapIx = type { ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr, ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, ptr, ${this.sizeType}, ptr, ${this.sizeType}, ptr, ptr, ptr, ${this.sizeType} }`,
|
|
1455
|
+
`%ScrMapIx = type { ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr, ${this.sizeType}, ${this.sizeType}, i64, ${this.sizeType}, ptr, ${this.sizeType}, ptr, ${this.sizeType}, ptr, ptr, ptr, ${this.sizeType} }`,
|
|
1464
1456
|
]
|
|
1465
1457
|
: []),
|
|
1466
1458
|
// String-keyed reference reads load the value slot of the entry
|
|
@@ -1544,6 +1536,26 @@ export class LlEmitter {
|
|
|
1544
1536
|
}
|
|
1545
1537
|
if (this.literals.size > 0)
|
|
1546
1538
|
out.push(``);
|
|
1539
|
+
for (const lit of this.bigintLiterals.values()) {
|
|
1540
|
+
// Immortal ScrBigInt: { rc = SIZE_MAX, len, sign, cap, limbs } with
|
|
1541
|
+
// pointer-sized little-endian limbs (scr_bigint.c); zero keeps one
|
|
1542
|
+
// zero limb so limb 0 is always readable.
|
|
1543
|
+
const size = this.sizeType;
|
|
1544
|
+
const limbBits = size === "i64" ? 64n : 32n;
|
|
1545
|
+
const mask = (1n << limbBits) - 1n;
|
|
1546
|
+
const limbs = [];
|
|
1547
|
+
for (let m = lit.value < 0n ? -lit.value : lit.value; m > 0n; m = m >> limbBits) {
|
|
1548
|
+
limbs.push(`${size} ${m & mask}`);
|
|
1549
|
+
}
|
|
1550
|
+
const len = limbs.length;
|
|
1551
|
+
if (len === 0)
|
|
1552
|
+
limbs.push(`${size} 0`);
|
|
1553
|
+
const sign = lit.value < 0n ? -1 : lit.value > 0n ? 1 : 0;
|
|
1554
|
+
out.push(`@${lit.sym} = internal global { ${size}, ${size}, i32, i32, [${limbs.length} x ${size}] } ` +
|
|
1555
|
+
`{ ${size} -1, ${size} ${len}, i32 ${sign}, i32 ${limbs.length}, [${limbs.length} x ${size}] [${limbs.join(", ")}] }`);
|
|
1556
|
+
}
|
|
1557
|
+
if (this.bigintLiterals.size > 0)
|
|
1558
|
+
out.push(``);
|
|
1547
1559
|
for (const [key, sym] of this.unitInstances) {
|
|
1548
1560
|
// One immortal instance per unit-armed (union, tag): tag set, payload
|
|
1549
1561
|
// slot and RC entry points zero — retain/release/collector all skip
|
|
@@ -1553,6 +1565,13 @@ export class LlEmitter {
|
|
|
1553
1565
|
}
|
|
1554
1566
|
if (this.unitInstances.size > 0)
|
|
1555
1567
|
out.push(``);
|
|
1568
|
+
for (const [unionId, sym] of this.retypeGuards) {
|
|
1569
|
+
// Thread-local like module globals: a thread's cast caches hold only
|
|
1570
|
+
// its own boxes. Never written, the flag folds to false.
|
|
1571
|
+
out.push(`@${sym} = internal ${tl}global i8 0 ; ${unionId} boxes in a cast cache`);
|
|
1572
|
+
}
|
|
1573
|
+
if (this.retypeGuards.size > 0)
|
|
1574
|
+
out.push(``);
|
|
1556
1575
|
for (const [unionId, sym] of this.absentInstances) {
|
|
1557
1576
|
// One immortal ABSENT field-slot instance per undefined-armed union:
|
|
1558
1577
|
// the undefined arm's tag with payload 1 (ordinary unit instances
|
|
@@ -1580,71 +1599,34 @@ export class LlEmitter {
|
|
|
1580
1599
|
}
|
|
1581
1600
|
for (const [key, re] of this.regexInstances) {
|
|
1582
1601
|
// One immortal ScrRegex per (pattern, flags) literal, pointing at
|
|
1583
|
-
// the interned source/flags strings. The bc and
|
|
1584
|
-
// start null (lazy compile, cached by the runtime) — a mutable
|
|
1585
|
-
// global, not constant.
|
|
1586
|
-
|
|
1602
|
+
// the interned source/flags strings. The bc and translated-program
|
|
1603
|
+
// slots start null (lazy compile, cached by the runtime) — a mutable
|
|
1604
|
+
// global, not constant. The last slot is the build-time matcher.
|
|
1605
|
+
const matcher = regexMatchers.has(key) ? `@${re.sym}_match` : "null";
|
|
1606
|
+
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, ptr ${matcher} } ; ${key.replace(/\n/g, "\\n")}`);
|
|
1587
1607
|
}
|
|
1588
1608
|
if (this.regexInstances.size > 0)
|
|
1589
1609
|
out.push(``);
|
|
1610
|
+
for (const code of regexMatchers.values())
|
|
1611
|
+
out.push(...code.lines, ``);
|
|
1590
1612
|
for (const [, inst] of this.templateStringsInstances) {
|
|
1591
1613
|
// One immortal ScrArr per tagged-template site: a [N x ptr] data
|
|
1592
1614
|
// global of interned cooked-string literals, and the ScrArr header
|
|
1593
|
-
// over it (rc == SIZE_MAX, len == cap, SCR_ELEM_STR = 2,
|
|
1594
|
-
// entry points). Every dense slot is present; reads retain
|
|
1595
|
-
// strings — a no-op.
|
|
1615
|
+
// over it (rc == SIZE_MAX, len == cap == filled, SCR_ELEM_STR = 2,
|
|
1616
|
+
// no REF entry points). Every dense slot is present; reads retain
|
|
1617
|
+
// immortal strings — a no-op.
|
|
1596
1618
|
const n = inst.slots.length;
|
|
1597
1619
|
const present = n === 0 ? "zeroinitializer" : `[ ${inst.slots.map(() => "i8 1").join(", ")} ]`;
|
|
1598
|
-
out.push(`@${inst.sym}_data = internal constant [${n} x ptr] [ ${inst.slots.map((s) => `ptr ${s}`).join(", ")} ]`, `@${inst.sym}_present = internal constant [${n} x i8] ${present}`, `@${inst.sym} = internal global %ScrArr { ${this.sizeType} -1, ${this.sizeType} ${n}, ${this.sizeType} ${n},
|
|
1620
|
+
out.push(`@${inst.sym}_data = internal constant [${n} x ptr] [ ${inst.slots.map((s) => `ptr ${s}`).join(", ")} ]`, `@${inst.sym}_present = internal constant [${n} x i8] ${present}`, `@${inst.sym} = internal global %ScrArr { ${this.sizeType} -1, ${this.sizeType} ${n}, ${this.sizeType} ${n}, i8 2, i8 0, i16 0, i32 ${n}, ptr null, ptr @${inst.sym}_data, ptr @${inst.sym}_present, ptr null }`);
|
|
1599
1621
|
}
|
|
1600
1622
|
if (this.templateStringsInstances.size > 0)
|
|
1601
1623
|
out.push(``);
|
|
1602
|
-
const islandEntryPath = usesIsland ? this.cstr(this.mod.sourceFile) : null;
|
|
1603
1624
|
for (const [text, c] of this.cstrs) {
|
|
1604
1625
|
// NUL-terminated byte-array constants for scr_jb_puts and indentation.
|
|
1605
1626
|
out.push(`@${c.sym} = internal constant [${c.len + 1} x i8] c"${llStrBytes(text)}"`);
|
|
1606
1627
|
}
|
|
1607
1628
|
if (this.cstrs.size > 0)
|
|
1608
1629
|
out.push(``);
|
|
1609
|
-
if (usesIsland) {
|
|
1610
|
-
const fmt = { esm: 0, cjs: 1, json: 2 };
|
|
1611
|
-
const edgeKind = { any: 0, import: 1, require: 2 };
|
|
1612
|
-
embedded.modules.forEach((m, i) => {
|
|
1613
|
-
const stored = npmStored[i];
|
|
1614
|
-
const key = Buffer.from(m.key, "utf8");
|
|
1615
|
-
out.push(`@sc_npm_key_${i} = internal constant [${key.length + 1} x i8] c"${llBytes(key)}"`, `@sc_npm_src_${i} = internal constant [${stored.src.bytes.length + 1} x i8] c"${llBytes(stored.src.bytes)}"`);
|
|
1616
|
-
if (stored.esm !== null) {
|
|
1617
|
-
out.push(`@sc_npm_esm_${i} = internal constant [${stored.esm.bytes.length + 1} x i8] c"${llBytes(stored.esm.bytes)}"`);
|
|
1618
|
-
}
|
|
1619
|
-
});
|
|
1620
|
-
const moduleRows = embedded.modules.map((m, i) => {
|
|
1621
|
-
const stored = npmStored[i];
|
|
1622
|
-
const esm = stored.esm === null
|
|
1623
|
-
? `ptr null, ${this.sizeType} 0, ${this.sizeType} 0`
|
|
1624
|
-
: `ptr @sc_npm_esm_${i}, ${this.sizeType} ${stored.esm.bytes.length}, ${this.sizeType} ${stored.esm.raw}`;
|
|
1625
|
-
return (`%ScrIslandModule { ptr @sc_npm_key_${i}, ptr @sc_npm_src_${i}, ` +
|
|
1626
|
-
`${this.sizeType} ${stored.src.bytes.length}, ${this.sizeType} ${stored.src.raw}, ` +
|
|
1627
|
-
`i32 ${fmt[m.format]}, ${esm} }`);
|
|
1628
|
-
});
|
|
1629
|
-
out.push(`@sc_npm_modules = internal constant [${moduleRows.length} x %ScrIslandModule] [ ${moduleRows.join(", ")} ]`);
|
|
1630
|
-
embedded.edges.forEach((edge, i) => {
|
|
1631
|
-
const parts = [
|
|
1632
|
-
["from", edge.from],
|
|
1633
|
-
["spec", edge.specifier],
|
|
1634
|
-
["to", edge.to],
|
|
1635
|
-
];
|
|
1636
|
-
for (const [part, text] of parts) {
|
|
1637
|
-
const bytes = Buffer.from(text, "utf8");
|
|
1638
|
-
out.push(`@sc_npm_edge_${i}_${part} = internal constant [${bytes.length + 1} x i8] c"${llBytes(bytes)}"`);
|
|
1639
|
-
}
|
|
1640
|
-
});
|
|
1641
|
-
if (embedded.edges.length > 0) {
|
|
1642
|
-
const edgeRows = embedded.edges.map((edge, i) => `%ScrIslandEdge { ptr @sc_npm_edge_${i}_from, ptr @sc_npm_edge_${i}_spec, ` +
|
|
1643
|
-
`ptr @sc_npm_edge_${i}_to, i32 ${edgeKind[edge.kind]} }`);
|
|
1644
|
-
out.push(`@sc_npm_edges = internal constant [${edgeRows.length} x %ScrIslandEdge] [ ${edgeRows.join(", ")} ]`);
|
|
1645
|
-
}
|
|
1646
|
-
out.push(``);
|
|
1647
|
-
}
|
|
1648
1630
|
for (const line of ffiCallbacks.globals)
|
|
1649
1631
|
out.push(line);
|
|
1650
1632
|
if (ffiCallbacks.globals.length > 0)
|
|
@@ -1734,7 +1716,7 @@ export class LlEmitter {
|
|
|
1734
1716
|
out.push(line);
|
|
1735
1717
|
out.push(...this.inlineRcTail(flushedDecls));
|
|
1736
1718
|
out.push(`attributes #0 = { sanitize_address }`);
|
|
1737
|
-
if (this.
|
|
1719
|
+
if (this.usesCoroutines)
|
|
1738
1720
|
out.push(`attributes #1 = { sanitize_address presplitcoroutine }`);
|
|
1739
1721
|
if (hasNoInlineRecordClone)
|
|
1740
1722
|
out.push(`attributes #2 = { noinline sanitize_address }`);
|
|
@@ -1758,16 +1740,7 @@ export class LlEmitter {
|
|
|
1758
1740
|
: []),
|
|
1759
1741
|
// fs.watch programs fill the loop's watch hooks the same way —
|
|
1760
1742
|
// scr_watch.c links only when this line is emitted.
|
|
1761
|
-
...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(this.ffiHasForeignCallback ? [` call void @scr_ffi_install()`] : []), ...(snapshotsTlsCa ? [` call void @scr_tls_ca_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(
|
|
1762
|
-
? [
|
|
1763
|
-
` call void @scr_island_entry_module(ptr ${islandEntryPath})`,
|
|
1764
|
-
...(moduleEmbedsCompressedNpm(this.mod)
|
|
1765
|
-
? [` call void @scr_island_set_inflate(ptr @scr_zlib_inflate_exact)`]
|
|
1766
|
-
: []),
|
|
1767
|
-
` call void @scr_island_modules(ptr @sc_npm_modules, ${this.sizeType} ${embedded.modules.length}, ` +
|
|
1768
|
-
`ptr ${embedded.edges.length > 0 ? "@sc_npm_edges" : "null"}, ${this.sizeType} ${embedded.edges.length})`,
|
|
1769
|
-
]
|
|
1770
|
-
: []), ...(asyncEntry
|
|
1743
|
+
...(usesFsWatch ? [` call void @scr_watch_install()`] : []), ...(this.ffiHasForeignCallback ? [` call void @scr_ffi_install()`] : []), ...(snapshotsTlsCa ? [` call void @scr_tls_ca_install()`] : []), ...(usesFetch ? [` call void @scr_fetch_install()`] : []), ...(usesNet ? [` call void @scr_net_install()`, ` call void @scr_net_dyn_install()`] : []), ...(usesHttp ? [` call void @scr_http_dyn_install()`] : []), ...(usesDgram ? [` call void @scr_dgram_install()`] : []), ...(usesHttp2 ? [` call void @scr_http2_dyn_install()`] : []), ...(usesStream ? [` call void @scr_stream_install()`] : []), ` call void @scr_lib_init(i32 %argc, ptr %argv)`, ...(asyncEntry
|
|
1771
1744
|
? [` %top = call ptr @${mangleAsyncSpawn(this.mod.entry)}()`]
|
|
1772
1745
|
: [` call void @${mangleFunction(this.mod.entry)}()`]),
|
|
1773
1746
|
// Uncaught exception from top-level code: Node exits 1.
|
|
@@ -1854,13 +1827,11 @@ export class LlEmitter {
|
|
|
1854
1827
|
]
|
|
1855
1828
|
: []), ...exitListenerLines("xn"), ...globalReleases, ...(usesNodeTest
|
|
1856
1829
|
? [` %test_exit = call i32 @scr_test_exit_code()`, ` ret i32 %test_exit`]
|
|
1857
|
-
:
|
|
1858
|
-
? [` %island_exit = call i32 @scr_island_exit_code()`, ` ret i32 %island_exit`]
|
|
1859
|
-
: [` %native_exit = call i32 @scr_exit_code_hint_get()`, ` ret i32 %native_exit`]), `}`, ``, ...this.inlineRcTail(flushedDecls),
|
|
1830
|
+
: [` %native_exit = call i32 @scr_exit_code_hint_get()`, ` ret i32 %native_exit`]), `}`, ``, ...this.inlineRcTail(flushedDecls),
|
|
1860
1831
|
// sanitize_address is inert under the plain pipeline; the sanitized
|
|
1861
1832
|
// lane's -fsanitize=address link activates instrumentation over the
|
|
1862
1833
|
// emitted functions too (the runtime TUs get theirs from clang).
|
|
1863
|
-
`attributes #0 = { sanitize_address }`, ...(this.
|
|
1834
|
+
`attributes #0 = { sanitize_address }`, ...(this.usesCoroutines ? [`attributes #1 = { sanitize_address presplitcoroutine }`] : []), ...(hasNoInlineRecordClone ? [`attributes #2 = { noinline sanitize_address }`] : []), ``);
|
|
1864
1835
|
if (this.mod.workers) {
|
|
1865
1836
|
out.push(`declare void @scr_context_cleanup()`, `declare i32 @scr_worker_argc()`, `declare ptr @scr_worker_argv()`, `define i32 @sc_worker_entry(i32 %root) ${FN_ATTRS} {`, `entry:`, ` %argc = call i32 @scr_worker_argc()`, ` %argv = call ptr @scr_worker_argv()`, ` %result = call i32 @sc_context_entry(i32 %argc, ptr %argv)`, ` ret i32 %result`, `}`, `define i32 @main(i32 %argc, ptr %argv) ${FN_ATTRS} {`, `entry:`, ` %result = call i32 @sc_context_entry(i32 %argc, ptr %argv)`, ` call void @scr_context_cleanup()`, ` ret i32 %result`, `}`);
|
|
1866
1837
|
}
|
|
@@ -2122,11 +2093,44 @@ export class LlEmitter {
|
|
|
2122
2093
|
const sz = this.sizeType;
|
|
2123
2094
|
let mapTmp = 0;
|
|
2124
2095
|
const mapIndex = exactIndexLines("%k", "%nd", sz, () => `%ix${mapTmp++}`);
|
|
2125
|
-
defs.push(`define internal ptr @sc_map_entry_f64(ptr %m, double %k) ${FN_ATTRS} {`, `entry:`, ` %ndp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32
|
|
2096
|
+
defs.push(`define internal ptr @sc_map_entry_f64(ptr %m, double %k) ${FN_ATTRS} {`, `entry:`, ` %ndp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 20`, ` %nd = load ${sz}, ptr %ndp`,
|
|
2126
2097
|
// A non-integral key inside the table is as absent as one outside it.
|
|
2127
2098
|
...mapIndex.lines.map((line) => ` ${line}`), ` br i1 ${mapIndex.ok}, label %slot, label %outside`, `slot:`, sz === "i64"
|
|
2128
2099
|
? ` %i = add i64 ${mapIndex.wide}, 0`
|
|
2129
|
-
: ` %i = trunc i64 ${mapIndex.wide} to ${sz}`, ` %dp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32
|
|
2100
|
+
: ` %i = trunc i64 ${mapIndex.wide} to ${sz}`, ` %dp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 19`, ` %dense = load ptr, ptr %dp`, ` %sp = getelementptr inbounds i32, ptr %dense, ${sz} %i`, ` %s = load i32, ptr %sp`, ` %empty = icmp eq i32 %s, 0`, ` br i1 %empty, label %miss, label %hit`, `hit:`, ` %e32 = sub i32 %s, 1`, sz === "i32" ? ` %e = add i32 %e32, 0` : ` %e = zext i32 %e32 to ${sz}`, ` %ep = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 13`, ` %entries = load ptr, ptr %ep`, ` %found = getelementptr inbounds %ScrMapEntry, ptr %entries, ${sz} %e`, ` ret ptr %found`, `miss:`, ` ret ptr null`, `outside:`, ` %dense.any = icmp ne ${sz} %nd, 0`, ` br i1 %dense.any, label %miss, label %slow`, `slow:`, ` %r = call ptr @scr_map_entry_f64(ptr %m, double %k)`, ` ret ptr %r`, `}`, ``);
|
|
2101
|
+
}
|
|
2102
|
+
// String-key Map/Set lookups test the map's absent-key filter inline
|
|
2103
|
+
// (scr_map.c scr_map_filter_bits): a key whose two filter bits are not
|
|
2104
|
+
// both set is absent, so most misses on small and medium maps return
|
|
2105
|
+
// without a call. On 64-bit targets the key's cached hash h is the high
|
|
2106
|
+
// half of its capacity word, the runtime's widened hash is (h << 32) | h,
|
|
2107
|
+
// and the filter reads its bits 44..55, which are the capacity word's
|
|
2108
|
+
// own. A key without a cached hash, and every possible hit, take the
|
|
2109
|
+
// runtime lookup.
|
|
2110
|
+
if (this.decls.has(STRING_MAP_ENTRY_DECL)) {
|
|
2111
|
+
defs.push(`define internal ptr @sc_map_entry_str(ptr %m, ptr %k) ${FN_ATTRS} {`, `entry:`, ...(this.sizeType === "i64"
|
|
2112
|
+
? [
|
|
2113
|
+
` %fp = getelementptr inbounds %ScrMapIx, ptr %m, i32 0, i32 11`,
|
|
2114
|
+
` %filter = load i64, ptr %fp`,
|
|
2115
|
+
` %capp = getelementptr inbounds %ScrStr, ptr %k, i32 0, i32 2`,
|
|
2116
|
+
` %cap = load i64, ptr %capp`,
|
|
2117
|
+
` %cached = icmp uge i64 %cap, 4294967296`,
|
|
2118
|
+
` br i1 %cached, label %test, label %slow`,
|
|
2119
|
+
`test:`,
|
|
2120
|
+
` %s1 = lshr i64 %cap, 44`,
|
|
2121
|
+
` %i1 = and i64 %s1, 63`,
|
|
2122
|
+
` %b1 = shl i64 1, %i1`,
|
|
2123
|
+
` %s2 = lshr i64 %cap, 50`,
|
|
2124
|
+
` %i2 = and i64 %s2, 63`,
|
|
2125
|
+
` %b2 = shl i64 1, %i2`,
|
|
2126
|
+
` %bits = or i64 %b1, %b2`,
|
|
2127
|
+
` %have = and i64 %filter, %bits`,
|
|
2128
|
+
` %maybe = icmp eq i64 %have, %bits`,
|
|
2129
|
+
` br i1 %maybe, label %slow, label %absent`,
|
|
2130
|
+
`absent:`,
|
|
2131
|
+
` ret ptr null`,
|
|
2132
|
+
]
|
|
2133
|
+
: [` br label %slow`]), `slow:`, ` %r = call ptr @scr_map_entry_str(ptr %m, ptr %k)`, ` ret ptr %r`, `}`, ``);
|
|
2130
2134
|
}
|
|
2131
2135
|
// String comparisons enter through small internal wrappers that LLVM
|
|
2132
2136
|
// inlines at each site: interned literals and shared values compare by
|
|
@@ -2135,6 +2139,9 @@ export class LlEmitter {
|
|
|
2135
2139
|
if (this.decls.has(`declare zeroext i1 @scr_str_eq(ptr, ptr)`)) {
|
|
2136
2140
|
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`, `}`, ``);
|
|
2137
2141
|
}
|
|
2142
|
+
for (const [text, name] of this.literalEqualities) {
|
|
2143
|
+
defs.push(...literalEqualityDefinition(name, Buffer.from(text, "utf8"), this.internLiteral(text), this.sizeType, FN_ATTRS), ``);
|
|
2144
|
+
}
|
|
2138
2145
|
// String length and charCodeAt read proven-ASCII strings inline: bit 31
|
|
2139
2146
|
// of the capacity word (SCR_STR_ASCII_BIT, the low half's sign bit on
|
|
2140
2147
|
// little-endian targets) makes UTF-16 units bytes. Unproven strings and
|
|
@@ -2218,7 +2225,12 @@ export class LlEmitter {
|
|
|
2218
2225
|
// never the inner return type.
|
|
2219
2226
|
const ret = fn.async === true || fn.generator !== undefined ? "ptr" : this.llType(fn.returnType);
|
|
2220
2227
|
const call = `call ${ret} @${this.callTarget(name)}(${args})`;
|
|
2221
|
-
|
|
2228
|
+
const lending = this.lendsEveryArgument(name);
|
|
2229
|
+
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, ptr ${lending ? `@${mangleBorrowingWrapper(name)}` : "null"} }`, ``);
|
|
2230
|
+
if (lending)
|
|
2231
|
+
out.push(`define internal ${ret} @${mangleBorrowingWrapper(name)}(ptr %env${params.length ? ", " + params.join(", ") : ""}) ${FN_ATTRS} { ; ${name} as a value, lending its arguments`, `entry:`, ret === "void"
|
|
2232
|
+
? ` call void @${mangleBorrowedFunction(name)}(${args})`
|
|
2233
|
+
: ` %r = call ${ret} @${mangleBorrowedFunction(name)}(${args})`, ret === "void" ? ` ret void` : ` ret ${ret} %r`, `}`, ``);
|
|
2222
2234
|
}
|
|
2223
2235
|
return out;
|
|
2224
2236
|
}
|
|
@@ -2231,8 +2243,14 @@ export class LlEmitter {
|
|
|
2231
2243
|
const fieldTys = [...(lifted ? ["ptr"] : []), ...fn.params.map((p) => this.llType(p.type))];
|
|
2232
2244
|
const definitions = [`%${pack} = type { ${fieldTys.join(", ") || "i8"} } ; ${fn.name} args`];
|
|
2233
2245
|
const sizeOf = `ptrtoint (ptr getelementptr (%${pack}, ptr null, i32 1) to ${this.sizeType})`;
|
|
2234
|
-
|
|
2235
|
-
|
|
2246
|
+
// Async argument packs live from the spawn to the trampoline's first
|
|
2247
|
+
// instructions, so they take the runtime's small-block allocator.
|
|
2248
|
+
// Generator packs keep malloc: the runtime frees never-started ones.
|
|
2249
|
+
const smallPack = fn.generator === undefined;
|
|
2250
|
+
const packAlloc = smallPack ? "scr_rt_calloc" : "malloc";
|
|
2251
|
+
const packFree = smallPack ? "scr_rt_free" : "free";
|
|
2252
|
+
this.declare(`declare void @${packFree}(ptr)`);
|
|
2253
|
+
this.declare(`declare ptr @${packAlloc}(${this.sizeType})`);
|
|
2236
2254
|
const tr = [
|
|
2237
2255
|
`define internal void @${mangleTrampoline(fn.name)}(ptr %self, ptr %ap) ${FN_ATTRS} {`,
|
|
2238
2256
|
`entry:`,
|
|
@@ -2242,17 +2260,17 @@ export class LlEmitter {
|
|
|
2242
2260
|
tr.push(` %fp${i} = getelementptr inbounds %${pack}, ptr %ap, i64 0, i32 ${i}`, ` %a${i} = load ${ty}, ptr %fp${i}`);
|
|
2243
2261
|
loads.push(`${ty} %a${i}`);
|
|
2244
2262
|
});
|
|
2245
|
-
tr.push(` call void
|
|
2263
|
+
tr.push(` call void @${packFree}(ptr %ap)`);
|
|
2246
2264
|
const ret = fn.returnType;
|
|
2247
2265
|
const retTy = this.llType(ret);
|
|
2248
2266
|
const bodyCall = `call ${retTy} @${mangleFunction(fn.name)}(${loads.join(", ")})`;
|
|
2249
2267
|
tr.push(retTy === "void" ? ` ${bodyCall}` : ` %r = ${bodyCall}`);
|
|
2250
|
-
if (lifted && !this.
|
|
2268
|
+
if (lifted && !this.coroLowered(fn)) {
|
|
2251
2269
|
tr.push(` call void @scr_closure_release(ptr %a0)`);
|
|
2252
2270
|
}
|
|
2253
2271
|
const spawnParams = fieldTys.map((ty, i) => `${ty} %a${i}`);
|
|
2254
2272
|
const argPackLines = [
|
|
2255
|
-
` %ap = call ptr
|
|
2273
|
+
` %ap = call ptr @${packAlloc}(${this.sizeType} ${sizeOf})`,
|
|
2256
2274
|
` %isnull = icmp eq ptr %ap, null`,
|
|
2257
2275
|
` br i1 %isnull, label %oom, label %ok`,
|
|
2258
2276
|
`oom:`,
|
|
@@ -2305,8 +2323,8 @@ export class LlEmitter {
|
|
|
2305
2323
|
* mentions (transitively), contains no suspension entry point. Such a
|
|
2306
2324
|
* body always completes before the call returns, which is exactly the
|
|
2307
2325
|
* behavior of a fiber that never suspends. Excluded conservatively:
|
|
2308
|
-
* wasm32 (its coroutine lowering owns async bodies),
|
|
2309
|
-
*
|
|
2326
|
+
* wasm32 (its coroutine lowering owns async bodies), module-evaluation
|
|
2327
|
+
* bodies (their promise caches),
|
|
2310
2328
|
* `dyn` results (resolution may adopt a thenable), and every program
|
|
2311
2329
|
* that uses node:test subtests — `t.test()` runs a subtest inline and
|
|
2312
2330
|
* awaits it on the CURRENT fiber even from synchronous code. Worker
|
|
@@ -2319,10 +2337,7 @@ export class LlEmitter {
|
|
|
2319
2337
|
const eligible = new Set();
|
|
2320
2338
|
if (this.wasi || this.mod.workers === true)
|
|
2321
2339
|
return eligible;
|
|
2322
|
-
const
|
|
2323
|
-
if (embedded !== undefined && embedded.modules.length > 0)
|
|
2324
|
-
return eligible;
|
|
2325
|
-
const suspends = /@scr_(?:await_|module_await\b|promise_await_settled\b|test_sub\b|gen_yield_|async_gen_|fiber_)/;
|
|
2340
|
+
const suspends = /@(?:scr_(?:await_|module_await\b|promise_await_settled\b|test_sub\b|gen_yield_|async_gen_|fiber_)|sc_a[wd]_)/;
|
|
2326
2341
|
for (const decl of this.decls)
|
|
2327
2342
|
if (/@scr_test_sub\b/.test(decl))
|
|
2328
2343
|
return eligible;
|
|
@@ -2418,6 +2433,133 @@ export class LlEmitter {
|
|
|
2418
2433
|
out.push(` call void @scr_promise_reject_pending(ptr %p)`, ` ret ptr %p`, `}`, ``);
|
|
2419
2434
|
return out;
|
|
2420
2435
|
}
|
|
2436
|
+
/** Whether `await f(...)` may enter f through its await-call entry. The
|
|
2437
|
+
* entry's result is the awaited value of f's own body type; direct calls
|
|
2438
|
+
* never carry a lifted environment. */
|
|
2439
|
+
awaitCallEligible(fnName) {
|
|
2440
|
+
if (this.wasi || this.mod.workers === true)
|
|
2441
|
+
return false;
|
|
2442
|
+
const fn = this.fnByName.get(fnName);
|
|
2443
|
+
if (fn === undefined || fn.async !== true || fn.generator !== undefined)
|
|
2444
|
+
return false;
|
|
2445
|
+
if (fn.captures !== undefined || fn.returnType.kind === "dyn")
|
|
2446
|
+
return false;
|
|
2447
|
+
return fn.asyncCacheGlobal === undefined && fn.asyncCycleCacheGlobal === undefined;
|
|
2448
|
+
}
|
|
2449
|
+
/** The await-call entry of an async function (`sc_aw_*`): one direct
|
|
2450
|
+
* `await f(...)` as a call returning the awaited value, so the call site
|
|
2451
|
+
* needs no promise of its own. A fiberless body runs on the caller's
|
|
2452
|
+
* stack as in its spawn wrapper; a clean result then takes the await's
|
|
2453
|
+
* one microtask turn (scr_await_hop) and is returned without ever
|
|
2454
|
+
* materializing the promise, which nothing else could observe. A throw
|
|
2455
|
+
* builds the rejected promise exactly as the spawn wrapper would and
|
|
2456
|
+
* awaits it, so the rejection reaches the awaiter on the same turn. Other
|
|
2457
|
+
* bodies pack their arguments as the spawn wrapper does and enter
|
|
2458
|
+
* scr_async_spawn_await_*, which parks the caller on the promise as soon
|
|
2459
|
+
* as the body first suspends. The adopting entry (`sc_ad_*`, for
|
|
2460
|
+
* `return f(...)` in an async function) adds the turn of the then job
|
|
2461
|
+
* that adopts the returned promise, before the await proper. */
|
|
2462
|
+
emitAsyncAwaitCall(fn, argPackLines, adopt = false) {
|
|
2463
|
+
const ret = fn.returnType;
|
|
2464
|
+
const retTy = this.llType(ret);
|
|
2465
|
+
const params = fn.params.map((p, i) => `${this.llType(p.type)} %a${i}`);
|
|
2466
|
+
const awaitLine = (dest, promise) => {
|
|
2467
|
+
switch (ret.kind) {
|
|
2468
|
+
case "void":
|
|
2469
|
+
this.declare(`declare void @scr_await_void(ptr)`);
|
|
2470
|
+
return ` call void @scr_await_void(ptr ${promise})`;
|
|
2471
|
+
case "f64":
|
|
2472
|
+
case "date":
|
|
2473
|
+
this.declare(`declare double @scr_await_f64(ptr)`);
|
|
2474
|
+
return ` ${dest} = call double @scr_await_f64(ptr ${promise})`;
|
|
2475
|
+
case "bool":
|
|
2476
|
+
this.declare(`declare zeroext i1 @scr_await_bool(ptr)`);
|
|
2477
|
+
return ` ${dest} = call zeroext i1 @scr_await_bool(ptr ${promise})`;
|
|
2478
|
+
case "string":
|
|
2479
|
+
this.declare(`declare ptr @scr_await_str(ptr)`);
|
|
2480
|
+
return ` ${dest} = call ptr @scr_await_str(ptr ${promise})`;
|
|
2481
|
+
default:
|
|
2482
|
+
this.declare(`declare ptr @scr_await_ref(ptr)`);
|
|
2483
|
+
return ` ${dest} = call ptr @scr_await_ref(ptr ${promise})`;
|
|
2484
|
+
}
|
|
2485
|
+
};
|
|
2486
|
+
const retLine = (v) => retTy === "void" ? ` ret void` : ` ret ${retTy} ${v}`;
|
|
2487
|
+
const head = adopt
|
|
2488
|
+
? `define internal ${retTy} @${mangleAsyncAdoptCall(fn.name)}(${params.join(", ")}) ${FN_ATTRS} { ; adopt ${fn.name}`
|
|
2489
|
+
: `define internal ${retTy} @${mangleAsyncAwaitCall(fn.name)}(${params.join(", ")}) ${FN_ATTRS} { ; await ${fn.name}`;
|
|
2490
|
+
const args = params.join(", ");
|
|
2491
|
+
if (adopt && argPackLines !== null) {
|
|
2492
|
+
// A body that may suspend settles later than the adoption's then job
|
|
2493
|
+
// runs: materialize its promise, take that turn, then await it.
|
|
2494
|
+
this.declare(`declare void @scr_await_hop()`);
|
|
2495
|
+
this.declare(`declare void @scr_promise_release(ptr)`);
|
|
2496
|
+
return [
|
|
2497
|
+
head,
|
|
2498
|
+
`entry:`,
|
|
2499
|
+
` %p = call ptr @${mangleAsyncSpawn(fn.name)}(${args})`,
|
|
2500
|
+
` call void @scr_await_hop()`,
|
|
2501
|
+
awaitLine("%v", "%p"),
|
|
2502
|
+
` call void @scr_promise_release(ptr %p)`,
|
|
2503
|
+
retLine("%v"),
|
|
2504
|
+
`}`,
|
|
2505
|
+
``,
|
|
2506
|
+
];
|
|
2507
|
+
}
|
|
2508
|
+
if (argPackLines !== null) {
|
|
2509
|
+
const kind = ret.kind === "void"
|
|
2510
|
+
? "void"
|
|
2511
|
+
: ret.kind === "f64" || ret.kind === "date"
|
|
2512
|
+
? "f64"
|
|
2513
|
+
: ret.kind === "bool"
|
|
2514
|
+
? "bool"
|
|
2515
|
+
: ret.kind === "string"
|
|
2516
|
+
? "str"
|
|
2517
|
+
: "ref";
|
|
2518
|
+
const cret = kind === "bool" ? "zeroext i1" : retTy;
|
|
2519
|
+
this.declare(`declare ${cret} @scr_async_spawn_await_${kind}(ptr, ptr)`);
|
|
2520
|
+
this.needOom();
|
|
2521
|
+
const call = `call ${cret} @scr_async_spawn_await_${kind}(ptr @${mangleTrampoline(fn.name)}, ptr %ap)`;
|
|
2522
|
+
return [
|
|
2523
|
+
head,
|
|
2524
|
+
`entry:`,
|
|
2525
|
+
...argPackLines,
|
|
2526
|
+
kind === "void" ? ` ${call}` : ` %v = ${call}`,
|
|
2527
|
+
retLine("%v"),
|
|
2528
|
+
`}`,
|
|
2529
|
+
``,
|
|
2530
|
+
];
|
|
2531
|
+
}
|
|
2532
|
+
this.declare(`declare void @scr_async_inline_enter(ptr)`);
|
|
2533
|
+
this.declare(`declare void @scr_async_inline_exit(ptr)`);
|
|
2534
|
+
this.declare(`declare void @scr_await_hop()`);
|
|
2535
|
+
this.declare(`declare ptr @scr_promise_new()`);
|
|
2536
|
+
this.declare(`declare void @scr_promise_reject_pending(ptr)`);
|
|
2537
|
+
this.declare(`declare void @scr_promise_release(ptr)`);
|
|
2538
|
+
const bodyCall = `call ${retTy} @${mangleFunction(fn.name)}(${args})`;
|
|
2539
|
+
const out = [
|
|
2540
|
+
head,
|
|
2541
|
+
`entry:`,
|
|
2542
|
+
` %frame = alloca { ptr, ptr }`,
|
|
2543
|
+
` call void @scr_async_inline_enter(ptr %frame)`,
|
|
2544
|
+
retTy === "void" ? ` ${bodyCall}` : ` %r = ${bodyCall}`,
|
|
2545
|
+
...this.pendingTestLines("%pend"),
|
|
2546
|
+
` call void @scr_async_inline_exit(ptr %frame)`,
|
|
2547
|
+
// Adopting: the then job's turn comes before the settled promise's
|
|
2548
|
+
// own reaction, whichever way the body completed.
|
|
2549
|
+
...(adopt ? [` call void @scr_await_hop()`] : []),
|
|
2550
|
+
` br i1 %pend, label %thrown, label %clean`,
|
|
2551
|
+
`clean:`,
|
|
2552
|
+
` call void @scr_await_hop()`,
|
|
2553
|
+
retLine("%r"),
|
|
2554
|
+
`thrown:`,
|
|
2555
|
+
];
|
|
2556
|
+
if (ret.kind !== "void" && isRefCounted(ret)) {
|
|
2557
|
+
// An escaping throw means %r is the never-read dummy (NULL).
|
|
2558
|
+
out.push(` call void ${releaseSym(this.shapeHost, ret)}(ptr %r)`);
|
|
2559
|
+
}
|
|
2560
|
+
out.push(` %p = call ptr @scr_promise_new()`, ` call void @scr_promise_reject_pending(ptr %p)`, awaitLine("%v", "%p"), ` call void @scr_promise_release(ptr %p)`, retLine("%v"), `}`, ``);
|
|
2561
|
+
return out;
|
|
2562
|
+
}
|
|
2421
2563
|
/** Per-async-function machinery — async.ts's scaffolding, .ll
|
|
2422
2564
|
* flavored: an argument-pack struct type, a fiber trampoline (unpacks,
|
|
2423
2565
|
* frees the pack, runs the ordinary compiled body, settles the
|
|
@@ -2436,6 +2578,14 @@ export class LlEmitter {
|
|
|
2436
2578
|
for (const fn of this.mod.functions) {
|
|
2437
2579
|
if (fn.async !== true || fn.generator !== undefined)
|
|
2438
2580
|
continue;
|
|
2581
|
+
if (fiberless.has(fn.name) && this.awaitCallees.has(fn.name)) {
|
|
2582
|
+
for (const line of this.emitAsyncAwaitCall(fn, null))
|
|
2583
|
+
out.push(line);
|
|
2584
|
+
}
|
|
2585
|
+
if (fiberless.has(fn.name) && this.adoptCallees.has(fn.name)) {
|
|
2586
|
+
for (const line of this.emitAsyncAwaitCall(fn, null, true))
|
|
2587
|
+
out.push(line);
|
|
2588
|
+
}
|
|
2439
2589
|
if (fiberless.has(fn.name)) {
|
|
2440
2590
|
const fieldTys = [
|
|
2441
2591
|
...(fn.captures !== undefined ? ["ptr"] : []),
|
|
@@ -2449,6 +2599,14 @@ export class LlEmitter {
|
|
|
2449
2599
|
const { definitions, ret, tr, spawnParams, argPackLines } = this.emitArgPackAndTrampolinePrologue(fn);
|
|
2450
2600
|
for (const line of definitions)
|
|
2451
2601
|
out.push(line);
|
|
2602
|
+
if (this.awaitCallees.has(fn.name)) {
|
|
2603
|
+
for (const line of this.emitAsyncAwaitCall(fn, argPackLines))
|
|
2604
|
+
out.push(line);
|
|
2605
|
+
}
|
|
2606
|
+
if (this.adoptCallees.has(fn.name)) {
|
|
2607
|
+
for (const line of this.emitAsyncAwaitCall(fn, argPackLines, true))
|
|
2608
|
+
out.push(line);
|
|
2609
|
+
}
|
|
2452
2610
|
this.declare(`declare ptr @scr_fiber_promise(ptr)`);
|
|
2453
2611
|
this.declare(`declare ptr @scr_async_spawn(ptr, ptr)`);
|
|
2454
2612
|
this.needOom();
|
|
@@ -2540,6 +2698,13 @@ export class LlEmitter {
|
|
|
2540
2698
|
out.push(line);
|
|
2541
2699
|
return out;
|
|
2542
2700
|
}
|
|
2701
|
+
/** Whether `fn`'s body is emitted as an LLVM switched coroutine: every
|
|
2702
|
+
* wasm32 async or generator body, and native synchronous generators. */
|
|
2703
|
+
coroLowered(fn) {
|
|
2704
|
+
if (this.wasi)
|
|
2705
|
+
return fn.async === true || fn.generator !== undefined;
|
|
2706
|
+
return this.stacklessGenerators && fn.generator !== undefined && fn.async !== true;
|
|
2707
|
+
}
|
|
2543
2708
|
/** Per-generator-function machinery — the async scaffolding's lazy
|
|
2544
2709
|
* sibling (async.ts's emitGenScaffolding): the same argument pack,
|
|
2545
2710
|
* a fiber trampoline whose epilogue stores the COMPLETION value (or
|
|
@@ -2560,14 +2725,15 @@ export class LlEmitter {
|
|
|
2560
2725
|
this.declare(`declare void @scr_gen_ret_to_out(ptr)`);
|
|
2561
2726
|
this.declare(`declare ptr @scr_gen_new(ptr, ptr, ptr)`);
|
|
2562
2727
|
this.needOom();
|
|
2563
|
-
|
|
2728
|
+
const coroBody = this.coroLowered(fn);
|
|
2729
|
+
if (lifted && !coroBody) {
|
|
2564
2730
|
this.declare(`declare void @scr_closure_release(ptr)`);
|
|
2565
2731
|
}
|
|
2566
2732
|
// Normal completion stores the (typed) return value; void completes
|
|
2567
2733
|
// with the NONE slot — JS's undefined done-value. A GENRET unwind
|
|
2568
2734
|
// consumes the sentinel and promotes the parked .return value; a
|
|
2569
2735
|
// real exception stays pending (the consumer-side resume moves it).
|
|
2570
|
-
if (
|
|
2736
|
+
if (coroBody) {
|
|
2571
2737
|
tr.push(` ret void`, `}`, ``);
|
|
2572
2738
|
}
|
|
2573
2739
|
else {
|
|
@@ -2652,8 +2818,11 @@ export class LlEmitter {
|
|
|
2652
2818
|
`entry:`,
|
|
2653
2819
|
...argPackLines,
|
|
2654
2820
|
];
|
|
2821
|
+
const genNew = coroBody && !this.wasi ? "scr_gen_new_stackless" : "scr_gen_new";
|
|
2822
|
+
if (genNew !== "scr_gen_new")
|
|
2823
|
+
this.declare(`declare ptr @${genNew}(ptr, ptr, ptr)`);
|
|
2655
2824
|
sp.push(settleAsync === null
|
|
2656
|
-
? ` %gg = call ptr
|
|
2825
|
+
? ` %gg = call ptr @${genNew}(ptr @${mangleTrampoline(fn.name)}, ptr %ap, ptr @${mangleGenDrop(fn.name)})`
|
|
2657
2826
|
: ` %gg = call ptr @scr_async_gen_new(ptr @${mangleTrampoline(fn.name)}, ptr %ap, ptr @${mangleGenDrop(fn.name)}, ptr @${settleAsync})`, ` ret ptr %gg`, `}`, ``);
|
|
2658
2827
|
for (const line of sp)
|
|
2659
2828
|
out.push(line);
|
|
@@ -2691,6 +2860,41 @@ export class LlEmitter {
|
|
|
2691
2860
|
this.immortalValues.add(name);
|
|
2692
2861
|
return name;
|
|
2693
2862
|
}
|
|
2863
|
+
/** The inline equality test against one string literal (an
|
|
2864
|
+
* `i1 (ptr)` helper LLVM inlines at each site), by literal symbol. */
|
|
2865
|
+
literalEquals(text) {
|
|
2866
|
+
const literal = this.internLiteral(text);
|
|
2867
|
+
const name = `@sc_streq_${literal.slice("@sc_lit_".length)}`;
|
|
2868
|
+
if (!this.literalEqualities.has(text)) {
|
|
2869
|
+
this.literalEqualities.set(text, name);
|
|
2870
|
+
if (Buffer.byteLength(text, "utf8") > INLINE_LITERAL_BYTES) {
|
|
2871
|
+
this.declare(`declare zeroext i1 @scr_str_eq(ptr, ptr)`);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
return name;
|
|
2875
|
+
}
|
|
2876
|
+
/** `@`-ref of an immortal ScrBigInt holding `value`, or null when the
|
|
2877
|
+
* text is not a valid StringToBigInt input (the runtime parse then
|
|
2878
|
+
* throws as usual). BigInt here is the same StringToBigInt as the
|
|
2879
|
+
* program's, so every accepted spelling folds to the value Node gives. */
|
|
2880
|
+
internBigIntLiteral(text) {
|
|
2881
|
+
let value;
|
|
2882
|
+
try {
|
|
2883
|
+
value = BigInt(text);
|
|
2884
|
+
}
|
|
2885
|
+
catch {
|
|
2886
|
+
return null;
|
|
2887
|
+
}
|
|
2888
|
+
const key = value.toString(16);
|
|
2889
|
+
let lit = this.bigintLiterals.get(key);
|
|
2890
|
+
if (!lit) {
|
|
2891
|
+
lit = { sym: this.contentSymbol("sc_bigint_", key), value };
|
|
2892
|
+
this.bigintLiterals.set(key, lit);
|
|
2893
|
+
}
|
|
2894
|
+
const name = `@${lit.sym}`;
|
|
2895
|
+
this.immortalValues.add(name);
|
|
2896
|
+
return name;
|
|
2897
|
+
}
|
|
2694
2898
|
/** Interned NUL-terminated C-string constant (the scr_jb_puts /
|
|
2695
2899
|
* stringify-indent currency) — `@`-ref, first-use order. */
|
|
2696
2900
|
cstr(text) {
|
|
@@ -2729,6 +2933,16 @@ export class LlEmitter {
|
|
|
2729
2933
|
this.immortalValues.add(name);
|
|
2730
2934
|
return name;
|
|
2731
2935
|
}
|
|
2936
|
+
/** The i8 flag that a checked-dynamic cast sets before caching one of
|
|
2937
|
+
* this tagged union's boxes (unionRetype rebuilds once it is set). */
|
|
2938
|
+
retypeGuardRef(unionId) {
|
|
2939
|
+
let sym = this.retypeGuards.get(unionId);
|
|
2940
|
+
if (!sym) {
|
|
2941
|
+
sym = `sc_retype_guard_${this.retypeGuards.size}`;
|
|
2942
|
+
this.retypeGuards.set(unionId, sym);
|
|
2943
|
+
}
|
|
2944
|
+
return `@${sym}`;
|
|
2945
|
+
}
|
|
2732
2946
|
/** The immortal ABSENT state of an undefined-armed record field slot
|
|
2733
2947
|
* (IR fieldAbsent). */
|
|
2734
2948
|
absentInstanceRef(unionId) {
|
|
@@ -2973,19 +3187,21 @@ export class LlEmitter {
|
|
|
2973
3187
|
target.used = true;
|
|
2974
3188
|
terminator = `br label %${target.label}`;
|
|
2975
3189
|
}
|
|
2976
|
-
else if (this.
|
|
2977
|
-
terminator = `br label %${this.
|
|
3190
|
+
else if (this.currentCoro !== null) {
|
|
3191
|
+
terminator = `br label %${this.currentCoro.finalLabel}`;
|
|
2978
3192
|
}
|
|
2979
3193
|
else {
|
|
2980
3194
|
const t = this.llType(this.currentReturnType);
|
|
2981
3195
|
terminator =
|
|
2982
3196
|
t === "void"
|
|
2983
3197
|
? "ret void"
|
|
2984
|
-
:
|
|
2985
|
-
?
|
|
2986
|
-
: t === "
|
|
2987
|
-
?
|
|
2988
|
-
:
|
|
3198
|
+
: this.currentInt32Result
|
|
3199
|
+
? "ret i32 0"
|
|
3200
|
+
: t === "double"
|
|
3201
|
+
? `ret double ${f64Lit(0)}`
|
|
3202
|
+
: t === "i1"
|
|
3203
|
+
? "ret i1 false"
|
|
3204
|
+
: "ret ptr null";
|
|
2989
3205
|
}
|
|
2990
3206
|
// Copy the entries now: later declarations extend these lexical scopes.
|
|
2991
3207
|
// Release order and the handler are part of the shared block's identity.
|
|
@@ -2998,7 +3214,10 @@ export class LlEmitter {
|
|
|
2998
3214
|
}
|
|
2999
3215
|
}
|
|
3000
3216
|
if (entries.length === 0) {
|
|
3001
|
-
if (passThrough !== undefined &&
|
|
3217
|
+
if (passThrough !== undefined &&
|
|
3218
|
+
!target &&
|
|
3219
|
+
this.currentCoro === null &&
|
|
3220
|
+
!this.currentInt32Result) {
|
|
3002
3221
|
const t = this.llType(this.currentReturnType);
|
|
3003
3222
|
if (t === "double" || t === "i1")
|
|
3004
3223
|
terminator = `ret ${t} ${passThrough}`;
|
|
@@ -3324,15 +3543,6 @@ export class LlEmitter {
|
|
|
3324
3543
|
this.declare(`declare void @scr_dyn_throw(ptr)`);
|
|
3325
3544
|
B.line(`call void @scr_dyn_throw(ptr ${v.name})`);
|
|
3326
3545
|
}
|
|
3327
|
-
else if (t.kind === "jsval") {
|
|
3328
|
-
const rc = vAdapters(this.shapeHost, t);
|
|
3329
|
-
const test = "scr_jsval_is_object";
|
|
3330
|
-
this.declare(`declare zeroext i1 @${test}(ptr)`);
|
|
3331
|
-
this.declare(`declare void @scr_throw_ref_classified(ptr, ptr, ptr, ptr, i1 zeroext)`);
|
|
3332
|
-
const object = B.tmp();
|
|
3333
|
-
B.line(`${object} = call zeroext i1 @${test}(ptr ${v.name})`);
|
|
3334
|
-
B.line(`call void @scr_throw_ref_classified(ptr ${v.name}, ptr ${rc.retain}, ptr ${rc.release}, ptr ${traceArg(this.shapeHost, t)}, i1 zeroext ${object})`);
|
|
3335
|
-
}
|
|
3336
3546
|
else {
|
|
3337
3547
|
const rc = vAdapters(this.shapeHost, t);
|
|
3338
3548
|
this.declare(`declare void @scr_throw_ref(ptr, ptr, ptr, ptr)`);
|
|
@@ -3370,7 +3580,7 @@ export class LlEmitter {
|
|
|
3370
3580
|
else if (arm.kind === "string") {
|
|
3371
3581
|
valueName = this.unionPeek(v.name, unionId);
|
|
3372
3582
|
}
|
|
3373
|
-
else if (arm.kind === "bigint" || arm.kind === "dyn"
|
|
3583
|
+
else if (arm.kind === "bigint" || arm.kind === "dyn") {
|
|
3374
3584
|
valueName = this.unionPeek(v.name, unionId);
|
|
3375
3585
|
}
|
|
3376
3586
|
const truthy = this.truthyOf(arm.kind, valueName, true);
|
|
@@ -3464,14 +3674,6 @@ export class LlEmitter {
|
|
|
3464
3674
|
B.line(`${truthy} = call zeroext i1 @scr_dyn_truthy(ptr ${valueName})`);
|
|
3465
3675
|
return truthy;
|
|
3466
3676
|
}
|
|
3467
|
-
case "jsval": {
|
|
3468
|
-
this.declare(`declare i32 @scr_jsval_truthy(ptr)`);
|
|
3469
|
-
const raw = B.tmp();
|
|
3470
|
-
const truthy = B.tmp();
|
|
3471
|
-
B.line(`${raw} = call i32 @scr_jsval_truthy(ptr ${valueName})`);
|
|
3472
|
-
B.line(`${truthy} = icmp ne i32 ${raw}, 0`);
|
|
3473
|
-
return truthy;
|
|
3474
|
-
}
|
|
3475
3677
|
default:
|
|
3476
3678
|
throw new LlvmUnsupportedError(`truthy:${unionArm ? "union:" : ""}${kind}`);
|
|
3477
3679
|
}
|
|
@@ -3988,6 +4190,29 @@ export class LlEmitter {
|
|
|
3988
4190
|
this.boxSet(box, local.type, borrowed && isRefCounted(local.type) ? this.retainValue(value, local.type) : value);
|
|
3989
4191
|
}
|
|
3990
4192
|
// ── functions ───────────────────────────────────────────────────────────
|
|
4193
|
+
/** True when `fnName`'s borrowing body lends every reference parameter
|
|
4194
|
+
* and returns an owned result: a closure over it then carries that body
|
|
4195
|
+
* as its borrowing entry (ScrClosure.fn_borrowed), which callers that
|
|
4196
|
+
* keep their arguments alive across the call may enter instead of `fn`. */
|
|
4197
|
+
lendsEveryArgument(fnName) {
|
|
4198
|
+
const fn = this.fnByName.get(fnName);
|
|
4199
|
+
if (!fn || fn.async || fn.generator || this.borrowedReturns.has(fnName))
|
|
4200
|
+
return false;
|
|
4201
|
+
if (fn.classCaptures !== undefined || this.implSlotBorrowed(fnName).size > 0)
|
|
4202
|
+
return false;
|
|
4203
|
+
const borrowed = this.callLifetimes.borrowed.get(fnName);
|
|
4204
|
+
if (!borrowed)
|
|
4205
|
+
return false;
|
|
4206
|
+
let any = false;
|
|
4207
|
+
for (const [index, param] of fn.params.entries()) {
|
|
4208
|
+
if (!isRefCounted(param.type))
|
|
4209
|
+
continue;
|
|
4210
|
+
if (!borrowed.has(index))
|
|
4211
|
+
return false;
|
|
4212
|
+
any = true;
|
|
4213
|
+
}
|
|
4214
|
+
return any;
|
|
4215
|
+
}
|
|
3991
4216
|
/** The LLVM symbol a direct call or closure enters a function through:
|
|
3992
4217
|
* async bodies are entered via their emitted spawn wrapper (which runs
|
|
3993
4218
|
* the fiber eagerly to its first suspension and returns the promise);
|
|
@@ -4005,7 +4230,7 @@ export class LlEmitter {
|
|
|
4005
4230
|
* whether the promise waiter list or the ready FIFO owns the continuation;
|
|
4006
4231
|
* LLVM keeps all live locals in the coroutine frame. */
|
|
4007
4232
|
emitWasiSuspend(promise) {
|
|
4008
|
-
const coro = this.
|
|
4233
|
+
const coro = this.currentCoro;
|
|
4009
4234
|
if (coro === null)
|
|
4010
4235
|
throw new InternalCompilerError("llvm emitter bug: wasm suspension outside async body");
|
|
4011
4236
|
if (promise === null) {
|
|
@@ -4016,12 +4241,12 @@ export class LlEmitter {
|
|
|
4016
4241
|
this.declare(`declare void @scr_wasi_await_prepare(ptr, ptr)`);
|
|
4017
4242
|
this.B.line(`call void @scr_wasi_await_prepare(ptr ${coro.self}, ptr ${promise})`);
|
|
4018
4243
|
}
|
|
4019
|
-
this.
|
|
4244
|
+
this.emitCoroSuspendPrepared();
|
|
4020
4245
|
}
|
|
4021
4246
|
/** Suspend after a target-specific runtime helper already queued the
|
|
4022
4247
|
* continuation (used by module awaits, which skip the hop when settled). */
|
|
4023
|
-
|
|
4024
|
-
const coro = this.
|
|
4248
|
+
emitCoroSuspendPrepared() {
|
|
4249
|
+
const coro = this.currentCoro;
|
|
4025
4250
|
if (coro === null)
|
|
4026
4251
|
throw new InternalCompilerError("llvm emitter bug: wasm suspension outside async body");
|
|
4027
4252
|
const save = this.B.tmp();
|
|
@@ -4034,7 +4259,7 @@ export class LlEmitter {
|
|
|
4034
4259
|
}
|
|
4035
4260
|
/** Move a clean async return value into the current fiber's promise. */
|
|
4036
4261
|
emitWasiFulfill(v) {
|
|
4037
|
-
const coro = this.
|
|
4262
|
+
const coro = this.currentCoro;
|
|
4038
4263
|
if (coro === null)
|
|
4039
4264
|
throw new InternalCompilerError("llvm emitter bug: wasm fulfillment outside async body");
|
|
4040
4265
|
const ret = this.currentReturnType;
|
|
@@ -4148,6 +4373,8 @@ export class LlEmitter {
|
|
|
4148
4373
|
this.borrowedParameters.add(fn.params[index].localId);
|
|
4149
4374
|
}
|
|
4150
4375
|
this.captureIds = new Set([...(fn.captures ?? []), ...(fn.classCaptures ?? [])].map((c) => c.localId));
|
|
4376
|
+
this.stableCaptureIds =
|
|
4377
|
+
fn.captures !== undefined ? this.lazyCaptures.stableCaptures(fn.name) : new Set();
|
|
4151
4378
|
this.integerLoopBindings.clear();
|
|
4152
4379
|
this.countedLoopsEnabled = this.debug === null && !fn.async && !fn.generator;
|
|
4153
4380
|
this.finalFieldBorrows =
|
|
@@ -4175,17 +4402,28 @@ export class LlEmitter {
|
|
|
4175
4402
|
this.walkBorrows = this.walkBorrowsByFunction.get(fn.name) ?? new Set();
|
|
4176
4403
|
this.integerRanges = analyzeIntegerRanges(numericFn, this.int32Slots.facts(fn.name));
|
|
4177
4404
|
this.bytesBounds = findBytesBounds(numericFn, this.integerRanges);
|
|
4405
|
+
// Debug builds keep every number local a double for the debugger's
|
|
4406
|
+
// variable view. Lazily boxed captured parameters keep their double,
|
|
4407
|
+
// which the box is created from.
|
|
4408
|
+
const int32Locals = this.debug === null
|
|
4409
|
+
? findInt32Locals(fn, this.integerRanges, this.int32Slots.facts(fn.name), new Set(this.lazyCaptureBoxes.keys()))
|
|
4410
|
+
: null;
|
|
4411
|
+
this.int32Locals = int32Locals?.locals ?? new Map();
|
|
4412
|
+
this.int32LocalWrites = int32Locals?.values ?? new Set();
|
|
4178
4413
|
this.chainSlots.clear();
|
|
4179
4414
|
this.finallyStack = [];
|
|
4180
4415
|
this.tryStack = [];
|
|
4181
4416
|
this.loopDepth = 0;
|
|
4182
4417
|
this.currentReturnType = fn.returnType;
|
|
4418
|
+
const int32Abi = this.int32Abis.get(fn.name);
|
|
4419
|
+
this.currentInt32Result = int32Abi?.result === true;
|
|
4183
4420
|
this.currentGenerator = fn.generator ?? null;
|
|
4184
4421
|
this.currentBorrowedReturn = this.borrowedReturns.has(fn.name);
|
|
4185
4422
|
this.inConstructor = this.constructorFunctions.has(fn.name);
|
|
4186
|
-
this.
|
|
4423
|
+
this.currentCoro = null;
|
|
4187
4424
|
this.logArgSlots = 0;
|
|
4188
|
-
if (this.
|
|
4425
|
+
if (this.coroLowered(fn)) {
|
|
4426
|
+
this.usesCoroutines = true;
|
|
4189
4427
|
this.declare(`declare token @llvm.coro.id(i32, ptr, ptr, ptr)`);
|
|
4190
4428
|
this.declare(`declare ${this.sizeType} @llvm.coro.size.${this.sizeType}()`);
|
|
4191
4429
|
this.declare(`declare ptr @llvm.coro.begin(token, ptr)`);
|
|
@@ -4193,9 +4431,12 @@ export class LlEmitter {
|
|
|
4193
4431
|
this.declare(`declare i8 @llvm.coro.suspend(token, i1)`);
|
|
4194
4432
|
this.declare(`declare ptr @llvm.coro.free(token, ptr)`);
|
|
4195
4433
|
this.declare(`declare void @llvm.coro.end(ptr, i1, token)`);
|
|
4196
|
-
|
|
4197
|
-
this.
|
|
4198
|
-
this.declare(`declare
|
|
4434
|
+
// Native frames come from the runtime's small-block allocator.
|
|
4435
|
+
const frameAlloc = this.wasi ? "malloc" : "scr_rt_calloc";
|
|
4436
|
+
this.declare(`declare ptr @${frameAlloc}(${this.sizeType})`);
|
|
4437
|
+
this.declare(`declare void @${this.wasi ? "free" : "scr_rt_free"}(ptr)`);
|
|
4438
|
+
const started = this.wasi ? "scr_wasi_coro_started" : "scr_coro_started";
|
|
4439
|
+
this.declare(`declare void @${started}(ptr)`);
|
|
4199
4440
|
this.declare(`declare ptr @scr_fiber_self()`);
|
|
4200
4441
|
const id = B.tmp();
|
|
4201
4442
|
const size = B.tmp();
|
|
@@ -4204,11 +4445,11 @@ export class LlEmitter {
|
|
|
4204
4445
|
const self = B.tmp();
|
|
4205
4446
|
B.line(`${id} = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)`);
|
|
4206
4447
|
B.line(`${size} = call ${this.sizeType} @llvm.coro.size.${this.sizeType}()`);
|
|
4207
|
-
B.line(`${mem} = call ptr
|
|
4448
|
+
B.line(`${mem} = call ptr @${frameAlloc}(${this.sizeType} ${size})`);
|
|
4208
4449
|
B.line(`${handle} = call ptr @llvm.coro.begin(token ${id}, ptr ${mem})`);
|
|
4209
|
-
B.line(`call void
|
|
4450
|
+
B.line(`call void @${started}(ptr ${handle})`);
|
|
4210
4451
|
B.line(`${self} = call ptr @scr_fiber_self()`);
|
|
4211
|
-
this.
|
|
4452
|
+
this.currentCoro = {
|
|
4212
4453
|
kind: fn.generator !== undefined ? "generator" : "async",
|
|
4213
4454
|
id,
|
|
4214
4455
|
handle,
|
|
@@ -4221,6 +4462,9 @@ export class LlEmitter {
|
|
|
4221
4462
|
const paramIds = new Set(fn.params.map((p) => p.localId));
|
|
4222
4463
|
if (this.debug === null) {
|
|
4223
4464
|
for (const id of findIntegerViews(numericFn)) {
|
|
4465
|
+
// An i32 slot is its own view.
|
|
4466
|
+
if (this.int32Locals.has(id))
|
|
4467
|
+
continue;
|
|
4224
4468
|
const slot = B.slot();
|
|
4225
4469
|
B.entryAllocas.push(`${slot} = alloca i32 ; integer view ${this.numericLocals.get(id).name}`);
|
|
4226
4470
|
this.integerViews.set(id, slot);
|
|
@@ -4240,7 +4484,9 @@ export class LlEmitter {
|
|
|
4240
4484
|
// box the catch prologue takes (scr_exc_take).
|
|
4241
4485
|
const slotTy = local.boxed || this.captureIds.has(local.id) || local.type.kind === "caught"
|
|
4242
4486
|
? "ptr"
|
|
4243
|
-
: this.
|
|
4487
|
+
: this.int32Locals.has(local.id)
|
|
4488
|
+
? "i32"
|
|
4489
|
+
: this.llType(local.type);
|
|
4244
4490
|
B.entryAllocas.push(`%${mangleLocal(local.id)} = alloca ${slotTy} ; ${local.name}`);
|
|
4245
4491
|
this.emitDebugLocal(local, `%${mangleLocal(local.id)}`, fn.params.findIndex((p) => p.localId === local.id) + 1, this.captureIds.has(local.id));
|
|
4246
4492
|
// Refcounted/boxed locals start NULL (the C prologue's `= NULL`):
|
|
@@ -4314,8 +4560,16 @@ export class LlEmitter {
|
|
|
4314
4560
|
fnScope.push({ slot, type: p.type, boxed: true });
|
|
4315
4561
|
continue;
|
|
4316
4562
|
}
|
|
4563
|
+
const incoming = { name: `%p_${mangleLocal(p.localId)}`, type: p.type };
|
|
4564
|
+
if (int32Abi?.params[index]) {
|
|
4565
|
+
const bits = `%p_${mangleLocal(p.localId)}${INT32_ABI_SUFFIX}`;
|
|
4566
|
+
incoming.uint32 = bits;
|
|
4567
|
+
incoming.integer = { name: bits, type: "i32", signed: true, range: INT32_RANGE };
|
|
4568
|
+
}
|
|
4569
|
+
if (this.storeInt32Local(p.localId, incoming))
|
|
4570
|
+
continue;
|
|
4317
4571
|
B.line(`store ${this.llType(p.type)} %p_${mangleLocal(p.localId)}, ptr ${slot}`);
|
|
4318
|
-
this.storeIntegerView(p.localId,
|
|
4572
|
+
this.storeIntegerView(p.localId, incoming);
|
|
4319
4573
|
if (isRefCounted(p.type) && !borrowed?.has(index))
|
|
4320
4574
|
fnScope.push({ slot, type: p.type });
|
|
4321
4575
|
if (this.reboundByFunction.get(fn.name)?.has(p.localId) && borrowed?.has(index)) {
|
|
@@ -4329,8 +4583,9 @@ export class LlEmitter {
|
|
|
4329
4583
|
}
|
|
4330
4584
|
}
|
|
4331
4585
|
this.scopes.push(fnScope);
|
|
4332
|
-
// Stackful native fibers
|
|
4333
|
-
// active exception context owns the chain, so
|
|
4586
|
+
// Stackful native fibers and native coroutine frames retain these frames
|
|
4587
|
+
// while suspended. The active exception context owns the chain, so
|
|
4588
|
+
// concurrent fibers isolate it.
|
|
4334
4589
|
if (this.stackTraces && !this.wasi && fn.sourceName !== undefined) {
|
|
4335
4590
|
this.declare(`declare void @scr_stack_enter(ptr, ptr)`);
|
|
4336
4591
|
this.declare(`declare void @scr_stack_leave(ptr)`);
|
|
@@ -4349,25 +4604,32 @@ export class LlEmitter {
|
|
|
4349
4604
|
// whose unwind released everything down to depth 0).
|
|
4350
4605
|
if (fn.returnType.kind === "void" && !B.isTerminated()) {
|
|
4351
4606
|
this.releaseScope(this.scopes[0]);
|
|
4352
|
-
if (this.
|
|
4607
|
+
if (this.currentCoro !== null) {
|
|
4353
4608
|
this.emitWasiFulfill(null);
|
|
4354
|
-
B.terminate(`br label %${this.
|
|
4609
|
+
B.terminate(`br label %${this.currentCoro.finalLabel}`);
|
|
4355
4610
|
}
|
|
4356
4611
|
else {
|
|
4357
4612
|
B.terminate("ret void");
|
|
4358
4613
|
}
|
|
4359
4614
|
}
|
|
4360
4615
|
this.scopes.pop();
|
|
4361
|
-
const coro = this.
|
|
4616
|
+
const coro = this.currentCoro;
|
|
4362
4617
|
if (coro !== null) {
|
|
4363
4618
|
B.startBlock(coro.finalLabel);
|
|
4619
|
+
// Every exit of a coroutine body joins here: its source frame (part
|
|
4620
|
+
// of the coroutine frame) leaves the generator's own chain.
|
|
4621
|
+
const epilogue = B.returnEpilogue;
|
|
4622
|
+
B.returnEpilogue = null;
|
|
4623
|
+
if (epilogue !== null)
|
|
4624
|
+
B.line(epilogue);
|
|
4364
4625
|
if (fn.captures !== undefined) {
|
|
4365
4626
|
this.declare(`declare void @scr_closure_release(ptr)`);
|
|
4366
4627
|
B.line(`call void @scr_closure_release(ptr %sc_env)`);
|
|
4367
4628
|
}
|
|
4368
4629
|
if (coro.kind === "generator") {
|
|
4369
|
-
this.
|
|
4370
|
-
|
|
4630
|
+
const finish = this.wasi ? "scr_wasi_gen_finish" : "scr_gen_coro_finish";
|
|
4631
|
+
this.declare(`declare void @${finish}(ptr)`);
|
|
4632
|
+
B.line(`call void @${finish}(ptr ${coro.self})`);
|
|
4371
4633
|
}
|
|
4372
4634
|
else {
|
|
4373
4635
|
this.declare(`declare void @scr_wasi_async_finish(ptr)`);
|
|
@@ -4379,7 +4641,7 @@ export class LlEmitter {
|
|
|
4379
4641
|
B.startBlock(coro.cleanupLabel);
|
|
4380
4642
|
const frame = B.tmp();
|
|
4381
4643
|
B.line(`${frame} = call ptr @llvm.coro.free(token ${coro.id}, ptr ${coro.handle})`);
|
|
4382
|
-
B.line(`call void
|
|
4644
|
+
B.line(`call void @${this.wasi ? "free" : "scr_rt_free"}(ptr ${frame})`);
|
|
4383
4645
|
B.br(coro.suspendLabel);
|
|
4384
4646
|
B.startBlock(coro.suspendLabel);
|
|
4385
4647
|
B.line(`call void @llvm.coro.end(ptr ${coro.handle}, i1 false, token none)`);
|
|
@@ -4404,11 +4666,22 @@ export class LlEmitter {
|
|
|
4404
4666
|
if (this.logArgSlots > 0) {
|
|
4405
4667
|
B.entryAllocas.push(`%logargs = alloca [${this.logArgSlots} x %ScrLogArg]`);
|
|
4406
4668
|
}
|
|
4407
|
-
const params = fn.params.map((p) =>
|
|
4669
|
+
const params = fn.params.map((p, index) => int32Abi?.params[index]
|
|
4670
|
+
? `i32 %p_${mangleLocal(p.localId)}${INT32_ABI_SUFFIX}`
|
|
4671
|
+
: `${this.llType(p.type)} %p_${mangleLocal(p.localId)}`);
|
|
4672
|
+
// The i32 entry widens its i32 parameters once; the proof makes the
|
|
4673
|
+
// double exact, so readers of the incoming number see the same value.
|
|
4674
|
+
if (int32Abi)
|
|
4675
|
+
fn.params.forEach((p, index) => {
|
|
4676
|
+
if (!int32Abi.params[index])
|
|
4677
|
+
return;
|
|
4678
|
+
const name = `%p_${mangleLocal(p.localId)}`;
|
|
4679
|
+
B.entryAllocas.push(`${name} = sitofp i32 ${name}${INT32_ABI_SUFFIX} to double`);
|
|
4680
|
+
});
|
|
4408
4681
|
// Lifted functions receive their closure first (the callValue ABI).
|
|
4409
4682
|
if (fn.captures !== undefined)
|
|
4410
4683
|
params.unshift("ptr %sc_env");
|
|
4411
|
-
const ret = this.llType(fn.returnType);
|
|
4684
|
+
const ret = int32Abi?.result ? "i32" : this.llType(fn.returnType);
|
|
4412
4685
|
let attrs = coro !== null ? "#1" : FN_ATTRS;
|
|
4413
4686
|
// The shared indexed-read helper (lowerSafeIndexRead) is a dense bounds
|
|
4414
4687
|
// test, a state test and a load behind cold runtime fallbacks, called
|
|
@@ -4419,7 +4692,9 @@ export class LlEmitter {
|
|
|
4419
4692
|
attrs = `inlinehint ${attrs}`;
|
|
4420
4693
|
const debug = this.debugScope === null ? "" : ` !dbg ${this.debugScope}`;
|
|
4421
4694
|
const symbol = borrowed ? mangleBorrowedFunction(fn.name) : mangleFunction(fn.name);
|
|
4422
|
-
|
|
4695
|
+
let body = `define internal ${ret} @${symbol}${int32Abi ? INT32_ABI_SUFFIX : ""}(${params.join(", ")}) ${attrs}${debug} { ; ${fn.name}\n${B.render()}\n}`;
|
|
4696
|
+
if (int32Abi)
|
|
4697
|
+
body += "\n" + this.emitInt32AbiWrapper(fn, symbol, int32Abi);
|
|
4423
4698
|
if (!borrowed)
|
|
4424
4699
|
return body;
|
|
4425
4700
|
const slot = this.implSlotBorrowed(fn.name);
|
|
@@ -4428,9 +4703,62 @@ export class LlEmitter {
|
|
|
4428
4703
|
: "";
|
|
4429
4704
|
return body + "\n" + this.emitOwnedCallAdapter(fn, borrowed) + virtual;
|
|
4430
4705
|
}
|
|
4706
|
+
/** The double-ABI entry of a function with an i32 entry: every caller
|
|
4707
|
+
* that is not a direct call (closures, dispatch, adapters, the runtime)
|
|
4708
|
+
* enters here. A pending exception passes through to its caller. */
|
|
4709
|
+
emitInt32AbiWrapper(fn, symbol, abi) {
|
|
4710
|
+
const lines = [];
|
|
4711
|
+
const params = [];
|
|
4712
|
+
const args = [];
|
|
4713
|
+
fn.params.forEach((param, index) => {
|
|
4714
|
+
const type = this.llType(param.type);
|
|
4715
|
+
params.push(`${type} %p${index}`);
|
|
4716
|
+
if (abi.params[index]) {
|
|
4717
|
+
lines.push(` %a${index} = fptosi double %p${index} to i32`);
|
|
4718
|
+
args.push(`i32 %a${index}`);
|
|
4719
|
+
}
|
|
4720
|
+
else
|
|
4721
|
+
args.push(`${type} %p${index}`);
|
|
4722
|
+
});
|
|
4723
|
+
const ret = this.llType(fn.returnType);
|
|
4724
|
+
const call = `call ${abi.result ? "i32" : ret} @${symbol}${INT32_ABI_SUFFIX}(${args.join(", ")})`;
|
|
4725
|
+
if (ret === "void")
|
|
4726
|
+
lines.push(` ${call}`, " ret void");
|
|
4727
|
+
else if (abi.result)
|
|
4728
|
+
lines.push(` %r = ${call}`, ` %d = sitofp i32 %r to double`, ` ret double %d`);
|
|
4729
|
+
else
|
|
4730
|
+
lines.push(` %r = ${call}`, ` ret ${ret} %r`);
|
|
4731
|
+
return `define internal ${ret} @${symbol}(${params.join(", ")}) ${FN_ATTRS} {\nentry:\n${lines.join("\n")}\n}`;
|
|
4732
|
+
}
|
|
4733
|
+
/** The i32 bits of a number proven an int32 (a direct call's i32
|
|
4734
|
+
* argument or an i32 result): its exact integer when the value carries
|
|
4735
|
+
* one, else the conversion the proof makes exact. */
|
|
4736
|
+
int32Argument(value, expr) {
|
|
4737
|
+
const integer = exactInteger(this, value, expr);
|
|
4738
|
+
if (integer && /^-?\d+$/.test(integer.name))
|
|
4739
|
+
return String(Number(integer.name) | 0);
|
|
4740
|
+
if (integer)
|
|
4741
|
+
return narrowInteger(this, integer);
|
|
4742
|
+
const bits = this.B.tmp();
|
|
4743
|
+
this.B.line(`${bits} = fptosi double ${value.name} to i32`);
|
|
4744
|
+
return bits;
|
|
4745
|
+
}
|
|
4746
|
+
/** A direct call's i32 result as a number, with the i32 as its integer
|
|
4747
|
+
* and ToUint32 forms. */
|
|
4748
|
+
int32Result(bits, type) {
|
|
4749
|
+
const number = this.B.tmp();
|
|
4750
|
+
this.B.line(`${number} = sitofp i32 ${bits} to double`);
|
|
4751
|
+
return {
|
|
4752
|
+
name: number,
|
|
4753
|
+
type,
|
|
4754
|
+
uint32: bits,
|
|
4755
|
+
integer: { name: bits, type: "i32", signed: true, range: INT32_RANGE },
|
|
4756
|
+
};
|
|
4757
|
+
}
|
|
4431
4758
|
/** Keep the ordinary owned ABI for closures, virtual dispatch, generated
|
|
4432
|
-
* runtime adapters and library exports. Only direct IR calls
|
|
4433
|
-
*
|
|
4759
|
+
* runtime adapters and library exports. Only direct IR calls, calls of a
|
|
4760
|
+
* known closure body and borrowing closure entries (fn_borrowed) select
|
|
4761
|
+
* the borrowing body. Ownership of other parameters moves into that body;
|
|
4434
4762
|
* borrowed parameters are released here on normal and exceptional exits.
|
|
4435
4763
|
* The pending exception remains for the adapter's caller to handle. */
|
|
4436
4764
|
/** With `kept`, the virtual adapter of a borrowing slot instead: the
|
|
@@ -4440,6 +4768,9 @@ export class LlEmitter {
|
|
|
4440
4768
|
const B = new BlockBuilder();
|
|
4441
4769
|
this.B = B;
|
|
4442
4770
|
const params = fn.params.map((param, index) => `${this.llType(param.type)} %p${index}`);
|
|
4771
|
+
// Lifted bodies receive their closure first (the callValue ABI).
|
|
4772
|
+
if (fn.captures !== undefined)
|
|
4773
|
+
params.unshift("ptr %sc_env");
|
|
4443
4774
|
const ret = this.llType(fn.returnType);
|
|
4444
4775
|
const call = `call ${ret} @${mangleBorrowedFunction(fn.name)}(${params.join(", ")})`;
|
|
4445
4776
|
B.line(ret === "void" ? call : `%result = ${call}`);
|
|
@@ -4527,6 +4858,9 @@ export class LlEmitter {
|
|
|
4527
4858
|
// A module constant owns its value for the rest of the program.
|
|
4528
4859
|
if (binding.kind === "global")
|
|
4529
4860
|
return this.stableGlobals.has(value.localId);
|
|
4861
|
+
// So does the environment for a capture nothing ever rebinds.
|
|
4862
|
+
if (binding.kind === "boxed")
|
|
4863
|
+
return this.stableCaptureIds.has(value.localId) && this.canBorrowReceiver(value);
|
|
4530
4864
|
return (binding.kind === "local" &&
|
|
4531
4865
|
binding.local !== undefined &&
|
|
4532
4866
|
(!binding.local.mutable ||
|
|
@@ -4604,12 +4938,74 @@ export class LlEmitter {
|
|
|
4604
4938
|
return false;
|
|
4605
4939
|
return !rw.conflicts(["a", ...keys], writes);
|
|
4606
4940
|
}
|
|
4941
|
+
/** `return p` of a promise inside try regions of an async function. JS
|
|
4942
|
+
* completes the return first: p is evaluated, every crossed finally runs,
|
|
4943
|
+
* and only then does the function's promise adopt p's settlement (a then
|
|
4944
|
+
* job, then p's reaction). So the promise parks in a slot through the
|
|
4945
|
+
* finally copies, like any pending return value, and the adopting await
|
|
4946
|
+
* runs afterwards with no handler in scope: a rejection settles the
|
|
4947
|
+
* function's promise instead of reaching its own catch clauses. */
|
|
4948
|
+
emitAdoptingReturn(s, adopted) {
|
|
4949
|
+
const B = this.B;
|
|
4950
|
+
const promise = this.emitExpr(adopted.value);
|
|
4951
|
+
this.moveTemp(promise);
|
|
4952
|
+
const slot = B.slot();
|
|
4953
|
+
B.entryAllocas.push(`${slot} = alloca ptr ; adopted return promise`);
|
|
4954
|
+
B.line(`store ptr ${promise.name}, ptr ${slot}`);
|
|
4955
|
+
const parked = { slot, type: adopted.value.type };
|
|
4956
|
+
const savedFrames = this.frames;
|
|
4957
|
+
const savedScopes = this.scopes;
|
|
4958
|
+
const savedFinally = this.finallyStack;
|
|
4959
|
+
const savedTry = this.tryStack;
|
|
4960
|
+
for (let i = savedFinally.length - 1; i >= 0 && !B.isTerminated(); i--) {
|
|
4961
|
+
const fin = savedFinally[i];
|
|
4962
|
+
this.releaseForJump(fin.frameDepth, fin.scopeDepth);
|
|
4963
|
+
this.frames = this.frames.slice(0, fin.frameDepth);
|
|
4964
|
+
this.scopes = this.scopes.slice(0, fin.scopeDepth);
|
|
4965
|
+
this.finallyStack = savedFinally.slice(0, i);
|
|
4966
|
+
this.tryStack = savedTry.slice(0, fin.tryDepth);
|
|
4967
|
+
this.scopes.push([parked]);
|
|
4968
|
+
this.emitBlock(fin.body);
|
|
4969
|
+
this.scopes.pop();
|
|
4970
|
+
}
|
|
4971
|
+
if (!B.isTerminated()) {
|
|
4972
|
+
this.releaseForJump(0, 0);
|
|
4973
|
+
// Nothing of the body is live any more except the parked promise,
|
|
4974
|
+
// whose scope releases it on either exit of the await.
|
|
4975
|
+
this.frames = [[]];
|
|
4976
|
+
this.scopes = [[parked]];
|
|
4977
|
+
this.finallyStack = [];
|
|
4978
|
+
this.tryStack = [];
|
|
4979
|
+
const loaded = B.tmp();
|
|
4980
|
+
B.line(`${loaded} = load ptr, ptr ${slot}`);
|
|
4981
|
+
this.adoptedOperand = { name: loaded, type: adopted.value.type };
|
|
4982
|
+
const v = this.emitExpr(s.value);
|
|
4983
|
+
if (this.adoptedOperand !== null)
|
|
4984
|
+
throw new InternalCompilerError("llvm emitter bug: adopted return promise was not awaited");
|
|
4985
|
+
this.moveTemp(v);
|
|
4986
|
+
this.releaseForJump(0, 0);
|
|
4987
|
+
if (!B.isTerminated()) {
|
|
4988
|
+
if (this.currentCoro !== null) {
|
|
4989
|
+
this.emitWasiFulfill(v);
|
|
4990
|
+
B.terminate(`br label %${this.currentCoro.finalLabel}`);
|
|
4991
|
+
}
|
|
4992
|
+
else if (s.value.type.kind === "void")
|
|
4993
|
+
B.terminate("ret void");
|
|
4994
|
+
else
|
|
4995
|
+
B.terminate(`ret ${this.llType(s.value.type)} ${v.name}`);
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4998
|
+
this.frames = savedFrames;
|
|
4999
|
+
this.scopes = savedScopes;
|
|
5000
|
+
this.finallyStack = savedFinally;
|
|
5001
|
+
this.tryStack = savedTry;
|
|
5002
|
+
}
|
|
4607
5003
|
/** `return x` of a local whose slot is an owned scope entry of the same
|
|
4608
5004
|
* type: the return can take over the local's reference. */
|
|
4609
5005
|
movableReturn(value) {
|
|
4610
5006
|
if (value === null || value.kind !== "varRef" || !isRefCounted(value.type))
|
|
4611
5007
|
return null;
|
|
4612
|
-
if (this.currentBorrowedReturn || this.
|
|
5008
|
+
if (this.currentBorrowedReturn || this.currentCoro !== null)
|
|
4613
5009
|
return null;
|
|
4614
5010
|
const binding = this.binding(value.localId);
|
|
4615
5011
|
if (binding.kind !== "local" || binding.local === undefined || binding.local.boxed)
|
|
@@ -4779,6 +5175,10 @@ export class LlEmitter {
|
|
|
4779
5175
|
this.frames.push([]);
|
|
4780
5176
|
switch (s.kind) {
|
|
4781
5177
|
case "varDecl": {
|
|
5178
|
+
if (s.init && this.int32Locals.has(s.localId)) {
|
|
5179
|
+
this.storeInt32Local(s.localId, this.emitExpr(s.init), s.init);
|
|
5180
|
+
break;
|
|
5181
|
+
}
|
|
4782
5182
|
const b = this.binding(s.localId);
|
|
4783
5183
|
if (this.walkBorrows.has(s.localId) && b.kind === "local") {
|
|
4784
5184
|
// A borrowed walk pointer: no retain now, no release at scope exit.
|
|
@@ -4926,6 +5326,10 @@ export class LlEmitter {
|
|
|
4926
5326
|
break;
|
|
4927
5327
|
}
|
|
4928
5328
|
case "assign": {
|
|
5329
|
+
if (this.int32Locals.has(s.localId)) {
|
|
5330
|
+
this.storeInt32Local(s.localId, this.emitExpr(s.value), s.value);
|
|
5331
|
+
break;
|
|
5332
|
+
}
|
|
4929
5333
|
const owner = this.reboundOwnerSlots.get(s.localId);
|
|
4930
5334
|
if (owner !== undefined && !this.walkBorrows.has(s.localId)) {
|
|
4931
5335
|
// A rebound borrowed parameter: the new value moves into the owner
|
|
@@ -5014,8 +5418,10 @@ export class LlEmitter {
|
|
|
5014
5418
|
case "arraySet": {
|
|
5015
5419
|
// Evaluation order matches JS: array, index, then value. Ownership
|
|
5016
5420
|
// of a refcounted value moves into the array (the runtime releases
|
|
5017
|
-
// the replaced element itself).
|
|
5018
|
-
|
|
5421
|
+
// the replaced element itself). A local receiver that the index and
|
|
5422
|
+
// value cannot rebind is read in place, as for field stores; the
|
|
5423
|
+
// released element cannot reach that binding.
|
|
5424
|
+
const arr = this.emitStableReceiver(s.arr, [s.index, s.value]);
|
|
5019
5425
|
const idx = this.emitExpr(s.index);
|
|
5020
5426
|
const v = this.emitExpr(s.value);
|
|
5021
5427
|
if (s.arr.type.kind !== "array")
|
|
@@ -5193,7 +5599,6 @@ export class LlEmitter {
|
|
|
5193
5599
|
break;
|
|
5194
5600
|
}
|
|
5195
5601
|
const join = B.newLabel("rks.j");
|
|
5196
|
-
this.declare(`declare zeroext i1 @scr_str_eq(ptr, ptr)`);
|
|
5197
5602
|
if (iv.kind === "dyn" && shape.indexValue) {
|
|
5198
5603
|
// A dyn-valued shape (the C recordKeySetHelper's dyn arm,
|
|
5199
5604
|
// inline): declared keys VALIDATE the dyn value against the
|
|
@@ -5203,9 +5608,8 @@ export class LlEmitter {
|
|
|
5203
5608
|
// insert the dyn value into the overflow map as-is.
|
|
5204
5609
|
this.declare(`declare void @scr_dyn_release(ptr)`);
|
|
5205
5610
|
for (const f of shape.fields) {
|
|
5206
|
-
const lit = this.internLiteral(f.name);
|
|
5207
5611
|
const hit = B.tmp();
|
|
5208
|
-
B.line(`${hit} = call zeroext i1
|
|
5612
|
+
B.line(`${hit} = call zeroext i1 ${this.literalEquals(f.name)}(ptr ${key.name}) ; ${llvmCommentText(f.name)}`);
|
|
5209
5613
|
const lh = B.newLabel("rks.h");
|
|
5210
5614
|
const ln = B.newLabel("rks.n");
|
|
5211
5615
|
B.condBr(hit, lh, ln);
|
|
@@ -5259,9 +5663,8 @@ export class LlEmitter {
|
|
|
5259
5663
|
}
|
|
5260
5664
|
for (const f of shape.fields) {
|
|
5261
5665
|
// typeEquals(f.type, iv) — the frontend fences everything else.
|
|
5262
|
-
const lit = this.internLiteral(f.name);
|
|
5263
5666
|
const hit = B.tmp();
|
|
5264
|
-
B.line(`${hit} = call zeroext i1
|
|
5667
|
+
B.line(`${hit} = call zeroext i1 ${this.literalEquals(f.name)}(ptr ${key.name}) ; ${llvmCommentText(f.name)}`);
|
|
5265
5668
|
const lh = B.newLabel("rks.h");
|
|
5266
5669
|
const ln = B.newLabel("rks.n");
|
|
5267
5670
|
B.condBr(hit, lh, ln);
|
|
@@ -5788,6 +6191,13 @@ export class LlEmitter {
|
|
|
5788
6191
|
// try, past its own catch), then the function-level releases and
|
|
5789
6192
|
// the actual ret. The parked value owns a synthetic slot-backed
|
|
5790
6193
|
// scope entry during each copy so a throwing finally releases it.
|
|
6194
|
+
const adopted = this.finallyStack.length > 0 || this.tryStack.length > 0
|
|
6195
|
+
? adoptingAwaitOf(s.value)
|
|
6196
|
+
: null;
|
|
6197
|
+
if (adopted !== null) {
|
|
6198
|
+
this.emitAdoptingReturn(s, adopted);
|
|
6199
|
+
break;
|
|
6200
|
+
}
|
|
5791
6201
|
let v = null;
|
|
5792
6202
|
const moved = this.finallyStack.length === 0 ? this.movableReturn(s.value) : null;
|
|
5793
6203
|
if (moved !== null) {
|
|
@@ -5855,12 +6265,14 @@ export class LlEmitter {
|
|
|
5855
6265
|
this.releaseForJump(0, 0);
|
|
5856
6266
|
}
|
|
5857
6267
|
if (!B.isTerminated()) {
|
|
5858
|
-
if (this.
|
|
6268
|
+
if (this.currentCoro !== null) {
|
|
5859
6269
|
this.emitWasiFulfill(v);
|
|
5860
|
-
B.terminate(`br label %${this.
|
|
6270
|
+
B.terminate(`br label %${this.currentCoro.finalLabel}`);
|
|
5861
6271
|
}
|
|
5862
6272
|
else if (v === null)
|
|
5863
6273
|
B.terminate("ret void");
|
|
6274
|
+
else if (this.currentInt32Result)
|
|
6275
|
+
B.terminate(`ret i32 ${this.int32Argument(v, s.value)}`);
|
|
5864
6276
|
else
|
|
5865
6277
|
B.terminate(`ret ${this.llType(s.value.type)} ${v.name}`);
|
|
5866
6278
|
}
|
|
@@ -6118,7 +6530,10 @@ export class LlEmitter {
|
|
|
6118
6530
|
this.frames.push([]);
|
|
6119
6531
|
const t = this.emitExpr(c.test);
|
|
6120
6532
|
const hit = B.tmp();
|
|
6121
|
-
if (c.test.
|
|
6533
|
+
if (c.test.kind === "strLit") {
|
|
6534
|
+
B.line(`${hit} = call zeroext i1 ${this.literalEquals(c.test.value)}(ptr ${disc.name})`);
|
|
6535
|
+
}
|
|
6536
|
+
else if (c.test.type.kind === "string") {
|
|
6122
6537
|
this.declare(`declare zeroext i1 @scr_str_eq(ptr, ptr)`);
|
|
6123
6538
|
B.line(`${hit} = call zeroext i1 @sc_str_eq(ptr ${disc.name}, ptr ${t.name})`);
|
|
6124
6539
|
}
|
|
@@ -6235,6 +6650,12 @@ export class LlEmitter {
|
|
|
6235
6650
|
return this.canBorrowCallArgument(value);
|
|
6236
6651
|
}
|
|
6237
6652
|
emitDiscarded(e) {
|
|
6653
|
+
// A statement-position yield never reads the resumed value: it stays
|
|
6654
|
+
// in the IN slot until the next resume replaces it.
|
|
6655
|
+
if (e.kind === "yieldExpr" && e.captureCompletion === undefined && e.type.kind !== "void") {
|
|
6656
|
+
this.emitExpr({ ...e, type: VOID });
|
|
6657
|
+
return;
|
|
6658
|
+
}
|
|
6238
6659
|
if (e.kind === "seqExpr" &&
|
|
6239
6660
|
isRefCounted(e.result.type) &&
|
|
6240
6661
|
e.result.kind !== "strLit" &&
|
|
@@ -6352,6 +6773,11 @@ export class LlEmitter {
|
|
|
6352
6773
|
emitCallExpr(e) {
|
|
6353
6774
|
return emitCallExpr(this, e);
|
|
6354
6775
|
}
|
|
6776
|
+
/** A direct async call an enclosing `await` consumes whole: emitted
|
|
6777
|
+
* through the callee's await-call entry, yielding the awaited value. */
|
|
6778
|
+
emitAwaitedCall(e, adopt = false) {
|
|
6779
|
+
return emitCallExpr(this, e, adopt ? "adopt" : "await");
|
|
6780
|
+
}
|
|
6355
6781
|
emitRecordExpr(e) {
|
|
6356
6782
|
return emitRecordExpr(this, e);
|
|
6357
6783
|
}
|
|
@@ -6518,9 +6944,6 @@ export class LlEmitter {
|
|
|
6518
6944
|
emitAsyncExpr(e) {
|
|
6519
6945
|
return emitAsyncExpr(this, e);
|
|
6520
6946
|
}
|
|
6521
|
-
emitJsInteropExpr(e) {
|
|
6522
|
-
return emitJsInteropExpr(this, e);
|
|
6523
|
-
}
|
|
6524
6947
|
/** Read a receiver whose value is consumed immediately by a field or tag
|
|
6525
6948
|
* load. No user code may run between this read and its consumer. Locals
|
|
6526
6949
|
* and their projections already have an owner; other expressions keep
|
|
@@ -7010,21 +7433,6 @@ export class LlEmitter {
|
|
|
7010
7433
|
emitExpr(e) {
|
|
7011
7434
|
return emitExpr(this, e);
|
|
7012
7435
|
}
|
|
7013
|
-
emitJsMarshal(e) {
|
|
7014
|
-
return emitJsMarshal(this, e);
|
|
7015
|
-
}
|
|
7016
|
-
emitJsOp(e) {
|
|
7017
|
-
return emitJsOp(this, e);
|
|
7018
|
-
}
|
|
7019
|
-
emitJsExit(e) {
|
|
7020
|
-
return emitJsExit(this, e);
|
|
7021
|
-
}
|
|
7022
|
-
islandAdapter(arity, retKind) {
|
|
7023
|
-
return islandAdapter(this, arity, retKind);
|
|
7024
|
-
}
|
|
7025
|
-
islandTypedAdapter(fn) {
|
|
7026
|
-
return islandTypedAdapter(this, fn);
|
|
7027
|
-
}
|
|
7028
7436
|
dynKind(d) {
|
|
7029
7437
|
return dynKind(this, d);
|
|
7030
7438
|
}
|
|
@@ -7139,6 +7547,49 @@ export class LlEmitter {
|
|
|
7139
7547
|
emitToUint32(value, expr, uint32) {
|
|
7140
7548
|
return emitToUint32(this, value, expr, uint32);
|
|
7141
7549
|
}
|
|
7550
|
+
/** Stores a number into its int32-stored local, when it is one. The
|
|
7551
|
+
* value is proven to be in the local's range, so its low 32 bits are the
|
|
7552
|
+
* exact value. */
|
|
7553
|
+
storeInt32Local(localId, value, expr) {
|
|
7554
|
+
const local = this.int32Locals.get(localId);
|
|
7555
|
+
if (local === undefined)
|
|
7556
|
+
return false;
|
|
7557
|
+
if (expr !== undefined && !this.int32LocalWrites.has(expr))
|
|
7558
|
+
throw new InternalCompilerError(`llvm emitter bug: unproven write of int32 local ${localId}`);
|
|
7559
|
+
const B = this.B;
|
|
7560
|
+
const integer = exactInteger(this, value, expr);
|
|
7561
|
+
let bits;
|
|
7562
|
+
if (integer && /^-?\d+$/.test(integer.name))
|
|
7563
|
+
bits = String(Number(integer.name) | 0); // a literal's i32 bits
|
|
7564
|
+
else if (integer)
|
|
7565
|
+
bits = narrowInteger(this, integer);
|
|
7566
|
+
else if (value.uint32 !== undefined)
|
|
7567
|
+
bits = value.uint32;
|
|
7568
|
+
else {
|
|
7569
|
+
// An Int32 contract proved this write without a range at the value.
|
|
7570
|
+
bits = B.tmp();
|
|
7571
|
+
B.line(`${bits} = ${local.signed ? "fptosi" : "fptoui"} double ${value.name} to i32`);
|
|
7572
|
+
}
|
|
7573
|
+
B.line(`store i32 ${bits}, ptr %${mangleLocal(localId)}`);
|
|
7574
|
+
return true;
|
|
7575
|
+
}
|
|
7576
|
+
/** The number of an int32-stored local's i32, with the i32 as its
|
|
7577
|
+
* integer and ToUint32 forms. A parameter the body never writes reads
|
|
7578
|
+
* as its incoming double, which is the same number. */
|
|
7579
|
+
int32LocalValue(localId, bits, type) {
|
|
7580
|
+
const local = this.int32Locals.get(localId);
|
|
7581
|
+
let number = `%p_${mangleLocal(localId)}`;
|
|
7582
|
+
if (!local.incoming) {
|
|
7583
|
+
number = this.B.tmp();
|
|
7584
|
+
this.B.line(`${number} = ${local.signed ? "sitofp" : "uitofp"} i32 ${bits} to double`);
|
|
7585
|
+
}
|
|
7586
|
+
return {
|
|
7587
|
+
name: number,
|
|
7588
|
+
type,
|
|
7589
|
+
uint32: bits,
|
|
7590
|
+
integer: { name: bits, type: "i32", signed: local.signed, range: local.range },
|
|
7591
|
+
};
|
|
7592
|
+
}
|
|
7142
7593
|
storeIntegerView(localId, value, expr) {
|
|
7143
7594
|
const slot = this.integerViews.get(localId);
|
|
7144
7595
|
if (slot === undefined)
|
|
@@ -7228,4 +7679,24 @@ export class LlEmitter {
|
|
|
7228
7679
|
return emitLibCall(this, e);
|
|
7229
7680
|
}
|
|
7230
7681
|
}
|
|
7682
|
+
/** The adopting await a `return` value resolves through, if any: the value
|
|
7683
|
+
* itself or the operand of its conversion into the declared result. */
|
|
7684
|
+
function adoptingAwaitOf(value) {
|
|
7685
|
+
if (value === null)
|
|
7686
|
+
return null;
|
|
7687
|
+
let found = null;
|
|
7688
|
+
everyExpr(value, {
|
|
7689
|
+
expr: (node) => {
|
|
7690
|
+
if (found !== null)
|
|
7691
|
+
return false;
|
|
7692
|
+
if (node.kind === "awaitExpr" && node.adopt === true) {
|
|
7693
|
+
found = node;
|
|
7694
|
+
return false;
|
|
7695
|
+
}
|
|
7696
|
+
return true;
|
|
7697
|
+
},
|
|
7698
|
+
stmt: () => true,
|
|
7699
|
+
});
|
|
7700
|
+
return found;
|
|
7701
|
+
}
|
|
7231
7702
|
//# sourceMappingURL=emitter.js.map
|