diff src/cpu/x86/vm/templateTable_x86_32.cpp @ 726:be93aad57795

6655646: dynamic languages need dynamically linked call sites Summary: invokedynamic instruction (JSR 292 RI) Reviewed-by: twisti, never
author jrose
date Tue, 21 Apr 2009 23:21:04 -0700
parents 7bb995fbd3c0
children 389049f3f393
line wrap: on
line diff
--- a/src/cpu/x86/vm/templateTable_x86_32.cpp	Mon Apr 20 14:48:03 2009 -0700
+++ b/src/cpu/x86/vm/templateTable_x86_32.cpp	Tue Apr 21 23:21:04 2009 -0700
@@ -206,12 +206,12 @@
     __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::set_original_bytecode_at), scratch, rsi, bc);
 #ifndef ASSERT
     __ jmpb(patch_done);
+#else
+    __ jmp(patch_done);
+#endif
     __ bind(fast_patch);
   }
-#else
-    __ jmp(patch_done);
-    __ bind(fast_patch);
-  }
+#ifdef ASSERT
   Label okay;
   __ load_unsigned_byte(scratch, at_bcp(0));
   __ cmpl(scratch, (int)Bytecodes::java_code(bytecode));
@@ -2105,6 +2105,7 @@
 
 void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Register index) {
   assert(byte_no == 1 || byte_no == 2, "byte_no out of range");
+  bool is_invokedynamic = (bytecode() == Bytecodes::_invokedynamic);
 
   Register temp = rbx;
 
@@ -2112,16 +2113,19 @@
 
   const int shift_count = (1 + byte_no)*BitsPerByte;
   Label resolved;
-  __ get_cache_and_index_at_bcp(Rcache, index, 1);
-  __ movl(temp, Address(Rcache,
-                          index,
-                          Address::times_ptr,
-                          constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()));
-  __ shrl(temp, shift_count);
-  // have we resolved this bytecode?
-  __ andptr(temp, 0xFF);
-  __ cmpl(temp, (int)bytecode());
-  __ jcc(Assembler::equal, resolved);
+  __ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic);
+  if (is_invokedynamic) {
+    // we are resolved if the f1 field contains a non-null CallSite object
+    __ cmpptr(Address(Rcache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f1_offset()), (int32_t) NULL_WORD);
+    __ jcc(Assembler::notEqual, resolved);
+  } else {
+    __ movl(temp, Address(Rcache, index, Address::times_4, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()));
+    __ shrl(temp, shift_count);
+    // have we resolved this bytecode?
+    __ andl(temp, 0xFF);
+    __ cmpl(temp, (int)bytecode());
+    __ jcc(Assembler::equal, resolved);
+  }
 
   // resolve first time through
   address entry;
@@ -2134,12 +2138,13 @@
     case Bytecodes::_invokespecial  : // fall through
     case Bytecodes::_invokestatic   : // fall through
     case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke);  break;
+    case Bytecodes::_invokedynamic  : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); break;
     default                         : ShouldNotReachHere();                                 break;
   }
   __ movl(temp, (int)bytecode());
   __ call_VM(noreg, entry, temp);
   // Update registers with resolved info
-  __ get_cache_and_index_at_bcp(Rcache, index, 1);
+  __ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic);
   __ bind(resolved);
 }
 
@@ -2884,12 +2889,17 @@
 }
 
 
-void TemplateTable::prepare_invoke(Register method, Register index, int byte_no, Bytecodes::Code code) {
+void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) {
+  bool is_invdyn_bootstrap = (byte_no < 0);
+  if (is_invdyn_bootstrap)  byte_no = -byte_no;
+
   // determine flags
+  Bytecodes::Code code = bytecode();
   const bool is_invokeinterface  = code == Bytecodes::_invokeinterface;
+  const bool is_invokedynamic    = code == Bytecodes::_invokedynamic;
   const bool is_invokevirtual    = code == Bytecodes::_invokevirtual;
   const bool is_invokespecial    = code == Bytecodes::_invokespecial;
-  const bool load_receiver       = code != Bytecodes::_invokestatic;
+  const bool load_receiver      = (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic);
   const bool receiver_null_check = is_invokespecial;
   const bool save_flags = is_invokeinterface || is_invokevirtual;
   // setup registers & access constant pool cache
@@ -2897,6 +2907,8 @@
   const Register flags  = rdx;
   assert_different_registers(method, index, recv, flags);
 
+  assert(!is_invdyn_bootstrap || is_invokedynamic, "byte_no<0 hack only for invdyn");
+
   // save 'interpreter return address'
   __ save_bcp();
 
@@ -2907,8 +2919,13 @@
     __ movl(recv, flags);
     __ andl(recv, 0xFF);
     // recv count is 0 based?
-    __ movptr(recv, Address(rsp, recv, Interpreter::stackElementScale(), -Interpreter::expr_offset_in_bytes(1)));
-    __ verify_oop(recv);
+    Address recv_addr(rsp, recv, Interpreter::stackElementScale(), -Interpreter::expr_offset_in_bytes(1));
+    if (is_invokedynamic) {
+      __ lea(recv, recv_addr);
+    } else {
+      __ movptr(recv, recv_addr);
+      __ verify_oop(recv);
+    }
   }
 
   // do null check if needed
@@ -2926,8 +2943,14 @@
   ConstantPoolCacheEntry::verify_tosBits();
   // load return address
   {
-    ExternalAddress table(is_invokeinterface ? (address)Interpreter::return_5_addrs_by_index_table() :
-                                               (address)Interpreter::return_3_addrs_by_index_table());
+    address table_addr;
+    if (is_invdyn_bootstrap)
+      table_addr = (address)Interpreter::return_5_unbox_addrs_by_index_table();
+    else if (is_invokeinterface || is_invokedynamic)
+      table_addr = (address)Interpreter::return_5_addrs_by_index_table();
+    else
+      table_addr = (address)Interpreter::return_3_addrs_by_index_table();
+    ExternalAddress table(table_addr);
     __ movptr(flags, ArrayAddress(table, Address(noreg, flags, Address::times_ptr)));
   }
 
@@ -2990,7 +3013,7 @@
 
 void TemplateTable::invokevirtual(int byte_no) {
   transition(vtos, vtos);
-  prepare_invoke(rbx, noreg, byte_no, bytecode());
+  prepare_invoke(rbx, noreg, byte_no);
 
   // rbx,: index
   // rcx: receiver
@@ -3002,7 +3025,7 @@
 
 void TemplateTable::invokespecial(int byte_no) {
   transition(vtos, vtos);
-  prepare_invoke(rbx, noreg, byte_no, bytecode());
+  prepare_invoke(rbx, noreg, byte_no);
   // do the call
   __ verify_oop(rbx);
   __ profile_call(rax);
@@ -3012,7 +3035,7 @@
 
 void TemplateTable::invokestatic(int byte_no) {
   transition(vtos, vtos);
-  prepare_invoke(rbx, noreg, byte_no, bytecode());
+  prepare_invoke(rbx, noreg, byte_no);
   // do the call
   __ verify_oop(rbx);
   __ profile_call(rax);
@@ -3028,7 +3051,7 @@
 
 void TemplateTable::invokeinterface(int byte_no) {
   transition(vtos, vtos);
-  prepare_invoke(rax, rbx, byte_no, bytecode());
+  prepare_invoke(rax, rbx, byte_no);
 
   // rax,: Interface
   // rbx,: index
@@ -3102,6 +3125,84 @@
   __ should_not_reach_here();
 }
 
+void TemplateTable::invokedynamic(int byte_no) {
+  transition(vtos, vtos);
+
+  if (!EnableInvokeDynamic) {
+    // We should not encounter this bytecode if !EnableInvokeDynamic.
+    // The verifier will stop it.  However, if we get past the verifier,
+    // this will stop the thread in a reasonable way, without crashing the JVM.
+    __ call_VM(noreg, CAST_FROM_FN_PTR(address,
+                     InterpreterRuntime::throw_IncompatibleClassChangeError));
+    // the call_VM checks for exception, so we should never return here.
+    __ should_not_reach_here();
+    return;
+  }
+
+  prepare_invoke(rax, rbx, byte_no);
+
+  // rax: CallSite object (f1)
+  // rbx: unused (f2)
+  // rcx: receiver address
+  // rdx: flags (unused)
+
+  if (ProfileInterpreter) {
+    Label L;
+    // %%% should make a type profile for any invokedynamic that takes a ref argument
+    // profile this call
+    __ profile_call(rsi);
+  }
+
+  Label handle_unlinked_site;
+  __ movptr(rcx, Address(rax, __ delayed_value(sun_dyn_CallSiteImpl::target_offset_in_bytes, rcx)));
+  __ testptr(rcx, rcx);
+  __ jcc(Assembler::zero, handle_unlinked_site);
+
+  __ prepare_to_jump_from_interpreted();
+  __ jump_to_method_handle_entry(rcx, rdx);
+
+  // Initial calls come here...
+  __ bind(handle_unlinked_site);
+  __ pop(rcx);                 // remove return address pushed by prepare_invoke
+
+  // box stacked arguments into an array for the bootstrap method
+  address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::bootstrap_invokedynamic);
+  __ restore_bcp();      // rsi must be correct for call_VM
+  __ call_VM(rax, entry, rax);
+  __ movl(rdi, rax);            // protect bootstrap MH from prepare_invoke
+
+  // recompute return address
+  __ restore_bcp();      // rsi must be correct for prepare_invoke
+  prepare_invoke(rax, rbx, -byte_no);  // smashes rcx, rdx
+  // rax: CallSite object (f1)
+  // rbx: unused (f2)
+  // rdi: bootstrap MH
+  // rdx: flags
+
+  // now load up the arglist, which has been neatly boxed
+  __ get_thread(rcx);
+  __ movptr(rdx, Address(rcx, JavaThread::vm_result_2_offset()));
+  __ movptr(Address(rcx, JavaThread::vm_result_2_offset()), NULL_WORD);
+  __ verify_oop(rdx);
+  // rdx = arglist
+
+  // save SP now, before we add the bootstrap call to the stack
+  // We must preserve a fiction that the original arguments are outgoing,
+  // because the return sequence will reset the stack to this point
+  // and then pop all those arguments.  It seems error-prone to use
+  // a different argument list size just for bootstrapping.
+  __ prepare_to_jump_from_interpreted();
+
+  // Now let's play adapter, pushing the real arguments on the stack.
+  __ pop(rbx);                  // return PC
+  __ push(rdi);                 // boot MH
+  __ push(rax);                 // call site
+  __ push(rdx);                 // arglist
+  __ push(rbx);                 // return PC, again
+  __ mov(rcx, rdi);
+  __ jump_to_method_handle_entry(rcx, rdx);
+}
+
 //----------------------------------------------------------------------------------------------------
 // Allocation