Browse Source

Adds support for pipeline restart (#683)

* Adds support for pause/resume in pipeline.
* Adds 0.0 as the validator version to indicate no validation should occur.
* Moves module parsing from dxcompiler down to HLSL library.
* Adds support for optimizer to process a DXIL part.
* Adds a form with designer support for the interactive optimizer.
Marcelo Lopez Ruiz 8 years ago
parent
commit
64f3b34995

+ 12 - 0
include/dxc/HLSL/DxilGenerationPass.h

@@ -18,6 +18,7 @@ class Function;
 class FunctionPass;
 class Instruction;
 class PassRegistry;
+class StringRef;
 }
 
 namespace hlsl {
@@ -31,6 +32,11 @@ public:
 };
 
 class HLSLExtensionsCodegenHelper;
+
+// Pause/resume support.
+bool ClearPauseResumePasses(llvm::Module &M); // true if modified; false if missing
+void GetPauseResumePasses(llvm::Module &M, llvm::StringRef &pause, llvm::StringRef &resume);
+void SetPauseResumePasses(llvm::Module &M, llvm::StringRef pause, llvm::StringRef resume);
 }
 
 namespace llvm {
@@ -61,6 +67,9 @@ ModulePass *createDxilRemoveDiscardsPass();
 ModulePass *createDxilReduceMSAAToSingleSamplePass();
 ModulePass *createDxilForceEarlyZPass();
 ModulePass *createDxilDebugInstrumentationPass();
+ModulePass *createNoPausePassesPass();
+ModulePass *createPausePassesPass();
+ModulePass *createResumePassesPass();
 
 void initializeDxilCondenseResourcesPass(llvm::PassRegistry&);
 void initializeDxilEliminateOutputDynamicIndexingPass(llvm::PassRegistry&);
@@ -86,6 +95,9 @@ void initializeDxilRemoveDiscardsPass(llvm::PassRegistry&);
 void initializeDxilReduceMSAAToSingleSamplePass(llvm::PassRegistry&);
 void initializeDxilForceEarlyZPass(llvm::PassRegistry&);
 void initializeDxilDebugInstrumentationPass(llvm::PassRegistry&);
+void initializeNoPausePassesPass(llvm::PassRegistry&);
+void initializePausePassesPass(llvm::PassRegistry&);
+void initializeResumePassesPass(llvm::PassRegistry&);
 
 bool AreDxilResourcesDense(llvm::Module *M, hlsl::DxilResourceBase **ppNonDense);
 

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

@@ -150,6 +150,8 @@ public:
   const DxilViewIdState &GetViewIdState() const;
 
   // DXIL metadata manipulation.
+  /// Clear all DXIL data that exists in in-memory form.
+  static void ClearDxilMetadata(llvm::Module &M);
   /// Serialize DXIL in-memory form to metadata form.
   void EmitDxilMetadata();
   /// Update resource metadata.

+ 10 - 1
include/dxc/HLSL/DxilUtil.h

@@ -5,7 +5,7 @@
 // This file is distributed under the University of Illinois Open Source     //
 // License. See LICENSE.TXT for details.                                     //
 //                                                                           //
-// Dxil helper functions.                                                    //
+// DXIL helper functions.                                                    //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -16,6 +16,9 @@ class Type;
 class GlobalVariable;
 class Function;
 class Module;
+class MemoryBuffer;
+class LLVMContext;
+class DiagnosticInfo;
 }
 
 namespace hlsl {
@@ -33,6 +36,12 @@ namespace dxilutil {
   bool IsSharedMemoryGlobal(llvm::GlobalVariable *GV);
   bool RemoveUnusedFunctions(llvm::Module &M, llvm::Function *EntryFunc,
                              llvm::Function *PatchConstantFunc, bool IsLib);
+
+  std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::StringRef BC,
+    llvm::LLVMContext &Ctx, std::string &DiagStr);
+  std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::MemoryBuffer *MB,
+    llvm::LLVMContext &Ctx, std::string &DiagStr);
+  void PrintDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context);
 }
 
 }

+ 1 - 0
lib/HLSL/CMakeLists.txt

@@ -47,6 +47,7 @@ add_llvm_library(LLVMHLSL
   HLOperationLowerExtension.cpp
   HLResource.cpp
   HLSignatureLower.cpp
+  PauseResumePasses.cpp
   ReducibilityAnalysis.cpp
   WaveSensitivityAnalysis.cpp
 

+ 30 - 12
lib/HLSL/DxcOptimizer.cpp

@@ -20,9 +20,11 @@
 #include "dxc/HLSL/HLMatrixLowerPass.h"
 #include "dxc/HLSL/DxilGenerationPass.h"
 #include "dxc/HLSL/ComputeViewIdState.h"
+#include "dxc/HLSL/DxilUtil.h"
 #include "dxc/Support/dxcapi.impl.h"
 
 #include "llvm/Pass.h"
+#include "llvm/PassInfo.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/IRReader/IRReader.h"
 #include "llvm/Bitcode/ReaderWriter.h"
@@ -135,12 +137,15 @@ HRESULT SetupRegistryPassForHLSL() {
     initializeMergeFunctionsPass(Registry);
     initializeMergedLoadStoreMotionPass(Registry);
     initializeMultiDimArrayToOneDimArrayPass(Registry);
+    initializeNoPausePassesPass(Registry);
+    initializePausePassesPass(Registry);
     initializePromotePassPass(Registry);
     initializePruneEHPass(Registry);
     initializeReassociatePass(Registry);
     initializeReducibilityAnalysisPass(Registry);
     initializeRegToMemHlslPass(Registry);
     initializeResourceToHandlePass(Registry);
+    initializeResumePassesPass(Registry);
     initializeRewriteSymbolsPass(Registry);
     initializeSCCPPass(Registry);
     initializeSROAPass(Registry);
@@ -563,22 +568,35 @@ HRESULT STDMETHODCALLTYPE DxcOptimizer::RunOptimizer(
   DxcThreadMalloc TM(m_pMalloc);
 
   // Setup input buffer.
+  //
   // The ir parsing requires the buffer to be null terminated. We deal with
   // both source and bitcode input, so the input buffer may not be null
-  // terminated.
-  // Create a new membuf that copies the buffer and adds a null terminator.
-  StringRef bufStrRef(reinterpret_cast<const char *>(pBlob->GetBufferPointer()),
-                      pBlob->GetBufferSize());
-  std::unique_ptr<MemoryBuffer> memBuf =
-      MemoryBuffer::getMemBufferCopy(bufStrRef);
-
-  // Parse IR
+  // terminated; we create a new membuf that copies and appends for this.
+  //
+  // If we have the beginning of a DXIL program header, skip to the bitcode.
+  //
   LLVMContext Context;
   SMDiagnostic Err;
-  std::unique_ptr<Module> M = parseIR(memBuf->getMemBufferRef(), Err, Context);
-  if (!M) {
-    //Err.print(argv[0], errs());
-    return E_INVALIDARG;
+  std::unique_ptr<MemoryBuffer> memBuf;
+  std::unique_ptr<Module> M;
+  const char * pBlobContent = reinterpret_cast<const char *>(pBlob->GetBufferPointer());
+  unsigned blobSize = pBlob->GetBufferSize();
+  const DxilProgramHeader *pProgramHeader =
+    reinterpret_cast<const DxilProgramHeader *>(pBlobContent);
+  if (IsValidDxilProgramHeader(pProgramHeader, blobSize)) {
+    std::string DiagStr;
+    GetDxilProgramBitcode(pProgramHeader, &pBlobContent, &blobSize);
+    M = hlsl::dxilutil::LoadModuleFromBitcode(
+      llvm::StringRef(pBlobContent, blobSize), Context, DiagStr);
+  }
+  else {
+    StringRef bufStrRef(pBlobContent, blobSize);
+    memBuf = MemoryBuffer::getMemBufferCopy(bufStrRef);
+    M = parseIR(memBuf->getMemBufferRef(), Err, Context);
+  }
+
+  if (M == nullptr) {
+    return DXC_E_IR_VERIFICATION_FAILED;
   }
 
   legacy::PassManager ModulePasses;

+ 4 - 0
lib/HLSL/DxilGenerationPass.cpp

@@ -291,6 +291,9 @@ public:
     HLModule::ClearHLMetadata(M);
     M.ResetHLModule();
 
+    // We now have a DXIL representation - record this.
+    SetPauseResumePasses(*m_pHLModule->GetModule(), "hlsl-dxilemit", "hlsl-dxilload");
+
     // Remove debug code when not debug info.
     if (!m_HasDbgInfo)
       DxilMod.StripDebugRelatedCode();
@@ -1259,6 +1262,7 @@ public:
 
   bool runOnModule(Module &M) override {
     if (M.HasHLModule()) {
+      HLModule::ClearHLMetadata(M);
       M.GetHLModule().EmitHLMetadata();
       return true;
     }

+ 1 - 0
lib/HLSL/DxilLinker.cpp

@@ -724,6 +724,7 @@ void DxilLinkJob::RunPreparePass(Module &M) {
   PM.add(createDxilFinalizeModulePass());
   PM.add(createComputeViewIdStatePass());
   PM.add(createDxilDeadFunctionEliminationPass());
+  PM.add(createNoPausePassesPass());
   PM.add(createDxilEmitMetadataPass());
 
   PM.run(M);

+ 38 - 1
lib/HLSL/DxilModule.cpp

@@ -160,6 +160,10 @@ void DxilModule::SetValidatorVersion(unsigned ValMajor, unsigned ValMinor) {
 }
 
 bool DxilModule::UpgradeValidatorVersion(unsigned ValMajor, unsigned ValMinor) {
+  // Don't upgrade if validation was disabled.
+  if (m_ValMajor == 0 && m_ValMinor == 0) {
+    return false;
+  }
   if (ValMajor > m_ValMajor || (ValMajor == m_ValMajor && ValMinor > m_ValMinor)) {
     // Module requires higher validator version than previously set
     SetValidatorVersion(ValMajor, ValMinor);
@@ -1186,6 +1190,38 @@ vector<GlobalVariable* > &DxilModule::GetLLVMUsed() {
 }
 
 // DXIL metadata serialization/deserialization.
+void DxilModule::ClearDxilMetadata(Module &M) {
+  // Delete: DXIL version, validator version, DXIL shader model,
+  // entry point tuples (shader properties, signatures, resources)
+  // type system, view ID state, LLVM used, entry point tuples,
+  // root signature, function properties.
+  // Other cases for libs pending.
+  // LLVM used is a global variable - handle separately.
+  Module::named_metadata_iterator
+    b = M.named_metadata_begin(),
+    e = M.named_metadata_end();
+  SmallVector<NamedMDNode*, 8> nodes;
+  for (; b != e; ++b) {
+    StringRef name = b->getName();
+    if (name == DxilMDHelper::kDxilVersionMDName ||
+      name == DxilMDHelper::kDxilValidatorVersionMDName ||
+      name == DxilMDHelper::kDxilShaderModelMDName ||
+      name == DxilMDHelper::kDxilEntryPointsMDName ||
+      name == DxilMDHelper::kDxilRootSignatureMDName ||
+      name == DxilMDHelper::kDxilResourcesMDName ||
+      name == DxilMDHelper::kDxilTypeSystemMDName ||
+      name == DxilMDHelper::kDxilViewIdStateMDName ||
+      name == DxilMDHelper::kDxilFunctionPropertiesMDName || // used in libraries
+      name == DxilMDHelper::kDxilEntrySignaturesMDName || // used in libraries
+      name.startswith(DxilMDHelper::kDxilTypeSystemHelperVariablePrefix)) {
+      nodes.push_back(b);
+    }
+  }
+  for (size_t i = 0; i < nodes.size(); ++i) {
+    M.eraseNamedMetadata(nodes[i]);
+  }
+}
+
 void DxilModule::EmitDxilMetadata() {
   m_pMDHelper->EmitDxilVersion(m_DxilMajor, m_DxilMinor);
   m_pMDHelper->EmitValidatorVersion(m_ValMajor, m_ValMinor);
@@ -1199,7 +1235,8 @@ void DxilModule::EmitDxilMetadata() {
     m_pMDHelper->EmitDxilResources(pMDResources);
   m_pMDHelper->EmitDxilTypeSystem(GetTypeSystem(), m_LLVMUsed);
   if (!m_pSM->IsCS() &&
-      (m_ValMajor > 1 || (m_ValMajor == 1 && m_ValMinor >= 1))) {
+      ((m_ValMajor == 0 &&  m_ValMinor == 0) ||
+       (m_ValMajor > 1 || (m_ValMajor == 1 && m_ValMinor >= 1)))) {
     m_pMDHelper->EmitDxilViewIdState(GetViewIdState());
   }
   EmitLLVMUsed();

+ 3 - 1
lib/HLSL/DxilPreparePasses.cpp

@@ -209,7 +209,7 @@ public:
   static char ID; // Pass identification, replacement for typeid
   explicit DxilFinalizeModule() : ModulePass(ID) {}
 
-  const char *getPassName() const override { return "HLSL DXIL Metadata Emit"; }
+  const char *getPassName() const override { return "HLSL DXIL Finalize Module"; }
 
   void patchValidation_1_1(Module &M) {
     for (iplist<Function>::iterator F : M.getFunctionList()) {
@@ -271,6 +271,7 @@ public:
                                  // Update Validator Version
         DM.UpgradeToMinValidatorVersion();
       }
+
       return true;
     }
 
@@ -409,6 +410,7 @@ public:
 
   bool runOnModule(Module &M) override {
     if (M.HasDxilModule()) {
+      DxilModule::ClearDxilMetadata(M);
       M.GetDxilModule().EmitDxilMetadata();
       return true;
     }

+ 34 - 1
lib/HLSL/DxilUtil.cpp

@@ -14,7 +14,13 @@
 #include "dxc/HLSL/DxilTypeSystem.h"
 #include "dxc/HLSL/DxilUtil.h"
 #include "dxc/HLSL/DxilModule.h"
+#include "llvm/Bitcode/ReaderWriter.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
 
 using namespace llvm;
 using namespace hlsl;
@@ -100,6 +106,33 @@ bool RemoveUnusedFunctions(Module &M, Function *EntryFunc,
     F->eraseFromParent();
   return bUpdated;
 }
+
+void PrintDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context) {
+  DiagnosticPrinter *printer = reinterpret_cast<DiagnosticPrinter *>(Context);
+  DI.print(*printer);
+}
+
+std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::MemoryBuffer *MB,
+  llvm::LLVMContext &Ctx,
+  std::string &DiagStr) {
+  raw_string_ostream DiagStream(DiagStr);
+  llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
+  Ctx.setDiagnosticHandler(PrintDiagnosticHandler, &DiagPrinter, true);
+  ErrorOr<std::unique_ptr<llvm::Module>> pModule(
+    llvm::parseBitcodeFile(MB->getMemBufferRef(), Ctx));
+  if (std::error_code ec = pModule.getError()) {
+    return nullptr;
+  }
+  return std::unique_ptr<llvm::Module>(pModule.get().release());
 }
 
-}
+std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::StringRef BC,
+  llvm::LLVMContext &Ctx,
+  std::string &DiagStr) {
+  std::unique_ptr<llvm::MemoryBuffer> pBitcodeBuf(
+    llvm::MemoryBuffer::getMemBuffer(BC, "", false));
+  return LoadModuleFromBitcode(pBitcodeBuf.get(), Ctx, DiagStr);
+}
+
+}
+}

+ 138 - 0
lib/HLSL/PauseResumePasses.cpp

@@ -0,0 +1,138 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// PauseResumePasses.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.                                     //
+//                                                                           //
+// Passes to pause/resume pipeline.                                          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+#include "dxc/HLSL/DxilGenerationPass.h"
+
+#include "llvm/Pass.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+
+using namespace llvm;
+using namespace hlsl;
+
+static const char kPauseResumeMDName[] = "pauseresume";
+static const char kPauseResumeNumFields = 2;
+static const char kPauseResumePassNameToPause = 0;
+static const char kPauseResumePassNameToResume = 1;
+
+namespace hlsl {
+
+bool ClearPauseResumePasses(Module &M) {
+  NamedMDNode *N = M.getNamedMetadata(kPauseResumeMDName);
+  if (N) {
+    M.eraseNamedMetadata(N);
+    return true;
+  }
+  return false;
+}
+
+void GetPauseResumePasses(Module &M, StringRef &pause, StringRef &resume) {
+  NamedMDNode *N = M.getNamedMetadata(kPauseResumeMDName);
+  if (N && N->getNumOperands() > 0) {
+    MDNode *MD = N->getOperand(0);
+    pause = dyn_cast<MDString>(MD->getOperand(kPauseResumePassNameToPause).get())->getString();
+    resume = dyn_cast<MDString>(MD->getOperand(kPauseResumePassNameToResume).get())->getString();
+  }
+}
+
+void SetPauseResumePasses(Module &M, StringRef pause, StringRef resume) {
+  LLVMContext &Ctx = M.getContext();
+  NamedMDNode *N = M.getOrInsertNamedMetadata(kPauseResumeMDName);
+  Metadata *MDs[kPauseResumeNumFields];
+  MDs[kPauseResumePassNameToPause] = MDString::get(Ctx, pause);
+  MDs[kPauseResumePassNameToResume] = MDString::get(Ctx, resume);
+  if (N->getNumOperands() == 0)
+    N->addOperand(MDNode::get(Ctx, MDs));
+  else
+    N->setOperand(kPauseResumePassNameToPause, MDNode::get(Ctx, MDs));
+}
+
+}
+
+namespace {
+
+class NoPausePasses : public ModulePass {
+public:
+  static char ID; // Pass identification, replacement for typeid
+  explicit NoPausePasses() : ModulePass(ID) {}
+
+  const char *getPassName() const override { return "NoPausePasses"; }
+
+  bool runOnModule(Module &M) override {
+    return ClearPauseResumePasses(M);
+  }
+};
+
+class PausePasses : public ModulePass {
+public:
+  static char ID; // Pass identification, replacement for typeid
+  explicit PausePasses() : ModulePass(ID) {}
+
+  const char *getPassName() const override { return "PausePasses"; }
+
+  bool runOnModule(Module &M) override {
+    StringRef pause, resume;
+    hlsl::GetPauseResumePasses(M, pause, resume);
+    if (!pause.empty()) {
+      const PassInfo *PI = PassRegistry::getPassRegistry()->getPassInfo(pause);
+      std::unique_ptr<ModulePass> pass((ModulePass *)PI->createPass());
+      pass->setOSOverride(OSOverride);
+      return pass->runOnModule(M);
+    }
+    return false;
+  }
+};
+
+class ResumePasses : public ModulePass {
+public:
+  static char ID; // Pass identification, replacement for typeid
+  explicit ResumePasses() : ModulePass(ID) {}
+
+  const char *getPassName() const override { return "ResumePasses"; }
+
+  bool runOnModule(Module &M) override {
+    StringRef pause, resume;
+    hlsl::GetPauseResumePasses(M, pause, resume);
+    if (!resume.empty()) {
+      const PassInfo *PI = PassRegistry::getPassRegistry()->getPassInfo(resume);
+      std::unique_ptr<ModulePass> pass((ModulePass *)PI->createPass());
+      pass->setOSOverride(OSOverride);
+      return pass->runOnModule(M);
+    }
+    return false;
+  }
+};
+
+char NoPausePasses::ID = 0;
+char PausePasses::ID = 0;
+char ResumePasses::ID = 0;
+
+}
+
+ModulePass *llvm::createNoPausePassesPass() {
+  return new NoPausePasses();
+}
+ModulePass *llvm::createPausePassesPass() {
+  return new PausePasses();
+}
+ModulePass *llvm::createResumePassesPass() {
+  return new ResumePasses();
+}
+
+INITIALIZE_PASS(NoPausePasses,
+                "hlsl-passes-nopause",
+                "Clears metadata used for pause and resume", false, false)
+INITIALIZE_PASS(PausePasses,
+                "hlsl-passes-pause",
+                "Prepare to pause passes", false, false)
+INITIALIZE_PASS(ResumePasses,
+                "hlsl-passes-resume",
+                "Prepare to resume passes", false, false)

+ 14 - 12
lib/Transforms/IPO/PassManagerBuilder.cpp

@@ -292,15 +292,16 @@ void PassManagerBuilder::populateModulePassManager(
 
     addExtensionsToPM(EP_EnabledOnOptLevel0, MPM);
     // HLSL Change Begins.
-    addHLSLPasses(HLSLHighLevel, true/*NoOpt*/, HLSLExtensionsCodeGen, MPM); // HLSL Change
+    addHLSLPasses(HLSLHighLevel, true/*NoOpt*/, HLSLExtensionsCodeGen, MPM);
     if (!HLSLHighLevel) {
-      MPM.add(createMultiDimArrayToOneDimArrayPass());// HLSL Change
-      MPM.add(createDxilCondenseResourcesPass()); // HLSL Change
-      MPM.add(createDxilLegalizeSampleOffsetPass()); // HLSL Change
-      MPM.add(createDxilFinalizeModulePass());      // HLSL Change
-      MPM.add(createComputeViewIdStatePass());    // HLSL Change
-      MPM.add(createDxilDeadFunctionEliminationPass()); // HLSL Change
-      MPM.add(createDxilEmitMetadataPass());      // HLSL Change
+      MPM.add(createMultiDimArrayToOneDimArrayPass());
+      MPM.add(createDxilCondenseResourcesPass());
+      MPM.add(createDxilLegalizeSampleOffsetPass());
+      MPM.add(createDxilFinalizeModulePass());
+      MPM.add(createComputeViewIdStatePass());
+      MPM.add(createDxilDeadFunctionEliminationPass());
+      MPM.add(createNoPausePassesPass());
+      MPM.add(createDxilEmitMetadataPass());
     }
     // HLSL Change Ends.
     return;
@@ -565,13 +566,14 @@ void PassManagerBuilder::populateModulePassManager(
 
   // HLSL Change Begins.
   if (!HLSLHighLevel) {
-    MPM.add(createMultiDimArrayToOneDimArrayPass());// HLSL Change
+    MPM.add(createMultiDimArrayToOneDimArrayPass());
     MPM.add(createDxilCondenseResourcesPass());
     if (DisableUnrollLoops)
-      MPM.add(createDxilLegalizeSampleOffsetPass()); // HLSL Change
+      MPM.add(createDxilLegalizeSampleOffsetPass());
     MPM.add(createDxilFinalizeModulePass());
-    MPM.add(createComputeViewIdStatePass()); // HLSL Change
-    MPM.add(createDxilDeadFunctionEliminationPass()); // HLSL Change
+    MPM.add(createComputeViewIdStatePass());
+    MPM.add(createDxilDeadFunctionEliminationPass());
+    MPM.add(createNoPausePassesPass());
     MPM.add(createDxilEmitMetadataPass());
   }
   // HLSL Change Ends.

+ 3 - 0
tools/clang/lib/CodeGen/CGHLSLMS.cpp

@@ -41,6 +41,7 @@
 #include "dxc/Support/WinIncludes.h"    // stream support
 #include "dxc/dxcapi.h"                 // stream support
 #include "dxc/HLSL/HLSLExtensionsCodegenHelper.h"
+#include "dxc/HLSL/DxilGenerationPass.h" // support pause/resume passes
 
 using namespace clang;
 using namespace CodeGen;
@@ -4151,6 +4152,8 @@ void CGMSHLSLRuntime::FinishCodeGen() {
     }
   }
 
+  // At this point, we have a high-level DXIL module - record this.
+  SetPauseResumePasses(*m_pHLModule->GetModule(), "hlsl-hlemit", "hlsl-hlensure");
 }
 
 RValue CGMSHLSLRuntime::EmitHLSLBuiltinCallExpr(CodeGenFunction &CGF,

+ 34 - 35
tools/clang/tools/dotnetc/EditorForm.Designer.cs

@@ -91,7 +91,7 @@ namespace MainNs
             this.ASTTabPage = new System.Windows.Forms.TabPage();
             this.ASTDumpBox = new System.Windows.Forms.RichTextBox();
             this.OptimizerTabPage = new System.Windows.Forms.TabPage();
-            this.PrintAllPassesBox = new System.Windows.Forms.CheckBox();
+            this.InteractiveEditorButton = new System.Windows.Forms.Button();
             this.ResetDefaultPassesButton = new System.Windows.Forms.Button();
             this.AnalyzeCheckBox = new System.Windows.Forms.CheckBox();
             this.AddPrintModuleButton = new System.Windows.Forms.Button();
@@ -105,6 +105,7 @@ namespace MainNs
             this.copyToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
             this.copyAllToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.PastePassesMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.DeleteAllPassesMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.label2 = new System.Windows.Forms.Label();
             this.label1 = new System.Windows.Forms.Label();
             this.AvailablePassesBox = new System.Windows.Forms.ListBox();
@@ -114,7 +115,6 @@ namespace MainNs
             this.RenderLogTabPage = new System.Windows.Forms.TabPage();
             this.RenderLogBox = new System.Windows.Forms.TextBox();
             this.RewriterOutputTextBox = new System.Windows.Forms.RichTextBox();
-            this.DeleteAllPassesMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.TheStatusStrip.SuspendLayout();
             this.TheMenuStrip.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
@@ -602,7 +602,7 @@ namespace MainNs
             // 
             // OptimizerTabPage
             // 
-            this.OptimizerTabPage.Controls.Add(this.PrintAllPassesBox);
+            this.OptimizerTabPage.Controls.Add(this.InteractiveEditorButton);
             this.OptimizerTabPage.Controls.Add(this.ResetDefaultPassesButton);
             this.OptimizerTabPage.Controls.Add(this.AnalyzeCheckBox);
             this.OptimizerTabPage.Controls.Add(this.AddPrintModuleButton);
@@ -622,22 +622,21 @@ namespace MainNs
             this.OptimizerTabPage.Text = "Optimizer";
             this.OptimizerTabPage.UseVisualStyleBackColor = true;
             // 
-            // PrintAllPassesBox
+            // InteractiveEditorButton
             // 
-            this.PrintAllPassesBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-            this.PrintAllPassesBox.AutoSize = true;
-            this.PrintAllPassesBox.Location = new System.Drawing.Point(6, 396);
-            this.PrintAllPassesBox.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
-            this.PrintAllPassesBox.Name = "PrintAllPassesBox";
-            this.PrintAllPassesBox.Size = new System.Drawing.Size(96, 17);
-            this.PrintAllPassesBox.TabIndex = 10;
-            this.PrintAllPassesBox.Text = "Print all passes";
-            this.PrintAllPassesBox.UseVisualStyleBackColor = true;
+            this.InteractiveEditorButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.InteractiveEditorButton.Location = new System.Drawing.Point(285, 437);
+            this.InteractiveEditorButton.Name = "InteractiveEditorButton";
+            this.InteractiveEditorButton.Size = new System.Drawing.Size(146, 23);
+            this.InteractiveEditorButton.TabIndex = 11;
+            this.InteractiveEditorButton.Text = "Interactive Editor...";
+            this.InteractiveEditorButton.UseVisualStyleBackColor = true;
+            this.InteractiveEditorButton.Click += new System.EventHandler(this.InteractiveEditorButton_Click);
             // 
             // ResetDefaultPassesButton
             // 
             this.ResetDefaultPassesButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-            this.ResetDefaultPassesButton.Location = new System.Drawing.Point(285, 399);
+            this.ResetDefaultPassesButton.Location = new System.Drawing.Point(285, 382);
             this.ResetDefaultPassesButton.Margin = new System.Windows.Forms.Padding(2);
             this.ResetDefaultPassesButton.Name = "ResetDefaultPassesButton";
             this.ResetDefaultPassesButton.Size = new System.Drawing.Size(146, 25);
@@ -650,7 +649,7 @@ namespace MainNs
             // 
             this.AnalyzeCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
             this.AnalyzeCheckBox.AutoSize = true;
-            this.AnalyzeCheckBox.Location = new System.Drawing.Point(6, 375);
+            this.AnalyzeCheckBox.Location = new System.Drawing.Point(6, 351);
             this.AnalyzeCheckBox.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
             this.AnalyzeCheckBox.Name = "AnalyzeCheckBox";
             this.AnalyzeCheckBox.Size = new System.Drawing.Size(99, 17);
@@ -661,7 +660,7 @@ namespace MainNs
             // AddPrintModuleButton
             // 
             this.AddPrintModuleButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-            this.AddPrintModuleButton.Location = new System.Drawing.Point(6, 418);
+            this.AddPrintModuleButton.Location = new System.Drawing.Point(4, 373);
             this.AddPrintModuleButton.Margin = new System.Windows.Forms.Padding(2);
             this.AddPrintModuleButton.Name = "AddPrintModuleButton";
             this.AddPrintModuleButton.Size = new System.Drawing.Size(146, 25);
@@ -673,7 +672,7 @@ namespace MainNs
             // RunPassesButton
             // 
             this.RunPassesButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-            this.RunPassesButton.Location = new System.Drawing.Point(285, 426);
+            this.RunPassesButton.Location = new System.Drawing.Point(285, 409);
             this.RunPassesButton.Margin = new System.Windows.Forms.Padding(2);
             this.RunPassesButton.Name = "RunPassesButton";
             this.RunPassesButton.Size = new System.Drawing.Size(146, 25);
@@ -685,7 +684,7 @@ namespace MainNs
             // SelectPassDownButton
             // 
             this.SelectPassDownButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-            this.SelectPassDownButton.Location = new System.Drawing.Point(358, 368);
+            this.SelectPassDownButton.Location = new System.Drawing.Point(358, 351);
             this.SelectPassDownButton.Margin = new System.Windows.Forms.Padding(2);
             this.SelectPassDownButton.Name = "SelectPassDownButton";
             this.SelectPassDownButton.Size = new System.Drawing.Size(74, 25);
@@ -697,7 +696,7 @@ namespace MainNs
             // SelectPassUpButton
             // 
             this.SelectPassUpButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-            this.SelectPassUpButton.Location = new System.Drawing.Point(285, 368);
+            this.SelectPassUpButton.Location = new System.Drawing.Point(285, 351);
             this.SelectPassUpButton.Margin = new System.Windows.Forms.Padding(2);
             this.SelectPassUpButton.Name = "SelectPassUpButton";
             this.SelectPassUpButton.Size = new System.Drawing.Size(70, 25);
@@ -715,7 +714,7 @@ namespace MainNs
             this.SelectedPassesBox.Location = new System.Drawing.Point(285, 30);
             this.SelectedPassesBox.Margin = new System.Windows.Forms.Padding(2);
             this.SelectedPassesBox.Name = "SelectedPassesBox";
-            this.SelectedPassesBox.Size = new System.Drawing.Size(206, 225);
+            this.SelectedPassesBox.Size = new System.Drawing.Size(206, 316);
             this.SelectedPassesBox.TabIndex = 3;
             this.SelectedPassesBox.DoubleClick += new System.EventHandler(this.SelectedPassesBox_DoubleClick);
             this.SelectedPassesBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.SelectedPassesBox_KeyUp);
@@ -731,41 +730,48 @@ namespace MainNs
             this.PastePassesMenuItem,
             this.DeleteAllPassesMenuItem});
             this.PassesContextMenu.Name = "PassesContextMenu";
-            this.PassesContextMenu.Size = new System.Drawing.Size(153, 142);
+            this.PassesContextMenu.Size = new System.Drawing.Size(137, 120);
             // 
             // PassPropertiesMenuItem
             // 
             this.PassPropertiesMenuItem.Name = "PassPropertiesMenuItem";
-            this.PassPropertiesMenuItem.Size = new System.Drawing.Size(152, 22);
+            this.PassPropertiesMenuItem.Size = new System.Drawing.Size(136, 22);
             this.PassPropertiesMenuItem.Text = "P&roperties...";
             this.PassPropertiesMenuItem.Click += new System.EventHandler(this.PassPropertiesMenuItem_Click);
             // 
             // toolStripMenuItem5
             // 
             this.toolStripMenuItem5.Name = "toolStripMenuItem5";
-            this.toolStripMenuItem5.Size = new System.Drawing.Size(149, 6);
+            this.toolStripMenuItem5.Size = new System.Drawing.Size(133, 6);
             // 
             // copyToolStripMenuItem1
             // 
             this.copyToolStripMenuItem1.Name = "copyToolStripMenuItem1";
-            this.copyToolStripMenuItem1.Size = new System.Drawing.Size(152, 22);
+            this.copyToolStripMenuItem1.Size = new System.Drawing.Size(136, 22);
             this.copyToolStripMenuItem1.Text = "&Copy";
             this.copyToolStripMenuItem1.Click += new System.EventHandler(this.copyToolStripMenuItem_Click);
             // 
             // copyAllToolStripMenuItem
             // 
             this.copyAllToolStripMenuItem.Name = "copyAllToolStripMenuItem";
-            this.copyAllToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
+            this.copyAllToolStripMenuItem.Size = new System.Drawing.Size(136, 22);
             this.copyAllToolStripMenuItem.Text = "Copy &All";
             this.copyAllToolStripMenuItem.Click += new System.EventHandler(this.copyAllToolStripMenuItem_Click);
             // 
             // PastePassesMenuItem
             // 
             this.PastePassesMenuItem.Name = "PastePassesMenuItem";
-            this.PastePassesMenuItem.Size = new System.Drawing.Size(152, 22);
+            this.PastePassesMenuItem.Size = new System.Drawing.Size(136, 22);
             this.PastePassesMenuItem.Text = "&Paste";
             this.PastePassesMenuItem.Click += new System.EventHandler(this.PastePassesMenuItem_Click);
             // 
+            // DeleteAllPassesMenuItem
+            // 
+            this.DeleteAllPassesMenuItem.Name = "DeleteAllPassesMenuItem";
+            this.DeleteAllPassesMenuItem.Size = new System.Drawing.Size(136, 22);
+            this.DeleteAllPassesMenuItem.Text = "Delete All";
+            this.DeleteAllPassesMenuItem.Click += new System.EventHandler(this.DeleteAllPassesMenuItem_Click);
+            // 
             // label2
             // 
             this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
@@ -797,7 +803,7 @@ namespace MainNs
             this.AvailablePassesBox.Margin = new System.Windows.Forms.Padding(2);
             this.AvailablePassesBox.Name = "AvailablePassesBox";
             this.AvailablePassesBox.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
-            this.AvailablePassesBox.Size = new System.Drawing.Size(274, 225);
+            this.AvailablePassesBox.Size = new System.Drawing.Size(274, 316);
             this.AvailablePassesBox.TabIndex = 0;
             this.AvailablePassesBox.DoubleClick += new System.EventHandler(this.AvailablePassesBox_DoubleClick);
             // 
@@ -870,13 +876,6 @@ namespace MainNs
             this.RewriterOutputTextBox.Text = "";
             this.RewriterOutputTextBox.WordWrap = false;
             // 
-            // DeleteAllPassesMenuItem
-            // 
-            this.DeleteAllPassesMenuItem.Name = "DeleteAllPassesMenuItem";
-            this.DeleteAllPassesMenuItem.Size = new System.Drawing.Size(152, 22);
-            this.DeleteAllPassesMenuItem.Text = "Delete All";
-            this.DeleteAllPassesMenuItem.Click += new System.EventHandler(this.DeleteAllPassesMenuItem_Click);
-            // 
             // EditorForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -984,7 +983,6 @@ namespace MainNs
         private System.Windows.Forms.TabPage ASTTabPage;
         private System.Windows.Forms.RichTextBox ASTDumpBox;
         private System.Windows.Forms.TabPage OptimizerTabPage;
-        private System.Windows.Forms.CheckBox PrintAllPassesBox;
         private System.Windows.Forms.Button ResetDefaultPassesButton;
         private System.Windows.Forms.CheckBox AnalyzeCheckBox;
         private System.Windows.Forms.Button AddPrintModuleButton;
@@ -998,5 +996,6 @@ namespace MainNs
         private System.Windows.Forms.RichTextBox RewriterOutputTextBox;
         private System.Windows.Forms.ToolStripMenuItem PastePassesMenuItem;
         private System.Windows.Forms.ToolStripMenuItem DeleteAllPassesMenuItem;
+        private System.Windows.Forms.Button InteractiveEditorButton;
     }
 }

+ 177 - 249
tools/clang/tools/dotnetc/EditorForm.cs

@@ -135,7 +135,7 @@ namespace MainNs
                     unsafe
                     {
                         IDxcBlob part = reflection.GetPartContent(index);
-                        UInt32* p = (UInt32 *)part.GetBufferPointer();
+                        UInt32* p = (UInt32*)part.GetBufferPointer();
                         return DescribeProgramVersionShort(*p);
                     }
                 }
@@ -957,13 +957,18 @@ namespace MainNs
         }
 
         private IDxcBlobEncoding CreateBlobForText(string text)
+        {
+            return CreateBlobForText(this.Library, text);
+        }
+
+        public static IDxcBlobEncoding CreateBlobForText(IDxcLibrary library, string text)
         {
             if (String.IsNullOrEmpty(text))
             {
                 return null;
             }
             const UInt32 CP_UTF16 = 1200;
-            var source = Library.CreateBlobWithEncodingOnHeapCopy(text, (UInt32)(text.Length * 2), CP_UTF16);
+            var source = library.CreateBlobWithEncodingOnHeapCopy(text, (UInt32)(text.Length * 2), CP_UTF16);
             return source;
         }
 
@@ -1067,10 +1072,15 @@ namespace MainNs
         }
 
         private string GetStringFromBlob(IDxcBlob blob)
+        {
+            return GetStringFromBlob(this.Library, blob);
+        }
+
+        public static string GetStringFromBlob(IDxcLibrary library, IDxcBlob blob)
         {
             unsafe
             {
-                blob = this.Library.GetBlobAstUf16(blob);
+                blob = library.GetBlobAstUf16(blob);
                 return new string(blob.GetBufferPointer(), 0, (int)(blob.GetBufferSize() / 2));
             }
         }
@@ -1267,7 +1277,7 @@ namespace MainNs
             return range;
         }
 
-        private void HandleDebugMetadata(string dbgLine)
+        private static void HandleDebugMetadata(string dbgLine, RichTextBox sourceBox)
         {
             Regex lineRE = new Regex(@"line: (\d+)");
             Match lineMatch = lineRE.Match(dbgLine);
@@ -1277,15 +1287,15 @@ namespace MainNs
             }
 
             int lineVal = Int32.Parse(lineMatch.Groups[1].Value) - 1;
-            int targetStart = this.CodeBox.GetFirstCharIndexFromLine(lineVal);
-            int targetEnd = this.CodeBox.GetFirstCharIndexFromLine(lineVal + 1);
-            var highlights = SelectionHighlightData.FromRtb(CodeBox);
-            highlights.ClearFromRtb(CodeBox);
+            int targetStart = sourceBox.GetFirstCharIndexFromLine(lineVal);
+            int targetEnd = sourceBox.GetFirstCharIndexFromLine(lineVal + 1);
+            var highlights = SelectionHighlightData.FromRtb(sourceBox);
+            highlights.ClearFromRtb(sourceBox);
             highlights.Add(targetStart, targetEnd - targetStart);
-            highlights.ApplyToRtb(CodeBox, Color.Yellow);
+            highlights.ApplyToRtb(sourceBox, Color.Yellow);
         }
 
-        private void HandleDebugTokenOnDisassemblyLine(RichTextBox rtb)
+        private static void HandleDebugTokenOnDisassemblyLine(RichTextBox rtb, RichTextBox sourceBox)
         {
             // Get the line.
             string[] lines = rtb.Lines;
@@ -1307,7 +1317,7 @@ namespace MainNs
                     int dbgIdx = Int32.Parse(dbgLine.Substring(1, dbgLine.IndexOf(' ') - 1));
                     if (dbgIdx == dbgMetadata)
                     {
-                        HandleDebugMetadata(dbgLine);
+                        HandleDebugMetadata(dbgLine, sourceBox);
                         return;
                     }
                     else if (dbgIdx < dbgMetadata)
@@ -1326,20 +1336,17 @@ namespace MainNs
             }
         }
 
-        private void DisassemblyTextBox_SelectionChanged(object sender, EventArgs e)
+        public static void HandleCodeSelectionChanged(RichTextBox rtb, RichTextBox sourceBox)
         {
-            // We use [) ranges for selection
-            RichTextBox rtb = (RichTextBox)sender;
             SelectionHighlightData data = SelectionHighlightData.FromRtb(rtb);
             SelectionExpandResult expand = SelectionExpandResult.Expand(rtb);
             if (expand.IsEmpty)
                 return;
 
             string token = expand.Token;
-
-            if (token == "dbg")
+            if (sourceBox != null && token == "dbg")
             {
-                HandleDebugTokenOnDisassemblyLine(rtb);
+                HandleDebugTokenOnDisassemblyLine(rtb, sourceBox);
             }
 
             if (data.SelectedToken == token)
@@ -1363,6 +1370,11 @@ namespace MainNs
             }
         }
 
+        private void DisassemblyTextBox_SelectionChanged(object sender, EventArgs e)
+        {
+            HandleCodeSelectionChanged((RichTextBox)sender, this.CodeBox);
+        }
+
 
         private string PassToPassString(IDxcOptimizerPass pass)
         {
@@ -1377,7 +1389,7 @@ namespace MainNs
             return value;
         }
 
-        private string PassStringToOption(string value)
+        private static string PassStringToOption(string value)
         {
             int separator = value.IndexOf(OptDescSeparator);
             if (separator >= 0)
@@ -1496,136 +1508,56 @@ namespace MainNs
             this.SelectedPassesBox.Items.AddRange(defaultPasses.ToArray());
         }
 
-        private string[] CreatePassOptions()
+        public static string[] CreatePassOptions(IEnumerable<string> passes, bool analyze, bool printAll)
         {
             List<string> result = new List<string>();
-            if (this.AnalyzeCheckBox.Checked)
+            if (analyze)
                 result.Add("-analyze");
-            bool printAll = this.PrintAllPassesBox.Checked;
-            var items = this.SelectedPassesBox.Items;
             if (printAll)
                 result.Add("-print-module:start");
-            for (int i = 0; i < items.Count; ++i)
+            bool inOptFn = false;
+            foreach (var itemText in passes)
             {
-                string itemText = items[i].ToString();
                 result.Add(PassStringToOption(itemText));
-                if (printAll)
-                    result.Add("-print-module:" + itemText);
-            }
-            return result.ToArray();
-        }
-
-        class TextSection
-        {
-            private string[] lines;
-            private int[] lineHashes;
-            public string Title;
-            public string Text;
-            public bool HasChange;
-            public string[] Lines
-            {
-                get
+                if (itemText == "opt-fn-passes")
                 {
-                    if (lines == null)
-                       lines = Text.Split(new char[] { '\n' });
-                    return lines;
+                    inOptFn = true;
                 }
-            }
-            public int[] LineHashes
-            {
-                get
+                else if (itemText.StartsWith("opt-") && itemText.EndsWith("-passes"))
                 {
-                    if (lineHashes == null)
-                       lineHashes = lines.Select(l => l.GetHashCode()).ToArray();
-                    return lineHashes;
+                    inOptFn = false;
                 }
-            }
-            public override string ToString()
-            {
-                return Title;
-            }
-        }
-
-        private static bool ClosestMatch(string text, ref int start, string[] separators, out string separator)
-        {
-            int closest = -1;
-            separator = null;
-            for (int i = 0; i < separators.Length; ++i)
-            {
-                int idx = text.IndexOf(separators[i], start);
-                if (idx >= 0 && (closest < 0 || idx < closest))
+                if (printAll && !inOptFn)
                 {
-                    closest = idx;
-                    separator = separators[i];
+                    result.Add("-hlsl-passes-pause");
+                    result.Add("-print-module:" + itemText);
                 }
             }
-            start = closest;
-            return closest >= 0;
+            return result.ToArray();
         }
 
-        private static IEnumerable<TextSection> EnumerateSections(string[] separators, string text)
+        private string[] CreatePassOptions(bool analyze, bool printAll)
         {
-            string prior = null;
-            string separator;
-            int idx = 0;
-            while (idx >= 0 && ClosestMatch(text, ref idx, separators, out separator))
-            {
-                int lineEnd = text.IndexOf('\n', idx);
-                if (lineEnd < 0) break;
-                string title = text.Substring(idx + separator.Length, lineEnd - (idx + separator.Length));
-                title = title.Trim();
-                int next = lineEnd;
-                if (!ClosestMatch(text, ref next, separators, out separator))
-                    next = -1;
-                string sectionText = (next < 0) ? text.Substring(lineEnd + 1) : text.Substring(lineEnd + 1, next - (lineEnd + 1));
-                sectionText = sectionText.Trim();
-                if (sectionText == prior)
-                    sectionText = prior;
-                yield return new TextSection { Title = title, Text = sectionText };
-                idx = next;
-                prior = sectionText;
-            }
+            return CreatePassOptions(this.SelectedPassesBox.Items.ToStringEnumerable(), analyze, printAll);
         }
 
         private void RunPassesButton_Click(object sender, EventArgs e)
         {
             // TODO: consider accepting DXIL in the code editor as well
             // Do a high-level only compile.
-            IDxcCompiler compiler = HlslDxcLib.CreateDxcCompiler();
-            string fileName = "hlsl.hlsl";
-            HlslFileVariables fileVars = HlslFileVariables.FromText(this.CodeBox.Text);
-            string[] args = new string[] { "-fcgl" };
-            string resultText = "";
-            IDxcBlob source = null;
+            HighLevelCompileResult compile = RunHighLevelCompile();
+            if (compile.Blob == null)
             {
-                var result = compiler.Compile(this.CreateBlobForCodeText(), fileName, fileVars.Entry, fileVars.Target, args, args.Length, null, 0, library.CreateIncludeHandler());
-                if (result.GetStatus() == 0)
-                {
-                    source = result.GetResult();
-                }
-                else
-                {
-                    resultText = GetStringFromBlob(result.GetErrors());
-                }
+                MessageBox.Show("Failed to compile: " + compile.ResultText);
+                return;
             }
 
-            if (source != null)
+            string[] options = CreatePassOptions(this.AnalyzeCheckBox.Checked, false);
+            OptimizeResult opt = RunOptimize(this.Library, options, compile.Blob);
+            if (!opt.Succeeded)
             {
-                string[] options = CreatePassOptions();
-                IDxcOptimizer opt = HlslDxcLib.CreateDxcOptimizer();
-
-                IDxcBlob module;
-                IDxcBlobEncoding text;
-                try
-                {
-                    opt.RunOptimizer(source, options, options.Length, out module, out text);
-                }
-                catch (Exception optException)
-                {
-                    HandleException(optException, "Failed to run optimizer");
-                    return;
-                }
-                resultText = GetStringFromBlob(text);
+                MessageBox.Show("Failed to optimize: " + opt.ResultText);
+                return;
             }
 
             Form form = new Form();
@@ -1639,135 +1571,11 @@ namespace MainNs
                     new MenuItem("Show Graph", helper.ShowGraphClick)
                 });
             rtb.SelectionChanged += DisassemblyTextBox_SelectionChanged;
-            if (this.PrintAllPassesBox.Checked)
-            {
-                SplitContainer split = new SplitContainer() { Dock = DockStyle.Fill };
-                ListBox sectionBox = new ListBox() { Dock = DockStyle.Fill };
-                RadioButton leftButton = new RadioButton() { Text = "Left Only" };
-                RadioButton diffButton = new RadioButton() { Checked = true, Text = "Both", Left = leftButton.Right };
-                RadioButton rightButton = new RadioButton() { Text = "Right Only", Left = diffButton.Right };
-                Panel optionsPanel = new Panel() { Dock = DockStyle.Top, Height = diffButton.Bottom };
-                optionsPanel.Controls.AddRange(new Control[] { leftButton, diffButton, rightButton } );
-                var sections = EnumerateSections(new string[] { "MODULE-PRINT", "Phase:" }, resultText).ToArray();
-                TextSection last = null;
-                foreach (var s in sections)
-                {
-                    s.HasChange = last != null && !object.ReferenceEquals(last.Text, s.Text);
-                    if (s.HasChange) s.Title = "* " + s.Title;
-                    last = s;
-                }
-                sectionBox.Items.AddRange(sections);
-                Action updateBox = () =>
-                {
-                    rtb.Clear();
-                    int index = sectionBox.SelectedIndex;
-                    if (index == -1) return;
-                    TextSection section = (TextSection)sectionBox.SelectedItem;
-                    TextSection prior = index == 0 ? null : sectionBox.Items[index-1] as TextSection;
-                    if (prior == null || section.Text == prior.Text || rightButton.Checked)
-                        rtb.Text = section.Text;
-                    else if (leftButton.Checked)
-                        rtb.Text = (prior == null) ? "(no prior text)" : prior.Text;
-                    else
-                        RunDiff(prior, section, rtb);
-                };
-                EventHandler H = (ccChanged, __) =>
-                {
-                    if (((RadioButton)ccChanged).Checked) updateBox();
-                };
-                leftButton.CheckedChanged += H;
-                diffButton.CheckedChanged += H;
-                rightButton.CheckedChanged += H;
-                sectionBox.SelectedIndexChanged += (_, __) => { updateBox(); };
-                split.Panel1.Controls.Add(sectionBox);
-                split.Panel2.Controls.Add(rtb);
-                split.Panel2.Controls.Add(optionsPanel);
-                form.Controls.Add(split);
-            }
-            else
-            {
-                rtb.Text = resultText;
-                form.Controls.Add(rtb);
-            }
+            rtb.Text = opt.ResultText;
             form.StartPosition = FormStartPosition.CenterParent;
             form.Show(this);
         }
 
-        private void RunDiff(TextSection oldText, TextSection newText, RichTextBox rtb)
-        {
-            // Longest common subsequence, simple edition. If/when something faster is needed,
-            // should probably take a dependency on a proper diff package. Other than shorter
-            // comparison windows, other things to look for are avoiding creating strings here,
-            // working on the RichTextBox buffer directly for color, and unique'ing lines.
-            string[] oldLines = oldText.Lines;
-            string[] newLines = newText.Lines;
-            // Reduce strings to hashes.
-            int[] oldHashes = oldText.LineHashes;
-            int[] newHashes = newText.LineHashes;
-            // Reduce by trimming prefix and suffix.
-            int diffStart = 0;
-            while (diffStart < oldHashes.Length && diffStart < newHashes.Length && oldHashes[diffStart] == newHashes[diffStart])
-                diffStart++;
-            int newDiffEndExc = newLines.Length, oldDiffEndExc = oldLines.Length;
-            while (newDiffEndExc > diffStart && oldDiffEndExc > diffStart)
-            {
-                if (oldHashes[oldDiffEndExc - 1] == newHashes[newDiffEndExc - 1])
-                {
-                    oldDiffEndExc--;
-                    newDiffEndExc--;
-                }
-                else
-                    break;
-            }
-            int suffixLength = (newLines.Length - newDiffEndExc);
-            // Build LCS table.
-            int oldLen = oldDiffEndExc - diffStart, newLen = newDiffEndExc - diffStart;
-            int[,] lcs = new int[oldLen + 1, newLen + 1]; // already zero-initialized
-            for (int i = 0; i < oldLen; i++)
-                for (int j = 0; j < newLen; j++)
-                    if (oldHashes[i + diffStart] == newHashes[j + diffStart])
-                        lcs[i + 1, j + 1] = lcs[i, j] + 1;
-                    else
-                        lcs[i + 1, j + 1] = Math.Max(lcs[i, j + 1], lcs[i + 1, j]);
-            // Print the diff - common prefix, backtracked diff and common suffix.
-            rtb.AppendLines("  ", newLines, 0, diffStart, Color.White);
-            {
-                int i = oldLen, j = newLen;
-                Stack<string> o = new Stack<string>();
-                for (;;)
-                {
-                    if (i > 0 && j > 0 && oldHashes[diffStart + i - 1] == newHashes[diffStart + j - 1])
-                    {
-                        o.Push("  " + oldLines[diffStart + i - 1]);
-                        i--;
-                        j--;
-                    }
-                    else if (j > 0 && (i == 0 || lcs[i, j - 1] >= lcs[i - 1, j]))
-                    {
-                        o.Push("+ " + newLines[diffStart + j - 1]);
-                        j--;
-                    }
-                    else if (i > 0 && (j == 0 || lcs[i, j - 1] < lcs[i - 1, j]))
-                    {
-                        o.Push("- " + oldLines[diffStart + i - 1]);
-                        i--;
-                    }
-                    else
-                    {
-                        break;
-                    }
-                }
-                while (o.Count != 0)
-                {
-                    string line = o.Pop();
-                    Color c = (line[0] == ' ') ? Color.White :
-                        ((line[0] == '+') ? Color.Yellow : Color.Red);
-                    rtb.AppendLine(line, c);
-                }
-            }
-            rtb.AppendLines("  ", newLines, newDiffEndExc, (newLines.Length - newDiffEndExc), Color.White);
-        }
-
         private void SelectPassUpButton_Click(object sender, EventArgs e)
         {
             ListBox lb = this.SelectedPassesBox;
@@ -1848,7 +1656,7 @@ namespace MainNs
         }
 
         /// <summary>Helper class to handle the context menu of operations log.</summary>
-        class LogContextMenuHelper
+        public class LogContextMenuHelper
         {
             public RichTextBox Rtb { get; set; }
 
@@ -2399,7 +2207,7 @@ namespace MainNs
                 {
                     BuildBitstreamForDXIL(bytes, offset, partNode);
                 }
-                else if (partCC == "ISGN" || partCC == "OSGN" || partCC == "PSGN" || partCC == "ISG1" || partCC == "OSG1" || partCC == "PSG1" )
+                else if (partCC == "ISGN" || partCC == "OSGN" || partCC == "PSGN" || partCC == "ISG1" || partCC == "OSG1" || partCC == "PSG1")
                 {
                     BuildBitstreamForSignature(bytes, offset, partNode, partCC);
                 }
@@ -3046,7 +2854,7 @@ namespace MainNs
         {
             IDxcRewriter rewriter = HlslDxcLib.CreateDxcRewriter();
             IDxcBlobEncoding code = CreateBlobForCodeText();
-            IDxcRewriteResult rewriterResult = rewriter.RewriteUnchangedWithInclude(code, "input.hlsl",null, 0, library.CreateIncludeHandler(), 1);
+            IDxcRewriteResult rewriterResult = rewriter.RewriteUnchangedWithInclude(code, "input.hlsl", null, 0, library.CreateIncludeHandler(), 1);
             IDxcBlobEncoding rewriteBlob = rewriterResult.GetRewrite();
             string rewriteText = GetStringFromBlob(rewriteBlob);
             RewriterOutputTextBox.Text = rewriteText;
@@ -3364,6 +3172,120 @@ namespace MainNs
         {
             this.SelectedPassesBox.Items.Clear();
         }
+
+        public class AssembleResult
+        {
+            public IDxcBlob Blob { get; set; }
+            public string ResultText { get; set; }
+            public bool Succeeded { get; set; }
+        }
+
+        public class HighLevelCompileResult
+        {
+            public IDxcBlob Blob { get; set; }
+            public string ResultText { get; set; }
+        }
+
+        public class OptimizeResult
+        {
+            public IDxcBlob Module { get; set; }
+            public string ResultText { get; set; }
+            public bool Succeeded { get; set; }
+        }
+
+        public static AssembleResult RunAssembly(IDxcLibrary library, IDxcBlob source)
+        {
+            IDxcBlob resultBlob = null;
+            string resultText = "";
+            bool succeeded;
+            var assembler = HlslDxcLib.CreateDxcAssembler();
+            var result = assembler.AssembleToContainer(source);
+            if (result.GetStatus() == 0)
+            {
+                resultBlob = result.GetResult();
+                succeeded = true;
+            }
+            else
+            {
+                resultText = GetStringFromBlob(library, result.GetErrors());
+                succeeded = false;
+            }
+            return new AssembleResult() { Blob = resultBlob, ResultText = resultText, Succeeded = succeeded };
+        }
+
+        public HighLevelCompileResult RunHighLevelCompile()
+        {
+            IDxcCompiler compiler = HlslDxcLib.CreateDxcCompiler();
+            string fileName = "hlsl.hlsl";
+            HlslFileVariables fileVars = HlslFileVariables.FromText(this.CodeBox.Text);
+            string[] args = new string[] { "-fcgl" };
+            string resultText = "";
+            IDxcBlob source = null;
+            {
+                var result = compiler.Compile(this.CreateBlobForCodeText(), fileName, fileVars.Entry, fileVars.Target, args, args.Length, null, 0, library.CreateIncludeHandler());
+                if (result.GetStatus() == 0)
+                {
+                    source = result.GetResult();
+                }
+                else
+                {
+                    resultText = GetStringFromBlob(result.GetErrors());
+                }
+            }
+            return new HighLevelCompileResult() { Blob = source, ResultText = resultText };
+        }
+
+        public static OptimizeResult RunOptimize(IDxcLibrary library, string[] options, string source)
+        {
+            return RunOptimize(library, options, CreateBlobForText(library, source));
+        }
+
+        public static OptimizeResult RunOptimize(IDxcLibrary library, string[] options, IDxcBlob source)
+        {
+            IDxcOptimizer opt = HlslDxcLib.CreateDxcOptimizer();
+            IDxcBlob module = null;
+            string resultText = "";
+            IDxcBlobEncoding text;
+            bool succeeded = true;
+            try
+            {
+                opt.RunOptimizer(source, options, options.Length, out module, out text);
+                resultText = GetStringFromBlob(library, text);
+            }
+            catch (Exception optException)
+            {
+                succeeded = false;
+                resultText = "Failed to run optimizer: " + optException.Message;
+            }
+            return new OptimizeResult() { Module = module, ResultText = resultText, Succeeded = succeeded };
+        }
+
+        private void InteractiveEditorButton_Click(object sender, EventArgs e)
+        {
+            // Do a high-level only compile.
+            HighLevelCompileResult compile = RunHighLevelCompile();
+            if (compile.Blob == null)
+            {
+                MessageBox.Show("Failed to compile: " + compile.ResultText);
+                return;
+            }
+
+            string[] options = CreatePassOptions(false, true);
+            OptimizeResult opt = RunOptimize(this.Library, options, compile.Blob);
+            if (!opt.Succeeded)
+            {
+                MessageBox.Show("Failed to optimize: " + opt.ResultText);
+                return;
+            }
+
+            OptEditorForm form = new OptEditorForm();
+            form.CodeFont = this.CodeBox.Font;
+            form.HighLevelSource = compile.Blob;
+            form.Library = this.Library;
+            form.Sections = TextSection.EnumerateSections(new string[] { "MODULE-PRINT", "Phase:" }, opt.ResultText).ToArray();
+            form.StartPosition = FormStartPosition.CenterParent;
+            form.Show(this);
+        }
     }
 
     public static class RichTextBoxExt
@@ -3382,5 +3304,11 @@ namespace MainNs
                 rtb.AppendLine(prefix + lines[i], c);
             }
         }
+
+        public static IEnumerable<string> ToStringEnumerable(this ListBox.ObjectCollection collection)
+        {
+            foreach (var item in collection)
+                yield return item.ToString();
+        }
     }
 }

+ 216 - 0
tools/clang/tools/dotnetc/OptEditorForm.Designer.cs

@@ -0,0 +1,216 @@
+namespace MainNs
+{
+    partial class OptEditorForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.TopContainer = new System.Windows.Forms.SplitContainer();
+            this.PassesListBox = new System.Windows.Forms.ListBox();
+            this.WorkContainer = new System.Windows.Forms.SplitContainer();
+            this.CodeBox = new System.Windows.Forms.RichTextBox();
+            this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
+            this.LeftButton = new System.Windows.Forms.RadioButton();
+            this.DiffButton = new System.Windows.Forms.RadioButton();
+            this.RightButton = new System.Windows.Forms.RadioButton();
+            this.ApplyChangesButton = new System.Windows.Forms.Button();
+            this.LogBox = new System.Windows.Forms.RichTextBox();
+            ((System.ComponentModel.ISupportInitialize)(this.TopContainer)).BeginInit();
+            this.TopContainer.Panel1.SuspendLayout();
+            this.TopContainer.Panel2.SuspendLayout();
+            this.TopContainer.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.WorkContainer)).BeginInit();
+            this.WorkContainer.Panel1.SuspendLayout();
+            this.WorkContainer.Panel2.SuspendLayout();
+            this.WorkContainer.SuspendLayout();
+            this.flowLayoutPanel1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // TopContainer
+            // 
+            this.TopContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.TopContainer.Location = new System.Drawing.Point(0, 0);
+            this.TopContainer.Name = "TopContainer";
+            // 
+            // TopContainer.Panel1
+            // 
+            this.TopContainer.Panel1.Controls.Add(this.PassesListBox);
+            // 
+            // TopContainer.Panel2
+            // 
+            this.TopContainer.Panel2.Controls.Add(this.WorkContainer);
+            this.TopContainer.Size = new System.Drawing.Size(697, 469);
+            this.TopContainer.SplitterDistance = 232;
+            this.TopContainer.TabIndex = 0;
+            // 
+            // PassesListBox
+            // 
+            this.PassesListBox.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.PassesListBox.FormattingEnabled = true;
+            this.PassesListBox.Location = new System.Drawing.Point(0, 0);
+            this.PassesListBox.Name = "PassesListBox";
+            this.PassesListBox.Size = new System.Drawing.Size(232, 469);
+            this.PassesListBox.TabIndex = 0;
+            this.PassesListBox.SelectedIndexChanged += new System.EventHandler(this.PassesListBox_SelectedIndexChanged);
+            // 
+            // WorkContainer
+            // 
+            this.WorkContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.WorkContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel2;
+            this.WorkContainer.Location = new System.Drawing.Point(0, 0);
+            this.WorkContainer.Name = "WorkContainer";
+            this.WorkContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
+            // 
+            // WorkContainer.Panel1
+            // 
+            this.WorkContainer.Panel1.Controls.Add(this.CodeBox);
+            this.WorkContainer.Panel1.Controls.Add(this.flowLayoutPanel1);
+            // 
+            // WorkContainer.Panel2
+            // 
+            this.WorkContainer.Panel2.Controls.Add(this.LogBox);
+            this.WorkContainer.Size = new System.Drawing.Size(461, 469);
+            this.WorkContainer.SplitterDistance = 400;
+            this.WorkContainer.TabIndex = 0;
+            // 
+            // CodeBox
+            // 
+            this.CodeBox.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.CodeBox.Location = new System.Drawing.Point(0, 29);
+            this.CodeBox.Name = "CodeBox";
+            this.CodeBox.Size = new System.Drawing.Size(461, 371);
+            this.CodeBox.TabIndex = 1;
+            this.CodeBox.Text = "";
+            this.CodeBox.SelectionChanged += new System.EventHandler(this.CodeBox_SelectionChanged);
+            this.CodeBox.TextChanged += new System.EventHandler(this.CodeBox_TextChanged);
+            // 
+            // flowLayoutPanel1
+            // 
+            this.flowLayoutPanel1.AutoSize = true;
+            this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+            this.flowLayoutPanel1.Controls.Add(this.LeftButton);
+            this.flowLayoutPanel1.Controls.Add(this.DiffButton);
+            this.flowLayoutPanel1.Controls.Add(this.RightButton);
+            this.flowLayoutPanel1.Controls.Add(this.ApplyChangesButton);
+            this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
+            this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
+            this.flowLayoutPanel1.Name = "flowLayoutPanel1";
+            this.flowLayoutPanel1.Size = new System.Drawing.Size(461, 29);
+            this.flowLayoutPanel1.TabIndex = 0;
+            // 
+            // LeftButton
+            // 
+            this.LeftButton.AutoSize = true;
+            this.LeftButton.Location = new System.Drawing.Point(3, 3);
+            this.LeftButton.Name = "LeftButton";
+            this.LeftButton.Size = new System.Drawing.Size(43, 17);
+            this.LeftButton.TabIndex = 0;
+            this.LeftButton.Text = "Left";
+            this.LeftButton.UseVisualStyleBackColor = true;
+            this.LeftButton.CheckedChanged += new System.EventHandler(this.LeftButton_CheckedChanged);
+            // 
+            // DiffButton
+            // 
+            this.DiffButton.AutoSize = true;
+            this.DiffButton.Checked = true;
+            this.DiffButton.Location = new System.Drawing.Point(52, 3);
+            this.DiffButton.Name = "DiffButton";
+            this.DiffButton.Size = new System.Drawing.Size(41, 17);
+            this.DiffButton.TabIndex = 1;
+            this.DiffButton.TabStop = true;
+            this.DiffButton.Text = "Diff";
+            this.DiffButton.UseVisualStyleBackColor = true;
+            this.DiffButton.CheckedChanged += new System.EventHandler(this.LeftButton_CheckedChanged);
+            // 
+            // RightButton
+            // 
+            this.RightButton.AutoSize = true;
+            this.RightButton.Location = new System.Drawing.Point(99, 3);
+            this.RightButton.Name = "RightButton";
+            this.RightButton.Size = new System.Drawing.Size(50, 17);
+            this.RightButton.TabIndex = 2;
+            this.RightButton.Text = "Right";
+            this.RightButton.UseVisualStyleBackColor = true;
+            this.RightButton.CheckedChanged += new System.EventHandler(this.LeftButton_CheckedChanged);
+            // 
+            // ApplyChangesButton
+            // 
+            this.ApplyChangesButton.Enabled = false;
+            this.ApplyChangesButton.Location = new System.Drawing.Point(155, 3);
+            this.ApplyChangesButton.Name = "ApplyChangesButton";
+            this.ApplyChangesButton.Size = new System.Drawing.Size(98, 23);
+            this.ApplyChangesButton.TabIndex = 3;
+            this.ApplyChangesButton.Text = "Apply Changes";
+            this.ApplyChangesButton.UseVisualStyleBackColor = true;
+            this.ApplyChangesButton.Click += new System.EventHandler(this.ApplyChangesButton_Click);
+            // 
+            // LogBox
+            // 
+            this.LogBox.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.LogBox.Location = new System.Drawing.Point(0, 0);
+            this.LogBox.Name = "LogBox";
+            this.LogBox.Size = new System.Drawing.Size(461, 65);
+            this.LogBox.TabIndex = 0;
+            this.LogBox.Text = "";
+            // 
+            // OptEditorForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(697, 469);
+            this.Controls.Add(this.TopContainer);
+            this.Name = "OptEditorForm";
+            this.Text = "Optimizer Editor";
+            this.Load += new System.EventHandler(this.OptEditorForm_Load);
+            this.TopContainer.Panel1.ResumeLayout(false);
+            this.TopContainer.Panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.TopContainer)).EndInit();
+            this.TopContainer.ResumeLayout(false);
+            this.WorkContainer.Panel1.ResumeLayout(false);
+            this.WorkContainer.Panel1.PerformLayout();
+            this.WorkContainer.Panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.WorkContainer)).EndInit();
+            this.WorkContainer.ResumeLayout(false);
+            this.flowLayoutPanel1.ResumeLayout(false);
+            this.flowLayoutPanel1.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.SplitContainer TopContainer;
+        private System.Windows.Forms.SplitContainer WorkContainer;
+        private System.Windows.Forms.ListBox PassesListBox;
+        private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
+        private System.Windows.Forms.RadioButton LeftButton;
+        private System.Windows.Forms.RadioButton DiffButton;
+        private System.Windows.Forms.RadioButton RightButton;
+        private System.Windows.Forms.Button ApplyChangesButton;
+        private System.Windows.Forms.RichTextBox CodeBox;
+        private System.Windows.Forms.RichTextBox LogBox;
+    }
+}

+ 291 - 0
tools/clang/tools/dotnetc/OptEditorForm.cs

@@ -0,0 +1,291 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using DotNetDxc;
+
+namespace MainNs
+{
+    public partial class OptEditorForm : Form
+    {
+        private TextSection[] sections;
+
+        public OptEditorForm()
+        {
+            InitializeComponent();
+        }
+
+        public Font CodeFont
+        {
+            get { return this.CodeBox.Font; }
+            set
+            {
+                this.CodeBox.Font = value;
+                this.LogBox.Font = value;
+            }
+        }
+
+        public TextSection[] Sections
+        {
+            get
+            {
+                return sections;
+            }
+            set
+            {
+                this.sections = value;
+                PassesListBox.Items.AddRange(value);
+            }
+        }
+
+        public IDxcLibrary Library { get; set; }
+        public IDxcBlob HighLevelSource { get; set; }
+
+        private void UpdateCodeBox()
+        {
+            CodeBox.Clear();
+            int index = PassesListBox.SelectedIndex;
+            if (index == -1) return;
+            TextSection section = (TextSection)PassesListBox.SelectedItem;
+            TextSection prior = index == 0 ? null : PassesListBox.Items[index - 1] as TextSection;
+            if (prior == null || section.Text == prior.Text || RightButton.Checked)
+                CodeBox.Text = section.Text;
+            else if (LeftButton.Checked)
+                CodeBox.Text = (prior == null) ? "(no prior text)" : prior.Text;
+            else
+                TextSection.RunDiff(prior, section, CodeBox);
+            CodeBox.Modified = false;
+            InvalidateApplyChanges();
+        }
+
+        private void LeftButton_CheckedChanged(object sender, EventArgs e)
+        {
+            UpdateCodeBox();
+        }
+
+        private void ApplyChangesButton_Click(object sender, EventArgs e)
+        {
+            // Turn the text into a container.
+            IDxcBlobEncoding sourceBlob = EditorForm.CreateBlobForText(this.Library, this.CodeBox.Text);
+            EditorForm.AssembleResult assembleResult = EditorForm.RunAssembly(this.Library, sourceBlob);
+            if (assembleResult.Blob == null)
+            {
+                MessageBox.Show("Failed to assemble: " + assembleResult.ResultText);
+                return;
+            }
+
+            // Extract the bitcode portion.
+            const uint DxilKind = 0x4c495844; // 'LIXD' - DXIL
+            uint index;
+            IDxcContainerReflection reflection = HlslDxcLib.CreateDxcContainerReflection();
+            reflection.Load(assembleResult.Blob);
+            reflection.FindFirstPartKind(DxilKind, out index);
+            IDxcBlob bitcodeBlob = reflection.GetPartContent(index);
+
+            List<string> passes = new List<string>();
+            passes.Add("hlsl-passes-resume");
+            for (int i = PassesListBox.SelectedIndex; i < PassesListBox.Items.Count; ++i)
+            {
+                passes.Add(((TextSection)PassesListBox.Items[i]).Title);
+            }
+            string[] options = EditorForm.CreatePassOptions(passes, false, true);
+            EditorForm.OptimizeResult opt = EditorForm.RunOptimize(this.Library, options, bitcodeBlob);
+            if (!opt.Succeeded)
+            {
+                MessageBox.Show("Failed to optimize: " + opt.ResultText);
+                return;
+            }
+
+            OptEditorForm form = new OptEditorForm();
+            form.CodeFont = this.CodeBox.Font;
+            form.Library = this.Library;
+            form.HighLevelSource = this.HighLevelSource;
+            form.Sections = TextSection.EnumerateSections(new string[] { "MODULE-PRINT", "Phase:" }, opt.ResultText).ToArray();
+            form.StartPosition = FormStartPosition.CenterParent;
+            form.Show(this);
+        }
+
+        private void OptEditorForm_Load(object sender, EventArgs e)
+        {
+            RichTextBox rtb = CodeBox;
+            var helper = new EditorForm.LogContextMenuHelper(rtb);
+            rtb.ContextMenu = new ContextMenu(
+                new MenuItem[]
+                {
+                    new MenuItem("Show Graph", helper.ShowGraphClick)
+                });
+        }
+
+        private void CodeBox_TextChanged(object sender, EventArgs e)
+        {
+            InvalidateApplyChanges();
+        }
+
+        private void InvalidateApplyChanges()
+        {
+            this.ApplyChangesButton.Enabled = this.CodeBox.Modified && RightButton.Checked;
+        }
+
+        private void PassesListBox_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            UpdateCodeBox();
+        }
+
+        private void CodeBox_SelectionChanged(object sender, EventArgs e)
+        {
+            EditorForm.HandleCodeSelectionChanged(this.CodeBox, null);
+        }
+    }
+
+    public class TextSection
+    {
+        private string[] lines;
+        private int[] lineHashes;
+        public string Title;
+        public string Text;
+        public bool HasChange;
+
+        public string[] Lines
+        {
+            get
+            {
+                if (lines == null)
+                    lines = Text.Split(new char[] { '\n' });
+                return lines;
+            }
+        }
+        public int[] LineHashes
+        {
+            get
+            {
+                if (lineHashes == null)
+                    lineHashes = lines.Select(l => l.GetHashCode()).ToArray();
+                return lineHashes;
+            }
+        }
+        public override string ToString()
+        {
+            return HasChange ? "* " + Title : Title;
+        }
+
+        private static bool ClosestMatch(string text, ref int start, string[] separators, out string separator)
+        {
+            int closest = -1;
+            separator = null;
+            for (int i = 0; i < separators.Length; ++i)
+            {
+                int idx = text.IndexOf(separators[i], start);
+                if (idx >= 0 && (closest < 0 || idx < closest))
+                {
+                    closest = idx;
+                    separator = separators[i];
+                }
+            }
+            start = closest;
+            return closest >= 0;
+        }
+
+        public static IEnumerable<TextSection> EnumerateSections(string[] separators, string text)
+        {
+            string prior = null;
+            string separator;
+            int idx = 0;
+            while (idx >= 0 && ClosestMatch(text, ref idx, separators, out separator))
+            {
+                int lineEnd = text.IndexOf('\n', idx);
+                if (lineEnd < 0) break;
+                string title = text.Substring(idx + separator.Length, lineEnd - (idx + separator.Length));
+                title = title.Trim();
+                int next = lineEnd;
+                if (!ClosestMatch(text, ref next, separators, out separator))
+                    next = -1;
+                string sectionText = (next < 0) ? text.Substring(lineEnd + 1) : text.Substring(lineEnd + 1, next - (lineEnd + 1));
+                sectionText = sectionText.Trim();
+                bool hasChange = sectionText != prior;
+                yield return new TextSection { HasChange = hasChange, Title = title, Text = hasChange ? sectionText : prior };
+                idx = next;
+                prior = sectionText;
+            }
+        }
+
+        public static void RunDiff(TextSection oldText, TextSection newText, RichTextBox rtb)
+        {
+            // Longest common subsequence, simple edition. If/when something faster is needed,
+            // should probably take a dependency on a proper diff package. Other than shorter
+            // comparison windows, other things to look for are avoiding creating strings here,
+            // working on the RichTextBox buffer directly for color, and unique'ing lines.
+            string[] oldLines = oldText.Lines;
+            string[] newLines = newText.Lines;
+            // Reduce strings to hashes.
+            int[] oldHashes = oldText.LineHashes;
+            int[] newHashes = newText.LineHashes;
+            // Reduce by trimming prefix and suffix.
+            int diffStart = 0;
+            while (diffStart < oldHashes.Length && diffStart < newHashes.Length && oldHashes[diffStart] == newHashes[diffStart])
+                diffStart++;
+            int newDiffEndExc = newLines.Length, oldDiffEndExc = oldLines.Length;
+            while (newDiffEndExc > diffStart && oldDiffEndExc > diffStart)
+            {
+                if (oldHashes[oldDiffEndExc - 1] == newHashes[newDiffEndExc - 1])
+                {
+                    oldDiffEndExc--;
+                    newDiffEndExc--;
+                }
+                else
+                    break;
+            }
+            int suffixLength = (newLines.Length - newDiffEndExc);
+            // Build LCS table.
+            int oldLen = oldDiffEndExc - diffStart, newLen = newDiffEndExc - diffStart;
+            int[,] lcs = new int[oldLen + 1, newLen + 1]; // already zero-initialized
+            for (int i = 0; i < oldLen; i++)
+                for (int j = 0; j < newLen; j++)
+                    if (oldHashes[i + diffStart] == newHashes[j + diffStart])
+                        lcs[i + 1, j + 1] = lcs[i, j] + 1;
+                    else
+                        lcs[i + 1, j + 1] = Math.Max(lcs[i, j + 1], lcs[i + 1, j]);
+            // Print the diff - common prefix, backtracked diff and common suffix.
+            rtb.AppendLines("  ", newLines, 0, diffStart, Color.White);
+            {
+                int i = oldLen, j = newLen;
+                Stack<string> o = new Stack<string>();
+                for (;;)
+                {
+                    if (i > 0 && j > 0 && oldHashes[diffStart + i - 1] == newHashes[diffStart + j - 1])
+                    {
+                        o.Push("  " + oldLines[diffStart + i - 1]);
+                        i--;
+                        j--;
+                    }
+                    else if (j > 0 && (i == 0 || lcs[i, j - 1] >= lcs[i - 1, j]))
+                    {
+                        o.Push("+ " + newLines[diffStart + j - 1]);
+                        j--;
+                    }
+                    else if (i > 0 && (j == 0 || lcs[i, j - 1] < lcs[i - 1, j]))
+                    {
+                        o.Push("- " + oldLines[diffStart + i - 1]);
+                        i--;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+                while (o.Count != 0)
+                {
+                    string line = o.Pop();
+                    Color c = (line[0] == ' ') ? Color.White :
+                        ((line[0] == '+') ? Color.Yellow : Color.Red);
+                    rtb.AppendLine(line, c);
+                }
+            }
+            rtb.AppendLines("  ", newLines, newDiffEndExc, (newLines.Length - newDiffEndExc), Color.White);
+        }
+    }
+}

+ 120 - 0
tools/clang/tools/dotnetc/OptEditorForm.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 10 - 1
tools/clang/tools/dotnetc/dndxc.csproj.txt

@@ -80,7 +80,13 @@
     <Compile Include="${DOS_STYLE_SOURCE_DIR}\GoToDialog.Designer.cs">
       <DependentUpon>GoToDialog.cs</DependentUpon>
     </Compile>
-	<Compile Include="${DOS_STYLE_SOURCE_DIR}\HlslHost.cs" />
+    <Compile Include="${DOS_STYLE_SOURCE_DIR}\HlslHost.cs" />
+    <Compile Include="${DOS_STYLE_SOURCE_DIR}\OptEditorForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="${DOS_STYLE_SOURCE_DIR}\OptEditorForm.Designer.cs">
+      <DependentUpon>OptEditorForm.cs</DependentUpon>
+    </Compile>
     <Compile Include="${DOS_STYLE_SOURCE_DIR}\Tom.cs" />
     <Compile Include="${DOS_STYLE_SOURCE_DIR}\Program.cs" />
   </ItemGroup>
@@ -94,6 +100,9 @@
     <EmbeddedResource Include="${DOS_STYLE_SOURCE_DIR}\GoToDialog.resx">
       <DependentUpon>GoToDialog.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="${DOS_STYLE_SOURCE_DIR}\OptEditorForm.resx">
+      <DependentUpon>OptEditorForm.cs</DependentUpon>
+    </EmbeddedResource>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 2 - 1
tools/clang/tools/dxcompiler/dxcdia.cpp

@@ -24,6 +24,7 @@
 #include "dxc/HLSL/DxilContainer.h"
 #include "dxc/HLSL/DxilShaderModel.h"
 #include "dxc/HLSL/DxilModule.h"
+#include "dxc/HLSL/DxilUtil.h"
 #include "dxc/Support/Global.h"
 #include "dia2.h"
 
@@ -2172,7 +2173,7 @@ public:
       }
 
       std::string DiagStr;
-      std::unique_ptr<llvm::Module> pModule = dxcutil::LoadModuleFromBitcode(
+      std::unique_ptr<llvm::Module> pModule = dxilutil::LoadModuleFromBitcode(
           pBitcodeBuffer, *m_context.get(), DiagStr);
       if (!pModule.get())
         return E_FAIL;

+ 2 - 1
tools/clang/tools/dxcompiler/dxcdisassembler.cpp

@@ -26,6 +26,7 @@
 #include "llvm/Support/Format.h"
 #include "dxc/HLSL/DxilPipelineStateValidation.h"
 #include "dxc/HLSL/DxilContainer.h"
+#include "dxc/HLSL/DxilUtil.h"
 #include "dxcutil.h"
 
 using namespace llvm;
@@ -1322,7 +1323,7 @@ HRESULT Disassemble(IDxcBlob *pProgram, raw_string_ostream &Stream) {
 
   std::string DiagStr;
   llvm::LLVMContext llvmContext;
-  std::unique_ptr<llvm::Module> pModule(dxcutil::LoadModuleFromBitcode(
+  std::unique_ptr<llvm::Module> pModule(dxilutil::LoadModuleFromBitcode(
     llvm::StringRef(pIL, pILLength), llvmContext, DiagStr));
   if (pModule.get() == nullptr) {
     return DXC_E_IR_VERIFICATION_FAILED;

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

@@ -411,7 +411,7 @@ public:
       bool needsValidation = produceFullContainer && !opts.DisableValidation &&
                              !opts.IsLibraryProfile();
 
-      if (needsValidation) {
+      if (needsValidation || (opts.CodeGenHighLevel && !opts.DisableValidation)) {
         UINT32 majorVer, minorVer;
         dxcutil::GetValidatorVersion(&majorVer, &minorVer);
         compiler.getCodeGenOpts().HLSLValidatorMajorVer = majorVer;

+ 0 - 27
tools/clang/tools/dxcompiler/dxcutil.cpp

@@ -95,11 +95,6 @@ private:
 
 } // namespace
 
-static void PrintDiagnosticHandler(const llvm::DiagnosticInfo &DI, void *Context) {
-  DiagnosticPrinter *printer = reinterpret_cast<DiagnosticPrinter *>(Context);
-  DI.print(*printer);
-}
-
 namespace dxcutil {
 void GetValidatorVersion(unsigned *pMajor, unsigned *pMinor) {
   if (pMajor == nullptr || pMinor == nullptr)
@@ -222,26 +217,4 @@ bool IsAbsoluteOrCurDirRelative(const Twine &T) {
   return false;
 }
 
-std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::MemoryBuffer *MB,
-                                                    llvm::LLVMContext &Ctx,
-                                                    std::string &DiagStr) {
-  raw_string_ostream DiagStream(DiagStr);
-  llvm::DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
-  Ctx.setDiagnosticHandler(PrintDiagnosticHandler, &DiagPrinter, true);
-  ErrorOr<std::unique_ptr<llvm::Module>> pModule(
-      llvm::parseBitcodeFile(MB->getMemBufferRef(), Ctx));
-  if (std::error_code ec = pModule.getError()) {
-    return nullptr;
-  }
-  return std::unique_ptr<llvm::Module>(pModule.get().release());
-}
-
-std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::StringRef BC,
-                                                    llvm::LLVMContext &Ctx,
-                                                    std::string &DiagStr) {
-  std::unique_ptr<llvm::MemoryBuffer> pBitcodeBuf(
-      llvm::MemoryBuffer::getMemBuffer(BC, "", false));
-  return LoadModuleFromBitcode(pBitcodeBuf.get(), Ctx, DiagStr);
-}
-
 } // namespace dxcutil

+ 0 - 7
tools/clang/tools/dxcompiler/dxcutil.h

@@ -55,11 +55,4 @@ void CreateOperationResultFromOutputs(
 
 bool IsAbsoluteOrCurDirRelative(const llvm::Twine &T);
 
-std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::StringRef BC,
-                                                    llvm::LLVMContext &Ctx,
-                                                    std::string &DiagStr);
-std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::MemoryBuffer *MB,
-                                                    llvm::LLVMContext &Ctx,
-                                                    std::string &DiagStr);
-
 } // namespace dxcutil

+ 1 - 0
tools/clang/unittests/HLSL/CMakeLists.txt

@@ -34,6 +34,7 @@ add_clang_library(clang-hlsl-tests SHARED
   LinkerTest.cpp
   MSFileSysTest.cpp
   Objects.cpp
+  OptimizerTest.cpp
   OptionsTest.cpp
   RewriterTest.cpp
   ShaderOpTest.cpp

+ 22 - 23
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -48,6 +48,14 @@
 using namespace std;
 using namespace hlsl_test;
 
+void AssembleToContainer(dxc::DxcDllSupport &dllSupport, IDxcBlob *pModule, IDxcBlob **pContainer) {
+  CComPtr<IDxcAssembler> pAssembler;
+  CComPtr<IDxcOperationResult> pResult;
+  VERIFY_SUCCEEDED(dllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
+  VERIFY_SUCCEEDED(pAssembler->AssembleToContainer(pModule, &pResult));
+  CheckOperationSucceeded(pResult, pContainer);
+}
+
 std::wstring BlobToUtf16(_In_ IDxcBlob *pBlob) {
   CComPtr<IDxcBlobEncoding> pBlobEncoding;
   const UINT CP_UTF16 = 1200;
@@ -70,6 +78,16 @@ std::wstring BlobToUtf16(_In_ IDxcBlob *pBlob) {
   }
 }
 
+void GetDxilPart(dxc::DxcDllSupport &dllSupport, IDxcBlob *pProgram, IDxcBlob **pDxilPart) {
+  const UINT32 DxilPartKind = 'LIXD'; // DXIL
+  UINT32 DxilIndex;
+  CComPtr<IDxcContainerReflection> pContainerReflection;
+  VERIFY_SUCCEEDED(dllSupport.CreateInstance(CLSID_DxcContainerReflection, &pContainerReflection));
+  VERIFY_SUCCEEDED(pContainerReflection->Load(pProgram));
+  VERIFY_SUCCEEDED(pContainerReflection->FindFirstPartKind(DxilPartKind, &DxilIndex));
+  VERIFY_SUCCEEDED(pContainerReflection->GetPartContent(DxilIndex, pDxilPart));
+}
+
 void Utf8ToBlob(dxc::DxcDllSupport &dllSupport, const char *pVal, _Outptr_ IDxcBlobEncoding **ppBlob) {
   CComPtr<IDxcLibrary> library;
   IFT(dllSupport.CreateInstance(CLSID_DxcLibrary, &library));
@@ -2307,29 +2325,7 @@ TEST_F(CompilerTest, CompileWhenODumpThenOptimizerMatch) {
     // Get wchar_t version and prepend hlsl-hlensure, to do a split high-level/opt compilation pass.
     CA2W passesW(passes.c_str(), CP_UTF8);
     std::vector<LPCWSTR> Options;
-    Options.push_back(L"-hlsl-hlensure");
-    wchar_t *pPassesBuffer = passesW.m_psz;
-    while (*pPassesBuffer) {
-      // Skip comment lines.
-      if (*pPassesBuffer == L'#') {
-        while (*pPassesBuffer && *pPassesBuffer != '\n' && *pPassesBuffer != '\r') {
-          ++pPassesBuffer;
-        }
-        while (*pPassesBuffer == '\n' || *pPassesBuffer == '\r') {
-          ++pPassesBuffer;
-        }
-        continue;
-      }
-      // Every other line is an option. Find the end of the line/buffer and terminate it.
-      Options.push_back(pPassesBuffer);
-      while (*pPassesBuffer && *pPassesBuffer != '\n' && *pPassesBuffer != '\r') {
-        ++pPassesBuffer;
-      }
-      while (*pPassesBuffer == '\n' || *pPassesBuffer == '\r') {
-        *pPassesBuffer = L'\0';
-        ++pPassesBuffer;
-      }
-    }
+    SplitPassList(passesW.m_psz, Options);
 
     // Now compile directly.
     pResult.Release();
@@ -2348,6 +2344,9 @@ TEST_F(CompilerTest, CompileWhenODumpThenOptimizerMatch) {
                                               Options.size(), &pOptimizedModule,
                                               nullptr));
 
+    string text = DisassembleProgram(m_dllSupport, pOptimizedModule);
+    LogCommentFmt(L"Final program:\r\n%S", text.c_str());
+
     // At the very least, the module should be valid.
     pResult.Release();
     VERIFY_SUCCEEDED(pAssembler->AssembleToContainer(pOptimizedModule, &pResult));

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

@@ -98,6 +98,7 @@ inline std::string BlobToUtf8(_In_ IDxcBlob *pBlob) {
   return std::string((char *)pBlob->GetBufferPointer(), pBlob->GetBufferSize());
 }
 
+void AssembleToContainer(dxc::DxcDllSupport &dllSupport, IDxcBlob *pModule, IDxcBlob **pContainer);
 std::wstring BlobToUtf16(_In_ IDxcBlob *pBlob);
 void CheckOperationSucceeded(IDxcOperationResult *pResult, IDxcBlob **ppBlob);
 bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
@@ -105,7 +106,9 @@ bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
                               bool maySucceedAnyway, bool bRegex);
 bool CheckMsgs(const LPCSTR pText, size_t TextCount, const LPCSTR *pErrorMsgs,
                size_t errorMsgCount, bool bRegex);
+void GetDxilPart(dxc::DxcDllSupport &dllSupport, IDxcBlob *pProgram, IDxcBlob **pDxilPart);
 std::string DisassembleProgram(dxc::DxcDllSupport &dllSupport, IDxcBlob *pProgram);
+void SplitPassList(LPWSTR pPassesBuffer, std::vector<LPCWSTR> &passes);
 void Utf8ToBlob(dxc::DxcDllSupport &dllSupport, const std::string &val, _Outptr_ IDxcBlob **ppBlob);
 void Utf8ToBlob(dxc::DxcDllSupport &dllSupport, const std::string &val, _Outptr_ IDxcBlobEncoding **ppBlob);
 void Utf8ToBlob(dxc::DxcDllSupport &dllSupport, const char *pVal, _Outptr_ IDxcBlobEncoding **ppBlob);

+ 263 - 0
tools/clang/unittests/HLSL/OptimizerTest.cpp

@@ -0,0 +1,263 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// OptimizerTest.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.                                     //
+//                                                                           //
+// Provides tests for the optimizer API.                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <map>
+#include <cassert>
+#include <sstream>
+#include <algorithm>
+#include "dxc/HLSL/DxilContainer.h"
+#include "dxc/Support/WinIncludes.h"
+#include "dxc/dxcapi.h"
+
+#include "HLSLTestData.h"
+#include "WexTestClass.h"
+#include "HlslTestUtils.h"
+#include "DxcTestUtils.h"
+
+#include "llvm/Support/raw_os_ostream.h"
+#include "dxc/Support/Global.h"
+#include "dxc/Support/dxcapi.use.h"
+#include "dxc/Support/microcom.h"
+#include "dxc/Support/HLSLOptions.h"
+#include "dxc/Support/Unicode.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MSFileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace std;
+using namespace hlsl_test;
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper functions to deal with passes.
+
+void SplitPassList(LPWSTR pPassesBuffer, std::vector<LPCWSTR> &passes) {
+  while (*pPassesBuffer) {
+    // Skip comment lines.
+    if (*pPassesBuffer == L'#') {
+      while (*pPassesBuffer && *pPassesBuffer != '\n' && *pPassesBuffer != '\r') {
+        ++pPassesBuffer;
+      }
+      while (*pPassesBuffer == '\n' || *pPassesBuffer == '\r') {
+        ++pPassesBuffer;
+      }
+      continue;
+    }
+    // Every other line is an option. Find the end of the line/buffer and terminate it.
+    passes.push_back(pPassesBuffer);
+    while (*pPassesBuffer && *pPassesBuffer != '\n' && *pPassesBuffer != '\r') {
+      ++pPassesBuffer;
+    }
+    while (*pPassesBuffer == '\n' || *pPassesBuffer == '\r') {
+      *pPassesBuffer = L'\0';
+      ++pPassesBuffer;
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Optimizer test cases.
+
+class OptimizerTest {
+public:
+  BEGIN_TEST_CLASS(OptimizerTest)
+    TEST_CLASS_PROPERTY(L"Parallel", L"true")
+    TEST_METHOD_PROPERTY(L"Priority", L"0")
+  END_TEST_CLASS()
+
+  TEST_CLASS_SETUP(InitSupport);
+
+  // Split just so we can run them with some degree of concurrency.
+  TEST_METHOD(OptimizerWhenSlice0ThenOK)
+  TEST_METHOD(OptimizerWhenSlice1ThenOK)
+  TEST_METHOD(OptimizerWhenSlice2ThenOK)
+  TEST_METHOD(OptimizerWhenSlice3ThenOK)
+
+  void OptimizerWhenSliceNThenOK(int optLevel);
+  void OptimizerWhenSliceNThenOK(int optLevel, LPCWSTR pText, LPCWSTR pTarget);
+
+  dxc::DxcDllSupport m_dllSupport;
+  VersionSupportInfo m_ver;
+
+  HRESULT CreateCompiler(IDxcCompiler **ppResult) {
+    return m_dllSupport.CreateInstance(CLSID_DxcCompiler, ppResult);
+  }
+
+  HRESULT CreateContainerBuilder(IDxcContainerBuilder **ppResult) {
+    return m_dllSupport.CreateInstance(CLSID_DxcContainerBuilder, ppResult);
+  }
+
+  void VerifyOperationSucceeded(IDxcOperationResult *pResult) {
+    HRESULT result;
+    VERIFY_SUCCEEDED(pResult->GetStatus(&result));
+    if (FAILED(result)) {
+      CComPtr<IDxcBlobEncoding> pErrors;
+      VERIFY_SUCCEEDED(pResult->GetErrorBuffer(&pErrors));
+      CA2W errorsWide(BlobToUtf8(pErrors).c_str(), CP_UTF8);
+      WEX::Logging::Log::Comment(errorsWide);
+    }
+    VERIFY_SUCCEEDED(result);
+  }
+};
+
+bool OptimizerTest::InitSupport() {
+  if (!m_dllSupport.IsEnabled()) {
+    VERIFY_SUCCEEDED(m_dllSupport.Initialize());
+    m_ver.Initialize(m_dllSupport);
+  }
+  return true;
+}
+
+TEST_F(OptimizerTest, OptimizerWhenSlice0ThenOK) { OptimizerWhenSliceNThenOK(0); }
+TEST_F(OptimizerTest, OptimizerWhenSlice1ThenOK) { OptimizerWhenSliceNThenOK(1); }
+TEST_F(OptimizerTest, OptimizerWhenSlice2ThenOK) { OptimizerWhenSliceNThenOK(2); }
+TEST_F(OptimizerTest, OptimizerWhenSlice3ThenOK) { OptimizerWhenSliceNThenOK(3); }
+
+void OptimizerTest::OptimizerWhenSliceNThenOK(int optLevel) {
+  LPCWSTR SampleProgram =
+    L"Texture2D g_Tex;\r\n"
+    L"SamplerState g_Sampler;\r\n"
+    L"void unused() { }\r\n"
+    L"float4 main(float4 pos : SV_Position, float4 user : USER, bool b : B) : SV_Target {\r\n"
+    L"  unused();\r\n"
+    L"  if (b) user = g_Tex.Sample(g_Sampler, pos.xy);\r\n"
+    L"  return user * pos;\r\n"
+    L"}";
+  OptimizerWhenSliceNThenOK(optLevel, SampleProgram, L"ps_6_0");
+}
+static bool IsPassMarkerFunction(LPCWSTR pName) {
+  return 0 == wcsicmp(pName, L"-opt-fn-passes");
+}
+static bool IsPassMarkerNotFunction(LPCWSTR pName) {
+  return 0 == wcsnicmp(pName, L"-opt-", 5) && !IsPassMarkerFunction(pName);
+}
+static void ExtractFunctionPasses(std::vector<LPCWSTR> &passes, std::vector<LPCWSTR> &functionPasses) {
+  // Assumption: contiguous range
+  typedef std::vector<LPCWSTR>::iterator it;
+  it firstPass = std::find_if(passes.begin(), passes.end(), IsPassMarkerFunction);
+  if (firstPass == passes.end()) return;
+  it lastPass = std::find_if(firstPass, passes.end(), IsPassMarkerNotFunction);
+  it cursor = firstPass;
+  while (cursor != lastPass) {
+    functionPasses.push_back(*cursor);
+    ++cursor;
+  }
+  passes.erase(firstPass, lastPass);
+}
+
+void OptimizerTest::OptimizerWhenSliceNThenOK(int optLevel, LPCWSTR pText, LPCWSTR pTarget) {
+  CComPtr<IDxcCompiler> pCompiler;
+  CComPtr<IDxcOptimizer> pOptimizer;
+  CComPtr<IDxcOperationResult> pResult;
+  CComPtr<IDxcBlobEncoding> pSource;
+  CComPtr<IDxcBlob> pProgram;
+  CComPtr<IDxcBlob> pProgramModule;
+  CComPtr<IDxcBlob> pProgramDisassemble;
+  CComPtr<IDxcBlob> pHighLevelBlob;
+  CComPtr<IDxcBlob> pOptDump;
+  std::string passes;
+  std::vector<LPCWSTR> passList;
+  std::vector<LPCWSTR> prefixPassList;
+
+  WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
+
+  VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
+  VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
+
+  // Create the target program with a single invocation.
+  wchar_t OptArg[4];
+  wsprintf(OptArg, L"/O%i", optLevel);
+  Utf16ToBlob(m_dllSupport, pText, &pSource);
+  LPCWSTR args[] = { L"/Vd", OptArg };
+  VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"main",
+    pTarget, args, _countof(args), nullptr, 0, nullptr, &pResult));
+  VerifyOperationSucceeded(pResult);
+  VERIFY_SUCCEEDED(pResult->GetResult(&pProgram));
+  pResult.Release();
+  std::string originalAssembly = DisassembleProgram(m_dllSupport, pProgram);
+
+  // Get a list of passes for this configuration.
+  LPCWSTR optDumpArgs[] = { L"/Vd", OptArg, L"/Odump" };
+  VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"main",
+    pTarget, optDumpArgs, _countof(optDumpArgs), nullptr, 0, nullptr, &pResult));
+  VerifyOperationSucceeded(pResult);
+  VERIFY_SUCCEEDED(pResult->GetResult(&pOptDump));
+  pResult.Release();
+  passes = BlobToUtf8(pOptDump);
+  CA2W passesW(passes.c_str(), CP_UTF8);
+
+  // Get the high-level compile of the program.
+  LPCWSTR highLevelArgs[] = { L"/Vd", OptArg, L"/fcgl" };
+  VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"main",
+    pTarget, highLevelArgs, _countof(highLevelArgs), nullptr, 0, nullptr, &pResult));
+  VerifyOperationSucceeded(pResult);
+  VERIFY_SUCCEEDED(pResult->GetResult(&pHighLevelBlob));
+  pResult.Release();
+
+  // Create a list of passes.
+  SplitPassList(passesW.m_psz, passList);
+  ExtractFunctionPasses(passList, prefixPassList);
+
+  // For each point in between the passes ...
+  for (size_t i = 0; i <= passList.size(); ++i) {
+    size_t secondPassIdx = i;
+    size_t firstPassCount = i;
+    size_t secondPassCount = passList.size() - i;
+
+    // If we find an -hlsl-passes-nopause, pause/resume will not work past this.
+    if (i > 0 && 0 == wcscmp(L"-hlsl-passes-nopause", passList[i - 1])) {
+      break;
+    }
+
+    CComPtr<IDxcBlob> pFirstModule;
+    CComPtr<IDxcBlob> pSecondModule;
+    CComPtr<IDxcBlob> pAssembledBlob;
+    std::vector<LPCWSTR> firstPassList, secondPassList;
+    firstPassList = prefixPassList;
+    firstPassList.push_back(L"-opt-mod-passes");
+    secondPassList = firstPassList;
+    firstPassList.insert(firstPassList.end(), passList.begin(), passList.begin() + firstPassCount);
+    firstPassList.push_back(L"-hlsl-passes-pause");
+    secondPassList.push_back(L"-hlsl-passes-resume");
+    secondPassList.insert(secondPassList.end(), passList.begin() + secondPassIdx, passList.begin() + secondPassIdx + secondPassCount);
+
+    // Run a first pass.
+    VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(pHighLevelBlob, 
+      firstPassList.data(), (UINT32)firstPassList.size(),
+      &pFirstModule, nullptr));
+
+    // Run a second pass.
+    VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(pFirstModule,
+      secondPassList.data(), (UINT32)secondPassList.size(),
+      &pSecondModule, nullptr));
+
+    // Assembly it into a container so the disassembler shows equivalent data.
+    AssembleToContainer(m_dllSupport, pSecondModule, &pAssembledBlob);
+
+    // Verify we get the same results as in the full version.
+    std::string assembly = DisassembleProgram(m_dllSupport, pAssembledBlob);
+    if (0 != strcmp(assembly.c_str(), originalAssembly.c_str())) {
+      LogCommentFmt(L"Difference found in disassembly in iteration %u when breaking before '%s'", i, (i == passList.size()) ? L"(full list)" : passList[i]);
+      LogCommentFmt(L"Original assembly\r\n%S", originalAssembly.c_str());
+      LogCommentFmt(L"\r\nReassembled assembly\r\n%S", assembly.c_str());
+      VERIFY_FAIL();
+    }
+  }
+}

+ 3 - 0
utils/hct/hctdb.py

@@ -1266,6 +1266,9 @@ class db_dxil(object):
         add_pass('scalarizer', 'Scalarizer', 'Scalarize vector operations', [])
         add_pass('multi-dim-one-dim', 'MultiDimArrayToOneDimArray', 'Flatten multi-dim array into one-dim array', [])
         add_pass('resource-handle', 'ResourceToHandle', 'Lower resource into handle', [])
+        add_pass('hlsl-passes-nopause', 'NoPausePasses', 'Clears metadata used for pause and resume', [])
+        add_pass('hlsl-passes-pause', 'PausePasses', 'Prepare to pause passes', [])
+        add_pass('hlsl-passes-resume', 'ResumePasses', 'Prepare to resume passes', [])
         add_pass('hlsl-dxil-condense', 'DxilCondenseResources', 'DXIL Condense Resources', [])
         add_pass('hlsl-dxil-eliminate-output-dynamic', 'DxilEliminateOutputDynamicIndexing', 'DXIL eliminate ouptut dynamic indexing', [])
         add_pass('hlsl-dxil-add-pixel-hit-instrmentation', 'DxilAddPixelHitInstrumentation', 'DXIL Count completed PS invocations and costs', [