blob: 1b27c0af700ae15ccc02639feeb6049c67ebd482 [file] [log] [blame]
/*
* 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 Alexander Astapchuk
*/
/**
* @file
* @brief CodeGen class and related datas declaration.
*/
#if !defined(__CG_H_INCLUDED__)
#define __CG_H_INCLUDED__
#include "enc.h"
#include "sconsts.h"
#include "structs.h"
#include "sframe.h"
#include "csig.h"
#include "mib.h"
#include "../shared/MemoryManager.h"
#include "../main/PMF.h"
#include "../main/JITInstanceContext.h"
#include "../main/CompilationContext.h"
#include <map>
using std::map;
namespace Jitrino {
namespace Jet {
/**
* CallSig for a VM helper that takes 1 argument of (#jobj).
*/
extern const CallSig ci_helper_o;
/**
* CallSig for a VM helper that takes no arguments.
*/
extern const CallSig ci_helper_v;
/**
* CallSig for a VM helper that takes 2 args: (#jobj, #i32).
*/
extern const CallSig ci_helper_oi;
/**
* CallSig for VM helper THROW_LINKAGE_ERROR.
*/
extern const CallSig ci_helper_linkerr;
/**
* Flag indicates that a patch item represents a table for
* LOOKUPSWITCH/TABLESWITCH.
*/
#define DATA_SWITCH_TABLE (0x00010000)
extern void check_arg_has_doc(const char* key);
/**
* @brief Counts number of slots occupied by the specified set #jtype items.
* @param args - array of #jtype
* @return number of slots occupied by the specified set of #jtype items
*/
inline unsigned count_slots(const ::std::vector<jtype>& args)
{
unsigned slots = 0;
for (unsigned i=0; i<args.size(); i++) {
slots += (args[i] == dbl64 || args[i] == i64 ? 2 : 1);
};
return slots;
}
/**
* @brief Counts number of slots occupied by the specified set #jtype items.
* @param num - number of items in the \c args array
* @param args - array of #jtype
* @return number of slots occupied by the specified set of #jtype items
*/
inline unsigned count_slots(unsigned num, const jtype * args)
{
unsigned slots = 0;
for (unsigned i=0; i<num; i++) {
slots += args[i] == dbl64 || args[i] == i64 ? 2 : 1;
}
return slots;
}
/**
* The structure contains information about counter for patching profiling counter after
* profile is ready
*/
struct ProfileCounterInfo {
//This field contains composite info on counter size (first byte) and offset (last 3 bytes)
U_32 offsetInfo;
//Link to the basic block to calculate counter's offset after code layout
BBInfo* bb;
ProfileCounterInfo() : offsetInfo(0), bb(NULL){}
static U_32 getInstSize(U_32 offsetInfo) { return offsetInfo >> 24;}
static U_32 getInstOffset(U_32 offsetInfo) { return offsetInfo & 0x00FFFFFF;}
static U_32 createOffsetInfo(U_32 instSize, U_32 instOffset) {
assert(instSize<0xFF && instOffset<0xFFFFFF);
return (instSize<<24) | (instOffset);
}
};
/**
* Map contains patching information for all the counters in current method
*/
typedef std::vector<ProfileCounterInfo> ProfileCounterInfos;
/** the class is used by codegen internally to keep field information */
class FieldOpInfo {
public:
FieldOpInfo(Field_Handle f, Class_Handle h, unsigned short i, JavaByteCodes oc)
: fld(f), enclClass(h), cpIndex(i), opcode(oc){}
bool isGet() const {return opcode == OPCODE_GETFIELD || opcode == OPCODE_GETSTATIC;}
bool isPut() const {return !isGet();}
bool isStatic() const {return opcode == OPCODE_GETSTATIC || opcode == OPCODE_PUTSTATIC;}
Field_Handle fld;
Class_Handle enclClass;
unsigned short cpIndex;
JavaByteCodes opcode;
};
/**
* Code generation routines for most of byte code instructions.
*
* The class also contains set of methods for <b>local register
* allocation</b>. The method that perform local register allocation if
* valloc(jtype jt).
*
*/
class CodeGen : public StaticConsts, public Encoder, public MethInfo {
protected:
CodeGen(void)
{
m_pmfPipeline = NULL;
m_pmf = NULL;
m_lazy_resolution = true;
m_compileHandle = NULL;
}
/**
* @brief Compilation parameters.
*/
OpenMethodExecutionParams compilation_params;
public:
/**
* @brief Generates code for pushing int constant on operand stack.
*/
void gen_push(int);
/**
* @brief Generates code for pushing jlong constant on operand stack.
*/
void gen_push(jlong);
/**
* @brief Generates code which reads a constant value of type 'jt' from
* the address specified by 'p' and pushes it onto the stack.
*/
void gen_push(jtype jt, const void * p);
/**
* @brief Generates code which pops out a value of type 'jt' from the
* operand stack and stores it at the address specified by 'p'.
*/
void gen_pop(jtype jt);
/**
* @brief Generates code to perform POP2 bytecode instruction.
*/
void gen_pop2(void);
/**
* @brief Generates various DUP_ operations.
*/
void gen_dup(JavaByteCodes opc);
/**
* @brief Generates xSTORE operations.
*/
void gen_st(jtype jt, unsigned idx);
/**
* @brief Generates xLOAD operations.
*/
void gen_ld(jtype jt, unsigned idx);
/**
* @brief Generates LDC operation.
*/
void gen_ldc(void);
/**
* @brief Generates PUTFIELD/GETFIELD/PUTSTATIC/GETSTATIC operations.
*
*/
void gen_field_op(JavaByteCodes opcode, Class_Handle enclClass, unsigned short cpIndex);
/**
* @brief Generates modification watchpoints if VM need it.
*
* @param jt - field type.
* @param fld - field handle.
*/
void gen_modification_watchpoint(JavaByteCodes opcode, jtype jt, Field_Handle fld);
/**
* @brief Generates access watchpoints if VM need it.
*
* @param jt - field type.
* @param fld - field handle.
*/
void gen_access_watchpoint(JavaByteCodes opcode, jtype jt, Field_Handle fld);
/**
* @brief Restore all scratch registers and operand stack state
*
* @param saveBB - pointer to operand stack state.
*/
void pop_all_state(BBState* saveBB);
/**
* @brief Save all scratch registers and operand stack state
*
* @param saveBB - pointer to operand stack state.
*/
void push_all_state(BBState* saveBB);
/**
* @brief Generates code for INVOKE instructions.
*/
void gen_invoke(JavaByteCodes opcod, Method_Handle meth, unsigned short cpIndex,
const ::std::vector<jtype>& args, jtype retType);
/**
* @brief Generates IINC operation.
* @param idx - index of local variable.
* @param value - value to add.
*/
void gen_iinc(unsigned idx, int value);
/**
* @brief Generates arithmetic operations.
*
* @note \c op argument always specified as integer operation (for
* example IADD, INEG), even if \c jt specifies float-point or long
* type.
*
* @param op - operation to perform.
* @param jt - types used in the arithmetic operation.
*/
void gen_a(JavaByteCodes op, jtype jt);
/**
* @brief Helper function for #gen_a, generates #i32 arithmetics.
*/
bool gen_a_i32(JavaByteCodes op);
/**
* @brief Helper function for #gen_a, generates #flt32 and #dbl64
* arithmetics.
*/
bool gen_a_f(JavaByteCodes op, jtype jt);
/**
* @brief Helper function for #gen_a, may implement some
* platform-dependent operations.
*/
bool gen_a_platf(JavaByteCodes op, jtype jt);
/**
* @brief Performs generic operations which do not depend on type.
*/
bool gen_a_generic(JavaByteCodes op, jtype jt);
/**
* @brief Generates conversion code.
*/
void gen_cnv(jtype from, jtype to);
/**
* @brief Generates various CMP operations.
*/
void gen_x_cmp(JavaByteCodes op, jtype jt);
/**
* @brief Generates code for NEW instruction.
*/
void gen_new(Class_Handle enclClass, unsigned short cpIndex);
/**
* @brief Generates ANEWARRAY, NEWARRAY.
*/
void gen_new_array(Allocation_Handle ah);
void gen_new_array(Class_Handle enclClass, unsigned cpIndex);
/**
* @brief Generates MULTIANEWARRAY.
*/
void gen_multianewarray(Class_Handle enclClass, unsigned short cpIndex, unsigned num_dims);
/**
* @brief Generates code for INSTANCEOF or CHECKCAST operations.
* @param chk - if \b true, generates CHECKCAST, INSTANCEOF otherwise.
* @param klass - Class_Handle of the class to cast to.
*/
void gen_instanceof_cast(JavaByteCodes opcode, Class_Handle enclClass, unsigned short cpIndex);
/**
* @brief Generates code for MONITOREXTERN and MONITOREXIT.
*/
void gen_monitor_ee(void);
/**
* @brief Generates code for ATHROW.
*/
void gen_athrow(void);
/**
* @brief Update BBState and pushes return value on operand stack,
* as if the code just executed a call that returned the given value.
*
* The item location is set according to the calling convention on
* which registers to use to return value of the given type.
*
* On IA-32 (where the float/double are returned through FPU) the
* item is spilled (code is generated to do so) into memory first.
* @param cs - calling signature descrubing the method to push return
* value for.
*/
void gen_save_ret(const CallSig& cs);
/**
* @brief Generates code to call one of the throw_ helpers.
*
* @note The method only synchronize local vars into the memory.
*/
void gen_call_throw(const CallSig& cs, void * target, unsigned idx, ...);
/**
* @brief Generates code to call non-VM helper.
*
* The method does not update GC info.
*/
void gen_call_novm(const CallSig& cs, void * target, unsigned idx, ...);
/**
* @brief Generates code to call a VM helper.
*
* The method updates GC info and synchronizes both stack and local vars.
*/
void gen_call_vm(const CallSig& cs, void * target, unsigned idx, ...);
/**
* @brief Generates code to call a VM helper and then restores the
* state of local variable, stack and registers.
*
* The method updates GC info and synchronizes both stack and local vars.
*/
void gen_call_vm_restore(bool exc, const CallSig& cs, void * target,
unsigned idx, ...);
/**
* @brief Generates code to take arguments from Java operand stack and
* put them to the native stack/registers to prepare a call.
*
* @note The method does rlock(cs), so caller must runlock(cs).
*
* @param pop - if \b true, the arguments are popped out from the operand
* stack. Otherwise, the operand stack is left intact.
* @param cs - CallSig describing args
* @param idx - an index of arguments to start from
* @param cnt - how many arguments to process. -1 means 'till the end'
*/
unsigned gen_stack_to_args(bool pop, const CallSig& cs, unsigned idx,
int cnt=-1);
/**
* @brief Generates code to prepare arguments for a call.
*
* @note The method does \b not rlock(cs).
* @note All possible conflicts of \c parg registers and registers
* used in \c cs, must be resolved before gen_args call.
*/
void gen_args(const CallSig& cs, unsigned idx, const Val * parg0 = NULL,
const Val * parg1 = NULL, const Val * parg2 = NULL,
const Val * parg3 = NULL, const Val * parg4 = NULL,
const Val * parg5 = NULL, const Val * parg6 = NULL);
/**
* @brief Generates ARRAYLENGTH instruction.
*/
void gen_array_length(void);
/**
* @brief Generates ALOAD instruction.
*
* Also invokes gen_check_bounds() and gen_check_null().
*/
void gen_arr_load(jtype jt);
/**
* @brief Generates ASTORE instruction.
*
* Also invokes gen_check_bounds() and gen_check_null().
*/
void gen_arr_store(jtype jt, bool helperOk = true);
/**
* @brief Generates code to check bounds for array access.
* @param aref_depth - depth (in the operand stack) of the array's object
* reference
* @param index_depth - depth (in the operand stack) of the index to be
* used for array's access.
*/
void gen_check_bounds(unsigned aref_depth, unsigned index_depth);
/**
* @brief Generates code to check whether the object ref at the given
* depth in the operand stack is \c null.
* @param stack_depth_of_ref - depth in the operand stack of the object
* reference to test against \c null.
*/
void gen_check_null(unsigned stack_depth_of_ref);
/**
* @brief Generates code to check whether the object apecified by the
* \c Val is not \b NULL.
* @param val - the value to check for \b NULL
* @param hw_ok - whether it's ok to eliminate explicit NPE check in
* favor of hardware NPE check.
*/
void gen_check_null(Val& val, bool hw_ok);
/**
* @brief Generates code to check whether an item used in division
* operation is zero.
* @param jt - type of division operation (#i64 or #i32) to be performed.
* @param stack_depth_of_divizor - depth in the operand stack of the
* divisor.
*/
void gen_check_div_by_zero(jtype jt, unsigned stack_depth_of_divizor);
/**
* @brief Generates a code which prepares GC info for operand stack -
* stack depth and GC mask.
*
* @param depth - if \b -1, then current stack depth is taken,
* otherwise, the specified depth is stored.
* @param trackIt - if \b true, then the GC info is also reflected in
* the current BB's state (BBState::stack_depth,
* BBState::stack_mask).
* @see BBState
*/
void gen_gc_stack(int depth=-1, bool trackIt=false);
/**
* @brief Generates code which either marks or clears mark on a local
* variable to reflect whether it holds an object or not
* (runtime GC info).
*/
void gen_gc_mark_local(jtype jt, unsigned idx);
/**
* @brief Generates GC safe point code which facilitate thread
* suspension on back branches.
*/
void gen_gc_safe_point(void);
/**
* @brief Inserts a break point.
*
* For debugging only.
*/
void gen_brk(void);
/**
* @brief Generates code to ensure stack integrity at runtime.
* @note For debug checks only.
* @param start - \b true if check start to be generated, false otherwise
*/
void gen_dbg_check_stack(bool start);
/**
* @brief Generates code to output string during runtime.
*
* The generated code preserves general-purpose registers.
* The string is formatted before the code generation, then
* \link #dbg_rt get printed \endlink during runtime.
*
* For debugging only.
*/
void gen_dbg_rt(bool save_regs, const char * fmt, ...);
/**
* @brief Generates code to throw specified tipe of exception.
*
* The generated code which throws exception of soecified type.
* Also it can updates GC info and synchronizes both stack and local vars.
*/
void gen_throw(Class_Handle exnClass, bool restore);
/**
* The opcode may be one of AASTORE, PUTFIELD or PUTSTATIC.
*
* For AASTORE \c fieldHandle must be \b NULL.
* For AASTORE \c fieldSlotAddress is not used
*/
void gen_write_barrier(JavaByteCodes opcode, Field_Handle fieldHandle, Opnd fieldSlotAddress);
/**
* @brief Prints out an argument's (or return value's) type and value.
*
* The method is invoked during runtime from the compiled code.
*
* The output goes through dbg_rt.
* @param val - value of the argument/return value
* @param idx - index of argument or -1 if return value is printed
* @param jt - type of the argument/return value
*/
static void __stdcall dbg_trace_arg(void * val, int idx, jtype jt) stdcall__;
/**
* @brief CallSig object for dbg_trace_arg().
*/
static const CallSig cs_trace_arg;
/**
* @brief Converts given \c s into human-readable string.
*
* Used for debugging output.
*
* @param s - Val to be converted.
* @param is_stack - \b true if \c s represents operand stack item.
*/
::std::string toStr2(const Val& s, bool is_stack) const;
/**
* @brief Prints out BBState.
* @param name - a name to print out before the dump, to identify the
* dump in the trace log.
* @param pState - BBState to dump.
*/
void dbg_dump_state(const char * name, BBState * pState);
/**
* @brief Enforces memory check.
*
* Implementation is platform dependent.
*
* Currently, the check is performed only on Windows in debug build,
* using CRT routines.
*/
void dbg_check_mem(void);
/**
* @brief Generates move operation from \c src to \c dst.
*
* The types of both \c src and \c dst must be the same and \c dst must
* not be immediate. If both items reside in the memory, then a
* temporary register is allocated to perform the movement.
*
* The method does not transfer attributes.
*
* @param src - source operand.
* @param dst - destination operand.
*/
void do_mov(const Val& dst, const Val& src, bool skipTypeCheck=false);
/**
* @brief Returns a stack item at the given depth.
*
* @param depth - depth of stack item to return.
* @param toReg - if \b true, then ensures that the stack item resides
* on a register.
*/
Val& vstack(unsigned depth, bool toReg = false);
/**
* @brief Returns a local variable item.
*
* The local variable's slot at the given \c idx must be either
* unknown (of #jvoid type) or must be of the same type \c jt.
*
* If operation being generted is defining operation, then vvar_def().
* must be called before vlocal() and \c willDef must be set to \b true.
*
* If the variable has globally allocated register, but is currently
* spilled out, then the code to upload it back from the memory to
* register is generated. If \c willDef == \b true, no upload code
* is generated.
*
* Also, if \c willDef == \b true, then the method ensures that any
* references to the variable in operand stack are 'unreferenced' -
* that is the value of variable is read and then written into
* operand stack memory cells.
*
* @note As a side effect of this function some items on operand stack
* may change their location.
*
* @param jt - type of the variable.
* @param idx - index of the variable.
* @param willDef - whether the next operation with the variable
* will be defining operation.
* @see vvar_def
*/
Val& vlocal(jtype jt, unsigned idx, bool willDef = false);
/**
* @brief Ensures the local variable slot has the proper type.
*
* If the \c idx slot is occupied by an register of other type (e.g.
* FSTORE followed by ISTORE), then the register is freed, and the
* slot get the proper type assigned to it.
*/
void vvar_def(jtype jt, unsigned idx);
/**
* @brief Returns offset of the given local variable in the stack frame.
*
* May not be equal to m_stack.local(idx) as some local variables that
* come as input args, are not copied into stack frame but are used
* at the same location as the input args are.
*
* The returned value reflect current stack depth (if applicable).
*/
int vlocal_off(unsigned idx) const;
/**
* @brief Returns offset of the operand stack item at the given depth
* in method's stack frame.
*
* The returned value reflect current stack depth (if applicable).
*/
int vstack_off(unsigned depth) const;
/**
* @brief Corrects an offset in the method's stack frame according
* to the current stack depth.
*
* The method is intended for use with sp-based stack frames.
*/
int voff(int off) const;
/**
* @brief Verifies data integrity in current BBState. For debugging
* purposes only.
*/
void vcheck(void);
/**
* @brief Allocates temporary scratch register of the given type.
*
* The allocation is based on reference count for a register, register
* locks and 'last_used' fields in the BBState.
*
* The 'last used' idiom allows to spread operations across several
* registers. When a code is about to allocate temporary register, then
* the allocation routine tries to allocate a register \b other than one
* allocated before. This reduces dependencies between operations and
* increases parallelism chances.
*
* If the allocation routine failed to find an unused register of
* proper type, then it generates spill code. A register to spill is
* chosen on reference count for the register. The reference count is
* current number of usages of the register in both operand stack and
* local variables array.
*
* A non-locked register with the lowest number of references is
* subject to spill out - the code is generated.
*
* The reference count is not calculated in valloc() itself, but used
* from BBState data (rref()).
*
* This presumes that usage of registers in mimic operand stack and
* local variables is tracked properly. Debug method vcheck() ensures
* the proper reference count.
*
* @note As a side effect of this function some non-locked items of
* operand stack or local variables may change its kind from register
* to memory.
*/
AR valloc(jtype jt);
/**
* @brief Ensures operand stack item at the given \c depth resides in
* the memory.
*/
void vswap(unsigned depth);
/**
* @brief Synchronizes all scratch registers into memory.
*
* If \c doStack is \b false, then only processes local variables.
*
* @param doStack - \c true to synchronize operand stack items as well
*/
void vpark(bool doStack = true);
/**
* @brief Synchronizes given register into memory.
*
* If \c doStack is \b false, then only processes local variables.
*
* @param ar - register to be synchronized
* @param doStack - \c true to synchronize operand stack items as well
*/
void vpark(AR ar, bool doStack = true);
/**
* @brief Tests whether operand stack item at the given depth is
* immediate.
*/
bool vis_imm(unsigned depth)
{
return m_jframe->dip(depth).is_imm();
}
/**
* @brief Tests whether operand stack item at the given depth resides
* in memory.
*/
bool vis_mem(unsigned depth)
{
return m_jframe->dip(depth).is_mem();
}
/**
* @brief Tests whether operand stack item at the given depth resides
* in register.
*/
bool vis_reg(unsigned depth)
{
return m_jframe->dip(depth).is_reg();
}
/**
* @brief One or 2 (for wide types) empty Val-s on the operand stack.
* @note In contrast to vpush(const Val&) accepts wide types.
*/
void vpush(jtype jt);
/**
* @brief Pushes the given Val into mimic operand stack.
*
* Increments reference counts for registers involved.
*/
void vpush(const Val& op);
/**
* @brief Pushes the given Val into mimic operand stack.
*
* Increments reference counts for registers involved.
*/
void vpush2(const Val& op_lo, const Val& op_hi);
/**
* @brief Pops out 2 parts of \link is_big() big \endlink type from
* mimic operand stack.
*
* Decrements reference counts for involved registers.
*/
void vpop2(Val* pop_low, Val* pop_hi);
/**
* @brief Pops out an item from mimic operand stack.
*
* Decrements reference counts for involved registers.
*/
void vpop(void);
/**
* @brief Marks that the local variable \c idx resides in the given
* register (\c s).
* @param jt - type of local variable
* @param idx - index of local variable
* @param s - must represent a register of proper type, to be assigned
* as current variable's location
*/
void vassign(jtype jt, unsigned idx, const Val& s);
/**
* @brief Tests whether the memory reference in \c s points to an
* operand stack item in the stack frame.
*/
bool vis_stack(const Val& s) const;
/**
* @brief Tests whether the operand stack at the given \c depth
* is memory <b>and is located in the operand stack area in the stack
* frame</b>.
* In other words, it's memory and represents neither a local variable,
* nor a field, nor any other value stored in memory, but exactly
* the operand stack element.
*/
bool vis_stack(unsigned depth) const
{
return vis_stack(m_jframe->dip(depth));
}
/**
* @brief Returns \b true if the local variable is not stored in the
* method's stack frame, but rather a stack slot of input argument is
* reused.
*/
bool vis_arg(unsigned local_idx) const;
/**
* @brief If vis_arg(local_idx) is \b true, the method returns
* index of input argument that corresponds to this local variable.
*
* Must only be called for local variables which really reuse input
* argument stack space (that is, vis_arg()==\b true).
*/
unsigned vget_arg(unsigned local_idx) const;
/**
* @brief Tests whether the memory reference in \c s points to a local
* variable. An index of the variable (>=0) is returned, or -1 if
* the memory does not point to local variable.
*
* This method takes vis_arg() into account - if the variable reuses
* input argument space, then the address is checked against that
* argument's address.
*/
int vvar_idx(const Val& s) const;
/**
* @brief Wraps given address into operand.
*
* The method checks whether the address fits into displacement part
* of complex address form (which is always true for IA-32). If so,
* then the operand with no base is returned.
*
* Otherwise, GR register allocated, the code is generated to load
* address into the register and the operand with the register as
* base and zero displacement returned.
*/
Opnd vaddr(jtype jt, const void* p)
{
const char * addr = (const char*)p;
// If we're going to address more that 4 bytes, ensure the next
// item will also fit into displacement.
if (fits32(addr) && fits32(addr+4)) {
return Opnd(jt, ar_x, (int)(int_ptr)addr);
}
AR ar = valloc(jobj);
movp(ar, addr);
return Opnd(jt, ar, 0);
}
/**
* @brief Return <i>static type</i> of the local variable.
*
* Static type is the type which is only used to access the variable.
*
* For example, if a variable is only used for ASTORE and ALOAD, then
* the static type is #jobj. Otherwise (e.g. FSTORE/ASTORE mix), static
* type is #jvoid.
*/
jtype vtype(unsigned idx);
/**
* Returns a register allocated for the given local variable, ar_x if
* none.
*/
AR vreg(jtype jt, unsigned idx);
/**
* @brief Dereferences all items of the given type on operand stack.
*
* Dereference here means the following:
*
* When mimic operand stack operations, CodeGen enforces lazy scheme -
* e.g. for xLOAD operations, the reference on local variable is
* stored onto mimic stack.
*
* However, when we perform a defining operation for a local variable
* we must ensure that operand stack items contain the value of the
* variable before the defining operation.
*
* The method generates code to reload such postponed references from
* the local variable(s) into proper operand stack memory slot(s).
*/
void vunref(jtype jt);
/**
* Dereferences all items on the operand stack the refer to the given
* Opnd (either register or memory location).
*
* @see vunref(jtype jt) on dereference description.
*/
void vunref(jtype jt, const Val& op, unsigned skipDepth = NOTHING);
/**
* @brief Locks the given \c ar.
*
* Increases number of locks for the specified \c ar.
*
* Lock on an AR prevents the register from being \link valloc(jtype)
* allocated for temporary needs \endlink.
*
* The lock may be acquired several times, but must be released the
* same number of times.
*/
void rlock(AR ar)
{
if (ar != ar_x && ar != (AR)NOTHING) {
assert(ar_idx(ar)<ar_num);
++m_bbstate->m_regs[ar_idx(ar)].locks;
}
}
/**
* @brief Unlocks the given \c ar.
*
* Decreases number of locks for the specified \c ar.
*
* @param ar - register to be locked
* @param force - if \b true, then drops the lock count to zero,
* regardless of how many lock(ar) was invoked before.
* @see rlock(AR ar)
*/
void runlock(AR ar, bool force = false)
{
if (ar != ar_x && ar != (AR)NOTHING) {
assert(ar_idx(ar)<ar_num);
assert(m_bbstate->m_regs[ar_idx(ar)].locks>0);
if (force) {
m_bbstate->m_regs[ar_idx(ar)].locks = 0;
}
else {
--m_bbstate->m_regs[ar_idx(ar)].locks;
}
}
}
/**
* @brief Locks all the registers used in the \c cs.
*/
void rlock(const CallSig& cs)
{
for (unsigned i=0; i<cs.count(); i++) {
AR ar = cs.reg(i);
if (ar != ar_x) {
rlock(ar);
}
}
}
/**
* @brief Unlocks all the registers used in the \c cs.
*/
void runlock(const CallSig& cs)
{
for (unsigned i=0; i<cs.count(); i++) {
AR ar = cs.reg(i);
if (ar != ar_x) {
runlock(ar);
}
}
}
/**
* @brief Locks the registers in the \c s.
*
* If \c s refers to register, then this register is locked.
*
* If \c is memory reference, then all valid registers of complex
* address form are locked.
*
* For immediate operand it's no-op.
*/
void rlock(const Val& s)
{
if (s.is_reg()) { rlock(s.reg()); }
else if (s.is_mem()) { rlock(s.base()); rlock(s.index()); }
}
/**
* @brief Unlocks all registers in the \c s.
*/
void runlock(const Val& s)
{
if (s.is_reg()) { runlock(s.reg()); }
else if (s.is_mem()) { runlock(s.base()); runlock(s.index()); }
}
/**
* @brief Increments reference counts for registers in the \c s.
*/
void rref(const Val& s)
{
if (s.is_reg()) { rref(s.reg()); }
else if (s.is_mem()) { rref(s.base()); rref(s.index()); }
}
/**
* @brief Decrements reference counts for registers in the \c s.
*/
void rfree(const Val& s)
{
if (s.is_reg()) { rfree(s.reg()); }
else if (s.is_mem()) { rfree(s.base()); rfree(s.index()); }
}
/**
* @brief Increments reference counts for \c ar;
*/
void rref(AR ar)
{
if (ar != ar_x && ar != (AR)NOTHING) {
assert(ar_idx(ar)<ar_num);
++m_bbstate->m_regs[ar_idx(ar)].refs;
}
}
/**
* @brief Decrements reference counts for \c ar;
*/
void rfree(AR ar)
{
if (ar != ar_x && ar != (AR)NOTHING) {
assert(ar_idx(ar)<ar_num);
assert(m_bbstate->m_regs[ar_idx(ar)].refs>0);
--m_bbstate->m_regs[ar_idx(ar)].refs;
}
}
/**
* @brief Returns number of references for \c ar;
*/
unsigned rrefs(AR ar) const
{
assert(ar_idx(ar)<ar_num);
return m_bbstate->m_regs[ar_idx(ar)].refs;
}
/**
* @brief Returns number of locks for \c ar;
*/
unsigned rlocks(AR ar) const
{
assert(ar_idx(ar)<ar_num);
return m_bbstate->m_regs[ar_idx(ar)].locks;
}
/**
* @brief Tries to find out which register is currently know to hold
* the given value (if \c v is immediate) or a value from the specified
* address (if \c v is memory).
*/
AR rfind(const Val& v) const
{
for (unsigned i=0; i<ar_num; i++) {
if (m_bbstate->m_regs[i].val == v) {
return _ar(i);
}
}
return ar_x;
}
/**
* @brief Set's 'currently known' value for the register.
*/
void rset(AR ar, const Val& v)
{
assert(ar_idx(ar)<ar_num);
m_bbstate->m_regs[ar_idx(ar)].val = v;
}
/**
* @brief Clears all registers-related info in current BBState.
*/
void rclear(void)
{
for (unsigned i=0; i<ar_num; i++) {
m_bbstate->m_regs[i].locks = 0;
m_bbstate->m_regs[i].refs = 0;
m_bbstate->m_regs[i].val = Val();
}
}
/**
* @brief Returns 'last used' register of the given type.
* @see valloc
*/
AR rlast(jtype jt) const
{
return is_f(jt) ? m_bbstate->m_last_fr : m_bbstate->m_last_gr;
}
/**
* @brief Sets 'last used' register for the given type.
* @see valloc
*/
void rlast(AR ar)
{
assert(ar != ar_x);
if (is_f(ar)) {
m_bbstate->m_last_fr = ar;
}
else {
assert(is_gr(ar));
m_bbstate->m_last_gr = ar;
}
}
protected:
/**
* @brief Tests whether the specified flag is set in method's compilation
* flags.
*
* If \c flag is a set of flags, then checks whether any of the flags
* is set.
* @see InfoBlock#get_flags
*/
bool is_set(unsigned flag)
{
return (m_infoBlock.get_flags() & flag);
}
/**
* @brief Returns command line argument for the given \c key, or \c def
* if the \c key was not specified.
*
* Normally, arguments are specified as
*
* '-XX:jit.\<JIT_NAME\>.arg.\<key\>=value'
*
* Implemented though Jitrino's PMF, refer to PMF docs for more info.
* @note The old-fashion -Xjit options are not processed by this method.
*/
const char* get_arg(const char* key, const char* def)
{
#ifdef _DEBUG
check_arg_has_doc(key);
#endif
if (m_pmf == NULL) {
m_pmf = &JITInstanceContext::getContextForJIT(m_hjit)->getPMF();
m_pmfPipeline = (PMF::Pipeline*)CompilationContext::getCurrentContext()->getPipeline();
}
return m_pmf->getStringArg(m_pmfPipeline, key, def);
}
/**
* @brief Returns a command-line argument interpreted as integer.
*/
int get_int_arg(const char* key, int def)
{
const char* val = get_arg(key, (const char*)NULL);
return val == NULL ? def : atoi(val);
}
/**
* @brief Returns a string command-line argument.
*
* Never returns NULL, but empty constant string ("") if argument
* was not set in through the command line.
*/
const char* get_arg(const char* key)
{
const char* val = get_arg(key, (const char*)NULL);
return val == NULL ? "" : val;
}
/**
* @brief Returns a command-line argument interpreted as integer.
* @see to_bool
*/
bool get_bool_arg(const char* key, bool def)
{
const char* val = get_arg(key, (const char*)NULL);
if (val == NULL) {
return def;
}
return to_bool(val);
}
/**
* @brief Do all the job for gen_field_op()
*
* Invokes gen_check_null() for GETFIELD and PUTFIELD.
*/
void do_field_op(const FieldOpInfo& fieldOp);
/**
* @brief Returns mem-opnd that is address of the given field. Used by do_field_op
* Invokes gen_check_null() for GETFIELD and PUTFIELD.
*/
Opnd get_field_addr(const FieldOpInfo& fieldOp, jtype jt);
/**
* PMF instance to get arguments from.
*/
PMF * m_pmf;
/**
* Current filter for PMF.
*/
PMF::HPipeline m_pmfPipeline;
/**
* @brief JIT handle.
*/
JIT_Handle m_hjit;
//*********************************************************************
//* Instrumentation, profiling
//*********************************************************************
/**
* Generates back edge counter increment.
* Does nothing is JMF_PROF_ENTRY_BE not set.
*/
void gen_prof_be(void);
/**
* @brief Pointer to method's entrances counter.
* @see JMF_PROF_ENTRY_BE
*/
unsigned * m_p_methentry_counter;
/**
* @brief Pointer to method's back branches counter.
* @see JMF_PROF_ENTRY_BE
*/
unsigned * m_p_backedge_counter;
/**
* @brief Threshold for method entry counter which fires recompilation
* (in synchronized recompilation mode).
*/
unsigned m_methentry_threshold;
/**
* @brief Threshold for back edges counter which fires recompilation
* (in synchronized recompilation mode).
*/
unsigned m_backedge_threshold;
/**
* @brief Profile handle to be passed to recompilation handler (in
* synchronized recompilation mode).
*/
void* m_profile_handle;
/**
* @brief Recompilation handler (in synchronized recompilation mode).
*/
void * m_recomp_handler_ptr;
/**
* @brief The byte code of the method being compiled.
*/
const unsigned char* m_bc;
JFrame * m_jframe;
/**
* @brief Current basic block's info.
*
* @note Only valid during code generation.
*/
BBInfo* m_bbinfo;
/**
* @brief Current basic block's state.
*
* @note Only valid during code generation.
*/
BBState * m_bbstate;
/**
* @brief PC of an instruction currently processed.
*/
unsigned m_pc;
/**
* @brief Instruction currently being processed.
*/
const JInst * m_curr_inst;
/**
* @brief Method's info block.
*/
MethodInfoBlock m_infoBlock;
/**
* @brief Method's native stack layout.
*/
StackFrame m_stack;
/**
* @brief Global register allocation.
*
* m_ra[local index] contains a register globally allocated for the
* local variable or ar_x.
*/
vector<AR> m_ra;
/**
* 'Static' types for local variables.
*
* A variable has static type if it's used as this type only (e.g. only
* ISTORE and ILOAD are performed). If a local variable slot is used
* as more than one type (e.g. both ASTORE_0 and DSTORE_0 exist) then
* the static type is #jvoid.
*
* The size of the array is equal to the number of local variables.
*/
vector<jtype> m_staticTypes;
/**
* @brief Numbers of def operations of local variables.
*/
vector<unsigned> m_defs; // [num_locals]
/**
* @brief Numbers of use operations of local variables.
*/
vector<unsigned> m_uses; // [num_locals]
/**
* @brief Mapping between input arguments and local variables they
* map to.
*/
vector<int> m_argids; // [m_argSlots];
/**
* @brief Number of slots occupied by input args of the method.
*/
unsigned m_argSlots;
/**
* @brief The bitset shows whether a register (by its index) was
* globally allocated or not.
*
* <code>
* <pre>
* AR ar = ...
* if (m_global_rusage[ar_idx(ar)]) {
* printf("%s allocated globally", to_str(ar).c_str());
* }
* </pre>
* </code>
*/
bitset<ar_num> m_global_rusage;
/**
* @brief CallSig instance for method being compiled.
*/
CallSig m_ci;
/**
* @brief A GR register used to make stack frame for the compiled
* method.
*
* Currently, only #bp is allowed here. In future, it's possible to
* implement #sp-based stack frame.
*/
AR m_base;
/**
* @brief Current stack depth.
*
* For sp-based frame, to track changes of depth of native stack.
*/
unsigned m_depth;
/**
* @brief Maximum value for #m_depth.
*/
unsigned m_max_native_stack_depth;
/**
* @brief Compilation id for the method being compiled.
*
* It's a simple sequential counter for compilation requests came
* through the Jitrino.JET.
* Technically speaking, not equal to the number of compiled methods
* as some methods may be rejected (seen, but not compiled).
*/
unsigned m_methID;
/**
* @brief If 'TRUE' JIT will not ask VM to resolve any unresolved types during compilation
*/
bool m_lazy_resolution;
/**
* @brief Compilation handle.
*/
Compile_Handle m_compileHandle;
ProfileCounterInfos m_profileCountersMap;
};
}}; // ~namespace Jitrino::Jet
#endif // ~ __CG_H_INCLUDED__