Revising parameter usage info.

This CL extends the range of supported cases for class inlining,
mostly for benefit of kotlin classes and lambdas inlining.

Adds 3 new cases of class inlining on tachoyomi (-300 bytes) and
3 new cases on gmscore10 (-1K).

Bug:
Change-Id: I79c9b63d2edd06d7e298fdd1c548b30fcaab6fc9
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 845eecd..69126be 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -25,10 +25,9 @@
 import com.android.tools.r8.dex.JumboStringRewriter;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.graph.AppInfo.ResolutionResult;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.ParameterUsage;
+import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.ValueNumberGenerator;
 import com.android.tools.r8.ir.code.ValueType;
@@ -45,13 +44,11 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
 public class DexEncodedMethod extends KeyedDexItem<DexMethod> implements ResolutionResult {
 
@@ -621,51 +618,6 @@
     }
   }
 
-  public static final class ParameterUsagesInfo {
-    private ImmutableList<ParameterUsage> parametersUsages;
-
-    public ParameterUsagesInfo(List<ParameterUsage> usages) {
-      assert !usages.isEmpty();
-      parametersUsages = ImmutableList.copyOf(usages);
-      assert parametersUsages.size() ==
-          parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
-    }
-
-    public static abstract class ParameterUsage {
-      public final int index;
-
-      ParameterUsage(int index) {
-        this.index = index;
-      }
-    }
-
-    public static class SingleCallOfArgumentMethod extends ParameterUsage {
-      public final Invoke.Type type;
-      public final DexMethod method;
-
-      public SingleCallOfArgumentMethod(int index, Type type, DexMethod method) {
-        super(index);
-        this.type = type;
-        this.method = method;
-      }
-    }
-
-    public static class NotUsed extends ParameterUsage {
-      public NotUsed(int index) {
-        super(index);
-      }
-    }
-
-    ParameterUsage getParameterUsage(int parameter) {
-      for (ParameterUsage usage : parametersUsages) {
-        if (usage.index == parameter) {
-          return usage;
-        }
-      }
-      return null;
-    }
-  }
-
   public static class OptimizationInfo {
 
     private int returnedArgument = -1;
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterUsagesInfo.java b/src/main/java/com/android/tools/r8/graph/ParameterUsagesInfo.java
new file mode 100644
index 0000000..84cf331
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ParameterUsagesInfo.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2018, 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.graph;
+
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public final class ParameterUsagesInfo {
+  private ImmutableList<ParameterUsage> parametersUsages;
+
+  public ParameterUsagesInfo(List<ParameterUsage> usages) {
+    assert !usages.isEmpty();
+    parametersUsages = ImmutableList.copyOf(usages);
+    assert parametersUsages.size() ==
+        parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
+  }
+
+  ParameterUsage getParameterUsage(int parameter) {
+    for (ParameterUsage usage : parametersUsages) {
+      if (usage.index == parameter) {
+        return usage;
+      }
+    }
+    return null;
+  }
+
+  public final static class ParameterUsage {
+    public final int index;
+    public final Set<Type> ifZeroTest;
+    public final List<Pair<Invoke.Type, DexMethod>> callsReceiver;
+    public final boolean returnValue;
+
+    ParameterUsage(int index, Set<Type> ifZeroTest,
+        List<Pair<Invoke.Type, DexMethod>> callsReceiver, boolean returnValue) {
+      this.index = index;
+      this.ifZeroTest = ifZeroTest.isEmpty()
+          ? Collections.emptySet() : ImmutableSet.copyOf(ifZeroTest);
+      this.callsReceiver = callsReceiver.isEmpty()
+          ? Collections.emptyList() : ImmutableList.copyOf(callsReceiver);
+      this.returnValue = returnValue;
+    }
+
+    public boolean notUsed() {
+      return ifZeroTest == null && callsReceiver == null && !returnValue;
+    }
+  }
+
+  public static class ParameterUsageBuilder {
+    private final int index;
+    private final Value arg;
+    private final Set<Type> ifZeroTestTypes = new HashSet<>();
+    private final List<Pair<Invoke.Type, DexMethod>> callsOnReceiver = new ArrayList<>();
+    private boolean returnValue = false;
+
+    public ParameterUsageBuilder(Value arg, int index) {
+      this.arg = arg;
+      this.index = index;
+    }
+
+    // Returns false if the instruction is not supported.
+    public boolean note(Instruction instruction) {
+      if (instruction.isInvokeMethodWithReceiver() &&
+          instruction.inValues().lastIndexOf(arg) == 0) {
+        callsOnReceiver.add(new Pair<>(
+            instruction.asInvokeMethodWithReceiver().getType(),
+            instruction.asInvokeMethodWithReceiver().getInvokedMethod()));
+        return true;
+      }
+
+      if (instruction.isIf() && instruction.asIf().isZeroTest()) {
+        assert instruction.inValues().size() == 1 && instruction.inValues().get(0) == arg;
+        ifZeroTestTypes.add(instruction.asIf().getType());
+        return true;
+      }
+
+      if (instruction.isReturn()) {
+        assert instruction.inValues().size() == 1 && instruction.inValues().get(0) == arg;
+        returnValue = true;
+        return true;
+      }
+
+      return false;
+    }
+
+    public ParameterUsage build() {
+      return new ParameterUsage(index, ifZeroTestTypes, callsOnReceiver, returnValue);
+    }
+  }
+}
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 590b0c5..3288aa9 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
@@ -745,7 +745,7 @@
           Suppliers.memoize(() -> inliner.createDefaultOracle(
               method, code, effectivelyFinalTypeEnvironment,
               isProcessedConcurrently, callSiteInformation,
-              Integer.MAX_VALUE, Integer.MAX_VALUE)
+              Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2)
           )
       );
       assert code.isConsistentSSA();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
index ad6d66d..c26aecf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import java.util.BitSet;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
index 326fa03..2b1a6db 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import java.util.BitSet;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
index 359c90d..a547b36 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import java.util.BitSet;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 5066f87..ee64165 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -13,10 +13,6 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.NotUsed;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.ParameterUsage;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.SingleCallOfArgumentMethod;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialInstanceInitializer;
@@ -37,6 +33,9 @@
 import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
+import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
+import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsageBuilder;
 import com.android.tools.r8.ir.analysis.type.TypeEnvironment;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.ArrayPut;
@@ -851,28 +850,27 @@
     List<Value> values = code.collectArguments(true);
     for (int i = 0; i < values.size(); i++) {
       Value value = values.get(i);
-
-      int numberOfAllUsages = value.numberOfAllUsers();
-      if (numberOfAllUsages == 0) {
-        usages.add(new NotUsed(i));
+      if (value.numberOfPhiUsers() > 0) {
         continue;
       }
-
-      if (numberOfAllUsages == 1 && value.numberOfUsers() == 1) {
-        Instruction instruction = value.singleUniqueUser();
-        if (instruction.isInvokeMethodWithReceiver() &&
-            instruction.inValues().lastIndexOf(value) == 0) {
-          usages.add(new SingleCallOfArgumentMethod(i,
-              instruction.asInvokeMethodWithReceiver().getType(),
-              instruction.asInvokeMethodWithReceiver().getInvokedMethod()));
-        }
-        continue;
+      ParameterUsage usage = collectParameterUsages(i, value);
+      if (usage != null) {
+        usages.add(usage);
       }
     }
-
     feedback.setParameterUsages(method, usages.isEmpty() ? null : new ParameterUsagesInfo(usages));
   }
 
+  private ParameterUsage collectParameterUsages(int i, Value value) {
+    ParameterUsageBuilder builder = new ParameterUsageBuilder(value, i);
+    for (Instruction user : value.uniqueUsers()) {
+      if (!builder.note(user)) {
+        return null;
+      }
+    }
+    return builder.build();
+  }
+
   // This method defines trivial instance initializer as follows:
   //
   // ** The initializer may call the initializer of the base class, which
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 4d6e3b1..8556905 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -9,9 +9,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
 import com.android.tools.r8.graph.DexEncodedMethod.OptimizationInfo;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.NotUsed;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.ParameterUsage;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.SingleCallOfArgumentMethod;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialInstanceInitializer;
@@ -19,6 +16,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.IRCode;
@@ -38,7 +36,9 @@
 import com.android.tools.r8.kotlin.KotlinInfo;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.IdentityHashMap;
 import java.util.LinkedList;
@@ -48,6 +48,9 @@
 import java.util.function.Supplier;
 
 final class InlineCandidateProcessor {
+  private static final ImmutableSet<If.Type> ALLOWED_ZERO_TEST_TYPES =
+      ImmutableSet.of(If.Type.EQ, If.Type.NE);
+
   private final DexItemFactory factory;
   private final AppInfoWithLiveness appInfo;
   private final Predicate<DexType> isClassEligible;
@@ -532,13 +535,24 @@
   //      eligible according to the same rules defined for direct method call eligibility
   //      (except we require the method receiver to not be used in return instruction)
   //
+  //   -- eligible instance is used in zero-test 'if' instructions testing if the value
+  //      is null/not-null (since we know the instance is not null, those checks can
+  //      be rewritten)
+  //
   //   -- method itself can be inlined
   //
-  private synchronized boolean isExtraMethodCallEligible(
+  private boolean isExtraMethodCallEligible(
       Supplier<InliningOracle> defaultOracle, InvokeMethod invokeMethod) {
 
     List<Value> arguments = Lists.newArrayList(invokeMethod.inValues());
 
+    // Don't consider constructor invocations and super calls, since
+    // we don't want to forcibly inline them.
+    if (invokeMethod.isInvokeSuper() ||
+        (invokeMethod.isInvokeDirect() && factory.isConstructor(invokeMethod.getInvokedMethod()))) {
+      return false;
+    }
+
     // Remove receiver from arguments.
     if (invokeMethod.isInvokeMethodWithReceiver()) {
       if (arguments.get(0) == eligibleInstance) {
@@ -569,35 +583,44 @@
         return false;  // Don't know anything.
       }
 
-      if (parameterUsage instanceof NotUsed) {
+      if (parameterUsage.notUsed()) {
         // Reference can be removed since it's not used.
         unusedArguments.add(new Pair<>(invokeMethod, argIndex));
         continue;
       }
 
-      if (parameterUsage instanceof SingleCallOfArgumentMethod) {
-        // Method exactly one time calls a method on passed eligibleInstance.
-        SingleCallOfArgumentMethod info = (SingleCallOfArgumentMethod) parameterUsage;
-        if (info.type != Type.VIRTUAL && info.type != Type.INTERFACE) {
-          return false; // Don't support direct and super calls yet.
-        }
-
-        // Is the method called indirectly still eligible?
-        InliningInfo potentialInliningInfo = isEligibleIndirectMethodCall(info.method);
-        if (potentialInliningInfo != null) {
-          // Check if the method is inline-able by standard inliner.
-          InlineAction inlineAction =
-              invokeMethod.computeInlining(defaultOracle.get(), method.method.holder);
-          if (inlineAction != null) {
-            extraMethodCalls.put(invokeMethod, new InliningInfo(singleTarget, null));
-            continue;
-          }
-        }
-
+      if (parameterUsage.returnValue &&
+          !(invokeMethod.outValue() == null || invokeMethod.outValue().numberOfAllUsers() == 0)) {
+        // Used as return value which is not ignored.
         return false;
       }
 
-      return false; // All other argument usages are not eligible.
+      if (!Sets.difference(parameterUsage.ifZeroTest, ALLOWED_ZERO_TEST_TYPES).isEmpty()) {
+        // Used in unsupported zero-check-if kinds.
+        return false;
+      }
+
+      for (Pair<Type, DexMethod> call : parameterUsage.callsReceiver) {
+        if (call.getFirst() != Type.VIRTUAL && call.getFirst() != Type.INTERFACE) {
+          // Don't support direct and super calls yet.
+          return false;
+        }
+
+        // Is the method called indirectly still eligible?
+        InliningInfo potentialInliningInfo = isEligibleIndirectMethodCall(call.getSecond());
+        if (potentialInliningInfo == null) {
+          return false;
+        }
+
+        // Check if the method is inline-able by standard inliner.
+        InlineAction inlineAction =
+            invokeMethod.computeInlining(defaultOracle.get(), method.method.holder);
+        if (inlineAction == null) {
+          return false;
+        }
+      }
+
+      extraMethodCalls.put(invokeMethod, new InliningInfo(singleTarget, null));
     }
 
     // Looks good.
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index a690429..81e12a1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -114,6 +114,21 @@
           "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1").isPresent());
       assertTrue(inspector.clazz(
           "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1").isPresent());
+      assertTrue(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1").isPresent());
+      assertTrue(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1").isPresent());
+      assertTrue(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1").isPresent());
+      assertTrue(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1")
+          .isPresent());
+      assertTrue(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1")
+          .isPresent());
+      assertTrue(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1")
+          .isPresent());
     });
 
     runTest("class_inliner_lambda_k_style", mainClassName, true, (app) -> {
@@ -136,6 +151,31 @@
 
       assertFalse(inspector.clazz(
           "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1").isPresent());
+
+      assertEquals(
+          Sets.newHashSet(),
+          collectAccessedLambdaTypes(lambdaCheck, clazz, "testBigExtraMethod"));
+
+      assertFalse(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1").isPresent());
+      assertFalse(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1").isPresent());
+      assertFalse(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1").isPresent());
+
+      assertEquals(
+          Sets.newHashSet(),
+          collectAccessedLambdaTypes(lambdaCheck, clazz, "testBigExtraMethodReturningLambda"));
+
+      assertFalse(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1")
+          .isPresent());
+      assertFalse(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1")
+          .isPresent());
+      assertFalse(inspector.clazz(
+          "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1")
+          .isPresent());
     });
   }
 
diff --git a/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt b/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
index 136f72d..06b937e 100644
--- a/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
+++ b/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
@@ -11,6 +11,8 @@
 fun main(args: Array<String>) {
     testKotlinSequencesStateless(produceSequence(10))
     testKotlinSequencesStateful(5, 2, produceSequence(10))
+    testBigExtraMethod()
+    testBigExtraMethodReturningLambda()
 }
 
 data class Record(val foo: String, val good: Boolean)
@@ -38,12 +40,57 @@
     }
 }
 
-private fun produceSequence(size: Int): Sequence<String> {
+@Synchronized
+fun testBigExtraMethod() {
+    useRecord()
+    bigUserWithNotNullChecksAndTwoCalls(next()) { next() }
+    testBigExtraMethod2()
+    testBigExtraMethod3()
+}
+
+fun testBigExtraMethod2() {
+    bigUserWithNotNullChecksAndTwoCalls(next()) { next() }
+}
+
+fun testBigExtraMethod3() {
+    bigUserWithNotNullChecksAndTwoCalls(next()) { next() }
+}
+
+fun bigUserWithNotNullChecksAndTwoCalls(id: String, lambda: () -> String): String {
+    useRecord()
+    println("[A] logging call#$id returning ${lambda()}")
+    return "$id: ${lambda()}"
+}
+
+@Synchronized
+fun testBigExtraMethodReturningLambda() {
+    useRecord()
+    bigUserReturningLambda(next()) { next() } // Not used
+    testBigExtraMethodReturningLambda2()
+    testBigExtraMethodReturningLambda3()
+}
+
+fun testBigExtraMethodReturningLambda2() {
+    bigUserReturningLambda(next()) { next() } // Not used
+}
+
+fun testBigExtraMethodReturningLambda3() {
+    bigUserReturningLambda(next()) { next() } // Not used
+}
+
+fun bigUserReturningLambda(id: String, lambda: () -> String): () -> String {
+    useRecord()
+    println("[B] logging call#$id returning ${lambda()}")
+    println("[C] logging call#$id returning ${lambda()}")
+    return lambda
+}
+
+fun produceSequence(size: Int): Sequence<String> {
     var count = size
     return generateSequence { if (count-- > 0) next() else null }
 }
 
 // Need this to make sure testKotlinSequenceXXX is not processed
 // concurrently with invoke() on lambdas.
-fun useRecord() = useRecord2()
-fun useRecord2() = Record("", true)
+@Synchronized fun useRecord() = useRecord2()
+@Synchronized fun useRecord2() = Record("", true)