[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