| /* |
| * 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. |
| */ |
| |
| #include "Dalvik.h" |
| #include "vm/compiler/CompilerInternals.h" |
| #include "ArmLIR.h" |
| |
| ArmLIR* dvmCompilerGenCopy(CompilationUnit *cUnit, int rDest, int rSrc); |
| |
| /* Is this a Dalvik register access? */ |
| static inline bool isDalvikLoad(ArmLIR *lir) |
| { |
| return ((lir->operands[1] == rFP) && |
| ((lir->opCode == THUMB_LDR_RRI5) || |
| (lir->opCode == THUMB2_LDR_RRI12) || |
| (lir->opCode == THUMB2_VLDRS) || |
| (lir->opCode == THUMB2_VLDRD))); |
| } |
| |
| static inline bool isDalvikStore(ArmLIR *lir) |
| { |
| return ((lir->operands[1] == rFP) && |
| ((lir->opCode == THUMB_STR_RRI5) || |
| (lir->opCode == THUMB2_STR_RRI12) || |
| (lir->opCode == THUMB2_VSTRS) || |
| (lir->opCode == THUMB2_VSTRD))); |
| } |
| |
| /* Double regs overlap float regs. Return true if collision */ |
| static bool regClobber(int reg1, int reg2) |
| { |
| int reg1a, reg1b; |
| int reg2a, reg2b; |
| if (!FPREG(reg1) || !FPREG(reg2)) |
| return (reg1 == reg2); |
| if (DOUBLEREG(reg1)) { |
| reg1a = reg1 & FP_REG_MASK; |
| reg1b = reg1a + 1; |
| } else { |
| reg1a = reg1b = reg1 & FP_REG_MASK; |
| } |
| if (DOUBLEREG(reg2)) { |
| reg2a = reg2 & FP_REG_MASK; |
| reg2b = reg2a + 1; |
| } else { |
| reg2a = reg2b = reg2 & FP_REG_MASK; |
| } |
| return (reg1a == reg2a) || (reg1a == reg2b) || |
| (reg1b == reg2a) || (reg1b == reg2b); |
| } |
| /* |
| * 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 = thisLIR->operands[2]; |
| 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. |
| */ |
| u8 stopMask = ENCODE_GP_REG(rpc) | thisLIR->useMask; |
| |
| for (checkLIR = NEXT_LIR(thisLIR); |
| checkLIR != tailLIR; |
| checkLIR = NEXT_LIR(checkLIR)) { |
| |
| /* Check if a Dalvik register load is redundant */ |
| if (isDalvikLoad(checkLIR) && |
| checkLIR->operands[2] == dRegId ) { |
| if (FPREG(nativeRegId) != FPREG(checkLIR->operands[0])) { |
| break; // TODO: handle gen<=>float copies |
| } |
| /* Insert a move to replace the load */ |
| if (checkLIR->operands[0] != nativeRegId) { |
| ArmLIR *moveLIR; |
| moveLIR = dvmCompilerRegCopy(cUnit, |
| checkLIR->operands[0], |
| nativeRegId); |
| /* |
| * Insertion is guaranteed to succeed since checkLIR |
| * is never the first LIR on the list |
| */ |
| dvmCompilerInsertLIRBefore((LIR *) checkLIR, |
| (LIR *) moveLIR); |
| } |
| checkLIR->isNop = true; |
| continue; |
| |
| /* Found a true output dependency - nuke the previous store */ |
| } else if (isDalvikStore(checkLIR) && |
| checkLIR->operands[2] == dRegId) { |
| thisLIR->isNop = true; |
| break; |
| /* Find out the latest slot that the store can be sunk into */ |
| } else { |
| bool stopHere = false; |
| |
| /* Last instruction reached */ |
| stopHere |= NEXT_LIR(checkLIR) == tailLIR; |
| |
| /* |
| * Conservatively assume there is a memory dependency |
| * for st/ld multiples and reg+reg address mode |
| */ |
| stopHere |= checkLIR->opCode == THUMB_STMIA || |
| checkLIR->opCode == THUMB_LDMIA || |
| checkLIR->opCode == THUMB_STR_RRR || |
| checkLIR->opCode == THUMB_LDR_RRR || |
| checkLIR->opCode == THUMB2_STR_RRR || |
| checkLIR->opCode == THUMB2_LDR_RRR || |
| checkLIR->opCode == THUMB2_STMIA || |
| checkLIR->opCode == THUMB2_LDMIA || |
| checkLIR->opCode == THUMB2_VLDRD || |
| checkLIR->opCode == THUMB2_VSTRD; |
| |
| |
| /* Store data is clobbered */ |
| stopHere |= (stopMask & checkLIR->defMask) != 0; |
| |
| /* Found a new place to put the store - move it here */ |
| if (stopHere == true) { |
| /* 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; |
| /* |
| * Insertion is guaranteed to succeed since checkLIR |
| * is never the first LIR on the list |
| */ |
| 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; |
| |
| 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 (isDalvikLoad(thisLIR)) { |
| int dRegId = thisLIR->operands[2]; |
| int nativeRegId = thisLIR->operands[0]; |
| ArmLIR *checkLIR; |
| int hoistDistance = 0; |
| u8 stopUseMask = ENCODE_GP_REG(rpc) | thisLIR->useMask; |
| u8 stopDefMask = thisLIR->defMask; |
| |
| for (checkLIR = PREV_LIR(thisLIR); |
| checkLIR != headLIR; |
| checkLIR = PREV_LIR(checkLIR)) { |
| |
| if (checkLIR->isNop) continue; |
| |
| /* Check if the current load is redundant */ |
| if ((isDalvikLoad(checkLIR) || isDalvikStore(checkLIR)) && |
| checkLIR->operands[2] == dRegId ) { |
| if (FPREG(nativeRegId) != FPREG(checkLIR->operands[0])) { |
| break; // TODO: handle gen<=>float copies |
| } |
| /* Insert a move to replace the load */ |
| if (checkLIR->operands[0] != nativeRegId) { |
| ArmLIR *moveLIR; |
| moveLIR = dvmCompilerRegCopy(cUnit, |
| nativeRegId, |
| checkLIR->operands[0]); |
| /* |
| * Convert *thisLIR* load into a move |
| */ |
| dvmCompilerInsertLIRAfter((LIR *) checkLIR, |
| (LIR *) moveLIR); |
| } |
| cUnit->printMe = true; |
| thisLIR->isNop = true; |
| break; |
| |
| /* Find out if the load can be yanked past the checkLIR */ |
| } else { |
| bool stopHere = false; |
| |
| /* Last instruction reached */ |
| stopHere |= PREV_LIR(checkLIR) == headLIR; |
| |
| /* |
| * Conservatively assume there is a memory dependency |
| * for st/ld multiples and reg+reg address mode |
| */ |
| stopHere |= checkLIR->opCode == THUMB_STMIA || |
| checkLIR->opCode == THUMB_LDMIA || |
| checkLIR->opCode == THUMB_STR_RRR || |
| checkLIR->opCode == THUMB_LDR_RRR || |
| checkLIR->opCode == THUMB2_STR_RRR || |
| checkLIR->opCode == THUMB2_LDR_RRR || |
| checkLIR->opCode == THUMB2_STMIA || |
| checkLIR->opCode == THUMB2_LDMIA || |
| checkLIR->opCode == THUMB2_VLDRD || |
| checkLIR->opCode == THUMB2_VSTRD; |
| |
| /* 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; |
| |
| /* Found a new place to put the load - move it here */ |
| if (stopHere == true) { |
| /* 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; |
| cUnit->printMe = true; |
| } |
| break; |
| } |
| |
| /* |
| * Saw a real instruction that the store can be sunk after |
| */ |
| if (!isPseudoOpCode(checkLIR->opCode)) { |
| hoistDistance++; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void dvmCompilerApplyLocalOptimizations(CompilationUnit *cUnit, LIR *headLIR, |
| LIR *tailLIR) |
| { |
| if (!(gDvmJit.disableOpt & (1 << kLoadStoreElimination))) { |
| applyLoadStoreElimination(cUnit, (ArmLIR *) headLIR, |
| (ArmLIR *) tailLIR); |
| } |
| if (!(gDvmJit.disableOpt & (1 << kLoadHoisting))) { |
| applyLoadHoisting(cUnit, (ArmLIR *) headLIR, (ArmLIR *) tailLIR); |
| } |
| } |