Instead of trying to precalculate GEP offsets ahead of time and then process resource accesses based off of these offsets, traverse the GEP chain inline for each access. This makes it easier to get the types correct when translating GEPs for cbuffer and structured buffer accesses, which in turn lets us access individual elements of those structures directly. Fixes #160208, #164517, and #169430
130 lines
4.9 KiB
LLVM
130 lines
4.9 KiB
LLVM
; RUN: opt -S -dxil-resource-access %s | FileCheck %s
|
|
|
|
target triple = "dxil-pc-shadermodel6.4-compute"
|
|
|
|
; struct S {
|
|
; double d;
|
|
; uint4 v;
|
|
; };
|
|
%struct.S = type <{ double, <4 x i32> }>
|
|
|
|
; struct T {
|
|
; uint x;
|
|
; S s[16];
|
|
; uint y;
|
|
; };
|
|
%struct.T = type { i32, [16 x %struct.S], i32 }
|
|
|
|
declare void @i32_user(i32)
|
|
declare void @double_user(double)
|
|
declare void @half_user(half)
|
|
|
|
; CHECK-LABEL: define void @load_offset_i32
|
|
define void @load_offset_i32(i32 %idx) {
|
|
%buffer = call target("dx.RawBuffer", %struct.S, 0, 0)
|
|
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
|
|
|
|
;; StructuredBuffer<S> In;
|
|
;; In[0].v[idx];
|
|
;
|
|
; Here we use a GEP to access the vector, even though that isn't necessarily
|
|
; what the clang frontend will codegen.
|
|
;
|
|
; CHECK: [[MUL:%.*]] = mul i32 %idx, 4
|
|
; CHECK: [[ADD:%.*]] = add i32 [[MUL]], 8
|
|
; CHECK: [[LOAD:%.*]] = call { i32, i1 } @llvm.dx.resource.load.rawbuffer.i32.tdx.RawBuffer_s_struct.Ss_0_0t(target("dx.RawBuffer", %struct.S, 0, 0) %buffer, i32 0, i32 [[ADD]])
|
|
; CHECK: [[VAL:%.*]] = extractvalue { i32, i1 } [[LOAD]], 0
|
|
; CHECK: call void @i32_user(i32 [[VAL]])
|
|
%ptr = call ptr @llvm.dx.resource.getpointer(
|
|
target("dx.RawBuffer", %struct.S, 0, 0) %buffer, i32 0)
|
|
%s.i = getelementptr inbounds nuw i8, ptr %ptr, i32 8
|
|
%v.i = getelementptr i32, ptr %s.i, i32 %idx
|
|
%elt = load i32, ptr %v.i
|
|
call void @i32_user(i32 %elt)
|
|
|
|
;; StructuredBuffer<S> In;
|
|
;; In[0].v[idx];
|
|
;
|
|
; This matches clang's codegen, using extractelement rather than a gep.
|
|
;
|
|
; CHECK: [[LOAD:%.*]] = call { <4 x i32>, i1 } @llvm.dx.resource.load.rawbuffer.v4i32.tdx.RawBuffer_s_struct.Ss_0_0t(target("dx.RawBuffer", %struct.S, 0, 0) %buffer, i32 0, i32 8)
|
|
; CHECK: [[VEC:%.*]] = extractvalue { <4 x i32>, i1 } [[LOAD]], 0
|
|
; CHECK: [[VAL:%.*]] = extractelement <4 x i32> [[VEC]], i32 %idx
|
|
; CHECK: call void @i32_user(i32 [[VAL]])
|
|
%ptr2 = call ptr @llvm.dx.resource.getpointer(
|
|
target("dx.RawBuffer", %struct.S, 0, 0) %buffer, i32 0)
|
|
%v2.i = getelementptr inbounds nuw i8, ptr %ptr, i32 8
|
|
%v2 = load <4 x i32>, ptr %v2.i
|
|
%elt2 = extractelement <4 x i32> %v2, i32 %idx
|
|
call void @i32_user(i32 %elt2)
|
|
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: define void @load_double
|
|
define void @load_double(i32 %idx) {
|
|
%buffer = call target("dx.RawBuffer", <4 x double>, 0, 0)
|
|
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
|
|
|
|
;; StructuredBuffer<double4> In;
|
|
;; In[0][idx];
|
|
;
|
|
; CHECK: [[MUL:%.*]] = mul i32 %idx, 8
|
|
; CHECK: [[LOAD:%.*]] = call { double, i1 } @llvm.dx.resource.load.rawbuffer.f64.tdx.RawBuffer_v4f64_0_0t(target("dx.RawBuffer", <4 x double>, 0, 0) %buffer, i32 0, i32 [[MUL]])
|
|
; CHECK: [[VAL:%.*]] = extractvalue { double, i1 } [[LOAD]], 0
|
|
; CHECK: call void @double_user(double [[VAL]])
|
|
%ptr = call ptr @llvm.dx.resource.getpointer(
|
|
target("dx.RawBuffer", <4 x double>, 0, 0) %buffer, i32 0)
|
|
%v.i = getelementptr double, ptr %ptr, i32 %idx
|
|
%v = load double, ptr %v.i
|
|
call void @double_user(double %v)
|
|
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: define void @load_half
|
|
define void @load_half(i32 %idx) {
|
|
%buffer = call target("dx.RawBuffer", <4 x half>, 0, 0)
|
|
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
|
|
|
|
;; StructuredBuffer<half4> In;
|
|
;; In[0][idx];
|
|
;
|
|
; CHECK: [[MUL:%.*]] = mul i32 %idx, 2
|
|
; CHECK: [[LOAD:%.*]] = call { half, i1 } @llvm.dx.resource.load.rawbuffer.f16.tdx.RawBuffer_v4f16_0_0t(target("dx.RawBuffer", <4 x half>, 0, 0) %buffer, i32 0, i32 [[MUL]])
|
|
; CHECK: [[VAL:%.*]] = extractvalue { half, i1 } [[LOAD]], 0
|
|
; CHECK: call void @half_user(half [[VAL]])
|
|
%ptr = call ptr @llvm.dx.resource.getpointer(
|
|
target("dx.RawBuffer", <4 x half>, 0, 0) %buffer, i32 0)
|
|
%v.i = getelementptr half, ptr %ptr, i32 %idx
|
|
%v = load half, ptr %v.i
|
|
call void @half_user(half %v)
|
|
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: define void @load_nested
|
|
define void @load_nested(i32 %idx, i32 %arrayidx, i32 %vecidx) {
|
|
%buffer = call target("dx.RawBuffer", %struct.T, 0, 0)
|
|
@llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
|
|
|
|
;; StructuredBuffer<T> In;
|
|
;; In[idx].s[arrayidx].v[vecidx];
|
|
;
|
|
; CHECK: [[MUL:%.*]] = mul i32 %arrayidx, 24
|
|
; CHECK: [[ADD:%.*]] = add i32 12, [[MUL]]
|
|
; CHECK: [[LOAD:%.*]] = call { <4 x i32>, i1 } @llvm.dx.resource.load.rawbuffer.v4i32.tdx.RawBuffer_s_struct.Ts_0_0t(target("dx.RawBuffer", %struct.T, 0, 0) %buffer, i32 %idx, i32 [[ADD]])
|
|
; CHECK: [[VEC:%.*]] = extractvalue { <4 x i32>, i1 } [[LOAD]], 0
|
|
; CHECK: [[VAL:%.*]] = extractelement <4 x i32> [[VEC]], i32 %vecidx
|
|
; CHECK: call void @i32_user(i32 [[VAL]])
|
|
%ptr = call ptr @llvm.dx.resource.getpointer(
|
|
target("dx.RawBuffer", %struct.T, 0, 0) %buffer, i32 %idx)
|
|
%s.i = getelementptr inbounds nuw %struct.S, ptr %ptr, i32 %arrayidx
|
|
%v.i = getelementptr inbounds nuw i8, ptr %s.i, i32 12
|
|
%v = load <4 x i32>, ptr %v.i
|
|
%elt = extractelement <4 x i32> %v, i32 %vecidx
|
|
call void @i32_user(i32 %elt)
|
|
|
|
ret void
|
|
}
|