Add debug-dot visualization for LifetimeConstraints.

PiperOrigin-RevId: 471752987
diff --git a/lifetime_analysis/analyze.cc b/lifetime_analysis/analyze.cc
index 53cc861..bbcfb0a 100644
--- a/lifetime_analysis/analyze.cc
+++ b/lifetime_analysis/analyze.cc
@@ -222,6 +222,34 @@
                       "}");
 }
 
+std::string ConstraintsEdgesDot(const ObjectRepository& object_repository,
+                                const LifetimeConstraints& constraints,
+                                absl::string_view name_prefix) {
+  std::vector<std::string> lines;
+
+  llvm::DenseSet<Lifetime> all_lifetimes;
+  for (const auto& cstr : constraints.AllConstraints()) {
+    lines.push_back(absl::StrFormat(R"("%1$s%2$d" -> "%1$s%3$d")", name_prefix,
+                                    cstr.second.Id(), cstr.first.Id()));
+    all_lifetimes.insert(cstr.first);
+    all_lifetimes.insert(cstr.second);
+  }
+
+  for (auto lftm : all_lifetimes) {
+    lines.push_back(absl::StrFormat(R"("%s%d"[label="%s"])", name_prefix,
+                                    lftm.Id(), lftm.DebugString()));
+  }
+
+  return absl::StrJoin(lines, ";\n");
+}
+
+std::string ConstraintsDot(const ObjectRepository& object_repository,
+                           const LifetimeConstraints& constraints) {
+  return absl::StrCat("digraph d {\n",
+                      ConstraintsEdgesDot(object_repository, constraints, ""),
+                      "}");
+}
+
 std::string CfgBlockLabel(const clang::CFGBlock* block, const clang::CFG& cfg,
                           const clang::ASTContext& ast_context) {
   std::string block_name = absl::StrCat("B", block->getBlockID());
@@ -310,6 +338,9 @@
         absl::StrAppend(&result,
                         PointsToEdgesDot(object_repository, lattice.PointsTo(),
                                          absl::StrCat("B", id, "_")));
+        absl::StrAppend(&result, ConstraintsEdgesDot(
+                                     object_repository, lattice.Constraints(),
+                                     absl::StrCat("B", id, "_cstr_")));
       }
     }
 
@@ -906,6 +937,8 @@
         analysis.object_repository.DebugString();
     (*debug_info)[func].points_to_map_dot =
         PointsToGraphDot(analysis.object_repository, analysis.points_to_map);
+    (*debug_info)[func].constraints_dot =
+        ConstraintsDot(analysis.object_repository, analysis.constraints);
   }
 
   if (llvm::Error err =
diff --git a/lifetime_analysis/analyze.h b/lifetime_analysis/analyze.h
index 2dc85a1..af4a5a9 100644
--- a/lifetime_analysis/analyze.h
+++ b/lifetime_analysis/analyze.h
@@ -33,6 +33,9 @@
 
   // A graph of the CFG in .dot file format.
   std::string cfg_dot;
+
+  // A graph of the constraints in .dot file format.
+  std::string constraints_dot;
 };
 
 // Returns if the two FunctionLifetimes have the same structures, without
diff --git a/lifetime_analysis/lifetime_constraints.h b/lifetime_analysis/lifetime_constraints.h
index 70ea271..62d5f87 100644
--- a/lifetime_analysis/lifetime_constraints.h
+++ b/lifetime_analysis/lifetime_constraints.h
@@ -35,6 +35,11 @@
     return outlives_constraints_ == other.outlives_constraints_;
   }
 
+  // Accessor for debug purposes.
+  const llvm::DenseSet<std::pair<Lifetime, Lifetime>>& AllConstraints() const {
+    return outlives_constraints_;
+  }
+
  private:
   // Constraints of the form p.first <= p.second
   llvm::DenseSet<std::pair<Lifetime, Lifetime>> outlives_constraints_;
diff --git a/lifetime_analysis/test/lifetime_analysis_test.cc b/lifetime_analysis/test/lifetime_analysis_test.cc
index b608eb6..d918d35 100644
--- a/lifetime_analysis/test/lifetime_analysis_test.cc
+++ b/lifetime_analysis/test/lifetime_analysis_test.cc
@@ -56,6 +56,9 @@
       SaveDotFile(debug_info.points_to_map_dot,
                   absl::StrCat(func, "_points_to"), test_name,
                   "Points-to map of exit block");
+      SaveDotFile(debug_info.constraints_dot,
+                  absl::StrCat(func, "_constraints"), test_name,
+                  "Constraint set at exit block");
       SaveDotFile(debug_info.cfg_dot, absl::StrCat(func, "_cfg"), test_name,
                   "Control-flow graph");
     }