1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * This file contains codegen and support common to all supported 19 * Mips variants. It is included by: 20 * 21 * Codegen-$(TARGET_ARCH_VARIANT).c 22 * 23 * which combines this common code with specific support found in the 24 * applicable directory below this one. 25 */ 26 27 28/* Load a word at base + displacement. Displacement must be word multiple */ 29static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement, 30 int rDest) 31{ 32 return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord, 33 INVALID_SREG); 34} 35 36static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase, 37 int displacement, int rSrc) 38{ 39 return storeBaseDisp(cUnit, rBase, displacement, rSrc, kWord); 40} 41 42/* 43 * Load a Dalvik register into a physical register. Take care when 44 * using this routine, as it doesn't perform any bookkeeping regarding 45 * register liveness. That is the responsibility of the caller. 46 */ 47static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc, 48 int reg1) 49{ 50 rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc); 51 if (rlSrc.location == kLocPhysReg) { 52 genRegCopy(cUnit, reg1, rlSrc.lowReg); 53 } else if (rlSrc.location == kLocRetval) { 54 loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), reg1); 55 } else { 56 assert(rlSrc.location == kLocDalvikFrame); 57 loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2, 58 reg1); 59 } 60} 61 62/* 63 * Similar to loadValueDirect, but clobbers and allocates the target 64 * register. Should be used when loading to a fixed register (for example, 65 * loading arguments to an out of line call. 66 */ 67static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc, 68 int reg1) 69{ 70 dvmCompilerClobber(cUnit, reg1); 71 dvmCompilerMarkInUse(cUnit, reg1); 72 loadValueDirect(cUnit, rlSrc, reg1); 73} 74 75/* 76 * Load a Dalvik register pair into a physical register[s]. Take care when 77 * using this routine, as it doesn't perform any bookkeeping regarding 78 * register liveness. That is the responsibility of the caller. 79 */ 80static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc, 81 int regLo, int regHi) 82{ 83 rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc); 84 if (rlSrc.location == kLocPhysReg) { 85 genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg); 86 } else if (rlSrc.location == kLocRetval) { 87 loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval), 88 regLo, regHi, INVALID_SREG); 89 } else { 90 assert(rlSrc.location == kLocDalvikFrame); 91 loadBaseDispWide(cUnit, NULL, rFP, 92 dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2, 93 regLo, regHi, INVALID_SREG); 94 } 95} 96 97/* 98 * Similar to loadValueDirect, but clobbers and allocates the target 99 * registers. Should be used when loading to a fixed registers (for example, 100 * loading arguments to an out of line call. 101 */ 102static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc, 103 int regLo, int regHi) 104{ 105 dvmCompilerClobber(cUnit, regLo); 106 dvmCompilerClobber(cUnit, regHi); 107 dvmCompilerMarkInUse(cUnit, regLo); 108 dvmCompilerMarkInUse(cUnit, regHi); 109 loadValueDirectWide(cUnit, rlSrc, regLo, regHi); 110} 111 112static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc, 113 RegisterClass opKind) 114{ 115 rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false); 116 if (rlSrc.location == kLocDalvikFrame) { 117 loadValueDirect(cUnit, rlSrc, rlSrc.lowReg); 118 rlSrc.location = kLocPhysReg; 119 dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow); 120 } else if (rlSrc.location == kLocRetval) { 121 loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), rlSrc.lowReg); 122 rlSrc.location = kLocPhysReg; 123 dvmCompilerClobber(cUnit, rlSrc.lowReg); 124 } 125 return rlSrc; 126} 127 128static void storeValue(CompilationUnit *cUnit, RegLocation rlDest, 129 RegLocation rlSrc) 130{ 131 LIR *defStart; 132 LIR *defEnd; 133 assert(!rlDest.wide); 134 assert(!rlSrc.wide); 135 dvmCompilerKillNullCheckedLoc(cUnit, rlDest); 136 rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc); 137 rlDest = dvmCompilerUpdateLoc(cUnit, rlDest); 138 if (rlSrc.location == kLocPhysReg) { 139 if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) || 140 (rlDest.location == kLocPhysReg)) { 141 // Src is live or Dest has assigned reg. 142 rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false); 143 genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg); 144 } else { 145 // Just re-assign the registers. Dest gets Src's regs 146 rlDest.lowReg = rlSrc.lowReg; 147 dvmCompilerClobber(cUnit, rlSrc.lowReg); 148 } 149 } else { 150 // Load Src either into promoted Dest or temps allocated for Dest 151 rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false); 152 loadValueDirect(cUnit, rlSrc, rlDest.lowReg); 153 } 154 155 // Dest is now live and dirty (until/if we flush it to home location) 156 dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow); 157 dvmCompilerMarkDirty(cUnit, rlDest.lowReg); 158 159 160 if (rlDest.location == kLocRetval) { 161 storeBaseDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), 162 rlDest.lowReg, kWord); 163 dvmCompilerClobber(cUnit, rlDest.lowReg); 164 } else { 165 dvmCompilerResetDefLoc(cUnit, rlDest); 166 if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) { 167 defStart = (LIR *)cUnit->lastLIRInsn; 168 int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow); 169 storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord); 170 dvmCompilerMarkClean(cUnit, rlDest.lowReg); 171 defEnd = (LIR *)cUnit->lastLIRInsn; 172 dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd); 173 } 174 } 175} 176 177static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc, 178 RegisterClass opKind) 179{ 180 assert(rlSrc.wide); 181 rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false); 182 if (rlSrc.location == kLocDalvikFrame) { 183 loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg); 184 rlSrc.location = kLocPhysReg; 185 dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow); 186 dvmCompilerMarkLive(cUnit, rlSrc.highReg, 187 dvmCompilerSRegHi(rlSrc.sRegLow)); 188 } else if (rlSrc.location == kLocRetval) { 189 loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval), 190 rlSrc.lowReg, rlSrc.highReg, INVALID_SREG); 191 rlSrc.location = kLocPhysReg; 192 dvmCompilerClobber(cUnit, rlSrc.lowReg); 193 dvmCompilerClobber(cUnit, rlSrc.highReg); 194 } 195 return rlSrc; 196} 197 198static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest, 199 RegLocation rlSrc) 200{ 201 LIR *defStart; 202 LIR *defEnd; 203 assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg)); 204 assert(rlDest.wide); 205 assert(rlSrc.wide); 206 dvmCompilerKillNullCheckedLoc(cUnit, rlDest); 207 if (rlSrc.location == kLocPhysReg) { 208 if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) || 209 dvmCompilerIsLive(cUnit, rlSrc.highReg) || 210 (rlDest.location == kLocPhysReg)) { 211 // Src is live or Dest has assigned reg. 212 rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false); 213 genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg, 214 rlSrc.lowReg, rlSrc.highReg); 215 } else { 216 // Just re-assign the registers. Dest gets Src's regs 217 rlDest.lowReg = rlSrc.lowReg; 218 rlDest.highReg = rlSrc.highReg; 219 dvmCompilerClobber(cUnit, rlSrc.lowReg); 220 dvmCompilerClobber(cUnit, rlSrc.highReg); 221 } 222 } else { 223 // Load Src either into promoted Dest or temps allocated for Dest 224 rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false); 225 loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg, 226 rlDest.highReg); 227 } 228 229 // Dest is now live and dirty (until/if we flush it to home location) 230 dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow); 231 dvmCompilerMarkLive(cUnit, rlDest.highReg, 232 dvmCompilerSRegHi(rlDest.sRegLow)); 233 dvmCompilerMarkDirty(cUnit, rlDest.lowReg); 234 dvmCompilerMarkDirty(cUnit, rlDest.highReg); 235 dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg); 236 237 238 if (rlDest.location == kLocRetval) { 239 storeBaseDispWide(cUnit, rSELF, offsetof(Thread, interpSave.retval), 240 rlDest.lowReg, rlDest.highReg); 241 dvmCompilerClobber(cUnit, rlDest.lowReg); 242 dvmCompilerClobber(cUnit, rlDest.highReg); 243 } else { 244 dvmCompilerResetDefLocWide(cUnit, rlDest); 245 if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) || 246 dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) { 247 defStart = (LIR *)cUnit->lastLIRInsn; 248 int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow); 249 assert((vReg+1) == dvmCompilerS2VReg(cUnit, 250 dvmCompilerSRegHi(rlDest.sRegLow))); 251 storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg, 252 rlDest.highReg); 253 dvmCompilerMarkClean(cUnit, rlDest.lowReg); 254 dvmCompilerMarkClean(cUnit, rlDest.highReg); 255 defEnd = (LIR *)cUnit->lastLIRInsn; 256 dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd); 257 } 258 } 259} 260/* 261 * Perform null-check on a register. sReg is the ssa register being checked, 262 * and mReg is the machine register holding the actual value. If internal state 263 * indicates that sReg has been checked before the check request is ignored. 264 */ 265static MipsLIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg, 266 int dOffset, MipsLIR *pcrLabel) 267{ 268 /* This particular Dalvik register has been null-checked */ 269 if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) { 270 return pcrLabel; 271 } 272 dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg); 273 return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel); 274} 275 276 277 278/* 279 * Perform a "reg cmp reg" operation and jump to the PCR region if condition 280 * satisfies. 281 */ 282static MipsLIR *genRegRegCheck(CompilationUnit *cUnit, 283 MipsConditionCode cond, 284 int reg1, int reg2, int dOffset, 285 MipsLIR *pcrLabel) 286{ 287 MipsLIR *res = NULL; 288 if (cond == kMipsCondGe) { /* signed >= case */ 289 int tReg = dvmCompilerAllocTemp(cUnit); 290 res = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2); 291 MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1); 292 genCheckCommon(cUnit, dOffset, branch, pcrLabel); 293 } else if (cond == kMipsCondCs) { /* unsigned >= case */ 294 int tReg = dvmCompilerAllocTemp(cUnit); 295 res = newLIR3(cUnit, kMipsSltu, tReg, reg1, reg2); 296 MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1); 297 genCheckCommon(cUnit, dOffset, branch, pcrLabel); 298 } else { 299 ALOGE("Unexpected condition in genRegRegCheck: %d\n", (int) cond); 300 dvmAbort(); 301 } 302 return res; 303} 304 305/* 306 * Perform zero-check on a register. Similar to genNullCheck but the value being 307 * checked does not have a corresponding Dalvik register. 308 */ 309static MipsLIR *genZeroCheck(CompilationUnit *cUnit, int mReg, 310 int dOffset, MipsLIR *pcrLabel) 311{ 312 return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel); 313} 314 315/* Perform bound check on two registers */ 316static MipsLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex, 317 int rBound, int dOffset, MipsLIR *pcrLabel) 318{ 319 return genRegRegCheck(cUnit, kMipsCondCs, rIndex, rBound, dOffset, 320 pcrLabel); 321} 322 323/* 324 * Jump to the out-of-line handler to finish executing the 325 * remaining of more complex instructions. 326 */ 327static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpcode opCode) 328{ 329 /* 330 * We're jumping from a trace to a template. Using jal is preferable to jalr, 331 * but we need to ensure source and target addresses allow the use of jal. 332 * This should almost always be the case, but if source and target are in 333 * different 256mb regions then use jalr. The test below is very conservative 334 * since we don't have a source address yet, but this is ok for now given that 335 * we expect this case to be very rare. The test can be made less conservative 336 * as needed in the future in coordination with address assignment during 337 * the assembly process. 338 */ 339 dvmCompilerClobberHandlerRegs(cUnit); 340 int targetAddr = (int) gDvmJit.codeCache + templateEntryOffsets[opCode]; 341 int maxSourceAddr = (int) gDvmJit.codeCache + gDvmJit.codeCacheSize; 342 343 if ((targetAddr & 0xF0000000) == (maxSourceAddr & 0xF0000000)) { 344 newLIR1(cUnit, kMipsJal, targetAddr); 345 } else { 346 loadConstant(cUnit, r_T9, targetAddr); 347 newLIR2(cUnit, kMipsJalr, r_RA, r_T9); 348 } 349} 350