Add --print-all optimizer option
authorDavid Neto <dneto@google.com>
Thu, 4 Jan 2018 17:59:50 +0000 (12:59 -0500)
committerDavid Neto <dneto@google.com>
Thu, 4 Jan 2018 23:34:18 +0000 (18:34 -0500)
Adds optimizer API to write disassembly to a given output stream
before each pass, and after the last pass.

Adds spirv-opt --print-all option to write disassembly to stderr
before each pass, and after the last pass.

include/spirv-tools/optimizer.hpp
source/opt/optimizer.cpp
source/opt/pass_manager.cpp
source/opt/pass_manager.h
tools/opt/opt.cpp

index afc544a..2141bda 100644 (file)
@@ -16,6 +16,7 @@
 #define SPIRV_TOOLS_OPTIMIZER_HPP_
 
 #include <memory>
+#include <ostream>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -113,6 +114,11 @@ class Optimizer {
   // pass manager is destroyed.
   std::vector<const char*> GetPassNames() const;
 
+  // Sets the option to print the disassembly before each pass and after the
+  // last pass.  If |out| is null, then no output is generated.  Otherwise,
+  // output is sent to the |out| output stream.
+  Optimizer& SetPrintAll(std::ostream* out);
+
  private:
   struct Impl;                  // Opaque struct for holding internal data.
   std::unique_ptr<Impl> impl_;  // Unique pointer to internal data.
index d0e5123..6c8b854 100644 (file)
@@ -179,6 +179,11 @@ bool Optimizer::Run(const uint32_t* original_binary,
   return status != opt::Pass::Status::Failure;
 }
 
+Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
+  impl_->pass_manager.SetPrintAll(out);
+  return *this;
+}
+
 Optimizer::PassToken CreateNullPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
 }
index bb79d2b..e55c7f2 100644 (file)
 // limitations under the License.
 
 #include "pass_manager.h"
+
+#include <iostream>
+#include <vector>
+
 #include "ir_context.h"
+#include "spirv-tools/libspirv.hpp"
 
 namespace spvtools {
+
 namespace opt {
 
 Pass::Status PassManager::Run(ir::IRContext* context) {
   auto status = Pass::Status::SuccessWithoutChange;
+
+  // If print_all_stream_ is not null, prints the disassembly of the module
+  // to that stream, with the given preamble and optionally the pass name.
+  auto print_disassembly = [&context, this](const char* preamble, Pass* pass) {
+    if (print_all_stream_) {
+      std::vector<uint32_t> binary;
+      context->module()->ToBinary(&binary, false);
+      SpirvTools t(SPV_ENV_UNIVERSAL_1_2);
+      std::string disassembly;
+      t.Disassemble(binary, &disassembly, 0);
+      *print_all_stream_ << preamble << (pass ? pass->name() : "") << "\n"
+                         << disassembly << std::endl;
+    }
+  };
+
   for (const auto& pass : passes_) {
+    print_disassembly("; IR before pass ", pass.get());
     const auto one_status = pass->Run(context);
     if (one_status == Pass::Status::Failure) return one_status;
     if (one_status == Pass::Status::SuccessWithChange) status = one_status;
   }
+  print_disassembly("; IR after last pass", nullptr);
 
   // Set the Id bound in the header in case a pass forgot to do so.
   //
index 7b1ef9d..189d226 100644 (file)
@@ -16,6 +16,7 @@
 #define LIBSPIRV_OPT_PASS_MANAGER_H_
 
 #include <memory>
+#include <ostream>
 #include <vector>
 
 #include "log.h"
@@ -38,7 +39,7 @@ class PassManager {
   // The constructed instance will have an empty message consumer, which just
   // ignores all messages from the library. Use SetMessageConsumer() to supply
   // one if messages are of concern.
-  PassManager() : consumer_(nullptr) {}
+  PassManager() : consumer_(nullptr), print_all_stream_(nullptr) {}
 
   // Sets the message consumer to the given |consumer|.
   void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
@@ -68,11 +69,22 @@ class PassManager {
   // After running all the passes, they are removed from the list.
   Pass::Status Run(ir::IRContext* context);
 
+  // Sets the option to print the disassembly before each pass and after the
+  // last pass.   Output is written to |out| if that is not null.  No output
+  // is generated if |out| is null.
+  PassManager& SetPrintAll(std::ostream* out) {
+    print_all_stream_ = out;
+    return *this;
+  }
+
  private:
   // Consumer for messages.
   MessageConsumer consumer_;
   // A vector of passes. Order matters.
   std::vector<std::unique_ptr<Pass>> passes_;
+  // The output stream to write disassembly to before each pass, and after
+  // the last pass.  If this is null, no output is generated.
+  std::ostream* print_all_stream_;
 };
 
 inline void PassManager::AddPass(std::unique_ptr<Pass> pass) {
index ead001b..2f62b4c 100644 (file)
@@ -218,6 +218,9 @@ Options (in lexicographical order):
                'spirv-opt --merge-blocks -O ...' applies the transformation
                --merge-blocks followed by all the transformations implied by
                -O.
+  --print-all
+               Print SPIR-V assembly to standard error output before each pass
+               and after the last pass.
   --private-to-local
                Change the scope of private variables that are used in a single
                function to that function.
@@ -457,6 +460,8 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
         }
       } else if (0 == strcmp(cur_arg, "--ccp")) {
         optimizer->RegisterPass(CreateCCPPass());
+      } else if (0 == strcmp(cur_arg, "--print-all")) {
+        optimizer->SetPrintAll(&std::cerr);
       } else if ('\0' == cur_arg[1]) {
         // Setting a filename of "-" to indicate stdin.
         if (!*in_file) {