/* * 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 * Mips 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. */ /* Load a word at base + displacement. Displacement must be word multiple */ static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement, int rDest) { return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord, INVALID_SREG); } static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase, int displacement, int rSrc) { return storeBaseDisp(cUnit, rBase, displacement, rSrc, kWord); } /* * Load a Dalvik register into a physical register. Take care when * using this routine, as it doesn't perform any bookkeeping regarding * register liveness. That is the responsibility of the caller. */ static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc, int reg1) { rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc); if (rlSrc.location == kLocPhysReg) { genRegCopy(cUnit, reg1, rlSrc.lowReg); } else if (rlSrc.location == kLocRetval) { loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), reg1); } else { assert(rlSrc.location == kLocDalvikFrame); loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2, reg1); } } /* * Similar to loadValueDirect, but clobbers and allocates the target * register. Should be used when loading to a fixed register (for example, * loading arguments to an out of line call. */ static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc, int reg1) { dvmCompilerClobber(cUnit, reg1); dvmCompilerMarkInUse(cUnit, reg1); loadValueDirect(cUnit, rlSrc, reg1); } /* * Load a Dalvik register pair into a physical register[s]. Take care when * using this routine, as it doesn't perform any bookkeeping regarding * register liveness. That is the responsibility of the caller. */ static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc, int regLo, int regHi) { rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc); if (rlSrc.location == kLocPhysReg) { genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg); } else if (rlSrc.location == kLocRetval) { loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval), regLo, regHi, INVALID_SREG); } else { assert(rlSrc.location == kLocDalvikFrame); loadBaseDispWide(cUnit, NULL, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2, regLo, regHi, INVALID_SREG); } } /* * Similar to loadValueDirect, but clobbers and allocates the target * registers. Should be used when loading to a fixed registers (for example, * loading arguments to an out of line call. */ static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc, int regLo, int regHi) { dvmCompilerClobber(cUnit, regLo); dvmCompilerClobber(cUnit, regHi); dvmCompilerMarkInUse(cUnit, regLo); dvmCompilerMarkInUse(cUnit, regHi); loadValueDirectWide(cUnit, rlSrc, regLo, regHi); } static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc, RegisterClass opKind) { rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false); if (rlSrc.location == kLocDalvikFrame) { loadValueDirect(cUnit, rlSrc, rlSrc.lowReg); rlSrc.location = kLocPhysReg; dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow); } else if (rlSrc.location == kLocRetval) { loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), rlSrc.lowReg); rlSrc.location = kLocPhysReg; dvmCompilerClobber(cUnit, rlSrc.lowReg); } return rlSrc; } static void storeValue(CompilationUnit *cUnit, RegLocation rlDest, RegLocation rlSrc) { LIR *defStart; LIR *defEnd; assert(!rlDest.wide); assert(!rlSrc.wide); dvmCompilerKillNullCheckedLoc(cUnit, rlDest); rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc); rlDest = dvmCompilerUpdateLoc(cUnit, rlDest); if (rlSrc.location == kLocPhysReg) { if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) || (rlDest.location == kLocPhysReg)) { // Src is live or Dest has assigned reg. rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false); genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg); } else { // Just re-assign the registers. Dest gets Src's regs rlDest.lowReg = rlSrc.lowReg; dvmCompilerClobber(cUnit, rlSrc.lowReg); } } else { // Load Src either into promoted Dest or temps allocated for Dest rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false); loadValueDirect(cUnit, rlSrc, rlDest.lowReg); } // Dest is now live and dirty (until/if we flush it to home location) dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow); dvmCompilerMarkDirty(cUnit, rlDest.lowReg); if (rlDest.location == kLocRetval) { storeBaseDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), rlDest.lowReg, kWord); dvmCompilerClobber(cUnit, rlDest.lowReg); } else { dvmCompilerResetDefLoc(cUnit, rlDest); if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) { defStart = (LIR *)cUnit->lastLIRInsn; int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow); storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord); dvmCompilerMarkClean(cUnit, rlDest.lowReg); defEnd = (LIR *)cUnit->lastLIRInsn; dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd); } } } static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc, RegisterClass opKind) { assert(rlSrc.wide); rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false); if (rlSrc.location == kLocDalvikFrame) { loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg); rlSrc.location = kLocPhysReg; dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow); dvmCompilerMarkLive(cUnit, rlSrc.highReg, dvmCompilerSRegHi(rlSrc.sRegLow)); } else if (rlSrc.location == kLocRetval) { loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval), rlSrc.lowReg, rlSrc.highReg, INVALID_SREG); rlSrc.location = kLocPhysReg; dvmCompilerClobber(cUnit, rlSrc.lowReg); dvmCompilerClobber(cUnit, rlSrc.highReg); } return rlSrc; } static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest, RegLocation rlSrc) { LIR *defStart; LIR *defEnd; assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg)); assert(rlDest.wide); assert(rlSrc.wide); dvmCompilerKillNullCheckedLoc(cUnit, rlDest); if (rlSrc.location == kLocPhysReg) { if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) || dvmCompilerIsLive(cUnit, rlSrc.highReg) || (rlDest.location == kLocPhysReg)) { // Src is live or Dest has assigned reg. rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false); genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg, rlSrc.lowReg, rlSrc.highReg); } else { // Just re-assign the registers. Dest gets Src's regs rlDest.lowReg = rlSrc.lowReg; rlDest.highReg = rlSrc.highReg; dvmCompilerClobber(cUnit, rlSrc.lowReg); dvmCompilerClobber(cUnit, rlSrc.highReg); } } else { // Load Src either into promoted Dest or temps allocated for Dest rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false); loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg, rlDest.highReg); } // Dest is now live and dirty (until/if we flush it to home location) dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow); dvmCompilerMarkLive(cUnit, rlDest.highReg, dvmCompilerSRegHi(rlDest.sRegLow)); dvmCompilerMarkDirty(cUnit, rlDest.lowReg); dvmCompilerMarkDirty(cUnit, rlDest.highReg); dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg); if (rlDest.location == kLocRetval) { storeBaseDispWide(cUnit, rSELF, offsetof(Thread, interpSave.retval), rlDest.lowReg, rlDest.highReg); dvmCompilerClobber(cUnit, rlDest.lowReg); dvmCompilerClobber(cUnit, rlDest.highReg); } else { dvmCompilerResetDefLocWide(cUnit, rlDest); if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) || dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) { defStart = (LIR *)cUnit->lastLIRInsn; int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow); assert((vReg+1) == dvmCompilerS2VReg(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))); storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg, rlDest.highReg); dvmCompilerMarkClean(cUnit, rlDest.lowReg); dvmCompilerMarkClean(cUnit, rlDest.highReg); defEnd = (LIR *)cUnit->lastLIRInsn; dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd); } } } /* * Perform null-check on a register. sReg is the ssa register being checked, * and mReg is the machine register holding the actual value. If internal state * indicates that sReg has been checked before the check request is ignored. */ static MipsLIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg, int dOffset, MipsLIR *pcrLabel) { /* This particular Dalvik register has been null-checked */ if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) { return pcrLabel; } dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg); return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel); } /* * Perform a "reg cmp reg" operation and jump to the PCR region if condition * satisfies. */ static MipsLIR *genRegRegCheck(CompilationUnit *cUnit, MipsConditionCode cond, int reg1, int reg2, int dOffset, MipsLIR *pcrLabel) { MipsLIR *res = NULL; if (cond == kMipsCondGe) { /* signed >= case */ int tReg = dvmCompilerAllocTemp(cUnit); res = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2); MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1); genCheckCommon(cUnit, dOffset, branch, pcrLabel); } else if (cond == kMipsCondCs) { /* unsigned >= case */ int tReg = dvmCompilerAllocTemp(cUnit); res = newLIR3(cUnit, kMipsSltu, tReg, reg1, reg2); MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1); genCheckCommon(cUnit, dOffset, branch, pcrLabel); } else { ALOGE("Unexpected condition in genRegRegCheck: %d\n", (int) cond); dvmAbort(); } return res; } /* * Perform zero-check on a register. Similar to genNullCheck but the value being * checked does not have a corresponding Dalvik register. */ static MipsLIR *genZeroCheck(CompilationUnit *cUnit, int mReg, int dOffset, MipsLIR *pcrLabel) { return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel); } /* Perform bound check on two registers */ static MipsLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex, int rBound, int dOffset, MipsLIR *pcrLabel) { return genRegRegCheck(cUnit, kMipsCondCs, rIndex, rBound, dOffset, pcrLabel); } /* * Jump to the out-of-line handler to finish executing the * remaining of more complex instructions. */ static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpcode opCode) { /* * We're jumping from a trace to a template. Using jal is preferable to jalr, * but we need to ensure source and target addresses allow the use of jal. * This should almost always be the case, but if source and target are in * different 256mb regions then use jalr. The test below is very conservative * since we don't have a source address yet, but this is ok for now given that * we expect this case to be very rare. The test can be made less conservative * as needed in the future in coordination with address assignment during * the assembly process. */ dvmCompilerClobberHandlerRegs(cUnit); int targetAddr = (int) gDvmJit.codeCache + templateEntryOffsets[opCode]; int maxSourceAddr = (int) gDvmJit.codeCache + gDvmJit.codeCacheSize; if ((targetAddr & 0xF0000000) == (maxSourceAddr & 0xF0000000)) { newLIR1(cUnit, kMipsJal, targetAddr); } else { loadConstant(cUnit, r_T9, targetAddr); newLIR2(cUnit, kMipsJalr, r_RA, r_T9); } }