@scriptc/compiler 0.2.5 → 0.2.6

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.
Files changed (307) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +57 -30
  2. package/dist/backend/cycle-analysis.test.js +1 -1
  3. package/dist/backend/executable-features.js +1 -0
  4. package/dist/backend/executable-features.js.map +1 -1
  5. package/dist/backend/link-plan-core.d.ts +2 -0
  6. package/dist/backend/link-plan-core.js +1 -0
  7. package/dist/backend/link-plan-core.js.map +1 -1
  8. package/dist/backend/link-plan.d.ts +1 -0
  9. package/dist/backend/link-plan.js +3 -0
  10. package/dist/backend/link-plan.js.map +1 -1
  11. package/dist/backend/llvm/byte-numbers.js +32 -4
  12. package/dist/backend/llvm/byte-numbers.js.map +1 -1
  13. package/dist/backend/llvm/byte-windows.d.ts +31 -0
  14. package/dist/backend/llvm/byte-windows.js +334 -0
  15. package/dist/backend/llvm/byte-windows.js.map +1 -0
  16. package/dist/backend/llvm/byte-windows.test.d.ts +1 -0
  17. package/dist/backend/llvm/byte-windows.test.js +244 -0
  18. package/dist/backend/llvm/byte-windows.test.js.map +1 -0
  19. package/dist/backend/llvm/call-lifetimes.d.ts +2 -2
  20. package/dist/backend/llvm/call-lifetimes.js +17 -7
  21. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  22. package/dist/backend/llvm/classes.d.ts +14 -1
  23. package/dist/backend/llvm/classes.js +80 -9
  24. package/dist/backend/llvm/classes.js.map +1 -1
  25. package/dist/backend/llvm/classes.test.d.ts +1 -0
  26. package/dist/backend/llvm/classes.test.js +99 -0
  27. package/dist/backend/llvm/classes.test.js.map +1 -0
  28. package/dist/backend/llvm/constant-callbacks.d.ts +11 -0
  29. package/dist/backend/llvm/constant-callbacks.js +213 -0
  30. package/dist/backend/llvm/constant-callbacks.js.map +1 -0
  31. package/dist/backend/llvm/constant-callbacks.test.d.ts +1 -0
  32. package/dist/backend/llvm/constant-callbacks.test.js +124 -0
  33. package/dist/backend/llvm/constant-callbacks.test.js.map +1 -0
  34. package/dist/backend/llvm/dyn.d.ts +1 -1
  35. package/dist/backend/llvm/dyn.js +7 -34
  36. package/dist/backend/llvm/dyn.js.map +1 -1
  37. package/dist/backend/llvm/emitter.d.ts +19 -0
  38. package/dist/backend/llvm/emitter.js +274 -46
  39. package/dist/backend/llvm/emitter.js.map +1 -1
  40. package/dist/backend/llvm/emitter.test.js +17 -11
  41. package/dist/backend/llvm/emitter.test.js.map +1 -1
  42. package/dist/backend/llvm/expr-bytes.js +11 -0
  43. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  44. package/dist/backend/llvm/expr-calls.js +91 -23
  45. package/dist/backend/llvm/expr-calls.js.map +1 -1
  46. package/dist/backend/llvm/expr-containers.js +21 -9
  47. package/dist/backend/llvm/expr-containers.js.map +1 -1
  48. package/dist/backend/llvm/expr-dynamic.js +36 -3
  49. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  50. package/dist/backend/llvm/expr-primitives.js +7 -0
  51. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  52. package/dist/backend/llvm/expr-primitives.test.js +1 -1
  53. package/dist/backend/llvm/expr-stream-bridges.test.js +1 -1
  54. package/dist/backend/llvm/integer-remainder.d.ts +7 -0
  55. package/dist/backend/llvm/integer-remainder.js +26 -0
  56. package/dist/backend/llvm/integer-remainder.js.map +1 -0
  57. package/dist/backend/llvm/layouts.js +1 -1
  58. package/dist/backend/llvm/layouts.js.map +1 -1
  59. package/dist/backend/llvm/lib-abi.js +7 -0
  60. package/dist/backend/llvm/lib-abi.js.map +1 -1
  61. package/dist/backend/llvm/lib-dispatch.js +7 -22
  62. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  63. package/dist/backend/llvm/lib-shared.js +15 -0
  64. package/dist/backend/llvm/lib-shared.js.map +1 -1
  65. package/dist/backend/llvm/local-array-reads.d.ts +8 -2
  66. package/dist/backend/llvm/local-array-reads.js +89 -68
  67. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  68. package/dist/backend/llvm/local-array-reads.test.js +28 -4
  69. package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
  70. package/dist/backend/llvm/local-union-storage.test.js +1 -1
  71. package/dist/backend/llvm/loop-array-borrows.d.ts +11 -0
  72. package/dist/backend/llvm/loop-array-borrows.js +70 -0
  73. package/dist/backend/llvm/loop-array-borrows.js.map +1 -0
  74. package/dist/backend/llvm/loop-array-borrows.test.d.ts +1 -0
  75. package/dist/backend/llvm/loop-array-borrows.test.js +145 -0
  76. package/dist/backend/llvm/loop-array-borrows.test.js.map +1 -0
  77. package/dist/backend/llvm/map-read-lifetimes.d.ts +5 -0
  78. package/dist/backend/llvm/map-read-lifetimes.js +20 -5
  79. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  80. package/dist/backend/llvm/map-read-lifetimes.test.js +1 -1
  81. package/dist/backend/llvm/reference-effects.d.ts +2 -1
  82. package/dist/backend/llvm/reference-effects.js +7 -0
  83. package/dist/backend/llvm/reference-effects.js.map +1 -1
  84. package/dist/backend/llvm/shapes.d.ts +4 -1
  85. package/dist/backend/llvm/shapes.js +22 -7
  86. package/dist/backend/llvm/shapes.js.map +1 -1
  87. package/dist/backend/llvm/split-loops.d.ts +48 -0
  88. package/dist/backend/llvm/split-loops.js +183 -0
  89. package/dist/backend/llvm/split-loops.js.map +1 -0
  90. package/dist/backend/llvm/split.js +0 -7
  91. package/dist/backend/llvm/split.js.map +1 -1
  92. package/dist/backend/llvm/split.test.js +66 -2
  93. package/dist/backend/llvm/split.test.js.map +1 -1
  94. package/dist/backend/llvm/stack-callbacks.d.ts +14 -0
  95. package/dist/backend/llvm/stack-callbacks.js +73 -0
  96. package/dist/backend/llvm/stack-callbacks.js.map +1 -0
  97. package/dist/backend/llvm/stack-captures.d.ts +25 -0
  98. package/dist/backend/llvm/stack-captures.js +150 -0
  99. package/dist/backend/llvm/stack-captures.js.map +1 -0
  100. package/dist/backend/llvm/stack-unions.test.js +1 -1
  101. package/dist/backend/llvm/string-construction.test.js +1 -1
  102. package/dist/backend/llvm/string-lifetimes.test.js +8 -4
  103. package/dist/backend/llvm/string-lifetimes.test.js.map +1 -1
  104. package/dist/backend/llvm/string-slices.d.ts +20 -0
  105. package/dist/backend/llvm/string-slices.js +87 -0
  106. package/dist/backend/llvm/string-slices.js.map +1 -0
  107. package/dist/backend/llvm/string-slices.test.d.ts +1 -0
  108. package/dist/backend/llvm/string-slices.test.js +98 -0
  109. package/dist/backend/llvm/string-slices.test.js.map +1 -0
  110. package/dist/backend/llvm/switch-dispatch.test.js +1 -1
  111. package/dist/backend/llvm/walkers.d.ts +4 -14
  112. package/dist/backend/llvm/walkers.js +53 -311
  113. package/dist/backend/llvm/walkers.js.map +1 -1
  114. package/dist/backend/may-throw.js +1 -1
  115. package/dist/backend/may-throw.js.map +1 -1
  116. package/dist/backend/may-throw.test.js +1 -1
  117. package/dist/backend/native/contracts.d.ts +2 -0
  118. package/dist/backend/native/executable.js +12 -4
  119. package/dist/backend/native/executable.js.map +1 -1
  120. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  121. package/dist/backend/native/runtime-inputs.js +2 -0
  122. package/dist/backend/native/runtime-inputs.js.map +1 -1
  123. package/dist/backend/native-codegen-core.d.ts +6 -0
  124. package/dist/backend/native-codegen-core.js +21 -0
  125. package/dist/backend/native-codegen-core.js.map +1 -1
  126. package/dist/backend/native-codegen.d.ts +4 -0
  127. package/dist/backend/native-codegen.js +34 -18
  128. package/dist/backend/native-codegen.js.map +1 -1
  129. package/dist/backend/native-codegen.test.js +47 -2
  130. package/dist/backend/native-codegen.test.js.map +1 -1
  131. package/dist/backend/native-link-info-core.d.ts +1 -0
  132. package/dist/backend/native-link-info-core.js.map +1 -1
  133. package/dist/backend/native-tools.d.ts +3 -1
  134. package/dist/backend/native-tools.js +6 -4
  135. package/dist/backend/native-tools.js.map +1 -1
  136. package/dist/backend/runtime-abi.d.ts +2 -2
  137. package/dist/backend/runtime-abi.js +2 -2
  138. package/dist/backend/runtime-pack-core.d.ts +1 -0
  139. package/dist/backend/runtime-pack-core.js +8 -1
  140. package/dist/backend/runtime-pack-core.js.map +1 -1
  141. package/dist/backend/runtime-pack-targets.test.js +41 -4
  142. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  143. package/dist/backend/runtime-pack.test.js +11 -1
  144. package/dist/backend/runtime-pack.test.js.map +1 -1
  145. package/dist/backend/target-diagnostics.test.js +1 -1
  146. package/dist/executable/executable-cache.d.ts +1 -0
  147. package/dist/executable/executable-cache.js +2 -1
  148. package/dist/executable/executable-cache.js.map +1 -1
  149. package/dist/executable/executable-cache.test.js +15 -0
  150. package/dist/executable/executable-cache.test.js.map +1 -1
  151. package/dist/frontend/fork-target.d.ts +5 -0
  152. package/dist/frontend/fork-target.js +40 -8
  153. package/dist/frontend/fork-target.js.map +1 -1
  154. package/dist/frontend/fork-target.test.d.ts +1 -0
  155. package/dist/frontend/fork-target.test.js +59 -0
  156. package/dist/frontend/fork-target.test.js.map +1 -0
  157. package/dist/frontend/lowering/binding-analysis.js +2 -2
  158. package/dist/frontend/lowering/binding-analysis.js.map +1 -1
  159. package/dist/frontend/lowering/builtins/atomics.d.ts +2 -11
  160. package/dist/frontend/lowering/builtins/atomics.js +64 -28
  161. package/dist/frontend/lowering/builtins/atomics.js.map +1 -1
  162. package/dist/frontend/lowering/builtins/filesystem-calls.d.ts +3 -0
  163. package/dist/frontend/lowering/builtins/filesystem-calls.js +51 -4
  164. package/dist/frontend/lowering/builtins/filesystem-calls.js.map +1 -1
  165. package/dist/frontend/lowering/builtins/module-calls.js +6 -1
  166. package/dist/frontend/lowering/builtins/module-calls.js.map +1 -1
  167. package/dist/frontend/lowering/builtins/workers.d.ts +5 -0
  168. package/dist/frontend/lowering/builtins/workers.js +65 -0
  169. package/dist/frontend/lowering/builtins/workers.js.map +1 -0
  170. package/dist/frontend/lowering/class-assertions.d.ts +7 -0
  171. package/dist/frontend/lowering/class-assertions.js +96 -0
  172. package/dist/frontend/lowering/class-assertions.js.map +1 -0
  173. package/dist/frontend/lowering/class-protocols.d.ts +17 -0
  174. package/dist/frontend/lowering/class-protocols.js +126 -0
  175. package/dist/frontend/lowering/class-protocols.js.map +1 -0
  176. package/dist/frontend/lowering/containers/array-callbacks.d.ts +7 -11
  177. package/dist/frontend/lowering/containers/array-callbacks.js +52 -87
  178. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  179. package/dist/frontend/lowering/containers/bytes.js +12 -34
  180. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  181. package/dist/frontend/lowering/containers/string-and-regexp.js +22 -3
  182. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  183. package/dist/frontend/lowering/expressions/object-literals.js +2 -6
  184. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  185. package/dist/frontend/lowering/generic-functions.d.ts +2 -5
  186. package/dist/frontend/lowering/generic-functions.js +13 -10
  187. package/dist/frontend/lowering/generic-functions.js.map +1 -1
  188. package/dist/frontend/lowering/instantiation-path.d.ts +8 -0
  189. package/dist/frontend/lowering/instantiation-path.js +10 -0
  190. package/dist/frontend/lowering/instantiation-path.js.map +1 -0
  191. package/dist/frontend/lowering/instantiation-path.test.d.ts +1 -0
  192. package/dist/frontend/lowering/instantiation-path.test.js +23 -0
  193. package/dist/frontend/lowering/instantiation-path.test.js.map +1 -0
  194. package/dist/frontend/lowering/lower-builtin-values.js +11 -7
  195. package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
  196. package/dist/frontend/lowering/lower-calls.js +36 -3
  197. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  198. package/dist/frontend/lowering/lower-classes.d.ts +5 -0
  199. package/dist/frontend/lowering/lower-classes.js +121 -53
  200. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  201. package/dist/frontend/lowering/lower-event-emitter.js +1 -1
  202. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  203. package/dist/frontend/lowering/lower-exprs.js +31 -10
  204. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  205. package/dist/frontend/lowering/lower-functions.d.ts +1 -1
  206. package/dist/frontend/lowering/lower-functions.js +2 -2
  207. package/dist/frontend/lowering/lower-functions.js.map +1 -1
  208. package/dist/frontend/lowering/lower-island.js +1 -1
  209. package/dist/frontend/lowering/lower-island.js.map +1 -1
  210. package/dist/frontend/lowering/lower-modules.d.ts +1 -0
  211. package/dist/frontend/lowering/lower-modules.js +44 -9
  212. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  213. package/dist/frontend/lowering/lower-stmts.js +13 -11
  214. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  215. package/dist/frontend/lowering/lower-stream.js +1 -1
  216. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  217. package/dist/frontend/lowering/lowerer.d.ts +13 -1
  218. package/dist/frontend/lowering/lowerer.js +51 -9
  219. package/dist/frontend/lowering/lowerer.js.map +1 -1
  220. package/dist/frontend/lowering/sanitize-class-types.test.js +2 -2
  221. package/dist/frontend/lowering/split-consumers.d.ts +7 -0
  222. package/dist/frontend/lowering/split-consumers.js +64 -0
  223. package/dist/frontend/lowering/split-consumers.js.map +1 -0
  224. package/dist/frontend/lowering/surfaces.d.ts +3 -4
  225. package/dist/frontend/lowering/surfaces.js +35 -7
  226. package/dist/frontend/lowering/surfaces.js.map +1 -1
  227. package/dist/frontend/module-source-candidates.d.ts +10 -0
  228. package/dist/frontend/module-source-candidates.js +40 -0
  229. package/dist/frontend/module-source-candidates.js.map +1 -0
  230. package/dist/frontend/module-source-candidates.test.d.ts +1 -0
  231. package/dist/frontend/module-source-candidates.test.js +53 -0
  232. package/dist/frontend/module-source-candidates.test.js.map +1 -0
  233. package/dist/frontend/pipeline.js +2 -2
  234. package/dist/frontend/pipeline.js.map +1 -1
  235. package/dist/frontend/program-analysis.d.ts +2 -0
  236. package/dist/frontend/program-analysis.js +2 -0
  237. package/dist/frontend/program-analysis.js.map +1 -1
  238. package/dist/frontend/program.js +17 -29
  239. package/dist/frontend/program.js.map +1 -1
  240. package/dist/frontend/ts7/ast-node.js +6 -8
  241. package/dist/frontend/ts7/ast-node.js.map +1 -1
  242. package/dist/frontend/ts7/ast-node.test.js +42 -1
  243. package/dist/frontend/ts7/ast-node.test.js.map +1 -1
  244. package/dist/frontend/ts7/ast-wire.d.ts +4 -0
  245. package/dist/frontend/ts7/ast-wire.js +28 -0
  246. package/dist/frontend/ts7/ast-wire.js.map +1 -1
  247. package/dist/frontend/ts7/ast.d.ts +2 -1
  248. package/dist/frontend/ts7/ast.js +8 -4
  249. package/dist/frontend/ts7/ast.js.map +1 -1
  250. package/dist/frontend/ts7/checker.d.ts +8 -2
  251. package/dist/frontend/ts7/checker.js +41 -5
  252. package/dist/frontend/ts7/checker.js.map +1 -1
  253. package/dist/frontend/ts7/checker.test.js +42 -0
  254. package/dist/frontend/ts7/checker.test.js.map +1 -1
  255. package/dist/frontend/ts7/program-host.d.ts +7 -0
  256. package/dist/frontend/ts7/program-host.js +26 -2
  257. package/dist/frontend/ts7/program-host.js.map +1 -1
  258. package/dist/frontend/ts7/program-host.test.js +29 -0
  259. package/dist/frontend/ts7/program-host.test.js.map +1 -1
  260. package/dist/frontend/type-mapper.d.ts +3 -0
  261. package/dist/frontend/type-mapper.js +44 -2
  262. package/dist/frontend/type-mapper.js.map +1 -1
  263. package/dist/frontend/worker-target.d.ts +6 -0
  264. package/dist/frontend/worker-target.js +108 -0
  265. package/dist/frontend/worker-target.js.map +1 -0
  266. package/dist/frontend/worker-target.test.d.ts +1 -0
  267. package/dist/frontend/worker-target.test.js +71 -0
  268. package/dist/frontend/worker-target.test.js.map +1 -0
  269. package/dist/index.js +24 -7
  270. package/dist/index.js.map +1 -1
  271. package/dist/ir/builtin-effects.js +12 -0
  272. package/dist/ir/builtin-effects.js.map +1 -1
  273. package/dist/ir/builtin-signatures.d.ts +1 -1
  274. package/dist/ir/builtin-signatures.js +13 -12
  275. package/dist/ir/builtin-signatures.js.map +1 -1
  276. package/dist/ir/constant-tables.test.js +1 -1
  277. package/dist/ir/initializer-bindings.test.js +1 -1
  278. package/dist/ir/ir.d.ts +9 -10
  279. package/dist/ir/ir.js.map +1 -1
  280. package/dist/ir/numeric-call-specialization.d.ts +8 -0
  281. package/dist/ir/numeric-call-specialization.js +198 -0
  282. package/dist/ir/numeric-call-specialization.js.map +1 -0
  283. package/dist/ir/runtime-features.js +4 -3
  284. package/dist/ir/runtime-features.js.map +1 -1
  285. package/dist/ir/scalar-records.test.js +1 -1
  286. package/dist/ir/serialize.d.ts +1 -1
  287. package/dist/ir/serialize.js +3 -3
  288. package/dist/ir/serialize.js.map +1 -1
  289. package/dist/ir/validate.js +89 -23
  290. package/dist/ir/validate.js.map +1 -1
  291. package/dist/ir/validate.test.js +91 -4
  292. package/dist/ir/validate.test.js.map +1 -1
  293. package/dist/library/fence-eval.test.js +1 -1
  294. package/dist/library/int-infer.test.js +4 -4
  295. package/dist/library/ir-policy.js +2 -0
  296. package/dist/library/ir-policy.js.map +1 -1
  297. package/dist/library/library-cache.test.js +5 -5
  298. package/dist/native/compiler.js +24 -11
  299. package/dist/native/compiler.js.map +1 -1
  300. package/dist/native/compiler.test.js +7 -3
  301. package/dist/native/compiler.test.js.map +1 -1
  302. package/dist/native/prepare.js +2 -0
  303. package/dist/native/prepare.js.map +1 -1
  304. package/dist/native/sanitizer.test.js +1 -0
  305. package/dist/native/sanitizer.test.js.map +1 -1
  306. package/package.json +20 -20
  307. package/surface-manifest.json +30 -2
@@ -21,13 +21,16 @@ import { InternalCompilerError } from "../../errors.js";
21
21
  import { deflateRawSync } from "node:zlib";
22
22
  import { endsWithJump, matchStringSelfConcat, streamTypedRefEligible } from "../../ir/analysis.js";
23
23
  import { emitLibraryIdentityLines } from "../library-identity-markers.js";
24
- import { CAUGHT, ffiCallbackType, isDynTypedRefType, isFfiContextParam, isRefCounted, isUnitType, POINTER_KINDS, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, typeKey, VOID, } from "../../ir/ir.js";
24
+ import { CAUGHT, ffiCallbackType, isDynTypedRefType, isFfiContextParam, isRefCounted, isUnitType, POINTER_KINDS, RUNTIME_EMITTER_CLASS, RUNTIME_ERROR_CLASSES, RUNTIME_STREAM_CLASSES, typeKey, STRING, VOID, } from "../../ir/ir.js";
25
25
  import { moduleRuntimeFeatures, moduleEmbedsBuiltin, moduleEmbedsCompressedNpm, NPM_COMPRESS_MIN, } from "../../ir/runtime-features.js";
26
26
  import { matchIntegerArrayForLoop, matchIntegerBytesForLoop, matchIntegerCountedForLoop, } from "../../ir/integer-loops.js";
27
27
  import { emitCountedLoopLimit } from "./counted-loops.js";
28
28
  import { emitLiteralSwitch } from "./switch-dispatch.js";
29
29
  import { findBytesBounds } from "./bytes-bounds.js";
30
+ import { emitByteWindowGuard, findInitializedByteLoopBindings, matchByteWindow, } from "./byte-windows.js";
31
+ import { canDeferSplitPiece, emitSplitSpanNext, materializeSplitPiece, emitSplitCursor, emitSplitNext, emitSplitScratch, emitSplitSnapshot, findPrivateSplitLocals, loadSplitSnapshot, storeSplitSnapshot, stringSplit, } from "./split-loops.js";
30
32
  import { scalarizeNumericRecords } from "../../ir/scalar-records.js";
33
+ import { specializeNumericCalls } from "../../ir/numeric-call-specialization.js";
31
34
  import { everyStmtList } from "../../ir/traverse.js";
32
35
  import { analyzeIntegerRanges } from "../../ir/integer-ranges.js";
33
36
  import { findIntegerViews } from "./integer-views.js";
@@ -41,12 +44,17 @@ import { analyzeCallLifetimes } from "./call-lifetimes.js";
41
44
  import { canStackUnion, emitStackUnion, findLocalStackUnions } from "./stack-unions.js";
42
45
  import { allocateLocalUnion, findLocalUnionStorage, storeLocalUnion, } from "./local-union-storage.js";
43
46
  import { BlockBuilder } from "./blocks.js";
44
- import { emitLocalArrayRead, findLocalArrayReads, findCallArrayReads, OptionalArrayReads, } from "./local-array-reads.js";
47
+ import { emitLocalArrayRead, emitBorrowedArrayRead, findLocalArrayReads, findCallArrayReads, OptionalArrayReads, } from "./local-array-reads.js";
45
48
  import { emitStackMapRead, findMapReadLifetimes, matchMapRead, } from "./map-read-lifetimes.js";
46
49
  import { emitBorrowedFieldSequence } from "./borrowed-receivers.js";
47
50
  import { emitBorrowedInput } from "./borrowed-inputs.js";
48
51
  import { ReferenceEffects } from "./reference-effects.js";
49
52
  import { LlvmDebugInfo } from "./debug-info.js";
53
+ import { StackCallbacks } from "./stack-callbacks.js";
54
+ import { findLoopArrayBorrows } from "./loop-array-borrows.js";
55
+ import { findConstantCallbacks } from "./constant-callbacks.js";
56
+ import { findScalarStringSlices, emitStringSliceSnapshot, } from "./string-slices.js";
57
+ import { StackCaptures } from "./stack-captures.js";
50
58
  import { f64Lit, ffiNativeTypeLl, ffiNativeParamLl, ffiNativeReturnLl, llvmCommentText, } from "./common.js";
51
59
  import { ffiExtendsNarrowIntegers } from "../targets.js";
52
60
  import { emitLiteralExpr, emitOperatorExpr, emitStringExpr, emitContainerExpr, emitRecordExpr, } from "./expr-primitives.js";
@@ -67,7 +75,7 @@ import { emitChildProcessLibCall, emitAsyncContextLibCall, emitProcessLibCall, e
67
75
  import { emitStreamLibCall } from "./lib-stream.js";
68
76
  import { emitNetworkHttpLibCall } from "./lib-network.js";
69
77
  import { emitAssertInspectLibCall, emitIoLibCall, emitGenericLibCall, emitLibCall, } from "./lib-dispatch.js";
70
- import { buildClassGraph, classEnvironmentIndex, classFieldIndex, classStructSym, } from "./classes.js";
78
+ import { buildClassGraph, classMembershipIntervals, emitClassMembershipHelper, classEnvironmentIndex, classFieldIndex, classStructSym, } from "./classes.js";
71
79
  import { LlDyn } from "./dyn.js";
72
80
  import { LlvmUnsupportedError } from "./unsupported.js";
73
81
  import { LlWalkers } from "./walkers.js";
@@ -100,11 +108,15 @@ function ffiCallbackDummyLl(callback) {
100
108
  }
101
109
  export function emitLlvmModule(mod, options = {}) {
102
110
  // Keep source storage intact for debugger inspection in dev builds.
103
- return new LlEmitter(options.debugSources === undefined ? scalarizeNumericRecords(mod) : mod, options).emit();
111
+ return new LlEmitter(options.debugSources === undefined
112
+ ? scalarizeNumericRecords(specializeNumericCalls(mod, mangleFunction))
113
+ : mod, options).emit();
104
114
  }
105
115
  /** Keep large translation units below the host's single-string limit. */
106
116
  export function emitLlvmModuleSource(mod, options = {}) {
107
- const parts = new LlEmitter(options.debugSources === undefined ? scalarizeNumericRecords(mod) : mod, options).emitParts();
117
+ const parts = new LlEmitter(options.debugSources === undefined
118
+ ? scalarizeNumericRecords(specializeNumericCalls(mod, mangleFunction))
119
+ : mod, options).emitParts();
108
120
  const length = parts.reduce((total, part) => total + part.length, Math.max(0, parts.length - 1));
109
121
  if (length <= 256 * 1024 * 1024)
110
122
  return parts.join("\n");
@@ -116,6 +128,9 @@ function llStrBytes(text) {
116
128
  export class LlEmitter {
117
129
  mod;
118
130
  localArrayReads = new Map();
131
+ loopArrayBorrows = new Set();
132
+ scalarStringSlices = new Map();
133
+ stringSlices = new Map();
119
134
  localStackUnions = new Map();
120
135
  localUnionStorageProofs = new Map();
121
136
  localUnionStorage = new Map();
@@ -206,6 +221,10 @@ export class LlEmitter {
206
221
  callArrayReads = new Map();
207
222
  mapReadLifetimes = { locals: new Map(), arguments: new Map() };
208
223
  callLifetimes;
224
+ constantCallbacks;
225
+ currentConstantCallbacks = new Map();
226
+ stackCallbacks;
227
+ stackCaptures;
209
228
  borrowedParameters = new Set();
210
229
  stableCallBindings = new Set();
211
230
  /** Manifest-bound native imports, used by ffiCall emission. */
@@ -288,6 +307,10 @@ export class LlEmitter {
288
307
  * for blocks and switches. */
289
308
  jumpTargets = [];
290
309
  currentLocals = new Map();
310
+ privateSplitLocals = new Map();
311
+ splitSpans = new Map();
312
+ storedSplits = new Map();
313
+ streamingSplitsEnabled = false;
291
314
  initializerBindings;
292
315
  numericLocals = new Map();
293
316
  captureIds = new Set();
@@ -295,6 +318,8 @@ export class LlEmitter {
295
318
  integerLoopBindings = new Map();
296
319
  countedLoopsEnabled = false;
297
320
  integerRanges = new Map();
321
+ byteWindowsEnabled = true;
322
+ byteWindowEntries = new Map();
298
323
  bytesBounds = new Set();
299
324
  integerViews = new Map();
300
325
  /** Enclosing try-with-FINALLY regions, innermost last: a `return`
@@ -359,7 +384,10 @@ export class LlEmitter {
359
384
  }
360
385
  const mt = computeMayThrow(mod);
361
386
  this.mayThrow = mt.fns;
362
- this.indirectMayThrow = mt.indirect;
387
+ if (mod.workers)
388
+ for (const fn of mod.functions)
389
+ this.mayThrow.add(fn.name);
390
+ this.indirectMayThrow = mt.indirect || mod.workers === true;
363
391
  for (const cls of mod.classes ?? []) {
364
392
  for (const m of cls.methods ?? []) {
365
393
  if (this.mayThrow.has(`%${cls.name}.${m}`))
@@ -371,6 +399,9 @@ export class LlEmitter {
371
399
  this.optionalArrayReads = new OptionalArrayReads(this.fnByName, this.unionsById);
372
400
  this.referenceEffects = new ReferenceEffects(this.fnByName, (call) => this.optionalArrayReads.get(call) !== null);
373
401
  this.callLifetimes = analyzeCallLifetimes(this.fnByName);
402
+ this.constantCallbacks = findConstantCallbacks(mod, this.callLifetimes);
403
+ this.stackCallbacks = new StackCallbacks(this.fnByName);
404
+ this.stackCaptures = new StackCaptures(this.fnByName, this.callLifetimes, this.stackCallbacks);
374
405
  for (const r of mod.records ?? [])
375
406
  this.recordsById.set(r.id, r);
376
407
  const traced = computeTraced(mod);
@@ -396,8 +427,9 @@ export class LlEmitter {
396
427
  isErrorClass: (name) => this.classMeta.get(name)?.root.def.name === "%Error",
397
428
  classSubtypes: (name) => {
398
429
  const target = this.classMetaOf(name);
430
+ const intervals = classMembershipIntervals(this.classMeta, target.def.name);
399
431
  return [...this.classMeta.values()]
400
- .filter((meta) => target.pre <= meta.pre && meta.pre <= target.post)
432
+ .filter((meta) => intervals.some((range) => range.pre <= meta.pre && meta.pre <= range.post))
401
433
  .map((meta) => meta.def.name);
402
434
  },
403
435
  };
@@ -507,7 +539,7 @@ export class LlEmitter {
507
539
  if (adapter.global !== null)
508
540
  globals.push(`@${adapter.global} = internal thread_local global ptr null`);
509
541
  if (adapter.table !== null) {
510
- globals.push(`@${adapter.table} = internal global %ScrFfiTable zeroinitializer`);
542
+ globals.push(`@${adapter.table} = internal ${this.mod.workers ? "thread_local " : ""}global %ScrFfiTable zeroinitializer`);
511
543
  }
512
544
  const params = cb.params.flatMap((param, i) => {
513
545
  if (isFfiContextParam(param))
@@ -953,7 +985,7 @@ export class LlEmitter {
953
985
  // with neither keep the atexit path, so their listener timing is
954
986
  // unchanged.
955
987
  const hasRefGlobals = globals.some((g) => isRefCounted(g.type)) || fnValueProps.length > 0;
956
- const inlineExitListeners = usesEvents && (hasRefGlobals || this.ffiHasRetainedCallback);
988
+ const inlineExitListeners = usesEvents && (this.mod.workers || hasRefGlobals || this.ffiHasRetainedCallback);
957
989
  this.declare(`declare i32 @scr_exit_code_hint_get()`);
958
990
  if (inlineExitListeners) {
959
991
  this.declare(`declare void @scr_run_exit_listeners(double)`);
@@ -1081,7 +1113,10 @@ export class LlEmitter {
1081
1113
  }
1082
1114
  // Helpers assemble BEFORE the declaration table flushes (they add
1083
1115
  // write/abort declarations).
1084
- const helpers = this.helperDefs();
1116
+ const helpers = [
1117
+ ...this.helperDefs(),
1118
+ ...emitClassMembershipHelper(this.classMeta, this.sizeType),
1119
+ ];
1085
1120
  if (this.constantNumericTables.size > 0)
1086
1121
  this.declare(`declare double @scr_arr_get_number(ptr, double)`);
1087
1122
  const out = [
@@ -1121,11 +1156,11 @@ export class LlEmitter {
1121
1156
  // scr_runtime.h's natural alignment: kind at 8, f64 at 16, b at 24,
1122
1157
  // payload at 32).
1123
1158
  `%ScrCaught = type { ${this.sizeType}, i32, double, i8, ptr, ptr, ptr, ptr }`,
1124
- // ScrBytes { rc, len, elem(i32+pad), data, backing }. Indexed
1159
+ // ScrBytes { rc, len, elem(i32+pad), data, backing, brands, shared }. Indexed
1125
1160
  // typed-array access GEPs through this directly: the IR type already
1126
1161
  // fixes elem, so the hot path needs neither a runtime kind load nor
1127
1162
  // the generic scr_bytes_get/set call.
1128
- `%ScrBytes = type { ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, i8 }`,
1163
+ `%ScrBytes = type { ${this.sizeType}, ${this.sizeType}, i32, ptr, ptr, i8, i8, i8, ptr }`,
1129
1164
  // The capture box { rc, kind, obj_retain, obj_release, obj_trace,
1130
1165
  // slot } — TDZ reads peek the payload slot (offset 40) directly.
1131
1166
  `%ScrBox = type { ${this.sizeType}, i32, ptr, ptr, ptr, i64 }`,
@@ -1148,8 +1183,8 @@ export class LlEmitter {
1148
1183
  // objects compiled with -DSCR_THREAD_INSTANCES. Immutable interned
1149
1184
  // data (string literals, unit arms, template arrays, vtables) stays
1150
1185
  // 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
1186
+ const tl = this.mod.workers === true || this.mod.lib?.threadInstances === true ? "thread_local " : "";
1187
+ out.push(``, `@scr_error_vts = external ${tl}global [${RUNTIME_ERROR_CLASSES.size} x %ScrVt]`, `declare void @scr_init()`, `declare void @scr_lib_init(i32, ptr)`, ...(this.mod.workers ? [`declare void @scr_runtime_workers_v7()`] : []), ...(this.runtimeAbiMarker && this.mod.lib === undefined
1153
1188
  ? [`declare void @${RUNTIME_ABI_MARKER}()`]
1154
1189
  : []));
1155
1190
  for (const d of this.decls)
@@ -1306,7 +1341,7 @@ export class LlEmitter {
1306
1341
  // The runtime stream vtables' intervals (streamVtStampLines) — the
1307
1342
  // emitter story: instanceof and dynamic teardown dispatch through
1308
1343
  // them.
1309
- out.push(`@${iv.vt} = external global %ScrVt`);
1344
+ out.push(`@${iv.vt} = external ${tl}global %ScrVt`);
1310
1345
  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
1346
  }
1312
1347
  if (this.streamIntervals.length > 0)
@@ -1343,7 +1378,7 @@ export class LlEmitter {
1343
1378
  out.push(this.debug.render());
1344
1379
  return out;
1345
1380
  }
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,
1381
+ out.push(`define i32 @${this.mod.workers ? "sc_context_entry" : this.wasi ? "__main_argc_argv" : "main"}(i32 %argc, ptr %argv) ${FN_ATTRS} {`, `entry:`, ...(this.runtimeAbiMarker ? [` call void @${RUNTIME_ABI_MARKER}()`] : []), ...(this.mod.workers ? [` call void @scr_runtime_workers_v7()`] : []), ` call void @scr_init()`, ...stamps,
1347
1382
  // Event-surface programs (signal/exit listeners) fill the loop's
1348
1383
  // nullable event hooks before %main — scr_events.c links only when
1349
1384
  // this line is emitted (native/executable.ts gates on the same predicate).
@@ -1456,6 +1491,9 @@ export class LlEmitter {
1456
1491
  // lane's -fsanitize=address link activates instrumentation over the
1457
1492
  // emitted functions too (the runtime TUs get theirs from clang).
1458
1493
  `attributes #0 = { sanitize_address }`, ...(this.wasi ? [`attributes #1 = { sanitize_address presplitcoroutine }`] : []), ...(hasNoInlineRecordClone ? [`attributes #2 = { noinline sanitize_address }`] : []), ``);
1494
+ if (this.mod.workers) {
1495
+ 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`, `}`);
1496
+ }
1459
1497
  out.push(...this.fieldAliasMetadata());
1460
1498
  if (this.debug !== null)
1461
1499
  out.push(this.debug.render());
@@ -1723,7 +1761,7 @@ export class LlEmitter {
1723
1761
  // never the inner return type.
1724
1762
  const ret = fn.async === true || fn.generator !== undefined ? "ptr" : this.llType(fn.returnType);
1725
1763
  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)} }`, ``);
1764
+ out.push(`define internal ${ret} @${mangleWrapper(name)}(ptr %env${params.length ? ", " + params.join(", ") : ""}) ${FN_ATTRS} { ; ${name} as a value`, `entry:`, ret === "void" ? ` ${call}` : ` %r = ${call}`, ret === "void" ? ` ret void` : ` ret ${ret} %r`, `}`, `@${mangleFnClosure(name)} = internal ${this.mod.workers === true || this.mod.lib?.threadInstances === true ? "thread_local " : ""}global %ScrClosure { ${this.sizeType} -1, ptr @${mangleWrapper(name)}, ${this.sizeType} 0, ptr null, i32 ${(fn.generator ? 1 : 0) + (fn.async ? 2 : 0) + (fn.ownsPrototype ? 4 : 0)} }`, ``);
1727
1765
  }
1728
1766
  return out;
1729
1767
  }
@@ -2206,6 +2244,20 @@ export class LlEmitter {
2206
2244
  * using the result). Callers own the surrounding pending branch; a
2207
2245
  * `throw` unwinds unconditionally. */
2208
2246
  emitUnwind() {
2247
+ if (this.mod.workers && this.tryStack.length > 0) {
2248
+ this.declare(`declare zeroext i1 @scr_context_stopping()`);
2249
+ const stopped = this.B.tmp();
2250
+ this.B.line(`${stopped} = call zeroext i1 @scr_context_stopping()`);
2251
+ const stop = this.B.newLabel("context.stop");
2252
+ const ordinary = this.B.newLabel("context.throw");
2253
+ this.B.condBr(stopped, stop, ordinary);
2254
+ this.B.startBlock(stop);
2255
+ const handlers = this.tryStack;
2256
+ this.tryStack = [];
2257
+ this.emitUnwind();
2258
+ this.tryStack = handlers;
2259
+ this.B.startBlock(ordinary);
2260
+ }
2209
2261
  const target = this.tryStack[this.tryStack.length - 1];
2210
2262
  const frameDepth = target?.frameDepth ?? 0;
2211
2263
  const scopeDepth = target?.scopeDepth ?? 0;
@@ -2269,6 +2321,34 @@ export class LlEmitter {
2269
2321
  this.emitUnwind();
2270
2322
  B.startBlock(lk);
2271
2323
  }
2324
+ workerLoopBudget() {
2325
+ if (!this.mod.workers)
2326
+ return null;
2327
+ const slot = this.B.slot();
2328
+ this.B.entryAllocas.push(`${slot} = alloca i32 ; worker cancellation budget`);
2329
+ this.B.line(`store i32 0, ptr ${slot}`);
2330
+ return slot;
2331
+ }
2332
+ emitWorkerLoopCheck(slot) {
2333
+ if (slot === null)
2334
+ return;
2335
+ const B = this.B;
2336
+ const budget = B.tmp(), due = B.tmp(), remaining = B.tmp();
2337
+ const poll = B.newLabel("worker.poll"), next = B.newLabel("worker.next");
2338
+ B.line(`${budget} = load i32, ptr ${slot}`);
2339
+ B.line(`${remaining} = sub i32 ${budget}, 1`);
2340
+ B.line(`store i32 ${remaining}, ptr ${slot}`);
2341
+ B.line(`${due} = icmp eq i32 ${budget}, 0`);
2342
+ B.condBr(due, poll, next);
2343
+ B.startBlock(poll);
2344
+ B.line(`store i32 63, ptr ${slot}`);
2345
+ // Calls and throwing operations retain their own checks. Amortize the
2346
+ // extra cancellation poll across bounded loop batches so scalar work
2347
+ // does not cross the runtime ABI on every iteration.
2348
+ this.emitPendingCheck();
2349
+ B.br(next);
2350
+ B.startBlock(next);
2351
+ }
2272
2352
  /** Moves an already-evaluated value into the runtime's exception cell —
2273
2353
  * the `throw` statement's kind dispatch (stmts.ts's), shared with
2274
2354
  * every synthetic thrower. Ownership of a refcounted payload must have
@@ -2973,6 +3053,17 @@ export class LlEmitter {
2973
3053
  this.unwindCleanups.clear();
2974
3054
  this.jumpTargets = [];
2975
3055
  this.currentLocals = new Map(fn.locals.map((l) => [l.id, l]));
3056
+ this.currentConstantCallbacks =
3057
+ this.debug === null ? (this.constantCallbacks.get(fn.name) ?? new Map()) : new Map();
3058
+ // Preserve concrete source bindings for debugger inspection. Suspended
3059
+ // functions keep the established array lifetime across continuations.
3060
+ this.streamingSplitsEnabled = this.debug === null && !fn.async && !fn.generator;
3061
+ this.stackCallbacks.reset(this.streamingSplitsEnabled);
3062
+ this.stackCaptures.reset(fn, this.streamingSplitsEnabled);
3063
+ this.privateSplitLocals = this.streamingSplitsEnabled ? findPrivateSplitLocals(fn) : new Map();
3064
+ this.storedSplits.clear();
3065
+ this.splitSpans.clear();
3066
+ this.stringSlices.clear();
2976
3067
  const initializerBindings = this.initializerBindings.get(fn.name) ?? [];
2977
3068
  const numericFn = withInitializerBindings(fn, initializerBindings);
2978
3069
  this.numericLocals = new Map(numericFn.locals.map((l) => [l.id, l]));
@@ -2986,11 +3077,22 @@ export class LlEmitter {
2986
3077
  this.captureIds = new Set([...(fn.captures ?? []), ...(fn.classCaptures ?? [])].map((c) => c.localId));
2987
3078
  this.integerLoopBindings.clear();
2988
3079
  this.countedLoopsEnabled = this.debug === null && !fn.async && !fn.generator;
3080
+ this.byteWindowsEnabled = this.countedLoopsEnabled;
3081
+ this.byteWindowEntries =
3082
+ this.byteWindowsEnabled && numericFn.locals.some((l) => l.type.kind === "bytes")
3083
+ ? findInitializedByteLoopBindings(fn)
3084
+ : new Map();
2989
3085
  this.integerViews.clear();
2990
3086
  this.fieldPointerTags.clear();
2991
3087
  this.integerArrayBindings.clear();
2992
- this.localArrayReads = findLocalArrayReads(fn, this.fnByName, this.unionsById, this.referenceEffects.functions, this.callLifetimes, this.optionalArrayReads);
3088
+ this.loopArrayBorrows =
3089
+ this.debug === null
3090
+ ? findLoopArrayBorrows(fn, this.callLifetimes, this.referenceEffects, this.optionalArrayReads)
3091
+ : new Set();
3092
+ this.localArrayReads = findLocalArrayReads(fn, this.fnByName, this.unionsById, this.referenceEffects.functions, this.callLifetimes, this.optionalArrayReads, this.loopArrayBorrows);
2993
3093
  this.callArrayReads = findCallArrayReads(fn, this.optionalArrayReads, this.referenceEffects.functions, this.callLifetimes);
3094
+ this.scalarStringSlices =
3095
+ this.debug === null ? findScalarStringSlices(fn, this.callLifetimes) : new Map();
2994
3096
  this.mapReadLifetimes = findMapReadLifetimes(fn, this.unionsById, this.callLifetimes);
2995
3097
  this.localStackUnions = findLocalStackUnions(fn, this.callLifetimes, this.unionsById);
2996
3098
  this.localUnionStorageProofs = findLocalUnionStorage(fn, this.callLifetimes, this.unionsById);
@@ -3129,6 +3231,8 @@ export class LlEmitter {
3129
3231
  B.line(`call void @scr_stack_enter(ptr %source_frame, ptr ${this.cstr(frame)})`);
3130
3232
  B.returnEpilogue = `call void @scr_stack_leave(ptr %source_frame)`;
3131
3233
  }
3234
+ if (this.mod.workers)
3235
+ this.emitPendingCheck();
3132
3236
  this.emitStmts(fn.body);
3133
3237
  // Implicit exit of a void function: release the function scope unless
3134
3238
  // the body already terminated its final block (return, or a throw
@@ -3279,6 +3383,20 @@ export class LlEmitter {
3279
3383
  switch (s.kind) {
3280
3384
  case "varDecl": {
3281
3385
  const b = this.binding(s.localId);
3386
+ const slice = this.scalarStringSlices.get(s.localId);
3387
+ if (slice) {
3388
+ const snapshot = emitStringSliceSnapshot(this, slice);
3389
+ this.stringSlices.set(s.localId, snapshot);
3390
+ this.scopes[this.scopes.length - 1].push({ slot: snapshot.source, type: STRING });
3391
+ break;
3392
+ }
3393
+ const split = this.privateSplitLocals.get(s.localId);
3394
+ if (split) {
3395
+ const snapshot = storeSplitSnapshot(this, emitSplitSnapshot(this, split));
3396
+ this.storedSplits.set(s.localId, snapshot);
3397
+ this.scopes[this.scopes.length - 1].push({ slot: snapshot.sourceSlot, type: STRING }, { slot: snapshot.separatorSlot, type: STRING });
3398
+ break;
3399
+ }
3282
3400
  const localUnion = this.localStackUnions.get(s.localId);
3283
3401
  if (localUnion) {
3284
3402
  const union = emitStackUnion(this, localUnion);
@@ -3321,6 +3439,19 @@ export class LlEmitter {
3321
3439
  break;
3322
3440
  }
3323
3441
  if (b.kind === "boxed") {
3442
+ const stack = this.stackCaptures.emit(this, s.localId, b.type);
3443
+ if (stack) {
3444
+ B.line(`store ptr ${stack.box}, ptr ${b.slot}`);
3445
+ if (stack.owner)
3446
+ this.scopes[this.scopes.length - 1].push(stack.owner);
3447
+ if (s.init) {
3448
+ const value = this.emitExpr(s.init);
3449
+ if (isRefCounted(value.type))
3450
+ this.moveTemp(value);
3451
+ this.boxSet(stack.box, b.type, value.name);
3452
+ }
3453
+ break;
3454
+ }
3324
3455
  // Box FIRST, then evaluate the initializer: a named function
3325
3456
  // expression's closure captures this box during init evaluation.
3326
3457
  // A SCALAR TDZ box rides an ARR-kind box: the value lives in a
@@ -3357,13 +3488,16 @@ export class LlEmitter {
3357
3488
  }
3358
3489
  break;
3359
3490
  }
3360
- // A lexical const alias cannot outlive its unchanged local owner.
3491
+ if (this.loopArrayBorrows.has(s) && s.init.kind === "arrayGet") {
3492
+ emitBorrowedArrayRead(this, s.init, b.slot);
3493
+ break;
3494
+ }
3495
+ // A stable lexical alias cannot outlive its unchanged local owner.
3361
3496
  // Whole-value uses still retain their own copies, so returning or
3362
3497
  // storing the alias preserves the ordinary heap representation.
3363
3498
  // Boxed captures, reassigned sources and projections need ownership.
3364
3499
  if (b.kind === "local" &&
3365
3500
  b.local &&
3366
- !b.local.mutable &&
3367
3501
  isRefCounted(b.type) &&
3368
3502
  this.stableCallBindings.has(s.localId) &&
3369
3503
  this.canBorrowCallArgument(s.init)) {
@@ -3694,11 +3828,13 @@ export class LlEmitter {
3694
3828
  break;
3695
3829
  }
3696
3830
  case "while": {
3831
+ const workerBudget = this.workerLoopBudget();
3697
3832
  const lc = B.newLabel("loop.c");
3698
3833
  const lb = B.newLabel("loop.b");
3699
3834
  const le = B.newLabel("loop.e");
3700
3835
  B.br(lc);
3701
3836
  B.startBlock(lc);
3837
+ this.emitWorkerLoopCheck(workerBudget);
3702
3838
  B.condBr(this.emitCondition(s.cond), lb, le);
3703
3839
  B.startBlock(lb);
3704
3840
  this.jumpTargets.push({
@@ -3717,12 +3853,14 @@ export class LlEmitter {
3717
3853
  break;
3718
3854
  }
3719
3855
  case "doWhile": {
3856
+ const workerBudget = this.workerLoopBudget();
3720
3857
  // Body first (runs at least once); continue jumps to the CONDITION.
3721
3858
  const lb = B.newLabel("loop.b");
3722
3859
  const lc = B.newLabel("loop.c");
3723
3860
  const le = B.newLabel("loop.e");
3724
3861
  B.br(lb);
3725
3862
  B.startBlock(lb);
3863
+ this.emitWorkerLoopCheck(workerBudget);
3726
3864
  this.jumpTargets.push({
3727
3865
  kind: "loop",
3728
3866
  brkLabel: le,
@@ -3751,6 +3889,33 @@ export class LlEmitter {
3751
3889
  const countedLoop = !integerLoop && this.countedLoopsEnabled
3752
3890
  ? matchIntegerCountedForLoop(s, this.numericLocals, this.integerRanges)
3753
3891
  : null;
3892
+ const window = countedLoop && this.byteWindowsEnabled
3893
+ ? matchByteWindow(s, countedLoop, this.numericLocals, this.captureIds, this.integerRanges, this.byteWindowEntries.get(s) ?? new Set())
3894
+ : null;
3895
+ if (window) {
3896
+ const valid = emitByteWindowGuard(this, window);
3897
+ const fast = B.newLabel("bytes.window.fast"), slow = B.newLabel("bytes.window.slow"), done = B.newLabel("bytes.window.done");
3898
+ B.condBr(valid, fast, slow);
3899
+ const originalRanges = this.integerRanges, originalBounds = this.bytesBounds;
3900
+ this.byteWindowsEnabled = false;
3901
+ try {
3902
+ B.startBlock(slow);
3903
+ this.emitStmt(s);
3904
+ B.br(done);
3905
+ B.startBlock(fast);
3906
+ this.integerRanges = window.ranges;
3907
+ this.bytesBounds = new Set([...originalBounds, ...window.bounds]);
3908
+ this.emitStmt(s);
3909
+ B.br(done);
3910
+ }
3911
+ finally {
3912
+ this.integerRanges = originalRanges;
3913
+ this.bytesBounds = originalBounds;
3914
+ this.byteWindowsEnabled = true;
3915
+ }
3916
+ B.startBlock(done);
3917
+ break;
3918
+ }
3754
3919
  let countedJoin = null;
3755
3920
  if (countedLoop?.guarded) {
3756
3921
  const bound = this.emitExpr(countedLoop.limit);
@@ -3839,8 +4004,10 @@ export class LlEmitter {
3839
4004
  };
3840
4005
  const lu = s.update || capturedLets.length > 0 ? B.newLabel("loop.u") : lc;
3841
4006
  freshenBindings();
4007
+ const workerBudget = this.workerLoopBudget();
3842
4008
  B.br(lc);
3843
4009
  B.startBlock(lc);
4010
+ this.emitWorkerLoopCheck(workerBudget);
3844
4011
  if (integerLoop && integerSlot) {
3845
4012
  const receiver = this.emitStableReceiver(integerLoop.limitReceiver, []);
3846
4013
  const lenPtr = B.tmp();
@@ -3922,23 +4089,49 @@ export class LlEmitter {
3922
4089
  if (s.iterable.type.kind !== "array")
3923
4090
  throw new LlvmUnsupportedError(`forOf:${s.iterable.type.kind}`, s.loc);
3924
4091
  const elem = s.iterable.type.elem;
3925
- const arr = this.emitExpr(s.iterable);
3926
- const idxSlot = B.slot();
3927
- B.entryAllocas.push(`${idxSlot} = alloca double`);
3928
- B.line(`store double ${f64Lit(0)}, ptr ${idxSlot}`);
4092
+ const stored = s.iterable.kind === "varRef" ? this.storedSplits.get(s.iterable.localId) : undefined;
4093
+ const direct = this.streamingSplitsEnabled ? stringSplit(s.iterable) : null;
4094
+ const snapshot = stored
4095
+ ? loadSplitSnapshot(this, stored)
4096
+ : direct
4097
+ ? emitSplitSnapshot(this, direct)
4098
+ : null;
4099
+ const arr = snapshot ? null : this.emitExpr(s.iterable);
4100
+ const cursor = snapshot ? emitSplitCursor(this, snapshot) : null;
4101
+ const scratch = snapshot ? emitSplitScratch(this) : null;
4102
+ const deferred = snapshot && canDeferSplitPiece(s, this.currentLocals.get(s.localId));
4103
+ const spanLength = deferred ? B.slot() : null;
4104
+ if (spanLength)
4105
+ B.entryAllocas.push(`${spanLength} = alloca ${this.sizeType}`);
4106
+ const idxSlot = snapshot ? null : B.slot();
4107
+ if (idxSlot) {
4108
+ B.entryAllocas.push(`${idxSlot} = alloca double`);
4109
+ B.line(`store double ${f64Lit(0)}, ptr ${idxSlot}`);
4110
+ }
3929
4111
  const lc = B.newLabel("fof.c");
3930
4112
  const lb = B.newLabel("fof.b");
3931
4113
  const lu = B.newLabel("fof.u");
3932
4114
  const le = B.newLabel("fof.e");
4115
+ const workerBudget = this.workerLoopBudget();
3933
4116
  B.br(lc);
3934
4117
  B.startBlock(lc);
3935
- const i = B.tmp();
3936
- const len = B.tmp();
4118
+ this.emitWorkerLoopCheck(workerBudget);
3937
4119
  const inBounds = B.tmp();
3938
- this.declare(`declare double @scr_arr_len(ptr)`);
3939
- B.line(`${i} = load double, ptr ${idxSlot}`);
3940
- B.line(`${len} = call double @scr_arr_len(ptr ${arr.name})`);
3941
- B.line(`${inBounds} = fcmp olt double ${i}, ${len}`);
4120
+ let cur;
4121
+ if (snapshot && cursor && scratch) {
4122
+ cur = spanLength
4123
+ ? emitSplitSpanNext(this, snapshot, cursor, spanLength)
4124
+ : emitSplitNext(this, snapshot, cursor, scratch);
4125
+ B.line(`${inBounds} = icmp ne ptr ${cur}, null`);
4126
+ }
4127
+ else {
4128
+ const i = B.tmp(), len = B.tmp();
4129
+ this.declare(`declare double @scr_arr_len(ptr)`);
4130
+ B.line(`${i} = load double, ptr ${idxSlot}`);
4131
+ B.line(`${len} = call double @scr_arr_len(ptr ${arr.name})`);
4132
+ B.line(`${inBounds} = fcmp olt double ${i}, ${len}`);
4133
+ cur = i;
4134
+ }
3942
4135
  B.condBr(inBounds, lb, le);
3943
4136
  B.startBlock(lb);
3944
4137
  this.jumpTargets.push({
@@ -3956,11 +4149,22 @@ export class LlEmitter {
3956
4149
  this.scopes.push([]);
3957
4150
  const localInfo = this.currentLocals.get(s.localId);
3958
4151
  const slot = `%${mangleLocal(s.localId)}`;
3959
- const acc = elemAccess(elem);
3960
- const accTy = acc === "f64" ? "double" : acc === "bool" ? "i1" : "ptr";
3961
- this.declare(`declare ${acc === "bool" ? "zeroext i1" : accTy} @scr_arr_get_${acc}(ptr, double)`);
3962
- const cur = B.tmp();
3963
- B.line(`${cur} = call ${accTy} @scr_arr_get_${acc}(ptr ${arr.name}, double ${i})`);
4152
+ const borrowedElement = !snapshot && this.loopArrayBorrows.has(s);
4153
+ if (!snapshot) {
4154
+ const acc = elemAccess(elem);
4155
+ const accTy = acc === "f64" ? "double" : acc === "bool" ? "i1" : "ptr";
4156
+ const getter = borrowedElement ? "scr_arr_borrow_ref" : `scr_arr_get_${acc}`;
4157
+ this.declare(`declare ${acc === "bool" ? "zeroext i1" : accTy} @${getter}(ptr, double)`);
4158
+ const value = B.tmp();
4159
+ B.line(`${value} = call ${accTy} @${getter}(ptr ${arr.name}, double ${cur})`);
4160
+ cur = value;
4161
+ }
4162
+ if (spanLength) {
4163
+ const length = B.tmp();
4164
+ B.line(`${length} = load ${this.sizeType}, ptr ${spanLength}`);
4165
+ this.splitSpans.set(s.localId, { bytes: cur, length, scratch: scratch });
4166
+ cur = "null";
4167
+ }
3964
4168
  if (localInfo?.boxed) {
3965
4169
  // Captured loop variable: a fresh box per iteration, matching the
3966
4170
  // fresh const binding. The box takes ownership of a ref element's
@@ -3973,10 +4177,12 @@ export class LlEmitter {
3973
4177
  }
3974
4178
  else {
3975
4179
  B.line(`store ${this.llType(elem)} ${cur}, ptr ${slot}`);
3976
- if (isRefCounted(elem))
4180
+ if (isRefCounted(elem) && !borrowedElement)
3977
4181
  this.scopes[this.scopes.length - 1].push({ slot, type: elem });
3978
4182
  }
3979
4183
  this.emitStmts(s.body);
4184
+ if (spanLength)
4185
+ this.splitSpans.delete(s.localId);
3980
4186
  const endedWithJump = endsWithJump(s.body);
3981
4187
  const scope = this.scopes.pop();
3982
4188
  if (!endedWithJump)
@@ -3984,11 +4190,12 @@ export class LlEmitter {
3984
4190
  this.jumpTargets.pop();
3985
4191
  B.br(lu);
3986
4192
  B.startBlock(lu);
3987
- const i2 = B.tmp();
3988
- const i3 = B.tmp();
3989
- B.line(`${i2} = load double, ptr ${idxSlot}`);
3990
- B.line(`${i3} = fadd double ${i2}, ${f64Lit(1)}`);
3991
- B.line(`store double ${i3}, ptr ${idxSlot}`);
4193
+ if (idxSlot) {
4194
+ const i2 = B.tmp(), i3 = B.tmp();
4195
+ B.line(`${i2} = load double, ptr ${idxSlot}`);
4196
+ B.line(`${i3} = fadd double ${i2}, ${f64Lit(1)}`);
4197
+ B.line(`store double ${i3}, ptr ${idxSlot}`);
4198
+ }
3992
4199
  B.br(lc);
3993
4200
  B.startBlock(le);
3994
4201
  break;
@@ -4517,10 +4724,20 @@ export class LlEmitter {
4517
4724
  if (e.kind === "strLit")
4518
4725
  return { name: this.internLiteral(e.value), type: e.type };
4519
4726
  if (e.kind === "varRef") {
4727
+ this.materializeSplitLocal(e.localId);
4520
4728
  const binding = this.binding(e.localId);
4521
- // Capture boxes can carry TDZ and caught-value conversion semantics.
4522
- if (binding.kind === "boxed")
4523
- return this.emitExpr(e);
4729
+ if (binding.kind === "boxed") {
4730
+ // A projection consumes this pointer before any user code runs.
4731
+ // Keep TDZ checks and caught-value conversion on the owned path.
4732
+ if (!this.canBorrowReceiver(e))
4733
+ return this.emitExpr(e);
4734
+ const box = this.loadBox(binding.slot);
4735
+ const payload = this.B.tmp();
4736
+ const value = this.B.tmp();
4737
+ this.B.line(`${payload} = getelementptr inbounds %ScrBox, ptr ${box}, i32 0, i32 5`);
4738
+ this.B.line(`${value} = load ptr, ptr ${payload}`);
4739
+ return { name: value, type: e.type };
4740
+ }
4524
4741
  if (binding.kind === "global")
4525
4742
  this.checkGlobalTdz(e.localId);
4526
4743
  const value = this.B.tmp();
@@ -4576,8 +4793,14 @@ export class LlEmitter {
4576
4793
  }
4577
4794
  canBorrowReceiver(e) {
4578
4795
  switch (e.kind) {
4579
- case "varRef":
4580
- return this.binding(e.localId).kind !== "boxed";
4796
+ case "varRef": {
4797
+ const binding = this.binding(e.localId);
4798
+ return (binding.kind !== "boxed" ||
4799
+ (!binding.local.tdz &&
4800
+ binding.type.kind === e.type.kind &&
4801
+ e.type.kind !== "caught" &&
4802
+ isRefCounted(e.type)));
4803
+ }
4581
4804
  case "unionNarrow":
4582
4805
  return this.canBorrowReceiver(e.value);
4583
4806
  case "fieldGet":
@@ -4593,6 +4816,11 @@ export class LlEmitter {
4593
4816
  return false;
4594
4817
  }
4595
4818
  }
4819
+ materializeSplitLocal(localId) {
4820
+ const span = this.splitSpans.get(localId);
4821
+ if (span)
4822
+ materializeSplitPiece(this, localId, span);
4823
+ }
4596
4824
  emitExpr(e) {
4597
4825
  return emitExpr(this, e);
4598
4826
  }