Add abstract function to optimization info of updateChangedFlags method

Change-Id: Iaad8f4a45c3cfa1518ec15262072ab28cf97da79
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
index 03085c1..2639a9d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/And.java
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -53,6 +53,11 @@
   }
 
   @Override
+  public And asAnd(Value leftValue, Value rightValue) {
+    return leftValue == leftValue() && rightValue == rightValue() ? this : null;
+  }
+
+  @Override
   public boolean isCommutative() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index eb88d9d..634225b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -308,6 +308,11 @@
   }
 
   @Override
+  public ConstNumber asConstNumber(long value) {
+    return this.value == value ? this : null;
+  }
+
+  @Override
   public DexType computeVerificationType(AppView<?> appView, TypeVerificationHelper helper) {
     assert outType().isObject();
     return appView.dexItemFactory().nullValueType;
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldGet.java b/src/main/java/com/android/tools/r8/ir/code/FieldGet.java
index 4d4a55b..6b7f62c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldGet.java
@@ -4,7 +4,11 @@
 
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 
 public interface FieldGet {
@@ -20,4 +24,7 @@
   boolean hasUsedOutValue();
 
   Value outValue();
+
+  FieldResolutionResult resolveField(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod context);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 99a3fe7..0fbadd1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -41,6 +41,11 @@
     this.field = field;
   }
 
+  public FieldResolutionResult resolveField(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod context) {
+    return appView.appInfo().resolveField(field, context);
+  }
+
   public abstract Value value();
 
   public FieldMemberType getType() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldPut.java b/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
index 991a263..488e60a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
@@ -4,7 +4,11 @@
 
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
 
 public interface FieldPut {
 
@@ -27,4 +31,7 @@
   boolean isStaticPut();
 
   StaticPut asStaticPut();
+
+  FieldResolutionResult resolveField(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod context);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 2bd7ab1..e81b4d0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -868,6 +868,10 @@
     return null;
   }
 
+  public ConstNumber asConstNumber(long value) {
+    return null;
+  }
+
   public boolean isResourceConstNumber() {
     return false;
   }
@@ -1240,6 +1244,10 @@
     return null;
   }
 
+  public Shl asShl(Value leftValue, Value rightValue) {
+    return null;
+  }
+
   public boolean isShr() {
     return false;
   }
@@ -1248,6 +1256,10 @@
     return null;
   }
 
+  public Shr asShr(Value leftValue, Value rightValue) {
+    return null;
+  }
+
   public boolean isUshr() {
     return false;
   }
@@ -1264,6 +1276,10 @@
     return null;
   }
 
+  public And asAnd(Value leftValue, Value rightValue) {
+    return null;
+  }
+
   public boolean isOr() {
     return false;
   }
@@ -1272,6 +1288,10 @@
     return null;
   }
 
+  public Or asOr(Value leftValue, Value rightValue) {
+    return null;
+  }
+
   public boolean isXor() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index e3ac085..63c5c94 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -29,6 +29,10 @@
   private InstructionListIterator currentBlockIterator;
   private Set<BasicBlock> seenBlocks = Sets.newIdentityHashSet();
 
+  public LinearFlowInstructionListIterator(IRCode code) {
+    this(code, code.entryBlock(), 0);
+  }
+
   public LinearFlowInstructionListIterator(IRCode code, BasicBlock block) {
     this(code, block, 0);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
index bca7903..0cc297a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Or.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -52,6 +52,11 @@
   }
 
   @Override
+  public Or asOr(Value leftValue, Value rightValue) {
+    return leftValue == leftValue() && rightValue == rightValue() ? this : null;
+  }
+
+  @Override
   public boolean isCommutative() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shl.java b/src/main/java/com/android/tools/r8/ir/code/Shl.java
index 747daf6..a9d9ee4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shl.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shl.java
@@ -52,6 +52,11 @@
   }
 
   @Override
+  public Shl asShl(Value leftValue, Value rightValue) {
+    return leftValue == leftValue() && rightValue == rightValue() ? this : null;
+  }
+
+  @Override
   public DexInstruction CreateInt(int dest, int left, int right) {
     return new DexShlInt(dest, left, right);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shr.java b/src/main/java/com/android/tools/r8/ir/code/Shr.java
index e18a846..bc59c79 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shr.java
@@ -47,6 +47,11 @@
   }
 
   @Override
+  public Shr asShr(Value leftValue, Value rightValue) {
+    return leftValue == leftValue() && rightValue == rightValue() ? this : null;
+  }
+
+  @Override
   public boolean isCommutative() {
     return false;
   }
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 c2fb0e6..f22f61a 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
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.BitSetUtils;
 import java.util.BitSet;
@@ -35,6 +36,8 @@
   void methodReturnsAbstractValue(
       DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue);
 
+  void setAbstractFunction(DexEncodedMethod method, AbstractFunction abstractFunction);
+
   default void setDynamicReturnType(
       ProgramMethod method, AppView<?> appView, DynamicType dynamicType) {
     setDynamicReturnType(method.getDefinition(), appView, dynamicType);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 36c1bce..6c10cc6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
 import com.android.tools.r8.shaking.MaximumRemovedAndroidLogLevelRule;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OptionalBool;
@@ -136,6 +137,11 @@
   }
 
   @Override
+  public AbstractFunction getAbstractFunction() {
+    return AbstractFunction.unknown();
+  }
+
+  @Override
   public AbstractValue getAbstractReturnValue() {
     return UNKNOWN_ABSTRACT_RETURN_VALUE;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index fadb852..9c7fb22 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OptionalBool;
 import java.util.BitSet;
@@ -82,6 +83,8 @@
 
   public abstract boolean isInitializerEnablingJavaVmAssertions();
 
+  public abstract AbstractFunction getAbstractFunction();
+
   public abstract AbstractValue getAbstractReturnValue();
 
   public abstract SimpleInliningConstraint getNopInliningConstraint(InternalOptions options);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 9b9b529..382be09 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -100,6 +100,9 @@
 import com.android.tools.r8.ir.optimize.typechecks.CheckCastAndInstanceOfMethodSpecialization;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.kotlin.Kotlin.Intrinsics;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
+import com.android.tools.r8.optimize.compose.ComposeUtils;
+import com.android.tools.r8.optimize.compose.UpdateChangedFlagsAbstractFunction;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -142,6 +145,7 @@
     DexEncodedMethod definition = method.getDefinition();
     identifyBridgeInfo(method, code, feedback, timing);
     analyzeReturns(code, feedback, methodProcessor, timing);
+    computeAbstractFunction(code, feedback);
     if (options.enableClassInlining) {
       computeClassInlinerMethodConstraint(method, code, feedback, timing);
     }
@@ -745,6 +749,16 @@
     return true;
   }
 
+  private void computeAbstractFunction(IRCode code, OptimizationFeedback feedback) {
+    if (ComposeUtils.isUpdateChangedFlags(code, appView.dexItemFactory())) {
+      MethodParameter methodParameter = new MethodParameter(code.context().getReference(), 0);
+      UpdateChangedFlagsAbstractFunction updateChangedFlagsAbstractFunction =
+          new UpdateChangedFlagsAbstractFunction(methodParameter);
+      feedback.setAbstractFunction(
+          code.context().getDefinition(), updateChangedFlagsAbstractFunction);
+    }
+  }
+
   private void computeClassInlinerMethodConstraint(
       ProgramMethod method,
       IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
index 2139f0f..4831f7f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OptionalBool;
 import java.util.BitSet;
@@ -75,6 +76,11 @@
   }
 
   @Override
+  public AbstractFunction getAbstractFunction() {
+    return AbstractFunction.unknown();
+  }
+
+  @Override
   public AbstractValue getAbstractReturnValue() {
     return abstractReturnValue;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index f2a5336..9987a33 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.MaximumRemovedAndroidLogLevelRule;
 import com.android.tools.r8.utils.BitSetUtils;
@@ -42,6 +43,7 @@
   private Set<DexType> initializedClassesOnNormalExit =
       DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
   private int returnedArgument = DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT;
+  private AbstractFunction abstractFunction = AbstractFunction.unknown();
   private AbstractValue abstractReturnValue =
       DefaultMethodOptimizationInfo.UNKNOWN_ABSTRACT_RETURN_VALUE;
   private ClassInlinerMethodConstraint classInlinerConstraint =
@@ -134,6 +136,7 @@
   // Copy constructor used to create a mutable copy. Do not forget to copy from template when a new
   // field is added.
   private MutableMethodOptimizationInfo(MutableMethodOptimizationInfo template) {
+    abstractFunction = template.abstractFunction;
     argumentInfos = template.argumentInfos;
     flags = template.flags;
     initializedClassesOnNormalExit = template.initializedClassesOnNormalExit;
@@ -473,6 +476,11 @@
   }
 
   @Override
+  public AbstractFunction getAbstractFunction() {
+    return abstractFunction;
+  }
+
+  @Override
   public AbstractValue getAbstractReturnValue() {
     return abstractReturnValue;
   }
@@ -699,6 +707,10 @@
     abstractReturnValue = UnknownValue.getInstance();
   }
 
+  void setAbstractFunction(AbstractFunction abstractFunction) {
+    this.abstractFunction = abstractFunction;
+  }
+
   void setDynamicType(AppView<?> appView, DynamicType newDynamicType, DexEncodedMethod method) {
     setDynamicType(appView, newDynamicType, method.getReturnType().toTypeElement(appView));
   }
@@ -780,6 +792,7 @@
     return argumentInfos == top.getArgumentInfos()
         && initializedClassesOnNormalExit == top.getInitializedClassesOnNormalExit()
         && returnedArgument == top.getReturnedArgument()
+        && abstractFunction == top.getAbstractFunction()
         && abstractReturnValue == top.getAbstractReturnValue()
         && classInlinerConstraint == top.getClassInlinerMethodConstraint()
         && convertCheckNotNull == top.isConvertCheckNotNull()
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 8add480..413d343 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
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.AppInfoWithLivenessModifier;
 import com.android.tools.r8.threading.ThreadingModule;
@@ -192,6 +193,11 @@
   }
 
   @Override
+  public void setAbstractFunction(DexEncodedMethod method, AbstractFunction abstractFunction) {
+    getMethodOptimizationInfoForUpdating(method).setAbstractFunction(abstractFunction);
+  }
+
+  @Override
   public synchronized void setDynamicReturnType(
       DexEncodedMethod method, AppView<?> appView, DynamicType dynamicType) {
     getMethodOptimizationInfoForUpdating(method).setDynamicType(appView, dynamicType, method);
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 bb8f841..61fe21d 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
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.BitSet;
 import java.util.Set;
@@ -72,6 +73,11 @@
       DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue value) {}
 
   @Override
+  public void setAbstractFunction(DexEncodedMethod method, AbstractFunction abstractFunction) {
+    assert false;
+  }
+
+  @Override
   public void setDynamicReturnType(
       DexEncodedMethod method, AppView<?> appView, DynamicType dynamicType) {}
 
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 34bb23b..9edabc2 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
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.OptionalBool;
 import java.util.BitSet;
@@ -110,6 +111,11 @@
   }
 
   @Override
+  public void setAbstractFunction(DexEncodedMethod method, AbstractFunction abstractFunction) {
+    assert false;
+  }
+
+  @Override
   public void setDynamicReturnType(
       DexEncodedMethod method, AppView<?> appView, DynamicType dynamicType) {
     method.getMutableOptimizationInfo().setDynamicType(appView, dynamicType, method);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/AbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/AbstractFunction.java
new file mode 100644
index 0000000..da529db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/AbstractFunction.java
@@ -0,0 +1,11 @@
+// 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.optimize.argumentpropagation.codescanner;
+
+public interface AbstractFunction extends InFlow {
+
+  static UnknownAbstractFunction unknown() {
+    return UnknownAbstractFunction.get();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
new file mode 100644
index 0000000..07a87b5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
@@ -0,0 +1,15 @@
+// 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.optimize.argumentpropagation.codescanner;
+
+public class UnknownAbstractFunction implements AbstractFunction {
+
+  private static final UnknownAbstractFunction INSTANCE = new UnknownAbstractFunction();
+
+  private UnknownAbstractFunction() {}
+
+  static UnknownAbstractFunction get() {
+    return INSTANCE;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/ComposeUtils.java b/src/main/java/com/android/tools/r8/optimize/compose/ComposeUtils.java
new file mode 100644
index 0000000..f539cd4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/compose/ComposeUtils.java
@@ -0,0 +1,120 @@
+// 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.optimize.compose;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.And;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.LinearFlowInstructionListIterator;
+import com.android.tools.r8.ir.code.Or;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Shl;
+import com.android.tools.r8.ir.code.Shr;
+
+public class ComposeUtils {
+
+  /**
+   * Checks if the given code object executes the following instructions.
+   *
+   * <pre>
+   * private const val changedLowBitMask = 0b001_001_001_001_001_001_001_001_001_001_0
+   * private const val changedHighBitMask = changedLowBitMask shl 1
+   * private const val changedMask = (changedLowBitMask or changedHighBitMask).inv()
+   *
+   * internal fun updateChangedFlags(flags: Int): Int {
+   *     val lowBits = flags and changedLowBitMask
+   *     val highBits = flags and changedHighBitMask
+   *     return ((flags and changedMask) or
+   *         (lowBits or (highBits shr 1)) or ((lowBits shl 1) and highBits))
+   * }
+   * </pre>
+   */
+  public static boolean isUpdateChangedFlags(IRCode code, DexItemFactory factory) {
+    ProgramMethod method = code.context();
+    if (!method.getAccessFlags().isStatic()
+        || method.getArity() != 1
+        || method.getParameter(0).isNotIdenticalTo(factory.intType)
+        || method.getReturnType().isNotIdenticalTo(factory.intType)) {
+      return false;
+    }
+    LinearFlowInstructionListIterator instructionIterator =
+        new LinearFlowInstructionListIterator(code);
+    Argument argument = instructionIterator.next().asArgument();
+    assert argument != null;
+    ConstNumber changedLowBitMask =
+        instructionIterator.next().asConstNumber(0b001_001_001_001_001_001_001_001_001_001_0);
+    if (changedLowBitMask == null) {
+      return false;
+    }
+    And lowBits =
+        instructionIterator.next().asAnd(argument.outValue(), changedLowBitMask.outValue());
+    if (lowBits == null) {
+      return false;
+    }
+    ConstNumber changedHighBitMask =
+        instructionIterator.next().asConstNumber(0b010_010_010_010_010_010_010_010_010_010_0);
+    if (changedHighBitMask == null) {
+      return false;
+    }
+    And highBits =
+        instructionIterator.next().asAnd(argument.outValue(), changedHighBitMask.outValue());
+    if (highBits == null) {
+      return false;
+    }
+    ConstNumber changedMask =
+        instructionIterator.next().asConstNumber(0b1_100_100_100_100_100_100_100_100_100_100_1);
+    if (changedMask == null) {
+      return false;
+    }
+    And changedBits = instructionIterator.next().asAnd(argument.outValue(), changedMask.outValue());
+    if (changedBits == null) {
+      return false;
+    }
+    ConstNumber one = instructionIterator.next().asConstNumber(1);
+    if (one == null) {
+      return false;
+    }
+    Shr highBitsShrOne = instructionIterator.next().asShr(highBits.outValue(), one.outValue());
+    if (highBitsShrOne == null) {
+      return false;
+    }
+    Or lowBitsOrHighBitsShrOne =
+        instructionIterator.next().asOr(lowBits.outValue(), highBitsShrOne.outValue());
+    if (lowBitsOrHighBitsShrOne == null) {
+      return false;
+    }
+    Or changedBitsOrLowBitsOrHighBitsShrOne =
+        instructionIterator.next().asOr(changedBits.outValue(), lowBitsOrHighBitsShrOne.outValue());
+    if (changedBitsOrLowBitsOrHighBitsShrOne == null) {
+      return false;
+    }
+    ConstNumber oneAgain = instructionIterator.next().asConstNumber(1);
+    if (oneAgain == null) {
+      return false;
+    }
+    Shl lowBitsShlOne = instructionIterator.next().asShl(lowBits.outValue(), oneAgain.outValue());
+    if (lowBitsShlOne == null) {
+      return false;
+    }
+    And lowBitsShlOneAndHighBits =
+        instructionIterator.next().asAnd(lowBitsShlOne.outValue(), highBits.outValue());
+    if (lowBitsShlOneAndHighBits == null) {
+      return false;
+    }
+    Or result =
+        instructionIterator
+            .next()
+            .asOr(
+                changedBitsOrLowBitsOrHighBitsShrOne.outValue(),
+                lowBitsShlOneAndHighBits.outValue());
+    if (result == null) {
+      return false;
+    }
+    Return theReturn = instructionIterator.next().asReturn();
+    return theReturn != null && theReturn.returnValue() == result.outValue();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
new file mode 100644
index 0000000..5ae767a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
@@ -0,0 +1,17 @@
+// 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.optimize.compose;
+
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.AbstractFunction;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.InFlow;
+
+public class UpdateChangedFlagsAbstractFunction implements AbstractFunction {
+
+  @SuppressWarnings("UnusedVariable")
+  private final InFlow inFlow;
+
+  public UpdateChangedFlagsAbstractFunction(InFlow inFlow) {
+    this.inFlow = inFlow;
+  }
+}