/* * 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. */ namespace art { void setMemRefType(LIR* lir, bool isLoad, int memType) { u8 *maskPtr; u8 mask = ENCODE_MEM;; DCHECK(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: DCHECK(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 */ DCHECK(!(EncodingMap[lir->opcode].flags & IS_STORE)); *maskPtr |= ENCODE_MUST_NOT_ALIAS; break; default: LOG(FATAL) << "Oat: invalid memref kind - " << memType; } } /* * Mark load/store instructions that access Dalvik registers through the stack. */ void annotateDalvikRegAccess(LIR* lir, int regId, bool isLoad, bool is64bit) { setMemRefType(lir, isLoad, kDalvikReg); /* * Store the Dalvik register id in aliasInfo. Mark the MSB if it is a 64-bit * access. */ lir->aliasInfo = regId; if (is64bit) { lir->aliasInfo |= 0x80000000; } } /* * Decode the register id. */ 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); } /* * Mark the corresponding bit(s). */ inline void setupRegMask(u8* mask, int reg) { *mask |= getRegMaskCommon(reg); } /* * Set up the proper fields in the resource mask */ void setupResourceMasks(LIR* lir) { int opcode = lir->opcode; int flags; if (opcode <= 0) { lir->useMask = lir->defMask = 0; return; } flags = EncodingMap[lir->opcode].flags; if (flags & NEEDS_FIXUP) { lir->flags.pcRelFixup = true; } /* Get the starting size of the instruction's template */ lir->flags.size = oatGetInsnSize(lir); /* 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 !defined(TARGET_X86) if (flags & REG_DEF_LR) { lir->defMask |= ENCODE_REG_LR; } #endif 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 defined(TARGET_ARM) if (flags & REG_DEF_FPCS_LIST0) { lir->defMask |= ENCODE_REG_FPCS_LIST(lir->operands[0]); } if (flags & REG_DEF_FPCS_LIST2) { for (int i = 0; i < lir->operands[2]; i++) { setupRegMask(&lir->defMask, lir->operands[1] + i); } } #endif if (flags & SETS_CCODES) { lir->defMask |= ENCODE_CCODE; } #if defined(TARGET_ARM) /* Conservatively treat the IT block */ if (flags & IS_IT) { lir->defMask = ENCODE_ALL; } #endif 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 defined(TARGET_ARM) if (flags & REG_USE_PC) { lir->useMask |= ENCODE_REG_PC; } #endif 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 defined(TARGET_ARM) if (flags & REG_USE_FPCS_LIST0) { lir->useMask |= ENCODE_REG_FPCS_LIST(lir->operands[0]); } if (flags & REG_USE_FPCS_LIST2) { for (int i = 0; i < lir->operands[2]; i++) { setupRegMask(&lir->useMask, lir->operands[1] + i); } } #endif if (flags & USES_CCODES) { lir->useMask |= ENCODE_CCODE; } #if defined(TARGET_ARM) /* 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; } } #endif } /* * Debugging macros */ #define DUMP_RESOURCE_MASK(X) #define DUMP_SSA_REP(X) /* Pretty-print a LIR instruction */ void oatDumpLIRInsn(CompilationUnit* cUnit, LIR* arg, unsigned char* baseAddr) { LIR* lir = (LIR*) arg; int offset = lir->offset; int dest = lir->operands[0]; const bool dumpNop = (cUnit->enableDebug & (1 << kDebugShowNops)); /* Handle pseudo-ops individually, and all regular insns as a group */ switch (lir->opcode) { case kPseudoMethodEntry: LOG(INFO) << "-------- method entry " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file); break; case kPseudoMethodExit: LOG(INFO) << "-------- Method_Exit"; break; case kPseudoBarrier: LOG(INFO) << "-------- BARRIER"; break; case kPseudoExtended: LOG(INFO) << "-------- " << (char* ) dest; break; case kPseudoSSARep: DUMP_SSA_REP(LOG(INFO) << "-------- kMirOpPhi: " << (char* ) dest); break; case kPseudoEntryBlock: LOG(INFO) << "-------- entry offset: 0x" << std::hex << dest; break; case kPseudoDalvikByteCodeBoundary: LOG(INFO) << "-------- dalvik offset: 0x" << std::hex << lir->dalvikOffset << " @ " << (char* )lir->operands[0]; break; case kPseudoExitBlock: LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest; break; case kPseudoPseudoAlign4: LOG(INFO) << (intptr_t)baseAddr + offset << " (0x" << std::hex << offset << "): .align4"; break; case kPseudoEHBlockLabel: LOG(INFO) << "Exception_Handling:"; break; case kPseudoTargetLabel: case kPseudoNormalBlockLabel: LOG(INFO) << "L" << (void*)lir << ":"; break; case kPseudoThrowTarget: LOG(INFO) << "LT" << (void*)lir << ":"; break; case kPseudoIntrinsicRetry: LOG(INFO) << "IR" << (void*)lir << ":"; break; case kPseudoSuspendTarget: LOG(INFO) << "LS" << (void*)lir << ":"; break; case kPseudoCaseLabel: LOG(INFO) << "LC" << (void*)lir << ": Case target 0x" << std::hex << lir->operands[0] << "|" << std::dec << lir->operands[0]; break; default: if (lir->flags.isNop && !dumpNop) { break; } else { std::string op_name(buildInsnString(EncodingMap[lir->opcode].name, lir, baseAddr)); std::string op_operands(buildInsnString(EncodingMap[lir->opcode].fmt, lir, baseAddr)); LOG(INFO) << StringPrintf("%05x: %-9s%s%s", (unsigned int)(baseAddr + offset), op_name.c_str(), op_operands.c_str(), lir->flags.isNop ? "(nop)" : ""); } break; } if (lir->useMask && (!lir->flags.isNop || dumpNop)) { DUMP_RESOURCE_MASK(oatDumpResourceMask((LIR* ) lir, lir->useMask, "use")); } if (lir->defMask && (!lir->flags.isNop || dumpNop)) { DUMP_RESOURCE_MASK(oatDumpResourceMask((LIR* ) lir, lir->defMask, "def")); } } void oatDumpPromotionMap(CompilationUnit *cUnit) { int numRegs = cUnit->numDalvikRegisters + cUnit->numCompilerTemps + 1; for (int i = 0; i < numRegs; i++) { PromotionMap vRegMap = cUnit->promotionMap[i]; std::string buf; if (vRegMap.fpLocation == kLocPhysReg) { StringAppendF(&buf, " : s%d", vRegMap.fpReg & FP_REG_MASK); } std::string buf3; if (i < cUnit->numDalvikRegisters) { StringAppendF(&buf3, "%02d", i); } else if (i == cUnit->methodSReg) { buf3 = "Method*"; } else { StringAppendF(&buf3, "ct%d", i - cUnit->numDalvikRegisters); } LOG(INFO) << StringPrintf("V[%s] -> %s%d%s", buf3.c_str(), vRegMap.coreLocation == kLocPhysReg ? "r" : "SP+", vRegMap.coreLocation == kLocPhysReg ? vRegMap.coreReg : oatSRegOffset(cUnit, i), buf.c_str()); } } /* Dump instructions and constant pool contents */ void oatCodegenDump(CompilationUnit* cUnit) { LOG(INFO) << "/*"; LOG(INFO) << "Dumping LIR insns for " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file); LIR* lirInsn; LIR* thisLIR; int insnsSize = cUnit->insnsSize; LOG(INFO) << "Regs (excluding ins) : " << cUnit->numRegs; LOG(INFO) << "Ins : " << cUnit->numIns; LOG(INFO) << "Outs : " << cUnit->numOuts; LOG(INFO) << "CoreSpills : " << cUnit->numCoreSpills; LOG(INFO) << "FPSpills : " << cUnit->numFPSpills; LOG(INFO) << "CompilerTemps : " << cUnit->numCompilerTemps; LOG(INFO) << "Frame size : " << cUnit->frameSize; LOG(INFO) << "code size is " << cUnit->totalSize << " bytes, Dalvik size is " << insnsSize * 2; LOG(INFO) << "expansion factor: " << (float)cUnit->totalSize / (float)(insnsSize * 2); oatDumpPromotionMap(cUnit); for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) { oatDumpLIRInsn(cUnit, lirInsn, 0); } for (lirInsn = cUnit->classPointerList; lirInsn; lirInsn = lirInsn->next) { thisLIR = (LIR*) lirInsn; LOG(INFO) << StringPrintf("%x (%04x): .class (%s)", thisLIR->offset, thisLIR->offset, ((CallsiteInfo *) thisLIR->operands[0])->classDescriptor); } for (lirInsn = cUnit->literalList; lirInsn; lirInsn = lirInsn->next) { thisLIR = (LIR*) lirInsn; LOG(INFO) << StringPrintf("%x (%04x): .word (%#x)", thisLIR->offset, thisLIR->offset, thisLIR->operands[0]); } const DexFile::MethodId& method_id = cUnit->dex_file->GetMethodId(cUnit->method_idx); std::string signature(cUnit->dex_file->GetMethodSignature(method_id)); std::string name(cUnit->dex_file->GetMethodName(method_id)); std::string descriptor(cUnit->dex_file->GetMethodDeclaringClassDescriptor(method_id)); // Dump mapping table if (cUnit->mappingTable.size() > 0) { std::string line(StringPrintf("\n MappingTable %s%s_%s_mappingTable[%zu] = {", descriptor.c_str(), name.c_str(), signature.c_str(), cUnit->mappingTable.size())); std::replace(line.begin(), line.end(), ';', '_'); LOG(INFO) << line; for (uint32_t i = 0; i < cUnit->mappingTable.size(); i+=2) { line = StringPrintf(" {0x%05x, 0x%04x},", cUnit->mappingTable[i], cUnit->mappingTable[i+1]); LOG(INFO) << line; } LOG(INFO) <<" };\n\n"; } } LIR* rawLIR(CompilationUnit* cUnit, int dalvikOffset, int opcode, int op0, int op1, int op2, int op3, int op4, LIR* target) { LIR* insn = (LIR* ) oatNew(cUnit, sizeof(LIR), true, kAllocLIR); insn->dalvikOffset = dalvikOffset; insn->opcode = opcode; insn->operands[0] = op0; insn->operands[1] = op1; insn->operands[2] = op2; insn->operands[3] = op3; insn->operands[4] = op4; insn->target = target; oatSetupResourceMasks(insn); if (opcode == kPseudoTargetLabel) { // Always make labels scheduling barriers insn->defMask = ENCODE_ALL; } return insn; } /* * The following are building blocks to construct low-level IRs with 0 - 4 * operands. */ LIR* newLIR0(CompilationUnit* cUnit, int opcode) { DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & NO_OPERAND)) << EncodingMap[opcode].name << " " << (int)opcode << " " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " " << cUnit->currentDalvikOffset; LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode); oatAppendLIR(cUnit, (LIR*) insn); return insn; } LIR* newLIR1(CompilationUnit* cUnit, int opcode, int dest) { DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_UNARY_OP)) << EncodingMap[opcode].name << " " << (int)opcode << " " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " " << cUnit->currentDalvikOffset; LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest); oatAppendLIR(cUnit, (LIR*) insn); return insn; } LIR* newLIR2(CompilationUnit* cUnit, int opcode, int dest, int src1) { DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_BINARY_OP)) << EncodingMap[opcode].name << " " << (int)opcode << " " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " " << cUnit->currentDalvikOffset; LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest, src1); oatAppendLIR(cUnit, (LIR*) insn); return insn; } LIR* newLIR3(CompilationUnit* cUnit, int opcode, int dest, int src1, int src2) { DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_TERTIARY_OP)) << EncodingMap[opcode].name << " " << (int)opcode << " " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " " << cUnit->currentDalvikOffset; LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest, src1, src2); oatAppendLIR(cUnit, (LIR*) insn); return insn; } LIR* newLIR4(CompilationUnit* cUnit, int opcode, int dest, int src1, int src2, int info) { DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_QUAD_OP)) << EncodingMap[opcode].name << " " << (int)opcode << " " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " " << cUnit->currentDalvikOffset; LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest, src1, src2, info); oatAppendLIR(cUnit, (LIR*) insn); return insn; } LIR* newLIR5(CompilationUnit* cUnit, int opcode, int dest, int src1, int src2, int info1, int info2) { DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_QUIN_OP)) << EncodingMap[opcode].name << " " << (int)opcode << " " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " " << cUnit->currentDalvikOffset; LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest, src1, src2, info1, info2); oatAppendLIR(cUnit, (LIR*) insn); return insn; } /* * Search the existing constants in the literal pool for an exact or close match * within specified delta (greater or equal to 0). */ LIR* scanLiteralPool(LIR* dataTarget, int value, unsigned int delta) { while (dataTarget) { if (((unsigned) (value - ((LIR* ) dataTarget)->operands[0])) <= delta) return (LIR* ) dataTarget; dataTarget = dataTarget->next; } return NULL; } /* Search the existing constants in the literal pool for an exact wide match */ LIR* scanLiteralPoolWide(LIR* dataTarget, int valLo, int valHi) { bool loMatch = false; LIR* loTarget = NULL; while (dataTarget) { if (loMatch && (((LIR*)dataTarget)->operands[0] == valHi)) { return (LIR*)loTarget; } loMatch = false; if (((LIR*)dataTarget)->operands[0] == valLo) { loMatch = true; loTarget = 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 */ LIR* addWordData(CompilationUnit* cUnit, LIR* *constantListP, int value) { /* Add the constant to the literal pool */ if (constantListP) { LIR* newValue = (LIR* ) oatNew(cUnit, sizeof(LIR), true, kAllocData); newValue->operands[0] = value; newValue->next = *constantListP; *constantListP = (LIR*) newValue; return newValue; } return NULL; } /* Add a 64-bit constant to the constant pool or mixed with code */ LIR* addWideData(CompilationUnit* cUnit, LIR* *constantListP, int valLo, int valHi) { //FIXME: hard-coded little endian, need BE variant // Insert high word into list first addWordData(cUnit, constantListP, valHi); return addWordData(cUnit, constantListP, valLo); } void pushWord(std::vector&buf, int data) { buf.push_back( data & 0xff); buf.push_back( (data >> 8) & 0xff); buf.push_back( (data >> 16) & 0xff); buf.push_back( (data >> 24) & 0xff); } void alignBuffer(std::vector&buf, size_t offset) { while (buf.size() < offset) { buf.push_back(0); } } bool IsDirect(int invokeType) { InvokeType type = static_cast(invokeType); return type == kStatic || type == kDirect; } /* Write the literal pool to the output stream */ void installLiteralPools(CompilationUnit* cUnit) { alignBuffer(cUnit->codeBuffer, cUnit->dataOffset); LIR* dataLIR = cUnit->literalList; while (dataLIR != NULL) { pushWord(cUnit->codeBuffer, dataLIR->operands[0]); dataLIR = NEXT_LIR(dataLIR); } // Push code and method literals, record offsets for the compiler to patch. dataLIR = cUnit->codeLiteralList; if (dataLIR != NULL) { while (dataLIR != NULL) { uint32_t target = dataLIR->operands[0]; cUnit->compiler->AddCodePatch(cUnit->dex_cache, cUnit->dex_file, cUnit->method_idx, cUnit->access_flags, target, IsDirect(dataLIR->operands[1]), cUnit->codeBuffer.size()); const DexFile::MethodId& id = cUnit->dex_file->GetMethodId(target); // unique based on target to ensure code deduplication works uint32_t unique_patch_value = reinterpret_cast(&id); pushWord(cUnit->codeBuffer, unique_patch_value); dataLIR = NEXT_LIR(dataLIR); } dataLIR = cUnit->methodLiteralList; while (dataLIR != NULL) { uint32_t target = dataLIR->operands[0]; cUnit->compiler->AddMethodPatch(cUnit->dex_cache, cUnit->dex_file, cUnit->method_idx, cUnit->access_flags, target, IsDirect(dataLIR->operands[1]), cUnit->codeBuffer.size()); const DexFile::MethodId& id = cUnit->dex_file->GetMethodId(target); // unique based on target to ensure code deduplication works uint32_t unique_patch_value = reinterpret_cast(&id); pushWord(cUnit->codeBuffer, unique_patch_value); dataLIR = NEXT_LIR(dataLIR); } } } /* Write the switch tables to the output stream */ void installSwitchTables(CompilationUnit* cUnit) { GrowableListIterator iterator; oatGrowableListIteratorInit(&cUnit->switchTables, &iterator); while (true) { SwitchTable* tabRec = (SwitchTable *) oatGrowableListIteratorNext( &iterator); if (tabRec == NULL) break; alignBuffer(cUnit->codeBuffer, tabRec->offset); /* * For Arm, our reference point is the address of the bx * instruction that does the launch, so we have to subtract * the auto pc-advance. For other targets the reference point * is a label, so we can use the offset as-is. */ #if defined(TARGET_ARM) int bxOffset = tabRec->anchor->offset + 4; #elif defined(TARGET_X86) int bxOffset = 0; #else int bxOffset = tabRec->anchor->offset; #endif if (cUnit->printMe) { LOG(INFO) << "Switch table for offset 0x" << std::hex << bxOffset; } if (tabRec->table[0] == Instruction::kSparseSwitchSignature) { int* keys = (int*)&(tabRec->table[2]); for (int elems = 0; elems < tabRec->table[1]; elems++) { int disp = tabRec->targets[elems]->offset - bxOffset; if (cUnit->printMe) { LOG(INFO) << " Case[" << elems << "] key: 0x" << std::hex << keys[elems] << ", disp: 0x" << std::hex << disp; } pushWord(cUnit->codeBuffer, keys[elems]); pushWord(cUnit->codeBuffer, tabRec->targets[elems]->offset - bxOffset); } } else { DCHECK_EQ(static_cast(tabRec->table[0]), static_cast(Instruction::kPackedSwitchSignature)); for (int elems = 0; elems < tabRec->table[1]; elems++) { int disp = tabRec->targets[elems]->offset - bxOffset; if (cUnit->printMe) { LOG(INFO) << " Case[" << elems << "] disp: 0x" << std::hex << disp; } pushWord(cUnit->codeBuffer, tabRec->targets[elems]->offset - bxOffset); } } } } /* Write the fill array dta to the output stream */ void installFillArrayData(CompilationUnit* cUnit) { GrowableListIterator iterator; oatGrowableListIteratorInit(&cUnit->fillArrayData, &iterator); while (true) { FillArrayData *tabRec = (FillArrayData *) oatGrowableListIteratorNext( &iterator); if (tabRec == NULL) break; alignBuffer(cUnit->codeBuffer, tabRec->offset); for (int i = 0; i < (tabRec->size + 1) / 2; i++) { cUnit->codeBuffer.push_back( tabRec->table[i] & 0xFF); cUnit->codeBuffer.push_back( (tabRec->table[i] >> 8) & 0xFF); } } } int assignLiteralOffsetCommon(LIR* lir, int offset) { for (;lir != NULL; lir = lir->next) { lir->offset = offset; offset += 4; } return offset; } void createMappingTable(CompilationUnit* cUnit) { LIR* tgtLIR; int currentDalvikOffset = -1; for (tgtLIR = (LIR *) cUnit->firstLIRInsn; tgtLIR; tgtLIR = NEXT_LIR(tgtLIR)) { if ((tgtLIR->opcode >= 0) && !tgtLIR->flags.isNop && (currentDalvikOffset != tgtLIR->dalvikOffset)) { // Changed - need to emit a record cUnit->mappingTable.push_back(tgtLIR->offset); cUnit->mappingTable.push_back(tgtLIR->dalvikOffset); currentDalvikOffset = tgtLIR->dalvikOffset; } } } /* Determine the offset of each literal field */ int assignLiteralOffset(CompilationUnit* cUnit, int offset) { offset = assignLiteralOffsetCommon(cUnit->literalList, offset); offset = assignLiteralOffsetCommon(cUnit->codeLiteralList, offset); offset = assignLiteralOffsetCommon(cUnit->methodLiteralList, offset); return offset; } int assignSwitchTablesOffset(CompilationUnit* cUnit, int offset) { GrowableListIterator iterator; oatGrowableListIteratorInit(&cUnit->switchTables, &iterator); while (true) { SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext( &iterator); if (tabRec == NULL) break; tabRec->offset = offset; if (tabRec->table[0] == Instruction::kSparseSwitchSignature) { offset += tabRec->table[1] * (sizeof(int) * 2); } else { DCHECK_EQ(static_cast(tabRec->table[0]), static_cast(Instruction::kPackedSwitchSignature)); offset += tabRec->table[1] * sizeof(int); } } return offset; } int assignFillArrayDataOffset(CompilationUnit* cUnit, int offset) { GrowableListIterator iterator; oatGrowableListIteratorInit(&cUnit->fillArrayData, &iterator); while (true) { FillArrayData *tabRec = (FillArrayData *) oatGrowableListIteratorNext( &iterator); if (tabRec == NULL) break; tabRec->offset = offset; offset += tabRec->size; // word align offset = (offset + 3) & ~3; } return offset; } /* * Walk the compilation unit and assign offsets to instructions * and literals and compute the total size of the compiled unit. */ void oatAssignOffsets(CompilationUnit* cUnit) { int offset = oatAssignInsnOffsets(cUnit); /* Const values have to be word aligned */ offset = (offset + 3) & ~3; /* Set up offsets for literals */ cUnit->dataOffset = offset; offset = assignLiteralOffset(cUnit, offset); offset = assignSwitchTablesOffset(cUnit, offset); offset = assignFillArrayDataOffset(cUnit, offset); cUnit->totalSize = offset; } /* * Go over each instruction in the list and calculate the offset from the top * before sending them off to the assembler. If out-of-range branch distance is * seen rearrange the instructions a bit to correct it. */ void oatAssembleLIR(CompilationUnit* cUnit) { oatAssignOffsets(cUnit); /* * Assemble here. Note that we generate code with optimistic assumptions * and if found now to work, we'll have to redo the sequence and retry. */ while (true) { AssemblerStatus res = oatAssembleInstructions(cUnit, 0); if (res == kSuccess) { break; } else { cUnit->assemblerRetries++; if (cUnit->assemblerRetries > MAX_ASSEMBLER_RETRIES) { oatCodegenDump(cUnit); LOG(FATAL) << "Assembler error - too many retries"; } // Redo offsets and try again oatAssignOffsets(cUnit); cUnit->codeBuffer.clear(); } } // Install literals installLiteralPools(cUnit); // Install switch tables installSwitchTables(cUnit); // Install fill array data installFillArrayData(cUnit); /* * Create the mapping table */ createMappingTable(cUnit); } /* * Insert a kPseudoCaseLabel at the beginning of the Dalvik * offset vaddr. This label will be used to fix up the case * branch table during the assembly phase. Be sure to set * all resource flags on this to prevent code motion across * target boundaries. KeyVal is just there for debugging. */ LIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal) { SafeMap::iterator it; it = cUnit->boundaryMap.find(vaddr); if (it == cUnit->boundaryMap.end()) { LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr; } LIR* newLabel = (LIR*)oatNew(cUnit, sizeof(LIR), true, kAllocLIR); newLabel->dalvikOffset = vaddr; newLabel->opcode = kPseudoCaseLabel; newLabel->operands[0] = keyVal; oatInsertLIRAfter(it->second, (LIR*)newLabel); return newLabel; } void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec) { const u2* table = tabRec->table; int baseVaddr = tabRec->vaddr; int *targets = (int*)&table[4]; int entries = table[1]; int lowKey = s4FromSwitchData(&table[2]); for (int i = 0; i < entries; i++) { tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i], i + lowKey); } } void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec) { const u2* table = tabRec->table; int baseVaddr = tabRec->vaddr; int entries = table[1]; int* keys = (int*)&table[2]; int* targets = &keys[entries]; for (int i = 0; i < entries; i++) { tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i], keys[i]); } } void oatProcessSwitchTables(CompilationUnit* cUnit) { GrowableListIterator iterator; oatGrowableListIteratorInit(&cUnit->switchTables, &iterator); while (true) { SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext( &iterator); if (tabRec == NULL) break; if (tabRec->table[0] == Instruction::kPackedSwitchSignature) { markPackedCaseLabels(cUnit, tabRec); } else if (tabRec->table[0] == Instruction::kSparseSwitchSignature) { markSparseCaseLabels(cUnit, tabRec); } else { LOG(FATAL) << "Invalid switch table"; } } } //FIXME: Do we have endian issues here? void dumpSparseSwitchTable(const u2* table) /* * Sparse switch data format: * ushort ident = 0x0200 magic value * ushort size number of entries in the table; > 0 * int keys[size] keys, sorted low-to-high; 32-bit aligned * int targets[size] branch targets, relative to switch opcode * * Total size is (2+size*4) 16-bit code units. */ { u2 ident = table[0]; int entries = table[1]; int* keys = (int*)&table[2]; int* targets = &keys[entries]; LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident << ", entries: " << std::dec << entries; for (int i = 0; i < entries; i++) { LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex << targets[i]; } } void dumpPackedSwitchTable(const u2* table) /* * Packed switch data format: * ushort ident = 0x0100 magic value * ushort size number of entries in the table * int first_key first (and lowest) switch case value * int targets[size] branch targets, relative to switch opcode * * Total size is (4+size*2) 16-bit code units. */ { u2 ident = table[0]; int* targets = (int*)&table[4]; int entries = table[1]; int lowKey = s4FromSwitchData(&table[2]); LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident << ", entries: " << std::dec << entries << ", lowKey: " << lowKey; for (int i = 0; i < entries; i++) { LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex << targets[i]; } } } // namespace art