blob: a2e4175029e4145942f05e35c15872ddfd655aff [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed 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.
*/
#include <math.h> // for double sqrt(double)
/*
* This file is included by Codegen-armv5te-vfp.c, and implements architecture
* variant-specific code.
*/
/*
* Determine the initial instruction set to be used for this trace.
* Later components may decide to change this.
*/
JitInstructionSetType dvmCompilerInstructionSet(CompilationUnit *cUnit)
{
return DALVIK_JIT_THUMB2;
}
/*
* Jump to the out-of-line handler in ARM mode to finish executing the
* remaining of more complex instructions.
*/
static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpCode opCode)
{
/*
* NOTE - In practice BLX only needs one operand, but since the assembler
* may abort itself and retry due to other out-of-range conditions we
* cannot really use operand[0] to store the absolute target address since
* it may get clobbered by the final relative offset. Therefore,
* we fake BLX_1 is a two operand instruction and the absolute target
* address is stored in operand[1].
*/
clobberHandlerRegs(cUnit);
newLIR2(cUnit, kThumbBlx1,
(int) gDvmJit.codeCache + templateEntryOffsets[opCode],
(int) gDvmJit.codeCache + templateEntryOffsets[opCode]);
newLIR2(cUnit, kThumbBlx2,
(int) gDvmJit.codeCache + templateEntryOffsets[opCode],
(int) gDvmJit.codeCache + templateEntryOffsets[opCode]);
}
void *dvmCompilerGetInterpretTemplate()
{
return (void*) ((int)gDvmJit.codeCache +
templateEntryOffsets[TEMPLATE_INTERPRET]);
}
/* Architecture-specific initializations and checks go here */
static bool compilerArchVariantInit(void)
{
/* First, declare dvmCompiler_TEMPLATE_XXX for each template */
#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
#include "../../../template/armv5te-vfp/TemplateOpList.h"
#undef JIT_TEMPLATE
int i = 0;
extern void dvmCompilerTemplateStart(void);
/*
* Then, populate the templateEntryOffsets array with the offsets from the
* the dvmCompilerTemplateStart symbol for each template.
*/
#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
(intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
#include "../../../template/armv5te-vfp/TemplateOpList.h"
#undef JIT_TEMPLATE
/* Codegen-specific assumptions */
assert(offsetof(ClassObject, vtable) < 128 &&
(offsetof(ClassObject, vtable) & 0x3) == 0);
assert(offsetof(ArrayObject, length) < 128 &&
(offsetof(ArrayObject, length) & 0x3) == 0);
assert(offsetof(ArrayObject, contents) < 256);
/* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
assert(sizeof(StackSaveArea) < 236);
/*
* EA is calculated by doing "Rn + imm5 << 2", and there are 5 entry points
* that codegen may access, make sure that the offset from the top of the
* struct is less than 108.
*/
assert(offsetof(InterpState, jitToInterpEntries) < 108);
return true;
}
static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
{
ArmLIR *branch;
DecodedInstruction *dInsn = &mir->dalvikInsn;
RegLocation rlSrc = getSrcLocWide(cUnit, mir, 0, 1);
RegLocation rlDest = inlinedTargetWide(cUnit, mir, true);
rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
RegLocation rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
newLIR2(cUnit, kThumb2Vsqrtd, S2D(rlResult.lowReg, rlResult.highReg),
S2D(rlSrc.lowReg, rlSrc.highReg));
newLIR2(cUnit, kThumb2Vcmpd, S2D(rlResult.lowReg, rlResult.highReg),
S2D(rlResult.lowReg, rlResult.highReg));
newLIR0(cUnit, kThumb2Fmstat);
branch = newLIR2(cUnit, kThumbBCond, 0, kArmCondEq);
clobberCallRegs(cUnit);
loadConstant(cUnit, r2, (int)sqrt);
newLIR3(cUnit, kThumb2Fmrrd, r0, r1, S2D(rlSrc.lowReg, rlSrc.highReg));
newLIR1(cUnit, kThumbBlxR, r2);
newLIR3(cUnit, kThumb2Fmdrr, S2D(rlResult.lowReg, rlResult.highReg),
r0, r1);
ArmLIR *label = newLIR0(cUnit, kArmPseudoTargetLabel);
label->defMask = ENCODE_ALL;
branch->generic.target = (LIR *)label;
storeValueWide(cUnit, rlDest, rlResult);
return true;
}
static bool handleArithOpFloat(CompilationUnit *cUnit, MIR *mir,
RegLocation rlDest, RegLocation rlSrc1,
RegLocation rlSrc2)
{
int op = kThumbBkpt;
RegLocation rlResult;
/*
* Don't attempt to optimize register usage since these opcodes call out to
* the handlers.
*/
switch (mir->dalvikInsn.opCode) {
case OP_ADD_FLOAT_2ADDR:
case OP_ADD_FLOAT:
op = kThumb2Vadds;
break;
case OP_SUB_FLOAT_2ADDR:
case OP_SUB_FLOAT:
op = kThumb2Vsubs;
break;
case OP_DIV_FLOAT_2ADDR:
case OP_DIV_FLOAT:
op = kThumb2Vdivs;
break;
case OP_MUL_FLOAT_2ADDR:
case OP_MUL_FLOAT:
op = kThumb2Vmuls;
break;
case OP_REM_FLOAT_2ADDR:
case OP_REM_FLOAT:
case OP_NEG_FLOAT: {
return handleArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1,
rlSrc2);
}
default:
return true;
}
rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
newLIR3(cUnit, op, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
storeValue(cUnit, rlDest, rlResult);
return false;
}
static bool handleArithOpDouble(CompilationUnit *cUnit, MIR *mir,
RegLocation rlDest, RegLocation rlSrc1,
RegLocation rlSrc2)
{
int op = kThumbBkpt;
RegLocation rlResult;
switch (mir->dalvikInsn.opCode) {
case OP_ADD_DOUBLE_2ADDR:
case OP_ADD_DOUBLE:
op = kThumb2Vaddd;
break;
case OP_SUB_DOUBLE_2ADDR:
case OP_SUB_DOUBLE:
op = kThumb2Vsubd;
break;
case OP_DIV_DOUBLE_2ADDR:
case OP_DIV_DOUBLE:
op = kThumb2Vdivd;
break;
case OP_MUL_DOUBLE_2ADDR:
case OP_MUL_DOUBLE:
op = kThumb2Vmuld;
break;
case OP_REM_DOUBLE_2ADDR:
case OP_REM_DOUBLE:
case OP_NEG_DOUBLE: {
return handleArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
rlSrc2);
}
default:
return true;
}
rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
assert(rlSrc1.wide);
rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
assert(rlSrc2.wide);
rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
assert(rlDest.wide);
assert(rlResult.wide);
newLIR3(cUnit, op, S2D(rlResult.lowReg, rlResult.highReg),
S2D(rlSrc1.lowReg, rlSrc1.highReg),
S2D(rlSrc2.lowReg, rlSrc2.highReg));
storeValueWide(cUnit, rlDest, rlResult);
return false;
}
static bool handleConversion(CompilationUnit *cUnit, MIR *mir)
{
OpCode opCode = mir->dalvikInsn.opCode;
int op = kThumbBkpt;
bool longSrc = false;
bool longDest = false;
int srcReg;
RegLocation rlSrc;
RegLocation rlDest;
RegLocation rlResult;
switch (opCode) {
case OP_INT_TO_FLOAT:
longSrc = false;
longDest = false;
op = kThumb2VcvtIF;
break;
case OP_FLOAT_TO_INT:
longSrc = false;
longDest = false;
op = kThumb2VcvtFI;
break;
case OP_DOUBLE_TO_FLOAT:
longSrc = true;
longDest = false;
op = kThumb2VcvtDF;
break;
case OP_FLOAT_TO_DOUBLE:
longSrc = false;
longDest = true;
op = kThumb2VcvtFd;
break;
case OP_INT_TO_DOUBLE:
longSrc = false;
longDest = true;
op = kThumb2VcvtID;
break;
case OP_DOUBLE_TO_INT:
longSrc = true;
longDest = false;
op = kThumb2VcvtDI;
break;
case OP_LONG_TO_DOUBLE:
case OP_FLOAT_TO_LONG:
case OP_LONG_TO_FLOAT:
case OP_DOUBLE_TO_LONG:
return handleConversionPortable(cUnit, mir);
default:
return true;
}
if (longSrc) {
rlSrc = getSrcLocWide(cUnit, mir, 0, 1);
rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
srcReg = S2D(rlSrc.lowReg, rlSrc.highReg);
} else {
rlSrc = getSrcLoc(cUnit, mir, 0);
rlSrc = loadValue(cUnit, rlSrc, kFPReg);
srcReg = rlSrc.lowReg;
}
if (longDest) {
rlDest = getDestLocWide(cUnit, mir, 0, 1);
rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
newLIR2(cUnit, op, S2D(rlResult.lowReg, rlResult.highReg), srcReg);
storeValueWide(cUnit, rlDest, rlResult);
} else {
rlDest = getDestLoc(cUnit, mir, 0);
rlResult = evalLoc(cUnit, rlDest, kFPReg, true);
newLIR2(cUnit, op, rlResult.lowReg, srcReg);
storeValue(cUnit, rlDest, rlResult);
}
return false;
}
static bool handleCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
RegLocation rlSrc1, RegLocation rlSrc2)
{
bool isDouble;
int defaultResult;
bool ltNaNBias;
RegLocation rlResult;
switch(mir->dalvikInsn.opCode) {
case OP_CMPL_FLOAT:
isDouble = false;
defaultResult = -1;
break;
case OP_CMPG_FLOAT:
isDouble = false;
defaultResult = 1;
break;
case OP_CMPL_DOUBLE:
isDouble = true;
defaultResult = -1;
break;
case OP_CMPG_DOUBLE:
isDouble = true;
defaultResult = 1;
break;
default:
return true;
}
if (isDouble) {
rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
clobberSReg(cUnit, rlDest.sRegLow);
rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
loadConstant(cUnit, rlResult.lowReg, defaultResult);
newLIR2(cUnit, kThumb2Vcmpd, S2D(rlSrc1.lowReg, r1Src2.highReg),
S2D(rlSrc2.lowReg, rlSrc2.highReg));
} else {
rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
clobberSReg(cUnit, rlDest.sRegLow);
rlResult = evalLoc(cUnit, rlDest, kCoreReg, true);
loadConstant(cUnit, rlResult.lowReg, defaultResult);
newLIR2(cUnit, kThumb2Vcmps, rlSrc1.lowReg, rlSrc2.lowReg);
}
assert(!FPREG(rlResult.lowReg));
newLIR0(cUnit, kThumb2Fmstat);
genIT(cUnit, (defaultResult == -1) ? kArmCondGt : kArmCondMi, "");
newLIR2(cUnit, kThumb2MovImmShift, rlResult.lowReg,
modifiedImmediate(-defaultResult)); // Must not alter ccodes
genIT(cUnit, kArmCondEq, "");
loadConstant(cUnit, rlResult.lowReg, 0);
storeValue(cUnit, rlDest, rlResult);
return false;
}