This commit is contained in:
Xiang Li 2017-06-19 15:05:18 -07:00
Родитель 18c0c08032
Коммит f408830296
18 изменённых файлов: 1531 добавлений и 19 удалений

Просмотреть файл

@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////
// //
// DxilLinker.h //
// Copyright (C) Microsoft Corporation. All rights reserved. //
// This file is distributed under the University of Illinois Open Source //
// License. See LICENSE.TXT for details. //
// //
// Representation of HLSL Linker. //
// //
///////////////////////////////////////////////////////////////////////////////
#pragma once
#include <unordered_map>
#include <unordered_set>
#include "llvm/ADT/StringRef.h"
#include <memory>
#include "llvm/Support/ErrorOr.h"
namespace llvm {
class Function;
class GlobalVariable;
class Constant;
class Module;
class LLVMContext;
} // namespace llvm
namespace hlsl {
class DxilModule;
class DxilResourceBase;
// Linker for DxilModule.
class DxilLinker {
public:
virtual ~DxilLinker() {}
static DxilLinker *CreateLinker(llvm::LLVMContext &Ctx);
virtual bool HasLibNameRegistered(llvm::StringRef name) = 0;
virtual bool RegisterLib(llvm::StringRef name,
std::unique_ptr<llvm::Module> pModule,
std::unique_ptr<llvm::Module> pDebugModule) = 0;
virtual bool AttachLib(llvm::StringRef name) = 0;
virtual bool DetachLib(llvm::StringRef name) = 0;
virtual void DetachAll() = 0;
virtual std::unique_ptr<llvm::Module> Link(llvm::StringRef entry,
llvm::StringRef profile) = 0;
protected:
DxilLinker(llvm::LLVMContext &Ctx) : m_ctx(Ctx) {}
llvm::LLVMContext &m_ctx;
};
} // namespace hlsl

Просмотреть файл

@ -182,6 +182,31 @@ IDxcCompiler2 : public IDxcCompiler {
) = 0;
};
struct __declspec(uuid("F1B5BE2A-62DD-4327-A1C2-42AC1E1E78E6"))
IDxcLinker : public IUnknown {
public:
// Register a library with name to ref it later.
virtual HRESULT RegisterLibrary(
_In_opt_ LPCWSTR pLibName, // Name of the library.
_In_ IDxcBlob *pLib // Library blob.
) = 0;
// Links the shader and produces a shader blob that the Direct3D runtime can
// use.
virtual HRESULT STDMETHODCALLTYPE Link(
_In_opt_ LPCWSTR pEntryName, // Entry point name
_In_ LPCWSTR pTargetProfile, // shader profile to link
_In_count_(libCount)
const LPCWSTR *pLibNames, // Array of library names to link
UINT32 libCount, // Number of libraries to link
_In_count_(argCount)
const LPCWSTR *pArguments, // Array of pointers to arguments
_In_ UINT32 argCount, // Number of arguments
_COM_Outptr_ IDxcOperationResult *
*ppResult // Linker output status, buffer, and errors
) = 0;
};
static const UINT32 DxcValidatorFlags_Default = 0;
static const UINT32 DxcValidatorFlags_InPlaceEdit = 1; // Validator is allowed to update shader blob in-place.
static const UINT32 DxcValidatorFlags_RootSignatureOnly = 2;
@ -262,6 +287,14 @@ __declspec(selectany) extern const CLSID CLSID_DxcCompiler = {
{ 0xb5, 0xbf, 0xf0, 0x66, 0x4f, 0x39, 0xc1, 0xb0 }
};
// {EF6A8087-B0EA-4D56-9E45-D07E1A8B7806}
__declspec(selectany) extern const GUID CLSID_DxcLinker = {
0xef6a8087,
0xb0ea,
0x4d56,
{0x9e, 0x45, 0xd0, 0x7e, 0x1a, 0x8b, 0x78, 0x6}
};
// {CD1F6B73-2AB0-484D-8EDC-EBE7A43CA09F}
__declspec(selectany) extern const CLSID CLSID_DxcDiaDataSource = {
0xcd1f6b73,

Просмотреть файл

@ -14,6 +14,7 @@ add_llvm_library(LLVMHLSL
DxilGenerationPass.cpp
DxilInterpolationMode.cpp
DxilLegalizeSampleOffsetPass.cpp
DxilLinker.cpp
DxilMetadataHelper.cpp
DxilModule.cpp
DxilOperations.cpp

786
lib/HLSL/DxilLinker.cpp Normal file
Просмотреть файл

@ -0,0 +1,786 @@
///////////////////////////////////////////////////////////////////////////////
// //
// 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/HLSL/DxilCBuffer.h"
#include "dxc/HLSL/DxilFunctionProps.h"
#include "dxc/HLSL/DxilModule.h"
#include "dxc/HLSL/DxilOperations.h"
#include "dxc/HLSL/DxilResource.h"
#include "dxc/HLSL/DxilSampler.h"
#include "dxc/Support/Global.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/Transforms/Utils/Cloning.h"
#include "llvm/ADT/StringMap.h"
#include <memory>
#include <vector>
#include "dxc/HLSL/DxilContainer.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"
using namespace llvm;
using namespace hlsl;
namespace {
void CollectUsedFunctions(Constant *C,
std::unordered_set<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,
std::unordered_map<const llvm::Constant *, DxilResourceBase *> &resMap,
DxilModule &DM) {
for (auto &Res : resTab) {
const DxilModule::ResourceLinkInfo &linkInfo =
DM.GetResourceLinkInfo(resClass, Res->GetID());
resMap[linkInfo.ResRangeID] = Res.get();
}
}
void CloneFunction(Function *F, Function *NewF, ValueToValueMapTy &vmap) {
SmallVector<ReturnInst *, 2> Returns;
// Map params.
auto paramIt = NewF->arg_begin();
for (Argument &param : F->args()) {
vmap[&param] = (paramIt++);
}
llvm::CloneFunctionInto(NewF, F, vmap, /*ModuleLevelChanges*/ true, Returns);
// Remove params from vmap.
for (Argument &param : F->args()) {
vmap.erase(&param);
}
}
} // namespace
namespace {
struct DxilFunctionLinkInfo {
DxilFunctionLinkInfo(llvm::Function *F);
llvm::Function *func;
std::unordered_set<llvm::Function *> usedFunctions;
std::unordered_set<llvm::GlobalVariable *> usedGVs;
std::unordered_set<DxilResourceBase *> usedResources;
};
// 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 IsResourceGlobal(const llvm::Constant *GV);
DxilResourceBase *GetResource(const llvm::Constant *GV);
DxilModule &GetDxilModule() { return m_DM; }
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;
// Map from resource link global to resource.
std::unordered_map<const llvm::Constant *, DxilResourceBase *> m_resourceMap;
// Set of initialize functions for global variable.
std::unordered_set<llvm::Function *> m_initFuncSet;
};
class DxilLinkerImpl : public hlsl::DxilLinker {
public:
DxilLinkerImpl(LLVMContext &Ctx) : DxilLinker(Ctx) {}
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) override;
private:
bool AttachLib(DxilLib *lib);
bool DetachLib(DxilLib *lib);
// 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 = M.getModuleIdentifier();
// 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);
}
// Build LinkInfo for each define.
for (Function &F : M.functions()) {
for (User *U : F.users()) {
// Skip ConstantStruct user of Construct function for static globals.
if (isa<ConstantStruct>(U))
continue;
CallInst *CI = cast<CallInst>(U);
Function *UserF = CI->getParent()->getParent();
DXASSERT(m_functionNameMap.count(UserF->getName()),
"must exist in internal table");
DxilFunctionLinkInfo *linkInfo =
m_functionNameMap[UserF->getName()].get();
linkInfo->usedFunctions.insert(&F);
}
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;
DXASSERT(m_functionNameMap.count(F.getName()),
"must exist in internal table");
DxilFunctionLinkInfo *linkInfo = m_functionNameMap[F.getName()].get();
linkInfo->usedFunctions.insert(patchConstantFunc);
}
}
}
for (GlobalVariable &GV : M.globals()) {
if (GV.getLinkage() == GlobalValue::LinkageTypes::InternalLinkage) {
// Add prefix to internal global.
GV.setName(MID + GV.getName());
}
std::unordered_set<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);
// 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);
}
}
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) {
std::unordered_set<Function *> funcSet;
CollectUsedFunctions(GV, funcSet);
for (Function *F : funcSet) {
if (F == Ctor)
continue;
DXASSERT(m_functionNameMap.count(F->getName()),
"must exist in table");
DxilFunctionLinkInfo *linkInfo =
m_functionNameMap[F->getName()].get();
linkInfo->usedFunctions.insert(Ctor);
}
}
}
}
}
bool DxilLib::HasFunction(std::string &name) {
return m_functionNameMap.count(name);
}
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) : m_ctx(Ctx) {}
std::unique_ptr<llvm::Module>
Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
StringRef profile);
void RunPreparePass(llvm::Module &M);
void AddFunction(std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair);
void AddFunction(llvm::Function *F);
private:
bool AddResource(DxilResourceBase *res, llvm::GlobalVariable *GV);
void AddResourceToDM(DxilModule &DM);
std::unordered_map<DxilFunctionLinkInfo *, DxilLib *> m_functionDefs;
llvm::StringMap<llvm::Function *> m_dxilFunctions;
// New created functions.
llvm::StringMap<llvm::Function *> m_newFunctions;
// New created globals.
llvm::StringMap<llvm::GlobalVariable *> m_newGlobals;
// Map for resource.
llvm::StringMap<std::pair<DxilResourceBase *, llvm::GlobalVariable *>>
m_resourceMap;
LLVMContext &m_ctx;
};
} // 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 kInvalidProfile[] = " is invalid profile to link";
const char kShaderKindMismatch[] =
"Profile mismatch between entry function and target profile:";
const char kNoEntryProps[] =
"Cannot find function property for entry function ";
const char kRefineResource[] =
"Resource already exists as ";
} // namespace
//------------------------------------------------------------------------------
//
// DxilLinkJob methods.
//
bool DxilLinkJob::AddResource(DxilResourceBase *res, llvm::GlobalVariable *GV) {
if (m_resourceMap.count(res->GetGlobalName())) {
DxilResourceBase *res0 = m_resourceMap[res->GetGlobalName()].first;
// Make sure res0 match res.
bool bMatch = res->GetGlobalSymbol()->getType() == res0->GetGlobalSymbol()->getType();
if (!bMatch) {
// Report error.
m_ctx.emitError(Twine(kRefineResource) + 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;
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));
} 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));
} 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));
} 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));
}
default:
DXASSERT(res->GetClass() == DXIL::ResourceClass::Sampler,
"else invalid resource");
break;
}
Constant *rangeID = ConstantInt::get(GV->getType()->getElementType(), ID);
for (User *U : GV->users()) {
LoadInst *LI = cast<LoadInst>(U);
LI->replaceAllUsesWith(rangeID);
}
}
}
std::unique_ptr<Module>
DxilLinkJob::Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
StringRef profile) {
Function *entryFunc = entryLinkPair.first->func;
DxilModule &entryDM = entryLinkPair.second->GetDxilModule();
if (!entryDM.HasDxilFunctionProps(entryFunc)) {
// Cannot get function props.
m_ctx.emitError(Twine(kNoEntryProps) + entryFunc->getName());
return nullptr;
}
DxilFunctionProps props = entryDM.GetDxilFunctionProps(entryFunc);
if (props.shaderKind == DXIL::ShaderKind::Library ||
props.shaderKind == DXIL::ShaderKind::Invalid) {
m_ctx.emitError(profile + Twine(kInvalidProfile));
// Invalid profile.
return nullptr;
}
const ShaderModel *pSM = ShaderModel::GetByName(profile.data());
if (pSM->GetKind() != props.shaderKind) {
// Shader kind mismatch.
m_ctx.emitError(Twine(kShaderKindMismatch) + profile + " 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.
for (auto &it : m_dxilFunctions) {
Function *F = it.second;
Function *NewF = Function::Create(F->getFunctionType(), F->getLinkage(),
F->getName(), pM.get());
NewF->setAttributes(F->getAttributes());
m_newFunctions[NewF->getName()] = NewF;
}
// Create DxilModule.
const bool bSkipInit = true;
DxilModule &DM = pM->GetOrCreateDxilModule(bSkipInit);
DM.SetShaderModel(pSM);
// Add type sys
DxilTypeSystem &typeSys = DM.GetTypeSystem();
ValueToValueMapTy vmap;
std::unordered_set<Function *> initFuncSet;
// Add function
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.get());
NewF->setAttributes(F->getAttributes());
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;
if (pLib->IsInitFunc(F))
initFuncSet.insert(NewF);
vmap[F] = NewF;
}
// Set Entry
Function *NewEntryFunc = m_newFunctions[entryFunc->getName()];
DM.SetEntryFunction(NewEntryFunc);
DM.SetEntryFunctionName(entryFunc->getName());
if (entryDM.HasDxilEntrySignature(entryFunc)) {
// Add signature.
DxilEntrySignature &entrySig = entryDM.GetDxilEntrySignature(entryFunc);
std::unique_ptr<DxilEntrySignature> newSig =
std::make_unique<DxilEntrySignature>(entrySig);
DM.ResetEntrySignature(newSig.release());
}
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;
newPatchConstantFunc->removeFnAttr(llvm::Attribute::AlwaysInline);
}
// Set EntryProps
DM.SetShaderProperties(&props);
// Debug info.
// Add global
bool bSuccess = true;
for (auto &it : m_functionDefs) {
DxilFunctionLinkInfo *linkInfo = it.first;
DxilLib *pLib = it.second;
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.
m_ctx.emitError(Twine(kRedefineGlobal) + GV->getName());
bSuccess = false;
}
continue;
}
Constant *Initializer = nullptr;
if (GV->hasInitializer())
Initializer = GV->getInitializer();
GlobalVariable *NewGV = new GlobalVariable(
*pM, GV->getType()->getElementType(), GV->isConstant(),
GV->getLinkage(), Initializer, GV->getName(),
/*InsertBefore*/ nullptr, GV->getThreadLocalMode(),
GV->getType()->getAddressSpace(), GV->isExternallyInitialized());
m_newGlobals[GV->getName()] = NewGV;
vmap[GV] = NewGV;
if (DxilResourceBase *res = pLib->GetResource(GV)) {
bSuccess &= AddResource(res, NewGV);
}
}
}
if (!bSuccess)
return nullptr;
// Clone functions.
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);
}
// Call global constrctor.
IRBuilder<> Builder(
DM.GetEntryFunction()->getEntryBlock().getFirstInsertionPt());
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()];
Builder.CreateCall(NewF);
}
}
// Refresh intrinsic cache.
DM.GetOP()->RefreshCache();
// Add resource to DM.
// This should be after functions cloned.
AddResourceToDM(DM);
RunPreparePass(*pM);
return pM;
}
void DxilLinkJob::AddFunction(
std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair) {
m_functionDefs[linkPair.first] = linkPair.second;
}
void DxilLinkJob::AddFunction(llvm::Function *F) {
m_dxilFunctions[F->getName()] = F;
}
void DxilLinkJob::RunPreparePass(Module &M) {
legacy::PassManager PM;
PM.add(createAlwaysInlinerPass(/*InsertLifeTime*/ false));
// Remove unused functions.
PM.add(createDeadCodeEliminationPass());
PM.add(createGlobalDCEPass());
PM.add(createSimplifyInstPass());
PM.add(createCFGSimplificationPass());
PM.add(createDxilCondenseResourcesPass());
PM.add(createComputeViewIdStatePass());
PM.add(createDxilEmitMetadataPass());
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 = std::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.
m_ctx.emitError(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;
}
std::unique_ptr<llvm::Module> DxilLinkerImpl::Link(StringRef entry,
StringRef profile) {
StringSet<> addedFunctionSet;
SmallVector<StringRef, 4> workList;
workList.emplace_back(entry);
DxilLinkJob linkJob(m_ctx);
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.
m_ctx.emitError(Twine(kUndefFunction) + name);
return nullptr;
}
std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair =
m_functionNameMap[name];
linkJob.AddFunction(linkPair);
for (Function *F : linkPair.first->usedFunctions) {
if (hlsl::OP::IsDxilOpFunc(F)) {
// Add dxil operations directly.
linkJob.AddFunction(F);
} else {
// Push function name to work list.
workList.emplace_back(F->getName());
}
}
addedFunctionSet.insert(name);
}
std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair =
m_functionNameMap[entry];
return linkJob.Link(entryLinkPair, profile);
}
namespace hlsl {
DxilLinker *DxilLinker::CreateLinker(LLVMContext &Ctx) {
return new DxilLinkerImpl(Ctx);
}
} // namespace hlsl

Просмотреть файл

@ -12,8 +12,8 @@
// CHECK: dx.func.props
// Make sure function props is correct for [numthreads(8,8,1)].
// CHECK: @entry, i32 5, i32 8, i32 8, i32 1
// CHECK: @entry2, i32 5, i32 8, i32 8, i32 1
// CHECK: @entry{{.*}}, i32 5, i32 8, i32 8, i32 1
// CHECK: @entry{{.*}}, i32 5, i32 8, i32 8, i32 1
[numthreads(8,8,1)]
void entry( uint2 tid : SV_DispatchThreadID, uint2 gid : SV_GroupID, uint2 gtid : SV_GroupThreadID, uint gidx : SV_GroupIndex )

Просмотреть файл

@ -0,0 +1,21 @@
// Make constructor for static global variable is called.
Texture2D g_txDiffuse;
SamplerState g_samLinear;
cbuffer X {
float f;
}
static float g[2] = { 1, f };
[shader("pixel")]
float4 test(float2 c : C) : SV_TARGET
{
float4 x = g_txDiffuse.Sample( g_samLinear, c );
return x + g[1];
}
void update() {
g[1]++;
}

Просмотреть файл

@ -0,0 +1,18 @@
// Check redefine global and resource with lib_global3.hlsl, lib_global4.hlsl.
float2x2 RotateMat(float2x2 m, uint x, uint y);
float2x2 MatRotate(float2x2 m, uint x, uint y);
RWStructuredBuffer<float2x2> fA;
[numthreads(8,8,1)]
void entry( uint2 tid : SV_DispatchThreadID, uint2 gid : SV_GroupID, uint2 gtid : SV_GroupThreadID, uint gidx : SV_GroupIndex )
{
float2x2 f2x2 = fA[tid.x];
f2x2 = RotateMat(f2x2, tid.x, tid.y);
f2x2 = MatRotate(f2x2, tid.x, tid.y);
fA[tid.y] = f2x2;
}

Просмотреть файл

@ -0,0 +1,13 @@
// Check redefine global and resource with lib_global2.hlsl, lib_global4.hlsl.
RWBuffer<float> buf0;
groupshared column_major float2x2 dataC[8*8];
float2x2 MatRotate(float2x2 m, uint x, uint y) {
buf0[x] = y;
dataC[x%(8*8)] = m;
GroupMemoryBarrierWithGroupSync();
float2x2 f2x2 = dataC[8*8-1-y%(8*8)];
return f2x2;
}

Просмотреть файл

@ -0,0 +1,13 @@
// Check redefine global and resource with lib_global2.hlsl, lib_global3.hlsl.
RWTexture1D<float> buf0;
groupshared column_major float2x2 dataC[8*8];
float2x2 RotateMat(float2x2 m, uint x, uint y) {
buf0[x] = y;
dataC[x%(8*8)] = m;
GroupMemoryBarrierWithGroupSync();
float2x2 f2x2 = dataC[8*8-1-y%(8*8)];
return f2x2;
}

Просмотреть файл

@ -0,0 +1,31 @@
// Check resource link with lib_cs_entry.hlsl
RWStructuredBuffer<float2x2> fA;
void StoreOutputMat(float2x2 m, uint gidx) {
fA[gidx] = m;
}
void StoreCSOutput(uint2 tid, uint2 gid) {
fA[gid.x][gid.y] = tid;
}
struct mat {
row_major float2x2 f2x2;
};
StructuredBuffer<mat> mats;
StructuredBuffer<row_major float2x2> mats2;
float2x2 LoadInputMat(uint x, uint y) {
return mats.Load(x).f2x2 + mats2.Load(y);
}
groupshared column_major float2x2 dataC[8*8];
float2x2 RotateMat(float2x2 m, uint x, uint y) {
dataC[x%(8*8)] = m;
GroupMemoryBarrierWithGroupSync();
float2x2 f2x2 = dataC[8*8-1-y%(8*8)];
return f2x2;
}

Просмотреть файл

@ -51,6 +51,7 @@ set(SOURCES
dxcontainerbuilder.cpp
dxcutil.cpp
dxcdisassembler.cpp
dxclinker.cpp
)
set(LIBRARIES

Просмотреть файл

@ -28,9 +28,11 @@ HRESULT CreateDxcValidator(_In_ REFIID riid, _Out_ LPVOID *ppv);
HRESULT CreateDxcAssembler(_In_ REFIID riid, _Out_ LPVOID *ppv);
HRESULT CreateDxcOptimizer(_In_ REFIID riid, _Out_ LPVOID *ppv);
HRESULT CreateDxcContainerBuilder(_In_ REFIID riid, _Out_ LPVOID *ppv);
HRESULT CreateDxcLinker(_In_ REFIID riid, _Out_ LPVOID *ppv);
namespace hlsl {
void CreateDxcContainerReflection(IDxcContainerReflection **ppResult);
void CreateDxcLinker(IDxcContainerReflection **ppResult);
}
HRESULT CreateDxcContainerReflection(_In_ REFIID riid, _Out_ LPVOID *ppv) {
@ -95,6 +97,9 @@ DxcCreateInstance(_In_ REFCLSID rclsid,
else if (IsEqualCLSID(rclsid, CLSID_DxcContainerReflection)) {
hr = CreateDxcContainerReflection(riid, ppv);
}
else if (IsEqualCLSID(rclsid, CLSID_DxcLinker)) {
hr = CreateDxcLinker(riid, ppv);
}
else if (IsEqualCLSID(rclsid, CLSID_DxcContainerBuilder)) {
hr = CreateDxcContainerBuilder(riid, ppv);
}

Просмотреть файл

@ -0,0 +1,259 @@
///////////////////////////////////////////////////////////////////////////////
// //
// dxclinker.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. //
// //
// Implements the Dxil Linker. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/Support/WinIncludes.h"
#include "dxc/HLSL/DxilContainer.h"
#include "dxc/Support/ErrorCodes.h"
#include "dxc/Support/FileIOHelper.h"
#include "dxc/Support/Global.h"
#include "dxc/Support/dxcapi.impl.h"
#include "dxc/Support/microcom.h"
#include "dxc/dxcapi.h"
#include "dxillib.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
#include "dxc/HLSL/DxilLinker.h"
#include "dxc/HLSL/DxilValidation.h"
#include "dxc/Support/Unicode.h"
#include "dxc/Support/microcom.h"
#include "dxc/dxcapi.internal.h"
#include "dxcutil.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
using namespace hlsl;
using namespace llvm;
// This declaration is used for the locally-linked validator.
HRESULT CreateDxcValidator(_In_ REFIID riid, _Out_ LPVOID *ppv);
class DxcLinker : public IDxcLinker, public IDxcContainerEvent {
public:
// Register a library with name to ref it later.
__override HRESULT RegisterLibrary(
_In_opt_ LPCWSTR pLibName, // Name of the library.
_In_ IDxcBlob *pLib // Library to add.
);
// Links the shader and produces a shader blob that the Direct3D runtime can
// use.
__override HRESULT STDMETHODCALLTYPE Link(
_In_opt_ LPCWSTR pEntryName, // Entry point name
_In_ LPCWSTR pTargetProfile, // shader profile to link
_In_count_(libCount)
const LPCWSTR *pLibNames, // Array of library names to link
UINT32 libCount, // Number of libraries to link
_In_count_(argCount)
const LPCWSTR *pArguments, // Array of pointers to arguments
_In_ UINT32 argCount, // Number of arguments
_COM_Outptr_ IDxcOperationResult *
*ppResult // Linker output status, buffer, and errors
);
DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
__override HRESULT STDMETHODCALLTYPE RegisterDxilContainerEventHandler(
IDxcContainerEventsHandler *pHandler, UINT64 *pCookie) {
DXASSERT(m_pDxcContainerEventsHandler == nullptr,
"else events handler is already registered");
*pCookie = 1; // Only one EventsHandler supported
m_pDxcContainerEventsHandler = pHandler;
return S_OK;
};
__override HRESULT STDMETHODCALLTYPE
UnRegisterDxilContainerEventHandler(UINT64 cookie) {
DXASSERT(m_pDxcContainerEventsHandler != nullptr,
"else unregister should not have been called");
m_pDxcContainerEventsHandler.Release();
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) {
return DoBasicQueryInterface<IDxcLinker>(this, riid, ppvObject);
}
DxcLinker() : m_dwRef(0), m_pLinker(nullptr) {
m_pLinker.reset(DxilLinker::CreateLinker(m_Ctx));
}
~DxcLinker() {
// Make sure DxilLinker is released before LLVMContext.
m_pLinker.reset();
}
private:
DXC_MICROCOM_REF_FIELD(m_dwRef)
LLVMContext m_Ctx;
std::unique_ptr<DxilLinker> m_pLinker;
CComPtr<IDxcContainerEventsHandler> m_pDxcContainerEventsHandler;
};
HRESULT
DxcLinker::RegisterLibrary(_In_opt_ LPCWSTR pLibName, // Name of the library.
_In_ IDxcBlob *pBlob // Library to add.
) {
// Prepare UTF8-encoded versions of API values.
CW2A pUtf8LibName(pLibName, CP_UTF8);
// Already exist lib with same name.
if (m_pLinker->HasLibNameRegistered(pUtf8LibName.m_psz))
return E_INVALIDARG;
try {
std::unique_ptr<llvm::Module> pModule, pDebugModule;
CComPtr<IMalloc> pMalloc;
CComPtr<AbstractMemoryStream> pDiagStream;
IFT(CoGetMalloc(1, &pMalloc));
IFT(CreateMemoryStream(pMalloc, &pDiagStream));
raw_stream_ostream DiagStream(pDiagStream);
llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
IFR(ValidateLoadModuleFromContainer(
pBlob->GetBufferPointer(), pBlob->GetBufferSize(), pModule,
pDebugModule, m_Ctx, m_Ctx, DiagStream));
return m_pLinker->RegisterLib(pUtf8LibName.m_psz, std::move(pModule),
std::move(pDebugModule))
? S_OK
: E_INVALIDARG;
} catch (hlsl::Exception &) {
return E_INVALIDARG;
}
}
// Links the shader and produces a shader blob that the Direct3D runtime can
// use.
HRESULT STDMETHODCALLTYPE DxcLinker::Link(
_In_opt_ LPCWSTR pEntryName, // Entry point name
_In_ LPCWSTR pTargetProfile, // shader profile to link
_In_count_(libCount)
const LPCWSTR *pLibNames, // Array of library names to link
UINT32 libCount, // Number of libraries to link
_In_count_(argCount)
const LPCWSTR *pArguments, // Array of pointers to arguments
_In_ UINT32 argCount, // Number of arguments
_COM_Outptr_ IDxcOperationResult *
*ppResult // Linker output status, buffer, and errors
) {
// Prepare UTF8-encoded versions of API values.
CW2A pUtf8EntryPoint(pEntryName, CP_UTF8);
CW2A pUtf8TargetProfile(pTargetProfile, CP_UTF8);
// TODO: read and validate options.
CComPtr<AbstractMemoryStream> pOutputStream;
// Detach previous libraries.
m_pLinker->DetachAll();
HRESULT hr = S_OK;
try {
CComPtr<IMalloc> pMalloc;
CComPtr<IDxcBlob> pOutputBlob;
CComPtr<AbstractMemoryStream> pDiagStream;
IFT(CoGetMalloc(1, &pMalloc));
IFT(CreateMemoryStream(pMalloc, &pOutputStream));
std::string warnings;
llvm::raw_string_ostream w(warnings);
{
UINT32 majorVer, minorVer;
dxcutil::GetValidatorVersion(&majorVer, &minorVer);
// TODO: use validation version when link.
(majorVer);
(minorVer);
}
IFT(CreateMemoryStream(pMalloc, &pDiagStream));
raw_stream_ostream DiagStream(pDiagStream);
llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
PrintDiagnosticContext DiagContext(DiagPrinter);
m_Ctx.setDiagnosticHandler(PrintDiagnosticContext::PrintDiagnosticHandler,
&DiagContext, true);
// Attach libraries.
bool bSuccess = true;
for (unsigned i = 0; i < libCount; i++) {
CW2A pUtf8LibName(pLibNames[i], CP_UTF8);
bSuccess &= m_pLinker->AttachLib(pUtf8LibName.m_psz);
}
bool hasErrorOccurred = !bSuccess;
if (bSuccess) {
std::unique_ptr<Module> pM =
m_pLinker->Link(pUtf8EntryPoint.m_psz, pUtf8TargetProfile.m_psz);
if (pM) {
const IntrusiveRefCntPtr<clang::DiagnosticIDs> Diags(
new clang::DiagnosticIDs);
clang::DiagnosticOptions *DiagOpts = nullptr;
clang::DiagnosticsEngine Diag(Diags, DiagOpts);
raw_stream_ostream outStream(pOutputStream.p);
// Create bitcode of M.
WriteBitcodeToFile(pM.get(), outStream);
outStream.flush();
// Validation.
HRESULT valHR = dxcutil::ValidateAndAssembleToContainer(
std::move(pM), pOutputBlob, pMalloc, SerializeDxilFlags::None,
pOutputStream,
/*bDebugInfo*/ false, Diag);
// Callback after valid DXIL is produced
if (SUCCEEDED(valHR)) {
CComPtr<IDxcBlob> pTargetBlob;
if (m_pDxcContainerEventsHandler != nullptr) {
HRESULT hr = m_pDxcContainerEventsHandler->OnDxilContainerBuilt(
pOutputBlob, &pTargetBlob);
if (SUCCEEDED(hr) && pTargetBlob != nullptr) {
std::swap(pOutputBlob, pTargetBlob);
}
}
// TODO: DFCC_ShaderDebugName
}
hasErrorOccurred = Diag.hasErrorOccurred();
} else {
hasErrorOccurred = true;
}
}
DiagStream.flush();
CComPtr<IStream> pStream = pDiagStream;
dxcutil::CreateOperationResultFromOutputs(pOutputBlob, pStream, warnings,
hasErrorOccurred, ppResult);
}
CATCH_CPP_ASSIGN_HRESULT();
return hr;
}
HRESULT CreateDxcLinker(_In_ REFIID riid, _Out_ LPVOID *ppv) {
CComPtr<IDxcLinker> Result = new (std::nothrow) DxcLinker();
if (Result == nullptr) {
*ppv = nullptr;
return E_OUTOFMEMORY;
}
return Result->QueryInterface(riid, ppv);
}

Просмотреть файл

@ -1061,6 +1061,8 @@ public:
pUtf8SourceName = opts.InputFile.data();
}
}
// Set target profile.
opts.TargetProfile = pUtf8TargetProfile.m_psz;
IFT(msfPtr->RegisterOutputStream(L"output.bc", pOutputStream));
IFT(msfPtr->CreateStdStreams(pMalloc));

Просмотреть файл

@ -31,6 +31,7 @@ add_clang_library(clang-hlsl-tests SHARED
FunctionTest.cpp
HLSLTestData.h
HlslTestUtils.h
LinkerTest.cpp
MSFileSysTest.cpp
Objects.cpp
OptionsTest.cpp

Просмотреть файл

@ -103,6 +103,8 @@ void CheckOperationSucceeded(IDxcOperationResult *pResult, IDxcBlob **ppBlob);
bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
const LPCSTR *pErrorMsgs, size_t errorMsgCount,
bool maySucceedAnyway, bool bRegex);
bool CheckMsgs(const LPCSTR pText, size_t TextCount, const LPCSTR *pErrorMsgs,
size_t errorMsgCount, bool bRegex);
std::string DisassembleProgram(dxc::DxcDllSupport &dllSupport, IDxcBlob *pProgram);
void Utf8ToBlob(dxc::DxcDllSupport &dllSupport, const std::string &val, _Outptr_ IDxcBlob **ppBlob);
void Utf8ToBlob(dxc::DxcDllSupport &dllSupport, const std::string &val, _Outptr_ IDxcBlobEncoding **ppBlob);

Просмотреть файл

@ -0,0 +1,257 @@
///////////////////////////////////////////////////////////////////////////////
// //
// LinkerTest.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 <memory>
#include <vector>
#include <string>
#include "llvm/ADT/ArrayRef.h"
#include "CompilationResult.h"
#include "HLSLTestData.h"
#include "llvm/Support/ManagedStatic.h"
#include <fstream>
#include "WexTestClass.h"
#include "HlslTestUtils.h"
#include "dxc/dxcapi.h"
#include "DxcTestUtils.h"
using namespace std;
using namespace hlsl;
using namespace llvm;
// The test fixture.
class LinkerTest
{
public:
BEGIN_TEST_CLASS(LinkerTest)
TEST_CLASS_PROPERTY(L"Parallel", L"true")
TEST_METHOD_PROPERTY(L"Priority", L"0")
END_TEST_CLASS()
TEST_CLASS_SETUP(InitSupport);
TEST_METHOD(RunLinkResource);
TEST_METHOD(RunLinkAllProfiles);
TEST_METHOD(RunLinkFailNoDefine);
TEST_METHOD(RunLinkFailReDefine);
TEST_METHOD(RunLinkGlobalInit);
TEST_METHOD(RunLinkFailReDefineGlobal);
TEST_METHOD(RunLinkFailProfileMismatch);
TEST_METHOD(RunLinkFailEntryNoProps);
dxc::DxcDllSupport m_dllSupport;
VersionSupportInfo m_ver;
void CreateLinker(IDxcLinker **pResultLinker) {
VERIFY_SUCCEEDED(
m_dllSupport.CreateInstance(CLSID_DxcLinker, pResultLinker));
}
void CompileLib(LPCWSTR filename, IDxcBlob **pResultBlob) {
std::wstring fullPath = hlsl_test::GetPathToHlslDataFile(filename);
CComPtr<IDxcBlobEncoding> pSource;
CComPtr<IDxcLibrary> pLibrary;
VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
VERIFY_SUCCEEDED(
pLibrary->CreateBlobFromFile(fullPath.c_str(), nullptr, &pSource));
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcOperationResult> pResult;
CComPtr<IDxcBlob> pProgram;
CA2W shWide("lib_6_1", CP_UTF8);
VERIFY_SUCCEEDED(
m_dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"hlsl.hlsl", L"", shWide,
nullptr, 0, nullptr, 0, nullptr,
&pResult));
VERIFY_SUCCEEDED(pResult->GetResult(pResultBlob));
}
void RegisterDxcModule(LPCWSTR pLibName, IDxcBlob *pBlob, IDxcLinker *pLinker) {
VERIFY_SUCCEEDED(pLinker->RegisterLibrary(pLibName, pBlob));
}
void Link(LPCWSTR pEntryName, LPCWSTR pShaderModel, IDxcLinker *pLinker,
ArrayRef<LPCWSTR> libNames, llvm::ArrayRef<LPCSTR> pCheckMsgs) {
CComPtr<IDxcOperationResult> pResult;
VERIFY_SUCCEEDED(pLinker->Link(pEntryName, pShaderModel, libNames.data(),
libNames.size(), nullptr, 0, &pResult));
CComPtr<IDxcBlob> pProgram;
CheckOperationSucceeded(pResult, &pProgram);
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcBlobEncoding> pDisassembly;
VERIFY_SUCCEEDED(
m_dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
VERIFY_SUCCEEDED(pCompiler->Disassemble(pProgram, &pDisassembly));
std::string IR = BlobToUtf8(pDisassembly);
CheckMsgs(IR.c_str(), IR.size(), pCheckMsgs.data(), pCheckMsgs.size(), false);
}
void LinkCheckMsg(LPCWSTR pEntryName, LPCWSTR pShaderModel, IDxcLinker *pLinker,
ArrayRef<LPCWSTR> libNames, llvm::ArrayRef<LPCSTR> pErrorMsgs) {
CComPtr<IDxcOperationResult> pResult;
VERIFY_SUCCEEDED(pLinker->Link(pEntryName, pShaderModel, libNames.data(),
libNames.size(), nullptr, 0, &pResult));
CheckOperationResultMsgs(pResult, pErrorMsgs.data(), pErrorMsgs.size(),
false, false);
}
};
bool LinkerTest::InitSupport() {
if (!m_dllSupport.IsEnabled()) {
VERIFY_SUCCEEDED(m_dllSupport.Initialize());
m_ver.Initialize(m_dllSupport);
}
return true;
}
TEST_F(LinkerTest, RunLinkResource) {
CComPtr<IDxcBlob> pResLib;
CompileLib(L"..\\CodeGenHLSL\\lib_resource2.hlsl", &pResLib);
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_cs_entry.hlsl", &pEntryLib);
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
LPCWSTR libName = L"entry";
RegisterDxcModule(libName, pEntryLib, pLinker);
LPCWSTR libResName = L"res";
RegisterDxcModule(libResName, pResLib, pLinker);
Link(L"entry", L"cs_6_1", pLinker, {libResName, libName}, {});
}
TEST_F(LinkerTest, RunLinkAllProfiles) {
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
LPCWSTR libName = L"entry";
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_entries2.hlsl", &pEntryLib);
RegisterDxcModule(libName, pEntryLib, pLinker);
Link(L"vs_main", L"vs_6_1", pLinker, {libName}, {});
Link(L"hs_main", L"hs_6_1", pLinker, {libName}, {});
Link(L"ds_main", L"ds_6_1", pLinker, {libName}, {});
Link(L"gs_main", L"gs_6_1", pLinker, {libName}, {});
Link(L"ps_main", L"ps_6_1", pLinker, {libName}, {});
CComPtr<IDxcBlob> pResLib;
CompileLib(L"..\\CodeGenHLSL\\lib_resource2.hlsl", &pResLib);
LPCWSTR libResName = L"res";
RegisterDxcModule(libResName, pResLib, pLinker);
Link(L"cs_main", L"cs_6_1", pLinker, {libName, libResName}, {});
}
TEST_F(LinkerTest, RunLinkFailNoDefine) {
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_cs_entry.hlsl", &pEntryLib);
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
LPCWSTR libName = L"entry";
RegisterDxcModule(libName, pEntryLib, pLinker);
LinkCheckMsg(L"entry", L"cs_6_1", pLinker, {libName},
{"Cannot find definition of function"});
}
TEST_F(LinkerTest, RunLinkFailReDefine) {
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_cs_entry.hlsl", &pEntryLib);
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
LPCWSTR libName = L"entry";
RegisterDxcModule(libName, pEntryLib, pLinker);
LPCWSTR libName2 = L"entry2";
RegisterDxcModule(libName2, pEntryLib, pLinker);
LinkCheckMsg(L"entry", L"cs_6_1", pLinker, {libName, libName2},
{"Definition already exists for function"});
}
TEST_F(LinkerTest, RunLinkGlobalInit) {
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_global.hlsl", &pEntryLib);
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
LPCWSTR libName = L"entry";
RegisterDxcModule(libName, pEntryLib, pLinker);
Link(L"test", L"ps_6_1", pLinker, {libName},
// Make sure cbuffer load is generated.
{"dx.op.cbufferLoad"});
}
TEST_F(LinkerTest, RunLinkFailReDefineGlobal) {
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_global2.hlsl", &pEntryLib);
CComPtr<IDxcBlob> pLib0;
CompileLib(L"..\\CodeGenHLSL\\lib_global3.hlsl", &pLib0);
CComPtr<IDxcBlob> pLib1;
CompileLib(L"..\\CodeGenHLSL\\lib_global4.hlsl", &pLib1);
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
LPCWSTR libName = L"entry";
RegisterDxcModule(libName, pEntryLib, pLinker);
LPCWSTR libName1 = L"lib0";
RegisterDxcModule(libName1, pLib0, pLinker);
LPCWSTR libName2 = L"lib1";
RegisterDxcModule(libName2, pLib1, pLinker);
LinkCheckMsg(L"entry", L"cs_6_1", pLinker, {libName, libName1, libName2},
{"Definition already exists for global variable", "Resource already exists"});
}
TEST_F(LinkerTest, RunLinkFailProfileMismatch) {
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_global.hlsl", &pEntryLib);
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
LPCWSTR libName = L"entry";
RegisterDxcModule(libName, pEntryLib, pLinker);
LinkCheckMsg(L"test", L"cs_6_1", pLinker, {libName},
{"Profile mismatch between entry function and target profile"});
}
TEST_F(LinkerTest, RunLinkFailEntryNoProps) {
CComPtr<IDxcBlob> pEntryLib;
CompileLib(L"..\\CodeGenHLSL\\lib_global.hlsl", &pEntryLib);
CComPtr<IDxcLinker> pLinker;
CreateLinker(&pLinker);
LPCWSTR libName = L"entry";
RegisterDxcModule(libName, pEntryLib, pLinker);
LinkCheckMsg(L"\01?update@@YAXXZ", L"cs_6_1", pLinker, {libName},
{"Cannot find function property for entry function"});
}

Просмотреть файл

@ -35,6 +35,35 @@ void CheckOperationSucceeded(IDxcOperationResult *pResult, IDxcBlob **ppBlob) {
VERIFY_SUCCEEDED(pResult->GetResult(ppBlob));
}
static bool CheckMsgs(llvm::StringRef text, llvm::ArrayRef<LPCSTR> pMsgs,
bool bRegex) {
const char *pStart = !text.empty() ? text.begin() : nullptr;
const char *pEnd = !text.empty() ? text.end() : nullptr;
for (auto pMsg : pMsgs) {
if (bRegex) {
llvm::Regex RE(pMsg);
std::string reErrors;
VERIFY_IS_TRUE(RE.isValid(reErrors));
VERIFY_IS_TRUE(RE.match(text));
} else {
const char *pMatch = std::search(pStart, pEnd, pMsg, pMsg + strlen(pMsg));
if (pEnd == pMatch) {
WEX::Logging::Log::Comment(WEX::Common::String().Format(
L"Unable to find '%S' in text:\r\n%.*S", pMsg, (pEnd - pStart),
pStart));
}
VERIFY_ARE_NOT_EQUAL(pEnd, pMatch);
}
}
return true;
}
bool CheckMsgs(const LPCSTR pText, size_t TextCount, const LPCSTR *pErrorMsgs,
size_t errorMsgCount, bool bRegex) {
return CheckMsgs(llvm::StringRef(pText, TextCount),
llvm::ArrayRef<LPCSTR>(pErrorMsgs, errorMsgCount), bRegex);
}
static
bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
llvm::ArrayRef<LPCSTR> pErrorMsgs,
@ -57,23 +86,9 @@ bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
if (SUCCEEDED(status) && maySucceedAnyway) {
return false;
}
for (auto pErrorMsg : pErrorMsgs) {
if (bRegex) {
llvm::Regex RE(pErrorMsg);
std::string reErrors;
VERIFY_IS_TRUE(RE.isValid(reErrors));
VERIFY_IS_TRUE(RE.match(llvm::StringRef((const char *)text->GetBufferPointer(), text->GetBufferSize())));
}
else {
const char *pMatch = std::search(pStart, pEnd, pErrorMsg, pErrorMsg + strlen(pErrorMsg));
if (pEnd == pMatch) {
WEX::Logging::Log::Comment(WEX::Common::String().Format(
L"Unable to find '%S' in text:\r\n%.*S", pErrorMsg, (pEnd - pStart),
pStart));
}
VERIFY_ARE_NOT_EQUAL(pEnd, pMatch);
}
}
CheckMsgs(llvm::StringRef((const char *)text->GetBufferPointer(),
text->GetBufferSize()),
pErrorMsgs, bRegex);
}
return true;
}