Justin Bogner c3039a7dc5
[DirectX] Avoid precalculating GEPs in DXILResourceAccess (#172720)
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
2025-12-18 22:15:12 +00:00

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
}