1010#include " CGHLSLRuntime.h"
1111#include " CodeGenModule.h"
1212#include " clang/AST/Type.h"
13+ #include < climits>
1314
1415// ===----------------------------------------------------------------------===//
1516// Implementation of constant buffer layout common between DirectX and
@@ -58,9 +59,15 @@ namespace CodeGen {
5859// classes) and calls layoutField to converts each field to its corresponding
5960// LLVM type and to calculate its HLSL constant buffer layout. Any embedded
6061// structs (or arrays of structs) are converted to target layout types as well.
62+ //
63+ // When Packoffsets are specified the elements will be placed based on the
64+ // user-specified offsets. Not all elements must have a packoffset/register(c#)
65+ // annotation though. For those that don't, the Packoffsets array will constain
66+ // -1 value instead. These elements must be placed at the end of the layout
67+ // after all of the elements with specific offset.
6168llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType (
6269 const RecordType *StructType,
63- const llvm::SmallVector<unsigned > *Packoffsets) {
70+ const llvm::SmallVector<int32_t > *Packoffsets) {
6471
6572 // check if we already have the layout type for this struct
6673 if (llvm::TargetExtType *Ty =
@@ -72,6 +79,8 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
7279 unsigned Index = 0 ; // packoffset index
7380 unsigned EndOffset = 0 ;
7481
82+ SmallVector<std::pair<const FieldDecl *, unsigned >> DelayLayoutFields;
83+
7584 // reserve first spot in the layout vector for buffer size
7685 Layout.push_back (0 );
7786
@@ -89,14 +98,57 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
8998 RecordTypes.pop_back ();
9099
91100 for (const auto *FD : RT->getDecl ()->fields ()) {
92- assert ((!Packoffsets || Index < Packoffsets->size ()) &&
93- " number of elements in layout struct does not "
94- " match number of packoffset annotations" );
101+ unsigned FieldOffset = UINT_MAX;
102+ llvm::Type *FieldType = nullptr ;
103+
104+ if (Packoffsets) {
105+ // have packoffset/register(c#) annotations
106+ assert (Index < Packoffsets->size () &&
107+ " number of elements in layout struct does not match number of "
108+ " packoffset annotations" );
109+ int PO = (*Packoffsets)[Index++];
110+ if (PO != -1 ) {
111+ if (!layoutField (FD, EndOffset, FieldOffset, FieldType, PO))
112+ return nullptr ;
113+ } else {
114+ // No packoffset/register(cX) annotation on this field;
115+ // Delay the layout until after all of the other elements
116+ // annotated with packoffsets/register(cX) are processed.
117+ DelayLayoutFields.emplace_back (FD, LayoutElements.size ());
118+ // reserve space for this field in the layout vector and elements list
119+ Layout.push_back (UINT_MAX);
120+ LayoutElements.push_back (nullptr );
121+ continue ;
122+ }
123+ } else {
124+ if (!layoutField (FD, EndOffset, FieldOffset, FieldType))
125+ return nullptr ;
126+ }
127+
128+ assert (FieldOffset != UINT_MAX && FieldType != nullptr );
129+ Layout.push_back ((unsigned )FieldOffset);
130+ LayoutElements.push_back (FieldType);
131+ }
132+ }
95133
96- if (!layoutField (FD, EndOffset, Layout, LayoutElements,
97- Packoffsets ? (*Packoffsets)[Index] : -1 ))
134+ // process delayed layouts
135+ if (!DelayLayoutFields.empty ()) {
136+ for (auto I : DelayLayoutFields) {
137+ const FieldDecl *FD = I.first ;
138+ unsigned IndexInLayoutElements = I.second ;
139+ // the first item in layout vector is size, so we need to offset the index
140+ // by 1
141+ unsigned IndexInLayout = IndexInLayoutElements + 1 ;
142+ assert (Layout[IndexInLayout] == UINT_MAX &&
143+ LayoutElements[IndexInLayoutElements] == nullptr );
144+
145+ unsigned FieldOffset = UINT_MAX;
146+ llvm::Type *FieldType = nullptr ;
147+ if (!layoutField (FD, EndOffset, FieldOffset, FieldType))
98148 return nullptr ;
99- Index++;
149+
150+ Layout[IndexInLayout] = (unsigned )FieldOffset;
151+ LayoutElements[IndexInLayoutElements] = FieldType;
100152 }
101153 }
102154
@@ -122,16 +174,19 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
122174// The function converts a single field of HLSL Buffer to its corresponding
123175// LLVM type and calculates it's layout. Any embedded structs (or
124176// arrays of structs) are converted to target layout types as well.
125- // The converted type is appended to the LayoutElements list, the element
126- // offset is added to the Layout list and the EndOffset updated to the offset
127- // just after the lay-ed out element (which is basically the size of the
128- // buffer).
177+ // The converted type is set to the FieldType parameter, the element
178+ // offset is set to the FieldOffset parameter. The EndOffset (=size of the
179+ // buffer) is also updated accordingly to the offset just after the placed
180+ // element, unless the incoming EndOffset already larger (may happen in case
181+ // of unsorted packoffset annotations).
129182// Returns true if the conversion was successful.
130183// The packoffset parameter contains the field's layout offset provided by the
131184// user or -1 if there was no packoffset (or register(cX)) annotation.
132- bool HLSLBufferLayoutBuilder::layoutField (
133- const FieldDecl *FD, unsigned &EndOffset, SmallVector<unsigned > &Layout,
134- SmallVector<llvm::Type *> &LayoutElements, int Packoffset) {
185+ bool HLSLBufferLayoutBuilder::layoutField (const FieldDecl *FD,
186+ unsigned &EndOffset,
187+ unsigned &FieldOffset,
188+ llvm::Type *&FieldType,
189+ int Packoffset) {
135190
136191 // Size of element; for arrays this is a size of a single element in the
137192 // array. Total array size of calculated as (ArrayCount-1) * ArrayStride +
@@ -220,8 +275,8 @@ bool HLSLBufferLayoutBuilder::layoutField(
220275 EndOffset = std::max<unsigned >(EndOffset, NewEndOffset);
221276
222277 // add the layout element and offset to the lists
223- Layout. push_back ( ElemOffset) ;
224- LayoutElements. push_back ( ElemLayoutTy) ;
278+ FieldOffset = ElemOffset;
279+ FieldType = ElemLayoutTy;
225280 return true ;
226281}
227282
0 commit comments