blob: 1b45809183d9554780b8457ebe36a639dda87dae [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 Compiler class' main methods implementation.
*/
#include <assert.h>
#include <algorithm>
#ifdef WIN32
#include <malloc.h>
#else
#include <stdlib.h>
#endif
#include "open/vm_class_loading.h"
#include "open/vm.h"
#include "open/vm_properties.h"
#include "open/hythread_ext.h"
#include "open/vm_class_info.h"
#include "open/vm_type_access.h"
#include "open/vm_method_access.h"
#include "open/vm_ee.h"
#include "jit_import.h"
#include "jit_runtime_support.h"
#include "jit_intf.h"
#include "mkernel.h"
//FIXME: needed for NOPs fix only, to be removed
#include "enc_ia32.h"
#include "jet.h"
#include "compiler.h"
#include "trace.h"
#include "stats.h"
#include "port_threadunsafe.h"
#include <stack>
using std::stack;
#if !defined(PROJECT_JET)
#include "Jitrino.h"
#include "EMInterface.h"
#include "JITInstanceContext.h"
#endif
/**
* A lock used to protect method's data in multi-threaded compilation.
* See VMInterface.h
* CompilationInterface::lockMethodData/unlockMethodData for details.
*/
static Jitrino::Mutex g_compileLock;
#include "../main/Log.h"
using Jitrino::Log;
using Jitrino::LogStream;
#ifndef PLATFORM_POSIX
#define snprintf _snprintf
#endif
namespace Jitrino {
namespace Jet {
unsigned Compiler::defaultFlags = JMF_BBPOLLING;
unsigned Compiler::g_acceptStartID = NOTHING;
unsigned Compiler::g_acceptEndID = NOTHING;
unsigned Compiler::g_rejectStartID = NOTHING;
unsigned Compiler::g_rejectEndID = NOTHING;
/**
* Simple counter of how much times the Compiler::compile() was called.
*/
static unsigned methodsSeen = 0;
static bool isSOEHandler(Class_Handle cls) {
if (cls==NULL) return true; //-> finally block
static const char* soeHandlers[] = {"java/lang/Throwable", "java/lang/Error", "java/lang/StackOverflowError", NULL};
const char* typeName = class_get_name(cls);
for (size_t i=0;soeHandlers[i]!=NULL; i++) {
if (!strcmp(typeName, soeHandlers[i])) {
return true;
}
}
return false;
}
JIT_Result Compiler::compile(Compile_Handle ch, Method_Handle method,
const OpenMethodExecutionParams& params)
{
compilation_params = params;
/*
compilation_params.exe_restore_context_after_unwind = true;
compilation_params.exe_provide_access_to_this = true;
//vm_properties_set_value("vm.jvmti.enabled", "true");
g_jvmtiMode = true;
*/
m_compileHandle = ch;
MethInfo::init(method);
// Currently use bp-based frame
m_base = bp;
// Will be used later, with sp-based frame
m_depth = 0;
//
// Check contract with VM
//
assert(!method_is_abstract(method) &&
"VM must not try to compile abstract method!");
assert(!method_is_native(method) &&
"VM must not try to compile native method!");
UNSAFE_REGION_START
// Non-atomic increment of compiled method counter.
// May affect accuracy of JIT logging or a special debug mode
// when a user specifies which range of methods accept/reject from compilation.
// Can't affect default JET execution mode.
STATS_SET_NAME_FILER(NULL);
m_methID = ++methodsSeen;
UNSAFE_REGION_END
unsigned compile_flags = defaultFlags;
initProfilingData(&compile_flags);
//
// the latest PMF machinery seems working without much overhead,
// let's try to have tracing functionality on by default
//
#if 1 //def JET_PROTO
// Ensure no memory problems exist on entrance
if (get_bool_arg("checkmem", false)) {
dbg_check_mem();
}
const char * lp;
//
// Process args, update flags if necessary
//
if (!get_bool_arg("bbp", true)) {
compile_flags &= ~JMF_BBPOLLING;
}
m_lazy_resolution = get_bool_arg("lazyResolution", true);
#ifdef _DEBUG
bool assertOnRecursion = get_bool_arg("assertOnRecursion", false);
if (assertOnRecursion) {
assert(Jitrino::getCompilationRecursionLevel() == 1);
}
#endif
//
// Debugging support
//
lp = get_arg("log", NULL);
if (lp != NULL) {
if (NULL != strstr(lp, "ct")) {
bool ct = false;
if (NULL != strstr(lp, "sum")) {
compile_flags |= DBG_TRACE_SUMM;
ct = true;
}
static const unsigned TRACE_CG =
DBG_DUMP_BBS | DBG_TRACE_CG |
DBG_TRACE_SUMM | DBG_DUMP_CODE;
if (NULL != strstr(lp, "cg")) {
compile_flags |= TRACE_CG;
ct = true;
}
if (NULL != strstr(lp, "layout")) {
compile_flags |= DBG_TRACE_LAYOUT;
ct = true;
}
if (NULL != strstr(lp, "code")) {
compile_flags |= DBG_DUMP_CODE;
ct = true;
}
if (!ct) {
// No category means 'code+sum'
compile_flags |= DBG_DUMP_CODE|DBG_TRACE_SUMM;
}
}
if (Log::log_rt().isEnabled()) {
if (NULL != strstr(lp, "rtsupp")) {
compile_flags |= DBG_TRACE_RT;
}
if (NULL != strstr(lp, "ee")) {
compile_flags |= DBG_TRACE_EE;
}
if (NULL != strstr(lp, "bc")) {
compile_flags |= DBG_TRACE_BC;
}
}
} // ~compLS.isEnabled()
//
// Accept or reject the method ?
//
bool accept = true;
if (g_acceptStartID != NOTHING && m_methID < g_acceptStartID) {
accept = false;
}
if (g_acceptEndID != NOTHING && m_methID > g_acceptEndID) {
accept = false;
}
if (g_rejectStartID != NOTHING && m_methID >= g_rejectStartID) {
accept = false;
}
if (g_rejectEndID != NOTHING && m_methID <= g_rejectEndID) {
accept = false;
}
lp = get_arg("reject", NULL);
if (lp != NULL && isalpha(lp[0])) {
accept = !to_bool(lp);
}
if (!accept) {
if (compile_flags & DBG_TRACE_SUMM) {
//dbg_trace_comp_start();
//dbg_trace_comp_end(false, "Due to accept or reject argument.");
}
return JIT_FAILURE;
}
//
// A special way to accept/reject method - list of method in file.
// May be useful to find problematic method in multi threaded
// compilation env when id-s get changed much.
//
static bool loadList = true;
static vector<string> data;
if (loadList) {
lp = get_arg("list", NULL);
if (lp != NULL) {
FILE * f = fopen(lp, "r");
if (f != NULL) {
char buf[1024*4];
while(NULL != fgets(buf, sizeof(buf)-1, f)) {
// Skip comments
if (buf[0] == '#') continue;
int len = (int)strlen(buf);
// Trim CRLF
if (len>=1 && buf[len-1]<=' ') { buf[len-1] = 0; }
if (len>=2 && buf[len-2]<=' ') { buf[len-2] = 0; }
data.push_back(buf);
}
fclose(f);
} // if f != NULL
else {
dbg("WARN: list option - can not open '%s'.\b", lp);
}
}
loadList = false;
}
bool found = data.size() == 0;
for (unsigned i=0; i<data.size(); i++) {
const string& s = data[i];
if (!strcmp(meth_fname(), s.c_str())) {
found = true;
break;
}
}
if (!found) {
if (compile_flags & DBG_TRACE_SUMM) {
//dbg_trace_comp_start();
//dbg_trace_comp_end(false, "Not in file list.");
}
return JIT_FAILURE;
}
//
// Only emulate compilation, without registering code in the VM ?
//
m_bEmulation = get_bool_arg("emulate", false);
//
// Check stack integrity ?
//
if (get_bool_arg("checkstack", false)) {
compile_flags |= DBG_CHECK_STACK;
}
//
// Insert software breakpoint at specified place of method ?
//
dbg_break_pc = NOTHING;
lp = get_arg("brk", NULL);
if (lp == NULL) { lp = get_arg("break", NULL); };
if (lp != NULL) {
// PC specified where to insert breakpoint into ...
if (isdigit(lp[0])) {
dbg_break_pc = atoi(lp);
}
else {
// no PC specified - break at entrance.
compile_flags |= DBG_BRK;
}
}
#endif // ~JET_PROTO
if (compile_flags & DBG_TRACE_SUMM) {
dbg_trace_comp_start();
}
Encoder::m_trace = (compile_flags & DBG_TRACE_CG);
if (NULL == rt_helper_throw) {
// The very first call of ::compile(), initialize
// runtime constants - addresses of helpers, offsets, etc
initStatics();
}
m_max_native_stack_depth = 0;
m_bc = method_get_bytecode(m_method);
unsigned bc_size = (unsigned)method_get_bytecode_length(m_method);
unsigned num_locals = method_get_max_locals(m_method);
unsigned max_stack = method_get_max_stack(m_method) + NATIVE_STACK_SIZE_2_THROW_SYN_EXC;
// Input arguments
::std::vector<jtype> inargs;
get_args_info(m_method, inargs, &m_retType);
m_ci.init(CCONV_MANAGED, inargs);
unsigned num_input_slots = count_slots(inargs);
m_argSlots = num_input_slots;
m_argids.resize(num_input_slots); //[in_slots];
m_ra.resize(num_locals, ar_x);
//
m_codeStream.init((unsigned)(bc_size*NATIVE_CODE_SIZE_2_BC_SIZE_RATIO));
m_stack.init(num_locals, max_stack, num_input_slots);
// We need to report 'this' additionally for the following cases:
// - non-static sync methods - to allow VM to call monitor_exit() for
// abrupt exit
// - constructors of classes with class_is_throwable == true
// to allow correct handling of stack trace in VM (see
// stack_trace.cpp + com_openintel_drl_vm_VMStack.cpp:
// Java_com_openintel_drl_vm_VMStack_getStackState.
//
if (!meth_is_static() && (meth_is_sync() || meth_is_exc_ctor())) {
compile_flags |= JMF_REPORT_THIS;
}
// Always report 'this' if we're asked about this explicitly
if (compilation_params.exe_provide_access_to_this && !meth_is_static()) {
compile_flags |= JMF_REPORT_THIS;
}
m_infoBlock.init(bc_size, max_stack, num_locals, num_input_slots,
compile_flags);
m_infoBlock.set_compile_params(compilation_params);
bool eh_ok = comp_resolve_ehandlers();
if (!eh_ok) {
// At least on of the exception handlers classes was not resolved:
// unable to resolve class of Exception => will be unable to register
// exception handlers => can't generate code et all => stop here
// TODO - might want to [re]consider and may generate LinkageError
// and throw it at runtime.
if (is_set(DBG_TRACE_SUMM)) {
dbg_trace_comp_end(false, "ehandler.resolve");
}
m_infoBlock.release();
return JIT_FAILURE;
}
//
// Initialization done, collect statistics
//
UNSAFE_REGION_START
STATS_INC(Stats::methodsCompiled, 1);
STATS_INC(Stats::methodsWOCatchHandlers, m_handlers.size() ? 0 : 1);
//
STATS_MEASURE_MIN_MAX_VALUE(bc_size, m_infoBlock.get_bc_size(), meth_fname());
STATS_MEASURE_MIN_MAX_VALUE(jstack, max_stack, meth_fname());
STATS_MEASURE_MIN_MAX_VALUE(locals, num_locals, meth_fname());
UNSAFE_REGION_END
//
// ~Stats
//
m_insts.alloc(bc_size, true);
//
// Initialization ends,
// Phase 1 - decoding instructions, finding basic blocks.
//
comp_parse_bytecode();
comp_alloc_regs();
// Statistics:: number of basic blocks
STATS_MEASURE_MIN_MAX_VALUE(bbs, (unsigned)m_bbs.size(), meth_fname());
if (is_set(DBG_DUMP_BBS)) {
dbg_dump_bbs();
}
//
// Phase 2 - code generation.
//
SmartPtr<BBState> allStates;
allStates.alloc((unsigned)m_bbs.size());
unsigned c = 0;
for (BBMAP::iterator i=m_bbs.begin(); i != m_bbs.end(); i++, c++) {
m_bbStates[i->first] = &allStates[c];
}
// Generate the whole code - will recursively generate all the
// reachable code, except the exception handlers ...
comp_gen_code_bb(0);
// ... now, generate exception handlers ...
for (unsigned i=0; i<m_handlers.size(); i++) {
comp_gen_code_bb(m_handlers[i].handler);
if (isSOEHandler(m_handlers[i].klass)) {
hasSOEHandlers=true;
}
}
// ... and finally, generate prolog.
// Fake BB data - it's used in gen_prolog()
BBInfo bbinfo;
unsigned prolog_ipoff = bbinfo.ipoff = ipoff();
BBState bbstate;
bbstate.jframe.init(m_infoBlock.get_stack_max(),
m_infoBlock.get_num_locals());
m_bbstate = &bbstate;
m_bbinfo = &bbinfo;
m_jframe = &bbstate.jframe;
rclear();
gen_prolog();
unsigned prolog_size = ipoff() - prolog_ipoff;
if (is_set(DBG_TRACE_CG)) {
dbg_dump_code(m_codeStream.data() + prolog_ipoff,
prolog_size, "prolog");
}
//
// phase 2 end.
// Register code and related info in the VM
//
// *************
// * LOCK HERE *
// *************
g_compileLock.lock();
if (method_get_code_block_size_jit(m_method, m_hjit) != 0) {
// the code generated already
STATS_INC(Stats::methodsCompiledSeveralTimes, 1);
g_compileLock.unlock(); /* Unlock here */
m_infoBlock.release();
if (get_bool_arg("checkmem", false)) {
dbg_check_mem();
}
return JIT_SUCCESS;
}
const unsigned total_code_size = m_codeStream.size();
if (m_bEmulation) {
m_vmCode = (char*)malloc(total_code_size);
}
else {
m_vmCode = (char*)method_allocate_code_block(m_method, m_hjit,
total_code_size,
16/*fixme aligment*/,
CodeBlockHeatDefault,
0, CAA_Allocate);
m_infoBlock.set_code_start(m_vmCode);
m_infoBlock.set_code_len(total_code_size);
}
//
// Copy and reposition code from m_codeStream into the allocated buf.
//
comp_layout_code(prolog_ipoff, prolog_size);
STATS_MEASURE_MIN_MAX_VALUE(code_size, total_code_size, meth_fname());
STATS_MEASURE_MIN_MAX_VALUE(native_per_bc_ratio,
(m_infoBlock.get_bc_size() == 0) ?
0 : total_code_size/m_infoBlock.get_bc_size(),
meth_fname());
#ifdef _DEBUG
// At this point, the codeStream content is completely copied into the
// 'codeBlock', thus no usage of m_codeStream beyond this point.
memset(m_codeStream.data(), 0xCC, m_codeStream.size());
#endif
//
// runtime data. must be initialized before code patching
//
//register profiler counters mapping info if present
std::vector<U_32> profiler_counters_vec; //will automatically be deleted on exit from this method
if (!m_profileCountersMap.empty()) {
m_infoBlock.num_profiler_counters = (U_32)m_profileCountersMap.size();
profiler_counters_vec.resize(m_infoBlock.num_profiler_counters, 0);
m_infoBlock.profiler_counters_map = &profiler_counters_vec.front();
for (size_t i =0; i<m_profileCountersMap.size(); i++) {
ProfileCounterInfo& info = m_profileCountersMap[i];
U_32 offset = ProfileCounterInfo::getInstOffset(info.offsetInfo) + (info.bb->addr - m_vmCode);
U_32 offsetInfo = ProfileCounterInfo::createOffsetInfo(ProfileCounterInfo::getInstSize(info.offsetInfo), offset);
m_infoBlock.profiler_counters_map[i]=offsetInfo;
}
}
unsigned data_size = m_infoBlock.get_total_size();
char * pdata;
if (m_bEmulation) {
pdata = (char*)malloc(data_size);
}
else {
pdata = (char*)method_allocate_info_block(m_method, m_hjit,
data_size);
}
m_infoBlock.save(pdata);
//
// Finalize addresses
//
comp_patch_code();
//
// register exception handlers
//
if (m_bEmulation) {
// no op
}
else {
comp_set_ehandlers();
}
// ***************
// * UNLOCK HERE *
// ***************
g_compileLock.unlock();
STATS_MEASURE_MIN_MAX_VALUE(patchItemsToBcSizeRatioX1000,
patch_count()*1000/bc_size, meth_fname());
if (is_set(DBG_DUMP_CODE)) {
dbg_dump_code_bc(m_vmCode, total_code_size);
}
if (is_set(DBG_TRACE_SUMM)) {
dbg_trace_comp_end(true, "ok");
}
m_infoBlock.release();
// Ensure no memory problems appeared during compilation
if (get_bool_arg("checkmem", false)) {
dbg_check_mem();
}
if (m_bEmulation) {
free(m_vmCode);
free(pdata);
return JIT_FAILURE;
}
return JIT_SUCCESS;
}
BBInfo& Compiler::comp_create_bb(unsigned pc)
{
if (m_bbs.find(pc) == m_bbs.end()) {
BBInfo bbinfo;
bbinfo.start = bbinfo.last_pc = bbinfo.next_bb = pc;
m_bbs[pc] = bbinfo;
}
return m_bbs[pc];
}
void Compiler::comp_parse_bytecode(void)
{
const unsigned vars = m_infoBlock.get_num_locals();
m_defs.resize(vars, 0);
m_uses.resize(vars, 0);
// jretAddr here is used as 'not initialized yet' value
m_staticTypes.resize(vars, jretAddr);
for (unsigned i=0, var=0; i<m_ci.count(); i++, var++) {
jtype jt = m_ci.jt(i);
if (jt < i32) { jt = i32; }
bool big = is_big(jt);
m_staticTypes[var] = jt;
m_argids[var] = m_ci.reg(i) == ar_x && !big? i : NOTHING;
if (is_wide(jt)) {
++var;
m_argids[var] = m_ci.reg(i) == ar_x && !big ? i : NOTHING;
m_staticTypes[var] = jt;
}
}
// PC=0 always starts new basic block
comp_create_bb(0);
JInst& start = m_insts[0];
start.ref_count = 1;
start.flags |= OPF_STARTS_BB;
unsigned id = 1;
const unsigned bc_size = m_infoBlock.get_bc_size();
for (unsigned pc=0; pc<bc_size; ) {
JInst& jinst = m_insts[pc];
pc = fetch(pc, jinst);
if (jinst.id != 0) {
continue;
}
jinst.id = id++;
if (jinst.flags & OPF_VAR_DU_MASK) {
// extract variable index
unsigned idx =
(jinst.flags&OPF_VAR_IDX_MASK) == OPF_VAR_OP0 ?
jinst.op0 : jinst.flags&OPF_VAR_IDX_MASK;
assert(idx<vars);
// extract variable type
jtype jt =
(jtype)((jinst.flags&OPF_VAR_TYPE_MASK)>>OPF_VAR_TYPE_SHIFT);
// update def-use info
if (jinst.flags & OPF_VAR_DEF) {
++m_defs[idx];
if (is_big(jt)) {
++m_defs[idx+1];
}
}
if (jinst.flags & OPF_VAR_USE) {
++m_uses[idx];
if (is_big(jt)) {
++m_uses[idx+1];
}
}
// Store a slot's 'static' type - if a slot is used only as
// one type (say, only i32). If the slot is used to keep various
// types (i.e. we have both ASTORE_1 and DSTORE_1 - then store
// 'jvoid' as 'static' type.
if (m_staticTypes[idx] == jretAddr || m_staticTypes[idx] == jt) {
m_staticTypes[idx] = jt;
if (is_wide(jt)) {
jtype jt_hi = m_staticTypes[idx+1];
if (jt_hi == jretAddr || jt_hi == jt) {
m_staticTypes[idx+1] = jt;
}
else {
m_staticTypes[idx+1] = jvoid;
m_staticTypes[idx] = jvoid;
}
}
}
else {
m_staticTypes[idx] = jvoid;
if (is_wide(jt)) {
m_staticTypes[idx+1] = jvoid;
}
}
}
if (jinst.flags & OPF_STARTS_BB) {
comp_create_bb(jinst.pc);
}
//JInst& next = m_insts[pc];
if (!(jinst.flags&OPF_DEAD_END)) {
if (pc<bc_size) { ++m_insts[pc].ref_count; }
}
if (!(jinst.flags & OPF_ENDS_BB)) {
continue;
}
if (pc<bc_size) {
m_insts[pc].flags |= OPF_STARTS_BB;
comp_create_bb(pc);
}
for (unsigned i=0, n = jinst.get_num_targets(); i<n; i++) {
// mark jmp target(s)
JInst& ji = m_insts[jinst.get_target(i)];
ji.flags |= OPF_STARTS_BB;
++ji.ref_count;
BBInfo& nbb = comp_create_bb(jinst.get_target(i));
nbb.jsr_target = nbb.jsr_target || jinst.is_jsr();
}
if (jinst.is_switch()) {
JInst& ji = m_insts[jinst.get_def_target()];
ji.flags |= OPF_STARTS_BB;
++ji.ref_count;
comp_create_bb(jinst.get_def_target());
}
} // ~for
//
// mark exception handlers as leads of basic blocks
for (unsigned i=0; i<m_handlers.size(); i++) {
const HandlerInfo& hi = m_handlers[i];
JInst& ji = m_insts[hi.handler];
ji.flags |= OPF_STARTS_BB;
comp_create_bb(hi.handler).ehandler = true;
++ji.ref_count;
if (ji.id == 0) {
ji.id = id++;
}
}
// Sometimes, Eclipse's javac generates NOPs at the end of method AND in a basic
// block which is [unreachable] exception handler. In this case we have an inst which
// is last in the method, has fall-through, but no following block. Always set up 'OPF_DEAD_END':
JInst& lastInst = m_insts[bc_size-1];
lastInst.flags = lastInst.flags | OPF_DEAD_END;
}
void Compiler::comp_alloc_regs(void)
{
if (g_jvmtiMode) {
// Do not allocate regs. Only ensure the m_base will be saved.
m_global_rusage.set(ar_idx(m_base));
return;
}
const unsigned vars = m_infoBlock.get_num_locals();
// 1. allocate GP registers
for (unsigned i=0; i<g_global_grs.size(); i++) {
AR ar = g_global_grs[i];
assert(is_callee_save(ar));
unsigned max_dus = 0;
unsigned idx = NOTHING;
for (unsigned j=0; j<vars; j++) {
jtype jt = m_staticTypes[j];
bool accept = jt==i32 || (jt==i64 && !is_big(i64)) || jt==jobj;
if (!accept) continue;
if (m_ra[j] != ar_x) continue;
if (m_defs[j]+m_uses[j]>max_dus) {
max_dus = m_defs[j]+m_uses[j];
idx = j;
}
}
if (idx == NOTHING) {
// no more vars remain
break;
}
m_ra[idx] = ar;
}
// 2. allocate FP registers
for (unsigned i=0; i<g_global_frs.size(); i++) {
AR ar = g_global_frs[i];
unsigned max_dus = 0;
unsigned idx = NOTHING;
for (unsigned j=0; j<vars; j++) {
jtype jt = m_staticTypes[j];
if (!is_f(jt)) continue;
if (m_ra[j] != ar_x) continue;
if (m_defs[j]+m_uses[j]>max_dus) {
max_dus = m_defs[j]+m_uses[j];
idx = j;
}
}
if (idx == NOTHING) {
// no more vars remain
break;
}
m_ra[idx] = ar;
}
// Set a bitset of globally used registers
// m_base is always used.
m_global_rusage.set(ar_idx(m_base));
for (unsigned i=0; i<m_ra.size(); i++) {
AR ar = m_ra[i];
if (ar == ar_x) { continue; }
m_global_rusage.set(ar_idx(ar));
}
// Print out
if (is_set(DBG_TRACE_CG)) {
for (unsigned i=0; i<vars; i++) {
jtype jt = m_staticTypes[i];
// common info on var - type (if any), uses, defs ...
dbg("local#%d (%s): d=%u, u=%u",
i, jt < jvoid ? jtypes[jt].name : "",
m_defs[i], m_uses[i]);
// ... which input arg it came from ...
if (i<m_argSlots && m_argids[i] != -1) {
dbg(" <= arg#%d", m_argids[i]);
}
// ... which register it's assigned on ...
if (m_ra[i] != ar_x) {
dbg(" => %s", to_str(m_ra[i]).c_str());
}
dbg("\n");
}
}
}
/**
* A helper structure to organize recursive compilation.
*/
struct GenItem {
/**
* No op.
*/
GenItem() {}
/**
* @param _pc - start of basic block to generate
* @param _parent - start of predecessor block (to inherit BBState
* from) or equal to _pc if no predecessor
* @param _jsr - PC of start of JSR subroutine the \c _pc block belongs
* to, or #NOTHING if the block lies outside of JSR subroutines.
*/
GenItem(unsigned _pc, unsigned _parent, unsigned _jsr)
{
pc = _pc; parentPC = _parent; jsr_lead = _jsr;
}
/** start of basic block to generate */
unsigned pc;
/** start of predecessor block */
unsigned parentPC;
/** start of JSR subroutine the block belongs to*/
unsigned jsr_lead;
};
void Compiler::comp_gen_code_bb(unsigned pc)
{
///////////////////////////////////////////////////////////////////////////
/*
Ok, here is a bit sophisticated code, but the idea is simple: we
emulate recursion, without doing real recursive calls of method.
We generate code in depth-first search order. Banal recursion is the
most natural, simple and clear way to do so:
//
generate_all_insts_in_bb(head);
foreach target in targets_of_last_instrution {
generate_all_insts_in_bb(target)
}
if (last_inst_has_fall_through) {
generate_all_insts_in_bb(last_inst.next)
}
//
Now, let's count. The max bytecode size is 65535, including RETURN at
the end. GOTO instruction takes 3 bytes, so the hypothetical max number
of basic blocks is (65'535-1(return))/3 = 21'844 which is also maximum
recursion depth for simplest implementation. The default maximum size
of thread's stack is 1 MB (at least on Win32). Having all of this in
mind: 1024*1024/21'844 = 48 bytes we can only spend in comp_gen_code()
for local variables, spilled regs, passed args, etc. In addition, in
real life there are many calls before comp_gen_code() and comp_gen_code()
also do some calls - they all require stack space - and we're in
trouble with the direct approach as we may easily exhaust stack on
a legal and valid bytecode.
So, here is what we do to avoid this: we emulate recursion, using
heap-based stack structure.
*/
stack<GenItem> stk;
stk.push(GenItem(pc, pc, NOTHING));
while (true) {
if (stk.empty()) {
break;
}
GenItem gi = stk.top(); stk.pop();
unsigned head = gi.pc;
//
if (!comp_gen_insts(gi.pc, gi.parentPC, gi.jsr_lead)) {
// Basic block was already seen, nothing to do.
continue;
}
const BBInfo& bbi = m_bbs[head];
const JInst& lastInst = m_insts[bbi.last_pc];
// Push fall-through block first, so it's processed at the very end
if (!lastInst.is_set(OPF_DEAD_END)) {
unsigned next = lastInst.next;
// If last inst was JSR, then when generating fall through,
// specify the JSR leader as 'parent PC', so the BBState
// to inherit will be taken from the JSR's one. This also
// processed in a special way in comp_gen_insts() - when we see
// that a block's parent is JSR lead, we take the state from
// m_jsrStates, rather than from the m_bbStates.
unsigned h = lastInst.is_jsr() ? lastInst.get_target(0): head;
if (lastInst.single_suc() && !m_bbs[next].processed) {
// If instruction has only one successor, so its BBState
// will be used once for inheritance. May eliminate
// copying and in operate directly on this BBState when
// generating next block.
m_bbStates[next] = m_bbStates[head];
}
stk.push(GenItem(next, h, gi.jsr_lead));
}
// Then, process target blocks - this is match with how code gen
// for branches works - it expects to visit branch target first,
// and then fall through
//
for (unsigned i=0; !lastInst.is_jsr() &&
i<lastInst.get_num_targets(); i++) {
unsigned next = lastInst.get_target(i);
if (lastInst.single_suc() && !m_bbs[next].processed) {
m_bbStates[next] = m_bbStates[head];
}
stk.push(GenItem(next, head, gi.jsr_lead));
}
if (lastInst.is_switch()) {
unsigned next = lastInst.get_def_target();
stk.push(GenItem(next, head, gi.jsr_lead));
}
// at the very end push JSR target, so JSR subroutine get processed
// first
if (lastInst.is_jsr()) {
unsigned jsr_lead = lastInst.get_target(0);
stk.push(GenItem(jsr_lead, head, jsr_lead));
}
}
}
bool Compiler::comp_gen_insts(unsigned pc, unsigned parentPC,
unsigned jsr_lead)
{
assert(m_bbs.find(pc) != m_bbs.end());
BBInfo& bbinfo = m_bbs[pc];
if (bbinfo.processed) {
if (bbinfo.jsr_target) {
// we're processing JSR subroutine
if (jsr_lead != NOTHING) {
assert(jsr_lead == pc);
// Simply load the state back to the parent's
BBState* prevState = m_bbStates[parentPC];
// assert(m_jsrStates.find(jsr_lead) != m_jsrStates.end());
if(m_jsrStates.find(jsr_lead) == m_jsrStates.end()) {
// There can be a specific testcase with jsr without respective ret
// In this case we can try to continue if there is a bb_State for jsr_lead
// This is a temporary solution. HARMONY-4740 is devoted to the complete one.
assert(m_bbStates.find(jsr_lead) != m_bbStates.end());
} else {
const BBState* jsrState = m_jsrStates[jsr_lead];
//prevState.jframe.init(&jsrState.jframe);
*prevState = *jsrState;
}
}
else {
// we have a fall through (and not through a JSR) path
// to a subroutine
// do nothing here - we only need to return the state
// back for JSR
}
}
return false;
}
BBState* pState = m_bbStates[pc];
BBState * parentState;
{
const BBInfo& parentBB = m_bbs[parentPC];
// If we see that parent block was a JSR subroutine, this may mean
// that the parent block ended with a JSR call, and then
// 'parentPC' of this block was substituted (see the appropriate
// code in comp_gen_code_bb()).
// So, in this case we must use the state after the JSR subroutine.
// The 'jsr_lead != parentPC' prevents from taking state from m_jsrStates
// when the parentPC is the real parent, that is in a JSR subroutine
// with several blocks.
if (parentBB.jsr_target && jsr_lead != parentPC && jsr_lead != pc) {
// There can be a specific testcase with jsr without respective ret
// In this case we can try to continue if there is a bb_State for parentPC
// This is a temporary solution. HARMONY-4740 is devoted to the complete one.
// assert(m_jsrStates.find(parentPC) != m_jsrStates.end());
if(m_jsrStates.find(parentPC) != m_jsrStates.end()) {
parentState = m_jsrStates[parentPC];
} else {
parentState = m_bbStates[parentPC];
}
}
else {
parentState = m_bbStates[parentPC];
}
}
JInst& bbhead = m_insts[pc];
if (pc != parentPC) {
if (pState != parentState) {
if (bbhead.ref_count > 1) {
pState->jframe = parentState->jframe;
}
else {
*pState = *parentState;
}
}
}
else {
pState->jframe.init(m_infoBlock.get_stack_max(),
m_infoBlock.get_num_locals());
}
BBState& bbstate = *pState;
bbinfo.processed = true;
m_jframe = &bbstate.jframe;
m_bbstate = &bbstate;
m_bbinfo = &bbinfo;
m_pc = pc;
unsigned bb_ip_start = bbinfo.ipoff = m_codeStream.ipoff();
//
// If there are several execution paths merged on this basic block,
// then we can not predict many things, including, but not limited to
// type of of local variables, state of stack items, what stack depth
// was saved last, etc. So, clearing all this stuff out.
//
if (bbinfo.ehandler || bbhead.ref_count > 1) {
bbstate.clear();
}
if (is_set(DBG_CHECK_STACK)){
gen_dbg_check_bb_stack();
}
if (bbinfo.ehandler && pc == parentPC) {
// This is a 'normal' handler, unreached via fall-through so far
// (otherwise parent bb must leave stack in consistent state).
// Here, we invoke gen_save_ret() because this is how the idea of
// exception handlers works in DRL VM:
// Loosely speaking, calling 'throw_<whatever>' is like a regular
// function call. The only difference is that the return point is
// at another address, not at the next instruction - we're
// 'returning' to the proper exception handler.
//
// That's why the exception object acts like a return value - for
// example on IA32 it's in EAX.
//
SYNC_FIRST(static const CallSig cs(CCONV_MANAGED, jobj));
gen_save_ret(cs);
}
if (is_set(DBG_TRACE_CG)) {
dbg("\n");
dbg(";; ======================================================\n");
dbg(";; bb.ref.count=%d%s%s savedStackDepth=%d stackMask=0x%X %s"
" jsr_lead=%d\n",
bbhead.ref_count, bbinfo.ehandler ? " ehandler " : "",
bbinfo.jsr_target ? " #JSR# " : "",
bbstate.stack_depth, bbstate.stack_mask,
bbstate.stack_mask_valid ? "" : "*mask.invalid*", jsr_lead);
if (bb_ip_start != m_codeStream.ipoff()) {
dbg_dump_code(m_codeStream.data() + bb_ip_start,
m_codeStream.ipoff()-bb_ip_start, "bb.head");
}
dbg(";; ======================================================\n");
}
gen_bb_enter();
#ifdef _DEBUG
vcheck();
#endif
const unsigned bc_size = m_infoBlock.get_bc_size();
unsigned next_pc = bbinfo.start;
bool last = false;
do {
// read out instruction to process
m_pc = next_pc;
m_curr_inst = &m_insts[m_pc];
next_pc = m_insts[m_pc].next;
last = next_pc>=bc_size || (m_insts[next_pc].is_set(OPF_STARTS_BB));
if (last) {
bbinfo.last_pc = m_pc;
bbinfo.next_bb = next_pc;
}
unsigned inst_code_start = m_codeStream.ipoff();
unsigned inst_code_dump_start = inst_code_start;
if (is_set(DBG_TRACE_CG)) {
dbg_dump_state("before", &bbstate);
// print an opcode
dbg(";; %-30s\n", toStr(m_insts[m_pc], true).c_str());
}
if (is_set(DBG_TRACE_BC)) {
gen_dbg_rt(true, "//%s@%u", meth_fname(), m_pc);
inst_code_dump_start = m_codeStream.ipoff();
}
#ifdef JET_PROTO
if (dbg_break_pc == m_pc) {
gen_brk();
}
#endif
STATS_INC(Stats::opcodesSeen[m_insts[m_pc].opcode], 1);
handle_inst();
#ifdef _DEBUG
vcheck();
#endif
unsigned inst_code_end = m_codeStream.ipoff();
if (g_jvmtiMode && (inst_code_end == inst_code_dump_start)) {
// XXX, FIXME: quick fix for JVMTI testing:
// if bytecode did not produce any native code, then add a fake
// NOP, so every BC instruction has its own separate native
// address
ip(EncoderBase::nops(ip(), 1));
inst_code_end = m_codeStream.ipoff();
}
unsigned inst_code_dump_size = inst_code_end - inst_code_dump_start;
unsigned bb_off = inst_code_start - bb_ip_start;
// store a native offset inside the basic block for now,
// this will be adjusted in comp_layout_code(), by adding the BB's
// start address
for (unsigned i=m_pc; i<next_pc; i++) {
m_infoBlock.set_code_info(i, (const char *)(int_ptr)bb_off);
}
if (m_infoBlock.get_flags() & DBG_TRACE_CG) {
// disassemble the code
dbg_dump_code(m_codeStream.data() + inst_code_dump_start,
inst_code_dump_size, NULL);
}
// no one should change m_pc, it's used right after the loop to get
// the same JInst back again
//assert(jinst.pc == m_pc);
} while(!last);
bbinfo.last_pc = m_pc;
bbinfo.next_bb = next_pc;
const JInst& jinst = m_insts[m_pc];
unsigned bb_code_end = m_codeStream.ipoff();
bbinfo.code_size = bb_code_end - bb_ip_start;
//
// We just finished JSR subroutine - store its state for further use
//
if (jinst.opcode == OPCODE_RET) {
assert(jsr_lead != NOTHING);
assert(m_insts[jsr_lead].ref_count>0);
assert(m_bbs[jsr_lead].processed);
m_jsrStates[jsr_lead] = pState;
}
else if (jsr_lead != NOTHING &&
(jinst.opcode == OPCODE_ATHROW || jinst.is_set(OPF_RETURN))) {
assert(m_insts[jsr_lead].ref_count>0);
assert(m_bbs[jsr_lead].processed);
m_jsrStates[jsr_lead] = pState;
}
return true;
}
void Compiler::comp_layout_code(unsigned prolog_ipoff, unsigned prolog_size)
{
char * p = m_vmCode;
// Copy prolog
memcpy(p, m_codeStream.data() + prolog_ipoff, prolog_size);
p += prolog_size;
// copy all BBs
unsigned bc_size = m_infoBlock.get_bc_size();
for (unsigned pc = 0; pc<bc_size; ) {
// it's basic block lead.
assert(m_bbs.find(pc) != m_bbs.end());
BBInfo& bbinfo = m_bbs[pc];
const char * pBBStart = p;
if (bbinfo.processed) {
// copy the code.
memcpy(p, m_codeStream.data()+bbinfo.ipoff, bbinfo.code_size);
bbinfo.addr = p;
if (m_infoBlock.get_flags() & DBG_TRACE_LAYOUT) {
dbg("code.range: [%u - %u] => [%p - %p)\n",
bbinfo.start, bbinfo.last_pc,
pBBStart, pBBStart+bbinfo.code_size);
}
}
else {
if (m_infoBlock.get_flags() & DBG_TRACE_LAYOUT) {
dbg("warning - dead code @ %d\n", pc);
}
assert(bbinfo.code_size == 0);
unsigned j=pc+1;
for (; j<bc_size; j++) {
if (m_insts[j].flags & OPF_STARTS_BB) {
break;
}
}
bbinfo.next_bb = j;
}
// update runtime info with the mapping pc/ip data
for (unsigned j=pc; j<bbinfo.next_bb; j++) {
const char * inst_ip = pBBStart +
(int)(int_ptr)m_infoBlock.get_code_info(j);
m_infoBlock.set_code_info(j, inst_ip);
}
p = (char*)(pBBStart+bbinfo.code_size);
pc = bbinfo.next_bb;
}
}
bool Compiler::comp_resolve_ehandlers(void)
{
unsigned num_handlers = method_get_exc_handler_number(m_method);
m_handlers.resize(num_handlers);
bool eh_ok = true;
for (unsigned i=0; i<num_handlers; i++) {
unsigned short regStart, regEnd, handlerStart, klassType;
method_get_exc_handler_info(m_method, i,
&regStart, &regEnd, &handlerStart,
&klassType);
HandlerInfo& hi = m_handlers[i];
hi.start = regStart;
hi.end = regEnd;
hi.handler = handlerStart;
hi.type = klassType;
if (hi.type != 0) {
hi.klass = resolve_class(m_compileHandle, m_klass, hi.type);
eh_ok = eh_ok && (hi.klass != NULL);
}
if (m_infoBlock.get_flags() & DBG_DUMP_BBS) {
dbg("handler: [%-02d,%-02d] => %-02d\n",
hi.start, hi.end, hi.handler);
}
}
return eh_ok;
}
void Compiler::comp_set_ehandlers(void)
{
unsigned real_num_handlers = 0;
for (unsigned i=0, n=(unsigned)m_handlers.size(); i<n; i++) {
HandlerInfo& hi = m_handlers[i];
if (comp_hi_to_native(hi)) {
++real_num_handlers;
}
else {
if (m_infoBlock.get_flags() & DBG_TRACE_LAYOUT) {
dbg("warning: unreachable handler [%d;%d)=>%d\n",
hi.start, hi.end, hi.handler);
}
}
}
method_set_num_target_handlers(m_method, m_hjit, real_num_handlers);
for (unsigned i=0, handlerID=0, n=(unsigned)m_handlers.size(); i<n; i++) {
const HandlerInfo& hi = m_handlers[i];
if (hi.handler_ip == NULL) {
continue;
}
// The last param is 'is_exception_object_dead'
// In many cases, when an exception handler does not use an
// exception object, the fist bytecode instruction is 'pop' to
// throw the exception object away. Thus, this can used as a quick,
// cheap but good check whether the 'exception_object_is_dead'.
method_set_target_handler_info(m_method, m_hjit, handlerID,
hi.start_ip, hi.end_ip, hi.handler_ip,
hi.klass,
m_bc[hi.handler] == OPCODE_POP);
++handlerID;
if (m_infoBlock.get_flags() & DBG_TRACE_LAYOUT) {
dbg("native.handler: [%08X,%08X] => %08X"
" ([%-02d,%-02d] => %-02d)\n",
hi.start_ip, hi.end_ip, hi.handler_ip, hi.start, hi.end,
hi.handler);
}
}
}
void Compiler::get_args_info(bool is_static, unsigned cp_idx,
::std::vector<jtype>& args, jtype * retType)
{
const char* cpentry =
class_cp_get_entry_descriptor(m_klass, (unsigned short)cp_idx);
// expecting an empty vector
assert(args.size() == 0);
if (!is_static) {
// all but static methods has 'this' as first argument
args.push_back(jobj);
}
if (!cpentry) {
assert(false);
*retType = jvoid;
return;
}
// skip '('
const char *p = cpentry + 1;
//
// The presumption (cast of '*p' to VM_Data_Type) below is based on the
// VM_Data_Type values - they are equal to the appropriate characters:
// i.e. VM_Data_Type's long is 'J' - exactly as it's used in methods'
// signatures
//
for (; *p != ')'; p++) {
jtype jt = to_jtype((VM_Data_Type)*p);
if (jt == jvoid) {
break;
}
if (jt==jobj) {
// skip multi-dimension array
while(*p == '[') { p++; }
// skip the full class name
if (*p == 'L') {
while(*p != ';') { p++; }
}
}
args.push_back(jt);
}
p++;
*retType = to_jtype((VM_Data_Type)*p);
}
void Compiler::get_args_info(Method_Handle meth, ::std::vector<jtype>& args,
jtype * retType)
{
Method_Signature_Handle hsig = method_get_signature(meth);
unsigned num_params = method_args_get_number(hsig);
if (num_params) {
args.resize(num_params);
}
for (unsigned i=0; i<num_params; i++) {
Type_Info_Handle th = method_args_get_type_info(hsig, i);
assert(!type_info_is_void(th));
args[i] = to_jtype(th);
}
*retType = to_jtype(method_ret_type_get_type_info(hsig));
}
jtype Compiler::to_jtype(Type_Info_Handle th)
{
if (type_info_is_void(th)) {
return jvoid;
}
if (type_info_is_primitive(th)) {
Class_Handle ch = type_info_get_class(th);
assert(class_is_primitive(ch));
VM_Data_Type vmdt = class_get_primitive_type_of_class(ch);
return to_jtype(vmdt);
}
assert(type_info_is_reference(th) || type_info_is_vector(th));
return jobj;
}
bool Compiler::comp_hi_to_native(HandlerInfo& hi)
{
// Find beginning of the area protected by the handler,
// lookup for the first reachable instruction
unsigned pc = hi.start;
for (; pc < hi.end; pc++) {
if ((hi.start_ip = (char*)m_infoBlock.get_ip(pc)) != NULL) break;
}
hi.end_ip = (char*)m_infoBlock.get_ip(hi.end);
// either both are NULLs, or both are not NULLs
assert(!((hi.start_ip == NULL) ^ (hi.end_ip == NULL)));
if (hi.start_ip != NULL) { // which also implies '&& end_ip != NULL'
// - see assert() above
// we must have the handler
assert(m_infoBlock.get_ip(hi.handler) != NULL);
const BBInfo& handlerBB = m_bbs[hi.handler];
assert(handlerBB.ehandler);
hi.handler_ip = handlerBB.addr;
}
else {
hi.handler_ip = NULL;
}
return hi.start_ip != NULL;
}
void Compiler::comp_patch_code(void)
{
for (void * h=enum_start(); !enum_is_end(h); enum_next(h)) {
unsigned udata, bb, pid;
bool done;
enum_patch_data(&pid, &udata, &bb, &done);
if (done) {
continue;
}
unsigned inst_ipoff = pid;
const BBInfo& bbinfo = m_bbs[bb];
assert(bbinfo.processed);
void * target;
if (udata & DATA_SWITCH_TABLE) {
assert(DATA_SWITCH_TABLE==(udata&0xFFFF0000));
unsigned pc = udata&0xFFFF;
assert(bbinfo.start<=pc && pc<=bbinfo.last_pc);
const JInst& jinst = m_insts[pc];
assert(jinst.opcode == OPCODE_TABLESWITCH);
unsigned targets = jinst.get_num_targets();
char * theTable;
if (m_bEmulation) {
theTable = (char*)malloc(targets * sizeof(void*));
}
else {
theTable = (char*)method_allocate_data_block(
m_method, m_hjit,
targets * sizeof(void*), 16);
}
char ** ptargets = (char**)theTable;
// Fill out the table with targets
for (unsigned j=0; j<targets; j++, ptargets++) {
unsigned pc = jinst.get_target(j);
*ptargets = (char*)m_infoBlock.get_ip(pc);
}
target = theTable;
if (m_bEmulation) {
free(theTable);
}
}
else {
target = (void*)m_infoBlock.get_ip(udata);
}
assert(target != NULL);
inst_ipoff -= bbinfo.ipoff;
void * inst_addr = bbinfo.addr + inst_ipoff;
assert(m_infoBlock.get_pc((char*)inst_addr) < m_infoBlock.get_bc_size());
patch(pid, inst_addr, target);
}
}
void Compiler::initStatics(void)
{
// must only be called once
assert(NULL == rt_helper_throw);
Encoder::init();
// Fill out lists of registers for global allocation
// ... for GP registers, always using callee-save
for (unsigned i=0; i<gr_num; i++) {
AR gr = _gr(i);
if (!is_callee_save(gr)) { continue; };
// Do not add bp
// TODO: need to synchronize somehow with JFM_SP_FRAME
if (gr == bp) continue;
g_global_grs.push_back(gr);
}
// On a platform with float-point args passed on registers, we need at
// least one scratch register in addition to the regs occupied by the args.
// For example: when all arg regs are occupied and we need to perform mem-mem
// move e.g. field from operand stack into a memory stack.
// When no float-point args are passed on regs, we need at least 3 scratch
// to perform computations and mem-mem moves in codegen.
const unsigned num_of_scratch = MAX_FR_ARGS<3 ? 3 : (MAX_FR_ARGS+1);
for (unsigned i=num_of_scratch; i<fr_num; i++) {
AR fr = _fr(i);
g_global_frs.push_back(fr);
}
//
// Collect addresses of runtime helpers
//
rt_helper_init_class =
(char*)vm_helper_get_addr(VM_RT_INITIALIZE_CLASS);
rt_helper_ldc_string =
(char*)vm_helper_get_addr(VM_RT_LDC_STRING);
rt_helper_new =
(char*)vm_helper_get_addr(VM_RT_NEW_RESOLVED_USING_VTABLE_AND_SIZE);
g_refs_squeeze = vm_is_heap_compressed();
g_vtbl_squeeze = vm_is_vtable_compressed();
OBJ_BASE = (const char*)vm_get_heap_base_address();
VTBL_BASE = (const char*)vm_get_vtable_base_address();
NULL_REF = g_refs_squeeze ? OBJ_BASE : NULL;
g_jvmtiMode = (bool)vm_property_get_boolean("vm.jvmti.enabled", false, VM_PROPERTIES);
rt_helper_monitor_enter =
(char*)vm_helper_get_addr(VM_RT_MONITOR_ENTER);
rt_helper_monitor_exit =
(char*)vm_helper_get_addr(VM_RT_MONITOR_EXIT);
rt_helper_class_2_jlc =
(char*)vm_helper_get_addr(VM_RT_CLASS_2_JLC);
rt_helper_new_array =
(char*)vm_helper_get_addr(VM_RT_NEW_VECTOR_USING_VTABLE);
rt_helper_aastore = (char*)vm_helper_get_addr(VM_RT_AASTORE);
rt_helper_get_vtable =
(char*)vm_helper_get_addr(VM_RT_GET_INTERFACE_VTABLE_VER0);
rt_helper_throw =
(char*)vm_helper_get_addr(VM_RT_THROW);
rt_helper_throw_lazy =
(char*)vm_helper_get_addr(VM_RT_THROW_LAZY);
rt_helper_throw_linking_exc =
(char*)vm_helper_get_addr(VM_RT_THROW_LINKING_EXCEPTION);
rt_helper_checkcast =
(char*)vm_helper_get_addr(VM_RT_CHECKCAST);
rt_helper_instanceof =
(char*)vm_helper_get_addr(VM_RT_INSTANCEOF);
rt_helper_multinewarray =
(char*)vm_helper_get_addr(VM_RT_MULTIANEWARRAY_RESOLVED);
rt_helper_gc_safepoint =
(char*)vm_helper_get_addr(VM_RT_GC_SAFE_POINT);
rt_helper_get_tls_base_ptr=
(char*)vm_helper_get_addr(VM_RT_GC_GET_TLS_BASE);
rt_helper_new_withresolve =
(char*)vm_helper_get_addr(VM_RT_NEWOBJ_WITHRESOLVE);
rt_helper_new_array_withresolve =
(char*)vm_helper_get_addr(VM_RT_NEWARRAY_WITHRESOLVE);
rt_helper_get_class_withresolve =
(char*)vm_helper_get_addr(VM_RT_INITIALIZE_CLASS_WITHRESOLVE);
rt_helper_checkcast_withresolve =
(char*)vm_helper_get_addr(VM_RT_CHECKCAST_WITHRESOLVE);
rt_helper_instanceof_withresolve =
(char*)vm_helper_get_addr(VM_RT_INSTANCEOF_WITHRESOLVE);
rt_helper_field_get_offset_withresolve =
(char*)vm_helper_get_addr(VM_RT_GET_NONSTATIC_FIELD_OFFSET_WITHRESOLVE);
rt_helper_field_get_address_withresolve =
(char*)vm_helper_get_addr(VM_RT_GET_STATIC_FIELD_ADDR_WITHRESOLVE);
rt_helper_get_invokevirtual_addr_withresolve =
(char*)vm_helper_get_addr(VM_RT_GET_INVOKEVIRTUAL_ADDR_WITHRESOLVE);
rt_helper_get_invokespecial_addr_withresolve =
(char*)vm_helper_get_addr(VM_RT_GET_INVOKE_SPECIAL_ADDR_WITHRESOLVE);
rt_helper_get_invokestatic_addr_withresolve =
(char*)vm_helper_get_addr(VM_RT_GET_INVOKESTATIC_ADDR_WITHRESOLVE);
rt_helper_get_invokeinterface_addr_withresolve =
(char*)vm_helper_get_addr(VM_RT_GET_INVOKEINTERFACE_ADDR_WITHRESOLVE);
//
rt_helper_ti_method_enter =
(char*)vm_helper_get_addr(VM_RT_JVMTI_METHOD_ENTER_CALLBACK);
rt_helper_ti_method_exit =
(char*)vm_helper_get_addr(VM_RT_JVMTI_METHOD_EXIT_CALLBACK);
rt_helper_ti_field_access =
(char*)vm_helper_get_addr(VM_RT_JVMTI_FIELD_ACCESS_CALLBACK);
rt_helper_ti_field_modification =
(char*)vm_helper_get_addr(VM_RT_JVMTI_FIELD_MODIFICATION_CALLBACK);;
//
// Collect runtime constants
//
rt_array_length_offset = vector_length_offset();
rt_suspend_req_flag_offset = (unsigned)hythread_tls_get_request_offset();
rt_method_entry_flag_address = get_method_entry_flag_address();
rt_method_exit_flag_address = get_method_exit_flag_address();
rt_vtable_offset = object_get_vtable_offset();
Class_Handle clss;
clss = class_get_class_of_primitive_type(VM_DATA_TYPE_INT8);
jtypes[i8].rt_offset = vector_first_element_offset_unboxed(clss);
clss = class_get_class_of_primitive_type(VM_DATA_TYPE_INT16);
jtypes[i16].rt_offset= vector_first_element_offset_unboxed(clss);
clss = class_get_class_of_primitive_type(VM_DATA_TYPE_CHAR);
jtypes[u16].rt_offset= vector_first_element_offset_unboxed(clss);
clss = class_get_class_of_primitive_type(VM_DATA_TYPE_INT32);
jtypes[i32].rt_offset= vector_first_element_offset_unboxed(clss);
clss = class_get_class_of_primitive_type(VM_DATA_TYPE_INT64);
jtypes[i64].rt_offset= vector_first_element_offset_unboxed(clss);
clss = class_get_class_of_primitive_type(VM_DATA_TYPE_F4);
jtypes[flt32].rt_offset= vector_first_element_offset_unboxed(clss);
clss = class_get_class_of_primitive_type(VM_DATA_TYPE_F8);
jtypes[dbl64].rt_offset= vector_first_element_offset_unboxed(clss);
jtypes[jobj].rt_offset= vector_first_element_offset(VM_DATA_TYPE_CLASS);
jtypes[jvoid].rt_offset = 0xFFFFFFFF;
}
void Compiler::initProfilingData(unsigned * pflags)
{
m_p_methentry_counter = NULL;
m_p_backedge_counter = NULL;
#if !defined(PROJECT_JET)
JITInstanceContext* jitContext = JITInstanceContext::getContextForJIT(m_hjit);
ProfilingInterface* pi = jitContext->getProfilingInterface();
if (pi->isProfilingEnabled(ProfileType_EntryBackedge, JITProfilingRole_GEN)) {
MemoryManager mm("jet_profiling_mm");
MethodDesc md(m_method, m_hjit);
g_compileLock.lock();
EntryBackedgeMethodProfile* mp =
(EntryBackedgeMethodProfile*)pi->getMethodProfile(mm,
ProfileType_EntryBackedge, md, JITProfilingRole_GEN);
if (mp == NULL) {
mp = pi->createEBMethodProfile(mm, md);
}
*pflags |= JMF_PROF_ENTRY_BE;
m_p_methentry_counter = mp->getEntryCounter();
m_p_backedge_counter = mp->getBackedgeCounter();
if (pi->isEBProfilerInSyncMode()) {
*pflags |= JMF_PROF_SYNC_CHECK;
m_methentry_threshold = pi->getMethodEntryThreshold();
m_backedge_threshold = pi->getBackedgeThreshold();
m_profile_handle = mp->getHandle();
m_recomp_handler_ptr = (void*)pi->getEBProfilerSyncModeCallback();
}
g_compileLock.unlock();
}
#endif
}
}}; // ~namespace Jitrino::Jet