From d6a2d2905125715629fe8972a374136189d0c9ef Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 10 Sep 2020 14:12:43 -0700 Subject: Add a pass to support resource return values (#1537) A long-standing problem for the Slang implementation has been that some targets (notably GLSL/SPIR-V) do not support treating resources (textures, buffers, samplers, etc.) as first-class types. Resource types on such platforms are restricted so that they may not be used as the type of: 1. fields of aggregate types (`struct`s) 2. local variables 3. function results or `out`/`inout` parameters Issue (1) is handled by our "type legalization" pass today, by splitting aggregates that contain resources into separate fields/variables/parameters. Issue (2) is worked around by putting code into SSA form and promoting local variables to SSA temporaries when possible; the net result is that many local variables of texture type are eliminated (that pass is not perfect, though, and it is possible for users to get errors when it doesn't fully clean up local variables of texture type). Issue (3) is a much more complicated matter, and it is what this change is concerned with. A typical solution to issue (3) is to simply inline all of the code in a program, at which point function results and `out`/`inout` parameters will no longer exist to cause problems. We reject such solutions for two reasons. First, there are limitations on control-flow structure in HLSL/GLSL/SPIR-V that mean they cannot express certain programs after inlining has been performed. Second, and more importantly, the philosophy of the Slang compiler is to perform as little duplication of code as possible, so that we do not accidentally contribute to binary size bloat. Instead, this change tackles the problem of functions that output resource types by adding a new specialization pass. The pass detects functions that ought to be specialized (because they have resource-type outputs), and inspects their bodies to see if the values they output have a predicatable structure that can be replicated outside of the function body. The same logic that inspects the function body also rewrites (a copy of) the function to not have the offending outputs. Finally, all the call sites to a function that is rewritten in this way also get rewritten so that instead of using output values from the function itself, they reproduce the expected output value(s) in their own code. The pass as presented here is intentionally limited in the scope of what it can optimize away (and the test case only touches on that specific functionality). The goal is to get a basic version of this pass in place and evaluated, and then to expand on its functionality incrementally over time. --- .../func-resource-result-simple.slang | 31 ++++++++++++++++++++++ .../func-resource-result-simple.slang.expected.txt | 4 +++ 2 files changed, 35 insertions(+) create mode 100644 tests/optimization/func-resource-result/func-resource-result-simple.slang create mode 100644 tests/optimization/func-resource-result/func-resource-result-simple.slang.expected.txt (limited to 'tests/optimization') diff --git a/tests/optimization/func-resource-result/func-resource-result-simple.slang b/tests/optimization/func-resource-result/func-resource-result-simple.slang new file mode 100644 index 000000000..5bebd69ac --- /dev/null +++ b/tests/optimization/func-resource-result/func-resource-result-simple.slang @@ -0,0 +1,31 @@ +// func-resource-result-simple.slang + +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute +//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute + +// Test that a function that returns a resource type can be +// compiled for targets that don't natively support resource +// return values. + +//TEST_INPUT:ubuffer(data=[0x11 0x22 0x33 0x44], stride=4):name=inputBuffer +RWStructuredBuffer inputBuffer; + +RWStructuredBuffer getInputBuffer() { return inputBuffer; } + +int test(int val) +{ + return getInputBuffer()[val]; +} + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + uint tid = dispatchThreadID.x; + int inVal = tid; + int outVal = test(inVal); + outputBuffer[tid] = outVal; +} diff --git a/tests/optimization/func-resource-result/func-resource-result-simple.slang.expected.txt b/tests/optimization/func-resource-result/func-resource-result-simple.slang.expected.txt new file mode 100644 index 000000000..e4511c4c7 --- /dev/null +++ b/tests/optimization/func-resource-result/func-resource-result-simple.slang.expected.txt @@ -0,0 +1,4 @@ +11 +22 +33 +44 -- cgit v1.2.3