Merge "[CF] Implement simple store/load elimination and avoid storing constants."
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 5ddca3f..07c7c53 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.CfBuilder.StackHelper;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
public class ConstString extends ConstInstruction {
@@ -82,6 +83,12 @@
}
@Override
+ public boolean canBeDeadCode(IRCode code, InternalOptions options) {
+ // The const-string instruction is a throwing instruction in DEX, but not so in CF.
+ return options.outputClassFiles;
+ }
+
+ @Override
public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
stack.storeOutValue(this, it);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 4d9c67a..ce990c1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -3,15 +3,18 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeStaticRange;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.InliningOracle;
import java.util.List;
+import org.objectweb.asm.Opcodes;
public class InvokeStatic extends InvokeMethod {
@@ -86,4 +89,9 @@
public InlineAction computeInlining(InliningOracle decider) {
return decider.computeForInvokeStatic(this);
}
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfInvoke(Opcodes.INVOKESTATIC, getInvokedMethod()));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
index 28d4165..734b2dd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Load.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -44,6 +44,6 @@
@Override
public void buildCf(CfBuilder builder) {
Value value = inValues.get(0);
- builder.add(new CfLoad(value.outType(), 2 * value.getNumber()));
+ builder.add(new CfLoad(value.outType(), builder.getLocalRegister(value)));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
index 2a22f28..ddc4f50 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Store.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -43,6 +43,6 @@
@Override
public void buildCf(CfBuilder builder) {
- builder.add(new CfStore(outType(), 2 * outValue.getNumber()));
+ builder.add(new CfStore(outType(), builder.getLocalRegister(outValue)));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 8c00fcc..aed20acb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -5,12 +5,16 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.Argument;
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.InstructionIterator;
@@ -21,6 +25,11 @@
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 com.android.tools.r8.ir.optimize.CodeRewriter;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover;
+import com.android.tools.r8.utils.InternalOptions;
+import it.unimi.dsi.fastutil.objects.Reference2IntArrayMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -30,27 +39,42 @@
private final DexEncodedMethod method;
private final IRCode code;
private List<CfInstruction> instructions;
+ private Reference2IntMap<Value> argumentRegisters;
+ private int maxLocals;
public static class StackHelper {
- public final DexMethod method;
-
- public StackHelper(DexMethod method) {
- this.method = method;
- }
-
public void loadInValues(Instruction instruction, InstructionListIterator it) {
it.previous();
for (int i = 0; i < instruction.inValues().size(); i++) {
Value value = instruction.inValues().get(i);
StackValue stackValue = new StackValue(value.outType());
+ Instruction load;
+ if (value.isConstant()) {
+ ConstInstruction constant = value.getConstInstruction();
+ if (constant.isConstNumber()) {
+ load = new ConstNumber(stackValue, constant.asConstNumber().getRawValue());
+ } else if (constant.isConstString()) {
+ load = new ConstString(stackValue, constant.asConstString().getValue());
+ } else if (constant.isConstClass()) {
+ load = new ConstClass(stackValue, constant.asConstClass().getValue());
+ } else {
+ throw new Unreachable("Unexpected constant value: " + value);
+ }
+ } else {
+ load = new Load(stackValue, value);
+ }
+ add(load, instruction, it);
+ value.removeUser(instruction);
instruction.replaceValue(value, stackValue);
- add(new Load(stackValue, value), instruction, it);
}
it.next();
}
public void storeOutValue(Instruction instruction, InstructionListIterator it) {
+ if (instruction.isOutConstant()) {
+ return;
+ }
StackValue newOutValue = new StackValue(instruction.outType());
Value oldOutValue = instruction.swapOutValue(newOutValue);
add(new Store(oldOutValue, newOutValue), instruction, it);
@@ -75,11 +99,12 @@
this.code = code;
}
- public Code build() {
+ public Code build(CodeRewriter rewriter, InternalOptions options) {
try {
loadStoreInsertion();
- // TODO(zerny): Optimize load/store patterns.
- // TODO(zerny): Compute locals/register allocation.
+ DeadCodeRemover.removeDeadCode(code, rewriter, options);
+ removeUnneededLoadsAndStores();
+ allocateLocalRegisters();
// TODO(zerny): Compute debug info.
return buildCfCode();
} catch (Unimplemented e) {
@@ -89,7 +114,7 @@
}
private void loadStoreInsertion() {
- StackHelper stack = new StackHelper(method.method);
+ StackHelper stack = new StackHelper();
for (BasicBlock block : code.blocks) {
InstructionListIterator it = block.listIterator();
while (it.hasNext()) {
@@ -99,8 +124,55 @@
}
}
+ private void removeUnneededLoadsAndStores() {
+ Iterator<BasicBlock> blockIterator = code.listIterator();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ InstructionListIterator it = block.listIterator();
+ while (it.hasNext()) {
+ Instruction store = it.next();
+ if (store instanceof Store && store.outValue().numberOfAllUsers() == 1) {
+ Instruction load = it.peekNext();
+ if (load instanceof Load && load.inValues().get(0) == store.outValue()) {
+ Value storeIn = store.inValues().get(0);
+ Value loadOut = load.outValue();
+ loadOut.replaceUsers(storeIn);
+ storeIn.removeUser(store);
+ store.outValue().removeUser(load);
+ // Remove the store.
+ it.previous();
+ it.remove();
+ // Remove the load.
+ it.next();
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+
+ private void allocateLocalRegisters() {
+ // TODO(zerny): Allocate locals based on live ranges.
+ InstructionIterator it = code.instructionIterator();
+ argumentRegisters = new Reference2IntArrayMap<>(
+ method.method.proto.parameters.values.length + (method.accessFlags.isStrict() ? 0 : 1));
+ int argumentRegister = 0;
+ int maxRegister = -1;
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ if (instruction.isArgument()) {
+ argumentRegisters.put(instruction.outValue(), argumentRegister);
+ argumentRegister += instruction.outValue().requiredRegisters();
+ maxRegister = argumentRegister - 1;
+ } else if (instruction.outValue() != null
+ && !(instruction.outValue() instanceof StackValue)) {
+ maxRegister = Math.max(maxRegister, 2 * instruction.outValue().getNumber());
+ }
+ }
+ maxLocals = maxRegister + 1;
+ }
+
private CfCode buildCfCode() {
- int maxLocalNumber = -1;
int maxStack = 0;
int currentStack = 0;
instructions = new ArrayList<>();
@@ -113,26 +185,31 @@
if (instruction.outValue() != null) {
Value outValue = instruction.outValue();
if (outValue instanceof StackValue) {
- ++currentStack;
+ currentStack += outValue.requiredRegisters();
maxStack = Math.max(maxStack, currentStack);
- } else {
- maxLocalNumber = Math.max(maxLocalNumber, outValue.getNumber());
}
}
for (Value inValue : instruction.inValues()) {
if (inValue instanceof StackValue) {
- --currentStack;
+ currentStack -= inValue.requiredRegisters();
}
}
instruction.buildCf(this);
}
}
assert currentStack == 0;
- return new CfCode(maxStack, 2 * (maxLocalNumber + 1), instructions);
+ return new CfCode(maxStack, maxLocals, instructions);
}
// Callbacks
+ public int getLocalRegister(Value value) {
+ if (value.isArgument()) {
+ return argumentRegisters.getInt(value);
+ }
+ return 2 * value.getNumber();
+ }
+
public void add(CfInstruction instruction) {
instructions.add(instruction);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index e31c592..0151b1a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -590,7 +590,7 @@
assert method.getCode().isJarCode();
CfBuilder builder = new CfBuilder(method, code);
// TODO(zerny): Change the return type of CfBuilder::build CfCode once complete.
- Code result = builder.build();
+ Code result = builder.build(codeRewriter, options);
assert result.isCfCode() || result.isJarCode();
method.setCode(result);
}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index ede2e85..3509357 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -129,6 +129,7 @@
reader.accept(node, ASM6);
StringWriter writer = new StringWriter();
for (MethodNode method : node.methods) {
+ writer.append(method.name).append(method.desc).append('\n');
TraceMethodVisitor visitor = new TraceMethodVisitor(new Textifier());
method.accept(visitor);
visitor.p.print(new PrintWriter(writer));
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index d353d88..0fc2489 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -122,6 +122,7 @@
};
String[] javaBytecodeTests = {
+ "constants.Constants",
"hello.Hello",
"arithmetic.Arithmetic",
};