[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-27 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
ymandel marked an inline comment as done.
Closed by commit rG26db5e651bb6: [clang][CFG] Support construction of a weak 
topological ordering of the CFG. (authored by ymandel).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,15 +8,109 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const std::vector &Nodes) {
+  OS << "Blocks{";
+  for (const auto *B : Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const std::vector &Nodes, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << Nodes;
+  *OS << Result;
+}
+
+namespace internal {
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGIntervalNode &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << "Blocks{";
+  for (const auto *B : I.Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}, Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGIntervalGraph &G) {
+  OS << "Intervals{";
+  for (const auto &I : G) {
+OS << I << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const CFGIntervalNode &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
+void PrintTo(const CFGIntervalGraph &G, std::ostream *OS) {
+  *OS << "Intervals{";
+  for (const auto &I : G) {
+PrintTo(I, OS);
+*OS << ", ";
+  }
+  *OS << "}";
+}
+} // namespace internal
+
 namespace {
 
-TEST(BuildInterval, PartitionSimpleOneInterval) {
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::clang::internal::buildInterval;
+using ::clang::internal::partitionIntoIntervals;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::Property;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(intervalID, ID, "") { return arg->ID == ID; }
+
+template  auto blockIDs(T... IDs) {
+  return UnorderedElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template  auto blockOrder(T... IDs) {
+  return ElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+MATCHER_P3(isInterval, ID, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(isInterval, ID, Nodes, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) && testing::Matches(Nodes)(arg.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
 
+TEST(BuildInterval, PartitionSimpleOneInterval) {
   const char *Code = R"(void f() {
   int x = 3;
   int y = 7;
@@ -32,12 +126,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
-
   const char *Code = R"(void f() {
   int x = 3;
   if (x > 3)
@@ -56,14 +149,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
-
   const char *Code = R"(void f() {
   int x = 3;
   while (x >= 3)
@@ -80,11 +170,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*cfg, *EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInt

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 5 inline comments as done.
ymandel added a comment.

Thanks for the helpful suggestions!




Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:104
+/// intervals) if and only if it is reducible (its limit flow graph has one
+/// node). Returns `nullop` when `Cfg` is not reducible.
+///

sammccall wrote:
> sammccall wrote:
> > nit: nullopt
> what do we expect to do in practice when the CFG is not reducible? or do we 
> expect that to never happen?
> 
> (mostly wondering if we need a fallback)
good question -- I'm not really sure what to expect. In practice, any 
structured function will yield a reducible graph. But, creative use of goto's 
can undermine that. So, my guess is that we will not encounter these in 
practice, but I don't know for sure.

As for fallback, RPO doesn't rely on reducibility. So, clients are free to use 
that instead when this fails.



Comment at: clang/lib/Analysis/IntervalPartition.cpp:105
+void fillIntervalNode(CFGIntervalGraph &Graph,
+  std::map &Index,
+  std::queue &Successors,

sammccall wrote:
> std::map of pointers is a bit suspicious, densemap?
I went with a vector mapping (implicitly) from Node IDs, like I did for the WTO.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-26 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 544551.
ymandel marked 2 inline comments as done.
ymandel added a comment.

Addressed comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,15 +8,109 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const std::vector &Nodes) {
+  OS << "Blocks{";
+  for (const auto *B : Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const std::vector &Nodes, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << Nodes;
+  *OS << Result;
+}
+
+namespace internal {
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGIntervalNode &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << "Blocks{";
+  for (const auto *B : I.Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}, Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGIntervalGraph &G) {
+  OS << "Intervals{";
+  for (const auto &I : G) {
+OS << I << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const CFGIntervalNode &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
+void PrintTo(const CFGIntervalGraph &G, std::ostream *OS) {
+  *OS << "Intervals{";
+  for (const auto &I : G) {
+PrintTo(I, OS);
+*OS << ", ";
+  }
+  *OS << "}";
+}
+} // namespace internal
+
 namespace {
 
-TEST(BuildInterval, PartitionSimpleOneInterval) {
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::clang::internal::buildInterval;
+using ::clang::internal::partitionIntoIntervals;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::Property;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(intervalID, ID, "") { return arg->ID == ID; }
+
+template  auto blockIDs(T... IDs) {
+  return UnorderedElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template  auto blockOrder(T... IDs) {
+  return ElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+MATCHER_P3(isInterval, ID, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(isInterval, ID, Nodes, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) && testing::Matches(Nodes)(arg.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
 
+TEST(BuildInterval, PartitionSimpleOneInterval) {
   const char *Code = R"(void f() {
   int x = 3;
   int y = 7;
@@ -32,12 +126,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
-
   const char *Code = R"(void f() {
   int x = 3;
   if (x > 3)
@@ -56,14 +149,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
-
   const char *Code = R"(void f() {
   int x = 3;
   while (x >= 3)
@@ -80,11 +170,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*cfg, *EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, ElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterval I2 = buildInterval(*cfg, *LoopHeadBlock);
-  EXPECT_EQ(I2.Blocks.size(), 5u);
+

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-26 Thread Sam McCall via Phabricator via cfe-commits
sammccall accepted this revision.
sammccall added a comment.

Thanks, this is much simpler!
Just nits & apologies for the delay.




Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:24
 #include "llvm/ADT/DenseSet.h"
+#include 
+#include 

some of these added headers are now unused: map, set, variant



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:32
 namespace clang {
-
+namespace internal {
 // An interval is a strongly-connected component of the CFG along with a

nit: `namespace internal` should probably be moved below the public API



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:104
+/// intervals) if and only if it is reducible (its limit flow graph has one
+/// node). Returns `nullop` when `Cfg` is not reducible.
+///

nit: nullopt



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:104
+/// intervals) if and only if it is reducible (its limit flow graph has one
+/// node). Returns `nullop` when `Cfg` is not reducible.
+///

sammccall wrote:
> nit: nullopt
what do we expect to do in practice when the CFG is not reducible? or do we 
expect that to never happen?

(mostly wondering if we need a fallback)



Comment at: clang/lib/Analysis/IntervalPartition.cpp:36
+
+// Requires: `Node::succs()` and `Node::preds()`.
+template 

Might be more useful to say concretely: Nodes are either CFGBlock or 
CFGIntervalNode



Comment at: clang/lib/Analysis/IntervalPartition.cpp:105
+void fillIntervalNode(CFGIntervalGraph &Graph,
+  std::map &Index,
+  std::queue &Successors,

std::map of pointers is a bit suspicious, densemap?



Comment at: clang/unittests/Analysis/IntervalPartitionTest.cpp:91
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }

nit: matcher factories are functions and so should be lowerCamelCase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-24 Thread Gábor Horváth via Phabricator via cfe-commits
xazax.hun accepted this revision.
xazax.hun added a comment.
This revision is now accepted and ready to land.

Thanks, looks good to me!


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-24 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added inline comments.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:101-103
+/// groups topologically. As a result, the blocks in a series of loops are
+/// ordered such that all nodes in loop `i` are earlier in the order than nodes
+/// in loop `j`. This ordering, when combined with widening, bounds the number

xazax.hun wrote:
> ymandel wrote:
> > xazax.hun wrote:
> > > I wonder if we still want to somehow enforce that within a loop/interval 
> > > the we visit nodes in the RPO order.
> > Starting with RPO should make this algorithm cheaper (and maybe simpler!).  
> > But, RPO construction isn't free. So, I think it is a matter of cost -- 
> > compare generating the RPO first and then using that here vs. just using 
> > this directly.  I can put a note in the implementation as FIXME. WDYT?
> Sounds good to me.
Added a FIXME in `buildIntervals`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-24 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 543552.
ymandel marked 2 inline comments as done.
ymandel added a comment.

Addressed comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,15 +8,110 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const std::vector &Nodes) {
+  OS << "Blocks{";
+  for (const auto *B : Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const std::vector &Nodes, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << Nodes;
+  *OS << Result;
+}
+
+namespace internal {
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGIntervalNode &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << "Blocks{";
+  for (const auto *B : I.Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}, Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGIntervalGraph &G) {
+  OS << "Intervals{";
+  for (const auto &I : G) {
+OS << I << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const CFGIntervalNode &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
+void PrintTo(const CFGIntervalGraph &G, std::ostream *OS) {
+  *OS << "Intervals{";
+  for (const auto &I : G) {
+PrintTo(I, OS);
+*OS << ", ";
+  }
+  *OS << "}";
+}
+} // namespace internal
+
 namespace {
 
-TEST(BuildInterval, PartitionSimpleOneInterval) {
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::clang::internal::buildInterval;
+using ::clang::internal::partitionIntoIntervals;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::Property;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template  auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template  auto BlockOrder(T... IDs) {
+  return ElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+MATCHER_P3(IsInterval, ID, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsInterval, ID, Nodes, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) && testing::Matches(Nodes)(arg.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
 
+TEST(BuildInterval, PartitionSimpleOneInterval) {
   const char *Code = R"(void f() {
   int x = 3;
   int y = 7;
@@ -32,12 +127,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
-
   const char *Code = R"(void f() {
   int x = 3;
   if (x > 3)
@@ -56,14 +150,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
-
   const char *Code = R"(void f() {
   int x = 3;
   while (x >= 3)
@@ -80,11 +171,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*cfg, *EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, ElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterval I2 = buildInterv

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-18 Thread Gábor Horváth via Phabricator via cfe-commits
xazax.hun added inline comments.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:122
+
+  using BlockOrderTy = llvm::DenseMap;
+  BlockOrderTy BlockOrder;

Would it make sense to have a vector instead and use block ids to get the order?



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:101-103
+/// groups topologically. As a result, the blocks in a series of loops are
+/// ordered such that all nodes in loop `i` are earlier in the order than nodes
+/// in loop `j`. This ordering, when combined with widening, bounds the number

ymandel wrote:
> xazax.hun wrote:
> > I wonder if we still want to somehow enforce that within a loop/interval 
> > the we visit nodes in the RPO order.
> Starting with RPO should make this algorithm cheaper (and maybe simpler!).  
> But, RPO construction isn't free. So, I think it is a matter of cost -- 
> compare generating the RPO first and then using that here vs. just using this 
> directly.  I can put a note in the implementation as FIXME. WDYT?
Sounds good to me.



Comment at: clang/lib/Analysis/IntervalPartition.cpp:121
+Index.emplace(N, ID);
+  Graph.Intervals.emplace_back(ID, Header, std::move(Data.Nodes));
 }

ymandel wrote:
> xazax.hun wrote:
> > ymandel wrote:
> > > xazax.hun wrote:
> > > > It would probably take for me some time to understand what is going on 
> > > > here, but I think this part might worth a comment. In particular, I 
> > > > have the impression that `Graph.Intervals` is the owner of all the 
> > > > intervals. But we use pointers to refer to these intervals. What would 
> > > > guarantee that those pointers are not being invalidated by this 
> > > > `emplace_back` operations?
> > > Excellent question, not least because I *wasn't* careful the first around 
> > > and indeed ran up against this as a memory bug. :) That's the reason I 
> > > added IDs, so that I could _avoid_ taking the addresses of elements until 
> > > after we finish growing the vector. See lines 148-165: after we finish 
> > > building the intervals, we go through and take addresses.
> > > 
> > > I can add comments to this effect, but perhaps the safe option is to use 
> > > a std::dequeue. What do you think?
> > Oh, thanks! I don't have a strong preference between a comment or a deque. 
> I ended up changing to a deque after almost making the same mistake again. 
> vector is just not safe when you want to be taking addresses. :) I've dropped 
> the use of IDs and added comments. Let me know if this clear enough.
Looks good, thanks! I agree that the deque solution looks cleaner, probably we 
should get it first right and we can optimize later if there is a need for that.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel added a comment.

Thanks for the very helpful feedback! 
I think I've addressed all of the suggestions/concerns. I suspect there's yet 
more room for improvement in the algorithm, but I think we're converging on 
"good enough".




Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:38
+// interval.
 struct CFGInterval {
+  CFGInterval() = default;

sammccall wrote:
> Summarizing an offline discussion, IIUC...
> - we build a tree of intervals that represents the whole sequence of interval 
> graphs
> - for the current worklist algorithm, we use only the total order and process 
> the first invalid block, the tree is not needed
> - the algorithm/data structure could be significantly simplified (no 
> variants, templates, recursion...) if it didn't build the tree, just the 
> interval graphs iteratively until a single node is reached
> - however a different, non-worklist algorithm could use the tree information 
> to determine which blocks to visit next. (Given `a (b c d) e`, after `d` we 
> try only `b` or `e`). This avoids expensive "is invalid" checks
> - so the motivation for preserving the full interval tree is future-proofing 
> for using a different algorithm
> 
> I think it's worth documenting this motivation, and also considering not 
> paying this complexity cost for a feature we aren't adding yet.
Sam, I've radically simplified the implementation to only build exactly what's 
needed -- the ordering. PTAL and let me know what further 
comments/documentation would help.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:56
+
+  template  struct NodeData {
+// The block from which the interval was constructed. It is either the

sammccall wrote:
> xazax.hun wrote:
> > Correct me if I'm wrong but I have the impression that `CFGInterval` might 
> > either contain only `CfgBlock`s or other `CfgInterval`s, but these might 
> > never be mixed. The current representation does allow such mixing. In case 
> > do not need that, I think it might be cleaner to make `CfgInterval` itself 
> > templated and remove the `std::variant`.
> (Very optional idea, in case you like it)
> 
> This data structure is complicated: a tree of Intervals with CFGBlocks at the 
> root, and at every level the Intervals have pred/succ edges that form part of 
> one of the intermediate flow graphs.
> 
> It seems nice to get rid of the tree if possible (ultimately we're using 
> these for ordering) and the nested pred/succs - we seem to only need these 
> for the top level interval (and in the end, we have one interval and 
> pred/succs are empty.
> 
> What if we made this a flat structure, preserving the bracket structure as 
> some kind of back-edge?
> 
> ```
> 1  (  2  (  3  4  )  )  5
> becomes
> 1 2 3  4 !3 !2  5
> ```
> 
> AIUI, the dataflow algorithm can basically treat this as a program, we move 
> left to right and `!X` means "if (X is invalidated) goto X".
> 
> This would give us something like:
> ```
> using Entry = PointerIntPair; // either an actual block, or a 
> conditional jump
> struct Interval { vector Sequence; SmallDenseSet Preds, 
> Succs; }
> using IntervalGraph = vector;
> ```
> 
> I also suspect that this would let us avoid the core algorithms 
> (buildInterval, addIntervalNode) being templated, simply wrap each CFGBlock 
> in a trivial Interval to start with instead. I guess this is OK 
> performance-wise, but the reason not to do it is ending up with those nodes 
> polluting your final data structure?
> 
> 
> Anyway, all this is up to you - for me keeping more data/structure around 
> than we're going to use is confusing, but it does seem closer to the textbook.
I went with this proposal and stripped it down to only track what we need. I 
didn't even preserve the backedge structure, because we can't use that yet. If 
and when we get there, I think you're approach or something similar, where we 
avoid the tree structure, is a great idea.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:56-64
+  template  struct NodeData {
+// The block from which the interval was constructed. It is either the
+// graph's entry block or has at least one predecessor outside the 
interval.
+const Node *Header;
+std::vector Nodes;
+  };
+  using IntervalData = std::variant, NodeData>;

ymandel wrote:
> sammccall wrote:
> > xazax.hun wrote:
> > > Correct me if I'm wrong but I have the impression that `CFGInterval` 
> > > might either contain only `CfgBlock`s or other `CfgInterval`s, but these 
> > > might never be mixed. The current representation does allow such mixing. 
> > > In case do not need that, I think it might be cleaner to make 
> > > `CfgInterval` itself templated and remove the `std::variant`.
> > (Very optional idea, in case you like it)
> > 
> > This data structure is complicated: a tree of Intervals with CFGBlocks at 
> > the 

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 541153.
ymandel marked 12 inline comments as done.
ymandel added a comment.

tweaks


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,15 +8,110 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const std::vector &Nodes) {
+  OS << "Blocks{";
+  for (const auto *B : Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const std::vector &Nodes, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << Nodes;
+  *OS << Result;
+}
+
+namespace internal {
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGIntervalNode &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << "Blocks{";
+  for (const auto *B : I.Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}, Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGIntervalGraph &G) {
+  OS << "Intervals{";
+  for (const auto &I : G) {
+OS << I << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const CFGIntervalNode &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
+void PrintTo(const CFGIntervalGraph &G, std::ostream *OS) {
+  *OS << "Intervals{";
+  for (const auto &I : G) {
+PrintTo(I, OS);
+*OS << ", ";
+  }
+  *OS << "}";
+}
+} // namespace internal
+
 namespace {
 
-TEST(BuildInterval, PartitionSimpleOneInterval) {
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::clang::internal::buildInterval;
+using ::clang::internal::partitionIntoIntervals;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::Property;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template  auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template  auto BlockOrder(T... IDs) {
+  return ElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+MATCHER_P3(IsInterval, ID, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsInterval, ID, Nodes, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) && testing::Matches(Nodes)(arg.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
 
+TEST(BuildInterval, PartitionSimpleOneInterval) {
   const char *Code = R"(void f() {
   int x = 3;
   int y = 7;
@@ -32,12 +127,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
-
   const char *Code = R"(void f() {
   int x = 3;
   if (x > 3)
@@ -56,14 +150,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
-
   const char *Code = R"(void f() {
   int x = 3;
   while (x >= 3)
@@ -80,11 +171,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*cfg, *EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, ElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterval I2 = buildInterval(*cfg, *Lo

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-17 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 541141.
ymandel added a comment.

Radically simplify the code, per comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,15 +8,110 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const std::vector &Nodes) {
+  OS << "Blocks{";
+  for (const auto *B : Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const std::vector &Nodes, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << Nodes;
+  *OS << Result;
+}
+
+namespace details {
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGIntervalNode &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << "Blocks{";
+  for (const auto *B : I.Nodes)
+OS << B->getBlockID() << ", ";
+  OS << "}, Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGIntervalGraph &G) {
+  OS << "Intervals{";
+  for (const auto &I : G) {
+OS << I << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+void PrintTo(const CFGIntervalNode &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
+void PrintTo(const CFGIntervalGraph &G, std::ostream *OS) {
+  *OS << "Intervals{";
+  for (const auto &I : G) {
+PrintTo(I, OS);
+*OS << ", ";
+  }
+  *OS << "}";
+}
+} // namespace details
+
 namespace {
 
-TEST(BuildInterval, PartitionSimpleOneInterval) {
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::clang::details::buildInterval;
+using ::clang::details::partitionIntoIntervals;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::Property;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template  auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template  auto BlockOrder(T... IDs) {
+  return ElementsAre(Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+MATCHER_P3(IsInterval, ID, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsInterval, ID, Nodes, Preds, Succs, "") {
+  return testing::Matches(ID)(arg.ID) && testing::Matches(Nodes)(arg.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
 
+TEST(BuildInterval, PartitionSimpleOneInterval) {
   const char *Code = R"(void f() {
   int x = 3;
   int y = 7;
@@ -32,12 +127,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
-
   const char *Code = R"(void f() {
   int x = 3;
   if (x > 3)
@@ -56,14 +150,11 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
-
   const char *Code = R"(void f() {
   int x = 3;
   while (x >= 3)
@@ -80,11 +171,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*cfg, *EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, ElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterval I2 = buildInterval(*cfg, *LoopHeadBlock

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-13 Thread Sam McCall via Phabricator via cfe-commits
sammccall added a comment.

Having (mostly!) understood what this is doing, the algorithm looks like it's 
doing the right thing.

High-level ideas would be:

- there are three different representations/sets of APIs exposed: intervals, 
ordering, and compare. It seems like we only have immediate plans to use 
compare, and maybe ordering in the future. Maybe we could hide the stuff we're 
not using, if it's necessary to test it then a narrower interface for testing 
might be enough? Like `string printIntervalGraph(CFG, int iters)` and then all 
of Interval could be hidden.
- the data structures for the interval graph are mechanically complicated 
(trees with differently templated node types etc), and dealing with this 
incidental complexity and the algorithm complexity is a lot. It may be possible 
to use simpler data structures, as we don't make use of everything in these 
ones.




Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:38
+// interval.
 struct CFGInterval {
+  CFGInterval() = default;

Summarizing an offline discussion, IIUC...
- we build a tree of intervals that represents the whole sequence of interval 
graphs
- for the current worklist algorithm, we use only the total order and process 
the first invalid block, the tree is not needed
- the algorithm/data structure could be significantly simplified (no variants, 
templates, recursion...) if it didn't build the tree, just the interval graphs 
iteratively until a single node is reached
- however a different, non-worklist algorithm could use the tree information to 
determine which blocks to visit next. (Given `a (b c d) e`, after `d` we try 
only `b` or `e`). This avoids expensive "is invalid" checks
- so the motivation for preserving the full interval tree is future-proofing 
for using a different algorithm

I think it's worth documenting this motivation, and also considering not paying 
this complexity cost for a feature we aren't adding yet.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:56
+
+  template  struct NodeData {
+// The block from which the interval was constructed. It is either the

xazax.hun wrote:
> Correct me if I'm wrong but I have the impression that `CFGInterval` might 
> either contain only `CfgBlock`s or other `CfgInterval`s, but these might 
> never be mixed. The current representation does allow such mixing. In case do 
> not need that, I think it might be cleaner to make `CfgInterval` itself 
> templated and remove the `std::variant`.
(Very optional idea, in case you like it)

This data structure is complicated: a tree of Intervals with CFGBlocks at the 
root, and at every level the Intervals have pred/succ edges that form part of 
one of the intermediate flow graphs.

It seems nice to get rid of the tree if possible (ultimately we're using these 
for ordering) and the nested pred/succs - we seem to only need these for the 
top level interval (and in the end, we have one interval and pred/succs are 
empty.

What if we made this a flat structure, preserving the bracket structure as some 
kind of back-edge?

```
1  (  2  (  3  4  )  )  5
becomes
1 2 3  4 !3 !2  5
```

AIUI, the dataflow algorithm can basically treat this as a program, we move 
left to right and `!X` means "if (X is invalidated) goto X".

This would give us something like:
```
using Entry = PointerIntPair; // either an actual block, or a 
conditional jump
struct Interval { vector Sequence; SmallDenseSet Preds, 
Succs; }
using IntervalGraph = vector;
```

I also suspect that this would let us avoid the core algorithms (buildInterval, 
addIntervalNode) being templated, simply wrap each CFGBlock in a trivial 
Interval to start with instead. I guess this is OK performance-wise, but the 
reason not to do it is ending up with those nodes polluting your final data 
structure?


Anyway, all this is up to you - for me keeping more data/structure around than 
we're going to use is confusing, but it does seem closer to the textbook.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:123
+/// This WTO construction is described in Section 4.2 of [Bourdoncle1993].
+std::optional getIntervalWTO(const CFG &Cfg);
+

Naming is confusing here: `getWTO` is the internal function whose API involves 
intervals, and `getIntervalWTO` be the main function whose API doesn't. I think 
literally swapping them might be clearer!

in general it's hard to tell which parts of this API people are actually meant 
to use.
Is it reasonable to wrap everything up to `WeakTopologicalOrdering` in 
`namespace internal` or so? Or are there places this will be used outside tests?



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:126
+/// Formats `WTO` as a human-readable string.
+std::string formatWTO(const WeakTopologicalOrdering &WTO);
+

consider instead exposing `prin

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-07-05 Thread Gábor Horváth via Phabricator via cfe-commits
xazax.hun added inline comments.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:56-64
+  template  struct NodeData {
+// The block from which the interval was constructed. It is either the
+// graph's entry block or has at least one predecessor outside the 
interval.
+const Node *Header;
+std::vector Nodes;
+  };
+  using IntervalData = std::variant, NodeData>;

Correct me if I'm wrong but I have the impression that `CFGInterval` might 
either contain only `CfgBlock`s or other `CfgInterval`s, but these might never 
be mixed. The current representation does allow such mixing. In case do not 
need that, I think it might be cleaner to make `CfgInterval` itself templated 
and remove the `std::variant`.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:101-103
+/// groups topologically. As a result, the blocks in a series of loops are
+/// ordered such that all nodes in loop `i` are earlier in the order than nodes
+/// in loop `j`. This ordering, when combined with widening, bounds the number

I wonder if we still want to somehow enforce that within a loop/interval the we 
visit nodes in the RPO order.



Comment at: clang/lib/Analysis/IntervalPartition.cpp:36
+bool inInterval(const Node *N, std::vector &Interval) {
+  return std::find(Interval.begin(), Interval.end(), N) != Interval.end();
+}

We have some convenience wrappers in LLVM: 
https://llvm.org/doxygen/namespacellvm.html#a086e9fbdb06276db7753101a08a63adf



Comment at: clang/lib/Analysis/IntervalPartition.cpp:121
+Index.emplace(N, ID);
+  Graph.Intervals.emplace_back(ID, Header, std::move(Data.Nodes));
 }

ymandel wrote:
> xazax.hun wrote:
> > It would probably take for me some time to understand what is going on 
> > here, but I think this part might worth a comment. In particular, I have 
> > the impression that `Graph.Intervals` is the owner of all the intervals. 
> > But we use pointers to refer to these intervals. What would guarantee that 
> > those pointers are not being invalidated by this `emplace_back` operations?
> Excellent question, not least because I *wasn't* careful the first around and 
> indeed ran up against this as a memory bug. :) That's the reason I added IDs, 
> so that I could _avoid_ taking the addresses of elements until after we 
> finish growing the vector. See lines 148-165: after we finish building the 
> intervals, we go through and take addresses.
> 
> I can add comments to this effect, but perhaps the safe option is to use a 
> std::dequeue. What do you think?
Oh, thanks! I don't have a strong preference between a comment or a deque. 


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 2 inline comments as done.
ymandel added inline comments.



Comment at: clang/lib/Analysis/IntervalPartition.cpp:121
+Index.emplace(N, ID);
+  Graph.Intervals.emplace_back(ID, Header, std::move(Data.Nodes));
 }

xazax.hun wrote:
> It would probably take for me some time to understand what is going on here, 
> but I think this part might worth a comment. In particular, I have the 
> impression that `Graph.Intervals` is the owner of all the intervals. But we 
> use pointers to refer to these intervals. What would guarantee that those 
> pointers are not being invalidated by this `emplace_back` operations?
Excellent question, not least because I *wasn't* careful the first around and 
indeed ran up against this as a memory bug. :) That's the reason I added IDs, 
so that I could _avoid_ taking the addresses of elements until after we finish 
growing the vector. See lines 148-165: after we finish building the intervals, 
we go through and take addresses.

I can add comments to this effect, but perhaps the safe option is to use a 
std::dequeue. What do you think?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-28 Thread Gábor Horváth via Phabricator via cfe-commits
xazax.hun added inline comments.



Comment at: clang/lib/Analysis/IntervalPartition.cpp:121
+Index.emplace(N, ID);
+  Graph.Intervals.emplace_back(ID, Header, std::move(Data.Nodes));
 }

It would probably take for me some time to understand what is going on here, 
but I think this part might worth a comment. In particular, I have the 
impression that `Graph.Intervals` is the owner of all the intervals. But we use 
pointers to refer to these intervals. What would guarantee that those pointers 
are not being invalidated by this `emplace_back` operations?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-28 Thread Gábor Horváth via Phabricator via cfe-commits
xazax.hun added inline comments.



Comment at: clang/unittests/Analysis/IntervalPartitionTest.cpp:21
+namespace {
+template  using NodeData = CFGInterval::NodeData;
+}  // namespace

Another redundant anonymous namespace here. 


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel marked 4 inline comments as done.
ymandel added a comment.

Rebased, thanks!




Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:73
+
+  // Whether this node is the head of a feedback edge within the interval.
+  bool IsFeedbackHead = false;

xazax.hun wrote:
> Is feedback edge a special terminology? I think we might simply call these 
> back edges most of the time.
That's the language from the paper but I prefer standard "back edge" 
terminology so updated to reflect.



Comment at: clang/lib/Analysis/IntervalPartition.cpp:23-25
+namespace {
+template  using NodeData = CFGInterval::NodeData;
+} // namespace

xazax.hun wrote:
> What is the reason for putting a using statement in an anonymous namespace? 
I... don't remember what I was thinking. Regardless, seems pointless for a 
template -- fixed.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 535402.
ymandel added a comment.

Address more comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,13 +8,119 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+namespace {
+template  using NodeData = CFGInterval::NodeData;
+}  // namespace
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGInterval::IntervalData &D) {
+  OS << "Nodes{";
+  if (std::holds_alternative>(D)) {
+auto &BlockData = std::get>(D);
+for (const auto *B : BlockData.Nodes)
+  OS << B->getBlockID() << ", ";
+  } else {
+assert(std::holds_alternative>(D));
+auto &IntervalData = std::get>(D);
+for (const auto *B : IntervalData.Nodes)
+  OS << B->ID << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGInterval &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << I.Data;
+  OS << ", Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}\n";
+  return OS;
+}
+
+void PrintTo(const CFGInterval::IntervalData &D, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << D;
+  *OS << Result;
+}
+
+void PrintTo(const CFGInterval &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
 namespace {
 
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template 
+auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template 
+auto IntervalIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Field(&CFGInterval::ID, IDs)...);
+}
+
+MATCHER_P3(IsBlockInterval, ID, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsBlockInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsNestedInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
 TEST(BuildInterval, PartitionSimpleOneInterval) {
 
   const char *Code = R"(void f() {
@@ -32,8 +138,8 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
@@ -56,12 +162,10 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
 
   const char *Code = R"(void f() {
@@ -80,11 +184,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*cfg, *EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, UnorderedElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterval I2 = buil

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-28 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 535397.
ymandel added a comment.

Rebase and address some comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,13 +8,119 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+namespace {
+template  using NodeData = CFGInterval::NodeData;
+}  // namespace
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGInterval::IntervalData &D) {
+  OS << "Nodes{";
+  if (std::holds_alternative>(D)) {
+auto &BlockData = std::get>(D);
+for (const auto *B : BlockData.Nodes)
+  OS << B->getBlockID() << ", ";
+  } else {
+assert(std::holds_alternative>(D));
+auto &IntervalData = std::get>(D);
+for (const auto *B : IntervalData.Nodes)
+  OS << B->ID << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGInterval &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << I.Data;
+  OS << ", Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}\n";
+  return OS;
+}
+
+void PrintTo(const CFGInterval::IntervalData &D, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << D;
+  *OS << Result;
+}
+
+void PrintTo(const CFGInterval &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
 namespace {
 
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template 
+auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template 
+auto IntervalIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Field(&CFGInterval::ID, IDs)...);
+}
+
+MATCHER_P3(IsBlockInterval, ID, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsBlockInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsNestedInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
 TEST(BuildInterval, PartitionSimpleOneInterval) {
 
   const char *Code = R"(void f() {
@@ -32,8 +138,8 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
@@ -56,12 +162,10 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(*cfg, EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
 
   const char *Code = R"(void f() {
@@ -80,11 +184,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*cfg, *EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, UnorderedElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterva

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-28 Thread Gábor Horváth via Phabricator via cfe-commits
xazax.hun added a comment.

Could you rebase this patch to the latest tip of tree?




Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:73
+
+  // Whether this node is the head of a feedback edge within the interval.
+  bool IsFeedbackHead = false;

Is feedback edge a special terminology? I think we might simply call these back 
edges most of the time.



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:83
 
-// Partitions `Cfg` into intervals and constructs a graph of the intervals,
+// Partitions `Cfg` into intervals and constructs a the graph of the intervals
 // based on the edges between nodes in these intervals.

typo?



Comment at: clang/include/clang/Analysis/Analyses/IntervalPartition.h:87
+
+// (Further) partitions `Graph` into intervals and constructs a the graph of 
the
+// intervals based on the edges between nodes (themselves intervals) in these

same typo here?



Comment at: clang/lib/Analysis/IntervalPartition.cpp:23-25
+namespace {
+template  using NodeData = CFGInterval::NodeData;
+} // namespace

What is the reason for putting a using statement in an anonymous namespace? 


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-15 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 531874.
ymandel added a comment.

more comment expansion.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,13 +8,119 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+namespace {
+template  using NodeData = CFGInterval::NodeData;
+}  // namespace
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGInterval::IntervalData &D) {
+  OS << "Nodes{";
+  if (std::holds_alternative>(D)) {
+auto &BlockData = std::get>(D);
+for (const auto *B : BlockData.Nodes)
+  OS << B->getBlockID() << ", ";
+  } else {
+assert(std::holds_alternative>(D));
+auto &IntervalData = std::get>(D);
+for (const auto *B : IntervalData.Nodes)
+  OS << B->ID << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGInterval &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << I.Data;
+  OS << ", Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}\n";
+  return OS;
+}
+
+void PrintTo(const CFGInterval::IntervalData &D, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << D;
+  *OS << Result;
+}
+
+void PrintTo(const CFGInterval &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
 namespace {
 
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template 
+auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template 
+auto IntervalIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Field(&CFGInterval::ID, IDs)...);
+}
+
+MATCHER_P3(IsBlockInterval, ID, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsBlockInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsNestedInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
 TEST(BuildInterval, PartitionSimpleOneInterval) {
 
   const char *Code = R"(void f() {
@@ -32,8 +138,8 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
@@ -56,12 +162,10 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
 
   const char *Code = R"(void f() {
@@ -80,11 +184,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, UnorderedElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterval I2 = buildInterval(*LoopHe

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-15 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 531865.
ymandel added a comment.

Fix build break and add some field comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,13 +8,119 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+namespace {
+template  using NodeData = CFGInterval::NodeData;
+}  // namespace
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGInterval::IntervalData &D) {
+  OS << "Nodes{";
+  if (std::holds_alternative>(D)) {
+auto &BlockData = std::get>(D);
+for (const auto *B : BlockData.Nodes)
+  OS << B->getBlockID() << ", ";
+  } else {
+assert(std::holds_alternative>(D));
+auto &IntervalData = std::get>(D);
+for (const auto *B : IntervalData.Nodes)
+  OS << B->ID << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGInterval &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << I.Data;
+  OS << ", Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}\n";
+  return OS;
+}
+
+void PrintTo(const CFGInterval::IntervalData &D, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << D;
+  *OS << Result;
+}
+
+void PrintTo(const CFGInterval &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
 namespace {
 
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template 
+auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template 
+auto IntervalIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Field(&CFGInterval::ID, IDs)...);
+}
+
+MATCHER_P3(IsBlockInterval, ID, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsBlockInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsNestedInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
 TEST(BuildInterval, PartitionSimpleOneInterval) {
 
   const char *Code = R"(void f() {
@@ -32,8 +138,8 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
@@ -56,12 +162,10 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
 
   const char *Code = R"(void f() {
@@ -80,11 +184,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, UnorderedElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterval I2 = 

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-15 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel updated this revision to Diff 531843.
ymandel added a comment.

fix some comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153058/new/

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,13 +8,115 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGInterval::IntervalData &D) {
+  OS << "Nodes{";
+  if (std::holds_alternative>(D)) {
+auto &BlockData = std::get>(D);
+for (const auto *B : BlockData.Nodes)
+  OS << B->getBlockID() << ", ";
+  } else {
+assert(std::holds_alternative>(D));
+auto &IntervalData = std::get>(D);
+for (const auto *B : IntervalData.Nodes)
+  OS << B->ID << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGInterval &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << I.Data;
+  OS << ", Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}\n";
+  return OS;
+}
+
+void PrintTo(const CFGInterval::IntervalData &D, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << D;
+  *OS << Result;
+}
+
+void PrintTo(const CFGInterval &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
 namespace {
 
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template 
+auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template 
+auto IntervalIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Field(&CFGInterval::ID, IDs)...);
+}
+
+MATCHER_P3(IsBlockInterval, ID, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsBlockInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsNestedInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
 TEST(BuildInterval, PartitionSimpleOneInterval) {
 
   const char *Code = R"(void f() {
@@ -32,8 +134,8 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
@@ -56,12 +158,10 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
 
   const char *Code = R"(void f() {
@@ -80,11 +180,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1 = buildInterval(EntryBlock);
+  EXPECT_THAT(I1, UnorderedElementsAre(EntryBlock, InitXBlock));
 
-  CFGInterval I2 = buildInterval(*LoopHeadBlock);
-  EXPECT_EQ(I2.Blocks.size(), 5u);
+  std::vector I2 = buildInterval(LoopHead

[PATCH] D153058: [clang][CFG] Support construction of a weak topological ordering of the CFG.

2023-06-15 Thread Yitzhak Mandelbaum via Phabricator via cfe-commits
ymandel created this revision.
ymandel added reviewers: xazax.hun, gribozavr.
Herald added a reviewer: NoQ.
Herald added a project: All.
ymandel requested review of this revision.
Herald added a project: clang.

This patch adds support for building a weak topological ordering of the CFG
blocks, based on a limit flow graph constructed via (repeated) interval
partitioning of the CFG.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D153058

Files:
  clang/include/clang/Analysis/Analyses/IntervalPartition.h
  clang/lib/Analysis/IntervalPartition.cpp
  clang/unittests/Analysis/IntervalPartitionTest.cpp

Index: clang/unittests/Analysis/IntervalPartitionTest.cpp
===
--- clang/unittests/Analysis/IntervalPartitionTest.cpp
+++ clang/unittests/Analysis/IntervalPartitionTest.cpp
@@ -8,13 +8,115 @@
 
 #include "clang/Analysis/Analyses/IntervalPartition.h"
 #include "CFGBuildResult.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include 
+#include 
 
 namespace clang {
-namespace analysis {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+  const CFGInterval::IntervalData &D) {
+  OS << "Nodes{";
+  if (std::holds_alternative>(D)) {
+auto &BlockData = std::get>(D);
+for (const auto *B : BlockData.Nodes)
+  OS << B->getBlockID() << ", ";
+  } else {
+assert(std::holds_alternative>(D));
+auto &IntervalData = std::get>(D);
+for (const auto *B : IntervalData.Nodes)
+  OS << B->ID << ", ";
+  }
+  OS << "}";
+  return OS;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CFGInterval &I) {
+  OS << "Interval{ID = " << I.ID << ", ";
+  OS << I.Data;
+  OS << ", Pre{";
+  for (const auto *P : I.Predecessors)
+OS << P->ID << ",";
+  OS << "}, Succ{";
+  for (const auto *P : I.Successors)
+OS << P->ID << ",";
+  OS << "}}\n";
+  return OS;
+}
+
+void PrintTo(const CFGInterval::IntervalData &D, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << D;
+  *OS << Result;
+}
+
+void PrintTo(const CFGInterval &I, std::ostream *OS) {
+  std::string Result;
+  llvm::raw_string_ostream StringOS(Result);
+  StringOS << I;
+  *OS << Result;
+}
+
 namespace {
 
+using ::clang::analysis::BuildCFG;
+using ::clang::analysis::BuildResult;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Optional;
+using ::testing::UnorderedElementsAre;
+
+MATCHER_P(BlockID, ID, "") { return arg->getBlockID == ID; }
+MATCHER_P(IntervalID, ID, "") { return arg->ID == ID; }
+
+template 
+auto BlockIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Property(&CFGBlock::getBlockID, IDs)...);
+}
+
+template 
+auto IntervalIDs(T... IDs) {
+  return UnorderedElementsAre(
+  ::testing::Field(&CFGInterval::ID, IDs)...);
+}
+
+MATCHER_P3(IsBlockInterval, ID, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsBlockInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
+MATCHER_P4(IsNestedInterval, ID, Nodes, Preds, Succs, "") {
+  if (!std::holds_alternative>(arg.Data))
+return false;
+  auto &IntervalData = std::get>(arg.Data);
+
+  return testing::Matches(ID)(arg.ID) &&
+ testing::Matches(Nodes)(IntervalData.Nodes) &&
+ testing::Matches(Preds)(arg.Predecessors) &&
+ testing::Matches(Succs)(arg.Successors);
+}
+
 TEST(BuildInterval, PartitionSimpleOneInterval) {
 
   const char *Code = R"(void f() {
@@ -32,8 +134,8 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 3u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 3u);
 }
 
 TEST(BuildInterval, PartitionIfThenOneInterval) {
@@ -56,12 +158,10 @@
 
   auto &EntryBlock = cfg->getEntry();
 
-  CFGInterval I = buildInterval(EntryBlock);
-  EXPECT_EQ(I.Blocks.size(), 6u);
+  std::vector I = buildInterval(&EntryBlock);
+  EXPECT_EQ(I.size(), 6u);
 }
 
-using ::testing::UnorderedElementsAre;
-
 TEST(BuildInterval, PartitionWhileMultipleIntervals) {
 
   const char *Code = R"(void f() {
@@ -80,11 +180,11 @@
   CFGBlock *InitXBlock = *EntryBlock->succ_begin();
   CFGBlock *LoopHeadBlock = *InitXBlock->succ_begin();
 
-  CFGInterval I1 = buildInterval(*EntryBlock);
-  EXPECT_THAT(I1.Blocks, UnorderedElementsAre(EntryBlock, InitXBlock));
+  std::vector I1