From 4c866e4662f625c1c914b600af37733706d380df Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Thu, 14 Jan 2021 15:58:19 +0100 Subject: [PATCH] Fix pathological complexity explosion for certain shaders. Certain shaders where functions have a *ton* of merging control flow will end up with exponential time complexity to figure out parameter preservation semantics. The trivial fix to make it O(1) again is to terminate recursive traversal early if we've seen the path before. Simple oversight :( --- spirv_cross.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spirv_cross.cpp b/spirv_cross.cpp index d814cd64d..991bf9114 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -2829,7 +2829,8 @@ const SPIRConstant &Compiler::get_constant(ConstantID id) const return get(id); } -static bool exists_unaccessed_path_to_return(const CFG &cfg, uint32_t block, const unordered_set &blocks) +static bool exists_unaccessed_path_to_return(const CFG &cfg, uint32_t block, const unordered_set &blocks, + unordered_set &visit_cache) { // This block accesses the variable. if (blocks.find(block) != end(blocks)) @@ -2841,8 +2842,14 @@ static bool exists_unaccessed_path_to_return(const CFG &cfg, uint32_t block, con // If any of our successors have a path to the end, there exists a path from block. for (auto &succ : cfg.get_succeeding_edges(block)) - if (exists_unaccessed_path_to_return(cfg, succ, blocks)) - return true; + { + if (visit_cache.count(succ) == 0) + { + if (exists_unaccessed_path_to_return(cfg, succ, blocks, visit_cache)) + return true; + visit_cache.insert(succ); + } + } return false; } @@ -2899,7 +2906,8 @@ void Compiler::analyze_parameter_preservation( // void foo(int &var) { if (cond) var = 10; } // Using read/write counts, we will think it's just an out variable, but it really needs to be inout, // because if we don't write anything whatever we put into the function must return back to the caller. - if (exists_unaccessed_path_to_return(cfg, entry.entry_block, itr->second)) + unordered_set visit_cache; + if (exists_unaccessed_path_to_return(cfg, entry.entry_block, itr->second, visit_cache)) arg.read_count++; } }