JDK-6674350 : HotSpot compiler error while optimization of a complex loop
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-03-12
  • Updated: 2011-02-16
  • Resolved: 2008-03-18
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.7.0-ea"
Java(TM) SE Runtime Environment (build 1.7.0-ea-b19)
Java HotSpot(TM) Client VM (build 1.7.0-ea-b19, mixed mode, sharing)


FULL OS VERSION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Unfortunately, this bug occurs only in enough complex test, including our library, processing large arrays. I tried to reproduce it in a separate little test, that performs absolutely identical operations, but the test worked fine. Though I've decided to report about this bug, because it seems serious enough and can lead to problems in end-user applications.

I'm almost sure that the problem is in the Java HotSpot compiler, because my test works fine under 1.6.0_02m under 1.7 without "-server" key and under 1.7 with "-Xint" key.

Below is the maximally full information about the situation when the bug occurs.

I have a library class PackedBitBuffers, containing, in particular, the following 3 procedures:

    /**
     * Returns the bit <tt>#index</tt> in the packed <tt>dest</tt> bit buffer.
     * Equivalent to the following expression:<pre>
     * (src.get((int)(index >>> 6)) & (1L << (index & 63))) != 0L;
     * </pre>
     *
     * @param src   the source buffer (bits are packed into <tt>long</tt> values).
     * @param index index of the returned bit.
     * @return      the bit at the specified index.
     * @throws IndexOutOfBoundsException if this method cause access of data outside buffer limits.
     * @throws NullPointerException if <tt>src</tt> is <tt>null</tt>.
     */
    public static boolean getBit(LongBuffer src, long index) {
        return (src.get((int)(index >>> 6)) & (1L << (index & 63))) != 0L;
    }

    /**
     * Sets the bit <tt>#index</tt> in the packed <tt>dest</tt> bit buffer.
     * Equivalent to the following operators:<pre>
     * if (value)
     * &#32;   dest.put((int)(index >>> 6), dest.get((int)(index >>> 6)) | 1L << (index & 63));
     * else
     * &#32;   dest.put((int)(index >>> 6), dest.get((int)(index >>> 6)) & ~(1L << (index & 63)));
     * </pre>
     *
     * @param dest  the destination buffer (bits are packed into <tt>long</tt> values).
     * @param index index of the written bit.
     * @param value new bit value.
     * @throws IndexOutOfBoundsException if this method cause access of data outside array bounds.
     * @throws NullPointerException if <tt>dest</tt> is <tt>null</tt>.
     */
    public static void setBit(LongBuffer dest, long index, boolean value) {
        if (value)
            dest.put((int)(index >>> 6), dest.get((int)(index >>> 6)) | 1L << (index & 63));
        else
            dest.put((int)(index >>> 6), dest.get((int)(index >>> 6)) & ~(1L << (index & 63)));
    }

    /**
     * Copies <tt>count</tt> bits from <tt>src</tt> array, starting from the element <tt>#srcPos</tt>,
     * to packed <tt>dest</tt> buffer, starting from the bit <tt>#destPos</tt>.
     *
     * @param src     the source array (unpacked <tt>boolean</tt> values).
     * @param srcPos  position of the first read bit in the source array.
     * @param dest    the destination <tt>LongBuffer</tt> (bits are packed into <tt>long</tt> values).
     * @param destPos position of the first written bit in the destination buffer.
     * @param count   the number of bits to be copied (should be &gt;=0).
     * @throws IndexOutOfBoundsException if copying would cause access of data outside array bounds or buffer limit.
     * @throws NullPointerException if either <tt>src</tt> or <tt>dest</tt> is <tt>null</tt>.
     */
    public static void packBits(boolean[] src, int srcPos, LongBuffer dest, long destPos, int count) {
        int countStart = (destPos & 63) == 0 ? 0 : 64 - (int)(destPos & 63);
        if (countStart > count)
            countStart = count;
        for (int srcPosMax = srcPos + countStart; srcPos < srcPosMax; srcPos++, destPos++) {
            if (src[srcPos])
                dest.put((int)(destPos >>> 6), dest.get((int)(destPos >>> 6)) | 1L << (destPos & 63));
            else
                dest.put((int)(destPos >>> 6), dest.get((int)(destPos >>> 6)) & ~(1L << (destPos & 63)));
        }
        count -= countStart;
        int cnt = count >>> 6;
        for (int k = (int)(destPos >>> 6), kMax = k + cnt; k < kMax; k++) {
            int low = (src[srcPos] ? 1 : 0)
                | (src[srcPos + 1] ? 1 << 1 : 0)
                | (src[srcPos + 2] ? 1 << 2 : 0)
                | (src[srcPos + 3] ? 1 << 3 : 0)
                | (src[srcPos + 4] ? 1 << 4 : 0)
                | (src[srcPos + 5] ? 1 << 5 : 0)
                | (src[srcPos + 6] ? 1 << 6 : 0)
                | (src[srcPos + 7] ? 1 << 7 : 0)
                | (src[srcPos + 8] ? 1 << 8 : 0)
                | (src[srcPos + 9] ? 1 << 9 : 0)
                | (src[srcPos + 10] ? 1 << 10 : 0)
                | (src[srcPos + 11] ? 1 << 11 : 0)
                | (src[srcPos + 12] ? 1 << 12 : 0)
                | (src[srcPos + 13] ? 1 << 13 : 0)
                | (src[srcPos + 14] ? 1 << 14 : 0)
                | (src[srcPos + 15] ? 1 << 15 : 0)
                | (src[srcPos + 16] ? 1 << 16 : 0)
                | (src[srcPos + 17] ? 1 << 17 : 0)
                | (src[srcPos + 18] ? 1 << 18 : 0)
                | (src[srcPos + 19] ? 1 << 19 : 0)
                | (src[srcPos + 20] ? 1 << 20 : 0)
                | (src[srcPos + 21] ? 1 << 21 : 0)
                | (src[srcPos + 22] ? 1 << 22 : 0)
                | (src[srcPos + 23] ? 1 << 23 : 0)
                | (src[srcPos + 24] ? 1 << 24 : 0)
                | (src[srcPos + 25] ? 1 << 25 : 0)
                | (src[srcPos + 26] ? 1 << 26 : 0)
                | (src[srcPos + 27] ? 1 << 27 : 0)
                | (src[srcPos + 28] ? 1 << 28 : 0)
                | (src[srcPos + 29] ? 1 << 29 : 0)
                | (src[srcPos + 30] ? 1 << 30 : 0)
                | (src[srcPos + 31] ? 1 << 31 : 0)
                ;
            srcPos += 32;
            int high = (src[srcPos] ? 1 : 0)        // PROBLEM!
                | (src[srcPos + 1] ? 1 << 1 : 0)
                | (src[srcPos + 2] ? 1 << 2 : 0)
                | (src[srcPos + 3] ? 1 << 3 : 0)
                | (src[srcPos + 4] ? 1 << 4 : 0)
                | (src[srcPos + 5] ? 1 << 5 : 0)
                | (src[srcPos + 6] ? 1 << 6 : 0)
                | (src[srcPos + 7] ? 1 << 7 : 0)
                | (src[srcPos + 8] ? 1 << 8 : 0)
                | (src[srcPos + 9] ? 1 << 9 : 0)
                | (src[srcPos + 10] ? 1 << 10 : 0)
                | (src[srcPos + 11] ? 1 << 11 : 0)
                | (src[srcPos + 12] ? 1 << 12 : 0)
                | (src[srcPos + 13] ? 1 << 13 : 0)
                | (src[srcPos + 14] ? 1 << 14 : 0)
                | (src[srcPos + 15] ? 1 << 15 : 0)
                | (src[srcPos + 16] ? 1 << 16 : 0)
                | (src[srcPos + 17] ? 1 << 17 : 0)
                | (src[srcPos + 18] ? 1 << 18 : 0)
                | (src[srcPos + 19] ? 1 << 19 : 0)
                | (src[srcPos + 20] ? 1 << 20 : 0)
                | (src[srcPos + 21] ? 1 << 21 : 0)
                | (src[srcPos + 22] ? 1 << 22 : 0)
                | (src[srcPos + 23] ? 1 << 23 : 0)
                | (src[srcPos + 24] ? 1 << 24 : 0)
                | (src[srcPos + 25] ? 1 << 25 : 0)
                | (src[srcPos + 26] ? 1 << 26 : 0)
                | (src[srcPos + 27] ? 1 << 27 : 0)
                | (src[srcPos + 28] ? 1 << 28 : 0)
                | (src[srcPos + 29] ? 1 << 29 : 0)
                | (src[srcPos + 30] ? 1 << 30 : 0)
                | (src[srcPos + 31] ? 1 << 31 : 0)
                ;
            srcPos += 32;
            dest.put(k, ((long)low & 0xFFFFFFFFL) | (((long)high) << 32));
            destPos += 64;
        }
        int countFinish = count & 63;
        for (int srcPosMax = srcPos + countFinish; srcPos < srcPosMax; srcPos++, destPos++) {
            if (src[srcPos])
                dest.put((int)(destPos >>> 6), dest.get((int)(destPos >>> 6)) | 1L << (destPos & 63));
            else
                dest.put((int)(destPos >>> 6), dest.get((int)(destPos >>> 6)) & ~(1L << (destPos & 63)));
        }
    }

These procedures process an array of bits packed into LongBuffer. In other my class, there is the following code using them:

        void setDataInFirstBank(long firstBankOffset, Object srcArray, int srcArrayOffset, int count) {
            PackedBitBuffers.packBits((boolean[])srcArray, srcArrayOffset, lb[0], firstBankOffset, count);
            for (int k = 0; k < count; k++) { // Java 1.7 bug here! This loop was added to help to find the bug.
                if (PackedBitBuffers.getBit(lb[0], firstBankOffset + k)
                    != ((boolean[])srcArray)[srcArrayOffset + k]) {
                    StringBuilder sb1 = new StringBuilder(), sb2 = new StringBuilder();
                    for (int j = 0; j < count; j++) {
                        if (j == k) {
                            sb1.append("    ");
                            sb2.append("    ");
                        }
                        sb1.append(PackedBitBuffers.getBit(lb[0], firstBankOffset + j) ? "1" : "0");
                        sb2.append(((boolean[])srcArray)[srcArrayOffset + j] ? "1" : "0");
                    }
                    throw new AssertionError("k=" + k + ", count=" + count + ", srcArrayOffset=" + srcArrayOffset
                        + ", firstBankOffset=" + firstBankOffset + ", srcArray["
                        + ((boolean[])srcArray).length + "]," + lb[0] + "\n" + sb1 + "\n" + sb2);
                }
            }
        }

Here I use the "packBits" procedure and check its results via more simple "getBit" function. This code is called from universal complex test, generating random bit sequence and testing a lot of another my procedures with them. And I found that in some rare situation this code throws AssertionError with the following message:

Exception in thread "main" java.lang.AssertionError: k=928, count=16384, srcArrayOffset=65536, firstBankOffset=0, srcArray[100000],java.nio.DirectLongBufferU[pos=0 lim=256 cap=256]
1000000100000100...0100110    10101000011111000111000110111001101100011001101010110...
1000000100000100...0100110    00101000011111000111000110111001101100011001101010110...
	at net.algart.arrays.MappedDataStorages$MappedBitStorage.setDataInFirstBank(MappedDataStorages.java:2311)
	at net.algart.arrays.MappedDataStorages$MappedStorage.setData(MappedDataStorages.java:872)
	at net.algart.arrays.BufferArraysImpl$AbstractBufferArray.setData(BufferArraysImpl.java:178)
	at net.algart.arrays.demo.MainOperationsTest.main(MainOperationsTest.java:402)

(I've removed the dump of extra bits. I used this dump to reproduce the bug in a separate test, but it doesn't want to be reproduced.) In other words, the bit #32 (928%64) from 64-bit block #14 (928/64) is set incorrectly: 1 instead 0!

I'll appreciate if you will try to comment what can be the problem here. If you need, I can send you (via email) full our code where the problem occurs: it's ~6 MB of my source code. The bug is reproduced always in my test, when it is called with the same arguments.


THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: No

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Sorry, but I was not able to reproduce the problem in a small test. In my test, using my libraries, it is reproduced always, but full amount of the library is ~6 megabytes of source code.

REPRODUCIBILITY :
This bug can be reproduced always.

Comments
EVALUATION I extracted a test case for your description and reproduced the failure. This is the same as 6661247. It only shows up with later builds because of changes in local schedule of the block that creates extra register pressure.
18-03-2008

EVALUATION What flags is the VM run with? Does the problem reliably appear in the failing test? Or is it an intermittant thing? The full library could be helpful, provided that we have a standalone test case, however complex. A hunch: does the problem reproduce with the -XX:-UseSuperWord flag?
12-03-2008