Skip to content

Commit cd4c30b

Browse files
authored
[HLSL][Sema] Fix Struct Size Calculation containing 16/32 bit scalars (#128086)
Fixes #119641 Update SemaHLSL to correctly calculate the alignment barrier for scalars that are not 4 bytes wide
1 parent 0983277 commit cd4c30b

File tree

2 files changed

+146
-4
lines changed

2 files changed

+146
-4
lines changed

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,23 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
172172
return Result;
173173
}
174174

175+
static unsigned calculateLegacyCbufferFieldAlign(const ASTContext &Context,
176+
QualType T) {
177+
// Arrays and Structs are always aligned to new buffer rows
178+
if (T->isArrayType() || T->isStructureType())
179+
return 16;
180+
181+
// Vectors are aligned to the type they contain
182+
if (const VectorType *VT = T->getAs<VectorType>())
183+
return calculateLegacyCbufferFieldAlign(Context, VT->getElementType());
184+
185+
assert(Context.getTypeSize(T) <= 64 &&
186+
"Scalar bit widths larger than 64 not supported");
187+
188+
// Scalar types are aligned to their byte width
189+
return Context.getTypeSize(T) / 8;
190+
}
191+
175192
// Calculate the size of a legacy cbuffer type in bytes based on
176193
// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
177194
static unsigned calculateLegacyCbufferSize(const ASTContext &Context,
@@ -183,11 +200,15 @@ static unsigned calculateLegacyCbufferSize(const ASTContext &Context,
183200
for (const FieldDecl *Field : RD->fields()) {
184201
QualType Ty = Field->getType();
185202
unsigned FieldSize = calculateLegacyCbufferSize(Context, Ty);
186-
// FIXME: This is not the correct alignment, it does not work for 16-bit
187-
// types. See llvm/llvm-project#119641.
188-
unsigned FieldAlign = 4;
189-
if (Ty->isAggregateType())
203+
unsigned FieldAlign = calculateLegacyCbufferFieldAlign(Context, Ty);
204+
205+
// If the field crosses the row boundary after alignment it drops to the
206+
// next row
207+
unsigned AlignSize = llvm::alignTo(Size, FieldAlign);
208+
if ((AlignSize % CBufferAlign) + FieldSize > CBufferAlign) {
190209
FieldAlign = CBufferAlign;
210+
}
211+
191212
Size = llvm::alignTo(Size, FieldAlign);
192213
Size += FieldSize;
193214
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
2+
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \
3+
// RUN: -fsyntax-only -verify -verify-ignore-unexpected=warning
4+
5+
struct S0 {
6+
half a;
7+
half b;
8+
half c;
9+
half d;
10+
half e;
11+
half f;
12+
half g;
13+
half h;
14+
};
15+
16+
cbuffer CB0Pass {
17+
S0 s0p : packoffset(c0.x);
18+
float f0p : packoffset(c1.x);
19+
}
20+
21+
cbuffer CB0Fail {
22+
S0 s0f : packoffset(c0.x);
23+
float f0f : packoffset(c0.w);
24+
// expected-error@-1 {{packoffset overlap between 'f0f', 's0f'}}
25+
}
26+
27+
struct S1 {
28+
float a;
29+
double b;
30+
float c;
31+
};
32+
33+
cbuffer CB1Pass {
34+
S1 s1p : packoffset(c0.x);
35+
float f1p : packoffset(c1.y);
36+
}
37+
38+
cbuffer CB1Fail {
39+
S1 s1f : packoffset(c0.x);
40+
float f1f : packoffset(c1.x);
41+
// expected-error@-1 {{packoffset overlap between 'f1f', 's1f'}}
42+
}
43+
44+
struct S2 {
45+
float3 a;
46+
float2 b;
47+
};
48+
49+
cbuffer CB2Pass {
50+
S2 s2p : packoffset(c0.x);
51+
float f2p : packoffset(c1.z);
52+
}
53+
54+
cbuffer CB2Fail {
55+
S2 s2f : packoffset(c0.x);
56+
float f2f : packoffset(c1.y);
57+
// expected-error@-1 {{packoffset overlap between 'f2f', 's2f'}}
58+
}
59+
60+
struct S3 {
61+
float3 a;
62+
float b;
63+
};
64+
65+
cbuffer CB3Pass {
66+
S3 s3p : packoffset(c0.x);
67+
float f3p : packoffset(c1.x);
68+
}
69+
70+
cbuffer CB3Fail {
71+
S3 s3f : packoffset(c0.x);
72+
float f3f : packoffset(c0.w);
73+
// expected-error@-1 {{packoffset overlap between 'f3f', 's3f'}}
74+
}
75+
76+
struct S4 {
77+
float2 a;
78+
float2 b;
79+
};
80+
81+
cbuffer CB4Pass {
82+
S4 s4p : packoffset(c0.x);
83+
float f4p : packoffset(c1.x);
84+
}
85+
86+
cbuffer CB4Fail {
87+
S4 s4f : packoffset(c0.x);
88+
float f4f : packoffset(c0.w);
89+
// expected-error@-1 {{packoffset overlap between 'f4f', 's4f'}}
90+
}
91+
92+
struct S5 {
93+
float a[3];
94+
};
95+
96+
cbuffer CB5Pass {
97+
S5 s5p : packoffset(c0.x);
98+
float f5p : packoffset(c2.y);
99+
}
100+
101+
cbuffer CB5Fail {
102+
S5 s5f : packoffset(c0.x);
103+
float f5f : packoffset(c2.x);
104+
// expected-error@-1 {{packoffset overlap between 'f5f', 's5f'}}
105+
}
106+
107+
struct S6 {
108+
float a;
109+
float2 b;
110+
};
111+
112+
cbuffer CB6Pass {
113+
S6 s6p : packoffset(c0.x);
114+
float f6p : packoffset(c0.w);
115+
}
116+
117+
cbuffer CB6Fail {
118+
S6 s6f : packoffset(c0.x);
119+
float f6f : packoffset(c0.y);
120+
// expected-error@-1 {{packoffset overlap between 'f6f', 's6f'}}
121+
}

0 commit comments

Comments
 (0)