mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 19:14:38 +02:00
6537506: Provide a mechanism for specifying Java-level USDT-like dtrace probes
Initial checkin of JSDT code Reviewed-by: acorn, sbohne
This commit is contained in:
parent
849e0dfc44
commit
f072bc9d3f
26 changed files with 2935 additions and 26 deletions
|
@ -472,3 +472,7 @@ address NativeGeneralJump::jump_destination() const {
|
|||
else
|
||||
return addr_at(0) + length + sbyte_at(offset);
|
||||
}
|
||||
|
||||
bool NativeInstruction::is_dtrace_trap() {
|
||||
return (*(int32_t*)this & 0xff) == 0xcc;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ class NativeInstruction VALUE_OBJ_CLASS_SPEC {
|
|||
};
|
||||
|
||||
bool is_nop() { return ubyte_at(0) == nop_instruction_code; }
|
||||
bool is_dtrace_trap();
|
||||
inline bool is_call();
|
||||
inline bool is_illegal();
|
||||
inline bool is_return();
|
||||
|
|
|
@ -1880,6 +1880,379 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
|
|||
|
||||
}
|
||||
|
||||
#ifdef HAVE_DTRACE_H
|
||||
// ---------------------------------------------------------------------------
|
||||
// Generate a dtrace nmethod for a given signature. The method takes arguments
|
||||
// in the Java compiled code convention, marshals them to the native
|
||||
// abi and then leaves nops at the position you would expect to call a native
|
||||
// function. When the probe is enabled the nops are replaced with a trap
|
||||
// instruction that dtrace inserts and the trace will cause a notification
|
||||
// to dtrace.
|
||||
//
|
||||
// The probes are only able to take primitive types and java/lang/String as
|
||||
// arguments. No other java types are allowed. Strings are converted to utf8
|
||||
// strings so that from dtrace point of view java strings are converted to C
|
||||
// strings. There is an arbitrary fixed limit on the total space that a method
|
||||
// can use for converting the strings. (256 chars per string in the signature).
|
||||
// So any java string larger then this is truncated.
|
||||
|
||||
nmethod *SharedRuntime::generate_dtrace_nmethod(
|
||||
MacroAssembler *masm, methodHandle method) {
|
||||
|
||||
// generate_dtrace_nmethod is guarded by a mutex so we are sure to
|
||||
// be single threaded in this method.
|
||||
assert(AdapterHandlerLibrary_lock->owned_by_self(), "must be");
|
||||
|
||||
// Fill in the signature array, for the calling-convention call.
|
||||
int total_args_passed = method->size_of_parameters();
|
||||
|
||||
BasicType* in_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed);
|
||||
VMRegPair *in_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed);
|
||||
|
||||
// The signature we are going to use for the trap that dtrace will see
|
||||
// java/lang/String is converted. We drop "this" and any other object
|
||||
// is converted to NULL. (A one-slot java/lang/Long object reference
|
||||
// is converted to a two-slot long, which is why we double the allocation).
|
||||
BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed * 2);
|
||||
VMRegPair* out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed * 2);
|
||||
|
||||
int i=0;
|
||||
int total_strings = 0;
|
||||
int first_arg_to_pass = 0;
|
||||
int total_c_args = 0;
|
||||
int box_offset = java_lang_boxing_object::value_offset_in_bytes();
|
||||
|
||||
if( !method->is_static() ) { // Pass in receiver first
|
||||
in_sig_bt[i++] = T_OBJECT;
|
||||
first_arg_to_pass = 1;
|
||||
}
|
||||
|
||||
// We need to convert the java args to where a native (non-jni) function
|
||||
// would expect them. To figure out where they go we convert the java
|
||||
// signature to a C signature.
|
||||
|
||||
SignatureStream ss(method->signature());
|
||||
for ( ; !ss.at_return_type(); ss.next()) {
|
||||
BasicType bt = ss.type();
|
||||
in_sig_bt[i++] = bt; // Collect remaining bits of signature
|
||||
out_sig_bt[total_c_args++] = bt;
|
||||
if( bt == T_OBJECT) {
|
||||
symbolOop s = ss.as_symbol_or_null();
|
||||
if (s == vmSymbols::java_lang_String()) {
|
||||
total_strings++;
|
||||
out_sig_bt[total_c_args-1] = T_ADDRESS;
|
||||
} else if (s == vmSymbols::java_lang_Boolean() ||
|
||||
s == vmSymbols::java_lang_Character() ||
|
||||
s == vmSymbols::java_lang_Byte() ||
|
||||
s == vmSymbols::java_lang_Short() ||
|
||||
s == vmSymbols::java_lang_Integer() ||
|
||||
s == vmSymbols::java_lang_Float()) {
|
||||
out_sig_bt[total_c_args-1] = T_INT;
|
||||
} else if (s == vmSymbols::java_lang_Long() ||
|
||||
s == vmSymbols::java_lang_Double()) {
|
||||
out_sig_bt[total_c_args-1] = T_LONG;
|
||||
out_sig_bt[total_c_args++] = T_VOID;
|
||||
}
|
||||
} else if ( bt == T_LONG || bt == T_DOUBLE ) {
|
||||
in_sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots
|
||||
out_sig_bt[total_c_args++] = T_VOID;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i==total_args_passed, "validly parsed signature");
|
||||
|
||||
// Now get the compiled-Java layout as input arguments
|
||||
int comp_args_on_stack;
|
||||
comp_args_on_stack = SharedRuntime::java_calling_convention(
|
||||
in_sig_bt, in_regs, total_args_passed, false);
|
||||
|
||||
// Now figure out where the args must be stored and how much stack space
|
||||
// they require (neglecting out_preserve_stack_slots).
|
||||
|
||||
int out_arg_slots;
|
||||
out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args);
|
||||
|
||||
// Calculate the total number of stack slots we will need.
|
||||
|
||||
// First count the abi requirement plus all of the outgoing args
|
||||
int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots;
|
||||
|
||||
// Now space for the string(s) we must convert
|
||||
|
||||
int* string_locs = NEW_RESOURCE_ARRAY(int, total_strings + 1);
|
||||
for (i = 0; i < total_strings ; i++) {
|
||||
string_locs[i] = stack_slots;
|
||||
stack_slots += max_dtrace_string_size / VMRegImpl::stack_slot_size;
|
||||
}
|
||||
|
||||
// + 2 for return address (which we own) and saved rbp,
|
||||
|
||||
stack_slots += 2;
|
||||
|
||||
// Ok The space we have allocated will look like:
|
||||
//
|
||||
//
|
||||
// FP-> | |
|
||||
// |---------------------|
|
||||
// | string[n] |
|
||||
// |---------------------| <- string_locs[n]
|
||||
// | string[n-1] |
|
||||
// |---------------------| <- string_locs[n-1]
|
||||
// | ... |
|
||||
// | ... |
|
||||
// |---------------------| <- string_locs[1]
|
||||
// | string[0] |
|
||||
// |---------------------| <- string_locs[0]
|
||||
// | outbound memory |
|
||||
// | based arguments |
|
||||
// | |
|
||||
// |---------------------|
|
||||
// | |
|
||||
// SP-> | out_preserved_slots |
|
||||
//
|
||||
//
|
||||
|
||||
// Now compute actual number of stack words we need rounding to make
|
||||
// stack properly aligned.
|
||||
stack_slots = round_to(stack_slots, 2 * VMRegImpl::slots_per_word);
|
||||
|
||||
int stack_size = stack_slots * VMRegImpl::stack_slot_size;
|
||||
|
||||
intptr_t start = (intptr_t)__ pc();
|
||||
|
||||
// First thing make an ic check to see if we should even be here
|
||||
|
||||
// We are free to use all registers as temps without saving them and
|
||||
// restoring them except rbp. rbp, is the only callee save register
|
||||
// as far as the interpreter and the compiler(s) are concerned.
|
||||
|
||||
const Register ic_reg = rax;
|
||||
const Register receiver = rcx;
|
||||
Label hit;
|
||||
Label exception_pending;
|
||||
|
||||
|
||||
__ verify_oop(receiver);
|
||||
__ cmpl(ic_reg, Address(receiver, oopDesc::klass_offset_in_bytes()));
|
||||
__ jcc(Assembler::equal, hit);
|
||||
|
||||
__ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
|
||||
|
||||
// verified entry must be aligned for code patching.
|
||||
// and the first 5 bytes must be in the same cache line
|
||||
// if we align at 8 then we will be sure 5 bytes are in the same line
|
||||
__ align(8);
|
||||
|
||||
__ bind(hit);
|
||||
|
||||
int vep_offset = ((intptr_t)__ pc()) - start;
|
||||
|
||||
|
||||
// The instruction at the verified entry point must be 5 bytes or longer
|
||||
// because it can be patched on the fly by make_non_entrant. The stack bang
|
||||
// instruction fits that requirement.
|
||||
|
||||
// Generate stack overflow check
|
||||
|
||||
|
||||
if (UseStackBanging) {
|
||||
if (stack_size <= StackShadowPages*os::vm_page_size()) {
|
||||
__ bang_stack_with_offset(StackShadowPages*os::vm_page_size());
|
||||
} else {
|
||||
__ movl(rax, stack_size);
|
||||
__ bang_stack_size(rax, rbx);
|
||||
}
|
||||
} else {
|
||||
// need a 5 byte instruction to allow MT safe patching to non-entrant
|
||||
__ fat_nop();
|
||||
}
|
||||
|
||||
assert(((int)__ pc() - start - vep_offset) >= 5,
|
||||
"valid size for make_non_entrant");
|
||||
|
||||
// Generate a new frame for the wrapper.
|
||||
__ enter();
|
||||
|
||||
// -2 because return address is already present and so is saved rbp,
|
||||
if (stack_size - 2*wordSize != 0) {
|
||||
__ subl(rsp, stack_size - 2*wordSize);
|
||||
}
|
||||
|
||||
// Frame is now completed as far a size and linkage.
|
||||
|
||||
int frame_complete = ((intptr_t)__ pc()) - start;
|
||||
|
||||
// First thing we do store all the args as if we are doing the call.
|
||||
// Since the C calling convention is stack based that ensures that
|
||||
// all the Java register args are stored before we need to convert any
|
||||
// string we might have.
|
||||
|
||||
int sid = 0;
|
||||
int c_arg, j_arg;
|
||||
int string_reg = 0;
|
||||
|
||||
for (j_arg = first_arg_to_pass, c_arg = 0 ;
|
||||
j_arg < total_args_passed ; j_arg++, c_arg++ ) {
|
||||
|
||||
VMRegPair src = in_regs[j_arg];
|
||||
VMRegPair dst = out_regs[c_arg];
|
||||
assert(dst.first()->is_stack() || in_sig_bt[j_arg] == T_VOID,
|
||||
"stack based abi assumed");
|
||||
|
||||
switch (in_sig_bt[j_arg]) {
|
||||
|
||||
case T_ARRAY:
|
||||
case T_OBJECT:
|
||||
if (out_sig_bt[c_arg] == T_ADDRESS) {
|
||||
// Any register based arg for a java string after the first
|
||||
// will be destroyed by the call to get_utf so we store
|
||||
// the original value in the location the utf string address
|
||||
// will eventually be stored.
|
||||
if (src.first()->is_reg()) {
|
||||
if (string_reg++ != 0) {
|
||||
simple_move32(masm, src, dst);
|
||||
}
|
||||
}
|
||||
} else if (out_sig_bt[c_arg] == T_INT || out_sig_bt[c_arg] == T_LONG) {
|
||||
// need to unbox a one-word value
|
||||
Register in_reg = rax;
|
||||
if ( src.first()->is_reg() ) {
|
||||
in_reg = src.first()->as_Register();
|
||||
} else {
|
||||
simple_move32(masm, src, in_reg->as_VMReg());
|
||||
}
|
||||
Label skipUnbox;
|
||||
__ movl(Address(rsp, reg2offset_out(dst.first())), NULL_WORD);
|
||||
if ( out_sig_bt[c_arg] == T_LONG ) {
|
||||
__ movl(Address(rsp, reg2offset_out(dst.second())), NULL_WORD);
|
||||
}
|
||||
__ testl(in_reg, in_reg);
|
||||
__ jcc(Assembler::zero, skipUnbox);
|
||||
assert(dst.first()->is_stack() &&
|
||||
(!dst.second()->is_valid() || dst.second()->is_stack()),
|
||||
"value(s) must go into stack slots");
|
||||
if ( out_sig_bt[c_arg] == T_LONG ) {
|
||||
__ movl(rbx, Address(in_reg,
|
||||
box_offset + VMRegImpl::stack_slot_size));
|
||||
__ movl(Address(rsp, reg2offset_out(dst.second())), rbx);
|
||||
}
|
||||
__ movl(in_reg, Address(in_reg, box_offset));
|
||||
__ movl(Address(rsp, reg2offset_out(dst.first())), in_reg);
|
||||
__ bind(skipUnbox);
|
||||
} else {
|
||||
// Convert the arg to NULL
|
||||
__ movl(Address(rsp, reg2offset_out(dst.first())), NULL_WORD);
|
||||
}
|
||||
if (out_sig_bt[c_arg] == T_LONG) {
|
||||
assert(out_sig_bt[c_arg+1] == T_VOID, "must be");
|
||||
++c_arg; // Move over the T_VOID To keep the loop indices in sync
|
||||
}
|
||||
break;
|
||||
|
||||
case T_VOID:
|
||||
break;
|
||||
|
||||
case T_FLOAT:
|
||||
float_move(masm, src, dst);
|
||||
break;
|
||||
|
||||
case T_DOUBLE:
|
||||
assert( j_arg + 1 < total_args_passed &&
|
||||
in_sig_bt[j_arg + 1] == T_VOID, "bad arg list");
|
||||
double_move(masm, src, dst);
|
||||
break;
|
||||
|
||||
case T_LONG :
|
||||
long_move(masm, src, dst);
|
||||
break;
|
||||
|
||||
case T_ADDRESS: assert(false, "found T_ADDRESS in java args");
|
||||
|
||||
default:
|
||||
simple_move32(masm, src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we must convert any string we have to utf8
|
||||
//
|
||||
|
||||
for (sid = 0, j_arg = first_arg_to_pass, c_arg = 0 ;
|
||||
sid < total_strings ; j_arg++, c_arg++ ) {
|
||||
|
||||
if (out_sig_bt[c_arg] == T_ADDRESS) {
|
||||
|
||||
Address utf8_addr = Address(
|
||||
rsp, string_locs[sid++] * VMRegImpl::stack_slot_size);
|
||||
__ leal(rax, utf8_addr);
|
||||
|
||||
// The first string we find might still be in the original java arg
|
||||
// register
|
||||
VMReg orig_loc = in_regs[j_arg].first();
|
||||
Register string_oop;
|
||||
|
||||
// This is where the argument will eventually reside
|
||||
Address dest = Address(rsp, reg2offset_out(out_regs[c_arg].first()));
|
||||
|
||||
if (sid == 1 && orig_loc->is_reg()) {
|
||||
string_oop = orig_loc->as_Register();
|
||||
assert(string_oop != rax, "smashed arg");
|
||||
} else {
|
||||
|
||||
if (orig_loc->is_reg()) {
|
||||
// Get the copy of the jls object
|
||||
__ movl(rcx, dest);
|
||||
} else {
|
||||
// arg is still in the original location
|
||||
__ movl(rcx, Address(rbp, reg2offset_in(orig_loc)));
|
||||
}
|
||||
string_oop = rcx;
|
||||
|
||||
}
|
||||
Label nullString;
|
||||
__ movl(dest, NULL_WORD);
|
||||
__ testl(string_oop, string_oop);
|
||||
__ jcc(Assembler::zero, nullString);
|
||||
|
||||
// Now we can store the address of the utf string as the argument
|
||||
__ movl(dest, rax);
|
||||
|
||||
// And do the conversion
|
||||
__ call_VM_leaf(CAST_FROM_FN_PTR(
|
||||
address, SharedRuntime::get_utf), string_oop, rax);
|
||||
__ bind(nullString);
|
||||
}
|
||||
|
||||
if (in_sig_bt[j_arg] == T_OBJECT && out_sig_bt[c_arg] == T_LONG) {
|
||||
assert(out_sig_bt[c_arg+1] == T_VOID, "must be");
|
||||
++c_arg; // Move over the T_VOID To keep the loop indices in sync
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ok now we are done. Need to place the nop that dtrace wants in order to
|
||||
// patch in the trap
|
||||
|
||||
int patch_offset = ((intptr_t)__ pc()) - start;
|
||||
|
||||
__ nop();
|
||||
|
||||
|
||||
// Return
|
||||
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
|
||||
__ flush();
|
||||
|
||||
nmethod *nm = nmethod::new_dtrace_nmethod(
|
||||
method, masm->code(), vep_offset, patch_offset, frame_complete,
|
||||
stack_slots / VMRegImpl::slots_per_word);
|
||||
return nm;
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE_DTRACE_H
|
||||
|
||||
// this function returns the adjust size (in number of words) to a c2i adapter
|
||||
// activation for use during deoptimization
|
||||
int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals ) {
|
||||
|
|
|
@ -1886,6 +1886,627 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
|
|||
|
||||
}
|
||||
|
||||
#ifdef HAVE_DTRACE_H
|
||||
// ---------------------------------------------------------------------------
|
||||
// Generate a dtrace nmethod for a given signature. The method takes arguments
|
||||
// in the Java compiled code convention, marshals them to the native
|
||||
// abi and then leaves nops at the position you would expect to call a native
|
||||
// function. When the probe is enabled the nops are replaced with a trap
|
||||
// instruction that dtrace inserts and the trace will cause a notification
|
||||
// to dtrace.
|
||||
//
|
||||
// The probes are only able to take primitive types and java/lang/String as
|
||||
// arguments. No other java types are allowed. Strings are converted to utf8
|
||||
// strings so that from dtrace point of view java strings are converted to C
|
||||
// strings. There is an arbitrary fixed limit on the total space that a method
|
||||
// can use for converting the strings. (256 chars per string in the signature).
|
||||
// So any java string larger then this is truncated.
|
||||
|
||||
static int fp_offset[ConcreteRegisterImpl::number_of_registers] = { 0 };
|
||||
static bool offsets_initialized = false;
|
||||
|
||||
|
||||
nmethod *SharedRuntime::generate_dtrace_nmethod(MacroAssembler *masm,
|
||||
methodHandle method) {
|
||||
|
||||
|
||||
// generate_dtrace_nmethod is guarded by a mutex so we are sure to
|
||||
// be single threaded in this method.
|
||||
assert(AdapterHandlerLibrary_lock->owned_by_self(), "must be");
|
||||
|
||||
if (!offsets_initialized) {
|
||||
fp_offset[c_rarg0->as_VMReg()->value()] = -1 * wordSize;
|
||||
fp_offset[c_rarg1->as_VMReg()->value()] = -2 * wordSize;
|
||||
fp_offset[c_rarg2->as_VMReg()->value()] = -3 * wordSize;
|
||||
fp_offset[c_rarg3->as_VMReg()->value()] = -4 * wordSize;
|
||||
fp_offset[c_rarg4->as_VMReg()->value()] = -5 * wordSize;
|
||||
fp_offset[c_rarg5->as_VMReg()->value()] = -6 * wordSize;
|
||||
|
||||
fp_offset[c_farg0->as_VMReg()->value()] = -7 * wordSize;
|
||||
fp_offset[c_farg1->as_VMReg()->value()] = -8 * wordSize;
|
||||
fp_offset[c_farg2->as_VMReg()->value()] = -9 * wordSize;
|
||||
fp_offset[c_farg3->as_VMReg()->value()] = -10 * wordSize;
|
||||
fp_offset[c_farg4->as_VMReg()->value()] = -11 * wordSize;
|
||||
fp_offset[c_farg5->as_VMReg()->value()] = -12 * wordSize;
|
||||
fp_offset[c_farg6->as_VMReg()->value()] = -13 * wordSize;
|
||||
fp_offset[c_farg7->as_VMReg()->value()] = -14 * wordSize;
|
||||
|
||||
offsets_initialized = true;
|
||||
}
|
||||
// Fill in the signature array, for the calling-convention call.
|
||||
int total_args_passed = method->size_of_parameters();
|
||||
|
||||
BasicType* in_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed);
|
||||
VMRegPair *in_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed);
|
||||
|
||||
// The signature we are going to use for the trap that dtrace will see
|
||||
// java/lang/String is converted. We drop "this" and any other object
|
||||
// is converted to NULL. (A one-slot java/lang/Long object reference
|
||||
// is converted to a two-slot long, which is why we double the allocation).
|
||||
BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed * 2);
|
||||
VMRegPair* out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed * 2);
|
||||
|
||||
int i=0;
|
||||
int total_strings = 0;
|
||||
int first_arg_to_pass = 0;
|
||||
int total_c_args = 0;
|
||||
int box_offset = java_lang_boxing_object::value_offset_in_bytes();
|
||||
|
||||
// Skip the receiver as dtrace doesn't want to see it
|
||||
if( !method->is_static() ) {
|
||||
in_sig_bt[i++] = T_OBJECT;
|
||||
first_arg_to_pass = 1;
|
||||
}
|
||||
|
||||
// We need to convert the java args to where a native (non-jni) function
|
||||
// would expect them. To figure out where they go we convert the java
|
||||
// signature to a C signature.
|
||||
|
||||
SignatureStream ss(method->signature());
|
||||
for ( ; !ss.at_return_type(); ss.next()) {
|
||||
BasicType bt = ss.type();
|
||||
in_sig_bt[i++] = bt; // Collect remaining bits of signature
|
||||
out_sig_bt[total_c_args++] = bt;
|
||||
if( bt == T_OBJECT) {
|
||||
symbolOop s = ss.as_symbol_or_null();
|
||||
if (s == vmSymbols::java_lang_String()) {
|
||||
total_strings++;
|
||||
out_sig_bt[total_c_args-1] = T_ADDRESS;
|
||||
} else if (s == vmSymbols::java_lang_Boolean() ||
|
||||
s == vmSymbols::java_lang_Character() ||
|
||||
s == vmSymbols::java_lang_Byte() ||
|
||||
s == vmSymbols::java_lang_Short() ||
|
||||
s == vmSymbols::java_lang_Integer() ||
|
||||
s == vmSymbols::java_lang_Float()) {
|
||||
out_sig_bt[total_c_args-1] = T_INT;
|
||||
} else if (s == vmSymbols::java_lang_Long() ||
|
||||
s == vmSymbols::java_lang_Double()) {
|
||||
out_sig_bt[total_c_args-1] = T_LONG;
|
||||
out_sig_bt[total_c_args++] = T_VOID;
|
||||
}
|
||||
} else if ( bt == T_LONG || bt == T_DOUBLE ) {
|
||||
in_sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots
|
||||
// We convert double to long
|
||||
out_sig_bt[total_c_args-1] = T_LONG;
|
||||
out_sig_bt[total_c_args++] = T_VOID;
|
||||
} else if ( bt == T_FLOAT) {
|
||||
// We convert float to int
|
||||
out_sig_bt[total_c_args-1] = T_INT;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i==total_args_passed, "validly parsed signature");
|
||||
|
||||
// Now get the compiled-Java layout as input arguments
|
||||
int comp_args_on_stack;
|
||||
comp_args_on_stack = SharedRuntime::java_calling_convention(
|
||||
in_sig_bt, in_regs, total_args_passed, false);
|
||||
|
||||
// Now figure out where the args must be stored and how much stack space
|
||||
// they require (neglecting out_preserve_stack_slots but space for storing
|
||||
// the 1st six register arguments). It's weird see int_stk_helper.
|
||||
|
||||
int out_arg_slots;
|
||||
out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args);
|
||||
|
||||
// Calculate the total number of stack slots we will need.
|
||||
|
||||
// First count the abi requirement plus all of the outgoing args
|
||||
int stack_slots = SharedRuntime::out_preserve_stack_slots() + out_arg_slots;
|
||||
|
||||
// Now space for the string(s) we must convert
|
||||
int* string_locs = NEW_RESOURCE_ARRAY(int, total_strings + 1);
|
||||
for (i = 0; i < total_strings ; i++) {
|
||||
string_locs[i] = stack_slots;
|
||||
stack_slots += max_dtrace_string_size / VMRegImpl::stack_slot_size;
|
||||
}
|
||||
|
||||
// Plus the temps we might need to juggle register args
|
||||
// regs take two slots each
|
||||
stack_slots += (Argument::n_int_register_parameters_c +
|
||||
Argument::n_float_register_parameters_c) * 2;
|
||||
|
||||
|
||||
// + 4 for return address (which we own) and saved rbp,
|
||||
|
||||
stack_slots += 4;
|
||||
|
||||
// Ok The space we have allocated will look like:
|
||||
//
|
||||
//
|
||||
// FP-> | |
|
||||
// |---------------------|
|
||||
// | string[n] |
|
||||
// |---------------------| <- string_locs[n]
|
||||
// | string[n-1] |
|
||||
// |---------------------| <- string_locs[n-1]
|
||||
// | ... |
|
||||
// | ... |
|
||||
// |---------------------| <- string_locs[1]
|
||||
// | string[0] |
|
||||
// |---------------------| <- string_locs[0]
|
||||
// | outbound memory |
|
||||
// | based arguments |
|
||||
// | |
|
||||
// |---------------------|
|
||||
// | |
|
||||
// SP-> | out_preserved_slots |
|
||||
//
|
||||
//
|
||||
|
||||
// Now compute actual number of stack words we need rounding to make
|
||||
// stack properly aligned.
|
||||
stack_slots = round_to(stack_slots, 4 * VMRegImpl::slots_per_word);
|
||||
|
||||
int stack_size = stack_slots * VMRegImpl::stack_slot_size;
|
||||
|
||||
intptr_t start = (intptr_t)__ pc();
|
||||
|
||||
// First thing make an ic check to see if we should even be here
|
||||
|
||||
// We are free to use all registers as temps without saving them and
|
||||
// restoring them except rbp. rbp, is the only callee save register
|
||||
// as far as the interpreter and the compiler(s) are concerned.
|
||||
|
||||
const Register ic_reg = rax;
|
||||
const Register receiver = rcx;
|
||||
Label hit;
|
||||
Label exception_pending;
|
||||
|
||||
|
||||
__ verify_oop(receiver);
|
||||
__ cmpl(ic_reg, Address(receiver, oopDesc::klass_offset_in_bytes()));
|
||||
__ jcc(Assembler::equal, hit);
|
||||
|
||||
__ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
|
||||
|
||||
// verified entry must be aligned for code patching.
|
||||
// and the first 5 bytes must be in the same cache line
|
||||
// if we align at 8 then we will be sure 5 bytes are in the same line
|
||||
__ align(8);
|
||||
|
||||
__ bind(hit);
|
||||
|
||||
int vep_offset = ((intptr_t)__ pc()) - start;
|
||||
|
||||
|
||||
// The instruction at the verified entry point must be 5 bytes or longer
|
||||
// because it can be patched on the fly by make_non_entrant. The stack bang
|
||||
// instruction fits that requirement.
|
||||
|
||||
// Generate stack overflow check
|
||||
|
||||
if (UseStackBanging) {
|
||||
if (stack_size <= StackShadowPages*os::vm_page_size()) {
|
||||
__ bang_stack_with_offset(StackShadowPages*os::vm_page_size());
|
||||
} else {
|
||||
__ movl(rax, stack_size);
|
||||
__ bang_stack_size(rax, rbx);
|
||||
}
|
||||
} else {
|
||||
// need a 5 byte instruction to allow MT safe patching to non-entrant
|
||||
__ fat_nop();
|
||||
}
|
||||
|
||||
assert(((uintptr_t)__ pc() - start - vep_offset) >= 5,
|
||||
"valid size for make_non_entrant");
|
||||
|
||||
// Generate a new frame for the wrapper.
|
||||
__ enter();
|
||||
|
||||
// -4 because return address is already present and so is saved rbp,
|
||||
if (stack_size - 2*wordSize != 0) {
|
||||
__ subq(rsp, stack_size - 2*wordSize);
|
||||
}
|
||||
|
||||
// Frame is now completed as far a size and linkage.
|
||||
|
||||
int frame_complete = ((intptr_t)__ pc()) - start;
|
||||
|
||||
int c_arg, j_arg;
|
||||
|
||||
// State of input register args
|
||||
|
||||
bool live[ConcreteRegisterImpl::number_of_registers];
|
||||
|
||||
live[j_rarg0->as_VMReg()->value()] = false;
|
||||
live[j_rarg1->as_VMReg()->value()] = false;
|
||||
live[j_rarg2->as_VMReg()->value()] = false;
|
||||
live[j_rarg3->as_VMReg()->value()] = false;
|
||||
live[j_rarg4->as_VMReg()->value()] = false;
|
||||
live[j_rarg5->as_VMReg()->value()] = false;
|
||||
|
||||
live[j_farg0->as_VMReg()->value()] = false;
|
||||
live[j_farg1->as_VMReg()->value()] = false;
|
||||
live[j_farg2->as_VMReg()->value()] = false;
|
||||
live[j_farg3->as_VMReg()->value()] = false;
|
||||
live[j_farg4->as_VMReg()->value()] = false;
|
||||
live[j_farg5->as_VMReg()->value()] = false;
|
||||
live[j_farg6->as_VMReg()->value()] = false;
|
||||
live[j_farg7->as_VMReg()->value()] = false;
|
||||
|
||||
|
||||
bool rax_is_zero = false;
|
||||
|
||||
// All args (except strings) destined for the stack are moved first
|
||||
for (j_arg = first_arg_to_pass, c_arg = 0 ;
|
||||
j_arg < total_args_passed ; j_arg++, c_arg++ ) {
|
||||
VMRegPair src = in_regs[j_arg];
|
||||
VMRegPair dst = out_regs[c_arg];
|
||||
|
||||
// Get the real reg value or a dummy (rsp)
|
||||
|
||||
int src_reg = src.first()->is_reg() ?
|
||||
src.first()->value() :
|
||||
rsp->as_VMReg()->value();
|
||||
|
||||
bool useless = in_sig_bt[j_arg] == T_ARRAY ||
|
||||
(in_sig_bt[j_arg] == T_OBJECT &&
|
||||
out_sig_bt[c_arg] != T_INT &&
|
||||
out_sig_bt[c_arg] != T_ADDRESS &&
|
||||
out_sig_bt[c_arg] != T_LONG);
|
||||
|
||||
live[src_reg] = !useless;
|
||||
|
||||
if (dst.first()->is_stack()) {
|
||||
|
||||
// Even though a string arg in a register is still live after this loop
|
||||
// after the string conversion loop (next) it will be dead so we take
|
||||
// advantage of that now for simpler code to manage live.
|
||||
|
||||
live[src_reg] = false;
|
||||
switch (in_sig_bt[j_arg]) {
|
||||
|
||||
case T_ARRAY:
|
||||
case T_OBJECT:
|
||||
{
|
||||
Address stack_dst(rsp, reg2offset_out(dst.first()));
|
||||
|
||||
if (out_sig_bt[c_arg] == T_INT || out_sig_bt[c_arg] == T_LONG) {
|
||||
// need to unbox a one-word value
|
||||
Register in_reg = rax;
|
||||
if ( src.first()->is_reg() ) {
|
||||
in_reg = src.first()->as_Register();
|
||||
} else {
|
||||
__ movq(rax, Address(rbp, reg2offset_in(src.first())));
|
||||
rax_is_zero = false;
|
||||
}
|
||||
Label skipUnbox;
|
||||
__ movptr(Address(rsp, reg2offset_out(dst.first())),
|
||||
(int32_t)NULL_WORD);
|
||||
__ testq(in_reg, in_reg);
|
||||
__ jcc(Assembler::zero, skipUnbox);
|
||||
|
||||
Address src1(in_reg, box_offset);
|
||||
if ( out_sig_bt[c_arg] == T_LONG ) {
|
||||
__ movq(in_reg, src1);
|
||||
__ movq(stack_dst, in_reg);
|
||||
assert(out_sig_bt[c_arg+1] == T_VOID, "must be");
|
||||
++c_arg; // skip over T_VOID to keep the loop indices in sync
|
||||
} else {
|
||||
__ movl(in_reg, src1);
|
||||
__ movl(stack_dst, in_reg);
|
||||
}
|
||||
|
||||
__ bind(skipUnbox);
|
||||
} else if (out_sig_bt[c_arg] != T_ADDRESS) {
|
||||
// Convert the arg to NULL
|
||||
if (!rax_is_zero) {
|
||||
__ xorq(rax, rax);
|
||||
rax_is_zero = true;
|
||||
}
|
||||
__ movq(stack_dst, rax);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case T_VOID:
|
||||
break;
|
||||
|
||||
case T_FLOAT:
|
||||
// This does the right thing since we know it is destined for the
|
||||
// stack
|
||||
float_move(masm, src, dst);
|
||||
break;
|
||||
|
||||
case T_DOUBLE:
|
||||
// This does the right thing since we know it is destined for the
|
||||
// stack
|
||||
double_move(masm, src, dst);
|
||||
break;
|
||||
|
||||
case T_LONG :
|
||||
long_move(masm, src, dst);
|
||||
break;
|
||||
|
||||
case T_ADDRESS: assert(false, "found T_ADDRESS in java args");
|
||||
|
||||
default:
|
||||
move32_64(masm, src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If we have any strings we must store any register based arg to the stack
|
||||
// This includes any still live xmm registers too.
|
||||
|
||||
int sid = 0;
|
||||
|
||||
if (total_strings > 0 ) {
|
||||
for (j_arg = first_arg_to_pass, c_arg = 0 ;
|
||||
j_arg < total_args_passed ; j_arg++, c_arg++ ) {
|
||||
VMRegPair src = in_regs[j_arg];
|
||||
VMRegPair dst = out_regs[c_arg];
|
||||
|
||||
if (src.first()->is_reg()) {
|
||||
Address src_tmp(rbp, fp_offset[src.first()->value()]);
|
||||
|
||||
// string oops were left untouched by the previous loop even if the
|
||||
// eventual (converted) arg is destined for the stack so park them
|
||||
// away now (except for first)
|
||||
|
||||
if (out_sig_bt[c_arg] == T_ADDRESS) {
|
||||
Address utf8_addr = Address(
|
||||
rsp, string_locs[sid++] * VMRegImpl::stack_slot_size);
|
||||
if (sid != 1) {
|
||||
// The first string arg won't be killed until after the utf8
|
||||
// conversion
|
||||
__ movq(utf8_addr, src.first()->as_Register());
|
||||
}
|
||||
} else if (dst.first()->is_reg()) {
|
||||
if (in_sig_bt[j_arg] == T_FLOAT || in_sig_bt[j_arg] == T_DOUBLE) {
|
||||
|
||||
// Convert the xmm register to an int and store it in the reserved
|
||||
// location for the eventual c register arg
|
||||
XMMRegister f = src.first()->as_XMMRegister();
|
||||
if (in_sig_bt[j_arg] == T_FLOAT) {
|
||||
__ movflt(src_tmp, f);
|
||||
} else {
|
||||
__ movdbl(src_tmp, f);
|
||||
}
|
||||
} else {
|
||||
// If the arg is an oop type we don't support don't bother to store
|
||||
// it remember string was handled above.
|
||||
bool useless = in_sig_bt[j_arg] == T_ARRAY ||
|
||||
(in_sig_bt[j_arg] == T_OBJECT &&
|
||||
out_sig_bt[c_arg] != T_INT &&
|
||||
out_sig_bt[c_arg] != T_LONG);
|
||||
|
||||
if (!useless) {
|
||||
__ movq(src_tmp, src.first()->as_Register());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (in_sig_bt[j_arg] == T_OBJECT && out_sig_bt[c_arg] == T_LONG) {
|
||||
assert(out_sig_bt[c_arg+1] == T_VOID, "must be");
|
||||
++c_arg; // skip over T_VOID to keep the loop indices in sync
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the volatile registers are safe, convert all the strings
|
||||
sid = 0;
|
||||
|
||||
for (j_arg = first_arg_to_pass, c_arg = 0 ;
|
||||
j_arg < total_args_passed ; j_arg++, c_arg++ ) {
|
||||
if (out_sig_bt[c_arg] == T_ADDRESS) {
|
||||
// It's a string
|
||||
Address utf8_addr = Address(
|
||||
rsp, string_locs[sid++] * VMRegImpl::stack_slot_size);
|
||||
// The first string we find might still be in the original java arg
|
||||
// register
|
||||
|
||||
VMReg src = in_regs[j_arg].first();
|
||||
|
||||
// We will need to eventually save the final argument to the trap
|
||||
// in the von-volatile location dedicated to src. This is the offset
|
||||
// from fp we will use.
|
||||
int src_off = src->is_reg() ?
|
||||
fp_offset[src->value()] : reg2offset_in(src);
|
||||
|
||||
// This is where the argument will eventually reside
|
||||
VMRegPair dst = out_regs[c_arg];
|
||||
|
||||
if (src->is_reg()) {
|
||||
if (sid == 1) {
|
||||
__ movq(c_rarg0, src->as_Register());
|
||||
} else {
|
||||
__ movq(c_rarg0, utf8_addr);
|
||||
}
|
||||
} else {
|
||||
// arg is still in the original location
|
||||
__ movq(c_rarg0, Address(rbp, reg2offset_in(src)));
|
||||
}
|
||||
Label done, convert;
|
||||
|
||||
// see if the oop is NULL
|
||||
__ testq(c_rarg0, c_rarg0);
|
||||
__ jcc(Assembler::notEqual, convert);
|
||||
|
||||
if (dst.first()->is_reg()) {
|
||||
// Save the ptr to utf string in the origina src loc or the tmp
|
||||
// dedicated to it
|
||||
__ movq(Address(rbp, src_off), c_rarg0);
|
||||
} else {
|
||||
__ movq(Address(rsp, reg2offset_out(dst.first())), c_rarg0);
|
||||
}
|
||||
__ jmp(done);
|
||||
|
||||
__ bind(convert);
|
||||
|
||||
__ lea(c_rarg1, utf8_addr);
|
||||
if (dst.first()->is_reg()) {
|
||||
__ movq(Address(rbp, src_off), c_rarg1);
|
||||
} else {
|
||||
__ movq(Address(rsp, reg2offset_out(dst.first())), c_rarg1);
|
||||
}
|
||||
// And do the conversion
|
||||
__ call(RuntimeAddress(
|
||||
CAST_FROM_FN_PTR(address, SharedRuntime::get_utf)));
|
||||
|
||||
__ bind(done);
|
||||
}
|
||||
if (in_sig_bt[j_arg] == T_OBJECT && out_sig_bt[c_arg] == T_LONG) {
|
||||
assert(out_sig_bt[c_arg+1] == T_VOID, "must be");
|
||||
++c_arg; // skip over T_VOID to keep the loop indices in sync
|
||||
}
|
||||
}
|
||||
// The get_utf call killed all the c_arg registers
|
||||
live[c_rarg0->as_VMReg()->value()] = false;
|
||||
live[c_rarg1->as_VMReg()->value()] = false;
|
||||
live[c_rarg2->as_VMReg()->value()] = false;
|
||||
live[c_rarg3->as_VMReg()->value()] = false;
|
||||
live[c_rarg4->as_VMReg()->value()] = false;
|
||||
live[c_rarg5->as_VMReg()->value()] = false;
|
||||
|
||||
live[c_farg0->as_VMReg()->value()] = false;
|
||||
live[c_farg1->as_VMReg()->value()] = false;
|
||||
live[c_farg2->as_VMReg()->value()] = false;
|
||||
live[c_farg3->as_VMReg()->value()] = false;
|
||||
live[c_farg4->as_VMReg()->value()] = false;
|
||||
live[c_farg5->as_VMReg()->value()] = false;
|
||||
live[c_farg6->as_VMReg()->value()] = false;
|
||||
live[c_farg7->as_VMReg()->value()] = false;
|
||||
}
|
||||
|
||||
// Now we can finally move the register args to their desired locations
|
||||
|
||||
rax_is_zero = false;
|
||||
|
||||
for (j_arg = first_arg_to_pass, c_arg = 0 ;
|
||||
j_arg < total_args_passed ; j_arg++, c_arg++ ) {
|
||||
|
||||
VMRegPair src = in_regs[j_arg];
|
||||
VMRegPair dst = out_regs[c_arg];
|
||||
|
||||
// Only need to look for args destined for the interger registers (since we
|
||||
// convert float/double args to look like int/long outbound)
|
||||
if (dst.first()->is_reg()) {
|
||||
Register r = dst.first()->as_Register();
|
||||
|
||||
// Check if the java arg is unsupported and thereofre useless
|
||||
bool useless = in_sig_bt[j_arg] == T_ARRAY ||
|
||||
(in_sig_bt[j_arg] == T_OBJECT &&
|
||||
out_sig_bt[c_arg] != T_INT &&
|
||||
out_sig_bt[c_arg] != T_ADDRESS &&
|
||||
out_sig_bt[c_arg] != T_LONG);
|
||||
|
||||
|
||||
// If we're going to kill an existing arg save it first
|
||||
if (live[dst.first()->value()]) {
|
||||
// you can't kill yourself
|
||||
if (src.first() != dst.first()) {
|
||||
__ movq(Address(rbp, fp_offset[dst.first()->value()]), r);
|
||||
}
|
||||
}
|
||||
if (src.first()->is_reg()) {
|
||||
if (live[src.first()->value()] ) {
|
||||
if (in_sig_bt[j_arg] == T_FLOAT) {
|
||||
__ movdl(r, src.first()->as_XMMRegister());
|
||||
} else if (in_sig_bt[j_arg] == T_DOUBLE) {
|
||||
__ movdq(r, src.first()->as_XMMRegister());
|
||||
} else if (r != src.first()->as_Register()) {
|
||||
if (!useless) {
|
||||
__ movq(r, src.first()->as_Register());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the arg is an oop type we don't support don't bother to store
|
||||
// it
|
||||
if (!useless) {
|
||||
if (in_sig_bt[j_arg] == T_DOUBLE ||
|
||||
in_sig_bt[j_arg] == T_LONG ||
|
||||
in_sig_bt[j_arg] == T_OBJECT ) {
|
||||
__ movq(r, Address(rbp, fp_offset[src.first()->value()]));
|
||||
} else {
|
||||
__ movl(r, Address(rbp, fp_offset[src.first()->value()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
live[src.first()->value()] = false;
|
||||
} else if (!useless) {
|
||||
// full sized move even for int should be ok
|
||||
__ movq(r, Address(rbp, reg2offset_in(src.first())));
|
||||
}
|
||||
|
||||
// At this point r has the original java arg in the final location
|
||||
// (assuming it wasn't useless). If the java arg was an oop
|
||||
// we have a bit more to do
|
||||
|
||||
if (in_sig_bt[j_arg] == T_ARRAY || in_sig_bt[j_arg] == T_OBJECT ) {
|
||||
if (out_sig_bt[c_arg] == T_INT || out_sig_bt[c_arg] == T_LONG) {
|
||||
// need to unbox a one-word value
|
||||
Label skip;
|
||||
__ testq(r, r);
|
||||
__ jcc(Assembler::equal, skip);
|
||||
Address src1(r, box_offset);
|
||||
if ( out_sig_bt[c_arg] == T_LONG ) {
|
||||
__ movq(r, src1);
|
||||
} else {
|
||||
__ movl(r, src1);
|
||||
}
|
||||
__ bind(skip);
|
||||
|
||||
} else if (out_sig_bt[c_arg] != T_ADDRESS) {
|
||||
// Convert the arg to NULL
|
||||
__ xorq(r, r);
|
||||
}
|
||||
}
|
||||
|
||||
// dst can longer be holding an input value
|
||||
live[dst.first()->value()] = false;
|
||||
}
|
||||
if (in_sig_bt[j_arg] == T_OBJECT && out_sig_bt[c_arg] == T_LONG) {
|
||||
assert(out_sig_bt[c_arg+1] == T_VOID, "must be");
|
||||
++c_arg; // skip over T_VOID to keep the loop indices in sync
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ok now we are done. Need to place the nop that dtrace wants in order to
|
||||
// patch in the trap
|
||||
int patch_offset = ((intptr_t)__ pc()) - start;
|
||||
|
||||
__ nop();
|
||||
|
||||
|
||||
// Return
|
||||
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
|
||||
__ flush();
|
||||
|
||||
nmethod *nm = nmethod::new_dtrace_nmethod(
|
||||
method, masm->code(), vep_offset, patch_offset, frame_complete,
|
||||
stack_slots / VMRegImpl::slots_per_word);
|
||||
return nm;
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE_DTRACE_H
|
||||
|
||||
// this function returns the adjust size (in number of words) to a c2i adapter
|
||||
// activation for use during deoptimization
|
||||
int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals ) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue