Skip to content

Commit

Permalink
Further optimizations for CMP and unused values
Browse files Browse the repository at this point in the history
  • Loading branch information
mirkosertic committed Apr 23, 2024
1 parent 1786aea commit 5d2c0f3
Show file tree
Hide file tree
Showing 22 changed files with 190 additions and 222 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ public Node[] outgoingDataFlowsFor(final Node n) {
for (final Node t : nodes) {
final Node[] incoming = t.incomingDataFlows;

for (int i = 0; i <incoming.length; i++) {
if (incoming[i] == n) {
for (final Node node : incoming) {
if (node == n) {
result.add(t);
}
}
Expand Down Expand Up @@ -462,8 +462,8 @@ void deleteFromControlFlowInternally(final ControlTokenConsumer consumer) {
if (consumer.hasIncomingBackEdges()) {
throw new IllegalStateException("Cannot delete node with incoming back edges! Node Type is " + consumer.nodeType + " #" + consumer.owner.nodes.indexOf(consumer));
}
if (consumer.controlFlowsTo.size() != 1) {
throw new IllegalStateException("Can only delete nodes with exactly one outgoing edge! Node Type is " + consumer.nodeType + " #" + consumer.owner.nodes.indexOf(consumer));
if (consumer.controlFlowsTo.size() > 1) {
throw new IllegalStateException("Can only delete nodes with exactly zero or one outgoing edge! Node Type is " + consumer.nodeType + " #" + consumer.owner.nodes.indexOf(consumer));
}
if (consumer.controlFlowsTo.keySet().stream().anyMatch(t -> t.edgeType() == EdgeType.BACK)) {
throw new IllegalStateException("Can only delete nodes without outgoing back edges! Node Type is " + consumer.nodeType + " #" + consumer.owner.nodes.indexOf(consumer));
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/de/mirkosertic/bytecoder/core/ir/Nop.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ public class Nop extends ControlTokenConsumer {
public Nop stampInto(final Graph target) {
return target.newNop();
}

public void deleteFromControlFlow() {
owner.deleteFromControlFlowInternally(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2024 Mirko Sertic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.mirkosertic.bytecoder.core.optimizer;

import de.mirkosertic.bytecoder.core.backend.BackendType;
import de.mirkosertic.bytecoder.core.ir.CMP;
import de.mirkosertic.bytecoder.core.ir.Graph;
import de.mirkosertic.bytecoder.core.ir.Node;
import de.mirkosertic.bytecoder.core.ir.NodeType;
import de.mirkosertic.bytecoder.core.ir.NumericalTest;
import de.mirkosertic.bytecoder.core.ir.PrimitiveInt;
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
import de.mirkosertic.bytecoder.core.parser.CompileUnit;

import java.util.Stack;

public class CMPInNumericalTestOptimizer implements Optimizer {

@Override
public boolean optimize(final BackendType backendType, final CompileUnit compileUnit, final ResolvedMethod method) {
boolean changed = false;

final Graph g = method.methodBody;

// Variable and Constant propagation
final Stack<NumericalTest> workingQueue = new Stack<>();

g.nodes().stream().filter(t -> t.nodeType == NodeType.NumericalTest && t.incomingDataFlows[0].nodeType == NodeType.CMP && t.incomingDataFlows[1].nodeType == NodeType.PrimitiveInt).map(t -> (NumericalTest) t).forEach(workingQueue::push);

while (!workingQueue.isEmpty()) {
final NumericalTest test = workingQueue.pop();
final CMP compareExpression = (CMP) test.incomingDataFlows[0];
final Node a = compareExpression.incomingDataFlows[0];
final Node b = compareExpression.incomingDataFlows[1];
final PrimitiveInt compareValue = (PrimitiveInt) test.incomingDataFlows[1];
if (compareValue.value == 0) {
switch (test.operation) {
case LT: {
// Compare operation must be -1, a < b
final NumericalTest replacement = g.newNumericalTest(NumericalTest.Operation.LT);
replacement.addIncomingData(a, b);
g.remapDataFlow(test, replacement);

test.removeFromIncomingData(compareExpression);
g.deleteNode(test);
g.deleteNode(compareExpression);
changed = true;
break;
}
case GT: {
// Compare must be 1, a > b
final NumericalTest replacement = g.newNumericalTest(NumericalTest.Operation.GT);
replacement.addIncomingData(a, b);
g.remapDataFlow(test, replacement);

test.removeFromIncomingData(compareExpression);
g.deleteNode(test);
g.deleteNode(compareExpression);
changed = true;
break;
}
case GE: {
// Compare must be >= 0, a >= b
final NumericalTest replacement = g.newNumericalTest(NumericalTest.Operation.GE);
replacement.addIncomingData(a, b);
g.remapDataFlow(test, replacement);

test.removeFromIncomingData(compareExpression);
g.deleteNode(test);
g.deleteNode(compareExpression);
changed = true;
break;
}
case LE: {
// Compare must be <= 0, a <= b
final NumericalTest replacement = g.newNumericalTest(NumericalTest.Operation.LE);
replacement.addIncomingData(a, b);
g.remapDataFlow(test, replacement);

test.removeFromIncomingData(compareExpression);
g.deleteNode(test);
g.deleteNode(compareExpression);
changed = true;
break;
}
}
}
}

return changed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
import de.mirkosertic.bytecoder.core.parser.CompileUnit;

import java.util.Arrays;
import java.util.Stack;

public class CopyToUnusedPHIOrVariable implements Optimizer {
Expand Down Expand Up @@ -63,15 +62,6 @@ public boolean optimize(final BackendType backendType, final CompileUnit compile
g.deleteNode(copyTarget);
changed = true;
}
} else {
if (Arrays.stream(copyTarget.incomingDataFlows).noneMatch(t -> t.nodeType != NodeType.Copy)) {
if (workingItem.controlFlowsTo.keySet().stream().noneMatch(t -> t.edgeType() == EdgeType.BACK)) {
copyTarget.removeFromIncomingData(workingItem);
workingItem.deleteFromControlFlow();

changed = true;
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@
import de.mirkosertic.bytecoder.core.backend.BackendType;
import de.mirkosertic.bytecoder.core.backend.sequencer.DominatorTree;
import de.mirkosertic.bytecoder.core.ir.ClassInitialization;
import de.mirkosertic.bytecoder.core.ir.ControlTokenConsumer;
import de.mirkosertic.bytecoder.core.ir.Graph;
import de.mirkosertic.bytecoder.core.ir.NodeType;
import de.mirkosertic.bytecoder.core.ir.ResolvedClass;
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
import de.mirkosertic.bytecoder.core.parser.CompileUnit;

import java.util.ArrayList;
import java.util.Stack;

public class DeleteRedundantClassInitializations implements GlobalOptimizer {
public class DeleteRedundantClassInitializations implements Optimizer {

public DeleteRedundantClassInitializations() {
}
Expand All @@ -46,16 +46,18 @@ public boolean optimize(final BackendType backendType, final CompileUnit compile
while (!workingQueue.empty()) {
final ClassInitialization ci = workingQueue.pop();
final ResolvedClass rc = compileUnit.findClass(ci.type);
if (method.owner != null && method.methodNode != null && !rc.requiresClassInitializer() && method.owner.allTypesOf().contains(rc)) {
final boolean requiredClassInit = rc.requiresClassInitializer();
if (method.owner != null && !requiredClassInit && method.methodNode != null && method.owner.allTypesOf().contains(rc)) {
ci.deleteFromControlFlow();
} else if (!rc.requiresClassInitializer()) {
} else if (!requiredClassInit) {
ci.deleteFromControlFlow();
} else {
for (final ClassInitialization j : new ArrayList<>(workingQueue)) {
if (j != ci && ci.type.equals(j.type)) {
if (dominatorTree.dominates(ci, j)) {
j.deleteFromControlFlow();
workingQueue.remove(j);
for (final ControlTokenConsumer dominated : dominatorTree.domSetOf(ci)) {
if (dominated.nodeType == NodeType.ClassInitialization && dominated != ci) {
final ClassInitialization domClassInit = (ClassInitialization) dominated;
if (ci.type.equals(domClassInit.type)) {
domClassInit.deleteFromControlFlow();
workingQueue.remove(domClassInit);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import de.mirkosertic.bytecoder.core.ir.EdgeType;
import de.mirkosertic.bytecoder.core.ir.Graph;
import de.mirkosertic.bytecoder.core.ir.NodeType;
import de.mirkosertic.bytecoder.core.ir.Nop;
import de.mirkosertic.bytecoder.core.ir.Region;
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
import de.mirkosertic.bytecoder.core.parser.CompileUnit;
Expand All @@ -37,6 +38,12 @@ public boolean optimize(final BackendType backendType, final CompileUnit compile
changed = true;
}
}
for (final Nop n : g.nodes().stream().filter(t -> t.nodeType == NodeType.Nop).map(t -> (Nop) t).collect(Collectors.toList())) {
if (n.controlComingFrom.size() == 1 && n.controlFlowsTo.size() == 1 && n.controlFlowsTo.keySet().iterator().next().edgeType() == EdgeType.FORWARD) {
n.deleteFromControlFlow();
changed = true;
}
}

return changed;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import de.mirkosertic.bytecoder.core.backend.BackendType;
import de.mirkosertic.bytecoder.core.ir.Graph;
import de.mirkosertic.bytecoder.core.ir.Node;
import de.mirkosertic.bytecoder.core.ir.NodeType;
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
import de.mirkosertic.bytecoder.core.ir.Value;
import de.mirkosertic.bytecoder.core.parser.CompileUnit;
Expand All @@ -30,9 +31,16 @@ public class DropUnusedValues implements Optimizer {
public boolean optimize(final BackendType backendType, final CompileUnit compileUnit, final ResolvedMethod method) {
final Graph g = method.methodBody;
boolean changed = false;
for (final Node value : g.nodes().stream().filter(t -> (t.incomingDataFlows.length == 0 && t instanceof Value && t.outgoingDataFlows().length == 0)).collect(Collectors.toList())) {
g.deleteNode(value);
changed = true;
for (final Node value : g.nodes().stream().filter(t -> (t instanceof Value && t.outgoingDataFlows().length == 0)).collect(Collectors.toList())) {
if (value.nodeType == NodeType.Variable || value.nodeType == NodeType.PHI || value.isConstant()) {
if (value.incomingDataFlows.length == 0) {
g.deleteNode(value);
changed = true;
}
} else {
g.deleteNode(value);
changed = true;
}
}

return changed;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
import de.mirkosertic.bytecoder.core.parser.CompileUnit;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
Expand Down Expand Up @@ -84,11 +85,20 @@ public boolean optimize(final BackendType backendType, final CompileUnit compile
for (final Node dataFlowsTo : token.outgoingDataFlows()) {
dataFlowsTo.removeFromIncomingData(token);
}
for (final ControlTokenConsumer pred : token.controlComingFrom) {
for (final Projection p : new HashSet<>(pred.controlFlowsTo.keySet())) {
if (pred.controlFlowsTo.get(p) == token) {
pred.controlFlowsTo.remove(p);
}
}
}
token.controlComingFrom.clear();
g.deleteNode(token);
}

replacementForIf.controlComingFrom.remove(workingItem);
g.replaceInControlFlow(workingItem, replacementForIf);
g.deleteNode(workingItem.incomingDataFlows[0]);
g.deleteNode(workingItem);

changed = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,14 @@ public boolean optimize(final BackendType backendType, final CompileUnit compile
}

// thisRef and arguments must be variable, phi or constant to do a valid transformation
// Convert all incoming data flows into variables
boolean valid = true;
if (thisRef != null) {
if (!Utils.isVariablePHIOrConstant(thisRef)) {
if (!Utils.isVariablePHIOrConstant(thisRef) && !thisRef.hasSideSideEffectRecursive()) {
valid = false;
}
}
for (final Node argument : arguments) {
if (!Utils.isVariablePHIOrConstant(argument)) {
if (!Utils.isVariablePHIOrConstant(argument) && !argument.hasSideSideEffectRecursive()) {
valid = false;
}
}
Expand Down
Loading

0 comments on commit 5d2c0f3

Please sign in to comment.