/*
* This file is a part of Alchemy OS project.
* Copyright (C) 2014, Sergey Basalaev <sbasalaev@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package alchemy.nec.opt;
import alchemy.nec.CompilerEnv;
import alchemy.nec.Token;
import alchemy.nec.syntax.Function;
import alchemy.nec.syntax.Scope;
import alchemy.nec.syntax.Unit;
import alchemy.nec.syntax.Var;
import alchemy.nec.syntax.expr.*;
import alchemy.nec.syntax.statement.*;
import alchemy.nec.syntax.type.BuiltinType;
import alchemy.nec.syntax.type.Type;
import alchemy.types.Float32;
import alchemy.types.Float64;
import alchemy.types.Int32;
import alchemy.types.Int64;
import alchemy.util.ArrayList;
import alchemy.util.HashMap;
/**
* Simple optimizer used with {@code -O1}
* and for constant folding.
* Performs the following optimizations:
* <ul>
* <li>Constant folding (CF)</li>
* <li>Constant/copy propagation (CP)</li>
* <li>Dead code elimination (DCE)</li>
* <li>Structural transformations (ST)</li>
* </ul>
* <p>
* Visit* methods accept Scope as argument and
* return optimized tree.
* </p>
*
* @author Sergey Basalaev
*/
public class ConstOptimizer implements ExprVisitor, StatementVisitor {
private final CompilerEnv env;
private HashMap copyVars = new HashMap();
public ConstOptimizer(CompilerEnv env) {
this.env = env;
}
public void visitUnit(Unit u) {
ArrayList funcs = u.implementedFunctions;
int fi = 0;
while (fi<funcs.size()) {
Function f = (Function) funcs.get(fi);
if (f.hits > 0) {
try {
copyVars.clear();
visitFunction(f);
} catch (Exception e) {
env.exceptionHappened("Optimizer", "Function: " + f.signature, e);
}
fi++;
} else {
funcs.remove(fi);
}
}
}
public void visitFunction(Function f) {
f.body = (Statement) f.body.accept(this, f);
}
/**
* <pre>
* ST:
* func.apply() => func
* func.apply(a1 ... am).apply(b1 ... bn) => func.apply(a1 ... am, b1 ... bn)
* </pre>
*/
public Object visitApply(ApplyExpr apply, Object scope) {
apply.funcExpr = (Expr) apply.funcExpr.accept(this, scope);
Expr[] args = apply.args;
for (int i=0; i<args.length; i++) {
args[i] = (Expr) args[i].accept(this, scope);
}
if (apply.args.length == 0) {
return apply.funcExpr;
}
if (apply.funcExpr.kind == Expr.EXPR_APPLY) {
ApplyExpr innerApply = (ApplyExpr) apply.funcExpr;
Expr[] newargs = new Expr[apply.args.length + innerApply.args.length];
System.arraycopy(apply.args, 0, newargs, 0, apply.args.length);
System.arraycopy(innerApply.args, 0, newargs, apply.args.length, innerApply.args.length);
return new ApplyExpr(innerApply.funcExpr, newargs);
}
return apply;
}
public Object visitArrayElement(ArrayElementExpr aget, Object scope) {
aget.arrayExpr = (Expr) aget.arrayExpr.accept(this, scope);
aget.indexExpr = (Expr) aget.indexExpr.accept(this, scope);
return aget;
}
public Object visitArrayLen(ArrayLenExpr alen, Object scope) {
alen.arrayExpr = (Expr) alen.arrayExpr.accept(this, scope);
return alen;
}
/**
* <pre>
* CF:
* expr + 0 => expr
* expr - 0 => expr
* expr | 0 => expr
* expr ^ 0 => expr
* expr << 0 => expr
* expr >> 0 => expr
* expr >>> 0 => expr
*
* expr * 1 => expr
* expr / 1 => expr
*
* expr & -1 => expr
* expr ^ -1 => ~expr
*
* expr + 0L => expr
* expr - 0L => expr
* expr | 0L => expr
* expr ^ 0L => expr
*
* expr * 1L => expr
* expr / 1L => expr
*
* expr & -1L => expr
* expr ^ -1L => ~expr
*
* expr & true => expr
*
* expr | false => expr
*
* expr ^ true => !expr
* expr ^ false => expr
*
* const op const => const
* </pre>
*/
public Object visitBinary(BinaryExpr binary, Object scope) {
binary.lhs = (Expr)binary.lhs.accept(this, scope);
binary.rhs = (Expr)binary.rhs.accept(this, scope);
// optimizing expr op const
if (binary.rhs.kind == Expr.EXPR_CONST) {
Object cnst = ((ConstExpr)binary.rhs).value;
if (cnst instanceof Int32) {
if (cnst.equals(Int32.ZERO)) {
switch (binary.operator) {
case '+':
case '-':
case '|':
case '^':
case Token.LTLT:
case Token.GTGT:
case Token.GTGTGT: {
return binary.lhs;
}
case '/':
case '%': {
env.warn(
((Scope)scope).enclosingFunction().source,
binary.rhs.lineNumber(),
CompilerEnv.W_DIVZERO,
"Integer division by zero");
return binary;
}
}
} else if (cnst.equals(Int32.ONE)) {
switch (binary.operator) {
case '*':
case '/':
case '%': {
return binary.lhs;
}
}
} else if (cnst.equals(Int32.M_ONE)) {
switch (binary.operator) {
case '&': {
return binary.lhs;
}
case '^': {
return new UnaryExpr('~', binary.lhs);
}
}
}
} else if (cnst instanceof Int64) {
long l = ((Int64)cnst).value;
if (l == 0L) {
switch (binary.operator) {
case '+':
case '-':
case '|':
case '^': {
return binary.lhs;
}
case '/':
case '%':
// refuse to optimize integer division by zero
return binary;
}
} else if (l == 1L) {
switch (binary.operator) {
case '*':
case '/':
case '%': {
return binary.lhs;
}
}
} else if (l == -1L) {
switch (binary.operator) {
case '&': {
return binary.lhs;
}
case '^': {
return new UnaryExpr('~', binary.lhs);
}
}
}
} else if (cnst instanceof Boolean) {
switch (binary.operator) {
case '&': {
if (cnst.equals(Boolean.TRUE)) return binary.lhs;
break;
}
case '|': {
if (cnst.equals(Boolean.FALSE)) return binary.lhs;
break;
}
case '^': {
if (cnst.equals(Boolean.FALSE)) return binary.lhs;
else return new UnaryExpr('!', binary.lhs);
}
}
}
}
// optimizing const op const
if (binary.lhs.kind == Expr.EXPR_CONST && binary.rhs.kind == Expr.EXPR_CONST) {
Object c1 = ((ConstExpr)binary.lhs).value;
Object c2 = ((ConstExpr)binary.rhs).value;
switch (binary.operator) {
case '+':
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value + ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value + ((Int64)c2).value);
} else if (c1 instanceof Float32) {
c1 = new Float32(((Float32)c1).value + ((Float32)c2).value);
} else if (c1 instanceof Float64) {
c1 = new Float64(((Float64)c1).value + ((Float64)c2).value);
}
break;
case '-':
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value - ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value - ((Int64)c2).value);
} else if (c1 instanceof Float32) {
c1 = new Float32(((Float32)c1).value - ((Float32)c2).value);
} else if (c1 instanceof Float64) {
c1 = new Float64(((Float64)c1).value - ((Float64)c2).value);
}
break;
case '*':
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value * ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value * ((Int64)c2).value);
} else if (c1 instanceof Float32) {
c1 = new Float32(((Float32)c1).value * ((Float32)c2).value);
} else if (c1 instanceof Float64) {
c1 = new Float64(((Float64)c1).value * ((Float64)c2).value);
}
break;
case '/':
// TODO: division by zero
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value / ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value / ((Int64)c2).value);
} else if (c1 instanceof Float32) {
c1 = new Float32(((Float32)c1).value / ((Float32)c2).value);
} else if (c1 instanceof Float64) {
c1 = new Float64(((Float64)c1).value / ((Float64)c2).value);
}
break;
case '%':
// TODO: division by zero
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value % ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value % ((Int64)c2).value);
} else if (c1 instanceof Float32) {
c1 = new Float32(((Float32)c1).value % ((Float32)c2).value);
} else if (c1 instanceof Float64) {
c1 = new Float64(((Float64)c1).value % ((Float64)c2).value);
}
break;
case '&':
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value & ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value & ((Int64)c2).value);
} else if (c1 instanceof Boolean) {
c1 = c1.equals(Boolean.TRUE) ? c2 : Boolean.FALSE;
}
break;
case '|':
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value | ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value | ((Int64)c2).value);
} else if (c1 instanceof Boolean) {
c1 = c1.equals(Boolean.TRUE) ? Boolean.TRUE : c2;
}
break;
case '^':
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value ^ ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value ^ ((Int64)c2).value);
} else if (c1 instanceof Boolean) {
c1 = c1.equals(c2) ? Boolean.FALSE : Boolean.TRUE;
}
break;
case Token.LTLT:
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value << ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value << ((Int32)c2).value);
}
break;
case Token.GTGT:
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value >> ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value >> ((Int32)c2).value);
}
break;
case Token.GTGTGT:
if (c1 instanceof Int32) {
c1 = Int32.toInt32(((Int32)c1).value >>> ((Int32)c2).value);
} else if (c1 instanceof Int64) {
c1 = new Int64(((Int64)c1).value >>> ((Int32)c2).value);
}
break;
}
return new ConstExpr(binary.lineNumber(), binary.lhs.returnType(), c1);
}
return binary;
}
/**
* <pre>
* ST:
* func.apply(a1, ..., ak)(a[k+1], ..., aN) => func(a1, ..., aN)
*
* CF:
* const.tostr() => "const"
* </pre>
*/
public Object visitCall(CallExpr fcall, Object scope) {
fcall.fload = (Expr) fcall.fload.accept(this, scope);
for (int i=0; i<fcall.args.length; i++) {
fcall.args[i] = (Expr)fcall.args[i].accept(this, scope);
}
if (fcall.fload.kind == Expr.EXPR_APPLY) {
ApplyExpr appliedLoad = (ApplyExpr) fcall.fload;
Expr[] newargs = new Expr[appliedLoad.args.length + fcall.args.length];
System.arraycopy(appliedLoad.args, 0, newargs, 0, appliedLoad.args.length);
System.arraycopy(fcall.args, 0, newargs, appliedLoad.args.length, fcall.args.length);
fcall.fload = appliedLoad.funcExpr;
fcall.args = newargs;
}
if (fcall.fload.kind == Expr.EXPR_CONST && fcall.args.length == 1
&& fcall.args[0].kind == Expr.EXPR_CONST) {
Function f = (Function)((ConstExpr)fcall.fload).value;
String sig = f.signature;
if (sig.equals("Any.tostr") || sig.equals("Bool.tostr") || sig.equals("Char.tostr")) {
Object cnst = ((ConstExpr)fcall.args[0]).value;
if (!(cnst instanceof Function)) {
f.hits--;
int line = fcall.lineNumber();
String str;
switch (fcall.args[0].returnType().kind) {
case Type.TYPE_BOOL:
str = (Int32.ZERO.equals(cnst)) ? "false" : "true";
break;
case Type.TYPE_CHAR:
str = String.valueOf((char) ((Int32)cnst).value);
break;
default:
str = String.valueOf(cnst);
break;
}
return new ConstExpr(line, BuiltinType.STRING, str);
}
}
}
return fcall;
}
/**
* <pre>
* CF:
* const.cast(Number) => const
* </pre>
*/
public Object visitCast(CastExpr cast, Object scope) {
cast.expr = (Expr)cast.expr.accept(this, scope);
Type fromType = cast.expr.returnType();
Type toType = cast.toType;
if (fromType.safeToCastTo(toType)) {
return cast.expr;
} else if (cast.expr.kind == Expr.EXPR_CONST && toType.kind != Type.TYPE_OBJECT) {
Object cnst = ((ConstExpr)cast.expr).value;
int line = cast.lineNumber();
switch (fromType.kind) {
case Type.TYPE_BYTE:
case Type.TYPE_SHORT:
case Type.TYPE_CHAR:
case Type.TYPE_INT: {
int i = ((Int32)cnst).value;
switch (toType.kind) {
case Type.TYPE_DOUBLE:
cnst = new Float64(i);
break;
case Type.TYPE_FLOAT:
cnst = new Float32(i);
break;
case Type.TYPE_LONG:
cnst = new Int64(i);
break;
case Type.TYPE_SHORT:
cnst = Int32.toInt32((short)i);
break;
case Type.TYPE_BYTE:
cnst = Int32.toInt32((byte)i);
break;
case Type.TYPE_CHAR:
cnst = Int32.toInt32((char)i);
break;
}
break;
}
case Type.TYPE_LONG: {
long l = ((Int64)cnst).value;
switch (toType.kind) {
case Type.TYPE_DOUBLE:
cnst = new Float64(l);
break;
case Type.TYPE_FLOAT:
cnst = new Float32(l);
break;
case Type.TYPE_INT:
cnst = Int32.toInt32((int)l);
break;
case Type.TYPE_SHORT:
cnst = Int32.toInt32((short)l);
break;
case Type.TYPE_BYTE:
cnst = Int32.toInt32((byte)l);
break;
case Type.TYPE_CHAR:
cnst = Int32.toInt32((char)l);
break;
}
break;
}
case Type.TYPE_FLOAT: {
float f = ((Float32)cnst).value;
switch (toType.kind) {
case Type.TYPE_DOUBLE:
cnst = new Float64(f);
break;
case Type.TYPE_LONG:
cnst = new Int64((long)f);
break;
case Type.TYPE_INT:
cnst = Int32.toInt32((int)f);
break;
case Type.TYPE_SHORT:
cnst = Int32.toInt32((short)f);
break;
case Type.TYPE_BYTE:
cnst = Int32.toInt32((byte)f);
break;
case Type.TYPE_CHAR:
cnst = Int32.toInt32((char)f);
break;
}
break;
}
case Type.TYPE_DOUBLE: {
double d = ((Float64)cnst).value;
switch (toType.kind) {
case Type.TYPE_FLOAT:
cnst = new Float32((float)d);
break;
case Type.TYPE_LONG:
cnst = new Int64((long)d);
break;
case Type.TYPE_INT:
cnst = Int32.toInt32((int)d);
break;
case Type.TYPE_SHORT:
cnst = Int32.toInt32((short)d);
break;
case Type.TYPE_BYTE:
cnst = Int32.toInt32((byte)d);
break;
case Type.TYPE_CHAR:
cnst = Int32.toInt32((char)d);
break;
}
break;
}
}
return new ConstExpr(line, toType, cnst);
}
return cast;
}
/**
* <pre>
* CF:
* const op const => const
*
* bool == bool => !(bool ^ bool)
* bool != bool => bool ^ bool
*
* ST:
* 0 < expr => expr > 0
* 0 > expr => expr < 0
* 0 <= expr => expr >= 0
* 0 >= expr => expr <= 0
* </pre>
*/
public Object visitComparison(ComparisonExpr cmp, Object scope) {
cmp.lhs = (Expr)cmp.lhs.accept(this, scope);
cmp.rhs = (Expr)cmp.rhs.accept(this, scope);
if (cmp.lhs.kind == Expr.EXPR_CONST && cmp.rhs.kind == Expr.EXPR_CONST) {
Object c1 = ((ConstExpr)cmp.lhs).value;
Object c2 = ((ConstExpr)cmp.rhs).value;
switch (cmp.operator) {
case Token.EQEQ:
c1 = (c1.equals(c2)) ? Boolean.TRUE : Boolean.FALSE;
break;
case Token.NOTEQ:
c1 = (c1.equals(c2)) ? Boolean.FALSE : Boolean.TRUE;
break;
case '<':
if (c1 instanceof Int32) {
c1 = ((Int32)c1).value < ((Int32)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Int64) {
c1 = ((Int64)c1).value < ((Int64)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Float32) {
c1 = ((Float32)c1).value < ((Float32)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Float64) {
c1 = ((Float64)c1).value < ((Float64)c2).value ? Boolean.TRUE : Boolean.FALSE;
}
break;
case '>':
if (c1 instanceof Int32) {
c1 = ((Int32)c1).value > ((Int32)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Int64) {
c1 = ((Int64)c1).value > ((Int64)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Float32) {
c1 = ((Float32)c1).value > ((Float32)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Float64) {
c1 = ((Float64)c1).value > ((Float64)c2).value ? Boolean.TRUE : Boolean.FALSE;
}
break;
case Token.LTEQ:
if (c1 instanceof Int32) {
c1 = ((Int32)c1).value <= ((Int32)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Int64) {
c1 = ((Int64)c1).value <= ((Int64)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Float32) {
c1 = ((Float32)c1).value <= ((Float32)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Float64) {
c1 = ((Float64)c1).value <= ((Float64)c2).value ? Boolean.TRUE : Boolean.FALSE;
}
break;
case Token.GTEQ:
if (c1 instanceof Int32) {
c1 = ((Int32)c1).value >= ((Int32)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Int64) {
c1 = ((Int64)c1).value >= ((Int64)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Float32) {
c1 = ((Float32)c1).value >= ((Float32)c2).value ? Boolean.TRUE : Boolean.FALSE;
} else if (c1 instanceof Float64) {
c1 = ((Float64)c1).value >= ((Float64)c2).value ? Boolean.TRUE : Boolean.FALSE;
}
break;
}
return new ConstExpr(cmp.lineNumber(), BuiltinType.BOOL, c1);
} else if (cmp.lhs instanceof ConstExpr) {
Object cnst = ((ConstExpr)cmp.lhs).value;
if (cnst.equals(Int32.ZERO)) {
// to not duplicate code in EAsmWriter and
// allow it to make assumptions only on rhs
Expr tmp = cmp.lhs;
cmp.lhs = cmp.rhs;
cmp.rhs = tmp;
switch (cmp.operator) {
case '<': cmp.operator = '>'; break;
case '>': cmp.operator = '<'; break;
case Token.LTEQ: cmp.operator = Token.GTEQ; break;
case Token.GTEQ: cmp.operator = Token.LTEQ; break;
}
return cmp;
}
}
if (cmp.lhs.returnType() == BuiltinType.BOOL && cmp.rhs.returnType() == BuiltinType.BOOL) {
Expr binary = new BinaryExpr(cmp.lhs, '^', cmp.rhs);
if (cmp.operator == Token.EQEQ) {
return new UnaryExpr('!', binary);
} else {
return binary;
}
}
return cmp;
}
public Object visitConst(ConstExpr cnst, Object scope) {
return cnst;
}
/**
* <pre>
* CF:
* "str"+const => "strconst"
* </pre>
*/
public Object visitConcat(ConcatExpr concat, Object scope) {
ArrayList oldexprs = concat.exprs;
ArrayList newexprs = new ArrayList();
String lastLiteral = null;
int lastLiteralLine = -1;
for (int i=0; i<oldexprs.size(); i++) {
Expr e = (Expr) ((Expr)oldexprs.get(i)).accept(this, scope);
if (e.kind == Expr.EXPR_CONST && e.returnType().kind != Type.TYPE_FUNCTION) {
String append = ((ConstExpr)e).value.toString();
if (append.equals("")) {
// ignore empty string
} else if (lastLiteral == null) {
lastLiteral = append;
lastLiteralLine = e.lineNumber();
} else {
lastLiteral += append.toString();
}
} else {
if (lastLiteral != null) {
newexprs.add(new ConstExpr(lastLiteralLine, BuiltinType.STRING, lastLiteral));
lastLiteral = null;
}
newexprs.add(e);
}
}
if (lastLiteral != null) {
newexprs.add(new ConstExpr(lastLiteralLine, BuiltinType.STRING, lastLiteral));
}
if (newexprs.isEmpty()) {
return new ConstExpr(concat.lineNumber(), BuiltinType.STRING, "");
} else if (newexprs.size() == 1) {
return newexprs.get(0);
} else {
concat.exprs = newexprs;
return concat;
}
}
/**
* <pre>
* DCE:
* if (true) e1 else e2 => e1
* if (false) e1 else e2 => e2
* </pre>
*/
public Object visitIfElse(IfElseExpr ifexpr, Object scope) {
ifexpr.condition = (Expr)ifexpr.condition.accept(this, scope);
ifexpr.ifexpr = (Expr)ifexpr.ifexpr.accept(this, scope);
ifexpr.elseexpr = (Expr)ifexpr.elseexpr.accept(this, scope);
if (ifexpr.condition.kind == Expr.EXPR_CONST) {
if (((ConstExpr)ifexpr.condition).value.equals(Boolean.TRUE)) {
return ifexpr.ifexpr;
} else {
return ifexpr.elseexpr;
}
}
return ifexpr;
}
public Object visitNewArray(NewArrayExpr expr, Object scope) {
Expr[] dimensions = expr.lengthExprs;
for (int i=0; i<dimensions.length; i++) {
dimensions[i] = (Expr) dimensions[i].accept(this, scope);
}
return expr;
}
public Object visitNewArrayInit(NewArrayInitExpr expr, Object scope) {
Expr[] inits = expr.initializers;
for (int i=0; i<inits.length; i++) {
if (inits[i] != null) {
inits[i] = (Expr) inits[i].accept(this, scope);
}
}
return expr;
}
public Object visitRange(RangeExpr expr, Object args) {
expr.fromExpr = (Expr) expr.fromExpr.accept(this, args);
expr.toExpr = (Expr) expr.toExpr.accept(this, args);
return expr;
}
/** Constant/copy propagation. */
public Object visitSequential(SequentialExpr expr, Object scope) {
Expr[] seq = expr.seqExprs;
int newsize = 0;
for (int i=0; i<seq.length; i++) {
Expr e = (Expr) seq[i].accept(this, scope);
seq[i] = e;
if (e.kind == Expr.EXPR_CONST) {
expr.seqVars[i].isConstant = true;
expr.seqVars[i].defaultValue = ((ConstExpr)e).value;
seq[i] = null;
} else if (e.kind == Expr.EXPR_VAR) {
copyVars.set(expr.seqVars[i], ((VarExpr)e).var);
seq[i] = null;
} else {
newsize++;
}
}
expr.lastExpr = (Expr) expr.lastExpr.accept(this, scope);
if (newsize == 0) {
return expr.lastExpr;
} else if (newsize < seq.length) {
Var[] newvars = new Var[newsize];
Expr[] newseq = new Expr[newsize];
int j=0;
for (int i=0; i<seq.length; i++) {
if (seq[i] != null) {
newvars[j] = expr.seqVars[i];
newseq[j] = seq[i];
j++;
}
}
expr.seqVars = newvars;
expr.seqExprs = newseq;
}
return expr;
}
/**
* <pre>
* DCE:
* switch (const) returns branch
* </pre>
*/
public Object visitSwitch(SwitchExpr switchExpr, Object scope) {
switchExpr.keyExpr = (Expr) switchExpr.keyExpr.accept(this, scope);
for (int i=0; i<switchExpr.exprs.length; i++) {
switchExpr.exprs[i] = (Expr) switchExpr.exprs[i].accept(this, scope);
}
switchExpr.elseExpr = (Expr) switchExpr.elseExpr.accept(this, scope);
if (switchExpr.keyExpr.kind == Expr.EXPR_CONST) {
int key = ((Int32)((ConstExpr)switchExpr.keyExpr).value).value;
for (int branchIndex = 0; branchIndex < switchExpr.keySets.length; branchIndex++) {
int[] keySet = switchExpr.keySets[branchIndex];
for (int keyIndex = 0; keyIndex < keySet.length; keyIndex++) {
if (keySet[keyIndex] == key) return switchExpr.exprs[branchIndex];
}
}
return switchExpr.elseExpr;
}
return switchExpr;
}
/**
* <pre>
* DCE:
* try const catch ... => const
* try var catch ... => const
* </pre>
*/
public Object visitTryCatch(TryCatchExpr trycatch, Object scope) {
trycatch.tryExpr = (Expr) trycatch.tryExpr.accept(this, scope);
trycatch.catchExpr = (Expr) trycatch.catchExpr.accept(this, scope);
int kind = trycatch.tryExpr.kind;
if (kind == Expr.EXPR_CONST || kind == Expr.EXPR_VAR) {
return trycatch.tryExpr;
}
return trycatch;
}
/**
* <pre>
* CF:
* +expr => expr
* -const => const
* ~const => const
* !const => const
*
* !!expr => expr
* --expr => expr
* ~~expr => expr
*
* ST:
* !(a == b) => a != b
* !(a != b) => a == b
* !(a < b) => a >= b
* !(a > b) => a <= b
* !(a <= b) => a > b
* !(a >= b) => a < b
*
* !(if (c) a else b) => if (c) !a else !b
* !(try a catch b) => try !a catch !b
* </pre>
*/
public Object visitUnary(UnaryExpr unary, Object scope) {
// optimize subexpression
unary.expr = (Expr)unary.expr.accept(this, scope);
if (unary.operator == '+') {
return unary.expr;
}
// optimize "op const"
if (unary.expr.kind == Expr.EXPR_CONST) {
Object cnst = ((ConstExpr)unary.expr).value;
switch (unary.operator) {
case '!':
cnst = (cnst == Boolean.TRUE) ? Boolean.FALSE : Boolean.TRUE;
break;
case '-':
if (cnst instanceof Int32) {
int i = ((Int32)cnst).value;
cnst = Int32.toInt32(-i);
} else if (cnst instanceof Int64) {
long l = ((Int64)cnst).value;
cnst = new Int64(-l);
} else if (cnst instanceof Float32) {
float f = ((Float32)cnst).value;
cnst = new Float32(-f);
} else if (cnst instanceof Float64) {
double d = ((Float64)cnst).value;
cnst = new Float64(-d);
}
break;
case '~':
if (cnst instanceof Int32) {
int i = ((Int32)cnst).value;
cnst = Int32.toInt32(~i);
} else if (cnst instanceof Int64) {
long l = ((Int64)cnst).value;
cnst = new Int64(~l);
}
break;
}
return new ConstExpr(unary.lineNumber(), unary.returnType(), cnst);
}
// optimize !(expr)
if (unary.operator == '!') {
if (unary.expr.kind == Expr.EXPR_COMPARISON) {
final ComparisonExpr cmp = (ComparisonExpr) unary.expr;
switch (cmp.operator) {
case '<': cmp.operator = Token.GTEQ; break;
case '>': cmp.operator = Token.LTEQ; break;
case Token.LTEQ: cmp.operator = '>'; break;
case Token.GTEQ: cmp.operator = '<'; break;
case Token.EQEQ: cmp.operator = Token.NOTEQ; break;
case Token.NOTEQ: cmp.operator = Token.EQEQ; break;
}
return cmp;
} else if (unary.expr.kind == Expr.EXPR_IF) {
final IfElseExpr ifelse = (IfElseExpr) unary.expr;
ifelse.ifexpr = (Expr) new UnaryExpr('!', ifelse.ifexpr).accept(this, scope);
ifelse.elseexpr = (Expr) new UnaryExpr('!', ifelse.elseexpr).accept(this, scope);
return ifelse;
} else if (unary.expr.kind == Expr.EXPR_TRYCATCH) {
final TryCatchExpr trycatch = (TryCatchExpr) unary.expr;
trycatch.tryExpr = (Expr) new UnaryExpr('!', trycatch.tryExpr).accept(this, scope);
trycatch.catchExpr = (Expr) new UnaryExpr('!', trycatch.catchExpr).accept(this, scope);
return trycatch;
}
}
// optimize double unary
if (unary.expr.kind == Expr.EXPR_UNARY && ((UnaryExpr)unary.expr).operator == unary.operator) {
return ((UnaryExpr)unary.expr).expr;
}
return unary;
}
/** Constant/copy propagation. */
public Object visitVar(VarExpr expr, Object scope) {
if (expr.var.isConstant && expr.var.defaultValue != null) {
expr.var.hits--;
return new ConstExpr(expr.lineNumber(), expr.var.type, expr.var.defaultValue);
}
Var copyVar = (Var) copyVars.get(expr.var);
if (copyVar != null) {
expr.var.hits--;
copyVar.hits++;
return new VarExpr(expr.lineNumber(), copyVar);
}
return expr;
}
public Object visitArraySetStatement(ArraySetStatement stat, Object scope) {
stat.arrayExpr = (Expr) stat.arrayExpr.accept(this, scope);
stat.indexExpr = (Expr) stat.indexExpr.accept(this, scope);
stat.assignExpr = (Expr) stat.assignExpr.accept(this, scope);
return stat;
}
/** Constant/copy propagation. */
public Object visitAssignStatement(AssignStatement stat, Object scope) {
stat.assignExpr = (Expr) stat.assignExpr.accept(this, scope);
if (stat.var.hits == 0) {
return new ExprStatement(stat.assignExpr).accept(this, scope);
}
if (stat.var.isConstant) {
switch (stat.assignExpr.kind) {
case Expr.EXPR_CONST: {
stat.var.defaultValue = ((ConstExpr)stat.assignExpr).value;
return new EmptyStatement();
}
case Expr.EXPR_VAR: {
copyVars.set(stat.var, ((VarExpr)stat.assignExpr).var);
return new EmptyStatement();
}
}
}
return stat;
}
/**
* <pre>
* ST:
* { ..., stat[N], empty, stat[N+2], ... } => { ..., stat[N], stat[N+2], ... }
* { stat } => stat
* { } => empty
* </pre>
*/
public Object visitBlockStatement(BlockStatement block, Object scope) {
ArrayList statements = block.statements;
int sti = 0;
while (sti < block.statements.size()) {
Statement stat = (Statement) block.statements.get(sti);
stat = (Statement) stat.accept(this, block);
if (stat.kind == Statement.STAT_EMPTY) {
statements.remove(sti);
} else {
statements.set(sti, stat);
sti++;
}
}
// remove unused vars
Object[] varnames = block.vars.keys();
for (int vi=0; vi<varnames.length; vi++) {
Var var = (Var) block.vars.get(varnames[vi]);
if (var.hits == 0) {
block.vars.remove(varnames[vi]);
copyVars.remove(var);
}
}
switch (statements.size()) {
case 0:
return new EmptyStatement();
case 1:
if (block.vars.size() == 0)
return statements.get(0);
else
return block;
default:
return block;
}
}
public Object visitBreakStatement(BreakStatement stat, Object scope) {
return stat;
}
public Object visitCompoundAssignStatement(CompoundAssignStatement stat, Object scope) {
stat.assignExpr = (Expr) stat.assignExpr.accept(this, scope);
return stat;
}
public Object visitContinueStatement(ContinueStatement stat, Object scope) {
return stat;
}
public Object visitEmptyStatement(EmptyStatement stat, Object scope) {
return stat;
}
/**
* <pre>
* DCE:
* const; => empty;
* var; => empty;
* ex1 op ex1; => {ex1; ex2;}
* op ex; => ex;
* new [](d1,...,dN); => {d1; ... dN;}
* new []{e1,...,eN}; => {e1; ... eN;}
* array.len; => array;
* array[i]; => {array; i;}
* </pre>
*/
public Object visitExprStatement(ExprStatement stat, Object scope) {
stat.expr = (Expr) stat.expr.accept(this, scope);
switch (stat.expr.kind) {
case Expr.EXPR_CONST:
case Expr.EXPR_VAR:
return new EmptyStatement();
case Expr.EXPR_BINARY: {
BinaryExpr binary = (BinaryExpr) stat.expr;
BlockStatement block = new BlockStatement((Scope)scope);
block.statements.add(new ExprStatement(binary.lhs));
block.statements.add(new ExprStatement(binary.rhs));
return block.accept(this, scope);
}
case Expr.EXPR_UNARY: {
UnaryExpr unary = (UnaryExpr) stat.expr;
return new ExprStatement(unary.expr).accept(this, scope);
}
case Expr.EXPR_NEWARRAY: {
NewArrayExpr newarray = (NewArrayExpr) stat.expr;
BlockStatement block = new BlockStatement((Scope)scope);
for (int i=0; i < newarray.lengthExprs.length; i++) {
block.statements.add(new ExprStatement(newarray.lengthExprs[i]));
}
return block.accept(this, scope);
}
case Expr.EXPR_NEWARRAY_INIT: {
NewArrayInitExpr newarray = (NewArrayInitExpr) stat.expr;
BlockStatement block = new BlockStatement((Scope)scope);
for (int i=0; i < newarray.initializers.length; i++) {
block.statements.add(new ExprStatement(newarray.initializers[i]));
}
return block.accept(this, scope);
}
case Expr.EXPR_ARRAY_LEN: {
ArrayLenExpr alen = (ArrayLenExpr) stat.expr;
return new ExprStatement(alen.arrayExpr).accept(this, scope);
}
case Expr.EXPR_ARRAY_ELEMENT: {
ArrayElementExpr aget = (ArrayElementExpr) stat.expr;
BlockStatement block = new BlockStatement((Scope)scope);
block.statements.add(new ExprStatement(aget.arrayExpr));
block.statements.add(new ExprStatement(aget.indexExpr));
return block.accept(this, scope);
}
default:
return stat;
}
}
/**
* <pre>
* DCE:
* for (... ,false, ...) => empty
* </pre>
*/
public Object visitForLoopStatement(ForLoopStatement forloop, Object scope) {
forloop.condition = (Expr) forloop.condition.accept(this, scope);
forloop.increment = (Statement) forloop.increment.accept(this, scope);
forloop.body = (Statement) forloop.body.accept(this, scope);
if (forloop.condition.kind == Expr.EXPR_CONST) {
Object cnst = ((ConstExpr)forloop.condition).value;
if (cnst.equals(Boolean.FALSE)) {
return new EmptyStatement();
}
}
return forloop;
}
/**
* <pre>
* DCE:
* if (true) st1 else st2 => st1
* if (false) st1 else st2 => st2
* if (expr) {} else st => if (!expr) st
* if (expr) {} else {} => expr;
* </pre>
*/
public Object visitIfStatement(IfStatement ifelse, Object scope) {
ifelse.condition = (Expr) ifelse.condition.accept(this, scope);
ifelse.ifstat = (Statement) ifelse.ifstat.accept(this, scope);
ifelse.elsestat = (Statement) ifelse.elsestat.accept(this, scope);
if (ifelse.condition.kind == Expr.EXPR_CONST) {
if (((ConstExpr)ifelse.condition).value.equals(Boolean.TRUE)) {
return ifelse.ifstat;
} else {
return ifelse.elsestat;
}
}
if (ifelse.ifstat.kind == Statement.STAT_EMPTY) {
if (ifelse.elsestat.kind == Statement.STAT_EMPTY) {
return new ExprStatement(ifelse.condition).accept(this, scope);
} else {
ifelse.condition = (Expr) new UnaryExpr('!', ifelse.condition).accept(this, scope);
ifelse.ifstat = ifelse.elsestat;
ifelse.elsestat = new EmptyStatement();
}
}
return ifelse;
}
/**
* <pre>
* DCE:
* do stat while (false) => stat
* while (stat, false) ... => stat
* while (false) ... => empty
* </pre>
*/
public Object visitLoopStatement(LoopStatement stat, Object scope) {
stat.condition = (Expr) stat.condition.accept(this, scope);
stat.preBody = (Statement) stat.preBody.accept(this, scope);
stat.postBody = (Statement) stat.postBody.accept(this, scope);
if (stat.condition.kind == Expr.EXPR_CONST) {
Object cnst = ((ConstExpr)stat.condition).value;
if (cnst.equals(Boolean.FALSE)) {
return stat.preBody;
}
}
return stat;
}
public Object visitReturnStatement(ReturnStatement stat, Object scope) {
stat.expr = (Expr) stat.expr.accept(this, scope);
return stat;
}
/**
* <pre>
* DCE:
* switch (const) returns branch
* </pre>
*/
public Object visitSwitchStatement(SwitchStatement stat, Object scope) {
stat.keyExpr = (Expr) stat.keyExpr.accept(this, scope);
for (int i=0; i<stat.statements.length; i++) {
stat.statements[i] = (Statement) stat.statements[i].accept(this, scope);
}
stat.elseStat = (Statement) stat.elseStat.accept(this, scope);
if (stat.keyExpr.kind == Expr.EXPR_CONST) {
int key = ((Int32)((ConstExpr)stat.keyExpr).value).value;
for (int branchIndex = 0; branchIndex < stat.keySets.length; branchIndex++) {
int[] keySet = stat.keySets[branchIndex];
for (int keyIndex = 0; keyIndex < keySet.length; keyIndex++) {
if (keySet[keyIndex] == key) return stat.statements[branchIndex];
}
}
return stat.elseStat;
}
return stat;
}
public Object visitThrowStatement(ThrowStatement stat, Object scope) {
stat.errCodeExpr = (Expr) stat.errCodeExpr.accept(this, scope);
stat.errMsgExpr = (Expr) stat.errMsgExpr.accept(this, scope);
return stat;
}
/**
* <pre>
* DCE:
* try { } catch ... => empty
* </pre>
*/
public Object visitTryCatchStatement(TryCatchStatement stat, Object scope) {
stat.tryStat = (Statement) stat.tryStat.accept(this, scope);
stat.catchStat = (Statement) stat.catchStat.accept(this, scope);
if (stat.tryStat.kind == Statement.STAT_EMPTY) {
return stat.tryStat;
}
return stat;
}
}