JDK-6550579 : FloatBuffer returns incorrect results when used with the Java 6 HotSpot ServerVM
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-04-26
  • Updated: 2014-07-31
  • Resolved: 2007-05-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 6 JDK 7 Other
6u4Fixed 7Fixed hs10Fixed
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Server VM (build 1.6.0_01-b06, mixed mode)

FULL OS VERSION :
WindowsXP (SP2+patches)
Linux 2.6.9-22.ELsmp x86_64
Linux 2.6.9-22.ELsmp i686 i386

A DESCRIPTION OF THE PROBLEM :
When using a java.nio.FloatBuffer in a loop, the values placed into the FloatBuffer via the put( float[], int, int) method are not always equal to the values of the source float[] data when the program is executed using the Hotspot Server VM (-server option).

Note that the problem appears to only be present when the jvm is executed with the -server option.  If -client is used, the FloatBuffer values are correct.

Note that sometimes the data in the FloatBuffer is correct.  It's the use of a FloatBuffer in a loop that appears to be part of the problem (at least as discovered and tested).

This problem appears to be new to Java 6 and is not present in the Java 1.4 or Java 5 vms that I tested.

THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Did not try

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a loop that executes more than 1 time
2. Copy an array for floats into a java.nio.FloatBuffer using the put(float[], int, int) method.  Ref http://java.sun.com/javase/6/docs/api/java/nio/FloatBuffer.html#put(float[],%20int,%20int)
3. Compare values of the source float array to those values in the FloatBuffer
4. Execute with the '-server' jvm option (e.g. java -server FloatBufferTest)

EXPECTED VERSUS ACTUAL BEHAVIOR :
When tested in prior jvms and in the Java 6 jvm using the '-client' option, the source float array and the values copied into the FloatBuffer are the same (as they should be).  When tested in the Java 6 jvm using the '-server' option, the source float array and the values copied into the FloatBuffer are not always the same when the FloatBuffer is being used inside of a loop.

ACTUAL -

java FloatBufferTest
0 loops experienced errors.  Total FloatBuffer loops executed 1000
0 loops experienced errors.  Total DoubleBuffer loops executed 1000
0 IntBuffer loops experienced errors.  Total IntBuffer loops executed 1000
Java version 1.6.0_02-ea-b02
Java name Java HotSpot(TM) Client VM

java -server FloatBufferTest
Error count for the current loop in comparing FloatBuffer to float src array 17618
Error count for the current loop in comparing FloatBuffer to float src array 24386
2 loops experienced errors.  Total FloatBuffer loops executed 1000
First FloatBuffer loop to error: 0
0 loops experienced errors.  Total DoubleBuffer loops executed 1000
0 IntBuffer loops experienced errors.  Total IntBuffer loops executed 1000
Java version 1.6.0_02-ea-b02
Java name Java HotSpot(TM) Server VM

REPRODUCIBILITY :
This bug can be reproduced always.



---------- BEGIN SOURCE ----------
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Random;

/**
 * The FloatBufferTest demonstrates an error in the FloatBuffer when executed
 * with the Java 6 jvm with the jvm arg '-server'. If the Java 6 jvm is executed
 * in client mode, then there is no error.
 *
 * Also worth noting is that the Java 1.4 and Java 5 jvms execute the code
 * without any errors for both jvm args '-server' and '-client'.
 *
 * To see the errors execute the code below using the Java 6 jvm with '-server'
 * specified.
 *
 * example: java -server FloatBufferTest
 *
 * Execute the same code using the same Java 6 jvm without the '-server' option
 * and no errors are produced.
 *
 * example: java FloatBufferTest or java -client FloatBufferTest
 *
 * Java version: Java version 1.6.0_01-b06
 *
 * Misc: The IntBuffer and DoubleBuffer are also tested in the same manner that
 * the FloatBuffer is tested and no errors have been seen using the same scenario.
 * Perhaps execution order of the methods may affect the outcome (not tested).
 */
public class FloatBufferTest {


	private static int NUM_LOOPS = 1000;
	
	//change to true to dump error values to console
	//note that doing so may affect results
	private static boolean DUMP_ERROR_VALUES = false;
	
	public static void main(String[] args) {
		long seed = 8675309;
		int dlength = 50000; // 50000 entries

		FloatBufferTest.testFloatBuffer(seed, dlength);
		//test DoubleBuffer in the same manner
		FloatBufferTest.testDoubleBuffer(seed, dlength);
		//test IntBuffer in the same manner
		FloatBufferTest.testIntBuffer(seed, dlength);
		
		//dump some jvm info
		String jvmversion = System.getProperty("java.vm.version");
		String jvmname = System.getProperty("java.vm.name");
		System.err.println("Java version " + jvmversion);
		System.err.println("Java name " + jvmname);

	}
	
	/**
	 * Test the FloatBuffer
	 * @param pSeed the seed value
	 * @param pLength the number of data values
	 */
	protected static void testFloatBuffer(long pSeed, int pLength) {
		
		int blength = 4; // 4 btyes
		float[] floats = new float[pLength];

		Random r = new Random(pSeed);
		//populate src float array
		for (int i = 0; i < pLength; i++) {
			floats[i] = r.nextFloat();
		}

		
		int loopErrorCount = 0;
		int firstLoopToErr = -1;


		
		

		for (int ii = 0; ii <= NUM_LOOPS; ii++) {

			ByteBuffer destbb = ByteBuffer.allocate(pLength * blength);
			FloatBuffer fb = destbb.asFloatBuffer();
			fb.put(floats, 0, pLength);

			int errorCount = 0;
			//compare source float values to those copied into the FloatBuffer
			for (int i = 0; i < floats.length; i++) {
				float fbfvalue = fb.get(i);
				float srcvalue = floats[i];
				//check for mismatching values
				if (fbfvalue != srcvalue) {
					errorCount++;
					
					if (DUMP_ERROR_VALUES) {
						System.err.println("*** Data values not equal error");
						System.err.println(i + " (array index) Expected " + srcvalue
							+ ", got " + fbfvalue);
					}
							
				}
			} //end i
			
			//check if there where any errors in loop execution
			if (errorCount > 0) {
				//track the first loop to error
				if (firstLoopToErr == -1) {
					firstLoopToErr = ii;
				}
				loopErrorCount++;
				System.err
					.println("Error count for the current loop in comparing FloatBuffer to float src array "
						+ errorCount);
			}
			
			
		}// end ii
		
		System.err.println(loopErrorCount + " loops experienced errors.  Total FloatBuffer loops executed " + NUM_LOOPS);
		if (loopErrorCount > 0) {
			System.err.println("First FloatBuffer loop to error: " + firstLoopToErr);
		}
		
	}
	
	/**
	 * Test the DoubleBuffer
	 * @param pSeed the seed value
	 * @param pLength the number of data values
	 */
	protected static void testDoubleBuffer(long pSeed, int pLength) {
		
		int blength = 8; // 8 btyes
		double[] src = new double[pLength];

		Random r = new Random(pSeed);
		//populate src array
		for (int i = 0; i < pLength; i++) {
			src[i] = r.nextDouble();
		}

		
		int loopErrorCount = 0;
		int firstLoopToErr = -1;


		
		

		for (int ii = 0; ii <= NUM_LOOPS; ii++) {

			ByteBuffer destbb = ByteBuffer.allocate(pLength * blength);
			DoubleBuffer fb = destbb.asDoubleBuffer();
			fb.put(src, 0, pLength);

			int errorCount = 0;
			//compare source float values to those copied into the DoubleBuffer
			for (int i = 0; i < src.length; i++) {
				double fbfvalue = fb.get(i);
				double srcvalue = src[i];
				//check for mismatching values
				if (fbfvalue != srcvalue) {
					errorCount++;
					
					if (DUMP_ERROR_VALUES) {
						System.err.println("*** Data values not equal error");
						System.err.println(i + " (array index) Expected " + srcvalue
							+ ", got " + fbfvalue);
					}
							
				}
			} //end i
			
			//check if there where any errors in loop execution
			if (errorCount > 0) {
				//track the first loop to error
				if (firstLoopToErr == -1) {
					firstLoopToErr = ii;
				}
				loopErrorCount++;
				System.err
					.println("Error count for the current loop in comparing DoubleBuffer to double src array "
						+ errorCount);
			}
			
			
		}// end ii
		
		System.err.println(loopErrorCount + " loops experienced errors.  Total DoubleBuffer loops executed " + NUM_LOOPS);
		if (loopErrorCount > 0) {
			System.err.println("First DoubleBuffer loop to error: " + firstLoopToErr);
		}
		
	}
	
	/**
	 * Test the IntBuffer
	 * @param pSeed the seed value
	 * @param pLength the number of data values
	 */
	protected static void testIntBuffer(long pSeed, int pLength) {
		
		int blength = 4; // 4 btyes
		int[] src = new int[pLength];

		Random r = new Random(pSeed);
		//populate src array
		for (int i = 0; i < pLength; i++) {
			src[i] = r.nextInt();
		}

		
		int loopErrorCount = 0;
		int firstLoopToErr = -1;


		
		

		for (int ii = 0; ii <= NUM_LOOPS; ii++) {

			ByteBuffer destbb = ByteBuffer.allocate(pLength * blength);
			IntBuffer fb = destbb.asIntBuffer();
			fb.put(src, 0, pLength);

			int errorCount = 0;
			//compare source float values to those copied into the IntBuffer
			for (int i = 0; i < src.length; i++) {
				int fbfvalue = fb.get(i);
				int srcvalue = src[i];
				//check for mismatching values
				if (fbfvalue != srcvalue) {
					errorCount++;
					
					if (DUMP_ERROR_VALUES) {
						System.err.println("*** Data values not equal error");
						System.err.println(i + " (array index) Expected " + srcvalue
							+ ", got " + fbfvalue);
					}
							
				}
			} //end i
			
			//check if there where any errors in loop execution
			if (errorCount > 0) {
				//track the first loop to error
				if (firstLoopToErr == -1) {
					firstLoopToErr = ii;
				}
				loopErrorCount++;
				System.err
					.println("Error count for the current loop in comparing IntBuffer to double src array "
						+ errorCount);
			}
			
			
		}// end ii
		
		System.err.println(loopErrorCount + " IntBuffer loops experienced errors.  Total IntBuffer loops executed " + NUM_LOOPS);
		if (loopErrorCount > 0) {
			System.err.println("First IntBuffer loop to error: " + firstLoopToErr);
		}
		
	}
	

}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Preliminary testing shows the usage of put (int index, float value) (Ref http://java.sun.com/javase/6/docs/api/java/nio/FloatBuffer.html#put(int,%20float) )
to be a possible workaround, although it has not been tested on all platforms

Comments
SUGGESTED FIX In the original putback 20070516113218.kvn.6550579 there is bug in ifnode.cpp changes (loop invariant 'iftrap' is redefined inside the loop). Solution: Use unique_ctrl_out() istead of loop in is_range_check(). IfTrue/IfFalse nodes should have only one control out. Webrev: http://prt-web.sfbay.sun.com/net/prt-archiver.sfbay/data/archived_workspaces/main/c2_baseline/2007/20070516152216.kvn.6550579/workspace/webrevs/webrev-2007.05.16/index.html
17-05-2007

SUGGESTED FIX Webrev: http://prt-web.sfbay.sun.com/net/prt-archiver.sfbay/data/archived_workspaces/main/c2_baseline/2007/20070516113218.kvn.6550579/workspace/webrevs/webrev-2007.05.16/index.html Solution: 1. insert_copies() should rematerialize only constants inputs for two_adr instructions as it does for Phis. At this point it is unsafe to extend live ranges as the case with MoveF2I instruction shows. MachSpillCopy node is created instead for such instructions. 2. Fixed the check for uncommon trap in IfNode::is_range_check(). 3. To catch the bug case during compilation I added the assert to check the overlap of an external LRG usage and the definition of the same LRG in the block. 4. Added instructions to move bits between xmm and gpr into amd64.ad. 5. Added the regression test.
16-05-2007

EVALUATION My putback 20041220124309.kvn.6206844 (yes, almost 2.5 years ago) added instructions to move raw 32-bits between xmm and regular registers. Before it was done through stack. Unfortunately Register Allocator can't handle them well causing the reported bug (RA clones MoveF2I instruction below an other loadF instruction which has the same xmm LRG so that MoveF2I uses a wrong xmm register). The code which clone MoveF2I in insert_copies() is unsafe since it could extend live ranges. Also I found why John's putback 20061023120554.jrose.dolphin-cleanups (part E) hided the problem. John added the additional check into IfNode::is_range_check() which looks for an uncommon trap with Reason_range_check. The check missed the case when the uncommon trap is in a block which is a merge point of several range checks. This prevents the scheduling of MoveF2I users (RShiftI two_adr instructions) to the block with the second loadF instruction.
16-05-2007