diff --git a/SPIRV/SPVRemapper.cpp b/SPIRV/SPVRemapper.cpp index 05f220a186e697c4bd3bb2153c3c5db10ca076d8..5c551fb8475fcb7ebed1fb20ea9a98404ad92bef 100755 --- a/SPIRV/SPVRemapper.cpp +++ b/SPIRV/SPVRemapper.cpp @@ -127,6 +127,33 @@ namespace spv { } } + // 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 + { + const unsigned typeStart = idPos(id); + const spv::Op opCode = asOpCode(typeStart); + + switch (opCode) { + case spv::OpTypeInt: // fall through... + case spv::OpTypeFloat: return (spv[typeStart+2]+31)/32; + default: + return 0; + } + } + + // 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 + { + const auto tid_it = idTypeSizeMap.find(id); + if (tid_it == idTypeSizeMap.end()) + error("type size for ID not found"); + + return tid_it->second; + } + // Is this an opcode we should remove when using --strip? bool spirvbin_t::isStripOp(spv::Op opCode) const { @@ -140,6 +167,7 @@ namespace spv { } } + // Return true if this opcode is flow control bool spirvbin_t::isFlowCtrl(spv::Op opCode) const { switch (opCode) { @@ -155,6 +183,7 @@ namespace spv { } } + // Return true if this opcode defines a type bool spirvbin_t::isTypeOp(spv::Op opCode) const { switch (opCode) { @@ -182,6 +211,7 @@ namespace spv { } } + // Return true if this opcode defines a constant bool spirvbin_t::isConstOp(spv::Op opCode) const { switch (opCode) { @@ -324,7 +354,7 @@ namespace spv { fnPosDCE.clear(); fnCalls.clear(); typeConstPos.clear(); - typeConstPosR.clear(); + idPosR.clear(); entryPoint = spv::NoResult; largestNewId = 0; @@ -340,6 +370,25 @@ namespace spv { if ((options & STRIP) && isStripOp(opCode)) stripInst(start); + unsigned word = start+1; + spv::Id typeId = spv::NoResult; + + if (spv::InstructionDesc[opCode].hasType()) + typeId = asId(word++); + + // If there's a result ID, remember the size of its type + if (spv::InstructionDesc[opCode].hasResult()) { + const spv::Id resultId = asId(word++); + idPosR[resultId] = start; + + if (typeId != spv::NoResult) { + const unsigned idTypeSize = typeSizeInWords(typeId); + + if (idTypeSize != 0) + idTypeSizeMap[resultId] = idTypeSize; + } + } + if (opCode == spv::Op::OpName) { const spv::Id target = asId(start+1); const std::string name = literalString(start+2); @@ -363,11 +412,9 @@ namespace spv { } else if (isConstOp(opCode)) { assert(asId(start + 2) != spv::NoResult); typeConstPos.insert(start); - typeConstPosR[asId(start + 2)] = start; } else if (isTypeOp(opCode)) { assert(asId(start + 1) != spv::NoResult); typeConstPos.insert(start); - typeConstPosR[asId(start + 1)] = start; } return false; @@ -436,12 +483,19 @@ namespace spv { return nextInst; } + // Circular buffer so we can look back at previous unmapped values during the mapping pass. + static const unsigned idBufferSize = 4; + spv::Id idBuffer[idBufferSize]; + unsigned idBufferPos = 0; + // Store IDs from instruction in our map for (int op = 0; numOperands > 0; ++op, --numOperands) { switch (spv::InstructionDesc[opCode].operands.getClass(op)) { case spv::OperandId: case spv::OperandScope: case spv::OperandMemorySemantics: + idBuffer[idBufferPos] = asId(word); + idBufferPos = (idBufferPos + 1) % idBufferSize; idFn(asId(word++)); break; @@ -459,13 +513,25 @@ namespace spv { // word += numOperands; return nextInst; - case spv::OperandVariableLiteralId: - while (numOperands > 0) { - ++word; // immediate - idFn(asId(word++)); // ID - numOperands -= 2; + case spv::OperandVariableLiteralId: { + if (opCode == OpSwitch) { + // word-2 is the position of the selector ID. OpSwitch Literals match its type. + // In case the IDs are currently being remapped, we get the word[-2] ID from + // the circular idBuffer. + const unsigned literalSizePos = (idBufferPos+idBufferSize-2) % idBufferSize; + const unsigned literalSize = idTypeSizeInWords(idBuffer[literalSizePos]); + const unsigned numLiteralIdPairs = (nextInst-word) / (1+literalSize); + + for (unsigned arg=0; arg<numLiteralIdPairs; ++arg) { + word += literalSize; // literal + idFn(asId(word++)); // label + } + } else { + assert(0); // currentely, only OpSwitch uses OperandVariableLiteralId } + return nextInst; + } case spv::OperandLiteralString: { const int stringWordCount = literalStringWords(literalString(word)); @@ -966,23 +1032,27 @@ namespace spv { std::unordered_map<spv::Id, int> typeUseCount; - // Count total type usage - process(inst_fn_nop, - [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; } - ); + // This is not the most efficient algorithm, but this is an offline tool, and + // it's easy to write this way. Can be improved opportunistically if needed. + bool changed = true; + while (changed) { + changed = false; + strip(); + typeUseCount.clear(); - // Remove types from deleted code - for (const auto& fn : fnPosDCE) + // Count total type usage process(inst_fn_nop, - [&](spv::Id& id) { if (isType[id]) --typeUseCount[id]; }, - fn.second.first, fn.second.second); - - // Remove single reference types - for (const auto typeStart : typeConstPos) { - const spv::Id typeId = asTypeConstId(typeStart); - if (typeUseCount[typeId] == 1) { - --typeUseCount[typeId]; - stripInst(typeStart); + [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; } + ); + + // Remove single reference types + for (const auto typeStart : typeConstPos) { + const spv::Id typeId = asTypeConstId(typeStart); + if (typeUseCount[typeId] == 1) { + changed = true; + --typeUseCount[typeId]; + stripInst(typeStart); + } } } } @@ -1060,12 +1130,12 @@ namespace spv { } #endif // NOTDEF - // Return start position in SPV of given type. error if not found. - unsigned spirvbin_t::typePos(spv::Id id) const + // Return start position in SPV of given Id. error if not found. + unsigned spirvbin_t::idPos(spv::Id id) const { - const auto tid_it = typeConstPosR.find(id); - if (tid_it == typeConstPosR.end()) - error("type ID not found"); + const auto tid_it = idPosR.find(id); + if (tid_it == idPosR.end()) + error("ID not found"); return tid_it->second; } @@ -1083,11 +1153,11 @@ namespace spv { case spv::OpTypeInt: return 3 + (spv[typeStart+3]); case spv::OpTypeFloat: return 5; case spv::OpTypeVector: - return 6 + hashType(typePos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + return 6 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); case spv::OpTypeMatrix: - return 30 + hashType(typePos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + return 30 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); case spv::OpTypeImage: - return 120 + hashType(typePos(spv[typeStart+2])) + + return 120 + hashType(idPos(spv[typeStart+2])) + spv[typeStart+3] + // dimensionality spv[typeStart+4] * 8 * 16 + // depth spv[typeStart+5] * 4 * 16 + // arrayed @@ -1098,24 +1168,24 @@ namespace spv { case spv::OpTypeSampledImage: return 502; case spv::OpTypeArray: - return 501 + hashType(typePos(spv[typeStart+2])) * spv[typeStart+3]; + return 501 + hashType(idPos(spv[typeStart+2])) * spv[typeStart+3]; case spv::OpTypeRuntimeArray: - return 5000 + hashType(typePos(spv[typeStart+2])); + return 5000 + hashType(idPos(spv[typeStart+2])); case spv::OpTypeStruct: { std::uint32_t hash = 10000; for (unsigned w=2; w < wordCount; ++w) - hash += w * hashType(typePos(spv[typeStart+w])); + hash += w * hashType(idPos(spv[typeStart+w])); return hash; } case spv::OpTypeOpaque: return 6000 + spv[typeStart+2]; - case spv::OpTypePointer: return 100000 + hashType(typePos(spv[typeStart+3])); + case spv::OpTypePointer: return 100000 + hashType(idPos(spv[typeStart+3])); case spv::OpTypeFunction: { std::uint32_t hash = 200000; for (unsigned w=2; w < wordCount; ++w) - hash += w * hashType(typePos(spv[typeStart+w])); + hash += w * hashType(idPos(spv[typeStart+w])); return hash; } @@ -1132,14 +1202,14 @@ namespace spv { case spv::OpConstantFalse: return 300008; case spv::OpConstantComposite: { - std::uint32_t hash = 300011 + hashType(typePos(spv[typeStart+1])); + std::uint32_t hash = 300011 + hashType(idPos(spv[typeStart+1])); for (unsigned w=3; w < wordCount; ++w) - hash += w * hashType(typePos(spv[typeStart+w])); + hash += w * hashType(idPos(spv[typeStart+w])); return hash; } case spv::OpConstant: { - std::uint32_t hash = 400011 + hashType(typePos(spv[typeStart+1])); + std::uint32_t hash = 400011 + hashType(idPos(spv[typeStart+1])); for (unsigned w=3; w < wordCount; ++w) hash += w * spv[typeStart+w]; return hash; @@ -1212,19 +1282,19 @@ namespace spv { msg(3, 4, std::string("ID bound: ") + std::to_string(bound())); strip(); // strip out data we decided to eliminate - if (options & OPT_LOADSTORE) optLoadStore(); if (options & OPT_FWD_LS) forwardLoadStores(); if (options & DCE_FUNCS) dceFuncs(); if (options & DCE_VARS) dceVars(); if (options & DCE_TYPES) dceTypes(); + strip(); // strip out data we decided to eliminate + if (options & MAP_TYPES) mapTypeConst(); if (options & MAP_NAMES) mapNames(); if (options & MAP_FUNCS) mapFnBodies(); mapRemainder(); // map any unmapped IDs applyMap(); // Now remap each shader to the new IDs we've come up with - strip(); // strip out data we decided to eliminate } // remap from a memory image diff --git a/SPIRV/SPVRemapper.h b/SPIRV/SPVRemapper.h index c2dc52911f2567894e6a079cea3ddf61e20b1f50..e24db7ced0a27effafe621abf4de42042d606b17 100755 --- a/SPIRV/SPVRemapper.h +++ b/SPIRV/SPVRemapper.h @@ -159,16 +159,21 @@ private: typedef std::set<int> posmap_t; typedef std::unordered_map<spv::Id, int> posmap_rev_t; + // Maps and ID to the size of its base type, if known. + typedef std::unordered_map<spv::Id, unsigned> typesize_map_t; + // handle error void error(const std::string& txt) const { errorHandler(txt); } - bool isConstOp(spv::Op opCode) const; - bool isTypeOp(spv::Op opCode) const; - bool isStripOp(spv::Op opCode) const; - bool isFlowCtrl(spv::Op opCode) const; - range_t literalRange(spv::Op opCode) const; - range_t typeRange(spv::Op opCode) const; - range_t constRange(spv::Op opCode) const; + bool isConstOp(spv::Op opCode) const; + bool isTypeOp(spv::Op opCode) const; + bool isStripOp(spv::Op opCode) const; + bool isFlowCtrl(spv::Op opCode) const; + range_t literalRange(spv::Op opCode) const; + range_t typeRange(spv::Op opCode) const; + range_t constRange(spv::Op opCode) const; + unsigned typeSizeInWords(spv::Id id) const; + unsigned idTypeSizeInWords(spv::Id id) const; spv::Id& asId(unsigned word) { return spv[word]; } const spv::Id& asId(unsigned word) const { return spv[word]; } @@ -177,10 +182,10 @@ private: spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); } unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); } spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); } - unsigned typePos(spv::Id id) const; + unsigned idPos(spv::Id id) const; - static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; } - static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); } + static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; } + static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); } // Header access & set methods spirword_t magic() const { return spv[0]; } // return magic number @@ -263,8 +268,9 @@ private: // Which functions are called, anywhere in the module, with a call count std::unordered_map<spv::Id, int> fnCalls; - posmap_t typeConstPos; // word positions that define types & consts (ordered) - posmap_rev_t typeConstPosR; // reverse map from IDs to positions + posmap_t typeConstPos; // word positions that define types & consts (ordered) + posmap_rev_t idPosR; // reverse map from IDs to positions + typesize_map_t idTypeSizeMap; // maps each ID to its type size, if known. std::vector<spv::Id> idMapL; // ID {M}ap from {L}ocal to {G}lobal IDs