diff --git a/src/com/annimon/ownlang/parser/Optimizer.java b/src/com/annimon/ownlang/parser/Optimizer.java index 43628cf..19e0fe4 100644 --- a/src/com/annimon/ownlang/parser/Optimizer.java +++ b/src/com/annimon/ownlang/parser/Optimizer.java @@ -7,6 +7,7 @@ import com.annimon.ownlang.parser.optimization.ConstantFolding; import com.annimon.ownlang.parser.optimization.ConstantPropagation; import com.annimon.ownlang.parser.optimization.DeadCodeElimination; import com.annimon.ownlang.parser.optimization.ExpressionSimplification; +import com.annimon.ownlang.parser.optimization.InstructionCombining; import com.annimon.ownlang.parser.optimization.Optimizable; import com.annimon.ownlang.parser.optimization.SummaryOptimization; @@ -19,11 +20,12 @@ public final class Optimizer { new ConstantFolding(), new ConstantPropagation(), new DeadCodeElimination(), - new ExpressionSimplification() + new ExpressionSimplification(), + new InstructionCombining() }); Node result = statement; - if (true || level >= 9) { + if (level >= 9) { int iteration = 0, lastModifications = 0; do { lastModifications = optimization.optimizationsCount(); diff --git a/src/com/annimon/ownlang/parser/optimization/InstructionCombining.java b/src/com/annimon/ownlang/parser/optimization/InstructionCombining.java new file mode 100644 index 0000000..c5ad497 --- /dev/null +++ b/src/com/annimon/ownlang/parser/optimization/InstructionCombining.java @@ -0,0 +1,110 @@ +package com.annimon.ownlang.parser.optimization; + +import com.annimon.ownlang.parser.ast.BlockStatement; +import com.annimon.ownlang.parser.ast.Expression; +import com.annimon.ownlang.parser.ast.Node; +import com.annimon.ownlang.parser.ast.PrintStatement; +import com.annimon.ownlang.parser.ast.PrintlnStatement; +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.ValueExpression; +import static com.annimon.ownlang.parser.visitors.VisitorUtils.isConstantValue; + +/** + * Performs instruction combining. + */ +public class InstructionCombining extends OptimizationVisitor implements Optimizable { + + private int printCombinedCount; + + @Override + public Node optimize(Node node) { + return node.accept(this, null); + } + + @Override + public int optimizationsCount() { + return printCombinedCount; + } + + @Override + public String summaryInfo() { + if (optimizationsCount() == 0) return ""; + final StringBuilder sb = new StringBuilder(); + if (printCombinedCount > 0) { + sb.append("\nPrint statement combined: ").append(printCombinedCount); + } + return sb.toString(); + } + + @Override + public Node visit(BlockStatement s, Void t) { + final int size = s.statements.size(); + if (size <= 1) { + return super.visit(s, t); + } + + boolean changed = false; + final BlockStatement result = new BlockStatement(); + int i; + for (i = 1; i < size; i++) { + Statement s1 = s.statements.get(i - 1); + Statement s2 = s.statements.get(i); + Node n1 = s1.accept(this, t); + Node n2 = s2.accept(this, t); + if (n1 != s1 || n2 != s2) { + changed = true; + } + final Node combined = tryCombine(n1, n2); + if (combined == null) { + result.add((Statement) n1); + } else { + changed = true; + result.add(consumeStatement(combined)); + i++; + } + } + if (i == size) { + // Last node + Statement s2 = s.statements.get(size - 1); + Node n2 = s2.accept(this, t); + if (n2 != s2) { + changed = true; + } + result.add((Statement) n2); + } + if (changed) { + return result; + } + return super.visit(s, t); + } + + private Node tryCombine(Node n1, Node n2) { + final int n1Type; + if (n1 instanceof PrintStatement) n1Type = 1; + else if (n1 instanceof PrintlnStatement) n1Type = 2; + else n1Type = 0; + + final int n2Type; + if (n2 instanceof PrintStatement) n2Type = 1; + else if (n2 instanceof PrintlnStatement) n2Type = 2; + else n2Type = 0; + + if (n1Type != 0 && n2Type != 0) { + final Expression e1 = (n1Type == 1) + ? ((PrintStatement) n1).expression + : ((PrintlnStatement) n1).expression; + final Expression e2 = (n2Type == 1) + ? ((PrintStatement) n2).expression + : ((PrintlnStatement) n2).expression; + if (isConstantValue(e1) && isConstantValue(e2)) { + String s1 = e1.eval().asString(); + if (n1Type == 2) s1 += System.lineSeparator(); + String s2 = e2.eval().asString(); + if (n2Type == 2) s2 += System.lineSeparator(); + printCombinedCount++; + return new PrintStatement(new ValueExpression(s1 + s2)); + } + } + return null; + } +} diff --git a/src/com/annimon/ownlang/parser/visitors/VisitorUtils.java b/src/com/annimon/ownlang/parser/visitors/VisitorUtils.java index 2a8bed0..408e569 100644 --- a/src/com/annimon/ownlang/parser/visitors/VisitorUtils.java +++ b/src/com/annimon/ownlang/parser/visitors/VisitorUtils.java @@ -56,6 +56,13 @@ public final class VisitorUtils { return value.asInt() == valueToCheck; } + public static boolean isConstantValue(Node node) { + if (!isValue(node)) return false; + + final int type = ((ValueExpression) node).value.type(); + return ( (type == Types.NUMBER) || (type == Types.STRING) ); + } + public static boolean isSameVariables(Node n1, Node n2) { if (isVariable(n1) && isVariable(n2)) { final VariableExpression v1 = (VariableExpression) n1;