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