This change resolves handles (or corresponding ptr) that all point into a unique global resource by propagating an index into that global resource through control flow. If a unique global resource can't be resolved, an error is reported instead. This specifically resolves all handles that point into the same global resource array. Resolves: https://github.com/llvm/llvm-project/issues/165288 By reporting an error, this is part of resolving https://github.com/llvm/llvm-project/issues/179303.
118 lines
7.0 KiB
LLVM
118 lines
7.0 KiB
LLVM
; RUN: opt -S -dxil-resource-type -dxil-resource-access -disable-verify \
|
|
; RUN: -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
|
|
|
|
; The file contains examples of hlsl snippets that will generate invalid
|
|
; looking resource access, either through code-gen or by optimization.
|
|
; These can be legalized by replacing the handles with indicies into the
|
|
; same global resource.
|
|
|
|
; NOTE: The below resources are generated with:
|
|
;
|
|
; RWBuffer<int> In : register(u0);
|
|
; RWStructuredBuffer<int> Out0 : register(u1);
|
|
; RWStructuredBuffer<int> Out1 : register(u2);
|
|
; RWStructuredBuffer<int> OutArr[];
|
|
|
|
; cbuffer c {
|
|
; bool cond;
|
|
; };
|
|
|
|
%__cblayout_c = type <{ i32 }>
|
|
|
|
@.str = internal unnamed_addr constant [3 x i8] c"In\00", align 1
|
|
@.str.2 = internal unnamed_addr constant [5 x i8] c"Out0\00", align 1
|
|
@.str.3 = internal unnamed_addr constant [5 x i8] c"Out1\00", align 1
|
|
@c.cb = local_unnamed_addr global target("dx.CBuffer", %__cblayout_c) poison
|
|
@c.str = internal unnamed_addr constant [2 x i8] c"c\00", align 1
|
|
@OutArr.str = internal unnamed_addr constant [7 x i8] c"OutArr\00", align 1
|
|
|
|
; Local select into global resource array:
|
|
;
|
|
; RWStructuredBuffer<int> Out = cond ? OutArr[0] : OutArr[1];
|
|
; Out[GI] = WaveActiveMax(In[GI]);
|
|
;
|
|
; This will ensure that the index is propogated through phi branching
|
|
; correctly. We see that two different handles are generated in the different
|
|
; branches, so we will ensure that the handle generation is removed from
|
|
; the branches and that the `phi i32` node is used to distinguish the index.
|
|
; Then that index is used to generate the handle.
|
|
;
|
|
; CHECK-LABEL: @select_global_resource_array()
|
|
define void @select_global_resource_array() {
|
|
entry:
|
|
%c.cb_h.i.i = tail call target("dx.CBuffer", %__cblayout_c) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_cst(i32 4, i32 0, i32 1, i32 0, ptr nonnull @c.str)
|
|
store target("dx.CBuffer", %__cblayout_c) %c.cb_h.i.i, ptr @c.cb, align 4
|
|
%c.cb = load target("dx.CBuffer", %__cblayout_c), ptr @c.cb, align 4
|
|
%0 = call ptr addrspace(2) @llvm.dx.resource.getpointer.p2.tdx.CBuffer_s___cblayout_cst(target("dx.CBuffer", %__cblayout_c) %c.cb, i32 0)
|
|
%1 = load i32, ptr addrspace(2) %0, align 4
|
|
%loadedv.i = trunc nuw i32 %1 to i1
|
|
br i1 %loadedv.i, label %cond.true.i, label %cond.false.i
|
|
|
|
cond.true.i:
|
|
; CHECK: cond.true.i:
|
|
; CHECK-NEXT: br label %cond.end.i
|
|
%2 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 0, ptr nonnull @OutArr.str)
|
|
br label %cond.end.i
|
|
|
|
cond.false.i:
|
|
; CHECK: cond.false.i:
|
|
; CHECK-NEXT: br label %cond.end.i
|
|
%3 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 1, ptr nonnull @OutArr.str)
|
|
br label %cond.end.i
|
|
|
|
cond.end.i:
|
|
; CHECK: cond.end.i
|
|
; CHECK-NEXT: %[[HANDLE_IDX:.*]] = phi i32 [ 0, %cond.true.i ], [ 1, %cond.false.i ]
|
|
; CHECK: %[[TID:.*]] = tail call i32 @llvm.dx.flattened.thread.id.in.group()
|
|
; CHECK: %[[WAVE_MAX:.*]] = tail call i32 @llvm.dx.wave.reduce.max.i32(i32 %{{.*}})
|
|
; CHECK-NEXT: %[[HANDLE:.*]] = tail call target("dx.RawBuffer", i32, 1, 0)
|
|
; CHECK-SAME: @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 %[[HANDLE_IDX]], ptr nonnull @OutArr.str)
|
|
; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) %[[HANDLE]], i32 %[[TID]], i32 0, i32 %[[WAVE_MAX]])
|
|
; CHECK-NEXT: ret void
|
|
%cond.i.sroa.speculated = phi target("dx.RawBuffer", i32, 1, 0) [ %2, %cond.true.i ], [ %3, %cond.false.i ]
|
|
%4 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
|
|
%5 = tail call i32 @llvm.dx.flattened.thread.id.in.group()
|
|
%6 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %4, i32 %5)
|
|
%7 = load i32, ptr %6, align 4
|
|
%hlsl.wave.active.max.i = tail call i32 @llvm.dx.wave.reduce.max.i32(i32 %7)
|
|
%8 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0) %cond.i.sroa.speculated, i32 %5)
|
|
store i32 %hlsl.wave.active.max.i, ptr %8, align 4
|
|
ret void
|
|
}
|
|
|
|
; Using a local array of global resources
|
|
;
|
|
; RWStructuredBuffer<int> Outs[2] = {OutArr[0], OutArr[1]};
|
|
; Outs[cond ? 0 : 1][GI] = In[GI];
|
|
;
|
|
; This will ensure that the index is propogated through the select of two
|
|
; resources. So we want to check that the `select i1` is based on the handle
|
|
; indices, and that this index is used to generate the handle.
|
|
;
|
|
; CHECK-LABEL: @local_array_of_global_resources()
|
|
define void @local_array_of_global_resources() {
|
|
entry:
|
|
%0 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
|
|
%c.cb_h.i.i = tail call target("dx.CBuffer", %__cblayout_c) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_cst(i32 4, i32 0, i32 1, i32 0, ptr nonnull @c.str)
|
|
store target("dx.CBuffer", %__cblayout_c) %c.cb_h.i.i, ptr @c.cb, align 4
|
|
%1 = tail call i32 @llvm.dx.flattened.thread.id.in.group()
|
|
%2 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 0, ptr nonnull @OutArr.str)
|
|
%3 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 1, ptr nonnull @OutArr.str)
|
|
%4 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %0, i32 %1)
|
|
%5 = load i32, ptr %4, align 4
|
|
%c.cb = load target("dx.CBuffer", %__cblayout_c), ptr @c.cb, align 4
|
|
%6 = call ptr addrspace(2) @llvm.dx.resource.getpointer.p2.tdx.CBuffer_s___cblayout_cst(target("dx.CBuffer", %__cblayout_c) %c.cb, i32 0)
|
|
%7 = load i32, ptr addrspace(2) %6, align 4
|
|
%loadedv.i = trunc nuw i32 %7 to i1
|
|
|
|
; CHECK: %[[TID:.*]] = tail call i32 @llvm.dx.flattened.thread.id.in.group()
|
|
; CHECK: %[[HANDLE_IDX:.*]] = select i1 %loadedv.i, i32 0, i32 1
|
|
; CHECK-NEXT: %[[HANDLE:.*]] = tail call target("dx.RawBuffer", i32, 1, 0)
|
|
; CHECK-SAME: @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 %[[HANDLE_IDX]], ptr nonnull @OutArr.str)
|
|
; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) %[[HANDLE]], i32 %[[TID]], i32 0, i32 {{.*}})
|
|
%.sroa.speculated = select i1 %loadedv.i, target("dx.RawBuffer", i32, 1, 0) %2, target("dx.RawBuffer", i32, 1, 0) %3
|
|
%8 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0) %.sroa.speculated, i32 %1)
|
|
store i32 %5, ptr %8, align 4
|
|
ret void
|
|
}
|