Xiang Li 8 роки тому
батько
коміт
8e21407ca9

+ 1 - 0
include/dxc/HLSL/DxilConstants.h

@@ -114,6 +114,7 @@ namespace DXIL {
     Hull,
     Hull,
     Domain,
     Domain,
     Compute,
     Compute,
+    Library,
     Invalid,
     Invalid,
   };
   };
 
 

+ 2 - 1
include/dxc/HLSL/DxilShaderModel.h

@@ -37,6 +37,7 @@ public:
   bool IsHS() const     { return m_Kind == Kind::Hull; }
   bool IsHS() const     { return m_Kind == Kind::Hull; }
   bool IsDS() const     { return m_Kind == Kind::Domain; }
   bool IsDS() const     { return m_Kind == Kind::Domain; }
   bool IsCS() const     { return m_Kind == Kind::Compute; }
   bool IsCS() const     { return m_Kind == Kind::Compute; }
+  bool IsLib() const    { return m_Kind == Kind::Library; }
   bool IsValid() const;
   bool IsValid() const;
   bool IsValidForDxil() const;
   bool IsValidForDxil() const;
 
 
@@ -84,7 +85,7 @@ private:
               unsigned m_NumInputRegs, unsigned m_NumOutputRegs,
               unsigned m_NumInputRegs, unsigned m_NumOutputRegs,
               bool m_bUAVs, bool m_bTypedUavs, unsigned m_UAVRegsLim);
               bool m_bUAVs, bool m_bTypedUavs, unsigned m_UAVRegsLim);
 
 
-  static const unsigned kNumShaderModels = 33;
+  static const unsigned kNumShaderModels = 34;
   static const ShaderModel ms_ShaderModels[kNumShaderModels];
   static const ShaderModel ms_ShaderModels[kNumShaderModels];
 
 
   static const ShaderModel *GetInvalid();
   static const ShaderModel *GetInvalid();

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

@@ -151,6 +151,7 @@ public:
   bool DisassembleByteOffset; //OPT_No
   bool DisassembleByteOffset; //OPT_No
   bool DisaseembleHex; //OPT_Lx
   bool DisaseembleHex; //OPT_Lx
   bool IsRootSignatureProfile();
   bool IsRootSignatureProfile();
+  bool IsLibraryProfile();
 };
 };
 
 
 /// Use this class to capture, convert and handle the lifetime for the
 /// Use this class to capture, convert and handle the lifetime for the

+ 14 - 0
lib/DxcSupport/HLSLOptions.cpp

@@ -118,6 +118,10 @@ bool DxcOpts::IsRootSignatureProfile() {
       TargetProfile == "rootsig_1_1";
       TargetProfile == "rootsig_1_1";
 }
 }
 
 
+bool DxcOpts::IsLibraryProfile() {
+  return TargetProfile == "lib_6_1";
+}
+
 MainArgs::MainArgs(int argc, const wchar_t **argv, int skipArgCount) {
 MainArgs::MainArgs(int argc, const wchar_t **argv, int skipArgCount) {
   if (argc > skipArgCount) {
   if (argc > skipArgCount) {
     Utf8StringVector.reserve(argc - skipArgCount);
     Utf8StringVector.reserve(argc - skipArgCount);
@@ -221,6 +225,16 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
 
 
   opts.TargetProfile = Args.getLastArgValue(OPT_target_profile);
   opts.TargetProfile = Args.getLastArgValue(OPT_target_profile);
 
 
+  if (opts.IsLibraryProfile()) {
+    if (Args.getLastArg(OPT_entrypoint)) {
+      errors << "cannot specify entry point for a library";
+      return 1;
+    } else {
+      // Set entry point to impossible name.
+      opts.EntryPoint = "lib.no::entry";
+    }
+  }
+
   llvm::StringRef ver = Args.getLastArgValue(OPT_hlsl_version);
   llvm::StringRef ver = Args.getLastArgValue(OPT_hlsl_version);
   opts.HLSL2015 = opts.HLSL2016 = opts.HLSL2017 = false;
   opts.HLSL2015 = opts.HLSL2016 = opts.HLSL2017 = false;
   if (ver.empty() || ver == "2016") { opts.HLSL2016 = true; }   // Default to 2016
   if (ver.empty() || ver == "2016") { opts.HLSL2016 = true; }   // Default to 2016

+ 17 - 14
lib/HLSL/DxilGenerationPass.cpp

@@ -264,7 +264,7 @@ public:
     // Load up debug information, to cross-reference values and the instructions
     // Load up debug information, to cross-reference values and the instructions
     // used to load them.
     // used to load them.
     m_HasDbgInfo = getDebugMetadataVersionFromModule(M) != 0;
     m_HasDbgInfo = getDebugMetadataVersionFromModule(M) != 0;
-    if (!SM->IsCS()) {
+    if (!SM->IsCS() && !SM->IsLib()) {
       CreateDxilSignatures();
       CreateDxilSignatures();
 
 
       // Allocate input output.
       // Allocate input output.
@@ -272,7 +272,7 @@ public:
 
 
       GenerateDxilInputs();
       GenerateDxilInputs();
       GenerateDxilOutputs();
       GenerateDxilOutputs();
-    } else
+    } else if (SM->IsCS())
       GenerateDxilCSInputs();
       GenerateDxilCSInputs();
 
 
     if (SM->IsDS() || SM->IsHS())
     if (SM->IsDS() || SM->IsHS())
@@ -2448,17 +2448,19 @@ void DxilGenerationPass::GenerateDxilOperations(
     patchConstantFunc = funcProps.ShaderProps.HS.patchConstantFunc;
     patchConstantFunc = funcProps.ShaderProps.HS.patchConstantFunc;
   }
   }
 
 
-  for (auto F = M.begin(); F != M.end();) {
-    Function *func = F++;
+  if (!pSM->IsLib()) {
+    for (auto F = M.begin(); F != M.end();) {
+      Function *func = F++;
 
 
-    if (func->isDeclaration())
-      continue;
-    if (func == entry)
-      continue;
-    if (func == patchConstantFunc)
-      continue;
-    if (func->user_empty())
-      func->eraseFromParent();
+      if (func->isDeclaration())
+        continue;
+      if (func == entry)
+        continue;
+      if (func == patchConstantFunc)
+        continue;
+      if (func->user_empty())
+        func->eraseFromParent();
+    }
   }
   }
 
 
   TranslateBuiltinOperations(*m_pHLModule, m_extensionsCodegenHelper,
   TranslateBuiltinOperations(*m_pHLModule, m_extensionsCodegenHelper,
@@ -2524,9 +2526,10 @@ void DxilGenerationPass::TranslatePreciseAttribute() {
   // argument and call site without precise argument, need to clone the function
   // argument and call site without precise argument, need to clone the function
   // to propagate the precise for the precise call site.
   // to propagate the precise for the precise call site.
   // This should be done at CGMSHLSLRuntime::FinishCodeGen.
   // This should be done at CGMSHLSLRuntime::FinishCodeGen.
-
   Function *EntryFn = m_pHLModule->GetEntryFunction();
   Function *EntryFn = m_pHLModule->GetEntryFunction();
-  TranslatePreciseAttributeOnFunction(*EntryFn, M);
+  if (!m_pHLModule->GetShaderModel()->IsLib()) {
+    TranslatePreciseAttributeOnFunction(*EntryFn, M);
+  }
 
 
   if (m_pHLModule->GetShaderModel()->IsHS()) {
   if (m_pHLModule->GetShaderModel()->IsHS()) {
     HLFunctionProps &EntryQual = m_pHLModule->GetHLFunctionProps(EntryFn);
     HLFunctionProps &EntryQual = m_pHLModule->GetHLFunctionProps(EntryFn);

+ 19 - 4
lib/HLSL/DxilShaderModel.cpp

@@ -40,7 +40,9 @@ bool ShaderModel::operator==(const ShaderModel &other) const {
 }
 }
 
 
 bool ShaderModel::IsValid() const {
 bool ShaderModel::IsValid() const {
-  DXASSERT(IsPS() || IsVS() || IsGS() || IsHS() || IsDS() || IsCS() || m_Kind == Kind::Invalid, "invalid shader model");
+  DXASSERT(IsPS() || IsVS() || IsGS() || IsHS() || IsDS() || IsCS() ||
+               IsLib() || m_Kind == Kind::Invalid,
+           "invalid shader model");
   return m_Kind != Kind::Invalid;
   return m_Kind != Kind::Invalid;
 }
 }
 
 
@@ -89,11 +91,18 @@ const ShaderModel *ShaderModel::GetByName(const char *pszName) {
   case 'h':   Kind = Kind::Hull;      break;
   case 'h':   Kind = Kind::Hull;      break;
   case 'd':   Kind = Kind::Domain;    break;
   case 'd':   Kind = Kind::Domain;    break;
   case 'c':   Kind = Kind::Compute;   break;
   case 'c':   Kind = Kind::Compute;   break;
+  case 'l':   Kind = Kind::Library;   break;
   default:    return GetInvalid();
   default:    return GetInvalid();
   }
   }
   unsigned Idx = 3;
   unsigned Idx = 3;
-  if (pszName[1] != 's' || pszName[2] != '_')
-    return GetInvalid();
+  if (Kind != Kind::Library) {
+    if (pszName[1] != 's' || pszName[2] != '_')
+      return GetInvalid();
+  } else {
+    if (pszName[1] != 'i' || pszName[2] != 'b' || pszName[3] != '_')
+      return GetInvalid();
+    Idx = 4;
+  }
 
 
   unsigned Major;
   unsigned Major;
   switch (pszName[Idx++]) {
   switch (pszName[Idx++]) {
@@ -149,8 +158,12 @@ void ShaderModel::GetMinValidatorVersion(unsigned &ValMajor, unsigned &ValMinor)
   }
   }
 }
 }
 
 
+static const char *ShaderModelKindNames[] = {
+    "ps", "vs", "gs", "hs", "ds", "cs", "lib", "invalid",
+};
+
 std::string ShaderModel::GetKindName() const {
 std::string ShaderModel::GetKindName() const {
-  return std::string(m_pszName).substr(0, 2);
+  return std::string(ShaderModelKindNames[static_cast<unsigned int>(m_Kind)]);
 }
 }
 
 
 const ShaderModel *ShaderModel::GetInvalid() {
 const ShaderModel *ShaderModel::GetInvalid() {
@@ -199,6 +212,8 @@ const ShaderModel ShaderModel::ms_ShaderModels[kNumShaderModels] = {
   SM(Kind::Vertex,   6, 0, "vs_6_0",  32, 32,  true,  true,  UINT_MAX),
   SM(Kind::Vertex,   6, 0, "vs_6_0",  32, 32,  true,  true,  UINT_MAX),
   SM(Kind::Vertex,   6, 1, "vs_6_1",  32, 32,  true,  true,  UINT_MAX),
   SM(Kind::Vertex,   6, 1, "vs_6_1",  32, 32,  true,  true,  UINT_MAX),
 
 
+  SM(Kind::Library,  6, 1, "lib_6_1",  32, 32,  true,  true,  UINT_MAX),
+
   SM(Kind::Invalid,  0, 0, "invalid", 0,  0,   false, false, 0),
   SM(Kind::Invalid,  0, 0, "invalid", 0,  0,   false, false, 0),
 };
 };
 
 

+ 1 - 0
tools/clang/include/clang/Basic/LangOptions.h

@@ -155,6 +155,7 @@ public:
   std::string HLSLEntryFunction;
   std::string HLSLEntryFunction;
   unsigned RootSigMajor;
   unsigned RootSigMajor;
   unsigned RootSigMinor;
   unsigned RootSigMinor;
+  bool IsHLSLLibrary;
   // MS Change Ends
   // MS Change Ends
   
   
   bool isSignedOverflowDefined() const {
   bool isSignedOverflowDefined() const {

+ 3 - 2
tools/clang/lib/AST/ASTContext.cpp

@@ -8462,8 +8462,9 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
         Linkage == GVA_DiscardableODR)
         Linkage == GVA_DiscardableODR)
       return false;
       return false;
     // HLSL Change Starts
     // HLSL Change Starts
-    // Don't just return true because of visibility, unless building a library (which is not currently implemented)
-    return FD->getName() == getLangOpts().HLSLEntryFunction || IsPatchConstantFunctionDecl(FD);
+    // Don't just return true because of visibility, unless building a library
+    return FD->getName() == getLangOpts().HLSLEntryFunction ||
+           IsPatchConstantFunctionDecl(FD) || getLangOpts().IsHLSLLibrary;
     // HLSL Change Ends
     // HLSL Change Ends
   }
   }
   
   

+ 46 - 38
tools/clang/lib/CodeGen/CGHLSLMS.cpp

@@ -95,6 +95,7 @@ private:
   std::unordered_map<llvm::Type *, MDNode*> resMetadataMap;
   std::unordered_map<llvm::Type *, MDNode*> resMetadataMap;
 
 
   bool  m_bDebugInfo;
   bool  m_bDebugInfo;
+  bool  m_bIsLib;
 
 
   HLCBuffer &GetGlobalCBuffer() {
   HLCBuffer &GetGlobalCBuffer() {
     return *static_cast<HLCBuffer*>(&(m_pHLModule->GetCBuffer(globalCBIndex)));
     return *static_cast<HLCBuffer*>(&(m_pHLModule->GetCBuffer(globalCBIndex)));
@@ -306,6 +307,7 @@ CGMSHLSLRuntime::CGMSHLSLRuntime(CodeGenModule &CGM)
     Diags.Report(DiagID) << CGM.getCodeGenOpts().HLSLProfile;
     Diags.Report(DiagID) << CGM.getCodeGenOpts().HLSLProfile;
     return;
     return;
   }
   }
+  m_bIsLib = SM->IsLib();
   // TODO: add AllResourceBound.
   // TODO: add AllResourceBound.
   if (CGM.getCodeGenOpts().HLSLAvoidControlFlow && !CGM.getCodeGenOpts().HLSLAllResourcesBound) {
   if (CGM.getCodeGenOpts().HLSLAvoidControlFlow && !CGM.getCodeGenOpts().HLSLAllResourcesBound) {
     if (SM->IsSM51Plus()) {
     if (SM->IsSM51Plus()) {
@@ -394,6 +396,7 @@ void CGMSHLSLRuntime::CheckParameterAnnotation(
     SourceLocation SLoc, DxilParamInputQual paramQual, llvm::StringRef semFullName,
     SourceLocation SLoc, DxilParamInputQual paramQual, llvm::StringRef semFullName,
     bool isPatchConstantFunction) {
     bool isPatchConstantFunction) {
   const ShaderModel *SM = m_pHLModule->GetShaderModel();
   const ShaderModel *SM = m_pHLModule->GetShaderModel();
+
   DXIL::SigPointKind sigPoint = SigPointFromInputQual(
   DXIL::SigPointKind sigPoint = SigPointFromInputQual(
     paramQual, SM->GetKind(), isPatchConstantFunction);
     paramQual, SM->GetKind(), isPatchConstantFunction);
 
 
@@ -3767,13 +3770,16 @@ static void SimpleTransformForHLDXIR(llvm::Module *pM) {
 }
 }
 
 
 void CGMSHLSLRuntime::FinishCodeGen() {
 void CGMSHLSLRuntime::FinishCodeGen() {
-  SetEntryFunction();
-
-  // If at this point we haven't determined the entry function it's an error.
-  if (m_pHLModule->GetEntryFunction() == nullptr) {
-    assert(CGM.getDiags().hasErrorOccurred() &&
-           "else SetEntryFunction should have reported this condition");
-    return;
+  // Library don't have entry.
+  if (!m_bIsLib) {
+    SetEntryFunction();
+
+    // If at this point we haven't determined the entry function it's an error.
+    if (m_pHLModule->GetEntryFunction() == nullptr) {
+      assert(CGM.getDiags().hasErrorOccurred() &&
+             "else SetEntryFunction should have reported this condition");
+      return;
+    }
   }
   }
 
 
   // Create copy for clip plane.
   // Create copy for clip plane.
@@ -3807,38 +3813,39 @@ void CGMSHLSLRuntime::FinishCodeGen() {
   // Create Global variable and type annotation for each CBuffer.
   // Create Global variable and type annotation for each CBuffer.
   ConstructCBuffer(m_pHLModule, CBufferType, m_ConstVarAnnotationMap);
   ConstructCBuffer(m_pHLModule, CBufferType, m_ConstVarAnnotationMap);
 
 
-  // add global call to entry func
-  auto AddGlobalCall = [&](StringRef globalName, Instruction *InsertPt) {
-    GlobalVariable *GV = TheModule.getGlobalVariable(globalName);
-    if (GV) {
-      if (ConstantArray *CA = dyn_cast<ConstantArray>(GV->getInitializer())) {
-
-        IRBuilder<> Builder(InsertPt);
-        for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e;
-             ++i) {
-          if (isa<ConstantAggregateZero>(*i))
-            continue;
-          ConstantStruct *CS = cast<ConstantStruct>(*i);
-          if (isa<ConstantPointerNull>(CS->getOperand(1)))
-            continue;
+  if (!m_bIsLib) {
+    // add global call to entry func
+    auto AddGlobalCall = [&](StringRef globalName, Instruction *InsertPt) {
+      GlobalVariable *GV = TheModule.getGlobalVariable(globalName);
+      if (GV) {
+        if (ConstantArray *CA = dyn_cast<ConstantArray>(GV->getInitializer())) {
+
+          IRBuilder<> Builder(InsertPt);
+          for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e;
+               ++i) {
+            if (isa<ConstantAggregateZero>(*i))
+              continue;
+            ConstantStruct *CS = cast<ConstantStruct>(*i);
+            if (isa<ConstantPointerNull>(CS->getOperand(1)))
+              continue;
 
 
-          // Must have a function or null ptr.
-          if (!isa<Function>(CS->getOperand(1)))
-            continue;
-          Function *Ctor = cast<Function>(CS->getOperand(1));
-          assert(Ctor->getReturnType()->isVoidTy() && Ctor->arg_size() == 0 &&
-                 "function type must be void (void)");
-          Builder.CreateCall(Ctor);
+            // Must have a function or null ptr.
+            if (!isa<Function>(CS->getOperand(1)))
+              continue;
+            Function *Ctor = cast<Function>(CS->getOperand(1));
+            assert(Ctor->getReturnType()->isVoidTy() && Ctor->arg_size() == 0 &&
+                   "function type must be void (void)");
+            Builder.CreateCall(Ctor);
+          }
+          // remove the GV
+          GV->eraseFromParent();
         }
         }
-        // remove the GV
-        GV->eraseFromParent();
       }
       }
-    }
-  };
-  // need this for "llvm.global_dtors"?
-  AddGlobalCall("llvm.global_ctors",
-                EntryFunc->getEntryBlock().getFirstInsertionPt());
-
+    };
+    // need this for "llvm.global_dtors"?
+    AddGlobalCall("llvm.global_ctors",
+                  EntryFunc->getEntryBlock().getFirstInsertionPt());
+  }
   // 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);
 
 
@@ -3853,8 +3860,9 @@ void CGMSHLSLRuntime::FinishCodeGen() {
     // Skip no inline functions.
     // Skip no inline functions.
     if (f.hasFnAttribute(llvm::Attribute::NoInline))
     if (f.hasFnAttribute(llvm::Attribute::NoInline))
       continue;
       continue;
-    // Always inline.
-    f.addFnAttr(llvm::Attribute::AlwaysInline);
+    // Always inline for used functions.
+    if (!f.user_empty())
+      f.addFnAttr(llvm::Attribute::AlwaysInline);
   }
   }
 
 
   // Do simple transform to make later lower pass easier.
   // Do simple transform to make later lower pass easier.

+ 5 - 0
tools/clang/lib/Sema/SemaHLSL.cpp

@@ -8809,6 +8809,11 @@ void hlsl::DiagnoseTranslationUnit(clang::Sema *self) {
   if (self->getDiagnostics().hasErrorOccurred()) {
   if (self->getDiagnostics().hasErrorOccurred()) {
     return;
     return;
   }
   }
+  // Don't check entry function for library.
+  if (self->getLangOpts().IsHLSLLibrary) {
+    // TODO: validate no recursion start from every function.
+    return;
+  }
 
 
   // TODO: make these error 'real' errors rather than on-the-fly things
   // TODO: make these error 'real' errors rather than on-the-fly things
   // Validate that the entry point is available.
   // Validate that the entry point is available.

+ 30 - 0
tools/clang/test/CodeGenHLSL/lib_unused_func.hlsl

@@ -0,0 +1,30 @@
+// RUN: %dxc -T lib_6_1 %s | FileCheck %s
+
+// Make sure all function still exist.
+// CHECK: unused
+// CHECK: test_out
+// CHECK: test_inout
+// CHECK: test
+
+float unused() {
+  return 3;
+}
+
+void test_out(out float4 m, float4 a)
+{
+    m = abs(a*a.yxxx);
+}
+
+export void test_inout(inout float4 m, float4 a)
+{
+    m = abs(m+a*a.yxxx);
+}
+
+export float4 test(float4 a : A, float4 b:B) : SV_TARGET
+{
+  float4 x = b;
+  test_inout(x, a);
+  test_out(x, b);
+  return x;
+}
+

+ 4 - 1
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -1110,11 +1110,14 @@ public:
         rootSigMajor = 1;
         rootSigMajor = 1;
         rootSigMinor = 0;
         rootSigMinor = 0;
       }
       }
+      compiler.getLangOpts().IsHLSLLibrary = opts.IsLibraryProfile();
 
 
       // NOTE: this calls the validation component from dxil.dll; the built-in
       // NOTE: this calls the validation component from dxil.dll; the built-in
       // validator can be used as a fallback.
       // validator can be used as a fallback.
       bool produceFullContainer = !opts.CodeGenHighLevel && !opts.AstDump && !opts.OptDump && rootSigMajor == 0;
       bool produceFullContainer = !opts.CodeGenHighLevel && !opts.AstDump && !opts.OptDump && rootSigMajor == 0;
-      bool needsValidation = produceFullContainer && !opts.DisableValidation;
+
+      bool needsValidation = produceFullContainer && !opts.DisableValidation &&
+                             !opts.IsLibraryProfile();
 
 
       if (needsValidation) {
       if (needsValidation) {
         UINT32 majorVer, minorVer;
         UINT32 majorVer, minorVer;

+ 5 - 0
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -501,6 +501,7 @@ public:
   TEST_METHOD(CodeGenIntrinsic5)
   TEST_METHOD(CodeGenIntrinsic5)
   TEST_METHOD(CodeGenInvalidInputOutputTypes)
   TEST_METHOD(CodeGenInvalidInputOutputTypes)
   TEST_METHOD(CodeGenLegacyStruct)
   TEST_METHOD(CodeGenLegacyStruct)
+  TEST_METHOD(CodeGenLibUnusedFunc)
   TEST_METHOD(CodeGenLitInParen)
   TEST_METHOD(CodeGenLitInParen)
   TEST_METHOD(CodeGenLiteralShift)
   TEST_METHOD(CodeGenLiteralShift)
   TEST_METHOD(CodeGenLiveness1)
   TEST_METHOD(CodeGenLiveness1)
@@ -2771,6 +2772,10 @@ TEST_F(CompilerTest, CodeGenLegacyStruct) {
   CodeGenTestCheck(L"..\\CodeGenHLSL\\legacy_struct.hlsl");
   CodeGenTestCheck(L"..\\CodeGenHLSL\\legacy_struct.hlsl");
 }
 }
 
 
+TEST_F(CompilerTest, CodeGenLibUnusedFunc) {
+  CodeGenTestCheck(L"..\\CodeGenHLSL\\lib_unused_func.hlsl");
+}
+
 TEST_F(CompilerTest, CodeGenLitInParen) {
 TEST_F(CompilerTest, CodeGenLitInParen) {
   CodeGenTestCheck(L"..\\CodeGenHLSL\\lit_in_paren.hlsl");
   CodeGenTestCheck(L"..\\CodeGenHLSL\\lit_in_paren.hlsl");
 }
 }

+ 6 - 0
tools/clang/unittests/HLSL/OptionsTest.cpp

@@ -141,6 +141,12 @@ TEST_F(OptionsTest, ReadOptionsConflict) {
       L"hlsl.hlsl"};
       L"hlsl.hlsl"};
   MainArgsArr controlFlowArr(controlFlowArgs);
   MainArgsArr controlFlowArr(controlFlowArgs);
   ReadOptsTest(controlFlowArr, DxrFlags, "Cannot specify /Gfa and /Gfp together, use /? to get usage information");
   ReadOptsTest(controlFlowArr, DxrFlags, "Cannot specify /Gfa and /Gfp together, use /? to get usage information");
+
+  const wchar_t *libArgs[] = {
+      L"exe.exe",   L"/E",        L"main",    L"/T",           L"lib_6_1",
+      L"hlsl.hlsl"};
+  MainArgsArr libArr(libArgs);
+  ReadOptsTest(libArr, DxrFlags, "cannot specify entry point for a library");
 }
 }
 
 
 TEST_F(OptionsTest, ReadOptionsWhenHelpThenShortcut) {
 TEST_F(OptionsTest, ReadOptionsWhenHelpThenShortcut) {