Add linker.
This commit is contained in:
Родитель
18c0c08032
Коммит
f408830296
|
@ -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
|
||||
|
|
|
@ -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 ¶m : F->args()) {
|
||||
vmap[¶m] = (paramIt++);
|
||||
}
|
||||
|
||||
llvm::CloneFunctionInto(NewF, F, vmap, /*ModuleLevelChanges*/ true, Returns);
|
||||
|
||||
// Remove params from vmap.
|
||||
for (Argument ¶m : F->args()) {
|
||||
vmap.erase(¶m);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче