RetroArch
SPVRemapper.h
Go to the documentation of this file.
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 <cstdlib>
42 #include <exception>
43 
44 namespace spv {
45 
46 // MSVC defines __cplusplus as an older value, even when it supports almost all of 11.
47 // We handle that here by making our own symbol.
48 #if __cplusplus >= 201103L || _MSC_VER >= 1700
49 # define use_cpp11 1
50 #endif
51 
53 {
54 public:
55  enum Options {
56  NONE = 0,
57  STRIP = (1<<0),
58  MAP_TYPES = (1<<1),
59  MAP_NAMES = (1<<2),
60  MAP_FUNCS = (1<<3),
61  DCE_FUNCS = (1<<4),
62  DCE_VARS = (1<<5),
63  DCE_TYPES = (1<<6),
64  OPT_LOADSTORE = (1<<7),
65  OPT_FWD_LS = (1<<8), // EXPERIMENTAL: PRODUCES INVALID SCHEMA-0 SPIRV
69 
72  };
73 };
74 
75 } // namespace SPV
76 
77 #if !defined (use_cpp11)
78 #include <cstdio>
79 #include <cstdint>
80 
81 namespace spv {
83 {
84 public:
85  spirvbin_t(int /*verbose = 0*/) { }
86 
87  void remap(std::vector<std::uint32_t>& /*spv*/, unsigned int /*opts = 0*/)
88  {
89  printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n");
90  exit(5);
91  }
92 };
93 
94 } // namespace SPV
95 
96 #else // defined (use_cpp11)
97 
98 #include <functional>
99 #include <cstdint>
100 #include <unordered_map>
101 #include <unordered_set>
102 #include <map>
103 #include <set>
104 #include <cassert>
105 
106 #include "spirv.hpp"
107 #include "spvIR.h"
108 
109 namespace spv {
110 
111 // class to hold SPIR-V binary data for remapping, DCE, and debug stripping
112 class spirvbin_t : public spirvbin_base_t
113 {
114 public:
115  spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose), errorLatch(false)
116  { }
117 
118  virtual ~spirvbin_t() { }
119 
120  // remap on an existing binary in memory
121  void remap(std::vector<std::uint32_t>& spv, std::uint32_t opts = DO_EVERYTHING);
122 
123  // Type for error/log handler functions
124  typedef std::function<void(const std::string&)> errorfn_t;
125  typedef std::function<void(const std::string&)> logfn_t;
126 
127  // Register error/log handling functions (can be lambda fn / functor / etc)
128  static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; }
129  static void registerLogHandler(logfn_t handler) { logHandler = handler; }
130 
131 protected:
132  // This can be overridden to provide other message behavior if needed
133  virtual void msg(int minVerbosity, int indent, const std::string& txt) const;
134 
135 private:
136  // Local to global, or global to local ID map
137  typedef std::unordered_map<spv::Id, spv::Id> idmap_t;
138  typedef std::unordered_set<spv::Id> idset_t;
139  typedef std::unordered_map<spv::Id, int> blockmap_t;
140 
141  void remap(std::uint32_t opts = DO_EVERYTHING);
142 
143  // Map of names to IDs
144  typedef std::unordered_map<std::string, spv::Id> namemap_t;
145 
146  typedef std::uint32_t spirword_t;
147 
148  typedef std::pair<unsigned, unsigned> range_t;
149  typedef std::function<void(spv::Id&)> idfn_t;
150  typedef std::function<bool(spv::Op, unsigned start)> instfn_t;
151 
152  // Special Values for ID map:
153  static const spv::Id unmapped; // unchanged from default value
154  static const spv::Id unused; // unused ID
155  static const int header_size; // SPIR header = 5 words
156 
157  class id_iterator_t;
158 
159  // For mapping type entries between different shaders
160  typedef std::vector<spirword_t> typeentry_t;
161  typedef std::map<spv::Id, typeentry_t> globaltypes_t;
162 
163  // A set that preserves position order, and a reverse map
164  typedef std::set<int> posmap_t;
165  typedef std::unordered_map<spv::Id, int> posmap_rev_t;
166 
167  // Maps and ID to the size of its base type, if known.
168  typedef std::unordered_map<spv::Id, unsigned> typesize_map_t;
169 
170  // handle error
171  void error(const std::string& txt) const { errorLatch = true; errorHandler(txt); }
172 
173  bool isConstOp(spv::Op opCode) const;
174  bool isTypeOp(spv::Op opCode) const;
175  bool isStripOp(spv::Op opCode) const;
176  bool isFlowCtrl(spv::Op opCode) const;
177  range_t literalRange(spv::Op opCode) const;
178  range_t typeRange(spv::Op opCode) const;
179  range_t constRange(spv::Op opCode) const;
180  unsigned typeSizeInWords(spv::Id id) const;
181  unsigned idTypeSizeInWords(spv::Id id) const;
182 
183  spv::Id& asId(unsigned word) { return spv[word]; }
184  const spv::Id& asId(unsigned word) const { return spv[word]; }
185  spv::Op asOpCode(unsigned word) const { return opOpCode(spv[word]); }
186  std::uint32_t asOpCodeHash(unsigned word);
187  spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); }
188  unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); }
189  spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); }
190  unsigned idPos(spv::Id id) const;
191 
192  static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; }
193  static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); }
194 
195  // Header access & set methods
196  spirword_t magic() const { return spv[0]; } // return magic number
197  spirword_t bound() const { return spv[3]; } // return Id bound from header
198  spirword_t bound(spirword_t b) { return spv[3] = b; };
199  spirword_t genmagic() const { return spv[2]; } // generator magic
200  spirword_t genmagic(spirword_t m) { return spv[2] = m; }
201  spirword_t schemaNum() const { return spv[4]; } // schema number from header
202 
203  // Mapping fns: get
204  spv::Id localId(spv::Id id) const { return idMapL[id]; }
205 
206  // Mapping fns: set
207  inline spv::Id localId(spv::Id id, spv::Id newId);
208  void countIds(spv::Id id);
209 
210  // Return next unused new local ID.
211  // NOTE: boost::dynamic_bitset would be more efficient due to find_next(),
212  // which std::vector<bool> doens't have.
213  inline spv::Id nextUnusedId(spv::Id id);
214 
215  void buildLocalMaps();
216  std::string literalString(unsigned word) const; // Return literal as a std::string
217  int literalStringWords(const std::string& str) const { return (int(str.size())+4)/4; }
218 
219  bool isNewIdMapped(spv::Id newId) const { return isMapped(newId); }
220  bool isOldIdUnmapped(spv::Id oldId) const { return localId(oldId) == unmapped; }
221  bool isOldIdUnused(spv::Id oldId) const { return localId(oldId) == unused; }
222  bool isOldIdMapped(spv::Id oldId) const { return !isOldIdUnused(oldId) && !isOldIdUnmapped(oldId); }
223  bool isFunction(spv::Id oldId) const { return fnPos.find(oldId) != fnPos.end(); }
224 
225  // bool matchType(const globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const;
226  // spv::Id findType(const globaltypes_t& globalTypes, spv::Id lt) const;
227  std::uint32_t hashType(unsigned typeStart) const;
228 
229  spirvbin_t& process(instfn_t, idfn_t, unsigned begin = 0, unsigned end = 0);
230  int processInstruction(unsigned word, instfn_t, idfn_t);
231 
232  void validate() const;
233  void mapTypeConst();
234  void mapFnBodies();
235  void optLoadStore();
236  void dceFuncs();
237  void dceVars();
238  void dceTypes();
239  void mapNames();
240  void foldIds(); // fold IDs to smallest space
241  void forwardLoadStores(); // load store forwarding (EXPERIMENTAL)
242  void offsetIds(); // create relative offset IDs
243 
244  void applyMap(); // remap per local name map
245  void mapRemainder(); // map any IDs we haven't touched yet
246  void stripDebug(); // strip all debug info
247  void stripDeadRefs(); // strips debug info for now-dead references after DCE
248  void strip(); // remove debug symbols
249 
250  std::vector<spirword_t> spv; // SPIR words
251 
252  namemap_t nameMap; // ID names from OpName
253 
254  // Since we want to also do binary ops, we can't use std::vector<bool>. we could use
255  // boost::dynamic_bitset, but we're trying to avoid a boost dependency.
256  typedef std::uint64_t bits_t;
257  std::vector<bits_t> mapped; // which new IDs have been mapped
258  static const int mBits = sizeof(bits_t) * 4;
259 
260  bool isMapped(spv::Id id) const { return id < maxMappedId() && ((mapped[id/mBits] & (1LL<<(id%mBits))) != 0); }
261  void setMapped(spv::Id id) { resizeMapped(id); mapped[id/mBits] |= (1LL<<(id%mBits)); }
262  void resizeMapped(spv::Id id) { if (id >= maxMappedId()) mapped.resize(id/mBits+1, 0); }
263  size_t maxMappedId() const { return mapped.size() * mBits; }
264 
265  // Add a strip range for a given instruction starting at 'start'
266  // Note: avoiding brace initializers to please older versions os MSVC.
267  void stripInst(unsigned start) { stripRange.push_back(range_t(start, start + asWordCount(start))); }
268 
269  // Function start and end. use unordered_map because we'll have
270  // many fewer functions than IDs.
271  std::unordered_map<spv::Id, range_t> fnPos;
272 
273  // Which functions are called, anywhere in the module, with a call count
274  std::unordered_map<spv::Id, int> fnCalls;
275 
276  posmap_t typeConstPos; // word positions that define types & consts (ordered)
277  posmap_rev_t idPosR; // reverse map from IDs to positions
278  typesize_map_t idTypeSizeMap; // maps each ID to its type size, if known.
279 
280  std::vector<spv::Id> idMapL; // ID {M}ap from {L}ocal to {G}lobal IDs
281 
282  spv::Id entryPoint; // module entry point
283  spv::Id largestNewId; // biggest new ID we have mapped anything to
284 
285  // Sections of the binary to strip, given as [begin,end)
286  std::vector<range_t> stripRange;
287 
288  // processing options:
290  int verbose; // verbosity level
291 
292  // Error latch: this is set if the error handler is ever executed. It would be better to
293  // use a try/catch block and throw, but that's not desired for certain environments, so
294  // this is the alternative.
295  mutable bool errorLatch;
296 
297  static errorfn_t errorHandler;
298  static logfn_t logHandler;
299 };
300 
301 } // namespace SPV
302 
303 #endif // defined (use_cpp11)
304 #endif // SPIRVREMAPPER_H
unsigned int Id
Definition: spirv.hpp:47
Definition: SPVRemapper.h:65
Definition: SPVRemapper.h:63
GLenum GLuint id
Definition: glext.h:6233
Definition: SPVRemapper.h:64
GLuint start
Definition: glext.h:6292
Decoration
Definition: spirv.hpp:344
const char *const str
Definition: portlistingparse.c:18
static l_noret error(LoadState *S, const char *why)
Definition: lundump.c:39
Op
Definition: spirv.hpp:714
Definition: SPVRemapper.h:60
GLsizei const GLchar *const * string
Definition: glext.h:6699
Definition: disassemble.cpp:50
static const unsigned int WordCountShift
Definition: spirv.hpp:56
Definition: SPVRemapper.h:70
static const unsigned char msg[]
Definition: ccm.c:375
Definition: SPVRemapper.h:62
Definition: SPVRemapper.h:58
Definition: SPVRemapper.h:82
Definition: SPVRemapper.h:61
JSON_Parser_EncodingDetectedHandler handler
Definition: jsonsax_full.h:561
#define LL(x)
Definition: lobject.c:479
unsigned __int64 uint64_t
Definition: stdint.h:136
void remap(std::vector< std::uint32_t > &, unsigned int)
Definition: SPVRemapper.h:87
Definition: SPVRemapper.h:52
const Id NoResult
Definition: spvIR.h:64
Options
Definition: SPVRemapper.h:55
Definition: SPVRemapper.h:66
GLuint GLuint end
Definition: glext.h:6292
Definition: SPVRemapper.h:56
unsigned int uint32_t
Definition: stdint.h:126
Definition: SPVRemapper.h:57
Definition: SPVRemapper.h:59
const GLfloat * m
Definition: glext.h:11755
Definition: ibxm.h:9
spirvbin_t(int)
Definition: SPVRemapper.h:85
#define false
Definition: ordinals.h:83
bf_uint8_t options
Definition: connect_ps4.c:78
const char * entryPoint
Definition: Hlsl.FromFile.cpp:45
typedef void(__stdcall *PFN_DESTRUCTION_CALLBACK)(void *pData)
Definition: SPVRemapper.h:71
static const unsigned int OpCodeMask
Definition: spirv.hpp:55
typedef bool(RETRO_CALLCONV *retro_replace_image_index_t)(unsigned index
GLboolean GLboolean GLboolean b
Definition: glext.h:6844
Definition: SPVRemapper.h:67
Definition: SPVRemapper.h:68