7007377: JSR 292 MethodHandlesTest.testCastFailure fails on SPARC with -Xcomp +DeoptimizeALot
authortwisti
Wed, 22 Dec 2010 02:02:53 -0800
changeset 7712 cee30a0f4315
parent 7711 59d53a41b3e7
child 7713 1e06d2419258
7007377: JSR 292 MethodHandlesTest.testCastFailure fails on SPARC with -Xcomp +DeoptimizeALot Reviewed-by: kvn, jrose
hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp
hotspot/src/cpu/x86/vm/methodHandles_x86.cpp
hotspot/src/share/vm/prims/methodHandles.cpp
hotspot/src/share/vm/prims/methodHandles.hpp
hotspot/src/share/vm/runtime/init.cpp
--- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp	Tue Dec 21 22:57:17 2010 -0800
+++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp	Wed Dec 22 02:02:53 2010 -0800
@@ -395,18 +395,23 @@
 //
 // Generate an "entry" field for a method handle.
 // This determines how the method handle will respond to calls.
-void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHandles::EntryKind ek) {
+void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHandles::EntryKind ek, TRAPS) {
   // Here is the register state during an interpreted call,
   // as set up by generate_method_handle_interpreter_entry():
   // - G5: garbage temp (was MethodHandle.invoke methodOop, unused)
   // - G3: receiver method handle
   // - O5_savedSP: sender SP (must preserve)
 
-  Register O0_argslot = O0;
-  Register O1_scratch = O1;
-  Register O2_scratch = O2;
-  Register O3_scratch = O3;
-  Register G5_index   = G5;
+  const Register O0_argslot = O0;
+  const Register O1_scratch = O1;
+  const Register O2_scratch = O2;
+  const Register O3_scratch = O3;
+  const Register G5_index   = G5;
+
+  // Argument registers for _raise_exception.
+  const Register O0_code     = O0;
+  const Register O1_actual   = O1;
+  const Register O2_required = O2;
 
   guarantee(java_dyn_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
 
@@ -439,48 +444,36 @@
   case _raise_exception:
     {
       // Not a real MH entry, but rather shared code for raising an
-      // exception.  Extra local arguments are passed in scratch
-      // registers, as required type in O3, failing object (or NULL)
-      // in O2, failing bytecode type in O1.
+      // exception.  Since we use a C2I adapter to set up the
+      // interpreter state, arguments are expected in compiler
+      // argument registers.
+      methodHandle mh(raise_exception_method());
+      address c2i_entry = methodOopDesc::make_adapters(mh, CATCH);
 
       __ mov(O5_savedSP, SP);  // Cut the stack back to where the caller started.
 
-      // Push arguments as if coming from the interpreter.
-      Register O0_scratch = O0_argslot;
-      int stackElementSize = Interpreter::stackElementSize;
-
-      // Make space on the stack for the arguments and set Gargs
-      // correctly.
-      __ sub(SP, 4*stackElementSize, SP);  // Keep stack aligned.
-      __ add(SP, (frame::varargs_offset)*wordSize - 1*Interpreter::stackElementSize + STACK_BIAS + BytesPerWord, Gargs);
-
-      // void raiseException(int code, Object actual, Object required)
-      __ st(    O1_scratch, Address(Gargs, 2*stackElementSize));  // code
-      __ st_ptr(O2_scratch, Address(Gargs, 1*stackElementSize));  // actual
-      __ st_ptr(O3_scratch, Address(Gargs, 0*stackElementSize));  // required
-
-      Label no_method;
+      Label L_no_method;
       // FIXME: fill in _raise_exception_method with a suitable sun.dyn method
       __ set(AddressLiteral((address) &_raise_exception_method), G5_method);
       __ ld_ptr(Address(G5_method, 0), G5_method);
       __ tst(G5_method);
-      __ brx(Assembler::zero, false, Assembler::pn, no_method);
+      __ brx(Assembler::zero, false, Assembler::pn, L_no_method);
       __ delayed()->nop();
 
-      int jobject_oop_offset = 0;
+      const int jobject_oop_offset = 0;
       __ ld_ptr(Address(G5_method, jobject_oop_offset), G5_method);
       __ tst(G5_method);
-      __ brx(Assembler::zero, false, Assembler::pn, no_method);
+      __ brx(Assembler::zero, false, Assembler::pn, L_no_method);
       __ delayed()->nop();
 
       __ verify_oop(G5_method);
-      __ jump_indirect_to(G5_method_fie, O1_scratch);
+      __ jump_to(AddressLiteral(c2i_entry), O3_scratch);
       __ delayed()->nop();
 
       // If we get here, the Java runtime did not do its job of creating the exception.
       // Do something that is at least causes a valid throw from the interpreter.
-      __ bind(no_method);
-      __ unimplemented("_raise_exception no method");
+      __ bind(L_no_method);
+      __ unimplemented("call throw_WrongMethodType_entry");
     }
     break;
 
@@ -570,10 +563,10 @@
       // Throw an exception.
       // For historical reasons, it will be IncompatibleClassChangeError.
       __ unimplemented("not tested yet");
-      __ ld_ptr(Address(O1_intf, java_mirror_offset), O3_scratch);  // required interface
-      __ mov(O0_klass, O2_scratch);  // bad receiver
-      __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O0_argslot);
-      __ delayed()->mov(Bytecodes::_invokeinterface, O1_scratch);  // who is complaining?
+      __ ld_ptr(Address(O1_intf, java_mirror_offset), O2_required);  // required interface
+      __ mov(   O0_klass,                             O1_actual);    // bad receiver
+      __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch);
+      __ delayed()->mov(Bytecodes::_invokeinterface,  O0_code);      // who is complaining?
     }
     break;
 
@@ -663,11 +656,10 @@
       __ check_klass_subtype(O1_scratch, G5_klass, O0_argslot, O2_scratch, done);
 
       // If we get here, the type check failed!
-      __ ldsw(G3_amh_vmargslot, O0_argslot);  // reload argslot field
-      __ load_heap_oop(G3_amh_argument, O3_scratch);  // required class
-      __ ld_ptr(vmarg, O2_scratch);  // bad object
-      __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O0_argslot);
-      __ delayed()->mov(Bytecodes::_checkcast, O1_scratch);  // who is complaining?
+      __ load_heap_oop(G3_amh_argument,        O2_required);  // required class
+      __ ld_ptr(       vmarg,                  O1_actual);    // bad object
+      __ jump_to(AddressLiteral(from_interpreted_entry(_raise_exception)), O3_scratch);
+      __ delayed()->mov(Bytecodes::_checkcast, O0_code);      // who is complaining?
 
       __ bind(done);
       // Get the new MH:
--- a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp	Tue Dec 21 22:57:17 2010 -0800
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp	Wed Dec 22 02:02:53 2010 -0800
@@ -385,9 +385,12 @@
   // FIXME: MethodHandlesTest gets a crash if we enable OP_SPREAD_ARGS.
 }
 
+//------------------------------------------------------------------------------
+// MethodHandles::generate_method_handle_stub
+//
 // Generate an "entry" field for a method handle.
 // This determines how the method handle will respond to calls.
-void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHandles::EntryKind ek) {
+void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHandles::EntryKind ek, TRAPS) {
   // Here is the register state during an interpreted call,
   // as set up by generate_method_handle_interpreter_entry():
   // - rbx: garbage temp (was MethodHandle.invoke methodOop, unused)
@@ -396,14 +399,21 @@
   // - rsi/r13: sender SP (must preserve; see prepare_to_jump_from_interpreted)
   // - rdx: garbage temp, can blow away
 
-  Register rcx_recv    = rcx;
-  Register rax_argslot = rax;
-  Register rbx_temp    = rbx;
-  Register rdx_temp    = rdx;
+  const Register rcx_recv    = rcx;
+  const Register rax_argslot = rax;
+  const Register rbx_temp    = rbx;
+  const Register rdx_temp    = rdx;
 
   // This guy is set up by prepare_to_jump_from_interpreted (from interpreted calls)
   // and gen_c2i_adapter (from compiled calls):
-  Register saved_last_sp = LP64_ONLY(r13) NOT_LP64(rsi);
+  const Register saved_last_sp = LP64_ONLY(r13) NOT_LP64(rsi);
+
+  // Argument registers for _raise_exception.
+  // 32-bit: Pass first two oop/int args in registers ECX and EDX.
+  const Register rarg0_code     = LP64_ONLY(j_rarg0) NOT_LP64(rcx);
+  const Register rarg1_actual   = LP64_ONLY(j_rarg1) NOT_LP64(rdx);
+  const Register rarg2_required = LP64_ONLY(j_rarg2) NOT_LP64(rdi);
+  assert_different_registers(rarg0_code, rarg1_actual, rarg2_required, saved_last_sp);
 
   guarantee(java_dyn_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets");
 
@@ -437,47 +447,41 @@
   switch ((int) ek) {
   case _raise_exception:
     {
-      // Not a real MH entry, but rather shared code for raising an exception.
-      // Extra local arguments are pushed on stack, as required type at TOS+8,
-      // failing object (or NULL) at TOS+4, failing bytecode type at TOS.
-      // Beyond those local arguments are the PC, of course.
-      Register rdx_code = rdx_temp;
-      Register rcx_fail = rcx_recv;
-      Register rax_want = rax_argslot;
-      Register rdi_pc   = rdi;
-      __ pop(rdx_code);  // TOS+0
-      __ pop(rcx_fail);  // TOS+4
-      __ pop(rax_want);  // TOS+8
-      __ pop(rdi_pc);    // caller PC
+      // Not a real MH entry, but rather shared code for raising an
+      // exception.  Since we use a C2I adapter to set up the
+      // interpreter state, arguments are expected in compiler
+      // argument registers.
+      methodHandle mh(raise_exception_method());
+      address c2i_entry = methodOopDesc::make_adapters(mh, CHECK);
 
-      __ mov(rsp, rsi);   // cut the stack back to where the caller started
-
-      // Repush the arguments as if coming from the interpreter.
-      __ push(rdx_code);
-      __ push(rcx_fail);
-      __ push(rax_want);
+      const Register rdi_pc = rax;
+      __ pop(rdi_pc);  // caller PC
+      __ mov(rsp, saved_last_sp);  // cut the stack back to where the caller started
 
       Register rbx_method = rbx_temp;
-      Label no_method;
+      Label L_no_method;
       // FIXME: fill in _raise_exception_method with a suitable sun.dyn method
       __ movptr(rbx_method, ExternalAddress((address) &_raise_exception_method));
       __ testptr(rbx_method, rbx_method);
-      __ jccb(Assembler::zero, no_method);
-      int jobject_oop_offset = 0;
+      __ jccb(Assembler::zero, L_no_method);
+
+      const int jobject_oop_offset = 0;
       __ movptr(rbx_method, Address(rbx_method, jobject_oop_offset));  // dereference the jobject
       __ testptr(rbx_method, rbx_method);
-      __ jccb(Assembler::zero, no_method);
+      __ jccb(Assembler::zero, L_no_method);
       __ verify_oop(rbx_method);
-      __ push(rdi_pc);          // and restore caller PC
-      __ jmp(rbx_method_fie);
+
+      // 32-bit: push remaining arguments as if coming from the compiler.
+      NOT_LP64(__ push(rarg2_required));
+
+      __ push(rdi_pc);  // restore caller PC
+      __ jump(ExternalAddress(c2i_entry));  // do C2I transition
 
       // If we get here, the Java runtime did not do its job of creating the exception.
       // Do something that is at least causes a valid throw from the interpreter.
-      __ bind(no_method);
-      __ pop(rax_want);
-      __ pop(rcx_fail);
-      __ push(rax_want);
-      __ push(rcx_fail);
+      __ bind(L_no_method);
+      __ push(rarg2_required);
+      __ push(rarg1_actual);
       __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry()));
     }
     break;
@@ -572,9 +576,11 @@
       __ bind(no_such_interface);
       // Throw an exception.
       // For historical reasons, it will be IncompatibleClassChangeError.
-      __ pushptr(Address(rdx_intf, java_mirror_offset));  // required interface
-      __ push(rcx_recv);        // bad receiver
-      __ push((int)Bytecodes::_invokeinterface);  // who is complaining?
+      __ mov(rbx_temp, rcx_recv);  // rarg2_required might be RCX
+      assert_different_registers(rarg2_required, rbx_temp);
+      __ movptr(rarg2_required, Address(rdx_intf, java_mirror_offset));  // required interface
+      __ mov(   rarg1_actual,   rbx_temp);                               // bad receiver
+      __ movl(  rarg0_code,     (int) Bytecodes::_invokeinterface);      // who is complaining?
       __ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
     }
     break;
@@ -669,10 +675,10 @@
       __ movl(rax_argslot, rcx_amh_vmargslot);  // reload argslot field
       __ movptr(rdx_temp, vmarg);
 
-      __ load_heap_oop(rbx_klass, rcx_amh_argument); // required class
-      __ push(rbx_klass);
-      __ push(rdx_temp);                             // bad object
-      __ push((int)Bytecodes::_checkcast);           // who is complaining?
+      assert_different_registers(rarg2_required, rdx_temp);
+      __ load_heap_oop(rarg2_required, rcx_amh_argument);             // required class
+      __ mov(          rarg1_actual,   rdx_temp);                     // bad object
+      __ movl(         rarg0_code,     (int) Bytecodes::_checkcast);  // who is complaining?
       __ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
 
       __ bind(done);
@@ -1189,16 +1195,18 @@
 
       __ bind(bad_array_klass);
       UNPUSH_RSI_RDI;
-      __ pushptr(Address(rdx_array_klass, java_mirror_offset)); // required type
-      __ pushptr(vmarg);                // bad array
-      __ push((int)Bytecodes::_aaload); // who is complaining?
+      assert(!vmarg.uses(rarg2_required), "must be different registers");
+      __ movptr(rarg2_required, Address(rdx_array_klass, java_mirror_offset));  // required type
+      __ movptr(rarg1_actual,   vmarg);                                         // bad array
+      __ movl(  rarg0_code,     (int) Bytecodes::_aaload);                      // who is complaining?
       __ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
 
       __ bind(bad_array_length);
       UNPUSH_RSI_RDI;
-      __ push(rcx_recv);        // AMH requiring a certain length
-      __ pushptr(vmarg);        // bad array
-      __ push((int)Bytecodes::_arraylength); // who is complaining?
+      assert(!vmarg.uses(rarg2_required), "must be different registers");
+      __ mov   (rarg2_required, rcx_recv);                       // AMH requiring a certain length
+      __ movptr(rarg1_actual,   vmarg);                          // bad array
+      __ movl(  rarg0_code,     (int) Bytecodes::_arraylength);  // who is complaining?
       __ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
 
 #undef UNPUSH_RSI_RDI
--- a/hotspot/src/share/vm/prims/methodHandles.cpp	Tue Dec 21 22:57:17 2010 -0800
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp	Wed Dec 22 02:02:53 2010 -0800
@@ -111,7 +111,7 @@
 //------------------------------------------------------------------------------
 // MethodHandles::generate_adapters
 //
-void MethodHandles::generate_adapters() {
+void MethodHandles::generate_adapters(TRAPS) {
   if (!EnableMethodHandles || SystemDictionary::MethodHandle_klass() == NULL)  return;
 
   assert(_adapter_code == NULL, "generate only once");
@@ -123,20 +123,20 @@
     vm_exit_out_of_memory(_adapter_code_size, "CodeCache: no room for MethodHandles adapters");
   CodeBuffer code(_adapter_code);
   MethodHandlesAdapterGenerator g(&code);
-  g.generate();
+  g.generate(CHECK);
 }
 
 
 //------------------------------------------------------------------------------
 // MethodHandlesAdapterGenerator::generate
 //
-void MethodHandlesAdapterGenerator::generate() {
+void MethodHandlesAdapterGenerator::generate(TRAPS) {
   // Generate generic method handle adapters.
   for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST;
        ek < MethodHandles::_EK_LIMIT;
        ek = MethodHandles::EntryKind(1 + (int)ek)) {
     StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek));
-    MethodHandles::generate_method_handle_stub(_masm, ek);
+    MethodHandles::generate_method_handle_stub(_masm, ek, CHECK);
   }
 }
 
@@ -2645,5 +2645,10 @@
       MethodHandles::set_enabled(true);
     }
   }
+
+  // Generate method handles adapters if enabled.
+  if (MethodHandles::enabled()) {
+    MethodHandles::generate_adapters(CHECK);
+  }
 }
 JVM_END
--- a/hotspot/src/share/vm/prims/methodHandles.hpp	Tue Dec 21 22:57:17 2010 -0800
+++ b/hotspot/src/share/vm/prims/methodHandles.hpp	Wed Dec 22 02:02:53 2010 -0800
@@ -294,11 +294,11 @@
   enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 };
 
   // Generate MethodHandles adapters.
-  static void generate_adapters();
+  static void generate_adapters(TRAPS);
 
   // Called from InterpreterGenerator and MethodHandlesAdapterGenerator.
   static address generate_method_handle_interpreter_entry(MacroAssembler* _masm);
-  static void generate_method_handle_stub(MacroAssembler* _masm, EntryKind ek);
+  static void generate_method_handle_stub(MacroAssembler* _masm, EntryKind ek, TRAPS);
 
   // argument list parsing
   static int argument_slot(oop method_type, int arg);
@@ -530,7 +530,7 @@
 public:
   MethodHandlesAdapterGenerator(CodeBuffer* code) : StubCodeGenerator(code) {}
 
-  void generate();
+  void generate(TRAPS);
 };
 
 #endif // SHARE_VM_PRIMS_METHODHANDLES_HPP
--- a/hotspot/src/share/vm/runtime/init.cpp	Tue Dec 21 22:57:17 2010 -0800
+++ b/hotspot/src/share/vm/runtime/init.cpp	Wed Dec 22 02:02:53 2010 -0800
@@ -125,9 +125,6 @@
   javaClasses_init();  // must happen after vtable initialization
   stubRoutines_init2(); // note: StubRoutines need 2-phase init
 
-  // Generate MethodHandles adapters.
-  MethodHandles::generate_adapters();
-
   // Although we'd like to, we can't easily do a heap verify
   // here because the main thread isn't yet a JavaThread, so
   // its TLAB may not be made parseable from the usual interfaces.