[clang] ebe9c7f - [HLSL] CodeGen hlsl cbuffer/tbuffer.
Xiang Li via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 12 21:17:47 PDT 2022
Author: Xiang Li
Date: 2022-10-12T21:17:38-07:00
New Revision: ebe9c7f3e2da779b984b336dadf37cc4ec2ae260
URL: https://github.com/llvm/llvm-project/commit/ebe9c7f3e2da779b984b336dadf37cc4ec2ae260
DIFF: https://github.com/llvm/llvm-project/commit/ebe9c7f3e2da779b984b336dadf37cc4ec2ae260.diff
LOG: [HLSL] CodeGen hlsl cbuffer/tbuffer.
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
Added:
clang/test/CodeGenHLSL/cbuf.hlsl
clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl
clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl
Modified:
clang/lib/CodeGen/CGDecl.cpp
clang/lib/CodeGen/CGHLSLRuntime.cpp
clang/lib/CodeGen/CGHLSLRuntime.h
clang/lib/CodeGen/CodeGenModule.cpp
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 85d45f4b20265..8f28f96e93785 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -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++]
@@ -179,9 +180,6 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
EmitVariablyModifiedType(Ty);
return;
}
- case Decl::HLSLBuffer:
- // FIXME: add codegen for HLSLBuffer.
- return;
}
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 96a8f22aa877e..245fe88c170f9 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "CGHLSLRuntime.h"
+#include "CGDebugInfo.h"
#include "CodeGenModule.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/TargetOptions.h"
@@ -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.
@@ -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();
@@ -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) {
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 120f53ffec14e..35f888512f544 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -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;
@@ -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>(
@@ -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
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 9c96195d85cb8..efec43069ec2b 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -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
diff --git a/clang/test/CodeGenHLSL/cbuf.hlsl b/clang/test/CodeGenHLSL/cbuf.hlsl
new file mode 100644
index 0000000000000..ea97561dce15a
--- /dev/null
+++ b/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;
+}
diff --git a/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl b/clang/test/CodeGenHLSL/cbuf_in_namespace.hlsl
new file mode 100644
index 0000000000000..73dc376942dfb
--- /dev/null
+++ b/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;
+}
diff --git a/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl b/clang/test/CodeGenHLSL/static_global_and_function_in_cb.hlsl
new file mode 100644
index 0000000000000..eabd0faff6a87
--- /dev/null
+++ b/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();
+}
More information about the cfe-commits
mailing list