// // Copyright (C) 2015 LunarG, Inc. // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // // Neither the name of 3Dlabs Inc. Ltd. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // #include "SPVRemapper.h" #include "doc.h" #include <algorithm> #include <cassert> #include "../glslang/Include/Common.h" namespace spv { // By default, just abort on error. Can be overridden via RegisterErrorHandler spirvbin_t::errorfn_t spirvbin_t::errorHandler = …; // By default, eat log messages. Can be overridden via RegisterLogHandler spirvbin_t::logfn_t spirvbin_t::logHandler = …; // This can be overridden to provide other message behavior if needed void spirvbin_t::msg(int minVerbosity, int indent, const std::string& txt) const { … } // hash opcode, with special handling for OpExtInst std::uint32_t spirvbin_t::asOpCodeHash(unsigned word) { … } spirvbin_t::range_t spirvbin_t::literalRange(spv::Op opCode) const { … } spirvbin_t::range_t spirvbin_t::typeRange(spv::Op opCode) const { … } spirvbin_t::range_t spirvbin_t::constRange(spv::Op opCode) const { … } // Return the size of a type in 32-bit words. This currently only // handles ints and floats, and is only invoked by queries which must be // integer types. If ever needed, it can be generalized. unsigned spirvbin_t::typeSizeInWords(spv::Id id) const { … } // Looks up the type of a given const or variable ID, and // returns its size in 32-bit words. unsigned spirvbin_t::idTypeSizeInWords(spv::Id id) const { … } // Is this an opcode we should remove when using --strip? bool spirvbin_t::isStripOp(spv::Op opCode, unsigned start) const { … } // Return true if this opcode is flow control bool spirvbin_t::isFlowCtrl(spv::Op opCode) const { … } // Return true if this opcode defines a type bool spirvbin_t::isTypeOp(spv::Op opCode) const { … } // Return true if this opcode defines a constant bool spirvbin_t::isConstOp(spv::Op opCode) const { … } const auto inst_fn_nop = …; const auto op_fn_nop = …; // g++ doesn't like these defined in the class proper in an anonymous namespace. // Dunno why. Also MSVC doesn't like the constexpr keyword. Also dunno why. // Defining them externally seems to please both compilers, so, here they are. const spv::Id spirvbin_t::unmapped = …; const spv::Id spirvbin_t::unused = …; const int spirvbin_t::header_size = …; spv::Id spirvbin_t::nextUnusedId(spv::Id id) { … } spv::Id spirvbin_t::localId(spv::Id id, spv::Id newId) { … } // Parse a literal string from the SPIR binary and return it as an std::string // Due to C++11 RValue references, this doesn't copy the result string. std::string spirvbin_t::literalString(unsigned word) const { … } void spirvbin_t::applyMap() { … } // Find free IDs for anything we haven't mapped void spirvbin_t::mapRemainder() { … } // Mark debug instructions for stripping void spirvbin_t::stripDebug() { … } // Mark instructions that refer to now-removed IDs for stripping void spirvbin_t::stripDeadRefs() { … } // Update local maps of ID, type, etc positions void spirvbin_t::buildLocalMaps() { … } // Validate the SPIR header void spirvbin_t::validate() const { … } int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn) { … } // Make a pass over all the instructions and process them given appropriate functions spirvbin_t& spirvbin_t::process(instfn_t instFn, idfn_t idFn, unsigned begin, unsigned end) { … } // Apply global name mapping to a single module void spirvbin_t::mapNames() { … } // Map fn contents to IDs of similar functions in other modules void spirvbin_t::mapFnBodies() { … } // EXPERIMENTAL: forward IO and uniform load/stores into operands // This produces invalid Schema-0 SPIRV void spirvbin_t::forwardLoadStores() { … } // optimize loads and stores void spirvbin_t::optLoadStore() { … } // remove bodies of uncalled functions void spirvbin_t::dceFuncs() { … } // remove unused function variables + decorations void spirvbin_t::dceVars() { … } // remove unused types void spirvbin_t::dceTypes() { … } #ifdef NOTDEF bool spirvbin_t::matchType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const { // Find the local type id "lt" and global type id "gt" const auto lt_it = typeConstPosR.find(lt); if (lt_it == typeConstPosR.end()) return false; const auto typeStart = lt_it->second; // Search for entry in global table const auto gtype = globalTypes.find(gt); if (gtype == globalTypes.end()) return false; const auto& gdata = gtype->second; // local wordcount and opcode const int wordCount = asWordCount(typeStart); const spv::Op opCode = asOpCode(typeStart); // no type match if opcodes don't match, or operand count doesn't match if (opCode != opOpCode(gdata[0]) || wordCount != opWordCount(gdata[0])) return false; const unsigned numOperands = wordCount - 2; // all types have a result const auto cmpIdRange = [&](range_t range) { for (int x=range.first; x<std::min(range.second, wordCount); ++x) if (!matchType(globalTypes, asId(typeStart+x), gdata[x])) return false; return true; }; const auto cmpConst = [&]() { return cmpIdRange(constRange(opCode)); }; const auto cmpSubType = [&]() { return cmpIdRange(typeRange(opCode)); }; // Compare literals in range [start,end) const auto cmpLiteral = [&]() { const auto range = literalRange(opCode); return std::equal(spir.begin() + typeStart + range.first, spir.begin() + typeStart + std::min(range.second, wordCount), gdata.begin() + range.first); }; assert(isTypeOp(opCode) || isConstOp(opCode)); switch (opCode) { case spv::OpTypeOpaque: // TODO: disable until we compare the literal strings. case spv::OpTypeQueue: return false; case spv::OpTypeEvent: // fall through... case spv::OpTypeDeviceEvent: // ... case spv::OpTypeReserveId: return false; // for samplers, we don't handle the optional parameters yet case spv::OpTypeSampler: return cmpLiteral() && cmpConst() && cmpSubType() && wordCount == 8; default: return cmpLiteral() && cmpConst() && cmpSubType(); } } // Look for an equivalent type in the globalTypes map spv::Id spirvbin_t::findType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt) const { // Try a recursive type match on each in turn, and return a match if we find one for (const auto& gt : globalTypes) if (matchType(globalTypes, lt, gt.first)) return gt.first; return spv::NoType; } #endif // NOTDEF // Return start position in SPV of given Id. error if not found. unsigned spirvbin_t::idPos(spv::Id id) const { … } // 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. std::uint32_t spirvbin_t::hashType(unsigned typeStart) const { … } void spirvbin_t::mapTypeConst() { … } // Strip a single binary by removing ranges given in stripRange void spirvbin_t::strip() { … } // Strip a single binary by removing ranges given in stripRange void spirvbin_t::remap(std::uint32_t opts) { … } // remap from a memory image void spirvbin_t::remap(std::vector<std::uint32_t>& in_spv, const std::vector<std::string>& whiteListStrings, std::uint32_t opts) { … } // remap from a memory image - legacy interface without white list void spirvbin_t::remap(std::vector<std::uint32_t>& in_spv, std::uint32_t opts) { … } } // namespace SPV