Commit 73d78e87 authored by Carlos Galindo's avatar Carlos Galindo
Browse files

ConstrainedTabularAlgorithm.Work#lastEdgeType was not being taken into account for sets

- ConstrainedTabularAlgorithm:
  - Unified State and MState, moving the last edge type to Work.
  - For efficiency, don't store the last edge type unless it is relevant.
  - Removed assertions.
- ConstrainedSubsumedTabularAlgorithm: added map to speed-up lookup of subsumed work objects.
parent c71aa21c
Loading
Loading
Loading
Loading
+34 −2
Original line number Diff line number Diff line
package edg.slicing;

import edg.graph.EDG;
import edg.graph.Node;

import java.util.*;

/**
 * Constraind tabular algorithm with subsumption.
@@ -9,18 +12,47 @@ import edg.graph.EDG;
 * @see ConstrainedTabularAlgorithm
 */
public class ConstrainedSubsumedTabularAlgorithm extends ConstrainedTabularAlgorithm {

    protected Map<WorkNS, Set<Work>> subLookupMap;

    public ConstrainedSubsumedTabularAlgorithm(EDG edg) {
        super(edg);
    }

    @Override
    public Set<Node> slice(Node node) {
        subLookupMap = new HashMap<>();
        return super.slice(node);
    }

    @Override
    protected void propagate(Work work) {
        if (!pathEdge.contains(work)) {
            if (work.current().stack().isEdgeConstraintsEmpty() || pathEdge.stream().noneMatch(work::isSubsumedBy)) {
                workList.removeIf(w -> w.isSubsumedBy(work));
            WorkNS wns = new WorkNS(work);
            Set<Work> matchedWorks = subLookupMap.getOrDefault(wns, Collections.emptySet());
            if (work.current().stack().isEdgeConstraintsEmpty() ||
                    matchedWorks.stream()
                            .map(Work::current)
                            .map(State::stack)
                            .noneMatch(work.current().stack()::isSuffix)) {
                for (Work w : matchedWorks)
                    if (w.isSubsumedBy(work))
                        workList.remove(w);
                pathEdge.add(work);
                workList.add(work);
                if (!subLookupMap.containsKey(wns))
                    subLookupMap.put(wns, new HashSet<>());
                subLookupMap.get(wns).add(work);
            }
        }
    }

    /**
     * An equivalent to {@link Work} without a current stack, for faster lookup
     */
    protected record WorkNS(State context, Node currentNode) {
        public WorkNS(Work work) {
            this(work.context(), work.current().node());
        }
    }
}
+26 −82
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {

    /** Summary edges that have already been found, will be immediately applied
     *  when any of this map's keys are reached in the algorithm. */
    protected final Map<State, Set<MState>> summaryEdges = new HashMap<>();
    protected final Map<State, Set<State>> summaryEdges = new HashMap<>();
    protected final EDG edg;

    /** Works that haven't been processed yet, always a subset of {@link #pathEdge}. */
@@ -46,7 +46,7 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
    public Set<Node> slice(Node node) {
        workList = new PriorityQueue<>();
        pathEdge = new HashSet<>();
        propagate(new Work(new MState(node, new Constraints(), null)));
        propagate(new Work(new State(node, new Constraints())));
        workTheList();
        return pathEdge.stream().map(w -> w.current.node).collect(Collectors.toSet());
    }
@@ -102,7 +102,7 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
    protected void traverseEdges(Work work, Predicate<Edge> filter) {
        // Processing NodeWork
        Node node = work.current.node;
        Edge.Type leType = work.current.lastEdgeType;
        Edge.Type leType = work.lastEdgeType;

        for (Edge edge : edg.getEdges(node, LAST.Direction.Backwards)) {
            // Edge-skipping rules
@@ -115,22 +115,15 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
                continue;
            // Processing EdgeWork
            Node nextNode = edg.getEdgeSource(edge);
            assert work.context == null
                    || edg.getAncestor(nextNode, Node.Type.AnonymousRoutine) == null
                    || edg.getAncestor(work.context.node, Node.Type.AnonymousRoutine) == nextNode
                    || edg.getAncestor(work.context.node, Node.Type.AnonymousRoutine) == edg.getAncestor(nextNode, Node.Type.AnonymousRoutine):
                    String.format("Illegal context switch in edge %d -> %d (%s)", node.getId(), nextNode.getId(), edge.getType());
            assert work.context == null
                    || edg.getAncestor(work.context.node, Node.Type.Routine) == nextNode
                    || edg.getAncestor(nextNode, Node.Type.Routine) == null
                    || edg.getAncestor(work.context.node, Node.Type.Routine) == edg.getAncestor(nextNode, Node.Type.Routine):
                    String.format("Illegal context switch in edge %d -> %d (%s)", node.getId(), nextNode.getId(), edge.getType());
            EdgeConstraint edgeCons = edge.getConstraint();
            EdgeConstraint topConstraint = work.current.stack.isEdgeConstraintsEmpty() ? null : work.current.stack.peekEdgeConstraint();
            List<Constraints> resolvedList = edgeCons.resolve(Phase.Tabular, edg, edge, (Constraints) work.current.stack.clone(), topConstraint, 0);

            for (Constraints resolved : resolvedList)
                propagate(new Work(work, new MState(nextNode, resolved, edge.getType())));
                if (edge.getType() == Edge.Type.Structural || edge.getType() == Edge.Type.Control)
                    propagate(new Work(work, new State(nextNode, resolved), edge.getType()));
                else // no need to store the type, type only matters for structural and control.
                    propagate(new Work(work, new State(nextNode, resolved)));
        }
    }

@@ -146,15 +139,7 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
     * actual-in, actual-out pair (must be in the same call). If a newly edge is added,
     * all actual-out nodes that have been reached immediately traverse the new summary edge.
     */
    protected void createSummaryEdges(MState formalIn, State formalOut) {
        assert edg.getAncestor(formalIn.node, Node.Type.AnonymousRoutine) == formalOut.node
                || edg.getAncestor(formalOut.node, Node.Type.AnonymousRoutine) == null
                || edg.getAncestor(formalIn.node, Node.Type.AnonymousRoutine) == edg.getAncestor(formalOut.node, Node.Type.AnonymousRoutine):
                String.format("Illegal context switch in potential summary edge %d -> %d", formalIn.node.getId(), formalOut.node.getId());
        assert edg.getAncestor(formalIn.node, Node.Type.Routine) == formalOut.node
                || edg.getAncestor(formalOut.node, Node.Type.Routine) == null
                || edg.getAncestor(formalIn.node, Node.Type.Routine) == edg.getAncestor(formalOut.node, Node.Type.Routine):
                String.format("Illegal context switch in potential summary edge %d -> %d", formalIn.node.getId(), formalOut.node.getId());
    protected void createSummaryEdges(State formalIn, State formalOut) {
        List<Node> actualOutNodes = edg.getNodes(formalOut.node, LAST.Direction.Forwards, Edge.Type.Output);
        for (Node aInNode : edg.getNodes(formalIn.node, LAST.Direction.Backwards, Edge.Type.Input)) {
            Node call = edg.getParent(aInNode).getType() == Node.Type.Arguments ?
@@ -164,7 +149,7 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
                Node aOutNode = actualOutNodes.get(i);
                if (call.equals(edg.getNodeFromRes(aOutNode))) {
                    toBeRemoved = i;
                    MState actualIn = new MState(aInNode, formalIn.stack, Edge.Type.Summary);
                    State actualIn = new State(aInNode, formalIn.stack);
                    State actualOut = new State(aOutNode, formalOut.stack);
                    if (!summaryEdges.containsKey(actualOut) || !summaryEdges.get(actualOut).contains(actualIn)) {
                        if (!summaryEdges.containsKey(actualOut))
@@ -172,7 +157,6 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
                        summaryEdges.get(actualOut).add(actualIn);
                        Set<Work> reached = new HashSet<>();
                        for (Work w : pathEdge)
                            //noinspection EqualsBetweenInconvertibleTypes
                            if (actualOut.equals(w.current))
                                reached.add(w);
                        for (Work w : reached)
@@ -187,10 +171,8 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
    }

    protected void traverseSummaries(Work work) {
        //noinspection SuspiciousMethodCalls
        if (summaryEdges.containsKey(work.current))
            //noinspection SuspiciousMethodCalls
            for (MState actualIn : summaryEdges.get(work.current))
            for (State actualIn : summaryEdges.get(work.current))
                propagate(new Work(work, actualIn));
    }

@@ -199,11 +181,9 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
     * node that constitutes the edge's source. The reached node is its own context.
     * @param actualOut The edge's target.
     */
    protected void contextSwitch(MState actualOut) {
    protected void contextSwitch(State actualOut) {
        for (Node formalOutNode : edg.getNodes(actualOut.node, LAST.Direction.Backwards, Edge.Type.Output))
            propagate(new Work(
                    new State(formalOutNode, actualOut.stack),
                    new MState(formalOutNode, actualOut.stack, Edge.Type.Output)));
            propagate(new Work(new State(formalOutNode, actualOut.stack), new State(formalOutNode, actualOut.stack)));
    }

    /** Checks whether a given node acts as the Enter node for a function. */
@@ -229,13 +209,21 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
     * <br>
     * Given a collection of works, the set of current nodes represents the slice.
     */
    protected record Work(State context, MState current) implements Comparable<Work> {
        public Work(MState current) {
            this((State) null, current);
    protected record Work(State context, State current, Edge.Type lastEdgeType) implements Comparable<Work> {
        public Work(State current) {
            this((State) null, current, null);
        }

        public Work(Work work, MState current) {
            this(work.context, current);
        public Work(State context, State current) {
            this(context, current, null);
        }

        public Work(Work work, State current) {
            this(work.context, current, null);
        }

        public Work(Work work, State current, Edge.Type lastEdgeType) {
            this(work.context, current, lastEdgeType);
        }

        /**
@@ -256,49 +244,5 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
        }
    }

    protected record State(Node node, Constraints stack) {
        @Override
        public boolean equals(Object obj) {
            if (obj == null)
                return false;
            if (obj instanceof MState m)
                return node.equals(m.node) && stack.equals(m.stack);
            else if (obj instanceof State s)
                return node.equals(s.node) && stack.equals(s.stack);
            return false;
        }

        @Override
        public int hashCode() {
            return Objects.hash(node, stack);
        }

        @Override
        public String toString() {
            return String.format("State[node=%d,stack=%s]", node.getId(), stack.getEdgeConstraints().toString());
        }
    }

    protected record MState(Node node, Constraints stack, Edge.Type lastEdgeType) {
        @Override
        public boolean equals(Object obj) {
            if (obj == null)
                return false;
            if (obj instanceof MState m)
                return node.equals(m.node) && stack.equals(m.stack);
            else if (obj instanceof State s)
                return node.equals(s.node) && stack.equals(s.stack);
            return false;
        }

        @Override
        public int hashCode() {
            return Objects.hash(node, stack);
        }

        @Override
        public String toString() {
            return String.format("State[node=%d,stack=%s,type=%s]", node.getId(), stack.getEdgeConstraints(), lastEdgeType != null ? lastEdgeType : "none");
        }
    }
    protected record State(Node node, Constraints stack) {}
}