numbl 0.4.17 → 0.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-browser/browser.js +1 -1
- package/dist-browser/numbl-core/executors/jit/callExecutor.d.ts +7 -1
- package/dist-browser/numbl-core/executors/jit/loopExecutor.d.ts +12 -7
- package/dist-browser/numbl-core/executors/jit/topLevelExecutor.d.ts +3 -0
- package/dist-browser/numbl-core/executors/jit/valueAdapter.d.ts +9 -2
- package/dist-browser/numbl-core/executors/registry.d.ts +8 -0
- package/dist-browser/numbl-core/interpreter/interpreter.d.ts +11 -0
- package/dist-browser/numbl-core/jit/builtins/runtime/system/cscalar.d.ts +4 -4
- package/dist-browser/numbl-core/jit/codegen/emitIndex.d.ts +1 -1
- package/dist-browser/numbl-core/jit/compileSpec.d.ts +4 -0
- package/dist-browser/numbl-core/jit/lowering/lower.d.ts +13 -0
- package/dist-browser/numbl-core/jitDeclineDiagnostics.d.ts +6 -0
- package/dist-browser/numbl-core/version.d.ts +1 -1
- package/dist-cli/cli.js +370 -110
- package/dist-lib/lib.js +307 -68
- package/dist-lib/numbl-core/executors/jit/callExecutor.d.ts +7 -1
- package/dist-lib/numbl-core/executors/jit/loopExecutor.d.ts +12 -7
- package/dist-lib/numbl-core/executors/jit/topLevelExecutor.d.ts +3 -0
- package/dist-lib/numbl-core/executors/jit/valueAdapter.d.ts +9 -2
- package/dist-lib/numbl-core/executors/registry.d.ts +8 -0
- package/dist-lib/numbl-core/interpreter/interpreter.d.ts +11 -0
- package/dist-lib/numbl-core/jit/builtins/runtime/system/cscalar.d.ts +4 -4
- package/dist-lib/numbl-core/jit/codegen/emitIndex.d.ts +1 -1
- package/dist-lib/numbl-core/jit/compileSpec.d.ts +4 -0
- package/dist-lib/numbl-core/jit/lowering/lower.d.ts +13 -0
- package/dist-lib/numbl-core/jitDeclineDiagnostics.d.ts +6 -0
- package/dist-lib/numbl-core/version.d.ts +1 -1
- package/dist-site-viewer/assets/{index-0mP47Rpm.js → index-B5Y5557F.js} +259 -259
- package/dist-site-viewer/assets/{numbl-worker-DjOZZaW4.js → numbl-worker-CtoT7C0f.js} +104 -88
- package/dist-site-viewer/index.html +1 -1
- package/dist-site-viewer/og.png +0 -0
- package/dist-site-viewer/robots.txt +4 -0
- package/package.json +3 -2
package/dist-cli/cli.js
CHANGED
|
@@ -29489,7 +29489,7 @@ function applyUnaryElemwiseMaybeComplex(v, realFn, complexFn, name, nativeOpCode
|
|
|
29489
29489
|
if (typeof v === "boolean") v = v ? 1 : 0;
|
|
29490
29490
|
if (typeof v === "number") {
|
|
29491
29491
|
const r = realFn(v);
|
|
29492
|
-
if (!Number.isNaN(r)) return r;
|
|
29492
|
+
if (!Number.isNaN(r) || Number.isNaN(v)) return r;
|
|
29493
29493
|
const c = complexFn(v, 0);
|
|
29494
29494
|
return mkc(c.re, c.im);
|
|
29495
29495
|
}
|
|
@@ -29505,7 +29505,7 @@ function applyUnaryElemwiseMaybeComplex(v, realFn, complexFn, name, nativeOpCode
|
|
|
29505
29505
|
tensorOps.realUnaryElemwise(nativeOpCode, n, v.data, nativeOut);
|
|
29506
29506
|
let firstNaN = -1;
|
|
29507
29507
|
for (let i = 0; i < n; i++) {
|
|
29508
|
-
if (Number.isNaN(nativeOut[i])) {
|
|
29508
|
+
if (Number.isNaN(nativeOut[i]) && !Number.isNaN(v.data[i])) {
|
|
29509
29509
|
firstNaN = i;
|
|
29510
29510
|
break;
|
|
29511
29511
|
}
|
|
@@ -29517,7 +29517,7 @@ function applyUnaryElemwiseMaybeComplex(v, realFn, complexFn, name, nativeOpCode
|
|
|
29517
29517
|
const outI3 = allocFloat64Array(n);
|
|
29518
29518
|
let hasImag2 = false;
|
|
29519
29519
|
for (let i = 0; i < n; i++) {
|
|
29520
|
-
if (!Number.isNaN(nativeOut[i])) {
|
|
29520
|
+
if (!Number.isNaN(nativeOut[i]) || Number.isNaN(v.data[i])) {
|
|
29521
29521
|
outR3[i] = nativeOut[i];
|
|
29522
29522
|
} else {
|
|
29523
29523
|
const c = complexFn(v.data[i], 0);
|
|
@@ -29533,7 +29533,7 @@ function applyUnaryElemwiseMaybeComplex(v, realFn, complexFn, name, nativeOpCode
|
|
|
29533
29533
|
let hasImag = false;
|
|
29534
29534
|
for (let i = 0; i < n; i++) {
|
|
29535
29535
|
const r = realFn(v.data[i]);
|
|
29536
|
-
if (!Number.isNaN(r)) {
|
|
29536
|
+
if (!Number.isNaN(r) || Number.isNaN(v.data[i])) {
|
|
29537
29537
|
outR2[i] = r;
|
|
29538
29538
|
outI2[i] = 0;
|
|
29539
29539
|
} else {
|
|
@@ -31940,6 +31940,9 @@ var ExpressionParser = class extends ParserBase {
|
|
|
31940
31940
|
if (this.peekToken() === 50 /* LParen */ && this.inMatrixExpr && this.pos > 0 && !this.tokensAdjacent(this.pos - 1, this.pos)) {
|
|
31941
31941
|
break;
|
|
31942
31942
|
}
|
|
31943
|
+
if (this.peekToken() === 54 /* LBrace */ && this.inMatrixExpr && this.pos > 0 && !this.tokensAdjacent(this.pos - 1, this.pos)) {
|
|
31944
|
+
break;
|
|
31945
|
+
}
|
|
31943
31946
|
if (this.consume(50 /* LParen */)) {
|
|
31944
31947
|
const start = expr.span.start;
|
|
31945
31948
|
const args = [];
|
|
@@ -39290,8 +39293,9 @@ defineBuiltin({
|
|
|
39290
39293
|
cases: [
|
|
39291
39294
|
{
|
|
39292
39295
|
match: (argTypes) => argTypes.length === 0 ? [{ kind: "number" }] : null,
|
|
39293
|
-
apply: () => {
|
|
39296
|
+
apply: (_args, nargout) => {
|
|
39294
39297
|
ticTime = performance.now();
|
|
39298
|
+
if (nargout === 0) return void 0;
|
|
39295
39299
|
return RTV.num(ticTime / 1e3);
|
|
39296
39300
|
}
|
|
39297
39301
|
}
|
|
@@ -40198,6 +40202,7 @@ function registerSpecialBuiltins(rt) {
|
|
|
40198
40202
|
if (na === 0) {
|
|
40199
40203
|
rt.output(`Elapsed time is ${elapsed.toFixed(6)} seconds.
|
|
40200
40204
|
`);
|
|
40205
|
+
return void 0;
|
|
40201
40206
|
}
|
|
40202
40207
|
return RTV.num(elapsed);
|
|
40203
40208
|
}
|
|
@@ -64795,7 +64800,7 @@ function getSourceLine(getSource, file, line) {
|
|
|
64795
64800
|
}
|
|
64796
64801
|
|
|
64797
64802
|
// src/numbl-core/version.ts
|
|
64798
|
-
var NUMBL_VERSION = "0.4.
|
|
64803
|
+
var NUMBL_VERSION = "0.4.19";
|
|
64799
64804
|
|
|
64800
64805
|
// src/cli-repl.ts
|
|
64801
64806
|
import { createInterface } from "readline";
|
|
@@ -66389,8 +66394,7 @@ var Registry = class {
|
|
|
66389
66394
|
const stmt = siblings[i];
|
|
66390
66395
|
const lowered = tryLower(siblings, i, ctx.interp, this.loweringCache);
|
|
66391
66396
|
if (lowered === null) {
|
|
66392
|
-
|
|
66393
|
-
return { signal: signal2 };
|
|
66397
|
+
return this.interpretFallback(ctx, stmt);
|
|
66394
66398
|
}
|
|
66395
66399
|
let bestExec = null;
|
|
66396
66400
|
let bestData = null;
|
|
@@ -66439,8 +66443,25 @@ var Registry = class {
|
|
|
66439
66443
|
}
|
|
66440
66444
|
}
|
|
66441
66445
|
}
|
|
66442
|
-
|
|
66443
|
-
|
|
66446
|
+
return this.interpretFallback(ctx, stmt);
|
|
66447
|
+
}
|
|
66448
|
+
/** Run one stmt on the AST interpreter.
|
|
66449
|
+
*
|
|
66450
|
+
* When that stmt is a loop, the interpreter is about to walk its body,
|
|
66451
|
+
* which invalidates the "an enclosing loop will capture me" assumption
|
|
66452
|
+
* behind the executors' `loopDepth` gate. `jitDeclinedLoopDepth` records
|
|
66453
|
+
* that for the duration of the body so nested loops and calls can bid for
|
|
66454
|
+
* themselves instead of running interpreted all the way down. */
|
|
66455
|
+
interpretFallback(ctx, stmt) {
|
|
66456
|
+
if (stmt.type === "For" || stmt.type === "While") {
|
|
66457
|
+
ctx.interp.jitDeclinedLoopDepth++;
|
|
66458
|
+
try {
|
|
66459
|
+
return { signal: ctx.interp.execStmt(stmt) };
|
|
66460
|
+
} finally {
|
|
66461
|
+
ctx.interp.jitDeclinedLoopDepth--;
|
|
66462
|
+
}
|
|
66463
|
+
}
|
|
66464
|
+
return { signal: ctx.interp.execStmt(stmt) };
|
|
66444
66465
|
}
|
|
66445
66466
|
runStmtCandidate(stmt, ctx, executor, data, lowered) {
|
|
66446
66467
|
const result = this.executeCandidate(ctx, executor, stmt, data, stmt);
|
|
@@ -66676,6 +66697,10 @@ __export(interpreterExec_exports, {
|
|
|
66676
66697
|
var lastDecline = null;
|
|
66677
66698
|
function recordJitDecline(d) {
|
|
66678
66699
|
lastDecline = d;
|
|
66700
|
+
if (typeof process !== "undefined" && process.env?.NUMBL_JIT_LOG) {
|
|
66701
|
+
const at = d.at ? ` [${d.at.file}@${d.at.start}]` : "";
|
|
66702
|
+
console.error(`[jit-decline] ${d.where}${at}: ${d.message}`);
|
|
66703
|
+
}
|
|
66679
66704
|
}
|
|
66680
66705
|
function getLastJitDecline() {
|
|
66681
66706
|
return lastDecline;
|
|
@@ -67842,7 +67867,12 @@ function evalLValueBase(base, defaultVal) {
|
|
|
67842
67867
|
}
|
|
67843
67868
|
const indices = this.evalIndicesWithEnd(baseVal, base.indices);
|
|
67844
67869
|
try {
|
|
67845
|
-
|
|
67870
|
+
const result = this.rt.indexCell(baseVal, indices);
|
|
67871
|
+
if (Array.isArray(result)) {
|
|
67872
|
+
if (result.length === 1) return result[0];
|
|
67873
|
+
if (result.length === 0) return defaultVal;
|
|
67874
|
+
}
|
|
67875
|
+
return result;
|
|
67846
67876
|
} catch {
|
|
67847
67877
|
return defaultVal;
|
|
67848
67878
|
}
|
|
@@ -68659,6 +68689,7 @@ function interpretWorkspaceFunction(target, args, nargout) {
|
|
|
68659
68689
|
fn = funcDefFromStmt(stmt);
|
|
68660
68690
|
break;
|
|
68661
68691
|
}
|
|
68692
|
+
if (stmt.type !== "ClassDef" && stmt.type !== "Import") break;
|
|
68662
68693
|
}
|
|
68663
68694
|
}
|
|
68664
68695
|
}
|
|
@@ -69458,6 +69489,17 @@ var Interpreter = class {
|
|
|
69458
69489
|
* interpreter never sees them, so this gate only fires on calls
|
|
69459
69490
|
* that genuinely happen at the interpreter level inside a loop. */
|
|
69460
69491
|
loopDepth = 0;
|
|
69492
|
+
/** @internal Number of those enclosing loops that the JIT *declined*, so
|
|
69493
|
+
* the interpreter is walking their bodies statement by statement. The
|
|
69494
|
+
* `loopDepth > 0` gate above assumes an enclosing loop will be compiled
|
|
69495
|
+
* and will swallow whatever is nested inside it; when that loop was
|
|
69496
|
+
* declined instead, the assumption is false and everything below it would
|
|
69497
|
+
* run interpreted however expensive it is. The executors therefore gate on
|
|
69498
|
+
* `loopDepth > 0 && jitDeclinedLoopDepth === 0`: still no per-iteration JIT
|
|
69499
|
+
* attempts underneath a loop that is about to be compiled, but a nested
|
|
69500
|
+
* loop or call under a declined one gets its own chance. Bumped by the
|
|
69501
|
+
* registry around the interpreter fallback for a loop stmt. */
|
|
69502
|
+
jitDeclinedLoopDepth = 0;
|
|
69461
69503
|
/** @internal Number of enclosing conditional blocks (`if` / `switch` /
|
|
69462
69504
|
* `try`) whose bodies the interpreter is currently executing. Used by
|
|
69463
69505
|
* the loop classifier: when a loop is dispatched with `condBlockDepth >
|
|
@@ -72643,8 +72685,12 @@ function mtoc2_cpow(a, b) {
|
|
|
72643
72685
|
}
|
|
72644
72686
|
return cexp2(mtoc2_cmul(b, clog2(a)));
|
|
72645
72687
|
}
|
|
72688
|
+
function nanOnRealAxis(z) {
|
|
72689
|
+
return z.im === 0 && Number.isNaN(z.re);
|
|
72690
|
+
}
|
|
72646
72691
|
function mtoc2_csqrt(z) {
|
|
72647
72692
|
if (z.im === 0) {
|
|
72693
|
+
if (Number.isNaN(z.re)) return { re: z.re, im: 0 };
|
|
72648
72694
|
if (z.re >= 0) return { re: Math.sqrt(z.re), im: 0 };
|
|
72649
72695
|
return { re: 0, im: Math.sqrt(-z.re) };
|
|
72650
72696
|
}
|
|
@@ -72658,14 +72704,17 @@ function mtoc2_cexp(z) {
|
|
|
72658
72704
|
return cexp2(z);
|
|
72659
72705
|
}
|
|
72660
72706
|
function mtoc2_clog(z) {
|
|
72707
|
+
if (nanOnRealAxis(z)) return { re: z.re, im: 0 };
|
|
72661
72708
|
return clog2(z);
|
|
72662
72709
|
}
|
|
72663
72710
|
function mtoc2_clog2(z) {
|
|
72711
|
+
if (nanOnRealAxis(z)) return { re: z.re, im: 0 };
|
|
72664
72712
|
const l = clog2(z);
|
|
72665
72713
|
const lg2 = Math.log(2);
|
|
72666
72714
|
return { re: l.re / lg2, im: l.im / lg2 };
|
|
72667
72715
|
}
|
|
72668
72716
|
function mtoc2_clog10(z) {
|
|
72717
|
+
if (nanOnRealAxis(z)) return { re: z.re, im: 0 };
|
|
72669
72718
|
const l = clog2(z);
|
|
72670
72719
|
const lg10 = Math.log(10);
|
|
72671
72720
|
return { re: l.re / lg10, im: l.im / lg10 };
|
|
@@ -75076,7 +75125,7 @@ var JS_SNIPPETS = {
|
|
|
75076
75125
|
"warning_fmt.js": '// JS sibling of `warning_fmt.h`. Format the message and write\n// `Warning: <msg>\\n` via `$write`. Matches numbl\n// (`runtime/specialBuiltins.ts:190` \u2014 `rt.output("Warning: " + msg + "\\n")`).\n\n\nfunction mtoc2_warning_fmt(fmt, ...args) {\n $write("Warning: " + mtoc2_sprintf_format(fmt, args) + "\\n");\n}\n\nfunction mtoc2_warning_fmt_id(_id, fmt, ...args) {\n // The id is informational \u2014 numbl threads it to lastwarn state,\n // which mtoc2 doesn\'t expose. Drop it here so the visible output\n // matches the bare form.\n $write("Warning: " + mtoc2_sprintf_format(fmt, args) + "\\n");\n}\n',
|
|
75077
75126
|
"plot_dispatch.js": '// JS sibling of `plot_dispatch.h`. Two delivery modes:\n//\n// 1. Host hook mode (numbl JIT bridge): when the host binds a\n// `$plotDispatch(name, args)` callback into the emit\'s `$h`\n// helpers, the helper forwards the raw mtoc2-shaped args to the\n// host. The host is expected to translate the args into its own\n// runtime value shape and route them into its plot pipeline\n// (numbl: `dispatchPlotBuiltin` into `rt.plotInstructions`).\n//\n// Wire-format encoding is skipped entirely in this mode \u2014 the\n// host operates in-process and consumes the same value graph the\n// emitted code already holds.\n//\n// 2. Wire-format mode (standalone AOT / viewer tee): when no host\n// hook is present, fall back to the RS-prefixed JSON record on\n// stdout that the launcher splits and forwards. Same shape the C\n// sibling emits, so a single viewer parses both backends.\n//\n// Wire shape per call (mode 2):\n// \\x1e mtoc2:plot \\t {"call":"<name>","args":[<arg>, ...]} \\n\n//\n// Arg encoding (per source-level arg):\n// number \u2192 bare numeric (non-finite \u2192 null, matching the C side\n// and JSON.stringify\'s natural behavior)\n// string \u2192 {"kind":"text","data":"<text>"}\n// char \u2192 same as string (MATLAB plot semantics don\'t distinguish)\n// tensor \u2192 {"kind":"tensor","dims":[\u2026],"data":[\u2026]} (column-major\n// flatten; non-finite as null)\n\nfunction isTensor(v) {\n return typeof v === "object" && v !== null && v.mtoc2Tag === "tensor";\n}\nfunction isChar(v) {\n return typeof v === "object" && v !== null && v.mtoc2Tag === "char";\n}\n\nfunction encodeArg(v) {\n if (typeof v === "number") {\n return Number.isFinite(v) ? v : null;\n }\n if (typeof v === "boolean") return v ? 1 : 0;\n if (typeof v === "string") return { kind: "text", data: v };\n if (isChar(v)) return { kind: "text", data: v.value };\n if (isTensor(v)) {\n const data = new Array(v.data.length);\n for (let i = 0; i < v.data.length; i++) {\n const x = v.data[i];\n data[i] = Number.isFinite(x) ? x : null;\n }\n return { kind: "tensor", dims: v.shape.slice(), data };\n }\n // Unknown shape \u2014 pass through. Type-level rejection on the\n // lowering side keeps complex/struct/class/handle out of here.\n return String(v);\n}\n\nfunction mtoc2_plot_dispatch(name, ...args) {\n if (typeof globalThis.$plotDispatch === "function") {\n globalThis.$plotDispatch(name, args);\n return;\n }\n const record = { call: name, args: args.map(encodeArg) };\n $write("\\x1emtoc2:plot\\t" + JSON.stringify(record) + "\\n");\n}\n',
|
|
75078
75127
|
"cdiv.js": "// JS sibling of `cdiv.h`. Scalar complex division matching numbl's\n// signed-Inf-on-zero-divisor behavior via Smith's algorithm. Mirrors\n// the C side so cross-runner output stays byte-for-byte.\n\nfunction mtoc2_cdiv(a, b) {\n const ar = a.re,\n ai = a.im;\n const br = b.re,\n bi = b.im;\n // Zero divisor: match the interpreter (helpers/arithmetic.ts) \u2014 0/0 is\n // NaN, a nonzero numerator yields a signed Inf per component. Smith's\n // algorithm below would otherwise produce NaN+NaNi here.\n if (br === 0 && bi === 0) {\n if (ar === 0 && ai === 0) return { re: NaN, im: 0 };\n const signedInf = x => (x > 0 ? Infinity : x < 0 ? -Infinity : 0);\n return { re: signedInf(ar), im: signedInf(ai) };\n }\n if (Math.abs(br) >= Math.abs(bi)) {\n const r = bi / br;\n const den = br + r * bi;\n return { re: (ar + ai * r) / den, im: (ai - ar * r) / den };\n }\n const r = br / bi;\n const den = bi + r * br;\n return { re: (ar * r + ai) / den, im: (ai * r - ar) / den };\n}\n",
|
|
75079
|
-
"cscalar.js": "// JS sibling of `cscalar.h`. JS has no native complex type, so each\n// helper takes and returns `{re, im}` objects. Mirrors the C\n// signatures so emitJs renders calls structurally identical to the\n// C path (just with JS objects instead of `double _Complex`).\n//\n// Division (`mtoc2_cdiv`) lives in `cdiv.js` because it needs\n// Smith's algorithm with signed-zero detection to match numbl\n// byte-for-byte.\n\nfunction mtoc2_cmake(re, im) {\n return { re, im };\n}\nfunction mtoc2_creal(z) {\n return z.re;\n}\nfunction mtoc2_cimag(z) {\n return z.im;\n}\nfunction mtoc2_cadd(a, b) {\n return { re: a.re + b.re, im: a.im + b.im };\n}\nfunction mtoc2_csub(a, b) {\n return { re: a.re - b.re, im: a.im - b.im };\n}\nfunction mtoc2_cmul(a, b) {\n return { re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re };\n}\nfunction mtoc2_cneg(z) {\n return { re: -z.re, im: -z.im };\n}\nfunction mtoc2_cconj(z) {\n return { re: z.re, im: -z.im };\n}\nfunction mtoc2_cabs(z) {\n return Math.hypot(z.re, z.im);\n}\nfunction mtoc2_cangle(z) {\n return Math.atan2(z.im, z.re);\n}\nfunction mtoc2_cnonzero(z) {\n return z.re !== 0 || z.im !== 0;\n}\nfunction mtoc2_ceq(a, b) {\n return a.re === b.re && a.im === b.im;\n}\nfunction mtoc2_cne(a, b) {\n return a.re !== b.re || a.im !== b.im;\n}\n\n// `cpow(a, b) = exp(b * log(a))`. Matches numbl / C99 `cpow` for\n// finite inputs; special zero / pole cases follow the same path\n// the C side picks (exp(NaN) = NaN, etc.).\nfunction clog(z) {\n // Normalize a -0 real part to +0 so log(-0) (a real -0) gives\n // -Inf + 0i, not -Inf + \u03C0i \u2014 MATLAB / C99 treat -0 as 0, not as an\n // approach-from-below negative. atan2's -0 second arg only changes the\n // result when the first arg is also 0, so this affects nothing else.\n const reAngle = z.re === 0 ? 0 : z.re;\n return {\n re: Math.log(Math.hypot(z.re, z.im)),\n im: Math.atan2(z.im, reAngle),\n };\n}\nfunction cexp(z) {\n const m = Math.exp(z.re);\n // Real-valued input (im === 0): the imaginary lane is m*sin(0) = 0, but\n // when m overflows to Inf that becomes Inf*0 = NaN. Return a clean 0\n // (matches the interpreter's real exp and C99 cexp).\n if (z.im === 0) return { re: m, im: 0 };\n return { re: m * Math.cos(z.im), im: m * Math.sin(z.im) };\n}\nfunction mtoc2_cpow(a, b) {\n // Zero base: match the interpreter's complexPow (helpers/arithmetic.ts)\n // \u2014 0^0 = 1, a positive real part of the exponent gives 0 (regardless\n // of the imaginary part), and anything else gives Inf+0i. The old\n // guard required b.im===0 for the zero case and otherwise returned\n // NaN+NaNi.\n if (a.re === 0 && a.im === 0) {\n if (b.re === 0 && b.im === 0) return { re: 1, im: 0 };\n if (b.re > 0) return { re: 0, im: 0 };\n return { re: Infinity, im: 0 };\n }\n return cexp(mtoc2_cmul(b, clog(a)));\n}\n\n// Unary math.\nfunction mtoc2_csqrt(z) {\n // Pure-real input: compute directly. Smith's formula below suffers\n // catastrophic cancellation/overflow when z.im === 0 \u2014 e.g.\n // sqrt(-Inf) \u2192 sqrt((Inf + -Inf)/2) = sqrt(NaN) = NaN, and\n // sqrt(-1e308) \u2192 sqrt((1e308 - -1e308)/2) overflows to Inf instead of\n // 1e154. Mirror the interpreter's complexSqrt (math.ts) and C's libm\n // csqrt, both of which special-case the real axis.\n if (z.im === 0) {\n if (z.re >= 0) return { re: Math.sqrt(z.re), im: 0 };\n return { re: 0, im: Math.sqrt(-z.re) };\n }\n // Smith's stable formula for hypot * sign.\n const r = Math.hypot(z.re, z.im);\n if (r === 0) return { re: 0, im: 0 };\n const re = Math.sqrt((r + z.re) / 2);\n const im = (z.im >= 0 ? 1 : -1) * Math.sqrt((r - z.re) / 2);\n return { re, im };\n}\nfunction mtoc2_cexp(z) {\n return cexp(z);\n}\nfunction mtoc2_clog(z) {\n return clog(z);\n}\nfunction mtoc2_clog2(z) {\n const l = clog(z);\n const lg2 = Math.log(2);\n return { re: l.re / lg2, im: l.im / lg2 };\n}\nfunction mtoc2_clog10(z) {\n const l = clog(z);\n const lg10 = Math.log(10);\n return { re: l.re / lg10, im: l.im / lg10 };\n}\nfunction mtoc2_csin(z) {\n // Guard 0*Inf=NaN on the imaginary axis: for a pure-imaginary input\n // (sin(z.re)===0), the real lane is 0 and the imaginary lane is\n // \xB1sinh(im); cosh/sinh overflow to Inf for large |im|, and 0*Inf would\n // poison the result. C99 csin special-cases this \u2014 match it.\n const sr = Math.sin(z.re);\n const cr = Math.cos(z.re);\n return {\n re: sr === 0 ? 0 : sr * Math.cosh(z.im),\n im: cr === 0 ? 0 : cr * Math.sinh(z.im),\n };\n}\nfunction mtoc2_ccos(z) {\n const cr = Math.cos(z.re);\n const sr = Math.sin(z.re);\n return {\n re: cr === 0 ? 0 : cr * Math.cosh(z.im),\n im: sr === 0 ? 0 : -sr * Math.sinh(z.im),\n };\n}\nfunction mtoc2_ctan(z) {\n // Double-angle form, matching the interpreter (interpreter/builtins/\n // math.ts) and C99 ctan. Computing sin(z)/cos(z) with a |cos|^2\n // denominator loses the (tiny but nonzero) real part and overflows\n // for large imaginary parts \u2014 e.g. tan(1+200i) collapsed to 1i.\n const denom = Math.cos(2 * z.re) + Math.cosh(2 * z.im);\n return { re: Math.sin(2 * z.re) / denom, im: Math.sinh(2 * z.im) / denom };\n}\nfunction mtoc2_catan(z) {\n // Real-valued input (im === 0): atan is a clean real function. The\n // complex formula leaves a spurious \xB11e-17i residue (and NaN+NaNi once\n // 1\xB1iz overflows, e.g. atan(1e300)). Take the real path.\n if (z.im === 0) return { re: Math.atan(z.re), im: 0 };\n // atan(z) = (i/2)\xB7log((1 \u2212 iz)/(1 + iz)) \u2014 MATLAB's branch (atan(2i) =\n // \u22121.5708 + 0.5493i). The C runtime overrides its libc catan (which\n // uses the opposite +\u03C0/2 Annex-G branch) to match this.\n const iz = { re: -z.im, im: z.re }; // i*z\n const num = { re: 1 - iz.re, im: -iz.im }; // 1 - iz\n const denom = { re: 1 + iz.re, im: iz.im }; // 1 + iz\n const dd = denom.re * denom.re + denom.im * denom.im;\n const q = {\n re: (num.re * denom.re + num.im * denom.im) / dd,\n im: (num.im * denom.re - num.re * denom.im) / dd,\n };\n const l = clog(q);\n return { re: -l.im / 2, im: l.re / 2 };\n}\n// Hyperbolic sinh/cosh/tanh \u2014 byte-for-byte with the interpreter's\n// complex formulas (interpreter/builtins/math.ts). Real-valued input\n// (im === 0) falls out cleanly: cos(0)=1, sin(0)=0, so the imaginary\n// lane is exactly 0.\nfunction mtoc2_csinh(z) {\n return {\n re: Math.sinh(z.re) * Math.cos(z.im),\n im: Math.cosh(z.re) * Math.sin(z.im),\n };\n}\nfunction mtoc2_ccosh(z) {\n return {\n re: Math.cosh(z.re) * Math.cos(z.im),\n im: Math.sinh(z.re) * Math.sin(z.im),\n };\n}\nfunction mtoc2_ctanh(z) {\n const denom = Math.cosh(2 * z.re) + Math.cos(2 * z.im);\n return { re: Math.sinh(2 * z.re) / denom, im: Math.sin(2 * z.im) / denom };\n}\nfunction mtoc2_cfloor(z) {\n return { re: Math.floor(z.re), im: Math.floor(z.im) };\n}\nfunction mtoc2_cceil(z) {\n return { re: Math.ceil(z.re), im: Math.ceil(z.im) };\n}\nfunction mtoc2_cround(z) {\n // MATLAB rounds each component half-away-from-zero.\n const half = x => Math.sign(x) * Math.round(Math.abs(x));\n return { re: half(z.re), im: half(z.im) };\n}\nfunction mtoc2_cfix(z) {\n return { re: Math.trunc(z.re), im: Math.trunc(z.im) };\n}\nfunction mtoc2_csign(z) {\n if (z.re === 0 && z.im === 0) return { re: 0, im: 0 };\n const m = Math.hypot(z.re, z.im);\n return { re: z.re / m, im: z.im / m };\n}\n",
|
|
75128
|
+
"cscalar.js": "// JS sibling of `cscalar.h`. JS has no native complex type, so each\n// helper takes and returns `{re, im}` objects. Mirrors the C\n// signatures so emitJs renders calls structurally identical to the\n// C path (just with JS objects instead of `double _Complex`).\n//\n// Division (`mtoc2_cdiv`) lives in `cdiv.js` because it needs\n// Smith's algorithm with signed-zero detection to match numbl\n// byte-for-byte.\n\nfunction mtoc2_cmake(re, im) {\n return { re, im };\n}\nfunction mtoc2_creal(z) {\n return z.re;\n}\nfunction mtoc2_cimag(z) {\n return z.im;\n}\nfunction mtoc2_cadd(a, b) {\n return { re: a.re + b.re, im: a.im + b.im };\n}\nfunction mtoc2_csub(a, b) {\n return { re: a.re - b.re, im: a.im - b.im };\n}\nfunction mtoc2_cmul(a, b) {\n return { re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re };\n}\nfunction mtoc2_cneg(z) {\n return { re: -z.re, im: -z.im };\n}\nfunction mtoc2_cconj(z) {\n return { re: z.re, im: -z.im };\n}\nfunction mtoc2_cabs(z) {\n return Math.hypot(z.re, z.im);\n}\nfunction mtoc2_cangle(z) {\n return Math.atan2(z.im, z.re);\n}\nfunction mtoc2_cnonzero(z) {\n return z.re !== 0 || z.im !== 0;\n}\nfunction mtoc2_ceq(a, b) {\n return a.re === b.re && a.im === b.im;\n}\nfunction mtoc2_cne(a, b) {\n return a.re !== b.re || a.im !== b.im;\n}\n\n// `cpow(a, b) = exp(b * log(a))`. Matches numbl / C99 `cpow` for\n// finite inputs; special zero / pole cases follow the same path\n// the C side picks (exp(NaN) = NaN, etc.).\nfunction clog(z) {\n // Normalize a -0 real part to +0 so log(-0) (a real -0) gives\n // -Inf + 0i, not -Inf + \u03C0i \u2014 MATLAB / C99 treat -0 as 0, not as an\n // approach-from-below negative. atan2's -0 second arg only changes the\n // result when the first arg is also 0, so this affects nothing else.\n const reAngle = z.re === 0 ? 0 : z.re;\n return {\n re: Math.log(Math.hypot(z.re, z.im)),\n im: Math.atan2(z.im, reAngle),\n };\n}\nfunction cexp(z) {\n const m = Math.exp(z.re);\n // Real-valued input (im === 0): the imaginary lane is m*sin(0) = 0, but\n // when m overflows to Inf that becomes Inf*0 = NaN. Return a clean 0\n // (matches the interpreter's real exp and C99 cexp).\n if (z.im === 0) return { re: m, im: 0 };\n return { re: m * Math.cos(z.im), im: m * Math.sin(z.im) };\n}\nfunction mtoc2_cpow(a, b) {\n // Zero base: match the interpreter's complexPow (helpers/arithmetic.ts)\n // \u2014 0^0 = 1, a positive real part of the exponent gives 0 (regardless\n // of the imaginary part), and anything else gives Inf+0i. The old\n // guard required b.im===0 for the zero case and otherwise returned\n // NaN+NaNi.\n if (a.re === 0 && a.im === 0) {\n if (b.re === 0 && b.im === 0) return { re: 1, im: 0 };\n if (b.re > 0) return { re: 0, im: 0 };\n return { re: Infinity, im: 0 };\n }\n return cexp(mtoc2_cmul(b, clog(a)));\n}\n\n/** True when `z` is a NaN sitting on the real axis.\n *\n * These helpers are reached both from genuinely complex arithmetic and from\n * the *lift* the JIT performs on a real argument whose sign it cannot prove\n * (`sqrt(x)`, `log(x)`). MATLAB's sqrt(NaN) and log(NaN) are a real NaN, so a\n * lifted NaN must come back as NaN + 0i and not, say, 0 + NaN i: the lift is\n * a compilation detail and must not change what `real`, `isreal`,\n * `jsonencode` or a printed value say. */\nfunction nanOnRealAxis(z) {\n return z.im === 0 && Number.isNaN(z.re);\n}\n\n// Unary math.\nfunction mtoc2_csqrt(z) {\n // Pure-real input: compute directly. Smith's formula below suffers\n // catastrophic cancellation/overflow when z.im === 0 \u2014 e.g.\n // sqrt(-Inf) \u2192 sqrt((Inf + -Inf)/2) = sqrt(NaN) = NaN, and\n // sqrt(-1e308) \u2192 sqrt((1e308 - -1e308)/2) overflows to Inf instead of\n // 1e154. Mirror the interpreter's complexSqrt (math.ts) and C's libm\n // csqrt, both of which special-case the real axis.\n if (z.im === 0) {\n if (Number.isNaN(z.re)) return { re: z.re, im: 0 };\n if (z.re >= 0) return { re: Math.sqrt(z.re), im: 0 };\n return { re: 0, im: Math.sqrt(-z.re) };\n }\n // Smith's stable formula for hypot * sign.\n const r = Math.hypot(z.re, z.im);\n if (r === 0) return { re: 0, im: 0 };\n const re = Math.sqrt((r + z.re) / 2);\n const im = (z.im >= 0 ? 1 : -1) * Math.sqrt((r - z.re) / 2);\n return { re, im };\n}\nfunction mtoc2_cexp(z) {\n return cexp(z);\n}\nfunction mtoc2_clog(z) {\n if (nanOnRealAxis(z)) return { re: z.re, im: 0 };\n return clog(z);\n}\nfunction mtoc2_clog2(z) {\n if (nanOnRealAxis(z)) return { re: z.re, im: 0 };\n const l = clog(z);\n const lg2 = Math.log(2);\n return { re: l.re / lg2, im: l.im / lg2 };\n}\nfunction mtoc2_clog10(z) {\n if (nanOnRealAxis(z)) return { re: z.re, im: 0 };\n const l = clog(z);\n const lg10 = Math.log(10);\n return { re: l.re / lg10, im: l.im / lg10 };\n}\nfunction mtoc2_csin(z) {\n // Guard 0*Inf=NaN on the imaginary axis: for a pure-imaginary input\n // (sin(z.re)===0), the real lane is 0 and the imaginary lane is\n // \xB1sinh(im); cosh/sinh overflow to Inf for large |im|, and 0*Inf would\n // poison the result. C99 csin special-cases this \u2014 match it.\n const sr = Math.sin(z.re);\n const cr = Math.cos(z.re);\n return {\n re: sr === 0 ? 0 : sr * Math.cosh(z.im),\n im: cr === 0 ? 0 : cr * Math.sinh(z.im),\n };\n}\nfunction mtoc2_ccos(z) {\n const cr = Math.cos(z.re);\n const sr = Math.sin(z.re);\n return {\n re: cr === 0 ? 0 : cr * Math.cosh(z.im),\n im: sr === 0 ? 0 : -sr * Math.sinh(z.im),\n };\n}\nfunction mtoc2_ctan(z) {\n // Double-angle form, matching the interpreter (interpreter/builtins/\n // math.ts) and C99 ctan. Computing sin(z)/cos(z) with a |cos|^2\n // denominator loses the (tiny but nonzero) real part and overflows\n // for large imaginary parts \u2014 e.g. tan(1+200i) collapsed to 1i.\n const denom = Math.cos(2 * z.re) + Math.cosh(2 * z.im);\n return { re: Math.sin(2 * z.re) / denom, im: Math.sinh(2 * z.im) / denom };\n}\nfunction mtoc2_catan(z) {\n // Real-valued input (im === 0): atan is a clean real function. The\n // complex formula leaves a spurious \xB11e-17i residue (and NaN+NaNi once\n // 1\xB1iz overflows, e.g. atan(1e300)). Take the real path.\n if (z.im === 0) return { re: Math.atan(z.re), im: 0 };\n // atan(z) = (i/2)\xB7log((1 \u2212 iz)/(1 + iz)) \u2014 MATLAB's branch (atan(2i) =\n // \u22121.5708 + 0.5493i). The C runtime overrides its libc catan (which\n // uses the opposite +\u03C0/2 Annex-G branch) to match this.\n const iz = { re: -z.im, im: z.re }; // i*z\n const num = { re: 1 - iz.re, im: -iz.im }; // 1 - iz\n const denom = { re: 1 + iz.re, im: iz.im }; // 1 + iz\n const dd = denom.re * denom.re + denom.im * denom.im;\n const q = {\n re: (num.re * denom.re + num.im * denom.im) / dd,\n im: (num.im * denom.re - num.re * denom.im) / dd,\n };\n const l = clog(q);\n return { re: -l.im / 2, im: l.re / 2 };\n}\n// Hyperbolic sinh/cosh/tanh \u2014 byte-for-byte with the interpreter's\n// complex formulas (interpreter/builtins/math.ts). Real-valued input\n// (im === 0) falls out cleanly: cos(0)=1, sin(0)=0, so the imaginary\n// lane is exactly 0.\nfunction mtoc2_csinh(z) {\n return {\n re: Math.sinh(z.re) * Math.cos(z.im),\n im: Math.cosh(z.re) * Math.sin(z.im),\n };\n}\nfunction mtoc2_ccosh(z) {\n return {\n re: Math.cosh(z.re) * Math.cos(z.im),\n im: Math.sinh(z.re) * Math.sin(z.im),\n };\n}\nfunction mtoc2_ctanh(z) {\n const denom = Math.cosh(2 * z.re) + Math.cos(2 * z.im);\n return { re: Math.sinh(2 * z.re) / denom, im: Math.sin(2 * z.im) / denom };\n}\nfunction mtoc2_cfloor(z) {\n return { re: Math.floor(z.re), im: Math.floor(z.im) };\n}\nfunction mtoc2_cceil(z) {\n return { re: Math.ceil(z.re), im: Math.ceil(z.im) };\n}\nfunction mtoc2_cround(z) {\n // MATLAB rounds each component half-away-from-zero.\n const half = x => Math.sign(x) * Math.round(Math.abs(x));\n return { re: half(z.re), im: half(z.im) };\n}\nfunction mtoc2_cfix(z) {\n return { re: Math.trunc(z.re), im: Math.trunc(z.im) };\n}\nfunction mtoc2_csign(z) {\n if (z.re === 0 && z.im === 0) return { re: 0, im: 0 };\n const m = Math.hypot(z.re, z.im);\n return { re: z.re / m, im: z.im / m };\n}\n",
|
|
75080
75129
|
"deep_clone.js": '/* Deep-clone an owned-typed runtime value so a subsequent member /\n * indexed write through one alias doesn\'t leak to others. Mirrors\n * the c-aot per-typedef `_copy` protocol: every Var-RHS Assign /\n * MemberLoad-RHS Assign / function-call arg of an owned type is\n * wrapped through this helper at emit time, so JS-side aliasing\n * stops at the assignment boundary.\n *\n * Recognises the runtime shapes the rest of mtoc2 emits:\n * - RuntimeTensor (`{mtoc2Tag:"tensor", shape, data, imag?, isLogical?}`)\n * - RuntimeChar (`{mtoc2Tag:"char", value}`)\n * - struct / class / handle / handle-capture objects \u2014 plain JS\n * objects, optionally with a non-enumerable `mtoc2Class` tag\n * (class instances) that must survive the clone for method\n * dispatch\n *\n * Primitives (numbers, booleans, strings, null/undefined) flow\n * through unchanged \u2014 they\'re already value-typed in JS.\n */\n\nfunction mtoc2_deep_clone(v) {\n if (v === null || typeof v !== "object") return v;\n if (v.mtoc2Tag === "tensor") {\n const out = {\n mtoc2Tag: "tensor",\n shape: v.shape.slice(),\n data: v.data.slice(),\n };\n if (v.imag !== undefined) out.imag = v.imag.slice();\n if (v.isLogical) out.isLogical = true;\n return out;\n }\n if (v.mtoc2Tag === "char") {\n return { mtoc2Tag: "char", value: v.value };\n }\n if (v.mtoc2Tag === "cell") {\n return {\n mtoc2Tag: "cell",\n shape: v.shape.slice(),\n data: v.data.map(mtoc2_deep_clone),\n };\n }\n if (v.mtoc2Handle === true) {\n // Function handle \u2014 captures are deep-cloned so a later write\n // through one alias doesn\'t bleed into another handle\'s frozen\n // capture snapshot.\n if (v.kind === "named") {\n return { mtoc2Handle: true, kind: "named", name: v.name };\n }\n const captures = {};\n for (const k of Object.keys(v.captures)) {\n captures[k] = mtoc2_deep_clone(v.captures[k]);\n }\n return {\n mtoc2Handle: true,\n kind: "anon",\n params: v.params,\n body: v.body,\n captures,\n };\n }\n // Plain struct / class instance: walk enumerable fields, then\n // restore the non-enumerable `mtoc2Class` tag if it was present.\n const out = {};\n for (const k of Object.keys(v)) {\n out[k] = mtoc2_deep_clone(v[k]);\n }\n const tag = Object.getOwnPropertyDescriptor(v, "mtoc2Class");\n if (tag !== undefined) {\n Object.defineProperty(out, "mtoc2Class", {\n value: tag.value,\n enumerable: false,\n writable: false,\n });\n }\n return out;\n}\n',
|
|
75081
75130
|
"isequal.js": "// JS sibling of `isequal.h`. Same real-numeric equality semantics for\n// the js-aot backend. Tensors carry `shape` / `data` (the JS analogue\n// of the C struct's `dims` / `real`).\n\nfunction mtoc2_isequal_tt(a, b) {\n if (a.shape.length !== b.shape.length) return 0;\n let n = 1;\n for (let i = 0; i < a.shape.length; i++) {\n if (a.shape[i] !== b.shape[i]) return 0;\n n *= a.shape[i];\n }\n for (let i = 0; i < n; i++) {\n if (a.data[i] !== b.data[i]) return 0;\n }\n return 1;\n}\n\nfunction mtoc2_isequal_st(s, t) {\n let n = 1;\n for (let i = 0; i < t.shape.length; i++) n *= t.shape[i];\n if (n !== 1) return 0;\n return t.data[0] === s ? 1 : 0;\n}\n",
|
|
75082
75131
|
"tictoc.js": '// JS sibling of `tictoc.h`. Same "one global timer" semantics. Uses\n// `performance.now()` to match numbl\'s wall-clock source; the output\n// format string mirrors numbl\'s `"Elapsed time is %.6f seconds.\\n"`\n// so the cross-runner\'s masking regex still treats both runtimes as\n// equivalent.\n//\n// Module-scope state for the shared `tic` slot. The codegen path\n// inlines this declaration alongside the helpers; the interpreter\n// imports them as ES exports and the same module-scope `let` is the\n// canonical slot.\n\nlet mtoc2_tic_seconds = 0;\n\nfunction mtoc2_now_seconds() {\n // `performance.now()` returns milliseconds since some monotonic\n // epoch; divide for seconds. (Node and browsers both supply it on\n // `globalThis.performance` as of recent versions.)\n return performance.now() / 1000;\n}\n\nfunction mtoc2_tic() {\n mtoc2_tic_seconds = mtoc2_now_seconds();\n return mtoc2_tic_seconds;\n}\n\nfunction mtoc2_toc() {\n return mtoc2_now_seconds() - mtoc2_tic_seconds;\n}\n\nfunction mtoc2_toc_handle(start_seconds) {\n return mtoc2_now_seconds() - start_seconds;\n}\n\nfunction mtoc2_toc_print() {\n const elapsed = mtoc2_toc();\n $write(`Elapsed time is ${elapsed.toFixed(6)} seconds.\\n`);\n}\n\nfunction mtoc2_toc_handle_print(start_seconds) {\n const elapsed = mtoc2_now_seconds() - start_seconds;\n $write(`Elapsed time is ${elapsed.toFixed(6)} seconds.\\n`);\n}\n',
|
|
@@ -87925,6 +87974,24 @@ function lowerMultiAssign(s) {
|
|
|
87925
87974
|
);
|
|
87926
87975
|
}
|
|
87927
87976
|
}
|
|
87977
|
+
if (target?.kind === "mtoc2UserFunction") {
|
|
87978
|
+
const userBuiltin = this.workspace.getUserBuiltin(target.name);
|
|
87979
|
+
if (!userBuiltin) {
|
|
87980
|
+
throw new UnsupportedConstruct(
|
|
87981
|
+
`internal: workspace resolved '${callName}' to a .mtoc2.js function but no Builtin is loaded`,
|
|
87982
|
+
s.span
|
|
87983
|
+
);
|
|
87984
|
+
}
|
|
87985
|
+
return buildBuiltinMultiAssign.call(
|
|
87986
|
+
this,
|
|
87987
|
+
callName,
|
|
87988
|
+
userBuiltin,
|
|
87989
|
+
anfArgs,
|
|
87990
|
+
argTypes,
|
|
87991
|
+
argHoists,
|
|
87992
|
+
s
|
|
87993
|
+
);
|
|
87994
|
+
}
|
|
87928
87995
|
if (target?.kind !== "userFunction") {
|
|
87929
87996
|
throw new UnsupportedConstruct(
|
|
87930
87997
|
`multi-assign of '${callName}': only user-defined functions can appear on the right of '[...] = ...' (or as a bare multi-output statement)`,
|
|
@@ -88460,23 +88527,6 @@ function lowerIndexSliceStore(lvalue, exprAst, span) {
|
|
|
88460
88527
|
}
|
|
88461
88528
|
}
|
|
88462
88529
|
}
|
|
88463
|
-
if (!isSingleSlot && r.baseTy.kind === "Numeric") {
|
|
88464
|
-
for (let i = 0; i < slots.length; i++) {
|
|
88465
|
-
const slot = slots[i];
|
|
88466
|
-
if (slot.kind !== "Scalar") continue;
|
|
88467
|
-
const dim = r.baseTy.dims[i];
|
|
88468
|
-
const t = slot.expr.ty;
|
|
88469
|
-
const dimKnown = dim !== void 0 && dim.kind === "exact";
|
|
88470
|
-
const valKnown = t.kind === "Numeric" && typeof t.exact === "number";
|
|
88471
|
-
const provablyInBounds = dimKnown && valKnown && Math.round(t.exact) >= 1 && Math.round(t.exact) <= dim.value;
|
|
88472
|
-
if (!provablyInBounds) {
|
|
88473
|
-
throw new UnsupportedConstruct(
|
|
88474
|
-
`indexed write to '${displayName}' at axis ${i + 1}: cannot statically prove the index stays within the tensor's bounds; would auto-grow / OOB in MATLAB, but auto-grow is not modeled in the JIT`,
|
|
88475
|
-
slot.span
|
|
88476
|
-
);
|
|
88477
|
-
}
|
|
88478
|
-
}
|
|
88479
|
-
}
|
|
88480
88530
|
const rawRhs = this.lowerExpr(exprAst);
|
|
88481
88531
|
if (!isNumeric2(rawRhs.ty)) {
|
|
88482
88532
|
throw new TypeError2(
|
|
@@ -88792,6 +88842,8 @@ var Lowerer = class {
|
|
|
88792
88842
|
return lowerMultiAssign.call(this, s);
|
|
88793
88843
|
case "If":
|
|
88794
88844
|
return this.lowerIf(s);
|
|
88845
|
+
case "Switch":
|
|
88846
|
+
return this.lowerSwitch(s);
|
|
88795
88847
|
case "While":
|
|
88796
88848
|
return this.lowerWhile(s);
|
|
88797
88849
|
case "For":
|
|
@@ -88883,6 +88935,23 @@ var Lowerer = class {
|
|
|
88883
88935
|
return entries;
|
|
88884
88936
|
}
|
|
88885
88937
|
lowerExprStmt(s) {
|
|
88938
|
+
if (this.env.get("tic") === void 0 && !this.workspace.isClass("tic")) {
|
|
88939
|
+
const bareTic = s.expr.type === "Ident" && s.expr.name === "tic" || s.expr.type === "FuncCall" && s.expr.name === "tic" && s.expr.args.length === 0;
|
|
88940
|
+
if (bareTic) {
|
|
88941
|
+
return {
|
|
88942
|
+
kind: "ExprStmt",
|
|
88943
|
+
expr: {
|
|
88944
|
+
kind: "Call",
|
|
88945
|
+
cName: "mtoc2_tic",
|
|
88946
|
+
name: "tic",
|
|
88947
|
+
args: [],
|
|
88948
|
+
ty: VOID,
|
|
88949
|
+
span: s.expr.span
|
|
88950
|
+
},
|
|
88951
|
+
span: s.span
|
|
88952
|
+
};
|
|
88953
|
+
}
|
|
88954
|
+
}
|
|
88886
88955
|
if (this.env.get("toc") === void 0 && !this.workspace.isClass("toc")) {
|
|
88887
88956
|
if (s.expr.type === "Ident" && s.expr.name === "toc") {
|
|
88888
88957
|
return {
|
|
@@ -89489,6 +89558,125 @@ var Lowerer = class {
|
|
|
89489
89558
|
}
|
|
89490
89559
|
];
|
|
89491
89560
|
}
|
|
89561
|
+
/** MATLAB `switch` lowered to the equivalent if/elseif chain.
|
|
89562
|
+
*
|
|
89563
|
+
* The subject is evaluated once — hoisted to a temp unless it is already a
|
|
89564
|
+
* plain variable — and each case label becomes an equality test against it:
|
|
89565
|
+
* `strcmp` for text, `==` for numbers. A `case {a, b}` list is the OR of
|
|
89566
|
+
* the individual tests. Everything after that is `lowerIf`'s problem, which
|
|
89567
|
+
* means switch inherits its branch-env merging and cond folding.
|
|
89568
|
+
*
|
|
89569
|
+
* Declined (leaving the unit to the interpreter, as before this existed):
|
|
89570
|
+
* a subject that is neither a scalar number nor text, a label whose kind
|
|
89571
|
+
* does not match the subject's, and an empty `case {}`.
|
|
89572
|
+
*/
|
|
89573
|
+
lowerSwitch(s) {
|
|
89574
|
+
const span = s.span;
|
|
89575
|
+
const pre = [];
|
|
89576
|
+
let subject = s.expr;
|
|
89577
|
+
if (!(s.expr.type === "Ident" && this.env.has(s.expr.name))) {
|
|
89578
|
+
const tmp = this.freshTempName();
|
|
89579
|
+
const assign = {
|
|
89580
|
+
type: "Assign",
|
|
89581
|
+
name: tmp,
|
|
89582
|
+
expr: s.expr,
|
|
89583
|
+
suppressed: true,
|
|
89584
|
+
span
|
|
89585
|
+
};
|
|
89586
|
+
const lowered = this.lowerStmt(assign);
|
|
89587
|
+
if (lowered !== null) {
|
|
89588
|
+
if (Array.isArray(lowered)) pre.push(...lowered);
|
|
89589
|
+
else pre.push(lowered);
|
|
89590
|
+
}
|
|
89591
|
+
subject = { type: "Ident", name: tmp, span };
|
|
89592
|
+
}
|
|
89593
|
+
const subjTy = this.env.get(subject.name)?.ty;
|
|
89594
|
+
if (subjTy === void 0) {
|
|
89595
|
+
throw new UnsupportedConstruct(
|
|
89596
|
+
"switch subject is not a value the JIT can type",
|
|
89597
|
+
span
|
|
89598
|
+
);
|
|
89599
|
+
}
|
|
89600
|
+
const subjIsText = subjTy.kind === "Char" || subjTy.kind === "String";
|
|
89601
|
+
const subjIsNum = isScalarRealNumeric(subjTy) || subjTy.kind === "Numeric";
|
|
89602
|
+
if (!subjIsText && !(subjIsNum && isScalar(subjTy))) {
|
|
89603
|
+
throw new UnsupportedConstruct(
|
|
89604
|
+
`switch on a ${typeToString(subjTy)} subject is not supported (scalar numeric or text only)`,
|
|
89605
|
+
span
|
|
89606
|
+
);
|
|
89607
|
+
}
|
|
89608
|
+
const labelIsText = (label) => {
|
|
89609
|
+
if (label.type === "Char" || label.type === "String") return true;
|
|
89610
|
+
if (label.type === "Ident") {
|
|
89611
|
+
const t = this.env.get(label.name)?.ty;
|
|
89612
|
+
return t !== void 0 && (t.kind === "Char" || t.kind === "String");
|
|
89613
|
+
}
|
|
89614
|
+
return false;
|
|
89615
|
+
};
|
|
89616
|
+
const labelCond = (label) => {
|
|
89617
|
+
if (subjIsText) {
|
|
89618
|
+
if (!labelIsText(label)) {
|
|
89619
|
+
throw new UnsupportedConstruct(
|
|
89620
|
+
"switch on text with a non-text case label is not supported",
|
|
89621
|
+
label.span
|
|
89622
|
+
);
|
|
89623
|
+
}
|
|
89624
|
+
return {
|
|
89625
|
+
type: "FuncCall",
|
|
89626
|
+
name: "strcmp",
|
|
89627
|
+
args: [subject, label],
|
|
89628
|
+
span: label.span
|
|
89629
|
+
};
|
|
89630
|
+
}
|
|
89631
|
+
if (labelIsText(label)) {
|
|
89632
|
+
throw new UnsupportedConstruct(
|
|
89633
|
+
"switch on a number with a text case label is not supported",
|
|
89634
|
+
label.span
|
|
89635
|
+
);
|
|
89636
|
+
}
|
|
89637
|
+
return {
|
|
89638
|
+
type: "Binary",
|
|
89639
|
+
left: subject,
|
|
89640
|
+
op: "Equal" /* Equal */,
|
|
89641
|
+
right: label,
|
|
89642
|
+
span: label.span
|
|
89643
|
+
};
|
|
89644
|
+
};
|
|
89645
|
+
const caseCond = (value) => {
|
|
89646
|
+
if (value.type !== "Cell") return labelCond(value);
|
|
89647
|
+
const items = value.rows.flat();
|
|
89648
|
+
if (items.length === 0) {
|
|
89649
|
+
throw new UnsupportedConstruct(
|
|
89650
|
+
"switch with an empty case list is not supported",
|
|
89651
|
+
value.span
|
|
89652
|
+
);
|
|
89653
|
+
}
|
|
89654
|
+
return items.map(labelCond).reduce((acc, c) => ({
|
|
89655
|
+
type: "Binary",
|
|
89656
|
+
left: acc,
|
|
89657
|
+
op: "OrOr" /* OrOr */,
|
|
89658
|
+
right: c,
|
|
89659
|
+
span: value.span
|
|
89660
|
+
}));
|
|
89661
|
+
};
|
|
89662
|
+
if (s.cases.length === 0) {
|
|
89663
|
+
const body = s.otherwise ? this.lowerStmts(s.otherwise) : [];
|
|
89664
|
+
return [...pre, ...body];
|
|
89665
|
+
}
|
|
89666
|
+
const chain = {
|
|
89667
|
+
type: "If",
|
|
89668
|
+
cond: caseCond(s.cases[0].value),
|
|
89669
|
+
thenBody: s.cases[0].body,
|
|
89670
|
+
elseifBlocks: s.cases.slice(1).map((c) => ({
|
|
89671
|
+
cond: caseCond(c.value),
|
|
89672
|
+
body: c.body
|
|
89673
|
+
})),
|
|
89674
|
+
elseBody: s.otherwise,
|
|
89675
|
+
span
|
|
89676
|
+
};
|
|
89677
|
+
const ir = this.lowerIf(chain);
|
|
89678
|
+
return [...pre, ...Array.isArray(ir) ? ir : [ir]];
|
|
89679
|
+
}
|
|
89492
89680
|
lowerWhile(s) {
|
|
89493
89681
|
const envBefore = new Map(this.env);
|
|
89494
89682
|
stripExactFromEnv(this.env, collectAssignedNames(s.body));
|
|
@@ -92376,30 +92564,39 @@ function compileSpec(args) {
|
|
|
92376
92564
|
const { workspace, lowerer, funcDecl, argTypes, nargout } = args;
|
|
92377
92565
|
workspace.finalize();
|
|
92378
92566
|
const widenedArgTypes = argTypes.map(withoutExact);
|
|
92379
|
-
const
|
|
92380
|
-
|
|
92381
|
-
|
|
92382
|
-
|
|
92383
|
-
|
|
92384
|
-
|
|
92385
|
-
|
|
92386
|
-
|
|
92387
|
-
|
|
92388
|
-
|
|
92389
|
-
|
|
92390
|
-
|
|
92391
|
-
|
|
92392
|
-
|
|
92393
|
-
|
|
92394
|
-
|
|
92395
|
-
|
|
92396
|
-
|
|
92397
|
-
|
|
92398
|
-
|
|
92399
|
-
|
|
92400
|
-
|
|
92401
|
-
|
|
92402
|
-
|
|
92567
|
+
const before = new Set(lowerer.specializations.keys());
|
|
92568
|
+
try {
|
|
92569
|
+
const spec = specializeUserFunction.call(
|
|
92570
|
+
lowerer,
|
|
92571
|
+
funcDecl,
|
|
92572
|
+
widenedArgTypes,
|
|
92573
|
+
void 0,
|
|
92574
|
+
void 0,
|
|
92575
|
+
void 0,
|
|
92576
|
+
nargout,
|
|
92577
|
+
void 0
|
|
92578
|
+
);
|
|
92579
|
+
assertNoNonVoidBareExprStmts(spec);
|
|
92580
|
+
const prog = {
|
|
92581
|
+
topLevelStmts: [],
|
|
92582
|
+
functions: new Map(lowerer.specializations)
|
|
92583
|
+
};
|
|
92584
|
+
const { source, activatedSnippets } = emitJsProgram(prog, {
|
|
92585
|
+
workspace,
|
|
92586
|
+
exposeSpec: spec.cName
|
|
92587
|
+
});
|
|
92588
|
+
return {
|
|
92589
|
+
cName: spec.cName,
|
|
92590
|
+
source,
|
|
92591
|
+
activatedSnippets,
|
|
92592
|
+
outputTypes: spec.outputTypes
|
|
92593
|
+
};
|
|
92594
|
+
} catch (err) {
|
|
92595
|
+
for (const key of lowerer.specializations.keys()) {
|
|
92596
|
+
if (!before.has(key)) lowerer.specializations.delete(key);
|
|
92597
|
+
}
|
|
92598
|
+
throw err;
|
|
92599
|
+
}
|
|
92403
92600
|
}
|
|
92404
92601
|
function assertNoNonVoidBareExprStmts(spec) {
|
|
92405
92602
|
const walk2 = (stmts) => {
|
|
@@ -94049,7 +94246,7 @@ function emitNdScalarOffset(state, indices, baseCName, grow = false) {
|
|
|
94049
94246
|
}
|
|
94050
94247
|
return terms.join(" + ");
|
|
94051
94248
|
}
|
|
94052
|
-
function emitSliceSlotSetup(state, lines, indent, slotsTyped, baseCName, cleanups) {
|
|
94249
|
+
function emitSliceSlotSetup(state, lines, indent, slotsTyped, baseCName, cleanups, grow = false) {
|
|
94053
94250
|
const slotSrc = [];
|
|
94054
94251
|
for (let i = 0; i < slotsTyped.length; i++) {
|
|
94055
94252
|
const slot = slotsTyped[i];
|
|
@@ -94058,12 +94255,18 @@ function emitSliceSlotSetup(state, lines, indent, slotsTyped, baseCName, cleanup
|
|
|
94058
94255
|
lines.push(`${indent}long _mtoc2_n_${i} = ${baseCName}.dims[${i}];`);
|
|
94059
94256
|
slotSrc.push(kVar);
|
|
94060
94257
|
} else if (slot.kind === "Scalar") {
|
|
94061
|
-
|
|
94258
|
+
const axisFn = grow ? "mtoc2_idx_axis_grow" : "mtoc2_idx_axis";
|
|
94259
|
+
if (grow) {
|
|
94260
|
+
useRuntimeByName(state, "mtoc2_grow_bail");
|
|
94261
|
+
state.usedGrowBail = true;
|
|
94262
|
+
} else {
|
|
94263
|
+
useRuntimeByName(state, "mtoc2_oob_abort");
|
|
94264
|
+
}
|
|
94062
94265
|
const scalarStr = emitExpr2(slot.expr, state);
|
|
94063
94266
|
const loc = locStringOf(slot.span);
|
|
94064
94267
|
lines.push(`${indent}long _mtoc2_n_${i} = 1;`);
|
|
94065
94268
|
lines.push(
|
|
94066
|
-
`${indent}long _mtoc2_src_${i} =
|
|
94269
|
+
`${indent}long _mtoc2_src_${i} = ${axisFn}(&${baseCName}, ${i}, mtoc2_to_idx(${scalarStr}), ${loc});`
|
|
94067
94270
|
);
|
|
94068
94271
|
slotSrc.push(`_mtoc2_src_${i}`);
|
|
94069
94272
|
} else if (slot.kind === "IndexVec") {
|
|
@@ -94453,7 +94656,10 @@ function emitIndexSliceStore(s, indent, state) {
|
|
|
94453
94656
|
lines,
|
|
94454
94657
|
`${indent} `,
|
|
94455
94658
|
s.index,
|
|
94456
|
-
baseCName
|
|
94659
|
+
baseCName,
|
|
94660
|
+
void 0,
|
|
94661
|
+
/*grow=*/
|
|
94662
|
+
true
|
|
94457
94663
|
);
|
|
94458
94664
|
const totalParts = [];
|
|
94459
94665
|
for (let i = 0; i < ndim; i++) totalParts.push(`_mtoc2_n_${i}`);
|
|
@@ -95513,40 +95719,48 @@ function compileSpecC(args) {
|
|
|
95513
95719
|
const { workspace, lowerer, funcDecl, argTypes, nargout } = args;
|
|
95514
95720
|
workspace.finalize();
|
|
95515
95721
|
const widenedArgTypes = argTypes.map(withoutExact);
|
|
95516
|
-
const
|
|
95517
|
-
|
|
95518
|
-
|
|
95519
|
-
|
|
95520
|
-
|
|
95521
|
-
|
|
95522
|
-
|
|
95523
|
-
|
|
95524
|
-
|
|
95525
|
-
|
|
95526
|
-
|
|
95527
|
-
|
|
95528
|
-
|
|
95529
|
-
|
|
95530
|
-
|
|
95531
|
-
|
|
95532
|
-
|
|
95533
|
-
|
|
95534
|
-
|
|
95535
|
-
|
|
95722
|
+
const before = new Set(lowerer.specializations.keys());
|
|
95723
|
+
try {
|
|
95724
|
+
const spec = specializeUserFunction.call(
|
|
95725
|
+
lowerer,
|
|
95726
|
+
funcDecl,
|
|
95727
|
+
widenedArgTypes,
|
|
95728
|
+
void 0,
|
|
95729
|
+
void 0,
|
|
95730
|
+
void 0,
|
|
95731
|
+
nargout,
|
|
95732
|
+
void 0
|
|
95733
|
+
);
|
|
95734
|
+
assertNoNonVoidBareExprStmts2(spec);
|
|
95735
|
+
const prog = {
|
|
95736
|
+
topLevelStmts: [],
|
|
95737
|
+
functions: new Map(lowerer.specializations)
|
|
95738
|
+
};
|
|
95739
|
+
const checkEntryOutputs = args.entrySynthetic !== true;
|
|
95740
|
+
assertDefiniteAssignment(spec, { checkOutputs: checkEntryOutputs });
|
|
95741
|
+
for (const fn of prog.functions.values()) {
|
|
95742
|
+
if (fn.cName === spec.cName) continue;
|
|
95743
|
+
assertDefiniteAssignment(fn, { checkOutputs: true });
|
|
95744
|
+
}
|
|
95745
|
+
const source = emitProgram(prog, {
|
|
95746
|
+
workspace,
|
|
95747
|
+
exposeSpec: spec.cName
|
|
95748
|
+
});
|
|
95749
|
+
return {
|
|
95750
|
+
cName: spec.cName,
|
|
95751
|
+
source,
|
|
95752
|
+
// The C emit doesn't currently expose its activated-snippet set
|
|
95753
|
+
// through emitProgram's return; the .so build doesn't need it.
|
|
95754
|
+
// Diagnostic field kept for symmetry with `compileSpec` (JS).
|
|
95755
|
+
activatedSnippets: [],
|
|
95756
|
+
signature: buildSignature(spec)
|
|
95757
|
+
};
|
|
95758
|
+
} catch (err) {
|
|
95759
|
+
for (const key of lowerer.specializations.keys()) {
|
|
95760
|
+
if (!before.has(key)) lowerer.specializations.delete(key);
|
|
95761
|
+
}
|
|
95762
|
+
throw err;
|
|
95536
95763
|
}
|
|
95537
|
-
const source = emitProgram(prog, {
|
|
95538
|
-
workspace,
|
|
95539
|
-
exposeSpec: spec.cName
|
|
95540
|
-
});
|
|
95541
|
-
return {
|
|
95542
|
-
cName: spec.cName,
|
|
95543
|
-
source,
|
|
95544
|
-
// The C emit doesn't currently expose its activated-snippet set
|
|
95545
|
-
// through emitProgram's return; the .so build doesn't need it.
|
|
95546
|
-
// Diagnostic field kept for symmetry with `compileSpec` (JS).
|
|
95547
|
-
activatedSnippets: [],
|
|
95548
|
-
signature: buildSignature(spec)
|
|
95549
|
-
};
|
|
95550
95764
|
}
|
|
95551
95765
|
function buildSignature(spec) {
|
|
95552
95766
|
const params = spec.cParams.map((cName, i) => ({
|
|
@@ -96608,7 +96822,17 @@ function jitTypeToCompilerType(jt) {
|
|
|
96608
96822
|
case "char":
|
|
96609
96823
|
return { kind: "Char" };
|
|
96610
96824
|
// Kinds the JIT path doesn't accept yet — fall through to interp.
|
|
96611
|
-
case "struct":
|
|
96825
|
+
case "struct": {
|
|
96826
|
+
if (jt.fields === void 0) return null;
|
|
96827
|
+
const names = Object.keys(jt.fields).sort();
|
|
96828
|
+
const fields = [];
|
|
96829
|
+
for (const name of names) {
|
|
96830
|
+
const fty = jitTypeToCompilerType(jt.fields[name]);
|
|
96831
|
+
if (fty === null) return null;
|
|
96832
|
+
fields.push({ name, ty: fty });
|
|
96833
|
+
}
|
|
96834
|
+
return { kind: "Struct", fields };
|
|
96835
|
+
}
|
|
96612
96836
|
case "struct_array":
|
|
96613
96837
|
case "class_instance":
|
|
96614
96838
|
case "sparse_matrix":
|
|
@@ -96654,11 +96878,20 @@ function numblToJit(v, paramType) {
|
|
|
96654
96878
|
if (isRuntimeComplexNumber(v)) {
|
|
96655
96879
|
return { re: v.re, im: v.im };
|
|
96656
96880
|
}
|
|
96881
|
+
if (isRuntimeStruct(v)) {
|
|
96882
|
+
const sty = paramType?.kind === "Struct" ? paramType : void 0;
|
|
96883
|
+
const out = {};
|
|
96884
|
+
for (const [name, fv] of v.fields) {
|
|
96885
|
+
const fty = sty?.fields.find((f) => f.name === name)?.ty;
|
|
96886
|
+
out[name] = numblToJit(fv, fty);
|
|
96887
|
+
}
|
|
96888
|
+
return out;
|
|
96889
|
+
}
|
|
96657
96890
|
throw new Error(
|
|
96658
96891
|
`numblToJit: unsupported RuntimeValue (executor should have declined)`
|
|
96659
96892
|
);
|
|
96660
96893
|
}
|
|
96661
|
-
function jitToNumbl(v) {
|
|
96894
|
+
function jitToNumbl(v, retType) {
|
|
96662
96895
|
if (typeof v === "number") return v;
|
|
96663
96896
|
if (typeof v === "boolean") return v;
|
|
96664
96897
|
if (typeof v === "string") return v;
|
|
@@ -96681,6 +96914,14 @@ function jitToNumbl(v) {
|
|
|
96681
96914
|
if (typeof tagged.re === "number" && typeof tagged.im === "number") {
|
|
96682
96915
|
return new RuntimeComplexNumber(tagged.re, tagged.im);
|
|
96683
96916
|
}
|
|
96917
|
+
if (retType?.kind === "Struct") {
|
|
96918
|
+
const fields = /* @__PURE__ */ new Map();
|
|
96919
|
+
for (const f of retType.fields) {
|
|
96920
|
+
const fv = v[f.name];
|
|
96921
|
+
if (fv !== void 0) fields.set(f.name, jitToNumbl(fv, f.ty));
|
|
96922
|
+
}
|
|
96923
|
+
return new RuntimeStruct(fields);
|
|
96924
|
+
}
|
|
96684
96925
|
throw new Error(`jitToNumbl: unrecognized return shape`);
|
|
96685
96926
|
}
|
|
96686
96927
|
function isGrowBail(e) {
|
|
@@ -96754,7 +96995,8 @@ var jitCallExecutor = {
|
|
|
96754
96995
|
name: "jit-call",
|
|
96755
96996
|
propose(lowered, ctx) {
|
|
96756
96997
|
if (lowered.kind !== "call") return null;
|
|
96757
|
-
if (ctx.interp.loopDepth > 0
|
|
96998
|
+
if (ctx.interp.loopDepth > 0 && ctx.interp.jitDeclinedLoopDepth === 0)
|
|
96999
|
+
return null;
|
|
96758
97000
|
const classification = lowered.classification;
|
|
96759
97001
|
if (ctx.interp.optimization === "2" && classification.assertsCJit) {
|
|
96760
97002
|
return null;
|
|
@@ -96786,7 +97028,7 @@ var jitCallExecutor = {
|
|
|
96786
97028
|
compile(d, ctx) {
|
|
96787
97029
|
const { workspace, lowerer } = getOrCreateSession(ctx.interp);
|
|
96788
97030
|
try {
|
|
96789
|
-
const { source, cName } = compileSpec({
|
|
97031
|
+
const { source, cName, outputTypes } = compileSpec({
|
|
96790
97032
|
workspace,
|
|
96791
97033
|
lowerer,
|
|
96792
97034
|
funcDecl: synthesizeFuncStmt(d.fn),
|
|
@@ -96802,12 +97044,13 @@ var jitCallExecutor = {
|
|
|
96802
97044
|
);
|
|
96803
97045
|
const factory = new Function(source)();
|
|
96804
97046
|
const specFn = factory(buildHostHelpers(interp.rt));
|
|
96805
|
-
return { specFn };
|
|
97047
|
+
return { specFn, outputTypes };
|
|
96806
97048
|
} catch (e) {
|
|
96807
97049
|
if (e instanceof UnsupportedConstruct || e instanceof TypeError2) {
|
|
96808
97050
|
recordJitDecline({
|
|
96809
97051
|
message: e.message,
|
|
96810
97052
|
kind: e.constructor.name,
|
|
97053
|
+
at: e.span ? { file: e.span.file, start: e.span.start } : void 0,
|
|
96811
97054
|
where: "jit-call"
|
|
96812
97055
|
});
|
|
96813
97056
|
return null;
|
|
@@ -96830,9 +97073,11 @@ var jitCallExecutor = {
|
|
|
96830
97073
|
`jit-call: expected array for nargout=${d.nargout}, got ${typeof result}`
|
|
96831
97074
|
);
|
|
96832
97075
|
}
|
|
96833
|
-
return {
|
|
97076
|
+
return {
|
|
97077
|
+
result: result.map((v, i) => jitToNumbl(v, compiled.outputTypes[i]))
|
|
97078
|
+
};
|
|
96834
97079
|
}
|
|
96835
|
-
return { result: jitToNumbl(result) };
|
|
97080
|
+
return { result: jitToNumbl(result, compiled.outputTypes[0]) };
|
|
96836
97081
|
} catch (e) {
|
|
96837
97082
|
if (isGrowBail(e)) {
|
|
96838
97083
|
ctx.interp.onJitBail?.(
|
|
@@ -96876,7 +97121,8 @@ var jitLoopExecutor = {
|
|
|
96876
97121
|
name: "jit-loop",
|
|
96877
97122
|
propose(lowered, ctx) {
|
|
96878
97123
|
if (lowered.kind !== "loop") return null;
|
|
96879
|
-
if (ctx.interp.loopDepth > 0
|
|
97124
|
+
if (ctx.interp.loopDepth > 0 && ctx.interp.jitDeclinedLoopDepth === 0)
|
|
97125
|
+
return null;
|
|
96880
97126
|
const classification = lowered.classification;
|
|
96881
97127
|
if (ctx.interp.optimization === "2" && classification.assertsCJit) {
|
|
96882
97128
|
return null;
|
|
@@ -96918,7 +97164,7 @@ var jitLoopExecutor = {
|
|
|
96918
97164
|
);
|
|
96919
97165
|
const nargout = d.outputs.length;
|
|
96920
97166
|
try {
|
|
96921
|
-
const { source, cName } = compileSpec({
|
|
97167
|
+
const { source, cName, outputTypes } = compileSpec({
|
|
96922
97168
|
workspace,
|
|
96923
97169
|
lowerer,
|
|
96924
97170
|
funcDecl,
|
|
@@ -96933,12 +97179,13 @@ var jitLoopExecutor = {
|
|
|
96933
97179
|
);
|
|
96934
97180
|
const factory = new Function(source)();
|
|
96935
97181
|
const specFn = factory(buildHostHelpers(interp.rt));
|
|
96936
|
-
return { specFn };
|
|
97182
|
+
return { specFn, outputTypes };
|
|
96937
97183
|
} catch (e) {
|
|
96938
97184
|
if (e instanceof UnsupportedConstruct || e instanceof TypeError2) {
|
|
96939
97185
|
recordJitDecline({
|
|
96940
97186
|
message: e.message,
|
|
96941
97187
|
kind: e.constructor.name,
|
|
97188
|
+
at: e.span ? { file: e.span.file, start: e.span.start } : void 0,
|
|
96942
97189
|
where: "jit-loop"
|
|
96943
97190
|
});
|
|
96944
97191
|
return null;
|
|
@@ -96963,7 +97210,9 @@ var jitLoopExecutor = {
|
|
|
96963
97210
|
if (d.outputs.length === 0) {
|
|
96964
97211
|
} else if (d.outputs.length === 1) {
|
|
96965
97212
|
if (result !== void 0) {
|
|
96966
|
-
const rv = ensureRuntimeValue(
|
|
97213
|
+
const rv = ensureRuntimeValue(
|
|
97214
|
+
jitToNumbl(result, compiled.outputTypes[0])
|
|
97215
|
+
);
|
|
96967
97216
|
interp.env.set(d.outputs[0], rv);
|
|
96968
97217
|
}
|
|
96969
97218
|
} else {
|
|
@@ -96975,7 +97224,9 @@ var jitLoopExecutor = {
|
|
|
96975
97224
|
for (let i = 0; i < d.outputs.length; i++) {
|
|
96976
97225
|
const elt = result[i];
|
|
96977
97226
|
if (elt !== void 0) {
|
|
96978
|
-
const rv = ensureRuntimeValue(
|
|
97227
|
+
const rv = ensureRuntimeValue(
|
|
97228
|
+
jitToNumbl(elt, compiled.outputTypes[i])
|
|
97229
|
+
);
|
|
96979
97230
|
interp.env.set(d.outputs[i], rv);
|
|
96980
97231
|
}
|
|
96981
97232
|
}
|
|
@@ -97063,7 +97314,7 @@ var jitTopLevelExecutor = {
|
|
|
97063
97314
|
);
|
|
97064
97315
|
const nargout = d.outputs.length;
|
|
97065
97316
|
try {
|
|
97066
|
-
const { source, cName } = compileSpec({
|
|
97317
|
+
const { source, cName, outputTypes } = compileSpec({
|
|
97067
97318
|
workspace,
|
|
97068
97319
|
lowerer,
|
|
97069
97320
|
funcDecl,
|
|
@@ -97078,12 +97329,13 @@ var jitTopLevelExecutor = {
|
|
|
97078
97329
|
);
|
|
97079
97330
|
const factory = new Function(source)();
|
|
97080
97331
|
const specFn = factory(buildHostHelpers(interp.rt));
|
|
97081
|
-
return { specFn, nargout };
|
|
97332
|
+
return { specFn, nargout, outputTypes };
|
|
97082
97333
|
} catch (e) {
|
|
97083
97334
|
if (e instanceof UnsupportedConstruct || e instanceof TypeError2) {
|
|
97084
97335
|
recordJitDecline({
|
|
97085
97336
|
message: e.message,
|
|
97086
97337
|
kind: e.constructor.name,
|
|
97338
|
+
at: e.span ? { file: e.span.file, start: e.span.start } : void 0,
|
|
97087
97339
|
where: "jit-top-level"
|
|
97088
97340
|
});
|
|
97089
97341
|
return null;
|
|
@@ -97108,7 +97360,9 @@ var jitTopLevelExecutor = {
|
|
|
97108
97360
|
if (d.outputs.length === 0) {
|
|
97109
97361
|
} else if (d.outputs.length === 1) {
|
|
97110
97362
|
if (result !== void 0) {
|
|
97111
|
-
const rv = ensureRuntimeValue(
|
|
97363
|
+
const rv = ensureRuntimeValue(
|
|
97364
|
+
jitToNumbl(result, compiled.outputTypes[0])
|
|
97365
|
+
);
|
|
97112
97366
|
interp.env.set(d.outputs[0], rv);
|
|
97113
97367
|
}
|
|
97114
97368
|
} else {
|
|
@@ -97120,7 +97374,9 @@ var jitTopLevelExecutor = {
|
|
|
97120
97374
|
for (let i = 0; i < d.outputs.length; i++) {
|
|
97121
97375
|
const elt = result[i];
|
|
97122
97376
|
if (elt !== void 0) {
|
|
97123
|
-
const rv = ensureRuntimeValue(
|
|
97377
|
+
const rv = ensureRuntimeValue(
|
|
97378
|
+
jitToNumbl(elt, compiled.outputTypes[i])
|
|
97379
|
+
);
|
|
97124
97380
|
interp.env.set(d.outputs[i], rv);
|
|
97125
97381
|
}
|
|
97126
97382
|
}
|
|
@@ -99942,7 +100198,7 @@ static void mtoc2__format_walk(mtoc2__writer_fn writer, void *ctx,
|
|
|
99942
100198
|
"warning_fmt.h": '/* mtoc2 runtime helper: warning \u2014 write a `Warning: <formatted text>\\n`\n * line to stdout. Matches numbl\'s `warning(...)` shape\n * (numbl/src/numbl-core/runtime/specialBuiltins.ts:190 \u2014 `rt.output("Warning: " + ... + "\\n")`).\n *\n * Two forms:\n * - `mtoc2_warning_fmt(fmt, nargs, args)` \u2014 bare `warning(fmt, ...)`.\n * - `mtoc2_warning_fmt_id(id, fmt, nargs, args)` \u2014 id-bearing\n * `warning(\'id:topic\', fmt, ...)`. The c-aot path ignores the id\n * (it has no `lastwarn`-style state to update); the JS sibling\n * matches numbl by including the id only in side-channel state if\n * we ever add it. For now both forms produce the same prefix.\n *\n * Format engine is shared with sprintf / fprintf \u2014 see format_engine.h.\n */\n\n#include <stdio.h>\n\nstatic void mtoc2__warning_writer(void *ctx, const char *bytes, long len) {\n (void)ctx;\n if (len > 0) fwrite(bytes, 1, (size_t)len, stdout);\n}\n\nstatic void mtoc2_warning_fmt(mtoc2_text_view_t fmt, int nargs,\n const mtoc2_fprintf_arg_t *args) {\n fputs("Warning: ", stdout);\n mtoc2__format_walk(mtoc2__warning_writer, NULL, fmt, nargs, args);\n fputc(\'\\n\', stdout);\n}\n\nstatic void mtoc2_warning_fmt_id(mtoc2_text_view_t id, mtoc2_text_view_t fmt,\n int nargs, const mtoc2_fprintf_arg_t *args) {\n (void)id; /* c-aot has no lastwarn state to set */\n mtoc2_warning_fmt(fmt, nargs, args);\n}\n',
|
|
99943
100199
|
"plot_dispatch.h": '/* mtoc2 runtime: plot dispatch \u2014 single helper for every plotting\n * builtin (`plot`, `surf`, `imagesc`, `bar`, `figure`, `hold`, \u2026).\n *\n * Numbl handles plotting by accumulating an in-memory "instruction\n * stream" that a separate viewer renders; the CLI never writes plot\n * output to stdout. mtoc2 mirrors that data-shape on the wire: each\n * plotting call emits one line of JSON on stdout, prefixed by the\n * ASCII RS (`\\x1e`) sentinel plus the literal tag `mtoc2:plot\\t`.\n * A launcher / viewer process tees stdout, splits on the prefix,\n * and feeds the records into numbl\'s own plot module \u2014 no C\n * plotting code needed.\n *\n * Why RS as the sentinel: RS (0x1E, "record separator") is a C0\n * control character that practical numeric / text output never\n * contains, so the splitter can identify plot lines purely by\n * prefix without escaping user output. The cross-runner drops\n * these lines globally before the byte-for-byte stdout compare\n * (numbl produces no such lines, so the drop is a no-op there).\n *\n * The wire shape per call is:\n *\n * \\x1e mtoc2:plot \\t {"call":"<name>","args":[<arg>, ...]} \\n\n *\n * Argument encoding (one element per source-level arg):\n * double -> bare numeric (NaN / \xB1Infinity rendered as `null`,\n * matching JS `JSON.stringify` behavior so the\n * viewer round-trips through `JSON.parse`)\n * text -> {"kind":"text","data":"<utf-8 with JSON escapes>"}\n * (char and string sources both flatten to this \u2014\n * MATLAB plot semantics don\'t distinguish them)\n * tensor -> {"kind":"tensor","dims":[d1,...,dN],\n * "data":[v1, ...]} (column-major flatten;\n * non-finite elements as `null`)\n *\n * Argument transport reuses `mtoc2_fprintf_arg_t` from\n * `format_engine.h` (slot kinds DOUBLE / TEXT / TENSOR). Type-level\n * rejection of unsupported arg kinds (complex / struct / class /\n * handle / Void / Unknown) happens at lowering \u2014 the dispatcher\n * itself never receives them.\n *\n * `fflush(stdout)` after every record makes plots stream live when\n * stdout is piped to a viewer; cost is one syscall per call and\n * negligible compared to user-level disp output.\n */\n\nstatic void mtoc2__plot_emit(const char *s, long n) {\n if (n > 0) fwrite(s, 1, (size_t)n, stdout);\n}\n\nstatic void mtoc2__plot_emit_cstr(const char *s) {\n mtoc2__plot_emit(s, (long)strlen(s));\n}\n\nstatic void mtoc2__plot_emit_double(double x) {\n if (!isfinite(x)) {\n mtoc2__plot_emit_cstr("null");\n return;\n }\n char buf[32];\n long n;\n if (fabs(x) < 1e16 && x == (double)(long long)x) {\n n = (long)snprintf(buf, sizeof(buf), "%lld", (long long)x);\n } else {\n n = (long)snprintf(buf, sizeof(buf), "%.17g", x);\n }\n mtoc2__plot_emit(buf, n);\n}\n\nstatic void mtoc2__plot_emit_json_string(const char *data, long len) {\n fputc(\'"\', stdout);\n for (long i = 0; i < len; i++) {\n unsigned char c = (unsigned char)data[i];\n if (c == \'"\' || c == \'\\\\\') {\n char esc[2] = { \'\\\\\', (char)c };\n mtoc2__plot_emit(esc, 2);\n } else if (c == \'\\n\') {\n mtoc2__plot_emit("\\\\n", 2);\n } else if (c == \'\\r\') {\n mtoc2__plot_emit("\\\\r", 2);\n } else if (c == \'\\t\') {\n mtoc2__plot_emit("\\\\t", 2);\n } else if (c == \'\\b\') {\n mtoc2__plot_emit("\\\\b", 2);\n } else if (c == \'\\f\') {\n mtoc2__plot_emit("\\\\f", 2);\n } else if (c < 0x20) {\n char esc[8];\n long n = (long)snprintf(esc, sizeof(esc), "\\\\u%04x", c);\n mtoc2__plot_emit(esc, n);\n } else {\n /* Bytes \u2265 0x20 (including high-bit UTF-8 continuation) pass\n * through verbatim; JSON allows raw UTF-8 in string content. */\n fputc((int)c, stdout);\n }\n }\n fputc(\'"\', stdout);\n}\n\nstatic void mtoc2__plot_emit_arg(const mtoc2_fprintf_arg_t *a) {\n switch (a->kind) {\n case MTOC2_FA_DOUBLE:\n mtoc2__plot_emit_double(a->u.d);\n return;\n case MTOC2_FA_TEXT: {\n mtoc2_text_view_t tv = a->u.t;\n long n = tv.len > 0 ? tv.len : 0;\n const char *data = tv.data ? tv.data : "";\n mtoc2__plot_emit_cstr("{\\"kind\\":\\"text\\",\\"data\\":");\n mtoc2__plot_emit_json_string(data, n);\n fputc(\'}\', stdout);\n return;\n }\n case MTOC2_FA_TENSOR: {\n const mtoc2_tensor_t *t = a->u.tensor;\n mtoc2__plot_emit_cstr("{\\"kind\\":\\"tensor\\",\\"dims\\":[");\n long total = 1;\n for (int k = 0; k < t->ndim; k++) {\n if (k > 0) fputc(\',\', stdout);\n char buf[24];\n long n = (long)snprintf(buf, sizeof(buf), "%ld", (long)t->dims[k]);\n mtoc2__plot_emit(buf, n);\n total *= t->dims[k];\n }\n mtoc2__plot_emit_cstr("],\\"data\\":[");\n for (long i = 0; i < total; i++) {\n if (i > 0) fputc(\',\', stdout);\n mtoc2__plot_emit_double(t->real[i]);\n }\n mtoc2__plot_emit_cstr("]}");\n return;\n }\n default:\n mtoc2__plot_emit_cstr("null");\n return;\n }\n}\n\nstatic void mtoc2_plot_dispatch(const char *name, int nargs,\n const mtoc2_fprintf_arg_t *args) {\n mtoc2__plot_emit_cstr("\\x1emtoc2:plot\\t{\\"call\\":");\n mtoc2__plot_emit_json_string(name, (long)strlen(name));\n mtoc2__plot_emit_cstr(",\\"args\\":[");\n for (int i = 0; i < nargs; i++) {\n if (i > 0) fputc(\',\', stdout);\n mtoc2__plot_emit_arg(&args[i]);\n }\n mtoc2__plot_emit_cstr("]}\\n");\n fflush(stdout);\n}\n',
|
|
99944
100200
|
"cdiv.h": "/* mtoc2 runtime helper: scalar complex division `a / b` matching\n * numbl's signed-Inf-on-zero-divisor behavior.\n *\n * C99's `_Complex` division is unspecified at the signs of NaN /\n * Inf results when the divisor is zero, so divisions like\n * `(1 + 2i) / 0` can disagree between compilers. Numbl uses Smith's\n * algorithm (which factors out the larger-magnitude divisor\n * component) and explicit \xB10 detection to land on the same byte\n * stream regardless of the underlying libc. This helper mirrors\n * that path so cross-runner output stays aligned.\n */\n\n#include <complex.h>\n#include <math.h>\n\nstatic double mtoc2_signed_inf(double x) {\n return x > 0 ? INFINITY : (x < 0 ? -INFINITY : 0.0);\n}\n\n/* Pack re/im into a complex without the `im * I` trap: `INFINITY * I`\n * is `NaN + INFINITY*i` (0*Inf in the real lane), which would corrupt a\n * signed-Inf result. __builtin_complex (gcc/clang) sets the components\n * directly; CMPLX is the C11 fallback. */\nstatic double _Complex mtoc2_cpack(double re, double im) {\n#if defined(__clang__) || defined(__GNUC__)\n return __builtin_complex(re, im);\n#else\n return CMPLX(re, im);\n#endif\n}\n\nstatic double _Complex mtoc2_cdiv(double _Complex a, double _Complex b) {\n double ar = creal(a), ai = cimag(a);\n double br = creal(b), bi = cimag(b);\n /* Zero divisor: match the interpreter (helpers/arithmetic.ts) \u2014 0/0 is\n * NaN, a nonzero numerator yields a signed Inf per component. Smith's\n * algorithm below would otherwise produce NaN+NaNi here. */\n if (br == 0.0 && bi == 0.0) {\n if (ar == 0.0 && ai == 0.0) return mtoc2_cpack(NAN, 0.0);\n return mtoc2_cpack(mtoc2_signed_inf(ar), mtoc2_signed_inf(ai));\n }\n /* Standard Smith's: pick the scaling that puts the larger-magnitude\n * divisor component in the denominator. */\n if (fabs(br) >= fabs(bi)) {\n double r = bi / br;\n double den = br + r * bi;\n return (ar + ai * r) / den + ((ai - ar * r) / den) * I;\n }\n double r = br / bi;\n double den = bi + r * br;\n return (ar * r + ai) / den + ((ai * r - ar) / den) * I;\n}\n",
|
|
99945
|
-
"cscalar.h": "/* mtoc2 runtime helpers: scalar complex operations as explicit\n * function calls.\n *\n * Every `double _Complex` operation mtoc2 emits into user code routes\n * through one of these helpers instead of relying on C99's operator\n * overloading or the `I` macro. On the C path each is `static inline`\n * and the compiler folds back to the same instructions C99 would have\n * generated. The win is parallelism with the js-aot path: the JS\n * sibling (`cscalar.js`) ships a matching set of helpers that operate\n * on a `{re, im}` JS representation, so user code emitted by `emitJs`\n * uses the same call shape the C emitter would have produced.\n *\n * Division and pow are NOT defined here:\n * - `mtoc2_cdiv` lives in `cdiv.h` because it needs Smith's\n * algorithm + signed-zero detection to match numbl byte-for-byte.\n * - `cpow` is emitted directly today (still a libm call, not an\n * operator); a `mtoc2_cpow` wrapper lands when the JS side grows\n * a power helper.\n */\n\n#include <complex.h>\n#include <math.h>\n\nstatic inline double _Complex mtoc2_cmake(double re, double im) {\n return re + im * I;\n}\nstatic inline double mtoc2_creal(double _Complex z) { return creal(z); }\nstatic inline double mtoc2_cimag(double _Complex z) { return cimag(z); }\nstatic inline double _Complex mtoc2_cadd(double _Complex a, double _Complex b) { return a + b; }\nstatic inline double _Complex mtoc2_csub(double _Complex a, double _Complex b) { return a - b; }\nstatic inline double _Complex mtoc2_cmul(double _Complex a, double _Complex b) { return a * b; }\nstatic inline double _Complex mtoc2_cneg(double _Complex z) { return -z; }\nstatic inline double _Complex mtoc2_cconj(double _Complex z) { return conj(z); }\nstatic inline double mtoc2_cabs(double _Complex z) {\n return hypot(creal(z), cimag(z));\n}\nstatic inline double mtoc2_cangle(double _Complex z) {\n return atan2(cimag(z), creal(z));\n}\nstatic inline int mtoc2_cnonzero(double _Complex z) {\n return creal(z) != 0.0 || cimag(z) != 0.0;\n}\nstatic inline int mtoc2_ceq(double _Complex a, double _Complex b) {\n return creal(a) == creal(b) && cimag(a) == cimag(b);\n}\nstatic inline int mtoc2_cne(double _Complex a, double _Complex b) {\n return creal(a) != creal(b) || cimag(a) != cimag(b);\n}\nstatic inline double _Complex mtoc2_cpow(double _Complex a, double _Complex b) {\n return cpow(a, b);\n}\n\n/* Unary complex math \u2014 `<complex.h>` wrappers + a handful of\n * derived helpers C99 doesn't ship (log2/log10, MATLAB-style\n * componentwise rounding, signum). Same `mtoc2_c*` shape as the\n * core scalar helpers above so the c2js backend can substitute\n * its own `{re, im}` JS implementations.\n *\n * The rounding helpers (floor/ceil/round/fix) match MATLAB by\n * applying the rounding mode to each component independently and\n * returning a complex result; the magnitude is NOT preserved\n * (MATLAB's `floor([1.5 + 1.5i])` returns `1 + 1i`, not the\n * floor of the magnitude).\n *\n * `mtoc2_csign(z)` matches MATLAB: `z/|z|` for nonzero, exactly\n * `0 + 0i` for the zero scalar. */\nstatic inline double _Complex mtoc2_csqrt(double _Complex z) { return csqrt(z)
|
|
100201
|
+
"cscalar.h": "/* mtoc2 runtime helpers: scalar complex operations as explicit\n * function calls.\n *\n * Every `double _Complex` operation mtoc2 emits into user code routes\n * through one of these helpers instead of relying on C99's operator\n * overloading or the `I` macro. On the C path each is `static inline`\n * and the compiler folds back to the same instructions C99 would have\n * generated. The win is parallelism with the js-aot path: the JS\n * sibling (`cscalar.js`) ships a matching set of helpers that operate\n * on a `{re, im}` JS representation, so user code emitted by `emitJs`\n * uses the same call shape the C emitter would have produced.\n *\n * Division and pow are NOT defined here:\n * - `mtoc2_cdiv` lives in `cdiv.h` because it needs Smith's\n * algorithm + signed-zero detection to match numbl byte-for-byte.\n * - `cpow` is emitted directly today (still a libm call, not an\n * operator); a `mtoc2_cpow` wrapper lands when the JS side grows\n * a power helper.\n */\n\n#include <complex.h>\n#include <math.h>\n\nstatic inline double _Complex mtoc2_cmake(double re, double im) {\n return re + im * I;\n}\nstatic inline double mtoc2_creal(double _Complex z) { return creal(z); }\nstatic inline double mtoc2_cimag(double _Complex z) { return cimag(z); }\nstatic inline double _Complex mtoc2_cadd(double _Complex a, double _Complex b) { return a + b; }\nstatic inline double _Complex mtoc2_csub(double _Complex a, double _Complex b) { return a - b; }\nstatic inline double _Complex mtoc2_cmul(double _Complex a, double _Complex b) { return a * b; }\nstatic inline double _Complex mtoc2_cneg(double _Complex z) { return -z; }\nstatic inline double _Complex mtoc2_cconj(double _Complex z) { return conj(z); }\nstatic inline double mtoc2_cabs(double _Complex z) {\n return hypot(creal(z), cimag(z));\n}\nstatic inline double mtoc2_cangle(double _Complex z) {\n return atan2(cimag(z), creal(z));\n}\nstatic inline int mtoc2_cnonzero(double _Complex z) {\n return creal(z) != 0.0 || cimag(z) != 0.0;\n}\nstatic inline int mtoc2_ceq(double _Complex a, double _Complex b) {\n return creal(a) == creal(b) && cimag(a) == cimag(b);\n}\nstatic inline int mtoc2_cne(double _Complex a, double _Complex b) {\n return creal(a) != creal(b) || cimag(a) != cimag(b);\n}\nstatic inline double _Complex mtoc2_cpow(double _Complex a, double _Complex b) {\n return cpow(a, b);\n}\n\n/* Unary complex math \u2014 `<complex.h>` wrappers + a handful of\n * derived helpers C99 doesn't ship (log2/log10, MATLAB-style\n * componentwise rounding, signum). Same `mtoc2_c*` shape as the\n * core scalar helpers above so the c2js backend can substitute\n * its own `{re, im}` JS implementations.\n *\n * The rounding helpers (floor/ceil/round/fix) match MATLAB by\n * applying the rounding mode to each component independently and\n * returning a complex result; the magnitude is NOT preserved\n * (MATLAB's `floor([1.5 + 1.5i])` returns `1 + 1i`, not the\n * floor of the magnitude).\n *\n * `mtoc2_csign(z)` matches MATLAB: `z/|z|` for nonzero, exactly\n * `0 + 0i` for the zero scalar. */\n/* True when z is a NaN on the real axis. The JIT lifts a real argument whose\n * sign it cannot prove onto these complex helpers, and MATLAB's sqrt(NaN) /\n * log(NaN) are a real NaN \u2014 so a lifted NaN must come back as NaN + 0i, not\n * 0 + NaN i. The lift is a compilation detail and must not be observable. */\nstatic inline int mtoc2_nan_on_real_axis(double _Complex z) {\n return cimag(z) == 0.0 && isnan(creal(z));\n}\nstatic inline double _Complex mtoc2_csqrt(double _Complex z) {\n if (mtoc2_nan_on_real_axis(z)) return mtoc2_cmake(creal(z), 0.0);\n return csqrt(z);\n}\nstatic inline double _Complex mtoc2_cexp(double _Complex z) { return cexp(z); }\nstatic inline double _Complex mtoc2_clog(double _Complex z) {\n if (mtoc2_nan_on_real_axis(z)) return mtoc2_cmake(creal(z), 0.0);\n return clog(z);\n}\nstatic inline double _Complex mtoc2_clog2(double _Complex z) {\n if (mtoc2_nan_on_real_axis(z)) return mtoc2_cmake(creal(z), 0.0);\n return clog(z) / log(2.0);\n}\nstatic inline double _Complex mtoc2_clog10(double _Complex z) {\n if (mtoc2_nan_on_real_axis(z)) return mtoc2_cmake(creal(z), 0.0);\n return clog(z) / log(10.0);\n}\nstatic inline double _Complex mtoc2_csin(double _Complex z) { return csin(z); }\nstatic inline double _Complex mtoc2_ccos(double _Complex z) { return ccos(z); }\nstatic inline double _Complex mtoc2_ctan(double _Complex z) { return ctan(z); }\nstatic inline double _Complex mtoc2_catan(double _Complex z) {\n /* Real-valued input: clean real atan (no spurious imaginary residue,\n no Inf/Inf NaN at large |re|). */\n if (cimag(z) == 0.0) return mtoc2_cmake(atan(creal(z)), 0.0);\n /* atan(z) = (i/2)*log((1 - iz)/(1 + iz)). MATLAB's branch for a\n pure-imaginary z with |z| > 1 is -pi/2 + ... (atan(2i) = -1.5708 +\n 0.5493i); libc catan uses the opposite +pi/2 Annex-G branch. Compute\n the formula here so the C path matches MATLAB and the JS runtime.\n Normalize a -0 imaginary part of the quotient to +0 (via +0.0) so\n clog picks the +pi branch \u2014 a -0 would flip it to -pi -> +pi/2. */\n double _Complex iz = mtoc2_cmake(-cimag(z), creal(z));\n double _Complex num = mtoc2_cmake(1.0 - creal(iz), -cimag(iz));\n double _Complex den = mtoc2_cmake(1.0 + creal(iz), cimag(iz));\n double _Complex q = num / den;\n double _Complex l = clog(mtoc2_cmake(creal(q), cimag(q) + 0.0));\n return mtoc2_cmake(-cimag(l) / 2.0, creal(l) / 2.0);\n}\n/* Hyperbolic sinh/cosh/tanh are entire functions (no branch cuts), so\n * C99's csinh/ccosh/ctanh agree with the JS componentwise formulas in\n * `cscalar.js` to within floating-point ULPs everywhere. */\nstatic inline double _Complex mtoc2_csinh(double _Complex z) { return csinh(z); }\nstatic inline double _Complex mtoc2_ccosh(double _Complex z) { return ccosh(z); }\nstatic inline double _Complex mtoc2_ctanh(double _Complex z) { return ctanh(z); }\nstatic inline double _Complex mtoc2_cfloor(double _Complex z) {\n return floor(creal(z)) + floor(cimag(z)) * I;\n}\nstatic inline double _Complex mtoc2_cceil(double _Complex z) {\n return ceil(creal(z)) + ceil(cimag(z)) * I;\n}\nstatic inline double _Complex mtoc2_cround(double _Complex z) {\n return round(creal(z)) + round(cimag(z)) * I;\n}\nstatic inline double _Complex mtoc2_cfix(double _Complex z) {\n return trunc(creal(z)) + trunc(cimag(z)) * I;\n}\nstatic inline double _Complex mtoc2_csign(double _Complex z) {\n double re = creal(z), im = cimag(z);\n if (re == 0.0 && im == 0.0) return 0.0;\n double m = hypot(re, im);\n return (re / m) + (im / m) * I;\n}\n",
|
|
99946
100202
|
"deep_clone.h": "/* JS-only snippet (paired with `deep_clone.js`).\n *\n * The C path's owned-value protocol (per-typedef `_copy` helpers\n * generated by `emitNamedTypedef`) provides the equivalent\n * always-copy semantics. This snippet exists only to give the\n * activation system a `.h` sibling for the JS-side\n * `mtoc2_deep_clone` helper; the C body is intentionally empty.\n */\n",
|
|
99947
100203
|
"isequal.h": "/* mtoc2 runtime helpers for `isequal` over real numeric values.\n *\n * Complex tensors, char / string, struct and cell comparison are\n * handled by the interpreter's `call` hook; the AOT `emitC` rejects\n * those argument combinations with `UnsupportedConstruct`. These two\n * helpers cover the dominant cases: tensor-vs-tensor and\n * scalar-vs-tensor over real data.\n *\n * Equality follows numbl's `valuesEqualSimple` / `tensorsEqual`:\n * same rank, same per-axis extent, element-for-element `==`\n * (so `NaN != NaN`, matching MATLAB / numbl).\n */\n\n/* Two real tensors: equal iff identical shape and data. */\nstatic double mtoc2_isequal_tt(mtoc2_tensor_t a, mtoc2_tensor_t b) {\n if (a.ndim != b.ndim) return 0.0;\n long n = 1;\n for (int i = 0; i < a.ndim; i++) {\n if (a.dims[i] != b.dims[i]) return 0.0;\n n *= a.dims[i];\n }\n for (long i = 0; i < n; i++) {\n if (a.real[i] != b.real[i]) return 0.0;\n }\n return 1.0;\n}\n\n/* Real scalar vs real tensor: equal iff the tensor holds exactly one\n * element equal to `s` (numbl treats a scalar as a 1\xD71 tensor). */\nstatic double mtoc2_isequal_st(double s, mtoc2_tensor_t t) {\n long n = 1;\n for (int i = 0; i < t.ndim; i++) n *= t.dims[i];\n if (n != 1) return 0.0;\n return t.real[0] == s ? 1.0 : 0.0;\n}\n",
|
|
99948
100204
|
"length.h": "/* mtoc runtime helper: MATLAB's `length(t)` on a multi-element tensor.\n *\n * Returns the size of the largest axis as a double, or 0.0 if any axis\n * has length 0. Mirrors MATLAB/numbl semantics. Scalar args don't reach\n * this helper \u2014 codegen emits the literal `1.0` directly because the C\n * arg is a bare `double`, not an `mtoc2_tensor_t`.\n */\n\nstatic double mtoc2_length(mtoc2_tensor_t t) {\n long max = 0;\n for (int i = 0; i < t.ndim; i++) {\n if (t.dims[i] == 0) return 0.0;\n if (t.dims[i] > max) max = t.dims[i];\n }\n return (double)max;\n}\n",
|
|
@@ -100479,7 +100735,8 @@ var cJitLoopExecutor = {
|
|
|
100479
100735
|
if (lowered.kind !== "loop") return null;
|
|
100480
100736
|
const bridge = ctx.interp.nativeBridge;
|
|
100481
100737
|
if (!bridge || !bridge.koffi) return null;
|
|
100482
|
-
if (ctx.interp.loopDepth > 0
|
|
100738
|
+
if (ctx.interp.loopDepth > 0 && ctx.interp.jitDeclinedLoopDepth === 0)
|
|
100739
|
+
return null;
|
|
100483
100740
|
const classification = lowered.classification;
|
|
100484
100741
|
if (classification.hasReturn) return null;
|
|
100485
100742
|
const compilerInputTypes = [];
|
|
@@ -100691,7 +100948,8 @@ var cJitCallExecutor = {
|
|
|
100691
100948
|
if (lowered.kind !== "call") return null;
|
|
100692
100949
|
const bridge = ctx.interp.nativeBridge;
|
|
100693
100950
|
if (!bridge || !bridge.koffi) return null;
|
|
100694
|
-
if (ctx.interp.loopDepth > 0
|
|
100951
|
+
if (ctx.interp.loopDepth > 0 && ctx.interp.jitDeclinedLoopDepth === 0)
|
|
100952
|
+
return null;
|
|
100695
100953
|
const classification = lowered.classification;
|
|
100696
100954
|
if (classification.nargout === 0) return null;
|
|
100697
100955
|
const compilerArgTypes = [];
|
|
@@ -101222,7 +101480,9 @@ Options (for run and eval):
|
|
|
101222
101480
|
|
|
101223
101481
|
Environment variables:
|
|
101224
101482
|
NUMBL_PATH Extra workspace directories (separated by ${delimiter})
|
|
101225
|
-
NUMBL_NO_NATIVE If set, do not load the native LAPACK addon
|
|
101483
|
+
NUMBL_NO_NATIVE If set, do not load the native LAPACK addon
|
|
101484
|
+
NUMBL_JIT_LOG If set, print every JIT decline (executor, source
|
|
101485
|
+
position, reason) to stderr`);
|
|
101226
101486
|
}
|
|
101227
101487
|
function parseOptions(args) {
|
|
101228
101488
|
const opts = {
|