Skip to content

Conversation

@marcus-h
Copy link
Contributor

First commit: Fix edge removal in the CallGraph class

If CallGraph.removeAllEdgesOutOf or CallGraph.removeEdges is called,
the corresponding edge(s) is not entirely removed from the CallGraph
data structures.

  • removeAllEdgesOutOf(Unit u):
    The to be removed edges are not removed from the call graph data structures.
    For each to be removed edge e, the code first calls e.remove(), which sets
    the Edge.invalid attribute to true. Afterwards, removeEdge(e, false) is
    calledonly executed. The edge removal code in removeEdge(...) is only
    executed, if the edge e is contained in the edges set. Since Edge.invalid
    was set to true, e.hashCode() returns the constant value 0. Since the return
    value of e.hashCode() was different before Edge.invalid was set to true, the
    edge cannot be found in the edges set. Hence, no edge removal code is
    executed.
    In order to fix this, do not call e.remove() before calling removeEdge(...).
    Moreoever, the call to e.remove() is not needed because it is already called
    in removeEdge(...).

  • removeEdges(Collection edges):
    First, this.edges.removeAll(edges) is executed and afterwards, for each
    edge e in the passed edges collection, removeEdge(e, false) is executed.
    Since the edge is not contained anymore in the this.edges set, no edge
    removal code in removeEdge(...) is executed.
    In order to fix this, do not call this.edges.removeAll(edges).

The expected behavior is documented in the CallGraphTest test case.

Second commit: Improve the runtime of CallGraph.removeAllEdgesOutOf a bit

Instead of iterating over all callgraph edges to find all edges that
originate from a specific unit, simply call edgesOutOf(...) (which
internally uses a LinkedHashMap for the initial lookup).
This seems to cause a noticeable speedup in UnreachableCodeEliminator.

Marcus Huewe added 2 commits December 12, 2025 17:48
If CallGraph.removeAllEdgesOutOf or CallGraph.removeEdges is called,
the corresponding edge(s) is not entirely removed from the CallGraph
data structures.

- removeAllEdgesOutOf(Unit u):
  The to be removed edges are not removed from the call graph data structures.
  For each to be removed edge e, the code first calls e.remove(), which sets
  the Edge.invalid attribute to true. Afterwards, removeEdge(e, false) is
  calledonly executed. The edge removal code in removeEdge(...) is only
  executed, if the edge e is contained in the edges set. Since Edge.invalid
  was set to true, e.hashCode() returns the constant value 0. Since the return
  value of e.hashCode() was different before Edge.invalid was set to true, the
  edge cannot be found in the edges set. Hence, no edge removal code is
  executed.
  In order to fix this, do not call e.remove() before calling removeEdge(...).
  Moreoever, the call to e.remove() is not needed because it is already called
  in removeEdge(...).

- removeEdges(Collection<Edge> edges):
  First, this.edges.removeAll(edges) is executed and afterwards, for each
  edge e in the passed edges collection, removeEdge(e, false) is executed.
  Since the edge is not contained anymore in the this.edges set, no edge
  removal code in removeEdge(...) is executed.
  In order to fix this, do not call this.edges.removeAll(edges).

The expected behavior is documented in the CallGraphTest test case.
Instead of iterating over all callgraph edges to find all edges that
originate from a specific unit, simply call edgesOutOf(...) (which
internally uses a LinkedHashMap for the initial lookup).
This seems to cause a noticeable speedup in UnreachableCodeEliminator.
@StevenArzt StevenArzt merged commit baa5166 into soot-oss:develop Dec 15, 2025
5 checks passed
@StevenArzt
Copy link
Contributor

Thank you for the good analysis and the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants