JDK-6816548 : Uninitialized register when performing casting + auto(un)boxing
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6u12
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2009-03-12
  • Updated: 2012-01-13
  • Resolved: 2012-01-13
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
7 b55Fixed
Description
FULL PRODUCT VERSION :
javac 1.6.0_11
javac 1.6.0_12

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Wersja 6.0.6001]  - Microsoft Vista,
Linux 32-bit

A DESCRIPTION OF THE PROBLEM :
Compilation of the attached test case results in a java.lang.VerifyError. This is due to a bad compilation of the class (see expected and actual results).

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the attached test case. Run the test case. Observe the error. Decompile the .class file to get the bad bytecode.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected bytecode:

> javap -c test.Test

Compiled from "Test.java"
public class test.Test extends java.lang.Object{
public test.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_2
   2:   iconst_0
   3:   invokestatic    #16; //Method java/lang/Byte.valueOf:(B)Ljava/lang/Byte;
   6:   astore_1
   7:   return

}

0 is assigned to 'fi' (lines 0-1 of main()) but later a constant 0 is used for assignment to 'B' (by means of Byte.valueOf() method) as 'fi' is a compile-time constant and should be inlined.
ACTUAL -
Actual bytecode:

> javap -c test.Test

Compiled from "Test.java"
public class test.Test extends java.lang.Object{
public test.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   iload_2
   1:   i2b
   2:   invokestatic    #2; //Method java/lang/Byte.valueOf:(B)Ljava/lang/Byte;
   5:   astore_1
   6:   return

}

in line 0 of the main() method the code tries lo load a local integer variable #2 ('fi'). But there is no initialisation of that variable. The whole main() method is reduced to the 'B=fi;' assignment, but neither the constant 0 value is used nor is 'fi' initialised with 0 previously.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
> java test.Test

Exception in thread "main" java.lang.VerifyError: (class: test/Test, method: main signature: ([Ljava/lang/String;)V) Accessing value from uninitialized register 2
Could not find the main class: test.Test.  Program will exit.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package test;
 
public class Test {
 
	public static void main(String[] args) {
		Byte B;
		final int fi = 0;
		B = fi; // narrowing pri. (int -> byte) + boxing (byte -> Byte)
	}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Assignment in two steps solves the case.

class Test {
 public static void main(String[] args) {
  final int fi = 0;
  byte b = fi; // int -> byte
  Byte B = b;  // byte -> Byte
 }
}

Comments
SUGGESTED FIX A webrev of this fix is available at the following URL: http://hg.openjdk.java.net/jdk7/tl/langtools/rev/6ce39250fa88
26-03-2009

EVALUATION This problem is caused by a bug in Lower; given the following lines: final int fi = 0; Byte b = fi; Lowering generates the following code: final int fi = 0; Byte b = Byte.valueOf(fi); Which is invalid as method invocation conversion (se JLS3 5.3) does not exploit primitive narrowing primitive conversion (JSL3 5.1.3); one would expect the lowered code to look like: final int fi = 0; Byte b = Byte.valueOf((byte)fi); That is, an explicit cast should be used to narrow the type of the value to be boxed (so that Byte.valueOf(byte) can be called). Instead of adding the cast, Lower currently simply changes the type of the 'fi' identifier (from int to byte) so that the method call can typecheck without problems. However, when Lower updates the identifier's type, the constant value stored in the original type (which has been set during attribution) is lost (here, since 'fi' is final, it has a constant value, namely 0 - an immediate value should be used instead of 'fi'). During code generation, since no constant value is associated to 'fi', no constand folding is applied - which means that code generation simply emits a reference to 'fi' - which unfortunately turns out to be invalid - being 'fi' a constant value, the compiler doesn't even emit code for 'fi' declaration - it simply replaces every occurrence of 'fi' with its constant value 0. Lower should obviously preserve the constant value associated with the identifier when updating the identifier's type - this way Gen can apply constant folding (as usual) and no reference to non-existent variable would be emitted.
12-03-2009