blob: 4ddad6903a1b530ae0f767b5a0ad7412ea37c21a [file] [log] [blame]
/*
* Copyright (C) 2011 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.
*/
/*
* Rebuild the interpreter frame then punt to the interpreter to execute
* instruction at specified PC.
*
* Currently parameters are passed to the current frame, so we just need to
* grow the stack save area above it, fill certain fields in StackSaveArea and
* Thread that are skipped during whole-method invocation (specified below),
* then return to the interpreter.
*
* StackSaveArea:
* - prevSave
* - prevFrame
* - savedPc
* - returnAddr
* - method
*
* Thread:
* - method
* - methodClassDex
* - curFrame
*/
static void genMethodInflateAndPunt(CompilationUnit *cUnit, MIR *mir,
BasicBlock *bb)
{
int oldStackSave = r0;
int newStackSave = r1;
int oldFP = r2;
int savedPC = r3;
int currentPC = r4PC;
int returnAddr = r7;
int method = r8;
int pDvmDex = r9;
/*
* TODO: check whether to raise the stack overflow exception when growing
* the stack save area.
*/
/* Send everything to home location */
dvmCompilerFlushAllRegs(cUnit);
/* oldStackSave = r5FP + sizeof(current frame) */
opRegRegImm(cUnit, kOpAdd, oldStackSave, r5FP,
cUnit->method->registersSize * 4);
/* oldFP = oldStackSave + sizeof(stackSaveArea) */
opRegRegImm(cUnit, kOpAdd, oldFP, oldStackSave, sizeof(StackSaveArea));
/* newStackSave = r5FP - sizeof(StackSaveArea) */
opRegRegImm(cUnit, kOpSub, newStackSave, r5FP, sizeof(StackSaveArea));
loadWordDisp(cUnit, r13sp, 0, savedPC);
loadConstant(cUnit, currentPC, (int) (cUnit->method->insns + mir->offset));
loadConstant(cUnit, method, (int) cUnit->method);
loadConstant(cUnit, pDvmDex, (int) cUnit->method->clazz->pDvmDex);
#ifdef EASY_GDB
/* newStackSave->prevSave = oldStackSave */
storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, prevSave),
oldStackSave);
#endif
/* newStackSave->prevSave = oldStackSave */
storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, prevFrame),
oldFP);
/* newStackSave->savedPc = savedPC */
storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, savedPc),
savedPC);
/* return address */
loadConstant(cUnit, returnAddr, 0);
storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, returnAddr),
returnAddr);
/* newStackSave->method = method */
storeWordDisp(cUnit, newStackSave, offsetof(StackSaveArea, method), method);
/* thread->method = method */
storeWordDisp(cUnit, r6SELF, offsetof(InterpSaveState, method), method);
/* thread->interpSave.curFrame = current FP */
storeWordDisp(cUnit, r6SELF, offsetof(Thread, interpSave.curFrame), r5FP);
/* thread->methodClassDex = pDvmDex */
storeWordDisp(cUnit, r6SELF, offsetof(InterpSaveState, methodClassDex),
pDvmDex);
/* Restore the stack pointer */
opRegImm(cUnit, kOpAdd, r13sp, 16);
genPuntToInterp(cUnit, mir->offset);
}
/*
* The following are the first-level codegen routines that analyze the format
* of each bytecode then either dispatch special purpose codegen routines
* or produce corresponding Thumb instructions directly.
*
* TODO - most them are just pass-through to the trace-based versions for now
*/
static bool handleMethodFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
BasicBlock *bb, ArmLIR *labelList)
{
/* backward branch? */
bool backwardBranch = (bb->taken->startOffset <= mir->offset);
if (backwardBranch && gDvmJit.genSuspendPoll) {
genSuspendPoll(cUnit, mir);
}
/* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
return false;
}
static bool handleMethodFmt10x(CompilationUnit *cUnit, MIR *mir)
{
Opcode dalvikOpcode = mir->dalvikInsn.opcode;
switch (dalvikOpcode) {
case OP_RETURN_VOID:
return false;
default:
return handleFmt10x(cUnit, mir);
}
}
static bool handleMethodFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt11n_Fmt31i(cUnit, mir);
}
static bool handleMethodFmt11x(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
ArmLIR *labelList)
{
Opcode dalvikOpcode = mir->dalvikInsn.opcode;
switch (dalvikOpcode) {
case OP_THROW:
genMethodInflateAndPunt(cUnit, mir, bb);
return false;
default:
return handleFmt11x(cUnit, mir);
}
}
static bool handleMethodFmt12x(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt12x(cUnit, mir);
}
static bool handleMethodFmt20bc_Fmt40sc(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt20bc_Fmt40sc(cUnit, mir);
}
static bool handleMethodFmt21c_Fmt31c_Fmt41c(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt21c_Fmt31c_Fmt41c(cUnit, mir);
}
static bool handleMethodFmt21h(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt21h(cUnit, mir);
}
static bool handleMethodFmt21s(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt21s(cUnit, mir);
}
static bool handleMethodFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
ArmLIR *labelList)
{
return handleFmt21t(cUnit, mir, bb, labelList);
}
static bool handleMethodFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt22b_Fmt22s(cUnit, mir);
}
static bool handleMethodFmt22c_Fmt52c(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt22c_Fmt52c(cUnit, mir);
}
static bool handleMethodFmt22cs(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt22cs(cUnit, mir);
}
static bool handleMethodFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
ArmLIR *labelList)
{
return handleFmt22t(cUnit, mir, bb, labelList);
}
static bool handleMethodFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt22x_Fmt32x(cUnit, mir);
}
static bool handleMethodFmt23x(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt23x(cUnit, mir);
}
static bool handleMethodFmt31t(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt31t(cUnit, mir);
}
static bool handleMethodFmt35c_3rc_5rc(CompilationUnit *cUnit, MIR *mir,
BasicBlock *bb, ArmLIR *labelList)
{
return handleFmt35c_3rc_5rc(cUnit, mir, bb, labelList);
}
static bool handleMethodFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
BasicBlock *bb, ArmLIR *labelList)
{
return handleFmt35ms_3rms(cUnit, mir, bb, labelList);
}
static bool handleMethodExecuteInline(CompilationUnit *cUnit, MIR *mir)
{
return handleExecuteInline(cUnit, mir);
}
static bool handleMethodFmt51l(CompilationUnit *cUnit, MIR *mir)
{
return handleFmt51l(cUnit, mir);
}
/* Handle the content in each basic block */
static bool methodBlockCodeGen(CompilationUnit *cUnit, BasicBlock *bb)
{
MIR *mir;
ArmLIR *labelList = (ArmLIR *) cUnit->blockLabelList;
int blockId = bb->id;
cUnit->curBlock = bb;
labelList[blockId].operands[0] = bb->startOffset;
/* Insert the block label */
labelList[blockId].opcode = kArmPseudoNormalBlockLabel;
dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
dvmCompilerClobberAllRegs(cUnit);
dvmCompilerResetNullCheck(cUnit);
ArmLIR *headLIR = NULL;
if (bb->blockType == kEntryBlock) {
/* r0 = callsitePC */
opImm(cUnit, kOpPush, (1 << r0 | 1 << r1 | 1 << r5FP | 1 << r14lr));
opRegImm(cUnit, kOpSub, r5FP,
sizeof(StackSaveArea) + cUnit->method->registersSize * 4);
} else if (bb->blockType == kExitBlock) {
/* No need to pop r0 and r1 */
opRegImm(cUnit, kOpAdd, r13sp, 8);
opImm(cUnit, kOpPop, (1 << r5FP | 1 << r15pc));
}
for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
dvmCompilerResetRegPool(cUnit);
if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
dvmCompilerClobberAllRegs(cUnit);
}
if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
dvmCompilerResetDefTracking(cUnit);
}
Opcode dalvikOpcode = mir->dalvikInsn.opcode;
InstructionFormat dalvikFormat =
dexGetFormatFromOpcode(dalvikOpcode);
ArmLIR *boundaryLIR;
/*
* Don't generate the boundary LIR unless we are debugging this
* trace or we need a scheduling barrier.
*/
if (headLIR == NULL || cUnit->printMe == true) {
boundaryLIR =
newLIR2(cUnit, kArmPseudoDalvikByteCodeBoundary,
mir->offset,
(int) dvmCompilerGetDalvikDisassembly(
&mir->dalvikInsn, ""));
/* Remember the first LIR for this block */
if (headLIR == NULL) {
headLIR = boundaryLIR;
/* Set the first boundaryLIR as a scheduling barrier */
headLIR->defMask = ENCODE_ALL;
}
}
/* Don't generate the SSA annotation unless verbose mode is on */
if (cUnit->printMe && mir->ssaRep) {
char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
}
bool notHandled;
switch (dalvikFormat) {
case kFmt10t:
case kFmt20t:
case kFmt30t:
notHandled = handleMethodFmt10t_Fmt20t_Fmt30t(cUnit, mir, bb,
labelList);
break;
case kFmt10x:
notHandled = handleMethodFmt10x(cUnit, mir);
break;
case kFmt11n:
case kFmt31i:
notHandled = handleMethodFmt11n_Fmt31i(cUnit, mir);
break;
case kFmt11x:
notHandled = handleMethodFmt11x(cUnit, mir, bb, labelList);
break;
case kFmt12x:
notHandled = handleMethodFmt12x(cUnit, mir);
break;
case kFmt20bc:
case kFmt40sc:
notHandled = handleMethodFmt20bc_Fmt40sc(cUnit, mir);
break;
case kFmt21c:
case kFmt31c:
case kFmt41c:
notHandled = handleMethodFmt21c_Fmt31c_Fmt41c(cUnit, mir);
break;
case kFmt21h:
notHandled = handleMethodFmt21h(cUnit, mir);
break;
case kFmt21s:
notHandled = handleMethodFmt21s(cUnit, mir);
break;
case kFmt21t:
notHandled = handleMethodFmt21t(cUnit, mir, bb, labelList);
break;
case kFmt22b:
case kFmt22s:
notHandled = handleMethodFmt22b_Fmt22s(cUnit, mir);
break;
case kFmt22c:
case kFmt52c:
notHandled = handleMethodFmt22c_Fmt52c(cUnit, mir);
break;
case kFmt22cs:
notHandled = handleMethodFmt22cs(cUnit, mir);
break;
case kFmt22t:
notHandled = handleMethodFmt22t(cUnit, mir, bb, labelList);
break;
case kFmt22x:
case kFmt32x:
notHandled = handleMethodFmt22x_Fmt32x(cUnit, mir);
break;
case kFmt23x:
notHandled = handleMethodFmt23x(cUnit, mir);
break;
case kFmt31t:
notHandled = handleMethodFmt31t(cUnit, mir);
break;
case kFmt3rc:
case kFmt35c:
case kFmt5rc:
notHandled = handleMethodFmt35c_3rc_5rc(cUnit, mir, bb,
labelList);
break;
case kFmt3rms:
case kFmt35ms:
notHandled = handleMethodFmt35ms_3rms(cUnit, mir, bb,
labelList);
break;
case kFmt35mi:
case kFmt3rmi:
notHandled = handleMethodExecuteInline(cUnit, mir);
break;
case kFmt51l:
notHandled = handleMethodFmt51l(cUnit, mir);
break;
default:
notHandled = true;
break;
}
/* FIXME - to be implemented */
if (notHandled == true && dalvikOpcode >= kNumPackedOpcodes) {
notHandled = false;
}
if (notHandled) {
LOGE("%#06x: Opcode %#x (%s) / Fmt %d not handled",
mir->offset,
dalvikOpcode, dexGetOpcodeName(dalvikOpcode),
dalvikFormat);
dvmCompilerAbort(cUnit);
break;
}
}
if (headLIR) {
/*
* Eliminate redundant loads/stores and delay stores into later
* slots
*/
dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
cUnit->lastLIRInsn);
/*
* Generate an unconditional branch to the fallthrough block.
*/
if (bb->fallThrough) {
genUnconditionalBranch(cUnit,
&labelList[bb->fallThrough->id]);
}
}
return false;
}
void dvmCompilerMethodMIR2LIR(CompilationUnit *cUnit)
{
// FIXME - enable method compilation for selected routines here
if (strcmp(cUnit->method->name, "add")) return;
/* Used to hold the labels of each block */
cUnit->blockLabelList =
(void *) dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
dvmCompilerDataFlowAnalysisDispatcher(cUnit, methodBlockCodeGen,
kPreOrderDFSTraversal,
false /* isIterative */);
dvmCompilerApplyGlobalOptimizations(cUnit);
// FIXME - temporarily enable verbose printing for all methods
cUnit->printMe = true;
#if defined(WITH_SELF_VERIFICATION)
selfVerificationBranchInsertPass(cUnit);
#endif
}