}\r
\r
// hash opcode, with special handling for OpExtInst\r
- std::uint32_t spirvbin_t::asOpCodeHash(int word)\r
+ std::uint32_t spirvbin_t::asOpCodeHash(unsigned word)\r
{\r
const spv::Op opCode = asOpCode(word);\r
\r
}\r
}\r
\r
- const auto inst_fn_nop = [](spv::Op, int) { return false; };\r
- const auto op_fn_nop = [](spv::Id&) { };\r
+ const auto inst_fn_nop = [](spv::Op, unsigned) { return false; };\r
+ const auto op_fn_nop = [](spv::Id&) { };\r
\r
// g++ doesn't like these defined in the class proper in an anonymous namespace.\r
// Dunno why. Also MSVC doesn't like the constexpr keyword. Also dunno why.\r
\r
// Parse a literal string from the SPIR binary and return it as an std::string\r
// Due to C++11 RValue references, this doesn't copy the result string.\r
- std::string spirvbin_t::literalString(int word) const\r
+ std::string spirvbin_t::literalString(unsigned word) const\r
{\r
std::string literal;\r
\r
\r
// build local Id and name maps\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
// remember opcodes we want to strip later\r
if (isStripOp(opCode))\r
stripInst(start);\r
\r
// build local Id and name maps\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
// remember opcodes we want to strip later\r
if ((options & Options::STRIP) && isStripOp(opCode))\r
stripInst(start);\r
assert(fnRes != spv::NoResult);\r
if (fnStart == 0)\r
error("function end without function start");\r
- fnPos[fnRes] = {fnStart, start + asWordCount(start)};\r
+ fnPos[fnRes] = range_t(fnStart, start + asWordCount(start));\r
fnStart = 0;\r
} else if (isConstOp(opCode)) {\r
assert(asId(start + 2) != spv::NoResult);\r
}\r
\r
\r
- int spirvbin_t::processInstruction(int word, instfn_t instFn, idfn_t idFn)\r
+ int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn)\r
{\r
const auto instructionStart = word;\r
const unsigned wordCount = asWordCount(instructionStart);\r
}\r
\r
// Make a pass over all the instructions and process them given appropriate functions\r
- spirvbin_t& spirvbin_t::process(instfn_t instFn, idfn_t idFn, int begin, int end)\r
+ spirvbin_t& spirvbin_t::process(instfn_t instFn, idfn_t idFn, unsigned begin, unsigned end)\r
{\r
// For efficiency, reserve name map space. It can grow if needed.\r
nameMap.reserve(32);\r
\r
// If begin or end == 0, use defaults\r
- begin = (begin == 0 ? header_size : begin);\r
- end = (end == 0 ? int(spv.size()) : end);\r
+ begin = (begin == 0 ? header_size : begin);\r
+ end = (end == 0 ? unsigned(spv.size()) : end);\r
\r
// basic parsing and InstructionDesc table borrowed from SpvDisassemble.cpp...\r
- int nextInst = int(spv.size());\r
+ unsigned nextInst = unsigned(spv.size());\r
\r
- for (int word = begin; word < end; word = nextInst)\r
+ for (unsigned word = begin; word < end; word = nextInst)\r
nextInst = processInstruction(word, instFn, idFn);\r
\r
return *this;\r
// Initial approach: go through some high priority opcodes first and assign them\r
// hash values.\r
\r
- spv::Id fnId = spv::NoResult;\r
- std::vector<int> instPos;\r
- instPos.reserve(int(spv.size()) / 16); // initial estimate; can grow if needed.\r
+ spv::Id fnId = spv::NoResult;\r
+ std::vector<unsigned> instPos;\r
+ instPos.reserve(unsigned(spv.size()) / 16); // initial estimate; can grow if needed.\r
\r
// Build local table of instruction start positions\r
process(\r
- [&](spv::Op, int start) { instPos.push_back(start); return true; },\r
+ [&](spv::Op, unsigned start) { instPos.push_back(start); return true; },\r
op_fn_nop);\r
\r
// Window size for context-sensitive canonicalization values\r
// We essentially performa a little convolution around each instruction,\r
// to capture the flavor of nearby code, to hopefully match to similar\r
// code in other modules.\r
- static const int windowSize = 2;\r
+ static const unsigned windowSize = 2;\r
\r
- for (int entry = 0; entry < int(instPos.size()); ++entry) {\r
- const int start = instPos[entry];\r
- const spv::Op opCode = asOpCode(start);\r
+ for (unsigned entry = 0; entry < unsigned(instPos.size()); ++entry) {\r
+ const unsigned start = instPos[entry];\r
+ const spv::Op opCode = asOpCode(start);\r
\r
if (opCode == spv::OpFunction)\r
fnId = asId(start + 2);\r
fnId = spv::NoResult;\r
\r
if (fnId != spv::NoResult) { // if inside a function\r
- const int word = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1);\r
- const int result = spv::InstructionDesc[opCode].hasResult() ? word : -1;\r
+ if (spv::InstructionDesc[opCode].hasResult()) {\r
+ const unsigned word = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1);\r
+ const spv::Id resId = asId(word);\r
+ std::uint32_t hashval = fnId * 17; // small prime\r
\r
- if (result > 0) {\r
- const spv::Id resId = asId(result);\r
- std::uint32_t hashval = fnId * 17; // small prime\r
-\r
- for (int i = entry-1; i >= entry-windowSize; --i) {\r
+ for (unsigned i = entry-1; i >= entry-windowSize; --i) {\r
if (asOpCode(instPos[i]) == spv::OpFunction)\r
break;\r
hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime\r
}\r
\r
- for (int i = entry; i <= entry + windowSize; ++i) {\r
+ for (unsigned i = entry; i <= entry + windowSize; ++i) {\r
if (asOpCode(instPos[i]) == spv::OpFunctionEnd)\r
break;\r
hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime\r
fnId = spv::NoResult;\r
\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
switch (opCode) {\r
case spv::OpFunction:\r
// Reset counters at each function\r
}\r
\r
return false;\r
- },\r
+ },\r
\r
[&](spv::Id& id) {\r
if (thisOpCode != spv::OpNop) {\r
if (isOldIdUnmapped(id))\r
localId(id, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));\r
}\r
- });\r
+ });\r
}\r
\r
-#ifdef NOTDEF\r
- // remove bodies of uncalled functions\r
- void spirvbin_t::offsetIds()\r
- {\r
- // Count of how many functions each ID appears within\r
- std::unordered_map<spv::Id, int> idFnCount;\r
- std::unordered_map<spv::Id, int> idDefinedLoc;\r
- idset_t idsUsed; // IDs used in a given function\r
-\r
- int instCount = 0;\r
-\r
- // create a count of how many functions each ID is used within\r
- process(\r
- [&](spv::OpCode opCode, int start) {\r
- ++instCount;\r
-\r
- switch (opCode) {\r
- case spv::OpFunction:\r
- for (const auto id : idsUsed)\r
- ++idFnCount[id];\r
- idsUsed.clear();\r
- break;\r
-\r
- default:\r
- {\r
- const int word = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1);\r
- const int result = spv::InstructionDesc[opCode].hasResult() ? word : -1;\r
-\r
- if (result > 0)\r
- idDefinedLoc[asId(result)] = instCount;\r
- }\r
- break;\r
- }\r
-\r
- return false;\r
- },\r
-\r
- [&](spv::Id& id) { idsUsed.insert(id); });\r
-\r
- // For each ID defined in exactly one function, replace uses by\r
- // negative offset to definitions in instructions.\r
-\r
- static const int relOffsetLimit = 64;\r
-\r
- instCount = 0;\r
- process([&](spv::OpCode, int) { ++instCount; return false; },\r
- [&](spv::Id& id) {\r
- if (idFnCount[id] == 1 && (instCount - idDefinedLoc[id]) < relOffsetLimit)\r
- id = idDefinedLoc[id] - instCount;\r
- });\r
- }\r
-#endif\r
-\r
-\r
// EXPERIMENTAL: forward IO and uniform load/stores into operands\r
// This produces invalid Schema-0 SPIRV\r
void spirvbin_t::forwardLoadStores()\r
\r
// EXPERIMENTAL: Forward input and access chain loads into consumptions\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
// Add inputs and uniforms to the map\r
if (((opCode == spv::OpVariable && asWordCount(start) == 4) || (opCode == spv::OpVariableArray)) &&\r
(spv[start+3] == spv::StorageClassUniform ||\r
}\r
\r
return false;\r
- },\r
+ },\r
\r
[&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }\r
);\r
idMap.clear();\r
\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
// Add inputs and uniforms to the map\r
if (((opCode == spv::OpVariable && asWordCount(start) == 4) || (opCode == spv::OpVariableArray)) &&\r
(spv[start+3] == spv::StorageClassOutput))\r
}\r
\r
return false;\r
- },\r
+ },\r
op_fn_nop);\r
\r
process(\r
\r
// Find all the function local pointers stored at most once, and not via access chains\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
const int wordCount = asWordCount(start);\r
\r
// Add local variables to the map\r
}\r
\r
return true;\r
- },\r
+ },\r
op_fn_nop);\r
\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0)\r
idMap[asId(start+2)] = idMap[asId(start+3)];\r
return false;\r
- },\r
+ },\r
op_fn_nop);\r
\r
// Remove the load/store/variables for the ones we've discovered\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
if ((opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) ||\r
(opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) ||\r
(opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) {\r
- stripInst(start);\r
- return true;\r
+ stripInst(start);\r
+ return true;\r
}\r
\r
return false;\r
- },\r
+ },\r
\r
[&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }\r
);\r
\r
-\r
strip(); // strip out data we decided to eliminate\r
buildLocalMaps(); // rebuild ID mapping data\r
}\r
\r
// decrease counts of called functions\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
if (opCode == spv::Op::OpFunctionCall) {\r
const auto call_it = fnCalls.find(asId(start + 3));\r
if (call_it != fnCalls.end()) {\r
}\r
\r
return true;\r
- },\r
+ },\r
op_fn_nop,\r
fn->second.first,\r
fn->second.second);\r
\r
// Count function variable use\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
if (opCode == spv::OpVariable) { ++varUseCount[asId(start+2)]; return true; }\r
return false;\r
},\r
\r
// Remove single-use function variables + associated decorations and names\r
process(\r
- [&](spv::Op opCode, int start) {\r
+ [&](spv::Op opCode, unsigned start) {\r
if ((opCode == spv::OpVariable && varUseCount[asId(start+2)] == 1) ||\r
(opCode == spv::OpDecorate && varUseCount[asId(start+1)] == 1) ||\r
(opCode == spv::OpName && varUseCount[asId(start+1)] == 1)) {\r
#endif // NOTDEF\r
\r
// Return start position in SPV of given type. error if not found.\r
- int spirvbin_t::typePos(spv::Id id) const\r
+ unsigned spirvbin_t::typePos(spv::Id id) const\r
{\r
const auto tid_it = typeConstPosR.find(id);\r
if (tid_it == typeConstPosR.end())\r
\r
// Hash types to canonical values. This can return ID collisions (it's a bit\r
// inevitable): it's up to the caller to handle that gracefully.\r
- std::uint32_t spirvbin_t::hashType(int typeStart) const\r
+ std::uint32_t spirvbin_t::hashType(unsigned typeStart) const\r
{\r
const unsigned wordCount = asWordCount(typeStart);\r
const spv::Op opCode = asOpCode(typeStart);\r
\r
// Allocate a new binary big enough to hold old binary\r
// We'll step this iterator through the strip ranges as we go through the binary\r
- decltype(stripRange)::const_iterator strip_it = stripRange.begin();\r
+ auto strip_it = stripRange.begin();\r
\r
int strippedPos = 0;\r
for (unsigned word = 0; word < unsigned(spv.size()); ++word) {\r
mapRemainder(); // map any unmapped IDs\r
applyMap(); // Now remap each shader to the new IDs we've come up with\r
strip(); // strip out data we decided to eliminate\r
-\r
-#define EXPERIMENT3 0\r
-#if (EXPERIMENT3)\r
- // TODO: ... shortcuts for simple single-const access chains and constants,\r
- // folded into high half of the ID space.\r
-#endif\r
}\r
\r
// remap from a memory image\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
+#if __cplusplus >= 201103L || _MSC_VER >= 1700\r
# define use_cpp11 1\r
#endif\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
+ exit(5);\r
}\r
};\r
\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
+ typedef std::pair<unsigned, unsigned> range_t;\r
+ typedef std::function<void(spv::Id&)> idfn_t;\r
+ typedef std::function<bool(spv::Op, unsigned start)> instfn_t;\r
\r
// Special Values for ID map:\r
static const spv::Id unmapped; // unchanged from default value\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
+ spv::Id& asId(unsigned word) { return spv[word]; }\r
+ const spv::Id& asId(unsigned word) const { return spv[word]; }\r
+ spv::Op asOpCode(unsigned word) const { return opOpCode(spv[word]); }\r
+ std::uint32_t asOpCodeHash(unsigned word);\r
+ spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); }\r
+ unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); }\r
+ spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); }\r
+ unsigned 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
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
+ std::string literalString(unsigned 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
\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
+ std::uint32_t hashType(unsigned 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
+ spirvbin_t& process(instfn_t, idfn_t, unsigned begin = 0, unsigned end = 0);\r
+ int processInstruction(unsigned word, instfn_t, idfn_t);\r
\r
void validate() const;\r
void mapTypeConst();\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
+ void stripInst(unsigned start) { stripRange.push_back(range_t(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
+ std::unordered_map<spv::Id, range_t> fnPos;\r
+ std::unordered_map<spv::Id, range_t> 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
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
+ std::vector<range_t> stripRange;\r
\r
// processing options:\r
std::uint32_t options;\r
//POSSIBILITY OF SUCH DAMAGE.\r
//\r
\r
-#include <iostream>
-#include <fstream>
-#include <cstring>
-#include <stdexcept>
-
-#include "../SPIRV/SPVRemapper.h"
-
-namespace {
-
- typedef unsigned int SpvWord;
-
- // 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.
-
- // OS dependent path separator (avoiding boost::filesystem dependency)
-#if defined(_WIN32)
- char path_sep_char() { return '\\'; }
-#else
- char path_sep_char() { return '/'; }
-#endif
-
- std::string 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);
- }
-
- void errHandler(const std::string& str) {
- std::cout << str << std::endl;
- exit(5);
- }
-
- void logHandler(const std::string& str) {
- std::cout << str << std::endl;
- }
-
- // Read word stream from disk
- void read(std::vector<SpvWord>& spv, const std::string& inFilename)
- {
- std::ifstream fp;
-
- std::cout << " reading: " << inFilename << std::endl;
-
- spv.clear();
- fp.open(inFilename, std::fstream::in | std::fstream::binary);
-
- if (fp.fail())
- errHandler("error opening file for read: ");
-
- // Reserve space (for efficiency, not for correctness)
- fp.seekg(0, fp.end);
- spv.reserve(size_t(fp.tellg()) / sizeof(SpvWord));
- fp.seekg(0, fp.beg);
-
- while (!fp.eof()) {
- SpvWord inWord;
- fp.read((char *)&inWord, sizeof(inWord));
-
- if (!fp.eof()) {
- spv.push_back(inWord);
- if (fp.fail())
- errHandler(std::string("error reading file: ") + inFilename);
- }
- }
- }
-
- void write(std::vector<SpvWord>& spv, const std::string& outFile)
- {
- if (outFile.empty())
- errHandler("missing output filename.");
-
- std::ofstream fp;
-
- std::cout << " writing: " << outFile << std::endl;
-
- fp.open(outFile, std::fstream::out | std::fstream::binary);
-
- if (fp.fail())
- errHandler(std::string("error opening file for write: ") + outFile);
-
- for (auto word : spv) {
- fp.write((char *)&word, sizeof(word));
- if (fp.fail())
- errHandler(std::string("error writing file: ") + outFile);
- }
-
- // file is closed by destructor
- }
-
- // Print helpful usage message to stdout, and exit
- void usage(const char* const name, const char* const msg = 0)
- {
- if (msg)
- std::cout << msg << std::endl << std::endl;
-
- std::cout << "Usage: " << std::endl;
-
- std::cout << " " << basename(name)
- << " [-v[v[...]] | --verbose [int]]"
- << " [--map (all|types|names|funcs)]"
- << " [--dce (all|types|funcs)]"
- << " [--opt (all|loadstore)]"
- << " [--strip-all | --strip all | -s]"
- << " [--do-everything]"
- << " --input | -i file1 [file2...] --output|-o DESTDIR"
- << std::endl;
-
- std::cout << " " << basename(name) << " [--version | -V]" << std::endl;
- std::cout << " " << basename(name) << " [--help | -?]" << std::endl;
-
- exit(5);
- }
-
- // grind through each SPIR in turn
- void execute(const std::vector<std::string>& inputFile, const std::string& outputDir,
- int opts, int verbosity)
- {
- for (const auto& filename : inputFile) {
- std::vector<SpvWord> spv;
- read(spv, filename);
- spv::spirvbin_t(verbosity).remap(spv, opts);
-
- const std::string outfile = outputDir + path_sep_char() + basename(filename);
-
- write(spv, outfile);
- }
-
- if (verbosity > 0)
- std::cout << "Done: " << inputFile.size() << " file(s) processed" << std::endl;
- }
-
- // Parse command line options
- void parseCmdLine(int argc, char** argv, std::vector<std::string>& inputFile,
- std::string& outputDir,
- int& options,
- int& verbosity)
- {
- if (argc < 2)
- usage(argv[0]);
-
- verbosity = 0;
- options = spv::spirvbin_t::Options::NONE;
-
- // Parse command line.
- // boost::program_options would be quite a bit nicer, but we don't want to
- // introduce a dependency on boost.
- for (int a=1; a<argc; ) {
- const std::string arg = argv[a];
-
- if (arg == "--output" || arg == "-o") {
- // Output directory
- if (++a >= argc)
- usage(argv[0], "--output requires an argument");
- if (!outputDir.empty())
- usage(argv[0], "--output can be provided only once");
-
- outputDir = argv[a++];
-
- // Remove trailing directory separator characters
- while (!outputDir.empty() && outputDir.back() == path_sep_char())
- outputDir.pop_back();
-
- }
- else if (arg == "-vv") { verbosity = 2; ++a; } // verbosity shortcuts
- else if (arg == "-vvv") { verbosity = 3; ++a; } // ...
- else if (arg == "-vvvv") { verbosity = 4; ++a; } // ...
- else if (arg == "-vvvvv") { verbosity = 5; ++a; } // ...
-
- else if (arg == "--verbose" || arg == "-v") {
- ++a;
- verbosity = 1;
-
- if (a < argc) {
- try {
- verbosity = std::stoi(argv[a]);
- ++a;
- } catch (const std::invalid_argument&) { } // ok to have no numeric value
- }
- }
- else if (arg == "--version" || arg == "-V") {
- std::cout << basename(argv[0]) << " version 0.97 " << __DATE__ << " " << __TIME__ << std::endl;
- exit(0);
- } else if (arg == "--input" || arg == "-i") {
- // Collect input files
- for (++a; a < argc && argv[a][0] != '-'; ++a)
- inputFile.push_back(argv[a]);
- } else if (arg == "--do-everything") {
- ++a;
- options = options | spv::spirvbin_t::Options::DO_EVERYTHING;
- } else if (arg == "--strip-all" || arg == "-s") {
- ++a;
- options = options | spv::spirvbin_t::Options::STRIP;
- } else if (arg == "--strip") {
- ++a;
- if (strncmp(argv[a], "all", 3) == 0) {
- options = options | spv::spirvbin_t::Options::STRIP;
- ++a;
- }
- } else if (arg == "--dce") {
- // Parse comma (or colon, etc) separated list of things to dce
- ++a;
- for (const char* c = argv[a]; *c; ++c) {
- if (strncmp(c, "all", 3) == 0) {
- options = (options | spv::spirvbin_t::Options::DCE_ALL);
- c += 3;
- } else if (strncmp(c, "*", 1) == 0) {
- options = (options | spv::spirvbin_t::Options::DCE_ALL);
- c += 1;
- } else if (strncmp(c, "funcs", 5) == 0) {
- options = (options | spv::spirvbin_t::Options::DCE_FUNCS);
- c += 5;
- } else if (strncmp(c, "types", 5) == 0) {
- options = (options | spv::spirvbin_t::Options::DCE_TYPES);
- c += 5;
- }
- }
- ++a;
- } else if (arg == "--map") {
- // Parse comma (or colon, etc) separated list of things to map
- ++a;
- for (const char* c = argv[a]; *c; ++c) {
- if (strncmp(c, "all", 3) == 0) {
- options = (options | spv::spirvbin_t::Options::MAP_ALL);
- c += 3;
- } else if (strncmp(c, "*", 1) == 0) {
- options = (options | spv::spirvbin_t::Options::MAP_ALL);
- c += 1;
- } else if (strncmp(c, "types", 5) == 0) {
- options = (options | spv::spirvbin_t::Options::MAP_TYPES);
- c += 5;
- } else if (strncmp(c, "names", 5) == 0) {
- options = (options | spv::spirvbin_t::Options::MAP_NAMES);
- c += 5;
- } else if (strncmp(c, "funcs", 5) == 0) {
- options = (options | spv::spirvbin_t::Options::MAP_FUNCS);
- c += 5;
- }
- }
- ++a;
- } else if (arg == "--opt") {
- ++a;
- for (const char* c = argv[a]; *c; ++c) {
- if (strncmp(c, "all", 3) == 0) {
- options = (options | spv::spirvbin_t::Options::OPT_ALL);
- c += 3;
- } else if (strncmp(c, "*", 1) == 0) {
- options = (options | spv::spirvbin_t::Options::OPT_ALL);
- c += 1;
- } else if (strncmp(c, "loadstore", 9) == 0) {
- options = (options | spv::spirvbin_t::Options::OPT_LOADSTORE);
- c += 9;
- }
- }
- ++a;
- } else if (arg == "--help" || arg == "-?") {
- usage(argv[0]);
- } else {
- usage(argv[0], "Unknown command line option");
- }
- }
- }
-
-} // namespace
-
-
-int main(int argc, char** argv)
-{
-#ifdef use_cpp11
- std::vector<std::string> inputFile;
- std::string outputDir;
- int opts;
- int verbosity;
-
- // handle errors by exiting
- spv::spirvbin_t::registerErrorHandler(errHandler);
-
- // Log messages to std::cout
- spv::spirvbin_t::registerLogHandler(logHandler);
-
- if (argc < 2)
- usage(argv[0]);
-
- parseCmdLine(argc, argv, inputFile, outputDir, opts, verbosity);
-
- if (outputDir.empty())
- usage(argv[0], "Output directory required");
-
- std::string errmsg;
-
- // Main operations: read, remap, and write.
- execute(inputFile, outputDir, opts, verbosity);
-
-#endif
-
- // If we get here, everything went OK! Nothing more to be done.
-}
+#include <iostream>\r
+#include <fstream>\r
+#include <cstring>\r
+#include <stdexcept>\r
+\r
+#include "../SPIRV/SPVRemapper.h"\r
+\r
+namespace {\r
+\r
+ typedef unsigned int SpvWord;\r
+\r
+ // Poor man's basename: given a complete path, return file portion.\r
+ // E.g:\r
+ // Linux: /foo/bar/test -> test\r
+ // Win: c:\foo\bar\test -> test\r
+ // It's not very efficient, but that doesn't matter for our minimal-duty use.\r
+ // Using boost::filesystem would be better in many ways, but want to avoid that dependency.\r
+\r
+ // OS dependent path separator (avoiding boost::filesystem dependency)\r
+#if defined(_WIN32)\r
+ char path_sep_char() { return '\\'; }\r
+#else\r
+ char path_sep_char() { return '/'; }\r
+#endif\r
+\r
+ std::string basename(const std::string filename)\r
+ {\r
+ const size_t sepLoc = filename.find_last_of(path_sep_char());\r
+\r
+ return (sepLoc == filename.npos) ? filename : filename.substr(sepLoc+1);\r
+ }\r
+\r
+ void errHandler(const std::string& str) {\r
+ std::cout << str << std::endl;\r
+ exit(5);\r
+ }\r
+\r
+ void logHandler(const std::string& str) {\r
+ std::cout << str << std::endl;\r
+ }\r
+\r
+ // Read word stream from disk\r
+ void read(std::vector<SpvWord>& spv, const std::string& inFilename, int verbosity)\r
+ {\r
+ std::ifstream fp;\r
+\r
+ if (verbosity > 0)\r
+ logHandler(std::string(" reading: ") + inFilename);\r
+\r
+ spv.clear();\r
+ fp.open(inFilename, std::fstream::in | std::fstream::binary);\r
+\r
+ if (fp.fail())\r
+ errHandler("error opening file for read: ");\r
+\r
+ // Reserve space (for efficiency, not for correctness)\r
+ fp.seekg(0, fp.end);\r
+ spv.reserve(size_t(fp.tellg()) / sizeof(SpvWord));\r
+ fp.seekg(0, fp.beg);\r
+\r
+ while (!fp.eof()) {\r
+ SpvWord inWord;\r
+ fp.read((char *)&inWord, sizeof(inWord));\r
+\r
+ if (!fp.eof()) {\r
+ spv.push_back(inWord);\r
+ if (fp.fail())\r
+ errHandler(std::string("error reading file: ") + inFilename);\r
+ }\r
+ }\r
+ }\r
+\r
+ void write(std::vector<SpvWord>& spv, const std::string& outFile, int verbosity)\r
+ {\r
+ if (outFile.empty())\r
+ errHandler("missing output filename.");\r
+\r
+ std::ofstream fp;\r
+\r
+ if (verbosity > 0)\r
+ logHandler(std::string(" writing: ") + outFile);\r
+\r
+ fp.open(outFile, std::fstream::out | std::fstream::binary);\r
+\r
+ if (fp.fail())\r
+ errHandler(std::string("error opening file for write: ") + outFile);\r
+\r
+ for (auto word : spv) {\r
+ fp.write((char *)&word, sizeof(word));\r
+ if (fp.fail())\r
+ errHandler(std::string("error writing file: ") + outFile);\r
+ }\r
+\r
+ // file is closed by destructor\r
+ }\r
+\r
+ // Print helpful usage message to stdout, and exit\r
+ void usage(const char* const name, const char* const msg = 0)\r
+ {\r
+ if (msg)\r
+ std::cout << msg << std::endl << std::endl;\r
+\r
+ std::cout << "Usage: " << std::endl;\r
+\r
+ std::cout << " " << basename(name)\r
+ << " [-v[v[...]] | --verbose [int]]"\r
+ << " [--map (all|types|names|funcs)]"\r
+ << " [--dce (all|types|funcs)]"\r
+ << " [--opt (all|loadstore)]"\r
+ << " [--strip-all | --strip all | -s]" \r
+ << " [--do-everything]" \r
+ << " --input | -i file1 [file2...] --output|-o DESTDIR"\r
+ << std::endl;\r
+\r
+ std::cout << " " << basename(name) << " [--version | -V]" << std::endl;\r
+ std::cout << " " << basename(name) << " [--help | -?]" << std::endl;\r
+\r
+ exit(5);\r
+ }\r
+\r
+ // grind through each SPIR in turn\r
+ void execute(const std::vector<std::string>& inputFile, const std::string& outputDir,\r
+ int opts, int verbosity)\r
+ {\r
+ for (const auto& filename : inputFile) {\r
+ std::vector<SpvWord> spv;\r
+ read(spv, filename, verbosity);\r
+ spv::spirvbin_t(verbosity).remap(spv, opts);\r
+\r
+ const std::string outfile = outputDir + path_sep_char() + basename(filename);\r
+\r
+ write(spv, outfile, verbosity);\r
+ }\r
+\r
+ if (verbosity > 0)\r
+ std::cout << "Done: " << inputFile.size() << " file(s) processed" << std::endl;\r
+ }\r
+\r
+ // Parse command line options\r
+ void parseCmdLine(int argc, char** argv, std::vector<std::string>& inputFile,\r
+ std::string& outputDir,\r
+ int& options,\r
+ int& verbosity)\r
+ {\r
+ if (argc < 2)\r
+ usage(argv[0]);\r
+\r
+ verbosity = 0;\r
+ options = spv::spirvbin_t::Options::NONE;\r
+\r
+ // Parse command line.\r
+ // boost::program_options would be quite a bit nicer, but we don't want to\r
+ // introduce a dependency on boost.\r
+ for (int a=1; a<argc; ) {\r
+ const std::string arg = argv[a];\r
+\r
+ if (arg == "--output" || arg == "-o") {\r
+ // Output directory\r
+ if (++a >= argc)\r
+ usage(argv[0], "--output requires an argument");\r
+ if (!outputDir.empty())\r
+ usage(argv[0], "--output can be provided only once");\r
+\r
+ outputDir = argv[a++];\r
+\r
+ // Remove trailing directory separator characters\r
+ while (!outputDir.empty() && outputDir.back() == path_sep_char())\r
+ outputDir.pop_back();\r
+\r
+ }\r
+ else if (arg == "-vv") { verbosity = 2; ++a; } // verbosity shortcuts\r
+ else if (arg == "-vvv") { verbosity = 3; ++a; } // ...\r
+ else if (arg == "-vvvv") { verbosity = 4; ++a; } // ...\r
+ else if (arg == "-vvvvv") { verbosity = 5; ++a; } // ...\r
+\r
+ else if (arg == "--verbose" || arg == "-v") {\r
+ ++a;\r
+ verbosity = 1;\r
+\r
+ if (a < argc) {\r
+ try {\r
+ verbosity = std::stoi(argv[a]);\r
+ ++a;\r
+ } catch (const std::invalid_argument&) { } // ok to have no numeric value\r
+ }\r
+ }\r
+ else if (arg == "--version" || arg == "-V") {\r
+ std::cout << basename(argv[0]) << " version 0.97 " << __DATE__ << " " << __TIME__ << std::endl;\r
+ exit(0);\r
+ } else if (arg == "--input" || arg == "-i") {\r
+ // Collect input files\r
+ for (++a; a < argc && argv[a][0] != '-'; ++a)\r
+ inputFile.push_back(argv[a]);\r
+ } else if (arg == "--do-everything") {\r
+ ++a;\r
+ options = options | spv::spirvbin_t::Options::DO_EVERYTHING;\r
+ } else if (arg == "--strip-all" || arg == "-s") {\r
+ ++a;\r
+ options = options | spv::spirvbin_t::Options::STRIP;\r
+ } else if (arg == "--strip") {\r
+ ++a;\r
+ if (strncmp(argv[a], "all", 3) == 0) {\r
+ options = options | spv::spirvbin_t::Options::STRIP;\r
+ ++a;\r
+ }\r
+ } else if (arg == "--dce") {\r
+ // Parse comma (or colon, etc) separated list of things to dce\r
+ ++a;\r
+ for (const char* c = argv[a]; *c; ++c) {\r
+ if (strncmp(c, "all", 3) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::DCE_ALL);\r
+ c += 3;\r
+ } else if (strncmp(c, "*", 1) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::DCE_ALL);\r
+ c += 1;\r
+ } else if (strncmp(c, "funcs", 5) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::DCE_FUNCS);\r
+ c += 5;\r
+ } else if (strncmp(c, "types", 5) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::DCE_TYPES);\r
+ c += 5;\r
+ }\r
+ }\r
+ ++a;\r
+ } else if (arg == "--map") {\r
+ // Parse comma (or colon, etc) separated list of things to map\r
+ ++a;\r
+ for (const char* c = argv[a]; *c; ++c) {\r
+ if (strncmp(c, "all", 3) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::MAP_ALL);\r
+ c += 3;\r
+ } else if (strncmp(c, "*", 1) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::MAP_ALL);\r
+ c += 1;\r
+ } else if (strncmp(c, "types", 5) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::MAP_TYPES);\r
+ c += 5;\r
+ } else if (strncmp(c, "names", 5) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::MAP_NAMES);\r
+ c += 5;\r
+ } else if (strncmp(c, "funcs", 5) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::MAP_FUNCS);\r
+ c += 5;\r
+ }\r
+ }\r
+ ++a;\r
+ } else if (arg == "--opt") {\r
+ ++a;\r
+ for (const char* c = argv[a]; *c; ++c) {\r
+ if (strncmp(c, "all", 3) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::OPT_ALL);\r
+ c += 3;\r
+ } else if (strncmp(c, "*", 1) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::OPT_ALL);\r
+ c += 1;\r
+ } else if (strncmp(c, "loadstore", 9) == 0) {\r
+ options = (options | spv::spirvbin_t::Options::OPT_LOADSTORE);\r
+ c += 9;\r
+ }\r
+ }\r
+ ++a;\r
+ } else if (arg == "--help" || arg == "-?") {\r
+ usage(argv[0]);\r
+ } else {\r
+ usage(argv[0], "Unknown command line option");\r
+ }\r
+ }\r
+ }\r
+\r
+} // namespace\r
+\r
+\r
+int main(int argc, char** argv)\r
+{\r
+ std::vector<std::string> inputFile;\r
+ std::string outputDir;\r
+ int opts;\r
+ int verbosity;\r
+\r
+#ifdef use_cpp11\r
+ // handle errors by exiting\r
+ spv::spirvbin_t::registerErrorHandler(errHandler);\r
+\r
+ // Log messages to std::cout\r
+ spv::spirvbin_t::registerLogHandler(logHandler);\r
+#endif\r
+\r
+ if (argc < 2)\r
+ usage(argv[0]);\r
+\r
+ parseCmdLine(argc, argv, inputFile, outputDir, opts, verbosity);\r
+\r
+ if (outputDir.empty())\r
+ usage(argv[0], "Output directory required");\r
+\r
+ std::string errmsg;\r
+\r
+ // Main operations: read, remap, and write.\r
+ execute(inputFile, outputDir, opts, verbosity);\r
+\r
+ // If we get here, everything went OK! Nothing more to be done.\r
+}\r