1 //===----- CGHLSLRuntime.cpp - Interface to HLSL Runtimes -----------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This provides an abstract class for HLSL code generation. Concrete
10 // subclasses of this implement code generation for specific HLSL
13 //===----------------------------------------------------------------------===//
15 #include "CGHLSLRuntime.h"
16 #include "CodeGenModule.h"
17 #include "clang/AST/Decl.h"
18 #include "clang/Basic/TargetOptions.h"
19 #include "llvm/IR/IntrinsicsDirectX.h"
20 #include "llvm/IR/Metadata.h"
21 #include "llvm/IR/Module.h"
23 using namespace clang
;
24 using namespace CodeGen
;
29 void addDxilValVersion(StringRef ValVersionStr
, llvm::Module
&M
) {
30 // The validation of ValVersionStr is done at HLSLToolChain::TranslateArgs.
31 // Assume ValVersionStr is legal here.
33 if (Version
.tryParse(ValVersionStr
) || Version
.getBuild() ||
34 Version
.getSubminor() || !Version
.getMinor()) {
38 uint64_t Major
= Version
.getMajor();
39 uint64_t Minor
= *Version
.getMinor();
41 auto &Ctx
= M
.getContext();
42 IRBuilder
<> B(M
.getContext());
43 MDNode
*Val
= MDNode::get(Ctx
, {ConstantAsMetadata::get(B
.getInt32(Major
)),
44 ConstantAsMetadata::get(B
.getInt32(Minor
))});
45 StringRef DXILValKey
= "dx.valver";
46 auto *DXILValMD
= M
.getOrInsertNamedMetadata(DXILValKey
);
47 DXILValMD
->addOperand(Val
);
49 void addDisableOptimizations(llvm::Module
&M
) {
50 StringRef Key
= "dx.disable_optimizations";
51 M
.addModuleFlag(llvm::Module::ModFlagBehavior::Override
, Key
, 1);
56 void CGHLSLRuntime::finishCodeGen() {
57 auto &TargetOpts
= CGM
.getTarget().getTargetOpts();
58 llvm::Module
&M
= CGM
.getModule();
59 Triple
T(M
.getTargetTriple());
60 if (T
.getArch() == Triple::ArchType::dxil
)
61 addDxilValVersion(TargetOpts
.DxilValidatorVersion
, M
);
63 generateGlobalCtorDtorCalls();
64 if (CGM
.getCodeGenOpts().OptimizationLevel
== 0)
65 addDisableOptimizations(M
);
68 void CGHLSLRuntime::annotateHLSLResource(const VarDecl
*D
, GlobalVariable
*GV
) {
69 const Type
*Ty
= D
->getType()->getPointeeOrArrayElementType();
72 const auto *RD
= Ty
->getAsCXXRecordDecl();
75 const auto *Attr
= RD
->getAttr
<HLSLResourceAttr
>();
79 HLSLResourceAttr::ResourceClass RC
= Attr
->getResourceType();
80 uint32_t Counter
= ResourceCounters
[static_cast<uint32_t>(RC
)]++;
82 NamedMDNode
*ResourceMD
= nullptr;
84 case HLSLResourceAttr::ResourceClass::UAV
:
85 ResourceMD
= CGM
.getModule().getOrInsertNamedMetadata("hlsl.uavs");
88 assert(false && "Unsupported buffer type!");
92 assert(ResourceMD
!= nullptr &&
93 "ResourceMD must have been set by the switch above.");
95 auto &Ctx
= CGM
.getModule().getContext();
98 ResourceMD
->addOperand(MDNode::get(
99 Ctx
, {ValueAsMetadata::get(GV
), MDString::get(Ctx
, QT
.getAsString()),
100 ConstantAsMetadata::get(B
.getInt32(Counter
))}));
103 void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes(
104 const FunctionDecl
*FD
, llvm::Function
*Fn
) {
105 const auto *ShaderAttr
= FD
->getAttr
<HLSLShaderAttr
>();
106 assert(ShaderAttr
&& "All entry functions must have a HLSLShaderAttr");
107 const StringRef ShaderAttrKindStr
= "hlsl.shader";
108 Fn
->addFnAttr(ShaderAttrKindStr
,
109 ShaderAttr
->ConvertShaderTypeToStr(ShaderAttr
->getType()));
112 llvm::Value
*CGHLSLRuntime::emitInputSemantic(IRBuilder
<> &B
,
113 const ParmVarDecl
&D
) {
114 assert(D
.hasAttrs() && "Entry parameter missing annotation attribute!");
115 if (D
.hasAttr
<HLSLSV_GroupIndexAttr
>()) {
116 llvm::Function
*DxGroupIndex
=
117 CGM
.getIntrinsic(Intrinsic::dx_flattened_thread_id_in_group
);
118 return B
.CreateCall(FunctionCallee(DxGroupIndex
));
120 assert(false && "Unhandled parameter attribute");
124 void CGHLSLRuntime::emitEntryFunction(const FunctionDecl
*FD
,
125 llvm::Function
*Fn
) {
126 llvm::Module
&M
= CGM
.getModule();
127 llvm::LLVMContext
&Ctx
= M
.getContext();
128 auto *EntryTy
= llvm::FunctionType::get(llvm::Type::getVoidTy(Ctx
), false);
130 Function::Create(EntryTy
, Function::ExternalLinkage
, FD
->getName(), &M
);
132 // Copy function attributes over, we have no argument or return attributes
133 // that can be valid on the real entry.
134 AttributeList NewAttrs
= AttributeList::get(Ctx
, AttributeList::FunctionIndex
,
135 Fn
->getAttributes().getFnAttrs());
136 EntryFn
->setAttributes(NewAttrs
);
137 setHLSLEntryAttributes(FD
, EntryFn
);
139 // Set the called function as internal linkage.
140 Fn
->setLinkage(GlobalValue::InternalLinkage
);
142 BasicBlock
*BB
= BasicBlock::Create(Ctx
, "entry", EntryFn
);
144 llvm::SmallVector
<Value
*> Args
;
145 // FIXME: support struct parameters where semantics are on members.
146 for (const auto *Param
: FD
->parameters()) {
147 Args
.push_back(emitInputSemantic(B
, *Param
));
150 CallInst
*CI
= B
.CreateCall(FunctionCallee(Fn
), Args
);
152 // FIXME: Handle codegen for return type semantics.
156 static void gatherFunctions(SmallVectorImpl
<Function
*> &Fns
, llvm::Module
&M
,
159 M
.getNamedGlobal(CtorOrDtor
? "llvm.global_ctors" : "llvm.global_dtors");
162 const auto *CA
= dyn_cast
<ConstantArray
>(GV
->getInitializer());
165 // The global_ctor array elements are a struct [Priority, Fn *, COMDat].
166 // HLSL neither supports priorities or COMDat values, so we will check those
167 // in an assert but not handle them.
169 llvm::SmallVector
<Function
*> CtorFns
;
170 for (const auto &Ctor
: CA
->operands()) {
171 if (isa
<ConstantAggregateZero
>(Ctor
))
173 ConstantStruct
*CS
= cast
<ConstantStruct
>(Ctor
);
175 assert(cast
<ConstantInt
>(CS
->getOperand(0))->getValue() == 65535 &&
176 "HLSL doesn't support setting priority for global ctors.");
177 assert(isa
<ConstantPointerNull
>(CS
->getOperand(2)) &&
178 "HLSL doesn't support COMDat for global ctors.");
179 Fns
.push_back(cast
<Function
>(CS
->getOperand(1)));
183 void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
184 llvm::Module
&M
= CGM
.getModule();
185 SmallVector
<Function
*> CtorFns
;
186 SmallVector
<Function
*> DtorFns
;
187 gatherFunctions(CtorFns
, M
, true);
188 gatherFunctions(DtorFns
, M
, false);
190 // Insert a call to the global constructor at the beginning of the entry block
191 // to externally exported functions. This is a bit of a hack, but HLSL allows
192 // global constructors, but doesn't support driver initialization of globals.
193 for (auto &F
: M
.functions()) {
194 if (!F
.hasFnAttribute("hlsl.shader"))
196 IRBuilder
<> B(&F
.getEntryBlock(), F
.getEntryBlock().begin());
197 for (auto *Fn
: CtorFns
)
198 B
.CreateCall(FunctionCallee(Fn
));
200 // Insert global dtors before the terminator of the last instruction
201 B
.SetInsertPoint(F
.back().getTerminator());
202 for (auto *Fn
: DtorFns
)
203 B
.CreateCall(FunctionCallee(Fn
));