3 questions

Peter Helfer peter.helfer.java at gmail.com
Fri Oct 12 12:51:15 PDT 2007


Hi all

1) how far is the release of the disassembler to the public, specifically:
x86 ?

2) I'd like to allocate a pool of threads (JavaThreads) in the VM, and keep
them waiting until I figure out, what entrypoint they should take.

Now the plan would be to keep the threads preallocated, and let them wait on
a condition variable to be released. It seems only JavaThread(ThreadFunction
entry_point, size_t stack_size = 0) is to be used, the other constructor is
only for the main thread & jni, right ?

Now I would provide a function matching 'typedef void
(*ThreadFunction)(JavaThread*, TRAPS)' to the constructor, add to my pool
(just an array so far), and invoke Thread::start():


// assume JavaThread is extended by
// - a monitor (runtime/mutex.hpp) '_sleepVar'
// - an additional entry point of type address '_newentry' which should be
either the entry point of  the interpreter when jumping back from a method
(assumed that bcp is correctly updated), or any instruction in a compiled
version of a java method. I assume (for now) that the frame is correctly
initialized to continue at that point.

while(true){
      _sleepVar.wait(no_safepoint_check = false, timeout = 0,
as_suspend_equivalent = !_as_suspend_equivalent_flag);
      // what is that last flag doing ?

      if(_newentry != NULL){
            // In GCC AT&T syntax: Jump to _newentry (clobbers eax)
                     asm ("movl %0, %%eax; \n\t"
                              "jmp %eax"

             :                /* output: none */
             :"r"(_newentry)  /* input: _newentry */
             :  "%eax"        /* clobbered register */
             );

           }
      }

      // return point of function
      _newentry = NULL;

      // do some housecleaning
      run_housecleaning();

}


.. and some starting function:

jbool start_entrypoint(address entrypoint){
     assert(entrypoint);
     JavaThread* thread = _singleton_pool.getThread();
     if(thread != NULL){
        thread->set_new_entry(entrypoint);  // setter for entry point
        thread->getSleepVar()->notify();    // getter for sleep var
        return true;
     }
     return false;
}


Does this look feasible or is there a better way to go for ? Is there a
thread pool around (apart from java.util.concurrent.Executor et al.) ?


3)
I know that the interpreter jumps away using jump_from(Method, temp) to jump
to either the compiled entry (_code->entry()) or again the interpreter
(_i2i_entry, _from_compiled initially). This entry corresponds to the type
of method (native, synchronized, accessors, empty, intrinsic aka math
functions, or zerolocals aka normal), and has been determined at link time
(methodOopDesc:link_method).

I know as well, that many return stubs are generated, in order to jump back
into the interpreter and pick up where it left, as described in
AbstractInterpreterGenerator::generate_return_entry_for(TosState state, int
step) and stored into 'static Entrypoint
Interpreter::_return_entry[number_of_return_entries = 9].

If I'm not totally mistaken, the _return_entry[3] are for invokespecial,
static, virtual and [5] for invokeinterface, because of:

address AbstractInterpreter::return_entry(TosState state, int length) {
  guarantee(0 <= length && length < Interpreter::number_of_return_entries,
"illegal length");
  return _return_entry[length].entry(state);
}

.. and in TemplateTable_i486.cpp, prepare_invoke:

  // compute return type
  __ shrl(flags, ConstantPoolCacheEntry::tosBits);
  // Make sure we don't need to mask flags for tosBits after the above shift
  ConstantPoolCacheEntry::verify_tosBits();
  // load return address
  { const int table =
      is_invokeinterface
      ? (int)Interpreter::return_5_addrs_by_index_table()
      : (int)Interpreter::return_3_addrs_by_index_table();
    __ movl(flags, Address(noreg, flags, Address::times_4, table));
  }

  // push return address
  __ pushl(flags);

  // Restore flag value from the constant pool cache, and restore rsi
  // for later null checks.  rsi is the bytecode pointer
  if (save_flags) {
    __ movl(flags, rsi);
    __ restore_bcp();
  }


So this code determines by checking the TosBits in the child method, what
kind of return value it has to expect, computes the offset in either
return_X_addrs_by_index_table and pushes that value on the stack ?
So this means that it expects the result of that method in RAX (+RDX for
long/double), irregarding of whether the child method is compiled or the
interpreter ?


Now if I wanted to reroute the return call, I could change this pushed
return address to another stub, which would save the result (RAX/RDX), do
some freaky stuff like call the VM again, and finally return to the entry
beforehand exchanged ?


Thanks, Peter





PS @Steve: your hint helped really well, thanks!
To bring Steve's answer again to the list - I had to add the save of the BCP
before leaving it, otherwise the assertion would fail in
methodOop::bcp_from(int bci)

void InterpreterMacroAssembler::jump_from_interpreted(Register method,
Register temp) {
if(MyMagicEnabled)
    Label ignore;

    cmpw(Address(method, methodOopDesc::myFlag_offset()), myFlagValue);
    jcc(Assembler::aboveEqual, ignore);

    restore_bcp(); // this saves the current BCP into the frame and allows
to jump into the VM
    call_VM(temp, CAST_FROM_FN_PTR(address, MyCode::setMyFlagValueRight),
temp, true);

    bind(ignore);
  }
   // add the custom code BEFORE moving the last_sp into place

   // set sender sp
  leal(rsi, Address(rsp, wordSize));
  // record last_sp
  movl(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize),
rsi);

  //here is the jvti in between

  //finally jump!
  jmp(Address(method, methodOopDesc::from_interpreted_offset()));
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/attachments/20071012/1f499761/attachment.html 


More information about the hotspot-runtime-dev mailing list