spirv-remap: Fixed strings not at end of operands, fixed L/S defect
Also added new op classes.
This commit is contained in:
parent
036a7944e5
commit
8548bab1fa
2 changed files with 90 additions and 32 deletions
|
|
@ -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,20 +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::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:
|
||||||
|
|
@ -493,6 +506,7 @@ 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;
|
||||||
|
|
||||||
|
|
@ -560,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;
|
||||||
|
|
@ -715,18 +729,23 @@ 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));
|
||||||
|
|
@ -741,27 +760,40 @@ namespace spv {
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
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
|
||||||
|
|
@ -769,6 +801,15 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -792,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;
|
||||||
}
|
}
|
||||||
|
|
@ -805,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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue