blob: 1ff49d0787548b2f2e4e8d843bacfd1478b7e0ac [file] [log] [blame]
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize.peepholes;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Load;
import com.android.tools.r8.ir.code.StackValue;
import com.android.tools.r8.ir.code.StackValues;
import com.android.tools.r8.ir.code.Store;
import com.android.tools.r8.ir.code.Swap;
import java.util.List;
/**
* {@link StoreSequenceLoadPeephole} looks for the following pattern:
*
* <pre>
* Store v5 <- s0
* ... (stackheight 1) s0 <- v4
* Load s1 <- v5
* </pre>
*
* and replaces with
*
* <pre>
* ... s1 <- v4
* Swap [s0, s1] <- s0, s1
* </pre>
*
* This saves a store and load and removes a use of a local.
*/
public class StoreSequenceLoadPeephole implements BasicBlockPeephole {
private Store store;
private int stackHeight = 0;
private final Point storeExp =
new Point(
(i) -> {
if (!i.isStore()
|| i.asStore().src().getType().isWidePrimitive()
|| i.outValue().hasLocalInfo()
|| i.asStore().outValue().numberOfAllUsers() != 1) {
return false;
}
store = i.asStore();
stackHeight = 0;
return true;
});
private final Wildcard seqExp =
new Wildcard(
(i) -> {
if (stackHeight == 1 && i.isLoad() && i.asLoad().src() == store.outValue()) {
// Allow loadExp to pick up the load.
return false;
}
stackHeight -= PeepholeHelper.numberOfValuesConsumedFromStack(i);
if (stackHeight < 0) {
store = null;
return false;
}
stackHeight += PeepholeHelper.numberOfValuesPutOnStack(i);
return true;
});
private final Point loadExp =
new Point(
(i) -> {
if (store == null
|| !i.isLoad()
|| i.asLoad().src().hasLocalInfo()
|| i.asLoad().src() != store.outValue()) {
return false;
}
return true;
});
private final PeepholeLayout layout = PeepholeLayout.lookForward(storeExp, seqExp, loadExp);
@Override
public boolean match(InstructionListIterator it) {
Match match = layout.test(it);
if (match == null || store == null) {
return false;
}
Store store = storeExp.get(match).asStore();
List<Instruction> seq = seqExp.get(match);
Load load = loadExp.get(match).asLoad();
// Make sure last instruction is a stack value that is not wide.
Instruction last = seq.get(seq.size() - 1);
StackValue lastOut = null;
if (last.outValue() instanceof StackValue) {
lastOut = (StackValue) last.outValue();
} else if (last.outValue() instanceof StackValues) {
StackValue[] stackValues = ((StackValues) last.outValue()).getStackValues();
lastOut = stackValues[stackValues.length - 1];
}
if (lastOut == null || lastOut.getType().isWidePrimitive()) {
return false;
}
// Create the swap instruction sources.
StackValue source = (StackValue) store.src();
store.outValue().removeUser(load);
load.outValue().replaceUsers(source);
// Remove the first store.
it.removeOrReplaceByDebugLocalRead();
it.nextUntil(i -> i == load);
// Insert a swap instruction
StackValue swapLastOut = lastOut.duplicate(source.getHeight());
StackValue swapSource = source.duplicate(swapLastOut.getHeight() + 1);
source.replaceUsers(swapSource);
lastOut.replaceUsers(swapLastOut);
it.replaceCurrentInstruction(new Swap(swapLastOut, swapSource, source, lastOut));
PeepholeHelper.resetNext(it, 2);
return true;
}
@Override
public boolean resetAfterMatch() {
return false;
}
}