/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

public class Main {
    public static void main(String[] args) {
        // Since the tests will share a value, we have the condition that the value starts
        // and ends as 0 before and after each test.

        // Int tests
        $noinline$testGetAndAdd_Int();
        $noinline$testGetAndSet_Int();
        $noinline$testGetAndBitwiseAnd_Int();
        $noinline$testGetAndBitwiseOr_Int();
        $noinline$testGetAndBitwiseXor_Int();

        // Long tests
        $noinline$testGetAndAdd_Long();
        $noinline$testGetAndSet_Long();
        $noinline$testGetAndBitwiseAnd_Long();
        $noinline$testGetAndBitwiseOr_Long();
        $noinline$testGetAndBitwiseXor_Long();

        // Float tests
        $noinline$testGetAndAdd_Float();
        $noinline$testGetAndSet_Float();

        // Double tests
        $noinline$testGetAndAdd_Double();
        $noinline$testGetAndSet_Double();
    }

    private static void $noinline$testGetAndAdd_Int() {
        Main m = new Main();
        // 0 + 100 = 100
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
        m.$noinline$getAndAdd_Int(100);
        $noinline$assertIntEquals(100, (int) INT_VALUE.get(m));

        // 100 - 100 = 0
        m.$noinline$getAndAdd_Int(-100);
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
    }

    private static void $noinline$testGetAndSet_Int() {
        Main m = new Main();
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
        m.$noinline$getAndSet_Int(100);
        $noinline$assertIntEquals(100, (int) INT_VALUE.get(m));

        m.$noinline$getAndSet_Int(-100);
        $noinline$assertIntEquals(-100, (int) INT_VALUE.get(m));

        m.$noinline$getAndSet_Int(0);
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
    }

    private static void $noinline$testGetAndBitwiseAnd_Int() {
        Main m = new Main();
        // 0 AND X = 0
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
        m.$noinline$getAndBitwiseAnd_Int(100);
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));

        // 10101010 AND
        // 11001100 =
        // 10001000
        m.$noinline$getAndSet_Int(0b10101010);
        m.$noinline$getAndBitwiseAnd_Int(0b11001100);
        $noinline$assertIntEquals(0b10001000, (int) INT_VALUE.get(m));

        // 10001000 AND
        // 11111111 =
        // 10001000
        m.$noinline$getAndBitwiseAnd_Int(0b11111111);
        $noinline$assertIntEquals(0b10001000, (int) INT_VALUE.get(m));

        // 10001000 AND
        // 01110111 =
        // 0
        m.$noinline$getAndBitwiseAnd_Int(0b01110111);
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
    }

    private static void $noinline$testGetAndBitwiseOr_Int() {
        Main m = new Main();

        // 0 OR X = X
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
        m.$noinline$getAndBitwiseOr_Int(0b10101010);
        $noinline$assertIntEquals(0b10101010, (int) INT_VALUE.get(m));

        // 10101010 OR
        // 01010101 =
        // 11111111
        m.$noinline$getAndBitwiseOr_Int(0b01010101);
        $noinline$assertIntEquals(0b11111111, (int) INT_VALUE.get(m));

        // 11111111 OR
        // 0 =
        // 11111111
        m.$noinline$getAndBitwiseOr_Int(0);
        $noinline$assertIntEquals(0b11111111, (int) INT_VALUE.get(m));

        // Set to 0 due to precondition. See comment in main.
        m.$noinline$getAndSet_Int(0);
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
    }

    private static void $noinline$testGetAndBitwiseXor_Int() {
        Main m = new Main();

        // 0 XOR X = X
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
        m.$noinline$getAndBitwiseXor_Int(0b10101010);
        $noinline$assertIntEquals(0b10101010, (int) INT_VALUE.get(m));

        // 10101010 XOR
        // 01010101 =
        // 11111111
        m.$noinline$getAndBitwiseXor_Int(0b01010101);
        $noinline$assertIntEquals(0b11111111, (int) INT_VALUE.get(m));

        // 11111111 XOR
        // 01010101 =
        // 10101010
        m.$noinline$getAndBitwiseXor_Int(0b01010101);
        $noinline$assertIntEquals(0b10101010, (int) INT_VALUE.get(m));

        // X XOR X = 0
        m.$noinline$getAndBitwiseXor_Int(0b10101010);
        $noinline$assertIntEquals(0, (int) INT_VALUE.get(m));
    }

    private static void $noinline$testGetAndAdd_Long() {
        Main m = new Main();
        // 0 + 100 = 100
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
        m.$noinline$getAndAdd_Long(100);
        $noinline$assertLongEquals(100L, (long) LONG_VALUE.get(m));

        // 100 - 100 = 0
        m.$noinline$getAndAdd_Long(-100);
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
    }

    private static void $noinline$testGetAndSet_Long() {
        Main m = new Main();
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
        m.$noinline$getAndSet_Long(100);
        $noinline$assertLongEquals(100L, (long) LONG_VALUE.get(m));

        m.$noinline$getAndSet_Long(-100);
        $noinline$assertLongEquals(-100L, (long) LONG_VALUE.get(m));

        m.$noinline$getAndSet_Long(0);
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
    }

    private static void $noinline$testGetAndBitwiseAnd_Long() {
        Main m = new Main();
        // 0 AND X = 0
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
        m.$noinline$getAndBitwiseAnd_Long(100);
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));

        // 10101010 AND
        // 11001100 =
        // 10001000
        m.$noinline$getAndSet_Long(0b10101010);
        m.$noinline$getAndBitwiseAnd_Long(0b11001100);
        $noinline$assertLongEquals(0b10001000L, (long) LONG_VALUE.get(m));

        // 10001000 AND
        // 11111111 =
        // 10001000
        m.$noinline$getAndBitwiseAnd_Long(0b11111111);
        $noinline$assertLongEquals(0b10001000L, (long) LONG_VALUE.get(m));

        // 10001000 AND
        // 01110111 =
        // 0
        m.$noinline$getAndBitwiseAnd_Long(0b01110111);
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
    }

    private static void $noinline$testGetAndBitwiseOr_Long() {
        Main m = new Main();

        // 0 OR X = X
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
        m.$noinline$getAndBitwiseOr_Long(0b10101010);
        $noinline$assertLongEquals(0b10101010L, (long) LONG_VALUE.get(m));

        // 10101010 OR
        // 01010101 =
        // 11111111
        m.$noinline$getAndBitwiseOr_Long(0b01010101);
        $noinline$assertLongEquals(0b11111111L, (long) LONG_VALUE.get(m));

        // 11111111 OR
        // 0 =
        // 11111111
        m.$noinline$getAndBitwiseOr_Long(0);
        $noinline$assertLongEquals(0b11111111L, (long) LONG_VALUE.get(m));

        // Set to 0 due to precondition. See comment in main.
        m.$noinline$getAndSet_Long(0);
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
    }

    private static void $noinline$testGetAndBitwiseXor_Long() {
        Main m = new Main();

        // 0 XOR X = X
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
        m.$noinline$getAndBitwiseXor_Long(0b10101010);
        $noinline$assertLongEquals(0b10101010L, (long) LONG_VALUE.get(m));

        // 10101010 XOR
        // 01010101 =
        // 11111111
        m.$noinline$getAndBitwiseXor_Long(0b01010101);
        $noinline$assertLongEquals(0b11111111L, (long) LONG_VALUE.get(m));

        // 11111111 XOR
        // 01010101 =
        // 10101010
        m.$noinline$getAndBitwiseXor_Long(0b01010101);
        $noinline$assertLongEquals(0b10101010L, (long) LONG_VALUE.get(m));

        // X XOR X = 0
        m.$noinline$getAndBitwiseXor_Long(0b10101010);
        $noinline$assertLongEquals(0L, (long) LONG_VALUE.get(m));
    }

    private static void $noinline$testGetAndAdd_Float() {
        Main m = new Main();
        // 0 + 100 = 100
        $noinline$assertFloatEquals(0.0f, (float) FLOAT_VALUE.get(m));
        m.$noinline$getAndAdd_Float(100.0f);
        $noinline$assertFloatEquals(100.0f, (float) FLOAT_VALUE.get(m));

        // 100 - 100 = 0
        m.$noinline$getAndAdd_Float(-100.0f);
        $noinline$assertFloatEquals(0.0f, (float) FLOAT_VALUE.get(m));
    }

    private static void $noinline$testGetAndSet_Float() {
        Main m = new Main();
        $noinline$assertFloatEquals(0.0f, (float) FLOAT_VALUE.get(m));
        m.$noinline$getAndSet_Float(100.0f);
        $noinline$assertFloatEquals(100.0f, (float) FLOAT_VALUE.get(m));

        m.$noinline$getAndSet_Float(-100.0f);
        $noinline$assertFloatEquals(-100.0f, (float) FLOAT_VALUE.get(m));

        m.$noinline$getAndSet_Float(0.0f);
        $noinline$assertFloatEquals(0.0f, (float) FLOAT_VALUE.get(m));
    }

    private static void $noinline$testGetAndAdd_Double() {
        Main m = new Main();
        // 0 + 100 = 100
        $noinline$assertDoubleEquals(0.0d, (double) DOUBLE_VALUE.get(m));
        m.$noinline$getAndAdd_Double(100.0d);
        $noinline$assertDoubleEquals(100.0d, (double) DOUBLE_VALUE.get(m));

        // 100 - 100 = 0
        m.$noinline$getAndAdd_Double(-100.0d);
        $noinline$assertDoubleEquals(0.0d, (double) DOUBLE_VALUE.get(m));
    }

    private static void $noinline$testGetAndSet_Double() {
        Main m = new Main();
        $noinline$assertDoubleEquals(0.0d, (double) DOUBLE_VALUE.get(m));
        m.$noinline$getAndSet_Double(100.0d);
        $noinline$assertDoubleEquals(100.0d, (double) DOUBLE_VALUE.get(m));

        m.$noinline$getAndSet_Double(-100.0d);
        $noinline$assertDoubleEquals(-100.0d, (double) DOUBLE_VALUE.get(m));

        m.$noinline$getAndSet_Double(0.0d);
        $noinline$assertDoubleEquals(0.0d, (double) DOUBLE_VALUE.get(m));
    }

    // VarHandles
    private volatile int int_value = 0;
    private static final VarHandle INT_VALUE;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            INT_VALUE = l.findVarHandle(Main.class, "int_value", int.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    private volatile long long_value = 0L;
    private static final VarHandle LONG_VALUE;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            LONG_VALUE = l.findVarHandle(Main.class, "long_value", long.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    private volatile float float_value = 0.0f;
    private static final VarHandle FLOAT_VALUE;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            FLOAT_VALUE = l.findVarHandle(Main.class, "float_value", float.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    private volatile double double_value = 0.0d;
    private static final VarHandle DOUBLE_VALUE;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            DOUBLE_VALUE = l.findVarHandle(Main.class, "double_value", double.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    // Check that we successfully intrinsify intrinsics (e.g. getAndAdd) by checking that there's no
    // call to the runtime.

    /// CHECK-START-{X86,X86_64}: void Main.$noinline$getAndAdd_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndAdd_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndAdd_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndAdd_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndAdd_Int(int value) {
        INT_VALUE.getAndAdd(this, value);
    }

    /// CHECK-START-{X86,X86_64}: void Main.$noinline$getAndSet_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndSet_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndSet_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndSet_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndSet_Int(int value) {
        INT_VALUE.getAndSet(this, value);
    }

    /// CHECK-START-{X86,X86_64}: void Main.$noinline$getAndBitwiseAnd_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseAnd_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseAnd_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndBitwiseAnd_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndBitwiseAnd_Int(int value) {
        INT_VALUE.getAndBitwiseAnd(this, value);
    }

    /// CHECK-START-{X86,X86_64}: void Main.$noinline$getAndBitwiseOr_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseOr_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseOr_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndBitwiseOr_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndBitwiseOr_Int(int value) {
        INT_VALUE.getAndBitwiseOr(this, value);
    }

    /// CHECK-START-{X86,X86_64}: void Main.$noinline$getAndBitwiseXor_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseXor_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseXor_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndBitwiseXor_Int(int) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndBitwiseXor_Int(int value) {
        INT_VALUE.getAndBitwiseXor(this, value);
    }

    private static void $noinline$assertIntEquals(int expected, int result) {
        if (expected != result) {
            throw new Error("Expected: " + expected + ", found: " + result);
        }
    }

    // Note that the Long ones do a call for X86.
    // TODO(solanes): Add this support.

    /// CHECK-START-X86: void Main.$noinline$getAndAdd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK:     call
    /// CHECK:     ReturnVoid

    /// CHECK-START-X86_64: void Main.$noinline$getAndAdd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndAdd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndAdd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndAdd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndAdd_Long(long value) {
        LONG_VALUE.getAndAdd(this, value);
    }

    /// CHECK-START-X86: void Main.$noinline$getAndSet_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK:     call
    /// CHECK:     ReturnVoid

    /// CHECK-START-X86_64: void Main.$noinline$getAndSet_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndSet_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndSet_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndSet_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndSet_Long(long value) {
        LONG_VALUE.getAndSet(this, value);
    }

    /// CHECK-START-X86: void Main.$noinline$getAndBitwiseAnd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK:     call
    /// CHECK:     ReturnVoid

    /// CHECK-START-X86_64: void Main.$noinline$getAndBitwiseAnd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseAnd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseAnd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndBitwiseAnd_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseAnd
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndBitwiseAnd_Long(long value) {
        LONG_VALUE.getAndBitwiseAnd(this, value);
    }

    /// CHECK-START-X86: void Main.$noinline$getAndBitwiseOr_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK:     call
    /// CHECK:     ReturnVoid

    /// CHECK-START-X86_64: void Main.$noinline$getAndBitwiseOr_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseOr_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseOr_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndBitwiseOr_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseOr
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndBitwiseOr_Long(long value) {
        LONG_VALUE.getAndBitwiseOr(this, value);
    }

    /// CHECK-START-X86: void Main.$noinline$getAndBitwiseXor_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK:     call
    /// CHECK:     ReturnVoid

    /// CHECK-START-X86_64: void Main.$noinline$getAndBitwiseXor_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseXor_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndBitwiseXor_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndBitwiseXor_Long(long) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndBitwiseXor
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndBitwiseXor_Long(long value) {
        LONG_VALUE.getAndBitwiseXor(this, value);
    }

    private static void $noinline$assertLongEquals(long expected, long result) {
        if (expected != result) {
            throw new Error("Expected: " + expected + ", found: " + result);
        }
    }

    /// CHECK-START-{X86,X86_64}: void Main.$noinline$getAndAdd_Float(float) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndAdd_Float(float) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndAdd_Float(float) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndAdd_Float(float) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndAdd_Float(float value) {
        FLOAT_VALUE.getAndAdd(this, value);
    }

    /// CHECK-START-{X86,X86_64}: void Main.$noinline$getAndSet_Float(float) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndSet_Float(float) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndSet_Float(float) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndSet_Float(float) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndSet_Float(float value) {
        FLOAT_VALUE.getAndSet(this, value);
    }

    private static void $noinline$assertFloatEquals(float expected, float result) {
        if (expected != result) {
            throw new Error("Expected: " + expected + ", found: " + result);
        }
    }

    // Note that the Double ones do a call for X86.
    // TODO(solanes): Add this support.

    /// CHECK-START-X86: void Main.$noinline$getAndAdd_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK:     call
    /// CHECK:     ReturnVoid

    /// CHECK-START-X86_64: void Main.$noinline$getAndAdd_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndAdd_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndAdd_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndAdd_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndAdd
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndAdd_Double(double value) {
        DOUBLE_VALUE.getAndAdd(this, value);
    }

    /// CHECK-START-X86: void Main.$noinline$getAndSet_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK:     call
    /// CHECK:     ReturnVoid

    /// CHECK-START-X86_64: void Main.$noinline$getAndSet_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: call
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndSet_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: blx
    /// CHECK:     ReturnVoid

    /// CHECK-START-ARM64: void Main.$noinline$getAndSet_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: blr
    /// CHECK:     ReturnVoid

    /// CHECK-START-RISCV64: void Main.$noinline$getAndSet_Double(double) disassembly (after)
    /// CHECK:     InvokePolymorphic intrinsic:VarHandleGetAndSet
    /// CHECK-NOT: jalr
    /// CHECK:     ReturnVoid
    private void $noinline$getAndSet_Double(double value) {
        DOUBLE_VALUE.getAndSet(this, value);
    }

    private static void $noinline$assertDoubleEquals(double expected, double result) {
        if (expected != result) {
            throw new Error("Expected: " + expected + ", found: " + result);
        }
    }
}
