SPV compression: Remove file/path manipulation stuff, setting up for that to be a...
authorJohn Kessenich <cepheus@frii.com>
Fri, 15 May 2015 16:02:07 +0000 (16:02 +0000)
committerJohn Kessenich <cepheus@frii.com>
Fri, 15 May 2015 16:02:07 +0000 (16:02 +0000)
git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@31201 e7fa87d3-cd2b-0410-9028-fcbf551c1848

SPIRV/SPVRemapper.cpp
SPIRV/SPVRemapper.h
StandAlone/StandAlone.cpp

index 8147a28..97eb4a5 100644 (file)
@@ -1,29 +1,50 @@
+//\r
+//Copyright (C) 2015 LunarG, Inc.\r
+//\r
+//All rights reserved.\r
+//\r
+//Redistribution and use in source and binary forms, with or without\r
+//modification, are permitted provided that the following conditions\r
+//are met:\r
+//\r
+//    Redistributions of source code must retain the above copyright\r
+//    notice, this list of conditions and the following disclaimer.\r
+//\r
+//    Redistributions in binary form must reproduce the above\r
+//    copyright notice, this list of conditions and the following\r
+//    disclaimer in the documentation and/or other materials provided\r
+//    with the distribution.\r
+//\r
+//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its\r
+//    contributors may be used to endorse or promote products derived\r
+//    from this software without specific prior written permission.\r
+//\r
+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\r
+//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\r
+//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\r
+//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\r
+//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
+//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
+//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+//POSSIBILITY OF SUCH DAMAGE.\r
+//\r
+
 #include "SPVRemapper.h"
 #include "doc.h"
-
-/* -*-mode:c++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
-
-// Poor man's basename: given a complete path, return file portion.
-// E.g:
-//      Linux:  /foo/bar/test  -> test
-//      Win:   c:\foo\bar\test -> test
-// It's not very efficient, but that doesn't matter for our minimal-duty use.
-// Using boost::filesystem would be better in many ways, but want to avoid that dependency.
-const std::string spv::spirvbin_base_t::basename(const std::string& filename)
-{
-   const size_t sepLoc = filename.find_last_of(path_sep_char());
-
-   return (sepLoc == filename.npos) ? filename : filename.substr(sepLoc+1);
-}
-
-#if !defined (use_cpp11)
-// ... not supported before C++11
-#else // defined (use_cpp11)
-
-#include <fstream>
-#include <algorithm>
-#include <cassert>
-
+\r
+/* -*-mode:c++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 3 -*- */\r
+\r
+#if !defined (use_cpp11)\r
+// ... not supported before C++11\r
+#else // defined (use_cpp11)\r
+\r
+#include <algorithm>\r
+#include <cassert>\r
+\r
 namespace spv {
 
 // By default, just abort on error.  Can be overridden via RegisterErrorHandler
@@ -201,20 +222,20 @@ spv::Id spirvbin_t::localId(spv::Id id, spv::Id newId)
    
    if (id >= idMapL.size())
       idMapL.resize(id+1, unused);
-
-   if (newId != unmapped && newId != unused) {
-      if (isOldIdUnused(id))
-         ferror(std::string("ID unused in module: ") + std::to_string(id));
-
-      if (!isOldIdUnmapped(id))
-         ferror(std::string("ID already mapped: ") + std::to_string(id) + " -> "
-                + std::to_string(localId(id)));
-
-      if (isNewIdMapped(newId))
-         ferror(std::string("ID already used in module: ") + std::to_string(newId));
-
-      msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId));
-      setMapped(newId);
+\r
+   if (newId != unmapped && newId != unused) {\r
+      if (isOldIdUnused(id))\r
+          error(std::string("ID unused in module: ") + std::to_string(id));\r
+\r
+      if (!isOldIdUnmapped(id))\r
+         error(std::string("ID already mapped: ") + std::to_string(id) + " -> "\r
+                + std::to_string(localId(id)));\r
+\r
+      if (isNewIdMapped(newId))\r
+         error(std::string("ID already used in module: ") + std::to_string(newId));\r
+\r
+      msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId));\r
+      setMapped(newId);\r
       largestNewId = std::max(largestNewId, newId);
    }
 
@@ -235,45 +256,15 @@ std::string spirvbin_t::literalString(int word) const
       literal += *bytes++;
    
    return literal;
-}
-
-
-// Write word stream to disk, in outputDir, with same filename used to read it.
-void spirvbin_t::write(const std::string& outputDir) const
-{
-   if (filename.empty())
-      error("missing filename");
-
-   if (outputDir.empty())
-      error("missing output directory");
-
-   const std::string outfile = outputDir + path_sep_char() + basename(filename);
-
-   std::ofstream fp;
-
-   msg(2, 2, std::string("writing: ") + outfile);
-
-   fp.open(outfile, std::fstream::out | std::fstream::binary);
-
-   if (fp.fail())
-      error(std::string("error opening file for write: ") + outfile);
-
-   for (auto word : spv) {
-      fp.write((char *)&word, sizeof(word));
-      if (fp.fail())
-         error(std::string("error writing file: ") + outfile);
-   }
-
-   // file is closed by destructor
-}
-
-
-void spirvbin_t::applyMap()
-{
-   msg(3, 2, std::string("Applying map: ") + basename(filename));
-   
-   // Map local IDs through the ID map
-   process(inst_fn_nop, // ignore instructions
+}\r
+\r
+\r
+void spirvbin_t::applyMap()\r
+{\r
+   msg(3, 2, std::string("Applying map: "));\r
+   \r
+   // Map local IDs through the ID map\r
+   process(inst_fn_nop, // ignore instructions\r
            [this](spv::Id& id) {
               id = localId(id);
               assert(id != unused && id != unmapped);
@@ -282,13 +273,13 @@ void spirvbin_t::applyMap()
 }
 
 
-// Find free IDs for anything we haven't mapped
-void spirvbin_t::mapRemainder()
-{
-   msg(3, 2, std::string("Remapping remainder: ") + basename(filename));
-
-   spv::Id     unusedId  = 1;  // can't use 0: that's NoResult
-   spirword_t  maxBound  = 0;
+// Find free IDs for anything we haven't mapped\r
+void spirvbin_t::mapRemainder()\r
+{\r
+   msg(3, 2, std::string("Remapping remainder: "));\r
+\r
+   spv::Id     unusedId  = 1;  // can't use 0: that's NoResult\r
+   spirword_t  maxBound  = 0;\r
 
    for (spv::Id id = 0; id < idMapL.size(); ++id) {
       if (isOldIdUnused(id))
@@ -296,13 +287,13 @@ void spirvbin_t::mapRemainder()
 
       // Find a new mapping for any used but unmapped IDs
       if (isOldIdUnmapped(id))
-         localId(id, unusedId = nextUnusedId(unusedId));
-
-      if (isOldIdUnmapped(id))
-         ferror(std::string("old ID not mapped: ") + std::to_string(id));
-
-      // Track max bound
-      maxBound = std::max(maxBound, localId(id) + 1);
+         localId(id, unusedId = nextUnusedId(unusedId));\r
+\r
+      if (isOldIdUnmapped(id))\r
+         error(std::string("old ID not mapped: ") + std::to_string(id));\r
+\r
+      // Track max bound\r
+      maxBound = std::max(maxBound, localId(id) + 1);\r
    }
 
    bound(maxBound); // reset header ID bound to as big as it now needs to be
@@ -323,13 +314,13 @@ void spirvbin_t::stripDebug()
       },
       op_fn_nop);
 }
-
-void spirvbin_t::buildLocalMaps()
-{
-   msg(2, 2, std::string("build local maps: ") + filename);
-
-   mapped.clear();
-   idMapL.clear();
+\r
+void spirvbin_t::buildLocalMaps()\r
+{\r
+   msg(2, 2, std::string("build local maps: "));\r
+\r
+   mapped.clear();\r
+   idMapL.clear();\r
    nameMap.clear();
    fnPos.clear();
    fnPosDCE.clear();
@@ -359,19 +350,19 @@ void spirvbin_t::buildLocalMaps()
          } else if (opCode == spv::Op::OpFunctionCall) {
             ++fnCalls[asId(start + 3)];
          } else if (opCode == spv::Op::OpEntryPoint) {
-            entryPoint = asId(start + 2);
-         } else if (opCode == spv::Op::OpFunction) {
-            if (fnStart != 0)
-               ferror("nested function found");
-            fnStart = start;
-            fnRes   = asId(start + 2);
-         } else if (opCode == spv::Op::OpFunctionEnd) {
-            assert(fnRes != spv::NoResult);
-            if (fnStart == 0)
-               ferror("function end without function start");
-            fnPos[fnRes] = {fnStart, start + asWordCount(start)};
-            fnStart = 0;
-         } else if (isConstOp(opCode)) {
+            entryPoint = asId(start + 2);\r
+         } else if (opCode == spv::Op::OpFunction) {\r
+            if (fnStart != 0)\r
+               error("nested function found");\r
+            fnStart = start;\r
+            fnRes   = asId(start + 2);\r
+         } else if (opCode == spv::Op::OpFunctionEnd) {\r
+            assert(fnRes != spv::NoResult);\r
+            if (fnStart == 0)\r
+               error("function end without function start");\r
+            fnPos[fnRes] = {fnStart, start + asWordCount(start)};\r
+            fnStart = 0;\r
+         } else if (isConstOp(opCode)) {\r
             assert(asId(start + 2) != spv::NoResult);
             typeConstPos.insert(start);
             typeConstPosR[asId(start + 2)] = start;
@@ -385,79 +376,45 @@ void spirvbin_t::buildLocalMaps()
       },
 
       [this](spv::Id& id) { localId(id, unmapped); }
-      );
-}
-
-// Read word stream from disk
-void spirvbin_t::read(const std::string& inFilename)
-{
-   std::ifstream fp;
-   filename = inFilename;
-
-   msg(2, 2, std::string("reading: ") + filename);
-
-   spv.clear();
-   fp.open(filename, std::fstream::in | std::fstream::binary);
-
-   if (fp.fail())
-      ferror("error opening file for read: ");
-
-   // Reserve space (for efficiency, not for correctness)
-   fp.seekg(0, fp.end);
-   spv.reserve(size_t(fp.tellg()) / sizeof(spirword_t));
-   fp.seekg(0, fp.beg);
-
-   while (!fp.eof()) {
-      spirword_t inWord;
-      fp.read((char *)&inWord, sizeof(inWord));
-
-      if (!fp.eof()) {
-         spv.push_back(inWord);
-         if (fp.fail())
-            ferror("error reading file: ");
-      }
-   }
-}
-
-
-// Validate the SPIR header
-void spirvbin_t::validate() const
-{
-   msg(2, 2, std::string("validating: ") + filename);
-
-   if (spv.size() < header_size)
-      ferror("file too short: ");
-
-   if (magic() != spv::MagicNumber)
-      ferror("bad magic number");
-
-   // 1 = version:            TODO: print for verbose output
-   // 2 = generator magic:    TODO: print for verbose output
-   // 3 = result <id> bound:  TODO: print for verbose output
-
-   if (schemaNum() != 0)
-      ferror("bad schema, must be 0");
-}
-
-
+      );\r
+}\r
+\r
+// Validate the SPIR header\r
+void spirvbin_t::validate() const\r
+{\r
+   msg(2, 2, std::string("validating: "));\r
+\r
+   if (spv.size() < header_size)\r
+      error("file too short: ");\r
+\r
+   if (magic() != spv::MagicNumber)\r
+      error("bad magic number");\r
+\r
+   // field 1 = version\r
+   // field 2 = generator magic\r
+   // field 3 = result <id> bound\r
+\r
+   if (schemaNum() != 0)\r
+      error("bad schema, must be 0");\r
+}\r
+\r
+\r
 int spirvbin_t::processInstruction(int word, instfn_t instFn, idfn_t idFn)
 {
    const auto     instructionStart = word;
    const unsigned wordCount = asWordCount(instructionStart);
    const spv::Op  opCode    = asOpCode(instructionStart);
-   const int      nextInst  = word++ + wordCount;
-
-   if (nextInst > int(spv.size()))
-      ferror("spir instruction terminated too early");
-
-   // Base for computing number of operands; will be updated as more is learned
-   unsigned numOperands = wordCount - 1;
-
-   // msg(5, 4, std::string("opcode: ") + spv::InstructionDesc[opCode].opName);
-
-   if (instFn(opCode, instructionStart))
-      return nextInst;
-
+   const int      nextInst  = word++ + wordCount;\r
+\r
+   if (nextInst > int(spv.size()))\r
+      error("spir instruction terminated too early");\r
+\r
+   // Base for computing number of operands; will be updated as more is learned\r
+   unsigned numOperands = wordCount - 1;\r
+\r
+   if (instFn(opCode, instructionStart))\r
+      return nextInst;\r
+\r
    // Read type and result ID from instruction desc table
    if (spv::InstructionDesc[opCode].hasType()) {
       idFn(asId(word++));
@@ -903,13 +860,13 @@ void spirvbin_t::optLoadStore()
    buildLocalMaps(); // rebuild ID mapping data
 }
 
-// remove bodies of uncalled functions
-void spirvbin_t::dceFuncs()
-{
-   msg(3, 2, std::string("Removing Dead Functions: ") + filename);
-
-   // TODO: There are more efficient ways to do this.
-   bool changed = true;
+// remove bodies of uncalled functions\r
+void spirvbin_t::dceFuncs()\r
+{\r
+   msg(3, 2, std::string("Removing Dead Functions: "));\r
+\r
+   // TODO: There are more efficient ways to do this.\r
+   bool changed = true;\r
 
    while (changed) {
       changed = false;
@@ -920,13 +877,12 @@ void spirvbin_t::dceFuncs()
             continue;
          }
          
-         const auto call_it = fnCalls.find(fn->first);
-         
-         if (call_it == fnCalls.end() || call_it->second == 0) {
-            // msg(3, 4, std::string("removing dead function: ") + std::to_string(fn->first));
-            changed = true;
-            stripRange.push_back(fn->second);
-            fnPosDCE.insert(*fn);
+         const auto call_it = fnCalls.find(fn->first);\r
+         \r
+         if (call_it == fnCalls.end() || call_it->second == 0) {\r
+            changed = true;\r
+            stripRange.push_back(fn->second);\r
+            fnPosDCE.insert(*fn);\r
 
             // decrease counts of called functions
             process(
@@ -951,13 +907,13 @@ void spirvbin_t::dceFuncs()
    }
 }
 
-// remove unused function variables + decorations
-void spirvbin_t::dceVars()
-{
-   msg(3, 2, std::string("DCE Vars: ") + basename(filename));
-
-   std::unordered_map<spv::Id, int> varUseCount;
-
+// remove unused function variables + decorations\r
+void spirvbin_t::dceVars()\r
+{\r
+   msg(3, 2, std::string("DCE Vars: "));\r
+\r
+   std::unordered_map<spv::Id, int> varUseCount;\r
+\r
    // Count function variable use
    process(
       [&](spv::Op opCode, int start) {
@@ -1088,13 +1044,13 @@ spv::Id spirvbin_t::findType(const spirvbin_t::globaltypes_t& globalTypes, spv::
 
 // Return start position in SPV of given type.  error if not found.
 int spirvbin_t::typePos(spv::Id id) const
-{
-   const auto tid_it = typeConstPosR.find(id);
-   if (tid_it == typeConstPosR.end())
-      ferror("type ID not found");
-
-   return tid_it->second;
-}
+{\r
+   const auto tid_it = typeConstPosR.find(id);\r
+   if (tid_it == typeConstPosR.end())\r
+      error("type ID not found");\r
+\r
+   return tid_it->second;\r
+}\r
 
 // Hash types to canonical values.  This can return ID collisions (it's a bit
 // inevitable): it's up to the caller to handle that gracefully.
@@ -1168,22 +1124,22 @@ std::uint32_t spirvbin_t::hashType(int typeStart) const
             for (unsigned w=3; w < wordCount; ++w)
                hash += w * spv[typeStart+w];
             return hash;
-         }
-
-      default:
-         ferror("unknown type opcode");
-         return 0;
-   }
-}
+         }\r
+\r
+      default:\r
+         error("unknown type opcode");\r
+         return 0;\r
+   }\r
+}\r
 
 void spirvbin_t::mapTypeConst()
-{
-   globaltypes_t globalTypeMap;
-
-   msg(3, 2, std::string("Remapping Consts & Types: ") + basename(filename));
-
-   static const std::uint32_t softTypeIdLimit = 3011; // small prime.  TODO: get from options
-   static const std::uint32_t firstMappedID   = 8;    // offset into ID space
+{\r
+   globaltypes_t globalTypeMap;\r
+\r
+   msg(3, 2, std::string("Remapping Consts & Types: "));\r
+\r
+   static const std::uint32_t softTypeIdLimit = 3011; // small prime.  TODO: get from options\r
+   static const std::uint32_t firstMappedID   = 8;    // offset into ID space\r
    
    for (auto& typeStart : typeConstPos) {
       const spv::Id       resId     = asTypeConstId(typeStart);
@@ -1257,19 +1213,10 @@ void spirvbin_t::remap(std::vector<std::uint32_t>& in_spv, std::uint32_t opts)
 {
    spv.swap(in_spv);
    remap(opts);
-   spv.swap(in_spv);
-}
-
-// remap from a disk file
-void spirvbin_t::remap(const std::string& file, const std::string& outputDir,
-                       std::uint32_t opts)
-{
-   read(file);
-   remap(opts);
-   write(outputDir);
-}
-
-} // namespace SPV
-    
-#endif // defined (use_cpp11)
+   spv.swap(in_spv);\r
+}\r
+\r
+} // namespace SPV\r
+    \r
+#endif // defined (use_cpp11)\r
 
index 4d63410..73b09ec 100644 (file)
-
-#ifndef SPIRVREMAPPER_H
-#define SPIRVREMAPPER_H
-
-#include <string>
-#include <vector>
-
-namespace spv {
-
-// MSVC defines __cplusplus as an older value, even when it supports almost all of 11.
-// We handle that here by making our own symbol.
-#if __cplusplus >= 201103L || _MSC_VER >= 1800
-#   define use_cpp11 1
-#endif
-
-class spirvbin_base_t
-{
-public:
-   enum Options {
-      NONE          = 0,
-      STRIP         = (1<<0),
-      MAP_TYPES     = (1<<1),
-      MAP_NAMES     = (1<<2),
-      MAP_FUNCS     = (1<<3),
-      DCE_FUNCS     = (1<<4),
-      DCE_VARS      = (1<<5),
-      DCE_TYPES     = (1<<6),
-      OPT_LOADSTORE = (1<<7),
-      OPT_FWD_LS    = (1<<8), // EXPERIMENTAL: PRODUCES INVALID SCHEMA-0 SPIRV
-      MAP_ALL       = (MAP_TYPES | MAP_NAMES | MAP_FUNCS),
-      DCE_ALL       = (DCE_FUNCS | DCE_VARS | DCE_TYPES),
-      OPT_ALL       = (OPT_LOADSTORE),
-
-      ALL_BUT_STRIP = (MAP_ALL | DCE_ALL | OPT_ALL),
-      DO_EVERYTHING = (STRIP | ALL_BUT_STRIP)
-   };
-   
-// OS dependent path separator (avoiding boost::filesystem dependency)
-#if defined(_WIN32)
-   static const char path_sep_char() { return '\\'; }
-#else
-   static const char path_sep_char() { return '/';  }
-#endif
-
-   // Poor man's basename, to avoid external dependencies
-   static const std::string basename(const std::string& filename);
-};
-
-} // namespace SPV
-
-#if !defined (use_cpp11)
-#include <stdio.h>
-
-namespace spv {
-
-class spirvbin_t : public spirvbin_base_t
-{
-public:
-    spirvbin_t(int verbose = 0) { }
-
-    void remap(std::vector<unsigned int>& spv, unsigned int opts = 0)
-    {
-        printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n");
-    }
-
-   void remap(const std::string& filename, const std::string& outputDir, unsigned int opts = 0)
-   {
-       printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n");
-   }
-};
-
-} // namespace SPV
-
-#else // defined (use_cpp11)
-
-#include <functional>
-#include <cstdint>
-#include <unordered_map>
-#include <unordered_set>
-#include <map>
-#include <set>
-#include <cassert>
-
-#include "../../glslang/SPIRV/spirv.h"
-#include "../../glslang/SPIRV/spvIR.h"
-
-namespace spv {
-
-// class to hold SPIRV binary data for remapping, DCE, and debug stripping
-class spirvbin_t : public spirvbin_base_t
-{
-public:
-   spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose) { }
-   
-   // remap on an existing binary in memory
-   void remap(std::vector<std::uint32_t>& spv, std::uint32_t opts = Options::DO_EVERYTHING);
-
-   // load binary from disk file, and remap that.
-   void remap(const std::string& filename, const std::string& outputDir,
-              std::uint32_t opts = Options::DO_EVERYTHING);
-
-   // Type for error/log handler functions
-   typedef std::function<void(const std::string&)> errorfn_t;
-   typedef std::function<void(const std::string&)> logfn_t;
-
-   // Register error/log handling functions (can be lambda fn / functor / etc)
-   static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; }
-   static void registerLogHandler(logfn_t handler)     { logHandler   = handler; }
-
-protected:
-   // This can be overridden to provide other message behavior if needed
-   virtual void msg(int minVerbosity, int indent, const std::string& txt) const;
-
-private:
-   // write SPV to given directory using filename passed to remap(filename...)
-   void write(const std::string& outputDir) const;
-
-   // Local to global, or global to local ID map
-   typedef std::unordered_map<spv::Id, spv::Id> idmap_t;
-   typedef std::unordered_set<spv::Id>          idset_t;
-
-   void read(const std::string& filename);          // read SPV from disk file
-   void remap(std::uint32_t opts = Options::DO_EVERYTHING);
-
-   // Map of names to IDs
-   typedef std::unordered_map<std::string, spv::Id> namemap_t;
-
-   typedef std::uint32_t spirword_t;
-
-   typedef std::pair<int, int> range_t;
-   typedef std::function<void(spv::Id&)>           idfn_t;
-   typedef std::function<bool(spv::Op, int start)> instfn_t;
-
-   // Special Values for ID map:
-   static const spv::Id unmapped;     // unchanged from default value
-   static const spv::Id unused;       // unused ID
-   static const int     header_size;  // SPIR header = 5 words
-
-   class id_iterator_t;
-
-   // For mapping type entries between different shaders
-   typedef std::vector<spirword_t>        typeentry_t;
-   typedef std::map<spv::Id, typeentry_t> globaltypes_t;
-
-   // A set that preserves position order, and a reverse map
-   typedef std::set<int>                    posmap_t;
-   typedef std::unordered_map<spv::Id, int> posmap_rev_t;
-
-   // handle error
-   void error(const std::string& txt) const { errorHandler(txt); }
-   // handle error with our filename appended to the string
-   void ferror(const std::string& txt) const {
-      error(std::string("\nERROR processing file ") + filename + ":\n" + txt);
-   }
-
-   bool    isConstOp(spv::Op opCode)       const;
-   bool    isTypeOp(spv::Op opCode)        const;
-   bool    isStripOp(spv::Op opCode)       const;
-   bool    isFlowCtrlOpen(spv::Op opCode)  const;
-   bool    isFlowCtrlClose(spv::Op opCode) const;
-   range_t literalRange(spv::Op opCode)    const;
-   range_t typeRange(spv::Op opCode)       const;
-   range_t constRange(spv::Op opCode)      const;
-   
-   spv::Id&        asId(int word)                { return spv[word]; }
-   const spv::Id&  asId(int word)          const { return spv[word]; }
-   spv::Op         asOpCode(int word)      const { return opOpCode(spv[word]); }
-   std::uint32_t   asOpCodeHash(int word);
-   spv::Decoration asDecoration(int word)  const { return spv::Decoration(spv[word]); }
-   unsigned        asWordCount(int word)   const { return opWordCount(spv[word]); }
-   spv::Id         asTypeConstId(int word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); }
-   int             typePos(spv::Id id)     const;
-
-   static unsigned    opWordCount(spirword_t data) { return data >> spv::WordCountShift; }
-   static spv::Op     opOpCode(spirword_t data)    { return spv::Op(data & spv::OpCodeMask); }
-
-   // Header access & set methods
-   spirword_t  magic()    const       { return spv[0]; } // return magic number
-   spirword_t  bound()    const       { return spv[3]; } // return Id bound from header
-   spirword_t  bound(spirword_t b)    { return spv[3] = b; };
-   spirword_t  genmagic() const       { return spv[2]; } // generator magic
-   spirword_t  genmagic(spirword_t m) { return spv[2] = m; }
-   spirword_t  schemaNum() const      { return spv[4]; } // schema number from header
-
-   // Mapping fns: get
-   spv::Id     localId(spv::Id id) const { return idMapL[id]; }
-
-   // Mapping fns: set
-   inline spv::Id   localId(spv::Id id, spv::Id newId);
-   void             countIds(spv::Id id);
-
-   // Return next unused new local ID.
-   // NOTE: boost::dynamic_bitset would be more efficient due to find_next(),
-   // which std::vector<bool> doens't have.
-   inline spv::Id   nextUnusedId(spv::Id id);
-
-   void buildLocalMaps();
-   std::string literalString(int word) const; // Return literal as a std::string
-   int literalStringWords(const std::string& str) const { return (int(str.size())+4)/4; }
-
-   bool isNewIdMapped(spv::Id newId)   const { return isMapped(newId);            }
-   bool isOldIdUnmapped(spv::Id oldId) const { return localId(oldId) == unmapped; }
-   bool isOldIdUnused(spv::Id oldId)   const { return localId(oldId) == unused;   }
-   bool isOldIdMapped(spv::Id oldId)   const { return !isOldIdUnused(oldId) && !isOldIdUnmapped(oldId); }
-   bool isFunction(spv::Id oldId)      const { return fnPos.find(oldId) != fnPos.end(); }
-
-   // bool    matchType(const globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const;
-   // spv::Id findType(const globaltypes_t& globalTypes, spv::Id lt) const;
-   std::uint32_t hashType(int typeStart) const;
-
-   spirvbin_t& process(instfn_t, idfn_t, int begin = 0, int end = 0);
-   int         processInstruction(int word, instfn_t, idfn_t);
-
-   void        validate() const;
-   void        mapTypeConst();
-   void        mapFnBodies();
-   void        optLoadStore();
-   void        dceFuncs();
-   void        dceVars();
-   void        dceTypes();
-   void        mapNames();
-   void        foldIds();  // fold IDs to smallest space
-   void        forwardLoadStores(); // load store forwarding (EXPERIMENTAL)
-   void        offsetIds(); // create relative offset IDs
-
-   void        applyMap();            // remap per local name map
-   void        mapRemainder();        // map any IDs we haven't touched yet
-   void        stripDebug();          // strip debug info
-   void        strip();               // remove debug symbols
-   
-   std::vector<spirword_t> spv;      // SPIR words
-   std::string             filename; // the file this came from
-
-   namemap_t               nameMap;  // ID names from OpName
-
-   // Since we want to also do binary ops, we can't use std::vector<bool>.  we could use
-   // boost::dynamic_bitset, but we're trying to avoid a boost dependency.
-   typedef std::uint64_t bits_t;
-   std::vector<bits_t> mapped; // which new IDs have been mapped
-   static const int mBits = sizeof(bits_t) * 4;
-
-   bool isMapped(spv::Id id) const  { return id < maxMappedId() && ((mapped[id/mBits] & (1LL<<(id%mBits))) != 0); }
-   void setMapped(spv::Id id) { resizeMapped(id); mapped[id/mBits] |= (1LL<<(id%mBits)); }
-   void resizeMapped(spv::Id id) { if (id >= maxMappedId()) mapped.resize(id/mBits+1, 0); }
-   size_t maxMappedId() const { return mapped.size() * mBits; }
-
-   // Add a strip range for a given instruction starting at 'start'
-   // Note: avoiding brace initializers to please older versions os MSVC.
-   void stripInst(int start) { stripRange.push_back(std::pair<unsigned, unsigned>(start, start + asWordCount(start))); }
-
-   // Function start and end.  use unordered_map because we'll have
-   // many fewer functions than IDs.
-   std::unordered_map<spv::Id, std::pair<int, int>> fnPos;
-   std::unordered_map<spv::Id, std::pair<int, int>> fnPosDCE; // deleted functions
-
-   // Which functions are called, anywhere in the module, with a call count
-   std::unordered_map<spv::Id, int> fnCalls;
-   
-   posmap_t     typeConstPos;   // word positions that define types & consts (ordered)
-   posmap_rev_t typeConstPosR;  // reverse map from IDs to positions
-   
-   std::vector<spv::Id>  idMapL;   // ID {M}ap from {L}ocal to {G}lobal IDs
-
-   spv::Id entryPoint;      // module entry point
-   spv::Id largestNewId;    // biggest new ID we have mapped anything to
-
-   // Sections of the binary to strip, given as [begin,end)
-   std::vector<std::pair<unsigned, unsigned>> stripRange;
-
-   // processing options:
-   std::uint32_t options;
-   int           verbose;     // verbosity level
-
-   static errorfn_t errorHandler;
-   static logfn_t   logHandler;
-};
-
-} // namespace SPV
-
-#endif // defined (use_cpp11)
-#endif // SPIRVREMAPPER_H
+//\r
+//Copyright (C) 2015 LunarG, Inc.\r
+//\r
+//All rights reserved.\r
+//\r
+//Redistribution and use in source and binary forms, with or without\r
+//modification, are permitted provided that the following conditions\r
+//are met:\r
+//\r
+//    Redistributions of source code must retain the above copyright\r
+//    notice, this list of conditions and the following disclaimer.\r
+//\r
+//    Redistributions in binary form must reproduce the above\r
+//    copyright notice, this list of conditions and the following\r
+//    disclaimer in the documentation and/or other materials provided\r
+//    with the distribution.\r
+//\r
+//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its\r
+//    contributors may be used to endorse or promote products derived\r
+//    from this software without specific prior written permission.\r
+//\r
+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\r
+//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\r
+//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\r
+//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\r
+//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
+//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
+//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+//POSSIBILITY OF SUCH DAMAGE.\r
+//\r
+\r
+#ifndef SPIRVREMAPPER_H\r
+#define SPIRVREMAPPER_H\r
+\r
+#include <string>\r
+#include <vector>\r
+\r
+namespace spv {\r
+\r
+// MSVC defines __cplusplus as an older value, even when it supports almost all of 11.\r
+// We handle that here by making our own symbol.\r
+#if __cplusplus >= 201103L || _MSC_VER >= 1800\r
+#   define use_cpp11 1\r
+#endif\r
+\r
+class spirvbin_base_t\r
+{\r
+public:\r
+   enum Options {\r
+      NONE          = 0,\r
+      STRIP         = (1<<0),\r
+      MAP_TYPES     = (1<<1),\r
+      MAP_NAMES     = (1<<2),\r
+      MAP_FUNCS     = (1<<3),\r
+      DCE_FUNCS     = (1<<4),\r
+      DCE_VARS      = (1<<5),\r
+      DCE_TYPES     = (1<<6),\r
+      OPT_LOADSTORE = (1<<7),\r
+      OPT_FWD_LS    = (1<<8), // EXPERIMENTAL: PRODUCES INVALID SCHEMA-0 SPIRV\r
+      MAP_ALL       = (MAP_TYPES | MAP_NAMES | MAP_FUNCS),\r
+      DCE_ALL       = (DCE_FUNCS | DCE_VARS | DCE_TYPES),\r
+      OPT_ALL       = (OPT_LOADSTORE),\r
+\r
+      ALL_BUT_STRIP = (MAP_ALL | DCE_ALL | OPT_ALL),\r
+      DO_EVERYTHING = (STRIP | ALL_BUT_STRIP)\r
+   };\r
+};\r
+\r
+} // namespace SPV\r
+\r
+#if !defined (use_cpp11)\r
+#include <stdio.h>\r
+\r
+namespace spv {\r
+class spirvbin_t : public spirvbin_base_t\r
+{\r
+public:\r
+    spirvbin_t(int verbose = 0) { }\r
+\r
+    void remap(std::vector<unsigned int>& spv, unsigned int opts = 0)\r
+    {\r
+        printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n");\r
+    }\r
+};\r
+\r
+} // namespace SPV\r
+\r
+#else // defined (use_cpp11)\r
+\r
+#include <functional>\r
+#include <cstdint>\r
+#include <unordered_map>\r
+#include <unordered_set>\r
+#include <map>\r
+#include <set>\r
+#include <cassert>\r
+\r
+#include "../../glslang/SPIRV/spirv.h"\r
+#include "../../glslang/SPIRV/spvIR.h"\r
+\r
+namespace spv {\r
+\r
+// class to hold SPIRV binary data for remapping, DCE, and debug stripping\r
+class spirvbin_t : public spirvbin_base_t\r
+{\r
+public:\r
+   spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose) { }\r
+   \r
+   // remap on an existing binary in memory\r
+   void remap(std::vector<std::uint32_t>& spv, std::uint32_t opts = Options::DO_EVERYTHING);\r
+\r
+   // Type for error/log handler functions\r
+   typedef std::function<void(const std::string&)> errorfn_t;\r
+   typedef std::function<void(const std::string&)> logfn_t;\r
+\r
+   // Register error/log handling functions (can be lambda fn / functor / etc)\r
+   static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; }\r
+   static void registerLogHandler(logfn_t handler)     { logHandler   = handler; }\r
+\r
+protected:\r
+   // This can be overridden to provide other message behavior if needed\r
+   virtual void msg(int minVerbosity, int indent, const std::string& txt) const;\r
+\r
+private:\r
+   // Local to global, or global to local ID map\r
+   typedef std::unordered_map<spv::Id, spv::Id> idmap_t;\r
+   typedef std::unordered_set<spv::Id>          idset_t;\r
+\r
+   void remap(std::uint32_t opts = Options::DO_EVERYTHING);\r
+\r
+   // Map of names to IDs\r
+   typedef std::unordered_map<std::string, spv::Id> namemap_t;\r
+\r
+   typedef std::uint32_t spirword_t;\r
+\r
+   typedef std::pair<int, int> range_t;\r
+   typedef std::function<void(spv::Id&)>           idfn_t;\r
+   typedef std::function<bool(spv::Op, int start)> instfn_t;\r
+\r
+   // Special Values for ID map:\r
+   static const spv::Id unmapped;     // unchanged from default value\r
+   static const spv::Id unused;       // unused ID\r
+   static const int     header_size;  // SPIR header = 5 words\r
+\r
+   class id_iterator_t;\r
+\r
+   // For mapping type entries between different shaders\r
+   typedef std::vector<spirword_t>        typeentry_t;\r
+   typedef std::map<spv::Id, typeentry_t> globaltypes_t;\r
+\r
+   // A set that preserves position order, and a reverse map\r
+   typedef std::set<int>                    posmap_t;\r
+   typedef std::unordered_map<spv::Id, int> posmap_rev_t;\r
+\r
+   // handle error\r
+   void error(const std::string& txt) const { errorHandler(txt); }\r
+\r
+   bool    isConstOp(spv::Op opCode)       const;\r
+   bool    isTypeOp(spv::Op opCode)        const;\r
+   bool    isStripOp(spv::Op opCode)       const;\r
+   bool    isFlowCtrlOpen(spv::Op opCode)  const;\r
+   bool    isFlowCtrlClose(spv::Op opCode) const;\r
+   range_t literalRange(spv::Op opCode)    const;\r
+   range_t typeRange(spv::Op opCode)       const;\r
+   range_t constRange(spv::Op opCode)      const;\r
+   \r
+   spv::Id&        asId(int word)                { return spv[word]; }\r
+   const spv::Id&  asId(int word)          const { return spv[word]; }\r
+   spv::Op         asOpCode(int word)      const { return opOpCode(spv[word]); }\r
+   std::uint32_t   asOpCodeHash(int word);\r
+   spv::Decoration asDecoration(int word)  const { return spv::Decoration(spv[word]); }\r
+   unsigned        asWordCount(int word)   const { return opWordCount(spv[word]); }\r
+   spv::Id         asTypeConstId(int word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); }\r
+   int             typePos(spv::Id id)     const;\r
+\r
+   static unsigned    opWordCount(spirword_t data) { return data >> spv::WordCountShift; }\r
+   static spv::Op     opOpCode(spirword_t data)    { return spv::Op(data & spv::OpCodeMask); }\r
+\r
+   // Header access & set methods\r
+   spirword_t  magic()    const       { return spv[0]; } // return magic number\r
+   spirword_t  bound()    const       { return spv[3]; } // return Id bound from header\r
+   spirword_t  bound(spirword_t b)    { return spv[3] = b; };\r
+   spirword_t  genmagic() const       { return spv[2]; } // generator magic\r
+   spirword_t  genmagic(spirword_t m) { return spv[2] = m; }\r
+   spirword_t  schemaNum() const      { return spv[4]; } // schema number from header\r
+\r
+   // Mapping fns: get\r
+   spv::Id     localId(spv::Id id) const { return idMapL[id]; }\r
+\r
+   // Mapping fns: set\r
+   inline spv::Id   localId(spv::Id id, spv::Id newId);\r
+   void             countIds(spv::Id id);\r
+\r
+   // Return next unused new local ID.\r
+   // NOTE: boost::dynamic_bitset would be more efficient due to find_next(),\r
+   // which std::vector<bool> doens't have.\r
+   inline spv::Id   nextUnusedId(spv::Id id);\r
+\r
+   void buildLocalMaps();\r
+   std::string literalString(int word) const; // Return literal as a std::string\r
+   int literalStringWords(const std::string& str) const { return (int(str.size())+4)/4; }\r
+\r
+   bool isNewIdMapped(spv::Id newId)   const { return isMapped(newId);            }\r
+   bool isOldIdUnmapped(spv::Id oldId) const { return localId(oldId) == unmapped; }\r
+   bool isOldIdUnused(spv::Id oldId)   const { return localId(oldId) == unused;   }\r
+   bool isOldIdMapped(spv::Id oldId)   const { return !isOldIdUnused(oldId) && !isOldIdUnmapped(oldId); }\r
+   bool isFunction(spv::Id oldId)      const { return fnPos.find(oldId) != fnPos.end(); }\r
+\r
+   // bool    matchType(const globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const;\r
+   // spv::Id findType(const globaltypes_t& globalTypes, spv::Id lt) const;\r
+   std::uint32_t hashType(int typeStart) const;\r
+\r
+   spirvbin_t& process(instfn_t, idfn_t, int begin = 0, int end = 0);\r
+   int         processInstruction(int word, instfn_t, idfn_t);\r
+\r
+   void        validate() const;\r
+   void        mapTypeConst();\r
+   void        mapFnBodies();\r
+   void        optLoadStore();\r
+   void        dceFuncs();\r
+   void        dceVars();\r
+   void        dceTypes();\r
+   void        mapNames();\r
+   void        foldIds();  // fold IDs to smallest space\r
+   void        forwardLoadStores(); // load store forwarding (EXPERIMENTAL)\r
+   void        offsetIds(); // create relative offset IDs\r
+\r
+   void        applyMap();            // remap per local name map\r
+   void        mapRemainder();        // map any IDs we haven't touched yet\r
+   void        stripDebug();          // strip debug info\r
+   void        strip();               // remove debug symbols\r
+   \r
+   std::vector<spirword_t> spv;      // SPIR words\r
+\r
+   namemap_t               nameMap;  // ID names from OpName\r
+\r
+   // Since we want to also do binary ops, we can't use std::vector<bool>.  we could use\r
+   // boost::dynamic_bitset, but we're trying to avoid a boost dependency.\r
+   typedef std::uint64_t bits_t;\r
+   std::vector<bits_t> mapped; // which new IDs have been mapped\r
+   static const int mBits = sizeof(bits_t) * 4;\r
+\r
+   bool isMapped(spv::Id id) const  { return id < maxMappedId() && ((mapped[id/mBits] & (1LL<<(id%mBits))) != 0); }\r
+   void setMapped(spv::Id id) { resizeMapped(id); mapped[id/mBits] |= (1LL<<(id%mBits)); }\r
+   void resizeMapped(spv::Id id) { if (id >= maxMappedId()) mapped.resize(id/mBits+1, 0); }\r
+   size_t maxMappedId() const { return mapped.size() * mBits; }\r
+\r
+   // Add a strip range for a given instruction starting at 'start'\r
+   // Note: avoiding brace initializers to please older versions os MSVC.\r
+   void stripInst(int start) { stripRange.push_back(std::pair<unsigned, unsigned>(start, start + asWordCount(start))); }\r
+\r
+   // Function start and end.  use unordered_map because we'll have\r
+   // many fewer functions than IDs.\r
+   std::unordered_map<spv::Id, std::pair<int, int>> fnPos;\r
+   std::unordered_map<spv::Id, std::pair<int, int>> fnPosDCE; // deleted functions\r
+\r
+   // Which functions are called, anywhere in the module, with a call count\r
+   std::unordered_map<spv::Id, int> fnCalls;\r
+   \r
+   posmap_t     typeConstPos;   // word positions that define types & consts (ordered)\r
+   posmap_rev_t typeConstPosR;  // reverse map from IDs to positions\r
+   \r
+   std::vector<spv::Id>  idMapL;   // ID {M}ap from {L}ocal to {G}lobal IDs\r
+\r
+   spv::Id entryPoint;      // module entry point\r
+   spv::Id largestNewId;    // biggest new ID we have mapped anything to\r
+\r
+   // Sections of the binary to strip, given as [begin,end)\r
+   std::vector<std::pair<unsigned, unsigned>> stripRange;\r
+\r
+   // processing options:\r
+   std::uint32_t options;\r
+   int           verbose;     // verbosity level\r
+\r
+   static errorfn_t errorHandler;\r
+   static logfn_t   logHandler;\r
+};\r
+\r
+} // namespace SPV\r
+\r
+#endif // defined (use_cpp11)\r
+#endif // SPIRVREMAPPER_H\r
index 3cf9deb..4f38566 100644 (file)
@@ -44,7 +44,6 @@
 #include "../SPIRV/GLSL450Lib.h"
 #include "../SPIRV/doc.h"
 #include "../SPIRV/disassemble.h"
-#include "../SPIRV/SPVRemapper.h"
 #include <string.h>
 #include <stdlib.h>
 #include <math.h>
@@ -72,8 +71,6 @@ enum TOptions {
     EOptionSpv                = 0x0800,
     EOptionHumanReadableSpv   = 0x1000,
     EOptionDefaultDesktop     = 0x2000,
-    EOptionCanonicalizeSpv    = 0x4000,
-    EOptionStripSpv           = 0x8000,
 };
 
 //
@@ -484,17 +481,11 @@ bool ProcessArguments(int argc, char* argv[])
     for (; argc >= 1; argc--, argv++) {
         Work[argc] = 0;
         if (argv[0][0] == '-') {
-            const char optLetter = argv[0][1];
-
-            switch (optLetter) {
-            case 'S': // fall through to -V
-            case 'C': // fall through to -V
-            case 'H': // fall through to -V
+            switch (argv[0][1]) {
+            case 'H':
+                Options |= EOptionHumanReadableSpv;
+                // fall through to -V
             case 'V':
-                if (optLetter == 'H') Options |= EOptionHumanReadableSpv;
-                if (optLetter == 'S') Options |= EOptionStripSpv;
-                if (optLetter == 'C') Options |= EOptionCanonicalizeSpv;
-                
                 Options |= EOptionSpv;
                 Options |= EOptionLinkProgram;
                 break;
@@ -669,17 +660,7 @@ void CompileAndLinkShaders()
                     case EShLangCompute:         name = "comp";    break;
                     default:                     name = "unknown"; break;
                     }
-                    if (Options & (EOptionCanonicalizeSpv | EOptionStripSpv)) {
-                        const unsigned int remapOpts =
-                            ((Options & EOptionCanonicalizeSpv) ? (spv::spirvbin_t::ALL_BUT_STRIP) : 0) |
-                            ((Options & EOptionStripSpv)        ? (spv::spirvbin_t::STRIP) : 0);
-
-                        spv::Parameterize();
-                        spv::spirvbin_t().remap(spirv, remapOpts);
-                    }
-
                     glslang::OutputSpv(spirv, name);
-
                     if (Options & EOptionHumanReadableSpv) {
                         spv::Parameterize();
                         GLSL_STD_450::GetDebugNames(GlslStd450DebugNames);
@@ -886,8 +867,6 @@ void usage()
            "To get other information, use one of the following options:\n"
            "(Each option must be specified separately, but can go anywhere in the command line.)\n"
            "  -V  create SPIR-V in file <stage>.spv\n"
-           "  -C  canonicalize generated SPIR-V: turns on -V\n"
-           "  -S  debug-strip SPIR-V: turns on -V\n"
            "  -H  print human readable form of SPIR-V; turns on -V\n"
            "  -c  configuration dump; use to create default configuration file (redirect to a .conf file)\n"
            "  -d  default to desktop (#version 110) when there is no version in the shader (default is ES version 100)\n"