tests: Test how constant folding works on SM1.
While reviewing !671 (merged) I wrote a few tests that are probably not too fundamental, but since now they exist it makes sense to keep them. !671 (merged) doesn't fix nor break any of them, anyway.
Merge request reports
Activity
- tests/hlsl/sm1-const-folding.shader_test 0 → 100644
14 15 % The uint value is never casted to int on SM1-3, just casted to float and treated as such 16 [pixel shader] 17 float4 main() : SV_TARGET 18 { 19 int x = 3000000000u; 20 return float4(x, 0.0, 0.0, 0.0); 21 } 22 23 [test] 24 draw quad 25 if(sm<4) todo probe all rgba (3.0e+009, 0.0, 0.0, 0.0) 26 if(sm>=4) probe all rgba (-1.29496730e+009, 0.0, 0.0, 0.0) 27 28 % On SM1-5 (FXC) the literal is considered signed independently of its value and casted to uint; 29 % on SM1-3 compilation fails because the uint it would be negative changed this line in version 2 of the diff
- tests/hlsl/sm1-const-folding.shader_test 0 → 100644
37 [test] 38 draw quad 39 if(sm>=4) probe all rgba (3.0e+009, 0.0, 0.0, 0.0) 40 41 [pixel shader] 42 float4 main() : SV_TARGET 43 { 44 int x = 3000000000; 45 return float4(x, 0.0, 0.0, 0.0); 46 } 47 48 [test] 49 draw quad 50 probe all rgba (-1.29496730e+009, 0.0, 0.0, 0.0) 51 52 % These two make sense changed this line in version 2 of the diff
Interesting experiments!
This is making me having two separate hypothesis for SM1:
A) Constant propagation skips casts from int to uint and vice-versa, which explains the result of the second test.
B) Native must be doing something similar to my pass from !671 (merged) 2/3 but before constant propagation. This means that constant folding casts the constants to float before performing the operation, and then casts the result back to the expected type.
So in this test:
[pixel shader fail(sm<4) todo(sm<4)] float4 main() : SV_TARGET { uint x = 3000000000 + 3000000000; return float4(x, 0.0, 0.0, 0.0); } [test] draw quad todo probe all rgba (2.14748365e+009, 0.0, 0.0, 0.0)
- Each
3000000000
is stored asint
, so each overflows to-1294967296
. - In the presence of arithmetic operators, constant folding turns them to float.
- The
+
is performed with floating-point arithmetic resulting in-1294967296.0f - 1294967296.0f = -2589934592.0f
. - The result is turned back to int which in C results in
2147483648
(INT_MAX) because it cannot be represented as int (yep, it is UB).
I am approving the merge request, check my minor comment nitpicks if you got the chance before it gets merged.
- Each
Please disregard my hypothesis B. I tried that and it didn't work. In fact you even mentioned:
% Constant folding is done with uint semantics before converting to float; % so constant folding seems to happen before converting integer operations to floats
but maybe there is a lot more going on, for instance, notice this shader: https://shader-playground.timjones.io/df675e96acb6d16bb77dc0ebdf69dfb2
Constant folding is even preempting catastrophic cancellation.
Okay, I think that what happens is that float constants use double arithmetic! And this is also true for SM4.
This test can be added, albeit I am not sure if in
sm1-const-folding.shader_test
.% Float constants use double arithmetic on all shader models. [pixel shader] float4 main() : SV_TARGET { float x = (200000000000.0f + 1.0f) - 200000000000.0f ; return x; } [test] draw quad probe all rgba (1.0, 1.0, 1.0, 1.0)
I did some additional testing, and it's more complicated than that.
I think I alluded to this elsewhere, but the native compiler has a concept of an "ambiguous" integer type, which is neither int nor uint. You can tell this by doing something like
float func(int x) { return 1; } float func(uint x) { return 2; } float4 main() : SV_TARGET { return func(1); }
Ambiguous ints are automatically promoted when in an expression with other types, and can of course be implicitly cast, but an expression comprised only of ambiguous ints is still ambiguous.
Different integer constants have different types:
uint: 1u, 0x1, 01, 0x0, 4294967295u, 4294967296u, 4294967296lu int: 3000000000, -3000000000, 2147483648, -2147483648 ambiguous: 1, 0, 00, 000, -0, -1, 2147483647, -2147483647, 4294967296l, 1 + 2, 1 / 2 empty: 4294967296
Note "empty": that last token lexes as if it were a space. Fun!
uint adds with overflow, as you'd expect. Int and ambiguous int behaves like an integer type in bounds, but an expression that would overflow (in either direction) yields INT_MIN. Note that, contrary to Francisco's hypothesis, it does not act like a float type: expressions like 2147483645 + 1 do have accuracy to the nearest integer. Also, "1 / 2" equals zero even if converted to float.
Of course the rules are different for sm6; I haven't checked those yet.
What if the ambiguous int type is actually an ambiguous number type -- meaning that it also includes floats and doubles -- and it is represented internally as double? That would explain why there is no loss of accuracy for that value.
Also, "1 / 2" equals zero even if converted to float.
Maybe there is an exception and
/
for ambiguous numbers is always integer division, to be consistent with C.What if the ambiguous int type is actually an ambiguous number type -- meaning that it also includes floats and doubles -- and it is represented internally as double? That would explain why there is no loss of accuracy for that value.
That seems unlikely if overflowing bounds always yields INT_MIN. E.g. "return (2147483647 + 1) - 1;" yields INT_MIN.
Note also that floats have their own "ambiguous" type. 1.0f is float; 1.0h is half; 1.0 is ambiguous.
Zeb, thanks for looking deeper into this. Personally I do not think there is anything urgent for us to do here and I do not plan to spend much more time on this issue, so I would like to know whether you want to upstream yourself some tests which better document your interpretation; if not I'll keep working on this MR (because I think the tests here are valuable, even if likely incomplete), but without necessarily investigating every possible edge case.
added 1 commit
- 4598a871 - tests: Test how constant folding works on SM1.
added 5 commits
-
4598a871...0914561f - 4 commits from branch
wine:master
- 500b61c3 - tests: Test how constant folding works on SM1.
-
4598a871...0914561f - 4 commits from branch