Explorar el Código

[spirv] No temporary variable for local resource variable argument passing (#3721)

```
void getResource(out Texture2D<float4> result) {
  result = global_resource_variable;
}

void main() {
  Texture2D<float4> x;
  getResource(x);
  ...
}
```

SPIR-V backend currently creates a temporary variable to pass `x` as an
argument. In pseudo code:
```
temp_var = x
call getResource temp_var

// use x in main()
```

Since `getResource` will set `temp_var = global_resource_variable`,
we have to use `temp_var` for `x` in `main()`.
However, in `main()`, it always just uses `x`, not `temp_var`.
Therefore, setting `x` as `global_resource_variable` does not work.

This CL lets SPIR-V backend not create a temporary variable for the
local variable with a resource type for argument passing with `out` or
`inout` keyword.

Note that we preserve the behavior of a global variable with a resource type i.e., use a temporary variable. It is because we interpret a local variable or a function parameter with some resource types as a pointer to the resource type while we interpret a global variable with a resource type just as the resource not pointer.
For example, we interpret a local variable or a function parameter with `RWStructuredBuffer<float4>` type as `%_ptr_Function__ptr_StorageBuffer_type_RWStructuredBuffer_v4float` while a global variable with `RWStructuredBuffer<float4>` type as `%_ptr_StorageBuffer_type_RWStructuredBuffer_v4float`. Because of this difference, before we use a local variable or a function parameter with the type, we always dereference it exactly once more than a global variable. If we pass a function argument with `RWStructuredBuffer<float4>` type without a temporary parameter variable, we pass a global variable directly and conduct one more dereferencing that results in the incorrect SPIR-V for both syntax and semantics.
Jaebaek Seo hace 4 años
padre
commit
3de85cffcc

+ 5 - 1
tools/clang/lib/SPIRV/SpirvEmitter.cpp

@@ -2326,6 +2326,10 @@ SpirvInstruction *SpirvEmitter::processCall(const CallExpr *callExpr) {
 
     auto *argInst = doExpr(arg);
 
+    bool isArgGlobalVarWithResourceType =
+        argInfo && argInfo->getStorageClass() != spv::StorageClass::Function &&
+        isResourceType(paramType);
+
     // If argInfo is nullptr and argInst is a rvalue, we do not have a proper
     // pointer to pass to the function. we need a temporary variable in that
     // case.
@@ -2335,7 +2339,7 @@ SpirvInstruction *SpirvEmitter::processCall(const CallExpr *callExpr) {
     // expects are point-to-pointer argument for resources, which will be
     // resolved by legalization.
     if ((argInfo || (argInst && !argInst->isRValue())) &&
-        canActAsOutParmVar(param) && !isResourceType(paramType) &&
+        canActAsOutParmVar(param) && !isArgGlobalVarWithResourceType &&
         paramTypeMatchesArgType(paramType, arg->getType())) {
       // Based on SPIR-V spec, function parameter must be always Function
       // scope. In addition, we must pass memory object declaration argument

+ 57 - 0
tools/clang/test/CodeGenSPIRV/fn.param.inout.global.resource.hlsl

@@ -0,0 +1,57 @@
+// Run: %dxc -E main -T ps_6_0 -fspv-target-env=vulkan1.2
+
+Texture2D<float4>               r0;
+RWTexture3D<float4>             r1;
+SamplerState                    r2;
+RaytracingAccelerationStructure r3;
+RWBuffer<float4>                r4;
+ByteAddressBuffer               r5;
+RWByteAddressBuffer             r6;
+RWStructuredBuffer<float4>      r7;
+AppendStructuredBuffer<float4>  r8;
+
+float4 run(inout    Texture2D<float4>               a0,
+           inout    RWTexture3D<float4>             a1,
+           inout    SamplerState                    a2,
+           inout    RaytracingAccelerationStructure a3,
+           inout    RWBuffer<float4>                a4,
+           inout    ByteAddressBuffer               a5,
+           inout    RWByteAddressBuffer             a6,
+           inout    RWStructuredBuffer<float4>      a7,
+           inout    AppendStructuredBuffer<float4>  a8)
+{
+    float4 pos = a4.Load(0);
+    return a0.Sample(a2, float2(a6.Load(pos.x), a5.Load(pos.y)));
+}
+
+float4 main(): SV_Target
+{
+// CHECK: %param_var_a0 = OpVariable %_ptr_Function_type_2d_image Function
+// CHECK: %param_var_a1 = OpVariable %_ptr_Function_type_3d_image Function
+// CHECK: %param_var_a2 = OpVariable %_ptr_Function_type_sampler Function
+// CHECK: %param_var_a3 = OpVariable %_ptr_Function_accelerationStructureNV Function
+// CHECK: %param_var_a4 = OpVariable %_ptr_Function_type_buffer_image Function
+// CHECK: %param_var_a5 = OpVariable %_ptr_Function__ptr_StorageBuffer_type_ByteAddressBuffer Function
+// CHECK: %param_var_a6 = OpVariable %_ptr_Function__ptr_StorageBuffer_type_RWByteAddressBuffer Function
+// CHECK: %param_var_a7 = OpVariable %_ptr_Function__ptr_StorageBuffer_type_RWStructuredBuffer_v4float Function
+// CHECK: %param_var_a8 = OpVariable %_ptr_Function__ptr_StorageBuffer_type_AppendStructuredBuffer_v4float Function
+
+// CHECK: [[r0:%\w+]] = OpLoad %type_2d_image %r0
+// CHECK:               OpStore %param_var_a0 [[r0]]
+// CHECK: [[r1:%\w+]] = OpLoad %type_3d_image %r1
+// CHECK:               OpStore %param_var_a1 [[r1]]
+// CHECK: [[r2:%\w+]] = OpLoad %type_sampler %r2
+// CHECK:               OpStore %param_var_a2 [[r2]]
+// CHECK: [[r3:%\w+]] = OpLoad %accelerationStructureNV %r3
+// CHECK:               OpStore %param_var_a3 [[r3]]
+// CHECK: [[r4:%\w+]] = OpLoad %type_buffer_image %r4
+// CHECK:               OpStore %param_var_a4 [[r4]]
+// CHECK:               OpStore %param_var_a5 %r5
+// CHECK:               OpStore %param_var_a6 %r6
+// CHECK:               OpStore %param_var_a7 %r7
+// CHECK:               OpStore %param_var_a8 %r8
+
+// CHECK: OpFunctionCall %v4float %run %param_var_a0 %param_var_a1 %param_var_a2 %param_var_a3 %param_var_a4 %param_var_a5 %param_var_a6 %param_var_a7 %param_var_a8
+
+    return run(r0, r1, r2, r3, r4, r5, r6, r7, r8);
+}

+ 51 - 0
tools/clang/test/CodeGenSPIRV/fn.param.inout.local.resource.hlsl

@@ -0,0 +1,51 @@
+// Run: %dxc -E main -T ps_6_0 -fspv-target-env=vulkan1.2
+
+Texture2D<float4>               r0;
+RWTexture3D<float4>             r1;
+SamplerState                    r2;
+RaytracingAccelerationStructure r3;
+RWBuffer<float4>                r4;
+ByteAddressBuffer               r5;
+RWByteAddressBuffer             r6;
+RWStructuredBuffer<float4>      r7;
+AppendStructuredBuffer<float4>  r8;
+
+void getResource(out    Texture2D<float4>               a0,
+                 out    RWTexture3D<float4>             a1,
+                 out    SamplerState                    a2,
+                 out    RaytracingAccelerationStructure a3,
+                 out    RWBuffer<float4>                a4,
+                 out    ByteAddressBuffer               a5,
+                 out    RWByteAddressBuffer             a6,
+                 out    RWStructuredBuffer<float4>      a7,
+                 out    AppendStructuredBuffer<float4>  a8)
+{
+    a0 = r0;
+    a1 = r1;
+    a2 = r2;
+    a3 = r3;
+    a4 = r4;
+    a5 = r5;
+    a6 = r6;
+    a7 = r7;
+    a8 = r8;
+}
+
+float4 main(): SV_Target
+{
+    Texture2D<float4>               x0;
+    RWTexture3D<float4>             x1;
+    SamplerState                    x2;
+    RaytracingAccelerationStructure x3;
+    RWBuffer<float4>                x4;
+    ByteAddressBuffer               x5;
+    RWByteAddressBuffer             x6;
+    RWStructuredBuffer<float4>      x7;
+    AppendStructuredBuffer<float4>  x8;
+
+// CHECK: OpFunctionCall %void %getResource %x0 %x1 %x2 %x3 %x4 %x5 %x6 %x7 %x8
+    getResource(x0, x1, x2, x3, x4, x5, x6, x7, x8);
+
+    float4 pos = x4.Load(0);
+    return x0.Sample(x2, float2(x6.Load(pos.x), x5.Load(pos.y)));
+}

+ 0 - 21
tools/clang/test/CodeGenSPIRV/fn.param.inout.resource.hlsl

@@ -1,21 +0,0 @@
-// Run: %dxc -E main -T cs_6_0
-
-RWStructuredBuffer<int> testrwbuf : register(u0);
-
-void testfn(uint index, uint value, inout RWStructuredBuffer<int> buf);
-
-[numthreads(1, 1, 1)]
-void main(uint3 GroupId          : SV_GroupID,
-          uint3 DispatchThreadId : SV_DispatchThreadID)
-{
-// CHECK: %param_var_buf = OpVariable %_ptr_Function__ptr_Uniform_type_RWStructuredBuffer_int Function
-// CHECK:       {{%\d+}} = OpFunctionCall %void %testfn %param_var_index %param_var_value %param_var_buf
-  testfn(GroupId.x, DispatchThreadId.x, testrwbuf);
-}
-
-// CHECK:   %buf = OpFunctionParameter %_ptr_Function__ptr_Uniform_type_RWStructuredBuffer_int
-void testfn(uint index, uint value, inout RWStructuredBuffer<int> buf) {
-  buf[index] = value;
-}
-
-

+ 6 - 2
tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp

@@ -558,9 +558,13 @@ TEST_F(FileTest, FunctionInOutParamVector) {
   setBeforeHLSLLegalization();
   runFileTest("fn.param.inout.vector.hlsl");
 }
-TEST_F(FileTest, FunctionInOutParamResource) {
+TEST_F(FileTest, FunctionInOutParamGlobalResource) {
   setBeforeHLSLLegalization();
-  runFileTest("fn.param.inout.resource.hlsl");
+  runFileTest("fn.param.inout.global.resource.hlsl");
+}
+TEST_F(FileTest, FunctionInOutParamLocalResource) {
+  setBeforeHLSLLegalization();
+  runFileTest("fn.param.inout.local.resource.hlsl");
 }
 TEST_F(FileTest, FunctionInOutParamDiffStorageClass) {
   setBeforeHLSLLegalization();