numbl 0.1.7 → 0.3.0

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 (108) hide show
  1. package/binding.gyp +59 -3
  2. package/dist-cli/cli.js +22538 -7936
  3. package/dist-lib/lib.js +34682 -20852
  4. package/dist-lib/numbl-core/executeCode.d.ts +13 -0
  5. package/dist-lib/numbl-core/fileIOAdapter.d.ts +2 -0
  6. package/dist-lib/numbl-core/helpers/reduction-helpers.d.ts +7 -2
  7. package/dist-lib/numbl-core/interpreter/builtins/datetime.d.ts +39 -0
  8. package/dist-lib/numbl-core/interpreter/builtins/index.d.ts +1 -0
  9. package/dist-lib/numbl-core/interpreter/builtins/time-system.d.ts +1 -0
  10. package/dist-lib/numbl-core/interpreter/builtins/types.d.ts +96 -5
  11. package/dist-lib/numbl-core/interpreter/interpreter.d.ts +41 -3
  12. package/dist-lib/numbl-core/interpreter/types.d.ts +1 -1
  13. package/dist-lib/numbl-core/jit/c/abi.d.ts +90 -0
  14. package/dist-lib/numbl-core/jit/c/assemble.d.ts +56 -0
  15. package/dist-lib/numbl-core/jit/c/classify.d.ts +70 -0
  16. package/dist-lib/numbl-core/jit/c/compile.d.ts +37 -0
  17. package/dist-lib/numbl-core/jit/c/context.d.ts +152 -0
  18. package/dist-lib/numbl-core/jit/c/emit/assign.d.ts +20 -0
  19. package/dist-lib/numbl-core/jit/c/emit/complexScalar.d.ts +18 -0
  20. package/dist-lib/numbl-core/jit/c/emit/fused.d.ts +42 -0
  21. package/dist-lib/numbl-core/jit/c/emit/helpers.d.ts +40 -0
  22. package/dist-lib/numbl-core/jit/c/emit/index.d.ts +14 -0
  23. package/dist-lib/numbl-core/jit/c/emit/scalar.d.ts +23 -0
  24. package/dist-lib/numbl-core/jit/c/emit/stmt.d.ts +25 -0
  25. package/dist-lib/numbl-core/jit/c/emit/tensor.d.ts +127 -0
  26. package/dist-lib/numbl-core/jit/c/emit/userCall.d.ts +58 -0
  27. package/dist-lib/numbl-core/jit/c/epilogue.d.ts +26 -0
  28. package/dist-lib/numbl-core/jit/c/feasibility.d.ts +44 -0
  29. package/dist-lib/numbl-core/jit/c/prelude.d.ts +37 -0
  30. package/dist-lib/numbl-core/jit/c/visit.d.ts +63 -0
  31. package/dist-lib/numbl-core/jit/e1/complexKernelEmit.d.ts +46 -0
  32. package/dist-lib/numbl-core/jit/e1/hash.d.ts +10 -0
  33. package/dist-lib/numbl-core/jit/e1/install.d.ts +13 -0
  34. package/dist-lib/numbl-core/jit/e1/kernelEmit.d.ts +54 -0
  35. package/dist-lib/numbl-core/jit/e1/multiReductionKernel.d.ts +66 -0
  36. package/dist-lib/numbl-core/jit/e1/openmpFlag.d.ts +13 -0
  37. package/dist-lib/numbl-core/jit/e1/scalarFnKernel.d.ts +44 -0
  38. package/dist-lib/numbl-core/jit/e2/assignKernel.d.ts +34 -0
  39. package/dist-lib/numbl-core/jit/e2/astToJitExpr.d.ts +25 -0
  40. package/dist-lib/numbl-core/jit/e2/cache.d.ts +80 -0
  41. package/dist-lib/numbl-core/jit/e2/chainKernelEmit.d.ts +55 -0
  42. package/dist-lib/numbl-core/jit/e2/classify.d.ts +119 -0
  43. package/dist-lib/numbl-core/jit/e2/compileFn.d.ts +16 -0
  44. package/dist-lib/numbl-core/jit/e2/complexChainKernelEmit.d.ts +79 -0
  45. package/dist-lib/numbl-core/jit/e2/emitShared.d.ts +71 -0
  46. package/dist-lib/numbl-core/jit/e2/install.d.ts +11 -0
  47. package/dist-lib/numbl-core/jit/e2/liveness.d.ts +29 -0
  48. package/dist-lib/numbl-core/jit/e2/loopKernel.d.ts +49 -0
  49. package/dist-lib/numbl-core/jit/e2/loopKernelEmit.d.ts +75 -0
  50. package/dist-lib/numbl-core/jit/e2/multiReductionDriver.d.ts +24 -0
  51. package/dist-lib/numbl-core/jit/e2/reductionKernelEmit.d.ts +72 -0
  52. package/dist-lib/numbl-core/jit/e2/scalarFnDriver.d.ts +29 -0
  53. package/dist-lib/numbl-core/jit/fusedChainHelpers.d.ts +65 -0
  54. package/dist-lib/numbl-core/jit/fusedScalarEmit.d.ts +69 -0
  55. package/dist-lib/numbl-core/jit/fusion.d.ts +71 -0
  56. package/dist-lib/numbl-core/jit/fusionOps.d.ts +25 -0
  57. package/dist-lib/numbl-core/jit/heavyOps.d.ts +15 -0
  58. package/dist-lib/numbl-core/{interpreter/jit → jit}/index.d.ts +2 -2
  59. package/dist-lib/numbl-core/jit/jitBailSafety.d.ts +41 -0
  60. package/dist-lib/numbl-core/{interpreter/jit → jit}/jitLoop.d.ts +2 -2
  61. package/dist-lib/numbl-core/{interpreter/jit → jit}/jitLoopAnalysis.d.ts +6 -1
  62. package/dist-lib/numbl-core/jit/jitLower.d.ts +122 -0
  63. package/dist-lib/numbl-core/jit/jitLowerExpr.d.ts +27 -0
  64. package/dist-lib/numbl-core/jit/jitLowerStmt.d.ts +9 -0
  65. package/dist-lib/numbl-core/{interpreter/jit → jit}/jitLowerTypes.d.ts +7 -3
  66. package/dist-lib/numbl-core/jit/jitTopLevel.d.ts +22 -0
  67. package/dist-lib/numbl-core/{interpreter/jit → jit}/jitTypes.d.ts +133 -1
  68. package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitCodegen.d.ts +2 -2
  69. package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitCodegenHoist.d.ts +19 -1
  70. package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitHelpers.d.ts +15 -3
  71. package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitHelpersIndex.d.ts +7 -0
  72. package/dist-lib/numbl-core/jit/js/jitHelpersTensor.d.ts +34 -0
  73. package/dist-lib/numbl-core/jit/js/jsFusedCodegen.d.ts +17 -0
  74. package/dist-lib/numbl-core/jit/js/jsMultiReduction.d.ts +70 -0
  75. package/dist-lib/numbl-core/jit/scalarEmit.d.ts +58 -0
  76. package/dist-lib/numbl-core/lexer/types.d.ts +2 -1
  77. package/dist-lib/numbl-core/native/lapack-bridge.d.ts +39 -1
  78. package/dist-lib/numbl-core/ops/bessel.d.ts +18 -0
  79. package/dist-lib/numbl-core/ops/comparison.d.ts +11 -0
  80. package/dist-lib/numbl-core/ops/complexBinaryElemwise.d.ts +10 -0
  81. package/dist-lib/numbl-core/ops/complexUnaryElemwise.d.ts +8 -0
  82. package/dist-lib/numbl-core/ops/dispatch.d.ts +26 -0
  83. package/dist-lib/numbl-core/ops/index.d.ts +8 -0
  84. package/dist-lib/numbl-core/ops/opCodes.d.ts +70 -0
  85. package/dist-lib/numbl-core/ops/realBinaryElemwise.d.ts +8 -0
  86. package/dist-lib/numbl-core/ops/realUnaryElemwise.d.ts +5 -0
  87. package/dist-lib/numbl-core/ops/reduce.d.ts +6 -0
  88. package/dist-lib/numbl-core/parser/types.d.ts +6 -0
  89. package/dist-lib/numbl-core/runtime/alloc.d.ts +23 -0
  90. package/dist-lib/numbl-core/runtime/runtime.d.ts +1 -0
  91. package/dist-lib/numbl-core/version.d.ts +1 -1
  92. package/native/jit_runtime/jit_runtime.c +261 -0
  93. package/native/jit_runtime/jit_runtime.h +204 -0
  94. package/native/numbl_addon.cpp +62 -1
  95. package/native/ops/bessel.c +572 -0
  96. package/native/ops/comparison.c +150 -0
  97. package/native/ops/complex_binary_elemwise.c +192 -0
  98. package/native/ops/complex_unary_elemwise.c +152 -0
  99. package/native/ops/numbl_ops.c +66 -0
  100. package/native/ops/numbl_ops.h +262 -0
  101. package/native/ops/real_binary_elemwise.c +85 -0
  102. package/native/ops/real_unary_elemwise.c +104 -0
  103. package/native/ops/reduce.c +162 -0
  104. package/native/ops_napi.cpp +320 -0
  105. package/package.json +8 -9
  106. package/dist-lib/numbl-core/interpreter/jit/jitHelpersTensor.d.ts +0 -28
  107. package/dist-lib/numbl-core/interpreter/jit/jitLower.d.ts +0 -23
  108. /package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitHelpersComplex.d.ts +0 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared fusible-operation name sets for JIT fusion analysis.
3
+ *
4
+ * Both the C-JIT and JS-JIT fusion paths use these to determine which
5
+ * tensor Call nodes are fusible element-wise unary ops or absorbable
6
+ * trailing reductions. The numeric op codes live in their respective
7
+ * backend files (feasibility.ts for C, jitHelpersTensor.ts for JS).
8
+ */
9
+ /** Tensor unary builtins fusible into per-element loops. */
10
+ export declare const FUSIBLE_TENSOR_UNARY_OPS: ReadonlySet<string>;
11
+ /**
12
+ * JS-JIT-safe subset: excludes transcendentals (exp, sin, cos, tan, etc.)
13
+ * which V8 can't SIMD-vectorize. Fusing these into a scalar per-element
14
+ * loop is slower than calling libnumbl_ops per-op (which uses -fopenmp-simd).
15
+ * The C-JIT uses the full set because GCC/Clang vectorize via #pragma omp simd.
16
+ */
17
+ export declare const FUSIBLE_TENSOR_UNARY_OPS_JS: ReadonlySet<string>;
18
+ /**
19
+ * Two-argument tensor element-wise builtins fusible into per-element loops.
20
+ * These are parsed as Call nodes (not Binary nodes) and need separate
21
+ * recognition in isPureElementwise / emitScalarExpr.
22
+ */
23
+ export declare const FUSIBLE_TENSOR_BINARY_OPS: ReadonlySet<string>;
24
+ /** Tensor reduction builtins absorbable as trailing reductions. */
25
+ export declare const FUSIBLE_TENSOR_REDUCTION_OPS: ReadonlySet<string>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared heavy-op heuristic for fused-loop emitters (e1 and e2).
3
+ *
4
+ * Counts the number of "expensive" math operations in a JitExpr — the
5
+ * kind of work that's heavy enough per element that OpenMP thread-
6
+ * spawn overhead pays off at N >= 100k. Arithmetic-only chains skip
7
+ * the parallel-for pragma because threads slow them down: the body
8
+ * becomes memory-bandwidth-bound and adding threads only adds overhead.
9
+ */
10
+ import type { JitExpr } from "./jitTypes.js";
11
+ export declare function countHeavyOps(expr: JitExpr): number;
12
+ /** Minimum element count before `#pragma omp parallel for simd` kicks
13
+ * in. Below this the thread-spawn cost dominates the work.
14
+ * Overridable via `NUMBL_OMP_THRESHOLD` for benchmarks. */
15
+ export declare function ompParallelThreshold(): number;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * JIT compilation entry point for interpreter function calls.
3
3
  */
4
- import type { Interpreter } from "../interpreter.js";
5
- import type { FunctionDef } from "../types.js";
4
+ import type { Interpreter } from "../interpreter/interpreter.js";
5
+ import type { FunctionDef } from "../interpreter/types.js";
6
6
  export declare const JIT_SKIP: unique symbol;
7
7
  export declare function tryJitCall(interp: Interpreter, fn: FunctionDef, args: unknown[], nargout: number): unknown | typeof JIT_SKIP;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Bail-safety gate for JIT bodies that contain I/O side effects.
3
+ *
4
+ * Mid-execution bails (JitBailToInterpreter / JitFuncHandleBailError)
5
+ * cause the interpreter to re-run the body from the top. If the body
6
+ * already emitted I/O before the bail, that output gets duplicated —
7
+ * which the user would notice.
8
+ *
9
+ * Rule: a body with any I/O statement may only be JIT-compiled if we
10
+ * can prove no bail can happen during execution. If there's no I/O,
11
+ * the body can be JIT'd normally (a bail just restarts silently).
12
+ *
13
+ * Walkers are IR-level — they see the actual lowered constructs that
14
+ * map 1:1 to runtime bail sites. The walkers recurse into the bodies
15
+ * of `UserCall`-ed functions (via `generatedIRBodies`), since a bail
16
+ * inside a callee still forces the caller to re-run.
17
+ */
18
+ import type { JitStmt } from "./jitTypes.js";
19
+ import type { GeneratedFn } from "./jitLower.js";
20
+ /**
21
+ * Names of I/O-emitting builtins that the JIT is willing to lower as
22
+ * ExprStmt calls. Any `Call` to one of these in the lowered IR marks
23
+ * the body as having observable I/O.
24
+ */
25
+ export declare const JIT_IO_BUILTINS: Set<string>;
26
+ /**
27
+ * Walk a body + its transitively called function bodies and return
28
+ * whether the combined execution graph contains an I/O call.
29
+ */
30
+ export declare function irHasIO(body: JitStmt[], generatedIRBodies: Map<string, GeneratedFn>): boolean;
31
+ /**
32
+ * Walk a body + its transitively called function bodies and return
33
+ * whether any construct exists that can throw `JitBailToInterpreter`
34
+ * or `JitFuncHandleBailError` at runtime.
35
+ *
36
+ * Bail-risky constructs:
37
+ * - AssignIndex / AssignIndexCol: may bail on out-of-bounds grow.
38
+ * - FuncHandleCall / UserDispatchCall: return-type check may bail.
39
+ * - UserCall: recurse into callee body.
40
+ */
41
+ export declare function irHasBailRisk(body: JitStmt[], generatedIRBodies: Map<string, GeneratedFn>): boolean;
@@ -7,8 +7,8 @@
7
7
  * inside the loop body. On success the compiled code runs and output
8
8
  * values are written back to the interpreter environment.
9
9
  */
10
- import type { Interpreter } from "../interpreter.js";
11
- import type { Stmt } from "../../parser/types.js";
10
+ import type { Interpreter } from "../interpreter/interpreter.js";
11
+ import type { Stmt } from "../parser/types.js";
12
12
  /**
13
13
  * Attempt to JIT-compile and execute a for-loop statement.
14
14
  * Returns true if JIT succeeded, false to fall back to interpretation.
@@ -6,7 +6,7 @@
6
6
  * - referenced: variables read inside the loop
7
7
  * - hasReturn: whether the loop body contains a return statement
8
8
  */
9
- import type { Stmt } from "../../parser/types.js";
9
+ import type { Stmt } from "../parser/types.js";
10
10
  export interface LoopVarInfo {
11
11
  /** Variables referenced in the loop that must come from enclosing scope */
12
12
  inputs: string[];
@@ -26,6 +26,11 @@ export declare function analyzeForLoop(stmt: Stmt & {
26
26
  * back when no later code reads them.
27
27
  */
28
28
  export declare function collectReadsFromSiblings(stmts: Stmt[], startIdx: number, out: Set<string>): void;
29
+ /**
30
+ * Analyze a top-level script body (list of statements) for JIT compilation.
31
+ * Used by `tryJitTopLevel` to wrap the whole main script as a synthetic fn.
32
+ */
33
+ export declare function analyzeTopLevel(stmts: Stmt[]): LoopVarInfo;
29
34
  /** Analyze a while loop statement for JIT compilation. */
30
35
  export declare function analyzeWhileLoop(stmt: Stmt & {
31
36
  type: "While";
@@ -0,0 +1,122 @@
1
+ /**
2
+ * AST -> JIT IR lowering with type propagation. Orchestrator that owns
3
+ * the shared context types (LowerCtx, SliceAlias), the public result
4
+ * types (GeneratedFn, LoweringResult), and the `lowerFunction` entry
5
+ * point. Actual statement and expression lowering lives in
6
+ * jitLowerStmt.ts and jitLowerExpr.ts; type-level helpers are in
7
+ * jitLowerTypes.ts.
8
+ *
9
+ * Returns null if any unsupported construct is encountered, causing the
10
+ * entire function to fall back to interpretation.
11
+ */
12
+ import type { FunctionDef } from "../interpreter/types.js";
13
+ import type { Interpreter } from "../interpreter/interpreter.js";
14
+ import type { JitType, JitExpr, JitStmt } from "./jitTypes.js";
15
+ import { type TypeEnv } from "./jitLowerTypes.js";
16
+ /**
17
+ * Lowered IR for a user function reached during a top-level lowering.
18
+ *
19
+ * JS-JIT caches the callee as generated JS source in `generatedFns` and
20
+ * is done. C-JIT needs to re-analyze the callee's IR (for recursive
21
+ * feasibility + emitting a static C function per callee), so we also
22
+ * cache the raw IR here. One entry per unique `jitName`; matches the
23
+ * JS-JIT specialization key.
24
+ */
25
+ export interface GeneratedFn {
26
+ fn: FunctionDef;
27
+ argTypes: JitType[];
28
+ outputNames: string[];
29
+ outputTypes: JitType[];
30
+ body: JitStmt[];
31
+ localVars: Set<string>;
32
+ nargout: number;
33
+ }
34
+ export interface LoweringResult {
35
+ body: JitStmt[];
36
+ outputNames: string[];
37
+ localVars: Set<string>;
38
+ hasTensorOps: boolean;
39
+ /** Generated JS code for called user functions: jitName → code */
40
+ generatedFns: Map<string, string>;
41
+ /** Lowered IR for called user functions: jitName → IR + metadata.
42
+ * Populated alongside `generatedFns` by `lowerUserFuncCall`; the
43
+ * C-JIT uses it for recursive feasibility and per-callee C emission. */
44
+ generatedIRBodies: Map<string, GeneratedFn>;
45
+ /** Type of the first output variable after lowering */
46
+ outputType: JitType | null;
47
+ /** Types of all output variables in outputNames order. Mirrors the
48
+ * shape of JS-JIT's `return [out0, out1, ...]`. */
49
+ outputTypes: JitType[];
50
+ /** Final typed environment after lowering the body. The hybrid
51
+ * loop-extraction pass uses this to look up live-in/live-out var
52
+ * types without re-running type inference. */
53
+ endEnv: TypeEnv;
54
+ }
55
+ /**
56
+ * A "slice alias" records that a MATLAB local was bound to a colon-slice of
57
+ * a real tensor (e.g. `pt = pts(:, i)`). Rather than materializing the slice
58
+ * as a RuntimeTensor per iteration, we remember the base tensor and a
59
+ * per-dim "template" of indices — scalar expressions captured at bind time
60
+ * for the non-colon dims, and `"colon"` placeholders for the colon dims.
61
+ * Subsequent reads like `pt(k)` substitute `k` into the colon positions and
62
+ * emit a direct scalar read on the base tensor, which compiles cleanly
63
+ * through the existing hoisted `idx{1,2,3}r_h` fast path.
64
+ */
65
+ export interface SliceAlias {
66
+ baseName: string;
67
+ baseType: JitType;
68
+ /**
69
+ * One entry per index of the original bind expression, in source order.
70
+ * A `"colon"` slot expects to be filled by the read-site's colon indices.
71
+ * An `"expr"` slot carries a JitExpr that will be substituted as-is.
72
+ */
73
+ template: ({
74
+ kind: "colon";
75
+ } | {
76
+ kind: "expr";
77
+ expr: JitExpr;
78
+ })[];
79
+ /** Sizes of the slice's colon dimensions, in source order. */
80
+ sliceShape: number[];
81
+ /** Indices into `template` where colon slots live, in source order. */
82
+ colonPositions: number[];
83
+ }
84
+ export interface LowerCtx {
85
+ env: TypeEnv;
86
+ localVars: Set<string>;
87
+ params: Set<string>;
88
+ /** First bail reason encountered during lowering; lets callers surface
89
+ * a hint for why JS-JIT declined a function (purely diagnostic, only
90
+ * consulted by the `NUMBL_LOG_CJIT_MISSES` tally). Writes should use
91
+ * `setBailReason()` which is idempotent — only the first reason sticks. */
92
+ bailReason?: string;
93
+ bailLine?: number;
94
+ /** Type of the deepest expr whose lowering was attempted — used as a
95
+ * last-resort bail reason hint when no more specific reason was set. */
96
+ lastExprType?: string;
97
+ lastExprLine?: number;
98
+ /** Variables that are actually assigned in the function body. */
99
+ assignedVars: Set<string>;
100
+ /**
101
+ * Map from a MATLAB local name to its slice alias, if any. A name is
102
+ * present here iff the most recent assignment to it was a whole-tensor
103
+ * colon slice. Reads of the name as a plain Ident bail; reads of
104
+ * `name(...)` substitute through the template and emit a direct scalar
105
+ * read of the base tensor. See `tryLowerAsSliceBind`.
106
+ */
107
+ sliceAliases: Map<string, SliceAlias>;
108
+ _hasTensorOps?: boolean;
109
+ interp?: Interpreter;
110
+ /** The nargout this specialization was requested with — inlined wherever
111
+ * the body reads the `nargout` identifier, since the JIT specializes per
112
+ * nargout already. */
113
+ nargout?: number;
114
+ generatedFns: Map<string, string>;
115
+ generatedIRBodies: Map<string, GeneratedFn>;
116
+ loweringInProgress: Set<string>;
117
+ /** Pre-built line break table for offset→line lookup. */
118
+ lineTable?: number[];
119
+ }
120
+ /** Idempotent bail-reason setter. Only the first reason per function sticks. */
121
+ export declare function setBailReason(ctx: LowerCtx, reason: string, line?: number): void;
122
+ export declare function lowerFunction(fn: FunctionDef, argTypes: JitType[], nargout: number, interp?: Interpreter, generatedFns?: Map<string, string>, loweringInProgress?: Set<string>, generatedIRBodies?: Map<string, GeneratedFn>): LoweringResult | null;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Expression lowering for the JIT. Paired with jitLowerStmt.ts; the two
3
+ * sides are mutually recursive (stmt bodies contain exprs; function-call
4
+ * exprs may trigger nested lowering of a callee's body). jitLower.ts is
5
+ * the orchestrator that ties them together via LowerCtx + lowerFunction.
6
+ */
7
+ import type { Expr } from "../parser/types.js";
8
+ import { type JitExpr } from "./jitTypes.js";
9
+ import type { LowerCtx } from "./jitLower.js";
10
+ export declare function lowerExpr(ctx: LowerCtx, expr: Expr): JitExpr | null;
11
+ /**
12
+ * Stage 21: lower `src(a:b)` on a real-tensor base into a
13
+ * `RangeSliceRead` IR node producing a fresh column-vector tensor.
14
+ *
15
+ * Accepts `Range` with no step (default step 1). `start` and `end`
16
+ * must lower to numeric/boolean scalar exprs. The result is a real
17
+ * tensor with shape `[?, 1]` — the exact length is runtime-dependent.
18
+ *
19
+ * Caller responsibility: match the parent expression shape
20
+ * `Index(Ident(src), [Range])` or `FuncCall(src, [Range])` before
21
+ * calling. Returns null if the source isn't a real tensor or the
22
+ * range isn't the expected shape.
23
+ */
24
+ export declare function tryLowerRangeSliceRead(ctx: LowerCtx, baseName: string, rangeExpr: Expr): JitExpr | null;
25
+ export declare function lowerIBuiltinCall(ctx: LowerCtx, expr: Expr & {
26
+ type: "FuncCall";
27
+ }): JitExpr | null;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Statement lowering for the JIT. Paired with jitLowerExpr.ts; the two
3
+ * sides are mutually recursive. jitLower.ts is the orchestrator that ties
4
+ * them together via LowerCtx + lowerFunction.
5
+ */
6
+ import type { Stmt } from "../parser/types.js";
7
+ import { type JitStmt } from "./jitTypes.js";
8
+ import type { LowerCtx } from "./jitLower.js";
9
+ export declare function lowerStmts(ctx: LowerCtx, stmts: Stmt[]): JitStmt[] | null;
@@ -9,8 +9,8 @@
9
9
  *
10
10
  * These have no dependency on LowerCtx or the lowering state.
11
11
  */
12
- import { BinaryOperation, UnaryOperation } from "../../parser/types.js";
13
- import { type JitType, type SignCategory } from "./jitTypes.js";
12
+ import { BinaryOperation, UnaryOperation } from "../parser/types.js";
13
+ import { type JitExpr, type JitType, type SignCategory } from "./jitTypes.js";
14
14
  export declare const KNOWN_CONSTANTS: Record<string, number>;
15
15
  export type TypeEnv = Map<string, JitType>;
16
16
  export declare function cloneEnv(env: TypeEnv): TypeEnv;
@@ -21,5 +21,9 @@ export declare function envsEqual(a: TypeEnv, b: TypeEnv): boolean;
21
21
  export declare function addSigns(a: SignCategory, b: SignCategory): SignCategory | undefined;
22
22
  export declare function mulSigns(a: SignCategory, b: SignCategory): SignCategory | undefined;
23
23
  export declare function combineSigns(a: SignCategory | undefined, b: SignCategory | undefined, op: BinaryOperation): SignCategory | undefined;
24
- export declare function binaryResultType(op: BinaryOperation, left: JitType, right: JitType): JitType | null;
24
+ export declare function binaryResultType(op: BinaryOperation, left: JitType, right: JitType,
25
+ /** Lowered operand exprs. Optional because tests / older callers pass
26
+ * only types. When provided, enables structural nonneg detection for
27
+ * patterns like `x .* x` that the type-only path can't see. */
28
+ leftExpr?: JitExpr, rightExpr?: JitExpr): JitType | null;
25
29
  export declare function unaryResultType(op: UnaryOperation, operand: JitType): JitType | null;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * JIT compilation for the top-level script body (the main workspace).
3
+ *
4
+ * Wraps the list of non-function, non-classdef statements of a script as
5
+ * a synthetic `FunctionDef` whose parameters are the live-in env vars
6
+ * and whose outputs are every variable assigned in the script. On
7
+ * success the compiled code runs once and all output values are written
8
+ * back to the interpreter's workspace env.
9
+ *
10
+ * Mirrors `tryJitLoop` in jitLoop.ts — same lowering, same JS/C backend
11
+ * pipeline, same progressive type widening. The differences:
12
+ * - the synthetic body is the list of stmts directly, not a single For/While
13
+ * - every assigned variable is live-out (the whole workspace is live)
14
+ * - cache key is per-Interpreter (a single script AST per interp run)
15
+ */
16
+ import type { Interpreter } from "../interpreter/interpreter.js";
17
+ import type { Stmt } from "../parser/types.js";
18
+ /**
19
+ * Attempt to JIT-compile and execute the top-level script body.
20
+ * Returns true if JIT succeeded, false to fall back to interpretation.
21
+ */
22
+ export declare function tryJitTopLevel(interp: Interpreter, stmts: Stmt[]): boolean;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * JIT type system and IR node definitions.
3
3
  */
4
- import type { BinaryOperation, UnaryOperation } from "../../parser/types.js";
4
+ import type { BinaryOperation, UnaryOperation } from "../parser/types.js";
5
5
  export type SignCategory = "positive" | "nonneg" | "nonpositive" | "negative";
6
6
  export type JitType = {
7
7
  kind: "number";
@@ -55,6 +55,8 @@ export type JitType = {
55
55
  shape?: number[];
56
56
  } | {
57
57
  kind: "dictionary";
58
+ } | {
59
+ kind: "function_handle";
58
60
  } | {
59
61
  kind: "unknown";
60
62
  };
@@ -125,6 +127,29 @@ export type JitExpr = {
125
127
  base: JitExpr;
126
128
  indices: JitExpr[];
127
129
  jitType: JitType;
130
+ } | {
131
+ /**
132
+ * Range slice read on a real tensor: `src(a:b)` producing a fresh
133
+ * column-vector tensor of length `b - a + 1`. Stage 21 — unblocks
134
+ * chunkie's chunk_nearparam Newton loop which splits a Legendre
135
+ * expansion vector via `r0 = all0(1:dim)`.
136
+ *
137
+ * Codegen emits `$h.subarrayCopy1r(srcData, srcLen, start, end)`
138
+ * using the hoisted data/length aliases. Per-iter allocation is
139
+ * unavoidable without a range-alias extension to stage 5; small
140
+ * slices are cheap in V8 young-gen.
141
+ */
142
+ tag: "RangeSliceRead";
143
+ baseName: string;
144
+ start: JitExpr;
145
+ /** `null` when the range endpoint is the `end` keyword — codegen
146
+ * substitutes the hoisted `.data.length` alias. */
147
+ end: JitExpr | null;
148
+ /** True when the source is a statically-known row vector
149
+ * (shape `[1, n]`); the slice then preserves row orientation.
150
+ * Otherwise the slice is a column vector (MATLAB semantics). */
151
+ isRow: boolean;
152
+ jitType: JitType;
128
153
  } | {
129
154
  tag: "TensorLiteral";
130
155
  rows: JitExpr[][];
@@ -181,6 +206,40 @@ export type JitExpr = {
181
206
  indexExpr: JitExpr;
182
207
  leafFieldName: string;
183
208
  jitType: JitType;
209
+ } | {
210
+ /**
211
+ * Indirect call through a function handle variable: `f(a, b)` where
212
+ * `f` has JitType `function_handle`. The codegen emits
213
+ * `$h.callFuncHandle($rt, f, arg1, arg2, ...)` which invokes the
214
+ * handle via its `jsFn` closure (or falls back to `rt.dispatch`).
215
+ * The `returnType` is determined at lowering time by probing the
216
+ * function handle's runtime value.
217
+ */
218
+ tag: "FuncHandleCall";
219
+ name: string;
220
+ args: JitExpr[];
221
+ jitType: JitType;
222
+ } | {
223
+ /**
224
+ * User function call that bailed recursive lowering (stage 24 —
225
+ * soft-bail). Instead of bailing the containing loop, emit a
226
+ * runtime dispatch through the interpreter. Return type is
227
+ * determined at JIT compile time by probing (actually calling
228
+ * the function once with representative args). The helper
229
+ * `callUserFunc` verifies the actual return type on every call
230
+ * and throws `JitFuncHandleBailError` on mismatch so the loop
231
+ * runner can invalidate the cache entry and fall back to
232
+ * interpretation.
233
+ *
234
+ * Use case: `oneintp(...)` / `lege.exev(...)` in chunkie's adap
235
+ * inner loop — the callee body has tensor arithmetic the JIT
236
+ * can't inline, but the outer loop (scalar stack + col-slice
237
+ * writes + scalar compares) is a perfect JIT fit.
238
+ */
239
+ tag: "UserDispatchCall";
240
+ name: string;
241
+ args: JitExpr[];
242
+ jitType: JitType;
184
243
  };
185
244
  export type JitStmt = {
186
245
  tag: "Assign";
@@ -217,6 +276,56 @@ export type JitStmt = {
217
276
  srcType: JitType;
218
277
  srcStart: JitExpr | null;
219
278
  srcEnd: JitExpr | null;
279
+ } | {
280
+ /**
281
+ * Multi-dim column slice write `dst(:, j) = src` where both `dst`
282
+ * and `src` are real tensors. LHS must be `Index(Ident(dst),
283
+ * [Colon, scalar_j])` with `dst` having statically 2-D shape.
284
+ * RHS must be a plain Ident referencing a real-tensor var whose
285
+ * total element count matches `dst.shape[0]` at runtime.
286
+ *
287
+ * Used by the loop JIT for chunkie's adapgausskerneval column
288
+ * stack write: `vals(:, jj+1) = v2`. Also covers
289
+ * chunkerinterior's `rss(:, jj) = rval(:, k)` after a slice
290
+ * alias binds the RHS.
291
+ */
292
+ tag: "AssignIndexCol";
293
+ baseName: string;
294
+ baseType: JitType;
295
+ colIndex: JitExpr;
296
+ srcBaseName: string;
297
+ srcType: JitType;
298
+ } | {
299
+ /**
300
+ * Page-slice write `dst(:, :, k) = rhs` where `dst` is a 3-D tensor
301
+ * and `rhs` is a 2-D tensor. Real-base + complex-rhs upgrades the
302
+ * base to complex in place. Drives chunkie helm2d.green's
303
+ * `grad(:,:,k) = ...` / `hess(:,:,k) = ...` assignments.
304
+ */
305
+ tag: "AssignIndexPage3d";
306
+ baseName: string;
307
+ /** Base tensor type AFTER the write (may be complex even if the
308
+ * initializer was real — the lowerer promotes in ctx.env). */
309
+ baseType: JitType;
310
+ /** Page index (1-based). */
311
+ pageIndex: JitExpr;
312
+ /** 2-D tensor RHS. */
313
+ value: JitExpr;
314
+ } | {
315
+ /**
316
+ * Struct field assign lvalue `s.f = v`. When `needsPromote` is
317
+ * true, the base is (re)initialized as a fresh empty struct
318
+ * before the field is set — mirrors MATLAB's
319
+ * `s = []; s.f = v` idiom that promotes the empty matrix to a
320
+ * struct on first field write. `valueType` is the JIT type of
321
+ * `value` so stage 12 can hoist subsequent reads through a
322
+ * typed alias.
323
+ */
324
+ tag: "AssignMember";
325
+ baseName: string;
326
+ fieldName: string;
327
+ value: JitExpr;
328
+ needsPromote: boolean;
220
329
  } | {
221
330
  tag: "If";
222
331
  cond: JitExpr;
@@ -252,9 +361,32 @@ export type JitStmt = {
252
361
  callName: string;
253
362
  args: JitExpr[];
254
363
  outputTypes: JitType[];
364
+ } | {
365
+ /**
366
+ * Call a synthetic specialization (installed as a forwarder on $h)
367
+ * and write back multiple outputs into outer-scope vars. Only
368
+ * produced by the hybrid-loop pass in hybrid.ts, which
369
+ * replaces a For/While JitStmt with this call form when the
370
+ * extracted loop specialization compiled to native C.
371
+ *
372
+ * Semantics (0 / 1 / N outputs):
373
+ * nargout==0 → emit `$h.callUser(...)` as a stmt (no writeback)
374
+ * nargout==1 → emit `out = $h.callUser(...)`
375
+ * nargout>=2 → emit `var $r = $h.callUser(...); out0=$r[0]; ...`
376
+ */
377
+ tag: "UserCallWriteback";
378
+ outputs: string[];
379
+ jitName: string;
380
+ name: string;
381
+ args: JitExpr[];
382
+ outputTypes: JitType[];
255
383
  } | {
256
384
  tag: "SetLoc";
257
385
  line: number;
386
+ } | {
387
+ /** C-JIT assertion: if this JitStmt survives to JS codegen at --opt 2,
388
+ * emit a runtime throw. The C-JIT codegen elides it. */
389
+ tag: "AssertCJit";
258
390
  };
259
391
  export interface JitCacheEntry {
260
392
  fn: (...args: unknown[]) => unknown;
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * IR walkers for hoist-pass data collection are in jitCodegenHoist.ts.
5
5
  */
6
- import { type JitStmt } from "./jitTypes.js";
7
- export declare function generateJS(body: JitStmt[], params: string[], outputs: string[], nargout: number, localVars: Set<string>, fileName?: string): string;
6
+ import { type JitStmt } from "../jitTypes.js";
7
+ export declare function generateJS(body: JitStmt[], params: string[], outputs: string[], nargout: number, localVars: Set<string>, fileName?: string, experimental?: string, par?: boolean): string;
@@ -5,7 +5,7 @@
5
5
  * for hoisting tensor aliases, struct field reads, and struct array
6
6
  * element reads to the top of the generated function.
7
7
  */
8
- import type { JitStmt } from "./jitTypes.js";
8
+ import type { JitStmt } from "../jitTypes.js";
9
9
  /**
10
10
  * Hoisted aliases for a tensor variable read/written in the loop body.
11
11
  * Maps the original variable name to local JS identifiers for its
@@ -33,6 +33,24 @@ export interface TensorUsage {
33
33
  * maximum indexing arity and whether all uses are on a real tensor.
34
34
  */
35
35
  export declare function collectTensorUsage(body: JitStmt[]): Map<string, TensorUsage>;
36
+ /**
37
+ * Walk the JIT IR collecting every base name that is the target of an
38
+ * `AssignMember` stmt (stage 22 struct field write). Used by the
39
+ * codegen to emit a one-time `$h.structUnshare_h(s)` at function entry
40
+ * for any such name that is also a function parameter — this preserves
41
+ * MATLAB value semantics when structs are passed by value and the
42
+ * callee mutates a field.
43
+ */
44
+ export declare function collectStructMemberWrites(body: JitStmt[]): Set<string>;
45
+ /**
46
+ * Walk the JIT IR collecting every name that is the target of any
47
+ * plain `Assign` stmt. Used by the stage-12 struct-field-read hoist to
48
+ * decide whether a struct-typed param is safe to hoist: if the body
49
+ * ever reassigns the name (e.g. `s = [];` promoted to struct, or `s =
50
+ * struct()` inside the loop), the pre-loop hoisted field aliases would
51
+ * be stale and must be disabled.
52
+ */
53
+ export declare function collectPlainAssignTargets(body: JitStmt[]): Set<string>;
36
54
  /**
37
55
  * Walk the JIT IR collecting all unique (baseName, fieldName) pairs
38
56
  * referenced by MemberRead nodes. The codegen hoists each pair as a
@@ -11,10 +11,22 @@
11
11
  * creates a fresh snapshot via a single spread expression.
12
12
  */
13
13
  export { re, im, mkc, cAdd, cSub, cMul, cDiv, cNeg, cConj, cAngle, cTruthy, } from "./jitHelpersComplex.js";
14
- export { bce, idx1, idx2, idxN, idx1r, idx2r, idx3r, idx1r_h, idx2r_h, idx3r_h, set1r_h, set2r_h, set3r_h, setRange1r_h, } from "./jitHelpersIndex.js";
15
- export { makeTensor, tensorBinaryOp, tensorCompareOp, tensorUnary, tensorNeg, vconcatGrow1r, unshare, asTensor, tAdd, tSub, tMul, tDiv, tPow, tEq, tNeq, tLt, tLe, tGt, tGe, } from "./jitHelpersTensor.js";
14
+ export { bce, idx1, idx2, idxN, idx1r, idx2r, idx3r, idx1r_h, idx2r_h, idx3r_h, set1r_h, set2r_h, set3r_h, setRange1r_h, setCol2r_h, subarrayCopy1r, subarrayCopy1rRow, } from "./jitHelpersIndex.js";
15
+ export { makeTensor, tensorBinaryOp, tensorCompareOp, tensorUnary, tensorNeg, vconcatGrow1r, unshare, shareTensor, asTensor, tDouble, tSum, tAdd, tSub, tMul, tDiv, tPow, tEq, tNeq, tLt, tLe, tGt, tGe, } from "./jitHelpersTensor.js";
16
+ export { JitBailToInterpreter } from "./jitHelpersIndex.js";
17
+ /**
18
+ * Thrown when a function handle called from JIT code returns a type
19
+ * different from what the JIT expected (determined by probing at compile
20
+ * time). The loop runner catches this and falls back to interpretation.
21
+ */
22
+ export declare class JitFuncHandleBailError extends Error {
23
+ readonly fnName: string;
24
+ readonly expectedType: string;
25
+ readonly actualType: string;
26
+ constructor(fnName: string, expectedType: string, actualType: string);
27
+ }
16
28
  export declare const jitHelpers: Record<string, unknown>;
17
- import type { IBuiltin } from "../builtins/index.js";
29
+ import type { IBuiltin } from "../../interpreter/builtins/index.js";
18
30
  /**
19
31
  * Build a per-runtime jitHelpers snapshot. Uses a single spread expression
20
32
  * for a fresh V8 hidden class (critical for inline-cache performance).
@@ -10,6 +10,10 @@
10
10
  * checks to ensure consistency with the interpreter.
11
11
  */
12
12
  import { type FloatXArrayType, type RuntimeTensor } from "../../runtime/types.js";
13
+ export declare class JitBailToInterpreter extends Error {
14
+ readonly reason: string;
15
+ constructor(reason: string);
16
+ }
13
17
  export declare function bce(): never;
14
18
  export declare function idx1(base: unknown, i: number): unknown;
15
19
  export declare function idx2(base: unknown, ri: number, ci: number): unknown;
@@ -24,3 +28,6 @@ export declare function set1r_h(data: FloatXArrayType, len: number, i: number, v
24
28
  export declare function set2r_h(data: FloatXArrayType, len: number, rows: number, ri: number, ci: number, v: number): void;
25
29
  export declare function set3r_h(data: FloatXArrayType, len: number, d0: number, d1: number, i1: number, i2: number, i3: number, v: number): void;
26
30
  export declare function setRange1r_h(dstData: FloatXArrayType, dstLen: number, dstStart: number, dstEnd: number, srcData: FloatXArrayType, srcLen: number, srcStart: number, srcEnd: number): void;
31
+ export declare function subarrayCopy1r(srcData: FloatXArrayType, srcLen: number, start: number, end: number): RuntimeTensor;
32
+ export declare function subarrayCopy1rRow(srcData: FloatXArrayType, srcLen: number, start: number, end: number): RuntimeTensor;
33
+ export declare function setCol2r_h(dstData: FloatXArrayType, dstRows: number, dstLen: number, col: number, srcData: FloatXArrayType, srcLen: number): void;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Tensor operation helpers for JIT-compiled code.
3
+ * Handles element-wise binary/unary ops, comparison ops, concat, and
4
+ * tensor coercion for struct-array field access.
5
+ */
6
+ import { type FloatXArrayType, type RuntimeTensor } from "../../runtime/types.js";
7
+ import type { RuntimeComplexNumber } from "../../runtime/types.js";
8
+ export declare function makeTensor(data: FloatXArrayType, imag: FloatXArrayType | undefined, shape: number[]): RuntimeTensor;
9
+ /** Bump rc on a tensor (no-op for non-tensor values). Used for var-to-var
10
+ * JIT assignments so the aliased tensor is no longer eligible for
11
+ * in-place buffer reuse. */
12
+ export declare function shareTensor(v: unknown): unknown;
13
+ type ScalarVal = number | RuntimeComplexNumber;
14
+ export declare function tensorBinaryOp(a: unknown, b: unknown, realOp: (x: number, y: number) => number, complexOp: (a: ScalarVal, b: ScalarVal) => ScalarVal): RuntimeTensor | ScalarVal;
15
+ export declare function tensorCompareOp(a: unknown, b: unknown, cmp: (x: number, y: number) => boolean): RuntimeTensor;
16
+ export declare function tensorUnary(dest: unknown, a: RuntimeTensor, fn: (x: number) => number): RuntimeTensor;
17
+ export declare function tensorNeg(dest: unknown, a: RuntimeTensor): RuntimeTensor;
18
+ export declare function tDouble(v: unknown): unknown;
19
+ export declare function tSum(v: unknown): unknown;
20
+ export declare function vconcatGrow1r(base: unknown, v: number): RuntimeTensor;
21
+ export declare function unshare(t: unknown): RuntimeTensor;
22
+ export declare function asTensor(v: unknown): RuntimeTensor;
23
+ export declare const tAdd: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor | ScalarVal;
24
+ export declare const tSub: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor | ScalarVal;
25
+ export declare const tMul: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor | ScalarVal;
26
+ export declare const tDiv: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor | ScalarVal;
27
+ export declare const tPow: (_dest: unknown, a: unknown, b: unknown) => RuntimeTensor | ScalarVal;
28
+ export declare const tEq: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor;
29
+ export declare const tNeq: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor;
30
+ export declare const tLt: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor;
31
+ export declare const tLe: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor;
32
+ export declare const tGt: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor;
33
+ export declare const tGe: (dest: unknown, a: unknown, b: unknown) => RuntimeTensor;
34
+ export {};
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Fused per-element loop emission for the JS-JIT.
3
+ *
4
+ * Given a FusibleChain (from fusion.ts), emits a single block-scoped
5
+ * JavaScript `for` loop that evaluates all the chain's tensor assigns
6
+ * as inline scalar expressions per element — no $h.tAdd / $h.tMul
7
+ * helper calls, no intermediate tensor allocations.
8
+ *
9
+ * Tensor var references become either:
10
+ * - `__<name>_data[__i]` for input params / pre-existing tensors
11
+ * - `__f_<name>` for chain-produced intermediates (scalar local)
12
+ *
13
+ * The optional trailing reduction is absorbed as an inline accumulator
14
+ * inside the same loop.
15
+ */
16
+ import type { FusibleChain } from "../fusion.js";
17
+ export declare function emitJsFusedChain(lines: string[], indent: string, chain: FusibleChain, allTensorVars: ReadonlySet<string>, paramTensors: ReadonlySet<string>, outputTensorNames: ReadonlySet<string>, _localTensorNames: ReadonlySet<string>, complexTensorNames: ReadonlySet<string>, complexScalarVars: ReadonlySet<string>, mangle: (n: string) => string, experimental?: string, par?: boolean): void;