blob: 5a8cd30daf969f4561fbda74f313edf675c388b1 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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 com.android.jack.optimizations;
import com.android.jack.Options;
import com.android.jack.analysis.DefinitionMarker;
import com.android.jack.analysis.UseDefsMarker;
import com.android.jack.cfg.BasicBlock;
import com.android.jack.cfg.ControlFlowGraph;
import com.android.jack.ir.ast.JBooleanLiteral;
import com.android.jack.ir.ast.JExpression;
import com.android.jack.ir.ast.JIfStatement;
import com.android.jack.ir.ast.JLock;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JNullLiteral;
import com.android.jack.ir.ast.JNumberValueLiteral;
import com.android.jack.ir.ast.JReinterpretCastOperation;
import com.android.jack.ir.ast.JStatement;
import com.android.jack.ir.ast.JSwitchStatement;
import com.android.jack.ir.ast.JUnlock;
import com.android.jack.ir.ast.JVariable;
import com.android.jack.ir.ast.JVariableRef;
import com.android.jack.ir.ast.JVisitor;
import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
import com.android.jack.transformations.request.Replace;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeForm;
import com.android.jack.util.CloneExpressionVisitor;
import com.android.jack.util.ControlFlowHelper;
import com.android.jack.util.DefsAndUsesChainOptimizationTools;
import com.android.sched.item.Description;
import com.android.sched.schedulable.Constraint;
import com.android.sched.schedulable.Filter;
import com.android.sched.schedulable.RunnableSchedulable;
import com.android.sched.schedulable.Support;
import com.android.sched.schedulable.Use;
import com.android.sched.util.config.ThreadConfig;
import com.android.sched.util.log.Tracer;
import com.android.sched.util.log.TracerFactory;
import com.android.sched.util.log.stats.Counter;
import com.android.sched.util.log.stats.CounterImpl;
import com.android.sched.util.log.stats.StatisticId;
import java.util.List;
import javax.annotation.Nonnull;
/**
* Optimization will transform
* a = b // called s0
* ...
* m(a) // called s1
* will be transform to:
* a = b
* ...
* m(b)
*
* Optimization can be apply if the following conditions are respected:
* - condition (0) a into s1 must used only one definition of a
* - condition (1) a must have a value and b must be variable reference or a literal value without
* side effect
* - condition (2) if b is a variable reference then if s0 and s1 are in the same block then the
* variable b must not be redefine between statement s0 and s1
* - condition (3) if b is a variable then if s0 and s1 are not into the same block then all
* definitions of b used by s0 must reach the block containing s1 and all of these definitions
* must dominates s0. The block that contains s1 must not redefine b between the beginning of
* this block and s1
*
* Temporary restriction of condition(3)
* if several definitions of b exists, Jack must checks that all definition statements dominate s0.
* Currently, as it can not be done efficiently (without walk through the cfg) we do not optimize
* this case and restrict optimization to case where b has only one definitions.
*/
@Description("Simplify use definitions chains.")
@Constraint(need = {UseDefsMarker.class, ThreeAddressCodeForm.class, ControlFlowGraph.class})
@Use(DefsAndUsesChainOptimizationTools.class)
@Support(Optimizations.UseDefSimplifier.class)
@Filter(TypeWithoutPrebuiltFilter.class)
public class UseDefsChainsSimplifier extends DefUsesAndUseDefsChainsSimplifier
implements RunnableSchedulable<JMethod> {
@Nonnull
private static final StatisticId<Counter> SIMPLIFIED_USE_DEF_SYNTH = new StatisticId<Counter>(
"jack.optimization.usedef.synthetic", "Synthetic use def chain simplified",
CounterImpl.class, Counter.class);
@Nonnull
private static final StatisticId<Counter> SIMPLIFIED_USE_DEF = new StatisticId<Counter>(
"jack.optimization.usedef.java", "Use def chain simplified",
CounterImpl.class, Counter.class);
@Nonnull
private static final StatisticId<Counter> SIMPLIFIED_USE_DEF_WITH_CST = new StatisticId<Counter>(
"jack.optimization.usedef.constant", "Use def chain with constant simplified",
CounterImpl.class, Counter.class);
@Nonnull
private final com.android.jack.util.filter.Filter<JMethod> filter =
ThreadConfig.get(Options.METHOD_FILTER);
@Nonnull
private final Tracer tracer = TracerFactory.getTracer();
private final boolean optimizeCstDef =
ThreadConfig.get(Optimizations.UseDefSimplifier.OPTIMIZE_CST_DEF).booleanValue();
private class Visitor extends JVisitor {
@Override
public boolean visit(@Nonnull JStatement s1) {
List<JVariableRef> varsUsedBys1 = DefsAndUsesChainOptimizationTools.getUsedVariables(s1);
// Copy variable used by s1 to update the list during the visit
for (JVariableRef varRefOfa : varsUsedBys1.toArray(new JVariableRef[varsUsedBys1.size()])) {
List<DefinitionMarker> defsOfa =
DefsAndUsesChainOptimizationTools.getUsedDefinitions(varRefOfa);
// Condition(0)
if (defsOfa.size() == 1) {
DefinitionMarker defOfa = defsOfa.get(0);
if (stmtCanBeOptimized(s1, defOfa)) {
if (defOfa.getDefinedVariable().isSynthetic()) {
tracer.getStatistic(SIMPLIFIED_USE_DEF_SYNTH).incValue();
} else {
tracer.getStatistic(SIMPLIFIED_USE_DEF).incValue();
}
JExpression exprValueOfa = defOfa.getValue();
TransformationRequest tr = new TransformationRequest(s1);
if (isLiteralWithoutSideEffect(exprValueOfa)) {
tracer.getStatistic(SIMPLIFIED_USE_DEF_WITH_CST).incValue();
JExpression newExpr = new CloneExpressionVisitor().cloneExpression(exprValueOfa);
if (newExpr instanceof JNullLiteral) {
newExpr = new JReinterpretCastOperation(newExpr.getSourceInfo(),
varRefOfa.getType(), newExpr);
}
tr.append(new Replace(varRefOfa, newExpr));
} else {
JVariableRef varRefb = (JVariableRef) exprValueOfa;
JVariableRef newVarRefb = getNewVarRef(varRefb, varRefOfa.getSourceInfo());
UseDefsMarker udmOfNewVarRefb = new UseDefsMarker();
udmOfNewVarRefb.addUsedDefinitions(
DefsAndUsesChainOptimizationTools.getUsedDefinitions(varRefb), newVarRefb);
newVarRefb.addMarker(udmOfNewVarRefb);
varsUsedBys1.add(newVarRefb);
tr.append(new Replace(varRefOfa, newVarRefb));
}
// Update use/def information.
varsUsedBys1.remove(varRefOfa);
defOfa.removeUse(varRefOfa);
tr.commit();
}
}
}
return super.visit(s1);
}
private boolean isLiteralWithoutSideEffect(@Nonnull JExpression expr) {
return expr instanceof JBooleanLiteral || expr instanceof JNullLiteral
|| expr instanceof JNumberValueLiteral;
}
private boolean stmtCanBeOptimized(@Nonnull JStatement s1, @Nonnull DefinitionMarker defOfa) {
// Condition (1)
if (!defOfa.hasValue()) {
return false;
}
// Condition (1)
if (defOfa.getValue() instanceof JVariableRef) {
JVariableRef varRefb = (JVariableRef) defOfa.getValue();
BasicBlock bbOfs1 = ControlFlowHelper.getBasicBlock(s1);
JStatement s0 = defOfa.getStatement();
assert s0 != null;
BasicBlock bbOfs0 = ControlFlowHelper.getBasicBlock(s0);
JVariable b = varRefb.getTarget();
// Condition (2)
if (bbOfs0 == bbOfs1) {
if (!hasLocalDef(b, bbOfs0, s0, s1)) {
return true;
}
} else {
// Condition (3)
List<DefinitionMarker> defsOfbUseFroms0 =
DefsAndUsesChainOptimizationTools.getUsedDefinitions(varRefb);
if (defsOfbUseFroms0.size() == 1 && bbHasOnlyDefinitions(bbOfs1, b, defsOfbUseFroms0)
&& !hasLocalDef(b, bbOfs1, null, s1)) {
return true;
}
}
// Condition (1)
} else if (optimizeCstDef && isLiteralWithoutSideEffect(defOfa.getValue())) {
// No more check since it is a literal value without side effect and that can not be
// redefined
return true;
}
return false;
}
private boolean bbHasOnlyDefinitions(@Nonnull BasicBlock bb, @Nonnull JVariable var,
@Nonnull List<DefinitionMarker> defsToFound) {
int nbDef = 0;
for (DefinitionMarker def : DefsAndUsesChainOptimizationTools.getReachingDefs(bb)) {
if (def.getDefinedVariable() == var) {
if (defsToFound.contains(def)) {
nbDef++;
} else {
return false;
}
}
}
return defsToFound.size() == nbDef;
}
@Override
public boolean visit(@Nonnull JLock x) {
// Do not optimize lock expression, otherwise Jack can add variable aliasing that result
// with lock/unlock expressions which do not use the same register, and it is not supported
// by the runtime
return false;
}
@Override
public boolean visit(@Nonnull JUnlock x) {
// Do not optimize unlock expression, otherwise Jack can add variable aliasing that result
// with lock/unlock expressions which do not use the same register, and it is not supported
// by the runtime
return false;
}
@Override
public boolean visit(@Nonnull JIfStatement jIf) {
super.visit(jIf);
accept(jIf.getIfExpr());
return false;
}
@Override
public boolean visit(@Nonnull JSwitchStatement switchStmt) {
super.visit(switchStmt);
this.accept(switchStmt.getExpr());
return false;
}
}
@Override
public void run(@Nonnull JMethod method) {
if (method.isNative() || method.isAbstract() || !filter.accept(this.getClass(), method)) {
return;
}
ControlFlowGraph cfg = method.getMarker(ControlFlowGraph.class);
assert cfg != null;
Visitor visitor = new Visitor();
for (BasicBlock bb : cfg.getNodes()) {
for (JStatement stmt : bb.getStatements()) {
visitor.accept(stmt);
}
}
}
}