blob: 6da064061ab75ee628446d840a51380e2fa616f8 [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.JIfStatement;
import com.android.jack.ir.ast.JLock;
import com.android.jack.ir.ast.JMethod;
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.transformations.request.Replace;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeForm;
import com.android.jack.util.ControlFlowHelper;
import com.android.jack.util.OptimizationTools;
import com.android.jack.util.filter.Filter;
import com.android.sched.item.Description;
import com.android.sched.schedulable.Constraint;
import com.android.sched.schedulable.RunnableSchedulable;
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
* - condition (2) 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 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(OptimizationTools.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 final Filter<JMethod> filter = ThreadConfig.get(Options.METHOD_FILTER);
@Nonnull
private final Tracer tracer = TracerFactory.getTracer();
private class Visitor extends JVisitor {
@Override
public boolean visit(@Nonnull JStatement s1) {
List<JVariableRef> varsUsedBys1 = OptimizationTools.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 = OptimizationTools.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();
}
JVariableRef varRefb = (JVariableRef) defOfa.getValue();
JVariableRef newVarRefb = getNewVarRef(varRefb);
UseDefsMarker udmOfNewVarRefb = new UseDefsMarker();
udmOfNewVarRefb.addUsedDefinitions(OptimizationTools.getUsedDefinitions(varRefb),
newVarRefb);
newVarRefb.addMarker(udmOfNewVarRefb);
varsUsedBys1.add(newVarRefb);
varsUsedBys1.remove(varRefOfa);
defOfa.removeUse(varRefOfa);
TransformationRequest tr = new TransformationRequest(s1);
tr.append(new Replace(varRefOfa, newVarRefb));
tr.commit();
}
}
}
return super.visit(s1);
}
private boolean stmtCanBeOptimized(@Nonnull JStatement s1, @Nonnull DefinitionMarker defOfa) {
// Condition (1)
if (!defOfa.hasValue() || !(defOfa.getValue() instanceof JVariableRef)) {
return false;
}
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 = OptimizationTools.getUsedDefinitions(varRefb);
if (defsOfbUseFroms0.size() == 1 && bbHasOnlyDefinitions(bbOfs1, b, defsOfbUseFroms0)
&& !hasLocalDef(b, bbOfs1, null, s1)) {
return true;
}
}
return false;
}
private boolean bbHasOnlyDefinitions(@Nonnull BasicBlock bb, @Nonnull JVariable var,
@Nonnull List<DefinitionMarker> defsToFound) {
int nbDef = 0;
for (DefinitionMarker def : OptimizationTools.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) throws Exception {
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);
}
}
}
}