/* Write the numbers in the literal pool to the codegen stream */ static void installDataContent(CompilationUnit *cUnit) { int *dataPtr = (int *) ((char *) cUnit->baseAddr + cUnit->dataOffset); ArmLIR *dataLIR = (ArmLIR *) cUnit->wordList; while (dataLIR) { *dataPtr++ = dataLIR->operands[0]; dataLIR = NEXT_LIR(dataLIR); } }
/* * Identify unconditional branches that jump to the immediate successor of the * branch itself. */ static void applyRedundantBranchElimination(CompilationUnit *cUnit) { ArmLIR *thisLIR; for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn; thisLIR != (ArmLIR *) cUnit->lastLIRInsn; thisLIR = NEXT_LIR(thisLIR)) { /* Branch to the next instruction */ if (thisLIR->opcode == kThumbBUncond) { ArmLIR *nextLIR = thisLIR; while (true) { nextLIR = NEXT_LIR(nextLIR); /* * Is the branch target the next instruction? */ if (nextLIR == (ArmLIR *) thisLIR->generic.target) { thisLIR->flags.isNop = true; break; } /* * Found real useful stuff between the branch and the target. * Need to explicitly check the lastLIRInsn here since with * method-based JIT the branch might be the last real * instruction. */ if (!isPseudoOpcode(nextLIR->opcode) || (nextLIR == (ArmLIR *) cUnit->lastLIRInsn)) break; } } } }
/* Return TRUE if error happens */ static bool assembleInstructions(CompilationUnit *cUnit, intptr_t startAddr) { short *bufferAddr = (short *) cUnit->codeBuffer; ArmLIR *lir; for (lir = (ArmLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) { if (lir->opCode < 0) { if ((lir->opCode == ARM_PSEUDO_ALIGN4) && /* 1 means padding is needed */ (lir->operands[0] == 1)) { *bufferAddr++ = PADDING_MOV_R0_R0; } continue; } if (lir->isNop) { continue; } if (lir->opCode == THUMB_LDR_PC_REL || lir->opCode == THUMB_ADD_PC_REL) { ArmLIR *lirTarget = (ArmLIR *) lir->generic.target; intptr_t pc = (lir->generic.offset + 4) & ~3; /* * Allow an offset (stored in operands[2] to be added to the * PC-relative target. Useful to get to a fixed field inside a * chaining cell. */ intptr_t target = lirTarget->generic.offset + lir->operands[2]; int delta = target - pc; if (delta & 0x3) { LOGE("PC-rel distance is not multiples of 4: %d\n", delta); dvmAbort(); } if (delta > 1023) { return true; } lir->operands[1] = delta >> 2; } else if (lir->opCode == THUMB2_CBNZ || lir->opCode == THUMB2_CBZ) {
/* * Perform a pass of top-down walk, from the second-last instruction in the * superblock, to eliminate redundant loads and stores. * * An earlier load can eliminate a later load iff * 1) They are must-aliases * 2) The native register is not clobbered in between * 3) The memory location is not written to in between * * An earlier store can eliminate a later load iff * 1) They are must-aliases * 2) The native register is not clobbered in between * 3) The memory location is not written to in between * * A later store can be eliminated by an earlier store iff * 1) They are must-aliases * 2) The memory location is not written to in between */ static void applyLoadStoreElimination(CompilationUnit *cUnit, MipsLIR *headLIR, MipsLIR *tailLIR) { MipsLIR *thisLIR; if (headLIR == tailLIR) return; for (thisLIR = PREV_LIR(tailLIR); thisLIR != headLIR; thisLIR = PREV_LIR(thisLIR)) { int sinkDistance = 0; /* Skip non-interesting instructions */ if ((thisLIR->flags.isNop == true) || isPseudoOpCode(thisLIR->opcode) || !(EncodingMap[thisLIR->opcode].flags & (IS_LOAD | IS_STORE))) { continue; } int nativeRegId = thisLIR->operands[0]; bool isThisLIRLoad = EncodingMap[thisLIR->opcode].flags & IS_LOAD; MipsLIR *checkLIR; /* Use the mem mask to determine the rough memory location */ u8 thisMemMask = (thisLIR->useMask | thisLIR->defMask) & ENCODE_MEM; /* * Currently only eliminate redundant ld/st for constant and Dalvik * register accesses. */ if (!(thisMemMask & (ENCODE_LITERAL | ENCODE_DALVIK_REG))) continue; /* * Add r15 (pc) to the resource mask to prevent this instruction * from sinking past branch instructions. Also take out the memory * region bits since stopMask is used to check data/control * dependencies. */ u8 stopUseRegMask = (ENCODE_REG_PC | thisLIR->useMask) & ~ENCODE_MEM; u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM; for (checkLIR = NEXT_LIR(thisLIR); checkLIR != tailLIR; checkLIR = NEXT_LIR(checkLIR)) { /* * Skip already dead instructions (whose dataflow information is * outdated and misleading). */ if (checkLIR->flags.isNop) continue; u8 checkMemMask = (checkLIR->useMask | checkLIR->defMask) & ENCODE_MEM; u8 aliasCondition = thisMemMask & checkMemMask; bool stopHere = false; /* * Potential aliases seen - check the alias relations */ if (checkMemMask != ENCODE_MEM && aliasCondition != 0) { bool isCheckLIRLoad = EncodingMap[checkLIR->opcode].flags & IS_LOAD; if (aliasCondition == ENCODE_LITERAL) { /* * Should only see literal loads in the instruction * stream. */ assert(!(EncodingMap[checkLIR->opcode].flags & IS_STORE)); /* Same value && same register type */ if (checkLIR->aliasInfo == thisLIR->aliasInfo && REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId)){ /* * Different destination register - insert * a move */ if (checkLIR->operands[0] != nativeRegId) { convertMemOpIntoMove(cUnit, checkLIR, checkLIR->operands[0], nativeRegId); } checkLIR->flags.isNop = true; } } else if (aliasCondition == ENCODE_DALVIK_REG) { /* Must alias */ if (checkLIR->aliasInfo == thisLIR->aliasInfo) { /* Only optimize compatible registers */ bool regCompatible = REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId); if ((isThisLIRLoad && isCheckLIRLoad) || (!isThisLIRLoad && isCheckLIRLoad)) { /* RAR or RAW */ if (regCompatible) { /* * Different destination register - * insert a move */ if (checkLIR->operands[0] != nativeRegId) { convertMemOpIntoMove(cUnit, checkLIR, checkLIR->operands[0], nativeRegId); } checkLIR->flags.isNop = true; } else { /* * Destinaions are of different types - * something complicated going on so * stop looking now. */ stopHere = true; } } else if (isThisLIRLoad && !isCheckLIRLoad) { /* WAR - register value is killed */ stopHere = true; } else if (!isThisLIRLoad && !isCheckLIRLoad) { /* WAW - nuke the earlier store */ thisLIR->flags.isNop = true; stopHere = true; } /* Partial overlap */ } else if (isDalvikRegisterClobbered(thisLIR, checkLIR)) { /* * It is actually ok to continue if checkLIR * is a read. But it is hard to make a test * case for this so we just stop here to be * conservative. */ stopHere = true; } } /* Memory content may be updated. Stop looking now. */ if (stopHere) { break; /* The checkLIR has been transformed - check the next one */ } else if (checkLIR->flags.isNop) { continue; } } /* * this and check LIRs have no memory dependency. Now check if * their register operands have any RAW, WAR, and WAW * dependencies. If so, stop looking. */ if (stopHere == false) { stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask, checkLIR); } if (stopHere == true) { DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR, "REG CLOBBERED")); /* Only sink store instructions */ if (sinkDistance && !isThisLIRLoad) { MipsLIR *newStoreLIR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true); *newStoreLIR = *thisLIR; /* * Stop point found - insert *before* the checkLIR * since the instruction list is scanned in the * top-down order. */ dvmCompilerInsertLIRBefore((LIR *) checkLIR, (LIR *) newStoreLIR); thisLIR->flags.isNop = true; } break; } else if (!checkLIR->flags.isNop) { sinkDistance++; } } } }
/* * Perform a pass of bottom-up walk, from the second instruction in the * superblock, to try to hoist loads to earlier slots. */ static void applyLoadHoisting(CompilationUnit *cUnit, MipsLIR *headLIR, MipsLIR *tailLIR) { MipsLIR *thisLIR, *checkLIR; /* * Store the list of independent instructions that can be hoisted past. * Will decide the best place to insert later. */ MipsLIR *prevInstList[MAX_HOIST_DISTANCE]; /* Empty block */ if (headLIR == tailLIR) return; /* Start from the second instruction */ for (thisLIR = NEXT_LIR(headLIR); thisLIR != tailLIR; thisLIR = NEXT_LIR(thisLIR)) { /* Skip non-interesting instructions */ if ((thisLIR->flags.isNop == true) || isPseudoOpCode(thisLIR->opcode) || !(EncodingMap[thisLIR->opcode].flags & IS_LOAD)) { continue; } u8 stopUseAllMask = thisLIR->useMask; /* * Branches for null/range checks are marked with the true resource * bits, and loads to Dalvik registers, constant pools, and non-alias * locations are safe to be hoisted. So only mark the heap references * conservatively here. */ if (stopUseAllMask & ENCODE_HEAP_REF) { stopUseAllMask |= ENCODE_REG_PC; } /* Similar as above, but just check for pure register dependency */ u8 stopUseRegMask = stopUseAllMask & ~ENCODE_MEM; u8 stopDefRegMask = thisLIR->defMask & ~ENCODE_MEM; int nextSlot = 0; bool stopHere = false; /* Try to hoist the load to a good spot */ for (checkLIR = PREV_LIR(thisLIR); checkLIR != headLIR; checkLIR = PREV_LIR(checkLIR)) { /* * Skip already dead instructions (whose dataflow information is * outdated and misleading). */ if (checkLIR->flags.isNop) continue; u8 checkMemMask = checkLIR->defMask & ENCODE_MEM; u8 aliasCondition = stopUseAllMask & checkMemMask; stopHere = false; /* Potential WAR alias seen - check the exact relation */ if (checkMemMask != ENCODE_MEM && aliasCondition != 0) { /* We can fully disambiguate Dalvik references */ if (aliasCondition == ENCODE_DALVIK_REG) { /* Must alias or partually overlap */ if ((checkLIR->aliasInfo == thisLIR->aliasInfo) || isDalvikRegisterClobbered(thisLIR, checkLIR)) { stopHere = true; } /* Conservatively treat all heap refs as may-alias */ } else { assert(aliasCondition == ENCODE_HEAP_REF); stopHere = true; } /* Memory content may be updated. Stop looking now. */ if (stopHere) { prevInstList[nextSlot++] = checkLIR; break; } } if (stopHere == false) { stopHere = CHECK_REG_DEP(stopUseRegMask, stopDefRegMask, checkLIR); } /* * Store the dependent or non-pseudo/indepedent instruction to the * list. */ if (stopHere || !isPseudoOpCode(checkLIR->opcode)) { prevInstList[nextSlot++] = checkLIR; if (nextSlot == MAX_HOIST_DISTANCE) break; } /* Found a new place to put the load - move it here */ if (stopHere == true) { DEBUG_OPT(dumpDependentInsnPair(checkLIR, thisLIR "HOIST STOP")); break; } } /* * Reached the top - use headLIR as the dependent marker as all labels * are barriers. */ if (stopHere == false && nextSlot < MAX_HOIST_DISTANCE) { prevInstList[nextSlot++] = headLIR; } /* * At least one independent instruction is found. Scan in the reversed * direction to find a beneficial slot. */ if (nextSlot >= 2) { int firstSlot = nextSlot - 2; int slot; MipsLIR *depLIR = prevInstList[nextSlot-1]; /* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */ if (!isPseudoOpCode(depLIR->opcode) && (EncodingMap[depLIR->opcode].flags & IS_LOAD)) { firstSlot -= LDLD_DISTANCE; } /* * Make sure we check slot >= 0 since firstSlot may be negative * when the loop is first entered. */ for (slot = firstSlot; slot >= 0; slot--) { MipsLIR *curLIR = prevInstList[slot]; MipsLIR *prevLIR = prevInstList[slot+1]; /* * Check the highest instruction. * ENCODE_ALL represents a scheduling barrier. */ if (prevLIR->defMask == ENCODE_ALL) { /* * If the first instruction is a load, don't hoist anything * above it since it is unlikely to be beneficial. */ if (EncodingMap[curLIR->opcode].flags & IS_LOAD) continue; /* * Need to unconditionally break here even if the hoisted * distance is greater than LD_LATENCY (ie more than enough * cycles are inserted to hide the load latency) since theu * subsequent code doesn't expect to compare against a * pseudo opcode (whose opcode value is negative). */ break; } /* * NOTE: now prevLIR is guaranteed to be a non-pseudo * instruction (ie accessing EncodingMap[prevLIR->opcode] is * safe). * * Try to find two instructions with load/use dependency until * the remaining instructions are less than LD_LATENCY. */ if (((curLIR->useMask & prevLIR->defMask) && (EncodingMap[prevLIR->opcode].flags & IS_LOAD)) || (slot < LD_LATENCY)) { break; } } /* Found a slot to hoist to */ if (slot >= 0) { MipsLIR *curLIR = prevInstList[slot]; MipsLIR *newLoadLIR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true); *newLoadLIR = *thisLIR; /* * Insertion is guaranteed to succeed since checkLIR * is never the first LIR on the list */ dvmCompilerInsertLIRBefore((LIR *) curLIR, (LIR *) newLoadLIR); thisLIR->flags.isNop = true; } } } }
/* * Perform a pass of top-down walk to * 1) Eliminate redundant loads and stores * 2) Sink stores to latest possible slot */ static void applyLoadStoreElimination(CompilationUnit *cUnit, ArmLIR *headLIR, ArmLIR *tailLIR) { ArmLIR *thisLIR; cUnit->optRound++; for (thisLIR = headLIR; thisLIR != tailLIR; thisLIR = NEXT_LIR(thisLIR)) { /* Skip newly added instructions */ if (thisLIR->age >= cUnit->optRound) { continue; } if (isDalvikStore(thisLIR)) { int dRegId = DECODE_ALIAS_INFO_REG(thisLIR->aliasInfo); int dRegIdHi = dRegId + DECODE_ALIAS_INFO_WIDE(thisLIR->aliasInfo); int nativeRegId = thisLIR->operands[0]; ArmLIR *checkLIR; int sinkDistance = 0; /* * Add r15 (pc) to the mask to prevent this instruction * from sinking past branch instructions. Unset the Dalvik register * bit when checking with native resource constraints. */ u8 stopMask = (ENCODE_REG_PC | thisLIR->useMask) & ~ENCODE_DALVIK_REG; for (checkLIR = NEXT_LIR(thisLIR); checkLIR != tailLIR; checkLIR = NEXT_LIR(checkLIR)) { /* Check if a Dalvik register load is redundant */ if (isDalvikLoad(checkLIR) && (checkLIR->aliasInfo == thisLIR->aliasInfo) && (REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId))) { /* Insert a move to replace the load */ if (checkLIR->operands[0] != nativeRegId) { ArmLIR *moveLIR; moveLIR = dvmCompilerRegCopyNoInsert( cUnit, checkLIR->operands[0], nativeRegId); /* * Insert the converted checkLIR instruction after the * the original checkLIR since the optimization is * scannng in the top-down order and the new instruction * will need to be checked. */ dvmCompilerInsertLIRAfter((LIR *) checkLIR, (LIR *) moveLIR); } checkLIR->isNop = true; continue; /* * Found a true output dependency - nuke the previous store. * The register type doesn't matter here. */ } else if (isDalvikStore(checkLIR) && (checkLIR->aliasInfo == thisLIR->aliasInfo)) { thisLIR->isNop = true; break; /* Find out the latest slot that the store can be sunk into */ } else { /* Last instruction reached */ bool stopHere = (NEXT_LIR(checkLIR) == tailLIR); /* Store data is clobbered */ stopHere |= ((stopMask & checkLIR->defMask) != 0); /* Store data partially clobbers the Dalvik register */ if (stopHere == false && ((checkLIR->useMask | checkLIR->defMask) & ENCODE_DALVIK_REG)) { stopHere = isDalvikRegisterClobbered(thisLIR, checkLIR); } /* Found a new place to put the store - move it here */ if (stopHere == true) { DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR, "SINK STORE")); /* The store can be sunk for at least one cycle */ if (sinkDistance != 0) { ArmLIR *newStoreLIR = dvmCompilerNew(sizeof(ArmLIR), true); *newStoreLIR = *thisLIR; newStoreLIR->age = cUnit->optRound; /* * Stop point found - insert *before* the checkLIR * since the instruction list is scanned in the * top-down order. */ dvmCompilerInsertLIRBefore((LIR *) checkLIR, (LIR *) newStoreLIR); thisLIR->isNop = true; } break; } /* * Saw a real instruction that the store can be sunk after */ if (!isPseudoOpCode(checkLIR->opCode)) { sinkDistance++; } } } } } }
static void applyLoadHoisting(CompilationUnit *cUnit, ArmLIR *headLIR, ArmLIR *tailLIR) { ArmLIR *thisLIR; /* * Don't want to hoist in front of first load following a barrier (or * first instruction of the block. */ bool firstLoad = true; int maxHoist = dvmCompilerTargetOptHint(kMaxHoistDistance); cUnit->optRound++; for (thisLIR = headLIR; thisLIR != tailLIR; thisLIR = NEXT_LIR(thisLIR)) { /* Skip newly added instructions */ if (thisLIR->age >= cUnit->optRound || thisLIR->isNop == true) { continue; } if (firstLoad && (EncodingMap[thisLIR->opCode].flags & IS_LOAD)) { /* * Ensure nothing will be hoisted in front of this load because * it's result will likely be needed soon. */ thisLIR->defMask |= ENCODE_MEM_USE; firstLoad = false; } firstLoad |= (thisLIR->defMask == ENCODE_ALL); if (isDalvikLoad(thisLIR)) { int dRegId = DECODE_ALIAS_INFO_REG(thisLIR->aliasInfo); int dRegIdHi = dRegId + DECODE_ALIAS_INFO_WIDE(thisLIR->aliasInfo); int nativeRegId = thisLIR->operands[0]; ArmLIR *checkLIR; int hoistDistance = 0; u8 stopUseMask = (ENCODE_REG_PC | thisLIR->useMask); u8 stopDefMask = thisLIR->defMask; u8 checkResult; /* First check if the load can be completely elinimated */ for (checkLIR = PREV_LIR(thisLIR); checkLIR != headLIR; checkLIR = PREV_LIR(checkLIR)) { if (checkLIR->isNop) continue; /* * Check if the Dalvik register is previously accessed * with exactly the same type. */ if ((isDalvikLoad(checkLIR) || isDalvikStore(checkLIR)) && (checkLIR->aliasInfo == thisLIR->aliasInfo) && (checkLIR->operands[0] == nativeRegId)) { /* * If it is previously accessed but with a different type, * the search will terminate later at the point checking * for partially overlapping stores. */ thisLIR->isNop = true; break; } /* * No earlier use/def can reach this load if: * 1) Head instruction is reached */ if (checkLIR == headLIR) { break; } checkResult = (stopUseMask | stopDefMask) & checkLIR->defMask; /* * If both instructions are verified Dalvik accesses, clear the * may- and must-alias bits to detect true resource * dependencies. */ if (checkResult & ENCODE_DALVIK_REG) { checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF); } /* * 2) load target register is clobbered * 3) A branch is seen (stopUseMask has the PC bit set). */ if (checkResult) { break; } /* Store data partially clobbers the Dalvik register */ if (isDalvikStore(checkLIR) && isDalvikRegisterClobbered(thisLIR, checkLIR)) { break; } } /* The load has been eliminated */ if (thisLIR->isNop) continue; /* * The load cannot be eliminated. See if it can be hoisted to an * earlier spot. */ for (checkLIR = PREV_LIR(thisLIR); /* empty by intention */; checkLIR = PREV_LIR(checkLIR)) { if (checkLIR->isNop) continue; /* * Check if the "thisLIR" load is redundant * NOTE: At one point, we also triggered if the checkLIR * instruction was a load. However, that tended to insert * a load/use dependency because the full scheduler is * not yet complete. When it is, we chould also trigger * on loads. */ if (isDalvikStore(checkLIR) && (checkLIR->aliasInfo == thisLIR->aliasInfo) && (REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId))) { /* Insert a move to replace the load */ if (checkLIR->operands[0] != nativeRegId) { ArmLIR *moveLIR; moveLIR = dvmCompilerRegCopyNoInsert( cUnit, nativeRegId, checkLIR->operands[0]); /* * Convert *thisLIR* load into a move */ dvmCompilerInsertLIRAfter((LIR *) checkLIR, (LIR *) moveLIR); } thisLIR->isNop = true; break; /* Find out if the load can be yanked past the checkLIR */ } else { /* Last instruction reached */ bool stopHere = (checkLIR == headLIR); /* Base address is clobbered by checkLIR */ checkResult = stopUseMask & checkLIR->defMask; if (checkResult & ENCODE_DALVIK_REG) { checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF); } stopHere |= (checkResult != 0); /* Load target clobbers use/def in checkLIR */ checkResult = stopDefMask & (checkLIR->useMask | checkLIR->defMask); if (checkResult & ENCODE_DALVIK_REG) { checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF); } stopHere |= (checkResult != 0); /* Store data partially clobbers the Dalvik register */ if (stopHere == false && (checkLIR->defMask & ENCODE_DALVIK_REG)) { stopHere = isDalvikRegisterClobbered(thisLIR, checkLIR); } /* * Stop at an earlier Dalvik load if the offset of checkLIR * is not less than thisLIR * * Experiments show that doing * * ldr r1, [r5, #16] * ldr r0, [r5, #20] * * is much faster than * * ldr r0, [r5, #20] * ldr r1, [r5, #16] */ if (isDalvikLoad(checkLIR)) { int dRegId2 = DECODE_ALIAS_INFO_REG(checkLIR->aliasInfo); if (dRegId2 <= dRegId) { stopHere = true; } } /* Don't go too far */ stopHere |= (hoistDistance >= maxHoist); /* Found a new place to put the load - move it here */ if (stopHere == true) { DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR, "HOIST LOAD")); /* The load can be hoisted for at least one cycle */ if (hoistDistance != 0) { ArmLIR *newLoadLIR = dvmCompilerNew(sizeof(ArmLIR), true); *newLoadLIR = *thisLIR; newLoadLIR->age = cUnit->optRound; /* * Stop point found - insert *after* the checkLIR * since the instruction list is scanned in the * bottom-up order. */ dvmCompilerInsertLIRAfter((LIR *) checkLIR, (LIR *) newLoadLIR); thisLIR->isNop = true; } break; } /* * Saw a real instruction that hosting the load is * beneficial */ if (!isPseudoOpCode(checkLIR->opCode)) { hoistDistance++; } } } } else if (isLiteralLoad(thisLIR)) { int litVal = thisLIR->aliasInfo; int nativeRegId = thisLIR->operands[0]; ArmLIR *checkLIR; int hoistDistance = 0; u8 stopUseMask = (ENCODE_REG_PC | thisLIR->useMask) & ~ENCODE_LITPOOL_REF; u8 stopDefMask = thisLIR->defMask & ~ENCODE_LITPOOL_REF; /* First check if the load can be completely elinimated */ for (checkLIR = PREV_LIR(thisLIR); checkLIR != headLIR; checkLIR = PREV_LIR(checkLIR)) { if (checkLIR->isNop) continue; /* Reloading same literal into same tgt reg? Eliminate if so */ if (isLiteralLoad(checkLIR) && (checkLIR->aliasInfo == litVal) && (checkLIR->operands[0] == nativeRegId)) { thisLIR->isNop = true; break; } /* * No earlier use/def can reach this load if: * 1) Head instruction is reached * 2) load target register is clobbered * 3) A branch is seen (stopUseMask has the PC bit set). */ if ((checkLIR == headLIR) || (stopUseMask | stopDefMask) & checkLIR->defMask) { break; } } /* The load has been eliminated */ if (thisLIR->isNop) continue; /* * The load cannot be eliminated. See if it can be hoisted to an * earlier spot. */ for (checkLIR = PREV_LIR(thisLIR); /* empty by intention */; checkLIR = PREV_LIR(checkLIR)) { if (checkLIR->isNop) continue; /* * TUNING: once a full scheduler exists, check here * for conversion of a redundant load into a copy similar * to the way redundant loads are handled above. */ /* Find out if the load can be yanked past the checkLIR */ /* Last instruction reached */ bool stopHere = (checkLIR == headLIR); /* Base address is clobbered by checkLIR */ stopHere |= ((stopUseMask & checkLIR->defMask) != 0); /* Load target clobbers use/def in checkLIR */ stopHere |= ((stopDefMask & (checkLIR->useMask | checkLIR->defMask)) != 0); /* Avoid re-ordering literal pool loads */ stopHere |= isLiteralLoad(checkLIR); /* Don't go too far */ stopHere |= (hoistDistance >= maxHoist); /* Found a new place to put the load - move it here */ if (stopHere == true) { DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR, "HOIST LOAD")); /* The store can be hoisted for at least one cycle */ if (hoistDistance != 0) { ArmLIR *newLoadLIR = dvmCompilerNew(sizeof(ArmLIR), true); *newLoadLIR = *thisLIR; newLoadLIR->age = cUnit->optRound; /* * Insertion is guaranteed to succeed since checkLIR * is never the first LIR on the list */ dvmCompilerInsertLIRAfter((LIR *) checkLIR, (LIR *) newLoadLIR); thisLIR->isNop = true; } break; } /* * Saw a real instruction that hosting the load is * beneficial */ if (!isPseudoOpCode(checkLIR->opCode)) { hoistDistance++; } } } } }
/* * Find all lsl/lsr and add that can be replaced with a * combined lsl/lsr + add */ static void applyShiftArithmeticOpts(CompilationUnit *cUnit, ArmLIR *headLIR, ArmLIR *tailLIR) { ArmLIR *thisLIR = NULL; for (thisLIR = headLIR; thisLIR != tailLIR; thisLIR = NEXT_LIR(thisLIR)) { if(thisLIR->flags.isNop) { continue; } if(thisLIR->opcode == kThumb2LslRRI5 || thisLIR->opcode == kThumb2LsrRRI5 || thisLIR->opcode == kThumbLslRRI5 || thisLIR->opcode == kThumbLsrRRI5) { /* Find next that is not nop and not pseudo code */ ArmLIR *nextLIR = NULL; for(nextLIR = NEXT_LIR(thisLIR); nextLIR != tailLIR; nextLIR = NEXT_LIR(nextLIR)) { if (!nextLIR->flags.isNop && !isPseudoOpcode(nextLIR->opcode)) { break; } } if(nextLIR == tailLIR) { return; } if(nextLIR->opcode == kThumb2AddRRR && nextLIR->operands[3] == 0 && (nextLIR->operands[1] == thisLIR->operands[0] || nextLIR->operands[2] == thisLIR->operands[0])) { bool applyOpt = true; if(!(thisLIR->operands[0] == nextLIR->operands[0])) { /* Check that shift dest reg is not used after * the addition. */ ArmLIR* tmpLIR = NULL; for(tmpLIR = NEXT_LIR(nextLIR); tmpLIR != tailLIR; tmpLIR = NEXT_LIR(tmpLIR)) { if (!tmpLIR->flags.isNop && !(EncodingMap[tmpLIR->opcode].flags & IS_BRANCH) && (tmpLIR->defMask | tmpLIR->useMask) & thisLIR->defMask) { if(tmpLIR->useMask & thisLIR->defMask) { /* Shift dest reg is used for src, skip opt. */ applyOpt = false; } break; } } } if(applyOpt) { /* * Found lsl/lsr & add, use barrel shifter for add instead * * (1) Normal case * [lsl/lsr] r9, r1, #x * [add] r0, r2, r9 * * (2) Changing place of args for add * [lsl/lsr] r9, r1, #x * [add] r0, r9, r2 * * (3) Using r1 and r1 shifted as args for add * [lsl/lsr] r9, r1, #x * [add] r0, r1, r9 * * (4) Using r1 and r1 shifted as args for add, variant 2 * [lsl/lsr] r9, r1, #x * [add] r0, r9, r1 * * Result: * [add] rDest, rSrc1, rSrc2, [lsl/lsr] x */ int type = kArmLsl; if(thisLIR->opcode == kThumb2LsrRRI5 || thisLIR->opcode == kThumbLsrRRI5) { type = kArmLsr; } /* For most cases keep original rSrc1 */ int rSrc1 = nextLIR->operands[1]; if(thisLIR->operands[0] == nextLIR->operands[1]) { /* Case 2 & 4: move original rSrc2 to rScr1 since reg to be shifted need to be in rSrc2 */ rSrc1 = nextLIR->operands[2]; } /* Reg to be shifted need to be in rSrc2 */ int rSrc2 = thisLIR->operands[1]; /* Encode type of shift and amount */ int shift = ((thisLIR->operands[2] & 0x1f) << 2) | type; /* Keep rDest, but change rSrc1, rSrc2 and use shift */ ArmLIR* newLIR = (ArmLIR *)dvmCompilerNew(sizeof(ArmLIR), true); newLIR->opcode = nextLIR->opcode; newLIR->operands[0] = nextLIR->operands[0]; newLIR->operands[1] = rSrc1; newLIR->operands[2] = rSrc2; newLIR->operands[3] = shift; dvmCompilerSetupResourceMasks(newLIR); dvmCompilerInsertLIRBefore((LIR *) nextLIR, (LIR *) newLIR); thisLIR->flags.isNop = true; nextLIR->flags.isNop = true; } /* * Avoid looping through nops already identified. * Continue directly after the updated instruction * instead. */ thisLIR = nextLIR; } } } }
/* * Perform a pass to hoist all frame pointer load instructions that * are independent, outside the loop. */ static void applyLoopLoadHoisting(CompilationUnit *cUnit) { ArmLIR *thisLIR, *labelLIR, *lastLIR, *insertLIR; ArmLIR *loadLIR[MAX_LOAD_HOISTS]; ArmLIR *regLIR[MAX_REGISTER_OPS]; u8 defLoadMask = 0; u8 defMask = 0; u8 masterDefMask = ~((1ULL << kRegEnd) - 1); bool isValidLoop = false; int loadCount = 0; int regCount = 0; int loadindex; int hoistCount = 0; #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("GlobalOpts LoopLoadHoisting applied on '%s'", cUnit->method->name); cUnit->printMe = true; #endif labelLIR = (ArmLIR *) cUnit->firstLIRInsn; lastLIR = (ArmLIR *) cUnit->lastLIRInsn; insertLIR = NULL; /* Find the insert point */ while ((labelLIR != lastLIR) && (labelLIR->flags.isNop || (labelLIR->opcode != kArmPseudoNormalBlockLabel))) { if ((cUnit->loopAnalysis->branchesAdded) && (labelLIR->opcode == kThumbBUncond) && (insertLIR == NULL) && (!labelLIR->flags.isNop)) insertLIR = labelLIR; labelLIR = NEXT_LIR(labelLIR); } if (labelLIR->opcode != kArmPseudoNormalBlockLabel) { #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Can't hoist - no loop label found!"); #endif return; } if (insertLIR == NULL) { insertLIR = labelLIR; } else if ((ArmLIR *) insertLIR->generic.target != labelLIR) { #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Can't hoist - branch target does not match!"); #endif return; } /* Search for eligible load instructions to hoist */ for (thisLIR = labelLIR; thisLIR != lastLIR; thisLIR = NEXT_LIR(thisLIR)) { bool handled = false; int flags; /* Skip non-interesting instructions */ if (thisLIR->flags.isNop || isPseudoOpcode(thisLIR->opcode)) continue; flags = EncodingMap[thisLIR->opcode].flags; /* If it's a load instruction, check if it's a hoist candidate. */ if (((flags & IS_LOAD) != 0) && ((thisLIR->useMask & ENCODE_DALVIK_REG) != 0)) { if (regCount >= MAX_REGISTER_OPS) { #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Out of register list space!"); #endif return; } regLIR[regCount++] = thisLIR; if ((((defLoadMask | defMask) & thisLIR->defMask) == 0) && (loadCount < MAX_LOAD_HOISTS)) { defLoadMask |= thisLIR->defMask; loadLIR[loadCount++] = thisLIR; handled = true; } else { masterDefMask |= thisLIR->defMask; } /* If it's a store instruction, check if it matches a previous hoistable load instruction. If so, reset the global def-flag to indicate that the load is still hoistable. */ } else if (((flags & IS_STORE) != 0) && ((thisLIR->defMask & ENCODE_DALVIK_REG) != 0)) { if (regCount >= MAX_REGISTER_OPS) { #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Out of register list space!"); #endif return; } regLIR[regCount++] = thisLIR; if ((thisLIR->useMask & defLoadMask) != 0) { handled = true; for (int i = loadCount - 1; i >= 0; i--) { if ((thisLIR->aliasInfo == loadLIR[i]->aliasInfo) && (thisLIR->operands[0] == loadLIR[i]->operands[0])) { defMask &= ~(loadLIR[i]->defMask); break; } } } /* If it's a branch instruction, check if it's the loop branch. If it matches the label, mark it as a valid loop. */ } else if ((flags & IS_BRANCH) != 0) { handled = true; if (labelLIR == (ArmLIR *) thisLIR->generic.target) { isValidLoop = true; } else if ((thisLIR->opcode >= kThumbBlx1) && (thisLIR->opcode <= kThumbBlxR)) { #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Trace contains branch to subroutine!"); #endif return; } } else if (thisLIR->opcode == kThumbUndefined) { break; } /* If it's not a 'special' instruction, accumulate into def-flags. */ if (!handled) defMask |= thisLIR->defMask; } defLoadMask &= ~(defMask | masterDefMask); if (!isValidLoop || (defLoadMask == 0)) { #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Loop not valid, or defLoadMask (0x%llx) was zero!", defLoadMask); #endif return; } #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Masks: masterDef: 0x%llx, def: 0x%llx, final defLoad: 0x%llx", masterDefMask, defMask, defLoadMask); #endif /* Try to hoist the load operations */ for (loadindex = 0; loadindex < loadCount; loadindex++) { thisLIR = loadLIR[loadindex]; /* Host this load? */ if ((thisLIR->defMask & defLoadMask) == thisLIR->defMask) { int i; bool foundAlias = false; for (i = 0; i < regCount; i++) { if ((thisLIR->aliasInfo == regLIR[i]->aliasInfo) && (thisLIR->operands[0] != regLIR[i]->operands[0])) { foundAlias = true; for (int k = loadindex; k < loadCount; k++) { if (loadLIR[k] == regLIR[i]) { loadLIR[k]->defMask = -1; break; } } #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Register alias found between these two load ops:"); dvmDumpLIRInsn((LIR*)thisLIR, NULL); dvmDumpLIRInsn((LIR*)regLIR[i], NULL); #endif break; } } if (!foundAlias) { #ifdef LOOP_LOAD_HOIST_VERBOSE ALOGD("Hoisting this load op:"); dvmDumpLIRInsn((LIR*)thisLIR, NULL); #endif ArmLIR *newLoadLIR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); *newLoadLIR = *thisLIR; dvmCompilerInsertLIRBefore((LIR *) insertLIR, (LIR *) newLoadLIR); thisLIR->flags.isNop = true; hoistCount++; } } } if (cUnit->printMe) ALOGD("GlobalOpt LoopLoadHoist hoisted %d load ops.", hoistCount); }