DirectXShaderCompiler/lib/HLSL/DxilExportMap.cpp

222 строки
7.2 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// //
// DxilExportMap.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. //
// //
// dxilutil::ExportMap for handling -exports option. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/HLSL/DxilExportMap.h"
#include "dxc/DXIL/DxilTypeSystem.h"
#include "dxc/DXIL/DxilUtil.h"
#include "dxc/Support/Global.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include <set>
#include <string>
#include <vector>
using namespace llvm;
using namespace hlsl;
namespace hlsl {
namespace dxilutil {
void ExportMap::clear() { m_ExportMap.clear(); }
bool ExportMap::empty() const { return m_ExportMap.empty(); }
bool ExportMap::ParseExports(const std::vector<std::string> &exportOpts,
llvm::raw_ostream &errors) {
for (auto &str : exportOpts) {
llvm::StringRef exports = StoreString(str);
size_t start = 0;
size_t end = llvm::StringRef::npos;
// def1;def2;...
while (true) {
end = exports.find_first_of(';', start);
llvm::StringRef exportDef = exports.slice(start, end);
// def: export1[[,export2,...]=internal]
llvm::StringRef internalName = exportDef;
size_t equals = exportDef.find_first_of('=');
if (equals != llvm::StringRef::npos) {
internalName = exportDef.substr(equals + 1);
size_t exportStart = 0;
while (true) {
size_t comma = exportDef.find_first_of(',', exportStart);
if (comma == llvm::StringRef::npos || comma > equals)
break;
if (exportStart < comma)
Add(exportDef.slice(exportStart, comma), internalName);
exportStart = comma + 1;
}
if (exportStart < equals)
Add(exportDef.slice(exportStart, equals), internalName);
} else {
Add(internalName);
}
if (equals == 0 || internalName.empty()) {
errors << "Invalid syntax for -exports: '" << exportDef
<< "'. Syntax is: export1[[,export2,...]=internal][;...]";
return false;
}
if (end == llvm::StringRef::npos)
break;
start = end + 1;
}
}
return true;
}
void ExportMap::Add(llvm::StringRef exportName, llvm::StringRef internalName) {
// Incoming strings may be escaped (because they originally come from
// arguments) Unescape them here, if necessary
if (exportName.startswith("\\")) {
std::string str;
llvm::raw_string_ostream os(str);
PrintUnescapedString(exportName, os);
exportName = StoreString(os.str());
}
if (internalName.startswith("\\")) {
std::string str;
llvm::raw_string_ostream os(str);
PrintUnescapedString(internalName, os);
internalName = StoreString(os.str());
}
if (internalName.empty())
internalName = exportName;
exportName = DemangleFunctionName(exportName);
m_ExportMap[internalName].insert(exportName);
}
ExportMap::const_iterator
ExportMap::GetExportsByName(llvm::StringRef Name) const {
ExportMap::const_iterator it = m_ExportMap.find(Name);
StringRef unmangled = DemangleFunctionName(Name);
if (it == end()) {
if (Name.startswith(ManglingPrefix)) {
it = m_ExportMap.find(unmangled);
} else if (Name.startswith(EntryPrefix)) {
it = m_ExportMap.find(Name.substr(strlen(EntryPrefix)));
}
}
return it;
}
bool ExportMap::IsExported(llvm::StringRef original) const {
if (m_ExportMap.empty())
return true;
return GetExportsByName(original) != end();
}
void ExportMap::BeginProcessing() {
m_ExportNames.clear();
m_NameCollisions.clear();
m_UnusedExports.clear();
for (auto &it : m_ExportMap) {
m_UnusedExports.emplace(it.getKey());
}
}
bool ExportMap::ProcessFunction(llvm::Function *F,
bool collisionAvoidanceRenaming) {
// Skip if already added. This can happen due to patch constant functions.
if (m_RenameMap.find(F) != m_RenameMap.end())
return true;
StringRef originalName = F->getName();
StringRef unmangled = DemangleFunctionName(originalName);
auto it = GetExportsByName(F->getName());
// Early out if not exported, and do optional collision avoidance
if (it == end()) {
F->setLinkage(GlobalValue::LinkageTypes::InternalLinkage);
if (collisionAvoidanceRenaming) {
std::string internalName = (Twine("internal.") + unmangled).str();
internalName = dxilutil::ReplaceFunctionName(originalName, internalName);
F->setName(internalName);
}
return false;
}
F->setLinkage(GlobalValue::LinkageTypes::ExternalLinkage);
// Add entry to m_RenameMap:
auto &renames = m_RenameMap[F];
const llvm::StringSet<> &exportRenames = it->getValue();
llvm::StringRef internalName = it->getKey();
// mark export used
UseExport(internalName);
// Add identity first
auto itIdentity = exportRenames.find(unmangled);
if (exportRenames.empty() || itIdentity != exportRenames.end()) {
if (exportRenames.size() > 1)
renames.insert(originalName);
ExportName(originalName);
} else if (collisionAvoidanceRenaming) {
// do optional collision avoidance for exports being renamed
std::string tempName = (Twine("temp.") + unmangled).str();
tempName = dxilutil::ReplaceFunctionName(originalName, tempName);
F->setName(tempName);
}
for (auto itName = exportRenames.begin(); itName != exportRenames.end();
itName++) {
// Now add actual renames
if (itName != itIdentity) {
StringRef newName = StoreString(
dxilutil::ReplaceFunctionName(F->getName(), itName->getKey()));
renames.insert(newName);
ExportName(newName);
}
}
return true;
}
void ExportMap::RegisterExportedFunction(llvm::Function *F) {
// Skip if already added
if (m_RenameMap.find(F) != m_RenameMap.end())
return;
F->setLinkage(GlobalValue::LinkageTypes::ExternalLinkage);
NameSet &renames = m_RenameMap[F];
(void)(renames); // Don't actually add anything
ExportName(F->getName());
}
void ExportMap::UseExport(llvm::StringRef internalName) {
auto it = m_UnusedExports.find(internalName);
if (it != m_UnusedExports.end())
m_UnusedExports.erase(it);
}
void ExportMap::ExportName(llvm::StringRef exportName) {
auto result = m_ExportNames.insert(exportName);
if (!result.second) {
// Already present, report collision
m_NameCollisions.insert(exportName);
}
}
bool ExportMap::EndProcessing() const {
return m_UnusedExports.empty() && m_NameCollisions.empty();
}
llvm::StringRef ExportMap::StoreString(llvm::StringRef str) {
return *m_StringStorage.insert(str).first;
}
} // namespace dxilutil
} // namespace hlsl