eclipse-openj9 / openj9

Eclipse OpenJ9: A Java Virtual Machine for OpenJDK that's optimized for small footprint, fast start-up, and high throughput. Builds on Eclipse OMR (https://github.com/eclipse/omr) and combines with the Extensions for OpenJDK for OpenJ9 repo.
Other
3.27k stars 721 forks source link

Inconsistent output value between "invokespecial" and "invokevirtual" Instructions #18705

Closed Brad0309 closed 7 months ago

Brad0309 commented 8 months ago

Java -version output

openjdk version "1.8.0_392"
IBM Semeru Runtime Open Edition (build 1.8.0_392-b08)
Eclipse OpenJ9 VM (build openj9-0.41.0, JRE 1.8.0 Mac OS X amd64-64-Bit Compressed References 20231122_842 (JIT enabled, AOT enabled)
OpenJ9   - 461bf3c70
OMR      - 5eee6ad9d
JCL      - b73cbdd342 based on jdk8u392-b08)
openjdk version "11.0.21" 2023-10-17
IBM Semeru Runtime Open Edition 11.0.21.0 (build 11.0.21+9)
Eclipse OpenJ9 VM 11.0.21.0 (build openj9-0.41.0, JRE 11 Mac OS X amd64-64-Bit Compressed References 20231122_809 (JIT enabled, AOT enabled)
OpenJ9   - 461bf3c70
OMR      - 5eee6ad9d
JCL      - 0fd7a05277 based on jdk-11.0.21+9)
openjdk version "17.0.9" 2023-10-17
IBM Semeru Runtime Open Edition 17.0.9.0 (build 17.0.9+9)
Eclipse OpenJ9 VM 17.0.9.0 (build openj9-0.41.0, JRE 17 Mac OS X amd64-64-Bit Compressed References 20231017_583 (JIT enabled, AOT enabled)
OpenJ9   - 461bf3c70
OMR      - 5eee6ad9d
JCL      - 3699725139c based on jdk-17.0.9+9)

Output from java -version.

Summary of problem

We have encountered an interesting test program that produces inconsistent output values when using two different instructions ("invokespecial" and "invokevirtual") .

The test program consists of the following source code for Class1:

public class Class1 extends Class2 implements Interface1 {
    public Class1() {
    }
    public static Integer test() {
        Class3 var1 = new Class3();
        return var1.m();
    }
}

The corresponding jasm code of Class1 is as follows:

super public class Class1
...
{
  ...
  public static Method test:"()Ljava/lang/Integer;"
    stack 2 locals 1
  {
        new class Class3;
        dup;
        invokespecial   Method Class3."<init>":"()V";
        astore_0;
        aload_0;
        invokevirtual   Method m:"()Ljava/lang/Integer;"; //change invokevirtual to invokespecial
        areturn;
  }
} 

When running this test program on both OpenJ9 and HotSpot Java virtual machines, the output is 1. However, if we change the instruction from "invokevirtual" to "invokespecial", the output value differs between OpenJ9 and HotSpot. Specifically, HotSpot produces an output of 1, while OpenJ9 produces an output of 2.

We have investigated the JVM spec to understand why this discrepancy occurs. According to the specification(https://docs.oracle.com/javase/specs/jvms/se11/jvms11.pdf) on page 509, the description of the invokespecial instruction is defined as follows: "The run-time constant pool entry at the index must be a symbolic reference to a method or an interface method. It provides the name and descriptor of the method or interface method, as well as a symbolic reference to the class or interface where the method or interface method is located."

Based on the specification, it seems that the behavior of invokespecial should be consistent with invokevirtual. However, further investigation and analysis are required to determine the root cause of this discrepancy. Please check this!

Test program: ValueError.zip

tajila commented 8 months ago

@theresa-m Please take a look at this

theresa-m commented 8 months ago

Thanks for the issue. I see in the spec from the notes on invokespecial:

The invokespecial instruction handles invocation of a nonabstract interface method, referenced either via a direct superinterface or via a superclass. In these cases, the rules for selection are essentially the same as those for invokeinterface (except that the search starts from a different class).

I am working on finding the cause of the different behavior in OpenJ9 and will create a fix.