JDK-4903383 : Incorrect ordering of loads/stores of same memory loc with -XX:-OptoScheduling
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: solaris_2.6
  • CPU: generic
  • Submitted: 2003-08-08
  • Updated: 2003-11-25
  • Resolved: 2003-09-05
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.
Other Other
1.4.1_07 07Fixed 1.4.2_04Fixed
Description
This test case shows what looks like a problem with memory aliasing
handling in the server compiler.  Only one method is compiled.


class Test {
  private int pos;
  private int charBufSize;
  private char[] charBuf = new char[8];

  // Only compile this method to see the failure.
  private void fill() throws Exception {
    int preloopPos = pos;  // pos == 1
    for (int i = 0; i < charBufSize - pos; i++)
      charBuf[i] = charBuf[i + pos];
    int postloopPos = pos;
    charBufSize = charBufSize - pos;
    pos = 0;
    int n = read(charBuf, charBufSize, charBuf.length - charBufSize);
    if (postloopPos != preloopPos) {
      // Should never reach here, but we do.
      throw new Exception("Pre loop " + preloopPos +
                          " Post loop " + postloopPos);
    }
    if (n > 0) {
      charBufSize += n;
    }
  }

  // This should not be inlined into fill().  What it does is irrelevant.
  private int read(char[] buf, int x, int y) {
    for (int i = 0; i != y; ++i)
      buf[i + x] = 'Z';
    return y;
  }

  public static void main(String[] args) {
    Test test = new Test();
    try {
      test.pos = 1;  // set pos != 0
      test.charBufSize = 2;
      test.fill();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}


Run the test like this on Solaris/SPARC:

% java_g -server -version
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Server VM (build 1.4.1_01-b01-debug, mixed mode)
% java_g -server -XX:-OptoScheduling -Xcomp -XX:CompileOnly=Test.fill -XX:-Inline -XX:+PrintCompilation Test
VM option '-OptoScheduling'
VM option 'CompileOnly=Test.fill'
VM option '-Inline'
VM option '+PrintCompilation'
  1   b   Test::fill (156 bytes)
java.lang.Exception: Pre loop 1 Post loop 0
        at Test.fill(Test.java:20)
        at Test.main(Test.java:39)


The code that throws the exception should never be reached!  This test
fails with 1.4.1_01, 1.4.2 fcs, and 1.5.0 build 13.  It does not fail
with 1.4.0_03.  The test also fails on Linux/x86.


In the PrintOptoAssembly output for fill() the bad code can be seen in
this block:

    1dc   B18: #    B40 B19 <- B17 B13  Freq: 0.000599997
    1dc     STW    #0,[R_I0 + #12]              # pos = 0
    1e0     LDUW   [R_I0 + #8],R_O1 ! ptr
    1e4     LDUW   [R_I0 + #16],R_L1
    1e8     LDUW   [R_I0 + #12],R_L5            # postloopPos = pos
    1ec     SUB    R_L1,R_L5,R_O2
    1f0     STW    R_O2,[R_I0 + #16]
    1f4     BReq   R_O1,B40  P=0.000001 C=-1.000000

In the source code the assignment "postloopPos = pos" is before the
assignment "pos = 0".  But in the generated code the store to "pos" is
first!  This causes the failure.

The load and store of "pos" occur in the correct order when
OptoScheduling is enabled.  It looks like the compiler is not
recognizing that the load and store refer to the same memory location.
So during scheduling it can switch the order of the load and store.
But scheduling should never move a load/store past a store to the same
memory location.

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.4.1_07 1.4.2_04 generic tiger FIXED IN: 1.4.1_07 1.4.2_04 tiger INTEGRATED IN: 1.4.1_07 1.4.2_04 tiger tiger-b17 tiger-b18
14-06-2004

EVALUATION Loop optimizations/split_if must preserve anti-dependence edges. ###@###.### 2003-08-08 ----- ###@###.### 2003-08-14 The loop optimizations and split_if preserved the structure of the code but did not put all necessary nodes onto the iGVN worklist. This resulted in not reaching an optimization fix-point. In specific, a load and store used the same memory state before loop opts. Since only the Store's clone was put onto the worklist during loop optimization, only the Store's memory input was updated. -----
11-06-2004

SUGGESTED FIX In 1.4.1 code base, loopopts.cpp line 61..64: // Else x is a new node we are keeping // We do not need register_new_node_with_optimizer // because set_type has already been called. ! _igvn._worklist.push(n); --- // Else x is a new node we are keeping // We do not need register_new_node_with_optimizer // because set_type has already been called. ! _igvn._worklist.push(x); ----- -----
11-06-2004