llvm-project/clang/lib/CodeGen/CGHLSLRuntime.cpp

185 lines
6.6 KiB
C++

//===----- CGHLSLRuntime.cpp - Interface to HLSL Runtimes -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This provides an abstract class for HLSL code generation. Concrete
// subclasses of this implement code generation for specific HLSL
// runtime libraries.
//
//===----------------------------------------------------------------------===//
#include "CGHLSLRuntime.h"
#include "CodeGenModule.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/TargetOptions.h"
#include "llvm/IR/IntrinsicsDirectX.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
using namespace clang;
using namespace CodeGen;
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.
VersionTuple Version;
if (Version.tryParse(ValVersionStr) || Version.getBuild() ||
Version.getSubminor() || !Version.getMinor()) {
return;
}
uint64_t Major = Version.getMajor();
uint64_t Minor = *Version.getMinor();
auto &Ctx = M.getContext();
IRBuilder<> B(M.getContext());
MDNode *Val = MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(Major)),
ConstantAsMetadata::get(B.getInt32(Minor))});
StringRef DXILValKey = "dx.valver";
auto *DXILValMD = M.getOrInsertNamedMetadata(DXILValKey);
DXILValMD->addOperand(Val);
}
} // namespace
void CGHLSLRuntime::finishCodeGen() {
auto &TargetOpts = CGM.getTarget().getTargetOpts();
llvm::Module &M = CGM.getModule();
Triple T(M.getTargetTriple());
if (T.getArch() == Triple::ArchType::dxil)
addDxilValVersion(TargetOpts.DxilValidatorVersion, M);
generateGlobalCtorCalls();
}
void CGHLSLRuntime::annotateHLSLResource(const VarDecl *D, GlobalVariable *GV) {
const Type *Ty = D->getType()->getPointeeOrArrayElementType();
if (!Ty)
return;
const auto *RD = Ty->getAsCXXRecordDecl();
if (!RD)
return;
const auto *Attr = RD->getAttr<HLSLResourceAttr>();
if (!Attr)
return;
HLSLResourceAttr::ResourceClass RC = Attr->getResourceType();
uint32_t Counter = ResourceCounters[static_cast<uint32_t>(RC)]++;
NamedMDNode *ResourceMD = nullptr;
switch (RC) {
case HLSLResourceAttr::ResourceClass::UAV:
ResourceMD = CGM.getModule().getOrInsertNamedMetadata("hlsl.uavs");
break;
default:
assert(false && "Unsupported buffer type!");
return;
}
assert(ResourceMD != nullptr &&
"ResourceMD must have been set by the switch above.");
auto &Ctx = CGM.getModule().getContext();
IRBuilder<> B(Ctx);
QualType QT(Ty, 0);
ResourceMD->addOperand(MDNode::get(
Ctx, {ValueAsMetadata::get(GV), MDString::get(Ctx, QT.getAsString()),
ConstantAsMetadata::get(B.getInt32(Counter))}));
}
void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes(
const FunctionDecl *FD, llvm::Function *Fn) {
const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
assert(ShaderAttr && "All entry functions must have a HLSLShaderAttr");
const StringRef ShaderAttrKindStr = "hlsl.shader";
Fn->addFnAttr(ShaderAttrKindStr,
ShaderAttr->ConvertShaderTypeToStr(ShaderAttr->getType()));
}
llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B,
const ParmVarDecl &D) {
assert(D.hasAttrs() && "Entry parameter missing annotation attribute!");
if (D.hasAttr<HLSLSV_GroupIndexAttr>()) {
llvm::Function *DxGroupIndex =
CGM.getIntrinsic(Intrinsic::dx_flattened_thread_id_in_group);
return B.CreateCall(FunctionCallee(DxGroupIndex));
}
assert(false && "Unhandled parameter attribute");
return nullptr;
}
void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
llvm::Function *Fn) {
llvm::Module &M = CGM.getModule();
llvm::LLVMContext &Ctx = M.getContext();
auto *EntryTy = llvm::FunctionType::get(llvm::Type::getVoidTy(Ctx), false);
Function *EntryFn =
Function::Create(EntryTy, Function::ExternalLinkage, FD->getName(), &M);
// Copy function attributes over, we have no argument or return attributes
// that can be valid on the real entry.
AttributeList NewAttrs = AttributeList::get(Ctx, AttributeList::FunctionIndex,
Fn->getAttributes().getFnAttrs());
EntryFn->setAttributes(NewAttrs);
setHLSLEntryAttributes(FD, EntryFn);
// Set the called function as internal linkage.
Fn->setLinkage(GlobalValue::InternalLinkage);
BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
IRBuilder<> B(BB);
llvm::SmallVector<Value *> Args;
// FIXME: support struct parameters where semantics are on members.
for (const auto *Param : FD->parameters()) {
Args.push_back(emitInputSemantic(B, *Param));
}
CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
(void)CI;
// 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));
}
}