Extend the use of LIR to application writer
Change-Id: Ia48dced965b3c0f665e81bbb37e90e7314d8dd5d
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ecb29de..cf5b2c1 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -63,10 +63,8 @@
import com.android.tools.r8.kotlin.KotlinMetadataUtils;
import com.android.tools.r8.naming.IdentifierMinifier;
import com.android.tools.r8.naming.Minifier;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.PrefixRewritingNamingLens;
import com.android.tools.r8.naming.ProguardMapMinifier;
-import com.android.tools.r8.naming.RecordInvokeDynamicInvokeCustomRewriter;
import com.android.tools.r8.naming.RecordRewritingNamingLens;
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
import com.android.tools.r8.optimize.BridgeHoistingToSharedSyntheticSuperClass;
@@ -765,10 +763,6 @@
assert appView.dexItemFactory().verifyNoCachedTypeElements();
}
- // TODO(b/225838009): Move further down.
- LirConverter.finalizeLirToOutputFormat(appView, timing, executorService);
- assert appView.dexItemFactory().verifyNoCachedTypeElements();
-
// Perform minification.
if (options.getProguardConfiguration().hasApplyMappingFile()) {
timing.begin("apply-mapping");
@@ -781,17 +775,13 @@
timing.begin("Minification");
new Minifier(appView.withLiveness()).run(executorService, timing);
timing.end();
- } else {
- timing.begin("MinifyIdentifiers");
- new IdentifierMinifier(appView, NamingLens.getIdentityLens()).run(executorService);
- timing.end();
- timing.begin("RecordInvokeDynamicRewrite");
- new RecordInvokeDynamicInvokeCustomRewriter(appView, NamingLens.getIdentityLens())
- .run(executorService);
- timing.end();
}
appView.appInfo().notifyMinifierFinished();
+ timing.begin("MinifyIdentifiers");
+ new IdentifierMinifier(appView).run(executorService);
+ timing.end();
+
// If a method filter is present don't produce output since the application is likely partial.
if (options.hasMethodsFilter()) {
System.out.println("Finished compilation with method filter: ");
@@ -870,6 +860,9 @@
assert appView.verifyMovedMethodsHaveOriginalMethodPosition();
timing.end();
+ LirConverter.finalizeLirToOutputFormat(appView, timing, executorService);
+ assert appView.dexItemFactory().verifyNoCachedTypeElements();
+
// Generate the resulting application resources.
writeApplication(appView, inputApp, executorService);
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index 40079b6..fa97f59 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -10,8 +10,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -23,6 +23,8 @@
import com.android.tools.r8.graph.LibraryClass;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ThrowExceptionCode;
+import com.android.tools.r8.lightir.LirCode;
+import com.android.tools.r8.lightir.LirCode.TryCatchTable;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
@@ -135,14 +137,24 @@
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
method -> {
- Code code = method.getDefinition().getCode();
- if (!code.isDexCode()) {
- return;
- }
- for (TryHandler handler : code.asDexCode().getHandlers()) {
- for (TypeAddrPair pair : handler.pairs) {
- DexType rewrittenType = appView.graphLens().lookupType(pair.getType());
- findReferencedLibraryClasses(rewrittenType, clazz);
+ if (appView.enableWholeProgramOptimizations()) {
+ LirCode<Integer> code = method.getDefinition().getCode().asLirCode();
+ if (code != null && code.hasTryCatchTable()) {
+ TryCatchTable tryCatchTable = code.getTryCatchTable();
+ tryCatchTable.forEachHandler(
+ (blockIndex, handlers) ->
+ handlers
+ .getGuards()
+ .forEach(guard -> findReferencedLibraryClasses(guard, clazz)));
+ }
+ } else {
+ DexCode code = method.getDefinition().getCode().asDexCode();
+ if (code != null) {
+ for (TryHandler handler : code.getHandlers()) {
+ for (TypeAddrPair pair : handler.pairs) {
+ findReferencedLibraryClasses(pair.getType(), clazz);
+ }
+ }
}
}
});
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
index 63f24c2..903796b 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
@@ -195,7 +195,9 @@
@Override
public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
- throw new Unreachable("Should not be called");
+ registry.registerNewInstance(exceptionType);
+ registry.registerInvokeDirect(
+ registry.dexItemFactory().createInstanceInitializer(exceptionType));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
index 56f38c3..f384ef5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -170,6 +170,10 @@
return get(Opcodes.INT_SWITCH);
}
+ public boolean mayHaveInvokeCustom() {
+ return get(Opcodes.INVOKE_CUSTOM);
+ }
+
public boolean mayHaveInvokeDirect() {
return get(Opcodes.INVOKE_DIRECT);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
index 5eb58db..5dcd034 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
@@ -12,14 +12,18 @@
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.passes.AdaptClassStringsRewriter;
+import com.android.tools.r8.ir.conversion.passes.CodeRewriterPassCollection;
import com.android.tools.r8.ir.conversion.passes.ConstResourceNumberRemover;
import com.android.tools.r8.ir.conversion.passes.ConstResourceNumberRewriter;
+import com.android.tools.r8.ir.conversion.passes.DexItemBasedConstStringRemover;
import com.android.tools.r8.ir.conversion.passes.FilledNewArrayRewriter;
import com.android.tools.r8.ir.optimize.ConstantCanonicalizer;
import com.android.tools.r8.ir.optimize.DeadCodeRemover;
import com.android.tools.r8.lightir.IR2LirConverter;
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirStrategy;
+import com.android.tools.r8.naming.RecordInvokeDynamicInvokeCustomRewriter;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.ThreadUtils;
@@ -152,11 +156,20 @@
DeadCodeRemover deadCodeRemover = new DeadCodeRemover(appView);
String output = appView.options().isGeneratingClassFiles() ? "CF" : "DEX";
timing.begin("LIR->IR->" + output);
+ CodeRewriterPassCollection codeRewriterPassCollection =
+ new CodeRewriterPassCollection(
+ new AdaptClassStringsRewriter(appView),
+ new ConstResourceNumberRemover(appView),
+ new DexItemBasedConstStringRemover(appView),
+ new RecordInvokeDynamicInvokeCustomRewriter(appView),
+ new FilledNewArrayRewriter(appView));
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz ->
clazz.forEachProgramMethod(
- m -> finalizeLirMethodToOutputFormat(m, deadCodeRemover, appView)),
+ m ->
+ finalizeLirMethodToOutputFormat(
+ m, deadCodeRemover, appView, codeRewriterPassCollection)),
appView.options().getThreadingModule(),
executorService);
timing.end();
@@ -169,7 +182,8 @@
private static void finalizeLirMethodToOutputFormat(
ProgramMethod method,
DeadCodeRemover deadCodeRemover,
- AppView<? extends AppInfoWithClassHierarchy> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ CodeRewriterPassCollection codeRewriterPassCollection) {
Code code = method.getDefinition().getCode();
if (!(code instanceof LirCode)) {
return;
@@ -177,10 +191,7 @@
Timing onThreadTiming = Timing.empty();
IRCode irCode = method.buildIR(appView, MethodConversionOptions.forPostLirPhase(appView));
assert irCode.verifyInvokeInterface(appView);
- ConstResourceNumberRemover constResourceNumberRemover = new ConstResourceNumberRemover(appView);
- constResourceNumberRemover.run(irCode, onThreadTiming);
- FilledNewArrayRewriter filledNewArrayRewriter = new FilledNewArrayRewriter(appView);
- boolean changed = filledNewArrayRewriter.run(irCode, onThreadTiming).hasChanged().toBoolean();
+ boolean changed = codeRewriterPassCollection.run(irCode, null, null, onThreadTiming);
if (appView.options().isGeneratingDex() && changed) {
ConstantCanonicalizer constantCanonicalizer =
new ConstantCanonicalizer(appView, method, irCode);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/AdaptClassStringsRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/AdaptClassStringsRewriter.java
new file mode 100644
index 0000000..dbbac32
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/AdaptClassStringsRewriter.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2024, 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.conversion.passes;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+import com.android.tools.r8.naming.IdentifierMinifier;
+import com.android.tools.r8.shaking.ProguardClassFilter;
+
+public class AdaptClassStringsRewriter extends CodeRewriterPass<AppInfoWithClassHierarchy> {
+
+ private final ProguardClassFilter adaptClassStrings;
+
+ public AdaptClassStringsRewriter(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ super(appView);
+ this.adaptClassStrings = appView.options().getProguardConfiguration().getAdaptClassStrings();
+ }
+
+ @Override
+ protected String getRewriterId() {
+ return "AdaptClassStringsRewriter";
+ }
+
+ @Override
+ protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
+ return !adaptClassStrings.isEmpty()
+ && adaptClassStrings.matches(code.context().getHolderType())
+ && code.metadata().mayHaveConstString();
+ }
+
+ @Override
+ protected CodeRewriterResult rewriteCode(IRCode code) {
+ boolean hasChanged = false;
+ InstructionListIterator iterator = code.instructionListIterator();
+ while (iterator.hasNext()) {
+ ConstString instruction = iterator.next().asConstString();
+ if (instruction != null) {
+ DexString replacement =
+ IdentifierMinifier.getRenamedStringLiteral(appView(), instruction.getValue());
+ if (replacement.isNotIdenticalTo(instruction.getValue())) {
+ iterator.replaceCurrentInstructionWithConstString(appView, code, replacement, null);
+ hasChanged = true;
+ }
+ }
+ }
+ return CodeRewriterResult.hasChanged(hasChanged);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
index bb62066..dbbf1f6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPassCollection.java
@@ -9,17 +9,23 @@
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination;
import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
import com.android.tools.r8.ir.optimize.string.StringBuilderAppendOptimizer;
import com.android.tools.r8.utils.Timing;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
public class CodeRewriterPassCollection {
private final List<CodeRewriterPass<?>> passes;
+ public CodeRewriterPassCollection(CodeRewriterPass<?>... passes) {
+ this(Arrays.asList(passes));
+ }
+
public CodeRewriterPassCollection(List<CodeRewriterPass<?>> passes) {
this.passes = passes;
}
@@ -46,14 +52,17 @@
return new CodeRewriterPassCollection(passes);
}
- public void run(
+ public boolean run(
IRCode code,
MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext,
Timing timing) {
+ boolean changed = false;
for (CodeRewriterPass<?> pass : passes) {
- pass.run(code, methodProcessor, methodProcessingContext, timing);
// TODO(b/286345542): Run printMethod after each run.
+ CodeRewriterResult result = pass.run(code, methodProcessor, methodProcessingContext, timing);
+ changed |= result.hasChanged().isTrue();
}
+ return changed;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/DexItemBasedConstStringRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/DexItemBasedConstStringRemover.java
new file mode 100644
index 0000000..15a4793
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/DexItemBasedConstStringRemover.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2024, 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.conversion.passes;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.code.DexItemBasedConstString;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
+
+public class DexItemBasedConstStringRemover extends CodeRewriterPass<AppInfoWithClassHierarchy> {
+
+ public DexItemBasedConstStringRemover(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ super(appView);
+ }
+
+ @Override
+ protected String getRewriterId() {
+ return "DexItemBasedConstStringRemover";
+ }
+
+ @Override
+ protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
+ return code.metadata().mayHaveDexItemBasedConstString();
+ }
+
+ @Override
+ protected CodeRewriterResult rewriteCode(IRCode code) {
+ boolean hasChanged = false;
+ InstructionListIterator iterator = code.instructionListIterator();
+ while (iterator.hasNext()) {
+ DexItemBasedConstString instruction = iterator.next().asDexItemBasedConstString();
+ if (instruction != null) {
+ DexString replacement =
+ instruction.getNameComputationInfo().computeNameFor(instruction.getItem(), appView());
+ iterator.replaceCurrentInstructionWithConstString(appView, code, replacement, null);
+ hasChanged = true;
+ }
+ }
+ return CodeRewriterResult.hasChanged(hasChanged);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/FilledNewArrayRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/FilledNewArrayRewriter.java
index 7c476ff..eb90225 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/FilledNewArrayRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/FilledNewArrayRewriter.java
@@ -45,11 +45,9 @@
public class FilledNewArrayRewriter extends CodeRewriterPass<AppInfo> {
- private final RewriteArrayOptions rewriteArrayOptions;
private static final Set<Instruction> NOTHING = ImmutableSet.of();
- private boolean mayHaveRedundantBlocks;
- Set<Instruction> toRemove = NOTHING;
+ private final RewriteArrayOptions rewriteArrayOptions;
public FilledNewArrayRewriter(AppView<?> appView) {
super(appView);
@@ -63,67 +61,7 @@
@Override
protected CodeRewriterResult rewriteCode(IRCode code) {
- assert !mayHaveRedundantBlocks;
- assert toRemove == NOTHING;
- CodeRewriterResult result = noChange();
- BooleanBox pendingRewrites = new BooleanBox(true);
- while (pendingRewrites.get()) {
- pendingRewrites.set(false);
- BasicBlockIterator blockIterator = code.listIterator();
- while (blockIterator.hasNext()) {
- BasicBlock block = blockIterator.next();
- BasicBlockInstructionListIterator instructionIterator = block.listIterator(code);
- while (instructionIterator.hasNext()) {
- Instruction instruction = instructionIterator.next();
- if (instruction.isNewArrayFilled()) {
- result =
- processInstruction(
- code,
- blockIterator,
- instructionIterator,
- instruction.asNewArrayFilled(),
- result,
- pendingRewrites);
- }
- }
- }
- if (!toRemove.isEmpty()) {
- Set<Instruction> additionalToRemove = SetUtils.newIdentityHashSet();
- InstructionListIterator it = code.instructionListIterator();
- while (it.hasNext()) {
- Instruction next = it.next();
- if (toRemove.contains(next)) {
- // Also remove constants used by the removed NewArrayFilled.
- if (next.isNewArrayFilled()) {
- next.inValues()
- .forEach(
- value -> {
- if (value.hasSingleUniqueUser()) {
- additionalToRemove.add(value.getDefinition());
- }
- });
- }
- it.remove();
- mayHaveRedundantBlocks = true;
- }
- }
- if (!additionalToRemove.isEmpty()) {
- InstructionListIterator itAdditional = code.instructionListIterator();
- while (itAdditional.hasNext()) {
- Instruction next = itAdditional.next();
- if (additionalToRemove.contains(next)) {
- itAdditional.remove();
- mayHaveRedundantBlocks = true;
- }
- }
- }
- }
- toRemove = NOTHING;
- if (mayHaveRedundantBlocks) {
- code.removeRedundantBlocks();
- }
- }
- return result;
+ return new FilledNewArrayCodeRewriter().rewriteCode(code);
}
@Override
@@ -131,293 +69,499 @@
return code.metadata().mayHaveNewArrayFilled();
}
- private boolean isNewArrayFilledOfConstants(NewArrayFilled newArrayFilled) {
- for (Value inValue : newArrayFilled.inValues()) {
- if (!inValue.isConstNumber() && !inValue.isConstString() && !inValue.isConstClass()) {
- return false;
- }
- }
- return true;
- }
+ private class FilledNewArrayCodeRewriter {
- private boolean isDefinedByNewArrayFilledOfConstants(Value value) {
- if (!value.isDefinedByInstructionSatisfying(Instruction::isNewArrayFilled)) {
- return false;
- }
- return isNewArrayFilledOfConstants(value.definition.asNewArrayFilled());
- }
+ private boolean mayHaveRedundantBlocks = false;
+ private Set<Instruction> toRemove = NOTHING;
- public NewArrayFilled copyConstantsNewArrayFilled(IRCode code, NewArrayFilled original) {
- assert isNewArrayFilledOfConstants(original);
- Value newValue = code.createValue(original.getOutType(), original.getLocalInfo());
- List<Value> newArguments = new ArrayList<>(original.inValues().size());
- for (Value value : original.inValues()) {
- if (value.isConstNumber()) {
- newArguments.add(
- ConstNumber.copyOf(code, value.getDefinition().asConstNumber()).outValue());
- } else if (value.isConstString()) {
- newArguments.add(
- ConstString.copyOf(code, value.getDefinition().asConstString()).outValue());
- } else if (value.isConstClass()) {
- newArguments.add(ConstClass.copyOf(code, value.getDefinition().asConstClass()).outValue());
- } else {
- assert false;
- }
- }
- return new NewArrayFilled(original.getArrayType(), newValue, newArguments);
- }
-
- private CodeRewriterResult processInstruction(
- IRCode code,
- BasicBlockIterator blockIterator,
- BasicBlockInstructionListIterator instructionIterator,
- NewArrayFilled newArrayFilled,
- CodeRewriterResult result,
- BooleanBox pendingRewrites) {
- if (canUseNewArrayFilled(newArrayFilled)) {
- return result;
- }
- if (newArrayFilled.hasUnusedOutValue()) {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- } else if (canUseNewArrayFilledData(newArrayFilled)) {
- rewriteToNewArrayFilledData(code, blockIterator, instructionIterator, newArrayFilled);
- } else if (newArrayFilled.outValue().hasSingleUniqueUser()
- && newArrayFilled.outValue().singleUniqueUser().isNewArrayFilled()
- && isNewArrayFilledOfConstants(newArrayFilled)) {
- if (canUseNewArrayFilled(newArrayFilled.outValue().singleUniqueUser().asNewArrayFilled())) {
- // The NewArrayFilled user is supported, so rewrite here.
- rewriteToArrayPuts(code, blockIterator, instructionIterator, newArrayFilled);
- } else {
- // The NewArrayFilled user is not supported so leave for rewriting after that.
- //
- // The effect of this is that when the user of this NewArrayFilled is rewritten to puts,
- // the NewArrayFilled construction is copied to the use site
- //
- // Input:
- //
- // v0 <- Const X
- // v1 <- NewArrayFilled(v0)
- // v2 <- Const Y
- // v3 <- NewArrayFilled(v2)
- // v4 <- NewArrayFilled(v1, v3)
- //
- // After rewriting the user (v0 - v3 are unused and removed):
- //
- // v4 <- NewArrayEmpty(...)
- // v5 <- Const X
- // v6 <- NewArrayFilled(v5)
- // APut v4, <Const 0>, v6
- // v7 <- Const Y
- // v8 <- NewArrayFilled(v7)
- // APut v4, <Const 1>, v8
- //
- // Setting pending rewrites cause the copied NewArrayFilled to be rewritten in their new
- // location in the fixpoint:
- //
- // v4 <- NewArrayEmpty(...)
- // v9 <- NewArrayEmpty(...)
- // v10 <- Const X
- // APut v9, <Const 0>, v10
- // APut v4, <Const 0>, v9
- // v11 <- NewArrayEmpty(...)
- // v12 <- Const Y
- // APut v11, <Const 0>, v12
- // APut v4, <Const 1>, v11
- //
- // If the NewArrayFilled which gets moved is supported then the second rewriting in the
- // fixpoint does not happen.
- pendingRewrites.set(true);
- }
- } else {
- rewriteToArrayPuts(code, blockIterator, instructionIterator, newArrayFilled);
- }
- return CodeRewriterResult.HAS_CHANGED;
- }
-
- private boolean canUseNewArrayFilled(NewArrayFilled newArrayFilled) {
- if (!options.isGeneratingDex()) {
- return false;
- }
- int size = newArrayFilled.size();
- if (size < rewriteArrayOptions.minSizeForFilledNewArray) {
- return false;
- }
- // filled-new-array is implemented only for int[] and Object[].
- DexType arrayType = newArrayFilled.getArrayType();
- if (arrayType.isIdenticalTo(dexItemFactory.intArrayType)) {
- // For int[], using filled-new-array is usually smaller than filled-array-data.
- // filled-new-array supports up to 5 registers before it's filled-new-array/range.
- if (size > rewriteArrayOptions.maxSizeForFilledNewArrayOfInts) {
- return false;
- }
- if (canUseNewArrayFilledData(newArrayFilled)
- && size
- > rewriteArrayOptions
- .maxSizeForFilledNewArrayOfIntsWhenNewArrayFilledDataApplicable) {
- return false;
- }
- return true;
- }
- if (!arrayType.isPrimitiveArrayType()) {
- if (size > rewriteArrayOptions.maxSizeForFilledNewArrayOfReferences) {
- return false;
- }
- if (arrayType.isIdenticalTo(dexItemFactory.stringArrayType)) {
- return rewriteArrayOptions.canUseFilledNewArrayOfStrings();
- }
- if (!rewriteArrayOptions.canUseFilledNewArrayOfNonStringObjects()) {
- return false;
- }
- if (!rewriteArrayOptions.canUseFilledNewArrayOfArrays()
- && arrayType.getNumberOfLeadingSquareBrackets() > 1) {
- return false;
- }
- // Check that all arguments to the array is the array type or that the array is type Object[].
- if (rewriteArrayOptions.canHaveSubTypesInFilledNewArrayBug()
- && arrayType.isNotIdenticalTo(dexItemFactory.objectArrayType)
- && !arrayType.isPrimitiveArrayType()) {
- DexType arrayElementType = arrayType.toArrayElementType(dexItemFactory);
- for (Value elementValue : newArrayFilled.inValues()) {
- if (!canStoreElementInNewArrayFilled(elementValue.getType(), arrayElementType)) {
- return false;
+ public CodeRewriterResult rewriteCode(IRCode code) {
+ assert !mayHaveRedundantBlocks;
+ assert toRemove == NOTHING;
+ CodeRewriterResult result = noChange();
+ BooleanBox pendingRewrites = new BooleanBox(true);
+ while (pendingRewrites.get()) {
+ pendingRewrites.set(false);
+ BasicBlockIterator blockIterator = code.listIterator();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ BasicBlockInstructionListIterator instructionIterator = block.listIterator(code);
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ if (instruction.isNewArrayFilled()) {
+ result =
+ processInstruction(
+ code,
+ blockIterator,
+ instructionIterator,
+ instruction.asNewArrayFilled(),
+ result,
+ pendingRewrites);
+ }
}
}
- }
- return true;
- }
- return false;
- }
-
- private boolean canStoreElementInNewArrayFilled(TypeElement valueType, DexType elementType) {
- if (elementType.isIdenticalTo(dexItemFactory.objectType)) {
- return true;
- }
- if (valueType.isNullType() && !elementType.isPrimitiveType()) {
- return true;
- }
- if (elementType.isArrayType()) {
- if (valueType.isNullType()) {
- return true;
- }
- ArrayTypeElement arrayTypeElement = valueType.asArrayType();
- if (arrayTypeElement == null
- || arrayTypeElement.getNesting() != elementType.getNumberOfLeadingSquareBrackets()) {
- return false;
- }
- valueType = arrayTypeElement.getBaseType();
- elementType = elementType.toBaseType(dexItemFactory);
- }
- assert !valueType.isArrayType();
- assert !elementType.isArrayType();
- if (valueType.isPrimitiveType() && !elementType.isPrimitiveType()) {
- return false;
- }
- if (valueType.isPrimitiveType()) {
- return true;
- }
- DexClass clazz = appView.definitionFor(elementType);
- if (clazz == null) {
- return false;
- }
- return valueType.isClassType(elementType);
- }
-
- private boolean canUseNewArrayFilledData(NewArrayFilled newArrayFilled) {
- // Only convert into NewArrayFilledData when compiling to DEX.
- if (!appView.options().isGeneratingDex()) {
- return false;
- }
- // If there is only one element it is typically smaller to generate the array put instruction
- // instead of fill array data.
- int size = newArrayFilled.size();
- if (size < rewriteArrayOptions.minSizeForFilledArrayData
- || size > rewriteArrayOptions.maxSizeForFilledArrayData) {
- return false;
- }
- if (!newArrayFilled.getArrayType().isPrimitiveArrayType()) {
- return false;
- }
- return Iterables.all(newArrayFilled.inValues(), Value::isConstant);
- }
-
- private NewArrayEmpty rewriteToNewArrayEmpty(
- IRCode code,
- BasicBlockInstructionListIterator instructionIterator,
- NewArrayFilled newArrayFilled) {
- // Load the size before the NewArrayEmpty instruction.
- ConstNumber constNumber =
- ConstNumber.builder()
- .setFreshOutValue(code, TypeElement.getInt())
- .setValue(newArrayFilled.size())
- .setPosition(options.debug ? newArrayFilled.getPosition() : Position.none())
- .build();
- instructionIterator.previous();
- instructionIterator.add(constNumber);
- Instruction next = instructionIterator.next();
- assert next == newArrayFilled;
-
- // Replace the InvokeNewArray instruction by a NewArrayEmpty instruction.
- NewArrayEmpty newArrayEmpty =
- new NewArrayEmpty(
- newArrayFilled.outValue(), constNumber.outValue(), newArrayFilled.getArrayType());
- instructionIterator.replaceCurrentInstruction(newArrayEmpty);
- return newArrayEmpty;
- }
-
- private void rewriteToNewArrayFilledData(
- IRCode code,
- BasicBlockIterator blockIterator,
- BasicBlockInstructionListIterator instructionIterator,
- NewArrayFilled newArrayFilled) {
- NewArrayEmpty newArrayEmpty = rewriteToNewArrayEmpty(code, instructionIterator, newArrayFilled);
-
- // Insert a new NewArrayFilledData instruction after the NewArrayEmpty instruction.
- short[] contents = computeArrayFilledData(newArrayFilled);
- NewArrayFilledData newArrayFilledData =
- new NewArrayFilledData(
- newArrayFilled.outValue(),
- newArrayFilled.getArrayType().elementSizeForPrimitiveArrayType(),
- newArrayFilled.size(),
- contents);
- newArrayFilledData.setPosition(newArrayFilled.getPosition());
- if (newArrayEmpty.getBlock().hasCatchHandlers()) {
- BasicBlock splitBlock =
- instructionIterator.splitCopyCatchHandlers(code, blockIterator, options);
- splitBlock.listIterator(code).add(newArrayFilledData);
- } else {
- instructionIterator.add(newArrayFilledData);
- }
- }
-
- private short[] computeArrayFilledData(NewArrayFilled newArrayFilled) {
- int elementSize = newArrayFilled.getArrayType().elementSizeForPrimitiveArrayType();
- int size = newArrayFilled.size();
- if (elementSize == 1) {
- short[] result = new short[(size + 1) / 2];
- for (int i = 0; i < size; i += 2) {
- ConstNumber constNumber =
- newArrayFilled.getOperand(i).getConstInstruction().asConstNumber();
- short value = (short) (constNumber.getIntValue() & 0xFF);
- if (i + 1 < size) {
- ConstNumber nextConstNumber =
- newArrayFilled.getOperand(i + 1).getConstInstruction().asConstNumber();
- value |= (short) ((nextConstNumber.getIntValue() & 0xFF) << 8);
+ if (!toRemove.isEmpty()) {
+ Set<Instruction> additionalToRemove = SetUtils.newIdentityHashSet();
+ InstructionListIterator it = code.instructionListIterator();
+ while (it.hasNext()) {
+ Instruction next = it.next();
+ if (toRemove.contains(next)) {
+ // Also remove constants used by the removed NewArrayFilled.
+ if (next.isNewArrayFilled()) {
+ next.inValues()
+ .forEach(
+ value -> {
+ if (value.hasSingleUniqueUser()) {
+ additionalToRemove.add(value.getDefinition());
+ }
+ });
+ }
+ it.remove();
+ mayHaveRedundantBlocks = true;
+ }
+ }
+ if (!additionalToRemove.isEmpty()) {
+ InstructionListIterator itAdditional = code.instructionListIterator();
+ while (itAdditional.hasNext()) {
+ Instruction next = itAdditional.next();
+ if (additionalToRemove.contains(next)) {
+ itAdditional.remove();
+ mayHaveRedundantBlocks = true;
+ }
+ }
+ }
}
- result[i / 2] = value;
+ toRemove = NOTHING;
+ if (mayHaveRedundantBlocks) {
+ code.removeRedundantBlocks();
+ }
}
return result;
}
- assert elementSize == 2 || elementSize == 4 || elementSize == 8;
- int shortsPerConstant = elementSize / 2;
- short[] result = new short[size * shortsPerConstant];
- for (int i = 0; i < size; i++) {
- ConstNumber constNumber = newArrayFilled.getOperand(i).getConstInstruction().asConstNumber();
- for (int part = 0; part < shortsPerConstant; part++) {
- result[i * shortsPerConstant + part] =
- (short) ((constNumber.getRawValue() >> (16 * part)) & 0xFFFFL);
+
+ private boolean isNewArrayFilledOfConstants(NewArrayFilled newArrayFilled) {
+ for (Value inValue : newArrayFilled.inValues()) {
+ if (!inValue.isConstNumber() && !inValue.isConstString() && !inValue.isConstClass()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isDefinedByNewArrayFilledOfConstants(Value value) {
+ if (!value.isDefinedByInstructionSatisfying(Instruction::isNewArrayFilled)) {
+ return false;
+ }
+ return isNewArrayFilledOfConstants(value.definition.asNewArrayFilled());
+ }
+
+ public NewArrayFilled copyConstantsNewArrayFilled(IRCode code, NewArrayFilled original) {
+ assert isNewArrayFilledOfConstants(original);
+ Value newValue = code.createValue(original.getOutType(), original.getLocalInfo());
+ List<Value> newArguments = new ArrayList<>(original.inValues().size());
+ for (Value value : original.inValues()) {
+ if (value.isConstNumber()) {
+ newArguments.add(
+ ConstNumber.copyOf(code, value.getDefinition().asConstNumber()).outValue());
+ } else if (value.isConstString()) {
+ newArguments.add(
+ ConstString.copyOf(code, value.getDefinition().asConstString()).outValue());
+ } else if (value.isConstClass()) {
+ newArguments.add(
+ ConstClass.copyOf(code, value.getDefinition().asConstClass()).outValue());
+ } else {
+ assert false;
+ }
+ }
+ return new NewArrayFilled(original.getArrayType(), newValue, newArguments);
+ }
+
+ private CodeRewriterResult processInstruction(
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ BasicBlockInstructionListIterator instructionIterator,
+ NewArrayFilled newArrayFilled,
+ CodeRewriterResult result,
+ BooleanBox pendingRewrites) {
+ if (canUseNewArrayFilled(newArrayFilled)) {
+ return result;
+ }
+ if (newArrayFilled.hasUnusedOutValue()) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ } else if (canUseNewArrayFilledData(newArrayFilled)) {
+ rewriteToNewArrayFilledData(code, blockIterator, instructionIterator, newArrayFilled);
+ } else if (newArrayFilled.outValue().hasSingleUniqueUser()
+ && newArrayFilled.outValue().singleUniqueUser().isNewArrayFilled()
+ && isNewArrayFilledOfConstants(newArrayFilled)) {
+ if (canUseNewArrayFilled(newArrayFilled.outValue().singleUniqueUser().asNewArrayFilled())) {
+ // The NewArrayFilled user is supported, so rewrite here.
+ rewriteToArrayPuts(code, blockIterator, instructionIterator, newArrayFilled);
+ } else {
+ // The NewArrayFilled user is not supported so leave for rewriting after that.
+ //
+ // The effect of this is that when the user of this NewArrayFilled is rewritten to puts,
+ // the NewArrayFilled construction is copied to the use site
+ //
+ // Input:
+ //
+ // v0 <- Const X
+ // v1 <- NewArrayFilled(v0)
+ // v2 <- Const Y
+ // v3 <- NewArrayFilled(v2)
+ // v4 <- NewArrayFilled(v1, v3)
+ //
+ // After rewriting the user (v0 - v3 are unused and removed):
+ //
+ // v4 <- NewArrayEmpty(...)
+ // v5 <- Const X
+ // v6 <- NewArrayFilled(v5)
+ // APut v4, <Const 0>, v6
+ // v7 <- Const Y
+ // v8 <- NewArrayFilled(v7)
+ // APut v4, <Const 1>, v8
+ //
+ // Setting pending rewrites cause the copied NewArrayFilled to be rewritten in their new
+ // location in the fixpoint:
+ //
+ // v4 <- NewArrayEmpty(...)
+ // v9 <- NewArrayEmpty(...)
+ // v10 <- Const X
+ // APut v9, <Const 0>, v10
+ // APut v4, <Const 0>, v9
+ // v11 <- NewArrayEmpty(...)
+ // v12 <- Const Y
+ // APut v11, <Const 0>, v12
+ // APut v4, <Const 1>, v11
+ //
+ // If the NewArrayFilled which gets moved is supported then the second rewriting in the
+ // fixpoint does not happen.
+ pendingRewrites.set(true);
+ }
+ } else {
+ rewriteToArrayPuts(code, blockIterator, instructionIterator, newArrayFilled);
+ }
+ return CodeRewriterResult.HAS_CHANGED;
+ }
+
+ private boolean canUseNewArrayFilled(NewArrayFilled newArrayFilled) {
+ if (!options.isGeneratingDex()) {
+ return false;
+ }
+ int size = newArrayFilled.size();
+ if (size < rewriteArrayOptions.minSizeForFilledNewArray) {
+ return false;
+ }
+ // filled-new-array is implemented only for int[] and Object[].
+ DexType arrayType = newArrayFilled.getArrayType();
+ if (arrayType.isIdenticalTo(dexItemFactory.intArrayType)) {
+ // For int[], using filled-new-array is usually smaller than filled-array-data.
+ // filled-new-array supports up to 5 registers before it's filled-new-array/range.
+ if (size > rewriteArrayOptions.maxSizeForFilledNewArrayOfInts) {
+ return false;
+ }
+ if (canUseNewArrayFilledData(newArrayFilled)
+ && size
+ > rewriteArrayOptions
+ .maxSizeForFilledNewArrayOfIntsWhenNewArrayFilledDataApplicable) {
+ return false;
+ }
+ return true;
+ }
+ if (!arrayType.isPrimitiveArrayType()) {
+ if (size > rewriteArrayOptions.maxSizeForFilledNewArrayOfReferences) {
+ return false;
+ }
+ if (arrayType.isIdenticalTo(dexItemFactory.stringArrayType)) {
+ return rewriteArrayOptions.canUseFilledNewArrayOfStrings();
+ }
+ if (!rewriteArrayOptions.canUseFilledNewArrayOfNonStringObjects()) {
+ return false;
+ }
+ if (!rewriteArrayOptions.canUseFilledNewArrayOfArrays()
+ && arrayType.getNumberOfLeadingSquareBrackets() > 1) {
+ return false;
+ }
+ // Check that all arguments to the array is the array type or that the array is type
+ // Object[].
+ if (rewriteArrayOptions.canHaveSubTypesInFilledNewArrayBug()
+ && arrayType.isNotIdenticalTo(dexItemFactory.objectArrayType)
+ && !arrayType.isPrimitiveArrayType()) {
+ DexType arrayElementType = arrayType.toArrayElementType(dexItemFactory);
+ for (Value elementValue : newArrayFilled.inValues()) {
+ if (!canStoreElementInNewArrayFilled(elementValue.getType(), arrayElementType)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private boolean canStoreElementInNewArrayFilled(TypeElement valueType, DexType elementType) {
+ if (elementType.isIdenticalTo(dexItemFactory.objectType)) {
+ return true;
+ }
+ if (valueType.isNullType() && !elementType.isPrimitiveType()) {
+ return true;
+ }
+ if (elementType.isArrayType()) {
+ if (valueType.isNullType()) {
+ return true;
+ }
+ ArrayTypeElement arrayTypeElement = valueType.asArrayType();
+ if (arrayTypeElement == null
+ || arrayTypeElement.getNesting() != elementType.getNumberOfLeadingSquareBrackets()) {
+ return false;
+ }
+ valueType = arrayTypeElement.getBaseType();
+ elementType = elementType.toBaseType(dexItemFactory);
+ }
+ assert !valueType.isArrayType();
+ assert !elementType.isArrayType();
+ if (valueType.isPrimitiveType() && !elementType.isPrimitiveType()) {
+ return false;
+ }
+ if (valueType.isPrimitiveType()) {
+ return true;
+ }
+ DexClass clazz = appView.definitionFor(elementType);
+ if (clazz == null) {
+ return false;
+ }
+ return valueType.isClassType(elementType);
+ }
+
+ private boolean canUseNewArrayFilledData(NewArrayFilled newArrayFilled) {
+ // Only convert into NewArrayFilledData when compiling to DEX.
+ if (!appView.options().isGeneratingDex()) {
+ return false;
+ }
+ // If there is only one element it is typically smaller to generate the array put instruction
+ // instead of fill array data.
+ int size = newArrayFilled.size();
+ if (size < rewriteArrayOptions.minSizeForFilledArrayData
+ || size > rewriteArrayOptions.maxSizeForFilledArrayData) {
+ return false;
+ }
+ if (!newArrayFilled.getArrayType().isPrimitiveArrayType()) {
+ return false;
+ }
+ return Iterables.all(newArrayFilled.inValues(), Value::isConstant);
+ }
+
+ private NewArrayEmpty rewriteToNewArrayEmpty(
+ IRCode code,
+ BasicBlockInstructionListIterator instructionIterator,
+ NewArrayFilled newArrayFilled) {
+ // Load the size before the NewArrayEmpty instruction.
+ ConstNumber constNumber =
+ ConstNumber.builder()
+ .setFreshOutValue(code, TypeElement.getInt())
+ .setValue(newArrayFilled.size())
+ .setPosition(options.debug ? newArrayFilled.getPosition() : Position.none())
+ .build();
+ instructionIterator.previous();
+ instructionIterator.add(constNumber);
+ Instruction next = instructionIterator.next();
+ assert next == newArrayFilled;
+
+ // Replace the InvokeNewArray instruction by a NewArrayEmpty instruction.
+ NewArrayEmpty newArrayEmpty =
+ new NewArrayEmpty(
+ newArrayFilled.outValue(), constNumber.outValue(), newArrayFilled.getArrayType());
+ instructionIterator.replaceCurrentInstruction(newArrayEmpty);
+ return newArrayEmpty;
+ }
+
+ private void rewriteToNewArrayFilledData(
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ BasicBlockInstructionListIterator instructionIterator,
+ NewArrayFilled newArrayFilled) {
+ NewArrayEmpty newArrayEmpty =
+ rewriteToNewArrayEmpty(code, instructionIterator, newArrayFilled);
+
+ // Insert a new NewArrayFilledData instruction after the NewArrayEmpty instruction.
+ short[] contents = computeArrayFilledData(newArrayFilled);
+ NewArrayFilledData newArrayFilledData =
+ new NewArrayFilledData(
+ newArrayFilled.outValue(),
+ newArrayFilled.getArrayType().elementSizeForPrimitiveArrayType(),
+ newArrayFilled.size(),
+ contents);
+ newArrayFilledData.setPosition(newArrayFilled.getPosition());
+ if (newArrayEmpty.getBlock().hasCatchHandlers()) {
+ BasicBlock splitBlock =
+ instructionIterator.splitCopyCatchHandlers(code, blockIterator, options);
+ splitBlock.listIterator(code).add(newArrayFilledData);
+ } else {
+ instructionIterator.add(newArrayFilledData);
}
}
- return result;
+
+ private short[] computeArrayFilledData(NewArrayFilled newArrayFilled) {
+ int elementSize = newArrayFilled.getArrayType().elementSizeForPrimitiveArrayType();
+ int size = newArrayFilled.size();
+ if (elementSize == 1) {
+ short[] result = new short[(size + 1) / 2];
+ for (int i = 0; i < size; i += 2) {
+ ConstNumber constNumber =
+ newArrayFilled.getOperand(i).getConstInstruction().asConstNumber();
+ short value = (short) (constNumber.getIntValue() & 0xFF);
+ if (i + 1 < size) {
+ ConstNumber nextConstNumber =
+ newArrayFilled.getOperand(i + 1).getConstInstruction().asConstNumber();
+ value |= (short) ((nextConstNumber.getIntValue() & 0xFF) << 8);
+ }
+ result[i / 2] = value;
+ }
+ return result;
+ }
+ assert elementSize == 2 || elementSize == 4 || elementSize == 8;
+ int shortsPerConstant = elementSize / 2;
+ short[] result = new short[size * shortsPerConstant];
+ for (int i = 0; i < size; i++) {
+ ConstNumber constNumber =
+ newArrayFilled.getOperand(i).getConstInstruction().asConstNumber();
+ for (int part = 0; part < shortsPerConstant; part++) {
+ result[i * shortsPerConstant + part] =
+ (short) ((constNumber.getRawValue() >> (16 * part)) & 0xFFFFL);
+ }
+ }
+ return result;
+ }
+
+ private void rewriteToArrayPuts(
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ BasicBlockInstructionListIterator instructionIterator,
+ NewArrayFilled newArrayFilled) {
+ NewArrayEmpty newArrayEmpty =
+ rewriteToNewArrayEmpty(code, instructionIterator, newArrayFilled);
+
+ ConstantMaterializingInstructionCache constantMaterializingInstructionCache =
+ new ConstantMaterializingInstructionCache(rewriteArrayOptions, newArrayFilled);
+
+ int index = 0;
+ for (Value elementValue : newArrayFilled.inValues()) {
+ if (instructionIterator.getBlock().hasCatchHandlers()) {
+ BasicBlock splitBlock =
+ instructionIterator.splitCopyCatchHandlers(code, blockIterator, options);
+ instructionIterator = splitBlock.listIterator(code);
+ Value putValue =
+ getPutValue(
+ code,
+ instructionIterator,
+ newArrayEmpty,
+ elementValue,
+ constantMaterializingInstructionCache);
+ blockIterator.positionAfterPreviousBlock(splitBlock);
+ splitBlock = instructionIterator.splitCopyCatchHandlers(code, blockIterator, options);
+ instructionIterator = splitBlock.listIterator(code);
+ addArrayPut(code, instructionIterator, newArrayEmpty, index, putValue);
+ blockIterator.positionAfterPreviousBlock(splitBlock);
+ mayHaveRedundantBlocks = true;
+ } else {
+ Value putValue =
+ getPutValue(
+ code,
+ instructionIterator,
+ newArrayEmpty,
+ elementValue,
+ constantMaterializingInstructionCache);
+ addArrayPut(code, instructionIterator, newArrayEmpty, index, putValue);
+ }
+ index++;
+ }
+
+ assert constantMaterializingInstructionCache.checkAllOccurrenceProcessed();
+ }
+
+ private Value getPutValue(
+ IRCode code,
+ BasicBlockInstructionListIterator instructionIterator,
+ NewArrayEmpty newArrayEmpty,
+ Value elementValue,
+ ConstantMaterializingInstructionCache constantMaterializingInstructionCache) {
+ // If the value was only used by the NewArrayFilled instruction it now has no normal users.
+ if (elementValue.hasAnyUsers()
+ || !(elementValue.isConstString()
+ || elementValue.isConstNumber()
+ || elementValue.isConstClass()
+ || elementValue.isDefinedByInstructionSatisfying(Instruction::isStaticGet)
+ || (isDefinedByNewArrayFilledOfConstants(elementValue)
+ && !instructionIterator.getBlock().hasCatchHandlers()))) {
+ return elementValue;
+ }
+
+ Value existingValue = constantMaterializingInstructionCache.getValue(elementValue);
+ if (existingValue != null) {
+ addToRemove(elementValue.getDefinition());
+ return existingValue;
+ }
+
+ Instruction copy;
+ if (elementValue.isConstNumber()) {
+ copy = ConstNumber.copyOf(code, elementValue.getDefinition().asConstNumber());
+ } else if (elementValue.isConstString()) {
+ copy = ConstString.copyOf(code, elementValue.getDefinition().asConstString());
+ constantMaterializingInstructionCache.putNewValue(copy.asConstString().outValue());
+ } else if (elementValue.isConstClass()) {
+ copy = ConstClass.copyOf(code, elementValue.getDefinition().asConstClass());
+ constantMaterializingInstructionCache.putNewValue(copy.asConstClass().outValue());
+ } else if (elementValue.isDefinedByInstructionSatisfying(Instruction::isStaticGet)) {
+ copy = StaticGet.copyOf(code, elementValue.getDefinition().asStaticGet());
+ constantMaterializingInstructionCache.putNewValue(copy.asStaticGet().outValue());
+ } else if (isDefinedByNewArrayFilledOfConstants(elementValue)) {
+ copy = copyConstantsNewArrayFilled(code, elementValue.getDefinition().asNewArrayFilled());
+ assert !instructionIterator.getBlock().hasCatchHandlers();
+ for (Value inValue : copy.asNewArrayFilled().inValues()) {
+ instructionIterator.add(inValue.getDefinition());
+ inValue.getDefinition().setBlock(instructionIterator.getBlock());
+ inValue.getDefinition().setPosition(newArrayEmpty.getPosition());
+ }
+ } else {
+ assert false;
+ return elementValue;
+ }
+ copy.setBlock(instructionIterator.getBlock());
+ copy.setPosition(newArrayEmpty.getPosition());
+ instructionIterator.add(copy);
+ addToRemove(elementValue.getDefinition());
+ return copy.outValue();
+ }
+
+ private void addToRemove(Instruction instruction) {
+ if (toRemove == NOTHING) {
+ toRemove = SetUtils.newIdentityHashSet();
+ }
+ toRemove.add(instruction);
+ }
+
+ private void addArrayPut(
+ IRCode code,
+ BasicBlockInstructionListIterator instructionIterator,
+ NewArrayEmpty newArrayEmpty,
+ int index,
+ Value elementValue) {
+ // Load the array index before the ArrayPut instruction.
+ ConstNumber constNumber =
+ ConstNumber.builder()
+ .setFreshOutValue(code, TypeElement.getInt())
+ .setValue(index)
+ .setPosition(options.debug ? newArrayEmpty.getPosition() : Position.none())
+ .build();
+ instructionIterator.add(constNumber);
+
+ // Add the ArrayPut instruction.
+ DexType arrayElementType = newArrayEmpty.getArrayType().toArrayElementType(dexItemFactory);
+ MemberType memberType = MemberType.fromDexType(arrayElementType);
+ ArrayPut arrayPut =
+ ArrayPut.create(
+ memberType, newArrayEmpty.outValue(), constNumber.outValue(), elementValue);
+ arrayPut.setPosition(newArrayEmpty.getPosition());
+ instructionIterator.add(arrayPut);
+ }
}
private static class ConstantMaterializingInstructionCache {
@@ -573,134 +717,4 @@
return true;
}
}
-
- private void rewriteToArrayPuts(
- IRCode code,
- BasicBlockIterator blockIterator,
- BasicBlockInstructionListIterator instructionIterator,
- NewArrayFilled newArrayFilled) {
- NewArrayEmpty newArrayEmpty = rewriteToNewArrayEmpty(code, instructionIterator, newArrayFilled);
-
- ConstantMaterializingInstructionCache constantMaterializingInstructionCache =
- new ConstantMaterializingInstructionCache(rewriteArrayOptions, newArrayFilled);
-
- int index = 0;
- for (Value elementValue : newArrayFilled.inValues()) {
- if (instructionIterator.getBlock().hasCatchHandlers()) {
- BasicBlock splitBlock =
- instructionIterator.splitCopyCatchHandlers(code, blockIterator, options);
- instructionIterator = splitBlock.listIterator(code);
- Value putValue =
- getPutValue(
- code,
- instructionIterator,
- newArrayEmpty,
- elementValue,
- constantMaterializingInstructionCache);
- blockIterator.positionAfterPreviousBlock(splitBlock);
- splitBlock = instructionIterator.splitCopyCatchHandlers(code, blockIterator, options);
- instructionIterator = splitBlock.listIterator(code);
- addArrayPut(code, instructionIterator, newArrayEmpty, index, putValue);
- blockIterator.positionAfterPreviousBlock(splitBlock);
- mayHaveRedundantBlocks = true;
- } else {
- Value putValue =
- getPutValue(
- code,
- instructionIterator,
- newArrayEmpty,
- elementValue,
- constantMaterializingInstructionCache);
- addArrayPut(code, instructionIterator, newArrayEmpty, index, putValue);
- }
- index++;
- }
-
- assert constantMaterializingInstructionCache.checkAllOccurrenceProcessed();
- }
-
- private Value getPutValue(
- IRCode code,
- BasicBlockInstructionListIterator instructionIterator,
- NewArrayEmpty newArrayEmpty,
- Value elementValue,
- ConstantMaterializingInstructionCache constantMaterializingInstructionCache) {
- // If the value was only used by the NewArrayFilled instruction it now has no normal users.
- if (elementValue.hasAnyUsers()
- || !(elementValue.isConstString()
- || elementValue.isConstNumber()
- || elementValue.isConstClass()
- || elementValue.isDefinedByInstructionSatisfying(Instruction::isStaticGet)
- || (isDefinedByNewArrayFilledOfConstants(elementValue)
- && !instructionIterator.getBlock().hasCatchHandlers()))) {
- return elementValue;
- }
-
- Value existingValue = constantMaterializingInstructionCache.getValue(elementValue);
- if (existingValue != null) {
- addToRemove(elementValue.getDefinition());
- return existingValue;
- }
-
- Instruction copy;
- if (elementValue.isConstNumber()) {
- copy = ConstNumber.copyOf(code, elementValue.getDefinition().asConstNumber());
- } else if (elementValue.isConstString()) {
- copy = ConstString.copyOf(code, elementValue.getDefinition().asConstString());
- constantMaterializingInstructionCache.putNewValue(copy.asConstString().outValue());
- } else if (elementValue.isConstClass()) {
- copy = ConstClass.copyOf(code, elementValue.getDefinition().asConstClass());
- constantMaterializingInstructionCache.putNewValue(copy.asConstClass().outValue());
- } else if (elementValue.isDefinedByInstructionSatisfying(Instruction::isStaticGet)) {
- copy = StaticGet.copyOf(code, elementValue.getDefinition().asStaticGet());
- constantMaterializingInstructionCache.putNewValue(copy.asStaticGet().outValue());
- } else if (isDefinedByNewArrayFilledOfConstants(elementValue)) {
- copy = copyConstantsNewArrayFilled(code, elementValue.getDefinition().asNewArrayFilled());
- assert !instructionIterator.getBlock().hasCatchHandlers();
- for (Value inValue : copy.asNewArrayFilled().inValues()) {
- instructionIterator.add(inValue.getDefinition());
- inValue.getDefinition().setBlock(instructionIterator.getBlock());
- inValue.getDefinition().setPosition(newArrayEmpty.getPosition());
- }
- } else {
- assert false;
- return elementValue;
- }
- copy.setBlock(instructionIterator.getBlock());
- copy.setPosition(newArrayEmpty.getPosition());
- instructionIterator.add(copy);
- addToRemove(elementValue.getDefinition());
- return copy.outValue();
- }
-
- private void addToRemove(Instruction instruction) {
- if (toRemove == NOTHING) {
- toRemove = SetUtils.newIdentityHashSet();
- }
- toRemove.add(instruction);
- }
-
- private void addArrayPut(
- IRCode code,
- BasicBlockInstructionListIterator instructionIterator,
- NewArrayEmpty newArrayEmpty,
- int index,
- Value elementValue) {
- // Load the array index before the ArrayPut instruction.
- ConstNumber constNumber =
- ConstNumber.builder()
- .setFreshOutValue(code, TypeElement.getInt())
- .setValue(index)
- .setPosition(options.debug ? newArrayEmpty.getPosition() : Position.none())
- .build();
- instructionIterator.add(constNumber);
-
- // Add the ArrayPut instruction.
- DexType arrayElementType = newArrayEmpty.getArrayType().toArrayElementType(dexItemFactory);
- MemberType memberType = MemberType.fromDexType(arrayElementType);
- ArrayPut arrayPut =
- ArrayPut.create(memberType, newArrayEmpty.outValue(), constNumber.outValue(), elementValue);
- arrayPut.setPosition(newArrayEmpty.getPosition());
- instructionIterator.add(arrayPut);
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index ff9fb1a..cb8592e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -115,7 +115,7 @@
ProgramMethod context,
RecordInstructionDesugaringEventConsumer eventConsumer) {
RecordInvokeDynamic recordInvokeDynamic =
- parseInvokeDynamicOnRecord(invokeDynamic, appView, context);
+ parseInvokeDynamicOnRecord(invokeDynamic.getCallSite(), appView);
if (recordInvokeDynamic.getMethodName() == factory.toStringMethodName
|| recordInvokeDynamic.getMethodName() == factory.hashCodeMethodName) {
ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions, context, eventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
index 7cd44a8..b697254 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -4,91 +4,35 @@
package com.android.tools.r8.ir.desugar.records;
-import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeCustomOnRecord;
-import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
-import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.parseInvokeCustomOnRecord;
-import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.parseInvokeDynamicOnRecord;
+import static com.android.tools.r8.graph.lens.GraphLens.getIdentityLens;
-import com.android.tools.r8.cf.code.CfInvokeDynamic;
-import com.android.tools.r8.dex.code.DexInstruction;
-import com.android.tools.r8.dex.code.DexInvokeCustom;
-import com.android.tools.r8.dex.code.DexInvokeCustomRange;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
-import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.graph.DexValue.DexValueType;
-import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.RecordInvokeDynamic;
-import com.android.tools.r8.naming.NamingLens;
import java.util.ArrayList;
/** Used to rewrite invokedynamic/invoke-custom when shrinking and minifying records. */
public class RecordRewriter {
- private final AppView<?> appView;
-
public static RecordRewriter create(AppView<?> appView) {
if (appView.enableWholeProgramOptimizations()) {
- return new RecordRewriter(appView);
+ return new RecordRewriter();
}
return null;
}
- private RecordRewriter(AppView<?> appView) {
- this.appView = appView;
- }
+ private RecordRewriter() {}
- // Called after final tree shaking, prune and minify field names and field values.
- public CfInvokeDynamic rewriteRecordInvokeDynamic(
- CfInvokeDynamic invokeDynamic, ProgramMethod context, NamingLens namingLens) {
- if (!isInvokeDynamicOnRecord(invokeDynamic, appView, context)) {
- return invokeDynamic;
- }
- RecordInvokeDynamic recordInvokeDynamic =
- parseInvokeDynamicOnRecord(invokeDynamic, appView, context);
- DexString newFieldNames =
- recordInvokeDynamic
- .computeRecordFieldNamesComputationInfo()
- .internalComputeNameFor(
- recordInvokeDynamic.getRecordType(), appView, appView.graphLens(), namingLens);
- DexField[] newFields = computePresentFields(appView.graphLens(), recordInvokeDynamic);
- return writeRecordInvokeDynamic(
- recordInvokeDynamic.withFieldNamesAndFields(newFieldNames, newFields));
- }
-
- public DexInstruction rewriteRecordInvokeCustom(
- DexInstruction invokeCustom, ProgramMethod context, NamingLens namingLens) {
- if (!isInvokeCustomOnRecord(invokeCustom, appView, context)) {
- return invokeCustom;
- }
- RecordInvokeDynamic recordInvokeDynamic =
- parseInvokeCustomOnRecord(invokeCustom, appView, context);
- DexString newFieldNames =
- recordInvokeDynamic
- .computeRecordFieldNamesComputationInfo()
- .internalComputeNameFor(
- recordInvokeDynamic.getRecordType(), appView, appView.graphLens(), namingLens);
- DexField[] newFields = computePresentFields(appView.graphLens(), recordInvokeDynamic);
- return writeRecordInvokeCustom(
- invokeCustom, recordInvokeDynamic.withFieldNamesAndFields(newFieldNames, newFields));
- }
-
- private DexField[] computePresentFields(
+ public DexField[] computePresentFields(
GraphLens graphLens, RecordInvokeDynamic recordInvokeDynamic) {
ArrayList<DexField> finalFields = new ArrayList<>();
for (DexField field : recordInvokeDynamic.getFields()) {
DexEncodedField dexEncodedField =
recordInvokeDynamic
.getRecordClass()
- .lookupInstanceField(graphLens.getRenamedFieldSignature(field));
+ .lookupInstanceField(graphLens.getRenamedFieldSignature(field, getIdentityLens()));
if (dexEncodedField != null) {
finalFields.add(field);
}
@@ -99,77 +43,4 @@
}
return newFields;
}
-
- @SuppressWarnings("ReferenceEquality")
- private CfInvokeDynamic writeRecordInvokeDynamic(RecordInvokeDynamic recordInvokeDynamic) {
- DexItemFactory factory = appView.dexItemFactory();
- DexMethodHandle bootstrapMethod =
- factory.createMethodHandle(
- MethodHandleType.INVOKE_STATIC, factory.objectMethodsMembers.bootstrap, false, null);
- ArrayList<DexValue> bootstrapArgs = new ArrayList<>();
- bootstrapArgs.add(new DexValueType(recordInvokeDynamic.getRecordType()));
- bootstrapArgs.add(new DexValueString(recordInvokeDynamic.getFieldNames()));
- for (DexField field : recordInvokeDynamic.getFields()) {
- assert recordInvokeDynamic.getRecordCodeType() == field.getHolderType();
- bootstrapArgs.add(
- new DexValueMethodHandle(
- new DexMethodHandle(MethodHandleType.INSTANCE_GET, field, false, null)));
- }
- return new CfInvokeDynamic(
- factory.createCallSite(
- recordInvokeDynamic.getMethodName(),
- recordInvokeDynamic.getMethodProto(),
- bootstrapMethod,
- bootstrapArgs));
- }
-
- private DexInstruction writeRecordInvokeCustom(
- DexInstruction invokeCustom, RecordInvokeDynamic recordInvokeDynamic) {
- DexItemFactory factory = appView.dexItemFactory();
- DexMethodHandle bootstrapMethod =
- factory.createMethodHandle(
- MethodHandleType.INVOKE_STATIC, factory.objectMethodsMembers.bootstrap, false, null);
- ArrayList<DexValue> bootstrapArgs = new ArrayList<>();
- bootstrapArgs.add(new DexValueType(recordInvokeDynamic.getRecordCodeType()));
- bootstrapArgs.add(new DexValueString(recordInvokeDynamic.getFieldNames()));
- for (DexField field : recordInvokeDynamic.getFields()) {
- // Rewrite using the code type of the field.
- DexField codeField =
- factory.createField(recordInvokeDynamic.getRecordCodeType(), field.type, field.name);
- bootstrapArgs.add(
- new DexValueMethodHandle(
- factory.createMethodHandle(MethodHandleType.INSTANCE_GET, codeField, false, null)));
- }
- if (invokeCustom instanceof DexInvokeCustom) {
- DexInvokeCustom current = (DexInvokeCustom) invokeCustom;
- DexInvokeCustom rewritten =
- new DexInvokeCustom(
- current.A,
- factory.createCallSite(
- recordInvokeDynamic.getMethodName(),
- recordInvokeDynamic.getMethodProto(),
- bootstrapMethod,
- bootstrapArgs),
- current.C,
- current.D,
- current.E,
- current.F,
- current.G);
- rewritten.setOffset(current.getOffset());
- return rewritten;
- } else {
- DexInvokeCustomRange current = (DexInvokeCustomRange) invokeCustom;
- DexInvokeCustomRange rewritten =
- new DexInvokeCustomRange(
- current.CCCC,
- current.AA,
- factory.createCallSite(
- recordInvokeDynamic.getMethodName(),
- recordInvokeDynamic.getMethodProto(),
- bootstrapMethod,
- bootstrapArgs));
- rewritten.setOffset(current.getOffset());
- return rewritten;
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriterHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriterHelper.java
index f465a98..82473ea 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriterHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriterHelper.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.desugar.records;
+import static com.android.tools.r8.graph.lens.GraphLens.getIdentityLens;
+
import com.android.tools.r8.cf.code.CfInvokeDynamic;
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.dex.code.DexInvokeCustom;
@@ -22,6 +24,7 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.naming.dexitembasedstring.RecordFieldNamesComputationInfo;
public class RecordRewriterHelper {
@@ -37,6 +40,11 @@
return isInvokeDynamicOnRecord(invokeCustom.getCallSite(), appView, context);
}
+ public static boolean isInvokeCustomOnRecord(
+ InvokeCustom invokeCustom, AppView<?> appView, ProgramMethod context) {
+ return isInvokeDynamicOnRecord(invokeCustom.getCallSite(), appView, context);
+ }
+
@SuppressWarnings("ReferenceEquality")
public static boolean isInvokeDynamicOnRecord(
DexCallSite callSite, AppView<?> appView, ProgramMethod context) {
@@ -117,17 +125,17 @@
public static RecordInvokeDynamic parseInvokeDynamicOnRecord(
CfInvokeDynamic invokeDynamic, AppView<?> appView, ProgramMethod context) {
assert isInvokeDynamicOnRecord(invokeDynamic, appView, context);
- return parseInvokeDynamicOnRecord(invokeDynamic.getCallSite(), appView, context);
+ return parseInvokeDynamicOnRecord(invokeDynamic.getCallSite(), appView);
}
public static RecordInvokeDynamic parseInvokeCustomOnRecord(
- DexInstruction invokeCustom, AppView<?> appView, ProgramMethod context) {
+ InvokeCustom invokeCustom, AppView<?> appView, ProgramMethod context) {
assert isInvokeCustomOnRecord(invokeCustom, appView, context);
- return parseInvokeDynamicOnRecord(invokeCustom.getCallSite(), appView, context);
+ return parseInvokeDynamicOnRecord(invokeCustom.getCallSite(), appView);
}
public static RecordInvokeDynamic parseInvokeDynamicOnRecord(
- DexCallSite callSite, AppView<?> appView, ProgramMethod context) {
+ DexCallSite callSite, AppView<?> appView) {
DexValueType recordValueType = callSite.bootstrapArgs.get(0).asDexValueType();
DexValueString valueString = callSite.bootstrapArgs.get(1).asDexValueString();
DexString fieldNames = valueString.getValue();
@@ -137,13 +145,16 @@
fields[i - 2] = handle.value.member.asDexField();
}
DexType recordCodeType = recordValueType.getValue();
+ // TODO(b/323336433): Is this the right choice of code lens?
DexProgramClass recordClass =
- appView.definitionFor(appView.graphLens().lookupType(recordCodeType)).asProgramClass();
+ appView
+ .definitionFor(appView.graphLens().lookupType(recordCodeType, getIdentityLens()))
+ .asProgramClass();
return new RecordInvokeDynamic(
callSite.methodName, callSite.methodProto, fieldNames, fields, recordClass, recordCodeType);
}
- static class RecordInvokeDynamic {
+ public static class RecordInvokeDynamic {
private final DexString methodName;
private final DexProto methodProto;
@@ -167,20 +178,20 @@
this.recordCodeType = recordCodeType;
}
- RecordInvokeDynamic withFieldNamesAndFields(DexString fieldNames, DexField[] fields) {
+ public RecordInvokeDynamic withFieldNamesAndFields(DexString fieldNames, DexField[] fields) {
return new RecordInvokeDynamic(
methodName, methodProto, fieldNames, fields, recordClass, recordCodeType);
}
- DexField[] getFields() {
+ public DexField[] getFields() {
return fields;
}
- DexType getRecordType() {
+ public DexType getRecordType() {
return recordClass.getType();
}
- DexType getRecordCodeType() {
+ public DexType getRecordCodeType() {
return recordCodeType;
}
@@ -188,19 +199,19 @@
return recordClass;
}
- DexString getFieldNames() {
+ public DexString getFieldNames() {
return fieldNames;
}
- DexString getMethodName() {
+ public DexString getMethodName() {
return methodName;
}
- DexProto getMethodProto() {
+ public DexProto getMethodProto() {
return methodProto;
}
- RecordFieldNamesComputationInfo computeRecordFieldNamesComputationInfo() {
+ public RecordFieldNamesComputationInfo computeRecordFieldNamesComputationInfo() {
return RecordFieldNamesComputationInfo.forFieldNamesAndFields(getFieldNames(), getFields());
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index 3a8b545..c425c1b 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -3,151 +3,94 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.graph.lens.GraphLens.getIdentityLens;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
-import com.android.tools.r8.cf.code.CfConstString;
-import com.android.tools.r8.cf.code.CfDexItemBasedConstString;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.dex.code.DexConstString;
-import com.android.tools.r8.dex.code.DexInstruction;
-import com.android.tools.r8.dex.code.DexItemBasedConstString;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexItemBasedValueString;
import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.ProguardClassFilter;
-import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
-import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-/**
- * Replaces all instances of DexItemBasedConstString by ConstString, and all instances of
- * DexItemBasedValueString by DexValueString.
- */
+/** Replaces all instances of DexItemBasedValueString by DexValueString. */
public class IdentifierMinifier {
- private final AppView<?> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final ProguardClassFilter adaptClassStrings;
private final NamingLens lens;
- public IdentifierMinifier(AppView<?> appView, NamingLens lens) {
+ public IdentifierMinifier(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
this.adaptClassStrings = appView.options().getProguardConfiguration().getAdaptClassStrings();
- this.lens = lens;
+ this.lens = appView.getNamingLens();
}
public void run(ExecutorService executorService) throws ExecutionException {
- if (!adaptClassStrings.isEmpty()) {
- adaptClassStrings(executorService);
- }
- replaceDexItemBasedConstString(executorService);
+ adaptClassStringsInStaticFields(executorService);
+ replaceDexItemBasedConstStringInStaticFields(executorService);
}
- private void adaptClassStrings(ExecutorService executorService) throws ExecutionException {
+ private void adaptClassStringsInStaticFields(ExecutorService executorService)
+ throws ExecutionException {
+ if (adaptClassStrings.isEmpty()) {
+ return;
+ }
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz -> {
- if (adaptClassStrings.matches(clazz.type)) {
+ if (adaptClassStrings.matches(clazz.getType())) {
for (DexEncodedField field : clazz.staticFields()) {
adaptClassStringsInStaticField(field);
}
- clazz.forEachMethod(this::adaptClassStringsInMethod);
}
},
appView.options().getThreadingModule(),
executorService);
}
- private void adaptClassStringsInStaticField(DexEncodedField encodedField) {
- assert encodedField.accessFlags.isStatic();
- DexValue staticValue = encodedField.getStaticValue();
- if (staticValue.isDexValueString()) {
- DexString original = staticValue.asDexValueString().getValue();
- encodedField.setStaticValue(new DexValueString(getRenamedStringLiteral(original)));
+ private void adaptClassStringsInStaticField(DexEncodedField field) {
+ assert field.isStatic();
+ DexValueString staticValue = field.getStaticValue().asDexValueString();
+ if (staticValue != null) {
+ field.setStaticValue(
+ new DexValueString(getRenamedStringLiteral(appView, staticValue.getValue())));
}
}
- private void adaptClassStringsInMethod(DexEncodedMethod encodedMethod) {
- // Abstract methods do not have code_item.
- if (encodedMethod.shouldNotHaveCode()) {
- return;
- }
- Code code = encodedMethod.getCode();
- if (code == null) {
- return;
- }
- if (code.isDexCode()) {
- DexInstruction[] instructions = code.asDexCode().instructions;
- DexInstruction[] newInstructions =
- ArrayUtils.map(
- instructions,
- (DexInstruction instruction) -> {
- if (instruction.isConstString()) {
- DexConstString cnst = instruction.asConstString();
- DexString renamedStringLiteral = getRenamedStringLiteral(cnst.getString());
- if (!renamedStringLiteral.equals(cnst.getString())) {
- DexConstString dexConstString =
- new DexConstString(instruction.asConstString().AA, renamedStringLiteral);
- dexConstString.setOffset(instruction.getOffset());
- return dexConstString;
- }
- }
- return instruction;
- },
- DexInstruction.EMPTY_ARRAY);
- if (instructions != newInstructions) {
- encodedMethod.setCode(
- code.asDexCode().withNewInstructions(newInstructions),
- encodedMethod.getParameterInfo());
- }
- } else if (code.isCfCode()) {
- for (CfInstruction instruction : code.asCfCode().getInstructions()) {
- if (instruction.isConstString()) {
- CfConstString cnst = instruction.asConstString();
- cnst.setString(getRenamedStringLiteral(cnst.getString()));
- }
- }
- } else {
- assert code.isCfWritableCode() || code.isDexWritableCode();
- }
- }
-
- @SuppressWarnings("ReferenceEquality")
- private DexString getRenamedStringLiteral(DexString originalLiteral) {
+ public static DexString getRenamedStringLiteral(
+ AppView<? extends AppInfoWithClassHierarchy> appView, DexString originalLiteral) {
String descriptor =
DescriptorUtils.javaTypeToDescriptorIfValidJavaType(originalLiteral.toString());
if (descriptor == null) {
return originalLiteral;
}
DexType type = appView.dexItemFactory().createType(descriptor);
- DexType originalType = appView.graphLens().getOriginalType(type);
- if (originalType != type) {
+ DexType originalType = appView.graphLens().getOriginalType(type, getIdentityLens());
+ if (originalType.isNotIdenticalTo(type)) {
// The type has changed to something clashing with the string.
return originalLiteral;
}
- DexType rewrittenType = appView.graphLens().lookupType(type);
+ DexType rewrittenType = appView.graphLens().lookupType(type, getIdentityLens());
DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(rewrittenType);
if (clazz == null || clazz.isNotProgramClass()) {
return originalLiteral;
}
- DexString rewrittenString = lens.lookupClassDescriptor(rewrittenType);
+ DexString rewrittenString = appView.getNamingLens().lookupClassDescriptor(rewrittenType);
return rewrittenString == null
? originalLiteral
: appView.dexItemFactory().createString(descriptorToJavaType(rewrittenString.toString()));
}
- void replaceDexItemBasedConstString(ExecutorService executorService) throws ExecutionException {
+ private void replaceDexItemBasedConstStringInStaticFields(ExecutorService executorService)
+ throws ExecutionException {
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz -> {
@@ -155,67 +98,20 @@
for (DexEncodedField field : clazz.staticFields()) {
replaceDexItemBasedConstStringInStaticField(field);
}
- clazz.forEachProgramMethodMatching(
- DexEncodedMethod::hasCode, this::replaceDexItemBasedConstStringInMethod);
},
appView.options().getThreadingModule(),
executorService);
}
- private void replaceDexItemBasedConstStringInStaticField(DexEncodedField encodedField) {
- assert encodedField.accessFlags.isStatic();
- DexValue staticValue = encodedField.getStaticValue();
- if (staticValue instanceof DexItemBasedValueString) {
- DexItemBasedValueString cnst = (DexItemBasedValueString) staticValue;
+ private void replaceDexItemBasedConstStringInStaticField(DexEncodedField field) {
+ assert field.isStatic();
+ DexItemBasedValueString staticValue = field.getStaticValue().asDexItemBasedValueString();
+ if (staticValue != null) {
DexString replacement =
- cnst.getNameComputationInfo()
- .computeNameFor(cnst.getValue(), appView, appView.graphLens(), lens);
- encodedField.setStaticValue(new DexValueString(replacement));
- }
- }
-
- private void replaceDexItemBasedConstStringInMethod(ProgramMethod programMethod) {
- Code code = programMethod.getDefinition().getCode();
- assert code != null;
- if (code.isDexCode()) {
- DexInstruction[] instructions = code.asDexCode().instructions;
- DexInstruction[] newInstructions =
- ArrayUtils.map(
- instructions,
- (DexInstruction instruction) -> {
- if (instruction.isDexItemBasedConstString()) {
- DexItemBasedConstString cnst = instruction.asDexItemBasedConstString();
- DexString replacement =
- cnst.getNameComputationInfo()
- .computeNameFor(cnst.getItem(), appView, appView.graphLens(), lens);
- DexConstString constString = new DexConstString(cnst.AA, replacement);
- constString.setOffset(instruction.getOffset());
- return constString;
- }
- return instruction;
- },
- DexInstruction.EMPTY_ARRAY);
- if (newInstructions != instructions) {
- programMethod.setCode(code.asDexCode().withNewInstructions(newInstructions), appView);
- }
- } else if (code.isCfCode()) {
- List<CfInstruction> instructions = code.asCfCode().getInstructions();
- List<CfInstruction> newInstructions =
- ListUtils.mapOrElse(
- instructions,
- (int i, CfInstruction instruction) -> {
- if (instruction.isDexItemBasedConstString()) {
- CfDexItemBasedConstString cnst = instruction.asDexItemBasedConstString();
- return new CfConstString(
- cnst.getNameComputationInfo()
- .computeNameFor(cnst.getItem(), appView, appView.graphLens(), lens));
- }
- return instruction;
- },
- instructions);
- code.asCfCode().setInstructions(newInstructions);
- } else {
- assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
+ staticValue
+ .getNameComputationInfo()
+ .computeNameFor(staticValue.getValue(), appView, appView.graphLens(), lens);
+ field.setStaticValue(new DexValueString(replacement));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 6c08cef..937d6a3 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -85,14 +85,6 @@
NamingLens lens = new MinifiedRenaming(appView, classRenaming, methodRenaming, fieldRenaming);
assert lens.verifyNoCollisions(appView.appInfo().classes(), appView.dexItemFactory());
- timing.begin("MinifyIdentifiers");
- new IdentifierMinifier(appView, lens).run(executorService);
- timing.end();
-
- timing.begin("RecordInvokeDynamicRewrite");
- new RecordInvokeDynamicInvokeCustomRewriter(appView, lens).run(executorService);
- timing.end();
-
appView.testing().namingLensConsumer.accept(appView.dexItemFactory(), lens);
appView.notifyOptimizationFinishedForTesting();
appView.setNamingLens(lens);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index ddb57d6..d5f4683 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -156,15 +156,6 @@
NamingLens lens =
new ProguardMapMinifiedRenaming(
appView, classRenaming, methodRenaming, fieldRenaming, notMappedReferences);
-
- timing.begin("MinifyIdentifiers");
- new IdentifierMinifier(appView, lens).run(executorService);
- timing.end();
-
- timing.begin("RecordInvokeDynamicRewrite");
- new RecordInvokeDynamicInvokeCustomRewriter(appView, lens).run(executorService);
- timing.begin("MinifyIdentifiers");
-
appView.notifyOptimizationFinishedForTesting();
return lens;
}
diff --git a/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java
index d3d1212..6ff9638 100644
--- a/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java
@@ -3,85 +3,124 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.dex.code.DexInstruction;
-import com.android.tools.r8.dex.code.DexInvokeCustom;
-import com.android.tools.r8.dex.code.DexInvokeCustomRange;
+import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeCustomOnRecord;
+import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.parseInvokeCustomOnRecord;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
import com.android.tools.r8.ir.desugar.records.RecordRewriter;
-import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.ThreadUtils;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
+import com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.RecordInvokeDynamic;
+import java.util.ArrayList;
/** Rewrites the record invokedynamic/invoke-custom in hashCode, equals and toString. */
-public class RecordInvokeDynamicInvokeCustomRewriter {
+public class RecordInvokeDynamicInvokeCustomRewriter
+ extends CodeRewriterPass<AppInfoWithClassHierarchy> {
- private final AppView<?> appView;
private final RecordRewriter recordRewriter;
- private final NamingLens lens;
- public RecordInvokeDynamicInvokeCustomRewriter(AppView<?> appView, NamingLens lens) {
- this.appView = appView;
+ public RecordInvokeDynamicInvokeCustomRewriter(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ super(appView);
this.recordRewriter = RecordRewriter.create(appView);
- this.lens = lens;
}
- public void run(ExecutorService executorService) throws ExecutionException {
- ThreadUtils.processItems(
- appView.appInfo().classes(),
- clazz -> {
- clazz.forEachProgramMethodMatching(
- DexEncodedMethod::hasCode, this::rewriteRecordInvokeDynamicInMethod);
- },
- appView.options().getThreadingModule(),
- executorService);
+ @Override
+ protected String getRewriterId() {
+ return "RecordInvokeDynamicInvokeCustomRewriter";
}
- private void rewriteRecordInvokeDynamicInMethod(ProgramMethod programMethod) {
- if (recordRewriter == null) {
- return;
- }
- if (!programMethod.getHolder().isRecord()) {
- return;
- }
- Code code = programMethod.getDefinition().getCode();
- assert code != null;
- if (code.isDexCode()) {
- DexInstruction[] instructions = code.asDexCode().instructions;
- DexInstruction[] newInstructions =
- ArrayUtils.map(
- instructions,
- (DexInstruction instruction) -> {
- if (instruction instanceof DexInvokeCustom
- || instruction instanceof DexInvokeCustomRange) {
- return recordRewriter.rewriteRecordInvokeCustom(instruction, programMethod, lens);
- }
- return instruction;
- },
- DexInstruction.EMPTY_ARRAY);
- if (newInstructions != instructions) {
- programMethod.setCode(code.asDexCode().withNewInstructions(newInstructions), appView);
+ @Override
+ protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
+ return recordRewriter != null
+ && code.context().getHolder().isRecord()
+ && code.metadata().mayHaveInvokeCustom();
+ }
+
+ @Override
+ protected CodeRewriterResult rewriteCode(IRCode code) {
+ boolean hasChanged = false;
+ InstructionListIterator iterator = code.instructionListIterator();
+ while (iterator.hasNext()) {
+ InvokeCustom instruction = iterator.next().asInvokeCustom();
+ if (instruction != null) {
+ InvokeCustom replacement = rewriteRecordInvokeCustom(code, instruction);
+ if (replacement != instruction) {
+ iterator.replaceCurrentInstruction(replacement);
+ hasChanged = true;
+ }
}
- } else if (code.isCfCode()) {
- List<CfInstruction> instructions = code.asCfCode().getInstructions();
- List<CfInstruction> newInstructions =
- ListUtils.mapOrElse(
- instructions,
- (int index, CfInstruction instruction) -> {
- if (instruction.isInvokeDynamic()) {
- return recordRewriter.rewriteRecordInvokeDynamic(
- instruction.asInvokeDynamic(), programMethod, lens);
- }
- return instruction;
- },
- instructions);
- code.asCfCode().setInstructions(newInstructions);
}
+ return CodeRewriterResult.hasChanged(hasChanged);
+ }
+
+ // Called after final tree shaking, prune and minify field names and field values.
+ public InvokeCustom rewriteRecordInvokeCustom(IRCode code, InvokeCustom invokeCustom) {
+ ProgramMethod context = code.context();
+ if (!isInvokeCustomOnRecord(invokeCustom, appView, context)) {
+ return invokeCustom;
+ }
+ RecordInvokeDynamic recordInvokeDynamic =
+ parseInvokeCustomOnRecord(invokeCustom, appView, context);
+ DexString newFieldNames =
+ recordInvokeDynamic
+ .computeRecordFieldNamesComputationInfo()
+ .internalComputeNameFor(
+ recordInvokeDynamic.getRecordType(),
+ appView,
+ appView.graphLens(),
+ appView.getNamingLens());
+ DexField[] newFields =
+ recordRewriter.computePresentFields(appView.graphLens(), recordInvokeDynamic);
+ return writeRecordInvokeCustom(
+ invokeCustom, recordInvokeDynamic.withFieldNamesAndFields(newFieldNames, newFields));
+ }
+
+ private InvokeCustom writeRecordInvokeCustom(
+ InvokeCustom invokeCustom, RecordInvokeDynamic recordInvokeDynamic) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexMethodHandle bootstrapMethod =
+ factory.createMethodHandle(
+ MethodHandleType.INVOKE_STATIC, factory.objectMethodsMembers.bootstrap, false, null);
+ ArrayList<DexValue> bootstrapArgs = new ArrayList<>();
+ bootstrapArgs.add(new DexValueType(recordInvokeDynamic.getRecordCodeType()));
+ bootstrapArgs.add(new DexValueString(recordInvokeDynamic.getFieldNames()));
+ for (DexField field : recordInvokeDynamic.getFields()) {
+ // Rewrite using the code type of the field.
+ DexField codeField =
+ factory.createField(recordInvokeDynamic.getRecordCodeType(), field.type, field.name);
+ bootstrapArgs.add(
+ new DexValueMethodHandle(
+ factory.createMethodHandle(MethodHandleType.INSTANCE_GET, codeField, false, null)));
+ }
+ DexCallSite callSite =
+ factory.createCallSite(
+ recordInvokeDynamic.getMethodName(),
+ recordInvokeDynamic.getMethodProto(),
+ bootstrapMethod,
+ bootstrapArgs);
+ DexType returnType = callSite.getMethodProto().getReturnType();
+ assert returnType.isBooleanType()
+ || returnType.isIntType()
+ || returnType.isIdenticalTo(factory.stringType);
+ assert invokeCustom.outValue().getType().equals(returnType.toTypeElement(appView));
+ return new InvokeCustom(callSite, invokeCustom.outValue(), invokeCustom.arguments());
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/NameComputationInfo.java b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/NameComputationInfo.java
index 02e29aa..a6fdb57 100644
--- a/src/main/java/com/android/tools/r8/naming/dexitembasedstring/NameComputationInfo.java
+++ b/src/main/java/com/android/tools/r8/naming/dexitembasedstring/NameComputationInfo.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.naming.dexitembasedstring;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
@@ -22,6 +24,11 @@
}
public final DexString computeNameFor(
+ DexReference reference, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return computeNameFor(reference, appView, appView.graphLens(), appView.getNamingLens());
+ }
+
+ public final DexString computeNameFor(
DexReference reference,
DexDefinitionSupplier definitions,
GraphLens graphLens,
diff --git a/src/test/java/com/android/tools/r8/androidresources/ResourceIDCannonicalizationTest.java b/src/test/java/com/android/tools/r8/androidresources/ResourceIDCannonicalizationTest.java
index 60ce942..7f81005 100644
--- a/src/test/java/com/android/tools/r8/androidresources/ResourceIDCannonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/androidresources/ResourceIDCannonicalizationTest.java
@@ -48,8 +48,8 @@
.inspect(
codeInspector -> {
// We should canonicalize the resource numbers separately from the normal const
- // numbers.
- // This implies that the output have two distinct const numbers with the same value.
+ // numbers. When mapping from LIR to DEX in the backend we then reapply
+ // canonicalization after changing each resource number to a const number.
long constNumbers =
codeInspector
.clazz(FooBar.class)
@@ -57,7 +57,7 @@
.streamInstructions()
.filter(i -> i.isConstNumber(EXPECTED_RESOURCE_NUMBER))
.count();
- assertEquals(2, constNumbers);
+ assertEquals(1, constNumbers);
})
.inspectShrunkenResources(
resourceTableInspector -> {
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index b31bff4..05bea2e 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -182,10 +182,11 @@
assertThat(main, isPresent());
verifyPresenceOfConstString(main);
+ // Output differs due to canonicalization which happens only on DEX.
ClassSubject a = inspector.clazz("getmembers.A");
Set<InstructionSubject> constStringInstructions =
getRenamedMemberIdentifierConstStrings(a, main);
- assertEquals(2, constStringInstructions.size());
+ assertEquals(parameters.isCfRuntime() ? 2 : 1, constStringInstructions.size());
ClassSubject b = inspector.clazz("getmembers.B");
MethodSubject inliner = b.uniqueMethodWithOriginalName("inliner");
@@ -243,10 +244,11 @@
assertThat(main, isPresent());
verifyPresenceOfConstString(main);
+ // Output differs due to canonicalization which happens only on DEX.
ClassSubject b = inspector.clazz("identifiernamestring.B");
Set<InstructionSubject> constStringInstructions =
getRenamedMemberIdentifierConstStrings(b, main);
- assertEquals(2, constStringInstructions.size());
+ assertEquals(parameters.isCfRuntime() ? 2 : 1, constStringInstructions.size());
}
private static void verifyPresenceOfConstString(MethodSubject method) {
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
index 4d3a3fa..28b621a 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringMarkerTest.java
@@ -121,13 +121,10 @@
DexSgetObject.class,
DexConstString.class,
DexInvokeVirtual.class,
- DexConstString.class,
DexIputObject.class,
DexReturnVoid.class));
DexConstString constString = (DexConstString) code.instructions[2];
assertEquals(BOO, constString.getString().toString());
- constString = (DexConstString) code.instructions[4];
- assertEquals(BOO, constString.getString().toString());
}
@Test
@@ -233,13 +230,10 @@
DexSgetObject.class,
DexConstString.class,
DexInvokeVirtual.class,
- DexConstString.class,
DexSputObject.class,
DexReturnVoid.class));
DexConstString constString = (DexConstString) code.instructions[1];
assertEquals(BOO, constString.getString().toString());
- constString = (DexConstString) code.instructions[3];
- assertEquals(BOO, constString.getString().toString());
}
@Test
@@ -462,13 +456,10 @@
DexSgetObject.class,
DexConstString.class,
DexInvokeVirtual.class,
- DexConstString.class,
DexInvokeStatic.class,
DexReturnVoid.class));
DexConstString constString = (DexConstString) code.instructions[2];
assertEquals(BOO, constString.getString().toString());
- constString = (DexConstString) code.instructions[4];
- assertEquals(BOO, constString.getString().toString());
}
@Test