Browse Source

Merged PR 74: Add -exports export1[[,export2,...]=internal][;...] for lib target

Add -exports export1[[,export2,...]=internal][;...] for lib target

- Remove IDxcLinker::LinkWithExports, use -exports option instead
- Added renaming/cloning support to linker
- Added validation and tests
- Fix AV in DxilPreparePasses when patch constant function is shared

ID3D12LibraryReflection:
- Fix shader input type for typed buffer and dimension for tbuffer
- Deterministic function order by name
Tex Riddell 7 years ago
parent
commit
8db08cb21e
28 changed files with 1643 additions and 172 deletions
  1. 107 0
      include/dxc/HLSL/DxilExportMap.h
  2. 2 2
      include/dxc/HLSL/DxilLinker.h
  3. 12 2
      include/dxc/HLSL/DxilUtil.h
  4. 1 0
      include/dxc/Support/HLSLOptions.h
  5. 1 0
      include/dxc/Support/HLSLOptions.td
  6. 0 16
      include/dxc/dxcapi.h
  7. 5 0
      lib/DxcSupport/HLSLOptions.cpp
  8. 1 0
      lib/HLSL/CMakeLists.txt
  9. 19 7
      lib/HLSL/DxilContainerReflection.cpp
  10. 221 0
      lib/HLSL/DxilExportMap.cpp
  11. 82 23
      lib/HLSL/DxilLinker.cpp
  12. 10 2
      lib/HLSL/DxilPreparePasses.cpp
  13. 52 3
      lib/HLSL/DxilUtil.cpp
  14. 2 0
      tools/clang/include/clang/Frontend/CodeGenOptions.h
  15. 122 25
      tools/clang/lib/CodeGen/CGHLSLMS.cpp
  16. 93 0
      tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports1.hlsl
  17. 92 0
      tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports2.hlsl
  18. 173 0
      tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports3.hlsl
  19. 103 0
      tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports4.hlsl
  20. 97 0
      tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports_nocollision.hlsl
  21. 96 0
      tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_hs_export1.hlsl
  22. 118 0
      tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_hs_export2.hlsl
  23. 40 0
      tools/clang/test/CodeGenHLSL/quick-test/lib_exports_collision1.hlsl
  24. 6 47
      tools/clang/tools/dxcompiler/dxclinker.cpp
  25. 3 0
      tools/clang/tools/dxcompiler/dxcompilerobj.cpp
  26. 2 0
      tools/clang/unittests/HLSL/DxcTestUtils.h
  27. 141 43
      tools/clang/unittests/HLSL/LinkerTest.cpp
  28. 42 2
      tools/clang/unittests/HLSL/ValidationTest.cpp

+ 107 - 0
include/dxc/HLSL/DxilExportMap.h

@@ -0,0 +1,107 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// DxilExportMap.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.                                     //
+//                                                                           //
+// dxilutil::ExportMap for handling -exports option.                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+// TODO: Refactor to separate name export verification part from
+// llvm/Function part so first part may be have shared use without llvm
+
+#pragma once
+#include <vector>
+#include <set>
+#include <unordered_set>
+#include <string>
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace llvm {
+class Function;
+class raw_ostream;
+}
+
+namespace hlsl {
+namespace dxilutil {
+  class ExportMap {
+  public:
+    typedef std::unordered_set<std::string> StringStore;
+    typedef std::set<llvm::StringRef> NameSet;
+    typedef llvm::DenseMap< llvm::Function*, NameSet > RenameMap;
+    typedef llvm::StringMap< llvm::StringSet<> > ExportMapByString;
+    typedef ExportMapByString::iterator iterator;
+    typedef ExportMapByString::const_iterator const_iterator;
+
+    ExportMap() {}
+    void clear();
+    bool empty() const;
+
+    // Iterate export map by string name
+    iterator begin() { return m_ExportMap.begin(); }
+    const_iterator begin() const { return m_ExportMap.begin(); }
+    iterator end() { return m_ExportMap.end(); }
+    const_iterator end() const { return m_ExportMap.end(); }
+
+    // Initialize export map from option strings
+    bool ParseExports(const std::vector<std::string> &exportOpts, llvm::raw_ostream &errors);
+    // Add one export to the export map
+    void Add(llvm::StringRef exportName, llvm::StringRef internalName = llvm::StringRef());
+    // Return true if export is present, or m_ExportMap is empty
+    bool IsExported(llvm::StringRef original) const;
+
+    // Retrieve export entry by name.  If Name is mangled, it will fallback to
+    // search for unmangled version if exact match fails.
+    // If result == end(), no matching export was found.
+    ExportMapByString::const_iterator GetExportsByName(llvm::StringRef Name) const;
+
+    // Call before processing functions for renaming and cloning validation
+    void BeginProcessing();
+
+    // Called for each function to be processed
+    // In order to avoid intermediate name collisions during renaming,
+    //  if collisionAvoidanceRenaming is true:
+    //    non-exported functions will be renamed internal.<name>
+    //    functions exported with a different name will be renamed temp.<name>
+    // returns true if function is exported
+    bool ProcessFunction(llvm::Function *F, bool collisionAvoidanceRenaming);
+
+    // Add function to exports without checking export map or renaming
+    //  (useful for patch constant functions used by exported HS)
+    void RegisterExportedFunction(llvm::Function *F);
+
+    // Called to mark an internal name as used (remove from unused set)
+    void UseExport(llvm::StringRef internalName);
+    // Called to add an exported (full) name (for collision detection)
+    void ExportName(llvm::StringRef exportName);
+
+    // Called after functions are processed.
+    // Returns true if no name collisions or unused exports are present.
+    bool EndProcessing() const;
+    const NameSet& GetNameCollisions() const { return m_NameCollisions; }
+    const NameSet& GetUnusedExports() const { return m_UnusedExports; }
+
+    // GetRenames gets the map of mangled renames by function pointer
+    const RenameMap &GetFunctionRenames() const { return m_RenameMap; }
+
+  private:
+    // {"internalname": ("export1", "export2", ...), ...}
+    ExportMapByString m_ExportMap;
+    StringStore m_StringStorage;
+    llvm::StringRef StoreString(llvm::StringRef str);
+
+    // Renaming/Validation state
+    RenameMap m_RenameMap;
+    NameSet m_ExportNames;
+    NameSet m_NameCollisions;
+    NameSet m_UnusedExports;
+  };
+}
+
+}

+ 2 - 2
include/dxc/HLSL/DxilLinker.h

@@ -17,6 +17,7 @@
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringMap.h"
 #include <memory>
 #include <memory>
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/ErrorOr.h"
+#include "dxc/HLSL/DxilExportMap.h"
 
 
 namespace llvm {
 namespace llvm {
 class Function;
 class Function;
@@ -45,8 +46,7 @@ public:
   virtual void DetachAll() = 0;
   virtual void DetachAll() = 0;
 
 
   virtual std::unique_ptr<llvm::Module>
   virtual std::unique_ptr<llvm::Module>
-  Link(llvm::StringRef entry, llvm::StringRef profile,
-       llvm::StringMap<llvm::StringRef> &exportMap) = 0;
+  Link(llvm::StringRef entry, llvm::StringRef profile, dxilutil::ExportMap &exportMap) = 0;
 
 
 protected:
 protected:
   DxilLinker(llvm::LLVMContext &Ctx, unsigned valMajor, unsigned valMinor) : m_ctx(Ctx), m_valMajor(valMajor), m_valMinor(valMinor) {}
   DxilLinker(llvm::LLVMContext &Ctx, unsigned valMajor, unsigned valMinor) : m_ctx(Ctx), m_valMajor(valMajor), m_valMinor(valMinor) {}

+ 12 - 2
include/dxc/HLSL/DxilUtil.h

@@ -11,6 +11,9 @@
 
 
 #pragma once
 #pragma once
 #include <unordered_set>
 #include <unordered_set>
+#include <string>
+#include <memory>
+#include "llvm/ADT/StringRef.h"
 
 
 namespace llvm {
 namespace llvm {
 class Type;
 class Type;
@@ -23,7 +26,7 @@ class DiagnosticInfo;
 class Value;
 class Value;
 class Instruction;
 class Instruction;
 class BasicBlock;
 class BasicBlock;
-class StringRef;
+class raw_ostream;
 }
 }
 
 
 namespace hlsl {
 namespace hlsl {
@@ -32,6 +35,9 @@ class DxilFieldAnnotation;
 class DxilTypeSystem;
 class DxilTypeSystem;
 
 
 namespace dxilutil {
 namespace dxilutil {
+  extern const char ManglingPrefix[];
+  extern const char EntryPrefix[];
+
   unsigned
   unsigned
   GetLegacyCBufferFieldElementSize(DxilFieldAnnotation &fieldAnnotation,
   GetLegacyCBufferFieldElementSize(DxilFieldAnnotation &fieldAnnotation,
                                    llvm::Type *Ty, DxilTypeSystem &typeSys);
                                    llvm::Type *Ty, DxilTypeSystem &typeSys);
@@ -54,6 +60,10 @@ namespace dxilutil {
   void EmitResMappingError(llvm::Instruction *Res);
   void EmitResMappingError(llvm::Instruction *Res);
   // Simple demangle just support case "\01?name@" pattern.
   // Simple demangle just support case "\01?name@" pattern.
   llvm::StringRef DemangleFunctionName(llvm::StringRef name);
   llvm::StringRef DemangleFunctionName(llvm::StringRef name);
+  // ReplaceFunctionName replaces the undecorated portion of originalName with undecorated newName
+  std::string ReplaceFunctionName(llvm::StringRef originalName, llvm::StringRef newName);
+  void PrintEscapedString(llvm::StringRef Name, llvm::raw_ostream &Out);
+  void PrintUnescapedString(llvm::StringRef Name, llvm::raw_ostream &Out);
   // Change select/phi on operands into select/phi on operation.
   // Change select/phi on operands into select/phi on operation.
   // phi0 = phi a0, b0, c0
   // phi0 = phi a0, b0, c0
   // phi1 = phi a1, b1, c1
   // phi1 = phi a1, b1, c1
@@ -78,4 +88,4 @@ namespace dxilutil {
   void PrintDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context);
   void PrintDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context);
 }
 }
 
 
-}
+}

+ 1 - 0
include/dxc/Support/HLSLOptions.h

@@ -109,6 +109,7 @@ public:
   llvm::StringRef VerifyRootSignatureSource; //OPT_verifyrootsignature
   llvm::StringRef VerifyRootSignatureSource; //OPT_verifyrootsignature
   llvm::StringRef RootSignatureDefine; // OPT_rootsig_define
   llvm::StringRef RootSignatureDefine; // OPT_rootsig_define
   llvm::StringRef FloatDenormalMode; // OPT_denorm
   llvm::StringRef FloatDenormalMode; // OPT_denorm
+  std::vector<std::string> Exports; // OPT_exports
 
 
   bool AllResourcesBound = false; // OPT_all_resources_bound
   bool AllResourcesBound = false; // OPT_all_resources_bound
   bool AstDump = false; // OPT_ast_dump
   bool AstDump = false; // OPT_ast_dump

+ 1 - 0
include/dxc/Support/HLSLOptions.td

@@ -233,6 +233,7 @@ def enable_16bit_types: Flag<["-", "/"], "enable-16bit-types">, Flags<[CoreOptio
   HelpText<"Enable 16bit types and disable min precision types. Available in HLSL 2018 and shader model 6.2">;
   HelpText<"Enable 16bit types and disable min precision types. Available in HLSL 2018 and shader model 6.2">;
 def ignore_line_directives : Flag<["-", "/"], "ignore-line-directives">, HelpText<"Ignore line directives">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 def ignore_line_directives : Flag<["-", "/"], "ignore-line-directives">, HelpText<"Ignore line directives">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 def auto_binding_space : Separate<["-", "/"], "auto-binding-space">, Group<hlslcomp_Group>, Flags<[CoreOption]>, HelpText<"Set auto binding space - enables auto resource binding in libraries">;
 def auto_binding_space : Separate<["-", "/"], "auto-binding-space">, Group<hlslcomp_Group>, Flags<[CoreOption]>, HelpText<"Set auto binding space - enables auto resource binding in libraries">;
+def exports : Separate<["-", "/"], "exports">, Group<hlslcomp_Group>, Flags<[CoreOption]>, HelpText<"Specify exports when compiling a library: export1[[,export1_clone,...]=internal_name][;...]">;
 
 
 // SPIRV Change Starts
 // SPIRV Change Starts
 def spirv : Flag<["-"], "spirv">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
 def spirv : Flag<["-"], "spirv">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,

+ 0 - 16
include/dxc/dxcapi.h

@@ -220,22 +220,6 @@ public:
       _COM_Outptr_ IDxcOperationResult *
       _COM_Outptr_ IDxcOperationResult *
           *ppResult // Linker output status, buffer, and errors
           *ppResult // Linker output status, buffer, and errors
   ) = 0;
   ) = 0;
-  // Links the shader with export and produces a shader blob that the Direct3D
-  // runtime can use.
-  virtual HRESULT STDMETHODCALLTYPE LinkWithExports(
-      _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
-      _In_count_(exportCount) const DxcDefine *pExports, // Array of exports
-      _In_ UINT32 exportCount,                           // Number of exports
-      _COM_Outptr_ IDxcOperationResult *
-          *ppResult // Linker output status, buffer, and errors
-      ) = 0;
 };
 };
 
 
 static const UINT32 DxcValidatorFlags_Default = 0;
 static const UINT32 DxcValidatorFlags_Default = 0;

+ 5 - 0
lib/DxcSupport/HLSLOptions.cpp

@@ -266,6 +266,9 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
       // Set entry point to impossible name.
       // Set entry point to impossible name.
       opts.EntryPoint = "lib.no::entry";
       opts.EntryPoint = "lib.no::entry";
     }
     }
+  } else if (Args.getLastArg(OPT_exports)) {
+    errors << "library profile required when using -exports option";
+    return 1;
   }
   }
 
 
   llvm::StringRef ver = Args.getLastArgValue(OPT_hlsl_version);
   llvm::StringRef ver = Args.getLastArgValue(OPT_hlsl_version);
@@ -351,6 +354,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
     }
     }
   }
   }
 
 
+  opts.Exports = Args.getAllArgValues(OPT_exports);
+
   // Check options only allowed in shader model >= 6.2FPDenormalMode
   // Check options only allowed in shader model >= 6.2FPDenormalMode
   unsigned Major = 0;
   unsigned Major = 0;
   unsigned Minor = 0;
   unsigned Minor = 0;

+ 1 - 0
lib/HLSL/CMakeLists.txt

@@ -41,6 +41,7 @@ add_llvm_library(LLVMHLSL
   DxilTargetTransformInfo.cpp
   DxilTargetTransformInfo.cpp
   DxilTypeSystem.cpp
   DxilTypeSystem.cpp
   DxilUtil.cpp
   DxilUtil.cpp
+  DxilExportMap.cpp
   DxilValidation.cpp
   DxilValidation.cpp
   DxcOptimizer.cpp
   DxcOptimizer.cpp
   HLMatrixLowerPass.cpp
   HLMatrixLowerPass.cpp

+ 19 - 7
lib/HLSL/DxilContainerReflection.cpp

@@ -190,10 +190,13 @@ class DxilLibraryReflection : public DxilModuleReflection, public ID3D12LibraryR
 private:
 private:
   DXC_MICROCOM_TM_REF_FIELDS()
   DXC_MICROCOM_TM_REF_FIELDS()
 
 
-  typedef MapVector<StringRef, std::unique_ptr<CFunctionReflection> > FunctionMap;
+  // Storage, and function by name:
+  typedef DenseMap<StringRef, std::unique_ptr<CFunctionReflection> > FunctionMap;
   typedef DenseMap<const Function*, CFunctionReflection*> FunctionsByPtr;
   typedef DenseMap<const Function*, CFunctionReflection*> FunctionsByPtr;
   FunctionMap m_FunctionMap;
   FunctionMap m_FunctionMap;
   FunctionsByPtr m_FunctionsByPtr;
   FunctionsByPtr m_FunctionsByPtr;
+  // Enable indexing into functions in deterministic order:
+  std::vector<CFunctionReflection*> m_FunctionVector;
 
 
   void AddResourceUseToFunctions(DxilResourceBase &resource, unsigned resIndex);
   void AddResourceUseToFunctions(DxilResourceBase &resource, unsigned resIndex);
   void AddResourceDependencies();
   void AddResourceDependencies();
@@ -1223,8 +1226,8 @@ static D3D_SHADER_INPUT_TYPE ResourceToShaderInputType(DxilResourceBase *RB) {
     if (R->HasCounter()) return D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER;
     if (R->HasCounter()) return D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER;
     return D3D_SIT_UAV_RWSTRUCTURED;
     return D3D_SIT_UAV_RWSTRUCTURED;
   }
   }
+  case DxilResource::Kind::TBuffer:
   case DxilResource::Kind::TypedBuffer:
   case DxilResource::Kind::TypedBuffer:
-    return isUAV ? D3D_SIT_UAV_RWTYPED : D3D_SIT_STRUCTURED;
   case DxilResource::Kind::Texture1D:
   case DxilResource::Kind::Texture1D:
   case DxilResource::Kind::Texture1DArray:
   case DxilResource::Kind::Texture1DArray:
   case DxilResource::Kind::Texture2D:
   case DxilResource::Kind::Texture2D:
@@ -1234,7 +1237,7 @@ static D3D_SHADER_INPUT_TYPE ResourceToShaderInputType(DxilResourceBase *RB) {
   case DxilResource::Kind::Texture3D:
   case DxilResource::Kind::Texture3D:
   case DxilResource::Kind::TextureCube:
   case DxilResource::Kind::TextureCube:
   case DxilResource::Kind::TextureCubeArray:
   case DxilResource::Kind::TextureCubeArray:
-    return R->IsRW() ? D3D_SIT_UAV_RWTYPED : D3D_SIT_TEXTURE;
+    return isUAV ? D3D_SIT_UAV_RWTYPED : D3D_SIT_TEXTURE;
   case DxilResource::Kind::RTAccelerationStructure:
   case DxilResource::Kind::RTAccelerationStructure:
     return (D3D_SHADER_INPUT_TYPE)D3D_SIT_RTACCELERATIONSTRUCTURE;
     return (D3D_SHADER_INPUT_TYPE)D3D_SIT_RTACCELERATIONSTRUCTURE;
   default:
   default:
@@ -1266,6 +1269,7 @@ static D3D_SRV_DIMENSION ResourceToDimension(DxilResourceBase *RB) {
   switch (RB->GetKind()) {
   switch (RB->GetKind()) {
   case DxilResource::Kind::StructuredBuffer:
   case DxilResource::Kind::StructuredBuffer:
   case DxilResource::Kind::TypedBuffer:
   case DxilResource::Kind::TypedBuffer:
+  case DxilResource::Kind::TBuffer:
     return D3D_SRV_DIMENSION_BUFFER;
     return D3D_SRV_DIMENSION_BUFFER;
   case DxilResource::Kind::Texture1D:
   case DxilResource::Kind::Texture1D:
     return D3D_SRV_DIMENSION_TEXTURE1D;
     return D3D_SRV_DIMENSION_TEXTURE1D;
@@ -2248,14 +2252,22 @@ void DxilLibraryReflection::AddResourceUseToFunctions(DxilResourceBase &resource
 }
 }
 
 
 void DxilLibraryReflection::AddResourceDependencies() {
 void DxilLibraryReflection::AddResourceDependencies() {
+  std::map<StringRef, CFunctionReflection*> orderedMap;
   for (auto &F : m_pModule->functions()) {
   for (auto &F : m_pModule->functions()) {
     if (F.isDeclaration())
     if (F.isDeclaration())
       continue;
       continue;
-    auto &func = m_FunctionMap[F.getName().str()];
+    auto &func = m_FunctionMap[F.getName()];
     DXASSERT(!func.get(), "otherwise duplicate named functions");
     DXASSERT(!func.get(), "otherwise duplicate named functions");
     func.reset(new CFunctionReflection());
     func.reset(new CFunctionReflection());
     func->Initialize(this, &F);
     func->Initialize(this, &F);
     m_FunctionsByPtr[&F] = func.get();
     m_FunctionsByPtr[&F] = func.get();
+    orderedMap[F.getName()] = func.get();
+  }
+  // Fill in function vector sorted by name
+  m_FunctionVector.clear();
+  m_FunctionVector.reserve(orderedMap.size());
+  for (auto &it : orderedMap) {
+    m_FunctionVector.push_back(it.second);
   }
   }
   UINT resIndex = 0;
   UINT resIndex = 0;
   for (auto &resource : m_Resources) {
   for (auto &resource : m_Resources) {
@@ -2305,15 +2317,15 @@ HRESULT DxilLibraryReflection::GetDesc(D3D12_LIBRARY_DESC * pDesc) {
   //Unset:  LPCSTR    Creator;           // The name of the originator of the library.
   //Unset:  LPCSTR    Creator;           // The name of the originator of the library.
   //Unset:  UINT      Flags;             // Compilation flags.
   //Unset:  UINT      Flags;             // Compilation flags.
   //UINT      FunctionCount;     // Number of functions exported from the library.
   //UINT      FunctionCount;     // Number of functions exported from the library.
-  pDesc->FunctionCount = (UINT)m_FunctionMap.size();
+  pDesc->FunctionCount = (UINT)m_FunctionVector.size();
   return S_OK;
   return S_OK;
 }
 }
 
 
 _Use_decl_annotations_
 _Use_decl_annotations_
 ID3D12FunctionReflection *DxilLibraryReflection::GetFunctionByIndex(INT FunctionIndex) {
 ID3D12FunctionReflection *DxilLibraryReflection::GetFunctionByIndex(INT FunctionIndex) {
-  if (FunctionIndex >= m_FunctionMap.size())
+  if (FunctionIndex >= m_FunctionVector.size())
     return &g_InvalidFunction;
     return &g_InvalidFunction;
-  return ((m_FunctionMap.begin() + FunctionIndex)->second).get();
+  return m_FunctionVector[FunctionIndex];
 }
 }
 
 
 // DxilRuntimeReflection implementation
 // DxilRuntimeReflection implementation

+ 221 - 0
lib/HLSL/DxilExportMap.cpp

@@ -0,0 +1,221 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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/Support/Global.h"
+#include "dxc/HLSL/DxilUtil.h"
+#include "dxc/HLSL/DxilExportMap.h"
+#include "dxc/HLSL/DxilTypeSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/IR/Function.h"
+#include <string>
+#include <vector>
+#include <set>
+
+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;
+}
+
+} // dxilutil
+} // hlsl

+ 82 - 23
lib/HLSL/DxilLinker.cpp

@@ -38,6 +38,8 @@
 #include "llvm/Transforms/IPO.h"
 #include "llvm/Transforms/IPO.h"
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/Scalar.h"
 
 
+#include "dxc/HLSL/DxilExportMap.h"
+
 using namespace llvm;
 using namespace llvm;
 using namespace hlsl;
 using namespace hlsl;
 
 
@@ -65,7 +67,9 @@ void AddResourceMap(
   }
   }
 }
 }
 
 
-void CloneFunction(Function *F, Function *NewF, ValueToValueMapTy &vmap) {
+void CloneFunction(Function *F, Function *NewF, ValueToValueMapTy &vmap,
+                   hlsl::DxilTypeSystem *TypeSys = nullptr,
+                   hlsl::DxilTypeSystem *SrcTypeSys = nullptr) {
   SmallVector<ReturnInst *, 2> Returns;
   SmallVector<ReturnInst *, 2> Returns;
   // Map params.
   // Map params.
   auto paramIt = NewF->arg_begin();
   auto paramIt = NewF->arg_begin();
@@ -74,6 +78,11 @@ void CloneFunction(Function *F, Function *NewF, ValueToValueMapTy &vmap) {
   }
   }
 
 
   llvm::CloneFunctionInto(NewF, F, vmap, /*ModuleLevelChanges*/ true, Returns);
   llvm::CloneFunctionInto(NewF, F, vmap, /*ModuleLevelChanges*/ true, Returns);
+  if (TypeSys) {
+    if (SrcTypeSys == nullptr)
+      SrcTypeSys = TypeSys;
+    TypeSys->CopyFunctionAnnotation(NewF, F, *SrcTypeSys);
+  }
 
 
   // Remove params from vmap.
   // Remove params from vmap.
   for (Argument &param : F->args()) {
   for (Argument &param : F->args()) {
@@ -138,8 +147,7 @@ public:
   void DetachAll() override;
   void DetachAll() override;
 
 
   std::unique_ptr<llvm::Module>
   std::unique_ptr<llvm::Module>
-  Link(StringRef entry, StringRef profile,
-       llvm::StringMap<llvm::StringRef> &exportMap) override;
+  Link(StringRef entry, StringRef profile, dxilutil::ExportMap &exportMap) override;
 
 
 private:
 private:
   bool AttachLib(DxilLib *lib);
   bool AttachLib(DxilLib *lib);
@@ -318,7 +326,7 @@ DxilResourceBase *DxilLib::GetResource(const llvm::Constant *GV) {
 namespace {
 namespace {
 // Create module from link defines.
 // Create module from link defines.
 struct DxilLinkJob {
 struct DxilLinkJob {
-  DxilLinkJob(LLVMContext &Ctx, llvm::StringMap<llvm::StringRef> &exportMap,
+  DxilLinkJob(LLVMContext &Ctx, dxilutil::ExportMap &exportMap,
               unsigned valMajor, unsigned valMinor)
               unsigned valMajor, unsigned valMinor)
       : m_ctx(Ctx), m_exportMap(exportMap), m_valMajor(valMajor),
       : m_ctx(Ctx), m_exportMap(exportMap), m_valMajor(valMajor),
         m_valMinor(valMinor) {}
         m_valMinor(valMinor) {}
@@ -350,7 +358,7 @@ private:
   llvm::StringMap<std::pair<DxilResourceBase *, llvm::GlobalVariable *>>
   llvm::StringMap<std::pair<DxilResourceBase *, llvm::GlobalVariable *>>
       m_resourceMap;
       m_resourceMap;
   LLVMContext &m_ctx;
   LLVMContext &m_ctx;
-  llvm::StringMap<llvm::StringRef> &m_exportMap;
+  dxilutil::ExportMap &m_exportMap;
   unsigned m_valMajor, m_valMinor;
   unsigned m_valMajor, m_valMinor;
 };
 };
 } // namespace
 } // namespace
@@ -368,6 +376,9 @@ const char kNoEntryProps[] =
 const char kRedefineResource[] =
 const char kRedefineResource[] =
     "Resource already exists as ";
     "Resource already exists as ";
 const char kInvalidValidatorVersion[] = "Validator version does not support target profile ";
 const char kInvalidValidatorVersion[] = "Validator version does not support target profile ";
+const char kExportNameCollision[] = "Export name collides with another export: ";
+const char kExportFunctionMissing[] = "Could not find target for export: ";
+const char kNoFunctionsToExport[] = "Library has no functions to export";
 } // namespace
 } // namespace
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 //
 //
@@ -783,6 +794,10 @@ DxilLinkJob::Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
 
 
 std::unique_ptr<Module>
 std::unique_ptr<Module>
 DxilLinkJob::LinkToLib(const ShaderModel *pSM) {
 DxilLinkJob::LinkToLib(const ShaderModel *pSM) {
+  if (m_functionDefs.empty()) {
+    m_ctx.emitError(Twine(kNoFunctionsToExport));
+    return nullptr;
+  }
   DxilLib *pLib = m_functionDefs.begin()->second;
   DxilLib *pLib = m_functionDefs.begin()->second;
   DxilModule &tmpDM = pLib->GetDxilModule();
   DxilModule &tmpDM = pLib->GetDxilModule();
   // Create new module.
   // Create new module.
@@ -854,19 +869,68 @@ DxilLinkJob::LinkToLib(const ShaderModel *pSM) {
   RunPreparePass(*pM);
   RunPreparePass(*pM);
 
 
   if (!m_exportMap.empty()) {
   if (!m_exportMap.empty()) {
+    m_exportMap.BeginProcessing();
+
     DM.ClearDxilMetadata(*pM);
     DM.ClearDxilMetadata(*pM);
     for (auto it = pM->begin(); it != pM->end();) {
     for (auto it = pM->begin(); it != pM->end();) {
       Function *F = it++;
       Function *F = it++;
       if (F->isDeclaration())
       if (F->isDeclaration())
         continue;
         continue;
-      StringRef name = F->getName();
-      name = dxilutil::DemangleFunctionName(name);
-      // Remove Function not in exportMap.
-      if (m_exportMap.find(name) == m_exportMap.end()) {
+      if (!m_exportMap.ProcessFunction(F, true)) {
+        // Remove Function not in exportMap.
         DM.RemoveFunction(F);
         DM.RemoveFunction(F);
         F->eraseFromParent();
         F->eraseFromParent();
       }
       }
     }
     }
+
+    if(!m_exportMap.EndProcessing()) {
+      for (auto &name : m_exportMap.GetNameCollisions()) {
+        std::string escaped;
+        llvm::raw_string_ostream os(escaped);
+        dxilutil::PrintEscapedString(name, os);
+        m_ctx.emitError(Twine(kExportNameCollision) + os.str());
+      }
+      for (auto &name : m_exportMap.GetUnusedExports()) {
+        std::string escaped;
+        llvm::raw_string_ostream os(escaped);
+        dxilutil::PrintEscapedString(name, os);
+        m_ctx.emitError(Twine(kExportFunctionMissing) + os.str());
+      }
+      return nullptr;
+    }
+
+    // Rename the original, if necessary, then clone the rest
+    for (auto &it : m_exportMap.GetFunctionRenames()) {
+      Function *F = it.first;
+      auto &renames = it.second;
+
+      if (renames.empty())
+        continue;
+
+      auto itName = renames.begin();
+
+      // Rename the original, if necessary, then clone the rest
+      if (renames.find(F->getName()) == renames.end())
+        F->setName(*(itName++));
+
+      while (itName != renames.end()) {
+        if (F->getName() != *itName) {
+          Function *NewF = Function::Create(F->getFunctionType(),
+            GlobalValue::LinkageTypes::ExternalLinkage,
+            *itName, DM.GetModule());
+          ValueToValueMapTy vmap;
+          CloneFunction(F, NewF, vmap, &DM.GetTypeSystem());
+          // add DxilFunctionProps if entry
+          if (DM.HasDxilFunctionProps(F)) {
+            DxilFunctionProps &props = DM.GetDxilFunctionProps(F);
+            auto newProps = llvm::make_unique<DxilFunctionProps>(props);
+            DM.AddDxilFunctionProps(NewF, newProps);
+          }
+        }
+        itName++;
+      }
+    }
+
     DM.EmitDxilMetadata();
     DM.EmitDxilMetadata();
   }
   }
 
 
@@ -1153,8 +1217,7 @@ bool DxilLinkerImpl::AddFunctions(SmallVector<StringRef, 4> &workList,
 }
 }
 
 
 std::unique_ptr<llvm::Module>
 std::unique_ptr<llvm::Module>
-DxilLinkerImpl::Link(StringRef entry, StringRef profile,
-                     llvm::StringMap<llvm::StringRef> &exportMap) {
+DxilLinkerImpl::Link(StringRef entry, StringRef profile, dxilutil::ExportMap &exportMap) {
   const ShaderModel *pSM = ShaderModel::GetByName(profile.data());
   const ShaderModel *pSM = ShaderModel::GetByName(profile.data());
   DXIL::ShaderKind kind = pSM->GetKind();
   DXIL::ShaderKind kind = pSM->GetKind();
   if (kind == DXIL::ShaderKind::Invalid ||
   if (kind == DXIL::ShaderKind::Invalid ||
@@ -1170,16 +1233,13 @@ DxilLinkerImpl::Link(StringRef entry, StringRef profile,
     return nullptr;
     return nullptr;
   }
   }
 
 
-  // Skip validation for lib target until implemented.
-  if (!pSM->IsLib()) {
-    // Verifying validator version supports the requested profile
-    unsigned minValMajor, minValMinor;
-    pSM->GetMinValidatorVersion(minValMajor, minValMinor);
-    if (minValMajor > m_valMajor ||
-        (minValMajor == m_valMajor && minValMinor > m_valMinor)) {
-      m_ctx.emitError(Twine(kInvalidValidatorVersion) + profile);
-      return nullptr;
-    }
+  // Verifying validator version supports the requested profile
+  unsigned minValMajor, minValMinor;
+  pSM->GetMinValidatorVersion(minValMajor, minValMinor);
+  if (minValMajor > m_valMajor ||
+      (minValMajor == m_valMajor && minValMinor > m_valMinor)) {
+    m_ctx.emitError(Twine(kInvalidValidatorVersion) + profile);
+    return nullptr;
   }
   }
 
 
   DxilLinkJob linkJob(m_ctx, exportMap, m_valMajor, m_valMinor);
   DxilLinkJob linkJob(m_ctx, exportMap, m_valMajor, m_valMinor);
@@ -1234,9 +1294,8 @@ DxilLinkerImpl::Link(StringRef entry, StringRef profile,
       // Only add exported functions.
       // Only add exported functions.
       for (auto &it : m_functionNameMap) {
       for (auto &it : m_functionNameMap) {
         StringRef name = it.getKey();
         StringRef name = it.getKey();
-        StringRef demangledName = dxilutil::DemangleFunctionName(name);
         // Only add names exist in exportMap.
         // Only add names exist in exportMap.
-        if (exportMap.find(demangledName) != exportMap.end())
+        if (exportMap.IsExported(name))
           workList.emplace_back(name);
           workList.emplace_back(name);
       }
       }
 
 

+ 10 - 2
lib/HLSL/DxilPreparePasses.cpp

@@ -373,6 +373,8 @@ private:
       }
       }
     } else {
     } else {
       std::vector<Function *> entries;
       std::vector<Function *> entries;
+      // Handle when multiple hull shaders point to the same patch constant function
+      DenseMap<Function*,Function*> patchConstantUpdates;
       for (iplist<Function>::iterator F : M.getFunctionList()) {
       for (iplist<Function>::iterator F : M.getFunctionList()) {
         if (DM.IsEntryThatUsesSignatures(F)) {
         if (DM.IsEntryThatUsesSignatures(F)) {
           auto *FT = F->getFunctionType();
           auto *FT = F->getFunctionType();
@@ -384,9 +386,15 @@ private:
       for (Function *entry : entries) {
       for (Function *entry : entries) {
         DxilFunctionProps &props = DM.GetDxilFunctionProps(entry);
         DxilFunctionProps &props = DM.GetDxilFunctionProps(entry);
         if (props.IsHS()) {
         if (props.IsHS()) {
+          Function* patchConstFunc = props.ShaderProps.HS.patchConstantFunc;
+          auto it = patchConstantUpdates.find(patchConstFunc);
+          if (it == patchConstantUpdates.end()) {
+            patchConstFunc = patchConstantUpdates[patchConstFunc] =
+              StripFunctionParameter(patchConstFunc, DM, FunctionDIs);
+          } else {
+            patchConstFunc = it->second;
+          }
           // Strip patch constant function first.
           // Strip patch constant function first.
-          Function *patchConstFunc = StripFunctionParameter(
-              props.ShaderProps.HS.patchConstantFunc, DM, FunctionDIs);
           DM.SetPatchConstantFunctionForHS(entry, patchConstFunc);
           DM.SetPatchConstantFunctionForHS(entry, patchConstFunc);
         }
         }
         StripFunctionParameter(entry, DM, FunctionDIs);
         StripFunctionParameter(entry, DM, FunctionDIs);

+ 52 - 3
lib/HLSL/DxilUtil.cpp

@@ -25,6 +25,8 @@
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/IRBuilder.h"
 #include "dxc/Support/Global.h"
 #include "dxc/Support/Global.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Twine.h"
 
 
 using namespace llvm;
 using namespace llvm;
 using namespace hlsl;
 using namespace hlsl;
@@ -33,6 +35,9 @@ namespace hlsl {
 
 
 namespace dxilutil {
 namespace dxilutil {
 
 
+const char ManglingPrefix[] = "\01?";
+const char EntryPrefix[] = "dx.entry.";
+
 Type *GetArrayEltTy(Type *Ty) {
 Type *GetArrayEltTy(Type *Ty) {
   if (isa<PointerType>(Ty))
   if (isa<PointerType>(Ty))
     Ty = Ty->getPointerElementType();
     Ty = Ty->getPointerElementType();
@@ -129,17 +134,60 @@ void PrintDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context) {
 }
 }
 
 
 StringRef DemangleFunctionName(StringRef name) {
 StringRef DemangleFunctionName(StringRef name) {
-  if (!name.startswith("\01?")) {
-    // Name don't mangled.
+  if (!name.startswith(ManglingPrefix)) {
+    // Name isn't mangled.
     return name;
     return name;
   }
   }
 
 
   size_t nameEnd = name.find_first_of("@");
   size_t nameEnd = name.find_first_of("@");
-  DXASSERT(nameEnd != StringRef::npos, "else Name don't mangled but has \01?");
+  DXASSERT(nameEnd != StringRef::npos, "else Name isn't mangled but has \01?");
 
 
   return name.substr(2, nameEnd - 2);
   return name.substr(2, nameEnd - 2);
 }
 }
 
 
+std::string ReplaceFunctionName(StringRef originalName, StringRef newName) {
+  if (originalName.startswith(ManglingPrefix)) {
+    return (Twine(ManglingPrefix) + newName +
+      originalName.substr(originalName.find_first_of('@'))).str();
+  } else if (originalName.startswith(EntryPrefix)) {
+    return (Twine(EntryPrefix) + newName).str();
+  }
+  return newName.str();
+}
+
+// From AsmWriter.cpp
+// PrintEscapedString - Print each character of the specified string, escaping
+// it if it is not printable or if it is an escape char.
+void PrintEscapedString(StringRef Name, raw_ostream &Out) {
+  for (unsigned i = 0, e = Name.size(); i != e; ++i) {
+    unsigned char C = Name[i];
+    if (isprint(C) && C != '\\' && C != '"')
+      Out << C;
+    else
+      Out << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F);
+  }
+}
+
+void PrintUnescapedString(StringRef Name, raw_ostream &Out) {
+  for (unsigned i = 0, e = Name.size(); i != e; ++i) {
+    unsigned char C = Name[i];
+    if (C == '\\') {
+      C = Name[++i];
+      unsigned value = hexDigitValue(C);
+      if (value != -1U) {
+        C = (unsigned char)value;
+        unsigned value2 = hexDigitValue(Name[i+1]);
+        assert(value2 != -1U && "otherwise, not a two digit hex escape");
+        if (value2 != -1U) {
+          C = (C << 4) + (unsigned char)value2;
+          ++i;
+        }
+      } // else, the next character (in C) should be the escaped character
+    }
+    Out << C;
+  }
+}
+
 std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::MemoryBuffer *MB,
 std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::MemoryBuffer *MB,
   llvm::LLVMContext &Ctx,
   llvm::LLVMContext &Ctx,
   std::string &DiagStr) {
   std::string &DiagStr) {
@@ -293,5 +341,6 @@ llvm::Instruction *FirstNonAllocaInsertionPt(llvm::Function* F) {
   return SkipAllocas(
   return SkipAllocas(
     F->getEntryBlock().getFirstInsertionPt());
     F->getEntryBlock().getFirstInsertionPt());
 }
 }
+
 }
 }
 }
 }

+ 2 - 0
tools/clang/include/clang/Frontend/CodeGenOptions.h

@@ -202,6 +202,8 @@ public:
   hlsl::DXIL::Float32DenormMode HLSLFloat32DenormMode;
   hlsl::DXIL::Float32DenormMode HLSLFloat32DenormMode;
   /// HLSLDefaultSpace also enables automatic binding for libraries if set. UINT_MAX == unset
   /// HLSLDefaultSpace also enables automatic binding for libraries if set. UINT_MAX == unset
   unsigned HLSLDefaultSpace = UINT_MAX;
   unsigned HLSLDefaultSpace = UINT_MAX;
+  /// HLSLLibraryExports specifies desired exports, with optional renaming
+  std::vector<std::string> HLSLLibraryExports;
   // HLSL Change Ends
   // HLSL Change Ends
   /// Regular expression to select optimizations for which we should enable
   /// Regular expression to select optimizations for which we should enable
   /// optimization remarks. Transformation passes whose name matches this
   /// optimization remarks. Transformation passes whose name matches this

+ 122 - 25
tools/clang/lib/CodeGen/CGHLSLMS.cpp

@@ -28,6 +28,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/GetElementPtrTypeIterator.h"
 #include "llvm/IR/GetElementPtrTypeIterator.h"
@@ -44,6 +45,7 @@
 #include "dxc/dxcapi.h"                 // stream support
 #include "dxc/dxcapi.h"                 // stream support
 #include "dxc/HLSL/HLSLExtensionsCodegenHelper.h"
 #include "dxc/HLSL/HLSLExtensionsCodegenHelper.h"
 #include "dxc/HLSL/DxilGenerationPass.h" // support pause/resume passes
 #include "dxc/HLSL/DxilGenerationPass.h" // support pause/resume passes
+#include "dxc/HLSL/DxilExportMap.h"
 
 
 using namespace clang;
 using namespace clang;
 using namespace CodeGen;
 using namespace CodeGen;
@@ -125,6 +127,9 @@ private:
   bool  m_bDebugInfo;
   bool  m_bDebugInfo;
   bool  m_bIsLib;
   bool  m_bIsLib;
 
 
+  // For library, m_ExportMap maps from internal name to zero or more renames
+  dxilutil::ExportMap m_ExportMap;
+
   HLCBuffer &GetGlobalCBuffer() {
   HLCBuffer &GetGlobalCBuffer() {
     return *static_cast<HLCBuffer*>(&(m_pHLModule->GetCBuffer(globalCBIndex)));
     return *static_cast<HLCBuffer*>(&(m_pHLModule->GetCBuffer(globalCBIndex)));
   }
   }
@@ -143,7 +148,7 @@ private:
   };
   };
 
 
   EntryFunctionInfo Entry;
   EntryFunctionInfo Entry;
-  
+
   // Map to save patch constant functions
   // Map to save patch constant functions
   struct PatchConstantInfo {
   struct PatchConstantInfo {
     clang::SourceLocation SL = clang::SourceLocation();
     clang::SourceLocation SL = clang::SourceLocation();
@@ -436,6 +441,15 @@ CGMSHLSLRuntime::CGMSHLSLRuntime(CodeGenModule &CGM)
   // set Float Denorm Mode
   // set Float Denorm Mode
   m_pHLModule->SetFloat32DenormMode(CGM.getCodeGenOpts().HLSLFloat32DenormMode);
   m_pHLModule->SetFloat32DenormMode(CGM.getCodeGenOpts().HLSLFloat32DenormMode);
 
 
+  // Fill in m_ExportMap, which maps from internal name to zero or more renames
+  m_ExportMap.clear();
+  std::string errors;
+  llvm::raw_string_ostream os(errors);
+  if (!m_ExportMap.ParseExports(CGM.getCodeGenOpts().HLSLLibraryExports, os)) {
+    DiagnosticsEngine &Diags = CGM.getDiags();
+    unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, "Error parsing -exports options: %0");
+    Diags.Report(DiagID) << os.str();
+  }
 }
 }
 
 
 bool CGMSHLSLRuntime::IsHlslObjectType(llvm::Type *Ty) {
 bool CGMSHLSLRuntime::IsHlslObjectType(llvm::Type *Ty) {
@@ -1880,14 +1894,24 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
     AddTypeAnnotation(Ty, dxilTypeSys, arrayEltSize);
     AddTypeAnnotation(Ty, dxilTypeSys, arrayEltSize);
   }
   }
 
 
+  // clear isExportedEntry if not exporting entry
+  bool isExportedEntry = profileAttributes != 0;
+  if (isExportedEntry) {
+    // use unmangled or mangled name depending on which is used for final entry function
+    StringRef name = isRay ? F->getName() : FD->getName();
+    if (!m_ExportMap.IsExported(name)) {
+      isExportedEntry = false;
+    }
+  }
+
   // Only add functionProps when exist.
   // Only add functionProps when exist.
-  if (profileAttributes || isEntry)
+  if (isExportedEntry || isEntry)
     m_pHLModule->AddDxilFunctionProps(F, funcProps);
     m_pHLModule->AddDxilFunctionProps(F, funcProps);
   if (isPatchConstantFunction)
   if (isPatchConstantFunction)
     patchConstantFunctionPropsMap[F] = std::move(funcProps);
     patchConstantFunctionPropsMap[F] = std::move(funcProps);
 
 
   // Save F to entry map.
   // Save F to entry map.
-  if (profileAttributes) {
+  if (isExportedEntry) {
     if (entryFunctionMap.count(FD->getName())) {
     if (entryFunctionMap.count(FD->getName())) {
       DiagnosticsEngine &Diags = CGM.getDiags();
       DiagnosticsEngine &Diags = CGM.getDiags();
       unsigned DiagID = Diags.getCustomDiagID(
       unsigned DiagID = Diags.getCustomDiagID(
@@ -4110,44 +4134,48 @@ static void SimpleTransformForHLDXIR(llvm::Module *pM) {
     I->eraseFromParent();
     I->eraseFromParent();
 }
 }
 
 
-// Clone shader entry function to be called by other functions.
-// The original function will be used as shader entry.
-static void CloneShaderEntry(Function *ShaderF, StringRef EntryName,
-                             HLModule &HLM) {
-  // Use mangled name for cloned one.
-  Function *F = Function::Create(ShaderF->getFunctionType(),
+static Function *CloneFunction(Function *Orig,
+                        const llvm::Twine &Name,
+                        llvm::Module *llvmModule,
+                        hlsl::DxilTypeSystem &TypeSys,
+                        hlsl::DxilTypeSystem &SrcTypeSys) {
+
+  Function *F = Function::Create(Orig->getFunctionType(),
                                  GlobalValue::LinkageTypes::ExternalLinkage,
                                  GlobalValue::LinkageTypes::ExternalLinkage,
-                                 "", HLM.GetModule());
-  F->takeName(ShaderF);
-  // Set to name before mangled.
-  ShaderF->setName(EntryName);
+                                 Name, llvmModule);
 
 
   SmallVector<ReturnInst *, 2> Returns;
   SmallVector<ReturnInst *, 2> Returns;
   ValueToValueMapTy vmap;
   ValueToValueMapTy vmap;
   // Map params.
   // Map params.
   auto entryParamIt = F->arg_begin();
   auto entryParamIt = F->arg_begin();
-  for (Argument &param : ShaderF->args()) {
+  for (Argument &param : Orig->args()) {
     vmap[&param] = (entryParamIt++);
     vmap[&param] = (entryParamIt++);
   }
   }
 
 
-  llvm::CloneFunctionInto(F, ShaderF, vmap, /*ModuleLevelChagnes*/ false,
-                          Returns);
+  llvm::CloneFunctionInto(F, Orig, vmap, /*ModuleLevelChagnes*/ false, Returns);
+  TypeSys.CopyFunctionAnnotation(F, Orig, SrcTypeSys);
+
+  return F;
+}
+
+// Clone shader entry function to be called by other functions.
+// The original function will be used as shader entry.
+static void CloneShaderEntry(Function *ShaderF, StringRef EntryName,
+                             HLModule &HLM) {
+  Function *F = CloneFunction(ShaderF, "", HLM.GetModule(),
+                              HLM.GetTypeSystem(), HLM.GetTypeSystem());
 
 
-  // Copy function annotation.
-  DxilFunctionAnnotation *shaderAnnot = HLM.GetFunctionAnnotation(ShaderF);
-  DxilFunctionAnnotation *annot = HLM.AddFunctionAnnotation(F);
+  F->takeName(ShaderF);
+  // Set to name before mangled.
+  ShaderF->setName(EntryName);
 
 
-  DxilParameterAnnotation &retAnnot = shaderAnnot->GetRetTypeAnnotation();
+  DxilFunctionAnnotation *annot = HLM.GetFunctionAnnotation(F);
   DxilParameterAnnotation &cloneRetAnnot = annot->GetRetTypeAnnotation();
   DxilParameterAnnotation &cloneRetAnnot = annot->GetRetTypeAnnotation();
-  cloneRetAnnot = retAnnot;
   // Clear semantic for cloned one.
   // Clear semantic for cloned one.
   cloneRetAnnot.SetSemanticString("");
   cloneRetAnnot.SetSemanticString("");
   cloneRetAnnot.SetSemanticIndexVec({});
   cloneRetAnnot.SetSemanticIndexVec({});
-  for (unsigned i = 0; i < shaderAnnot->GetNumParameters(); i++) {
+  for (unsigned i = 0; i < annot->GetNumParameters(); i++) {
     DxilParameterAnnotation &cloneParamAnnot = annot->GetParameterAnnotation(i);
     DxilParameterAnnotation &cloneParamAnnot = annot->GetParameterAnnotation(i);
-    DxilParameterAnnotation &paramAnnot =
-        shaderAnnot->GetParameterAnnotation(i);
-    cloneParamAnnot = paramAnnot;
     // Clear semantic for cloned one.
     // Clear semantic for cloned one.
     cloneParamAnnot.SetSemanticString("");
     cloneParamAnnot.SetSemanticString("");
     cloneParamAnnot.SetSemanticIndexVec({});
     cloneParamAnnot.SetSemanticIndexVec({});
@@ -4429,6 +4457,8 @@ void CGMSHLSLRuntime::FinishCodeGen() {
       if (m_pHLModule->GetDxilFunctionProps(it.second.Func).IsRay())
       if (m_pHLModule->GetDxilFunctionProps(it.second.Func).IsRay())
         continue;
         continue;
 
 
+      // TODO: change flattened function names to dx.entry.<name>:
+      //std::string entryName = (Twine(dxilutil::EntryPrefix) + it.getKey()).str();
       CloneShaderEntry(it.second.Func, it.getKey(), *m_pHLModule);
       CloneShaderEntry(it.second.Func, it.getKey(), *m_pHLModule);
 
 
       auto AttrIter = HSEntryPatchConstantFuncAttr.find(it.second.Func);
       auto AttrIter = HSEntryPatchConstantFuncAttr.find(it.second.Func);
@@ -4480,6 +4510,17 @@ void CGMSHLSLRuntime::FinishCodeGen() {
   // translate opcode into parameter for intrinsic functions
   // translate opcode into parameter for intrinsic functions
   AddOpcodeParamForIntrinsics(*m_pHLModule, m_IntrinsicMap, resMetadataMap);
   AddOpcodeParamForIntrinsics(*m_pHLModule, m_IntrinsicMap, resMetadataMap);
 
 
+  // Register patch constant functions referenced by exported Hull Shaders
+  if (m_bIsLib && !m_ExportMap.empty()) {
+    for (auto &it : entryFunctionMap) {
+      if (m_pHLModule->HasDxilFunctionProps(it.second.Func)) {
+        const DxilFunctionProps &props = m_pHLModule->GetDxilFunctionProps(it.second.Func);
+        if (props.IsHS())
+          m_ExportMap.RegisterExportedFunction(props.ShaderProps.HS.patchConstantFunc);
+      }
+    }
+  }
+
   // Pin entry point and constant buffers, mark everything else internal.
   // Pin entry point and constant buffers, mark everything else internal.
   for (Function &f : m_pHLModule->GetModule()->functions()) {
   for (Function &f : m_pHLModule->GetModule()->functions()) {
     if (!m_bIsLib) {
     if (!m_bIsLib) {
@@ -4498,6 +4539,62 @@ void CGMSHLSLRuntime::FinishCodeGen() {
       f.addFnAttr(llvm::Attribute::AlwaysInline);
       f.addFnAttr(llvm::Attribute::AlwaysInline);
   }
   }
 
 
+  if (m_bIsLib && !m_ExportMap.empty()) {
+    m_ExportMap.BeginProcessing();
+    for (Function &f : m_pHLModule->GetModule()->functions()) {
+      if (f.isDeclaration() || f.isIntrinsic() ||
+        GetHLOpcodeGroup(&f) != HLOpcodeGroup::NotHL)
+        continue;
+      m_ExportMap.ProcessFunction(&f, true);
+    }
+    // TODO: add subobject export names here.
+    if (!m_ExportMap.EndProcessing()) {
+      for (auto &name : m_ExportMap.GetNameCollisions()) {
+        DiagnosticsEngine &Diags = CGM.getDiags();
+        unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+          "Export name collides with another export: %0");
+        std::string escaped;
+        llvm::raw_string_ostream os(escaped);
+        dxilutil::PrintEscapedString(name, os);
+        Diags.Report(DiagID) << os.str();
+      }
+      for (auto &name : m_ExportMap.GetUnusedExports()) {
+        DiagnosticsEngine &Diags = CGM.getDiags();
+        unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
+          "Could not find target for export: %0");
+        std::string escaped;
+        llvm::raw_string_ostream os(escaped);
+        dxilutil::PrintEscapedString(name, os);
+        Diags.Report(DiagID) << os.str();
+      }
+    }
+  }
+
+  for (auto &it : m_ExportMap.GetFunctionRenames()) {
+    Function *F = it.first;
+    auto &renames = it.second;
+
+    if (renames.empty())
+      continue;
+
+    // Rename the original, if necessary, then clone the rest
+    if (renames.find(F->getName()) == renames.end())
+      F->setName(*renames.begin());
+
+    for (auto &itName : renames) {
+      if (F->getName() != itName) {
+        Function *pClone = CloneFunction(F, itName, m_pHLModule->GetModule(),
+          m_pHLModule->GetTypeSystem(), m_pHLModule->GetTypeSystem());
+        // add DxilFunctionProps if entry
+        if (m_pHLModule->HasDxilFunctionProps(F)) {
+          DxilFunctionProps &props = m_pHLModule->GetDxilFunctionProps(F);
+          auto newProps = llvm::make_unique<DxilFunctionProps>(props);
+          m_pHLModule->AddDxilFunctionProps(pClone, newProps);
+        }
+      }
+    }
+  }
+
   // Do simple transform to make later lower pass easier.
   // Do simple transform to make later lower pass easier.
   SimpleTransformForHLDXIR(m_pHLModule->GetModule());
   SimpleTransformForHLDXIR(m_pHLModule->GetModule());
 
 

+ 93 - 0
tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports1.hlsl

@@ -0,0 +1,93 @@
+// RUN: %dxc -auto-binding-space 13 -exports VS_RENAMED=\01?VSMain@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z;PS_RENAMED=PSMain -T lib_6_3 %s | %D3DReflect %s | FileCheck %s
+
+Buffer<float> T_unused;
+
+float fn_unused(int i) { return T_unused.Load(i); }
+
+Buffer<int> T0;
+
+Texture2D<float4> T1;
+
+struct Foo { uint u; float f; };
+StructuredBuffer<Foo> T2;
+
+[shader("vertex")]
+float4 VSMain(int3 coord : COORD) : SV_Position {
+  return T1.Load(coord);
+}
+
+[shader("pixel")]
+float4 PSMain(int idx : INDEX) : SV_Target {
+  return T2[T0.Load(idx)].f;
+}
+
+
+
+// CHECK: ID3D12LibraryReflection:
+// CHECK:   D3D12_LIBRARY_DESC:
+// CHECK:     FunctionCount: 3
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?PS_RENAMED@@YA?AV?$vector@M$03@@H@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 2
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 2
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?VS_RENAMED@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T1
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_FLOAT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_TEXTURE2D
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0xc
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: PS_RENAMED
+// CHECK:       Shader Version: Pixel 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 2
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 2
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0

+ 92 - 0
tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports2.hlsl

@@ -0,0 +1,92 @@
+// RUN: %dxc -auto-binding-space 13 -exports VSMain;VS_RENAMED=\01?VSMain@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z;RayGen1,RayGen2=RayGen -T lib_6_3 %s | %D3DReflect %s | FileCheck %s
+
+Buffer<int> T0;
+
+Texture2D<float4> T1;
+
+struct Foo { uint u; float f; };
+StructuredBuffer<Foo> T2;
+
+RWByteAddressBuffer U0;
+
+[shader("vertex")]
+float4 VSMain(int3 coord : COORD) : SV_Position {
+  return T1.Load(coord);
+}
+
+[shader("pixel")]
+float4 PSMain(int idx : INDEX) : SV_Target {
+  return T2[T0.Load(idx)].f;
+}
+
+[shader("raygeneration")]
+void RayGen() {
+  uint2 dim = DispatchRaysDimensions();
+  uint2 idx = DispatchRaysIndex();
+  U0.Store(idx.y * dim.x * 4 + idx.x * 4, idx.x ^ idx.y);
+}
+
+
+// CHECK: ID3D12LibraryReflection:
+// CHECK:   D3D12_LIBRARY_DESC:
+// CHECK:     FunctionCount: 4
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?RayGen1@@YAXXZ
+// CHECK:       Shader Version: RayGeneration 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: U0
+// CHECK:         Type: D3D_SIT_UAV_RWBYTEADDRESS
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 0
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?RayGen2@@YAXXZ
+// CHECK:       Shader Version: RayGeneration 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: U0
+// CHECK:         Type: D3D_SIT_UAV_RWBYTEADDRESS
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 0
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?VS_RENAMED@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T1
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_FLOAT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_TEXTURE2D
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0xc
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: VSMain
+// CHECK:       Shader Version: Vertex 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T1
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_FLOAT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_TEXTURE2D
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0xc

+ 173 - 0
tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports3.hlsl

@@ -0,0 +1,173 @@
+// RUN: %dxc -auto-binding-space 13 -exports PSMain,PSMain_Clone1,PSMain_Clone2=PSMain -T lib_6_3 %s | %D3DReflect %s | FileCheck %s
+
+Buffer<int> T0;
+
+Texture2D<float4> T1;
+
+struct Foo { uint u; float f; };
+StructuredBuffer<Foo> T2;
+
+[shader("vertex")]
+float4 VSMain(int3 coord : COORD) : SV_Position {
+  return T1.Load(coord);
+}
+
+[shader("pixel")]
+float4 PSMain(int idx : INDEX) : SV_Target {
+  return T2[T0.Load(idx)].f;
+}
+
+
+// CHECK: ID3D12LibraryReflection:
+// CHECK:   D3D12_LIBRARY_DESC:
+// CHECK:     FunctionCount: 6
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?PSMain@@YA?AV?$vector@M$03@@H@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?PSMain_Clone1@@YA?AV?$vector@M$03@@H@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?PSMain_Clone2@@YA?AV?$vector@M$03@@H@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: PSMain
+// CHECK:       Shader Version: Pixel 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: PSMain_Clone1
+// CHECK:       Shader Version: Pixel 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: PSMain_Clone2
+// CHECK:       Shader Version: Pixel 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0

+ 103 - 0
tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports4.hlsl

@@ -0,0 +1,103 @@
+// RUN: %dxc -auto-binding-space 13 -exports VSMain=PSMain;PSMain=VSMain -T lib_6_3 %s | %D3DReflect %s | FileCheck %s
+
+Buffer<int> T0;
+
+Texture2D<float4> T1;
+
+struct Foo { uint u; float f; };
+StructuredBuffer<Foo> T2;
+
+[shader("vertex")]
+float4 VSMain(int3 coord : COORD) : SV_Position {
+  return T1.Load(coord);
+}
+
+[shader("pixel")]
+float4 PSMain(int idx : INDEX) : SV_Target {
+  return T2[T0.Load(idx)].f;
+}
+
+
+// CHECK: ID3D12LibraryReflection:
+// CHECK:   D3D12_LIBRARY_DESC:
+// CHECK:     FunctionCount: 4
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?PSMain@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T1
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_FLOAT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_TEXTURE2D
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0xc
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?VSMain@@YA?AV?$vector@M$03@@H@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 2
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 2
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: PSMain
+// CHECK:       Shader Version: Vertex 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T1
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 1
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 1
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_FLOAT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_TEXTURE2D
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0xc
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: VSMain
+// CHECK:       Shader Version: Pixel 6.3
+// CHECK:       BoundResources: 2
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T0
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_SINT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T2
+// CHECK:         Type: D3D_SIT_STRUCTURED
+// CHECK:         uID: 2
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 2
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 8
+// CHECK:         uFlags: 0

+ 97 - 0
tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_exports_nocollision.hlsl

@@ -0,0 +1,97 @@
+// RUN: %dxc -auto-binding-space 13 -exports Foo=VSMain;Foo=\01?VSMain@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z;Foo=RayGen;fn1=fn2 -T lib_6_3 %s | %D3DReflect %s | FileCheck %s
+
+Buffer<int> T0;
+
+Texture2D<float4> T1;
+
+struct Foo { uint u; float f; };
+StructuredBuffer<Foo> T2;
+
+RWByteAddressBuffer U0;
+
+[shader("vertex")]
+float4 VSMain(int3 coord : COORD) : SV_Position {
+  return T1.Load(coord);
+}
+
+[shader("pixel")]
+float4 PSMain(int idx : INDEX) : SV_Target {
+  return T2[T0.Load(idx)].f;
+}
+
+void fn1() { U0.Store(1, T0.Load(2)); }
+// rename fn2 to fn1, no collision because fn1 is internal
+void fn2() { U0.Store(1, 3); }
+
+[shader("raygeneration")]
+void RayGen() {
+  uint2 dim = DispatchRaysDimensions();
+  uint2 idx = DispatchRaysIndex();
+  U0.Store(idx.y * dim.x * 4 + idx.x * 4, idx.x ^ idx.y);
+}
+
+// No Collision here between differently mangled functions, or to non-exported functions.
+
+// CHECK: ID3D12LibraryReflection:
+// CHECK:   D3D12_LIBRARY_DESC:
+// CHECK:     FunctionCount: 4
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?Foo@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T1
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_FLOAT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_TEXTURE2D
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0xc
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?Foo@@YAXXZ
+// CHECK:       Shader Version: RayGeneration 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: U0
+// CHECK:         Type: D3D_SIT_UAV_RWBYTEADDRESS
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 0
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?fn1@@YAXXZ
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: U0
+// CHECK:         Type: D3D_SIT_UAV_RWBYTEADDRESS
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_MIXED
+// CHECK:         Dimension: D3D_SRV_DIMENSION_BUFFER
+// CHECK:         NumSamples (or stride): 0
+// CHECK:         uFlags: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: Foo
+// CHECK:       Shader Version: Vertex 6.3
+// CHECK:       BoundResources: 1
+// CHECK:     Bound Resources:
+// CHECK:       D3D12_SHADER_BUFFER_DESC: Name: T1
+// CHECK:         Type: D3D_SIT_TEXTURE
+// CHECK:         uID: 0
+// CHECK:         BindCount: 1
+// CHECK:         BindPoint: 0
+// CHECK:         Space: 13
+// CHECK:         ReturnType: D3D_RETURN_TYPE_FLOAT
+// CHECK:         Dimension: D3D_SRV_DIMENSION_TEXTURE2D
+// CHECK:         NumSamples (or stride): 4294967295
+// CHECK:         uFlags: 0xc

+ 96 - 0
tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_hs_export1.hlsl

@@ -0,0 +1,96 @@
+// RUN: %dxc -auto-binding-space 13 -T lib_6_3 -exports HSMain1;HSMain2;HSMain3 %s | %D3DReflect %s | FileCheck %s
+
+// This version of HSPerPatchFunc1 should not be exported
+// CHECK: ID3D12FunctionReflection:
+// CHECK-NOT: D3D12_FUNCTION_DESC: Name: \01?HSPerPatchFunc1@@YA?AUHSPerPatchData@@V?$InputPatch@UPSSceneIn@@$0BA@@@@Z
+// CHECK-NOT: D3D_SRV_DIMENSION_BUFFER
+
+Buffer<float> T_unused;
+
+struct PSSceneIn
+{
+  float4 pos  : SV_Position;
+  float2 tex  : TEXCOORD0;
+  float3 norm : NORMAL;
+};
+
+struct HSPerPatchData
+{
+  float edges[3] : SV_TessFactor;
+  float inside   : SV_InsideTessFactor;
+};
+
+// Should not be selected, since later candidate function with same name exists.
+// If selected, it should fail, since patch size mismatches HS function.
+HSPerPatchData HSPerPatchFunc1(
+  const InputPatch< PSSceneIn, 16 > points)
+{
+  HSPerPatchData d;
+
+  d.edges[0] = -5;
+  d.edges[1] = -6;
+  d.edges[2] = -7;
+  d.inside = T_unused.Load(1).x;
+
+  return d;
+}
+
+HSPerPatchData HSPerPatchFunc2(
+  const InputPatch< PSSceneIn, 4 > points)
+{
+  HSPerPatchData d;
+
+  d.edges[0] = -5;
+  d.edges[1] = -6;
+  d.edges[2] = -7;
+  d.inside = -8;
+
+  return d;
+}
+
+
+[shader("hull")]
+[domain("tri")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_cw")]
+[patchconstantfunc("HSPerPatchFunc1")]
+[outputcontrolpoints(3)]
+void HSMain1( const uint id : SV_OutputControlPointID,
+              const InputPatch< PSSceneIn, 3 > points )
+{
+}
+
+[shader("hull")]
+[domain("quad")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_cw")]
+[patchconstantfunc("HSPerPatchFunc2")]
+[outputcontrolpoints(4)]
+void HSMain2( const uint id : SV_OutputControlPointID,
+              const InputPatch< PSSceneIn, 4 > points )
+{
+}
+
+[shader("hull")]
+[domain("tri")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_ccw")]
+[patchconstantfunc("HSPerPatchFunc1")]
+[outputcontrolpoints(3)]
+void HSMain3( const uint id : SV_OutputControlPointID,
+              const InputPatch< PSSceneIn, 3 > points )
+{
+}
+
+// actual selected HSPerPatchFunc1 for HSMain1 and HSMain3
+HSPerPatchData HSPerPatchFunc1()
+{
+  HSPerPatchData d;
+
+  d.edges[0] = -5;
+  d.edges[1] = -6;
+  d.edges[2] = -7;
+  d.inside = -8;
+
+  return d;
+}

+ 118 - 0
tools/clang/test/CodeGenHLSL/quick-test/d3dreflect/lib_hs_export2.hlsl

@@ -0,0 +1,118 @@
+// RUN: %dxc -auto-binding-space 13 -T lib_6_3 -exports HSMain1;HSMain3 %s | %D3DReflect %s | FileCheck %s
+
+Buffer<float> T_unused;
+
+struct PSSceneIn
+{
+  float4 pos  : SV_Position;
+  float2 tex  : TEXCOORD0;
+  float3 norm : NORMAL;
+};
+
+struct HSPerPatchData
+{
+  float edges[3] : SV_TessFactor;
+  float inside   : SV_InsideTessFactor;
+};
+
+// Should not be selected, since later candidate function with same name exists.
+// If selected, it should fail, since patch size mismatches HS function.
+HSPerPatchData HSPerPatchFunc1(
+  const InputPatch< PSSceneIn, 16 > points)
+{
+  HSPerPatchData d;
+
+  d.edges[0] = -5;
+  d.edges[1] = -6;
+  d.edges[2] = -7;
+  d.inside = T_unused.Load(1).x;
+
+  return d;
+}
+
+HSPerPatchData HSPerPatchFunc2(
+  const InputPatch< PSSceneIn, 4 > points)
+{
+  HSPerPatchData d;
+
+  d.edges[0] = -5;
+  d.edges[1] = -6;
+  d.edges[2] = -7;
+  d.inside = -8;
+
+  return d;
+}
+
+
+[shader("hull")]
+[domain("tri")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_cw")]
+[patchconstantfunc("HSPerPatchFunc1")]
+[outputcontrolpoints(3)]
+void HSMain1( const uint id : SV_OutputControlPointID,
+              const InputPatch< PSSceneIn, 3 > points )
+{
+}
+
+[shader("hull")]
+[domain("quad")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_cw")]
+[patchconstantfunc("HSPerPatchFunc2")]
+[outputcontrolpoints(4)]
+void HSMain2( const uint id : SV_OutputControlPointID,
+              const InputPatch< PSSceneIn, 4 > points )
+{
+}
+
+[shader("hull")]
+[domain("tri")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_ccw")]
+[patchconstantfunc("HSPerPatchFunc1")]
+[outputcontrolpoints(3)]
+void HSMain3( const uint id : SV_OutputControlPointID,
+              const InputPatch< PSSceneIn, 3 > points )
+{
+}
+
+// actual selected HSPerPatchFunc1 for HSMain1 and HSMain3
+HSPerPatchData HSPerPatchFunc1()
+{
+  HSPerPatchData d;
+
+  d.edges[0] = -5;
+  d.edges[1] = -6;
+  d.edges[2] = -7;
+  d.inside = -8;
+
+  return d;
+}
+
+// CHECK: ID3D12LibraryReflection:
+// CHECK:   D3D12_LIBRARY_DESC:
+// CHECK:     FunctionCount: 5
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?HSMain1@@YAXIV?$InputPatch@UPSSceneIn@@$02@@@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK-NOT:     D3D12_FUNCTION_DESC: Name: \01?HSMain2@@YAXIV?$InputPatch@UPSSceneIn@@$03@@@Z
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?HSMain3@@YAXIV?$InputPatch@UPSSceneIn@@$02@@@Z
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK:     D3D12_FUNCTION_DESC: Name: \01?HSPerPatchFunc1@@YA?AUHSPerPatchData@@XZ
+// CHECK:       Shader Version: Library 6.3
+// CHECK:       BoundResources: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK-NOT:     D3D12_FUNCTION_DESC: Name: \01?HSPerPatchFunc2@@YA?AUHSPerPatchData@@V?$InputPatch@UPSSceneIn@@$03@@@Z
+// CHECK:     D3D12_FUNCTION_DESC: Name: HSMain1
+// CHECK:       Shader Version: Hull 6.3
+// CHECK:       BoundResources: 0
+// CHECK:   ID3D12FunctionReflection:
+// CHECK-NOT:     D3D12_FUNCTION_DESC: Name: HSMain2
+// CHECK:     D3D12_FUNCTION_DESC: Name: HSMain3
+// CHECK:       Shader Version: Hull 6.3
+// CHECK:       BoundResources: 0

+ 40 - 0
tools/clang/test/CodeGenHLSL/quick-test/lib_exports_collision1.hlsl

@@ -0,0 +1,40 @@
+// RUN: %dxc -auto-binding-space 13 -exports Foo=VSMain;Foo=VSMainDup;Foo=\01?VSMain@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z;Foo=\01?VSMainDup@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z;RayGen;RayGen=fn -T lib_6_3 %s | FileCheck %s
+
+// Verify export collision errors
+// CHECK: error: Export name collides with another export: \01?Foo@@YA?AV?$vector@M$03@@V?$vector@H$02@@@Z
+// CHECK: error: Export name collides with another export: \01?RayGen@@YAXXZ
+// CHECK: error: Export name collides with another export: Foo
+
+Buffer<int> T0;
+
+Texture2D<float4> T1;
+
+struct Foo { uint u; float f; };
+StructuredBuffer<Foo> T2;
+
+RWByteAddressBuffer U0;
+
+[shader("vertex")]
+float4 VSMain(int3 coord : COORD) : SV_Position {
+  return T1.Load(coord);
+}
+
+[shader("vertex")]
+float4 VSMainDup(int3 coord : COORD) : SV_Position {
+  return T1.Load(coord);
+}
+
+[shader("pixel")]
+float4 PSMain(int idx : INDEX) : SV_Target {
+  return T2[T0.Load(idx)].f;
+}
+
+void fn() {
+}
+
+[shader("raygeneration")]
+void RayGen() {
+  uint2 dim = DispatchRaysDimensions();
+  uint2 idx = DispatchRaysIndex();
+  U0.Store(idx.y * dim.x * 4 + idx.x * 4, idx.x ^ idx.y);
+}

+ 6 - 47
tools/clang/tools/dxcompiler/dxclinker.cpp

@@ -69,23 +69,6 @@ public:
           *ppResult // Linker output status, buffer, and errors
           *ppResult // Linker output status, buffer, and errors
   ) override;
   ) override;
 
 
-  // Links the shader with export and produces a shader blob that the Direct3D
-  // runtime can use.
-  HRESULT STDMETHODCALLTYPE LinkWithExports(
-      _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
-      _In_count_(exportCount) const DxcDefine *pExports, // Array of exports
-      _In_ UINT32 exportCount,                           // Number of exports
-      _COM_Outptr_ IDxcOperationResult *
-          *ppResult // Linker output status, buffer, and errors
-      ) override;
-
   HRESULT STDMETHODCALLTYPE RegisterDxilContainerEventHandler(
   HRESULT STDMETHODCALLTYPE RegisterDxilContainerEventHandler(
       IDxcContainerEventsHandler *pHandler, UINT64 *pCookie) override {
       IDxcContainerEventsHandler *pHandler, UINT64 *pCookie) override {
     DxcThreadMalloc TM(m_pMalloc);
     DxcThreadMalloc TM(m_pMalloc);
@@ -166,6 +149,8 @@ DxcLinker::RegisterLibrary(_In_opt_ LPCWSTR pLibName, // Name of the library.
   }
   }
 }
 }
 
 
+// Links the shader and produces a shader blob that the Direct3D runtime can
+// use.
 HRESULT STDMETHODCALLTYPE DxcLinker::Link(
 HRESULT STDMETHODCALLTYPE DxcLinker::Link(
     _In_opt_ LPCWSTR pEntryName, // Entry point name
     _In_opt_ LPCWSTR pEntryName, // Entry point name
     _In_ LPCWSTR pTargetProfile, // shader profile to link
     _In_ LPCWSTR pTargetProfile, // shader profile to link
@@ -177,27 +162,6 @@ HRESULT STDMETHODCALLTYPE DxcLinker::Link(
     _In_ UINT32 argCount,          // Number of arguments
     _In_ UINT32 argCount,          // Number of arguments
     _COM_Outptr_ IDxcOperationResult *
     _COM_Outptr_ IDxcOperationResult *
         *ppResult // Linker output status, buffer, and errors
         *ppResult // Linker output status, buffer, and errors
-) {
-  return LinkWithExports(pEntryName, pTargetProfile, pLibNames, libCount,
-                         pArguments, argCount, /*pExorts*/ nullptr,
-                         /*exportCount*/ 0, ppResult);
-}
-
-// Links the shader with export and produces a shader blob that the Direct3D
-// runtime can use.
-__override HRESULT STDMETHODCALLTYPE DxcLinker::LinkWithExports(
-    _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
-    _In_count_(exportCount) const DxcDefine *pExports, // Array of exports
-    _In_ UINT32 exportCount,                           // Number of exports
-    _COM_Outptr_ IDxcOperationResult *
-        *ppResult // Linker output status, buffer, and errors
 ) {
 ) {
   DxcThreadMalloc TM(m_pMalloc);
   DxcThreadMalloc TM(m_pMalloc);
   // Prepare UTF8-encoded versions of API values.
   // Prepare UTF8-encoded versions of API values.
@@ -237,7 +201,7 @@ __override HRESULT STDMETHODCALLTYPE DxcLinker::LinkWithExports(
     }
     }
 
 
     std::string warnings;
     std::string warnings;
-    llvm::raw_string_ostream w(warnings);
+    //llvm::raw_string_ostream w(warnings);
     IFT(CreateMemoryStream(pMalloc, &pDiagStream));
     IFT(CreateMemoryStream(pMalloc, &pDiagStream));
     raw_stream_ostream DiagStream(pDiagStream);
     raw_stream_ostream DiagStream(pDiagStream);
     llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
     llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
@@ -252,16 +216,11 @@ __override HRESULT STDMETHODCALLTYPE DxcLinker::LinkWithExports(
       bSuccess &= m_pLinker->AttachLib(pUtf8LibName.m_psz);
       bSuccess &= m_pLinker->AttachLib(pUtf8LibName.m_psz);
     }
     }
 
 
+    dxilutil::ExportMap exportMap;
+    bSuccess = exportMap.ParseExports(opts.Exports, DiagStream);
+
     bool hasErrorOccurred = !bSuccess;
     bool hasErrorOccurred = !bSuccess;
     if (bSuccess) {
     if (bSuccess) {
-      StringMap<StringRef> exportMap;
-      std::vector<std::string> names(exportCount);
-      for (unsigned i=0;i<exportCount;i++) {
-        const DxcDefine &pExport = pExports[i];
-        names[i] = CW2A(pExport.Name);
-        exportMap[names[i]] = "";
-      }
-
       std::unique_ptr<Module> pM = m_pLinker->Link(
       std::unique_ptr<Module> pM = m_pLinker->Link(
           opts.EntryPoint, pUtf8TargetProfile.m_psz, exportMap);
           opts.EntryPoint, pUtf8TargetProfile.m_psz, exportMap);
       if (pM) {
       if (pM) {

+ 3 - 0
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -876,6 +876,9 @@ public:
 
 
     // AutoBindingSpace also enables automatic binding for libraries if set. UINT_MAX == unset
     // AutoBindingSpace also enables automatic binding for libraries if set. UINT_MAX == unset
     compiler.getCodeGenOpts().HLSLDefaultSpace = Opts.AutoBindingSpace;
     compiler.getCodeGenOpts().HLSLDefaultSpace = Opts.AutoBindingSpace;
+
+    // processed export names from -exports option:
+    compiler.getCodeGenOpts().HLSLLibraryExports = Opts.Exports;
   }
   }
 
 
   // IDxcVersionInfo
   // IDxcVersionInfo

+ 2 - 0
tools/clang/unittests/HLSL/DxcTestUtils.h

@@ -109,6 +109,8 @@ bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
                               bool maySucceedAnyway, bool bRegex);
                               bool maySucceedAnyway, bool bRegex);
 bool CheckMsgs(const LPCSTR pText, size_t TextCount, const LPCSTR *pErrorMsgs,
 bool CheckMsgs(const LPCSTR pText, size_t TextCount, const LPCSTR *pErrorMsgs,
                size_t errorMsgCount, bool bRegex);
                size_t errorMsgCount, bool bRegex);
+bool CheckNotMsgs(const LPCSTR pText, size_t TextCount, const LPCSTR *pErrorMsgs,
+               size_t errorMsgCount, bool bRegex);
 void GetDxilPart(dxc::DxcDllSupport &dllSupport, IDxcBlob *pProgram, IDxcBlob **pDxilPart);
 void GetDxilPart(dxc::DxcDllSupport &dllSupport, IDxcBlob *pProgram, IDxcBlob **pDxilPart);
 std::string DisassembleProgram(dxc::DxcDllSupport &dllSupport, IDxcBlob *pProgram);
 std::string DisassembleProgram(dxc::DxcDllSupport &dllSupport, IDxcBlob *pProgram);
 void SplitPassList(LPWSTR pPassesBuffer, std::vector<LPCWSTR> &passes);
 void SplitPassList(LPWSTR pPassesBuffer, std::vector<LPCWSTR> &passes);

+ 141 - 43
tools/clang/unittests/HLSL/LinkerTest.cpp

@@ -53,6 +53,10 @@ public:
   TEST_METHOD(RunLinkFailSelectRes);
   TEST_METHOD(RunLinkFailSelectRes);
   TEST_METHOD(RunLinkToLibWithUnresolvedFunctions);
   TEST_METHOD(RunLinkToLibWithUnresolvedFunctions);
   TEST_METHOD(RunLinkToLibWithUnresolvedFunctionsExports);
   TEST_METHOD(RunLinkToLibWithUnresolvedFunctionsExports);
+  TEST_METHOD(RunLinkToLibWithExportNamesSwapped);
+  TEST_METHOD(RunLinkToLibWithExportCollision);
+  TEST_METHOD(RunLinkToLibWithUnusedExport);
+  TEST_METHOD(RunLinkToLibWithNoExports);
   TEST_METHOD(RunLinkWithPotentialIntrinsicNameCollisions);
   TEST_METHOD(RunLinkWithPotentialIntrinsicNameCollisions);
 
 
 
 
@@ -98,10 +102,13 @@ public:
 
 
   void Link(LPCWSTR pEntryName, LPCWSTR pShaderModel, IDxcLinker *pLinker,
   void Link(LPCWSTR pEntryName, LPCWSTR pShaderModel, IDxcLinker *pLinker,
             ArrayRef<LPCWSTR> libNames, llvm::ArrayRef<LPCSTR> pCheckMsgs,
             ArrayRef<LPCWSTR> libNames, llvm::ArrayRef<LPCSTR> pCheckMsgs,
-            llvm::ArrayRef<LPCSTR> pCheckNotMsgs) {
+            llvm::ArrayRef<LPCSTR> pCheckNotMsgs,
+            llvm::ArrayRef<LPCWSTR> pArguments = {}) {
     CComPtr<IDxcOperationResult> pResult;
     CComPtr<IDxcOperationResult> pResult;
     VERIFY_SUCCEEDED(pLinker->Link(pEntryName, pShaderModel, libNames.data(),
     VERIFY_SUCCEEDED(pLinker->Link(pEntryName, pShaderModel, libNames.data(),
-                                   libNames.size(), nullptr, 0, &pResult));
+                                   libNames.size(),
+                                   pArguments.data(), pArguments.size(),
+                                   &pResult));
     CComPtr<IDxcBlob> pProgram;
     CComPtr<IDxcBlob> pProgram;
     CheckOperationSucceeded(pResult, &pProgram);
     CheckOperationSucceeded(pResult, &pProgram);
 
 
@@ -113,40 +120,17 @@ public:
     VERIFY_SUCCEEDED(pCompiler->Disassemble(pProgram, &pDisassembly));
     VERIFY_SUCCEEDED(pCompiler->Disassemble(pProgram, &pDisassembly));
     std::string IR = BlobToUtf8(pDisassembly);
     std::string IR = BlobToUtf8(pDisassembly);
     CheckMsgs(IR.c_str(), IR.size(), pCheckMsgs.data(), pCheckMsgs.size(), false);
     CheckMsgs(IR.c_str(), IR.size(), pCheckMsgs.data(), pCheckMsgs.size(), false);
-    for (auto notMsg : pCheckNotMsgs) {
-      VERIFY_IS_TRUE(IR.find(notMsg) == std::string::npos);
-    }
+    CheckNotMsgs(IR.c_str(), IR.size(), pCheckNotMsgs.data(), pCheckNotMsgs.size(), false);
   }
   }
 
 
-  void LinkWithExports(IDxcLinker *pLinker, ArrayRef<LPCWSTR> libNames,
-                       ArrayRef<DxcDefine> exportNames,
-                       llvm::ArrayRef<LPCSTR> pCheckMsgs,
-                       llvm::ArrayRef<LPCSTR> pCheckNotMsgs) {
-    CComPtr<IDxcOperationResult> pResult;
-    VERIFY_SUCCEEDED(pLinker->LinkWithExports(
-        /*pEntryName*/ nullptr, /*pShaderModel*/ L"lib_6_2", libNames.data(),
-        libNames.size(), nullptr, 0, exportNames.data(), exportNames.size(),
-        &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);
-    for (auto notMsg : pCheckNotMsgs) {
-      VERIFY_IS_TRUE(IR.find(notMsg) == std::string::npos);
-    }
-  }
   void LinkCheckMsg(LPCWSTR pEntryName, LPCWSTR pShaderModel, IDxcLinker *pLinker,
   void LinkCheckMsg(LPCWSTR pEntryName, LPCWSTR pShaderModel, IDxcLinker *pLinker,
-            ArrayRef<LPCWSTR> libNames, llvm::ArrayRef<LPCSTR> pErrorMsgs) {
+            ArrayRef<LPCWSTR> libNames, llvm::ArrayRef<LPCSTR> pErrorMsgs,
+            llvm::ArrayRef<LPCWSTR> pArguments = {}) {
     CComPtr<IDxcOperationResult> pResult;
     CComPtr<IDxcOperationResult> pResult;
-    VERIFY_SUCCEEDED(pLinker->Link(pEntryName, pShaderModel, libNames.data(),
-                                   libNames.size(), nullptr, 0, &pResult));
+    VERIFY_SUCCEEDED(pLinker->Link(pEntryName, pShaderModel,
+                                   libNames.data(), libNames.size(),
+                                   pArguments.data(), pArguments.size(),
+                                   &pResult));
     CheckOperationResultMsgs(pResult, pErrorMsgs.data(), pErrorMsgs.size(),
     CheckOperationResultMsgs(pResult, pErrorMsgs.data(), pErrorMsgs.size(),
                              false, false);
                              false, false);
   }
   }
@@ -374,8 +358,10 @@ TEST_F(LinkerTest, RunLinkToLibExport) {
 
 
   LPCWSTR libName2 = L"test";
   LPCWSTR libName2 = L"test";
   RegisterDxcModule(libName2, pLib, pLinker);
   RegisterDxcModule(libName2, pLib, pLinker);
-  DxcDefine exports[] = { {L"test", L""} };
-  LinkWithExports(pLinker, {libName, libName2}, exports, {"@\"\\01?test@@","@test"}, {"@\"\\01?GetBuf"});
+  Link(L"", L"lib_6_3", pLinker, {libName, libName2},
+    { "@\"\\01?renamed_test@@","@\"\\01?cloned_test@@","@test" },
+    { "@\"\\01?GetBuf", "@renamed_test", "@cloned_test" },
+    {L"-exports", L"renamed_test,cloned_test=\\01?test@@YA?AV?$vector@M$03@@I@Z;test"});
 }
 }
 
 
 TEST_F(LinkerTest, RunLinkFailSelectRes) {
 TEST_F(LinkerTest, RunLinkFailSelectRes) {
@@ -447,15 +433,127 @@ TEST_F(LinkerTest, RunLinkToLibWithUnresolvedFunctionsExports) {
   LPCWSTR libName2 = L"lib2";
   LPCWSTR libName2 = L"lib2";
   RegisterDxcModule(libName2, pLib2, pLinker);
   RegisterDxcModule(libName2, pLib2, pLinker);
 
 
-  DxcDefine exports[] = { { L"call_lib1", L"" }, { L"call_lib2", L"" } };
-  LinkWithExports(pLinker, { libName1, libName2 }, exports, {
-    "declare float @\"\\01?external_fn1@@YAMXZ\"()",
-    "declare float @\"\\01?external_fn2@@YAMXZ\"()",
-    "declare float @\"\\01?external_fn@@YAMXZ\"()",
-    "define float @\"\\01?call_lib1@@YAMXZ\"()",
-    "define float @\"\\01?call_lib2@@YAMXZ\"()"
-  }, { "declare float @\"\\01?unused_fn1", "declare float @\"\\01?unused_fn2",
-       "declare float @\"\\01?lib1_fn", "declare float @\"\\01?lib2_fn" });
+  Link(L"", L"lib_6_3", pLinker, { libName1, libName2 },
+    { "declare float @\"\\01?external_fn1@@YAMXZ\"()",
+      "declare float @\"\\01?external_fn2@@YAMXZ\"()",
+      "declare float @\"\\01?external_fn@@YAMXZ\"()",
+      "define float @\"\\01?renamed_lib1@@YAMXZ\"()",
+      "define float @\"\\01?call_lib2@@YAMXZ\"()"
+    },
+    { "float @\"\\01?unused_fn1", "float @\"\\01?unused_fn2",
+      "float @\"\\01?lib1_fn", "float @\"\\01?lib2_fn",
+      "float @\"\\01?call_lib1"
+    },
+    { L"-exports", L"renamed_lib1=call_lib1",
+      L"-exports", L"call_lib2"
+    });
+}
+
+TEST_F(LinkerTest, RunLinkToLibWithExportNamesSwapped) {
+  LPCWSTR option[] = { L"-Zi" };
+
+  CComPtr<IDxcBlob> pLib1;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_unresolved_func1.hlsl",
+    &pLib1, option, 1);
+  CComPtr<IDxcBlob> pLib2;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_unresolved_func2.hlsl",
+    &pLib2, option, 1);
+
+  CComPtr<IDxcLinker> pLinker;
+  CreateLinker(&pLinker);
+
+  LPCWSTR libName1 = L"lib1";
+  RegisterDxcModule(libName1, pLib1, pLinker);
+
+  LPCWSTR libName2 = L"lib2";
+  RegisterDxcModule(libName2, pLib2, pLinker);
+
+  Link(L"", L"lib_6_3", pLinker, { libName1, libName2 },
+    { "declare float @\"\\01?external_fn1@@YAMXZ\"()",
+      "declare float @\"\\01?external_fn2@@YAMXZ\"()",
+      "declare float @\"\\01?external_fn@@YAMXZ\"()",
+      "define float @\"\\01?call_lib1@@YAMXZ\"()",
+      "define float @\"\\01?call_lib2@@YAMXZ\"()"
+    },
+    { "float @\"\\01?unused_fn1", "float @\"\\01?unused_fn2",
+      "float @\"\\01?lib1_fn", "float @\"\\01?lib2_fn"
+    },
+    { L"-exports", L"call_lib2=call_lib1;call_lib1=call_lib2" });
+}
+
+TEST_F(LinkerTest, RunLinkToLibWithExportCollision) {
+  LPCWSTR option[] = { L"-Zi" };
+
+  CComPtr<IDxcBlob> pLib1;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_unresolved_func1.hlsl",
+    &pLib1, option, 1);
+  CComPtr<IDxcBlob> pLib2;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_unresolved_func2.hlsl",
+    &pLib2, option, 1);
+
+  CComPtr<IDxcLinker> pLinker;
+  CreateLinker(&pLinker);
+
+  LPCWSTR libName1 = L"lib1";
+  RegisterDxcModule(libName1, pLib1, pLinker);
+
+  LPCWSTR libName2 = L"lib2";
+  RegisterDxcModule(libName2, pLib2, pLinker);
+
+  LinkCheckMsg(L"", L"lib_6_3", pLinker, { libName1, libName2 },
+    { "Export name collides with another export: \\01?call_lib2@@YAMXZ"
+    },
+    { L"-exports", L"call_lib2=call_lib1;call_lib2" });
+}
+
+TEST_F(LinkerTest, RunLinkToLibWithUnusedExport) {
+  LPCWSTR option[] = { L"-Zi" };
+
+  CComPtr<IDxcBlob> pLib1;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_unresolved_func1.hlsl",
+    &pLib1, option, 1);
+  CComPtr<IDxcBlob> pLib2;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_unresolved_func2.hlsl",
+    &pLib2, option, 1);
+
+  CComPtr<IDxcLinker> pLinker;
+  CreateLinker(&pLinker);
+
+  LPCWSTR libName1 = L"lib1";
+  RegisterDxcModule(libName1, pLib1, pLinker);
+
+  LPCWSTR libName2 = L"lib2";
+  RegisterDxcModule(libName2, pLib2, pLinker);
+
+  LinkCheckMsg(L"", L"lib_6_3", pLinker, { libName1, libName2 },
+    { "Could not find target for export: call_lib"
+    },
+    { L"-exports", L"call_lib2=call_lib;call_lib1" });
+}
+
+TEST_F(LinkerTest, RunLinkToLibWithNoExports) {
+  LPCWSTR option[] = { L"-Zi" };
+
+  CComPtr<IDxcBlob> pLib1;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_unresolved_func1.hlsl",
+    &pLib1, option, 1);
+  CComPtr<IDxcBlob> pLib2;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_unresolved_func2.hlsl",
+    &pLib2, option, 1);
+
+  CComPtr<IDxcLinker> pLinker;
+  CreateLinker(&pLinker);
+
+  LPCWSTR libName1 = L"lib1";
+  RegisterDxcModule(libName1, pLib1, pLinker);
+
+  LPCWSTR libName2 = L"lib2";
+  RegisterDxcModule(libName2, pLib2, pLinker);
+
+  LinkCheckMsg(L"", L"lib_6_3", pLinker, { libName1, libName2 },
+    { "Library has no functions to export"
+    },
+    { L"-exports", L"call_lib2=call_lib" });
 }
 }
 
 
 TEST_F(LinkerTest, RunLinkWithPotentialIntrinsicNameCollisions) {
 TEST_F(LinkerTest, RunLinkWithPotentialIntrinsicNameCollisions) {

+ 42 - 2
tools/clang/unittests/HLSL/ValidationTest.cpp

@@ -45,7 +45,12 @@ static bool CheckMsgs(llvm::StringRef text, llvm::ArrayRef<LPCSTR> pMsgs,
       llvm::Regex RE(pMsg);
       llvm::Regex RE(pMsg);
       std::string reErrors;
       std::string reErrors;
       VERIFY_IS_TRUE(RE.isValid(reErrors));
       VERIFY_IS_TRUE(RE.isValid(reErrors));
-      VERIFY_IS_TRUE(RE.match(text));
+      if (!RE.match(text)) {
+        WEX::Logging::Log::Comment(WEX::Common::String().Format(
+          L"Unable to find regex '%S' in text:\r\n%.*S", pMsg, (pEnd - pStart),
+          pStart));
+        VERIFY_IS_TRUE(false);
+      }
     } else {
     } else {
       const char *pMatch = std::search(pStart, pEnd, pMsg, pMsg + strlen(pMsg));
       const char *pMatch = std::search(pStart, pEnd, pMsg, pMsg + strlen(pMsg));
       if (pEnd == pMatch) {
       if (pEnd == pMatch) {
@@ -53,7 +58,7 @@ static bool CheckMsgs(llvm::StringRef text, llvm::ArrayRef<LPCSTR> pMsgs,
             L"Unable to find '%S' in text:\r\n%.*S", pMsg, (pEnd - pStart),
             L"Unable to find '%S' in text:\r\n%.*S", pMsg, (pEnd - pStart),
             pStart));
             pStart));
       }
       }
-      VERIFY_ARE_NOT_EQUAL(pEnd, pMatch);
+      VERIFY_IS_FALSE(pEnd == pMatch);
     }
     }
   }
   }
   return true;
   return true;
@@ -65,6 +70,41 @@ bool CheckMsgs(const LPCSTR pText, size_t TextCount, const LPCSTR *pErrorMsgs,
                    llvm::ArrayRef<LPCSTR>(pErrorMsgs, errorMsgCount), bRegex);
                    llvm::ArrayRef<LPCSTR>(pErrorMsgs, errorMsgCount), bRegex);
 }
 }
 
 
+static bool CheckNotMsgs(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));
+      if (RE.match(text)) {
+        WEX::Logging::Log::Comment(WEX::Common::String().Format(
+          L"Unexpectedly found regex '%S' in text:\r\n%.*S", pMsg, (pEnd - pStart),
+          pStart));
+        VERIFY_IS_TRUE(false);
+      }
+    }
+    else {
+      const char *pMatch = std::search(pStart, pEnd, pMsg, pMsg + strlen(pMsg));
+      if (pEnd != pMatch) {
+        WEX::Logging::Log::Comment(WEX::Common::String().Format(
+          L"Unexpectedly found '%S' in text:\r\n%.*S", pMsg, (pEnd - pStart),
+          pStart));
+      }
+      VERIFY_IS_TRUE(pEnd == pMatch);
+    }
+  }
+  return true;
+}
+
+bool CheckNotMsgs(const LPCSTR pText, size_t TextCount, const LPCSTR *pErrorMsgs,
+                  size_t errorMsgCount, bool bRegex) {
+  return CheckNotMsgs(llvm::StringRef(pText, TextCount),
+    llvm::ArrayRef<LPCSTR>(pErrorMsgs, errorMsgCount), bRegex);
+}
+
 static
 static
 bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
 bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
                               llvm::ArrayRef<LPCSTR> pErrorMsgs,
                               llvm::ArrayRef<LPCSTR> pErrorMsgs,