From 05fa054ca0bd3c427e96f4aaf42caf3ee02ca172 Mon Sep 17 00:00:00 2001 From: goetz Date: Wed, 18 Sep 2013 14:34:56 -0700 Subject: [PATCH] 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 --- src/cpu/ppc/vm/globalDefinitions_ppc.hpp | 8 ++- src/cpu/ppc/vm/sharedRuntime_ppc.cpp | 6 +- src/cpu/sparc/vm/globalDefinitions_sparc.hpp | 6 ++ src/cpu/x86/vm/globalDefinitions_x86.hpp | 6 ++ src/cpu/zero/vm/globalDefinitions_zero.hpp | 6 ++ src/share/vm/opto/generateOptoStub.cpp | 28 +++++++-- src/share/vm/opto/loopTransform.cpp | 29 ++++++--- src/share/vm/opto/runtime.cpp | 17 +++-- src/share/vm/runtime/sharedRuntime.cpp | 65 ++++++++++++++++++++ src/share/vm/runtime/sharedRuntime.hpp | 12 +++- 10 files changed, 157 insertions(+), 26 deletions(-) diff --git a/src/cpu/ppc/vm/globalDefinitions_ppc.hpp b/src/cpu/ppc/vm/globalDefinitions_ppc.hpp index 889d2857b..aa8b89f90 100644 --- a/src/cpu/ppc/vm/globalDefinitions_ppc.hpp +++ b/src/cpu/ppc/vm/globalDefinitions_ppc.hpp @@ -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 diff --git a/src/cpu/ppc/vm/sharedRuntime_ppc.cpp b/src/cpu/ppc/vm/sharedRuntime_ppc.cpp index 444882ffd..e9d217601 100644 --- a/src/cpu/ppc/vm/sharedRuntime_ppc.cpp +++ b/src/cpu/ppc/vm/sharedRuntime_ppc.cpp @@ -734,11 +734,8 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, // 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 @@ static address gen_c2i_adapter(MacroAssembler *masm, 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(); diff --git a/src/cpu/sparc/vm/globalDefinitions_sparc.hpp b/src/cpu/sparc/vm/globalDefinitions_sparc.hpp index cadaffe73..8bf65375c 100644 --- a/src/cpu/sparc/vm/globalDefinitions_sparc.hpp +++ b/src/cpu/sparc/vm/globalDefinitions_sparc.hpp @@ -30,6 +30,12 @@ const int BytesPerInstWord = 4; 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 diff --git a/src/cpu/x86/vm/globalDefinitions_x86.hpp b/src/cpu/x86/vm/globalDefinitions_x86.hpp index 0771794ff..d0b685aef 100644 --- a/src/cpu/x86/vm/globalDefinitions_x86.hpp +++ b/src/cpu/x86/vm/globalDefinitions_x86.hpp @@ -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 diff --git a/src/cpu/zero/vm/globalDefinitions_zero.hpp b/src/cpu/zero/vm/globalDefinitions_zero.hpp index ceb010f3c..5777b8ff9 100644 --- a/src/cpu/zero/vm/globalDefinitions_zero.hpp +++ b/src/cpu/zero/vm/globalDefinitions_zero.hpp @@ -28,4 +28,10 @@ #include +// 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 diff --git a/src/share/vm/opto/generateOptoStub.cpp b/src/share/vm/opto/generateOptoStub.cpp index a229faaee..832f9f951 100644 --- a/src/share/vm/opto/generateOptoStub.cpp +++ b/src/share/vm/opto/generateOptoStub.cpp @@ -117,8 +117,16 @@ void GraphKit::gen_stub(address C_function, 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( ; cntfield_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 @@ void GraphKit::gen_stub(address C_function, // Set fixed predefined input arguments cnt = 0; - for( i=0; iinit_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( ; iinit_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 diff --git a/src/share/vm/opto/loopTransform.cpp b/src/share/vm/opto/loopTransform.cpp index a30fc80df..2db3d7c09 100644 --- a/src/share/vm/opto/loopTransform.cpp +++ b/src/share/vm/opto/loopTransform.cpp @@ -2692,27 +2692,38 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { _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); diff --git a/src/share/vm/opto/runtime.cpp b/src/share/vm/opto/runtime.cpp index 73f40e2d3..bd5d13700 100644 --- a/src/share/vm/opto/runtime.cpp +++ b/src/share/vm/opto/runtime.cpp @@ -795,11 +795,20 @@ const TypeFunc* OptoRuntime::generic_arraycopy_Type() { 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); diff --git a/src/share/vm/runtime/sharedRuntime.cpp b/src/share/vm/runtime/sharedRuntime.cpp index 32f045a96..81210beba 100644 --- a/src/share/vm/runtime/sharedRuntime.cpp +++ b/src/share/vm/runtime/sharedRuntime.cpp @@ -2714,6 +2714,71 @@ void SharedRuntime::get_utf(oopDesc* src, address dst) { } #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) diff --git a/src/share/vm/runtime/sharedRuntime.hpp b/src/share/vm/runtime/sharedRuntime.hpp index 8ca5da3b5..7e3abee63 100644 --- a/src/share/vm/runtime/sharedRuntime.hpp +++ b/src/share/vm/runtime/sharedRuntime.hpp @@ -366,6 +366,16 @@ class SharedRuntime: AllStatic { 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 @@ class SharedRuntime: AllStatic { // 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 -- GitLab