Skip to content

Conversation

@V-FEXrt
Copy link
Contributor

@V-FEXrt V-FEXrt commented Feb 20, 2025

Fixes #119641

Update SemaHLSL to correctly calculate the alignment barrier for scalars that are not 4 bytes wide

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" HLSL HLSL Language Support labels Feb 20, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 20, 2025

@llvm/pr-subscribers-hlsl

Author: Ashley Coleman (V-FEXrt)

Changes

Fixes #119641

Update SemaHLSL to correctly calculate the alignment barrier for scalars that are not 4 bytes wide


Full diff: https://github.com/llvm/llvm-project/pull/128086.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaHLSL.cpp (+22-5)
  • (added) clang/test/CodeGenHLSL/cbuffer_align.hlsl (+60)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d26d85d5861b1..cccfd4aec7eb2 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -172,6 +172,27 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
   return Result;
 }
 
+static unsigned calculateLegacyCbufferFieldAlign(const ASTContext &Context,
+                                                 QualType T) {
+  // Aggregate types are always aligned to new buffer rows
+  if (T->isAggregateType())
+    return 16;
+
+  assert(Context.getTypeSize(T) <= 64 &&
+         "Scalar bit widths larger than 64 not supported");
+
+  // 64 bit types such as double and uint64_t align to 8 bytes
+  if (Context.getTypeSize(T) == 64)
+    return 8;
+
+  // Half types align to 2 bytes only if native half is available
+  if (T->isHalfType() && Context.getLangOpts().NativeHalfType)
+    return 2;
+
+  // Everything else aligns to 4 bytes
+  return 4;
+}
+
 // Calculate the size of a legacy cbuffer type in bytes based on
 // https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
 static unsigned calculateLegacyCbufferSize(const ASTContext &Context,
@@ -183,11 +204,7 @@ static unsigned calculateLegacyCbufferSize(const ASTContext &Context,
     for (const FieldDecl *Field : RD->fields()) {
       QualType Ty = Field->getType();
       unsigned FieldSize = calculateLegacyCbufferSize(Context, Ty);
-      // FIXME: This is not the correct alignment, it does not work for 16-bit
-      // types. See llvm/llvm-project#119641.
-      unsigned FieldAlign = 4;
-      if (Ty->isAggregateType())
-        FieldAlign = CBufferAlign;
+      unsigned FieldAlign = calculateLegacyCbufferFieldAlign(Context, Ty);
       Size = llvm::alignTo(Size, FieldAlign);
       Size += FieldSize;
     }
diff --git a/clang/test/CodeGenHLSL/cbuffer_align.hlsl b/clang/test/CodeGenHLSL/cbuffer_align.hlsl
new file mode 100644
index 0000000000000..31db2e0726020
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_align.hlsl
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s -fnative-half-type \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-HALF
+
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-FLOAT
+
+
+struct S1 {
+  half a;   // 2 bytes + 2 bytes pad or 4 bytes
+  float b;  // 4 bytes
+  half c;   // 2 bytes + 2 bytes pad or 4 bytes
+  float d;  // 4 bytes
+  double e; // 8 bytes
+};
+
+struct S2 {
+  half a;   // 2 bytes or 4 bytes
+  half b;   // 2 bytes or 4 bytes
+  float e;  // 4 bytes or 4 bytes + 4 padding
+  double f; // 8 bytes
+};
+
+struct S3 {
+  half a;     // 2 bytes + 6 bytes pad or 4 bytes + 4 bytes pad
+  uint64_t b; // 8 bytes
+};
+
+struct S4 {
+  float a;  // 4 bytes
+  half b;   // 2 bytes or 4 bytes
+  half c;   // 2 bytes or 4 bytes + 4 bytes pad
+  double d; // 8 bytes
+};
+
+
+cbuffer CB0 {
+  // CHECK-HALF: @CB0.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB0, 24, 0))
+  // CHECK-FLOAT: @CB0.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB0, 24, 0))
+  S1 s1;
+}
+
+cbuffer CB1 {
+  // CHECK-HALF: @CB1.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB1, 16, 0))
+  // CHECK-FLOAT: @CB1.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB1, 24, 0))
+  S2 s2;
+}
+
+cbuffer CB2 {
+  // CHECK-HALF: @CB2.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB2, 16, 0))
+  // CHECK-FLOAT: @CB2.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB2, 16, 0))
+  S3 s3;
+}
+
+cbuffer CB3 {
+  // CHECK-HALF: @CB3.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB3, 16, 0))
+  // CHECK-FLOAT: @CB3.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB3, 24, 0))
+  S4 s4;
+}

@llvmbot
Copy link
Member

llvmbot commented Feb 20, 2025

@llvm/pr-subscribers-clang

Author: Ashley Coleman (V-FEXrt)

Changes

Fixes #119641

Update SemaHLSL to correctly calculate the alignment barrier for scalars that are not 4 bytes wide


Full diff: https://github.com/llvm/llvm-project/pull/128086.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaHLSL.cpp (+22-5)
  • (added) clang/test/CodeGenHLSL/cbuffer_align.hlsl (+60)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d26d85d5861b1..cccfd4aec7eb2 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -172,6 +172,27 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
   return Result;
 }
 
+static unsigned calculateLegacyCbufferFieldAlign(const ASTContext &Context,
+                                                 QualType T) {
+  // Aggregate types are always aligned to new buffer rows
+  if (T->isAggregateType())
+    return 16;
+
+  assert(Context.getTypeSize(T) <= 64 &&
+         "Scalar bit widths larger than 64 not supported");
+
+  // 64 bit types such as double and uint64_t align to 8 bytes
+  if (Context.getTypeSize(T) == 64)
+    return 8;
+
+  // Half types align to 2 bytes only if native half is available
+  if (T->isHalfType() && Context.getLangOpts().NativeHalfType)
+    return 2;
+
+  // Everything else aligns to 4 bytes
+  return 4;
+}
+
 // Calculate the size of a legacy cbuffer type in bytes based on
 // https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
 static unsigned calculateLegacyCbufferSize(const ASTContext &Context,
@@ -183,11 +204,7 @@ static unsigned calculateLegacyCbufferSize(const ASTContext &Context,
     for (const FieldDecl *Field : RD->fields()) {
       QualType Ty = Field->getType();
       unsigned FieldSize = calculateLegacyCbufferSize(Context, Ty);
-      // FIXME: This is not the correct alignment, it does not work for 16-bit
-      // types. See llvm/llvm-project#119641.
-      unsigned FieldAlign = 4;
-      if (Ty->isAggregateType())
-        FieldAlign = CBufferAlign;
+      unsigned FieldAlign = calculateLegacyCbufferFieldAlign(Context, Ty);
       Size = llvm::alignTo(Size, FieldAlign);
       Size += FieldSize;
     }
diff --git a/clang/test/CodeGenHLSL/cbuffer_align.hlsl b/clang/test/CodeGenHLSL/cbuffer_align.hlsl
new file mode 100644
index 0000000000000..31db2e0726020
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_align.hlsl
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s -fnative-half-type \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-HALF
+
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-FLOAT
+
+
+struct S1 {
+  half a;   // 2 bytes + 2 bytes pad or 4 bytes
+  float b;  // 4 bytes
+  half c;   // 2 bytes + 2 bytes pad or 4 bytes
+  float d;  // 4 bytes
+  double e; // 8 bytes
+};
+
+struct S2 {
+  half a;   // 2 bytes or 4 bytes
+  half b;   // 2 bytes or 4 bytes
+  float e;  // 4 bytes or 4 bytes + 4 padding
+  double f; // 8 bytes
+};
+
+struct S3 {
+  half a;     // 2 bytes + 6 bytes pad or 4 bytes + 4 bytes pad
+  uint64_t b; // 8 bytes
+};
+
+struct S4 {
+  float a;  // 4 bytes
+  half b;   // 2 bytes or 4 bytes
+  half c;   // 2 bytes or 4 bytes + 4 bytes pad
+  double d; // 8 bytes
+};
+
+
+cbuffer CB0 {
+  // CHECK-HALF: @CB0.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB0, 24, 0))
+  // CHECK-FLOAT: @CB0.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB0, 24, 0))
+  S1 s1;
+}
+
+cbuffer CB1 {
+  // CHECK-HALF: @CB1.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB1, 16, 0))
+  // CHECK-FLOAT: @CB1.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB1, 24, 0))
+  S2 s2;
+}
+
+cbuffer CB2 {
+  // CHECK-HALF: @CB2.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB2, 16, 0))
+  // CHECK-FLOAT: @CB2.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB2, 16, 0))
+  S3 s3;
+}
+
+cbuffer CB3 {
+  // CHECK-HALF: @CB3.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB3, 16, 0))
+  // CHECK-FLOAT: @CB3.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB3, 24, 0))
+  S4 s4;
+}

Copy link
Contributor

@tex3d tex3d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens for vector fields?

@V-FEXrt
Copy link
Contributor Author

V-FEXrt commented Feb 24, 2025

@tex3d @hekota Okay PTAL when you have a moment!

Copy link
Member

@hekota hekota left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@tex3d tex3d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@V-FEXrt V-FEXrt merged commit cd4c30b into llvm:main Feb 26, 2025
12 checks passed
@V-FEXrt V-FEXrt deleted the hlsl-119641-align-16-64-bit-types branch February 26, 2025 00:23
@damyanp damyanp moved this to Closed in HLSL Support Apr 25, 2025
@damyanp damyanp removed this from HLSL Support Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category HLSL HLSL Language Support

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HLSL] packoffset validation does not handle 16-bit or 64-bit types well

4 participants