diff options
Diffstat (limited to 'vm/compiler/codegen/arm/CodegenCommon.cpp')
-rw-r--r-- | vm/compiler/codegen/arm/CodegenCommon.cpp | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/vm/compiler/codegen/arm/CodegenCommon.cpp b/vm/compiler/codegen/arm/CodegenCommon.cpp new file mode 100644 index 0000000..ae41fe9 --- /dev/null +++ b/vm/compiler/codegen/arm/CodegenCommon.cpp @@ -0,0 +1,447 @@ +/* + * 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. + */ + +/* + * This file contains codegen and support common to all supported + * ARM variants. It is included by: + * + * Codegen-$(TARGET_ARCH_VARIANT).c + * + * which combines this common code with specific support found in the + * applicable directory below this one. + */ + +#include "compiler/Loop.h" + +/* Array holding the entry offset of each template relative to the first one */ +static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK]; + +/* Track exercised opcodes */ +static int opcodeCoverage[kNumPackedOpcodes]; + +static void setMemRefType(ArmLIR *lir, bool isLoad, int memType) +{ + u8 *maskPtr; + u8 mask = ENCODE_MEM;; + assert(EncodingMap[lir->opcode].flags & (IS_LOAD | IS_STORE)); + if (isLoad) { + maskPtr = &lir->useMask; + } else { + maskPtr = &lir->defMask; + } + /* Clear out the memref flags */ + *maskPtr &= ~mask; + /* ..and then add back the one we need */ + switch(memType) { + case kLiteral: + assert(isLoad); + *maskPtr |= ENCODE_LITERAL; + break; + case kDalvikReg: + *maskPtr |= ENCODE_DALVIK_REG; + break; + case kHeapRef: + *maskPtr |= ENCODE_HEAP_REF; + break; + case kMustNotAlias: + /* Currently only loads can be marked as kMustNotAlias */ + assert(!(EncodingMap[lir->opcode].flags & IS_STORE)); + *maskPtr |= ENCODE_MUST_NOT_ALIAS; + break; + default: + LOGE("Jit: invalid memref kind - %d", memType); + assert(0); // Bail if debug build, set worst-case in the field + *maskPtr |= ENCODE_ALL; + } +} + +/* + * Mark load/store instructions that access Dalvik registers through r5FP + + * offset. + */ +static void annotateDalvikRegAccess(ArmLIR *lir, int regId, bool isLoad) +{ + setMemRefType(lir, isLoad, kDalvikReg); + + /* + * Store the Dalvik register id in aliasInfo. Mark he MSB if it is a 64-bit + * access. + */ + lir->aliasInfo = regId; + if (DOUBLEREG(lir->operands[0])) { + lir->aliasInfo |= 0x80000000; + } +} + +/* + * Decode the register id. + */ +static inline u8 getRegMaskCommon(int reg) +{ + u8 seed; + int shift; + int regId = reg & 0x1f; + + /* + * Each double register is equal to a pair of single-precision FP registers + */ + seed = DOUBLEREG(reg) ? 3 : 1; + /* FP register starts at bit position 16 */ + shift = FPREG(reg) ? kFPReg0 : 0; + /* Expand the double register id into single offset */ + shift += regId; + return (seed << shift); +} + +/* External version of getRegMaskCommon */ +u8 dvmGetRegResourceMask(int reg) +{ + return getRegMaskCommon(reg); +} + +/* + * Mark the corresponding bit(s). + */ +static inline void setupRegMask(u8 *mask, int reg) +{ + *mask |= getRegMaskCommon(reg); +} + +/* + * Set up the proper fields in the resource mask + */ +static void setupResourceMasks(ArmLIR *lir) +{ + int opcode = lir->opcode; + int flags; + + if (opcode <= 0) { + lir->useMask = lir->defMask = 0; + return; + } + + flags = EncodingMap[lir->opcode].flags; + + /* Set up the mask for resources that are updated */ + if (flags & (IS_LOAD | IS_STORE)) { + /* Default to heap - will catch specialized classes later */ + setMemRefType(lir, flags & IS_LOAD, kHeapRef); + } + + /* + * Conservatively assume the branch here will call out a function that in + * turn will trash everything. + */ + if (flags & IS_BRANCH) { + lir->defMask = lir->useMask = ENCODE_ALL; + return; + } + + if (flags & REG_DEF0) { + setupRegMask(&lir->defMask, lir->operands[0]); + } + + if (flags & REG_DEF1) { + setupRegMask(&lir->defMask, lir->operands[1]); + } + + if (flags & REG_DEF_SP) { + lir->defMask |= ENCODE_REG_SP; + } + + if (flags & REG_DEF_LR) { + lir->defMask |= ENCODE_REG_LR; + } + + if (flags & REG_DEF_LIST0) { + lir->defMask |= ENCODE_REG_LIST(lir->operands[0]); + } + + if (flags & REG_DEF_LIST1) { + lir->defMask |= ENCODE_REG_LIST(lir->operands[1]); + } + + if (flags & SETS_CCODES) { + lir->defMask |= ENCODE_CCODE; + } + + /* Conservatively treat the IT block */ + if (flags & IS_IT) { + lir->defMask = ENCODE_ALL; + } + + if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) { + int i; + + for (i = 0; i < 4; i++) { + if (flags & (1 << (kRegUse0 + i))) { + setupRegMask(&lir->useMask, lir->operands[i]); + } + } + } + + if (flags & REG_USE_PC) { + lir->useMask |= ENCODE_REG_PC; + } + + if (flags & REG_USE_SP) { + lir->useMask |= ENCODE_REG_SP; + } + + if (flags & REG_USE_LIST0) { + lir->useMask |= ENCODE_REG_LIST(lir->operands[0]); + } + + if (flags & REG_USE_LIST1) { + lir->useMask |= ENCODE_REG_LIST(lir->operands[1]); + } + + if (flags & USES_CCODES) { + lir->useMask |= ENCODE_CCODE; + } + + /* Fixup for kThumbPush/lr and kThumbPop/pc */ + if (opcode == kThumbPush || opcode == kThumbPop) { + u8 r8Mask = getRegMaskCommon(r8); + if ((opcode == kThumbPush) && (lir->useMask & r8Mask)) { + lir->useMask &= ~r8Mask; + lir->useMask |= ENCODE_REG_LR; + } else if ((opcode == kThumbPop) && (lir->defMask & r8Mask)) { + lir->defMask &= ~r8Mask; + lir->defMask |= ENCODE_REG_PC; + } + } +} + +/* + * Set up the accurate resource mask for branch instructions + */ +static void relaxBranchMasks(ArmLIR *lir) +{ + int flags = EncodingMap[lir->opcode].flags; + + /* Make sure only branch instructions are passed here */ + assert(flags & IS_BRANCH); + + lir->useMask = lir->defMask = ENCODE_REG_PC; + + if (flags & REG_DEF_LR) { + lir->defMask |= ENCODE_REG_LR; + } + + if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) { + int i; + + for (i = 0; i < 4; i++) { + if (flags & (1 << (kRegUse0 + i))) { + setupRegMask(&lir->useMask, lir->operands[i]); + } + } + } + + if (flags & USES_CCODES) { + lir->useMask |= ENCODE_CCODE; + } +} + +/* + * The following are building blocks to construct low-level IRs with 0 - 4 + * operands. + */ +static ArmLIR *newLIR0(CompilationUnit *cUnit, ArmOpcode opcode) +{ + ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); + assert(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & NO_OPERAND)); + insn->opcode = opcode; + setupResourceMasks(insn); + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static ArmLIR *newLIR1(CompilationUnit *cUnit, ArmOpcode opcode, + int dest) +{ + ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); + assert(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_UNARY_OP)); + insn->opcode = opcode; + insn->operands[0] = dest; + setupResourceMasks(insn); + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static ArmLIR *newLIR2(CompilationUnit *cUnit, ArmOpcode opcode, + int dest, int src1) +{ + ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); + assert(isPseudoOpcode(opcode) || + (EncodingMap[opcode].flags & IS_BINARY_OP)); + insn->opcode = opcode; + insn->operands[0] = dest; + insn->operands[1] = src1; + setupResourceMasks(insn); + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static ArmLIR *newLIR3(CompilationUnit *cUnit, ArmOpcode opcode, + int dest, int src1, int src2) +{ + ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); + if (!(EncodingMap[opcode].flags & IS_TERTIARY_OP)) { + LOGE("Bad LIR3: %s[%d]",EncodingMap[opcode].name,opcode); + } + assert(isPseudoOpcode(opcode) || + (EncodingMap[opcode].flags & IS_TERTIARY_OP)); + insn->opcode = opcode; + insn->operands[0] = dest; + insn->operands[1] = src1; + insn->operands[2] = src2; + setupResourceMasks(insn); + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +#if defined(_ARMV7_A) || defined(_ARMV7_A_NEON) +static ArmLIR *newLIR4(CompilationUnit *cUnit, ArmOpcode opcode, + int dest, int src1, int src2, int info) +{ + ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); + assert(isPseudoOpcode(opcode) || + (EncodingMap[opcode].flags & IS_QUAD_OP)); + insn->opcode = opcode; + insn->operands[0] = dest; + insn->operands[1] = src1; + insn->operands[2] = src2; + insn->operands[3] = info; + setupResourceMasks(insn); + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} +#endif + +/* + * If the next instruction is a move-result or move-result-long, + * return the target Dalvik sReg[s] and convert the next to a + * nop. Otherwise, return INVALID_SREG. Used to optimize method inlining. + */ +static RegLocation inlinedTarget(CompilationUnit *cUnit, MIR *mir, + bool fpHint) +{ + if (mir->next && + ((mir->next->dalvikInsn.opcode == OP_MOVE_RESULT) || + (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_OBJECT))) { + mir->next->dalvikInsn.opcode = OP_NOP; + return dvmCompilerGetDest(cUnit, mir->next, 0); + } else { + RegLocation res = LOC_DALVIK_RETURN_VAL; + res.fp = fpHint; + return res; + } +} + +/* + * Search the existing constants in the literal pool for an exact or close match + * within specified delta (greater or equal to 0). + */ +static ArmLIR *scanLiteralPool(LIR *dataTarget, int value, unsigned int delta) +{ + while (dataTarget) { + if (((unsigned) (value - ((ArmLIR *) dataTarget)->operands[0])) <= + delta) + return (ArmLIR *) dataTarget; + dataTarget = dataTarget->next; + } + return NULL; +} + +/* + * The following are building blocks to insert constants into the pool or + * instruction streams. + */ + +/* Add a 32-bit constant either in the constant pool or mixed with code */ +static ArmLIR *addWordData(CompilationUnit *cUnit, LIR **constantListP, + int value) +{ + /* Add the constant to the literal pool */ + if (constantListP) { + ArmLIR *newValue = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); + newValue->operands[0] = value; + newValue->generic.next = *constantListP; + *constantListP = (LIR *) newValue; + return newValue; + } else { + /* Add the constant in the middle of code stream */ + newLIR1(cUnit, kArm16BitData, (value & 0xffff)); + newLIR1(cUnit, kArm16BitData, (value >> 16)); + } + return NULL; +} + +static RegLocation inlinedTargetWide(CompilationUnit *cUnit, MIR *mir, + bool fpHint) +{ + if (mir->next && + (mir->next->dalvikInsn.opcode == OP_MOVE_RESULT_WIDE)) { + mir->next->dalvikInsn.opcode = OP_NOP; + return dvmCompilerGetDestWide(cUnit, mir->next, 0, 1); + } else { + RegLocation res = LOC_DALVIK_RETURN_VAL_WIDE; + res.fp = fpHint; + return res; + } +} + + +/* + * Generate an kArmPseudoBarrier marker to indicate the boundary of special + * blocks. + */ +static void genBarrier(CompilationUnit *cUnit) +{ + ArmLIR *barrier = newLIR0(cUnit, kArmPseudoBarrier); + /* Mark all resources as being clobbered */ + barrier->defMask = -1; +} + +/* Create the PC reconstruction slot if not already done */ +static ArmLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset, + ArmLIR *branch, + ArmLIR *pcrLabel) +{ + /* Forget all def info (because we might rollback here. Bug #2367397 */ + dvmCompilerResetDefTracking(cUnit); + + /* Set up the place holder to reconstruct this Dalvik PC */ + if (pcrLabel == NULL) { + int dPC = (int) (cUnit->method->insns + dOffset); + pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); + pcrLabel->opcode = kArmPseudoPCReconstructionCell; + pcrLabel->operands[0] = dPC; + pcrLabel->operands[1] = dOffset; + /* Insert the place holder to the growable list */ + dvmInsertGrowableList(&cUnit->pcReconstructionList, + (intptr_t) pcrLabel); + } + /* Branch to the PC reconstruction code */ + branch->generic.target = (LIR *) pcrLabel; + + /* Clear the conservative flags for branches that punt to the interpreter */ + relaxBranchMasks(branch); + + return pcrLabel; +} |