From f6409250b9cfd6ec70503ca33c033166c062f32f Mon Sep 17 00:00:00 2001 From: Alexander Astapchuk Date: Sat, 13 Jan 2007 17:07:23 +0600 Subject: [PATCH 2/6] [drlvm][fastcc]Fast calling convention for IA-32; part vm/port --- vm/port/include/callconv.h | 117 + vm/port/include/callsig.h | 264 ++ vm/port/include/lil.h | 61 +- vm/port/src/encoder/ia32_em64t/enc_defs.h | 19 +- vm/port/src/encoder/ia32_em64t/encoder.cpp | 23 +- vm/port/src/encoder/ia32_em64t/encoder.h | 230 +- vm/port/src/encoder/ia32_em64t/encoder.inl | 62 +- .../em64t/pim/include/lil_code_generator_em64t.h | 12 +- .../src/lil/em64t/pim/lil_code_generator_em64t.cpp | 12 +- .../lil/ia32/pim/include/lil_code_generator_ia32.h | 716 ++++++- .../src/lil/ia32/pim/lil_code_generator_ia32.cpp | 2661 +++++++++++--------- vm/port/src/lil/ia32/pim/m2n_ia32.cpp | 328 ++- vm/port/src/lil/ia32/pim/m2n_ia32_internal.h | 60 +- vm/port/src/lil/ia32/pim/stack_iterator_ia32.cpp | 226 ++- vm/port/src/lil/lil.cpp | 65 +- vm/port/src/lil/lil_dbg.cpp | 178 ++ vm/port/src/misc/ia32/callconv.cpp | 400 +++ 17 files changed, 3800 insertions(+), 1634 deletions(-) diff --git a/vm/port/include/callconv.h b/vm/port/include/callconv.h new file mode 100644 index 0000000..20608e3 --- /dev/null +++ b/vm/port/include/callconv.h @@ -0,0 +1,117 @@ +/* + * 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" + +struct Global_Env; + + +/** + * 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(Global_Env *p_env); + + +/** + * \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..2033162 --- /dev/null +++ b/vm/port/include/callsig.h @@ -0,0 +1,264 @@ +/* + * 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 "callconv.h" +#include +using std::vector; + + +/** + * Represents call signature, and carries info about arguments layout - + * stack or registers. + * + * The CallSig may be constructed in 2 ways. + * + * One way is to create an empty CallSig (using default ctor) then fill up + * info about arguments (add_arg()) and then invoke calculate(). + * + * Another way may be used when all arguments are ready - e.g. having + * Method_Signature_Handle or a simple list of up to 3 argument types. The + * non-default ctor-s invoke calculate() itself. + * + * After the CallSig is completely calculated, its properties and properties + * of arguments may be retrieved. For an argument that must be passed + * through the stack, the offset is measured from the ESP (RSP) presuming + * the stack for arguments is allocated first. In other words the following + * calling sequence presumed:
+ *
+ *  sub     esp, csig.get_stack_size()
+ *  mov     [esp+csig.get_arg_offset(i)], arg#i
+ * 
+ * + * The CallSig calculations base on presumption that arguments do not get + * splitted. That says, that if an argument does not fit into a register, + * it get passed through the stack (like int64 on IA-32). + * + * @note The CallSig is intended to work for both IA-32 and Intel64, but + * Intel64 part is not fully implemented yet. TODO? + */ +struct CallSig { +public: + /** + * Constructs empty CallSig. + * @see add_arg + * @see calculate + */ + CallSig(void) + { + calculate(CC_Unknown); + } + + /** + * Constructs CallSig from Method_Signature_Handle, presuming current + * managed calling convention. + */ + CallSig(Method_Signature_Handle msh) + { + init(msh); + } + + /** + * Constructs CallSig with specified calling convention up to 3 arguments. + */ + 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); } + calculate(cc); + } + + /** + * Calculates info about arguments - which register to use or an offset + * in the stack. + */ + void calculate(CallingConvention cc); + + + /** + * Returns size of stack occupied by the arguments. + */ + unsigned get_stack_size(void) const + { + return m_stackSize; + } + + /** + * Returns number of arguments. + */ + unsigned get_num_args(void) const + { + return m_args.size(); + } + + /** + * Returns type of the i-th artgument. + */ + VM_Data_Type get_arg_type(unsigned i) const + { + return m_args.at(i); + } + + /** + * Returns RegName to be used to pass the i-th argument, or RegName_Null + * if argument is passed through the stack. + */ + RegName get_arg_reg(unsigned i) const + { + return m_regs.at(i); + } + + /** + * Returns offset of the argument in the stack or -1 if argument must + * be passed on a register. + */ + int get_arg_offset(unsigned i) const + { + return m_offs.at(i); + } + + /** + * Returns RegName to be used to return float and double values. + * Depending on calling convention this may be either RegName_FP0 or + * RegName_XMM0 (or even something more exotic). + * + * The returned RegName denotes the full size of the register. That is + * RegName_XMM0 (of 128 bit) may be returned but neither RegName_XMM0D + * (of 64 bit) nor RegName_XMM0S (of 32 bits). + */ + RegName get_fp_ret_register(void) const + { + return vm_cc_get_fp_ret_register(m_cc); + } + + /** + * \c true if callee must restore stack. + */ + bool callee_restores_stack(void) const + { +#ifdef _EM64T_ + return false; +#else + if (m_cc == CC_Cdecl) return false; + return true; +#endif + } + + /** + * Returns CallingConvention. + * + * Returns CC_Unknown if it was not set yet. + */ + CallingConvention get_cc(void) const + { + return m_cc; + } + + /** + * \c true if the reg is callee-save in the current calling convention. + */ + bool is_callee_save(RegName reg) + { + return vm_cc_is_callee_save_reg(m_cc, reg); + } + + /** + * \c true if the args are pushed into stack from the left to right. + * + * iN other words, l2r==false means that leftmost (arg#0) is on top of + * stack. l2r==true means that the rightmost argument is on the top of + * the stack. + */ + bool l2r(void) const + { + return m_l2r; + } + + /** + * Adds a type of the next argument to the current calling convention. + * + * The arguments must be added from left to right. + */ + void add_arg(VM_Data_Type type); + + /** + * Checks whether the current CallSig instance had its arguments info + * calculated. + */ + void ensure_calculated(void) + { + if (!m_calculated) { + calculate(m_cc); + } + } + +private: + /** + * Internal helper. For each argument in the passed signature invokes + * add_arg() and then invokes calculate(). + */ + void init(Method_Signature_Handle msh); + + /// \c true if CallSig instance had its info calculated. + bool m_calculated; + /// CallingConvention used or CC_Unknown if it was not set yet. + CallingConvention m_cc; + /// Whether the arguments are *pushed* into the stack in left-to-right order. + bool m_l2r; + /// Vector denoting types of arguments. + vector m_args; + /** + * Vector denoting registers used for arguments. Filled in calculate(). + * RegName_Null means that argument is passed on stack. + */ + vector m_regs; + /** + * Vector of offsets of arguments. -1 means that argument is passed on + * stack. + */ + vector m_offs; + /** + * Total size of stack needed to pass arguments. May include alignment + * (where applicable). + */ + unsigned m_stackSize; +}; + + +#endif //~ ifdef __CALLSIG_H_INCLUDED__ + diff --git a/vm/port/include/lil.h b/vm/port/include/lil.h index b01ff27..a23705c 100644 --- a/vm/port/include/lil.h +++ b/vm/port/include/lil.h @@ -263,7 +263,7 @@ enum LilLdX { LLX_None, LLX_Zero, LLX_Si enum LilCallKind { LCK_Call, LCK_CallNoRet, LCK_TailCall }; enum LilType { LT_G1, LT_G2, LT_G4, LT_G8, LT_F4, LT_F8, LT_Ref, LT_PInt, LT_Void }; -enum LilCc { LCC_Platform, LCC_Managed, LCC_Rth, LCC_Jni, LCC_StdCall }; +enum LilCc { LCC_Platform, LCC_Managed, LCC_ManagedFast, LCC_Rth, LCC_Jni, LCC_StdCall }; struct LilVariable { enum LilVariableKind tag; @@ -327,9 +327,15 @@ VMEXPORT LilType lil_ic_get_type(LilCode // refers to the situation _before_ the instruction. VMEXPORT LilType lil_instruction_get_dest_type(LilCodeStub*, LilInstruction*, LilInstructionContext*); +/** + * \c true is the given instruion is Lil's TRAP instruction. + * + * Safely accepts NULL-s and returns \c false in this case. + */ +VMEXPORT bool lil_instruction_is_trap(const LilInstruction* i); -//*** Interogators +//*** Interrogators // Is a LIL code stub valid? VMEXPORT bool lil_is_valid(LilCodeStub* cs); @@ -508,10 +514,10 @@ class LilInstructionVisitor { virtual void asgn(LilVariable*, enum LilOperation, LilOperand*, LilOperand*) = 0; virtual void ts(LilVariable*) = 0; virtual void handles(LilOperand*) = 0; - virtual void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilLdX) = 0; - virtual void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilOperand* src) = 0; - virtual void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel) = 0; - virtual void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, + virtual void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, int offset, LilAcqRel, LilLdX) = 0; + virtual void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, int offset, LilAcqRel, LilOperand* src) = 0; + virtual void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, int offset, LilAcqRel) = 0; + virtual void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, int offset, LilAcqRel, LilOperand* cmp, LilOperand* src, LilLabel) = 0; virtual void j(LilLabel) = 0; virtual void jc(LilPredicate, LilOperand*, LilOperand*, LilLabel) = 0; @@ -522,6 +528,7 @@ class LilInstructionVisitor { virtual void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) = 0; virtual void m2n_save_all() = 0; virtual void pop_m2n() = 0; + virtual void trap(void) = 0; virtual void print(char *, LilOperand *) = 0; }; @@ -542,10 +549,10 @@ class LilInstructionVisitor_Default : pu void asgn(LilVariable*, enum LilOperation, LilOperand*, LilOperand*) {} void ts(LilVariable*) {} void handles(LilOperand*) {} - void ld(LilType UNREF t, LilVariable* UNREF dst, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel, LilLdX) {} - void st(LilType UNREF t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel, LilOperand* UNREF src) {} - void inc(LilType UNREF t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel) {} - void cas(LilType UNREF t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel, + void ld(LilType UNREF t, LilVariable* UNREF dst, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, int offset, LilAcqRel, LilLdX) {} + void st(LilType UNREF t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, int offset, LilAcqRel, LilOperand* UNREF src) {} + void inc(LilType UNREF t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, int UNREF offset, LilAcqRel) {} + void cas(LilType UNREF t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, int UNREF offset, LilAcqRel, LilOperand* UNREF cmp, LilOperand* UNREF src, LilLabel) {} void j(LilLabel) {} void jc(LilPredicate, LilOperand*, LilOperand*, LilLabel) {} @@ -556,6 +563,7 @@ class LilInstructionVisitor_Default : pu void push_m2n(Method_Handle UNREF method, frame_type UNREF current_frame_type, bool UNREF handles) {} void m2n_save_all() {} void pop_m2n() {} + void trap(void) {}; void print(char *, LilOperand *) {} }; @@ -565,16 +573,16 @@ VMEXPORT void lil_visit_instruction(LilI // Return variable's kind -VMEXPORT LilVariableKind lil_variable_get_kind(LilVariable* v); +VMEXPORT LilVariableKind lil_variable_get_kind(const LilVariable* v); // Return variable's index -VMEXPORT unsigned lil_variable_get_index(LilVariable* v); +VMEXPORT unsigned lil_variable_get_index(const LilVariable* v); // Are two variables the same -VMEXPORT bool lil_variable_is_equal(LilVariable* v1, LilVariable* v2); +VMEXPORT bool lil_variable_is_equal(const LilVariable* v1, const LilVariable* v2); // Is operand an immediate? -VMEXPORT bool lil_operand_is_immed(LilOperand* o); +VMEXPORT bool lil_operand_is_immed(const LilOperand* o); // If operand is an immediate return the immediate value -VMEXPORT POINTER_SIZE_INT lil_operand_get_immed(LilOperand* o); +VMEXPORT POINTER_SIZE_INT lil_operand_get_immed(const LilOperand* o); // If operand is not an immediate return the variable it represents VMEXPORT LilVariable* lil_operand_get_variable(LilOperand* o); @@ -598,6 +606,29 @@ VMEXPORT bool lil_predicate_is_signed(Li //*** Printers +#define LIL_DUMP_FILE_NAME "lil.dump.txt" + +/** + * Returns a FILE* to dump the debugging log into. The file is created if + * it does not exist, and is overwritten if does. + */ +VMEXPORT FILE* lil_dbg_get_dump_file(void); + +/// Prints out a text into debugging log file, preceded with "== start:". +VMEXPORT void lil_dbg_dump_start(const char* name); +/** + * Dumps out disassembled code. The function only perform disassembling if + * it can load a dynamic disassembler provider. + * + * Refer to the Jitrino.JET's LWDIS library for more details. + * + * If the disassembler provider is not presented, the function only prints + * out code bytes. + */ +VMEXPORT void lil_dbg_dump(const char* code, unsigned code_len, bool showAddr = false); +/// Prints out a text into debugging log file, preceded with "== end:". +VMEXPORT void lil_dbg_dump_finish(const char* name); + // Print a type to a stream VMEXPORT void lil_print_type(FILE*, LilType); // Print a signature to a stream diff --git a/vm/port/src/encoder/ia32_em64t/enc_defs.h b/vm/port/src/encoder/ia32_em64t/enc_defs.h index 5752853..7276293 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 @@ */ #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..6e49bc0 100644 --- a/vm/port/src/encoder/ia32_em64t/encoder.cpp +++ b/vm/port/src/encoder/ia32_em64t/encoder.cpp @@ -87,6 +87,14 @@ XMM_Opnd xmm5_opnd(xmm5_reg); XMM_Opnd xmm6_opnd(xmm6_reg); XMM_Opnd xmm7_opnd(xmm7_reg); +const M_Base_Opnd tos_opnd(stack_reg, 0); +const M_Base_Opnd stack_slot_0(stack_reg, 0); +const M_Base_Opnd stack_slot_1(stack_reg, STACK_SLOT_SIZE); + +const Imm_Opnd imm_zero_opnd(size_platf, 0); +const Imm_Opnd imm_one_opnd(size_platf, 1); +const Imm_Opnd imm_minus_one_opnd(size_platf, -1); + #define countof(a) (sizeof(a)/sizeof(a[0])) @@ -98,7 +106,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 +116,12 @@ const RegName map_of_regno_2_regname [] #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[] = { @@ -141,9 +148,9 @@ const Mnemonic map_of_shift_opcode_2_mne 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 +158,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..9e6dc3b 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 @@ -52,27 +51,71 @@ #endif enum Reg_No { + first_reg, #ifdef _EM64T_ - rax_reg = 0,rbx_reg, rcx_reg, rdx_reg, - rdi_reg, rsi_reg, rsp_reg, rbp_reg, + rax_reg = first_reg, + rbx_reg, rcx_reg, rdx_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, stack_reg = rsp_reg, #else // !defined(_EM64T_) - - eax_reg = 0,ebx_reg, ecx_reg, edx_reg, - edi_reg, esi_reg, esp_reg, ebp_reg, + eax_reg=first_reg, + ebx_reg, ecx_reg, edx_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, stack_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 }; + +inline bool is_xmm(Reg_No reg) +{ +#ifdef _EM64T_ + return xmm0_reg <= reg && reg <= xmm15_reg; +#else + return xmm0_reg <= reg && reg <= xmm7_reg; +#endif +} + +inline bool is_xmm_reg(Reg_No reg) { return is_xmm(reg); }; + +inline Reg_No reg_from_index(unsigned i) +{ + Reg_No reg = (Reg_No)(first_reg + i); + return reg; +} + +inline unsigned get_reg_index(Reg_No reg) +{ + unsigned index = (reg-first_reg); + assert(index #include -extern const RegName map_of_regno_2_regname[]; +extern const RegName map_of_regno_2_regname[total_regs]; extern const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[]; extern const Mnemonic map_of_alu_opcode_2_mnemonic[]; extern const Mnemonic map_of_shift_opcode_2_mnemonic[]; @@ -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 <= stack_reg); return map_of_regno_2_regname[r]; } @@ -42,22 +42,56 @@ inline static OpndSize map_size(Opnd_Siz return map_of_EncoderOpndSize_2_RealOpndSize[o_size]; } -inline static Mnemonic map_alu(ALU_Opcode alu) { +inline static Mnemonic map_alu(ALU_Opcode alu) +{ assert(alu >= 0 && alu < n_alu); return map_of_alu_opcode_2_mnemonic[alu]; } -inline static Mnemonic map_shift(Shift_Opcode shc) { +inline static Mnemonic map_shift(Shift_Opcode shc) +{ assert(shc >= 0 && shc < n_shift); return map_of_shift_opcode_2_mnemonic[shc]; } -inline static bool fit8(int64 val) { +inline static bool fit8(int64 val) +{ return (CHAR_MIN <= val) && (val <= CHAR_MAX); } -inline static bool fit32(int64 val) { - return val == (int64)(int32)val; +inline bool fit16(int64 val) +{ + return (SHRT_MIN <= val) && (val <= SHRT_MAX); +} + +inline bool fit32(int64 val) +{ + return (INT_MIN <= val) && (val <= INT_MAX); +} + +inline Reg_No to_Reg_No(RegName regName) +{ + if (regName == RegName_Null) return n_reg; + if (getRegKind(regName) == OpndKind_XMMReg) { + unsigned index = getRegIndex(regName); + assert(index < MAX_XMM_REGS); + return (Reg_No)(xmm0_reg+index); + } + // No FPU RegName-s handled by this method - only GP regs. + assert(getRegKind(regName) == OpndKind_GPReg); + for (unsigned i=0; i=0); + return size() + offset; + } + + // + // Frame offsets below. Every next offset depends on previous one, + // beginning from the get_ret_addr(). + // + + int get_ret_addr(void) const + { + return 0; + } + /** + * @note Must be very first in the frame - see comments on top of this + * file for rationale. + */ + int get_std_places(void) const + { + return -(int)(MAX_NUMBER_STD_PLACES*STACK_SLOT_SIZE); + } + + int get_std_place(unsigned i) const + { + assert(i0 && offset0); + --m_tmp_grs_allocated; + } + + Reg_No alloc_tmp_xmm(void) + { + static const Reg_No tmp_regs[] = {xmm0_reg, xmm1_reg}; + assert(m_tmp_grs_allocated0); + --m_tmp_xmms_allocated; + } + + /** + * returns true if m2n contains local handles + */ + bool m2n_has_handles() const { + return m_m2n_has_handles; + } + /// \c true if stub declares std places + bool declares_std_places(void) const { return m_declares_std_places; } + /// \c true if stub uses std places + bool uses_std_places(void) const { return m_uses_std_places; } + + int adjust(int offset_from_esp) const + { + return m_stackLayout.adjust(offset_from_esp) + m_stack_depth; + } + + int adjust_input_or_ret(int offset_from_esp) const + { + return m_stackLayout.adjust_input_or_ret(offset_from_esp) + m_stack_depth; + } + + // returns the offset of the start of the m2n frame from ESP + unsigned get_m2n_offset() const { + return adjust(m_stackLayout.get_m2n()); + } + + unsigned get_stk_local_offset(unsigned i) const { + return adjust(m_stackLayout.get_local(i)); + } + + unsigned get_stk_std_place_offset(unsigned i) const { + return adjust(m_stackLayout.get_std_place(i)); + } + + unsigned get_stk_reg_spill_offset(Reg_No reg) const { + return adjust(m_stackLayout.get_m2n_reg(reg)); + } + + unsigned get_return_value_save_offset(void) const + { + return adjust(m_stackLayout.get_ret_value_save()); + } + + unsigned get_ret_addr_offset(void) const + { + return adjust_input_or_ret(m_stackLayout.get_ret_addr()); + } + + // returns the offset of the first "allocatable" byte + unsigned get_alloc_start_offset() const { + return adjust(m_stackLayout.get_allocate_area()); + } + + /// returns size of allocatable memory on the stack + unsigned get_stk_total_alloc_size() const { return stk_alloc_size; } + + // returns the size of the stack frame + unsigned get_stk_size() const { + return m_stackLayout.size(); + } + +private: + + /* Helper functions, used by visitor functions */ + + // gather info from variable + void check_variable(LilVariable * var, bool lvalue) { + switch (lil_variable_get_kind(var)) { + case LVK_In: + // it's illegal to redefine inputs + assert(!lvalue); + // arbitrary stubs should not access inputs + assert(!lil_sig_is_arbitrary(lil_cs_get_sig(cs))); + // check if we use inputs after normal call + if (does_normal_calls) { + save_inputs = true; + } + break; + case LVK_Out: + if (lvalue) { + save_inputs = true; + } + break; + case LVK_StdPlace: + m_uses_std_places = true; + break; + default:; + } + } + + // gather info from operand + void check_operand(LilOperand * o, bool lvalue) { + if (o != NULL && !lil_operand_is_immed(o)) { + check_variable(lil_operand_get_variable(o), lvalue); + } + } + + //************************** + // visitor functions + + void label(LilLabel label) { + // nothing to do here + } + + void locals(unsigned num) { + m_num_locals = num; + } + + void std_places(unsigned num) { + m_declares_std_places = true; + } + + void alloc(LilVariable * var, unsigned alloc_space) { + stk_alloc_size += align_8(alloc_space); + } + + void asgn(LilVariable * var, LilOperation operation, LilOperand * op1, LilOperand * op2) { + check_variable(var, true); + check_operand(op1, false); + if (lil_operation_is_binary(operation)) { + check_operand(op2, false); + } + } + + void ts(LilVariable * var) { + does_normal_calls = true; + check_variable(var, true); + } + + + void handles(LilOperand * op) { + check_operand(op, true); + } + + void ld(LilType t, LilVariable * dst, LilVariable * base, unsigned scale, + LilVariable * index, POINTER_SIZE_SINT offset, LilAcqRel, LilLdX) { + check_variable(dst, true); + if (base != NULL) { + check_variable(base, false); + } + if (index != NULL) { + check_variable(index, false); + } + } + + void st(LilType t, LilVariable * base, unsigned scale, LilVariable * index, + POINTER_SIZE_SINT offset, LilAcqRel, LilOperand * src) { + if (base != NULL) { + check_variable(base, false); + } + if (index != NULL) { + check_variable(index, false); + } + check_operand(src, false); + } + + void inc(LilType t, LilVariable * base, unsigned scale, LilVariable * index, + POINTER_SIZE_SINT offset, LilAcqRel) { + if (base != NULL) { + check_variable(base, false); + } + if (index != NULL) { + check_variable(index, false); + } + } + + void cas(LilType t, LilVariable * base, unsigned scale, LilVariable * index, + POINTER_SIZE_SINT offset, LilAcqRel, LilOperand * cmp, LilOperand * src, LilLabel) { + if (base != NULL) { + check_variable(base, false); + } + if (index != NULL) { + check_variable(index, false); + } + check_operand(cmp, false); + check_operand(src, false); + } + + void j(LilLabel) { + // nothing to do + } + + void jc(LilPredicate p, LilOperand* o1, LilOperand* o2, LilLabel) { + check_operand(o1, false); + if (lil_predicate_is_binary(p)) { + check_operand(o2, false); + } + } + + void out(LilSig* sig) {} + + void in2out(LilSig * sig) { + assert(!lil_sig_is_arbitrary(lil_cs_get_sig(cs))); + // check if we need to save inputs + if (does_normal_calls) { + save_inputs = true; + } + out(sig); + } + + void call(LilOperand* o, LilCallKind k) { + check_operand(o, false); + if (k == LCK_Call) { + does_normal_calls = true; + } else if (k == LCK_TailCall) { + // no need to reserve extra outputs, like in in2out + // since tailcall is implemented differently + does_tail_calls = true; + } + } + + void ret() {} + + void push_m2n(Method_Handle method, frame_type current_frame_type, bool has_handles) { + m_m2n_has_handles = has_handles; + has_m2n = true; // remember that this stub requires an m2n frame + does_normal_calls = true; + } + + void m2n_save_all() {} + + void pop_m2n() { + bool handles = lil_ic_get_m2n_state(iter.get_context()) == LMS_Handles; + if (handles) { + // it will execute a call + does_normal_calls = true; + } + } + + void trap(void) {}; + void print(char *, LilOperand *) {} +}; + +/** + * an enum indicating a variable's location: in a register class or on the + * stack. + */ +enum LilLocationKind { + /// general purpose registers + LLK_Gr, + /// 128-bit XMM registers + LLK_Fr, + /// memory stack + LLK_Stk, + /// Special case for IA-32 - top of FPU stack + LLK_FPU +}; + +/** + * keeps location of a LIL variable + */ +class LilVarLocation { + +public: + /// Location of the variable + const LilLocationKind kind; + /// Reg_No or SP-relative offset - interpretation depends on kind value. + const int addr; + + LilVarLocation(LilLocationKind k_, int a_): kind(k_), addr(a_) {} + + bool operator==(const LilVarLocation & loc) const { + return (kind == loc.kind && addr == loc.addr); + } + + bool operator!=(const LilVarLocation & loc) const { + return (kind != loc.kind || addr != loc.addr); + } + + void * operator new(size_t sz, tl::MemoryPool & m) { + return m.alloc(sz); + } + + void operator delete (void * p, tl::MemoryPool & m) {} + +private: + LilVarLocation(LilVarLocation &); // disable copying + LilVarLocation & operator=(const LilVarLocation &); // disable copying +}; + +class LilCodeGeneratorIa32 : public LilCodeGenerator { +public: + LilCodeGeneratorIa32() {} protected: - NativeCodePtr compile_main(LilCodeStub* , size_t*, PoolManager*); + NativeCodePtr compile_main(LilCodeStub*, size_t*, PoolManager*); }; #endif // _LIL_CODE_GENERATOR_IA32_ diff --git a/vm/port/src/lil/ia32/pim/lil_code_generator_ia32.cpp b/vm/port/src/lil/ia32/pim/lil_code_generator_ia32.cpp index d290427..bbbb834 100644 --- a/vm/port/src/lil/ia32/pim/lil_code_generator_ia32.cpp +++ b/vm/port/src/lil/ia32/pim/lil_code_generator_ia32.cpp @@ -14,1464 +14,1615 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.2.4.5 $ - */ - +/** + * @author Alexander Astapchuk + * + * Based on Evgueni Brevnov's codegen for Intel64. + */ #include #include +#include +#include #define LOG_DOMAIN "vm.helpers" #include "cxxlog.h" +#include "open/types.h" +#include "tl/memory_pool.h" + +#include "encoder.h" + #include "lil.h" #include "lil_code_generator.h" #include "lil_code_generator_ia32.h" #include "lil_code_generator_utils.h" #include "m2n.h" #include "m2n_ia32_internal.h" -#include "vm_threads.h" -#include "encoder.h" -// Strategy: -// Up to 2 standard places -// Up to 4 32-bit quantities can be used as locals -// (i.e. 4 integer locals, or 2 int and 1 long local, or 2 long locals) -// In, m2n, alloc, and out are on stack -// Standard places in edx, ecx -// Returns go where the calling convention requires -// Locals in ebp, ebx, esi, and edi. - -// Things that need fixing: -// * Arguments are 4 bytes and return always in eax -// * Temp register is eax, which might stomp on return -// * Base must be register for st -// * Index in addresses must be register - -// Register allocation: -// -// Stack: -// (hi) |-------------------------| -// | args | -// |----------+--------------| -// | | return ip | -// | m2n (or) |--------------| -// | | callee saves | -// |----------+--------------| -// | alloced memory | -// |-------------------------| -// | out | -// (lo) |-------------------------| -// -// Return value is in eax, eax/edx, or top of floating point stack depending upon type. -// sp0 is in edx, sp1 is in ecx -// Locals use ebp, ebx, esi, edi, ecx, and edx, in that order -// (one of they are 32-bit, 2 if they are 64 bit -// for example: if l0 is 32 bit, l1 64 bit, and l2 32 bit, they'll use: -// l0 -> ebp, l1 -> ebx:esi, l2 -> edi) - -LilCodeGeneratorIa32::LilCodeGeneratorIa32() - : LilCodeGenerator() +#include +using std::bitset; + +#include "dump.h" + +#ifdef _EM64T_ + #define LilType_Platf LT_G8 +#else + #define LilType_Platf LT_G4 +#endif + +#pragma warning(disable:4244) + +/** + * Returns a register dedicated to hold a local#n. + */ +static Reg_No get_reg_for_local(unsigned n) { + if (n == 0) return ebp_reg; + if (n == 1) return ebx_reg; + if (n == 2) return esi_reg; + if (n == 3) return edi_reg; + assert(false); + return n_reg; } -/////////////////////////////////////// -// Prepass information -enum LcgIa32OpLocType { LOLT_Stack, LOLT_Reg, LOLT_Immed, LOLT_Tofs }; +LilCodeGenContext::LilCodeGenContext(LilCodeStub * stub, tl::MemoryPool & m): + cs(stub), mem(m), iter(cs, true) { -struct LcgIa32OpLoc { - LcgIa32OpLocType t; - union { - unsigned v; // Offset for stack, value for immediate - struct { - R_Opnd* r1; - R_Opnd* r2; - } r; // For register - } u; -}; + stk_alloc_size = 0; -// Represents information about the address part of a ld, st, inc, or cas -struct LcgIa32Addr { - LcgIa32OpLoc base_loc, index_loc; - bool has_base, has_index; - R_Opnd* base_reg; - R_Opnd* index_reg; - M_Opnd* addr; -}; + m_m2n_has_handles = false; + does_normal_calls = false; + does_tail_calls = false; + calls_unmanaged_code = false; + has_m2n = false; + save_inputs = false; + m_num_locals = 0; -enum LcgIa32SpecialOp { LSO_None, LSO_Lea }; - -enum LcgIa32ConGen { LCG_Cmp, LCG_Test, LCG_IsZeroG8 }; - -struct LcgIa32InstInfo { - unsigned size_after; - LilType t; - LcgIa32OpLoc loc1, loc2, loc3; - R_Opnd* r1; - R_Opnd* r2; - bool mov_to_dst; - unsigned temp_register; - union { - struct { - bool mov_to_r; - LcgIa32SpecialOp special; - } asgn; - struct { - LcgIa32ConGen cg; - bool immed, stack, swap, invert; - ConditionCode cc; - } jc; - unsigned out; - LcgIa32Addr address; // used by ld, st, inc - unsigned pop_m2n; - } u; -}; + m_stack_depth = 0; + m_tmp_xmms_allocated = 0; + m_tmp_grs_allocated = 0; -struct LcgIa32PrePassInfo { - unsigned num_is; - unsigned num_callee_saves; // How many of the callee saves are used (order is same as m2n frame) - bool short_jumps; - size_t size; - LcgIa32InstInfo* is; -}; + m_declares_std_places = false; + m_uses_std_places = false; -// Push arguments left to right or right to left -enum LilArgOrder { LAO_L2r, LAO_R2l }; + /* 2) SCAN THE CODE STUB FOR THE INFORMATION */ + while (!iter.at_end()) { + lil_visit_instruction(iter.get_current(), this); + iter.goto_next(); + } +} -struct LcgIa32CcInfo { - LilArgOrder arg_order; - bool callee_pop; -}; +/** + * Implementation notes: + * 1) Current implementation doesn't correctly processes back branches when + * input arguments are accessed + */ +class LilCodeGen : public LilInstructionVisitor { -struct LcgIa32Context { - LcgIa32PrePassInfo* info; - LilInstructionContext* ctxt; - LcgIa32CcInfo entry_cc; - LilSig* entry_sig; - LcgIa32CcInfo out_cc; -}; + enum StdPlacesDestiny { StdPlaces_Keep, StdPlaces_Drop }; + /// Maximum length of the code generated for a single LIL instruction + static const size_t MAX_INST_SIZE = 0x100; -/////////////////////////////////////// -// Stack layout stuff + char * buf_beg; // pointer to the beginning position of the generated code + char * buf; // pointer to the current position of the generated code + /// Size of the allocated buffer in buf_beg + unsigned m_max_buffer_size; -static void cc_to_cc_info(LcgIa32CcInfo* info, LilCc cc) -{ - switch (cc) { - case LCC_Managed: - info->arg_order = LAO_L2r; - info->callee_pop = true; - break; -#ifdef PLATFORM_POSIX - case LCC_Jni: -#endif - case LCC_Platform: - info->arg_order = LAO_R2l; - info->callee_pop = false; - break; -#ifndef PLATFORM_POSIX - case LCC_Jni: -#endif - case LCC_Rth: - case LCC_StdCall: - info->arg_order = LAO_R2l; - info->callee_pop = true; - break; - default: ASSERT(0, "Unknown calling convention"); - } -} + LilCguLabelAddresses labels; // a set of defined labels and theirs addresses -static bool type_in_two_regs(LilType t) -{ - switch (t) { - case LT_G1: - case LT_G2: - case LT_G4: - case LT_F4: - case LT_Ref: - case LT_PInt: - return false; - case LT_G8: - case LT_F8: - return true; - default: ASSERT(0, "Unexpected LIL type"); for(;;); - } -} + LilCodeStub * cs; + LilCodeGenContext & context; + tl::MemoryPool & mem; + LilInstructionIterator iter; -static unsigned type_number_regs(LilType t) -{ - return (t==LT_Void ? 0 : type_in_two_regs(t) ? 2 : 1); -} + LilInstructionContext * ic; // visit functions can always assume that inst points to the + LilInstruction * inst; // current instruction and ic points to the current context -static unsigned type_number_return_registers(LilType t) -{ - switch (t) { - case LT_Void: - case LT_F4: - case LT_F8: - return 0; - default: - return (type_in_two_regs(t) ? 2 : 1); - } -} + const LilVarLocation * stack_loc; // location denoting stack pointer + unsigned current_alloc; // keeps track of memory allocation -static unsigned type_size_on_stack(LilType t) -{ - switch (t) { - case LT_G1: - case LT_G2: - case LT_G4: - case LT_F4: - case LT_Ref: - case LT_PInt: - return 4; - case LT_G8: - case LT_F8: - return 8; - default: ASSERT(0, "Unexpected LIL type"); for(;;); + LilCallSig m_csig; + /// \c true if we need to keep all the registers (e.g. arbitrary calling convention) + bool m_save_all_regs; + bitset m_regsToRestore; +private: + + /* Inner Classes */ + class Tmp_GR_Opnd : public R_Opnd { + private: + LilCodeGenContext& m_context; + public: + Tmp_GR_Opnd(LilCodeGenContext& context, LilInstructionContext*) : R_Opnd(n_reg), m_context(context) + { + _reg_no = m_context.alloc_tmp_gr(); + } + + virtual ~Tmp_GR_Opnd() { + m_context.free_tmp_gr(); + } + private: + void *operator new(size_t sz, tl::MemoryPool& m) + { + return m.alloc(sz); + } + void operator delete(void*, tl::MemoryPool&) {}; + void operator delete(void*) {}; + }; + + class Tmp_FR_Opnd: public XMM_Opnd { + private: + LilCodeGenContext& m_context; + public: + Tmp_FR_Opnd(LilCodeGenContext& context, LilInstructionContext*): XMM_Opnd(0), m_context(context) + { + Reg_No reg = context.alloc_tmp_xmm(); + assert(is_xmm(reg)); + m_idx = reg-xmm0_reg; + } + virtual ~Tmp_FR_Opnd() { + m_context.free_tmp_xmm(); + } + }; + + /** + * returns the location of the n'th int local + */ + const LilVarLocation * get_gp_local(const unsigned n) const { + Reg_No reg = get_reg_for_local(n); + return new(mem) LilVarLocation(LLK_Gr, reg); + } + + // returns the location of the n'th standard place + const LilVarLocation * get_std_place(const unsigned n) const { + int off = context.get_stk_std_place_offset(n); + return new(mem) LilVarLocation(LLK_Stk, off); + } + + // returns location of the n'th input + // location of the n'th input can vary depending on the current instruction context + // in case there was a call then return input saved on the memory stack + const LilVarLocation * get_input(const unsigned i) const { + assert(i < m_csig.get_num_args()); + Reg_No reg = m_csig.get_arg_reg_no(i); + int offset = -1; + + if (n_reg == reg) { + // argument came on stack + offset = m_csig.get_arg_offset(i) + sizeof(void*); + offset = context.adjust_input_or_ret(offset); + } + else { + // argument came on register + offset = context.get_stk_reg_spill_offset(reg); + } + return new(mem) LilVarLocation(LLK_Stk, offset); } -} -static unsigned sig_size_on_stack(LilSig* s) -{ - unsigned size = 0; - for(unsigned i=0; iarg_order==LAO_R2l) - for(i=0; iidx; i--) - offset += type_size_on_stack(lil_sig_get_arg_type(s, i)); - return offset; -} + // returns the location of the n'th output + const LilVarLocation * get_output(const unsigned i, LilSig * out_sig) const + { + LilCallSig lilSig(out_sig); -static int m2n_base(LcgIa32Context* c) -{ - LilSig* s = lil_ic_get_out_sig(c->ctxt); - unsigned out = (s ? sig_size_on_stack(s) : 0); - return lil_ic_get_amt_alloced(c->ctxt) + out; -} + Reg_No reg = lilSig.get_arg_reg_no(i); + if (n_reg == reg) { + // argument came on stack + int offset = lilSig.get_arg_offset(i); + return new(mem) LilVarLocation(LLK_Stk, offset); + } + if (is_xmm(reg)) { + return new(mem) LilVarLocation(LLK_Fr, reg); + } + return new(mem) LilVarLocation(LLK_Gr, reg); + } + + /** + * returns the location of a LIL operand + * (input, output, local, std place, or return value) + * current instruction and instruction context is used + * is_lvalue: true if the variable is used as an l-value + */ + const LilVarLocation * get_op_loc(LilOperand * operand, bool is_lvalue) const { + assert(!lil_operand_is_immed(operand)); + return get_var_loc(lil_operand_get_variable(operand), is_lvalue); + } + + /** + * returns the location of a LIL variable + * (input, output, local, std place, or return value) + * current instruction and instruction context is used + * is_lvalue: true if the variable is used as an l-value + */ + const LilVarLocation * get_var_loc(const LilVariable * var, bool is_lvalue) const { + unsigned index = lil_variable_get_index(var); + LilVariableKind lvk = lil_variable_get_kind(var); + + if (LVK_In == lvk) { + return get_input(index); + } -static int in_base(LcgIa32Context* c) -{ - unsigned saved = (lil_ic_get_m2n_state(c->ctxt)!=LMS_NoM2n ? m2n_sizeof_m2n_frame : (1+c->info->num_callee_saves)*4); - return m2n_base(c) + saved; -} + if (LVK_StdPlace == lvk) { + return get_std_place(index); + } -/////////////////////////////////////// -// Operand conversion + if (LVK_Out == lvk) { + LilSig * out_sig = lil_ic_get_out_sig(ic); + assert(out_sig != NULL); + return get_output(index, out_sig); + } -// do a pre-prepass to determine the number of registers needed for locals -// this also checks that there are always enough regs for locals, -// std places, and the return -static unsigned get_num_regs_for_locals(LilCodeStub *cs) -{ - LilInstructionIterator iter(cs, true); - unsigned max_regs = 0; + if (LVK_Local == lvk) { + // no support for fp locals + return get_gp_local(index); + } - while (!iter.at_end()) { - LilInstructionContext *ic = iter.get_context(); - unsigned n_locals = lil_ic_get_num_locals(ic); - unsigned n_regs = 0; - for (unsigned i=0; i max_regs) - max_regs = n_regs; - - LilType rt = lil_ic_get_ret_type(ic); - unsigned UNUSED n_ret_regs = type_number_return_registers(rt); - - // check that locals, std_places, and return fit - assert(lil_ic_get_num_std_places(ic) + n_regs + n_ret_regs <= 7); - assert(lil_ic_get_num_std_places(ic)==0 || n_ret_regs<2); - iter.goto_next(); + if (LVK_Ret == lvk) { + int offset = context.get_return_value_save_offset(); + return new(mem) LilVarLocation(LLK_Stk, offset); + } + // should never be here + ASSERT(0, "Unknown variable kind"); + return NULL; } - assert(max_regs <= 6); - return max_regs; -} - -// Return the number of bytes of an instruction where loc is the primary operand -static unsigned size_loc(LcgIa32OpLoc* loc) -{ - switch (loc->t) { - case LOLT_Reg: return 2; - case LOLT_Stack: return (loc->u.v<124 ? 4 : 7); // 124 is used to work correctly for two words on the stack - case LOLT_Immed: return 5; - case LOLT_Tofs: return 8; - default: ASSERT(0, "Unexpected type"); for(;;); + static bool is_gr_reg(const LilVarLocation* varLoc) + { + return varLoc != NULL && varLoc->kind == LLK_Gr; } -} -// Return the register for the ith local -static R_Opnd* get_local_reg(unsigned i) { - switch (i) { - case 0: - return &ebp_opnd; - case 1: - return &ebx_opnd; - case 2: - return &esi_opnd; - case 3: - return &edi_opnd; - case 4: - return &ecx_opnd; - case 5: - return &edx_opnd; - default: - ASSERT(0, "Unexpected index"); - return NULL; + static Reg_No get_gr_reg(const LilVarLocation* varLoc) + { + return is_gr_reg(varLoc) ? (Reg_No)varLoc->addr : n_reg; } -} -static R_Opnd* get_temp_register(LcgIa32Context* c, unsigned num) -{ - LilType rt = lil_ic_get_ret_type(c->ctxt); - unsigned n_ret_regs = type_number_return_registers(rt); - if (n_ret_regs==0) - if (num==0) - return &eax_opnd; - else - num--; - unsigned n_std_places = lil_ic_get_num_std_places(c->ctxt); - unsigned n_locals = 0; - for (unsigned i=0; ictxt); i++) { - LilType t = lil_ic_get_local_type(c->ctxt, i); - n_locals += type_number_regs(t); + bool is_gr_reg(const LilVariable* lilVar) + { + return is_gr_reg(get_var_loc(lilVar, false)); } - if (n_ret_regs<=1 && n_std_places==0 && n_locals<=5) - if (num==0) - return &edx_opnd; - else - num--; - if (n_std_places<=1 && n_locals<=4) - if (num==0) - return &ecx_opnd; - else - num--; - if (n_std_places==0 && n_locals+num<=3) - return get_local_reg(3-num); - ASSERT(0, "All the possible cases are supposed to be already covered"); - return NULL; -} -static void variable_to_location(LcgIa32OpLoc* loc, LcgIa32Context* c, LilVariable* v, LilType t) -{ - bool two = type_in_two_regs(t); - switch (lil_variable_get_kind(v)) { - case LVK_In: - loc->t = LOLT_Stack; - loc->u.v = in_base(c) + offset_in_sig(&c->entry_cc, c->entry_sig, lil_variable_get_index(v)); - break; - case LVK_StdPlace: - assert(!two); - loc->t = LOLT_Reg; - switch (lil_variable_get_index(v)) { - case 0: loc->u.r.r1 = &edx_opnd; break; - case 1: loc->u.r.r1 = &ecx_opnd; break; - default: ASSERT(0, "Unexpected index"); - } - break; - case LVK_Out: - loc->t = LOLT_Stack; - loc->u.v = offset_in_sig(&c->out_cc, lil_ic_get_out_sig(c->ctxt), lil_variable_get_index(v)); - break; - case LVK_Local: + Reg_No get_gr_reg(const LilVariable* lilVar) { - loc->t = LOLT_Reg; - unsigned index = lil_variable_get_index(v); - // see how many regs are taken up by locals before this one - unsigned n_regs = 0; - for (unsigned i=0; ictxt, i); - n_regs += (t == LT_G8 || t == LT_F8) ? 2 : 1; - } - loc->u.r.r1 = get_local_reg(n_regs); - if (two) - loc->u.r.r2 = get_local_reg(n_regs+1); - break; + return get_gr_reg(get_var_loc(lilVar, false)); } - case LVK_Ret: - if (t==LT_F4 || t==LT_F8) { - loc->t = LOLT_Tofs; - } else { - loc->t = LOLT_Reg; - loc->u.r.r1 = &eax_opnd; - loc->u.r.r2 = &edx_opnd; - } - break; - default: ASSERT(0, "Unknown kind"); + + int64 get_imm_value(LilOperand * op) const { + assert(lil_operand_is_immed(op)); + return lil_operand_get_immed(op); } -} -static void operand_to_location(LcgIa32OpLoc* loc, LcgIa32Context* c, LilOperand* o, LilType t) -{ - if (lil_operand_is_immed(o)) { - loc->t = LOLT_Immed; - loc->u.v = lil_operand_get_immed(o); - } else { - variable_to_location(loc, c, lil_operand_get_variable(o), t); + inline const Imm_Opnd & get_imm_opnd(LilOperand * op, Opnd_Size sz = n_size) const { + return get_imm_opnd(get_imm_value(op), sz); } -} -void convert_addr(tl::MemoryPool* mem, LcgIa32Context* ctxt, LcgIa32Addr* out, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, unsigned* tmp_reg) -{ - out->has_base = (base!=NULL); - out->has_index = (index!=NULL); - out->base_reg = NULL; - out->index_reg = NULL; - - if (base) { - variable_to_location(&out->base_loc, ctxt, base, LT_PInt); - if (out->base_loc.t==LOLT_Reg) { - out->base_reg = out->base_loc.u.r.r1; - } else { - out->base_reg = get_temp_register(ctxt, *tmp_reg); - ++*tmp_reg; + inline const Imm_Opnd & get_imm_opnd(int64 value, Opnd_Size sz = n_size) const { + void * const mem_ptr = mem.alloc(sizeof(Imm_Opnd)); + if (sz == n_size) { + return *(new(mem_ptr) Imm_Opnd(fit32(value) ? size_32 : size_platf, value)); } + return *(new(mem_ptr) Imm_Opnd(sz, value)); } - if (index) { - assert(base); - variable_to_location(&out->index_loc, ctxt, index, LT_PInt); - if (out->base_loc.t==LOLT_Reg) { - out->index_reg = out->index_loc.u.r.r1; - } else { - out->index_reg = get_temp_register(ctxt, *tmp_reg); - ++*tmp_reg; - } + const R_Opnd & get_r_opnd(const LilVarLocation * loc) const { + assert(loc->kind == LLK_Gr); + Reg_No reg = (Reg_No)loc->addr; + void * const mem_ptr = mem.alloc(sizeof(R_Opnd)); + R_Opnd* preg_opnd = new(mem_ptr) R_Opnd(reg); + return *preg_opnd; } - void * const mem_ptr = mem->alloc(sizeof(M_Index_Opnd)); - if (base) - if (index) - out->addr = new(mem_ptr) M_Index_Opnd(out->base_reg->reg_no(), out->index_reg->reg_no(), offset, scale); - else - out->addr = new(mem_ptr) M_Index_Opnd(out->base_reg->reg_no(), n_reg, offset, 0); - else - out->addr = new(mem_ptr) M_Index_Opnd(n_reg, n_reg, offset, 0); -} + const XMM_Opnd & get_xmm_r_opnd(const LilVarLocation * loc) const { + assert(loc->kind == LLK_Fr); + Reg_No reg = (Reg_No)loc->addr; + void * const mem_ptr = mem.alloc(sizeof(XMM_Opnd)); + XMM_Opnd* preg_opnd = new(mem_ptr) XMM_Opnd(reg); + return *preg_opnd; + } -unsigned size_addr(LcgIa32Addr* addr) -{ - unsigned size = 5 + 1; // +1 is for the opcode - if (addr->has_base && addr->base_loc.t != LOLT_Reg) - size += size_loc(&addr->base_loc); - if (addr->has_index && addr->index_loc.t != LOLT_Reg) - size += size_loc(&addr->index_loc); - return size; -} + const M_Opnd & get_m_opnd(const LilVarLocation * loc) const { + assert(loc->kind == LLK_Stk); + void * const mem_ptr = mem.alloc(sizeof(M_Base_Opnd)); + return *(new(mem_ptr) M_Base_Opnd(stack_reg, loc->addr)); + } -////////////////////////////////////////////////////////////////////////// -// Pre Pass + const RM_Opnd & get_rm_opnd(const LilVarLocation * loc) const { + assert(loc->kind == LLK_Gr || loc->kind == LLK_Stk); + if (loc->kind == LLK_Gr) { + return get_r_opnd(loc); + } + //return M_Base_Opnd(stack_reg, loc->addr); + return get_m_opnd(loc); + } -class LcgIa32IntrPrePass : public LilInstructionVisitor { -public: - LcgIa32IntrPrePass(tl::MemoryPool* _mem, LilCodeStub* _cs, LcgIa32PrePassInfo* _info) - : mem(_mem), cs(_cs), info(_info), ii(NULL), rets(0), m2n_ops(0), js(0), jcs(0), tailcalls(0) - { - ctxt.info = info; - ctxt.ctxt = NULL; - ctxt.entry_sig = lil_cs_get_sig(cs); - cc_to_cc_info(&ctxt.entry_cc, lil_sig_get_cc(ctxt.entry_sig)); + const RM_Opnd & get_rm_opnd(const LilVariable * lilVar) const { + return get_rm_opnd(get_var_loc(lilVar, false)); } - void update_context(LilInstructionContext* c) - { - if (c && lil_ic_get_out_sig(c)) - cc_to_cc_info(&ctxt.out_cc, lil_sig_get_cc(lil_ic_get_out_sig(c))); - ctxt.ctxt = c; - if (ii) - ii++; - else - ii = info->is; - ii->temp_register = 0; + // move integer immediate to GR or memory stack + void move_imm(const LilVarLocation * dest, int64 imm_val) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_Stk); + buf = mov(buf, get_rm_opnd(dest), get_imm_opnd(imm_val), size_platf); } - void label(LilLabel UNREF l) { } - void locals(unsigned) { } - void std_places(unsigned) { } + // move between two register or stack locations + void move_rm(LilType dstType, const LilVarLocation* dst, const LilVarLocation* src) { + if (*dst == *src) { + return; // nothing to be done + } - void alloc(LilVariable* dst, unsigned amt) - { - variable_to_location(&ii->loc1, &ctxt, dst, LT_PInt); - info->size += (amt<=124 ? 3 : 6) + size_loc(&ii->loc1); - } + assert(!(dst->kind == LLK_Gr && src->kind == LLK_Fr)); + assert(!(dst->kind == LLK_Fr && src->kind == LLK_Gr)); - void prepass_dst(LilVariable* dst, LilType t) - { - variable_to_location(&ii->loc1, &ctxt, dst, t); - if (ii->loc1.t==LOLT_Reg) { - ii->r1 = ii->loc1.u.r.r1; - ii->r2 = ii->loc1.u.r.r2; - ii->mov_to_dst = false; - } else { - ii->r1 = get_temp_register(&ctxt, ii->temp_register++); - if (type_in_two_regs(t)) { - ii->r2 = get_temp_register(&ctxt, ii->temp_register++); + const bool src_is_mem = (src->kind == LLK_Stk); + const bool dst_is_mem = (dst->kind == LLK_Stk); + + if (src_is_mem && dst_is_mem) { + // memory=>memory move + const bool is_dbl = (dstType == LT_G8 || dstType == LT_F8 || LilType_Platf == LT_G8); + if (is_dbl) { + Tmp_FR_Opnd tmp(context, ic); + buf = sse_mov(buf, tmp, get_m_opnd(src), is_dbl); + buf = sse_mov(buf, get_m_opnd(dst), tmp, is_dbl); + } + else { + buf = mov(buf, ecx_opnd, get_m_opnd(src)); + buf = mov(buf, get_m_opnd(dst), ecx_opnd); } - ii->mov_to_dst = true; + return; } + + if (src->kind == LLK_Fr && dst->kind == LLK_Stk) { + // mem/mem move was handled above. + // only possible variant is 'm,r' + buf = sse_mov(buf, get_m_opnd(dst), get_xmm_r_opnd(src), true); + return; + } + + if (dst->kind == LLK_Gr) { + // mem/mem move was handled above. + // only possible variants are 'r,m' or 'r,r' + assert(src->kind == LLK_Gr || src->kind == LLK_Stk); + buf = mov(buf, get_r_opnd(dst), get_rm_opnd(src), size_platf); + return; + } + + if (dst->kind == LLK_Stk) { + // mem/mem move was handled above. + // only possible variant is 'm,r' + assert(src->kind == LLK_Gr); + buf = mov(buf, get_m_opnd(dst), get_r_opnd(src), size_platf); + return; + } + + if (dst->kind == LLK_Fr) { + // mem/mem move was handled above. + // only possible variants are 'r,m' or 'r,r' + if (src->kind == LLK_Fr) { + buf = sse_mov(buf, get_xmm_r_opnd(dst), get_xmm_r_opnd(src), true); + } + else { + buf = sse_mov(buf, get_xmm_r_opnd(dst), get_m_opnd(src), true); + } + return; + } + + //Huh?? We've already processed all the variants?? + assert(false); } - void asgn(LilVariable* dst, enum LilOperation op, LilOperand* o1, LilOperand* o2) - { - // Strategy: - // mov o1 -> r - // do op on r - // mov r -> dst - // However: - // This doesn't work if r==o2, so assert on this for now - // For s/zx1/2: can get o1 sign/zero extended into r with a movsz/movzx instruction, so skip mov o1->r - // For mov stack<-immed: can do this directly as the op - // For add of register & immed: can use lea to get this into r without mov o1->r - - assert(!lil_operation_is_binary(op) || lil_operand_is_immed(o2) || !lil_variable_is_equal(dst, lil_operand_get_variable(o2))); - - if (lil_operation_is_binary(op) && lil_operand_is_immed(o1)) - ii->t = lil_ic_get_type(cs, ctxt.ctxt, o2); - else - ii->t = lil_ic_get_type(cs, ctxt.ctxt, o1); - unsigned num_regs = type_number_regs(ii->t); - - assert(op==LO_Mov || !(ii->t==LT_F4 || ii->t==LT_F8 || ii->t==LT_G8)); - - operand_to_location(&ii->loc2, &ctxt, o1, ii->t); - if (lil_operation_is_binary(op)) { - operand_to_location(&ii->loc3, &ctxt, o2, ii->t); - } - - // Determine what r should be and whether the final move is needed - // This may be overriden below in the case of a move - prepass_dst(dst, ii->t); - - // Special cases - if ((op==LO_Add || op==LO_Sub) && ii->loc2.t==LOLT_Reg && ii->loc3.t==LOLT_Immed) { - // In this case use a lea instruction - ii->u.asgn.special = LSO_Lea; - ii->u.asgn.mov_to_r = false; - int n = (op==LO_Add ? ii->loc3.u.v : -(int)ii->loc3.u.v); - info->size += (n ? -128<=n && n<128 ? 3 : 6 : 2); - } else { - // General case - ii->u.asgn.special = LSO_None; - switch (op) { - case LO_Sx4: - case LO_Zx4: - // Treat sx4 & zx4 as a move - case LO_Mov: - // Treat immed->stack, the others are done by the movs o1->r->dst - if (ii->loc1.t==LOLT_Stack && ii->loc2.t==LOLT_Immed) { - ii->u.asgn.mov_to_r = false; - ii->mov_to_dst = false; - info->size += (size_loc(&ii->loc1)+4)*num_regs; - } else if ((ii->loc1.t==LOLT_Stack || ii->loc1.t==LOLT_Tofs) && ii->loc2.t==LOLT_Reg) { - // In this case change r to be o1 then r->dst will do the move - ii->r1 = ii->loc2.u.r.r1; - ii->r2 = ii->loc2.u.r.r2; - ii->u.asgn.mov_to_r = false; - } else { - ii->u.asgn.mov_to_r = (ii->loc2.t!=LOLT_Reg || ii->loc2.u.r.r1!=ii->r1); + void shift_op_imm_rm(const LilVarLocation * dest, int32 imm_val, const LilVarLocation * src) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_Stk); + assert(src->kind == LLK_Gr || src->kind == LLK_Stk); + // this code sequence can be optimized if dest is located in memory + move_imm(dest, imm_val); + bin_op_rm_rm(LO_Shl, dest, dest, src); + } + + void shift_op_rm_imm(const LilVarLocation * dest, const LilVarLocation * src, int32 imm_val) { + const Imm_Opnd & imm = get_imm_opnd(imm_val); + if (src->kind == LLK_Gr) { + if (dest->kind == LLK_Gr) { + buf = mov(buf, get_r_opnd(dest), get_r_opnd(src), size_platf); + } else { + buf = mov(buf, get_m_opnd(dest), get_r_opnd(src), size_platf); + } + buf = shift(buf, shl_opc, get_rm_opnd(dest), imm); + return; + } + // src->kind != LLK_Gr + if (dest->kind == LLK_Gr) { + buf = mov(buf, get_r_opnd(dest), get_m_opnd(src), size_platf); + buf = shift(buf, shl_opc, get_r_opnd(dest), imm, size_platf); + return; + } + // dest->kind != LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_m_opnd(src), size_platf); + buf = shift(buf, shl_opc, tmp_reg, imm); + buf = mov(buf, get_m_opnd(dest), tmp_reg); + } + + // subtract op where the first op is immediate + // (allowed only for intefer values) + void sub_op_imm_rm(const LilVarLocation * dest, int32 imm_val, const LilVarLocation * src) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_Stk); + assert(src->kind == LLK_Gr || src->kind == LLK_Stk); + // TODO: this code sequence can be optimized if dest is located in memory + alu_op_rm_imm(add_opc, dest, src, -imm_val); + } + + void alu_op_rm_imm(const ALU_Opcode alu_opc, const LilVarLocation * dest, + const LilVarLocation * src, int32 imm_val) { + assert(alu_opc < n_alu); + assert(dest->kind == LLK_Gr || dest->kind == LLK_Stk); + assert(src->kind == LLK_Gr || src->kind == LLK_Stk); + const Imm_Opnd & imm = get_imm_opnd(imm_val); + if (*dest == *src) { + buf = alu(buf, alu_opc, get_rm_opnd(dest), imm, size_platf); + return; + } + // dest != src + if (dest->kind == LLK_Gr) { + buf = mov(buf, get_r_opnd(dest), get_rm_opnd(src), size_platf); + buf = alu(buf, alu_opc, get_r_opnd(dest), imm, size_platf); + return; + } + // dest->kind != LLK_Gr + if (src->kind == LLK_Gr) { + buf = mov(buf, get_m_opnd(dest), get_r_opnd(src), size_platf); + buf = alu(buf, alu_opc, get_m_opnd(dest), imm, size_platf); + return; + } + // src->kind == LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_m_opnd(src), size_platf); + buf = alu(buf, alu_opc, tmp_reg, imm, size_platf); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_platf); + } + + void alu_op_rm_rm(const ALU_Opcode alu_opc, const LilVarLocation * dest, + const LilVarLocation * src1, const LilVarLocation * src2) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_Stk); + assert(src1->kind == LLK_Gr || src1->kind == LLK_Stk); + assert(src2->kind == LLK_Gr || src2->kind == LLK_Stk); + + if (*dest == *src1) { + if (dest->kind == LLK_Gr) { + buf = alu(buf, alu_opc, get_r_opnd(dest), get_rm_opnd(src2)); + return; + } + // dest->kind != LLK_Gr + if (src2->kind == LLK_Gr) { + buf = alu(buf, alu_opc, get_m_opnd(dest), get_r_opnd(src2)); + return; + } + // src2->kind != LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_m_opnd(src2), size_platf); + buf = alu(buf, alu_opc, get_m_opnd(dest), tmp_reg); + return; + } + // dest != src1 + if (dest->kind == LLK_Gr) { + buf = mov(buf, get_r_opnd(dest), get_rm_opnd(src1), size_platf); + buf = alu(buf, alu_opc, get_r_opnd(dest), get_rm_opnd(src2)); + return; + } + // dest->kind != LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_rm_opnd(src1), size_platf); + buf = alu(buf, alu_opc, tmp_reg, get_rm_opnd(src2)); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_platf); + } + + // where the second operand is immediate (allowed only for integer values!) + void bin_op_rm_imm(LilOperation o, const LilVarLocation* dest, + const LilVarLocation* src, int32 imm_val) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_Stk); + assert(src->kind == LLK_Gr || src->kind == LLK_Stk); + + switch (o) { + case LO_Add: + alu_op_rm_imm(add_opc, dest, src, imm_val); + break; + case LO_Sub: + alu_op_rm_imm(sub_opc, dest, src, imm_val); + break; + case LO_And: + alu_op_rm_imm(and_opc, dest, src, imm_val); + break; + case LO_SgMul: { + const Imm_Opnd & imm = get_imm_opnd(imm_val); + if (dest->kind == LLK_Gr) { + if (*dest == *src) { + buf = imul(buf, get_r_opnd(dest), imm); + } + else { + buf = imul(buf, get_r_opnd(dest), get_rm_opnd(src), imm); } break; - case LO_SgMul: - info->size++; - // Fall through - case LO_Add: - case LO_Sub: - case LO_Shl: - case LO_And: - ii->u.asgn.mov_to_r = (ii->loc2.t!=LOLT_Reg || ii->loc2.u.r.r1!=ii->r1); - info->size += size_loc(&ii->loc3); - break; - case LO_Neg: - ii->u.asgn.mov_to_r = (ii->loc2.t!=LOLT_Reg || ii->loc2.u.r.r1!=ii->r1); - info->size += 2; - break; - case LO_Not: - ASSERT(0, "Unexpected operation"); - case LO_Sx1: - case LO_Sx2: - case LO_Zx1: - case LO_Zx2: - ii->u.asgn.mov_to_r = false; - info->size += 1+size_loc(&ii->loc2); - break; - default: ASSERT(0, "Unknown operation"); } + // dest->kind != LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_rm_opnd(src), size_platf); + buf = imul(buf, tmp_reg, imm); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_platf); + break; + } + case LO_Shl: + shift_op_rm_imm(dest, src, imm_val); + break; + default: + ASSERT(0, "Unexpected operation"); } - if (ii->u.asgn.mov_to_r) info->size += size_loc(&ii->loc2)*num_regs; - if (ii->mov_to_dst) info->size += size_loc(&ii->loc1)*num_regs; } - void ts(LilVariable* dst) - { - prepass_dst(dst, LT_PInt); - info->size += m2n_ts_to_register_size() + (ii->mov_to_dst ? size_loc(&ii->loc1) : 0); + // binary arithmetic operations without immediates + // (allowed only for integer values!) + void bin_op_rm_rm(LilOperation o, const LilVarLocation * dest, + const LilVarLocation * src1, const LilVarLocation * src2) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_Stk); + assert(src1->kind == LLK_Gr || src1->kind == LLK_Stk); + assert(src2->kind == LLK_Gr || src2->kind == LLK_Stk); + + switch (o) { + case LO_Add: + return alu_op_rm_rm(add_opc, dest, src1, src2); + case LO_Sub: + return alu_op_rm_rm(sub_opc, dest, src1, src2); + case LO_And: + return alu_op_rm_rm(and_opc, dest, src1, src2); + case LO_SgMul: { + if (dest->kind == LLK_Gr) { + if (dest != src1) { + buf = mov(buf, get_r_opnd(dest), get_rm_opnd(src1), size_platf); + } + buf = imul(buf, get_r_opnd(dest), get_rm_opnd(src2)); + return; + } + // dest->kind != LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_rm_opnd(src1), size_platf); + buf = imul(buf, tmp_reg, get_rm_opnd(src2)); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_platf); + break; + } + case LO_Shl: { + move_rm(LilType_Platf, dest, src1); + const Tmp_GR_Opnd tmp_reg(context, ic); + const R_Opnd * src2_reg; + if (src2->kind == LLK_Gr) { + src2_reg = &get_r_opnd(src2); + } else { + src2_reg = &tmp_reg; + buf = mov(buf, tmp_reg, get_m_opnd(src2), size_platf); + } + buf = shift(buf, shl_opc, get_rm_opnd(dest), *src2_reg); + break; + } + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point + } } - void handles(LilOperand* o) - { - operand_to_location(&ii->loc2, &ctxt, o, LT_PInt); - info->size += (ii->loc2.t!=LOLT_Reg ? size_loc(&ii->loc2) : 0); - info->size += m2n_set_local_handles_size(m2n_base(&ctxt)); - } + // unary operation without immediates + void un_op_rm(LilOperation o, const LilVarLocation * dest, const LilVarLocation * src) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_Stk); + assert(src->kind == LLK_Gr || src->kind == LLK_Stk); - void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilLdX) - { - // for the moment, can't load floats - assert(t != LT_F4 && t != LT_F8 && t != LT_Void); - prepass_dst(dst, t); - convert_addr(mem, &ctxt, &ii->u.address, base, scale, index, offset, &ii->temp_register); - info->size += (t==LT_G8 ? 2 : 1) * size_addr(&ii->u.address); - if (t == LT_G1 || t == LT_G2) { - // MOVZX has a 2-byte opcode! - info->size++; - } - if (ii->mov_to_dst) info->size += (t==LT_G8 ? 2 : 1) * size_loc(&ii->loc1); - } + const Tmp_GR_Opnd tmp_reg(context, ic); + //FIXME: getting a warning on inaccessible copy ctor, replace with + // pointers trick. Huh? + //const R_Opnd& dest_reg = dest->kind == LLK_Gr ? get_r_opnd(dest) : tmp_reg); + const R_Opnd& dest_reg = *(dest->kind == LLK_Gr ? &get_r_opnd(dest) : &tmp_reg); - void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilOperand* src) - { - assert(t==LT_PInt || t==LT_G4 || t==LT_Ref || t==LT_G2); - convert_addr(mem, &ctxt, &ii->u.address, base, scale, index, offset, &ii->temp_register); - info->size += size_addr(&ii->u.address); - operand_to_location(&ii->loc3, &ctxt, src, t); - if (ii->loc3.t==LOLT_Immed) info->size += 4; - if (ii->loc3.t==LOLT_Stack) info->size += size_loc(&ii->loc3); - if (t==LT_G2) info->size++; // operand size prefix - } + switch (o) { + case LO_Neg: + buf = mov(buf, dest_reg, get_rm_opnd(src), size_platf); + buf = neg(buf, dest_reg, size_platf); + break; + case LO_Not: + buf = mov(buf, dest_reg, get_rm_opnd(src), size_platf); + buf = _not(buf, dest_reg, size_platf); + break; + case LO_Sx1: + buf = movsx(buf, dest_reg, get_rm_opnd(src), size_8); + break; + case LO_Sx2: + buf = movsx(buf, dest_reg, get_rm_opnd(src), size_16); + break; + case LO_Sx4: + buf = movsx(buf, dest_reg, get_rm_opnd(src), size_32); + break; + case LO_Zx1: + buf = movzx(buf, dest_reg, get_rm_opnd(src), size_8); + break; + case LO_Zx2: + buf = movzx(buf, dest_reg, get_rm_opnd(src), size_16); + break; + case LO_Zx4: + // movzx r64, r/m32 is not available on em64t + // mov r32, r/m32 should zero out upper bytes + buf = mov(buf, dest_reg, get_rm_opnd(src), size_32); + break; + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point + } - void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel) - { - assert(t==LT_PInt || t==LT_G4 || t==LT_Ref); - convert_addr(mem, &ctxt, &ii->u.address, base, scale, index, offset, &ii->temp_register); - info->size += size_addr(&ii->u.address); + if (dest->kind != LLK_Gr) { + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_platf); + } } - void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, - LilOperand* cmp, LilOperand* src, LilLabel) - { - assert(t==LT_PInt || t==LT_G4 || t==LT_Ref || t==LT_G2); - // Make sure that ret is not in eax - assert(type_number_return_registers(lil_ic_get_ret_type(ctxt.ctxt))==0); - // We need eax for compare, so start the address temp registers after that - ii->temp_register = 1; - convert_addr(mem, &ctxt, &ii->u.address, base, scale, index, offset, &ii->temp_register); - info->size += size_addr(&ii->u.address); - operand_to_location(&ii->loc2, &ctxt, src, t); - assert(ii->loc2.t==LOLT_Reg); // Otherwise we have to move it to & from a register - operand_to_location(&ii->loc3, &ctxt, cmp, t); - info->size += size_loc(&ii->loc3); - if (t==LT_G2) info->size++; // operand size prefix - info->size += 8; // lock cmpxhcg has a 2-byte opcode plus a prefix, plus a long condition jump - } + // generate instructions that calculate a LIL address + const M_Opnd & get_effective_addr(LilVariable * base, unsigned scale, + LilVariable * index, int32 offset, + const R_Opnd & tmp_reg) { +#if 0 + void * const M_Index_Opnd_mem = mem.alloc(sizeof(M_Index_Opnd)); + // handle special case + if (base == NULL && (index == NULL || scale == 0)) { + buf = mov(buf, tmp_reg, Imm_Opnd(size_platf, offset)); + return *(new(M_Index_Opnd_mem) M_Index_Opnd(tmp_reg.reg_no(), n_reg, 0, 0)); + } - void j(LilLabel) - { - info->size += 2; - js++; - } + if (index != NULL && scale != 0) { + const LilVarLocation * index_loc = get_var_loc(index, false); + const RM_Opnd& index_rm = get_rm_opnd(index_loc); + buf = mov(buf, tmp_reg, index_rm, size_platf); + assert(scale == 1 || scale == 2 || scale == 4 || scale == 8); + unsigned shift_bits = 0; + if (scale == 2) { shift_bits = 1; } + else if (scale == 4) { shift_bits = 2; } + else if (scale == 8) { shift_bits = 3; } + else { /*shift_bits = 0*/ } + if (shift_bits != 0) { + buf = shift(buf, shl_opc, tmp_reg, Imm_Opnd(shift_bits)); + } + } + else { + buf = alu(buf, xor_opc, tmp_reg, tmp_reg); + } - void jc(enum LilPredicate p, LilOperand* o1, LilOperand* o2, LilLabel) - { - LilType t = lil_ic_get_type(cs, ctxt.ctxt, o1); - assert(t==LT_G4 || t==LT_Ref || t==LT_PInt || t==LT_G1 || t==LT_G2 || (t==LT_G8 && (p==LP_IsZero || p==LP_IsNonzero))); - bool invert = false; - - operand_to_location(&ii->loc1, &ctxt, o1, t); - if (lil_predicate_is_binary(p)) { - operand_to_location(&ii->loc2, &ctxt, o2, t); - ii->u.jc.cg = LCG_Cmp; - ii->u.jc.swap = false; - if (ii->loc1.t==LOLT_Immed) { - assert(ii->loc2.t!=LOLT_Immed); - ii->u.jc.immed = true; - ii->u.jc.stack = (ii->loc2.t==LOLT_Stack); - ii->u.jc.swap = invert = true; - } else if (ii->loc2.t==LOLT_Immed) { - ii->u.jc.immed = true; - ii->u.jc.stack = (ii->loc1.t==LOLT_Stack); - } else if (ii->loc1.t==LOLT_Stack) { - if (ii->loc2.t==LOLT_Stack) { - ASSERT(0, "Unexpected type"); - } else { - ii->u.jc.immed = false; - ii->u.jc.stack = true; - } - } else if (ii->loc2.t==LOLT_Stack) { - ii->u.jc.immed = false; - ii->u.jc.stack = true; - ii->u.jc.swap = invert = true; - } else { - ii->u.jc.immed = false; - ii->u.jc.stack = false; + if (base != NULL) { + const LilVarLocation * base_loc = get_var_loc(base, false); + const RM_Opnd& base_rm = get_rm_opnd(base_loc); + if (base_rm.is_reg()) { + Reg_No reg = ((R_Opnd*)&base_rm)->reg_no(); + assert(!equals(reg, tmp_reg.reg_no())); } - info->size += 2 + (ii->u.jc.stack ? 4 : 0) + (ii->u.jc.immed ? 4 : 0); - } else if (t==LT_G8) { - ii->u.jc.cg = LCG_IsZeroG8; - info->size += 2*size_loc(&ii->loc1); - } else { - ii->u.jc.cg = LCG_Test; - if (ii->loc1.t!=LOLT_Reg) info->size += size_loc(&ii->loc1); - info->size += 2; - }; - switch (p) { - case LP_IsZero: ii->u.jc.cc=Condition_Z; break; - case LP_IsNonzero: ii->u.jc.cc=Condition_NZ; break; - case LP_Eq: ii->u.jc.cc=Condition_Z; break; - case LP_Ne: ii->u.jc.cc=Condition_NZ; break; - case LP_Le: ii->u.jc.cc=Condition_LE; break; - case LP_Lt: ii->u.jc.cc=Condition_L; break; - case LP_Ule: ii->u.jc.cc=Condition_BE; break; - case LP_Ult: ii->u.jc.cc=Condition_B; break; - default: ASSERT(0, "Unknown predicate"); - } - if (invert) { - switch (ii->u.jc.cc) { - case Condition_L: ii->u.jc.cc = Condition_NL; break; - case Condition_LE: ii->u.jc.cc = Condition_NLE; break; - case Condition_B: ii->u.jc.cc = Condition_NB; break; - case Condition_BE: ii->u.jc.cc = Condition_NBE; break; - default:; + buf = alu(buf, add_opc, tmp_reg, base_rm, size_platf); + } + + if (offset != 0) { + buf = alu(buf, add_opc, tmp_reg, Imm_Opnd(offset)); + } + void * const M_Base_Opnd_mem = mem.alloc(sizeof(M_Base_Opnd)); + return *(new(M_Base_Opnd_mem) M_Base_Opnd(tmp_reg.reg_no(), 0)); +#else + if (base == NULL && (index == NULL || scale == 0)) { + // special case - only displacement + void* const ptr = mem.alloc(sizeof(M_Base_Opnd)); + return *(new(ptr) M_Base_Opnd(n_reg, offset)); + } + + if (base != NULL && index != NULL && !is_gr_reg(base) && !is_gr_reg(index)) { + // One more special case: we process a complex address form + // [someVar + someOtherVar*scale+immediate] and both someVar and + // someOtherVar are located on memory. + buf = mov(buf, tmp_reg, get_rm_opnd(index)); + buf = lea(buf, tmp_reg, M_Index_Opnd(n_reg, tmp_reg.reg_no(), 0, scale)); + buf = alu(buf, add_opc, tmp_reg, get_rm_opnd(base)); + void* const ptr = mem.alloc(sizeof(M_Base_Opnd)); + return *(new(ptr) M_Base_Opnd(tmp_reg.reg_no(), 0)); + } + + Reg_No rbase = n_reg; + Reg_No rindex = n_reg; + + if (NULL != base) { + if (is_gr_reg(base)) { + rbase = get_gr_reg(base); + } + else { + const RM_Opnd& base_rm = get_rm_opnd(base); + buf = mov(buf, tmp_reg, base_rm); + rbase = tmp_reg.reg_no(); } } - info->size += 2; - jcs++; - } - void out(LilSig* s) - { - ii->u.out = sig_size_on_stack(s); - if (ii->u.out) - info->size += 2 + (ii->u.out<128 ? 1 : 4); + if (NULL != index) { + if (is_gr_reg(index)) { + rindex = get_gr_reg(index); + } + else { + // double check - both index & base are on memory - must + // not happen as it was processed above + assert(!equals(rbase, tmp_reg.reg_no())); + const RM_Opnd& index_rm = get_rm_opnd(index); + buf = mov(buf, tmp_reg, index_rm); + rindex = tmp_reg.reg_no(); + } + } + void* const ptr = mem.alloc(sizeof(M_Index_Opnd)); + return *(new(ptr) M_Index_Opnd(rbase, rindex, offset, scale)); +#endif } - void in2out(LilSig* s) - { - unsigned num = sig_size_on_stack(s); - unsigned npushs = num/4; - if (2*num+in_base(&ctxt) < 128) - info->size += 4*npushs; - else - info->size += 7*npushs; - } + // sets up stack frame + void prolog() { + // + // Allocate stack frame + unsigned stackFrameSize = context.get_stk_size(); + unsigned allocateStack = stackFrameSize; + if (context.uses_std_places() && !context.declares_std_places()) { + allocateStack -= STD_PLACES_STACK_SIZE; + } + buf = alu(buf, sub_opc, stack_opnd, allocateStack); +#ifdef _DEBUG + // + // Fill out stack frame with a dummy value + // + static const int dummyValue = 0xFEE1DEAD; + // preserve used regs + buf = push(buf, eax_opnd); + buf = push(buf, ecx_opnd); + unsigned stackOffset = STACK_SLOT_SIZE*2; // *2 = eax+ecx + + buf = mov(buf, eax_opnd, Imm_Opnd(dummyValue)); + + const unsigned word_size = 4; // 4 bytes, as per int32 + const unsigned dbg_words = allocateStack/word_size; + buf = mov(buf, ecx_opnd, Imm_Opnd(dbg_words)); + + M_Index_Opnd stack(stack_reg, ecx_reg, stackOffset, 4); + char* storeAddr = buf; + buf = dec(buf, ecx_opnd); + buf = mov(buf, stack, eax_opnd, size_32); + buf = branch8(buf, Condition_NZ, Imm_Opnd(0)); + // patch the back branch + char* branchAddr = buf-1; + int offset = storeAddr - buf; + *branchAddr = (char)offset; + // + buf = pop(buf, ecx_opnd); + buf = pop(buf, eax_opnd); +#endif +#define LIL_SLOW_PROLOG 0 +#if LIL_SLOW_PROLOG + // Preserve callee-save registers + for (unsigned i=0; i regsToSave; + // + // If calling convention presumes parameters on regs, spill these + // regs onto stack and then use from there + for (unsigned i=0; iloc1, &ctxt, o, LT_PInt); - switch (k) { - case LCK_Call:{ - info->size += size_loc(&ii->loc1); - if (!ctxt.out_cc.callee_pop) { - unsigned num = sig_size_on_stack(lil_ic_get_out_sig(ctxt.ctxt)); - if (num) info->size += (num<128 ? 3 : 6); + // + // Preserve callee-save registers used as locals + for (unsigned i=0; isize += size_loc(&ii->loc1); - break; - case LCK_TailCall: - tailcalls++; - info->size += size_loc(&ii->loc1); - break; - default: ASSERT(0, "Unexpected call kind"); } - } - void ret() - { - if (lil_ic_get_out_sig(ctxt.ctxt) || lil_ic_get_amt_alloced(ctxt.ctxt)) - info->size += 6; - rets++; - } + // + // Special case - if the stub uses DRLFast convention, and does + // any calls (that are not aware of XMM callee-saves), then XMM + // registers may not be preserved. Keep them. + for (unsigned i=0; (m_csig.get_cc() == CC_DRLFast) && isize += m2n_push_m2n_size(handles, 4); + bool arbitraryCC = lil_sig_is_arbitrary(m_csig.get_lil_sig()); + + // Preserve callee-save registers + for (unsigned i=0; iu.pop_m2n = 0; - break; - case LT_G8: - ii->u.pop_m2n = 2; - break; + switch (t) { case LT_G1: + return size_8; case LT_G2: + return size_16; case LT_G4: - case LT_Ref: + return size_32; + case LT_G8: + return size_64; case LT_PInt: - ii->u.pop_m2n = 1; - break; - default: ASSERT(0, "Unknown LIL type"); + case LT_Ref: + return size_platf; + default: + return n_size; } - info->size += m2n_pop_m2n_size(lil_ic_get_m2n_state(ctxt.ctxt)==LMS_Handles, 4, num, ii->u.pop_m2n); } - void print(char *, LilOperand *) { - // not implemented on ia32 - } - - void after_inst() - { - ii->size_after = info->size; - } +public: - void finalise_size() - { - LcgIa32CcInfo entry_cc_info; - cc_to_cc_info(&entry_cc_info, lil_sig_get_cc(lil_cs_get_sig(cs))); - unsigned num = sig_size_on_stack(lil_cs_get_sig(cs)); - unsigned ret_size = (entry_cc_info.callee_pop && num ? 3 : 1); - ret_size += info->num_callee_saves; // need this many pop insts at every ret - info->size += ret_size*rets; - info->size += info->num_callee_saves*tailcalls; // tailcalls need the same as rets - info->size += (4-info->num_callee_saves)*m2n_ops; // this many push/pops at push_m2n and pop_m2n - info->size += info->num_callee_saves; // this many pushes at the start - info->short_jumps = (info->size < 128); - if (!info->short_jumps) info->size += 3*js+4*jcs; - } + /** + * constructor + */ + LilCodeGen(LilCodeStub * cs, LilCodeGenContext & c, tl::MemoryPool & m): + buf_beg(NULL), buf(NULL), + labels(&m, NULL), cs(cs), context(c), mem(m), iter(cs, true), ic(NULL), inst(NULL), + stack_loc(NULL), + current_alloc(0) { + // + // + m_max_buffer_size = estimate_code_size(); + buf = buf_beg = (char *)mem.alloc(m_max_buffer_size); + labels.change_base(buf); + + LilSig* lilSig = lil_cs_get_sig(cs); + m_csig.init(lilSig); + m_save_all_regs = lil_sig_is_arbitrary(lilSig); + // When DRLFast convention is active - need to preserve the argument + // registers as well. + if (m_csig.get_cc() == CC_DRLFast) { + m_save_all_regs = true; + } -private: - tl::MemoryPool* mem; - LilCodeStub* cs; - LcgIa32PrePassInfo* info; - LcgIa32Context ctxt; - LcgIa32InstInfo* ii; - unsigned rets, m2n_ops, js, jcs, tailcalls; -}; + context.init(); -static size_t pre_pass(LilCodeStub* cs, tl::MemoryPool* mem, LcgIa32PrePassInfo** data) -{ - LcgIa32PrePassInfo* info = (LcgIa32PrePassInfo*)mem->alloc(sizeof(LcgIa32PrePassInfo)); - info->num_is = lil_cs_get_num_instructions(cs); - info->size = 0; - info->num_callee_saves = get_num_regs_for_locals(cs); - info->is = (LcgIa32InstInfo*)mem->alloc(info->num_is*sizeof(LcgIa32InstInfo)); - - LilInstructionIterator iter(cs, true); - LcgIa32IntrPrePass ppv(mem, cs, info); - while(!iter.at_end()) { - LilInstruction* i = iter.get_current(); - ppv.update_context(iter.get_context()); - lil_visit_instruction(i, &ppv); - ppv.after_inst(); - iter.goto_next(); - } + stack_loc = new(mem) LilVarLocation(LLK_Gr, stack_reg); - ppv.finalise_size(); + // + // Prolog code comes first + // + FILE* dbgOutFile = dump_stubs ? lil_dbg_get_dump_file() : NULL; + char* saveBuf; - *data = info; - return info->size; -} + saveBuf = buf; + // Special processing of a very first trap instruciton - we need + // it before the prolog code. + LilInstruction* possibleTrapInst = iter.get_current(); + if (lil_instruction_is_trap(possibleTrapInst)) { + lil_visit_instruction(possibleTrapInst, this); + iter.goto_next(); + } -////////////////////////////////////////////////////////////////////////// -// Movement + prolog(); + unsigned codeLength = buf-saveBuf; + assert(codeLength <= MAX_INST_SIZE); + // + if (dump_stubs) { + lil_print_sig(dbgOutFile, lil_cs_get_sig(cs)); + const char* reuseStd = + context.uses_std_places() && !context.declares_std_places() ? "(re-uses STD)" : ""; + const char* leaveStd = context.declares_std_places() ? "leaves STD on stack" : ""; + fprintf(dbgOutFile, " - prolog %s %s\n", reuseStd, leaveStd); + lil_dbg_dump(saveBuf, codeLength); + } -static R_Opnd* move_location_to_a_register(char** buf, LcgIa32Context* c, LcgIa32OpLoc* loc, unsigned* temp_reg) -{ - switch (loc->t) { - case LOLT_Reg: - return loc->u.r.r1; - // break; // remark #111: statement is unreachable - case LOLT_Stack:{ - R_Opnd* tmp = get_temp_register(c, *temp_reg); - ++*temp_reg; - *buf = mov(*buf, *tmp, M_Base_Opnd(esp_reg, loc->u.v)); - return tmp;} - case LOLT_Immed: { - R_Opnd* tmp = get_temp_register(c, *temp_reg); - ++*temp_reg; - *buf = mov(*buf, *tmp, Imm_Opnd(loc->u.v)); - return tmp;} - default: ASSERT(0, "Unknown type"); for(;;); - } -} + saveBuf = buf; -static void move_location_to_register(char** buf, R_Opnd* reg1, R_Opnd* reg2, LcgIa32OpLoc* loc, LilType t) -{ - bool two = type_in_two_regs(t); - switch (loc->t) { - case LOLT_Reg: - if (loc->u.r.r1==reg1) return; - *buf = mov(*buf, *reg1, *(loc->u.r.r1)); - if (two) *buf = mov(*buf, *reg2, *(loc->u.r.r2)); - break; - case LOLT_Stack:{ - *buf = mov(*buf, *reg1, M_Base_Opnd(esp_reg, loc->u.v)); - if (two) { - *buf = mov(*buf, *reg2, M_Base_Opnd(esp_reg, loc->u.v+4)); - } - break;} - case LOLT_Immed: { - *buf = mov(*buf, *reg1, Imm_Opnd(loc->u.v)); - if (two) { - *buf = mov(*buf, *reg2, Imm_Opnd(0)); - } - break;} - case LOLT_Tofs:{ - *buf = alu(*buf, sub_opc, esp_opnd, Imm_Opnd(t==LT_F4 ? 4 : 8)); - *buf = fst(*buf, M_Base_Opnd(esp_reg, 0), (t==LT_F8), 1); - *buf = pop(*buf, *reg1); - if (t==LT_F8) *buf = pop(*buf, *reg2); - break;} - default: ASSERT(0, "Unknown type"); + // + // Generate all instructions - in linear order. + // + while (!iter.at_end()) { + ic = iter.get_context(); + inst = iter.get_current(); + if (dump_stubs) { + lil_print_instruction(dbgOutFile, inst); + } + char* saveBuf = buf; + lil_visit_instruction(inst, this); + unsigned codeLength = buf-saveBuf; + assert(codeLength <= MAX_INST_SIZE); + // + // + if (dump_stubs) { + lil_dbg_dump(saveBuf, codeLength); + } + // + iter.goto_next(); + } + if (dump_stubs) { + fflush(dbgOutFile); + } } -} -static void move_register_to_location(char** buf, LcgIa32OpLoc* loc, R_Opnd* reg1, R_Opnd* reg2, LilType t) -{ - bool two = type_in_two_regs(t); - switch (loc->t) { - case LOLT_Reg: - *buf = mov(*buf, *(loc->u.r.r1), *reg1); - if (two) *buf = mov(*buf, *(loc->u.r.r2), *reg2); - break; - case LOLT_Stack:{ - *buf = mov(*buf, M_Base_Opnd(esp_reg, loc->u.v), *reg1); - if (two) { - *buf = mov(*buf, M_Base_Opnd(esp_reg, loc->u.v+4), *reg2); - } - break;} - case LOLT_Tofs:{ - if (t==LT_F8) *buf = push(*buf, *reg2); - *buf = push(*buf, *reg1); - *buf = fld(*buf, M_Base_Opnd(esp_reg, 0), (t==LT_F8)); - *buf = alu(*buf, add_opc, esp_opnd, Imm_Opnd(t==LT_F4 ? 4 : 8)); - break;} - default: ASSERT(0, "Unknown type"); + /** + * returns actual size of the generated code + */ + size_t get_size() const { + assert((unsigned)(buf - buf_beg) < m_max_buffer_size); + return buf - buf_beg; } -} - -static char* addr_emit_moves(char* buf, LcgIa32Addr* addr) -{ - if (addr->has_base && addr->base_loc.t!=LOLT_Reg) - move_location_to_register(&buf, addr->base_reg, NULL, &addr->base_loc, LT_PInt); - if (addr->has_index && addr->index_loc.t!=LOLT_Reg) - move_location_to_register(&buf, addr->index_reg, NULL, &addr->index_loc, LT_PInt); - return buf; -} -static Opnd_Size type_to_opnd_size(LilType t) -{ - switch (t) { - case LT_G2: - return size_16; - //break;// remark #111: statement is unreachable - case LT_G4: - case LT_PInt: - case LT_Ref: - return size_32; - //break;// remark #111: statement is unreachable - default: - ASSERT(0, "Unknown LIL type"); for(;;); + /** + * returns the beginning of the code + */ + NativeCodePtr copy_stub(NativeCodePtr base) const { + memcpy((void *)base, buf_beg, get_size()); + //labels.change_base((char *)base); + return base; } -} -////////////////////////////////////////////////////////////////////////// -// Main Pass - -class LcgIa32IntrCodeGen : public LilInstructionVisitor { -public: - LcgIa32IntrCodeGen(tl::MemoryPool* _mem, LilCodeStub* cs, char** _buf, LcgIa32PrePassInfo* info) - : mem(_mem), buf(_buf), la_tab(_mem, 0), ii(NULL) + /** + * Estimates a maximum possible size of generated code for current stub. + */ + unsigned estimate_code_size(void) const { - ctxt.info = info; - ctxt.ctxt = NULL; - ctxt.entry_sig = lil_cs_get_sig(cs); - cc_to_cc_info(&ctxt.entry_cc, lil_sig_get_cc(ctxt.entry_sig)); + unsigned num_insts = lil_cs_get_num_instructions(cs); + return num_insts*MAX_INST_SIZE; } - void update_context(LilInstructionContext* c) - { - if (c && lil_ic_get_out_sig(c)) - cc_to_cc_info(&ctxt.out_cc, lil_sig_get_cc(lil_ic_get_out_sig(c))); - ctxt.ctxt = c; - if (ii) - ii++; - else - ii = ctxt.info->is; - } - void label(LilLabel l) - { - la_tab.define_label(l, *buf, false); + /* Visitor Functions */ + + void label(LilLabel lab) { + labels.define_label(lab, buf, true); } - void locals(unsigned UNREF num) - { - // nothing to do; everything is taken care of by get_num_regs_for_locals + void locals(unsigned num) { + // nothing to be done here; } - void std_places(unsigned UNREF num) - { - // nothing to do; everything is taken care of by get_num_regs_for_locals + void std_places(unsigned num) { + // nothing to be done here; } - void alloc(LilVariable* UNREF dst, unsigned amt) - { - // Keep the stack 4-byte aligned - Imm_Opnd imm((amt+3)&~3); - *buf = alu(*buf, sub_opc, esp_opnd, imm); - move_register_to_location(buf, &ii->loc1, &esp_opnd, NULL, LT_PInt); + void alloc(LilVariable * var, unsigned sz) { + int32 alloc_offset = context.get_alloc_start_offset() + current_alloc; + // the actual size allocated will always be a multiple of 8 + current_alloc += align_8(sz); + // var = sp + alloc_offset + bin_op_rm_imm(LO_Add, get_var_loc(var, true), stack_loc, alloc_offset); } - void asgn(LilVariable* UNREF dst, LilOperation op, LilOperand* UNREF o1, LilOperand* UNREF o2) - { - // Move o1 to register r - if (ii->u.asgn.mov_to_r) - move_location_to_register(buf, ii->r1, ii->r2, &ii->loc2, ii->t); - - // Do operation - switch (ii->u.asgn.special) { - case LSO_None: - ALU_Opcode opc; - bool sign; - Opnd_Size sz; - switch (op) { - case LO_Sx4: - case LO_Zx4: - // Treat sx4 & zx4 as a move - case LO_Mov: - // Treat immed->stack specially, the others are done by the movs o1->r->dst - if (ii->loc1.t==LOLT_Stack && ii->loc2.t==LOLT_Immed) { - *buf = mov(*buf, M_Base_Opnd(esp_reg, ii->loc1.u.v), Imm_Opnd(ii->loc2.u.v)); - if (type_in_two_regs(ii->t)) { - *buf = mov(*buf, M_Base_Opnd(esp_reg, ii->loc1.u.v+4), Imm_Opnd(0)); - } + void asgn(LilVariable * dest, enum LilOperation o, LilOperand * op1, LilOperand * op2) { + if (dest->tag == LVK_Out) { + // since inputs and outputs occupies same registers we need to take inputs from the stack + //take_inputs_from_stack = true; + } + const LilVarLocation * dest_loc = get_var_loc(dest, true); + if (o == LO_Mov) { + if (lil_operand_is_immed(op1)) { + move_imm(dest_loc, get_imm_value(op1)); + } else { + const LilVarLocation * src_loc = get_op_loc(op1, false); + LilType type = lil_ic_get_type(cs, ic, op1); + move_rm(type, dest_loc, src_loc); + } + return; + } + // check if this is binary operation + if (lil_operation_is_binary(o)) { + if (lil_operand_is_immed(op1) && lil_operand_is_immed(op2)) { + // type-convert to get signed types of same length + int32 op1_imm = get_imm_value(op1); + int32 op2_imm = get_imm_value(op2); + int32 result = 0; + switch (o) { + case LO_Add: + result = op1_imm + op2_imm; + break; + case LO_Sub: + result = op1_imm - op2_imm; + break; + case LO_SgMul: + result = op1_imm * op2_imm; + break; + case LO_Shl: + result = op1_imm << op2_imm; + break; + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point } - break; - case LO_Add: - opc = add_opc; goto alu; - case LO_Sub: - opc = sub_opc; goto alu; - case LO_SgMul: - switch (ii->loc3.t) { - case LOLT_Reg: - *buf = imul(*buf, *(ii->r1), *(ii->loc3.u.r.r1)); + move_imm(dest_loc, result); + } else if (lil_operand_is_immed(op1)) { + const int32 op1_imm = get_imm_value(op1); + const LilVarLocation * op2_loc = get_op_loc(op2, false); + switch (o) { + case LO_Add: + bin_op_rm_imm(LO_Add, dest_loc, op2_loc, op1_imm); break; - case LOLT_Stack:{ - *buf = imul(*buf, *(ii->r1), M_Base_Opnd(esp_reg, ii->loc3.u.v)); - break;} - case LOLT_Immed:{ - *buf = imul(*buf, *(ii->r1), Imm_Opnd(ii->loc3.u.v)); - break;} - default: ASSERT(0, "Unexpected type"); - } - break; - case LO_Neg: - *buf = neg(*buf, *(ii->r1)); - break; - case LO_Shl: - switch (ii->loc3.t) { - case LOLT_Reg: - case LOLT_Stack: - ASSERT(0, "Unexpected type"); - case LOLT_Immed:{ - *buf = shift(*buf, shl_opc, *(ii->r1), Imm_Opnd(ii->loc3.u.v)); - break;} - default: ASSERT(0, "Unexpected type"); + case LO_Sub: + sub_op_imm_rm(dest_loc, op1_imm, op2_loc); + break; + case LO_SgMul: + bin_op_rm_imm(LO_SgMul, dest_loc, op2_loc, op1_imm); + case LO_Shl: + shift_op_imm_rm(dest_loc, op1_imm, op2_loc); + break; + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point } - break; - case LO_And: - opc = and_opc; goto alu; - case LO_Not: - ASSERT(0, "Unexpected operation"); - case LO_Sx1: sign=true; sz = size_8; goto widden; - case LO_Sx2: sign=true; sz = size_16; goto widden; - case LO_Zx1: sign=false; sz = size_8; goto widden; - case LO_Zx2: sign=false; sz = size_16; goto widden; - alu: - switch (ii->loc3.t) { - case LOLT_Reg: - *buf = alu(*buf, opc, *(ii->r1), *(ii->loc3.u.r.r1)); + } else if (lil_operand_is_immed(op2)) { + const LilVarLocation* op1_loc = get_op_loc(op1, false); + const int32 op2_imm = get_imm_value(op2); + bin_op_rm_imm(o, dest_loc, op1_loc, op2_imm); + } else { // both operands non-immediate + const LilVarLocation * src1_loc = get_op_loc(op1, false); + const LilVarLocation * src2_loc = get_op_loc(op2, false); + bin_op_rm_rm(o, dest_loc, src1_loc, src2_loc); + } + } else { // unary operation + if (lil_operand_is_immed(op1)) { + int32 imm = get_imm_value(op1); + int32 result = 0; + switch (o) { + case LO_Neg: + result = -imm; break; - case LOLT_Stack:{ - *buf = alu(*buf, opc, *(ii->r1), M_Base_Opnd(esp_reg, ii->loc3.u.v)); - break;} - case LOLT_Immed:{ - *buf = alu(*buf, opc, *(ii->r1), Imm_Opnd(ii->loc3.u.v)); - break;} - default: ASSERT(0, "Unexpected type"); - } - break; - widden: - switch (ii->loc2.t) { - case LOLT_Reg: - // You cannot widen ebp, esp, esi, or edi. - assert(ii->loc2.u.r.r1==&eax_opnd || ii->loc2.u.r.r1==&ebx_opnd); - if (sign) { - *buf = movsx(*buf, *(ii->r1), *(ii->loc2.u.r.r1), sz); - } else { - *buf = movzx(*buf, *(ii->r1), *(ii->loc2.u.r.r1), sz); - } + case LO_Not: + result = ~imm; + break; + case LO_Sx1: + result = (int32) (int8) imm; + break; + case LO_Sx2: + result = (int32) (int16) imm; + break; + case LO_Sx4: + result = (int32) (int32) imm; + break; + case LO_Zx1: + result = (int32) (uint64) (uint8) imm; + break; + case LO_Zx2: + result = (int32) (uint64) (uint16) imm; break; - case LOLT_Stack:{ - if (sign) { - *buf = movsx(*buf, *(ii->r1), M_Base_Opnd(esp_reg, ii->loc2.u.v), sz); - } else { - *buf = movzx(*buf, *(ii->r1), M_Base_Opnd(esp_reg, ii->loc2.u.v), sz); - } - break;} - default: ASSERT(0, "Unexpected type"); + case LO_Zx4: + result = (int32) (uint64) (uint32) imm; + break; + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point } - break; - default: ASSERT(0, "Unexpected operartion"); + move_imm(dest_loc, result); + } else { // non-immediate operand + const LilVarLocation * op1_loc = get_op_loc(op1, false); + un_op_rm(o, dest_loc, op1_loc); } - break; - case LSO_Lea:{ - M_Base_Opnd m(ii->loc2.u.r.r1->reg_no(), (op==LO_Add ? ii->loc3.u.v : -(int)ii->loc3.u.v)); - *buf = lea(*buf, *(ii->r1), m); - break;} } - - // Move register r to dst - if (ii->mov_to_dst) - move_register_to_location(buf, &ii->loc1, ii->r1, ii->r2, ii->t); } - void ts(LilVariable* UNREF v) + void ts(LilVariable * var) { - *buf = m2n_gen_ts_to_register(*buf, ii->r1); - if (ii->mov_to_dst) - move_register_to_location(buf, &ii->loc1, ii->r1, ii->r2, LT_PInt); + Tmp_GR_Opnd tmp(context, ic); + buf = m2n_gen_ts_to_register(buf, &tmp); + const LilVarLocation * var_loc = get_var_loc(var, true); + if (var_loc->kind == LLK_Gr) { + buf = mov(buf, get_r_opnd(var_loc), tmp, size_platf); + } + else { + buf = mov(buf, get_m_opnd(var_loc), tmp, size_platf); + } + //take_inputs_from_stack = true; } - void handles(LilOperand*) + void handles(LilOperand * op) { - R_Opnd* r = move_location_to_a_register(buf, &ctxt, &ii->loc2, &ii->temp_register); - *buf = m2n_gen_set_local_handles(*buf, m2n_base(&ctxt), r); + if (lil_operand_is_immed(op)) { + buf = m2n_gen_set_local_handles_imm(buf, context.get_m2n_offset(), &get_imm_opnd(op)); + } + else { + const LilVarLocation * loc = get_op_loc(op, false); + if (loc->kind == LLK_Gr) { + buf = m2n_gen_set_local_handles_r(buf, + context.get_m2n_offset(), &get_r_opnd(loc)); + } + else { + const Tmp_GR_Opnd tmp(context, ic); + buf = mov(buf, tmp, get_m_opnd(loc), size_platf); + buf = m2n_gen_set_local_handles_r(buf, context.get_m2n_offset(), &tmp); + } + } } - void ld(LilType t, LilVariable*, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel, LilLdX ext) - { - *buf = addr_emit_moves(*buf, &ii->u.address); + void ld(LilType t, LilVariable * dstVar, LilVariable * base, unsigned scale, + LilVariable * index, POINTER_SIZE_SINT offset, LilAcqRel UNREF /*XXX*/ acqrel, LilLdX ext) { + + assert(t != LT_F4 && t != LT_F8 && t != LT_Void); + assert(acqrel == LAR_Acquire || acqrel == LAR_None); + + const Tmp_GR_Opnd tmp_reg(context, ic); + // calculate the address + const M_Opnd& srcAddr = get_effective_addr(base, scale, index, offset, tmp_reg); + + Opnd_Size size = n_size; switch(t) { - case LT_G1: - case LT_G2: - if (ext == LLX_Sign) { - *buf = movsx(*buf, *(ii->r1), *(ii->u.address.addr), t == LT_G1 ? size_8 : size_16); - } else { - *buf = movzx(*buf, *(ii->r1), *(ii->u.address.addr), t == LT_G1 ? size_8 : size_16); + case LT_G1: + size = size_8; + break; + case LT_G2: + size = size_16; + break; + case LT_G4: + size = size_32; + break; + case LT_G8: + case LT_Ref: + case LT_PInt: + size = size_platf; + break; + default: + ASSERT(0, "Unexpected LIL type"); // invalid value in type + } + + if (size == size_platf) { + buf = mov(buf, tmp_reg, srcAddr, size); + } + else if(ext == LLX_Zero) { + // movzx r64, r/m32 is not available on em64t + // mov r64, r/m32 should zero out upper bytes + if (size_platf == size_64 && size==size_32) { + buf = mov(buf, tmp_reg, srcAddr, size); } - if (ext!=LLX_None) t = LT_PInt; - break; - case LT_G4: - case LT_PInt: - case LT_Ref: - *buf = mov(*buf, *(ii->r1), *(ii->u.address.addr)); - break; - case LT_G8: - ASSERT(0, "Unexpected type"); - //ASSERT(0 == 1, "Need proper implementation"); - //*buf = mov(*buf, *(ii->r1), *(ii->u.address.addr)); - // Bit of a hack to change this value, but it works - //ii->u.address.addr->disp.value += 4; - //*buf = mov(*buf, *(ii->r2), *(ii->u.address.addr)); - break; - default: - ASSERT(0, "Unexpected LIL type"); // other types not allowed + else { + buf = movzx(buf, tmp_reg, srcAddr, size); + } + } + else { + buf = movsx(buf, tmp_reg, srcAddr, size); + } + const LilVarLocation * dst_loc = get_var_loc(dstVar, true); + assert(dst_loc->kind == LLK_Gr || dst_loc->kind == LLK_Stk); + + if (dst_loc->kind == LLK_Gr) { + buf = mov(buf, get_r_opnd(dst_loc), tmp_reg, size); + } + else { + buf = mov(buf, get_m_opnd(dst_loc), tmp_reg, size); } - if (ii->mov_to_dst) move_register_to_location(buf, &ii->loc1, ii->r1, ii->r2, t); } - void st(LilType t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel, LilOperand*) - { - *buf = addr_emit_moves(*buf, &ii->u.address); - if (ii->loc3.t==LOLT_Immed) { - Imm_Opnd imm(ii->loc3.u.v); - *buf = mov(*buf, *(ii->u.address.addr), imm, type_to_opnd_size(t)); + void st(LilType t, LilVariable * base, unsigned scale, LilVariable * index, + POINTER_SIZE_SINT offset, LilAcqRel acqrel, LilOperand * src) { + assert(t != LT_F4 && t != LT_F8 && t != LT_Void); + assert(acqrel == LAR_Release || acqrel == LAR_None); + + const Tmp_GR_Opnd tmp_reg1(context, ic); + // calculate the address + const M_Opnd & addr = get_effective_addr(base, scale, index, offset, tmp_reg1); + + if (lil_operand_is_immed(src)) { + buf = mov(buf, addr, get_imm_opnd(src), type_to_opnd_size(t)); } else { - R_Opnd* r = move_location_to_a_register(buf, &ctxt, &ii->loc3, &ii->temp_register); - *buf = mov(*buf, *(ii->u.address.addr), *r, type_to_opnd_size(t)); + const LilVarLocation * src_loc = get_op_loc(src, false); + if (src_loc->kind == LLK_Gr) { + buf = mov(buf, addr, get_r_opnd(src_loc), type_to_opnd_size(t)); + } else { + const Tmp_GR_Opnd tmp_reg2(context, ic); + buf = mov(buf, tmp_reg2, get_m_opnd(src_loc), type_to_opnd_size(t)); + buf = mov(buf, addr, tmp_reg2, type_to_opnd_size(t)); + } } + } // st + + void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, + POINTER_SIZE_SINT offset, LilAcqRel acqrel) { + assert(acqrel == LAR_None); + + Tmp_GR_Opnd tmp_reg(context, ic); + // calculate the address + const M_Opnd & addr = get_effective_addr(base, scale, index, offset, tmp_reg); + buf = ::inc(buf, addr, type_to_opnd_size(t)); + } - void inc(LilType UNREF t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel) - { - *buf = addr_emit_moves(*buf, &ii->u.address); - *buf = ::inc(*buf, *(ii->u.address.addr)); + void cas(LilType t, LilVariable * base, unsigned scale, + LilVariable * index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, + LilOperand * cmp, LilOperand * src, LilLabel label) { + assert(false && + "LIL atomics are not used anymore, dropped on Intel64/IA-32." + " Rewrite your LIL stub."); } - void cas(LilType t, LilVariable* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel, - LilOperand* UNREF cmp, LilOperand* UNREF src, LilLabel l) + void trap(void) { - *buf = addr_emit_moves(*buf, &ii->u.address); - R_Opnd* r = move_location_to_a_register(buf, &ctxt, &ii->loc2, &ii->temp_register); - move_location_to_register(buf, &eax_opnd, NULL, &ii->loc3, t); - *buf = prefix(*buf, lock_prefix); - *buf = cmpxchg(*buf, *(ii->u.address.addr), *r, type_to_opnd_size(t)); - *buf = branch32(*buf, Condition_NZ, Imm_Opnd(-4)); - la_tab.add_patch_to_label(l, *buf-4, LPT_Rel32); + buf = ::int3(buf); } - void j(LilLabel l) - { - if (ctxt.info->short_jumps) { - Imm_Opnd imm(size_8, -1); - *buf = jump8(*buf, imm); - la_tab.add_patch_to_label(l, *buf-1, LPT_Rel8); - } else { - Imm_Opnd imm(size_32, -4); - *buf = jump32(*buf, imm); - la_tab.add_patch_to_label(l, *buf-4, LPT_Rel32); - } + void j(LilLabel lab) { + buf = jump32(buf, get_imm_opnd((int64)0, size_32)); + labels.add_patch_to_label(lab, buf - 4, LPT_Rel32); } - void jc(LilPredicate, LilOperand*, LilOperand*, LilLabel l) - { - if (ii->u.jc.cg==LCG_Cmp) { - if (ii->u.jc.immed) { - Imm_Opnd imm(ii->u.jc.swap ? ii->loc1.u.v : ii->loc2.u.v); - if (ii->u.jc.stack) { - M_Base_Opnd m(esp_reg, (ii->u.jc.swap ? ii->loc2.u.v : ii->loc1.u.v)); - *buf = alu(*buf, cmp_opc, m, imm); - } else { - *buf = alu(*buf, cmp_opc, *(ii->u.jc.swap ? ii->loc2.u.r.r1 : ii->loc1.u.r.r1), imm); - } - } else { - if (ii->u.jc.stack) { - M_Base_Opnd m(esp_reg, (ii->u.jc.swap ? ii->loc2.u.v : ii->loc1.u.v)); - *buf = alu(*buf, cmp_opc, m, *(ii->u.jc.swap ? ii->loc1.u.r.r1 : ii->loc2.u.r.r1)); - } else { - *buf = alu(*buf, cmp_opc, *(ii->loc1.u.r.r1), *(ii->loc2.u.r.r1)); - } + void jc(LilPredicate p, LilOperand * op1, LilOperand * op2, LilLabel label) { + // compute the condition + ConditionCode cc = Condition_Count; + + switch (p) { + case LP_Eq: + case LP_IsZero: + cc = Condition_E; + break; + case LP_Ne: + case LP_IsNonzero: + cc = Condition_NE; + break; + case LP_Le: + cc = Condition_LE; + break; + case LP_Ule: + cc = Condition_BE; + break; + case LP_Lt: + cc = Condition_L; + break; + case LP_Ult: + cc = Condition_B; + break; + default: + ASSERT(0, "Unknown predicate"); + } + + LilOperand zeroLilOpnd = {0}; + if (!lil_predicate_is_binary(p)) { + op2 = &zeroLilOpnd; + op2->is_immed = true; + op2->val.imm = 0; + } + + Tmp_GR_Opnd tmpReg(context, ic); + if (lil_operand_is_immed(op1)) { + buf = mov(buf, tmpReg, get_imm_opnd(op1), size_platf); + } + else { + const RM_Opnd& left = get_rm_opnd(get_op_loc(op1, false)); + buf = mov(buf, tmpReg, left, size_platf); + } + + if (lil_operand_is_immed(op2)) { + int64 imm = lil_operand_get_immed(op2); + if (fit32(imm)) { + buf = alu(buf, cmp_opc, tmpReg, get_imm_opnd(imm, size_32), size_platf); } - } else if (ii->u.jc.cg==LCG_Test) { - R_Opnd* reg = move_location_to_a_register(buf, &ctxt, &ii->loc1, &ii->temp_register); - *buf = test(*buf, *reg, *reg); - } else if (ii->u.jc.cg==LCG_IsZeroG8) { - R_Opnd* reg = get_temp_register(&ctxt, ii->temp_register++); - move_location_to_register(buf, reg, NULL, &ii->loc1, LT_PInt); - switch (ii->loc1.t) { - case LOLT_Reg: - *buf = alu(*buf, or_opc, *reg, *(ii->loc1.u.r.r2)); - break; - case LOLT_Stack:{ - *buf = alu(*buf, or_opc, *reg, M_Base_Opnd(esp_reg, ii->loc1.u.v+4)); - break;} - default: - ASSERT(0, "Unexpected type"); + else { + // use temporary register + Tmp_GR_Opnd rightTmp(context, ic); + buf = mov(buf, rightTmp, get_imm_opnd(imm, size_platf), size_platf); + buf = alu(buf, cmp_opc, tmpReg, rightTmp); } } - if (ctxt.info->short_jumps) { - *buf = branch8(*buf, ii->u.jc.cc, Imm_Opnd(size_8, -1)); - la_tab.add_patch_to_label(l, *buf-1, LPT_Rel8); - } else { - *buf = branch32(*buf, ii->u.jc.cc, Imm_Opnd(size_32, -4)); - la_tab.add_patch_to_label(l, *buf-4, LPT_Rel32); - } - } - - void out(LilSig*) - { - if (ii->u.out) { - *buf = alu(*buf, sub_opc, esp_opnd, Imm_Opnd(ii->u.out)); + else { + const LilVarLocation * src2_loc = get_op_loc(op2, false); + buf = alu(buf, cmp_opc, tmpReg, get_rm_opnd(src2_loc)); } + + // the actual branch + buf = branch32(buf, cc, get_imm_opnd((int64)0, size_32)); + labels.add_patch_to_label(label, buf - 4, LPT_Rel32); } - void in2out(LilSig* s) - { - LcgIa32CcInfo out_cc; - cc_to_cc_info(&out_cc, lil_sig_get_cc(s)); - bool l2r = out_cc.arg_order==LAO_L2r; - unsigned nargs = lil_sig_get_num_args(s); - unsigned extra = 0, base = in_base(&ctxt); - unsigned i = (l2r ? 0 : nargs); - while ((l2r && i0)) { - if (!l2r) i--; - unsigned arg_base = extra+base+offset_in_sig(&ctxt.entry_cc, ctxt.entry_sig, i); - switch (type_size_on_stack(lil_sig_get_arg_type(s, i))) { - case 4:{ - *buf = push(*buf, M_Base_Opnd(esp_reg, arg_base)); - extra += 4; - break;} - case 8:{ - M_Base_Opnd m1(esp_reg, arg_base+4); // +4 because of the first push - M_Base_Opnd m2(esp_reg, arg_base+4); - *buf = push(*buf, m2); - *buf = push(*buf, m1); - extra += 8; - break;} - default: ASSERT(0, "Unexpected type size"); - } - if (l2r) i++; + void out(LilSig * sig) { + // + // Prepare stack frame for parameters, if any. + // + LilCallSig lilSig(sig); + unsigned stackSize = lilSig.get_stack_size(); + if (stackSize != 0) { + buf = alu(buf, sub_opc, stack_opnd, Imm_Opnd(size_32, stackSize)); + context.stack_depth_add(stackSize); } } - void do_call(LcgIa32OpLoc* l) - { - switch (l->t) { - case LOLT_Reg: - *buf = ::call(*buf, *(l->u.r.r1)); - break; - case LOLT_Stack:{ - *buf = ::call(*buf, M_Base_Opnd(esp_reg, l->u.v)); - break;} - case LOLT_Immed: - *buf = ::call(*buf, (char*)l->u.v); - break; - default: ASSERT(0, "Unexpected type"); + /** + * implements the copying of incoming to outgoing args + */ + void in2out(LilSig * sig) { + assert(!lil_sig_is_arbitrary(lil_cs_get_sig(cs))); + + out(sig); + + for (unsigned i = 0; i < m_csig.get_num_args(); i++) { + const LilVarLocation * in_loc = get_input(i); + const LilVarLocation * out_loc = get_output(i, sig); + LilType outType = lil_sig_get_arg_type(sig, i); + move_rm(outType, out_loc, in_loc); } } - void call(LilOperand* UNREF o, LilCallKind k) + void call(LilOperand * targetOpnd, LilCallKind kind) { - switch (k) { - case LCK_Call: - // Do call - do_call(&ii->loc1); - // Argument pop if necessary - if (!ctxt.out_cc.callee_pop) { - unsigned num_bytes = sig_size_on_stack(lil_ic_get_out_sig(ctxt.ctxt)); - if (num_bytes) { - *buf = alu(*buf, add_opc, esp_opnd, Imm_Opnd(num_bytes)); - } + if (kind == LCK_TailCall) { + // unwind current stack frame + + // A small trick - as we want to keep all the registers + // untouched, we push target address on the stack, and then + // 'RET' to it. Not a best practice, but let avoid register + // usage and allows to free the stack frame. + + // 1. Push where to go on the top of the stack + // 2. Adjust stack depth + // 3. go! + + // jump (instead of call) + if (lil_operand_is_immed(targetOpnd)) { + const Imm_Opnd address(lil_operand_get_immed(targetOpnd)); + //TODO: fix for Intel64, if any + buf = push(buf, address); } - break; - case LCK_CallNoRet: - do_call(&ii->loc1); - break; - case LCK_TailCall: - adjust_stack_for_return(); - switch (ii->loc1.t) { - case LOLT_Reg: - *buf = jump(*buf, *(ii->loc1.u.r.r1)); - break; - case LOLT_Stack:{ - *buf = jump(*buf, M_Base_Opnd(esp_reg, ii->loc1.u.v)); - break;} - case LOLT_Immed: - *buf = jump(*buf, (char*)ii->loc1.u.v); - break; - default: ASSERT(0, "Unexpected type"); + else { + const LilVarLocation * loc = get_op_loc(targetOpnd, false); + const RM_Opnd& rm = get_rm_opnd(loc); + buf = push(buf, rm); } - break; - default: ASSERT(0, "Unexpected call kind"); + context.stack_depth_add(STACK_SLOT_SIZE); + unsigned stackSize2free = epilog(StdPlaces_Keep); + assert(fit16(stackSize2free)); + buf = ::ret(buf, Imm_Opnd(stackSize2free)); + context.stack_depth_set(0); + return; } - } - void adjust_stack_to_callee_saved() - { - unsigned num_bytes = m2n_base(&ctxt); - if (num_bytes) { - *buf = alu(*buf, add_opc, esp_opnd, Imm_Opnd(num_bytes)); - } - } + assert(kind == LCK_Call || kind == LCK_CallNoRet); - void adjust_stack_for_return() - { - assert(lil_ic_get_m2n_state(ctxt.ctxt)==LMS_NoM2n); - adjust_stack_to_callee_saved(); - if (ctxt.info->num_callee_saves>=4) *buf = pop(*buf, edi_opnd); - if (ctxt.info->num_callee_saves>=3) *buf = pop(*buf, esi_opnd); - if (ctxt.info->num_callee_saves>=2) *buf = pop(*buf, ebx_opnd); - if (ctxt.info->num_callee_saves>=1) *buf = pop(*buf, ebp_opnd); + if (lil_operand_is_immed(targetOpnd)) { + int64 target_offset = lil_operand_get_immed(targetOpnd); + const Imm_Opnd& target = get_imm_opnd(target_offset, size_platf); + // make absolute call + Tmp_GR_Opnd tmpReg(context, ic); + buf = mov(buf, tmpReg, target, size_platf); + buf = ::call(buf, tmpReg, size_platf); + } + else { + const LilVarLocation * loc = get_op_loc(targetOpnd, false); + buf = ::call(buf, get_rm_opnd(loc), size_platf); + } + LilSig* lilSig = lil_ic_get_out_sig(ic); + if (lilSig == NULL) { + return; // nothing to do anymore + } + LilCallSig csig(lilSig); + // restore stack, if necessary + unsigned stackSize = csig.get_stack_size(); + if (stackSize != 0 && !csig.callee_restores_stack()) { + buf = alu(buf, add_opc, stack_opnd, Imm_Opnd(stackSize)); + } + // reflect the stack depth change + context.stack_depth_sub(stackSize); + // preserve return value + LilType retType = lil_sig_get_ret_type(lilSig); + int offset = context.get_return_value_save_offset(); + M_Opnd retSave(stack_reg, offset); + + if (retType == LT_F4 || retType == LT_F8) { + const bool is_dbl = retType == LT_F8; + if (csig.use_fpu_ret()) { + buf = fst(buf, retSave, is_dbl, true); + } + else { + Reg_No xmmRetReg = csig.get_fp_ret_reg(); + XMM_Opnd xmmRet(xmmRetReg); + buf = sse_mov(buf, retSave, xmmRet, is_dbl); + } + } + else if (retType == LT_G8) { + buf = mov(buf, retSave, eax_opnd, size_platf); +#if !defined(_EM64T_) + M_Opnd retSaveHi(stack_reg, offset+4); + buf = mov(buf, retSaveHi, edx_opnd, size_32); +#endif + } + else if (retType != LT_Void) { + buf = mov(buf, retSave, eax_opnd, size_32); + } + if (kind == LCK_CallNoRet) { + context.stack_depth_set(0); + } } - void ret() - { - adjust_stack_for_return(); - unsigned sz = sig_size_on_stack(ctxt.entry_sig); - if (ctxt.entry_cc.callee_pop && sz) { - *buf = ::ret(*buf, Imm_Opnd(sz)); - } else { - *buf = ::ret(*buf); + void ret() { + // unwind current stack frame + unsigned stackSize2free = epilog(StdPlaces_Drop); + if (stackSize2free != 0) { + buf = alu(buf, add_opc, stack_opnd, Imm_Opnd(stackSize2free)); + } + unsigned argsSize = m_csig.get_stack_size(); + if (m_csig.callee_restores_stack() && argsSize != 0) { + buf = ::ret(buf, Imm_Opnd(argsSize)); + } + else { + buf = ::ret(buf); } } - void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) - { - adjust_stack_to_callee_saved(); - *buf = m2n_gen_push_m2n(*buf, method, current_frame_type, handles, ctxt.info->num_callee_saves); + // guarantee to keep inputs, locals & standard places + // input registers should be preserved outside if required + void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) { + int retAddr_offset = context.get_ret_addr_offset(); + M_Base_Opnd retAddr(stack_reg, retAddr_offset); + buf = m2n_gen_fill_m2n(buf, + context.get_m2n_offset(), + method, current_frame_type, false, retAddr); } - void m2n_save_all() - { - // This is a nop on IA32 + void m2n_save_all() { } - void pop_m2n() - { - *buf = m2n_gen_pop_m2n(*buf, lil_ic_get_m2n_state(ctxt.ctxt)==LMS_Handles, ctxt.info->num_callee_saves, m2n_base(&ctxt), ii->u.pop_m2n); + void pop_m2n() { + buf = m2n_gen_clear_m2n(buf, context.get_m2n_offset(), context.m2n_has_handles(), false); } - void print(char *, LilOperand *) { - // not implemented on ia32 + void print(char * str, LilOperand * o) { + //no op } - - tl::MemoryPool* mem; - char** buf; - LilCguLabelAddresses la_tab; - LcgIa32Context ctxt; - LcgIa32InstInfo* ii; }; -// a helper function, called by compile() and get_stub_size() -// previously a part of the LilCodeGeneratorIa32 interface -static void main_pass(LilCodeStub* cs, tl::MemoryPool* mem, NativeCodePtr _buf, - LcgIa32PrePassInfo* info, size_t * stub_size) +NativeCodePtr LilCodeGeneratorIa32::compile_main( + LilCodeStub * cs, + size_t * stub_size, + PoolManager* code_pool) { - char* buf = (char*)_buf; - LilCguLabelAddresses la_tab(mem, 0); - - if (info->num_callee_saves>=1) buf = push(buf, ebp_opnd); - if (info->num_callee_saves>=2) buf = push(buf, ebx_opnd); - if (info->num_callee_saves>=3) buf = push(buf, esi_opnd); - if (info->num_callee_saves>=4) buf = push(buf, edi_opnd); - - LilInstructionIterator iter(cs, true); - LcgIa32IntrCodeGen cgv(mem, cs, &buf, info); - while(!iter.at_end()) { - LilInstruction* i = iter.get_current(); - cgv.update_context(iter.get_context()); - lil_visit_instruction(i, &cgv); - iter.goto_next(); - } - - assert((char *)_buf+info->size >= buf); - *stub_size = buf - (char *)_buf; + // start a memory manager + tl::MemoryPool m; + // get context + LilCodeGenContext * context = new(m) LilCodeGenContext(cs, m); + // generate code + LilCodeGen codegen(cs, *context, m); + // copy generated code to the destination + *stub_size = codegen.get_size(); + NativeCodePtr buffer = allocate_memory(*stub_size, code_pool); + codegen.copy_stub(buffer); + + return buffer; } - -NativeCodePtr LilCodeGeneratorIa32::compile_main(LilCodeStub* cs, size_t* stub_size, PoolManager* code_pool) -{ - LcgIa32PrePassInfo* data; - tl::MemoryPool mem; - size_t size = pre_pass(cs, &mem, &data); - NativeCodePtr buf = allocate_memory(size, code_pool); - main_pass(cs, &mem, buf, data, stub_size); - return buf; +GenericFunctionPointer lil_npc_to_fp(NativeCodePtr ncp) { + return (GenericFunctionPointer)ncp; } -GenericFunctionPointer lil_npc_to_fp(NativeCodePtr p) -{ - return (GenericFunctionPointer)p; -} diff --git a/vm/port/src/lil/ia32/pim/m2n_ia32.cpp b/vm/port/src/lil/ia32/pim/m2n_ia32.cpp index c6c85af..fc5f75a 100644 --- a/vm/port/src/lil/ia32/pim/m2n_ia32.cpp +++ b/vm/port/src/lil/ia32/pim/m2n_ia32.cpp @@ -1,4 +1,4 @@ -/* +/* * 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. @@ -14,24 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** +/** * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ + */ -#include "open/types.h" -#include "open/hythread.h" +#include "port_malloc.h" #include "m2n.h" #include "m2n_ia32_internal.h" -#include "port_malloc.h" #include "object_handles.h" #include "vm_threads.h" #include "encoder.h" -#include "interpreter.h" // for asserts only +#include "include/lil_code_generator_ia32.h" #include "exceptions.h" - ////////////////////////////////////////////////////////////////////////// // M2nFrame Interface @@ -89,7 +85,6 @@ void* m2n_get_ip(M2nFrame* lm2nf) return (void*)lm2nf->eip; } -// 20040708 New function - needs proper implementation. void m2n_set_ip(M2nFrame* lm2nf, NativeCodePtr ip) { lm2nf->eip = (uint32)ip; @@ -97,7 +92,6 @@ void m2n_set_ip(M2nFrame* lm2nf, NativeC Method_Handle m2n_get_method(M2nFrame* m2nf) { - ASSERT_NO_INTERPRETER return m2nf->method; } @@ -153,16 +147,17 @@ bool m2n_is_suspended_frame(M2nFrame * m } -void * m2n_get_frame_base(M2nFrame * m2nf) { - // regs should be last field in the M2nFrame structure - return &m2nf->regs; -} - //***** Stub Interface uint32* m2n_get_args(M2nFrame* m2nf) { - return (uint32*)&m2nf->regs; + char* retAddrPtr = (char*)m2nf->esp; +#ifdef _DEBUG + void* retAddr = *(void**)retAddrPtr; + assert((void*)m2nf->eip == retAddr); +#endif + char* argsAreaPtr = retAddrPtr + sizeof(void*); + return (uint32*)argsAreaPtr; //(uint32*)&m2nf->regs; } unsigned m2n_ts_to_register_size() @@ -172,86 +167,205 @@ unsigned m2n_ts_to_register_size() char* m2n_gen_ts_to_register(char* buf, R_Opnd* reg) { - if (reg!=&eax_opnd) - buf = push(buf, eax_opnd); - buf = push(buf, ecx_opnd); - buf = push(buf, edx_opnd); - buf = call(buf, (char *)get_thread_ptr); - buf = pop(buf, edx_opnd); - buf = pop(buf, ecx_opnd); - if (reg!=&eax_opnd) { + buf = mov(buf, eax_opnd, Imm_Opnd((char*)get_thread_ptr)); + buf = call(buf, eax_opnd); + if (!equals(reg->reg_no(), eax_reg)) { buf = mov(buf, *reg, eax_opnd); - buf = pop(buf, eax_opnd); } return buf; } unsigned m2n_push_m2n_size(bool UNREF handles, unsigned num_callee_saves) { - return 20+(4-num_callee_saves)+m2n_ts_to_register_size()+11+8; + // should be enough. anyway, I would suggest to kill all these magic + // numbers and switch to dynamically grown arrays. + return 0x100; } -char* m2n_gen_push_m2n(char* buf, Method_Handle method, frame_type current_frame_type, bool UNREF handles, unsigned num_callee_saves) +char* m2n_gen_set_local_handles_r( + char * buf, + unsigned bytes_to_m2n, + const R_Opnd * src_reg) { - // The -4 is because the normal M2nFrames do not include the esp field. - assert(m2n_sizeof_m2n_frame == sizeof(M2nFrame)-4); - - if (num_callee_saves<1) buf = push(buf, ebp_opnd); - if (num_callee_saves<2) buf = push(buf, ebx_opnd); - if (num_callee_saves<3) buf = push(buf, esi_opnd); - if (num_callee_saves<4) buf = push(buf, edi_opnd); - - buf = m2n_gen_ts_to_register(buf, &eax_opnd); - - buf = push(buf, Imm_Opnd(size_32, 0)); - //MVM APN 20050513 work with frame_type set up current_frame_type to NULL - //int frame_type_offset = (int)&((VM_thread*)0)->current_frame_type; - buf = push(buf, Imm_Opnd(current_frame_type)); - - int last_m2n_frame_offset = (int)&((VM_thread*)0)->last_m2n_frame; - Imm_Opnd imm1(last_m2n_frame_offset); - buf = alu(buf, add_opc, eax_opnd, imm1); - - Imm_Opnd imm2((unsigned)method); - buf = push(buf, imm2); - Imm_Opnd imm3(0); - buf = push(buf, imm3); - buf = push(buf, eax_opnd); - buf = push(buf, M_Base_Opnd(eax_reg, +0)); - buf = mov(buf, M_Base_Opnd(eax_reg, +0), esp_opnd ); + unsigned offset_local_handles = offsetof(M2nFrame, local_object_handles); + M_Base_Opnd onStack(stack_reg, bytes_to_m2n + offset_local_handles); + buf = mov(buf, onStack, *src_reg, size_platf); + return buf; +} +char* m2n_gen_set_local_handles_imm( + char * buf, + unsigned bytes_to_m2n, + const Imm_Opnd * imm) +{ + unsigned offset_local_handles = offsetof(M2nFrame, local_object_handles); + M_Base_Opnd onStack(stack_reg, bytes_to_m2n + offset_local_handles); + buf = mov(buf, onStack, *imm, size_platf); + return buf; +} + +char* m2n_gen_push_m2n( + char* buf, + Method_Handle method, + frame_type current_frame_type, + const RM_Opnd& ip_before_m2n) +{ + const Imm_Opnd m2n_size(size_32, sizeof(M2nFrame)); + buf = alu(buf, sub_opc, stack_opnd, m2n_size); + + int offset = -1; + + if (ip_before_m2n.is_reg()) { + // TODO: implement me. Currently is not used across the LIL codegen + // and seems will not be used. + assert(false); + } + else { + //FIXME: Presumption that only M_Base_opnd is here. + // Based only on current LIL IA32 codegen. + M_Base_Opnd* old_ip = (M_Base_Opnd*)&ip_before_m2n; + offset = old_ip->disp().get_value() + m2n_size.get_value(); + assert(old_ip->base().reg_no() == stack_reg); + } + M_Base_Opnd ip(stack_reg, offset); + //ip_before_m2n. + buf = m2n_gen_fill_m2n(buf, 0/*offset from ESP*/, + method, current_frame_type, true, ip); + return buf; +} + +char* m2n_gen_pop_m2n(char* buf, bool handles) +{ + const Imm_Opnd m2n_size(size_32, sizeof(M2nFrame)); + buf = m2n_gen_clear_m2n(buf, 0/*offset from ESP*/, handles, true); + buf = alu(buf, add_opc, stack_opnd, m2n_size); + return buf; +} + + +char* m2n_gen_fill_m2n( + char* buf, + unsigned offsetFromSP, + Method_Handle method, + frame_type frameType, + bool spillRegs, + const RM_Opnd& ip) +{ + // + // M2NFrame:: + for (unsigned i=0; spillRegs && ilocal_object_handles; + unsigned num = bytes_to_m2n + offsetof(M2nFrame, local_object_handles); return num<128 ? 4 : 7; } char* m2n_gen_set_local_handles(char* buf, unsigned bytes_to_m2n, R_Opnd* src_reg) { - unsigned offset_local_handles = (unsigned)&((M2nFrame*)0)->local_object_handles; + unsigned offset_local_handles = offsetof(M2nFrame, local_object_handles); buf = mov(buf, M_Base_Opnd(esp_reg, bytes_to_m2n+offset_local_handles), *src_reg); return buf; } +void free_local_object_handles2(ObjectHandles*); + unsigned m2n_pop_m2n_size(bool handles, unsigned num_callee_saves, unsigned extra_on_stack, unsigned preserve_ret) { - unsigned size = 7+(4-num_callee_saves); - if (handles) size += (extra_on_stack+8<128 ? 12 : 15)+preserve_ret*2; - else size += 11; - if (extra_on_stack) size += (extra_on_stack<128 ? 3 : 6); - return size + 128; + // should be enough. anyway all these magic numbers are subjects to be + // killed and switch to dynamically allocated buffer instead. + return 0x100; } static void m2n_pop_local_handles() { assert(!hythread_is_suspend_enabled()); - + if (exn_raised()) { exn_rethrow(); } - + M2nFrame * m2n = m2n_get_last_frame(); free_local_object_handles2(m2n->local_object_handles); } @@ -267,55 +381,64 @@ static void m2n_free_local_handles() { free_local_object_handles3(m2n->local_object_handles); } -char* m2n_gen_pop_m2n(char* buf, bool handles, unsigned num_callee_saves, unsigned extra_on_stack, unsigned preserve_ret) -{ - if (preserve_ret > 0) { - // Save return value - buf = push(buf, eax_opnd); - if (preserve_ret > 1) { - buf = push(buf, edx_opnd); - } +void m2n_free_local_object_handles() { + M2nFrame *m2n = m2n_get_last_frame(); + if (m2n->local_object_handles != NULL) { + free_local_object_handles2(m2n->local_object_handles); } - - if (handles) { +} + +char* m2n_gen_clear_m2n( + char* buf, + unsigned offsetFromSP, + bool hasHandles, + bool restoreRegs) +{ + if (hasHandles) { // There are handles located on the stack - buf = call(buf, (char*)m2n_pop_local_handles); + buf = mov(buf, eax_opnd, (char*)m2n_pop_local_handles); } else { - buf = call(buf, (char*)m2n_free_local_handles); + buf = mov(buf, eax_opnd, (char*)m2n_free_local_handles); } - - if (preserve_ret > 0) { - // Restore return value - if (preserve_ret > 1) { - buf = pop(buf, edx_opnd); + buf = call(buf, eax_opnd); + + // Unlink the M2nFrame from the list of the current thread + + /* mov eax, M2N::prev_m2nf */ + buf = mov(buf, eax_opnd, M_Base_Opnd(stack_reg, offsetFromSP+offsetof(M2nFrame, prev_m2nf))); + /* mov ecx, M2N::p_lm2nf */ + buf = mov(buf, ecx_opnd, M_Base_Opnd(stack_reg, offsetFromSP+offsetof(M2nFrame, p_lm2nf))); + /* mov [ecx], eax */ + buf = mov(buf, M_Base_Opnd(ecx_reg, 0), eax_opnd); + + for (unsigned i=0; restoreRegs && ipop_regs; @@ -325,4 +448,3 @@ Registers* get_pop_frame_registers(M2nFr void set_pop_frame_registers(M2nFrame* m2nf, Registers* regs) { m2nf->pop_regs = regs; } - diff --git a/vm/port/src/lil/ia32/pim/m2n_ia32_internal.h b/vm/port/src/lil/ia32/pim/m2n_ia32_internal.h index 40e00d4..f346002 100644 --- a/vm/port/src/lil/ia32/pim/m2n_ia32_internal.h +++ b/vm/port/src/lil/ia32/pim/m2n_ia32_internal.h @@ -14,10 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** +/** * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ + */ #ifndef _M2N_IA32_INTERNAL_H_ @@ -31,8 +30,7 @@ #include "m2n.h" #include "vm_threads.h" #include "open/types.h" - -class R_Opnd; +#include "encoder.h" // Return a pointer to the argument just above the return address of an M2nFrame uint32* m2n_get_args(M2nFrame*); @@ -40,8 +38,6 @@ uint32* m2n_get_args(M2nFrame*); // An M2nFrame is a structure that resides on the stack. // It takes up space below and including the return address to the managed code, and thus is immediately below the arguments. -// This is the size of the structure that goes on the stack. -const unsigned m2n_sizeof_m2n_frame = 44; // Generate code to put the thread local storage pointer into a given register unsigned m2n_ts_to_register_size(); @@ -54,7 +50,14 @@ char* m2n_gen_ts_to_register(char* buf, // After the sequence, esp points to the M2nFrame. // handles: will the stub want local handles or not unsigned m2n_push_m2n_size(bool handles, unsigned num_callee_saves); -char* m2n_gen_push_m2n(char* buf, Method_Handle method, frame_type current_frame_type, bool handles, unsigned num_callee_saves); +char* m2n_gen_push_m2n(char* buf, Method_Handle method, frame_type current_frame_type, const RM_Opnd& ip); +char* m2n_gen_fill_m2n( + char* buf, + unsigned offsetFromSP, + Method_Handle method, + frame_type current_frame_type, + bool spillRegs, + const RM_Opnd& ip); // Generate code to set the local handles of an M2nFrame // The M2nFrame is located bytes_to_m2n above esp, and src_reg has the address of the frames. @@ -67,7 +70,14 @@ char* m2n_gen_set_local_handles(char* bu // preserve_ret: the number of return registers to preserve, 0 means none, 1 means eax, 2 means eax & edx; st(0) is always preserved. // handles: as for push_m2n, frees the handles if true. unsigned m2n_pop_m2n_size(bool handles, unsigned num_callee_saves, unsigned extra_on_stack, unsigned preserve_ret); -char* m2n_gen_pop_m2n(char* buf, bool handles, unsigned num_callee_saves, unsigned extra_on_stack, unsigned preserve_ret); +char* m2n_gen_pop_m2n(char* buf, bool handles); +char* m2n_gen_clear_m2n( + char* buf, + unsigned offsetFromSP, + bool hasHandles, + bool restoreRegs); + +char* m2n_gen_store_reg(char* buf, unsigned offsetFromSP, const R_Opnd& reg); ////////////////////////////////////////////////////////////////////////// // Implementation details @@ -97,12 +107,44 @@ struct M2nFrame { Method_Handle method; frame_type current_frame_type; // type of the current frame also shows is the frame unwindable Registers* pop_regs; // This is only for M2nFrames for suspended managed code (as against ones that call stubs and prepare jvmtiPopFrame) + // Callee-save (common) uint32 edi; uint32 esi; uint32 ebx; uint32 ebp; + // Scratch + uint32 eax; + uint32 ecx; + uint32 edx; + // + double xmms[n_xmm_regs]; + // + uint32 esp; + // uint32 eip; + // Registers* regs; // This is only for M2nFrames for suspended managed code (as against ones that call stubs and prepare jvmtiPopFrame) }; +inline int m2n_get_reg_offset(Reg_No reg) +{ + int offset = 0; + if (reg == ebx_reg) { offset = offsetof(M2nFrame, ebx);} + else if (reg == ebp_reg) { offset = offsetof(M2nFrame, ebp);} + else if (reg == esi_reg) { offset = offsetof(M2nFrame, esi);} + else if (reg == edi_reg) { offset = offsetof(M2nFrame, edi);} + else if (reg == eax_reg) { offset = offsetof(M2nFrame, eax);} + else if (reg == ecx_reg) { offset = offsetof(M2nFrame, ecx);} + else if (reg == edx_reg) { offset = offsetof(M2nFrame, edx);} + else if (is_xmm_reg(reg)) { int idx = (reg-xmm0_reg); offset = offsetof(M2nFrame, xmms[idx]); } + else {assert(false); offset = 0; } + return offset; +} + +char* m2n_gen_set_local_handles_imm(char * buf, unsigned bytes_to_m2n, const Imm_Opnd * imm); +char* m2n_gen_set_local_handles_r(char * buf, unsigned bytes_to_m2n, const R_Opnd * src_reg); + +// This is the size of the structure that goes on the stack. +const unsigned m2n_sizeof_m2n_frame = sizeof(M2nFrame); + #endif //!_M2N_IA32_INTERNAL_H_ diff --git a/vm/port/src/lil/ia32/pim/stack_iterator_ia32.cpp b/vm/port/src/lil/ia32/pim/stack_iterator_ia32.cpp index a5f2abf..767e66e 100644 --- a/vm/port/src/lil/ia32/pim/stack_iterator_ia32.cpp +++ b/vm/port/src/lil/ia32/pim/stack_iterator_ia32.cpp @@ -15,10 +15,9 @@ * limitations under the License. */ -/** +/** * @author Intel, Pavel Afremov - * @version $Revision$ - */ + */ #include "environment.h" #include "jit_intf_cpp.h" @@ -34,8 +33,8 @@ #include "cci.h" #include "clog.h" - #include "dump.h" +#include "callconv.h" // Invariants: // Native frames: @@ -75,9 +74,9 @@ static void si_unwind_from_m2n(StackIter TRACE2("si", ("si_unwind_from_m2n, ip = %p",(void*)m2nfl->eip)); // Is it a normal M2nFrame or one for suspended managed code? - if ((uint32)m2nfl->p_lm2nf==1) { + if (m2n_is_suspended_frame(m2nfl)) { // Suspended managed code, eip is at instruction, esp & registers are in regs structure - TRACE2("si", ("si_unwind_from_m2n from suspended managed code, ip = %p", + TRACE2("si", ("si_unwind_from_m2n from suspended managed code, ip = %p", (void*)m2nfl->regs->eip)); si->c.esp = m2nfl->regs->esp; si->c.p_eip = &(m2nfl->regs->eip); @@ -90,6 +89,9 @@ static void si_unwind_from_m2n(StackIter si->c.p_edi = &m2nfl->regs->edi; si->c.p_ebp = &m2nfl->regs->ebp; si->c.eflags = m2nfl->regs->eflags; + for (unsigned i=0; ic.p_xmms); i++) { + si->c.p_xmms[i] = &m2nfl->regs->xmms[i]; + } } else if (over_popped && (FRAME_MODIFIED_STACK == (FRAME_MODIFIED_STACK & m2n_get_frame_type(m2nfl)))) { si->c.esp = m2nfl->pop_regs->esp; @@ -103,16 +105,29 @@ static void si_unwind_from_m2n(StackIter si->c.p_edi = &m2nfl->pop_regs->edi; si->c.p_ebp = &m2nfl->pop_regs->ebp; si->c.eflags = m2nfl->pop_regs->eflags; + for (unsigned i=0; ic.p_xmms); i++) { + si->c.p_xmms[i] = &m2nfl->pop_regs->xmms[i]; + } } else { - // Normal M2nFrame, eip is past instruction, esp is implicitly address just beyond the frame, callee saves registers in M2nFrame - si->c.esp = (uint32)m2nfl + m2n_sizeof_m2n_frame; + // Normal M2nFrame, eip is past instruction, + // callee saves registers in M2nFrame + si->c.esp = m2nfl->esp + sizeof(void*); // pop out retAddr from stack si->c.p_eip = &m2nfl->eip; si->c.is_ip_past = TRUE; + si->c.eflags = 0; + // si->c.p_edi = &m2nfl->edi; si->c.p_esi = &m2nfl->esi; si->c.p_ebx = &m2nfl->ebx; si->c.p_ebp = &m2nfl->ebp; - si->c.p_eip = &m2nfl->eip; + // + si->c.p_eax = &m2nfl->eax; + si->c.p_ecx = &m2nfl->ecx; + si->c.p_edx = &m2nfl->edx; + // + for (unsigned i=0; ic.p_xmms); i++) { + si->c.p_xmms[i] = &m2nfl->xmms[i]; + } } } @@ -123,6 +138,32 @@ static char* get_reg(char* ss, R_Opnd* d return ss; } +static char* get_xmm( + char* ss, + unsigned i, + const Reg_No baseReg, + const Reg_No tmpReg) +{ + XMM_Opnd xmm(i); + unsigned offset = offsetof(StackIterator, c.p_xmms[i]); + M_Base_Opnd ptr2ptr(baseReg, offset); + M_Base_Opnd ptr(tmpReg, 0); + ss = mov(ss, R_Opnd(tmpReg), ptr2ptr); + ss = movq(ss, xmm, ptr); + return ss; +} + +static char* get_reg( + char* ss, R_Opnd& dst, + Reg_No si_ptr_var, + unsigned offset) +{ + ss = mov(ss, dst, M_Base_Opnd(si_ptr_var, offset)); + ss = mov(ss, dst, M_Base_Opnd(dst.reg_no(), 0)); + return ss; +} + + typedef void (__cdecl *transfer_control_stub_type)(StackIterator*); static transfer_control_stub_type gen_transfer_control_stub() @@ -132,95 +173,111 @@ static transfer_control_stub_type gen_tr return addr; } - const int stub_size = 0x47; + const int stub_size = 0x80; char *stub = (char *)malloc_fixed_code_for_jit(stub_size, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_COLD, CAA_Allocate); #ifdef _DEBUG memset(stub, 0xcc /*int 3*/, stub_size); #endif char *ss = stub; - // - // ************* LOW LEVEL DEPENDENCY! *************** - // This code sequence must be atomic. The "atomicity" effect is achieved by - // changing the esp at the very end of the sequence. + // The stub has a single argument which is StackIterator* (pSI) // + M_Base_Opnd pSI(stack_reg, sizeof(void*)); + // + // Here, in the stub we can use 2 memory slots without a fear - + // ESP+0 - it keeps callee address (but we don't return there anyway) + // ESP+4 - pSI, input arg after loading the argumen on a register (this + // slot is not used currently) + const Reg_No pSIRegNo = ebx_reg; + const R_Opnd pSIReg(pSIRegNo); + + const Reg_No tmpRegNo = eax_reg; + /* + Strategy: + mov ebx, pSI + for i = 0 .. 7 + load xmm#i + ; prepare ESP, EIP + mov edx, newESP + mov ecx, newIP + sub edx, sizeof(void*) + mov [edx], ecx + mov [esp+0], edx + ;; + restore flags + restore all but ebx + restore ebx + load esp + load eip + } + */ + ss = mov(ss, pSIReg, pSI); + // minor optimization - we only have callee-save XMMs in CC_DRLFast + if (vm_cc_is_drlfast_enabled()) { + for (unsigned i=0; ic.p_eip); - - M_Base_Opnd m2(edx_reg, (int)&((StackIterator*)0)->c.esp); - ss = mov(ss, ecx_opnd, m2); - - ss = alu(ss, sub_opc, ecx_opnd, Imm_Opnd(4)); - ss = mov(ss, m1, ecx_opnd); - - ss = get_reg(ss, &esi_opnd, esi_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.p_esi); - ss = get_reg(ss, &edi_opnd, edi_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.p_edi); - ss = get_reg(ss, &ebp_opnd, ebp_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.p_ebp); - - M_Base_Opnd m3(ecx_reg, 0); - ss = mov(ss, m3, ebx_opnd); - - ss = get_reg(ss, &eax_opnd, eax_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.p_eax); - ss = get_reg(ss, &ebx_opnd, ebx_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.p_ebx); - - ss = mov(ss, ecx_opnd, M_Base_Opnd(edx_reg, (unsigned)&((StackIterator*)0)->c.eflags)); + // + // May need to restore EFLAGS, if it's not zero: + // mov ecx, [pSI->c.eflags] + // test ecx, ecx + // jz proceed_loading_regs + // push ecx + // popfd + //proceed_loading_regs: + // ... fall through + ss = mov(ss, ecx_opnd, M_Base_Opnd(pSIRegNo, offsetof(StackIterator, c.eflags))); ss = test(ss, ecx_opnd, ecx_opnd); ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0)); char* patch_offset = ((char *)ss) - 1; // Store location for jump patch ss = push(ss, ecx_opnd); - *ss++ = (char)0x9D; // POPFD + ss = popfd(ss); // Patch conditional jump signed offset = (signed)ss - (signed)patch_offset - 1; *patch_offset = (char)offset; - ss = get_reg(ss, &ecx_opnd, ecx_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.p_ecx); - ss = get_reg(ss, &edx_opnd, edx_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.p_edx); + // + // Restore all, but pSIRegNo + // + ss = get_reg(ss, eax_opnd, pSIRegNo, offsetof(StackIterator, c.p_eax)); + assert(pSIRegNo == ebx_reg); + ss = get_reg(ss, ecx_opnd, pSIRegNo, offsetof(StackIterator, c.p_ecx)); + ss = get_reg(ss, edx_opnd, pSIRegNo, offsetof(StackIterator, c.p_edx)); + ss = get_reg(ss, esi_opnd, pSIRegNo, offsetof(StackIterator, c.p_esi)); + ss = get_reg(ss, edi_opnd, pSIRegNo, offsetof(StackIterator, c.p_edi)); + ss = get_reg(ss, ebp_opnd, pSIRegNo, offsetof(StackIterator, c.p_ebp)); + // restore pSIRegNo + ss = get_reg(ss, ebx_opnd, pSIRegNo, offsetof(StackIterator, c.p_ebx)); - ss = mov(ss, esp_opnd, m1); + // + // Load 'newESP-sizeof(void*)' into ESP + // + ss = mov(ss, stack_opnd, tos_opnd); + // Now, the return address is right on the top of stack - + // restore EIP & correct ESP ss = ret(ss); + // + // + // addr = (transfer_control_stub_type)stub; - assert(ss-stub <= stub_size); - - /* - The following code will be generated: - - mov edx,dword ptr [esp+4] - mov ebx,dword ptr [edx+0Ch] - mov ebx,dword ptr [ebx] - mov ecx,dword ptr [edx+4] - sub ecx,4 - mov dword ptr [esp+4],ecx - mov esi,dword ptr [edx+14h] - mov esi,dword ptr [esi] - mov edi,dword ptr [edx+10h] - mov edi,dword ptr [edi] - mov ebp,dword ptr [edx+8] - mov ebp,dword ptr [ebp] - mov dword ptr [ecx],ebx - mov eax,dword ptr [edx+1Ch] - mov eax,dword ptr [eax] - mov ebx,dword ptr [edx+18h] - mov ebx,dword ptr [ebx] - mov ecx,dword ptr [edx+28h] - test ecx,ecx - je _label_ - push ecx - popfd -_label_: - mov ecx,dword ptr [edx+20h] - mov ecx,dword ptr [ecx] - mov edx,dword ptr [edx+24h] - mov edx,dword ptr [edx] - mov esp,dword ptr [esp+4] - ret - */ - - DUMP_STUB(stub, "getaddress__transfer_control", ss - stub); - + const unsigned realSize = ss-stub; + assert(realSize <= stub_size); + DUMP_STUB(stub, "getaddress__transfer_control", realSize); return addr; } @@ -284,7 +341,9 @@ StackIterator* si_create_from_registers( res->c.is_ip_past = is_ip_past; res->c.eflags = regs->eflags; res->m2nfl = lm2nf; - + for (unsigned i=0; ic.p_xmms); i++) { + res->c.p_xmms[i] = ®s->xmms[i]; + } return res; } @@ -477,6 +536,7 @@ void si_transfer_control(StackIterator* if (si->c.p_eip == &si->ip) local_si.c.p_eip = &local_si.ip; + si_free(si); // 2. Set the M2nFrame list @@ -496,6 +556,11 @@ void si_transfer_control(StackIterator* inline static uint32 unref_reg(uint32* p_reg) { return p_reg ? *p_reg : 0; } + +inline static double unref_reg(double* p_reg) { + return p_reg ? *p_reg : 0; +} + void si_copy_to_registers(StackIterator* si, Registers* regs) { ASSERT_NO_INTERPRETER @@ -510,6 +575,9 @@ void si_copy_to_registers(StackIterator* regs->ecx = unref_reg(si->c.p_ecx); regs->ebx = unref_reg(si->c.p_ebx); regs->eax = unref_reg(si->c.p_eax); + for (unsigned i=0; ixmms); i++) { + regs->xmms[i] = unref_reg(si->c.p_xmms[i]); + } } void si_set_callbak(StackIterator* si, NativeCodePtr* callback) { diff --git a/vm/port/src/lil/lil.cpp b/vm/port/src/lil/lil.cpp index f1a6416..89eec03 100644 --- a/vm/port/src/lil/lil.cpp +++ b/vm/port/src/lil/lil.cpp @@ -22,11 +22,6 @@ #include -//MVM -#include - -using namespace std; - #define LOG_DOMAIN "vm.helpers" #include "cxxlog.h" @@ -62,7 +57,7 @@ struct LilCond { enum LilInstructionTag { LIT_Label, LIT_Locals, LIT_StdPlaces, LIT_Alloc, LIT_Asgn, LIT_Ts, LIT_Handles, LIT_Ld, LIT_St, LIT_Inc, LIT_Cas, LIT_J, LIT_Jc, LIT_Out, LIT_In2Out, LIT_Call, LIT_Ret, - LIT_PushM2N, LIT_M2NSaveAll, LIT_PopM2N, LIT_Print + LIT_PushM2N, LIT_M2NSaveAll, LIT_PopM2N, LIT_Trap, LIT_Print }; struct LilInstruction { @@ -253,6 +248,10 @@ static bool lil_parse_cc(const char** sr { lil_skip_ws(src); switch ((*src)[0]) { + case 'd': // drlfast + if (!lil_parse_kw(src, "drlfast")) return false; + *cc = LCC_ManagedFast; + return true; case 'j': // jni if (!lil_parse_kw(src, "jni")) return false; *cc = LCC_Jni; @@ -947,11 +946,17 @@ static LilInstruction* lil_parse_instruc if (!lil_parse_operand(src, va, &(i->u.ldst.operand))) return NULL; } break; - case 't': // tailcall - i->tag = LIT_Call; - i->u.call.k = LCK_TailCall; - if (!lil_parse_kw(src, "tailcall")) return NULL; - if (!lil_parse_operand(src, va, &(i->u.call.target))) return NULL; + case 't': // tailcall, trap + if ((*src)[1] == 'r') { + i->tag = LIT_Trap; + if (!lil_parse_kw(src, "trap")) return NULL; + } + else { + i->tag = LIT_Call; + i->u.call.k = LCK_TailCall; + if (!lil_parse_kw(src, "tailcall")) return NULL; + if (!lil_parse_operand(src, va, &(i->u.call.target))) return NULL; + } break; default: error(src, "expecting instruction", ""); @@ -1025,6 +1030,18 @@ LilCodeStub* lil_parse_code_stub(const c cs->bb_list_head = NULL; cs->num_bbs = 0; + // Special handling of very first 'trap' even before a signature + lil_skip_ws(cur); + const char trap_name[] = "trap"; + if (0 == strncmp(*cur, trap_name, sizeof(trap_name)-1)) { + i = lil_parse_instruction(cur, mem, &cs->cur_gen_label, &va, &cs->sig); + assert(i); + *tail = i; + i->next = NULL; + tail = &(i->next); + lil_skip_ws(cur); + cs->num_is++; + } if (!lil_parse_kw(cur, "entry")) goto clean_up; POINTER_SIZE_INT n; if (!lil_parse_number(cur, &va, &n)) { @@ -1249,6 +1266,11 @@ LilType lil_instruction_get_dest_type(Li } } +bool lil_instruction_is_trap(const LilInstruction* i) +{ + return (NULL != i) && (i->tag == LIT_Trap); +} + static LilType lil_type_unary_op(LilOperation op, LilType t) { switch (op) { @@ -1368,6 +1390,8 @@ static void lil_next_context(LilInstruct c->num_std_places = 0; c->out_sig = NULL; break; + case LIT_Trap: + break; case LIT_Print: // nothing to do here break; @@ -1746,6 +1770,9 @@ void lil_visit_instruction(LilInstructio case LIT_PopM2N: v->pop_m2n(); break; + case LIT_Trap: + v->trap(); + break; case LIT_Print: v->print(i->u.print.str, &i->u.print.arg); break; @@ -1753,16 +1780,16 @@ void lil_visit_instruction(LilInstructio } } -LilVariableKind lil_variable_get_kind(LilVariable* v) { return v->tag; } -unsigned lil_variable_get_index(LilVariable* v) { return v->index; } +LilVariableKind lil_variable_get_kind(const LilVariable* v) { return v->tag; } +unsigned lil_variable_get_index(const LilVariable* v) { return v->index; } -bool lil_variable_is_equal(LilVariable* v1, LilVariable* v2) +bool lil_variable_is_equal(const LilVariable* v1, const LilVariable* v2) { return v1->tag==v2->tag && v1->index==v2->index; } -bool lil_operand_is_immed(LilOperand* o) { return o->is_immed; } -POINTER_SIZE_INT lil_operand_get_immed(LilOperand* o) { +bool lil_operand_is_immed(const LilOperand* o) { return o->is_immed; } +POINTER_SIZE_INT lil_operand_get_immed(const LilOperand* o) { assert(o->is_immed); return o->val.imm; } @@ -2055,6 +2082,8 @@ bool lil_is_valid(LilCodeStub* cs) case LIT_PopM2N: if (c->m2n==LMS_NoM2n) ERR("pop m2n not dominated by push"); break; + case LIT_Trap: + break; case LIT_Print: if (!lil_is_valid_operand(cs, c, &i->u.print.arg)) ERR("invalid argument to print"); @@ -2080,6 +2109,7 @@ void lil_print_cc(FILE* out, enum LilCc switch (cc) { case LCC_Platform: fprintf(out, "platform"); break; case LCC_Managed: fprintf(out, "managed"); break; + case LCC_ManagedFast: fprintf(out, "drlfast"); break; case LCC_Rth: fprintf(out, "rth"); break; case LCC_Jni: fprintf(out, "jni"); break; case LCC_StdCall: fprintf(out, "stdcall"); break; @@ -2405,6 +2435,9 @@ void lil_print_instruction(FILE* out, Li case LIT_PopM2N: fprintf(out, "pop_m2n"); break; + case LIT_Trap: + fprintf(out, "trap"); + break; case LIT_Print: fprintf(out, "print %p, ", i->u.print.str); lil_print_operand(out, &i->u.print.arg); diff --git a/vm/port/src/lil/lil_dbg.cpp b/vm/port/src/lil/lil_dbg.cpp new file mode 100644 index 0000000..017dbca --- /dev/null +++ b/vm/port/src/lil/lil_dbg.cpp @@ -0,0 +1,178 @@ +/* + * 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. + */ + +/** + * Debugging trace and disassembling routines for dynamically generated + * code. + * + * @author Alexander Astapchuk + */ + + +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif // defined(_WIN32) +#include + +#include "lil.h" + +static const char * lil_dump_file_name = LIL_DUMP_FILE_NAME; + +typedef unsigned (*DISFUNC)(const char *kode, char *buf, unsigned buf_len); + +static const char * get_exe_dir(void) +{ +#ifdef _WIN32 + static char buf[_MAX_PATH+1]; + static const char FSEP = '\\'; +#else + static char buf[PATH_MAX+1]; + static const char FSEP = '/'; +#endif // ~_WIN32 + + static bool path_done = false; + if (path_done) { + return buf; + } + +#ifdef _WIN32 + if (!GetModuleFileName(NULL, buf, sizeof(buf))) { +#else + static const char self_path[] = "/proc/self/exe"; + // Resolve full path to module + if (readlink(self_path, buf, sizeof(buf)) < 0) { +#endif + // Something wrong happened. Trying last resort path. + // buf <= "./" + buf[0] = '.'; buf[1] = FSEP; buf[2] = '\0'; + } + else { + // Extract directory path + char * slash = strrchr(buf, FSEP); + if (NULL == slash) { // Huh ? How comes ? + // Trying last resort path. + // buf <= "./" + buf[0] = '.'; buf[1] = FSEP; buf[2] = '\0'; + } + else { + *(slash+1) = '\0'; + } + } + path_done = true; + return buf; +} + +/** + * Loads a disassembler provider. + * + * Refer to Jitrino.JET doxygen on lwdis description. + */ +static DISFUNC get_disfunc(void) +{ +#if !defined(PLATFORM_POSIX) + static HMODULE hDll = LoadLibrary("lwdis.dll"); + static void *disfunc = + hDll == NULL ? NULL : GetProcAddress(hDll, "disasm"); +#else // if !platform_posix + static bool load_done = false; + static void *disfunc = NULL; + if (!load_done) { + char buf[PATH_MAX+1]; + snprintf(buf, sizeof(buf), "%sliblwdis.so", get_exe_dir()); + void * handle = dlopen(buf, RTLD_NOW); + // An access with full path failed - let's try LD_LIBRARY_PATH. + if (handle==NULL) { + handle = dlopen("liblwdis.so", RTLD_NOW); + } + disfunc = handle == NULL ? NULL : dlsym(handle, "disasm"); + load_done = true; + } +#endif + return (DISFUNC)disfunc; +} + +FILE* lil_dbg_get_dump_file(void) +{ + static FILE* outFile = NULL; + if (NULL == outFile) { + outFile = fopen(LIL_DUMP_FILE_NAME, "w"); + assert(NULL != outFile); + } + return outFile; +} + +void lil_dbg_dump_start(const char* name) +{ + FILE* outFile = lil_dbg_get_dump_file(); + fprintf(outFile, "===== start: %s =====\n", name); +} + +void lil_dbg_dump_finish(const char* name) +{ + FILE* outFile = lil_dbg_get_dump_file(); + fprintf(outFile, "===== finish: %s =====\n", name); + fflush(outFile); +} + +void lil_dbg_dump(const char* code, unsigned code_len, bool showAddr) +{ + FILE* outFile = lil_dbg_get_dump_file(); + static DISFUNC disfunc = get_disfunc(); + + if (disfunc != NULL) { + for (unsigned i = 0; i < code_len; /**/) { + char buf[1024]; + const char* ip = code + i; + unsigned bytes = (disfunc) (ip, buf, sizeof(buf)); + if (showAddr) { fprintf(outFile, "%p", ip); } + if (bytes==0) { + // unknown instruction + unsigned char b = *(unsigned char*)(code+i); + fprintf(outFile, + "\tdb 0x%x (%d, %c)\n", b, b, b<33 || b>127 ? '.' : b); + i += 1; // cant be 0, or we'll fall into infinite loop + } + else { + fprintf(outFile, "\t%s\n", buf); + i += bytes; + } + } + } + else { + // if disassembler shared library not present, simply + // dump out the code + fprintf(outFile, "\t"); + for (unsigned i = 0; i < code_len; i++) { + fprintf(outFile, " %02X", (unsigned) (unsigned char) code[i]); + unsigned pos = (i + 1) % 16; + if (0 == pos) { + // output by 16 bytes per line + fprintf(outFile, "\n\t"); + } + else if (7 == pos) { + // additional space in the middle of the output + fprintf(outFile, " "); + } + } + } + fprintf(outFile, "\n"); +} diff --git a/vm/port/src/misc/ia32/callconv.cpp b/vm/port/src/misc/ia32/callconv.cpp new file mode 100644 index 0000000..be1d90e --- /dev/null +++ b/vm/port/src/misc/ia32/callconv.cpp @@ -0,0 +1,400 @@ +/* + * 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. + */ +#include "callsig.h" +#include "callconv.h" +#include + +#include "environment.h" + +#define LOG_DOMAIN "vm.core" +#include "cxxlog.h" + +#include +using std::bitset; + +#ifdef _WIN32 + #define srtcasecmp strcasecmp +#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 = false; + +/// Callee save XMM registers for DRLFast. +static bitset sm_drlfast_xmm_callee_save_flags(0); + +/// Registers used to pass arguments. RegName_Null marks end of the list. +static RegName sm_drlfast_gr_args[MAX_GP_REGS+1] = { + RegName_ECX, RegName_EDX, RegName_EAX, RegName_Null, +}; + + +/// Registers used to pass float-point args. RegName_Null marks end of the list. +static RegName sm_drlfast_fr_args[MAX_XMM_REGS+1] = { + RegName_XMM0, RegName_XMM1, RegName_XMM2, RegName_XMM3, + RegName_XMM4, RegName_XMM5, RegName_XMM6, RegName_XMM7, + RegName_Null +}; + + +/// Returns number of bytes occupied by the type when passed as argument on stack. +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 + // everything else takes 1 stack slot which is 4 bytes + return STACK_SLOT_SIZE; +} + + +/// \c true if \c type represents float-point type (float or double) +static bool is_f(VM_Data_Type type) +{ + return (VM_DATA_TYPE_F8 == type || VM_DATA_TYPE_F4 == type); +} + + +/** + * Safely converts a string value into bool. If the string could not be + * recognized, returns default value. + */ +static bool toBool(const char* value, bool def) +{ + if (NULL == value) { + return def; + } + if (!strcasecmp(value, "true") || + !strcasecmp(value, "yes") || !strcasecmp(value, "y") || + !strcasecmp(value, "on") || + !strcasecmp(value, "1")) { + return true; + } + if (!strcasecmp(value, "false") || + !strcasecmp(value, "no") || !strcasecmp(value, "n") || + !strcasecmp(value, "off") || + !strcasecmp(value, "0")) { + return false; + } + return def; +} + + +/** + * Safely converts a string value into integer. If the string could not be + * recognized, returns default value. + */ +static int toInt(const char* value, int def) +{ + if (NULL == value) { + return def; + } + if (!isdigit(value[0])) { + return def; + } + return atoi(value); +} + + +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(Global_Env *p_env) +{ + // + // Is DRLFast enabled after all?.. + // + CallingConvention cc = CC_Unknown; + const char* cc_name = get_property("vm.cc", VM_PROPERTIES); + if (cc_name != NULL) { + cc = vm_cc_parse_name(cc_name); + if (CC_Unknown == cc) { + ECHO("Warning: unknown calling convention: " << cc_name); + } + } + if (cc != CC_DRLFast) { + // .. no? Hmmm... Ok, then. + return; + } + vm_cc_enable_drlfast(true); + p_env->managed_calling_convention = CC_DRLFast; + + // + // Use GP regs to pass arguments? + // + const char* gp_args_prop = get_property("vm.drlfast.gp_reg_args", VM_PROPERTIES); + unsigned gp_args = toInt(gp_args_prop, 0); + const unsigned MAX_ALLOWED_GPS = 3; // == ECX + EDX + EAX + if (gp_args > MAX_ALLOWED_GPS) { + WARN("Too many gp_reg_args (" << gp_args << ")" + << ", max "<< MAX_ALLOWED_GPS <<"allowed." + << " Reduced to zero."); + gp_args = 0; + } + sm_drlfast_gr_args[gp_args] = RegName_Null; + + // + // Use the old-fashion FPU for float/double returns? + // + const char* fpu_ret_prop = get_property("vm.drlfast.use_fpu_ret", VM_PROPERTIES); + if (!toBool(fpu_ret_prop, true)) { + sm_drlfast_use_xmm_ret = true; + } + + // + // Use XMMs to pass float/double args? + // + const char* num_xmms_prop = get_property("vm.drlfast.xmm_reg_args", VM_PROPERTIES); + unsigned num_xmms = toInt(num_xmms_prop, 0); + if (num_xmms > MAX_XMM_REGS) { + WARN("Invalid number for 'xmm_reg_args': " << num_xmms << "; reduced to zero"); + num_xmms = 0; + } + for (unsigned i=0; i MAX_XMM_REGS) { + WARN("Invalid number for 'xmm_callee_save_regs': " + << num_xmms_callee_save + << "; reduced to zero"); + num_xmms_callee_save = 0; + } + if (num_xmms + num_xmms_callee_save > MAX_XMM_REGS) { + unsigned new_callee_saves = MAX_XMM_REGS - num_xmms; + WARN("Number of xmm_callee_save_regs (" << num_xmms_callee_save <<")" + << "and xmm_reg_args (" << num_xmms << ")" + << "exceed total number of XMM registers.\n" + "xmm_callee_save_regs reduced to " << new_callee_saves); + num_xmms_callee_save = new_callee_saves; + } + for (unsigned i=num_xmms; i<(num_xmms+num_xmms_callee_save); i++) { + sm_drlfast_xmm_callee_save_flags.set(i, true); + } + +} + + +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; + pSig->ensure_calculated(); + return pSig->get_arg_reg(index); +} + + +int vm_cc_csig_get_arg_offset(CallSigHandle csh, unsigned index) +{ + CallSig* pSig = (CallSig*)csh; + pSig->ensure_calculated(); + 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