1610 строки
52 KiB
C++
1610 строки
52 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// DxilLinker.cpp //
|
|
// Copyright (C) Microsoft Corporation. All rights reserved. //
|
|
// This file is distributed under the University of Illinois Open Source //
|
|
// License. See LICENSE.TXT for details. //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "dxc/HLSL/DxilLinker.h"
|
|
#include "dxc/DXIL/DxilCBuffer.h"
|
|
#include "dxc/DXIL/DxilEntryProps.h"
|
|
#include "dxc/DXIL/DxilFunctionProps.h"
|
|
#include "dxc/DXIL/DxilModule.h"
|
|
#include "dxc/DXIL/DxilOperations.h"
|
|
#include "dxc/DXIL/DxilResource.h"
|
|
#include "dxc/DXIL/DxilSampler.h"
|
|
#include "dxc/DXIL/DxilUtil.h"
|
|
#include "dxc/Support/Global.h"
|
|
#include "llvm/ADT/DenseSet.h"
|
|
#include "llvm/ADT/MapVector.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SetVector.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Transforms/Utils/Cloning.h"
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include "dxc/DxilContainer/DxilContainer.h"
|
|
#include "llvm/IR/DebugInfo.h"
|
|
#include "llvm/IR/DiagnosticPrinter.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
|
|
#include "dxc/HLSL/DxilGenerationPass.h"
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
#include "llvm/Transforms/IPO.h"
|
|
#include "llvm/Transforms/Scalar.h"
|
|
|
|
#include "dxc/HLSL/ComputeViewIdState.h"
|
|
#include "dxc/HLSL/DxilExportMap.h"
|
|
|
|
using namespace llvm;
|
|
using namespace hlsl;
|
|
|
|
namespace {
|
|
|
|
void CollectUsedFunctions(Constant *C, llvm::SetVector<Function *> &funcSet) {
|
|
for (User *U : C->users()) {
|
|
if (Instruction *I = dyn_cast<Instruction>(U)) {
|
|
funcSet.insert(I->getParent()->getParent());
|
|
} else {
|
|
Constant *CU = cast<Constant>(U);
|
|
CollectUsedFunctions(CU, funcSet);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void AddResourceMap(
|
|
const std::vector<std::unique_ptr<T>> &resTab, DXIL::ResourceClass resClass,
|
|
llvm::MapVector<const llvm::Constant *, DxilResourceBase *> &resMap,
|
|
DxilModule &DM) {
|
|
for (auto &Res : resTab) {
|
|
resMap[Res->GetGlobalSymbol()] = Res.get();
|
|
}
|
|
}
|
|
|
|
void CloneFunction(Function *F, Function *NewF, ValueToValueMapTy &vmap,
|
|
hlsl::DxilTypeSystem *TypeSys = nullptr,
|
|
hlsl::DxilTypeSystem *SrcTypeSys = nullptr) {
|
|
SmallVector<ReturnInst *, 2> Returns;
|
|
// Map params.
|
|
auto paramIt = NewF->arg_begin();
|
|
for (Argument ¶m : F->args()) {
|
|
vmap[¶m] = (paramIt++);
|
|
}
|
|
|
|
llvm::CloneFunctionInto(NewF, F, vmap, /*ModuleLevelChanges*/ true, Returns);
|
|
if (TypeSys) {
|
|
if (SrcTypeSys == nullptr)
|
|
SrcTypeSys = TypeSys;
|
|
TypeSys->CopyFunctionAnnotation(NewF, F, *SrcTypeSys);
|
|
}
|
|
|
|
// Remove params from vmap.
|
|
for (Argument ¶m : F->args()) {
|
|
vmap.erase(¶m);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace {
|
|
|
|
struct DxilFunctionLinkInfo {
|
|
DxilFunctionLinkInfo(llvm::Function *F);
|
|
llvm::Function *func;
|
|
// SetVectors for deterministic iteration
|
|
llvm::SetVector<llvm::Function *> usedFunctions;
|
|
llvm::SetVector<llvm::GlobalVariable *> usedGVs;
|
|
};
|
|
|
|
// Library to link.
|
|
class DxilLib {
|
|
|
|
public:
|
|
DxilLib(std::unique_ptr<llvm::Module> pModule);
|
|
virtual ~DxilLib() {}
|
|
bool HasFunction(std::string &name);
|
|
llvm::StringMap<std::unique_ptr<DxilFunctionLinkInfo>> &GetFunctionTable() {
|
|
return m_functionNameMap;
|
|
}
|
|
bool IsInitFunc(llvm::Function *F);
|
|
bool IsEntry(llvm::Function *F);
|
|
bool IsResourceGlobal(const llvm::Constant *GV);
|
|
DxilResourceBase *GetResource(const llvm::Constant *GV);
|
|
|
|
DxilModule &GetDxilModule() { return m_DM; }
|
|
void LazyLoadFunction(Function *F);
|
|
void BuildGlobalUsage();
|
|
void CollectUsedInitFunctions(SetVector<StringRef> &addedFunctionSet,
|
|
SmallVector<StringRef, 4> &workList);
|
|
|
|
void FixIntrinsicOverloads();
|
|
|
|
private:
|
|
std::unique_ptr<llvm::Module> m_pModule;
|
|
DxilModule &m_DM;
|
|
// Map from name to Link info for extern functions.
|
|
llvm::StringMap<std::unique_ptr<DxilFunctionLinkInfo>> m_functionNameMap;
|
|
llvm::SmallPtrSet<llvm::Function *, 4> m_entrySet;
|
|
// Map from resource link global to resource. MapVector for deterministic
|
|
// iteration.
|
|
llvm::MapVector<const llvm::Constant *, DxilResourceBase *> m_resourceMap;
|
|
// Set of initialize functions for global variable. SetVector for
|
|
// deterministic iteration.
|
|
llvm::SetVector<llvm::Function *> m_initFuncSet;
|
|
};
|
|
|
|
struct DxilLinkJob;
|
|
|
|
class DxilLinkerImpl : public hlsl::DxilLinker {
|
|
public:
|
|
DxilLinkerImpl(LLVMContext &Ctx, unsigned valMajor, unsigned valMinor)
|
|
: DxilLinker(Ctx, valMajor, valMinor) {}
|
|
virtual ~DxilLinkerImpl() {}
|
|
bool HasLibNameRegistered(StringRef name) override;
|
|
bool RegisterLib(StringRef name, std::unique_ptr<llvm::Module> pModule,
|
|
std::unique_ptr<llvm::Module> pDebugModule) override;
|
|
bool AttachLib(StringRef name) override;
|
|
bool DetachLib(StringRef name) override;
|
|
void DetachAll() override;
|
|
|
|
std::unique_ptr<llvm::Module> Link(StringRef entry, StringRef profile,
|
|
dxilutil::ExportMap &exportMap) override;
|
|
|
|
private:
|
|
bool AttachLib(DxilLib *lib);
|
|
bool DetachLib(DxilLib *lib);
|
|
bool AddFunctions(SmallVector<StringRef, 4> &workList,
|
|
SetVector<DxilLib *> &libSet,
|
|
SetVector<StringRef> &addedFunctionSet,
|
|
DxilLinkJob &linkJob, bool bLazyLoadDone,
|
|
bool bAllowFuncionDecls);
|
|
// Attached libs to link.
|
|
std::unordered_set<DxilLib *> m_attachedLibs;
|
|
// Owner of all DxilLib.
|
|
StringMap<std::unique_ptr<DxilLib>> m_LibMap;
|
|
llvm::StringMap<std::pair<DxilFunctionLinkInfo *, DxilLib *>>
|
|
m_functionNameMap;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// DxilFunctionLinkInfo methods.
|
|
//
|
|
DxilFunctionLinkInfo::DxilFunctionLinkInfo(Function *F) : func(F) {
|
|
DXASSERT_NOMSG(F);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// DxilLib methods.
|
|
//
|
|
|
|
DxilLib::DxilLib(std::unique_ptr<llvm::Module> pModule)
|
|
: m_pModule(std::move(pModule)), m_DM(m_pModule->GetOrCreateDxilModule()) {
|
|
Module &M = *m_pModule;
|
|
const std::string MID = (Twine(M.getModuleIdentifier()) + ".").str();
|
|
|
|
// Collect function defines.
|
|
for (Function &F : M.functions()) {
|
|
if (F.isDeclaration())
|
|
continue;
|
|
if (F.getLinkage() == GlobalValue::LinkageTypes::InternalLinkage) {
|
|
// Add prefix to internal function.
|
|
F.setName(MID + F.getName());
|
|
}
|
|
m_functionNameMap[F.getName()] =
|
|
llvm::make_unique<DxilFunctionLinkInfo>(&F);
|
|
if (m_DM.IsEntry(&F))
|
|
m_entrySet.insert(&F);
|
|
}
|
|
|
|
// Update internal global name.
|
|
for (GlobalVariable &GV : M.globals()) {
|
|
if (GV.getLinkage() == GlobalValue::LinkageTypes::InternalLinkage) {
|
|
// Add prefix to internal global.
|
|
GV.setName(MID + GV.getName());
|
|
}
|
|
}
|
|
}
|
|
|
|
void DxilLib::FixIntrinsicOverloads() {
|
|
// Fix DXIL overload name collisions that may be caused by name
|
|
// collisions between dxil ops with different overload types,
|
|
// when those types may have had the same name in the original
|
|
// modules.
|
|
m_DM.GetOP()->FixOverloadNames();
|
|
}
|
|
|
|
void DxilLib::LazyLoadFunction(Function *F) {
|
|
DXASSERT(m_functionNameMap.count(F->getName()), "else invalid Function");
|
|
DxilFunctionLinkInfo *linkInfo = m_functionNameMap[F->getName()].get();
|
|
std::error_code EC = F->materialize();
|
|
DXASSERT_LOCALVAR(EC, !EC, "else fail to materialize");
|
|
|
|
// Build used functions for F.
|
|
for (auto &BB : F->getBasicBlockList()) {
|
|
for (auto &I : BB.getInstList()) {
|
|
if (CallInst *CI = dyn_cast<CallInst>(&I)) {
|
|
linkInfo->usedFunctions.insert(CI->getCalledFunction());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_DM.HasDxilFunctionProps(F)) {
|
|
DxilFunctionProps &props = m_DM.GetDxilFunctionProps(F);
|
|
if (props.IsHS()) {
|
|
// Add patch constant function to usedFunctions of entry.
|
|
Function *patchConstantFunc = props.ShaderProps.HS.patchConstantFunc;
|
|
linkInfo->usedFunctions.insert(patchConstantFunc);
|
|
}
|
|
}
|
|
// Used globals will be build before link.
|
|
}
|
|
|
|
void DxilLib::BuildGlobalUsage() {
|
|
Module &M = *m_pModule;
|
|
|
|
// Collect init functions for static globals.
|
|
if (GlobalVariable *Ctors = M.getGlobalVariable("llvm.global_ctors")) {
|
|
if (ConstantArray *CA = dyn_cast<ConstantArray>(Ctors->getInitializer())) {
|
|
for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e;
|
|
++i) {
|
|
if (isa<ConstantAggregateZero>(*i))
|
|
continue;
|
|
ConstantStruct *CS = cast<ConstantStruct>(*i);
|
|
if (isa<ConstantPointerNull>(CS->getOperand(1)))
|
|
continue;
|
|
|
|
// Must have a function or null ptr.
|
|
if (!isa<Function>(CS->getOperand(1)))
|
|
continue;
|
|
Function *Ctor = cast<Function>(CS->getOperand(1));
|
|
assert(Ctor->getReturnType()->isVoidTy() && Ctor->arg_size() == 0 &&
|
|
"function type must be void (void)");
|
|
// Add Ctor.
|
|
m_initFuncSet.insert(Ctor);
|
|
LazyLoadFunction(Ctor);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build used globals.
|
|
for (GlobalVariable &GV : M.globals()) {
|
|
llvm::SetVector<Function *> funcSet;
|
|
CollectUsedFunctions(&GV, funcSet);
|
|
for (Function *F : funcSet) {
|
|
DXASSERT(m_functionNameMap.count(F->getName()), "must exist in table");
|
|
DxilFunctionLinkInfo *linkInfo = m_functionNameMap[F->getName()].get();
|
|
linkInfo->usedGVs.insert(&GV);
|
|
}
|
|
}
|
|
|
|
// Build resource map.
|
|
AddResourceMap(m_DM.GetUAVs(), DXIL::ResourceClass::UAV, m_resourceMap, m_DM);
|
|
AddResourceMap(m_DM.GetSRVs(), DXIL::ResourceClass::SRV, m_resourceMap, m_DM);
|
|
AddResourceMap(m_DM.GetCBuffers(), DXIL::ResourceClass::CBuffer,
|
|
m_resourceMap, m_DM);
|
|
AddResourceMap(m_DM.GetSamplers(), DXIL::ResourceClass::Sampler,
|
|
m_resourceMap, m_DM);
|
|
}
|
|
|
|
void DxilLib::CollectUsedInitFunctions(SetVector<StringRef> &addedFunctionSet,
|
|
SmallVector<StringRef, 4> &workList) {
|
|
// Add init functions to used functions.
|
|
for (Function *Ctor : m_initFuncSet) {
|
|
DXASSERT(m_functionNameMap.count(Ctor->getName()),
|
|
"must exist in internal table");
|
|
DxilFunctionLinkInfo *linkInfo = m_functionNameMap[Ctor->getName()].get();
|
|
// If function other than Ctor used GV of Ctor.
|
|
// Add Ctor to usedFunctions for it.
|
|
for (GlobalVariable *GV : linkInfo->usedGVs) {
|
|
llvm::SetVector<Function *> funcSet;
|
|
CollectUsedFunctions(GV, funcSet);
|
|
bool bAdded = false;
|
|
for (Function *F : funcSet) {
|
|
if (F == Ctor)
|
|
continue;
|
|
// If F is added for link, add init func to workList.
|
|
if (addedFunctionSet.count(F->getName())) {
|
|
workList.emplace_back(Ctor->getName());
|
|
bAdded = true;
|
|
break;
|
|
}
|
|
}
|
|
if (bAdded)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DxilLib::HasFunction(std::string &name) {
|
|
return m_functionNameMap.count(name);
|
|
}
|
|
|
|
bool DxilLib::IsEntry(llvm::Function *F) { return m_entrySet.count(F); }
|
|
bool DxilLib::IsInitFunc(llvm::Function *F) { return m_initFuncSet.count(F); }
|
|
bool DxilLib::IsResourceGlobal(const llvm::Constant *GV) {
|
|
return m_resourceMap.count(GV);
|
|
}
|
|
DxilResourceBase *DxilLib::GetResource(const llvm::Constant *GV) {
|
|
if (IsResourceGlobal(GV))
|
|
return m_resourceMap[GV];
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
namespace {
|
|
// Create module from link defines.
|
|
struct DxilLinkJob {
|
|
DxilLinkJob(LLVMContext &Ctx, dxilutil::ExportMap &exportMap,
|
|
unsigned valMajor, unsigned valMinor)
|
|
: m_ctx(Ctx), m_exportMap(exportMap), m_valMajor(valMajor),
|
|
m_valMinor(valMinor) {}
|
|
std::unique_ptr<llvm::Module>
|
|
Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
|
|
const ShaderModel *pSM);
|
|
std::unique_ptr<llvm::Module> LinkToLib(const ShaderModel *pSM);
|
|
void StripDeadDebugInfo(llvm::Module &M);
|
|
// Fix issues when link to different shader model.
|
|
void FixShaderModelMismatch(llvm::Module &M);
|
|
void RunPreparePass(llvm::Module &M);
|
|
void AddFunction(std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair);
|
|
void AddFunction(llvm::Function *F);
|
|
|
|
private:
|
|
void LinkNamedMDNodes(Module *pM, ValueToValueMapTy &vmap);
|
|
void AddFunctionDecls(Module *pM);
|
|
bool AddGlobals(DxilModule &DM, ValueToValueMapTy &vmap);
|
|
void EmitCtorListForLib(Module *pM);
|
|
void CloneFunctions(ValueToValueMapTy &vmap);
|
|
void AddFunctions(DxilModule &DM, ValueToValueMapTy &vmap);
|
|
bool AddResource(DxilResourceBase *res, llvm::GlobalVariable *GV);
|
|
void AddResourceToDM(DxilModule &DM);
|
|
llvm::MapVector<DxilFunctionLinkInfo *, DxilLib *> m_functionDefs;
|
|
|
|
// Function decls, in order added.
|
|
llvm::MapVector<llvm::StringRef,
|
|
std::pair<llvm::SmallPtrSet<llvm::FunctionType *, 2>,
|
|
llvm::SmallVector<llvm::Function *, 2>>>
|
|
m_functionDecls;
|
|
|
|
// New created functions, in order added.
|
|
llvm::MapVector<llvm::StringRef, llvm::Function *> m_newFunctions;
|
|
|
|
// New created globals, in order added.
|
|
llvm::MapVector<llvm::StringRef, llvm::GlobalVariable *> m_newGlobals;
|
|
|
|
// Map for resource, ordered by name.
|
|
std::map<llvm::StringRef,
|
|
std::pair<DxilResourceBase *, llvm::GlobalVariable *>>
|
|
m_resourceMap;
|
|
|
|
LLVMContext &m_ctx;
|
|
dxilutil::ExportMap &m_exportMap;
|
|
unsigned m_valMajor, m_valMinor;
|
|
};
|
|
} // namespace
|
|
|
|
namespace {
|
|
const char kUndefFunction[] = "Cannot find definition of function ";
|
|
const char kRedefineFunction[] = "Definition already exists for function ";
|
|
const char kRedefineGlobal[] = "Definition already exists for global variable ";
|
|
const char kShaderKindMismatch[] =
|
|
"Profile mismatch between entry function and target profile:";
|
|
const char kNoEntryProps[] =
|
|
"Cannot find function property for entry function ";
|
|
const char kRedefineResource[] = "Resource already exists as ";
|
|
const char kInvalidValidatorVersion[] =
|
|
"Validator version does not support target profile ";
|
|
const char kExportNameCollision[] =
|
|
"Export name collides with another export: ";
|
|
const char kExportFunctionMissing[] = "Could not find target for export: ";
|
|
const char kNoFunctionsToExport[] = "Library has no functions to export";
|
|
} // namespace
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// DxilLinkJob methods.
|
|
//
|
|
|
|
namespace {
|
|
// Helper function to check type match.
|
|
bool IsMatchedType(Type *Ty0, Type *Ty);
|
|
|
|
StringRef RemoveNameSuffix(StringRef Name) {
|
|
size_t DotPos = Name.rfind('.');
|
|
if (DotPos != StringRef::npos && Name.back() != '.' &&
|
|
isdigit(static_cast<unsigned char>(Name[DotPos + 1])))
|
|
Name = Name.substr(0, DotPos);
|
|
return Name;
|
|
}
|
|
|
|
bool IsMatchedStructType(StructType *ST0, StructType *ST) {
|
|
StringRef Name0 = RemoveNameSuffix(ST0->getName());
|
|
StringRef Name = RemoveNameSuffix(ST->getName());
|
|
|
|
if (Name0 != Name)
|
|
return false;
|
|
|
|
if (ST0->getNumElements() != ST->getNumElements())
|
|
return false;
|
|
|
|
if (ST0->isLayoutIdentical(ST))
|
|
return true;
|
|
|
|
for (unsigned i = 0; i < ST->getNumElements(); i++) {
|
|
Type *Ty = ST->getElementType(i);
|
|
Type *Ty0 = ST0->getElementType(i);
|
|
if (!IsMatchedType(Ty, Ty0))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool IsMatchedArrayType(ArrayType *AT0, ArrayType *AT) {
|
|
if (AT0->getNumElements() != AT->getNumElements())
|
|
return false;
|
|
return IsMatchedType(AT0->getElementType(), AT->getElementType());
|
|
}
|
|
|
|
bool IsMatchedType(Type *Ty0, Type *Ty) {
|
|
if (Ty0->isStructTy() && Ty->isStructTy()) {
|
|
StructType *ST0 = cast<StructType>(Ty0);
|
|
StructType *ST = cast<StructType>(Ty);
|
|
return IsMatchedStructType(ST0, ST);
|
|
}
|
|
|
|
if (Ty0->isArrayTy() && Ty->isArrayTy()) {
|
|
ArrayType *AT0 = cast<ArrayType>(Ty0);
|
|
ArrayType *AT = cast<ArrayType>(Ty);
|
|
return IsMatchedArrayType(AT0, AT);
|
|
}
|
|
|
|
if (Ty0->isPointerTy() && Ty->isPointerTy()) {
|
|
if (Ty0->getPointerAddressSpace() != Ty->getPointerAddressSpace())
|
|
return false;
|
|
|
|
return IsMatchedType(Ty0->getPointerElementType(),
|
|
Ty->getPointerElementType());
|
|
}
|
|
|
|
return Ty0 == Ty;
|
|
}
|
|
} // namespace
|
|
|
|
bool DxilLinkJob::AddResource(DxilResourceBase *res, llvm::GlobalVariable *GV) {
|
|
if (m_resourceMap.count(res->GetGlobalName())) {
|
|
DxilResourceBase *res0 = m_resourceMap[res->GetGlobalName()].first;
|
|
Type *Ty0 = res0->GetHLSLType()->getPointerElementType();
|
|
Type *Ty = res->GetHLSLType()->getPointerElementType();
|
|
// Make sure res0 match res.
|
|
bool bMatch = IsMatchedType(Ty0, Ty);
|
|
if (!bMatch) {
|
|
// Report error.
|
|
dxilutil::EmitErrorOnGlobalVariable(
|
|
m_ctx, dyn_cast<GlobalVariable>(res->GetGlobalSymbol()),
|
|
Twine(kRedefineResource) + res->GetResClassName() + " for " +
|
|
res->GetGlobalName());
|
|
return false;
|
|
}
|
|
} else {
|
|
m_resourceMap[res->GetGlobalName()] = std::make_pair(res, GV);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DxilLinkJob::AddResourceToDM(DxilModule &DM) {
|
|
for (auto &it : m_resourceMap) {
|
|
DxilResourceBase *res = it.second.first;
|
|
GlobalVariable *GV = it.second.second;
|
|
unsigned ID = 0;
|
|
DxilResourceBase *basePtr = nullptr;
|
|
switch (res->GetClass()) {
|
|
case DXIL::ResourceClass::UAV: {
|
|
std::unique_ptr<DxilResource> pUAV = llvm::make_unique<DxilResource>();
|
|
DxilResource *ptr = pUAV.get();
|
|
// Copy the content.
|
|
*ptr = *(static_cast<DxilResource *>(res));
|
|
ID = DM.AddUAV(std::move(pUAV));
|
|
basePtr = &DM.GetUAV(ID);
|
|
} break;
|
|
case DXIL::ResourceClass::SRV: {
|
|
std::unique_ptr<DxilResource> pSRV = llvm::make_unique<DxilResource>();
|
|
DxilResource *ptr = pSRV.get();
|
|
// Copy the content.
|
|
*ptr = *(static_cast<DxilResource *>(res));
|
|
ID = DM.AddSRV(std::move(pSRV));
|
|
basePtr = &DM.GetSRV(ID);
|
|
} break;
|
|
case DXIL::ResourceClass::CBuffer: {
|
|
std::unique_ptr<DxilCBuffer> pCBuf = llvm::make_unique<DxilCBuffer>();
|
|
DxilCBuffer *ptr = pCBuf.get();
|
|
// Copy the content.
|
|
*ptr = *(static_cast<DxilCBuffer *>(res));
|
|
ID = DM.AddCBuffer(std::move(pCBuf));
|
|
basePtr = &DM.GetCBuffer(ID);
|
|
} break;
|
|
case DXIL::ResourceClass::Sampler: {
|
|
std::unique_ptr<DxilSampler> pSampler = llvm::make_unique<DxilSampler>();
|
|
DxilSampler *ptr = pSampler.get();
|
|
// Copy the content.
|
|
*ptr = *(static_cast<DxilSampler *>(res));
|
|
ID = DM.AddSampler(std::move(pSampler));
|
|
basePtr = &DM.GetSampler(ID);
|
|
|
|
} break;
|
|
default:
|
|
DXASSERT(false, "Invalid resource class");
|
|
break;
|
|
}
|
|
// Update ID.
|
|
basePtr->SetID(ID);
|
|
|
|
basePtr->SetGlobalSymbol(GV);
|
|
DM.GetLLVMUsed().push_back(GV);
|
|
}
|
|
// Prevent global vars used for resources from being deleted through
|
|
// optimizations while we still have hidden uses (pointers in resource
|
|
// vectors).
|
|
DM.EmitLLVMUsed();
|
|
}
|
|
|
|
void DxilLinkJob::LinkNamedMDNodes(Module *pM, ValueToValueMapTy &vmap) {
|
|
SetVector<Module *> moduleSet;
|
|
for (auto &it : m_functionDefs) {
|
|
DxilLib *pLib = it.second;
|
|
moduleSet.insert(pLib->GetDxilModule().GetModule());
|
|
}
|
|
// Link normal NamedMDNode.
|
|
// TODO: skip duplicate operands.
|
|
for (Module *pSrcM : moduleSet) {
|
|
const NamedMDNode *pSrcModFlags = pSrcM->getModuleFlagsMetadata();
|
|
for (const NamedMDNode &NMD : pSrcM->named_metadata()) {
|
|
// Don't link module flags here. Do them separately.
|
|
if (&NMD == pSrcModFlags)
|
|
continue;
|
|
// Skip dxil metadata which will be regenerated.
|
|
if (DxilMDHelper::IsKnownNamedMetaData(NMD))
|
|
continue;
|
|
NamedMDNode *DestNMD = pM->getOrInsertNamedMetadata(NMD.getName());
|
|
// Add Src elements into Dest node.
|
|
for (const MDNode *op : NMD.operands())
|
|
DestNMD->addOperand(MapMetadata(op, vmap, RF_None, /*TypeMap*/ nullptr,
|
|
/*ValMaterializer*/ nullptr));
|
|
}
|
|
}
|
|
// Link mod flags.
|
|
SetVector<MDNode *> flagSet;
|
|
for (Module *pSrcM : moduleSet) {
|
|
NamedMDNode *pSrcModFlags = pSrcM->getModuleFlagsMetadata();
|
|
if (pSrcModFlags) {
|
|
for (MDNode *flag : pSrcModFlags->operands()) {
|
|
flagSet.insert(flag);
|
|
}
|
|
}
|
|
}
|
|
// TODO: check conflict in flags.
|
|
if (!flagSet.empty()) {
|
|
NamedMDNode *ModFlags = pM->getOrInsertModuleFlagsMetadata();
|
|
for (MDNode *flag : flagSet) {
|
|
ModFlags->addOperand(flag);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DxilLinkJob::AddFunctionDecls(Module *pM) {
|
|
for (auto &it : m_functionDecls) {
|
|
for (auto F : it.second.second) {
|
|
Function *NewF = pM->getFunction(F->getName());
|
|
if (!NewF || F->getFunctionType() != NewF->getFunctionType()) {
|
|
NewF = Function::Create(F->getFunctionType(), F->getLinkage(),
|
|
F->getName(), pM);
|
|
NewF->setAttributes(F->getAttributes());
|
|
}
|
|
m_newFunctions[F->getName()] = NewF;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DxilLinkJob::AddGlobals(DxilModule &DM, ValueToValueMapTy &vmap) {
|
|
DxilTypeSystem &typeSys = DM.GetTypeSystem();
|
|
Module *pM = DM.GetModule();
|
|
bool bSuccess = true;
|
|
for (auto &it : m_functionDefs) {
|
|
DxilFunctionLinkInfo *linkInfo = it.first;
|
|
DxilLib *pLib = it.second;
|
|
DxilModule &tmpDM = pLib->GetDxilModule();
|
|
DxilTypeSystem &tmpTypeSys = tmpDM.GetTypeSystem();
|
|
for (GlobalVariable *GV : linkInfo->usedGVs) {
|
|
// Skip added globals.
|
|
if (m_newGlobals.count(GV->getName())) {
|
|
if (vmap.find(GV) == vmap.end()) {
|
|
if (DxilResourceBase *res = pLib->GetResource(GV)) {
|
|
// For resource of same name, if class and type match, just map to
|
|
// same NewGV.
|
|
GlobalVariable *NewGV = m_newGlobals[GV->getName()];
|
|
if (AddResource(res, NewGV)) {
|
|
vmap[GV] = NewGV;
|
|
} else {
|
|
bSuccess = false;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Redefine of global.
|
|
dxilutil::EmitErrorOnGlobalVariable(
|
|
m_ctx, GV, Twine(kRedefineGlobal) + GV->getName());
|
|
bSuccess = false;
|
|
}
|
|
continue;
|
|
}
|
|
Constant *Initializer = nullptr;
|
|
if (GV->hasInitializer())
|
|
Initializer = GV->getInitializer();
|
|
|
|
Type *Ty = GV->getType()->getElementType();
|
|
GlobalVariable *NewGV = new GlobalVariable(
|
|
*pM, Ty, GV->isConstant(), GV->getLinkage(), Initializer,
|
|
GV->getName(),
|
|
/*InsertBefore*/ nullptr, GV->getThreadLocalMode(),
|
|
GV->getType()->getAddressSpace(), GV->isExternallyInitialized());
|
|
|
|
m_newGlobals[GV->getName()] = NewGV;
|
|
|
|
vmap[GV] = NewGV;
|
|
|
|
typeSys.CopyTypeAnnotation(Ty, tmpTypeSys);
|
|
|
|
if (DxilResourceBase *res = pLib->GetResource(GV)) {
|
|
bSuccess &= AddResource(res, NewGV);
|
|
typeSys.CopyTypeAnnotation(res->GetHLSLType(), tmpTypeSys);
|
|
}
|
|
}
|
|
}
|
|
return bSuccess;
|
|
}
|
|
|
|
void DxilLinkJob::CloneFunctions(ValueToValueMapTy &vmap) {
|
|
for (auto &it : m_functionDefs) {
|
|
DxilFunctionLinkInfo *linkInfo = it.first;
|
|
|
|
Function *F = linkInfo->func;
|
|
Function *NewF = m_newFunctions[F->getName()];
|
|
|
|
// Add dxil functions to vmap.
|
|
for (Function *UsedF : linkInfo->usedFunctions) {
|
|
if (!vmap.count(UsedF)) {
|
|
// Extern function need match by name
|
|
DXASSERT(m_newFunctions.count(UsedF->getName()),
|
|
"Must have new function.");
|
|
vmap[UsedF] = m_newFunctions[UsedF->getName()];
|
|
}
|
|
}
|
|
|
|
CloneFunction(F, NewF, vmap);
|
|
}
|
|
}
|
|
|
|
void DxilLinkJob::AddFunctions(DxilModule &DM, ValueToValueMapTy &vmap) {
|
|
DxilTypeSystem &typeSys = DM.GetTypeSystem();
|
|
Module *pM = DM.GetModule();
|
|
for (auto &it : m_functionDefs) {
|
|
DxilFunctionLinkInfo *linkInfo = it.first;
|
|
DxilLib *pLib = it.second;
|
|
DxilModule &tmpDM = pLib->GetDxilModule();
|
|
DxilTypeSystem &tmpTypeSys = tmpDM.GetTypeSystem();
|
|
|
|
Function *F = linkInfo->func;
|
|
Function *NewF = Function::Create(F->getFunctionType(), F->getLinkage(),
|
|
F->getName(), pM);
|
|
NewF->setAttributes(F->getAttributes());
|
|
|
|
if (!NewF->hasFnAttribute(llvm::Attribute::NoInline))
|
|
NewF->addFnAttr(llvm::Attribute::AlwaysInline);
|
|
|
|
if (DxilFunctionAnnotation *funcAnnotation =
|
|
tmpTypeSys.GetFunctionAnnotation(F)) {
|
|
// Clone funcAnnotation to typeSys.
|
|
typeSys.CopyFunctionAnnotation(NewF, F, tmpTypeSys);
|
|
}
|
|
|
|
// Add to function map.
|
|
m_newFunctions[NewF->getName()] = NewF;
|
|
|
|
vmap[F] = NewF;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Module>
|
|
DxilLinkJob::Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
|
|
const ShaderModel *pSM) {
|
|
Function *entryFunc = entryLinkPair.first->func;
|
|
DxilModule &entryDM = entryLinkPair.second->GetDxilModule();
|
|
if (!entryDM.HasDxilFunctionProps(entryFunc)) {
|
|
// Cannot get function props.
|
|
dxilutil::EmitErrorOnFunction(m_ctx, entryFunc,
|
|
Twine(kNoEntryProps) + entryFunc->getName());
|
|
return nullptr;
|
|
}
|
|
|
|
DxilFunctionProps props = entryDM.GetDxilFunctionProps(entryFunc);
|
|
|
|
if (pSM->GetKind() != props.shaderKind) {
|
|
// Shader kind mismatch.
|
|
dxilutil::EmitErrorOnFunction(
|
|
m_ctx, entryFunc,
|
|
Twine(kShaderKindMismatch) + ShaderModel::GetKindName(pSM->GetKind()) +
|
|
" and " + ShaderModel::GetKindName(props.shaderKind));
|
|
return nullptr;
|
|
}
|
|
|
|
// Create new module.
|
|
std::unique_ptr<Module> pM =
|
|
llvm::make_unique<Module>(entryFunc->getName(), entryDM.GetCtx());
|
|
// Set target.
|
|
pM->setTargetTriple(entryDM.GetModule()->getTargetTriple());
|
|
// Add dxil operation functions before create DxilModule.
|
|
AddFunctionDecls(pM.get());
|
|
|
|
// Create DxilModule.
|
|
const bool bSkipInit = true;
|
|
DxilModule &DM = pM->GetOrCreateDxilModule(bSkipInit);
|
|
DM.SetShaderModel(pSM, entryDM.GetUseMinPrecision());
|
|
|
|
// Set Validator version.
|
|
DM.SetValidatorVersion(m_valMajor, m_valMinor);
|
|
|
|
ValueToValueMapTy vmap;
|
|
|
|
// Add function
|
|
AddFunctions(DM, vmap);
|
|
|
|
// Set Entry
|
|
Function *NewEntryFunc = m_newFunctions[entryFunc->getName()];
|
|
DM.SetEntryFunction(NewEntryFunc);
|
|
DM.SetEntryFunctionName(entryFunc->getName());
|
|
|
|
DxilEntryPropsMap EntryPropMap;
|
|
std::unique_ptr<DxilEntryProps> pProps =
|
|
llvm::make_unique<DxilEntryProps>(entryDM.GetDxilEntryProps(entryFunc));
|
|
EntryPropMap[NewEntryFunc] = std::move(pProps);
|
|
DM.ResetEntryPropsMap(std::move(EntryPropMap));
|
|
|
|
if (NewEntryFunc->hasFnAttribute(llvm::Attribute::AlwaysInline))
|
|
NewEntryFunc->removeFnAttr(llvm::Attribute::AlwaysInline);
|
|
if (props.IsHS()) {
|
|
Function *patchConstantFunc = props.ShaderProps.HS.patchConstantFunc;
|
|
Function *newPatchConstantFunc =
|
|
m_newFunctions[patchConstantFunc->getName()];
|
|
props.ShaderProps.HS.patchConstantFunc = newPatchConstantFunc;
|
|
|
|
if (newPatchConstantFunc->hasFnAttribute(llvm::Attribute::AlwaysInline))
|
|
newPatchConstantFunc->removeFnAttr(llvm::Attribute::AlwaysInline);
|
|
}
|
|
|
|
// Set root sig if exist.
|
|
if (!props.serializedRootSignature.empty()) {
|
|
DM.ResetSerializedRootSignature(props.serializedRootSignature);
|
|
props.serializedRootSignature.clear();
|
|
}
|
|
// Set EntryProps
|
|
DM.SetShaderProperties(&props);
|
|
|
|
// Add global
|
|
bool bSuccess = AddGlobals(DM, vmap);
|
|
if (!bSuccess)
|
|
return nullptr;
|
|
|
|
// Clone functions.
|
|
CloneFunctions(vmap);
|
|
|
|
// Call global constrctor.
|
|
IRBuilder<> Builder(dxilutil::FindAllocaInsertionPt(DM.GetEntryFunction()));
|
|
for (auto &it : m_functionDefs) {
|
|
DxilFunctionLinkInfo *linkInfo = it.first;
|
|
DxilLib *pLib = it.second;
|
|
// Skip constructor in entry lib which is already called for entries inside
|
|
// entry lib.
|
|
if (pLib == entryLinkPair.second)
|
|
continue;
|
|
Function *F = linkInfo->func;
|
|
if (pLib->IsInitFunc(F)) {
|
|
Function *NewF = m_newFunctions[F->getName()];
|
|
Builder.CreateCall(NewF);
|
|
}
|
|
}
|
|
|
|
// Refresh intrinsic cache.
|
|
DM.GetOP()->RefreshCache();
|
|
|
|
// Add resource to DM.
|
|
// This should be after functions cloned.
|
|
AddResourceToDM(DM);
|
|
|
|
// Link metadata like debug info.
|
|
LinkNamedMDNodes(pM.get(), vmap);
|
|
|
|
RunPreparePass(*pM);
|
|
|
|
return pM;
|
|
}
|
|
|
|
// Based on CodeGenModule::EmitCtorList.
|
|
void DxilLinkJob::EmitCtorListForLib(Module *pM) {
|
|
LLVMContext &Ctx = pM->getContext();
|
|
|
|
Type *VoidTy = Type::getVoidTy(Ctx);
|
|
Type *Int32Ty = Type::getInt32Ty(Ctx);
|
|
Type *VoidPtrTy = Type::getInt8PtrTy(Ctx);
|
|
// Ctor function type is void()*.
|
|
llvm::FunctionType *CtorFTy = llvm::FunctionType::get(VoidTy, false);
|
|
llvm::Type *CtorPFTy = llvm::PointerType::getUnqual(CtorFTy);
|
|
|
|
// Get the type of a ctor entry, { i32, void ()*, i8* }.
|
|
llvm::StructType *CtorStructTy = llvm::StructType::get(
|
|
Int32Ty, llvm::PointerType::getUnqual(CtorFTy), VoidPtrTy, nullptr);
|
|
|
|
// Construct the constructor and destructor arrays.
|
|
SmallVector<llvm::Constant *, 8> Ctors;
|
|
|
|
for (auto &it : m_functionDefs) {
|
|
DxilFunctionLinkInfo *linkInfo = it.first;
|
|
DxilLib *pLib = it.second;
|
|
|
|
Function *F = linkInfo->func;
|
|
if (pLib->IsInitFunc(F)) {
|
|
Function *NewF = m_newFunctions[F->getName()];
|
|
|
|
llvm::Constant *S[] = {llvm::ConstantInt::get(Int32Ty, 65535, false),
|
|
llvm::ConstantExpr::getBitCast(NewF, CtorPFTy),
|
|
(llvm::Constant::getNullValue(VoidPtrTy))};
|
|
Ctors.push_back(llvm::ConstantStruct::get(CtorStructTy, S));
|
|
}
|
|
}
|
|
|
|
if (!Ctors.empty()) {
|
|
const StringRef GlobalName = "llvm.global_ctors";
|
|
llvm::ArrayType *AT = llvm::ArrayType::get(CtorStructTy, Ctors.size());
|
|
new llvm::GlobalVariable(*pM, AT, false,
|
|
llvm::GlobalValue::AppendingLinkage,
|
|
llvm::ConstantArray::get(AT, Ctors), GlobalName);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Module> DxilLinkJob::LinkToLib(const ShaderModel *pSM) {
|
|
if (m_functionDefs.empty()) {
|
|
dxilutil::EmitErrorOnContext(m_ctx, Twine(kNoFunctionsToExport));
|
|
return nullptr;
|
|
}
|
|
DxilLib *pLib = m_functionDefs.begin()->second;
|
|
DxilModule &tmpDM = pLib->GetDxilModule();
|
|
// Create new module.
|
|
std::unique_ptr<Module> pM =
|
|
llvm::make_unique<Module>("merged_lib", tmpDM.GetCtx());
|
|
// Set target.
|
|
pM->setTargetTriple(tmpDM.GetModule()->getTargetTriple());
|
|
// Add dxil operation functions and external decls before create DxilModule.
|
|
AddFunctionDecls(pM.get());
|
|
|
|
// Create DxilModule.
|
|
const bool bSkipInit = true;
|
|
DxilModule &DM = pM->GetOrCreateDxilModule(bSkipInit);
|
|
DM.SetShaderModel(pSM, tmpDM.GetUseMinPrecision());
|
|
|
|
// Set Validator version.
|
|
DM.SetValidatorVersion(m_valMajor, m_valMinor);
|
|
|
|
ValueToValueMapTy vmap;
|
|
|
|
// Add function
|
|
AddFunctions(DM, vmap);
|
|
|
|
// Set DxilFunctionProps.
|
|
DxilEntryPropsMap EntryPropMap;
|
|
for (auto &it : m_functionDefs) {
|
|
DxilFunctionLinkInfo *linkInfo = it.first;
|
|
DxilLib *pLib = it.second;
|
|
DxilModule &tmpDM = pLib->GetDxilModule();
|
|
|
|
Function *F = linkInfo->func;
|
|
if (tmpDM.HasDxilEntryProps(F)) {
|
|
Function *NewF = m_newFunctions[F->getName()];
|
|
DxilEntryProps &props = tmpDM.GetDxilEntryProps(F);
|
|
std::unique_ptr<DxilEntryProps> pProps =
|
|
llvm::make_unique<DxilEntryProps>(props);
|
|
EntryPropMap[NewF] = std::move(pProps);
|
|
}
|
|
}
|
|
DM.ResetEntryPropsMap(std::move(EntryPropMap));
|
|
|
|
// Add global
|
|
bool bSuccess = AddGlobals(DM, vmap);
|
|
if (!bSuccess)
|
|
return nullptr;
|
|
|
|
// Clone functions.
|
|
CloneFunctions(vmap);
|
|
|
|
// Refresh intrinsic cache.
|
|
DM.GetOP()->RefreshCache();
|
|
|
|
// Add resource to DM.
|
|
// This should be after functions cloned.
|
|
AddResourceToDM(DM);
|
|
|
|
// Link metadata like debug info.
|
|
LinkNamedMDNodes(pM.get(), vmap);
|
|
|
|
// Build global.ctors.
|
|
EmitCtorListForLib(pM.get());
|
|
|
|
RunPreparePass(*pM);
|
|
|
|
if (!m_exportMap.empty()) {
|
|
m_exportMap.BeginProcessing();
|
|
|
|
DM.ClearDxilMetadata(*pM);
|
|
for (auto it = pM->begin(); it != pM->end();) {
|
|
Function *F = it++;
|
|
if (F->isDeclaration())
|
|
continue;
|
|
if (!m_exportMap.ProcessFunction(F, true)) {
|
|
// Remove Function not in exportMap.
|
|
DM.RemoveFunction(F);
|
|
|
|
// Only erase function if user is empty. The function can still be
|
|
// used by @llvm.global_ctors
|
|
if (F->user_empty())
|
|
F->eraseFromParent();
|
|
}
|
|
}
|
|
|
|
if (!m_exportMap.EndProcessing()) {
|
|
for (auto &name : m_exportMap.GetNameCollisions()) {
|
|
std::string escaped;
|
|
llvm::raw_string_ostream os(escaped);
|
|
dxilutil::PrintEscapedString(name, os);
|
|
dxilutil::EmitErrorOnContext(m_ctx,
|
|
Twine(kExportNameCollision) + os.str());
|
|
}
|
|
for (auto &name : m_exportMap.GetUnusedExports()) {
|
|
std::string escaped;
|
|
llvm::raw_string_ostream os(escaped);
|
|
dxilutil::PrintEscapedString(name, os);
|
|
dxilutil::EmitErrorOnContext(m_ctx,
|
|
Twine(kExportFunctionMissing) + os.str());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Rename the original, if necessary, then clone the rest
|
|
for (auto &it : m_exportMap.GetFunctionRenames()) {
|
|
Function *F = it.first;
|
|
auto &renames = it.second;
|
|
|
|
if (renames.empty())
|
|
continue;
|
|
|
|
auto itName = renames.begin();
|
|
|
|
// Rename the original, if necessary, then clone the rest
|
|
if (renames.find(F->getName()) == renames.end())
|
|
F->setName(*(itName++));
|
|
|
|
while (itName != renames.end()) {
|
|
if (F->getName() != *itName) {
|
|
Function *NewF = Function::Create(
|
|
F->getFunctionType(), GlobalValue::LinkageTypes::ExternalLinkage,
|
|
*itName, DM.GetModule());
|
|
ValueToValueMapTy vmap;
|
|
CloneFunction(F, NewF, vmap, &DM.GetTypeSystem());
|
|
// add DxilFunctionProps if entry
|
|
if (DM.HasDxilFunctionProps(F)) {
|
|
DM.CloneDxilEntryProps(F, NewF);
|
|
}
|
|
}
|
|
itName++;
|
|
}
|
|
}
|
|
|
|
DM.EmitDxilMetadata();
|
|
}
|
|
|
|
return pM;
|
|
}
|
|
|
|
void DxilLinkJob::AddFunction(
|
|
std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair) {
|
|
m_functionDefs[linkPair.first] = linkPair.second;
|
|
}
|
|
|
|
void DxilLinkJob::AddFunction(llvm::Function *F) {
|
|
// Rarely, DXIL op overloads could collide, due to different types with same
|
|
// name. Later, we will rename these functions, but for now, we need to
|
|
// prevent clobbering an existing entry.
|
|
auto &entry = m_functionDecls[F->getName()];
|
|
if (entry.first.insert(F->getFunctionType()).second)
|
|
entry.second.push_back(F);
|
|
}
|
|
|
|
// Clone of StripDeadDebugInfo::runOnModule.
|
|
// Also remove function which not not in current Module.
|
|
void DxilLinkJob::StripDeadDebugInfo(Module &M) {
|
|
LLVMContext &C = M.getContext();
|
|
// Find all debug info in F. This is actually overkill in terms of what we
|
|
// want to do, but we want to try and be as resilient as possible in the face
|
|
// of potential debug info changes by using the formal interfaces given to us
|
|
// as much as possible.
|
|
DebugInfoFinder F;
|
|
F.processModule(M);
|
|
|
|
// For each compile unit, find the live set of global variables/functions and
|
|
// replace the current list of potentially dead global variables/functions
|
|
// with the live list.
|
|
SmallVector<Metadata *, 64> LiveGlobalVariables;
|
|
SmallVector<Metadata *, 64> LiveSubprograms;
|
|
DenseSet<const MDNode *> VisitedSet;
|
|
|
|
for (DICompileUnit *DIC : F.compile_units()) {
|
|
// Create our live subprogram list.
|
|
bool SubprogramChange = false;
|
|
for (DISubprogram *DISP : DIC->getSubprograms()) {
|
|
// Make sure we visit each subprogram only once.
|
|
if (!VisitedSet.insert(DISP).second)
|
|
continue;
|
|
|
|
// If the function referenced by DISP is not null, the function is live.
|
|
if (Function *Func = DISP->getFunction()) {
|
|
LiveSubprograms.push_back(DISP);
|
|
if (Func->getParent() != &M)
|
|
DISP->replaceFunction(nullptr);
|
|
} else {
|
|
// Copy it in anyway even if there's no function. When function is
|
|
// inlined the function reference is gone, but the subprogram is still
|
|
// valid as scope.
|
|
LiveSubprograms.push_back(DISP);
|
|
}
|
|
}
|
|
|
|
// Create our live global variable list.
|
|
bool GlobalVariableChange = false;
|
|
for (DIGlobalVariable *DIG : DIC->getGlobalVariables()) {
|
|
// Make sure we only visit each global variable only once.
|
|
if (!VisitedSet.insert(DIG).second)
|
|
continue;
|
|
|
|
// If the global variable referenced by DIG is not null, the global
|
|
// variable is live.
|
|
if (Constant *CV = DIG->getVariable()) {
|
|
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(CV)) {
|
|
if (GV->getParent() == &M) {
|
|
LiveGlobalVariables.push_back(DIG);
|
|
} else {
|
|
GlobalVariableChange = true;
|
|
}
|
|
} else {
|
|
LiveGlobalVariables.push_back(DIG);
|
|
}
|
|
} else {
|
|
GlobalVariableChange = true;
|
|
}
|
|
}
|
|
|
|
// If we found dead subprograms or global variables, replace the current
|
|
// subprogram list/global variable list with our new live subprogram/global
|
|
// variable list.
|
|
if (SubprogramChange) {
|
|
DIC->replaceSubprograms(MDTuple::get(C, LiveSubprograms));
|
|
}
|
|
|
|
if (GlobalVariableChange) {
|
|
DIC->replaceGlobalVariables(MDTuple::get(C, LiveGlobalVariables));
|
|
}
|
|
|
|
// Reset lists for the next iteration.
|
|
LiveSubprograms.clear();
|
|
LiveGlobalVariables.clear();
|
|
}
|
|
}
|
|
|
|
// TODO: move FixShaderModelMismatch to separate file.
|
|
#include "dxc/DXIL/DxilInstructions.h"
|
|
namespace {
|
|
bool onlyUsedByAnnotateHandle(Value *V) {
|
|
bool bResult = true;
|
|
for (User *U : V->users()) {
|
|
CallInst *CI = dyn_cast<CallInst>(U);
|
|
if (!CI) {
|
|
bResult = false;
|
|
break;
|
|
}
|
|
DxilInst_AnnotateHandle Hdl(CI);
|
|
if (!Hdl) {
|
|
bResult = false;
|
|
break;
|
|
}
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
DxilResourceBase *
|
|
findResourceFromPtr(Value *Ptr, DxilModule &DM,
|
|
DenseMap<Value *, DxilResourceBase *> &PtrResMap) {
|
|
auto it = PtrResMap.find(Ptr);
|
|
if (Ptr)
|
|
return it->second;
|
|
DxilResourceBase *Res = nullptr;
|
|
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Ptr)) {
|
|
DXASSERT(false, "global resource should already in map");
|
|
} else {
|
|
// Not support allocaInst of resource when missing annotateHandle.
|
|
GEPOperator *GEP = cast<GEPOperator>(Ptr);
|
|
Res = findResourceFromPtr(GEP->getPointerOperand(), DM, PtrResMap);
|
|
}
|
|
PtrResMap[Ptr] = Res;
|
|
return Res;
|
|
}
|
|
|
|
template <typename T>
|
|
void addGVFromResTable(T &Tab,
|
|
DenseMap<Value *, DxilResourceBase *> &PtrResMap) {
|
|
for (auto &it : Tab) {
|
|
PtrResMap[it->GetGlobalSymbol()] = it.get();
|
|
}
|
|
}
|
|
|
|
// Make sure createHandleForLib is annotated before use.
|
|
bool addAnnotHandle(Module &M, DxilModule &DM) {
|
|
hlsl::OP *hlslOP = DM.GetOP();
|
|
auto *pSM = DM.GetShaderModel();
|
|
if (!pSM->IsSM66Plus())
|
|
return false;
|
|
// If no createHandleForLib, do nothing.
|
|
if (!hlslOP->IsDxilOpUsed(DXIL::OpCode::CreateHandleForLib))
|
|
return false;
|
|
|
|
Type *pVoidTy = Type::getVoidTy(M.getContext());
|
|
SmallVector<CallInst *, 4> Candidates;
|
|
for (Function &F : M) {
|
|
if (!F.isDeclaration())
|
|
continue;
|
|
if (!hlslOP->IsDxilOpFunc(&F))
|
|
continue;
|
|
DXIL::OpCodeClass opClass;
|
|
if (!hlslOP->GetOpCodeClass(&F, opClass))
|
|
continue;
|
|
if (opClass != DXIL::OpCodeClass::CreateHandleForLib)
|
|
continue;
|
|
for (User *U : F.users()) {
|
|
CallInst *CI = cast<CallInst>(U);
|
|
// Check user is annotateHandle.
|
|
if (onlyUsedByAnnotateHandle(CI))
|
|
continue;
|
|
Candidates.emplace_back(CI);
|
|
}
|
|
}
|
|
|
|
if (Candidates.empty())
|
|
return false;
|
|
|
|
DenseMap<Value *, DxilResourceBase *> PtrResMap;
|
|
// Add GV from resTable first.
|
|
addGVFromResTable(DM.GetCBuffers(), PtrResMap);
|
|
addGVFromResTable(DM.GetSRVs(), PtrResMap);
|
|
addGVFromResTable(DM.GetUAVs(), PtrResMap);
|
|
addGVFromResTable(DM.GetSamplers(), PtrResMap);
|
|
|
|
Function *annotHandleFn =
|
|
hlslOP->GetOpFunc(DXIL::OpCode::AnnotateHandle, pVoidTy);
|
|
Value *annotHandleArg =
|
|
hlslOP->GetI32Const((unsigned)DXIL::OpCode::AnnotateHandle);
|
|
// Replace createHandle with annotateHandle and createHandleFromBinding.
|
|
Type *resPropertyTy = hlslOP->GetResourcePropertiesType();
|
|
for (CallInst *CI : Candidates) {
|
|
DxilInst_CreateHandleForLib Hdl(CI);
|
|
LoadInst *Ld = cast<LoadInst>(Hdl.get_Resource());
|
|
Value *Ptr = Ld->getPointerOperand();
|
|
DxilResourceBase *Res = findResourceFromPtr(Ptr, DM, PtrResMap);
|
|
DXASSERT(Res, "fail to find resource when missing annotateHandle");
|
|
|
|
DxilResourceProperties RP = resource_helper::loadPropsFromResourceBase(Res);
|
|
Value *propertiesV =
|
|
resource_helper::getAsConstant(RP, resPropertyTy, *DM.GetShaderModel());
|
|
IRBuilder<> B(CI->getNextNode());
|
|
CallInst *annotHdl =
|
|
B.CreateCall(annotHandleFn, {annotHandleArg, CI, propertiesV});
|
|
CI->replaceAllUsesWith(annotHdl);
|
|
annotHdl->setArgOperand(DxilInst_AnnotateHandle::arg_res, CI);
|
|
}
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
void DxilLinkJob::FixShaderModelMismatch(llvm::Module &M) {
|
|
// TODO: fix more issues.
|
|
addAnnotHandle(M, M.GetDxilModule());
|
|
}
|
|
|
|
void DxilLinkJob::RunPreparePass(Module &M) {
|
|
StripDeadDebugInfo(M);
|
|
FixShaderModelMismatch(M);
|
|
|
|
DxilModule &DM = M.GetDxilModule();
|
|
const ShaderModel *pSM = DM.GetShaderModel();
|
|
|
|
legacy::PassManager PM;
|
|
PM.add(createDxilReinsertNopsPass());
|
|
PM.add(createAlwaysInlinerPass(/*InsertLifeTime*/ false));
|
|
|
|
// Remove unused functions.
|
|
PM.add(createDxilDeadFunctionEliminationPass());
|
|
|
|
// SROA
|
|
PM.add(createSROAPass(/*RequiresDomTree*/ false, /*SkipHLSLMat*/ false));
|
|
// For static global handle.
|
|
PM.add(createLowerStaticGlobalIntoAlloca());
|
|
|
|
// Remove MultiDimArray from function call arg.
|
|
PM.add(createMultiDimArrayToOneDimArrayPass());
|
|
|
|
// Lower matrix bitcast.
|
|
PM.add(createMatrixBitcastLowerPass());
|
|
|
|
// mem2reg.
|
|
PM.add(createPromoteMemoryToRegisterPass());
|
|
|
|
// Clean up vectors, and run mem2reg again
|
|
PM.add(createScalarizerPass());
|
|
PM.add(createPromoteMemoryToRegisterPass());
|
|
|
|
PM.add(createSimplifyInstPass());
|
|
PM.add(createCFGSimplificationPass());
|
|
|
|
PM.add(createDeadCodeEliminationPass());
|
|
PM.add(createGlobalDCEPass());
|
|
|
|
if (pSM->IsSM66Plus() && pSM->IsLib())
|
|
PM.add(createDxilMutateResourceToHandlePass());
|
|
PM.add(createDxilCleanupDynamicResourceHandlePass());
|
|
PM.add(createDxilLowerCreateHandleForLibPass());
|
|
PM.add(createDxilTranslateRawBuffer());
|
|
PM.add(createDxilFinalizeModulePass());
|
|
PM.add(createComputeViewIdStatePass());
|
|
PM.add(createDxilDeadFunctionEliminationPass());
|
|
PM.add(createNoPausePassesPass());
|
|
PM.add(createDxilEmitMetadataPass());
|
|
PM.add(createDxilFinalizePreservesPass());
|
|
|
|
PM.run(M);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// DxilLinkerImpl methods.
|
|
//
|
|
|
|
bool DxilLinkerImpl::HasLibNameRegistered(StringRef name) {
|
|
return m_LibMap.count(name);
|
|
}
|
|
|
|
bool DxilLinkerImpl::RegisterLib(StringRef name,
|
|
std::unique_ptr<llvm::Module> pModule,
|
|
std::unique_ptr<llvm::Module> pDebugModule) {
|
|
if (m_LibMap.count(name))
|
|
return false;
|
|
|
|
std::unique_ptr<llvm::Module> pM =
|
|
pDebugModule ? std::move(pDebugModule) : std::move(pModule);
|
|
|
|
if (!pM)
|
|
return false;
|
|
|
|
pM->setModuleIdentifier(name);
|
|
std::unique_ptr<DxilLib> pLib = llvm::make_unique<DxilLib>(std::move(pM));
|
|
m_LibMap[name] = std::move(pLib);
|
|
return true;
|
|
}
|
|
|
|
bool DxilLinkerImpl::AttachLib(StringRef name) {
|
|
auto iter = m_LibMap.find(name);
|
|
if (iter == m_LibMap.end()) {
|
|
return false;
|
|
}
|
|
|
|
return AttachLib(iter->second.get());
|
|
}
|
|
bool DxilLinkerImpl::DetachLib(StringRef name) {
|
|
auto iter = m_LibMap.find(name);
|
|
if (iter == m_LibMap.end()) {
|
|
return false;
|
|
}
|
|
return DetachLib(iter->second.get());
|
|
}
|
|
|
|
void DxilLinkerImpl::DetachAll() {
|
|
m_functionNameMap.clear();
|
|
m_attachedLibs.clear();
|
|
}
|
|
|
|
bool DxilLinkerImpl::AttachLib(DxilLib *lib) {
|
|
if (!lib) {
|
|
// Invalid arg.
|
|
return false;
|
|
}
|
|
|
|
if (m_attachedLibs.count(lib))
|
|
return false;
|
|
|
|
StringMap<std::unique_ptr<DxilFunctionLinkInfo>> &funcTable =
|
|
lib->GetFunctionTable();
|
|
bool bSuccess = true;
|
|
for (auto it = funcTable.begin(), e = funcTable.end(); it != e; it++) {
|
|
StringRef name = it->getKey();
|
|
if (m_functionNameMap.count(name)) {
|
|
// Redefine of function.
|
|
const DxilFunctionLinkInfo *DFLI = it->getValue().get();
|
|
dxilutil::EmitErrorOnFunction(m_ctx, DFLI->func,
|
|
Twine(kRedefineFunction) + name);
|
|
bSuccess = false;
|
|
continue;
|
|
}
|
|
m_functionNameMap[name] = std::make_pair(it->second.get(), lib);
|
|
}
|
|
|
|
if (bSuccess) {
|
|
m_attachedLibs.insert(lib);
|
|
} else {
|
|
for (auto it = funcTable.begin(), e = funcTable.end(); it != e; it++) {
|
|
StringRef name = it->getKey();
|
|
auto iter = m_functionNameMap.find(name);
|
|
|
|
if (iter == m_functionNameMap.end())
|
|
continue;
|
|
|
|
// Remove functions of lib.
|
|
if (m_functionNameMap[name].second == lib)
|
|
m_functionNameMap.erase(name);
|
|
}
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
bool DxilLinkerImpl::DetachLib(DxilLib *lib) {
|
|
if (!lib) {
|
|
// Invalid arg.
|
|
return false;
|
|
}
|
|
|
|
if (!m_attachedLibs.count(lib))
|
|
return false;
|
|
|
|
m_attachedLibs.erase(lib);
|
|
|
|
// Remove functions from lib.
|
|
StringMap<std::unique_ptr<DxilFunctionLinkInfo>> &funcTable =
|
|
lib->GetFunctionTable();
|
|
for (auto it = funcTable.begin(), e = funcTable.end(); it != e; it++) {
|
|
StringRef name = it->getKey();
|
|
m_functionNameMap.erase(name);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DxilLinkerImpl::AddFunctions(SmallVector<StringRef, 4> &workList,
|
|
SetVector<DxilLib *> &libSet,
|
|
SetVector<StringRef> &addedFunctionSet,
|
|
DxilLinkJob &linkJob, bool bLazyLoadDone,
|
|
bool bAllowFuncionDecls) {
|
|
while (!workList.empty()) {
|
|
StringRef name = workList.pop_back_val();
|
|
// Ignore added function.
|
|
if (addedFunctionSet.count(name))
|
|
continue;
|
|
if (!m_functionNameMap.count(name)) {
|
|
// Cannot find function, report error.
|
|
dxilutil::EmitErrorOnContext(m_ctx, Twine(kUndefFunction) + name);
|
|
return false;
|
|
}
|
|
|
|
std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair =
|
|
m_functionNameMap[name];
|
|
linkJob.AddFunction(linkPair);
|
|
|
|
DxilLib *pLib = linkPair.second;
|
|
libSet.insert(pLib);
|
|
if (!bLazyLoadDone) {
|
|
Function *F = linkPair.first->func;
|
|
pLib->LazyLoadFunction(F);
|
|
}
|
|
for (Function *F : linkPair.first->usedFunctions) {
|
|
if (hlsl::OP::IsDxilOpFunc(F) || F->isIntrinsic()) {
|
|
// Add dxil operations directly.
|
|
linkJob.AddFunction(F);
|
|
} else if (addedFunctionSet.count(F->getName()) == 0) {
|
|
if (bAllowFuncionDecls && F->isDeclaration() &&
|
|
!m_functionNameMap.count(F->getName())) {
|
|
// When linking to lib, use of undefined function is allowed; add
|
|
// directly.
|
|
linkJob.AddFunction(F);
|
|
} else {
|
|
// Push function name to work list.
|
|
workList.emplace_back(F->getName());
|
|
}
|
|
}
|
|
}
|
|
|
|
addedFunctionSet.insert(name);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<llvm::Module>
|
|
DxilLinkerImpl::Link(StringRef entry, StringRef profile,
|
|
dxilutil::ExportMap &exportMap) {
|
|
const ShaderModel *pSM = ShaderModel::GetByName(profile.data());
|
|
DXIL::ShaderKind kind = pSM->GetKind();
|
|
if (kind == DXIL::ShaderKind::Invalid ||
|
|
(kind >= DXIL::ShaderKind::RayGeneration &&
|
|
kind <= DXIL::ShaderKind::Callable)) {
|
|
// Invalid profile, user error emitted earlier with option check
|
|
llvm_unreachable("invalid profile kind to link");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!exportMap.empty() && kind != DXIL::ShaderKind::Library) {
|
|
llvm_unreachable("export map is only for library");
|
|
return nullptr;
|
|
}
|
|
|
|
// Verifying validator version supports the requested profile
|
|
unsigned minValMajor, minValMinor;
|
|
pSM->GetMinValidatorVersion(minValMajor, minValMinor);
|
|
if (minValMajor > m_valMajor ||
|
|
(minValMajor == m_valMajor && minValMinor > m_valMinor)) {
|
|
dxilutil::EmitErrorOnContext(m_ctx,
|
|
Twine(kInvalidValidatorVersion) + profile);
|
|
return nullptr;
|
|
}
|
|
|
|
DxilLinkJob linkJob(m_ctx, exportMap, m_valMajor, m_valMinor);
|
|
|
|
SetVector<DxilLib *> libSet;
|
|
SetVector<StringRef> addedFunctionSet;
|
|
|
|
bool bIsLib = pSM->IsLib();
|
|
if (!bIsLib) {
|
|
SmallVector<StringRef, 4> workList;
|
|
workList.emplace_back(entry);
|
|
|
|
if (!AddFunctions(workList, libSet, addedFunctionSet, linkJob,
|
|
/*bLazyLoadDone*/ false,
|
|
/*bAllowFuncionDecls*/ false))
|
|
return nullptr;
|
|
|
|
} else {
|
|
if (exportMap.empty() && !exportMap.isExportShadersOnly()) {
|
|
// Add every function for lib profile.
|
|
for (auto &it : m_functionNameMap) {
|
|
StringRef name = it.getKey();
|
|
std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair = it.second;
|
|
DxilFunctionLinkInfo *linkInfo = linkPair.first;
|
|
DxilLib *pLib = linkPair.second;
|
|
|
|
Function *F = linkInfo->func;
|
|
pLib->LazyLoadFunction(F);
|
|
|
|
linkJob.AddFunction(linkPair);
|
|
|
|
libSet.insert(pLib);
|
|
|
|
addedFunctionSet.insert(name);
|
|
}
|
|
// Add every dxil function and llvm intrinsic.
|
|
for (auto *pLib : libSet) {
|
|
auto &DM = pLib->GetDxilModule();
|
|
DM.GetOP();
|
|
auto *pM = DM.GetModule();
|
|
for (Function &F : pM->functions()) {
|
|
if (hlsl::OP::IsDxilOpFunc(&F) || F.isIntrinsic() ||
|
|
(F.isDeclaration() &&
|
|
m_functionNameMap.count(F.getName()) == 0)) {
|
|
// Add intrinsics and function decls still not defined in any lib
|
|
linkJob.AddFunction(&F);
|
|
}
|
|
}
|
|
}
|
|
} else if (exportMap.isExportShadersOnly()) {
|
|
SmallVector<StringRef, 4> workList;
|
|
for (auto *pLib : m_attachedLibs) {
|
|
auto &DM = pLib->GetDxilModule();
|
|
auto *pM = DM.GetModule();
|
|
for (Function &F : pM->functions()) {
|
|
if (!pLib->IsEntry(&F)) {
|
|
if (!F.isDeclaration()) {
|
|
// Set none entry to be internal so they could be removed.
|
|
F.setLinkage(GlobalValue::LinkageTypes::InternalLinkage);
|
|
}
|
|
continue;
|
|
}
|
|
workList.emplace_back(F.getName());
|
|
}
|
|
libSet.insert(pLib);
|
|
}
|
|
|
|
if (!AddFunctions(workList, libSet, addedFunctionSet, linkJob,
|
|
/*bLazyLoadDone*/ false,
|
|
/*bAllowFuncionDecls*/ false))
|
|
return nullptr;
|
|
} else {
|
|
SmallVector<StringRef, 4> workList;
|
|
|
|
// Only add exported functions.
|
|
for (auto &it : m_functionNameMap) {
|
|
StringRef name = it.getKey();
|
|
// Only add names exist in exportMap.
|
|
if (exportMap.IsExported(name))
|
|
workList.emplace_back(name);
|
|
}
|
|
|
|
if (!AddFunctions(workList, libSet, addedFunctionSet, linkJob,
|
|
/*bLazyLoadDone*/ false,
|
|
/*bAllowFuncionDecls*/ true))
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Save global users.
|
|
for (auto &pLib : libSet) {
|
|
pLib->BuildGlobalUsage();
|
|
}
|
|
|
|
SmallVector<StringRef, 4> workList;
|
|
// Save global ctor users.
|
|
for (auto &pLib : libSet) {
|
|
pLib->CollectUsedInitFunctions(addedFunctionSet, workList);
|
|
}
|
|
|
|
for (auto &pLib : libSet) {
|
|
pLib->FixIntrinsicOverloads();
|
|
}
|
|
|
|
// Add init functions if used.
|
|
// All init function already loaded in BuildGlobalUsage,
|
|
// so set bLazyLoadDone to true here.
|
|
// Decls should have been added to addedFunctionSet if lib,
|
|
// so set bAllowFuncionDecls is false here.
|
|
if (!AddFunctions(workList, libSet, addedFunctionSet, linkJob,
|
|
/*bLazyLoadDone*/ true,
|
|
/*bAllowFuncionDecls*/ false))
|
|
return nullptr;
|
|
|
|
if (!bIsLib) {
|
|
std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair =
|
|
m_functionNameMap[entry];
|
|
|
|
return linkJob.Link(entryLinkPair, pSM);
|
|
} else {
|
|
return linkJob.LinkToLib(pSM);
|
|
}
|
|
}
|
|
|
|
namespace hlsl {
|
|
|
|
DxilLinker *DxilLinker::CreateLinker(LLVMContext &Ctx, unsigned valMajor,
|
|
unsigned valMinor) {
|
|
return new DxilLinkerImpl(Ctx, valMajor, valMinor);
|
|
}
|
|
} // namespace hlsl
|