HRESULT STDMETHODCALLTYPE Compile()

in tools/clang/tools/dxcompiler/dxcompilerobj.cpp [615:1253]


  HRESULT STDMETHODCALLTYPE Compile(
    _In_ const DxcBuffer *pSource,                // Source text to compile
    _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments
    _In_ UINT32 argCount,                         // Number of arguments
    _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional)
    _In_ REFIID riid, _Out_ LPVOID *ppResult      // IDxcResult: status, buffer, and errors
  ) override {
    if (pSource == nullptr || ppResult == nullptr ||
        (argCount > 0 && pArguments == nullptr))
      return E_INVALIDARG;
    if (!(IsEqualIID(riid, __uuidof(IDxcResult)) ||
          IsEqualIID(riid, __uuidof(IDxcOperationResult))))
      return E_INVALIDARG;

    *ppResult = nullptr;

    HRESULT hr = S_OK;
    CComPtr<IDxcBlobUtf8> utf8Source;
    CComPtr<AbstractMemoryStream> pOutputStream;
    CComPtr<IDxcOperationResult> pDxcOperationResult;
    bool bCompileStarted = false;
    bool bPreprocessStarted = false;
    DxilShaderHash ShaderHashContent;
    DxcThreadMalloc TM(m_pMalloc);

    try {
      DefaultFPEnvScope fpEnvScope;

      IFT(CreateMemoryStream(m_pMalloc, &pOutputStream));

      // Parse command-line options into DxcOpts
      int argCountInt;
      IFT(UIntToInt(argCount, &argCountInt));
      hlsl::options::MainArgs mainArgs(argCountInt, pArguments, 0);
      hlsl::options::DxcOpts opts;
      std::string warnings;
      raw_string_ostream w(warnings);
      {
        bool finished = false;
        CComPtr<AbstractMemoryStream> pOptionErrorStream;
        IFT(CreateMemoryStream(m_pMalloc, &pOptionErrorStream));
        dxcutil::ReadOptsAndValidate(mainArgs, opts, pOptionErrorStream, &pDxcOperationResult, finished);
        if (finished) {
          IFT(pDxcOperationResult->QueryInterface(riid, ppResult));
          hr = S_OK;
          goto Cleanup;
        }
        if (pOptionErrorStream->GetPtrSize() > 0) {
          w << StringRef((const char*)pOptionErrorStream->GetPtr(), (size_t)pOptionErrorStream->GetPtrSize());
        }
      }

      bool isPreprocessing = !opts.Preprocess.empty();
      if (isPreprocessing) {
        DxcEtw_DXCompilerPreprocess_Start();
        bPreprocessStarted = true;
      } else {
        DxcEtw_DXCompilerCompile_Start();
        bCompileStarted = true;
      }

      CComPtr<DxcResult> pResult = DxcResult::Alloc(m_pMalloc);
      IFT(pResult->SetEncoding(opts.DefaultTextCodePage));
      DxcOutputObject primaryOutput;

      // Formerly API values.
      const char *pUtf8SourceName = opts.InputFile.empty() ? "hlsl.hlsl" : opts.InputFile.data();
      CA2W pUtf16SourceName(pUtf8SourceName, CP_UTF8);
      const char *pUtf8EntryPoint = opts.EntryPoint.empty() ? "main" : opts.EntryPoint.data();
      const char *pUtf8OutputName = isPreprocessing
                                    ? opts.Preprocess.data()
                                    : opts.OutputObject.empty()
                                      ? "" : opts.OutputObject.data();
      CA2W pUtf16OutputName(isPreprocessing ?
                              opts.Preprocess.data() : pUtf8OutputName,
                            CP_UTF8);
      LPCWSTR pObjectName = (!isPreprocessing && opts.OutputObject.empty()) ?
                            nullptr : pUtf16OutputName.m_psz;
      IFT(primaryOutput.SetName(pObjectName));

      // Wrap source in blob
      CComPtr<IDxcBlobEncoding> pSourceEncoding;
      IFT(hlsl::DxcCreateBlob(pSource->Ptr, pSource->Size,
        true, false, pSource->Encoding != 0, pSource->Encoding,
        nullptr, &pSourceEncoding));

 #ifdef ENABLE_SPIRV_CODEGEN
      // We want to embed the preprocessed source code in the final SPIR-V if
      // debug information is enabled. Therefore, we invoke Preprocess() here
      // first for such case. Then we invoke the compilation process over the
      // preprocessed source code, so that line numbers are consistent with the
      // embedded source code.
      if (!isPreprocessing && opts.GenSPIRV && opts.DebugInfo &&
          !opts.SpirvOptions.debugInfoVulkan) {
        CComPtr<IDxcResult> pSrcCodeResult;
        std::vector<LPCWSTR> PreprocessArgs;
        PreprocessArgs.reserve(argCount + 1);
        PreprocessArgs.assign(pArguments, pArguments + argCount);
        PreprocessArgs.push_back(L"-P");
        PreprocessArgs.push_back(L"preprocessed.hlsl");
        IFT(Compile(pSource, PreprocessArgs.data(), PreprocessArgs.size(), pIncludeHandler, IID_PPV_ARGS(&pSrcCodeResult)));
        HRESULT status;
        IFT(pSrcCodeResult->GetStatus(&status));
        if (SUCCEEDED(status)) {
          pSourceEncoding.Release();
          IFT(pSrcCodeResult->GetOutput(DXC_OUT_HLSL, IID_PPV_ARGS(&pSourceEncoding), nullptr));
        }
      }
#endif // ENABLE_SPIRV_CODEGEN

      // Convert source code encoding
      IFC(hlsl::DxcGetBlobAsUtf8(pSourceEncoding, m_pMalloc, &utf8Source,
                                 opts.DefaultTextCodePage));

      CComPtr<IDxcBlob> pOutputBlob;
      dxcutil::DxcArgsFileSystem *msfPtr = dxcutil::CreateDxcArgsFileSystem(
          utf8Source, pUtf16SourceName.m_psz, pIncludeHandler,
          opts.DefaultTextCodePage);
      std::unique_ptr<::llvm::sys::fs::MSFileSystem> msf(msfPtr);

      ::llvm::sys::fs::AutoPerThreadSystem pts(msf.get());
      IFTLLVM(pts.error_code());

      IFT(pOutputStream.QueryInterface(&pOutputBlob));

      primaryOutput.kind = DXC_OUT_OBJECT;
      if (opts.AstDump || opts.OptDump || opts.DumpDependencies)
        primaryOutput.kind = DXC_OUT_TEXT;
      else if (isPreprocessing)
        primaryOutput.kind = DXC_OUT_HLSL;

      IFT(pResult->SetOutputName(DXC_OUT_REFLECTION, opts.OutputReflectionFile));
      IFT(pResult->SetOutputName(DXC_OUT_SHADER_HASH, opts.OutputShaderHashFile));
      IFT(pResult->SetOutputName(DXC_OUT_ERRORS, opts.OutputWarningsFile));
      IFT(pResult->SetOutputName(DXC_OUT_ROOT_SIGNATURE, opts.OutputRootSigFile));

      if (opts.DisplayIncludeProcess)
        msfPtr->EnableDisplayIncludeProcess();

      IFT(msfPtr->RegisterOutputStream(L"output.bc", pOutputStream));
      IFT(msfPtr->CreateStdStreams(m_pMalloc));

      StringRef Data(utf8Source->GetStringPointer(),
                     utf8Source->GetStringLength());

      // Not very efficient but also not very important.
      std::vector<std::string> defines;
      CreateDefineStrings(opts.Defines.data(), opts.Defines.size(), defines);

      // Setup a compiler instance.
      raw_stream_ostream outStream(pOutputStream.p);
      llvm::LLVMContext llvmContext; // LLVMContext should outlive CompilerInstance
      std::unique_ptr<llvm::Module> debugModule;
      CComPtr<AbstractMemoryStream> pReflectionStream;
      CompilerInstance compiler;
      std::unique_ptr<TextDiagnosticPrinter> diagPrinter =
          llvm::make_unique<TextDiagnosticPrinter>(w, &compiler.getDiagnosticOpts());
      SetupCompilerForCompile(compiler, &m_langExtensionsHelper, pUtf8SourceName, diagPrinter.get(), defines, opts, pArguments, argCount);
      msfPtr->SetupForCompilerInstance(compiler);

      // The clang entry point (cc1_main) would now create a compiler invocation
      // from arguments, but depending on the Preprocess option, we either compile
      // to LLVM bitcode and then package that into a DXBC blob, or preprocess to
      // HLSL text.
      //
      // With the compiler invocation built from command line arguments, the
      // next step is to call ExecuteCompilerInvocation, which creates a
      // FrontendAction* of EmitBCAction, which is a CodeGenAction, which is an
      // ASTFrontendAction. That sets up a BackendConsumer as the ASTConsumer.
      compiler.getFrontendOpts().OutputFile = "output.bc";
      compiler.WriteDefaultOutputDirectly = true;
      compiler.setOutStream(&outStream);

      unsigned rootSigMajor = 0;
      unsigned rootSigMinor = 0;
      // NOTE: this calls the validation component from dxil.dll; the built-in
      // validator can be used as a fallback.
      bool produceFullContainer = false;
      bool needsValidation = false;
      bool validateRootSigContainer = false;

      if (isPreprocessing) {
        // These settings are back-compatible with fxc.
        clang::PreprocessorOutputOptions &PPOutOpts =
          compiler.getPreprocessorOutputOpts();
        PPOutOpts.ShowCPP = 1;            // Print normal preprocessed output.
        PPOutOpts.ShowComments = 0;       // Show comments.
        PPOutOpts.ShowLineMarkers = 1;    // Show \#line markers.
        PPOutOpts.UseLineDirectives = 1;  // Use \#line instead of GCC-style \# N.
        PPOutOpts.ShowMacroComments = 0;  // Show comments, even in macros.
        PPOutOpts.ShowMacros = 0;         // Print macro definitions.
        PPOutOpts.RewriteIncludes = 0;    // Preprocess include directives only.

        FrontendInputFile file(pUtf8SourceName, IK_HLSL);
        clang::PrintPreprocessedAction action;
        if (action.BeginSourceFile(compiler, file)) {
          action.Execute();
          action.EndSourceFile();
        }
        outStream.flush();
      } else {
        compiler.getLangOpts().HLSLEntryFunction =
          compiler.getCodeGenOpts().HLSLEntryFunction = pUtf8EntryPoint;
        compiler.getLangOpts().HLSLProfile =
          compiler.getCodeGenOpts().HLSLProfile = opts.TargetProfile;

        // Parse and apply 
        if (opts.BindingTableDefine.size()) {
          // Just pas the define for now because preprocessor is not available yet.
          struct BindingTableParserImpl : public CodeGenOptions::BindingTableParserType {
            CompilerInstance &compiler;
            std::string define;
            BindingTableParserImpl(CompilerInstance &compiler, StringRef define)
              :compiler(compiler), define(define.str())
            {}

            bool Parse(llvm::raw_ostream &os, hlsl::DxcBindingTable *outBindingTable) override {
              Preprocessor &pp = compiler.getPreprocessor();
              MacroInfo *macro = MacroExpander::FindMacroInfo(pp, define);
              if (!macro) {
                os << Twine("Binding table define'") + define + "' not found.";
                os.flush();
                return false;
              }

              std::string bindingTableStr;
              // Combine tokens into single string
              MacroExpander expander(pp, MacroExpander::STRIP_QUOTES);
              if (!expander.ExpandMacro(macro, &bindingTableStr)) {
                os << Twine("Binding table define'") + define + "' failed to expand.";
                os.flush();
                return false;
              }
              return hlsl::ParseBindingTable(
                define, StringRef(bindingTableStr),
                os, outBindingTable);
            }
          };

          compiler.getCodeGenOpts().BindingTableParser.reset(new BindingTableParserImpl(compiler, opts.BindingTableDefine));
        }
        else if (opts.ImportBindingTable.size()) {
          hlsl::options::StringRefUtf16 wstrRef(opts.ImportBindingTable);
          CComPtr<IDxcBlob> pBlob;
          std::string error;
          llvm::raw_string_ostream os(error);
          if (!pIncludeHandler) {
            os << Twine("Binding table binding file '") + opts.ImportBindingTable + "' specified, but no include handler was given.";
            os.flush();
            return ErrorWithString(error, riid, ppResult);
          }
          else if (SUCCEEDED(pIncludeHandler->LoadSource(wstrRef, &pBlob))) {
            bool succ = hlsl::ParseBindingTable(
              opts.ImportBindingTable,
              StringRef((const char *)pBlob->GetBufferPointer(), pBlob->GetBufferSize()),
              os, &compiler.getCodeGenOpts().HLSLBindingTable);

            if (!succ) {
              os.flush();
              return ErrorWithString(error, riid, ppResult);
            }
          }
          else {
            os << Twine("Could not load binding table file '") + opts.ImportBindingTable + "'.";
            os.flush();
            return ErrorWithString(error, riid, ppResult);
          }
        }

        if (compiler.getCodeGenOpts().HLSLProfile == "rootsig_1_1") {
          rootSigMajor = 1;
          rootSigMinor = 1;
        } else if (compiler.getCodeGenOpts().HLSLProfile == "rootsig_1_0") {
          rootSigMajor = 1;
          rootSigMinor = 0;
        }
        compiler.getLangOpts().IsHLSLLibrary = opts.IsLibraryProfile();

        // Clear entry function if library target
        if (compiler.getLangOpts().IsHLSLLibrary)
          compiler.getLangOpts().HLSLEntryFunction =
            compiler.getCodeGenOpts().HLSLEntryFunction = "";

        // NOTE: this calls the validation component from dxil.dll; the built-in
        // validator can be used as a fallback.
        produceFullContainer = !opts.CodeGenHighLevel && !opts.AstDump &&
                               !opts.OptDump && rootSigMajor == 0 &&
                               !opts.DumpDependencies;
        needsValidation = produceFullContainer && !opts.DisableValidation;

        if (compiler.getCodeGenOpts().HLSLProfile == "lib_6_x") {
          // Currently do not support stripping reflection from offline linking target.
          opts.KeepReflectionInDxil = true;
        }

        if (opts.ValVerMajor != UINT_MAX) {
          // user-specified validator version override
          compiler.getCodeGenOpts().HLSLValidatorMajorVer = opts.ValVerMajor;
          compiler.getCodeGenOpts().HLSLValidatorMinorVer = opts.ValVerMinor;
        } else {
          // Version from dxil.dll, or internal validator if unavailable
          dxcutil::GetValidatorVersion(&compiler.getCodeGenOpts().HLSLValidatorMajorVer,
                                      &compiler.getCodeGenOpts().HLSLValidatorMinorVer);
        }

        // Root signature-only container validation is only supported on 1.5 and above.
        validateRootSigContainer = DXIL::CompareVersions(
          compiler.getCodeGenOpts().HLSLValidatorMajorVer,
          compiler.getCodeGenOpts().HLSLValidatorMinorVer,
          1, 5) >= 0;
      }

      if (opts.AstDump) {
        clang::ASTDumpAction dumpAction;
        // Consider - ASTDumpFilter, ASTDumpLookups
        compiler.getFrontendOpts().ASTDumpDecls = true;
        FrontendInputFile file(pUtf8SourceName, IK_HLSL);
        dumpAction.BeginSourceFile(compiler, file);
        dumpAction.Execute();
        dumpAction.EndSourceFile();
        outStream.flush();
      } else if (opts.DumpDependencies) {
        auto dependencyCollector = std::make_shared<DependencyCollector>();
        compiler.addDependencyCollector(dependencyCollector);
        compiler.createPreprocessor(clang::TranslationUnitKind::TU_Complete);

        clang::PreprocessOnlyAction preprocessAction;
        FrontendInputFile file(pUtf8SourceName, IK_HLSL);
        preprocessAction.BeginSourceFile(compiler, file);
        preprocessAction.Execute();
        preprocessAction.EndSourceFile();

        outStream << (opts.OutputObject.empty() ? opts.InputFile
                                                : opts.OutputObject);
        bool firstDependency = true;
        for (auto &dependency : dependencyCollector->getDependencies()) {
          if (firstDependency) {
            outStream << ": " << dependency;
            firstDependency = false;
            continue;
          }
          outStream << " \\\n " << dependency;
        }
        outStream << "\n";
        outStream.flush();
      } else if (opts.OptDump) {
        EmitOptDumpAction action(&llvmContext);
        FrontendInputFile file(pUtf8SourceName, IK_HLSL);
        action.BeginSourceFile(compiler, file);
        action.Execute();
        action.EndSourceFile();
        outStream.flush();
      } else if (rootSigMajor) {
        HLSLRootSignatureAction action(
            compiler.getCodeGenOpts().HLSLEntryFunction, rootSigMajor,
            rootSigMinor);
        FrontendInputFile file(pUtf8SourceName, IK_HLSL);
        action.BeginSourceFile(compiler, file);
        action.Execute();
        action.EndSourceFile();
        outStream.flush();
        // Don't do work to put in a container if an error has occurred
        bool compileOK = !compiler.getDiagnostics().hasErrorOccurred();
        if (compileOK) {
          auto rootSigHandle = action.takeRootSigHandle();

          CComPtr<AbstractMemoryStream> pContainerStream;
          IFT(CreateMemoryStream(m_pMalloc, &pContainerStream));
          SerializeDxilContainerForRootSignature(rootSigHandle.get(),
                                                 pContainerStream);

          pOutputBlob.Release();
          IFT(pContainerStream.QueryInterface(&pOutputBlob));
          if (validateRootSigContainer && !opts.DisableValidation) {
            CComPtr<IDxcBlobEncoding> pValErrors;
            // Validation failure communicated through diagnostic error
            dxcutil::ValidateRootSignatureInContainer(
              pOutputBlob, &compiler.getDiagnostics());
          }
        }
      }
      // SPIRV change starts
#ifdef ENABLE_SPIRV_CODEGEN
      else if (!isPreprocessing && opts.GenSPIRV) {
        // Since SpirvOptions is passed to the SPIR-V CodeGen as a whole
        // structure, we need to copy a few non-spirv-specific options into the
        // structure.
        opts.SpirvOptions.enable16BitTypes = opts.Enable16BitTypes;
        opts.SpirvOptions.codeGenHighLevel = opts.CodeGenHighLevel;
        opts.SpirvOptions.defaultRowMajor = opts.DefaultRowMajor;
        opts.SpirvOptions.disableValidation = opts.DisableValidation;
        // Store a string representation of command line options.
        if (opts.DebugInfo)
          for (auto opt : mainArgs.getArrayRef())
            opts.SpirvOptions.clOptions += " " + std::string(opt);

        compiler.getCodeGenOpts().SpirvOptions = opts.SpirvOptions;
        clang::EmitSpirvAction action;
        FrontendInputFile file(pUtf8SourceName, IK_HLSL);
        action.BeginSourceFile(compiler, file);
        action.Execute();
        action.EndSourceFile();
        outStream.flush();
      }
#endif
      // SPIRV change ends
      else if (!isPreprocessing) {
        EmitBCAction action(&llvmContext);
        FrontendInputFile file(pUtf8SourceName, IK_HLSL);
        bool compileOK;
        if (action.BeginSourceFile(compiler, file)) {
          action.Execute();
          action.EndSourceFile();
          compileOK = !compiler.getDiagnostics().hasErrorOccurred();
        }
        else {
          compileOK = false;
        }
        outStream.flush();

        SerializeDxilFlags SerializeFlags = SerializeDxilFlags::None;
        if (opts.EmbedPDBName()) {
          SerializeFlags |= SerializeDxilFlags::IncludeDebugNamePart;
        }
        // If -Qembed_debug specified, embed the debug info.
        // Or, if there is no output pointer for the debug blob (such as when called by Compile()),
        // embed the debug info and emit a note.
        if (opts.EmbedDebugInfo()) {
          SerializeFlags |= SerializeDxilFlags::IncludeDebugInfoPart;
        }
        if (opts.DebugNameForSource) {
          // Implies name part
          SerializeFlags |= SerializeDxilFlags::IncludeDebugNamePart;
          SerializeFlags |= SerializeDxilFlags::DebugNameDependOnSource;
        } else if (opts.DebugNameForBinary) {
          // Implies name part
          SerializeFlags |= SerializeDxilFlags::IncludeDebugNamePart;
        }
        if (!opts.KeepReflectionInDxil) {
          SerializeFlags |= SerializeDxilFlags::StripReflectionFromDxilPart;
        }
        if (!opts.StripReflection) {
          SerializeFlags |= SerializeDxilFlags::IncludeReflectionPart;
        }
        if (opts.StripRootSignature) {
          SerializeFlags |= SerializeDxilFlags::StripRootSignature;
        }

        // Don't do work to put in a container if an error has occurred
        // Do not create a container when there is only a a high-level representation in the module.
        if (compileOK && !opts.CodeGenHighLevel) {
          HRESULT valHR = S_OK;
          CComPtr<AbstractMemoryStream> pRootSigStream;
          IFT(CreateMemoryStream(DxcGetThreadMallocNoRef(), &pReflectionStream));
          IFT(CreateMemoryStream(DxcGetThreadMallocNoRef(), &pRootSigStream));

          std::unique_ptr<llvm::Module> serializeModule( action.takeModule() );

          // Clone and save the copy.
          if (opts.GenerateFullDebugInfo()) {
            debugModule.reset(llvm::CloneModule(serializeModule.get()));
          }

          dxcutil::AssembleInputs inputs(
                std::move(serializeModule), pOutputBlob, m_pMalloc, SerializeFlags,
                pOutputStream,
                opts.GetPDBName(), &compiler.getDiagnostics(),
                &ShaderHashContent, pReflectionStream, pRootSigStream);

          if (needsValidation) {
            valHR = dxcutil::ValidateAndAssembleToContainer(inputs);
          } else {
            dxcutil::AssembleToContainer(inputs);
          }

          // Callback after valid DXIL is produced
          if (SUCCEEDED(valHR)) {
            CComPtr<IDxcBlob> pTargetBlob;
            if (m_pDxcContainerEventsHandler != nullptr) {
              HRESULT hr = m_pDxcContainerEventsHandler->OnDxilContainerBuilt(pOutputBlob, &pTargetBlob);
              if (SUCCEEDED(hr) && pTargetBlob != nullptr) {
                std::swap(pOutputBlob, pTargetBlob);
              }
            }

            if (pOutputBlob && produceFullContainer && (SerializeFlags & SerializeDxilFlags::IncludeDebugNamePart) != 0) {
              const DxilContainerHeader *pContainer = reinterpret_cast<DxilContainerHeader *>(pOutputBlob->GetBufferPointer());
              DXASSERT(IsValidDxilContainer(pContainer, pOutputBlob->GetBufferSize()), "else invalid container generated");
              auto it = std::find_if(begin(pContainer), end(pContainer), DxilPartIsType(DFCC_ShaderDebugName));
              if (it != end(pContainer)) {
                const char *pDebugName;
                if (GetDxilShaderDebugName(*it, &pDebugName, nullptr) && pDebugName && *pDebugName) {
                  IFT(pResult->SetOutputName(DXC_OUT_PDB, pDebugName));
                }
              }
            }

            if (pReflectionStream && pReflectionStream->GetPtrSize()) {
              CComPtr<IDxcBlob> pReflection;
              IFT(pReflectionStream->QueryInterface(&pReflection));
              IFT(pResult->SetOutputObject(DXC_OUT_REFLECTION, pReflection));
            }
            if (pRootSigStream && pRootSigStream->GetPtrSize()) {
              CComPtr<IDxcBlob> pRootSignature;
              IFT(pRootSigStream->QueryInterface(&pRootSignature));
              if (validateRootSigContainer && needsValidation) {
                CComPtr<IDxcBlobEncoding> pValErrors;
                // Validation failure communicated through diagnostic error
                dxcutil::ValidateRootSignatureInContainer(pRootSignature, &compiler.getDiagnostics());
              }
              IFT(pResult->SetOutputObject(DXC_OUT_ROOT_SIGNATURE, pRootSignature));
            }
            CComPtr<IDxcBlob> pHashBlob;
            IFT(hlsl::DxcCreateBlobOnHeapCopy(&ShaderHashContent, (UINT32)sizeof(ShaderHashContent), &pHashBlob));
            IFT(pResult->SetOutputObject(DXC_OUT_SHADER_HASH, pHashBlob));
          } // SUCCEEDED(valHR)
        } // compileOK && !opts.CodeGenHighLevel
      }

      // Add std err to warnings.
      msfPtr->WriteStdErrToStream(w);
      CComPtr<IStream> pErrorStream;
      msfPtr->GetStdOutpuHandleStream(&pErrorStream);
      CComPtr<IDxcBlob> pErrorBlob;
      IFT(pErrorStream.QueryInterface(&pErrorBlob));
      if (IsBlobNullOrEmpty(pErrorBlob)) {
        IFT(pResult->SetOutputString(DXC_OUT_ERRORS, warnings.c_str(), warnings.size()));
      } else {
        IFT(pResult->SetOutputObject(DXC_OUT_ERRORS, pErrorBlob));
      }

      bool hasErrorOccurred = compiler.getDiagnostics().hasErrorOccurred();

      bool writePDB = opts.GeneratePDB() && produceFullContainer;

      // SPIRV change starts
#if defined(ENABLE_SPIRV_CODEGEN)
      writePDB &= !opts.GenSPIRV;
#endif
      // SPIRV change ends

      if (!hasErrorOccurred && writePDB) {
        CComPtr<IDxcBlob> pStrippedContainer;
        {
          // Create the shader source information for PDB
          hlsl::SourceInfoWriter debugSourceInfoWriter;
          const hlsl::DxilSourceInfo *pSourceInfo = nullptr;
          if (!opts.SourceInDebugModule) { // If we are using old PDB format where sources are in debug module, do not generate source info at all
            debugSourceInfoWriter.Write(opts.TargetProfile, opts.EntryPoint, compiler.getCodeGenOpts(), compiler.getSourceManager());
            pSourceInfo = debugSourceInfoWriter.GetPart();
          }

          CComPtr<IDxcBlob> pDebugProgramBlob;
          CComPtr<AbstractMemoryStream> pReflectionInPdb;
          // Don't include the debug part if using source only PDB
          if (opts.SourceOnlyDebug) {
            assert(pSourceInfo);
            pReflectionInPdb = pReflectionStream;
          }
          else {
            if (!opts.SourceInDebugModule) {
              // Strip out the source related metadata
              debugModule->GetOrCreateDxilModule()
                .StripShaderSourcesAndCompileOptions(/* bReplaceWithDummyData */ true);
            }
            CComPtr<AbstractMemoryStream> pDebugBlobStorage;
            IFT(CreateMemoryStream(DxcGetThreadMallocNoRef(), &pDebugBlobStorage));
            raw_stream_ostream outStream(pDebugBlobStorage.p);
            WriteBitcodeToFile(debugModule.get(), outStream, true);
            outStream.flush();
            IFT(pDebugBlobStorage.QueryInterface(&pDebugProgramBlob));
          }

          IFT(CreateContainerForPDB(
            m_pMalloc,
            pOutputBlob, pDebugProgramBlob,
            static_cast<IDxcVersionInfo *>(this), pSourceInfo,
            pReflectionInPdb,
            &pStrippedContainer));
        }

        // Create the final PDB Blob
        CComPtr<IDxcBlob> pPdbBlob;
        IFT(hlsl::pdb::WriteDxilPDB(m_pMalloc, pStrippedContainer, ShaderHashContent.Digest, &pPdbBlob));
        IFT(pResult->SetOutputObject(DXC_OUT_PDB, pPdbBlob));

        // If option Qpdb_in_private given, add the PDB to the DXC_OUT_OBJECT container output as a
        // DFCC_PrivateData part.
        if (opts.PdbInPrivate) {
          CComPtr<IDxcBlobEncoding> pContainerBlob;
          hlsl::DxcCreateBlobWithEncodingFromPinned(pOutputBlob->GetBufferPointer(), pOutputBlob->GetBufferSize(), CP_ACP, &pContainerBlob);

          CComPtr<IDxcContainerBuilder> pContainerBuilder;
          DxcCreateInstance2(this->m_pMalloc, CLSID_DxcContainerBuilder, IID_PPV_ARGS(&pContainerBuilder));
          IFT(pContainerBuilder->Load(pOutputBlob));
          IFT(pContainerBuilder->AddPart(hlsl::DFCC_PrivateData, pPdbBlob));

          CComPtr<IDxcOperationResult> pReserializeResult;
          IFT(pContainerBuilder->SerializeContainer(&pReserializeResult));

          CComPtr<IDxcBlob> pNewOutput;
          IFT(pReserializeResult->GetResult(&pNewOutput));
          pOutputBlob = pNewOutput;
        } // PDB in private
      } // Write PDB

      IFT(primaryOutput.SetObject(pOutputBlob, opts.DefaultTextCodePage));
      IFT(pResult->SetOutput(primaryOutput));
      IFT(pResult->SetStatusAndPrimaryResult(hasErrorOccurred ? E_FAIL : S_OK, primaryOutput.kind));
      IFT(pResult->QueryInterface(riid, ppResult));

      hr = S_OK;
    } catch (std::bad_alloc &) {
      hr = E_OUTOFMEMORY;
    } catch (hlsl::Exception &e) {
      _Analysis_assume_(DXC_FAILED(e.hr));
      CComPtr<IDxcResult> pResult;
      hr = e.hr;
      std::string msg("Internal Compiler error: ");
      msg += e.msg;
      if (SUCCEEDED(DxcResult::Create(e.hr, DXC_OUT_NONE, {
              DxcOutputObject::ErrorOutput(CP_UTF8,
                msg.c_str(), msg.size())
            }, &pResult)) &&
          SUCCEEDED(pResult->QueryInterface(riid, ppResult))) {
        hr = S_OK;
      }
    } catch (...) {
      hr = E_FAIL;
    }
  Cleanup:
    if (bPreprocessStarted) {
      DxcEtw_DXCompilerPreprocess_Stop(hr);
    }
    if (bCompileStarted) {
      DxcEtw_DXCompilerCompile_Stop(hr);
    }
    return hr;
  }