@scriptc/compiler 0.4.8 → 0.4.9
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/node-fallback/buffer.d.ts +2 -0
- package/ambient/node-fallback/child_process.d.ts +9 -3
- package/ambient/node-fallback/dns-promises.d.ts +25 -0
- package/ambient/node-fallback/dns.d.ts +29 -0
- package/ambient/node-fallback/test.d.ts +7 -0
- package/ambient/scriptc-node-fallback.d.ts +145 -2
- package/ambient/scriptc-overrides.d.ts +22 -13
- package/dist/backend/build-cache.d.ts +1 -1
- package/dist/backend/build-cache.js +1 -1
- package/dist/backend/build-cache.js.map +1 -1
- package/dist/backend/cache-root.d.ts +7 -0
- package/dist/backend/cache-root.js +27 -0
- package/dist/backend/cache-root.js.map +1 -1
- package/dist/backend/executable-features.js +5 -1
- package/dist/backend/executable-features.js.map +1 -1
- package/dist/backend/link-plan.js +7 -1
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/llvm/bytes-loop-versions.d.ts +12 -1
- package/dist/backend/llvm/bytes-loop-versions.js +107 -30
- package/dist/backend/llvm/bytes-loop-versions.js.map +1 -1
- package/dist/backend/llvm/bytes-loop-versions.test.js +96 -0
- package/dist/backend/llvm/bytes-loop-versions.test.js.map +1 -1
- package/dist/backend/llvm/checked-value-lifetimes.js +1 -0
- package/dist/backend/llvm/checked-value-lifetimes.js.map +1 -1
- package/dist/backend/llvm/classes.d.ts +16 -5
- package/dist/backend/llvm/classes.js +34 -6
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.js +21 -2
- package/dist/backend/llvm/classes.test.js.map +1 -1
- package/dist/backend/llvm/common.d.ts +9 -0
- package/dist/backend/llvm/common.js +9 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/cpu-dispatch.d.ts +36 -2
- package/dist/backend/llvm/cpu-dispatch.js +96 -2
- package/dist/backend/llvm/cpu-dispatch.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.d.ts +5 -2
- package/dist/backend/llvm/dense-array-access.js +16 -12
- package/dist/backend/llvm/dense-array-access.js.map +1 -1
- package/dist/backend/llvm/dense-array-access.test.js +3 -1
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +24 -0
- package/dist/backend/llvm/dyn.js +117 -3
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emit-function.js +6 -0
- package/dist/backend/llvm/emit-function.js.map +1 -1
- package/dist/backend/llvm/emit-journal.d.ts +1 -0
- package/dist/backend/llvm/emit-journal.js.map +1 -1
- package/dist/backend/llvm/emit-module.js +48 -5
- package/dist/backend/llvm/emit-module.js.map +1 -1
- package/dist/backend/llvm/emit-module.test.js +41 -0
- package/dist/backend/llvm/emit-module.test.js.map +1 -1
- package/dist/backend/llvm/emit-statements.js +11 -0
- package/dist/backend/llvm/emit-statements.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +87 -7
- package/dist/backend/llvm/emitter.js +136 -15
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +28 -6
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +19 -5
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +29 -1
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +104 -6
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +15 -1
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +53 -5
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/inline-arrays.js +1 -1
- package/dist/backend/llvm/inline-arrays.js.map +1 -1
- package/dist/backend/llvm/js-ic.js +126 -2
- package/dist/backend/llvm/js-ic.js.map +1 -1
- package/dist/backend/llvm/lanes.d.ts +38 -0
- package/dist/backend/llvm/lanes.js +778 -0
- package/dist/backend/llvm/lanes.js.map +1 -0
- package/dist/backend/llvm/lanes.test.d.ts +1 -0
- package/dist/backend/llvm/lanes.test.js +402 -0
- package/dist/backend/llvm/lanes.test.js.map +1 -0
- package/dist/backend/llvm/lib-dispatch.js +9 -4
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-network.js +20 -0
- package/dist/backend/llvm/lib-network.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +27 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/lib-test.js +11 -0
- package/dist/backend/llvm/lib-test.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.js +2 -2
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/map-number-lookup.js +8 -2
- package/dist/backend/llvm/map-number-lookup.js.map +1 -1
- package/dist/backend/llvm/nullable-unions.js +5 -2
- package/dist/backend/llvm/nullable-unions.js.map +1 -1
- package/dist/backend/llvm/publish.d.ts +3 -0
- package/dist/backend/llvm/publish.js +2 -2
- package/dist/backend/llvm/publish.js.map +1 -1
- package/dist/backend/llvm/reference-writes.js +1 -0
- package/dist/backend/llvm/reference-writes.js.map +1 -1
- package/dist/backend/llvm/region-groups.js +43 -4
- package/dist/backend/llvm/region-groups.js.map +1 -1
- package/dist/backend/llvm/region-groups.test.js +79 -0
- package/dist/backend/llvm/region-groups.test.js.map +1 -1
- package/dist/backend/llvm/runtime-registries.test.js +32 -1
- package/dist/backend/llvm/runtime-registries.test.js.map +1 -1
- package/dist/backend/llvm/shapes.js +10 -1
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/string-reads.d.ts +1 -1
- package/dist/backend/llvm/string-reads.js +1 -1
- package/dist/backend/llvm/string-reads.js.map +1 -1
- package/dist/backend/llvm/unwind-abi.js +24 -2
- package/dist/backend/llvm/unwind-abi.js.map +1 -1
- package/dist/backend/llvm/unwind-abi.test.js +38 -0
- package/dist/backend/llvm/unwind-abi.test.js.map +1 -1
- package/dist/backend/llvm/walkers.d.ts +3 -0
- package/dist/backend/llvm/walkers.js +20 -2
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/native/contracts.d.ts +15 -3
- package/dist/backend/native/executable.js +19 -5
- package/dist/backend/native/executable.js.map +1 -1
- package/dist/backend/native-codegen-core.d.ts +20 -0
- package/dist/backend/native-codegen-core.js +15 -0
- package/dist/backend/native-codegen-core.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +5 -0
- package/dist/backend/native-codegen.js +62 -8
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +48 -8
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info-core.d.ts +27 -3
- package/dist/backend/native-link-info-core.js.map +1 -1
- package/dist/backend/native-link-info.test.js +17 -1
- package/dist/backend/native-link-info.test.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +1 -1
- package/dist/backend/native-toolchain.js +1 -1
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +14 -1
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/runtime-pack-core.d.ts +9 -3
- package/dist/backend/runtime-pack-core.js +6 -1
- package/dist/backend/runtime-pack-core.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +32 -1
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/targets.d.ts +9 -1
- package/dist/backend/targets.js +11 -2
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +6 -0
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/cli/command.js +15 -1
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/host.d.ts +6 -0
- package/dist/cli/mcp.d.ts +72 -0
- package/dist/cli/mcp.js +601 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli/mcp.test.d.ts +1 -0
- package/dist/cli/mcp.test.js +380 -0
- package/dist/cli/mcp.test.js.map +1 -0
- package/dist/cli/output-options.d.ts +1 -0
- package/dist/cli/output-options.js +3 -0
- package/dist/cli/output-options.js.map +1 -1
- package/dist/cli/usage.d.ts +51 -1
- package/dist/cli/usage.js +709 -186
- package/dist/cli/usage.js.map +1 -1
- package/dist/compat/fetch-profile.js +9 -4
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/compile-types.d.ts +17 -0
- package/dist/coverage/report.d.ts +5 -9
- package/dist/coverage/report.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +17 -3
- package/dist/executable/executable-cache.js +17 -3
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/prepare.js +87 -10
- package/dist/executable/prepare.js.map +1 -1
- package/dist/frontend/analysis.js +3 -2
- package/dist/frontend/analysis.js.map +1 -1
- package/dist/frontend/canon-map.js +13 -8
- package/dist/frontend/canon-map.js.map +1 -1
- package/dist/frontend/decl-projection.d.ts +18 -0
- package/dist/frontend/decl-projection.js +90 -22
- package/dist/frontend/decl-projection.js.map +1 -1
- package/dist/frontend/decl-projection.test.js +47 -1
- package/dist/frontend/decl-projection.test.js.map +1 -1
- package/dist/frontend/default-library.d.ts +10 -0
- package/dist/frontend/default-library.js +43 -0
- package/dist/frontend/default-library.js.map +1 -0
- package/dist/frontend/fork-target.js +54 -9
- package/dist/frontend/fork-target.js.map +1 -1
- package/dist/frontend/fork-target.test.js +82 -0
- package/dist/frontend/fork-target.test.js.map +1 -1
- package/dist/frontend/inferred-js-diagnostics.test.js +73 -0
- package/dist/frontend/inferred-js-diagnostics.test.js.map +1 -1
- package/dist/frontend/lowering/abstract-equality.d.ts +2 -1
- package/dist/frontend/lowering/abstract-equality.js +7 -1
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/array-values.js +2 -2
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/assertion-members.js +52 -2
- package/dist/frontend/lowering/assertion-members.js.map +1 -1
- package/dist/frontend/lowering/builtin-namespaces.d.ts +5 -0
- package/dist/frontend/lowering/builtin-namespaces.js +19 -12
- package/dist/frontend/lowering/builtin-namespaces.js.map +1 -1
- package/dist/frontend/lowering/builtins/callable-aliases.js +42 -6
- package/dist/frontend/lowering/builtins/callable-aliases.js.map +1 -1
- package/dist/frontend/lowering/builtins/child-process.d.ts +12 -11
- package/dist/frontend/lowering/builtins/child-process.js +235 -38
- package/dist/frontend/lowering/builtins/child-process.js.map +1 -1
- package/dist/frontend/lowering/builtins/compression.d.ts +6 -1
- package/dist/frontend/lowering/builtins/compression.js +3 -3
- package/dist/frontend/lowering/builtins/compression.js.map +1 -1
- package/dist/frontend/lowering/builtins/constants.js +2 -2
- package/dist/frontend/lowering/builtins/constants.js.map +1 -1
- package/dist/frontend/lowering/builtins/crypto.d.ts +3 -1
- package/dist/frontend/lowering/builtins/crypto.js +9 -1
- package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
- package/dist/frontend/lowering/builtins/dates.js +26 -6
- package/dist/frontend/lowering/builtins/dates.js.map +1 -1
- package/dist/frontend/lowering/builtins/environment.d.ts +13 -1
- package/dist/frontend/lowering/builtins/environment.js +15 -4
- package/dist/frontend/lowering/builtins/environment.js.map +1 -1
- package/dist/frontend/lowering/builtins/module-bindings.d.ts +33 -0
- package/dist/frontend/lowering/builtins/module-bindings.js +213 -11
- package/dist/frontend/lowering/builtins/module-bindings.js.map +1 -1
- package/dist/frontend/lowering/builtins/module-resolution.d.ts +9 -2
- package/dist/frontend/lowering/builtins/module-resolution.js +187 -19
- package/dist/frontend/lowering/builtins/module-resolution.js.map +1 -1
- package/dist/frontend/lowering/builtins/performance.js +1 -1
- package/dist/frontend/lowering/builtins/performance.js.map +1 -1
- package/dist/frontend/lowering/builtins/process.js +40 -2
- package/dist/frontend/lowering/builtins/process.js.map +1 -1
- package/dist/frontend/lowering/builtins/promises.js +33 -0
- package/dist/frontend/lowering/builtins/promises.js.map +1 -1
- package/dist/frontend/lowering/builtins/readline.d.ts +9 -5
- package/dist/frontend/lowering/builtins/readline.js +184 -75
- package/dist/frontend/lowering/builtins/readline.js.map +1 -1
- package/dist/frontend/lowering/builtins/url.d.ts +4 -0
- package/dist/frontend/lowering/builtins/url.js +6 -0
- package/dist/frontend/lowering/builtins/url.js.map +1 -1
- package/dist/frontend/lowering/call-arguments.js +15 -0
- package/dist/frontend/lowering/call-arguments.js.map +1 -1
- package/dist/frontend/lowering/callable-properties.d.ts +34 -0
- package/dist/frontend/lowering/callable-properties.js +210 -0
- package/dist/frontend/lowering/callable-properties.js.map +1 -0
- package/dist/frontend/lowering/cjs-inline-require.d.ts +16 -0
- package/dist/frontend/lowering/cjs-inline-require.js +105 -0
- package/dist/frontend/lowering/cjs-inline-require.js.map +1 -1
- package/dist/frontend/lowering/cjs-inline-require.test.d.ts +1 -0
- package/dist/frontend/lowering/cjs-inline-require.test.js +61 -0
- package/dist/frontend/lowering/cjs-inline-require.test.js.map +1 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js +82 -2
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-protocols.d.ts +57 -4
- package/dist/frontend/lowering/class-protocols.js +597 -30
- package/dist/frontend/lowering/class-protocols.js.map +1 -1
- package/dist/frontend/lowering/class-unions.js +2 -2
- package/dist/frontend/lowering/class-unions.js.map +1 -1
- package/dist/frontend/lowering/coercions/function-adapters.d.ts +12 -0
- package/dist/frontend/lowering/coercions/function-adapters.js +62 -0
- package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
- package/dist/frontend/lowering/coercions/structural-plans.js +56 -1
- package/dist/frontend/lowering/coercions/structural-plans.js.map +1 -1
- package/dist/frontend/lowering/coercions/structural.js +9 -4
- package/dist/frontend/lowering/coercions/structural.js.map +1 -1
- package/dist/frontend/lowering/collection-ingestion.d.ts +9 -0
- package/dist/frontend/lowering/collection-ingestion.js +115 -41
- package/dist/frontend/lowering/collection-ingestion.js.map +1 -1
- package/dist/frontend/lowering/containers/array-callbacks.js +16 -0
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +66 -10
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/collection-methods.js +61 -5
- package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/filter-predicates.js +11 -0
- package/dist/frontend/lowering/containers/filter-predicates.js.map +1 -1
- package/dist/frontend/lowering/containers/indexed-objects.d.ts +15 -0
- package/dist/frontend/lowering/containers/indexed-objects.js +213 -30
- package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
- package/dist/frontend/lowering/error-name-order.d.ts +16 -0
- package/dist/frontend/lowering/error-name-order.js +61 -0
- package/dist/frontend/lowering/error-name-order.js.map +1 -1
- package/dist/frontend/lowering/expressions/asserted-receiver.d.ts +17 -0
- package/dist/frontend/lowering/expressions/asserted-receiver.js +123 -1
- package/dist/frontend/lowering/expressions/asserted-receiver.js.map +1 -1
- package/dist/frontend/lowering/expressions/binary.js +147 -20
- package/dist/frontend/lowering/expressions/binary.js.map +1 -1
- package/dist/frontend/lowering/expressions/element-access.js +63 -14
- package/dist/frontend/lowering/expressions/element-access.js.map +1 -1
- package/dist/frontend/lowering/expressions/erased-delete.d.ts +59 -0
- package/dist/frontend/lowering/expressions/erased-delete.js +247 -0
- package/dist/frontend/lowering/expressions/erased-delete.js.map +1 -0
- package/dist/frontend/lowering/expressions/hosted-narrowing.d.ts +10 -0
- package/dist/frontend/lowering/expressions/hosted-narrowing.js +48 -1
- package/dist/frontend/lowering/expressions/hosted-narrowing.js.map +1 -1
- package/dist/frontend/lowering/expressions/identifiers.js +20 -0
- package/dist/frontend/lowering/expressions/identifiers.js.map +1 -1
- package/dist/frontend/lowering/expressions/narrowing.d.ts +7 -2
- package/dist/frontend/lowering/expressions/narrowing.js +77 -45
- package/dist/frontend/lowering/expressions/narrowing.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +62 -19
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.js +19 -0
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/expressions/property-access.d.ts +7 -7
- package/dist/frontend/lowering/expressions/property-access.js +247 -48
- package/dist/frontend/lowering/expressions/property-access.js.map +1 -1
- package/dist/frontend/lowering/expressions/relational.d.ts +1 -0
- package/dist/frontend/lowering/expressions/relational.js +18 -8
- package/dist/frontend/lowering/expressions/relational.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-fields.js +11 -5
- package/dist/frontend/lowering/expressions/union-fields.js.map +1 -1
- package/dist/frontend/lowering/expressions/updates.js +121 -11
- package/dist/frontend/lowering/expressions/updates.js.map +1 -1
- package/dist/frontend/lowering/function-context.d.ts +10 -0
- package/dist/frontend/lowering/function-context.js +36 -0
- package/dist/frontend/lowering/function-context.js.map +1 -1
- package/dist/frontend/lowering/function-returns.js +8 -8
- package/dist/frontend/lowering/function-returns.js.map +1 -1
- package/dist/frontend/lowering/handle-properties.d.ts +62 -3
- package/dist/frontend/lowering/handle-properties.js +674 -26
- package/dist/frontend/lowering/handle-properties.js.map +1 -1
- package/dist/frontend/lowering/handle-properties.test.js +95 -2
- package/dist/frontend/lowering/handle-properties.test.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.d.ts +12 -0
- package/dist/frontend/lowering/lower-builtin-values.js +44 -0
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.js +328 -136
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.js +82 -17
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +113 -9
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-expando.d.ts +13 -21
- package/dist/frontend/lowering/lower-expando.js +90 -126
- package/dist/frontend/lowering/lower-expando.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.js +2 -2
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-functions.js +65 -0
- package/dist/frontend/lowering/lower-functions.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +471 -0
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +36 -10
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-node-island.js +28 -0
- package/dist/frontend/lowering/lower-node-island.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +53 -7
- package/dist/frontend/lowering/lower-server.js +213 -42
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stdlib.js +50 -7
- package/dist/frontend/lowering/lower-stdlib.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +24 -0
- package/dist/frontend/lowering/lower-stmts.js +463 -220
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +2 -2
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lower-test.js +71 -2
- package/dist/frontend/lowering/lower-test.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +81 -10
- package/dist/frontend/lowering/lowerer.js +319 -72
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/new-target.js +10 -2
- package/dist/frontend/lowering/new-target.js.map +1 -1
- package/dist/frontend/lowering/nullish-empty-pattern.js +3 -3
- package/dist/frontend/lowering/nullish-empty-pattern.js.map +1 -1
- package/dist/frontend/lowering/partial-assertions.js +30 -5
- package/dist/frontend/lowering/partial-assertions.js.map +1 -1
- package/dist/frontend/lowering/primitive-throws.js +91 -1
- package/dist/frontend/lowering/primitive-throws.js.map +1 -1
- package/dist/frontend/lowering/primitive-wrappers.d.ts +9 -0
- package/dist/frontend/lowering/primitive-wrappers.js +2 -2
- package/dist/frontend/lowering/primitive-wrappers.js.map +1 -1
- package/dist/frontend/lowering/promise-executor-adapter.js +5 -2
- package/dist/frontend/lowering/promise-executor-adapter.js.map +1 -1
- package/dist/frontend/lowering/record-links.js +7 -2
- package/dist/frontend/lowering/record-links.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +4 -0
- package/dist/frontend/lowering/surfaces.js +118 -2
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/surfaces.test.d.ts +1 -0
- package/dist/frontend/lowering/surfaces.test.js +31 -0
- package/dist/frontend/lowering/surfaces.test.js.map +1 -0
- package/dist/frontend/mock-shapes.js +3 -3
- package/dist/frontend/mock-shapes.js.map +1 -1
- package/dist/frontend/npm-static-ladder.d.ts +21 -0
- package/dist/frontend/npm-static-ladder.js +2 -0
- package/dist/frontend/npm-static-ladder.js.map +1 -0
- package/dist/frontend/npm-static-ladder.test.d.ts +1 -0
- package/dist/frontend/npm-static-ladder.test.js +138 -0
- package/dist/frontend/npm-static-ladder.test.js.map +1 -0
- package/dist/frontend/npm-static-status.d.ts +15 -0
- package/dist/frontend/npm-static-status.js +2 -0
- package/dist/frontend/npm-static-status.js.map +1 -0
- package/dist/frontend/package-shims.js +1 -1
- package/dist/frontend/package-shims.js.map +1 -1
- package/dist/frontend/pipeline.d.ts +1 -1
- package/dist/frontend/pipeline.js +222 -25
- package/dist/frontend/pipeline.js.map +1 -1
- package/dist/frontend/program-analysis.d.ts +4 -0
- package/dist/frontend/program-analysis.js +6 -0
- package/dist/frontend/program-analysis.js.map +1 -1
- package/dist/frontend/program.js +258 -32
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +30 -0
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/resolve.js +29 -5
- package/dist/frontend/resolve.js.map +1 -1
- package/dist/frontend/resolve.test.js +40 -0
- package/dist/frontend/resolve.test.js.map +1 -1
- package/dist/frontend/services.d.ts +8 -0
- package/dist/frontend/services.js +7 -0
- package/dist/frontend/services.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +7 -0
- package/dist/frontend/ts7/checker.js +12 -0
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/checker.test.js +1 -0
- package/dist/frontend/ts7/checker.test.js.map +1 -1
- package/dist/frontend/ts7/program-host.d.ts +39 -2
- package/dist/frontend/ts7/program-host.js +62 -8
- package/dist/frontend/ts7/program-host.js.map +1 -1
- package/dist/frontend/ts7/program-host.test.js +33 -0
- package/dist/frontend/ts7/program-host.test.js.map +1 -1
- package/dist/frontend/ts7/session-cache.d.ts +5 -1
- package/dist/frontend/ts7/session-cache.js +7 -1
- package/dist/frontend/ts7/session-cache.js.map +1 -1
- package/dist/frontend/ts7/session-cache.test.js +37 -0
- package/dist/frontend/ts7/session-cache.test.js.map +1 -1
- package/dist/frontend/ts7/session.js +19 -1
- package/dist/frontend/ts7/session.js.map +1 -1
- package/dist/frontend/ts7/source-parser.d.ts +17 -2
- package/dist/frontend/ts7/source-parser.js +61 -2
- package/dist/frontend/ts7/source-parser.js.map +1 -1
- package/dist/frontend/ts7/source-parser.test.js +55 -2
- package/dist/frontend/ts7/source-parser.test.js.map +1 -1
- package/dist/frontend/tsc-codes.d.ts +3 -2
- package/dist/frontend/tsc-codes.js +129 -22
- package/dist/frontend/tsc-codes.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +5 -11
- package/dist/frontend/type-mapper.js +380 -70
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +67 -7
- package/dist/index.js.map +1 -1
- package/dist/ir/builtin-effects.js +29 -0
- package/dist/ir/builtin-effects.js.map +1 -1
- package/dist/ir/builtin-signatures.d.ts +1 -1
- package/dist/ir/builtin-signatures.js +72 -6
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/codec.generated.d.ts +1 -1
- package/dist/ir/codec.generated.js +321 -263
- package/dist/ir/codec.generated.js.map +1 -1
- package/dist/ir/constant-length-clones.d.ts +26 -0
- package/dist/ir/constant-length-clones.js +589 -0
- package/dist/ir/constant-length-clones.js.map +1 -0
- package/dist/ir/constant-length-clones.test.d.ts +1 -0
- package/dist/ir/constant-length-clones.test.js +292 -0
- package/dist/ir/constant-length-clones.test.js.map +1 -0
- package/dist/ir/constant-tables.d.ts +10 -5
- package/dist/ir/constant-tables.js +83 -17
- package/dist/ir/constant-tables.js.map +1 -1
- package/dist/ir/constant-tables.test.js +58 -1
- package/dist/ir/constant-tables.test.js.map +1 -1
- package/dist/ir/fields.generated.js +5 -2
- package/dist/ir/fields.generated.js.map +1 -1
- package/dist/ir/function-references.js +2 -0
- package/dist/ir/function-references.js.map +1 -1
- package/dist/ir/function-references.test.js +13 -0
- package/dist/ir/function-references.test.js.map +1 -1
- package/dist/ir/integer-loops.js +1 -0
- package/dist/ir/integer-loops.js.map +1 -1
- package/dist/ir/integer-loops.test.js +16 -0
- package/dist/ir/integer-loops.test.js.map +1 -1
- package/dist/ir/ir.d.ts +50 -6
- package/dist/ir/ir.js +44 -2
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +83 -1
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/js-groups.js +5 -1
- package/dist/ir/js-groups.js.map +1 -1
- package/dist/ir/js-ic.d.ts +20 -0
- package/dist/ir/js-ic.js +81 -4
- package/dist/ir/js-ic.js.map +1 -1
- package/dist/ir/js-ic.test.js +8 -1
- package/dist/ir/js-ic.test.js.map +1 -1
- package/dist/ir/lane-plans.d.ts +86 -0
- package/dist/ir/lane-plans.js +589 -0
- package/dist/ir/lane-plans.js.map +1 -0
- package/dist/ir/lane-plans.test.d.ts +1 -0
- package/dist/ir/lane-plans.test.js +549 -0
- package/dist/ir/lane-plans.test.js.map +1 -0
- package/dist/ir/runtime-features.d.ts +68 -10
- package/dist/ir/runtime-features.js +285 -17
- package/dist/ir/runtime-features.js.map +1 -1
- package/dist/ir/runtime-features.test.js +190 -1
- package/dist/ir/runtime-features.test.js.map +1 -1
- package/dist/ir/validate.js +61 -24
- package/dist/ir/validate.js.map +1 -1
- package/dist/native/cache.d.ts +5 -0
- package/dist/native/cache.js +23 -1
- package/dist/native/cache.js.map +1 -1
- package/dist/native/cache.test.js +29 -1
- package/dist/native/cache.test.js.map +1 -1
- package/dist/native/cli.js +4 -2
- package/dist/native/cli.js.map +1 -1
- package/dist/native/compiler.d.ts +11 -3
- package/dist/native/compiler.js +102 -16
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/compiler.test.js +244 -2
- package/dist/native/compiler.test.js.map +1 -1
- package/dist/native/prepare.d.ts +14 -1
- package/dist/native/prepare.js +79 -29
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/sanitizer.test.js +3 -0
- package/dist/native/sanitizer.test.js.map +1 -1
- package/dist/native/unchanged.d.ts +80 -0
- package/dist/native/unchanged.js +301 -0
- package/dist/native/unchanged.js.map +1 -0
- package/dist/native/unchanged.test.d.ts +1 -0
- package/dist/native/unchanged.test.js +249 -0
- package/dist/native/unchanged.test.js.map +1 -0
- package/dist/node-islands/boundary.d.ts +16 -0
- package/dist/node-islands/boundary.js +70 -7
- package/dist/node-islands/boundary.js.map +1 -1
- package/dist/node-islands/boundary.test.js +59 -14
- package/dist/node-islands/boundary.test.js.map +1 -1
- package/dist/node-islands/detect.js +203 -47
- package/dist/node-islands/detect.js.map +1 -1
- package/dist/node-islands/detect.test.js +45 -0
- package/dist/node-islands/detect.test.js.map +1 -1
- package/dist/node-islands/growth.js +4 -16
- package/dist/node-islands/growth.js.map +1 -1
- package/dist/node-islands/modules.d.ts +3 -0
- package/dist/node-islands/modules.js +143 -12
- package/dist/node-islands/modules.js.map +1 -1
- package/dist/node-islands/modules.test.js +125 -0
- package/dist/node-islands/modules.test.js.map +1 -1
- package/dist/node-islands/report.d.ts +1 -1
- package/dist/node-islands/report.js +2 -1
- package/dist/node-islands/report.js.map +1 -1
- package/package.json +20 -20
- package/shims/undici/core/request.ts +7 -1
- package/surface-manifest.json +380 -2
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
/* Lane loops: the `elementwise` lane plans of ir/lane-plans.ts, lowered to
|
|
2
|
+
* LLVM vector IR.
|
|
3
|
+
*
|
|
4
|
+
* A planned loop keeps its scalar form. The emitter adds, before the loop's
|
|
5
|
+
* copies, an entry guard and a lane loop:
|
|
6
|
+
*
|
|
7
|
+
* guard: SCRIPTC_LANES is on, at least one whole vector is left, and (for
|
|
8
|
+
* a sum) the element count is below the bound that makes every
|
|
9
|
+
* partial sum an exact double;
|
|
10
|
+
* lanes: load <W x iN> at the element index, widen to <W x i64>, fold;
|
|
11
|
+
* exit: add the folded lanes to the accumulator once and leave the
|
|
12
|
+
* induction at the lane loop's limit.
|
|
13
|
+
*
|
|
14
|
+
* The false edge of the guard, and the induction the lane loop leaves behind,
|
|
15
|
+
* are the loop's own scalar code, unchanged: the loop's copies start at the
|
|
16
|
+
* current induction, so they run the elements the lanes left over. The
|
|
17
|
+
* accumulator is read from its slot after the last lane iteration (the scalar
|
|
18
|
+
* body stores it at the end of every iteration), never from a value the lane
|
|
19
|
+
* loop is about to overwrite.
|
|
20
|
+
*
|
|
21
|
+
* A store loop is the same skeleton without an accumulator: the guard keeps
|
|
22
|
+
* the toggle and a whole vector and adds the entry tests the stored value
|
|
23
|
+
* needs, the lane loop transcribes the value's tree lane by lane and writes
|
|
24
|
+
* one vector, and the scalar copies write the elements the lanes left over.
|
|
25
|
+
*
|
|
26
|
+
* A dense `number[]` plan carries one more entry test. Its slots hold int32s
|
|
27
|
+
* or doubles, decided per array at run time and never mixed, so the guard
|
|
28
|
+
* also requires the storage kind the plan was made for (`SCR_ELEM_I32` for a
|
|
29
|
+
* sum's or count's exact integer elements, `SCR_ELEM_F64` for a store's
|
|
30
|
+
* doubles) and that every slot below the length holds a value (`filled`) —
|
|
31
|
+
* the scalar element read traps on a hole, and the lanes have nowhere to put
|
|
32
|
+
* that trap. A sum's or count's lanes read the int32 slots and widen them
|
|
33
|
+
* with their sign, which the entry bounds keep in range.
|
|
34
|
+
*
|
|
35
|
+
* Exactness. Each lane sums a subclass of the elements, and the lanes' i64
|
|
36
|
+
* sums are folded into the accumulator once. Any order gives the same double
|
|
37
|
+
* when every partial sum is an integer below 2^53 in magnitude. The plan
|
|
38
|
+
* requires the accumulator to be exactly +0 at the loop entry, so the loop's
|
|
39
|
+
* own partial sums are the sums of element prefixes, which the entry guard
|
|
40
|
+
* keeps below 2^53 for the sum shape (the count shape's result is at most the
|
|
41
|
+
* length). The scalar remainder then adds the elements the lanes left over:
|
|
42
|
+
* every partial sum of that addition is also a prefix sum of the loop, hence
|
|
43
|
+
* also below the bound. Node's ordered fadd chain over the same integers
|
|
44
|
+
* cannot round either, so both produce the same double. */
|
|
45
|
+
import { laneElem, laneShapesFromEnvironment, planLaneLoops } from "../../ir/lane-plans.js";
|
|
46
|
+
import { withinInt32 } from "../../ir/integer-ranges.js";
|
|
47
|
+
import { loadArrCount, SCR_ARR_FIELD } from "./common.js";
|
|
48
|
+
import { SCR_ELEM_F64, SCR_ELEM_I32 } from "./dense-array-access.js";
|
|
49
|
+
import { emitBytesData, emitBytesHeaderLength } from "./expr-bytes.js";
|
|
50
|
+
import { defineModuleState } from "./emitter-state.js";
|
|
51
|
+
/** The run-time toggle: `SCRIPTC_LANES=0` runs the scalar loops. */
|
|
52
|
+
const TOGGLE = "@sc_lanes";
|
|
53
|
+
/** The call main makes when the program has lane loops. */
|
|
54
|
+
export const LANE_INIT_CALL = ` call void @sc_lanes_init()`;
|
|
55
|
+
export const LANES = defineModuleState((emitter) => ({
|
|
56
|
+
shapes: laneShapesFromEnvironment(),
|
|
57
|
+
width: laneWidth(emitter),
|
|
58
|
+
plans: new Map(),
|
|
59
|
+
}));
|
|
60
|
+
/** Whether this build emits lane loops at all. */
|
|
61
|
+
export function lanePlansEnabled(emitter) {
|
|
62
|
+
return LANES.of(emitter).shapes.size > 0;
|
|
63
|
+
}
|
|
64
|
+
/** Integers per vector for the target. The width is a property of the
|
|
65
|
+
* target, not of a plan, so one plan lowers to every target's width. A
|
|
66
|
+
* target without vector integers (wasm) gets width 1 and no plans. */
|
|
67
|
+
export function laneWidth(emitter) {
|
|
68
|
+
if (emitter.wasi)
|
|
69
|
+
return 1;
|
|
70
|
+
const triple = emitter.targetTriple.toLowerCase();
|
|
71
|
+
const host = triple || `${process.arch}-${process.platform}`;
|
|
72
|
+
if (/^(aarch64|arm64)/.test(host))
|
|
73
|
+
return 4;
|
|
74
|
+
if (!/^(x86_64|amd64|x64)/.test(host))
|
|
75
|
+
return 1;
|
|
76
|
+
// A dispatching executable carries one body per level; the body is the
|
|
77
|
+
// baseline's width, which every level it runs on can execute.
|
|
78
|
+
if (emitter.cpuDispatch)
|
|
79
|
+
return 4;
|
|
80
|
+
switch (emitter.targetCpu) {
|
|
81
|
+
case "x86-64-v2":
|
|
82
|
+
return 4;
|
|
83
|
+
case "x86-64-v3":
|
|
84
|
+
return 8;
|
|
85
|
+
case "x86-64-v4":
|
|
86
|
+
return 16;
|
|
87
|
+
default:
|
|
88
|
+
return 4;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Sets the lane plans of the function emission is starting. The matcher is
|
|
92
|
+
* the loop matcher's: the function with its initializer bindings, which is
|
|
93
|
+
* the one `emitter.emitStmts` walks. */
|
|
94
|
+
export function beginFunctionLanes(emitter, fn) {
|
|
95
|
+
const state = LANES.of(emitter);
|
|
96
|
+
state.plans =
|
|
97
|
+
state.shapes.size === 0 || emitter.debug !== null
|
|
98
|
+
? new Map()
|
|
99
|
+
: planLaneLoops(fn, emitter.numericLocals, state.shapes);
|
|
100
|
+
}
|
|
101
|
+
/** The plan for one loop, when the emitter can lower it here: a synchronous
|
|
102
|
+
* function on a target with vector integers, outside every value-layout copy
|
|
103
|
+
* the lane body does not write. */
|
|
104
|
+
export function laneLoopPlan(emitter, loop) {
|
|
105
|
+
const state = LANES.of(emitter);
|
|
106
|
+
if (state.width < 2 || emitter.debug !== null)
|
|
107
|
+
return null;
|
|
108
|
+
if (emitter.mod.workers === true || emitter.mod.bytesHeadersMutable === true)
|
|
109
|
+
return null;
|
|
110
|
+
if (emitter.speculation !== null || emitter.speculationResume !== null)
|
|
111
|
+
return null;
|
|
112
|
+
if (emitter.int32Kinds !== null)
|
|
113
|
+
return null;
|
|
114
|
+
const plan = state.plans.get(loop);
|
|
115
|
+
if (plan === undefined)
|
|
116
|
+
return null;
|
|
117
|
+
// The lane body stores the accumulator's double slot directly; an int32
|
|
118
|
+
// local keeps an integer in that slot, and its copies handle those loops.
|
|
119
|
+
if (plan.shape !== "store") {
|
|
120
|
+
if (emitter.int32Locals.has(plan.accumulator))
|
|
121
|
+
return null;
|
|
122
|
+
const binding = emitter.binding(plan.accumulator);
|
|
123
|
+
if (binding.kind !== "local" || binding.type.kind !== "f64")
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return plan;
|
|
127
|
+
}
|
|
128
|
+
/** The LLVM integer comparison for a count's element test. */
|
|
129
|
+
const ICMP = new Map([
|
|
130
|
+
["<", ["slt", "ult"]],
|
|
131
|
+
["<=", ["sle", "ule"]],
|
|
132
|
+
[">", ["sgt", "ugt"]],
|
|
133
|
+
[">=", ["sge", "uge"]],
|
|
134
|
+
["===", ["eq", "eq"]],
|
|
135
|
+
["!==", ["ne", "ne"]],
|
|
136
|
+
]);
|
|
137
|
+
/** The address of one %ScrArr header field. */
|
|
138
|
+
function arrayHeaderField(emitter, array, field) {
|
|
139
|
+
const B = emitter.B;
|
|
140
|
+
const ptr = B.tmp();
|
|
141
|
+
B.line(`${ptr} = getelementptr inbounds %ScrArr, ptr ${array}, i32 0, i32 ${field}`);
|
|
142
|
+
emitter.markMemoryPointer(ptr, "array:header");
|
|
143
|
+
return ptr;
|
|
144
|
+
}
|
|
145
|
+
/** A dense number array's length, or the hole-free prefix bound below which
|
|
146
|
+
* every slot holds a present value, in the target's size type. */
|
|
147
|
+
function emitArrayHeaderCount(emitter, array, field) {
|
|
148
|
+
const B = emitter.B;
|
|
149
|
+
const ptr = arrayHeaderField(emitter, array, SCR_ARR_FIELD[field]);
|
|
150
|
+
const count = B.tmp();
|
|
151
|
+
if (field === "len") {
|
|
152
|
+
loadArrCount(emitter, count, ptr, emitter.fieldAliasAttachment(ptr));
|
|
153
|
+
return count;
|
|
154
|
+
}
|
|
155
|
+
const narrow = B.tmp();
|
|
156
|
+
B.line(`${narrow} = load i32, ptr ${ptr}${emitter.fieldAliasAttachment(ptr)}`);
|
|
157
|
+
if (emitter.sizeType === "i32")
|
|
158
|
+
return narrow;
|
|
159
|
+
B.line(`${count} = zext i32 ${narrow} to i64`);
|
|
160
|
+
return count;
|
|
161
|
+
}
|
|
162
|
+
/** The dense value storage base of a number array. */
|
|
163
|
+
function emitArrayData(emitter, array) {
|
|
164
|
+
const B = emitter.B;
|
|
165
|
+
const ptr = arrayHeaderField(emitter, array, SCR_ARR_FIELD.data);
|
|
166
|
+
const data = B.tmp();
|
|
167
|
+
B.line(`${data} = load ptr, ptr ${ptr}${emitter.fieldAliasAttachment(ptr)}`);
|
|
168
|
+
return data;
|
|
169
|
+
}
|
|
170
|
+
/** Whether every slot below the array's length is a present value held in
|
|
171
|
+
* the storage the plan reads: `length <= filled`, and the array's slots are
|
|
172
|
+
* the kind the plan was made for. A dense array keeps every slot below
|
|
173
|
+
* `filled` a present value, so the lane loop needs no per-element state
|
|
174
|
+
* test; the storage kind decides whether those slots are int32s (sums and
|
|
175
|
+
* counts) or doubles (stores). */
|
|
176
|
+
function emitDenseSlotTest(emitter, array, length, elem) {
|
|
177
|
+
const B = emitter.B;
|
|
178
|
+
const size = emitter.sizeType;
|
|
179
|
+
const filled = emitArrayHeaderCount(emitter, array, "filled");
|
|
180
|
+
const covered = B.tmp();
|
|
181
|
+
B.line(`${covered} = icmp ule ${size} ${length}, ${filled}`);
|
|
182
|
+
const kindPtr = arrayHeaderField(emitter, array, SCR_ARR_FIELD.elem);
|
|
183
|
+
const kind = B.tmp(), same = B.tmp(), wanted = elem === "i32slot" ? SCR_ELEM_I32 : SCR_ELEM_F64;
|
|
184
|
+
B.line(`${kind} = load i8, ptr ${kindPtr}${emitter.fieldAliasAttachment(kindPtr)}`);
|
|
185
|
+
B.line(`${same} = icmp eq i8 ${kind}, ${wanted}`);
|
|
186
|
+
const ok = B.tmp();
|
|
187
|
+
B.line(`${ok} = and i1 ${covered}, ${same}`);
|
|
188
|
+
return ok;
|
|
189
|
+
}
|
|
190
|
+
function emitLaneEntry(emitter, plan, receiver, inductionSlot) {
|
|
191
|
+
const B = emitter.B;
|
|
192
|
+
const width = LANES.of(emitter).width;
|
|
193
|
+
const size = emitter.sizeType;
|
|
194
|
+
const view = emitter.emitStableReceiver(receiver, []);
|
|
195
|
+
const length = plan.container === "bytes"
|
|
196
|
+
? emitBytesHeaderLength(emitter, view.name)
|
|
197
|
+
: emitArrayHeaderCount(emitter, view.name, "len");
|
|
198
|
+
const data = plan.container === "bytes"
|
|
199
|
+
? emitBytesData(emitter, view.name)
|
|
200
|
+
: emitArrayData(emitter, view.name);
|
|
201
|
+
const storage = plan.container === "bytes" ? null : emitDenseSlotTest(emitter, view.name, length, plan.elem);
|
|
202
|
+
// The counter at the entry, and the elements left from it: the lane loop
|
|
203
|
+
// covers the whole vectors of that span and leaves the rest to the loop.
|
|
204
|
+
const start = B.tmp(), span = B.tmp();
|
|
205
|
+
B.line(`${start} = load ${size}, ptr ${inductionSlot}`);
|
|
206
|
+
B.line(`${span} = sub nuw ${size} ${length}, ${start}`);
|
|
207
|
+
// The toggle: a predictable load every entry, as the design's guard says.
|
|
208
|
+
const toggle = B.tmp(), enabled = B.tmp(), wide = B.tmp();
|
|
209
|
+
B.line(`${toggle} = load i32, ptr ${TOGGLE}`);
|
|
210
|
+
B.line(`${enabled} = icmp ne i32 ${toggle}, 0`);
|
|
211
|
+
B.line(`${wide} = icmp uge ${size} ${span}, ${width}`);
|
|
212
|
+
const entry = {
|
|
213
|
+
length,
|
|
214
|
+
data,
|
|
215
|
+
start,
|
|
216
|
+
span,
|
|
217
|
+
whole: "",
|
|
218
|
+
limit: "",
|
|
219
|
+
ok: "",
|
|
220
|
+
base: "",
|
|
221
|
+
tighten: (cond) => {
|
|
222
|
+
const both = B.tmp();
|
|
223
|
+
B.line(`${both} = and i1 ${entry.ok}, ${cond}`);
|
|
224
|
+
entry.ok = both;
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
let first = B.tmp();
|
|
228
|
+
B.line(`${first} = and i1 ${enabled}, ${wide}`);
|
|
229
|
+
if (storage !== null) {
|
|
230
|
+
const inStorage = B.tmp();
|
|
231
|
+
B.line(`${inStorage} = and i1 ${first}, ${storage}`);
|
|
232
|
+
first = inStorage;
|
|
233
|
+
}
|
|
234
|
+
entry.ok = first;
|
|
235
|
+
entry.base = first;
|
|
236
|
+
// The lane loop's exclusive end: whole vectors from the entry.
|
|
237
|
+
const whole = B.tmp(), limit = B.tmp();
|
|
238
|
+
B.line(`${whole} = and ${size} ${span}, ${-width}`);
|
|
239
|
+
B.line(`${limit} = add nuw ${size} ${start}, ${whole}`);
|
|
240
|
+
entry.whole = whole;
|
|
241
|
+
entry.limit = limit;
|
|
242
|
+
return entry;
|
|
243
|
+
}
|
|
244
|
+
/** Emits the entry guard and the lane loop for `plan`, then starts `cont`.
|
|
245
|
+
* `receiver` is the loop's limit expression (the typed array read at the
|
|
246
|
+
* induction variable); `inductionSlot` is the loop's counter. Every path out
|
|
247
|
+
* of the lane code reaches `cont` with the scalar loop's inputs in place: the
|
|
248
|
+
* counter at the lane loop's limit when the lanes ran, untouched when they
|
|
249
|
+
* did not. */
|
|
250
|
+
export function emitLaneLoop(emitter, plan, receiver, inductionSlot, cont) {
|
|
251
|
+
if (plan.shape === "store") {
|
|
252
|
+
emitLaneStore(emitter, plan, receiver, inductionSlot, cont);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const B = emitter.B;
|
|
256
|
+
const width = LANES.of(emitter).width;
|
|
257
|
+
const elem = laneElem(plan.elem);
|
|
258
|
+
if (elem === null)
|
|
259
|
+
throw new Error(`lane plan on a ${plan.elem} element`);
|
|
260
|
+
const size = emitter.sizeType;
|
|
261
|
+
const entry = emitLaneEntry(emitter, plan, receiver, inductionSlot);
|
|
262
|
+
const { length, data, limit } = entry;
|
|
263
|
+
if (plan.shape === "sum") {
|
|
264
|
+
const bound = B.tmp();
|
|
265
|
+
B.line(`${bound} = icmp ult ${size} ${length}, ${plan.lengthBound}`);
|
|
266
|
+
entry.tighten(bound);
|
|
267
|
+
}
|
|
268
|
+
const init = B.newLabel("lane.init"), header = B.newLabel("lane.c"), body = B.newLabel("lane.b"), exit = B.newLabel("lane.x");
|
|
269
|
+
const accSlot = B.slot();
|
|
270
|
+
B.entryAllocas.push(`${accSlot} = alloca <${width} x i64> ; lane sums`);
|
|
271
|
+
let record = `<${width} x i64> zeroinitializer`;
|
|
272
|
+
if (plan.shape === "count") {
|
|
273
|
+
// `element <op> limit` in integers: the limit must be an exact integer
|
|
274
|
+
// in the element's range, which the entry checks once. Otherwise the
|
|
275
|
+
// scalar loop answers with the same doubles it would have compared.
|
|
276
|
+
const value = emitter.emitExpr(plan.compare.limit);
|
|
277
|
+
const lowest = elem.signed ? -(2 ** (elem.bits - 1)) : 0;
|
|
278
|
+
const highest = elem.signed ? 2 ** (elem.bits - 1) : 2 ** elem.bits;
|
|
279
|
+
const lowOk = B.tmp(), highOk = B.tmp(), inRange = B.tmp(), safe = B.tmp();
|
|
280
|
+
B.line(`${lowOk} = fcmp oge double ${value.name}, ${lowest}.0`);
|
|
281
|
+
B.line(`${highOk} = fcmp olt double ${value.name}, ${highest}.0`);
|
|
282
|
+
B.line(`${inRange} = and i1 ${lowOk}, ${highOk}`);
|
|
283
|
+
B.line(`${safe} = select i1 ${inRange}, double ${value.name}, double 0.0`);
|
|
284
|
+
const narrow64 = B.tmp(), back = B.tmp(), exact = B.tmp();
|
|
285
|
+
B.line(`${narrow64} = fptosi double ${safe} to i64`);
|
|
286
|
+
B.line(`${back} = sitofp i64 ${narrow64} to double`);
|
|
287
|
+
B.line(`${exact} = fcmp oeq double ${back}, ${value.name}`);
|
|
288
|
+
const integer = B.tmp();
|
|
289
|
+
B.line(`${integer} = and i1 ${inRange}, ${exact}`);
|
|
290
|
+
entry.tighten(integer);
|
|
291
|
+
const narrow = B.tmp(), first = B.tmp(), splat = B.tmp();
|
|
292
|
+
B.line(`${narrow} = trunc i64 ${narrow64} to ${elem.type}`);
|
|
293
|
+
B.line(`${first} = insertelement <${width} x ${elem.type}> poison, ${elem.type} ${narrow}, i32 0`);
|
|
294
|
+
B.line(`${splat} = shufflevector <${width} x ${elem.type}> ${first}, <${width} x ${elem.type}> poison, <${width} x i32> zeroinitializer`);
|
|
295
|
+
record = splat;
|
|
296
|
+
}
|
|
297
|
+
B.condBr(entry.ok, init, cont);
|
|
298
|
+
B.startBlock(init);
|
|
299
|
+
B.line(`store <${width} x i64> zeroinitializer, ptr ${accSlot}`);
|
|
300
|
+
B.br(header);
|
|
301
|
+
B.startBlock(header);
|
|
302
|
+
const index = B.tmp(), more = B.tmp();
|
|
303
|
+
B.line(`${index} = load ${size}, ptr ${inductionSlot}`);
|
|
304
|
+
B.line(`${more} = icmp ult ${size} ${index}, ${limit}`);
|
|
305
|
+
B.condBr(more, body, exit);
|
|
306
|
+
B.startBlock(body);
|
|
307
|
+
const at = B.tmp(), elements = B.tmp(), terms = B.tmp(), before = B.tmp(), after = B.tmp(), next = B.tmp();
|
|
308
|
+
B.line(`${at} = getelementptr inbounds ${elem.type}, ptr ${data}, ${size} ${index}`);
|
|
309
|
+
// align 1: a view's backing may be any alignment; the load still lowers to
|
|
310
|
+
// one unaligned vector move on every target this emits for.
|
|
311
|
+
B.line(`${elements} = load <${width} x ${elem.type}>, ptr ${at}, align 1`);
|
|
312
|
+
if (plan.shape === "sum") {
|
|
313
|
+
B.line(`${terms} = ${elem.signed ? "sext" : "zext"} <${width} x ${elem.type}> ${elements} to <${width} x i64>`);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
const [signed, unsigned] = ICMP.get(plan.compare.op) ?? ["sge", "uge"];
|
|
317
|
+
const mask = B.tmp();
|
|
318
|
+
B.line(`${mask} = icmp ${elem.signed ? signed : unsigned} <${width} x ${elem.type}> ${elements}, ${record}`);
|
|
319
|
+
B.line(`${terms} = zext <${width} x i1> ${mask} to <${width} x i64>`);
|
|
320
|
+
}
|
|
321
|
+
B.line(`${before} = load <${width} x i64>, ptr ${accSlot}`);
|
|
322
|
+
B.line(`${after} = add <${width} x i64> ${before}, ${terms}`);
|
|
323
|
+
B.line(`store <${width} x i64> ${after}, ptr ${accSlot}`);
|
|
324
|
+
B.line(`${next} = add nuw ${size} ${index}, ${width}`);
|
|
325
|
+
B.line(`store ${size} ${next}, ptr ${inductionSlot}`);
|
|
326
|
+
B.br(header);
|
|
327
|
+
B.startBlock(exit);
|
|
328
|
+
const lanes = B.tmp();
|
|
329
|
+
B.line(`${lanes} = load <${width} x i64>, ptr ${accSlot}`);
|
|
330
|
+
let reduced = null;
|
|
331
|
+
for (let lane = 0; lane < width; lane++) {
|
|
332
|
+
const value = B.tmp();
|
|
333
|
+
B.line(`${value} = extractelement <${width} x i64> ${lanes}, i32 ${lane}`);
|
|
334
|
+
if (reduced === null)
|
|
335
|
+
reduced = value;
|
|
336
|
+
else {
|
|
337
|
+
const sum = B.tmp();
|
|
338
|
+
B.line(`${sum} = add i64 ${reduced}, ${value}`);
|
|
339
|
+
reduced = sum;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const exact = B.tmp(), current = B.tmp(), total = B.tmp();
|
|
343
|
+
B.line(`${exact} = sitofp i64 ${reduced} to double`);
|
|
344
|
+
B.line(`${current} = load double, ptr ${emitter.binding(plan.accumulator).slot}`);
|
|
345
|
+
B.line(`${total} = fadd double ${current}, ${exact}`);
|
|
346
|
+
B.line(`store double ${total}, ptr ${emitter.binding(plan.accumulator).slot}`);
|
|
347
|
+
B.br(cont);
|
|
348
|
+
B.startBlock(cont);
|
|
349
|
+
}
|
|
350
|
+
function storeElem(elem) {
|
|
351
|
+
switch (elem) {
|
|
352
|
+
case "u8c":
|
|
353
|
+
return { type: "i8", max: 255, float: false, clamped: true, signed: false };
|
|
354
|
+
case "u8":
|
|
355
|
+
return { type: "i8", max: 255, float: false, clamped: false, signed: false };
|
|
356
|
+
case "i8":
|
|
357
|
+
return { type: "i8", max: 128, float: false, clamped: false, signed: true };
|
|
358
|
+
case "u16":
|
|
359
|
+
return { type: "i16", max: 65535, float: false, clamped: false, signed: false };
|
|
360
|
+
case "i16":
|
|
361
|
+
return { type: "i16", max: 32768, float: false, clamped: false, signed: true };
|
|
362
|
+
case "u32":
|
|
363
|
+
return { type: "i32", max: 4294967295, float: false, clamped: false, signed: false };
|
|
364
|
+
case "i32":
|
|
365
|
+
return { type: "i32", max: 2147483648, float: false, clamped: false, signed: true };
|
|
366
|
+
case "f32":
|
|
367
|
+
return { type: "float", max: 0, float: true, clamped: false, signed: true };
|
|
368
|
+
case "f64":
|
|
369
|
+
return { type: "double", max: 0, float: true, clamped: false, signed: true };
|
|
370
|
+
// A dense number array's double slots hold the stored double as it is.
|
|
371
|
+
case "f64slot":
|
|
372
|
+
return { type: "double", max: 0, float: true, clamped: false, signed: true };
|
|
373
|
+
default:
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/** A vector holding one constant in every lane, as the constant expression
|
|
378
|
+
* the IR uses (`splat (i64 1)`). */
|
|
379
|
+
function vectorSplatLiteral(type, literal) {
|
|
380
|
+
return `splat (${type} ${literal})`;
|
|
381
|
+
}
|
|
382
|
+
/** `<W x i64>` <0, 1, ... W-1>, built once per loop entry: the constant
|
|
383
|
+
* folding pass turns the chain into a vector constant. */
|
|
384
|
+
function laneIndices(emitter, width) {
|
|
385
|
+
const B = emitter.B;
|
|
386
|
+
let vec = B.tmp();
|
|
387
|
+
B.line(`${vec} = insertelement <${width} x i64> poison, i64 0, i32 0`);
|
|
388
|
+
for (let lane = 1; lane < width; lane++) {
|
|
389
|
+
const next = B.tmp();
|
|
390
|
+
B.line(`${next} = insertelement <${width} x i64> ${vec}, i64 ${lane}, i32 ${lane}`);
|
|
391
|
+
vec = next;
|
|
392
|
+
}
|
|
393
|
+
return vec;
|
|
394
|
+
}
|
|
395
|
+
function splatValue(emitter, type, value, width) {
|
|
396
|
+
const B = emitter.B;
|
|
397
|
+
const first = B.tmp(), vec = B.tmp();
|
|
398
|
+
B.line(`${first} = insertelement <${width} x ${type}> poison, ${type} ${value}, i32 0`);
|
|
399
|
+
B.line(`${vec} = shufflevector <${width} x ${type}> ${first}, <${width} x ${type}> poison, <${width} x i32> zeroinitializer`);
|
|
400
|
+
return vec;
|
|
401
|
+
}
|
|
402
|
+
/** An exact integer a stored value's invariant contributes, when it has one:
|
|
403
|
+
* the integer the invariant was emitted with, or an integer literal within
|
|
404
|
+
* int32. The literal's bound is what keeps the integer route's i64 arithmetic
|
|
405
|
+
* from overflowing: an induction below 2^31 times such a literal is below
|
|
406
|
+
* 2^62. A larger or fractional literal takes the double route instead. */
|
|
407
|
+
function invariantInteger(expr, value) {
|
|
408
|
+
if (value.integer !== undefined)
|
|
409
|
+
return value.integer;
|
|
410
|
+
if (expr.kind !== "numLit")
|
|
411
|
+
return null;
|
|
412
|
+
const literal = expr.value;
|
|
413
|
+
if (!Number.isInteger(literal) || Object.is(literal, -0))
|
|
414
|
+
return null;
|
|
415
|
+
if (!withinInt32({ min: literal, max: literal }))
|
|
416
|
+
return null;
|
|
417
|
+
return { type: "i64", name: String(literal), range: { min: literal, max: literal } };
|
|
418
|
+
}
|
|
419
|
+
/** The value's low 32 bits as lanes: ToUint32, exactly as expr-bytes.ts
|
|
420
|
+
* `emitToUint32` computes it. `emitValueBound` keeps every lane below 2^63 in
|
|
421
|
+
* magnitude at the loop entry, where one `fptosi` and one `trunc` are the
|
|
422
|
+
* integer the scalar body's own fast path stores. */
|
|
423
|
+
function emitVectorToUint32(emitter, value, width) {
|
|
424
|
+
const B = emitter.B;
|
|
425
|
+
const integer = B.tmp(), low = B.tmp();
|
|
426
|
+
B.line(`${integer} = fptosi <${width} x double> ${value} to <${width} x i64>`);
|
|
427
|
+
B.line(`${low} = trunc <${width} x i64> ${integer} to <${width} x i32>`);
|
|
428
|
+
return low;
|
|
429
|
+
}
|
|
430
|
+
/** ToUint8Clamp as one lane of a vector, the same branch-free form the
|
|
431
|
+
* scalar store uses: NaN and values at most 0 store 0, values at least 255
|
|
432
|
+
* store 255, and the others round to nearest with ties to even. */
|
|
433
|
+
function emitVectorClamp(emitter, value, width) {
|
|
434
|
+
const B = emitter.B;
|
|
435
|
+
const v = `<${width} x double>`;
|
|
436
|
+
const positive = B.tmp(), high = B.tmp(), shifted = B.tmp(), rounded = B.tmp(), upper = B.tmp(), clamped = B.tmp();
|
|
437
|
+
B.line(`${positive} = fcmp ogt ${v} ${value}, ${vectorSplatLiteral("double", "0.0")}`);
|
|
438
|
+
B.line(`${high} = fcmp oge ${v} ${value}, ${vectorSplatLiteral("double", "255.0")}`);
|
|
439
|
+
B.line(`${shifted} = fadd ${v} ${value}, ${vectorSplatLiteral("double", "4503599627370496.0")}`);
|
|
440
|
+
B.line(`${rounded} = fsub ${v} ${shifted}, ${vectorSplatLiteral("double", "4503599627370496.0")}`);
|
|
441
|
+
B.line(`${upper} = select <${width} x i1> ${high}, ${v} ${vectorSplatLiteral("double", "255.0")}, ${v} ${rounded}`);
|
|
442
|
+
B.line(`${clamped} = select <${width} x i1> ${positive}, ${v} ${upper}, ${v} zeroinitializer`);
|
|
443
|
+
const out = B.tmp();
|
|
444
|
+
B.line(`${out} = fptoui ${v} ${clamped} to <${width} x i32>`);
|
|
445
|
+
return out;
|
|
446
|
+
}
|
|
447
|
+
function once(emit) {
|
|
448
|
+
let value = null;
|
|
449
|
+
return () => (value ??= emit());
|
|
450
|
+
}
|
|
451
|
+
/** An exact integer as `<W x i64>` lanes. An i32 integer widens with its sign,
|
|
452
|
+
* which the element store never sees past the low bits and the entry bounds
|
|
453
|
+
* keep in range; i64 integers are already lanes. */
|
|
454
|
+
function integerLanes(emitter, integer, width) {
|
|
455
|
+
const lanes = splatValue(emitter, integer.type, integer.name, width);
|
|
456
|
+
if (integer.type === "i64")
|
|
457
|
+
return lanes;
|
|
458
|
+
const wide = emitter.B.tmp();
|
|
459
|
+
emitter.B.line(`${wide} = sext <${width} x i32> ${lanes} to <${width} x i64>`);
|
|
460
|
+
return wide;
|
|
461
|
+
}
|
|
462
|
+
/** One node of the stored value as lanes: the scalar loop's operations,
|
|
463
|
+
* applied to `<W x double>` or `<W x i64>` instead of doubles and integers. */
|
|
464
|
+
function laneNodeValue(emitter, node, ctx) {
|
|
465
|
+
const B = emitter.B;
|
|
466
|
+
const { width } = ctx;
|
|
467
|
+
if (node.op === null) {
|
|
468
|
+
if (node.role === "induction")
|
|
469
|
+
return { int: ctx.integers ? ctx.index : null, dbl: ctx.indexDouble };
|
|
470
|
+
if (node.role === "element")
|
|
471
|
+
return { int: null, dbl: once(() => emitElementLanes(emitter, ctx)) };
|
|
472
|
+
const value = ctx.invariants.get(node.expr);
|
|
473
|
+
const integer = ctx.integers ? invariantInteger(node.expr, value) : null;
|
|
474
|
+
return {
|
|
475
|
+
int: integer === null ? null : integerLanes(emitter, integer, width),
|
|
476
|
+
dbl: once(() => splatValue(emitter, "double", value.name, width)),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
const left = laneNodeValue(emitter, node.left, ctx);
|
|
480
|
+
const right = laneNodeValue(emitter, node.right, ctx);
|
|
481
|
+
if (node.op === "*") {
|
|
482
|
+
// The double form is what a float element stores. An integer element
|
|
483
|
+
// stores the exact product when both operands are exact integers: the
|
|
484
|
+
// entry bound keeps it below 2^62, inside i64.
|
|
485
|
+
let product = null;
|
|
486
|
+
if (left.int !== null && right.int !== null) {
|
|
487
|
+
product = B.tmp();
|
|
488
|
+
B.line(`${product} = mul nsw <${width} x i64> ${left.int}, ${right.int}`);
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
int: product,
|
|
492
|
+
dbl: once(() => {
|
|
493
|
+
const value = B.tmp();
|
|
494
|
+
B.line(`${value} = fmul <${width} x double> ${left.dbl()}, ${right.dbl()}`);
|
|
495
|
+
return value;
|
|
496
|
+
}),
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
if (left.int !== null && right.int !== null) {
|
|
500
|
+
const result = B.tmp();
|
|
501
|
+
B.line(`${result} = ${node.op === "+" ? "add" : "sub"} nsw <${width} x i64> ${left.int}, ${right.int}`);
|
|
502
|
+
return {
|
|
503
|
+
int: result,
|
|
504
|
+
dbl: once(() => {
|
|
505
|
+
const exact = B.tmp();
|
|
506
|
+
B.line(`${exact} = sitofp <${width} x i64> ${result} to <${width} x double>`);
|
|
507
|
+
return exact;
|
|
508
|
+
}),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
const result = B.tmp();
|
|
512
|
+
B.line(`${result} = ${node.op === "+" ? "fadd" : "fsub"} <${width} x double> ${left.dbl()}, ${right.dbl()}`);
|
|
513
|
+
return { int: null, dbl: () => result };
|
|
514
|
+
}
|
|
515
|
+
/** `receiver[induction]` as `<W x double>`, through the element conversion
|
|
516
|
+
* the scalar read uses (widen to i32, then convert to double). */
|
|
517
|
+
function emitElementLanes(emitter, ctx) {
|
|
518
|
+
const B = emitter.B;
|
|
519
|
+
const { width, data } = ctx;
|
|
520
|
+
const info = storeElem(ctx.elem);
|
|
521
|
+
const at = B.tmp(), raw = B.tmp();
|
|
522
|
+
B.line(`${at} = getelementptr inbounds ${info.type}, ptr ${data}, ${emitter.sizeType} ${ctx.scalar}`);
|
|
523
|
+
B.line(`${raw} = load <${width} x ${info.type}>, ptr ${at}, align 1`);
|
|
524
|
+
if (info.type === "double")
|
|
525
|
+
return raw;
|
|
526
|
+
if (info.type === "float") {
|
|
527
|
+
const wide = B.tmp();
|
|
528
|
+
B.line(`${wide} = fpext <${width} x float> ${raw} to <${width} x double>`);
|
|
529
|
+
return wide;
|
|
530
|
+
}
|
|
531
|
+
const wide = info.type === "i32" ? raw : B.tmp();
|
|
532
|
+
if (info.type !== "i32")
|
|
533
|
+
B.line(`${wide} = ${info.signed ? "sext" : "zext"} <${width} x ${info.type}> ${raw} to <${width} x i32>`);
|
|
534
|
+
const out = B.tmp();
|
|
535
|
+
B.line(`${out} = ${info.signed ? "sitofp" : "uitofp"} <${width} x i32> ${wide} to <${width} x double>`);
|
|
536
|
+
return out;
|
|
537
|
+
}
|
|
538
|
+
/** Every invariant expression in a stored value. */
|
|
539
|
+
function storeInvariants(node, out) {
|
|
540
|
+
if (node.left !== null)
|
|
541
|
+
storeInvariants(node.left, out);
|
|
542
|
+
if (node.right !== null)
|
|
543
|
+
storeInvariants(node.right, out);
|
|
544
|
+
if (node.role === "invariant" && node.expr !== null)
|
|
545
|
+
out.push(node.expr);
|
|
546
|
+
}
|
|
547
|
+
/** Whether a stored value reads the element or the induction. */
|
|
548
|
+
function storeReads(node, role) {
|
|
549
|
+
if (node.role === role)
|
|
550
|
+
return true;
|
|
551
|
+
return ((node.left !== null && storeReads(node.left, role)) ||
|
|
552
|
+
(node.right !== null && storeReads(node.right, role)));
|
|
553
|
+
}
|
|
554
|
+
/** A scalar double that no lane of the stored value exceeds in magnitude,
|
|
555
|
+
* computed with each operand at its own largest: the element at the widest
|
|
556
|
+
* value its type holds, an invariant at its magnitude, the counter at the
|
|
557
|
+
* array's length. Every operation is monotone and the lanes apply the same
|
|
558
|
+
* operations to smaller operands, so rounding the bound can only overstate
|
|
559
|
+
* the lanes. A NaN anywhere leaves the bound a NaN, which fails the test. */
|
|
560
|
+
function emitValueBound(emitter, node, invariants, elementMax, length) {
|
|
561
|
+
const B = emitter.B;
|
|
562
|
+
if (node.op === null) {
|
|
563
|
+
if (node.role === "induction") {
|
|
564
|
+
const count = B.tmp();
|
|
565
|
+
B.line(`${count} = uitofp ${emitter.sizeType} ${length} to double`);
|
|
566
|
+
return count;
|
|
567
|
+
}
|
|
568
|
+
if (node.role === "element")
|
|
569
|
+
return `${elementMax}.0`;
|
|
570
|
+
const value = invariants.get(node.expr);
|
|
571
|
+
const positive = B.tmp(), negative = B.tmp(), magnitude = B.tmp();
|
|
572
|
+
B.line(`${positive} = fcmp oge double ${value.name}, 0.0`);
|
|
573
|
+
B.line(`${negative} = fneg double ${value.name}`);
|
|
574
|
+
B.line(`${magnitude} = select i1 ${positive}, double ${value.name}, double ${negative}`);
|
|
575
|
+
return magnitude;
|
|
576
|
+
}
|
|
577
|
+
const left = emitValueBound(emitter, node.left, invariants, elementMax, length);
|
|
578
|
+
const right = emitValueBound(emitter, node.right, invariants, elementMax, length);
|
|
579
|
+
const combined = B.tmp();
|
|
580
|
+
B.line(`${combined} = ${node.op === "*" ? "fmul" : "fadd"} double ${left}, ${right}`);
|
|
581
|
+
return combined;
|
|
582
|
+
}
|
|
583
|
+
/** Emits a store loop for `plan`: whole vectors of the array are written
|
|
584
|
+
* from lane values, and the loop's own copies write what the lanes left.
|
|
585
|
+
*
|
|
586
|
+
* Every lane computes the value with the operations the scalar body uses,
|
|
587
|
+
* on the same operands, so the stored bits are the scalar loop's. Entry
|
|
588
|
+
* tests keep that true where the lanes take a different route than the
|
|
589
|
+
* scalar body: the exact integer the integer lanes compute must stay below
|
|
590
|
+
* 2^53 in magnitude, an element times an exact integer must keep every
|
|
591
|
+
* product it meets exact, and a value converted straight to ToUint32 must
|
|
592
|
+
* stay below 2^63. All are loop-invariant, so the scalar remainder still
|
|
593
|
+
* answers with its own bytes whenever they fail. */
|
|
594
|
+
function emitLaneStore(emitter, plan, receiver, inductionSlot, cont) {
|
|
595
|
+
const B = emitter.B;
|
|
596
|
+
const width = LANES.of(emitter).width;
|
|
597
|
+
const info = storeElem(plan.elem);
|
|
598
|
+
if (info === null)
|
|
599
|
+
throw new Error(`lane store on a ${plan.elem} element`);
|
|
600
|
+
const invariants = new Map();
|
|
601
|
+
const exprs = [];
|
|
602
|
+
storeInvariants(plan.value, exprs);
|
|
603
|
+
for (const expr of exprs)
|
|
604
|
+
if (!invariants.has(expr))
|
|
605
|
+
invariants.set(expr, emitter.emitExpr(expr));
|
|
606
|
+
const exact = [...invariants.entries()].flatMap(([expr, value]) => {
|
|
607
|
+
const integer = invariantInteger(expr, value);
|
|
608
|
+
return integer === null ? [] : [{ name: value.name, integer }];
|
|
609
|
+
});
|
|
610
|
+
const usesElement = storeReads(plan.value, "element");
|
|
611
|
+
const size = emitter.sizeType;
|
|
612
|
+
// Entry tests beyond the shared guard, each a loop-invariant condition on
|
|
613
|
+
// the values the scalar body is about to read.
|
|
614
|
+
const tests = [];
|
|
615
|
+
const magnitude = (name, range, limit) => {
|
|
616
|
+
if (range.min > -limit && range.max < limit)
|
|
617
|
+
return;
|
|
618
|
+
tests.push((entry) => {
|
|
619
|
+
const low = B.tmp(), high = B.tmp();
|
|
620
|
+
B.line(`${low} = fcmp ogt double ${name}, ${-limit}.0`);
|
|
621
|
+
B.line(`${high} = fcmp olt double ${name}, ${limit}.0`);
|
|
622
|
+
entry.tighten(low);
|
|
623
|
+
entry.tighten(high);
|
|
624
|
+
});
|
|
625
|
+
};
|
|
626
|
+
// An integer element is stored from the exact integer the scalar body
|
|
627
|
+
// computes when every operand is an exact integer; the value is then
|
|
628
|
+
// ToUint32's low bits, which the integer route truncates directly. A
|
|
629
|
+
// float element is always stored from the scalar body's own doubles: the
|
|
630
|
+
// lane body applies the same operations to the same operands, so its
|
|
631
|
+
// result is bit-identical and needs no test.
|
|
632
|
+
let int = false;
|
|
633
|
+
if (!info.float) {
|
|
634
|
+
if (!usesElement && exact.length === invariants.size) {
|
|
635
|
+
// The integer route stores the low bits of the exact integer its lane
|
|
636
|
+
// arithmetic produces. Those are the scalar body's bytes only while
|
|
637
|
+
// the value stays below 2^53 in magnitude: below it every integer is
|
|
638
|
+
// its own double, so the scalar body's double form stores the same
|
|
639
|
+
// bits, and above it the double rounds and its low bits differ. A
|
|
640
|
+
// clamped element rounds and clamps the double, so it needs no test.
|
|
641
|
+
int = true;
|
|
642
|
+
if (!info.clamped)
|
|
643
|
+
tests.push((entry) => {
|
|
644
|
+
const limit = emitValueBound(emitter, plan.value, invariants, info.max, entry.length);
|
|
645
|
+
const within = B.tmp();
|
|
646
|
+
B.line(`${within} = fcmp olt double ${limit}, 9007199254740992.0`);
|
|
647
|
+
entry.tighten(within);
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
else if (exact.length > 0 && usesElement) {
|
|
651
|
+
// An element operand and an exact integer in one operation: the scalar
|
|
652
|
+
// body may have done the operation on the integers. Below 2^53 the
|
|
653
|
+
// double form stores the same bits.
|
|
654
|
+
const limit = Math.floor(plan.value.op === "*" ? 2 ** 53 / info.max : 2 ** 53 - info.max);
|
|
655
|
+
for (const value of exact)
|
|
656
|
+
magnitude(value.name, value.integer.range, limit);
|
|
657
|
+
}
|
|
658
|
+
if (!int && !info.clamped) {
|
|
659
|
+
// The lanes that are not exact integers take ToUint32's direct
|
|
660
|
+
// conversion, which is exact below 2^63. The entry test asks whether
|
|
661
|
+
// the value's own magnitude, with every operand at its largest, stays
|
|
662
|
+
// there; loops that can leave it keep their scalar form.
|
|
663
|
+
tests.push((entry) => {
|
|
664
|
+
const limit = emitValueBound(emitter, plan.value, invariants, info.max, entry.length);
|
|
665
|
+
const inside = B.tmp();
|
|
666
|
+
B.line(`${inside} = fcmp olt double ${limit}, 9223372036854775808.0`);
|
|
667
|
+
entry.tighten(inside);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const entry = emitLaneEntry(emitter, plan, receiver, inductionSlot);
|
|
672
|
+
const steps = laneIndices(emitter, width);
|
|
673
|
+
const header = B.newLabel("lane.c"), body = B.newLabel("lane.b"), exit = B.newLabel("lane.x");
|
|
674
|
+
// The shape's own tests are computed only where the shared guard holds, so
|
|
675
|
+
// a build with the toggle off pays the toggle load and the guard's branch,
|
|
676
|
+
// not the bounds, for every loop it keeps scalar.
|
|
677
|
+
if (tests.length === 0)
|
|
678
|
+
B.condBr(entry.ok, header, cont);
|
|
679
|
+
else {
|
|
680
|
+
const test = B.newLabel("lane.t");
|
|
681
|
+
B.condBr(entry.base, test, cont);
|
|
682
|
+
B.startBlock(test);
|
|
683
|
+
for (const run of tests)
|
|
684
|
+
run(entry);
|
|
685
|
+
B.condBr(entry.ok, header, cont);
|
|
686
|
+
}
|
|
687
|
+
B.startBlock(header);
|
|
688
|
+
const index = B.tmp(), more = B.tmp();
|
|
689
|
+
B.line(`${index} = load ${size}, ptr ${inductionSlot}`);
|
|
690
|
+
B.line(`${more} = icmp ult ${size} ${index}, ${entry.limit}`);
|
|
691
|
+
B.condBr(more, body, exit);
|
|
692
|
+
B.startBlock(body);
|
|
693
|
+
const indices = B.tmp();
|
|
694
|
+
B.line(`${indices} = add <${width} x i64> ${steps}, ${splatValue(emitter, "i64", index, width)}`);
|
|
695
|
+
const ctx = {
|
|
696
|
+
width,
|
|
697
|
+
data: entry.data,
|
|
698
|
+
elem: plan.elem,
|
|
699
|
+
scalar: index,
|
|
700
|
+
index: indices,
|
|
701
|
+
indexDouble: once(() => {
|
|
702
|
+
const doubles = B.tmp();
|
|
703
|
+
B.line(`${doubles} = sitofp <${width} x i64> ${indices} to <${width} x double>`);
|
|
704
|
+
return doubles;
|
|
705
|
+
}),
|
|
706
|
+
invariants,
|
|
707
|
+
integers: !info.float,
|
|
708
|
+
};
|
|
709
|
+
const value = laneNodeValue(emitter, plan.value, ctx);
|
|
710
|
+
const at = B.tmp();
|
|
711
|
+
B.line(`${at} = getelementptr inbounds ${info.type}, ptr ${entry.data}, ${size} ${index}`);
|
|
712
|
+
const type = info.type;
|
|
713
|
+
const exactInteger = int ? value.int : null;
|
|
714
|
+
let stored;
|
|
715
|
+
if (info.float) {
|
|
716
|
+
const doubles = value.dbl();
|
|
717
|
+
if (type === "float") {
|
|
718
|
+
stored = B.tmp();
|
|
719
|
+
B.line(`${stored} = fptrunc <${width} x double> ${doubles} to <${width} x float>`);
|
|
720
|
+
}
|
|
721
|
+
else
|
|
722
|
+
stored = doubles;
|
|
723
|
+
}
|
|
724
|
+
else if (info.clamped) {
|
|
725
|
+
// Rounding and clamping are the scalar body's own double operations.
|
|
726
|
+
const bits = emitVectorClamp(emitter, value.dbl(), width);
|
|
727
|
+
stored = B.tmp();
|
|
728
|
+
B.line(`${stored} = trunc <${width} x i32> ${bits} to <${width} x i8>`);
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
// Every element type is at most 32 bits wide, so the value is ToUint32
|
|
732
|
+
// of the scalar body's number, as emitBytesSet stores it.
|
|
733
|
+
const from = exactInteger === null ? "i32" : "i64";
|
|
734
|
+
const bits = exactInteger === null ? emitVectorToUint32(emitter, value.dbl(), width) : exactInteger;
|
|
735
|
+
if (type === from)
|
|
736
|
+
stored = bits;
|
|
737
|
+
else {
|
|
738
|
+
stored = B.tmp();
|
|
739
|
+
B.line(`${stored} = trunc <${width} x ${from}> ${bits} to <${width} x ${type}>`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
B.line(`store <${width} x ${type}> ${stored}, ptr ${at}, align 1`);
|
|
743
|
+
const next = B.tmp();
|
|
744
|
+
B.line(`${next} = add nuw ${size} ${index}, ${width}`);
|
|
745
|
+
B.line(`store ${size} ${next}, ptr ${inductionSlot}`);
|
|
746
|
+
B.br(header);
|
|
747
|
+
B.startBlock(exit);
|
|
748
|
+
B.br(cont);
|
|
749
|
+
B.startBlock(cont);
|
|
750
|
+
}
|
|
751
|
+
/** @sc_lanes and @sc_lanes_init, which main calls first: `SCRIPTC_LANES=0`
|
|
752
|
+
* turns every lane loop off, and any other value leaves them on. */
|
|
753
|
+
export function laneToggleDefs(declare) {
|
|
754
|
+
declare(`declare ptr @getenv(ptr)`);
|
|
755
|
+
declare(`declare i32 @strcmp(ptr, ptr)`);
|
|
756
|
+
return [
|
|
757
|
+
`${TOGGLE} = internal global i32 1`,
|
|
758
|
+
`@sc_lanes_env = private unnamed_addr constant [14 x i8] c"SCRIPTC_LANES\\00"`,
|
|
759
|
+
`@sc_lanes_off = private unnamed_addr constant [2 x i8] c"0\\00"`,
|
|
760
|
+
`define internal void @sc_lanes_init() #0 {`,
|
|
761
|
+
`entry:`,
|
|
762
|
+
` %env = call ptr @getenv(ptr @sc_lanes_env)`,
|
|
763
|
+
` %has = icmp ne ptr %env, null`,
|
|
764
|
+
` br i1 %has, label %check, label %done`,
|
|
765
|
+
`check:`,
|
|
766
|
+
` %same = call i32 @strcmp(ptr %env, ptr @sc_lanes_off)`,
|
|
767
|
+
` %off = icmp eq i32 %same, 0`,
|
|
768
|
+
` br i1 %off, label %disable, label %done`,
|
|
769
|
+
`disable:`,
|
|
770
|
+
` store i32 0, ptr ${TOGGLE}`,
|
|
771
|
+
` br label %done`,
|
|
772
|
+
`done:`,
|
|
773
|
+
` ret void`,
|
|
774
|
+
`}`,
|
|
775
|
+
``,
|
|
776
|
+
];
|
|
777
|
+
}
|
|
778
|
+
//# sourceMappingURL=lanes.js.map
|