/* * 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++; } } } } } }
/* * 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++; } } } }
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++; } } } } }