8024342: PPC64 (part 111): Support for C calling conventions that require 64-bit ints.
authorgoetz
Wed, 18 Sep 2013 14:34:56 -0700
changeset 22834 3e2df6a4a28c
parent 22833 92d74f188f73
child 22835 e1c589c71c23
8024342: PPC64 (part 111): Support for C calling conventions that require 64-bit ints. Summary: Some platforms, as ppc and s390x/zArch require that 32-bit ints are passed as 64-bit values to C functions. This change adds support to adapt the signature and to issue proper casts to c2-compiled stubs. The functions are used in generate_native_wrapper(). Adapt signature used by the compiler as in PhaseIdealLoop::intrinsify_fill(). Reviewed-by: kvn
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
hotspot/src/share/vm/runtime/sharedRuntime.hpp
--- a/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp	Fri Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp	Wed Sep 18 14:34:56 2013 -0700
@@ -29,6 +29,12 @@
 // Size of PPC Instructions
 const int BytesPerInstWord = 4;
 
-const int StackAlignmentInBytes  = 16;
+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;
 
 #endif // CPU_PPC_VM_GLOBALDEFINITIONS_PPC_HPP
--- a/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp	Fri Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp	Wed Sep 18 14:34:56 2013 -0700
@@ -734,11 +734,8 @@
       // 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.
-      Unimplemented(); // TODO: PPC port
-      /*
-      assert(SharedRuntime::c_calling_convention_requires_ints_as_longs(),
+      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}");
@@ -856,7 +853,6 @@
   const int adapter_size = frame::top_ijava_frame_abi_size +
                            round_to(total_args_passed * wordSize, frame::alignment_in_bytes);
 
-
   // regular (verified) c2i entry point
   c2i_entrypoint = __ pc();
 
--- a/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp	Fri Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp	Wed Sep 18 14:34:56 2013 -0700
@@ -30,6 +30,12 @@
 
 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
 
 #endif // CPU_SPARC_VM_GLOBALDEFINITIONS_SPARC_HPP
--- a/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp	Fri Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp	Wed Sep 18 14:34:56 2013 -0700
@@ -27,6 +27,12 @@
 
 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
 
 #endif // CPU_X86_VM_GLOBALDEFINITIONS_X86_HPP
--- a/hotspot/src/cpu/zero/vm/globalDefinitions_zero.hpp	Fri Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/cpu/zero/vm/globalDefinitions_zero.hpp	Wed Sep 18 14:34:56 2013 -0700
@@ -28,4 +28,10 @@
 
 #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 Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/share/vm/opto/generateOptoStub.cpp	Wed Sep 18 14:34:56 2013 -0700
@@ -117,8 +117,16 @@
   uint cnt = TypeFunc::Parms;
   // 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( ; cnt<parm_cnt; cnt++ )
-    fields[cnt] = jdomain->field_at(cnt);
+  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++] = TypeRawPtr::BOTTOM; // Thread-local storage
   // Also pass in the caller's PC, if asked for.
   if( return_pc )
@@ -169,12 +177,20 @@
 
   // Set fixed predefined input arguments
   cnt = 0;
-  for( i=0; i<TypeFunc::Parms; i++ )
-    call->init_req( cnt++, map()->in(i) );
+  for (i = 0; i < TypeFunc::Parms; i++)
+    call->init_req(cnt++, map()->in(i));
   // 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
-    call->init_req( cnt++, map()->in(i) );
+  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 (C) 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++, thread );
   if( return_pc )             // Return PC, if asked for
--- a/hotspot/src/share/vm/opto/loopTransform.cpp	Fri Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/share/vm/opto/loopTransform.cpp	Wed Sep 18 14:34:56 2013 -0700
@@ -2692,27 +2692,38 @@
     _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 (C) ConvI2LNode(store_value);
+    _igvn.register_new_node_with_optimizer(store_value);
+  }
+
   Node* mem_phi = store->in(MemNode::Memory);
   Node* result_ctrl;
   Node* result_mem;
   const TypeFunc* call_type = OptoRuntime::array_fill_Type();
   CallLeafNode *call = new (C) CallLeafNoFPNode(call_type, fill,
                                                 fill_name, TypeAryPtr::get_array_body_type(t));
-  call->init_req(TypeFunc::Parms+0, from);
-  call->init_req(TypeFunc::Parms+1, store_value);
+  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 (C) ConvI2LNode(len);
   _igvn.register_new_node_with_optimizer(len);
 #endif
-  call->init_req(TypeFunc::Parms+2, len);
+  call->init_req(TypeFunc::Parms + cnt++, len);
 #ifdef _LP64
-  call->init_req(TypeFunc::Parms+3, C->top());
+  call->init_req(TypeFunc::Parms + cnt++, C->top());
 #endif
-  call->init_req( TypeFunc::Control, head->init_control());
-  call->init_req( TypeFunc::I_O    , C->top() )        ;   // does no i/o
-  call->init_req( TypeFunc::Memory ,  mem_phi->in(LoopNode::EntryControl) );
-  call->init_req( TypeFunc::ReturnAdr, C->start()->proj_out(TypeFunc::ReturnAdr) );
-  call->init_req( TypeFunc::FramePtr, C->start()->proj_out(TypeFunc::FramePtr) );
+  call->init_req(TypeFunc::Control,   head->init_control());
+  call->init_req(TypeFunc::I_O,       C->top());       // Does no I/O.
+  call->init_req(TypeFunc::Memory,    mem_phi->in(LoopNode::EntryControl));
+  call->init_req(TypeFunc::ReturnAdr, C->start()->proj_out(TypeFunc::ReturnAdr));
+  call->init_req(TypeFunc::FramePtr,  C->start()->proj_out(TypeFunc::FramePtr));
   _igvn.register_new_node_with_optimizer(call);
   result_ctrl = new (C) ProjNode(call,TypeFunc::Control);
   _igvn.register_new_node_with_optimizer(result_ctrl);
--- a/hotspot/src/share/vm/opto/runtime.cpp	Fri Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/share/vm/opto/runtime.cpp	Wed Sep 18 14:34:56 2013 -0700
@@ -795,11 +795,20 @@
 
 
 const TypeFunc* OptoRuntime::array_fill_Type() {
-  // create input type (domain): pointer, int, size_t
-  const Type** fields = TypeTuple::fields(3 LP64_ONLY( + 1));
+  const Type** fields;
   int argp = TypeFunc::Parms;
-  fields[argp++] = TypePtr::NOTNULL;
-  fields[argp++] = TypeInt::INT;
+  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[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 Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp	Wed Sep 18 14:34:56 2013 -0700
@@ -2714,6 +2714,71 @@
 }
 #endif // ndef HAVE_DTRACE_H
 
+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)
--- a/hotspot/src/share/vm/runtime/sharedRuntime.hpp	Fri Sep 13 22:50:47 2013 +0200
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.hpp	Wed Sep 18 14:34:56 2013 -0700
@@ -366,6 +366,16 @@
   static int c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, VMRegPair *regs2,
                                   int total_args_passed);
 
+  // Compute the new number of arguments in the signature if 32 bit ints
+  // must be converted to longs. Needed if CCallingConventionRequiresIntsAsLongs
+  // is true.
+  static int  convert_ints_to_longints_argcnt(int in_args_count, BasicType* in_sig_bt);
+  // Adapt a method's signature if it contains 32 bit integers that must
+  // be converted to longs. Needed if CCallingConventionRequiresIntsAsLongs
+  // is true.
+  static void convert_ints_to_longints(int i2l_argcnt, int& in_args_count,
+                                       BasicType*& in_sig_bt, VMRegPair*& in_regs);
+
   // Generate I2C and C2I adapters. These adapters are simple argument marshalling
   // blobs. Unlike adapters in the tiger and earlier releases the code in these
   // blobs does not create a new frame and are therefore virtually invisible
@@ -378,7 +388,7 @@
   // location for the interpreter to record. This is used by the frame code
   // to correct the sender code to match up with the stack pointer when the
   // thread left the compiled code. In addition it allows the interpreter
-  // to remove the space the c2i adapter allocated to do it argument conversion.
+  // to remove the space the c2i adapter allocated to do its argument conversion.
 
   // Although a c2i blob will always run interpreted even if compiled code is
   // present if we see that compiled code is present the compiled call site