[clang] d3c54a1 - [HLSL] Call global constructors inside entry

Chris Bieneman via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 9 07:01:51 PDT 2022


Author: Chris Bieneman
Date: 2022-09-09T09:01:28-05:00
New Revision: d3c54a172d48a2e3a0c2740c300a1d7dbdf4e292

URL: https://github.com/llvm/llvm-project/commit/d3c54a172d48a2e3a0c2740c300a1d7dbdf4e292
DIFF: https://github.com/llvm/llvm-project/commit/d3c54a172d48a2e3a0c2740c300a1d7dbdf4e292.diff

LOG: [HLSL] Call global constructors inside entry

HLSL doesn't have a runtime loader model that supports global
construction by a loader or runtime initializer. To allow us to leverage
global constructors with minimal code generation impact we put calls to
the global constructors inside the generated entry function.

Differential Revision: https://reviews.llvm.org/D132977

Added: 
    clang/test/CodeGenHLSL/GlobalConstructorFunction.hlsl
    clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl
    clang/test/CodeGenHLSL/GlobalConstructors.hlsl
    clang/test/SemaHLSL/GlobalConstructors.hlsl

Modified: 
    clang/docs/HLSL/EntryFunctions.rst
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/CodeGen/CGHLSLRuntime.cpp
    clang/lib/CodeGen/CGHLSLRuntime.h
    clang/lib/Sema/SemaDeclAttr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/HLSL/EntryFunctions.rst b/clang/docs/HLSL/EntryFunctions.rst
index 0e547aab420c8..8591814777388 100644
--- a/clang/docs/HLSL/EntryFunctions.rst
+++ b/clang/docs/HLSL/EntryFunctions.rst
@@ -37,10 +37,16 @@ linkage following normal function code generation.
 
 The actual exported entry function which can be called by the GPU driver is a
 ``void(void)`` function that isn't name mangled. In code generation we generate
-the unmangled entry function, instantiations of the parameters with their
-semantic values populated, and a call to the user-defined function. After the
-call instruction the return value (if any) is saved using a target-appropriate
-intrinsic for storing outputs (for DirectX, the ``llvm.dx.store.output``).
+the unmangled entry function to serve as the actual shader entry. The shader
+entry function is annotated with the ``hlsl.shader`` function attribute
+identifying the entry's pipeline stage.
+
+The body of the unmangled entry function contains first a call to execute global
+constructors, then instantiations of the user-defined entry parameters with
+their semantic values populated, and a call to the user-defined function.
+After the call instruction the return value (if any) is saved using a
+target-appropriate intrinsic for storing outputs (for DirectX, the
+``llvm.dx.store.output``). Global destructors are not supported in HLSL.
 
 .. note::
 

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a59eeda85bc03..76e18c9deff46 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11655,6 +11655,8 @@ def err_hlsl_attribute_param_mismatch : Error<"%0 attribute parameters do not ma
 def err_hlsl_missing_semantic_annotation : Error<
   "semantic annotations must be present for all parameters of an entry "
   "function or patch constant function">;
+def err_hlsl_init_priority_unsupported : Error<
+  "initializer priorities are not supported in HLSL">;
 
 def err_hlsl_pointers_unsupported : Error<
   "%select{pointers|references}0 are unsupported in HLSL">;

diff  --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 9ca3b64996edd..2f0c00169af69 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -54,6 +54,8 @@ void CGHLSLRuntime::finishCodeGen() {
   Triple T(M.getTargetTriple());
   if (T.getArch() == Triple::ArchType::dxil)
     addDxilValVersion(TargetOpts.DxilValidatorVersion, M);
+
+  generateGlobalCtorCalls();
 }
 
 void CGHLSLRuntime::annotateHLSLResource(const VarDecl *D, GlobalVariable *GV) {
@@ -143,3 +145,40 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
   // FIXME: Handle codegen for return type semantics.
   B.CreateRetVoid();
 }
+
+void CGHLSLRuntime::generateGlobalCtorCalls() {
+  llvm::Module &M = CGM.getModule();
+  const auto *GlobalCtors = M.getNamedGlobal("llvm.global_ctors");
+  if (!GlobalCtors)
+    return;
+  const auto *CA = dyn_cast<ConstantArray>(GlobalCtors->getInitializer());
+  if (!CA)
+    return;
+  // The global_ctor array elements are a struct [Priority, Fn *, COMDat].
+  // HLSL neither supports priorities or COMDat values, so we will check those
+  // in an assert but not handle them.
+
+  llvm::SmallVector<Function *> CtorFns;
+  for (const auto &Ctor : CA->operands()) {
+    if (isa<ConstantAggregateZero>(Ctor))
+      continue;
+    ConstantStruct *CS = cast<ConstantStruct>(Ctor);
+
+    assert(cast<ConstantInt>(CS->getOperand(0))->getValue() == 65535 &&
+           "HLSL doesn't support setting priority for global ctors.");
+    assert(isa<ConstantPointerNull>(CS->getOperand(2)) &&
+           "HLSL doesn't support COMDat for global ctors.");
+    CtorFns.push_back(cast<Function>(CS->getOperand(1)));
+  }
+
+  // Insert a call to the global constructor at the beginning of the entry block
+  // to externally exported functions. This is a bit of a hack, but HLSL allows
+  // global constructors, but doesn't support driver initialization of globals.
+  for (auto &F : M.functions()) {
+    if (!F.hasFnAttribute("hlsl.shader"))
+      continue;
+    IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+    for (auto *Fn : CtorFns)
+      B.CreateCall(FunctionCallee(Fn));
+  }
+}

diff  --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 9bd698b325026..ee265922c0f51 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -46,6 +46,7 @@ class CGHLSLRuntime {
   virtual ~CGHLSLRuntime() {}
 
   void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV);
+  void generateGlobalCtorCalls();
 
   void finishCodeGen();
 

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 580ecf8995db1..128eefbf7197b 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2328,6 +2328,10 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
 
 static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   uint32_t priority = ConstructorAttr::DefaultPriority;
+  if (S.getLangOpts().HLSL && AL.getNumArgs()) {
+    S.Diag(AL.getLoc(), diag::err_hlsl_init_priority_unsupported);
+    return;
+  }
   if (AL.getNumArgs() &&
       !checkUInt32Argument(S, AL, AL.getArgAsExpr(0), priority))
     return;
@@ -3729,6 +3733,11 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
     S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL;
     return;
   }
+  
+  if (S.getLangOpts().HLSL) {
+    S.Diag(AL.getLoc(), diag::err_hlsl_init_priority_unsupported);
+    return;
+  }
 
   if (S.getCurFunctionOrMethodDecl()) {
     S.Diag(AL.getLoc(), diag::err_init_priority_object_attr);

diff  --git a/clang/test/CodeGenHLSL/GlobalConstructorFunction.hlsl b/clang/test/CodeGenHLSL/GlobalConstructorFunction.hlsl
new file mode 100644
index 0000000000000..d25e4551c7286
--- /dev/null
+++ b/clang/test/CodeGenHLSL/GlobalConstructorFunction.hlsl
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -S -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
+
+int i;
+
+__attribute__((constructor)) void call_me_first(void) {
+  i = 12;
+}
+
+__attribute__((constructor)) void then_call_me(void) {
+  i = 12;
+}
+
+[numthreads(1,1,1)]
+void main(unsigned GI : SV_GroupIndex) {}
+
+//CHECK: define void @main()
+//CHECK-NEXT: entry:
+//CHECK-NEXT:   call void @"?call_me_first@@YAXXZ"()
+//CHECK-NEXT:   call void @"?then_call_me@@YAXXZ"()
+//CHECK-NEXT:   %0 = call i32 @llvm.dx.flattened.thread.id.in.group()
+//CHECK-NEXT:   call void @"?main@@YAXI at Z"(i32 %0)
+//CHECK-NEXT:   ret void

diff  --git a/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl b/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl
new file mode 100644
index 0000000000000..4a366946219b4
--- /dev/null
+++ b/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -S -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
+
+RWBuffer<float> Buffer;
+
+[shader("compute")]
+[numthreads(1,1,1)]
+void FirstEntry() {}
+
+// CHECK: define void @FirstEntry()
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   call void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl()
+
+[shader("compute")]
+[numthreads(1,1,1)]
+void SecondEntry() {}
+
+// CHECK: define void @SecondEntry()
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   call void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl()
+// CHECK-NEXT:   call void @"?SecondEntry@@YAXXZ"()

diff  --git a/clang/test/CodeGenHLSL/GlobalConstructors.hlsl b/clang/test/CodeGenHLSL/GlobalConstructors.hlsl
new file mode 100644
index 0000000000000..cb250766099d5
--- /dev/null
+++ b/clang/test/CodeGenHLSL/GlobalConstructors.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -S -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
+
+RWBuffer<float> Buffer;
+
+[numthreads(1,1,1)]
+void main(unsigned GI : SV_GroupIndex) {}
+
+//CHECK:      define void @main()
+//CHECK-NEXT: entry:
+//CHECK-NEXT:   call void @_GLOBAL__sub_I_GlobalConstructors.hlsl()
+//CHECK-NEXT:   %0 = call i32 @llvm.dx.flattened.thread.id.in.group()
+//CHECK-NEXT:   call void @"?main@@YAXI at Z"(i32 %0)
+//CHECK-NEXT:   ret void

diff  --git a/clang/test/SemaHLSL/GlobalConstructors.hlsl b/clang/test/SemaHLSL/GlobalConstructors.hlsl
new file mode 100644
index 0000000000000..ddd09422d7485
--- /dev/null
+++ b/clang/test/SemaHLSL/GlobalConstructors.hlsl
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fsyntax-only %s -verify
+
+int i;
+
+struct Pup {
+  Pup() {
+    i++;
+  }
+};
+
+// expected-error at +1 {{initializer priorities are not supported in HLSL}}
+Pup __attribute__((init_priority(1))) Fido;
+
+// expected-error at +1 {{initializer priorities are not supported in HLSL}}
+__attribute__((constructor(1))) void call_me_first(void) {
+  i = 12;
+}
+


        


More information about the cfe-commits mailing list