Version 1.6.33
Cherry-pick: Avoid that force inlining marks are overridden by delayed
optimization feedback
CL: https://r8-review.googlesource.com/c/r8/+/43662
Cherry-pick: Move ServiceLoader desugaring to CfCode to support catch
handler
CL: https://r8-review.googlesource.com/c/r8/+/43744
Bug: 141735091
Bug: 141290856
Change-Id: I800bbd7582845a1ee10fb730c130533348c15d97
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 02abab8..753d1b1 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.6.32";
+ public static final String LABEL = "1.6.33";
private Version() {
}
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 36e4cc8..e52c246 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -213,6 +213,8 @@
createString("Ljava/lang/reflect/InvocationHandler;");
public final DexString proxyDescriptor = createString("Ljava/lang/reflect/Proxy;");
public final DexString serviceLoaderDescriptor = createString("Ljava/util/ServiceLoader;");
+ public final DexString serviceLoaderConfigurationErrorDescriptor =
+ createString("Ljava/util/ServiceConfigurationError;");
public final DexString listDescriptor = createString("Ljava/util/List;");
public final DexString comparatorDescriptor = createString("Ljava/util/Comparator;");
public final DexString callableDescriptor = createString("Ljava/util/concurrent/Callable;");
@@ -301,6 +303,8 @@
public final DexType invocationHandlerType = createType(invocationHandlerDescriptor);
public final DexType proxyType = createType(proxyDescriptor);
public final DexType serviceLoaderType = createType(serviceLoaderDescriptor);
+ public final DexType serviceLoaderConfigurationErrorType =
+ createType(serviceLoaderConfigurationErrorDescriptor);
public final DexType listType = createType(listDescriptor);
public final DexType comparatorType = createType(comparatorDescriptor);
public final DexType callableType = createType(callableDescriptor);
@@ -574,6 +578,7 @@
public class ThrowableMethods {
public final DexMethod addSuppressed;
+ public final DexMethod getMessage;
public final DexMethod getSuppressed;
public final DexMethod initCause;
@@ -584,6 +589,12 @@
createString("getSuppressed"), throwableArrayDescriptor, DexString.EMPTY_ARRAY);
initCause = createMethod(throwableDescriptor, createString("initCause"), throwableDescriptor,
new DexString[] { throwableDescriptor });
+ getMessage =
+ createMethod(
+ throwableDescriptor,
+ createString("getMessage"),
+ stringDescriptor,
+ DexString.EMPTY_ARRAY);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 83ecf82..055b120 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -258,7 +259,8 @@
|| name.contains(OutlineOptions.CLASS_NAME)
|| name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME)
|| name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME)
- || name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX);
+ || name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX)
+ || name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME);
}
public boolean isProgramType(DexDefinitionSupplier definitions) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index e370192..9c4215e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -161,6 +161,7 @@
private final UninstantiatedTypeOptimization uninstantiatedTypeOptimization;
private final TypeChecker typeChecker;
private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
+ private final ServiceLoaderRewriter serviceLoaderRewriter;
// Assumers that will insert Assume instructions.
private final AliasIntroducer aliasIntroducer;
@@ -243,6 +244,7 @@
this.d8NestBasedAccessDesugaring = null;
this.stringSwitchRemover = null;
this.desugaredLibraryAPIConverter = null;
+ this.serviceLoaderRewriter = null;
return;
}
this.lambdaRewriter = options.enableDesugaring ? new LambdaRewriter(appView, this) : null;
@@ -309,6 +311,10 @@
: null;
this.typeChecker = new TypeChecker(appView.withLiveness());
this.d8NestBasedAccessDesugaring = null;
+ this.serviceLoaderRewriter =
+ options.enableServiceLoaderRewriting
+ ? new ServiceLoaderRewriter(appView.withLiveness())
+ : null;
} else {
this.classInliner = null;
this.classStaticizer = null;
@@ -326,6 +332,7 @@
this.typeChecker = null;
this.d8NestBasedAccessDesugaring =
options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
+ this.serviceLoaderRewriter = null;
}
this.stringSwitchRemover =
options.isStringSwitchConversionEnabled()
@@ -717,6 +724,12 @@
printPhase("Lambda merging finalization");
finalizeLambdaMerging(application, feedback, builder, executorService);
+ if (serviceLoaderRewriter != null && serviceLoaderRewriter.getSynthesizedClass() != null) {
+ forEachSynthesizedServiceLoaderMethod(
+ executorService, serviceLoaderRewriter.getSynthesizedClass());
+ builder.addSynthesizedClass(serviceLoaderRewriter.getSynthesizedClass(), true);
+ }
+
if (outliner != null) {
printPhase("Outlining");
timing.begin("IR conversion phase 3");
@@ -842,6 +855,24 @@
ThreadUtils.awaitFutures(futures);
}
+ private void forEachSynthesizedServiceLoaderMethod(
+ ExecutorService executorService, DexClass synthesizedClass) throws ExecutionException {
+ List<Future<?>> futures = new ArrayList<>();
+ for (DexEncodedMethod method : synthesizedClass.methods()) {
+ futures.add(
+ executorService.submit(
+ () -> {
+ IRCode code =
+ method.buildIR(appView, appView.appInfo().originFor(method.method.holder));
+ assert code != null;
+ codeRewriter.rewriteMoveResult(code);
+ finalizeIR(method, code, OptimizationFeedbackIgnore.getInstance());
+ return null;
+ }));
+ }
+ ThreadUtils.awaitFutures(futures);
+ }
+
private void collectLambdaMergingCandidates(DexApplication application) {
if (lambdaMerger != null) {
lambdaMerger.collectGroupCandidates(application, appView.withLiveness());
@@ -1092,9 +1123,9 @@
// we will return with finalizeEmptyThrowingCode() above.
assert code.verifyTypes(appView);
- if (appView.enableWholeProgramOptimizations() && options.enableServiceLoaderRewriting) {
+ if (serviceLoaderRewriter != null) {
assert appView.appInfo().hasLiveness();
- ServiceLoaderRewriter.rewrite(code, appView.withLiveness());
+ serviceLoaderRewriter.rewrite(code);
}
if (classStaticizer != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 376e186..08aefe9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -19,6 +19,8 @@
public interface MethodOptimizationFeedback {
+ void markForceInline(DexEncodedMethod method);
+
void markInlinedIntoSingleCallSite(DexEncodedMethod method);
void markMethodCannotBeKept(DexEncodedMethod method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
new file mode 100644
index 0000000..74b1631
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ServiceLoaderSourceCode.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2019, 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.desugar;
+
+import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+
+import com.android.tools.r8.cf.code.CfArrayStore;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.ValueType;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class ServiceLoaderSourceCode {
+
+ // This is building the following implementation for service-loader rewriting:
+ // public static <S> Iterator<S> loadS() {
+ // try {
+ // return Arrays.asList(X, Y, Z).iterator();
+ // } catch (Throwable t) {
+ // throw new ServiceConfigurationError(t.getMessage(), t);
+ // }
+ // }
+ public static CfCode generate(
+ DexType serviceType, List<DexClass> classes, DexItemFactory factory) {
+ ImmutableList.Builder<CfInstruction> builder = ImmutableList.builder();
+
+ CfLabel tryCatchStart = new CfLabel();
+ CfLabel tryCatchEnd = new CfLabel();
+
+ builder.add(
+ tryCatchStart,
+ new CfConstNumber(classes.size(), ValueType.INT),
+ new CfNewArray(factory.createArrayType(1, serviceType)));
+
+ for (int i = 0; i < classes.size(); i++) {
+ builder.add(
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(i, ValueType.INT),
+ new CfNew(classes.get(i).type),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ INVOKESPECIAL,
+ factory.createMethod(
+ classes.get(i).type,
+ factory.createProto(factory.voidType),
+ factory.constructorMethodName),
+ false),
+ new CfArrayStore(MemberType.OBJECT));
+ }
+
+ builder.add(
+ new CfInvoke(INVOKESTATIC, factory.utilArraysMethods.asList, false),
+ new CfInvoke(
+ INVOKEINTERFACE,
+ factory.createMethod(
+ factory.listType,
+ factory.createProto(factory.iteratorType),
+ factory.createString("iterator")),
+ true),
+ tryCatchEnd,
+ new CfReturn(ValueType.OBJECT));
+
+ // Build the exception handler.
+ CfLabel tryCatchHandler = new CfLabel();
+ builder.add(
+ tryCatchHandler,
+ new CfStore(ValueType.OBJECT, 0),
+ new CfNew(factory.serviceLoaderConfigurationErrorType),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(INVOKEVIRTUAL, factory.throwableMethods.getMessage, false),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ INVOKESPECIAL,
+ factory.createMethod(
+ factory.serviceLoaderConfigurationErrorType,
+ factory.createProto(factory.voidType, factory.stringType, factory.throwableType),
+ factory.constructorMethodName),
+ false),
+ new CfThrow());
+
+ CfTryCatch cfTryCatch =
+ new CfTryCatch(
+ tryCatchStart,
+ tryCatchEnd,
+ ImmutableList.of(factory.throwableType),
+ ImmutableList.of(tryCatchHandler));
+
+ return new CfCode(
+ null, 5, 1, builder.build(), ImmutableList.of(cfTryCatch), ImmutableList.of());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 89e0f2d..7830f6e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -4,33 +4,36 @@
package com.android.tools.r8.ir.optimize;
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
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.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.ConstClass;
-import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeInterface;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.MemberType;
-import com.android.tools.r8.ir.code.NewArrayEmpty;
-import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.desugar.ServiceLoaderSourceCode;
+import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
/**
* ServiceLoaderRewriter will attempt to rewrite calls on the form of: ServiceLoader.load(X.class,
@@ -61,7 +64,24 @@
*/
public class ServiceLoaderRewriter {
- public static void rewrite(IRCode code, AppView<? extends AppInfoWithLiveness> appView) {
+ public static final String SERVICE_LOADER_CLASS_NAME = "$$ServiceLoaderMethods";
+ private static final String SERVICE_LOADER_METHOD_PREFIX_NAME = "$load";
+
+ private DexProgramClass synthesizedClass;
+ private ConcurrentHashMap<DexType, DexEncodedMethod> synthesizedServiceLoaders =
+ new ConcurrentHashMap<>();
+
+ private final AppView<? extends AppInfoWithLiveness> appView;
+
+ public ServiceLoaderRewriter(AppView<? extends AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ public DexProgramClass getSynthesizedClass() {
+ return synthesizedClass;
+ }
+
+ public void rewrite(IRCode code) {
DexItemFactory factory = appView.dexItemFactory();
InstructionListIterator instructionIterator = code.instructionListIterator();
while (instructionIterator.hasNext()) {
@@ -144,71 +164,106 @@
}
// We can perform the rewrite of the ServiceLoader.load call.
- new Rewriter(appView, code, instructionIterator, serviceLoaderLoad)
- .perform(classLoaderInvoke, constClass.getValue(), classes);
+ DexEncodedMethod synthesizedMethod =
+ synthesizedServiceLoaders.computeIfAbsent(
+ constClass.getValue(),
+ service -> {
+ DexEncodedMethod addedMethod = createSynthesizedMethod(service, classes);
+ if (appView.options().isGeneratingClassFiles()) {
+ addedMethod.upgradeClassFileVersion(code.method.getClassFileVersion());
+ }
+ return addedMethod;
+ });
+
+ new Rewriter(code, instructionIterator, serviceLoaderLoad)
+ .perform(classLoaderInvoke, synthesizedMethod.method);
}
}
+ private DexEncodedMethod createSynthesizedMethod(DexType serviceType, List<DexClass> classes) {
+ DexType serviceLoaderType =
+ appView.dexItemFactory().createType("L" + SERVICE_LOADER_CLASS_NAME + ";");
+ if (synthesizedClass == null) {
+ synthesizedClass =
+ new DexProgramClass(
+ serviceLoaderType,
+ null,
+ new SynthesizedOrigin("Service Loader desugaring", getClass()),
+ ClassAccessFlags.fromDexAccessFlags(
+ Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
+ appView.dexItemFactory().objectType,
+ DexTypeList.empty(),
+ appView.dexItemFactory().createString("ServiceLoader"),
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY, // Static fields.
+ DexEncodedField.EMPTY_ARRAY, // Instance fields.
+ DexEncodedMethod.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+ appView.dexItemFactory().getSkipNameValidationForTesting());
+ appView.appInfo().addSynthesizedClass(synthesizedClass);
+ }
+ DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
+ DexMethod method =
+ appView
+ .dexItemFactory()
+ .createMethod(
+ serviceLoaderType,
+ proto,
+ SERVICE_LOADER_METHOD_PREFIX_NAME + synthesizedServiceLoaders.size());
+ MethodAccessFlags methodAccess =
+ MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
+ DexEncodedMethod encodedMethod =
+ new DexEncodedMethod(
+ method,
+ methodAccess,
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()));
+ synthesizedClass.addDirectMethod(encodedMethod);
+ return encodedMethod;
+ }
+
/**
- * Rewriter will look assume that code is on the form:
+ * Rewriter assumes that the code is of the form:
*
* <pre>
* ConstClass v1 <- X
- * ConstClass v2 <- X
+ * ConstClass v2 <- X or NULL
* Invoke-Virtual v3 <- v2; method: java.lang.ClassLoader java.lang.Class.getClassLoader()
* Invoke-Static v4 <- v1, v3; method: java.util.ServiceLoader java.util.ServiceLoader
* .load(java.lang.Class, java.lang.ClassLoader)
* Invoke-Virtual v5 <- v4; method: java.util.Iterator java.util.ServiceLoader.iterator()
* </pre>
*
- * and rewrites it to for classes impl(X) defined in META-INF/services/X:
+ * and rewrites it to:
*
* <pre>
- * ConstClass v1 <- X
- * ConstClass v2 <- X
- * ConstNumber va <- impl(X).size() (INT)
- * NewArrayEmpty vb <- va X[]
- * for i = 0 to C - 1:
- * ConstNumber vc(i) <- i (INT)
- * NewInstance vd <- impl(X).get(i)
- * Invoke-Direct vd; method: void impl(X).get(i).<init>()
- * ArrayPut vb, vc(i), vd
- * end for
- * Invoke-Static ve <- vb; method: java.util.List java.util.Arrays.asList(java.lang.Object[])
- * Invoke-Interface v5 <- ve; method: java.util.Iterator java.util.List.iterator()
+ * Invoke-Static v5 <- ; method: java.util.Iterator syn(X)()
* </pre>
*
- * We rely on the DeadCodeRemover to remove the ConstClasses and any aliased values no longer
+ * where syn(X) is the synthesized method generated for the service class.
+ *
+ * <p>We rely on the DeadCodeRemover to remove the ConstClasses and any aliased values no longer
* used.
*/
private static class Rewriter {
- private final AppView appView;
- private final DexItemFactory factory;
private final IRCode code;
private final InvokeStatic serviceLoaderLoad;
private InstructionListIterator iterator;
- private MemberType memberType;
- private Value valueArray;
- private int index = 0;
- public Rewriter(
- AppView appView,
- IRCode code,
- InstructionListIterator iterator,
- InvokeStatic serviceLoaderLoad) {
- this.appView = appView;
- this.factory = appView.dexItemFactory();
+ Rewriter(IRCode code, InstructionListIterator iterator, InvokeStatic serviceLoaderLoad) {
this.iterator = iterator;
this.code = code;
this.serviceLoaderLoad = serviceLoaderLoad;
}
- public void perform(InvokeVirtual classLoaderInvoke, DexType dexType, List<DexClass> classes) {
- assert valueArray == null;
- assert memberType == null;
-
+ public void perform(InvokeVirtual classLoaderInvoke, DexMethod method) {
// Remove the ClassLoader call since this can throw and will not be removed otherwise.
if (classLoaderInvoke != null) {
clearGetClassLoader(classLoaderInvoke);
@@ -220,69 +275,11 @@
serviceLoaderLoad.outValue().singleUniqueUser().asInvokeVirtual();
iterator.replaceCurrentInstruction(code.createConstNull());
- // Build the array for the "loaded" classes.
- ConstNumber arrayLength = code.createIntConstant(classes.size());
- arrayLength.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(arrayLength);
-
- DexType arrayType = factory.createArrayType(1, dexType);
- TypeLatticeElement arrayLatticeElement =
- TypeLatticeElement.fromDexType(arrayType, definitelyNotNull(), appView);
- valueArray = code.createValue(arrayLatticeElement);
- NewArrayEmpty newArrayEmpty =
- new NewArrayEmpty(valueArray, arrayLength.outValue(), arrayType);
- newArrayEmpty.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(newArrayEmpty);
-
- this.memberType = MemberType.fromDexType(dexType);
-
- // Add all new instances to the array.
- classes.forEach(this::addNewServiceAndPutInArray);
-
- // Build the Arrays.asList(...) instruction.
- Value vArrayAsList =
- code.createValue(
- TypeLatticeElement.fromDexType(factory.listType, definitelyNotNull(), appView));
- InvokeStatic arraysAsList =
- new InvokeStatic(
- factory.utilArraysMethods.asList, vArrayAsList, ImmutableList.of(valueArray));
- arraysAsList.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(arraysAsList);
-
// Find the iterator instruction and replace it.
iterator.nextUntil(x -> x == serviceLoaderIterator);
-
- DexMethod method =
- factory.createMethod(
- factory.listType, factory.createProto(factory.iteratorType), "iterator");
- InvokeInterface arrayIterator =
- new InvokeInterface(
- method, serviceLoaderIterator.outValue(), ImmutableList.of(vArrayAsList));
- iterator.replaceCurrentInstruction(arrayIterator);
- }
-
- private void addNewServiceAndPutInArray(DexClass clazz) {
- ConstNumber indexInArray = code.createIntConstant(index++);
- indexInArray.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(indexInArray);
-
- TypeLatticeElement clazzLatticeElement =
- TypeLatticeElement.fromDexType(clazz.type, definitelyNotNull(), appView);
- Value vInstance = code.createValue(clazzLatticeElement);
- NewInstance newInstance = new NewInstance(clazz.type, vInstance);
- newInstance.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(newInstance);
-
- DexMethod method = clazz.getDefaultInitializer().method;
- assert method.getArity() == 0;
- InvokeDirect invokeDirect =
- new InvokeDirect(method, null, Collections.singletonList(vInstance));
- invokeDirect.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(invokeDirect);
-
- ArrayPut put = new ArrayPut(memberType, valueArray, indexInArray.outValue(), vInstance);
- put.setPosition(serviceLoaderLoad.getPosition());
- iterator.add(put);
+ InvokeStatic synthesizedInvoke =
+ new InvokeStatic(method, serviceLoaderIterator.outValue(), ImmutableList.of());
+ iterator.replaceCurrentInstruction(synthesizedInvoke);
}
private void clearGetClassLoader(InvokeVirtual classLoaderInvoke) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 162642c..d185a37 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -107,6 +107,11 @@
// METHOD OPTIMIZATION INFO:
@Override
+ public void markForceInline(DexEncodedMethod method) {
+ getMethodOptimizationInfoForUpdating(method).markForceInline();
+ }
+
+ @Override
public synchronized void markInlinedIntoSingleCallSite(DexEncodedMethod method) {
getMethodOptimizationInfoForUpdating(method).markInlinedIntoSingleCallSite();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index ea5afa0..79b2e7f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -44,6 +44,9 @@
// METHOD OPTIMIZATION INFO:
@Override
+ public void markForceInline(DexEncodedMethod method) {}
+
+ @Override
public void markInlinedIntoSingleCallSite(DexEncodedMethod method) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 537cbd1..9119b4b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -52,6 +52,11 @@
// METHOD OPTIMIZATION INFO.
@Override
+ public void markForceInline(DexEncodedMethod method) {
+ // Ignored.
+ }
+
+ @Override
public synchronized void markInlinedIntoSingleCallSite(DexEncodedMethod method) {
method.getMutableOptimizationInfo().markInlinedIntoSingleCallSite();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index a90efc7..4ce3033 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -79,6 +79,10 @@
}
}
+ public final boolean allLambdas(Predicate<LambdaInfo> predicate) {
+ return !anyLambda(lambda -> !predicate.test(lambda));
+ }
+
public final boolean anyLambda(Predicate<LambdaInfo> predicate) {
assert verifyLambdaIds(false);
for (LambdaInfo info : lambdas.values()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index f399ad6..89451c9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -228,6 +228,11 @@
// sequential lambda ids, create group lambda classes.
Map<LambdaGroup, DexProgramClass> lambdaGroupsClasses = finalizeLambdaGroups();
+ // Mark all the implementation methods for force inlining.
+ for (LambdaGroup group : lambdaGroupsClasses.keySet()) {
+ group.forEachLambda(info -> info.clazz.virtualMethods().forEach(feedback::markForceInline));
+ }
+
// Fixup optimization info to ensure that the optimization info does not refer to any merged
// lambdas.
LambdaMergerOptimizationInfoFixer optimizationInfoFixer =
@@ -238,7 +243,6 @@
this.strategyFactory = (method, code) -> new ApplyStrategy(method, code, optimizationInfoFixer);
// Add synthesized lambda group classes to the builder.
-
for (Entry<LambdaGroup, DexProgramClass> entry : lambdaGroupsClasses.entrySet()) {
DexProgramClass synthesizedClass = entry.getValue();
appView.appInfo().addSynthesizedClass(synthesizedClass);
@@ -254,6 +258,18 @@
synthesizedClass.forEachMethod(
encodedMethod -> encodedMethod.markProcessed(ConstraintWithTarget.NEVER));
}
+
+ // Verify that all implementation methods are marked for force inlining (i.e., check that the
+ // delayed optimization feedback has been flushed).
+ assert lambdaGroupsClasses.keySet().stream()
+ .allMatch(
+ group ->
+ group.allLambdas(
+ lambda ->
+ lambda.clazz.virtualMethods().stream()
+ .map(DexEncodedMethod::getOptimizationInfo)
+ .allMatch(MethodOptimizationInfo::forceInline)));
+
converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
// Rewrite lambda class references into lambda group class
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index 4226daa..15d4160 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -105,14 +105,6 @@
// anyways and our new method is a product of inlining.
MethodAccessFlags accessFlags = MAIN_METHOD_FLAGS.copy();
- // Mark all the impl methods for force inlining
- // LambdaGroupVirtualMethodSourceCode relies on.
- for (DexEncodedMethod implMethod : implMethods) {
- if (implMethod != null) {
- implMethod.getMutableOptimizationInfo().markForceInline();
- }
- }
-
DexMethod method = factory.createMethod(group.getGroupClassType(), methodProto, methodName);
result.add(
new DexEncodedMethod(