Merge pull request #153 from greg-lunarg/gsf-pr1

SPV Remapper: Update for 1.0 and bug fixes
This commit is contained in:
John Kessenich 2016-02-02 09:11:01 -07:00
commit fd8a38276f
2 changed files with 108 additions and 35 deletions

View File

@ -140,20 +140,17 @@ namespace spv {
} }
} }
bool spirvbin_t::isFlowCtrlOpen(spv::Op opCode) const bool spirvbin_t::isFlowCtrl(spv::Op opCode) const
{ {
switch (opCode) { switch (opCode) {
case spv::OpBranchConditional: case spv::OpBranchConditional:
case spv::OpSwitch: return true; case spv::OpBranch:
default: return false; case spv::OpSwitch:
}
}
bool spirvbin_t::isFlowCtrlClose(spv::Op opCode) const
{
switch (opCode) {
case spv::OpLoopMerge: case spv::OpLoopMerge:
case spv::OpSelectionMerge: return true; case spv::OpSelectionMerge:
case spv::OpLabel:
case spv::OpFunction:
case spv::OpFunctionEnd: return true;
default: return false; default: return false;
} }
} }
@ -468,19 +465,36 @@ namespace spv {
} }
return nextInst; return nextInst;
case spv::OperandLiteralString: case spv::OperandLiteralString: {
// word += literalStringWords(literalString(word)); // for clarity const int stringWordCount = literalStringWords(literalString(word));
word += stringWordCount;
numOperands -= (stringWordCount-1); // -1 because for() header post-decrements
break;
}
// Execution mode might have extra literal operands. Skip them.
case spv::OperandExecutionMode:
return nextInst; return nextInst;
// Single word operands we simply ignore, as they hold no IDs // Single word operands we simply ignore, as they hold no IDs
case spv::OperandLiteralNumber: case spv::OperandLiteralNumber:
case spv::OperandSource: case spv::OperandSource:
case spv::OperandExecutionModel: case spv::OperandExecutionModel:
case spv::OperandAddressing: case spv::OperandAddressing:
case spv::OperandMemory: case spv::OperandMemory:
case spv::OperandExecutionMode:
case spv::OperandStorage: case spv::OperandStorage:
case spv::OperandDimensionality: case spv::OperandDimensionality:
case spv::OperandSamplerAddressingMode:
case spv::OperandSamplerFilterMode:
case spv::OperandSamplerImageFormat:
case spv::OperandImageChannelOrder:
case spv::OperandImageChannelDataType:
case spv::OperandImageOperands:
case spv::OperandFPFastMath:
case spv::OperandFPRoundingMode:
case spv::OperandLinkageType:
case spv::OperandAccessQualifier:
case spv::OperandFuncParamAttr:
case spv::OperandDecoration: case spv::OperandDecoration:
case spv::OperandBuiltIn: case spv::OperandBuiltIn:
case spv::OperandSelect: case spv::OperandSelect:
@ -492,10 +506,12 @@ namespace spv {
case spv::OperandGroupOperation: case spv::OperandGroupOperation:
case spv::OperandKernelEnqueueFlags: case spv::OperandKernelEnqueueFlags:
case spv::OperandKernelProfilingInfo: case spv::OperandKernelProfilingInfo:
case spv::OperandCapability:
++word; ++word;
break; break;
default: default:
assert(0 && "Unhandled Operand Class");
break; break;
} }
} }
@ -558,7 +574,7 @@ namespace spv {
// Window size for context-sensitive canonicalization values // Window size for context-sensitive canonicalization values
// Emperical best size from a single data set. TODO: Would be a good tunable. // Emperical best size from a single data set. TODO: Would be a good tunable.
// We essentially performa a little convolution around each instruction, // We essentially perform a little convolution around each instruction,
// to capture the flavor of nearby code, to hopefully match to similar // to capture the flavor of nearby code, to hopefully match to similar
// code in other modules. // code in other modules.
static const unsigned windowSize = 2; static const unsigned windowSize = 2;
@ -713,49 +729,71 @@ namespace spv {
strip(); // strip out data we decided to eliminate strip(); // strip out data we decided to eliminate
} }
// remove bodies of uncalled functions // optimize loads and stores
void spirvbin_t::optLoadStore() void spirvbin_t::optLoadStore()
{ {
idset_t fnLocalVars; idset_t fnLocalVars; // candidates for removal (only locals)
// Map of load result IDs to what they load idmap_t idMap; // Map of load result IDs to what they load
idmap_t idMap; blockmap_t blockMap; // Map of IDs to blocks they first appear in
int blockNum = 0; // block count, to avoid crossing flow control
// Find all the function local pointers stored at most once, and not via access chains // Find all the function local pointers stored at most once, and not via access chains
process( process(
[&](spv::Op opCode, unsigned start) { [&](spv::Op opCode, unsigned start) {
const int wordCount = asWordCount(start); const int wordCount = asWordCount(start);
// Count blocks, so we can avoid crossing flow control
if (isFlowCtrl(opCode))
++blockNum;
// Add local variables to the map // Add local variables to the map
if ((opCode == spv::OpVariable && spv[start+3] == spv::StorageClassFunction && asWordCount(start) == 4)) if ((opCode == spv::OpVariable && spv[start+3] == spv::StorageClassFunction && asWordCount(start) == 4)) {
fnLocalVars.insert(asId(start+2)); fnLocalVars.insert(asId(start+2));
return true;
}
// Ignore process vars referenced via access chain // Ignore process vars referenced via access chain
if ((opCode == spv::OpAccessChain || opCode == spv::OpInBoundsAccessChain) && fnLocalVars.count(asId(start+3)) > 0) { if ((opCode == spv::OpAccessChain || opCode == spv::OpInBoundsAccessChain) && fnLocalVars.count(asId(start+3)) > 0) {
fnLocalVars.erase(asId(start+3)); fnLocalVars.erase(asId(start+3));
idMap.erase(asId(start+3)); idMap.erase(asId(start+3));
return true;
} }
if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) { if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) {
// Avoid loads before stores (TODO: why? Crashes driver, but seems like it shouldn't). const spv::Id varId = asId(start+3);
if (idMap.find(asId(start+3)) == idMap.end()) {
fnLocalVars.erase(asId(start+3)); // Avoid loads before stores
idMap.erase(asId(start+3)); if (idMap.find(varId) == idMap.end()) {
fnLocalVars.erase(varId);
idMap.erase(varId);
} }
// don't do for volatile references // don't do for volatile references
if (wordCount > 4 && (spv[start+4] & spv::MemoryAccessVolatileMask)) { if (wordCount > 4 && (spv[start+4] & spv::MemoryAccessVolatileMask)) {
fnLocalVars.erase(asId(start+3)); fnLocalVars.erase(varId);
idMap.erase(asId(start+3)); idMap.erase(varId);
} }
// Handle flow control
if (blockMap.find(varId) == blockMap.end()) {
blockMap[varId] = blockNum; // track block we found it in.
} else if (blockMap[varId] != blockNum) {
fnLocalVars.erase(varId); // Ignore if crosses flow control
idMap.erase(varId);
}
return true;
} }
if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) { if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) {
if (idMap.find(asId(start+1)) == idMap.end()) { const spv::Id varId = asId(start+1);
idMap[asId(start+1)] = asId(start+2);
if (idMap.find(varId) == idMap.end()) {
idMap[varId] = asId(start+2);
} else { } else {
// Remove if it has more than one store to the same pointer // Remove if it has more than one store to the same pointer
fnLocalVars.erase(asId(start+1)); fnLocalVars.erase(varId);
idMap.erase(asId(start+1)); idMap.erase(varId);
} }
// don't do for volatile references // don't do for volatile references
@ -763,11 +801,29 @@ namespace spv {
fnLocalVars.erase(asId(start+3)); fnLocalVars.erase(asId(start+3));
idMap.erase(asId(start+3)); idMap.erase(asId(start+3));
} }
// Handle flow control
if (blockMap.find(varId) == blockMap.end()) {
blockMap[varId] = blockNum; // track block we found it in.
} else if (blockMap[varId] != blockNum) {
fnLocalVars.erase(varId); // Ignore if crosses flow control
idMap.erase(varId);
}
return true;
} }
return true; return false;
}, },
op_fn_nop);
// If local var id used anywhere else, don't eliminate
[&](spv::Id& id) {
if (fnLocalVars.count(id) > 0) {
fnLocalVars.erase(id);
idMap.erase(id);
}
}
);
process( process(
[&](spv::Op opCode, unsigned start) { [&](spv::Op opCode, unsigned start) {
@ -777,12 +833,27 @@ namespace spv {
}, },
op_fn_nop); op_fn_nop);
// Chase replacements to their origins, in case there is a chain such as:
// 2 = store 1
// 3 = load 2
// 4 = store 3
// 5 = load 4
// We want to replace uses of 5 with 1.
for (const auto& idPair : idMap) {
spv::Id id = idPair.first;
while (idMap.find(id) != idMap.end()) // Chase to end of chain
id = idMap[id];
idMap[idPair.first] = id; // replace with final result
}
// Remove the load/store/variables for the ones we've discovered // Remove the load/store/variables for the ones we've discovered
process( process(
[&](spv::Op opCode, unsigned start) { [&](spv::Op opCode, unsigned start) {
if ((opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) || if ((opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) ||
(opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) || (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) ||
(opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) { (opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) {
stripInst(start); stripInst(start);
return true; return true;
} }
@ -790,7 +861,9 @@ namespace spv {
return false; return false;
}, },
[&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; } [&](spv::Id& id) {
if (idMap.find(id) != idMap.end()) id = idMap[id];
}
); );
strip(); // strip out data we decided to eliminate strip(); // strip out data we decided to eliminate

View File

@ -131,6 +131,7 @@ private:
// Local to global, or global to local ID map // Local to global, or global to local ID map
typedef std::unordered_map<spv::Id, spv::Id> idmap_t; typedef std::unordered_map<spv::Id, spv::Id> idmap_t;
typedef std::unordered_set<spv::Id> idset_t; typedef std::unordered_set<spv::Id> idset_t;
typedef std::unordered_map<spv::Id, int> blockmap_t;
void remap(std::uint32_t opts = DO_EVERYTHING); void remap(std::uint32_t opts = DO_EVERYTHING);
@ -164,8 +165,7 @@ private:
bool isConstOp(spv::Op opCode) const; bool isConstOp(spv::Op opCode) const;
bool isTypeOp(spv::Op opCode) const; bool isTypeOp(spv::Op opCode) const;
bool isStripOp(spv::Op opCode) const; bool isStripOp(spv::Op opCode) const;
bool isFlowCtrlOpen(spv::Op opCode) const; bool isFlowCtrl(spv::Op opCode) const;
bool isFlowCtrlClose(spv::Op opCode) const;
range_t literalRange(spv::Op opCode) const; range_t literalRange(spv::Op opCode) const;
range_t typeRange(spv::Op opCode) const; range_t typeRange(spv::Op opCode) const;
range_t constRange(spv::Op opCode) const; range_t constRange(spv::Op opCode) const;