8086069: Adapt runtime calls to recent intrinsics to pass ints as long
authorgoetz
Mon, 29 Jun 2015 15:30:55 +0200
changeset 31590 427d073af867
parent 31589 e0d5c4d48464
child 31591 82134a118aea
8086069: Adapt runtime calls to recent intrinsics to pass ints as long Summary: Remove CCallingConventionRequiresIntsAsLongs from shared code and push functionality to native wrapper. Less optimal but more flexible. Reviewed-by: jrose, kvn
hotspot/src/cpu/aarch64/vm/globalDefinitions_aarch64.hpp
hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp
hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp
hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp
hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp
hotspot/src/cpu/zero/vm/globalDefinitions_zero.hpp
hotspot/src/share/vm/opto/generateOptoStub.cpp
hotspot/src/share/vm/opto/loopTransform.cpp
hotspot/src/share/vm/opto/runtime.cpp
hotspot/src/share/vm/runtime/sharedRuntime.cpp
--- a/hotspot/src/cpu/aarch64/vm/globalDefinitions_aarch64.hpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/cpu/aarch64/vm/globalDefinitions_aarch64.hpp	Mon Jun 29 15:30:55 2015 +0200
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Red Hat Inc. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,12 +28,6 @@
 
 const int StackAlignmentInBytes  = 16;
 
-// Indicates whether the C calling conventions require that
-// 32-bit integer argument values are properly extended to 64 bits.
-// If set, SharedRuntime::c_calling_convention() must adapt
-// signatures accordingly.
-const bool CCallingConventionRequiresIntsAsLongs = true;
-
 #define SUPPORTS_NATIVE_CX8
 
 // The maximum B/BL offset range on AArch64 is 128MB.
--- a/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp	Mon Jun 29 15:30:55 2015 +0200
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012, 2015 SAP AG. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,12 +31,6 @@
 
 const int StackAlignmentInBytes = 16;
 
-// Indicates whether the C calling conventions require that
-// 32-bit integer argument values are properly extended to 64 bits.
-// If set, SharedRuntime::c_calling_convention() must adapt
-// signatures accordingly.
-const bool CCallingConventionRequiresIntsAsLongs = true;
-
 #define SUPPORTS_NATIVE_CX8
 
 // The PPC CPUs are NOT multiple-copy-atomic.
--- a/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp	Mon Jun 29 15:30:55 2015 +0200
@@ -731,23 +731,8 @@
     case T_SHORT:
     case T_INT:
       // We must cast ints to longs and use full 64 bit stack slots
-      // here. We do the cast in GraphKit::gen_stub() and just guard
-      // here against loosing that change.
-      assert(CCallingConventionRequiresIntsAsLongs,
-             "argument of type int should be promoted to type long");
-      guarantee(i > 0 && sig_bt[i-1] == T_LONG,
-                "argument of type (bt) should have been promoted to type (T_LONG,bt) for bt in "
-                "{T_BOOLEAN, T_CHAR, T_BYTE, T_SHORT, T_INT}");
-      // Do not count halves.
-      regs[i].set_bad();
-      --arg;
-      break;
+      // here.  Thus fall through, handle as long.
     case T_LONG:
-      guarantee(sig_bt[i+1] == T_VOID    ||
-                sig_bt[i+1] == T_BOOLEAN || sig_bt[i+1] == T_CHAR  ||
-                sig_bt[i+1] == T_BYTE    || sig_bt[i+1] == T_SHORT ||
-                sig_bt[i+1] == T_INT,
-                "expecting type (T_LONG,half) or type (T_LONG,bt) with bt in {T_BOOLEAN, T_CHAR, T_BYTE, T_SHORT, T_INT}");
     case T_OBJECT:
     case T_ARRAY:
     case T_ADDRESS:
@@ -1273,7 +1258,7 @@
 static void int_move(MacroAssembler*masm,
                      VMRegPair src, VMRegPair dst,
                      Register r_caller_sp, Register r_temp) {
-  assert(src.first()->is_valid() && src.second() == src.first()->next(), "incoming must be long-int");
+  assert(src.first()->is_valid(), "incoming must be int");
   assert(dst.first()->is_valid() && dst.second() == dst.first()->next(), "outgoing must be long");
 
   if (src.first()->is_stack()) {
@@ -1762,13 +1747,6 @@
   // the jni function will expect them. To figure out where they go
   // we convert the java signature to a C signature by inserting
   // the hidden arguments as arg[0] and possibly arg[1] (static method)
-  //
-  // Additionally, on ppc64 we must convert integers to longs in the C
-  // signature. We do this in advance in order to have no trouble with
-  // indexes into the bt-arrays.
-  // So convert the signature and registers now, and adjust the total number
-  // of in-arguments accordingly.
-  int i2l_argcnt = convert_ints_to_longints_argcnt(total_in_args, in_sig_bt); // PPC64: pass ints as longs.
 
   // Calculate the total number of C arguments and create arrays for the
   // signature and the outgoing registers.
@@ -1776,7 +1754,7 @@
   // some floating-point arguments must be passed in registers _and_
   // in stack locations.
   bool method_is_static = method->is_static();
-  int  total_c_args     = i2l_argcnt;
+  int  total_c_args     = total_in_args;
 
   if (!is_critical_native) {
     int n_hidden_args = method_is_static ? 2 : 1;
@@ -1785,7 +1763,7 @@
     // No JNIEnv*, no this*, but unpacked arrays (base+length).
     for (int i = 0; i < total_in_args; i++) {
       if (in_sig_bt[i] == T_ARRAY) {
-        total_c_args += 2; // PPC64: T_LONG, T_INT, T_ADDRESS (see convert_ints_to_longints and c_calling_convention)
+        total_c_args++;
       }
     }
   }
@@ -1803,8 +1781,6 @@
 
   int argc = 0;
   if (!is_critical_native) {
-    convert_ints_to_longints(i2l_argcnt, total_in_args, in_sig_bt, in_regs); // PPC64: pass ints as longs.
-
     out_sig_bt[argc++] = T_ADDRESS;
     if (method->is_static()) {
       out_sig_bt[argc++] = T_OBJECT;
@@ -1815,7 +1791,7 @@
     }
   } else {
     Thread* THREAD = Thread::current();
-    in_elem_bt = NEW_RESOURCE_ARRAY(BasicType, i2l_argcnt);
+    in_elem_bt = NEW_RESOURCE_ARRAY(BasicType, total_c_args);
     SignatureStream ss(method->signature());
     int o = 0;
     for (int i = 0; i < total_in_args ; i++, o++) {
@@ -1839,28 +1815,16 @@
         }
       } else {
         in_elem_bt[o] = T_VOID;
-        switch(in_sig_bt[i]) { // PPC64: pass ints as longs.
-          case T_BOOLEAN:
-          case T_CHAR:
-          case T_BYTE:
-          case T_SHORT:
-          case T_INT: in_elem_bt[++o] = T_VOID; break;
-          default: break;
-        }
       }
       if (in_sig_bt[i] != T_VOID) {
         assert(in_sig_bt[i] == ss.type(), "must match");
         ss.next();
       }
     }
-    assert(i2l_argcnt==o, "must match");
-
-    convert_ints_to_longints(i2l_argcnt, total_in_args, in_sig_bt, in_regs); // PPC64: pass ints as longs.
 
     for (int i = 0; i < total_in_args ; i++ ) {
       if (in_sig_bt[i] == T_ARRAY) {
         // Arrays are passed as int, elem* pair.
-        out_sig_bt[argc++] = T_LONG; // PPC64: pass ints as longs.
         out_sig_bt[argc++] = T_INT;
         out_sig_bt[argc++] = T_ADDRESS;
       } else {
@@ -1921,7 +1885,8 @@
           case T_BYTE:
           case T_SHORT:
           case T_CHAR:
-          case T_INT:  /*single_slots++;*/ break; // PPC64: pass ints as longs.
+          case T_INT:
+          // Fall through.
           case T_ARRAY:
           case T_LONG: double_slots++; break;
           default:  ShouldNotReachHere();
@@ -2019,7 +1984,7 @@
 
   __ save_LR_CR(r_temp_1);
   __ generate_stack_overflow_check(frame_size_in_bytes); // Check before creating frame.
-  __ mr(r_callers_sp, R1_SP);                       // Remember frame pointer.
+  __ mr(r_callers_sp, R1_SP);                            // Remember frame pointer.
   __ push_frame(frame_size_in_bytes, r_temp_1);          // Push the c2n adapter's frame.
   frame_done_pc = (intptr_t)__ pc();
 
@@ -2098,24 +2063,16 @@
       case T_BYTE:
       case T_SHORT:
       case T_INT:
-        guarantee(in > 0 && in_sig_bt[in-1] == T_LONG,
-                  "expecting type (T_LONG,bt) for bt in {T_BOOLEAN, T_CHAR, T_BYTE, T_SHORT, T_INT}");
+        // Move int and do sign extension.
+        int_move(masm, in_regs[in], out_regs[out], r_callers_sp, r_temp_1);
         break;
       case T_LONG:
-        if (in_sig_bt[in+1] == T_VOID) {
-          long_move(masm, in_regs[in], out_regs[out], r_callers_sp, r_temp_1);
-        } else {
-          guarantee(in_sig_bt[in+1] == T_BOOLEAN || in_sig_bt[in+1] == T_CHAR  ||
-                    in_sig_bt[in+1] == T_BYTE    || in_sig_bt[in+1] == T_SHORT ||
-                    in_sig_bt[in+1] == T_INT,
-                 "expecting type (T_LONG,bt) for bt in {T_BOOLEAN, T_CHAR, T_BYTE, T_SHORT, T_INT}");
-          int_move(masm, in_regs[in], out_regs[out], r_callers_sp, r_temp_1);
-        }
+        long_move(masm, in_regs[in], out_regs[out], r_callers_sp, r_temp_1);
         break;
       case T_ARRAY:
         if (is_critical_native) {
           int body_arg = out;
-          out -= 2; // Point to length arg. PPC64: pass ints as longs.
+          out -= 1; // Point to length arg.
           unpack_array_argument(masm, in_regs[in], in_elem_bt[in], out_regs[body_arg], out_regs[out],
                                 r_callers_sp, r_temp_1, r_temp_2);
           break;
@@ -2187,7 +2144,6 @@
   // Make sure that thread is non-volatile; it crosses a bunch of VM calls below.
   assert(R16_thread->is_nonvolatile(), "thread must be in non-volatile register");
 
-
 # if 0
   // DTrace method entry
 # endif
--- a/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp	Mon Jun 29 15:30:55 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,12 +30,6 @@
 
 const int StackAlignmentInBytes = (2*wordSize);
 
-// Indicates whether the C calling conventions require that
-// 32-bit integer argument values are properly extended to 64 bits.
-// If set, SharedRuntime::c_calling_convention() must adapt
-// signatures accordingly.
-const bool CCallingConventionRequiresIntsAsLongs = false;
-
 #define SUPPORTS_NATIVE_CX8
 
 // The expected size in bytes of a cache line, used to pad data structures.
--- a/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp	Mon Jun 29 15:30:55 2015 +0200
@@ -27,12 +27,6 @@
 
 const int StackAlignmentInBytes  = 16;
 
-// Indicates whether the C calling conventions require that
-// 32-bit integer argument values are properly extended to 64 bits.
-// If set, SharedRuntime::c_calling_convention() must adapt
-// signatures accordingly.
-const bool CCallingConventionRequiresIntsAsLongs = false;
-
 #define SUPPORTS_NATIVE_CX8
 
 // The expected size in bytes of a cache line, used to pad data structures.
--- a/hotspot/src/cpu/zero/vm/globalDefinitions_zero.hpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/cpu/zero/vm/globalDefinitions_zero.hpp	Mon Jun 29 15:30:55 2015 +0200
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2009 Red Hat, Inc.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2009, 2015, Red Hat, Inc.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,10 +28,4 @@
 
 #include <ffi.h>
 
-// Indicates whether the C calling conventions require that
-// 32-bit integer argument values are properly extended to 64 bits.
-// If set, SharedRuntime::c_calling_convention() must adapt
-// signatures accordingly.
-const bool CCallingConventionRequiresIntsAsLongs = false;
-
 #endif // CPU_ZERO_VM_GLOBALDEFINITIONS_ZERO_HPP
--- a/hotspot/src/share/vm/opto/generateOptoStub.cpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/share/vm/opto/generateOptoStub.cpp	Mon Jun 29 15:30:55 2015 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -118,19 +118,14 @@
   // The C routines gets the base of thread-local storage passed in as an
   // extra argument.  Not all calls need it, but its cheap to add here.
   for (uint pcnt = cnt; pcnt < parm_cnt; pcnt++, cnt++) {
-    // Convert ints to longs if required.
-    if (CCallingConventionRequiresIntsAsLongs && jdomain->field_at(pcnt)->isa_int()) {
-      fields[cnt++] = TypeLong::LONG;
-      fields[cnt]   = Type::HALF; // must add an additional half for a long
-    } else {
-      fields[cnt] = jdomain->field_at(pcnt);
-    }
+    fields[cnt] = jdomain->field_at(pcnt);
   }
 
   fields[cnt++] = TypeRawPtr::BOTTOM; // Thread-local storage
   // Also pass in the caller's PC, if asked for.
-  if( return_pc )
+  if (return_pc) {
     fields[cnt++] = TypeRawPtr::BOTTOM; // Return PC
+  }
 
   const TypeTuple* domain = TypeTuple::make(cnt,fields);
   // The C routine we are about to call cannot return an oop; it can block on
@@ -143,21 +138,22 @@
   const Type **rfields = TypeTuple::fields(jrange->cnt() - TypeFunc::Parms);
   // Fixup oop returns
   int retval_ptr = retval->isa_oop_ptr();
-  if( retval_ptr ) {
+  if (retval_ptr) {
     assert( pass_tls, "Oop must be returned thru TLS" );
     // Fancy-jumps return address; others return void
     rfields[TypeFunc::Parms] = is_fancy_jump ? TypeRawPtr::BOTTOM : Type::TOP;
 
-  } else if( retval->isa_int() ) { // Returning any integer subtype?
+  } else if (retval->isa_int()) { // Returning any integer subtype?
     // "Fatten" byte, char & short return types to 'int' to show that
     // the native C code can return values with junk high order bits.
     // We'll sign-extend it below later.
     rfields[TypeFunc::Parms] = TypeInt::INT; // It's "dirty" and needs sign-ext
 
-  } else if( jrange->cnt() >= TypeFunc::Parms+1 ) { // Else copy other types
+  } else if (jrange->cnt() >= TypeFunc::Parms+1) { // Else copy other types
     rfields[TypeFunc::Parms] = jrange->field_at(TypeFunc::Parms);
-    if( jrange->cnt() == TypeFunc::Parms+2 )
+    if (jrange->cnt() == TypeFunc::Parms+2) {
       rfields[TypeFunc::Parms+1] = jrange->field_at(TypeFunc::Parms+1);
+    }
   }
   const TypeTuple* range = TypeTuple::make(jrange->cnt(),rfields);
 
@@ -181,14 +177,7 @@
   // A little too aggressive on the parm copy; return address is not an input
   call->set_req(TypeFunc::ReturnAdr, top());
   for (; i < parm_cnt; i++) { // Regular input arguments
-    // Convert ints to longs if required.
-    if (CCallingConventionRequiresIntsAsLongs && jdomain->field_at(i)->isa_int()) {
-      Node* int_as_long = _gvn.transform(new ConvI2LNode(map()->in(i)));
-      call->init_req(cnt++, int_as_long); // long
-      call->init_req(cnt++, top());       // half
-    } else {
-      call->init_req(cnt++, map()->in(i));
-    }
+    call->init_req(cnt++, map()->in(i));
   }
 
   call->init_req( cnt++, thread );
--- a/hotspot/src/share/vm/opto/loopTransform.cpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/share/vm/opto/loopTransform.cpp	Mon Jun 29 15:30:55 2015 +0200
@@ -2941,13 +2941,6 @@
     _igvn.register_new_node_with_optimizer(store_value);
   }
 
-  if (CCallingConventionRequiresIntsAsLongs &&
-      // See StubRoutines::select_fill_function for types. FLOAT has been converted to INT.
-      (t == T_FLOAT || t == T_INT ||  is_subword_type(t))) {
-    store_value = new ConvI2LNode(store_value);
-    _igvn.register_new_node_with_optimizer(store_value);
-  }
-
   Node* mem_phi = store->in(MemNode::Memory);
   Node* result_ctrl;
   Node* result_mem;
@@ -2957,9 +2950,6 @@
   uint cnt = 0;
   call->init_req(TypeFunc::Parms + cnt++, from);
   call->init_req(TypeFunc::Parms + cnt++, store_value);
-  if (CCallingConventionRequiresIntsAsLongs) {
-    call->init_req(TypeFunc::Parms + cnt++, C->top());
-  }
 #ifdef _LP64
   len = new ConvI2LNode(len);
   _igvn.register_new_node_with_optimizer(len);
--- a/hotspot/src/share/vm/opto/runtime.cpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/share/vm/opto/runtime.cpp	Mon Jun 29 15:30:55 2015 +0200
@@ -779,18 +779,10 @@
 const TypeFunc* OptoRuntime::array_fill_Type() {
   const Type** fields;
   int argp = TypeFunc::Parms;
-  if (CCallingConventionRequiresIntsAsLongs) {
   // create input type (domain): pointer, int, size_t
-    fields = TypeTuple::fields(3 LP64_ONLY( + 2));
-    fields[argp++] = TypePtr::NOTNULL;
-    fields[argp++] = TypeLong::LONG;
-    fields[argp++] = Type::HALF;
-  } else {
-    // create input type (domain): pointer, int, size_t
-    fields = TypeTuple::fields(3 LP64_ONLY( + 1));
-    fields[argp++] = TypePtr::NOTNULL;
-    fields[argp++] = TypeInt::INT;
-  }
+  fields = TypeTuple::fields(3 LP64_ONLY( + 1));
+  fields[argp++] = TypePtr::NOTNULL;
+  fields[argp++] = TypeInt::INT;
   fields[argp++] = TypeX_X;               // size in whatevers (size_t)
   LP64_ONLY(fields[argp++] = Type::HALF); // other half of long length
   const TypeTuple *domain = TypeTuple::make(argp, fields);
--- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp	Fri Jul 03 16:29:37 2015 +0200
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp	Mon Jun 29 15:30:55 2015 +0200
@@ -2616,71 +2616,6 @@
   GC_locker::unlock_critical(thread);
 JRT_END
 
-int SharedRuntime::convert_ints_to_longints_argcnt(int in_args_count, BasicType* in_sig_bt) {
-  int argcnt = in_args_count;
-  if (CCallingConventionRequiresIntsAsLongs) {
-    for (int in = 0; in < in_args_count; in++) {
-      BasicType bt = in_sig_bt[in];
-      switch (bt) {
-        case T_BOOLEAN:
-        case T_CHAR:
-        case T_BYTE:
-        case T_SHORT:
-        case T_INT:
-          argcnt++;
-          break;
-        default:
-          break;
-      }
-    }
-  } else {
-    assert(0, "This should not be needed on this platform");
-  }
-
-  return argcnt;
-}
-
-void SharedRuntime::convert_ints_to_longints(int i2l_argcnt, int& in_args_count,
-                                             BasicType*& in_sig_bt, VMRegPair*& in_regs) {
-  if (CCallingConventionRequiresIntsAsLongs) {
-    VMRegPair *new_in_regs   = NEW_RESOURCE_ARRAY(VMRegPair, i2l_argcnt);
-    BasicType *new_in_sig_bt = NEW_RESOURCE_ARRAY(BasicType, i2l_argcnt);
-
-    int argcnt = 0;
-    for (int in = 0; in < in_args_count; in++, argcnt++) {
-      BasicType bt  = in_sig_bt[in];
-      VMRegPair reg = in_regs[in];
-      switch (bt) {
-        case T_BOOLEAN:
-        case T_CHAR:
-        case T_BYTE:
-        case T_SHORT:
-        case T_INT:
-          // Convert (bt) to (T_LONG,bt).
-          new_in_sig_bt[argcnt] = T_LONG;
-          new_in_sig_bt[argcnt+1] = bt;
-          assert(reg.first()->is_valid() && !reg.second()->is_valid(), "");
-          new_in_regs[argcnt].set2(reg.first());
-          new_in_regs[argcnt+1].set_bad();
-          argcnt++;
-          break;
-        default:
-          // No conversion needed.
-          new_in_sig_bt[argcnt] = bt;
-          new_in_regs[argcnt]   = reg;
-          break;
-      }
-    }
-    assert(argcnt == i2l_argcnt, "must match");
-
-    in_regs = new_in_regs;
-    in_sig_bt = new_in_sig_bt;
-    in_args_count = i2l_argcnt;
-  } else {
-    assert(0, "This should not be needed on this platform");
-  }
-}
-
 // -------------------------------------------------------------------------
 // Java-Java calling convention
 // (what you use when Java calls Java)