blob: 757a34e20f220a9b7faae50c387621a2a8df6ebe [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 Various simple structures for CodeGen and Compiler needs.
*
* Mostly structures that defer better classification are collected here.
*/
#if !defined(__BBSTATE_H_INCLUDED__)
#define __BBSTATE_H_INCLUDED__
#include "jdefs.h"
#include "jframe.h"
//#include "open/vm.h"
#include "jit_import.h"
#include "open/vm_method_access.h"
#include "open/vm_class_manipulation.h"
namespace Jitrino {
namespace Jet {
/**
* @brief Reads out a 4-byte integer stored in the Java's format - 'most
* significant bytes first'.
* @param pbc - a buffer to read from
* @return integer
*/
inline int bc_int(const unsigned char * pbc)
{
return (int)(((*(pbc+0))<<24) | ((*(pbc+1))<<16) |
((*(pbc+2))<<8) | ((*(pbc+3))<<0));
}
/**
* @brief Simple smart pointer.
*
* A trivial smart pointer implementation for dynamically allocated arrays.
* Allocated memory is deallocated in destructor.
*
* Does not support neither copying, nor resizing of array. Used to avoid
* head ache about controlling raw pointers of allocated memory.
*
* Used for arrays and includes bounds check in debug build - no bound
* checks performed in release one.
*/
template<class SomeType> class SmartPtr {
public:
/**
* @brief Initializes empty object.
*/
SmartPtr(void)
{
m_data = NULL;
}
/**
* @brief Allocates memory for \c count elements of SomeType.
*
* May zero the allocated memory if \c clear parameter is \c true.
*/
void alloc(unsigned count, bool clear = false)
{
m_data = new SomeType[count];
m_count = count;
if (clear) {
memset(m_data, 0, bytes());
}
}
/**
* @brief Returns number of elements in the allocates array.
*/
unsigned count(void) const
{
return m_count;
}
/**
* @brief Provides random access to an item by index.
*
* Bounds check is preformed only in debug build.
*/
SomeType& operator[](unsigned idx)
{
assert(idx<m_count);
return m_data[idx];
}
/**
* @brief Provides a direct access to the allocated array.
*/
SomeType* data(void)
{
return m_data;
}
/**
* @brief Returns size of allocated memory, in bytes.
*/
unsigned bytes(void)
{
return sizeof(SomeType)*m_count;
}
/**
* @brief Fress allocated resources.
*/
~SmartPtr()
{
delete[] m_data;
}
private:
/**
* @brief Pointer to the allocated memory.
*/
SomeType * m_data;
/**
* @brief Number of elements in the allocated array.
*/
unsigned m_count;
/**
* @brief Disallow copying.
*/
SmartPtr(const SmartPtr&);
/**
* @brief Disallow copying.
*/
SmartPtr& operator=(const SmartPtr&);
};
/**
* @brief Decoded byte code instruction.
*/
struct JInst {
unsigned id;
/**
* @brief Number of references on this instruction.
*
* The number of references is number of incoming control flow edges.
* Regular instructions have \c ref_count==1, basic block leaders may
* have \c ref_count > 1. \c ref_count == 0 means the instruction is
* dead code.
*/
unsigned ref_count;
/**
* @brief Program counter of this instruction.
*/
unsigned pc;
/**
* @brief Program counter of next instruction.
*/
unsigned next;
/**
* @brief Instruction's opcode.
*/
JavaByteCodes opcode;
/**
* @brief Instruction's flags.
*/
unsigned flags;
/**
* @brief Value of the first instrution's operand, if applicable.
*
* @note If instruction has no operands, the value of 'op0' is undefined.
*/
int op0;
/**
* @brief Value of the second instrution's operand, if applicable.
*
* @note If instruction has no second operands, the value of 'op1' is
* undefined.
*/
int op1;
/**
* @brief Address of data (the padding bytes skipped) for the switch
* instructions.
*
* @note For instructions other than switch, the value is undefined.
*/
const unsigned char* data;
/**
* @brief Returns number of targets for a branch or switch instruction.
*
* For branches it's always 1.
* For switches the default target is not included in the value.
* For other instructions returns 0.
*/
unsigned get_num_targets(void) const
{
if (is_branch()) { return 1; };
if (opcode == OPCODE_TABLESWITCH) {
return high() - low() + 1;
}
if (opcode != OPCODE_LOOKUPSWITCH) {
return 0;
}
// 4 here is the number of bytecode bytes between the values - comes
// from the JVM Spec
return bc_int(data + 4); // +4 - skip the 'defaultbyte*4'
}
/**
* @brief Returns a PC value for the target 'i'.
*
* Must be only invoked for the branch or switch instructions and 'i'
* must be less or equal to gen_num_targets().
* For other cases the behavior is unpredictable.
*/
unsigned get_target(unsigned i) const
{
assert(i < get_num_targets());
if (is_branch()) {
return (unsigned short)(((unsigned short)pc) +
((unsigned short)op0));
}
if (opcode == OPCODE_TABLESWITCH) {
// '4+4+4' - skip defaultbyte, lowbyte and hightbyte
const int * poffsets = (int*)(data + 4 + 4 + 4);
return pc + bc_int((unsigned char*)(poffsets + i));
}
assert(opcode == OPCODE_LOOKUPSWITCH);
// '4+4' - skip defaultbyte and npairs
const int * ppairs = (int*)(data+4+4);
return pc + bc_int((unsigned char*)(ppairs + i*2 + 1));
}
/**
* @brief Returns default target for switch instructions.
*
* Must only be invoked for TABLESWITCH or LOOKUPSWITCH instructions.
*/
unsigned get_def_target(void) const
{
assert(opcode == OPCODE_TABLESWITCH || opcode == OPCODE_LOOKUPSWITCH);
unsigned offset = bc_int(data);
return (unsigned short)(((unsigned short)pc)+((unsigned short)offset));
}
/**
* @brief Returns size of data (in bytes) occupied by LOOKUPSWITCH or
* TABLESWITCH datas.
*
* Must only be invoked for TABLESWITCH or LOOKUPSWITCH instructions.
*/
unsigned get_data_len(void) const
{
if (opcode == OPCODE_TABLESWITCH) {
// 4*3 = defaultbyte,lowbyte,highbyte ; +jmp offsets
return 4*3 + 4*(high()-low()+1);
}
assert(opcode == OPCODE_LOOKUPSWITCH);
// defaultbyte + npairs + (4*2)*npairs
return (4 + 4 + 4*2 * get_num_targets());
}
/**
* @brief Returns minimum value of TABLESWITCH instruction.
*
* Must only be invoked for TABLESWITCH instructions.
*/
int low(void) const
{
assert(opcode == OPCODE_TABLESWITCH);
// 4 here is the number of bytecode bytes between the values - comes
// from the JVM Spec
return bc_int(data + 4);
}
/**
* @brief Returns maximum value of TABLESWITCH instruction.
*
* Must only be invoked for TABLESWITCH instructions.
*/
int high(void) const
{
assert(opcode == OPCODE_TABLESWITCH);
// 4 here is the number of bytecode bytes between the values - comes
// from the JVM Spec
return bc_int(data + 4+4);
}
/**
* @brief Returns Nth key of LOOKUPSWITCH instruction.
*
* Must only be invoked for LOOKUPSWITCH instructions.
*/
int key(unsigned i) const
{
assert(opcode == OPCODE_LOOKUPSWITCH);
// skip defaultbyte(+4), npairs(+4) and 'i' pairs
return bc_int(data+4+4 + i*2*4);
}
/**
* @brief Tests whether the instruction is branch - either conditional
* or not. JSR-s are also considered branches, but not SWITCH
* instructions.
*/
bool is_branch(void) const
{
return (OPCODE_IFEQ <= opcode && opcode <= OPCODE_JSR) ||
(OPCODE_IFNULL <= opcode && opcode <= OPCODE_JSR_W);
}
/**
* @brief Tests whether instruction is either TABLESWITCH or
* LOOKUPSWITCH.
*/
bool is_switch(void) const
{
return (opcode == OPCODE_TABLESWITCH ||
opcode == OPCODE_LOOKUPSWITCH);
}
/**
* @brief Tests whether instruction is JSR[_W].
*/
bool is_jsr(void) const
{
return opcode == OPCODE_JSR || opcode == OPCODE_JSR_W;
}
/**
* @brief Tests whether the given flag (or set of flags) is set.
* @see OPF_
*/
bool is_set(unsigned opf_flag) const
{
return (flags & opf_flag);
}
/**
* @brief Tests whether instruction has single successor.
*/
bool single_suc(void) const
{
// GOTO is the case as well as non-branch instructions with
// fall through pass.
return (get_num_targets()==0 && !is_set(OPF_DEAD_END)) ||
opcode == OPCODE_GOTO || opcode == OPCODE_GOTO_W;
}
};
/**
* @brief Information about exception handler.
*/
struct HandlerInfo {
/**
* @brief Start PC of bytecode region protected by this exception handler
* (inclusive).
*/
unsigned start;
/**
* @brief End PC of bytecode region protected by this exception handler
* (exclusive).
*/
unsigned end;
/**
* @brief Entry point PC of exception handler.
*/
unsigned handler;
/**
* @brief Type of exception handled by this handler. 0 for 'any type'.
*/
unsigned type;
/**
* @brief Class_Handle of the exception class for this handler.
*/
Class_Handle klass;
/**
* @brief IP for #start.
*/
char * start_ip;
/**
* @brief IP for #end.
*/
char * end_ip;
/**
* @brief IP for #handler.
*/
char * handler_ip;
};
/**
* @brief General info about basic block
*/
struct BBInfo {
BBInfo()
{
start = last_pc = next_bb = 0;
code_size = 0;
ehandler = false;
jsr_target = false;
processed = false;
}
/**
* @brief Very first bytecode instruction - the basic block's leader.
*/
unsigned start;
/**
*@brief Last bytecode instruction which belongs to this basic block.
*/
unsigned last_pc;
/**
* @brief Leader of the next (layout successor) basic block.
*
* Actually, the next BB's leader can be obtained by a call to
* Compiler::fetch(last_pc). The redundancy is intentional to avoid
* this additional call.
*/
unsigned next_bb;
/**
* An offset of the BB's code before code layout.
* If the BB has a prolog, then ipoff points to the prolog code.
*/
unsigned ipoff;
/**
* @brief Total size of native code of the basic block.
*/
unsigned code_size;
/**
* @brief Code address after layout (the final and real address).
*/
char * addr;
/**
* \b true if the basic block servers as a target for at least one
* JSR instruction.
*/
bool jsr_target;
/**
* \b true if this basic block is a catch handler.
*
* Normally this means that is has an additional small prolog
* before a code which is generated for its first bytecode.
*/
bool ehandler;
/**
* \b true if the basic block was processed in
* Compiler::comp_gen_code_bb().
*
* Used to avoid recursion and to detect unreachable code
* at the stage of code layout.
*/
bool processed;
};
typedef map<unsigned, BBInfo> BBMAP;
/**
* General info and various attributes of a method.
*/
struct MethInfo
{
/**
* Initializes emty MethInfo.
*
* init() must be called before a first usage of MethInfo instance.
*/
MethInfo()
{
m_method = NULL;
m_klass = NULL;
m_kname = m_mname = m_sig = NULL;
}
/** Initializes MethInfo structure. */
void init(Method_Handle meth)
{
m_method = meth;
m_klass = method_get_class(m_method);
}
/** Returns name of the method. */
const char* meth_name(void)
{
if (NULL==m_mname) {
m_mname = method_get_name(m_method);
}
return m_mname;
}
/** Returns name of method's class. */
const char* meth_kname(void)
{
if (NULL==m_kname) {
m_kname = class_get_name(m_klass);
}
return m_kname;
}
/** Returns method's signature. */
const char* meth_sig(void)
{
if (NULL==m_sig) {
m_sig = method_get_descriptor(m_method);
}
return m_sig;
}
/** Returns method fully qualified name, without signature.*/
const char* meth_fname(void)
{
if (m_fname.empty()) {
m_fname = meth_kname();
m_fname += "::";
m_fname += meth_name();
}
return m_fname.c_str();
}
/** Returns method fully qualified name, including signature.*/
const char* meth_fname_sig(void)
{
if (m_fname_sig.empty()) {
m_fname_sig = meth_fname();
m_fname_sig += meth_sig();
}
return m_fname_sig.c_str();
}
/** Returns number of exception handlers in the method. */
unsigned meth_num_handlers(void) const
{
return method_get_exc_handler_number(m_method);
}
/** Tests whether the method is synchronized. */
bool meth_is_sync(void) const
{
return method_is_synchronized(m_method);
}
/** Tests whether the method is static. */
bool meth_is_static(void) const
{
return method_is_static(m_method);
}
/**
* Tests whether the method is synchronized and not static (instance).
*/
bool meth_is_sync_inst(void) const
{
return meth_is_sync() && !meth_is_static();
}
/** Tests whether the method is constructor. */
bool meth_is_ctor(void)
{
return !strcmp(meth_name(), "<init>");
}
/** Tests whether the method is constructor of Exception. */
bool meth_is_exc_ctor(void)
{
return class_is_throwable(m_klass) && meth_is_ctor();
}
protected:
/** Method handle. */
Method_Handle m_method;
/** Class handle. */
Class_Handle m_klass;
private:
/** Class name. */
const char* m_kname;
/** Method name. */
const char* m_mname;
/** Method signature. */
const char* m_sig;
/** Method fully qualified name, without signature.*/
string m_fname;
/** Method fully qualified name, including signature.*/
string m_fname_sig;
};
/**
* @brief Info about current state of a register in BBState.
*/
struct RegInfo {
/**
* @brief Number of references.
*/
unsigned refs;
/**
* @brief Number of locks.
*/
unsigned locks;
/**
* @brief A value currently known to be loaded into the register.
*/
Val val;
};
/**
* @brief State of a basic block during code generation.
*/
class BBState {
public:
BBState()
{
clear();
m_last_fr = fr0;
m_last_gr = gr0;
}
void clear(void)
{
seen_gcpt = false;
stack_depth = NOTHING;
stack_mask = 0;
stack_mask_valid = false;
}
/**
* @brief Current state of mimic frame for the BB.
*/
JFrame jframe;
/**
* @brief State of registers.
*/
RegInfo m_regs[ar_num];
/**
* @brief Recently stored stack depth.
*
* Used to eliminate unnecessary stack depth updates.
*/
unsigned stack_depth;
/**
* @brief Recently stored GC mask for stack.
*
* Used to eliminate unnecessary stack GC map updates.
*
* Only single word of the GC mask is stored - it's enough for most
* applications.
*/
unsigned stack_mask;
/**
* @brief Last used (allocated) GR register.
*/
AR m_last_gr;
/**
* @brief Last used (allocated) FR register.
*/
AR m_last_fr;
/**
* @brief \b true if #stack_mask contains a 'valid' (that is which was
* really written).
*
* The #stack_depth field may contain limited set of values and thus
* the field itself may carry info whether it contains 'non-initialized'
* flag (#NOTHING) or the real stack depth (any other value).
*
* In opposite, for the #stack_mask any value is valid combination. Thus
* it's necessary to indicate whether the stack_mask was initialized or
* not. Here is this flag intended for.
*/
unsigned stack_mask_valid;
/**
* @brief 'true' if there was at least one GC point in the basic block.
*
* Set during the code generation and is used to reduce unnecessary back
* branch polling code.
*/
unsigned seen_gcpt;
};
}}; // ~namespace Jitrino::Jet2
#endif // ~__BBSTATE_H_INCLUDED__