Skip to content

Commit 0de8e7f

Browse files
committed
feat: Enhance constant folding and optimization in JavaScript and GLSL targets
- Implemented constant folding for various arithmetic operations in JavaScript target, including Add, Multiply, and Power functions. - Added support for Mandelbrot and Julia set functions in JavaScript target. - Optimized mathematical functions (Ceil, Floor, Round, Truncate) to bypass unnecessary computations for integer inputs. - Updated GLSL and WGSL compilation to use direct multiplication for squares instead of pow function. - Refactored complex number handling, renaming functions from Re, Im, Arg to Real, Imaginary, Argument for clarity. - Introduced comprehensive tests for constant folding utilities and GPU handler optimizations. - Improved error handling in ComputeEngine for unexpected values.
1 parent 5dc3170 commit 0de8e7f

18 files changed

+2313
-206
lines changed

CHANGELOG.md

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
1+
### [Unreleased]
2+
3+
#### Improved
4+
5+
- **Compilation: constant folding**`Add`, `Multiply`, `Subtract`, `Negate`,
6+
`Divide`, `Power`, `Sqrt`, and `Root` handlers now fold numeric literals at
7+
compile time and eliminate identity values.
8+
- `x + yi` compiles to `vec2(x, y)` instead of
9+
`vec2(x, 0.0) + (y * vec2(0.0, 1.0))`
10+
- `2 + 3``5.0`, `x + 0``x`, `x * 1``x`, `x * 0``0.0`
11+
- `Power(x, 2)``(x * x)` for simple operands, `pow(f(x), 2.0)` for complex
12+
expressions to avoid duplicate computation
13+
- `Power(x, 0.5)``sqrt(x)`, `Power(x, 0)``1.0`, `Power(x, -1)`
14+
`(1.0 / x)`
15+
- `Sqrt(4)``2.0`, `Root(x, 2)``sqrt(x)`
16+
- **`isComplexValued`** uses expression type system instead of hard-coded
17+
operator list.
18+
- **Integer arguments** in GPU fractal functions emit as `200` instead of
19+
`int(200.0)`.
20+
- **Type-based optimizations** — compilation handlers now use expression type
21+
information for better code generation:
22+
- `Floor`/`Ceil`/`Round`/`Truncate` are no-ops when the operand is
23+
integer-typed
24+
- `Abs` is a no-op when the operand is provably non-negative
25+
- `Power(x, 2)` only expands to `(x * x)` for simple operands (symbols,
26+
literals) — function calls like `Power(Sin(x), 2)` use `pow`/`Math.pow`
27+
to avoid duplicate evaluation
28+
- Integer `Mod` with non-negative dividend uses plain `%` instead of the
29+
Euclidean double-mod formula
30+
- GPU variable declarations infer `i32`/`int` type for integer-typed locals
31+
32+
#### Fixed
33+
34+
- **`Abs` signature**: return type is now `real` instead of propagating the
35+
input type (which incorrectly returned `complex` for complex inputs).
36+
- **Compilation fallback**: uses `pushScope`/`assign` pattern instead of
37+
crashing when receiving a vars object.
38+
39+
#### Added
40+
41+
- **`Mandelbrot` and `Julia`** operators in JavaScript and GPU compilation
42+
targets.
43+
144
### 0.55.2 _2026-03-04_
245

346
#### Fixed
@@ -12,9 +55,9 @@
1255
symbol validation.
1356
- **`Text` operator type**: The `Text` operator now has return type `string`
1457
instead of `expression`.
15-
- **`\textcolor` inside `\text{}`**: `\textcolor{red}{RED}` inside `\text{}`
16-
now correctly parses the body as text (`'RED'`) instead of switching to math
17-
mode and treating each letter as a separate symbol.
58+
- **`\textcolor` inside `\text{}`**: `\textcolor{red}{RED}` inside `\text{}` now
59+
correctly parses the body as text (`'RED'`) instead of switching to math mode
60+
and treating each letter as a separate symbol.
1861
- **`parseSyntaxError` token consumption**: Non-command tokens (like `#`, `&`)
1962
are now consumed when producing errors, preventing potential parser loops.
2063
- **`parseSymbolToken` hardening**: Raw tokens are pre-validated against
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Constant Folding for Compilation Targets
2+
3+
## Problem
4+
5+
The GPU compiler emits redundant code for complex number construction and
6+
real arithmetic. For example, `x + yi` compiles to
7+
`vec2(x, 0.0) + (y * vec2(0.0, 1.0))` instead of `vec2(x, y)`.
8+
9+
## Approach
10+
11+
Add helper utilities for constant inspection and term folding, then update
12+
the `GPU_FUNCTIONS` handlers to use them. Folding happens at compile time
13+
by inspecting the expression tree — no string-level peephole passes.
14+
15+
## Helper Utilities
16+
17+
New file: `src/compute-engine/compilation/constant-folding.ts`
18+
19+
### `tryGetConstant(expr: Expression): number | undefined`
20+
21+
Returns a compile-time numeric constant if the expression is a literal
22+
number (integer, rational, float). Returns `undefined` for symbols,
23+
function expressions, or non-finite values.
24+
25+
### `tryGetComplexParts(expr, compile, target): { re: string | null, im: string | null }`
26+
27+
Decomposes an expression into real and imaginary code-string contributions.
28+
`null` means zero (no contribution to that component).
29+
30+
Patterns handled:
31+
- `Complex(a, b)` with constant parts
32+
- `ImaginaryUnit` -> `{ re: null, im: "1.0" }`
33+
- `Multiply(k, ImaginaryUnit)` -> `{ re: null, im: compiled(k) }`
34+
- `Multiply(...reals, ImaginaryUnit, ...reals)` -> separate real product as im
35+
- Plain real expr -> `{ re: compiled, im: null }`
36+
37+
### `foldTerms(terms: string[], identity: string, op: '+' | '*'): string`
38+
39+
Combines compiled string terms with an operator:
40+
- Folds numeric literals (`"2.0" + "3.0"` -> `"5.0"`)
41+
- Eliminates identities (`x + 0.0` -> `x`, `x * 1.0` -> `x`)
42+
- Handles absorbing elements (`x * 0.0` -> `0.0`)
43+
- Returns identity for empty array, term itself for single-element
44+
45+
## Handler Changes (gpu-target.ts)
46+
47+
### Add
48+
49+
1. No complex operands: use `foldTerms` on compiled real terms.
50+
2. Complex operands: partition all operands into real/imaginary contributions
51+
via `tryGetComplexParts`, collect parts, fold each with `foldTerms`,
52+
emit `vec2(realSum, imagSum)`.
53+
54+
### Multiply
55+
56+
1. Real-only: use `foldTerms` for constant folding and identity elimination.
57+
2. `Multiply(scalar, ImaginaryUnit)`: emit `vec2(0.0, scalar)` directly.
58+
3. `Multiply(scalars..., complexExpr)`: fold scalar part first, then
59+
scalar-multiply the complex expression.
60+
61+
### Negate
62+
63+
- `Negate(Complex(a,b))`: emit `vec2(-a, -b)`.
64+
- `Negate(constant)`: fold to negated constant.
65+
- `Negate(ImaginaryUnit)`: emit `vec2(0.0, -1.0)`.
66+
67+
### Divide
68+
69+
- `Divide(constant, constant)`: fold to result.
70+
- `Divide(a, constant)`: fold constant divisor.
71+
- Complex path unchanged (uses `_gpu_cdiv`).
72+
73+
### Power
74+
75+
- `Power(x, 0)` -> `1.0`
76+
- `Power(x, 1)` -> `x`
77+
- `Power(x, 2)` -> `(x * x)`
78+
- `Power(x, -1)` -> `(1.0 / x)`
79+
- `Power(x, 0.5)` -> `sqrt(x)`
80+
- `Power(constant, constant)` -> fold to result.
81+
82+
### Sqrt
83+
84+
- `Sqrt(constant)` -> fold to result.
85+
- `Sqrt(0)` -> `0.0`, `Sqrt(1)` -> `1.0`.
86+
87+
### Root
88+
89+
- `Root(x, 2)` -> `sqrt(x)`.
90+
- `Root(constant, constant)` -> fold to result.
91+
92+
## Testing
93+
94+
New file: `test/compute-engine/compile-constant-folding.test.ts`
95+
96+
### vec2 construction
97+
- `x + yi` -> `vec2(x, y)`
98+
- `x + 3i` -> `vec2(x, 3.0)`
99+
- `2x + 3yi` -> `vec2(2.0 * x, 3.0 * y)`
100+
- `3 + 4i` -> `vec2(3.0, 4.0)` (already works)
101+
- `0 + yi` -> `vec2(0.0, y)`
102+
103+
### Real folding
104+
- `2 + 3` -> `5.0`
105+
- `x + 0` -> `x`
106+
- `x * 1` -> `x`
107+
- `x * 0` -> `0.0`
108+
- `(2 + 3) * x` -> `5.0 * x`
109+
110+
### Identity elimination
111+
- `Power(x, 0)` -> `1.0`
112+
- `Power(x, 1)` -> `x`
113+
- `Sqrt(4)` -> `2.0`
114+
- `Divide(6, 3)` -> `2.0`
115+
116+
### Operator-specific
117+
- `Negate(Complex(3, 4))` -> `vec2(-3.0, -4.0)`
118+
- `Power(x, 2)` -> `(x * x)`
119+
- `Root(x, 2)` -> `sqrt(x)`
120+
121+
## Scope
122+
123+
- Primary target: GPU (GLSL/WGSL)
124+
- Helpers are target-agnostic so JS target can adopt them later
125+
- No changes to expression canonicalization or simplification

0 commit comments

Comments
 (0)