6

We know the calling convention that "first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX (R10 in the Linux kernel interface[17]:124), R8, and R9" for the c/c++ code in the Linux platform based on the following article. https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions

However what's the calling convention for the Java code in Linux platform (suppose the JVM is hotspot)? The following is example, what's registers store the four parameters?

protected void caller( ) {
callee(1,"123", 123,1)
}

protected void callee(int a,String b, Integer c,Object d) {

}
Florian Albrecht
  • 2,266
  • 1
  • 20
  • 25
YuFeng Shen
  • 1,475
  • 1
  • 17
  • 41
  • 2
    I do not think that they are stored in any registers. Java is a virtual machine, having its own registers and stacks. – Florian Albrecht Jan 17 '17 at 09:44
  • 2
    I suppose you mean conventions, not conversations? Java is a VM language. It has its [own machine definition spec](https://docs.oracle.com/javase/specs/jvms/se7/html/), but there is no necessary connection between the X86 registers and the JVM registers. – RealSkeptic Jan 17 '17 at 09:44
  • However ,finally the Java code would be jitted, so they finally would be stored in the X86 registers. However I don't know how Jit compiler would assign the Java parameters to X86 registers. I have no idea if there are any rules exists. – YuFeng Shen Jan 17 '17 at 10:13
  • @FlorianAlbrecht : JVM is not using registers, it is completely stack based VM – rkosegi Jan 17 '17 at 18:54

2 Answers2

13

It is not specified how JVM calls Java methods internally. Various JVM implementations may follow different calling conventions. Here is how it works in HotSpot JVM on Linux x64.

  • A Java method may run in interpreter or it can be JIT-compiled.
  • Interpreted and compiled code use different calling conventions.

1. Interpreter method entry

Each Java method has an entry point into the interpreter. This entry is used to jump from an interpreted method to another interpreted method.

  • All arguments are passed on stack, from bottom to top.
  • rbx contains a pointer to Method* structure - internal metadata of a method being called.
  • r13 holds sender_sp - stack pointer of a caller method. It may differ from rsp + 8 if c2i adapter is used (see below).

More details about interpreter entries in HotSpot source code: templateInterpreter_x86_64.cpp.

2. Compiled entry

A compiled method has its own entry point. Compiled code calls compiled methods via this entry.

  • Up to 6 first integer arguments are passed in registers: rsi, rdx, rcx, r8, r9, rdi. Non-static methods receive this reference as the first argument in rsi.
  • Up to 8 floating point arguments are passed in xmm0 ... xmm7 registers.
  • All other arguments are passed on stack from top to bottom.

This convention is nicely illustrated in assembler_x86.hpp:

    |-------------------------------------------------------|
    | c_rarg0   c_rarg1  c_rarg2 c_rarg3 c_rarg4 c_rarg5    |
    |-------------------------------------------------------|
    | rcx       rdx      r8      r9      rdi*    rsi*       | windows (* not a c_rarg)
    | rdi       rsi      rdx     rcx     r8      r9         | solaris/linux
    |-------------------------------------------------------|
    | j_rarg5   j_rarg0  j_rarg1 j_rarg2 j_rarg3 j_rarg4    |
    |-------------------------------------------------------|

You may notice that Java calling convention looks similar to C calling convention but shifted by one argument right. This is done intentionally to avoid extra register shuffling when calling JNI methods (you know, JNI methods have extra JNIEnv* argument prepended to method parameters).

3. Adapters

Java methods may have two more entry points: c2i and i2c adapters. These adapters are pieces of dynamically generated code that convert compiled calling convention to interpreter layout and vice versa. с2i and i2c entry points are used to call interpreted method from compiled code and compiled method from interpreted code respectively.


P.S. It does not usually matter how JVM calls methods internally, because these are just implementation details opaque to end-user. Moreover, these details may change even in a minor JDK update. However, I know at least one case when the knowledge of Java calling convention may appear useful - when analysing JVM crash dumps.

apangin
  • 92,924
  • 10
  • 193
  • 247
  • Thank you so much apangin, you mentioned Java calling convention is useful when analysing the dump, and as I know the gdb just call print the c/c++ call stack by the command backtrace , then frame N(N is the thread number). and x/20i $pc-64 to check the assembly code. However the Java method can not be printed in the call stack from gdb, so how to check the assembly code for the Java method from the dump? – YuFeng Shen Jan 18 '17 at 02:51
  • @Hermas HotSpot Serviceability Agent can do this for you. I've mentioned it in [another answer](http://stackoverflow.com/questions/41715532/how-to-check-the-jitted-java-method-parameters-from-the-jvm-core-dump-through-as/41730565#41730565). – apangin Jan 18 '17 at 22:29
  • Please kindly help to check this related question http://stackoverflow.com/questions/42313695/which-register-used-to-pass-the-jnienv-for-jni-methods – YuFeng Shen Feb 18 '17 at 10:00
0

There are no specific rules, as both, caller and callee are under the JVM’s control, so there is no need to adhere to a convention.

Especially when you consider the case that both methods have been compiled to native code, as this is usually triggered when this code path turns out to be a hotspot. In this case, it is very likely that the code of the invoked method gets inlined into the callers code, enabling subsequent code transformation which will turn it into something that hardly ever resembles the source code you have written. Instead of referring to parameter variables, the inlined version of the invoked method may refer to the values or constants from which the invocation arguments originally were derived. (That applies especially to your example where all arguments are constant values)

See Static single assignment form, Global value numbering, and Sparse conditional constant propagation for more details. The assignment of variables to registers happens only after all these higher level optimizations on the remaining variables, so it doesn’t apply to the parameters in any fixed scheme, if their variables still exist.

In case, the call hasn’t been inlined, there are several different scenarios, each probably having their own calling convention:

  • Interpreted execution of the caller enters interpreted execution of the callee
  • Interpreted execution of the caller enters an already compiled callee
  • Native code not having inlined the callee enters interpreted execution of the callee
  • Native code not having inlined the callee enters an already compiled callee
Holger
  • 285,553
  • 42
  • 434
  • 765