From nobody Mon Sep 17 00:00:00 2001 From: Ilya Berezhniuk Date: Wed, 13 Feb 2008 14:37:53 +0300 Subject: [PATCH] --move-- moved LIL files from PORT to VMCORE --- vm/port/include/lil.h | 618 ----- vm/port/include/lil_code_generator.h | 70 - vm/port/include/lil_code_generator_utils.h | 82 - .../em64t/pim/include/lil_code_generator_em64t.h | 742 ------ .../src/lil/em64t/pim/lil_code_generator_em64t.cpp | 1730 --------------- vm/port/src/lil/em64t/pim/m2n_em64t.cpp | 437 --- vm/port/src/lil/em64t/pim/m2n_em64t_internal.h | 133 - vm/port/src/lil/em64t/pim/stack_iterator_em64t.cpp | 543 ---- .../lil/ia32/pim/include/lil_code_generator_ia32.h | 38 .../src/lil/ia32/pim/lil_code_generator_ia32.cpp | 1489 ------------- vm/port/src/lil/ia32/pim/m2n_ia32.cpp | 329 -- vm/port/src/lil/ia32/pim/m2n_ia32_internal.h | 108 - vm/port/src/lil/ia32/pim/stack_iterator_ia32.cpp | 541 ---- .../lil/ipf/pim/include/lil_code_generator_ipf.h | 43 vm/port/src/lil/ipf/pim/lil_code_generator_ipf.cpp | 2163 ---------------- vm/port/src/lil/ipf/pim/m2n_ipf.cpp | 514 ---- vm/port/src/lil/ipf/pim/m2n_ipf_internal.h | 173 - vm/port/src/lil/ipf/pim/stack_iterator_ipf.cpp | 624 ----- vm/port/src/lil/lil.cpp | 2708 -------------------- vm/port/src/lil/lil_code_generator.cpp | 84 - vm/port/src/lil/lil_code_generator_utils.cpp | 147 - vm/port/src/lil/pim/m2n.cpp | 34 vm/port/src/lil/pim/stack_iterator.cpp | 66 vm/vmcore/include/lil.h | 618 +++++ vm/vmcore/include/lil_code_generator.h | 70 + vm/vmcore/include/lil_code_generator_utils.h | 82 + .../lil/em64t/include/lil_code_generator_em64t.h | 742 ++++++ .../src/lil/em64t/lil_code_generator_em64t.cpp | 1730 +++++++++++++++ vm/vmcore/src/lil/em64t/m2n_em64t.cpp | 437 +++ vm/vmcore/src/lil/em64t/m2n_em64t_internal.h | 133 + vm/vmcore/src/lil/em64t/stack_iterator_em64t.cpp | 543 ++++ .../src/lil/ia32/include/lil_code_generator_ia32.h | 38 vm/vmcore/src/lil/ia32/lil_code_generator_ia32.cpp | 1489 +++++++++++ vm/vmcore/src/lil/ia32/m2n_ia32.cpp | 329 ++ vm/vmcore/src/lil/ia32/m2n_ia32_internal.h | 108 + vm/vmcore/src/lil/ia32/stack_iterator_ia32.cpp | 541 ++++ .../src/lil/ipf/include/lil_code_generator_ipf.h | 43 vm/vmcore/src/lil/ipf/lil_code_generator_ipf.cpp | 2163 ++++++++++++++++ vm/vmcore/src/lil/ipf/m2n_ipf.cpp | 514 ++++ vm/vmcore/src/lil/ipf/m2n_ipf_internal.h | 173 + vm/vmcore/src/lil/ipf/stack_iterator_ipf.cpp | 624 +++++ vm/vmcore/src/lil/lil.cpp | 2708 ++++++++++++++++++++ vm/vmcore/src/lil/lil_code_generator.cpp | 84 + vm/vmcore/src/lil/lil_code_generator_utils.cpp | 147 + vm/vmcore/src/lil/stack_iterator/m2n.cpp | 34 .../src/lil/stack_iterator/stack_iterator.cpp | 66 + 46 files changed, 13416 insertions(+), 13416 deletions(-) delete mode 100644 vm/port/include/lil.h delete mode 100644 vm/port/include/lil_code_generator.h delete mode 100644 vm/port/include/lil_code_generator_utils.h delete mode 100644 vm/port/src/lil/em64t/pim/include/lil_code_generator_em64t.h delete mode 100644 vm/port/src/lil/em64t/pim/lil_code_generator_em64t.cpp delete mode 100644 vm/port/src/lil/em64t/pim/m2n_em64t.cpp delete mode 100644 vm/port/src/lil/em64t/pim/m2n_em64t_internal.h delete mode 100644 vm/port/src/lil/em64t/pim/stack_iterator_em64t.cpp delete mode 100644 vm/port/src/lil/ia32/pim/include/lil_code_generator_ia32.h delete mode 100644 vm/port/src/lil/ia32/pim/lil_code_generator_ia32.cpp delete mode 100644 vm/port/src/lil/ia32/pim/m2n_ia32.cpp delete mode 100644 vm/port/src/lil/ia32/pim/m2n_ia32_internal.h delete mode 100644 vm/port/src/lil/ia32/pim/stack_iterator_ia32.cpp delete mode 100644 vm/port/src/lil/ipf/pim/include/lil_code_generator_ipf.h delete mode 100644 vm/port/src/lil/ipf/pim/lil_code_generator_ipf.cpp delete mode 100644 vm/port/src/lil/ipf/pim/m2n_ipf.cpp delete mode 100644 vm/port/src/lil/ipf/pim/m2n_ipf_internal.h delete mode 100644 vm/port/src/lil/ipf/pim/stack_iterator_ipf.cpp delete mode 100644 vm/port/src/lil/lil.cpp delete mode 100644 vm/port/src/lil/lil_code_generator.cpp delete mode 100644 vm/port/src/lil/lil_code_generator_utils.cpp delete mode 100644 vm/port/src/lil/pim/m2n.cpp delete mode 100644 vm/port/src/lil/pim/stack_iterator.cpp create mode 100644 vm/vmcore/include/lil.h create mode 100644 vm/vmcore/include/lil_code_generator.h create mode 100644 vm/vmcore/include/lil_code_generator_utils.h create mode 100644 vm/vmcore/src/lil/em64t/include/lil_code_generator_em64t.h create mode 100644 vm/vmcore/src/lil/em64t/lil_code_generator_em64t.cpp create mode 100644 vm/vmcore/src/lil/em64t/m2n_em64t.cpp create mode 100644 vm/vmcore/src/lil/em64t/m2n_em64t_internal.h create mode 100644 vm/vmcore/src/lil/em64t/stack_iterator_em64t.cpp create mode 100644 vm/vmcore/src/lil/ia32/include/lil_code_generator_ia32.h create mode 100644 vm/vmcore/src/lil/ia32/lil_code_generator_ia32.cpp create mode 100644 vm/vmcore/src/lil/ia32/m2n_ia32.cpp create mode 100644 vm/vmcore/src/lil/ia32/m2n_ia32_internal.h create mode 100644 vm/vmcore/src/lil/ia32/stack_iterator_ia32.cpp create mode 100644 vm/vmcore/src/lil/ipf/include/lil_code_generator_ipf.h create mode 100644 vm/vmcore/src/lil/ipf/lil_code_generator_ipf.cpp create mode 100644 vm/vmcore/src/lil/ipf/m2n_ipf.cpp create mode 100644 vm/vmcore/src/lil/ipf/m2n_ipf_internal.h create mode 100644 vm/vmcore/src/lil/ipf/stack_iterator_ipf.cpp create mode 100644 vm/vmcore/src/lil/lil.cpp create mode 100644 vm/vmcore/src/lil/lil_code_generator.cpp create mode 100644 vm/vmcore/src/lil/lil_code_generator_utils.cpp create mode 100644 vm/vmcore/src/lil/stack_iterator/m2n.cpp create mode 100644 vm/vmcore/src/lil/stack_iterator/stack_iterator.cpp cb68bb40cbc39e17d88017224505cbe6fc23ff27 diff --git a/vm/port/include/lil.h b/vm/port/include/lil.h deleted file mode 100644 index 41e7d30..0000000 --- a/vm/port/include/lil.h +++ /dev/null @@ -1,618 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#ifndef _LIL_H_ -#define _LIL_H_ - -#include -#include "open/types.h" -#include "vm_core_types.h" -#include "tl/memory_pool.h" -#include "m2n.h" - -/* This file describes LIL - the architecture-independent language used to generate code stubs etc. - -Goals: -* Lightweight - i.e. small language, quality code generation possible with small simple algorithms -* Remove most architecture dependencies from code -* (Future) allow inlining of VM stubs into JITed code -* (Future) allow inlining of GC or Thread/Sync code via LIL -* (Possibly future) be the lowest ir of a simple JIT - -Design constaints: -* Keep language syntax LL(1) so that parsing can be done with a recursive descent parser -* Make single-pass register allocation possible -* Allow easy experimentation with calling conventions - -The basic unit of LIL is a code stub. It is intended to be the entry point of a function that can be -called by managed or native code, and executes in the presence of an activation frame. The frame consists -of up to six parts: inputs, standard places, an m2n frame, locals, outputs, and a return variable. Which -parts are present in the activation frame, and how big each part is, can vary across points in the code stub. - -A LIL code stub is an entry and a sequence of statements. The entry specifies the inputs and their types, the -number of standard places, the type of the return, and the calling convention (which determines where the inputs, -return address, and return goes, which registers are caller save vs callee save, and who pops the inputs; standard -places are determined by LIL and the platform, and are intended for trampolines). An entry can have an "arbitrary" -signature, meaning that the number and type of inputs and type of return is unknown (the stub must work for all -possibilities). After entry the declared number -of input variables are available, the declared number of standard places are available, there is no m2n frame, -there are no locals, there are no outputs, and there is no return variable. - -cs ::= entry i:cc:sig; ss -ss ::= s | s ss - -The statements are: - -s ::= :l; -| locals i; -| std_places i; -| alloc v,i; -| v=uop o; -| v=o op o; -| v=o; -| v=ts; -| handles=o; -| ld v,addr(,zx|,sx)?; -| st addr,o; -| inc addr; -| cas addr=o,o,l; -| j l; -| jc cond,l; -| out cc:sig; -| in2out cc:RT; -| calli o; -| ret; -| push_m2n i oh; -| m2n_save_all; -| pop_m2n; -| print str, o; - -uop ::= neg | not | sx1 | sx2 | sx4 | zx1 | zx2 | zx4 | ... -op ::= + | - | * | & | << | ... - -addr ::= [ov osv i :T ackrel] -ov ::= | v+ -osv ::= | 1*v+ | 2*v+ | 4*v+ | 8*v+ -acqrel ::= | ,acquire | ,release - -cond ::= v=0 | v!=0 | o=o | o!=o | o-1 - int id; - // private constructor; create BBs by calling init_fg() - LilBb(LilCodeStub *_cs, LilInstruction *_start, - LilInstructionContext* ctxt_at_start); - - // private operator new; create BBs using new_bb() only - void* operator new(size_t sz, tl::MemoryPool& m); - //MVM - //add empty destructor to avoid warning - void operator delete(void * UNREF obj, tl::MemoryPool& UNREF m){ - //do nothing - }; - -public: - - // the only way to create new BBs; create a whole FG for a stub - VMEXPORT static void init_fg(LilCodeStub*); - - // sets the last instruction of the BB - VMEXPORT void set_last(LilInstruction *i); - - // gets the first instruction - VMEXPORT LilInstruction* get_first(); - // gets the last instruction - VMEXPORT LilInstruction* get_last(); - - // get the context right before the start of the BB - VMEXPORT LilInstructionContext* get_context(); - - // does this bb contain instruction i? - VMEXPORT bool contains(LilInstruction *i); - - // get the label of this BB; NULL if no label exists - VMEXPORT LilLabel get_label(); - - // set a fallthrough successor to this bb - VMEXPORT void set_fallthru(LilBb *); - // set a branch-target successor to this bb - VMEXPORT void set_branch_target(LilBb *); - - // get the fallthrough and branch target successors; - // either of them can be NULL if they don't exist - VMEXPORT LilBb* get_fallthru(); - VMEXPORT LilBb* get_branch_target(); - - // gets the i'th predecessor (NULL if i >= num_pred) - VMEXPORT LilBb *get_pred(unsigned i); - - // gets the next BB in the list - VMEXPORT LilBb *get_next(); - - // returns whether this BB ends in a return instruction - // (tailcall implies return!) - VMEXPORT bool is_ret(); - - // the id of this BB - VMEXPORT int get_id(); - - // true if this BB contains calls (which means it may throw exceptions) - VMEXPORT bool does_calls(); - - // true if this BB ends with a call.noret (which means it is probably cold code) - VMEXPORT bool does_call_noret(); - - // find a BB with the specified label; NULL if no such bb exists - VMEXPORT static LilBb* get_by_label(LilCodeStub *cs, LilLabel l); - - // find a BB which contains the specified instruction; NULL if no such BB exists - VMEXPORT static LilBb* get_by_instruction(LilCodeStub *cs, LilInstruction *i); - - // print a BB to a stream (does not print the BB's instructions) - VMEXPORT void print(FILE* out); -}; - - - -// Iterate over the instructions in a code stub -class LilInstructionIterator { - public: - // Create an iterator from a code stub, if track_contexts then track the context prior to each instruction - VMEXPORT LilInstructionIterator(LilCodeStub*, bool track_contexts); - - // Create an iterator for a BB in a code stub. - // NOTE: track_contexts is currently NOT supported for this type of iterator - VMEXPORT LilInstructionIterator(LilCodeStub*, LilBb*, bool track_contexts); - - // Is iterator past the end of the instructions? - VMEXPORT bool at_end(); - // Return current instruction - VMEXPORT LilInstruction* get_current(); - // Return context prior to current instruction, must have created iterator to track contexts - VMEXPORT LilInstructionContext* get_context(); - // Goto the next instruction - VMEXPORT void goto_next(); - - private: - LilCodeStub* cs; - LilBb *bb; // NULL, unless this is an iterator for a BB - LilInstruction* cur; - bool track_ctxt; - LilInstructionContext* ctxt; -}; - - - -// Visitor pattern for instructions -class LilInstructionVisitor { - protected: - VMEXPORT LilInstructionVisitor(); - - public: - virtual void label(LilLabel) = 0; - virtual void locals(unsigned) = 0; - virtual void std_places(unsigned) = 0; - virtual void alloc(LilVariable*, unsigned) = 0; - 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, - LilOperand* cmp, LilOperand* src, LilLabel) = 0; - virtual void j(LilLabel) = 0; - virtual void jc(LilPredicate, LilOperand*, LilOperand*, LilLabel) = 0; - virtual void out(LilSig*) = 0; - virtual void in2out(LilSig*) = 0; - virtual void call(LilOperand*, LilCallKind) = 0; - virtual void ret() = 0; - 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 print(char *, LilOperand *) = 0; -}; - - -// LilInstructionVisitor_Default provides empty implementations for the -// abstract functions in LilInstructionVisitor. It is a convenient base -// class for scanners that need to act only on a few instructions. -class LilInstructionVisitor_Default : public LilInstructionVisitor { - protected: - LilInstructionVisitor_Default(): - LilInstructionVisitor() {} - - public: - void label(LilLabel) {} - void locals(unsigned) {} - void std_places(unsigned) {} - void alloc(LilVariable*, unsigned) {} - 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, - LilOperand* UNREF cmp, LilOperand* UNREF src, LilLabel) {} - void j(LilLabel) {} - void jc(LilPredicate, LilOperand*, LilOperand*, LilLabel) {} - void out(LilSig*) {} - void in2out(LilSig*) {} - void call(LilOperand*, LilCallKind) {} - void ret() {} - void push_m2n(Method_Handle UNREF method, frame_type UNREF current_frame_type, bool UNREF handles) {} - void m2n_save_all() {} - void pop_m2n() {} - void print(char *, LilOperand *) {} -}; - - -// Visit instruction using a visitor -VMEXPORT void lil_visit_instruction(LilInstruction*, LilInstructionVisitor*); - - -// Return variable's kind -VMEXPORT LilVariableKind lil_variable_get_kind(LilVariable* v); -// Return variable's index -VMEXPORT unsigned lil_variable_get_index(LilVariable* v); -// Are two variables the same -VMEXPORT bool lil_variable_is_equal(LilVariable* v1, LilVariable* v2); - -// Is operand an immediate? -VMEXPORT bool lil_operand_is_immed(LilOperand* o); -// If operand is an immediate return the immediate value -VMEXPORT POINTER_SIZE_INT lil_operand_get_immed(LilOperand* o); -// If operand is not an immediate return the variable it represents -VMEXPORT LilVariable* lil_operand_get_variable(LilOperand* o); - -// Return a signature's calling convention -VMEXPORT LilCc lil_sig_get_cc(LilSig* sig); -// Return whether this signature is arbitrary -VMEXPORT bool lil_sig_is_arbitrary(LilSig *sig); -// Return the number of arguments for a signature (0 if arbitrary) -VMEXPORT unsigned lil_sig_get_num_args(LilSig* sig); -// Return the num-th argument type of a signature (zero based) (undefined if signature is arbitrary) -VMEXPORT LilType lil_sig_get_arg_type(LilSig* sig, unsigned num); -// Return the return type of a signature -VMEXPORT LilType lil_sig_get_ret_type(LilSig* sig); - -// Is an operation binary? -VMEXPORT bool lil_operation_is_binary(enum LilOperation op); -// Is a predicate binary? -VMEXPORT bool lil_predicate_is_binary(enum LilPredicate c); -// Is a predicate signed? -VMEXPORT bool lil_predicate_is_signed(LilPredicate c); - -//*** Printers - -// Print a type to a stream -VMEXPORT void lil_print_type(FILE*, LilType); -// Print a signature to a stream -VMEXPORT void lil_print_sig(FILE*, LilSig*); -// Print an instruction to a stream -VMEXPORT void lil_print_instruction(FILE*, LilInstruction* i); -// Print a code stub to a stream -VMEXPORT void lil_print_code_stub(FILE*, LilCodeStub* cs); -// Print just the entry of a code stub to a stream -VMEXPORT void lil_print_entry(FILE*, LilCodeStub* cs); - -//*** Freers - -// Return all resources associated with a code stub -// Post: code stub no longer usable -VMEXPORT void lil_free_code_stub(LilCodeStub* cs); - -//*** Function pointer utilities - -VMEXPORT GenericFunctionPointer lil_npc_to_fp(NativeCodePtr); - -#endif // _LIL_H_ diff --git a/vm/port/include/lil_code_generator.h b/vm/port/include/lil_code_generator.h deleted file mode 100644 index e0a6e78..0000000 --- a/vm/port/include/lil_code_generator.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#ifndef _LIL_CODE_GENERATOR_H_ -#define _LIL_CODE_GENERATOR_H_ - -#include "lil.h" -#include "vm_core_types.h" -#include "environment.h" -#include "mem_alloc.h" - -// This is an abstract base case for LIL code generators -// Subclasses compile LIL into native code for a particular -// architecture - -// Note that this is a compiler algorithm abstraction not -// a compilation process abstraction. Subclasses should -// not store state about any particular compilation, only -// options that configure the compilation. - -class LilCodeGenerator { -public: - // Return the code generator for the current platform - static LilCodeGenerator* get_platform(); - - // Compile LIL code stub to native code and return it - // The stub_name is for vtune support - // Dump an ascii version of the compiled stub to stdout if dump_stub - // If cs_stats is nonnull add the number of bytes of the compiled code to *cs_stats - NativeCodePtr compile(LilCodeStub* cs, PoolManager* code_pool = - VM_Global_State::loader_env->GlobalCodeMemoryManager); - -protected: - LilCodeGenerator(); - - // allocates a chunk of memory for a LIL stub; the user-provided function - // compile_main() should call this function instead of allocating memory - // directly. - NativeCodePtr allocate_memory(size_t, PoolManager*); - - // generates compiled code for a LIL stub, and returns its address. The - // size of the compiled stub is placed in stub_size. Called by the - // compile() function to do most of the work. - // - // Each subclass of LilCodeGenerator should provide a platform-dependent - // implementation of compile_main(). The memory area that holds the - // compiled code should be allocated by calling allocate_memory(). - virtual NativeCodePtr compile_main(LilCodeStub* cs, size_t* stub_size, PoolManager* code_pool) = 0; -}; - -#endif // _LIL_CODE_GENERATOR_H_ diff --git a/vm/port/include/lil_code_generator_utils.h b/vm/port/include/lil_code_generator_utils.h deleted file mode 100644 index b1cbe65..0000000 --- a/vm/port/include/lil_code_generator_utils.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#ifndef _LIL_CODE_GENERATOR_UTILS_H_ -#define _LIL_CODE_GENERATOR_UTILS_H_ - -#include "lil.h" -#include "vm_core_types.h" - -// This module provides some common utilities for generating native code from LIL code stubs - -//*** The following is for managing label to address translation and back patching - -// Patch types have to be the union of the various cases of the various architectures, -// so unfortunately are somewhat platform specific. -typedef enum LilCguPatchType { - LPT_Rel8, LPT_Rel32, LPT_Abs32 -} LilCguPatchType; - -typedef struct LilCguPatch { - char * addr; - LilCguPatchType type; - LilCguPatch * next; -} LilCguPatch; - -typedef struct LilCguLabelAddress { - LilLabel l; - char * addr; - bool base_relative; - LilCguPatch * patches; - LilCguLabelAddress * next; -}LilCguLabelAddress; - - -// This class mantains a mapping between labels and addresses, and ensures the references to -// labels eventually point to those labels. -// Clients should call define_label to set a label's address, and patch_to_label to ensure references -// correct point to a label; the class will make sure that a reference points to its label once both -// functions have been called. -class LilCguLabelAddresses { -public: - // Create a label to address mapping with back patching - LilCguLabelAddresses(tl::MemoryPool*, char * b); - // - void change_base(char * new_base); - // Label l should have addres code - void define_label(LilLabel l, void * code, bool base_relative); - // The contents of address patch_address should point to label l according to patch_type - void add_patch_to_label(LilLabel l, void * patch_address, LilCguPatchType patch_type); - -private: - void add_new_label_adress(LilLabel l, void * code, bool base_relative); - // Apply all patches associated with the current label - void apply_patches(LilCguLabelAddress * label_adress); - // Actually apply a patch - void apply_patch(LilCguLabelAddress * label_adress, LilCguPatch * patch); - - LilCguLabelAddress * first; - tl::MemoryPool * my_mem; - char * base; -}; - -#endif // _LIL_CODE_GENERATOR_UTILS_H_ diff --git a/vm/port/src/lil/em64t/pim/include/lil_code_generator_em64t.h b/vm/port/src/lil/em64t/pim/include/lil_code_generator_em64t.h deleted file mode 100644 index 7b676f5..0000000 --- a/vm/port/src/lil/em64t/pim/include/lil_code_generator_em64t.h +++ /dev/null @@ -1,742 +0,0 @@ -/* - * 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. - */ - -/** - * @author Evgueni Brevnov - * @version $Revision$ - */ - -/** - * Stack frame layout created by LIL CG on EM64T - * - * |--------------------------| - * | Extra inputs | - * |--------------------------| <--- previouse stack frame bottom - * | Return ip | - * |==========================| <--- current stack frame top - * | M2N frame | callee saved | - * |--------------------------| - * | GR inputs save area | - * |--------------------------| - * | FR inputs save area | - * |--------------------------| - * | Dynamicly allocated area | - * | (includes stack padding) | - * |--------------------------| - * | Extra outputs | - * |==========================| <--- current stack frame bottom - * - * Note: - * EM64T architecture requires stack frame bottom address - * to be aligned on 16 byte boundary (rsp % 16 == 0) - * - * Register usage: - * r12-r15 are used for lil local variables (l0-l3) - * r10-r11 are used for lil standard places (sp0-sp1) - */ - -#ifndef _LIL_CODE_GENERATOR_EM64T_ -#define _LIL_CODE_GENERATOR_EM64T_ - -#include "lil.h" -#include "lil_code_generator.h" -#include "encoder.h" - - -/** - * rounds up an integer value to the closest multiple of 8 - */ -inline unsigned align_8(unsigned n) { - return (n + 0x7) & ~0x7; -} - -/** - * rounds up an integer value to the closest multiple of 16 - */ -inline unsigned align_16(unsigned n) { - return (n + 0xF) & ~0xF; -} - -/** -* an enum indicating a variable's location: in a register class or on the -* stack (no LIL variable is ever on the heap!) -*/ -enum LcgEM64TLocKind { - LLK_Gr, // 64-bit general purpose registers - LLK_Fr, // 128-bit xmm registers - LLK_GStk, // memory stack which holds gp value - LLK_FStk // memory stack which holds fp value -}; - -class LcgEM64TContext: public LilInstructionVisitor { - -public: -#ifdef _WIN64 - // maximum number of GR reserved for returns - static const unsigned MAX_GR_RETURNS = 1; - // maximum number of GR reserved for outputs/inputs - static const unsigned MAX_GR_OUTPUTS = 4; - // maximum number of locals that can be placed in GR - static const unsigned MAX_GR_LOCALS = 8; - // maximum number of stand places - static const unsigned MAX_STD_PLACES = 2; - - // maximum number of FR reserved for returns - static const unsigned MAX_FR_RETURNS = 1; - // maximum number of FR reserved for outputs/inputs - static const unsigned MAX_FR_OUTPUTS = 4; - // maximum number of temporary XMM registers - static const unsigned MAX_FR_LOCALS = 10; - // maximum number of temporary XMM registers - static const unsigned MAX_FR_TEMPORARY = 2; -#else - // maximum number of GR reserved for returns - static const unsigned MAX_GR_RETURNS = 2; - // maximum number of GR reserved for outputs/inputs - static const unsigned MAX_GR_OUTPUTS = 6; - // maximum number of locals that can be placed in GR - static const unsigned MAX_GR_LOCALS = 6; - // maximum number of stand places - static const unsigned MAX_STD_PLACES = 2; - - // maximum number of FR reserved for returns - static const unsigned MAX_FR_RETURNS = 2; - // maximum number of FR reserved for outputs/inputs - static const unsigned MAX_FR_OUTPUTS = 8; - // maximum number of temporary XMM registers - static const unsigned MAX_FR_LOCALS = 8; - // maximum number of temporary XMM registers - static const unsigned MAX_FR_TEMPORARY = 0; -#endif - - // size of GR in bytes - // TODO: Think about using GR_STACK_SIZE - static const unsigned GR_SIZE = 8; - // size of FR in bytes - // TODO: Think about using FR_STACK_SIZE - static const unsigned FR_SIZE = 8; - - // offsets for the REG_MAP array - static const unsigned STD_PLACES_OFFSET = 0; - static const unsigned GR_LOCALS_OFFSET = STD_PLACES_OFFSET + MAX_STD_PLACES; - static const unsigned GR_OUTPUTS_OFFSET = GR_LOCALS_OFFSET + MAX_GR_LOCALS; - static const unsigned GR_RETURNS_OFFSET = GR_OUTPUTS_OFFSET + MAX_GR_OUTPUTS; - static const unsigned RSP_OFFSET = GR_RETURNS_OFFSET + MAX_GR_RETURNS; - - // offsets for the XMM_REG_MAP array - static const unsigned FR_OUTPUTS_OFFSET = 0; - static const unsigned FR_RETURNS_OFFSET = FR_OUTPUTS_OFFSET + MAX_FR_OUTPUTS; - static const unsigned FR_TEMPORARY_OFFSET = FR_RETURNS_OFFSET + MAX_FR_RETURNS; - static const unsigned FR_LOCALS_OFFSET = FR_TEMPORARY_OFFSET + MAX_FR_TEMPORARY; - -private: - - LilCodeStub * cs; // the code stub - tl::MemoryPool & mem; // a memory manager - LilInstructionIterator iter; // instruction iterator - - unsigned n_inputs; // total number of inputs - unsigned n_gr_inputs; // total number of GRs reserved for inputs -#ifdef _WIN64 - // Windows x64 has 4 slots for both integer and float inputs -#define n_fr_inputs n_gr_inputs -#else - unsigned n_fr_inputs; // total number of FRs reserved for inputs -#endif - - unsigned n_outputs; // total number of outputs - unsigned n_gr_outputs; // total number of GRs reserved for outputs -#ifdef _WIN64 - // Windows x64 has 4 slots for both integer and float inputs -#define n_fr_outputs n_gr_outputs -#else - unsigned n_fr_outputs; // total number of FRs reserved for outputs -#endif - - /// Number of GR registers currently allocated for temporary needs. - unsigned m_tmp_grs_used; - /// Number of XMM registers currently allocated for temporary needs. - unsigned m_tmp_xmms_used; - - unsigned stk_m2n_size; // size reserved for the m2n frame - unsigned stk_input_gr_save_size; // size reserved for saving GR inputs - unsigned stk_input_fr_save_size; // size reserved for saving FR inputs - unsigned stk_alloc_size; // size of allocatable memory on the stack - unsigned stk_output_size; // bytes needed for outgoing params on the stack - unsigned stk_size; // total size of the memory stack frame (in bytes) - - Method_Handle m2n_method; // method handle of the m2n frame - frame_type m2n_frame_type; // m2n frame type - - bool m2n_handles; // true if m2n contains local handles - bool does_normal_calls; // true if the stub contains "normal" calls - bool does_tail_calls; // true if the stub contains tail calls - bool calls_unmanaged_code; // true if the stub calls calls code with a calling convention other than managed - bool has_m2n; // true if the stub contains push_m2n/pop_m2n instructions - bool save_inputs; // true if inputs are accessed after a normal call - -public: - - - LcgEM64TContext(LilCodeStub * stub, tl::MemoryPool & m); - -#ifdef _WIN64 - /** - * returns general purpose register associated with given index - * this association is used across whole lil code generator - */ - static const R_Opnd & get_reg_from_map(unsigned index) { - static const R_Opnd * REG_MAP[] = { - // std places (scratched) - &r10_opnd, &r11_opnd, - // GR locals (calee-saved) - &r12_opnd, &r13_opnd, &r14_opnd, &r15_opnd, - &rdi_opnd, &rsi_opnd, &rbp_opnd, &rbx_opnd, - // gr inputs/outputs (scratched) - &rcx_opnd, &rdx_opnd, &r8_opnd, &r9_opnd, - // gr returns (scratched) - &rax_opnd, - // rsp - &rsp_opnd - }; - return *REG_MAP[index]; - } - - /** - * returns xmm register associated with given index - * this association is used across whole lil code generator - */ - static const XMM_Opnd & get_xmm_reg_from_map(unsigned index) { - static const XMM_Opnd * XMM_REG_MAP[] = { - // fr inputs/outputs (scratched) - &xmm0_opnd, &xmm1_opnd, &xmm2_opnd, &xmm3_opnd, - // fr returns (scratched) - &xmm0_opnd, - // temporary xmm registers (scratched) - &xmm4_opnd, &xmm5_opnd, - // locals xmm registers - &xmm6_opnd, &xmm7_opnd, &xmm8_opnd, &xmm9_opnd, - &xmm10_opnd, &xmm11_opnd, &xmm12_opnd, &xmm13_opnd, - &xmm14_opnd, &xmm15_opnd - }; - return *XMM_REG_MAP[index]; - } - - /** - * an association between register number and index in the REG_MAP array - */ - static unsigned get_index_in_map(const Reg_No reg) { - static const unsigned INDEX_MAP[] = { - // rax_reg, rbx_reg, rcx_reg, - GR_RETURNS_OFFSET, GR_LOCALS_OFFSET + 7, GR_OUTPUTS_OFFSET + 0, - // rdx_reg, rdi_reg, rsi_reg, - GR_OUTPUTS_OFFSET + 1, GR_LOCALS_OFFSET + 4, GR_LOCALS_OFFSET + 5, - // rsp_reg, rbp_reg, r8_reg, - RSP_OFFSET, GR_LOCALS_OFFSET + 6, GR_OUTPUTS_OFFSET + 2, - // r9_reg, r10_reg, r11_reg, - GR_OUTPUTS_OFFSET + 3, STD_PLACES_OFFSET, STD_PLACES_OFFSET + 1, - // r12_reg, r13_reg, r14_reg, - GR_LOCALS_OFFSET, GR_LOCALS_OFFSET + 1, GR_LOCALS_OFFSET + 2, - // r15_reg, xmm0_reg, xmm1_reg, - GR_LOCALS_OFFSET + 3, FR_OUTPUTS_OFFSET, FR_OUTPUTS_OFFSET + 1, - // xmm2_reg, xmm3_reg, xmm4_reg, - FR_OUTPUTS_OFFSET + 2, FR_OUTPUTS_OFFSET + 3, FR_TEMPORARY_OFFSET, - // xmm5_reg, xmm6_reg, xmm7_reg, - FR_TEMPORARY_OFFSET + 1, FR_LOCALS_OFFSET, FR_LOCALS_OFFSET + 1, - // xmm8_reg, xmm9_reg, xmm10_reg, - FR_LOCALS_OFFSET + 2, FR_LOCALS_OFFSET + 3, FR_LOCALS_OFFSET + 4, - // xmm11_reg, xmm12_reg, xmm13_reg, - FR_LOCALS_OFFSET + 5, FR_LOCALS_OFFSET + 6, FR_LOCALS_OFFSET + 7, - // xmm14_reg, xmm15_reg - FR_LOCALS_OFFSET + 8, FR_LOCALS_OFFSET + 9 - }; - return INDEX_MAP[reg]; - } -#else - static const R_Opnd & get_reg_from_map(unsigned index) { - static const R_Opnd * REG_MAP[] = { - // std places (scratched) - &r10_opnd, &r11_opnd, - // GR locals (calee-saved) - &r12_opnd, &r13_opnd, &r14_opnd, &r15_opnd, &rbp_opnd, &rbx_opnd, - // gr inputs/outputs (scratched) - &rdi_opnd, &rsi_opnd, &rdx_opnd, &rcx_opnd, &r8_opnd, &r9_opnd, - // gr returns (scratched) - &rax_opnd, &rdx_opnd, - // rsp - &rsp_opnd - }; - return *REG_MAP[index]; - } - - /** - * returns xmm register associated with given index - * this association is used across whole lil code generator - */ - static const XMM_Opnd & get_xmm_reg_from_map(unsigned index) { - static const XMM_Opnd * XMM_REG_MAP[] = { - // fr inputs/outputs (scratched) - &xmm0_opnd, &xmm1_opnd, &xmm2_opnd, &xmm3_opnd, - &xmm4_opnd, &xmm5_opnd, &xmm6_opnd, &xmm7_opnd, - // fr returns (scratched) - &xmm0_opnd, &xmm1_opnd, - // temporary xmm registers (scratched) - &xmm8_opnd, &xmm9_opnd, &xmm10_opnd, &xmm11_opnd, - &xmm12_opnd, &xmm13_opnd, &xmm14_opnd, &xmm15_opnd - }; - return *XMM_REG_MAP[index]; - } - - /** - * an association between register number and index in the REG_MAP array - */ - static unsigned get_index_in_map(const Reg_No reg) { - static const unsigned INDEX_MAP[] = { - // rax_reg, rbx_reg, rcx_reg, - GR_RETURNS_OFFSET, GR_LOCALS_OFFSET + 5, GR_OUTPUTS_OFFSET + 3, - // rdx_reg, rdi_reg, rsi_reg, - GR_OUTPUTS_OFFSET + 2, GR_OUTPUTS_OFFSET, GR_OUTPUTS_OFFSET + 1, - // rsp_reg, rbp_reg, r8_reg, - RSP_OFFSET, GR_LOCALS_OFFSET + 4, GR_OUTPUTS_OFFSET + 4, - // r9_reg, r10_reg, r11_reg, - GR_OUTPUTS_OFFSET + 5, STD_PLACES_OFFSET, STD_PLACES_OFFSET + 1, - // r12_reg, r13_reg, r14_reg, - GR_LOCALS_OFFSET, GR_LOCALS_OFFSET + 1, GR_LOCALS_OFFSET + 2, - // r15_reg, xmm0_reg, xmm1_reg, - GR_LOCALS_OFFSET + 3, FR_OUTPUTS_OFFSET, FR_OUTPUTS_OFFSET + 1, - // xmm2_reg, xmm3_reg, xmm4_reg, - FR_OUTPUTS_OFFSET + 2, FR_OUTPUTS_OFFSET + 3, FR_OUTPUTS_OFFSET + 4, - // xmm5_reg, xmm6_reg, x mm7_reg, - FR_OUTPUTS_OFFSET + 5, FR_OUTPUTS_OFFSET + 6, FR_OUTPUTS_OFFSET + 7, - // xmm8_reg, xmm9_reg, xmm10_reg, - FR_TEMPORARY_OFFSET, FR_TEMPORARY_OFFSET + 1, FR_TEMPORARY_OFFSET + 2, - // xmm11_reg, xmm12_reg, xmm13_reg, - FR_TEMPORARY_OFFSET + 3, FR_TEMPORARY_OFFSET + 4, FR_TEMPORARY_OFFSET + 5, - // xmm14_reg, xmm15_reg - FR_TEMPORARY_OFFSET + 6, FR_TEMPORARY_OFFSET + 7 - }; - return INDEX_MAP[reg]; - } -#endif - void * operator new(size_t sz, tl::MemoryPool & m) { - return m.alloc(sz); - } - - void operator delete (void * p, tl::MemoryPool & m) {} - - /** - * returns the number of incoming arguments - */ - unsigned get_num_inputs() const { - return n_inputs; - } - - /** - * returns the number of incoming arguments stored in GRs - */ - unsigned get_num_gr_inputs() const { - assert(n_gr_inputs <= MAX_GR_OUTPUTS); - return n_gr_inputs; - } - - /** - * Returns *reference* to the number of temporary GR registers currently allocated. - */ - unsigned& get_tmp_grs_used(void) { - return m_tmp_grs_used; - } - - /** - * Returns *reference* to the number of temporary XMM registers currently allocated. - */ - unsigned& get_tmp_xmms_used(void) { - return m_tmp_xmms_used; - } - - /** - * returns the number of incoming arguments stored in FRs - */ - unsigned get_num_fr_inputs() const { - assert(n_fr_inputs <= MAX_FR_OUTPUTS); - return n_fr_inputs; - } - - unsigned get_num_outputs() const { - return n_outputs; - } - - unsigned get_num_gr_outputs() const { - assert(n_gr_outputs <= MAX_GR_OUTPUTS); - return n_gr_outputs; - } - - unsigned get_num_fr_outputs() const { - assert(n_fr_outputs <= MAX_FR_OUTPUTS); - return n_fr_outputs; - } - - /** - * returns true if m2n is required on the activation frame - */ - bool has_m2n_frame() const { - return has_m2n; - } - - /** - * returns true if we need to reserve space on the stack to save inputs - */ - bool must_save_inputs() const { - return save_inputs; - } - - /** - * true if type represents floating point value - */ - bool is_fp_type(LilType t) const { - return t == LT_F4 || t == LT_F8; - } - - /** - * method which corresponds to the m2n frame - */ - Method_Handle get_m2n_method() const { - return m2n_method; - } - - /** - * m2n frame type - */ - frame_type get_m2n_frame_type() const { - return m2n_frame_type; - } - - /** - * returns true if m2n contains local handles - */ - bool m2n_has_handles() const { - return m2n_handles; - } - - // returns the offset of the start of the m2n frame - unsigned get_m2n_offset() const { - return get_input_gr_save_offset() + stk_input_gr_save_size; - } - - // returns the offset of the start of the gr input register save space - unsigned get_input_gr_save_offset() const { - return get_input_fr_save_offset() + stk_input_fr_save_size; - } - - // returns the offset of the start of the fr input register save space - unsigned get_input_fr_save_offset() const { - return get_alloc_start_offset() + stk_alloc_size; - } - - // returns the offset of the first "allocatable" byte - unsigned get_alloc_start_offset() const { - return get_output_offset() + stk_output_size; - } - - // returns the offset of the start of the m2n frame - unsigned get_output_offset() const { - return 0; - } - - // size reserved for saving GR inputs - unsigned get_stk_input_gr_save_size() const { - return stk_input_gr_save_size; - } - - // size reserved for saving FR inputs - unsigned get_stk_input_fr_save_size() const { - return stk_input_fr_save_size; - } - - // size of allocatable memory on the stack - unsigned get_stk_alloc_size() const { - return stk_alloc_size; - } - - // size reserved for the m2n frame - unsigned get_stk_m2n_size() const { - return stk_m2n_size; - } - - // bytes needed for outgoing params on the stack - unsigned get_stk_output_size() const { - return stk_output_size; - } - - // returns the size of the stack frame - unsigned get_stk_size() const { - return stk_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; - 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) { - assert(num <= MAX_GR_LOCALS); - } - - void std_places(unsigned num) { - assert(num <= MAX_STD_PLACES); - } - - 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) { - // make sure there is enough space for this command's outputs - unsigned outs_cnt = lil_sig_get_num_args(sig); - n_outputs = outs_cnt > n_outputs ? outs_cnt : n_outputs; - - // reserve enough GR & FR outputs - unsigned gp_out_cnt = 0; -#ifdef _WIN64 -# define fp_out_cnt gp_out_cnt -#else - unsigned fp_out_cnt = 0; -#endif - for (unsigned i = 0; i < lil_sig_get_num_args(sig); i++) { - LilType t = lil_sig_get_arg_type(sig, i); - if (is_fp_type(t)) { - fp_out_cnt++; - } else { - gp_out_cnt++; - } - } - - if (n_gr_outputs < gp_out_cnt) { - if (gp_out_cnt <= MAX_GR_OUTPUTS) { - n_gr_outputs = gp_out_cnt; - } else { - n_gr_outputs = MAX_GR_OUTPUTS; - stk_output_size += (gp_out_cnt - n_gr_outputs) * GR_SIZE; - } - } - - if (n_fr_outputs < fp_out_cnt) { - if (fp_out_cnt <= MAX_FR_OUTPUTS) { - n_fr_outputs = fp_out_cnt; - } else { - n_fr_outputs = MAX_FR_OUTPUTS; - stk_output_size += (fp_out_cnt - n_fr_outputs) * FR_SIZE; - } - } - } - - 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() { - // nothing to do - } - - void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) { - m2n_method = method; - m2n_frame_type = current_frame_type; - m2n_handles = 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 print(char *, LilOperand *) { - } -}; - -/** -* keeps location of a LIL variable -*/ -class LcgEM64TLoc { - -public: - LcgEM64TLocKind kind; - int64 addr; // register number or SP-relative offset - - LcgEM64TLoc(LcgEM64TLocKind k, int64 a): kind(k), addr(a) {} - - bool operator==(const LcgEM64TLoc & loc) const { - return (kind == loc.kind && addr == loc.addr); - } - - bool operator!=(const LcgEM64TLoc & loc) { - 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: - LcgEM64TLoc(LcgEM64TLoc &); // disable copying - LcgEM64TLoc & operator=(LcgEM64TLoc &); // disable copying -}; - -class LilCodeGeneratorEM64T : public LilCodeGenerator { - - public: - LilCodeGeneratorEM64T(); - - protected: - NativeCodePtr compile_main(LilCodeStub* , size_t*, PoolManager*); -}; - -#endif // _LIL_CODE_GENERATOR_EM64T_ diff --git a/vm/port/src/lil/em64t/pim/lil_code_generator_em64t.cpp b/vm/port/src/lil/em64t/pim/lil_code_generator_em64t.cpp deleted file mode 100644 index c211136..0000000 --- a/vm/port/src/lil/em64t/pim/lil_code_generator_em64t.cpp +++ /dev/null @@ -1,1730 +0,0 @@ -/* - * 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. - */ -/** - * @author Evgueni Brevnov - * @version $Revision: 1.1.2.2.4.3 $ - */ - -#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_em64t.h" -#include "lil_code_generator_utils.h" -#include "m2n.h" -#include "m2n_em64t_internal.h" - -#ifndef NDEBUG -#include "dump.h" -#endif - -LcgEM64TContext::LcgEM64TContext(LilCodeStub * stub, tl::MemoryPool & m): - cs(stub), mem(m), iter(cs, true) { - - /* 1) PRE-INITIALIZATION */ - n_inputs = 0; - n_gr_inputs = 0; - n_fr_inputs = 0; - - n_outputs = 0; - n_gr_outputs = 0; - n_fr_outputs = 0; - - m_tmp_grs_used = 0; - m_tmp_xmms_used = 0; - - stk_input_gr_save_size = 0; - stk_input_fr_save_size = 0; - stk_output_size = 0; - stk_alloc_size = 0; - - m2n_handles = false; - does_normal_calls = false; - does_tail_calls = false; - calls_unmanaged_code = false; - has_m2n = false; - save_inputs = false; - - /* 2) SCAN THE CODE STUB FOR THE INFORMATION */ - - while (!iter.at_end()) { - lil_visit_instruction(iter.get_current(), this); - iter.goto_next(); - } - - /* 3) INITIALIZE INPUTS ACCORDING TO THE ENTRY SIGNATURE */ - if (lil_sig_is_arbitrary(lil_cs_get_sig(stub))) { - n_gr_inputs = MAX_GR_OUTPUTS; - n_fr_inputs = MAX_FR_OUTPUTS; - n_inputs = n_gr_inputs + n_fr_outputs; - if (does_normal_calls) { - save_inputs = true; - } - } else { - n_inputs = lil_sig_get_num_args(lil_cs_get_sig(stub)); - // TODO: check if it makes sense to move to separate function - for (unsigned i = 0; i < n_inputs; i++) { - if (is_fp_type(lil_sig_get_arg_type(lil_cs_get_sig(stub), i))) { - n_fr_inputs++; - } else { - n_gr_inputs++; - } - } - n_gr_inputs = n_gr_inputs > MAX_GR_OUTPUTS ? MAX_GR_OUTPUTS : n_gr_inputs; - n_fr_inputs = n_fr_inputs > MAX_FR_OUTPUTS ? MAX_FR_OUTPUTS : n_fr_inputs; - } - - /* 4) INITILIZE STACK INFORMATION */ - - if (has_m2n) { - stk_m2n_size = (unsigned)(m2n_get_size() - 2*sizeof(void*)); - } else { - // preserve space for callee-saves registers - stk_m2n_size = lil_cs_get_max_locals(stub) * GR_SIZE; - } - - // stack size needed for saving GR & FR inputs - if (must_save_inputs()) { - // TODO: check if we need alignment here - stk_input_gr_save_size = n_gr_inputs * GR_SIZE; - stk_input_fr_save_size = n_fr_inputs * FR_SIZE; - } - - // determine the size of the stack frame - stk_size = - stk_m2n_size + // memory for m2n frame - stk_input_gr_save_size + // GR input spill space - stk_input_fr_save_size + // FR input spill space - stk_alloc_size + // memory for dynamic allocation - stk_output_size; // outputs on the stack - - // allign stack - if (stk_size % 16 == 0) { - stk_size += 8; - stk_alloc_size +=8; - } -} - -/** - * Implementation notes: - * 1) Current implementation doesn't correctly processes back branches when - * input arguments are accessed - */ -class LcgEM64TCodeGen: public LilInstructionVisitor { - - /** - * Maximum possible size of code generated for a single LIL instruction. - * - * Speculatevely calculated as a sequence of code that used all available - * registers (e.g. potential prolog or epilog) * 2. The real maximum sizes - * are far this speculation (about 120 bytes), so I hardly believe we will - * ever reach this theoretical limit even with deep changes in the LIL codegen. - * As the buffer size is predicted basing on this high estimation, we're safe - * in the terms of buffer overruns in current codeged. - */ - static const unsigned MAX_LIL_INSTRUCTION_CODE_LENGTH = 512*2; - - static const unsigned MAX_DEC_SIZE = 20; // maximum number of bytes required for string representation of int64 - - char * buf_beg; // pointer to the beginning position of the generated code - char * buf; // pointer to the current position of the generated code - - LilCguLabelAddresses labels; // a set of defined labels and theirs addresses - - LilCodeStub * cs; - LcgEM64TContext & context; - tl::MemoryPool & mem; - LilInstructionIterator iter; - - LilInstructionContext * ic; // visit functions can always assume that inst points to the - LilInstruction * inst; // current instruction and ic points to the current context - - const LcgEM64TLoc * rsp_loc; // location denoting rsp register - bool take_inputs_from_stack; // true if inputs were preserved on the stack - // and should be taken from that location - unsigned current_alloc; // keeps track of memory allocation - -private: - - /* Inner Classes */ - - class Tmp_GR_Opnd: public R_Opnd { - - private: - /// Ref to compilation context - LcgEM64TContext& m_context; - /// Returns *reference* to the counter of currently allocated GR registers - unsigned & get_num_used_reg(void) { - return m_context.get_tmp_grs_used(); - } - - public: - Tmp_GR_Opnd(LcgEM64TContext & context, LilInstructionContext * ic): R_Opnd(n_reg), m_context(context) { - // next temporary register is allocated from unused scratched - // registers in the following order: - // ret, std0, std1, out0, out1, out2, out3, out4, out5 - unsigned cur_tmp_reg = 0; - if (lil_ic_get_ret_type(ic) == LT_Void) { - if (cur_tmp_reg == get_num_used_reg()) { - _reg_no = context.get_reg_from_map(LcgEM64TContext::GR_RETURNS_OFFSET).reg_no(); // should be rax - ++get_num_used_reg(); - return; - } else { - ++cur_tmp_reg; - } - } - for (unsigned i = lil_ic_get_num_std_places(ic); i < LcgEM64TContext::MAX_STD_PLACES; i++) { - if (cur_tmp_reg == get_num_used_reg()) { - _reg_no = context.get_reg_from_map(LcgEM64TContext::STD_PLACES_OFFSET + i).reg_no(); - ++get_num_used_reg(); - return; - } else { - ++cur_tmp_reg; - } - } - for (unsigned i = context.get_num_gr_inputs(); i < LcgEM64TContext::MAX_GR_OUTPUTS; i++) { - if (cur_tmp_reg == get_num_used_reg()) { - _reg_no = context.get_reg_from_map(LcgEM64TContext::GR_OUTPUTS_OFFSET + i).reg_no(); - ++get_num_used_reg(); - return; - } else { - ++cur_tmp_reg; - } - } - ASSERT(0,"LIL INTERNAL ERROR: Not enough temporary registers"); - } - - virtual ~Tmp_GR_Opnd() { - --get_num_used_reg(); - } - }; - - class Tmp_FR_Opnd: public XMM_Opnd { - - private: - /// Ref to compilation context - LcgEM64TContext& m_context; - /// Returns *reference* to the counter of currently allocated XMM registers - unsigned & get_num_used_reg(void) { - return m_context.get_tmp_xmms_used(); - } - - public: - Tmp_FR_Opnd(LcgEM64TContext& context, LilInstructionContext * ic): XMM_Opnd(0), m_context(context) { - // next temporary register is allocated from unused scratched - // registers in the following order: - // xmm8, xmm9, ... xmm15 - ASSERT(get_num_used_reg() < LcgEM64TContext::MAX_FR_TEMPORARY + LcgEM64TContext:: MAX_FR_LOCALS, - //ASSERT(get_num_used_reg() < LcgEM64TContext::MAX_FR_TEMPORARY , - "LIL INTERNAL ERROR: Not enough temporary registers"); - m_idx = LcgEM64TContext::get_xmm_reg_from_map( - LcgEM64TContext::FR_TEMPORARY_OFFSET + get_num_used_reg()).get_idx(); - ++get_num_used_reg(); - } - - virtual ~Tmp_FR_Opnd() { - --get_num_used_reg(); - } - }; - - /* Helper functions */ - - /** - * Estimates maximum possible code size for the current stub. - */ - unsigned estimate_code_size(void) { - unsigned num_insts = lil_cs_get_num_instructions(cs); - return num_insts*MAX_LIL_INSTRUCTION_CODE_LENGTH; - } - - /** - * returns the location of the n'th int local - */ - const LcgEM64TLoc * get_gp_local(const unsigned n) const { - assert(n < LcgEM64TContext::MAX_GR_LOCALS); - return new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::GR_LOCALS_OFFSET + n); - } - - // returns the location of the n'th fp local - const LcgEM64TLoc * get_fp_local(const unsigned n) const { - // DO NOT SUPPORT FP LOCALS - ABORT("Not supported"); - return NULL; - /* - assert(n < context.get_num_fr_locals()); - if (does_normal_calls) { - return new(mem) LcgEM64TLoc(LLK_FStk, context.get_local_fr_offset() + n * FR_SIZE); - else { - return new(mem) LcgEM64TLoc(LLK_Fr, LcgEM64TContext::FR_LOCALS_OFFSET + n); - } - */ - } - - // returns the location of the n'th standard place - const LcgEM64TLoc * get_std_place(const unsigned n) const { - assert(n < LcgEM64TContext::MAX_STD_PLACES); - return new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::STD_PLACES_OFFSET + n); - } - - /** - * returns number of bytes required for the given type on the stack - */ - unsigned get_num_bytes_on_stack(LilType t) const { - switch (t) { - case LT_Void: - return 0; - case LT_G1: - case LT_G2: - case LT_G4: - case LT_F4: - return 4; - case LT_Ref: - case LT_PInt: - case LT_G8: - case LT_F8: - return 8; - default: - ASSERT(0, "Unknown LIL type"); - } - return 0; - } - - // 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 LcgEM64TLoc * get_input(const unsigned n) const { - assert(n < context.get_num_inputs()); -#ifndef NDEBUG - if (take_inputs_from_stack) { - assert(context.must_save_inputs()); - } -#endif - LilType t; - unsigned gp_param_cnt = 0; -#ifdef _WIN64 -#define fp_param_cnt gp_param_cnt -#else - unsigned fp_param_cnt = 0; -#endif - for (unsigned i = 0; i < n; i++) { - t = lil_sig_get_arg_type(lil_cs_get_sig(cs), i); - if (context.is_fp_type(t)) { - ++fp_param_cnt; - } else { - ++gp_param_cnt; - } - } - - // type of n'th input - t = lil_sig_get_arg_type(lil_cs_get_sig(cs), n); - - if (context.is_fp_type(t)) { - if (fp_param_cnt < LcgEM64TContext::MAX_FR_OUTPUTS) { - if (take_inputs_from_stack) { - // compute rsp-relative offset of the bottom of FR save area - int32 offset = context.get_input_fr_save_offset() + - fp_param_cnt * LcgEM64TContext::FR_SIZE; - return new(mem) LcgEM64TLoc(LLK_FStk, offset); - } else { - return new(mem) LcgEM64TLoc(LLK_Fr, - LcgEM64TContext::FR_OUTPUTS_OFFSET + fp_param_cnt); - } - } else { - unsigned gp_on_stack = gp_param_cnt > LcgEM64TContext::MAX_GR_OUTPUTS - ? (gp_param_cnt - LcgEM64TContext::MAX_GR_OUTPUTS) - * LcgEM64TContext::GR_SIZE : 0; - unsigned fp_on_stack = fp_param_cnt > LcgEM64TContext::MAX_FR_OUTPUTS - ? (fp_param_cnt - LcgEM64TContext::MAX_FR_OUTPUTS) - * LcgEM64TContext::FR_SIZE : 0; - // compute rsp-relative offset of the top of the activation frame - int32 offset = context.get_stk_size(); - // skip rip - offset += LcgEM64TContext::GR_SIZE; - // skip size allocated for preceding inputs - offset += gp_on_stack; -#ifndef _WIN64 - offset += fp_on_stack; -#endif - return new(mem) LcgEM64TLoc(LLK_FStk, offset); - } - } else { // if (context.is_fp_type(t)) - if (gp_param_cnt < LcgEM64TContext::MAX_GR_OUTPUTS) { - if (take_inputs_from_stack) { - // compute rsp-relative offset of the bottom of GR save area - int32 offset = context.get_input_gr_save_offset() + - gp_param_cnt * LcgEM64TContext::GR_SIZE; - return new(mem) LcgEM64TLoc(LLK_GStk, offset); - } else { - return new(mem) LcgEM64TLoc(LLK_Gr, - LcgEM64TContext::GR_OUTPUTS_OFFSET + gp_param_cnt); - } - } else { - unsigned gp_on_stack = gp_param_cnt > LcgEM64TContext::MAX_GR_OUTPUTS - ? (gp_param_cnt - LcgEM64TContext::MAX_GR_OUTPUTS) - * LcgEM64TContext::GR_SIZE : 0; - unsigned fp_on_stack = fp_param_cnt > LcgEM64TContext::MAX_FR_OUTPUTS - ? (fp_param_cnt - LcgEM64TContext::MAX_FR_OUTPUTS) - * LcgEM64TContext::FR_SIZE : 0; - // compute rsp-relative offset of the top of the activation frame - int32 offset = context.get_stk_size(); - // skip rip - offset += LcgEM64TContext::GR_SIZE; - // skip size allocated for preceding inputs - offset += gp_on_stack; -#ifndef _WIN64 - offset += fp_on_stack; -#endif - return new(mem) LcgEM64TLoc(LLK_GStk, offset); - } - } - } - - - // returns the location of the n'th output - const LcgEM64TLoc * get_output(const unsigned n, LilSig * out_sig) const { - assert(n <= context.get_num_outputs()); - - LilType t; - unsigned gp_param_cnt = 0; -#ifdef _WIN64 -#define fp_param_cnt gp_param_cnt -#else - unsigned fp_param_cnt = 0; -#endif - for (unsigned i = 0; i < n; i++) { - t = lil_sig_get_arg_type(out_sig, i); - if (context.is_fp_type(t)) { - ++fp_param_cnt; - } else { - ++gp_param_cnt; - } - } - - // type of n'th output - t = lil_sig_get_arg_type(out_sig, n); - - if (context.is_fp_type(t)) { - if (fp_param_cnt < LcgEM64TContext::MAX_FR_OUTPUTS) { - return new(mem) LcgEM64TLoc(LLK_Fr, - LcgEM64TContext::FR_OUTPUTS_OFFSET + fp_param_cnt); - } else { - unsigned gp_on_stack = gp_param_cnt > LcgEM64TContext::MAX_GR_OUTPUTS - ? (gp_param_cnt - LcgEM64TContext::MAX_GR_OUTPUTS) - * LcgEM64TContext::GR_SIZE : 0; - unsigned fp_on_stack = fp_param_cnt > LcgEM64TContext::MAX_FR_OUTPUTS - ? (fp_param_cnt - LcgEM64TContext::MAX_FR_OUTPUTS) - * LcgEM64TContext::FR_SIZE : 0; - int32 offset = gp_on_stack; -#ifndef _WIN64 - offset += fp_on_stack; -#endif - return new(mem) LcgEM64TLoc(LLK_FStk, offset); - } - } else { - if (gp_param_cnt < LcgEM64TContext::MAX_GR_OUTPUTS) { - return new(mem) LcgEM64TLoc(LLK_Gr, - LcgEM64TContext::GR_OUTPUTS_OFFSET + gp_param_cnt); - } else { - unsigned gp_on_stack = gp_param_cnt > LcgEM64TContext::MAX_GR_OUTPUTS - ? (gp_param_cnt - LcgEM64TContext::MAX_GR_OUTPUTS) - * LcgEM64TContext::GR_SIZE : 0; - unsigned fp_on_stack = fp_param_cnt > LcgEM64TContext::MAX_FR_OUTPUTS - ? (fp_param_cnt - LcgEM64TContext::MAX_FR_OUTPUTS) - * LcgEM64TContext::FR_SIZE : 0; - int32 offset = gp_on_stack; -#ifndef _WIN64 - offset += fp_on_stack; -#endif - return new(mem) LcgEM64TLoc(LLK_GStk, offset); - } - } - } - - /** - * 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 LcgEM64TLoc * 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 LcgEM64TLoc * get_var_loc(LilVariable * var, bool is_lvalue) const { - unsigned index = lil_variable_get_index(var); - switch (lil_variable_get_kind(var)) { - case LVK_In: - return get_input(index); - case LVK_StdPlace: - return get_std_place(index); - case LVK_Out: { - LilSig * out_sig = lil_ic_get_out_sig(ic); - assert(out_sig != NULL); - return get_output(index, out_sig); - } - case LVK_Local: { - //LilType t = is_lvalue - // ? lil_instruction_get_dest_type(cs, inst, ic) - // : lil_ic_get_local_type(ic, index); - //return context.is_fp_type(t) ? get_fp_local(index) : get_gp_local(index); - // no support for fp locals - return get_gp_local(index); - } - case LVK_Ret: { - //LilType t = is_lvalue - // ? lil_instruction_get_dest_type(cs, inst, ic) - // : lil_ic_get_ret_type(ic); - LilType t = lil_ic_get_ret_type(ic); - return context.is_fp_type(t) - ? new(mem) LcgEM64TLoc(LLK_Fr, LcgEM64TContext::FR_RETURNS_OFFSET) - : new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::GR_RETURNS_OFFSET); - } - default: - ASSERT(0, "Unknown variable kind"); - } - return NULL; // should never be reached - } - - inline int64 get_imm_value(LilOperand * op) const { - assert(lil_operand_is_immed(op)); - return lil_operand_get_immed(op); - } - - inline const Imm_Opnd & get_imm_opnd(LilOperand * op, Opnd_Size sz = n_size) const { - return get_imm_opnd(get_imm_value(op), sz); - } - - 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_64, value)); - } - return *(new(mem_ptr) Imm_Opnd(sz, value)); - } - - inline const R_Opnd & get_r_opnd(const LcgEM64TLoc * loc) const { - assert(loc->kind == LLK_Gr); - return context.get_reg_from_map((unsigned)loc->addr); - } - - inline const XMM_Opnd & get_xmm_r_opnd(const LcgEM64TLoc * loc) const { - assert(loc->kind == LLK_Fr); - return context.get_xmm_reg_from_map((unsigned)loc->addr); - } - - inline const M_Opnd & get_m_opnd(const LcgEM64TLoc * loc) const { - assert(loc->kind == LLK_GStk || loc->kind == LLK_FStk); - void * const mem_ptr = mem.alloc(sizeof(M_Base_Opnd)); - return *(new(mem_ptr) M_Base_Opnd(rsp_reg, (int32)loc->addr)); - } - - inline const RM_Opnd & get_rm_opnd(const LcgEM64TLoc * loc) const { - assert(loc->kind == LLK_Gr || loc->kind == LLK_GStk); - if (loc->kind == LLK_Gr) { - return get_r_opnd(loc); - } - //return M_Base_Opnd(rsp_reg, loc->addr); - return get_m_opnd(loc); - } - - inline void adjust_stack_pointer(int32 offset) { - if (offset != 0) { - buf = alu(buf, add_opc, rsp_opnd, get_imm_opnd(offset), size_64); - } - } - - // move integer immediate to GR or memory stack - void move_imm(const LcgEM64TLoc * dest, int64 imm_val) { - assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); - buf = mov(buf, get_rm_opnd(dest), get_imm_opnd(imm_val), size_64); - } - - // move between two register or stack locations - void move_rm(const LcgEM64TLoc* dest, const LcgEM64TLoc* src) { - if (*dest == *src) { - return; // nothing to be done - } - if (dest->kind == LLK_Gr || dest->kind == LLK_GStk) { - if (src->kind == LLK_Gr || src->kind == LLK_GStk) { - if (dest->kind == LLK_Gr) { - buf = mov(buf, get_r_opnd(dest), get_rm_opnd(src), size_64); - return; - } - // dest->kind != LLK_Gr - if (src->kind == LLK_Gr) { - buf = mov(buf, get_m_opnd(dest), get_r_opnd(src), size_64); - return; - } - // src->kind != LLK_Gr - // use temporary register - Tmp_GR_Opnd tmp_reg(context, ic); - buf = mov(buf, tmp_reg, get_m_opnd(src), size_64); - buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); - } else { // src->kind == LLK_Fr || src->kind == LLK_FStk - // src->kind == LLK_Fr supported only - assert(src->kind == LLK_Fr); - buf = movq(buf, get_rm_opnd(dest), get_xmm_r_opnd(src)); - return; - } - } else { // dest->kind == LLK_Fr || dest->kind == LLK_FStk - if (src->kind == LLK_Fr || src->kind == LLK_FStk) { - if (dest->kind == LLK_Fr) { - if (src->kind == LLK_Fr) { - buf = sse_mov(buf, get_xmm_r_opnd(dest), get_xmm_r_opnd(src), true); - } else { - buf = sse_mov(buf, get_xmm_r_opnd(dest), get_m_opnd(src), true); - } - return; - } - // dest->kind != LLK_Gr - if (src->kind == LLK_Fr) { - buf = sse_mov(buf, get_m_opnd(dest), get_xmm_r_opnd(src), true); - return; - } - // src->kind != LLK_Gr - // use temporary register - Tmp_FR_Opnd tmp_reg(context, ic); - buf = sse_mov(buf, tmp_reg, get_m_opnd(src), true); - buf = sse_mov(buf, get_m_opnd(dest), tmp_reg, true); - } else { // src->kind == LLK_Gr || src->kind == LLK_GStk - // dest->kind == LLK_Fr supported only - assert(dest->kind == LLK_Fr); - buf = movq(buf, get_xmm_r_opnd(dest), get_rm_opnd(src)); - return; - } - } - } - - void shift_op_imm_rm(const LcgEM64TLoc * dest, int32 imm_val, const LcgEM64TLoc * src) { - assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); - assert(src->kind == LLK_Gr || src->kind == LLK_GStk); - // 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 LcgEM64TLoc * dest, const LcgEM64TLoc * 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_64); - } else { - buf = mov(buf, get_m_opnd(dest), get_r_opnd(src), size_64); - } - 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_64); - buf = shift(buf, shl_opc, get_r_opnd(dest), imm, size_64); - return; - } - // dest->kind != LLK_Gr - Tmp_GR_Opnd tmp_reg(context, ic); - buf = mov(buf, tmp_reg, get_m_opnd(src), size_64); - 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 LcgEM64TLoc * dest, int32 imm_val, const LcgEM64TLoc * src) { - assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); - assert(src->kind == LLK_Gr || src->kind == LLK_GStk); - // 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 LcgEM64TLoc * dest, - const LcgEM64TLoc * src, int32 imm_val) { - assert(alu_opc < n_alu); - assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); - assert(src->kind == LLK_Gr || src->kind == LLK_GStk); - const Imm_Opnd & imm = get_imm_opnd(imm_val); - if (*dest == *src) { - buf = alu(buf, alu_opc, get_rm_opnd(dest), imm, size_64); - return; - } - // dest != src - if (dest->kind == LLK_Gr) { - buf = mov(buf, get_r_opnd(dest), get_rm_opnd(src), size_64); - buf = alu(buf, alu_opc, get_r_opnd(dest), imm, size_64); - return; - } - // dest->kind != LLK_Gr - if (src->kind == LLK_Gr) { - buf = mov(buf, get_m_opnd(dest), get_r_opnd(src), size_64); - buf = alu(buf, alu_opc, get_m_opnd(dest), imm, size_64); - return; - } - // src->kind == LLK_Gr - Tmp_GR_Opnd tmp_reg(context, ic); - buf = mov(buf, tmp_reg, get_m_opnd(src), size_64); - buf = alu(buf, alu_opc, tmp_reg, imm, size_64); - buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); - } - - void alu_op_rm_rm(const ALU_Opcode alu_opc, const LcgEM64TLoc * dest, - const LcgEM64TLoc * src1, const LcgEM64TLoc * src2) { - assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); - assert(src1->kind == LLK_Gr || src1->kind == LLK_GStk); - assert(src2->kind == LLK_Gr || src2->kind == LLK_GStk); - - 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_64); - 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_64); - 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_64); - buf = alu(buf, alu_opc, tmp_reg, get_rm_opnd(src2)); - buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); - } - - // where the second operand is immediate (allowed only for integer values!) - void bin_op_rm_imm(LilOperation o, const LcgEM64TLoc* dest, - const LcgEM64TLoc* src, int32 imm_val) { - assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); - assert(src->kind == LLK_Gr || src->kind == LLK_GStk); - - switch (o) { - case LO_Add: - return alu_op_rm_imm(add_opc, dest, src, imm_val); - case LO_Sub: - return alu_op_rm_imm(sub_opc, dest, src, imm_val); - case LO_And: - return alu_op_rm_imm(and_opc, dest, src, imm_val); - 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); - } - return; - } - // dest->kind != LLK_Gr - Tmp_GR_Opnd tmp_reg(context, ic); - buf = mov(buf, tmp_reg, get_rm_opnd(src), size_64); - buf = imul(buf, tmp_reg, imm); - buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); - break; - } - case LO_Shl: - shift_op_rm_imm(dest, src, imm_val); - break; - default: - ASSERT(0, "Unexpected operation"); - } - } - - // binary arithmetic operations without immediates - // (allowed only for integer values!) - void bin_op_rm_rm(LilOperation o, const LcgEM64TLoc * dest, - const LcgEM64TLoc * src1, const LcgEM64TLoc * src2) { - assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); - assert(src1->kind == LLK_Gr || src1->kind == LLK_GStk); - assert(src2->kind == LLK_Gr || src2->kind == LLK_GStk); - - 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_64); - } - 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_64); - buf = imul(buf, tmp_reg, get_rm_opnd(src2)); - buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); - break; - } - case LO_Shl: { - move_rm(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_64); - } - buf = shift(buf, shl_opc, get_rm_opnd(dest), *src2_reg); - break; - } - default: - ASSERT(0, "Unexpected operation"); // control should never reach this point - } - } - - // unary operation without immediates - void un_op_rm(LilOperation o, const LcgEM64TLoc * dest, const LcgEM64TLoc * src) { - assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); - assert(src->kind == LLK_Gr || src->kind == LLK_GStk); - - const Tmp_GR_Opnd tmp_reg(context, ic); - const R_Opnd & dest_reg = - (dest->kind == LLK_Gr ? get_r_opnd(dest) : (const R_Opnd &)tmp_reg); - - switch (o) { - case LO_Neg: - buf = mov(buf, dest_reg, get_rm_opnd(src), size_64); - buf = neg(buf, dest_reg, size_64); - break; - case LO_Not: - buf = mov(buf, dest_reg, get_rm_opnd(src), size_64); - buf = _not(buf, dest_reg, size_64); - 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 - } - - if (dest->kind != LLK_Gr) { - buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); - } - } - - // generate instructions that calculate a LIL address - const M_Opnd & get_effective_addr(LilVariable * base, unsigned scale, - LilVariable * index, int64 offset, - const R_Opnd & tmp_reg) { - 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_64, offset)); - return *(new(M_Index_Opnd_mem) M_Index_Opnd(tmp_reg.reg_no(), n_reg, 0, 0)); - } - - // initialize locations - const bool is_offset32 = fit32(offset); - const LcgEM64TLoc * base_loc = base != NULL ? get_var_loc(base, false) : NULL; - const LcgEM64TLoc * index_loc = index != NULL && scale != 0 ? get_var_loc(index, false) : NULL; - const bool is_base_in_mem = base_loc != NULL && base_loc->kind == LLK_GStk; - const bool is_index_in_mem = index_loc != NULL && index_loc->kind == LLK_GStk; - - // check if there is no need to use temporary register - if (is_offset32 && !is_base_in_mem && !is_index_in_mem) { - return *(new(M_Index_Opnd_mem) M_Index_Opnd( - base_loc != NULL ? get_r_opnd(base_loc).reg_no() : n_reg, - index_loc != NULL ? get_r_opnd(index_loc).reg_no() : n_reg, - (uint32)offset, scale)); - } - - // check if it's enough to use only one temporary register - if (is_offset32 && (is_base_in_mem || is_index_in_mem)) { - if (is_base_in_mem) { - buf = mov(buf, tmp_reg, get_m_opnd(base_loc), size_64); - return *(new(M_Index_Opnd_mem) M_Index_Opnd(tmp_reg.reg_no(), - index_loc != NULL ? get_r_opnd(index_loc).reg_no() : n_reg, - (uint32)offset, scale)); - } - if (is_index_in_mem) { - buf = mov(buf, tmp_reg, get_m_opnd(index_loc), size_64); - return *(new(M_Index_Opnd_mem) M_Index_Opnd( - base_loc != NULL ? get_r_opnd(base_loc).reg_no() : n_reg, - tmp_reg.reg_no(), (uint32)offset, scale)); - } - ASSERT(0, "Should never reach this point"); - } - - // need to perform manual calculation of the effective address - const LcgEM64TLoc * ret_loc = - new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::get_index_in_map(tmp_reg.reg_no())); - if (index_loc != NULL) { - int32 shift = scale / 4 + 1; - bin_op_rm_imm(LO_Shl, ret_loc, index_loc, shift); - if (base_loc != NULL) { - bin_op_rm_rm(LO_Add, ret_loc, ret_loc, base_loc); - } - if (!is_offset32) { - Tmp_GR_Opnd tmp_reg2(context, ic); - const LcgEM64TLoc * tmp_loc = - new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::get_index_in_map(tmp_reg2.reg_no())); - move_imm(tmp_loc, offset); - bin_op_rm_rm(LO_Add, ret_loc, ret_loc, tmp_loc); - offset = 0; - } - } else if (base_loc != NULL) { - // index_loc is NULL - if (is_offset32) { - move_rm(ret_loc, base_loc); - } else { - move_imm(ret_loc, offset); - bin_op_rm_rm(LO_Add, ret_loc, ret_loc, base_loc); - offset = 0; - } - - } else { - assert(!is_offset32); - move_imm(ret_loc, offset); - offset = 0; - } - return *(new(M_Index_Opnd_mem) M_Base_Opnd(get_r_opnd(ret_loc).reg_no(), (uint32)offset)); - } - - // sets up stack frame - void prolog() { - // push callee-saves registers on the stack - if (lil_cs_get_max_locals(cs) > 0) { - assert(context.get_stk_size() != 0); - for (unsigned i = 0; i < lil_cs_get_max_locals(cs); i++) { - const LcgEM64TLoc * loc = get_gp_local(i); - assert(loc->kind == LLK_Gr); - buf = push(buf, get_r_opnd(loc), size_64); - } - } - adjust_stack_pointer(lil_cs_get_max_locals(cs) * LcgEM64TContext::GR_SIZE - - context.get_stk_size()); - } - - // unsets the stack frame - void epilog() { - adjust_stack_pointer(context.get_stk_size() - - lil_cs_get_max_locals(cs) * LcgEM64TContext::GR_SIZE); - - if (lil_cs_get_max_locals(cs) > 0) { - assert(context.get_stk_size() != 0); - // pop callee-saves registers from the stack - for (int i = lil_cs_get_max_locals(cs) - 1; i >= 0; i--) { - const LcgEM64TLoc * loc = get_gp_local(i); - assert(loc->kind == LLK_Gr); - buf = pop(buf, get_r_opnd(loc), size_64); - } - } - } - - // moves GR & FR inputs into the stack - void move_inputs() { - if (!context.must_save_inputs()) { - return; - } - - // compute the rsp-relative offset of the top of the GR save area - int32 offset = (int32)context.get_input_gr_save_offset() + - context.get_stk_input_gr_save_size(); - // store GR inputs to the computed locations - for (int i = context.get_num_gr_inputs() - 1; i >= 0; i--) { - const R_Opnd & r_opnd = - LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_OUTPUTS_OFFSET + i); - offset -= LcgEM64TContext::GR_SIZE; - const M_Opnd dest(rsp_reg, offset); - buf = mov(buf, dest, r_opnd); - } - // compute the rsp-relative offset of the top of the FR save area - offset = (int32)context.get_input_fr_save_offset() + - context.get_stk_input_fr_save_size(); - // store FR inputs to the computed locations - for (int i = context.get_num_fr_inputs() - 1; i >= 0; i--) { - const XMM_Opnd & r_opnd = - LcgEM64TContext::get_xmm_reg_from_map(LcgEM64TContext::FR_OUTPUTS_OFFSET + i); - offset -= LcgEM64TContext::FR_SIZE; - const M_Opnd dest(rsp_reg, offset); - buf = sse_mov(buf, dest, r_opnd, true); - } - } - - // moves GR & FR inputs from the stack back to the registers - void unmove_inputs() { - if (!context.must_save_inputs()) { - // inputs should be already in place - return; - } - // compute the rsp-relative offset of the bottom of the FR save area - int32 offset = (int32)context.get_input_fr_save_offset(); - // restore FR inputs - for (unsigned i = 0; i < context.get_num_fr_inputs(); i++) { - const XMM_Opnd & r_opnd = - LcgEM64TContext::get_xmm_reg_from_map(LcgEM64TContext::FR_OUTPUTS_OFFSET + i); - const M_Opnd src(rsp_reg, offset); - buf = sse_mov(buf, r_opnd, src, true); - offset += LcgEM64TContext::FR_SIZE; - } - // compute the rsp-relative offset of the bottom of the GR save area - offset = (int32)context.get_input_gr_save_offset(); - // restore GR inputs - for (unsigned i = 0; i < context.get_num_gr_inputs(); i++) { - const R_Opnd & r_opnd = - LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_OUTPUTS_OFFSET + i); - const M_Opnd src(rsp_reg, offset); - buf = mov(buf, r_opnd, src, size_64); - offset += LcgEM64TContext::GR_SIZE; - } - } - - Opnd_Size type_to_opnd_size(LilType t) const { - switch (t) { - case LT_G1: - return size_8; - case LT_G2: - return size_16; - case LT_G4: - return size_32; - case LT_G8: - case LT_PInt: - case LT_Ref: - return size_64; - default: - return n_size; - } - } - -public: - - /** - * constructor - */ - LcgEM64TCodeGen(LilCodeStub * cs, LcgEM64TContext & c, tl::MemoryPool & m): - buf_beg(NULL), buf(NULL), - labels(&m, buf_beg), cs(cs), context(c), mem(m), iter(cs, true), ic(NULL), inst(NULL), - rsp_loc(new(m) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::RSP_OFFSET)), take_inputs_from_stack(false), - current_alloc(0) { - - unsigned max_code_size = estimate_code_size(); - - buf = buf_beg = (char *)m.alloc(max_code_size); - char* buf_end = buf_beg + max_code_size; - - static unsigned max_code = 0; - - // emit entry code - char* i_start = buf; - - // the size of code for prolog accounted in estimate_code_size - // as the code for 'entry' instruction. - - prolog(); - move_inputs(); - - // debug code: check the estimate - char* i_end = buf; - unsigned i_len = (unsigned)(i_end - i_start); - if (i_len > MAX_LIL_INSTRUCTION_CODE_LENGTH) { - // the MAX_LIL_INSTRUCTION_CODE_LENGTH was underestimated. - // most likely will not cause problems in real life, though still requires correction. - assert(false); - } - - // go through the instructions - while (!iter.at_end()) { - char* i_start = buf; - - ic = iter.get_context(); - inst = iter.get_current(); - lil_visit_instruction(inst, this); - iter.goto_next(); - - // debug code: see above for the rationale - char* i_end = buf; - unsigned i_len = (unsigned)(i_end - i_start); - if (i_len > MAX_LIL_INSTRUCTION_CODE_LENGTH) { - assert(false); - } - } - - if (buf>buf_end) { - /// Ugh. Buffer overrun. - /// Must be accompanied with previous assert()-s on MAX_LIL_INSTRUCTION_CODE_LENGTH? - assert(false); - } - } - - /** - * memory allocator - */ - void *operator new(size_t sz, tl::MemoryPool & m) { - return m.alloc(sz); - } - - /** - * returns actual size of the generated code - */ - size_t get_size() const { - return buf - buf_beg; - } - - /** - * 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; - } - - - /* Visitor Functions */ - - void label(LilLabel lab) { - labels.define_label(lab, buf, true); - } - - void locals(unsigned num) { - // nothing to be done here; - } - - void std_places(unsigned num) { - // nothing to be done here; - } - - 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), rsp_loc, alloc_offset); - } - - 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 LcgEM64TLoc * 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 LcgEM64TLoc * src_loc = get_op_loc(op1, false); - move_rm(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 - assert(fit32(get_imm_value(op1))); - int32 op1_imm = (int32)get_imm_value(op1); - assert(fit32(get_imm_value(op2))); - int32 op2_imm = (int32)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 - } - move_imm(dest_loc, result); - } else if (lil_operand_is_immed(op1)) { - assert(fit32(get_imm_value(op1))); - const int32 op1_imm = (int32)get_imm_value(op1); - const LcgEM64TLoc * 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 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 - } - } else if (lil_operand_is_immed(op2)) { - const LcgEM64TLoc* op1_loc = get_op_loc(op1, false); - assert(fit32(get_imm_value(op2))); - const int32 op2_imm = (int32)get_imm_value(op2); - bin_op_rm_imm(o, dest_loc, op1_loc, op2_imm); - } else { // both operands non-immediate - const LcgEM64TLoc * src1_loc = get_op_loc(op1, false); - const LcgEM64TLoc * 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)) { - assert(fit32(get_imm_value(op1))); - int32 imm = (int32)get_imm_value(op1); - int32 result = 0; - switch (o) { - case LO_Neg: - result = -imm; - break; - 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 LO_Zx4: - result = (int32) (uint64) (uint32) imm; - break; - default: - ASSERT(0, "Unexpected operation"); // control should never reach this point - } - move_imm(dest_loc, result); - } else { // non-immediate operand - const LcgEM64TLoc * op1_loc = get_op_loc(op1, false); - un_op_rm(o, dest_loc, op1_loc); - } - } - } - - // TODO: think over if it makes sense to preserve a register for VM_Tread pointer - void ts(LilVariable * var) { - const LcgEM64TLoc * var_loc = get_var_loc(var, true); - assert(var_loc->kind == LLK_Gr || var_loc->kind == LLK_GStk); - - if (var_loc->kind == LLK_Gr) { - buf = m2n_gen_ts_to_register(buf, &get_r_opnd(var_loc), - lil_ic_get_num_locals(ic), lil_cs_get_max_locals(cs), - lil_ic_get_num_std_places(ic), lil_ic_get_ret_type(ic) == LT_Void ? 0 : 1); - } else { - const Tmp_GR_Opnd tmp(context, ic); - buf = m2n_gen_ts_to_register(buf, &tmp, - lil_ic_get_num_locals(ic), lil_cs_get_max_locals(cs), - lil_ic_get_num_std_places(ic), lil_ic_get_ret_type(ic) == LT_Void ? 0 : 1); - buf = mov(buf, get_m_opnd(var_loc), tmp, size_64); - } - - take_inputs_from_stack = true; - } - - void handles(LilOperand * op) { - if (lil_operand_is_immed(op)) { - buf = m2n_gen_set_local_handles_imm(buf, - context.get_m2n_offset(), &get_imm_opnd(op)); - } else { - const LcgEM64TLoc * 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 = m2n_gen_set_local_handles_r(buf, - context.get_m2n_offset(), &tmp); - buf = mov(buf, get_m_opnd(loc), tmp, size_64); - } - } - } - - void ld(LilType t, LilVariable * dest, LilVariable * base, unsigned scale, - LilVariable * index, long long int offset, LilAcqRel 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_reg1(context, ic); - const Tmp_GR_Opnd * tmp_reg2 = NULL; - - // calculate the address - const M_Opnd & addr = get_effective_addr(base, scale, index, offset, tmp_reg1); - const LcgEM64TLoc * dest_loc = get_var_loc(dest, true); - assert(dest_loc->kind == LLK_Gr || dest_loc->kind == LLK_GStk); - void * const mem_ptr = mem.alloc(sizeof(Tmp_GR_Opnd)); - const R_Opnd * dest_reg = dest_loc->kind == LLK_Gr ? &get_r_opnd(dest_loc) : - (tmp_reg2 = new(mem_ptr) Tmp_GR_Opnd(context, ic)); - - Opnd_Size size = n_size; - switch(t) { - 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: - buf = mov(buf, *dest_reg, addr, size_64); - goto move_to_destination; - default: - ASSERT(0, "Unexpected LIL type"); // invalid value in type - } - - assert(size != n_size); - - if (ext == LLX_Zero) { - // movzx r64, r/m32 is not available on em64t - // mov r32, r/m32 should zero out upper bytes - if (size == size_32) { - buf = mov(buf, *dest_reg, addr, size); - } else { - buf = movzx(buf, *dest_reg, addr, size); - } - } else { - buf = movsx(buf, *dest_reg, addr, size); - } -move_to_destination: - if (dest_loc->kind != LLK_Gr) { - buf = mov(buf, get_m_opnd(dest_loc), *dest_reg, size_64); - delete dest_reg; - } - } - - void st(LilType t, LilVariable * base, unsigned scale, LilVariable * index, - long long int 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 { - const LcgEM64TLoc * 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, - long long int 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 cas(LilType t, LilVariable * base, unsigned scale, - LilVariable * index, long long int offset, LilAcqRel acqrel, - LilOperand * cmp, LilOperand * src, LilLabel label) { - // this is either rax register itself or keeps value of rax - const Tmp_GR_Opnd tmp_reg(context, ic); - - const LcgEM64TLoc * cmp_loc = lil_operand_is_immed(cmp) ? NULL : get_op_loc(cmp, false); - const LcgEM64TLoc * src_loc = lil_operand_is_immed(src) ? NULL : get_op_loc(src, false); - - bool is_rax_used = tmp_reg.reg_no() != rax_reg; - bool is_rax_used_by_cmp = (cmp_loc != NULL && cmp_loc->kind == LLK_Gr - && get_r_opnd(cmp_loc).reg_no() == rax_reg); - if (is_rax_used && !is_rax_used_by_cmp) { - // need to preserve rax value - buf = mov(buf, tmp_reg, rax_opnd, size_64); - } - - if (!is_rax_used_by_cmp) { - if (cmp_loc == NULL) { // cmp is immediate - buf = mov(buf, rax_opnd, get_imm_opnd(cmp), type_to_opnd_size(t)); - } else { - buf = mov(buf, rax_opnd, get_rm_opnd(cmp_loc), type_to_opnd_size(t)); - } - } - - const Tmp_GR_Opnd tmp_reg1(context, ic); - // calculate the address - const M_Opnd & addr = get_effective_addr(base, scale, index, offset, tmp_reg1); - - void * const mem_ptr = mem.alloc(sizeof(Tmp_GR_Opnd)); - const R_Opnd * src_reg = NULL; - const R_Opnd * src_reg2 = NULL; - if (src_loc == NULL) { // src is immediate value - src_reg = new(mem_ptr) Tmp_GR_Opnd(context, ic); - buf = mov(buf, *src_reg, get_imm_opnd(src), type_to_opnd_size(t)); - } else if (src_loc->kind == LLK_GStk) { - src_reg = new(mem_ptr) Tmp_GR_Opnd(context, ic); - buf = mov(buf, *src_reg, get_m_opnd(src_loc), type_to_opnd_size(t)); - } else if (is_rax_used && get_r_opnd(src_loc).reg_no() == rax_reg) { - // value of rax was saved in tmp_reg - src_reg2 = &tmp_reg; - } else { - src_reg2 = &get_r_opnd(src_loc); - } - buf = prefix(buf, lock_prefix); - buf = cmpxchg(buf, addr, src_reg != NULL ? *src_reg : *src_reg2, - type_to_opnd_size(t)); - buf = branch32(buf, Condition_NE, get_imm_opnd((int64)0, size_32)); - labels.add_patch_to_label(label, buf - 4, LPT_Rel32); - - delete src_reg; - - if (is_rax_used && src_reg2->reg_no() != rax_reg) { - // move preserved value back to rax - buf = mov(buf, rax_reg, tmp_reg, size_64); - } - } - - 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 p, LilOperand * op1, LilOperand * op2, LilLabel label) { - // compute the condition - ConditionCode cc = Condition_O; - - 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"); - } - - void * const mem_ptr = mem.alloc(sizeof(Tmp_GR_Opnd)); - const RM_Opnd * src1 = NULL; - const Tmp_GR_Opnd * tmp_reg = NULL; - if (!lil_predicate_is_binary(p)) { - op2 = (LilOperand *)mem.alloc(sizeof(LilOperand)); - op2->is_immed = true; - op2->val.imm = 0; - } - if (lil_operand_is_immed(op1) && lil_operand_is_immed(op2)) { - tmp_reg = new(mem_ptr) Tmp_GR_Opnd(context, ic); - src1 = tmp_reg; - buf = mov(buf, *tmp_reg, get_imm_opnd(op1), size_64); - } else { - if (lil_operand_is_immed(op1)) { - assert(!lil_operand_is_immed(op2)); - LilOperand * tmp_op = op1; - op1 = op2; - op2 = tmp_op; - switch (cc) { - case Condition_LE: - cc = Condition_G; - break; - case Condition_L: - cc = Condition_GE; - break; - default:; - } - } - assert(!lil_operand_is_immed(op1)); - src1 = &get_rm_opnd(get_op_loc(op1, false)); - } - // src1 is set here (not an immediate) - if (lil_operand_is_immed(op2)) { - int64 imm = lil_operand_get_immed(op2); - if (fit32(imm)) { - buf = alu(buf, cmp_opc, *src1, get_imm_opnd(imm, size_32), size_64); - } else { - // use temporary register - Tmp_GR_Opnd src2_reg(context, ic); - buf = mov(buf, src2_reg, get_imm_opnd(imm, size_64), size_64); - if (src1->is_reg()) { - buf = alu(buf, cmp_opc, *(R_Opnd *)src1, src2_reg); - } else { - buf = alu(buf, cmp_opc, *(M_Opnd *)src1, src2_reg); - } - } - } else { - // second operand is not an immediate value - const LcgEM64TLoc * src2_loc = get_op_loc(op2, false); - if (src1->is_reg()) { - buf = alu(buf, cmp_opc, *(R_Opnd *)src1, get_rm_opnd(src2_loc)); - } else if (src2_loc->kind == LLK_Gr) { - buf = alu(buf, cmp_opc, *(M_Opnd *)src1, get_r_opnd(src2_loc)); - } else { - // src1 is in memory as well as src2 - const Tmp_GR_Opnd src2_reg(context, ic); - buf = mov(buf, src2_reg, get_m_opnd(src2_loc), size_64); - buf = alu(buf, cmp_opc, *((M_Opnd *)src1), src2_reg); - } - } - delete tmp_reg; - - // 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 out(LilSig * sig) { - // nothing else to do; space has been reserved already - } - - /** - * implements the copying of incoming to outgoing args - */ - void in2out(LilSig * sig) { - assert(!lil_sig_is_arbitrary(lil_cs_get_sig(cs))); - - for (unsigned i = 0; i < context.get_num_inputs(); i++) { - const LcgEM64TLoc * in_loc = get_input(i); - const LcgEM64TLoc * out_loc = get_output(i, sig); - move_rm(out_loc, in_loc); - } - } - - void call(LilOperand * target, LilCallKind kind) { - switch (kind) { - case LCK_TailCall: { - // restore input FR & GR - unmove_inputs(); - // unwind current stack frame - epilog(); - // jump (instead of calling) - if (lil_operand_is_immed(target)) { - // check if we can perform relative call - int64 target_value = lil_operand_get_immed(target); - /* - - // TODO: relative addressing isn't supported now - // need to compute code size before emitting - - int64 offset = target_value - (int64)buf; - // sub 5 bytes for this instruction - if (fit32(offset)) { - // make a relative call - buf = jump32(buf, get_imm_opnd((int64)0, size_32)); - // label name is equal to address - char * label = (char *)mem.alloc(MAX_DEC_SIZE); - assert(sizeof(long) == sizeof(POINTER_SIZE_INT)); - sprintf(label, "%" FMT64 "d", target_value); - // offset should be patched when the stub is copied to other place - labels.define_label(label, (void *)(int64 *)target_value, false); - labels.add_patch_to_label(label, buf - 4, LPT_Rel32); - } else { - */ - // make absolute jump - buf = mov(buf, rax_opnd, get_imm_opnd(target_value, size_64), size_64); - buf = ::jump(buf, rax_opnd, size_64); - //} - } else { - const LcgEM64TLoc * loc = get_op_loc(target, false); - buf = jump(buf, get_rm_opnd(loc), size_64); - } - break; - } - case LCK_Call: - case LCK_CallNoRet: { -#ifdef _WIN64 - buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(-SHADOW), size_64); -#endif - if (lil_operand_is_immed(target)) { - // check if we can perform relative call - int64 target_value = lil_operand_get_immed(target); - - /* - - // TODO: relative addressing isn't supported now - // need to compute code size before emitting - - int64 offset = target_value - (int64)buf; - // sub 5 bytes for this instruction - if (fit32(offset)) { - // make a relative call - buf = ::call(buf, get_imm_opnd((int64)0, size_32)); - // label name is equal to address - char * label = (char *)mem.alloc(MAX_DEC_SIZE); - assert(sizeof(long) == sizeof(POINTER_SIZE_INT)); - sprintf(label, "%" FMT64 "d", target_value); - // offset should be patched when the stub is copied to other place - labels.define_label(label, (void *)(int64 *)target_value, false); - labels.add_patch_to_label(label, buf - 4, LPT_Rel32); - } else { - */ - // make absolute call - buf = mov(buf, rax_opnd, get_imm_opnd(target_value, size_64), size_64); - buf = ::call(buf, rax_opnd, size_64); - //} - } else { - const LcgEM64TLoc * loc = get_op_loc(target, false); - buf = ::call(buf, get_rm_opnd(loc), size_64); - } -#ifdef _WIN64 - buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(SHADOW), size_64); -#endif - take_inputs_from_stack = true; - break; - } - default: - ASSERT(0, "Unknown kind"); - } - } - - void ret() { - // unwind current stack frame - epilog(); - buf = ::ret(buf); - } - - // 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) { - take_inputs_from_stack = true; - - // rsp-relative offset of the top of the m2n frame - int32 offset = context.get_m2n_offset() + context.get_stk_m2n_size(); - - buf = m2n_gen_push_m2n(buf, method, current_frame_type, handles, - lil_cs_get_max_locals(cs), lil_ic_get_num_std_places(ic), offset); - } - - void m2n_save_all() { - } - - void pop_m2n() { - buf = m2n_gen_pop_m2n(buf, context.m2n_has_handles(), lil_cs_get_max_locals(cs), - // TODO: FIXME: need to define proper return registers to be preserved - context.get_m2n_offset(), 1); - // after m2n_gen_pop_m2n rsp points to the last callee-saves register - } - - void print(char * str, LilOperand * o) { - // this is a no-op if debugging is off -/* -#ifdef STUB_DEBUG - unsigned print_reg; - if (lil_operand_is_immed(o)) { - // dummy operand; print r0 - print_reg = 0; - } else { - LilVariable *var = lil_operand_get_variable(o); - const LcgEM64TLoc* var_loc = - get_var_loc(var, inst, ic, false); - assert(var_loc->kind == LLK_Gr); - print_reg = var_loc->addr; - } - emit_print_reg(emitter, str, print_reg, context.get_num_inputs(), - context.get_first_output_gr(), false); -#endif // STUB_DEBUG -*/ - } -}; - -LilCodeGeneratorEM64T::LilCodeGeneratorEM64T(): LilCodeGenerator() {} - -NativeCodePtr LilCodeGeneratorEM64T::compile_main(LilCodeStub * cs, size_t * stub_size, PoolManager* code_pool) { - // start a memory manager - tl::MemoryPool m; - // get context - LcgEM64TContext * context = new(m) LcgEM64TContext(cs, m); - // generate code - LcgEM64TCodeGen 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; -} - -GenericFunctionPointer lil_npc_to_fp(NativeCodePtr ncp) { - return (GenericFunctionPointer)ncp; -} diff --git a/vm/port/src/lil/em64t/pim/m2n_em64t.cpp b/vm/port/src/lil/em64t/pim/m2n_em64t.cpp deleted file mode 100644 index a9cc5e7..0000000 --- a/vm/port/src/lil/em64t/pim/m2n_em64t.cpp +++ /dev/null @@ -1,437 +0,0 @@ -/* - * 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. - */ - -/** - * @author Evgueni Brevnov - * @version $Revision$ - */ - -#include - -#include "open/types.h" -#include "port_malloc.h" -#include "vm_threads.h" -#include "exceptions.h" - -#include "m2n.h" -#include "encoder.h" -#include "m2n_em64t_internal.h" -#include "lil_code_generator_em64t.h" - -#define LOG_DOMAIN "vm.helpers" -#include "cxxlog.h" - -/* Generic Interface */ - -void m2n_null_init(M2nFrame * m2n){ - memset(m2n, 0, sizeof(M2nFrame)); -} - -M2nFrame* m2n_get_last_frame() { - return (M2nFrame*)p_TLS_vmthread->last_m2n_frame; -} - -M2nFrame* m2n_get_last_frame(VM_thread * thread) { - return (M2nFrame*)thread->last_m2n_frame; -} - -void m2n_set_last_frame(VM_thread* thread, M2nFrame * lm2nf) { - thread->last_m2n_frame = lm2nf; -} - -void m2n_set_last_frame(M2nFrame * lm2nf) { - vm_thread_t vm_thread = jthread_self_vm_thread_unsafe(); - vm_thread->last_m2n_frame = lm2nf; -} - -M2nFrame * m2n_get_previous_frame(M2nFrame * m2nf) { - return m2nf->prev_m2nf; -} - -ObjectHandles* m2n_get_local_handles(M2nFrame * m2nf) { - return m2nf->local_object_handles; -} - -void m2n_set_local_handles(M2nFrame * m2nf, ObjectHandles * handles) { - m2nf->local_object_handles = handles; -} - -NativeCodePtr m2n_get_ip(M2nFrame * m2nf) { - return (NativeCodePtr *) m2nf->rip; -} - -void m2n_set_ip(M2nFrame * lm2nf, NativeCodePtr ip) { - lm2nf->rip = (uint64)ip; -} - -Method_Handle m2n_get_method(M2nFrame * m2nf) { - return m2nf->method; -} - -frame_type m2n_get_frame_type(M2nFrame * m2nf) { - return m2nf->current_frame_type; -} - -void m2n_set_frame_type(M2nFrame * m2nf, frame_type m2nf_type) -{ - m2nf->current_frame_type = m2nf_type; -} - -void m2n_push_suspended_frame(M2nFrame* m2nf, Registers* regs) -{ - m2n_push_suspended_frame(p_TLS_vmthread, m2nf, regs); -} - -void m2n_push_suspended_frame(VM_thread* thread, M2nFrame* m2nf, Registers* regs) -{ - assert(m2nf); - m2nf->p_lm2nf = (M2nFrame**)1; - m2nf->method = NULL; - m2nf->local_object_handles = NULL; - m2nf->current_frame_type = FRAME_UNKNOWN; - - m2nf->rip = (POINTER_SIZE_INT)regs->get_ip(); - m2nf->regs = regs; - - m2nf->prev_m2nf = m2n_get_last_frame(thread); - m2n_set_last_frame(thread, m2nf); -} - -M2nFrame* m2n_push_suspended_frame(Registers* regs) -{ - return m2n_push_suspended_frame(p_TLS_vmthread, regs); -} - -M2nFrame* m2n_push_suspended_frame(VM_thread* thread, Registers* regs) -{ - M2nFrame* m2nf = (M2nFrame*)STD_MALLOC(sizeof(M2nFrame)); - assert(m2nf); - m2n_push_suspended_frame(thread, m2nf, regs); - return m2nf; -} - -bool m2n_is_suspended_frame(M2nFrame * m2nf) { - return (uint64)m2nf->p_lm2nf == 1; - -} - -void * m2n_get_frame_base(M2nFrame * m2nf) { - return &m2nf->rip; -} - -/* Internal Interface */ - -unsigned m2n_ts_to_register_size(unsigned num_std_need_to_save, - unsigned num_ret_need_to_save) { - return 13 + (6 * num_std_need_to_save) + - 3 + (6 * num_ret_need_to_save); -} - -// rsp should point to the bottom of the activation frame since push may occur -// inputs should be preserved outside if required since we do a call -// num_std_need_to_save registers will be preserved -char * m2n_gen_ts_to_register(char * buf, const R_Opnd * reg, - unsigned num_callee_saves_used, - unsigned num_callee_saves_max, - unsigned num_std_need_to_save, - unsigned num_ret_need_to_save) { - // we can't preserve rax and return value on it at the same time - assert (num_ret_need_to_save == 0 || reg != &rax_opnd); - - -//#ifdef PLATFORM_POSIX - - // preserve std places - unsigned i; - unsigned num_std_saved = 0; - // use calle-saves registers first - while (num_std_saved < num_std_need_to_save && - (i = num_callee_saves_used + num_std_saved) < num_callee_saves_max) { - buf = mov(buf, - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::GR_LOCALS_OFFSET + i), - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved), - size_64); - ++num_std_saved; - } - // if we still have not preserved std places save them on the stack - while (num_std_saved < num_std_need_to_save) { - buf = push(buf, - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved), - size_64); - ++num_std_saved; - } - assert(num_std_saved == num_std_need_to_save); - - // preserve returns - unsigned num_ret_saved = 0; - while (num_ret_saved < num_ret_need_to_save && - (i = num_callee_saves_used + num_std_saved + num_ret_saved) < num_callee_saves_max) { - buf = mov(buf, - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::GR_LOCALS_OFFSET + i), - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved), - size_64); - ++num_ret_saved; - } - // if we still have not preserved returns save them on the stack - while (num_ret_saved < num_ret_need_to_save) { - buf = push(buf, - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::GR_RETURNS_OFFSET + num_std_saved), - size_64); - ++num_ret_saved; - } - assert(num_ret_saved == num_ret_need_to_save); - - // TODO: FIXME: only absolute addressing mode is supported now - buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)get_thread_ptr), size_64); -#ifdef _WIN64 - buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(-SHADOW)); -#endif - buf = call(buf, rax_opnd, size_64); -#ifdef _WIN64 - buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(SHADOW)); -#endif - if (reg != &rax_opnd) { - buf = mov(buf, *reg, rax_opnd, size_64); - } - - // restore returns from the stack - i = num_callee_saves_used + num_std_saved; - while (num_ret_saved > 0 && i + num_ret_saved > num_callee_saves_max) { - --num_ret_saved; - buf = pop(buf, - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved), - size_64); - } - // restore std places from the stack - while (num_std_saved > 0 && num_callee_saves_used + num_std_saved > num_callee_saves_max) { - --num_std_saved; - buf = pop(buf, - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved), - size_64); - } - // restore returns from callee-saves registers - i = num_callee_saves_used + num_std_saved; - while (num_ret_saved > 0) { - --num_ret_saved; - buf = mov(buf, - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved), - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::GR_LOCALS_OFFSET + i + num_ret_saved), - size_64); - } - // restore std places from callee-saves registers - while (num_std_saved > 0) { - --num_std_saved; - buf = mov(buf, - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved), - LcgEM64TContext::get_reg_from_map( - LcgEM64TContext::GR_LOCALS_OFFSET + num_callee_saves_used + num_std_saved), - size_64); - } -//#else //!PLATFORM_POSIX -// buf = prefix(buf, prefix_fs); -// buf = mov(buf, *reg, M_Opnd(0x14), size_64); -//#endif //!PLATFORM_POSIX - return buf; -} - -char * m2n_gen_set_local_handles_r(char * buf, unsigned bytes_to_m2n, const R_Opnd * src_reg) { - unsigned offset_local_handles = (unsigned)(uint64) &((M2nFrame*)0)->local_object_handles; - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n + offset_local_handles), *src_reg, size_64); - return buf; -} - -char * m2n_gen_set_local_handles_imm(char * buf, unsigned bytes_to_m2n, const Imm_Opnd * imm) { - unsigned offset_local_handles = (unsigned)(uint64)&((M2nFrame*)0)->local_object_handles; - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n + offset_local_handles), *imm, size_64); - return buf; -} - -unsigned m2n_push_m2n_size(unsigned num_callee_saves, - unsigned num_std_need_to_save) { - return 91 - (5 * num_callee_saves) + - m2n_ts_to_register_size(num_std_need_to_save, 0); -} - -// inputs should be preserved outside if required since we do a call -// num_std_need_to_save registers will be preserved -char * m2n_gen_push_m2n(char * buf, Method_Handle method, - frame_type current_frame_type, - bool handles, - unsigned num_callee_saves, - unsigned num_std_need_to_save, - int32 bytes_to_m2n_top) { - // skip callee-saves registers - bytes_to_m2n_top -= num_callee_saves * LcgEM64TContext::GR_SIZE; - // TODO: check if it makes sense to save all callee-saves registers here - //store rest of callee-saves registers - for (unsigned i = num_callee_saves; i < LcgEM64TContext::MAX_GR_LOCALS; i++) { - bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; - buf = mov(buf, - M_Base_Opnd(rsp_reg, bytes_to_m2n_top), - LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_LOCALS_OFFSET + i), - size_64); - } - // init pop_regs to null - bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), - Imm_Opnd(size_32, 0), size_64); - // store current_frame_type - bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; - assert(fit32(current_frame_type)); - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), - Imm_Opnd(size_32, current_frame_type), size_64); - // store a method associated with the current m2n frame - bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; - if (fit32((int64)method)) { - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), - Imm_Opnd(size_32, (int64)method), size_64); - } else { - buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (int64)method), size_64); - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), rax_opnd); - } - // store local object handles - bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), - Imm_Opnd(size_64, (int64)0), size_64); - - // move pointer to the current VM_Thread structure to rax - buf = m2n_gen_ts_to_register(buf, &rax_opnd, - num_callee_saves, LcgEM64TContext::MAX_GR_LOCALS, - num_std_need_to_save, 0); - - // shift to the last_m2n_frame field - int32 last_m2n_frame_offset = (int32)(int64)&((VM_thread*)0)->last_m2n_frame; - buf = alu(buf, add_opc, rax_opnd, Imm_Opnd(size_32, last_m2n_frame_offset), size_64); - // store pointer to pointer to last m2n frame - bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), rax_opnd, size_64); - // save pointer to the previous m2n frame - bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; - buf = mov(buf, r9_opnd, M_Base_Opnd(rax_reg, 0)); - buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), r9_opnd, size_64); - // update last m2n frame of the current thread - buf = lea(buf, r9_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_top)); - buf = mov(buf, M_Base_Opnd(rax_reg, 0), r9_opnd, size_64); - return buf; -} - -unsigned m2n_pop_m2n_size(bool handles, unsigned num_callee_saves, unsigned preserve_ret) -{ - return 56 - 5*num_callee_saves + (preserve_ret ? 4: 0); -} - -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); -} - -static void m2n_free_local_handles() { - assert(!hythread_is_suspend_enabled()); - - if (exn_raised()) { - exn_rethrow(); - } - - M2nFrame * m2n = m2n_get_last_frame(); - free_local_object_handles3(m2n->local_object_handles); -} - -char * m2n_gen_pop_m2n(char * buf, bool handles, unsigned num_callee_saves, - int32 bytes_to_m2n_bottom, unsigned num_preserve_ret) { - assert (num_preserve_ret <= 2); - assert(LcgEM64TContext::GR_SIZE == 8); - - if (num_preserve_ret > 0) { - // Save return value - // NOTE: don't break stack allignment by pushing only one register. - buf = push(buf, rax_opnd, size_64); - buf = push(buf, rdx_opnd, size_64); - } - - if (handles) { - // There are handles located on the stack - buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)m2n_pop_local_handles), size_64); - } else { - buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)m2n_free_local_handles), size_64); - } - - // NOTE: the following should be true before the call ($rsp % 8 == 0 && $rsp % 16 != 0)! - - // Call m2n_pop_local_handles or m2n_free_local_handles -#ifdef _WIN64 - buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(-SHADOW)); -#endif - buf = call(buf, rax_opnd, size_64); -#ifdef _WIN64 - buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(SHADOW)); -#endif - - if (num_preserve_ret > 0) { - // Restore return value - buf = pop(buf, rdx_opnd, size_64); - buf = pop(buf, rax_opnd, size_64); - } - - // pop prev_m2nf - buf = mov(buf, r10_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom), size_64); - bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE; - // pop p_lm2nf - buf = mov(buf, r11_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom), size_64); - bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE; - buf = mov(buf, M_Base_Opnd(r11_reg, 0), r10_opnd, size_64); - // skip local_object_handles, method, current_frame_type, pop_regs - bytes_to_m2n_bottom += 4 * LcgEM64TContext::GR_SIZE; - - // restore part of callee-saves registers - for (int i = LcgEM64TContext::MAX_GR_LOCALS - 1; i >= (int)num_callee_saves; i--) { - buf = mov(buf, - LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_LOCALS_OFFSET + i), - M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom), - size_64); - bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE; - } - - return buf; -}//m2n_gen_pop_m2n - -// returns pointer to the registers used for jvmti PopFrame -Registers* get_pop_frame_registers(M2nFrame* m2nf) { - return m2nf->pop_regs; -} - -// sets pointer to the registers used for jvmti PopFrame -void set_pop_frame_registers(M2nFrame* m2nf, Registers* regs) { - m2nf->pop_regs = regs; -} - diff --git a/vm/port/src/lil/em64t/pim/m2n_em64t_internal.h b/vm/port/src/lil/em64t/pim/m2n_em64t_internal.h deleted file mode 100644 index 7ac8fc3..0000000 --- a/vm/port/src/lil/em64t/pim/m2n_em64t_internal.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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. - */ - -/** - * @author Evgueni Brevnov - * @version $Revision$ - */ - -#ifndef _M2N_EM64T_INTERNAL_H_ -#define _M2N_EM64T_INTERNAL_H_ - -// This file describes the internal EM64T interface of m2n frames. -// It can be used by stubs to generate code to push and pop m2n frames, to update object handles fields, and -// to access the arguments from managed to native code. -// It is also used by stack iterators. - -#include "m2n.h" -#include "open/types.h" -#include "encoder.h" - -#ifdef _WIN64 -const unsigned m2n_sizeof_m2n_frame = 120; -#else -const unsigned m2n_sizeof_m2n_frame = 104; -#endif - -typedef struct M2nFrame M2nFrame; - -/** - * There are two types of M2nFrames: those that result from managed code calling a stub, - * and those that represent suspended managed code. The second type is needed to deal with - * throwing exceptions from OS contexts with the exception filter or signal mechanisms. - * For the first type: - * rip points to the instruction past the one in question - * the bottom two bits of p_lm2nf are zero - * regs is not present, and is implicitly the address of the word above rip - * For the second type: - * rip points to the instruction in question - * p_lm2nf==1 - * regs is present - */ -struct M2nFrame { - M2nFrame * prev_m2nf; - M2nFrame ** p_lm2nf; - ObjectHandles * local_object_handles; - Method_Handle method; - frame_type current_frame_type; - Registers* pop_regs; // This is only for M2nFrames for suspended managed code (as against ones that call stubs and prepare jvmtiPopFrame) - uint64 rbx; - uint64 rbp; -#ifdef _WIN64 - uint64 rsi; - uint64 rdi; -#endif - uint64 r15; - uint64 r14; - uint64 r13; - uint64 r12; - uint64 rip; - // This is only for M2nFrames for suspended managed code (as against ones that call stubs) - Registers * regs; -}; - -/** - * returns size of m2n frame in bytes - */ -inline size_t m2n_get_size() { - return sizeof(M2nFrame); -} - -/** - * Generate code to put the thread local storage pointer into a given register. - * It destroys outputs. - */ -char * m2n_gen_ts_to_register(char * buf, const R_Opnd * reg, - unsigned num_callee_saves_used, unsigned num_callee_saves_max, - unsigned num_std_need_to_save, unsigned num_ret_need_to_save); -unsigned m2n_ts_to_register_size(unsigned num_std_need_to_save, unsigned num_ret_need_to_save); - -/** - * Generate code to set the local handles of an M2nFrame. - * The M2nFrame is located bytes_to_m2n above rsp, and src_reg has the address of the frames. - */ -char * m2n_gen_set_local_handles_r(char * buf, unsigned bytes_to_m2n, const R_Opnd * src_reg); -char * m2n_gen_set_local_handles_imm(char * buf, unsigned bytes_to_m2n, const Imm_Opnd * imm); - -/** - * Generate code to push an M2nFrame onto the stack. - * It assumes that num_callee_saves registers have already been saved and the rest have been preserved, - * that the saved registers are immediately below the return address, and that rsp points to the last one saved. - * The order for callee saves is r12, r13, r14, r15, rbp, rbx. - * It destroys returns (rax) and outputs. - * After the sequence, rsp points to the M2nFrame. - * - * @param handles Indicates whether the stub will want local handles or not - * @param bytes_to_m2n_top Number of bytes to the beginning of m2n frame relative to the current rsp value. - Negative value means that current rsp is above m2n bottom. - */ -char * m2n_gen_push_m2n(char * buf, Method_Handle method, frame_type current_frame_type, bool handles, - unsigned num_callee_saves, unsigned num_std_need_to_save, int32 bytes_to_m2n_top); -unsigned m2n_push_m2n_size(unsigned num_callee_saves, unsigned num_std_need_to_save); - -/** - * Generate code to pop an M2nFrame off the stack. - * @param num_callee_saves Number of callee saves registers to leave - * on the stack as at the entry to push_m2n. - * @param bytes_to_m2n_bottom Number of bytes between rsp and the bottom of the M2nFrame. - * @param preserve_ret Number of return registers to preserve, 0 means none, - * 1 means rax, 2 means rax & rdx. - * @param handles As for push_m2n, frees the handles if true. - */ -char * m2n_gen_pop_m2n(char * buf, bool handles, unsigned num_callee_saves, - int32 bytes_to_m2n_bottom, unsigned preserve_ret); -unsigned m2n_pop_m2n_size(bool handles, unsigned num_callee_saves, unsigned preserve_ret); - -// returns top of the specified frame on the stack (it should point to return ip) -void * m2n_get_frame_base(M2nFrame *); - -#endif // _M2N_EM64T_INTERNAL_H_ diff --git a/vm/port/src/lil/em64t/pim/stack_iterator_em64t.cpp b/vm/port/src/lil/em64t/pim/stack_iterator_em64t.cpp deleted file mode 100644 index a57c909..0000000 --- a/vm/port/src/lil/em64t/pim/stack_iterator_em64t.cpp +++ /dev/null @@ -1,543 +0,0 @@ -/* - * 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. - */ -/** - * @author Evgueni Brevnov - * @version $Revision: 1.1.2.1.4.3 $ - */ - -#include - -#include "environment.h" -#include "stack_iterator.h" -#include "vm_threads.h" -#include "method_lookup.h" -#include "jit_intf_cpp.h" -#include "encoder.h" -#include "m2n.h" -#include "m2n_em64t_internal.h" -#include "nogc.h" -#include "interpreter.h" // for ASSERT_NO_INTERPRETER -#include "cci.h" - -#include "dump.h" -#include "vm_stats.h" - -#include "cxxlog.h" - -// see stack_iterator_ia32.cpp -struct StackIterator { - CodeChunkInfo * cci; - JitFrameContext jit_frame_context; - M2nFrame * m2n_frame; - uint64 ip; -}; - -////////////////////////////////////////////////////////////////////////// -// Utilities - -static void si_copy(StackIterator * dst, const StackIterator * src) { - memcpy(dst, src, sizeof(StackIterator)); - // If src uses itself for ip then the dst should also do - // to avoid problems if src is deallocated first. - if (src->jit_frame_context.p_rip == &src->ip) { - dst->jit_frame_context.p_rip = &dst->ip; - } -} - -static void init_context_from_registers(JitFrameContext & context, - Registers & regs, bool is_ip_past) { - context.rsp = regs.rsp; - context.p_rbp = ®s.rbp; - context.p_rip = ®s.rip; - - context.p_rbx = ®s.rbx; - context.p_r12 = ®s.r12; - context.p_r13 = ®s.r13; - context.p_r14 = ®s.r14; - context.p_r15 = ®s.r15; - - context.p_rax = ®s.rax; - context.p_rcx = ®s.rcx; - context.p_rdx = ®s.rdx; - context.p_rsi = ®s.rsi; - context.p_rdi = ®s.rdi; - context.p_r8 = ®s.r8; - context.p_r9 = ®s.r9; - context.p_r10 = ®s.r10; - context.p_r11 = ®s.r11; - - context.eflags = regs.eflags; - - context.is_ip_past = is_ip_past; -} - - -// Goto the managed frame immediately prior to m2nfl -static void si_unwind_from_m2n(StackIterator * si) { -#ifdef VM_STATS - VM_Statistics::get_vm_stats().num_unwind_native_frames_all++; -#endif - - M2nFrame * current_m2n_frame = si->m2n_frame; - assert(current_m2n_frame); - - si->m2n_frame = m2n_get_previous_frame(current_m2n_frame); - - TRACE2("si", "si_unwind_from_m2n, ip = " - << (void*)current_m2n_frame->rip); - - // Is it a normal M2nFrame or one for suspended managed code? - if (m2n_is_suspended_frame(current_m2n_frame)) { - // Suspended managed code, rip is at instruction, - // rsp & registers are in regs structure - TRACE2("si", "si_unwind_from_m2n from suspended managed code, ip = " - << (void*)current_m2n_frame->regs->rip); - init_context_from_registers(si->jit_frame_context, *current_m2n_frame->regs, false); - } else { - // Normal M2nFrame, rip is past instruction, - // rsp is implicitly address just beyond the frame, - // callee saves registers in M2nFrame - - si->jit_frame_context.rsp = (uint64)((uint64*) m2n_get_frame_base(current_m2n_frame) + 1); - - si->jit_frame_context.p_rbp = ¤t_m2n_frame->rbp; - si->jit_frame_context.p_rip = ¤t_m2n_frame->rip; - -#ifdef _WIN64 - si->jit_frame_context.p_rdi = ¤t_m2n_frame->rdi; - si->jit_frame_context.p_rsi = ¤t_m2n_frame->rsi; -#endif - si->jit_frame_context.p_rbx = ¤t_m2n_frame->rbx; - si->jit_frame_context.p_r12 = ¤t_m2n_frame->r12; - si->jit_frame_context.p_r13 = ¤t_m2n_frame->r13; - si->jit_frame_context.p_r14 = ¤t_m2n_frame->r14; - si->jit_frame_context.p_r15 = ¤t_m2n_frame->r15; - si->jit_frame_context.is_ip_past = true; - } -} - -static char* get_reg(char* ss, const R_Opnd & dst, Reg_No base, int64 offset, - bool check_null = false, bool preserve_flags = false) -{ - char* patch_offset = NULL; - - ss = mov(ss, dst, M_Base_Opnd(base, (int32)offset)); - - if (check_null) - { - if (preserve_flags) - *ss++ = (char)0x9C; // PUSHFD - - ss = test(ss, dst, dst); - ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0)); - patch_offset = ((char*)ss) - 1; // Store location for jump patch - } - - ss = mov(ss, dst, M_Base_Opnd(dst.reg_no(), 0)); - - if (check_null) - { - // Patch conditional jump - POINTER_SIZE_SINT offset = - (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)patch_offset - 1; - assert(offset >= -128 && offset < 127); - *patch_offset = (char)offset; - - if (preserve_flags) - *ss++ = (char)0x9D; // POPFD - } - - return ss; -} - -typedef void (* transfer_control_stub_type)(StackIterator *); - -#define CONTEXT_OFFSET(_field_) \ - ((int64)&((StackIterator*)0)->jit_frame_context._field_) - -static transfer_control_stub_type gen_transfer_control_stub() -{ - static transfer_control_stub_type addr = NULL; - - if (addr) { - return addr; - } - - const int STUB_SIZE = 247; - char * stub = (char *)malloc_fixed_code_for_jit(STUB_SIZE, - DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_COLD, CAA_Allocate); - char * ss = stub; -#ifndef NDEBUG - memset(stub, 0xcc /*int 3*/, STUB_SIZE); -#endif - - // - // ************* LOW LEVEL DEPENDENCY! *************** - // This code sequence must be atomic. The "atomicity" effect is achieved by - // changing the rsp at the very end of the sequence. - - // rdx holds the pointer to the stack iterator -#if defined (PLATFORM_POSIX) // RDI holds 1st parameter on Linux - ss = mov(ss, rdx_opnd, rdi_opnd); -#else // RCX holds 1st parameter on Windows - ss = mov(ss, rdx_opnd, rcx_opnd); -#endif - - // Restore general registers - ss = get_reg(ss, rbp_opnd, rdx_reg, CONTEXT_OFFSET(p_rbp), false); - ss = get_reg(ss, rbx_opnd, rdx_reg, CONTEXT_OFFSET(p_rbx), true); - ss = get_reg(ss, r12_opnd, rdx_reg, CONTEXT_OFFSET(p_r12), true); - ss = get_reg(ss, r13_opnd, rdx_reg, CONTEXT_OFFSET(p_r13), true); - ss = get_reg(ss, r14_opnd, rdx_reg, CONTEXT_OFFSET(p_r14), true); - ss = get_reg(ss, r15_opnd, rdx_reg, CONTEXT_OFFSET(p_r15), true); - ss = get_reg(ss, rsi_opnd, rdx_reg, CONTEXT_OFFSET(p_rsi), true); - ss = get_reg(ss, rdi_opnd, rdx_reg, CONTEXT_OFFSET(p_rdi), true); - ss = get_reg(ss, r8_opnd, rdx_reg, CONTEXT_OFFSET(p_r8), true); - ss = get_reg(ss, r9_opnd, rdx_reg, CONTEXT_OFFSET(p_r9), true); - ss = get_reg(ss, r10_opnd, rdx_reg, CONTEXT_OFFSET(p_r10), true); - ss = get_reg(ss, r11_opnd, rdx_reg, CONTEXT_OFFSET(p_r11), true); - - // Get the new RSP - M_Base_Opnd saved_rsp(rdx_reg, CONTEXT_OFFSET(rsp)); - ss = mov(ss, rax_opnd, saved_rsp); - // Store it over return address for future use - ss = mov(ss, M_Base_Opnd(rsp_reg, 0), rax_opnd); - // Get the new RIP - ss = get_reg(ss, rcx_opnd, rdx_reg, CONTEXT_OFFSET(p_rip), false); - // Store RIP to [ - 136] to preserve 128 bytes under RSP - // which are 'reserved' on Linux - ss = mov(ss, M_Base_Opnd(rax_reg, -136), rcx_opnd); - - ss = get_reg(ss, rax_opnd, rdx_reg, CONTEXT_OFFSET(p_rax), true); - - // Restore processor flags - ss = alu(ss, xor_opc, rcx_opnd, rcx_opnd); - ss = mov(ss, rcx_opnd, M_Base_Opnd(rdx_reg, CONTEXT_OFFSET(eflags)), size_8); - ss = test(ss, rcx_opnd, rcx_opnd); - ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0)); - char* patch_offset = ((char*)ss) - 1; // Store location for jump patch - ss = alu(ss, and_opc, rcx_opnd, Imm_Opnd(size_32, 0xff)); - ss = push(ss, rcx_opnd); - *ss++ = (char)0x9D; // POPFD - // Patch conditional jump - POINTER_SIZE_SINT offset = - (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)patch_offset - 1; - *patch_offset = (char)offset; - - ss = get_reg(ss, rcx_opnd, rdx_reg, CONTEXT_OFFSET(p_rcx), true, true); - ss = get_reg(ss, rdx_opnd, rdx_reg, CONTEXT_OFFSET(p_rdx), true, true); - - // Setup stack pointer to previously saved value - ss = mov(ss, rsp_opnd, M_Base_Opnd(rsp_reg, 0)); - - // Jump to address stored to [ - 136] - ss = jump(ss, M_Base_Opnd(rsp_reg, -136)); - - addr = (transfer_control_stub_type)stub; - assert(ss-stub <= STUB_SIZE); - - /* - The following code will be generated: - - mov rdx,rcx - mov rbp,qword ptr [rdx+10h] - mov rbp,qword ptr [rbp] - mov rbx,qword ptr [rdx+20h] - test rbx,rbx - je __label1__ - mov rbx,qword ptr [rbx] -__label1__ - ; .... The same for r12,r13,r14,r15,rsi,rdi,r8,r9,r10 - mov r11,qword ptr [rdx+88h] - test r11,r11 - je __label11__ - mov r11,qword ptr [r11] -__label11__ - mov rax,qword ptr [rdx+8] - mov qword ptr [rsp],rax - mov rcx,qword ptr [rdx+18h] - mov rcx,qword ptr [rcx] - mov qword ptr [rax-88h],rcx - mov rax,qword ptr [rdx+48h] - test rax,rax - je __label12__ - mov rax,qword ptr [rax] -__label12__ - xor rcx,rcx - mov ecx,dword ptr [rdx+90h] - test rcx,rcx - je __label13__ - push rcx - popfq -__label13__ - mov rcx,qword ptr [rdx+50h] - pushfq - test rcx,rcx - je __label14__ - mov rcx,qword ptr [rcx] -__label14__ - popfq - mov rdx,qword ptr [rdx+58h] - pushfq - test rdx,rdx - je __label15__ - mov rdx,qword ptr [rdx] -__label15__ - popfq - mov rsp,qword ptr [rsp] - jmp qword ptr [rsp-88h] - */ - - DUMP_STUB(stub, "getaddress__transfer_control", ss-stub); - - return addr; -} - -#undef CONTEXT_OFFSET -////////////////////////////////////////////////////////////////////////// -// Stack Iterator Interface - -StackIterator * si_create_from_native() { - return si_create_from_native(p_TLS_vmthread); -} - -void si_fill_from_native(StackIterator* si) { - si_fill_from_native(si, p_TLS_vmthread); -} - - -StackIterator * si_create_from_native(VM_thread * thread) { - ASSERT_NO_INTERPRETER - // Allocate iterator - StackIterator * si = (StackIterator *)STD_MALLOC(sizeof(StackIterator)); - - si_fill_from_native(si, thread); - return si; -} - -void si_fill_from_native(StackIterator* si, VM_thread * thread) { - memset(si, 0, sizeof(StackIterator)); - - si->cci = NULL; - si->jit_frame_context.p_rip = &si->ip; - si->m2n_frame = m2n_get_last_frame(thread); - si->ip = 0; -} - -StackIterator * si_create_from_registers(Registers * regs, bool is_ip_past, - M2nFrame * lm2nf) { - ASSERT_NO_INTERPRETER - // Allocate iterator - StackIterator * si = (StackIterator *)STD_MALLOC(sizeof(StackIterator)); - assert(si); - - si_fill_from_registers(si, regs, is_ip_past, lm2nf); - - return si; -} - -void si_fill_from_registers(StackIterator* si, Registers* regs, bool is_ip_past, M2nFrame* lm2nf) -{ - memset(si, 0, sizeof(StackIterator)); - - Global_Env *env = VM_Global_State::loader_env; - // Setup current frame - // It's possible that registers represent native code and res->cci==NULL - si->cci = env->vm_methods->find((NativeCodePtr)regs->rip, is_ip_past); - init_context_from_registers(si->jit_frame_context, *regs, is_ip_past); - - si->m2n_frame = lm2nf; - si->ip = regs->rip; -} - -size_t si_size(){ - return sizeof(StackIterator); -} - -// On EM64T all registers are preserved automatically, so this is a nop. -void si_transfer_all_preserved_registers(StackIterator *) { - ASSERT_NO_INTERPRETER - // Do nothing -} - -bool si_is_past_end(StackIterator * si) { - ASSERT_NO_INTERPRETER - // check if current position neither corresponds - // to jit frame nor to m2n frame - return si->cci == NULL && si->m2n_frame == NULL; -} - -void si_goto_previous(StackIterator * si, bool over_popped) { - ASSERT_NO_INTERPRETER - if (si_is_native(si)) { - TRACE2("si", "si_goto_previous from ip = " - << (void*)si_get_ip(si) << " (M2N)"); - if (si->m2n_frame == NULL) return; - si_unwind_from_m2n(si); - } else { - assert(si->cci->get_jit() && si->cci->get_method()); - TRACE2("si", "si_goto_previous from ip = " - << (void*)si_get_ip(si) << " (" - << method_get_name(si->cci->get_method()) - << method_get_descriptor(si->cci->get_method()) << ")"); - si->cci->get_jit()->unwind_stack_frame(si->cci->get_method(), si_get_jit_context(si)); - si->jit_frame_context.is_ip_past = TRUE; - } - - Global_Env *vm_env = VM_Global_State::loader_env; - si->cci = vm_env->vm_methods->find(si_get_ip(si), si_get_jit_context(si)->is_ip_past); -#ifndef NDEBUG - if (si_is_native(si)) { - TRACE2("si", "si_goto_previous to ip = " << (void*)si_get_ip(si) - << " (M2N)"); - } else { - TRACE2("si", "si_goto_previous to ip = " << (void*)si_get_ip(si) - << " (" << method_get_name(si->cci->get_method()) - << method_get_descriptor(si->cci->get_method()) << ")"); - } -#endif -} - -StackIterator * si_dup(StackIterator * si) { - ASSERT_NO_INTERPRETER - StackIterator * dup_si = (StackIterator *)STD_MALLOC(sizeof(StackIterator)); - si_copy(dup_si, si); - return dup_si; -} - -void si_free(StackIterator * si) { - STD_FREE(si); -} - -void* si_get_sp(StackIterator* si) { - return (void*)si->jit_frame_context.rsp; -} - -NativeCodePtr si_get_ip(StackIterator * si) { - ASSERT_NO_INTERPRETER - return (NativeCodePtr)(*si->jit_frame_context.p_rip); -} - -void si_set_ip(StackIterator * si, NativeCodePtr ip, bool also_update_stack_itself) { - if (also_update_stack_itself) { - *(si->jit_frame_context.p_rip) = (uint64)ip; - } else { - si->ip = (uint64)ip; - si->jit_frame_context.p_rip = &si->ip; - } -} - -// 20040713 Experimental: set the code chunk in the stack iterator -void si_set_code_chunk_info(StackIterator * si, CodeChunkInfo * cci) { - ASSERT_NO_INTERPRETER - assert(si); - si->cci = cci; -} - -CodeChunkInfo * si_get_code_chunk_info(StackIterator * si) { - return si->cci; -} - -JitFrameContext * si_get_jit_context(StackIterator * si) { - return &si->jit_frame_context; -} - -bool si_is_native(StackIterator * si) { - ASSERT_NO_INTERPRETER - return si->cci == NULL; -} - -M2nFrame * si_get_m2n(StackIterator * si) { - ASSERT_NO_INTERPRETER - return si->m2n_frame; -} - -void** si_get_return_pointer(StackIterator* si) -{ - return (void**)si->jit_frame_context.p_rax; -} - -void si_set_return_pointer(StackIterator * si, void ** return_value) { - // TODO: check if it is needed to dereference return_value - si->jit_frame_context.p_rax = (uint64 *)return_value; -} - -void si_transfer_control(StackIterator * si) { - // !!! NO LOGGER IS ALLOWED IN THIS FUNCTION !!! - // !!! RELEASE BUILD WILL BE BROKEN !!! - // !!! NO TRACE2, INFO, WARN, ECHO, ASSERT, ... - - // 1. Copy si to stack - StackIterator local_si; - si_copy(&local_si, si); - //si_free(si); - - // 2. Set the M2nFrame list - m2n_set_last_frame(local_si.m2n_frame); - - // 3. Call the stub - transfer_control_stub_type tcs = gen_transfer_control_stub(); - tcs(&local_si); -} - -inline static uint64 unref_reg(uint64* p_reg) { - return p_reg ? *p_reg : 0; -} - -void si_copy_to_registers(StackIterator * si, Registers * regs) { - ASSERT_NO_INTERPRETER - - regs->rsp = si->jit_frame_context.rsp; - regs->rbp = unref_reg(si->jit_frame_context.p_rbp); - regs->rip = unref_reg(si->jit_frame_context.p_rip); - - regs->rbx = unref_reg(si->jit_frame_context.p_rbx); - regs->r12 = unref_reg(si->jit_frame_context.p_r12); - regs->r13 = unref_reg(si->jit_frame_context.p_r13); - regs->r14 = unref_reg(si->jit_frame_context.p_r14); - regs->r15 = unref_reg(si->jit_frame_context.p_r15); - - regs->rax = unref_reg(si->jit_frame_context.p_rax); - regs->rcx = unref_reg(si->jit_frame_context.p_rcx); - regs->rdx = unref_reg(si->jit_frame_context.p_rdx); - regs->rsi = unref_reg(si->jit_frame_context.p_rsi); - regs->rdi = unref_reg(si->jit_frame_context.p_rdi); - regs->r8 = unref_reg(si->jit_frame_context.p_r8); - regs->r9 = unref_reg(si->jit_frame_context.p_r9); - regs->r10 = unref_reg(si->jit_frame_context.p_r10); - regs->r11 = unref_reg(si->jit_frame_context.p_r11); - - regs->eflags = si->jit_frame_context.eflags; -} - -void si_set_callback(StackIterator* si, NativeCodePtr* callback) { -#ifdef WIN32 - // Shadow memory to save 4 registers into stack, - // this is necessary for WIN64 calling conventions. - // NOTE: This file is used only for x86_64 architectures - const static uint64 red_zone_size = 0x28; -#else - const static uint64 red_zone_size = 0x88; -#endif - si->jit_frame_context.rsp = si->jit_frame_context.rsp - red_zone_size - sizeof(void*); - *((uint64*) si->jit_frame_context.rsp) = *(si->jit_frame_context.p_rip); - si->jit_frame_context.p_rip = ((uint64*)callback); -} - -void si_reload_registers() { - // Nothing to do -} diff --git a/vm/port/src/lil/ia32/pim/include/lil_code_generator_ia32.h b/vm/port/src/lil/ia32/pim/include/lil_code_generator_ia32.h deleted file mode 100644 index bbd361a..0000000 --- a/vm/port/src/lil/ia32/pim/include/lil_code_generator_ia32.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#ifndef _LIL_CODE_GENERATOR_IA32_ -#define _LIL_CODE_GENERATOR_IA32_ - -#include "lil.h" -#include "lil_code_generator.h" - -class LilCodeGeneratorIa32 : public LilCodeGenerator { - - public: - LilCodeGeneratorIa32(); - - protected: - 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 deleted file mode 100644 index 71612cd..0000000 --- a/vm/port/src/lil/ia32/pim/lil_code_generator_ia32.cpp +++ /dev/null @@ -1,1489 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.2.4.5 $ - */ - - -#include -#include - -#define LOG_DOMAIN "vm.helpers" -#include "cxxlog.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" -#include "jit_runtime_support_common.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() -{ -} - -/////////////////////////////////////// -// Prepass information - -enum LcgIa32OpLocType { LOLT_Stack, LOLT_Reg, LOLT_Immed, LOLT_Tofs }; - -struct LcgIa32OpLoc { - LcgIa32OpLocType t; - union { - unsigned v; // Offset for stack, value for immediate - struct { - R_Opnd* r1; - R_Opnd* r2; - } r; // For register - } u; -}; - -// 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; -}; - -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; -}; - -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; -}; - -// Push arguments left to right or right to left -enum LilArgOrder { LAO_L2r, LAO_R2l }; - -struct LcgIa32CcInfo { - LilArgOrder arg_order; - bool callee_pop; -}; - -struct LcgIa32Context { - LcgIa32PrePassInfo* info; - LilInstructionContext* ctxt; - LcgIa32CcInfo entry_cc; - LilSig* entry_sig; - LcgIa32CcInfo out_cc; -}; - - -/////////////////////////////////////// -// Stack layout stuff - -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"); - } -} - -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(;;); - } -} - -static unsigned type_number_regs(LilType t) -{ - return (t==LT_Void ? 0 : type_in_two_regs(t) ? 2 : 1); -} - -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); - } -} - -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(;;); - } -} - -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; -} - -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; -} - -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; -} - -/////////////////////////////////////// -// Operand conversion - -// 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; - - 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(); - } - - 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(;;); - } -} - -// 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 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); - } - 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: - { - 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; - } - 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"); - } -} - -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); - } -} - -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; - } - } - - 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; - } - } - - 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); -} - -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; -} - -////////////////////////////////////////////////////////////////////////// -// Pre Pass - -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)); - } - - 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; - } - - void label(LilLabel UNREF l) { } - void locals(unsigned) { } - void std_places(unsigned) { } - - 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); - } - - 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++); - } - ii->mov_to_dst = true; - } - } - - 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); - } - 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"); - } - } - 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); - } - - 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)); - } - - 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); - } - - 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 - } - - 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); - } - - 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 - } - - void j(LilLabel) - { - info->size += 2; - js++; - } - - 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; - } - 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:; - } - } - 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); - } - - 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; - } - - void call(LilOperand* o, LilCallKind k) - { - operand_to_location(&ii->loc1, &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); - } - break;} - case LCK_CallNoRet: - info->size += 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++; - } - - void push_m2n(Method_Handle UNREF method, frame_type UNREF current_frame_type, bool handles) - { - m2n_ops++; - info->size += m2n_push_m2n_size(handles, 4); - } - - void m2n_save_all() - { - // This is a nop on IA32 - } - - void pop_m2n() - { - m2n_ops++; - unsigned num = m2n_base(&ctxt); - switch (lil_ic_get_ret_type(ctxt.ctxt)) { - case LT_Void: - case LT_F4: - case LT_F8: - ii->u.pop_m2n = 0; - break; - case LT_G8: - ii->u.pop_m2n = 2; - break; - case LT_G1: - case LT_G2: - case LT_G4: - case LT_Ref: - case LT_PInt: - ii->u.pop_m2n = 1; - break; - default: ASSERT(0, "Unknown LIL type"); - } - 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; - } - - 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; - } - -private: - tl::MemoryPool* mem; - LilCodeStub* cs; - LcgIa32PrePassInfo* info; - LcgIa32Context ctxt; - LcgIa32InstInfo* ii; - unsigned rets, m2n_ops, js, jcs, tailcalls; -}; - -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(); - } - - ppv.finalise_size(); - - *data = info; - return info->size; -} - -////////////////////////////////////////////////////////////////////////// -// Movement - -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(;;); - } -} - -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"); - } -} - -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"); - } -} - -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(;;); - } -} - -////////////////////////////////////////////////////////////////////////// -// 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) - { - 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)); - } - - 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); - } - - void locals(unsigned UNREF num) - { - // nothing to do; everything is taken care of by get_num_regs_for_locals - } - - void std_places(unsigned UNREF num) - { - // nothing to do; everything is taken care of by get_num_regs_for_locals - } - - 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 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)); - } - } - 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)); - 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"); - } - 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)); - 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); - } - 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"); - } - break; - default: ASSERT(0, "Unexpected operartion"); - } - 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) - { - *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); - } - - void handles(LilOperand*) - { - 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); - } - - 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); - 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); - } - 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 - } - 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)); - } 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)); - } - } - - 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* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel, - LilOperand* UNREF cmp, LilOperand* UNREF src, LilLabel l) - { - *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); - } - - 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 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)); - } - } - } 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"); - } - } - 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)); - } - } - - 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 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"); - } - } - - void call(LilOperand* UNREF o, LilCallKind k) - { - 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)); - } - } - 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"); - } - break; - default: ASSERT(0, "Unexpected call kind"); - } - } - - 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)); - } - } - - 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); - } - - void ret() - { - adjust_stack_for_return(); - unsigned sz = sig_size_on_stack(ctxt.entry_sig); - - if (ctxt.entry_cc.callee_pop) { - if (lil_sig_get_cc(ctxt.entry_sig) == LCC_Managed) { - // Managed calling convention assumes callee responsibility to - // handle alignment properly. Assuming that arguments were aligned, - // size of input arguments plus return pointer on the stack also should be aligned - sz += sizeof(POINTER_SIZE_INT); - sz = (sz + (MANAGED_STACK_ALIGNMENT - 1)) & ~(MANAGED_STACK_ALIGNMENT - 1); - sz -= sizeof(POINTER_SIZE_INT); - } - if (sz != 0) { - *buf = ::ret(*buf, Imm_Opnd(sz)); - return; - } - } - *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); - } - - void m2n_save_all() - { - // This is a nop on IA32 - } - - 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 print(char *, LilOperand *) { - // not implemented on ia32 - } - - 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) -{ - 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; -} - - -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 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 deleted file mode 100644 index 7ee3e49..0000000 --- a/vm/port/src/lil/ia32/pim/m2n_ia32.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - -#include "open/types.h" -#include "open/hythread.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 "exceptions.h" - - -////////////////////////////////////////////////////////////////////////// -// M2nFrame Interface - -//***** Generic Interface - -// fill m2n frame as empty -void m2n_null_init(M2nFrame* m2n){ - memset(m2n, 0, sizeof(M2nFrame)); -} - -VMEXPORT // temporary solution for interpreter unplug -M2nFrame* m2n_get_last_frame() -{ - return (M2nFrame*)p_TLS_vmthread->last_m2n_frame; -} - -VMEXPORT // temporary solution for interpreter unplug -M2nFrame* m2n_get_last_frame(VM_thread* thread) -{ - return (M2nFrame*)thread->last_m2n_frame; -} - -VMEXPORT // temporary solution for interpreter unplug -void m2n_set_last_frame(M2nFrame* lm2nf) -{ - vm_thread_t vm_thread = jthread_self_vm_thread_unsafe(); - vm_thread->last_m2n_frame = lm2nf; -} - -VMEXPORT -void m2n_set_last_frame(VM_thread* thread, M2nFrame* lm2nf) -{ - thread->last_m2n_frame = lm2nf; -} - -VMEXPORT // temporary solution for interpreter unplug -M2nFrame* m2n_get_previous_frame(M2nFrame* lm2nf) -{ - return lm2nf->prev_m2nf; -} - -VMEXPORT // temporary solution for interpreter unplug -ObjectHandles* m2n_get_local_handles(M2nFrame* lm2nf) -{ - return lm2nf->local_object_handles; -} - -VMEXPORT // temporary solution for interpreter unplug -void m2n_set_local_handles(M2nFrame* lm2nf, ObjectHandles* h) -{ - lm2nf->local_object_handles = h; -} - -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; -} - -Method_Handle m2n_get_method(M2nFrame* m2nf) -{ - ASSERT_NO_INTERPRETER - return m2nf->method; -} - -// Returns type of noted m2n frame -frame_type m2n_get_frame_type(M2nFrame* m2nf) { - return m2nf->current_frame_type; -} - -// Sets type of noted m2n frame -void m2n_set_frame_type(M2nFrame* m2nf, frame_type m2nf_type) { - m2nf->current_frame_type = m2nf_type; -} - -size_t m2n_get_size() { - return sizeof(M2nFrame); -} - -void m2n_push_suspended_frame(M2nFrame* m2nf, Registers* regs) -{ - m2n_push_suspended_frame(p_TLS_vmthread, m2nf, regs); -} - -void m2n_push_suspended_frame(VM_thread* thread, M2nFrame* m2nf, Registers* regs) -{ - assert(m2nf); - m2nf->p_lm2nf = (M2nFrame**)1; - m2nf->method = NULL; - m2nf->local_object_handles = NULL; - m2nf->current_frame_type = FRAME_UNKNOWN; - - m2nf->eip = regs->eip; - m2nf->regs = regs; - - m2nf->prev_m2nf = m2n_get_last_frame(thread); - m2n_set_last_frame(thread, m2nf); -} - -M2nFrame* m2n_push_suspended_frame(Registers* regs) -{ - return m2n_push_suspended_frame(p_TLS_vmthread, regs); -} - -M2nFrame* m2n_push_suspended_frame(VM_thread* thread, Registers* regs) -{ - M2nFrame* m2nf = (M2nFrame*)STD_MALLOC(sizeof(M2nFrame)); - assert(m2nf); - m2n_push_suspended_frame(thread, m2nf, regs); - return m2nf; -} - -bool m2n_is_suspended_frame(M2nFrame * m2nf) { - return (uint32)m2nf->p_lm2nf == 1; - -} - -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; -} - -unsigned m2n_ts_to_register_size() -{ - return 22; -} - -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, *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; -} - -char* m2n_gen_push_m2n(char* buf, Method_Handle method, frame_type current_frame_type, bool UNREF handles, unsigned num_callee_saves) -{ - // 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 ); - - return buf; -} - -unsigned m2n_set_local_handles_size(unsigned bytes_to_m2n) -{ - unsigned num = bytes_to_m2n + (unsigned)&((M2nFrame*)0)->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; - buf = mov(buf, M_Base_Opnd(esp_reg, bytes_to_m2n+offset_local_handles), *src_reg); - return buf; -} - -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; -} - -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); -} - -static void m2n_free_local_handles() { - assert(!hythread_is_suspend_enabled()); - - if (exn_raised()) { - exn_rethrow(); - } - - M2nFrame * m2n = m2n_get_last_frame(); - 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); - } - } - - if (handles) { - // There are handles located on the stack - buf = call(buf, (char*)m2n_pop_local_handles); - } else { - buf = call(buf, (char*)m2n_free_local_handles); - } - - if (preserve_ret > 0) { - // Restore return value - if (preserve_ret > 1) { - buf = pop(buf, edx_opnd); - } - buf = pop(buf, eax_opnd); - } - - // pop "garbage" from the stack - if (extra_on_stack) { - Imm_Opnd imm(extra_on_stack); - buf = alu(buf, add_opc, esp_opnd, imm); - } - - // Unlink the M2nFrame from the list of the current thread - buf = pop(buf, esi_opnd); - buf = pop(buf, ebx_opnd); - buf = mov(buf, M_Base_Opnd(ebx_reg, +0), esi_opnd); - buf = alu(buf, add_opc, esp_opnd, Imm_Opnd(+16)); - - // TODO: check if there is no need to restore callee saved registers - // JUSTIFICATION: m2n frame is popped as a result of "normal" - // (opposite to destuctive) stack unwinding - // Restore callee saved general registers - if (num_callee_saves<4) buf = pop(buf, edi_opnd); - if (num_callee_saves<3) buf = pop(buf, esi_opnd); - if (num_callee_saves<2) buf = pop(buf, ebx_opnd); - if (num_callee_saves<1) buf = pop(buf, ebp_opnd); - - return buf; -} - -// returns pointer to the registers used for jvmti PopFrame -Registers* get_pop_frame_registers(M2nFrame* m2nf) { - return m2nf->pop_regs; -} - -// sets pointer to the registers used for jvmti PopFrame -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 deleted file mode 100644 index 40e00d4..0000000 --- a/vm/port/src/lil/ia32/pim/m2n_ia32_internal.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#ifndef _M2N_IA32_INTERNAL_H_ -#define _M2N_IA32_INTERNAL_H_ - -// This file describes the internal IPF interface of m2n frames. -// It can be used by stubs to generate code to push and pop m2n frames, to update object handles fields, and -// to access the arguments from managed to native code. -// It is also used by stack iterators. - -#include "m2n.h" -#include "vm_threads.h" -#include "open/types.h" - -class R_Opnd; - -// Return a pointer to the argument just above the return address of an M2nFrame -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(); -char* m2n_gen_ts_to_register(char* buf, R_Opnd* reg); - -// Generate code to push an M2nFrame onto the stack. -// It assumes that num_callee_saves registers have already been saved and the rest have been preserved, that the saved registers are immediately -// below the return address, and that esp points to the last one saved. The order for callee saves is ebp, ebx, esi, edi. -// It destroys eax. -// 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); - -// 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. -unsigned m2n_set_local_handles_size(unsigned bytes_to_m2n); -char* m2n_gen_set_local_handles(char* buf, unsigned bytes_to_m2n, R_Opnd* src_reg); - -// Generate code to pop an M2nFrame off the stack. -// num_callee_saves: the number of callee saves registers to leave on the stack as at the entry to push_m2n. -// extra_on_stack: the number of bytes between esp and the bottom of the M2nFrame. -// 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); - -////////////////////////////////////////////////////////////////////////// -// Implementation details - -// This information is used by the m2n implementation and the stack iterator implementation. -// It may not be used by any other module. - -// There are two types of M2nFrames: those that result from managed code calling a stub, and those that represent suspended managed code. -// The second type is needed to deal with throwing exceptions from OS contexts with the exception filter or signal mechanisms. -// For the first type: -// eip points to the instruction past the one in question (ie is the return address into managed code) -// the bottom two bits of p_lm2nf are zero -// regs is not present, and is implicitly the address of the word above eip -// For the second type: -// eip points to the instruction in question -// p_lm2nf==1 -// regs is present - -#ifdef _EM64T_ -#error Wrong header file. -#endif - -struct M2nFrame { - M2nFrame* prev_m2nf; - M2nFrame** p_lm2nf; - ObjectHandles* local_object_handles; - 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) - uint32 edi; - uint32 esi; - uint32 ebx; - uint32 ebp; - uint32 eip; - Registers* regs; // This is only for M2nFrames for suspended managed code (as against ones that call stubs and prepare jvmtiPopFrame) -}; - -#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 deleted file mode 100644 index 9236205..0000000 --- a/vm/port/src/lil/ia32/pim/stack_iterator_ia32.cpp +++ /dev/null @@ -1,541 +0,0 @@ -/* - * 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. - */ - -/** - * @author Intel, Pavel Afremov - * @version $Revision$ - */ - -#include "environment.h" -#include "jit_intf_cpp.h" -#include "m2n.h" -#include "m2n_ia32_internal.h" -#include "nogc.h" -#include "method_lookup.h" -#include "stack_iterator.h" -#include "vm_stats.h" -#include "open/types.h" -#include "encoder.h" -#include "interpreter.h" -#include "cci.h" - -#include "clog.h" - -#include "dump.h" -#include "port_threadunsafe.h" - -// Invariants: -// Native frames: -// cci should be NULL -// m2nfl should point to the m2n frame list for the native frame -// c.p_eip should point to an address that is not a valid IP for managed code -// Managed frames: -// cci should point to the code chunk info for the method and ip in question -// m2nfl should point to the m2n frame immediately preceeding the current one or NULL is there is no preceeding m2n frame -// the callee saves registers should point to their values at the time the frame was suspended -// for frames suspended at noncall sites, the caller saves registers should point to their values at the time of suspension -// c.p_eip and c.esp should point-to/have their values at the time the frame was suspended -// c.p_eax is also valid for returning a pointer in transfer control - -struct StackIterator { - CodeChunkInfo* cci; - JitFrameContext c; - M2nFrame* m2nfl; - uint32 ip; -}; - -////////////////////////////////////////////////////////////////////////// -// Utilities - -// Goto the managed frame immediately prior to m2nfl -static void si_unwind_from_m2n(StackIterator* si, bool over_popped = true) -{ -#ifdef VM_STATS - UNSAFE_REGION_START - VM_Statistics::get_vm_stats().num_unwind_native_frames_all++; - UNSAFE_REGION_END -#endif - - M2nFrame* m2nfl = si->m2nfl; - assert(m2nfl); - - si->m2nfl = m2n_get_previous_frame(m2nfl); - - 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) { - // 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", - (void*)m2nfl->regs->eip)); - si->c.esp = m2nfl->regs->esp; - si->c.p_eip = &(m2nfl->regs->eip); - si->c.is_ip_past = FALSE; - si->c.p_eax = &m2nfl->regs->eax; - si->c.p_ebx = &m2nfl->regs->ebx; - si->c.p_ecx = &m2nfl->regs->ecx; - si->c.p_edx = &m2nfl->regs->edx; - si->c.p_esi = &m2nfl->regs->esi; - si->c.p_edi = &m2nfl->regs->edi; - si->c.p_ebp = &m2nfl->regs->ebp; - si->c.eflags = m2nfl->regs->eflags; - } else if (over_popped && - (FRAME_MODIFIED_STACK == (FRAME_MODIFIED_STACK & m2n_get_frame_type(m2nfl)))) { - si->c.esp = m2nfl->pop_regs->esp; - si->c.p_eip = &(m2nfl->pop_regs->eip); - si->c.is_ip_past = FALSE; - si->c.p_eax = &m2nfl->pop_regs->eax; - si->c.p_ebx = &m2nfl->pop_regs->ebx; - si->c.p_ecx = &m2nfl->pop_regs->ecx; - si->c.p_edx = &m2nfl->pop_regs->edx; - si->c.p_esi = &m2nfl->pop_regs->esi; - si->c.p_edi = &m2nfl->pop_regs->edi; - si->c.p_ebp = &m2nfl->pop_regs->ebp; - si->c.eflags = m2nfl->pop_regs->eflags; - } 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; - si->c.p_eip = &m2nfl->eip; - si->c.is_ip_past = TRUE; - si->c.p_edi = &m2nfl->edi; - si->c.p_esi = &m2nfl->esi; - si->c.p_ebx = &m2nfl->ebx; - si->c.p_ebp = &m2nfl->ebp; - } -} - -static char* get_reg(char* ss, R_Opnd* dst, Reg_No dst_reg, 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, 0)); - return ss; -} - -typedef void (__cdecl *transfer_control_stub_type)(StackIterator*); - -static transfer_control_stub_type gen_transfer_control_stub() -{ - static transfer_control_stub_type addr = NULL; - if (addr) { - return addr; - } - - const int stub_size = 0x4d; - 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. - // - - M_Base_Opnd m1(esp_reg, 4); - ss = mov(ss, edx_opnd, m1); - - ss = get_reg(ss, &ebx_opnd, ebx_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.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)); - 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 = alu(ss, and_opc, ecx_opnd, Imm_Opnd(size_32, 0xff)); - ss = push(ss, ecx_opnd); - *ss++ = (char)0x9D; // POPFD - // 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); - - ss = mov(ss, esp_opnd, m1); - 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); - - return addr; -} - -////////////////////////////////////////////////////////////////////////// -// Stack Iterator Interface - -StackIterator* si_create_from_native() -{ - return si_create_from_native(p_TLS_vmthread); -} - -void si_fill_from_native(StackIterator* si) -{ - si_fill_from_native(si, p_TLS_vmthread); -} - -StackIterator* si_create_from_native(VM_thread* thread) -{ - ASSERT_NO_INTERPRETER - // Allocate iterator - StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); - assert(res); - - // Setup current frame - si_fill_from_native(res, thread); - - return res; -} - -void si_fill_from_native(StackIterator* si, VM_thread* thread) -{ - memset(si, 0, sizeof(StackIterator)); - - // Setup current frame - si->cci = NULL; - si->m2nfl = m2n_get_last_frame(thread); - si->ip = 0; - si->c.p_eip = &si->ip; -} - - -StackIterator* si_create_from_registers(Registers* regs, bool is_ip_past, M2nFrame* lm2nf) -{ - // Allocate iterator - StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); - assert(res); - - si_fill_from_registers(res, regs, is_ip_past, lm2nf); - - return res; -} -void si_fill_from_registers(StackIterator* si,Registers* regs, bool is_ip_past, M2nFrame* lm2nf) -{ - memset(si, 0, sizeof(StackIterator)); - - // Setup current frame - Global_Env *env = VM_Global_State::loader_env; - // It's possible that registers represent native code and si->cci==NULL - si->cci = env->vm_methods->find((NativeCodePtr)regs->eip, is_ip_past); - si->c.esp = regs->esp; - si->c.p_eip = ®s->eip; - si->c.p_ebp = ®s->ebp; - si->c.p_edi = ®s->edi; - si->c.p_esi = ®s->esi; - si->c.p_ebx = ®s->ebx; - si->c.p_eax = ®s->eax; - si->c.p_ecx = ®s->ecx; - si->c.p_edx = ®s->edx; - si->c.is_ip_past = is_ip_past; - si->c.eflags = regs->eflags; - si->m2nfl = lm2nf; -} - -size_t si_size(){ - return sizeof(StackIterator); -} - -// On IA32 all registers are preserved automatically, so this is a nop. -void si_transfer_all_preserved_registers(StackIterator*) -{ - // Do nothing -} - -bool si_is_past_end(StackIterator* si) -{ - ASSERT_NO_INTERPRETER - return si->cci==NULL && si->m2nfl==NULL; -} - -void si_goto_previous(StackIterator* si, bool over_popped) -{ - ASSERT_NO_INTERPRETER - if (si->cci) { - TRACE2("si", ("si_goto_previous from ip = %p (%s%s)", - (void*)si_get_ip(si), - method_get_name(si->cci->get_method()), - method_get_descriptor(si->cci->get_method()))); - assert(si->cci->get_jit() && si->cci->get_method()); - si->cci->get_jit()->unwind_stack_frame(si->cci->get_method(), si_get_jit_context(si)); - si->c.is_ip_past = TRUE; - } else { - TRACE2("si", ("si_goto_previous from ip = %p (M2N)", - (void*)si_get_ip(si))); - if (!si->m2nfl) return; - si_unwind_from_m2n(si, over_popped); - } - Global_Env *vm_env = VM_Global_State::loader_env; - si->cci = vm_env->vm_methods->find(si_get_ip(si), si_get_jit_context(si)->is_ip_past); - if (si->cci) { - TRACE2("si", ("si_goto_previous to ip = %p (%s%s)", - (void*)si_get_ip(si), - method_get_name(si->cci->get_method()), - method_get_descriptor(si->cci->get_method()))); - } else { - TRACE2("si", ("si_goto_previous to ip = %p (M2N)", - (void*)si_get_ip(si))); - } -} - -StackIterator* si_dup(StackIterator* si) -{ - ASSERT_NO_INTERPRETER - StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); - memcpy(res, si, sizeof(StackIterator)); - // If si uses itself for IP then res should also to avoid problems if si is deallocated first. - if (si->c.p_eip == &si->ip) - res->c.p_eip = &res->ip; - return res; -} - -void si_free(StackIterator* si) -{ - STD_FREE(si); -} - -void* si_get_sp(StackIterator* si) { - return (void*)si->c.esp; -} - -NativeCodePtr si_get_ip(StackIterator* si) -{ - ASSERT_NO_INTERPRETER - return (NativeCodePtr)*si->c.p_eip; -} - -void si_set_ip(StackIterator* si, NativeCodePtr ip, bool also_update_stack_itself) -{ - if (also_update_stack_itself) { - *(si->c.p_eip) = (uint32)ip; - } else { - si->ip = (uint32)ip; - si->c.p_eip = &si->ip; - } -} - -// Set the code chunk in the stack iterator -void si_set_code_chunk_info(StackIterator* si, CodeChunkInfo* cci) -{ - ASSERT_NO_INTERPRETER - assert(si); - si->cci = cci; -} - -CodeChunkInfo* si_get_code_chunk_info(StackIterator* si) -{ - return si->cci; -} - -JitFrameContext* si_get_jit_context(StackIterator* si) -{ - return &si->c; -} - -bool si_is_native(StackIterator* si) -{ - ASSERT_NO_INTERPRETER - return si->cci==NULL; -} - -M2nFrame* si_get_m2n(StackIterator* si) -{ - ASSERT_NO_INTERPRETER - return si->m2nfl; -} - -void** si_get_return_pointer(StackIterator* si) -{ - return (void**) si->c.p_eax; -} - -void si_set_return_pointer(StackIterator* si, void** return_value) -{ - si->c.p_eax = (uint32*)return_value; -} - -#ifdef _WIN32 - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4733) // Inline asm assigning to 'FS:0' : handler not registered as safe handler -#endif -/** - * Temporary workaround and detection of problem. - * - * This is a hack function to detect problems with C++ object living on - * while destructive unwinding is used. Known problem is logger in release - * mode. No (even potential) logging or other c++ objects created on stack - * is alowed in any functions in the stack when calling destructive unwinding. - * - * The function prints warning message and corrects exception handlers if - * the objects exist. - * - * See also function: JIT_execute_method_default - */ -static void cpp_check_last_frame() -{ - void *handler; - __asm { - mov eax, fs:[0] - mov handler, eax - } - - void *lastFrame = p_TLS_vmthread->lastFrame; - - TRACE2("exn",(" curr = 0x%p\n", lastFrame)); - TRACE2("exn",(" handler=0x%p\n", handler)); - - if (!(handler < lastFrame)) { - TRACE2("exn",("all ok\n")); - return; - } - - // NO CXX LOGGER PLEASE! doesn't work with destructive unwinding! - INFO2("exn", ("ERROR: Destructive unwinding: C++ objects detected on stack!\n")); - - while(handler < lastFrame) { - INFO2("exn", (" droping 0x%p\n", handler)); - handler = *(int**)handler; - } - INFO2("exn", (" setting curr 0x%p\n", handler)); - __asm { - mov eax, handler - mov fs:[0], eax - } -} -#ifdef _MSC_VER -#pragma warning(pop) -#endif -#endif // _WIN32 - -void si_transfer_control(StackIterator* si) -{ -/* !!!! NO CXX LOGGER IS ALLOWED IN THIS FUNCTION !!! - * !!!! RELEASE BUILD WILL BE BROKEN !!!*/ - // 1. Copy si to stack - void* null_pointer = NULL; - StackIterator local_si; - memcpy(&local_si, si, sizeof(StackIterator)); - - if (NULL == si->c.p_eax) - local_si.c.p_eax = (uint32*)&null_pointer; - if (NULL == si->c.p_ebx) - local_si.c.p_ebx = (uint32*)&null_pointer; - if (NULL == si->c.p_ecx) - local_si.c.p_ecx = (uint32*)&null_pointer; - if (NULL == si->c.p_edx) - local_si.c.p_edx = (uint32*)&null_pointer; - - if (si->c.p_eip == &si->ip) - local_si.c.p_eip = &local_si.ip; - //si_free(si); - - // 2. Set the M2nFrame list - m2n_set_last_frame(local_si.m2nfl); -#ifdef _WIN32 // Workaround and detection of possible problems with - // objects on stack. - cpp_check_last_frame(); -#endif // _WIN32 - - TRACE2("exn", ("generating control transfer stub")); - // 3. Call the stub - transfer_control_stub_type tcs = gen_transfer_control_stub(); - TRACE2("exn", ("tcs")); - tcs(&local_si); -} - -inline static uint32 unref_reg(uint32* p_reg) { - return p_reg ? *p_reg : 0; -} -void si_copy_to_registers(StackIterator* si, Registers* regs) -{ - ASSERT_NO_INTERPRETER - - regs->esp = si->c.esp; - regs->eflags = si->c.eflags; - regs->eip = unref_reg(si->c.p_eip); - regs->ebp = unref_reg(si->c.p_ebp); - regs->edi = unref_reg(si->c.p_edi); - regs->esi = unref_reg(si->c.p_esi); - regs->edx = unref_reg(si->c.p_edx); - regs->ecx = unref_reg(si->c.p_ecx); - regs->ebx = unref_reg(si->c.p_ebx); - regs->eax = unref_reg(si->c.p_eax); -} - -void si_set_callback(StackIterator* si, NativeCodePtr* callback) { - si->c.esp = si->c.esp - 4; - *((uint32*) si->c.esp) = *(si->c.p_eip); - si->c.p_eip = ((uint32*)callback); -} - -void si_reload_registers() -{ - // Do nothing -} diff --git a/vm/port/src/lil/ipf/pim/include/lil_code_generator_ipf.h b/vm/port/src/lil/ipf/pim/include/lil_code_generator_ipf.h deleted file mode 100644 index 3c712e0..0000000 --- a/vm/port/src/lil/ipf/pim/include/lil_code_generator_ipf.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#ifndef _LIL_CODE_GENERATOR_IPF_ -#define _LIL_CODE_GENERATOR_IPF_ - -#include "lil.h" -#include "lil_code_generator.h" - -// this class is for internal use only; it is declared in -// lil_code_generator_ipf.cpp -class LcgIpfContext; -class Merced_Code_Emitter; - - -class LilCodeGeneratorIpf : public LilCodeGenerator { -public: - LilCodeGeneratorIpf(); - -protected: - NativeCodePtr compile_main(LilCodeStub* , size_t*, PoolManager*); -}; - -#endif // _LIL_CODE_GENERATOR_IPF_ diff --git a/vm/port/src/lil/ipf/pim/lil_code_generator_ipf.cpp b/vm/port/src/lil/ipf/pim/lil_code_generator_ipf.cpp deleted file mode 100644 index c9772f7..0000000 --- a/vm/port/src/lil/ipf/pim/lil_code_generator_ipf.cpp +++ /dev/null @@ -1,2163 +0,0 @@ -/* - * 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. - */ - -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision$ - */ - -#include -#include - -#define LOG_DOMAIN "vm.helpers" -#include "cxxlog.h" - -#include "lil.h" -#include "lil_code_generator.h" -#include "lil_code_generator_ipf.h" -#include "lil_code_generator_utils.h" -#include "m2n.h" -#include "m2n_ipf_internal.h" -#include "tl/memory_pool.h" -#include "vm_ipf.h" -#include "vm_threads.h" -#include "stub_code_utils.h" -#include "open/vm_util.h" -#include "environment.h" -#include "port_malloc.h" - -#ifndef NDEBUG -#include "dump.h" -#endif -/************************ - * Where everything is - * (up = higher addresses / reg numbers) - * Stacked registers: - * - * |----------------------------| - * | outputs | - * |----------------------------| - * | ar.pfs, b0, sp (if needed) | - * |----------------------------| - * | locals | - * |----------------------------| - * | m2n frame (if needed) | - * |----------------------------| - * | inputs (8 spaces) | - * r32 |----------------------------| - * - * Other registers: - * - standard places go into r17-r18 - * - return values go into r8 or f8 - * - fp arguments go into f8-f15 - * - fp locals go into scratch registers if the stub makes no ordinary calls, otherwise onto the memory stack - * - r3 is used for add-long-immediate - * - r26-31 are used as temporaries in arithmetic operations - * - * Memory stack frame: - * - * |------------------------| - * | extra inputs | - * prev sp+16 |========================| - * | Local FR area | - * |------------------------| - * | FR input save area | - * |------------------------| - * | dyn alloc | - * |------------------------| - * | extra saves area | - * |------------------------| - * | extra outputs | - * sp+16 |------------------------| - * | scratch | - * sp |========================| - * - * All pieces of the stack are 16-byte aligned! - * - * If the stub executes calls other than tailcall and call.noret, - * inputs residing in FRs are moved to memory, to avoid being overwritten - * by FR outputs and return values and/or being used as scratch in the - * callee. In addition local floating point variables are put on the stack - * rather than in scratch registers. - * - * Things that are saved right after entry / restored right before ret: - * - ar.pfs (by the alloc instruction), in the stacked regs - * - b0 (only if the stub executes calls), in the stacked regs - * - * Things that are saved right before a call and restored right after a call - * (except for call.noret): - * - sp - * - gp, if needed - */ - - -// maximum number of locals. Increase it if needed, but not more than 80 or so. -#define MAX_LOCALS 10 - -// maximum number of stand places. Probably upper limit here is around 16. -#define MAX_STD_PLACES 2 - -// rounds up an integer value to the closest multiple of 16 -static inline unsigned align_16(unsigned n) { - return n = (n + 0xF) & ~0xF; -} - - - -// an enum indicating a variable's location: in a register class or on the -// stack (no LIL variable is ever on the heap!) -enum LcgIpfLocKind { - LLK_Gr, LLK_Fr, LLK_Stk, LLK_Stk16 -}; - - -// location of a LIL variable -struct LcgIpfLoc { - void *operator new(size_t sz, tl::MemoryPool& m) { - return m.alloc(sz); - } - void operator delete (void *p, tl::MemoryPool& m) { } - - LcgIpfLocKind kind; - int addr; // register number or SP-relative offset - - LcgIpfLoc(LcgIpfLocKind k, int a): kind(k), addr(a) - {} - - bool operator==(const LcgIpfLoc& loc) { - return (kind == loc.kind && addr == loc.addr); - } - - bool operator!=(const LcgIpfLoc& loc) { - return (kind != loc.kind || addr != loc.addr); - } - -private: - LcgIpfLoc(LcgIpfLoc &); // disable copying - LcgIpfLoc &operator=(LcgIpfLoc &); // disable copying -}; // struct LcgIpfLoc - - -// This class holds all relevant information needed in compilation, -// such as the mapping of variables to locations and the mapping of -// labels to ints. Some ofthis information is gathered during a prepass. -class LcgIpfContext: public LilInstructionVisitor { -private: - - LilCodeStub *cs; // the code stub - LilSig *entry_sig; // the stub's entry signature - bool is_arbitrary; // true if the stub's entry signature is "arbitrary" - tl::MemoryPool &mem; // a memory manager - - unsigned n_labels; // number of labels - LilLabel *labels; // a label's index is its label id - - // number of targets used by support functions, such as pop_m2n - unsigned extra_targets; - - - unsigned n_inputs; // total number of inputs - unsigned n_gr_inputs; // total number of GRs reserved for inputs - unsigned n_fr_inputs; // number of inputs placed in FRs - - unsigned first_local; // location of the first local variable - unsigned n_locals; // maximum number of locals - unsigned n_fr_locals; // maximum number of locals that may contain FP values - bool uses_local_fr[MAX_LOCALS]; // true if preserved fp f(16+index) is used by a local - unsigned local_fr_offset[MAX_LOCALS]; // offsets for local variable from start of local fr area - - unsigned first_output; // location of the start of the output space - unsigned n_outputs; // maximum number of outputs - unsigned n_gr_outputs; // total number of GRs reserved for outputs - unsigned n_fr_outputs; // maximum number of outputs placed in FRs - - unsigned first_gr_save; // start of region where ar.pfs, b0, etc. are saved - unsigned n_gr_saves; // number of GRs reserved for saving stuff - - unsigned stk_output_size; // bytes needed for outgoing params on the stack - unsigned stk_input_save_size; // size reserved for saving FR inputs - unsigned stk_local_fr_size; // size reserved for local FRs - unsigned stk_extra_saves_size; // bytes for m2n_save_all - - unsigned n_std_places; // number of standard places - unsigned stk_alloc_size; // size of allocatable memory on the stack - - // total size of the memory stack frame (in bytes) - unsigned stk_size; - - bool does_normal_calls; // true if the stub contains "normal" calls - bool does_tail_calls; // true if the stub contains tail calls - bool calls_unmanaged_code; // true if the stub calls calls code with a calling convention other than managed - bool has_m2n; // true if the stub contains push_m2n/pop_m2n instructions - bool uses_inputs; // true if inputs are ever accessed - bool needs_alloc; // true if an alloc instruction is needed at the start - - // an iterator used during pre-pass; it has to be stored here because - // it must be accessible from the visitor functions - LilInstructionIterator iter; - -public: - - void *operator new(size_t sz, tl::MemoryPool& m) { - return m.alloc(sz); - } - void operator delete (void *p, tl::MemoryPool& m) { } - - LcgIpfContext(LilCodeStub *cs, tl::MemoryPool &m): - cs(cs), - mem(m), - iter(cs, true) - { - // initialize some members - entry_sig = lil_cs_get_sig(cs); - is_arbitrary = lil_sig_is_arbitrary(entry_sig); - n_labels = 0; - extra_targets = 0; - labels = (LilLabel *) mem.alloc(lil_cs_get_num_instructions(cs) * sizeof (LilLabel)); - - n_locals = 0; - - for (unsigned i=0; i < MAX_LOCALS; i++) { - uses_local_fr[i] = false; - } - - n_outputs = 0; - n_fr_outputs = 0; - - n_std_places = 0; - - stk_extra_saves_size = 0; - stk_alloc_size = 0; - does_normal_calls = false; - calls_unmanaged_code = false; - does_tail_calls = false; - has_m2n = false; - uses_inputs = false; - - // get some info from the entry signature - if (is_arbitrary) { - n_inputs = 8; - n_gr_inputs = 8; - n_fr_inputs = 8; - } - else { - n_inputs = lil_sig_get_num_args(entry_sig); - n_gr_inputs = (n_inputs > 8) ? 8 : n_inputs; - n_fr_inputs = 0; - for (unsigned i=0; i 0 || n_locals > 0); - - // determine how many save spots are needed in stacked GRs - // ( if an M2N frame is present, registers will be saved in there) - if (has_m2n || !needs_alloc) - n_gr_saves = 0; - else if (does_normal_calls) - n_gr_saves = 4; // save ar.pfs, b0, gp, sp - else - n_gr_saves = 1; // save ar.pfs only - - - // decide where stacked register regions should start - if (has_m2n) - first_local = m2n_get_last_m2n_reg() + 1; - else - first_local = 32 + n_gr_inputs; - - first_gr_save = first_local + n_locals; - first_output = first_gr_save + n_gr_saves; - - // stack size needed for outputs (16-byte aligned) - if (n_outputs <= 8) { - n_gr_outputs = n_outputs; - stk_output_size = 0; - } - else { - n_gr_outputs = 8; - stk_output_size = align_16((n_outputs-8)*8); - } - - // 16-byte align allocatable space - stk_alloc_size = align_16(stk_alloc_size); - - // stack size needed for saving FR inputs - if (does_normal_calls) - stk_input_save_size = n_fr_inputs * 16; - else - stk_input_save_size = 0; - - // stack size needed for local FRs - stk_local_fr_size = 0; - if (does_normal_calls) { - for(unsigned i=0; i 0); // haven't run out of targets yet! - --extra_targets; - return n_labels + extra_targets; - } - - // returns the number of incoming arguments - unsigned get_num_inputs() { - return n_inputs; - } // get_num_input - - // returns the number of incoming arguments stored in FRs - unsigned get_num_fr_inputs() { - return n_fr_inputs; - } // get_num_fr_inputs - - // returns the size of the input space in the stacked GRs - unsigned get_input_space_size() { - return n_gr_inputs; - } // get_input_space_size - - // returns the size of the local space in the stacked GRs - // (this includes local vars, the M2N frame, and the save regs) - unsigned get_local_space_size() { - return first_output - (32 + n_gr_inputs); - } // get_local_space_size - - // returns the size of the output space in the stacked GRs - unsigned get_output_space_size() { - return n_gr_outputs; - } // get_output_space_size - - // returns true if there are floating-point inputs in registers f8-f15 that must be moved to registers f16-f23 - bool must_move_fr_inputs() { - return (does_normal_calls && n_fr_inputs > 0); - } // must_move_fr_inputs - - // returns true if ar.ccv should be set to zero before a call to managed - // code. This is the case if the stub or any function it calls is - // non-managed (i.e. it has a platform, jni, or stdcall cc) - bool must_reset_ccv() { - LilCc stub_cc = lil_sig_get_cc(entry_sig); - return !(stub_cc==LCC_Managed || stub_cc==LCC_Rth) || calls_unmanaged_code; - } // must_reset_ccv - - // returns the location of the i'th input - const LcgIpfLoc* get_input(unsigned n) { - // function should not be called if signature is arbitrary - assert(!is_arbitrary); - - LilType tp = lil_sig_get_arg_type(entry_sig, n); - - // if n >= 8, input is on stack - if (n >= 8) { - return new(mem) LcgIpfLoc(LLK_Stk, 16 + stk_size + (n-8)*8); - } - - if (tp != LT_F4 && tp != LT_F8) { - return new(mem) LcgIpfLoc(LLK_Gr, 32+n); - } - - // parameter is fp; its location depends on how many fp parameters - // come before it. - unsigned fp_param_cnt = 0; - for (unsigned i=0; i= 8, output is in stack - if (n >= 8) { - return new(mem) LcgIpfLoc(LLK_Stk, 16 + (n-8)*8); - } - - if (tp != LT_F4 && tp != LT_F8) { - return new(mem) LcgIpfLoc(LLK_Gr, first_output + n); - } - - // parameter is fp; its location depends on how many fp parameters - // come before it. - unsigned fp_param_cnt = 0; - for (unsigned i=0; i n_std_places) - n_std_places = sp; - } // std_places - - void alloc(LilVariable* var, unsigned alloc_space) { - alloc_space = (alloc_space + 0x7) & ~0x7; - stk_alloc_size += alloc_space; - } // alloc - - void asgn(LilVariable* var, enum LilOperation operation, LilOperand* op1, LilOperand* op2) { - check_variable(var); - check_operand(op1); - if (lil_operation_is_binary(operation)) - check_operand(op2); - } // asgn - - void ts(LilVariable*var) { - check_variable(var); - } // ts - - void handles(LilOperand* op) { - check_operand(op); - } // handles - - void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilLdX) { - check_variable(dst); - if (base != NULL) - check_variable(base); - if (index != NULL) - check_variable(index); - } // ld - - void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilOperand* src) { - if (base != NULL) - check_variable(base); - if (index != NULL) - check_variable(index); - check_operand(src); - } // st - - void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel) { - if (base != NULL) - check_variable(base); - if (index != NULL) - check_variable(index); - } // inc - - void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, - LilOperand* cmp, LilOperand* src, LilLabel) - { - if (base) check_variable(base); - if (index) check_variable(index); - check_operand(cmp); - check_operand(src); - } - - void j(LilLabel) { - // nothing to do - } // j - - void jc(LilPredicate p, LilOperand* o1, LilOperand* o2, LilLabel) - { - check_operand(o1); - if (lil_predicate_is_binary(p)) - check_operand(o2); - } // jc - - void out(LilSig* sig) { - // make sure there is enough space for this command's outputs - unsigned outs = lil_sig_get_num_args(sig); - if (outs > n_outputs) - n_outputs = outs; - - // reserve enough FR outputs - unsigned n_fo = 0; - for (unsigned i=0; i n_fr_outputs) - n_fr_outputs = n_fo; - - // check if this refers to a call to unmanaged code - LilCc cc = lil_sig_get_cc(sig); - if (cc != LCC_Managed && cc != LCC_Rth) - calls_unmanaged_code = true; - } // out - - void in2out(LilSig* sig) { - // reserve enough outputs and FR outputs - if (n_inputs > n_outputs) - n_outputs = n_inputs; - if (n_fr_inputs > n_fr_outputs) - n_fr_outputs = n_fr_inputs; - // in2out implies that inputs are being accessed - uses_inputs = true; - - // check if this refers to a call to unmanaged code - LilCc cc =lil_sig_get_cc(sig); - if (cc != LCC_Managed && cc != LCC_Rth) - calls_unmanaged_code = true; - - } // in2out - - void call(LilOperand* o, LilCallKind k) { - check_operand(o); - if (k == LCK_Call) - does_normal_calls = true; - else if (k == LCK_TailCall) { - does_tail_calls = true; - // no need to reserve extra outputs, like in in2out, since tailcall is implemented differently - } - } // call - - void ret() { - // nothing to do - } // ret - - void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) { - has_m2n = true; // remember that this stub requires an m2n frame - } // push_m2n - - void m2n_save_all() - { - stk_extra_saves_size = M2N_EXTRA_SAVES_SPACE; - } // m2n_save_all - - void pop_m2n() { - // if handles are present, pop_m2n may be using an extra target - bool handles = lil_ic_get_m2n_state(iter.get_context()) == LMS_Handles; - if (handles) { - // this pop_m2n will use an extra target - extra_targets++; - // it will also execute a call - does_normal_calls = true; - } - } - - void print(char *, LilOperand *) { - // this is a no-op if debugging is off -#ifdef STUB_DEBUG - // reserve at least 2 outputs; remember that a call will be made - if (n_outputs < 2) - n_outputs = 2; - does_normal_calls = true; -#endif // STUB_DEBUG - } // print - - //visitor functions - end - //*********************** - -}; //class LcgIpfContext - - -// the following class is a LIL instruction visitor used by -// LilCodeGeneratorIpf to do most of the work. -// After the constructor exits, all the necessary code is in the -// code emitter. -class LcgIpfCodeGen: public LilInstructionVisitor { - - // the following variables are here because visitor functions need to - // access them in addition to their arguments - LilCodeStub *cs; - Merced_Code_Emitter& emitter; - LcgIpfContext& context; - tl::MemoryPool& mem; - LilInstructionIterator iter; - LilInstructionContext *ic; - LilInstruction *inst; - // visit functions can always assume that inst points to the current instruction and ic points to the current context - - unsigned current_alloc; // keeps track of memory allocation - - LilSig *cur_out_sig; // keeps track of the current out signature - - // some useful constants - static const LcgIpfLoc* gp; - static const LcgIpfLoc* sp; - static const LcgIpfLoc* r0; - static const LcgIpfLoc* tp; // thread pointer - static const LcgIpfLoc* tmp_op1; // used for temp storage of operands - static const LcgIpfLoc* tmp_op2; // used for temp storage of operands - static const LcgIpfLoc* tmp_res; // used for temp storage of results - static const LcgIpfLoc* tmp_f; // used for temp storage of FPs - static const LcgIpfLoc* tmp_addr1; // used for temp storage of addresses - static const LcgIpfLoc* tmp_addr2; // used for temp storage of addresses - static const LcgIpfLoc* tmp_addr3; // used for temp storage of addresses - static const LcgIpfLoc* tmp_addl; // used as a temporary op of addl - static const unsigned tmp_pred; // used for the jc instruction - static const unsigned tmp_br; // used for indirect calls - -private: - /******************* - * helper functions - */ - - // performs an immediate addition; may use adds, addl, or movl and add, - // depending on the immediate operand's size - void add_imm(const LcgIpfLoc *dest, const LcgIpfLoc *src, int64 imm) { - assert(src->kind == LLK_Gr && (dest->kind==LLK_Stk || dest->kind==LLK_Gr)); - - unsigned dst_reg = (dest->kind==LLK_Stk ? tmp_res->addr : dest->addr); - - if (imm >= -0x2000 && imm < 0x2000) { - // imm fits into 14 bits; use adds - emitter.ipf_adds(dst_reg, (int) imm, src->addr, current_predicate); - } - else if (imm >= -0x200000 && imm < 0x200000) { - // imm fits into 22 bits; use addl - emitter.ipf_addl(dst_reg, (int) imm, src->addr, current_predicate); - } - else { - // have to use movl - unsigned lower_32 = (unsigned) imm; - unsigned upper_32 = (unsigned) (imm >> 32); - if (src->addr==0) { - emitter.ipf_movl(dst_reg, upper_32, lower_32, current_predicate); - } else { - emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); - emitter.ipf_add(dst_reg, src->addr, tmp_addl->addr, current_predicate); - } - } - - if (dest->kind==LLK_Stk) - move(dest, tmp_res); - } - - void int_mul(unsigned dest_reg, unsigned src1_reg, unsigned src2_reg) - { - unsigned ftmp1 = 7, ftmp2 = 8, ftmp3 = 9; - emitter.ipf_setf(freg_sig, ftmp1, src1_reg, current_predicate); - emitter.ipf_setf(freg_sig, ftmp2, src2_reg, current_predicate); - emitter.ipf_xma(ftmp3, ftmp1, ftmp2, 0, l_form, current_predicate); - emitter.ipf_getf(freg_sig, dest_reg, ftmp3); - } - - // binary arithmetic operations without immediates - // (allowed only for integer values!) - void bin_op(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src1, const LcgIpfLoc* src2) - { - assert((dest->kind == LLK_Gr || dest->kind == LLK_Stk) && - (src1->kind == LLK_Gr || src1->kind == LLK_Stk) && - (src2->kind == LLK_Gr || src2->kind == LLK_Stk)); - - unsigned dest_reg = (dest->kind == LLK_Stk) ? tmp_res->addr : dest->addr; - unsigned src1_reg = (src1->kind == LLK_Stk) ? tmp_op1->addr : src1->addr; - unsigned src2_reg = (src2->kind == LLK_Stk) ? tmp_op2->addr : src2->addr; - - if (src1->kind == LLK_Stk) { - move(tmp_op1, src1); // load src1 into tmp_op1 - } - if (src2->kind == LLK_Stk) { - move(tmp_op2, src2); // load src2 into tmp_op2 - } - - switch (o) { - case LO_Add: - emitter.ipf_add(dest_reg, src1_reg, src2_reg, current_predicate); - break; - case LO_Sub: - emitter.ipf_sub(dest_reg, src1_reg, src2_reg, current_predicate); - break; - case LO_SgMul: - int_mul(dest_reg, src1_reg, src2_reg); - break; - case LO_Shl: - emitter.ipf_shl(dest_reg, src1_reg, src2_reg, current_predicate); - break; - default: - ASSERT(0, "Unexpected operation"); // control should never reach this point - } - - if (dest->kind == LLK_Stk) { - // store tmp_res back to dest - move(dest, tmp_res); - } - } // bin_op - - - // binary op where the second op is immediate - // (allowed only for integer values!) - void bin_op_imm(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src, POINTER_SIZE_INT imm_val) { - int64 imm = (int64) imm_val; // convert to signed - assert(dest->kind != LLK_Fr && src->kind != LLK_Stk16); - const LcgIpfLoc* dest_reg = (dest->kind == LLK_Stk) ? tmp_res : dest; - const LcgIpfLoc* src_reg = (src->kind == LLK_Stk) ? tmp_op1 : src; - - if (src_reg != src) { - move(src_reg, src); // load src into tmp_op1 - } - - if (o == LO_Shl) { - if (imm > 63) { - emitter.ipf_add(dest_reg->addr, 0, 0, current_predicate); // dest_reg = 0 - } - else { - emitter.ipf_shli(dest_reg->addr, src_reg->addr, (int) imm, current_predicate); - } - } - else if (o == LO_Sub) { - add_imm(dest_reg, src_reg, -imm); - } - else if (o == LO_Add) { - add_imm(dest_reg, src_reg, imm); - } - else if (o == LO_SgMul) { - // This is not as optimised as it could be - // We could move the immediate directly into a floating point register - unsigned lower_32 = (unsigned) imm; - unsigned upper_32 = (unsigned) (imm >> 32); - emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); - int_mul(dest_reg->addr, tmp_addl->addr, src_reg->addr); - } - else if (o == LO_And) { - if (imm>=0x80 && imm<0x80) { - emitter.ipf_and(dest_reg->addr, src_reg->addr, (unsigned)imm, current_predicate); - } else { - unsigned lower_32 = (unsigned) imm; - unsigned upper_32 = (unsigned) (imm >> 32); - emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); - emitter.ipf_and(dest_reg->addr, tmp_addl->addr, src_reg->addr, current_predicate); - } - } else { - ASSERT(0, "Unexpected operation"); // invalid value in o - } - - if (dest_reg != dest) { - //move tmp_res back to dest - move(dest, dest_reg); - } - } // bin_op_imm - - // subtract op where the first op is immediate - // (allowed only for intefer values) - void sub_op_imm(const LcgIpfLoc* dest, POINTER_SIZE_INT imm_val, const LcgIpfLoc* src) - { - int64 imm = (int64)imm_val; - assert(dest->kind != LLK_Fr && src->kind != LLK_Stk16); - const LcgIpfLoc* dest_reg = (dest->kind == LLK_Stk) ? tmp_res : dest; - const LcgIpfLoc* src_reg = (src->kind == LLK_Stk) ? tmp_op1 : src; - - if (src_reg!=src) - move(src_reg, src); - - if (imm>=-0x80 && imm<0x80) { - emitter.ipf_subi(dest_reg->addr, (unsigned)imm, src_reg->addr, current_predicate); - } else { - move_imm(tmp_res, imm); - emitter.ipf_sub(dest_reg->addr, tmp_res->addr, src_reg->addr, current_predicate); - } - - if (dest_reg!=dest) - move(dest, dest_reg); - } - - // unary operation without immediates - void un_op(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src) { - assert(dest->kind != LLK_Fr && src->kind != LLK_Fr); - unsigned dest_reg = (dest->kind == LLK_Stk) ? tmp_res->addr : dest->addr; - unsigned src_reg = (src->kind == LLK_Stk) ? tmp_op1->addr : src->addr; - - if (src->kind == LLK_Stk) { - move(tmp_op1, src); // load src into tmp_op1 - } - - switch (o) { - case LO_Neg: - emitter.ipf_sub(dest_reg, 0, src_reg, current_predicate); - break; - case LO_Not: - emitter.ipf_xori(dest_reg, 0xFF, src_reg, current_predicate); - break; - case LO_Sx1: - emitter.ipf_sxt(sxt_size_1, dest_reg, src_reg, current_predicate); - break; - case LO_Sx2: - emitter.ipf_sxt(sxt_size_2, dest_reg, src_reg, current_predicate); - break; - case LO_Sx4: - emitter.ipf_sxt(sxt_size_4, dest_reg, src_reg, current_predicate); - break; - case LO_Zx1: - emitter.ipf_zxt(sxt_size_1, dest_reg, src_reg, current_predicate); - break; - case LO_Zx2: - emitter.ipf_zxt(sxt_size_2, dest_reg, src_reg, current_predicate); - break; - case LO_Zx4: - emitter.ipf_zxt(sxt_size_4, dest_reg, src_reg, current_predicate); - break; - default: - ASSERT(0, "Unexpected operation"); // control should never reach this point - } - - if (dest->kind == LLK_Stk) { - //move tmp_res back to dest - move(dest, tmp_res); - } - } // un_op - - - // move between two register or stack locations - void move(const LcgIpfLoc* dest, const LcgIpfLoc* src) { - if (dest->kind == LLK_Stk) { - // put sp + (dest offset) in tmp_addr1 - add_imm(tmp_addr1, sp, dest->addr); - - if (src->kind == LLK_Stk) { - // put sp + (src offset) in tmp_addr2 - add_imm(tmp_addr2, sp, src->addr); - // load src into tmp_op1 - emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, tmp_op1->addr, tmp_addr2->addr, current_predicate); - // store tmp_op1 into dest - emitter.ipf_st(int_mem_size_8, mem_st_spill, mem_none, tmp_addr1->addr, tmp_op1->addr, current_predicate); - } - else if (src->kind == LLK_Stk16) { - // put sp + (src offset) in tmp_addr2 - add_imm(tmp_addr2, sp, src->addr); - // load (128-bit) src into tmp_f - emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); - // store (64-bit) tmp_f into dest - emitter.ipf_stf(float_mem_size_d, mem_st_none, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); - } - else if (src->kind == LLK_Gr) { - // store src into dest - emitter.ipf_st(int_mem_size_8, mem_st_spill, mem_none, tmp_addr1->addr, src->addr, current_predicate); - } - else if (src->kind == LLK_Fr) { - // store src into (64-bit) dest - emitter.ipf_stf(float_mem_size_d, mem_st_none, mem_none, tmp_addr1->addr, src->addr, current_predicate); - } - } - else if (dest->kind == LLK_Stk16) { - // put sp + (dest offset) in tmp_addr1 - add_imm(tmp_addr1, sp, dest->addr); - - if (src->kind == LLK_Stk) { - // put sp + (src offset) in tmp_addr2 - add_imm(tmp_addr2, sp, src->addr); - // load (64-bit) src into tmp_f - emitter.ipf_ldf(float_mem_size_d, mem_ld_none, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); - // store tmp_f into (128-bit) dest - emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); - } - else if (src->kind == LLK_Stk16) { - // put sp + (src offset) in tmp_addr2 - add_imm(tmp_addr2, sp, src->addr); - // load (128-bit) src into tmp_f - emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); - // store tmp_f into (128-bit) dest - emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); - } - else if (src->kind == LLK_Fr) { - // store src into (128-bit) dest - emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, src->addr, current_predicate); - } - else { - ASSERT(0, "Unexpected kind"); // src shouldn't be a GR! - } - } - else if (dest->kind == LLK_Gr) { - if (src->kind == LLK_Stk) { - // put sp + (src offset) in tmp_addr2 - add_imm(tmp_addr2, sp, src->addr); - // load src into dest - emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, dest->addr, tmp_addr2->addr, current_predicate); - } - else if (src->kind == LLK_Gr) { - // move src into dest - emitter.ipf_mov(dest->addr, src->addr, current_predicate); - } - else { - ASSERT(0, "Unexpected kind"); // src->kind shouldn't be LLK_Fr or LLK_Stk16 - } - } - else if (dest->kind == LLK_Fr) { - if (src->kind == LLK_Stk) { - // put sp + (src offset) in tmp_addr2 - add_imm(tmp_addr2, sp, src->addr); - // load (64-bit) src into dest - emitter.ipf_ldf(float_mem_size_d, mem_ld_none, mem_none, dest->addr, tmp_addr2->addr, current_predicate); - } - else if (src->kind == LLK_Stk16) { - // put sp + (src offset) in tmp_addr2 - add_imm(tmp_addr2, sp, src->addr); - // load (128-bit) src into dest - emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, dest->addr, tmp_addr2->addr, current_predicate); - } - else if (src->kind == LLK_Fr) { - // move src into dest - emitter.ipf_fmov(dest->addr, src->addr, current_predicate); - } - else { - ASSERT(0, "Unexpected kind"); // src should not be GR! - } - } - else { - ASSERT(0, "Unknown kind"); // dest->kind has illegal value! - } - } // move - - - // move immediate - // (not allowed for FRs!) - void move_imm(const LcgIpfLoc* dest, POINTER_SIZE_INT imm_val) { - assert(dest->kind==LLK_Gr || dest->kind==LLK_Stk); - add_imm(dest, r0, (int64)imm_val); - } // move_imm - - // generate instructions that compute a condition into two predicate registers - void do_cond(LilPredicate p, LilOperand* op1, LilOperand* op2, unsigned true_pr, unsigned false_pr) - { - assert(current_predicate==0); - - // make sure operands are in registers - unsigned op1_reg = 0, op2_reg = 0; // GR numbers holding op1 and op2 - - if (lil_operand_is_immed(op1)) { - int64 imm = (int64) lil_operand_get_immed(op1); - move_imm(tmp_op1, imm); - op1_reg = tmp_op1->addr; - } - else { // op1 is a variable - LilVariable *op1_var = lil_operand_get_variable(op1); - const LcgIpfLoc* op1_loc = - context.get_var_loc(op1_var, inst, ic, false); - if (op1_loc->kind == LLK_Stk) { - // load op1_loc into tmp_op1 - move(tmp_op1, op1_loc); - op1_reg = tmp_op1->addr; - } - else if (op1_loc->kind == LLK_Gr) { - assert(op1_loc->kind != LLK_Fr); // comparisons available only for ints! - op1_reg = op1_loc->addr; - } - else { - ASSERT(0, "Wrong kind"); // comparisons available only for ints! - } - } - - if (lil_predicate_is_binary(p)) { - if (lil_operand_is_immed(op2)) { - int64 imm = (int64) lil_operand_get_immed(op2); - move_imm(tmp_op2, imm); - op2_reg = tmp_op2->addr; - } - else { // op2 is a variable - LilVariable *op2_var = lil_operand_get_variable(op2); - const LcgIpfLoc* op2_loc = - context.get_var_loc(op2_var, inst, ic, false); - if (op2_loc->kind == LLK_Stk) { - // load op2_loc into tmp_op2 - move(tmp_op2, op2_loc); - op2_reg = tmp_op2->addr; - } - else if (op2_loc->kind == LLK_Gr) { - op2_reg = op2_loc->addr; - } - else { - ASSERT(0, "Wrong kind"); // comparisons available only for ints! - } - } - } - else { // the predicate is unary, i.e. the 2nd op is r0 - op2_reg = 0; - } - - // do the comparison using a cmp instruction, place result in tmp_pred - Int_Comp_Rel emitter_p = icmp_invalid; - switch (p) { - case LP_IsZero: - case LP_Eq: - emitter_p = icmp_eq; break; - case LP_IsNonzero: - case LP_Ne: - emitter_p = icmp_ne; break; - case LP_Le: - emitter_p = icmp_le; break; - case LP_Lt: - emitter_p = icmp_lt; break; - case LP_Ule: - emitter_p = icmp_leu; break; - case LP_Ult: - emitter_p = icmp_ltu; break; - default: - ASSERT(0, "Unknown predicate"); // should never be reached - } - - emitter.ipf_cmp(emitter_p, cmp_unc, true_pr, false_pr, op1_reg, op2_reg, false, 0); - } - - // generate instructions that calculate a LIL address; - // return the GR number where the address can be found - // (this can be either the base register itself, or a temp register) - const LcgIpfLoc* do_addr(LilVariable *base, unsigned scale, LilVariable *index, POINTER_SIZE_INT offset) { - // locations for base and index (NULL if base or index doesn't exist) - const LcgIpfLoc *bloc=NULL, *iloc=NULL; - // shift amount, if index exists - unsigned shift=0; - - if (base != NULL) { - const LcgIpfLoc* orig_bloc = - context.get_var_loc(base, inst, ic, false); - assert(orig_bloc->kind == LLK_Gr || orig_bloc->kind == LLK_Stk); - if (orig_bloc->kind == LLK_Stk) { - move(tmp_addr1, orig_bloc); - bloc = tmp_addr1; - } - else { - bloc = orig_bloc; - } - } - - if (index != NULL && scale != 0) { - const LcgIpfLoc* orig_iloc = - context.get_var_loc(index, inst, ic, false); - assert(orig_iloc->kind == LLK_Gr || orig_iloc->kind == LLK_Stk); - - // fetch index from memory if needed - if (orig_iloc->kind == LLK_Stk) { - move(tmp_res, orig_iloc); - iloc = tmp_addr2; - } - else { - iloc = orig_iloc; - } - - // determine size of shift - while (scale > 1) { - scale >>= 1; - shift++; - } - assert(shift >= 1 && shift <= 4); - } - - if (bloc) { - if (offset) { - add_imm(tmp_addr3, bloc, offset); - if (iloc) - emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, tmp_addr3->addr, current_predicate); - return tmp_addr3; - } - else { // no offset - if (iloc) { - emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, bloc->addr, current_predicate); - return tmp_addr3; - } - else // no index - return bloc; - } - } // if (bloc) - else { // no base - if (offset) { - move_imm(tmp_addr3, offset); - if (iloc) - emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, tmp_addr3->addr, current_predicate); - } - else { // no offset - if (iloc) - emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, 0, current_predicate); - else // no index - ASSERT(0, "Can't have no base, no index and no offset"); - } - return tmp_addr3; - } // else (bloc) - } // do_addr - - - // implements the copying of incoming to outgoing args in in2out and tailcall - void do_in_to_out() - { - assert(!context.entry_sig_is_arbitrary()); - unsigned n_inputs = context.get_num_inputs(); - unsigned i; - for (i=0; i<8 && ikind==LLK_Stk && out_loc->kind==LLK_Stk); - add_imm(tmp_addr1, sp, in_loc->addr); - add_imm(tmp_addr2, sp, out_loc->addr); - for(; iaddr, tmp_addr1->addr, 8, current_predicate); - emitter.ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, tmp_addr2->addr, tmp_res->addr, 8, current_predicate); - } - } // do_in_to_out - - - // sets up the stack frame - void set_stk_frame() - { - unsigned stk_size = context.get_stk_size(); - if (stk_size != 0) { - add_imm(sp, sp, -(int)stk_size); - } - } // set_stk_frame - - // unsets the stack frame - void unset_stk_frame() - { - unsigned stk_size = context.get_stk_size(); - if (stk_size != 0) { - add_imm(sp, sp, (int)stk_size); - } - } // unset_stk_frame - - // moves FR inputs, originally residing in f8-f15, into the stack - void move_FR_inputs() - { - if (context.must_move_fr_inputs()) { - unsigned n_fr_inputs = context.get_num_fr_inputs(); - int start_offset = context.get_input_save_offset(); - - // using the same address reg in all stores will create too many dependencies; - // alternate the address reg between tmp_addr1 and tmp_addr2 for more ILP - add_imm(tmp_addr1, sp, start_offset); - if (n_fr_inputs >=2) - add_imm(tmp_addr2, sp, start_offset+16); - - for (unsigned i=0; i < n_fr_inputs; i++) { - unsigned addr_reg = (i%2) ? tmp_addr2->addr : tmp_addr1->addr; - emitter.ipf_stf_inc_imm(float_mem_size_d, mem_st_spill, mem_none, - addr_reg, 8+i, 32, current_predicate); - } - } - } // move_FR_inputs - - // moves FR inputs from the stack back to f8-f15 - void unmove_FR_inputs() - { - if (context.must_move_fr_inputs()) { - unsigned n_fr_inputs = context.get_num_fr_inputs(); - int start_offset = context.get_input_save_offset(); - - // using the same address reg in all stores will create too many dependencies; - // alternate the address reg between tmp_addr1 and tmp_addr2 for more ILP - add_imm(tmp_addr1, sp, start_offset); - if (n_fr_inputs >=2) - add_imm(tmp_addr2, sp, start_offset+16); - - for (unsigned i=0; i < n_fr_inputs; i++) { - unsigned addr_reg = (i%2) ? tmp_addr2->addr : tmp_addr1->addr; - emitter.ipf_ldf_inc_imm(float_mem_size_d, mem_ld_fill, mem_none, 8+i, addr_reg, 32, current_predicate); - } - } - } // unmove_FR_inputs - - - // emits the code that needs to go at the very beginning of the code stub - void do_entry() - { - assert(current_predicate==0); - - // start with an alloc instruction - const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); - if (pfs_save_gr) { - emitter.ipf_alloc(pfs_save_gr->addr, - context.get_input_space_size(), - context.get_local_space_size(), - context.get_output_space_size(), - 0); // no rotating regs! - } - - // save b0 - const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); - if (return_save_gr) { - emitter.ipf_mfbr(return_save_gr->addr, BRANCH_RETURN_LINK_REG); - } - - // save gp - const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); - if (gp_save_gr) - emitter.ipf_mov(gp_save_gr->addr, GP_REG); - - set_stk_frame(); - move_FR_inputs(); - } // do_entry - - - - /********************** - * visitor functions - */ - - unsigned current_predicate; // Predicate all instructions with this instruction (may not work for all instructions) - -public: - - void ret() - { - unset_stk_frame(); - - // restore gp - const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); - if (gp_save_gr) - emitter.ipf_mov(GP_REG, gp_save_gr->addr, current_predicate); - - // restore b0 - const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); - if (return_save_gr) { - emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, return_save_gr->addr, current_predicate); - } - - // restore ar.pfs - const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); - if (pfs_save_gr) - emitter.ipf_mtap(AR_pfs, pfs_save_gr->addr, current_predicate); - - // if this ret is going back to LIL or managed code, reset ar.ccv - LilCc call_conv = lil_sig_get_cc(context.get_entry_sig()); - if (context.must_reset_ccv() && - (call_conv == LCC_Managed || - call_conv == LCC_Rth)) { - emitter.ipf_mtap(AR_ccv, 0, current_predicate); - } - - // return - emitter.ipf_brret(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, current_predicate); - } // ret - - - void label(LilLabel lab) { - emitter.set_target(context.get_label_id(lab)); - } // label - - - void locals(unsigned) { - // nothing to be done here; - // has been taken care of in the prepass - } // locals - - - void std_places(unsigned) { - // nothing to be done here; - // has been taken care of in the prepass - } // std_places - - void alloc(LilVariable* var, unsigned sz) { - // the actual size allocated will always be a multiple of 8 - sz = (sz + 0x7) & ~0x7; - int alloc_offset = context.get_alloc_start_offset() + current_alloc; - current_alloc += sz; - - // var = sp + alloc_offset - const LcgIpfLoc* var_loc = - context.get_var_loc(var, inst, ic, true); - add_imm(var_loc, sp, alloc_offset); - } // alloc - - - void asgn(LilVariable* dest, enum LilOperation o, LilOperand* op1, LilOperand*op2) - { - const LcgIpfLoc* dest_loc = - context.get_var_loc(dest, inst, ic, true); - - if (o == LO_Mov) { - if (lil_operand_is_immed(op1)) { - move_imm(dest_loc, lil_operand_get_immed(op1)); - } - else { - const LcgIpfLoc* src_loc = - context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); - move(dest_loc, src_loc); - } - } - else 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 - int64 op1_imm = (int64) lil_operand_get_immed(op1); - int64 op2_imm = (int64) lil_operand_get_immed(op2); - POINTER_SIZE_INT 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 LIL operation"); // control should never reach this point - } - move_imm(dest_loc, result); - } - else if (lil_operand_is_immed(op1)) { - const LcgIpfLoc* src2_loc = context.get_var_loc(lil_operand_get_variable(op2), inst, ic, false); - switch (o) { - case LO_Add: - bin_op_imm(LO_Add, dest_loc, src2_loc, lil_operand_get_immed(op1)); - break; - case LO_Sub: - sub_op_imm(dest_loc, lil_operand_get_immed(op1), src2_loc); - break; - case LO_SgMul: - case LO_Shl: - move_imm(tmp_res, (int64) lil_operand_get_immed(op1)); - bin_op(o, dest_loc, tmp_res, src2_loc); - break; - default: - ASSERT(0, "Unexpected LIL operation"); // control should never reach this point - } - } - else if (lil_operand_is_immed(op2)) { - const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); - bin_op_imm(o, dest_loc, src1_loc, lil_operand_get_immed(op2)); - } - else { // both operands non-immediate - const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); - const LcgIpfLoc* src2_loc = context.get_var_loc(lil_operand_get_variable(op2), inst, ic, false); - bin_op(o, dest_loc, src1_loc, src2_loc); - } - } - else { // unary operation - if (lil_operand_is_immed(op1)) { - int64 imm = (int64) lil_operand_get_immed(op1); // typecast to get signed type - int64 result = 0; - switch (o) { - case LO_Neg: - result = -imm; - break; - case LO_Not: - result = ~imm; - break; - case LO_Sx1: - result = (int64) (int8) imm; - break; - case LO_Sx2: - result = (int64) (int16) imm; - break; - case LO_Sx4: - result = (int64) (int32) imm; - break; - case LO_Zx1: - result = (int64) (uint64) (uint8) imm; - break; - case LO_Zx2: - result = (int64) (uint64) (uint16) imm; - break; - case LO_Zx4: - result = (int64) (uint64) (uint32) imm; - break; - default: - ASSERT(0, "Unexpected LIL operation"); // control should never reach this point - } - move_imm(dest_loc, result); - } - else { // non-immediate operand - const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); - un_op(o, dest_loc, src1_loc); - } - } - } // asgn - - - void ts(LilVariable* var) { - const LcgIpfLoc* var_loc = - context.get_var_loc(var, inst, ic, true); - move(var_loc, tp); - } // ts - - void handles(LilOperand* op) - { - assert(current_predicate==0); - - if (lil_operand_is_immed(op)) { - m2n_gen_set_local_handles_imm(&emitter, lil_operand_get_immed(op)); - } else { - const LcgIpfLoc* op_loc = context.get_var_loc(lil_operand_get_variable(op), inst, ic, false); - assert(op_loc->kind == LLK_Gr); - m2n_gen_set_local_handles(&emitter, op_loc->addr); - } - } // handles - - void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, LilLdX ext) - { - // form the address - const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); - - // decide the size of the load (1, 2, 4, or 8) - Int_Mem_Size size = int_mem_size_1; - Sxt_Size ext_size = sxt_size_invalid; - bool sxt = ext==LLX_Sign; - switch(t) { - case LT_G1: - size = int_mem_size_1; ext_size = sxt_size_1; break; - case LT_G2: - size = int_mem_size_2; ext_size = sxt_size_2; break; - case LT_G4: - size = int_mem_size_4; ext_size = sxt_size_4; break; - case LT_G8: - case LT_Ref: - case LT_PInt: - size = int_mem_size_8; sxt = false; break; - case LT_F4: - case LT_F8: - case LT_Void: - ASSERT(0, "Unexpected LIL type"); // types not allowed in loads / stores - default: - ASSERT(0, "Unknown LIL type"); // invalid value in type - } - - Ld_Flag ld_flag = mem_ld_none; - switch (acqrel) { - case LAR_Acquire: ld_flag = mem_ld_acq; break; - case LAR_Release: ASSERT(0, "Unexpected acqrel value"); break; - case LAR_None: break; - } - - const LcgIpfLoc* dst_loc = context.get_var_loc(dst, inst, ic, true); - if (dst_loc->kind == LLK_Stk) { - // load value into tmp_res - emitter.ipf_ld(size, ld_flag, mem_none, tmp_res->addr, addr_loc->addr, current_predicate); - if (sxt) - emitter.ipf_sxt(ext_size, tmp_res->addr, tmp_res->addr, current_predicate); - // store value into dst_loc - move(dst_loc, tmp_res); - } - else if (dst_loc->kind == LLK_Gr) { - // load value into dst_loc - emitter.ipf_ld(size, ld_flag, mem_none, dst_loc->addr, addr_loc->addr, current_predicate); - if (sxt) - emitter.ipf_sxt(ext_size, dst_loc->addr, dst_loc->addr, current_predicate); - } - else { - ASSERT(0, "Unexpected kind"); // dst_loc shouldn't be FR or STK16 - } - } // ld - - - void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, LilOperand* src) - { - // move the address into tmp_addr1 - const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); - - // decide the size of the store (1, 2, 4, or 8) - Int_Mem_Size size = int_mem_size_1; - switch(t) { - case LT_G1: - size = int_mem_size_1; break; - case LT_G2: - size = int_mem_size_2; break; - case LT_G4: - size = int_mem_size_4; break; - case LT_G8: - case LT_Ref: - case LT_PInt: - size = int_mem_size_8; break; - case LT_F4: - case LT_F8: - case LT_Void: - ASSERT(0, "Unexpected LIL type"); // types not allowed in loads / stores - default: - ASSERT(0, "Unknown LIL type"); // invalid value in type - } - - St_Flag st_flag = mem_st_none; - switch (acqrel) { - case LAR_Acquire: ASSERT(0, "Unexpected value of acqrel"); break; - case LAR_Release: st_flag = mem_st_rel; break; - case LAR_None: break; - } - - const LcgIpfLoc* src_loc = NULL; - if (lil_operand_is_immed(src)) { - // move source into temp reg - POINTER_SIZE_INT imm = lil_operand_get_immed(src); - if (imm == 0) { - //just use r0 - src_loc = r0; - } - else { - move_imm(tmp_op1, lil_operand_get_immed(src)); - src_loc = tmp_op1; - } - } - else { - LilVariable *src_var = lil_operand_get_variable(src); - src_loc = context.get_var_loc(src_var, inst, ic, false); - } - if (src_loc->kind == LLK_Stk) { - // load src_loc into tmp_res - move(tmp_res, src_loc); - // store tmp_res - emitter.ipf_st(size, st_flag, mem_none, addr_loc->addr, tmp_res->addr, current_predicate); - } - else if (src_loc->kind == LLK_Gr) { - // store src_loc - emitter.ipf_st(size, st_flag, mem_none, addr_loc->addr, src_loc->addr, current_predicate); - } - else { - ASSERT(0, "Unexpected kind"); // src_loc shouldn't be FR or STK16! - } - } // st - - - void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel) - { - assert(acqrel==LAR_None); - - const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); - // load addr1 into tmp_res - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, - tmp_res->addr, addr_loc->addr, current_predicate); - // inc tmp_res - add_imm(tmp_res, tmp_res, 1); - // store tmp_res back to addr1 - emitter.ipf_st(int_mem_size_8, mem_st_none, mem_none, - addr_loc->addr, tmp_res->addr, current_predicate); - } // inc - - void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, - LilOperand* cmp, LilOperand* src, LilLabel label) - { - assert(current_predicate==0); - - // move the address into tmp_addr1 - const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); - - // decide the size of the store (1, 2, 4, or 8) - Int_Mem_Size size = int_mem_size_1; - switch(t) { - case LT_G1: - size = int_mem_size_1; break; - case LT_G2: - size = int_mem_size_2; break; - case LT_G4: - size = int_mem_size_4; break; - case LT_G8: - case LT_Ref: - case LT_PInt: - size = int_mem_size_8; break; - case LT_F4: - case LT_F8: - case LT_Void: - ASSERT(0, "Unexpected LIL type"); // types not allowed in loads / stores - default: - ASSERT(0, "Unknown LIL type"); // invalid value in type - } - - Cmpxchg_Flag flag = mem_cmpxchg_acq; - switch (acqrel) { - case LAR_Acquire: flag = mem_cmpxchg_acq; break; - case LAR_Release: flag = mem_cmpxchg_rel; break; - default: - ASSERT(0, "Unexpected value of acqrel"); - } - - const LcgIpfLoc* src_loc = NULL; - if (lil_operand_is_immed(src)) { - // move source into temp reg - POINTER_SIZE_INT imm = lil_operand_get_immed(src); - if (imm == 0) { - //just use r0 - src_loc = r0; - } - else { - move_imm(tmp_op1, lil_operand_get_immed(src)); - src_loc = tmp_op1; - } - } - else { - LilVariable *src_var = lil_operand_get_variable(src); - src_loc = context.get_var_loc(src_var, inst, ic, false); - if (src_loc->kind == LLK_Stk) { - move(tmp_op1, src_loc); - src_loc = tmp_op1; - } - } - - const LcgIpfLoc* cmp_loc = NULL; - if (lil_operand_is_immed(cmp)) { - // move source into temp reg - POINTER_SIZE_INT imm = lil_operand_get_immed(cmp); - if (imm == 0) { - //just use r0 - cmp_loc = r0; - } - else { - move_imm(tmp_op2, lil_operand_get_immed(cmp)); - cmp_loc = tmp_op2; - } - } - else { - LilVariable *cmp_var = lil_operand_get_variable(cmp); - cmp_loc = context.get_var_loc(cmp_var, inst, ic, false); - if (cmp_loc->kind == LLK_Stk) { - move(tmp_op2, cmp_loc); - cmp_loc = tmp_op2; - } - } - - emitter.ipf_mtap(AR_ccv, cmp_loc->addr, current_predicate); - emitter.ipf_cmpxchg(size, flag, mem_none, tmp_res->addr, addr_loc->addr, src_loc->addr, current_predicate); - emitter.ipf_mtap(AR_ccv, 0, current_predicate); - emitter.ipf_cmp(icmp_ne, cmp_unc, tmp_pred, 0, tmp_res->addr, cmp_loc->addr, false, 0); - unsigned label_id = context.get_label_id(label); - emitter.ipf_br(br_cond, br_few, br_dptk, br_none, label_id, tmp_pred); - } - - void j(LilLabel lab) - { - unsigned label_id = context.get_label_id(lab); - emitter.ipf_br(br_cond, br_few, br_sptk, br_none, label_id, current_predicate); - } // j - - void jc(LilPredicate p, LilOperand* op1, LilOperand* op2, LilLabel label) - { - assert(current_predicate==0); - - // compute the condition - do_cond(p, op1, op2, tmp_pred, 0); - - // the actual branch - unsigned label_id = context.get_label_id(label); - emitter.ipf_br(br_cond, br_few, br_dptk, br_none, label_id, tmp_pred); - } // jc - - void out(LilSig* sig) - { - cur_out_sig = sig; - // nothing else to do; space has been reserved already - } // out - - void in2out(LilSig* sig) - { - assert(!context.entry_sig_is_arbitrary()); - cur_out_sig = sig; - do_in_to_out(); - } // in2out - - - void call(LilOperand* target, LilCallKind kind) - { - assert(current_predicate==0); - - LilSig *out_sig = lil_ic_get_out_sig(ic); - LilCc call_conv = (kind == LCK_TailCall) ? lil_sig_get_cc(context.get_entry_sig()) : - lil_sig_get_cc(out_sig); - - // reset ar.ccv if this call could lead to managed or LIL code - if (context.must_reset_ccv() && - (call_conv == LCC_Managed || - call_conv == LCC_Rth)) { - emitter.ipf_mtap(AR_ccv, 0); - } - - if (kind == LCK_TailCall) { - // move FR inputs to their original place - unmove_FR_inputs(); - - // unset stack, so that old stacked arguments become visible - unset_stk_frame(); - - // restore gp - const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); - if (gp_save_gr) - emitter.ipf_mov(GP_REG, gp_save_gr->addr); - - // restore b0 - const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); - if (return_save_gr) { - emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, return_save_gr->addr); - } - - // restore ar.pfs - const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); - if (pfs_save_gr) - emitter.ipf_mtap(AR_pfs, pfs_save_gr->addr); - - // jump (instead of calling) - if (lil_operand_is_immed(target)) { - void **proc_ptr = (void **) lil_operand_get_immed(target); - emit_branch_with_gp(emitter, proc_ptr); - } - else { - LilVariable *var = lil_operand_get_variable(target); - const LcgIpfLoc* loc = - context.get_var_loc(var, inst, ic, false); - unsigned call_addr_gr = 0; - if (loc->kind == LLK_Gr) { - call_addr_gr = loc->addr; - } - else if (loc->kind == LLK_Stk) { - // load loc into tmp_res - move(tmp_res, loc); - call_addr_gr = tmp_res->addr; - } - else { - ASSERT(0, "Unexpected kind"); // address can't be FP! - } - emitter.ipf_mtbr(tmp_br, call_addr_gr); - emitter.ipf_bri(br_cond, br_many, br_sptk, br_none, tmp_br); - } - - return; - } // kind == LCK_TailCall - - // kind == LCK_Call or kind == LCK_CallNoRet - - if (lil_operand_is_immed(target)) { - void** proc_ptr = (void **) lil_operand_get_immed(target); - void* fn_addr = proc_ptr[0]; - void* gp_new = proc_ptr[1]; - void* gp_old = get_vm_gp_value(); - if (gp_new != gp_old) { - // Set new gp - emit_mov_imm_compactor(emitter, GP_REG, (uint64)gp_new); - } - emit_mov_imm_compactor(emitter, tmp_res->addr, (uint64)fn_addr, 0); - if (context.has_push_m2n()) { - emitter.ipf_mtbr(BRANCH_CALL_REG, tmp_res->addr); - emit_mov_imm_compactor(emitter, tmp_res->addr, (uint64)m2n_gen_flush_and_call(), 0); - } - emitter.ipf_mtbr(tmp_br, tmp_res->addr); - emitter.ipf_bricall(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, tmp_br); - if (gp_new != gp_old) { - // Restore gp - const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); - assert(gp_save_gr); - emitter.ipf_mov(GP_REG, gp_save_gr->addr, current_predicate); - } - } - else { - LilVariable *var = lil_operand_get_variable(target); - const LcgIpfLoc* loc = - context.get_var_loc(var, inst, ic, false); - unsigned call_addr_gr = 0; - if (loc->kind == LLK_Gr) { - call_addr_gr = loc->addr; - } - else if (loc->kind == LLK_Stk) { - // load loc into tmp_res - move(tmp_res, loc); - call_addr_gr = tmp_res->addr; - } - else { - ASSERT(0, "Unexpected kind"); // address can't be FP! - } - if (context.has_push_m2n()) { - emitter.ipf_mtbr(BRANCH_CALL_REG, call_addr_gr); - emit_mov_imm_compactor(emitter, call_addr_gr, (uint64)m2n_gen_flush_and_call(), 0); - } - emitter.ipf_mtbr(tmp_br, call_addr_gr); - emitter.ipf_bricall(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, tmp_br); - } - } // call - - - void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) - { - assert(current_predicate==0); - m2n_gen_push_m2n(&emitter, method, current_frame_type, handles, context.get_stk_size(), 0, 0, false); - } // push_m2n - - void m2n_save_all() - { - assert(current_predicate==0); - add_imm(sp, sp, context.get_extra_saves_start_offset()+M2N_EXTRA_SAVES_SPACE-16); - m2n_gen_save_extra_preserved_registers(&emitter); - add_imm(sp, sp, -(int64)(context.get_extra_saves_start_offset()-16)); - } // m2n_save_all - - void pop_m2n() - { - assert(current_predicate==0); - // see if handles are present - bool handles = lil_ic_get_m2n_state(ic) == LMS_Handles; - int target = -1; - if (handles) - // this pop_m2n will need to use an emitter target - target = (int) context.get_unused_target(); - M2nPreserveRet pr; - LilType rt = lil_ic_get_ret_type(ic); - if (rt==LT_Void) - pr = MPR_None; - else if (rt==LT_F4 || rt==LT_F8) - pr = MPR_Fr; - else - pr = MPR_Gr; - m2n_gen_pop_m2n(&emitter, handles, pr, false, context.get_first_output_gr(), target); - } // pop_m2n - - void print(char *str, LilOperand *o) - { - assert(current_predicate==0); - - // this is a no-op if debugging is off -#ifdef STUB_DEBUG - unsigned print_reg; - if (lil_operand_is_immed(o)) { - // dummy operand; print r0 - print_reg = 0; - } - else { - LilVariable *var = lil_operand_get_variable(o); - const LcgIpfLoc* var_loc = - context.get_var_loc(var, inst, ic, false); - assert(var_loc->kind == LLK_Gr); - print_reg = var_loc->addr; - } - emit_print_reg(emitter, str, print_reg, context.get_num_inputs(), - context.get_first_output_gr(), false); -#endif // STUB_DEBUG - } // print - -public: - /****************************** - * constructors and destructors - */ - void *operator new(size_t sz, tl::MemoryPool &m) { - return m.alloc(sz); - } - - - LcgIpfCodeGen(LilCodeStub* cs, Merced_Code_Emitter& e, LcgIpfContext& c, tl::MemoryPool& m): - cs(cs), - emitter(e), - context(c), - mem(m), - iter(cs, true), - ic(NULL), - current_alloc(0), - cur_out_sig(NULL), - current_predicate(0) - { - // emit entry code - do_entry(); - - while (!iter.at_end()) { - ic = iter.get_context(); - inst = iter.get_current(); - lil_visit_instruction(inst, this); - iter.goto_next(); - } - } // LcgIpfCodeGen (constructor) - -}; // class LcgIpfCodeGen - - -// initialization of static members -// TODO: we have to get rid of memory pool in static area due to initialization problems -static tl::MemoryPool loc_mem; -const LcgIpfLoc* LcgIpfCodeGen::gp = new(loc_mem) LcgIpfLoc(LLK_Gr, 1); -const LcgIpfLoc* LcgIpfCodeGen::sp = new(loc_mem) LcgIpfLoc(LLK_Gr, 12); -const LcgIpfLoc* LcgIpfCodeGen::r0 = new(loc_mem) LcgIpfLoc(LLK_Gr, 0); -const LcgIpfLoc* LcgIpfCodeGen::tp = new(loc_mem) LcgIpfLoc(LLK_Gr, 4); // thread pointer is in r4 -const LcgIpfLoc* LcgIpfCodeGen::tmp_op1 = new(loc_mem) LcgIpfLoc(LLK_Gr, 31); // r30 and r31 used as temp operand locations -const LcgIpfLoc* LcgIpfCodeGen::tmp_op2 = new(loc_mem) LcgIpfLoc(LLK_Gr, 30); -const LcgIpfLoc* LcgIpfCodeGen::tmp_res = new(loc_mem) LcgIpfLoc(LLK_Gr, 29); -const LcgIpfLoc* LcgIpfCodeGen::tmp_f = new(loc_mem) LcgIpfLoc(LLK_Fr, 6); -const LcgIpfLoc* LcgIpfCodeGen::tmp_addr1 = new(loc_mem) LcgIpfLoc(LLK_Gr, 28); -const LcgIpfLoc* LcgIpfCodeGen::tmp_addr2 = new(loc_mem) LcgIpfLoc(LLK_Gr, 27); -const LcgIpfLoc* LcgIpfCodeGen::tmp_addr3 = new(loc_mem) LcgIpfLoc(LLK_Gr, 26); -const LcgIpfLoc* LcgIpfCodeGen::tmp_addl = new(loc_mem) LcgIpfLoc(LLK_Gr, 3); -const unsigned LcgIpfCodeGen::tmp_pred = 6; // use p6 (scratch) for any conditional jumps -const unsigned LcgIpfCodeGen::tmp_br = 6; //use BR 6 as a temporary branch address register - - - -LilCodeGeneratorIpf::LilCodeGeneratorIpf() - : LilCodeGenerator() -{ -} - -NativeCodePtr LilCodeGeneratorIpf::compile_main(LilCodeStub* cs, size_t* stub_size, PoolManager* code_pool) { - - // start a memory manager - tl::MemoryPool m; - - // get context info and do a prepass - LcgIpfContext context(cs, m); - - // initiate an IPF code emitter - Merced_Code_Emitter emitter(m, 100, context.get_num_targets()); - emitter.memory_type_is_unknown(); // fix later - emitter.disallow_instruction_exchange(); - - LcgIpfCodeGen codegen(cs, emitter, context, m); - - // get the goodies from the emitter - emitter.flush_buffer(); - *stub_size = emitter.get_size(); - NativeCodePtr buffer = allocate_memory(*stub_size, code_pool); - emitter.copy((char*)buffer); - flush_hw_cache((Byte*)buffer, *stub_size); - sync_i_cache(); - - return buffer; -} // compile_main - -GenericFunctionPointer lil_npc_to_fp(NativeCodePtr ncp) -{ - void** p = (void**)STD_MALLOC(2*sizeof(void*)); - assert(p); - p[0] = ncp; - p[1] = get_vm_gp_value(); - - return (GenericFunctionPointer)p; -} // lil_npc_to_fp diff --git a/vm/port/src/lil/ipf/pim/m2n_ipf.cpp b/vm/port/src/lil/ipf/pim/m2n_ipf.cpp deleted file mode 100644 index d6f1068..0000000 --- a/vm/port/src/lil/ipf/pim/m2n_ipf.cpp +++ /dev/null @@ -1,514 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#define LOG_DOMAIN = "vm.m2n" -#include "cxxlog.h" - -#include "Code_Emitter.h" -#include "environment.h" -#include "m2n.h" -#include "m2n_ipf_internal.h" -#include "vm_ipf.h" -#include "vm_threads.h" -#include "open/types.h" -#include "open/vm_util.h" -#include "stub_code_utils.h" -#include "interpreter.h" -#include "exceptions.h" - - -////////////////////////////////////////////////////////////////////////// -// Utilities - -extern "C" void *do_flushrs_asm(); - -extern "C" void *do_flushrs() -{ - return do_flushrs_asm(); -} //do_flushrs - -// Given a bsp value for register 32 and a stacked register number -// return a pointer to where the stacked register is spilled -uint64* get_stacked_register_address(uint64* bsp, unsigned reg) -{ - if (interpreter_enabled()) { - return interpreter.interpreter_get_stacked_register_address(bsp, reg); - } - assert(bsp && 32<=reg && reg<128); - unsigned r = (reg-32)<<3; - uint64 b = (uint64)bsp; - uint64 d4 = b+r; - uint64 d5 = (b&0x1f8)+r; - if (d5>=63*8) - if (d5>=126*8) - d4 += 16; - else - d4 += 8; - return (uint64*)d4; -} - -// Get the bsp value for register 32 of the M2nFrame -uint64* m2n_get_bsp(M2nFrame* m2nf) -{ - return (uint64*)m2nf; -} - -uint64* m2n_get_extra_saved(M2nFrame* m2nf) -{ - do_flushrs(); - return (uint64*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_EXTRA_SAVED_PTR); -} - -////////////////////////////////////////////////////////////////////////// -// M2nFrame Interface - -//***** Generic Interface - -// fill m2n frame as empty -void m2n_null_init(M2nFrame* m2n){ - memset(m2n, 0, sizeof(M2nFrame)); -} - -VMEXPORT // temporary solution for interpreter unplug -M2nFrame* m2n_get_last_frame() -{ - return (M2nFrame*)p_TLS_vmthread->last_m2n_frame; -} - -VMEXPORT // temporary solution for interpreter unplug -M2nFrame* m2n_get_last_frame(VM_thread* thread) -{ - return (M2nFrame*)thread->last_m2n_frame; -} - -VMEXPORT // temporary solution for interpreter unplug -void m2n_set_last_frame(M2nFrame* lm2nf) -{ - vm_thread_t vm_thread = jthread_self_vm_thread_unsafe(); - vm_thread->last_m2n_frame = lm2nf; -} - -VMEXPORT -void m2n_set_last_frame(VM_thread* thread, M2nFrame* lm2nf) -{ - thread->last_m2n_frame = lm2nf; -} - -VMEXPORT // temporary solution for interpreter unplug -M2nFrame* m2n_get_previous_frame(M2nFrame* m2nfl) -{ - assert(m2nfl); - do_flushrs(); - return (M2nFrame*)*get_stacked_register_address(m2n_get_bsp(m2nfl), M2N_SAVED_M2NFL); -} - -ObjectHandles* m2n_get_local_handles(M2nFrame* m2nf) -{ - assert(m2nf); - do_flushrs(); - return (ObjectHandles*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_OBJECT_HANDLES); -} - -void m2n_set_local_handles(M2nFrame* m2nf, ObjectHandles* handles) -{ - assert(m2nf); - do_flushrs(); - uint64* p_head = get_stacked_register_address(m2n_get_bsp(m2nf), M2N_OBJECT_HANDLES); - *p_head = (uint64)handles; -} - -NativeCodePtr m2n_get_ip(M2nFrame* m2nf) -{ - assert(m2nf); - do_flushrs(); - uint64 * UNUSED bsp = (uint64 *)m2nf; - assert(bsp); - return (NativeCodePtr)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_SAVED_RETURN_ADDRESS); -} - -// 20040708 New function - needs proper implementation. -void m2n_set_ip(M2nFrame* lm2nf, NativeCodePtr ip) -{ - assert(lm2nf); - ABORT("Not implemented"); -} - -// sets pointer to the registers used for jvmti PopFrame -void set_pop_frame_registers(M2nFrame* m2nf, Registers* regs) { - // FIXME: not sure we want to support this function on IPF - assert(0); - abort(); -} - -// returns pointer to the registers used for jvmti PopFrame -Registers* get_pop_frame_registers(M2nFrame* m2nf) { - // FIXME: not sure we want to support this function on IPF - assert(0); - abort(); - return 0; -} - -Method_Handle m2n_get_method(M2nFrame* m2nf) -{ - assert(m2nf); - do_flushrs(); - uint64 * UNUSED bsp = (uint64 *)m2nf; - assert(bsp); - return (Method_Handle)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_METHOD); -} - -// Returns type of noted m2n frame -frame_type m2n_get_frame_type(M2nFrame* m2nf) { - assert(m2nf); - do_flushrs(); - uint64 * UNUSED bsp = (uint64 *)m2nf; - assert(bsp); - return (frame_type)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_FRAME_TYPE); -} - -// Sets type of noted m2n frame -void m2n_set_frame_type(M2nFrame* m2nf, frame_type m2nf_type) { - assert(m2nf); - do_flushrs(); - uint64 * UNUSED bsp = (uint64 *)m2nf; - assert(bsp); - *get_stacked_register_address(m2n_get_bsp(m2nf), M2N_FRAME_TYPE) = m2nf_type; -} - -size_t m2n_get_size() { - return sizeof(M2nFrame); -} - -//***** Stub Interface - -// Flushes register stack of the current thread into backing store and calls target procedure. -NativeCodePtr m2n_gen_flush_and_call() { - static NativeCodePtr addr = NULL; - - if (addr != NULL) { - return addr; - } - - tl::MemoryPool mem_pool; - Merced_Code_Emitter emitter(mem_pool, 2, 0); - emitter.disallow_instruction_exchange(); - emitter.memory_type_is_unknown(); - - // We need to remember pfs & b0 here but there is no space to save them in. - // Register stack contains valid outputs and we don't know how many registers are used. - // Memory stack holds output values beyound those 8 which are on register stack. - // The only place is general caller-saves registers. It is save to use them with out preserving - // because they are alredy preserved by the corresponding M2N frame. - - // r4 is used to keep a thread pointer...so let's use r5 & r6. - - emitter.ipf_mfap(PRESERV_GENERAL_REG1, AR_pfs); - emitter.ipf_mfbr(PRESERV_GENERAL_REG2, BRANCH_RETURN_LINK_REG); - - emitter.flush_buffer(); - emitter.ipf_flushrs(); - - emitter.ipf_bricall(br_many, br_sptk, br_none, BRANCH_RETURN_LINK_REG, BRANCH_CALL_REG); - - emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, PRESERV_GENERAL_REG2); - emitter.ipf_mtap(AR_pfs, PRESERV_GENERAL_REG1); - - emitter.ipf_brret(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG); - - addr = finalize_stub(emitter, ""); - return addr; -} - -unsigned m2n_gen_push_m2n(Merced_Code_Emitter* emitter, Method_Handle method, frame_type current_frame_type, bool handles, unsigned num_on_stack, unsigned num_local, unsigned num_out, bool do_alloc) -{ - // Allocate new frame - if (do_alloc) { - emitter->ipf_alloc(M2N_SAVED_PFS, 8, M2N_NUMBER_LOCALS+num_local, num_out, 0); - - // The alloc instruction saves pfs, now save return address and GP - emitter->ipf_mfbr(M2N_SAVED_RETURN_ADDRESS, BRANCH_RETURN_LINK_REG); - emitter->ipf_mov (M2N_SAVED_GP, GP_REG); - } - - // 20031205: The alloc writes the CFM and the mfpr reads it, so they must be separated by a stop bit, this is a brutal way of achieving this. - emitter->flush_buffer(); - - // Save predicates, SP, and callee saves general registers - emitter->ipf_adds(M2N_SAVED_SP, num_on_stack, SP_REG); - - emitter->ipf_mfpr(M2N_SAVED_PR ); - emitter->ipf_mfap(M2N_SAVED_UNAT, AR_unat); - emitter->ipf_mov (M2N_SAVED_R4, 4); - emitter->ipf_mov (M2N_SAVED_R5, 5); - emitter->ipf_mov (M2N_SAVED_R6, 6); - emitter->ipf_mov (M2N_SAVED_R7, 7); - - // Set object handles to NULL and set method information - emitter->ipf_mov(M2N_OBJECT_HANDLES, 0); - emit_mov_imm_compactor(*emitter, M2N_METHOD, (uint64)method); - emit_mov_imm_compactor(*emitter, M2N_FRAME_TYPE, (uint64)current_frame_type); - - const int P1 = SCRATCH_PRED_REG; - const int P2 = SCRATCH_PRED_REG2; - const int OLD_RSE_MODE = SCRATCH_GENERAL_REG2; - const int NEW_RSE_MODE = SCRATCH_GENERAL_REG3; - // SCRATCH_GENERAL_REG4 & SCRATCH_GENERAL_REG5 are reserved for std places. - const int BSP = SCRATCH_GENERAL_REG6; - const int IMM_8 = SCRATCH_GENERAL_REG7; - const int IMM_1F8 = SCRATCH_GENERAL_REG8; - const int TMP_REG = SCRATCH_GENERAL_REG9; - // Scratch branch register. - const int TMP_BRANCH_REG = 6; - - // Switch RSE to "forced lazy" mode. This is required to access RNAT. - emitter->ipf_mfap(OLD_RSE_MODE, AR_rsc); - emitter->ipf_dep(NEW_RSE_MODE, 0, OLD_RSE_MODE, 0, 2); - emitter->ipf_mtap(AR_rsc, NEW_RSE_MODE); - - // Flush must be the first instruction in the group. - emitter->flush_buffer(); - // Spill parent frames so that corresponding RNAT bits become valid. - emitter->ipf_flushrs(); - // Extract backing store pointer - emitter->ipf_mfap(BSP, AR_bsp); - // Remember parent RNAT collection. - emitter->ipf_mfap(M2N_EXTRA_RNAT, AR_rnat); - - // TODO: This is not fully legal reset nat bits for the whole m2n frame because it - // contains r4-r7 general registers which may have corresponding unat bits up. - emitter->ipf_mov(M2N_EXTRA_UNAT, 0); - -/* The following code spills M2N into backing store. - emitter->ipf_movl(IMM_1F8, 0, (uint64)0x1f8); - emitter->ipf_movl(IMM_8, 0, (uint64)0x8); - - // Forcebly spill M2N frame into backing store. - for(int i = M2N_NUMBER_INPUTS; i < M2N_NUMBER_LOCALS; i++) { - emitter->ipf_and(TMP_REG, IMM_1F8, BSP); - emitter->ipf_cmp(icmp_eq, cmp_none, P1, P2, IMM_1F8, TMP_REG); - emitter->ipf_add(BSP, BSP, IMM_8, P1); - emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_spill, mem_none, BSP, 32 + i, 8); - } - - // Remember UNAT collection for the current frame. - emitter->ipf_sub(BSP, BSP, IMM_8); - emitter->ipf_mfap(M2N_EXTRA_UNAT, AR_unat); - emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, BSP, M2N_EXTRA_UNAT); - - // Restore original UNAT. - emitter->ipf_mtap(AR_unat, M2N_SAVED_UNAT); - emitter->flush_buffer(); -*/ - - // Switch RSE to the original mode. - emitter->ipf_mtap(AR_rsc, OLD_RSE_MODE); - - // Link M2nFrame into list of current thread - size_t offset_lm2nf = (size_t)&((VM_thread*)0)->last_m2n_frame; - emitter->ipf_adds(SCRATCH_GENERAL_REG2, (int)offset_lm2nf, THREAD_PTR_REG); - emitter->ipf_ld(int_mem_size_8, mem_ld_none, mem_none, M2N_SAVED_M2NFL, SCRATCH_GENERAL_REG2); - emitter->ipf_mfap(SCRATCH_GENERAL_REG7, AR_bsp); - emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, SCRATCH_GENERAL_REG2, SCRATCH_GENERAL_REG7); - - return 32+8+M2N_NUMBER_LOCALS; -} - -void m2n_gen_set_local_handles(Merced_Code_Emitter* emitter, unsigned src_reg) -{ - emitter->ipf_mov(M2N_OBJECT_HANDLES, src_reg); -} - -void m2n_gen_set_local_handles_imm(Merced_Code_Emitter* emitter, uint64 imm_val) -{ - int64 UNUSED imm = (int64)imm_val; - assert(imm>=-0x200000 && imm<-0x200000); - emitter->ipf_movi(M2N_OBJECT_HANDLES, (int)imm_val); -} - -static void m2n_pop_local_handles() { - assert(!hythread_is_suspend_enabled()); - - exn_rethrow_if_pending(); - - M2nFrame *m2n = m2n_get_last_frame(); - free_local_object_handles2(m2n_get_local_handles(m2n)); -} - -static void m2n_free_local_handles() { - assert(!hythread_is_suspend_enabled()); - - if (exn_raised()) { - exn_rethrow(); - } - - M2nFrame * m2n = m2n_get_last_frame(); - // iche free_local_object_handles3(m2n->local_object_handles); - free_local_object_handles3(m2n_get_local_handles(m2n)); // iche -} - -void m2n_gen_pop_m2n(Merced_Code_Emitter* emitter, bool handles, M2nPreserveRet preserve_ret, bool do_alloc, unsigned out_reg, int target) -{ - unsigned free_target; - - if (handles) { - assert(target != -1); // make sure a target has been provided - // Do we need to call free? - free_target = (unsigned) target; - emitter->ipf_cmp(icmp_eq, cmp_none, SCRATCH_PRED_REG, SCRATCH_PRED_REG2, M2N_OBJECT_HANDLES, 0); - emitter->ipf_br(br_cond, br_many, br_spnt, br_none, free_target, SCRATCH_PRED_REG); - } - // Yes, save return register - if (preserve_ret == MPR_Gr) { - emitter->ipf_add(6, RETURN_VALUE_REG, 0); - } else if (preserve_ret == MPR_Fr) { - emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, RETURN_VALUE_REG, unsigned(-16)); - } - - if (handles) { - emit_call_with_gp(*emitter, (void**)m2n_pop_local_handles, false); - } else { - emit_call_with_gp(*emitter, (void**)m2n_free_local_handles, false); - } - - // Restore return register - if (preserve_ret == MPR_Gr) { - emitter->ipf_add(RETURN_VALUE_REG, 6, 0); - } else if (preserve_ret == MPR_Fr) { - emitter->ipf_adds(SP_REG, 16, SP_REG); - emitter->ipf_ldf(float_mem_size_e, mem_ld_fill, mem_none, RETURN_VALUE_REG, SP_REG); - } - - - if (handles) { - emitter->set_target(free_target); - } - - // Unlink the M2nFrame from the list of the current thread - size_t offset_lm2nf = (size_t)&((VM_thread*)0)->last_m2n_frame; - emitter->ipf_adds(SCRATCH_GENERAL_REG2, (int)offset_lm2nf, THREAD_PTR_REG); - emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, SCRATCH_GENERAL_REG2, M2N_SAVED_M2NFL); - - // Restore callee saved general registers, predicates, return address, and pfs - emitter->ipf_mov (7, M2N_SAVED_R7); - emitter->ipf_mov (6, M2N_SAVED_R6); - emitter->ipf_mov (5, M2N_SAVED_R5); - emitter->ipf_mov (4, M2N_SAVED_R4); - emitter->ipf_mtpr( M2N_SAVED_PR); - if (do_alloc) { - emitter->ipf_mov (GP_REG, M2N_SAVED_GP); - emitter->ipf_mtbr(BRANCH_RETURN_LINK_REG, M2N_SAVED_RETURN_ADDRESS); - emitter->ipf_mtap(AR_pfs, M2N_SAVED_PFS); - } -} - -void m2n_gen_save_extra_preserved_registers(Merced_Code_Emitter* emitter) -{ - unsigned reg; - - // Save pointer to saves area - emitter->ipf_mov(M2N_EXTRA_SAVED_PTR, SP_REG); - - // Save callee saves floating point registers - for (reg = 2; reg < 6; reg++) - emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, reg, unsigned(-16)); - for (reg = 16; reg < 32; reg++) - emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, reg, unsigned(-16)); - - // Save callee saves branch registers - for (reg = 1; reg < 6; reg++) { - emitter->ipf_mfbr(SCRATCH_GENERAL_REG, reg); - emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8)); - } - - // Save ar.fpsr, ar.unat, and ar.lc - emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8)); - emitter->ipf_mfap(SCRATCH_GENERAL_REG, AR_unat); - emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8)); - emitter->ipf_mfap(SCRATCH_GENERAL_REG, AR_lc); - emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-24)); - - - // Note that the last postdec (postinc of -24) has created the required scratch area on the memory stack -} - -unsigned m2n_get_last_m2n_reg() { - return M2N_SAVED_PFS + M2N_NUMBER_LOCALS - 1; -} - -unsigned m2n_get_pfs_save_reg() { - return M2N_SAVED_PFS; -} - -unsigned m2n_get_return_save_reg() { - return M2N_SAVED_RETURN_ADDRESS; -} - -unsigned m2n_get_gp_save_reg() { - return M2N_SAVED_GP; -} - -uint64* m2n_get_arg_word(M2nFrame* m2nf, unsigned n) -{ - do_flushrs(); - if (n<8) - return get_stacked_register_address(m2n_get_bsp(m2nf), n+32); - else - return ((uint64*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_SAVED_SP))+(n-8+2); // +2 is for 16-bytes scratch on mem stack -} - -void m2n_push_suspended_frame(M2nFrame* m2nf, Registers* regs) -{ - abort(); // FIXME: check that it works - m2n_push_suspended_frame(p_TLS_vmthread, m2nf, regs); -} - -void m2n_push_suspended_frame(VM_thread* thread, M2nFrame* m2nf, Registers* regs) -{ - // FIXME: not implemented - assert(0); - abort(); -} - - -M2nFrame* m2n_push_suspended_frame(Registers* regs) -{ - abort(); // FIXME: check that it works - return m2n_push_suspended_frame(p_TLS_vmthread, regs); -} - -M2nFrame* m2n_push_suspended_frame(VM_thread* thread, Registers* regs) -{ - abort(); // FIXME: check that it works - M2nFrame* m2nf = (M2nFrame*)STD_MALLOC(sizeof(M2nFrame)); - assert(m2nf); - m2n_push_suspended_frame(thread, m2nf, regs); - return m2nf; -} - -bool m2n_is_suspended_frame(M2nFrame * m2nf) { - // FIXME: not implemented - assert(0); - abort(); - return false; - -} - diff --git a/vm/port/src/lil/ipf/pim/m2n_ipf_internal.h b/vm/port/src/lil/ipf/pim/m2n_ipf_internal.h deleted file mode 100644 index b9c9eef..0000000 --- a/vm/port/src/lil/ipf/pim/m2n_ipf_internal.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#ifndef _M2N_IPF_INTERNAL_H_ -#define _M2N_IPF_INTERNAL_H_ - -// This file describes the internal IPF interface of m2n frames. -// It can be used by stubs to generate code to push and pop m2n frames and to update object handles fields. -// It is also used by stack iterators. - -#include "Code_Emitter.h" -#include "m2n.h" -#include "open/types.h" - -// Generate code to push an M2nFrame onto the stack -// The activation frame of the stub is used for the M2nFrame as are certain registers in this frame. -// The stub must preserve all preserved registers including pfs, gp, and b0 from entry to the stub to the time of push_m2n. -// The stub may use up to 8 inputs, the requested number of locals, and the requested number of outputs after push_m2n. -// method: the method to be associated with the M2nFrame or NULL for no association -// handles: does the stub want local handles or not -// num_on_stack: this number plus the current sp is the sp at entry to the stub (should be positive as stack grows down) -// num_local: the number of local registers above the M2N registers required by the stub -// num_outputs: the number of output registers required by the stub -// do_alloc: if false, the function should assume that the stacked register -// frame has been allocated, and no alloc instruction needs to be executed; it should also assume that ar.pfs is already saved at its proper place -// return: the register number for the first local, the outputs immediately follow the locals -// Note, the stub may use some of the 8 inputs as locals if it wants to -unsigned m2n_gen_push_m2n(Merced_Code_Emitter*, Method_Handle method, frame_type current_frame_type, bool handles, unsigned num_on_stack, unsigned num_locals, - unsigned num_outputs, bool do_alloc=true); - -enum M2nPreserveRet { MPR_None, MPR_Gr, MPR_Fr }; - -// Generate code to pop the M2nFrame from the stack. -// This should be matched by a preceeding push_m2n in the stub. -// handles: should match the push_m2n handles argument, if true the generated code will free the handles. -// preserve_ret_regs: the number of return registers to preserve (starting with r8). -// Note that the pop restores the callee saves gp registers, pfs, gp, and b0 to the values that had at the push m2n; it does not restore sp. -// do_alloc: must have the same value as the corresponding m2n_gen_push_m2n() parameter -// target: if handles==true and the vm property vm.free_local_object_handles -// is also true, m2n_gen_pop_m2n will need to set a target in the emitter; -// target will be the number used. Otherwise, this parameter is ignored. -// out_reg: if handles==true and the vm property vm.free_local_object_handles -// is also true, m2n_gen_pop_m2n needs to know the first output register; out_reg is this register. Otherwise this parameter is ignored -void m2n_gen_pop_m2n(Merced_Code_Emitter*, bool handles, M2nPreserveRet preserve_ret_regs, bool do_alloc=true, unsigned out_reg=0, int target=-1); - -// Generate code to set the local handles of the M2nFrame that is also the current frame. -// Preserves all registers that are not used to store M2nFrame information. -void m2n_gen_set_local_handles(Merced_Code_Emitter*, unsigned src_reg); - -// Generate code to set the local handles of the M2nFrame that is also the current frame. -// Preserves all registers that are not used to store M2nFrame information. -void m2n_gen_set_local_handles_imm(Merced_Code_Emitter*, uint64 imm_val); - -// Generate code to save additional preserved registers not normally saved by push_m2n. -// The combination of push_m2n and save_extra_preserved_registers will save all preserved registers as needed by exception propogation. -// The code generated by this function must follow that of push_m2n. -// Note that this function uses the memory stack, expects the scratch area above sp, and leaves a scratch area above sp. -void m2n_gen_save_extra_preserved_registers(Merced_Code_Emitter* emitter); - - -// returns the number of the last GR that the M2N frame uses -unsigned m2n_get_last_m2n_reg(); - -// the following functions return the GR numbers where various things should be saved -unsigned m2n_get_pfs_save_reg(); -unsigned m2n_get_return_save_reg(); -unsigned m2n_get_gp_save_reg(); - -// The IPF calling convention defines how to layout the arguments into words and then how to place -// these into gp registers, fp registers, or memory stack. This function returns a pointer to the -// nth word assuming it is either in a gp register or on the memory stack. -uint64* m2n_get_arg_word(M2nFrame*, unsigned n); - -////////////////////////////////////////////////////////////////////////// -// Implementation details - -// An M2nFrame is always represented using the bsp value for register 32 of the frame -// The information needed for the frame is stored in stacked local registers or on the memory stack. -// It can be accessed by computing the spill location from the bsp value or by retrieving the sp value and - -uint64* m2n_get_bsp(M2nFrame*); -uint64* m2n_get_extra_saved(M2nFrame*); - -// Flushes register stack of the current thread into backing store and calls target procedure. -NativeCodePtr m2n_gen_flush_and_call(); - -// An M2nFrame will always have 8 input registers, some local stacked registers to save stuff, and some outputs - -#define M2N_NUMBER_ALIGNS 2 -#define M2N_NUMBER_INPUTS 8 -#define M2N_NUMBER_LOCALS 17 - -// The following registers are used in M2nFrames to hold the indicated values -// The register numbers must be distinct, at least 40 (so they don't conflict with inputs), and less than 40+M2N_NUMBER_LOCALS - -#define M2N_SAVED_PFS 40 -#define M2N_SAVED_RETURN_ADDRESS 41 -#define M2N_SAVED_M2NFL 42 -#define M2N_SAVED_SP 43 -#define M2N_SAVED_GP 44 -#define M2N_SAVED_PR 45 -#define M2N_SAVED_UNAT 46 -#define M2N_SAVED_R4 47 -#define M2N_SAVED_R5 48 -#define M2N_SAVED_R6 49 -#define M2N_SAVED_R7 50 -#define M2N_EXTRA_SAVED_PTR 51 -#define M2N_OBJECT_HANDLES 52 -#define M2N_METHOD 53 -#define M2N_FRAME_TYPE 54 -#define M2N_EXTRA_RNAT 55 -// this must be last register -#define M2N_EXTRA_UNAT 56 - -// Only the callee saves general registers are normally saved in the M2nFrame along with special things like pfs, return address, etc. -// The full set of preserved registers includes callee saves floating point and branch registers as well. -// These are saved, if requested, onto the memory stack as follows: -// +-------------------------+ -// | Saved f2 | -// Extra Saved ---> +-------------------------+ -// | Saved f3..f5 | -// +-------------------------+ -// | Saved f16..f31 | -// +-------------------------+ -// | Scratch area (8 bytes) | -// +-------------------------+ -// | Saved b1..b5 | -// +-------------------------+ -// | Saved ar.fpsr | -// +-------------------------+ -// | Saved ar.unat | -// +-------------------------+ -// | Saved ar.lc | -// +-------------------------+ - -#define M2N_EXTRA_SAVES_SPACE 400 - -#ifdef _EM64T_ -#error Should not be included! -#endif - -struct M2nFrame { - union { - uint64 buff[M2N_NUMBER_INPUTS + M2N_NUMBER_LOCALS + M2N_NUMBER_ALIGNS]; - struct { - M2nFrame* prev_m2nf; - ObjectHandles* local_object_handles; - Method_Handle method; - frame_type current_frame_type; // type of the current frame also shows is the frame unwindable - }; - }; -}; - -#endif //!_M2N_IPF_INTERNAL_H_ diff --git a/vm/port/src/lil/ipf/pim/stack_iterator_ipf.cpp b/vm/port/src/lil/ipf/pim/stack_iterator_ipf.cpp deleted file mode 100644 index 4135c16..0000000 --- a/vm/port/src/lil/ipf/pim/stack_iterator_ipf.cpp +++ /dev/null @@ -1,624 +0,0 @@ -/* - * 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. - */ - -/** - * @author Intel, Pavel Afremov - * @version $Revision$ - */ - -#define LOG_DOMAIN "port.old" -#include "cxxlog.h" - -#include "environment.h" -#include -#include -#include -#include - -using namespace std; - -#include "open/types.h" -#include "jit_intf_cpp.h" -#include "m2n.h" -#include "m2n_ipf_internal.h" -#include "method_lookup.h" -#include "nogc.h" -#include "vm_ipf.h" -#include "vm_threads.h" -#include "stack_iterator.h" -#include "stub_code_utils.h" -#include "root_set_enum_internal.h" -#include "cci.h" - -#include "dump.h" -#include "vm_stats.h" - -// Invariants: -// Note that callee saves and stacked registers below means both the pointers and the corresponding nat bits in nats_lo and nats_hi -// All frames: -// last_legal_rsnat should point to a valid spilled nat set on the rse stack (for the stack of this frame context) -// extra_nats should be for the nats bits that are or would be spilled next after last_legal_rsnat -// the valid bits of extra_nats plus all spilled nat sets at or below last_legal_rsnat must cover all of the relevant parts of the rse stack -// the bottom 32 bits of nats_lo should reflect the nat status of those registers in the current frame -// Native frames: -// m2nfl should point to the m2n frame list for the native frame -// cci must be zero -// Managed frames: -// m2nfl should point to the m2n frame immediately preceeding the current one or NULL if there is no preceeding m2n frame -// cci must point to the code chunk info for the ip at which the frame is suspended, and must be nonnull -// bsp should point to the bsp spill location for gr32 of the current frame -// the callee saves registers and stacked registers of the frame should point to their values at the point of suspension of the frame -// c.p_ar_pfs should point to the saved pfs for the current frame (ie the cfm for the current frame formatted as for ar.pfs) -// c.p_eip and c.sp should point-to/have their values at the time the frame was suspended - -struct StackIterator { - CodeChunkInfo* cci; - JitFrameContext c; - M2nFrame* m2nfl; - uint64 ip; - uint64* bsp; - uint64 extra_rnats; - uint64 extra_unats; -}; - -////////////////////////////////////////////////////////////////////////// -// Utilities - -// At some point the rse engine was flushed upto bsp and rnat is the rnat register immediately after -// this flush. We assume that all nat bits we are interested in do not change from the time of flush -// to after we are finished with the iterator that calls this function, even if the rse engine has -// returned to a point prior to bsp. -/* -static void si_init_nats(StackIterator* si, uint64* bsp, uint64 rnat) -{ - si->last_legal_rsnat = (uint64*)((uint64)bsp & ~0x1f8); - si->extra_nats = rnat; -} -*/ - -// This function flushes the rse and puts the value of bsp/bspstore into res[1] and rnat into res[0] -/* -extern "C" void get_rnat_and_bsp(uint64* res); -extern "C" void get_rnat_and_bspstore(uint64* res); -*/ - -static uint64 get_rnat(uint64 * bsp) { - uint64 * last_m2n = (uint64 *)m2n_get_last_frame(); - uint64 * rnat_ptr = (uint64*)((uint64)bsp | (uint64)0x1f8); - uint64 * extra_nat_ptr; - - if (rnat_ptr <= last_m2n) { - return *rnat_ptr; - } - - // All nat bits for last M2N are stored at M2N_EXTRA_UNAT. - // All nat bits for parent frames are stored at M2N_EXTRA_RNAT. - - if (bsp >= last_m2n) { - extra_nat_ptr = last_m2n + (M2N_EXTRA_UNAT - 32); - } else { - extra_nat_ptr = last_m2n + (M2N_EXTRA_RNAT - 32); - } - - if (rnat_ptr <= extra_nat_ptr) { - // There is rnat collection inside M2N. Need to adjust... - extra_nat_ptr += 1; - } - - return *extra_nat_ptr; -} - -// Setup the stacked register for the current frame given bsp and ar.pfs (cfm for current frame) -static void si_setup_stacked_registers(StackIterator* si) -{ - const uint64 ALL_ONES = ~0; - uint64 pfs = *si->c.p_ar_pfs; - unsigned sof = (unsigned)EXTRACT64_SOF(pfs); - - uint64 nats_lo = si->c.nats_lo & 0xffffffff; - uint64 nats_hi = 0; - uint64* bsp = si->bsp; - uint64 nats = get_rnat(bsp); - - unsigned index = (unsigned)(((uint64)bsp & (uint64)0x1f8) >> 3); - uint64 mask = ((uint64)1) << index; - - for(unsigned i=0; ic.p_gr[32+i] = bsp; - // Set the nat bit of the register - if (nats & mask) - if (i<32) - nats_lo |= (1<<(i+32)); - else - nats_hi |= (1<<(i-32)); - // Advance bsp and mask - bsp++; - mask <<= 1; - // If bsp is on a spilled rsnat recompute nats and mask - if (((uint64)bsp&(uint64)0x1f8) == (uint64)0x1f8) { - bsp++; - mask = 1; - nats = get_rnat(bsp); - } - } - - si->c.nats_lo = nats_lo; - si->c.nats_hi = nats_hi; -} - -// Set p_eip, sp, callee saves registers, and ar_pfs for the frame prior to the current m2nfl -static void si_unwind_from_m2n(StackIterator* si) -{ -#ifdef VM_STATS - VM_Statistics::get_vm_stats().num_unwind_native_frames_all++; -#endif - // First setup the stack registers for the m2n frame - si->bsp = m2n_get_bsp(si->m2nfl); - uint64 pfs = M2N_NUMBER_LOCALS+8; - si->c.p_ar_pfs = &pfs; - si_setup_stacked_registers(si); - - // Now extract various saved values from the m2n frame - - // Callee saved general registers - si->c.p_gr[4] = si->c.p_gr[M2N_SAVED_R4]; - si->c.p_gr[5] = si->c.p_gr[M2N_SAVED_R5]; - si->c.p_gr[6] = si->c.p_gr[M2N_SAVED_R6]; - si->c.p_gr[7] = si->c.p_gr[M2N_SAVED_R7]; - assert(M2N_SAVED_R7 < 64); - si->c.nats_lo = si->c.nats_lo & ~(uint64)0xf0 | (si->c.nats_lo >> (M2N_SAVED_R4-4)) & (uint64)0xf0; - - // IP, SP, PFS, preds, unat, m2nfl - si->c.p_eip = si->c.p_gr[M2N_SAVED_RETURN_ADDRESS]; - si->c.sp = *si->c.p_gr[M2N_SAVED_SP]; - si->c.p_ar_pfs = si->c.p_gr[M2N_SAVED_PFS]; - si->c.preds = *si->c.p_gr[M2N_SAVED_PR]; - si->c.ar_unat = *si->c.p_gr[M2N_SAVED_UNAT]; - si->m2nfl = m2n_get_previous_frame(si->m2nfl); -} - -// Adjust the bsp based on the old frames bsp and ar.pfs (which is the cfm for the new frame) -static void si_unwind_bsp(StackIterator* si) -{ - uint64 pfs = *si->c.p_ar_pfs; - unsigned sol = (unsigned)EXTRACT64_SOL(pfs); - - assert(sol<=96); - - uint64 bsp = (uint64)si->bsp; - uint64 local_area_size = sol << 3; - - // Direct computation, see IPF arch manual, volume 3, table 6.2. - uint64 d2 = bsp - local_area_size; - uint64 d3 = 62*8 - (bsp & 0x1f8) + local_area_size; - if (d3 >= 63*8) - if (d3 >= 126*8) - d2 -= 16; - else - d2 -= 8; - si->bsp = (uint64*)d2; -} - -typedef void (__cdecl *transfer_control_stub_type)(StackIterator*, uint64*); -struct Fp { - NativeCodePtr addr; - void* gp; -}; - -// The transfer control stub takes two arguments: -// i0: pointer to the stack iterator with context to transfer to -// i1: value of the unat register for restoring nonstacked registers with nat bits -// The stub does not return -// The stack iterator should be one for the current thread -static transfer_control_stub_type gen_transfer_control_stub() -{ - static Fp fp = {NULL, NULL}; - if (fp.addr) { - return (transfer_control_stub_type)&fp; - } - - tl::MemoryPool mem_pool; - Merced_Code_Emitter emitter(mem_pool, 2, 0); - emitter.disallow_instruction_exchange(); - emitter.memory_type_is_unknown(); - unsigned i; - - // This register will hold the pointer to the stack iterator - const int stack_iterator_ptr = SCRATCH_GENERAL_REG2; - // This register points to the unat values to use for restoring global general registers - const int unat_ptr = SCRATCH_GENERAL_REG3; - // This register will hold the new SP until after the stack is finished with - const int tmp_sp = SCRATCH_GENERAL_REG4; - // These registers are used to hold temporary values - const int tmp1 = SCRATCH_GENERAL_REG5; - const int tmp2 = SCRATCH_GENERAL_REG6; - const int tmp3 = SCRATCH_GENERAL_REG7; - - emitter.ipf_mov(stack_iterator_ptr, IN_REG0); - emitter.ipf_mov(unat_ptr, IN_REG1); - - // Cover and flush the register stack. - // This is needed to properly set bsp using bspstore below. - - emitter.ipf_cover(); - emitter.ipf_flushrs(); - - // Restore ar.pfs, ip to return branch register, sp to a temp register, and the predicates - uint64 ar_pfs_offset = (uint64)&((StackIterator*)0)->c.p_ar_pfs; - emitter.ipf_adds(tmp1, (int)ar_pfs_offset, stack_iterator_ptr); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); - emitter.ipf_mtap(AR_pfs, tmp1); - uint64 ip_offset = (uint64)&((StackIterator*)0)->c.p_eip; - emitter.ipf_adds(tmp1, (int)ip_offset, stack_iterator_ptr); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); - emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, tmp1); - uint64 sp_offset = (uint64)&((StackIterator*)0)->c.sp; - emitter.ipf_adds(tmp1, (int)sp_offset, stack_iterator_ptr); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp_sp, tmp1); - uint64 preds_offset = (uint64)&((StackIterator*)0)->c.preds; - emitter.ipf_adds(tmp1, (int)preds_offset, stack_iterator_ptr); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); - emitter.ipf_mtpr(tmp1); - - // Restore callee-saves general registers and the first return register - uint64 g4_offset = (uint64)&((StackIterator*)0)->c.p_gr[4]; - emitter.ipf_adds(tmp1, (int)g4_offset, stack_iterator_ptr); - for(i=4; i<=8; i++) { - emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp3, unat_ptr, 8); - emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp1, 8); - emitter.ipf_mtap(AR_unat, tmp3); - emitter.ipf_cmp(icmp_eq, cmp_none, SCRATCH_PRED_REG, SCRATCH_PRED_REG2, tmp2, 0); - emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, i, tmp2, SCRATCH_PRED_REG2); - } - - // Restore callee-saves floating-point registers - uint64 f2_offset = (uint64)&((StackIterator*)0)->c.p_fp[2]; - emitter.ipf_adds(tmp1, (int)f2_offset, stack_iterator_ptr); - for(i=2; i<=5; i++) { - emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp1, (i==5 ? 88 : 8)); - emitter.ipf_ldf(float_mem_size_e, mem_ld_fill, mem_none, i, tmp2); - } - for(i=16; i<=31; i++) { - emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp1, 8); - emitter.ipf_ldf(float_mem_size_e, mem_ld_fill, mem_none, i, tmp2); - } - - // Restore callee-saves branch registers - uint64 b1_offset = (uint64)&((StackIterator*)0)->c.p_br[1]; - emitter.ipf_adds(tmp1, (int)b1_offset, stack_iterator_ptr); - for(i=1; i<=5; i++) { - emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp1, 8); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp2); - emitter.ipf_mtbr(i, tmp2); - } - - // Restore ar.fpsr, ar.unat, and ar.lc - uint64 fpsr_offset = (uint64)&((StackIterator*)0)->c.ar_fpsr; - emitter.ipf_adds(tmp1, (int)fpsr_offset, stack_iterator_ptr); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); - uint64 unat_offset = (uint64)&((StackIterator*)0)->c.ar_unat; - emitter.ipf_adds(tmp1, (int)unat_offset, stack_iterator_ptr); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); - emitter.ipf_mtap(AR_unat, tmp1); - uint64 lc_offset = (uint64)&((StackIterator*)0)->c.ar_lc; - emitter.ipf_adds(tmp1, (int)lc_offset, stack_iterator_ptr); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); - emitter.ipf_mtap(AR_lc, tmp1); - - // Restore rse stack and the memory stack - // For the rse stack we must go to rse enforced lazy mode - emitter.ipf_mfap(tmp1, AR_rsc); - emitter.ipf_andi(tmp2, -4, tmp1); - emitter.ipf_mtap(AR_rsc, tmp2); - uint64 bsp_offset = (uint64)&((StackIterator*)0)->bsp; - emitter.ipf_adds(tmp2, (int)bsp_offset, stack_iterator_ptr); - emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp2); - emitter.ipf_mtap(AR_rnat, 0); - emitter.ipf_mtap(AR_bspstore, tmp2); - emitter.ipf_mtap(AR_rsc, tmp1); - emitter.ipf_mov(SP_REG, tmp_sp); - - enforce_calling_conventions(&emitter); - // Return to the code - emitter.ipf_brret(br_many, br_sptk, br_none, BRANCH_RETURN_LINK_REG); - - emitter.flush_buffer(); - size_t stub_size = emitter.get_size(); - fp.addr = (NativeCodePtr)malloc_fixed_code_for_jit(stub_size, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_DEFAULT, CAA_Allocate); - emitter.copy((char*)fp.addr); - flush_hw_cache((Byte*)fp.addr, stub_size); - sync_i_cache(); - - DUMP_STUB(fp.addr, "transfer_control_stub (non-LIL)", stub_size); - - fp.gp = get_vm_gp_value(); - - return (transfer_control_stub_type)&fp; -} - -////////////////////////////////////////////////////////////////////////// -// Stack Iterator Interface - -StackIterator* si_create_from_native() -{ - return si_create_from_native(p_TLS_vmthread); -} - -void si_fill_from_native(StackIterator* si) -{ - si_fill_from_native(si, p_TLS_vmthread); -} - -StackIterator* si_create_from_native(VM_thread* thread) -{ - // Allocate iterator - StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); - assert(res); - - // Setup current frame - si_fill_from_native(res, thread); - return res; -} - -void si_fill_from_native(StackIterator* si, VM_thread * thread) { - memset(si, 0, sizeof(StackIterator)); - - // Setup current frame - si->cci = NULL; - si->m2nfl = m2n_get_last_frame(thread); - si->ip = 0; - si->c.p_eip = &si->ip; -} - -/* -#if defined (PLATFORM_POSIX) -#elif defined (PLATFORM_NT) - -// Get the bspstore and rnat values of another thread from the OS. -// Hopefully this will also flush the RSE of the other stack enough for our purposes. -static void get_bsp_and_rnat_from_os(VM_thread * thread, uint64 ** bspstore, uint64 * rnat) { - CONTEXT ctx; - ctx.ContextFlags |= CONTEXT_INTEGER; - BOOL UNREF stat = GetThreadContext(thread->thread_handle, &ctx)); - assert(stat); - *bspstore = (uint64*)ctx.RsBSPSTORE; - *rnat = ctx->RsRNAT; -} - -StackIterator* si_create_from_native(VM_thread* thread) -{ - // Allocate iterator - StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); - assert(res); - - // Setup last_legal_rsnat and extra_nats - uint64* bsp; - uint64 rnat; - - get_bsp_and_rnat_from_os(thread, &bsp, &rnat); - si_init_nats(res, bsp, rnat); - - // Check that bsp covers everything - assert((uint64)m2n_get_last_frame(thread)+(M2N_NUMBER_LOCALS+9)*8 <= (uint64)bsp); - - // Setup current frame - res->cci = NULL; - res->m2nfl = m2n_get_last_frame(thread); - res->ip = NULL; - res->c.p_eip = &res->ip; - - return res; -} - -#else -#error Stack iterator is not implemented for the given platform -#endif -*/ - -// On IPF stack iterators must be created from threads (suspended) in native code. -// We do not support threads suspended in managed code yet. -StackIterator* si_create_from_registers(Registers*, bool is_ip_past, M2nFrame*) -{ - ABORT("Not implemented"); - return NULL; -} - -void si_fill_from_registers(StackIterator* si, Registers*, bool is_ip_past, M2nFrame*) -{ - ABORT("Not implemented"); -} - -size_t si_size(){ - return sizeof(StackIterator); -} - -void si_transfer_all_preserved_registers(StackIterator* si) -{ - unsigned i; - uint64* sp = m2n_get_extra_saved(si->m2nfl); - - // Floating point registers - for(i=2; i<=5; i++) { - si->c.p_fp[i] = sp; - sp -= 2; - } - for(i=16; i<=31; i++) { - si->c.p_fp[i] = sp; - sp -= 2; - } - - // Branch registers - for(i=1; i<=5; i++) { - si->c.p_br[i] = sp; - sp--; - } - - // ar.fpsr, ar.unat, ar.lc - si->c.ar_fpsr = *sp--; - si->c.ar_unat = *sp--; - si->c.ar_lc = *sp--; -} - -bool si_is_past_end(StackIterator* si) -{ - return si->cci==NULL && si->m2nfl==NULL; -} - -void si_goto_previous(StackIterator* si, bool over_popped) -{ - if (si->cci) { - assert(si->cci->get_jit() && si->cci->get_method()); - si->cci->get_jit()->unwind_stack_frame(si->cci->get_method(), si_get_jit_context(si)); - } else { - if (!si->m2nfl) return; - si_unwind_from_m2n(si); - } - Global_Env *env = VM_Global_State::loader_env; - si->c.is_ip_past = TRUE; - si->cci = env->vm_methods->find(si_get_ip(si)); - si_unwind_bsp(si); - si_setup_stacked_registers(si); -} - -StackIterator* si_dup(StackIterator* si) -{ - StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); - memcpy(res, si, sizeof(StackIterator)); - // If si uses itself for IP then res should also to avoid problems if si is deallocated first. - if (si->c.p_eip == &si->ip) - res->c.p_eip = &res->ip; - return res; -} - -void si_free(StackIterator* si) -{ - STD_FREE(si); -} - -void* si_get_sp(StackIterator* si) { - return (void*)si->c.sp; -} - -NativeCodePtr si_get_ip(StackIterator* si) -{ - return (NativeCodePtr)*si->c.p_eip; -} - -void si_set_ip(StackIterator* si, NativeCodePtr ip, bool also_update_stack_itself) -{ - if (also_update_stack_itself) { - *(si->c.p_eip) = (uint64)ip; - } else { - si->ip = (uint64)ip; - si->c.p_eip = &si->ip; - } -} - -// 20040713 Experimental: set the code chunk in the stack iterator -void si_set_code_chunk_info(StackIterator* si, CodeChunkInfo* cci) -{ - ABORT("Not implemented"); -} - -CodeChunkInfo* si_get_code_chunk_info(StackIterator* si) -{ - return si->cci; -} - -JitFrameContext* si_get_jit_context(StackIterator* si) -{ - return &si->c; -} - -bool si_is_native(StackIterator* si) -{ - return si->cci==NULL; -} - -M2nFrame* si_get_m2n(StackIterator* si) -{ - return si->m2nfl; -} - -void** si_get_return_pointer(StackIterator* si) -{ - // FIXME: not implemented - assert(0); - abort(); - return 0; -} - -void si_set_return_pointer(StackIterator* si, void** return_value) -{ - si->c.p_gr[RETURN_VALUE_REG] = (uint64*)return_value; - si->c.nats_lo &= ~(1<c.p_eip == &si->ip) - local_si.c.p_eip = &local_si.ip; - //si_free(si); - - // 2. Set the M2nFrame list - m2n_set_last_frame(local_si.m2nfl); - - // 3. Move bsp to next frame - uint64 sol = EXTRACT64_SOL(*local_si.c.p_ar_pfs); - for(unsigned i=0; i> 3; - uint64 mask = 1 << index; - unat[reg] = mask; - } else { - unat[reg] = 0; - } - } - - // 5. Call the stub - transfer_control_stub_type tcs = gen_transfer_control_stub(); - tcs(&local_si, unat+4); -} - -void si_copy_to_registers(StackIterator* si, Registers*) -{ - ABORT("Not implemented"); -} - -void si_set_callback(StackIterator* si, NativeCodePtr* callback) { - ABORT("Not implemented"); -} - -extern "C" void do_loadrs_asm(int loadrs); - -void si_reload_registers() -{ - do_loadrs_asm(0); -} diff --git a/vm/port/src/lil/lil.cpp b/vm/port/src/lil/lil.cpp deleted file mode 100644 index f1a6416..0000000 --- a/vm/port/src/lil/lil.cpp +++ /dev/null @@ -1,2708 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.2.4.4 $ - */ - - -#include - -//MVM -#include - -using namespace std; - -#define LOG_DOMAIN "vm.helpers" -#include "cxxlog.h" - -#include -#include -#include - -#include "lil.h" -#include "open/types.h" -#include "open/vm.h" -#include "nogc.h" - -// Forward decs of local functions - -static LilInstruction* lil_find_label(LilCodeStub*, LilLabel); - -//////////////////////////////////////////////////////////////////////////////////////// -// The LIL internal representation - -struct LilSig { - LilCc cc; - bool arbitrary; // If true then num_arg_types must be 0 - unsigned num_arg_types; // Must be 0 if arbitrary is true - LilType* arg_types; - enum LilType ret_type; -}; - -struct LilCond { - enum LilPredicate tag; - LilOperand o1, o2; -}; - -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 -}; - -struct LilInstruction { - enum LilInstructionTag tag; - union { - struct { - LilLabel l; - LilInstructionContext* ctxt; // Computed lazily - } label; - unsigned locals; - unsigned std_places; - struct { - LilVariable dst; - unsigned num_bytes; - } alloc; - struct { - enum LilOperation op; - LilVariable dst; - LilOperand o1, o2; - } asgn; - LilVariable ts; - LilOperand handles; - // Both ld, st, inc, & case use ldst - // operand is dst for ld, src for st, unused for inc, src for case - // cmp and l are used only for cas - // [base,scale,index,offset] is src for ld, dst for st - // base present iff is_base - // index present iff is_index - struct { - LilType t; - bool is_base, is_index; - LilVariable base, index; - LilOperand operand; - unsigned scale; - POINTER_SIZE_SINT offset; - LilAcqRel acqrel; - LilLdX extend; - LilOperand compare; - LilLabel l; - } ldst; - LilLabel j; - struct { - LilCond c; - LilLabel l; - } jc; - LilSig out; - struct { - LilCallKind k; - LilOperand target; - } call; - struct { - Method_Handle method; - frame_type current_frame_type; - bool handles; - } push_m2n; - struct { - char *str; - LilOperand arg; - } print; - } u; - struct LilInstruction* next; -}; - - -enum LilCodeStubContexts { LCSC_NotComputed, LCSC_Computed, LCSC_Error }; - -struct LilCodeStub { - tl::MemoryPool* my_memory; - unsigned cur_gen_label; - unsigned num_std_places; - LilSig sig; - LilInstruction* is; - LilCodeStubContexts ctxt_state; - unsigned num_is, max_std_places, max_locals; - LilInstructionContext* init_ctxt; // Computed lazily - - // size of generated code - size_t compiled_code_size; - - // a list of BBs in this code stub; null if the FG has not been initializsed; computed lazily - LilBb* bb_list_head; - unsigned num_bbs; -}; - - - -//*** Freers - -VMEXPORT void lil_free_code_stub(LilCodeStub* cs) -{ - delete cs->my_memory; -} - -//*** Freers - end - - -//////////////////////////////////////////////////////////////////////////////////// -// The LIL Parser - -// The LIL parser is loosely designed as a scannerless recursive-descent parser. -// The various functions take a pointer to a source string pointer and update -// the pointer as they parse. Most functions are designed to advance the pointer -// only by token amounts. Functions that parse something and return a pointer -// to the structure, return NULL on failure. Other functions that parse directly -// a structure, take a pointer to the structure to parse into, and return a boolean -// to indicate success; the latter also include parsing primitive types. -// Parsing functions other than for code stubs do not cleanup allocated memory as -// this is arena deallocated in parse_code_stub. - -// Here are some character classes used in various parts of the parser - -#define alpha(c) (((c)>='a' && (c)<='z') || ((c)>='A' && (c)<='Z') || (c)=='_') -#define num(c) ((c)>='0' && (c) <='9') -#define hexnum(c) (num(c) || ((c)>='a' && (c)<='f') || ((c)>='A' && (c)<='F')) -#define alphanum(c) (alpha(c) || num(c)) - -static void error(const char** src, char* err1, char* err2="") -{ - FILE* err_f = stderr; - fprintf(err_f, "lil parse error: %s%s\n\t", err1, err2); - unsigned count=0; - const char* s =*src; - while (s[0] && count++<70) { - fputc((s[0]<=' ' ? ' ' : s[0]), err_f); - s++; - } - fputc('\n', err_f); - fflush(err_f); -} - -// Skip over any white space -static void lil_skip_ws(const char** src) -{ - while ((*src)[0]==' ' || (*src)[0]=='\n' || (*src)[0]=='\r' || (*src)[0]=='\t' || (*src)[0]=='/') { - if ((*src)[0]=='/') - if ((*src)[1]=='/') - while ((*src)[0] && (*src)[0]!='\n') ++*src; - else - return; - else - ++*src; - } -} - -struct LilVarArgs { - unsigned current; - va_list val; -}; - -static char lil_parse_percent(const char** src, LilVarArgs* va) -{ - if ((*src)[0]!='%') { error(src, "expecting %", ""); return '\0'; } - unsigned i=1, v=0; - while (num((*src)[i])) - v = 10*v+((*src)[i++]-'0'); - if (v!=va->current) { error(src, "%s not numbered sequentially", ""); return '\0'; } - va->current++; - *src += i+1; - return (*src)[-1]; -} - -// Check and skip over a specific "keyword" -// This function does not change *src on failure -// (except to skip whitespace), -// and this is needed in some code below -static bool lil_parse_kw_no_error(const char** src, char* kw) -{ - lil_skip_ws(src); - unsigned c; - for (c=0; kw[c]; c++) - if ((*src)[c]!=kw[c]) return false; - // If the keyword could be an identifier or number then check that the source is - // not a longer identifier or number - if (alphanum(kw[c-1]) && alphanum((*src)[c])) return false; - *src += c; - return true; -} - -static bool lil_parse_kw(const char** src, char* kw) -{ - bool res = lil_parse_kw_no_error(src, kw); - if (!res) error(src, "expected ", kw); - return res; -} - -// Parse a calling convention -static bool lil_parse_cc(const char** src, LilCc* cc) -{ - lil_skip_ws(src); - switch ((*src)[0]) { - case 'j': // jni - if (!lil_parse_kw(src, "jni")) return false; - *cc = LCC_Jni; - return true; - case 'm': // managed - if (!lil_parse_kw(src, "managed")) return false; - *cc = LCC_Managed; - return true; - case 'p': // platform - if (!lil_parse_kw(src, "platform")) return false; - *cc = LCC_Platform; - return true; - case 'r': // rth - if (!lil_parse_kw(src, "rth")) return false; - *cc = LCC_Rth; - return true; - case 's': // stdcall - if (!lil_parse_kw(src, "stdcall")) return false; - *cc = LCC_StdCall; - return true; - default: - error(src, "bad calling convention", ""); - return false; - } -} - -// Parse a type -// If ret is true then void is allowed otherwise not -static bool lil_parse_type(const char** src, LilType* t, bool ret) -{ - lil_skip_ws(src); - switch ((*src)[0]) { - case 'f': // f4, f8 - if ((*src)[1]=='4') *t = LT_F4; - else if ((*src)[1]=='8') *t = LT_F8; - else { error(src, "expecting f4 or f8", ""); return false; } - if (alphanum((*src)[2])) { error(src, "expecting f4 or f8", ""); return false; } - *src += 2; - return true; - case 'g': // g1, g2, g4, g8 - if ((*src)[1]=='1') *t = LT_G1; - else if ((*src)[1]=='2') *t = LT_G2; - else if ((*src)[1]=='4') *t = LT_G4; - else if ((*src)[1]=='8') *t = LT_G8; - else { error(src, "expecting g1, g2, g4, or g8", ""); return false; } - if (alphanum((*src)[2])) { error(src, "expecting g1, g2, g4, or g8", ""); return false; } - *src += 2; - return true; - case 'r': // refmethod_get_name(meth) - if (!lil_parse_kw(src, "ref")) return false; - *t = LT_Ref; - return true; - case 'p': // pint - if (!lil_parse_kw(src, "pint")) return false; - *t = LT_PInt; - return true; - case 'v': // void - if (!ret) { error(src, "cannot have void type", ""); return false; } - if (!lil_parse_kw(src, "void")) return false; - *t = LT_Void; - return true; - default: - error(src, "bad type", ""); - return false; - } -} - -static LilType type_info_to_lil_type(Type_Info_Handle tih, bool handles) -{ - extern Boolean type_info_is_managed_pointer(Type_Info_Handle tih); - if (type_info_is_managed_pointer(tih)) { - return LT_PInt; - } - VM_Data_Type dt = type_info_get_type(tih); - switch (dt) { - case VM_DATA_TYPE_INT8: - case VM_DATA_TYPE_UINT8: return LT_G1; - case VM_DATA_TYPE_INT16: - case VM_DATA_TYPE_UINT16: return LT_G2; - case VM_DATA_TYPE_INT32: - case VM_DATA_TYPE_UINT32: return LT_G4; - case VM_DATA_TYPE_INT64: - case VM_DATA_TYPE_UINT64: return LT_G8; - case VM_DATA_TYPE_INTPTR: - case VM_DATA_TYPE_UINTPTR: return LT_PInt; - case VM_DATA_TYPE_F8: return LT_F8; - case VM_DATA_TYPE_F4: return LT_F4; - case VM_DATA_TYPE_BOOLEAN: return LT_G1; - case VM_DATA_TYPE_CHAR: return LT_G2; - case VM_DATA_TYPE_CLASS: - case VM_DATA_TYPE_ARRAY: return (handles ? LT_PInt : LT_Ref); - case VM_DATA_TYPE_VOID: return LT_Void; - case VM_DATA_TYPE_VALUE:{ - // ? 20030613: I really don't want this code here, but for now... - Class_Handle UNUSED ch = type_info_get_class(tih); - assert(ch); - assert(class_is_valuetype(ch)); - ASSERT(0, "Unexpected data type"); - } - case VM_DATA_TYPE_MP: - case VM_DATA_TYPE_UP: - return LT_PInt; - case VM_DATA_TYPE_STRING: - case VM_DATA_TYPE_INVALID: - default: ASSERT(0, "Unknown data type"); for(;;); - } -} - -// Parse a signature (cc:Ts:RT) -// Currently this function can only handle a maximum number of -// argument types -#define MAX_ARG_TYPES 20 -static bool lil_parse_sig(const char** src, tl::MemoryPool* mem, LilVarArgs* va, LilSig* s) -{ - if (!lil_parse_cc(src, &(s->cc))) return false; - if (!lil_parse_kw(src, ":")) return false; - lil_skip_ws(src); - if ((*src)[0]=='%') { - char specifier = lil_parse_percent(src, va); - bool jni; - switch (specifier) { - case 'm': - jni = false; - break; - case 'j': - jni = true; - break; - default: - if (specifier) error(src, "bad specifier for signature", ""); - return false; - } - Method_Handle m = va_arg(va->val, Method_Handle); - Method_Signature_Handle sig = method_get_signature(m); - unsigned num_method_args = method_args_get_number(sig); - unsigned num_args = num_method_args; - if (jni) { - num_args++; - if (method_is_static(m)) num_args++; - } - s->arbitrary = false; - s->num_arg_types = num_args; - s->arg_types = (LilType*)mem->alloc(num_args*sizeof(LilType)); - unsigned cur=0; - if (jni) { - s->arg_types[cur++] = LT_PInt; - if (method_is_static(m)) s->arg_types[cur++] = LT_PInt; - } - for(unsigned i=0; iarg_types[cur++] = type_info_to_lil_type(method_args_get_type_info(sig, i), jni); - s->ret_type = type_info_to_lil_type(method_ret_type_get_type_info(sig), jni); - } else if ((*src)[0]=='a') { - if (!lil_parse_kw(src, "arbitrary")) return false; - s->arbitrary = true; - s->num_arg_types = 0; // This is important to the validity checks - s->arg_types = NULL; - s->ret_type = LT_Void; - } else { - s->arbitrary = false; - if ((*src)[0]!=':') { - LilType ts[MAX_ARG_TYPES]; - unsigned cur = 0; - for(;;) { - assert(curnum_arg_types = cur; - s->arg_types = (LilType*)mem->alloc(cur*sizeof(LilType)); - for(unsigned i=0; iarg_types[i] = ts[i]; - } else { - s->num_arg_types = 0; - s->arg_types = NULL; - } - if (!lil_parse_kw(src, ":")) return false; - if (!lil_parse_type(src, &(s->ret_type), true)) return false; - } - return true; -} - -// Parse an immediate string -static bool lil_parse_string(const char** src, char** str) -{ - static char buffer[1000]; - lil_skip_ws(src); - if ((*src)[0] != '\'') - return false; - (*src)++; // skip opening quote - int len; - for (len=0; **src != '\'' && **src != '\0'; ++*src, ++len) { - if (len >= 1000) { - error(src, "string too long (more than 1000 chars)"); - return false; - } - buffer[len] = **src; - } - if (**src == '\0') { - error(src, "open string"); - return false; - } - // skip closing quote - ++*src; - buffer[len] = '\0'; - *str = (char *) malloc_fixed_code_for_jit(strlen(buffer) + 1, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_DEFAULT, CAA_Allocate); - strcpy(*str, buffer); - return true; -} - - -// Parse an immediate -static bool lil_parse_number(const char** src, LilVarArgs* va, POINTER_SIZE_INT* num) -{ - lil_skip_ws(src); - if ((*src)[0]=='%') { - char specifier = lil_parse_percent(src, va); - if (specifier=='i') { - *num = va_arg(va->val, POINTER_SIZE_INT); - return true; - } - else { - if (specifier) error(src, "bad specifier for immediate", ""); - return false; - } - } else if ((*src)[0]=='0' && (*src)[1]=='x') { - *num=0; - unsigned c=2; - while (hexnum((*src)[c])) { - // NB the following statement is dependent upon ASCII ordering - *num = *num * 16 + ((*src)[c]>='a' ? (*src)[c]-'a'+10 : (*src)[c]>='A' ? (*src)[c]-'A'+10 : (*src)[c]-'0'); - c++; - } - if (c==2) { error(src, "0x must be followed by at least one hexidigit", ""); return false; } - *src += c; - return true; - } else { - *num=0; - unsigned c=0; - while (num((*src)[c])) { - *num = *num * 10 + (*src)[c]-'0'; - c++; - } - if (c==0) { error(src, "expecting number", ""); return false; } - *src += c; - return true; - } -} - -// Parse a label -static LilLabel lil_parse_label(const char** src, unsigned* cur_gen_label, tl::MemoryPool* mem) -{ - lil_skip_ws(src); - if ((*src)[0]=='%') { - unsigned index; - switch ((*src)[1]) { - case 'g': - index = ++*cur_gen_label; - break; - case 'p': - index = *cur_gen_label; - break; - case 'n': - index = *cur_gen_label + 1; - break; - case 'o': - index = *cur_gen_label + 2; - break; - default: - error(src, "bad % specifier in label", ""); - return NULL; - } - char* buf = (char*)mem->alloc(17); - sprintf(buf, "$%x", index); - *src += 2; - return buf; - } - if (!alpha((*src)[0])) { error(src, "null label", ""); return NULL; } - unsigned c=1; - while (alphanum((*src)[c])) c++; - char* buf = (char*)mem->alloc(c+1); - for(unsigned i=0; itag = LVK_In; start = 1; break; - case 'l': v->tag = LVK_Local; start = 1; break; - case 'o': v->tag = LVK_Out; start = 1; break; - case 's': - if ((*src)[1]!='p') { error(src, "expecting variable", ""); return false; } - v->tag = LVK_StdPlace; - start = 2; - break; - case 'r': - if (alphanum((*src)[1])) { error(src, "expecting variable", ""); return false; } - v->tag = LVK_Ret; - v->index = 0; - ++*src; - return true; - default: - error(src, "expecting variable", ""); - return false; - } - if ((*src)[start]=='%') { - *src += start; - char specifier = lil_parse_percent(src, va); - if (specifier=='i') { - v->index = va_arg(va->val, int); - return true; - } else { - if (specifier) error(src, "bad specifier for variable index", ""); - return false; - } - } else { - unsigned c=0; - v->index = 0; - while (num((*src)[start+c])) { - v->index = v->index*10 + (*src)[start+c]-'0'; - c++; - } - if (c==0 || alpha((*src)[start+c])) { error(src, "bad variable", ""); return false; } - *src += start+c; - return true; - } -} - - -// Parse an operand -static bool lil_parse_operand(const char** src, LilVarArgs* va, LilOperand* o) -{ - lil_skip_ws(src); - if (((*src)[0]>='0' && (*src)[0]<='9') || (*src)[0]=='%') { - o->is_immed = true; - if (!lil_parse_number(src, va, &(o->val.imm))) return false; - } else { - o->is_immed = false; - if (!lil_parse_variable(src, va, &(o->val.var))) return false; - } - lil_skip_ws(src); - if ((*src)[0]==':') { - o->has_cast = true; - ++*src; - if (!lil_parse_type(src, &o->t, false)) return false; - } else { - o->has_cast = false; - } - return true; -} - -// Parse a condition -static bool lil_parse_cond(const char** src, LilVarArgs* va, LilCond* c) -{ - if (!lil_parse_operand(src, va, &(c->o1))) return false; - lil_skip_ws(src); - if ((*src)[0]=='=') - c->tag = LP_Eq, ++*src; - else if ((*src)[0]=='!' && (*src)[1]=='=') - c->tag = LP_Ne, *src += 2; - else if ((*src)[0]=='<') - if ((*src)[1]=='=') - if ((*src)[2]=='u') - if (alphanum((*src)[3])) - { error(src, "expecting predicate", ""); return false; } - else - c->tag = LP_Ule, *src += 3; - else - c->tag = LP_Le, *src += 2; - else if ((*src)[1]=='u') - if (alphanum((*src)[2])) - { error(src, "expecting predicate", ""); return false; } - else - c->tag = LP_Ult, *src += 2; - else - c->tag = LP_Lt, ++*src; - else - { error(src, "expecting predicate", ""); return false; } - if (!lil_parse_operand(src, va, &(c->o2))) return false; - // Fixup some special cases - if (!c->o1.is_immed && c->o2.is_immed && c->o2.val.imm==0) - if (c->tag==LP_Eq) - c->tag = LP_IsZero; - else if (c->tag==LP_Ne) - c->tag = LP_IsNonzero; - return true; -} - -static bool lil_parse_plusminus(const char** src, int* sign) -{ - lil_skip_ws(src); - if ((*src)[0]=='+') { - *sign = +1; - ++*src; - return true; - } else if ((*src)[0]=='-') { - *sign = -1; - ++*src; - return true; - } else { - error(src, "expecting + or -", ""); - return false; - } -} - -// Parse an address (part of load or store) -static bool lil_parse_address(const char** src, LilVarArgs* va, LilInstruction* i) -{ - int sign = +1; - if (!lil_parse_kw(src, "[")) return false; - lil_skip_ws(src); - if (alpha((*src)[0])) { - if (!lil_parse_variable(src, va, &(i->u.ldst.base))) return false; - if (!lil_parse_plusminus(src, &sign)) return false; - i->u.ldst.is_base = true; - } else { - i->u.ldst.is_base = false; - } - // Parse a number, this could be the scale or the immediate, - // which will be determined by what follows - POINTER_SIZE_INT n; - if (!lil_parse_number(src, va, &n)) return false; - lil_skip_ws(src); - if (lil_parse_kw_no_error(src, "*")) { - if (sign<0) { error(src, "cannot subtract scaled index", ""); return false; } - i->u.ldst.is_index = true; - i->u.ldst.scale = (unsigned) n; - if (!lil_parse_variable(src, va, &(i->u.ldst.index))) return false; - if (!lil_parse_plusminus(src, &sign)) return false; - if (!lil_parse_number(src, va, &n)) return false; - } else { - i->u.ldst.is_index = false; - i->u.ldst.scale = 0; - } - i->u.ldst.offset = sign * (POINTER_SIZE_SINT) n; - if (!lil_parse_kw(src, ":")) return false; - if (!lil_parse_type(src, &i->u.ldst.t, false)) return false; - if (lil_parse_kw_no_error(src, ",")) { - if (lil_parse_kw_no_error(src, "acquire")) { - i->u.ldst.acqrel = LAR_Acquire; - } else if (lil_parse_kw_no_error(src, "release")) { - i->u.ldst.acqrel = LAR_Release; - } else { - error(src, "expecting acquire or release", ""); - return false; - } - } else { - i->u.ldst.acqrel = LAR_None; - } - if (!lil_parse_kw(src, "]")) return false; - return true; -} - -static bool lil_parse_load_extend(const char** src, LilInstruction* i) -{ - if (lil_parse_kw_no_error(src, ",")) { - if (lil_parse_kw_no_error(src, "sx")) { - i->u.ldst.extend = LLX_Sign; - } else if (lil_parse_kw_no_error(src, "zx")) { - i->u.ldst.extend = LLX_Zero; - } else { - error(src, "expecting sx or zx", ""); - return false; - } - } else { - i->u.ldst.extend = LLX_None; - } - return true; -} - -// Parse an instruction -static LilInstruction* lil_parse_instruction(const char** src, tl::MemoryPool* mem, unsigned* cgl, LilVarArgs* va, LilSig* entry_sig) -{ - LilInstruction* i = (LilInstruction*)mem->alloc(sizeof(LilInstruction)); - lil_skip_ws(src); - // Look ahead at characters until the instruction form is determined - // Then parse that form - // Not the most maintainable code, but it keeps the design simple for now - switch ((*src)[0]) { - case ':': // :label - ++*src; - i->tag = LIT_Label; - i->u.label.l = lil_parse_label(src, cgl, mem); - if (!i->u.label.l) return NULL; - i->u.label.ctxt = NULL; - break; - case 'a': // alloc - { - i->tag = LIT_Alloc; - if (!lil_parse_kw(src, "alloc")) return NULL; - if (!lil_parse_variable(src, va, &(i->u.alloc.dst))) return NULL; - if (!lil_parse_kw(src, ",")) return NULL; - POINTER_SIZE_INT n; - if (!lil_parse_number(src, va, &n)) return NULL; - i->u.alloc.num_bytes = (unsigned) n; - break; - } - case 'c': // call, call.noret, cas - if ((*src)[1] && (*src)[2]=='s') { - i->tag = LIT_Cas; - if (!lil_parse_kw(src, "cas")) return NULL; - if (!lil_parse_address(src, va, i)) return NULL; - if (!lil_parse_kw(src, "=")) return NULL; - if (!lil_parse_operand(src, va, &i->u.ldst.compare)) return NULL; - if (!lil_parse_kw(src, ",")) return NULL; - if (!lil_parse_operand(src, va, &i->u.ldst.operand)) return NULL; - if (!lil_parse_kw(src, ",")) return NULL; - i->u.ldst.l = lil_parse_label(src, cgl, mem); - if (!i->u.ldst.l) return NULL; - break; - } else if ((*src)[1] && (*src)[2] && (*src)[3] && (*src)[4]=='.') { - i->tag = LIT_Call; - if (!lil_parse_kw(src, "call.noret")) return NULL; - i->u.call.k = LCK_CallNoRet; - } else { - i->tag = LIT_Call; - if (!lil_parse_kw(src, "call")) return NULL; - i->u.call.k = LCK_Call; - } - if (!lil_parse_operand(src, va, &(i->u.call.target))) return NULL; - break; - case 'h': // handles - i->tag = LIT_Handles; - if (!lil_parse_kw(src, "handles")) return NULL; - if (!lil_parse_kw(src, "=")) return NULL; - if (!lil_parse_operand(src, va, &(i->u.handles))) return NULL; - break; - case 'i': // =, in2out, inc - if (num((*src)[1]) || (*src)[1]=='%') goto asgn; - // in2out, inc - if ((*src)[1]=='n' && (*src)[2]=='c') { - // inc - i->tag = LIT_Inc; - if (!lil_parse_kw(src, "inc")) return NULL; - if (!lil_parse_address(src, va, i)) return NULL; - } else { - // in2out - if (entry_sig->arbitrary) { error(src, "in2out in an arbitrary code stub", ""); return NULL; } - i->tag = LIT_In2Out; - if (!lil_parse_kw(src, "in2out")) return NULL; - if (!lil_parse_cc(src, &(i->u.out.cc))) return NULL; - i->u.out.arbitrary = false; - i->u.out.num_arg_types = entry_sig->num_arg_types; - i->u.out.arg_types = entry_sig->arg_types; - if (!lil_parse_kw(src, ":")) return NULL; - if (!lil_parse_type(src, &(i->u.out.ret_type), true)) return NULL; - } - break; - case 'j': // j, jc - if ((*src)[1]=='c') { // jc - i->tag = LIT_Jc; - if (!lil_parse_kw(src, "jc")) return NULL; - if (!lil_parse_cond(src, va, &(i->u.jc.c))) return NULL; - if (!lil_parse_kw(src, ",")) return NULL; - i->u.jc.l = lil_parse_label(src, cgl, mem); - if (!i->u.jc.l) return NULL; - } else { // j - i->tag = LIT_J; - if (!lil_parse_kw(src, "j")) return NULL; - i->u.j = lil_parse_label(src, cgl, mem); - if (!i->u.j) return NULL; - } - break; - case 'l': // =, locals, ld - if (num((*src)[1]) || (*src)[1]=='%') { - goto asgn; - } else if ((*src)[1]=='d') { - // ld - i->tag = LIT_Ld; - if (!lil_parse_kw(src, "ld")) return NULL; - if (!lil_parse_variable(src, va, &(i->u.ldst.operand.val.var))) return NULL; - if (!lil_parse_kw(src, ",")) return NULL; - if (!lil_parse_address(src, va, i)) return NULL; - if (!lil_parse_load_extend(src, i)) return NULL; - } else { - // locals - POINTER_SIZE_INT n; - i->tag = LIT_Locals; - if (!lil_parse_kw(src, "locals")) return NULL; - if (!lil_parse_number(src, va, &n)) return NULL; - i->u.locals = (unsigned) n; - } - break; - case 'm': // m2n_save_all - if (!lil_parse_kw(src, "m2n_save_all")) return NULL; - i->tag = LIT_M2NSaveAll; - break; - case 'o': // =, out - if (num((*src)[1]) || (*src)[1]=='%') goto asgn; - // out - i->tag = LIT_Out; - if (!lil_parse_kw(src, "out")) return NULL; - if (!lil_parse_sig(src, mem, va, &(i->u.out))) return NULL; - break; - case 'p': // push_m2n, pop_m2n, print - if ((*src)[1]=='u') { - // push_m2n - i->tag = LIT_PushM2N; - if (!lil_parse_kw(src, "push_m2n")) return NULL; - i->u.push_m2n.method = NULL; - i->u.push_m2n.current_frame_type = FRAME_UNKNOWN; - POINTER_SIZE_INT n; - POINTER_SIZE_INT ft; - if (!lil_parse_number(src, va, &n)) - return NULL; - else - i->u.push_m2n.method = (Method_Handle) n; - if (!lil_parse_kw(src, ",")) return NULL; - lil_skip_ws(src); - if (!lil_parse_number(src, va, &ft)) - return NULL; - else - i->u.push_m2n.current_frame_type = (frame_type)ft; - lil_skip_ws(src); - if ((*src)[0]!=';') { - if (!lil_parse_kw(src, ",")) return NULL; - if (!lil_parse_kw(src, "handles")) return NULL; - i->u.push_m2n.handles = true; - } else { - i->u.push_m2n.handles = false; - } - } - else if ((*src)[1]=='o') { - // pop_m2n - i->tag = LIT_PopM2N; - if (!lil_parse_kw(src, "pop_m2n")) return NULL; - } - else { - // print - i->tag = LIT_Print; - if (!lil_parse_kw(src, "print")) - return NULL; - lil_skip_ws(src); - if ((*src)[0] == '\'') { - // immediate string - if (!lil_parse_string(src, &i->u.print.str)) - return NULL; - } - else { - // look for string address - POINTER_SIZE_INT n; - if (!lil_parse_number(src, va, &n)) - return NULL; - else - i->u.print.str = (char *) n; - } - lil_skip_ws(src); - if ((*src)[0] == ';') { - // create a dummy immediate 0 operand - i->u.print.arg.is_immed = true; - i->u.print.arg.val.imm = 0; - i->u.print.arg.has_cast = false; - i->u.print.arg.t = LT_Ref; - } - else { - if (!lil_parse_kw(src, ",") || - !lil_parse_operand(src, va, &i->u.print.arg)) - return NULL; - } - } - break; - case 'r': // =, ret - if ((*src)[1]!='e') goto asgn; - // ret - i->tag = LIT_Ret; - if (!lil_parse_kw(src, "ret")) return NULL; - break; - case 's': // =, std_places, st - if ((*src)[1]=='p') goto asgn; - if ((*src)[1] && (*src)[2]=='d') { - // std_places - i->tag = LIT_StdPlaces; - if (!lil_parse_kw(src, "std_places")) return NULL; - POINTER_SIZE_INT n; - if (!lil_parse_number(src, va, &n)) - return NULL; - else - i->u.std_places = (unsigned) n; - } else { - // st - i->tag = LIT_St; - if (!lil_parse_kw(src, "st")) return NULL; - if (!lil_parse_address(src, va, i)) return NULL; - if (!lil_parse_kw(src, ",")) return NULL; - 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; - break; - default: - error(src, "expecting instruction", ""); - return NULL; - asgn: // =, v=ts - i->tag = LIT_Asgn; - if (!lil_parse_variable(src, va, &(i->u.asgn.dst))) return NULL; - if (!lil_parse_kw(src, "=")) return NULL; - lil_skip_ws(src); - // At this point there is either ts, a unary op, or an operand - if ((*src)[0]=='t') { - i->tag = LIT_Ts; - i->u.ts = i->u.asgn.dst; - if (!lil_parse_kw(src, "ts")) return NULL; - } else if ((*src)[0]=='n' || ((*src)[0]=='s' && (*src)[1]=='x') || (*src)[0]=='z') { - // Unary operation - // This code relies on parse_kw not changing src on failure since white space is skipped - if (lil_parse_kw_no_error(src, "not")) i->u.asgn.op = LO_Not; - else if (lil_parse_kw_no_error(src, "neg")) i->u.asgn.op = LO_Neg; - else if (lil_parse_kw_no_error(src, "sx1")) i->u.asgn.op = LO_Sx1; - else if (lil_parse_kw_no_error(src, "sx2")) i->u.asgn.op = LO_Sx2; - else if (lil_parse_kw_no_error(src, "sx4")) i->u.asgn.op = LO_Sx4; - else if (lil_parse_kw_no_error(src, "zx1")) i->u.asgn.op = LO_Zx1; - else if (lil_parse_kw_no_error(src, "zx2")) i->u.asgn.op = LO_Zx2; - else if (lil_parse_kw_no_error(src, "zx4")) i->u.asgn.op = LO_Zx4; - else { error(src, "expecting unary operation", ""); return NULL; } - if (!lil_parse_operand(src, va, &(i->u.asgn.o1))) return NULL; - } else { - // Operand - if (!lil_parse_operand(src, va, &(i->u.asgn.o1))) return NULL; - // Now the statement can either end or have a binary op followed by an operand - lil_skip_ws(src); - if ((*src)[0]!=';') { - if ((*src)[0]=='+') i->u.asgn.op = LO_Add, ++*src; - else if ((*src)[0]=='-') i->u.asgn.op = LO_Sub, ++*src; - else if ((*src)[0]=='*') i->u.asgn.op = LO_SgMul, ++*src; - else if ((*src)[0]=='<' && (*src)[1]=='<') i->u.asgn.op = LO_Shl, *src += 2; - else if ((*src)[0]=='&') i->u.asgn.op = LO_And, ++*src; - else { error(src, "expecting binary operation", ""); return NULL; } - if (!lil_parse_operand(src, va, &(i->u.asgn.o2))) return NULL; - } else { - i->u.asgn.op = LO_Mov; - } - } - break; - } - if (!lil_parse_kw(src,";")) return NULL; - return i; -} - -LilCodeStub* lil_parse_code_stub(const char* src, ...) -{ - assert(src); - const char** cur = &src; - LilVarArgs va; - va.current = 0; - va_start(va.val, src); - - tl::MemoryPool* mem = new tl::MemoryPool; - LilCodeStub* cs = (LilCodeStub*)mem->alloc(sizeof(LilCodeStub)); - cs->my_memory = mem; - cs->cur_gen_label = 0; - cs->is = NULL; - cs->ctxt_state = LCSC_NotComputed; - cs->num_is = 0; - cs->init_ctxt = NULL; - cs->compiled_code_size = 0; - - LilInstruction *i, **tail=&(cs->is); - // originally no BB information is present; lil_cs_init_fg() will create it - cs->bb_list_head = NULL; - cs->num_bbs = 0; - - if (!lil_parse_kw(cur, "entry")) goto clean_up; - POINTER_SIZE_INT n; - if (!lil_parse_number(cur, &va, &n)) { - goto clean_up; - } - else { - cs->num_std_places = (unsigned) n; - } - if (!lil_parse_kw(cur, ":")) goto clean_up; - if (!lil_parse_sig(cur, mem, &va, &(cs->sig))) goto clean_up; - if (!lil_parse_kw(cur, ";")) goto clean_up; - - lil_skip_ws(cur); - while ((*cur)[0]) { - i = lil_parse_instruction(cur, mem, &cs->cur_gen_label, &va, &cs->sig); - if (!i) goto clean_up; - *tail = i; - i->next = NULL; - tail = &(i->next); - lil_skip_ws(cur); - cs->num_is++; - }; - - va_end(va.val); - return cs; - - clean_up: - va_end(va.val); - delete mem; - return NULL; -} - -LilCodeStub* lil_parse_onto_end(LilCodeStub* cs, const char* src, ...) -{ - if (!cs) return NULL; - assert(src); - const char** cur = &src; - LilVarArgs va; - va.current = 0; - va_start(va.val, src); - - tl::MemoryPool* mem = cs->my_memory; - LilInstruction *i=cs->is, **tail=&(cs->is); - while (i) { tail = &i->next; i=i->next; } - - lil_skip_ws(cur); - while ((*cur)[0]) { - i = lil_parse_instruction(cur, mem, &cs->cur_gen_label, &va, &(cs->sig)); - if (!i) goto clean_up; - *tail = i; - i->next = NULL; - tail = &(i->next); - lil_skip_ws(cur); - cs->num_is++; - }; - - va_end(va.val); - return cs; - - clean_up: - va_end(va.val); - delete mem; - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////////// -// Contexts - -enum LilContextState { LCS_Unchanged, LCS_Changed, LCS_Error, LCS_Terminal }; - -struct LilInstructionContext { - LilContextState s; - unsigned num_std_places; - LilType* std_place_types; - LilM2nState m2n; - unsigned num_locals; - LilType* local_types; - LilSig* out_sig; - LilType ret; - unsigned amt_alloced; -}; - -static void lil_new_context2(LilCodeStub* cs, LilInstructionContext* ic) -{ - ic->std_place_types = (LilType*)cs->my_memory->alloc(cs->max_std_places*sizeof(LilType)); - ic->local_types = (LilType*)cs->my_memory->alloc(cs->max_locals *sizeof(LilType)); -} - -static LilInstructionContext* lil_new_context(LilCodeStub* cs) -{ - LilInstructionContext* ic = (LilInstructionContext*)cs->my_memory->alloc(sizeof(LilInstructionContext)); - lil_new_context2(cs, ic); - return ic; -} - -// Copy c1 to c2 -static void lil_copy_context(LilCodeStub* cs, LilInstructionContext* c1, LilInstructionContext* c2) -{ - *c2 = *c1; - for(unsigned i=0; imax_std_places; i++) c2->std_place_types[i] = c1->std_place_types[i]; - for(unsigned j=0; jmax_locals; j++) c2->local_types [j] = c1->local_types [j]; -} - -unsigned lil_ic_get_num_std_places(LilInstructionContext* ic) -{ - return ic->num_std_places; -} - -LilM2nState lil_ic_get_m2n_state(LilInstructionContext* ic) -{ - return ic->m2n; -} - -unsigned lil_ic_get_num_locals(LilInstructionContext* ic) -{ - return ic->num_locals; -} - -LilType lil_ic_get_local_type(LilInstructionContext* ic, unsigned idx) -{ - assert(idxnum_locals); - return ic->local_types[idx]; -} - -LilSig* lil_ic_get_out_sig(LilInstructionContext* ic) -{ - return ic->out_sig; -} - -LilType lil_ic_get_ret_type(LilInstructionContext* ic) -{ - return ic->ret; -} - -unsigned lil_ic_get_amt_alloced(LilInstructionContext* ic) -{ - return ic->amt_alloced; -} - -LilType lil_ic_get_type(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v, bool silent) -{ - switch (v->tag) { - case LVK_In: - if (silent && v->index>=cs->sig.num_arg_types) return LT_Void; - assert(v->indexsig.num_arg_types); - return cs->sig.arg_types[v->index]; - case LVK_StdPlace: - if (silent && v->index>=c->num_std_places) return LT_Void; - assert(v->indexnum_std_places); - return c->std_place_types[v->index]; - case LVK_Local: - if (silent && v->index>=c->num_locals) return LT_Void; - assert(v->indexnum_locals); - return c->local_types[v->index]; - //break; //remark #111: statement is unreachable - case LVK_Out: - if (silent && v->index>=c->out_sig->num_arg_types) return LT_Void; - assert(c->out_sig && v->indexout_sig->num_arg_types); - return c->out_sig->arg_types[v->index]; - case LVK_Ret: - return c->ret; - default: ASSERT(0, "Unknown variable kind"); for(;;); - } -} - -LilType lil_ic_get_type_aux(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o, bool silent) -{ - if (o->has_cast) - return o->t; - else - if (o->is_immed) - return LT_PInt; - else - return lil_ic_get_type(cs, c, &o->val.var, silent); -} - -LilType lil_ic_get_type(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o) -{ - return lil_ic_get_type_aux(cs, c, o, false); -} - -static void lil_ic_set_type(LilInstructionContext* c, LilVariable* v, LilType t) -{ - switch (v->tag) { - case LVK_In: - // Ignore - break; - case LVK_StdPlace: - if (v->indexnum_std_places) - c->std_place_types[v->index] = t; - break; - case LVK_Local: - if (v->indexnum_locals) - c->local_types[v->index] = t; - break; - case LVK_Out: - // Ignore - break; - case LVK_Ret: - c->ret = t; - break; - default: ASSERT(0, "Unknown variable kind"); - } -} - -static LilType lil_type_asgn(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i); - -LilType lil_instruction_get_dest_type(LilCodeStub *cs, - LilInstruction *i, - LilInstructionContext *ctxt) { - switch (i->tag) { - case LIT_Alloc: - return LT_PInt; - case LIT_Asgn: - return lil_type_asgn(cs, ctxt, i); - case LIT_Ts: - return LT_PInt; - case LIT_Ld: - return i->u.ldst.t; - default: - return LT_Void; - } -} - -static LilType lil_type_unary_op(LilOperation op, LilType t) -{ - switch (op) { - case LO_Mov: - case LO_Neg: - case LO_Not: - return t; - case LO_Sx1: - case LO_Sx2: - case LO_Sx4: - case LO_Zx1: - case LO_Zx2: - case LO_Zx4: - return t; - default: - ASSERT(0, "Unexpected operation"); - return LT_Void; - } -} - -static LilType lil_type_binary_op(LilOperation UNREF op, LilType t1, LilType UNREF t2) -{ - return t1; -} - -static LilType lil_type_asgn(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i) -{ - if (lil_operation_is_binary(i->u.asgn.op)) - return lil_type_binary_op(i->u.asgn.op, lil_ic_get_type_aux(cs, c, &i->u.asgn.o1, true), lil_ic_get_type_aux(cs, c, &i->u.asgn.o2, true)); - // ? 20040205: This is a hack to get the object allocation fastpath to type check - else if (i->u.asgn.op == LO_Sx4 && !i->u.asgn.o1.is_immed && i->u.asgn.o1.val.var.tag == LVK_In && i->u.asgn.o1.val.var.index == 0) - return LT_PInt; - else - return lil_type_unary_op(i->u.asgn.op, lil_ic_get_type_aux(cs, c, &i->u.asgn.o1, true)); -} - -// Compute the context at the beginning of an instruction given the context that falls through to it -// This function mutates c in place -static void lil_pre_context(LilInstructionContext* c, LilCodeStub* cs, LilInstruction* i) -{ - if (i->tag==LIT_Label && i->u.label.ctxt) lil_copy_context(cs, i->u.label.ctxt, c); -} - -// Compute the context following the given instruction in the given code stub given the context before the instruction -// This function mutates c in place -// Note: -// 1) If the instruction is terminal the context state is set to LCS_Terminal - -static void lil_next_context(LilInstructionContext* c, LilCodeStub* cs, LilInstruction* i) -{ - unsigned j; - switch (i->tag) { - case LIT_Label: - break; - case LIT_Locals: - c->num_locals = i->u.locals; - for(j=0; jnum_locals; j++) c->local_types[j] = LT_Void; - break; - case LIT_StdPlaces: - c->num_std_places = i->u.std_places; - for(j=0; jnum_std_places; j++) c->std_place_types[j] = LT_Void; - break; - case LIT_Alloc: - c->amt_alloced += i->u.alloc.num_bytes; - lil_ic_set_type(c, &i->u.alloc.dst, LT_PInt); - break; - case LIT_Asgn:{ - LilType t = lil_type_asgn(cs, c, i); - lil_ic_set_type(c, &i->u.asgn.dst, t); - break;} - case LIT_Ts: - lil_ic_set_type(c, &i->u.ts, LT_PInt); - break; - case LIT_Handles: - break; - case LIT_Ld: - lil_ic_set_type(c, &i->u.ldst.operand.val.var, (i->u.ldst.extend==LLX_None ? i->u.ldst.t : LT_PInt)); - break; - case LIT_St: - break; - case LIT_Inc: - break; - case LIT_Cas: - break; - case LIT_J: - c->s = LCS_Terminal; - break; - case LIT_Jc: - break; - case LIT_Out: - case LIT_In2Out: - c->out_sig = &i->u.out; - break; - case LIT_Call: - if (c->out_sig) c->ret = c->out_sig->ret_type; - c->num_std_places = 0; - c->out_sig = NULL; - if (i->u.call.k!=LCK_Call) c->s = LCS_Terminal; - break; - case LIT_Ret: - c->amt_alloced = 0; - c->num_locals = 0; - c->num_std_places = 0; - c->out_sig = NULL; - c->s = LCS_Terminal; - break; - case LIT_PushM2N: - c->m2n = (i->u.push_m2n.handles ? LMS_Handles : LMS_NoHandles); - c->out_sig = NULL; - c->ret = LT_Void; - break; - case LIT_M2NSaveAll: - break; - case LIT_PopM2N: - c->m2n = LMS_NoM2n; - c->amt_alloced = 0; - c->num_std_places = 0; - c->out_sig = NULL; - break; - case LIT_Print: - // nothing to do here - break; - default: ASSERT(0, "Unknown instruction tag"); - } -} - -// Are two signatures equal? - -static bool lil_sig_equal(LilSig* s1, LilSig* s2) -{ - if (s1->cc!=s2->cc || s1->num_arg_types!=s2->num_arg_types || s1->ret_type!=s2->ret_type) return false; - for(unsigned i=0; inum_arg_types; i++) - if (s1->arg_types[i] != s2->arg_types[i]) return false; - return true; -} - -// Merge the contexts of two control flow edges -// This function mutates c1 in place to the merged context, setting the state to changed if it changed and error if the merge is invalid - -static void lil_merge_contexts(LilInstructionContext* c1, LilInstructionContext* c2) -{ - if (c1->s==LCS_Error) return; - if (c1->num_std_places > c2->num_std_places) { c1->s = LCS_Changed; c1->num_std_places = c2->num_std_places; } - for(unsigned j=0; jnum_std_places; j++) - if (c1->std_place_types[j]!=c2->std_place_types[j]) { c1->s = LCS_Changed; c1->num_std_places = j; break; } - if (c1->m2n != c2->m2n) { - fprintf(stderr, "Context mismatch: different M2N states\n"); - fflush(stderr); - c1->s = LCS_Error; - return; - } - if (c1->num_locals != c2->num_locals) { - fprintf(stderr, "Context mismatch: different number of locals\n"); - fflush(stderr); - c1->s = LCS_Error; - return; - } - for(unsigned k=0; knum_locals; k++) - if (c1->local_types[k]!=c2->local_types[k]) { - fprintf(stderr, "Context mismatch: different types at local %d\n", k); - fflush(stderr); - c1->s = LCS_Error; - return; - } - if (c1->out_sig) { - if (!c2->out_sig || !lil_sig_equal(c1->out_sig, c2->out_sig)) { - fprintf(stderr, "Context mismatch: different output signatures\n"); - fflush(stderr); - c1->s = LCS_Error; - return; - } - } else { - if (c2->out_sig) { - fprintf(stderr, "Context mismatch: different output signatures\n"); - fflush(stderr); - c1->s = LCS_Error; - return; } - } - if (c1->ret!=LT_Void && c1->ret!=c2->ret) { c1->s = LCS_Changed; c1->ret = LT_Void; } - if (c1->amt_alloced != c2->amt_alloced) { - fprintf(stderr, "Context mismatch: different allocated memory amounts\n"); - fflush(stderr); - c1->s = LCS_Error; - return; - } -} - -// Merge a context from a control transfer instruction into one of the label it transfers to -// returns false if the label is not found - -static bool lil_merge_context_with_label(LilCodeStub* cs, LilLabel l, LilInstructionContext* c) -{ - LilInstruction* i = lil_find_label(cs, l); - if (i == NULL) - return false; - - if (i->u.label.ctxt) { - lil_merge_contexts(i->u.label.ctxt, c); - } else { - i->u.label.ctxt = lil_new_context(cs); - lil_copy_context(cs, c, i->u.label.ctxt); - i->u.label.ctxt->s = LCS_Changed; - } - return true; -} - -// Merge a fall through context into a label instruction -// Note that if c->s is LCS_Terminal then there was no fall through as the previous instruction was terminal - -static void lil_merge_context_with_label_instruction(LilCodeStub* cs, LilInstruction* i, LilInstructionContext* c) -{ - if (c->s==LCS_Terminal) { - assert(i->u.label.ctxt); - lil_copy_context(cs, i->u.label.ctxt, c); - return; - } - if (i->u.label.ctxt) { - lil_merge_contexts(i->u.label.ctxt, c); - lil_copy_context(cs, i->u.label.ctxt, c); - } else { - i->u.label.ctxt = lil_new_context(cs); - lil_copy_context(cs, c, i->u.label.ctxt); - i->u.label.ctxt->s = LCS_Changed; - } -} - -// Compute the context of each label in a code stub so that contexts can be tracked as instructions are iterated over - -void lil_compute_contexts(LilCodeStub* cs) -{ - // If already determined then return - if (!cs || cs->ctxt_state!=LCSC_NotComputed) return; - - // Count stuff - cs->max_std_places = cs->num_std_places; - cs->max_locals = 0; - for(LilInstruction* i = cs->is; i; i=i->next) { - if (i->tag==LIT_StdPlaces && i->u.std_places>cs->max_std_places) cs->max_std_places = i->u.std_places; - if (i->tag==LIT_Locals && i->u.locals>cs->max_locals) cs->max_locals = i->u.locals; - } - - // Compute the initial context - cs->init_ctxt = (LilInstructionContext*)cs->my_memory->alloc(sizeof(LilInstructionContext)); - lil_new_context2(cs, cs->init_ctxt); - cs->init_ctxt->s = LCS_Unchanged; - cs->init_ctxt->num_locals = 0; - cs->init_ctxt->out_sig = NULL; - cs->init_ctxt->num_std_places = cs->num_std_places; - for(unsigned k=0; knum_std_places; k++) cs->init_ctxt->std_place_types[k] = LT_PInt; - cs->init_ctxt->ret = LT_Void; - cs->init_ctxt->m2n = LMS_NoM2n; - cs->init_ctxt->amt_alloced = 0; - - // Fairly standard dataflow analysis - // while (something needs recomputing) - // iterate through instructions from beginning to end - // if label then: merge current context with label's stored context - // if j or jc or cas then: merge current context with target - // in all cases: compute next context - LilInstructionContext cur_ctxt; - lil_new_context2(cs, &cur_ctxt); - bool changed = true; - while (changed) { - changed = false; - lil_copy_context(cs, cs->init_ctxt, &cur_ctxt); - LilInstructionIterator iter(cs, false); - unsigned inum = 0; - while (!iter.at_end()) { - LilInstruction* i = iter.get_current(); - if (i->tag == LIT_Label) { - lil_merge_context_with_label_instruction(cs, i, &cur_ctxt); - if (!i->u.label.ctxt || i->u.label.ctxt->s==LCS_Error) { - fprintf(stderr, "Error merging contexts at label: %s\n", i->u.label.l); - fflush(stderr); - cs->ctxt_state = LCSC_Error; - return; - } - if (i->u.label.ctxt->s==LCS_Changed) { i->u.label.ctxt->s = LCS_Unchanged; changed = true; } - } - lil_next_context(&cur_ctxt, cs, i); - if (cur_ctxt.s==LCS_Error) { - fprintf(stderr, "Error: computed next context for instruction %d:\n", inum); - fflush(stderr); - lil_print_instruction(stderr, i); - cs->ctxt_state = LCSC_Error; - return; - } - if (i->tag == LIT_J) { - if (!lil_merge_context_with_label(cs, i->u.j, &cur_ctxt)) { - fprintf(stderr, "Error: label %s does not exist\n", i->u.j); - fflush(stderr); - cs->ctxt_state = LCSC_Error; - return; - } - } else if (i->tag == LIT_Jc) { - if (!lil_merge_context_with_label(cs, i->u.jc.l, &cur_ctxt)) { - fprintf(stderr, "Error: label %s does not exist\n", i->u.jc.l); - fflush(stderr); - cs->ctxt_state = LCSC_Error; - return; - } - } else if (i->tag == LIT_Cas) { - if (!lil_merge_context_with_label(cs, i->u.ldst.l, &cur_ctxt)) { - fprintf(stderr, "Error: label %s does not exist\n", i->u.ldst.l); - fflush(stderr); - cs->ctxt_state = LCSC_Error; - return; - } - } - iter.goto_next(); - ++inum; - } - } - cs->ctxt_state = LCSC_Computed; -} - -//////////////////////////////////////////////////////////////////////////////////// -// Iteragators (other than validity) - -VMEXPORT LilSig* lil_cs_get_sig(LilCodeStub* cs) { return &cs->sig; } - -VMEXPORT unsigned lil_cs_get_num_instructions(LilCodeStub* cs) -{ - return cs->num_is; -} - -VMEXPORT unsigned lil_cs_get_num_BBs(LilCodeStub *cs) { - if (cs->bb_list_head == NULL) - LilBb::init_fg(cs); - assert(cs->bb_list_head != NULL); - return cs->num_bbs; -} - -VMEXPORT LilBb* lil_cs_get_entry_BB(LilCodeStub* cs) { - if (cs->bb_list_head == NULL) - LilBb::init_fg(cs); - assert(cs->bb_list_head != NULL); - return cs->bb_list_head; -} - -VMEXPORT unsigned lil_cs_get_max_std_places(LilCodeStub * cs) { - return cs->max_std_places; -} - -VMEXPORT unsigned lil_cs_get_max_locals(LilCodeStub * cs) { - return cs->max_locals; -} - -VMEXPORT void lil_cs_set_code_size(LilCodeStub * cs, size_t size) { - cs->compiled_code_size = size; -} - -VMEXPORT size_t lil_cs_get_code_size(LilCodeStub * cs) { - return cs->compiled_code_size; -} - -static LilInstruction* lil_find_label(LilCodeStub* cs, LilLabel l) -{ - for(LilInstruction* i=cs->is; i; i=i->next) - if (i->tag==LIT_Label && strcmp(i->u.label.l, l)==0) - return i; - return NULL; -} - -VMEXPORT LilInstructionIterator::LilInstructionIterator(LilCodeStub* _cs, bool _track_ctxt) - : cs(_cs), bb(NULL), cur(_cs->is), track_ctxt(_track_ctxt) -{ - if (track_ctxt) { - lil_compute_contexts(cs); - ctxt = lil_new_context(cs); - lil_copy_context(cs, cs->init_ctxt, ctxt); - if (cs->is) lil_pre_context(ctxt, cs, cs->is); - } -} - - -VMEXPORT LilInstructionIterator::LilInstructionIterator(LilCodeStub* _cs, LilBb *_bb, bool _track_ctxt) - : cs(_cs), bb(_bb), track_ctxt(_track_ctxt) -{ - assert(bb != NULL); - cur = bb->get_first(); - - if (track_ctxt) { - lil_compute_contexts(cs); - ctxt = lil_new_context(cs); - // current context is the initial context of the BB! - lil_copy_context(cs, _bb->get_context(), ctxt); - if (cur) - lil_pre_context(ctxt, cs, cur); - } -} - - -VMEXPORT bool LilInstructionIterator::at_end() -{ - return cur==NULL; -} - -VMEXPORT LilInstruction* LilInstructionIterator::get_current() -{ - return cur; -} - -VMEXPORT LilInstructionContext* LilInstructionIterator::get_context() -{ - assert(track_ctxt); - return ctxt; -} - -VMEXPORT void LilInstructionIterator::goto_next() -{ - if (track_ctxt && cur) lil_next_context(ctxt, cs, cur); - if (cur) { - // if this is a BB iterator, gotta check for BB end - if (bb != NULL && cur == bb->get_last()) - cur = NULL; - else - cur = cur->next; - } - if (track_ctxt && cur) lil_pre_context(ctxt, cs, cur); -} - - -VMEXPORT LilInstructionVisitor::LilInstructionVisitor() -{ -} - - -void lil_visit_instruction(LilInstruction* i, LilInstructionVisitor* v) -{ - switch (i->tag) { - case LIT_Label: - v->label(i->u.label.l); - break; - case LIT_Locals: - v->locals(i->u.locals); - break; - case LIT_StdPlaces: - v->std_places(i->u.std_places); - break; - case LIT_Alloc: - v->alloc(&i->u.alloc.dst, i->u.alloc.num_bytes); - break; - case LIT_Asgn: - v->asgn(&i->u.asgn.dst, i->u.asgn.op, &i->u.asgn.o1, &i->u.asgn.o2); - break; - case LIT_Ts: - v->ts(&i->u.ts); - break; - case LIT_Handles: - v->handles(&i->u.handles); - break; - case LIT_Ld: - v->ld(i->u.ldst.t, &i->u.ldst.operand.val.var, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), - i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, i->u.ldst.extend); - break; - case LIT_St: - v->st(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), - i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, - &i->u.ldst.operand); - break; - case LIT_Inc: - v->inc(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), - i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel); - break; - case LIT_Cas: - v->cas(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), - i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, - &i->u.ldst.compare, &i->u.ldst.operand, i->u.ldst.l); - break; - case LIT_J: - v->j(i->u.j); - break; - case LIT_Jc: - v->jc(i->u.jc.c.tag, &i->u.jc.c.o1, &i->u.jc.c.o2, i->u.jc.l); - break; - case LIT_Out: - v->out(&i->u.out); - break; - case LIT_In2Out: - v->in2out(&i->u.out); - break; - case LIT_Call: - v->call(&i->u.call.target, i->u.call.k); - break; - case LIT_Ret: - v->ret(); - break; - case LIT_PushM2N: - v->push_m2n(i->u.push_m2n.method, i->u.push_m2n.current_frame_type, i->u.push_m2n.handles); - break; - case LIT_M2NSaveAll: - v->m2n_save_all(); - break; - case LIT_PopM2N: - v->pop_m2n(); - break; - case LIT_Print: - v->print(i->u.print.str, &i->u.print.arg); - break; - default: ASSERT(0, "Unknown instruction tag"); - } -} - -LilVariableKind lil_variable_get_kind(LilVariable* v) { return v->tag; } -unsigned lil_variable_get_index(LilVariable* v) { return v->index; } - -bool lil_variable_is_equal(LilVariable* v1, 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) { - assert(o->is_immed); - return o->val.imm; -} - -LilVariable* lil_operand_get_variable(LilOperand* o) { - assert(!o->is_immed); - return &o->val.var; -} - -LilCc lil_sig_get_cc(LilSig* sig) { return sig->cc; } -bool lil_sig_is_arbitrary(LilSig* sig) { return sig->arbitrary; } -unsigned lil_sig_get_num_args(LilSig* sig) { return sig->num_arg_types; } -LilType lil_sig_get_arg_type(LilSig* sig, unsigned num) { - assert(num < sig->num_arg_types); - return sig->arg_types[num]; -} -LilType lil_sig_get_ret_type(LilSig* sig) { return sig->ret_type; } - -bool lil_predicate_is_binary(enum LilPredicate c) -{ - switch (c) { - case LP_IsZero: - case LP_IsNonzero: - return false; - case LP_Eq: - case LP_Ne: - case LP_Le: - case LP_Lt: - case LP_Ule: - case LP_Ult: - return true; - default: - ASSERT(0, "Unknown predicate"); - return false; // not reached - } -} - -bool lil_predicate_is_signed(LilPredicate p) { - return (p == LP_Ule || p == LP_Ult); -} - -bool lil_operation_is_binary(enum LilOperation op) -{ - switch (op) { - case LO_Mov: - case LO_Neg: - case LO_Not: - case LO_Sx1: - case LO_Sx2: - case LO_Sx4: - case LO_Zx1: - case LO_Zx2: - case LO_Zx4: - return false; - case LO_Add: - case LO_Sub: - case LO_SgMul: - case LO_Shl: - case LO_And: - return true; - default: - ASSERT(0, "Unknown operation"); - return false; // not reached - } -} - -//////////////////////////////////////////////////////////////////////////////////// -// Validity - -static bool lil_is_valid_asgn(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v, LilType t) -{ - switch (v->tag) { - case LVK_In: - return v->indexsig.num_arg_types && cs->sig.arg_types[v->index]==t; - case LVK_StdPlace: - return v->indexnum_std_places; - case LVK_Local: - return v->indexnum_locals; - case LVK_Out: - return c->out_sig && v->indexout_sig->num_arg_types && c->out_sig->arg_types[v->index]==t; - case LVK_Ret: - return v->index<1; - default: - return false; - } -} - -static bool lil_is_valid_variable(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v) -{ - switch (v->tag) { - case LVK_In: return v->indexsig.num_arg_types; - case LVK_StdPlace: return v->indexnum_std_places; - case LVK_Local: return v->indexnum_locals; - case LVK_Out: return c->out_sig && v->indexout_sig->num_arg_types; - case LVK_Ret: return v->index<1 && c->ret!=LT_Void; - default: return false; - } -} - -#ifdef _IPF_ -#define PLATFORM_INT LT_G8 -#else -#define PLATFORM_INT LT_G4 -#endif - -static bool lil_are_types_compatible(LilType t1, LilType t2) -{ - return t1==t2 || ((t1==LT_Ref || t1==LT_PInt || t1==PLATFORM_INT) && (t2==LT_Ref || t2==LT_PInt || t2==PLATFORM_INT)); -} - -static bool lil_is_valid_operand(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o) -{ - if (!o->is_immed && !lil_is_valid_variable(cs, c, &(o->val.var))) return false; - if (o->has_cast) { - LilType raw_type = lil_ic_get_type(cs, c, o); - if (!lil_are_types_compatible(o->t, raw_type)) return false; - } - return true; -} - -static bool lil_is_valid_address(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i) -{ - if (i->u.ldst.is_base) { - if (!lil_is_valid_variable(cs, c, &(i->u.ldst.base))) return false; - LilType t = lil_ic_get_type(cs, c, &(i->u.ldst.base), true); - if (t!=LT_Ref && t!=LT_PInt && t!=PLATFORM_INT) return false; - } - if (i->u.ldst.is_index) { - if (!lil_is_valid_variable(cs, c, &(i->u.ldst.index))) return false; - LilType t = lil_ic_get_type(cs, c, &(i->u.ldst.index), true); - if (t!=LT_Ref && t!=LT_PInt && t!=PLATFORM_INT) return false; - } - return true; -} - -static bool lil_is_valid_label(LilCodeStub* cs, LilLabel l) -{ - return lil_find_label(cs, l)!=NULL; -} - -static bool lil_verify_unary_op(LilOperation UNREF op, LilType UNREF t) -{ - return true; -} - -static bool lil_verify_binary_op(LilOperation op, LilType t1, LilType t2) -{ - if (op==LO_Shl) - return t2==LT_PInt; - else - return t1==t2; -} - -static bool lil_verify_binary_cond(LilPredicate UNREF p, LilType t1, LilType t2) -{ - return t1==t2; -} - -static bool lil_print_err(char *s, LilInstruction* i, unsigned inst_number) -{ - fprintf(stderr, "lil code stub invalid at instruction %d: %s\n ", inst_number, s); - if (i) lil_print_instruction(stdout, i); - fflush(stderr); - return false; -} - -#define ERR(s) { return lil_print_err(s, i, inst_number); } - -bool lil_is_valid(LilCodeStub* cs) -{ - unsigned inst_number = 0; - LilInstruction* i = NULL; - - if (!cs) ERR("code stub is null"); - - lil_compute_contexts(cs); - if (cs->ctxt_state == LCSC_Error) ERR("control flow contexts inconsistent"); - - // Check instructions - bool last_was_terminal = false; - LilInstructionIterator iter(cs, true); - while (!iter.at_end()) { - inst_number++; - i = iter.get_current(); - LilInstructionContext* c = iter.get_context(); - - switch (i->tag) { - case LIT_Label: - { - LilLabel l = i->u.label.l; - LilInstruction *label_def = lil_find_label(cs, l); - if (label_def != i) { - char buffer[256]; - sprintf(buffer, "label %s redefined", l); - ERR(buffer); - } - break; - } - case LIT_Locals: - break; - case LIT_StdPlaces: - break; - case LIT_Alloc: - if (c->out_sig) ERR("alloc between out and call"); - if (!lil_is_valid_variable(cs, c, &(i->u.alloc.dst))) ERR("invalid variable in alloc"); - break; - case LIT_Asgn: - if (!lil_is_valid_operand(cs, c, &(i->u.asgn.o1))) ERR("invalid first operand in assignment"); - if (lil_operation_is_binary(i->u.asgn.op)) { - if (!lil_is_valid_operand(cs, c, &(i->u.asgn.o2))) ERR("invalid second operand in assignment"); - if (!lil_verify_binary_op(i->u.asgn.op, lil_ic_get_type(cs, c, &i->u.asgn.o1), lil_ic_get_type(cs, c, &i->u.asgn.o2))) - ERR("operand type mismatch in assignment"); - } else { - if (!lil_verify_unary_op(i->u.asgn.op, lil_ic_get_type(cs, c, &i->u.asgn.o1))) - ERR("operand type mismatch in assignment"); - } - // ? 20040205: This is a hack to get the object allocation fastpath to type check - if (i->u.asgn.op == LO_Sx4 && !i->u.asgn.o1.is_immed && i->u.asgn.o1.val.var.tag == LVK_In && i->u.asgn.o1.val.var.index == 0) { - if (!lil_is_valid_asgn(cs, c, &i->u.asgn.dst, LT_PInt)) - ERR("invalid destination or type incompatibility in assignment"); - } else if (!lil_is_valid_asgn(cs, c, &i->u.asgn.dst, lil_type_asgn(cs, c, i))) { - ERR("invalid destination or type incompatibility in assignment"); - } - break; - case LIT_Ts: - if (!lil_is_valid_asgn(cs, c, &i->u.ts, LT_PInt)) ERR("invalid destination in ts"); - break; - case LIT_Handles: - if (c->m2n!=LMS_Handles) ERR("handles not dominated by push_m2n handles"); - if (!lil_is_valid_operand(cs, c, &(i->u.handles))) ERR("invalid operand in handles"); - if (lil_ic_get_type(cs, c, &i->u.handles)!=LT_PInt) ERR("operand not platform int in handles"); - break; - case LIT_Ld: - if (!lil_is_valid_asgn(cs, c, &i->u.ldst.operand.val.var, (i->u.ldst.extend==LLX_None ? i->u.ldst.t : LT_PInt))) - ERR("invalid destination in load"); - if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in load"); - break; - case LIT_St: - if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in store"); - if (!i->u.ldst.operand.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in store"); - if (!lil_is_valid_operand(cs, c, &(i->u.ldst.operand))) ERR("invalid source in store"); - break; - case LIT_Inc: - if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in inc"); - break; - case LIT_Cas: - if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in cas"); - if (!i->u.ldst.compare.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in cas compare"); - if (!i->u.ldst.operand.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in cas source"); - if (!lil_is_valid_operand(cs, c, &(i->u.ldst.compare))) ERR("invalid compare in cas"); - if (!lil_is_valid_operand(cs, c, &(i->u.ldst.operand))) ERR("invalid source in cas"); - if (!lil_is_valid_label(cs, i->u.ldst.l)) ERR("bad target in cas"); - break; - case LIT_J: - if (!lil_is_valid_label(cs, i->u.j)) ERR("bad target in j"); - break; - case LIT_Jc: - // Should do typechecks here - if (!lil_is_valid_label(cs, i->u.jc.l)) ERR("bad target in jc"); - if (!lil_is_valid_operand(cs, c, &(i->u.jc.c.o1))) ERR("invalid first operand in condition"); - if (lil_predicate_is_binary(i->u.jc.c.tag)) { - if (!lil_is_valid_operand(cs, c, &(i->u.jc.c.o2))) ERR("invalid second operand in condition"); - if (!lil_verify_binary_cond(i->u.jc.c.tag, lil_ic_get_type(cs, c, &(i->u.jc.c.o1)), lil_ic_get_type(cs, c, &(i->u.jc.c.o2)))) - ERR("operand type mismatch in conditional jump"); - } - break; - case LIT_Out: - break; - case LIT_In2Out: - if (cs->sig.arbitrary) ERR("in2out in arbitrary code stub"); - break; - case LIT_Call: - if (i->u.call.k!=LCK_TailCall && !c->out_sig) ERR("call not dominated by out or in2out"); - if (!lil_is_valid_operand(cs, c, &(i->u.call.target))) ERR("invalid operand in call"); - if (lil_ic_get_type(cs, c, &i->u.call.target)!=LT_PInt) ERR("operand not platform int in call"); - break; - case LIT_Ret: - if (cs->sig.arbitrary) ERR("cannot return from an arbitrary signature entry"); - if (cs->sig.ret_type!=LT_Void && cs->sig.ret_type!=c->ret) - ERR("ret with invalid return value"); - if (c->m2n) ERR("return with m2n"); - break; - case LIT_PushM2N: - if (c->amt_alloced>0) ERR("alloc before push_m2n"); - if (c->m2n!=LMS_NoM2n) ERR("push m2n twice"); - break; - case LIT_M2NSaveAll: - if (c->m2n==LMS_NoM2n) ERR("m2n save all not dominated by push m2n"); - break; - case LIT_PopM2N: - if (c->m2n==LMS_NoM2n) ERR("pop m2n not dominated by push"); - break; - case LIT_Print: - if (!lil_is_valid_operand(cs, c, &i->u.print.arg)) - ERR("invalid argument to print"); - break; - default: - ERR("unknown instruction"); - } - - iter.goto_next(); - last_was_terminal = (c->s==LCS_Terminal); - } - - if (!last_was_terminal) ERR("last instruction not terminal"); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////////// -// Printing Utilities - -void lil_print_cc(FILE* out, enum LilCc cc) -{ - switch (cc) { - case LCC_Platform: fprintf(out, "platform"); break; - case LCC_Managed: fprintf(out, "managed"); break; - case LCC_Rth: fprintf(out, "rth"); break; - case LCC_Jni: fprintf(out, "jni"); break; - case LCC_StdCall: fprintf(out, "stdcall"); break; - default: ASSERT(0, "Unknown calling convention"); - } - fflush(out); -} - -void lil_print_type(FILE* out, enum LilType t) -{ - switch (t) { - case LT_G1: fprintf(out, "g1"); break; - case LT_G2: fprintf(out, "g2"); break; - case LT_G4: fprintf(out, "g4"); break; - case LT_G8: fprintf(out, "g8"); break; - case LT_F4: fprintf(out, "f4"); break; - case LT_F8: fprintf(out, "f8"); break; - case LT_Ref: fprintf(out, "ref"); break; - case LT_PInt: fprintf(out, "pint"); break; - case LT_Void: fprintf(out, "void"); break; - default: ASSERT(0, "Unknown LIL type"); - } - fflush(out); -} - -void lil_print_sig(FILE* out, LilSig* sig) -{ - assert(sig); - lil_print_cc(out, sig->cc); - fprintf(out, ":"); - if (sig->arbitrary) { - fprintf(out, "arbitrary"); - } else { - for(unsigned i=0; inum_arg_types; i++) { - if (i>0) fprintf(out, ","); - lil_print_type(out, sig->arg_types[i]); - } - fprintf(out, ":"); - lil_print_type(out, sig->ret_type); - } - fflush(out); -} - -void lil_print_variable(FILE* out, LilVariable* v) -{ - assert(v); - switch (v->tag) { - case LVK_In: - fprintf(out, "i"); - break; - case LVK_StdPlace: - fprintf(out, "sp"); - break; - case LVK_Out: - fprintf(out, "o"); - break; - case LVK_Local: - fprintf(out, "l"); - break; - case LVK_Ret: - assert(v->index==0); - fprintf(out, "r"); - return; - default: ASSERT(0, "Unknown kind"); - } - fprintf(out, "%d", v->index); - fflush(out); -} - -void lil_print_operand(FILE* out, LilOperand* o) -{ - assert(o); - if (o->is_immed) - // since imm is POINTER_SIZE_INT, %p should work in all cases - fprintf(out, "0x%p", (void *)o->val.imm); - else - lil_print_variable(out, &(o->val.var)); - if (o->has_cast) { - fprintf(out, ":"); - lil_print_type(out, o->t); - } - fflush(out); -} - -void lil_print_address(FILE* out, LilInstruction* i) -{ - bool printed_term = false; - fprintf(out, "["); - if (i->u.ldst.is_base) { - lil_print_variable(out, &(i->u.ldst.base)); - printed_term = true; - } - if (i->u.ldst.is_index) { - if (printed_term) - fprintf(out, "+"); - fprintf(out, "%d*", i->u.ldst.scale); - lil_print_variable(out, &(i->u.ldst.index)); - printed_term = true; - } - if (i->u.ldst.offset != 0) { - if (printed_term) - fprintf(out, "+"); - fprintf(out, "0x%p", (void *)i->u.ldst.offset); - printed_term = true; - } - if (!printed_term) - fprintf(out, "0x0"); - fprintf(out, ":"); - lil_print_type(out, i->u.ldst.t); - switch (i->u.ldst.acqrel) { - case LAR_None: break; - case LAR_Acquire: fprintf(out, ",acquire"); break; - case LAR_Release: fprintf(out, ",release"); break; - default: ASSERT(0, "Unexpected acqrel value"); - } - fprintf(out, "]"); - fflush(out); -} - -void lil_print_instruction(FILE* out, LilInstruction* i) -{ - assert(i); - switch (i->tag) { - case LIT_Label: - fprintf(out, ":%s", i->u.label.l); - break; - case LIT_Locals: - fprintf(out, "locals %d", i->u.locals); - break; - case LIT_StdPlaces: - fprintf(out, "std_places %d", i->u.std_places); - break; - case LIT_Alloc: - fprintf(out, "alloc "); - lil_print_variable(out, &i->u.alloc.dst); - fprintf(out, ",0x%x", i->u.alloc.num_bytes); - break; - case LIT_Asgn: - lil_print_variable(out, &(i->u.asgn.dst)); - fprintf(out, "="); - switch (i->u.asgn.op) { - case LO_Mov: - lil_print_operand(out, &(i->u.asgn.o1)); - break; - case LO_Add: - lil_print_operand(out, &(i->u.asgn.o1)); - fprintf(out, "+"); - lil_print_operand(out, &(i->u.asgn.o2)); - break; - case LO_Sub: - lil_print_operand(out, &(i->u.asgn.o1)); - fprintf(out, "-"); - lil_print_operand(out, &(i->u.asgn.o2)); - break; - case LO_SgMul: - lil_print_operand(out, &(i->u.asgn.o1)); - fprintf(out, "*"); - lil_print_operand(out, &(i->u.asgn.o2)); - break; - case LO_Shl: - lil_print_operand(out, &(i->u.asgn.o1)); - fprintf(out, "<<"); - lil_print_operand(out, &(i->u.asgn.o2)); - break; - case LO_And: - lil_print_operand(out, &(i->u.asgn.o1)); - fprintf(out, "&"); - lil_print_operand(out, &(i->u.asgn.o2)); - break; - case LO_Neg: - fprintf(out, "-"); - lil_print_operand(out, &(i->u.asgn.o1)); - break; - case LO_Not: - fprintf(out, "not "); - lil_print_operand(out, &(i->u.asgn.o1)); - break; - case LO_Sx1: - fprintf(out, "sx1 "); - lil_print_operand(out, &(i->u.asgn.o1)); - break; - case LO_Sx2: - fprintf(out, "sx2 "); - lil_print_operand(out, &(i->u.asgn.o1)); - break; - case LO_Sx4: - fprintf(out, "sx4 "); - lil_print_operand(out, &(i->u.asgn.o1)); - break; - case LO_Zx1: - fprintf(out, "zx1 "); - lil_print_operand(out, &(i->u.asgn.o1)); - break; - case LO_Zx2: - fprintf(out, "zx2 "); - lil_print_operand(out, &(i->u.asgn.o1)); - break; - case LO_Zx4: - fprintf(out, "zx4 "); - lil_print_operand(out, &(i->u.asgn.o1)); - break; - default: ASSERT(0, "Unknown operation"); - } - break; - case LIT_Ts: - lil_print_variable(out, &i->u.ts); - fprintf(out, "=ts"); - break; - case LIT_Handles: - fprintf(out, "handles="); - lil_print_operand(out, &i->u.handles); - break; - case LIT_Ld: - fprintf(out, "ld "); - lil_print_variable(out, &(i->u.ldst.operand.val.var)); - fprintf(out, ","); - lil_print_address(out, i); - if (i->u.ldst.extend==LLX_Sign) fprintf(out, ",sx"); - if (i->u.ldst.extend==LLX_Zero) fprintf(out, ",zx"); - break; - case LIT_St: - fprintf(out, "st "); - lil_print_address(out, i); - fprintf(out, ","); - lil_print_operand(out, &(i->u.ldst.operand)); - break; - case LIT_Inc: - fprintf(out, "inc "); - lil_print_address(out, i); - break; - case LIT_Cas: - fprintf(out, "cas "); - lil_print_address(out, i); - fprintf(out, "="); - lil_print_operand(out, &(i->u.ldst.compare)); - fprintf(out, ","); - lil_print_operand(out, &(i->u.ldst.operand)); - fprintf(out, ",%s", i->u.ldst.l); - break; - case LIT_J: - fprintf(out, "j %s", i->u.j); - break; - case LIT_Jc: - fprintf(out, "jc "); - switch (i->u.jc.c.tag) { - case LP_IsZero: - lil_print_operand(out, &(i->u.jc.c.o1)); - fprintf(out, "=0"); - break; - case LP_IsNonzero: - lil_print_operand(out, &(i->u.jc.c.o1)); - fprintf(out, "!=0"); - break; - case LP_Eq: - lil_print_operand(out, &(i->u.jc.c.o1)); - fprintf(out, "="); - lil_print_operand(out, &(i->u.jc.c.o2)); - break; - case LP_Ne: - lil_print_operand(out, &(i->u.jc.c.o1)); - fprintf(out, "!="); - lil_print_operand(out, &(i->u.jc.c.o2)); - break; - case LP_Le: - lil_print_operand(out, &(i->u.jc.c.o1)); - fprintf(out, "<="); - lil_print_operand(out, &(i->u.jc.c.o2)); - break; - case LP_Lt: - lil_print_operand(out, &(i->u.jc.c.o1)); - fprintf(out, "<"); - lil_print_operand(out, &(i->u.jc.c.o2)); - break; - case LP_Ule: - lil_print_operand(out, &(i->u.jc.c.o1)); - fprintf(out, " <=u "); - lil_print_operand(out, &(i->u.jc.c.o2)); - break; - case LP_Ult: - lil_print_operand(out, &(i->u.jc.c.o1)); - fprintf(out, " u.jc.c.o2)); - break; - default: ASSERT(0, "Unknown predicate"); - } - fprintf(out, ",%s", i->u.jc.l); - break; - case LIT_Out: - fprintf(out, "out "); - lil_print_sig(out, &(i->u.out)); - break; - case LIT_In2Out: - fprintf(out, "in2out "); - lil_print_cc(out, i->u.out.cc); - fprintf(out, ":"); - lil_print_type(out, i->u.out.ret_type); - break; - case LIT_Call: - switch (i->u.call.k) { - case LCK_Call: - fprintf(out, "call "); - break; - case LCK_CallNoRet: - fprintf(out, "call.noret "); - break; - case LCK_TailCall: - fprintf(out, "tailcall "); - break; - default: ASSERT(0, "Unknown call kind"); - } - lil_print_operand(out, &(i->u.call.target)); - break; - case LIT_Ret: - fprintf(out, "ret"); - break; - case LIT_PushM2N: - fprintf(out, "push_m2n %p, frame_type= %p", i->u.push_m2n.method, i->u.push_m2n.current_frame_type); - if (i->u.push_m2n.handles) fprintf(out, ",handles"); - break; - case LIT_M2NSaveAll: - fprintf(out, "m2n_save_all"); - break; - case LIT_PopM2N: - fprintf(out, "pop_m2n"); - break; - case LIT_Print: - fprintf(out, "print %p, ", i->u.print.str); - lil_print_operand(out, &i->u.print.arg); - fprintf(out, "\n"); - break; - default: - ASSERT(0, "Unknown instruction tag"); - }; - fprintf(out, ";\n"); - fflush(out); -} - -void lil_print_entry(FILE* out, LilCodeStub* cs) -{ - fprintf(out, "entry %d:", cs->num_std_places); - lil_print_sig(out, &(cs->sig)); - fprintf(out, ";\n"); - fflush(out); -} - -void lil_print_code_stub(FILE* out, LilCodeStub* cs) -{ - assert(cs); - lil_print_entry(out, cs); - for(LilInstruction* i=cs->is; i; i=i->next) - lil_print_instruction(out, i); -} - -//////////////////////////////////////////////////////////////////////////////////// -// Basic Blocks - -// private constructor; create BBs by calling init_fg() -LilBb::LilBb(LilCodeStub *_cs, LilInstruction *_start, - LilInstructionContext* ctxt_at_start): - cs(_cs), start(_start), end(NULL), - ctxt(NULL), - fallthru(NULL), - branch_target(NULL), - num_pred(0), - next(NULL), id(-1) -{ - // add this to the end of the stub's BB list - if (cs->bb_list_head == NULL) - cs->bb_list_head = this; - else { - LilBb* last_bb = cs->bb_list_head; - while (last_bb->next != NULL) - last_bb = last_bb->next; - last_bb->next = this; - } - - // store a copy of the current context in ctxt - assert(ctxt_at_start != NULL); - ctxt = lil_new_context(cs); - lil_copy_context(cs, ctxt_at_start, ctxt); -} // LilBb::LilBb - -// private operator new; create BBs using new_bb() only -void* LilBb::operator new(size_t sz, tl::MemoryPool& m) { - return m.alloc(sz); -} // LilBb::operator new - -int LilBb::get_id() { - return id; -} // LilBb::get_id - -// sets the last instruction of the BB -void LilBb::set_last(LilInstruction *i) { - // can't set the last instruction twice! - assert(end == NULL); - end = i; -} // LilBb::set_last - -// gets the first instruction -LilInstruction* LilBb::get_first() { - return start; -} // LilBb::get_first - -// gets the last instruction -LilInstruction* LilBb::get_last() { - return end; -} // LilBb::get_last - -LilInstructionContext* LilBb::get_context() { - return ctxt; -} // LilBb::get_context - -// does this bb contain instruction i? -bool LilBb::contains(LilInstruction *i) { - for (LilInstruction* j = start; j != NULL; j++) { - if (j == i) - return true; - if (j == end) - break; - } - return false; -} // LilBb::contains - -// get the label of this BB; NULL if no label exists -LilLabel LilBb::get_label() { - if (start->tag == LIT_Label) - return start->u.label.l; - return NULL; -} // LilBb::get_label - -// set a fallthrough successor to this bb -void LilBb::set_fallthru(LilBb *succ) { - fallthru = succ; - assert(succ->num_pred < MAX_BB_PRED); - succ->pred[succ->num_pred++] = this; -} // LilBb::set_fallthru - -// set a branch-target successor to this bb -void LilBb::set_branch_target(LilBb *succ) { - branch_target = succ; - assert(succ->num_pred < MAX_BB_PRED); - succ->pred[succ->num_pred++] = this; -} // LilBb::set_branch_target - -// get the fallthrough and branch target successors; -// either of them can be NULL if they don't exist -LilBb* LilBb::get_fallthru() { - return fallthru; -} // LilBb::get_fallthru - -LilBb* LilBb::get_branch_target() { - return branch_target; -} // LilBb::get_branch_target - - -// gets the i'th predecessor (NULL if i >= num_pred) -LilBb *LilBb::get_pred(unsigned i) { - return (i < num_pred) ? pred[i] : NULL; -} // LilBb::get_pred - -// gets the next BB in the list -LilBb* LilBb::get_next() { - return next; -} // LilBb::get_next - -// returns whether this BB ends in a return instruction -// (tailcall implies return!) -bool LilBb::is_ret() { - return (end != NULL && - (end->tag == LIT_Ret || - (end->tag == LIT_Call && end->u.call.k == LCK_TailCall))); -} - - -// true if this BB contains calls (which means it may throw exceptions) -bool LilBb::does_calls() { - for (LilInstruction *i=start; i != NULL; i = i->next) { - if (i->tag == LIT_Call) - return true; - if (i == end) - break; - } - return false; -} - - -// true if this BB ends with a call.noret (which means it is probably -// cold code) -bool LilBb::does_call_noret() { - return (end != NULL && end->tag == LIT_Call && - end->u.call.k == LCK_CallNoRet); -} - -// find a BB with the specified label -// NULL if no such bb exists -LilBb* LilBb::get_by_label(LilCodeStub *cs, LilLabel l) { - assert(l != NULL); - LilBb *bb = cs->bb_list_head; - - while (bb != NULL && - (bb->get_label() == NULL || strcmp(bb->get_label(), l))) - bb = bb->next; - return bb; -} // LilBb::get_by_label - -// find a BB which contains the specified instruction -// NULL if no such BB exists -LilBb* LilBb::get_by_instruction(LilCodeStub *cs, LilInstruction *i) { - LilBb* bb = cs->bb_list_head; - - while (bb != NULL && !bb->contains(i)) - bb = bb->next; - - return bb; -} // LilBb::get_by_instruction - -// print a BB to a stream -// (does not print the BB's instructions) -void LilBb::print(FILE* out) { - fprintf(out, "-- BB %d ", id); - - // print predecessors - fprintf(out, "(pred:"); - if (num_pred == 0) - fprintf(out, " none)"); - else { - for (unsigned i=0; iid); - fprintf(out, ")"); - } - - // print successors - fprintf(out, " (succ:"); - if (fallthru != NULL) - fprintf(out, " %d", fallthru->id); - if (branch_target != NULL) - fprintf(out, " %d", branch_target->id); - if (fallthru == NULL && branch_target == NULL) - fprintf(out, " none"); - fprintf(out, ")"); - - // print label - if (get_label() != NULL) - fprintf(out, " (label: %s)", get_label()); - - fprintf(out, " --\n"); - fflush(out); -} // void LilBb::print - - -// initializes the flowgraph by creating a list of BBs -// BBs in the list appear in the same order as they appear in the source code -void LilBb::init_fg(LilCodeStub *cs) { - LilInstructionIterator it(cs, true); - - LilInstruction *prev_inst = NULL; // previous instruction - LilBb* cur_bb = NULL; // current BB - - while (!it.at_end()) { - LilInstruction* inst = it.get_current(); - LilInstructionContext* ctxt = it.get_context(); - - if (inst->tag == LIT_Label || cur_bb == NULL) { - // close old bb, if it exists - if (cur_bb != NULL) { - assert(prev_inst != NULL); - cur_bb->set_last(prev_inst); - } - - // create new bb - cur_bb = new(*cs->my_memory) LilBb(cs, inst, ctxt); - cur_bb->id = cs->num_bbs++; - } - - // check if the BB should end here - if (inst->tag == LIT_J || - inst->tag == LIT_Jc || - inst->tag == LIT_Cas || - inst->tag == LIT_Ret || - (inst->tag == LIT_Call && inst->u.call.k != LCK_Call)) { - cur_bb->set_last(inst); - cur_bb = NULL; // so that the next inst will start a new one - } - - // advance pointers - prev_inst = inst; - it.goto_next(); - } - - // close the last BB - if (cur_bb != NULL) - cur_bb->set_last(prev_inst); - - // set up the successor / predecessor relations - for (cur_bb = cs->bb_list_head; cur_bb != NULL; cur_bb = cur_bb->next) { - LilInstruction *last_i = cur_bb->end; - assert(last_i != NULL); - if (last_i->tag == LIT_J) { - LilBb* succ = get_by_label(cs, last_i->u.j); - assert(succ != NULL); - cur_bb->set_branch_target(succ); - continue; // don't set fallthru - } - if (last_i->tag == LIT_Ret || - (last_i->tag == LIT_Call && last_i->u.call.k != LCK_Call)) { - continue; // terminal inst; don't set fallthru - } - if (last_i->tag == LIT_Jc) { - LilBb* succ = get_by_label(cs, last_i->u.jc.l); - assert(succ != NULL); - cur_bb->set_branch_target(succ); - } - if (last_i->tag == LIT_Cas) { - LilBb* succ = get_by_label(cs, last_i->u.ldst.l); - assert(succ); - cur_bb->set_branch_target(succ); - } - - // set fallthru - assert(cur_bb->next != NULL); - cur_bb->set_fallthru(cur_bb->next); - } -} // LilBb::init_fg - - -// EOF: lil.cpp diff --git a/vm/port/src/lil/lil_code_generator.cpp b/vm/port/src/lil/lil_code_generator.cpp deleted file mode 100644 index 9a238db..0000000 --- a/vm/port/src/lil/lil_code_generator.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#include "nogc.h" -#include "jvmti_direct.h" -#include "environment.h" -#include "compile.h" - -#include "jit_intf.h" -#include "lil.h" -#include "lil_code_generator.h" - -#ifdef _IA32_ -#include "lil_code_generator_ia32.h" -#elif _EM64T_ -#include "lil_code_generator_em64t.h" -#elif _IPF_ -#include "lil_code_generator_ipf.h" -#endif - - -LilCodeGenerator* LilCodeGenerator::get_platform() -{ -#ifdef _IA32_ - static LilCodeGeneratorIa32 cg; -#elif _EM64T_ - static LilCodeGeneratorEM64T cg; -#elif _IPF_ - static LilCodeGeneratorIpf cg; -#endif - return (LilCodeGenerator*)&cg; -} - -LilCodeGenerator::LilCodeGenerator() -{ -} - -NativeCodePtr LilCodeGenerator::compile(LilCodeStub* cs, PoolManager* code_pool) -{ - assert (code_pool); - size_t stub_size; - NativeCodePtr stub = compile_main(cs, &stub_size, code_pool); - lil_cs_set_code_size(cs, stub_size); - - compile_add_dynamic_generated_code_chunk("unknown", false, stub, stub_size); - - if(jvmti_should_report_event(JVMTI_EVENT_DYNAMIC_CODE_GENERATED)) - { - jvmti_send_dynamic_code_generated_event("unknown", stub, - (jint)stub_size); - } - - return stub; -} - - -NativeCodePtr LilCodeGenerator::allocate_memory(size_t size, PoolManager* code_pool) -{ - assert(code_pool); - NativeCodePtr buf = code_pool->alloc(size, DEFAULT_CODE_ALIGNMENT, CAA_Allocate); - - // Check for 16-byte alignment - assert((((POINTER_SIZE_INT)buf)&15)==0); - return buf; -} diff --git a/vm/port/src/lil/lil_code_generator_utils.cpp b/vm/port/src/lil/lil_code_generator_utils.cpp deleted file mode 100644 index 78ea911..0000000 --- a/vm/port/src/lil/lil_code_generator_utils.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Evgueni Brevnov, Ivan Volosyuk - * @version $Revision: 1.1.2.1.4.4 $ - */ - - -#include -#include - -#define LOG_DOMAIN "vm.helpers" -#include "cxxlog.h" - -#include "lil.h" -#include "lil_code_generator_utils.h" -#include "tl/memory_pool.h" - -LilCguLabelAddresses::LilCguLabelAddresses(tl::MemoryPool* mem, char * init_base): -first(NULL), my_mem(mem), base(init_base) {} - -void LilCguLabelAddresses::change_base(char * new_base) { - LilCguLabelAddress * cur_label_addr; - LilCguPatch * cur_patch; - for (cur_label_addr = first; cur_label_addr != NULL; cur_label_addr = cur_label_addr->next) { - if (cur_label_addr->base_relative) { - // all patch addresses are sensitive to the base address - // since label address is sensitive to the base as well - // we don't need to patch the code. just remember new addresses - cur_label_addr->addr += new_base - base; - for (cur_patch = cur_label_addr->patches; cur_patch != NULL; cur_patch = cur_patch->next) { - cur_patch->addr += new_base - base; - } - } else { - //need to patch the code - for (cur_patch = cur_label_addr->patches; cur_patch != NULL; cur_patch = cur_patch->next) { - cur_patch->addr += new_base - base; - apply_patch(cur_label_addr, cur_patch); - } - } - } - base = new_base; -} - -// when base_relative is true it means that label address should be recalculated if base is changed -void LilCguLabelAddresses::define_label(LilLabel l, void * code, bool base_relative) { - LilCguLabelAddress * cur = first; - while (cur != NULL) { - if (strcmp(cur->l, l) == 0) { - if (cur->addr == NULL) { - // not defined - cur->addr = (char *)code; - cur->base_relative = base_relative; - apply_patches(cur); - } -#ifndef NDEBUG - else { - // such label has already been defined - // check that they are consistent - assert(base_relative == cur->base_relative && cur->addr == code); - } -#endif - return; - } - cur = cur->next; - } - // need to create new label address - add_new_label_adress(l, code, base_relative); -} - -void LilCguLabelAddresses::add_patch_to_label(LilLabel l, void * patch_address, LilCguPatchType patch_type) { - LilCguLabelAddress * cur = first; - // try to find existing label address - while (cur != NULL && strcmp(cur->l, l) != 0) { - cur = cur->next; - } - - // create new label address if not found - if (!cur) { - add_new_label_adress(l, NULL, false); - cur = first; - } - - // add new patch - LilCguPatch * p = (LilCguPatch*)my_mem->alloc(sizeof(LilCguPatch)); - p->addr = (char *)patch_address; - p->type = patch_type; - p->next = cur->patches; - cur->patches = p; - - // apply patch if label defined - if (cur->addr != NULL) { - apply_patch(cur, p); - } -} - -void LilCguLabelAddresses::apply_patches(LilCguLabelAddress * label_adress) { - for(LilCguPatch * p = label_adress->patches; p != NULL; p = p->next) { - apply_patch(label_adress, p); - } -} - -void LilCguLabelAddresses::apply_patch(LilCguLabelAddress * label_adress, LilCguPatch * patch) { - int64 diff; - switch (patch->type) { - case LPT_Rel8: - diff = (int64)((char *)label_adress->addr - ((char *)patch->addr + 1)); - assert(diff == (int64)(int8)diff); - *(int8*)patch->addr = (int8)diff; - break; - case LPT_Rel32: - diff = (int64)((char *)label_adress->addr - (char *)((int32 *)patch->addr + 1)); - assert(diff == (int64)(int32)diff); - *(int32*)patch->addr = (int32)diff; - break; - case LPT_Abs32: - assert((POINTER_SIZE_INT)label_adress->addr <= 0xFFFFffff); - *(int32*)patch->addr = (int32)(POINTER_SIZE_INT)label_adress->addr; - break; - default: - ASSERT(0, "Unknown patch type"); - } -} - -void LilCguLabelAddresses::add_new_label_adress(LilLabel l, void * code, bool base_relative) { - LilCguLabelAddress * cur = (LilCguLabelAddress*)my_mem->alloc(sizeof(LilCguLabelAddress)); - cur->l = l; - cur->addr = (char *)code; - cur->base_relative = base_relative; - cur->patches = NULL; - cur->next = first; - first = cur; -} diff --git a/vm/port/src/lil/pim/m2n.cpp b/vm/port/src/lil/pim/m2n.cpp deleted file mode 100644 index 172c82f..0000000 --- a/vm/port/src/lil/pim/m2n.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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. - */ -/** - * @author Evgueni Brevnov - * @version $Revision: 1.1 $ - */ - -#include "m2n.h" - -const uint32 FRAME_UNKNOWN = 0x00; -const uint32 FRAME_NON_UNWINDABLE = 0x80; -const uint32 FRAME_JNI = 0x01 | FRAME_NON_UNWINDABLE; -const uint32 FRAME_COMPILATION = 0x02 | FRAME_NON_UNWINDABLE; -const uint32 FRAME_UNPOPABLE = 0x0000; -const uint32 FRAME_POPABLE = 0x0100; -const uint32 FRAME_POP_NOW = 0x0200; -const uint32 FRAME_POP_DONE = FRAME_POPABLE | FRAME_POP_NOW; -const uint32 FRAME_POP_MASK = 0x0700; -const uint32 FRAME_SAFE_POINT = 0x0800; -const uint32 FRAME_MODIFIED_STACK = 0x1000; diff --git a/vm/port/src/lil/pim/stack_iterator.cpp b/vm/port/src/lil/pim/stack_iterator.cpp deleted file mode 100644 index 5d2d230..0000000 --- a/vm/port/src/lil/pim/stack_iterator.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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. - */ -/** - * @author Intel, Pavel Afremov - * @version $Revision: 1.1 $ - */ - - -#include "interpreter.h" -#include "jit_intf_cpp.h" -#include "m2n.h" -#include "stack_iterator.h" -#include "cci.h" - -Method_Handle si_get_method(StackIterator* si) -{ - ASSERT_NO_INTERPRETER - CodeChunkInfo* cci = si_get_code_chunk_info(si); - if (cci) - return cci->get_method(); - else - return m2n_get_method(si_get_m2n(si)); -} - -uint32 si_get_inline_depth(StackIterator* si) -{ - // - // Here we assume that JIT data blocks can store only InlineInfo - // A better idea is to extend JIT_Data_Block with some type information - // Example: - // - // enum JIT_Data_Block_Type { InlineInfo, Empty } - // - // struct JIT_Data_Block { - // JIT_Data_Block *next; - // JIT_Data_Block_Type type; - // char bytes[1]; - // }; - // - // void *Method::allocate_JIT_data_block(size_t size, JIT *jit, JIT_Data_Block_Type) - // - - ASSERT_NO_INTERPRETER - CodeChunkInfo* cci = si_get_code_chunk_info(si); - if ( cci != NULL && cci->has_inline_info()) { - return cci->get_jit()->get_inline_depth( - cci->get_inline_info(), - // FIXME64: no support for large methods - (uint32)((POINTER_SIZE_INT)si_get_ip(si) - (POINTER_SIZE_INT)cci->get_code_block_addr())); - } - return 0; -} diff --git a/vm/vmcore/include/lil.h b/vm/vmcore/include/lil.h new file mode 100644 index 0000000..41e7d30 --- /dev/null +++ b/vm/vmcore/include/lil.h @@ -0,0 +1,618 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#ifndef _LIL_H_ +#define _LIL_H_ + +#include +#include "open/types.h" +#include "vm_core_types.h" +#include "tl/memory_pool.h" +#include "m2n.h" + +/* This file describes LIL - the architecture-independent language used to generate code stubs etc. + +Goals: +* Lightweight - i.e. small language, quality code generation possible with small simple algorithms +* Remove most architecture dependencies from code +* (Future) allow inlining of VM stubs into JITed code +* (Future) allow inlining of GC or Thread/Sync code via LIL +* (Possibly future) be the lowest ir of a simple JIT + +Design constaints: +* Keep language syntax LL(1) so that parsing can be done with a recursive descent parser +* Make single-pass register allocation possible +* Allow easy experimentation with calling conventions + +The basic unit of LIL is a code stub. It is intended to be the entry point of a function that can be +called by managed or native code, and executes in the presence of an activation frame. The frame consists +of up to six parts: inputs, standard places, an m2n frame, locals, outputs, and a return variable. Which +parts are present in the activation frame, and how big each part is, can vary across points in the code stub. + +A LIL code stub is an entry and a sequence of statements. The entry specifies the inputs and their types, the +number of standard places, the type of the return, and the calling convention (which determines where the inputs, +return address, and return goes, which registers are caller save vs callee save, and who pops the inputs; standard +places are determined by LIL and the platform, and are intended for trampolines). An entry can have an "arbitrary" +signature, meaning that the number and type of inputs and type of return is unknown (the stub must work for all +possibilities). After entry the declared number +of input variables are available, the declared number of standard places are available, there is no m2n frame, +there are no locals, there are no outputs, and there is no return variable. + +cs ::= entry i:cc:sig; ss +ss ::= s | s ss + +The statements are: + +s ::= :l; +| locals i; +| std_places i; +| alloc v,i; +| v=uop o; +| v=o op o; +| v=o; +| v=ts; +| handles=o; +| ld v,addr(,zx|,sx)?; +| st addr,o; +| inc addr; +| cas addr=o,o,l; +| j l; +| jc cond,l; +| out cc:sig; +| in2out cc:RT; +| calli o; +| ret; +| push_m2n i oh; +| m2n_save_all; +| pop_m2n; +| print str, o; + +uop ::= neg | not | sx1 | sx2 | sx4 | zx1 | zx2 | zx4 | ... +op ::= + | - | * | & | << | ... + +addr ::= [ov osv i :T ackrel] +ov ::= | v+ +osv ::= | 1*v+ | 2*v+ | 4*v+ | 8*v+ +acqrel ::= | ,acquire | ,release + +cond ::= v=0 | v!=0 | o=o | o!=o | o-1 + int id; + // private constructor; create BBs by calling init_fg() + LilBb(LilCodeStub *_cs, LilInstruction *_start, + LilInstructionContext* ctxt_at_start); + + // private operator new; create BBs using new_bb() only + void* operator new(size_t sz, tl::MemoryPool& m); + //MVM + //add empty destructor to avoid warning + void operator delete(void * UNREF obj, tl::MemoryPool& UNREF m){ + //do nothing + }; + +public: + + // the only way to create new BBs; create a whole FG for a stub + VMEXPORT static void init_fg(LilCodeStub*); + + // sets the last instruction of the BB + VMEXPORT void set_last(LilInstruction *i); + + // gets the first instruction + VMEXPORT LilInstruction* get_first(); + // gets the last instruction + VMEXPORT LilInstruction* get_last(); + + // get the context right before the start of the BB + VMEXPORT LilInstructionContext* get_context(); + + // does this bb contain instruction i? + VMEXPORT bool contains(LilInstruction *i); + + // get the label of this BB; NULL if no label exists + VMEXPORT LilLabel get_label(); + + // set a fallthrough successor to this bb + VMEXPORT void set_fallthru(LilBb *); + // set a branch-target successor to this bb + VMEXPORT void set_branch_target(LilBb *); + + // get the fallthrough and branch target successors; + // either of them can be NULL if they don't exist + VMEXPORT LilBb* get_fallthru(); + VMEXPORT LilBb* get_branch_target(); + + // gets the i'th predecessor (NULL if i >= num_pred) + VMEXPORT LilBb *get_pred(unsigned i); + + // gets the next BB in the list + VMEXPORT LilBb *get_next(); + + // returns whether this BB ends in a return instruction + // (tailcall implies return!) + VMEXPORT bool is_ret(); + + // the id of this BB + VMEXPORT int get_id(); + + // true if this BB contains calls (which means it may throw exceptions) + VMEXPORT bool does_calls(); + + // true if this BB ends with a call.noret (which means it is probably cold code) + VMEXPORT bool does_call_noret(); + + // find a BB with the specified label; NULL if no such bb exists + VMEXPORT static LilBb* get_by_label(LilCodeStub *cs, LilLabel l); + + // find a BB which contains the specified instruction; NULL if no such BB exists + VMEXPORT static LilBb* get_by_instruction(LilCodeStub *cs, LilInstruction *i); + + // print a BB to a stream (does not print the BB's instructions) + VMEXPORT void print(FILE* out); +}; + + + +// Iterate over the instructions in a code stub +class LilInstructionIterator { + public: + // Create an iterator from a code stub, if track_contexts then track the context prior to each instruction + VMEXPORT LilInstructionIterator(LilCodeStub*, bool track_contexts); + + // Create an iterator for a BB in a code stub. + // NOTE: track_contexts is currently NOT supported for this type of iterator + VMEXPORT LilInstructionIterator(LilCodeStub*, LilBb*, bool track_contexts); + + // Is iterator past the end of the instructions? + VMEXPORT bool at_end(); + // Return current instruction + VMEXPORT LilInstruction* get_current(); + // Return context prior to current instruction, must have created iterator to track contexts + VMEXPORT LilInstructionContext* get_context(); + // Goto the next instruction + VMEXPORT void goto_next(); + + private: + LilCodeStub* cs; + LilBb *bb; // NULL, unless this is an iterator for a BB + LilInstruction* cur; + bool track_ctxt; + LilInstructionContext* ctxt; +}; + + + +// Visitor pattern for instructions +class LilInstructionVisitor { + protected: + VMEXPORT LilInstructionVisitor(); + + public: + virtual void label(LilLabel) = 0; + virtual void locals(unsigned) = 0; + virtual void std_places(unsigned) = 0; + virtual void alloc(LilVariable*, unsigned) = 0; + 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, + LilOperand* cmp, LilOperand* src, LilLabel) = 0; + virtual void j(LilLabel) = 0; + virtual void jc(LilPredicate, LilOperand*, LilOperand*, LilLabel) = 0; + virtual void out(LilSig*) = 0; + virtual void in2out(LilSig*) = 0; + virtual void call(LilOperand*, LilCallKind) = 0; + virtual void ret() = 0; + 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 print(char *, LilOperand *) = 0; +}; + + +// LilInstructionVisitor_Default provides empty implementations for the +// abstract functions in LilInstructionVisitor. It is a convenient base +// class for scanners that need to act only on a few instructions. +class LilInstructionVisitor_Default : public LilInstructionVisitor { + protected: + LilInstructionVisitor_Default(): + LilInstructionVisitor() {} + + public: + void label(LilLabel) {} + void locals(unsigned) {} + void std_places(unsigned) {} + void alloc(LilVariable*, unsigned) {} + 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, + LilOperand* UNREF cmp, LilOperand* UNREF src, LilLabel) {} + void j(LilLabel) {} + void jc(LilPredicate, LilOperand*, LilOperand*, LilLabel) {} + void out(LilSig*) {} + void in2out(LilSig*) {} + void call(LilOperand*, LilCallKind) {} + void ret() {} + void push_m2n(Method_Handle UNREF method, frame_type UNREF current_frame_type, bool UNREF handles) {} + void m2n_save_all() {} + void pop_m2n() {} + void print(char *, LilOperand *) {} +}; + + +// Visit instruction using a visitor +VMEXPORT void lil_visit_instruction(LilInstruction*, LilInstructionVisitor*); + + +// Return variable's kind +VMEXPORT LilVariableKind lil_variable_get_kind(LilVariable* v); +// Return variable's index +VMEXPORT unsigned lil_variable_get_index(LilVariable* v); +// Are two variables the same +VMEXPORT bool lil_variable_is_equal(LilVariable* v1, LilVariable* v2); + +// Is operand an immediate? +VMEXPORT bool lil_operand_is_immed(LilOperand* o); +// If operand is an immediate return the immediate value +VMEXPORT POINTER_SIZE_INT lil_operand_get_immed(LilOperand* o); +// If operand is not an immediate return the variable it represents +VMEXPORT LilVariable* lil_operand_get_variable(LilOperand* o); + +// Return a signature's calling convention +VMEXPORT LilCc lil_sig_get_cc(LilSig* sig); +// Return whether this signature is arbitrary +VMEXPORT bool lil_sig_is_arbitrary(LilSig *sig); +// Return the number of arguments for a signature (0 if arbitrary) +VMEXPORT unsigned lil_sig_get_num_args(LilSig* sig); +// Return the num-th argument type of a signature (zero based) (undefined if signature is arbitrary) +VMEXPORT LilType lil_sig_get_arg_type(LilSig* sig, unsigned num); +// Return the return type of a signature +VMEXPORT LilType lil_sig_get_ret_type(LilSig* sig); + +// Is an operation binary? +VMEXPORT bool lil_operation_is_binary(enum LilOperation op); +// Is a predicate binary? +VMEXPORT bool lil_predicate_is_binary(enum LilPredicate c); +// Is a predicate signed? +VMEXPORT bool lil_predicate_is_signed(LilPredicate c); + +//*** Printers + +// Print a type to a stream +VMEXPORT void lil_print_type(FILE*, LilType); +// Print a signature to a stream +VMEXPORT void lil_print_sig(FILE*, LilSig*); +// Print an instruction to a stream +VMEXPORT void lil_print_instruction(FILE*, LilInstruction* i); +// Print a code stub to a stream +VMEXPORT void lil_print_code_stub(FILE*, LilCodeStub* cs); +// Print just the entry of a code stub to a stream +VMEXPORT void lil_print_entry(FILE*, LilCodeStub* cs); + +//*** Freers + +// Return all resources associated with a code stub +// Post: code stub no longer usable +VMEXPORT void lil_free_code_stub(LilCodeStub* cs); + +//*** Function pointer utilities + +VMEXPORT GenericFunctionPointer lil_npc_to_fp(NativeCodePtr); + +#endif // _LIL_H_ diff --git a/vm/vmcore/include/lil_code_generator.h b/vm/vmcore/include/lil_code_generator.h new file mode 100644 index 0000000..e0a6e78 --- /dev/null +++ b/vm/vmcore/include/lil_code_generator.h @@ -0,0 +1,70 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#ifndef _LIL_CODE_GENERATOR_H_ +#define _LIL_CODE_GENERATOR_H_ + +#include "lil.h" +#include "vm_core_types.h" +#include "environment.h" +#include "mem_alloc.h" + +// This is an abstract base case for LIL code generators +// Subclasses compile LIL into native code for a particular +// architecture + +// Note that this is a compiler algorithm abstraction not +// a compilation process abstraction. Subclasses should +// not store state about any particular compilation, only +// options that configure the compilation. + +class LilCodeGenerator { +public: + // Return the code generator for the current platform + static LilCodeGenerator* get_platform(); + + // Compile LIL code stub to native code and return it + // The stub_name is for vtune support + // Dump an ascii version of the compiled stub to stdout if dump_stub + // If cs_stats is nonnull add the number of bytes of the compiled code to *cs_stats + NativeCodePtr compile(LilCodeStub* cs, PoolManager* code_pool = + VM_Global_State::loader_env->GlobalCodeMemoryManager); + +protected: + LilCodeGenerator(); + + // allocates a chunk of memory for a LIL stub; the user-provided function + // compile_main() should call this function instead of allocating memory + // directly. + NativeCodePtr allocate_memory(size_t, PoolManager*); + + // generates compiled code for a LIL stub, and returns its address. The + // size of the compiled stub is placed in stub_size. Called by the + // compile() function to do most of the work. + // + // Each subclass of LilCodeGenerator should provide a platform-dependent + // implementation of compile_main(). The memory area that holds the + // compiled code should be allocated by calling allocate_memory(). + virtual NativeCodePtr compile_main(LilCodeStub* cs, size_t* stub_size, PoolManager* code_pool) = 0; +}; + +#endif // _LIL_CODE_GENERATOR_H_ diff --git a/vm/vmcore/include/lil_code_generator_utils.h b/vm/vmcore/include/lil_code_generator_utils.h new file mode 100644 index 0000000..b1cbe65 --- /dev/null +++ b/vm/vmcore/include/lil_code_generator_utils.h @@ -0,0 +1,82 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#ifndef _LIL_CODE_GENERATOR_UTILS_H_ +#define _LIL_CODE_GENERATOR_UTILS_H_ + +#include "lil.h" +#include "vm_core_types.h" + +// This module provides some common utilities for generating native code from LIL code stubs + +//*** The following is for managing label to address translation and back patching + +// Patch types have to be the union of the various cases of the various architectures, +// so unfortunately are somewhat platform specific. +typedef enum LilCguPatchType { + LPT_Rel8, LPT_Rel32, LPT_Abs32 +} LilCguPatchType; + +typedef struct LilCguPatch { + char * addr; + LilCguPatchType type; + LilCguPatch * next; +} LilCguPatch; + +typedef struct LilCguLabelAddress { + LilLabel l; + char * addr; + bool base_relative; + LilCguPatch * patches; + LilCguLabelAddress * next; +}LilCguLabelAddress; + + +// This class mantains a mapping between labels and addresses, and ensures the references to +// labels eventually point to those labels. +// Clients should call define_label to set a label's address, and patch_to_label to ensure references +// correct point to a label; the class will make sure that a reference points to its label once both +// functions have been called. +class LilCguLabelAddresses { +public: + // Create a label to address mapping with back patching + LilCguLabelAddresses(tl::MemoryPool*, char * b); + // + void change_base(char * new_base); + // Label l should have addres code + void define_label(LilLabel l, void * code, bool base_relative); + // The contents of address patch_address should point to label l according to patch_type + void add_patch_to_label(LilLabel l, void * patch_address, LilCguPatchType patch_type); + +private: + void add_new_label_adress(LilLabel l, void * code, bool base_relative); + // Apply all patches associated with the current label + void apply_patches(LilCguLabelAddress * label_adress); + // Actually apply a patch + void apply_patch(LilCguLabelAddress * label_adress, LilCguPatch * patch); + + LilCguLabelAddress * first; + tl::MemoryPool * my_mem; + char * base; +}; + +#endif // _LIL_CODE_GENERATOR_UTILS_H_ diff --git a/vm/vmcore/src/lil/em64t/include/lil_code_generator_em64t.h b/vm/vmcore/src/lil/em64t/include/lil_code_generator_em64t.h new file mode 100644 index 0000000..7b676f5 --- /dev/null +++ b/vm/vmcore/src/lil/em64t/include/lil_code_generator_em64t.h @@ -0,0 +1,742 @@ +/* + * 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. + */ + +/** + * @author Evgueni Brevnov + * @version $Revision$ + */ + +/** + * Stack frame layout created by LIL CG on EM64T + * + * |--------------------------| + * | Extra inputs | + * |--------------------------| <--- previouse stack frame bottom + * | Return ip | + * |==========================| <--- current stack frame top + * | M2N frame | callee saved | + * |--------------------------| + * | GR inputs save area | + * |--------------------------| + * | FR inputs save area | + * |--------------------------| + * | Dynamicly allocated area | + * | (includes stack padding) | + * |--------------------------| + * | Extra outputs | + * |==========================| <--- current stack frame bottom + * + * Note: + * EM64T architecture requires stack frame bottom address + * to be aligned on 16 byte boundary (rsp % 16 == 0) + * + * Register usage: + * r12-r15 are used for lil local variables (l0-l3) + * r10-r11 are used for lil standard places (sp0-sp1) + */ + +#ifndef _LIL_CODE_GENERATOR_EM64T_ +#define _LIL_CODE_GENERATOR_EM64T_ + +#include "lil.h" +#include "lil_code_generator.h" +#include "encoder.h" + + +/** + * rounds up an integer value to the closest multiple of 8 + */ +inline unsigned align_8(unsigned n) { + return (n + 0x7) & ~0x7; +} + +/** + * rounds up an integer value to the closest multiple of 16 + */ +inline unsigned align_16(unsigned n) { + return (n + 0xF) & ~0xF; +} + +/** +* an enum indicating a variable's location: in a register class or on the +* stack (no LIL variable is ever on the heap!) +*/ +enum LcgEM64TLocKind { + LLK_Gr, // 64-bit general purpose registers + LLK_Fr, // 128-bit xmm registers + LLK_GStk, // memory stack which holds gp value + LLK_FStk // memory stack which holds fp value +}; + +class LcgEM64TContext: public LilInstructionVisitor { + +public: +#ifdef _WIN64 + // maximum number of GR reserved for returns + static const unsigned MAX_GR_RETURNS = 1; + // maximum number of GR reserved for outputs/inputs + static const unsigned MAX_GR_OUTPUTS = 4; + // maximum number of locals that can be placed in GR + static const unsigned MAX_GR_LOCALS = 8; + // maximum number of stand places + static const unsigned MAX_STD_PLACES = 2; + + // maximum number of FR reserved for returns + static const unsigned MAX_FR_RETURNS = 1; + // maximum number of FR reserved for outputs/inputs + static const unsigned MAX_FR_OUTPUTS = 4; + // maximum number of temporary XMM registers + static const unsigned MAX_FR_LOCALS = 10; + // maximum number of temporary XMM registers + static const unsigned MAX_FR_TEMPORARY = 2; +#else + // maximum number of GR reserved for returns + static const unsigned MAX_GR_RETURNS = 2; + // maximum number of GR reserved for outputs/inputs + static const unsigned MAX_GR_OUTPUTS = 6; + // maximum number of locals that can be placed in GR + static const unsigned MAX_GR_LOCALS = 6; + // maximum number of stand places + static const unsigned MAX_STD_PLACES = 2; + + // maximum number of FR reserved for returns + static const unsigned MAX_FR_RETURNS = 2; + // maximum number of FR reserved for outputs/inputs + static const unsigned MAX_FR_OUTPUTS = 8; + // maximum number of temporary XMM registers + static const unsigned MAX_FR_LOCALS = 8; + // maximum number of temporary XMM registers + static const unsigned MAX_FR_TEMPORARY = 0; +#endif + + // size of GR in bytes + // TODO: Think about using GR_STACK_SIZE + static const unsigned GR_SIZE = 8; + // size of FR in bytes + // TODO: Think about using FR_STACK_SIZE + static const unsigned FR_SIZE = 8; + + // offsets for the REG_MAP array + static const unsigned STD_PLACES_OFFSET = 0; + static const unsigned GR_LOCALS_OFFSET = STD_PLACES_OFFSET + MAX_STD_PLACES; + static const unsigned GR_OUTPUTS_OFFSET = GR_LOCALS_OFFSET + MAX_GR_LOCALS; + static const unsigned GR_RETURNS_OFFSET = GR_OUTPUTS_OFFSET + MAX_GR_OUTPUTS; + static const unsigned RSP_OFFSET = GR_RETURNS_OFFSET + MAX_GR_RETURNS; + + // offsets for the XMM_REG_MAP array + static const unsigned FR_OUTPUTS_OFFSET = 0; + static const unsigned FR_RETURNS_OFFSET = FR_OUTPUTS_OFFSET + MAX_FR_OUTPUTS; + static const unsigned FR_TEMPORARY_OFFSET = FR_RETURNS_OFFSET + MAX_FR_RETURNS; + static const unsigned FR_LOCALS_OFFSET = FR_TEMPORARY_OFFSET + MAX_FR_TEMPORARY; + +private: + + LilCodeStub * cs; // the code stub + tl::MemoryPool & mem; // a memory manager + LilInstructionIterator iter; // instruction iterator + + unsigned n_inputs; // total number of inputs + unsigned n_gr_inputs; // total number of GRs reserved for inputs +#ifdef _WIN64 + // Windows x64 has 4 slots for both integer and float inputs +#define n_fr_inputs n_gr_inputs +#else + unsigned n_fr_inputs; // total number of FRs reserved for inputs +#endif + + unsigned n_outputs; // total number of outputs + unsigned n_gr_outputs; // total number of GRs reserved for outputs +#ifdef _WIN64 + // Windows x64 has 4 slots for both integer and float inputs +#define n_fr_outputs n_gr_outputs +#else + unsigned n_fr_outputs; // total number of FRs reserved for outputs +#endif + + /// Number of GR registers currently allocated for temporary needs. + unsigned m_tmp_grs_used; + /// Number of XMM registers currently allocated for temporary needs. + unsigned m_tmp_xmms_used; + + unsigned stk_m2n_size; // size reserved for the m2n frame + unsigned stk_input_gr_save_size; // size reserved for saving GR inputs + unsigned stk_input_fr_save_size; // size reserved for saving FR inputs + unsigned stk_alloc_size; // size of allocatable memory on the stack + unsigned stk_output_size; // bytes needed for outgoing params on the stack + unsigned stk_size; // total size of the memory stack frame (in bytes) + + Method_Handle m2n_method; // method handle of the m2n frame + frame_type m2n_frame_type; // m2n frame type + + bool m2n_handles; // true if m2n contains local handles + bool does_normal_calls; // true if the stub contains "normal" calls + bool does_tail_calls; // true if the stub contains tail calls + bool calls_unmanaged_code; // true if the stub calls calls code with a calling convention other than managed + bool has_m2n; // true if the stub contains push_m2n/pop_m2n instructions + bool save_inputs; // true if inputs are accessed after a normal call + +public: + + + LcgEM64TContext(LilCodeStub * stub, tl::MemoryPool & m); + +#ifdef _WIN64 + /** + * returns general purpose register associated with given index + * this association is used across whole lil code generator + */ + static const R_Opnd & get_reg_from_map(unsigned index) { + static const R_Opnd * REG_MAP[] = { + // std places (scratched) + &r10_opnd, &r11_opnd, + // GR locals (calee-saved) + &r12_opnd, &r13_opnd, &r14_opnd, &r15_opnd, + &rdi_opnd, &rsi_opnd, &rbp_opnd, &rbx_opnd, + // gr inputs/outputs (scratched) + &rcx_opnd, &rdx_opnd, &r8_opnd, &r9_opnd, + // gr returns (scratched) + &rax_opnd, + // rsp + &rsp_opnd + }; + return *REG_MAP[index]; + } + + /** + * returns xmm register associated with given index + * this association is used across whole lil code generator + */ + static const XMM_Opnd & get_xmm_reg_from_map(unsigned index) { + static const XMM_Opnd * XMM_REG_MAP[] = { + // fr inputs/outputs (scratched) + &xmm0_opnd, &xmm1_opnd, &xmm2_opnd, &xmm3_opnd, + // fr returns (scratched) + &xmm0_opnd, + // temporary xmm registers (scratched) + &xmm4_opnd, &xmm5_opnd, + // locals xmm registers + &xmm6_opnd, &xmm7_opnd, &xmm8_opnd, &xmm9_opnd, + &xmm10_opnd, &xmm11_opnd, &xmm12_opnd, &xmm13_opnd, + &xmm14_opnd, &xmm15_opnd + }; + return *XMM_REG_MAP[index]; + } + + /** + * an association between register number and index in the REG_MAP array + */ + static unsigned get_index_in_map(const Reg_No reg) { + static const unsigned INDEX_MAP[] = { + // rax_reg, rbx_reg, rcx_reg, + GR_RETURNS_OFFSET, GR_LOCALS_OFFSET + 7, GR_OUTPUTS_OFFSET + 0, + // rdx_reg, rdi_reg, rsi_reg, + GR_OUTPUTS_OFFSET + 1, GR_LOCALS_OFFSET + 4, GR_LOCALS_OFFSET + 5, + // rsp_reg, rbp_reg, r8_reg, + RSP_OFFSET, GR_LOCALS_OFFSET + 6, GR_OUTPUTS_OFFSET + 2, + // r9_reg, r10_reg, r11_reg, + GR_OUTPUTS_OFFSET + 3, STD_PLACES_OFFSET, STD_PLACES_OFFSET + 1, + // r12_reg, r13_reg, r14_reg, + GR_LOCALS_OFFSET, GR_LOCALS_OFFSET + 1, GR_LOCALS_OFFSET + 2, + // r15_reg, xmm0_reg, xmm1_reg, + GR_LOCALS_OFFSET + 3, FR_OUTPUTS_OFFSET, FR_OUTPUTS_OFFSET + 1, + // xmm2_reg, xmm3_reg, xmm4_reg, + FR_OUTPUTS_OFFSET + 2, FR_OUTPUTS_OFFSET + 3, FR_TEMPORARY_OFFSET, + // xmm5_reg, xmm6_reg, xmm7_reg, + FR_TEMPORARY_OFFSET + 1, FR_LOCALS_OFFSET, FR_LOCALS_OFFSET + 1, + // xmm8_reg, xmm9_reg, xmm10_reg, + FR_LOCALS_OFFSET + 2, FR_LOCALS_OFFSET + 3, FR_LOCALS_OFFSET + 4, + // xmm11_reg, xmm12_reg, xmm13_reg, + FR_LOCALS_OFFSET + 5, FR_LOCALS_OFFSET + 6, FR_LOCALS_OFFSET + 7, + // xmm14_reg, xmm15_reg + FR_LOCALS_OFFSET + 8, FR_LOCALS_OFFSET + 9 + }; + return INDEX_MAP[reg]; + } +#else + static const R_Opnd & get_reg_from_map(unsigned index) { + static const R_Opnd * REG_MAP[] = { + // std places (scratched) + &r10_opnd, &r11_opnd, + // GR locals (calee-saved) + &r12_opnd, &r13_opnd, &r14_opnd, &r15_opnd, &rbp_opnd, &rbx_opnd, + // gr inputs/outputs (scratched) + &rdi_opnd, &rsi_opnd, &rdx_opnd, &rcx_opnd, &r8_opnd, &r9_opnd, + // gr returns (scratched) + &rax_opnd, &rdx_opnd, + // rsp + &rsp_opnd + }; + return *REG_MAP[index]; + } + + /** + * returns xmm register associated with given index + * this association is used across whole lil code generator + */ + static const XMM_Opnd & get_xmm_reg_from_map(unsigned index) { + static const XMM_Opnd * XMM_REG_MAP[] = { + // fr inputs/outputs (scratched) + &xmm0_opnd, &xmm1_opnd, &xmm2_opnd, &xmm3_opnd, + &xmm4_opnd, &xmm5_opnd, &xmm6_opnd, &xmm7_opnd, + // fr returns (scratched) + &xmm0_opnd, &xmm1_opnd, + // temporary xmm registers (scratched) + &xmm8_opnd, &xmm9_opnd, &xmm10_opnd, &xmm11_opnd, + &xmm12_opnd, &xmm13_opnd, &xmm14_opnd, &xmm15_opnd + }; + return *XMM_REG_MAP[index]; + } + + /** + * an association between register number and index in the REG_MAP array + */ + static unsigned get_index_in_map(const Reg_No reg) { + static const unsigned INDEX_MAP[] = { + // rax_reg, rbx_reg, rcx_reg, + GR_RETURNS_OFFSET, GR_LOCALS_OFFSET + 5, GR_OUTPUTS_OFFSET + 3, + // rdx_reg, rdi_reg, rsi_reg, + GR_OUTPUTS_OFFSET + 2, GR_OUTPUTS_OFFSET, GR_OUTPUTS_OFFSET + 1, + // rsp_reg, rbp_reg, r8_reg, + RSP_OFFSET, GR_LOCALS_OFFSET + 4, GR_OUTPUTS_OFFSET + 4, + // r9_reg, r10_reg, r11_reg, + GR_OUTPUTS_OFFSET + 5, STD_PLACES_OFFSET, STD_PLACES_OFFSET + 1, + // r12_reg, r13_reg, r14_reg, + GR_LOCALS_OFFSET, GR_LOCALS_OFFSET + 1, GR_LOCALS_OFFSET + 2, + // r15_reg, xmm0_reg, xmm1_reg, + GR_LOCALS_OFFSET + 3, FR_OUTPUTS_OFFSET, FR_OUTPUTS_OFFSET + 1, + // xmm2_reg, xmm3_reg, xmm4_reg, + FR_OUTPUTS_OFFSET + 2, FR_OUTPUTS_OFFSET + 3, FR_OUTPUTS_OFFSET + 4, + // xmm5_reg, xmm6_reg, x mm7_reg, + FR_OUTPUTS_OFFSET + 5, FR_OUTPUTS_OFFSET + 6, FR_OUTPUTS_OFFSET + 7, + // xmm8_reg, xmm9_reg, xmm10_reg, + FR_TEMPORARY_OFFSET, FR_TEMPORARY_OFFSET + 1, FR_TEMPORARY_OFFSET + 2, + // xmm11_reg, xmm12_reg, xmm13_reg, + FR_TEMPORARY_OFFSET + 3, FR_TEMPORARY_OFFSET + 4, FR_TEMPORARY_OFFSET + 5, + // xmm14_reg, xmm15_reg + FR_TEMPORARY_OFFSET + 6, FR_TEMPORARY_OFFSET + 7 + }; + return INDEX_MAP[reg]; + } +#endif + void * operator new(size_t sz, tl::MemoryPool & m) { + return m.alloc(sz); + } + + void operator delete (void * p, tl::MemoryPool & m) {} + + /** + * returns the number of incoming arguments + */ + unsigned get_num_inputs() const { + return n_inputs; + } + + /** + * returns the number of incoming arguments stored in GRs + */ + unsigned get_num_gr_inputs() const { + assert(n_gr_inputs <= MAX_GR_OUTPUTS); + return n_gr_inputs; + } + + /** + * Returns *reference* to the number of temporary GR registers currently allocated. + */ + unsigned& get_tmp_grs_used(void) { + return m_tmp_grs_used; + } + + /** + * Returns *reference* to the number of temporary XMM registers currently allocated. + */ + unsigned& get_tmp_xmms_used(void) { + return m_tmp_xmms_used; + } + + /** + * returns the number of incoming arguments stored in FRs + */ + unsigned get_num_fr_inputs() const { + assert(n_fr_inputs <= MAX_FR_OUTPUTS); + return n_fr_inputs; + } + + unsigned get_num_outputs() const { + return n_outputs; + } + + unsigned get_num_gr_outputs() const { + assert(n_gr_outputs <= MAX_GR_OUTPUTS); + return n_gr_outputs; + } + + unsigned get_num_fr_outputs() const { + assert(n_fr_outputs <= MAX_FR_OUTPUTS); + return n_fr_outputs; + } + + /** + * returns true if m2n is required on the activation frame + */ + bool has_m2n_frame() const { + return has_m2n; + } + + /** + * returns true if we need to reserve space on the stack to save inputs + */ + bool must_save_inputs() const { + return save_inputs; + } + + /** + * true if type represents floating point value + */ + bool is_fp_type(LilType t) const { + return t == LT_F4 || t == LT_F8; + } + + /** + * method which corresponds to the m2n frame + */ + Method_Handle get_m2n_method() const { + return m2n_method; + } + + /** + * m2n frame type + */ + frame_type get_m2n_frame_type() const { + return m2n_frame_type; + } + + /** + * returns true if m2n contains local handles + */ + bool m2n_has_handles() const { + return m2n_handles; + } + + // returns the offset of the start of the m2n frame + unsigned get_m2n_offset() const { + return get_input_gr_save_offset() + stk_input_gr_save_size; + } + + // returns the offset of the start of the gr input register save space + unsigned get_input_gr_save_offset() const { + return get_input_fr_save_offset() + stk_input_fr_save_size; + } + + // returns the offset of the start of the fr input register save space + unsigned get_input_fr_save_offset() const { + return get_alloc_start_offset() + stk_alloc_size; + } + + // returns the offset of the first "allocatable" byte + unsigned get_alloc_start_offset() const { + return get_output_offset() + stk_output_size; + } + + // returns the offset of the start of the m2n frame + unsigned get_output_offset() const { + return 0; + } + + // size reserved for saving GR inputs + unsigned get_stk_input_gr_save_size() const { + return stk_input_gr_save_size; + } + + // size reserved for saving FR inputs + unsigned get_stk_input_fr_save_size() const { + return stk_input_fr_save_size; + } + + // size of allocatable memory on the stack + unsigned get_stk_alloc_size() const { + return stk_alloc_size; + } + + // size reserved for the m2n frame + unsigned get_stk_m2n_size() const { + return stk_m2n_size; + } + + // bytes needed for outgoing params on the stack + unsigned get_stk_output_size() const { + return stk_output_size; + } + + // returns the size of the stack frame + unsigned get_stk_size() const { + return stk_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; + 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) { + assert(num <= MAX_GR_LOCALS); + } + + void std_places(unsigned num) { + assert(num <= MAX_STD_PLACES); + } + + 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) { + // make sure there is enough space for this command's outputs + unsigned outs_cnt = lil_sig_get_num_args(sig); + n_outputs = outs_cnt > n_outputs ? outs_cnt : n_outputs; + + // reserve enough GR & FR outputs + unsigned gp_out_cnt = 0; +#ifdef _WIN64 +# define fp_out_cnt gp_out_cnt +#else + unsigned fp_out_cnt = 0; +#endif + for (unsigned i = 0; i < lil_sig_get_num_args(sig); i++) { + LilType t = lil_sig_get_arg_type(sig, i); + if (is_fp_type(t)) { + fp_out_cnt++; + } else { + gp_out_cnt++; + } + } + + if (n_gr_outputs < gp_out_cnt) { + if (gp_out_cnt <= MAX_GR_OUTPUTS) { + n_gr_outputs = gp_out_cnt; + } else { + n_gr_outputs = MAX_GR_OUTPUTS; + stk_output_size += (gp_out_cnt - n_gr_outputs) * GR_SIZE; + } + } + + if (n_fr_outputs < fp_out_cnt) { + if (fp_out_cnt <= MAX_FR_OUTPUTS) { + n_fr_outputs = fp_out_cnt; + } else { + n_fr_outputs = MAX_FR_OUTPUTS; + stk_output_size += (fp_out_cnt - n_fr_outputs) * FR_SIZE; + } + } + } + + 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() { + // nothing to do + } + + void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) { + m2n_method = method; + m2n_frame_type = current_frame_type; + m2n_handles = 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 print(char *, LilOperand *) { + } +}; + +/** +* keeps location of a LIL variable +*/ +class LcgEM64TLoc { + +public: + LcgEM64TLocKind kind; + int64 addr; // register number or SP-relative offset + + LcgEM64TLoc(LcgEM64TLocKind k, int64 a): kind(k), addr(a) {} + + bool operator==(const LcgEM64TLoc & loc) const { + return (kind == loc.kind && addr == loc.addr); + } + + bool operator!=(const LcgEM64TLoc & loc) { + 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: + LcgEM64TLoc(LcgEM64TLoc &); // disable copying + LcgEM64TLoc & operator=(LcgEM64TLoc &); // disable copying +}; + +class LilCodeGeneratorEM64T : public LilCodeGenerator { + + public: + LilCodeGeneratorEM64T(); + + protected: + NativeCodePtr compile_main(LilCodeStub* , size_t*, PoolManager*); +}; + +#endif // _LIL_CODE_GENERATOR_EM64T_ diff --git a/vm/vmcore/src/lil/em64t/lil_code_generator_em64t.cpp b/vm/vmcore/src/lil/em64t/lil_code_generator_em64t.cpp new file mode 100644 index 0000000..c211136 --- /dev/null +++ b/vm/vmcore/src/lil/em64t/lil_code_generator_em64t.cpp @@ -0,0 +1,1730 @@ +/* + * 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. + */ +/** + * @author Evgueni Brevnov + * @version $Revision: 1.1.2.2.4.3 $ + */ + +#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_em64t.h" +#include "lil_code_generator_utils.h" +#include "m2n.h" +#include "m2n_em64t_internal.h" + +#ifndef NDEBUG +#include "dump.h" +#endif + +LcgEM64TContext::LcgEM64TContext(LilCodeStub * stub, tl::MemoryPool & m): + cs(stub), mem(m), iter(cs, true) { + + /* 1) PRE-INITIALIZATION */ + n_inputs = 0; + n_gr_inputs = 0; + n_fr_inputs = 0; + + n_outputs = 0; + n_gr_outputs = 0; + n_fr_outputs = 0; + + m_tmp_grs_used = 0; + m_tmp_xmms_used = 0; + + stk_input_gr_save_size = 0; + stk_input_fr_save_size = 0; + stk_output_size = 0; + stk_alloc_size = 0; + + m2n_handles = false; + does_normal_calls = false; + does_tail_calls = false; + calls_unmanaged_code = false; + has_m2n = false; + save_inputs = false; + + /* 2) SCAN THE CODE STUB FOR THE INFORMATION */ + + while (!iter.at_end()) { + lil_visit_instruction(iter.get_current(), this); + iter.goto_next(); + } + + /* 3) INITIALIZE INPUTS ACCORDING TO THE ENTRY SIGNATURE */ + if (lil_sig_is_arbitrary(lil_cs_get_sig(stub))) { + n_gr_inputs = MAX_GR_OUTPUTS; + n_fr_inputs = MAX_FR_OUTPUTS; + n_inputs = n_gr_inputs + n_fr_outputs; + if (does_normal_calls) { + save_inputs = true; + } + } else { + n_inputs = lil_sig_get_num_args(lil_cs_get_sig(stub)); + // TODO: check if it makes sense to move to separate function + for (unsigned i = 0; i < n_inputs; i++) { + if (is_fp_type(lil_sig_get_arg_type(lil_cs_get_sig(stub), i))) { + n_fr_inputs++; + } else { + n_gr_inputs++; + } + } + n_gr_inputs = n_gr_inputs > MAX_GR_OUTPUTS ? MAX_GR_OUTPUTS : n_gr_inputs; + n_fr_inputs = n_fr_inputs > MAX_FR_OUTPUTS ? MAX_FR_OUTPUTS : n_fr_inputs; + } + + /* 4) INITILIZE STACK INFORMATION */ + + if (has_m2n) { + stk_m2n_size = (unsigned)(m2n_get_size() - 2*sizeof(void*)); + } else { + // preserve space for callee-saves registers + stk_m2n_size = lil_cs_get_max_locals(stub) * GR_SIZE; + } + + // stack size needed for saving GR & FR inputs + if (must_save_inputs()) { + // TODO: check if we need alignment here + stk_input_gr_save_size = n_gr_inputs * GR_SIZE; + stk_input_fr_save_size = n_fr_inputs * FR_SIZE; + } + + // determine the size of the stack frame + stk_size = + stk_m2n_size + // memory for m2n frame + stk_input_gr_save_size + // GR input spill space + stk_input_fr_save_size + // FR input spill space + stk_alloc_size + // memory for dynamic allocation + stk_output_size; // outputs on the stack + + // allign stack + if (stk_size % 16 == 0) { + stk_size += 8; + stk_alloc_size +=8; + } +} + +/** + * Implementation notes: + * 1) Current implementation doesn't correctly processes back branches when + * input arguments are accessed + */ +class LcgEM64TCodeGen: public LilInstructionVisitor { + + /** + * Maximum possible size of code generated for a single LIL instruction. + * + * Speculatevely calculated as a sequence of code that used all available + * registers (e.g. potential prolog or epilog) * 2. The real maximum sizes + * are far this speculation (about 120 bytes), so I hardly believe we will + * ever reach this theoretical limit even with deep changes in the LIL codegen. + * As the buffer size is predicted basing on this high estimation, we're safe + * in the terms of buffer overruns in current codeged. + */ + static const unsigned MAX_LIL_INSTRUCTION_CODE_LENGTH = 512*2; + + static const unsigned MAX_DEC_SIZE = 20; // maximum number of bytes required for string representation of int64 + + char * buf_beg; // pointer to the beginning position of the generated code + char * buf; // pointer to the current position of the generated code + + LilCguLabelAddresses labels; // a set of defined labels and theirs addresses + + LilCodeStub * cs; + LcgEM64TContext & context; + tl::MemoryPool & mem; + LilInstructionIterator iter; + + LilInstructionContext * ic; // visit functions can always assume that inst points to the + LilInstruction * inst; // current instruction and ic points to the current context + + const LcgEM64TLoc * rsp_loc; // location denoting rsp register + bool take_inputs_from_stack; // true if inputs were preserved on the stack + // and should be taken from that location + unsigned current_alloc; // keeps track of memory allocation + +private: + + /* Inner Classes */ + + class Tmp_GR_Opnd: public R_Opnd { + + private: + /// Ref to compilation context + LcgEM64TContext& m_context; + /// Returns *reference* to the counter of currently allocated GR registers + unsigned & get_num_used_reg(void) { + return m_context.get_tmp_grs_used(); + } + + public: + Tmp_GR_Opnd(LcgEM64TContext & context, LilInstructionContext * ic): R_Opnd(n_reg), m_context(context) { + // next temporary register is allocated from unused scratched + // registers in the following order: + // ret, std0, std1, out0, out1, out2, out3, out4, out5 + unsigned cur_tmp_reg = 0; + if (lil_ic_get_ret_type(ic) == LT_Void) { + if (cur_tmp_reg == get_num_used_reg()) { + _reg_no = context.get_reg_from_map(LcgEM64TContext::GR_RETURNS_OFFSET).reg_no(); // should be rax + ++get_num_used_reg(); + return; + } else { + ++cur_tmp_reg; + } + } + for (unsigned i = lil_ic_get_num_std_places(ic); i < LcgEM64TContext::MAX_STD_PLACES; i++) { + if (cur_tmp_reg == get_num_used_reg()) { + _reg_no = context.get_reg_from_map(LcgEM64TContext::STD_PLACES_OFFSET + i).reg_no(); + ++get_num_used_reg(); + return; + } else { + ++cur_tmp_reg; + } + } + for (unsigned i = context.get_num_gr_inputs(); i < LcgEM64TContext::MAX_GR_OUTPUTS; i++) { + if (cur_tmp_reg == get_num_used_reg()) { + _reg_no = context.get_reg_from_map(LcgEM64TContext::GR_OUTPUTS_OFFSET + i).reg_no(); + ++get_num_used_reg(); + return; + } else { + ++cur_tmp_reg; + } + } + ASSERT(0,"LIL INTERNAL ERROR: Not enough temporary registers"); + } + + virtual ~Tmp_GR_Opnd() { + --get_num_used_reg(); + } + }; + + class Tmp_FR_Opnd: public XMM_Opnd { + + private: + /// Ref to compilation context + LcgEM64TContext& m_context; + /// Returns *reference* to the counter of currently allocated XMM registers + unsigned & get_num_used_reg(void) { + return m_context.get_tmp_xmms_used(); + } + + public: + Tmp_FR_Opnd(LcgEM64TContext& context, LilInstructionContext * ic): XMM_Opnd(0), m_context(context) { + // next temporary register is allocated from unused scratched + // registers in the following order: + // xmm8, xmm9, ... xmm15 + ASSERT(get_num_used_reg() < LcgEM64TContext::MAX_FR_TEMPORARY + LcgEM64TContext:: MAX_FR_LOCALS, + //ASSERT(get_num_used_reg() < LcgEM64TContext::MAX_FR_TEMPORARY , + "LIL INTERNAL ERROR: Not enough temporary registers"); + m_idx = LcgEM64TContext::get_xmm_reg_from_map( + LcgEM64TContext::FR_TEMPORARY_OFFSET + get_num_used_reg()).get_idx(); + ++get_num_used_reg(); + } + + virtual ~Tmp_FR_Opnd() { + --get_num_used_reg(); + } + }; + + /* Helper functions */ + + /** + * Estimates maximum possible code size for the current stub. + */ + unsigned estimate_code_size(void) { + unsigned num_insts = lil_cs_get_num_instructions(cs); + return num_insts*MAX_LIL_INSTRUCTION_CODE_LENGTH; + } + + /** + * returns the location of the n'th int local + */ + const LcgEM64TLoc * get_gp_local(const unsigned n) const { + assert(n < LcgEM64TContext::MAX_GR_LOCALS); + return new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::GR_LOCALS_OFFSET + n); + } + + // returns the location of the n'th fp local + const LcgEM64TLoc * get_fp_local(const unsigned n) const { + // DO NOT SUPPORT FP LOCALS + ABORT("Not supported"); + return NULL; + /* + assert(n < context.get_num_fr_locals()); + if (does_normal_calls) { + return new(mem) LcgEM64TLoc(LLK_FStk, context.get_local_fr_offset() + n * FR_SIZE); + else { + return new(mem) LcgEM64TLoc(LLK_Fr, LcgEM64TContext::FR_LOCALS_OFFSET + n); + } + */ + } + + // returns the location of the n'th standard place + const LcgEM64TLoc * get_std_place(const unsigned n) const { + assert(n < LcgEM64TContext::MAX_STD_PLACES); + return new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::STD_PLACES_OFFSET + n); + } + + /** + * returns number of bytes required for the given type on the stack + */ + unsigned get_num_bytes_on_stack(LilType t) const { + switch (t) { + case LT_Void: + return 0; + case LT_G1: + case LT_G2: + case LT_G4: + case LT_F4: + return 4; + case LT_Ref: + case LT_PInt: + case LT_G8: + case LT_F8: + return 8; + default: + ASSERT(0, "Unknown LIL type"); + } + return 0; + } + + // 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 LcgEM64TLoc * get_input(const unsigned n) const { + assert(n < context.get_num_inputs()); +#ifndef NDEBUG + if (take_inputs_from_stack) { + assert(context.must_save_inputs()); + } +#endif + LilType t; + unsigned gp_param_cnt = 0; +#ifdef _WIN64 +#define fp_param_cnt gp_param_cnt +#else + unsigned fp_param_cnt = 0; +#endif + for (unsigned i = 0; i < n; i++) { + t = lil_sig_get_arg_type(lil_cs_get_sig(cs), i); + if (context.is_fp_type(t)) { + ++fp_param_cnt; + } else { + ++gp_param_cnt; + } + } + + // type of n'th input + t = lil_sig_get_arg_type(lil_cs_get_sig(cs), n); + + if (context.is_fp_type(t)) { + if (fp_param_cnt < LcgEM64TContext::MAX_FR_OUTPUTS) { + if (take_inputs_from_stack) { + // compute rsp-relative offset of the bottom of FR save area + int32 offset = context.get_input_fr_save_offset() + + fp_param_cnt * LcgEM64TContext::FR_SIZE; + return new(mem) LcgEM64TLoc(LLK_FStk, offset); + } else { + return new(mem) LcgEM64TLoc(LLK_Fr, + LcgEM64TContext::FR_OUTPUTS_OFFSET + fp_param_cnt); + } + } else { + unsigned gp_on_stack = gp_param_cnt > LcgEM64TContext::MAX_GR_OUTPUTS + ? (gp_param_cnt - LcgEM64TContext::MAX_GR_OUTPUTS) + * LcgEM64TContext::GR_SIZE : 0; + unsigned fp_on_stack = fp_param_cnt > LcgEM64TContext::MAX_FR_OUTPUTS + ? (fp_param_cnt - LcgEM64TContext::MAX_FR_OUTPUTS) + * LcgEM64TContext::FR_SIZE : 0; + // compute rsp-relative offset of the top of the activation frame + int32 offset = context.get_stk_size(); + // skip rip + offset += LcgEM64TContext::GR_SIZE; + // skip size allocated for preceding inputs + offset += gp_on_stack; +#ifndef _WIN64 + offset += fp_on_stack; +#endif + return new(mem) LcgEM64TLoc(LLK_FStk, offset); + } + } else { // if (context.is_fp_type(t)) + if (gp_param_cnt < LcgEM64TContext::MAX_GR_OUTPUTS) { + if (take_inputs_from_stack) { + // compute rsp-relative offset of the bottom of GR save area + int32 offset = context.get_input_gr_save_offset() + + gp_param_cnt * LcgEM64TContext::GR_SIZE; + return new(mem) LcgEM64TLoc(LLK_GStk, offset); + } else { + return new(mem) LcgEM64TLoc(LLK_Gr, + LcgEM64TContext::GR_OUTPUTS_OFFSET + gp_param_cnt); + } + } else { + unsigned gp_on_stack = gp_param_cnt > LcgEM64TContext::MAX_GR_OUTPUTS + ? (gp_param_cnt - LcgEM64TContext::MAX_GR_OUTPUTS) + * LcgEM64TContext::GR_SIZE : 0; + unsigned fp_on_stack = fp_param_cnt > LcgEM64TContext::MAX_FR_OUTPUTS + ? (fp_param_cnt - LcgEM64TContext::MAX_FR_OUTPUTS) + * LcgEM64TContext::FR_SIZE : 0; + // compute rsp-relative offset of the top of the activation frame + int32 offset = context.get_stk_size(); + // skip rip + offset += LcgEM64TContext::GR_SIZE; + // skip size allocated for preceding inputs + offset += gp_on_stack; +#ifndef _WIN64 + offset += fp_on_stack; +#endif + return new(mem) LcgEM64TLoc(LLK_GStk, offset); + } + } + } + + + // returns the location of the n'th output + const LcgEM64TLoc * get_output(const unsigned n, LilSig * out_sig) const { + assert(n <= context.get_num_outputs()); + + LilType t; + unsigned gp_param_cnt = 0; +#ifdef _WIN64 +#define fp_param_cnt gp_param_cnt +#else + unsigned fp_param_cnt = 0; +#endif + for (unsigned i = 0; i < n; i++) { + t = lil_sig_get_arg_type(out_sig, i); + if (context.is_fp_type(t)) { + ++fp_param_cnt; + } else { + ++gp_param_cnt; + } + } + + // type of n'th output + t = lil_sig_get_arg_type(out_sig, n); + + if (context.is_fp_type(t)) { + if (fp_param_cnt < LcgEM64TContext::MAX_FR_OUTPUTS) { + return new(mem) LcgEM64TLoc(LLK_Fr, + LcgEM64TContext::FR_OUTPUTS_OFFSET + fp_param_cnt); + } else { + unsigned gp_on_stack = gp_param_cnt > LcgEM64TContext::MAX_GR_OUTPUTS + ? (gp_param_cnt - LcgEM64TContext::MAX_GR_OUTPUTS) + * LcgEM64TContext::GR_SIZE : 0; + unsigned fp_on_stack = fp_param_cnt > LcgEM64TContext::MAX_FR_OUTPUTS + ? (fp_param_cnt - LcgEM64TContext::MAX_FR_OUTPUTS) + * LcgEM64TContext::FR_SIZE : 0; + int32 offset = gp_on_stack; +#ifndef _WIN64 + offset += fp_on_stack; +#endif + return new(mem) LcgEM64TLoc(LLK_FStk, offset); + } + } else { + if (gp_param_cnt < LcgEM64TContext::MAX_GR_OUTPUTS) { + return new(mem) LcgEM64TLoc(LLK_Gr, + LcgEM64TContext::GR_OUTPUTS_OFFSET + gp_param_cnt); + } else { + unsigned gp_on_stack = gp_param_cnt > LcgEM64TContext::MAX_GR_OUTPUTS + ? (gp_param_cnt - LcgEM64TContext::MAX_GR_OUTPUTS) + * LcgEM64TContext::GR_SIZE : 0; + unsigned fp_on_stack = fp_param_cnt > LcgEM64TContext::MAX_FR_OUTPUTS + ? (fp_param_cnt - LcgEM64TContext::MAX_FR_OUTPUTS) + * LcgEM64TContext::FR_SIZE : 0; + int32 offset = gp_on_stack; +#ifndef _WIN64 + offset += fp_on_stack; +#endif + return new(mem) LcgEM64TLoc(LLK_GStk, offset); + } + } + } + + /** + * 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 LcgEM64TLoc * 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 LcgEM64TLoc * get_var_loc(LilVariable * var, bool is_lvalue) const { + unsigned index = lil_variable_get_index(var); + switch (lil_variable_get_kind(var)) { + case LVK_In: + return get_input(index); + case LVK_StdPlace: + return get_std_place(index); + case LVK_Out: { + LilSig * out_sig = lil_ic_get_out_sig(ic); + assert(out_sig != NULL); + return get_output(index, out_sig); + } + case LVK_Local: { + //LilType t = is_lvalue + // ? lil_instruction_get_dest_type(cs, inst, ic) + // : lil_ic_get_local_type(ic, index); + //return context.is_fp_type(t) ? get_fp_local(index) : get_gp_local(index); + // no support for fp locals + return get_gp_local(index); + } + case LVK_Ret: { + //LilType t = is_lvalue + // ? lil_instruction_get_dest_type(cs, inst, ic) + // : lil_ic_get_ret_type(ic); + LilType t = lil_ic_get_ret_type(ic); + return context.is_fp_type(t) + ? new(mem) LcgEM64TLoc(LLK_Fr, LcgEM64TContext::FR_RETURNS_OFFSET) + : new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::GR_RETURNS_OFFSET); + } + default: + ASSERT(0, "Unknown variable kind"); + } + return NULL; // should never be reached + } + + inline int64 get_imm_value(LilOperand * op) const { + assert(lil_operand_is_immed(op)); + return lil_operand_get_immed(op); + } + + inline const Imm_Opnd & get_imm_opnd(LilOperand * op, Opnd_Size sz = n_size) const { + return get_imm_opnd(get_imm_value(op), sz); + } + + 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_64, value)); + } + return *(new(mem_ptr) Imm_Opnd(sz, value)); + } + + inline const R_Opnd & get_r_opnd(const LcgEM64TLoc * loc) const { + assert(loc->kind == LLK_Gr); + return context.get_reg_from_map((unsigned)loc->addr); + } + + inline const XMM_Opnd & get_xmm_r_opnd(const LcgEM64TLoc * loc) const { + assert(loc->kind == LLK_Fr); + return context.get_xmm_reg_from_map((unsigned)loc->addr); + } + + inline const M_Opnd & get_m_opnd(const LcgEM64TLoc * loc) const { + assert(loc->kind == LLK_GStk || loc->kind == LLK_FStk); + void * const mem_ptr = mem.alloc(sizeof(M_Base_Opnd)); + return *(new(mem_ptr) M_Base_Opnd(rsp_reg, (int32)loc->addr)); + } + + inline const RM_Opnd & get_rm_opnd(const LcgEM64TLoc * loc) const { + assert(loc->kind == LLK_Gr || loc->kind == LLK_GStk); + if (loc->kind == LLK_Gr) { + return get_r_opnd(loc); + } + //return M_Base_Opnd(rsp_reg, loc->addr); + return get_m_opnd(loc); + } + + inline void adjust_stack_pointer(int32 offset) { + if (offset != 0) { + buf = alu(buf, add_opc, rsp_opnd, get_imm_opnd(offset), size_64); + } + } + + // move integer immediate to GR or memory stack + void move_imm(const LcgEM64TLoc * dest, int64 imm_val) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); + buf = mov(buf, get_rm_opnd(dest), get_imm_opnd(imm_val), size_64); + } + + // move between two register or stack locations + void move_rm(const LcgEM64TLoc* dest, const LcgEM64TLoc* src) { + if (*dest == *src) { + return; // nothing to be done + } + if (dest->kind == LLK_Gr || dest->kind == LLK_GStk) { + if (src->kind == LLK_Gr || src->kind == LLK_GStk) { + if (dest->kind == LLK_Gr) { + buf = mov(buf, get_r_opnd(dest), get_rm_opnd(src), size_64); + return; + } + // dest->kind != LLK_Gr + if (src->kind == LLK_Gr) { + buf = mov(buf, get_m_opnd(dest), get_r_opnd(src), size_64); + return; + } + // src->kind != LLK_Gr + // use temporary register + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_m_opnd(src), size_64); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); + } else { // src->kind == LLK_Fr || src->kind == LLK_FStk + // src->kind == LLK_Fr supported only + assert(src->kind == LLK_Fr); + buf = movq(buf, get_rm_opnd(dest), get_xmm_r_opnd(src)); + return; + } + } else { // dest->kind == LLK_Fr || dest->kind == LLK_FStk + if (src->kind == LLK_Fr || src->kind == LLK_FStk) { + if (dest->kind == LLK_Fr) { + if (src->kind == LLK_Fr) { + buf = sse_mov(buf, get_xmm_r_opnd(dest), get_xmm_r_opnd(src), true); + } else { + buf = sse_mov(buf, get_xmm_r_opnd(dest), get_m_opnd(src), true); + } + return; + } + // dest->kind != LLK_Gr + if (src->kind == LLK_Fr) { + buf = sse_mov(buf, get_m_opnd(dest), get_xmm_r_opnd(src), true); + return; + } + // src->kind != LLK_Gr + // use temporary register + Tmp_FR_Opnd tmp_reg(context, ic); + buf = sse_mov(buf, tmp_reg, get_m_opnd(src), true); + buf = sse_mov(buf, get_m_opnd(dest), tmp_reg, true); + } else { // src->kind == LLK_Gr || src->kind == LLK_GStk + // dest->kind == LLK_Fr supported only + assert(dest->kind == LLK_Fr); + buf = movq(buf, get_xmm_r_opnd(dest), get_rm_opnd(src)); + return; + } + } + } + + void shift_op_imm_rm(const LcgEM64TLoc * dest, int32 imm_val, const LcgEM64TLoc * src) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); + assert(src->kind == LLK_Gr || src->kind == LLK_GStk); + // 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 LcgEM64TLoc * dest, const LcgEM64TLoc * 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_64); + } else { + buf = mov(buf, get_m_opnd(dest), get_r_opnd(src), size_64); + } + 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_64); + buf = shift(buf, shl_opc, get_r_opnd(dest), imm, size_64); + return; + } + // dest->kind != LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_m_opnd(src), size_64); + 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 LcgEM64TLoc * dest, int32 imm_val, const LcgEM64TLoc * src) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); + assert(src->kind == LLK_Gr || src->kind == LLK_GStk); + // 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 LcgEM64TLoc * dest, + const LcgEM64TLoc * src, int32 imm_val) { + assert(alu_opc < n_alu); + assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); + assert(src->kind == LLK_Gr || src->kind == LLK_GStk); + const Imm_Opnd & imm = get_imm_opnd(imm_val); + if (*dest == *src) { + buf = alu(buf, alu_opc, get_rm_opnd(dest), imm, size_64); + return; + } + // dest != src + if (dest->kind == LLK_Gr) { + buf = mov(buf, get_r_opnd(dest), get_rm_opnd(src), size_64); + buf = alu(buf, alu_opc, get_r_opnd(dest), imm, size_64); + return; + } + // dest->kind != LLK_Gr + if (src->kind == LLK_Gr) { + buf = mov(buf, get_m_opnd(dest), get_r_opnd(src), size_64); + buf = alu(buf, alu_opc, get_m_opnd(dest), imm, size_64); + return; + } + // src->kind == LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_m_opnd(src), size_64); + buf = alu(buf, alu_opc, tmp_reg, imm, size_64); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); + } + + void alu_op_rm_rm(const ALU_Opcode alu_opc, const LcgEM64TLoc * dest, + const LcgEM64TLoc * src1, const LcgEM64TLoc * src2) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); + assert(src1->kind == LLK_Gr || src1->kind == LLK_GStk); + assert(src2->kind == LLK_Gr || src2->kind == LLK_GStk); + + 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_64); + 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_64); + 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_64); + buf = alu(buf, alu_opc, tmp_reg, get_rm_opnd(src2)); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); + } + + // where the second operand is immediate (allowed only for integer values!) + void bin_op_rm_imm(LilOperation o, const LcgEM64TLoc* dest, + const LcgEM64TLoc* src, int32 imm_val) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); + assert(src->kind == LLK_Gr || src->kind == LLK_GStk); + + switch (o) { + case LO_Add: + return alu_op_rm_imm(add_opc, dest, src, imm_val); + case LO_Sub: + return alu_op_rm_imm(sub_opc, dest, src, imm_val); + case LO_And: + return alu_op_rm_imm(and_opc, dest, src, imm_val); + 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); + } + return; + } + // dest->kind != LLK_Gr + Tmp_GR_Opnd tmp_reg(context, ic); + buf = mov(buf, tmp_reg, get_rm_opnd(src), size_64); + buf = imul(buf, tmp_reg, imm); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); + break; + } + case LO_Shl: + shift_op_rm_imm(dest, src, imm_val); + break; + default: + ASSERT(0, "Unexpected operation"); + } + } + + // binary arithmetic operations without immediates + // (allowed only for integer values!) + void bin_op_rm_rm(LilOperation o, const LcgEM64TLoc * dest, + const LcgEM64TLoc * src1, const LcgEM64TLoc * src2) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); + assert(src1->kind == LLK_Gr || src1->kind == LLK_GStk); + assert(src2->kind == LLK_Gr || src2->kind == LLK_GStk); + + 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_64); + } + 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_64); + buf = imul(buf, tmp_reg, get_rm_opnd(src2)); + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); + break; + } + case LO_Shl: { + move_rm(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_64); + } + buf = shift(buf, shl_opc, get_rm_opnd(dest), *src2_reg); + break; + } + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point + } + } + + // unary operation without immediates + void un_op_rm(LilOperation o, const LcgEM64TLoc * dest, const LcgEM64TLoc * src) { + assert(dest->kind == LLK_Gr || dest->kind == LLK_GStk); + assert(src->kind == LLK_Gr || src->kind == LLK_GStk); + + const Tmp_GR_Opnd tmp_reg(context, ic); + const R_Opnd & dest_reg = + (dest->kind == LLK_Gr ? get_r_opnd(dest) : (const R_Opnd &)tmp_reg); + + switch (o) { + case LO_Neg: + buf = mov(buf, dest_reg, get_rm_opnd(src), size_64); + buf = neg(buf, dest_reg, size_64); + break; + case LO_Not: + buf = mov(buf, dest_reg, get_rm_opnd(src), size_64); + buf = _not(buf, dest_reg, size_64); + 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 + } + + if (dest->kind != LLK_Gr) { + buf = mov(buf, get_m_opnd(dest), tmp_reg, size_64); + } + } + + // generate instructions that calculate a LIL address + const M_Opnd & get_effective_addr(LilVariable * base, unsigned scale, + LilVariable * index, int64 offset, + const R_Opnd & tmp_reg) { + 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_64, offset)); + return *(new(M_Index_Opnd_mem) M_Index_Opnd(tmp_reg.reg_no(), n_reg, 0, 0)); + } + + // initialize locations + const bool is_offset32 = fit32(offset); + const LcgEM64TLoc * base_loc = base != NULL ? get_var_loc(base, false) : NULL; + const LcgEM64TLoc * index_loc = index != NULL && scale != 0 ? get_var_loc(index, false) : NULL; + const bool is_base_in_mem = base_loc != NULL && base_loc->kind == LLK_GStk; + const bool is_index_in_mem = index_loc != NULL && index_loc->kind == LLK_GStk; + + // check if there is no need to use temporary register + if (is_offset32 && !is_base_in_mem && !is_index_in_mem) { + return *(new(M_Index_Opnd_mem) M_Index_Opnd( + base_loc != NULL ? get_r_opnd(base_loc).reg_no() : n_reg, + index_loc != NULL ? get_r_opnd(index_loc).reg_no() : n_reg, + (uint32)offset, scale)); + } + + // check if it's enough to use only one temporary register + if (is_offset32 && (is_base_in_mem || is_index_in_mem)) { + if (is_base_in_mem) { + buf = mov(buf, tmp_reg, get_m_opnd(base_loc), size_64); + return *(new(M_Index_Opnd_mem) M_Index_Opnd(tmp_reg.reg_no(), + index_loc != NULL ? get_r_opnd(index_loc).reg_no() : n_reg, + (uint32)offset, scale)); + } + if (is_index_in_mem) { + buf = mov(buf, tmp_reg, get_m_opnd(index_loc), size_64); + return *(new(M_Index_Opnd_mem) M_Index_Opnd( + base_loc != NULL ? get_r_opnd(base_loc).reg_no() : n_reg, + tmp_reg.reg_no(), (uint32)offset, scale)); + } + ASSERT(0, "Should never reach this point"); + } + + // need to perform manual calculation of the effective address + const LcgEM64TLoc * ret_loc = + new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::get_index_in_map(tmp_reg.reg_no())); + if (index_loc != NULL) { + int32 shift = scale / 4 + 1; + bin_op_rm_imm(LO_Shl, ret_loc, index_loc, shift); + if (base_loc != NULL) { + bin_op_rm_rm(LO_Add, ret_loc, ret_loc, base_loc); + } + if (!is_offset32) { + Tmp_GR_Opnd tmp_reg2(context, ic); + const LcgEM64TLoc * tmp_loc = + new(mem) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::get_index_in_map(tmp_reg2.reg_no())); + move_imm(tmp_loc, offset); + bin_op_rm_rm(LO_Add, ret_loc, ret_loc, tmp_loc); + offset = 0; + } + } else if (base_loc != NULL) { + // index_loc is NULL + if (is_offset32) { + move_rm(ret_loc, base_loc); + } else { + move_imm(ret_loc, offset); + bin_op_rm_rm(LO_Add, ret_loc, ret_loc, base_loc); + offset = 0; + } + + } else { + assert(!is_offset32); + move_imm(ret_loc, offset); + offset = 0; + } + return *(new(M_Index_Opnd_mem) M_Base_Opnd(get_r_opnd(ret_loc).reg_no(), (uint32)offset)); + } + + // sets up stack frame + void prolog() { + // push callee-saves registers on the stack + if (lil_cs_get_max_locals(cs) > 0) { + assert(context.get_stk_size() != 0); + for (unsigned i = 0; i < lil_cs_get_max_locals(cs); i++) { + const LcgEM64TLoc * loc = get_gp_local(i); + assert(loc->kind == LLK_Gr); + buf = push(buf, get_r_opnd(loc), size_64); + } + } + adjust_stack_pointer(lil_cs_get_max_locals(cs) * LcgEM64TContext::GR_SIZE - + context.get_stk_size()); + } + + // unsets the stack frame + void epilog() { + adjust_stack_pointer(context.get_stk_size() - + lil_cs_get_max_locals(cs) * LcgEM64TContext::GR_SIZE); + + if (lil_cs_get_max_locals(cs) > 0) { + assert(context.get_stk_size() != 0); + // pop callee-saves registers from the stack + for (int i = lil_cs_get_max_locals(cs) - 1; i >= 0; i--) { + const LcgEM64TLoc * loc = get_gp_local(i); + assert(loc->kind == LLK_Gr); + buf = pop(buf, get_r_opnd(loc), size_64); + } + } + } + + // moves GR & FR inputs into the stack + void move_inputs() { + if (!context.must_save_inputs()) { + return; + } + + // compute the rsp-relative offset of the top of the GR save area + int32 offset = (int32)context.get_input_gr_save_offset() + + context.get_stk_input_gr_save_size(); + // store GR inputs to the computed locations + for (int i = context.get_num_gr_inputs() - 1; i >= 0; i--) { + const R_Opnd & r_opnd = + LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_OUTPUTS_OFFSET + i); + offset -= LcgEM64TContext::GR_SIZE; + const M_Opnd dest(rsp_reg, offset); + buf = mov(buf, dest, r_opnd); + } + // compute the rsp-relative offset of the top of the FR save area + offset = (int32)context.get_input_fr_save_offset() + + context.get_stk_input_fr_save_size(); + // store FR inputs to the computed locations + for (int i = context.get_num_fr_inputs() - 1; i >= 0; i--) { + const XMM_Opnd & r_opnd = + LcgEM64TContext::get_xmm_reg_from_map(LcgEM64TContext::FR_OUTPUTS_OFFSET + i); + offset -= LcgEM64TContext::FR_SIZE; + const M_Opnd dest(rsp_reg, offset); + buf = sse_mov(buf, dest, r_opnd, true); + } + } + + // moves GR & FR inputs from the stack back to the registers + void unmove_inputs() { + if (!context.must_save_inputs()) { + // inputs should be already in place + return; + } + // compute the rsp-relative offset of the bottom of the FR save area + int32 offset = (int32)context.get_input_fr_save_offset(); + // restore FR inputs + for (unsigned i = 0; i < context.get_num_fr_inputs(); i++) { + const XMM_Opnd & r_opnd = + LcgEM64TContext::get_xmm_reg_from_map(LcgEM64TContext::FR_OUTPUTS_OFFSET + i); + const M_Opnd src(rsp_reg, offset); + buf = sse_mov(buf, r_opnd, src, true); + offset += LcgEM64TContext::FR_SIZE; + } + // compute the rsp-relative offset of the bottom of the GR save area + offset = (int32)context.get_input_gr_save_offset(); + // restore GR inputs + for (unsigned i = 0; i < context.get_num_gr_inputs(); i++) { + const R_Opnd & r_opnd = + LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_OUTPUTS_OFFSET + i); + const M_Opnd src(rsp_reg, offset); + buf = mov(buf, r_opnd, src, size_64); + offset += LcgEM64TContext::GR_SIZE; + } + } + + Opnd_Size type_to_opnd_size(LilType t) const { + switch (t) { + case LT_G1: + return size_8; + case LT_G2: + return size_16; + case LT_G4: + return size_32; + case LT_G8: + case LT_PInt: + case LT_Ref: + return size_64; + default: + return n_size; + } + } + +public: + + /** + * constructor + */ + LcgEM64TCodeGen(LilCodeStub * cs, LcgEM64TContext & c, tl::MemoryPool & m): + buf_beg(NULL), buf(NULL), + labels(&m, buf_beg), cs(cs), context(c), mem(m), iter(cs, true), ic(NULL), inst(NULL), + rsp_loc(new(m) LcgEM64TLoc(LLK_Gr, LcgEM64TContext::RSP_OFFSET)), take_inputs_from_stack(false), + current_alloc(0) { + + unsigned max_code_size = estimate_code_size(); + + buf = buf_beg = (char *)m.alloc(max_code_size); + char* buf_end = buf_beg + max_code_size; + + static unsigned max_code = 0; + + // emit entry code + char* i_start = buf; + + // the size of code for prolog accounted in estimate_code_size + // as the code for 'entry' instruction. + + prolog(); + move_inputs(); + + // debug code: check the estimate + char* i_end = buf; + unsigned i_len = (unsigned)(i_end - i_start); + if (i_len > MAX_LIL_INSTRUCTION_CODE_LENGTH) { + // the MAX_LIL_INSTRUCTION_CODE_LENGTH was underestimated. + // most likely will not cause problems in real life, though still requires correction. + assert(false); + } + + // go through the instructions + while (!iter.at_end()) { + char* i_start = buf; + + ic = iter.get_context(); + inst = iter.get_current(); + lil_visit_instruction(inst, this); + iter.goto_next(); + + // debug code: see above for the rationale + char* i_end = buf; + unsigned i_len = (unsigned)(i_end - i_start); + if (i_len > MAX_LIL_INSTRUCTION_CODE_LENGTH) { + assert(false); + } + } + + if (buf>buf_end) { + /// Ugh. Buffer overrun. + /// Must be accompanied with previous assert()-s on MAX_LIL_INSTRUCTION_CODE_LENGTH? + assert(false); + } + } + + /** + * memory allocator + */ + void *operator new(size_t sz, tl::MemoryPool & m) { + return m.alloc(sz); + } + + /** + * returns actual size of the generated code + */ + size_t get_size() const { + return buf - buf_beg; + } + + /** + * 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; + } + + + /* Visitor Functions */ + + void label(LilLabel lab) { + labels.define_label(lab, buf, true); + } + + void locals(unsigned num) { + // nothing to be done here; + } + + void std_places(unsigned num) { + // nothing to be done here; + } + + 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), rsp_loc, alloc_offset); + } + + 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 LcgEM64TLoc * 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 LcgEM64TLoc * src_loc = get_op_loc(op1, false); + move_rm(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 + assert(fit32(get_imm_value(op1))); + int32 op1_imm = (int32)get_imm_value(op1); + assert(fit32(get_imm_value(op2))); + int32 op2_imm = (int32)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 + } + move_imm(dest_loc, result); + } else if (lil_operand_is_immed(op1)) { + assert(fit32(get_imm_value(op1))); + const int32 op1_imm = (int32)get_imm_value(op1); + const LcgEM64TLoc * 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 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 + } + } else if (lil_operand_is_immed(op2)) { + const LcgEM64TLoc* op1_loc = get_op_loc(op1, false); + assert(fit32(get_imm_value(op2))); + const int32 op2_imm = (int32)get_imm_value(op2); + bin_op_rm_imm(o, dest_loc, op1_loc, op2_imm); + } else { // both operands non-immediate + const LcgEM64TLoc * src1_loc = get_op_loc(op1, false); + const LcgEM64TLoc * 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)) { + assert(fit32(get_imm_value(op1))); + int32 imm = (int32)get_imm_value(op1); + int32 result = 0; + switch (o) { + case LO_Neg: + result = -imm; + break; + 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 LO_Zx4: + result = (int32) (uint64) (uint32) imm; + break; + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point + } + move_imm(dest_loc, result); + } else { // non-immediate operand + const LcgEM64TLoc * op1_loc = get_op_loc(op1, false); + un_op_rm(o, dest_loc, op1_loc); + } + } + } + + // TODO: think over if it makes sense to preserve a register for VM_Tread pointer + void ts(LilVariable * var) { + const LcgEM64TLoc * var_loc = get_var_loc(var, true); + assert(var_loc->kind == LLK_Gr || var_loc->kind == LLK_GStk); + + if (var_loc->kind == LLK_Gr) { + buf = m2n_gen_ts_to_register(buf, &get_r_opnd(var_loc), + lil_ic_get_num_locals(ic), lil_cs_get_max_locals(cs), + lil_ic_get_num_std_places(ic), lil_ic_get_ret_type(ic) == LT_Void ? 0 : 1); + } else { + const Tmp_GR_Opnd tmp(context, ic); + buf = m2n_gen_ts_to_register(buf, &tmp, + lil_ic_get_num_locals(ic), lil_cs_get_max_locals(cs), + lil_ic_get_num_std_places(ic), lil_ic_get_ret_type(ic) == LT_Void ? 0 : 1); + buf = mov(buf, get_m_opnd(var_loc), tmp, size_64); + } + + take_inputs_from_stack = true; + } + + void handles(LilOperand * op) { + if (lil_operand_is_immed(op)) { + buf = m2n_gen_set_local_handles_imm(buf, + context.get_m2n_offset(), &get_imm_opnd(op)); + } else { + const LcgEM64TLoc * 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 = m2n_gen_set_local_handles_r(buf, + context.get_m2n_offset(), &tmp); + buf = mov(buf, get_m_opnd(loc), tmp, size_64); + } + } + } + + void ld(LilType t, LilVariable * dest, LilVariable * base, unsigned scale, + LilVariable * index, long long int offset, LilAcqRel 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_reg1(context, ic); + const Tmp_GR_Opnd * tmp_reg2 = NULL; + + // calculate the address + const M_Opnd & addr = get_effective_addr(base, scale, index, offset, tmp_reg1); + const LcgEM64TLoc * dest_loc = get_var_loc(dest, true); + assert(dest_loc->kind == LLK_Gr || dest_loc->kind == LLK_GStk); + void * const mem_ptr = mem.alloc(sizeof(Tmp_GR_Opnd)); + const R_Opnd * dest_reg = dest_loc->kind == LLK_Gr ? &get_r_opnd(dest_loc) : + (tmp_reg2 = new(mem_ptr) Tmp_GR_Opnd(context, ic)); + + Opnd_Size size = n_size; + switch(t) { + 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: + buf = mov(buf, *dest_reg, addr, size_64); + goto move_to_destination; + default: + ASSERT(0, "Unexpected LIL type"); // invalid value in type + } + + assert(size != n_size); + + if (ext == LLX_Zero) { + // movzx r64, r/m32 is not available on em64t + // mov r32, r/m32 should zero out upper bytes + if (size == size_32) { + buf = mov(buf, *dest_reg, addr, size); + } else { + buf = movzx(buf, *dest_reg, addr, size); + } + } else { + buf = movsx(buf, *dest_reg, addr, size); + } +move_to_destination: + if (dest_loc->kind != LLK_Gr) { + buf = mov(buf, get_m_opnd(dest_loc), *dest_reg, size_64); + delete dest_reg; + } + } + + void st(LilType t, LilVariable * base, unsigned scale, LilVariable * index, + long long int 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 { + const LcgEM64TLoc * 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, + long long int 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 cas(LilType t, LilVariable * base, unsigned scale, + LilVariable * index, long long int offset, LilAcqRel acqrel, + LilOperand * cmp, LilOperand * src, LilLabel label) { + // this is either rax register itself or keeps value of rax + const Tmp_GR_Opnd tmp_reg(context, ic); + + const LcgEM64TLoc * cmp_loc = lil_operand_is_immed(cmp) ? NULL : get_op_loc(cmp, false); + const LcgEM64TLoc * src_loc = lil_operand_is_immed(src) ? NULL : get_op_loc(src, false); + + bool is_rax_used = tmp_reg.reg_no() != rax_reg; + bool is_rax_used_by_cmp = (cmp_loc != NULL && cmp_loc->kind == LLK_Gr + && get_r_opnd(cmp_loc).reg_no() == rax_reg); + if (is_rax_used && !is_rax_used_by_cmp) { + // need to preserve rax value + buf = mov(buf, tmp_reg, rax_opnd, size_64); + } + + if (!is_rax_used_by_cmp) { + if (cmp_loc == NULL) { // cmp is immediate + buf = mov(buf, rax_opnd, get_imm_opnd(cmp), type_to_opnd_size(t)); + } else { + buf = mov(buf, rax_opnd, get_rm_opnd(cmp_loc), type_to_opnd_size(t)); + } + } + + const Tmp_GR_Opnd tmp_reg1(context, ic); + // calculate the address + const M_Opnd & addr = get_effective_addr(base, scale, index, offset, tmp_reg1); + + void * const mem_ptr = mem.alloc(sizeof(Tmp_GR_Opnd)); + const R_Opnd * src_reg = NULL; + const R_Opnd * src_reg2 = NULL; + if (src_loc == NULL) { // src is immediate value + src_reg = new(mem_ptr) Tmp_GR_Opnd(context, ic); + buf = mov(buf, *src_reg, get_imm_opnd(src), type_to_opnd_size(t)); + } else if (src_loc->kind == LLK_GStk) { + src_reg = new(mem_ptr) Tmp_GR_Opnd(context, ic); + buf = mov(buf, *src_reg, get_m_opnd(src_loc), type_to_opnd_size(t)); + } else if (is_rax_used && get_r_opnd(src_loc).reg_no() == rax_reg) { + // value of rax was saved in tmp_reg + src_reg2 = &tmp_reg; + } else { + src_reg2 = &get_r_opnd(src_loc); + } + buf = prefix(buf, lock_prefix); + buf = cmpxchg(buf, addr, src_reg != NULL ? *src_reg : *src_reg2, + type_to_opnd_size(t)); + buf = branch32(buf, Condition_NE, get_imm_opnd((int64)0, size_32)); + labels.add_patch_to_label(label, buf - 4, LPT_Rel32); + + delete src_reg; + + if (is_rax_used && src_reg2->reg_no() != rax_reg) { + // move preserved value back to rax + buf = mov(buf, rax_reg, tmp_reg, size_64); + } + } + + 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 p, LilOperand * op1, LilOperand * op2, LilLabel label) { + // compute the condition + ConditionCode cc = Condition_O; + + 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"); + } + + void * const mem_ptr = mem.alloc(sizeof(Tmp_GR_Opnd)); + const RM_Opnd * src1 = NULL; + const Tmp_GR_Opnd * tmp_reg = NULL; + if (!lil_predicate_is_binary(p)) { + op2 = (LilOperand *)mem.alloc(sizeof(LilOperand)); + op2->is_immed = true; + op2->val.imm = 0; + } + if (lil_operand_is_immed(op1) && lil_operand_is_immed(op2)) { + tmp_reg = new(mem_ptr) Tmp_GR_Opnd(context, ic); + src1 = tmp_reg; + buf = mov(buf, *tmp_reg, get_imm_opnd(op1), size_64); + } else { + if (lil_operand_is_immed(op1)) { + assert(!lil_operand_is_immed(op2)); + LilOperand * tmp_op = op1; + op1 = op2; + op2 = tmp_op; + switch (cc) { + case Condition_LE: + cc = Condition_G; + break; + case Condition_L: + cc = Condition_GE; + break; + default:; + } + } + assert(!lil_operand_is_immed(op1)); + src1 = &get_rm_opnd(get_op_loc(op1, false)); + } + // src1 is set here (not an immediate) + if (lil_operand_is_immed(op2)) { + int64 imm = lil_operand_get_immed(op2); + if (fit32(imm)) { + buf = alu(buf, cmp_opc, *src1, get_imm_opnd(imm, size_32), size_64); + } else { + // use temporary register + Tmp_GR_Opnd src2_reg(context, ic); + buf = mov(buf, src2_reg, get_imm_opnd(imm, size_64), size_64); + if (src1->is_reg()) { + buf = alu(buf, cmp_opc, *(R_Opnd *)src1, src2_reg); + } else { + buf = alu(buf, cmp_opc, *(M_Opnd *)src1, src2_reg); + } + } + } else { + // second operand is not an immediate value + const LcgEM64TLoc * src2_loc = get_op_loc(op2, false); + if (src1->is_reg()) { + buf = alu(buf, cmp_opc, *(R_Opnd *)src1, get_rm_opnd(src2_loc)); + } else if (src2_loc->kind == LLK_Gr) { + buf = alu(buf, cmp_opc, *(M_Opnd *)src1, get_r_opnd(src2_loc)); + } else { + // src1 is in memory as well as src2 + const Tmp_GR_Opnd src2_reg(context, ic); + buf = mov(buf, src2_reg, get_m_opnd(src2_loc), size_64); + buf = alu(buf, cmp_opc, *((M_Opnd *)src1), src2_reg); + } + } + delete tmp_reg; + + // 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 out(LilSig * sig) { + // nothing else to do; space has been reserved already + } + + /** + * implements the copying of incoming to outgoing args + */ + void in2out(LilSig * sig) { + assert(!lil_sig_is_arbitrary(lil_cs_get_sig(cs))); + + for (unsigned i = 0; i < context.get_num_inputs(); i++) { + const LcgEM64TLoc * in_loc = get_input(i); + const LcgEM64TLoc * out_loc = get_output(i, sig); + move_rm(out_loc, in_loc); + } + } + + void call(LilOperand * target, LilCallKind kind) { + switch (kind) { + case LCK_TailCall: { + // restore input FR & GR + unmove_inputs(); + // unwind current stack frame + epilog(); + // jump (instead of calling) + if (lil_operand_is_immed(target)) { + // check if we can perform relative call + int64 target_value = lil_operand_get_immed(target); + /* + + // TODO: relative addressing isn't supported now + // need to compute code size before emitting + + int64 offset = target_value - (int64)buf; + // sub 5 bytes for this instruction + if (fit32(offset)) { + // make a relative call + buf = jump32(buf, get_imm_opnd((int64)0, size_32)); + // label name is equal to address + char * label = (char *)mem.alloc(MAX_DEC_SIZE); + assert(sizeof(long) == sizeof(POINTER_SIZE_INT)); + sprintf(label, "%" FMT64 "d", target_value); + // offset should be patched when the stub is copied to other place + labels.define_label(label, (void *)(int64 *)target_value, false); + labels.add_patch_to_label(label, buf - 4, LPT_Rel32); + } else { + */ + // make absolute jump + buf = mov(buf, rax_opnd, get_imm_opnd(target_value, size_64), size_64); + buf = ::jump(buf, rax_opnd, size_64); + //} + } else { + const LcgEM64TLoc * loc = get_op_loc(target, false); + buf = jump(buf, get_rm_opnd(loc), size_64); + } + break; + } + case LCK_Call: + case LCK_CallNoRet: { +#ifdef _WIN64 + buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(-SHADOW), size_64); +#endif + if (lil_operand_is_immed(target)) { + // check if we can perform relative call + int64 target_value = lil_operand_get_immed(target); + + /* + + // TODO: relative addressing isn't supported now + // need to compute code size before emitting + + int64 offset = target_value - (int64)buf; + // sub 5 bytes for this instruction + if (fit32(offset)) { + // make a relative call + buf = ::call(buf, get_imm_opnd((int64)0, size_32)); + // label name is equal to address + char * label = (char *)mem.alloc(MAX_DEC_SIZE); + assert(sizeof(long) == sizeof(POINTER_SIZE_INT)); + sprintf(label, "%" FMT64 "d", target_value); + // offset should be patched when the stub is copied to other place + labels.define_label(label, (void *)(int64 *)target_value, false); + labels.add_patch_to_label(label, buf - 4, LPT_Rel32); + } else { + */ + // make absolute call + buf = mov(buf, rax_opnd, get_imm_opnd(target_value, size_64), size_64); + buf = ::call(buf, rax_opnd, size_64); + //} + } else { + const LcgEM64TLoc * loc = get_op_loc(target, false); + buf = ::call(buf, get_rm_opnd(loc), size_64); + } +#ifdef _WIN64 + buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(SHADOW), size_64); +#endif + take_inputs_from_stack = true; + break; + } + default: + ASSERT(0, "Unknown kind"); + } + } + + void ret() { + // unwind current stack frame + epilog(); + buf = ::ret(buf); + } + + // 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) { + take_inputs_from_stack = true; + + // rsp-relative offset of the top of the m2n frame + int32 offset = context.get_m2n_offset() + context.get_stk_m2n_size(); + + buf = m2n_gen_push_m2n(buf, method, current_frame_type, handles, + lil_cs_get_max_locals(cs), lil_ic_get_num_std_places(ic), offset); + } + + void m2n_save_all() { + } + + void pop_m2n() { + buf = m2n_gen_pop_m2n(buf, context.m2n_has_handles(), lil_cs_get_max_locals(cs), + // TODO: FIXME: need to define proper return registers to be preserved + context.get_m2n_offset(), 1); + // after m2n_gen_pop_m2n rsp points to the last callee-saves register + } + + void print(char * str, LilOperand * o) { + // this is a no-op if debugging is off +/* +#ifdef STUB_DEBUG + unsigned print_reg; + if (lil_operand_is_immed(o)) { + // dummy operand; print r0 + print_reg = 0; + } else { + LilVariable *var = lil_operand_get_variable(o); + const LcgEM64TLoc* var_loc = + get_var_loc(var, inst, ic, false); + assert(var_loc->kind == LLK_Gr); + print_reg = var_loc->addr; + } + emit_print_reg(emitter, str, print_reg, context.get_num_inputs(), + context.get_first_output_gr(), false); +#endif // STUB_DEBUG +*/ + } +}; + +LilCodeGeneratorEM64T::LilCodeGeneratorEM64T(): LilCodeGenerator() {} + +NativeCodePtr LilCodeGeneratorEM64T::compile_main(LilCodeStub * cs, size_t * stub_size, PoolManager* code_pool) { + // start a memory manager + tl::MemoryPool m; + // get context + LcgEM64TContext * context = new(m) LcgEM64TContext(cs, m); + // generate code + LcgEM64TCodeGen 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; +} + +GenericFunctionPointer lil_npc_to_fp(NativeCodePtr ncp) { + return (GenericFunctionPointer)ncp; +} diff --git a/vm/vmcore/src/lil/em64t/m2n_em64t.cpp b/vm/vmcore/src/lil/em64t/m2n_em64t.cpp new file mode 100644 index 0000000..a9cc5e7 --- /dev/null +++ b/vm/vmcore/src/lil/em64t/m2n_em64t.cpp @@ -0,0 +1,437 @@ +/* + * 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. + */ + +/** + * @author Evgueni Brevnov + * @version $Revision$ + */ + +#include + +#include "open/types.h" +#include "port_malloc.h" +#include "vm_threads.h" +#include "exceptions.h" + +#include "m2n.h" +#include "encoder.h" +#include "m2n_em64t_internal.h" +#include "lil_code_generator_em64t.h" + +#define LOG_DOMAIN "vm.helpers" +#include "cxxlog.h" + +/* Generic Interface */ + +void m2n_null_init(M2nFrame * m2n){ + memset(m2n, 0, sizeof(M2nFrame)); +} + +M2nFrame* m2n_get_last_frame() { + return (M2nFrame*)p_TLS_vmthread->last_m2n_frame; +} + +M2nFrame* m2n_get_last_frame(VM_thread * thread) { + return (M2nFrame*)thread->last_m2n_frame; +} + +void m2n_set_last_frame(VM_thread* thread, M2nFrame * lm2nf) { + thread->last_m2n_frame = lm2nf; +} + +void m2n_set_last_frame(M2nFrame * lm2nf) { + vm_thread_t vm_thread = jthread_self_vm_thread_unsafe(); + vm_thread->last_m2n_frame = lm2nf; +} + +M2nFrame * m2n_get_previous_frame(M2nFrame * m2nf) { + return m2nf->prev_m2nf; +} + +ObjectHandles* m2n_get_local_handles(M2nFrame * m2nf) { + return m2nf->local_object_handles; +} + +void m2n_set_local_handles(M2nFrame * m2nf, ObjectHandles * handles) { + m2nf->local_object_handles = handles; +} + +NativeCodePtr m2n_get_ip(M2nFrame * m2nf) { + return (NativeCodePtr *) m2nf->rip; +} + +void m2n_set_ip(M2nFrame * lm2nf, NativeCodePtr ip) { + lm2nf->rip = (uint64)ip; +} + +Method_Handle m2n_get_method(M2nFrame * m2nf) { + return m2nf->method; +} + +frame_type m2n_get_frame_type(M2nFrame * m2nf) { + return m2nf->current_frame_type; +} + +void m2n_set_frame_type(M2nFrame * m2nf, frame_type m2nf_type) +{ + m2nf->current_frame_type = m2nf_type; +} + +void m2n_push_suspended_frame(M2nFrame* m2nf, Registers* regs) +{ + m2n_push_suspended_frame(p_TLS_vmthread, m2nf, regs); +} + +void m2n_push_suspended_frame(VM_thread* thread, M2nFrame* m2nf, Registers* regs) +{ + assert(m2nf); + m2nf->p_lm2nf = (M2nFrame**)1; + m2nf->method = NULL; + m2nf->local_object_handles = NULL; + m2nf->current_frame_type = FRAME_UNKNOWN; + + m2nf->rip = (POINTER_SIZE_INT)regs->get_ip(); + m2nf->regs = regs; + + m2nf->prev_m2nf = m2n_get_last_frame(thread); + m2n_set_last_frame(thread, m2nf); +} + +M2nFrame* m2n_push_suspended_frame(Registers* regs) +{ + return m2n_push_suspended_frame(p_TLS_vmthread, regs); +} + +M2nFrame* m2n_push_suspended_frame(VM_thread* thread, Registers* regs) +{ + M2nFrame* m2nf = (M2nFrame*)STD_MALLOC(sizeof(M2nFrame)); + assert(m2nf); + m2n_push_suspended_frame(thread, m2nf, regs); + return m2nf; +} + +bool m2n_is_suspended_frame(M2nFrame * m2nf) { + return (uint64)m2nf->p_lm2nf == 1; + +} + +void * m2n_get_frame_base(M2nFrame * m2nf) { + return &m2nf->rip; +} + +/* Internal Interface */ + +unsigned m2n_ts_to_register_size(unsigned num_std_need_to_save, + unsigned num_ret_need_to_save) { + return 13 + (6 * num_std_need_to_save) + + 3 + (6 * num_ret_need_to_save); +} + +// rsp should point to the bottom of the activation frame since push may occur +// inputs should be preserved outside if required since we do a call +// num_std_need_to_save registers will be preserved +char * m2n_gen_ts_to_register(char * buf, const R_Opnd * reg, + unsigned num_callee_saves_used, + unsigned num_callee_saves_max, + unsigned num_std_need_to_save, + unsigned num_ret_need_to_save) { + // we can't preserve rax and return value on it at the same time + assert (num_ret_need_to_save == 0 || reg != &rax_opnd); + + +//#ifdef PLATFORM_POSIX + + // preserve std places + unsigned i; + unsigned num_std_saved = 0; + // use calle-saves registers first + while (num_std_saved < num_std_need_to_save && + (i = num_callee_saves_used + num_std_saved) < num_callee_saves_max) { + buf = mov(buf, + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::GR_LOCALS_OFFSET + i), + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved), + size_64); + ++num_std_saved; + } + // if we still have not preserved std places save them on the stack + while (num_std_saved < num_std_need_to_save) { + buf = push(buf, + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved), + size_64); + ++num_std_saved; + } + assert(num_std_saved == num_std_need_to_save); + + // preserve returns + unsigned num_ret_saved = 0; + while (num_ret_saved < num_ret_need_to_save && + (i = num_callee_saves_used + num_std_saved + num_ret_saved) < num_callee_saves_max) { + buf = mov(buf, + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::GR_LOCALS_OFFSET + i), + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved), + size_64); + ++num_ret_saved; + } + // if we still have not preserved returns save them on the stack + while (num_ret_saved < num_ret_need_to_save) { + buf = push(buf, + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::GR_RETURNS_OFFSET + num_std_saved), + size_64); + ++num_ret_saved; + } + assert(num_ret_saved == num_ret_need_to_save); + + // TODO: FIXME: only absolute addressing mode is supported now + buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)get_thread_ptr), size_64); +#ifdef _WIN64 + buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(-SHADOW)); +#endif + buf = call(buf, rax_opnd, size_64); +#ifdef _WIN64 + buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(SHADOW)); +#endif + if (reg != &rax_opnd) { + buf = mov(buf, *reg, rax_opnd, size_64); + } + + // restore returns from the stack + i = num_callee_saves_used + num_std_saved; + while (num_ret_saved > 0 && i + num_ret_saved > num_callee_saves_max) { + --num_ret_saved; + buf = pop(buf, + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved), + size_64); + } + // restore std places from the stack + while (num_std_saved > 0 && num_callee_saves_used + num_std_saved > num_callee_saves_max) { + --num_std_saved; + buf = pop(buf, + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved), + size_64); + } + // restore returns from callee-saves registers + i = num_callee_saves_used + num_std_saved; + while (num_ret_saved > 0) { + --num_ret_saved; + buf = mov(buf, + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved), + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::GR_LOCALS_OFFSET + i + num_ret_saved), + size_64); + } + // restore std places from callee-saves registers + while (num_std_saved > 0) { + --num_std_saved; + buf = mov(buf, + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved), + LcgEM64TContext::get_reg_from_map( + LcgEM64TContext::GR_LOCALS_OFFSET + num_callee_saves_used + num_std_saved), + size_64); + } +//#else //!PLATFORM_POSIX +// buf = prefix(buf, prefix_fs); +// buf = mov(buf, *reg, M_Opnd(0x14), size_64); +//#endif //!PLATFORM_POSIX + return buf; +} + +char * m2n_gen_set_local_handles_r(char * buf, unsigned bytes_to_m2n, const R_Opnd * src_reg) { + unsigned offset_local_handles = (unsigned)(uint64) &((M2nFrame*)0)->local_object_handles; + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n + offset_local_handles), *src_reg, size_64); + return buf; +} + +char * m2n_gen_set_local_handles_imm(char * buf, unsigned bytes_to_m2n, const Imm_Opnd * imm) { + unsigned offset_local_handles = (unsigned)(uint64)&((M2nFrame*)0)->local_object_handles; + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n + offset_local_handles), *imm, size_64); + return buf; +} + +unsigned m2n_push_m2n_size(unsigned num_callee_saves, + unsigned num_std_need_to_save) { + return 91 - (5 * num_callee_saves) + + m2n_ts_to_register_size(num_std_need_to_save, 0); +} + +// inputs should be preserved outside if required since we do a call +// num_std_need_to_save registers will be preserved +char * m2n_gen_push_m2n(char * buf, Method_Handle method, + frame_type current_frame_type, + bool handles, + unsigned num_callee_saves, + unsigned num_std_need_to_save, + int32 bytes_to_m2n_top) { + // skip callee-saves registers + bytes_to_m2n_top -= num_callee_saves * LcgEM64TContext::GR_SIZE; + // TODO: check if it makes sense to save all callee-saves registers here + //store rest of callee-saves registers + for (unsigned i = num_callee_saves; i < LcgEM64TContext::MAX_GR_LOCALS; i++) { + bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; + buf = mov(buf, + M_Base_Opnd(rsp_reg, bytes_to_m2n_top), + LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_LOCALS_OFFSET + i), + size_64); + } + // init pop_regs to null + bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), + Imm_Opnd(size_32, 0), size_64); + // store current_frame_type + bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; + assert(fit32(current_frame_type)); + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), + Imm_Opnd(size_32, current_frame_type), size_64); + // store a method associated with the current m2n frame + bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; + if (fit32((int64)method)) { + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), + Imm_Opnd(size_32, (int64)method), size_64); + } else { + buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (int64)method), size_64); + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), rax_opnd); + } + // store local object handles + bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), + Imm_Opnd(size_64, (int64)0), size_64); + + // move pointer to the current VM_Thread structure to rax + buf = m2n_gen_ts_to_register(buf, &rax_opnd, + num_callee_saves, LcgEM64TContext::MAX_GR_LOCALS, + num_std_need_to_save, 0); + + // shift to the last_m2n_frame field + int32 last_m2n_frame_offset = (int32)(int64)&((VM_thread*)0)->last_m2n_frame; + buf = alu(buf, add_opc, rax_opnd, Imm_Opnd(size_32, last_m2n_frame_offset), size_64); + // store pointer to pointer to last m2n frame + bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), rax_opnd, size_64); + // save pointer to the previous m2n frame + bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE; + buf = mov(buf, r9_opnd, M_Base_Opnd(rax_reg, 0)); + buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), r9_opnd, size_64); + // update last m2n frame of the current thread + buf = lea(buf, r9_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_top)); + buf = mov(buf, M_Base_Opnd(rax_reg, 0), r9_opnd, size_64); + return buf; +} + +unsigned m2n_pop_m2n_size(bool handles, unsigned num_callee_saves, unsigned preserve_ret) +{ + return 56 - 5*num_callee_saves + (preserve_ret ? 4: 0); +} + +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); +} + +static void m2n_free_local_handles() { + assert(!hythread_is_suspend_enabled()); + + if (exn_raised()) { + exn_rethrow(); + } + + M2nFrame * m2n = m2n_get_last_frame(); + free_local_object_handles3(m2n->local_object_handles); +} + +char * m2n_gen_pop_m2n(char * buf, bool handles, unsigned num_callee_saves, + int32 bytes_to_m2n_bottom, unsigned num_preserve_ret) { + assert (num_preserve_ret <= 2); + assert(LcgEM64TContext::GR_SIZE == 8); + + if (num_preserve_ret > 0) { + // Save return value + // NOTE: don't break stack allignment by pushing only one register. + buf = push(buf, rax_opnd, size_64); + buf = push(buf, rdx_opnd, size_64); + } + + if (handles) { + // There are handles located on the stack + buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)m2n_pop_local_handles), size_64); + } else { + buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)m2n_free_local_handles), size_64); + } + + // NOTE: the following should be true before the call ($rsp % 8 == 0 && $rsp % 16 != 0)! + + // Call m2n_pop_local_handles or m2n_free_local_handles +#ifdef _WIN64 + buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(-SHADOW)); +#endif + buf = call(buf, rax_opnd, size_64); +#ifdef _WIN64 + buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(SHADOW)); +#endif + + if (num_preserve_ret > 0) { + // Restore return value + buf = pop(buf, rdx_opnd, size_64); + buf = pop(buf, rax_opnd, size_64); + } + + // pop prev_m2nf + buf = mov(buf, r10_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom), size_64); + bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE; + // pop p_lm2nf + buf = mov(buf, r11_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom), size_64); + bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE; + buf = mov(buf, M_Base_Opnd(r11_reg, 0), r10_opnd, size_64); + // skip local_object_handles, method, current_frame_type, pop_regs + bytes_to_m2n_bottom += 4 * LcgEM64TContext::GR_SIZE; + + // restore part of callee-saves registers + for (int i = LcgEM64TContext::MAX_GR_LOCALS - 1; i >= (int)num_callee_saves; i--) { + buf = mov(buf, + LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_LOCALS_OFFSET + i), + M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom), + size_64); + bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE; + } + + return buf; +}//m2n_gen_pop_m2n + +// returns pointer to the registers used for jvmti PopFrame +Registers* get_pop_frame_registers(M2nFrame* m2nf) { + return m2nf->pop_regs; +} + +// sets pointer to the registers used for jvmti PopFrame +void set_pop_frame_registers(M2nFrame* m2nf, Registers* regs) { + m2nf->pop_regs = regs; +} + diff --git a/vm/vmcore/src/lil/em64t/m2n_em64t_internal.h b/vm/vmcore/src/lil/em64t/m2n_em64t_internal.h new file mode 100644 index 0000000..7ac8fc3 --- /dev/null +++ b/vm/vmcore/src/lil/em64t/m2n_em64t_internal.h @@ -0,0 +1,133 @@ +/* + * 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. + */ + +/** + * @author Evgueni Brevnov + * @version $Revision$ + */ + +#ifndef _M2N_EM64T_INTERNAL_H_ +#define _M2N_EM64T_INTERNAL_H_ + +// This file describes the internal EM64T interface of m2n frames. +// It can be used by stubs to generate code to push and pop m2n frames, to update object handles fields, and +// to access the arguments from managed to native code. +// It is also used by stack iterators. + +#include "m2n.h" +#include "open/types.h" +#include "encoder.h" + +#ifdef _WIN64 +const unsigned m2n_sizeof_m2n_frame = 120; +#else +const unsigned m2n_sizeof_m2n_frame = 104; +#endif + +typedef struct M2nFrame M2nFrame; + +/** + * There are two types of M2nFrames: those that result from managed code calling a stub, + * and those that represent suspended managed code. The second type is needed to deal with + * throwing exceptions from OS contexts with the exception filter or signal mechanisms. + * For the first type: + * rip points to the instruction past the one in question + * the bottom two bits of p_lm2nf are zero + * regs is not present, and is implicitly the address of the word above rip + * For the second type: + * rip points to the instruction in question + * p_lm2nf==1 + * regs is present + */ +struct M2nFrame { + M2nFrame * prev_m2nf; + M2nFrame ** p_lm2nf; + ObjectHandles * local_object_handles; + Method_Handle method; + frame_type current_frame_type; + Registers* pop_regs; // This is only for M2nFrames for suspended managed code (as against ones that call stubs and prepare jvmtiPopFrame) + uint64 rbx; + uint64 rbp; +#ifdef _WIN64 + uint64 rsi; + uint64 rdi; +#endif + uint64 r15; + uint64 r14; + uint64 r13; + uint64 r12; + uint64 rip; + // This is only for M2nFrames for suspended managed code (as against ones that call stubs) + Registers * regs; +}; + +/** + * returns size of m2n frame in bytes + */ +inline size_t m2n_get_size() { + return sizeof(M2nFrame); +} + +/** + * Generate code to put the thread local storage pointer into a given register. + * It destroys outputs. + */ +char * m2n_gen_ts_to_register(char * buf, const R_Opnd * reg, + unsigned num_callee_saves_used, unsigned num_callee_saves_max, + unsigned num_std_need_to_save, unsigned num_ret_need_to_save); +unsigned m2n_ts_to_register_size(unsigned num_std_need_to_save, unsigned num_ret_need_to_save); + +/** + * Generate code to set the local handles of an M2nFrame. + * The M2nFrame is located bytes_to_m2n above rsp, and src_reg has the address of the frames. + */ +char * m2n_gen_set_local_handles_r(char * buf, unsigned bytes_to_m2n, const R_Opnd * src_reg); +char * m2n_gen_set_local_handles_imm(char * buf, unsigned bytes_to_m2n, const Imm_Opnd * imm); + +/** + * Generate code to push an M2nFrame onto the stack. + * It assumes that num_callee_saves registers have already been saved and the rest have been preserved, + * that the saved registers are immediately below the return address, and that rsp points to the last one saved. + * The order for callee saves is r12, r13, r14, r15, rbp, rbx. + * It destroys returns (rax) and outputs. + * After the sequence, rsp points to the M2nFrame. + * + * @param handles Indicates whether the stub will want local handles or not + * @param bytes_to_m2n_top Number of bytes to the beginning of m2n frame relative to the current rsp value. + Negative value means that current rsp is above m2n bottom. + */ +char * m2n_gen_push_m2n(char * buf, Method_Handle method, frame_type current_frame_type, bool handles, + unsigned num_callee_saves, unsigned num_std_need_to_save, int32 bytes_to_m2n_top); +unsigned m2n_push_m2n_size(unsigned num_callee_saves, unsigned num_std_need_to_save); + +/** + * Generate code to pop an M2nFrame off the stack. + * @param num_callee_saves Number of callee saves registers to leave + * on the stack as at the entry to push_m2n. + * @param bytes_to_m2n_bottom Number of bytes between rsp and the bottom of the M2nFrame. + * @param preserve_ret Number of return registers to preserve, 0 means none, + * 1 means rax, 2 means rax & rdx. + * @param handles As for push_m2n, frees the handles if true. + */ +char * m2n_gen_pop_m2n(char * buf, bool handles, unsigned num_callee_saves, + int32 bytes_to_m2n_bottom, unsigned preserve_ret); +unsigned m2n_pop_m2n_size(bool handles, unsigned num_callee_saves, unsigned preserve_ret); + +// returns top of the specified frame on the stack (it should point to return ip) +void * m2n_get_frame_base(M2nFrame *); + +#endif // _M2N_EM64T_INTERNAL_H_ diff --git a/vm/vmcore/src/lil/em64t/stack_iterator_em64t.cpp b/vm/vmcore/src/lil/em64t/stack_iterator_em64t.cpp new file mode 100644 index 0000000..a57c909 --- /dev/null +++ b/vm/vmcore/src/lil/em64t/stack_iterator_em64t.cpp @@ -0,0 +1,543 @@ +/* + * 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. + */ +/** + * @author Evgueni Brevnov + * @version $Revision: 1.1.2.1.4.3 $ + */ + +#include + +#include "environment.h" +#include "stack_iterator.h" +#include "vm_threads.h" +#include "method_lookup.h" +#include "jit_intf_cpp.h" +#include "encoder.h" +#include "m2n.h" +#include "m2n_em64t_internal.h" +#include "nogc.h" +#include "interpreter.h" // for ASSERT_NO_INTERPRETER +#include "cci.h" + +#include "dump.h" +#include "vm_stats.h" + +#include "cxxlog.h" + +// see stack_iterator_ia32.cpp +struct StackIterator { + CodeChunkInfo * cci; + JitFrameContext jit_frame_context; + M2nFrame * m2n_frame; + uint64 ip; +}; + +////////////////////////////////////////////////////////////////////////// +// Utilities + +static void si_copy(StackIterator * dst, const StackIterator * src) { + memcpy(dst, src, sizeof(StackIterator)); + // If src uses itself for ip then the dst should also do + // to avoid problems if src is deallocated first. + if (src->jit_frame_context.p_rip == &src->ip) { + dst->jit_frame_context.p_rip = &dst->ip; + } +} + +static void init_context_from_registers(JitFrameContext & context, + Registers & regs, bool is_ip_past) { + context.rsp = regs.rsp; + context.p_rbp = ®s.rbp; + context.p_rip = ®s.rip; + + context.p_rbx = ®s.rbx; + context.p_r12 = ®s.r12; + context.p_r13 = ®s.r13; + context.p_r14 = ®s.r14; + context.p_r15 = ®s.r15; + + context.p_rax = ®s.rax; + context.p_rcx = ®s.rcx; + context.p_rdx = ®s.rdx; + context.p_rsi = ®s.rsi; + context.p_rdi = ®s.rdi; + context.p_r8 = ®s.r8; + context.p_r9 = ®s.r9; + context.p_r10 = ®s.r10; + context.p_r11 = ®s.r11; + + context.eflags = regs.eflags; + + context.is_ip_past = is_ip_past; +} + + +// Goto the managed frame immediately prior to m2nfl +static void si_unwind_from_m2n(StackIterator * si) { +#ifdef VM_STATS + VM_Statistics::get_vm_stats().num_unwind_native_frames_all++; +#endif + + M2nFrame * current_m2n_frame = si->m2n_frame; + assert(current_m2n_frame); + + si->m2n_frame = m2n_get_previous_frame(current_m2n_frame); + + TRACE2("si", "si_unwind_from_m2n, ip = " + << (void*)current_m2n_frame->rip); + + // Is it a normal M2nFrame or one for suspended managed code? + if (m2n_is_suspended_frame(current_m2n_frame)) { + // Suspended managed code, rip is at instruction, + // rsp & registers are in regs structure + TRACE2("si", "si_unwind_from_m2n from suspended managed code, ip = " + << (void*)current_m2n_frame->regs->rip); + init_context_from_registers(si->jit_frame_context, *current_m2n_frame->regs, false); + } else { + // Normal M2nFrame, rip is past instruction, + // rsp is implicitly address just beyond the frame, + // callee saves registers in M2nFrame + + si->jit_frame_context.rsp = (uint64)((uint64*) m2n_get_frame_base(current_m2n_frame) + 1); + + si->jit_frame_context.p_rbp = ¤t_m2n_frame->rbp; + si->jit_frame_context.p_rip = ¤t_m2n_frame->rip; + +#ifdef _WIN64 + si->jit_frame_context.p_rdi = ¤t_m2n_frame->rdi; + si->jit_frame_context.p_rsi = ¤t_m2n_frame->rsi; +#endif + si->jit_frame_context.p_rbx = ¤t_m2n_frame->rbx; + si->jit_frame_context.p_r12 = ¤t_m2n_frame->r12; + si->jit_frame_context.p_r13 = ¤t_m2n_frame->r13; + si->jit_frame_context.p_r14 = ¤t_m2n_frame->r14; + si->jit_frame_context.p_r15 = ¤t_m2n_frame->r15; + si->jit_frame_context.is_ip_past = true; + } +} + +static char* get_reg(char* ss, const R_Opnd & dst, Reg_No base, int64 offset, + bool check_null = false, bool preserve_flags = false) +{ + char* patch_offset = NULL; + + ss = mov(ss, dst, M_Base_Opnd(base, (int32)offset)); + + if (check_null) + { + if (preserve_flags) + *ss++ = (char)0x9C; // PUSHFD + + ss = test(ss, dst, dst); + ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0)); + patch_offset = ((char*)ss) - 1; // Store location for jump patch + } + + ss = mov(ss, dst, M_Base_Opnd(dst.reg_no(), 0)); + + if (check_null) + { + // Patch conditional jump + POINTER_SIZE_SINT offset = + (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)patch_offset - 1; + assert(offset >= -128 && offset < 127); + *patch_offset = (char)offset; + + if (preserve_flags) + *ss++ = (char)0x9D; // POPFD + } + + return ss; +} + +typedef void (* transfer_control_stub_type)(StackIterator *); + +#define CONTEXT_OFFSET(_field_) \ + ((int64)&((StackIterator*)0)->jit_frame_context._field_) + +static transfer_control_stub_type gen_transfer_control_stub() +{ + static transfer_control_stub_type addr = NULL; + + if (addr) { + return addr; + } + + const int STUB_SIZE = 247; + char * stub = (char *)malloc_fixed_code_for_jit(STUB_SIZE, + DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_COLD, CAA_Allocate); + char * ss = stub; +#ifndef NDEBUG + memset(stub, 0xcc /*int 3*/, STUB_SIZE); +#endif + + // + // ************* LOW LEVEL DEPENDENCY! *************** + // This code sequence must be atomic. The "atomicity" effect is achieved by + // changing the rsp at the very end of the sequence. + + // rdx holds the pointer to the stack iterator +#if defined (PLATFORM_POSIX) // RDI holds 1st parameter on Linux + ss = mov(ss, rdx_opnd, rdi_opnd); +#else // RCX holds 1st parameter on Windows + ss = mov(ss, rdx_opnd, rcx_opnd); +#endif + + // Restore general registers + ss = get_reg(ss, rbp_opnd, rdx_reg, CONTEXT_OFFSET(p_rbp), false); + ss = get_reg(ss, rbx_opnd, rdx_reg, CONTEXT_OFFSET(p_rbx), true); + ss = get_reg(ss, r12_opnd, rdx_reg, CONTEXT_OFFSET(p_r12), true); + ss = get_reg(ss, r13_opnd, rdx_reg, CONTEXT_OFFSET(p_r13), true); + ss = get_reg(ss, r14_opnd, rdx_reg, CONTEXT_OFFSET(p_r14), true); + ss = get_reg(ss, r15_opnd, rdx_reg, CONTEXT_OFFSET(p_r15), true); + ss = get_reg(ss, rsi_opnd, rdx_reg, CONTEXT_OFFSET(p_rsi), true); + ss = get_reg(ss, rdi_opnd, rdx_reg, CONTEXT_OFFSET(p_rdi), true); + ss = get_reg(ss, r8_opnd, rdx_reg, CONTEXT_OFFSET(p_r8), true); + ss = get_reg(ss, r9_opnd, rdx_reg, CONTEXT_OFFSET(p_r9), true); + ss = get_reg(ss, r10_opnd, rdx_reg, CONTEXT_OFFSET(p_r10), true); + ss = get_reg(ss, r11_opnd, rdx_reg, CONTEXT_OFFSET(p_r11), true); + + // Get the new RSP + M_Base_Opnd saved_rsp(rdx_reg, CONTEXT_OFFSET(rsp)); + ss = mov(ss, rax_opnd, saved_rsp); + // Store it over return address for future use + ss = mov(ss, M_Base_Opnd(rsp_reg, 0), rax_opnd); + // Get the new RIP + ss = get_reg(ss, rcx_opnd, rdx_reg, CONTEXT_OFFSET(p_rip), false); + // Store RIP to [ - 136] to preserve 128 bytes under RSP + // which are 'reserved' on Linux + ss = mov(ss, M_Base_Opnd(rax_reg, -136), rcx_opnd); + + ss = get_reg(ss, rax_opnd, rdx_reg, CONTEXT_OFFSET(p_rax), true); + + // Restore processor flags + ss = alu(ss, xor_opc, rcx_opnd, rcx_opnd); + ss = mov(ss, rcx_opnd, M_Base_Opnd(rdx_reg, CONTEXT_OFFSET(eflags)), size_8); + ss = test(ss, rcx_opnd, rcx_opnd); + ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0)); + char* patch_offset = ((char*)ss) - 1; // Store location for jump patch + ss = alu(ss, and_opc, rcx_opnd, Imm_Opnd(size_32, 0xff)); + ss = push(ss, rcx_opnd); + *ss++ = (char)0x9D; // POPFD + // Patch conditional jump + POINTER_SIZE_SINT offset = + (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)patch_offset - 1; + *patch_offset = (char)offset; + + ss = get_reg(ss, rcx_opnd, rdx_reg, CONTEXT_OFFSET(p_rcx), true, true); + ss = get_reg(ss, rdx_opnd, rdx_reg, CONTEXT_OFFSET(p_rdx), true, true); + + // Setup stack pointer to previously saved value + ss = mov(ss, rsp_opnd, M_Base_Opnd(rsp_reg, 0)); + + // Jump to address stored to [ - 136] + ss = jump(ss, M_Base_Opnd(rsp_reg, -136)); + + addr = (transfer_control_stub_type)stub; + assert(ss-stub <= STUB_SIZE); + + /* + The following code will be generated: + + mov rdx,rcx + mov rbp,qword ptr [rdx+10h] + mov rbp,qword ptr [rbp] + mov rbx,qword ptr [rdx+20h] + test rbx,rbx + je __label1__ + mov rbx,qword ptr [rbx] +__label1__ + ; .... The same for r12,r13,r14,r15,rsi,rdi,r8,r9,r10 + mov r11,qword ptr [rdx+88h] + test r11,r11 + je __label11__ + mov r11,qword ptr [r11] +__label11__ + mov rax,qword ptr [rdx+8] + mov qword ptr [rsp],rax + mov rcx,qword ptr [rdx+18h] + mov rcx,qword ptr [rcx] + mov qword ptr [rax-88h],rcx + mov rax,qword ptr [rdx+48h] + test rax,rax + je __label12__ + mov rax,qword ptr [rax] +__label12__ + xor rcx,rcx + mov ecx,dword ptr [rdx+90h] + test rcx,rcx + je __label13__ + push rcx + popfq +__label13__ + mov rcx,qword ptr [rdx+50h] + pushfq + test rcx,rcx + je __label14__ + mov rcx,qword ptr [rcx] +__label14__ + popfq + mov rdx,qword ptr [rdx+58h] + pushfq + test rdx,rdx + je __label15__ + mov rdx,qword ptr [rdx] +__label15__ + popfq + mov rsp,qword ptr [rsp] + jmp qword ptr [rsp-88h] + */ + + DUMP_STUB(stub, "getaddress__transfer_control", ss-stub); + + return addr; +} + +#undef CONTEXT_OFFSET +////////////////////////////////////////////////////////////////////////// +// Stack Iterator Interface + +StackIterator * si_create_from_native() { + return si_create_from_native(p_TLS_vmthread); +} + +void si_fill_from_native(StackIterator* si) { + si_fill_from_native(si, p_TLS_vmthread); +} + + +StackIterator * si_create_from_native(VM_thread * thread) { + ASSERT_NO_INTERPRETER + // Allocate iterator + StackIterator * si = (StackIterator *)STD_MALLOC(sizeof(StackIterator)); + + si_fill_from_native(si, thread); + return si; +} + +void si_fill_from_native(StackIterator* si, VM_thread * thread) { + memset(si, 0, sizeof(StackIterator)); + + si->cci = NULL; + si->jit_frame_context.p_rip = &si->ip; + si->m2n_frame = m2n_get_last_frame(thread); + si->ip = 0; +} + +StackIterator * si_create_from_registers(Registers * regs, bool is_ip_past, + M2nFrame * lm2nf) { + ASSERT_NO_INTERPRETER + // Allocate iterator + StackIterator * si = (StackIterator *)STD_MALLOC(sizeof(StackIterator)); + assert(si); + + si_fill_from_registers(si, regs, is_ip_past, lm2nf); + + return si; +} + +void si_fill_from_registers(StackIterator* si, Registers* regs, bool is_ip_past, M2nFrame* lm2nf) +{ + memset(si, 0, sizeof(StackIterator)); + + Global_Env *env = VM_Global_State::loader_env; + // Setup current frame + // It's possible that registers represent native code and res->cci==NULL + si->cci = env->vm_methods->find((NativeCodePtr)regs->rip, is_ip_past); + init_context_from_registers(si->jit_frame_context, *regs, is_ip_past); + + si->m2n_frame = lm2nf; + si->ip = regs->rip; +} + +size_t si_size(){ + return sizeof(StackIterator); +} + +// On EM64T all registers are preserved automatically, so this is a nop. +void si_transfer_all_preserved_registers(StackIterator *) { + ASSERT_NO_INTERPRETER + // Do nothing +} + +bool si_is_past_end(StackIterator * si) { + ASSERT_NO_INTERPRETER + // check if current position neither corresponds + // to jit frame nor to m2n frame + return si->cci == NULL && si->m2n_frame == NULL; +} + +void si_goto_previous(StackIterator * si, bool over_popped) { + ASSERT_NO_INTERPRETER + if (si_is_native(si)) { + TRACE2("si", "si_goto_previous from ip = " + << (void*)si_get_ip(si) << " (M2N)"); + if (si->m2n_frame == NULL) return; + si_unwind_from_m2n(si); + } else { + assert(si->cci->get_jit() && si->cci->get_method()); + TRACE2("si", "si_goto_previous from ip = " + << (void*)si_get_ip(si) << " (" + << method_get_name(si->cci->get_method()) + << method_get_descriptor(si->cci->get_method()) << ")"); + si->cci->get_jit()->unwind_stack_frame(si->cci->get_method(), si_get_jit_context(si)); + si->jit_frame_context.is_ip_past = TRUE; + } + + Global_Env *vm_env = VM_Global_State::loader_env; + si->cci = vm_env->vm_methods->find(si_get_ip(si), si_get_jit_context(si)->is_ip_past); +#ifndef NDEBUG + if (si_is_native(si)) { + TRACE2("si", "si_goto_previous to ip = " << (void*)si_get_ip(si) + << " (M2N)"); + } else { + TRACE2("si", "si_goto_previous to ip = " << (void*)si_get_ip(si) + << " (" << method_get_name(si->cci->get_method()) + << method_get_descriptor(si->cci->get_method()) << ")"); + } +#endif +} + +StackIterator * si_dup(StackIterator * si) { + ASSERT_NO_INTERPRETER + StackIterator * dup_si = (StackIterator *)STD_MALLOC(sizeof(StackIterator)); + si_copy(dup_si, si); + return dup_si; +} + +void si_free(StackIterator * si) { + STD_FREE(si); +} + +void* si_get_sp(StackIterator* si) { + return (void*)si->jit_frame_context.rsp; +} + +NativeCodePtr si_get_ip(StackIterator * si) { + ASSERT_NO_INTERPRETER + return (NativeCodePtr)(*si->jit_frame_context.p_rip); +} + +void si_set_ip(StackIterator * si, NativeCodePtr ip, bool also_update_stack_itself) { + if (also_update_stack_itself) { + *(si->jit_frame_context.p_rip) = (uint64)ip; + } else { + si->ip = (uint64)ip; + si->jit_frame_context.p_rip = &si->ip; + } +} + +// 20040713 Experimental: set the code chunk in the stack iterator +void si_set_code_chunk_info(StackIterator * si, CodeChunkInfo * cci) { + ASSERT_NO_INTERPRETER + assert(si); + si->cci = cci; +} + +CodeChunkInfo * si_get_code_chunk_info(StackIterator * si) { + return si->cci; +} + +JitFrameContext * si_get_jit_context(StackIterator * si) { + return &si->jit_frame_context; +} + +bool si_is_native(StackIterator * si) { + ASSERT_NO_INTERPRETER + return si->cci == NULL; +} + +M2nFrame * si_get_m2n(StackIterator * si) { + ASSERT_NO_INTERPRETER + return si->m2n_frame; +} + +void** si_get_return_pointer(StackIterator* si) +{ + return (void**)si->jit_frame_context.p_rax; +} + +void si_set_return_pointer(StackIterator * si, void ** return_value) { + // TODO: check if it is needed to dereference return_value + si->jit_frame_context.p_rax = (uint64 *)return_value; +} + +void si_transfer_control(StackIterator * si) { + // !!! NO LOGGER IS ALLOWED IN THIS FUNCTION !!! + // !!! RELEASE BUILD WILL BE BROKEN !!! + // !!! NO TRACE2, INFO, WARN, ECHO, ASSERT, ... + + // 1. Copy si to stack + StackIterator local_si; + si_copy(&local_si, si); + //si_free(si); + + // 2. Set the M2nFrame list + m2n_set_last_frame(local_si.m2n_frame); + + // 3. Call the stub + transfer_control_stub_type tcs = gen_transfer_control_stub(); + tcs(&local_si); +} + +inline static uint64 unref_reg(uint64* p_reg) { + return p_reg ? *p_reg : 0; +} + +void si_copy_to_registers(StackIterator * si, Registers * regs) { + ASSERT_NO_INTERPRETER + + regs->rsp = si->jit_frame_context.rsp; + regs->rbp = unref_reg(si->jit_frame_context.p_rbp); + regs->rip = unref_reg(si->jit_frame_context.p_rip); + + regs->rbx = unref_reg(si->jit_frame_context.p_rbx); + regs->r12 = unref_reg(si->jit_frame_context.p_r12); + regs->r13 = unref_reg(si->jit_frame_context.p_r13); + regs->r14 = unref_reg(si->jit_frame_context.p_r14); + regs->r15 = unref_reg(si->jit_frame_context.p_r15); + + regs->rax = unref_reg(si->jit_frame_context.p_rax); + regs->rcx = unref_reg(si->jit_frame_context.p_rcx); + regs->rdx = unref_reg(si->jit_frame_context.p_rdx); + regs->rsi = unref_reg(si->jit_frame_context.p_rsi); + regs->rdi = unref_reg(si->jit_frame_context.p_rdi); + regs->r8 = unref_reg(si->jit_frame_context.p_r8); + regs->r9 = unref_reg(si->jit_frame_context.p_r9); + regs->r10 = unref_reg(si->jit_frame_context.p_r10); + regs->r11 = unref_reg(si->jit_frame_context.p_r11); + + regs->eflags = si->jit_frame_context.eflags; +} + +void si_set_callback(StackIterator* si, NativeCodePtr* callback) { +#ifdef WIN32 + // Shadow memory to save 4 registers into stack, + // this is necessary for WIN64 calling conventions. + // NOTE: This file is used only for x86_64 architectures + const static uint64 red_zone_size = 0x28; +#else + const static uint64 red_zone_size = 0x88; +#endif + si->jit_frame_context.rsp = si->jit_frame_context.rsp - red_zone_size - sizeof(void*); + *((uint64*) si->jit_frame_context.rsp) = *(si->jit_frame_context.p_rip); + si->jit_frame_context.p_rip = ((uint64*)callback); +} + +void si_reload_registers() { + // Nothing to do +} diff --git a/vm/vmcore/src/lil/ia32/include/lil_code_generator_ia32.h b/vm/vmcore/src/lil/ia32/include/lil_code_generator_ia32.h new file mode 100644 index 0000000..bbd361a --- /dev/null +++ b/vm/vmcore/src/lil/ia32/include/lil_code_generator_ia32.h @@ -0,0 +1,38 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#ifndef _LIL_CODE_GENERATOR_IA32_ +#define _LIL_CODE_GENERATOR_IA32_ + +#include "lil.h" +#include "lil_code_generator.h" + +class LilCodeGeneratorIa32 : public LilCodeGenerator { + + public: + LilCodeGeneratorIa32(); + + protected: + NativeCodePtr compile_main(LilCodeStub* , size_t*, PoolManager*); +}; + +#endif // _LIL_CODE_GENERATOR_IA32_ diff --git a/vm/vmcore/src/lil/ia32/lil_code_generator_ia32.cpp b/vm/vmcore/src/lil/ia32/lil_code_generator_ia32.cpp new file mode 100644 index 0000000..71612cd --- /dev/null +++ b/vm/vmcore/src/lil/ia32/lil_code_generator_ia32.cpp @@ -0,0 +1,1489 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.2.4.5 $ + */ + + +#include +#include + +#define LOG_DOMAIN "vm.helpers" +#include "cxxlog.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" +#include "jit_runtime_support_common.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() +{ +} + +/////////////////////////////////////// +// Prepass information + +enum LcgIa32OpLocType { LOLT_Stack, LOLT_Reg, LOLT_Immed, LOLT_Tofs }; + +struct LcgIa32OpLoc { + LcgIa32OpLocType t; + union { + unsigned v; // Offset for stack, value for immediate + struct { + R_Opnd* r1; + R_Opnd* r2; + } r; // For register + } u; +}; + +// 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; +}; + +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; +}; + +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; +}; + +// Push arguments left to right or right to left +enum LilArgOrder { LAO_L2r, LAO_R2l }; + +struct LcgIa32CcInfo { + LilArgOrder arg_order; + bool callee_pop; +}; + +struct LcgIa32Context { + LcgIa32PrePassInfo* info; + LilInstructionContext* ctxt; + LcgIa32CcInfo entry_cc; + LilSig* entry_sig; + LcgIa32CcInfo out_cc; +}; + + +/////////////////////////////////////// +// Stack layout stuff + +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"); + } +} + +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(;;); + } +} + +static unsigned type_number_regs(LilType t) +{ + return (t==LT_Void ? 0 : type_in_two_regs(t) ? 2 : 1); +} + +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); + } +} + +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(;;); + } +} + +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; +} + +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; +} + +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; +} + +/////////////////////////////////////// +// Operand conversion + +// 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; + + 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(); + } + + 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(;;); + } +} + +// 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 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); + } + 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: + { + 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; + } + 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"); + } +} + +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); + } +} + +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; + } + } + + 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; + } + } + + 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); +} + +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; +} + +////////////////////////////////////////////////////////////////////////// +// Pre Pass + +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)); + } + + 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; + } + + void label(LilLabel UNREF l) { } + void locals(unsigned) { } + void std_places(unsigned) { } + + 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); + } + + 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++); + } + ii->mov_to_dst = true; + } + } + + 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); + } + 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"); + } + } + 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); + } + + 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)); + } + + 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); + } + + 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 + } + + 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); + } + + 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 + } + + void j(LilLabel) + { + info->size += 2; + js++; + } + + 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; + } + 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:; + } + } + 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); + } + + 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; + } + + void call(LilOperand* o, LilCallKind k) + { + operand_to_location(&ii->loc1, &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); + } + break;} + case LCK_CallNoRet: + info->size += 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++; + } + + void push_m2n(Method_Handle UNREF method, frame_type UNREF current_frame_type, bool handles) + { + m2n_ops++; + info->size += m2n_push_m2n_size(handles, 4); + } + + void m2n_save_all() + { + // This is a nop on IA32 + } + + void pop_m2n() + { + m2n_ops++; + unsigned num = m2n_base(&ctxt); + switch (lil_ic_get_ret_type(ctxt.ctxt)) { + case LT_Void: + case LT_F4: + case LT_F8: + ii->u.pop_m2n = 0; + break; + case LT_G8: + ii->u.pop_m2n = 2; + break; + case LT_G1: + case LT_G2: + case LT_G4: + case LT_Ref: + case LT_PInt: + ii->u.pop_m2n = 1; + break; + default: ASSERT(0, "Unknown LIL type"); + } + 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; + } + + 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; + } + +private: + tl::MemoryPool* mem; + LilCodeStub* cs; + LcgIa32PrePassInfo* info; + LcgIa32Context ctxt; + LcgIa32InstInfo* ii; + unsigned rets, m2n_ops, js, jcs, tailcalls; +}; + +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(); + } + + ppv.finalise_size(); + + *data = info; + return info->size; +} + +////////////////////////////////////////////////////////////////////////// +// Movement + +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(;;); + } +} + +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"); + } +} + +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"); + } +} + +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(;;); + } +} + +////////////////////////////////////////////////////////////////////////// +// 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) + { + 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)); + } + + 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); + } + + void locals(unsigned UNREF num) + { + // nothing to do; everything is taken care of by get_num_regs_for_locals + } + + void std_places(unsigned UNREF num) + { + // nothing to do; everything is taken care of by get_num_regs_for_locals + } + + 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 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)); + } + } + 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)); + 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"); + } + 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)); + 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); + } + 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"); + } + break; + default: ASSERT(0, "Unexpected operartion"); + } + 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) + { + *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); + } + + void handles(LilOperand*) + { + 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); + } + + 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); + 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); + } + 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 + } + 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)); + } 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)); + } + } + + 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* UNREF base, unsigned UNREF scale, LilVariable* UNREF index, POINTER_SIZE_SINT UNREF offset, LilAcqRel, + LilOperand* UNREF cmp, LilOperand* UNREF src, LilLabel l) + { + *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); + } + + 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 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)); + } + } + } 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"); + } + } + 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)); + } + } + + 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 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"); + } + } + + void call(LilOperand* UNREF o, LilCallKind k) + { + 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)); + } + } + 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"); + } + break; + default: ASSERT(0, "Unexpected call kind"); + } + } + + 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)); + } + } + + 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); + } + + void ret() + { + adjust_stack_for_return(); + unsigned sz = sig_size_on_stack(ctxt.entry_sig); + + if (ctxt.entry_cc.callee_pop) { + if (lil_sig_get_cc(ctxt.entry_sig) == LCC_Managed) { + // Managed calling convention assumes callee responsibility to + // handle alignment properly. Assuming that arguments were aligned, + // size of input arguments plus return pointer on the stack also should be aligned + sz += sizeof(POINTER_SIZE_INT); + sz = (sz + (MANAGED_STACK_ALIGNMENT - 1)) & ~(MANAGED_STACK_ALIGNMENT - 1); + sz -= sizeof(POINTER_SIZE_INT); + } + if (sz != 0) { + *buf = ::ret(*buf, Imm_Opnd(sz)); + return; + } + } + *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); + } + + void m2n_save_all() + { + // This is a nop on IA32 + } + + 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 print(char *, LilOperand *) { + // not implemented on ia32 + } + + 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) +{ + 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; +} + + +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 p) +{ + return (GenericFunctionPointer)p; +} diff --git a/vm/vmcore/src/lil/ia32/m2n_ia32.cpp b/vm/vmcore/src/lil/ia32/m2n_ia32.cpp new file mode 100644 index 0000000..7ee3e49 --- /dev/null +++ b/vm/vmcore/src/lil/ia32/m2n_ia32.cpp @@ -0,0 +1,329 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + +#include "open/types.h" +#include "open/hythread.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 "exceptions.h" + + +////////////////////////////////////////////////////////////////////////// +// M2nFrame Interface + +//***** Generic Interface + +// fill m2n frame as empty +void m2n_null_init(M2nFrame* m2n){ + memset(m2n, 0, sizeof(M2nFrame)); +} + +VMEXPORT // temporary solution for interpreter unplug +M2nFrame* m2n_get_last_frame() +{ + return (M2nFrame*)p_TLS_vmthread->last_m2n_frame; +} + +VMEXPORT // temporary solution for interpreter unplug +M2nFrame* m2n_get_last_frame(VM_thread* thread) +{ + return (M2nFrame*)thread->last_m2n_frame; +} + +VMEXPORT // temporary solution for interpreter unplug +void m2n_set_last_frame(M2nFrame* lm2nf) +{ + vm_thread_t vm_thread = jthread_self_vm_thread_unsafe(); + vm_thread->last_m2n_frame = lm2nf; +} + +VMEXPORT +void m2n_set_last_frame(VM_thread* thread, M2nFrame* lm2nf) +{ + thread->last_m2n_frame = lm2nf; +} + +VMEXPORT // temporary solution for interpreter unplug +M2nFrame* m2n_get_previous_frame(M2nFrame* lm2nf) +{ + return lm2nf->prev_m2nf; +} + +VMEXPORT // temporary solution for interpreter unplug +ObjectHandles* m2n_get_local_handles(M2nFrame* lm2nf) +{ + return lm2nf->local_object_handles; +} + +VMEXPORT // temporary solution for interpreter unplug +void m2n_set_local_handles(M2nFrame* lm2nf, ObjectHandles* h) +{ + lm2nf->local_object_handles = h; +} + +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; +} + +Method_Handle m2n_get_method(M2nFrame* m2nf) +{ + ASSERT_NO_INTERPRETER + return m2nf->method; +} + +// Returns type of noted m2n frame +frame_type m2n_get_frame_type(M2nFrame* m2nf) { + return m2nf->current_frame_type; +} + +// Sets type of noted m2n frame +void m2n_set_frame_type(M2nFrame* m2nf, frame_type m2nf_type) { + m2nf->current_frame_type = m2nf_type; +} + +size_t m2n_get_size() { + return sizeof(M2nFrame); +} + +void m2n_push_suspended_frame(M2nFrame* m2nf, Registers* regs) +{ + m2n_push_suspended_frame(p_TLS_vmthread, m2nf, regs); +} + +void m2n_push_suspended_frame(VM_thread* thread, M2nFrame* m2nf, Registers* regs) +{ + assert(m2nf); + m2nf->p_lm2nf = (M2nFrame**)1; + m2nf->method = NULL; + m2nf->local_object_handles = NULL; + m2nf->current_frame_type = FRAME_UNKNOWN; + + m2nf->eip = regs->eip; + m2nf->regs = regs; + + m2nf->prev_m2nf = m2n_get_last_frame(thread); + m2n_set_last_frame(thread, m2nf); +} + +M2nFrame* m2n_push_suspended_frame(Registers* regs) +{ + return m2n_push_suspended_frame(p_TLS_vmthread, regs); +} + +M2nFrame* m2n_push_suspended_frame(VM_thread* thread, Registers* regs) +{ + M2nFrame* m2nf = (M2nFrame*)STD_MALLOC(sizeof(M2nFrame)); + assert(m2nf); + m2n_push_suspended_frame(thread, m2nf, regs); + return m2nf; +} + +bool m2n_is_suspended_frame(M2nFrame * m2nf) { + return (uint32)m2nf->p_lm2nf == 1; + +} + +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; +} + +unsigned m2n_ts_to_register_size() +{ + return 22; +} + +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, *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; +} + +char* m2n_gen_push_m2n(char* buf, Method_Handle method, frame_type current_frame_type, bool UNREF handles, unsigned num_callee_saves) +{ + // 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 ); + + return buf; +} + +unsigned m2n_set_local_handles_size(unsigned bytes_to_m2n) +{ + unsigned num = bytes_to_m2n + (unsigned)&((M2nFrame*)0)->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; + buf = mov(buf, M_Base_Opnd(esp_reg, bytes_to_m2n+offset_local_handles), *src_reg); + return buf; +} + +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; +} + +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); +} + +static void m2n_free_local_handles() { + assert(!hythread_is_suspend_enabled()); + + if (exn_raised()) { + exn_rethrow(); + } + + M2nFrame * m2n = m2n_get_last_frame(); + 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); + } + } + + if (handles) { + // There are handles located on the stack + buf = call(buf, (char*)m2n_pop_local_handles); + } else { + buf = call(buf, (char*)m2n_free_local_handles); + } + + if (preserve_ret > 0) { + // Restore return value + if (preserve_ret > 1) { + buf = pop(buf, edx_opnd); + } + buf = pop(buf, eax_opnd); + } + + // pop "garbage" from the stack + if (extra_on_stack) { + Imm_Opnd imm(extra_on_stack); + buf = alu(buf, add_opc, esp_opnd, imm); + } + + // Unlink the M2nFrame from the list of the current thread + buf = pop(buf, esi_opnd); + buf = pop(buf, ebx_opnd); + buf = mov(buf, M_Base_Opnd(ebx_reg, +0), esi_opnd); + buf = alu(buf, add_opc, esp_opnd, Imm_Opnd(+16)); + + // TODO: check if there is no need to restore callee saved registers + // JUSTIFICATION: m2n frame is popped as a result of "normal" + // (opposite to destuctive) stack unwinding + // Restore callee saved general registers + if (num_callee_saves<4) buf = pop(buf, edi_opnd); + if (num_callee_saves<3) buf = pop(buf, esi_opnd); + if (num_callee_saves<2) buf = pop(buf, ebx_opnd); + if (num_callee_saves<1) buf = pop(buf, ebp_opnd); + + return buf; +} + +// returns pointer to the registers used for jvmti PopFrame +Registers* get_pop_frame_registers(M2nFrame* m2nf) { + return m2nf->pop_regs; +} + +// sets pointer to the registers used for jvmti PopFrame +void set_pop_frame_registers(M2nFrame* m2nf, Registers* regs) { + m2nf->pop_regs = regs; +} + diff --git a/vm/vmcore/src/lil/ia32/m2n_ia32_internal.h b/vm/vmcore/src/lil/ia32/m2n_ia32_internal.h new file mode 100644 index 0000000..40e00d4 --- /dev/null +++ b/vm/vmcore/src/lil/ia32/m2n_ia32_internal.h @@ -0,0 +1,108 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#ifndef _M2N_IA32_INTERNAL_H_ +#define _M2N_IA32_INTERNAL_H_ + +// This file describes the internal IPF interface of m2n frames. +// It can be used by stubs to generate code to push and pop m2n frames, to update object handles fields, and +// to access the arguments from managed to native code. +// It is also used by stack iterators. + +#include "m2n.h" +#include "vm_threads.h" +#include "open/types.h" + +class R_Opnd; + +// Return a pointer to the argument just above the return address of an M2nFrame +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(); +char* m2n_gen_ts_to_register(char* buf, R_Opnd* reg); + +// Generate code to push an M2nFrame onto the stack. +// It assumes that num_callee_saves registers have already been saved and the rest have been preserved, that the saved registers are immediately +// below the return address, and that esp points to the last one saved. The order for callee saves is ebp, ebx, esi, edi. +// It destroys eax. +// 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); + +// 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. +unsigned m2n_set_local_handles_size(unsigned bytes_to_m2n); +char* m2n_gen_set_local_handles(char* buf, unsigned bytes_to_m2n, R_Opnd* src_reg); + +// Generate code to pop an M2nFrame off the stack. +// num_callee_saves: the number of callee saves registers to leave on the stack as at the entry to push_m2n. +// extra_on_stack: the number of bytes between esp and the bottom of the M2nFrame. +// 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); + +////////////////////////////////////////////////////////////////////////// +// Implementation details + +// This information is used by the m2n implementation and the stack iterator implementation. +// It may not be used by any other module. + +// There are two types of M2nFrames: those that result from managed code calling a stub, and those that represent suspended managed code. +// The second type is needed to deal with throwing exceptions from OS contexts with the exception filter or signal mechanisms. +// For the first type: +// eip points to the instruction past the one in question (ie is the return address into managed code) +// the bottom two bits of p_lm2nf are zero +// regs is not present, and is implicitly the address of the word above eip +// For the second type: +// eip points to the instruction in question +// p_lm2nf==1 +// regs is present + +#ifdef _EM64T_ +#error Wrong header file. +#endif + +struct M2nFrame { + M2nFrame* prev_m2nf; + M2nFrame** p_lm2nf; + ObjectHandles* local_object_handles; + 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) + uint32 edi; + uint32 esi; + uint32 ebx; + uint32 ebp; + uint32 eip; + Registers* regs; // This is only for M2nFrames for suspended managed code (as against ones that call stubs and prepare jvmtiPopFrame) +}; + +#endif //!_M2N_IA32_INTERNAL_H_ diff --git a/vm/vmcore/src/lil/ia32/stack_iterator_ia32.cpp b/vm/vmcore/src/lil/ia32/stack_iterator_ia32.cpp new file mode 100644 index 0000000..9236205 --- /dev/null +++ b/vm/vmcore/src/lil/ia32/stack_iterator_ia32.cpp @@ -0,0 +1,541 @@ +/* + * 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. + */ + +/** + * @author Intel, Pavel Afremov + * @version $Revision$ + */ + +#include "environment.h" +#include "jit_intf_cpp.h" +#include "m2n.h" +#include "m2n_ia32_internal.h" +#include "nogc.h" +#include "method_lookup.h" +#include "stack_iterator.h" +#include "vm_stats.h" +#include "open/types.h" +#include "encoder.h" +#include "interpreter.h" +#include "cci.h" + +#include "clog.h" + +#include "dump.h" +#include "port_threadunsafe.h" + +// Invariants: +// Native frames: +// cci should be NULL +// m2nfl should point to the m2n frame list for the native frame +// c.p_eip should point to an address that is not a valid IP for managed code +// Managed frames: +// cci should point to the code chunk info for the method and ip in question +// m2nfl should point to the m2n frame immediately preceeding the current one or NULL is there is no preceeding m2n frame +// the callee saves registers should point to their values at the time the frame was suspended +// for frames suspended at noncall sites, the caller saves registers should point to their values at the time of suspension +// c.p_eip and c.esp should point-to/have their values at the time the frame was suspended +// c.p_eax is also valid for returning a pointer in transfer control + +struct StackIterator { + CodeChunkInfo* cci; + JitFrameContext c; + M2nFrame* m2nfl; + uint32 ip; +}; + +////////////////////////////////////////////////////////////////////////// +// Utilities + +// Goto the managed frame immediately prior to m2nfl +static void si_unwind_from_m2n(StackIterator* si, bool over_popped = true) +{ +#ifdef VM_STATS + UNSAFE_REGION_START + VM_Statistics::get_vm_stats().num_unwind_native_frames_all++; + UNSAFE_REGION_END +#endif + + M2nFrame* m2nfl = si->m2nfl; + assert(m2nfl); + + si->m2nfl = m2n_get_previous_frame(m2nfl); + + 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) { + // 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", + (void*)m2nfl->regs->eip)); + si->c.esp = m2nfl->regs->esp; + si->c.p_eip = &(m2nfl->regs->eip); + si->c.is_ip_past = FALSE; + si->c.p_eax = &m2nfl->regs->eax; + si->c.p_ebx = &m2nfl->regs->ebx; + si->c.p_ecx = &m2nfl->regs->ecx; + si->c.p_edx = &m2nfl->regs->edx; + si->c.p_esi = &m2nfl->regs->esi; + si->c.p_edi = &m2nfl->regs->edi; + si->c.p_ebp = &m2nfl->regs->ebp; + si->c.eflags = m2nfl->regs->eflags; + } else if (over_popped && + (FRAME_MODIFIED_STACK == (FRAME_MODIFIED_STACK & m2n_get_frame_type(m2nfl)))) { + si->c.esp = m2nfl->pop_regs->esp; + si->c.p_eip = &(m2nfl->pop_regs->eip); + si->c.is_ip_past = FALSE; + si->c.p_eax = &m2nfl->pop_regs->eax; + si->c.p_ebx = &m2nfl->pop_regs->ebx; + si->c.p_ecx = &m2nfl->pop_regs->ecx; + si->c.p_edx = &m2nfl->pop_regs->edx; + si->c.p_esi = &m2nfl->pop_regs->esi; + si->c.p_edi = &m2nfl->pop_regs->edi; + si->c.p_ebp = &m2nfl->pop_regs->ebp; + si->c.eflags = m2nfl->pop_regs->eflags; + } 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; + si->c.p_eip = &m2nfl->eip; + si->c.is_ip_past = TRUE; + si->c.p_edi = &m2nfl->edi; + si->c.p_esi = &m2nfl->esi; + si->c.p_ebx = &m2nfl->ebx; + si->c.p_ebp = &m2nfl->ebp; + } +} + +static char* get_reg(char* ss, R_Opnd* dst, Reg_No dst_reg, 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, 0)); + return ss; +} + +typedef void (__cdecl *transfer_control_stub_type)(StackIterator*); + +static transfer_control_stub_type gen_transfer_control_stub() +{ + static transfer_control_stub_type addr = NULL; + if (addr) { + return addr; + } + + const int stub_size = 0x4d; + 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. + // + + M_Base_Opnd m1(esp_reg, 4); + ss = mov(ss, edx_opnd, m1); + + ss = get_reg(ss, &ebx_opnd, ebx_reg, edx_reg, (unsigned)&((StackIterator*)0)->c.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)); + 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 = alu(ss, and_opc, ecx_opnd, Imm_Opnd(size_32, 0xff)); + ss = push(ss, ecx_opnd); + *ss++ = (char)0x9D; // POPFD + // 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); + + ss = mov(ss, esp_opnd, m1); + 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); + + return addr; +} + +////////////////////////////////////////////////////////////////////////// +// Stack Iterator Interface + +StackIterator* si_create_from_native() +{ + return si_create_from_native(p_TLS_vmthread); +} + +void si_fill_from_native(StackIterator* si) +{ + si_fill_from_native(si, p_TLS_vmthread); +} + +StackIterator* si_create_from_native(VM_thread* thread) +{ + ASSERT_NO_INTERPRETER + // Allocate iterator + StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); + assert(res); + + // Setup current frame + si_fill_from_native(res, thread); + + return res; +} + +void si_fill_from_native(StackIterator* si, VM_thread* thread) +{ + memset(si, 0, sizeof(StackIterator)); + + // Setup current frame + si->cci = NULL; + si->m2nfl = m2n_get_last_frame(thread); + si->ip = 0; + si->c.p_eip = &si->ip; +} + + +StackIterator* si_create_from_registers(Registers* regs, bool is_ip_past, M2nFrame* lm2nf) +{ + // Allocate iterator + StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); + assert(res); + + si_fill_from_registers(res, regs, is_ip_past, lm2nf); + + return res; +} +void si_fill_from_registers(StackIterator* si,Registers* regs, bool is_ip_past, M2nFrame* lm2nf) +{ + memset(si, 0, sizeof(StackIterator)); + + // Setup current frame + Global_Env *env = VM_Global_State::loader_env; + // It's possible that registers represent native code and si->cci==NULL + si->cci = env->vm_methods->find((NativeCodePtr)regs->eip, is_ip_past); + si->c.esp = regs->esp; + si->c.p_eip = ®s->eip; + si->c.p_ebp = ®s->ebp; + si->c.p_edi = ®s->edi; + si->c.p_esi = ®s->esi; + si->c.p_ebx = ®s->ebx; + si->c.p_eax = ®s->eax; + si->c.p_ecx = ®s->ecx; + si->c.p_edx = ®s->edx; + si->c.is_ip_past = is_ip_past; + si->c.eflags = regs->eflags; + si->m2nfl = lm2nf; +} + +size_t si_size(){ + return sizeof(StackIterator); +} + +// On IA32 all registers are preserved automatically, so this is a nop. +void si_transfer_all_preserved_registers(StackIterator*) +{ + // Do nothing +} + +bool si_is_past_end(StackIterator* si) +{ + ASSERT_NO_INTERPRETER + return si->cci==NULL && si->m2nfl==NULL; +} + +void si_goto_previous(StackIterator* si, bool over_popped) +{ + ASSERT_NO_INTERPRETER + if (si->cci) { + TRACE2("si", ("si_goto_previous from ip = %p (%s%s)", + (void*)si_get_ip(si), + method_get_name(si->cci->get_method()), + method_get_descriptor(si->cci->get_method()))); + assert(si->cci->get_jit() && si->cci->get_method()); + si->cci->get_jit()->unwind_stack_frame(si->cci->get_method(), si_get_jit_context(si)); + si->c.is_ip_past = TRUE; + } else { + TRACE2("si", ("si_goto_previous from ip = %p (M2N)", + (void*)si_get_ip(si))); + if (!si->m2nfl) return; + si_unwind_from_m2n(si, over_popped); + } + Global_Env *vm_env = VM_Global_State::loader_env; + si->cci = vm_env->vm_methods->find(si_get_ip(si), si_get_jit_context(si)->is_ip_past); + if (si->cci) { + TRACE2("si", ("si_goto_previous to ip = %p (%s%s)", + (void*)si_get_ip(si), + method_get_name(si->cci->get_method()), + method_get_descriptor(si->cci->get_method()))); + } else { + TRACE2("si", ("si_goto_previous to ip = %p (M2N)", + (void*)si_get_ip(si))); + } +} + +StackIterator* si_dup(StackIterator* si) +{ + ASSERT_NO_INTERPRETER + StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); + memcpy(res, si, sizeof(StackIterator)); + // If si uses itself for IP then res should also to avoid problems if si is deallocated first. + if (si->c.p_eip == &si->ip) + res->c.p_eip = &res->ip; + return res; +} + +void si_free(StackIterator* si) +{ + STD_FREE(si); +} + +void* si_get_sp(StackIterator* si) { + return (void*)si->c.esp; +} + +NativeCodePtr si_get_ip(StackIterator* si) +{ + ASSERT_NO_INTERPRETER + return (NativeCodePtr)*si->c.p_eip; +} + +void si_set_ip(StackIterator* si, NativeCodePtr ip, bool also_update_stack_itself) +{ + if (also_update_stack_itself) { + *(si->c.p_eip) = (uint32)ip; + } else { + si->ip = (uint32)ip; + si->c.p_eip = &si->ip; + } +} + +// Set the code chunk in the stack iterator +void si_set_code_chunk_info(StackIterator* si, CodeChunkInfo* cci) +{ + ASSERT_NO_INTERPRETER + assert(si); + si->cci = cci; +} + +CodeChunkInfo* si_get_code_chunk_info(StackIterator* si) +{ + return si->cci; +} + +JitFrameContext* si_get_jit_context(StackIterator* si) +{ + return &si->c; +} + +bool si_is_native(StackIterator* si) +{ + ASSERT_NO_INTERPRETER + return si->cci==NULL; +} + +M2nFrame* si_get_m2n(StackIterator* si) +{ + ASSERT_NO_INTERPRETER + return si->m2nfl; +} + +void** si_get_return_pointer(StackIterator* si) +{ + return (void**) si->c.p_eax; +} + +void si_set_return_pointer(StackIterator* si, void** return_value) +{ + si->c.p_eax = (uint32*)return_value; +} + +#ifdef _WIN32 + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4733) // Inline asm assigning to 'FS:0' : handler not registered as safe handler +#endif +/** + * Temporary workaround and detection of problem. + * + * This is a hack function to detect problems with C++ object living on + * while destructive unwinding is used. Known problem is logger in release + * mode. No (even potential) logging or other c++ objects created on stack + * is alowed in any functions in the stack when calling destructive unwinding. + * + * The function prints warning message and corrects exception handlers if + * the objects exist. + * + * See also function: JIT_execute_method_default + */ +static void cpp_check_last_frame() +{ + void *handler; + __asm { + mov eax, fs:[0] + mov handler, eax + } + + void *lastFrame = p_TLS_vmthread->lastFrame; + + TRACE2("exn",(" curr = 0x%p\n", lastFrame)); + TRACE2("exn",(" handler=0x%p\n", handler)); + + if (!(handler < lastFrame)) { + TRACE2("exn",("all ok\n")); + return; + } + + // NO CXX LOGGER PLEASE! doesn't work with destructive unwinding! + INFO2("exn", ("ERROR: Destructive unwinding: C++ objects detected on stack!\n")); + + while(handler < lastFrame) { + INFO2("exn", (" droping 0x%p\n", handler)); + handler = *(int**)handler; + } + INFO2("exn", (" setting curr 0x%p\n", handler)); + __asm { + mov eax, handler + mov fs:[0], eax + } +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif // _WIN32 + +void si_transfer_control(StackIterator* si) +{ +/* !!!! NO CXX LOGGER IS ALLOWED IN THIS FUNCTION !!! + * !!!! RELEASE BUILD WILL BE BROKEN !!!*/ + // 1. Copy si to stack + void* null_pointer = NULL; + StackIterator local_si; + memcpy(&local_si, si, sizeof(StackIterator)); + + if (NULL == si->c.p_eax) + local_si.c.p_eax = (uint32*)&null_pointer; + if (NULL == si->c.p_ebx) + local_si.c.p_ebx = (uint32*)&null_pointer; + if (NULL == si->c.p_ecx) + local_si.c.p_ecx = (uint32*)&null_pointer; + if (NULL == si->c.p_edx) + local_si.c.p_edx = (uint32*)&null_pointer; + + if (si->c.p_eip == &si->ip) + local_si.c.p_eip = &local_si.ip; + //si_free(si); + + // 2. Set the M2nFrame list + m2n_set_last_frame(local_si.m2nfl); +#ifdef _WIN32 // Workaround and detection of possible problems with + // objects on stack. + cpp_check_last_frame(); +#endif // _WIN32 + + TRACE2("exn", ("generating control transfer stub")); + // 3. Call the stub + transfer_control_stub_type tcs = gen_transfer_control_stub(); + TRACE2("exn", ("tcs")); + tcs(&local_si); +} + +inline static uint32 unref_reg(uint32* p_reg) { + return p_reg ? *p_reg : 0; +} +void si_copy_to_registers(StackIterator* si, Registers* regs) +{ + ASSERT_NO_INTERPRETER + + regs->esp = si->c.esp; + regs->eflags = si->c.eflags; + regs->eip = unref_reg(si->c.p_eip); + regs->ebp = unref_reg(si->c.p_ebp); + regs->edi = unref_reg(si->c.p_edi); + regs->esi = unref_reg(si->c.p_esi); + regs->edx = unref_reg(si->c.p_edx); + regs->ecx = unref_reg(si->c.p_ecx); + regs->ebx = unref_reg(si->c.p_ebx); + regs->eax = unref_reg(si->c.p_eax); +} + +void si_set_callback(StackIterator* si, NativeCodePtr* callback) { + si->c.esp = si->c.esp - 4; + *((uint32*) si->c.esp) = *(si->c.p_eip); + si->c.p_eip = ((uint32*)callback); +} + +void si_reload_registers() +{ + // Do nothing +} diff --git a/vm/vmcore/src/lil/ipf/include/lil_code_generator_ipf.h b/vm/vmcore/src/lil/ipf/include/lil_code_generator_ipf.h new file mode 100644 index 0000000..3c712e0 --- /dev/null +++ b/vm/vmcore/src/lil/ipf/include/lil_code_generator_ipf.h @@ -0,0 +1,43 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#ifndef _LIL_CODE_GENERATOR_IPF_ +#define _LIL_CODE_GENERATOR_IPF_ + +#include "lil.h" +#include "lil_code_generator.h" + +// this class is for internal use only; it is declared in +// lil_code_generator_ipf.cpp +class LcgIpfContext; +class Merced_Code_Emitter; + + +class LilCodeGeneratorIpf : public LilCodeGenerator { +public: + LilCodeGeneratorIpf(); + +protected: + NativeCodePtr compile_main(LilCodeStub* , size_t*, PoolManager*); +}; + +#endif // _LIL_CODE_GENERATOR_IPF_ diff --git a/vm/vmcore/src/lil/ipf/lil_code_generator_ipf.cpp b/vm/vmcore/src/lil/ipf/lil_code_generator_ipf.cpp new file mode 100644 index 0000000..c9772f7 --- /dev/null +++ b/vm/vmcore/src/lil/ipf/lil_code_generator_ipf.cpp @@ -0,0 +1,2163 @@ +/* + * 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. + */ + +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision$ + */ + +#include +#include + +#define LOG_DOMAIN "vm.helpers" +#include "cxxlog.h" + +#include "lil.h" +#include "lil_code_generator.h" +#include "lil_code_generator_ipf.h" +#include "lil_code_generator_utils.h" +#include "m2n.h" +#include "m2n_ipf_internal.h" +#include "tl/memory_pool.h" +#include "vm_ipf.h" +#include "vm_threads.h" +#include "stub_code_utils.h" +#include "open/vm_util.h" +#include "environment.h" +#include "port_malloc.h" + +#ifndef NDEBUG +#include "dump.h" +#endif +/************************ + * Where everything is + * (up = higher addresses / reg numbers) + * Stacked registers: + * + * |----------------------------| + * | outputs | + * |----------------------------| + * | ar.pfs, b0, sp (if needed) | + * |----------------------------| + * | locals | + * |----------------------------| + * | m2n frame (if needed) | + * |----------------------------| + * | inputs (8 spaces) | + * r32 |----------------------------| + * + * Other registers: + * - standard places go into r17-r18 + * - return values go into r8 or f8 + * - fp arguments go into f8-f15 + * - fp locals go into scratch registers if the stub makes no ordinary calls, otherwise onto the memory stack + * - r3 is used for add-long-immediate + * - r26-31 are used as temporaries in arithmetic operations + * + * Memory stack frame: + * + * |------------------------| + * | extra inputs | + * prev sp+16 |========================| + * | Local FR area | + * |------------------------| + * | FR input save area | + * |------------------------| + * | dyn alloc | + * |------------------------| + * | extra saves area | + * |------------------------| + * | extra outputs | + * sp+16 |------------------------| + * | scratch | + * sp |========================| + * + * All pieces of the stack are 16-byte aligned! + * + * If the stub executes calls other than tailcall and call.noret, + * inputs residing in FRs are moved to memory, to avoid being overwritten + * by FR outputs and return values and/or being used as scratch in the + * callee. In addition local floating point variables are put on the stack + * rather than in scratch registers. + * + * Things that are saved right after entry / restored right before ret: + * - ar.pfs (by the alloc instruction), in the stacked regs + * - b0 (only if the stub executes calls), in the stacked regs + * + * Things that are saved right before a call and restored right after a call + * (except for call.noret): + * - sp + * - gp, if needed + */ + + +// maximum number of locals. Increase it if needed, but not more than 80 or so. +#define MAX_LOCALS 10 + +// maximum number of stand places. Probably upper limit here is around 16. +#define MAX_STD_PLACES 2 + +// rounds up an integer value to the closest multiple of 16 +static inline unsigned align_16(unsigned n) { + return n = (n + 0xF) & ~0xF; +} + + + +// an enum indicating a variable's location: in a register class or on the +// stack (no LIL variable is ever on the heap!) +enum LcgIpfLocKind { + LLK_Gr, LLK_Fr, LLK_Stk, LLK_Stk16 +}; + + +// location of a LIL variable +struct LcgIpfLoc { + void *operator new(size_t sz, tl::MemoryPool& m) { + return m.alloc(sz); + } + void operator delete (void *p, tl::MemoryPool& m) { } + + LcgIpfLocKind kind; + int addr; // register number or SP-relative offset + + LcgIpfLoc(LcgIpfLocKind k, int a): kind(k), addr(a) + {} + + bool operator==(const LcgIpfLoc& loc) { + return (kind == loc.kind && addr == loc.addr); + } + + bool operator!=(const LcgIpfLoc& loc) { + return (kind != loc.kind || addr != loc.addr); + } + +private: + LcgIpfLoc(LcgIpfLoc &); // disable copying + LcgIpfLoc &operator=(LcgIpfLoc &); // disable copying +}; // struct LcgIpfLoc + + +// This class holds all relevant information needed in compilation, +// such as the mapping of variables to locations and the mapping of +// labels to ints. Some ofthis information is gathered during a prepass. +class LcgIpfContext: public LilInstructionVisitor { +private: + + LilCodeStub *cs; // the code stub + LilSig *entry_sig; // the stub's entry signature + bool is_arbitrary; // true if the stub's entry signature is "arbitrary" + tl::MemoryPool &mem; // a memory manager + + unsigned n_labels; // number of labels + LilLabel *labels; // a label's index is its label id + + // number of targets used by support functions, such as pop_m2n + unsigned extra_targets; + + + unsigned n_inputs; // total number of inputs + unsigned n_gr_inputs; // total number of GRs reserved for inputs + unsigned n_fr_inputs; // number of inputs placed in FRs + + unsigned first_local; // location of the first local variable + unsigned n_locals; // maximum number of locals + unsigned n_fr_locals; // maximum number of locals that may contain FP values + bool uses_local_fr[MAX_LOCALS]; // true if preserved fp f(16+index) is used by a local + unsigned local_fr_offset[MAX_LOCALS]; // offsets for local variable from start of local fr area + + unsigned first_output; // location of the start of the output space + unsigned n_outputs; // maximum number of outputs + unsigned n_gr_outputs; // total number of GRs reserved for outputs + unsigned n_fr_outputs; // maximum number of outputs placed in FRs + + unsigned first_gr_save; // start of region where ar.pfs, b0, etc. are saved + unsigned n_gr_saves; // number of GRs reserved for saving stuff + + unsigned stk_output_size; // bytes needed for outgoing params on the stack + unsigned stk_input_save_size; // size reserved for saving FR inputs + unsigned stk_local_fr_size; // size reserved for local FRs + unsigned stk_extra_saves_size; // bytes for m2n_save_all + + unsigned n_std_places; // number of standard places + unsigned stk_alloc_size; // size of allocatable memory on the stack + + // total size of the memory stack frame (in bytes) + unsigned stk_size; + + bool does_normal_calls; // true if the stub contains "normal" calls + bool does_tail_calls; // true if the stub contains tail calls + bool calls_unmanaged_code; // true if the stub calls calls code with a calling convention other than managed + bool has_m2n; // true if the stub contains push_m2n/pop_m2n instructions + bool uses_inputs; // true if inputs are ever accessed + bool needs_alloc; // true if an alloc instruction is needed at the start + + // an iterator used during pre-pass; it has to be stored here because + // it must be accessible from the visitor functions + LilInstructionIterator iter; + +public: + + void *operator new(size_t sz, tl::MemoryPool& m) { + return m.alloc(sz); + } + void operator delete (void *p, tl::MemoryPool& m) { } + + LcgIpfContext(LilCodeStub *cs, tl::MemoryPool &m): + cs(cs), + mem(m), + iter(cs, true) + { + // initialize some members + entry_sig = lil_cs_get_sig(cs); + is_arbitrary = lil_sig_is_arbitrary(entry_sig); + n_labels = 0; + extra_targets = 0; + labels = (LilLabel *) mem.alloc(lil_cs_get_num_instructions(cs) * sizeof (LilLabel)); + + n_locals = 0; + + for (unsigned i=0; i < MAX_LOCALS; i++) { + uses_local_fr[i] = false; + } + + n_outputs = 0; + n_fr_outputs = 0; + + n_std_places = 0; + + stk_extra_saves_size = 0; + stk_alloc_size = 0; + does_normal_calls = false; + calls_unmanaged_code = false; + does_tail_calls = false; + has_m2n = false; + uses_inputs = false; + + // get some info from the entry signature + if (is_arbitrary) { + n_inputs = 8; + n_gr_inputs = 8; + n_fr_inputs = 8; + } + else { + n_inputs = lil_sig_get_num_args(entry_sig); + n_gr_inputs = (n_inputs > 8) ? 8 : n_inputs; + n_fr_inputs = 0; + for (unsigned i=0; i 0 || n_locals > 0); + + // determine how many save spots are needed in stacked GRs + // ( if an M2N frame is present, registers will be saved in there) + if (has_m2n || !needs_alloc) + n_gr_saves = 0; + else if (does_normal_calls) + n_gr_saves = 4; // save ar.pfs, b0, gp, sp + else + n_gr_saves = 1; // save ar.pfs only + + + // decide where stacked register regions should start + if (has_m2n) + first_local = m2n_get_last_m2n_reg() + 1; + else + first_local = 32 + n_gr_inputs; + + first_gr_save = first_local + n_locals; + first_output = first_gr_save + n_gr_saves; + + // stack size needed for outputs (16-byte aligned) + if (n_outputs <= 8) { + n_gr_outputs = n_outputs; + stk_output_size = 0; + } + else { + n_gr_outputs = 8; + stk_output_size = align_16((n_outputs-8)*8); + } + + // 16-byte align allocatable space + stk_alloc_size = align_16(stk_alloc_size); + + // stack size needed for saving FR inputs + if (does_normal_calls) + stk_input_save_size = n_fr_inputs * 16; + else + stk_input_save_size = 0; + + // stack size needed for local FRs + stk_local_fr_size = 0; + if (does_normal_calls) { + for(unsigned i=0; i 0); // haven't run out of targets yet! + --extra_targets; + return n_labels + extra_targets; + } + + // returns the number of incoming arguments + unsigned get_num_inputs() { + return n_inputs; + } // get_num_input + + // returns the number of incoming arguments stored in FRs + unsigned get_num_fr_inputs() { + return n_fr_inputs; + } // get_num_fr_inputs + + // returns the size of the input space in the stacked GRs + unsigned get_input_space_size() { + return n_gr_inputs; + } // get_input_space_size + + // returns the size of the local space in the stacked GRs + // (this includes local vars, the M2N frame, and the save regs) + unsigned get_local_space_size() { + return first_output - (32 + n_gr_inputs); + } // get_local_space_size + + // returns the size of the output space in the stacked GRs + unsigned get_output_space_size() { + return n_gr_outputs; + } // get_output_space_size + + // returns true if there are floating-point inputs in registers f8-f15 that must be moved to registers f16-f23 + bool must_move_fr_inputs() { + return (does_normal_calls && n_fr_inputs > 0); + } // must_move_fr_inputs + + // returns true if ar.ccv should be set to zero before a call to managed + // code. This is the case if the stub or any function it calls is + // non-managed (i.e. it has a platform, jni, or stdcall cc) + bool must_reset_ccv() { + LilCc stub_cc = lil_sig_get_cc(entry_sig); + return !(stub_cc==LCC_Managed || stub_cc==LCC_Rth) || calls_unmanaged_code; + } // must_reset_ccv + + // returns the location of the i'th input + const LcgIpfLoc* get_input(unsigned n) { + // function should not be called if signature is arbitrary + assert(!is_arbitrary); + + LilType tp = lil_sig_get_arg_type(entry_sig, n); + + // if n >= 8, input is on stack + if (n >= 8) { + return new(mem) LcgIpfLoc(LLK_Stk, 16 + stk_size + (n-8)*8); + } + + if (tp != LT_F4 && tp != LT_F8) { + return new(mem) LcgIpfLoc(LLK_Gr, 32+n); + } + + // parameter is fp; its location depends on how many fp parameters + // come before it. + unsigned fp_param_cnt = 0; + for (unsigned i=0; i= 8, output is in stack + if (n >= 8) { + return new(mem) LcgIpfLoc(LLK_Stk, 16 + (n-8)*8); + } + + if (tp != LT_F4 && tp != LT_F8) { + return new(mem) LcgIpfLoc(LLK_Gr, first_output + n); + } + + // parameter is fp; its location depends on how many fp parameters + // come before it. + unsigned fp_param_cnt = 0; + for (unsigned i=0; i n_std_places) + n_std_places = sp; + } // std_places + + void alloc(LilVariable* var, unsigned alloc_space) { + alloc_space = (alloc_space + 0x7) & ~0x7; + stk_alloc_size += alloc_space; + } // alloc + + void asgn(LilVariable* var, enum LilOperation operation, LilOperand* op1, LilOperand* op2) { + check_variable(var); + check_operand(op1); + if (lil_operation_is_binary(operation)) + check_operand(op2); + } // asgn + + void ts(LilVariable*var) { + check_variable(var); + } // ts + + void handles(LilOperand* op) { + check_operand(op); + } // handles + + void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilLdX) { + check_variable(dst); + if (base != NULL) + check_variable(base); + if (index != NULL) + check_variable(index); + } // ld + + void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, LilOperand* src) { + if (base != NULL) + check_variable(base); + if (index != NULL) + check_variable(index); + check_operand(src); + } // st + + void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel) { + if (base != NULL) + check_variable(base); + if (index != NULL) + check_variable(index); + } // inc + + void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel, + LilOperand* cmp, LilOperand* src, LilLabel) + { + if (base) check_variable(base); + if (index) check_variable(index); + check_operand(cmp); + check_operand(src); + } + + void j(LilLabel) { + // nothing to do + } // j + + void jc(LilPredicate p, LilOperand* o1, LilOperand* o2, LilLabel) + { + check_operand(o1); + if (lil_predicate_is_binary(p)) + check_operand(o2); + } // jc + + void out(LilSig* sig) { + // make sure there is enough space for this command's outputs + unsigned outs = lil_sig_get_num_args(sig); + if (outs > n_outputs) + n_outputs = outs; + + // reserve enough FR outputs + unsigned n_fo = 0; + for (unsigned i=0; i n_fr_outputs) + n_fr_outputs = n_fo; + + // check if this refers to a call to unmanaged code + LilCc cc = lil_sig_get_cc(sig); + if (cc != LCC_Managed && cc != LCC_Rth) + calls_unmanaged_code = true; + } // out + + void in2out(LilSig* sig) { + // reserve enough outputs and FR outputs + if (n_inputs > n_outputs) + n_outputs = n_inputs; + if (n_fr_inputs > n_fr_outputs) + n_fr_outputs = n_fr_inputs; + // in2out implies that inputs are being accessed + uses_inputs = true; + + // check if this refers to a call to unmanaged code + LilCc cc =lil_sig_get_cc(sig); + if (cc != LCC_Managed && cc != LCC_Rth) + calls_unmanaged_code = true; + + } // in2out + + void call(LilOperand* o, LilCallKind k) { + check_operand(o); + if (k == LCK_Call) + does_normal_calls = true; + else if (k == LCK_TailCall) { + does_tail_calls = true; + // no need to reserve extra outputs, like in in2out, since tailcall is implemented differently + } + } // call + + void ret() { + // nothing to do + } // ret + + void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) { + has_m2n = true; // remember that this stub requires an m2n frame + } // push_m2n + + void m2n_save_all() + { + stk_extra_saves_size = M2N_EXTRA_SAVES_SPACE; + } // m2n_save_all + + void pop_m2n() { + // if handles are present, pop_m2n may be using an extra target + bool handles = lil_ic_get_m2n_state(iter.get_context()) == LMS_Handles; + if (handles) { + // this pop_m2n will use an extra target + extra_targets++; + // it will also execute a call + does_normal_calls = true; + } + } + + void print(char *, LilOperand *) { + // this is a no-op if debugging is off +#ifdef STUB_DEBUG + // reserve at least 2 outputs; remember that a call will be made + if (n_outputs < 2) + n_outputs = 2; + does_normal_calls = true; +#endif // STUB_DEBUG + } // print + + //visitor functions - end + //*********************** + +}; //class LcgIpfContext + + +// the following class is a LIL instruction visitor used by +// LilCodeGeneratorIpf to do most of the work. +// After the constructor exits, all the necessary code is in the +// code emitter. +class LcgIpfCodeGen: public LilInstructionVisitor { + + // the following variables are here because visitor functions need to + // access them in addition to their arguments + LilCodeStub *cs; + Merced_Code_Emitter& emitter; + LcgIpfContext& context; + tl::MemoryPool& mem; + LilInstructionIterator iter; + LilInstructionContext *ic; + LilInstruction *inst; + // visit functions can always assume that inst points to the current instruction and ic points to the current context + + unsigned current_alloc; // keeps track of memory allocation + + LilSig *cur_out_sig; // keeps track of the current out signature + + // some useful constants + static const LcgIpfLoc* gp; + static const LcgIpfLoc* sp; + static const LcgIpfLoc* r0; + static const LcgIpfLoc* tp; // thread pointer + static const LcgIpfLoc* tmp_op1; // used for temp storage of operands + static const LcgIpfLoc* tmp_op2; // used for temp storage of operands + static const LcgIpfLoc* tmp_res; // used for temp storage of results + static const LcgIpfLoc* tmp_f; // used for temp storage of FPs + static const LcgIpfLoc* tmp_addr1; // used for temp storage of addresses + static const LcgIpfLoc* tmp_addr2; // used for temp storage of addresses + static const LcgIpfLoc* tmp_addr3; // used for temp storage of addresses + static const LcgIpfLoc* tmp_addl; // used as a temporary op of addl + static const unsigned tmp_pred; // used for the jc instruction + static const unsigned tmp_br; // used for indirect calls + +private: + /******************* + * helper functions + */ + + // performs an immediate addition; may use adds, addl, or movl and add, + // depending on the immediate operand's size + void add_imm(const LcgIpfLoc *dest, const LcgIpfLoc *src, int64 imm) { + assert(src->kind == LLK_Gr && (dest->kind==LLK_Stk || dest->kind==LLK_Gr)); + + unsigned dst_reg = (dest->kind==LLK_Stk ? tmp_res->addr : dest->addr); + + if (imm >= -0x2000 && imm < 0x2000) { + // imm fits into 14 bits; use adds + emitter.ipf_adds(dst_reg, (int) imm, src->addr, current_predicate); + } + else if (imm >= -0x200000 && imm < 0x200000) { + // imm fits into 22 bits; use addl + emitter.ipf_addl(dst_reg, (int) imm, src->addr, current_predicate); + } + else { + // have to use movl + unsigned lower_32 = (unsigned) imm; + unsigned upper_32 = (unsigned) (imm >> 32); + if (src->addr==0) { + emitter.ipf_movl(dst_reg, upper_32, lower_32, current_predicate); + } else { + emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); + emitter.ipf_add(dst_reg, src->addr, tmp_addl->addr, current_predicate); + } + } + + if (dest->kind==LLK_Stk) + move(dest, tmp_res); + } + + void int_mul(unsigned dest_reg, unsigned src1_reg, unsigned src2_reg) + { + unsigned ftmp1 = 7, ftmp2 = 8, ftmp3 = 9; + emitter.ipf_setf(freg_sig, ftmp1, src1_reg, current_predicate); + emitter.ipf_setf(freg_sig, ftmp2, src2_reg, current_predicate); + emitter.ipf_xma(ftmp3, ftmp1, ftmp2, 0, l_form, current_predicate); + emitter.ipf_getf(freg_sig, dest_reg, ftmp3); + } + + // binary arithmetic operations without immediates + // (allowed only for integer values!) + void bin_op(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src1, const LcgIpfLoc* src2) + { + assert((dest->kind == LLK_Gr || dest->kind == LLK_Stk) && + (src1->kind == LLK_Gr || src1->kind == LLK_Stk) && + (src2->kind == LLK_Gr || src2->kind == LLK_Stk)); + + unsigned dest_reg = (dest->kind == LLK_Stk) ? tmp_res->addr : dest->addr; + unsigned src1_reg = (src1->kind == LLK_Stk) ? tmp_op1->addr : src1->addr; + unsigned src2_reg = (src2->kind == LLK_Stk) ? tmp_op2->addr : src2->addr; + + if (src1->kind == LLK_Stk) { + move(tmp_op1, src1); // load src1 into tmp_op1 + } + if (src2->kind == LLK_Stk) { + move(tmp_op2, src2); // load src2 into tmp_op2 + } + + switch (o) { + case LO_Add: + emitter.ipf_add(dest_reg, src1_reg, src2_reg, current_predicate); + break; + case LO_Sub: + emitter.ipf_sub(dest_reg, src1_reg, src2_reg, current_predicate); + break; + case LO_SgMul: + int_mul(dest_reg, src1_reg, src2_reg); + break; + case LO_Shl: + emitter.ipf_shl(dest_reg, src1_reg, src2_reg, current_predicate); + break; + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point + } + + if (dest->kind == LLK_Stk) { + // store tmp_res back to dest + move(dest, tmp_res); + } + } // bin_op + + + // binary op where the second op is immediate + // (allowed only for integer values!) + void bin_op_imm(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src, POINTER_SIZE_INT imm_val) { + int64 imm = (int64) imm_val; // convert to signed + assert(dest->kind != LLK_Fr && src->kind != LLK_Stk16); + const LcgIpfLoc* dest_reg = (dest->kind == LLK_Stk) ? tmp_res : dest; + const LcgIpfLoc* src_reg = (src->kind == LLK_Stk) ? tmp_op1 : src; + + if (src_reg != src) { + move(src_reg, src); // load src into tmp_op1 + } + + if (o == LO_Shl) { + if (imm > 63) { + emitter.ipf_add(dest_reg->addr, 0, 0, current_predicate); // dest_reg = 0 + } + else { + emitter.ipf_shli(dest_reg->addr, src_reg->addr, (int) imm, current_predicate); + } + } + else if (o == LO_Sub) { + add_imm(dest_reg, src_reg, -imm); + } + else if (o == LO_Add) { + add_imm(dest_reg, src_reg, imm); + } + else if (o == LO_SgMul) { + // This is not as optimised as it could be + // We could move the immediate directly into a floating point register + unsigned lower_32 = (unsigned) imm; + unsigned upper_32 = (unsigned) (imm >> 32); + emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); + int_mul(dest_reg->addr, tmp_addl->addr, src_reg->addr); + } + else if (o == LO_And) { + if (imm>=0x80 && imm<0x80) { + emitter.ipf_and(dest_reg->addr, src_reg->addr, (unsigned)imm, current_predicate); + } else { + unsigned lower_32 = (unsigned) imm; + unsigned upper_32 = (unsigned) (imm >> 32); + emitter.ipf_movl(tmp_addl->addr, upper_32, lower_32, current_predicate); + emitter.ipf_and(dest_reg->addr, tmp_addl->addr, src_reg->addr, current_predicate); + } + } else { + ASSERT(0, "Unexpected operation"); // invalid value in o + } + + if (dest_reg != dest) { + //move tmp_res back to dest + move(dest, dest_reg); + } + } // bin_op_imm + + // subtract op where the first op is immediate + // (allowed only for intefer values) + void sub_op_imm(const LcgIpfLoc* dest, POINTER_SIZE_INT imm_val, const LcgIpfLoc* src) + { + int64 imm = (int64)imm_val; + assert(dest->kind != LLK_Fr && src->kind != LLK_Stk16); + const LcgIpfLoc* dest_reg = (dest->kind == LLK_Stk) ? tmp_res : dest; + const LcgIpfLoc* src_reg = (src->kind == LLK_Stk) ? tmp_op1 : src; + + if (src_reg!=src) + move(src_reg, src); + + if (imm>=-0x80 && imm<0x80) { + emitter.ipf_subi(dest_reg->addr, (unsigned)imm, src_reg->addr, current_predicate); + } else { + move_imm(tmp_res, imm); + emitter.ipf_sub(dest_reg->addr, tmp_res->addr, src_reg->addr, current_predicate); + } + + if (dest_reg!=dest) + move(dest, dest_reg); + } + + // unary operation without immediates + void un_op(LilOperation o, const LcgIpfLoc* dest, const LcgIpfLoc* src) { + assert(dest->kind != LLK_Fr && src->kind != LLK_Fr); + unsigned dest_reg = (dest->kind == LLK_Stk) ? tmp_res->addr : dest->addr; + unsigned src_reg = (src->kind == LLK_Stk) ? tmp_op1->addr : src->addr; + + if (src->kind == LLK_Stk) { + move(tmp_op1, src); // load src into tmp_op1 + } + + switch (o) { + case LO_Neg: + emitter.ipf_sub(dest_reg, 0, src_reg, current_predicate); + break; + case LO_Not: + emitter.ipf_xori(dest_reg, 0xFF, src_reg, current_predicate); + break; + case LO_Sx1: + emitter.ipf_sxt(sxt_size_1, dest_reg, src_reg, current_predicate); + break; + case LO_Sx2: + emitter.ipf_sxt(sxt_size_2, dest_reg, src_reg, current_predicate); + break; + case LO_Sx4: + emitter.ipf_sxt(sxt_size_4, dest_reg, src_reg, current_predicate); + break; + case LO_Zx1: + emitter.ipf_zxt(sxt_size_1, dest_reg, src_reg, current_predicate); + break; + case LO_Zx2: + emitter.ipf_zxt(sxt_size_2, dest_reg, src_reg, current_predicate); + break; + case LO_Zx4: + emitter.ipf_zxt(sxt_size_4, dest_reg, src_reg, current_predicate); + break; + default: + ASSERT(0, "Unexpected operation"); // control should never reach this point + } + + if (dest->kind == LLK_Stk) { + //move tmp_res back to dest + move(dest, tmp_res); + } + } // un_op + + + // move between two register or stack locations + void move(const LcgIpfLoc* dest, const LcgIpfLoc* src) { + if (dest->kind == LLK_Stk) { + // put sp + (dest offset) in tmp_addr1 + add_imm(tmp_addr1, sp, dest->addr); + + if (src->kind == LLK_Stk) { + // put sp + (src offset) in tmp_addr2 + add_imm(tmp_addr2, sp, src->addr); + // load src into tmp_op1 + emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, tmp_op1->addr, tmp_addr2->addr, current_predicate); + // store tmp_op1 into dest + emitter.ipf_st(int_mem_size_8, mem_st_spill, mem_none, tmp_addr1->addr, tmp_op1->addr, current_predicate); + } + else if (src->kind == LLK_Stk16) { + // put sp + (src offset) in tmp_addr2 + add_imm(tmp_addr2, sp, src->addr); + // load (128-bit) src into tmp_f + emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); + // store (64-bit) tmp_f into dest + emitter.ipf_stf(float_mem_size_d, mem_st_none, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); + } + else if (src->kind == LLK_Gr) { + // store src into dest + emitter.ipf_st(int_mem_size_8, mem_st_spill, mem_none, tmp_addr1->addr, src->addr, current_predicate); + } + else if (src->kind == LLK_Fr) { + // store src into (64-bit) dest + emitter.ipf_stf(float_mem_size_d, mem_st_none, mem_none, tmp_addr1->addr, src->addr, current_predicate); + } + } + else if (dest->kind == LLK_Stk16) { + // put sp + (dest offset) in tmp_addr1 + add_imm(tmp_addr1, sp, dest->addr); + + if (src->kind == LLK_Stk) { + // put sp + (src offset) in tmp_addr2 + add_imm(tmp_addr2, sp, src->addr); + // load (64-bit) src into tmp_f + emitter.ipf_ldf(float_mem_size_d, mem_ld_none, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); + // store tmp_f into (128-bit) dest + emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); + } + else if (src->kind == LLK_Stk16) { + // put sp + (src offset) in tmp_addr2 + add_imm(tmp_addr2, sp, src->addr); + // load (128-bit) src into tmp_f + emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, tmp_f->addr, tmp_addr2->addr, current_predicate); + // store tmp_f into (128-bit) dest + emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, tmp_f->addr, current_predicate); + } + else if (src->kind == LLK_Fr) { + // store src into (128-bit) dest + emitter.ipf_stf(float_mem_size_d, mem_st_spill, mem_none, tmp_addr1->addr, src->addr, current_predicate); + } + else { + ASSERT(0, "Unexpected kind"); // src shouldn't be a GR! + } + } + else if (dest->kind == LLK_Gr) { + if (src->kind == LLK_Stk) { + // put sp + (src offset) in tmp_addr2 + add_imm(tmp_addr2, sp, src->addr); + // load src into dest + emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, dest->addr, tmp_addr2->addr, current_predicate); + } + else if (src->kind == LLK_Gr) { + // move src into dest + emitter.ipf_mov(dest->addr, src->addr, current_predicate); + } + else { + ASSERT(0, "Unexpected kind"); // src->kind shouldn't be LLK_Fr or LLK_Stk16 + } + } + else if (dest->kind == LLK_Fr) { + if (src->kind == LLK_Stk) { + // put sp + (src offset) in tmp_addr2 + add_imm(tmp_addr2, sp, src->addr); + // load (64-bit) src into dest + emitter.ipf_ldf(float_mem_size_d, mem_ld_none, mem_none, dest->addr, tmp_addr2->addr, current_predicate); + } + else if (src->kind == LLK_Stk16) { + // put sp + (src offset) in tmp_addr2 + add_imm(tmp_addr2, sp, src->addr); + // load (128-bit) src into dest + emitter.ipf_ldf(float_mem_size_d, mem_ld_fill, mem_none, dest->addr, tmp_addr2->addr, current_predicate); + } + else if (src->kind == LLK_Fr) { + // move src into dest + emitter.ipf_fmov(dest->addr, src->addr, current_predicate); + } + else { + ASSERT(0, "Unexpected kind"); // src should not be GR! + } + } + else { + ASSERT(0, "Unknown kind"); // dest->kind has illegal value! + } + } // move + + + // move immediate + // (not allowed for FRs!) + void move_imm(const LcgIpfLoc* dest, POINTER_SIZE_INT imm_val) { + assert(dest->kind==LLK_Gr || dest->kind==LLK_Stk); + add_imm(dest, r0, (int64)imm_val); + } // move_imm + + // generate instructions that compute a condition into two predicate registers + void do_cond(LilPredicate p, LilOperand* op1, LilOperand* op2, unsigned true_pr, unsigned false_pr) + { + assert(current_predicate==0); + + // make sure operands are in registers + unsigned op1_reg = 0, op2_reg = 0; // GR numbers holding op1 and op2 + + if (lil_operand_is_immed(op1)) { + int64 imm = (int64) lil_operand_get_immed(op1); + move_imm(tmp_op1, imm); + op1_reg = tmp_op1->addr; + } + else { // op1 is a variable + LilVariable *op1_var = lil_operand_get_variable(op1); + const LcgIpfLoc* op1_loc = + context.get_var_loc(op1_var, inst, ic, false); + if (op1_loc->kind == LLK_Stk) { + // load op1_loc into tmp_op1 + move(tmp_op1, op1_loc); + op1_reg = tmp_op1->addr; + } + else if (op1_loc->kind == LLK_Gr) { + assert(op1_loc->kind != LLK_Fr); // comparisons available only for ints! + op1_reg = op1_loc->addr; + } + else { + ASSERT(0, "Wrong kind"); // comparisons available only for ints! + } + } + + if (lil_predicate_is_binary(p)) { + if (lil_operand_is_immed(op2)) { + int64 imm = (int64) lil_operand_get_immed(op2); + move_imm(tmp_op2, imm); + op2_reg = tmp_op2->addr; + } + else { // op2 is a variable + LilVariable *op2_var = lil_operand_get_variable(op2); + const LcgIpfLoc* op2_loc = + context.get_var_loc(op2_var, inst, ic, false); + if (op2_loc->kind == LLK_Stk) { + // load op2_loc into tmp_op2 + move(tmp_op2, op2_loc); + op2_reg = tmp_op2->addr; + } + else if (op2_loc->kind == LLK_Gr) { + op2_reg = op2_loc->addr; + } + else { + ASSERT(0, "Wrong kind"); // comparisons available only for ints! + } + } + } + else { // the predicate is unary, i.e. the 2nd op is r0 + op2_reg = 0; + } + + // do the comparison using a cmp instruction, place result in tmp_pred + Int_Comp_Rel emitter_p = icmp_invalid; + switch (p) { + case LP_IsZero: + case LP_Eq: + emitter_p = icmp_eq; break; + case LP_IsNonzero: + case LP_Ne: + emitter_p = icmp_ne; break; + case LP_Le: + emitter_p = icmp_le; break; + case LP_Lt: + emitter_p = icmp_lt; break; + case LP_Ule: + emitter_p = icmp_leu; break; + case LP_Ult: + emitter_p = icmp_ltu; break; + default: + ASSERT(0, "Unknown predicate"); // should never be reached + } + + emitter.ipf_cmp(emitter_p, cmp_unc, true_pr, false_pr, op1_reg, op2_reg, false, 0); + } + + // generate instructions that calculate a LIL address; + // return the GR number where the address can be found + // (this can be either the base register itself, or a temp register) + const LcgIpfLoc* do_addr(LilVariable *base, unsigned scale, LilVariable *index, POINTER_SIZE_INT offset) { + // locations for base and index (NULL if base or index doesn't exist) + const LcgIpfLoc *bloc=NULL, *iloc=NULL; + // shift amount, if index exists + unsigned shift=0; + + if (base != NULL) { + const LcgIpfLoc* orig_bloc = + context.get_var_loc(base, inst, ic, false); + assert(orig_bloc->kind == LLK_Gr || orig_bloc->kind == LLK_Stk); + if (orig_bloc->kind == LLK_Stk) { + move(tmp_addr1, orig_bloc); + bloc = tmp_addr1; + } + else { + bloc = orig_bloc; + } + } + + if (index != NULL && scale != 0) { + const LcgIpfLoc* orig_iloc = + context.get_var_loc(index, inst, ic, false); + assert(orig_iloc->kind == LLK_Gr || orig_iloc->kind == LLK_Stk); + + // fetch index from memory if needed + if (orig_iloc->kind == LLK_Stk) { + move(tmp_res, orig_iloc); + iloc = tmp_addr2; + } + else { + iloc = orig_iloc; + } + + // determine size of shift + while (scale > 1) { + scale >>= 1; + shift++; + } + assert(shift >= 1 && shift <= 4); + } + + if (bloc) { + if (offset) { + add_imm(tmp_addr3, bloc, offset); + if (iloc) + emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, tmp_addr3->addr, current_predicate); + return tmp_addr3; + } + else { // no offset + if (iloc) { + emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, bloc->addr, current_predicate); + return tmp_addr3; + } + else // no index + return bloc; + } + } // if (bloc) + else { // no base + if (offset) { + move_imm(tmp_addr3, offset); + if (iloc) + emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, tmp_addr3->addr, current_predicate); + } + else { // no offset + if (iloc) + emitter.ipf_shladd(tmp_addr3->addr, iloc->addr, shift, 0, current_predicate); + else // no index + ASSERT(0, "Can't have no base, no index and no offset"); + } + return tmp_addr3; + } // else (bloc) + } // do_addr + + + // implements the copying of incoming to outgoing args in in2out and tailcall + void do_in_to_out() + { + assert(!context.entry_sig_is_arbitrary()); + unsigned n_inputs = context.get_num_inputs(); + unsigned i; + for (i=0; i<8 && ikind==LLK_Stk && out_loc->kind==LLK_Stk); + add_imm(tmp_addr1, sp, in_loc->addr); + add_imm(tmp_addr2, sp, out_loc->addr); + for(; iaddr, tmp_addr1->addr, 8, current_predicate); + emitter.ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, tmp_addr2->addr, tmp_res->addr, 8, current_predicate); + } + } // do_in_to_out + + + // sets up the stack frame + void set_stk_frame() + { + unsigned stk_size = context.get_stk_size(); + if (stk_size != 0) { + add_imm(sp, sp, -(int)stk_size); + } + } // set_stk_frame + + // unsets the stack frame + void unset_stk_frame() + { + unsigned stk_size = context.get_stk_size(); + if (stk_size != 0) { + add_imm(sp, sp, (int)stk_size); + } + } // unset_stk_frame + + // moves FR inputs, originally residing in f8-f15, into the stack + void move_FR_inputs() + { + if (context.must_move_fr_inputs()) { + unsigned n_fr_inputs = context.get_num_fr_inputs(); + int start_offset = context.get_input_save_offset(); + + // using the same address reg in all stores will create too many dependencies; + // alternate the address reg between tmp_addr1 and tmp_addr2 for more ILP + add_imm(tmp_addr1, sp, start_offset); + if (n_fr_inputs >=2) + add_imm(tmp_addr2, sp, start_offset+16); + + for (unsigned i=0; i < n_fr_inputs; i++) { + unsigned addr_reg = (i%2) ? tmp_addr2->addr : tmp_addr1->addr; + emitter.ipf_stf_inc_imm(float_mem_size_d, mem_st_spill, mem_none, + addr_reg, 8+i, 32, current_predicate); + } + } + } // move_FR_inputs + + // moves FR inputs from the stack back to f8-f15 + void unmove_FR_inputs() + { + if (context.must_move_fr_inputs()) { + unsigned n_fr_inputs = context.get_num_fr_inputs(); + int start_offset = context.get_input_save_offset(); + + // using the same address reg in all stores will create too many dependencies; + // alternate the address reg between tmp_addr1 and tmp_addr2 for more ILP + add_imm(tmp_addr1, sp, start_offset); + if (n_fr_inputs >=2) + add_imm(tmp_addr2, sp, start_offset+16); + + for (unsigned i=0; i < n_fr_inputs; i++) { + unsigned addr_reg = (i%2) ? tmp_addr2->addr : tmp_addr1->addr; + emitter.ipf_ldf_inc_imm(float_mem_size_d, mem_ld_fill, mem_none, 8+i, addr_reg, 32, current_predicate); + } + } + } // unmove_FR_inputs + + + // emits the code that needs to go at the very beginning of the code stub + void do_entry() + { + assert(current_predicate==0); + + // start with an alloc instruction + const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); + if (pfs_save_gr) { + emitter.ipf_alloc(pfs_save_gr->addr, + context.get_input_space_size(), + context.get_local_space_size(), + context.get_output_space_size(), + 0); // no rotating regs! + } + + // save b0 + const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); + if (return_save_gr) { + emitter.ipf_mfbr(return_save_gr->addr, BRANCH_RETURN_LINK_REG); + } + + // save gp + const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); + if (gp_save_gr) + emitter.ipf_mov(gp_save_gr->addr, GP_REG); + + set_stk_frame(); + move_FR_inputs(); + } // do_entry + + + + /********************** + * visitor functions + */ + + unsigned current_predicate; // Predicate all instructions with this instruction (may not work for all instructions) + +public: + + void ret() + { + unset_stk_frame(); + + // restore gp + const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); + if (gp_save_gr) + emitter.ipf_mov(GP_REG, gp_save_gr->addr, current_predicate); + + // restore b0 + const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); + if (return_save_gr) { + emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, return_save_gr->addr, current_predicate); + } + + // restore ar.pfs + const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); + if (pfs_save_gr) + emitter.ipf_mtap(AR_pfs, pfs_save_gr->addr, current_predicate); + + // if this ret is going back to LIL or managed code, reset ar.ccv + LilCc call_conv = lil_sig_get_cc(context.get_entry_sig()); + if (context.must_reset_ccv() && + (call_conv == LCC_Managed || + call_conv == LCC_Rth)) { + emitter.ipf_mtap(AR_ccv, 0, current_predicate); + } + + // return + emitter.ipf_brret(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, current_predicate); + } // ret + + + void label(LilLabel lab) { + emitter.set_target(context.get_label_id(lab)); + } // label + + + void locals(unsigned) { + // nothing to be done here; + // has been taken care of in the prepass + } // locals + + + void std_places(unsigned) { + // nothing to be done here; + // has been taken care of in the prepass + } // std_places + + void alloc(LilVariable* var, unsigned sz) { + // the actual size allocated will always be a multiple of 8 + sz = (sz + 0x7) & ~0x7; + int alloc_offset = context.get_alloc_start_offset() + current_alloc; + current_alloc += sz; + + // var = sp + alloc_offset + const LcgIpfLoc* var_loc = + context.get_var_loc(var, inst, ic, true); + add_imm(var_loc, sp, alloc_offset); + } // alloc + + + void asgn(LilVariable* dest, enum LilOperation o, LilOperand* op1, LilOperand*op2) + { + const LcgIpfLoc* dest_loc = + context.get_var_loc(dest, inst, ic, true); + + if (o == LO_Mov) { + if (lil_operand_is_immed(op1)) { + move_imm(dest_loc, lil_operand_get_immed(op1)); + } + else { + const LcgIpfLoc* src_loc = + context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); + move(dest_loc, src_loc); + } + } + else 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 + int64 op1_imm = (int64) lil_operand_get_immed(op1); + int64 op2_imm = (int64) lil_operand_get_immed(op2); + POINTER_SIZE_INT 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 LIL operation"); // control should never reach this point + } + move_imm(dest_loc, result); + } + else if (lil_operand_is_immed(op1)) { + const LcgIpfLoc* src2_loc = context.get_var_loc(lil_operand_get_variable(op2), inst, ic, false); + switch (o) { + case LO_Add: + bin_op_imm(LO_Add, dest_loc, src2_loc, lil_operand_get_immed(op1)); + break; + case LO_Sub: + sub_op_imm(dest_loc, lil_operand_get_immed(op1), src2_loc); + break; + case LO_SgMul: + case LO_Shl: + move_imm(tmp_res, (int64) lil_operand_get_immed(op1)); + bin_op(o, dest_loc, tmp_res, src2_loc); + break; + default: + ASSERT(0, "Unexpected LIL operation"); // control should never reach this point + } + } + else if (lil_operand_is_immed(op2)) { + const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); + bin_op_imm(o, dest_loc, src1_loc, lil_operand_get_immed(op2)); + } + else { // both operands non-immediate + const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); + const LcgIpfLoc* src2_loc = context.get_var_loc(lil_operand_get_variable(op2), inst, ic, false); + bin_op(o, dest_loc, src1_loc, src2_loc); + } + } + else { // unary operation + if (lil_operand_is_immed(op1)) { + int64 imm = (int64) lil_operand_get_immed(op1); // typecast to get signed type + int64 result = 0; + switch (o) { + case LO_Neg: + result = -imm; + break; + case LO_Not: + result = ~imm; + break; + case LO_Sx1: + result = (int64) (int8) imm; + break; + case LO_Sx2: + result = (int64) (int16) imm; + break; + case LO_Sx4: + result = (int64) (int32) imm; + break; + case LO_Zx1: + result = (int64) (uint64) (uint8) imm; + break; + case LO_Zx2: + result = (int64) (uint64) (uint16) imm; + break; + case LO_Zx4: + result = (int64) (uint64) (uint32) imm; + break; + default: + ASSERT(0, "Unexpected LIL operation"); // control should never reach this point + } + move_imm(dest_loc, result); + } + else { // non-immediate operand + const LcgIpfLoc* src1_loc = context.get_var_loc(lil_operand_get_variable(op1), inst, ic, false); + un_op(o, dest_loc, src1_loc); + } + } + } // asgn + + + void ts(LilVariable* var) { + const LcgIpfLoc* var_loc = + context.get_var_loc(var, inst, ic, true); + move(var_loc, tp); + } // ts + + void handles(LilOperand* op) + { + assert(current_predicate==0); + + if (lil_operand_is_immed(op)) { + m2n_gen_set_local_handles_imm(&emitter, lil_operand_get_immed(op)); + } else { + const LcgIpfLoc* op_loc = context.get_var_loc(lil_operand_get_variable(op), inst, ic, false); + assert(op_loc->kind == LLK_Gr); + m2n_gen_set_local_handles(&emitter, op_loc->addr); + } + } // handles + + void ld(LilType t, LilVariable* dst, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, LilLdX ext) + { + // form the address + const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); + + // decide the size of the load (1, 2, 4, or 8) + Int_Mem_Size size = int_mem_size_1; + Sxt_Size ext_size = sxt_size_invalid; + bool sxt = ext==LLX_Sign; + switch(t) { + case LT_G1: + size = int_mem_size_1; ext_size = sxt_size_1; break; + case LT_G2: + size = int_mem_size_2; ext_size = sxt_size_2; break; + case LT_G4: + size = int_mem_size_4; ext_size = sxt_size_4; break; + case LT_G8: + case LT_Ref: + case LT_PInt: + size = int_mem_size_8; sxt = false; break; + case LT_F4: + case LT_F8: + case LT_Void: + ASSERT(0, "Unexpected LIL type"); // types not allowed in loads / stores + default: + ASSERT(0, "Unknown LIL type"); // invalid value in type + } + + Ld_Flag ld_flag = mem_ld_none; + switch (acqrel) { + case LAR_Acquire: ld_flag = mem_ld_acq; break; + case LAR_Release: ASSERT(0, "Unexpected acqrel value"); break; + case LAR_None: break; + } + + const LcgIpfLoc* dst_loc = context.get_var_loc(dst, inst, ic, true); + if (dst_loc->kind == LLK_Stk) { + // load value into tmp_res + emitter.ipf_ld(size, ld_flag, mem_none, tmp_res->addr, addr_loc->addr, current_predicate); + if (sxt) + emitter.ipf_sxt(ext_size, tmp_res->addr, tmp_res->addr, current_predicate); + // store value into dst_loc + move(dst_loc, tmp_res); + } + else if (dst_loc->kind == LLK_Gr) { + // load value into dst_loc + emitter.ipf_ld(size, ld_flag, mem_none, dst_loc->addr, addr_loc->addr, current_predicate); + if (sxt) + emitter.ipf_sxt(ext_size, dst_loc->addr, dst_loc->addr, current_predicate); + } + else { + ASSERT(0, "Unexpected kind"); // dst_loc shouldn't be FR or STK16 + } + } // ld + + + void st(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, LilOperand* src) + { + // move the address into tmp_addr1 + const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); + + // decide the size of the store (1, 2, 4, or 8) + Int_Mem_Size size = int_mem_size_1; + switch(t) { + case LT_G1: + size = int_mem_size_1; break; + case LT_G2: + size = int_mem_size_2; break; + case LT_G4: + size = int_mem_size_4; break; + case LT_G8: + case LT_Ref: + case LT_PInt: + size = int_mem_size_8; break; + case LT_F4: + case LT_F8: + case LT_Void: + ASSERT(0, "Unexpected LIL type"); // types not allowed in loads / stores + default: + ASSERT(0, "Unknown LIL type"); // invalid value in type + } + + St_Flag st_flag = mem_st_none; + switch (acqrel) { + case LAR_Acquire: ASSERT(0, "Unexpected value of acqrel"); break; + case LAR_Release: st_flag = mem_st_rel; break; + case LAR_None: break; + } + + const LcgIpfLoc* src_loc = NULL; + if (lil_operand_is_immed(src)) { + // move source into temp reg + POINTER_SIZE_INT imm = lil_operand_get_immed(src); + if (imm == 0) { + //just use r0 + src_loc = r0; + } + else { + move_imm(tmp_op1, lil_operand_get_immed(src)); + src_loc = tmp_op1; + } + } + else { + LilVariable *src_var = lil_operand_get_variable(src); + src_loc = context.get_var_loc(src_var, inst, ic, false); + } + if (src_loc->kind == LLK_Stk) { + // load src_loc into tmp_res + move(tmp_res, src_loc); + // store tmp_res + emitter.ipf_st(size, st_flag, mem_none, addr_loc->addr, tmp_res->addr, current_predicate); + } + else if (src_loc->kind == LLK_Gr) { + // store src_loc + emitter.ipf_st(size, st_flag, mem_none, addr_loc->addr, src_loc->addr, current_predicate); + } + else { + ASSERT(0, "Unexpected kind"); // src_loc shouldn't be FR or STK16! + } + } // st + + + void inc(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel) + { + assert(acqrel==LAR_None); + + const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); + // load addr1 into tmp_res + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, + tmp_res->addr, addr_loc->addr, current_predicate); + // inc tmp_res + add_imm(tmp_res, tmp_res, 1); + // store tmp_res back to addr1 + emitter.ipf_st(int_mem_size_8, mem_st_none, mem_none, + addr_loc->addr, tmp_res->addr, current_predicate); + } // inc + + void cas(LilType t, LilVariable* base, unsigned scale, LilVariable* index, POINTER_SIZE_SINT offset, LilAcqRel acqrel, + LilOperand* cmp, LilOperand* src, LilLabel label) + { + assert(current_predicate==0); + + // move the address into tmp_addr1 + const LcgIpfLoc* addr_loc = do_addr(base, scale, index, offset); + + // decide the size of the store (1, 2, 4, or 8) + Int_Mem_Size size = int_mem_size_1; + switch(t) { + case LT_G1: + size = int_mem_size_1; break; + case LT_G2: + size = int_mem_size_2; break; + case LT_G4: + size = int_mem_size_4; break; + case LT_G8: + case LT_Ref: + case LT_PInt: + size = int_mem_size_8; break; + case LT_F4: + case LT_F8: + case LT_Void: + ASSERT(0, "Unexpected LIL type"); // types not allowed in loads / stores + default: + ASSERT(0, "Unknown LIL type"); // invalid value in type + } + + Cmpxchg_Flag flag = mem_cmpxchg_acq; + switch (acqrel) { + case LAR_Acquire: flag = mem_cmpxchg_acq; break; + case LAR_Release: flag = mem_cmpxchg_rel; break; + default: + ASSERT(0, "Unexpected value of acqrel"); + } + + const LcgIpfLoc* src_loc = NULL; + if (lil_operand_is_immed(src)) { + // move source into temp reg + POINTER_SIZE_INT imm = lil_operand_get_immed(src); + if (imm == 0) { + //just use r0 + src_loc = r0; + } + else { + move_imm(tmp_op1, lil_operand_get_immed(src)); + src_loc = tmp_op1; + } + } + else { + LilVariable *src_var = lil_operand_get_variable(src); + src_loc = context.get_var_loc(src_var, inst, ic, false); + if (src_loc->kind == LLK_Stk) { + move(tmp_op1, src_loc); + src_loc = tmp_op1; + } + } + + const LcgIpfLoc* cmp_loc = NULL; + if (lil_operand_is_immed(cmp)) { + // move source into temp reg + POINTER_SIZE_INT imm = lil_operand_get_immed(cmp); + if (imm == 0) { + //just use r0 + cmp_loc = r0; + } + else { + move_imm(tmp_op2, lil_operand_get_immed(cmp)); + cmp_loc = tmp_op2; + } + } + else { + LilVariable *cmp_var = lil_operand_get_variable(cmp); + cmp_loc = context.get_var_loc(cmp_var, inst, ic, false); + if (cmp_loc->kind == LLK_Stk) { + move(tmp_op2, cmp_loc); + cmp_loc = tmp_op2; + } + } + + emitter.ipf_mtap(AR_ccv, cmp_loc->addr, current_predicate); + emitter.ipf_cmpxchg(size, flag, mem_none, tmp_res->addr, addr_loc->addr, src_loc->addr, current_predicate); + emitter.ipf_mtap(AR_ccv, 0, current_predicate); + emitter.ipf_cmp(icmp_ne, cmp_unc, tmp_pred, 0, tmp_res->addr, cmp_loc->addr, false, 0); + unsigned label_id = context.get_label_id(label); + emitter.ipf_br(br_cond, br_few, br_dptk, br_none, label_id, tmp_pred); + } + + void j(LilLabel lab) + { + unsigned label_id = context.get_label_id(lab); + emitter.ipf_br(br_cond, br_few, br_sptk, br_none, label_id, current_predicate); + } // j + + void jc(LilPredicate p, LilOperand* op1, LilOperand* op2, LilLabel label) + { + assert(current_predicate==0); + + // compute the condition + do_cond(p, op1, op2, tmp_pred, 0); + + // the actual branch + unsigned label_id = context.get_label_id(label); + emitter.ipf_br(br_cond, br_few, br_dptk, br_none, label_id, tmp_pred); + } // jc + + void out(LilSig* sig) + { + cur_out_sig = sig; + // nothing else to do; space has been reserved already + } // out + + void in2out(LilSig* sig) + { + assert(!context.entry_sig_is_arbitrary()); + cur_out_sig = sig; + do_in_to_out(); + } // in2out + + + void call(LilOperand* target, LilCallKind kind) + { + assert(current_predicate==0); + + LilSig *out_sig = lil_ic_get_out_sig(ic); + LilCc call_conv = (kind == LCK_TailCall) ? lil_sig_get_cc(context.get_entry_sig()) : + lil_sig_get_cc(out_sig); + + // reset ar.ccv if this call could lead to managed or LIL code + if (context.must_reset_ccv() && + (call_conv == LCC_Managed || + call_conv == LCC_Rth)) { + emitter.ipf_mtap(AR_ccv, 0); + } + + if (kind == LCK_TailCall) { + // move FR inputs to their original place + unmove_FR_inputs(); + + // unset stack, so that old stacked arguments become visible + unset_stk_frame(); + + // restore gp + const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); + if (gp_save_gr) + emitter.ipf_mov(GP_REG, gp_save_gr->addr); + + // restore b0 + const LcgIpfLoc* return_save_gr = context.get_return_save_gr(); + if (return_save_gr) { + emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, return_save_gr->addr); + } + + // restore ar.pfs + const LcgIpfLoc* pfs_save_gr = context.get_pfs_save_gr(); + if (pfs_save_gr) + emitter.ipf_mtap(AR_pfs, pfs_save_gr->addr); + + // jump (instead of calling) + if (lil_operand_is_immed(target)) { + void **proc_ptr = (void **) lil_operand_get_immed(target); + emit_branch_with_gp(emitter, proc_ptr); + } + else { + LilVariable *var = lil_operand_get_variable(target); + const LcgIpfLoc* loc = + context.get_var_loc(var, inst, ic, false); + unsigned call_addr_gr = 0; + if (loc->kind == LLK_Gr) { + call_addr_gr = loc->addr; + } + else if (loc->kind == LLK_Stk) { + // load loc into tmp_res + move(tmp_res, loc); + call_addr_gr = tmp_res->addr; + } + else { + ASSERT(0, "Unexpected kind"); // address can't be FP! + } + emitter.ipf_mtbr(tmp_br, call_addr_gr); + emitter.ipf_bri(br_cond, br_many, br_sptk, br_none, tmp_br); + } + + return; + } // kind == LCK_TailCall + + // kind == LCK_Call or kind == LCK_CallNoRet + + if (lil_operand_is_immed(target)) { + void** proc_ptr = (void **) lil_operand_get_immed(target); + void* fn_addr = proc_ptr[0]; + void* gp_new = proc_ptr[1]; + void* gp_old = get_vm_gp_value(); + if (gp_new != gp_old) { + // Set new gp + emit_mov_imm_compactor(emitter, GP_REG, (uint64)gp_new); + } + emit_mov_imm_compactor(emitter, tmp_res->addr, (uint64)fn_addr, 0); + if (context.has_push_m2n()) { + emitter.ipf_mtbr(BRANCH_CALL_REG, tmp_res->addr); + emit_mov_imm_compactor(emitter, tmp_res->addr, (uint64)m2n_gen_flush_and_call(), 0); + } + emitter.ipf_mtbr(tmp_br, tmp_res->addr); + emitter.ipf_bricall(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, tmp_br); + if (gp_new != gp_old) { + // Restore gp + const LcgIpfLoc* gp_save_gr = context.get_gp_save_gr(); + assert(gp_save_gr); + emitter.ipf_mov(GP_REG, gp_save_gr->addr, current_predicate); + } + } + else { + LilVariable *var = lil_operand_get_variable(target); + const LcgIpfLoc* loc = + context.get_var_loc(var, inst, ic, false); + unsigned call_addr_gr = 0; + if (loc->kind == LLK_Gr) { + call_addr_gr = loc->addr; + } + else if (loc->kind == LLK_Stk) { + // load loc into tmp_res + move(tmp_res, loc); + call_addr_gr = tmp_res->addr; + } + else { + ASSERT(0, "Unexpected kind"); // address can't be FP! + } + if (context.has_push_m2n()) { + emitter.ipf_mtbr(BRANCH_CALL_REG, call_addr_gr); + emit_mov_imm_compactor(emitter, call_addr_gr, (uint64)m2n_gen_flush_and_call(), 0); + } + emitter.ipf_mtbr(tmp_br, call_addr_gr); + emitter.ipf_bricall(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, tmp_br); + } + } // call + + + void push_m2n(Method_Handle method, frame_type current_frame_type, bool handles) + { + assert(current_predicate==0); + m2n_gen_push_m2n(&emitter, method, current_frame_type, handles, context.get_stk_size(), 0, 0, false); + } // push_m2n + + void m2n_save_all() + { + assert(current_predicate==0); + add_imm(sp, sp, context.get_extra_saves_start_offset()+M2N_EXTRA_SAVES_SPACE-16); + m2n_gen_save_extra_preserved_registers(&emitter); + add_imm(sp, sp, -(int64)(context.get_extra_saves_start_offset()-16)); + } // m2n_save_all + + void pop_m2n() + { + assert(current_predicate==0); + // see if handles are present + bool handles = lil_ic_get_m2n_state(ic) == LMS_Handles; + int target = -1; + if (handles) + // this pop_m2n will need to use an emitter target + target = (int) context.get_unused_target(); + M2nPreserveRet pr; + LilType rt = lil_ic_get_ret_type(ic); + if (rt==LT_Void) + pr = MPR_None; + else if (rt==LT_F4 || rt==LT_F8) + pr = MPR_Fr; + else + pr = MPR_Gr; + m2n_gen_pop_m2n(&emitter, handles, pr, false, context.get_first_output_gr(), target); + } // pop_m2n + + void print(char *str, LilOperand *o) + { + assert(current_predicate==0); + + // this is a no-op if debugging is off +#ifdef STUB_DEBUG + unsigned print_reg; + if (lil_operand_is_immed(o)) { + // dummy operand; print r0 + print_reg = 0; + } + else { + LilVariable *var = lil_operand_get_variable(o); + const LcgIpfLoc* var_loc = + context.get_var_loc(var, inst, ic, false); + assert(var_loc->kind == LLK_Gr); + print_reg = var_loc->addr; + } + emit_print_reg(emitter, str, print_reg, context.get_num_inputs(), + context.get_first_output_gr(), false); +#endif // STUB_DEBUG + } // print + +public: + /****************************** + * constructors and destructors + */ + void *operator new(size_t sz, tl::MemoryPool &m) { + return m.alloc(sz); + } + + + LcgIpfCodeGen(LilCodeStub* cs, Merced_Code_Emitter& e, LcgIpfContext& c, tl::MemoryPool& m): + cs(cs), + emitter(e), + context(c), + mem(m), + iter(cs, true), + ic(NULL), + current_alloc(0), + cur_out_sig(NULL), + current_predicate(0) + { + // emit entry code + do_entry(); + + while (!iter.at_end()) { + ic = iter.get_context(); + inst = iter.get_current(); + lil_visit_instruction(inst, this); + iter.goto_next(); + } + } // LcgIpfCodeGen (constructor) + +}; // class LcgIpfCodeGen + + +// initialization of static members +// TODO: we have to get rid of memory pool in static area due to initialization problems +static tl::MemoryPool loc_mem; +const LcgIpfLoc* LcgIpfCodeGen::gp = new(loc_mem) LcgIpfLoc(LLK_Gr, 1); +const LcgIpfLoc* LcgIpfCodeGen::sp = new(loc_mem) LcgIpfLoc(LLK_Gr, 12); +const LcgIpfLoc* LcgIpfCodeGen::r0 = new(loc_mem) LcgIpfLoc(LLK_Gr, 0); +const LcgIpfLoc* LcgIpfCodeGen::tp = new(loc_mem) LcgIpfLoc(LLK_Gr, 4); // thread pointer is in r4 +const LcgIpfLoc* LcgIpfCodeGen::tmp_op1 = new(loc_mem) LcgIpfLoc(LLK_Gr, 31); // r30 and r31 used as temp operand locations +const LcgIpfLoc* LcgIpfCodeGen::tmp_op2 = new(loc_mem) LcgIpfLoc(LLK_Gr, 30); +const LcgIpfLoc* LcgIpfCodeGen::tmp_res = new(loc_mem) LcgIpfLoc(LLK_Gr, 29); +const LcgIpfLoc* LcgIpfCodeGen::tmp_f = new(loc_mem) LcgIpfLoc(LLK_Fr, 6); +const LcgIpfLoc* LcgIpfCodeGen::tmp_addr1 = new(loc_mem) LcgIpfLoc(LLK_Gr, 28); +const LcgIpfLoc* LcgIpfCodeGen::tmp_addr2 = new(loc_mem) LcgIpfLoc(LLK_Gr, 27); +const LcgIpfLoc* LcgIpfCodeGen::tmp_addr3 = new(loc_mem) LcgIpfLoc(LLK_Gr, 26); +const LcgIpfLoc* LcgIpfCodeGen::tmp_addl = new(loc_mem) LcgIpfLoc(LLK_Gr, 3); +const unsigned LcgIpfCodeGen::tmp_pred = 6; // use p6 (scratch) for any conditional jumps +const unsigned LcgIpfCodeGen::tmp_br = 6; //use BR 6 as a temporary branch address register + + + +LilCodeGeneratorIpf::LilCodeGeneratorIpf() + : LilCodeGenerator() +{ +} + +NativeCodePtr LilCodeGeneratorIpf::compile_main(LilCodeStub* cs, size_t* stub_size, PoolManager* code_pool) { + + // start a memory manager + tl::MemoryPool m; + + // get context info and do a prepass + LcgIpfContext context(cs, m); + + // initiate an IPF code emitter + Merced_Code_Emitter emitter(m, 100, context.get_num_targets()); + emitter.memory_type_is_unknown(); // fix later + emitter.disallow_instruction_exchange(); + + LcgIpfCodeGen codegen(cs, emitter, context, m); + + // get the goodies from the emitter + emitter.flush_buffer(); + *stub_size = emitter.get_size(); + NativeCodePtr buffer = allocate_memory(*stub_size, code_pool); + emitter.copy((char*)buffer); + flush_hw_cache((Byte*)buffer, *stub_size); + sync_i_cache(); + + return buffer; +} // compile_main + +GenericFunctionPointer lil_npc_to_fp(NativeCodePtr ncp) +{ + void** p = (void**)STD_MALLOC(2*sizeof(void*)); + assert(p); + p[0] = ncp; + p[1] = get_vm_gp_value(); + + return (GenericFunctionPointer)p; +} // lil_npc_to_fp diff --git a/vm/vmcore/src/lil/ipf/m2n_ipf.cpp b/vm/vmcore/src/lil/ipf/m2n_ipf.cpp new file mode 100644 index 0000000..d6f1068 --- /dev/null +++ b/vm/vmcore/src/lil/ipf/m2n_ipf.cpp @@ -0,0 +1,514 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#define LOG_DOMAIN = "vm.m2n" +#include "cxxlog.h" + +#include "Code_Emitter.h" +#include "environment.h" +#include "m2n.h" +#include "m2n_ipf_internal.h" +#include "vm_ipf.h" +#include "vm_threads.h" +#include "open/types.h" +#include "open/vm_util.h" +#include "stub_code_utils.h" +#include "interpreter.h" +#include "exceptions.h" + + +////////////////////////////////////////////////////////////////////////// +// Utilities + +extern "C" void *do_flushrs_asm(); + +extern "C" void *do_flushrs() +{ + return do_flushrs_asm(); +} //do_flushrs + +// Given a bsp value for register 32 and a stacked register number +// return a pointer to where the stacked register is spilled +uint64* get_stacked_register_address(uint64* bsp, unsigned reg) +{ + if (interpreter_enabled()) { + return interpreter.interpreter_get_stacked_register_address(bsp, reg); + } + assert(bsp && 32<=reg && reg<128); + unsigned r = (reg-32)<<3; + uint64 b = (uint64)bsp; + uint64 d4 = b+r; + uint64 d5 = (b&0x1f8)+r; + if (d5>=63*8) + if (d5>=126*8) + d4 += 16; + else + d4 += 8; + return (uint64*)d4; +} + +// Get the bsp value for register 32 of the M2nFrame +uint64* m2n_get_bsp(M2nFrame* m2nf) +{ + return (uint64*)m2nf; +} + +uint64* m2n_get_extra_saved(M2nFrame* m2nf) +{ + do_flushrs(); + return (uint64*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_EXTRA_SAVED_PTR); +} + +////////////////////////////////////////////////////////////////////////// +// M2nFrame Interface + +//***** Generic Interface + +// fill m2n frame as empty +void m2n_null_init(M2nFrame* m2n){ + memset(m2n, 0, sizeof(M2nFrame)); +} + +VMEXPORT // temporary solution for interpreter unplug +M2nFrame* m2n_get_last_frame() +{ + return (M2nFrame*)p_TLS_vmthread->last_m2n_frame; +} + +VMEXPORT // temporary solution for interpreter unplug +M2nFrame* m2n_get_last_frame(VM_thread* thread) +{ + return (M2nFrame*)thread->last_m2n_frame; +} + +VMEXPORT // temporary solution for interpreter unplug +void m2n_set_last_frame(M2nFrame* lm2nf) +{ + vm_thread_t vm_thread = jthread_self_vm_thread_unsafe(); + vm_thread->last_m2n_frame = lm2nf; +} + +VMEXPORT +void m2n_set_last_frame(VM_thread* thread, M2nFrame* lm2nf) +{ + thread->last_m2n_frame = lm2nf; +} + +VMEXPORT // temporary solution for interpreter unplug +M2nFrame* m2n_get_previous_frame(M2nFrame* m2nfl) +{ + assert(m2nfl); + do_flushrs(); + return (M2nFrame*)*get_stacked_register_address(m2n_get_bsp(m2nfl), M2N_SAVED_M2NFL); +} + +ObjectHandles* m2n_get_local_handles(M2nFrame* m2nf) +{ + assert(m2nf); + do_flushrs(); + return (ObjectHandles*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_OBJECT_HANDLES); +} + +void m2n_set_local_handles(M2nFrame* m2nf, ObjectHandles* handles) +{ + assert(m2nf); + do_flushrs(); + uint64* p_head = get_stacked_register_address(m2n_get_bsp(m2nf), M2N_OBJECT_HANDLES); + *p_head = (uint64)handles; +} + +NativeCodePtr m2n_get_ip(M2nFrame* m2nf) +{ + assert(m2nf); + do_flushrs(); + uint64 * UNUSED bsp = (uint64 *)m2nf; + assert(bsp); + return (NativeCodePtr)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_SAVED_RETURN_ADDRESS); +} + +// 20040708 New function - needs proper implementation. +void m2n_set_ip(M2nFrame* lm2nf, NativeCodePtr ip) +{ + assert(lm2nf); + ABORT("Not implemented"); +} + +// sets pointer to the registers used for jvmti PopFrame +void set_pop_frame_registers(M2nFrame* m2nf, Registers* regs) { + // FIXME: not sure we want to support this function on IPF + assert(0); + abort(); +} + +// returns pointer to the registers used for jvmti PopFrame +Registers* get_pop_frame_registers(M2nFrame* m2nf) { + // FIXME: not sure we want to support this function on IPF + assert(0); + abort(); + return 0; +} + +Method_Handle m2n_get_method(M2nFrame* m2nf) +{ + assert(m2nf); + do_flushrs(); + uint64 * UNUSED bsp = (uint64 *)m2nf; + assert(bsp); + return (Method_Handle)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_METHOD); +} + +// Returns type of noted m2n frame +frame_type m2n_get_frame_type(M2nFrame* m2nf) { + assert(m2nf); + do_flushrs(); + uint64 * UNUSED bsp = (uint64 *)m2nf; + assert(bsp); + return (frame_type)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_FRAME_TYPE); +} + +// Sets type of noted m2n frame +void m2n_set_frame_type(M2nFrame* m2nf, frame_type m2nf_type) { + assert(m2nf); + do_flushrs(); + uint64 * UNUSED bsp = (uint64 *)m2nf; + assert(bsp); + *get_stacked_register_address(m2n_get_bsp(m2nf), M2N_FRAME_TYPE) = m2nf_type; +} + +size_t m2n_get_size() { + return sizeof(M2nFrame); +} + +//***** Stub Interface + +// Flushes register stack of the current thread into backing store and calls target procedure. +NativeCodePtr m2n_gen_flush_and_call() { + static NativeCodePtr addr = NULL; + + if (addr != NULL) { + return addr; + } + + tl::MemoryPool mem_pool; + Merced_Code_Emitter emitter(mem_pool, 2, 0); + emitter.disallow_instruction_exchange(); + emitter.memory_type_is_unknown(); + + // We need to remember pfs & b0 here but there is no space to save them in. + // Register stack contains valid outputs and we don't know how many registers are used. + // Memory stack holds output values beyound those 8 which are on register stack. + // The only place is general caller-saves registers. It is save to use them with out preserving + // because they are alredy preserved by the corresponding M2N frame. + + // r4 is used to keep a thread pointer...so let's use r5 & r6. + + emitter.ipf_mfap(PRESERV_GENERAL_REG1, AR_pfs); + emitter.ipf_mfbr(PRESERV_GENERAL_REG2, BRANCH_RETURN_LINK_REG); + + emitter.flush_buffer(); + emitter.ipf_flushrs(); + + emitter.ipf_bricall(br_many, br_sptk, br_none, BRANCH_RETURN_LINK_REG, BRANCH_CALL_REG); + + emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, PRESERV_GENERAL_REG2); + emitter.ipf_mtap(AR_pfs, PRESERV_GENERAL_REG1); + + emitter.ipf_brret(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG); + + addr = finalize_stub(emitter, ""); + return addr; +} + +unsigned m2n_gen_push_m2n(Merced_Code_Emitter* emitter, Method_Handle method, frame_type current_frame_type, bool handles, unsigned num_on_stack, unsigned num_local, unsigned num_out, bool do_alloc) +{ + // Allocate new frame + if (do_alloc) { + emitter->ipf_alloc(M2N_SAVED_PFS, 8, M2N_NUMBER_LOCALS+num_local, num_out, 0); + + // The alloc instruction saves pfs, now save return address and GP + emitter->ipf_mfbr(M2N_SAVED_RETURN_ADDRESS, BRANCH_RETURN_LINK_REG); + emitter->ipf_mov (M2N_SAVED_GP, GP_REG); + } + + // 20031205: The alloc writes the CFM and the mfpr reads it, so they must be separated by a stop bit, this is a brutal way of achieving this. + emitter->flush_buffer(); + + // Save predicates, SP, and callee saves general registers + emitter->ipf_adds(M2N_SAVED_SP, num_on_stack, SP_REG); + + emitter->ipf_mfpr(M2N_SAVED_PR ); + emitter->ipf_mfap(M2N_SAVED_UNAT, AR_unat); + emitter->ipf_mov (M2N_SAVED_R4, 4); + emitter->ipf_mov (M2N_SAVED_R5, 5); + emitter->ipf_mov (M2N_SAVED_R6, 6); + emitter->ipf_mov (M2N_SAVED_R7, 7); + + // Set object handles to NULL and set method information + emitter->ipf_mov(M2N_OBJECT_HANDLES, 0); + emit_mov_imm_compactor(*emitter, M2N_METHOD, (uint64)method); + emit_mov_imm_compactor(*emitter, M2N_FRAME_TYPE, (uint64)current_frame_type); + + const int P1 = SCRATCH_PRED_REG; + const int P2 = SCRATCH_PRED_REG2; + const int OLD_RSE_MODE = SCRATCH_GENERAL_REG2; + const int NEW_RSE_MODE = SCRATCH_GENERAL_REG3; + // SCRATCH_GENERAL_REG4 & SCRATCH_GENERAL_REG5 are reserved for std places. + const int BSP = SCRATCH_GENERAL_REG6; + const int IMM_8 = SCRATCH_GENERAL_REG7; + const int IMM_1F8 = SCRATCH_GENERAL_REG8; + const int TMP_REG = SCRATCH_GENERAL_REG9; + // Scratch branch register. + const int TMP_BRANCH_REG = 6; + + // Switch RSE to "forced lazy" mode. This is required to access RNAT. + emitter->ipf_mfap(OLD_RSE_MODE, AR_rsc); + emitter->ipf_dep(NEW_RSE_MODE, 0, OLD_RSE_MODE, 0, 2); + emitter->ipf_mtap(AR_rsc, NEW_RSE_MODE); + + // Flush must be the first instruction in the group. + emitter->flush_buffer(); + // Spill parent frames so that corresponding RNAT bits become valid. + emitter->ipf_flushrs(); + // Extract backing store pointer + emitter->ipf_mfap(BSP, AR_bsp); + // Remember parent RNAT collection. + emitter->ipf_mfap(M2N_EXTRA_RNAT, AR_rnat); + + // TODO: This is not fully legal reset nat bits for the whole m2n frame because it + // contains r4-r7 general registers which may have corresponding unat bits up. + emitter->ipf_mov(M2N_EXTRA_UNAT, 0); + +/* The following code spills M2N into backing store. + emitter->ipf_movl(IMM_1F8, 0, (uint64)0x1f8); + emitter->ipf_movl(IMM_8, 0, (uint64)0x8); + + // Forcebly spill M2N frame into backing store. + for(int i = M2N_NUMBER_INPUTS; i < M2N_NUMBER_LOCALS; i++) { + emitter->ipf_and(TMP_REG, IMM_1F8, BSP); + emitter->ipf_cmp(icmp_eq, cmp_none, P1, P2, IMM_1F8, TMP_REG); + emitter->ipf_add(BSP, BSP, IMM_8, P1); + emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_spill, mem_none, BSP, 32 + i, 8); + } + + // Remember UNAT collection for the current frame. + emitter->ipf_sub(BSP, BSP, IMM_8); + emitter->ipf_mfap(M2N_EXTRA_UNAT, AR_unat); + emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, BSP, M2N_EXTRA_UNAT); + + // Restore original UNAT. + emitter->ipf_mtap(AR_unat, M2N_SAVED_UNAT); + emitter->flush_buffer(); +*/ + + // Switch RSE to the original mode. + emitter->ipf_mtap(AR_rsc, OLD_RSE_MODE); + + // Link M2nFrame into list of current thread + size_t offset_lm2nf = (size_t)&((VM_thread*)0)->last_m2n_frame; + emitter->ipf_adds(SCRATCH_GENERAL_REG2, (int)offset_lm2nf, THREAD_PTR_REG); + emitter->ipf_ld(int_mem_size_8, mem_ld_none, mem_none, M2N_SAVED_M2NFL, SCRATCH_GENERAL_REG2); + emitter->ipf_mfap(SCRATCH_GENERAL_REG7, AR_bsp); + emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, SCRATCH_GENERAL_REG2, SCRATCH_GENERAL_REG7); + + return 32+8+M2N_NUMBER_LOCALS; +} + +void m2n_gen_set_local_handles(Merced_Code_Emitter* emitter, unsigned src_reg) +{ + emitter->ipf_mov(M2N_OBJECT_HANDLES, src_reg); +} + +void m2n_gen_set_local_handles_imm(Merced_Code_Emitter* emitter, uint64 imm_val) +{ + int64 UNUSED imm = (int64)imm_val; + assert(imm>=-0x200000 && imm<-0x200000); + emitter->ipf_movi(M2N_OBJECT_HANDLES, (int)imm_val); +} + +static void m2n_pop_local_handles() { + assert(!hythread_is_suspend_enabled()); + + exn_rethrow_if_pending(); + + M2nFrame *m2n = m2n_get_last_frame(); + free_local_object_handles2(m2n_get_local_handles(m2n)); +} + +static void m2n_free_local_handles() { + assert(!hythread_is_suspend_enabled()); + + if (exn_raised()) { + exn_rethrow(); + } + + M2nFrame * m2n = m2n_get_last_frame(); + // iche free_local_object_handles3(m2n->local_object_handles); + free_local_object_handles3(m2n_get_local_handles(m2n)); // iche +} + +void m2n_gen_pop_m2n(Merced_Code_Emitter* emitter, bool handles, M2nPreserveRet preserve_ret, bool do_alloc, unsigned out_reg, int target) +{ + unsigned free_target; + + if (handles) { + assert(target != -1); // make sure a target has been provided + // Do we need to call free? + free_target = (unsigned) target; + emitter->ipf_cmp(icmp_eq, cmp_none, SCRATCH_PRED_REG, SCRATCH_PRED_REG2, M2N_OBJECT_HANDLES, 0); + emitter->ipf_br(br_cond, br_many, br_spnt, br_none, free_target, SCRATCH_PRED_REG); + } + // Yes, save return register + if (preserve_ret == MPR_Gr) { + emitter->ipf_add(6, RETURN_VALUE_REG, 0); + } else if (preserve_ret == MPR_Fr) { + emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, RETURN_VALUE_REG, unsigned(-16)); + } + + if (handles) { + emit_call_with_gp(*emitter, (void**)m2n_pop_local_handles, false); + } else { + emit_call_with_gp(*emitter, (void**)m2n_free_local_handles, false); + } + + // Restore return register + if (preserve_ret == MPR_Gr) { + emitter->ipf_add(RETURN_VALUE_REG, 6, 0); + } else if (preserve_ret == MPR_Fr) { + emitter->ipf_adds(SP_REG, 16, SP_REG); + emitter->ipf_ldf(float_mem_size_e, mem_ld_fill, mem_none, RETURN_VALUE_REG, SP_REG); + } + + + if (handles) { + emitter->set_target(free_target); + } + + // Unlink the M2nFrame from the list of the current thread + size_t offset_lm2nf = (size_t)&((VM_thread*)0)->last_m2n_frame; + emitter->ipf_adds(SCRATCH_GENERAL_REG2, (int)offset_lm2nf, THREAD_PTR_REG); + emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, SCRATCH_GENERAL_REG2, M2N_SAVED_M2NFL); + + // Restore callee saved general registers, predicates, return address, and pfs + emitter->ipf_mov (7, M2N_SAVED_R7); + emitter->ipf_mov (6, M2N_SAVED_R6); + emitter->ipf_mov (5, M2N_SAVED_R5); + emitter->ipf_mov (4, M2N_SAVED_R4); + emitter->ipf_mtpr( M2N_SAVED_PR); + if (do_alloc) { + emitter->ipf_mov (GP_REG, M2N_SAVED_GP); + emitter->ipf_mtbr(BRANCH_RETURN_LINK_REG, M2N_SAVED_RETURN_ADDRESS); + emitter->ipf_mtap(AR_pfs, M2N_SAVED_PFS); + } +} + +void m2n_gen_save_extra_preserved_registers(Merced_Code_Emitter* emitter) +{ + unsigned reg; + + // Save pointer to saves area + emitter->ipf_mov(M2N_EXTRA_SAVED_PTR, SP_REG); + + // Save callee saves floating point registers + for (reg = 2; reg < 6; reg++) + emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, reg, unsigned(-16)); + for (reg = 16; reg < 32; reg++) + emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, reg, unsigned(-16)); + + // Save callee saves branch registers + for (reg = 1; reg < 6; reg++) { + emitter->ipf_mfbr(SCRATCH_GENERAL_REG, reg); + emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8)); + } + + // Save ar.fpsr, ar.unat, and ar.lc + emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8)); + emitter->ipf_mfap(SCRATCH_GENERAL_REG, AR_unat); + emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8)); + emitter->ipf_mfap(SCRATCH_GENERAL_REG, AR_lc); + emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-24)); + + + // Note that the last postdec (postinc of -24) has created the required scratch area on the memory stack +} + +unsigned m2n_get_last_m2n_reg() { + return M2N_SAVED_PFS + M2N_NUMBER_LOCALS - 1; +} + +unsigned m2n_get_pfs_save_reg() { + return M2N_SAVED_PFS; +} + +unsigned m2n_get_return_save_reg() { + return M2N_SAVED_RETURN_ADDRESS; +} + +unsigned m2n_get_gp_save_reg() { + return M2N_SAVED_GP; +} + +uint64* m2n_get_arg_word(M2nFrame* m2nf, unsigned n) +{ + do_flushrs(); + if (n<8) + return get_stacked_register_address(m2n_get_bsp(m2nf), n+32); + else + return ((uint64*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_SAVED_SP))+(n-8+2); // +2 is for 16-bytes scratch on mem stack +} + +void m2n_push_suspended_frame(M2nFrame* m2nf, Registers* regs) +{ + abort(); // FIXME: check that it works + m2n_push_suspended_frame(p_TLS_vmthread, m2nf, regs); +} + +void m2n_push_suspended_frame(VM_thread* thread, M2nFrame* m2nf, Registers* regs) +{ + // FIXME: not implemented + assert(0); + abort(); +} + + +M2nFrame* m2n_push_suspended_frame(Registers* regs) +{ + abort(); // FIXME: check that it works + return m2n_push_suspended_frame(p_TLS_vmthread, regs); +} + +M2nFrame* m2n_push_suspended_frame(VM_thread* thread, Registers* regs) +{ + abort(); // FIXME: check that it works + M2nFrame* m2nf = (M2nFrame*)STD_MALLOC(sizeof(M2nFrame)); + assert(m2nf); + m2n_push_suspended_frame(thread, m2nf, regs); + return m2nf; +} + +bool m2n_is_suspended_frame(M2nFrame * m2nf) { + // FIXME: not implemented + assert(0); + abort(); + return false; + +} + diff --git a/vm/vmcore/src/lil/ipf/m2n_ipf_internal.h b/vm/vmcore/src/lil/ipf/m2n_ipf_internal.h new file mode 100644 index 0000000..b9c9eef --- /dev/null +++ b/vm/vmcore/src/lil/ipf/m2n_ipf_internal.h @@ -0,0 +1,173 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#ifndef _M2N_IPF_INTERNAL_H_ +#define _M2N_IPF_INTERNAL_H_ + +// This file describes the internal IPF interface of m2n frames. +// It can be used by stubs to generate code to push and pop m2n frames and to update object handles fields. +// It is also used by stack iterators. + +#include "Code_Emitter.h" +#include "m2n.h" +#include "open/types.h" + +// Generate code to push an M2nFrame onto the stack +// The activation frame of the stub is used for the M2nFrame as are certain registers in this frame. +// The stub must preserve all preserved registers including pfs, gp, and b0 from entry to the stub to the time of push_m2n. +// The stub may use up to 8 inputs, the requested number of locals, and the requested number of outputs after push_m2n. +// method: the method to be associated with the M2nFrame or NULL for no association +// handles: does the stub want local handles or not +// num_on_stack: this number plus the current sp is the sp at entry to the stub (should be positive as stack grows down) +// num_local: the number of local registers above the M2N registers required by the stub +// num_outputs: the number of output registers required by the stub +// do_alloc: if false, the function should assume that the stacked register +// frame has been allocated, and no alloc instruction needs to be executed; it should also assume that ar.pfs is already saved at its proper place +// return: the register number for the first local, the outputs immediately follow the locals +// Note, the stub may use some of the 8 inputs as locals if it wants to +unsigned m2n_gen_push_m2n(Merced_Code_Emitter*, Method_Handle method, frame_type current_frame_type, bool handles, unsigned num_on_stack, unsigned num_locals, + unsigned num_outputs, bool do_alloc=true); + +enum M2nPreserveRet { MPR_None, MPR_Gr, MPR_Fr }; + +// Generate code to pop the M2nFrame from the stack. +// This should be matched by a preceeding push_m2n in the stub. +// handles: should match the push_m2n handles argument, if true the generated code will free the handles. +// preserve_ret_regs: the number of return registers to preserve (starting with r8). +// Note that the pop restores the callee saves gp registers, pfs, gp, and b0 to the values that had at the push m2n; it does not restore sp. +// do_alloc: must have the same value as the corresponding m2n_gen_push_m2n() parameter +// target: if handles==true and the vm property vm.free_local_object_handles +// is also true, m2n_gen_pop_m2n will need to set a target in the emitter; +// target will be the number used. Otherwise, this parameter is ignored. +// out_reg: if handles==true and the vm property vm.free_local_object_handles +// is also true, m2n_gen_pop_m2n needs to know the first output register; out_reg is this register. Otherwise this parameter is ignored +void m2n_gen_pop_m2n(Merced_Code_Emitter*, bool handles, M2nPreserveRet preserve_ret_regs, bool do_alloc=true, unsigned out_reg=0, int target=-1); + +// Generate code to set the local handles of the M2nFrame that is also the current frame. +// Preserves all registers that are not used to store M2nFrame information. +void m2n_gen_set_local_handles(Merced_Code_Emitter*, unsigned src_reg); + +// Generate code to set the local handles of the M2nFrame that is also the current frame. +// Preserves all registers that are not used to store M2nFrame information. +void m2n_gen_set_local_handles_imm(Merced_Code_Emitter*, uint64 imm_val); + +// Generate code to save additional preserved registers not normally saved by push_m2n. +// The combination of push_m2n and save_extra_preserved_registers will save all preserved registers as needed by exception propogation. +// The code generated by this function must follow that of push_m2n. +// Note that this function uses the memory stack, expects the scratch area above sp, and leaves a scratch area above sp. +void m2n_gen_save_extra_preserved_registers(Merced_Code_Emitter* emitter); + + +// returns the number of the last GR that the M2N frame uses +unsigned m2n_get_last_m2n_reg(); + +// the following functions return the GR numbers where various things should be saved +unsigned m2n_get_pfs_save_reg(); +unsigned m2n_get_return_save_reg(); +unsigned m2n_get_gp_save_reg(); + +// The IPF calling convention defines how to layout the arguments into words and then how to place +// these into gp registers, fp registers, or memory stack. This function returns a pointer to the +// nth word assuming it is either in a gp register or on the memory stack. +uint64* m2n_get_arg_word(M2nFrame*, unsigned n); + +////////////////////////////////////////////////////////////////////////// +// Implementation details + +// An M2nFrame is always represented using the bsp value for register 32 of the frame +// The information needed for the frame is stored in stacked local registers or on the memory stack. +// It can be accessed by computing the spill location from the bsp value or by retrieving the sp value and + +uint64* m2n_get_bsp(M2nFrame*); +uint64* m2n_get_extra_saved(M2nFrame*); + +// Flushes register stack of the current thread into backing store and calls target procedure. +NativeCodePtr m2n_gen_flush_and_call(); + +// An M2nFrame will always have 8 input registers, some local stacked registers to save stuff, and some outputs + +#define M2N_NUMBER_ALIGNS 2 +#define M2N_NUMBER_INPUTS 8 +#define M2N_NUMBER_LOCALS 17 + +// The following registers are used in M2nFrames to hold the indicated values +// The register numbers must be distinct, at least 40 (so they don't conflict with inputs), and less than 40+M2N_NUMBER_LOCALS + +#define M2N_SAVED_PFS 40 +#define M2N_SAVED_RETURN_ADDRESS 41 +#define M2N_SAVED_M2NFL 42 +#define M2N_SAVED_SP 43 +#define M2N_SAVED_GP 44 +#define M2N_SAVED_PR 45 +#define M2N_SAVED_UNAT 46 +#define M2N_SAVED_R4 47 +#define M2N_SAVED_R5 48 +#define M2N_SAVED_R6 49 +#define M2N_SAVED_R7 50 +#define M2N_EXTRA_SAVED_PTR 51 +#define M2N_OBJECT_HANDLES 52 +#define M2N_METHOD 53 +#define M2N_FRAME_TYPE 54 +#define M2N_EXTRA_RNAT 55 +// this must be last register +#define M2N_EXTRA_UNAT 56 + +// Only the callee saves general registers are normally saved in the M2nFrame along with special things like pfs, return address, etc. +// The full set of preserved registers includes callee saves floating point and branch registers as well. +// These are saved, if requested, onto the memory stack as follows: +// +-------------------------+ +// | Saved f2 | +// Extra Saved ---> +-------------------------+ +// | Saved f3..f5 | +// +-------------------------+ +// | Saved f16..f31 | +// +-------------------------+ +// | Scratch area (8 bytes) | +// +-------------------------+ +// | Saved b1..b5 | +// +-------------------------+ +// | Saved ar.fpsr | +// +-------------------------+ +// | Saved ar.unat | +// +-------------------------+ +// | Saved ar.lc | +// +-------------------------+ + +#define M2N_EXTRA_SAVES_SPACE 400 + +#ifdef _EM64T_ +#error Should not be included! +#endif + +struct M2nFrame { + union { + uint64 buff[M2N_NUMBER_INPUTS + M2N_NUMBER_LOCALS + M2N_NUMBER_ALIGNS]; + struct { + M2nFrame* prev_m2nf; + ObjectHandles* local_object_handles; + Method_Handle method; + frame_type current_frame_type; // type of the current frame also shows is the frame unwindable + }; + }; +}; + +#endif //!_M2N_IPF_INTERNAL_H_ diff --git a/vm/vmcore/src/lil/ipf/stack_iterator_ipf.cpp b/vm/vmcore/src/lil/ipf/stack_iterator_ipf.cpp new file mode 100644 index 0000000..4135c16 --- /dev/null +++ b/vm/vmcore/src/lil/ipf/stack_iterator_ipf.cpp @@ -0,0 +1,624 @@ +/* + * 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. + */ + +/** + * @author Intel, Pavel Afremov + * @version $Revision$ + */ + +#define LOG_DOMAIN "port.old" +#include "cxxlog.h" + +#include "environment.h" +#include +#include +#include +#include + +using namespace std; + +#include "open/types.h" +#include "jit_intf_cpp.h" +#include "m2n.h" +#include "m2n_ipf_internal.h" +#include "method_lookup.h" +#include "nogc.h" +#include "vm_ipf.h" +#include "vm_threads.h" +#include "stack_iterator.h" +#include "stub_code_utils.h" +#include "root_set_enum_internal.h" +#include "cci.h" + +#include "dump.h" +#include "vm_stats.h" + +// Invariants: +// Note that callee saves and stacked registers below means both the pointers and the corresponding nat bits in nats_lo and nats_hi +// All frames: +// last_legal_rsnat should point to a valid spilled nat set on the rse stack (for the stack of this frame context) +// extra_nats should be for the nats bits that are or would be spilled next after last_legal_rsnat +// the valid bits of extra_nats plus all spilled nat sets at or below last_legal_rsnat must cover all of the relevant parts of the rse stack +// the bottom 32 bits of nats_lo should reflect the nat status of those registers in the current frame +// Native frames: +// m2nfl should point to the m2n frame list for the native frame +// cci must be zero +// Managed frames: +// m2nfl should point to the m2n frame immediately preceeding the current one or NULL if there is no preceeding m2n frame +// cci must point to the code chunk info for the ip at which the frame is suspended, and must be nonnull +// bsp should point to the bsp spill location for gr32 of the current frame +// the callee saves registers and stacked registers of the frame should point to their values at the point of suspension of the frame +// c.p_ar_pfs should point to the saved pfs for the current frame (ie the cfm for the current frame formatted as for ar.pfs) +// c.p_eip and c.sp should point-to/have their values at the time the frame was suspended + +struct StackIterator { + CodeChunkInfo* cci; + JitFrameContext c; + M2nFrame* m2nfl; + uint64 ip; + uint64* bsp; + uint64 extra_rnats; + uint64 extra_unats; +}; + +////////////////////////////////////////////////////////////////////////// +// Utilities + +// At some point the rse engine was flushed upto bsp and rnat is the rnat register immediately after +// this flush. We assume that all nat bits we are interested in do not change from the time of flush +// to after we are finished with the iterator that calls this function, even if the rse engine has +// returned to a point prior to bsp. +/* +static void si_init_nats(StackIterator* si, uint64* bsp, uint64 rnat) +{ + si->last_legal_rsnat = (uint64*)((uint64)bsp & ~0x1f8); + si->extra_nats = rnat; +} +*/ + +// This function flushes the rse and puts the value of bsp/bspstore into res[1] and rnat into res[0] +/* +extern "C" void get_rnat_and_bsp(uint64* res); +extern "C" void get_rnat_and_bspstore(uint64* res); +*/ + +static uint64 get_rnat(uint64 * bsp) { + uint64 * last_m2n = (uint64 *)m2n_get_last_frame(); + uint64 * rnat_ptr = (uint64*)((uint64)bsp | (uint64)0x1f8); + uint64 * extra_nat_ptr; + + if (rnat_ptr <= last_m2n) { + return *rnat_ptr; + } + + // All nat bits for last M2N are stored at M2N_EXTRA_UNAT. + // All nat bits for parent frames are stored at M2N_EXTRA_RNAT. + + if (bsp >= last_m2n) { + extra_nat_ptr = last_m2n + (M2N_EXTRA_UNAT - 32); + } else { + extra_nat_ptr = last_m2n + (M2N_EXTRA_RNAT - 32); + } + + if (rnat_ptr <= extra_nat_ptr) { + // There is rnat collection inside M2N. Need to adjust... + extra_nat_ptr += 1; + } + + return *extra_nat_ptr; +} + +// Setup the stacked register for the current frame given bsp and ar.pfs (cfm for current frame) +static void si_setup_stacked_registers(StackIterator* si) +{ + const uint64 ALL_ONES = ~0; + uint64 pfs = *si->c.p_ar_pfs; + unsigned sof = (unsigned)EXTRACT64_SOF(pfs); + + uint64 nats_lo = si->c.nats_lo & 0xffffffff; + uint64 nats_hi = 0; + uint64* bsp = si->bsp; + uint64 nats = get_rnat(bsp); + + unsigned index = (unsigned)(((uint64)bsp & (uint64)0x1f8) >> 3); + uint64 mask = ((uint64)1) << index; + + for(unsigned i=0; ic.p_gr[32+i] = bsp; + // Set the nat bit of the register + if (nats & mask) + if (i<32) + nats_lo |= (1<<(i+32)); + else + nats_hi |= (1<<(i-32)); + // Advance bsp and mask + bsp++; + mask <<= 1; + // If bsp is on a spilled rsnat recompute nats and mask + if (((uint64)bsp&(uint64)0x1f8) == (uint64)0x1f8) { + bsp++; + mask = 1; + nats = get_rnat(bsp); + } + } + + si->c.nats_lo = nats_lo; + si->c.nats_hi = nats_hi; +} + +// Set p_eip, sp, callee saves registers, and ar_pfs for the frame prior to the current m2nfl +static void si_unwind_from_m2n(StackIterator* si) +{ +#ifdef VM_STATS + VM_Statistics::get_vm_stats().num_unwind_native_frames_all++; +#endif + // First setup the stack registers for the m2n frame + si->bsp = m2n_get_bsp(si->m2nfl); + uint64 pfs = M2N_NUMBER_LOCALS+8; + si->c.p_ar_pfs = &pfs; + si_setup_stacked_registers(si); + + // Now extract various saved values from the m2n frame + + // Callee saved general registers + si->c.p_gr[4] = si->c.p_gr[M2N_SAVED_R4]; + si->c.p_gr[5] = si->c.p_gr[M2N_SAVED_R5]; + si->c.p_gr[6] = si->c.p_gr[M2N_SAVED_R6]; + si->c.p_gr[7] = si->c.p_gr[M2N_SAVED_R7]; + assert(M2N_SAVED_R7 < 64); + si->c.nats_lo = si->c.nats_lo & ~(uint64)0xf0 | (si->c.nats_lo >> (M2N_SAVED_R4-4)) & (uint64)0xf0; + + // IP, SP, PFS, preds, unat, m2nfl + si->c.p_eip = si->c.p_gr[M2N_SAVED_RETURN_ADDRESS]; + si->c.sp = *si->c.p_gr[M2N_SAVED_SP]; + si->c.p_ar_pfs = si->c.p_gr[M2N_SAVED_PFS]; + si->c.preds = *si->c.p_gr[M2N_SAVED_PR]; + si->c.ar_unat = *si->c.p_gr[M2N_SAVED_UNAT]; + si->m2nfl = m2n_get_previous_frame(si->m2nfl); +} + +// Adjust the bsp based on the old frames bsp and ar.pfs (which is the cfm for the new frame) +static void si_unwind_bsp(StackIterator* si) +{ + uint64 pfs = *si->c.p_ar_pfs; + unsigned sol = (unsigned)EXTRACT64_SOL(pfs); + + assert(sol<=96); + + uint64 bsp = (uint64)si->bsp; + uint64 local_area_size = sol << 3; + + // Direct computation, see IPF arch manual, volume 3, table 6.2. + uint64 d2 = bsp - local_area_size; + uint64 d3 = 62*8 - (bsp & 0x1f8) + local_area_size; + if (d3 >= 63*8) + if (d3 >= 126*8) + d2 -= 16; + else + d2 -= 8; + si->bsp = (uint64*)d2; +} + +typedef void (__cdecl *transfer_control_stub_type)(StackIterator*, uint64*); +struct Fp { + NativeCodePtr addr; + void* gp; +}; + +// The transfer control stub takes two arguments: +// i0: pointer to the stack iterator with context to transfer to +// i1: value of the unat register for restoring nonstacked registers with nat bits +// The stub does not return +// The stack iterator should be one for the current thread +static transfer_control_stub_type gen_transfer_control_stub() +{ + static Fp fp = {NULL, NULL}; + if (fp.addr) { + return (transfer_control_stub_type)&fp; + } + + tl::MemoryPool mem_pool; + Merced_Code_Emitter emitter(mem_pool, 2, 0); + emitter.disallow_instruction_exchange(); + emitter.memory_type_is_unknown(); + unsigned i; + + // This register will hold the pointer to the stack iterator + const int stack_iterator_ptr = SCRATCH_GENERAL_REG2; + // This register points to the unat values to use for restoring global general registers + const int unat_ptr = SCRATCH_GENERAL_REG3; + // This register will hold the new SP until after the stack is finished with + const int tmp_sp = SCRATCH_GENERAL_REG4; + // These registers are used to hold temporary values + const int tmp1 = SCRATCH_GENERAL_REG5; + const int tmp2 = SCRATCH_GENERAL_REG6; + const int tmp3 = SCRATCH_GENERAL_REG7; + + emitter.ipf_mov(stack_iterator_ptr, IN_REG0); + emitter.ipf_mov(unat_ptr, IN_REG1); + + // Cover and flush the register stack. + // This is needed to properly set bsp using bspstore below. + + emitter.ipf_cover(); + emitter.ipf_flushrs(); + + // Restore ar.pfs, ip to return branch register, sp to a temp register, and the predicates + uint64 ar_pfs_offset = (uint64)&((StackIterator*)0)->c.p_ar_pfs; + emitter.ipf_adds(tmp1, (int)ar_pfs_offset, stack_iterator_ptr); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); + emitter.ipf_mtap(AR_pfs, tmp1); + uint64 ip_offset = (uint64)&((StackIterator*)0)->c.p_eip; + emitter.ipf_adds(tmp1, (int)ip_offset, stack_iterator_ptr); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); + emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, tmp1); + uint64 sp_offset = (uint64)&((StackIterator*)0)->c.sp; + emitter.ipf_adds(tmp1, (int)sp_offset, stack_iterator_ptr); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp_sp, tmp1); + uint64 preds_offset = (uint64)&((StackIterator*)0)->c.preds; + emitter.ipf_adds(tmp1, (int)preds_offset, stack_iterator_ptr); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); + emitter.ipf_mtpr(tmp1); + + // Restore callee-saves general registers and the first return register + uint64 g4_offset = (uint64)&((StackIterator*)0)->c.p_gr[4]; + emitter.ipf_adds(tmp1, (int)g4_offset, stack_iterator_ptr); + for(i=4; i<=8; i++) { + emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp3, unat_ptr, 8); + emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp1, 8); + emitter.ipf_mtap(AR_unat, tmp3); + emitter.ipf_cmp(icmp_eq, cmp_none, SCRATCH_PRED_REG, SCRATCH_PRED_REG2, tmp2, 0); + emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, i, tmp2, SCRATCH_PRED_REG2); + } + + // Restore callee-saves floating-point registers + uint64 f2_offset = (uint64)&((StackIterator*)0)->c.p_fp[2]; + emitter.ipf_adds(tmp1, (int)f2_offset, stack_iterator_ptr); + for(i=2; i<=5; i++) { + emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp1, (i==5 ? 88 : 8)); + emitter.ipf_ldf(float_mem_size_e, mem_ld_fill, mem_none, i, tmp2); + } + for(i=16; i<=31; i++) { + emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp1, 8); + emitter.ipf_ldf(float_mem_size_e, mem_ld_fill, mem_none, i, tmp2); + } + + // Restore callee-saves branch registers + uint64 b1_offset = (uint64)&((StackIterator*)0)->c.p_br[1]; + emitter.ipf_adds(tmp1, (int)b1_offset, stack_iterator_ptr); + for(i=1; i<=5; i++) { + emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp1, 8); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp2); + emitter.ipf_mtbr(i, tmp2); + } + + // Restore ar.fpsr, ar.unat, and ar.lc + uint64 fpsr_offset = (uint64)&((StackIterator*)0)->c.ar_fpsr; + emitter.ipf_adds(tmp1, (int)fpsr_offset, stack_iterator_ptr); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); + uint64 unat_offset = (uint64)&((StackIterator*)0)->c.ar_unat; + emitter.ipf_adds(tmp1, (int)unat_offset, stack_iterator_ptr); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); + emitter.ipf_mtap(AR_unat, tmp1); + uint64 lc_offset = (uint64)&((StackIterator*)0)->c.ar_lc; + emitter.ipf_adds(tmp1, (int)lc_offset, stack_iterator_ptr); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp1, tmp1); + emitter.ipf_mtap(AR_lc, tmp1); + + // Restore rse stack and the memory stack + // For the rse stack we must go to rse enforced lazy mode + emitter.ipf_mfap(tmp1, AR_rsc); + emitter.ipf_andi(tmp2, -4, tmp1); + emitter.ipf_mtap(AR_rsc, tmp2); + uint64 bsp_offset = (uint64)&((StackIterator*)0)->bsp; + emitter.ipf_adds(tmp2, (int)bsp_offset, stack_iterator_ptr); + emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, tmp2, tmp2); + emitter.ipf_mtap(AR_rnat, 0); + emitter.ipf_mtap(AR_bspstore, tmp2); + emitter.ipf_mtap(AR_rsc, tmp1); + emitter.ipf_mov(SP_REG, tmp_sp); + + enforce_calling_conventions(&emitter); + // Return to the code + emitter.ipf_brret(br_many, br_sptk, br_none, BRANCH_RETURN_LINK_REG); + + emitter.flush_buffer(); + size_t stub_size = emitter.get_size(); + fp.addr = (NativeCodePtr)malloc_fixed_code_for_jit(stub_size, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_DEFAULT, CAA_Allocate); + emitter.copy((char*)fp.addr); + flush_hw_cache((Byte*)fp.addr, stub_size); + sync_i_cache(); + + DUMP_STUB(fp.addr, "transfer_control_stub (non-LIL)", stub_size); + + fp.gp = get_vm_gp_value(); + + return (transfer_control_stub_type)&fp; +} + +////////////////////////////////////////////////////////////////////////// +// Stack Iterator Interface + +StackIterator* si_create_from_native() +{ + return si_create_from_native(p_TLS_vmthread); +} + +void si_fill_from_native(StackIterator* si) +{ + si_fill_from_native(si, p_TLS_vmthread); +} + +StackIterator* si_create_from_native(VM_thread* thread) +{ + // Allocate iterator + StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); + assert(res); + + // Setup current frame + si_fill_from_native(res, thread); + return res; +} + +void si_fill_from_native(StackIterator* si, VM_thread * thread) { + memset(si, 0, sizeof(StackIterator)); + + // Setup current frame + si->cci = NULL; + si->m2nfl = m2n_get_last_frame(thread); + si->ip = 0; + si->c.p_eip = &si->ip; +} + +/* +#if defined (PLATFORM_POSIX) +#elif defined (PLATFORM_NT) + +// Get the bspstore and rnat values of another thread from the OS. +// Hopefully this will also flush the RSE of the other stack enough for our purposes. +static void get_bsp_and_rnat_from_os(VM_thread * thread, uint64 ** bspstore, uint64 * rnat) { + CONTEXT ctx; + ctx.ContextFlags |= CONTEXT_INTEGER; + BOOL UNREF stat = GetThreadContext(thread->thread_handle, &ctx)); + assert(stat); + *bspstore = (uint64*)ctx.RsBSPSTORE; + *rnat = ctx->RsRNAT; +} + +StackIterator* si_create_from_native(VM_thread* thread) +{ + // Allocate iterator + StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); + assert(res); + + // Setup last_legal_rsnat and extra_nats + uint64* bsp; + uint64 rnat; + + get_bsp_and_rnat_from_os(thread, &bsp, &rnat); + si_init_nats(res, bsp, rnat); + + // Check that bsp covers everything + assert((uint64)m2n_get_last_frame(thread)+(M2N_NUMBER_LOCALS+9)*8 <= (uint64)bsp); + + // Setup current frame + res->cci = NULL; + res->m2nfl = m2n_get_last_frame(thread); + res->ip = NULL; + res->c.p_eip = &res->ip; + + return res; +} + +#else +#error Stack iterator is not implemented for the given platform +#endif +*/ + +// On IPF stack iterators must be created from threads (suspended) in native code. +// We do not support threads suspended in managed code yet. +StackIterator* si_create_from_registers(Registers*, bool is_ip_past, M2nFrame*) +{ + ABORT("Not implemented"); + return NULL; +} + +void si_fill_from_registers(StackIterator* si, Registers*, bool is_ip_past, M2nFrame*) +{ + ABORT("Not implemented"); +} + +size_t si_size(){ + return sizeof(StackIterator); +} + +void si_transfer_all_preserved_registers(StackIterator* si) +{ + unsigned i; + uint64* sp = m2n_get_extra_saved(si->m2nfl); + + // Floating point registers + for(i=2; i<=5; i++) { + si->c.p_fp[i] = sp; + sp -= 2; + } + for(i=16; i<=31; i++) { + si->c.p_fp[i] = sp; + sp -= 2; + } + + // Branch registers + for(i=1; i<=5; i++) { + si->c.p_br[i] = sp; + sp--; + } + + // ar.fpsr, ar.unat, ar.lc + si->c.ar_fpsr = *sp--; + si->c.ar_unat = *sp--; + si->c.ar_lc = *sp--; +} + +bool si_is_past_end(StackIterator* si) +{ + return si->cci==NULL && si->m2nfl==NULL; +} + +void si_goto_previous(StackIterator* si, bool over_popped) +{ + if (si->cci) { + assert(si->cci->get_jit() && si->cci->get_method()); + si->cci->get_jit()->unwind_stack_frame(si->cci->get_method(), si_get_jit_context(si)); + } else { + if (!si->m2nfl) return; + si_unwind_from_m2n(si); + } + Global_Env *env = VM_Global_State::loader_env; + si->c.is_ip_past = TRUE; + si->cci = env->vm_methods->find(si_get_ip(si)); + si_unwind_bsp(si); + si_setup_stacked_registers(si); +} + +StackIterator* si_dup(StackIterator* si) +{ + StackIterator* res = (StackIterator*)STD_MALLOC(sizeof(StackIterator)); + memcpy(res, si, sizeof(StackIterator)); + // If si uses itself for IP then res should also to avoid problems if si is deallocated first. + if (si->c.p_eip == &si->ip) + res->c.p_eip = &res->ip; + return res; +} + +void si_free(StackIterator* si) +{ + STD_FREE(si); +} + +void* si_get_sp(StackIterator* si) { + return (void*)si->c.sp; +} + +NativeCodePtr si_get_ip(StackIterator* si) +{ + return (NativeCodePtr)*si->c.p_eip; +} + +void si_set_ip(StackIterator* si, NativeCodePtr ip, bool also_update_stack_itself) +{ + if (also_update_stack_itself) { + *(si->c.p_eip) = (uint64)ip; + } else { + si->ip = (uint64)ip; + si->c.p_eip = &si->ip; + } +} + +// 20040713 Experimental: set the code chunk in the stack iterator +void si_set_code_chunk_info(StackIterator* si, CodeChunkInfo* cci) +{ + ABORT("Not implemented"); +} + +CodeChunkInfo* si_get_code_chunk_info(StackIterator* si) +{ + return si->cci; +} + +JitFrameContext* si_get_jit_context(StackIterator* si) +{ + return &si->c; +} + +bool si_is_native(StackIterator* si) +{ + return si->cci==NULL; +} + +M2nFrame* si_get_m2n(StackIterator* si) +{ + return si->m2nfl; +} + +void** si_get_return_pointer(StackIterator* si) +{ + // FIXME: not implemented + assert(0); + abort(); + return 0; +} + +void si_set_return_pointer(StackIterator* si, void** return_value) +{ + si->c.p_gr[RETURN_VALUE_REG] = (uint64*)return_value; + si->c.nats_lo &= ~(1<c.p_eip == &si->ip) + local_si.c.p_eip = &local_si.ip; + //si_free(si); + + // 2. Set the M2nFrame list + m2n_set_last_frame(local_si.m2nfl); + + // 3. Move bsp to next frame + uint64 sol = EXTRACT64_SOL(*local_si.c.p_ar_pfs); + for(unsigned i=0; i> 3; + uint64 mask = 1 << index; + unat[reg] = mask; + } else { + unat[reg] = 0; + } + } + + // 5. Call the stub + transfer_control_stub_type tcs = gen_transfer_control_stub(); + tcs(&local_si, unat+4); +} + +void si_copy_to_registers(StackIterator* si, Registers*) +{ + ABORT("Not implemented"); +} + +void si_set_callback(StackIterator* si, NativeCodePtr* callback) { + ABORT("Not implemented"); +} + +extern "C" void do_loadrs_asm(int loadrs); + +void si_reload_registers() +{ + do_loadrs_asm(0); +} diff --git a/vm/vmcore/src/lil/lil.cpp b/vm/vmcore/src/lil/lil.cpp new file mode 100644 index 0000000..f1a6416 --- /dev/null +++ b/vm/vmcore/src/lil/lil.cpp @@ -0,0 +1,2708 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.2.4.4 $ + */ + + +#include + +//MVM +#include + +using namespace std; + +#define LOG_DOMAIN "vm.helpers" +#include "cxxlog.h" + +#include +#include +#include + +#include "lil.h" +#include "open/types.h" +#include "open/vm.h" +#include "nogc.h" + +// Forward decs of local functions + +static LilInstruction* lil_find_label(LilCodeStub*, LilLabel); + +//////////////////////////////////////////////////////////////////////////////////////// +// The LIL internal representation + +struct LilSig { + LilCc cc; + bool arbitrary; // If true then num_arg_types must be 0 + unsigned num_arg_types; // Must be 0 if arbitrary is true + LilType* arg_types; + enum LilType ret_type; +}; + +struct LilCond { + enum LilPredicate tag; + LilOperand o1, o2; +}; + +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 +}; + +struct LilInstruction { + enum LilInstructionTag tag; + union { + struct { + LilLabel l; + LilInstructionContext* ctxt; // Computed lazily + } label; + unsigned locals; + unsigned std_places; + struct { + LilVariable dst; + unsigned num_bytes; + } alloc; + struct { + enum LilOperation op; + LilVariable dst; + LilOperand o1, o2; + } asgn; + LilVariable ts; + LilOperand handles; + // Both ld, st, inc, & case use ldst + // operand is dst for ld, src for st, unused for inc, src for case + // cmp and l are used only for cas + // [base,scale,index,offset] is src for ld, dst for st + // base present iff is_base + // index present iff is_index + struct { + LilType t; + bool is_base, is_index; + LilVariable base, index; + LilOperand operand; + unsigned scale; + POINTER_SIZE_SINT offset; + LilAcqRel acqrel; + LilLdX extend; + LilOperand compare; + LilLabel l; + } ldst; + LilLabel j; + struct { + LilCond c; + LilLabel l; + } jc; + LilSig out; + struct { + LilCallKind k; + LilOperand target; + } call; + struct { + Method_Handle method; + frame_type current_frame_type; + bool handles; + } push_m2n; + struct { + char *str; + LilOperand arg; + } print; + } u; + struct LilInstruction* next; +}; + + +enum LilCodeStubContexts { LCSC_NotComputed, LCSC_Computed, LCSC_Error }; + +struct LilCodeStub { + tl::MemoryPool* my_memory; + unsigned cur_gen_label; + unsigned num_std_places; + LilSig sig; + LilInstruction* is; + LilCodeStubContexts ctxt_state; + unsigned num_is, max_std_places, max_locals; + LilInstructionContext* init_ctxt; // Computed lazily + + // size of generated code + size_t compiled_code_size; + + // a list of BBs in this code stub; null if the FG has not been initializsed; computed lazily + LilBb* bb_list_head; + unsigned num_bbs; +}; + + + +//*** Freers + +VMEXPORT void lil_free_code_stub(LilCodeStub* cs) +{ + delete cs->my_memory; +} + +//*** Freers - end + + +//////////////////////////////////////////////////////////////////////////////////// +// The LIL Parser + +// The LIL parser is loosely designed as a scannerless recursive-descent parser. +// The various functions take a pointer to a source string pointer and update +// the pointer as they parse. Most functions are designed to advance the pointer +// only by token amounts. Functions that parse something and return a pointer +// to the structure, return NULL on failure. Other functions that parse directly +// a structure, take a pointer to the structure to parse into, and return a boolean +// to indicate success; the latter also include parsing primitive types. +// Parsing functions other than for code stubs do not cleanup allocated memory as +// this is arena deallocated in parse_code_stub. + +// Here are some character classes used in various parts of the parser + +#define alpha(c) (((c)>='a' && (c)<='z') || ((c)>='A' && (c)<='Z') || (c)=='_') +#define num(c) ((c)>='0' && (c) <='9') +#define hexnum(c) (num(c) || ((c)>='a' && (c)<='f') || ((c)>='A' && (c)<='F')) +#define alphanum(c) (alpha(c) || num(c)) + +static void error(const char** src, char* err1, char* err2="") +{ + FILE* err_f = stderr; + fprintf(err_f, "lil parse error: %s%s\n\t", err1, err2); + unsigned count=0; + const char* s =*src; + while (s[0] && count++<70) { + fputc((s[0]<=' ' ? ' ' : s[0]), err_f); + s++; + } + fputc('\n', err_f); + fflush(err_f); +} + +// Skip over any white space +static void lil_skip_ws(const char** src) +{ + while ((*src)[0]==' ' || (*src)[0]=='\n' || (*src)[0]=='\r' || (*src)[0]=='\t' || (*src)[0]=='/') { + if ((*src)[0]=='/') + if ((*src)[1]=='/') + while ((*src)[0] && (*src)[0]!='\n') ++*src; + else + return; + else + ++*src; + } +} + +struct LilVarArgs { + unsigned current; + va_list val; +}; + +static char lil_parse_percent(const char** src, LilVarArgs* va) +{ + if ((*src)[0]!='%') { error(src, "expecting %", ""); return '\0'; } + unsigned i=1, v=0; + while (num((*src)[i])) + v = 10*v+((*src)[i++]-'0'); + if (v!=va->current) { error(src, "%s not numbered sequentially", ""); return '\0'; } + va->current++; + *src += i+1; + return (*src)[-1]; +} + +// Check and skip over a specific "keyword" +// This function does not change *src on failure +// (except to skip whitespace), +// and this is needed in some code below +static bool lil_parse_kw_no_error(const char** src, char* kw) +{ + lil_skip_ws(src); + unsigned c; + for (c=0; kw[c]; c++) + if ((*src)[c]!=kw[c]) return false; + // If the keyword could be an identifier or number then check that the source is + // not a longer identifier or number + if (alphanum(kw[c-1]) && alphanum((*src)[c])) return false; + *src += c; + return true; +} + +static bool lil_parse_kw(const char** src, char* kw) +{ + bool res = lil_parse_kw_no_error(src, kw); + if (!res) error(src, "expected ", kw); + return res; +} + +// Parse a calling convention +static bool lil_parse_cc(const char** src, LilCc* cc) +{ + lil_skip_ws(src); + switch ((*src)[0]) { + case 'j': // jni + if (!lil_parse_kw(src, "jni")) return false; + *cc = LCC_Jni; + return true; + case 'm': // managed + if (!lil_parse_kw(src, "managed")) return false; + *cc = LCC_Managed; + return true; + case 'p': // platform + if (!lil_parse_kw(src, "platform")) return false; + *cc = LCC_Platform; + return true; + case 'r': // rth + if (!lil_parse_kw(src, "rth")) return false; + *cc = LCC_Rth; + return true; + case 's': // stdcall + if (!lil_parse_kw(src, "stdcall")) return false; + *cc = LCC_StdCall; + return true; + default: + error(src, "bad calling convention", ""); + return false; + } +} + +// Parse a type +// If ret is true then void is allowed otherwise not +static bool lil_parse_type(const char** src, LilType* t, bool ret) +{ + lil_skip_ws(src); + switch ((*src)[0]) { + case 'f': // f4, f8 + if ((*src)[1]=='4') *t = LT_F4; + else if ((*src)[1]=='8') *t = LT_F8; + else { error(src, "expecting f4 or f8", ""); return false; } + if (alphanum((*src)[2])) { error(src, "expecting f4 or f8", ""); return false; } + *src += 2; + return true; + case 'g': // g1, g2, g4, g8 + if ((*src)[1]=='1') *t = LT_G1; + else if ((*src)[1]=='2') *t = LT_G2; + else if ((*src)[1]=='4') *t = LT_G4; + else if ((*src)[1]=='8') *t = LT_G8; + else { error(src, "expecting g1, g2, g4, or g8", ""); return false; } + if (alphanum((*src)[2])) { error(src, "expecting g1, g2, g4, or g8", ""); return false; } + *src += 2; + return true; + case 'r': // refmethod_get_name(meth) + if (!lil_parse_kw(src, "ref")) return false; + *t = LT_Ref; + return true; + case 'p': // pint + if (!lil_parse_kw(src, "pint")) return false; + *t = LT_PInt; + return true; + case 'v': // void + if (!ret) { error(src, "cannot have void type", ""); return false; } + if (!lil_parse_kw(src, "void")) return false; + *t = LT_Void; + return true; + default: + error(src, "bad type", ""); + return false; + } +} + +static LilType type_info_to_lil_type(Type_Info_Handle tih, bool handles) +{ + extern Boolean type_info_is_managed_pointer(Type_Info_Handle tih); + if (type_info_is_managed_pointer(tih)) { + return LT_PInt; + } + VM_Data_Type dt = type_info_get_type(tih); + switch (dt) { + case VM_DATA_TYPE_INT8: + case VM_DATA_TYPE_UINT8: return LT_G1; + case VM_DATA_TYPE_INT16: + case VM_DATA_TYPE_UINT16: return LT_G2; + case VM_DATA_TYPE_INT32: + case VM_DATA_TYPE_UINT32: return LT_G4; + case VM_DATA_TYPE_INT64: + case VM_DATA_TYPE_UINT64: return LT_G8; + case VM_DATA_TYPE_INTPTR: + case VM_DATA_TYPE_UINTPTR: return LT_PInt; + case VM_DATA_TYPE_F8: return LT_F8; + case VM_DATA_TYPE_F4: return LT_F4; + case VM_DATA_TYPE_BOOLEAN: return LT_G1; + case VM_DATA_TYPE_CHAR: return LT_G2; + case VM_DATA_TYPE_CLASS: + case VM_DATA_TYPE_ARRAY: return (handles ? LT_PInt : LT_Ref); + case VM_DATA_TYPE_VOID: return LT_Void; + case VM_DATA_TYPE_VALUE:{ + // ? 20030613: I really don't want this code here, but for now... + Class_Handle UNUSED ch = type_info_get_class(tih); + assert(ch); + assert(class_is_valuetype(ch)); + ASSERT(0, "Unexpected data type"); + } + case VM_DATA_TYPE_MP: + case VM_DATA_TYPE_UP: + return LT_PInt; + case VM_DATA_TYPE_STRING: + case VM_DATA_TYPE_INVALID: + default: ASSERT(0, "Unknown data type"); for(;;); + } +} + +// Parse a signature (cc:Ts:RT) +// Currently this function can only handle a maximum number of +// argument types +#define MAX_ARG_TYPES 20 +static bool lil_parse_sig(const char** src, tl::MemoryPool* mem, LilVarArgs* va, LilSig* s) +{ + if (!lil_parse_cc(src, &(s->cc))) return false; + if (!lil_parse_kw(src, ":")) return false; + lil_skip_ws(src); + if ((*src)[0]=='%') { + char specifier = lil_parse_percent(src, va); + bool jni; + switch (specifier) { + case 'm': + jni = false; + break; + case 'j': + jni = true; + break; + default: + if (specifier) error(src, "bad specifier for signature", ""); + return false; + } + Method_Handle m = va_arg(va->val, Method_Handle); + Method_Signature_Handle sig = method_get_signature(m); + unsigned num_method_args = method_args_get_number(sig); + unsigned num_args = num_method_args; + if (jni) { + num_args++; + if (method_is_static(m)) num_args++; + } + s->arbitrary = false; + s->num_arg_types = num_args; + s->arg_types = (LilType*)mem->alloc(num_args*sizeof(LilType)); + unsigned cur=0; + if (jni) { + s->arg_types[cur++] = LT_PInt; + if (method_is_static(m)) s->arg_types[cur++] = LT_PInt; + } + for(unsigned i=0; iarg_types[cur++] = type_info_to_lil_type(method_args_get_type_info(sig, i), jni); + s->ret_type = type_info_to_lil_type(method_ret_type_get_type_info(sig), jni); + } else if ((*src)[0]=='a') { + if (!lil_parse_kw(src, "arbitrary")) return false; + s->arbitrary = true; + s->num_arg_types = 0; // This is important to the validity checks + s->arg_types = NULL; + s->ret_type = LT_Void; + } else { + s->arbitrary = false; + if ((*src)[0]!=':') { + LilType ts[MAX_ARG_TYPES]; + unsigned cur = 0; + for(;;) { + assert(curnum_arg_types = cur; + s->arg_types = (LilType*)mem->alloc(cur*sizeof(LilType)); + for(unsigned i=0; iarg_types[i] = ts[i]; + } else { + s->num_arg_types = 0; + s->arg_types = NULL; + } + if (!lil_parse_kw(src, ":")) return false; + if (!lil_parse_type(src, &(s->ret_type), true)) return false; + } + return true; +} + +// Parse an immediate string +static bool lil_parse_string(const char** src, char** str) +{ + static char buffer[1000]; + lil_skip_ws(src); + if ((*src)[0] != '\'') + return false; + (*src)++; // skip opening quote + int len; + for (len=0; **src != '\'' && **src != '\0'; ++*src, ++len) { + if (len >= 1000) { + error(src, "string too long (more than 1000 chars)"); + return false; + } + buffer[len] = **src; + } + if (**src == '\0') { + error(src, "open string"); + return false; + } + // skip closing quote + ++*src; + buffer[len] = '\0'; + *str = (char *) malloc_fixed_code_for_jit(strlen(buffer) + 1, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_DEFAULT, CAA_Allocate); + strcpy(*str, buffer); + return true; +} + + +// Parse an immediate +static bool lil_parse_number(const char** src, LilVarArgs* va, POINTER_SIZE_INT* num) +{ + lil_skip_ws(src); + if ((*src)[0]=='%') { + char specifier = lil_parse_percent(src, va); + if (specifier=='i') { + *num = va_arg(va->val, POINTER_SIZE_INT); + return true; + } + else { + if (specifier) error(src, "bad specifier for immediate", ""); + return false; + } + } else if ((*src)[0]=='0' && (*src)[1]=='x') { + *num=0; + unsigned c=2; + while (hexnum((*src)[c])) { + // NB the following statement is dependent upon ASCII ordering + *num = *num * 16 + ((*src)[c]>='a' ? (*src)[c]-'a'+10 : (*src)[c]>='A' ? (*src)[c]-'A'+10 : (*src)[c]-'0'); + c++; + } + if (c==2) { error(src, "0x must be followed by at least one hexidigit", ""); return false; } + *src += c; + return true; + } else { + *num=0; + unsigned c=0; + while (num((*src)[c])) { + *num = *num * 10 + (*src)[c]-'0'; + c++; + } + if (c==0) { error(src, "expecting number", ""); return false; } + *src += c; + return true; + } +} + +// Parse a label +static LilLabel lil_parse_label(const char** src, unsigned* cur_gen_label, tl::MemoryPool* mem) +{ + lil_skip_ws(src); + if ((*src)[0]=='%') { + unsigned index; + switch ((*src)[1]) { + case 'g': + index = ++*cur_gen_label; + break; + case 'p': + index = *cur_gen_label; + break; + case 'n': + index = *cur_gen_label + 1; + break; + case 'o': + index = *cur_gen_label + 2; + break; + default: + error(src, "bad % specifier in label", ""); + return NULL; + } + char* buf = (char*)mem->alloc(17); + sprintf(buf, "$%x", index); + *src += 2; + return buf; + } + if (!alpha((*src)[0])) { error(src, "null label", ""); return NULL; } + unsigned c=1; + while (alphanum((*src)[c])) c++; + char* buf = (char*)mem->alloc(c+1); + for(unsigned i=0; itag = LVK_In; start = 1; break; + case 'l': v->tag = LVK_Local; start = 1; break; + case 'o': v->tag = LVK_Out; start = 1; break; + case 's': + if ((*src)[1]!='p') { error(src, "expecting variable", ""); return false; } + v->tag = LVK_StdPlace; + start = 2; + break; + case 'r': + if (alphanum((*src)[1])) { error(src, "expecting variable", ""); return false; } + v->tag = LVK_Ret; + v->index = 0; + ++*src; + return true; + default: + error(src, "expecting variable", ""); + return false; + } + if ((*src)[start]=='%') { + *src += start; + char specifier = lil_parse_percent(src, va); + if (specifier=='i') { + v->index = va_arg(va->val, int); + return true; + } else { + if (specifier) error(src, "bad specifier for variable index", ""); + return false; + } + } else { + unsigned c=0; + v->index = 0; + while (num((*src)[start+c])) { + v->index = v->index*10 + (*src)[start+c]-'0'; + c++; + } + if (c==0 || alpha((*src)[start+c])) { error(src, "bad variable", ""); return false; } + *src += start+c; + return true; + } +} + + +// Parse an operand +static bool lil_parse_operand(const char** src, LilVarArgs* va, LilOperand* o) +{ + lil_skip_ws(src); + if (((*src)[0]>='0' && (*src)[0]<='9') || (*src)[0]=='%') { + o->is_immed = true; + if (!lil_parse_number(src, va, &(o->val.imm))) return false; + } else { + o->is_immed = false; + if (!lil_parse_variable(src, va, &(o->val.var))) return false; + } + lil_skip_ws(src); + if ((*src)[0]==':') { + o->has_cast = true; + ++*src; + if (!lil_parse_type(src, &o->t, false)) return false; + } else { + o->has_cast = false; + } + return true; +} + +// Parse a condition +static bool lil_parse_cond(const char** src, LilVarArgs* va, LilCond* c) +{ + if (!lil_parse_operand(src, va, &(c->o1))) return false; + lil_skip_ws(src); + if ((*src)[0]=='=') + c->tag = LP_Eq, ++*src; + else if ((*src)[0]=='!' && (*src)[1]=='=') + c->tag = LP_Ne, *src += 2; + else if ((*src)[0]=='<') + if ((*src)[1]=='=') + if ((*src)[2]=='u') + if (alphanum((*src)[3])) + { error(src, "expecting predicate", ""); return false; } + else + c->tag = LP_Ule, *src += 3; + else + c->tag = LP_Le, *src += 2; + else if ((*src)[1]=='u') + if (alphanum((*src)[2])) + { error(src, "expecting predicate", ""); return false; } + else + c->tag = LP_Ult, *src += 2; + else + c->tag = LP_Lt, ++*src; + else + { error(src, "expecting predicate", ""); return false; } + if (!lil_parse_operand(src, va, &(c->o2))) return false; + // Fixup some special cases + if (!c->o1.is_immed && c->o2.is_immed && c->o2.val.imm==0) + if (c->tag==LP_Eq) + c->tag = LP_IsZero; + else if (c->tag==LP_Ne) + c->tag = LP_IsNonzero; + return true; +} + +static bool lil_parse_plusminus(const char** src, int* sign) +{ + lil_skip_ws(src); + if ((*src)[0]=='+') { + *sign = +1; + ++*src; + return true; + } else if ((*src)[0]=='-') { + *sign = -1; + ++*src; + return true; + } else { + error(src, "expecting + or -", ""); + return false; + } +} + +// Parse an address (part of load or store) +static bool lil_parse_address(const char** src, LilVarArgs* va, LilInstruction* i) +{ + int sign = +1; + if (!lil_parse_kw(src, "[")) return false; + lil_skip_ws(src); + if (alpha((*src)[0])) { + if (!lil_parse_variable(src, va, &(i->u.ldst.base))) return false; + if (!lil_parse_plusminus(src, &sign)) return false; + i->u.ldst.is_base = true; + } else { + i->u.ldst.is_base = false; + } + // Parse a number, this could be the scale or the immediate, + // which will be determined by what follows + POINTER_SIZE_INT n; + if (!lil_parse_number(src, va, &n)) return false; + lil_skip_ws(src); + if (lil_parse_kw_no_error(src, "*")) { + if (sign<0) { error(src, "cannot subtract scaled index", ""); return false; } + i->u.ldst.is_index = true; + i->u.ldst.scale = (unsigned) n; + if (!lil_parse_variable(src, va, &(i->u.ldst.index))) return false; + if (!lil_parse_plusminus(src, &sign)) return false; + if (!lil_parse_number(src, va, &n)) return false; + } else { + i->u.ldst.is_index = false; + i->u.ldst.scale = 0; + } + i->u.ldst.offset = sign * (POINTER_SIZE_SINT) n; + if (!lil_parse_kw(src, ":")) return false; + if (!lil_parse_type(src, &i->u.ldst.t, false)) return false; + if (lil_parse_kw_no_error(src, ",")) { + if (lil_parse_kw_no_error(src, "acquire")) { + i->u.ldst.acqrel = LAR_Acquire; + } else if (lil_parse_kw_no_error(src, "release")) { + i->u.ldst.acqrel = LAR_Release; + } else { + error(src, "expecting acquire or release", ""); + return false; + } + } else { + i->u.ldst.acqrel = LAR_None; + } + if (!lil_parse_kw(src, "]")) return false; + return true; +} + +static bool lil_parse_load_extend(const char** src, LilInstruction* i) +{ + if (lil_parse_kw_no_error(src, ",")) { + if (lil_parse_kw_no_error(src, "sx")) { + i->u.ldst.extend = LLX_Sign; + } else if (lil_parse_kw_no_error(src, "zx")) { + i->u.ldst.extend = LLX_Zero; + } else { + error(src, "expecting sx or zx", ""); + return false; + } + } else { + i->u.ldst.extend = LLX_None; + } + return true; +} + +// Parse an instruction +static LilInstruction* lil_parse_instruction(const char** src, tl::MemoryPool* mem, unsigned* cgl, LilVarArgs* va, LilSig* entry_sig) +{ + LilInstruction* i = (LilInstruction*)mem->alloc(sizeof(LilInstruction)); + lil_skip_ws(src); + // Look ahead at characters until the instruction form is determined + // Then parse that form + // Not the most maintainable code, but it keeps the design simple for now + switch ((*src)[0]) { + case ':': // :label + ++*src; + i->tag = LIT_Label; + i->u.label.l = lil_parse_label(src, cgl, mem); + if (!i->u.label.l) return NULL; + i->u.label.ctxt = NULL; + break; + case 'a': // alloc + { + i->tag = LIT_Alloc; + if (!lil_parse_kw(src, "alloc")) return NULL; + if (!lil_parse_variable(src, va, &(i->u.alloc.dst))) return NULL; + if (!lil_parse_kw(src, ",")) return NULL; + POINTER_SIZE_INT n; + if (!lil_parse_number(src, va, &n)) return NULL; + i->u.alloc.num_bytes = (unsigned) n; + break; + } + case 'c': // call, call.noret, cas + if ((*src)[1] && (*src)[2]=='s') { + i->tag = LIT_Cas; + if (!lil_parse_kw(src, "cas")) return NULL; + if (!lil_parse_address(src, va, i)) return NULL; + if (!lil_parse_kw(src, "=")) return NULL; + if (!lil_parse_operand(src, va, &i->u.ldst.compare)) return NULL; + if (!lil_parse_kw(src, ",")) return NULL; + if (!lil_parse_operand(src, va, &i->u.ldst.operand)) return NULL; + if (!lil_parse_kw(src, ",")) return NULL; + i->u.ldst.l = lil_parse_label(src, cgl, mem); + if (!i->u.ldst.l) return NULL; + break; + } else if ((*src)[1] && (*src)[2] && (*src)[3] && (*src)[4]=='.') { + i->tag = LIT_Call; + if (!lil_parse_kw(src, "call.noret")) return NULL; + i->u.call.k = LCK_CallNoRet; + } else { + i->tag = LIT_Call; + if (!lil_parse_kw(src, "call")) return NULL; + i->u.call.k = LCK_Call; + } + if (!lil_parse_operand(src, va, &(i->u.call.target))) return NULL; + break; + case 'h': // handles + i->tag = LIT_Handles; + if (!lil_parse_kw(src, "handles")) return NULL; + if (!lil_parse_kw(src, "=")) return NULL; + if (!lil_parse_operand(src, va, &(i->u.handles))) return NULL; + break; + case 'i': // =, in2out, inc + if (num((*src)[1]) || (*src)[1]=='%') goto asgn; + // in2out, inc + if ((*src)[1]=='n' && (*src)[2]=='c') { + // inc + i->tag = LIT_Inc; + if (!lil_parse_kw(src, "inc")) return NULL; + if (!lil_parse_address(src, va, i)) return NULL; + } else { + // in2out + if (entry_sig->arbitrary) { error(src, "in2out in an arbitrary code stub", ""); return NULL; } + i->tag = LIT_In2Out; + if (!lil_parse_kw(src, "in2out")) return NULL; + if (!lil_parse_cc(src, &(i->u.out.cc))) return NULL; + i->u.out.arbitrary = false; + i->u.out.num_arg_types = entry_sig->num_arg_types; + i->u.out.arg_types = entry_sig->arg_types; + if (!lil_parse_kw(src, ":")) return NULL; + if (!lil_parse_type(src, &(i->u.out.ret_type), true)) return NULL; + } + break; + case 'j': // j, jc + if ((*src)[1]=='c') { // jc + i->tag = LIT_Jc; + if (!lil_parse_kw(src, "jc")) return NULL; + if (!lil_parse_cond(src, va, &(i->u.jc.c))) return NULL; + if (!lil_parse_kw(src, ",")) return NULL; + i->u.jc.l = lil_parse_label(src, cgl, mem); + if (!i->u.jc.l) return NULL; + } else { // j + i->tag = LIT_J; + if (!lil_parse_kw(src, "j")) return NULL; + i->u.j = lil_parse_label(src, cgl, mem); + if (!i->u.j) return NULL; + } + break; + case 'l': // =, locals, ld + if (num((*src)[1]) || (*src)[1]=='%') { + goto asgn; + } else if ((*src)[1]=='d') { + // ld + i->tag = LIT_Ld; + if (!lil_parse_kw(src, "ld")) return NULL; + if (!lil_parse_variable(src, va, &(i->u.ldst.operand.val.var))) return NULL; + if (!lil_parse_kw(src, ",")) return NULL; + if (!lil_parse_address(src, va, i)) return NULL; + if (!lil_parse_load_extend(src, i)) return NULL; + } else { + // locals + POINTER_SIZE_INT n; + i->tag = LIT_Locals; + if (!lil_parse_kw(src, "locals")) return NULL; + if (!lil_parse_number(src, va, &n)) return NULL; + i->u.locals = (unsigned) n; + } + break; + case 'm': // m2n_save_all + if (!lil_parse_kw(src, "m2n_save_all")) return NULL; + i->tag = LIT_M2NSaveAll; + break; + case 'o': // =, out + if (num((*src)[1]) || (*src)[1]=='%') goto asgn; + // out + i->tag = LIT_Out; + if (!lil_parse_kw(src, "out")) return NULL; + if (!lil_parse_sig(src, mem, va, &(i->u.out))) return NULL; + break; + case 'p': // push_m2n, pop_m2n, print + if ((*src)[1]=='u') { + // push_m2n + i->tag = LIT_PushM2N; + if (!lil_parse_kw(src, "push_m2n")) return NULL; + i->u.push_m2n.method = NULL; + i->u.push_m2n.current_frame_type = FRAME_UNKNOWN; + POINTER_SIZE_INT n; + POINTER_SIZE_INT ft; + if (!lil_parse_number(src, va, &n)) + return NULL; + else + i->u.push_m2n.method = (Method_Handle) n; + if (!lil_parse_kw(src, ",")) return NULL; + lil_skip_ws(src); + if (!lil_parse_number(src, va, &ft)) + return NULL; + else + i->u.push_m2n.current_frame_type = (frame_type)ft; + lil_skip_ws(src); + if ((*src)[0]!=';') { + if (!lil_parse_kw(src, ",")) return NULL; + if (!lil_parse_kw(src, "handles")) return NULL; + i->u.push_m2n.handles = true; + } else { + i->u.push_m2n.handles = false; + } + } + else if ((*src)[1]=='o') { + // pop_m2n + i->tag = LIT_PopM2N; + if (!lil_parse_kw(src, "pop_m2n")) return NULL; + } + else { + // print + i->tag = LIT_Print; + if (!lil_parse_kw(src, "print")) + return NULL; + lil_skip_ws(src); + if ((*src)[0] == '\'') { + // immediate string + if (!lil_parse_string(src, &i->u.print.str)) + return NULL; + } + else { + // look for string address + POINTER_SIZE_INT n; + if (!lil_parse_number(src, va, &n)) + return NULL; + else + i->u.print.str = (char *) n; + } + lil_skip_ws(src); + if ((*src)[0] == ';') { + // create a dummy immediate 0 operand + i->u.print.arg.is_immed = true; + i->u.print.arg.val.imm = 0; + i->u.print.arg.has_cast = false; + i->u.print.arg.t = LT_Ref; + } + else { + if (!lil_parse_kw(src, ",") || + !lil_parse_operand(src, va, &i->u.print.arg)) + return NULL; + } + } + break; + case 'r': // =, ret + if ((*src)[1]!='e') goto asgn; + // ret + i->tag = LIT_Ret; + if (!lil_parse_kw(src, "ret")) return NULL; + break; + case 's': // =, std_places, st + if ((*src)[1]=='p') goto asgn; + if ((*src)[1] && (*src)[2]=='d') { + // std_places + i->tag = LIT_StdPlaces; + if (!lil_parse_kw(src, "std_places")) return NULL; + POINTER_SIZE_INT n; + if (!lil_parse_number(src, va, &n)) + return NULL; + else + i->u.std_places = (unsigned) n; + } else { + // st + i->tag = LIT_St; + if (!lil_parse_kw(src, "st")) return NULL; + if (!lil_parse_address(src, va, i)) return NULL; + if (!lil_parse_kw(src, ",")) return NULL; + 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; + break; + default: + error(src, "expecting instruction", ""); + return NULL; + asgn: // =, v=ts + i->tag = LIT_Asgn; + if (!lil_parse_variable(src, va, &(i->u.asgn.dst))) return NULL; + if (!lil_parse_kw(src, "=")) return NULL; + lil_skip_ws(src); + // At this point there is either ts, a unary op, or an operand + if ((*src)[0]=='t') { + i->tag = LIT_Ts; + i->u.ts = i->u.asgn.dst; + if (!lil_parse_kw(src, "ts")) return NULL; + } else if ((*src)[0]=='n' || ((*src)[0]=='s' && (*src)[1]=='x') || (*src)[0]=='z') { + // Unary operation + // This code relies on parse_kw not changing src on failure since white space is skipped + if (lil_parse_kw_no_error(src, "not")) i->u.asgn.op = LO_Not; + else if (lil_parse_kw_no_error(src, "neg")) i->u.asgn.op = LO_Neg; + else if (lil_parse_kw_no_error(src, "sx1")) i->u.asgn.op = LO_Sx1; + else if (lil_parse_kw_no_error(src, "sx2")) i->u.asgn.op = LO_Sx2; + else if (lil_parse_kw_no_error(src, "sx4")) i->u.asgn.op = LO_Sx4; + else if (lil_parse_kw_no_error(src, "zx1")) i->u.asgn.op = LO_Zx1; + else if (lil_parse_kw_no_error(src, "zx2")) i->u.asgn.op = LO_Zx2; + else if (lil_parse_kw_no_error(src, "zx4")) i->u.asgn.op = LO_Zx4; + else { error(src, "expecting unary operation", ""); return NULL; } + if (!lil_parse_operand(src, va, &(i->u.asgn.o1))) return NULL; + } else { + // Operand + if (!lil_parse_operand(src, va, &(i->u.asgn.o1))) return NULL; + // Now the statement can either end or have a binary op followed by an operand + lil_skip_ws(src); + if ((*src)[0]!=';') { + if ((*src)[0]=='+') i->u.asgn.op = LO_Add, ++*src; + else if ((*src)[0]=='-') i->u.asgn.op = LO_Sub, ++*src; + else if ((*src)[0]=='*') i->u.asgn.op = LO_SgMul, ++*src; + else if ((*src)[0]=='<' && (*src)[1]=='<') i->u.asgn.op = LO_Shl, *src += 2; + else if ((*src)[0]=='&') i->u.asgn.op = LO_And, ++*src; + else { error(src, "expecting binary operation", ""); return NULL; } + if (!lil_parse_operand(src, va, &(i->u.asgn.o2))) return NULL; + } else { + i->u.asgn.op = LO_Mov; + } + } + break; + } + if (!lil_parse_kw(src,";")) return NULL; + return i; +} + +LilCodeStub* lil_parse_code_stub(const char* src, ...) +{ + assert(src); + const char** cur = &src; + LilVarArgs va; + va.current = 0; + va_start(va.val, src); + + tl::MemoryPool* mem = new tl::MemoryPool; + LilCodeStub* cs = (LilCodeStub*)mem->alloc(sizeof(LilCodeStub)); + cs->my_memory = mem; + cs->cur_gen_label = 0; + cs->is = NULL; + cs->ctxt_state = LCSC_NotComputed; + cs->num_is = 0; + cs->init_ctxt = NULL; + cs->compiled_code_size = 0; + + LilInstruction *i, **tail=&(cs->is); + // originally no BB information is present; lil_cs_init_fg() will create it + cs->bb_list_head = NULL; + cs->num_bbs = 0; + + if (!lil_parse_kw(cur, "entry")) goto clean_up; + POINTER_SIZE_INT n; + if (!lil_parse_number(cur, &va, &n)) { + goto clean_up; + } + else { + cs->num_std_places = (unsigned) n; + } + if (!lil_parse_kw(cur, ":")) goto clean_up; + if (!lil_parse_sig(cur, mem, &va, &(cs->sig))) goto clean_up; + if (!lil_parse_kw(cur, ";")) goto clean_up; + + lil_skip_ws(cur); + while ((*cur)[0]) { + i = lil_parse_instruction(cur, mem, &cs->cur_gen_label, &va, &cs->sig); + if (!i) goto clean_up; + *tail = i; + i->next = NULL; + tail = &(i->next); + lil_skip_ws(cur); + cs->num_is++; + }; + + va_end(va.val); + return cs; + + clean_up: + va_end(va.val); + delete mem; + return NULL; +} + +LilCodeStub* lil_parse_onto_end(LilCodeStub* cs, const char* src, ...) +{ + if (!cs) return NULL; + assert(src); + const char** cur = &src; + LilVarArgs va; + va.current = 0; + va_start(va.val, src); + + tl::MemoryPool* mem = cs->my_memory; + LilInstruction *i=cs->is, **tail=&(cs->is); + while (i) { tail = &i->next; i=i->next; } + + lil_skip_ws(cur); + while ((*cur)[0]) { + i = lil_parse_instruction(cur, mem, &cs->cur_gen_label, &va, &(cs->sig)); + if (!i) goto clean_up; + *tail = i; + i->next = NULL; + tail = &(i->next); + lil_skip_ws(cur); + cs->num_is++; + }; + + va_end(va.val); + return cs; + + clean_up: + va_end(va.val); + delete mem; + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////////// +// Contexts + +enum LilContextState { LCS_Unchanged, LCS_Changed, LCS_Error, LCS_Terminal }; + +struct LilInstructionContext { + LilContextState s; + unsigned num_std_places; + LilType* std_place_types; + LilM2nState m2n; + unsigned num_locals; + LilType* local_types; + LilSig* out_sig; + LilType ret; + unsigned amt_alloced; +}; + +static void lil_new_context2(LilCodeStub* cs, LilInstructionContext* ic) +{ + ic->std_place_types = (LilType*)cs->my_memory->alloc(cs->max_std_places*sizeof(LilType)); + ic->local_types = (LilType*)cs->my_memory->alloc(cs->max_locals *sizeof(LilType)); +} + +static LilInstructionContext* lil_new_context(LilCodeStub* cs) +{ + LilInstructionContext* ic = (LilInstructionContext*)cs->my_memory->alloc(sizeof(LilInstructionContext)); + lil_new_context2(cs, ic); + return ic; +} + +// Copy c1 to c2 +static void lil_copy_context(LilCodeStub* cs, LilInstructionContext* c1, LilInstructionContext* c2) +{ + *c2 = *c1; + for(unsigned i=0; imax_std_places; i++) c2->std_place_types[i] = c1->std_place_types[i]; + for(unsigned j=0; jmax_locals; j++) c2->local_types [j] = c1->local_types [j]; +} + +unsigned lil_ic_get_num_std_places(LilInstructionContext* ic) +{ + return ic->num_std_places; +} + +LilM2nState lil_ic_get_m2n_state(LilInstructionContext* ic) +{ + return ic->m2n; +} + +unsigned lil_ic_get_num_locals(LilInstructionContext* ic) +{ + return ic->num_locals; +} + +LilType lil_ic_get_local_type(LilInstructionContext* ic, unsigned idx) +{ + assert(idxnum_locals); + return ic->local_types[idx]; +} + +LilSig* lil_ic_get_out_sig(LilInstructionContext* ic) +{ + return ic->out_sig; +} + +LilType lil_ic_get_ret_type(LilInstructionContext* ic) +{ + return ic->ret; +} + +unsigned lil_ic_get_amt_alloced(LilInstructionContext* ic) +{ + return ic->amt_alloced; +} + +LilType lil_ic_get_type(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v, bool silent) +{ + switch (v->tag) { + case LVK_In: + if (silent && v->index>=cs->sig.num_arg_types) return LT_Void; + assert(v->indexsig.num_arg_types); + return cs->sig.arg_types[v->index]; + case LVK_StdPlace: + if (silent && v->index>=c->num_std_places) return LT_Void; + assert(v->indexnum_std_places); + return c->std_place_types[v->index]; + case LVK_Local: + if (silent && v->index>=c->num_locals) return LT_Void; + assert(v->indexnum_locals); + return c->local_types[v->index]; + //break; //remark #111: statement is unreachable + case LVK_Out: + if (silent && v->index>=c->out_sig->num_arg_types) return LT_Void; + assert(c->out_sig && v->indexout_sig->num_arg_types); + return c->out_sig->arg_types[v->index]; + case LVK_Ret: + return c->ret; + default: ASSERT(0, "Unknown variable kind"); for(;;); + } +} + +LilType lil_ic_get_type_aux(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o, bool silent) +{ + if (o->has_cast) + return o->t; + else + if (o->is_immed) + return LT_PInt; + else + return lil_ic_get_type(cs, c, &o->val.var, silent); +} + +LilType lil_ic_get_type(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o) +{ + return lil_ic_get_type_aux(cs, c, o, false); +} + +static void lil_ic_set_type(LilInstructionContext* c, LilVariable* v, LilType t) +{ + switch (v->tag) { + case LVK_In: + // Ignore + break; + case LVK_StdPlace: + if (v->indexnum_std_places) + c->std_place_types[v->index] = t; + break; + case LVK_Local: + if (v->indexnum_locals) + c->local_types[v->index] = t; + break; + case LVK_Out: + // Ignore + break; + case LVK_Ret: + c->ret = t; + break; + default: ASSERT(0, "Unknown variable kind"); + } +} + +static LilType lil_type_asgn(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i); + +LilType lil_instruction_get_dest_type(LilCodeStub *cs, + LilInstruction *i, + LilInstructionContext *ctxt) { + switch (i->tag) { + case LIT_Alloc: + return LT_PInt; + case LIT_Asgn: + return lil_type_asgn(cs, ctxt, i); + case LIT_Ts: + return LT_PInt; + case LIT_Ld: + return i->u.ldst.t; + default: + return LT_Void; + } +} + +static LilType lil_type_unary_op(LilOperation op, LilType t) +{ + switch (op) { + case LO_Mov: + case LO_Neg: + case LO_Not: + return t; + case LO_Sx1: + case LO_Sx2: + case LO_Sx4: + case LO_Zx1: + case LO_Zx2: + case LO_Zx4: + return t; + default: + ASSERT(0, "Unexpected operation"); + return LT_Void; + } +} + +static LilType lil_type_binary_op(LilOperation UNREF op, LilType t1, LilType UNREF t2) +{ + return t1; +} + +static LilType lil_type_asgn(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i) +{ + if (lil_operation_is_binary(i->u.asgn.op)) + return lil_type_binary_op(i->u.asgn.op, lil_ic_get_type_aux(cs, c, &i->u.asgn.o1, true), lil_ic_get_type_aux(cs, c, &i->u.asgn.o2, true)); + // ? 20040205: This is a hack to get the object allocation fastpath to type check + else if (i->u.asgn.op == LO_Sx4 && !i->u.asgn.o1.is_immed && i->u.asgn.o1.val.var.tag == LVK_In && i->u.asgn.o1.val.var.index == 0) + return LT_PInt; + else + return lil_type_unary_op(i->u.asgn.op, lil_ic_get_type_aux(cs, c, &i->u.asgn.o1, true)); +} + +// Compute the context at the beginning of an instruction given the context that falls through to it +// This function mutates c in place +static void lil_pre_context(LilInstructionContext* c, LilCodeStub* cs, LilInstruction* i) +{ + if (i->tag==LIT_Label && i->u.label.ctxt) lil_copy_context(cs, i->u.label.ctxt, c); +} + +// Compute the context following the given instruction in the given code stub given the context before the instruction +// This function mutates c in place +// Note: +// 1) If the instruction is terminal the context state is set to LCS_Terminal + +static void lil_next_context(LilInstructionContext* c, LilCodeStub* cs, LilInstruction* i) +{ + unsigned j; + switch (i->tag) { + case LIT_Label: + break; + case LIT_Locals: + c->num_locals = i->u.locals; + for(j=0; jnum_locals; j++) c->local_types[j] = LT_Void; + break; + case LIT_StdPlaces: + c->num_std_places = i->u.std_places; + for(j=0; jnum_std_places; j++) c->std_place_types[j] = LT_Void; + break; + case LIT_Alloc: + c->amt_alloced += i->u.alloc.num_bytes; + lil_ic_set_type(c, &i->u.alloc.dst, LT_PInt); + break; + case LIT_Asgn:{ + LilType t = lil_type_asgn(cs, c, i); + lil_ic_set_type(c, &i->u.asgn.dst, t); + break;} + case LIT_Ts: + lil_ic_set_type(c, &i->u.ts, LT_PInt); + break; + case LIT_Handles: + break; + case LIT_Ld: + lil_ic_set_type(c, &i->u.ldst.operand.val.var, (i->u.ldst.extend==LLX_None ? i->u.ldst.t : LT_PInt)); + break; + case LIT_St: + break; + case LIT_Inc: + break; + case LIT_Cas: + break; + case LIT_J: + c->s = LCS_Terminal; + break; + case LIT_Jc: + break; + case LIT_Out: + case LIT_In2Out: + c->out_sig = &i->u.out; + break; + case LIT_Call: + if (c->out_sig) c->ret = c->out_sig->ret_type; + c->num_std_places = 0; + c->out_sig = NULL; + if (i->u.call.k!=LCK_Call) c->s = LCS_Terminal; + break; + case LIT_Ret: + c->amt_alloced = 0; + c->num_locals = 0; + c->num_std_places = 0; + c->out_sig = NULL; + c->s = LCS_Terminal; + break; + case LIT_PushM2N: + c->m2n = (i->u.push_m2n.handles ? LMS_Handles : LMS_NoHandles); + c->out_sig = NULL; + c->ret = LT_Void; + break; + case LIT_M2NSaveAll: + break; + case LIT_PopM2N: + c->m2n = LMS_NoM2n; + c->amt_alloced = 0; + c->num_std_places = 0; + c->out_sig = NULL; + break; + case LIT_Print: + // nothing to do here + break; + default: ASSERT(0, "Unknown instruction tag"); + } +} + +// Are two signatures equal? + +static bool lil_sig_equal(LilSig* s1, LilSig* s2) +{ + if (s1->cc!=s2->cc || s1->num_arg_types!=s2->num_arg_types || s1->ret_type!=s2->ret_type) return false; + for(unsigned i=0; inum_arg_types; i++) + if (s1->arg_types[i] != s2->arg_types[i]) return false; + return true; +} + +// Merge the contexts of two control flow edges +// This function mutates c1 in place to the merged context, setting the state to changed if it changed and error if the merge is invalid + +static void lil_merge_contexts(LilInstructionContext* c1, LilInstructionContext* c2) +{ + if (c1->s==LCS_Error) return; + if (c1->num_std_places > c2->num_std_places) { c1->s = LCS_Changed; c1->num_std_places = c2->num_std_places; } + for(unsigned j=0; jnum_std_places; j++) + if (c1->std_place_types[j]!=c2->std_place_types[j]) { c1->s = LCS_Changed; c1->num_std_places = j; break; } + if (c1->m2n != c2->m2n) { + fprintf(stderr, "Context mismatch: different M2N states\n"); + fflush(stderr); + c1->s = LCS_Error; + return; + } + if (c1->num_locals != c2->num_locals) { + fprintf(stderr, "Context mismatch: different number of locals\n"); + fflush(stderr); + c1->s = LCS_Error; + return; + } + for(unsigned k=0; knum_locals; k++) + if (c1->local_types[k]!=c2->local_types[k]) { + fprintf(stderr, "Context mismatch: different types at local %d\n", k); + fflush(stderr); + c1->s = LCS_Error; + return; + } + if (c1->out_sig) { + if (!c2->out_sig || !lil_sig_equal(c1->out_sig, c2->out_sig)) { + fprintf(stderr, "Context mismatch: different output signatures\n"); + fflush(stderr); + c1->s = LCS_Error; + return; + } + } else { + if (c2->out_sig) { + fprintf(stderr, "Context mismatch: different output signatures\n"); + fflush(stderr); + c1->s = LCS_Error; + return; } + } + if (c1->ret!=LT_Void && c1->ret!=c2->ret) { c1->s = LCS_Changed; c1->ret = LT_Void; } + if (c1->amt_alloced != c2->amt_alloced) { + fprintf(stderr, "Context mismatch: different allocated memory amounts\n"); + fflush(stderr); + c1->s = LCS_Error; + return; + } +} + +// Merge a context from a control transfer instruction into one of the label it transfers to +// returns false if the label is not found + +static bool lil_merge_context_with_label(LilCodeStub* cs, LilLabel l, LilInstructionContext* c) +{ + LilInstruction* i = lil_find_label(cs, l); + if (i == NULL) + return false; + + if (i->u.label.ctxt) { + lil_merge_contexts(i->u.label.ctxt, c); + } else { + i->u.label.ctxt = lil_new_context(cs); + lil_copy_context(cs, c, i->u.label.ctxt); + i->u.label.ctxt->s = LCS_Changed; + } + return true; +} + +// Merge a fall through context into a label instruction +// Note that if c->s is LCS_Terminal then there was no fall through as the previous instruction was terminal + +static void lil_merge_context_with_label_instruction(LilCodeStub* cs, LilInstruction* i, LilInstructionContext* c) +{ + if (c->s==LCS_Terminal) { + assert(i->u.label.ctxt); + lil_copy_context(cs, i->u.label.ctxt, c); + return; + } + if (i->u.label.ctxt) { + lil_merge_contexts(i->u.label.ctxt, c); + lil_copy_context(cs, i->u.label.ctxt, c); + } else { + i->u.label.ctxt = lil_new_context(cs); + lil_copy_context(cs, c, i->u.label.ctxt); + i->u.label.ctxt->s = LCS_Changed; + } +} + +// Compute the context of each label in a code stub so that contexts can be tracked as instructions are iterated over + +void lil_compute_contexts(LilCodeStub* cs) +{ + // If already determined then return + if (!cs || cs->ctxt_state!=LCSC_NotComputed) return; + + // Count stuff + cs->max_std_places = cs->num_std_places; + cs->max_locals = 0; + for(LilInstruction* i = cs->is; i; i=i->next) { + if (i->tag==LIT_StdPlaces && i->u.std_places>cs->max_std_places) cs->max_std_places = i->u.std_places; + if (i->tag==LIT_Locals && i->u.locals>cs->max_locals) cs->max_locals = i->u.locals; + } + + // Compute the initial context + cs->init_ctxt = (LilInstructionContext*)cs->my_memory->alloc(sizeof(LilInstructionContext)); + lil_new_context2(cs, cs->init_ctxt); + cs->init_ctxt->s = LCS_Unchanged; + cs->init_ctxt->num_locals = 0; + cs->init_ctxt->out_sig = NULL; + cs->init_ctxt->num_std_places = cs->num_std_places; + for(unsigned k=0; knum_std_places; k++) cs->init_ctxt->std_place_types[k] = LT_PInt; + cs->init_ctxt->ret = LT_Void; + cs->init_ctxt->m2n = LMS_NoM2n; + cs->init_ctxt->amt_alloced = 0; + + // Fairly standard dataflow analysis + // while (something needs recomputing) + // iterate through instructions from beginning to end + // if label then: merge current context with label's stored context + // if j or jc or cas then: merge current context with target + // in all cases: compute next context + LilInstructionContext cur_ctxt; + lil_new_context2(cs, &cur_ctxt); + bool changed = true; + while (changed) { + changed = false; + lil_copy_context(cs, cs->init_ctxt, &cur_ctxt); + LilInstructionIterator iter(cs, false); + unsigned inum = 0; + while (!iter.at_end()) { + LilInstruction* i = iter.get_current(); + if (i->tag == LIT_Label) { + lil_merge_context_with_label_instruction(cs, i, &cur_ctxt); + if (!i->u.label.ctxt || i->u.label.ctxt->s==LCS_Error) { + fprintf(stderr, "Error merging contexts at label: %s\n", i->u.label.l); + fflush(stderr); + cs->ctxt_state = LCSC_Error; + return; + } + if (i->u.label.ctxt->s==LCS_Changed) { i->u.label.ctxt->s = LCS_Unchanged; changed = true; } + } + lil_next_context(&cur_ctxt, cs, i); + if (cur_ctxt.s==LCS_Error) { + fprintf(stderr, "Error: computed next context for instruction %d:\n", inum); + fflush(stderr); + lil_print_instruction(stderr, i); + cs->ctxt_state = LCSC_Error; + return; + } + if (i->tag == LIT_J) { + if (!lil_merge_context_with_label(cs, i->u.j, &cur_ctxt)) { + fprintf(stderr, "Error: label %s does not exist\n", i->u.j); + fflush(stderr); + cs->ctxt_state = LCSC_Error; + return; + } + } else if (i->tag == LIT_Jc) { + if (!lil_merge_context_with_label(cs, i->u.jc.l, &cur_ctxt)) { + fprintf(stderr, "Error: label %s does not exist\n", i->u.jc.l); + fflush(stderr); + cs->ctxt_state = LCSC_Error; + return; + } + } else if (i->tag == LIT_Cas) { + if (!lil_merge_context_with_label(cs, i->u.ldst.l, &cur_ctxt)) { + fprintf(stderr, "Error: label %s does not exist\n", i->u.ldst.l); + fflush(stderr); + cs->ctxt_state = LCSC_Error; + return; + } + } + iter.goto_next(); + ++inum; + } + } + cs->ctxt_state = LCSC_Computed; +} + +//////////////////////////////////////////////////////////////////////////////////// +// Iteragators (other than validity) + +VMEXPORT LilSig* lil_cs_get_sig(LilCodeStub* cs) { return &cs->sig; } + +VMEXPORT unsigned lil_cs_get_num_instructions(LilCodeStub* cs) +{ + return cs->num_is; +} + +VMEXPORT unsigned lil_cs_get_num_BBs(LilCodeStub *cs) { + if (cs->bb_list_head == NULL) + LilBb::init_fg(cs); + assert(cs->bb_list_head != NULL); + return cs->num_bbs; +} + +VMEXPORT LilBb* lil_cs_get_entry_BB(LilCodeStub* cs) { + if (cs->bb_list_head == NULL) + LilBb::init_fg(cs); + assert(cs->bb_list_head != NULL); + return cs->bb_list_head; +} + +VMEXPORT unsigned lil_cs_get_max_std_places(LilCodeStub * cs) { + return cs->max_std_places; +} + +VMEXPORT unsigned lil_cs_get_max_locals(LilCodeStub * cs) { + return cs->max_locals; +} + +VMEXPORT void lil_cs_set_code_size(LilCodeStub * cs, size_t size) { + cs->compiled_code_size = size; +} + +VMEXPORT size_t lil_cs_get_code_size(LilCodeStub * cs) { + return cs->compiled_code_size; +} + +static LilInstruction* lil_find_label(LilCodeStub* cs, LilLabel l) +{ + for(LilInstruction* i=cs->is; i; i=i->next) + if (i->tag==LIT_Label && strcmp(i->u.label.l, l)==0) + return i; + return NULL; +} + +VMEXPORT LilInstructionIterator::LilInstructionIterator(LilCodeStub* _cs, bool _track_ctxt) + : cs(_cs), bb(NULL), cur(_cs->is), track_ctxt(_track_ctxt) +{ + if (track_ctxt) { + lil_compute_contexts(cs); + ctxt = lil_new_context(cs); + lil_copy_context(cs, cs->init_ctxt, ctxt); + if (cs->is) lil_pre_context(ctxt, cs, cs->is); + } +} + + +VMEXPORT LilInstructionIterator::LilInstructionIterator(LilCodeStub* _cs, LilBb *_bb, bool _track_ctxt) + : cs(_cs), bb(_bb), track_ctxt(_track_ctxt) +{ + assert(bb != NULL); + cur = bb->get_first(); + + if (track_ctxt) { + lil_compute_contexts(cs); + ctxt = lil_new_context(cs); + // current context is the initial context of the BB! + lil_copy_context(cs, _bb->get_context(), ctxt); + if (cur) + lil_pre_context(ctxt, cs, cur); + } +} + + +VMEXPORT bool LilInstructionIterator::at_end() +{ + return cur==NULL; +} + +VMEXPORT LilInstruction* LilInstructionIterator::get_current() +{ + return cur; +} + +VMEXPORT LilInstructionContext* LilInstructionIterator::get_context() +{ + assert(track_ctxt); + return ctxt; +} + +VMEXPORT void LilInstructionIterator::goto_next() +{ + if (track_ctxt && cur) lil_next_context(ctxt, cs, cur); + if (cur) { + // if this is a BB iterator, gotta check for BB end + if (bb != NULL && cur == bb->get_last()) + cur = NULL; + else + cur = cur->next; + } + if (track_ctxt && cur) lil_pre_context(ctxt, cs, cur); +} + + +VMEXPORT LilInstructionVisitor::LilInstructionVisitor() +{ +} + + +void lil_visit_instruction(LilInstruction* i, LilInstructionVisitor* v) +{ + switch (i->tag) { + case LIT_Label: + v->label(i->u.label.l); + break; + case LIT_Locals: + v->locals(i->u.locals); + break; + case LIT_StdPlaces: + v->std_places(i->u.std_places); + break; + case LIT_Alloc: + v->alloc(&i->u.alloc.dst, i->u.alloc.num_bytes); + break; + case LIT_Asgn: + v->asgn(&i->u.asgn.dst, i->u.asgn.op, &i->u.asgn.o1, &i->u.asgn.o2); + break; + case LIT_Ts: + v->ts(&i->u.ts); + break; + case LIT_Handles: + v->handles(&i->u.handles); + break; + case LIT_Ld: + v->ld(i->u.ldst.t, &i->u.ldst.operand.val.var, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), + i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, i->u.ldst.extend); + break; + case LIT_St: + v->st(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), + i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, + &i->u.ldst.operand); + break; + case LIT_Inc: + v->inc(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), + i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel); + break; + case LIT_Cas: + v->cas(i->u.ldst.t, (i->u.ldst.is_base ? &i->u.ldst.base : NULL), + i->u.ldst.scale, (i->u.ldst.is_index ? &i->u.ldst.index : NULL), i->u.ldst.offset, i->u.ldst.acqrel, + &i->u.ldst.compare, &i->u.ldst.operand, i->u.ldst.l); + break; + case LIT_J: + v->j(i->u.j); + break; + case LIT_Jc: + v->jc(i->u.jc.c.tag, &i->u.jc.c.o1, &i->u.jc.c.o2, i->u.jc.l); + break; + case LIT_Out: + v->out(&i->u.out); + break; + case LIT_In2Out: + v->in2out(&i->u.out); + break; + case LIT_Call: + v->call(&i->u.call.target, i->u.call.k); + break; + case LIT_Ret: + v->ret(); + break; + case LIT_PushM2N: + v->push_m2n(i->u.push_m2n.method, i->u.push_m2n.current_frame_type, i->u.push_m2n.handles); + break; + case LIT_M2NSaveAll: + v->m2n_save_all(); + break; + case LIT_PopM2N: + v->pop_m2n(); + break; + case LIT_Print: + v->print(i->u.print.str, &i->u.print.arg); + break; + default: ASSERT(0, "Unknown instruction tag"); + } +} + +LilVariableKind lil_variable_get_kind(LilVariable* v) { return v->tag; } +unsigned lil_variable_get_index(LilVariable* v) { return v->index; } + +bool lil_variable_is_equal(LilVariable* v1, 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) { + assert(o->is_immed); + return o->val.imm; +} + +LilVariable* lil_operand_get_variable(LilOperand* o) { + assert(!o->is_immed); + return &o->val.var; +} + +LilCc lil_sig_get_cc(LilSig* sig) { return sig->cc; } +bool lil_sig_is_arbitrary(LilSig* sig) { return sig->arbitrary; } +unsigned lil_sig_get_num_args(LilSig* sig) { return sig->num_arg_types; } +LilType lil_sig_get_arg_type(LilSig* sig, unsigned num) { + assert(num < sig->num_arg_types); + return sig->arg_types[num]; +} +LilType lil_sig_get_ret_type(LilSig* sig) { return sig->ret_type; } + +bool lil_predicate_is_binary(enum LilPredicate c) +{ + switch (c) { + case LP_IsZero: + case LP_IsNonzero: + return false; + case LP_Eq: + case LP_Ne: + case LP_Le: + case LP_Lt: + case LP_Ule: + case LP_Ult: + return true; + default: + ASSERT(0, "Unknown predicate"); + return false; // not reached + } +} + +bool lil_predicate_is_signed(LilPredicate p) { + return (p == LP_Ule || p == LP_Ult); +} + +bool lil_operation_is_binary(enum LilOperation op) +{ + switch (op) { + case LO_Mov: + case LO_Neg: + case LO_Not: + case LO_Sx1: + case LO_Sx2: + case LO_Sx4: + case LO_Zx1: + case LO_Zx2: + case LO_Zx4: + return false; + case LO_Add: + case LO_Sub: + case LO_SgMul: + case LO_Shl: + case LO_And: + return true; + default: + ASSERT(0, "Unknown operation"); + return false; // not reached + } +} + +//////////////////////////////////////////////////////////////////////////////////// +// Validity + +static bool lil_is_valid_asgn(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v, LilType t) +{ + switch (v->tag) { + case LVK_In: + return v->indexsig.num_arg_types && cs->sig.arg_types[v->index]==t; + case LVK_StdPlace: + return v->indexnum_std_places; + case LVK_Local: + return v->indexnum_locals; + case LVK_Out: + return c->out_sig && v->indexout_sig->num_arg_types && c->out_sig->arg_types[v->index]==t; + case LVK_Ret: + return v->index<1; + default: + return false; + } +} + +static bool lil_is_valid_variable(LilCodeStub* cs, LilInstructionContext* c, LilVariable* v) +{ + switch (v->tag) { + case LVK_In: return v->indexsig.num_arg_types; + case LVK_StdPlace: return v->indexnum_std_places; + case LVK_Local: return v->indexnum_locals; + case LVK_Out: return c->out_sig && v->indexout_sig->num_arg_types; + case LVK_Ret: return v->index<1 && c->ret!=LT_Void; + default: return false; + } +} + +#ifdef _IPF_ +#define PLATFORM_INT LT_G8 +#else +#define PLATFORM_INT LT_G4 +#endif + +static bool lil_are_types_compatible(LilType t1, LilType t2) +{ + return t1==t2 || ((t1==LT_Ref || t1==LT_PInt || t1==PLATFORM_INT) && (t2==LT_Ref || t2==LT_PInt || t2==PLATFORM_INT)); +} + +static bool lil_is_valid_operand(LilCodeStub* cs, LilInstructionContext* c, LilOperand* o) +{ + if (!o->is_immed && !lil_is_valid_variable(cs, c, &(o->val.var))) return false; + if (o->has_cast) { + LilType raw_type = lil_ic_get_type(cs, c, o); + if (!lil_are_types_compatible(o->t, raw_type)) return false; + } + return true; +} + +static bool lil_is_valid_address(LilCodeStub* cs, LilInstructionContext* c, LilInstruction* i) +{ + if (i->u.ldst.is_base) { + if (!lil_is_valid_variable(cs, c, &(i->u.ldst.base))) return false; + LilType t = lil_ic_get_type(cs, c, &(i->u.ldst.base), true); + if (t!=LT_Ref && t!=LT_PInt && t!=PLATFORM_INT) return false; + } + if (i->u.ldst.is_index) { + if (!lil_is_valid_variable(cs, c, &(i->u.ldst.index))) return false; + LilType t = lil_ic_get_type(cs, c, &(i->u.ldst.index), true); + if (t!=LT_Ref && t!=LT_PInt && t!=PLATFORM_INT) return false; + } + return true; +} + +static bool lil_is_valid_label(LilCodeStub* cs, LilLabel l) +{ + return lil_find_label(cs, l)!=NULL; +} + +static bool lil_verify_unary_op(LilOperation UNREF op, LilType UNREF t) +{ + return true; +} + +static bool lil_verify_binary_op(LilOperation op, LilType t1, LilType t2) +{ + if (op==LO_Shl) + return t2==LT_PInt; + else + return t1==t2; +} + +static bool lil_verify_binary_cond(LilPredicate UNREF p, LilType t1, LilType t2) +{ + return t1==t2; +} + +static bool lil_print_err(char *s, LilInstruction* i, unsigned inst_number) +{ + fprintf(stderr, "lil code stub invalid at instruction %d: %s\n ", inst_number, s); + if (i) lil_print_instruction(stdout, i); + fflush(stderr); + return false; +} + +#define ERR(s) { return lil_print_err(s, i, inst_number); } + +bool lil_is_valid(LilCodeStub* cs) +{ + unsigned inst_number = 0; + LilInstruction* i = NULL; + + if (!cs) ERR("code stub is null"); + + lil_compute_contexts(cs); + if (cs->ctxt_state == LCSC_Error) ERR("control flow contexts inconsistent"); + + // Check instructions + bool last_was_terminal = false; + LilInstructionIterator iter(cs, true); + while (!iter.at_end()) { + inst_number++; + i = iter.get_current(); + LilInstructionContext* c = iter.get_context(); + + switch (i->tag) { + case LIT_Label: + { + LilLabel l = i->u.label.l; + LilInstruction *label_def = lil_find_label(cs, l); + if (label_def != i) { + char buffer[256]; + sprintf(buffer, "label %s redefined", l); + ERR(buffer); + } + break; + } + case LIT_Locals: + break; + case LIT_StdPlaces: + break; + case LIT_Alloc: + if (c->out_sig) ERR("alloc between out and call"); + if (!lil_is_valid_variable(cs, c, &(i->u.alloc.dst))) ERR("invalid variable in alloc"); + break; + case LIT_Asgn: + if (!lil_is_valid_operand(cs, c, &(i->u.asgn.o1))) ERR("invalid first operand in assignment"); + if (lil_operation_is_binary(i->u.asgn.op)) { + if (!lil_is_valid_operand(cs, c, &(i->u.asgn.o2))) ERR("invalid second operand in assignment"); + if (!lil_verify_binary_op(i->u.asgn.op, lil_ic_get_type(cs, c, &i->u.asgn.o1), lil_ic_get_type(cs, c, &i->u.asgn.o2))) + ERR("operand type mismatch in assignment"); + } else { + if (!lil_verify_unary_op(i->u.asgn.op, lil_ic_get_type(cs, c, &i->u.asgn.o1))) + ERR("operand type mismatch in assignment"); + } + // ? 20040205: This is a hack to get the object allocation fastpath to type check + if (i->u.asgn.op == LO_Sx4 && !i->u.asgn.o1.is_immed && i->u.asgn.o1.val.var.tag == LVK_In && i->u.asgn.o1.val.var.index == 0) { + if (!lil_is_valid_asgn(cs, c, &i->u.asgn.dst, LT_PInt)) + ERR("invalid destination or type incompatibility in assignment"); + } else if (!lil_is_valid_asgn(cs, c, &i->u.asgn.dst, lil_type_asgn(cs, c, i))) { + ERR("invalid destination or type incompatibility in assignment"); + } + break; + case LIT_Ts: + if (!lil_is_valid_asgn(cs, c, &i->u.ts, LT_PInt)) ERR("invalid destination in ts"); + break; + case LIT_Handles: + if (c->m2n!=LMS_Handles) ERR("handles not dominated by push_m2n handles"); + if (!lil_is_valid_operand(cs, c, &(i->u.handles))) ERR("invalid operand in handles"); + if (lil_ic_get_type(cs, c, &i->u.handles)!=LT_PInt) ERR("operand not platform int in handles"); + break; + case LIT_Ld: + if (!lil_is_valid_asgn(cs, c, &i->u.ldst.operand.val.var, (i->u.ldst.extend==LLX_None ? i->u.ldst.t : LT_PInt))) + ERR("invalid destination in load"); + if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in load"); + break; + case LIT_St: + if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in store"); + if (!i->u.ldst.operand.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in store"); + if (!lil_is_valid_operand(cs, c, &(i->u.ldst.operand))) ERR("invalid source in store"); + break; + case LIT_Inc: + if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in inc"); + break; + case LIT_Cas: + if (!lil_is_valid_address(cs, c, i)) ERR("invalid address in cas"); + if (!i->u.ldst.compare.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in cas compare"); + if (!i->u.ldst.operand.is_immed && i->u.ldst.t!=lil_ic_get_type(cs, c, &i->u.ldst.operand)) ERR("type mismatch in cas source"); + if (!lil_is_valid_operand(cs, c, &(i->u.ldst.compare))) ERR("invalid compare in cas"); + if (!lil_is_valid_operand(cs, c, &(i->u.ldst.operand))) ERR("invalid source in cas"); + if (!lil_is_valid_label(cs, i->u.ldst.l)) ERR("bad target in cas"); + break; + case LIT_J: + if (!lil_is_valid_label(cs, i->u.j)) ERR("bad target in j"); + break; + case LIT_Jc: + // Should do typechecks here + if (!lil_is_valid_label(cs, i->u.jc.l)) ERR("bad target in jc"); + if (!lil_is_valid_operand(cs, c, &(i->u.jc.c.o1))) ERR("invalid first operand in condition"); + if (lil_predicate_is_binary(i->u.jc.c.tag)) { + if (!lil_is_valid_operand(cs, c, &(i->u.jc.c.o2))) ERR("invalid second operand in condition"); + if (!lil_verify_binary_cond(i->u.jc.c.tag, lil_ic_get_type(cs, c, &(i->u.jc.c.o1)), lil_ic_get_type(cs, c, &(i->u.jc.c.o2)))) + ERR("operand type mismatch in conditional jump"); + } + break; + case LIT_Out: + break; + case LIT_In2Out: + if (cs->sig.arbitrary) ERR("in2out in arbitrary code stub"); + break; + case LIT_Call: + if (i->u.call.k!=LCK_TailCall && !c->out_sig) ERR("call not dominated by out or in2out"); + if (!lil_is_valid_operand(cs, c, &(i->u.call.target))) ERR("invalid operand in call"); + if (lil_ic_get_type(cs, c, &i->u.call.target)!=LT_PInt) ERR("operand not platform int in call"); + break; + case LIT_Ret: + if (cs->sig.arbitrary) ERR("cannot return from an arbitrary signature entry"); + if (cs->sig.ret_type!=LT_Void && cs->sig.ret_type!=c->ret) + ERR("ret with invalid return value"); + if (c->m2n) ERR("return with m2n"); + break; + case LIT_PushM2N: + if (c->amt_alloced>0) ERR("alloc before push_m2n"); + if (c->m2n!=LMS_NoM2n) ERR("push m2n twice"); + break; + case LIT_M2NSaveAll: + if (c->m2n==LMS_NoM2n) ERR("m2n save all not dominated by push m2n"); + break; + case LIT_PopM2N: + if (c->m2n==LMS_NoM2n) ERR("pop m2n not dominated by push"); + break; + case LIT_Print: + if (!lil_is_valid_operand(cs, c, &i->u.print.arg)) + ERR("invalid argument to print"); + break; + default: + ERR("unknown instruction"); + } + + iter.goto_next(); + last_was_terminal = (c->s==LCS_Terminal); + } + + if (!last_was_terminal) ERR("last instruction not terminal"); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////// +// Printing Utilities + +void lil_print_cc(FILE* out, enum LilCc cc) +{ + switch (cc) { + case LCC_Platform: fprintf(out, "platform"); break; + case LCC_Managed: fprintf(out, "managed"); break; + case LCC_Rth: fprintf(out, "rth"); break; + case LCC_Jni: fprintf(out, "jni"); break; + case LCC_StdCall: fprintf(out, "stdcall"); break; + default: ASSERT(0, "Unknown calling convention"); + } + fflush(out); +} + +void lil_print_type(FILE* out, enum LilType t) +{ + switch (t) { + case LT_G1: fprintf(out, "g1"); break; + case LT_G2: fprintf(out, "g2"); break; + case LT_G4: fprintf(out, "g4"); break; + case LT_G8: fprintf(out, "g8"); break; + case LT_F4: fprintf(out, "f4"); break; + case LT_F8: fprintf(out, "f8"); break; + case LT_Ref: fprintf(out, "ref"); break; + case LT_PInt: fprintf(out, "pint"); break; + case LT_Void: fprintf(out, "void"); break; + default: ASSERT(0, "Unknown LIL type"); + } + fflush(out); +} + +void lil_print_sig(FILE* out, LilSig* sig) +{ + assert(sig); + lil_print_cc(out, sig->cc); + fprintf(out, ":"); + if (sig->arbitrary) { + fprintf(out, "arbitrary"); + } else { + for(unsigned i=0; inum_arg_types; i++) { + if (i>0) fprintf(out, ","); + lil_print_type(out, sig->arg_types[i]); + } + fprintf(out, ":"); + lil_print_type(out, sig->ret_type); + } + fflush(out); +} + +void lil_print_variable(FILE* out, LilVariable* v) +{ + assert(v); + switch (v->tag) { + case LVK_In: + fprintf(out, "i"); + break; + case LVK_StdPlace: + fprintf(out, "sp"); + break; + case LVK_Out: + fprintf(out, "o"); + break; + case LVK_Local: + fprintf(out, "l"); + break; + case LVK_Ret: + assert(v->index==0); + fprintf(out, "r"); + return; + default: ASSERT(0, "Unknown kind"); + } + fprintf(out, "%d", v->index); + fflush(out); +} + +void lil_print_operand(FILE* out, LilOperand* o) +{ + assert(o); + if (o->is_immed) + // since imm is POINTER_SIZE_INT, %p should work in all cases + fprintf(out, "0x%p", (void *)o->val.imm); + else + lil_print_variable(out, &(o->val.var)); + if (o->has_cast) { + fprintf(out, ":"); + lil_print_type(out, o->t); + } + fflush(out); +} + +void lil_print_address(FILE* out, LilInstruction* i) +{ + bool printed_term = false; + fprintf(out, "["); + if (i->u.ldst.is_base) { + lil_print_variable(out, &(i->u.ldst.base)); + printed_term = true; + } + if (i->u.ldst.is_index) { + if (printed_term) + fprintf(out, "+"); + fprintf(out, "%d*", i->u.ldst.scale); + lil_print_variable(out, &(i->u.ldst.index)); + printed_term = true; + } + if (i->u.ldst.offset != 0) { + if (printed_term) + fprintf(out, "+"); + fprintf(out, "0x%p", (void *)i->u.ldst.offset); + printed_term = true; + } + if (!printed_term) + fprintf(out, "0x0"); + fprintf(out, ":"); + lil_print_type(out, i->u.ldst.t); + switch (i->u.ldst.acqrel) { + case LAR_None: break; + case LAR_Acquire: fprintf(out, ",acquire"); break; + case LAR_Release: fprintf(out, ",release"); break; + default: ASSERT(0, "Unexpected acqrel value"); + } + fprintf(out, "]"); + fflush(out); +} + +void lil_print_instruction(FILE* out, LilInstruction* i) +{ + assert(i); + switch (i->tag) { + case LIT_Label: + fprintf(out, ":%s", i->u.label.l); + break; + case LIT_Locals: + fprintf(out, "locals %d", i->u.locals); + break; + case LIT_StdPlaces: + fprintf(out, "std_places %d", i->u.std_places); + break; + case LIT_Alloc: + fprintf(out, "alloc "); + lil_print_variable(out, &i->u.alloc.dst); + fprintf(out, ",0x%x", i->u.alloc.num_bytes); + break; + case LIT_Asgn: + lil_print_variable(out, &(i->u.asgn.dst)); + fprintf(out, "="); + switch (i->u.asgn.op) { + case LO_Mov: + lil_print_operand(out, &(i->u.asgn.o1)); + break; + case LO_Add: + lil_print_operand(out, &(i->u.asgn.o1)); + fprintf(out, "+"); + lil_print_operand(out, &(i->u.asgn.o2)); + break; + case LO_Sub: + lil_print_operand(out, &(i->u.asgn.o1)); + fprintf(out, "-"); + lil_print_operand(out, &(i->u.asgn.o2)); + break; + case LO_SgMul: + lil_print_operand(out, &(i->u.asgn.o1)); + fprintf(out, "*"); + lil_print_operand(out, &(i->u.asgn.o2)); + break; + case LO_Shl: + lil_print_operand(out, &(i->u.asgn.o1)); + fprintf(out, "<<"); + lil_print_operand(out, &(i->u.asgn.o2)); + break; + case LO_And: + lil_print_operand(out, &(i->u.asgn.o1)); + fprintf(out, "&"); + lil_print_operand(out, &(i->u.asgn.o2)); + break; + case LO_Neg: + fprintf(out, "-"); + lil_print_operand(out, &(i->u.asgn.o1)); + break; + case LO_Not: + fprintf(out, "not "); + lil_print_operand(out, &(i->u.asgn.o1)); + break; + case LO_Sx1: + fprintf(out, "sx1 "); + lil_print_operand(out, &(i->u.asgn.o1)); + break; + case LO_Sx2: + fprintf(out, "sx2 "); + lil_print_operand(out, &(i->u.asgn.o1)); + break; + case LO_Sx4: + fprintf(out, "sx4 "); + lil_print_operand(out, &(i->u.asgn.o1)); + break; + case LO_Zx1: + fprintf(out, "zx1 "); + lil_print_operand(out, &(i->u.asgn.o1)); + break; + case LO_Zx2: + fprintf(out, "zx2 "); + lil_print_operand(out, &(i->u.asgn.o1)); + break; + case LO_Zx4: + fprintf(out, "zx4 "); + lil_print_operand(out, &(i->u.asgn.o1)); + break; + default: ASSERT(0, "Unknown operation"); + } + break; + case LIT_Ts: + lil_print_variable(out, &i->u.ts); + fprintf(out, "=ts"); + break; + case LIT_Handles: + fprintf(out, "handles="); + lil_print_operand(out, &i->u.handles); + break; + case LIT_Ld: + fprintf(out, "ld "); + lil_print_variable(out, &(i->u.ldst.operand.val.var)); + fprintf(out, ","); + lil_print_address(out, i); + if (i->u.ldst.extend==LLX_Sign) fprintf(out, ",sx"); + if (i->u.ldst.extend==LLX_Zero) fprintf(out, ",zx"); + break; + case LIT_St: + fprintf(out, "st "); + lil_print_address(out, i); + fprintf(out, ","); + lil_print_operand(out, &(i->u.ldst.operand)); + break; + case LIT_Inc: + fprintf(out, "inc "); + lil_print_address(out, i); + break; + case LIT_Cas: + fprintf(out, "cas "); + lil_print_address(out, i); + fprintf(out, "="); + lil_print_operand(out, &(i->u.ldst.compare)); + fprintf(out, ","); + lil_print_operand(out, &(i->u.ldst.operand)); + fprintf(out, ",%s", i->u.ldst.l); + break; + case LIT_J: + fprintf(out, "j %s", i->u.j); + break; + case LIT_Jc: + fprintf(out, "jc "); + switch (i->u.jc.c.tag) { + case LP_IsZero: + lil_print_operand(out, &(i->u.jc.c.o1)); + fprintf(out, "=0"); + break; + case LP_IsNonzero: + lil_print_operand(out, &(i->u.jc.c.o1)); + fprintf(out, "!=0"); + break; + case LP_Eq: + lil_print_operand(out, &(i->u.jc.c.o1)); + fprintf(out, "="); + lil_print_operand(out, &(i->u.jc.c.o2)); + break; + case LP_Ne: + lil_print_operand(out, &(i->u.jc.c.o1)); + fprintf(out, "!="); + lil_print_operand(out, &(i->u.jc.c.o2)); + break; + case LP_Le: + lil_print_operand(out, &(i->u.jc.c.o1)); + fprintf(out, "<="); + lil_print_operand(out, &(i->u.jc.c.o2)); + break; + case LP_Lt: + lil_print_operand(out, &(i->u.jc.c.o1)); + fprintf(out, "<"); + lil_print_operand(out, &(i->u.jc.c.o2)); + break; + case LP_Ule: + lil_print_operand(out, &(i->u.jc.c.o1)); + fprintf(out, " <=u "); + lil_print_operand(out, &(i->u.jc.c.o2)); + break; + case LP_Ult: + lil_print_operand(out, &(i->u.jc.c.o1)); + fprintf(out, " u.jc.c.o2)); + break; + default: ASSERT(0, "Unknown predicate"); + } + fprintf(out, ",%s", i->u.jc.l); + break; + case LIT_Out: + fprintf(out, "out "); + lil_print_sig(out, &(i->u.out)); + break; + case LIT_In2Out: + fprintf(out, "in2out "); + lil_print_cc(out, i->u.out.cc); + fprintf(out, ":"); + lil_print_type(out, i->u.out.ret_type); + break; + case LIT_Call: + switch (i->u.call.k) { + case LCK_Call: + fprintf(out, "call "); + break; + case LCK_CallNoRet: + fprintf(out, "call.noret "); + break; + case LCK_TailCall: + fprintf(out, "tailcall "); + break; + default: ASSERT(0, "Unknown call kind"); + } + lil_print_operand(out, &(i->u.call.target)); + break; + case LIT_Ret: + fprintf(out, "ret"); + break; + case LIT_PushM2N: + fprintf(out, "push_m2n %p, frame_type= %p", i->u.push_m2n.method, i->u.push_m2n.current_frame_type); + if (i->u.push_m2n.handles) fprintf(out, ",handles"); + break; + case LIT_M2NSaveAll: + fprintf(out, "m2n_save_all"); + break; + case LIT_PopM2N: + fprintf(out, "pop_m2n"); + break; + case LIT_Print: + fprintf(out, "print %p, ", i->u.print.str); + lil_print_operand(out, &i->u.print.arg); + fprintf(out, "\n"); + break; + default: + ASSERT(0, "Unknown instruction tag"); + }; + fprintf(out, ";\n"); + fflush(out); +} + +void lil_print_entry(FILE* out, LilCodeStub* cs) +{ + fprintf(out, "entry %d:", cs->num_std_places); + lil_print_sig(out, &(cs->sig)); + fprintf(out, ";\n"); + fflush(out); +} + +void lil_print_code_stub(FILE* out, LilCodeStub* cs) +{ + assert(cs); + lil_print_entry(out, cs); + for(LilInstruction* i=cs->is; i; i=i->next) + lil_print_instruction(out, i); +} + +//////////////////////////////////////////////////////////////////////////////////// +// Basic Blocks + +// private constructor; create BBs by calling init_fg() +LilBb::LilBb(LilCodeStub *_cs, LilInstruction *_start, + LilInstructionContext* ctxt_at_start): + cs(_cs), start(_start), end(NULL), + ctxt(NULL), + fallthru(NULL), + branch_target(NULL), + num_pred(0), + next(NULL), id(-1) +{ + // add this to the end of the stub's BB list + if (cs->bb_list_head == NULL) + cs->bb_list_head = this; + else { + LilBb* last_bb = cs->bb_list_head; + while (last_bb->next != NULL) + last_bb = last_bb->next; + last_bb->next = this; + } + + // store a copy of the current context in ctxt + assert(ctxt_at_start != NULL); + ctxt = lil_new_context(cs); + lil_copy_context(cs, ctxt_at_start, ctxt); +} // LilBb::LilBb + +// private operator new; create BBs using new_bb() only +void* LilBb::operator new(size_t sz, tl::MemoryPool& m) { + return m.alloc(sz); +} // LilBb::operator new + +int LilBb::get_id() { + return id; +} // LilBb::get_id + +// sets the last instruction of the BB +void LilBb::set_last(LilInstruction *i) { + // can't set the last instruction twice! + assert(end == NULL); + end = i; +} // LilBb::set_last + +// gets the first instruction +LilInstruction* LilBb::get_first() { + return start; +} // LilBb::get_first + +// gets the last instruction +LilInstruction* LilBb::get_last() { + return end; +} // LilBb::get_last + +LilInstructionContext* LilBb::get_context() { + return ctxt; +} // LilBb::get_context + +// does this bb contain instruction i? +bool LilBb::contains(LilInstruction *i) { + for (LilInstruction* j = start; j != NULL; j++) { + if (j == i) + return true; + if (j == end) + break; + } + return false; +} // LilBb::contains + +// get the label of this BB; NULL if no label exists +LilLabel LilBb::get_label() { + if (start->tag == LIT_Label) + return start->u.label.l; + return NULL; +} // LilBb::get_label + +// set a fallthrough successor to this bb +void LilBb::set_fallthru(LilBb *succ) { + fallthru = succ; + assert(succ->num_pred < MAX_BB_PRED); + succ->pred[succ->num_pred++] = this; +} // LilBb::set_fallthru + +// set a branch-target successor to this bb +void LilBb::set_branch_target(LilBb *succ) { + branch_target = succ; + assert(succ->num_pred < MAX_BB_PRED); + succ->pred[succ->num_pred++] = this; +} // LilBb::set_branch_target + +// get the fallthrough and branch target successors; +// either of them can be NULL if they don't exist +LilBb* LilBb::get_fallthru() { + return fallthru; +} // LilBb::get_fallthru + +LilBb* LilBb::get_branch_target() { + return branch_target; +} // LilBb::get_branch_target + + +// gets the i'th predecessor (NULL if i >= num_pred) +LilBb *LilBb::get_pred(unsigned i) { + return (i < num_pred) ? pred[i] : NULL; +} // LilBb::get_pred + +// gets the next BB in the list +LilBb* LilBb::get_next() { + return next; +} // LilBb::get_next + +// returns whether this BB ends in a return instruction +// (tailcall implies return!) +bool LilBb::is_ret() { + return (end != NULL && + (end->tag == LIT_Ret || + (end->tag == LIT_Call && end->u.call.k == LCK_TailCall))); +} + + +// true if this BB contains calls (which means it may throw exceptions) +bool LilBb::does_calls() { + for (LilInstruction *i=start; i != NULL; i = i->next) { + if (i->tag == LIT_Call) + return true; + if (i == end) + break; + } + return false; +} + + +// true if this BB ends with a call.noret (which means it is probably +// cold code) +bool LilBb::does_call_noret() { + return (end != NULL && end->tag == LIT_Call && + end->u.call.k == LCK_CallNoRet); +} + +// find a BB with the specified label +// NULL if no such bb exists +LilBb* LilBb::get_by_label(LilCodeStub *cs, LilLabel l) { + assert(l != NULL); + LilBb *bb = cs->bb_list_head; + + while (bb != NULL && + (bb->get_label() == NULL || strcmp(bb->get_label(), l))) + bb = bb->next; + return bb; +} // LilBb::get_by_label + +// find a BB which contains the specified instruction +// NULL if no such BB exists +LilBb* LilBb::get_by_instruction(LilCodeStub *cs, LilInstruction *i) { + LilBb* bb = cs->bb_list_head; + + while (bb != NULL && !bb->contains(i)) + bb = bb->next; + + return bb; +} // LilBb::get_by_instruction + +// print a BB to a stream +// (does not print the BB's instructions) +void LilBb::print(FILE* out) { + fprintf(out, "-- BB %d ", id); + + // print predecessors + fprintf(out, "(pred:"); + if (num_pred == 0) + fprintf(out, " none)"); + else { + for (unsigned i=0; iid); + fprintf(out, ")"); + } + + // print successors + fprintf(out, " (succ:"); + if (fallthru != NULL) + fprintf(out, " %d", fallthru->id); + if (branch_target != NULL) + fprintf(out, " %d", branch_target->id); + if (fallthru == NULL && branch_target == NULL) + fprintf(out, " none"); + fprintf(out, ")"); + + // print label + if (get_label() != NULL) + fprintf(out, " (label: %s)", get_label()); + + fprintf(out, " --\n"); + fflush(out); +} // void LilBb::print + + +// initializes the flowgraph by creating a list of BBs +// BBs in the list appear in the same order as they appear in the source code +void LilBb::init_fg(LilCodeStub *cs) { + LilInstructionIterator it(cs, true); + + LilInstruction *prev_inst = NULL; // previous instruction + LilBb* cur_bb = NULL; // current BB + + while (!it.at_end()) { + LilInstruction* inst = it.get_current(); + LilInstructionContext* ctxt = it.get_context(); + + if (inst->tag == LIT_Label || cur_bb == NULL) { + // close old bb, if it exists + if (cur_bb != NULL) { + assert(prev_inst != NULL); + cur_bb->set_last(prev_inst); + } + + // create new bb + cur_bb = new(*cs->my_memory) LilBb(cs, inst, ctxt); + cur_bb->id = cs->num_bbs++; + } + + // check if the BB should end here + if (inst->tag == LIT_J || + inst->tag == LIT_Jc || + inst->tag == LIT_Cas || + inst->tag == LIT_Ret || + (inst->tag == LIT_Call && inst->u.call.k != LCK_Call)) { + cur_bb->set_last(inst); + cur_bb = NULL; // so that the next inst will start a new one + } + + // advance pointers + prev_inst = inst; + it.goto_next(); + } + + // close the last BB + if (cur_bb != NULL) + cur_bb->set_last(prev_inst); + + // set up the successor / predecessor relations + for (cur_bb = cs->bb_list_head; cur_bb != NULL; cur_bb = cur_bb->next) { + LilInstruction *last_i = cur_bb->end; + assert(last_i != NULL); + if (last_i->tag == LIT_J) { + LilBb* succ = get_by_label(cs, last_i->u.j); + assert(succ != NULL); + cur_bb->set_branch_target(succ); + continue; // don't set fallthru + } + if (last_i->tag == LIT_Ret || + (last_i->tag == LIT_Call && last_i->u.call.k != LCK_Call)) { + continue; // terminal inst; don't set fallthru + } + if (last_i->tag == LIT_Jc) { + LilBb* succ = get_by_label(cs, last_i->u.jc.l); + assert(succ != NULL); + cur_bb->set_branch_target(succ); + } + if (last_i->tag == LIT_Cas) { + LilBb* succ = get_by_label(cs, last_i->u.ldst.l); + assert(succ); + cur_bb->set_branch_target(succ); + } + + // set fallthru + assert(cur_bb->next != NULL); + cur_bb->set_fallthru(cur_bb->next); + } +} // LilBb::init_fg + + +// EOF: lil.cpp diff --git a/vm/vmcore/src/lil/lil_code_generator.cpp b/vm/vmcore/src/lil/lil_code_generator.cpp new file mode 100644 index 0000000..9a238db --- /dev/null +++ b/vm/vmcore/src/lil/lil_code_generator.cpp @@ -0,0 +1,84 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#include "nogc.h" +#include "jvmti_direct.h" +#include "environment.h" +#include "compile.h" + +#include "jit_intf.h" +#include "lil.h" +#include "lil_code_generator.h" + +#ifdef _IA32_ +#include "lil_code_generator_ia32.h" +#elif _EM64T_ +#include "lil_code_generator_em64t.h" +#elif _IPF_ +#include "lil_code_generator_ipf.h" +#endif + + +LilCodeGenerator* LilCodeGenerator::get_platform() +{ +#ifdef _IA32_ + static LilCodeGeneratorIa32 cg; +#elif _EM64T_ + static LilCodeGeneratorEM64T cg; +#elif _IPF_ + static LilCodeGeneratorIpf cg; +#endif + return (LilCodeGenerator*)&cg; +} + +LilCodeGenerator::LilCodeGenerator() +{ +} + +NativeCodePtr LilCodeGenerator::compile(LilCodeStub* cs, PoolManager* code_pool) +{ + assert (code_pool); + size_t stub_size; + NativeCodePtr stub = compile_main(cs, &stub_size, code_pool); + lil_cs_set_code_size(cs, stub_size); + + compile_add_dynamic_generated_code_chunk("unknown", false, stub, stub_size); + + if(jvmti_should_report_event(JVMTI_EVENT_DYNAMIC_CODE_GENERATED)) + { + jvmti_send_dynamic_code_generated_event("unknown", stub, + (jint)stub_size); + } + + return stub; +} + + +NativeCodePtr LilCodeGenerator::allocate_memory(size_t size, PoolManager* code_pool) +{ + assert(code_pool); + NativeCodePtr buf = code_pool->alloc(size, DEFAULT_CODE_ALIGNMENT, CAA_Allocate); + + // Check for 16-byte alignment + assert((((POINTER_SIZE_INT)buf)&15)==0); + return buf; +} diff --git a/vm/vmcore/src/lil/lil_code_generator_utils.cpp b/vm/vmcore/src/lil/lil_code_generator_utils.cpp new file mode 100644 index 0000000..78ea911 --- /dev/null +++ b/vm/vmcore/src/lil/lil_code_generator_utils.cpp @@ -0,0 +1,147 @@ +/* + * 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. + */ +/** + * @author Intel, Evgueni Brevnov, Ivan Volosyuk + * @version $Revision: 1.1.2.1.4.4 $ + */ + + +#include +#include + +#define LOG_DOMAIN "vm.helpers" +#include "cxxlog.h" + +#include "lil.h" +#include "lil_code_generator_utils.h" +#include "tl/memory_pool.h" + +LilCguLabelAddresses::LilCguLabelAddresses(tl::MemoryPool* mem, char * init_base): +first(NULL), my_mem(mem), base(init_base) {} + +void LilCguLabelAddresses::change_base(char * new_base) { + LilCguLabelAddress * cur_label_addr; + LilCguPatch * cur_patch; + for (cur_label_addr = first; cur_label_addr != NULL; cur_label_addr = cur_label_addr->next) { + if (cur_label_addr->base_relative) { + // all patch addresses are sensitive to the base address + // since label address is sensitive to the base as well + // we don't need to patch the code. just remember new addresses + cur_label_addr->addr += new_base - base; + for (cur_patch = cur_label_addr->patches; cur_patch != NULL; cur_patch = cur_patch->next) { + cur_patch->addr += new_base - base; + } + } else { + //need to patch the code + for (cur_patch = cur_label_addr->patches; cur_patch != NULL; cur_patch = cur_patch->next) { + cur_patch->addr += new_base - base; + apply_patch(cur_label_addr, cur_patch); + } + } + } + base = new_base; +} + +// when base_relative is true it means that label address should be recalculated if base is changed +void LilCguLabelAddresses::define_label(LilLabel l, void * code, bool base_relative) { + LilCguLabelAddress * cur = first; + while (cur != NULL) { + if (strcmp(cur->l, l) == 0) { + if (cur->addr == NULL) { + // not defined + cur->addr = (char *)code; + cur->base_relative = base_relative; + apply_patches(cur); + } +#ifndef NDEBUG + else { + // such label has already been defined + // check that they are consistent + assert(base_relative == cur->base_relative && cur->addr == code); + } +#endif + return; + } + cur = cur->next; + } + // need to create new label address + add_new_label_adress(l, code, base_relative); +} + +void LilCguLabelAddresses::add_patch_to_label(LilLabel l, void * patch_address, LilCguPatchType patch_type) { + LilCguLabelAddress * cur = first; + // try to find existing label address + while (cur != NULL && strcmp(cur->l, l) != 0) { + cur = cur->next; + } + + // create new label address if not found + if (!cur) { + add_new_label_adress(l, NULL, false); + cur = first; + } + + // add new patch + LilCguPatch * p = (LilCguPatch*)my_mem->alloc(sizeof(LilCguPatch)); + p->addr = (char *)patch_address; + p->type = patch_type; + p->next = cur->patches; + cur->patches = p; + + // apply patch if label defined + if (cur->addr != NULL) { + apply_patch(cur, p); + } +} + +void LilCguLabelAddresses::apply_patches(LilCguLabelAddress * label_adress) { + for(LilCguPatch * p = label_adress->patches; p != NULL; p = p->next) { + apply_patch(label_adress, p); + } +} + +void LilCguLabelAddresses::apply_patch(LilCguLabelAddress * label_adress, LilCguPatch * patch) { + int64 diff; + switch (patch->type) { + case LPT_Rel8: + diff = (int64)((char *)label_adress->addr - ((char *)patch->addr + 1)); + assert(diff == (int64)(int8)diff); + *(int8*)patch->addr = (int8)diff; + break; + case LPT_Rel32: + diff = (int64)((char *)label_adress->addr - (char *)((int32 *)patch->addr + 1)); + assert(diff == (int64)(int32)diff); + *(int32*)patch->addr = (int32)diff; + break; + case LPT_Abs32: + assert((POINTER_SIZE_INT)label_adress->addr <= 0xFFFFffff); + *(int32*)patch->addr = (int32)(POINTER_SIZE_INT)label_adress->addr; + break; + default: + ASSERT(0, "Unknown patch type"); + } +} + +void LilCguLabelAddresses::add_new_label_adress(LilLabel l, void * code, bool base_relative) { + LilCguLabelAddress * cur = (LilCguLabelAddress*)my_mem->alloc(sizeof(LilCguLabelAddress)); + cur->l = l; + cur->addr = (char *)code; + cur->base_relative = base_relative; + cur->patches = NULL; + cur->next = first; + first = cur; +} diff --git a/vm/vmcore/src/lil/stack_iterator/m2n.cpp b/vm/vmcore/src/lil/stack_iterator/m2n.cpp new file mode 100644 index 0000000..172c82f --- /dev/null +++ b/vm/vmcore/src/lil/stack_iterator/m2n.cpp @@ -0,0 +1,34 @@ +/* + * 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. + */ +/** + * @author Evgueni Brevnov + * @version $Revision: 1.1 $ + */ + +#include "m2n.h" + +const uint32 FRAME_UNKNOWN = 0x00; +const uint32 FRAME_NON_UNWINDABLE = 0x80; +const uint32 FRAME_JNI = 0x01 | FRAME_NON_UNWINDABLE; +const uint32 FRAME_COMPILATION = 0x02 | FRAME_NON_UNWINDABLE; +const uint32 FRAME_UNPOPABLE = 0x0000; +const uint32 FRAME_POPABLE = 0x0100; +const uint32 FRAME_POP_NOW = 0x0200; +const uint32 FRAME_POP_DONE = FRAME_POPABLE | FRAME_POP_NOW; +const uint32 FRAME_POP_MASK = 0x0700; +const uint32 FRAME_SAFE_POINT = 0x0800; +const uint32 FRAME_MODIFIED_STACK = 0x1000; diff --git a/vm/vmcore/src/lil/stack_iterator/stack_iterator.cpp b/vm/vmcore/src/lil/stack_iterator/stack_iterator.cpp new file mode 100644 index 0000000..5d2d230 --- /dev/null +++ b/vm/vmcore/src/lil/stack_iterator/stack_iterator.cpp @@ -0,0 +1,66 @@ +/* + * 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. + */ +/** + * @author Intel, Pavel Afremov + * @version $Revision: 1.1 $ + */ + + +#include "interpreter.h" +#include "jit_intf_cpp.h" +#include "m2n.h" +#include "stack_iterator.h" +#include "cci.h" + +Method_Handle si_get_method(StackIterator* si) +{ + ASSERT_NO_INTERPRETER + CodeChunkInfo* cci = si_get_code_chunk_info(si); + if (cci) + return cci->get_method(); + else + return m2n_get_method(si_get_m2n(si)); +} + +uint32 si_get_inline_depth(StackIterator* si) +{ + // + // Here we assume that JIT data blocks can store only InlineInfo + // A better idea is to extend JIT_Data_Block with some type information + // Example: + // + // enum JIT_Data_Block_Type { InlineInfo, Empty } + // + // struct JIT_Data_Block { + // JIT_Data_Block *next; + // JIT_Data_Block_Type type; + // char bytes[1]; + // }; + // + // void *Method::allocate_JIT_data_block(size_t size, JIT *jit, JIT_Data_Block_Type) + // + + ASSERT_NO_INTERPRETER + CodeChunkInfo* cci = si_get_code_chunk_info(si); + if ( cci != NULL && cci->has_inline_info()) { + return cci->get_jit()->get_inline_depth( + cci->get_inline_info(), + // FIXME64: no support for large methods + (uint32)((POINTER_SIZE_INT)si_get_ip(si) - (POINTER_SIZE_INT)cci->get_code_block_addr())); + } + return 0; +} -- 1.3.3