Temporarily disable string concat optimization for roll
Revert "Rewrite StringBuilder.appends with non-constants to String.concat"
This reverts commit 1a0a373dd8f7eed2f9dcbd715b097445a4e17f96.
Revert "Fix missing call to newInstances"
This reverts commit ed5226ebcca8663777670307e3520b556cee6bd4.
Change-Id: If4ad3fbf7801834218e4b90048e64a11ce057b2f
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 44506fd..8fe1c7e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2118,7 +2118,7 @@
}
public boolean isAppendCharSequenceMethod(DexMethod method) {
- return method == appendCharSequence || method == appendSubCharSequence;
+ return method == appendCharSequence;
}
public boolean isAppendObjectOrCharSequenceMethod(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
index 4383519..05a91e5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAction.java
@@ -7,8 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -104,7 +102,28 @@
Instruction previous = iterator.previous();
InvokeMethodWithReceiver invoke = previous.asInvokeMethodWithReceiver();
assert invoke != null;
- Value value = insertStringConstantInstruction(appView, code, iterator, invoke, replacement);
+ // If the block has catch handlers, inserting a constant string in the same block as the
+ // append violates our block representation in DEX since constant string is throwing. If the
+ // append is in a block with catch handlers, we simply insert a new constant string in the
+ // entry block after all arguments.
+ Value value;
+ if (!invoke.getBlock().hasCatchHandlers()) {
+ value =
+ iterator.insertConstStringInstruction(
+ appView, code, appView.dexItemFactory().createString(replacement));
+ } else {
+ InstructionListIterator stringInsertIterator = code.entryBlock().listIterator(code);
+ while (stringInsertIterator.hasNext()) {
+ Instruction next = stringInsertIterator.next();
+ if (!next.isArgument()) {
+ stringInsertIterator.previous();
+ break;
+ }
+ }
+ value =
+ stringInsertIterator.insertConstStringInstruction(
+ appView, code, appView.dexItemFactory().createString(replacement));
+ }
iterator.next();
DexMethod invokedMethod = invoke.getInvokedMethod();
if (invoke.isInvokeConstructor(appView.dexItemFactory())) {
@@ -132,6 +151,16 @@
|| factory.stringBuilderMethods.isAppendStringMethod(method);
}
+ private DexMethod getConstructorWithStringParameter(
+ DexMethod invokedMethod, DexItemFactory factory) {
+ if (invokedMethod.getHolderType() == factory.stringBufferType) {
+ return factory.stringBufferMethods.stringConstructor;
+ } else {
+ assert invokedMethod.getHolderType() == factory.stringBuilderType;
+ return factory.stringBuilderMethods.stringConstructor;
+ }
+ }
+
private DexMethod getAppendWithStringParameter(
DexMethod invokedMethod, DexItemFactory factory) {
if (invokedMethod.getHolderType() == factory.stringBufferType) {
@@ -142,128 +171,4 @@
}
}
}
-
- class ReplaceByExistingString implements StringBuilderAction {
-
- private final Value existingString;
-
- public ReplaceByExistingString(Value existingString) {
- assert existingString.isNeverNull();
- this.existingString = existingString;
- }
-
- @Override
- public void perform(
- AppView<?> appView,
- IRCode code,
- InstructionListIterator iterator,
- Instruction instruction,
- StringBuilderOracle oracle) {
- instruction.outValue().replaceUsers(existingString);
- iterator.removeOrReplaceByDebugLocalRead();
- }
- }
-
- class ReplaceByStringConcat implements StringBuilderAction {
-
- private final Value first;
- private final Value second;
-
- private final String newConstant;
-
- private ReplaceByStringConcat(Value first, Value second, String newConstant) {
- assert first != null || newConstant != null;
- assert second != null || newConstant != null;
- this.first = first;
- this.second = second;
- this.newConstant = newConstant;
- }
-
- public static ReplaceByStringConcat replaceByValues(Value first, Value second) {
- return new ReplaceByStringConcat(first, second, null);
- }
-
- public static ReplaceByStringConcat replaceByNewConstantConcatValue(
- String newConstant, Value second) {
- return new ReplaceByStringConcat(null, second, newConstant);
- }
-
- public static ReplaceByStringConcat replaceByValueConcatNewConstant(
- Value first, String newConstant) {
- return new ReplaceByStringConcat(first, null, newConstant);
- }
-
- @Override
- public void perform(
- AppView<?> appView,
- IRCode code,
- InstructionListIterator iterator,
- Instruction instruction,
- StringBuilderOracle oracle) {
- Value constString = null;
- if (newConstant != null) {
- Instruction previous = iterator.previous();
- constString =
- insertStringConstantInstruction(appView, code, iterator, previous, newConstant);
- iterator.next();
- }
- assert first != null || constString != null;
- assert second != null || constString != null;
- // To ensure that we do not fail narrowing when evaluating String.concat, we mark the type
- // as maybe null.
- Value newOutValue =
- code.createValue(
- TypeElement.stringClassType(appView, Nullability.maybeNull()),
- instruction.getLocalInfo());
- iterator.replaceCurrentInstruction(
- InvokeVirtual.builder()
- .setOutValue(newOutValue)
- .setMethod(appView.dexItemFactory().stringMembers.concat)
- .setArguments(
- ImmutableList.of(
- first != null ? first : constString, second != null ? second : constString))
- .build());
- }
- }
-
- static DexMethod getConstructorWithStringParameter(
- DexMethod invokedMethod, DexItemFactory factory) {
- if (invokedMethod.getHolderType() == factory.stringBufferType) {
- return factory.stringBufferMethods.stringConstructor;
- } else {
- assert invokedMethod.getHolderType() == factory.stringBuilderType;
- return factory.stringBuilderMethods.stringConstructor;
- }
- }
-
- static Value insertStringConstantInstruction(
- AppView<?> appView,
- IRCode code,
- InstructionListIterator iterator,
- Instruction instruction,
- String newString) {
- // If the block has catch handlers, inserting a constant string in the same block as the
- // append violates our block representation in DEX since constant string is throwing. If the
- // append is in a block with catch handlers, we simply insert a new constant string in the
- // entry block after all arguments.
- Value value;
- if (!instruction.getBlock().hasCatchHandlers()) {
- value =
- iterator.insertConstStringInstruction(
- appView, code, appView.dexItemFactory().createString(newString));
- } else {
- InstructionListIterator stringInsertIterator = code.entryBlock().listIterator(code);
- while (stringInsertIterator.hasNext()) {
- Instruction next = stringInsertIterator.next();
- if (!next.isArgument()) {
- stringInsertIterator.previous();
- break;
- }
- }
- value =
- stringInsertIterator.insertConstStringInstruction(
- appView, code, appView.dexItemFactory().createString(newString));
- }
- return value;
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
index 93c004f..ee85c13 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
@@ -33,7 +33,7 @@
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.EscapeNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
-import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppendNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppend;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.LoopNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.NewInstanceNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.SplitReferenceNode;
@@ -273,13 +273,7 @@
Value receiver = invoke.getReceiver();
if (oracle.isInit(instruction)) {
InitNode initNode = createInitNode(instruction.asInvokeDirect());
- String constantArgument = oracle.getConstantArgument(instruction);
- initNode.setConstantArgument(constantArgument);
- if (constantArgument == null
- && oracle.isStringConstructor(instruction)
- && invoke.getFirstNonReceiverArgument().isNeverNull()) {
- initNode.setNonConstantArgument(invoke.getFirstNonReceiverArgument());
- }
+ initNode.setConstantArgument(oracle.getConstantArgument(instruction));
if (invoke.arguments().size() == 2) {
Value arg = invoke.getOperand(1);
if (oracle.hasStringBuilderType(arg)) {
@@ -294,15 +288,9 @@
escaped -> nodeConsumer.accept(escaped, createInspectionNode(instruction)));
} else if (oracle.isAppend(instruction)) {
AppendNode appendNode = createAppendNode(instruction.asInvokeVirtual());
- String constantArgument = oracle.getConstantArgument(instruction);
- appendNode.setConstantArgument(constantArgument);
- Value arg = invoke.getFirstNonReceiverArgument();
- if (constantArgument == null
- && oracle.isAppendString(instruction)
- && arg.isNeverNull()) {
- appendNode.setNonConstantArgument(arg);
- }
- if (oracle.hasStringBuilderType(arg.getAliasedValue())) {
+ appendNode.setConstantArgument(oracle.getConstantArgument(instruction));
+ Value arg = invoke.getFirstNonReceiverArgument().getAliasedValue();
+ if (oracle.hasStringBuilderType(arg)) {
insertImplicitToStringNode(
arg, instruction, appendNode, escapeState, nodeConsumer);
}
@@ -353,7 +341,7 @@
private void insertImplicitToStringNode(
Value value,
Instruction instruction,
- InitOrAppendNode node,
+ InitOrAppend node,
StringBuilderEscapeState escapeState,
BiConsumer<Value, StringBuilderNode> nodeConsumer) {
assert escapeState.isLiveStringBuilder(value);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
index 5e01833..c189299 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNode.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Value;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -28,15 +27,7 @@
StringBuilderInstruction asStringBuilderInstructionNode();
}
- interface InitOrAppendNode extends StringBuilderInstruction {
-
- boolean isInitNode();
-
- boolean isAppendNode();
-
- InitNode asInitNode();
-
- AppendNode asAppendNode();
+ interface InitOrAppend extends StringBuilderInstruction {
boolean hasConstantArgument();
@@ -47,22 +38,6 @@
void setImplicitToStringNode(ImplicitToStringNode node);
ImplicitToStringNode getImplicitToStringNode();
-
- boolean hasNonConstantArgument();
-
- Value getNonConstantArgument();
-
- default boolean hasConstantOrNonConstantArgument() {
- return hasConstantArgument() || hasNonConstantArgument();
- }
-
- boolean hasSingleSuccessor();
-
- boolean hasSinglePredecessor();
-
- StringBuilderNode getSingleSuccessor();
-
- StringBuilderNode getSinglePredecessor();
}
private final Set<StringBuilderNode> successors = Sets.newLinkedHashSet();
@@ -94,11 +69,11 @@
return false;
}
- public boolean isInitNode() {
+ boolean isInitNode() {
return false;
}
- public boolean isAppendNode() {
+ boolean isAppendNode() {
return false;
}
@@ -130,15 +105,15 @@
return null;
}
- public InitNode asInitNode() {
+ InitNode asInitNode() {
return null;
}
- public AppendNode asAppendNode() {
+ AppendNode asAppendNode() {
return null;
}
- InitOrAppendNode asInitOrAppend() {
+ InitOrAppend asInitOrAppend() {
return null;
}
@@ -166,11 +141,11 @@
return isDead;
}
- public boolean hasSingleSuccessor() {
+ boolean hasSingleSuccessor() {
return successors.size() == 1;
}
- public StringBuilderNode getSingleSuccessor() {
+ StringBuilderNode getSingleSuccessor() {
assert hasSingleSuccessor();
return successors.iterator().next();
}
@@ -184,11 +159,11 @@
return successors;
}
- public boolean hasSinglePredecessor() {
+ boolean hasSinglePredecessor() {
return predecessors.size() == 1;
}
- public StringBuilderNode getSinglePredecessor() {
+ StringBuilderNode getSinglePredecessor() {
assert hasSinglePredecessor();
return predecessors.iterator().next();
}
@@ -303,19 +278,18 @@
/** An initNode is where a StringBuilder/StringBuffer is initialized. */
static class InitNode extends StringBuilderNode
- implements InitOrAppendNode, StringBuilderInstruction {
+ implements InitOrAppend, StringBuilderInstruction {
private final InvokeDirect instruction;
private ImplicitToStringNode implicitToStringNode;
private String constantArgument;
- private Value nonConstantArgument;
private InitNode(InvokeDirect instruction) {
this.instruction = instruction;
}
@Override
- public boolean isInitNode() {
+ boolean isInitNode() {
return true;
}
@@ -325,12 +299,12 @@
}
@Override
- public InitNode asInitNode() {
+ InitNode asInitNode() {
return this;
}
@Override
- InitOrAppendNode asInitOrAppend() {
+ InitOrAppend asInitOrAppend() {
return this;
}
@@ -355,6 +329,16 @@
}
@Override
+ public void setImplicitToStringNode(ImplicitToStringNode node) {
+ implicitToStringNode = node;
+ }
+
+ @Override
+ public ImplicitToStringNode getImplicitToStringNode() {
+ return implicitToStringNode;
+ }
+
+ @Override
public String getConstantArgument() {
return constantArgument;
}
@@ -363,53 +347,22 @@
public boolean hasConstantArgument() {
return constantArgument != null;
}
-
- public void setNonConstantArgument(Value value) {
- assert value.isNeverNull();
- this.nonConstantArgument = value;
- }
-
- @Override
- public void setImplicitToStringNode(ImplicitToStringNode node) {
- implicitToStringNode = node;
- }
-
- @Override
- public ImplicitToStringNode getImplicitToStringNode() {
- return implicitToStringNode;
- }
-
- @Override
- public boolean hasNonConstantArgument() {
- return nonConstantArgument != null;
- }
-
- @Override
- public Value getNonConstantArgument() {
- assert nonConstantArgument != null;
- return nonConstantArgument;
- }
-
- boolean isConstructorInvokeSideEffectFree(StringBuilderOracle oracle) {
- return oracle.isConstructorInvokeSideEffectFree(instruction);
- }
}
/** AppendNodes are StringBuilder.append or StringBuffer.append. */
static class AppendNode extends StringBuilderNode
- implements InitOrAppendNode, StringBuilderInstruction {
+ implements InitOrAppend, StringBuilderInstruction {
private final InvokeVirtual instruction;
private ImplicitToStringNode implicitToStringNode;
private String constantArgument;
- private Value nonConstantArgument;
private AppendNode(InvokeVirtual instruction) {
this.instruction = instruction;
}
@Override
- public boolean isAppendNode() {
+ boolean isAppendNode() {
return true;
}
@@ -419,12 +372,12 @@
}
@Override
- public AppendNode asAppendNode() {
+ AppendNode asAppendNode() {
return this;
}
@Override
- InitOrAppendNode asInitOrAppend() {
+ InitOrAppend asInitOrAppend() {
return this;
}
@@ -459,21 +412,6 @@
}
@Override
- public boolean hasNonConstantArgument() {
- return nonConstantArgument != null;
- }
-
- public void setNonConstantArgument(Value value) {
- assert value.isNeverNull();
- this.nonConstantArgument = value;
- }
-
- @Override
- public Value getNonConstantArgument() {
- return nonConstantArgument;
- }
-
- @Override
public String getConstantArgument() {
return constantArgument;
}
@@ -602,13 +540,13 @@
*/
static class ImplicitToStringNode extends StringBuilderNode {
- private final InitOrAppendNode initOrAppend;
+ private final InitOrAppend initOrAppend;
- ImplicitToStringNode(InitOrAppendNode initOrAppend) {
+ ImplicitToStringNode(InitOrAppend initOrAppend) {
this.initOrAppend = initOrAppend;
}
- public InitOrAppendNode getInitOrAppend() {
+ public InitOrAppend getInitOrAppend() {
return initOrAppend;
}
@@ -663,7 +601,7 @@
return new OtherStringBuilderNode(instruction);
}
- static ImplicitToStringNode createImplicitToStringNode(InitOrAppendNode otherNode) {
+ static ImplicitToStringNode createImplicitToStringNode(InitOrAppend otherNode) {
return new ImplicitToStringNode(otherNode);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
index 3bf5773..0659a8a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderNodeMuncher.java
@@ -9,18 +9,14 @@
import com.android.tools.r8.ir.optimize.string.StringBuilderAction.AppendWithNewConstantString;
import com.android.tools.r8.ir.optimize.string.StringBuilderAction.RemoveStringBuilderAction;
import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceByConstantString;
-import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceByExistingString;
-import com.android.tools.r8.ir.optimize.string.StringBuilderAction.ReplaceByStringConcat;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.AppendNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
-import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppendNode;
+import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppend;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.NewInstanceNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.StringBuilderInstruction;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ToStringNode;
import com.android.tools.r8.utils.WorkList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -71,14 +67,6 @@
public boolean isLooping(StringBuilderNode root) {
return looping.contains(root);
}
-
- public boolean isEscaping(StringBuilderNode root) {
- return escaping.contains(root);
- }
-
- public boolean isInspecting(StringBuilderNode root) {
- return inspectingCapacity.contains(root);
- }
}
private interface PeepholePattern {
@@ -105,32 +93,30 @@
@Override
public boolean optimize(
StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState) {
- AppendNode appendNode = currentNode.asAppendNode();
- if (appendNode == null || !appendNode.hasSinglePredecessor()) {
+ if (!currentNode.isAppendNode()) {
return false;
}
- InitOrAppendNode previous = currentNode.getSinglePredecessor().asInitOrAppend();
- if (previous == null || !previous.hasSingleSuccessor()) {
+ String currentConstantArgument = getConstantArgumentForNode(currentNode, munchingState);
+ if (currentConstantArgument == null || !currentNode.hasSinglePredecessor()) {
+ return false;
+ }
+ StringBuilderNode previous = currentNode.getSinglePredecessor();
+ String previousConstantArgument = getConstantArgumentForNode(previous, munchingState);
+ if (previousConstantArgument == null || !previous.hasSingleSuccessor()) {
return false;
}
// The capacity changes based on the init call (on JVM it adds 16 to length of input).
if (previous.isInitNode() && munchingState.inspectingCapacity.contains(root)) {
return false;
}
- String currentConstantArgument = getConstantArgumentForNode(appendNode, munchingState);
- if (currentConstantArgument == null) {
- return false;
- }
- String previousConstantArgument = getConstantArgumentForNode(previous, munchingState);
- if (previousConstantArgument == null) {
- return false;
- }
+ assert previous.isInitOrAppend();
String newConstant = previousConstantArgument + currentConstantArgument;
- previous.setConstantArgument(newConstant);
+ InitOrAppend initOrAppend = previous.asInitOrAppend();
+ initOrAppend.setConstantArgument(newConstant);
munchingState.actions.put(
- previous.getInstruction(), new AppendWithNewConstantString(newConstant));
+ initOrAppend.getInstruction(), new AppendWithNewConstantString(newConstant));
munchingState.actions.put(
- appendNode.getInstruction(), RemoveStringBuilderAction.getInstance());
+ currentNode.asAppendNode().getInstruction(), RemoveStringBuilderAction.getInstance());
currentNode.removeNode();
return true;
}
@@ -140,13 +126,13 @@
* This peephole will try to remove toString nodes and replace by a constant string:
*
* <pre>
- * newInstance -> init("foo") -> toString() => newInstance -> init("foo") -> append("bar")
- * actions: [toString() => ReplaceByConstantString("foo")]
+ * newInstance -> init("foo") -> append("bar") -> toString() =>
+ * newInstance -> init("foo") -> append("bar")
* </pre>
*
* <p>If the node is an implicitToString, we update the append of another builder to have the new
- * constant value directly. If not, we keep track of the outValue toString() being replaced by a
- * constant by updating {@code MunchingState.optimizedStrings}
+ * constant value directly. If not, we keep track of the outValue toString() had is replaced by a
+ * constant, by updating {@code MunchingState.optimizedStrings}
*/
private static class MunchToString implements PeepholePattern {
@@ -155,9 +141,6 @@
StringBuilderNode originalRoot,
StringBuilderNode currentNode,
MunchingState munchingState) {
- if (munchingState.isEscaping(originalRoot) || munchingState.isInspecting(originalRoot)) {
- return false;
- }
if (!currentNode.isToStringNode() && !currentNode.isImplicitToStringNode()) {
return false;
}
@@ -165,127 +148,48 @@
if (newInstanceNode == null || !newInstanceNode.hasSingleSuccessor()) {
return false;
}
- InitNode init = newInstanceNode.getSingleSuccessor().asInitNode();
- if (init == null || !init.hasSingleSuccessor()) {
+ StringBuilderNode init = newInstanceNode.getSingleSuccessor();
+ String rootConstantArgument = getConstantArgumentForNode(init, munchingState);
+ if (rootConstantArgument == null || !init.isInitNode()) {
return false;
}
- if (!currentNode.hasSinglePredecessor() || currentNode.getSinglePredecessor() != init) {
- return false;
- }
- String initConstantArgument = getConstantArgumentForNode(init, munchingState);
- if (initConstantArgument == null) {
- return false;
- }
+ // This is either <init>(str) -> toString() or <init>(str) -> append(str) -> toString()
// If the string builder dependency is directly given to another string builder, there is
// no toString() but an append with this string builder as argument.
+ if (!currentNode.hasSinglePredecessor() || !init.hasSingleSuccessor()) {
+ return false;
+ }
+ String constantArgument = null;
+ if (currentNode.getSinglePredecessor() == init) {
+ constantArgument = rootConstantArgument;
+ } else {
+ StringBuilderNode expectedAppend = init.getSingleSuccessor();
+ StringBuilderNode expectedSameAppend = currentNode.getSinglePredecessor();
+ String appendConstantArgument = getConstantArgumentForNode(expectedAppend, munchingState);
+ if (expectedAppend == expectedSameAppend && appendConstantArgument != null) {
+ // TODO(b/190489514): See if this larger pattern is necessary.
+ assert false : "See why this larger pattern is necessary";
+ constantArgument = rootConstantArgument + appendConstantArgument;
+ }
+ }
+ if (constantArgument == null) {
+ return false;
+ }
if (currentNode.isToStringNode()) {
ToStringNode toStringNode = currentNode.asToStringNode();
munchingState.actions.put(
- toStringNode.getInstruction(), new ReplaceByConstantString(initConstantArgument));
+ toStringNode.getInstruction(), new ReplaceByConstantString(constantArgument));
String oldValue =
munchingState.optimizedStrings.put(
- toStringNode.getInstruction().outValue(), initConstantArgument);
+ toStringNode.getInstruction().outValue(), constantArgument);
assert oldValue == null;
} else {
assert currentNode.isImplicitToStringNode();
ImplicitToStringNode implicitToStringNode = currentNode.asImplicitToStringNode();
- InitOrAppendNode initOrAppend = implicitToStringNode.getInitOrAppend();
- initOrAppend.setConstantArgument(initConstantArgument);
+ InitOrAppend initOrAppend = implicitToStringNode.getInitOrAppend();
+ initOrAppend.setConstantArgument(constantArgument);
munchingState.actions.put(
- initOrAppend.getInstruction(), new AppendWithNewConstantString(initConstantArgument));
- }
- munchingState.materializingInstructions.get(originalRoot).remove(currentNode);
- currentNode.removeNode();
- return true;
- }
- }
-
- /**
- * This peephole will try to remove toString nodes and replace by an invoke to String.concat:
- *
- * <pre>
- * newInstance -> init(notNull(string)) -> append(notNull(otherString)) -> toString() =>
- * newInstance -> init(notNull(string)) -> append(otherString)
- * actions: [toString() => string.concat(otherString)]
- * </pre>
- *
- * <p>This pattern only triggers when a constant munching of toString could happen.
- */
- private static class MunchToStringIntoStringConcat implements PeepholePattern {
-
- @Override
- public boolean optimize(
- StringBuilderNode originalRoot,
- StringBuilderNode currentNode,
- MunchingState munchingState) {
- if (munchingState.isEscaping(originalRoot) || munchingState.isInspecting(originalRoot)) {
- return false;
- }
- // TODO(b/129200243): Handle implicit tostring nodes.
- if (!currentNode.isToStringNode() || !currentNode.hasSinglePredecessor()) {
- return false;
- }
- NewInstanceNode newInstanceNode = munchingState.getNewInstanceNode(originalRoot);
- if (newInstanceNode == null || !newInstanceNode.hasSingleSuccessor()) {
- return false;
- }
- InitOrAppendNode firstNode = newInstanceNode.getSingleSuccessor().asInitNode();
- if (firstNode == null || !firstNode.hasSingleSuccessor()) {
- return false;
- }
- if (firstNode.asInitNode().isConstructorInvokeSideEffectFree(munchingState.oracle)
- && "".equals(firstNode.getConstantArgument())
- && firstNode.hasSingleSuccessor()) {
- firstNode = firstNode.getSingleSuccessor().asAppendNode();
- if (firstNode == null
- || !firstNode.hasSinglePredecessor()
- || !firstNode.hasSingleSuccessor()) {
- return false;
- }
- }
- // We cannot String.concat or return the string safely when it is not constant and maybe null.
- if (!firstNode.hasConstantOrNonConstantArgument()) {
- return false;
- }
- List<InitOrAppendNode> initOrAppends = Lists.newArrayList(firstNode);
- if (currentNode.getSinglePredecessor() != firstNode) {
- AppendNode appendAfterFirstNode = firstNode.getSingleSuccessor().asAppendNode();
- AppendNode appendBeforeToString = currentNode.getSinglePredecessor().asAppendNode();
- if (appendAfterFirstNode == null
- || appendAfterFirstNode != appendBeforeToString
- || !appendAfterFirstNode.hasConstantOrNonConstantArgument()) {
- return false;
- }
- initOrAppends.add(appendAfterFirstNode);
- }
- // Check that all values are not constant otherwise we can compute the constant value and
- // replace all entirely.
- if (Iterables.all(initOrAppends, InitOrAppendNode::hasConstantArgument)) {
- return false;
- }
- // Replace with the string itself.
- Instruction currentInstruction = currentNode.asToStringNode().getInstruction();
- InitOrAppendNode first = initOrAppends.get(0);
- if (initOrAppends.size() == 1) {
- munchingState.actions.put(
- currentInstruction, new ReplaceByExistingString(first.getNonConstantArgument()));
- } else {
- InitOrAppendNode second = initOrAppends.get(1);
- ReplaceByStringConcat concatAction;
- if (first.hasConstantArgument()) {
- concatAction =
- ReplaceByStringConcat.replaceByNewConstantConcatValue(
- first.getConstantArgument(), second.getNonConstantArgument());
- } else if (second.hasConstantArgument()) {
- concatAction =
- ReplaceByStringConcat.replaceByValueConcatNewConstant(
- first.getNonConstantArgument(), second.getConstantArgument());
- } else {
- concatAction =
- ReplaceByStringConcat.replaceByValues(
- first.getNonConstantArgument(), second.getNonConstantArgument());
- }
- munchingState.actions.put(currentInstruction, concatAction);
+ initOrAppend.getInstruction(), new AppendWithNewConstantString(constantArgument));
}
munchingState.materializingInstructions.get(originalRoot).remove(currentNode);
currentNode.removeNode();
@@ -294,11 +198,21 @@
}
private static String getConstantArgumentForNode(
- InitOrAppendNode node, MunchingState munchingState) {
- if (node.hasConstantArgument()) {
- return node.getConstantArgument();
+ StringBuilderNode node, MunchingState munchingState) {
+ if (node.isAppendNode()) {
+ AppendNode appendNode = node.asAppendNode();
+ if (appendNode.hasConstantArgument()) {
+ return appendNode.getConstantArgument();
+ }
+ return getOptimizedConstantArgument(appendNode, munchingState);
+ } else if (node.isInitNode()) {
+ InitNode initNode = node.asInitNode();
+ if (initNode.hasConstantArgument()) {
+ return initNode.getConstantArgument();
+ }
+ return getOptimizedConstantArgument(initNode, munchingState);
}
- return getOptimizedConstantArgument(node, munchingState);
+ return null;
}
private static String getOptimizedConstantArgument(
@@ -348,6 +262,7 @@
removeNode = true;
}
} else if (currentNode.isInitNode()
+ && currentNode.asInitNode().hasConstantArgument()
&& currentNode.hasSinglePredecessor()
&& currentNode.getSinglePredecessor().isNewInstanceNode()
&& currentNode.getSuccessors().isEmpty()
@@ -397,12 +312,7 @@
}
private static final PeepholePattern[] peepholePatterns =
- new PeepholePattern[] {
- new MunchAppends(),
- new MunchToString(),
- new MunchToStringIntoStringConcat(),
- new MunchNonMaterializing()
- };
+ new PeepholePattern[] {new MunchAppends(), new MunchToString(), new MunchNonMaterializing()};
static boolean optimize(
StringBuilderNode root, StringBuilderNode currentNode, MunchingState munchingState) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
index 8b59c2f..063f791 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOracle.java
@@ -43,12 +43,6 @@
boolean isInit(Instruction instruction);
- boolean isAppendString(Instruction instruction);
-
- boolean isStringConstructor(Instruction instruction);
-
- boolean isConstructorInvokeSideEffectFree(Instruction instruction);
-
class DefaultStringBuilderOracle implements StringBuilderOracle {
private final DexItemFactory factory;
@@ -191,12 +185,12 @@
}
DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
if (factory.stringBuilderMethods.isAppendObjectOrCharSequenceMethod(invokedMethod)
- || factory.stringBufferMethods.isAppendObjectOrCharSequenceMethod(invokedMethod)) {
+ || factory.stringBufferMethods.isAppendObjectOrCharSequenceMethod(invokedMethod)
+ || factory.stringBuilderMethods.charSequenceConstructor == invokedMethod
+ || factory.stringBufferMethods.charSequenceConstructor == invokedMethod) {
+ assert instruction.inValues().size() == 2;
return !instruction.inValues().get(1).getType().isStringType(factory);
}
- if (invokedMethod.isInstanceInitializer(factory)) {
- return !isConstructorInvokeSideEffectFree(instruction);
- }
if (factory.stringBuilderMethods.isAppendCharArrayMethod(invokedMethod)
|| factory.stringBufferMethods.isAppendCharArrayMethod(invokedMethod)) {
return instruction.asInvokeVirtual().getFirstNonReceiverArgument().isMaybeNull();
@@ -213,41 +207,5 @@
return factory.stringBuilderMethods.isConstructorMethod(invokedMethod)
|| factory.stringBufferMethods.isConstructorMethod(invokedMethod);
}
-
- @Override
- public boolean isAppendString(Instruction instruction) {
- if (!instruction.isInvokeMethod()) {
- return false;
- }
- DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
- return factory.stringBuilderMethods.isAppendStringMethod(invokedMethod)
- || factory.stringBufferMethods.isAppendStringMethod(invokedMethod);
- }
-
- @Override
- public boolean isStringConstructor(Instruction instruction) {
- if (!instruction.isInvokeMethod()) {
- return false;
- }
- DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
- return invokedMethod == factory.stringBuilderMethods.stringConstructor
- || invokedMethod == factory.stringBufferMethods.stringConstructor;
- }
-
- @Override
- public boolean isConstructorInvokeSideEffectFree(Instruction instruction) {
- if (!instruction.isInvokeConstructor(factory)) {
- return false;
- }
- DexMethod invokedMethod = instruction.asInvokeMethod().getInvokedMethod();
- if (invokedMethod.getHolderType() == factory.stringBuilderType) {
- return factory.stringBuilderMethods.constructorInvokeIsSideEffectFree(
- invokedMethod, instruction.inValues());
- } else {
- assert invokedMethod.getHolderType() == factory.stringBufferType;
- return factory.stringBufferMethods.constructorInvokeIsSideEffectFree(
- invokedMethod, instruction.inValues());
- }
- }
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendConstantAppendNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendConstantAppendNotNullTest.java
index 4646691..d4ac494 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendConstantAppendNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendConstantAppendNotNullTest.java
@@ -53,6 +53,7 @@
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ // TODO(b/129200243): Should only have two appends.
assertEquals(3, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendNotNullTest.java
index cbe8fa7..c28729a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullAppendNotNullTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,9 +52,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
- assertThat(methodSubject, CodeMatchers.invokesMethodWithName("concat"));
+ // TODO(b/129200243): Should be String.concat.
+ assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(2, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullTest.java
index 8005ed5..d2c95ab 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatAppendNotNullTest.java
@@ -7,14 +7,12 @@
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderAppends;
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderInits;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,9 +52,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
- assertThat(methodSubject, not(CodeMatchers.invokesMethodWithName("concat")));
+ // TODO(b/129200243): This should just be arg.toString()
+ assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(1, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatConstantInitTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatConstantInitTest.java
index 0f7d286..9b9280f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatConstantInitTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatConstantInitTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,9 +52,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
- assertThat(methodSubject, CodeMatchers.invokesMethodWithName("concat"));
+ // TODO(b/129200243): Should be String.concat.
+ assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(1, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
});
}
@@ -64,7 +63,7 @@
public static void main(String[] args) {
String arg = System.currentTimeMillis() > 0 ? "o" : null;
if (arg != null) {
- // The optimization should join "ok" into init and then concat with arg.
+ // The optimization should join "ok" into init and then append with arg.
System.out.println(new StringBuilder("o").append("k").append(arg).toString());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullAppendNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullAppendNotNullTest.java
index aca301c..4dce8fd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullAppendNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullAppendNotNullTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,9 +52,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
- assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
- assertThat(methodSubject, CodeMatchers.invokesMethodWithName("concat"));
+ // TODO(b/129200243): Should be concat.
+ assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ assertEquals(1, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullTest.java
index 4bb550e..690a604 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatInitNotNullTest.java
@@ -7,14 +7,12 @@
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderAppends;
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderInits;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,9 +52,9 @@
inspect -> {
MethodSubject methodSubject = inspect.clazz(Main.class).mainMethod();
assertThat(methodSubject, isPresent());
- assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
+ // TODO(b/129200243): This should just be the string as is.
+ assertEquals(1, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
- assertThat(methodSubject, not(CodeMatchers.invokesMethodWithName("concat")));
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatNoRewriteConstantsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatNoRewriteConstantsTest.java
index 33320dc..73e61bc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatNoRewriteConstantsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatNoRewriteConstantsTest.java
@@ -7,14 +7,12 @@
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderAppends;
import static com.android.tools.r8.ir.optimize.string.utils.StringBuilderCodeMatchers.countStringBuilderInits;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,7 +54,6 @@
assertThat(methodSubject, isPresent());
assertEquals(0, countStringBuilderInits(methodSubject.asFoundMethodSubject()));
assertEquals(0, countStringBuilderAppends(methodSubject.asFoundMethodSubject()));
- assertThat(methodSubject, not(CodeMatchers.invokesMethodWithName("concat")));
});
}