Merge optimizations-package into latest

This commit is contained in:
Victor 2016-06-26 11:53:16 +03:00
commit 46349bdd36
12 changed files with 270 additions and 207 deletions

View File

@ -1,40 +1,31 @@
package com.annimon.ownlang.parser; package com.annimon.ownlang.parser;
import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.Statement;
import com.annimon.ownlang.parser.visitors.ConstantFolding; import com.annimon.ownlang.parser.optimization.ConstantFolding;
import com.annimon.ownlang.parser.visitors.ConstantPropagation; import com.annimon.ownlang.parser.optimization.ConstantPropagation;
import com.annimon.ownlang.parser.visitors.DeadCodeElimination; import com.annimon.ownlang.parser.optimization.DeadCodeElimination;
import com.annimon.ownlang.parser.visitors.ExpressionSimplification; import com.annimon.ownlang.parser.optimization.ExpressionSimplification;
import com.annimon.ownlang.parser.optimization.Optimizable;
import com.annimon.ownlang.parser.optimization.SummaryOptimization;
public final class Optimizer { public final class Optimizer {
public interface Info {
int optimizationsCount();
String summaryInfo();
}
public static Statement optimize(Statement statement, int level) { public static Statement optimize(Statement statement, int level) {
if (level == 0) return statement; if (level == 0) return statement;
final ConstantFolding constantFolding = new ConstantFolding();
final ConstantPropagation constantPropagation = new ConstantPropagation();
final DeadCodeElimination deadCodeElimination = new DeadCodeElimination();
final ExpressionSimplification expressionSimplification = new ExpressionSimplification();
Statement result = statement; final Optimizable optimization = new SummaryOptimization(new Optimizable[] {
new ConstantFolding(),
new ConstantPropagation(),
new DeadCodeElimination(),
new ExpressionSimplification()
});
Node result = statement;
for (int i = 0; i < level; i++) { for (int i = 0; i < level; i++) {
result = (Statement) result.accept(constantFolding, null); result = optimization.optimize(result);
result = (Statement) constantPropagation.visit(result);
result = (Statement) result.accept(deadCodeElimination, null);
result = (Statement) result.accept(expressionSimplification, null);
} }
System.out.print(constantFolding.summaryInfo()); System.out.println(optimization.summaryInfo());
System.out.print(constantPropagation.summaryInfo()); return (Statement) result;
System.out.print(deadCodeElimination.summaryInfo());
System.out.print(expressionSimplification.summaryInfo());
System.out.println();
return result;
} }
} }

View File

@ -1,20 +1,20 @@
package com.annimon.ownlang.parser.visitors; package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.exceptions.OperationIsNotSupportedException; import com.annimon.ownlang.exceptions.OperationIsNotSupportedException;
import com.annimon.ownlang.parser.Optimizer;
import com.annimon.ownlang.parser.ast.BinaryExpression; import com.annimon.ownlang.parser.ast.BinaryExpression;
import com.annimon.ownlang.parser.ast.ConditionalExpression; import com.annimon.ownlang.parser.ast.ConditionalExpression;
import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.FunctionDefineStatement;
import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.parser.ast.UnaryExpression; import com.annimon.ownlang.parser.ast.UnaryExpression;
import com.annimon.ownlang.parser.ast.ValueExpression; import com.annimon.ownlang.parser.ast.ValueExpression;
import com.annimon.ownlang.parser.visitors.VisitorUtils;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
/** /**
* Performs constant folding optimization. * Performs constant folding optimization.
*/ */
public class ConstantFolding extends OptimizationVisitor<Void> implements Optimizer.Info { public class ConstantFolding extends OptimizationVisitor<Void> implements Optimizable {
private static final Set<String> OPERATORS = VisitorUtils.operators(); private static final Set<String> OPERATORS = VisitorUtils.operators();
@ -28,6 +28,11 @@ public class ConstantFolding extends OptimizationVisitor<Void> implements Optimi
overloadedOperators = new HashSet<>(); overloadedOperators = new HashSet<>();
} }
@Override
public Node optimize(Node node) {
return node.accept(this, null);
}
@Override @Override
public int optimizationsCount() { public int optimizationsCount() {
return binaryExpressionFoldingCount return binaryExpressionFoldingCount

View File

@ -0,0 +1,74 @@
package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.parser.ast.ValueExpression;
import com.annimon.ownlang.parser.ast.VariableExpression;
import java.util.HashMap;
import java.util.Map;
/**
* Performs constant propagation.
*/
public class ConstantPropagation extends OptimizationVisitor<Map<String, Value>> implements Optimizable {
private final Map<String, Integer> propagatedVariables;
public ConstantPropagation() {
propagatedVariables = new HashMap<>();
}
@Override
public Node optimize(Node node) {
final Map<String, VariableInfo> variables = new HashMap<>();
// Find variables
node.accept(new VariablesGrabber(), variables);
// Filter only string/number values with 1 modification
final Map<String, Value> candidates = new HashMap<>();
for (Map.Entry<String, VariableInfo> e : variables.entrySet()) {
final VariableInfo info = e.getValue();
if (info.modifications != 1) continue;
if (info.value == null) continue;
switch (info.value.type()) {
case Types.NUMBER:
case Types.STRING:
candidates.put(e.getKey(), info.value);
break;
}
}
// Replace VariableExpression with ValueExpression
return node.accept(this, candidates);
}
@Override
public int optimizationsCount() {
return propagatedVariables.size();
}
@Override
public String summaryInfo() {
if (optimizationsCount() == 0) return "";
final StringBuilder sb = new StringBuilder();
if (propagatedVariables.size() > 0) {
sb.append("\nConstant propagations: ").append(propagatedVariables.size());
for (Map.Entry<String, Integer> e : propagatedVariables.entrySet()) {
sb.append("\n ").append(e.getKey()).append(": ").append(e.getValue());
}
}
return sb.toString();
}
@Override
public Node visit(VariableExpression s, Map<String, Value> t) {
if (t.containsKey(s.name)) {
if (!propagatedVariables.containsKey(s.name)) {
propagatedVariables.put(s.name, 1);
} else {
propagatedVariables.put(s.name, 1 + propagatedVariables.get(s.name));
}
return new ValueExpression(t.get(s.name));
}
return super.visit(s, t);
}
}

View File

@ -1,22 +1,27 @@
package com.annimon.ownlang.parser.visitors; package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.parser.Optimizer;
import com.annimon.ownlang.parser.ast.ExprStatement; import com.annimon.ownlang.parser.ast.ExprStatement;
import com.annimon.ownlang.parser.ast.IfStatement; import com.annimon.ownlang.parser.ast.IfStatement;
import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.parser.ast.TernaryExpression; import com.annimon.ownlang.parser.ast.TernaryExpression;
import com.annimon.ownlang.parser.ast.ValueExpression;
import com.annimon.ownlang.parser.ast.WhileStatement; import com.annimon.ownlang.parser.ast.WhileStatement;
import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue;
import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValueAsInt;
/** /**
* Performs dead code elimination. * Performs dead code elimination.
*/ */
public class DeadCodeElimination extends OptimizationVisitor<Void> implements Optimizer.Info { public class DeadCodeElimination extends OptimizationVisitor<Void> implements Optimizable {
private int ifStatementEliminatedCount; private int ifStatementEliminatedCount;
private int ternaryExpressionEliminatedCount; private int ternaryExpressionEliminatedCount;
private int whileStatementEliminatedCount; private int whileStatementEliminatedCount;
@Override
public Node optimize(Node node) {
return node.accept(this, null);
}
@Override @Override
public int optimizationsCount() { public int optimizationsCount() {
return ifStatementEliminatedCount + ternaryExpressionEliminatedCount return ifStatementEliminatedCount + ternaryExpressionEliminatedCount
@ -41,7 +46,7 @@ public class DeadCodeElimination extends OptimizationVisitor<Void> implements Op
@Override @Override
public Node visit(IfStatement s, Void t) { public Node visit(IfStatement s, Void t) {
if (s.expression instanceof ValueExpression) { if (isValue(s.expression)) {
ifStatementEliminatedCount++; ifStatementEliminatedCount++;
// true statement // true statement
if (s.expression.eval().asInt() != 0) { if (s.expression.eval().asInt() != 0) {
@ -58,7 +63,7 @@ public class DeadCodeElimination extends OptimizationVisitor<Void> implements Op
@Override @Override
public Node visit(TernaryExpression s, Void t) { public Node visit(TernaryExpression s, Void t) {
if (s.condition instanceof ValueExpression) { if (isValue(s.condition)) {
ternaryExpressionEliminatedCount++; ternaryExpressionEliminatedCount++;
if (s.condition.eval().asInt() != 0) { if (s.condition.eval().asInt() != 0) {
return s.trueExpr; return s.trueExpr;
@ -70,11 +75,9 @@ public class DeadCodeElimination extends OptimizationVisitor<Void> implements Op
@Override @Override
public Node visit(WhileStatement s, Void t) { public Node visit(WhileStatement s, Void t) {
if (s.condition instanceof ValueExpression) { if (isValueAsInt(s.condition, 0)) {
if (s.condition.eval().asInt() == 0) { whileStatementEliminatedCount++;
whileStatementEliminatedCount++; return new ExprStatement(s.condition);
return new ExprStatement(s.condition);
}
} }
return super.visit(s, t); return super.visit(s, t);
} }

View File

@ -1,12 +1,12 @@
package com.annimon.ownlang.parser.visitors; package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.parser.Optimizer;
import com.annimon.ownlang.parser.ast.BinaryExpression; import com.annimon.ownlang.parser.ast.BinaryExpression;
import com.annimon.ownlang.parser.ast.ConditionalExpression; import com.annimon.ownlang.parser.ast.ConditionalExpression;
import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.FunctionDefineStatement;
import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.parser.ast.UnaryExpression; import com.annimon.ownlang.parser.ast.UnaryExpression;
import com.annimon.ownlang.parser.ast.ValueExpression; import com.annimon.ownlang.parser.ast.ValueExpression;
import com.annimon.ownlang.parser.visitors.VisitorUtils;
import static com.annimon.ownlang.parser.visitors.VisitorUtils.isIntegerValue; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isIntegerValue;
import static com.annimon.ownlang.parser.visitors.VisitorUtils.isSameVariables; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isSameVariables;
import java.util.HashSet; import java.util.HashSet;
@ -15,7 +15,7 @@ import java.util.Set;
/** /**
* Performs expression simplification. * Performs expression simplification.
*/ */
public class ExpressionSimplification extends OptimizationVisitor<Void> implements Optimizer.Info { public class ExpressionSimplification extends OptimizationVisitor<Void> implements Optimizable {
private static final Set<String> OPERATORS = VisitorUtils.operators(); private static final Set<String> OPERATORS = VisitorUtils.operators();
@ -28,6 +28,11 @@ public class ExpressionSimplification extends OptimizationVisitor<Void> implemen
overloadedOperators = new HashSet<>(); overloadedOperators = new HashSet<>();
} }
@Override
public Node optimize(Node node) {
return node.accept(this, null);
}
@Override @Override
public int optimizationsCount() { public int optimizationsCount() {
return simplificationsCount; return simplificationsCount;

View File

@ -0,0 +1,12 @@
package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.parser.ast.Node;
public interface Optimizable {
Node optimize(Node node);
int optimizationsCount();
String summaryInfo();
}

View File

@ -1,4 +1,4 @@
package com.annimon.ownlang.parser.visitors; package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.parser.ast.*; import com.annimon.ownlang.parser.ast.*;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -0,0 +1,38 @@
package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.parser.ast.Node;
public final class SummaryOptimization implements Optimizable {
private final Optimizable[] optimizations;
public SummaryOptimization(Optimizable[] optimizations) {
this.optimizations = optimizations;
}
@Override
public Node optimize(Node node) {
for (Optimizable optimization : optimizations) {
node = optimization.optimize(node);
}
return node;
}
@Override
public int optimizationsCount() {
int count = 0;
for (Optimizable optimization : optimizations) {
count += optimization.optimizationsCount();
}
return count;
}
@Override
public String summaryInfo() {
final StringBuilder sb = new StringBuilder();
for (Optimizable optimization : optimizations) {
sb.append(optimization.summaryInfo());
}
return sb.toString();
}
}

View File

@ -0,0 +1,13 @@
package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.lib.Value;
public final class VariableInfo {
public Value value;
public int modifications;
@Override
public String toString() {
return (value == null ? "?" : value) + " (" + modifications + " mods)";
}
}

View File

@ -0,0 +1,75 @@
package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.parser.ast.Argument;
import com.annimon.ownlang.parser.ast.AssignmentExpression;
import com.annimon.ownlang.parser.ast.DestructuringAssignmentStatement;
import com.annimon.ownlang.parser.ast.FunctionDefineStatement;
import com.annimon.ownlang.parser.ast.MatchExpression;
import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.parser.ast.ValueExpression;
import com.annimon.ownlang.parser.ast.VariableExpression;
import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue;
import static com.annimon.ownlang.parser.visitors.VisitorUtils.isVariable;
import java.util.HashMap;
import java.util.Map;
public class VariablesGrabber extends OptimizationVisitor<Map<String, VariableInfo>> {
public static Map<String, VariableInfo> getInfo(Node node) {
Map<String, VariableInfo> variableInfos = new HashMap<>();
node.accept(new VariablesGrabber(), variableInfos);
return variableInfos;
}
@Override
public Node visit(AssignmentExpression s, Map<String, VariableInfo> t) {
if (!isVariable((Node)s.target)) {
return super.visit(s, t);
}
final String variableName = ((VariableExpression) s.target).name;
final VariableInfo var = variableInfo(t, variableName);
if (s.operation == null && isValue(s.expression)) {
var.value = ((ValueExpression) s.expression).value;
}
t.put(variableName, var);
return super.visit(s, t);
}
@Override
public Node visit(DestructuringAssignmentStatement s, Map<String, VariableInfo> t) {
for (String variableName : s.variables) {
if (variableName == null) continue;
t.put(variableName, variableInfo(t, variableName));
}
return super.visit(s, t);
}
@Override
public Node visit(FunctionDefineStatement s, Map<String, VariableInfo> t) {
for (Argument argument : s.arguments) {
final String variableName = argument.getName();
t.put(variableName, variableInfo(t, variableName));
}
return super.visit(s, t);
}
@Override
public Node visit(MatchExpression s, Map<String, VariableInfo> t) {
// no visit match expression
return s;
}
private VariableInfo variableInfo(Map<String, VariableInfo> t, final String variableName) {
final VariableInfo var;
if (t.containsKey(variableName)) {
var = t.get(variableName);
var.modifications++;
} else {
var = new VariableInfo();
var.modifications = 1;
}
return var;
}
}

View File

@ -1,162 +0,0 @@
package com.annimon.ownlang.parser.visitors;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.parser.Optimizer;
import com.annimon.ownlang.parser.ast.Argument;
import com.annimon.ownlang.parser.ast.AssignmentExpression;
import com.annimon.ownlang.parser.ast.DestructuringAssignmentStatement;
import com.annimon.ownlang.parser.ast.FunctionDefineStatement;
import com.annimon.ownlang.parser.ast.MatchExpression;
import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.parser.ast.Statement;
import com.annimon.ownlang.parser.ast.ValueExpression;
import com.annimon.ownlang.parser.ast.VariableExpression;
import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue;
import static com.annimon.ownlang.parser.visitors.VisitorUtils.isVariable;
import java.util.HashMap;
import java.util.Map;
/**
* Performs constant propagation.
*/
public class ConstantPropagation implements Optimizer.Info {
private final Map<String, Integer> propagatedVariables;
public ConstantPropagation() {
propagatedVariables = new HashMap<>();
}
@Override
public int optimizationsCount() {
return propagatedVariables.size();
}
@Override
public String summaryInfo() {
if (optimizationsCount() == 0) return "";
final StringBuilder sb = new StringBuilder();
if (propagatedVariables.size() > 0) {
sb.append("\nConstant propagations: ").append(propagatedVariables.size());
for (Map.Entry<String, Integer> e : propagatedVariables.entrySet()) {
sb.append("\n ").append(e.getKey()).append(": ").append(e.getValue());
}
}
return sb.toString();
}
public Node visit(Statement s) {
final Map<String, VariableInfo> variables = new HashMap<>();
// Find variables
s.accept(new VariablesGrabber(), variables);
// Filter only string/number values with 1 modification
final Map<String, Value> candidates = new HashMap<>();
for (Map.Entry<String, VariableInfo> e : variables.entrySet()) {
final VariableInfo info = e.getValue();
if (info.modifications != 1) continue;
if (info.value == null) continue;
switch (info.value.type()) {
case Types.NUMBER:
case Types.STRING:
candidates.put(e.getKey(), info.value);
break;
}
}
// Replace VariableExpression with ValueExpression
return s.accept(new VariablesPropagator(), candidates);
}
private class VariablesGrabber extends OptimizationVisitor<Map<String, VariableInfo>> {
@Override
public Node visit(AssignmentExpression s, Map<String, VariableInfo> t) {
if (!isVariable((Node)s.target)) {
return super.visit(s, t);
}
final String variableName = ((VariableExpression) s.target).name;
final VariableInfo var;
if (t.containsKey(variableName)) {
var = t.get(variableName);
var.modifications++;
} else {
var = new VariableInfo();
var.modifications = 1;
}
if (s.operation == null && isValue(s.expression)) {
var.value = ((ValueExpression) s.expression).value;
}
t.put(variableName, var);
return super.visit(s, t);
}
@Override
public Node visit(DestructuringAssignmentStatement s, Map<String, VariableInfo> t) {
for (String variableName : s.variables) {
if (variableName == null) continue;
final VariableInfo var;
if (t.containsKey(variableName)) {
var = t.get(variableName);
var.modifications++;
} else {
var = new VariableInfo();
var.modifications = 1;
}
t.put(variableName, var);
}
return super.visit(s, t);
}
@Override
public Node visit(FunctionDefineStatement s, Map<String, VariableInfo> t) {
for (Argument argument : s.arguments) {
final String variableName = argument.getName();
final VariableInfo var;
if (t.containsKey(variableName)) {
var = t.get(variableName);
var.modifications++;
} else {
var = new VariableInfo();
var.modifications = 1;
}
t.put(variableName, var);
}
return super.visit(s, t);
}
@Override
public Node visit(MatchExpression s, Map<String, VariableInfo> t) {
// no visit match expression
return s;
}
}
private class VariablesPropagator extends OptimizationVisitor<Map<String, Value>> {
@Override
public Node visit(VariableExpression s, Map<String, Value> t) {
if (t.containsKey(s.name)) {
if (!propagatedVariables.containsKey(s.name)) {
propagatedVariables.put(s.name, 1);
} else {
propagatedVariables.put(s.name, 1 + propagatedVariables.get(s.name));
}
return new ValueExpression(t.get(s.name));
}
return super.visit(s, t);
}
}
private static class VariableInfo {
Value value;
int modifications;
@Override
public String toString() {
return (value == null ? "?" : value) + " (" + modifications + " mods)";
}
}
}

View File

@ -47,6 +47,15 @@ public final class VisitorUtils {
return false; return false;
} }
public static boolean isValueAsInt(Node node, int valueToCheck) {
if (!isValue(node)) return false;
final Value value = ((ValueExpression) node).value;
if (value.type() != Types.NUMBER) return false;
return value.asInt() == valueToCheck;
}
public static boolean isSameVariables(Node n1, Node n2) { public static boolean isSameVariables(Node n1, Node n2) {
if (isVariable(n1) && isVariable(n2)) { if (isVariable(n1) && isVariable(n2)) {
final VariableExpression v1 = (VariableExpression) n1; final VariableExpression v1 = (VariableExpression) n1;