https://github.com/bogner created 
https://github.com/llvm/llvm-project/pull/179768

We weren't accounting for skipped fields correctly when emitting struct member 
exprs, which could lead to us reading padding instead of the member itself when 
a struct followed an array.

Fixes #179716

>From 845dfdf59ee290fd7989b40a130cb2746be2be16 Mon Sep 17 00:00:00 2001
From: Justin Bogner <[email protected]>
Date: Mon, 2 Feb 2026 17:08:20 -0800
Subject: [PATCH] [HLSL] Fix miscompile of small structs after arrays in
 cbuffers

We weren't accounting for skipped fields correctly when emitting struct
member exprs, which could lead to us reading padding instead of the
member itself when a struct followed an array.

Fixes #179716
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 19 ++++++++++++++++---
 .../CodeGenHLSL/resources/cbuffer_geps.hlsl   |  6 +++---
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index cff2824d29dc5..985bc8640118e 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1556,10 +1556,7 @@ LValue 
CGHLSLRuntime::emitBufferMemberExpr(CodeGenFunction &CGF,
   auto *Field = dyn_cast<FieldDecl>(E->getMemberDecl());
   assert(Field && "Unexpected access into HLSL buffer");
 
-  // Get the field index for the struct.
   const RecordDecl *Rec = Field->getParent();
-  unsigned FieldIdx =
-      CGM.getTypes().getCGRecordLayout(Rec).getLLVMFieldNo(Field);
 
   // Work out the buffer layout type to index into.
   QualType RecType = CGM.getContext().getCanonicalTagType(Rec);
@@ -1571,6 +1568,22 @@ LValue 
CGHLSLRuntime::emitBufferMemberExpr(CodeGenFunction &CGF,
   llvm::StructType *LayoutTy = HLSLBufferLayoutBuilder(CGM).layOutStruct(
       RecType->getAsCanonical<RecordType>(), EmptyOffsets);
 
+  // Get the field index for the layout struct, accounting for padding.
+  unsigned FieldIdx =
+      CGM.getTypes().getCGRecordLayout(Rec).getLLVMFieldNo(Field);
+  assert(FieldIdx < LayoutTy->getNumElements() &&
+         "Layout struct is smaller than member struct");
+  unsigned Skipped = 0;
+  for (unsigned I = 0; I <= FieldIdx;) {
+    llvm::Type *ElementTy = LayoutTy->getElementType(I + Skipped);
+    if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(ElementTy))
+      ++Skipped;
+    else
+      ++I;
+  }
+  FieldIdx += Skipped;
+  assert(FieldIdx < LayoutTy->getNumElements() && "Access out of bounds");
+
   // Now index into the struct, making sure that the type we return is the
   // buffer layout type rather than the original type in the AST.
   QualType FieldType = Field->getType();
diff --git a/clang/test/CodeGenHLSL/resources/cbuffer_geps.hlsl 
b/clang/test/CodeGenHLSL/resources/cbuffer_geps.hlsl
index 7a0b45875faf9..657694aa90fd7 100644
--- a/clang/test/CodeGenHLSL/resources/cbuffer_geps.hlsl
+++ b/clang/test/CodeGenHLSL/resources/cbuffer_geps.hlsl
@@ -73,7 +73,7 @@ void cbstructs() {
   use(s1.a1);
   // CHECK: load <3 x i16>, ptr addrspace(2) getelementptr inbounds nuw (%B, 
ptr addrspace(2) @s2, i32 0, i32 1), align 2
   use(s2.b1);
-  // CHECK: load <2 x float>, ptr addrspace(2) getelementptr inbounds nuw (%C, 
ptr addrspace(2) @s3, i32 0, i32 1), align 8
+  // CHECK: load <2 x float>, ptr addrspace(2) getelementptr inbounds nuw (%C, 
ptr addrspace(2) @s3, i32 0, i32 2), align 8
   use(s3.c2.a1);
   // CHECK: load <2 x float>, ptr addrspace(2) getelementptr (<{ %A, 
target("dx.Padding", 8) }>, ptr addrspace(2) @s4, i32 2, i32 0), align 8
   use(s4[2].a1);
@@ -108,9 +108,9 @@ void cbmix() {
   use(m3.y);
   // CHECK: load <2 x float>, ptr addrspace(2) getelementptr (<{ <2 x float>, 
target("dx.Padding", 8) }>, ptr addrspace(2) getelementptr (<{ <{ [3 x <{ <2 x 
float>, target("dx.Padding", 8) }>], <2 x float> }>, target("dx.Padding", 8) 
}>, ptr addrspace(2) @m4, i32 2, i32 0), i32 3, i32 0), align 16
   use(m4[2][3]);
-  // CHECK: load <4 x i32>, ptr addrspace(2) getelementptr inbounds nuw 
([[ANON_1]], ptr addrspace(2) @m5, i32 0, i32 1), align 16
+  // CHECK: load <4 x i32>, ptr addrspace(2) getelementptr inbounds nuw 
([[ANON_1]], ptr addrspace(2) @m5, i32 0, i32 2), align 16
   use(m5.d);
-  // CHECK: load <4 x i32>, ptr addrspace(2) getelementptr inbounds ([5 x <4 x 
i32>], ptr addrspace(2) getelementptr (<{ %ArrayAndScalar, target("dx.Padding", 
12) }>, ptr addrspace(2) getelementptr inbounds nuw ([[ANON_2]], ptr 
addrspace(2) @m6, i32 0, i32 1), i32 2, i32 0), i32 0, i32 2), align 16
+  // CHECK: load <4 x i32>, ptr addrspace(2) getelementptr inbounds ([5 x <4 x 
i32>], ptr addrspace(2) getelementptr (<{ %ArrayAndScalar, target("dx.Padding", 
12) }>, ptr addrspace(2) getelementptr inbounds nuw ([[ANON_2]], ptr 
addrspace(2) @m6, i32 0, i32 2), i32 2, i32 0), i32 0, i32 2), align 16
   use(m6.j[2].x[2]);
   // CHECK: load <1 x double>, ptr addrspace(2) @m7, align 8
   use(m7);

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to