Add LoopDescriptor as an IRContext analysis.
authorVictor Lomuller <victor@codeplay.com>
Thu, 25 Jan 2018 10:33:06 +0000 (10:33 +0000)
committerSteven Perron <stevenperron@google.com>
Thu, 25 Jan 2018 21:12:32 +0000 (16:12 -0500)
Move some function definitions from header to source to avoid circular definition.

source/opt/ir_context.cpp
source/opt/ir_context.h
source/opt/loop_descriptor.cpp
source/opt/loop_descriptor.h
test/opt/loop_optimizations/loop_descriptions.cpp
test/opt/loop_optimizations/nested_loops.cpp

index c332d8c..e3d6497 100644 (file)
@@ -40,6 +40,9 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
   if (set & kAnalysisDominatorAnalysis) {
     ResetDominatorAnalysis();
   }
+  if (set & kAnalysisLoopAnalysis) {
+    ResetLoopAnalysis();
+  }
 }
 
 void IRContext::InvalidateAnalysesExceptFor(
@@ -483,6 +486,21 @@ void IRContext::InitializeCombinators() {
   valid_analyses_ |= kAnalysisCombinators;
 }
 
+ir::LoopDescriptor* IRContext::GetLoopDescriptor(const ir::Function* f) {
+  if (!AreAnalysesValid(kAnalysisLoopAnalysis)) {
+    ResetLoopAnalysis();
+  }
+
+  std::unordered_map<const ir::Function*, ir::LoopDescriptor>::iterator it =
+      loop_descriptors_.find(f);
+  if (it == loop_descriptors_.end()) {
+    return &loop_descriptors_.emplace(std::make_pair(f, ir::LoopDescriptor(f)))
+                .first->second;
+  }
+
+  return &it->second;
+}
+
 // Gets the dominator analysis for function |f|.
 opt::DominatorAnalysis* IRContext::GetDominatorAnalysis(const ir::Function* f,
                                                         const ir::CFG& in_cfg) {
index 600ae61..bc5f643 100644 (file)
@@ -22,6 +22,7 @@
 #include "def_use_manager.h"
 #include "dominator_analysis.h"
 #include "feature_manager.h"
+#include "loop_descriptor.h"
 #include "module.h"
 #include "type_manager.h"
 
@@ -55,7 +56,8 @@ class IRContext {
     kAnalysisCombinators = 1 << 3,
     kAnalysisCFG = 1 << 4,
     kAnalysisDominatorAnalysis = 1 << 5,
-    kAnalysisEnd = 1 << 6
+    kAnalysisLoopAnalysis = 1 << 6,
+    kAnalysisEnd = 1 << 7
   };
 
   friend inline constexpr Analysis operator|(Analysis lhs, Analysis rhs);
@@ -363,6 +365,9 @@ class IRContext {
     return cfg_.get();
   }
 
+  // Gets the loop descriptor for function |f|.
+  ir::LoopDescriptor* GetLoopDescriptor(const ir::Function* f);
+
   // Gets the dominator analysis for function |f|.
   opt::DominatorAnalysis* GetDominatorAnalysis(const ir::Function* f,
                                                const ir::CFG&);
@@ -433,6 +438,13 @@ class IRContext {
     valid_analyses_ = valid_analyses_ | kAnalysisDominatorAnalysis;
   }
 
+  // Removes all computed loop descriptors.
+  void ResetLoopAnalysis() {
+    // Clear the cache.
+    loop_descriptors_.clear();
+    valid_analyses_ = valid_analyses_ | kAnalysisLoopAnalysis;
+  }
+
   // Analyzes the features in the owned module. Builds the manager if required.
   void AnalyzeFeatures() {
     feature_mgr_.reset(new opt::FeatureManager(grammar_));
@@ -499,6 +511,9 @@ class IRContext {
   std::map<const ir::Function*, opt::PostDominatorAnalysis>
       post_dominator_trees_;
 
+  // Cache of loop descriptors for each function.
+  std::unordered_map<const ir::Function*, ir::LoopDescriptor> loop_descriptors_;
+
   // Constant manager for |module_|.
   std::unique_ptr<opt::analysis::ConstantManager> constant_mgr_;
 
index e1bb0c7..2545be3 100644 (file)
@@ -18,6 +18,9 @@
 #include <utility>
 #include <vector>
 
+#include "opt/cfg.h"
+#include "opt/dominator_tree.h"
+#include "opt/ir_context.h"
 #include "opt/iterator.h"
 #include "opt/loop_descriptor.h"
 #include "opt/make_unique.h"
@@ -80,6 +83,26 @@ BasicBlock* Loop::FindLoopPreheader(IRContext* ir_context,
   return nullptr;
 }
 
+bool Loop::IsInsideLoop(Instruction* inst) const {
+  const BasicBlock* parent_block = inst->context()->get_instr_block(inst);
+  if (!parent_block) return false;
+  return IsInsideLoop(parent_block);
+}
+
+bool Loop::IsBasicBlockInLoopSlow(const BasicBlock* bb) {
+  assert(bb->GetParent() && "The basic block does not belong to a function");
+  IRContext* context = bb->GetParent()->GetParent()->context();
+
+  opt::DominatorAnalysis* dom_analysis =
+      context->GetDominatorAnalysis(bb->GetParent(), *context->cfg());
+  if (!dom_analysis->Dominates(GetHeaderBlock(), bb)) return false;
+
+  opt::PostDominatorAnalysis* postdom_analysis =
+      context->GetPostDominatorAnalysis(bb->GetParent(), *context->cfg());
+  if (!postdom_analysis->Dominates(GetMergeBlock(), bb)) return false;
+  return true;
+}
+
 LoopDescriptor::LoopDescriptor(const Function* f) { PopulateList(f); }
 
 void LoopDescriptor::PopulateList(const Function* f) {
index b00c18e..87d457d 100644 (file)
 #include <unordered_set>
 #include <vector>
 
-#include "opt/module.h"
-#include "opt/pass.h"
+#include "opt/basic_block.h"
 #include "opt/tree_iterator.h"
 
 namespace spvtools {
+namespace opt {
+class DominatorAnalysis;
+struct DominatorTreeNode;
+}  // namespace opt
 namespace ir {
+class IRContext;
 class CFG;
 class LoopDescriptor;
 
@@ -128,26 +132,12 @@ class Loop {
   }
 
   // Returns true if the instruction |inst| is inside this loop.
-  inline bool IsInsideLoop(Instruction* inst) const {
-    const BasicBlock* parent_block = inst->context()->get_instr_block(inst);
-    if (!parent_block) return true;
-    return IsInsideLoop(parent_block);
-  }
+  bool IsInsideLoop(Instruction* inst) const;
 
   // Adds the Basic Block |bb| this loop and its parents.
   void AddBasicBlockToLoop(const BasicBlock* bb) {
-#ifndef NDEBUG
-    assert(bb->GetParent() && "The basic block does not belong to a function");
-    IRContext* context = bb->GetParent()->GetParent()->context();
-
-    opt::DominatorAnalysis* dom_analysis =
-        context->GetDominatorAnalysis(bb->GetParent(), *context->cfg());
-    assert(dom_analysis->Dominates(GetHeaderBlock(), bb));
-
-    opt::PostDominatorAnalysis* postdom_analysis =
-        context->GetPostDominatorAnalysis(bb->GetParent(), *context->cfg());
-    assert(postdom_analysis->Dominates(GetMergeBlock(), bb));
-#endif  // NDEBUG
+    assert(IsBasicBlockInLoopSlow(bb) &&
+           "Basic block does not belong to the loop");
 
     for (Loop* loop = this; loop != nullptr; loop = loop->parent_) {
       loop_basic_blocks_.insert(bb->id());
@@ -177,6 +167,11 @@ class Loop {
   // computed only when needed on demand.
   BasicBlockListTy loop_basic_blocks_;
 
+  // Check that |bb| is inside the loop using domination properties.
+  // Note: this is for assertion purposes only, IsInsideLoop should be used
+  // instead.
+  bool IsBasicBlockInLoopSlow(const BasicBlock* bb);
+
   // Sets the parent loop of this loop, that is, a loop which contains this loop
   // as a nested child loop.
   inline void SetParent(Loop* parent) { parent_ = parent; }
index d54b789..d2a9110 100644 (file)
@@ -97,7 +97,7 @@ TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                              << text << std::endl;
   const ir::Function* f = spvtest::GetFunction(module, 2);
-  ir::LoopDescriptor ld{f};
+  ir::LoopDescriptor& ld = *context->GetLoopDescriptor(f);
 
   EXPECT_EQ(ld.NumLoops(), 1u);
 
@@ -194,7 +194,7 @@ TEST_F(PassClassTest, LoopWithNoPreHeader) {
   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                              << text << std::endl;
   const ir::Function* f = spvtest::GetFunction(module, 2);
-  ir::LoopDescriptor ld{f};
+  ir::LoopDescriptor& ld = *context->GetLoopDescriptor(f);
 
   EXPECT_EQ(ld.NumLoops(), 2u);
 
index d635586..be42dff 100644 (file)
@@ -145,7 +145,7 @@ TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                              << text << std::endl;
   const ir::Function* f = spvtest::GetFunction(module, 2);
-  ir::LoopDescriptor ld{f};
+  ir::LoopDescriptor& ld = *context->GetLoopDescriptor(f);
 
   EXPECT_EQ(ld.NumLoops(), 3u);
 
@@ -331,7 +331,7 @@ TEST_F(PassClassTest, TripleNestedLoop) {
   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                              << text << std::endl;
   const ir::Function* f = spvtest::GetFunction(module, 2);
-  ir::LoopDescriptor ld{f};
+  ir::LoopDescriptor& ld = *context->GetLoopDescriptor(f);
 
   EXPECT_EQ(ld.NumLoops(), 4u);
 
@@ -555,7 +555,7 @@ TEST_F(PassClassTest, LoopParentTest) {
   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                              << text << std::endl;
   const ir::Function* f = spvtest::GetFunction(module, 2);
-  ir::LoopDescriptor ld{f};
+  ir::LoopDescriptor& ld = *context->GetLoopDescriptor(f);
 
   EXPECT_EQ(ld.NumLoops(), 4u);