| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2011 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 for the Thumb2 ISA and is intended to be | 
|  | 19 | * includes by: | 
|  | 20 | * | 
|  | 21 | *        Codegen-$(TARGET_ARCH_VARIANT).c | 
|  | 22 | * | 
|  | 23 | */ | 
|  | 24 |  | 
|  | 25 | /* | 
|  | 26 | * Construct an s4 from two consecutive half-words of switch data. | 
|  | 27 | * This needs to check endianness because the DEX optimizer only swaps | 
|  | 28 | * half-words in instruction stream. | 
|  | 29 | * | 
|  | 30 | * "switchData" must be 32-bit aligned. | 
|  | 31 | */ | 
|  | 32 | #if __BYTE_ORDER == __LITTLE_ENDIAN | 
|  | 33 | static inline s4 s4FromSwitchData(const void* switchData) { | 
|  | 34 | return *(s4*) switchData; | 
|  | 35 | } | 
|  | 36 | #else | 
|  | 37 | static inline s4 s4FromSwitchData(const void* switchData) { | 
|  | 38 | u2* data = switchData; | 
|  | 39 | return data[0] | (((s4) data[1]) << 16); | 
|  | 40 | } | 
|  | 41 | #endif | 
|  | 42 |  | 
|  | 43 | /* | 
|  | 44 | * Generate a Thumb2 IT instruction, which can nullify up to | 
|  | 45 | * four subsequent instructions based on a condition and its | 
|  | 46 | * inverse.  The condition applies to the first instruction, which | 
|  | 47 | * is executed if the condition is met.  The string "guide" consists | 
|  | 48 | * of 0 to 3 chars, and applies to the 2nd through 4th instruction. | 
|  | 49 | * A "T" means the instruction is executed if the condition is | 
|  | 50 | * met, and an "E" means the instruction is executed if the condition | 
|  | 51 | * is not met. | 
|  | 52 | */ | 
|  | 53 | static ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code, | 
|  | 54 | const char* guide) | 
|  | 55 | { | 
|  | 56 | int mask; | 
|  | 57 | int condBit = code & 1; | 
|  | 58 | int altBit = condBit ^ 1; | 
|  | 59 | int mask3 = 0; | 
|  | 60 | int mask2 = 0; | 
|  | 61 | int mask1 = 0; | 
|  | 62 |  | 
|  | 63 | //Note: case fallthroughs intentional | 
|  | 64 | switch(strlen(guide)) { | 
|  | 65 | case 3: | 
|  | 66 | mask1 = (guide[2] == 'T') ? condBit : altBit; | 
|  | 67 | case 2: | 
|  | 68 | mask2 = (guide[1] == 'T') ? condBit : altBit; | 
|  | 69 | case 1: | 
|  | 70 | mask3 = (guide[0] == 'T') ? condBit : altBit; | 
|  | 71 | break; | 
|  | 72 | case 0: | 
|  | 73 | break; | 
|  | 74 | default: | 
|  | 75 | LOG(FATAL) << "OAT: bad case in genIT"; | 
|  | 76 | } | 
|  | 77 | mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) | | 
|  | 78 | (1 << (3 - strlen(guide))); | 
|  | 79 | return newLIR2(cUnit, kThumb2It, code, mask); | 
|  | 80 | } | 
|  | 81 |  | 
|  | 82 | /* | 
|  | 83 | * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik | 
|  | 84 | * offset vaddr.  This label will be used to fix up the case | 
|  | 85 | * branch table during the assembly phase.  Be sure to set | 
|  | 86 | * all resource flags on this to prevent code motion across | 
|  | 87 | * target boundaries.  KeyVal is just there for debugging. | 
|  | 88 | */ | 
|  | 89 | static ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal) | 
|  | 90 | { | 
|  | 91 | ArmLIR* lir; | 
|  | 92 | for (lir = (ArmLIR*)cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) { | 
|  | 93 | if ((lir->opcode == kArmPseudoDalvikByteCodeBoundary) && | 
|  | 94 | (lir->generic.dalvikOffset == vaddr)) { | 
|  | 95 | ArmLIR* newLabel = (ArmLIR*)oatNew(sizeof(ArmLIR), true); | 
|  | 96 | newLabel->generic.dalvikOffset = vaddr; | 
|  | 97 | newLabel->opcode = kArmPseudoCaseLabel; | 
|  | 98 | newLabel->operands[0] = keyVal; | 
|  | 99 | oatInsertLIRAfter((LIR*)lir, (LIR*)newLabel); | 
|  | 100 | return newLabel; | 
|  | 101 | } | 
|  | 102 | } | 
|  | 103 | oatCodegenDump(cUnit); | 
|  | 104 | LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr; | 
|  | 105 | return NULL; // Quiet gcc | 
|  | 106 | } | 
|  | 107 |  | 
|  | 108 | static void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec) | 
|  | 109 | { | 
|  | 110 | const u2* table = tabRec->table; | 
|  | 111 | int baseVaddr = tabRec->vaddr; | 
|  | 112 | int *targets = (int*)&table[4]; | 
|  | 113 | int entries = table[1]; | 
|  | 114 | int lowKey = s4FromSwitchData(&table[2]); | 
|  | 115 | for (int i = 0; i < entries; i++) { | 
|  | 116 | tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i], | 
|  | 117 | i + lowKey); | 
|  | 118 | } | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | static void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec) | 
|  | 122 | { | 
|  | 123 | const u2* table = tabRec->table; | 
|  | 124 | int baseVaddr = tabRec->vaddr; | 
|  | 125 | int entries = table[1]; | 
|  | 126 | int* keys = (int*)&table[2]; | 
|  | 127 | int* targets = &keys[entries]; | 
|  | 128 | for (int i = 0; i < entries; i++) { | 
|  | 129 | tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i], | 
|  | 130 | keys[i]); | 
|  | 131 | } | 
|  | 132 | } | 
|  | 133 |  | 
|  | 134 | void oatProcessSwitchTables(CompilationUnit* cUnit) | 
|  | 135 | { | 
|  | 136 | GrowableListIterator iterator; | 
|  | 137 | oatGrowableListIteratorInit(&cUnit->switchTables, &iterator); | 
|  | 138 | while (true) { | 
|  | 139 | SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext( | 
|  | 140 | &iterator); | 
|  | 141 | if (tabRec == NULL) break; | 
|  | 142 | if (tabRec->table[0] == kPackedSwitchSignature) | 
|  | 143 | markPackedCaseLabels(cUnit, tabRec); | 
|  | 144 | else if (tabRec->table[0] == kSparseSwitchSignature) | 
|  | 145 | markSparseCaseLabels(cUnit, tabRec); | 
|  | 146 | else { | 
|  | 147 | LOG(FATAL) << "Invalid switch table"; | 
|  | 148 | } | 
|  | 149 | } | 
|  | 150 | } | 
|  | 151 |  | 
|  | 152 | static void dumpSparseSwitchTable(const u2* table) | 
|  | 153 | /* | 
|  | 154 | * Sparse switch data format: | 
|  | 155 | *  ushort ident = 0x0200   magic value | 
|  | 156 | *  ushort size             number of entries in the table; > 0 | 
|  | 157 | *  int keys[size]          keys, sorted low-to-high; 32-bit aligned | 
|  | 158 | *  int targets[size]       branch targets, relative to switch opcode | 
|  | 159 | * | 
|  | 160 | * Total size is (2+size*4) 16-bit code units. | 
|  | 161 | */ | 
|  | 162 | { | 
|  | 163 | u2 ident = table[0]; | 
|  | 164 | int entries = table[1]; | 
|  | 165 | int* keys = (int*)&table[2]; | 
|  | 166 | int* targets = &keys[entries]; | 
|  | 167 | LOG(INFO) <<  "Sparse switch table - ident:0x" << std::hex << ident << | 
|  | 168 | ", entries: " << std::dec << entries; | 
|  | 169 | for (int i = 0; i < entries; i++) { | 
|  | 170 | LOG(INFO) << "    Key[" << keys[i] << "] -> 0x" << std::hex << | 
|  | 171 | targets[i]; | 
|  | 172 | } | 
|  | 173 | } | 
|  | 174 |  | 
|  | 175 | static void dumpPackedSwitchTable(const u2* table) | 
|  | 176 | /* | 
|  | 177 | * Packed switch data format: | 
|  | 178 | *  ushort ident = 0x0100   magic value | 
|  | 179 | *  ushort size             number of entries in the table | 
|  | 180 | *  int first_key           first (and lowest) switch case value | 
|  | 181 | *  int targets[size]       branch targets, relative to switch opcode | 
|  | 182 | * | 
|  | 183 | * Total size is (4+size*2) 16-bit code units. | 
|  | 184 | */ | 
|  | 185 | { | 
|  | 186 | u2 ident = table[0]; | 
|  | 187 | int* targets = (int*)&table[4]; | 
|  | 188 | int entries = table[1]; | 
|  | 189 | int lowKey = s4FromSwitchData(&table[2]); | 
|  | 190 | LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident << | 
|  | 191 | ", entries: " << std::dec << entries << ", lowKey: " << lowKey; | 
|  | 192 | for (int i = 0; i < entries; i++) { | 
|  | 193 | LOG(INFO) << "    Key[" << (i + lowKey) << "] -> 0x" << std::hex << | 
|  | 194 | targets[i]; | 
|  | 195 | } | 
|  | 196 | } | 
|  | 197 |  | 
|  | 198 | /* | 
|  | 199 | * The sparse table in the literal pool is an array of <key,displacement> | 
|  | 200 | * pairs.  For each set, we'll load them as a pair using ldmia. | 
|  | 201 | * This means that the register number of the temp we use for the key | 
|  | 202 | * must be lower than the reg for the displacement. | 
|  | 203 | * | 
|  | 204 | * The test loop will look something like: | 
|  | 205 | * | 
|  | 206 | *   adr   rBase, <table> | 
|  | 207 | *   ldr   rVal, [rSP, vRegOff] | 
|  | 208 | *   mov   rIdx, #tableSize | 
|  | 209 | * lp: | 
|  | 210 | *   ldmia rBase!, {rKey, rDisp} | 
|  | 211 | *   sub   rIdx, #1 | 
|  | 212 | *   cmp   rVal, rKey | 
|  | 213 | *   ifeq | 
|  | 214 | *   add   rPC, rDisp   ; This is the branch from which we compute displacement | 
|  | 215 | *   cbnz  rIdx, lp | 
|  | 216 | */ | 
|  | 217 | static void genSparseSwitch(CompilationUnit* cUnit, MIR* mir, | 
|  | 218 | RegLocation rlSrc) | 
|  | 219 | { | 
|  | 220 | const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB; | 
|  | 221 | if (cUnit->printMe) { | 
|  | 222 | dumpSparseSwitchTable(table); | 
|  | 223 | } | 
|  | 224 | // Add the table to the list - we'll process it later | 
|  | 225 | SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable), | 
|  | 226 | true); | 
|  | 227 | tabRec->table = table; | 
|  | 228 | tabRec->vaddr = mir->offset; | 
|  | 229 | int size = table[1]; | 
|  | 230 | tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true); | 
|  | 231 | oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec); | 
|  | 232 |  | 
|  | 233 | // Get the switch value | 
|  | 234 | rlSrc = loadValue(cUnit, rlSrc, kCoreReg); | 
|  | 235 | int rBase = oatAllocTemp(cUnit); | 
|  | 236 | /* Allocate key and disp temps */ | 
|  | 237 | int rKey = oatAllocTemp(cUnit); | 
|  | 238 | int rDisp = oatAllocTemp(cUnit); | 
|  | 239 | // Make sure rKey's register number is less than rDisp's number for ldmia | 
|  | 240 | if (rKey > rDisp) { | 
|  | 241 | int tmp = rDisp; | 
|  | 242 | rDisp = rKey; | 
|  | 243 | rKey = tmp; | 
|  | 244 | } | 
|  | 245 | // Materialize a pointer to the switch table | 
|  | 246 | newLIR3(cUnit, kThumb2AdrST, rBase, 0, (intptr_t)tabRec); | 
|  | 247 | // Set up rIdx | 
|  | 248 | int rIdx = oatAllocTemp(cUnit); | 
|  | 249 | loadConstant(cUnit, rIdx, size); | 
|  | 250 | // Establish loop branch target | 
|  | 251 | ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 252 | target->defMask = ENCODE_ALL; | 
|  | 253 | // Load next key/disp | 
|  | 254 | newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp)); | 
|  | 255 | opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg); | 
|  | 256 | // Go if match. NOTE: No instruction set switch here - must stay Thumb2 | 
|  | 257 | genIT(cUnit, kArmCondEq, ""); | 
|  | 258 | ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp); | 
|  | 259 | tabRec->bxInst = switchBranch; | 
|  | 260 | // Needs to use setflags encoding here | 
|  | 261 | newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1); | 
|  | 262 | ArmLIR* branch = opCondBranch(cUnit, kArmCondNe); | 
|  | 263 | branch->generic.target = (LIR*)target; | 
|  | 264 | } | 
|  | 265 |  | 
|  | 266 |  | 
|  | 267 | static void genPackedSwitch(CompilationUnit* cUnit, MIR* mir, | 
|  | 268 | RegLocation rlSrc) | 
|  | 269 | { | 
|  | 270 | const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB; | 
|  | 271 | if (cUnit->printMe) { | 
|  | 272 | dumpPackedSwitchTable(table); | 
|  | 273 | } | 
|  | 274 | // Add the table to the list - we'll process it later | 
|  | 275 | SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable), | 
|  | 276 | true); | 
|  | 277 | tabRec->table = table; | 
|  | 278 | tabRec->vaddr = mir->offset; | 
|  | 279 | int size = table[1]; | 
|  | 280 | tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true); | 
|  | 281 | oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec); | 
|  | 282 |  | 
|  | 283 | // Get the switch value | 
|  | 284 | rlSrc = loadValue(cUnit, rlSrc, kCoreReg); | 
|  | 285 | int tableBase = oatAllocTemp(cUnit); | 
|  | 286 | // Materialize a pointer to the switch table | 
|  | 287 | newLIR3(cUnit, kThumb2AdrST, tableBase, 0, (intptr_t)tabRec); | 
|  | 288 | int lowKey = s4FromSwitchData(&table[2]); | 
|  | 289 | int keyReg; | 
|  | 290 | // Remove the bias, if necessary | 
|  | 291 | if (lowKey == 0) { | 
|  | 292 | keyReg = rlSrc.lowReg; | 
|  | 293 | } else { | 
|  | 294 | keyReg = oatAllocTemp(cUnit); | 
|  | 295 | opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey); | 
|  | 296 | } | 
|  | 297 | // Bounds check - if < 0 or >= size continue following switch | 
|  | 298 | opRegImm(cUnit, kOpCmp, keyReg, size-1); | 
|  | 299 | ArmLIR* branchOver = opCondBranch(cUnit, kArmCondHi); | 
|  | 300 |  | 
|  | 301 | // Load the displacement from the switch table | 
|  | 302 | int dispReg = oatAllocTemp(cUnit); | 
|  | 303 | loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord); | 
|  | 304 |  | 
|  | 305 | // ..and go! NOTE: No instruction set switch here - must stay Thumb2 | 
|  | 306 | ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg); | 
|  | 307 | tabRec->bxInst = switchBranch; | 
|  | 308 |  | 
|  | 309 | /* branchOver target here */ | 
|  | 310 | ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 311 | target->defMask = ENCODE_ALL; | 
|  | 312 | branchOver->generic.target = (LIR*)target; | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | /* | 
|  | 316 | * Array data table format: | 
|  | 317 | *  ushort ident = 0x0300   magic value | 
|  | 318 | *  ushort width            width of each element in the table | 
|  | 319 | *  uint   size             number of elements in the table | 
|  | 320 | *  ubyte  data[size*width] table of data values (may contain a single-byte | 
|  | 321 | *                          padding at the end) | 
|  | 322 | * | 
|  | 323 | * Total size is 4+(width * size + 1)/2 16-bit code units. | 
|  | 324 | */ | 
|  | 325 | static void genFillArrayData(CompilationUnit* cUnit, MIR* mir, | 
|  | 326 | RegLocation rlSrc) | 
|  | 327 | { | 
|  | 328 | const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB; | 
|  | 329 | // Add the table to the list - we'll process it later | 
|  | 330 | FillArrayData *tabRec = (FillArrayData *) | 
|  | 331 | oatNew(sizeof(FillArrayData), true); | 
|  | 332 | tabRec->table = table; | 
|  | 333 | tabRec->vaddr = mir->offset; | 
|  | 334 | u2 width = tabRec->table[1]; | 
|  | 335 | u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16); | 
|  | 336 | tabRec->size = (size * width) + 8; | 
|  | 337 |  | 
|  | 338 | oatInsertGrowableList(&cUnit->fillArrayData, (intptr_t)tabRec); | 
|  | 339 |  | 
|  | 340 | // Making a call - use explicit registers | 
|  | 341 | oatFlushAllRegs(cUnit);   /* Everything to home location */ | 
|  | 342 | loadValueDirectFixed(cUnit, rlSrc, r0); | 
|  | 343 | loadWordDisp(cUnit, rSELF, | 
|  | 344 | OFFSETOF_MEMBER(Thread, pArtHandleFillArrayDataNoThrow), rLR); | 
|  | 345 | // Materialize a pointer to the switch table | 
|  | 346 | newLIR3(cUnit, kThumb2AdrST, r1, 0, (intptr_t)tabRec); | 
|  | 347 | opReg(cUnit, kOpBlx, rLR); | 
|  | 348 | oatClobberCallRegs(cUnit); | 
|  | 349 | } | 
|  | 350 |  | 
|  | 351 | /* | 
|  | 352 | * Mark garbage collection card. Skip if the value we're storing is null. | 
|  | 353 | */ | 
|  | 354 | static void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg) | 
|  | 355 | { | 
|  | 356 | int regCardBase = oatAllocTemp(cUnit); | 
|  | 357 | int regCardNo = oatAllocTemp(cUnit); | 
|  | 358 | ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 359 | loadWordDisp(cUnit, rSELF, Thread::CardTableOffset().Int32Value(), | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 360 | regCardBase); | 
|  | 361 | opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT); | 
|  | 362 | storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0, | 
|  | 363 | kUnsignedByte); | 
|  | 364 | ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 365 | target->defMask = ENCODE_ALL; | 
|  | 366 | branchOver->generic.target = (LIR*)target; | 
|  | 367 | oatFreeTemp(cUnit, regCardBase); | 
|  | 368 | oatFreeTemp(cUnit, regCardNo); | 
|  | 369 | } | 
|  | 370 |  | 
|  | 371 | static void genIGetX(CompilationUnit* cUnit, MIR* mir, OpSize size, | 
|  | 372 | RegLocation rlDest, RegLocation rlObj) | 
|  | 373 | { | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 374 | Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 375 | GetResolvedField(mir->dalvikInsn.vC); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 376 | if (fieldPtr == NULL) { | 
|  | 377 | /* | 
|  | 378 | * With current scheme, we should never be in a situation | 
|  | 379 | * in which the fieldPtr is null here.  If something changes | 
|  | 380 | * and we need to handle it, generate code to load the field | 
|  | 381 | * pointer at run-time. | 
|  | 382 | */ | 
|  | 383 | LOG(FATAL) << "Unexpected null field pointer"; | 
|  | 384 | } | 
|  | 385 | #if ANDROID_SMP != 0 | 
|  | 386 | bool isVolatile = dvmIsVolatileField(fieldPtr); | 
|  | 387 | #else | 
|  | 388 | bool isVolatile = false; | 
|  | 389 | #endif | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 390 | int fieldOffset = fieldPtr->GetOffset(); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 391 | RegLocation rlResult; | 
|  | 392 | RegisterClass regClass = oatRegClassBySize(size); | 
|  | 393 | rlObj = loadValue(cUnit, rlObj, kCoreReg); | 
|  | 394 | rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); | 
|  | 395 | genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, | 
|  | 396 | NULL);/* null object? */ | 
|  | 397 | loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg, | 
|  | 398 | size, rlObj.sRegLow); | 
|  | 399 | if (isVolatile) { | 
|  | 400 | oatGenMemBarrier(cUnit, kSY); | 
|  | 401 | } | 
|  | 402 |  | 
|  | 403 | storeValue(cUnit, rlDest, rlResult); | 
|  | 404 | } | 
|  | 405 |  | 
|  | 406 | static void genIPutX(CompilationUnit* cUnit, MIR* mir, OpSize size, | 
|  | 407 | RegLocation rlSrc, RegLocation rlObj, bool isObject) | 
|  | 408 | { | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 409 | Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 410 | GetResolvedField(mir->dalvikInsn.vC); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 411 | if (fieldPtr == NULL) { | 
|  | 412 | /* | 
|  | 413 | * With current scheme, we should never be in a situation | 
|  | 414 | * in which the fieldPtr is null here.  If something changes | 
|  | 415 | * and we need to handle it, generate code to load the field | 
|  | 416 | * pointer at run-time. | 
|  | 417 | */ | 
|  | 418 | LOG(FATAL) << "Unexpected null field pointer"; | 
|  | 419 | } | 
|  | 420 | #if ANDROID_SMP != 0 | 
|  | 421 | bool isVolatile = dvmIsVolatileField(fieldPtr); | 
|  | 422 | #else | 
|  | 423 | bool isVolatile = false; | 
|  | 424 | #endif | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 425 | int fieldOffset = fieldPtr->GetOffset(); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 426 | RegisterClass regClass = oatRegClassBySize(size); | 
|  | 427 | rlObj = loadValue(cUnit, rlObj, kCoreReg); | 
|  | 428 | rlSrc = loadValue(cUnit, rlSrc, regClass); | 
|  | 429 | genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, | 
|  | 430 | NULL);/* null object? */ | 
|  | 431 |  | 
|  | 432 | if (isVolatile) { | 
|  | 433 | oatGenMemBarrier(cUnit, kSY); | 
|  | 434 | } | 
|  | 435 | storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size); | 
|  | 436 | if (isObject) { | 
|  | 437 | /* NOTE: marking card based on object head */ | 
|  | 438 | markGCCard(cUnit, rlSrc.lowReg, rlObj.lowReg); | 
|  | 439 | } | 
|  | 440 | } | 
|  | 441 |  | 
|  | 442 | static void genIGetWideX(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest, | 
|  | 443 | RegLocation rlObj) | 
|  | 444 | { | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 445 | Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 446 | GetResolvedField(mir->dalvikInsn.vC); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 447 | if (fieldPtr == NULL) { | 
|  | 448 | /* | 
|  | 449 | * With current scheme, we should never be in a situation | 
|  | 450 | * in which the fieldPtr is null here.  If something changes | 
|  | 451 | * and we need to handle it, generate code to load the field | 
|  | 452 | * pointer at run-time. | 
|  | 453 | */ | 
|  | 454 | LOG(FATAL) << "Unexpected null field pointer"; | 
|  | 455 | } | 
|  | 456 | #if ANDROID_SMP != 0 | 
|  | 457 | bool isVolatile = dvmIsVolatileField(fieldPtr); | 
|  | 458 | #else | 
|  | 459 | bool isVolatile = false; | 
|  | 460 | #endif | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 461 | int fieldOffset = fieldPtr->GetOffset(); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 462 | RegLocation rlResult; | 
|  | 463 | rlObj = loadValue(cUnit, rlObj, kCoreReg); | 
|  | 464 | int regPtr = oatAllocTemp(cUnit); | 
|  | 465 |  | 
|  | 466 | assert(rlDest.wide); | 
|  | 467 |  | 
|  | 468 | genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, | 
|  | 469 | NULL);/* null object? */ | 
|  | 470 | opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset); | 
|  | 471 | rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true); | 
|  | 472 |  | 
|  | 473 | loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg); | 
|  | 474 |  | 
|  | 475 | if (isVolatile) { | 
|  | 476 | oatGenMemBarrier(cUnit, kSY); | 
|  | 477 | } | 
|  | 478 |  | 
|  | 479 | oatFreeTemp(cUnit, regPtr); | 
|  | 480 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 481 | } | 
|  | 482 |  | 
|  | 483 | static void genIPutWideX(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc, | 
|  | 484 | RegLocation rlObj) | 
|  | 485 | { | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 486 | Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 487 | GetResolvedField(mir->dalvikInsn.vC); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 488 | if (fieldPtr == NULL) { | 
|  | 489 | /* | 
|  | 490 | * With current scheme, we should never be in a situation | 
|  | 491 | * in which the fieldPtr is null here.  If something changes | 
|  | 492 | * and we need to handle it, generate code to load the field | 
|  | 493 | * pointer at run-time. | 
|  | 494 | */ | 
|  | 495 | LOG(FATAL) << "Unexpected null field pointer"; | 
|  | 496 | } | 
|  | 497 | #if ANDROID_SMP != 0 | 
|  | 498 | bool isVolatile = dvmIsVolatileField(fieldPtr); | 
|  | 499 | #else | 
|  | 500 | bool isVolatile = false; | 
|  | 501 | #endif | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 502 | int fieldOffset = fieldPtr->GetOffset(); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 503 |  | 
|  | 504 | rlObj = loadValue(cUnit, rlObj, kCoreReg); | 
|  | 505 | int regPtr; | 
|  | 506 | rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg); | 
|  | 507 | genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, | 
|  | 508 | NULL);/* null object? */ | 
|  | 509 | regPtr = oatAllocTemp(cUnit); | 
|  | 510 | opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset); | 
|  | 511 |  | 
|  | 512 | if (isVolatile) { | 
|  | 513 | oatGenMemBarrier(cUnit, kSY); | 
|  | 514 | } | 
|  | 515 | storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg); | 
|  | 516 |  | 
|  | 517 | oatFreeTemp(cUnit, regPtr); | 
|  | 518 | } | 
|  | 519 |  | 
|  | 520 | static void genConstClass(CompilationUnit* cUnit, MIR* mir, | 
|  | 521 | RegLocation rlDest, RegLocation rlSrc) | 
|  | 522 | { | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 523 | Class* classPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 524 | GetResolvedClass(mir->dalvikInsn.vB); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 525 |  | 
|  | 526 | if (classPtr == NULL) { | 
|  | 527 | LOG(FATAL) << "Unexpected null class pointer"; | 
|  | 528 | } | 
|  | 529 |  | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 530 | UNIMPLEMENTED(WARNING) << "Not position independent.  Fix"; | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 531 | RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 532 | loadConstantNoClobber(cUnit, rlResult.lowReg, (int) classPtr ); | 
|  | 533 | storeValue(cUnit, rlDest, rlResult); | 
|  | 534 | } | 
|  | 535 |  | 
|  | 536 | static void genConstString(CompilationUnit* cUnit, MIR* mir, | 
|  | 537 | RegLocation rlDest, RegLocation rlSrc) | 
|  | 538 | { | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 539 | String* strPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 540 | GetResolvedString(mir->dalvikInsn.vB); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 541 |  | 
|  | 542 | if (strPtr == NULL) { | 
|  | 543 | /* Shouldn't happen */ | 
|  | 544 | LOG(FATAL) << "Unexpected null const string pointer"; | 
|  | 545 | } | 
|  | 546 |  | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 547 | UNIMPLEMENTED(WARNING) << "Not position indendent.  Fix"; | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 548 | RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 549 | loadConstantNoClobber(cUnit, rlResult.lowReg, (int) strPtr ); | 
|  | 550 | storeValue(cUnit, rlDest, rlResult); | 
|  | 551 | } | 
|  | 552 |  | 
|  | 553 | static void genNewInstance(CompilationUnit* cUnit, MIR* mir, | 
|  | 554 | RegLocation rlDest) | 
|  | 555 | { | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 556 | Class* classPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 557 | GetResolvedClass(mir->dalvikInsn.vB); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 558 |  | 
|  | 559 | if (classPtr == NULL) { | 
|  | 560 | /* Shouldn't happen */ | 
|  | 561 | LOG(FATAL) << "Unexpected null class pointer"; | 
|  | 562 | } | 
|  | 563 |  | 
|  | 564 | // Verifier should have already rejected abstract/interface | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 565 | assert((classPtr->access_flags_ & | 
|  | 566 | (art::kAccInterface|art::kAccAbstract)) == 0); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 567 | oatFlushAllRegs(cUnit);   /* Everything to home location */ | 
|  | 568 | loadWordDisp(cUnit, rSELF, | 
|  | 569 | OFFSETOF_MEMBER(Thread, pArtAllocObjectNoThrow), rLR); | 
|  | 570 | loadConstant(cUnit, r0, (int) classPtr); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 571 | UNIMPLEMENTED(WARNING) << "Need NewWorld dvmAllocObject"; | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 572 | opReg(cUnit, kOpBlx, rLR); | 
|  | 573 | oatClobberCallRegs(cUnit); | 
|  | 574 | RegLocation rlResult = oatGetReturn(cUnit); | 
|  | 575 | storeValue(cUnit, rlDest, rlResult); | 
|  | 576 | } | 
|  | 577 |  | 
|  | 578 | void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc) | 
|  | 579 | { | 
|  | 580 | loadWordDisp(cUnit, rSELF, | 
|  | 581 | OFFSETOF_MEMBER(Thread, pArtAllocObjectNoThrow), rLR); | 
|  | 582 | loadValueDirectFixed(cUnit, rlSrc, r1);  /* Exception object */ | 
|  | 583 | genRegCopy(cUnit, r0, rSELF); | 
|  | 584 | opReg(cUnit, kOpBlx, rLR); | 
|  | 585 | } | 
|  | 586 |  | 
|  | 587 | static void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest, | 
|  | 588 | RegLocation rlSrc) | 
|  | 589 | { | 
|  | 590 | // May generate a call - use explicit registers | 
|  | 591 | RegLocation rlResult; | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 592 | Class* classPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 593 | GetResolvedClass(mir->dalvikInsn.vC); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 594 | if (classPtr == NULL) { | 
|  | 595 | /* Shouldn't happen */ | 
|  | 596 | LOG(FATAL) << "Unexpected null class pointer"; | 
|  | 597 | } | 
|  | 598 | oatFlushAllRegs(cUnit);   /* Everything to home location */ | 
|  | 599 | loadValueDirectFixed(cUnit, rlSrc, r0);  /* Ref */ | 
|  | 600 | loadConstant(cUnit, r2, (int) classPtr ); | 
|  | 601 | /* When taken r0 has NULL which can be used for store directly */ | 
|  | 602 | ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0); | 
|  | 603 | /* r1 now contains object->clazz */ | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 604 | assert(OFFSETOF_MEMBER(Object, klass_) == 0); | 
|  | 605 | loadWordDisp(cUnit, r0, OFFSETOF_MEMBER(Object, klass_), r1); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 606 | /* r1 now contains object->clazz */ | 
|  | 607 | loadWordDisp(cUnit, rSELF, | 
|  | 608 | OFFSETOF_MEMBER(Thread, pArtInstanceofNonTrivial), rLR); | 
|  | 609 | loadConstant(cUnit, r0, 1);                /* Assume true */ | 
|  | 610 | opRegReg(cUnit, kOpCmp, r1, r2); | 
|  | 611 | ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); | 
|  | 612 | genRegCopy(cUnit, r0, r1); | 
|  | 613 | genRegCopy(cUnit, r1, r2); | 
|  | 614 | opReg(cUnit, kOpBlx, rLR); | 
|  | 615 | oatClobberCallRegs(cUnit); | 
|  | 616 | /* branch target here */ | 
|  | 617 | ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 618 | target->defMask = ENCODE_ALL; | 
|  | 619 | rlResult = oatGetReturn(cUnit); | 
|  | 620 | storeValue(cUnit, rlDest, rlResult); | 
|  | 621 | branch1->generic.target = (LIR*)target; | 
|  | 622 | branch2->generic.target = (LIR*)target; | 
|  | 623 | } | 
|  | 624 |  | 
|  | 625 | static void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc) | 
|  | 626 | { | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 627 | Class* classPtr = cUnit->method->GetDeclaringClass()->GetDexCache()-> | 
|  | 628 | GetResolvedClass(mir->dalvikInsn.vB); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 629 | if (classPtr == NULL) { | 
|  | 630 | /* Shouldn't happen with our current model */ | 
|  | 631 | LOG(FATAL) << "Unexpected null class pointer"; | 
|  | 632 | } | 
|  | 633 | oatFlushAllRegs(cUnit);   /* Everything to home location */ | 
|  | 634 | loadConstant(cUnit, r1, (int) classPtr ); | 
|  | 635 | rlSrc = loadValue(cUnit, rlSrc, kCoreReg); | 
|  | 636 | /* Null? */ | 
|  | 637 | ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, | 
|  | 638 | rlSrc.lowReg, 0); | 
|  | 639 | /* | 
|  | 640 | *  rlSrc.lowReg now contains object->clazz.  Note that | 
|  | 641 | *  it could have been allocated r0, but we're okay so long | 
|  | 642 | *  as we don't do anything desctructive until r0 is loaded | 
|  | 643 | *  with clazz. | 
|  | 644 | */ | 
|  | 645 | /* r0 now contains object->clazz */ | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 646 | loadWordDisp(cUnit, rlSrc.lowReg, OFFSETOF_MEMBER(Object, klass_), r0); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 647 | loadWordDisp(cUnit, rSELF, | 
|  | 648 | OFFSETOF_MEMBER(Thread, pArtInstanceofNonTrivialNoThrow), rLR); | 
|  | 649 | opRegReg(cUnit, kOpCmp, r0, r1); | 
|  | 650 | ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); | 
|  | 651 | // Assume success - if not, artInstanceOfNonTrivial will handle throw | 
|  | 652 | opReg(cUnit, kOpBlx, rLR); | 
|  | 653 | oatClobberCallRegs(cUnit); | 
|  | 654 | ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 655 | target->defMask = ENCODE_ALL; | 
|  | 656 | branch1->generic.target = (LIR*)target; | 
|  | 657 | branch2->generic.target = (LIR*)target; | 
|  | 658 | } | 
|  | 659 |  | 
|  | 660 | static void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest, | 
|  | 661 | RegLocation rlSrc) | 
|  | 662 | { | 
|  | 663 | RegLocation rlResult; | 
|  | 664 | rlSrc = loadValue(cUnit, rlSrc, kFPReg); | 
|  | 665 | rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true); | 
|  | 666 | newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg); | 
|  | 667 | storeValue(cUnit, rlDest, rlResult); | 
|  | 668 | } | 
|  | 669 |  | 
|  | 670 | static void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest, | 
|  | 671 | RegLocation rlSrc) | 
|  | 672 | { | 
|  | 673 | RegLocation rlResult; | 
|  | 674 | rlSrc = loadValueWide(cUnit, rlSrc, kFPReg); | 
|  | 675 | rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true); | 
|  | 676 | newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg), | 
|  | 677 | S2D(rlSrc.lowReg, rlSrc.highReg)); | 
|  | 678 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 679 | } | 
|  | 680 |  | 
|  | 681 | /* | 
|  | 682 | * To avoid possible conflicts, we use a lot of temps here.  Note that | 
|  | 683 | * our usage of Thumb2 instruction forms avoids the problems with register | 
|  | 684 | * reuse for multiply instructions prior to arm6. | 
|  | 685 | */ | 
|  | 686 | static void genMulLong(CompilationUnit* cUnit, RegLocation rlDest, | 
|  | 687 | RegLocation rlSrc1, RegLocation rlSrc2) | 
|  | 688 | { | 
|  | 689 | RegLocation rlResult; | 
|  | 690 | int resLo = oatAllocTemp(cUnit); | 
|  | 691 | int resHi = oatAllocTemp(cUnit); | 
|  | 692 | int tmp1 = oatAllocTemp(cUnit); | 
|  | 693 |  | 
|  | 694 | rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg); | 
|  | 695 | rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); | 
|  | 696 |  | 
|  | 697 | newLIR3(cUnit, kThumb2MulRRR, tmp1, rlSrc2.lowReg, rlSrc1.highReg); | 
|  | 698 | newLIR4(cUnit, kThumb2Umull, resLo, resHi, rlSrc2.lowReg, rlSrc1.lowReg); | 
|  | 699 | newLIR4(cUnit, kThumb2Mla, tmp1, rlSrc1.lowReg, rlSrc2.highReg, tmp1); | 
|  | 700 | newLIR4(cUnit, kThumb2AddRRR, resHi, tmp1, resHi, 0); | 
|  | 701 | oatFreeTemp(cUnit, tmp1); | 
|  | 702 |  | 
|  | 703 | rlResult = oatGetReturnWide(cUnit); | 
|  | 704 | rlResult.lowReg = resLo; | 
|  | 705 | rlResult.highReg = resHi; | 
|  | 706 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 707 | } | 
|  | 708 |  | 
|  | 709 | static void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp, | 
|  | 710 | OpKind secondOp, RegLocation rlDest, | 
|  | 711 | RegLocation rlSrc1, RegLocation rlSrc2) | 
|  | 712 | { | 
|  | 713 | RegLocation rlResult; | 
|  | 714 | rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg); | 
|  | 715 | rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); | 
|  | 716 | rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 717 | opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg); | 
|  | 718 | opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg, | 
|  | 719 | rlSrc2.highReg); | 
|  | 720 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 721 | } | 
|  | 722 |  | 
|  | 723 | void oatInitializeRegAlloc(CompilationUnit* cUnit) | 
|  | 724 | { | 
|  | 725 | int numRegs = sizeof(coreRegs)/sizeof(*coreRegs); | 
|  | 726 | int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs); | 
|  | 727 | int numTemps = sizeof(coreTemps)/sizeof(*coreTemps); | 
|  | 728 | int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs); | 
|  | 729 | int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps); | 
|  | 730 | RegisterPool *pool = (RegisterPool *)oatNew(sizeof(*pool), true); | 
|  | 731 | cUnit->regPool = pool; | 
|  | 732 | pool->numCoreRegs = numRegs; | 
|  | 733 | pool->coreRegs = (RegisterInfo *) | 
|  | 734 | oatNew(numRegs * sizeof(*cUnit->regPool->coreRegs), true); | 
|  | 735 | pool->numFPRegs = numFPRegs; | 
|  | 736 | pool->FPRegs = (RegisterInfo *) | 
|  | 737 | oatNew(numFPRegs * sizeof(*cUnit->regPool->FPRegs), true); | 
|  | 738 | oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs); | 
|  | 739 | oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs); | 
|  | 740 | // Keep special registers from being allocated | 
|  | 741 | for (int i = 0; i < numReserved; i++) { | 
|  | 742 | oatMarkInUse(cUnit, reservedRegs[i]); | 
|  | 743 | } | 
|  | 744 | // Mark temp regs - all others not in use can be used for promotion | 
|  | 745 | for (int i = 0; i < numTemps; i++) { | 
|  | 746 | oatMarkTemp(cUnit, coreTemps[i]); | 
|  | 747 | } | 
|  | 748 | for (int i = 0; i < numFPTemps; i++) { | 
|  | 749 | oatMarkTemp(cUnit, fpTemps[i]); | 
|  | 750 | } | 
|  | 751 | pool->nullCheckedRegs = | 
|  | 752 | oatAllocBitVector(cUnit->numSSARegs, false); | 
|  | 753 | } | 
|  | 754 |  | 
|  | 755 | /* | 
|  | 756 | * Handle simple case (thin lock) inline.  If it's complicated, bail | 
|  | 757 | * out to the heavyweight lock/unlock routines.  We'll use dedicated | 
|  | 758 | * registers here in order to be in the right position in case we | 
|  | 759 | * to bail to dvm[Lock/Unlock]Object(self, object) | 
|  | 760 | * | 
|  | 761 | * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object | 
|  | 762 | * r1 -> object [arg1 for dvm[Lock/Unlock]Object | 
|  | 763 | * r2 -> intial contents of object->lock, later result of strex | 
|  | 764 | * r3 -> self->threadId | 
|  | 765 | * r12 -> allow to be used by utilities as general temp | 
|  | 766 | * | 
|  | 767 | * The result of the strex is 0 if we acquire the lock. | 
|  | 768 | * | 
|  | 769 | * See comments in Sync.c for the layout of the lock word. | 
|  | 770 | * Of particular interest to this code is the test for the | 
|  | 771 | * simple case - which we handle inline.  For monitor enter, the | 
|  | 772 | * simple case is thin lock, held by no-one.  For monitor exit, | 
|  | 773 | * the simple case is thin lock, held by the unlocking thread with | 
|  | 774 | * a recurse count of 0. | 
|  | 775 | * | 
|  | 776 | * A minor complication is that there is a field in the lock word | 
|  | 777 | * unrelated to locking: the hash state.  This field must be ignored, but | 
|  | 778 | * preserved. | 
|  | 779 | * | 
|  | 780 | */ | 
|  | 781 | static void genMonitorEnter(CompilationUnit* cUnit, MIR* mir, | 
|  | 782 | RegLocation rlSrc) | 
|  | 783 | { | 
|  | 784 | ArmLIR* target; | 
|  | 785 | ArmLIR* hopTarget; | 
|  | 786 | ArmLIR* branch; | 
|  | 787 | ArmLIR* hopBranch; | 
|  | 788 |  | 
|  | 789 | oatFlushAllRegs(cUnit); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 790 | assert(art::Monitor::kLwShapeThin == 0); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 791 | loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj | 
|  | 792 | oatLockAllTemps(cUnit);  // Prepare for explicit register usage | 
|  | 793 | genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 794 | loadWordDisp(cUnit, rSELF, Thread::IdOffset().Int32Value(), r3); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 795 | newLIR3(cUnit, kThumb2Ldrex, r2, r1, | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 796 | OFFSETOF_MEMBER(Object, monitor_) >> 2); // Get object->lock | 
|  | 797 | // Align owner | 
|  | 798 | opRegImm(cUnit, kOpLsl, r3, art::Monitor::kLwLockOwnerShift); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 799 | // Is lock unheld on lock or held by us (==threadId) on unlock? | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 800 | newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, art::Monitor::kLwLockOwnerShift | 
|  | 801 | - 1); | 
|  | 802 | newLIR3(cUnit, kThumb2Bfc, r2, art::Monitor::kLwHashStateShift, | 
|  | 803 | art::Monitor::kLwLockOwnerShift - 1); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 804 | hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 805 | newLIR4(cUnit, kThumb2Strex, r2, r3, r1, | 
|  | 806 | OFFSETOF_MEMBER(Object, monitor_) >> 2); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 807 | oatGenMemBarrier(cUnit, kSY); | 
|  | 808 | branch = newLIR2(cUnit, kThumb2Cbz, r2, 0); | 
|  | 809 |  | 
|  | 810 | hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 811 | hopTarget->defMask = ENCODE_ALL; | 
|  | 812 | hopBranch->generic.target = (LIR*)hopTarget; | 
|  | 813 |  | 
|  | 814 | // Go expensive route - artLockObjectNoThrow(self, obj); | 
|  | 815 | loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pArtLockObjectNoThrow), | 
|  | 816 | rLR); | 
|  | 817 | genRegCopy(cUnit, r0, rSELF); | 
|  | 818 | newLIR1(cUnit, kThumbBlxR, rLR); | 
|  | 819 |  | 
|  | 820 | // Resume here | 
|  | 821 | target = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 822 | target->defMask = ENCODE_ALL; | 
|  | 823 | branch->generic.target = (LIR*)target; | 
|  | 824 | } | 
|  | 825 |  | 
|  | 826 | /* | 
|  | 827 | * For monitor unlock, we don't have to use ldrex/strex.  Once | 
|  | 828 | * we've determined that the lock is thin and that we own it with | 
|  | 829 | * a zero recursion count, it's safe to punch it back to the | 
|  | 830 | * initial, unlock thin state with a store word. | 
|  | 831 | */ | 
|  | 832 | static void genMonitorExit(CompilationUnit* cUnit, MIR* mir, | 
|  | 833 | RegLocation rlSrc) | 
|  | 834 | { | 
|  | 835 | ArmLIR* target; | 
|  | 836 | ArmLIR* branch; | 
|  | 837 | ArmLIR* hopTarget; | 
|  | 838 | ArmLIR* hopBranch; | 
|  | 839 |  | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 840 | assert(art::Monitor::kLwShapeThin == 0); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 841 | oatFlushAllRegs(cUnit); | 
|  | 842 | loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj | 
|  | 843 | oatLockAllTemps(cUnit);  // Prepare for explicit register usage | 
|  | 844 | genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 845 | loadWordDisp(cUnit, r1, OFFSETOF_MEMBER(Object, monitor_), r2); // Get lock | 
|  | 846 | loadWordDisp(cUnit, rSELF, Thread::IdOffset().Int32Value(), r3); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 847 | // Is lock unheld on lock or held by us (==threadId) on unlock? | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 848 | opRegRegImm(cUnit, kOpAnd, r12, r2, (art::Monitor::kLwHashStateMask << | 
|  | 849 | art::Monitor::kLwHashStateShift)); | 
|  | 850 | // Align owner | 
|  | 851 | opRegImm(cUnit, kOpLsl, r3, art::Monitor::kLwLockOwnerShift); | 
|  | 852 | newLIR3(cUnit, kThumb2Bfc, r2, art::Monitor::kLwHashStateShift, | 
|  | 853 | art::Monitor::kLwLockOwnerShift - 1); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 854 | opRegReg(cUnit, kOpSub, r2, r3); | 
|  | 855 | hopBranch = opCondBranch(cUnit, kArmCondNe); | 
|  | 856 | oatGenMemBarrier(cUnit, kSY); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 857 | storeWordDisp(cUnit, r1, OFFSETOF_MEMBER(Object, monitor_), r12); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 858 | branch = opNone(cUnit, kOpUncondBr); | 
|  | 859 |  | 
|  | 860 | hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 861 | hopTarget->defMask = ENCODE_ALL; | 
|  | 862 | hopBranch->generic.target = (LIR*)hopTarget; | 
|  | 863 |  | 
|  | 864 | // Go expensive route - artUnlockObjectNoThrow(self, obj); | 
|  | 865 | loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pArtUnlockObjectNoThrow), | 
|  | 866 | rLR); | 
|  | 867 | genRegCopy(cUnit, r0, rSELF); | 
|  | 868 | newLIR1(cUnit, kThumbBlxR, rLR); | 
|  | 869 |  | 
|  | 870 | // Resume here | 
|  | 871 | target = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 872 | target->defMask = ENCODE_ALL; | 
|  | 873 | branch->generic.target = (LIR*)target; | 
|  | 874 | } | 
|  | 875 |  | 
|  | 876 | /* | 
|  | 877 | * 64-bit 3way compare function. | 
|  | 878 | *     mov   rX, #-1 | 
|  | 879 | *     cmp   op1hi, op2hi | 
|  | 880 | *     blt   done | 
|  | 881 | *     bgt   flip | 
|  | 882 | *     sub   rX, op1lo, op2lo (treat as unsigned) | 
|  | 883 | *     beq   done | 
|  | 884 | *     ite   hi | 
|  | 885 | *     mov(hi)   rX, #-1 | 
|  | 886 | *     mov(!hi)  rX, #1 | 
|  | 887 | * flip: | 
|  | 888 | *     neg   rX | 
|  | 889 | * done: | 
|  | 890 | */ | 
|  | 891 | static void genCmpLong(CompilationUnit* cUnit, MIR* mir, | 
|  | 892 | RegLocation rlDest, RegLocation rlSrc1, | 
|  | 893 | RegLocation rlSrc2) | 
|  | 894 | { | 
|  | 895 | RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change | 
|  | 896 | ArmLIR* target1; | 
|  | 897 | ArmLIR* target2; | 
|  | 898 | rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg); | 
|  | 899 | rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); | 
|  | 900 | rlTemp.lowReg = oatAllocTemp(cUnit); | 
|  | 901 | loadConstant(cUnit, rlTemp.lowReg, -1); | 
|  | 902 | opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg); | 
|  | 903 | ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt); | 
|  | 904 | ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt); | 
|  | 905 | opRegRegReg(cUnit, kOpSub, rlTemp.lowReg, rlSrc1.lowReg, rlSrc2.lowReg); | 
|  | 906 | ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq); | 
|  | 907 |  | 
|  | 908 | genIT(cUnit, kArmCondHi, "E"); | 
|  | 909 | newLIR2(cUnit, kThumb2MovImmShift, rlTemp.lowReg, modifiedImmediate(-1)); | 
|  | 910 | loadConstant(cUnit, rlTemp.lowReg, 1); | 
|  | 911 | genBarrier(cUnit); | 
|  | 912 |  | 
|  | 913 | target2 = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 914 | target2->defMask = -1; | 
|  | 915 | opRegReg(cUnit, kOpNeg, rlTemp.lowReg, rlTemp.lowReg); | 
|  | 916 |  | 
|  | 917 | target1 = newLIR0(cUnit, kArmPseudoTargetLabel); | 
|  | 918 | target1->defMask = -1; | 
|  | 919 |  | 
|  | 920 | storeValue(cUnit, rlDest, rlTemp); | 
|  | 921 |  | 
|  | 922 | branch1->generic.target = (LIR*)target1; | 
|  | 923 | branch2->generic.target = (LIR*)target2; | 
|  | 924 | branch3->generic.target = branch1->generic.target; | 
|  | 925 | } | 
|  | 926 |  | 
|  | 927 | static void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit, | 
|  | 928 | RegLocation rlSrc, RegLocation rlResult, int lit, | 
|  | 929 | int firstBit, int secondBit) | 
|  | 930 | { | 
|  | 931 | opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg, | 
|  | 932 | encodeShift(kArmLsl, secondBit - firstBit)); | 
|  | 933 | if (firstBit != 0) { | 
|  | 934 | opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit); | 
|  | 935 | } | 
|  | 936 | } | 
|  | 937 |  | 
|  | 938 | static bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset, | 
|  | 939 | int srcSize, int tgtSize) | 
|  | 940 | { | 
|  | 941 | /* | 
|  | 942 | * Don't optimize the register usage since it calls out to support | 
|  | 943 | * functions | 
|  | 944 | */ | 
|  | 945 | RegLocation rlSrc; | 
|  | 946 | RegLocation rlDest; | 
|  | 947 | oatFlushAllRegs(cUnit);   /* Send everything to home location */ | 
|  | 948 | loadWordDisp(cUnit, rSELF, funcOffset, rLR); | 
|  | 949 | if (srcSize == 1) { | 
|  | 950 | rlSrc = oatGetSrc(cUnit, mir, 0); | 
|  | 951 | loadValueDirectFixed(cUnit, rlSrc, r0); | 
|  | 952 | } else { | 
|  | 953 | rlSrc = oatGetSrcWide(cUnit, mir, 0, 1); | 
|  | 954 | loadValueDirectWideFixed(cUnit, rlSrc, r0, r1); | 
|  | 955 | } | 
|  | 956 | opReg(cUnit, kOpBlx, rLR); | 
|  | 957 | oatClobberCallRegs(cUnit); | 
|  | 958 | if (tgtSize == 1) { | 
|  | 959 | RegLocation rlResult; | 
|  | 960 | rlDest = oatGetDest(cUnit, mir, 0); | 
|  | 961 | rlResult = oatGetReturn(cUnit); | 
|  | 962 | storeValue(cUnit, rlDest, rlResult); | 
|  | 963 | } else { | 
|  | 964 | RegLocation rlResult; | 
|  | 965 | rlDest = oatGetDestWide(cUnit, mir, 0, 1); | 
|  | 966 | rlResult = oatGetReturnWide(cUnit); | 
|  | 967 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 968 | } | 
|  | 969 | return false; | 
|  | 970 | } | 
|  | 971 |  | 
|  | 972 | static bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir, | 
|  | 973 | RegLocation rlDest, RegLocation rlSrc1, | 
|  | 974 | RegLocation rlSrc2) | 
|  | 975 | { | 
|  | 976 | RegLocation rlResult; | 
|  | 977 | int funcOffset; | 
|  | 978 |  | 
|  | 979 | switch (mir->dalvikInsn.opcode) { | 
|  | 980 | case OP_ADD_FLOAT_2ADDR: | 
|  | 981 | case OP_ADD_FLOAT: | 
|  | 982 | funcOffset = OFFSETOF_MEMBER(Thread, pFadd); | 
|  | 983 | break; | 
|  | 984 | case OP_SUB_FLOAT_2ADDR: | 
|  | 985 | case OP_SUB_FLOAT: | 
|  | 986 | funcOffset = OFFSETOF_MEMBER(Thread, pFsub); | 
|  | 987 | break; | 
|  | 988 | case OP_DIV_FLOAT_2ADDR: | 
|  | 989 | case OP_DIV_FLOAT: | 
|  | 990 | funcOffset = OFFSETOF_MEMBER(Thread, pFdiv); | 
|  | 991 | break; | 
|  | 992 | case OP_MUL_FLOAT_2ADDR: | 
|  | 993 | case OP_MUL_FLOAT: | 
|  | 994 | funcOffset = OFFSETOF_MEMBER(Thread, pFmul); | 
|  | 995 | break; | 
|  | 996 | case OP_REM_FLOAT_2ADDR: | 
|  | 997 | case OP_REM_FLOAT: | 
|  | 998 | funcOffset = OFFSETOF_MEMBER(Thread, pFmodf); | 
|  | 999 | break; | 
|  | 1000 | case OP_NEG_FLOAT: { | 
|  | 1001 | genNegFloat(cUnit, rlDest, rlSrc1); | 
|  | 1002 | return false; | 
|  | 1003 | } | 
|  | 1004 | default: | 
|  | 1005 | return true; | 
|  | 1006 | } | 
|  | 1007 | oatFlushAllRegs(cUnit);   /* Send everything to home location */ | 
|  | 1008 | loadWordDisp(cUnit, rSELF, funcOffset, rLR); | 
|  | 1009 | loadValueDirectFixed(cUnit, rlSrc1, r0); | 
|  | 1010 | loadValueDirectFixed(cUnit, rlSrc2, r1); | 
|  | 1011 | opReg(cUnit, kOpBlx, rLR); | 
|  | 1012 | oatClobberCallRegs(cUnit); | 
|  | 1013 | rlResult = oatGetReturn(cUnit); | 
|  | 1014 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1015 | return false; | 
|  | 1016 | } | 
|  | 1017 |  | 
|  | 1018 | static bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir, | 
|  | 1019 | RegLocation rlDest, RegLocation rlSrc1, | 
|  | 1020 | RegLocation rlSrc2) | 
|  | 1021 | { | 
|  | 1022 | RegLocation rlResult; | 
|  | 1023 | int funcOffset; | 
|  | 1024 |  | 
|  | 1025 | switch (mir->dalvikInsn.opcode) { | 
|  | 1026 | case OP_ADD_DOUBLE_2ADDR: | 
|  | 1027 | case OP_ADD_DOUBLE: | 
|  | 1028 | funcOffset = OFFSETOF_MEMBER(Thread, pDadd); | 
|  | 1029 | break; | 
|  | 1030 | case OP_SUB_DOUBLE_2ADDR: | 
|  | 1031 | case OP_SUB_DOUBLE: | 
|  | 1032 | funcOffset = OFFSETOF_MEMBER(Thread, pDsub); | 
|  | 1033 | break; | 
|  | 1034 | case OP_DIV_DOUBLE_2ADDR: | 
|  | 1035 | case OP_DIV_DOUBLE: | 
|  | 1036 | funcOffset = OFFSETOF_MEMBER(Thread, pDdiv); | 
|  | 1037 | break; | 
|  | 1038 | case OP_MUL_DOUBLE_2ADDR: | 
|  | 1039 | case OP_MUL_DOUBLE: | 
|  | 1040 | funcOffset = OFFSETOF_MEMBER(Thread, pDmul); | 
|  | 1041 | break; | 
|  | 1042 | case OP_REM_DOUBLE_2ADDR: | 
|  | 1043 | case OP_REM_DOUBLE: | 
|  | 1044 | funcOffset = OFFSETOF_MEMBER(Thread, pFmod); | 
|  | 1045 | break; | 
|  | 1046 | case OP_NEG_DOUBLE: { | 
|  | 1047 | genNegDouble(cUnit, rlDest, rlSrc1); | 
|  | 1048 | return false; | 
|  | 1049 | } | 
|  | 1050 | default: | 
|  | 1051 | return true; | 
|  | 1052 | } | 
|  | 1053 | oatFlushAllRegs(cUnit);   /* Send everything to home location */ | 
|  | 1054 | loadWordDisp(cUnit, rSELF, funcOffset, rLR); | 
|  | 1055 | loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); | 
|  | 1056 | loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3); | 
|  | 1057 | opReg(cUnit, kOpBlx, rLR); | 
|  | 1058 | oatClobberCallRegs(cUnit); | 
|  | 1059 | rlResult = oatGetReturnWide(cUnit); | 
|  | 1060 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 1061 | return false; | 
|  | 1062 | } | 
|  | 1063 |  | 
|  | 1064 | static bool genConversionPortable(CompilationUnit* cUnit, MIR* mir) | 
|  | 1065 | { | 
|  | 1066 | Opcode opcode = mir->dalvikInsn.opcode; | 
|  | 1067 |  | 
|  | 1068 | switch (opcode) { | 
|  | 1069 | case OP_INT_TO_FLOAT: | 
|  | 1070 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f), | 
|  | 1071 | 1, 1); | 
|  | 1072 | case OP_FLOAT_TO_INT: | 
|  | 1073 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz), | 
|  | 1074 | 1, 1); | 
|  | 1075 | case OP_DOUBLE_TO_FLOAT: | 
|  | 1076 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f), | 
|  | 1077 | 2, 1); | 
|  | 1078 | case OP_FLOAT_TO_DOUBLE: | 
|  | 1079 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d), | 
|  | 1080 | 1, 2); | 
|  | 1081 | case OP_INT_TO_DOUBLE: | 
|  | 1082 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d), | 
|  | 1083 | 1, 2); | 
|  | 1084 | case OP_DOUBLE_TO_INT: | 
|  | 1085 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz), | 
|  | 1086 | 2, 1); | 
|  | 1087 | case OP_FLOAT_TO_LONG: | 
|  | 1088 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, | 
|  | 1089 | pArtF2l), 1, 2); | 
|  | 1090 | case OP_LONG_TO_FLOAT: | 
|  | 1091 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f), | 
|  | 1092 | 2, 1); | 
|  | 1093 | case OP_DOUBLE_TO_LONG: | 
|  | 1094 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, | 
|  | 1095 | pArtD2l), 2, 2); | 
|  | 1096 | case OP_LONG_TO_DOUBLE: | 
|  | 1097 | return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d), | 
|  | 1098 | 2, 2); | 
|  | 1099 | default: | 
|  | 1100 | return true; | 
|  | 1101 | } | 
|  | 1102 | return false; | 
|  | 1103 | } | 
|  | 1104 |  | 
|  | 1105 | /* Generate conditional branch instructions */ | 
|  | 1106 | static ArmLIR* genConditionalBranch(CompilationUnit* cUnit, | 
|  | 1107 | ArmConditionCode cond, | 
|  | 1108 | ArmLIR* target) | 
|  | 1109 | { | 
|  | 1110 | ArmLIR* branch = opCondBranch(cUnit, cond); | 
|  | 1111 | branch->generic.target = (LIR*) target; | 
|  | 1112 | return branch; | 
|  | 1113 | } | 
|  | 1114 |  | 
|  | 1115 | /* Generate a unconditional branch to go to the interpreter */ | 
|  | 1116 | static inline ArmLIR* genTrap(CompilationUnit* cUnit, int dOffset, | 
|  | 1117 | ArmLIR* pcrLabel) | 
|  | 1118 | { | 
|  | 1119 | ArmLIR* branch = opNone(cUnit, kOpUncondBr); | 
|  | 1120 | return genCheckCommon(cUnit, dOffset, branch, pcrLabel); | 
|  | 1121 | } | 
|  | 1122 |  | 
|  | 1123 | /* | 
|  | 1124 | * Generate array store | 
|  | 1125 | * | 
|  | 1126 | */ | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 1127 | static void genArrayPut(CompilationUnit* cUnit, MIR* mir, | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 1128 | RegLocation rlArray, RegLocation rlIndex, | 
|  | 1129 | RegLocation rlSrc, int scale) | 
|  | 1130 | { | 
|  | 1131 | RegisterClass regClass = oatRegClassBySize(kWord); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 1132 | int lenOffset = Array::LengthOffset().Int32Value(); | 
|  | 1133 | int dataOffset = Array::DataOffset().Int32Value(); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 1134 |  | 
|  | 1135 | /* Make sure it's a legal object Put. Use direct regs at first */ | 
|  | 1136 | loadValueDirectFixed(cUnit, rlArray, r1); | 
|  | 1137 | loadValueDirectFixed(cUnit, rlSrc, r0); | 
|  | 1138 |  | 
|  | 1139 | /* null array object? */ | 
|  | 1140 | ArmLIR*  pcrLabel = NULL; | 
|  | 1141 |  | 
|  | 1142 | if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { | 
|  | 1143 | pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, r1, | 
|  | 1144 | mir->offset, NULL); | 
|  | 1145 | } | 
|  | 1146 | loadWordDisp(cUnit, rSELF, | 
|  | 1147 | OFFSETOF_MEMBER(Thread, pArtCanPutArrayElementNoThrow), rLR); | 
|  | 1148 | /* Get the array's clazz */ | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 1149 | loadWordDisp(cUnit, r1, OFFSETOF_MEMBER(Object, klass_), r1); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 1150 | /* Get the object's clazz */ | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 1151 | loadWordDisp(cUnit, r0, OFFSETOF_MEMBER(Object, klass_), r0); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 1152 | opReg(cUnit, kOpBlx, rLR); | 
|  | 1153 | oatClobberCallRegs(cUnit); | 
|  | 1154 |  | 
|  | 1155 | // Now, redo loadValues in case they didn't survive the call | 
|  | 1156 |  | 
|  | 1157 | int regPtr; | 
|  | 1158 | rlArray = loadValue(cUnit, rlArray, kCoreReg); | 
|  | 1159 | rlIndex = loadValue(cUnit, rlIndex, kCoreReg); | 
|  | 1160 |  | 
|  | 1161 | if (oatIsTemp(cUnit, rlArray.lowReg)) { | 
|  | 1162 | oatClobber(cUnit, rlArray.lowReg); | 
|  | 1163 | regPtr = rlArray.lowReg; | 
|  | 1164 | } else { | 
|  | 1165 | regPtr = oatAllocTemp(cUnit); | 
|  | 1166 | genRegCopy(cUnit, regPtr, rlArray.lowReg); | 
|  | 1167 | } | 
|  | 1168 |  | 
|  | 1169 | if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { | 
|  | 1170 | int regLen = oatAllocTemp(cUnit); | 
|  | 1171 | //NOTE: max live temps(4) here. | 
|  | 1172 | /* Get len */ | 
|  | 1173 | loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); | 
|  | 1174 | /* regPtr -> array data */ | 
|  | 1175 | opRegImm(cUnit, kOpAdd, regPtr, dataOffset); | 
|  | 1176 | genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset, | 
|  | 1177 | pcrLabel); | 
|  | 1178 | oatFreeTemp(cUnit, regLen); | 
|  | 1179 | } else { | 
|  | 1180 | /* regPtr -> array data */ | 
|  | 1181 | opRegImm(cUnit, kOpAdd, regPtr, dataOffset); | 
|  | 1182 | } | 
|  | 1183 | /* at this point, regPtr points to array, 2 live temps */ | 
|  | 1184 | rlSrc = loadValue(cUnit, rlSrc, regClass); | 
|  | 1185 | storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg, | 
|  | 1186 | scale, kWord); | 
|  | 1187 | } | 
|  | 1188 |  | 
|  | 1189 | /* | 
|  | 1190 | * Generate array load | 
|  | 1191 | */ | 
|  | 1192 | static void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size, | 
|  | 1193 | RegLocation rlArray, RegLocation rlIndex, | 
|  | 1194 | RegLocation rlDest, int scale) | 
|  | 1195 | { | 
|  | 1196 | RegisterClass regClass = oatRegClassBySize(size); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 1197 | int lenOffset = Array::LengthOffset().Int32Value(); | 
|  | 1198 | int dataOffset = Array::DataOffset().Int32Value(); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 1199 | RegLocation rlResult; | 
|  | 1200 | rlArray = loadValue(cUnit, rlArray, kCoreReg); | 
|  | 1201 | rlIndex = loadValue(cUnit, rlIndex, kCoreReg); | 
|  | 1202 | int regPtr; | 
|  | 1203 |  | 
|  | 1204 | /* null object? */ | 
|  | 1205 | ArmLIR*  pcrLabel = NULL; | 
|  | 1206 |  | 
|  | 1207 | if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { | 
|  | 1208 | pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, | 
|  | 1209 | rlArray.lowReg, mir->offset, NULL); | 
|  | 1210 | } | 
|  | 1211 |  | 
|  | 1212 | regPtr = oatAllocTemp(cUnit); | 
|  | 1213 |  | 
|  | 1214 | if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { | 
|  | 1215 | int regLen = oatAllocTemp(cUnit); | 
|  | 1216 | /* Get len */ | 
|  | 1217 | loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); | 
|  | 1218 | /* regPtr -> array data */ | 
|  | 1219 | opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset); | 
|  | 1220 | genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset, | 
|  | 1221 | pcrLabel); | 
|  | 1222 | oatFreeTemp(cUnit, regLen); | 
|  | 1223 | } else { | 
|  | 1224 | /* regPtr -> array data */ | 
|  | 1225 | opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset); | 
|  | 1226 | } | 
|  | 1227 | if ((size == kLong) || (size == kDouble)) { | 
|  | 1228 | if (scale) { | 
|  | 1229 | int rNewIndex = oatAllocTemp(cUnit); | 
|  | 1230 | opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale); | 
|  | 1231 | opRegReg(cUnit, kOpAdd, regPtr, rNewIndex); | 
|  | 1232 | oatFreeTemp(cUnit, rNewIndex); | 
|  | 1233 | } else { | 
|  | 1234 | opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg); | 
|  | 1235 | } | 
|  | 1236 | rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); | 
|  | 1237 |  | 
|  | 1238 | loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg); | 
|  | 1239 |  | 
|  | 1240 | oatFreeTemp(cUnit, regPtr); | 
|  | 1241 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 1242 | } else { | 
|  | 1243 | rlResult = oatEvalLoc(cUnit, rlDest, regClass, true); | 
|  | 1244 |  | 
|  | 1245 | loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg, | 
|  | 1246 | scale, size); | 
|  | 1247 |  | 
|  | 1248 | oatFreeTemp(cUnit, regPtr); | 
|  | 1249 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1250 | } | 
|  | 1251 | } | 
|  | 1252 |  | 
|  | 1253 | /* | 
|  | 1254 | * Generate array store | 
|  | 1255 | * | 
|  | 1256 | */ | 
|  | 1257 | static void genArrayPut(CompilationUnit* cUnit, MIR* mir, OpSize size, | 
|  | 1258 | RegLocation rlArray, RegLocation rlIndex, | 
|  | 1259 | RegLocation rlSrc, int scale) | 
|  | 1260 | { | 
|  | 1261 | RegisterClass regClass = oatRegClassBySize(size); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame^] | 1262 | int lenOffset = Array::LengthOffset().Int32Value(); | 
|  | 1263 | int dataOffset = Array::DataOffset().Int32Value(); | 
| buzbee | 67bf885 | 2011-08-17 17:51:35 -0700 | [diff] [blame] | 1264 |  | 
|  | 1265 | int regPtr; | 
|  | 1266 | rlArray = loadValue(cUnit, rlArray, kCoreReg); | 
|  | 1267 | rlIndex = loadValue(cUnit, rlIndex, kCoreReg); | 
|  | 1268 |  | 
|  | 1269 | if (oatIsTemp(cUnit, rlArray.lowReg)) { | 
|  | 1270 | oatClobber(cUnit, rlArray.lowReg); | 
|  | 1271 | regPtr = rlArray.lowReg; | 
|  | 1272 | } else { | 
|  | 1273 | regPtr = oatAllocTemp(cUnit); | 
|  | 1274 | genRegCopy(cUnit, regPtr, rlArray.lowReg); | 
|  | 1275 | } | 
|  | 1276 |  | 
|  | 1277 | /* null object? */ | 
|  | 1278 | ArmLIR*  pcrLabel = NULL; | 
|  | 1279 |  | 
|  | 1280 | if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { | 
|  | 1281 | pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, | 
|  | 1282 | mir->offset, NULL); | 
|  | 1283 | } | 
|  | 1284 |  | 
|  | 1285 | if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { | 
|  | 1286 | int regLen = oatAllocTemp(cUnit); | 
|  | 1287 | //NOTE: max live temps(4) here. | 
|  | 1288 | /* Get len */ | 
|  | 1289 | loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); | 
|  | 1290 | /* regPtr -> array data */ | 
|  | 1291 | opRegImm(cUnit, kOpAdd, regPtr, dataOffset); | 
|  | 1292 | genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset, | 
|  | 1293 | pcrLabel); | 
|  | 1294 | oatFreeTemp(cUnit, regLen); | 
|  | 1295 | } else { | 
|  | 1296 | /* regPtr -> array data */ | 
|  | 1297 | opRegImm(cUnit, kOpAdd, regPtr, dataOffset); | 
|  | 1298 | } | 
|  | 1299 | /* at this point, regPtr points to array, 2 live temps */ | 
|  | 1300 | if ((size == kLong) || (size == kDouble)) { | 
|  | 1301 | //TODO: need specific wide routine that can handle fp regs | 
|  | 1302 | if (scale) { | 
|  | 1303 | int rNewIndex = oatAllocTemp(cUnit); | 
|  | 1304 | opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale); | 
|  | 1305 | opRegReg(cUnit, kOpAdd, regPtr, rNewIndex); | 
|  | 1306 | oatFreeTemp(cUnit, rNewIndex); | 
|  | 1307 | } else { | 
|  | 1308 | opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg); | 
|  | 1309 | } | 
|  | 1310 | rlSrc = loadValueWide(cUnit, rlSrc, regClass); | 
|  | 1311 |  | 
|  | 1312 | storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg); | 
|  | 1313 |  | 
|  | 1314 | oatFreeTemp(cUnit, regPtr); | 
|  | 1315 | } else { | 
|  | 1316 | rlSrc = loadValue(cUnit, rlSrc, regClass); | 
|  | 1317 |  | 
|  | 1318 | storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg, | 
|  | 1319 | scale, size); | 
|  | 1320 | } | 
|  | 1321 | } | 
|  | 1322 |  | 
|  | 1323 | static bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir, | 
|  | 1324 | RegLocation rlDest, RegLocation rlSrc1, | 
|  | 1325 | RegLocation rlShift) | 
|  | 1326 | { | 
|  | 1327 | /* | 
|  | 1328 | * Don't mess with the regsiters here as there is a particular calling | 
|  | 1329 | * convention to the out-of-line handler. | 
|  | 1330 | */ | 
|  | 1331 | RegLocation rlResult; | 
|  | 1332 |  | 
|  | 1333 | loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); | 
|  | 1334 | loadValueDirect(cUnit, rlShift, r2); | 
|  | 1335 | switch( mir->dalvikInsn.opcode) { | 
|  | 1336 | case OP_SHL_LONG: | 
|  | 1337 | case OP_SHL_LONG_2ADDR: | 
|  | 1338 | //genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG); | 
|  | 1339 | assert(0);  // unimp | 
|  | 1340 | break; | 
|  | 1341 | case OP_SHR_LONG: | 
|  | 1342 | case OP_SHR_LONG_2ADDR: | 
|  | 1343 | //genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG); | 
|  | 1344 | assert(0); // unimp | 
|  | 1345 | break; | 
|  | 1346 | case OP_USHR_LONG: | 
|  | 1347 | case OP_USHR_LONG_2ADDR: | 
|  | 1348 | //genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG); | 
|  | 1349 | assert(0); // unimp | 
|  | 1350 | break; | 
|  | 1351 | default: | 
|  | 1352 | return true; | 
|  | 1353 | } | 
|  | 1354 | rlResult = oatGetReturnWide(cUnit); | 
|  | 1355 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 1356 | return false; | 
|  | 1357 | } | 
|  | 1358 |  | 
|  | 1359 | static bool genArithOpLong(CompilationUnit* cUnit, MIR* mir, | 
|  | 1360 | RegLocation rlDest, RegLocation rlSrc1, | 
|  | 1361 | RegLocation rlSrc2) | 
|  | 1362 | { | 
|  | 1363 | RegLocation rlResult; | 
|  | 1364 | OpKind firstOp = kOpBkpt; | 
|  | 1365 | OpKind secondOp = kOpBkpt; | 
|  | 1366 | bool callOut = false; | 
|  | 1367 | int funcOffset; | 
|  | 1368 | int retReg = r0; | 
|  | 1369 |  | 
|  | 1370 | switch (mir->dalvikInsn.opcode) { | 
|  | 1371 | case OP_NOT_LONG: | 
|  | 1372 | rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); | 
|  | 1373 | rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1374 | opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg); | 
|  | 1375 | opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg); | 
|  | 1376 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 1377 | return false; | 
|  | 1378 | break; | 
|  | 1379 | case OP_ADD_LONG: | 
|  | 1380 | case OP_ADD_LONG_2ADDR: | 
|  | 1381 | firstOp = kOpAdd; | 
|  | 1382 | secondOp = kOpAdc; | 
|  | 1383 | break; | 
|  | 1384 | case OP_SUB_LONG: | 
|  | 1385 | case OP_SUB_LONG_2ADDR: | 
|  | 1386 | firstOp = kOpSub; | 
|  | 1387 | secondOp = kOpSbc; | 
|  | 1388 | break; | 
|  | 1389 | case OP_MUL_LONG: | 
|  | 1390 | case OP_MUL_LONG_2ADDR: | 
|  | 1391 | genMulLong(cUnit, rlDest, rlSrc1, rlSrc2); | 
|  | 1392 | return false; | 
|  | 1393 | case OP_DIV_LONG: | 
|  | 1394 | case OP_DIV_LONG_2ADDR: | 
|  | 1395 | callOut = true; | 
|  | 1396 | retReg = r0; | 
|  | 1397 | funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod); | 
|  | 1398 | break; | 
|  | 1399 | /* NOTE - result is in r2/r3 instead of r0/r1 */ | 
|  | 1400 | case OP_REM_LONG: | 
|  | 1401 | case OP_REM_LONG_2ADDR: | 
|  | 1402 | callOut = true; | 
|  | 1403 | funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod); | 
|  | 1404 | retReg = r2; | 
|  | 1405 | break; | 
|  | 1406 | case OP_AND_LONG_2ADDR: | 
|  | 1407 | case OP_AND_LONG: | 
|  | 1408 | firstOp = kOpAnd; | 
|  | 1409 | secondOp = kOpAnd; | 
|  | 1410 | break; | 
|  | 1411 | case OP_OR_LONG: | 
|  | 1412 | case OP_OR_LONG_2ADDR: | 
|  | 1413 | firstOp = kOpOr; | 
|  | 1414 | secondOp = kOpOr; | 
|  | 1415 | break; | 
|  | 1416 | case OP_XOR_LONG: | 
|  | 1417 | case OP_XOR_LONG_2ADDR: | 
|  | 1418 | firstOp = kOpXor; | 
|  | 1419 | secondOp = kOpXor; | 
|  | 1420 | break; | 
|  | 1421 | case OP_NEG_LONG: { | 
|  | 1422 | //TUNING: can improve this using Thumb2 code | 
|  | 1423 | int tReg = oatAllocTemp(cUnit); | 
|  | 1424 | rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); | 
|  | 1425 | rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1426 | loadConstantNoClobber(cUnit, tReg, 0); | 
|  | 1427 | opRegRegReg(cUnit, kOpSub, rlResult.lowReg, | 
|  | 1428 | tReg, rlSrc2.lowReg); | 
|  | 1429 | opRegReg(cUnit, kOpSbc, tReg, rlSrc2.highReg); | 
|  | 1430 | genRegCopy(cUnit, rlResult.highReg, tReg); | 
|  | 1431 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 1432 | return false; | 
|  | 1433 | } | 
|  | 1434 | default: | 
|  | 1435 | LOG(FATAL) << "Invalid long arith op"; | 
|  | 1436 | } | 
|  | 1437 | if (!callOut) { | 
|  | 1438 | genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2); | 
|  | 1439 | } else { | 
|  | 1440 | // Adjust return regs in to handle case of rem returning r2/r3 | 
|  | 1441 | oatFlushAllRegs(cUnit);   /* Send everything to home location */ | 
|  | 1442 | loadWordDisp(cUnit, rSELF, funcOffset, rLR); | 
|  | 1443 | loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); | 
|  | 1444 | loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3); | 
|  | 1445 | opReg(cUnit, kOpBlx, rLR); | 
|  | 1446 | oatClobberCallRegs(cUnit); | 
|  | 1447 | if (retReg == r0) | 
|  | 1448 | rlResult = oatGetReturnWide(cUnit); | 
|  | 1449 | else | 
|  | 1450 | rlResult = oatGetReturnWideAlt(cUnit); | 
|  | 1451 | storeValueWide(cUnit, rlDest, rlResult); | 
|  | 1452 | } | 
|  | 1453 | return false; | 
|  | 1454 | } | 
|  | 1455 |  | 
|  | 1456 | static bool genArithOpInt(CompilationUnit* cUnit, MIR* mir, | 
|  | 1457 | RegLocation rlDest, RegLocation rlSrc1, | 
|  | 1458 | RegLocation rlSrc2) | 
|  | 1459 | { | 
|  | 1460 | OpKind op = kOpBkpt; | 
|  | 1461 | bool callOut = false; | 
|  | 1462 | bool checkZero = false; | 
|  | 1463 | bool unary = false; | 
|  | 1464 | int retReg = r0; | 
|  | 1465 | int funcOffset; | 
|  | 1466 | RegLocation rlResult; | 
|  | 1467 | bool shiftOp = false; | 
|  | 1468 |  | 
|  | 1469 | switch (mir->dalvikInsn.opcode) { | 
|  | 1470 | case OP_NEG_INT: | 
|  | 1471 | op = kOpNeg; | 
|  | 1472 | unary = true; | 
|  | 1473 | break; | 
|  | 1474 | case OP_NOT_INT: | 
|  | 1475 | op = kOpMvn; | 
|  | 1476 | unary = true; | 
|  | 1477 | break; | 
|  | 1478 | case OP_ADD_INT: | 
|  | 1479 | case OP_ADD_INT_2ADDR: | 
|  | 1480 | op = kOpAdd; | 
|  | 1481 | break; | 
|  | 1482 | case OP_SUB_INT: | 
|  | 1483 | case OP_SUB_INT_2ADDR: | 
|  | 1484 | op = kOpSub; | 
|  | 1485 | break; | 
|  | 1486 | case OP_MUL_INT: | 
|  | 1487 | case OP_MUL_INT_2ADDR: | 
|  | 1488 | op = kOpMul; | 
|  | 1489 | break; | 
|  | 1490 | case OP_DIV_INT: | 
|  | 1491 | case OP_DIV_INT_2ADDR: | 
|  | 1492 | callOut = true; | 
|  | 1493 | checkZero = true; | 
|  | 1494 | funcOffset = OFFSETOF_MEMBER(Thread, pIdiv); | 
|  | 1495 | retReg = r0; | 
|  | 1496 | break; | 
|  | 1497 | /* NOTE: returns in r1 */ | 
|  | 1498 | case OP_REM_INT: | 
|  | 1499 | case OP_REM_INT_2ADDR: | 
|  | 1500 | callOut = true; | 
|  | 1501 | checkZero = true; | 
|  | 1502 | funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod); | 
|  | 1503 | retReg = r1; | 
|  | 1504 | break; | 
|  | 1505 | case OP_AND_INT: | 
|  | 1506 | case OP_AND_INT_2ADDR: | 
|  | 1507 | op = kOpAnd; | 
|  | 1508 | break; | 
|  | 1509 | case OP_OR_INT: | 
|  | 1510 | case OP_OR_INT_2ADDR: | 
|  | 1511 | op = kOpOr; | 
|  | 1512 | break; | 
|  | 1513 | case OP_XOR_INT: | 
|  | 1514 | case OP_XOR_INT_2ADDR: | 
|  | 1515 | op = kOpXor; | 
|  | 1516 | break; | 
|  | 1517 | case OP_SHL_INT: | 
|  | 1518 | case OP_SHL_INT_2ADDR: | 
|  | 1519 | shiftOp = true; | 
|  | 1520 | op = kOpLsl; | 
|  | 1521 | break; | 
|  | 1522 | case OP_SHR_INT: | 
|  | 1523 | case OP_SHR_INT_2ADDR: | 
|  | 1524 | shiftOp = true; | 
|  | 1525 | op = kOpAsr; | 
|  | 1526 | break; | 
|  | 1527 | case OP_USHR_INT: | 
|  | 1528 | case OP_USHR_INT_2ADDR: | 
|  | 1529 | shiftOp = true; | 
|  | 1530 | op = kOpLsr; | 
|  | 1531 | break; | 
|  | 1532 | default: | 
|  | 1533 | LOG(FATAL) << "Invalid word arith op: " << | 
|  | 1534 | (int)mir->dalvikInsn.opcode; | 
|  | 1535 | } | 
|  | 1536 | if (!callOut) { | 
|  | 1537 | rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); | 
|  | 1538 | if (unary) { | 
|  | 1539 | rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1540 | opRegReg(cUnit, op, rlResult.lowReg, | 
|  | 1541 | rlSrc1.lowReg); | 
|  | 1542 | } else { | 
|  | 1543 | rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); | 
|  | 1544 | if (shiftOp) { | 
|  | 1545 | int tReg = oatAllocTemp(cUnit); | 
|  | 1546 | opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31); | 
|  | 1547 | rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1548 | opRegRegReg(cUnit, op, rlResult.lowReg, | 
|  | 1549 | rlSrc1.lowReg, tReg); | 
|  | 1550 | oatFreeTemp(cUnit, tReg); | 
|  | 1551 | } else { | 
|  | 1552 | rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1553 | opRegRegReg(cUnit, op, rlResult.lowReg, | 
|  | 1554 | rlSrc1.lowReg, rlSrc2.lowReg); | 
|  | 1555 | } | 
|  | 1556 | } | 
|  | 1557 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1558 | } else { | 
|  | 1559 | RegLocation rlResult; | 
|  | 1560 | oatFlushAllRegs(cUnit);   /* Send everything to home location */ | 
|  | 1561 | loadValueDirectFixed(cUnit, rlSrc2, r1); | 
|  | 1562 | loadWordDisp(cUnit, rSELF, funcOffset, rLR); | 
|  | 1563 | loadValueDirectFixed(cUnit, rlSrc1, r0); | 
|  | 1564 | if (checkZero) { | 
|  | 1565 | genNullCheck(cUnit, rlSrc2.sRegLow, r1, mir->offset, NULL); | 
|  | 1566 | } | 
|  | 1567 | opReg(cUnit, kOpBlx, rLR); | 
|  | 1568 | oatClobberCallRegs(cUnit); | 
|  | 1569 | if (retReg == r0) | 
|  | 1570 | rlResult = oatGetReturn(cUnit); | 
|  | 1571 | else | 
|  | 1572 | rlResult = oatGetReturnAlt(cUnit); | 
|  | 1573 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1574 | } | 
|  | 1575 | return false; | 
|  | 1576 | } | 
|  | 1577 |  | 
|  | 1578 | /* Generate unconditional branch instructions */ | 
|  | 1579 | static ArmLIR* genUnconditionalBranch(CompilationUnit* cUnit, ArmLIR* target) | 
|  | 1580 | { | 
|  | 1581 | ArmLIR* branch = opNone(cUnit, kOpUncondBr); | 
|  | 1582 | branch->generic.target = (LIR*) target; | 
|  | 1583 | return branch; | 
|  | 1584 | } | 
|  | 1585 |  | 
|  | 1586 | /* | 
|  | 1587 | * Fetch *self->info.breakFlags. If the breakFlags are non-zero, | 
|  | 1588 | * punt to the interpreter. | 
|  | 1589 | */ | 
|  | 1590 | static void genSuspendPoll(CompilationUnit* cUnit, MIR* mir) | 
|  | 1591 | { | 
|  | 1592 | UNIMPLEMENTED(WARNING); | 
|  | 1593 | #if 0 | 
|  | 1594 | int rTemp = oatAllocTemp(cUnit); | 
|  | 1595 | ArmLIR* ld; | 
|  | 1596 | ld = loadBaseDisp(cUnit, NULL, rSELF, | 
|  | 1597 | offsetof(Thread, interpBreak.ctl.breakFlags), | 
|  | 1598 | rTemp, kUnsignedByte, INVALID_SREG); | 
|  | 1599 | setMemRefType(ld, true /* isLoad */, kMustNotAlias); | 
|  | 1600 | genRegImmCheck(cUnit, kArmCondNe, rTemp, 0, mir->offset, NULL); | 
|  | 1601 | #endif | 
|  | 1602 | } | 
|  | 1603 |  | 
|  | 1604 | /* | 
|  | 1605 | * The following are the first-level codegen routines that analyze the format | 
|  | 1606 | * of each bytecode then either dispatch special purpose codegen routines | 
|  | 1607 | * or produce corresponding Thumb instructions directly. | 
|  | 1608 | */ | 
|  | 1609 |  | 
|  | 1610 | static bool isPowerOfTwo(int x) | 
|  | 1611 | { | 
|  | 1612 | return (x & (x - 1)) == 0; | 
|  | 1613 | } | 
|  | 1614 |  | 
|  | 1615 | // Returns true if no more than two bits are set in 'x'. | 
|  | 1616 | static bool isPopCountLE2(unsigned int x) | 
|  | 1617 | { | 
|  | 1618 | x &= x - 1; | 
|  | 1619 | return (x & (x - 1)) == 0; | 
|  | 1620 | } | 
|  | 1621 |  | 
|  | 1622 | // Returns the index of the lowest set bit in 'x'. | 
|  | 1623 | static int lowestSetBit(unsigned int x) { | 
|  | 1624 | int bit_posn = 0; | 
|  | 1625 | while ((x & 0xf) == 0) { | 
|  | 1626 | bit_posn += 4; | 
|  | 1627 | x >>= 4; | 
|  | 1628 | } | 
|  | 1629 | while ((x & 1) == 0) { | 
|  | 1630 | bit_posn++; | 
|  | 1631 | x >>= 1; | 
|  | 1632 | } | 
|  | 1633 | return bit_posn; | 
|  | 1634 | } | 
|  | 1635 |  | 
|  | 1636 | // Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit' | 
|  | 1637 | // and store the result in 'rlDest'. | 
|  | 1638 | static bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode, | 
|  | 1639 | RegLocation rlSrc, RegLocation rlDest, int lit) | 
|  | 1640 | { | 
|  | 1641 | if (lit < 2 || !isPowerOfTwo(lit)) { | 
|  | 1642 | return false; | 
|  | 1643 | } | 
|  | 1644 | int k = lowestSetBit(lit); | 
|  | 1645 | if (k >= 30) { | 
|  | 1646 | // Avoid special cases. | 
|  | 1647 | return false; | 
|  | 1648 | } | 
|  | 1649 | bool div = (dalvikOpcode == OP_DIV_INT_LIT8 || | 
|  | 1650 | dalvikOpcode == OP_DIV_INT_LIT16); | 
|  | 1651 | rlSrc = loadValue(cUnit, rlSrc, kCoreReg); | 
|  | 1652 | RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1653 | if (div) { | 
|  | 1654 | int tReg = oatAllocTemp(cUnit); | 
|  | 1655 | if (lit == 2) { | 
|  | 1656 | // Division by 2 is by far the most common division by constant. | 
|  | 1657 | opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k); | 
|  | 1658 | opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg); | 
|  | 1659 | opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k); | 
|  | 1660 | } else { | 
|  | 1661 | opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31); | 
|  | 1662 | opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k); | 
|  | 1663 | opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg); | 
|  | 1664 | opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k); | 
|  | 1665 | } | 
|  | 1666 | } else { | 
|  | 1667 | int cReg = oatAllocTemp(cUnit); | 
|  | 1668 | loadConstant(cUnit, cReg, lit - 1); | 
|  | 1669 | int tReg1 = oatAllocTemp(cUnit); | 
|  | 1670 | int tReg2 = oatAllocTemp(cUnit); | 
|  | 1671 | if (lit == 2) { | 
|  | 1672 | opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k); | 
|  | 1673 | opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg); | 
|  | 1674 | opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg); | 
|  | 1675 | opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1); | 
|  | 1676 | } else { | 
|  | 1677 | opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31); | 
|  | 1678 | opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k); | 
|  | 1679 | opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg); | 
|  | 1680 | opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg); | 
|  | 1681 | opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1); | 
|  | 1682 | } | 
|  | 1683 | } | 
|  | 1684 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1685 | return true; | 
|  | 1686 | } | 
|  | 1687 |  | 
|  | 1688 | // Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit' | 
|  | 1689 | // and store the result in 'rlDest'. | 
|  | 1690 | static bool handleEasyMultiply(CompilationUnit* cUnit, | 
|  | 1691 | RegLocation rlSrc, RegLocation rlDest, int lit) | 
|  | 1692 | { | 
|  | 1693 | // Can we simplify this multiplication? | 
|  | 1694 | bool powerOfTwo = false; | 
|  | 1695 | bool popCountLE2 = false; | 
|  | 1696 | bool powerOfTwoMinusOne = false; | 
|  | 1697 | if (lit < 2) { | 
|  | 1698 | // Avoid special cases. | 
|  | 1699 | return false; | 
|  | 1700 | } else if (isPowerOfTwo(lit)) { | 
|  | 1701 | powerOfTwo = true; | 
|  | 1702 | } else if (isPopCountLE2(lit)) { | 
|  | 1703 | popCountLE2 = true; | 
|  | 1704 | } else if (isPowerOfTwo(lit + 1)) { | 
|  | 1705 | powerOfTwoMinusOne = true; | 
|  | 1706 | } else { | 
|  | 1707 | return false; | 
|  | 1708 | } | 
|  | 1709 | rlSrc = loadValue(cUnit, rlSrc, kCoreReg); | 
|  | 1710 | RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1711 | if (powerOfTwo) { | 
|  | 1712 | // Shift. | 
|  | 1713 | opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg, | 
|  | 1714 | lowestSetBit(lit)); | 
|  | 1715 | } else if (popCountLE2) { | 
|  | 1716 | // Shift and add and shift. | 
|  | 1717 | int firstBit = lowestSetBit(lit); | 
|  | 1718 | int secondBit = lowestSetBit(lit ^ (1 << firstBit)); | 
|  | 1719 | genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit, | 
|  | 1720 | firstBit, secondBit); | 
|  | 1721 | } else { | 
|  | 1722 | // Reverse subtract: (src << (shift + 1)) - src. | 
|  | 1723 | assert(powerOfTwoMinusOne); | 
|  | 1724 | // TODO: rsb dst, src, src lsl#lowestSetBit(lit + 1) | 
|  | 1725 | int tReg = oatAllocTemp(cUnit); | 
|  | 1726 | opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1)); | 
|  | 1727 | opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg); | 
|  | 1728 | } | 
|  | 1729 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1730 | return true; | 
|  | 1731 | } | 
|  | 1732 |  | 
|  | 1733 | static bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir, | 
|  | 1734 | RegLocation rlDest, RegLocation rlSrc, | 
|  | 1735 | int lit) | 
|  | 1736 | { | 
|  | 1737 | Opcode dalvikOpcode = mir->dalvikInsn.opcode; | 
|  | 1738 | RegLocation rlResult; | 
|  | 1739 | OpKind op = (OpKind)0;      /* Make gcc happy */ | 
|  | 1740 | int shiftOp = false; | 
|  | 1741 | bool isDiv = false; | 
|  | 1742 | int funcOffset; | 
|  | 1743 |  | 
|  | 1744 | switch (dalvikOpcode) { | 
|  | 1745 | case OP_RSUB_INT_LIT8: | 
|  | 1746 | case OP_RSUB_INT: { | 
|  | 1747 | int tReg; | 
|  | 1748 | //TUNING: add support for use of Arm rsub op | 
|  | 1749 | rlSrc = loadValue(cUnit, rlSrc, kCoreReg); | 
|  | 1750 | tReg = oatAllocTemp(cUnit); | 
|  | 1751 | loadConstant(cUnit, tReg, lit); | 
|  | 1752 | rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1753 | opRegRegReg(cUnit, kOpSub, rlResult.lowReg, | 
|  | 1754 | tReg, rlSrc.lowReg); | 
|  | 1755 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1756 | return false; | 
|  | 1757 | break; | 
|  | 1758 | } | 
|  | 1759 |  | 
|  | 1760 | case OP_ADD_INT_LIT8: | 
|  | 1761 | case OP_ADD_INT_LIT16: | 
|  | 1762 | op = kOpAdd; | 
|  | 1763 | break; | 
|  | 1764 | case OP_MUL_INT_LIT8: | 
|  | 1765 | case OP_MUL_INT_LIT16: { | 
|  | 1766 | if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) { | 
|  | 1767 | return false; | 
|  | 1768 | } | 
|  | 1769 | op = kOpMul; | 
|  | 1770 | break; | 
|  | 1771 | } | 
|  | 1772 | case OP_AND_INT_LIT8: | 
|  | 1773 | case OP_AND_INT_LIT16: | 
|  | 1774 | op = kOpAnd; | 
|  | 1775 | break; | 
|  | 1776 | case OP_OR_INT_LIT8: | 
|  | 1777 | case OP_OR_INT_LIT16: | 
|  | 1778 | op = kOpOr; | 
|  | 1779 | break; | 
|  | 1780 | case OP_XOR_INT_LIT8: | 
|  | 1781 | case OP_XOR_INT_LIT16: | 
|  | 1782 | op = kOpXor; | 
|  | 1783 | break; | 
|  | 1784 | case OP_SHL_INT_LIT8: | 
|  | 1785 | lit &= 31; | 
|  | 1786 | shiftOp = true; | 
|  | 1787 | op = kOpLsl; | 
|  | 1788 | break; | 
|  | 1789 | case OP_SHR_INT_LIT8: | 
|  | 1790 | lit &= 31; | 
|  | 1791 | shiftOp = true; | 
|  | 1792 | op = kOpAsr; | 
|  | 1793 | break; | 
|  | 1794 | case OP_USHR_INT_LIT8: | 
|  | 1795 | lit &= 31; | 
|  | 1796 | shiftOp = true; | 
|  | 1797 | op = kOpLsr; | 
|  | 1798 | break; | 
|  | 1799 |  | 
|  | 1800 | case OP_DIV_INT_LIT8: | 
|  | 1801 | case OP_DIV_INT_LIT16: | 
|  | 1802 | case OP_REM_INT_LIT8: | 
|  | 1803 | case OP_REM_INT_LIT16: | 
|  | 1804 | if (lit == 0) { | 
|  | 1805 | UNIMPLEMENTED(FATAL); | 
|  | 1806 | // FIXME: generate an explicit throw here | 
|  | 1807 | return false; | 
|  | 1808 | } | 
|  | 1809 | if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) { | 
|  | 1810 | return false; | 
|  | 1811 | } | 
|  | 1812 | oatFlushAllRegs(cUnit);   /* Everything to home location */ | 
|  | 1813 | loadValueDirectFixed(cUnit, rlSrc, r0); | 
|  | 1814 | oatClobber(cUnit, r0); | 
|  | 1815 | if ((dalvikOpcode == OP_DIV_INT_LIT8) || | 
|  | 1816 | (dalvikOpcode == OP_DIV_INT_LIT16)) { | 
|  | 1817 | funcOffset = OFFSETOF_MEMBER(Thread, pIdiv); | 
|  | 1818 | isDiv = true; | 
|  | 1819 | } else { | 
|  | 1820 | funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod); | 
|  | 1821 | isDiv = false; | 
|  | 1822 | } | 
|  | 1823 | loadWordDisp(cUnit, rSELF, funcOffset, rLR); | 
|  | 1824 | loadConstant(cUnit, r1, lit); | 
|  | 1825 | opReg(cUnit, kOpBlx, rLR); | 
|  | 1826 | oatClobberCallRegs(cUnit); | 
|  | 1827 | if (isDiv) | 
|  | 1828 | rlResult = oatGetReturn(cUnit); | 
|  | 1829 | else | 
|  | 1830 | rlResult = oatGetReturnAlt(cUnit); | 
|  | 1831 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1832 | return false; | 
|  | 1833 | break; | 
|  | 1834 | default: | 
|  | 1835 | return true; | 
|  | 1836 | } | 
|  | 1837 | rlSrc = loadValue(cUnit, rlSrc, kCoreReg); | 
|  | 1838 | rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true); | 
|  | 1839 | // Avoid shifts by literal 0 - no support in Thumb.  Change to copy | 
|  | 1840 | if (shiftOp && (lit == 0)) { | 
|  | 1841 | genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg); | 
|  | 1842 | } else { | 
|  | 1843 | opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit); | 
|  | 1844 | } | 
|  | 1845 | storeValue(cUnit, rlDest, rlResult); | 
|  | 1846 | return false; | 
|  | 1847 | } | 
|  | 1848 |  | 
|  | 1849 | /* Architectural-specific debugging helpers go here */ | 
|  | 1850 | void oatArchDump(void) | 
|  | 1851 | { | 
|  | 1852 | /* Print compiled opcode in this VM instance */ | 
|  | 1853 | int i, start, streak; | 
|  | 1854 | char buf[1024]; | 
|  | 1855 |  | 
|  | 1856 | streak = i = 0; | 
|  | 1857 | buf[0] = 0; | 
|  | 1858 | while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) { | 
|  | 1859 | i++; | 
|  | 1860 | } | 
|  | 1861 | if (i == kNumPackedOpcodes) { | 
|  | 1862 | return; | 
|  | 1863 | } | 
|  | 1864 | for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) { | 
|  | 1865 | if (opcodeCoverage[i]) { | 
|  | 1866 | streak++; | 
|  | 1867 | } else { | 
|  | 1868 | if (streak == 1) { | 
|  | 1869 | sprintf(buf+strlen(buf), "%x,", start); | 
|  | 1870 | } else { | 
|  | 1871 | sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1); | 
|  | 1872 | } | 
|  | 1873 | streak = 0; | 
|  | 1874 | while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) { | 
|  | 1875 | i++; | 
|  | 1876 | } | 
|  | 1877 | if (i < kNumPackedOpcodes) { | 
|  | 1878 | streak = 1; | 
|  | 1879 | start = i; | 
|  | 1880 | } | 
|  | 1881 | } | 
|  | 1882 | } | 
|  | 1883 | if (streak) { | 
|  | 1884 | if (streak == 1) { | 
|  | 1885 | sprintf(buf+strlen(buf), "%x", start); | 
|  | 1886 | } else { | 
|  | 1887 | sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1); | 
|  | 1888 | } | 
|  | 1889 | } | 
|  | 1890 | if (strlen(buf)) { | 
|  | 1891 | LOG(INFO) << "dalvik.vm.oat.op = " << buf; | 
|  | 1892 | } | 
|  | 1893 | } |