浏览代码

Support link to lib.

Xiang Li 7 年之前
父节点
当前提交
247f74a647

+ 2 - 0
include/dxc/HLSL/DxilModule.h

@@ -132,6 +132,8 @@ public:
   // DxilFunctionProps.
   bool HasDxilFunctionProps(llvm::Function *F) const;
   DxilFunctionProps &GetDxilFunctionProps(llvm::Function *F);
+  void AddDxilFunctionProps(llvm::Function *F, std::unique_ptr<DxilFunctionProps> &info);
+
   // Move DxilFunctionProps of F to NewF.
   void ReplaceDxilFunctionProps(llvm::Function *F, llvm::Function *NewF);
   void SetPatchConstantFunctionForHS(llvm::Function *hullShaderFunc, llvm::Function *patchConstantFunc);

+ 269 - 129
lib/HLSL/DxilLinker.cpp

@@ -316,12 +316,18 @@ struct DxilLinkJob {
   DxilLinkJob(LLVMContext &Ctx, unsigned valMajor, unsigned valMinor) : m_ctx(Ctx), m_valMajor(valMajor), m_valMinor(valMinor) {}
   std::unique_ptr<llvm::Module>
   Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
-       StringRef profile);
+       const ShaderModel *pSM);
+  std::unique_ptr<llvm::Module> LinkToLib(const ShaderModel *pSM);
   void RunPreparePass(llvm::Module &M);
   void AddFunction(std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair);
   void AddFunction(llvm::Function *F);
 
 private:
+  void AddDxilOperations(Module *pM);
+  bool AddGlobals(DxilModule &DM, ValueToValueMapTy &vmap);
+  void CloneFunctions(ValueToValueMapTy &vmap);
+  void AddFunctions(DxilModule &DM, ValueToValueMapTy &vmap,
+                    std::unordered_set<Function *> &initFuncSet);
   bool AddResource(DxilResourceBase *res, llvm::GlobalVariable *GV);
   void AddResourceToDM(DxilModule &DM);
   std::unordered_map<DxilFunctionLinkInfo *, DxilLib *> m_functionDefs;
@@ -491,71 +497,97 @@ void DxilLinkJob::AddResourceToDM(DxilModule &DM) {
   }
 }
 
-std::unique_ptr<Module>
-DxilLinkJob::Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
-                  StringRef profile) {
-
-  Function *entryFunc = entryLinkPair.first->func;
-  DxilModule &entryDM = entryLinkPair.second->GetDxilModule();
-  if (!entryDM.HasDxilFunctionProps(entryFunc)) {
-    // Cannot get function props.
-    m_ctx.emitError(Twine(kNoEntryProps) + entryFunc->getName());
-    return nullptr;
-  }
-
-  DxilFunctionProps props = entryDM.GetDxilFunctionProps(entryFunc);
-  if (props.shaderKind == DXIL::ShaderKind::Library ||
-      props.shaderKind == DXIL::ShaderKind::Invalid ||
-      (props.shaderKind >= DXIL::ShaderKind::RayGeneration &&
-      props.shaderKind <= DXIL::ShaderKind::Callable)) {
-    m_ctx.emitError(profile + Twine(kInvalidProfile));
-    // Invalid profile.
-    return nullptr;
-  }
-
-  const ShaderModel *pSM = ShaderModel::GetByName(profile.data());
-  if (pSM->GetKind() != props.shaderKind) {
-    // Shader kind mismatch.
-    m_ctx.emitError(Twine(kShaderKindMismatch) + profile + " and " +
-                    ShaderModel::GetKindName(props.shaderKind));
-    return nullptr;
-  }
-
-  // Create new module.
-  std::unique_ptr<Module> pM =
-      llvm::make_unique<Module>(entryFunc->getName(), entryDM.GetCtx());
-  // Set target.
-  pM->setTargetTriple(entryDM.GetModule()->getTargetTriple());
-  // Add dxil operation functions before create DxilModule.
+void DxilLinkJob::AddDxilOperations(Module *pM) {
   for (auto &it : m_dxilFunctions) {
     Function *F = it.second;
     Function *NewF = Function::Create(F->getFunctionType(), F->getLinkage(),
-                                      F->getName(), pM.get());
+                                      F->getName(), pM);
     NewF->setAttributes(F->getAttributes());
     m_newFunctions[NewF->getName()] = NewF;
   }
+}
 
-  // Create DxilModule.
-  const bool bSkipInit = true;
-  DxilModule &DM = pM->GetOrCreateDxilModule(bSkipInit);
-  DM.SetShaderModel(pSM);
+bool DxilLinkJob::AddGlobals(DxilModule &DM, ValueToValueMapTy &vmap) {
+  DxilTypeSystem &typeSys = DM.GetTypeSystem();
+  Module *pM = DM.GetModule();
+  bool bSuccess = true;
+  for (auto &it : m_functionDefs) {
+    DxilFunctionLinkInfo *linkInfo = it.first;
+    DxilLib *pLib = it.second;
+    DxilModule &tmpDM = pLib->GetDxilModule();
+    DxilTypeSystem &tmpTypeSys = tmpDM.GetTypeSystem();
+    for (GlobalVariable *GV : linkInfo->usedGVs) {
+      // Skip added globals.
+      if (m_newGlobals.count(GV->getName())) {
+        if (vmap.find(GV) == vmap.end()) {
+          if (DxilResourceBase *res = pLib->GetResource(GV)) {
+            // For resource of same name, if class and type match, just map to
+            // same NewGV.
+            GlobalVariable *NewGV = m_newGlobals[GV->getName()];
+            if (AddResource(res, NewGV)) {
+              vmap[GV] = NewGV;
+            } else {
+              bSuccess = false;
+            }
+            continue;
+          }
 
-  // Set Validator version, verifying that it supports the requested profile
-  unsigned minValMajor, minValMinor;
-  DM.GetMinValidatorVersion(minValMajor, minValMinor);
-  if (minValMajor > m_valMajor || (minValMajor == m_valMajor && minValMinor > m_valMinor)) {
-    m_ctx.emitError(Twine(kInvalidValidatorVersion) + profile);
-    return nullptr;
+          // Redefine of global.
+          m_ctx.emitError(Twine(kRedefineGlobal) + GV->getName());
+          bSuccess = false;
+        }
+        continue;
+      }
+      Constant *Initializer = nullptr;
+      if (GV->hasInitializer())
+        Initializer = GV->getInitializer();
+
+      Type *Ty = GV->getType()->getElementType();
+      GlobalVariable *NewGV = new GlobalVariable(
+          *pM, Ty, GV->isConstant(), GV->getLinkage(), Initializer,
+          GV->getName(),
+          /*InsertBefore*/ nullptr, GV->getThreadLocalMode(),
+          GV->getType()->getAddressSpace(), GV->isExternallyInitialized());
+
+      m_newGlobals[GV->getName()] = NewGV;
+
+      vmap[GV] = NewGV;
+
+      typeSys.CopyTypeAnnotation(Ty, tmpTypeSys);
+
+      if (DxilResourceBase *res = pLib->GetResource(GV)) {
+        bSuccess &= AddResource(res, NewGV);
+      }
+    }
   }
-  DM.SetValidatorVersion(m_valMajor, m_valMinor);
+  return bSuccess;
+}
 
-  // Add type sys
-  DxilTypeSystem &typeSys = DM.GetTypeSystem();
+void DxilLinkJob::CloneFunctions(ValueToValueMapTy &vmap) {
+  for (auto &it : m_functionDefs) {
+    DxilFunctionLinkInfo *linkInfo = it.first;
 
-  ValueToValueMapTy vmap;
+    Function *F = linkInfo->func;
+    Function *NewF = m_newFunctions[F->getName()];
 
-  std::unordered_set<Function *> initFuncSet;
-  // Add function
+    // Add dxil functions to vmap.
+    for (Function *UsedF : linkInfo->usedFunctions) {
+      if (!vmap.count(UsedF)) {
+        // Extern function need match by name
+        DXASSERT(m_newFunctions.count(UsedF->getName()),
+                 "Must have new function.");
+        vmap[UsedF] = m_newFunctions[UsedF->getName()];
+      }
+    }
+
+    CloneFunction(F, NewF, vmap);
+  }
+}
+
+void DxilLinkJob::AddFunctions(DxilModule &DM, ValueToValueMapTy &vmap,
+                               std::unordered_set<Function *> &initFuncSet) {
+  DxilTypeSystem &typeSys = DM.GetTypeSystem();
+  Module *pM = DM.GetModule();
   for (auto &it : m_functionDefs) {
     DxilFunctionLinkInfo *linkInfo = it.first;
     DxilLib *pLib = it.second;
@@ -564,7 +596,7 @@ DxilLinkJob::Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
 
     Function *F = linkInfo->func;
     Function *NewF = Function::Create(F->getFunctionType(), F->getLinkage(),
-                                      F->getName(), pM.get());
+                                      F->getName(), pM);
     NewF->setAttributes(F->getAttributes());
 
     if (!NewF->hasFnAttribute(llvm::Attribute::NoInline))
@@ -583,6 +615,50 @@ DxilLinkJob::Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
 
     vmap[F] = NewF;
   }
+}
+
+std::unique_ptr<Module>
+DxilLinkJob::Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
+                  const ShaderModel *pSM) {
+  Function *entryFunc = entryLinkPair.first->func;
+  DxilModule &entryDM = entryLinkPair.second->GetDxilModule();
+  if (!entryDM.HasDxilFunctionProps(entryFunc)) {
+    // Cannot get function props.
+    m_ctx.emitError(Twine(kNoEntryProps) + entryFunc->getName());
+    return nullptr;
+  }
+
+  DxilFunctionProps props = entryDM.GetDxilFunctionProps(entryFunc);
+
+  if (pSM->GetKind() != props.shaderKind) {
+    // Shader kind mismatch.
+    m_ctx.emitError(Twine(kShaderKindMismatch) +
+                    ShaderModel::GetKindName(pSM->GetKind()) + " and " +
+                    ShaderModel::GetKindName(props.shaderKind));
+    return nullptr;
+  }
+
+  // Create new module.
+  std::unique_ptr<Module> pM =
+      llvm::make_unique<Module>(entryFunc->getName(), entryDM.GetCtx());
+  // Set target.
+  pM->setTargetTriple(entryDM.GetModule()->getTargetTriple());
+  // Add dxil operation functions before create DxilModule.
+  AddDxilOperations(pM.get());
+
+  // Create DxilModule.
+  const bool bSkipInit = true;
+  DxilModule &DM = pM->GetOrCreateDxilModule(bSkipInit);
+  DM.SetShaderModel(pSM);
+
+  // Set Validator version.
+  DM.SetValidatorVersion(m_valMajor, m_valMinor);
+
+  ValueToValueMapTy vmap;
+
+  std::unordered_set<Function *> initFuncSet;
+  // Add function
+  AddFunctions(DM, vmap, initFuncSet);
 
   // Set Entry
   Function *NewEntryFunc = m_newFunctions[entryFunc->getName()];
@@ -613,93 +689,101 @@ DxilLinkJob::Link(std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair,
   // Debug info.
 
   // Add global
-  bool bSuccess = true;
+  bool bSuccess = AddGlobals(DM, vmap);
+  if (!bSuccess)
+    return nullptr;
+
+  // Clone functions.
+  CloneFunctions(vmap);
+
+  // Call global constrctor.
+  IRBuilder<> Builder(
+      DM.GetEntryFunction()->getEntryBlock().getFirstInsertionPt());
   for (auto &it : m_functionDefs) {
     DxilFunctionLinkInfo *linkInfo = it.first;
     DxilLib *pLib = it.second;
-    DxilModule &tmpDM = pLib->GetDxilModule();
-    DxilTypeSystem &tmpTypeSys = tmpDM.GetTypeSystem();
-    for (GlobalVariable *GV : linkInfo->usedGVs) {
-      // Skip added globals.
-      if (m_newGlobals.count(GV->getName())) {
-        if (vmap.find(GV) == vmap.end()) {
-          if (DxilResourceBase *res = pLib->GetResource(GV)) {
-            // For resource of same name, if class and type match, just map to
-            // same NewGV.
-            GlobalVariable *NewGV = m_newGlobals[GV->getName()];
-            if (AddResource(res, NewGV)) {
-              vmap[GV] = NewGV;
-            } else {
-              bSuccess = false;
-            }
-            continue;
-          }
-
-          // Redefine of global.
-          m_ctx.emitError(Twine(kRedefineGlobal) + GV->getName());
-          bSuccess = false;
-        }
-        continue;
-      }
-      Constant *Initializer = nullptr;
-      if (GV->hasInitializer())
-        Initializer = GV->getInitializer();
 
-      Type *Ty = GV->getType()->getElementType();
-      GlobalVariable *NewGV = new GlobalVariable(
-          *pM, Ty, GV->isConstant(),
-          GV->getLinkage(), Initializer, GV->getName(),
-          /*InsertBefore*/ nullptr, GV->getThreadLocalMode(),
-          GV->getType()->getAddressSpace(), GV->isExternallyInitialized());
+    Function *F = linkInfo->func;
+    if (pLib->IsInitFunc(F)) {
+      Function *NewF = m_newFunctions[F->getName()];
+      Builder.CreateCall(NewF);
+    }
+  }
 
-      m_newGlobals[GV->getName()] = NewGV;
+  // Refresh intrinsic cache.
+  DM.GetOP()->RefreshCache();
 
-      vmap[GV] = NewGV;
+  // Add resource to DM.
+  // This should be after functions cloned.
+  AddResourceToDM(DM);
 
-      typeSys.CopyTypeAnnotation(Ty, tmpTypeSys);
+  RunPreparePass(*pM);
 
-      if (DxilResourceBase *res = pLib->GetResource(GV)) {
-        bSuccess &= AddResource(res, NewGV);
-      }
-    }
-  }
+  return pM;
+}
 
-  if (!bSuccess)
-    return nullptr;
+std::unique_ptr<Module>
+DxilLinkJob::LinkToLib(const ShaderModel *pSM) {
+  DxilLib *pLib = m_functionDefs.begin()->second;
+  DxilModule &tmpDM = pLib->GetDxilModule();
+  // Create new module.
+  std::unique_ptr<Module> pM =
+      llvm::make_unique<Module>("merged_lib", tmpDM.GetCtx());
+  // Set target.
+  pM->setTargetTriple(tmpDM.GetModule()->getTargetTriple());
+  // Add dxil operation functions before create DxilModule.
+  AddDxilOperations(pM.get());
 
-  // Clone functions.
-  for (auto &it : m_functionDefs) {
-    DxilFunctionLinkInfo *linkInfo = it.first;
+  // Create DxilModule.
+  const bool bSkipInit = true;
+  DxilModule &DM = pM->GetOrCreateDxilModule(bSkipInit);
+  DM.SetShaderModel(pSM);
 
-    Function *F = linkInfo->func;
-    Function *NewF = m_newFunctions[F->getName()];
+  // Set Validator version.
+  DM.SetValidatorVersion(m_valMajor, m_valMinor);
 
-    // Add dxil functions to vmap.
-    for (Function *UsedF : linkInfo->usedFunctions) {
-      if (!vmap.count(UsedF)) {
-        // Extern function need match by name
-        DXASSERT(m_newFunctions.count(UsedF->getName()),
-                 "Must have new function.");
-        vmap[UsedF] = m_newFunctions[UsedF->getName()];
-      }
-    }
+  ValueToValueMapTy vmap;
 
-    CloneFunction(F, NewF, vmap);
-  }
+  std::unordered_set<Function *> initFuncSet;
+  // Add function
+  AddFunctions(DM, vmap, initFuncSet);
 
-  // Call global constrctor.
-  IRBuilder<> Builder(
-      DM.GetEntryFunction()->getEntryBlock().getFirstInsertionPt());
+  // Set DxilFunctionProps.
+  std::unordered_map<Function *, std::unique_ptr<DxilEntrySignature>>
+      DxilEntrySignatureMap;
   for (auto &it : m_functionDefs) {
     DxilFunctionLinkInfo *linkInfo = it.first;
     DxilLib *pLib = it.second;
+    DxilModule &tmpDM = pLib->GetDxilModule();
 
     Function *F = linkInfo->func;
-    if (pLib->IsInitFunc(F)) {
+    if (tmpDM.HasDxilFunctionProps(F)) {
       Function *NewF = m_newFunctions[F->getName()];
-      Builder.CreateCall(NewF);
+      DxilFunctionProps props = tmpDM.GetDxilFunctionProps(F);
+      std::unique_ptr<DxilFunctionProps> pProps =
+          std::make_unique<DxilFunctionProps>();
+      *pProps = props;
+      DM.AddDxilFunctionProps(NewF, pProps);
+    }
+
+    if (tmpDM.HasDxilEntrySignature(F)) {
+      Function *NewF = m_newFunctions[F->getName()];
+      std::unique_ptr<DxilEntrySignature> pSig =
+          llvm::make_unique<DxilEntrySignature>(tmpDM.GetDxilEntrySignature(F));
+      DxilEntrySignatureMap[NewF] = std::move(pSig);
     }
   }
+  DM.ResetEntrySignatureMap(std::move(DxilEntrySignatureMap));
+
+  // Debug info.
+
+  // Add global
+  bool bSuccess = AddGlobals(DM, vmap);
+  if (!bSuccess)
+    return nullptr;
+
+  // Clone functions.
+  CloneFunctions(vmap);
 
   // Refresh intrinsic cache.
   DM.GetOP()->RefreshCache();
@@ -910,22 +994,74 @@ bool DxilLinkerImpl::AddFunctions(SmallVector<StringRef, 4> &workList,
 
 std::unique_ptr<llvm::Module> DxilLinkerImpl::Link(StringRef entry,
                                                StringRef profile) {
-  StringSet<> addedFunctionSet;
-  SmallVector<StringRef, 4> workList;
-  workList.emplace_back(entry);
+  const ShaderModel *pSM = ShaderModel::GetByName(profile.data());
+  DXIL::ShaderKind kind = pSM->GetKind();
+  if (kind == DXIL::ShaderKind::Invalid ||
+      (kind >= DXIL::ShaderKind::RayGeneration &&
+       kind <= DXIL::ShaderKind::Callable)) {
+    m_ctx.emitError(profile + Twine(kInvalidProfile));
+    // Invalid 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, m_valMajor, m_valMinor);
 
   DenseSet<DxilLib *> libSet;
-  if (!AddFunctions(workList, libSet, addedFunctionSet, linkJob,
-                    /*bLazyLoadDone*/ false))
-    return nullptr;
+  StringSet<> addedFunctionSet;
+
+  bool bIsLib = pSM->IsLib();
+  if (!bIsLib) {
+    SmallVector<StringRef, 4> workList;
+    workList.emplace_back(entry);
+
+    if (!AddFunctions(workList, libSet, addedFunctionSet, linkJob,
+                      /*bLazyLoadDone*/ false))
+      return nullptr;
+
+  } else {
+    // Add every function for lib profile.
+    for (auto &it : m_functionNameMap) {
+      StringRef name = it.getKey();
+      std::pair<DxilFunctionLinkInfo *, DxilLib *> &linkPair = it.second;
+      DxilFunctionLinkInfo *linkInfo = linkPair.first;
+      DxilLib *pLib = linkPair.second;
+
+      Function *F = linkInfo->func;
+      pLib->LazyLoadFunction(F);
+
+      linkJob.AddFunction(linkPair);
+
+      libSet.insert(pLib);
+
+      addedFunctionSet.insert(name);
+    }
+    // Add every dxil functions.
+    for (auto *pLib : libSet) {
+      auto &DM = pLib->GetDxilModule();
+      DM.GetOP();
+      auto *pM = DM.GetModule();
+      for (Function &F : pM->functions()) {
+        if (hlsl::OP::IsDxilOpFunc(&F)) {
+          linkJob.AddFunction(&F);
+        }
+      }
+    }
+  }
 
   // Save global users.
   for (auto &pLib : libSet) {
     pLib->BuildGlobalUsage();
   }
 
+  SmallVector<StringRef, 4> workList;
   // Save global ctor users.
   for (auto &pLib : libSet) {
     pLib->CollectUsedInitFunctions(addedFunctionSet, workList);
@@ -937,10 +1073,14 @@ std::unique_ptr<llvm::Module> DxilLinkerImpl::Link(StringRef entry,
                     /*bLazyLoadDone*/ true))
     return nullptr;
 
-  std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair =
-      m_functionNameMap[entry];
+  if (!bIsLib) {
+    std::pair<DxilFunctionLinkInfo *, DxilLib *> &entryLinkPair =
+        m_functionNameMap[entry];
 
-  return linkJob.Link(entryLinkPair, profile);
+    return linkJob.Link(entryLinkPair, pSM);
+  } else {
+    return linkJob.LinkToLib(pSM);
+  }
 }
 
 namespace hlsl {

+ 7 - 0
lib/HLSL/DxilModule.cpp

@@ -1066,6 +1066,13 @@ DxilFunctionProps &DxilModule::GetDxilFunctionProps(llvm::Function *F) {
   DXASSERT(m_DxilFunctionPropsMap.count(F) != 0, "cannot find F in map");
   return *m_DxilFunctionPropsMap[F];
 }
+void DxilModule::AddDxilFunctionProps(
+    llvm::Function *F, std::unique_ptr<DxilFunctionProps> &info) {
+  DXASSERT(m_DxilFunctionPropsMap.count(F) == 0,
+           "F already in map, info will be overwritten");
+  DXASSERT_NOMSG(info->shaderKind != DXIL::ShaderKind::Invalid);
+  m_DxilFunctionPropsMap[F] = std::move(info);
+}
 void DxilModule::ReplaceDxilFunctionProps(llvm::Function *F,
                                           llvm::Function *NewF) {
   DXASSERT(m_DxilFunctionPropsMap.count(F) != 0, "cannot find F in map");

+ 4 - 1
lib/HLSL/DxilPreparePasses.cpp

@@ -375,7 +375,10 @@ private:
       std::vector<Function *> entries;
       for (iplist<Function>::iterator F : M.getFunctionList()) {
         if (DM.IsEntryThatUsesSignatures(F)) {
-          entries.emplace_back(F);
+          auto *FT = F->getFunctionType();
+          // Only do this when has parameters.
+          if (FT->getNumParams() > 0 || !FT->getReturnType()->isVoidTy())
+            entries.emplace_back(F);
         }
       }
       for (Function *entry : entries) {

+ 1 - 1
lib/HLSL/DxilTypeSystem.cpp

@@ -318,7 +318,7 @@ void DxilTypeSystem::CopyFunctionAnnotation(const llvm::Function *pDstFunction,
 
   // Copy the annotation.
   *dstAnnot = *annot;
-
+  dstAnnot->m_pFunction = pDstFunction;
   // Clone ret type annotation.
   CopyTypeAnnotation(pDstFunction->getReturnType(), src);
   // Clone param type annotations.

+ 18 - 7
tools/clang/tools/dxcompiler/dxclinker.cpp

@@ -164,10 +164,15 @@ HRESULT STDMETHODCALLTYPE DxcLinker::Link(
 ) {
   DxcThreadMalloc TM(m_pMalloc);
   // Prepare UTF8-encoded versions of API values.
-  CW2A pUtf8EntryPoint(pEntryName, CP_UTF8);
   CW2A pUtf8TargetProfile(pTargetProfile, CP_UTF8);
+  CW2A pUtf8EntryPoint(pEntryName, CP_UTF8);
   // TODO: read and validate options.
-
+  bool bIsLib = StringRef(pUtf8TargetProfile.m_psz).startswith_lower("lib");
+  if (bIsLib) {
+    if (StringRef(pUtf8EntryPoint.m_psz).size() > 0) {
+      return E_INVALIDARG;
+    }
+  }
   CComPtr<AbstractMemoryStream> pOutputStream;
 
   // Detach previous libraries.
@@ -218,11 +223,17 @@ HRESULT STDMETHODCALLTYPE DxcLinker::Link(
         outStream.flush();
 
         // Validation.
-        HRESULT valHR = dxcutil::ValidateAndAssembleToContainer(
-            std::move(pM), pOutputBlob, pMalloc, SerializeDxilFlags::None,
-            pOutputStream,
-            /*bDebugInfo*/ false, Diag);
-
+        HRESULT valHR = S_OK;
+        // Skip validation on lib for now.
+        if (!bIsLib) {
+          valHR = dxcutil::ValidateAndAssembleToContainer(
+              std::move(pM), pOutputBlob, pMalloc, SerializeDxilFlags::None,
+              pOutputStream,
+              /*bDebugInfo*/ false, Diag);
+        } else {
+          dxcutil::AssembleToContainer(std::move(pM), pOutputBlob, m_pMalloc,
+                                       SerializeDxilFlags::None, pOutputStream);
+        }
         // Callback after valid DXIL is produced
         if (SUCCEEDED(valHR)) {
           CComPtr<IDxcBlob> pTargetBlob;

+ 18 - 0
tools/clang/unittests/HLSL/LinkerTest.cpp

@@ -45,6 +45,7 @@ public:
   TEST_METHOD(RunLinkGlobalInit);
   TEST_METHOD(RunLinkNoAlloca);
   TEST_METHOD(RunLinkResRet);
+  TEST_METHOD(RunLinkToLib);
   TEST_METHOD(RunLinkFailReDefineGlobal);
   TEST_METHOD(RunLinkFailProfileMismatch);
   TEST_METHOD(RunLinkFailEntryNoProps);
@@ -299,6 +300,23 @@ TEST_F(LinkerTest, RunLinkResRet) {
   Link(L"test", L"ps_6_0", pLinker, {libName, libName2}, {}, {"alloca"});
 }
 
+TEST_F(LinkerTest, RunLinkToLib) {
+  CComPtr<IDxcBlob> pEntryLib;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_out_param_res.hlsl", &pEntryLib);
+  CComPtr<IDxcBlob> pLib;
+  CompileLib(L"..\\CodeGenHLSL\\shader-compat-suite\\lib_out_param_res_imp.hlsl", &pLib);
+
+  CComPtr<IDxcLinker> pLinker;
+  CreateLinker(&pLinker);
+
+  LPCWSTR libName = L"ps_main";
+  RegisterDxcModule(libName, pEntryLib, pLinker);
+
+  LPCWSTR libName2 = L"test";
+  RegisterDxcModule(libName2, pLib, pLinker);
+
+  Link(L"", L"lib_6_2", pLinker, {libName, libName2}, {}, {"alloca"});
+}
 
 TEST_F(LinkerTest, RunLinkFailSelectRes) {
   if (m_ver.SkipDxilVersion(1, 3)) return;