From nobody Mon Sep 17 00:00:00 2001 From: Alexander Astapchuk Date: Thu, 7 Dec 2006 16:39:32 +0600 Subject: [PATCH] [drlvm][fastcc]preliminary: vm part --- vm/include/jit_import.h | 27 +++ vm/include/jit_intf.h | 10 - vm/port/include/callconv.h | 115 +++++++++++++ vm/port/include/callsig.h | 134 +++++++++++++++ vm/port/src/encoder/ia32_em64t/enc_defs.h | 19 +- vm/port/src/encoder/ia32_em64t/encoder.cpp | 13 + vm/port/src/encoder/ia32_em64t/encoder.h | 27 ++- vm/port/src/encoder/ia32_em64t/encoder.inl | 2 vm/port/src/misc/callconv.cpp | 238 +++++++++++++++++++++++++++ vm/vmcore/include/environment.h | 6 + vm/vmcore/src/class_support/Environment.cpp | 1 vm/vmcore/src/init/parse_arguments.cpp | 48 +++++ 12 files changed, 603 insertions(+), 37 deletions(-) create mode 100644 vm/port/include/callconv.h create mode 100644 vm/port/include/callsig.h create mode 100644 vm/port/src/misc/callconv.cpp d7cdfbdb7221b790ac1780c146a8fb4424be200a diff --git a/vm/include/jit_import.h b/vm/include/jit_import.h index 736f1aa..9aadc0d 100644 --- a/vm/include/jit_import.h +++ b/vm/include/jit_import.h @@ -474,11 +474,32 @@ VMEXPORT Boolean jit_may_inline_object_s Boolean *jit_clears_ccv); typedef enum CallingConvention { + /** + * Empty value. + */ + CC_Unknown, + /** + * A default calling convention for managed code. + * Platform dependent. + */ CC_Vm, - CC_Jrockit, - CC_Rotor, + /** + * STDCALL - where applicable, default platform convention otherwise. + */ CC_Stdcall, - CC_Cdecl + /** + * CDECL - where applicable, default platform convention otherwise. + */ + CC_Cdecl, + /** + * FASTCALL on IA-32, default platform convention otherwise. + */ + CC_Fastcall, + /** + * Special tunable calling convention for managed code on IA-32. + * On other platforms same as CC_Vm. + */ + CC_DRLFast } CallingConvention; VMEXPORT CallingConvention vm_managed_calling_convention(); diff --git a/vm/include/jit_intf.h b/vm/include/jit_intf.h index 3ed963e..af7d39d 100644 --- a/vm/include/jit_intf.h +++ b/vm/include/jit_intf.h @@ -25,8 +25,9 @@ #ifndef _JIT_INTF_H_ #define _JIT_INTF_H_ - -#include "open/types.h" +#include "open/vm.h" +#include "jit_export.h" +#include "jit_import.h" #ifdef __cplusplus extern "C" { @@ -35,11 +36,6 @@ #endif typedef const void *Arg_List_Iterator; // Java only typedef const void *Arg_List_Iter; - -#include "open/vm.h" -#include "jit_export.h" -#include "jit_import.h" - // // The implementation of those types is private to the VM. // The JIT uses them as handles. diff --git a/vm/port/include/callconv.h b/vm/port/include/callconv.h new file mode 100644 index 0000000..d6041b1 --- /dev/null +++ b/vm/port/include/callconv.h @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Definitions for calling conventions routines. + * + * @author Alexander Astapchuk + */ +#if !defined(__CALLCONV_H_INCLUDED__) +#define __CALLCONV_H_INCLUDED__ + +#ifdef _IPF_ + #error "Wrong header for this platform." +#endif + +#include "enc_base.h" +#include "jit_import.h" + + +/** + * Enables or disables usage of DRLFast calling convention. + * + * @note Intentionally left VM-private, as it's only allowed to call + * this function on VM's startup before a first method gets compiled. + */ +/*VMEXPORT*/ void vm_cc_enable_drlfast(bool b); + +/** + * Parses VM properties and initializes calling convention properties. + * + * @note Intentionally left VM-private, as it's only allowed to call + * this function on VM's startup before a first method gets compiled. + */ +/*VMEXPORT*/ void vm_cc_init_from_properties(void); + + +/** + * \c true if DRLFast calling convention is enabled. + */ +VMEXPORT bool vm_cc_is_drlfast_enabled(void); + +/** + * Returns a RegName used to return float/double values according to the + * specified calling convention. + */ +VMEXPORT RegName vm_cc_get_fp_ret_register(CallingConvention cc); + +/** + * \c true if the given register is callee-save for the specified calling + * convention. \c false otherwise. + */ +VMEXPORT bool vm_cc_is_callee_save_reg(CallingConvention cc, RegName reg); + +/** + * \c true if callee restores stack. + */ +VMEXPORT bool vm_cc_is_callee_restores_stack(CallingConvention cc); + +/** + * Returns a CallingConvention by the given name. + * + * The name is case-insensitive. If the name could not be recognized, + * then CC_Unknown returned. + */ +VMEXPORT CallingConvention vm_cc_parse_name(const char* name); + +/** + * Represents a method call signature. + */ +typedef void* CallSigHandle; + +/** + * Allocates signature handle for the specified calling convention. + * + * @note The allocated handle must be destroyed with vm_cc_csig_destroy(). + */ +VMEXPORT CallSigHandle vm_cc_csig_create(CallingConvention cc); + +/** + * Adds argument of the given type to the call signature. + */ +VMEXPORT void vm_cc_csig_add_arg(CallSigHandle csh, VM_Data_Type type); + +/** + * Returns a register used to pass the argument, or RegNull if the register + * must be passed on stack. + */ +VMEXPORT RegName vm_cc_csig_get_arg_reg(CallSigHandle csh, unsigned index); + +/** + * Returns an offset from ESP/RSP for the given argument. + */ +VMEXPORT int vm_cc_csig_get_arg_offset(CallSigHandle csh, unsigned index); + +/** + * Releases resources associated with the call signature. + */ +VMEXPORT void vm_cc_csig_destroy(CallSigHandle csh); + +#endif //~ ifdef __CALLCONV_H_INCLUDED__ + diff --git a/vm/port/include/callsig.h b/vm/port/include/callsig.h new file mode 100644 index 0000000..465466f --- /dev/null +++ b/vm/port/include/callsig.h @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Definition for CallSig class. + * + * @author Alexander Astapchuk + */ +#if !defined(__CALLSIG_H_INCLUDED__) +#define __CALLSIG_H_INCLUDED__ + +#ifdef _IPF_ + #error "Wrong header for this platform." +#endif + +#if !defined(BUILDING_VM) + #error "VM-private header, must only be included in VM stuff." +#endif + +#include "open/types.h" +#include "enc_base.h" +#include "jit_import.h" +#include +using std::vector; + + + +struct CallSig { +public: + static void set_fast_managed_cc(bool on); + static bool is_callee_save(CallingConvention cc, RegName reg); + static RegName get_fp_ret_register(CallingConvention cc); + // + CallSig(Method_Signature_Handle msh) + { + init(msh); + } + + CallSig(void) + { + init(CC_Unknown); + } + + + CallSig(CallingConvention cc, + VM_Data_Type arg0 = VM_DATA_TYPE_VOID, + VM_Data_Type arg1 = VM_DATA_TYPE_VOID, + VM_Data_Type arg2 = VM_DATA_TYPE_VOID) + { + if (arg0 != VM_DATA_TYPE_VOID) { add_arg(arg0); } + if (arg1 != VM_DATA_TYPE_VOID) { add_arg(arg1); } + if (arg2 != VM_DATA_TYPE_VOID) { add_arg(arg2); } + init(cc); + } + + void init(Method_Signature_Handle msh); + + unsigned get_stack_size(void) const + { + return m_stackSize; + } + + unsigned get_num_args(void) const + { + return m_args.size(); + } + + VM_Data_Type get_arg_type(unsigned i) const + { + return m_args.at(i); + } + + RegName get_arg_reg(unsigned i, bool low=true) const + { + return low ? m_regs.at(i) : m_regsHi.at(i); + } + + int get_arg_offset(unsigned i, bool low=true) const + { + return low ? m_offs.at(i) : m_offsHi.at(i); + } + + RegName get_fp_ret_register(void) const + { + return get_fp_ret_register(m_cc); + } + + bool callee_restores_stack(void) const + { + //XXX fix for Intel64 + if (m_cc == CC_Cdecl) return false; + return true; + } + + CallingConvention get_cc(void) const + { + return m_cc; + } + + bool l2r(void) const { return m_l2r; } + + void add_arg(VM_Data_Type type); + //ZZZ rename into, say, 'calculate/build/compute' + void init(CallingConvention cc); + +private: + CallingConvention m_cc; + bool m_l2r; + vector m_args; + vector m_regs; + vector m_regsHi; + vector m_offs; + vector m_offsHi; + unsigned m_stackSize; + static bool sm_use_drlfast; + static bool sm_drlfast_use_xmm_ret; +}; + + +#endif //~ ifdef __CALLSIG_H_INCLUDED__ + diff --git a/vm/port/src/encoder/ia32_em64t/enc_defs.h b/vm/port/src/encoder/ia32_em64t/enc_defs.h index 6876224..10ad188 100644 --- a/vm/port/src/encoder/ia32_em64t/enc_defs.h +++ b/vm/port/src/encoder/ia32_em64t/enc_defs.h @@ -47,18 +47,23 @@ #ifdef _EM64T_ */ #define REG_STACK RegName_RSP /** - * A max GP register (with a highest index number) - */ - #define REG_MAX RegName_R15 - /** * Total number of GP registers including stack pointer. */ - #define MAX_REGS 15 + #define MAX_GP_REGS 15 + #define MAX_XMM_REGS 15 + #define FIRST_XMM_REG RegName_XMM0 + #define LAST_XMM_REG RegName_XMM15 + #define FIRST_GP_REG RegName_RAX + #define LAST_GP_REG RegName_R15 #else #define REG_STACK RegName_ESP - #define REG_MAX RegName_EDI typedef long int_ptr; - #define MAX_REGS 8 + #define MAX_GP_REGS 8 + #define MAX_XMM_REGS 8 + #define FIRST_XMM_REG RegName_XMM0 + #define LAST_XMM_REG RegName_XMM7 + #define FIRST_GP_REG RegName_EAX + #define LAST_GP_REG RegName_EDI #endif ENCODER_NAMESPACE_START diff --git a/vm/port/src/encoder/ia32_em64t/encoder.cpp b/vm/port/src/encoder/ia32_em64t/encoder.cpp index 106ce29..fbbc19f 100644 --- a/vm/port/src/encoder/ia32_em64t/encoder.cpp +++ b/vm/port/src/encoder/ia32_em64t/encoder.cpp @@ -98,7 +98,7 @@ extern const Mnemonic map_of_shift_opcod const RegName map_of_regno_2_regname [] = { #ifdef _EM64T_ RegName_RAX, RegName_RBX, RegName_RCX, RegName_RDX, - RegName_RDI, RegName_RSI, RegName_RSP, RegName_RBP, + RegName_RDI, RegName_RSI, RegName_RBP, RegName_R8, RegName_R9, RegName_R10, RegName_R11, RegName_R12, RegName_R13, RegName_R14, RegName_R15, RegName_XMM0, RegName_XMM1, RegName_XMM2, RegName_XMM3, @@ -108,13 +108,12 @@ #ifdef _EM64T_ #else RegName_EAX, RegName_EBX, RegName_ECX, RegName_EDX, - RegName_EDI, RegName_ESI, RegName_ESP, RegName_EBP, + RegName_EDI, RegName_ESI, RegName_EBP, RegName_XMM0, RegName_XMM1, RegName_XMM2, RegName_XMM3, RegName_XMM4, RegName_XMM5, RegName_XMM6, RegName_XMM7, - RegName_FS, #endif // _EM64T_ - - RegName_Null, + RegName_Null, // == n_reg, + REG_STACK // == sp_reg }; const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[] = { @@ -143,7 +142,7 @@ static int debug_check() { // performs checks of some presumptions // 1. all items of Encoder.h:enum Reg_No must be mapped plus n_reg->RegName_Null - assert(countof(map_of_regno_2_regname) == n_reg + 1); + assert(countof(map_of_regno_2_regname) == n_reg + 2); assert(countof(map_of_alu_opcode_2_mnemonic) == n_alu); assert(countof(map_of_shift_opcode_2_mnemonic) == n_shift); return 0; @@ -151,6 +150,4 @@ static int debug_check() { static int dummy = debug_check(); -// can have this - initialization order problems.... static int dummy_run_the_debug_test = debug_check(); - #endif diff --git a/vm/port/src/encoder/ia32_em64t/encoder.h b/vm/port/src/encoder/ia32_em64t/encoder.h index 2bcd156..3c8860b 100644 --- a/vm/port/src/encoder/ia32_em64t/encoder.h +++ b/vm/port/src/encoder/ia32_em64t/encoder.h @@ -16,7 +16,6 @@ */ /** * @author Alexander V. Astapchuk - * @version $Revision: 1.1.2.2.4.5 $ */ /** * @file @@ -54,25 +53,37 @@ #endif enum Reg_No { #ifdef _EM64T_ rax_reg = 0,rbx_reg, rcx_reg, rdx_reg, - rdi_reg, rsi_reg, rsp_reg, rbp_reg, + rdi_reg, rsi_reg, rbp_reg, r8_reg, r9_reg, r10_reg, r11_reg, r12_reg, r13_reg, r14_reg, r15_reg, + xmm0_reg, xmm1_reg, xmm2_reg, xmm3_reg, xmm4_reg, xmm5_reg, xmm6_reg, xmm7_reg, xmm8_reg, xmm9_reg, xmm10_reg, xmm11_reg, xmm12_reg, xmm13_reg, xmm14_reg, xmm15_reg, - + /** @brief Total number of registers. NOT including RSP.*/ + n_reg, + rsp_reg, sp_reg = rsp_reg, #else // !defined(_EM64T_) - eax_reg = 0,ebx_reg, ecx_reg, edx_reg, - edi_reg, esi_reg, esp_reg, ebp_reg, + edi_reg, esi_reg, ebp_reg, xmm0_reg, xmm1_reg, xmm2_reg, xmm3_reg, xmm4_reg, xmm5_reg, xmm6_reg, xmm7_reg, - fs_reg, + /** @brief Total number of registers. NOT including ESP.*/ + n_reg, + esp_reg, sp_reg = esp_reg, +#endif + /** @brief Total number of registers. Including SP.*/ + total_regs, +#ifdef _EM64T_ + n_gp_regs = 15, + n_xmm_regs = 16, +#else + n_gp_regs = 7, + n_xmm_regs = 8, #endif - /** @brief Total number of registers.*/ - n_reg }; + // // instruction operand sizes: 8,16,32,64 bits // diff --git a/vm/port/src/encoder/ia32_em64t/encoder.inl b/vm/port/src/encoder/ia32_em64t/encoder.inl index e8a51b5..c9e89bc 100644 --- a/vm/port/src/encoder/ia32_em64t/encoder.inl +++ b/vm/port/src/encoder/ia32_em64t/encoder.inl @@ -33,7 +33,7 @@ extern const Mnemonic S_map_of_condition extern const Mnemonic U_map_of_condition_code_2_branch_mnemonic[]; inline static RegName map_reg(Reg_No r) { - assert(r >= 0 && r <= n_reg); + assert(r >= 0 && r <= sp_reg); return map_of_regno_2_regname[r]; } diff --git a/vm/port/src/misc/callconv.cpp b/vm/port/src/misc/callconv.cpp new file mode 100644 index 0000000..0098321 --- /dev/null +++ b/vm/port/src/misc/callconv.cpp @@ -0,0 +1,238 @@ +#include "callsig.h" +#include "callconv.h" +#include + +#include +using std::bitset; + +#if defined(_WIN32) + #define strcasecmp stricmp +#endif + +/// Whether DRLFast convention is enabled. +static bool sm_drlfast_enabled = false; + +/// Whether DRLFast convention uses FPU to return float point values. +static bool sm_drlfast_use_xmm_ret = true; + +/// Callee save XMM registers for DRLFast. +static bitset sm_drlfast_xmm_callee_save_flags(0); + + +static RegName sm_drlfast_gr_args[MAX_GP_REGS] = { + RegName_ECX, RegName_EDX, RegName_EAX, RegName_Null +}; + +static RegName sm_drlfast_fr_args[MAX_XMM_REGS] = { + RegName_XMM0, RegName_XMM1, RegName_XMM2, RegName_XMM3, RegName_Null +}; + + +static unsigned get_type_bytes_on_stack(VM_Data_Type type) +{ + if (type == VM_DATA_TYPE_INT64 || type == VM_DATA_TYPE_F8) { + return 8; + } + assert(type != VM_DATA_TYPE_UINT64); // Can't happen in valid Java + return STACK_SLOT_SIZE; // everything else takes 1 stack slot which is 4 bytes +} + + +static bool is_f(VM_Data_Type type) +{ + return (VM_DATA_TYPE_F8 == type || VM_DATA_TYPE_F4 == type); +} + +void vm_cc_enable_drlfast(bool b) +{ +#ifdef _EM64T_ + // no op +#else + sm_drlfast_enabled = b; +#endif +} + +bool vm_cc_is_drlfast_enabled(void) +{ +#ifdef _EM64T_ + return false; +#else + return sm_drlfast_enabled; +#endif +} + +void vm_cc_init_from_properties(void) +{ + +} + + +CallingConvention vm_cc_parse_name(const char* name) +{ + if (!strcasecmp(name, "vm")) { return CC_Vm; } + if (!strcasecmp(name, "stdcall")) { return CC_Stdcall; }; + if (!strcasecmp(name, "cdecl")) { return CC_Cdecl; }; + if (!strcasecmp(name, "fastcall")) { return CC_Fastcall; }; + if (!strcasecmp(name, "drlfast")) { return CC_DRLFast; }; + if (!strcasecmp(name, "default") || !strcasecmp(name, "managed")) { + return vm_cc_is_drlfast_enabled() ? CC_DRLFast : CC_Vm; + }; + return CC_Unknown; +} + +RegName vm_cc_get_fp_ret_register(CallingConvention cc) +{ +#ifdef _EM64T_ + return RegName_XMM0; +#else + if (CC_DRLFast == cc && vm_cc_is_drlfast_enabled() && sm_drlfast_use_xmm_ret) { + return RegName_XMM0; + } + return RegName_FP0; +#endif +} + +bool vm_cc_is_callee_save_reg(CallingConvention cc, RegName reg) +{ + if(reg == RegName_Null) { + return false; + } + OpndKind regKind = getRegKind(reg); + // + // Callee-save XMMs are only known for DRLFast + if (regKind == OpndKind_XMMReg && cc == CC_DRLFast && vm_cc_is_drlfast_enabled()) { + unsigned index = getRegIndex(reg); + return sm_drlfast_xmm_callee_save_flags[0]; + } + if (regKind != OpndKind_GPReg) { + return false; + } + // + + if (equals(reg, RegName_EBX)) return true; + if (equals(reg, RegName_EBP)) return true; +#ifdef _EM64T_ + if (equals(reg, RegName_R12)) return true; + if (equals(reg, RegName_R13)) return true; + if (equals(reg, RegName_R14)) return true; + if (equals(reg, RegName_R15)) return true; +#else + if (equals(reg, RegName_ESI)) return true; + if (equals(reg, RegName_EDI)) return true; +#endif + return false; +} + +CallSigHandle vm_cc_csig_create(CallingConvention cc) +{ + return new CallSig(cc); +} + +void vm_cc_csig_add_arg(CallSigHandle csh, VM_Data_Type type) +{ + CallSig* pSig = (CallSig*)csh; + pSig->add_arg(type); +} + +RegName vm_cc_csig_get_arg_reg(CallSigHandle csh, unsigned index) +{ + CallSig* pSig = (CallSig*)csh; + return pSig->get_arg_reg(index); +} + +int vm_cc_csig_get_arg_offset(CallSigHandle csh, unsigned index) +{ + CallSig* pSig = (CallSig*)csh; + return pSig->get_arg_offset(index); +} + +void vm_cc_csig_destroy(CallSigHandle csh) +{ + CallSig* pSig = (CallSig*)csh; + delete pSig; +} + + +void CallSig::init(Method_Signature_Handle msh) +{ + unsigned num_args = method_args_get_number(msh); + m_args.resize(num_args, VM_DATA_TYPE_INVALID); + for (unsigned i=0; i\n" \ + " Specifies calling convention for managed code\n" \ + " The possible values are:\n" \ + " default - default DRLVM calling convention\n" \ + " fast - IA-32 fastcall\n" \ + " stdcall - IA-32 stdcall\n" \ + " cdecl - IA-32 cdecl\n" \ + " drlfast - DRLVM-specific variant of fastcall\n" \ + " drlfast has the following properties (* means defaul):\n" \ + " -XDvm.drlfast.use_fpu_ret={true*,false}\n" \ + " -XDvm.drlfast.gp_reg_args={0*,1,2,3}\n" \ + " -XDvm.drlfast.xmm_reg_args={0*,1,2,3,4,5,6,7,8}\n" \ + " -XDvm.drlfast.xmm_callee_save_args={0*,1,2,3,4,5,6,7,8}\n" +#else + #define CALLING_CONVENTION_OPTIONS_HELP +#endif + + ECHO(" -Xbootclasspath:\n" " Set bootclasspath to the specified value\n" " -Xbootclasspath/a:\n" @@ -206,7 +230,9 @@ #endif // VM_STATS " -XnoCleanupOnExit\n" " Exit without cleaning internal resorces\n" " -XD=\n" - " set an internal system property\n"); + " set an internal system property\n" + CALLING_CONVENTION_OPTIONS_HELP + ); } //print_help_on_nonstandard_options void print_vm_standard_properties() @@ -520,13 +546,29 @@ #endif //_DEBUG p_env->assert_reg->enable_system = false; } } - +#ifdef _IA32_ + else if (begins_with(option, "-Xcc:")) { + const char* cname = option+4; + CallingConvention cc = vm_cc_parse_name(cname); + if (cc == CC_Unknown) { + ECHO("Unknown calling convention" << cname << "\n"); + } + else { + p_env->managed_calling_convention = cc; + if (cc == CC_DRLFast) { + vm_cc_enable_drlfast(true); + } + } + } +#endif else { ECHO("Unknown option " << option << USE_JAVA_HELP); LOGGER_EXIT(1); } } // for - +#ifdef _IA32_ + vm_cc_init_from_properties(); +#endif apr_pool_destroy(pool); } //parse_vm_arguments -- 1.3.3