| /* |
| * 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(CompilationUnit *cUnit, MIR *mir) |
| { |
| return handleFmt20bc(cUnit, mir); |
| } |
| |
| static bool handleMethodFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) |
| { |
| return handleFmt21c_Fmt31c(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(CompilationUnit *cUnit, MIR *mir) |
| { |
| return handleFmt22c(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(CompilationUnit *cUnit, MIR *mir, |
| BasicBlock *bb, ArmLIR *labelList) |
| { |
| return handleFmt35c_3rc(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: |
| notHandled = handleMethodFmt20bc(cUnit, mir); |
| break; |
| case kFmt21c: |
| case kFmt31c: |
| notHandled = handleMethodFmt21c_Fmt31c(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: |
| notHandled = handleMethodFmt22c(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: |
| notHandled = handleMethodFmt35c_3rc(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 |
| } |