| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // 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 |