United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6912517 JIT bug compiles out (and stops running) code that needs to be run. Causes NPE.
JDK-6912517 : JIT bug compiles out (and stops running) code that needs to be run. Causes NPE.

Details
Type:
Bug
Submit Date:
2009-12-22
Status:
Closed
Updated Date:
2011-03-08
Project Name:
JDK
Resolved Date:
2011-03-08
Component:
hotspot
OS:
linux_redhat_4.0,linux_redhat_5.2,solaris_10
Sub-Component:
compiler
CPU:
x86,sparc
Priority:
P4
Resolution:
Fixed
Affected Versions:
6u14,6u16
Fixed Versions:
hs17 (b07)

Related Reports
Backport:
Backport:
Duplicate:
Duplicate:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) Server VM (build 14.0-b16, mixed mode)


FULL OS VERSION :
SunOS desb0028.nyc.deshaw.com 5.10 127128-11 i86pc i386 i86pc


A DESCRIPTION OF THE PROBLEM :
The JIT compile kicks in and compiles the method ensureProperCallingThread() below into a version that null pointers.  This should never happen, and the JIT is producing incorrect code.

If you compile this bug on Solaris 10, and then run with:

java -d64 -XX:CompileThreshold=100 JITBug



THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: No

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See description

EXPECTED VERSUS ACTUAL BEHAVIOR :
Should not compile this code in such a way that it null pointers.
ERROR MESSAGES/STACK TRACES THAT OCCUR :

Eventually (may take a few runs) you will get this error:
Starting thread: 0
Starting thread: 1
Starting thread: 2
Exception in thread "Runner: 2" java.lang.NullPointerException
        at JITBug.ensureProperCallingThread(JITBug.java:99)
        at JITBug.run(JITBug.java:87)
        at java.lang.Thread.run(Thread.java:619)
Starting thread: 3
Exception in thread "Runner: 3" java.lang.NullPointerException
        at JITBug.ensureProperCallingThread(JITBug.java:99)
        at JITBug.run(JITBug.java:87)
        at java.lang.Thread.run(Thread.java:619)
Starting thread: 4
Exception in thread "Runner: 4" java.lang.NullPointerException
        at JITBug.ensureProperCallingThread(JITBug.java:99)
        at JITBug.run(JITBug.java:87)
        at java.lang.Thread.run(Thread.java:619)



REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------

/**
 * Highlights a bug with the JIT compiler.
 * @author Matt Bruce m b r u c e __\at/__ g m a i l DOT c o m
 */
public class JITBug implements Runnable
{
    private final Thread myThread;
    private Thread       myInitialThread;
    private boolean      myShouldCheckThreads;

    /**
     * Sets up the running thread, and starts it.
     */
    public JITBug(int id)
    {
        myThread = new Thread(this);
        myThread.setName("Runner: " + id);
        myThread.start();
        myShouldCheckThreads = false;
    }

    /**
     * @param shouldCheckThreads the shouldCheckThreads to set
     */
    public void setShouldCheckThreads(boolean shouldCheckThreads)
    {
        myShouldCheckThreads = shouldCheckThreads;
    }

    /**
     * Starts up the two threads with enough delay between them for JIT to
     * kick in.
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException
    {
        // let this run for a bit, so the "run" below is JITTed.
        for (int id = 0; id < 20; id++) {
            System.out.println("Starting thread: " + id);
            JITBug bug = new JITBug(id);
            bug.setShouldCheckThreads(true);
            Thread.sleep(2500);
        }
    }

    /**
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run()
    {
        long runNumber = 0;
        while (true) {
            // run hot for a little while, give JIT time to kick in to this loop.
            // then run less hot.
            if (runNumber > 15000) {
                try {
                    Thread.sleep(5);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            runNumber++;
            ensureProperCallingThread();
        }
    }

    private void ensureProperCallingThread()
    {
        // this should never be null.  but with the JIT bug, it will be.
        // JIT BUG IS HERE ==>>>>>
        if (myShouldCheckThreads) {
            if (myInitialThread == null) {
                myInitialThread = Thread.currentThread();
            }
            else if (myInitialThread != Thread.currentThread()) {
                System.out.println("Not working: " + myInitialThread.getName());
            }
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Change compile threshold and ensure that the code is JIT compiled only after at least two threads have used the code.

                                    

Comments
PUBLIC COMMENTS

Can the submitter please:
- clarify what line 99 (as per the stacktrace) corresponds to in the actual code.
- confirm whether this only fails on 64-bit or whether 32-bit as well
- how many processors are on the target machine

The ability to reproduce this may well depend on the actual execution environment.

Can the submitter also please correct the test program to use proper synchronization. The mutable field myShouldCheckThreads is set by the main thread and read by the target thread, and this needs to be done under synchronization, such as by declaring the field volatile

    private volatile  boolean      myShouldCheckThreads;

After doing that please see if the problem still reproduces. I don't think this is a likely cause of the problem as described but it may have an impact.
                                     
2009-12-22
EVALUATION

The explicit check of "if (myInitialThread == null)" was replaced with incorrect implicit check:

03d   B4: #	B28 B5 <- B3  Freq: 1e+06
03d   	movq    R10, [R10 + #32 (8-bit)]	# ptr ! Field JITBug.myInitialThread
041   	# TLS is in R15
041   	cmpq    R10, [R15 + #448 (32-bit)]	# raw ptr
048   	NullCheck R10
048

The code should be (generated by latest HS17 with -XX:+UseCompressedOops):

04e   B4: #	B7 B5 <- B3  Freq: 1e+06
04e   	movl    R10, [RBP + #20 (8-bit)]	# compressed ptr ! Field JITBug.myInitialThread
052   	# TLS is in R15
052   	movq    R11, [R15 + #456 (32-bit)]	# ptr
059   	testl   R10, R10	# compressed ptr
05c   	je,s   B7  P=0.009950 C=201.000000
05c
                                     
2010-01-06
EVALUATION

http://hg.openjdk.java.net/jdk7/hotspot-comp/hotspot/rev/1271af4ec18c
                                     
2010-01-08
PUBLIC COMMENTS

Problem:
Block::implicit_null_check() does not check that the value
is used in memory expression in instructions with embedded
memory load. In the bug case the instruction was:

CmpP val,(tls+off)

Solution:
Add the missing check.
Add regression test.
                                     
2010-01-08



Hardware and Software, Engineered to Work Together