blob: d445ecce5371486e2754c96822a3a367bdb12e26 [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 Astapchuk
*/
/**
* @file
* @brief Code generation routines for various branches - GOTO, IF_*,
* TABLE- and LOOKUP switches, JSR and RET.
*/
#include "compiler.h"
namespace Jitrino {
namespace Jet {
/**
* @brief Maps conditional branches #JavaByteCodes to appropriate
* #ConditionMnemonic.
*
* Accepts only opcode in ranges: [OPCODE_IFEQ; OPCODE_IFLE] and
* [OPCODE_IF_ICMPEQ; OPCODE_IF_ICMPLE].
*
* @param opcod - a byte code
* @return appropriate ConditionMnemonic
*/
static COND to_cond(JavaByteCodes opcod)
{
if (opcod==OPCODE_GOTO) {
return cond_none;
}
unsigned cod;
if (OPCODE_IFEQ <= opcod && opcod <= OPCODE_IFLE) {
cod = opcod - OPCODE_IFEQ;
}
else {
assert(OPCODE_IF_ICMPEQ <= opcod && opcod <= OPCODE_IF_ICMPLE);
cod = opcod - OPCODE_IF_ICMPEQ;
}
switch (cod) {
case 0: return eq;
case 1: return ne;
case 2: return lt;
case 3: return ge;
case 4: return gt;
case 5: return le;
default:
break;
}
assert(false);
return (COND)NOTHING;
}
/**
* @brief Reverts a condition so operands in comparation may be swapped.
*
* Eg. 'op0 <= op1' may become 'op1 >= op0', which is 'lt' changed to 'gt'.
*
* Used to swap operands in IF_ICMPx operations when we have an immediate
* operand as a first operand.
*/
static COND flip(COND cond)
{
switch(cond) {
case eq:
case ne: return cond;
case lt: return gt;
case ge: return le;
case gt: return lt;
case le: return ge;
default:
break;
}
assert(false);
return (COND)NOTHING;
}
void Compiler::gen_goto(unsigned target)
{
if (target <= m_pc) {
// Back branch
gen_prof_be();
gen_gc_safe_point();
}
gen_bb_leave(target);
br(cond_none, target, m_bbinfo->start);
}
void Compiler::gen_if(JavaByteCodes opcod, unsigned target)
{
if (target <= m_pc) {
// have back branch here
gen_prof_be();
gen_gc_safe_point();
}
jtype jt = i32;
if (opcod == OPCODE_IFNULL) {
opcod = OPCODE_IFEQ;
jt = jobj;
}
else if (opcod == OPCODE_IFNONNULL) {
opcod = OPCODE_IFNE;
jt = jobj;
}
OpndKind kind = m_jframe->dip(0).kind();
bool forceReg = (kind == opnd_imm) || (jt == jobj && g_refs_squeeze);
Opnd op1 = vstack(0, forceReg).as_opnd();
vpop();
rlock(op1);
COND cond = to_cond(opcod);
static const Opnd zero((int)0);
if (jt == jobj && g_refs_squeeze) {
AR ar = valloc(jobj);
movp(ar, NULL_REF);
alu(alu_cmp, Opnd(jobj, ar), op1);
}
else if (opcod == OPCODE_IFEQ || opcod == OPCODE_IFNE) {
if (op1.is_reg()) {
alu(alu_test, op1, op1);
}
else {
alu(alu_cmp, op1, zero);
}
}
else {
alu(alu_cmp, op1, zero);
}
runlock(op1);
gen_bb_leave(target);
br(cond, target, m_bbinfo->start);
}
void Compiler::gen_if_icmp(JavaByteCodes opcod, unsigned target)
{
if (target <= m_pc) {
// have back branch here
gen_prof_be();
gen_gc_safe_point();
}
if (opcod == OPCODE_IF_ACMPEQ) {
opcod = OPCODE_IF_ICMPEQ;
}
else if (opcod == OPCODE_IF_ACMPNE) {
opcod = OPCODE_IF_ICMPNE;
}
Opnd op2 = vstack(0).as_opnd();
vpop();
rlock(op2);
OpndKind kind = m_jframe->dip(0).kind();
// 'Bad' combinations are 'm,m' and 'imm,<any, but imm>' - have to force
// an item into a register
bool forceReg = (op2.is_mem() && kind == opnd_mem) ||
(op2.is_imm() && kind == opnd_imm);
Opnd op1 = vstack(0, forceReg).as_opnd();
vpop();
rlock(op1);
COND cond = to_cond(opcod);
if ( (op1.is_mem() && op2.is_reg()) || op1.is_imm()) {
// here we have 'mem, reg' or 'imm, mem-or-reg' - swap them so it
// become 'reg, mem' (more efficient) or 'mem-or-reg, imm' (existent)
// operations. change the branch condition appropriately.
alu(alu_cmp, op2, op1);
cond = flip(cond);
}
else {
alu(alu_cmp, op1, op2);
}
runlock(op1);
runlock(op2);
gen_bb_leave(target);
br(cond, target, m_bbinfo->start);
}
void Compiler::gen_switch(const JInst & jinst)
{
assert(jinst.opcode == OPCODE_LOOKUPSWITCH
|| jinst.opcode == OPCODE_TABLESWITCH);
Opnd val = vstack(0, true).as_opnd();
vpop();
rlock(val);
gen_bb_leave(NOTHING);
if (jinst.opcode == OPCODE_LOOKUPSWITCH) {
unsigned n = jinst.get_num_targets();
for (unsigned i = 0; i < n; i++) {
Opnd key(jinst.key(i));
unsigned pc = jinst.get_target(i);
alu(alu_cmp, val, key);
br(eq, pc, m_bbinfo->start);
}
runlock(val);
br(cond_none, jinst.get_def_target(), m_bbinfo->start);
return;
}
//
// TABLESWITCH
//
alu(alu_cmp, val, jinst.high());
br(gt, jinst.get_def_target(), m_bbinfo->start);
alu(alu_cmp, val, jinst.low());
br(lt, jinst.get_def_target(), m_bbinfo->start);
AR gr_tabl = valloc(jobj);
movp(gr_tabl, DATA_SWITCH_TABLE | m_curr_inst->pc, m_bbinfo->start);
#ifdef _EM64T_
// On EM64T, we operate with I_32 value in a register, but the
// register will be used as 64 bit in address form - have to extend
sx(Opnd(i64, val.reg()), Opnd(i32, val.reg()));
#endif
// Here, we need to extract 'index-=low()' - can pack this into
// complex address form:
// [table + index*sizeof(void*) - low()*sizeof(void*)],
// but only if low()*sizeof(void*) does fit into displacement ...
int tmp = -jinst.low();
const int LO_BOUND = INT_MIN/(int)sizeof(void*);
const int UP_BOUND = INT_MAX/(int)sizeof(void*);
if (LO_BOUND<=tmp && tmp<=UP_BOUND) {
ld(jobj, gr_tabl, gr_tabl, -jinst.low()*sizeof(void*),
val.reg(), sizeof(void*));
}
else {
// ... otherwise subtract explicitly, but only if the register
// is not used anywhere else
if (rrefs(val.reg()) !=0) {
Opnd vtmp(i32, valloc(i32));
mov(vtmp, val); // make a copy of val
runlock(val);
val = vtmp;
rlock(val);
}
alu(alu_sub, val, jinst.low());
ld(jobj, gr_tabl, gr_tabl, 0, val.reg(), sizeof(void*));
}
runlock(val);
br(gr_tabl);
}
void Compiler::gen_jsr(unsigned target)
{
AR gr = valloc(jobj);
const JInst& jinst = *m_curr_inst;
movp(gr, jinst.next, m_bbinfo->start);
vpush(Val(jretAddr, gr));
gen_bb_leave(target);
br(cond_none, target, m_bbinfo->start);
}
void Compiler::gen_ret(unsigned idx)
{
Opnd ret = vlocal(jretAddr, idx, false).as_opnd();
gen_bb_leave(NOTHING);
br(ret);
}
}}; // ~namespace Jitrino::Jet