Skip to content
Merged
40 changes: 33 additions & 7 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ using namespace CodeGen;
using namespace clang::hlsl;
using namespace llvm;

using llvm::hlsl::CBufferRowSizeInBytes;

static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
unsigned Slot, unsigned Space);

Expand Down Expand Up @@ -70,7 +72,7 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {

llvm::Type *
CGHLSLRuntime::convertHLSLSpecificType(const Type *T,
SmallVector<unsigned> *Packoffsets) {
SmallVector<int32_t> *Packoffsets) {
assert(T->isHLSLSpecificType() && "Not an HLSL specific type!");

// Check if the target has a specific translation for this type first.
Expand Down Expand Up @@ -174,21 +176,45 @@ createBufferHandleType(const HLSLBufferDecl *BufDecl) {
return cast<HLSLAttributedResourceType>(QT.getTypePtr());
}

// Iterates over all declarations in the HLSL buffer and based on the
// packoffset or register(c#) annotations it fills outs the Layout
// vector with the user-specified layout offsets.
// The buffer offsets can be specified 2 ways:
// 1. declarations in cbuffer {} block can have a packoffset annotation
// (translates to HLSLPackOffsetAttr)
// 2. default constant buffer declarations at global scope can have
// register(c#) annotations (translates to HLSLResourceBindingAttr with
// RegisterType::C)
// It is not guaranteed that all declarations in a buffer have an annotation.
// For those where it is not specified a -1 value is added to the Layout
// vector. In the final layout these declarations will be placed at the end
// of the HLSL buffer after all of the elements with specified offset.
static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl,
SmallVector<unsigned> &Layout) {
SmallVector<int32_t> &Layout) {
assert(Layout.empty() && "expected empty vector for layout");
assert(BufDecl->hasValidPackoffset());

for (Decl *D : BufDecl->decls()) {
for (Decl *D : BufDecl->buffer_decls()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: On formatting, it sounds like the llvm repo prefers 'auto *name' for for loops?

Copy link
Member Author

Choose a reason for hiding this comment

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

It depends. I got feedback in the past to not use auto if the type is not immediately obvious. For example with dyn_cast the type name is on the same line, so it is ok to use auto there. In the end I think it is more of a personal preference.

if (isa<CXXRecordDecl, EmptyDecl>(D) || isa<FunctionDecl>(D)) {
continue;
}
VarDecl *VD = dyn_cast<VarDecl>(D);
if (!VD || VD->getType().getAddressSpace() != LangAS::hlsl_constant)
continue;
assert(VD->hasAttr<HLSLPackOffsetAttr>() &&
"expected packoffset attribute on every declaration");
size_t Offset = VD->getAttr<HLSLPackOffsetAttr>()->getOffsetInBytes();
size_t Offset = -1;
if (VD->hasAttrs()) {
for (auto *Attr : VD->getAttrs()) {
if (auto *POA = dyn_cast<HLSLPackOffsetAttr>(Attr)) {
Offset = POA->getOffsetInBytes();
} else if (auto *RBA = dyn_cast<HLSLResourceBindingAttr>(Attr)) {
if (RBA->getRegisterType() ==
HLSLResourceBindingAttr::RegisterType::C) {
// size of constant buffer row is 16 bytes
Offset = RBA->getSlotNumber() * CBufferRowSizeInBytes;
}
}
}
}
Layout.push_back(Offset);
}
}
Expand All @@ -207,7 +233,7 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
return;

// create global variable for the constant buffer
SmallVector<unsigned> Layout;
SmallVector<int32_t> Layout;
if (BufDecl->hasValidPackoffset())
fillPackoffsetLayout(BufDecl, Layout);

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class CGHLSLRuntime {

llvm::Type *
convertHLSLSpecificType(const Type *T,
SmallVector<unsigned> *Packoffsets = nullptr);
SmallVector<int32_t> *Packoffsets = nullptr);

void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV);
void generateGlobalCtorDtorCalls();
Expand Down
93 changes: 74 additions & 19 deletions clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "CGHLSLRuntime.h"
#include "CodeGenModule.h"
#include "clang/AST/Type.h"
#include <climits>

//===----------------------------------------------------------------------===//
// Implementation of constant buffer layout common between DirectX and
Expand All @@ -18,6 +19,7 @@

using namespace clang;
using namespace clang::CodeGen;
using llvm::hlsl::CBufferRowSizeInBytes;

namespace {

Expand Down Expand Up @@ -58,9 +60,15 @@ namespace CodeGen {
// classes) and calls layoutField to converts each field to its corresponding
// LLVM type and to calculate its HLSL constant buffer layout. Any embedded
// structs (or arrays of structs) are converted to target layout types as well.
//
// When Packoffsets are specified the elements will be placed based on the
// user-specified offsets. Not all elements must have a packoffset/register(c#)
// annotation though. For those that don't, the Packoffsets array will constain
// -1 value instead. These elements must be placed at the end of the layout
// after all of the elements with specific offset.
llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
const RecordType *StructType,
const llvm::SmallVector<unsigned> *Packoffsets) {
const llvm::SmallVector<int32_t> *Packoffsets) {

// check if we already have the layout type for this struct
if (llvm::TargetExtType *Ty =
Expand All @@ -72,6 +80,8 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
unsigned Index = 0; // packoffset index
unsigned EndOffset = 0;

SmallVector<std::pair<const FieldDecl *, unsigned>> DelayLayoutFields;

// reserve first spot in the layout vector for buffer size
Layout.push_back(0);

Expand All @@ -89,14 +99,57 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
RecordTypes.pop_back();

for (const auto *FD : RT->getDecl()->fields()) {
assert((!Packoffsets || Index < Packoffsets->size()) &&
"number of elements in layout struct does not "
"match number of packoffset annotations");
unsigned FieldOffset = UINT_MAX;
llvm::Type *FieldType = nullptr;

if (Packoffsets) {
// have packoffset/register(c#) annotations
assert(Index < Packoffsets->size() &&
"number of elements in layout struct does not match number of "
"packoffset annotations");
int PO = (*Packoffsets)[Index++];
if (PO != -1) {
if (!layoutField(FD, EndOffset, FieldOffset, FieldType, PO))
return nullptr;
} else {
// No packoffset/register(cX) annotation on this field;
// Delay the layout until after all of the other elements
// annotated with packoffsets/register(cX) are processed.
DelayLayoutFields.emplace_back(FD, LayoutElements.size());
// reserve space for this field in the layout vector and elements list
Layout.push_back(UINT_MAX);
LayoutElements.push_back(nullptr);
continue;
}
} else {
if (!layoutField(FD, EndOffset, FieldOffset, FieldType))
return nullptr;
}

assert(FieldOffset != UINT_MAX && FieldType != nullptr);
Layout.push_back((unsigned)FieldOffset);
LayoutElements.push_back(FieldType);
}
}

if (!layoutField(FD, EndOffset, Layout, LayoutElements,
Packoffsets ? (*Packoffsets)[Index] : -1))
// process delayed layouts
if (!DelayLayoutFields.empty()) {
for (auto I : DelayLayoutFields) {
const FieldDecl *FD = I.first;
unsigned IndexInLayoutElements = I.second;
// the first item in layout vector is size, so we need to offset the index
// by 1
unsigned IndexInLayout = IndexInLayoutElements + 1;
assert(Layout[IndexInLayout] == UINT_MAX &&
LayoutElements[IndexInLayoutElements] == nullptr);

unsigned FieldOffset = UINT_MAX;
llvm::Type *FieldType = nullptr;
if (!layoutField(FD, EndOffset, FieldOffset, FieldType))
return nullptr;
Index++;

Layout[IndexInLayout] = (unsigned)FieldOffset;
LayoutElements[IndexInLayoutElements] = FieldType;
}
}

Expand All @@ -122,16 +175,19 @@ llvm::TargetExtType *HLSLBufferLayoutBuilder::createLayoutType(
// The function converts a single field of HLSL Buffer to its corresponding
// LLVM type and calculates it's layout. Any embedded structs (or
// arrays of structs) are converted to target layout types as well.
// The converted type is appended to the LayoutElements list, the element
// offset is added to the Layout list and the EndOffset updated to the offset
// just after the lay-ed out element (which is basically the size of the
// buffer).
// The converted type is set to the FieldType parameter, the element
// offset is set to the FieldOffset parameter. The EndOffset (=size of the
// buffer) is also updated accordingly to the offset just after the placed
// element, unless the incoming EndOffset already larger (may happen in case
// of unsorted packoffset annotations).
// Returns true if the conversion was successful.
// The packoffset parameter contains the field's layout offset provided by the
// user or -1 if there was no packoffset (or register(cX)) annotation.
bool HLSLBufferLayoutBuilder::layoutField(
const FieldDecl *FD, unsigned &EndOffset, SmallVector<unsigned> &Layout,
SmallVector<llvm::Type *> &LayoutElements, int Packoffset) {
bool HLSLBufferLayoutBuilder::layoutField(const FieldDecl *FD,
unsigned &EndOffset,
unsigned &FieldOffset,
llvm::Type *&FieldType,
int Packoffset) {

// Size of element; for arrays this is a size of a single element in the
// array. Total array size of calculated as (ArrayCount-1) * ArrayStride +
Expand All @@ -141,8 +197,7 @@ bool HLSLBufferLayoutBuilder::layoutField(
unsigned ArrayCount = 1;
unsigned ArrayStride = 0;

const unsigned BufferRowAlign = 16U;
unsigned NextRowOffset = llvm::alignTo(EndOffset, BufferRowAlign);
unsigned NextRowOffset = llvm::alignTo(EndOffset, CBufferRowSizeInBytes);

llvm::Type *ElemLayoutTy = nullptr;
QualType FieldTy = FD->getType();
Expand Down Expand Up @@ -172,7 +227,7 @@ bool HLSLBufferLayoutBuilder::layoutField(
getScalarOrVectorSizeInBytes(CGM.getTypes().ConvertTypeForMem(Ty));
ElemLayoutTy = CGM.getTypes().ConvertTypeForMem(FieldTy);
}
ArrayStride = llvm::alignTo(ElemSize, BufferRowAlign);
ArrayStride = llvm::alignTo(ElemSize, CBufferRowSizeInBytes);
ElemOffset = (Packoffset != -1) ? Packoffset : NextRowOffset;

} else if (FieldTy->isStructureType()) {
Expand Down Expand Up @@ -220,8 +275,8 @@ bool HLSLBufferLayoutBuilder::layoutField(
EndOffset = std::max<unsigned>(EndOffset, NewEndOffset);

// add the layout element and offset to the lists
Layout.push_back(ElemOffset);
LayoutElements.push_back(ElemLayoutTy);
FieldOffset = ElemOffset;
FieldType = ElemLayoutTy;
return true;
}

Expand Down
7 changes: 3 additions & 4 deletions clang/lib/CodeGen/HLSLBufferLayoutBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ class HLSLBufferLayoutBuilder {
// the Layout is the size followed by offsets for each struct element.
llvm::TargetExtType *
createLayoutType(const RecordType *StructType,
const llvm::SmallVector<unsigned> *Packoffsets = nullptr);
const llvm::SmallVector<int32_t> *Packoffsets = nullptr);

private:
bool layoutField(const clang::FieldDecl *FD, unsigned &EndOffset,
llvm::SmallVector<unsigned> &Layout,
llvm::SmallVector<llvm::Type *> &LayoutElements,
int Packoffset);
unsigned &FieldOffset, llvm::Type *&FieldType,
int Packoffset = -1);
};

} // namespace CodeGen
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ class TargetCodeGenInfo {
/// Return an LLVM type that corresponds to a HLSL type
virtual llvm::Type *
getHLSLType(CodeGenModule &CGM, const Type *T,
const SmallVector<unsigned> *Packoffsets = nullptr) const {
const SmallVector<int32_t> *Packoffsets = nullptr) const {
return nullptr;
}

Expand Down
8 changes: 4 additions & 4 deletions clang/lib/CodeGen/Targets/DirectX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ class DirectXTargetCodeGenInfo : public TargetCodeGenInfo {
DirectXTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT)
: TargetCodeGenInfo(std::make_unique<DefaultABIInfo>(CGT)) {}

llvm::Type *getHLSLType(
CodeGenModule &CGM, const Type *T,
const SmallVector<unsigned> *Packoffsets = nullptr) const override;
llvm::Type *
getHLSLType(CodeGenModule &CGM, const Type *T,
const SmallVector<int32_t> *Packoffsets = nullptr) const override;
};

llvm::Type *DirectXTargetCodeGenInfo::getHLSLType(
CodeGenModule &CGM, const Type *Ty,
const SmallVector<unsigned> *Packoffsets) const {
const SmallVector<int32_t> *Packoffsets) const {
auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);
if (!ResType)
return nullptr;
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/CodeGen/Targets/SPIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo {

unsigned getOpenCLKernelCallingConv() const override;
llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
llvm::Type *getHLSLType(
CodeGenModule &CGM, const Type *Ty,
const SmallVector<unsigned> *Packoffsets = nullptr) const override;
llvm::Type *
getHLSLType(CodeGenModule &CGM, const Type *Ty,
const SmallVector<int32_t> *Packoffsets = nullptr) const override;
llvm::Type *getSPIRVImageTypeFromHLSLResource(
const HLSLAttributedResourceType::Attributes &attributes,
llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
Expand Down Expand Up @@ -371,7 +371,7 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,

llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(
CodeGenModule &CGM, const Type *Ty,
const SmallVector<unsigned> *Packoffsets) const {
const SmallVector<int32_t> *Packoffsets) const {
auto *ResType = dyn_cast<HLSLAttributedResourceType>(Ty);
if (!ResType)
return nullptr;
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1961,6 +1961,18 @@ void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);

// Set HasValidPackoffset if any of the decls has a register(c#) annotation;
for (const Decl *VD : DefaultCBufferDecls) {
if (const HLSLResourceBindingAttr *RBA =
VD->getAttr<HLSLResourceBindingAttr>()) {
if (RBA->getRegisterType() ==
HLSLResourceBindingAttr::RegisterType::C) {
DefaultCBuffer->setHasValidPackoffset(true);
break;
}
}
}

DeclGroupRef DG(DefaultCBuffer);
SemaRef.Consumer.HandleTopLevelDecl(DG);
}
Expand Down
17 changes: 15 additions & 2 deletions clang/test/CodeGenHLSL/cbuffer_with_packoffset.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s

// CHECK: %__cblayout_CB = type <{ float, double, <2 x i32> }>
// CHECK: %__cblayout_CB_1 = type <{ float, <2 x float> }>

// CHECK: @CB.cb = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88))
// CHECK: @a = external addrspace(2) global float, align 4
Expand All @@ -15,6 +16,17 @@ cbuffer CB : register(b1, space3) {
int2 c : packoffset(c5.z);
}

// CHECK: @CB.cb.1 = external constant target("dx.CBuffer", target("dx.Layout", %__cblayout_CB_1, 92, 88, 80))
// CHECK: @x = external addrspace(2) global float, align 4
// CHECK: @y = external addrspace(2) global <2 x float>, align 8

// Missing packoffset annotation will produce a warning.
// Element x will be placed after the element y that has an explicit packoffset.
cbuffer CB : register(b0) {
float x;
float2 y : packoffset(c5);
}

// CHECK: define internal void @_init_resource_CB.cb()
// CHECK-NEXT: entry:
// CHECK-NEXT: %CB.cb_h = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 176, 16, 168, 88))
Expand All @@ -34,5 +46,6 @@ void main() {
foo();
}

// CHECK: !hlsl.cbs = !{![[CB:[0-9]+]]}
// CHECK: ![[CB]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c}
// CHECK: !hlsl.cbs = !{![[CB1:[0-9]+]], ![[CB2:[0-9]+]]}
// CHECK: ![[CB1]] = !{ptr @CB.cb, ptr addrspace(2) @a, ptr addrspace(2) @b, ptr addrspace(2) @c}
// CHECK: ![[CB2]] = !{ptr @CB.cb.1, ptr addrspace(2) @x, ptr addrspace(2) @y}
Loading