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