diff options
| author | Yong He <yonghe@outlook.com> | 2025-09-29 17:45:08 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-30 00:45:08 +0000 |
| commit | a6deb5ed82cb8fc6b4f4c5c5fee264e09f97ff89 (patch) | |
| tree | 1c374bd52498cad2e142e3c7f5482fd42dca966f /tests/optimization | |
| parent | 2827c94de5901cac42a67f73a78ab2548771b28c (diff) | |
Rewriting the lower-buffer-element-type pass to avoid unnecessary packing/unpacking. (#8526)
Part of the effort to improve the performance of generated SPIRV code.
The existing lower-buffer-element-type pass works by loading the entire
buffer element content from memory, and translate it to logical type
stored in a local variable at the earliest reference of a buffer handle.
This means that is can generate inefficient code that reads more than
necessary.
Consider this example:
```
struct BigStruct { bool values[1024]; }
ConstantBuffer<BigStruct> cb;
void test(BigStruct v)
{
if (v.values[0]) { printf("ok"); }
}
[numthreads(1,1,1)]
void computeMain()
{
test(cb);
}
```
In IR, the `computeMain` function before lower-buffer-element-type pass
is something like following:
```
func test:
%v = param : BigStruct
%barr = fieldExtract(%v, "values")
%element = elementExtract(%barr, 0)
... // uses %element
func computeMain:
%v = load(cb)
call %test %v
```
The existing lower-buffer-element-type pass will rewrite the bool array
in `BigStruct` into `int` array so it is legal in SPIRV. However, it
does so by inserting the translation on the first `load` of the constant
buffer:
```
struct BigStruct_std430 {
int values[1024];
}
var cb : ConstantBuffer<BigStruct_std430>;
func computeMain:
%tmpVar : var<BigStruct>
call %unpackStorage(%tmpVar, cb)
%v : BigStruct = load %tmpVar
call %test %v
```
This means that the entire array will be loaded and translated to int,
before calling `test`, which only uses one element. It turns out that
the downstream compiler isn't always able to optimize out this
inefficient translation/copy.
This PR completely rewrites the way buffer-element-type lowering is
handled to avoid producing this inefficient code. It works in two parts:
first we turn on the `transformParamsToConstRef` pass for SPIRV target
as well, so we will translate the `test` function to take the `v`
parameter as `constref`. The second part is a redesigned
buffer-element-type pass that defers the storage-type to logical-type
translation until a value is actually used by a `load` instruction.
In this example, after `transformParamsToConstRef`, the IR is:
```
func test:
%v = param : ConstRef<BigStruct>
%barr = fieldAddr(%v, "values")
%elementPtr = elementAddr(%barr, 0)
%element = load(%elementPtr)
... // uses %element
func computeMain:
call %test %cb
```
The new `buffer-element-type-lowering` pass will take this IR, and
insert translation at latest possible time across the entire call graph,
and translate the IR into:
```
func test:
%v = param : ConstRef<BigStruct_std430>
%barr = fieldAddr(%v, "values")
%elementPtr : ptr<int> = elementAddr(%barr, 0)
%element_int = load(%elementPtr)
%element = cast(%element_int) : %bool
... // uses %element
func computeMain:
call %test %cb
```
In this new IR, there is no longer a load and conversion of the entire
array.
See new comment in `slang-ir-lower-buffer-element-type.cpp` for more
details of how the pass works.
This PR also address many other issues surfaced by turning on
`transformParamsToConstRef` pass on SPIRV backend.
---------
Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'tests/optimization')
| -rw-r--r-- | tests/optimization/arrray-storage-lowering.slang | 42 | ||||
| -rw-r--r-- | tests/optimization/get-array-element.slang | 17 |
2 files changed, 59 insertions, 0 deletions
diff --git a/tests/optimization/arrray-storage-lowering.slang b/tests/optimization/arrray-storage-lowering.slang new file mode 100644 index 000000000..42bb8f127 --- /dev/null +++ b/tests/optimization/arrray-storage-lowering.slang @@ -0,0 +1,42 @@ +// TEST:SIMPLE(filecheck=SPV): -target spirv + +// TEST(compute, vulkan):COMPARE_COMPUTE_EX(filecheck-buffer=CHECK):-vk -compute -shaderobj -output-using-type -emit-spirv-directly + +struct DoubleNested +{ + int4x3 matrix; + int getMatVal(int i, int j) { return matrix[i][j]; } +} + +struct Nested +{ + bool values[4]; + DoubleNested doubleNested; + int getVal(int id) { return (int)values[0] + doubleNested.getMatVal(0, 1); } +} + +struct Params +{ + Nested nested; + + int getVal(int id) { return nested.getVal(id) + nested.getVal(id + 1); } +} + +// TEST_INPUT: set outputBuffer = out ubuffer(data=[0], stride=4) +RWStructuredBuffer<int4> outputBuffer; + +// TEST_INPUT:set gParams = cbuffer(data=[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]) +ConstantBuffer<Params> gParams; + +// TEST_INPUT: set gDoubleNested = ubuffer(data=[1 2 3 4 5 6 7 8 9 10 11 12]) +uniform DoubleNested *gDoubleNested; + +// CHECK: 9 + +[numthreads(1,1,1)] +void computeMain(int id: SV_DispatchThreadID) +{ + outputBuffer[0].xyz = gParams.getVal(id) + gDoubleNested.getMatVal(1, 1); +} + +// SPV-NOT: OpCompositeConstruct diff --git a/tests/optimization/get-array-element.slang b/tests/optimization/get-array-element.slang new file mode 100644 index 000000000..16a71aee2 --- /dev/null +++ b/tests/optimization/get-array-element.slang @@ -0,0 +1,17 @@ +//TEST:SIMPLE(filecheck=CHECK):-target spirv + +int test(int arr[32]) { + int sum = 0; + for (int i =0; i < 32; i++) sum += arr[i]; + return sum; +} + +uniform int gArr[32]; +uniform int* result; + +[numthreads(1,1,1)] +void computeMain() +{ + // CHECK-NOT: OpCompositeConstruct + *result = test(gArr); +}
\ No newline at end of file |
