blob: 50dd351532fab30046d98cc43715ee42edc18e5d [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.
*/
/**
* @file
* @brief Magics and WBs support for MMTk.
*/
#include "compiler.h"
#include "enc_defs.h"
#include "enc.h"
#include "open/vm_class_info.h"
#include "open/vm.h"
#include "jit_intf.h"
#include "VMMagic.h"
#include <vector>
using std::vector;
namespace Jitrino {
namespace Jet {
static size_t sizeof_jt(jtype jt) {
static size_t sizes[] = {
1, //i8,
2, //i16,
2, //u16,
4, //i32,
8, //i64,
4, //flt32,
8, //dbl64,
sizeof(POINTER_SIZE_INT), //jobj,
sizeof(POINTER_SIZE_INT), //jvoid,
sizeof(POINTER_SIZE_INT), //jretAddr,
0, //jtypes_count,
};
size_t res= sizes[jt];
assert(res >= 1 && res<=8 && (res%2==0 || res == 1));
return res;
}
/** creates new opnd with the specified type and generates move from old one to new one */
static void vstack_replace_top_opnd(Compiler* c, jtype jt) {
Opnd src = c->vstack(0).as_opnd();
Opnd dst(jt, c->valloc(jt));
size_t dstSize = sizeof_jt(dst.jt());
size_t srcSize = sizeof_jt(src.jt());
assert((srcSize==4 || srcSize == 8) && (dstSize == 4 || dstSize == 8));
if (srcSize == dstSize) { //simple mov
c->mov(dst, src);
c->vpop(); //pop src
c->vpush(dst);
} else if (srcSize > dstSize) { //truncation
c->do_mov(dst, src);
c->vpop(); //lo&hi parts are popped with a single pop operation for i64 type
c->vpush(dst); //push result
} else { // expansion
assert(srcSize<dstSize);
c->do_mov(dst, src, true); //fill lower part -> dst
Opnd hi(jt, c->g_iconst_0); //fill hi part with 0
c->vpop(); //pop old value
c->vpush2(dst, hi); //push new value
}
}
bool Compiler::gen_magic(void)
{
const JInst& jinst = m_insts[m_pc];
JavaByteCodes opkod = jinst.opcode;
if (opkod != OPCODE_INVOKEVIRTUAL &&
opkod != OPCODE_INVOKESTATIC &&
opkod != OPCODE_INVOKESPECIAL)
{
return false;
}
const char* kname = class_cp_get_entry_class_name(m_klass, (unsigned short)jinst.op0);
if (!VMMagicUtils::isVMMagicClass(kname)) {
return false;
}
// This is a magic -> transform it
const char* mname = class_cp_get_entry_name(m_klass, (unsigned short)jinst.op0);
jtype magicType = iplatf;
if (!strcmp(mname, "fromLong")) {
vstack_replace_top_opnd(this, magicType);
return true;
}
if (!strcmp(mname, "toLong")) {
vstack_replace_top_opnd(this, i64);
return true;
}
return false;
/** Old IA32 implementation. This implementation is not GC safe,
it keeps all magic classes as objects and magics becomes a part of GC enumeration
This code must be refactored or removed
vector<jtype> args;
jtype retType;
bool is_static = opkod == OPCODE_INVOKESTATIC;
get_args_info(is_static, jinst.op0, args, &retType);
// ADD, SUB, DIFF, etc - 2 args arithmetics
ALU oper = alu_count;
if (!strcmp(mname, "add")) { oper = alu_add; }
else if (!strcmp(mname, "plus")) { oper = alu_add; }
else if (!strcmp(mname, "sub")) { oper = alu_sub; }
else if (!strcmp(mname, "minus")) { oper = alu_sub; }
else if (!strcmp(mname, "diff")) { oper = alu_sub; }
else if (!strcmp(mname, "or")) { oper = alu_or; }
else if (!strcmp(mname, "xor")) { oper = alu_xor; }
else if (!strcmp(mname, "and")) { oper = alu_and; }
if (oper != alu_count) {
Val& v0 = vstack(0, true);
Val& v1 = vstack(1, true);
Opnd newObj(jobj, valloc(jobj));
mov(newObj, v1.as_opnd());
alu(oper, newObj, v0.as_opnd());
vpop();
vpop();
vpush(newObj);
return true;
}
JavaByteCodes shiftOp = OPCODE_NOP;
if (!strcmp(mname, "lsh")) {shiftOp = OPCODE_ISHL;}
else if (!strcmp(mname, "rsha")) {shiftOp = OPCODE_ISHR;}
else if (!strcmp(mname, "rshl")) {shiftOp = OPCODE_IUSHR;}
if (shiftOp != OPCODE_NOP) {
Opnd shiftAmount = vstack(0, false).as_opnd(i32);
shiftAmount = vstack(0, true).as_opnd(i32);
rlock(shiftAmount.reg());
vpop();
//changing type of obj opnd type to ia32
vstack_replace_top_opnd(this, i32);
vpush(shiftAmount);
runlock(shiftAmount.reg());
//processing as java bytecode and converting back to the obj type
gen_a(shiftOp, i32);
vstack_replace_top_opnd(this, jobj);
return true;
}
if (!strcmp(mname, "not")) {
Opnd v1 = vstack(0, true).as_opnd(jobj);
rlock(v1.reg());
Opnd v2(jobj, valloc(jobj));
mov(v2, v1);
bitwise_not(v2);
runlock(v1.reg());
vpop();
vpush(v2);
return true;
}
//
// EQ, GE, GT, LE, LT, sXX - 2 args compare
//
COND cm = cond_none;
if (!strcmp(mname, "EQ")) { cm = eq; }
if (!strcmp(mname, "equals")) { cm = eq; }
else if (!strcmp(mname, "NE")) { cm = ne; }
// unsigned compare
else if (!strcmp(mname, "GE")) { cm = ae; }
else if (!strcmp(mname, "GT")) { cm = above; }
else if (!strcmp(mname, "LE")) { cm = be;}
else if (!strcmp(mname, "LT")) { cm = below; }
// signed compare
else if (!strcmp(mname, "sGE")) { cm = ge; }
else if (!strcmp(mname, "sGT")) { cm = gt; }
else if (!strcmp(mname, "sLE")) { cm = le;}
else if (!strcmp(mname, "sLT")) { cm = lt; }
//
if (cm != cond_none) {
Opnd o1 = vstack(1, true).as_opnd(i32);
Opnd o2 = vstack(0, true).as_opnd(i32);
alu(alu_cmp, o1, o2);
vpop();
vpop();
Opnd boolResult(i32, valloc(i32));
rlock(boolResult.reg());
mov(boolResult, g_iconst_0);
cmovcc(cm, boolResult, vaddr(i32, &g_iconst_1));
runlock(boolResult.reg());
vpush(boolResult);
return true;
}
//
// is<Smth> one arg testing
//
bool oneArgCmp = false;
int theConst = 0;
if (!strcmp(mname, "isZero")) { oneArgCmp = true; theConst = 0; }
else if (!strcmp(mname, "isMax")) { oneArgCmp = true; theConst = ~0; }
else if (!strcmp(mname, "isNull")) { oneArgCmp = true; theConst = 0; }
if (oneArgCmp) {
AR regVal = vstack(0, true).reg();
rlock(regVal);
alu(alu_cmp, Opnd(jobj, regVal), theConst);
//save the result
AR resultReg = valloc(i32);
rlock(resultReg);
mov(resultReg, Opnd(g_iconst_0));
cmovcc(z, resultReg, vaddr(i32, &g_iconst_1));
runlock(resultReg);
vpop();
vpush(Opnd(i32, resultReg));
runlock(regVal);
return true;
}
//
// fromXXX - static creation from something
//
if (strcmp(mname, "fromLong")) {
assert(0);
}
else if (!strcmp(mname, "fromIntSignExtend")) {
vstack_replace_top_opnd(this, jobj);
return true;
}
else if (!strcmp(mname, "fromIntZeroExtend")) {
vstack_replace_top_opnd(this, jobj);
return true;
}
else if (!strcmp(mname, "fromObject") ||
!strcmp(mname, "toAddress") ||
!strcmp(mname, "toObjectReference"))
{
vstack_replace_top_opnd(this, jobj);
return true;
}
const char* msig = method_get_descriptor(meth);
//
// load<type> things
//
jtype jt = jvoid;
bool load = true;
bool has_offset = false;
if (!strcmp(mname, "loadObjectReference")) { jt = jobj; }
else if (!strcmp(mname, "loadAddress")) { jt = jobj; }
else if (!strcmp(mname, "loadWord")) { jt = jobj; }
else if (!strcmp(mname, "loadByte")) { jt = i8; }
else if (!strcmp(mname, "loadChar")) { jt = u16; }
else if (!strcmp(mname, "loadDouble")) { jt = dbl64; }
else if (!strcmp(mname, "loadFloat")) { jt = flt32; }
else if (!strcmp(mname, "loadInt")) { jt = i32; }
else if (!strcmp(mname, "loadLong")) { jt = i64; }
else if (!strcmp(mname, "loadShort")) { jt = i16; }
else if (!strcmp(mname, "prepareWord")) { jt = i32; }
else if (!strcmp(mname, "prepareObjectReference")) { jt = jobj;}
else if (!strcmp(mname, "prepareAddress")) { jt = jobj;}
else if (!strcmp(mname, "prepareInt")) { jt = i32; }
else if (!strcmp(mname, "store")) {
load = false;
// store() must have at least one arg
assert(strlen(msig) > strlen("()V"));
char ch = msig[1]; // first symbol after '('.
VM_Data_Type vdt = (VM_Data_Type)ch;
switch(vdt) {
case VM_DATA_TYPE_BOOLEAN: // i8
case VM_DATA_TYPE_INT8: jt = i8; break;
case VM_DATA_TYPE_INT16: jt = i16; break;
case VM_DATA_TYPE_CHAR: jt = u16; break;
case VM_DATA_TYPE_INT32: jt = i32; break;
case VM_DATA_TYPE_INT64: jt = i64; break;
case VM_DATA_TYPE_F4: jt = flt32; break;
case VM_DATA_TYPE_F8: jt = dbl64; break;
case VM_DATA_TYPE_ARRAY: // jobj
case VM_DATA_TYPE_CLASS: jt = jobj; break;
default: assert(false);
}
jtype retType;
vector<jtype> args;
get_args_info(meth, args, &retType);
assert(args.size()>=2);
has_offset = args.size() > 2;
}
if (jt != jvoid) {
size_t jt_size = sizeof_jt(jt);
if (load) {
// if loadXX() has any arg, then it's offset
if(strncmp(msig, "()", 2)) {
has_offset = true;
}
}
unsigned addr_depth = has_offset ? 1 : 0;
if (!load) {
++addr_depth;
if (is_wide(jt)) {
++addr_depth;
}
}
AR addrReg = vstack(addr_depth, true).reg();
rlock(addrReg);
if (has_offset) {
//Add offset. Save to the new location.
AR addrWithOffsetReg = valloc(jobj);
mov(addrWithOffsetReg, addrReg);
runlock(addrReg);
addrReg = addrWithOffsetReg;
rlock(addrReg);
AR offsetReg = vstack(0, true).reg();
vpop();
alu(alu_add, addrReg, offsetReg);
}
if (load) {
vpop();
if (!is_big(jt)) {
AR resReg = valloc(jt);
ld(jt, resReg, addrReg);
Opnd resOpnd(jt, resReg);
if (jt_size < 4) {
Opnd extendedOpnd(i32, valloc(i32));
if (jt == u16) {
zx2(extendedOpnd, resOpnd);
} else {
sx(extendedOpnd, resOpnd);
}
resOpnd = extendedOpnd;
}
vpush(resOpnd);
} else { //code is taken from array element load -> TODO: avoid duplication
AR ar_lo = valloc(jt);
Opnd lo(jt, ar_lo);
rlock(lo);
do_mov(lo, Opnd(i32, addrReg, 0));
AR ar_hi = valloc(jt);
Opnd hi(jt, ar_hi);
rlock(hi);
Opnd mem_hi(jt, Opnd(i32, addrReg, 4));
do_mov(hi, mem_hi);
vpush2(lo, hi);
runlock(lo);
runlock(hi);
}
} else {
Opnd v0 = vstack(0, true).as_opnd(jt);
if (!is_big(jt)) {
mov(Opnd(jt, addrReg, 0), v0);
} else {
do_mov(Opnd(i32, addrReg, 0), v0);
Opnd v1 = vstack(1, true).as_opnd(jt);
do_mov(Opnd(i32, addrReg, 4), v1);
}
vpop(); // pop out value
vpop(); // pop out Address
}
runlock(addrReg);
return true;
}
//
// max, one, zero
//
bool loadConst = false;
if (!strcmp(mname, "max")) { loadConst = true; theConst = -1;}
else if (!strcmp(mname, "one")) { loadConst = true; theConst = 1;}
else if (!strcmp(mname, "zero")) { loadConst = true; theConst = 0;}
else if (!strcmp(mname, "nullReference"))
{ loadConst = true; theConst = 0;}
if (loadConst) {
Opnd regOpnd(jobj, valloc(jobj));
mov(regOpnd, theConst);
vpush(regOpnd);
return true;
}
//
// toInt, toLong, toObjectRef, toWord(), etc.
//
jt = jvoid;
if (!strcmp(mname, "toInt")) { jt = i32; }
else if (!strcmp(mname, "toLong")) { jt = i64; }
else if (!strcmp(mname, "toObjectRef")) { jt = jobj; }
else if (!strcmp(mname, "toWord")) { jt = jobj; }
else if (!strcmp(mname, "toAddress")) { jt = jobj; }
else if (!strcmp(mname, "toObject")) { jt = jobj; }
else if (!strcmp(mname, "toExtent")) { jt = jobj; }
else if (!strcmp(mname, "toOffset")) { jt = jobj; }
if (jt != jvoid) {
if (jt!=i64) {
vstack_replace_top_opnd(this, jt);
return true;
}
vstack_replace_top_opnd(this, i32);
Opnd srcOpnd = vstack(0, true).as_opnd(i32);
Opnd lo(jt, valloc(jt));
do_mov(lo, srcOpnd);
Opnd hi(jt, g_iconst_0);
vpop();
vpush2(lo, hi);
return true;
}
if (!strcmp(mname, "attempt")) {
AR addrReg;
if (args.size() == 4) { //attempt with Offset
AR newAddressReg = valloc(jobj);
rlock(newAddressReg);
addrReg= vstack(3, true).reg();
mov(newAddressReg, addrReg);
AR offsetReg= vstack(0, true).reg();
alu(alu_add, newAddressReg, offsetReg);
runlock(newAddressReg);
addrReg = newAddressReg;
vpop();
} else {
addrReg = vstack(2, true).reg();
}
rlock(addrReg);
AR newReg = vstack(0, true).reg();
rlock(newReg);
AR oldReg = vstack(1, true).reg();
cmpxchg(true, addrReg, newReg, oldReg);
runlock(addrReg);
runlock(newReg);
//save the result
AR resultReg = valloc(i32);
rlock(resultReg);
mov(resultReg, Opnd(g_iconst_0));
cmovcc(z, resultReg, vaddr(i32, &g_iconst_1));
runlock(resultReg);
//fixing the stack and saving the result.
vpop();
vpop();
vpop();
vpush(Opnd(i32, resultReg));
return true;
}
//
// xArray stuff
//
if (!strcmp(mname, "create")) {
VM_Data_Type atype = VM_DATA_TYPE_INT32;
Class_Handle elem_class = class_get_class_of_primitive_type(atype);
assert(elem_class != NULL);
Class_Handle array_class = class_get_array_of_class(elem_class);
assert(array_class != NULL);
Allocation_Handle ah = class_get_allocation_handle(array_class);
gen_new_array(ah);
return true;
}
if (!strcmp(mname, "get")) {
gen_arr_load(jobj);
return true;
}
if (!strcmp(mname, "set")) {
gen_arr_store(jobj, false);
return true;
}
if (!strcmp(mname, "length")) {
gen_array_length();
return true;
}
//assert(false);
return false;
*/
}
}}; // ~namespace Jitrino::Jet