DXIL Control Flow Change FindUniformBlocks to ignore blocks in a loop

This commit is contained in:
Jake Turner
2024-11-24 11:46:51 +00:00
parent 7b767c9e6e
commit e7a8ad4614
@@ -31,6 +31,7 @@
Inputs are links of blocks : from -> to (can be forwards or backwards links)
Output is a list of uniform control flow blocks which all possible flows go through (not diverged)
and are not in a loop
The algorithm is:
@@ -46,8 +47,10 @@ already computed
3. Find Uniform Blocks
* Generate a list of path indexes for each block in the paths
* uniform block is defined by any block which appears in all paths
* all paths includes walking any paths linked at the end node of the path being walked
* Generate a list of all paths blocks which are blocks which appear in all possible paths
* all paths includes walking any paths linked at the end node of the path being walked
* Generate a list of loop blocks which are blocks which appear in any path starting from the block
* uniform blocks are defined to be blocks which are all paths blocks minus loop blocks
*/
namespace DXIL
@@ -62,7 +65,8 @@ private:
typedef rdcarray<uint32_t> BlockPath;
bool TraceBlockFlow(const uint32_t from, BlockPath &path);
bool BlockInPath(uint32_t block, uint32_t pathIdx, uint32_t startIdx);
bool BlockInAllPaths(uint32_t block, uint32_t pathIdx, uint32_t startIdx);
bool BlockInAnyPath(uint32_t block, uint32_t pathIdx, uint32_t startIdx);
const uint32_t PATH_END = ~0U;
@@ -141,7 +145,50 @@ bool ControlFlow::TraceBlockFlow(const uint32_t from, BlockPath &path)
return true;
}
bool ControlFlow::BlockInPath(uint32_t block, uint32_t pathIdx, uint32_t startIdx)
bool ControlFlow::BlockInAnyPath(uint32_t block, uint32_t pathIdx, uint32_t startIdx)
{
const rdcarray<uint32_t> &path = m_Paths[pathIdx];
if(path.size() == 0)
return false;
// Check the current path
for(uint32_t i = startIdx; i < path.size(); ++i)
{
if(block == path[i])
return true;
}
uint32_t endNode = path[path.size() - 1];
if(endNode == PATH_END)
return false;
m_CheckedPaths.insert(endNode);
// Check any paths linked to by the end node of the current path
const rdcarray<uint32_t> &childPathsToCheck = m_BlockPathLinks[endNode];
for(uint32_t childPathIdx : childPathsToCheck)
{
if(m_CheckedPaths.count(childPathIdx) != 0)
continue;
m_CheckedPaths.insert(childPathIdx);
const rdcarray<uint32_t> &childPath = m_Paths[childPathIdx];
uint32_t childPartStartIdx = ~0U;
for(childPartStartIdx = 0; childPartStartIdx < childPath.size(); ++childPartStartIdx)
{
if(childPath[childPartStartIdx] == endNode)
break;
}
if(childPartStartIdx != ~0U)
{
if(BlockInAnyPath(block, childPathIdx, childPartStartIdx))
return true;
}
}
return false;
}
bool ControlFlow::BlockInAllPaths(uint32_t block, uint32_t pathIdx, uint32_t startIdx)
{
const rdcarray<uint32_t> &path = m_Paths[pathIdx];
if(path.size() == 0)
@@ -174,7 +221,7 @@ bool ControlFlow::BlockInPath(uint32_t block, uint32_t pathIdx, uint32_t startId
if(childPath[childPartStartIdx] == endNode)
break;
}
if(!BlockInPath(block, childPathIdx, childPartStartIdx))
if(!BlockInAllPaths(block, childPathIdx, childPartStartIdx))
return false;
}
return true;
@@ -197,9 +244,37 @@ void ControlFlow::FindUniformBlocks(rdcarray<uint32_t> &uniformBlocks)
}
}
uniformBlocks.clear();
rdcarray<uint32_t> loopBlocks;
// A loop block is defined by any block which appears in any path starting from the block
for(uint32_t block : m_Blocks)
{
bool loop = false;
for(uint32_t pathIdx = 0; pathIdx < m_Paths.size(); ++pathIdx)
{
m_CheckedPaths.clear();
uint32_t startIdx = ~0U;
for(uint32_t i = 0; i < m_Paths[pathIdx].size() - 1; ++i)
{
if(m_Paths[pathIdx][i] == block)
{
startIdx = i;
break;
}
}
// BlockInAllPaths will also check all paths linked to from the end node of the path
if(startIdx != ~0U && BlockInAnyPath(block, pathIdx, startIdx + 1))
{
loop = true;
break;
}
}
if(loop)
loopBlocks.push_back(block);
}
// A uniform block is defined by any block which appears in all paths
rdcarray<uint32_t> allPathsBlocks;
// An all paths block is defined by any block which appears in all paths
// all paths includes walking any paths linked at the end node of the path being walked
for(uint32_t block : m_Blocks)
{
@@ -207,14 +282,22 @@ void ControlFlow::FindUniformBlocks(rdcarray<uint32_t> &uniformBlocks)
for(uint32_t pathIdx = 0; pathIdx < m_Paths.size(); ++pathIdx)
{
m_CheckedPaths.clear();
// BlockInPath will also check all paths linked to from the end node of the path
if(!BlockInPath(block, pathIdx, 0))
// BlockInAllPaths will also check all paths linked to from the end node of the path
if(!BlockInAllPaths(block, pathIdx, 0))
{
uniform = false;
break;
}
}
if(uniform)
allPathsBlocks.push_back(block);
}
// A uniform block is defined as an all paths block which is not part of a loop
uniformBlocks.clear();
for(uint32_t block : allPathsBlocks)
{
if(!loopBlocks.contains(block))
uniformBlocks.push_back(block);
}
}
@@ -276,7 +359,7 @@ TEST_CASE("DXIL Control Flow", "[dxil]")
}
{
// Finite Loop (3 -> 4 -> 5 -> 3)
// Finite loop (3 -> 4 -> 5 -> 3)
// 0 -> 1 -> 3
// 0 -> 2 -> 3
// 3 -> 4 -> 5
@@ -294,10 +377,26 @@ TEST_CASE("DXIL Control Flow", "[dxil]")
inputs.push_back({5, 3});
inputs.push_back({5, 6});
DXIL::FindUniformBlocks(inputs, outputs);
REQUIRE(4 == outputs.count());
REQUIRE(2 == outputs.count());
REQUIRE(outputs.contains(0U));
REQUIRE(outputs.contains(3U));
REQUIRE(outputs.contains(4U));
REQUIRE(outputs.contains(6U));
}
{
// Finite loop (3 -> 4 -> 5 -> 3)
rdcarray<BlockLink> inputs;
inputs.push_back({0, 1});
inputs.push_back({1, 2});
inputs.push_back({0, 2});
inputs.push_back({5, 3});
inputs.push_back({2, 3});
inputs.push_back({3, 4});
inputs.push_back({4, 5});
inputs.push_back({3, 5});
inputs.push_back({5, 6});
DXIL::FindUniformBlocks(inputs, outputs);
REQUIRE(3 == outputs.count());
REQUIRE(outputs.contains(0U));
REQUIRE(outputs.contains(2U));
REQUIRE(outputs.contains(6U));
}