John Kessenich | 140f3df | 2015-06-26 16:58:36 -0600 | [diff] [blame] | 1 | // |
| 2 | //Copyright (C) 2015 LunarG, Inc. |
| 3 | // |
| 4 | //All rights reserved. |
| 5 | // |
| 6 | //Redistribution and use in source and binary forms, with or without |
| 7 | //modification, are permitted provided that the following conditions |
| 8 | //are met: |
| 9 | // |
| 10 | // Redistributions of source code must retain the above copyright |
| 11 | // notice, this list of conditions and the following disclaimer. |
| 12 | // |
| 13 | // Redistributions in binary form must reproduce the above |
| 14 | // copyright notice, this list of conditions and the following |
| 15 | // disclaimer in the documentation and/or other materials provided |
| 16 | // with the distribution. |
| 17 | // |
| 18 | // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
| 19 | // contributors may be used to endorse or promote products derived |
| 20 | // from this software without specific prior written permission. |
| 21 | // |
| 22 | //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 23 | //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 24 | //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| 25 | //FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| 26 | //COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 27 | //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| 28 | //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 29 | //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 30 | //CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 31 | //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| 32 | //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 33 | //POSSIBILITY OF SUCH DAMAGE. |
| 34 | // |
| 35 | |
| 36 | #ifndef SPIRVREMAPPER_H |
| 37 | #define SPIRVREMAPPER_H |
| 38 | |
| 39 | #include <string> |
| 40 | #include <vector> |
| 41 | #include <stdlib.h> |
| 42 | |
| 43 | namespace spv { |
| 44 | |
| 45 | // MSVC defines __cplusplus as an older value, even when it supports almost all of 11. |
| 46 | // We handle that here by making our own symbol. |
| 47 | #if __cplusplus >= 201103L || _MSC_VER >= 1700 |
| 48 | # define use_cpp11 1 |
| 49 | #endif |
| 50 | |
| 51 | class spirvbin_base_t |
| 52 | { |
| 53 | public: |
| 54 | enum Options { |
| 55 | NONE = 0, |
| 56 | STRIP = (1<<0), |
| 57 | MAP_TYPES = (1<<1), |
| 58 | MAP_NAMES = (1<<2), |
| 59 | MAP_FUNCS = (1<<3), |
| 60 | DCE_FUNCS = (1<<4), |
| 61 | DCE_VARS = (1<<5), |
| 62 | DCE_TYPES = (1<<6), |
| 63 | OPT_LOADSTORE = (1<<7), |
| 64 | OPT_FWD_LS = (1<<8), // EXPERIMENTAL: PRODUCES INVALID SCHEMA-0 SPIRV |
| 65 | MAP_ALL = (MAP_TYPES | MAP_NAMES | MAP_FUNCS), |
| 66 | DCE_ALL = (DCE_FUNCS | DCE_VARS | DCE_TYPES), |
| 67 | OPT_ALL = (OPT_LOADSTORE), |
| 68 | |
| 69 | ALL_BUT_STRIP = (MAP_ALL | DCE_ALL | OPT_ALL), |
| 70 | DO_EVERYTHING = (STRIP | ALL_BUT_STRIP) |
| 71 | }; |
| 72 | }; |
| 73 | |
| 74 | } // namespace SPV |
| 75 | |
| 76 | #if !defined (use_cpp11) |
| 77 | #include <stdio.h> |
| 78 | |
| 79 | namespace spv { |
| 80 | class spirvbin_t : public spirvbin_base_t |
| 81 | { |
| 82 | public: |
| 83 | spirvbin_t(int /*verbose = 0*/) { } |
| 84 | |
| 85 | void remap(std::vector<unsigned int>& /*spv*/, unsigned int /*opts = 0*/) |
| 86 | { |
| 87 | printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n"); |
| 88 | exit(5); |
| 89 | } |
| 90 | }; |
| 91 | |
| 92 | } // namespace SPV |
| 93 | |
| 94 | #else // defined (use_cpp11) |
| 95 | |
| 96 | #include <functional> |
| 97 | #include <cstdint> |
| 98 | #include <unordered_map> |
| 99 | #include <unordered_set> |
| 100 | #include <map> |
| 101 | #include <set> |
| 102 | #include <cassert> |
| 103 | |
John Kessenich | 5e4b124 | 2015-08-06 22:53:06 -0600 | [diff] [blame] | 104 | #include "spirv.hpp" |
Felix Kaaman | 61deffd | 2015-07-02 15:25:23 +0200 | [diff] [blame] | 105 | #include "spvIR.h" |
John Kessenich | 140f3df | 2015-06-26 16:58:36 -0600 | [diff] [blame] | 106 | |
| 107 | namespace spv { |
| 108 | |
| 109 | // class to hold SPIR-V binary data for remapping, DCE, and debug stripping |
| 110 | class spirvbin_t : public spirvbin_base_t |
| 111 | { |
| 112 | public: |
| 113 | spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose) { } |
| 114 | |
| 115 | // remap on an existing binary in memory |
| 116 | void remap(std::vector<std::uint32_t>& spv, std::uint32_t opts = DO_EVERYTHING); |
| 117 | |
| 118 | // Type for error/log handler functions |
| 119 | typedef std::function<void(const std::string&)> errorfn_t; |
| 120 | typedef std::function<void(const std::string&)> logfn_t; |
| 121 | |
| 122 | // Register error/log handling functions (can be lambda fn / functor / etc) |
| 123 | static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; } |
| 124 | static void registerLogHandler(logfn_t handler) { logHandler = handler; } |
| 125 | |
| 126 | protected: |
| 127 | // This can be overridden to provide other message behavior if needed |
| 128 | virtual void msg(int minVerbosity, int indent, const std::string& txt) const; |
| 129 | |
| 130 | private: |
| 131 | // Local to global, or global to local ID map |
| 132 | typedef std::unordered_map<spv::Id, spv::Id> idmap_t; |
| 133 | typedef std::unordered_set<spv::Id> idset_t; |
GregF | 8548bab | 2016-02-01 16:44:57 -0700 | [diff] [blame] | 134 | typedef std::unordered_map<spv::Id, int> blockmap_t; |
John Kessenich | 140f3df | 2015-06-26 16:58:36 -0600 | [diff] [blame] | 135 | |
| 136 | void remap(std::uint32_t opts = DO_EVERYTHING); |
| 137 | |
| 138 | // Map of names to IDs |
| 139 | typedef std::unordered_map<std::string, spv::Id> namemap_t; |
| 140 | |
| 141 | typedef std::uint32_t spirword_t; |
| 142 | |
| 143 | typedef std::pair<unsigned, unsigned> range_t; |
| 144 | typedef std::function<void(spv::Id&)> idfn_t; |
| 145 | typedef std::function<bool(spv::Op, unsigned start)> instfn_t; |
| 146 | |
| 147 | // Special Values for ID map: |
| 148 | static const spv::Id unmapped; // unchanged from default value |
| 149 | static const spv::Id unused; // unused ID |
| 150 | static const int header_size; // SPIR header = 5 words |
| 151 | |
| 152 | class id_iterator_t; |
| 153 | |
| 154 | // For mapping type entries between different shaders |
| 155 | typedef std::vector<spirword_t> typeentry_t; |
| 156 | typedef std::map<spv::Id, typeentry_t> globaltypes_t; |
| 157 | |
| 158 | // A set that preserves position order, and a reverse map |
| 159 | typedef std::set<int> posmap_t; |
| 160 | typedef std::unordered_map<spv::Id, int> posmap_rev_t; |
| 161 | |
| 162 | // handle error |
| 163 | void error(const std::string& txt) const { errorHandler(txt); } |
| 164 | |
| 165 | bool isConstOp(spv::Op opCode) const; |
| 166 | bool isTypeOp(spv::Op opCode) const; |
| 167 | bool isStripOp(spv::Op opCode) const; |
GregF | 8548bab | 2016-02-01 16:44:57 -0700 | [diff] [blame] | 168 | bool isFlowCtrl(spv::Op opCode) const; |
John Kessenich | 140f3df | 2015-06-26 16:58:36 -0600 | [diff] [blame] | 169 | range_t literalRange(spv::Op opCode) const; |
| 170 | range_t typeRange(spv::Op opCode) const; |
| 171 | range_t constRange(spv::Op opCode) const; |
| 172 | |
| 173 | spv::Id& asId(unsigned word) { return spv[word]; } |
| 174 | const spv::Id& asId(unsigned word) const { return spv[word]; } |
| 175 | spv::Op asOpCode(unsigned word) const { return opOpCode(spv[word]); } |
| 176 | std::uint32_t asOpCodeHash(unsigned word); |
| 177 | spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); } |
| 178 | unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); } |
| 179 | spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); } |
| 180 | unsigned typePos(spv::Id id) const; |
| 181 | |
| 182 | static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; } |
| 183 | static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); } |
| 184 | |
| 185 | // Header access & set methods |
| 186 | spirword_t magic() const { return spv[0]; } // return magic number |
| 187 | spirword_t bound() const { return spv[3]; } // return Id bound from header |
| 188 | spirword_t bound(spirword_t b) { return spv[3] = b; }; |
| 189 | spirword_t genmagic() const { return spv[2]; } // generator magic |
| 190 | spirword_t genmagic(spirword_t m) { return spv[2] = m; } |
| 191 | spirword_t schemaNum() const { return spv[4]; } // schema number from header |
| 192 | |
| 193 | // Mapping fns: get |
| 194 | spv::Id localId(spv::Id id) const { return idMapL[id]; } |
| 195 | |
| 196 | // Mapping fns: set |
| 197 | inline spv::Id localId(spv::Id id, spv::Id newId); |
| 198 | void countIds(spv::Id id); |
| 199 | |
| 200 | // Return next unused new local ID. |
| 201 | // NOTE: boost::dynamic_bitset would be more efficient due to find_next(), |
| 202 | // which std::vector<bool> doens't have. |
| 203 | inline spv::Id nextUnusedId(spv::Id id); |
| 204 | |
| 205 | void buildLocalMaps(); |
| 206 | std::string literalString(unsigned word) const; // Return literal as a std::string |
| 207 | int literalStringWords(const std::string& str) const { return (int(str.size())+4)/4; } |
| 208 | |
| 209 | bool isNewIdMapped(spv::Id newId) const { return isMapped(newId); } |
| 210 | bool isOldIdUnmapped(spv::Id oldId) const { return localId(oldId) == unmapped; } |
| 211 | bool isOldIdUnused(spv::Id oldId) const { return localId(oldId) == unused; } |
| 212 | bool isOldIdMapped(spv::Id oldId) const { return !isOldIdUnused(oldId) && !isOldIdUnmapped(oldId); } |
| 213 | bool isFunction(spv::Id oldId) const { return fnPos.find(oldId) != fnPos.end(); } |
| 214 | |
| 215 | // bool matchType(const globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const; |
| 216 | // spv::Id findType(const globaltypes_t& globalTypes, spv::Id lt) const; |
| 217 | std::uint32_t hashType(unsigned typeStart) const; |
| 218 | |
| 219 | spirvbin_t& process(instfn_t, idfn_t, unsigned begin = 0, unsigned end = 0); |
| 220 | int processInstruction(unsigned word, instfn_t, idfn_t); |
| 221 | |
| 222 | void validate() const; |
| 223 | void mapTypeConst(); |
| 224 | void mapFnBodies(); |
| 225 | void optLoadStore(); |
| 226 | void dceFuncs(); |
| 227 | void dceVars(); |
| 228 | void dceTypes(); |
| 229 | void mapNames(); |
| 230 | void foldIds(); // fold IDs to smallest space |
| 231 | void forwardLoadStores(); // load store forwarding (EXPERIMENTAL) |
| 232 | void offsetIds(); // create relative offset IDs |
| 233 | |
| 234 | void applyMap(); // remap per local name map |
| 235 | void mapRemainder(); // map any IDs we haven't touched yet |
| 236 | void stripDebug(); // strip debug info |
| 237 | void strip(); // remove debug symbols |
| 238 | |
| 239 | std::vector<spirword_t> spv; // SPIR words |
| 240 | |
| 241 | namemap_t nameMap; // ID names from OpName |
| 242 | |
| 243 | // Since we want to also do binary ops, we can't use std::vector<bool>. we could use |
| 244 | // boost::dynamic_bitset, but we're trying to avoid a boost dependency. |
| 245 | typedef std::uint64_t bits_t; |
| 246 | std::vector<bits_t> mapped; // which new IDs have been mapped |
| 247 | static const int mBits = sizeof(bits_t) * 4; |
| 248 | |
| 249 | bool isMapped(spv::Id id) const { return id < maxMappedId() && ((mapped[id/mBits] & (1LL<<(id%mBits))) != 0); } |
| 250 | void setMapped(spv::Id id) { resizeMapped(id); mapped[id/mBits] |= (1LL<<(id%mBits)); } |
| 251 | void resizeMapped(spv::Id id) { if (id >= maxMappedId()) mapped.resize(id/mBits+1, 0); } |
| 252 | size_t maxMappedId() const { return mapped.size() * mBits; } |
| 253 | |
| 254 | // Add a strip range for a given instruction starting at 'start' |
| 255 | // Note: avoiding brace initializers to please older versions os MSVC. |
| 256 | void stripInst(unsigned start) { stripRange.push_back(range_t(start, start + asWordCount(start))); } |
| 257 | |
| 258 | // Function start and end. use unordered_map because we'll have |
| 259 | // many fewer functions than IDs. |
| 260 | std::unordered_map<spv::Id, range_t> fnPos; |
| 261 | std::unordered_map<spv::Id, range_t> fnPosDCE; // deleted functions |
| 262 | |
| 263 | // Which functions are called, anywhere in the module, with a call count |
| 264 | std::unordered_map<spv::Id, int> fnCalls; |
| 265 | |
| 266 | posmap_t typeConstPos; // word positions that define types & consts (ordered) |
| 267 | posmap_rev_t typeConstPosR; // reverse map from IDs to positions |
| 268 | |
| 269 | std::vector<spv::Id> idMapL; // ID {M}ap from {L}ocal to {G}lobal IDs |
| 270 | |
| 271 | spv::Id entryPoint; // module entry point |
| 272 | spv::Id largestNewId; // biggest new ID we have mapped anything to |
| 273 | |
| 274 | // Sections of the binary to strip, given as [begin,end) |
| 275 | std::vector<range_t> stripRange; |
| 276 | |
| 277 | // processing options: |
| 278 | std::uint32_t options; |
| 279 | int verbose; // verbosity level |
| 280 | |
| 281 | static errorfn_t errorHandler; |
| 282 | static logfn_t logHandler; |
| 283 | }; |
| 284 | |
| 285 | } // namespace SPV |
| 286 | |
| 287 | #endif // defined (use_cpp11) |
| 288 | #endif // SPIRVREMAPPER_H |