Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: taken from callingconv.txt, by Arnold Schwaighofer

Patch name: tcall.patchtailc.patch

Call Sites

in native code:

  • vtable calls
    assumption each method's vtable entry contains either
  • native function
  • c2i adapter
  • static calls go to
  • native function
  • c2i adapter
    How is recompiling/back to interpreter dealt with (patch native method? how garbage collect dead methods)?
  • byte code interpreter?
    vtable: probably jumps to methods vtable entry?

Compile to Interpreted Code Adapter C2I

No Format
titlefrom sharedRuntime_x86_32.cpp

// Before we get into the guts of the C2I adapter, see if we should be here
// at all.  We've come from compiled code and are attempting to jump to the
// interpreter, which means the caller made a static call to get here
// (vcalls always get a compiled target if there is one).  Check for a
// compiled target.  If there is one, we need to patch the caller's call.
  • c2i_unverified_entry: ?
  • c2i_entry ?
  • gen_c2i_adapter only entered from a static call.
  • patches call site (call in caller) to compiled code entry if there is compile code
  • sets up area for arguments of interpreter on top of compiled callee's stack (SP = SP+total_args_passed* Interpreter::stackElementSize)
  • copies arguments from registers and callers outgoing argument area to area for arguments of interpreter
  • remembers old rsp (real rsp of caller) in rsi, which is stored as old sp in the interpreter to a stack slot - somehow)
  • moves return address
No Format
title"c2i entry frame"

                            . ..............
                            .              .  Caller frame
                     .      .              .
                     .      .              .
                     .      .    argn      .
                     .      .    arg1      ........
                   .....    ................      .
                    ...     .    argn+m    . .    .  Created by c2i adapter (m=number of args in regs)
                     .      .    arg1      ........
                            . ..............
                            .   RetAddr    .  InterpreterFrame (Callee) setup will start with
                            .              .  templateInterpreter_x86_32.cpp:
                            .              .   generate_normal_entry(bool synchronized)
  • generate_normal_entry:
    sets up interpreter frame: see frame_x86.hpp (expects rsi to hold old sp in case)

Tail Call Situations

Described by three relevant frames. The function performing the tail call is called self. The tail called function is called callee in the following. The function that has called the tail calling function is called calller.

A code example leading to this situation.

No Format

public static int caller(int a) {
  int x = self(a);
  return x+1;
}

public static int self(int a) {
  return tail call calllee(a);
}

public static int callee(int a) {
  return a+1;
}

assumption: sibling call (callee less or equal arg space requirement than self)

No Format

self: compiled
      callee compiled  /  interpreted
c
a
l c      mov args          mov args + c2i entry
l
e
r
  i      mov args         mov args + c2i entry
      (works because
       of i2c adapter in
       caller. on ret
        interpreter will
         restore sp)


self: interpreted
      callee compiled  /  interpreted
c
a
l c   mov args for compiled   move args for compiled + c2i entry
l       (use old sp)
e
r
  i      mov args int.        mov args int.
      (works because
       of i2c adapter)

interpreter call setup sequence:

No Format

registers:
  rsi  during bytecode execution holds bcp/bci

  rdi
  rax
  rbx
  rcx
  rdx
  rbp
  rsp

prepare_invoke(invokevirtual)

  • push parameters to operand stack (implicitly)
  • save bci (from rsi) to stack slot in current method's frame not for tail call
  • load constant pool cache entry method=rbx flags=rdx tail call
  • load receiver = rcx (flags contain number parameters) tail call
  • push returnadress (interpreter entry, index by numberofreturnaddrs (3 static,vtable, special or 5 - interface) and tos_state (itos, vtos, atos, ...)
  • restore bcp (rsi)

state after prepare_invoke

No Format

...............
.   RET ADDR  .   ... interpreter entry [numberretaddrs][tos_state]
...............   rbx = method (index or pointer (vfinal))
.  PARAM N    .   rcx = receiver
...............   rdx = cpCache Flags
.  PARM  1    .   rsi = bcp
...............

invokevirtual_helper

  • test for final method (using the cpCache flags in rdx)
  • final rbx = method pointer
  • null check receiver =rcx
  • profile_final_call
  • jump_from_interpreted(method, rax)
  • vtable get receiver klass, rbx = method index
  • null_check(recv, oopDesc::klass_offset_in_bytes());
  • rax = klass
  • profile_virtual_call
  • compute target (klass ptr + vtable index
    const int base = instanceKlass::vtable_start_offset() * wordSize;
    __ movl(method, Address(rax, index, Address::times_4, base + vtableEntry::method_offset_in_bytes()));
  • jump_from_interpreted(method, rdx)
    // set sender sp
    leal(rsi, Address(rsp, wordSize));
    // record last_sp
    movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), rsi);
No Format

...............
.   RET ADDR  .   ... interpreter entry [numberretaddrs][tos_state]
...............   rbx = method (oop ?) pointer
.  PARAM N    .   rcx = receiver
...............   rdx = cpCache Flags (or garbage if profiles)
.  PARM  1    .   rsi = rsp stack pointer
...............   rax = klass ptr
                  rbp = base pointer/ dynlink
                  rsp points to RET ADDR 
                  rdi = garbage 

invoketailcall