Search code examples
assemblyjvmjvm-hotspot

Why do hotspot generated compiled methods stash eax high up the stack before execution?


When looking at generated assembly code from OpenJDK's HotSpot, there is part of the prologue that copies the %eax value to a location further up the stack (rsp-0x14000), and I don't understand why it's doing that:

[Disassembling for mach='i386:x86-64']
[Entry Point]
[Verified Entry Point]
[Constants]
  # {method} {0x0000000120f0f410} 'add' '(JJ)J' in 'Test'
  # parm0:    rsi:rsi   = long
  # parm1:    rdx:rdx   = long
  #           [sp+0x40]  (sp of caller)
  0x000000010cd8a3e0: mov    %eax,-0x14000(%rsp)
  0x000000010cd8a3e7: push   %rbp
  0x000000010cd8a3e8: sub    $0x30,%rsp
  0x000000010cd8a3ec: movabs $0x120f0f5d0,%rax 
  ;   {metadata(method data for {method} {...} 'add' '(JJ)J' in 'Test')}

This same address seems to be used for a number of other assignments for different compiled methods, and I've not seen where that memory location is read from:

# {method} {0x0000000120000090} 'indexOf' '(I)I' in 'java/lang/String'
[Verified Entry Point]
0x000000010c29bfa0: mov    DWORD PTR [rsp-0x14000],eax
...
# {method} {0x00000001203fb410} 'add' '(JJ)J' in 'Test'
[Verified Entry Point]
0x000000010c29c3a0: mov    DWORD PTR [rsp-0x14000],eax

I'm generating this from this piece of Java code:

public class Test {
  public static void main(String args[]) {
    long total = 0;
    for(int i=0;i<20_000;i++) {
      total = add(total,i);
    }
    System.out.println("Total is: " + total);
  }
  public static long add(long a, long b) {
    return a+b;
  }
}

and I'm running this with:

$ java -XX:CompileCommand=print,*.* Test

The version of Java I'm using is:

$ java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

and it's on OSX 10.11.4

$ uname -a
Darwin alblue.local 15.4.0 Darwin Kernel Version 15.4.0: Fri Feb 26 22:08:05 PST 2016; root:xnu-3248.40.184~3/RELEASE_X86_64 x86_64

Can anyone explain what this is doing, and whether it would cause contention writing to that address? I've seen different numbers used for the offset in different JVMs and with different examples, but when I have seen it being used the number has remained constant for all invocations of the JVM in the same session.


Solution

  • It's called stackbanging. I can't find much documentation on the details, but a cursory google search indicates that it serves several purposes, such as notifying the VM through a page fault handler about the need for stack growth and ensuring it has enough headroom for internal events such as method deoptimization or unwinding the stack in case of asynchronous exceptions such as SOE or OOME.