Search Issue Tracker

Won't Fix

Votes

1

Found in [Package]

1.8.4

Issue ID

BUR-2424

Regression

No

Burst Constant.IsConstantExpression() check is ignored when using float8

Package: Burst

-

Reproduction steps:

1. Open the attached “ASDQWE” project

2. Open the “Asset/Scenes/SampleScene.unity” Scene

3. Open the Burst Inspector (Jobs > Burst > Open Inspector…)

4. Observe the “Bug.Testing.ConstantPow(MaxMath.float8&, MaxMath.float8&)” method

Expected result: “Bug.Testing.ConstantPow(MaxMath.float8&, MaxMath.float8&)” method performs a series of multiplications instead of pow calls

Actual result: “Bug.Testing.ConstantPow(MaxMath.float8&, MaxMath.float8&)” method ignores the Constant.IsConstantExpression() check and contains calls to pow

Reproducible with: 1.7.4 (2021.3.27f1), 1.8.3(2021.3.27f1), 1.8.4 (2021.3.27f1, 2022.3.2f1, 2023.1.0f1, 2023.2.0a19)

Reproducible on: Intel MacOS 13.4

  1. Resolution Note:

    Constant.IsConstantExpression is technically working as intended. The short explanation of why the float8 version doesn't behave like the float4 one is mainly due to internal optimization heuristics doing things in a slightly different order, causing `y` at the time `Constant.IsConstantExpression` is evaluated not to be constant.

    Here's the more technical and longer answer:

    Burst compiles your code into LLVM's intermediate representation (IR) and then uses several LLVM optimization-passes to produce the final program. Some of these optimization-passes do constant folding (evaluating things at compile time) and some do inlining (substituting calls to functions for their implementations at the call-site). We have several phases of these and they interact non-triviallly. The order in which those are applied on a per-function basis also has an effect on the final out-come.

    Now, we compile our Constant.IsConstantExpression down to LLVM's intrinsic `llvm.is.constant.*`. And as LLVM write themselves:

    "The result [of llvm.is.constant] also intentionally depends on the result of optimization passes – e.g., the result can change depending on whether a function gets inlined or not. A function’s parameters are obviously not constant. However, a call like llvm.is.constant.i32(i32 %param) can return true after the function is inlined, if the value passed to the function parameter was a constant.

    On the other hand, if constant folding is not run, it will never evaluate to true, even in simple cases."

    Now, we don't have full control over exactly in what order any given collection of functions get visited, but if for example the `maxmath.pow` happens to get folded before it gets inlined into the caller, then Constant.IsConstantExpression would be false. Which, is exactly what happens in your case. I suspect this different ordering happens become of the relative order of the functions being visited through a hash-map iterator, because changing `maxmath.pow`'s name to `maxmath.powee` will yield the "nice" result. We don't control this ordering from our side, as this is very much just part of LLVM's internals.

    While it's obviously suboptimal in your specific case, there's no trivial good general solution. As said, the interaction between the many different optimization passes are non-trivial, and changing them to generate better code in your case, might worsen the code-gen in many order cases.

    We will be updating the documentation for `Constant.IsConstantExpression` in order to make the nature of it clearer though.

Add comment

Log in to post comment

All about bugs

View bugs we have successfully reproduced, and vote for the bugs you want to see fixed most urgently.