Skip to content

Commit

Permalink
Alloc optimizations in varversions
Browse files Browse the repository at this point in the history
  • Loading branch information
jaskarth committed Oct 13, 2023
1 parent 3570365 commit 2cac62e
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 64 deletions.
3 changes: 2 additions & 1 deletion src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class BasicBlock implements IGraphNode {
Expand Down Expand Up @@ -174,7 +175,7 @@ public int getId() {
}

@Override
public List<? extends IGraphNode> getPredecessors() {
public Collection<? extends IGraphNode> getPredecessors() {
List<BasicBlock> lst = new ArrayList<>(preds);
lst.addAll(predExceptions);
return lst;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.function.Predicate;

public final class ValidationHelper {
private static final boolean VALIDATE = System.getProperty("VALIDATE_DECOMPILED_CODE", "false").equals("true");
public static final boolean VALIDATE = System.getProperty("VALIDATE_DECOMPILED_CODE", "false").equals("true");

public static void validateStatement(RootStatement statement) {
if (!VALIDATE) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.modules.decompiler.decompose;

import java.util.List;
import java.util.Collection;

public interface IGraphNode {

List<? extends IGraphNode> getPredecessors();
Collection<? extends IGraphNode> getPredecessors();
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ public class VarVersionNode implements IGraphNode {
}

@Override
public List<IGraphNode> getPredecessors() {
// TODO: does this have to return a list?
return new ArrayList<>(this.predecessors);
public Collection<VarVersionNode> getPredecessors() {
return this.predecessors;
}

public void removeSuccessor(VarVersionNode node) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public VarVersionNode createNode(VarVersionPair ver) {
}

public VarVersionNode createNode(VarVersionPair ver, LocalVariable lvt) {
VarVersionNode node;
this.nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version, lvt), ver);
VarVersionNode node = new VarVersionNode(ver.var, ver.version, lvt);
this.nodes.addWithKey(node, ver);
return node;
}

Expand Down Expand Up @@ -71,51 +71,53 @@ public void initDominators() {
}

// TODO: optimization!! This is called multiple times for each method and the allocations will add up!
Set<VarVersionNode> reached = rootReachability(roots);
ValidationHelper.validateTrue(this.nodes.size() == reached.size(), "Cyclic roots detected");
// If the nodes we reach don't include every node we have, then we need to process further to decompose the cycles
//noinspection ConstantValue
if (this.nodes.size() != reached.size()) {
// Not all nodes are reachable, due to cyclic nodes

// Find only the nodes that aren't accounted for
Set<VarVersionNode> intersection = new HashSet<>(this.nodes);
intersection.removeAll(reached);

// Var -> [versions]
Map<Integer, List<Integer>> varMap = new HashMap<>();

Set<VarVersionNode> visited = new HashSet<>();
for (VarVersionNode node : intersection) {
if (visited.contains(node)) {
continue;
}
if (ValidationHelper.VALIDATE) {
Set<VarVersionNode> reached = rootReachability(roots);
ValidationHelper.validateTrue(this.nodes.size() == reached.size(), "Cyclic roots detected");
// If the nodes we reach don't include every node we have, then we need to process further to decompose the cycles
//noinspection ConstantValue
if (this.nodes.size() != reached.size()) {
// Not all nodes are reachable, due to cyclic nodes

// Find only the nodes that aren't accounted for
Set<VarVersionNode> intersection = new HashSet<>(this.nodes);
intersection.removeAll(reached);

// Var -> [versions]
Map<Integer, List<Integer>> varMap = new HashMap<>();

Set<VarVersionNode> visited = new HashSet<>();
for (VarVersionNode node : intersection) {
if (visited.contains(node)) {
continue;
}

// DFS to find all nodes reachable from this node
Set<VarVersionNode> found = this.findReachableNodes(node);
// Skip all the found nodes from this node in the future
visited.addAll(found);
// DFS to find all nodes reachable from this node
Set<VarVersionNode> found = this.findReachableNodes(node);
// Skip all the found nodes from this node in the future
visited.addAll(found);

// For every node that we found, keep track of the var index and the versions of each node
// Each disjoint set *should* only reference a single var, so we operate under that assumption and keep track of the versions based on the var index
// If this isn't true, then this algorithm won't find every cyclic root as it will account multiple disjoint sets as one!
for (VarVersionNode foundNode : found) {
varMap.computeIfAbsent(foundNode.var, k -> new ArrayList<>()).add(foundNode.version);
// For every node that we found, keep track of the var index and the versions of each node
// Each disjoint set *should* only reference a single var, so we operate under that assumption and keep track of the versions based on the var index
// If this isn't true, then this algorithm won't find every cyclic root as it will account multiple disjoint sets as one!
for (VarVersionNode foundNode : found) {
varMap.computeIfAbsent(foundNode.var, k -> new ArrayList<>()).add(foundNode.version);
}
}
}

for (Integer var : varMap.keySet()) {
// Sort versions
varMap.get(var).sort(Comparator.naturalOrder());
for (Integer var : varMap.keySet()) {
// Sort versions
varMap.get(var).sort(Comparator.naturalOrder());

// First version is the lowest version, so that can be considered as the root
VarVersionPair pair = new VarVersionPair(var, varMap.get(var).get(0));
// First version is the lowest version, so that can be considered as the root
VarVersionPair pair = new VarVersionPair(var, varMap.get(var).get(0));

// Add to existing roots
roots.add(this.nodes.getWithKey(pair));
}
// Add to existing roots
roots.add(this.nodes.getWithKey(pair));
}

// TODO: needs another validation pass?
// TODO: needs another validation pass?
}
}

this.engine = new GenericDominatorEngine(new IGraph() {
Expand All @@ -126,7 +128,7 @@ public List<? extends IGraphNode> getReversePostOrderList() {

@Override
public Set<? extends IGraphNode> getRoots() {
return new HashSet<IGraphNode>(roots);
return roots;
}
});

Expand Down Expand Up @@ -216,12 +218,15 @@ public boolean areVarsAnalogous(int varBase, int varCheck) {
}

private static List<VarVersionNode> getReversedPostOrder(Collection<VarVersionNode> roots) {
List<VarVersionNode> lst = new LinkedList<>();
List<VarVersionNode> lst = new ArrayList<>();
Set<VarVersionNode> setVisited = new HashSet<>();
Deque<VarVersionNode> stackNode = new ArrayDeque<>();
Deque<Integer> stackIndex = new ArrayDeque<>();
List<VarVersionNode> lstSuccs = new ArrayList<>();

for (VarVersionNode root : roots) {
List<VarVersionNode> lstTemp = new LinkedList<>();
addToReversePostOrderListIterative(root, lstTemp, setVisited);
List<VarVersionNode> lstTemp = new ArrayList<>();
addToReversePostOrderListIterative(root, lstTemp, setVisited, stackNode, stackIndex, lstSuccs);
lst.addAll(lstTemp);
}

Expand All @@ -231,20 +236,25 @@ private static List<VarVersionNode> getReversedPostOrder(Collection<VarVersionNo
private static void addToReversePostOrderListIterative(
VarVersionNode root,
List<? super VarVersionNode> lst,
Set<? super VarVersionNode> setVisited) {
ListStack<VarVersionNode> stackNode = new ListStack<>();
ListStack<Integer> stackIndex = new ListStack<>();
Set<? super VarVersionNode> setVisited,
Deque<VarVersionNode> stackNode,
Deque<Integer> stackIndex,
List<VarVersionNode> lstSuccs
) {
stackNode.clear();
stackIndex.clear();

stackNode.add(root);
stackIndex.add(0);

while (!stackNode.isEmpty()) {
VarVersionNode node = stackNode.peek();
int index = stackIndex.pop();
VarVersionNode node = stackNode.peekLast();
int index = stackIndex.removeLast();

setVisited.add(node);

List<VarVersionNode> lstSuccs = new ArrayList<>(node.successors);
lstSuccs.clear();
lstSuccs.addAll(node.successors);
for (; index < lstSuccs.size(); index++) {
VarVersionNode succ = lstSuccs.get(index);

Expand All @@ -258,7 +268,7 @@ private static void addToReversePostOrderListIterative(

if (index == lstSuccs.size()) {
lst.add(0, node);
stackNode.pop();
stackNode.removeLast();
}
}
}
Expand Down
15 changes: 9 additions & 6 deletions src/org/jetbrains/java/decompiler/struct/match/MatchNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,15 @@ public List<MatchNode> getChildren() {

public boolean iterateRules(BiFunction<MatchProperties, RuleValue, Boolean> consumer) {
// Make sure that the iterator succeeds for every rule in the map
return rules
.entrySet()
.stream()
.allMatch(e -> e.getValue()
.stream()
.allMatch(rule -> consumer.apply(e.getKey(), rule)));
for (Map.Entry<MatchProperties, List<RuleValue>> e : rules.entrySet()) {
for (RuleValue rule : e.getValue()) {
if (!consumer.apply(e.getKey(), rule)) {
return false;
}
}
}

return true;
}

public RuleValue getRawRule(MatchProperties property) {
Expand Down

0 comments on commit 2cac62e

Please sign in to comment.