Add backporting of AtomicReferenceFieldUpdater.compareAndSet
Bug: 211646483
Change-Id: Ia25ebc48b432377bb8df47a487a2951e064f7f4b
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index a3fc0e8..ee7f8e1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1091,27 +1091,44 @@
}
private void initializeAndroidSv2MethodProviders(DexItemFactory factory) {
- DexString name;
- DexProto proto;
- DexMethod method;
// sun.misc.Unsafe
+ {
+ // compareAndSwapObject(Object receiver, long offset, Object expect, Object update)
+ DexType type = factory.unsafeType;
+ DexString name = factory.createString("compareAndSwapObject");
+ DexProto proto =
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.longType,
+ factory.objectType,
+ factory.objectType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ new StatifyingMethodWithForwardingGenerator(
+ method,
+ BackportedMethods::UnsafeMethods_compareAndSwapObject,
+ "compareAndSwapObject",
+ type));
+ }
- // compareAndSwapObject(Object receiver, long offset, Object expect, Object update)
- name = factory.createString("compareAndSwapObject");
- proto =
- factory.createProto(
- factory.booleanType,
- factory.objectType,
- factory.longType,
- factory.objectType,
- factory.objectType);
- method = factory.createMethod(factory.unsafeType, proto, name);
- addProvider(
- new StatifyingMethodWithForwardingGenerator(
- method,
- BackportedMethods::UnsafeMethods_compareAndSwapObject,
- "compareAndSwapObject",
- factory.unsafeType));
+ // java.util.concurrent.atomic.AtomicReferenceFieldUpdater
+ {
+ // compareAndSet(Object object, Object expect, Object update)
+ DexType type =
+ factory.createType("Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;");
+ DexString name = factory.createString("compareAndSet");
+ DexProto proto =
+ factory.createProto(
+ factory.booleanType, factory.objectType, factory.objectType, factory.objectType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ new StatifyingMethodWithForwardingGenerator(
+ method,
+ BackportedMethods::AtomicReferenceFieldUpdaterMethods_compareAndSet,
+ "compareAndSet",
+ type));
+ }
}
private void initializeJava9MethodProviders(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index b934c40..e6b793f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -97,6 +97,7 @@
factory.createSynthesizedType("Ljava/util/OptionalInt;");
factory.createSynthesizedType("Ljava/util/OptionalLong;");
factory.createSynthesizedType("Ljava/util/Set;");
+ factory.createSynthesizedType("Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;");
factory.createSynthesizedType("Ljava/util/function/Consumer;");
factory.createSynthesizedType("Ljava/util/function/DoubleConsumer;");
factory.createSynthesizedType("Ljava/util/function/IntConsumer;");
@@ -114,6 +115,85 @@
factory.createSynthesizedType("[Ljava/util/Map$Entry;");
}
+ public static CfCode AtomicReferenceFieldUpdaterMethods_compareAndSet(
+ InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 4,
+ 4,
+ ImmutableList.of(
+ label0,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1, 2, 3},
+ new FrameType[] {
+ FrameType.initialized(
+ options.itemFactory.createType(
+ "Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;")),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.objectType)
+ }),
+ new ArrayDeque<>(Arrays.asList())),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType(
+ "Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;"),
+ options.itemFactory.createProto(
+ options.itemFactory.booleanType,
+ options.itemFactory.objectType,
+ options.itemFactory.objectType,
+ options.itemFactory.objectType),
+ options.itemFactory.createString("compareAndSet")),
+ false),
+ new CfIf(If.Type.EQ, ValueType.INT, label2),
+ label1,
+ new CfConstNumber(1, ValueType.INT),
+ new CfReturn(ValueType.INT),
+ label2,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1, 2, 3},
+ new FrameType[] {
+ FrameType.initialized(
+ options.itemFactory.createType(
+ "Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;")),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.objectType)
+ }),
+ new ArrayDeque<>(Arrays.asList())),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType(
+ "Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;"),
+ options.itemFactory.createProto(
+ options.itemFactory.objectType, options.itemFactory.objectType),
+ options.itemFactory.createString("get")),
+ false),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label0),
+ label3,
+ new CfConstNumber(0, ValueType.INT),
+ new CfReturn(ValueType.INT),
+ label4),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode BooleanMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
new file mode 100644
index 0000000..1e66d40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2022, 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.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AtomicReferenceFieldUpdaterTest extends AbstractBackportTest {
+
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
+ .withAllApiLevels()
+ .build();
+ }
+
+ public AtomicReferenceFieldUpdaterTest(TestParameters parameters) {
+ super(parameters, AtomicReferenceFieldUpdater.class, Main.class);
+
+ ignoreInvokes("newUpdater");
+
+ // java.util.concurrent.atomic.AtomicReferenceFieldUpdater issue is on API 31, see b/211646483.
+ registerTarget(AndroidApiLevel.Sv2, 3);
+ }
+
+ public static class Main extends MiniAssert {
+ public volatile String field;
+
+ public static void main(String[] args) throws Exception {
+ AtomicReferenceFieldUpdater<Main, String> updater =
+ AtomicReferenceFieldUpdater.newUpdater(Main.class, String.class, "field");
+ Main x = new Main();
+ assertTrue(updater.compareAndSet(x, null, "A"));
+ assertTrue(updater.compareAndSet(x, "A", "B"));
+ assertFalse(updater.compareAndSet(x, "A", "B"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java
index dbe597d..00134b5 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java
@@ -39,7 +39,7 @@
ignoreInvokes("objectFieldOffset");
- // sun.misc.Unsafe issue is on API 31.
+ // sun.misc.Unsafe issue is on API 31, see b/211646483..
registerTarget(AndroidApiLevel.Sv2, 3);
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceFieldUpdaterMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceFieldUpdaterMethods.java
new file mode 100644
index 0000000..b3f658c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceFieldUpdaterMethods.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2022, 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.backports;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+public final class AtomicReferenceFieldUpdaterMethods {
+ // Workaround Android S issue with AtomicReferenceFieldUpdater.compareAndSet (b/211646483).
+ public static boolean compareAndSet(
+ AtomicReferenceFieldUpdater<Object, Object> updater,
+ Object object,
+ Object expect,
+ Object update) {
+ do {
+ if (updater.compareAndSet(object, expect, update)) {
+ return true;
+ }
+ } while (updater.get(object) == expect);
+ return false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index f955ca3..c6e3eea 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -34,6 +34,7 @@
factory.createType("Lcom/android/tools/r8/ir/desugar/backports/BackportedMethods;");
private final List<Class<?>> METHOD_TEMPLATE_CLASSES =
ImmutableList.of(
+ AtomicReferenceFieldUpdaterMethods.class,
BooleanMethods.class,
ByteMethods.class,
CharSequenceMethods.class,