JDK-7131028 : Switch statement takes wrong path
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: hs23
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_10
  • CPU: sparc
  • Submitted: 2012-01-18
  • Updated: 2012-03-24
  • Resolved: 2012-03-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 Other
7u4Fixed 8Fixed hs23Fixed
Description
When qualifying Java DB on JDK 7u4-ea-b06, we saw intermittent errors
(manifested as SQLExceptions) on the 64-bit VM on SPARC, and initially
filed a bug in the Apache Derby bug tracker, including a repro:
https://issues.apache.org/jira/browse/DERBY-5569

This problem is also reproducible on 7u4-ea-b05, but not on
7u4-ea-b04, and it is only seen with the 64-bit VM for SPARC.

By instrumenting the code, we narrowed down the problem to the
following method in the org.apache.derby.impl.jdbc.EmbedResultSet
class (the println() call has been added by us for debugging):

    public final Object getObject(int columnIndex) throws SQLException {
        checkIfClosed("getObject");

        // need special handling for some types.
        int colType = getColumnType(columnIndex);
        switch (colType) {
        case Types.CHAR:
        case Types.VARCHAR:
        case Types.LONGVARCHAR:
            // handles maxfield size correctly
            return getString(columnIndex);

        case Types.CLOB:
            return getClob(columnIndex);

        case Types.BINARY:
        case Types.VARBINARY:
        case Types.LONGVARBINARY:
            System.out.println(colType);
            // handles maxfield size correctly
            return getBytes(columnIndex);

        case Types.BLOB:
            return getBlob(columnIndex);

        default:
            break;
        }

        try {

            DataValueDescriptor dvd = getColumn(columnIndex);
            if (wasNull = dvd.isNull())
                return null;

            return dvd.getObject();

        } catch (StandardException t) {
            throw noStateChangeException(t);
        }
    }

If the above code ever calls println(), it should print a value equal
to one of the constants Types.BINARY, Types.VARBINARY or
Types.LONGVARBINARY. These constants are defined in java.sql.Types as
-2, -3 and -4, respectively.

However, when we see the error, the number -5 is printed (which
corresponds to Types.BIGINT). That should be impossible, since there's
no way the colType variable could have changed between the switch and
the println(), so we suspect that the wrong code is produced for the
above switch statement under some conditions.

Unfortunately, we haven't been able to come up with a simple,
standalone test case that demonstrates the problem. The test case we
do have, runs quite a lot of Java DB code.

So, for now, in order to reproduce the bug you will have to download
and compile the D5569 class from
https://issues.apache.org/jira/browse/DERBY-5569 and run it on JDK
7u4-ea on a SPARC machine:

  java -d64 -Xmx512M -Xms30M -cp $JAVA_HOME/db/lib/derby.jar:. D5569

(the memory settings are not necessary, but seem to make the test case
fail more frequently on the machines where we've tested it)

Then observe that the repro typically fails after 1700 to 3000
iterations because it takes the wrong code path in the above mentioned
getObject() method:

0
100
200
300
(...)
1500
1600
1700
Exception in thread "main" java.sql.SQLException: The exception 'java.sql.SQLException: An attempt was made to get a data value of type 'byte[]' from a data value of type 'BIGINT'.' was thrown while evaluating an expression.
        at org.apache.derby.impl.jdbc.SQLExceptionFactory40.getSQLException(Unknown Source)
(...)
        at org.apache.derby.impl.jdbc.EmbedResultSet.getBytes(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedResultSet.getObject(Unknown Source)
        at org.apache.derby.exe.ac9e300ac8x0134xf0dfx8da0x00000165e5883.e0(Unknown Source)
(...)

It reproduces fairly consistently on the two SPARC machines where
we've run the repro, but not always. If it haven't reproduced after
3000 iterations (the number printed to the console is the iteration
count), kill the repro and restart it.

Running with -XX:+PrintCompilation shows that the getObject() method
is compiled a long time before the error (it's compiled somewhere
between the 100th and 200th iteration in the test case), so the
compiled version appears to have been executed quite a number of times
successfully before it starts failing.

What's somewhat suspicious, though, is that the error always happens
almost immediately after the getColumnType() method in the same class
is recompiled, assuming that's what the following output means:

 416520 7406       4       org.apache.derby.impl.jdbc.EmbedResultSet::getColumnType (63 bytes)
 416530 5658       3       org.apache.derby.impl.jdbc.EmbedResultSet::getColumnType (63 bytes)   made not entrant

getColumnType() is the method that returns the value that's passed to
the problematic switch statement in getObject():

        int colType = getColumnType(columnIndex);
        switch (colType) {
        ...

Comments
EVALUATION http://hg.openjdk.java.net/lambda/lambda/hotspot/rev/9164b8236699
22-03-2012

EVALUATION http://hg.openjdk.java.net/hsx/hotspot-comp/hotspot/rev/9164b8236699
21-01-2012

EVALUATION C1 compiler (used in Tiered Compilation) generate incorrect first branch instruction (at 0x5175c850) after the call to getColumnType(). getColumnType() returns int type but the branch checks all 64 bits ( %xcc ). C2 code does not extend sign of loaded integer value. As result when getColumnType() recompiled by C2 and it is called by C1 code in getObject(), the result of switch is wrong. Here is C1 switch code in getObject(): 0xffffffff5175c844: call 0xffffffff500d8960 ;*invokevirtual getColumnType ; - org.apache.derby.impl.jdbc.EmbedResultSet::getObject@8 ; {optimized virtual_call} 0xffffffff5175c848: mov %i0, %o0 ; OopMap{I0=Oop off=620} ;*invokevirtual getColumnType ; - org.apache.derby.impl.jdbc.EmbedResultSet::getObject@8 0xffffffff5175c84c: cmp %o0, -4 ;; 60 branch [LT] [label:0x53883b0] 0xffffffff5175c850: bl,pn %xcc, 0xffffffff5175c864 0xffffffff5175c854: nop 0xffffffff5175c858: cmp %o0, -2 ;; 64 branch [LE] [B2] 0xffffffff5175c85c: ble,pn %icc, 0xffffffff5175cbc0 0xffffffff5175c860: nop Here is C2 code in getColumnType(): 0xffffffff50375ed0: ld [ %l0 + 0x10 ], %i0 ;*getfield JDBCTypeId Here is PrintOptoAssembly output of getColumnType(): 0f0 + LDUW [R_L0 + #16],R_I0 ! int ! Field org/apache/derby/catalog/types/BaseTypeIdImpl.JDBCTypeId
20-01-2012