diff src/cpu/x86/vm/sharedRuntime_x86_64.cpp @ 116:018d5b58dd4f

6537506: Provide a mechanism for specifying Java-level USDT-like dtrace probes Summary: Initial checkin of JSDT code Reviewed-by: acorn, sbohne
author kamg
date Thu, 17 Apr 2008 22:18:15 -0400
parents ba764ed4b6f2
children 437d03ea40b1
line wrap: on
line diff
--- a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp	Wed Apr 16 17:36:29 2008 -0400
+++ b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp	Thu Apr 17 22:18:15 2008 -0400
@@ -1886,6 +1886,627 @@
 
 }
 
+#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 ) {