@scriptc/compiler 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +62 -48
- package/dist/backend/cycle-analysis.d.ts +21 -9
- package/dist/backend/cycle-analysis.js +139 -67
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/cycle-analysis.test.js +283 -3
- package/dist/backend/cycle-analysis.test.js.map +1 -1
- package/dist/backend/cycle-mutability.d.ts +39 -0
- package/dist/backend/cycle-mutability.js +205 -0
- package/dist/backend/cycle-mutability.js.map +1 -0
- package/dist/backend/executable-features.js +1 -0
- package/dist/backend/executable-features.js.map +1 -1
- package/dist/backend/link-plan-core.d.ts +4 -1
- package/dist/backend/link-plan-core.js +16 -3
- package/dist/backend/link-plan-core.js.map +1 -1
- package/dist/backend/link-plan.d.ts +3 -1
- package/dist/backend/link-plan.js +3 -0
- package/dist/backend/link-plan.js.map +1 -1
- package/dist/backend/llvm/alloc.d.ts +40 -0
- package/dist/backend/llvm/alloc.js +205 -0
- package/dist/backend/llvm/alloc.js.map +1 -0
- package/dist/backend/llvm/alloc.test.d.ts +1 -0
- package/dist/backend/llvm/alloc.test.js +102 -0
- package/dist/backend/llvm/alloc.test.js.map +1 -0
- package/dist/backend/llvm/byte-numbers.js +32 -4
- package/dist/backend/llvm/byte-numbers.js.map +1 -1
- package/dist/backend/llvm/byte-windows.d.ts +31 -0
- package/dist/backend/llvm/byte-windows.js +334 -0
- package/dist/backend/llvm/byte-windows.js.map +1 -0
- package/dist/backend/llvm/byte-windows.test.d.ts +1 -0
- package/dist/backend/llvm/byte-windows.test.js +244 -0
- package/dist/backend/llvm/byte-windows.test.js.map +1 -0
- package/dist/backend/llvm/call-lifetimes.d.ts +17 -5
- package/dist/backend/llvm/call-lifetimes.js +58 -24
- package/dist/backend/llvm/call-lifetimes.js.map +1 -1
- package/dist/backend/llvm/call-lifetimes.test.js +170 -0
- package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/classes.d.ts +18 -2
- package/dist/backend/llvm/classes.js +115 -41
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/classes.test.d.ts +1 -0
- package/dist/backend/llvm/classes.test.js +132 -0
- package/dist/backend/llvm/classes.test.js.map +1 -0
- package/dist/backend/llvm/common.d.ts +13 -0
- package/dist/backend/llvm/common.js +30 -0
- package/dist/backend/llvm/common.js.map +1 -1
- package/dist/backend/llvm/constant-callbacks.d.ts +27 -0
- package/dist/backend/llvm/constant-callbacks.js +450 -0
- package/dist/backend/llvm/constant-callbacks.js.map +1 -0
- package/dist/backend/llvm/constant-callbacks.test.d.ts +1 -0
- package/dist/backend/llvm/constant-callbacks.test.js +261 -0
- package/dist/backend/llvm/constant-callbacks.test.js.map +1 -0
- package/dist/backend/llvm/dense-array-access.d.ts +29 -0
- package/dist/backend/llvm/dense-array-access.js +335 -0
- package/dist/backend/llvm/dense-array-access.js.map +1 -0
- package/dist/backend/llvm/dense-array-access.test.d.ts +1 -0
- package/dist/backend/llvm/dense-array-access.test.js +149 -0
- package/dist/backend/llvm/dense-array-access.test.js.map +1 -0
- package/dist/backend/llvm/dyn.d.ts +35 -1
- package/dist/backend/llvm/dyn.js +196 -35
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +143 -4
- package/dist/backend/llvm/emitter.js +882 -136
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.test.js +82 -14
- package/dist/backend/llvm/emitter.test.js.map +1 -1
- package/dist/backend/llvm/expr-async.js +58 -4
- package/dist/backend/llvm/expr-async.js.map +1 -1
- package/dist/backend/llvm/expr-async.test.d.ts +1 -0
- package/dist/backend/llvm/expr-async.test.js +62 -0
- package/dist/backend/llvm/expr-async.test.js.map +1 -0
- package/dist/backend/llvm/expr-bytes.js +11 -0
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-calls.js +151 -25
- package/dist/backend/llvm/expr-calls.js.map +1 -1
- package/dist/backend/llvm/expr-containers.js +37 -14
- package/dist/backend/llvm/expr-containers.js.map +1 -1
- package/dist/backend/llvm/expr-control.js +92 -2
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dispatch.js +2 -0
- package/dist/backend/llvm/expr-dispatch.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +210 -28
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.d.ts +1 -1
- package/dist/backend/llvm/expr-primitives.js +58 -23
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.test.js +1 -1
- package/dist/backend/llvm/expr-records.js +3 -3
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +59 -2
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.test.js +1 -1
- package/dist/backend/llvm/integer-remainder.d.ts +18 -0
- package/dist/backend/llvm/integer-remainder.js +117 -0
- package/dist/backend/llvm/integer-remainder.js.map +1 -0
- package/dist/backend/llvm/layouts.d.ts +2 -1
- package/dist/backend/llvm/layouts.js +4 -3
- package/dist/backend/llvm/layouts.js.map +1 -1
- package/dist/backend/llvm/lazy-captures.d.ts +16 -0
- package/dist/backend/llvm/lazy-captures.js +96 -0
- package/dist/backend/llvm/lazy-captures.js.map +1 -0
- package/dist/backend/llvm/lazy-captures.test.d.ts +1 -0
- package/dist/backend/llvm/lazy-captures.test.js +78 -0
- package/dist/backend/llvm/lazy-captures.test.js.map +1 -0
- package/dist/backend/llvm/lib-abi.js +7 -0
- package/dist/backend/llvm/lib-abi.js.map +1 -1
- package/dist/backend/llvm/lib-dispatch.js +7 -22
- package/dist/backend/llvm/lib-dispatch.js.map +1 -1
- package/dist/backend/llvm/lib-filesystem.js +3 -4
- package/dist/backend/llvm/lib-filesystem.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +15 -0
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.d.ts +13 -3
- package/dist/backend/llvm/local-array-reads.js +159 -77
- package/dist/backend/llvm/local-array-reads.js.map +1 -1
- package/dist/backend/llvm/local-array-reads.test.js +105 -4
- package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.d.ts +5 -1
- package/dist/backend/llvm/local-union-storage.js +48 -6
- package/dist/backend/llvm/local-union-storage.js.map +1 -1
- package/dist/backend/llvm/local-union-storage.test.js +91 -4
- package/dist/backend/llvm/local-union-storage.test.js.map +1 -1
- package/dist/backend/llvm/loop-array-borrows.d.ts +11 -0
- package/dist/backend/llvm/loop-array-borrows.js +70 -0
- package/dist/backend/llvm/loop-array-borrows.js.map +1 -0
- package/dist/backend/llvm/loop-array-borrows.test.d.ts +1 -0
- package/dist/backend/llvm/loop-array-borrows.test.js +145 -0
- package/dist/backend/llvm/loop-array-borrows.test.js.map +1 -0
- package/dist/backend/llvm/map-read-lifetimes.d.ts +5 -0
- package/dist/backend/llvm/map-read-lifetimes.js +20 -5
- package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
- package/dist/backend/llvm/map-read-lifetimes.test.js +1 -1
- package/dist/backend/llvm/nullable-fields.d.ts +28 -0
- package/dist/backend/llvm/nullable-fields.js +45 -0
- package/dist/backend/llvm/nullable-fields.js.map +1 -0
- package/dist/backend/llvm/nullable-fields.test.d.ts +1 -0
- package/dist/backend/llvm/nullable-fields.test.js +48 -0
- package/dist/backend/llvm/nullable-fields.test.js.map +1 -0
- package/dist/backend/llvm/reference-effects.d.ts +2 -1
- package/dist/backend/llvm/reference-effects.js +7 -0
- package/dist/backend/llvm/reference-effects.js.map +1 -1
- package/dist/backend/llvm/serialized-ir-emission.test.d.ts +1 -0
- package/dist/backend/llvm/serialized-ir-emission.test.js +52 -0
- package/dist/backend/llvm/serialized-ir-emission.test.js.map +1 -0
- package/dist/backend/llvm/shapes.d.ts +73 -3
- package/dist/backend/llvm/shapes.js +281 -34
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/llvm/shapes.test.js +114 -5
- package/dist/backend/llvm/shapes.test.js.map +1 -1
- package/dist/backend/llvm/split-loops.d.ts +48 -0
- package/dist/backend/llvm/split-loops.js +183 -0
- package/dist/backend/llvm/split-loops.js.map +1 -0
- package/dist/backend/llvm/split.js +0 -7
- package/dist/backend/llvm/split.js.map +1 -1
- package/dist/backend/llvm/split.test.js +66 -2
- package/dist/backend/llvm/split.test.js.map +1 -1
- package/dist/backend/llvm/stack-callbacks.d.ts +14 -0
- package/dist/backend/llvm/stack-callbacks.js +72 -0
- package/dist/backend/llvm/stack-callbacks.js.map +1 -0
- package/dist/backend/llvm/stack-captures.d.ts +27 -0
- package/dist/backend/llvm/stack-captures.js +152 -0
- package/dist/backend/llvm/stack-captures.js.map +1 -0
- package/dist/backend/llvm/stack-unions.test.js +38 -1
- package/dist/backend/llvm/stack-unions.test.js.map +1 -1
- package/dist/backend/llvm/string-construction.test.js +1 -1
- package/dist/backend/llvm/string-key-hash.d.ts +8 -0
- package/dist/backend/llvm/string-key-hash.js +59 -0
- package/dist/backend/llvm/string-key-hash.js.map +1 -0
- package/dist/backend/llvm/string-key-hash.test.d.ts +1 -0
- package/dist/backend/llvm/string-key-hash.test.js +34 -0
- package/dist/backend/llvm/string-key-hash.test.js.map +1 -0
- package/dist/backend/llvm/string-lifetimes.test.js +48 -7
- package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
- package/dist/backend/llvm/string-slices.d.ts +20 -0
- package/dist/backend/llvm/string-slices.js +87 -0
- package/dist/backend/llvm/string-slices.js.map +1 -0
- package/dist/backend/llvm/string-slices.test.d.ts +1 -0
- package/dist/backend/llvm/string-slices.test.js +98 -0
- package/dist/backend/llvm/string-slices.test.js.map +1 -0
- package/dist/backend/llvm/switch-dispatch.js +1 -1
- package/dist/backend/llvm/switch-dispatch.js.map +1 -1
- package/dist/backend/llvm/switch-dispatch.test.js +2 -2
- package/dist/backend/llvm/switch-dispatch.test.js.map +1 -1
- package/dist/backend/llvm/walkers.d.ts +4 -14
- package/dist/backend/llvm/walkers.js +53 -311
- package/dist/backend/llvm/walkers.js.map +1 -1
- package/dist/backend/may-throw.js +4 -1
- package/dist/backend/may-throw.js.map +1 -1
- package/dist/backend/may-throw.test.js +1 -1
- package/dist/backend/native/cache-warm.d.ts +2 -1
- package/dist/backend/native/cache-warm.js.map +1 -1
- package/dist/backend/native/contracts.d.ts +5 -2
- package/dist/backend/native/contracts.js.map +1 -1
- package/dist/backend/native/executable.js +12 -4
- package/dist/backend/native/executable.js.map +1 -1
- package/dist/backend/native/runtime-inputs.d.ts +1 -1
- package/dist/backend/native/runtime-inputs.js +3 -0
- package/dist/backend/native/runtime-inputs.js.map +1 -1
- package/dist/backend/native-codegen-core.d.ts +7 -0
- package/dist/backend/native-codegen-core.js +21 -0
- package/dist/backend/native-codegen-core.js.map +1 -1
- package/dist/backend/native-codegen.d.ts +9 -0
- package/dist/backend/native-codegen.js +38 -18
- package/dist/backend/native-codegen.js.map +1 -1
- package/dist/backend/native-codegen.test.js +70 -2
- package/dist/backend/native-codegen.test.js.map +1 -1
- package/dist/backend/native-link-info-core.d.ts +4 -2
- package/dist/backend/native-link-info-core.js.map +1 -1
- package/dist/backend/native-link-info.d.ts +2 -1
- package/dist/backend/native-link-info.js.map +1 -1
- package/dist/backend/native-link-info.test.js +2 -2
- package/dist/backend/native-link-info.test.js.map +1 -1
- package/dist/backend/native-tools.d.ts +6 -2
- package/dist/backend/native-tools.js +6 -5
- package/dist/backend/native-tools.js.map +1 -1
- package/dist/backend/optimization.d.ts +25 -0
- package/dist/backend/optimization.js +21 -0
- package/dist/backend/optimization.js.map +1 -0
- package/dist/backend/optimization.test.d.ts +1 -0
- package/dist/backend/optimization.test.js +24 -0
- package/dist/backend/optimization.test.js.map +1 -0
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-core.d.ts +16 -4
- package/dist/backend/runtime-pack-core.js +31 -13
- package/dist/backend/runtime-pack-core.js.map +1 -1
- package/dist/backend/runtime-pack-native.d.ts +7 -3
- package/dist/backend/runtime-pack-native.js +0 -3
- package/dist/backend/runtime-pack-native.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +41 -4
- package/dist/backend/runtime-pack-targets.test.js.map +1 -1
- package/dist/backend/runtime-pack.d.ts +20 -2
- package/dist/backend/runtime-pack.js +23 -0
- package/dist/backend/runtime-pack.js.map +1 -1
- package/dist/backend/runtime-pack.test.js +125 -2
- package/dist/backend/runtime-pack.test.js.map +1 -1
- package/dist/backend/target-diagnostics.test.js +1 -1
- package/dist/backend/targets.d.ts +2 -1
- package/dist/backend/targets.js +1 -1
- package/dist/backend/targets.js.map +1 -1
- package/dist/cli/command.js +5 -4
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/host.d.ts +2 -1
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/usage.js +5 -2
- package/dist/cli/usage.js.map +1 -1
- package/dist/compile-types.d.ts +5 -2
- package/dist/diagnostics/diagnostic.js +10 -8
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/executable/executable-cache.d.ts +3 -2
- package/dist/executable/executable-cache.js +8 -4
- package/dist/executable/executable-cache.js.map +1 -1
- package/dist/executable/executable-cache.test.js +35 -0
- package/dist/executable/executable-cache.test.js.map +1 -1
- package/dist/frontend/fork-target.d.ts +5 -0
- package/dist/frontend/fork-target.js +40 -8
- package/dist/frontend/fork-target.js.map +1 -1
- package/dist/frontend/fork-target.test.d.ts +1 -0
- package/dist/frontend/fork-target.test.js +59 -0
- package/dist/frontend/fork-target.test.js.map +1 -0
- package/dist/frontend/lowering/abstract-equality.js +5 -49
- package/dist/frontend/lowering/abstract-equality.js.map +1 -1
- package/dist/frontend/lowering/array-values.d.ts +23 -0
- package/dist/frontend/lowering/array-values.js +129 -0
- package/dist/frontend/lowering/array-values.js.map +1 -1
- package/dist/frontend/lowering/binding-analysis.js +2 -2
- package/dist/frontend/lowering/binding-analysis.js.map +1 -1
- package/dist/frontend/lowering/builtins/atomics.d.ts +2 -11
- package/dist/frontend/lowering/builtins/atomics.js +64 -28
- package/dist/frontend/lowering/builtins/atomics.js.map +1 -1
- package/dist/frontend/lowering/builtins/errors.js +17 -2
- package/dist/frontend/lowering/builtins/errors.js.map +1 -1
- package/dist/frontend/lowering/builtins/filesystem-calls.d.ts +3 -0
- package/dist/frontend/lowering/builtins/filesystem-calls.js +51 -4
- package/dist/frontend/lowering/builtins/filesystem-calls.js.map +1 -1
- package/dist/frontend/lowering/builtins/module-calls.js +6 -1
- package/dist/frontend/lowering/builtins/module-calls.js.map +1 -1
- package/dist/frontend/lowering/builtins/process.d.ts +6 -17
- package/dist/frontend/lowering/builtins/process.js +56 -29
- package/dist/frontend/lowering/builtins/process.js.map +1 -1
- package/dist/frontend/lowering/builtins/workers.d.ts +5 -0
- package/dist/frontend/lowering/builtins/workers.js +65 -0
- package/dist/frontend/lowering/builtins/workers.js.map +1 -0
- package/dist/frontend/lowering/caught-value-dispatch.d.ts +11 -0
- package/dist/frontend/lowering/caught-value-dispatch.js +87 -0
- package/dist/frontend/lowering/caught-value-dispatch.js.map +1 -0
- package/dist/frontend/lowering/class-assertions.d.ts +11 -0
- package/dist/frontend/lowering/class-assertions.js +104 -0
- package/dist/frontend/lowering/class-assertions.js.map +1 -0
- package/dist/frontend/lowering/class-callbacks.d.ts +3 -3
- package/dist/frontend/lowering/class-callbacks.js +18 -5
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +1 -0
- package/dist/frontend/lowering/class-dynamic-dispatch.js +4 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-method-mutations.d.ts +5 -0
- package/dist/frontend/lowering/class-method-mutations.js +472 -0
- package/dist/frontend/lowering/class-method-mutations.js.map +1 -0
- package/dist/frontend/lowering/class-method-values.js +25 -5
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-protocols.d.ts +17 -0
- package/dist/frontend/lowering/class-protocols.js +126 -0
- package/dist/frontend/lowering/class-protocols.js.map +1 -0
- package/dist/frontend/lowering/class-prototypes.js +8 -2
- package/dist/frontend/lowering/class-prototypes.js.map +1 -1
- package/dist/frontend/lowering/class-unions.d.ts +16 -0
- package/dist/frontend/lowering/class-unions.js +127 -0
- package/dist/frontend/lowering/class-unions.js.map +1 -0
- package/dist/frontend/lowering/coercions/builders.js +8 -1
- package/dist/frontend/lowering/coercions/builders.js.map +1 -1
- package/dist/frontend/lowering/coercions/function-adapters.js +2 -0
- package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
- package/dist/frontend/lowering/coercions/identity.d.ts +7 -0
- package/dist/frontend/lowering/coercions/identity.js +26 -0
- package/dist/frontend/lowering/coercions/identity.js.map +1 -0
- package/dist/frontend/lowering/coercions/structural.js +9 -6
- package/dist/frontend/lowering/coercions/structural.js.map +1 -1
- package/dist/frontend/lowering/collection-narrowing.d.ts +6 -0
- package/dist/frontend/lowering/collection-narrowing.js +31 -0
- package/dist/frontend/lowering/collection-narrowing.js.map +1 -0
- package/dist/frontend/lowering/containers/array-callback-call.d.ts +7 -0
- package/dist/frontend/lowering/containers/array-callback-call.js +52 -0
- package/dist/frontend/lowering/containers/array-callback-call.js.map +1 -0
- package/dist/frontend/lowering/containers/array-callbacks.d.ts +8 -12
- package/dist/frontend/lowering/containers/array-callbacks.js +415 -136
- package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
- package/dist/frontend/lowering/containers/array-concat.d.ts +7 -0
- package/dist/frontend/lowering/containers/array-concat.js +130 -0
- package/dist/frontend/lowering/containers/array-concat.js.map +1 -0
- package/dist/frontend/lowering/containers/array-construction.d.ts +2 -0
- package/dist/frontend/lowering/containers/array-construction.js +95 -3
- package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
- package/dist/frontend/lowering/containers/array-methods.d.ts +1 -11
- package/dist/frontend/lowering/containers/array-methods.js +43 -132
- package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +12 -34
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/collection-methods.js +10 -1
- package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
- package/dist/frontend/lowering/containers/indexed-objects.js +56 -70
- package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +22 -3
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/cycle-initialization.d.ts +41 -0
- package/dist/frontend/lowering/cycle-initialization.js +277 -0
- package/dist/frontend/lowering/cycle-initialization.js.map +1 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js +4 -0
- package/dist/frontend/lowering/expressions/evaluation-safety.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +140 -38
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/union-field-write.d.ts +4 -4
- package/dist/frontend/lowering/expressions/union-field-write.js +54 -44
- package/dist/frontend/lowering/expressions/union-field-write.js.map +1 -1
- package/dist/frontend/lowering/generic-functions.d.ts +2 -5
- package/dist/frontend/lowering/generic-functions.js +13 -10
- package/dist/frontend/lowering/generic-functions.js.map +1 -1
- package/dist/frontend/lowering/instantiation-path.d.ts +8 -0
- package/dist/frontend/lowering/instantiation-path.js +10 -0
- package/dist/frontend/lowering/instantiation-path.js.map +1 -0
- package/dist/frontend/lowering/instantiation-path.test.d.ts +1 -0
- package/dist/frontend/lowering/instantiation-path.test.js +23 -0
- package/dist/frontend/lowering/instantiation-path.test.js.map +1 -0
- package/dist/frontend/lowering/iterator-adapters.d.ts +20 -0
- package/dist/frontend/lowering/iterator-adapters.js +605 -0
- package/dist/frontend/lowering/iterator-adapters.js.map +1 -0
- package/dist/frontend/lowering/lower-array-sort.d.ts +1 -1
- package/dist/frontend/lowering/lower-array-sort.js +44 -5
- package/dist/frontend/lowering/lower-array-sort.js.map +1 -1
- package/dist/frontend/lowering/lower-array-sort.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-array-sort.test.js +31 -0
- package/dist/frontend/lowering/lower-array-sort.test.js.map +1 -0
- package/dist/frontend/lowering/lower-assert.js +7 -0
- package/dist/frontend/lowering/lower-assert.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.js +12 -7
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +3 -3
- package/dist/frontend/lowering/lower-calls.js +198 -124
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +7 -0
- package/dist/frontend/lowering/lower-classes.js +166 -96
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js +1 -1
- package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +9 -2
- package/dist/frontend/lowering/lower-exprs.js +421 -167
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-functions.d.ts +1 -1
- package/dist/frontend/lowering/lower-functions.js +33 -20
- package/dist/frontend/lowering/lower-functions.js.map +1 -1
- package/dist/frontend/lowering/lower-inspect.js +26 -2
- package/dist/frontend/lowering/lower-inspect.js.map +1 -1
- package/dist/frontend/lowering/lower-island.js +1 -1
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +1 -0
- package/dist/frontend/lowering/lower-modules.js +54 -11
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +59 -69
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +1 -1
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +50 -1
- package/dist/frontend/lowering/lowerer.js +333 -26
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/native-ffi.js +3 -1
- package/dist/frontend/lowering/native-ffi.js.map +1 -1
- package/dist/frontend/lowering/runtime-optional-provenance.d.ts +15 -0
- package/dist/frontend/lowering/runtime-optional-provenance.js +40 -0
- package/dist/frontend/lowering/runtime-optional-provenance.js.map +1 -0
- package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
- package/dist/frontend/lowering/split-consumers.d.ts +7 -0
- package/dist/frontend/lowering/split-consumers.js +64 -0
- package/dist/frontend/lowering/split-consumers.js.map +1 -0
- package/dist/frontend/lowering/strict-equality.d.ts +9 -0
- package/dist/frontend/lowering/strict-equality.js +116 -0
- package/dist/frontend/lowering/strict-equality.js.map +1 -0
- package/dist/frontend/lowering/surfaces.d.ts +3 -4
- package/dist/frontend/lowering/surfaces.js +38 -7
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/union-comparison.d.ts +5 -0
- package/dist/frontend/lowering/union-comparison.js +47 -0
- package/dist/frontend/lowering/union-comparison.js.map +1 -0
- package/dist/frontend/module-source-candidates.d.ts +10 -0
- package/dist/frontend/module-source-candidates.js +40 -0
- package/dist/frontend/module-source-candidates.js.map +1 -0
- package/dist/frontend/module-source-candidates.test.d.ts +1 -0
- package/dist/frontend/module-source-candidates.test.js +53 -0
- package/dist/frontend/module-source-candidates.test.js.map +1 -0
- package/dist/frontend/pipeline.js +2 -2
- package/dist/frontend/pipeline.js.map +1 -1
- package/dist/frontend/program-analysis.d.ts +2 -0
- package/dist/frontend/program-analysis.js +2 -0
- package/dist/frontend/program-analysis.js.map +1 -1
- package/dist/frontend/program.d.ts +38 -2
- package/dist/frontend/program.js +457 -88
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +59 -6
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/ts7/ast-node.js +6 -8
- package/dist/frontend/ts7/ast-node.js.map +1 -1
- package/dist/frontend/ts7/ast-node.test.js +42 -1
- package/dist/frontend/ts7/ast-node.test.js.map +1 -1
- package/dist/frontend/ts7/ast-wire.d.ts +4 -0
- package/dist/frontend/ts7/ast-wire.js +28 -0
- package/dist/frontend/ts7/ast-wire.js.map +1 -1
- package/dist/frontend/ts7/ast.d.ts +2 -1
- package/dist/frontend/ts7/ast.js +8 -4
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/checker.d.ts +8 -2
- package/dist/frontend/ts7/checker.js +41 -5
- package/dist/frontend/ts7/checker.js.map +1 -1
- package/dist/frontend/ts7/checker.test.js +42 -0
- package/dist/frontend/ts7/checker.test.js.map +1 -1
- package/dist/frontend/ts7/program-host.d.ts +7 -0
- package/dist/frontend/ts7/program-host.js +26 -2
- package/dist/frontend/ts7/program-host.js.map +1 -1
- package/dist/frontend/ts7/program-host.test.js +29 -0
- package/dist/frontend/ts7/program-host.test.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +4 -1
- package/dist/frontend/type-mapper.js +133 -16
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +15 -3
- package/dist/frontend/type-mapper.test.js.map +1 -1
- package/dist/frontend/worker-target.d.ts +6 -0
- package/dist/frontend/worker-target.js +108 -0
- package/dist/frontend/worker-target.js.map +1 -0
- package/dist/frontend/worker-target.test.d.ts +1 -0
- package/dist/frontend/worker-target.test.js +71 -0
- package/dist/frontend/worker-target.test.js.map +1 -0
- package/dist/index.js +47 -14
- package/dist/index.js.map +1 -1
- package/dist/ir/analysis.d.ts +3 -1
- package/dist/ir/analysis.js +4 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/builtin-effects.js +12 -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 +15 -15
- package/dist/ir/builtin-signatures.js.map +1 -1
- package/dist/ir/constant-tables.test.js +1 -1
- package/dist/ir/initializer-bindings.test.js +1 -1
- package/dist/ir/ir.d.ts +85 -23
- package/dist/ir/ir.js +20 -6
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/numeric-call-specialization.d.ts +8 -0
- package/dist/ir/numeric-call-specialization.js +198 -0
- package/dist/ir/numeric-call-specialization.js.map +1 -0
- package/dist/ir/runtime-features.js +4 -3
- 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 +8 -0
- package/dist/ir/traverse.js.map +1 -1
- package/dist/ir/validate.js +207 -43
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +356 -8
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/library/fence-eval.test.js +1 -1
- package/dist/library/int-infer.test.js +4 -4
- package/dist/library/ir-policy.js +2 -0
- package/dist/library/ir-policy.js.map +1 -1
- package/dist/library/library-cache.test.js +5 -5
- package/dist/library/library-profile.d.ts +2 -1
- package/dist/library/library-profile.js +4 -3
- package/dist/library/library-profile.js.map +1 -1
- package/dist/native/compiler.js +24 -11
- package/dist/native/compiler.js.map +1 -1
- package/dist/native/compiler.test.js +7 -3
- package/dist/native/compiler.test.js.map +1 -1
- package/dist/native/library.d.ts +2 -1
- package/dist/native/library.js.map +1 -1
- package/dist/native/prepare.js +29 -3
- package/dist/native/prepare.js.map +1 -1
- package/dist/native/sanitizer.d.ts +3 -2
- package/dist/native/sanitizer.js +3 -13
- package/dist/native/sanitizer.js.map +1 -1
- package/dist/native/sanitizer.test.js +1 -0
- package/dist/native/sanitizer.test.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +37 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { emitLlvmLayouts } from "./layouts.js";
|
|
2
|
+
import { inlineAllocSupported } from "./alloc.js";
|
|
2
3
|
import { llvmBytes as llBytes } from "../literals.js";
|
|
3
4
|
import { InternalCompilerError } from "../../errors.js";
|
|
4
5
|
/** Lower typed IR to LLVM, using the runtime's C ABI.
|
|
@@ -19,15 +20,18 @@ import { InternalCompilerError } from "../../errors.js";
|
|
|
19
20
|
* compressed source tables that the runtime inflates on demand.
|
|
20
21
|
*/
|
|
21
22
|
import { deflateRawSync } from "node:zlib";
|
|
22
|
-
import { endsWithJump, matchStringSelfConcat, streamTypedRefEligible } from "../../ir/analysis.js";
|
|
23
|
+
import { endsWithJump, matchStringSelfConcat, streamTypedRefEligible, undefinedArmTag, } from "../../ir/analysis.js";
|
|
23
24
|
import { emitLibraryIdentityLines } from "../library-identity-markers.js";
|
|
24
|
-
import { CAUGHT, ffiCallbackType, isDynTypedRefType, isFfiContextParam, isRefCounted, isUnitType, POINTER_KINDS, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, typeKey, VOID, } from "../../ir/ir.js";
|
|
25
|
+
import { CAUGHT, ffiCallbackType, isDynTypedRefType, isFfiContextParam, isRefCounted, isUnitType, POINTER_KINDS, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, typeEquals, typeKey, STRING, VOID, } from "../../ir/ir.js";
|
|
25
26
|
import { moduleRuntimeFeatures, moduleEmbedsBuiltin, moduleEmbedsCompressedNpm, NPM_COMPRESS_MIN, } from "../../ir/runtime-features.js";
|
|
26
27
|
import { matchIntegerArrayForLoop, matchIntegerBytesForLoop, matchIntegerCountedForLoop, } from "../../ir/integer-loops.js";
|
|
27
28
|
import { emitCountedLoopLimit } from "./counted-loops.js";
|
|
28
29
|
import { emitLiteralSwitch } from "./switch-dispatch.js";
|
|
29
30
|
import { findBytesBounds } from "./bytes-bounds.js";
|
|
31
|
+
import { emitByteWindowGuard, findInitializedByteLoopBindings, matchByteWindow, } from "./byte-windows.js";
|
|
32
|
+
import { canDeferSplitPiece, emitSplitSpanNext, materializeSplitPiece, emitSplitCursor, emitSplitNext, emitSplitScratch, emitSplitSnapshot, findPrivateSplitLocals, loadSplitSnapshot, storeSplitSnapshot, stringSplit, } from "./split-loops.js";
|
|
30
33
|
import { scalarizeNumericRecords } from "../../ir/scalar-records.js";
|
|
34
|
+
import { specializeNumericCalls } from "../../ir/numeric-call-specialization.js";
|
|
31
35
|
import { everyStmtList } from "../../ir/traverse.js";
|
|
32
36
|
import { analyzeIntegerRanges } from "../../ir/integer-ranges.js";
|
|
33
37
|
import { findIntegerViews } from "./integer-views.js";
|
|
@@ -41,13 +45,21 @@ import { analyzeCallLifetimes } from "./call-lifetimes.js";
|
|
|
41
45
|
import { canStackUnion, emitStackUnion, findLocalStackUnions } from "./stack-unions.js";
|
|
42
46
|
import { allocateLocalUnion, findLocalUnionStorage, storeLocalUnion, } from "./local-union-storage.js";
|
|
43
47
|
import { BlockBuilder } from "./blocks.js";
|
|
44
|
-
import {
|
|
48
|
+
import { NullableRefFields } from "./nullable-fields.js";
|
|
49
|
+
import { emitLocalArrayRead, emitProjectedArrayRead, emitBorrowedArrayRead, findLocalArrayReads, findCallArrayReads, OptionalArrayReads, } from "./local-array-reads.js";
|
|
50
|
+
import { emitDenseArrayGet, emitDenseArraySet } from "./dense-array-access.js";
|
|
45
51
|
import { emitStackMapRead, findMapReadLifetimes, matchMapRead, } from "./map-read-lifetimes.js";
|
|
46
52
|
import { emitBorrowedFieldSequence } from "./borrowed-receivers.js";
|
|
47
53
|
import { emitBorrowedInput } from "./borrowed-inputs.js";
|
|
48
54
|
import { ReferenceEffects } from "./reference-effects.js";
|
|
49
55
|
import { LlvmDebugInfo } from "./debug-info.js";
|
|
50
|
-
import {
|
|
56
|
+
import { StackCallbacks } from "./stack-callbacks.js";
|
|
57
|
+
import { findLoopArrayBorrows } from "./loop-array-borrows.js";
|
|
58
|
+
import { AmbientReceiverReaders, findConstantCallbacks } from "./constant-callbacks.js";
|
|
59
|
+
import { findScalarStringSlices, emitStringSliceSnapshot, } from "./string-slices.js";
|
|
60
|
+
import { LazyCaptures } from "./lazy-captures.js";
|
|
61
|
+
import { StackCaptures } from "./stack-captures.js";
|
|
62
|
+
import { ABSENT_FIELD_PAYLOAD, emitFieldAbsentTest, f64Lit, ffiNativeTypeLl, ffiNativeParamLl, ffiNativeReturnLl, llvmCommentText, } from "./common.js";
|
|
51
63
|
import { ffiExtendsNarrowIntegers } from "../targets.js";
|
|
52
64
|
import { emitLiteralExpr, emitOperatorExpr, emitStringExpr, emitContainerExpr, emitRecordExpr, } from "./expr-primitives.js";
|
|
53
65
|
import { emitControlExpr } from "./expr-control.js";
|
|
@@ -67,11 +79,12 @@ import { emitChildProcessLibCall, emitAsyncContextLibCall, emitProcessLibCall, e
|
|
|
67
79
|
import { emitStreamLibCall } from "./lib-stream.js";
|
|
68
80
|
import { emitNetworkHttpLibCall } from "./lib-network.js";
|
|
69
81
|
import { emitAssertInspectLibCall, emitIoLibCall, emitGenericLibCall, emitLibCall, } from "./lib-dispatch.js";
|
|
70
|
-
import { buildClassGraph, classEnvironmentIndex, classFieldIndex, classStructSym, } from "./classes.js";
|
|
82
|
+
import { buildClassGraph, classMembershipIntervals, emitClassMembershipHelper, classEnvironmentIndex, classFieldIndex, classStructSym, } from "./classes.js";
|
|
71
83
|
import { LlDyn } from "./dyn.js";
|
|
72
84
|
import { LlvmUnsupportedError } from "./unsupported.js";
|
|
73
85
|
import { LlWalkers } from "./walkers.js";
|
|
74
|
-
import {
|
|
86
|
+
import { literalCapWord } from "./string-key-hash.js";
|
|
87
|
+
import { arrNewCall, boxAccess, boxNewCall, boxReleaseSym, computeTraced, elemAccess, emitInlineRcHelpers, FN_ATTRS, inlineRcDecls, llFieldType, releaseSym, retainSym, traceArg, typedRefConstructor, vAdapters, } from "./shapes.js";
|
|
75
88
|
export { LlvmUnsupportedError } from "./unsupported.js";
|
|
76
89
|
function ffiCallbackDummyLl(callback) {
|
|
77
90
|
switch (callback.returns) {
|
|
@@ -100,11 +113,15 @@ function ffiCallbackDummyLl(callback) {
|
|
|
100
113
|
}
|
|
101
114
|
export function emitLlvmModule(mod, options = {}) {
|
|
102
115
|
// Keep source storage intact for debugger inspection in dev builds.
|
|
103
|
-
return new LlEmitter(options.debugSources === undefined
|
|
116
|
+
return new LlEmitter(options.debugSources === undefined
|
|
117
|
+
? scalarizeNumericRecords(specializeNumericCalls(mod, mangleFunction))
|
|
118
|
+
: mod, options).emit();
|
|
104
119
|
}
|
|
105
120
|
/** Keep large translation units below the host's single-string limit. */
|
|
106
121
|
export function emitLlvmModuleSource(mod, options = {}) {
|
|
107
|
-
const parts = new LlEmitter(options.debugSources === undefined
|
|
122
|
+
const parts = new LlEmitter(options.debugSources === undefined
|
|
123
|
+
? scalarizeNumericRecords(specializeNumericCalls(mod, mangleFunction))
|
|
124
|
+
: mod, options).emitParts();
|
|
108
125
|
const length = parts.reduce((total, part) => total + part.length, Math.max(0, parts.length - 1));
|
|
109
126
|
if (length <= 256 * 1024 * 1024)
|
|
110
127
|
return parts.join("\n");
|
|
@@ -116,6 +133,9 @@ function llStrBytes(text) {
|
|
|
116
133
|
export class LlEmitter {
|
|
117
134
|
mod;
|
|
118
135
|
localArrayReads = new Map();
|
|
136
|
+
loopArrayBorrows = new Set();
|
|
137
|
+
scalarStringSlices = new Map();
|
|
138
|
+
stringSlices = new Map();
|
|
119
139
|
localStackUnions = new Map();
|
|
120
140
|
localUnionStorageProofs = new Map();
|
|
121
141
|
localUnionStorage = new Map();
|
|
@@ -167,6 +187,8 @@ export class LlEmitter {
|
|
|
167
187
|
* immortal (rc == SIZE_MAX) static per (union, unit tag).
|
|
168
188
|
* RC entry points and the collector skip immortals. */
|
|
169
189
|
unitInstances = new Map();
|
|
190
|
+
/** unionId → symbol of the union's immortal ABSENT field-slot instance. */
|
|
191
|
+
absentInstances = new Map();
|
|
170
192
|
immortalValues = new Set();
|
|
171
193
|
/** Regex literal templates: "<flags>/<pattern>" → { symbol, interned
|
|
172
194
|
* source/flags literal refs } — one immortal ScrRegex template per distinct
|
|
@@ -200,13 +222,31 @@ export class LlEmitter {
|
|
|
200
222
|
needsBadTag = false;
|
|
201
223
|
needsBadKey = false;
|
|
202
224
|
needsRetainBox = false;
|
|
225
|
+
/** Inline RC fast-path helpers requested by call sites (shapes.ts); null
|
|
226
|
+
* when inline RC is off. */
|
|
227
|
+
rcHelpers;
|
|
203
228
|
fnByName = new Map();
|
|
229
|
+
/** Emitted LLVM text of every IR function body, by function name: the
|
|
230
|
+
* fiberless-async analysis scans it for suspension entry points. */
|
|
231
|
+
fnDefText = new Map();
|
|
204
232
|
referenceEffects;
|
|
205
233
|
optionalArrayReads;
|
|
206
234
|
callArrayReads = new Map();
|
|
207
235
|
mapReadLifetimes = { locals: new Map(), arguments: new Map() };
|
|
208
236
|
callLifetimes;
|
|
237
|
+
constantCallbacks;
|
|
238
|
+
currentConstantCallbacks = new Map();
|
|
239
|
+
stackCallbacks;
|
|
240
|
+
receiverReaders;
|
|
241
|
+
stackCaptures;
|
|
242
|
+
lazyCaptures;
|
|
243
|
+
/** Unchanged captured parameters of the current function: each maps to
|
|
244
|
+
* the slot caching its box, NULL until the first closure needs it. */
|
|
245
|
+
lazyCaptureBoxes = new Map();
|
|
246
|
+
lazyStackBoxes = new Map();
|
|
209
247
|
borrowedParameters = new Set();
|
|
248
|
+
/** Parameters proven projection-only: callers may pass stack boxes. */
|
|
249
|
+
projectedParameters = new Set();
|
|
210
250
|
stableCallBindings = new Set();
|
|
211
251
|
/** Manifest-bound native imports, used by ffiCall emission. */
|
|
212
252
|
ffiByName = new Map();
|
|
@@ -246,6 +286,7 @@ export class LlEmitter {
|
|
|
246
286
|
liveDynUnionRefAdapters = new Map();
|
|
247
287
|
dynPromiseAdapters = new Map();
|
|
248
288
|
unionsById = new Map();
|
|
289
|
+
nullableFields;
|
|
249
290
|
recordsById = new Map();
|
|
250
291
|
recordCloneShapes = new Set();
|
|
251
292
|
tracedShapes;
|
|
@@ -288,6 +329,10 @@ export class LlEmitter {
|
|
|
288
329
|
* for blocks and switches. */
|
|
289
330
|
jumpTargets = [];
|
|
290
331
|
currentLocals = new Map();
|
|
332
|
+
privateSplitLocals = new Map();
|
|
333
|
+
splitSpans = new Map();
|
|
334
|
+
storedSplits = new Map();
|
|
335
|
+
streamingSplitsEnabled = false;
|
|
291
336
|
initializerBindings;
|
|
292
337
|
numericLocals = new Map();
|
|
293
338
|
captureIds = new Set();
|
|
@@ -295,6 +340,8 @@ export class LlEmitter {
|
|
|
295
340
|
integerLoopBindings = new Map();
|
|
296
341
|
countedLoopsEnabled = false;
|
|
297
342
|
integerRanges = new Map();
|
|
343
|
+
byteWindowsEnabled = true;
|
|
344
|
+
byteWindowEntries = new Map();
|
|
298
345
|
bytesBounds = new Set();
|
|
299
346
|
integerViews = new Map();
|
|
300
347
|
/** Enclosing try-with-FINALLY regions, innermost last: a `return`
|
|
@@ -345,6 +392,7 @@ export class LlEmitter {
|
|
|
345
392
|
this.wasi = options.wasi === true;
|
|
346
393
|
this.emitLibraryIdentity = options.emitLibraryIdentity !== false;
|
|
347
394
|
this.runtimeAbiMarker = options.runtimeAbiMarker === true;
|
|
395
|
+
this.rcHelpers = options.inlineRc === true ? new Set() : null;
|
|
348
396
|
// ScrCycHdr.color stays 12 bytes behind a wasm32 object and 16 bytes
|
|
349
397
|
// behind a 64-bit object. The wasm32 header pads before color so its
|
|
350
398
|
// payload remains double-aligned without changing this ABI offset.
|
|
@@ -359,7 +407,10 @@ export class LlEmitter {
|
|
|
359
407
|
}
|
|
360
408
|
const mt = computeMayThrow(mod);
|
|
361
409
|
this.mayThrow = mt.fns;
|
|
362
|
-
|
|
410
|
+
if (mod.workers)
|
|
411
|
+
for (const fn of mod.functions)
|
|
412
|
+
this.mayThrow.add(fn.name);
|
|
413
|
+
this.indirectMayThrow = mt.indirect || mod.workers === true;
|
|
363
414
|
for (const cls of mod.classes ?? []) {
|
|
364
415
|
for (const m of cls.methods ?? []) {
|
|
365
416
|
if (this.mayThrow.has(`%${cls.name}.${m}`))
|
|
@@ -368,9 +419,15 @@ export class LlEmitter {
|
|
|
368
419
|
}
|
|
369
420
|
for (const u of mod.unions ?? [])
|
|
370
421
|
this.unionsById.set(u.id, u);
|
|
422
|
+
this.nullableFields = new NullableRefFields(mod.classes ?? [], this.unionsById);
|
|
371
423
|
this.optionalArrayReads = new OptionalArrayReads(this.fnByName, this.unionsById);
|
|
372
424
|
this.referenceEffects = new ReferenceEffects(this.fnByName, (call) => this.optionalArrayReads.get(call) !== null);
|
|
373
|
-
this.callLifetimes = analyzeCallLifetimes(this.fnByName);
|
|
425
|
+
this.callLifetimes = analyzeCallLifetimes(this.fnByName, (className, field) => this.nullableFields.get(className, field) !== null);
|
|
426
|
+
this.constantCallbacks = findConstantCallbacks(mod, this.callLifetimes);
|
|
427
|
+
this.stackCallbacks = new StackCallbacks(this.fnByName);
|
|
428
|
+
this.receiverReaders = new AmbientReceiverReaders(this.fnByName);
|
|
429
|
+
this.stackCaptures = new StackCaptures(this.fnByName, this.callLifetimes, this.stackCallbacks);
|
|
430
|
+
this.lazyCaptures = new LazyCaptures(this.fnByName);
|
|
374
431
|
for (const r of mod.records ?? [])
|
|
375
432
|
this.recordsById.set(r.id, r);
|
|
376
433
|
const traced = computeTraced(mod);
|
|
@@ -383,6 +440,17 @@ export class LlEmitter {
|
|
|
383
440
|
tracedUnions: this.tracedUnions,
|
|
384
441
|
recordsById: this.recordsById,
|
|
385
442
|
recordCloneShapes: this.recordCloneShapes,
|
|
443
|
+
objectAudit: options.objectAudit !== false,
|
|
444
|
+
inlineAlloc: inlineAllocSupported({
|
|
445
|
+
pointerBits: options.pointerBits,
|
|
446
|
+
wasi: options.wasi,
|
|
447
|
+
targetTriple: options.targetTriple,
|
|
448
|
+
// Worker programs (SCR_WORKERS) make scr_cyc_live and
|
|
449
|
+
// scr_weak_dispose_hook thread-local and compile the allocator out,
|
|
450
|
+
// exactly like thread-instanced libraries.
|
|
451
|
+
threadInstances: mod.lib?.threadInstances === true || mod.workers === true,
|
|
452
|
+
}),
|
|
453
|
+
rcHelpers: this.rcHelpers,
|
|
386
454
|
unionsById: this.unionsById,
|
|
387
455
|
declare: (decl) => this.declare(decl),
|
|
388
456
|
needOom: () => this.needOom(),
|
|
@@ -390,14 +458,16 @@ export class LlEmitter {
|
|
|
390
458
|
internLiteral: (text) => this.internLiteral(text),
|
|
391
459
|
cstr: (text) => this.cstr(text),
|
|
392
460
|
unitInstanceRef: (unionId, tag) => this.unitInstanceRef(unionId, tag),
|
|
461
|
+
absentInstanceRef: (unionId) => this.absentInstanceRef(unionId),
|
|
393
462
|
liveDynRefAdapter: (type) => this.liveDynRefAdapter(type),
|
|
394
463
|
liveDynUnionRefAdapter: (type) => this.liveDynUnionRefAdapter(type),
|
|
395
464
|
dynPromiseAdapter: (type) => this.dynPromiseAdapter(type),
|
|
396
465
|
isErrorClass: (name) => this.classMeta.get(name)?.root.def.name === "%Error",
|
|
397
466
|
classSubtypes: (name) => {
|
|
398
467
|
const target = this.classMetaOf(name);
|
|
468
|
+
const intervals = classMembershipIntervals(this.classMeta, target.def.name);
|
|
399
469
|
return [...this.classMeta.values()]
|
|
400
|
-
.filter((meta) =>
|
|
470
|
+
.filter((meta) => intervals.some((range) => range.pre <= meta.pre && meta.pre <= range.post))
|
|
401
471
|
.map((meta) => meta.def.name);
|
|
402
472
|
},
|
|
403
473
|
};
|
|
@@ -507,7 +577,7 @@ export class LlEmitter {
|
|
|
507
577
|
if (adapter.global !== null)
|
|
508
578
|
globals.push(`@${adapter.global} = internal thread_local global ptr null`);
|
|
509
579
|
if (adapter.table !== null) {
|
|
510
|
-
globals.push(`@${adapter.table} = internal global %ScrFfiTable zeroinitializer`);
|
|
580
|
+
globals.push(`@${adapter.table} = internal ${this.mod.workers ? "thread_local " : ""}global %ScrFfiTable zeroinitializer`);
|
|
511
581
|
}
|
|
512
582
|
const params = cb.params.flatMap((param, i) => {
|
|
513
583
|
if (isFfiContextParam(param))
|
|
@@ -765,8 +835,11 @@ export class LlEmitter {
|
|
|
765
835
|
// Function bodies first (the literal/unit/fn-value tables fill as they
|
|
766
836
|
// emit), then the file assembles around them — the runtime ABI’s order.
|
|
767
837
|
const fnDefs = [];
|
|
768
|
-
for (const fn of this.mod.functions)
|
|
769
|
-
|
|
838
|
+
for (const fn of this.mod.functions) {
|
|
839
|
+
const def = this.emitFunction(fn);
|
|
840
|
+
fnDefs.push(def);
|
|
841
|
+
this.fnDefText.set(fn.name, def);
|
|
842
|
+
}
|
|
770
843
|
const errorViews = [];
|
|
771
844
|
for (const property of ["name", "message"])
|
|
772
845
|
if (this.mod.functions.some((fn) => fn.name === `%error.${property}.read`)) {
|
|
@@ -785,7 +858,7 @@ export class LlEmitter {
|
|
|
785
858
|
this.declare(`declare void @scr_error_register_dyn(ptr, ptr)`);
|
|
786
859
|
errorViews.push(` call void @scr_error_register_dyn(ptr @${mangleVtInstance(meta.def.name)}, ptr @${name})`);
|
|
787
860
|
}
|
|
788
|
-
const layouts = emitLlvmLayouts(this.shapeHost, this.mod, this.classMeta, this.classObjs, this.fnByName, (t) => this.llType(t));
|
|
861
|
+
const layouts = emitLlvmLayouts(this.shapeHost, this.mod, this.classMeta, this.classObjs, this.fnByName, (t) => this.llType(t), this.nullableFields);
|
|
789
862
|
const shapes = layouts.records;
|
|
790
863
|
const classShapes = layouts.classes;
|
|
791
864
|
const classObjDefs = layouts.classObjects;
|
|
@@ -953,7 +1026,7 @@ export class LlEmitter {
|
|
|
953
1026
|
// with neither keep the atexit path, so their listener timing is
|
|
954
1027
|
// unchanged.
|
|
955
1028
|
const hasRefGlobals = globals.some((g) => isRefCounted(g.type)) || fnValueProps.length > 0;
|
|
956
|
-
const inlineExitListeners = usesEvents && (hasRefGlobals || this.ffiHasRetainedCallback);
|
|
1029
|
+
const inlineExitListeners = usesEvents && (this.mod.workers || hasRefGlobals || this.ffiHasRetainedCallback);
|
|
957
1030
|
this.declare(`declare i32 @scr_exit_code_hint_get()`);
|
|
958
1031
|
if (inlineExitListeners) {
|
|
959
1032
|
this.declare(`declare void @scr_run_exit_listeners(double)`);
|
|
@@ -1081,7 +1154,10 @@ export class LlEmitter {
|
|
|
1081
1154
|
}
|
|
1082
1155
|
// Helpers assemble BEFORE the declaration table flushes (they add
|
|
1083
1156
|
// write/abort declarations).
|
|
1084
|
-
const helpers =
|
|
1157
|
+
const helpers = [
|
|
1158
|
+
...this.helperDefs(),
|
|
1159
|
+
...emitClassMembershipHelper(this.classMeta, this.sizeType),
|
|
1160
|
+
];
|
|
1085
1161
|
if (this.constantNumericTables.size > 0)
|
|
1086
1162
|
this.declare(`declare double @scr_arr_get_number(ptr, double)`);
|
|
1087
1163
|
const out = [
|
|
@@ -1097,13 +1173,19 @@ export class LlEmitter {
|
|
|
1097
1173
|
`%ScrLogArg = type { i32, i64 }`,
|
|
1098
1174
|
`%ScrVt = type { ${this.sizeType}, ${this.sizeType}, ptr }`,
|
|
1099
1175
|
`%ScrUnion = type { ${this.sizeType}, i32, ptr, ptr, ptr, i64 }`,
|
|
1100
|
-
`%ScrClosure = type { ${this.sizeType}, ptr, ${this.sizeType}, ptr, i32 }`,
|
|
1176
|
+
`%ScrClosure = type { ${this.sizeType}, ptr, ${this.sizeType}, ptr, i32, i32, ptr }`,
|
|
1101
1177
|
`%ScrFfiTable = type { ptr, ${this.sizeType}, ${this.sizeType}, ptr, i8, ptr, ptr, ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, ptr, ptr }`,
|
|
1102
|
-
`%ScrRegex = type { ${this.sizeType}, ptr, ptr, ptr, double, ptr }`,
|
|
1178
|
+
`%ScrRegex = type { ${this.sizeType}, ptr, ptr, ptr, double, ptr, ptr }`,
|
|
1103
1179
|
// ScrArr mirrors scr_runtime.h field-for-field. Live dynamic stream
|
|
1104
1180
|
// commits swap its mutable dense, sparse, presence, and property
|
|
1105
1181
|
// storage while preserving the target object's identity.
|
|
1106
1182
|
`%ScrArr = type { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, ptr, ptr, ptr, ptr, ${this.sizeType}, ${this.sizeType}, ptr, ${this.sizeType}, ${this.sizeType}, ptr }`,
|
|
1183
|
+
// The ScrMap prefix { rc, key_kind, val_kind, val_retain, val_release,
|
|
1184
|
+
// val_trace, key_retain, key_release, key_trace } — the inline RC
|
|
1185
|
+
// fast paths test the two trace slots (shapes.ts).
|
|
1186
|
+
...(this.rcHelpers === null
|
|
1187
|
+
? []
|
|
1188
|
+
: [`%ScrMapRc = type { ${this.sizeType}, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr }`]),
|
|
1107
1189
|
// The runtime error prefix { rc, vt, name, message, code, cause } and the
|
|
1108
1190
|
// class-object shape { rc, pre, post, ctor, name } — field reads on
|
|
1109
1191
|
// builtin errors and classval loads GEP through these.
|
|
@@ -1121,11 +1203,11 @@ export class LlEmitter {
|
|
|
1121
1203
|
// scr_runtime.h's natural alignment: kind at 8, f64 at 16, b at 24,
|
|
1122
1204
|
// payload at 32).
|
|
1123
1205
|
`%ScrCaught = type { ${this.sizeType}, i32, double, i8, ptr, ptr, ptr, ptr }`,
|
|
1124
|
-
// ScrBytes { rc, len, elem(i32+pad), data, backing }. Indexed
|
|
1206
|
+
// ScrBytes { rc, len, elem(i32+pad), data, backing, brands, shared }. Indexed
|
|
1125
1207
|
// typed-array access GEPs through this directly: the IR type already
|
|
1126
1208
|
// fixes elem, so the hot path needs neither a runtime kind load nor
|
|
1127
1209
|
// the generic scr_bytes_get/set call.
|
|
1128
|
-
`%ScrBytes = type { ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, i8 }`,
|
|
1210
|
+
`%ScrBytes = type { ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, i8, i8, i8, ptr }`,
|
|
1129
1211
|
// The capture box { rc, kind, obj_retain, obj_release, obj_trace,
|
|
1130
1212
|
// slot } — TDZ reads peek the payload slot (offset 40) directly.
|
|
1131
1213
|
`%ScrBox = type { ${this.sizeType}, i32, ptr, ptr, ptr, i64 }`,
|
|
@@ -1148,13 +1230,14 @@ export class LlEmitter {
|
|
|
1148
1230
|
// objects compiled with -DSCR_THREAD_INSTANCES. Immutable interned
|
|
1149
1231
|
// data (string literals, unit arms, template arrays, vtables) stays
|
|
1150
1232
|
// shared.
|
|
1151
|
-
const tl = this.mod.lib?.threadInstances === true ? "thread_local " : "";
|
|
1152
|
-
out.push(``, `@scr_error_vts = external ${tl}global [${RUNTIME_ERROR_CLASSES.size} x %ScrVt]`, `declare void @scr_init()`, `declare void @scr_lib_init(i32, ptr)`, ...(this.runtimeAbiMarker && this.mod.lib === undefined
|
|
1233
|
+
const tl = this.mod.workers === true || this.mod.lib?.threadInstances === true ? "thread_local " : "";
|
|
1234
|
+
out.push(``, `@scr_error_vts = external ${tl}global [${RUNTIME_ERROR_CLASSES.size} x %ScrVt]`, `declare void @scr_init()`, `declare void @scr_lib_init(i32, ptr)`, ...(this.mod.workers ? [`declare void @scr_runtime_workers_v8()`] : []), ...(this.runtimeAbiMarker && this.mod.lib === undefined
|
|
1153
1235
|
? [`declare void @${RUNTIME_ABI_MARKER}()`]
|
|
1154
1236
|
: []));
|
|
1155
1237
|
for (const d of this.decls)
|
|
1156
1238
|
out.push(d);
|
|
1157
1239
|
out.push(``);
|
|
1240
|
+
const flushedDecls = new Set(this.decls);
|
|
1158
1241
|
for (const table of this.constantNumericTables.values()) {
|
|
1159
1242
|
const n = table.values.length;
|
|
1160
1243
|
out.push(`@${table.symbol} = private constant [${n} x double] [${table.values.map((v) => `double ${f64Lit(v)}`).join(", ")}]`, `define internal double @${table.symbol}_get(ptr %a, double %i) alwaysinline #0 {`, `entry:`, ` %initialized = icmp ne ptr %a, null`, ` %nonnegative = fcmp oge double %i, ${f64Lit(0)}`, ` %below = fcmp olt double %i, ${f64Lit(n)}`, ` %range = and i1 %nonnegative, %below`, ` %safe = and i1 %initialized, %range`, ` br i1 %safe, label %convert, label %fallback`, `convert:`,
|
|
@@ -1165,8 +1248,11 @@ export class LlEmitter {
|
|
|
1165
1248
|
for (const [text, lit] of this.literals) {
|
|
1166
1249
|
// Immortal interned ScrStr: { rc = SIZE_MAX, len, cap = len, bytes\0 } —
|
|
1167
1250
|
// the runtime ABI’s static table, retain/release skip rc == SIZE_MAX.
|
|
1251
|
+
// 64-bit (little-endian) targets split the capacity word: the high
|
|
1252
|
+
// half carries the precomputed Map key hash (SCR_STR_HASH_CACHE).
|
|
1253
|
+
const capWord = this.sizeType === "i64" ? literalCapWord(Buffer.from(text, "utf8")) : String(lit.len);
|
|
1168
1254
|
out.push(`@${lit.sym} = internal global { ${this.sizeType}, ${this.sizeType}, ${this.sizeType}, [${lit.len + 1} x i8] } ` +
|
|
1169
|
-
`{ ${this.sizeType} -1, ${this.sizeType} ${lit.len}, ${this.sizeType} ${
|
|
1255
|
+
`{ ${this.sizeType} -1, ${this.sizeType} ${lit.len}, ${this.sizeType} ${capWord}, [${lit.len + 1} x i8] c"${llStrBytes(text)}" }`);
|
|
1170
1256
|
}
|
|
1171
1257
|
if (this.literals.size > 0)
|
|
1172
1258
|
out.push(``);
|
|
@@ -1179,11 +1265,23 @@ export class LlEmitter {
|
|
|
1179
1265
|
}
|
|
1180
1266
|
if (this.unitInstances.size > 0)
|
|
1181
1267
|
out.push(``);
|
|
1268
|
+
for (const [unionId, sym] of this.absentInstances) {
|
|
1269
|
+
// One immortal ABSENT field-slot instance per undefined-armed union:
|
|
1270
|
+
// the undefined arm's tag with payload 1 (ordinary unit instances
|
|
1271
|
+
// carry 0). Every tag reader sees undefined; recordHas and the
|
|
1272
|
+
// field reads compare tag and payload, never the address, so the
|
|
1273
|
+
// per-object copies of separately compiled units agree.
|
|
1274
|
+
const tag = undefinedArmTag({ kind: "union", unionId }, this.unionsById);
|
|
1275
|
+
out.push(`@${sym} = internal global %ScrUnion { ${this.sizeType} -1, i32 ${tag}, ptr null, ptr null, ptr null, i64 ${ABSENT_FIELD_PAYLOAD} } ; ${unionId} absent field`);
|
|
1276
|
+
}
|
|
1277
|
+
if (this.absentInstances.size > 0)
|
|
1278
|
+
out.push(``);
|
|
1182
1279
|
for (const [key, re] of this.regexInstances) {
|
|
1183
1280
|
// One immortal ScrRegex per (pattern, flags) literal, pointing at
|
|
1184
|
-
// the interned source/flags strings. The bc
|
|
1185
|
-
// compile, cached by the runtime) — a mutable
|
|
1186
|
-
|
|
1281
|
+
// the interned source/flags strings. The bc and native-matcher slots
|
|
1282
|
+
// start null (lazy compile, cached by the runtime) — a mutable
|
|
1283
|
+
// global, not constant.
|
|
1284
|
+
out.push(`@${re.sym} = internal ${tl}global %ScrRegex { ${this.sizeType} -1, ptr ${re.src}, ptr ${re.fl}, ptr null, double 0.0, ptr null, ptr null } ; ${key.replace(/\n/g, "\\n")}`);
|
|
1187
1285
|
}
|
|
1188
1286
|
if (this.regexInstances.size > 0)
|
|
1189
1287
|
out.push(``);
|
|
@@ -1306,7 +1404,7 @@ export class LlEmitter {
|
|
|
1306
1404
|
// The runtime stream vtables' intervals (streamVtStampLines) — the
|
|
1307
1405
|
// emitter story: instanceof and dynamic teardown dispatch through
|
|
1308
1406
|
// them.
|
|
1309
|
-
out.push(`@${iv.vt} = external global %ScrVt`);
|
|
1407
|
+
out.push(`@${iv.vt} = external ${tl}global %ScrVt`);
|
|
1310
1408
|
stamps.push(` store ${this.sizeType} ${iv.pre}, ptr getelementptr inbounds (%ScrVt, ptr @${iv.vt}, i64 0, i32 0)`, ` store ${this.sizeType} ${iv.post}, ptr getelementptr inbounds (%ScrVt, ptr @${iv.vt}, i64 0, i32 1) ; ${iv.lib}`);
|
|
1311
1409
|
}
|
|
1312
1410
|
if (this.streamIntervals.length > 0)
|
|
@@ -1332,6 +1430,7 @@ export class LlEmitter {
|
|
|
1332
1430
|
// symbols specified by the library IR instead.
|
|
1333
1431
|
for (const line of this.emitLibDefs(globals, globalReleaseLines, stamps))
|
|
1334
1432
|
out.push(line);
|
|
1433
|
+
out.push(...this.inlineRcTail(flushedDecls));
|
|
1335
1434
|
out.push(`attributes #0 = { sanitize_address }`);
|
|
1336
1435
|
if (this.wasi)
|
|
1337
1436
|
out.push(`attributes #1 = { sanitize_address presplitcoroutine }`);
|
|
@@ -1343,7 +1442,7 @@ export class LlEmitter {
|
|
|
1343
1442
|
out.push(this.debug.render());
|
|
1344
1443
|
return out;
|
|
1345
1444
|
}
|
|
1346
|
-
out.push(`define i32 @${this.wasi ? "__main_argc_argv" : "main"}(i32 %argc, ptr %argv) ${FN_ATTRS} {`, `entry:`, ...(this.runtimeAbiMarker ? [` call void @${RUNTIME_ABI_MARKER}()`] : []), ` call void @scr_init()`, ...stamps,
|
|
1445
|
+
out.push(`define i32 @${this.mod.workers ? "sc_context_entry" : this.wasi ? "__main_argc_argv" : "main"}(i32 %argc, ptr %argv) ${FN_ATTRS} {`, `entry:`, ...(this.runtimeAbiMarker ? [` call void @${RUNTIME_ABI_MARKER}()`] : []), ...(this.mod.workers ? [` call void @scr_runtime_workers_v8()`] : []), ` call void @scr_init()`, ...stamps,
|
|
1347
1446
|
// Event-surface programs (signal/exit listeners) fill the loop's
|
|
1348
1447
|
// nullable event hooks before %main — scr_events.c links only when
|
|
1349
1448
|
// this line is emitted (native/executable.ts gates on the same predicate).
|
|
@@ -1451,11 +1550,14 @@ export class LlEmitter {
|
|
|
1451
1550
|
? [` %test_exit = call i32 @scr_test_exit_code()`, ` ret i32 %test_exit`]
|
|
1452
1551
|
: usesIsland
|
|
1453
1552
|
? [` %island_exit = call i32 @scr_island_exit_code()`, ` ret i32 %island_exit`]
|
|
1454
|
-
: [` %native_exit = call i32 @scr_exit_code_hint_get()`, ` ret i32 %native_exit`]), `}`, ``,
|
|
1553
|
+
: [` %native_exit = call i32 @scr_exit_code_hint_get()`, ` ret i32 %native_exit`]), `}`, ``, ...this.inlineRcTail(flushedDecls),
|
|
1455
1554
|
// sanitize_address is inert under the plain pipeline; the sanitized
|
|
1456
1555
|
// lane's -fsanitize=address link activates instrumentation over the
|
|
1457
1556
|
// emitted functions too (the runtime TUs get theirs from clang).
|
|
1458
1557
|
`attributes #0 = { sanitize_address }`, ...(this.wasi ? [`attributes #1 = { sanitize_address presplitcoroutine }`] : []), ...(hasNoInlineRecordClone ? [`attributes #2 = { noinline sanitize_address }`] : []), ``);
|
|
1558
|
+
if (this.mod.workers) {
|
|
1559
|
+
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`, `}`);
|
|
1560
|
+
}
|
|
1459
1561
|
out.push(...this.fieldAliasMetadata());
|
|
1460
1562
|
if (this.debug !== null)
|
|
1461
1563
|
out.push(this.debug.render());
|
|
@@ -1705,10 +1807,62 @@ export class LlEmitter {
|
|
|
1705
1807
|
// emitted once with internal linkage, like the record retains.
|
|
1706
1808
|
defs.push(`define internal ptr @sc_retain_box(ptr %b) ${FN_ATTRS} {`, `entry:`, ` %rc = load ${this.sizeType}, ptr %b`, ` %imm = icmp eq ${this.sizeType} %rc, -1`, ` br i1 %imm, label %done, label %inc`, `inc:`, ` %n = add ${this.sizeType} %rc, 1`, ` store ${this.sizeType} %n, ptr %b`, ` %colorp = getelementptr i8, ptr %b, ${this.sizeType} -${this.cycleColorOffset}`, ` store i32 0, ptr %colorp ; mark live`, ` br label %done`, `done:`, ` ret ptr %b`, `}`, ``);
|
|
1707
1809
|
}
|
|
1810
|
+
// String comparisons enter through small internal wrappers that LLVM
|
|
1811
|
+
// inlines at each site: interned literals and shared values compare by
|
|
1812
|
+
// identity, and equality rejects different byte lengths, before any
|
|
1813
|
+
// runtime call (scr_str_eq/scr_str_cmp keep the complete semantics).
|
|
1814
|
+
if (this.decls.has(`declare zeroext i1 @scr_str_eq(ptr, ptr)`)) {
|
|
1815
|
+
defs.push(`define internal zeroext i1 @sc_str_eq(ptr %a, ptr %b) ${FN_ATTRS} {`, `entry:`, ` %same = icmp eq ptr %a, %b`, ` br i1 %same, label %yes, label %lengths`, `lengths:`, ` %alen.p = getelementptr inbounds %ScrStr, ptr %a, i32 0, i32 1`, ` %blen.p = getelementptr inbounds %ScrStr, ptr %b, i32 0, i32 1`, ` %alen = load ${this.sizeType}, ptr %alen.p`, ` %blen = load ${this.sizeType}, ptr %blen.p`, ` %samelen = icmp eq ${this.sizeType} %alen, %blen`, ` br i1 %samelen, label %bytes, label %no`, `bytes:`, ` %eq = call zeroext i1 @scr_str_eq(ptr %a, ptr %b)`, ` ret i1 %eq`, `yes:`, ` ret i1 true`, `no:`, ` ret i1 false`, `}`, ``);
|
|
1816
|
+
}
|
|
1817
|
+
// Ordering also decides inline when the first bytes differ: byte order
|
|
1818
|
+
// is the plain comparison's order, and the UTF-16 order whenever both
|
|
1819
|
+
// bytes are ASCII. Callers only test the sign of the result.
|
|
1820
|
+
for (const fn of ["scr_str_cmp", "scr_str_cmp_u16"]) {
|
|
1821
|
+
if (!this.decls.has(`declare i32 @${fn}(ptr, ptr)`))
|
|
1822
|
+
continue;
|
|
1823
|
+
this.declare("declare i64 @llvm.bswap.i64(i64)");
|
|
1824
|
+
const utf16 = fn === "scr_str_cmp_u16";
|
|
1825
|
+
defs.push(`define internal i32 @${fn.replace(/^scr_/, "sc_")}(ptr %a, ptr %b) ${FN_ATTRS} {`, `entry:`, ` %same = icmp eq ptr %a, %b`, ` br i1 %same, label %equal, label %lengths`, `lengths:`, ` %alen.p = getelementptr inbounds %ScrStr, ptr %a, i32 0, i32 1`, ` %blen.p = getelementptr inbounds %ScrStr, ptr %b, i32 0, i32 1`, ` %alen = load ${this.sizeType}, ptr %alen.p`, ` %blen = load ${this.sizeType}, ptr %blen.p`, ` %aempty = icmp eq ${this.sizeType} %alen, 0`, ` %bempty = icmp eq ${this.sizeType} %blen, 0`, ` %empty = or i1 %aempty, %bempty`, ` br i1 %empty, label %order, label %words`,
|
|
1826
|
+
// Both at least 8 bytes: the first differing byte of one word pair.
|
|
1827
|
+
// Byte-swapped little-endian words compare in byte order.
|
|
1828
|
+
`words:`, ` %along = icmp uge ${this.sizeType} %alen, 8`, ` %blong = icmp uge ${this.sizeType} %blen, 8`, ` %long = and i1 %along, %blong`, ` br i1 %long, label %word, label %first`, `word:`, ` %awp = getelementptr inbounds %ScrStr, ptr %a, i32 1`, ` %bwp = getelementptr inbounds %ScrStr, ptr %b, i32 1`, ` %aw = load i64, ptr %awp, align 1`, ` %bw = load i64, ptr %bwp, align 1`, ` %wdiffer = icmp ne i64 %aw, %bw`, ` br i1 %wdiffer, label %wdiff, label %wsame`, `wdiff:`, ...(utf16
|
|
1829
|
+
? [
|
|
1830
|
+
` %wbits = or i64 %aw, %bw`,
|
|
1831
|
+
` %whigh = and i64 %wbits, -9187201950435737472`,
|
|
1832
|
+
` %wascii = icmp eq i64 %whigh, 0`,
|
|
1833
|
+
` br i1 %wascii, label %wordorder, label %order`,
|
|
1834
|
+
]
|
|
1835
|
+
: [` br label %wordorder`]), `wordorder:`, ` %asw = call i64 @llvm.bswap.i64(i64 %aw)`, ` %bsw = call i64 @llvm.bswap.i64(i64 %bw)`, ` %wbelow = icmp ult i64 %asw, %bsw`, ` %wsign = select i1 %wbelow, i32 -1, i32 1`, ` ret i32 %wsign`,
|
|
1836
|
+
// Equal first words and one length of at most 16 bytes: the word
|
|
1837
|
+
// ending at that length covers the rest, and any difference lies
|
|
1838
|
+
// past the equal first word.
|
|
1839
|
+
`wsame:`, ` %samelen = icmp eq ${this.sizeType} %alen, %blen`, ` %short = icmp ule ${this.sizeType} %alen, 16`, ` %tailable = and i1 %samelen, %short`, ` br i1 %tailable, label %tail, label %order`, `tail:`, ` %toff = sub ${this.sizeType} %alen, 8`, ` %atp = getelementptr inbounds i8, ptr %awp, ${this.sizeType} %toff`, ` %btp = getelementptr inbounds i8, ptr %bwp, ${this.sizeType} %toff`, ` %at = load i64, ptr %atp, align 1`, ` %bt = load i64, ptr %btp, align 1`, ` %tdiffer = icmp ne i64 %at, %bt`, ` br i1 %tdiffer, label %tdiff, label %equal`, `tdiff:`, ...(utf16
|
|
1840
|
+
? [
|
|
1841
|
+
` %tbits = or i64 %at, %bt`,
|
|
1842
|
+
` %thigh = and i64 %tbits, -9187201950435737472`,
|
|
1843
|
+
` %tascii = icmp eq i64 %thigh, 0`,
|
|
1844
|
+
` br i1 %tascii, label %tailorder, label %order`,
|
|
1845
|
+
]
|
|
1846
|
+
: [` br label %tailorder`]), `tailorder:`, ` %ats = call i64 @llvm.bswap.i64(i64 %at)`, ` %bts = call i64 @llvm.bswap.i64(i64 %bt)`, ` %tbelow = icmp ult i64 %ats, %bts`, ` %tsign = select i1 %tbelow, i32 -1, i32 1`, ` ret i32 %tsign`, `first:`, ` %adata = getelementptr inbounds %ScrStr, ptr %a, i32 1`, ` %bdata = getelementptr inbounds %ScrStr, ptr %b, i32 1`, ` %x = load i8, ptr %adata`, ` %y = load i8, ptr %bdata`, ` %differ = icmp ne i8 %x, %y`, ...(utf16
|
|
1847
|
+
? [
|
|
1848
|
+
` %bits = or i8 %x, %y`,
|
|
1849
|
+
` %ascii = icmp sge i8 %bits, 0`,
|
|
1850
|
+
` %decide = and i1 %differ, %ascii`,
|
|
1851
|
+
]
|
|
1852
|
+
: [` %decide = and i1 %differ, true`]), ` br i1 %decide, label %byte, label %order`, `byte:`, ` %below = icmp ult i8 %x, %y`, ` %sign = select i1 %below, i32 -1, i32 1`, ` ret i32 %sign`, `order:`, ` %c = call i32 @${fn}(ptr %a, ptr %b)`, ` ret i32 %c`, `equal:`, ` ret i32 0`, `}`, ``);
|
|
1853
|
+
}
|
|
1708
1854
|
// The declarations these helpers added must land in the extern block,
|
|
1709
1855
|
// which already flushed — append here instead (LLVM is order-free).
|
|
1710
1856
|
return defs.length > 0 ? [...defs] : defs;
|
|
1711
1857
|
}
|
|
1858
|
+
/** The inline RC helpers every call site requested, emitted last so
|
|
1859
|
+
* requests from main/library epilogues are included. Slow-path
|
|
1860
|
+
* declarations a late request added after the extern block flushed land
|
|
1861
|
+
* here too. */
|
|
1862
|
+
inlineRcTail(flushedDecls) {
|
|
1863
|
+
const late = inlineRcDecls(this.shapeHost).filter((d) => !flushedDecls.has(d));
|
|
1864
|
+
return [...late, ...emitInlineRcHelpers(this.shapeHost)];
|
|
1865
|
+
}
|
|
1712
1866
|
/** Env-signature wrappers + interned immortal closures for declared
|
|
1713
1867
|
* functions used as values (the runtime ABI’s sc_w_/sc_fc_ pair): every
|
|
1714
1868
|
* mention of `f` yields the same pointer, so `f === f` holds. */
|
|
@@ -1723,7 +1877,7 @@ export class LlEmitter {
|
|
|
1723
1877
|
// never the inner return type.
|
|
1724
1878
|
const ret = fn.async === true || fn.generator !== undefined ? "ptr" : this.llType(fn.returnType);
|
|
1725
1879
|
const call = `call ${ret} @${this.callTarget(name)}(${args})`;
|
|
1726
|
-
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 global %ScrClosure { ${this.sizeType} -1, ptr @${mangleWrapper(name)}, ${this.sizeType} 0, ptr null, i32 ${(fn.generator ? 1 : 0) + (fn.async ? 2 : 0) + (fn.ownsPrototype ? 4 : 0)} }`, ``);
|
|
1880
|
+
out.push(`define internal ${ret} @${mangleWrapper(name)}(ptr %env${params.length ? ", " + params.join(", ") : ""}) ${FN_ATTRS} { ; ${name} as a value`, `entry:`, ret === "void" ? ` ${call}` : ` %r = ${call}`, ret === "void" ? ` ret void` : ` ret ${ret} %r`, `}`, `@${mangleFnClosure(name)} = internal ${this.mod.workers === true || this.mod.lib?.threadInstances === true ? "thread_local " : ""}global %ScrClosure { ${this.sizeType} -1, ptr @${mangleWrapper(name)}, ${this.sizeType} 0, ptr null, i32 ${(fn.generator ? 1 : 0) + (fn.async ? 2 : 0) + (fn.ownsPrototype ? 4 : 0)}, i32 0, ptr null }`, ``);
|
|
1727
1881
|
}
|
|
1728
1882
|
return out;
|
|
1729
1883
|
}
|
|
@@ -1777,6 +1931,154 @@ export class LlEmitter {
|
|
|
1777
1931
|
});
|
|
1778
1932
|
return { definitions, pack, lifted, fieldTys, ret, tr, spawnParams, argPackLines };
|
|
1779
1933
|
}
|
|
1934
|
+
/** Settles `promise` from a clean async body result `%r` (moved in). */
|
|
1935
|
+
asyncFulfillLines(ret, promise) {
|
|
1936
|
+
switch (ret.kind) {
|
|
1937
|
+
case "void":
|
|
1938
|
+
this.declare(`declare void @scr_promise_fulfill_void(ptr)`);
|
|
1939
|
+
return [` call void @scr_promise_fulfill_void(ptr ${promise})`];
|
|
1940
|
+
case "f64":
|
|
1941
|
+
case "date":
|
|
1942
|
+
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
1943
|
+
return [` call void @scr_promise_fulfill_f64(ptr ${promise}, double %r)`];
|
|
1944
|
+
case "bool":
|
|
1945
|
+
this.declare(`declare void @scr_promise_fulfill_bool(ptr, i1 zeroext)`);
|
|
1946
|
+
return [` call void @scr_promise_fulfill_bool(ptr ${promise}, i1 %r)`];
|
|
1947
|
+
case "string":
|
|
1948
|
+
this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
|
|
1949
|
+
return [` call void @scr_promise_fulfill_str(ptr ${promise}, ptr %r) ; moves in`];
|
|
1950
|
+
case "dyn":
|
|
1951
|
+
this.declare(`declare void @scr_promise_resolve_dyn(ptr, ptr)`);
|
|
1952
|
+
return [` call void @scr_promise_resolve_dyn(ptr ${promise}, ptr %r) ; moves in`];
|
|
1953
|
+
default: {
|
|
1954
|
+
const v = vAdapters(this.shapeHost, ret);
|
|
1955
|
+
this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
|
|
1956
|
+
return [
|
|
1957
|
+
` call void @scr_promise_fulfill_ref(ptr ${promise}, ptr %r, ptr ${v.retain}, ptr ${v.release}, ptr ${traceArg(this.shapeHost, ret)})`,
|
|
1958
|
+
];
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
/** Async functions whose calls can skip the fiber (scr_async_inline_*).
|
|
1963
|
+
* A body qualifies only when nothing it runs can park its execution
|
|
1964
|
+
* context: its emitted text, and the text of every module function it
|
|
1965
|
+
* mentions (transitively), contains no suspension entry point. Such a
|
|
1966
|
+
* body always completes before the call returns, which is exactly the
|
|
1967
|
+
* behavior of a fiber that never suspends. Excluded conservatively:
|
|
1968
|
+
* wasm32 (its coroutine lowering owns async bodies), programs embedding
|
|
1969
|
+
* the dynamic engine, module-evaluation bodies (their promise caches),
|
|
1970
|
+
* `dyn` results (resolution may adopt a thenable), and every program
|
|
1971
|
+
* that uses node:test subtests — `t.test()` runs a subtest inline and
|
|
1972
|
+
* awaits it on the CURRENT fiber even from synchronous code. Worker
|
|
1973
|
+
* programs keep fibers too: their fiber epilogue owns context
|
|
1974
|
+
* termination (a stopping worker rejects and observes the promise), which
|
|
1975
|
+
* the fiberless wrapper does not reproduce. A missed suspension still
|
|
1976
|
+
* cannot reorder silently: the runtime traps when a context parks while a
|
|
1977
|
+
* fiberless body is on its stack. */
|
|
1978
|
+
fiberlessAsyncFunctions() {
|
|
1979
|
+
const eligible = new Set();
|
|
1980
|
+
if (this.wasi || this.mod.workers === true)
|
|
1981
|
+
return eligible;
|
|
1982
|
+
const embedded = this.mod.embedded;
|
|
1983
|
+
if (embedded !== undefined && embedded.modules.length > 0)
|
|
1984
|
+
return eligible;
|
|
1985
|
+
const suspends = /@scr_(?:await_|module_await\b|promise_await_settled\b|test_sub\b|gen_yield_|async_gen_|fiber_)/;
|
|
1986
|
+
for (const decl of this.decls)
|
|
1987
|
+
if (/@scr_test_sub\b/.test(decl))
|
|
1988
|
+
return eligible;
|
|
1989
|
+
for (const text of this.fnDefText.values())
|
|
1990
|
+
if (/@scr_test_sub\b/.test(text))
|
|
1991
|
+
return eligible;
|
|
1992
|
+
const byMangled = new Map();
|
|
1993
|
+
for (const fn of this.mod.functions) {
|
|
1994
|
+
byMangled.set(mangleFunction(fn.name), fn.name);
|
|
1995
|
+
byMangled.set(mangleBorrowedFunction(fn.name), fn.name);
|
|
1996
|
+
}
|
|
1997
|
+
// Reverse mention edges, then propagate "may park" to every mentioner.
|
|
1998
|
+
const mentionedBy = new Map();
|
|
1999
|
+
const parks = new Set();
|
|
2000
|
+
const work = [];
|
|
2001
|
+
for (const [name, text] of this.fnDefText) {
|
|
2002
|
+
if (suspends.test(text)) {
|
|
2003
|
+
parks.add(name);
|
|
2004
|
+
work.push(name);
|
|
2005
|
+
}
|
|
2006
|
+
for (const match of text.matchAll(/@(sc_b?f_[A-Za-z0-9_]+)/g)) {
|
|
2007
|
+
const callee = byMangled.get(match[1]);
|
|
2008
|
+
if (callee === undefined || callee === name)
|
|
2009
|
+
continue;
|
|
2010
|
+
let callers = mentionedBy.get(callee);
|
|
2011
|
+
if (callers === undefined)
|
|
2012
|
+
mentionedBy.set(callee, (callers = new Set()));
|
|
2013
|
+
callers.add(name);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
while (work.length > 0) {
|
|
2017
|
+
const callers = mentionedBy.get(work.pop());
|
|
2018
|
+
if (callers === undefined)
|
|
2019
|
+
continue;
|
|
2020
|
+
for (const caller of callers) {
|
|
2021
|
+
if (parks.has(caller))
|
|
2022
|
+
continue;
|
|
2023
|
+
parks.add(caller);
|
|
2024
|
+
work.push(caller);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
for (const fn of this.mod.functions) {
|
|
2028
|
+
if (fn.async !== true || fn.generator !== undefined)
|
|
2029
|
+
continue;
|
|
2030
|
+
if (fn.asyncCacheGlobal !== undefined || fn.asyncCycleCacheGlobal !== undefined)
|
|
2031
|
+
continue;
|
|
2032
|
+
if (fn.returnType.kind === "dyn" || parks.has(fn.name))
|
|
2033
|
+
continue;
|
|
2034
|
+
if (!this.fnDefText.has(fn.name))
|
|
2035
|
+
continue;
|
|
2036
|
+
eligible.add(fn.name);
|
|
2037
|
+
}
|
|
2038
|
+
return eligible;
|
|
2039
|
+
}
|
|
2040
|
+
/** The spawn wrapper of a fiberless async function: runs the body on the
|
|
2041
|
+
* caller's stack between scr_async_inline_enter/leave and settles the
|
|
2042
|
+
* fresh promise from its outcome — the fiber trampoline's epilogue,
|
|
2043
|
+
* without the fiber. */
|
|
2044
|
+
emitFiberlessAsyncSpawn(fn, spawnParams) {
|
|
2045
|
+
const ret = fn.returnType;
|
|
2046
|
+
const retTy = this.llType(ret);
|
|
2047
|
+
const lifted = fn.captures !== undefined;
|
|
2048
|
+
this.declare(`declare void @scr_async_inline_enter(ptr)`);
|
|
2049
|
+
this.declare(`declare ptr @scr_async_inline_leave(ptr)`);
|
|
2050
|
+
this.declare(`declare void @scr_promise_reject_pending(ptr)`);
|
|
2051
|
+
this.declare(`declare zeroext i1 @scr_exc_pending()`);
|
|
2052
|
+
if (lifted) {
|
|
2053
|
+
this.declare(`declare ptr @scr_closure_retain_v(ptr)`);
|
|
2054
|
+
this.declare(`declare void @scr_closure_release(ptr)`);
|
|
2055
|
+
}
|
|
2056
|
+
const bodyCall = `call ${retTy} @${mangleFunction(fn.name)}(${spawnParams.join(", ")})`;
|
|
2057
|
+
const out = [
|
|
2058
|
+
`define internal ptr @${mangleAsyncSpawn(fn.name)}(${spawnParams.join(", ")}) ${FN_ATTRS} { ; fiberless ${fn.name}`,
|
|
2059
|
+
`entry:`,
|
|
2060
|
+
` %frame = alloca { ptr, ptr }`,
|
|
2061
|
+
// The fiber path holds +1 on a lifted environment for the body's
|
|
2062
|
+
// duration (packed, released by the trampoline); keep that lifetime.
|
|
2063
|
+
...(lifted ? [` %env = call ptr @scr_closure_retain_v(ptr %a0)`] : []),
|
|
2064
|
+
` call void @scr_async_inline_enter(ptr %frame)`,
|
|
2065
|
+
retTy === "void" ? ` ${bodyCall}` : ` %r = ${bodyCall}`,
|
|
2066
|
+
...(lifted ? [` call void @scr_closure_release(ptr %a0)`] : []),
|
|
2067
|
+
` %pend = call zeroext i1 @scr_exc_pending()`,
|
|
2068
|
+
` %p = call ptr @scr_async_inline_leave(ptr %frame)`,
|
|
2069
|
+
` br i1 %pend, label %thrown, label %clean`,
|
|
2070
|
+
`clean:`,
|
|
2071
|
+
...this.asyncFulfillLines(ret, "%p"),
|
|
2072
|
+
` ret ptr %p`,
|
|
2073
|
+
`thrown:`,
|
|
2074
|
+
];
|
|
2075
|
+
if (ret.kind !== "void" && isRefCounted(ret)) {
|
|
2076
|
+
// An escaping throw means %r is the never-read dummy (NULL).
|
|
2077
|
+
out.push(` call void ${releaseSym(this.shapeHost, ret)}(ptr %r)`);
|
|
2078
|
+
}
|
|
2079
|
+
out.push(` call void @scr_promise_reject_pending(ptr %p)`, ` ret ptr %p`, `}`, ``);
|
|
2080
|
+
return out;
|
|
2081
|
+
}
|
|
1780
2082
|
/** Per-async-function machinery — async.ts's scaffolding, .ll
|
|
1781
2083
|
* flavored: an argument-pack struct type, a fiber trampoline (unpacks,
|
|
1782
2084
|
* frees the pack, runs the ordinary compiled body, settles the
|
|
@@ -1791,9 +2093,20 @@ export class LlEmitter {
|
|
|
1791
2093
|
this.declare(`declare void @llvm.coro.destroy(ptr)`);
|
|
1792
2094
|
out.push(`define void @scr_wasi_coro_resume(ptr %handle) ${FN_ATTRS} {`, `entry:`, ` call void @llvm.coro.resume(ptr %handle)`, ` ret void`, `}`, `define void @scr_wasi_coro_destroy(ptr %handle) ${FN_ATTRS} {`, `entry:`, ` call void @llvm.coro.destroy(ptr %handle)`, ` ret void`, `}`, ``);
|
|
1793
2095
|
}
|
|
2096
|
+
const fiberless = this.fiberlessAsyncFunctions();
|
|
1794
2097
|
for (const fn of this.mod.functions) {
|
|
1795
2098
|
if (fn.async !== true || fn.generator !== undefined)
|
|
1796
2099
|
continue;
|
|
2100
|
+
if (fiberless.has(fn.name)) {
|
|
2101
|
+
const fieldTys = [
|
|
2102
|
+
...(fn.captures !== undefined ? ["ptr"] : []),
|
|
2103
|
+
...fn.params.map((p) => this.llType(p.type)),
|
|
2104
|
+
];
|
|
2105
|
+
const params = fieldTys.map((ty, i) => `${ty} %a${i}`);
|
|
2106
|
+
for (const line of this.emitFiberlessAsyncSpawn(fn, params))
|
|
2107
|
+
out.push(line);
|
|
2108
|
+
continue;
|
|
2109
|
+
}
|
|
1797
2110
|
const { definitions, ret, tr, spawnParams, argPackLines } = this.emitArgPackAndTrampolinePrologue(fn);
|
|
1798
2111
|
for (const line of definitions)
|
|
1799
2112
|
out.push(line);
|
|
@@ -1811,34 +2124,8 @@ export class LlEmitter {
|
|
|
1811
2124
|
this.declare(`declare void @scr_closure_release(ptr)`);
|
|
1812
2125
|
}
|
|
1813
2126
|
tr.push(` %pend = call zeroext i1 @scr_exc_pending()`, ` br i1 %pend, label %thrown, label %clean`, `clean:`, ` %pr = call ptr @scr_fiber_promise(ptr %self)`);
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
this.declare(`declare void @scr_promise_fulfill_void(ptr)`);
|
|
1817
|
-
tr.push(` call void @scr_promise_fulfill_void(ptr %pr)`);
|
|
1818
|
-
break;
|
|
1819
|
-
case "f64":
|
|
1820
|
-
case "date":
|
|
1821
|
-
this.declare(`declare void @scr_promise_fulfill_f64(ptr, double)`);
|
|
1822
|
-
tr.push(` call void @scr_promise_fulfill_f64(ptr %pr, double %r)`);
|
|
1823
|
-
break;
|
|
1824
|
-
case "bool":
|
|
1825
|
-
this.declare(`declare void @scr_promise_fulfill_bool(ptr, i1 zeroext)`);
|
|
1826
|
-
tr.push(` call void @scr_promise_fulfill_bool(ptr %pr, i1 %r)`);
|
|
1827
|
-
break;
|
|
1828
|
-
case "string":
|
|
1829
|
-
this.declare(`declare void @scr_promise_fulfill_str(ptr, ptr)`);
|
|
1830
|
-
tr.push(` call void @scr_promise_fulfill_str(ptr %pr, ptr %r) ; moves in`);
|
|
1831
|
-
break;
|
|
1832
|
-
case "dyn":
|
|
1833
|
-
this.declare(`declare void @scr_promise_resolve_dyn(ptr, ptr)`);
|
|
1834
|
-
tr.push(` call void @scr_promise_resolve_dyn(ptr %pr, ptr %r) ; moves in`);
|
|
1835
|
-
break;
|
|
1836
|
-
default: {
|
|
1837
|
-
const v = vAdapters(this.shapeHost, ret);
|
|
1838
|
-
this.declare(`declare void @scr_promise_fulfill_ref(ptr, ptr, ptr, ptr, ptr)`);
|
|
1839
|
-
tr.push(` call void @scr_promise_fulfill_ref(ptr %pr, ptr %r, ptr ${v.retain}, ptr ${v.release}, ptr ${traceArg(this.shapeHost, ret)})`);
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
2127
|
+
for (const line of this.asyncFulfillLines(ret, "%pr"))
|
|
2128
|
+
tr.push(line);
|
|
1842
2129
|
tr.push(` ret void`, `thrown:`);
|
|
1843
2130
|
if (ret.kind !== "void" && isRefCounted(ret)) {
|
|
1844
2131
|
// An escaping throw means %r is the never-read dummy (NULL).
|
|
@@ -2077,6 +2364,40 @@ export class LlEmitter {
|
|
|
2077
2364
|
this.immortalValues.add(name);
|
|
2078
2365
|
return name;
|
|
2079
2366
|
}
|
|
2367
|
+
/** The immortal ABSENT state of an undefined-armed record field slot
|
|
2368
|
+
* (IR fieldAbsent). */
|
|
2369
|
+
absentInstanceRef(unionId) {
|
|
2370
|
+
if (undefinedArmTag({ kind: "union", unionId }, this.unionsById) < 0) {
|
|
2371
|
+
throw new InternalCompilerError(`llvm emitter bug: absent state of ${unionId} without undefined`);
|
|
2372
|
+
}
|
|
2373
|
+
let sym = this.absentInstances.get(unionId);
|
|
2374
|
+
if (!sym) {
|
|
2375
|
+
sym = `sc_absent_${this.absentInstances.size}`;
|
|
2376
|
+
this.absentInstances.set(unionId, sym);
|
|
2377
|
+
}
|
|
2378
|
+
const name = `@${sym}`;
|
|
2379
|
+
this.immortalValues.add(name);
|
|
2380
|
+
return name;
|
|
2381
|
+
}
|
|
2382
|
+
/** i1: a union value loaded from a field slot is the ABSENT state — the
|
|
2383
|
+
* undefined arm's tag with the absent payload marker. */
|
|
2384
|
+
fieldAbsentTest(value, unionId) {
|
|
2385
|
+
return emitFieldAbsentTest(this.B, value, undefinedArmTag({ kind: "union", unionId }, this.unionsById));
|
|
2386
|
+
}
|
|
2387
|
+
/** Loads a record field slot as a VALUE: an undefined-armed union slot
|
|
2388
|
+
* holding the ABSENT state surfaces as the ordinary undefined arm, so
|
|
2389
|
+
* absence never travels beyond its slot (a later `{ f: r.f }` is an
|
|
2390
|
+
* explicit, present property). */
|
|
2391
|
+
loadRecordField(ptr, t) {
|
|
2392
|
+
const v = this.loadField(ptr, t);
|
|
2393
|
+
if (t.kind !== "union" || undefinedArmTag(t, this.unionsById) < 0)
|
|
2394
|
+
return v;
|
|
2395
|
+
const absent = this.fieldAbsentTest(v, t.unionId);
|
|
2396
|
+
const unit = this.unitInstanceRef(t.unionId, undefinedArmTag(t, this.unionsById));
|
|
2397
|
+
const out = this.B.tmp();
|
|
2398
|
+
this.B.line(`${out} = select i1 ${absent}, ptr ${unit}, ptr ${v}`);
|
|
2399
|
+
return out;
|
|
2400
|
+
}
|
|
2080
2401
|
declare(decl) {
|
|
2081
2402
|
this.decls.add(decl);
|
|
2082
2403
|
}
|
|
@@ -2136,8 +2457,7 @@ export class LlEmitter {
|
|
|
2136
2457
|
const t = this.B.tmp();
|
|
2137
2458
|
this.B.line(`${t} = load ptr, ptr ${v.name}`);
|
|
2138
2459
|
if (v.boxed) {
|
|
2139
|
-
this.
|
|
2140
|
-
this.B.line(`call void @scr_box_release(ptr ${t})`);
|
|
2460
|
+
this.B.line(`call void ${boxReleaseSym(this.shapeHost)}(ptr ${t})`);
|
|
2141
2461
|
}
|
|
2142
2462
|
else {
|
|
2143
2463
|
this.releaseValue(t, v.type);
|
|
@@ -2153,8 +2473,7 @@ export class LlEmitter {
|
|
|
2153
2473
|
const t = this.B.tmp();
|
|
2154
2474
|
this.B.line(`${t} = load ptr, ptr ${e.slot}`);
|
|
2155
2475
|
if (e.boxed) {
|
|
2156
|
-
this.
|
|
2157
|
-
this.B.line(`call void @scr_box_release(ptr ${t})`);
|
|
2476
|
+
this.B.line(`call void ${boxReleaseSym(this.shapeHost)}(ptr ${t})`);
|
|
2158
2477
|
}
|
|
2159
2478
|
else {
|
|
2160
2479
|
this.releaseValue(t, e.type); // runtime releases are NULL-tolerant
|
|
@@ -2206,6 +2525,20 @@ export class LlEmitter {
|
|
|
2206
2525
|
* using the result). Callers own the surrounding pending branch; a
|
|
2207
2526
|
* `throw` unwinds unconditionally. */
|
|
2208
2527
|
emitUnwind() {
|
|
2528
|
+
if (this.mod.workers && this.tryStack.length > 0) {
|
|
2529
|
+
this.declare(`declare zeroext i1 @scr_context_stopping()`);
|
|
2530
|
+
const stopped = this.B.tmp();
|
|
2531
|
+
this.B.line(`${stopped} = call zeroext i1 @scr_context_stopping()`);
|
|
2532
|
+
const stop = this.B.newLabel("context.stop");
|
|
2533
|
+
const ordinary = this.B.newLabel("context.throw");
|
|
2534
|
+
this.B.condBr(stopped, stop, ordinary);
|
|
2535
|
+
this.B.startBlock(stop);
|
|
2536
|
+
const handlers = this.tryStack;
|
|
2537
|
+
this.tryStack = [];
|
|
2538
|
+
this.emitUnwind();
|
|
2539
|
+
this.tryStack = handlers;
|
|
2540
|
+
this.B.startBlock(ordinary);
|
|
2541
|
+
}
|
|
2209
2542
|
const target = this.tryStack[this.tryStack.length - 1];
|
|
2210
2543
|
const frameDepth = target?.frameDepth ?? 0;
|
|
2211
2544
|
const scopeDepth = target?.scopeDepth ?? 0;
|
|
@@ -2220,13 +2553,13 @@ export class LlEmitter {
|
|
|
2220
2553
|
terminator = `br label %${this.currentWasiCoro.finalLabel}`;
|
|
2221
2554
|
}
|
|
2222
2555
|
else {
|
|
2223
|
-
const t = this.currentReturnType;
|
|
2556
|
+
const t = this.llType(this.currentReturnType);
|
|
2224
2557
|
terminator =
|
|
2225
|
-
t
|
|
2558
|
+
t === "void"
|
|
2226
2559
|
? "ret void"
|
|
2227
|
-
: t
|
|
2560
|
+
: t === "double"
|
|
2228
2561
|
? `ret double ${f64Lit(0)}`
|
|
2229
|
-
: t
|
|
2562
|
+
: t === "i1"
|
|
2230
2563
|
? "ret i1 false"
|
|
2231
2564
|
: "ret ptr null";
|
|
2232
2565
|
}
|
|
@@ -2269,6 +2602,34 @@ export class LlEmitter {
|
|
|
2269
2602
|
this.emitUnwind();
|
|
2270
2603
|
B.startBlock(lk);
|
|
2271
2604
|
}
|
|
2605
|
+
workerLoopBudget() {
|
|
2606
|
+
if (!this.mod.workers)
|
|
2607
|
+
return null;
|
|
2608
|
+
const slot = this.B.slot();
|
|
2609
|
+
this.B.entryAllocas.push(`${slot} = alloca i32 ; worker cancellation budget`);
|
|
2610
|
+
this.B.line(`store i32 0, ptr ${slot}`);
|
|
2611
|
+
return slot;
|
|
2612
|
+
}
|
|
2613
|
+
emitWorkerLoopCheck(slot) {
|
|
2614
|
+
if (slot === null)
|
|
2615
|
+
return;
|
|
2616
|
+
const B = this.B;
|
|
2617
|
+
const budget = B.tmp(), due = B.tmp(), remaining = B.tmp();
|
|
2618
|
+
const poll = B.newLabel("worker.poll"), next = B.newLabel("worker.next");
|
|
2619
|
+
B.line(`${budget} = load i32, ptr ${slot}`);
|
|
2620
|
+
B.line(`${remaining} = sub i32 ${budget}, 1`);
|
|
2621
|
+
B.line(`store i32 ${remaining}, ptr ${slot}`);
|
|
2622
|
+
B.line(`${due} = icmp eq i32 ${budget}, 0`);
|
|
2623
|
+
B.condBr(due, poll, next);
|
|
2624
|
+
B.startBlock(poll);
|
|
2625
|
+
B.line(`store i32 63, ptr ${slot}`);
|
|
2626
|
+
// Calls and throwing operations retain their own checks. Amortize the
|
|
2627
|
+
// extra cancellation poll across bounded loop batches so scalar work
|
|
2628
|
+
// does not cross the runtime ABI on every iteration.
|
|
2629
|
+
this.emitPendingCheck();
|
|
2630
|
+
B.br(next);
|
|
2631
|
+
B.startBlock(next);
|
|
2632
|
+
}
|
|
2272
2633
|
/** Moves an already-evaluated value into the runtime's exception cell —
|
|
2273
2634
|
* the `throw` statement's kind dispatch (stmts.ts's), shared with
|
|
2274
2635
|
* every synthetic thrower. Ownership of a refcounted payload must have
|
|
@@ -2344,14 +2705,10 @@ export class LlEmitter {
|
|
|
2344
2705
|
this.unionTagSwitch(v.name, def, (arm) => {
|
|
2345
2706
|
let valueName = "false";
|
|
2346
2707
|
if (arm.kind === "f64" || arm.kind === "procStream") {
|
|
2347
|
-
valueName =
|
|
2348
|
-
this.declare(`declare double @scr_union_get_f64(ptr)`);
|
|
2349
|
-
B.line(`${valueName} = call double @scr_union_get_f64(ptr ${v.name})`);
|
|
2708
|
+
valueName = this.unionGetF64(v.name);
|
|
2350
2709
|
}
|
|
2351
2710
|
else if (arm.kind === "bool") {
|
|
2352
|
-
valueName =
|
|
2353
|
-
this.declare(`declare zeroext i1 @scr_union_get_bool(ptr)`);
|
|
2354
|
-
B.line(`${valueName} = call zeroext i1 @scr_union_get_bool(ptr ${v.name})`);
|
|
2711
|
+
valueName = this.unionGetBool(v.name);
|
|
2355
2712
|
}
|
|
2356
2713
|
else if (arm.kind === "string") {
|
|
2357
2714
|
valueName = this.unionPeek(v.name);
|
|
@@ -2530,24 +2887,34 @@ export class LlEmitter {
|
|
|
2530
2887
|
B.terminate(`unreachable`);
|
|
2531
2888
|
}
|
|
2532
2889
|
/** The +1 extraction of a union's single narrowed arm (unionNarrow /
|
|
2533
|
-
* the nullish-family reads): scalars via
|
|
2890
|
+
* the nullish-family reads): scalars via inline payload loads, ref arms
|
|
2534
2891
|
* a retained peek. */
|
|
2535
2892
|
unionExtract(uName, arm) {
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
this.
|
|
2540
|
-
B.line(`${t} = call double @scr_union_get_f64(ptr ${uName})`);
|
|
2541
|
-
return t;
|
|
2542
|
-
}
|
|
2543
|
-
if (arm.kind === "bool") {
|
|
2544
|
-
const t = B.tmp();
|
|
2545
|
-
this.declare(`declare zeroext i1 @scr_union_get_bool(ptr)`);
|
|
2546
|
-
B.line(`${t} = call zeroext i1 @scr_union_get_bool(ptr ${uName})`);
|
|
2547
|
-
return t;
|
|
2548
|
-
}
|
|
2893
|
+
if (arm.kind === "f64" || arm.kind === "procStream")
|
|
2894
|
+
return this.unionGetF64(uName);
|
|
2895
|
+
if (arm.kind === "bool")
|
|
2896
|
+
return this.unionGetBool(uName);
|
|
2549
2897
|
return this.retainValue(this.unionPeek(uName), arm);
|
|
2550
2898
|
}
|
|
2899
|
+
/** A scalar f64 arm's payload (scr_union_get_f64 inlined). Inline loads
|
|
2900
|
+
* keep stack boxes free of escaping calls, so LLVM can scalarize them. */
|
|
2901
|
+
unionGetF64(uName) {
|
|
2902
|
+
const p = this.B.tmp();
|
|
2903
|
+
const t = this.B.tmp();
|
|
2904
|
+
this.B.line(`${p} = getelementptr inbounds %ScrUnion, ptr ${uName}, i64 0, i32 5`);
|
|
2905
|
+
this.B.line(`${t} = load double, ptr ${p}`);
|
|
2906
|
+
return t;
|
|
2907
|
+
}
|
|
2908
|
+
/** A bool arm's payload (scr_union_get_bool inlined: nonzero slot). */
|
|
2909
|
+
unionGetBool(uName) {
|
|
2910
|
+
const p = this.B.tmp();
|
|
2911
|
+
const bits = this.B.tmp();
|
|
2912
|
+
const t = this.B.tmp();
|
|
2913
|
+
this.B.line(`${p} = getelementptr inbounds %ScrUnion, ptr ${uName}, i64 0, i32 5`);
|
|
2914
|
+
this.B.line(`${bits} = load i64, ptr ${p}`);
|
|
2915
|
+
this.B.line(`${t} = icmp ne i64 ${bits}, 0`);
|
|
2916
|
+
return t;
|
|
2917
|
+
}
|
|
2551
2918
|
/** Constructs a union box around an OWNED (+1, already moved) value —
|
|
2552
2919
|
* the scr_union_new_* dispatch of unionWrap and the wrap-into-join
|
|
2553
2920
|
* sites (shift). */
|
|
@@ -2726,17 +3093,26 @@ export class LlEmitter {
|
|
|
2726
3093
|
this.B.line(`${b} = load ptr, ptr ${slot}`);
|
|
2727
3094
|
return b;
|
|
2728
3095
|
}
|
|
3096
|
+
/** The payload slot of a capture box (ScrBox.slot, field 5). */
|
|
3097
|
+
boxSlot(box) {
|
|
3098
|
+
const slot = this.B.tmp();
|
|
3099
|
+
this.B.line(`${slot} = getelementptr inbounds %ScrBox, ptr ${box}, i32 0, i32 5`);
|
|
3100
|
+
return slot;
|
|
3101
|
+
}
|
|
2729
3102
|
boxGet(box, t) {
|
|
2730
3103
|
const B = this.B;
|
|
2731
3104
|
const acc = boxAccess(t);
|
|
2732
3105
|
const r = B.tmp();
|
|
3106
|
+
// Scalar payloads are read in place (scr_box_get_f64/bool are a
|
|
3107
|
+
// memcpy and a nonzero test of the same slot), so LLVM can keep a
|
|
3108
|
+
// captured number in a register across a loop of calls.
|
|
2733
3109
|
if (acc === "f64") {
|
|
2734
|
-
|
|
2735
|
-
B.line(`${r} = call double @scr_box_get_f64(ptr ${box})`);
|
|
3110
|
+
B.line(`${r} = load double, ptr ${this.boxSlot(box)}`);
|
|
2736
3111
|
}
|
|
2737
3112
|
else if (acc === "bool") {
|
|
2738
|
-
|
|
2739
|
-
B.line(`${
|
|
3113
|
+
const raw = B.tmp();
|
|
3114
|
+
B.line(`${raw} = load i64, ptr ${this.boxSlot(box)}`);
|
|
3115
|
+
B.line(`${r} = icmp ne i64 ${raw}, 0`);
|
|
2740
3116
|
}
|
|
2741
3117
|
else {
|
|
2742
3118
|
this.declare(`declare ptr @scr_box_get_ref(ptr)`);
|
|
@@ -2749,18 +3125,45 @@ export class LlEmitter {
|
|
|
2749
3125
|
const B = this.B;
|
|
2750
3126
|
const acc = boxAccess(t);
|
|
2751
3127
|
if (acc === "f64") {
|
|
2752
|
-
|
|
2753
|
-
B.line(`call void @scr_box_set_f64(ptr ${box}, double ${value})`);
|
|
3128
|
+
B.line(`store double ${value}, ptr ${this.boxSlot(box)}`);
|
|
2754
3129
|
}
|
|
2755
3130
|
else if (acc === "bool") {
|
|
2756
|
-
|
|
2757
|
-
B.line(
|
|
3131
|
+
const raw = B.tmp();
|
|
3132
|
+
B.line(`${raw} = zext i1 ${value} to i64`);
|
|
3133
|
+
B.line(`store i64 ${raw}, ptr ${this.boxSlot(box)}`);
|
|
2758
3134
|
}
|
|
2759
3135
|
else {
|
|
2760
3136
|
this.declare(`declare void @scr_box_set_ref(ptr, ptr)`);
|
|
2761
3137
|
B.line(`call void @scr_box_set_ref(ptr ${box}, ptr ${value})`);
|
|
2762
3138
|
}
|
|
2763
3139
|
}
|
|
3140
|
+
/** The box a new environment captures for `localId` (borrowed). An
|
|
3141
|
+
* unchanged parameter gets one box on first capture, holding its own
|
|
3142
|
+
* reference to the value; the function scope releases the cached box. */
|
|
3143
|
+
captureBox(localId) {
|
|
3144
|
+
const cache = this.lazyCaptureBoxes.get(localId);
|
|
3145
|
+
if (cache === undefined)
|
|
3146
|
+
return this.loadBox(`%${mangleLocal(localId)}`);
|
|
3147
|
+
const B = this.B;
|
|
3148
|
+
const type = this.currentLocals.get(localId).type;
|
|
3149
|
+
const current = this.loadBox(cache);
|
|
3150
|
+
const missing = B.tmp();
|
|
3151
|
+
B.line(`${missing} = icmp eq ptr ${current}, null`);
|
|
3152
|
+
const create = B.newLabel("lazy.box"), join = B.newLabel("lazy.box.j");
|
|
3153
|
+
B.condBr(missing, create, join);
|
|
3154
|
+
B.startBlock(create);
|
|
3155
|
+
const stack = this.lazyStackBoxes.get(localId);
|
|
3156
|
+
const box = stack ?? B.tmp();
|
|
3157
|
+
if (!stack)
|
|
3158
|
+
B.line(`${box} = ${boxNewCall(this.shapeHost, type)}`);
|
|
3159
|
+
const value = B.tmp();
|
|
3160
|
+
B.line(`${value} = load ${this.llType(type)}, ptr %${mangleLocal(localId)}`);
|
|
3161
|
+
this.boxSet(box, type, isRefCounted(type) ? this.retainValue(value, type) : value);
|
|
3162
|
+
B.line(`store ptr ${box}, ptr ${cache}`);
|
|
3163
|
+
B.br(join);
|
|
3164
|
+
B.startBlock(join);
|
|
3165
|
+
return this.loadBox(cache);
|
|
3166
|
+
}
|
|
2764
3167
|
retainBox(box) {
|
|
2765
3168
|
this.needsRetainBox = true;
|
|
2766
3169
|
const t = this.B.tmp();
|
|
@@ -2805,6 +3208,12 @@ export class LlEmitter {
|
|
|
2805
3208
|
B.line(`${empty} = icmp eq ptr ${value}, null`);
|
|
2806
3209
|
this.throwIfUninitialized(empty, name);
|
|
2807
3210
|
}
|
|
3211
|
+
/** Throws the TDZ ReferenceError for `name` unless the i1 `flag` is set. */
|
|
3212
|
+
checkInitializedFlag(flag, name) {
|
|
3213
|
+
const empty = this.B.tmp();
|
|
3214
|
+
this.B.line(`${empty} = xor i1 ${flag}, true`);
|
|
3215
|
+
this.throwIfUninitialized(empty, name);
|
|
3216
|
+
}
|
|
2808
3217
|
throwIfUninitialized(empty, name) {
|
|
2809
3218
|
const B = this.B;
|
|
2810
3219
|
const lt = B.newLabel("tdz.t");
|
|
@@ -2973,10 +3382,36 @@ export class LlEmitter {
|
|
|
2973
3382
|
this.unwindCleanups.clear();
|
|
2974
3383
|
this.jumpTargets = [];
|
|
2975
3384
|
this.currentLocals = new Map(fn.locals.map((l) => [l.id, l]));
|
|
3385
|
+
this.lazyCaptureBoxes.clear();
|
|
3386
|
+
this.lazyStackBoxes.clear();
|
|
3387
|
+
// An unchanged captured parameter keeps its value in a plain slot; the
|
|
3388
|
+
// binding is boxed only for closures (captureBox).
|
|
3389
|
+
const lazyParameters = this.lazyCaptures.parameters(fn);
|
|
3390
|
+
for (const id of lazyParameters) {
|
|
3391
|
+
const { boxed: _boxed, ...plain } = this.currentLocals.get(id);
|
|
3392
|
+
this.currentLocals.set(id, plain);
|
|
3393
|
+
this.lazyCaptureBoxes.set(id, "");
|
|
3394
|
+
}
|
|
3395
|
+
this.currentConstantCallbacks =
|
|
3396
|
+
this.debug === null ? (this.constantCallbacks.get(fn.name) ?? new Map()) : new Map();
|
|
3397
|
+
// Preserve concrete source bindings for debugger inspection. Suspended
|
|
3398
|
+
// functions keep the established array lifetime across continuations.
|
|
3399
|
+
this.streamingSplitsEnabled = this.debug === null && !fn.async && !fn.generator;
|
|
3400
|
+
this.stackCallbacks.reset(this.streamingSplitsEnabled);
|
|
3401
|
+
this.stackCaptures.reset(fn, this.streamingSplitsEnabled, lazyParameters);
|
|
3402
|
+
this.privateSplitLocals = this.streamingSplitsEnabled ? findPrivateSplitLocals(fn) : new Map();
|
|
3403
|
+
this.storedSplits.clear();
|
|
3404
|
+
this.splitSpans.clear();
|
|
3405
|
+
this.stringSlices.clear();
|
|
2976
3406
|
const initializerBindings = this.initializerBindings.get(fn.name) ?? [];
|
|
2977
3407
|
const numericFn = withInitializerBindings(fn, initializerBindings);
|
|
2978
3408
|
this.numericLocals = new Map(numericFn.locals.map((l) => [l.id, l]));
|
|
2979
3409
|
this.borrowedParameters.clear();
|
|
3410
|
+
this.projectedParameters.clear();
|
|
3411
|
+
const projectedParameterIndexes = this.callLifetimes.parameters.get(fn.name);
|
|
3412
|
+
if (projectedParameterIndexes)
|
|
3413
|
+
for (const index of projectedParameterIndexes)
|
|
3414
|
+
this.projectedParameters.add(fn.params[index].localId);
|
|
2980
3415
|
this.stableCallBindings = this.callLifetimes.bindings.get(fn.name) ?? new Set();
|
|
2981
3416
|
const borrowedParameterIndexes = this.callLifetimes.borrowed.get(fn.name);
|
|
2982
3417
|
if (borrowedParameterIndexes) {
|
|
@@ -2986,14 +3421,25 @@ export class LlEmitter {
|
|
|
2986
3421
|
this.captureIds = new Set([...(fn.captures ?? []), ...(fn.classCaptures ?? [])].map((c) => c.localId));
|
|
2987
3422
|
this.integerLoopBindings.clear();
|
|
2988
3423
|
this.countedLoopsEnabled = this.debug === null && !fn.async && !fn.generator;
|
|
3424
|
+
this.byteWindowsEnabled = this.countedLoopsEnabled;
|
|
3425
|
+
this.byteWindowEntries =
|
|
3426
|
+
this.byteWindowsEnabled && numericFn.locals.some((l) => l.type.kind === "bytes")
|
|
3427
|
+
? findInitializedByteLoopBindings(fn)
|
|
3428
|
+
: new Map();
|
|
2989
3429
|
this.integerViews.clear();
|
|
2990
3430
|
this.fieldPointerTags.clear();
|
|
2991
3431
|
this.integerArrayBindings.clear();
|
|
2992
|
-
this.
|
|
3432
|
+
this.loopArrayBorrows =
|
|
3433
|
+
this.debug === null
|
|
3434
|
+
? findLoopArrayBorrows(fn, this.callLifetimes, this.referenceEffects, this.optionalArrayReads)
|
|
3435
|
+
: new Set();
|
|
3436
|
+
this.localArrayReads = findLocalArrayReads(fn, this.fnByName, this.unionsById, this.referenceEffects.functions, this.callLifetimes, this.optionalArrayReads, this.loopArrayBorrows);
|
|
2993
3437
|
this.callArrayReads = findCallArrayReads(fn, this.optionalArrayReads, this.referenceEffects.functions, this.callLifetimes);
|
|
3438
|
+
this.scalarStringSlices =
|
|
3439
|
+
this.debug === null ? findScalarStringSlices(fn, this.callLifetimes) : new Map();
|
|
2994
3440
|
this.mapReadLifetimes = findMapReadLifetimes(fn, this.unionsById, this.callLifetimes);
|
|
2995
3441
|
this.localStackUnions = findLocalStackUnions(fn, this.callLifetimes, this.unionsById);
|
|
2996
|
-
this.localUnionStorageProofs = findLocalUnionStorage(fn, this.callLifetimes, this.unionsById);
|
|
3442
|
+
this.localUnionStorageProofs = findLocalUnionStorage(fn, this.callLifetimes, this.unionsById, (e) => this.nullableFieldGet(e) !== null);
|
|
2997
3443
|
this.localUnionStorage = new Map();
|
|
2998
3444
|
this.integerRanges = analyzeIntegerRanges(numericFn);
|
|
2999
3445
|
this.bytesBounds = findBytesBounds(numericFn, this.integerRanges);
|
|
@@ -3052,7 +3498,7 @@ export class LlEmitter {
|
|
|
3052
3498
|
B.line(`${value} = load double, ptr @${mangleGlobal(global.id)}`);
|
|
3053
3499
|
this.storeIntegerView(global.id, { name: value, type: global.type });
|
|
3054
3500
|
}
|
|
3055
|
-
for (const local of
|
|
3501
|
+
for (const local of this.currentLocals.values()) {
|
|
3056
3502
|
// Boxed locals' slots hold their capture BOX (a ptr); captured
|
|
3057
3503
|
// (env-borrowed) locals bind the incoming box below. A caught-typed
|
|
3058
3504
|
// local is a catch binding: its slot holds the ScrCaught snapshot
|
|
@@ -3101,6 +3547,21 @@ export class LlEmitter {
|
|
|
3101
3547
|
// params (callees own their params — callers passed +1). Boxed params
|
|
3102
3548
|
// allocate the shared binding and move the raw value in.
|
|
3103
3549
|
const fnScope = [];
|
|
3550
|
+
for (const id of this.lazyCaptureBoxes.keys()) {
|
|
3551
|
+
const cache = B.slot();
|
|
3552
|
+
B.entryAllocas.push(`${cache} = alloca ptr ; lazy box ${this.currentLocals.get(id).name}`);
|
|
3553
|
+
B.line(`store ptr null, ptr ${cache}`);
|
|
3554
|
+
fnScope.push({ slot: cache, type: this.currentLocals.get(id).type, boxed: true });
|
|
3555
|
+
this.lazyCaptureBoxes.set(id, cache);
|
|
3556
|
+
// Environments that finish during the call can share an immortal
|
|
3557
|
+
// frame box. Its payload owner is released with the function scope.
|
|
3558
|
+
const stack = this.stackCaptures.emit(this, id, this.currentLocals.get(id).type);
|
|
3559
|
+
if (stack) {
|
|
3560
|
+
this.lazyStackBoxes.set(id, stack.box);
|
|
3561
|
+
if (stack.owner)
|
|
3562
|
+
fnScope.push(stack.owner);
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3104
3565
|
const borrowed = this.callLifetimes.borrowed.get(fn.name);
|
|
3105
3566
|
for (const [index, p] of fn.params.entries()) {
|
|
3106
3567
|
const local = this.currentLocals.get(p.localId);
|
|
@@ -3129,6 +3590,8 @@ export class LlEmitter {
|
|
|
3129
3590
|
B.line(`call void @scr_stack_enter(ptr %source_frame, ptr ${this.cstr(frame)})`);
|
|
3130
3591
|
B.returnEpilogue = `call void @scr_stack_leave(ptr %source_frame)`;
|
|
3131
3592
|
}
|
|
3593
|
+
if (this.mod.workers)
|
|
3594
|
+
this.emitPendingCheck();
|
|
3132
3595
|
this.emitStmts(fn.body);
|
|
3133
3596
|
// Implicit exit of a void function: release the function scope unless
|
|
3134
3597
|
// the body already terminated its final block (return, or a throw
|
|
@@ -3279,6 +3742,20 @@ export class LlEmitter {
|
|
|
3279
3742
|
switch (s.kind) {
|
|
3280
3743
|
case "varDecl": {
|
|
3281
3744
|
const b = this.binding(s.localId);
|
|
3745
|
+
const slice = this.scalarStringSlices.get(s.localId);
|
|
3746
|
+
if (slice) {
|
|
3747
|
+
const snapshot = emitStringSliceSnapshot(this, slice);
|
|
3748
|
+
this.stringSlices.set(s.localId, snapshot);
|
|
3749
|
+
this.scopes[this.scopes.length - 1].push({ slot: snapshot.source, type: STRING });
|
|
3750
|
+
break;
|
|
3751
|
+
}
|
|
3752
|
+
const split = this.privateSplitLocals.get(s.localId);
|
|
3753
|
+
if (split) {
|
|
3754
|
+
const snapshot = storeSplitSnapshot(this, emitSplitSnapshot(this, split));
|
|
3755
|
+
this.storedSplits.set(s.localId, snapshot);
|
|
3756
|
+
this.scopes[this.scopes.length - 1].push({ slot: snapshot.sourceSlot, type: STRING }, { slot: snapshot.separatorSlot, type: STRING });
|
|
3757
|
+
break;
|
|
3758
|
+
}
|
|
3282
3759
|
const localUnion = this.localStackUnions.get(s.localId);
|
|
3283
3760
|
if (localUnion) {
|
|
3284
3761
|
const union = emitStackUnion(this, localUnion);
|
|
@@ -3321,6 +3798,19 @@ export class LlEmitter {
|
|
|
3321
3798
|
break;
|
|
3322
3799
|
}
|
|
3323
3800
|
if (b.kind === "boxed") {
|
|
3801
|
+
const stack = this.stackCaptures.emit(this, s.localId, b.type);
|
|
3802
|
+
if (stack) {
|
|
3803
|
+
B.line(`store ptr ${stack.box}, ptr ${b.slot}`);
|
|
3804
|
+
if (stack.owner)
|
|
3805
|
+
this.scopes[this.scopes.length - 1].push(stack.owner);
|
|
3806
|
+
if (s.init) {
|
|
3807
|
+
const value = this.emitExpr(s.init);
|
|
3808
|
+
if (isRefCounted(value.type))
|
|
3809
|
+
this.moveTemp(value);
|
|
3810
|
+
this.boxSet(stack.box, b.type, value.name);
|
|
3811
|
+
}
|
|
3812
|
+
break;
|
|
3813
|
+
}
|
|
3324
3814
|
// Box FIRST, then evaluate the initializer: a named function
|
|
3325
3815
|
// expression's closure captures this box during init evaluation.
|
|
3326
3816
|
// A SCALAR TDZ box rides an ARR-kind box: the value lives in a
|
|
@@ -3357,13 +3847,16 @@ export class LlEmitter {
|
|
|
3357
3847
|
}
|
|
3358
3848
|
break;
|
|
3359
3849
|
}
|
|
3360
|
-
|
|
3850
|
+
if (this.loopArrayBorrows.has(s) && s.init.kind === "arrayGet") {
|
|
3851
|
+
emitBorrowedArrayRead(this, s.init, b.slot);
|
|
3852
|
+
break;
|
|
3853
|
+
}
|
|
3854
|
+
// A stable lexical alias cannot outlive its unchanged local owner.
|
|
3361
3855
|
// Whole-value uses still retain their own copies, so returning or
|
|
3362
3856
|
// storing the alias preserves the ordinary heap representation.
|
|
3363
3857
|
// Boxed captures, reassigned sources and projections need ownership.
|
|
3364
3858
|
if (b.kind === "local" &&
|
|
3365
3859
|
b.local &&
|
|
3366
|
-
!b.local.mutable &&
|
|
3367
3860
|
isRefCounted(b.type) &&
|
|
3368
3861
|
this.stableCallBindings.has(s.localId) &&
|
|
3369
3862
|
this.canBorrowCallArgument(s.init)) {
|
|
@@ -3428,9 +3921,7 @@ export class LlEmitter {
|
|
|
3428
3921
|
const acc = elemAccess(s.arr.type.elem);
|
|
3429
3922
|
if (acc === "ref")
|
|
3430
3923
|
this.moveTemp(v);
|
|
3431
|
-
|
|
3432
|
-
this.declare(`declare void @scr_arr_set_${acc}(ptr, double, ${argTy === "i1" ? "i1 zeroext" : argTy})`);
|
|
3433
|
-
B.line(`call void @scr_arr_set_${acc}(ptr ${arr.name}, double ${idx.name}, ${argTy} ${v.name})`);
|
|
3924
|
+
emitDenseArraySet(this, arr.name, idx, s.index, acc, s.arr.type.elem, v.name);
|
|
3434
3925
|
break;
|
|
3435
3926
|
}
|
|
3436
3927
|
case "arraySetLength": {
|
|
@@ -3478,6 +3969,12 @@ export class LlEmitter {
|
|
|
3478
3969
|
// release — a release can trigger a cycle collection, which must
|
|
3479
3970
|
// never see a heap edge whose count was already given up).
|
|
3480
3971
|
// Classes and records share the struct layout, so one emission.
|
|
3972
|
+
const nullable = s.kind === "fieldSet" ? this.nullableFields.get(s.className, s.field) : null;
|
|
3973
|
+
if (nullable && s.kind === "fieldSet") {
|
|
3974
|
+
const obj = this.emitExpr(s.obj);
|
|
3975
|
+
this.emitNullableFieldStore(obj.name, s.className, s.field, nullable, s.value);
|
|
3976
|
+
break;
|
|
3977
|
+
}
|
|
3481
3978
|
const obj = isRefCounted(s.value.type)
|
|
3482
3979
|
? this.emitExpr(s.obj)
|
|
3483
3980
|
: this.emitStableReceiver(s.obj, [s.value]);
|
|
@@ -3561,7 +4058,7 @@ export class LlEmitter {
|
|
|
3561
4058
|
for (const f of shape.fields) {
|
|
3562
4059
|
const lit = this.internLiteral(f.name);
|
|
3563
4060
|
const hit = B.tmp();
|
|
3564
|
-
B.line(`${hit} = call zeroext i1 @
|
|
4061
|
+
B.line(`${hit} = call zeroext i1 @sc_str_eq(ptr ${key.name}, ptr ${lit}) ; ${llvmCommentText(f.name)}`);
|
|
3565
4062
|
const lh = B.newLabel("rks.h");
|
|
3566
4063
|
const ln = B.newLabel("rks.n");
|
|
3567
4064
|
B.condBr(hit, lh, ln);
|
|
@@ -3617,7 +4114,7 @@ export class LlEmitter {
|
|
|
3617
4114
|
// typeEquals(f.type, iv) — the frontend fences everything else.
|
|
3618
4115
|
const lit = this.internLiteral(f.name);
|
|
3619
4116
|
const hit = B.tmp();
|
|
3620
|
-
B.line(`${hit} = call zeroext i1 @
|
|
4117
|
+
B.line(`${hit} = call zeroext i1 @sc_str_eq(ptr ${key.name}, ptr ${lit}) ; ${llvmCommentText(f.name)}`);
|
|
3621
4118
|
const lh = B.newLabel("rks.h");
|
|
3622
4119
|
const ln = B.newLabel("rks.n");
|
|
3623
4120
|
B.condBr(hit, lh, ln);
|
|
@@ -3694,11 +4191,13 @@ export class LlEmitter {
|
|
|
3694
4191
|
break;
|
|
3695
4192
|
}
|
|
3696
4193
|
case "while": {
|
|
4194
|
+
const workerBudget = this.workerLoopBudget();
|
|
3697
4195
|
const lc = B.newLabel("loop.c");
|
|
3698
4196
|
const lb = B.newLabel("loop.b");
|
|
3699
4197
|
const le = B.newLabel("loop.e");
|
|
3700
4198
|
B.br(lc);
|
|
3701
4199
|
B.startBlock(lc);
|
|
4200
|
+
this.emitWorkerLoopCheck(workerBudget);
|
|
3702
4201
|
B.condBr(this.emitCondition(s.cond), lb, le);
|
|
3703
4202
|
B.startBlock(lb);
|
|
3704
4203
|
this.jumpTargets.push({
|
|
@@ -3717,12 +4216,14 @@ export class LlEmitter {
|
|
|
3717
4216
|
break;
|
|
3718
4217
|
}
|
|
3719
4218
|
case "doWhile": {
|
|
4219
|
+
const workerBudget = this.workerLoopBudget();
|
|
3720
4220
|
// Body first (runs at least once); continue jumps to the CONDITION.
|
|
3721
4221
|
const lb = B.newLabel("loop.b");
|
|
3722
4222
|
const lc = B.newLabel("loop.c");
|
|
3723
4223
|
const le = B.newLabel("loop.e");
|
|
3724
4224
|
B.br(lb);
|
|
3725
4225
|
B.startBlock(lb);
|
|
4226
|
+
this.emitWorkerLoopCheck(workerBudget);
|
|
3726
4227
|
this.jumpTargets.push({
|
|
3727
4228
|
kind: "loop",
|
|
3728
4229
|
brkLabel: le,
|
|
@@ -3751,6 +4252,33 @@ export class LlEmitter {
|
|
|
3751
4252
|
const countedLoop = !integerLoop && this.countedLoopsEnabled
|
|
3752
4253
|
? matchIntegerCountedForLoop(s, this.numericLocals, this.integerRanges)
|
|
3753
4254
|
: null;
|
|
4255
|
+
const window = countedLoop && this.byteWindowsEnabled
|
|
4256
|
+
? matchByteWindow(s, countedLoop, this.numericLocals, this.captureIds, this.integerRanges, this.byteWindowEntries.get(s) ?? new Set())
|
|
4257
|
+
: null;
|
|
4258
|
+
if (window) {
|
|
4259
|
+
const valid = emitByteWindowGuard(this, window);
|
|
4260
|
+
const fast = B.newLabel("bytes.window.fast"), slow = B.newLabel("bytes.window.slow"), done = B.newLabel("bytes.window.done");
|
|
4261
|
+
B.condBr(valid, fast, slow);
|
|
4262
|
+
const originalRanges = this.integerRanges, originalBounds = this.bytesBounds;
|
|
4263
|
+
this.byteWindowsEnabled = false;
|
|
4264
|
+
try {
|
|
4265
|
+
B.startBlock(slow);
|
|
4266
|
+
this.emitStmt(s);
|
|
4267
|
+
B.br(done);
|
|
4268
|
+
B.startBlock(fast);
|
|
4269
|
+
this.integerRanges = window.ranges;
|
|
4270
|
+
this.bytesBounds = new Set([...originalBounds, ...window.bounds]);
|
|
4271
|
+
this.emitStmt(s);
|
|
4272
|
+
B.br(done);
|
|
4273
|
+
}
|
|
4274
|
+
finally {
|
|
4275
|
+
this.integerRanges = originalRanges;
|
|
4276
|
+
this.bytesBounds = originalBounds;
|
|
4277
|
+
this.byteWindowsEnabled = true;
|
|
4278
|
+
}
|
|
4279
|
+
B.startBlock(done);
|
|
4280
|
+
break;
|
|
4281
|
+
}
|
|
3754
4282
|
let countedJoin = null;
|
|
3755
4283
|
if (countedLoop?.guarded) {
|
|
3756
4284
|
const bound = this.emitExpr(countedLoop.limit);
|
|
@@ -3839,8 +4367,10 @@ export class LlEmitter {
|
|
|
3839
4367
|
};
|
|
3840
4368
|
const lu = s.update || capturedLets.length > 0 ? B.newLabel("loop.u") : lc;
|
|
3841
4369
|
freshenBindings();
|
|
4370
|
+
const workerBudget = this.workerLoopBudget();
|
|
3842
4371
|
B.br(lc);
|
|
3843
4372
|
B.startBlock(lc);
|
|
4373
|
+
this.emitWorkerLoopCheck(workerBudget);
|
|
3844
4374
|
if (integerLoop && integerSlot) {
|
|
3845
4375
|
const receiver = this.emitStableReceiver(integerLoop.limitReceiver, []);
|
|
3846
4376
|
const lenPtr = B.tmp();
|
|
@@ -3922,23 +4452,52 @@ export class LlEmitter {
|
|
|
3922
4452
|
if (s.iterable.type.kind !== "array")
|
|
3923
4453
|
throw new LlvmUnsupportedError(`forOf:${s.iterable.type.kind}`, s.loc);
|
|
3924
4454
|
const elem = s.iterable.type.elem;
|
|
3925
|
-
const
|
|
3926
|
-
const
|
|
3927
|
-
|
|
3928
|
-
|
|
4455
|
+
const stored = s.iterable.kind === "varRef" ? this.storedSplits.get(s.iterable.localId) : undefined;
|
|
4456
|
+
const direct = this.streamingSplitsEnabled ? stringSplit(s.iterable) : null;
|
|
4457
|
+
const snapshot = stored
|
|
4458
|
+
? loadSplitSnapshot(this, stored)
|
|
4459
|
+
: direct
|
|
4460
|
+
? emitSplitSnapshot(this, direct)
|
|
4461
|
+
: null;
|
|
4462
|
+
const arr = snapshot ? null : this.emitExpr(s.iterable);
|
|
4463
|
+
const cursor = snapshot ? emitSplitCursor(this, snapshot) : null;
|
|
4464
|
+
const scratch = snapshot ? emitSplitScratch(this) : null;
|
|
4465
|
+
const deferred = snapshot && canDeferSplitPiece(s, this.currentLocals.get(s.localId));
|
|
4466
|
+
const spanLength = deferred ? B.slot() : null;
|
|
4467
|
+
if (spanLength)
|
|
4468
|
+
B.entryAllocas.push(`${spanLength} = alloca ${this.sizeType}`);
|
|
4469
|
+
const idxSlot = snapshot ? null : B.slot();
|
|
4470
|
+
if (idxSlot) {
|
|
4471
|
+
B.entryAllocas.push(`${idxSlot} = alloca double`);
|
|
4472
|
+
B.line(`store double ${f64Lit(0)}, ptr ${idxSlot}`);
|
|
4473
|
+
}
|
|
3929
4474
|
const lc = B.newLabel("fof.c");
|
|
3930
4475
|
const lb = B.newLabel("fof.b");
|
|
3931
4476
|
const lu = B.newLabel("fof.u");
|
|
3932
4477
|
const le = B.newLabel("fof.e");
|
|
4478
|
+
const workerBudget = this.workerLoopBudget();
|
|
3933
4479
|
B.br(lc);
|
|
3934
4480
|
B.startBlock(lc);
|
|
3935
|
-
|
|
3936
|
-
const len = B.tmp();
|
|
4481
|
+
this.emitWorkerLoopCheck(workerBudget);
|
|
3937
4482
|
const inBounds = B.tmp();
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
4483
|
+
let cur;
|
|
4484
|
+
if (snapshot && cursor && scratch) {
|
|
4485
|
+
cur = spanLength
|
|
4486
|
+
? emitSplitSpanNext(this, snapshot, cursor, spanLength)
|
|
4487
|
+
: emitSplitNext(this, snapshot, cursor, scratch);
|
|
4488
|
+
B.line(`${inBounds} = icmp ne ptr ${cur}, null`);
|
|
4489
|
+
}
|
|
4490
|
+
else {
|
|
4491
|
+
const i = B.tmp(), lenPtr = B.tmp(), rawLen = B.tmp(), len = B.tmp();
|
|
4492
|
+
B.line(`${i} = load double, ptr ${idxSlot}`);
|
|
4493
|
+
// The inline form of scr_arr_len, like the `length` intrinsic.
|
|
4494
|
+
B.line(`${lenPtr} = getelementptr inbounds %ScrArr, ptr ${arr.name}, i32 0, i32 1`);
|
|
4495
|
+
this.markMemoryPointer(lenPtr, "array:header");
|
|
4496
|
+
B.line(`${rawLen} = load ${this.sizeType}, ptr ${lenPtr}${this.fieldAliasAttachment(lenPtr)}`);
|
|
4497
|
+
B.line(`${len} = uitofp ${this.sizeType} ${rawLen} to double`);
|
|
4498
|
+
B.line(`${inBounds} = fcmp olt double ${i}, ${len}`);
|
|
4499
|
+
cur = i;
|
|
4500
|
+
}
|
|
3942
4501
|
B.condBr(inBounds, lb, le);
|
|
3943
4502
|
B.startBlock(lb);
|
|
3944
4503
|
this.jumpTargets.push({
|
|
@@ -3956,11 +4515,18 @@ export class LlEmitter {
|
|
|
3956
4515
|
this.scopes.push([]);
|
|
3957
4516
|
const localInfo = this.currentLocals.get(s.localId);
|
|
3958
4517
|
const slot = `%${mangleLocal(s.localId)}`;
|
|
3959
|
-
const
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
4518
|
+
const borrowedElement = !snapshot && this.loopArrayBorrows.has(s);
|
|
4519
|
+
if (!snapshot) {
|
|
4520
|
+
const acc = elemAccess(elem);
|
|
4521
|
+
const getter = borrowedElement ? "scr_arr_borrow_ref" : `scr_arr_get_${acc}`;
|
|
4522
|
+
cur = emitDenseArrayGet(this, arr.name, { name: cur, type: { kind: "f64" } }, undefined, acc, elem, getter, !borrowedElement);
|
|
4523
|
+
}
|
|
4524
|
+
if (spanLength) {
|
|
4525
|
+
const length = B.tmp();
|
|
4526
|
+
B.line(`${length} = load ${this.sizeType}, ptr ${spanLength}`);
|
|
4527
|
+
this.splitSpans.set(s.localId, { bytes: cur, length, scratch: scratch });
|
|
4528
|
+
cur = "null";
|
|
4529
|
+
}
|
|
3964
4530
|
if (localInfo?.boxed) {
|
|
3965
4531
|
// Captured loop variable: a fresh box per iteration, matching the
|
|
3966
4532
|
// fresh const binding. The box takes ownership of a ref element's
|
|
@@ -3973,10 +4539,12 @@ export class LlEmitter {
|
|
|
3973
4539
|
}
|
|
3974
4540
|
else {
|
|
3975
4541
|
B.line(`store ${this.llType(elem)} ${cur}, ptr ${slot}`);
|
|
3976
|
-
if (isRefCounted(elem))
|
|
4542
|
+
if (isRefCounted(elem) && !borrowedElement)
|
|
3977
4543
|
this.scopes[this.scopes.length - 1].push({ slot, type: elem });
|
|
3978
4544
|
}
|
|
3979
4545
|
this.emitStmts(s.body);
|
|
4546
|
+
if (spanLength)
|
|
4547
|
+
this.splitSpans.delete(s.localId);
|
|
3980
4548
|
const endedWithJump = endsWithJump(s.body);
|
|
3981
4549
|
const scope = this.scopes.pop();
|
|
3982
4550
|
if (!endedWithJump)
|
|
@@ -3984,11 +4552,12 @@ export class LlEmitter {
|
|
|
3984
4552
|
this.jumpTargets.pop();
|
|
3985
4553
|
B.br(lu);
|
|
3986
4554
|
B.startBlock(lu);
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
4555
|
+
if (idxSlot) {
|
|
4556
|
+
const i2 = B.tmp(), i3 = B.tmp();
|
|
4557
|
+
B.line(`${i2} = load double, ptr ${idxSlot}`);
|
|
4558
|
+
B.line(`${i3} = fadd double ${i2}, ${f64Lit(1)}`);
|
|
4559
|
+
B.line(`store double ${i3}, ptr ${idxSlot}`);
|
|
4560
|
+
}
|
|
3992
4561
|
B.br(lc);
|
|
3993
4562
|
B.startBlock(le);
|
|
3994
4563
|
break;
|
|
@@ -4346,7 +4915,7 @@ export class LlEmitter {
|
|
|
4346
4915
|
const hit = B.tmp();
|
|
4347
4916
|
if (c.test.type.kind === "string") {
|
|
4348
4917
|
this.declare(`declare zeroext i1 @scr_str_eq(ptr, ptr)`);
|
|
4349
|
-
B.line(`${hit} = call zeroext i1 @
|
|
4918
|
+
B.line(`${hit} = call zeroext i1 @sc_str_eq(ptr ${disc.name}, ptr ${t.name})`);
|
|
4350
4919
|
}
|
|
4351
4920
|
else if (c.test.type.kind === "bool") {
|
|
4352
4921
|
B.line(`${hit} = icmp eq i1 ${disc.name}, ${t.name}`);
|
|
@@ -4517,10 +5086,20 @@ export class LlEmitter {
|
|
|
4517
5086
|
if (e.kind === "strLit")
|
|
4518
5087
|
return { name: this.internLiteral(e.value), type: e.type };
|
|
4519
5088
|
if (e.kind === "varRef") {
|
|
5089
|
+
this.materializeSplitLocal(e.localId);
|
|
4520
5090
|
const binding = this.binding(e.localId);
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
5091
|
+
if (binding.kind === "boxed") {
|
|
5092
|
+
// A projection consumes this pointer before any user code runs.
|
|
5093
|
+
// Keep TDZ checks and caught-value conversion on the owned path.
|
|
5094
|
+
if (!this.canBorrowReceiver(e))
|
|
5095
|
+
return this.emitExpr(e);
|
|
5096
|
+
const box = this.loadBox(binding.slot);
|
|
5097
|
+
const payload = this.B.tmp();
|
|
5098
|
+
const value = this.B.tmp();
|
|
5099
|
+
this.B.line(`${payload} = getelementptr inbounds %ScrBox, ptr ${box}, i32 0, i32 5`);
|
|
5100
|
+
this.B.line(`${value} = load ptr, ptr ${payload}`);
|
|
5101
|
+
return { name: value, type: e.type };
|
|
5102
|
+
}
|
|
4524
5103
|
if (binding.kind === "global")
|
|
4525
5104
|
this.checkGlobalTdz(e.localId);
|
|
4526
5105
|
const value = this.B.tmp();
|
|
@@ -4534,17 +5113,26 @@ export class LlEmitter {
|
|
|
4534
5113
|
this.ownSlot(result.owner.slot, result.owner.type);
|
|
4535
5114
|
return result.value;
|
|
4536
5115
|
}
|
|
5116
|
+
const arrayRead = this.optionalArrayReads.get(e);
|
|
5117
|
+
if (arrayRead)
|
|
5118
|
+
return emitProjectedArrayRead(this, arrayRead);
|
|
4537
5119
|
if (canStackUnion(e, this.unionsById))
|
|
4538
5120
|
return emitStackUnion(this, e).value;
|
|
4539
5121
|
if (e.kind === "unionNarrow") {
|
|
4540
|
-
const union = this.
|
|
5122
|
+
const union = this.emitUnionProjection(e.value);
|
|
4541
5123
|
return { name: this.unionPeek(union.name), type: e.type };
|
|
4542
5124
|
}
|
|
5125
|
+
// A nullable-pointer field has no box to borrow. Projections use
|
|
5126
|
+
// emitUnionProjection; other borrowers get an owned heap box.
|
|
5127
|
+
if (this.nullableFieldGet(e))
|
|
5128
|
+
return this.emitExpr(e);
|
|
4543
5129
|
if (e.kind === "recordGet" || e.kind === "fieldGet") {
|
|
4544
5130
|
const receiver = this.emitReadReceiver(e.obj);
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
: this.
|
|
5131
|
+
if (e.kind === "recordGet") {
|
|
5132
|
+
const { ptr, type } = this.recordFieldPtr(receiver.name, e.shapeId, e.field);
|
|
5133
|
+
return { name: this.loadRecordField(ptr, type), type: e.type };
|
|
5134
|
+
}
|
|
5135
|
+
const { ptr, type } = this.classFieldPtr(receiver.name, e.className, e.field);
|
|
4548
5136
|
return { name: this.loadField(ptr, type), type: e.type };
|
|
4549
5137
|
}
|
|
4550
5138
|
if (e.kind === "ternary" && this.canBorrowReceiver(e.then) && this.canBorrowReceiver(e.else_)) {
|
|
@@ -4574,10 +5162,163 @@ export class LlEmitter {
|
|
|
4574
5162
|
}
|
|
4575
5163
|
return this.emitExpr(e);
|
|
4576
5164
|
}
|
|
5165
|
+
/** A private stack union source with an independently owned payload. */
|
|
5166
|
+
canStackReceiver(e) {
|
|
5167
|
+
return this.isStackUnionSource(e);
|
|
5168
|
+
}
|
|
5169
|
+
/** True for union sources that emitReadReceiver can produce as a private
|
|
5170
|
+
* stack box with an independently owned payload (no heap union). */
|
|
5171
|
+
isStackUnionSource(e) {
|
|
5172
|
+
if (e.type.kind !== "union")
|
|
5173
|
+
return false;
|
|
5174
|
+
return (canStackUnion(e, this.unionsById) ||
|
|
5175
|
+
matchMapRead(e, this.unionsById) !== null ||
|
|
5176
|
+
this.optionalArrayReads.get(e) !== null);
|
|
5177
|
+
}
|
|
5178
|
+
/** The nullable-pointer representation behind a union-typed class field
|
|
5179
|
+
* read, or null for every other expression. */
|
|
5180
|
+
nullableFieldGet(e) {
|
|
5181
|
+
if (e.kind !== "fieldGet" || e.type.kind !== "union")
|
|
5182
|
+
return null;
|
|
5183
|
+
return this.nullableFields.get(e.className, e.field);
|
|
5184
|
+
}
|
|
5185
|
+
/** A union operand consumed only by a projection (tag test, payload
|
|
5186
|
+
* extraction, arm compare, truthiness). A nullable-pointer field read
|
|
5187
|
+
* becomes a private stack box whose payload is borrowed from the field:
|
|
5188
|
+
* the consumer must finish before any later expression can store to the
|
|
5189
|
+
* field, and must never retain, release, or store the box. Every other
|
|
5190
|
+
* operand takes the ordinary borrowed-receiver path. */
|
|
5191
|
+
emitUnionProjection(e) {
|
|
5192
|
+
const nullable = this.nullableFieldGet(e);
|
|
5193
|
+
if (!nullable || e.kind !== "fieldGet")
|
|
5194
|
+
return this.emitReadReceiver(e);
|
|
5195
|
+
const B = this.B;
|
|
5196
|
+
const receiver = this.emitReadReceiver(e.obj);
|
|
5197
|
+
const { ptr } = this.classFieldPtr(receiver.name, e.className, e.field);
|
|
5198
|
+
const p = B.tmp();
|
|
5199
|
+
B.line(`${p} = load ptr, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
5200
|
+
return { name: this.stackNullableBox(p, nullable), type: e.type };
|
|
5201
|
+
}
|
|
5202
|
+
/** A private stack box over a nullable-pointer field read whose payload
|
|
5203
|
+
* is retained into the current frame: an independent owner, so the box
|
|
5204
|
+
* may outlive later stores to the field (call arguments, local copies).
|
|
5205
|
+
* The box itself must still never reach an RC entry point. */
|
|
5206
|
+
emitOwnedNullableStack(e) {
|
|
5207
|
+
const nullable = this.nullableFieldGet(e);
|
|
5208
|
+
if (!nullable || e.kind !== "fieldGet")
|
|
5209
|
+
throw new InternalCompilerError("llvm emitter bug: owned nullable stack of non-field");
|
|
5210
|
+
const receiver = this.emitReadReceiver(e.obj);
|
|
5211
|
+
const { ptr } = this.classFieldPtr(receiver.name, e.className, e.field);
|
|
5212
|
+
const p = this.B.tmp();
|
|
5213
|
+
this.B.line(`${p} = load ptr, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
5214
|
+
const owner = this.own({ name: this.retainValue(p, nullable.arm), type: nullable.arm });
|
|
5215
|
+
return { box: this.stackNullableBox(owner.name, nullable), owner };
|
|
5216
|
+
}
|
|
5217
|
+
/** A private stack box over a borrowed nullable pointer. */
|
|
5218
|
+
stackNullableBox(p, nullable) {
|
|
5219
|
+
const B = this.B;
|
|
5220
|
+
const box = B.slot();
|
|
5221
|
+
B.entryAllocas.push(`${box} = alloca %ScrUnion`);
|
|
5222
|
+
const isNull = B.tmp(), tag = B.tmp(), tagPtr = B.tmp(), slot = B.tmp();
|
|
5223
|
+
B.line(`${isNull} = icmp eq ptr ${p}, null`);
|
|
5224
|
+
B.line(`${tag} = select i1 ${isNull}, i32 ${nullable.unitTag}, i32 ${nullable.refTag}`);
|
|
5225
|
+
B.line(`${tagPtr} = getelementptr inbounds %ScrUnion, ptr ${box}, i64 0, i32 1`);
|
|
5226
|
+
B.line(`store i32 ${tag}, ptr ${tagPtr}`);
|
|
5227
|
+
B.line(`${slot} = getelementptr inbounds %ScrUnion, ptr ${box}, i64 0, i32 5`);
|
|
5228
|
+
B.line(`store i64 0, ptr ${slot}`);
|
|
5229
|
+
B.line(`store ptr ${p}, ptr ${slot}`);
|
|
5230
|
+
return box;
|
|
5231
|
+
}
|
|
5232
|
+
/** The owned (+1) union value of a nullable-pointer slot: the unit arm's
|
|
5233
|
+
* immortal instance, or a fresh heap box around a retained instance. */
|
|
5234
|
+
nullableToOwnedUnion(p, nullable) {
|
|
5235
|
+
const B = this.B;
|
|
5236
|
+
const slot = B.slot();
|
|
5237
|
+
B.entryAllocas.push(`${slot} = alloca ptr`);
|
|
5238
|
+
const isNull = B.tmp();
|
|
5239
|
+
const unit = B.newLabel("nf.u"), ref = B.newLabel("nf.r"), join = B.newLabel("nf.j");
|
|
5240
|
+
B.line(`${isNull} = icmp eq ptr ${p}, null`);
|
|
5241
|
+
B.condBr(isNull, unit, ref);
|
|
5242
|
+
B.startBlock(unit);
|
|
5243
|
+
B.line(`store ptr ${this.unitInstanceRef(nullable.unionId, nullable.unitTag)}, ptr ${slot}`);
|
|
5244
|
+
B.br(join);
|
|
5245
|
+
B.startBlock(ref);
|
|
5246
|
+
const owned = this.retainValue(p, nullable.arm);
|
|
5247
|
+
const box = this.unionNewOwned(nullable.refTag, { name: owned, type: nullable.arm });
|
|
5248
|
+
B.line(`store ptr ${box}, ptr ${slot}`);
|
|
5249
|
+
B.br(join);
|
|
5250
|
+
B.startBlock(join);
|
|
5251
|
+
const t = B.tmp();
|
|
5252
|
+
B.line(`${t} = load ptr, ptr ${slot}`);
|
|
5253
|
+
return t;
|
|
5254
|
+
}
|
|
5255
|
+
/** The owned (+1) instance pointer (NULL for the unit arm) of a union
|
|
5256
|
+
* box, which stays owned by its existing owner. */
|
|
5257
|
+
unionToNullable(u, nullable) {
|
|
5258
|
+
const B = this.B;
|
|
5259
|
+
const tag = this.unionTag(u);
|
|
5260
|
+
const isRef = B.tmp(), sel = B.tmp();
|
|
5261
|
+
B.line(`${isRef} = icmp eq i32 ${tag}, ${nullable.refTag}`);
|
|
5262
|
+
const payload = this.unionPeek(u);
|
|
5263
|
+
B.line(`${sel} = select i1 ${isRef}, ptr ${payload}, ptr null`);
|
|
5264
|
+
return this.retainValue(sel, nullable.arm);
|
|
5265
|
+
}
|
|
5266
|
+
/** `obj.f = value` for a nullable-pointer field: the new instance pointer
|
|
5267
|
+
* is produced without a union box where the value is a wrap, and as a
|
|
5268
|
+
* retained payload of a borrowed box otherwise. Unlink-then-release like
|
|
5269
|
+
* every field store. */
|
|
5270
|
+
emitNullableFieldStore(obj, className, field, nullable, value) {
|
|
5271
|
+
const B = this.B;
|
|
5272
|
+
let next;
|
|
5273
|
+
if (value.kind === "unionWrap" &&
|
|
5274
|
+
value.unionId === nullable.unionId &&
|
|
5275
|
+
(value.tag === nullable.refTag
|
|
5276
|
+
? typeEquals(value.value.type, nullable.arm)
|
|
5277
|
+
: value.tag === nullable.unitTag)) {
|
|
5278
|
+
if (value.tag === nullable.refTag) {
|
|
5279
|
+
const inner = this.emitExpr(value.value);
|
|
5280
|
+
this.moveTemp(inner);
|
|
5281
|
+
next = inner.name;
|
|
5282
|
+
}
|
|
5283
|
+
else {
|
|
5284
|
+
// Unit payloads carry nothing; a void payload runs for effects.
|
|
5285
|
+
if (!isUnitType(value.value.type))
|
|
5286
|
+
this.emitExpr(value.value);
|
|
5287
|
+
next = "null";
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
else if (value.kind === "varRef" && !this.canBorrowReceiver(value)) {
|
|
5291
|
+
next = this.unionToNullable(this.emitExpr(value).name, nullable);
|
|
5292
|
+
}
|
|
5293
|
+
else {
|
|
5294
|
+
next = this.unionToNullable(this.emitUnionProjection(value).name, nullable);
|
|
5295
|
+
}
|
|
5296
|
+
const { ptr } = this.classFieldPtr(obj, className, field);
|
|
5297
|
+
const old = B.tmp();
|
|
5298
|
+
B.line(`${old} = load ptr, ptr ${ptr}`);
|
|
5299
|
+
B.line(`store ptr ${next}, ptr ${ptr}${this.fieldAliasAttachment(ptr)}`);
|
|
5300
|
+
this.releaseValue(old, nullable.arm);
|
|
5301
|
+
}
|
|
5302
|
+
/** A union binding whose slot may hold a private stack box: stack locals
|
|
5303
|
+
* and projection-only parameters (callers may pass stack boxes). Such a
|
|
5304
|
+
* box must only be projected: never retained, released, or stored. */
|
|
5305
|
+
isStackUnionLocal(localId) {
|
|
5306
|
+
return (this.projectedParameters.has(localId) ||
|
|
5307
|
+
this.localStackUnions.has(localId) ||
|
|
5308
|
+
this.localUnionStorage.has(localId) ||
|
|
5309
|
+
this.localArrayReads.has(localId) ||
|
|
5310
|
+
this.mapReadLifetimes.locals.has(localId));
|
|
5311
|
+
}
|
|
4577
5312
|
canBorrowReceiver(e) {
|
|
4578
5313
|
switch (e.kind) {
|
|
4579
|
-
case "varRef":
|
|
4580
|
-
|
|
5314
|
+
case "varRef": {
|
|
5315
|
+
const binding = this.binding(e.localId);
|
|
5316
|
+
return (binding.kind !== "boxed" ||
|
|
5317
|
+
(!binding.local.tdz &&
|
|
5318
|
+
binding.type.kind === e.type.kind &&
|
|
5319
|
+
e.type.kind !== "caught" &&
|
|
5320
|
+
isRefCounted(e.type)));
|
|
5321
|
+
}
|
|
4581
5322
|
case "unionNarrow":
|
|
4582
5323
|
return this.canBorrowReceiver(e.value);
|
|
4583
5324
|
case "fieldGet":
|
|
@@ -4593,6 +5334,11 @@ export class LlEmitter {
|
|
|
4593
5334
|
return false;
|
|
4594
5335
|
}
|
|
4595
5336
|
}
|
|
5337
|
+
materializeSplitLocal(localId) {
|
|
5338
|
+
const span = this.splitSpans.get(localId);
|
|
5339
|
+
if (span)
|
|
5340
|
+
materializeSplitPiece(this, localId, span);
|
|
5341
|
+
}
|
|
4596
5342
|
emitExpr(e) {
|
|
4597
5343
|
return emitExpr(this, e);
|
|
4598
5344
|
}
|