Advice on finding out the cause of test failure of VarHandleTestAccessBoolean on windows x64

Paul Sandoz paul.sandoz at oracle.com
Mon Aug 15 22:01:17 UTC 2016


Hi John,

Thanks.

I managed to boil this down to a reproducible test case just using Unsafe that fails on windows x64 but not on x86.

I think there is sufficient information to log a bug against HotSpot.

It can be reproduced by applying the webrev to jdk9/dev:

  http://cr.openjdk.java.net/~psandoz/jdk9/JDK-8161444-vhs-bitwise-atomics/webrev/

compiling [2] with:

  javac --add-exports java.base/jdk.internal.misc=ALL-UNNAMED VarHandleTestAccessBoolean.java

then running [2] with [1].

The test case runs the following in a loop:

    static void testInstanceField(VarHandleTestAccessBoolean recv) {
        boolean o = UNSAFE.getAndBitwiseAndBoolean(recv, V_OFFSET, false);
        boolean r = UNSAFE.compareAndExchangeBooleanVolatile(recv, V_OFFSET, false, true);
    }

then runs the following once:

    static void testStaticFieldUnsafeBoolean() {
        static_v = true;

        boolean o = UNSAFE.getAndBitwiseAndBoolean(STATIC_V_BASE, STATIC_V_OFFSET, false);
        if (o != true) throw new RuntimeException();

        boolean x = static_v;
        if (x != (boolean)(true & false)) throw new RuntimeException();
    }

where the second test fails. Replace UNSAFE.getAndBitwiseAndBoolean with UNSAFE.getAndBitwiseAndByte and there is no failure:

    static void testStaticFieldUnsafeByte() {
        static_v = true;

        byte bo = UNSAFE.getAndBitwiseAndByte(STATIC_V_BASE, STATIC_V_OFFSET, (byte)0);
        boolean o = bo != 0;
        if (o != true) throw new RuntimeException();

        boolean x = static_v;
        if (x != (boolean)(true & false)) throw new RuntimeException();
    }

I can produce assembler output on request.

Thanks,
Paul.

[1]
#!/bin/sh

echo test 1
java -XX:+UnlockDiagnosticVMOptions \
   --add-exports java.base/jdk.internal.misc=ALL-UNNAMED \
   -XX:-TieredCompilation \
   VarHandleTestAccessBoolean


echo test 2
java -XX:+UnlockDiagnosticVMOptions \
   --add-exports java.base/jdk.internal.misc=ALL-UNNAMED \
   -XX:-TieredCompilation \
   -XX:DisableIntrinsic=_getByteVolatile \
   VarHandleTestAccessBoolean


echo test 3
java -XX:+UnlockDiagnosticVMOptions \
   --add-exports java.base/jdk.internal.misc=ALL-UNNAMED \
   -XX:-TieredCompilation \
   -XX:CompileCommand=dontinline,jdk.internal.misc.Unsafe::getAndBitwiseAndByte \
   VarHandleTestAccessBoolean


echo test 4
java -XX:+UnlockDiagnosticVMOptions \
   --add-exports java.base/jdk.internal.misc=ALL-UNNAMED \
   -Xcomp \
   VarHandleTestAccessBoolean


[2]
import java.lang.reflect.Field;

public class VarHandleTestAccessBoolean {

    static final jdk.internal.misc.Unsafe UNSAFE;


    static boolean static_v;
    static final Object STATIC_V_BASE;
    static final long STATIC_V_OFFSET;


    boolean v;
    static final long V_OFFSET;


    static {
        try {
            try {
                Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");
                f.setAccessible(true);
                UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);
            } catch (Exception e) {
                throw new RuntimeException("Unable to get Unsafe instance.", e);
            }

            Field vField = VarHandleTestAccessBoolean.class.getDeclaredField("v");
            V_OFFSET = UNSAFE.objectFieldOffset(vField);

            Field staticVField = VarHandleTestAccessBoolean.class.getDeclaredField("static_v");
            STATIC_V_BASE = UNSAFE.staticFieldBase(staticVField);
            STATIC_V_OFFSET = UNSAFE.staticFieldOffset(staticVField);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        VarHandleTestAccessBoolean t = new VarHandleTestAccessBoolean();
        for (int i = 0; i < 60_000; i++) {
            testInstanceField(t);
        }

        try {
            testStaticFieldUnsafeBoolean();
        } catch (Exception e) {
            System.err.println("FAILED: testStaticFieldUnsafeBoolean");
        }
        try {
            testStaticFieldUnsafeByte();
        } catch (Exception e) {
            System.err.println("FAILED: testStaticFieldUnsafeByte");
        }
    }

    static void testInstanceField(VarHandleTestAccessBoolean recv) {
        boolean o = UNSAFE.getAndBitwiseAndBoolean(recv, V_OFFSET, false);
        boolean r = UNSAFE.compareAndExchangeBooleanVolatile(recv, V_OFFSET, false, true);
    }

    static void testStaticFieldUnsafeBoolean() {
        static_v = true;

        boolean o = UNSAFE.getAndBitwiseAndBoolean(STATIC_V_BASE, STATIC_V_OFFSET, false);
        if (o != true) throw new RuntimeException();

        boolean x = static_v;
        if (x != (boolean)(true & false)) throw new RuntimeException();
    }

    static void testStaticFieldUnsafeByte() {
        static_v = true;

        byte bo = UNSAFE.getAndBitwiseAndByte(STATIC_V_BASE, STATIC_V_OFFSET, (byte)0);
        boolean o = bo != 0;
        if (o != true) throw new RuntimeException();

        boolean x = static_v;
        if (x != (boolean)(true & false)) throw new RuntimeException();
    }
}



> On 12 Aug 2016, at 13:18, John Rose <john.r.rose at oracle.com> wrote:
> 
> On Aug 12, 2016, at 8:36 AM, Paul Sandoz <paul.sandoz at oracle.com> wrote:
>> 
>> Hi,
>> 
>> Please can someone help/advice how to find the cause of a test failure i am observing.
> 
> It might be a failure of alias analysis.  The T_BOOLEAN unsafe access, when applied to
> a real boolean field of a class, can weaken to the same code as a getfield or putfield.
> A later getfield can "see" the most recent matching getfield or putfield, whether it is
> a bytecode or a successfully weakened unsafe access.
> 
> But the CAS address probably won't weaken to a field address, either because CAS
> is complicated (doesn't match a get/set bytecode semantics) or because T_BYTE doesn't
> match T_BOOLEAN.  In that case, maybe the JIT is pulling the previously read T_BOOLEAN
> access across the T_BYTE access, on the grounds that the T_BYTE access cannot alias.
> 
> If all this is true, we have a C2 issue related to mismatched loads and stores.
> 
> Possible test for hypothesis:  Obscure the offset portion of the boolean field, in the test
> code (run it through a volatile variable, or an all-constant array indexed by a random number).
> See if the problem persists.  Of course, you'll detune the optimization in that case also,
> which could mask the bug.  To do this you have to use raw unsafe, not a VH.  Could lead
> (at least) to a good minimal reproducer.
> 
> I can think of several reasons why the above may *not* be your problem, but it's worth a look.
> Maybe a kind C2 person can look into it?  (Someone who fixed the last store-mismatch bug? :-) )
> 
> VarHandles are throwing a lot of really new, cool code shapes into C2, which will lead to bugs
> like this.  It's the same kind of bug we'll also see with more aggressive use of pointer reshaping
> by Panama APIs.  (E.g., a "struct stat" stashed in a Java long[] in the heap.)
> 
> — John



More information about the hotspot-dev mailing list