Polish AtomicFieldUpdaterOptimizer.

Change-Id: I89108c970052db8798b4986e13d2fe245b84d9b0
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/AtomicFieldUpdaterOptimizer.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/AtomicFieldUpdaterOptimizer.java
index 4d45fbb..7ed38ed 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/AtomicFieldUpdaterOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/AtomicFieldUpdaterOptimizer.java
@@ -34,8 +34,8 @@
 import java.util.Map;
 
 /**
- * This pass uses the information and instrumentation of {@code AtomicFieldUpdaterInstrumentor} to
- * find calls to AtomicFieldUpdater and replace them by their underlying unsafe operation.
+ * This pass uses the instrumentation of {@code AtomicFieldUpdaterInstrumentor} to find calls to
+ * AtomicReferenceFieldUpdater and replace them by their underlying unsafe operation.
  *
  * <BlockQuote>
  *
@@ -53,11 +53,10 @@
  *    ReturnType exampleFunction(..) {
  *      ..
  *      updater.compareAndSet(instance, expect, update);
- *      // If instance is known to be non-null and be a subtype of Example,
- *      // and update is known to have type SomeType or be null,
- *      // then replace the call with
+ *      // If static information permits, translate into:
+ *      checkNull(updater) // If necessary.
+ *      checkNull(instance) // If necessary.
  *      SyntheticUnsafeClass.unsafe.compareAndSet(instance, updater$offset, expect, update)
- *      // If updater can be null, a null-check is inserted before the new call.
  *      ..
  *    }
  *  }
@@ -99,16 +98,10 @@
     var atomicUpdaterFields = info.getInstrumentations().get(code.context().getHolderType());
     assert atomicUpdaterFields != null;
 
-    // This code is the assumed implementation of AtomicReferenceFieldUpdater.compareAndSet.
-    //
-    // boolean compareAndSet(T obj, V expect, V update) {
-    //   if (!this.cclass.isInstance(obj))
-    //     throwAccessCheckException(obj);
-    //   if (update != null && !(vclass.isInstance(update)))
-    //     throwCCE();
-    //   return U.compareAndSetReference(obj, offset, expect, update);
-    // }
     var it = code.instructionListIterator();
+    var context =
+        new OptimizationContext(
+            methodProcessor, methodProcessingContext, code, it, info, atomicUpdaterFields);
     var changed = false;
     while (it.hasNext()) {
       var next = it.nextUntil(Instruction::isInvokeVirtual);
@@ -123,58 +116,24 @@
         continue;
       }
 
-      // Check for updater.compareAndSet(holder, expect, update) call.
       if (invokedMethod.isIdenticalTo(
           dexItemFactory.atomicFieldUpdaterMethods.referenceCompareAndSet)) {
-        if (visitCompareAndSet(
-            code,
-            it,
-            methodProcessor,
-            methodProcessingContext,
-            invoke,
-            atomicUpdaterFields,
-            info,
-            next.outValue())) {
+        if (visitCompareAndSet(context, invoke)) {
           changed = true;
         }
       } else if (invokedMethod.isIdenticalTo(
           dexItemFactory.atomicFieldUpdaterMethods.referenceGet)) {
-        if (visitGet(
-            code,
-            it,
-            methodProcessor,
-            methodProcessingContext,
-            invoke,
-            atomicUpdaterFields,
-            info,
-            next.outValue())) {
+        if (visitGet(context, invoke)) {
           changed = true;
         }
       } else if (invokedMethod.isIdenticalTo(
           dexItemFactory.atomicFieldUpdaterMethods.referenceSet)) {
-        if (visitSet(
-            code,
-            it,
-            methodProcessor,
-            methodProcessingContext,
-            invoke,
-            atomicUpdaterFields,
-            info,
-            next.outValue())) {
+        if (visitSet(context, invoke)) {
           changed = true;
         }
       } else if (invokedMethod.isIdenticalTo(
           dexItemFactory.atomicFieldUpdaterMethods.referenceGetAndSet)) {
-        if (visitGetAndSet(
-            code,
-            it,
-            methodProcessor,
-            methodProcessingContext,
-            next.getPosition(),
-            invoke,
-            atomicUpdaterFields,
-            info,
-            next.outValue())) {
+        if (visitGetAndSet(context, invoke)) {
           changed = true;
         }
       } else {
@@ -184,34 +143,21 @@
     return CodeRewriterResult.hasChanged(changed);
   }
 
-  private boolean visitCompareAndSet(
-      IRCode code,
-      IRCodeInstructionListIterator it,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      InvokeVirtual invoke,
-      Map<DexField, AtomicFieldUpdaterInfo> atomicUpdaterFields,
-      AtomicFieldUpdaterInstrumentorInfo info,
-      Value outValue) {
-    // Resolve updater.
-    var updaterValue = invoke.getReceiver();
-    var resolvedUpdater = resolveUpdater(code, atomicUpdaterFields, updaterValue, invoke);
+  /** Returns true if {@code invoke} was rewritten. */
+  private boolean visitCompareAndSet(OptimizationContext context, InvokeVirtual invoke) {
+    var resolvedUpdater = resolveUpdater(context, invoke);
     if (resolvedUpdater == null) {
       return false;
     }
 
-    // Resolve holder.
-    var holderValue = invoke.getFirstNonReceiverArgument();
     var expectedHolder = resolvedUpdater.updaterFieldInfo.holder;
-    var resolvedHolder = resolveHolder(holderValue, expectedHolder, invoke);
+    var resolvedHolder = resolveHolder(invoke, expectedHolder);
     if (resolvedHolder == null) {
       return false;
     }
 
-    // Resolve expect.
     var expectValue = invoke.getSecondNonReceiverArgument();
 
-    // Resolve update.
     var updateValue = invoke.getThirdNonReceiverArgument();
     if (!isNewValueValid(resolvedUpdater, updateValue, invoke)) {
       return false;
@@ -221,42 +167,19 @@
         appView,
         new Event.CanOptimize(invoke, resolvedUpdater.isNullable, resolvedHolder.isNullable));
     rewriteCompareAndSet(
-        code,
-        it,
-        methodProcessor,
-        methodProcessingContext,
-        invoke.getPosition(),
-        resolvedUpdater,
-        resolvedHolder,
-        info,
-        updaterValue,
-        holderValue,
-        expectValue,
-        updateValue,
-        outValue);
+        context, invoke, resolvedUpdater, resolvedHolder, expectValue, updateValue);
     return true;
   }
 
-  private boolean visitGet(
-      IRCode code,
-      IRCodeInstructionListIterator it,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      InvokeVirtual invoke,
-      Map<DexField, AtomicFieldUpdaterInfo> atomicUpdaterFields,
-      AtomicFieldUpdaterInstrumentorInfo info,
-      Value outValue) {
-    // Resolve updater.
-    var updaterValue = invoke.getReceiver();
-    var resolvedUpdater = resolveUpdater(code, atomicUpdaterFields, updaterValue, invoke);
+  /** Returns true if {@code invoke} was rewritten. */
+  private boolean visitGet(OptimizationContext context, InvokeVirtual invoke) {
+    var resolvedUpdater = resolveUpdater(context, invoke);
     if (resolvedUpdater == null) {
       return false;
     }
 
-    // Resolve holder.
-    var holderValue = invoke.getFirstNonReceiverArgument();
     var expectedHolder = resolvedUpdater.updaterFieldInfo.holder;
-    var resolvedHolder = resolveHolder(holderValue, expectedHolder, invoke);
+    var resolvedHolder = resolveHolder(invoke, expectedHolder);
     if (resolvedHolder == null) {
       return false;
     }
@@ -264,46 +187,23 @@
     reportInfo(
         appView,
         new Event.CanOptimize(invoke, resolvedUpdater.isNullable, resolvedHolder.isNullable));
-    rewriteGet(
-        code,
-        it,
-        methodProcessor,
-        methodProcessingContext,
-        invoke.getPosition(),
-        resolvedUpdater,
-        resolvedHolder,
-        info,
-        updaterValue,
-        holderValue,
-        outValue);
+    rewriteGet(context, invoke, resolvedUpdater, resolvedHolder);
     return true;
   }
 
-  private boolean visitSet(
-      IRCode code,
-      IRCodeInstructionListIterator it,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      InvokeVirtual invoke,
-      Map<DexField, AtomicFieldUpdaterInfo> atomicUpdaterFields,
-      AtomicFieldUpdaterInstrumentorInfo info,
-      Value outValue) {
-    // Resolve updater.
-    var updaterValue = invoke.getReceiver();
-    var resolvedUpdater = resolveUpdater(code, atomicUpdaterFields, updaterValue, invoke);
+  /** Returns true if {@code invoke} was rewritten. */
+  private boolean visitSet(OptimizationContext context, InvokeVirtual invoke) {
+    var resolvedUpdater = resolveUpdater(context, invoke);
     if (resolvedUpdater == null) {
       return false;
     }
 
-    // Resolve holder.
-    var holderValue = invoke.getFirstNonReceiverArgument();
     var expectedHolder = resolvedUpdater.updaterFieldInfo.holder;
-    var resolvedHolder = resolveHolder(holderValue, expectedHolder, invoke);
+    var resolvedHolder = resolveHolder(invoke, expectedHolder);
     if (resolvedHolder == null) {
       return false;
     }
 
-    // Resolve newValue.
     var newValueValue = invoke.getSecondNonReceiverArgument();
     if (!isNewValueValid(resolvedUpdater, newValueValue, invoke)) {
       return false;
@@ -312,48 +212,23 @@
     reportInfo(
         appView,
         new Event.CanOptimize(invoke, resolvedUpdater.isNullable, resolvedHolder.isNullable));
-    rewriteSet(
-        code,
-        it,
-        methodProcessor,
-        methodProcessingContext,
-        invoke.getPosition(),
-        resolvedUpdater,
-        resolvedHolder,
-        info,
-        updaterValue,
-        holderValue,
-        newValueValue,
-        outValue);
+    rewriteSet(context, invoke, resolvedUpdater, resolvedHolder, newValueValue);
     return true;
   }
 
-  private boolean visitGetAndSet(
-      IRCode code,
-      IRCodeInstructionListIterator it,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      Position position,
-      InvokeVirtual invoke,
-      Map<DexField, AtomicFieldUpdaterInfo> atomicUpdaterFields,
-      AtomicFieldUpdaterInstrumentorInfo info,
-      Value outValue) {
-    // Resolve updater.
-    var updaterValue = invoke.getReceiver();
-    var resolvedUpdater = resolveUpdater(code, atomicUpdaterFields, updaterValue, invoke);
+  /** Returns true if {@code invoke} was rewritten. */
+  private boolean visitGetAndSet(OptimizationContext context, InvokeVirtual invoke) {
+    var resolvedUpdater = resolveUpdater(context, invoke);
     if (resolvedUpdater == null) {
       return false;
     }
 
-    // Resolve holder.
-    var holderValue = invoke.getFirstNonReceiverArgument();
     var expectedHolder = resolvedUpdater.updaterFieldInfo.holder;
-    var resolvedHolder = resolveHolder(holderValue, expectedHolder, invoke);
+    var resolvedHolder = resolveHolder(invoke, expectedHolder);
     if (resolvedHolder == null) {
       return false;
     }
 
-    // Resolve newValue.
     var newValueValue = invoke.getSecondNonReceiverArgument();
     if (!isNewValueValid(resolvedUpdater, newValueValue, invoke)) {
       return false;
@@ -362,81 +237,73 @@
     reportInfo(
         appView,
         new Event.CanOptimize(invoke, resolvedUpdater.isNullable, resolvedHolder.isNullable));
-    rewriteGetAndSet(
-        code,
-        it,
-        methodProcessor,
-        methodProcessingContext,
-        position,
-        resolvedUpdater,
-        resolvedHolder,
-        info,
-        updaterValue,
-        holderValue,
-        newValueValue,
-        outValue);
+    rewriteGetAndSet(context, invoke, resolvedUpdater, resolvedHolder, newValueValue);
     return true;
   }
 
-  private ResolvedUpdater resolveUpdater(
-      IRCode code,
-      Map<DexField, AtomicFieldUpdaterInfo> atomicUpdaterFields,
-      Value updaterValue,
-      InvokeMethod invokeForLogging) {
+  /** Returns null if the updater cannot be resolved. */
+  private ResolvedUpdater resolveUpdater(OptimizationContext context, InvokeVirtual invoke) {
+    var updaterValue = invoke.getReceiver();
     var unusedUpdaterMightBeNull = updaterValue.getType().isNullable();
     DexField updaterField;
     var updaterAbstractValue =
-        updaterValue.getAbstractValue(appView, code.context()).removeNullOrAbstractValue();
+        updaterValue.getAbstractValue(appView, context.code.context()).removeNullOrAbstractValue();
     if (updaterAbstractValue.isSingleFieldValue()) {
       updaterField = updaterAbstractValue.asSingleFieldValue().getField();
     } else {
       reportInfo(
           appView,
-          new Event.CannotOptimize(invokeForLogging),
+          new Event.CannotOptimize(invoke),
           new Reason.StaticallyUnclearUpdater(updaterAbstractValue));
       return null;
     }
-    var updaterInfo = atomicUpdaterFields.get(updaterField);
+    var updaterInfo = context.instrumentations.get(updaterField);
     if (updaterInfo == null) {
-      reportInfo(
-          appView, new Event.CannotOptimize(invokeForLogging), Reason.UPDATER_NOT_INSTRUMENTED);
+      reportInfo(appView, new Event.CannotOptimize(invoke), Reason.UPDATER_NOT_INSTRUMENTED);
       return null;
     }
     // TODO(b/453628974): stop assuming non-null for all updaters.
-    return new ResolvedUpdater(false, updaterInfo);
+    return new ResolvedUpdater(false, updaterInfo, updaterValue);
   }
 
   private static class ResolvedUpdater {
 
     final boolean isNullable;
     final AtomicFieldUpdaterInfo updaterFieldInfo;
+    final Value value;
 
-    private ResolvedUpdater(boolean isNullable, AtomicFieldUpdaterInfo updaterFieldInfo) {
+    private ResolvedUpdater(
+        boolean isNullable, AtomicFieldUpdaterInfo updaterFieldInfo, Value value) {
       this.isNullable = isNullable;
       this.updaterFieldInfo = updaterFieldInfo;
+      this.value = value;
     }
   }
 
-  private ResolvedHolder resolveHolder(
-      Value holderValue, DexType expectedHolder, InvokeMethod invokeForLogging) {
+  /** Returns null if the holder cannot be resolved. */
+  private ResolvedHolder resolveHolder(InvokeVirtual invoke, DexType expectedHolder) {
+    Value holderValue = invoke.getFirstNonReceiverArgument();
     TypeElement holderType = holderValue.getType();
     TypeElement expectedHolderType = expectedHolder.toTypeElement(appView);
     if (!holderType.lessThanOrEqual(expectedHolderType, appView)) {
       reportInfo(
           appView,
-          new Event.CannotOptimize(invokeForLogging),
+          new Event.CannotOptimize(invoke),
           new Reason.WrongHolderType(holderType, expectedHolderType));
       return null;
     }
     var isNullable = holderType.isNullable();
-    return new ResolvedHolder(isNullable);
+    return new ResolvedHolder(isNullable, holderValue);
   }
 
   private static class ResolvedHolder {
-    public final boolean isNullable;
 
-    private ResolvedHolder(boolean isNullable) {
+    final boolean isNullable;
+    final Value value;
+
+    private ResolvedHolder(boolean isNullable, Value value) {
       this.isNullable = isNullable;
+      this.value = value;
     }
   }
 
@@ -455,46 +322,37 @@
   }
 
   /**
-   * Rewrites a call to {@code updater.compareAndSet(holder, expect, update)} (assumed to be the
-   * last instruction returned by {@code it.next}) into a call {@code
-   * SyntheticUnsafeClass.unsafe.compareAndSwapObject(holder, this.offsetField, expect, update)} and
-   * potentially a null-check on updater.
+   * Rewrites {@code updater.compareAndSet(holder, expect, update)} into {@code
+   * SyntheticUnsafeClass.unsafe.compareAndSwapObject(holder, this.offsetField, expect, update)}.
    */
   private void rewriteCompareAndSet(
-      IRCode code,
-      IRCodeInstructionListIterator it,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      Position position,
+      OptimizationContext context,
+      InvokeVirtual invoke,
       ResolvedUpdater resolvedUpdater,
       ResolvedHolder resolvedHolder,
-      AtomicFieldUpdaterInstrumentorInfo info,
-      Value updaterValue,
-      Value holderValue,
       Value expectValue,
-      Value updateValue,
-      Value outValue) {
+      Value updateValue) {
+    var position = invoke.getPosition();
     var instructions = new ArrayList<Instruction>(4);
 
     if (resolvedUpdater.isNullable) {
-      instructions.add(createNullCheck(code, position, updaterValue));
+      instructions.add(createNullCheck(context, position, resolvedUpdater.value));
     }
 
     if (resolvedHolder.isNullable) {
       instructions.add(
-          createNullCheckWithClassCastException(
-              methodProcessor, methodProcessingContext, position, holderValue));
+          createNullCheckWithClassCastException(context, position, resolvedHolder.value));
     }
 
-    Instruction unsafeInstance = createUnsafeGet(code, position, info.getUnsafeInstanceField());
+    Instruction unsafeInstance = createUnsafeGet(context, position);
     instructions.add(unsafeInstance);
 
     Instruction offset =
-        createOffsetGet(code, position, resolvedUpdater.updaterFieldInfo.offsetField);
+        createOffsetGet(context, position, resolvedUpdater.updaterFieldInfo.offsetField);
     instructions.add(offset);
 
     // Add instructions BEFORE the compareAndSet instruction.
-    insertInstructionsBeforeCurrentInstruction(it, instructions);
+    insertInstructionsBeforeCurrentInstruction(context.it, instructions);
 
     // Call underlying unsafe method.
     DexMethod unsafeCompareAndSetMethod =
@@ -510,58 +368,47 @@
     Instruction unsafeCompareAndSet =
         new InvokeVirtual(
             unsafeCompareAndSetMethod,
-            outValue,
+            invoke.outValue(),
             ImmutableList.of(
                 unsafeInstance.outValue(),
-                holderValue,
+                resolvedHolder.value,
                 offset.outValue(),
                 expectValue,
                 updateValue));
     unsafeCompareAndSet.setPosition(position);
-    it.replaceCurrentInstruction(unsafeCompareAndSet);
+    context.it.replaceCurrentInstruction(unsafeCompareAndSet);
   }
 
   /**
-   * Rewrites a call to {@code updater.get(holder)} (assumed to be the last instruction returned by
-   * {@code it.next}) into a call {@code SyntheticUnsafeClass.unsafe.getReferenceVolatile(holder,
-   * this.offset)} and potentially a null-check on updater.
+   * Rewrites {@code updater.get(holder)} into {@code
+   * SyntheticUnsafeClass.unsafe.getReferenceVolatile(holder, this.offset)}.
    */
   private void rewriteGet(
-      IRCode code,
-      IRCodeInstructionListIterator it,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      Position position,
+      OptimizationContext context,
+      InvokeVirtual invoke,
       ResolvedUpdater resolvedUpdater,
-      ResolvedHolder resolvedHolder,
-      AtomicFieldUpdaterInstrumentorInfo info,
-      Value updaterValue,
-      Value holderValue,
-      Value outValue) {
+      ResolvedHolder resolvedHolder) {
+    var position = invoke.getPosition();
     var instructions = new ArrayList<Instruction>(4);
 
-    // Null-check for updater.
     if (resolvedUpdater.isNullable) {
-      instructions.add(createNullCheck(code, position, updaterValue));
+      instructions.add(createNullCheck(context, position, resolvedUpdater.value));
     }
 
     if (resolvedHolder.isNullable) {
       instructions.add(
-          createNullCheckWithClassCastException(
-              methodProcessor, methodProcessingContext, position, holderValue));
+          createNullCheckWithClassCastException(context, position, resolvedHolder.value));
     }
 
-    // Get unsafe instance.
-    Instruction unsafeInstance = createUnsafeGet(code, position, info.getUnsafeInstanceField());
+    Instruction unsafeInstance = createUnsafeGet(context, position);
     instructions.add(unsafeInstance);
 
-    // Get offset field.
     Instruction offset =
-        createOffsetGet(code, position, resolvedUpdater.updaterFieldInfo.offsetField);
+        createOffsetGet(context, position, resolvedUpdater.updaterFieldInfo.offsetField);
     instructions.add(offset);
 
     // Add instructions BEFORE the get instruction.
-    insertInstructionsBeforeCurrentInstruction(it, instructions);
+    insertInstructionsBeforeCurrentInstruction(context.it, instructions);
 
     // Call underlying unsafe method.
     DexMethod unsafeGetMethod =
@@ -573,55 +420,43 @@
     Instruction unsafeGet =
         new InvokeVirtual(
             unsafeGetMethod,
-            outValue,
-            ImmutableList.of(unsafeInstance.outValue(), holderValue, offset.outValue()));
+            invoke.outValue(),
+            ImmutableList.of(unsafeInstance.outValue(), resolvedHolder.value, offset.outValue()));
     unsafeGet.setPosition(position);
-    it.replaceCurrentInstruction(unsafeGet);
+    context.it.replaceCurrentInstruction(unsafeGet);
   }
 
   /**
-   * Rewrites a call to {@code updater.set(holder, newValue)} (assumed to be the last instruction
-   * returned by {@code it.next}) into a call {@code
-   * SyntheticUnsafeClass.unsafe.putReferenceVolatile(holder, this.offset, newValue)} and
-   * potentially a null-check on updater.
+   * Rewrites {@code updater.set(holder, newValue)} into {@code
+   * SyntheticUnsafeClass.unsafe.putReferenceVolatile(holder, this.offset, newValue)}.
    */
   private void rewriteSet(
-      IRCode code,
-      IRCodeInstructionListIterator it,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      Position position,
+      OptimizationContext context,
+      InvokeVirtual invoke,
       ResolvedUpdater resolvedUpdater,
       ResolvedHolder resolvedHolder,
-      AtomicFieldUpdaterInstrumentorInfo info,
-      Value updaterValue,
-      Value holderValue,
-      Value newValueValue,
-      Value outValue) {
+      Value newValueValue) {
+    var position = invoke.getPosition();
     var instructions = new ArrayList<Instruction>(4);
 
-    // Null-check for updater.
     if (resolvedUpdater.isNullable) {
-      instructions.add(createNullCheck(code, position, updaterValue));
+      instructions.add(createNullCheck(context, position, resolvedUpdater.value));
     }
 
     if (resolvedHolder.isNullable) {
       instructions.add(
-          createNullCheckWithClassCastException(
-              methodProcessor, methodProcessingContext, position, holderValue));
+          createNullCheckWithClassCastException(context, position, resolvedHolder.value));
     }
 
-    // Get unsafe instance.
-    Instruction unsafeInstance = createUnsafeGet(code, position, info.getUnsafeInstanceField());
+    Instruction unsafeInstance = createUnsafeGet(context, position);
     instructions.add(unsafeInstance);
 
-    // Get offset field.
     Instruction offset =
-        createOffsetGet(code, position, resolvedUpdater.updaterFieldInfo.offsetField);
+        createOffsetGet(context, position, resolvedUpdater.updaterFieldInfo.offsetField);
     instructions.add(offset);
 
     // Add instructions BEFORE the get instruction.
-    insertInstructionsBeforeCurrentInstruction(it, instructions);
+    insertInstructionsBeforeCurrentInstruction(context.it, instructions);
 
     // Call underlying unsafe method.
     DexMethod unsafeSetMethod =
@@ -636,63 +471,51 @@
     Instruction unsafeSet =
         new InvokeVirtual(
             unsafeSetMethod,
-            outValue,
+            invoke.outValue(),
             ImmutableList.of(
-                unsafeInstance.outValue(), holderValue, offset.outValue(), newValueValue));
+                unsafeInstance.outValue(), resolvedHolder.value, offset.outValue(), newValueValue));
     unsafeSet.setPosition(position);
-    it.replaceCurrentInstruction(unsafeSet);
+    context.it.replaceCurrentInstruction(unsafeSet);
   }
 
   /**
-   * Rewrites a call to {@code updater.getAndSet(holder, newValue)} (assumed to be the last
-   * instruction returned by {@code it.next}) into a call {@code
-   * SyntheticUnsafeClass.unsafe.getAndSetObject(holder, this.offset, newValue)} and potentially a
-   * null-check on updater.
+   * Rewrites {@code updater.getAndSet(holder, newValue)} into {@code
+   * SyntheticUnsafeClass.unsafe.getAndSetObject(holder, this.offset, newValue)}.
    */
   private void rewriteGetAndSet(
-      IRCode code,
-      IRCodeInstructionListIterator it,
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      Position position,
+      OptimizationContext context,
+      InvokeVirtual invoke,
       ResolvedUpdater resolvedUpdater,
       ResolvedHolder resolvedHolder,
-      AtomicFieldUpdaterInstrumentorInfo info,
-      Value updaterValue,
-      Value holderValue,
-      Value newValueValue,
-      Value outValue) {
-    boolean isGetAndSetDefined =
-        !appView.options().isGeneratingDex()
-            || appView.options().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
+      Value newValueValue) {
+    var position = invoke.getPosition();
 
     var instructions = new ArrayList<Instruction>(4);
 
-    // Null-check for updater.
     if (resolvedUpdater.isNullable) {
-      instructions.add(createNullCheck(code, position, updaterValue));
+      instructions.add(createNullCheck(context, position, resolvedUpdater.value));
     }
 
     if (resolvedHolder.isNullable) {
       instructions.add(
-          createNullCheckWithClassCastException(
-              methodProcessor, methodProcessingContext, position, holderValue));
+          createNullCheckWithClassCastException(context, position, resolvedHolder.value));
     }
 
-    // Get unsafe instance.
-    Instruction unsafeInstance = createUnsafeGet(code, position, info.getUnsafeInstanceField());
+    Instruction unsafeInstance = createUnsafeGet(context, position);
     instructions.add(unsafeInstance);
 
-    // Get offset field.
     Instruction offset =
-        createOffsetGet(code, position, resolvedUpdater.updaterFieldInfo.offsetField);
+        createOffsetGet(context, position, resolvedUpdater.updaterFieldInfo.offsetField);
     instructions.add(offset);
 
     // Add instructions BEFORE the get instruction.
-    insertInstructionsBeforeCurrentInstruction(it, instructions);
+    insertInstructionsBeforeCurrentInstruction(context.it, instructions);
 
-    // Call underlying unsafe method.
+    // Call underlying unsafe method directly or backport if necessary.
     Instruction getAndSet;
+    boolean isGetAndSetDefined =
+        !appView.options().isGeneratingDex()
+            || appView.options().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
     if (isGetAndSetDefined) {
       DexMethod unsafeGetAndSetMethod =
           dexItemFactory.createMethod(
@@ -706,67 +529,72 @@
       getAndSet =
           new InvokeVirtual(
               unsafeGetAndSetMethod,
-              outValue,
+              invoke.outValue(),
               ImmutableList.of(
-                  unsafeInstance.outValue(), holderValue, offset.outValue(), newValueValue));
+                  unsafeInstance.outValue(),
+                  resolvedHolder.value,
+                  offset.outValue(),
+                  newValueValue));
     } else {
-      DexMethod backportedGetAndSet = info.getGetAndSetMethod();
+      DexMethod backportedGetAndSet = context.info.getGetAndSetMethod();
       getAndSet =
           new InvokeStatic(
               backportedGetAndSet,
-              outValue,
+              invoke.outValue(),
               ImmutableList.of(
-                  unsafeInstance.outValue(), holderValue, offset.outValue(), newValueValue));
+                  unsafeInstance.outValue(),
+                  resolvedHolder.value,
+                  offset.outValue(),
+                  newValueValue));
       var profiling = ProfileCollectionAdditions.create(appView);
       profiling.applyIfContextIsInProfile(
-          code.context().getReference(), builder -> builder.addMethodRule(backportedGetAndSet));
+          context.code.context().getReference(),
+          builder -> builder.addMethodRule(backportedGetAndSet));
       profiling.commit(appView);
     }
     getAndSet.setPosition(position);
-    it.replaceCurrentInstruction(getAndSet);
+    context.it.replaceCurrentInstruction(getAndSet);
   }
 
-  private Instruction createOffsetGet(IRCode code, Position position, DexField offsetField) {
+  private Instruction createOffsetGet(
+      OptimizationContext context, Position position, DexField offsetField) {
     assert offsetField.type.isIdenticalTo(dexItemFactory.longType);
     Instruction offset =
         new StaticGet(
-            code.createValue(dexItemFactory.longType.toTypeElement(appView)), offsetField);
+            context.code.createValue(dexItemFactory.longType.toTypeElement(appView)), offsetField);
     offset.setPosition(position);
     return offset;
   }
 
-  private InvokeVirtual createNullCheck(IRCode code, Position position, Value value) {
+  private InvokeVirtual createNullCheck(
+      OptimizationContext context, Position position, Value value) {
     var nullCheck =
         new InvokeVirtual(
             dexItemFactory.objectMembers.getClass,
-            code.createValue(dexItemFactory.classType.toTypeElement(appView)),
+            context.code.createValue(dexItemFactory.classType.toTypeElement(appView)),
             ImmutableList.of(value));
     nullCheck.setPosition(position);
     return nullCheck;
   }
 
   private InvokeStatic createNullCheckWithClassCastException(
-      MethodProcessor methodProcessor,
-      MethodProcessingContext methodProcessingContext,
-      Position position,
-      Value value) {
+      OptimizationContext context, Position position, Value value) {
     var optimizations =
         UtilityMethodsForCodeOptimizations.synthesizeThrowClassCastExceptionIfNullMethod(
-            appView, methodProcessor.getEventConsumer(), methodProcessingContext);
-    optimizations.optimize(methodProcessor);
+            appView, context.methodProcessor.getEventConsumer(), context.methodProcessingContext);
+    optimizations.optimize(context.methodProcessor);
     InvokeStatic invokeStatic =
         new InvokeStatic(optimizations.getMethod().getReference(), null, ImmutableList.of(value));
     invokeStatic.setPosition(position);
     return invokeStatic;
   }
 
-  private Instruction createUnsafeGet(
-      IRCode code, Position position, DexField unsafeInstanceField) {
-    assert unsafeInstanceField.type.isIdenticalTo(dexItemFactory.unsafeType);
+  private Instruction createUnsafeGet(OptimizationContext context, Position position) {
+    assert context.info.getUnsafeInstanceField().type.isIdenticalTo(dexItemFactory.unsafeType);
     Instruction unsafeInstance =
         new StaticGet(
-            code.createValue(dexItemFactory.unsafeType.toTypeElement(appView)),
-            unsafeInstanceField);
+            context.code.createValue(dexItemFactory.unsafeType.toTypeElement(appView)),
+            context.info.getUnsafeInstanceField());
     unsafeInstance.setPosition(position);
     return unsafeInstance;
   }
@@ -780,14 +608,14 @@
   }
 
   /**
-   * Stores static creation information around a atomic field updater.
+   * Creation information about an AtomicReferenceFieldUpdater.
    *
    * <blockquote>
    *
    * <pre>
    * class holder {
-   *   volatile reflectedFieldType someField;
-   *   static final AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(holder.class, reflectedFieldType, "someField");
+   *   volatile reflectedFieldType reflectedField;
+   *   static final AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(holder.class, reflectedFieldType, "reflectedField");
    *   public static final long offsetField = ..
    * }
    * </pre>
@@ -807,4 +635,29 @@
       this.offsetField = offsetField;
     }
   }
+
+  private static class OptimizationContext {
+
+    final MethodProcessor methodProcessor;
+    final MethodProcessingContext methodProcessingContext;
+    final IRCode code;
+    final IRCodeInstructionListIterator it;
+    final AtomicFieldUpdaterInstrumentorInfo info;
+    final Map<DexField, AtomicFieldUpdaterInfo> instrumentations;
+
+    public OptimizationContext(
+        MethodProcessor methodProcessor,
+        MethodProcessingContext methodProcessingContext,
+        IRCode code,
+        IRCodeInstructionListIterator it,
+        AtomicFieldUpdaterInstrumentorInfo info,
+        Map<DexField, AtomicFieldUpdaterInfo> instrumentations) {
+      this.methodProcessor = methodProcessor;
+      this.methodProcessingContext = methodProcessingContext;
+      this.code = code;
+      this.it = it;
+      this.info = info;
+      this.instrumentations = instrumentations;
+    }
+  }
 }