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::
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">;
Triple T(M.getTargetTriple());
if (T.getArch() == Triple::ArchType::dxil)
addDxilValVersion(TargetOpts.DxilValidatorVersion, M);
+
+ generateGlobalCtorCalls();
}
void CGHLSLRuntime::annotateHLSLResource(const VarDecl *D, GlobalVariable *GV) {
// 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));
+ }
+}
virtual ~CGHLSLRuntime() {}
void annotateHLSLResource(const VarDecl *D, llvm::GlobalVariable *GV);
+ void generateGlobalCtorCalls();
void finishCodeGen();
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;
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);
--- /dev/null
+// 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@Z"(i32 %0)
+//CHECK-NEXT: ret void
--- /dev/null
+// 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"()
--- /dev/null
+// 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@Z"(i32 %0)
+//CHECK-NEXT: ret void
--- /dev/null
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fsyntax-only %s -verify
+
+int i;
+
+struct Pup {
+ Pup() {
+ i++;
+ }
+};
+
+// expected-error@+1 {{initializer priorities are not supported in HLSL}}
+Pup __attribute__((init_priority(1))) Fido;
+
+// expected-error@+1 {{initializer priorities are not supported in HLSL}}
+__attribute__((constructor(1))) void call_me_first(void) {
+ i = 12;
+}
+