[𝘀𝗽𝗿] initial version

Created using spr 1.3.7
diff --git a/src/trace_processor/plugins/dominator_tree/dominator_tree.cc b/src/trace_processor/plugins/dominator_tree/dominator_tree.cc
index 674346c..fd15ef5 100644
--- a/src/trace_processor/plugins/dominator_tree/dominator_tree.cc
+++ b/src/trace_processor/plugins/dominator_tree/dominator_tree.cc
@@ -89,13 +89,18 @@
     }
   }
 
-  // Returns the TreeNumber for a given Node.
+  // Returns the semi-dominator TreeNumber for a given Node. v must satisfy
+  // IsInTree(v).
   TreeNumber GetSemiDominator(Node v) const {
-    // Note: if you happen to see this check failing, it's likely a problem that
-    // the graph has nodes which are not reachable from the root node.
     return *GetStateForNode(v).semi_dominator;
   }
 
+  // Returns whether the given node was visited by RunDfs and is therefore
+  // part of the DFS spanning tree.
+  bool IsInTree(Node v) const {
+    return GetStateForNode(v).semi_dominator.has_value();
+  }
+
   // Returns the number of nodes in the tree (== the number of nodes in
   // the graph.)
   uint32_t node_count_in_tree() const {
@@ -149,8 +154,14 @@
     a = ancestor;
   }
 
-  // Corresponds to the "Eval" function in the paper.
-  Node GetMinSemiDominatorToAncestor(Node vertex, const Graph& graph) {
+  // Corresponds to the "Eval" function in the paper. Returns nullopt if
+  // `vertex` was not visited by the graph's DFS; such a node has no
+  // meaningful semi-dominator to report.
+  std::optional<Node> GetMinSemiDominatorToAncestor(Node vertex,
+                                                    const Graph& graph) {
+    if (!graph.IsInTree(vertex)) {
+      return std::nullopt;
+    }
     NodeState& state = GetStateForNode(vertex);
     if (!state.ancestor) {
       return vertex;
@@ -239,9 +250,12 @@
     Node w = GetNodeForTreeNumber(TreeNumber{i});
     NodeState& w_state = GetStateForNode(w);
     for (Node v : w_state.predecessors) {
-      Node u = forest.GetMinSemiDominatorToAncestor(v, *this);
+      auto u = forest.GetMinSemiDominatorToAncestor(v, *this);
+      if (!u) {
+        continue;
+      }
       w_state.semi_dominator =
-          std::min(*w_state.semi_dominator, GetSemiDominator(u));
+          std::min(*w_state.semi_dominator, GetSemiDominator(*u));
     }
     NodeState& semi_dominator_state =
         GetStateForNode(GetNodeForTreeNumber(*w_state.semi_dominator));
@@ -253,7 +267,10 @@
 
     NodeState& w_parent_state = GetStateForNode(w_parent);
     for (Node v : w_parent_state.self_as_semi_dominator) {
-      Node u = forest.GetMinSemiDominatorToAncestor(v, *this);
+      // v is always in-tree: it was pushed onto some node's
+      // self_as_semi_dominator list in an earlier iteration of this loop,
+      // which only runs over tree nodes.
+      Node u = *forest.GetMinSemiDominatorToAncestor(v, *this);
       NodeState& v_state = GetStateForNode(v);
       v_state.dominator =
           GetSemiDominator(u) < v_state.semi_dominator ? u : w_parent;
diff --git a/test/trace_processor/diff_tests/stdlib/graphs/dominator_tree_tests.py b/test/trace_processor/diff_tests/stdlib/graphs/dominator_tree_tests.py
index 1287b7c..e664349 100644
--- a/test/trace_processor/diff_tests/stdlib/graphs/dominator_tree_tests.py
+++ b/test/trace_processor/diff_tests/stdlib/graphs/dominator_tree_tests.py
@@ -159,6 +159,31 @@
         12,10
         """))
 
+  def test_unreachable_predecessor(self):
+    return DiffTestBlueprint(
+        trace=DataPath('counters.json'),
+        query="""
+          INCLUDE PERFETTO MODULE graphs.dominator_tree;
+
+          CREATE PERFETTO TABLE foo AS
+          SELECT NULL AS source_node_id, NULL AS dest_node_id WHERE FALSE
+          UNION ALL
+          VALUES (0, 1), (1, 2), (2, 3)
+          UNION ALL
+          VALUES (99, 2), (98, 99);
+
+          SELECT *
+          FROM graph_dominator_tree!(foo, 0)
+          ORDER BY node_id;
+        """,
+        out=Csv("""
+        "node_id","dominator_node_id"
+        0,"[NULL]"
+        1,0
+        2,1
+        3,2
+        """))
+
   def test_forest(self):
     return DiffTestBlueprint(
         trace=DataPath('counters.json'),