numbl 0.2.0 → 0.3.3

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 (127) hide show
  1. package/binding.gyp +24 -36
  2. package/dist-cli/cli.js +16096 -18209
  3. package/dist-lib/graphics/types.d.ts +22 -0
  4. package/dist-lib/lib.d.ts +1 -0
  5. package/dist-lib/lib.js +61324 -62782
  6. package/dist-lib/numbl-core/executeCode.d.ts +11 -21
  7. package/dist-lib/numbl-core/executors/cJit/builtins.d.ts +30 -0
  8. package/dist-lib/numbl-core/executors/cJit/chainCodegen.d.ts +59 -0
  9. package/dist-lib/numbl-core/executors/cJit/chainExecutor.d.ts +27 -0
  10. package/dist-lib/numbl-core/executors/cJit/chainPass.d.ts +42 -0
  11. package/dist-lib/numbl-core/executors/cJit/codegen.d.ts +44 -0
  12. package/dist-lib/numbl-core/executors/cJit/compile.d.ts +45 -0
  13. package/dist-lib/numbl-core/executors/cJit/elemwiseCodegen.d.ts +23 -0
  14. package/dist-lib/numbl-core/executors/cJit/elemwiseStructural.d.ts +33 -0
  15. package/dist-lib/numbl-core/executors/cJit/fuseAnalyze.d.ts +39 -0
  16. package/dist-lib/numbl-core/executors/cJit/fuseCodegen.d.ts +16 -0
  17. package/dist-lib/numbl-core/executors/cJit/fuseExecutor.d.ts +28 -0
  18. package/dist-lib/numbl-core/executors/cJit/loopExecutor.d.ts +32 -0
  19. package/dist-lib/numbl-core/executors/cJit/register.d.ts +10 -0
  20. package/dist-lib/numbl-core/executors/cJit/whitelist.d.ts +15 -0
  21. package/dist-lib/numbl-core/executors/cache.d.ts +26 -0
  22. package/dist-lib/numbl-core/executors/context.d.ts +76 -0
  23. package/dist-lib/numbl-core/executors/index.d.ts +17 -0
  24. package/dist-lib/numbl-core/executors/jsJit/callExecutor.d.ts +25 -0
  25. package/dist-lib/numbl-core/{jit/js → executors/jsJit/codegen}/jitCodegen.d.ts +2 -2
  26. package/dist-lib/numbl-core/{jit/js → executors/jsJit/codegen}/jitCodegenHoist.d.ts +1 -1
  27. package/dist-lib/numbl-core/executors/jsJit/codegen/jsMultiReduction.d.ts +67 -0
  28. package/dist-lib/numbl-core/executors/jsJit/helpers/alloc.d.ts +12 -0
  29. package/dist-lib/numbl-core/{jit/js → executors/jsJit/helpers}/jitHelpers.d.ts +2 -2
  30. package/dist-lib/numbl-core/{jit/js → executors/jsJit/helpers}/jitHelpersComplex.d.ts +1 -1
  31. package/dist-lib/numbl-core/executors/jsJit/helpers/jitHelpersIndex.d.ts +33 -0
  32. package/dist-lib/numbl-core/{jit/js → executors/jsJit/helpers}/jitHelpersTensor.d.ts +7 -7
  33. package/dist-lib/numbl-core/executors/jsJit/jitCall.d.ts +59 -0
  34. package/dist-lib/numbl-core/executors/jsJit/jitLoop.d.ts +53 -0
  35. package/dist-lib/numbl-core/executors/jsJit/jitTopLevel.d.ts +44 -0
  36. package/dist-lib/numbl-core/executors/jsJit/loopExecutor.d.ts +15 -0
  37. package/dist-lib/numbl-core/{jit/jitLoopAnalysis.d.ts → executors/jsJit/lower/blockAnalysis.d.ts} +5 -5
  38. package/dist-lib/numbl-core/{jit → executors/jsJit/lower}/jitBailSafety.d.ts +1 -1
  39. package/dist-lib/numbl-core/{jit → executors/jsJit/lower}/jitLower.d.ts +18 -4
  40. package/dist-lib/numbl-core/{jit → executors/jsJit/lower}/jitLowerExpr.d.ts +11 -2
  41. package/dist-lib/numbl-core/{jit → executors/jsJit/lower}/jitLowerStmt.d.ts +2 -2
  42. package/dist-lib/numbl-core/{jit → executors/jsJit/lower}/jitLowerTypes.d.ts +2 -2
  43. package/dist-lib/numbl-core/{jit → executors/jsJit/lower}/scalarEmit.d.ts +2 -2
  44. package/dist-lib/numbl-core/executors/jsJit/shared.d.ts +120 -0
  45. package/dist-lib/numbl-core/executors/jsJit/topLevelExecutor.d.ts +17 -0
  46. package/dist-lib/numbl-core/executors/lowering.d.ts +166 -0
  47. package/dist-lib/numbl-core/executors/plugins.d.ts +39 -0
  48. package/dist-lib/numbl-core/executors/registry.d.ts +148 -0
  49. package/dist-lib/numbl-core/executors/types.d.ts +103 -0
  50. package/dist-lib/numbl-core/fileIOAdapter.d.ts +2 -0
  51. package/dist-lib/numbl-core/functionResolve.d.ts +7 -0
  52. package/dist-lib/numbl-core/helpers/check-helpers.d.ts +4 -5
  53. package/dist-lib/numbl-core/helpers/linsolve.d.ts +2 -3
  54. package/dist-lib/numbl-core/helpers/prng.d.ts +1 -2
  55. package/dist-lib/numbl-core/interpreter/builtins/datetime.d.ts +2 -1
  56. package/dist-lib/numbl-core/interpreter/builtins/misc.d.ts +4 -1
  57. package/dist-lib/numbl-core/interpreter/builtins/types.d.ts +4 -91
  58. package/dist-lib/numbl-core/interpreter/interpreter.d.ts +34 -44
  59. package/dist-lib/numbl-core/interpreter/interpreterSpecialBuiltins.d.ts +6 -3
  60. package/dist-lib/numbl-core/interpreter/types.d.ts +27 -12
  61. package/dist-lib/numbl-core/{jit/jitTypes.d.ts → jitTypes.d.ts} +15 -1
  62. package/dist-lib/numbl-core/jsUserFunctions.d.ts +8 -0
  63. package/dist-lib/numbl-core/lowering/loweringContext.d.ts +24 -0
  64. package/dist-lib/numbl-core/native/lapack-bridge.d.ts +3 -3
  65. package/dist-lib/numbl-core/parser/types.d.ts +20 -0
  66. package/dist-lib/numbl-core/runtime/constructors.d.ts +6 -6
  67. package/dist-lib/numbl-core/runtime/cow.d.ts +33 -0
  68. package/dist-lib/numbl-core/runtime/index.d.ts +3 -2
  69. package/dist-lib/numbl-core/runtime/indexing.d.ts +6 -1
  70. package/dist-lib/numbl-core/runtime/plotBuiltinDispatch.d.ts +86 -0
  71. package/dist-lib/numbl-core/runtime/plotUtils.d.ts +17 -2
  72. package/dist-lib/numbl-core/runtime/refcount.d.ts +85 -0
  73. package/dist-lib/numbl-core/runtime/runtime.d.ts +27 -66
  74. package/dist-lib/numbl-core/runtime/runtimeDispatch.d.ts +2 -2
  75. package/dist-lib/numbl-core/runtime/runtimeIndexing.d.ts +2 -2
  76. package/dist-lib/numbl-core/runtime/runtimeMemberAccess.d.ts +1 -1
  77. package/dist-lib/numbl-core/runtime/runtimePlot.d.ts +1 -0
  78. package/dist-lib/numbl-core/runtime/struct-access.d.ts +2 -1
  79. package/dist-lib/numbl-core/runtime/types.d.ts +104 -62
  80. package/dist-lib/numbl-core/runtime/utils.d.ts +2 -8
  81. package/dist-lib/numbl-core/version.d.ts +1 -1
  82. package/dist-plot-viewer/assets/index-COAM8o1E.js +4426 -0
  83. package/dist-plot-viewer/index.html +1 -1
  84. package/native/lapack_linsolve.cpp +1 -1
  85. package/native/numbl_addon.cpp +9 -0
  86. package/native/numbl_addon_common.h +2 -2
  87. package/native/ops/comparison.c +1 -1
  88. package/package.json +3 -8
  89. package/dist-lib/numbl-core/jit/c/abi.d.ts +0 -90
  90. package/dist-lib/numbl-core/jit/c/assemble.d.ts +0 -56
  91. package/dist-lib/numbl-core/jit/c/classify.d.ts +0 -70
  92. package/dist-lib/numbl-core/jit/c/compile.d.ts +0 -37
  93. package/dist-lib/numbl-core/jit/c/context.d.ts +0 -152
  94. package/dist-lib/numbl-core/jit/c/emit/assign.d.ts +0 -20
  95. package/dist-lib/numbl-core/jit/c/emit/complexScalar.d.ts +0 -18
  96. package/dist-lib/numbl-core/jit/c/emit/fused.d.ts +0 -42
  97. package/dist-lib/numbl-core/jit/c/emit/helpers.d.ts +0 -40
  98. package/dist-lib/numbl-core/jit/c/emit/index.d.ts +0 -14
  99. package/dist-lib/numbl-core/jit/c/emit/scalar.d.ts +0 -23
  100. package/dist-lib/numbl-core/jit/c/emit/stmt.d.ts +0 -25
  101. package/dist-lib/numbl-core/jit/c/emit/tensor.d.ts +0 -127
  102. package/dist-lib/numbl-core/jit/c/emit/userCall.d.ts +0 -58
  103. package/dist-lib/numbl-core/jit/c/epilogue.d.ts +0 -26
  104. package/dist-lib/numbl-core/jit/c/feasibility.d.ts +0 -44
  105. package/dist-lib/numbl-core/jit/c/hybrid.d.ts +0 -42
  106. package/dist-lib/numbl-core/jit/c/install.d.ts +0 -15
  107. package/dist-lib/numbl-core/jit/c/parityError.d.ts +0 -26
  108. package/dist-lib/numbl-core/jit/c/prelude.d.ts +0 -37
  109. package/dist-lib/numbl-core/jit/c/registry.d.ts +0 -51
  110. package/dist-lib/numbl-core/jit/c/visit.d.ts +0 -63
  111. package/dist-lib/numbl-core/jit/e1/install.d.ts +0 -13
  112. package/dist-lib/numbl-core/jit/e1/kernelEmit.d.ts +0 -54
  113. package/dist-lib/numbl-core/jit/e1/openmpFlag.d.ts +0 -13
  114. package/dist-lib/numbl-core/jit/e1/scalarFnKernel.d.ts +0 -44
  115. package/dist-lib/numbl-core/jit/fusedChainHelpers.d.ts +0 -65
  116. package/dist-lib/numbl-core/jit/fusedScalarEmit.d.ts +0 -61
  117. package/dist-lib/numbl-core/jit/fusion.d.ts +0 -71
  118. package/dist-lib/numbl-core/jit/fusionOps.d.ts +0 -25
  119. package/dist-lib/numbl-core/jit/index.d.ts +0 -7
  120. package/dist-lib/numbl-core/jit/jitLoop.d.ts +0 -25
  121. package/dist-lib/numbl-core/jit/jitTopLevel.d.ts +0 -22
  122. package/dist-lib/numbl-core/jit/js/jitHelpersIndex.d.ts +0 -33
  123. package/dist-lib/numbl-core/jit/js/jsFusedCodegen.d.ts +0 -17
  124. package/dist-lib/numbl-core/runtime/alloc.d.ts +0 -23
  125. package/dist-plot-viewer/assets/index-GiUNnMQg.js +0 -4426
  126. package/native/jit_runtime/jit_runtime.c +0 -261
  127. package/native/jit_runtime/jit_runtime.h +0 -204
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Shared helpers for the JS-JIT shape modules.
3
+ *
4
+ * The three shape modules (`jitTopLevel.ts`, `jitLoop.ts`,
5
+ * `jitCall.ts`) all do the same handful of things — type inference
6
+ * with `exact` pruning, progressive type widening, JS body assembly,
7
+ * `new Function` instantiation, env writeback. Those bits live here
8
+ * so the shape modules can stay focused on what's actually different
9
+ * between them (synthetic-fn shape, cacheKey scheme, run-time entry).
10
+ */
11
+ import type { Interpreter } from "../../interpreter/interpreter.js";
12
+ import type { Runtime } from "../../runtime/runtime.js";
13
+ import type { JitType } from "../../jitTypes.js";
14
+ import { type LoweringResult } from "./lower/jitLower.js";
15
+ import type { Stmt } from "../../parser/types.js";
16
+ /** Names that look like variables but never resolve to env values
17
+ * inside the JIT (constants, literals, the special `end` slot, the
18
+ * imaginary unit). Skipped when collecting env inputs. */
19
+ export declare const KNOWN_CONSTANTS: ReadonlySet<string>;
20
+ /**
21
+ * Drop `exact` from a numeric scalar `JitType`. Numeric `exact` only
22
+ * survives unification when two consecutive specializations see the
23
+ * *same* literal — almost never the case for variables — so stripping
24
+ * up front means the first specialization's cacheKey already matches
25
+ * later calls.
26
+ */
27
+ export declare function pruneArgType(t: JitType): JitType;
28
+ /**
29
+ * Progressive type widening: in-place unify each entry of `types`
30
+ * with the corresponding entry of `prev`. No-op when shapes don't
31
+ * match (different arity → different specialization, no widening).
32
+ *
33
+ * Widening that would collapse a known type to `unknown` is rejected
34
+ * — keep this call's concrete type so a fresh, specific spec gets
35
+ * built. Without this, a 1st call with (number, …) followed by a 2nd
36
+ * call with (tensor, …) would unify the first arg to `unknown` and
37
+ * poison every subsequent specialization with the same arg shape
38
+ * (chunkie hits this with `fcurve(ta)` (scalar discovery) followed
39
+ * by `fcurve(ts)` (tensor)).
40
+ */
41
+ export declare function widenAgainst(types: JitType[], prev: readonly JitType[] | undefined): void;
42
+ /**
43
+ * Gather env inputs for the synthetic FunctionDef of a top-level or
44
+ * loop block. For each candidate name: skip known constants, skip
45
+ * names not in env (likely fn names), infer the JIT type, prune
46
+ * `exact`. Returns null if any candidate has an unknown type — that's
47
+ * a structural blocker for lowering.
48
+ */
49
+ export declare function gatherTypedEnvInputs(interp: Interpreter, candidates: readonly string[]): {
50
+ inputs: string[];
51
+ inputTypes: JitType[];
52
+ } | null;
53
+ /**
54
+ * Build a `// File: …` + indented source-slice comment block for the
55
+ * code being JIT-compiled. Used by `--dump-js` so each emitted JS
56
+ * section is annotated with the .m source it came from. Falls back
57
+ * gracefully when the source isn't in `interp.fileSources`.
58
+ *
59
+ * `start` is rewound to the beginning of its line so the first line
60
+ * keeps its original indentation (Stmt spans start at the first
61
+ * non-whitespace char, which would otherwise leave the first line
62
+ * dedented relative to the rest).
63
+ */
64
+ export declare function buildJitSourceComment(interp: Interpreter, file: string, start: number, end: number): string;
65
+ /**
66
+ * Read the live env values for `inputs` in order. Used at run time
67
+ * to bind the compiled fn's parameters before invocation.
68
+ */
69
+ export declare function gatherEnvValues(interp: Interpreter, inputs: readonly string[]): unknown[];
70
+ /**
71
+ * Build the JS body for a synthetic FunctionDef: prepend each
72
+ * generated callee body (indented 2 spaces to nest inside the
73
+ * outer function), then append the main body.
74
+ */
75
+ export declare function assembleJsBody(result: LoweringResult, mainBody: string): string;
76
+ /**
77
+ * Wrap a JS body string in a `new Function(...)` and bind it to the
78
+ * runtime's helpers and `$rt`. Returns null when the body fails to
79
+ * parse (defensive — should not happen for IR that lowering accepted).
80
+ */
81
+ export declare function instantiateJsFn(rt: Runtime, paramNames: readonly string[], jsBody: string): ((...args: unknown[]) => unknown) | null;
82
+ /**
83
+ * Write a compiled fn's return value back into the interpreter env.
84
+ * The compiled fn returns either a single value (for one-output fns)
85
+ * or an array of values (for multi-output) — matching the JS-JIT
86
+ * convention used in the wrapper layer.
87
+ */
88
+ export declare function writeBackOutputs(interp: Interpreter, outputs: readonly string[], result: unknown): void;
89
+ /**
90
+ * Build a synthetic `FunctionDef` from (name, inputs, outputs, body)
91
+ * and lower it through `lowerFunction`. Returns null when lowering
92
+ * declines (constructs the JS-JIT IR doesn't model).
93
+ */
94
+ export declare function lowerSyntheticFn(interp: Interpreter, name: string, inputs: readonly string[], inputTypes: readonly JitType[], outputs: readonly string[], body: Stmt[]): LoweringResult | null;
95
+ /**
96
+ * Generate JS for a synthetic-fn lowered IR and instantiate it.
97
+ * Returns null when `new Function` throws (defensive — should not
98
+ * happen for IR that lowering accepted). Both top-level and loop
99
+ * codegen use this; the shape module then wraps the returned fn in
100
+ * its `*Compiled` shape and emits its own source/diagnostics.
101
+ */
102
+ export declare function generateSyntheticFnJS(interp: Interpreter, result: LoweringResult, inputs: readonly string[], outputCount: number): {
103
+ fn: (...args: unknown[]) => unknown;
104
+ jsBody: string;
105
+ } | null;
106
+ /** Outcome of `runSyntheticFnAgainstEnv`. `transient` distinguishes
107
+ * the recoverable JitBailToInterpreter case (cache preserved) from
108
+ * the hard JitFuncHandleBailError case (cache invalidated). */
109
+ export type SyntheticFnRunResult = {
110
+ ok: true;
111
+ } | {
112
+ ok: false;
113
+ transient: boolean;
114
+ };
115
+ /**
116
+ * Gather input values from env, invoke the compiled fn, write back
117
+ * outputs. Translates JIT bails to a SyntheticFnRunResult. Used by
118
+ * both top-level and loop run phases.
119
+ */
120
+ export declare function runSyntheticFnAgainstEnv(interp: Interpreter, fn: (...args: unknown[]) => unknown, inputs: readonly string[], outputs: readonly string[]): SyntheticFnRunResult;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * js-jit-top-level — JS codegen executor for the top-level shape.
3
+ *
4
+ * - `propose()` filters on `lowered.kind === "top-level"`, applies
5
+ * JIT-feasibility checks (hasReturn, display mode + unsuppressed
6
+ * assigns, IO+bail-risk).
7
+ *
8
+ * - `compile()` calls `generateTopLevelJS`. Cached by the registry
9
+ * under the classification's cacheKey.
10
+ *
11
+ * - `run()` calls `runTopLevelCompiled`. JitBailToInterpreter →
12
+ * transient bail; JitFuncHandleBailError → permanent bail.
13
+ * `consumed` claims the entire script body.
14
+ */
15
+ import type { Executor } from "../types.js";
16
+ import { type TopLevelLowered, type TopLevelCompiled } from "./jitTopLevel.js";
17
+ export declare const jsJitTopLevelExecutor: Executor<TopLevelLowered, TopLevelCompiled | null>;
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Shared lowering pipeline.
3
+ *
4
+ * The dispatcher calls `tryLower` once per stmt-dispatch (and
5
+ * `tryLowerCall` per function-call dispatch), before any executor is
6
+ * asked to propose. The result — a `LoweredStmt` — is passed to every
7
+ * executor's `propose()` as the first argument. `tryLower` returns
8
+ * `null` for stmts with no specialized lowering shape; the dispatcher
9
+ * falls through to its hardcoded `interp.execStmt` path in that case
10
+ * (no executor needs to filter on "raw stmt").
11
+ *
12
+ * Lowering produces an IR; it does NOT make codegen-feasibility
13
+ * decisions. "Can this be JS-JIT'd?" lives in the JS-JIT executor's
14
+ * propose. Lowering's only "no" is structural: type-unknown inputs,
15
+ * lowerFunction declined.
16
+ *
17
+ * Shapes today:
18
+ * - `top-level` — script body (top-level scope, first stmt). Whole
19
+ * script lowered as a synthetic FunctionDef.
20
+ * - `loop` — for/while loop stmt. Loop lowered as a synthetic
21
+ * FunctionDef that wraps just that stmt.
22
+ * - `call` — user-function call. Lowered via `tryLowerCall`
23
+ * from `dispatchCall`.
24
+ *
25
+ * Top-level and loop share the same underlying mechanics
26
+ * (`shared.ts`); they're modeled as separate kinds because they have
27
+ * distinct trigger conditions (script-root vs. control-flow stmt) and
28
+ * runtime semantics (claim entire stmt list vs. consume one stmt).
29
+ *
30
+ * Lowerings are cached by (head Stmt or FunctionDef, classification
31
+ * cacheKey).
32
+ */
33
+ import type { Stmt } from "../parser/types.js";
34
+ import type { FunctionDef } from "../interpreter/types.js";
35
+ import type { Interpreter } from "../interpreter/interpreter.js";
36
+ import type { DispatchContext } from "./context.js";
37
+ import { type TopLevelClassification, type TopLevelLowered } from "./jsJit/jitTopLevel.js";
38
+ import { type LoopClassification, type LoopLowered } from "./jsJit/jitLoop.js";
39
+ import { type CallClassification, type CallLowered } from "./jsJit/jitCall.js";
40
+ import { type FuseClassification } from "./cJit/fuseAnalyze.js";
41
+ /** What `propose()` receives — a discriminated union of the
42
+ * specialized shapes the dispatcher knows how to lower. Stmts with
43
+ * no specialized shape don't reach `propose()` at all; the dispatcher
44
+ * falls through to its hardcoded interpreter path. */
45
+ export type LoweredStmt = TopLevelLoweredStmt | LoopLoweredStmt | CallLoweredStmt | FuseLoweredStmt | SynthLoweredStmt;
46
+ /** Top-level shape: script body lowered to JS-JIT IR. */
47
+ export interface TopLevelLoweredStmt {
48
+ readonly kind: "top-level";
49
+ readonly classification: TopLevelClassification;
50
+ readonly lowered: TopLevelLowered;
51
+ readonly flags: TopLevelFlags;
52
+ }
53
+ /** Pre-computed feasibility flags for top-level codegen executors. */
54
+ export interface TopLevelFlags {
55
+ /** Body contains a `return` statement. JIT cannot model
56
+ * early-return from the synthetic top-level fn. */
57
+ readonly hasReturn: boolean;
58
+ /** Source body contains an unsuppressed assign / multiassign /
59
+ * non-void-call ExprStmt. In display-mode the JIT must bail —
60
+ * it has no emit for auto-display. */
61
+ readonly hasUnsuppressedAssign: boolean;
62
+ /** Lowered IR contains an I/O builtin (disp, fprintf, ...). */
63
+ readonly hasIO: boolean;
64
+ /** Lowered IR contains a possibly-bailing operation. Combined
65
+ * with hasIO, signals a body that mustn't be retried after a
66
+ * partial run (already-emitted output would duplicate). */
67
+ readonly hasBailRisk: boolean;
68
+ }
69
+ /** Loop shape: a For/While stmt lowered to JS-JIT IR. */
70
+ export interface LoopLoweredStmt {
71
+ readonly kind: "loop";
72
+ readonly classification: LoopClassification;
73
+ readonly lowered: LoopLowered;
74
+ readonly flags: LoopFlags;
75
+ }
76
+ /** Pre-computed feasibility flags for loop codegen executors. */
77
+ export interface LoopFlags {
78
+ readonly hasReturn: boolean;
79
+ readonly hasIO: boolean;
80
+ readonly hasBailRisk: boolean;
81
+ }
82
+ /** Call shape: a user-function call lowered to JS-JIT IR. Produced
83
+ * by `tryLowerCall`, not `tryLower` — function calls fire from
84
+ * expression evaluation, not from the stmt loop. */
85
+ export interface CallLoweredStmt {
86
+ readonly kind: "call";
87
+ readonly classification: CallClassification;
88
+ readonly lowered: CallLowered;
89
+ readonly flags: CallFlags;
90
+ /** Runtime arg values. Carried alongside the classification
91
+ * because the executor needs them at runCall time; unlike
92
+ * stmt-shape executors, the call executor can't re-fetch them
93
+ * from env. */
94
+ readonly args: readonly unknown[];
95
+ }
96
+ /** Pre-computed feasibility flags for call codegen executors. */
97
+ export interface CallFlags {
98
+ readonly hasIO: boolean;
99
+ readonly hasBailRisk: boolean;
100
+ }
101
+ /** Fuse shape: a single AST `Assign` whose RHS is a fusable
102
+ * element-wise expression tree. Produced by `analyzeFuse` over the
103
+ * raw AST stmt. */
104
+ export interface FuseLoweredStmt {
105
+ readonly kind: "fuse";
106
+ readonly classification: FuseClassification;
107
+ }
108
+ /** Synth shape: a `Synth` AST stmt produced by a registered AST
109
+ * transformer. The matching executor reads `data` (analysis the
110
+ * transformer pre-computed) and the `tag` discriminates among
111
+ * multiple registered transformers. */
112
+ export interface SynthLoweredStmt {
113
+ readonly kind: "synth";
114
+ readonly tag: string;
115
+ readonly data: unknown;
116
+ }
117
+ declare const BAILED: unique symbol;
118
+ type Bailed = typeof BAILED;
119
+ /** Per-owner lowering cache. Owner is either the head Stmt
120
+ * (stmt-shape lowerings) or the FunctionDef (call-shape lowerings).
121
+ * WeakMap-scoped so entries are reclaimed when the AST is dropped.
122
+ *
123
+ * Also tracks per-owner type-widening state: the most recent input
124
+ * type signature seen for a given (owner, slot). Classify phases
125
+ * consult this so a callee invoked with shifting input types
126
+ * converges to a single specialization rather than thrashing the
127
+ * cache. The slot string lets one owner host multiple widening
128
+ * trackers (e.g. one per nargout for call-shape). */
129
+ export declare class LoweringCache {
130
+ private readonly slots;
131
+ private readonly widening;
132
+ get(owner: object, cacheKey: string): LoweredStmt | Bailed | undefined;
133
+ set(owner: object, cacheKey: string, value: LoweredStmt): void;
134
+ markBailed(owner: object, cacheKey: string): void;
135
+ isBailed(value: unknown): value is Bailed;
136
+ /** Most recent input-type signature recorded for (owner, slot), or
137
+ * undefined when nothing has been recorded yet. */
138
+ getLastInputTypes(owner: object, slot: string): import("../jitTypes.js").JitType[] | undefined;
139
+ /** Record the latest unified input-type signature for (owner, slot). */
140
+ setLastInputTypes(owner: object, slot: string, types: readonly import("../jitTypes.js").JitType[]): void;
141
+ }
142
+ /**
143
+ * Try to lower the stmt at `siblings[i]` based on current runtime
144
+ * info. Returns a `LoweredStmt` for stmts that match a specialized
145
+ * shape, or `null` for stmts with no shape — the dispatcher falls
146
+ * through to its hardcoded interpreter path in that case.
147
+ *
148
+ * Whole-scope shapes (`top-level`) are NOT produced here — they're
149
+ * lowered separately via `tryLowerTopLevel` and dispatched through
150
+ * `Registry.tryRunWholeScope` before the per-stmt loop runs.
151
+ */
152
+ export declare function tryLower(siblings: readonly Stmt[], i: number, ctx: DispatchContext, cache: LoweringCache): LoweredStmt | null;
153
+ /**
154
+ * Lower a script body as a whole-scope unit. Called by the registry
155
+ * before the per-stmt dispatch loop runs; returns a TopLevelLoweredStmt
156
+ * for whole-scope executors to consider, or null when the lowering
157
+ * declines.
158
+ */
159
+ export declare function tryLowerTopLevel(interp: Interpreter, siblings: readonly Stmt[], cache: LoweringCache): TopLevelLoweredStmt | null;
160
+ /**
161
+ * Try to lower a user-function call. Always returns a
162
+ * `CallLoweredStmt` when classification succeeds; null when the
163
+ * cheap classify declines (`~` params, type-unknown args).
164
+ */
165
+ export declare function tryLowerCall(fn: FunctionDef, args: unknown[], nargout: number, interp: Interpreter, cache: LoweringCache): CallLoweredStmt | null;
166
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Mode-driven executor registration.
3
+ *
4
+ * `registerExecutorsForOpt(registry, opt)` is the single switch from
5
+ * an `--opt` value to a set of registered executors. Adding a new
6
+ * mode means extending the switch here — no other call-site changes.
7
+ *
8
+ * The AST interpreter is the dispatcher's hardcoded last-resort
9
+ * fallback (see `Registry.dispatch`); it doesn't need to be a
10
+ * registered executor.
11
+ *
12
+ * The C-JIT (e3) executors are registered via an injected callback
13
+ * (`setCJitRegistrar`). A Node-only entry point (`cli.ts`) imports
14
+ * `executors/cJit/register.ts`, which calls `setCJitRegistrar` at
15
+ * load time. The browser worker never imports that module, so the
16
+ * cJit subtree (which pulls in `node:fs`/`node:os`/`node:child_process`
17
+ * via `compile.ts`) stays out of the web bundle.
18
+ */
19
+ import type { Registry } from "./registry.js";
20
+ /** Optimization mode label.
21
+ *
22
+ * - `"0"` — pure AST interpreter, no executors registered.
23
+ * - `"1"` — JS-JIT suite (top-level / loop / call).
24
+ * - `"e3"` — C-JIT scalar-loop only. Targets compute-bound scalar
25
+ * loops by compiling the loop body to C and loading via koffi.
26
+ * Does NOT register the JS-JIT suite — the C-JIT loop executor
27
+ * either matches (and runs in C) or falls back to the AST
28
+ * interpreter. */
29
+ export type OptLevel = "0" | "1" | "e3";
30
+ export declare const OPT_LEVELS: readonly OptLevel[];
31
+ export declare function isOptLevel(s: string): s is OptLevel;
32
+ type CJitRegistrar = (registry: Registry) => void;
33
+ /** Wire up the C-JIT (e3) executors. Called from a Node-only entry
34
+ * point at startup so the browser bundle never reaches the cJit
35
+ * module graph. */
36
+ export declare function setCJitRegistrar(fn: CJitRegistrar): void;
37
+ /** Register the executors for a given optimization mode. */
38
+ export declare function registerExecutorsForOpt(registry: Registry, opt: OptLevel): void;
39
+ export {};
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Executor registry and dispatch.
3
+ *
4
+ * Three entry points, three concerns:
5
+ * - `dispatch(siblings, i, ctx)` — per-stmt dispatch. Each executor
6
+ * handles exactly one stmt (no consumed-N).
7
+ * - `dispatchCall(fn, args, nargout, interp)` — user-function call.
8
+ * - `tryRunWholeScope(siblings, interp)` — whole-script attempt,
9
+ * called once before the per-stmt loop runs. Top-level executors
10
+ * register here separately from per-stmt ones.
11
+ *
12
+ * Plugins register executors at startup; the dispatcher selects among
13
+ * them at runtime based on cost estimates. The AST interpreter is the
14
+ * always-matching last-resort fallback for per-stmt dispatch and isn't
15
+ * a registered executor.
16
+ */
17
+ import type { Stmt } from "../parser/types.js";
18
+ import type { ControlSignal, FunctionDef } from "../interpreter/types.js";
19
+ import type { Executor } from "./types.js";
20
+ import { DispatchContext } from "./context.js";
21
+ /** AST stmt-list transformer. Receives a stmt list and returns a
22
+ * list of the same semantics, possibly with `Synth` stmts inserted
23
+ * where the transformer can collapse a contiguous run of stmts into
24
+ * a unit handled by a specialized executor. The transform is
25
+ * shallow — recursive descent into For/While/If bodies happens
26
+ * lazily on the next call to `transformStmts`, cached separately. */
27
+ export type StmtTransformer = (stmts: readonly Stmt[]) => Stmt[];
28
+ export interface DispatchResult {
29
+ /** Control signal from interpreter execution (break/continue/return),
30
+ * if any. */
31
+ signal: ControlSignal | null;
32
+ }
33
+ /** Result from `dispatchCall`. `null` means no executor handled the
34
+ * call — the caller should fall through to its own
35
+ * interpreter-execution path. */
36
+ export type CallDispatchResult = {
37
+ result: unknown;
38
+ } | null;
39
+ /** Result from `tryRunWholeScope`. `null` means no whole-scope
40
+ * executor matched (or all bailed) — the caller should fall through
41
+ * to the per-stmt dispatch loop. */
42
+ export type WholeScopeResult = {
43
+ signal: ControlSignal | null;
44
+ } | null;
45
+ export declare class Registry {
46
+ /** Per-stmt executors (loop, call, fuse). */
47
+ private readonly executors;
48
+ /** Whole-scope executors (top-level). Iterated only by
49
+ * `tryRunWholeScope`, separate from per-stmt dispatch. */
50
+ private readonly wholeScopeExecutors;
51
+ /** AST stmt-list transformers. Run lazily on each stmt list before
52
+ * the per-stmt loop walks it; result cached per input list. */
53
+ private readonly transformers;
54
+ /** Memoize transformed lists by input list identity. WeakMap so
55
+ * entries vanish when the AST is dropped. The transformed list
56
+ * is also stored mapped to itself so a second call with the
57
+ * result hits the cache (idempotency). */
58
+ private transformCache;
59
+ private cache;
60
+ private loweringCache;
61
+ /** Pre-allocated context reused for `dispatchCall`. Avoids the
62
+ * per-call Map+Set allocation cost that previously motivated
63
+ * bypassing ctx entirely on call-heavy workloads. Safe to reuse
64
+ * because dispatchCall isn't reentrant within itself: each call's
65
+ * propose/compile/run touches the context synchronously, and any
66
+ * nested call dispatch goes through `Interpreter.callUserFunction`
67
+ * which re-enters dispatchCall and re-resets the context. */
68
+ private callCtx;
69
+ register(executor: Executor): void;
70
+ /** Register an executor that handles a whole stmt list as a unit
71
+ * (e.g. JS-JIT top-level). Iterated only by `tryRunWholeScope`
72
+ * and never seen by per-stmt dispatch. */
73
+ registerWholeScope(executor: Executor): void;
74
+ /** Register an AST stmt-list transformer. Transformers run on every
75
+ * stmt list the interpreter is about to walk (script body, function
76
+ * body, loop body, if branch, ...) before the per-stmt loop kicks
77
+ * in. They typically wrap contiguous runs of stmts in `Synth`
78
+ * nodes that a matching executor will recognize. Adding new
79
+ * transformers is additive — they compose in registration order. */
80
+ registerStmtTransformer(fn: StmtTransformer): void;
81
+ /** Transform a stmt list, applying all registered transformers in
82
+ * registration order. Cached per input-list identity (WeakMap).
83
+ * When no transformers are registered, returns the input unchanged
84
+ * without populating the cache (saves a Map lookup per dispatch). */
85
+ transformStmts(stmts: readonly Stmt[]): Stmt[];
86
+ /** Number of registered per-stmt executors. Mainly for tests. */
87
+ get size(): number;
88
+ /** Drop all cached compiled artifacts. Called from
89
+ * `Interpreter.clearAllCaches()` after addpath/rmpath etc. */
90
+ clearCache(): void;
91
+ /**
92
+ * Dispatch one statement at `siblings[i]`. Each executor handles
93
+ * exactly one stmt — the dispatcher always advances by one on
94
+ * success.
95
+ *
96
+ * Hot path. Two optimizations matter for the chunkie-helmholtz-
97
+ * scale workloads:
98
+ *
99
+ * 1. The AST interpreter is the always-applicable last-resort
100
+ * fallback. It is *not* a registered executor — the dispatcher
101
+ * hardcodes a direct call to `ctx.interp.execStmt(stmt)` when
102
+ * no specialized executor matches or all of them bail. This
103
+ * avoids per-dispatch match/Candidate/cache/result allocations
104
+ * for the most common path (where only the interpreter would
105
+ * fit anyway).
106
+ *
107
+ * 2. The reentrancy guard (`pushActive`/`popActive` /
108
+ * `isActive`) is short-circuited when the active set is
109
+ * empty — no sub-dispatch in flight means the bookkeeping is
110
+ * pure overhead.
111
+ */
112
+ dispatch(siblings: readonly Stmt[], i: number, ctx: DispatchContext): DispatchResult;
113
+ private runStmtCandidate;
114
+ /** Cache lookup + compile-on-miss + run + bail handling. Returns
115
+ * the executor's success RunResult, or null when the candidate
116
+ * bailed (cache may have been marked BAILED). When `guardKey` is
117
+ * non-null, the reentrancy guard pushes/pops around `run()` —
118
+ * passed by the stmt path; the call path passes null because
119
+ * `dispatchCall` isn't reentrant within itself. */
120
+ private executeCandidate;
121
+ /**
122
+ * Dispatch a user-function call. Iterates the same `executors`
123
+ * array as stmt dispatch — call-shape executors filter on
124
+ * `lowered.kind === "call"`. Returns `{ result }` when one of them
125
+ * handles the call, or `null` so the caller (callUserFunction)
126
+ * falls through to the AST interpreter.
127
+ *
128
+ * Reuses a pre-allocated DispatchContext (`callCtx`) to avoid the
129
+ * per-call Map+Set allocation that previously motivated bypassing
130
+ * ctx for call dispatch entirely.
131
+ */
132
+ dispatchCall(fn: FunctionDef, args: unknown[], nargout: number, interp: import("../interpreter/interpreter.js").Interpreter): CallDispatchResult;
133
+ private runCallCandidate;
134
+ private getCallCtx;
135
+ /**
136
+ * Whole-scope dispatch: try to handle the entire stmt list as a
137
+ * single unit (e.g. JS-JIT top-level). Called by the Interpreter
138
+ * once before the per-stmt loop runs. Returns:
139
+ * - `{ signal }` when a whole-scope executor handled the body.
140
+ * The caller should skip the per-stmt loop entirely.
141
+ * - `null` when no whole-scope executor matched (or all bailed).
142
+ * The caller falls through to per-stmt dispatch.
143
+ */
144
+ tryRunWholeScope(siblings: readonly Stmt[], interp: import("../interpreter/interpreter.js").Interpreter): WholeScopeResult;
145
+ private finishWholeScope;
146
+ }
147
+ /** Build a fresh dispatch context. */
148
+ export declare function makeRootContext(interp: import("../interpreter/interpreter.js").Interpreter, registry: Registry): DispatchContext;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Executor registry — core types.
3
+ *
4
+ * See docs/developer_reference/executors.md for the design overview.
5
+ *
6
+ * The interpreter delegates each statement (or run of statements) — and
7
+ * each user-function call — to a registry of Executors. Each executor
8
+ * implements one strategy (interpreter, JS-JIT, C-kernel, ...). On each
9
+ * dispatch, every executor may submit a Proposal; the dispatcher picks
10
+ * the lowest-cost. Stmt-shape and call-shape work share the same
11
+ * Executor interface — they're discriminated via the lowered statement
12
+ * passed to propose().
13
+ */
14
+ import type { DispatchContext } from "./context.js";
15
+ import type { LoweredStmt } from "./lowering.js";
16
+ /** Estimated cost of using this executor for the proposed work.
17
+ * Numbers can be very rough at first; the dispatcher's policy is
18
+ * refined separately from executors. */
19
+ export interface CostEstimate {
20
+ /** One-time compile cost on cache miss. */
21
+ compileMs: number;
22
+ /** Per-call dispatch overhead (marshaling, frame setup, ...). */
23
+ perCallNs: number;
24
+ /** Estimated work done by the compiled artifact for the proposed
25
+ * input sizes. */
26
+ runNs: number;
27
+ }
28
+ /** Reason a `run` invocation could not complete. The dispatcher
29
+ * invalidates the cache entry and tries the next-best candidate. */
30
+ export interface BailReason {
31
+ message: string;
32
+ cause?: unknown;
33
+ }
34
+ export type RunResult =
35
+ /** Stmt-shape success — handled the head stmt. Registered executors
36
+ * don't produce control signals (break/continue/return); only the
37
+ * hardcoded interpreter fallback in `Registry.dispatch` does.
38
+ * The dispatcher always advances by exactly one stmt on success. */
39
+ {
40
+ ok: true;
41
+ }
42
+ /** Call-shape success — used by executors that handle a
43
+ * CallLoweredStmt. The dispatcher's call entry point
44
+ * (`dispatchCall`) returns this `result` to the caller. */
45
+ | {
46
+ result: unknown;
47
+ } | {
48
+ bail: BailReason;
49
+ /** When true, the bail is not cached: future dispatches re-enter
50
+ * the executor as if cache had never been touched. Use for
51
+ * shim/wrapper executors whose internal classify logic must
52
+ * re-run on every call (e.g., because the wrapped layer caches
53
+ * on its own keying scheme). Default false. */
54
+ transient?: boolean;
55
+ };
56
+ /** An executor's bid to handle the current dispatch. The dispatcher
57
+ * picks the lowest-cost proposal; the executor's own `data` flows
58
+ * through to compile() and run() unchanged. */
59
+ export interface Proposal<D> {
60
+ /** Opaque per-executor data; passed to compile() and run(). */
61
+ data: D;
62
+ cost: CostEstimate;
63
+ /** Whether this specific proposal's compiled artifact may fail an
64
+ * invariant mid-execution (and thus need re-running by a fallback).
65
+ * The dispatcher filters bail-risk proposals out of contexts marked
66
+ * `requireNoBail`. Per-proposal because a single executor may
67
+ * produce both bail-risky and bail-safe proposals depending on the
68
+ * inputs it sees. */
69
+ bailRisk: boolean;
70
+ }
71
+ export interface Executor<D = unknown, C = unknown> {
72
+ /** Stable identifier for logging, cache keys, and test selection. */
73
+ readonly name: string;
74
+ /** Submit a bid to handle this stmt. Runs on every dispatch — must
75
+ * be cheap.
76
+ *
77
+ * Receives the lowered statement produced by the dispatcher's
78
+ * pre-propose lowering pass. The `kind` field discriminates: a
79
+ * specialized shape (e.g. `"top-level"`) carries a lowered IR
80
+ * plus pre-computed feasibility flags; the fallback `"stmt"` kind
81
+ * carries the raw AST stmt for executors that classify from the
82
+ * AST directly.
83
+ *
84
+ * Codegen-feasibility decisions (display mode, IO+bail-risk, etc.)
85
+ * belong here — the lowering pipeline produces an IR; the
86
+ * executor decides whether to commit. For lookahead across
87
+ * multiple stmts, use `ctx.peekSibling(offset)` or `ctx.siblings`.
88
+ *
89
+ * Returns null to decline. */
90
+ propose(lowered: LoweredStmt, ctx: DispatchContext): Proposal<D> | null;
91
+ /** Stable cache key projected from the proposal data. Drops
92
+ * volatile bits (e.g., exact scalar values; tensor shape if
93
+ * codegen is shape-agnostic) so unrelated runs of the same code
94
+ * reuse compiled artifacts. */
95
+ cacheKey(data: D): string;
96
+ /** Compile to a runnable artifact. Called only on cache miss.
97
+ * Cached under (executor, headStmt, cacheKey). */
98
+ compile(data: D, ctx: DispatchContext): C;
99
+ /** Execute. Returns the number of consumed sibling stmts on success,
100
+ * or a Bail signalling the cache entry should be invalidated and
101
+ * the next-best candidate tried. */
102
+ run(compiled: C, data: D, ctx: DispatchContext): RunResult;
103
+ }
@@ -62,6 +62,8 @@ export interface FileIOAdapter {
62
62
  unzip?(zipfilename: string, outputfolder: string): string[];
63
63
  /** Return the temporary directory path. Optional. */
64
64
  tempdir?(): string;
65
+ /** Return the numbl user/config directory path. Optional. */
66
+ userpath?(): string;
65
67
  /** List directory entries. Returns array of {name, folder, bytes, isdir, mtimeMs}. Optional. */
66
68
  listDir?(dirPath: string): {
67
69
  name: string;
@@ -43,6 +43,13 @@ export type ResolvedTarget = {
43
43
  kind: "jsUserFunction";
44
44
  name: string;
45
45
  argTypes: ItemType[];
46
+ } | {
47
+ /** mtoc2-only user function (.mtoc2.js). Numbl's interpreter
48
+ * rejects calls to this kind; mtoc2's loader picks up the source
49
+ * from the workspace registry and evaluates it. */
50
+ kind: "mtoc2UserFunction";
51
+ name: string;
52
+ argTypes: ItemType[];
46
53
  } | {
47
54
  kind: "workspaceClassConstructor";
48
55
  className: string;
@@ -2,10 +2,9 @@
2
2
  * Helpers for writing builtin check functions.
3
3
  * These make check implementations shorter and easier to read.
4
4
  */
5
- import { type FloatXArrayType } from "../runtime/types.js";
6
5
  import { RTV, RuntimeValue } from "../runtime/index.js";
7
6
  /** Ensure data is Float64Array (needed by LAPACK bridges). */
8
- export declare function toF64(data: FloatXArrayType): Float64Array;
7
+ export declare function toF64(data: Float64Array): Float64Array;
9
8
  /**
10
9
  * Extract and normalize a string argument at runtime.
11
10
  * Strips surrounding quotes and lowercases the result.
@@ -31,18 +30,18 @@ export declare function buildEigenvectorMatrix(packedV: Float64Array, wi: Float6
31
30
  * Build a tensor, attaching the imaginary part only when it contains non-zero values.
32
31
  * Replaces the repeated `hasComplex ? RTV.tensor(re, shape, im) : RTV.tensor(re, shape)` pattern.
33
32
  */
34
- export declare function maybeComplexTensor(re: FloatXArrayType | Float64Array, shape: number[], im: FloatXArrayType | Float64Array | undefined): ReturnType<typeof RTV.tensor>;
33
+ export declare function maybeComplexTensor(re: Float64Array | Float64Array, shape: number[], im: Float64Array | Float64Array | undefined): ReturnType<typeof RTV.tensor>;
35
34
  /**
36
35
  * Build a diagonal matrix from a vector of values (column-major).
37
36
  * For square matrices pass just n; for rectangular pass [rows, cols].
38
37
  * Optionally includes an imaginary diagonal.
39
38
  */
40
- export declare function buildDiagMatrix(realVals: Float64Array | FloatXArrayType, imagVals: Float64Array | FloatXArrayType | undefined, size: number | [number, number]): ReturnType<typeof RTV.tensor>;
39
+ export declare function buildDiagMatrix(realVals: Float64Array | Float64Array, imagVals: Float64Array | Float64Array | undefined, size: number | [number, number]): ReturnType<typeof RTV.tensor>;
41
40
  /**
42
41
  * In-place Gauss-Jordan elimination with partial pivoting on a column-major
43
42
  * augmented matrix [A | B] of size `rows × totalCols`.
44
43
  */
45
- export declare function gaussJordanEliminate(aug: FloatXArrayType, rows: number, totalCols: number): void;
44
+ export declare function gaussJordanEliminate(aug: Float64Array, rows: number, totalCols: number): void;
46
45
  /**
47
46
  * Call a registered builtin from within another builtin's apply().
48
47
  * Convenience wrapper that includes the caller name in error messages.