blob: 85e8bbb5db8fb35efddf951e1faa499d3f518018 [file] [log] [blame] [edit]
; 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
}