Summary
Unary minus (`-`) applied to a function parameter or let-bound variable is miscompiled. In HikariRenderer's PExpr stdlib, `abs(-3.5)` returns `-3.5` instead of `3.5`.
Reproduction
Using the PExpr compiler embedded in HikariRenderer (`SVMCompiler`, `dev` branch of PExpr), with all optimisation passes enabled:
| Expression |
Expected |
Actual |
| `abs(-3.5)` |
`3.5` |
`-3.5` |
| `abs(3.5)` |
`3.5` |
`3.5` ✓ |
| `abs(-1.0)` |
`1.0` |
`-1.0` |
| `if (-3.5 < 0.0) { -(-3.5) } else { -3.5 }` |
`3.5` |
`3.5` ✓ |
| `-(-3.5)` |
`3.5` |
`3.5` ✓ |
| `let a = -3.5 in -a` |
`3.5` |
`0` |
| `let a = -3.5 in 0.0 - a` |
`3.5` |
`0` |
The stdlib `abs` is defined as:
```
fn abs(a:num) = if a < 0 { -a } else { a };
```
The SSA dump for `abs(-3.5)` is a single line:
```
return -3.5:num
```
The `abs` call is fully folded away and the unary minus on the function parameter `-a` has been dropped.
Scope
- An explicit `if` with literal branches works — branch selection is fine.
- A top-level `-(-literal)` works — unary minus on a literal is fine.
- The bug shows up specifically when unary minus is applied to a function argument or a let-bound variable, i.e. when the operand is a reference to a binding being substituted/inlined.
Likely cause
Constant folding/inlining substitutes the argument's value into the function body but loses (or mis-handles) the surrounding unary minus. The `0 - a` path folding to `0` in the let case suggests `a` is being resolved to `0` when read from the binding — possibly an undefined/zero-initialised register slot for the let binding in one of the lowering passes.
Workaround
Use arithmetic negation instead of unary minus: `0.0 - x`. This also fails for let-bound variables (returns 0), so the workaround only applies to inlined literals.
Environment
- PExpr: `dev` branch as fetched by PearCoding/HikariRenderer (CPMAddPackage)
- Host compiler: MSVC 19.44, C++20
- OS: Windows 11 x64
Filed while building out CI-runnable unit tests for HikariRenderer's shader system.
Summary
Unary minus (`-`) applied to a function parameter or let-bound variable is miscompiled. In HikariRenderer's PExpr stdlib, `abs(-3.5)` returns `-3.5` instead of `3.5`.
Reproduction
Using the PExpr compiler embedded in HikariRenderer (`SVMCompiler`, `dev` branch of PExpr), with all optimisation passes enabled:
The stdlib `abs` is defined as:
```
fn abs(a:num) = if a < 0 { -a } else { a };
```
The SSA dump for `abs(-3.5)` is a single line:
```
return -3.5:num
```
The `abs` call is fully folded away and the unary minus on the function parameter `-a` has been dropped.
Scope
Likely cause
Constant folding/inlining substitutes the argument's value into the function body but loses (or mis-handles) the surrounding unary minus. The `0 - a` path folding to `0` in the let case suggests `a` is being resolved to `0` when read from the binding — possibly an undefined/zero-initialised register slot for the let binding in one of the lowering passes.
Workaround
Use arithmetic negation instead of unary minus: `0.0 - x`. This also fails for let-bound variables (returns 0), so the workaround only applies to inlined literals.
Environment
Filed while building out CI-runnable unit tests for HikariRenderer's shader system.