Skip to content

Commit

Permalink
[HLSL] CodeGen hlsl cbuffer/tbuffer.
Browse files Browse the repository at this point in the history
cbuffer A {
  float a;
  float b;
}

will be translated to a global variable.

Something like

struct CB_Ty {
  float a;
  float b;
};

CB_Ty A;

And all use of a and b will be replaced with A.a and A.b.

Only support none-legacy cbuffer layout now.
CodeGen for Resource binding will be in separate patch.
In the separate patch, resource binding will map the resource information to the global variable.

Reviewed By: efriedma

Differential Revision: https://reviews.llvm.org/D130131
  • Loading branch information
python3kgae committed Oct 13, 2022
1 parent e231a58 commit ebe9c7f
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 3 deletions.
4 changes: 1 addition & 3 deletions clang/lib/CodeGen/CGDecl.cpp
Expand Up @@ -100,6 +100,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
case Decl::ObjCTypeParam:
case Decl::Binding:
case Decl::UnresolvedUsingIfExists:
case Decl::HLSLBuffer:
llvm_unreachable("Declaration should not be in declstmts!");
case Decl::Record: // struct/union/class X;
case Decl::CXXRecord: // struct/union/class X; [C++]
Expand Down Expand Up @@ -179,9 +180,6 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
EmitVariablyModifiedType(Ty);
return;
}
case Decl::HLSLBuffer:
// FIXME: add codegen for HLSLBuffer.
return;
}
}

Expand Down
135 changes: 135 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Expand Up @@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//

#include "CGHLSLRuntime.h"
#include "CGDebugInfo.h"
#include "CodeGenModule.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/TargetOptions.h"
Expand All @@ -27,6 +28,7 @@ using namespace hlsl;
using namespace llvm;

namespace {

void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
// The validation of ValVersionStr is done at HLSLToolChain::TranslateArgs.
// Assume ValVersionStr is legal here.
Expand All @@ -51,9 +53,116 @@ void addDisableOptimizations(llvm::Module &M) {
StringRef Key = "dx.disable_optimizations";
M.addModuleFlag(llvm::Module::ModFlagBehavior::Override, Key, 1);
}
// cbuffer will be translated into global variable in special address space.
// If translate into C,
// cbuffer A {
// float a;
// float b;
// }
// float foo() { return a + b; }
//
// will be translated into
//
// struct A {
// float a;
// float b;
// } cbuffer_A __attribute__((address_space(4)));
// float foo() { return cbuffer_A.a + cbuffer_A.b; }
//
// layoutBuffer will create the struct A type.
// replaceBuffer will replace use of global variable a and b with cbuffer_A.a
// and cbuffer_A.b.
//
void layoutBuffer(CGHLSLRuntime::Buffer &Buf, const DataLayout &DL) {
if (Buf.Constants.empty())
return;

std::vector<llvm::Type *> EltTys;
for (auto &Const : Buf.Constants) {
GlobalVariable *GV = Const.first;
Const.second = EltTys.size();
llvm::Type *Ty = GV->getValueType();
EltTys.emplace_back(Ty);
}
Buf.LayoutStruct = llvm::StructType::get(EltTys[0]->getContext(), EltTys);
}

GlobalVariable *replaceBuffer(CGHLSLRuntime::Buffer &Buf) {
// Create global variable for CB.
GlobalVariable *CBGV =
new GlobalVariable(Buf.LayoutStruct, /*isConstant*/ true,
GlobalValue::LinkageTypes::ExternalLinkage, nullptr,
Buf.Name + (Buf.IsCBuffer ? ".cb." : ".tb."),
GlobalValue::NotThreadLocal);

IRBuilder<> B(CBGV->getContext());
Value *ZeroIdx = B.getInt32(0);
// Replace Const use with CB use.
for (auto &Const : Buf.Constants) {
llvm::Type *EltTy = Buf.LayoutStruct->getElementType(Const.second);
GlobalVariable *GV = Const.first;
unsigned Offset = Const.second;

Value *GEP =
B.CreateGEP(Buf.LayoutStruct, CBGV, {ZeroIdx, B.getInt32(Offset)});

llvm::Type *GVTy = GV->getValueType();
assert(EltTy == GVTy && "constant type mismatch");

// Replace.
GV->replaceAllUsesWith(GEP);
// Erase GV.
GV->removeDeadConstantUsers();
GV->eraseFromParent();
}
return CBGV;
}

} // namespace

void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) {
if (D->getStorageClass() == SC_Static) {
// For static inside cbuffer, take as global static.
// Don't add to cbuffer.
CGM.EmitGlobal(D);
return;
}

auto *GV = cast<GlobalVariable>(CGM.GetAddrOfGlobalVar(D));
// Add debug info for constVal.
if (CGDebugInfo *DI = CGM.getModuleDebugInfo())
if (CGM.getCodeGenOpts().getDebugInfo() >=
codegenoptions::DebugInfoKind::LimitedDebugInfo)
DI->EmitGlobalVariable(cast<GlobalVariable>(GV), D);

// FIXME: support packoffset.
// See https://github.com/llvm/llvm-project/issues/57914.
uint32_t Offset = 0;
bool HasUserOffset = false;

unsigned LowerBound = HasUserOffset ? Offset : UINT_MAX;
CB.Constants.emplace_back(std::make_pair(GV, LowerBound));
}

void CGHLSLRuntime::addBufferDecls(const DeclContext *DC, Buffer &CB) {
for (Decl *it : DC->decls()) {
if (auto *ConstDecl = dyn_cast<VarDecl>(it)) {
addConstant(ConstDecl, CB);
} else if (isa<CXXRecordDecl, EmptyDecl>(it)) {
// Nothing to do for this declaration.
} else if (isa<FunctionDecl>(it)) {
// A function within an cbuffer is effectively a top-level function,
// as it only refers to globally scoped declarations.
CGM.EmitTopLevelDecl(it);
}
}
}

void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *D) {
Buffers.emplace_back(Buffer(D));
addBufferDecls(D, Buffers.back());
}

void CGHLSLRuntime::finishCodeGen() {
auto &TargetOpts = CGM.getTarget().getTargetOpts();
llvm::Module &M = CGM.getModule();
Expand All @@ -64,6 +173,32 @@ void CGHLSLRuntime::finishCodeGen() {
generateGlobalCtorDtorCalls();
if (CGM.getCodeGenOpts().OptimizationLevel == 0)
addDisableOptimizations(M);

const DataLayout &DL = M.getDataLayout();

for (auto &Buf : Buffers) {
layoutBuffer(Buf, DL);
GlobalVariable *GV = replaceBuffer(Buf);
M.getGlobalList().push_back(GV);
// FIXME: generate resource binding.
// See https://github.com/llvm/llvm-project/issues/57915.
}
}

CGHLSLRuntime::Buffer::Buffer(const HLSLBufferDecl *D) {
Name = D->getName();
IsCBuffer = D->isCBuffer();
if (auto *Binding = D->getAttr<HLSLResourceBindingAttr>()) {
llvm::APInt RegInt(64, 0);
Binding->getSlot().substr(1).getAsInteger(10, RegInt);
Reg = RegInt.getLimitedValue();

llvm::APInt SpaceInt(64, 0);
Binding->getSpace().substr(5).getAsInteger(10, RegInt);
Space = SpaceInt.getLimitedValue();
} else {
Space = 0;
}
}

void CGHLSLRuntime::annotateHLSLResource(const VarDecl *D, GlobalVariable *GV) {
Expand Down
32 changes: 32 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Expand Up @@ -19,13 +19,25 @@

#include "clang/Basic/HLSLRuntime.h"

#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"

#include <vector>

namespace llvm {
class GlobalVariable;
class Function;
class StructType;
} // namespace llvm

namespace clang {
class VarDecl;
class ParmVarDecl;
class HLSLBufferDecl;
class CallExpr;
class Type;
class DeclContext;

class FunctionDecl;

Expand All @@ -34,6 +46,19 @@ namespace CodeGen {
class CodeGenModule;

class CGHLSLRuntime {
public:
struct Buffer {
Buffer(const HLSLBufferDecl *D);
llvm::StringRef Name;
// IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer).
bool IsCBuffer;
llvm::Optional<unsigned> Reg;
unsigned Space;
// Global variable and offset for each constant.
std::vector<std::pair<llvm::GlobalVariable *, unsigned>> Constants;
llvm::StructType *LayoutStruct = nullptr;
};

protected:
CodeGenModule &CGM;
uint32_t ResourceCounters[static_cast<uint32_t>(
Expand All @@ -48,11 +73,18 @@ class CGHLSLRuntime {
void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV);
void generateGlobalCtorDtorCalls();

void addBuffer(const HLSLBufferDecl *D);
void finishCodeGen();

void setHLSLEntryAttributes(const FunctionDecl *FD, llvm::Function *Fn);

void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
void setHLSLFunctionAttributes(llvm::Function *, const FunctionDecl *);

private:
void addConstant(VarDecl *D, Buffer &CB);
void addBufferDecls(const DeclContext *DC, Buffer &CB);
llvm::SmallVector<Buffer> Buffers;
};

} // namespace CodeGen
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -6451,6 +6451,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
DI->EmitAndRetainType(getContext().getEnumType(cast<EnumDecl>(D)));
break;

case Decl::HLSLBuffer:
getHLSLRuntime().addBuffer(cast<HLSLBufferDecl>(D));
break;

default:
// Make sure we handled everything we should, every other kind is a
// non-top-level decl. FIXME: Would be nice to have an isTopLevelDeclKind
Expand Down
23 changes: 23 additions & 0 deletions clang/test/CodeGenHLSL/cbuf.hlsl
@@ -0,0 +1,23 @@
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
// RUN: dxil-pc-shadermodel6.3-library %s \
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s

// CHECK: @[[CB:.+]] = external constant { float, double }
cbuffer A : register(b0, space1) {
float a;
double b;
}

// CHECK: @[[TB:.+]] = external constant { float, double }
tbuffer A : register(t2, space1) {
float c;
double d;
}

float foo() {
// CHECK: load float, ptr @[[CB]], align 4
// CHECK: load double, ptr getelementptr inbounds ({ float, double }, ptr @[[CB]], i32 0, i32 1), align 8
// CHECK: load float, ptr @[[TB]], align 4
// CHECK: load double, ptr getelementptr inbounds ({ float, double }, ptr @[[TB]], i32 0, i32 1), align 8
return a + b + c*d;
}
23 changes: 23 additions & 0 deletions clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl
@@ -0,0 +1,23 @@
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
// RUN: dxil-pc-shadermodel6.3-library %s \
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s

// Make sure cbuffer inside namespace works.
// CHECK: @[[CB:.+]] = external constant { float }
// CHECK: @[[TB:.+]] = external constant { float }
namespace n0 {
namespace n1 {
cbuffer A {
float a;
}
}
tbuffer B {
float b;
}
}

float foo() {
// CHECK: load float, ptr @[[CB]], align 4
// CHECK: load float, ptr @[[TB]], align 4
return n0::n1::a + n0::b;
}
18 changes: 18 additions & 0 deletions clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl
@@ -0,0 +1,18 @@
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
// RUN: dxil-pc-shadermodel6.3-library %s \
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s

// CHECK-DAG: @[[CB:.+]] = external constant { float }

cbuffer A {
float a;
// CHECK-DAG:@b = internal global float 3.000000e+00, align 4
static float b = 3;
// CHECK:load float, ptr @[[CB]], align 4
// CHECK:load float, ptr @b, align 4
float foo() { return a + b; }
}

float bar() {
return foo();
}

0 comments on commit ebe9c7f

Please sign in to comment.