blob: 12c13210d240386d1a5bc5c541fb84f9b0912b7a [file] [log] [blame]
// Copyright (c) 2017, 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.cf;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.IRCode;
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.Phi;
import com.android.tools.r8.ir.code.Pop;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.StackValue;
import com.android.tools.r8.ir.code.Store;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class LoadStoreHelper {
private final IRCode code;
private final Map<Value, DexType> types;
public LoadStoreHelper(IRCode code, Map<Value, DexType> types) {
this.code = code;
this.types = types;
}
public void insertLoadsAndStores() {
// Insert per-instruction loads and stores.
for (BasicBlock block : code.blocks) {
InstructionListIterator it = block.listIterator();
while (it.hasNext()) {
Instruction current = it.next();
current.insertLoadAndStores(it, this);
}
}
}
public void insertPhiMoves(CfRegisterAllocator allocator) {
// Insert phi stores in all predecessors.
for (BasicBlock block : code.blocks) {
if (!block.getPhis().isEmpty()) {
// TODO(zerny): Phi's at an exception block must be dealt with at block entry.
assert !block.entry().isMoveException();
for (int predIndex = 0; predIndex < block.getPredecessors().size(); predIndex++) {
BasicBlock pred = block.getPredecessors().get(predIndex);
List<Phi> phis = block.getPhis();
List<PhiMove> moves = new ArrayList<>(phis.size());
for (Phi phi : phis) {
Value value = phi.getOperand(predIndex);
if (allocator.getRegisterForValue(phi) != allocator.getRegisterForValue(value)) {
moves.add(new PhiMove(phi, value));
}
}
InstructionListIterator it = pred.listIterator(pred.getInstructions().size());
it.previous();
movePhis(moves, it);
}
allocator.addToLiveAtEntrySet(block, block.getPhis());
}
}
code.blocks.forEach(BasicBlock::clearUserInfo);
}
private StackValue createStackValue(Value value, int height) {
if (value.outType().isObject()) {
return StackValue.forObjectType(types.get(value), height);
}
return StackValue.forNonObjectType(value.outType(), height);
}
private StackValue createStackValue(DexType type, int height) {
if (type.isPrimitiveType()) {
return StackValue.forNonObjectType(ValueType.fromDexType(type), height);
}
return StackValue.forObjectType(type, height);
}
public void loadInValues(Instruction instruction, InstructionListIterator it) {
int topOfStack = 0;
it.previous();
for (int i = 0; i < instruction.inValues().size(); i++) {
Value value = instruction.inValues().get(i);
StackValue stackValue = createStackValue(value, topOfStack++);
add(load(stackValue, value), instruction, it);
instruction.replaceValue(i, stackValue);
}
it.next();
}
public void storeOutValue(Instruction instruction, InstructionListIterator it) {
if (instruction.outValue() instanceof StackValue) {
assert instruction.isConstInstruction();
return;
}
if (!instruction.outValue().isUsed()) {
popOutValue(instruction.outValue(), instruction, it);
return;
}
StackValue newOutValue = createStackValue(instruction.outValue(), 0);
Value oldOutValue = instruction.swapOutValue(newOutValue);
Store store = new Store(oldOutValue, newOutValue);
// Move the debugging-locals liveness pertaining to the instruction to the store.
instruction.moveDebugValues(store);
add(store, instruction, it);
}
public void popOutValue(Value value, Instruction instruction, InstructionListIterator it) {
StackValue newOutValue = createStackValue(value, 0);
instruction.swapOutValue(newOutValue);
add(new Pop(newOutValue), instruction, it);
}
public void popOutType(DexType type, Instruction instruction, InstructionListIterator it) {
StackValue newOutValue = createStackValue(type, 0);
instruction.swapOutValue(newOutValue);
add(new Pop(newOutValue), instruction, it);
}
private static class PhiMove {
final Phi phi;
final Value operand;
public PhiMove(Phi phi, Value operand) {
this.phi = phi;
this.operand = operand;
}
}
private void movePhis(List<PhiMove> moves, InstructionListIterator it) {
// TODO(zerny): Accounting for non-interfering phis would lower the max stack size.
int topOfStack = 0;
List<StackValue> temps = new ArrayList<>(moves.size());
for (PhiMove move : moves) {
StackValue tmp = createStackValue(move.phi, topOfStack++);
add(load(tmp, move.operand), move.phi.getBlock(), Position.none(), it);
temps.add(tmp);
move.operand.removePhiUser(move.phi);
}
for (int i = moves.size() - 1; i >= 0; i--) {
PhiMove move = moves.get(i);
StackValue tmp = temps.get(i);
FixedLocalValue out = new FixedLocalValue(move.phi);
add(new Store(out, tmp), move.phi.getBlock(), Position.none(), it);
move.phi.replaceUsers(out);
}
}
private Instruction load(StackValue stackValue, Value value) {
if (value.isConstant()) {
ConstInstruction constant = value.getConstInstruction();
if (constant.isConstNumber()) {
return new ConstNumber(stackValue, constant.asConstNumber().getRawValue());
} else if (constant.isConstString()) {
return new ConstString(stackValue, constant.asConstString().getValue());
} else if (constant.isConstClass()) {
return new ConstClass(stackValue, constant.asConstClass().getValue());
} else {
throw new Unreachable("Unexpected constant value: " + value);
}
}
return new Load(stackValue, value);
}
private static void add(
Instruction newInstruction, Instruction existingInstruction, InstructionListIterator it) {
add(newInstruction, existingInstruction.getBlock(), existingInstruction.getPosition(), it);
}
private static void add(
Instruction newInstruction, BasicBlock block, Position position, InstructionListIterator it) {
newInstruction.setBlock(block);
newInstruction.setPosition(position);
it.add(newInstruction);
}
}