blob: abe747fe9718f75475e39c089765722afd31550d [file]
/*
* 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 V. Astapchuk
*/
#include "compiler.h"
#include "trace.h"
#include "stats.h"
#include "open/vm_class_info.h"
#include "open/vm_class_loading.h"
#include "jit_import.h"
//#include "jit_intf.h"
#include "open/vm_ee.h"
/**
* @file
* @brief Mostly a huge switch(OPCODE), separated to several groups.
*/
namespace Jitrino {
namespace Jet {
void Compiler::handle_inst(void)
{
// is it last instruction in basic block ?
//const bool last = m_bbinfo->last_pc == jinst.pc;
const JInst& jinst = m_insts[m_pc];
unsigned bc_size = m_infoBlock.get_bc_size();
bool lastInBB = jinst.next>=bc_size || (m_insts[jinst.next].flags & OPF_STARTS_BB);
if (is_set(DBG_CHECK_STACK)) {
gen_dbg_check_stack(true);
}
// First test if this is a magic. If not, then proceed with regular
// code gen.
if (!gen_magic()) {
const InstrDesc& idesc = instrs[jinst.opcode];
switch (idesc.ik) {
case ik_a:
handle_ik_a(jinst);
break;
case ik_cf:
handle_ik_cf(jinst);
break;
case ik_cnv:
handle_ik_cnv(jinst);
break;
case ik_ls:
handle_ik_ls(jinst);
break;
case ik_meth:
handle_ik_meth(jinst);
break;
case ik_obj:
handle_ik_obj(jinst);
break;
case ik_stack:
handle_ik_stack(jinst);
break;
case ik_throw:
gen_athrow();
break;
default:
assert(jinst.opcode == OPCODE_NOP);
break;
} // ~switch(opcodegroup)
} else { // if (!gen_magic()) {
// no op. Just check stack (if applicable) and do mem manipulations
}
if (is_set(DBG_CHECK_STACK)) {
gen_dbg_check_stack(false);
}
if (g_jvmtiMode) {
// Do not allow values to cross instruction boundaries
// on a temporary registers
vpark();
// We must have GC info at every bytecode instruction
// to support possible enumeration at a breakpoint
gen_gc_stack(-1, false);
}
const bool has_fall_through = !jinst.is_set(OPF_DEAD_END);
if (lastInBB && has_fall_through && jinst.get_num_targets() == 0) {
gen_bb_leave(jinst.next);
}
}
void Compiler::handle_ik_a(const JInst& jinst) {
if (jinst.opcode == OPCODE_IINC) {
gen_iinc(jinst.op0, jinst.op1);
return;
}
switch(jinst.opcode) {
case OPCODE_LCMP:
gen_x_cmp(jinst.opcode, i64);
return;
case OPCODE_FCMPL:
case OPCODE_FCMPG:
gen_x_cmp(jinst.opcode, flt32);
return;
case OPCODE_DCMPL:
case OPCODE_DCMPG:
gen_x_cmp(jinst.opcode, dbl64);
return;
default: break;
}
jtype opnd = jvoid;
JavaByteCodes inst = jinst.opcode;
switch(jinst.opcode) {
case OPCODE_IADD: opnd = i32; inst = OPCODE_IADD; break;
case OPCODE_LADD: opnd = i64; inst = OPCODE_IADD; break;
case OPCODE_FADD: opnd = flt32; inst = OPCODE_IADD; break;
case OPCODE_DADD: opnd = dbl64; inst = OPCODE_IADD; break;
case OPCODE_ISUB: opnd = i32; inst = OPCODE_ISUB; break;
case OPCODE_LSUB: opnd = i64; inst = OPCODE_ISUB; break;
case OPCODE_FSUB: opnd = flt32; inst = OPCODE_ISUB; break;
case OPCODE_DSUB: opnd = dbl64; inst = OPCODE_ISUB; break;
case OPCODE_IMUL: opnd = i32; inst = OPCODE_IMUL; break;
case OPCODE_LMUL: opnd = i64; inst = OPCODE_IMUL; break;
case OPCODE_FMUL: opnd = flt32; inst = OPCODE_IMUL; break;
case OPCODE_DMUL: opnd = dbl64; inst = OPCODE_IMUL; break;
case OPCODE_IDIV: opnd = i32; inst = OPCODE_IDIV; break;
case OPCODE_LDIV: opnd = i64; inst = OPCODE_IDIV; break;
case OPCODE_FDIV: opnd = flt32; inst = OPCODE_IDIV; break;
case OPCODE_DDIV: opnd = dbl64; inst = OPCODE_IDIV; break;
case OPCODE_IREM: opnd = i32; inst = OPCODE_IREM; break;
case OPCODE_LREM: opnd = i64; inst = OPCODE_IREM; break;
case OPCODE_FREM: opnd = flt32; inst = OPCODE_IREM; break;
case OPCODE_DREM: opnd = dbl64; inst = OPCODE_IREM; break;
case OPCODE_INEG: opnd = i32; inst = OPCODE_INEG; break;
case OPCODE_LNEG: opnd = i64; inst = OPCODE_INEG; break;
case OPCODE_FNEG: opnd = flt32; inst = OPCODE_INEG; break;
case OPCODE_DNEG: opnd = dbl64; inst = OPCODE_INEG; break;
case OPCODE_ISHL: opnd = i32; inst = OPCODE_ISHL; break;
case OPCODE_LSHL: opnd = i64; inst = OPCODE_ISHL; break;
case OPCODE_ISHR: opnd = i32; inst = OPCODE_ISHR; break;
case OPCODE_LSHR: opnd = i64; inst = OPCODE_ISHR; break;
case OPCODE_IUSHR: opnd = i32; inst = OPCODE_IUSHR; break;
case OPCODE_LUSHR: opnd = i64; inst = OPCODE_IUSHR; break;
case OPCODE_IAND: opnd = i32; inst = OPCODE_IAND; break;
case OPCODE_LAND: opnd = i64; inst = OPCODE_IAND; break;
case OPCODE_IOR: opnd = i32; inst = OPCODE_IOR; break;
case OPCODE_LOR: opnd = i64; inst = OPCODE_IOR; break;
case OPCODE_IXOR: opnd = i32; inst = OPCODE_IXOR; break;
case OPCODE_LXOR: opnd = i64; inst = OPCODE_IXOR; break;
default: assert(false); break;
}
if ((inst == OPCODE_IDIV || inst == OPCODE_IREM ) &&
(opnd == i32 || opnd == i64)) {
gen_check_div_by_zero(opnd, 0);
}
gen_a(inst, opnd);
}
void Compiler::handle_ik_cf(const JInst& jinst) {
switch(jinst.opcode) {
case OPCODE_IFNULL:
case OPCODE_IFNONNULL:
case OPCODE_IFEQ:
case OPCODE_IFNE:
case OPCODE_IFLT:
case OPCODE_IFGE:
case OPCODE_IFGT:
case OPCODE_IFLE:
gen_if(jinst.opcode, jinst.get_target(0));
break;
case OPCODE_IF_ACMPEQ:
case OPCODE_IF_ACMPNE:
case OPCODE_IF_ICMPEQ:
case OPCODE_IF_ICMPNE:
case OPCODE_IF_ICMPLT:
case OPCODE_IF_ICMPGE:
case OPCODE_IF_ICMPGT:
case OPCODE_IF_ICMPLE:
gen_if_icmp(jinst.opcode, jinst.get_target(0));
break;
case OPCODE_GOTO:
case OPCODE_GOTO_W:
gen_goto(jinst.get_target(0));
break;
case OPCODE_JSR:
case OPCODE_JSR_W:
gen_jsr(jinst.get_target(0));
break;
case OPCODE_RET:
gen_ret(jinst.op0);
break;
case OPCODE_TABLESWITCH:
case OPCODE_LOOKUPSWITCH:
gen_switch(jinst);
break;
default: assert(false); break;
}
}
void Compiler::handle_ik_cnv(const JInst& jinst)
{
jtype from, to;
switch(jinst.opcode) {
case OPCODE_I2B: from = i32; to = i8; break;
case OPCODE_I2C: from = i32; to = u16; break;
case OPCODE_I2S: from = i32; to = i16; break;
case OPCODE_I2L: from = i32; to = i64; break;
case OPCODE_I2F: from = i32; to = flt32; break;
case OPCODE_I2D: from = i32; to = dbl64; break;
case OPCODE_L2I: from = i64; to = i32; break;
case OPCODE_L2F: from = i64; to = flt32; break;
case OPCODE_L2D: from = i64; to = dbl64; break;
case OPCODE_F2I: from = flt32; to = i32; break;
case OPCODE_F2L: from = flt32; to = i64; break;
case OPCODE_F2D: from = flt32; to = dbl64; break;
case OPCODE_D2I: from = dbl64; to = i32; break;
case OPCODE_D2L: from = dbl64; to = i64; break;
case OPCODE_D2F: from = dbl64; to = flt32; break;
default: assert(false); from = to = jvoid; break;
}// ~switch opcode
gen_cnv(from, to);
}
void Compiler::handle_ik_ls(const JInst& jinst) {
switch(jinst.opcode) {
case OPCODE_ICONST_M1: gen_push((int)-1); break;
case OPCODE_ICONST_0: gen_push((int)0); break;
case OPCODE_ICONST_1: gen_push((int)1); break;
case OPCODE_ICONST_2: gen_push((int)2); break;
case OPCODE_ICONST_3: gen_push((int)3); break;
case OPCODE_ICONST_4: gen_push((int)4); break;
case OPCODE_ICONST_5: gen_push((int)5); break;
case OPCODE_LCONST_0: gen_push((jlong)0); break;
case OPCODE_LCONST_1: gen_push((jlong)1); break;
case OPCODE_FCONST_0: gen_push(flt32, &g_fconst_0); break;
case OPCODE_FCONST_1: gen_push(flt32, &g_fconst_1); break;
case OPCODE_FCONST_2: gen_push(flt32, &g_fconst_2); break;
case OPCODE_DCONST_0: gen_push(dbl64, &g_dconst_0); break;
case OPCODE_DCONST_1: gen_push(dbl64, &g_dconst_1); break;
case OPCODE_LDC:
case OPCODE_LDC_W:
case OPCODE_LDC2_W:
gen_ldc();
break;
case OPCODE_SIPUSH:
gen_push((int)(short)(unsigned short)jinst.op0);
break;
case OPCODE_BIPUSH:
gen_push((int)(char)(unsigned char)jinst.op0);
break;
case OPCODE_ASTORE:
assert(m_jframe->top() == jobj || m_jframe->top() == jretAddr);
gen_st(m_jframe->top(), jinst.op0);
break;
case OPCODE_ISTORE: gen_st(i32, jinst.op0); break;
case OPCODE_LSTORE: gen_st(i64, jinst.op0); break;
case OPCODE_FSTORE: gen_st(flt32, jinst.op0); break;
case OPCODE_DSTORE: gen_st(dbl64, jinst.op0); break;
case OPCODE_ISTORE_0:
case OPCODE_ISTORE_1:
case OPCODE_ISTORE_2:
case OPCODE_ISTORE_3:
gen_st(i32, jinst.opcode-OPCODE_ISTORE_0);
break;
case OPCODE_LSTORE_0:
case OPCODE_LSTORE_1:
case OPCODE_LSTORE_2:
case OPCODE_LSTORE_3:
gen_st(i64, jinst.opcode-OPCODE_LSTORE_0);
break;
case OPCODE_FSTORE_0:
case OPCODE_FSTORE_1:
case OPCODE_FSTORE_2:
case OPCODE_FSTORE_3:
gen_st(flt32, jinst.opcode-OPCODE_FSTORE_0);
break;
case OPCODE_DSTORE_0:
case OPCODE_DSTORE_1:
case OPCODE_DSTORE_2:
case OPCODE_DSTORE_3:
gen_st(dbl64, jinst.opcode-OPCODE_DSTORE_0);
break;
case OPCODE_ASTORE_0:
case OPCODE_ASTORE_1:
case OPCODE_ASTORE_2:
case OPCODE_ASTORE_3:
assert(m_jframe->top() == jobj || m_jframe->top() == jretAddr);
gen_st(m_jframe->top(), jinst.opcode-OPCODE_ASTORE_0);
break;
case OPCODE_ILOAD: gen_ld(i32, jinst.op0); break;
case OPCODE_LLOAD: gen_ld(i64, jinst.op0); break;
case OPCODE_FLOAD: gen_ld(flt32, jinst.op0); break;
case OPCODE_DLOAD: gen_ld(dbl64, jinst.op0); break;
case OPCODE_ALOAD: gen_ld(jobj, jinst.op0); break;
case OPCODE_ILOAD_0:
case OPCODE_ILOAD_1:
case OPCODE_ILOAD_2:
case OPCODE_ILOAD_3:
gen_ld(i32, jinst.opcode-OPCODE_ILOAD_0);
break;
case OPCODE_LLOAD_0:
case OPCODE_LLOAD_1:
case OPCODE_LLOAD_2:
case OPCODE_LLOAD_3:
gen_ld(i64, jinst.opcode-OPCODE_LLOAD_0);
break;
case OPCODE_FLOAD_0:
case OPCODE_FLOAD_1:
case OPCODE_FLOAD_2:
case OPCODE_FLOAD_3:
gen_ld(flt32, jinst.opcode-OPCODE_FLOAD_0);
break;
case OPCODE_DLOAD_0:
case OPCODE_DLOAD_1:
case OPCODE_DLOAD_2:
case OPCODE_DLOAD_3:
gen_ld(dbl64, jinst.opcode-OPCODE_DLOAD_0);
break;
case OPCODE_ALOAD_0:
case OPCODE_ALOAD_1:
case OPCODE_ALOAD_2:
case OPCODE_ALOAD_3:
gen_ld(jobj, jinst.opcode-OPCODE_ALOAD_0);
break;
case OPCODE_ACONST_NULL:
gen_push(jobj, NULL_REF);
break;
default: assert(false); break;
}
}
void Compiler::handle_ik_meth(const JInst& jinst) {
if (jinst.opcode == OPCODE_INVOKESTATIC ||
jinst.opcode == OPCODE_INVOKESPECIAL ||
jinst.opcode == OPCODE_INVOKEVIRTUAL ||
jinst.opcode == OPCODE_INVOKEINTERFACE) {
JavaByteCodes opkod = jinst.opcode;
::std::vector<jtype> args;
jtype retType;
bool is_static = opkod == OPCODE_INVOKESTATIC;
get_args_info(is_static, jinst.op0, args, &retType);
Method_Handle meth = NULL;
unsigned short cpIndex = (unsigned short)jinst.op0;
bool lazy = m_lazy_resolution;
bool resolve = !lazy || class_cp_is_entry_resolved(m_klass, cpIndex);
if (!resolve) {
assert(lazy);
gen_invoke(opkod, NULL, cpIndex, args, retType);
return;
}
if (opkod == OPCODE_INVOKESTATIC) {
meth = resolve_static_method(m_compileHandle, m_klass,
jinst.op0);
if (meth != NULL) {
Class_Handle klass = method_get_class(meth);
if (!class_is_initialized(klass)) {
gen_call_vm(ci_helper_o, rt_helper_init_class, 0, klass);
}
}
}
else if (opkod == OPCODE_INVOKEVIRTUAL) {
meth = resolve_virtual_method(m_compileHandle, m_klass,
jinst.op0);
}
else if (opkod == OPCODE_INVOKEINTERFACE) {
// BUG and HACK - all in one:
// An 'org/eclipse/ui/keys/KeyStroke::hashCode' (e3.0) does
// invokeinterface on java/util/SortedSet::hashCode(), but the
// entry get resolved into the 'java/lang/Object::hashCode' !
// later: for eclipse 3.1.1 the same problem happens with
// org/eclipse/jdt/internal/core/JavaProject::equals
// which tries to resolve
// 'org/eclipse/core/resources/IProject::equals (Ljava/lang/Object;)Z'
meth = resolve_interface_method(m_compileHandle, m_klass, jinst.op0);
//
//*** workaround here:
if (meth != NULL &&
!class_is_interface(method_get_class(meth))) {
opkod = OPCODE_INVOKEVIRTUAL;
}
}
else {
assert(opkod == OPCODE_INVOKESPECIAL);
meth = resolve_special_method(m_compileHandle, m_klass, jinst.op0);
}
// if class to call to is available, but method is not found in the class
// meth here will be equal to NULL and lazy resolution call will be
// generated in gen_invoke
gen_invoke(opkod, meth, cpIndex, args, retType);
return;
}
switch(jinst.opcode) {
case OPCODE_IRETURN: {
SYNC_FIRST(static const CallSig cs(CCONV_MANAGED, i32));
gen_return(cs);
break;
}
case OPCODE_LRETURN: {
SYNC_FIRST(static const CallSig cs(CCONV_MANAGED, i64));
gen_return(cs);
break;
}
case OPCODE_FRETURN: {
SYNC_FIRST(static const CallSig cs(CCONV_MANAGED, flt32));
gen_return(cs);
break;
}
case OPCODE_DRETURN: {
SYNC_FIRST(static const CallSig cs(CCONV_MANAGED, dbl64));
gen_return(cs);
break;
}
case OPCODE_ARETURN: {
SYNC_FIRST(static const CallSig cs(CCONV_MANAGED, jobj));
gen_return(cs);
break;
}
case OPCODE_RETURN: {
SYNC_FIRST(static const CallSig cs(CCONV_MANAGED, jvoid));
gen_return(cs);
break;
}
default: assert(false); break;
};
}
void Compiler::handle_ik_obj(const JInst& jinst) {
jtype jt = jvoid;
bool store = false;
switch(jinst.opcode) {
case OPCODE_IASTORE: store = true;
case OPCODE_IALOAD: jt = i32; break;
case OPCODE_LASTORE: store = true;
case OPCODE_LALOAD: jt = i64; break;
case OPCODE_FASTORE: store = true;
case OPCODE_FALOAD: jt = flt32; break;
case OPCODE_DASTORE: store = true;
case OPCODE_DALOAD: jt = dbl64; break;
case OPCODE_AASTORE: store = true;
case OPCODE_AALOAD: jt = jobj; break;
case OPCODE_BASTORE: store = true;
case OPCODE_BALOAD: jt = i8; break;
case OPCODE_CASTORE: store = true;
case OPCODE_CALOAD: jt = u16; break;
case OPCODE_SASTORE: store = true;
case OPCODE_SALOAD: jt = i16; break;
default: break;
}
if (jt != jvoid) {
// that was indeed *aload/*astore
if (store) {
gen_arr_store(jt);
}
else {
gen_arr_load(jt);
}
return;
}
switch(jinst.opcode) {
case OPCODE_NEW:
gen_new(m_klass, (unsigned short)jinst.op0);
break;
case OPCODE_PUTSTATIC:
case OPCODE_GETSTATIC:
case OPCODE_PUTFIELD:
case OPCODE_GETFIELD:
gen_field_op(jinst.opcode, m_klass, (unsigned short)jinst.op0);
break;
case OPCODE_ARRAYLENGTH:
gen_array_length();
break;
case OPCODE_ANEWARRAY:
gen_new_array(m_klass, (unsigned short)jinst.op0);
break;
case OPCODE_NEWARRAY:
{
VM_Data_Type atype;
switch(jinst.op0) {
case 4: atype = VM_DATA_TYPE_BOOLEAN; break;
case 5: atype = VM_DATA_TYPE_CHAR; break;
case 6: atype = VM_DATA_TYPE_F4; break;
case 7: atype = VM_DATA_TYPE_F8; break;
case 8: atype = VM_DATA_TYPE_INT8; break;
case 9: atype = VM_DATA_TYPE_INT16; break;
case 10: atype = VM_DATA_TYPE_INT32; break;
case 11: atype = VM_DATA_TYPE_INT64; break;
default: assert(false); atype = VM_DATA_TYPE_INVALID; break;
}
Class_Handle elem_class = class_get_class_of_primitive_type(atype);
Class_Handle array_class = class_get_array_of_class(elem_class);
Allocation_Handle ah = class_get_allocation_handle(array_class);
gen_new_array(ah);
}
break;
case OPCODE_MULTIANEWARRAY:
gen_multianewarray(m_klass, (unsigned short)jinst.op0, jinst.op1);
break;
case OPCODE_MONITORENTER:
case OPCODE_MONITOREXIT:
gen_monitor_ee();
break;
case OPCODE_CHECKCAST:
case OPCODE_INSTANCEOF:
gen_instanceof_cast(jinst.opcode, m_klass, (unsigned short)jinst.op0);
break;
default: assert(false); break;
}
}
void Compiler::handle_ik_stack(const JInst& jinst) {
switch(jinst.opcode) {
case OPCODE_POP:
gen_pop(m_jframe->top());
break;
case OPCODE_POP2:
gen_pop2();
break;
case OPCODE_DUP:
case OPCODE_DUP_X1:
case OPCODE_DUP_X2:
case OPCODE_DUP2:
case OPCODE_DUP2_X1:
case OPCODE_DUP2_X2:
case OPCODE_SWAP:
gen_dup(jinst.opcode);
break;
default: assert(false); break;
}
}
}
} // ~namespace Jitrino::Jet