blob: db51fb8a3a8ff8e90c6d4867f08fd6ce163e209a [file] [log] [blame]
buzbee67bf8852011-08-17 17:51:35 -07001/*
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
buzbeece302932011-10-04 14:32:18 -070025#define SLOW_FIELD_PATH (cUnit->enableDebug & (1 << kDebugSlowFieldPath))
26#define SLOW_INVOKE_PATH (cUnit->enableDebug & (1 << kDebugSlowInvokePath))
27#define SLOW_STRING_PATH (cUnit->enableDebug & (1 << kDebugSlowStringPath))
28#define SLOW_TYPE_PATH (cUnit->enableDebug & (1 << kDebugSlowTypePath))
29#define EXERCISE_SLOWEST_FIELD_PATH (cUnit->enableDebug & \
30 (1 << kDebugSlowestFieldPath))
31#define EXERCISE_SLOWEST_STRING_PATH (cUnit->enableDebug & \
32 (1 << kDebugSlowestStringPath))
buzbee34c77ad2012-01-11 13:01:32 -080033#define EXERCISE_RESOLVE_METHOD (cUnit->enableDebug & \
34 (1 << kDebugExerciseResolveMethod))
buzbeece302932011-10-04 14:32:18 -070035
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080036namespace art {
37
buzbeece302932011-10-04 14:32:18 -070038STATIC RegLocation getRetLoc(CompilationUnit* cUnit);
buzbee34cd9e52011-09-08 14:31:52 -070039
Elliott Hughes81bc5092011-09-30 17:25:59 -070040void warnIfUnresolved(CompilationUnit* cUnit, int fieldIdx, Field* field) {
41 if (field == NULL) {
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080042 const DexFile::FieldId& field_id = cUnit->dex_file->GetFieldId(fieldIdx);
Elliott Hughes95572412011-12-13 18:14:20 -080043 std::string class_name(cUnit->dex_file->GetFieldDeclaringClassDescriptor(field_id));
44 std::string field_name(cUnit->dex_file->GetFieldName(field_id));
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080045 LOG(INFO) << "Field " << PrettyDescriptor(class_name) << "." << field_name
Elliott Hughes95572412011-12-13 18:14:20 -080046 << " unresolved at compile time";
Elliott Hughes81bc5092011-09-30 17:25:59 -070047 } else {
48 // We also use the slow path for wide volatile fields.
49 }
50}
51
buzbee67bf8852011-08-17 17:51:35 -070052/*
53 * Construct an s4 from two consecutive half-words of switch data.
54 * This needs to check endianness because the DEX optimizer only swaps
55 * half-words in instruction stream.
56 *
57 * "switchData" must be 32-bit aligned.
58 */
59#if __BYTE_ORDER == __LITTLE_ENDIAN
buzbeeed3e9302011-09-23 17:34:19 -070060STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070061 return *(s4*) switchData;
62}
63#else
buzbeeed3e9302011-09-23 17:34:19 -070064STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070065 u2* data = switchData;
66 return data[0] | (((s4) data[1]) << 16);
67}
68#endif
69
buzbeeed3e9302011-09-23 17:34:19 -070070STATIC ArmLIR* callRuntimeHelper(CompilationUnit* cUnit, int reg)
buzbeeec5adf32011-09-11 15:25:43 -070071{
buzbee6181f792011-09-29 11:14:04 -070072 oatClobberCalleeSave(cUnit);
buzbeeec5adf32011-09-11 15:25:43 -070073 return opReg(cUnit, kOpBlx, reg);
74}
75
buzbee1b4c8592011-08-31 10:43:51 -070076/* Generate unconditional branch instructions */
buzbeeed3e9302011-09-23 17:34:19 -070077STATIC ArmLIR* genUnconditionalBranch(CompilationUnit* cUnit, ArmLIR* target)
buzbee1b4c8592011-08-31 10:43:51 -070078{
79 ArmLIR* branch = opNone(cUnit, kOpUncondBr);
80 branch->generic.target = (LIR*) target;
81 return branch;
82}
83
buzbee67bf8852011-08-17 17:51:35 -070084/*
85 * Generate a Thumb2 IT instruction, which can nullify up to
86 * four subsequent instructions based on a condition and its
87 * inverse. The condition applies to the first instruction, which
88 * is executed if the condition is met. The string "guide" consists
89 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
90 * A "T" means the instruction is executed if the condition is
91 * met, and an "E" means the instruction is executed if the condition
92 * is not met.
93 */
buzbeeed3e9302011-09-23 17:34:19 -070094STATIC ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code,
buzbee67bf8852011-08-17 17:51:35 -070095 const char* guide)
96{
97 int mask;
98 int condBit = code & 1;
99 int altBit = condBit ^ 1;
100 int mask3 = 0;
101 int mask2 = 0;
102 int mask1 = 0;
103
104 //Note: case fallthroughs intentional
105 switch(strlen(guide)) {
106 case 3:
107 mask1 = (guide[2] == 'T') ? condBit : altBit;
108 case 2:
109 mask2 = (guide[1] == 'T') ? condBit : altBit;
110 case 1:
111 mask3 = (guide[0] == 'T') ? condBit : altBit;
112 break;
113 case 0:
114 break;
115 default:
116 LOG(FATAL) << "OAT: bad case in genIT";
117 }
118 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
119 (1 << (3 - strlen(guide)));
120 return newLIR2(cUnit, kThumb2It, code, mask);
121}
122
123/*
124 * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik
125 * offset vaddr. This label will be used to fix up the case
126 * branch table during the assembly phase. Be sure to set
127 * all resource flags on this to prevent code motion across
128 * target boundaries. KeyVal is just there for debugging.
129 */
buzbeeed3e9302011-09-23 17:34:19 -0700130STATIC ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
buzbee67bf8852011-08-17 17:51:35 -0700131{
buzbee85d8c1e2012-01-27 15:52:35 -0800132 std::map<unsigned int, LIR*>::iterator it;
133 it = cUnit->boundaryMap.find(vaddr);
134 if (it == cUnit->boundaryMap.end()) {
135 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
buzbee67bf8852011-08-17 17:51:35 -0700136 }
buzbeeba938cb2012-02-03 14:47:55 -0800137 ArmLIR* newLabel = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbee85d8c1e2012-01-27 15:52:35 -0800138 newLabel->generic.dalvikOffset = vaddr;
139 newLabel->opcode = kArmPseudoCaseLabel;
140 newLabel->operands[0] = keyVal;
141 oatInsertLIRAfter(it->second, (LIR*)newLabel);
142 return newLabel;
buzbee67bf8852011-08-17 17:51:35 -0700143}
144
buzbeeed3e9302011-09-23 17:34:19 -0700145STATIC void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700146{
147 const u2* table = tabRec->table;
148 int baseVaddr = tabRec->vaddr;
149 int *targets = (int*)&table[4];
150 int entries = table[1];
151 int lowKey = s4FromSwitchData(&table[2]);
152 for (int i = 0; i < entries; i++) {
153 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
154 i + lowKey);
155 }
156}
157
buzbeeed3e9302011-09-23 17:34:19 -0700158STATIC void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700159{
160 const u2* table = tabRec->table;
161 int baseVaddr = tabRec->vaddr;
162 int entries = table[1];
163 int* keys = (int*)&table[2];
164 int* targets = &keys[entries];
165 for (int i = 0; i < entries; i++) {
166 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
167 keys[i]);
168 }
169}
170
171void oatProcessSwitchTables(CompilationUnit* cUnit)
172{
173 GrowableListIterator iterator;
174 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
175 while (true) {
176 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
177 &iterator);
178 if (tabRec == NULL) break;
179 if (tabRec->table[0] == kPackedSwitchSignature)
180 markPackedCaseLabels(cUnit, tabRec);
181 else if (tabRec->table[0] == kSparseSwitchSignature)
182 markSparseCaseLabels(cUnit, tabRec);
183 else {
184 LOG(FATAL) << "Invalid switch table";
185 }
186 }
187}
188
buzbeeed3e9302011-09-23 17:34:19 -0700189STATIC void dumpSparseSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700190 /*
191 * Sparse switch data format:
192 * ushort ident = 0x0200 magic value
193 * ushort size number of entries in the table; > 0
194 * int keys[size] keys, sorted low-to-high; 32-bit aligned
195 * int targets[size] branch targets, relative to switch opcode
196 *
197 * Total size is (2+size*4) 16-bit code units.
198 */
199{
200 u2 ident = table[0];
201 int entries = table[1];
202 int* keys = (int*)&table[2];
203 int* targets = &keys[entries];
204 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
205 ", entries: " << std::dec << entries;
206 for (int i = 0; i < entries; i++) {
207 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
208 targets[i];
209 }
210}
211
buzbeeed3e9302011-09-23 17:34:19 -0700212STATIC void dumpPackedSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700213 /*
214 * Packed switch data format:
215 * ushort ident = 0x0100 magic value
216 * ushort size number of entries in the table
217 * int first_key first (and lowest) switch case value
218 * int targets[size] branch targets, relative to switch opcode
219 *
220 * Total size is (4+size*2) 16-bit code units.
221 */
222{
223 u2 ident = table[0];
224 int* targets = (int*)&table[4];
225 int entries = table[1];
226 int lowKey = s4FromSwitchData(&table[2]);
227 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
228 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
229 for (int i = 0; i < entries; i++) {
230 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
231 targets[i];
232 }
233}
234
235/*
236 * The sparse table in the literal pool is an array of <key,displacement>
237 * pairs. For each set, we'll load them as a pair using ldmia.
238 * This means that the register number of the temp we use for the key
239 * must be lower than the reg for the displacement.
240 *
241 * The test loop will look something like:
242 *
243 * adr rBase, <table>
244 * ldr rVal, [rSP, vRegOff]
245 * mov rIdx, #tableSize
246 * lp:
247 * ldmia rBase!, {rKey, rDisp}
248 * sub rIdx, #1
249 * cmp rVal, rKey
250 * ifeq
251 * add rPC, rDisp ; This is the branch from which we compute displacement
252 * cbnz rIdx, lp
253 */
buzbeeed3e9302011-09-23 17:34:19 -0700254STATIC void genSparseSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700255 RegLocation rlSrc)
256{
257 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
258 if (cUnit->printMe) {
259 dumpSparseSwitchTable(table);
260 }
261 // Add the table to the list - we'll process it later
buzbeeba938cb2012-02-03 14:47:55 -0800262 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
buzbee5abfa3e2012-01-31 17:01:43 -0800263 true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700264 tabRec->table = table;
265 tabRec->vaddr = mir->offset;
266 int size = table[1];
buzbeeba938cb2012-02-03 14:47:55 -0800267 tabRec->targets = (ArmLIR* *)oatNew(cUnit, size * sizeof(ArmLIR*), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800268 kAllocLIR);
buzbeeba938cb2012-02-03 14:47:55 -0800269 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700270
271 // Get the switch value
272 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
273 int rBase = oatAllocTemp(cUnit);
274 /* Allocate key and disp temps */
275 int rKey = oatAllocTemp(cUnit);
276 int rDisp = oatAllocTemp(cUnit);
277 // Make sure rKey's register number is less than rDisp's number for ldmia
278 if (rKey > rDisp) {
279 int tmp = rDisp;
280 rDisp = rKey;
281 rKey = tmp;
282 }
283 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700284 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700285 // Set up rIdx
286 int rIdx = oatAllocTemp(cUnit);
287 loadConstant(cUnit, rIdx, size);
288 // Establish loop branch target
289 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
290 target->defMask = ENCODE_ALL;
291 // Load next key/disp
292 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
293 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
294 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
295 genIT(cUnit, kArmCondEq, "");
296 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
297 tabRec->bxInst = switchBranch;
298 // Needs to use setflags encoding here
299 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
300 ArmLIR* branch = opCondBranch(cUnit, kArmCondNe);
301 branch->generic.target = (LIR*)target;
302}
303
304
buzbeeed3e9302011-09-23 17:34:19 -0700305STATIC void genPackedSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700306 RegLocation rlSrc)
307{
308 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
309 if (cUnit->printMe) {
310 dumpPackedSwitchTable(table);
311 }
312 // Add the table to the list - we'll process it later
buzbeeba938cb2012-02-03 14:47:55 -0800313 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
buzbee5abfa3e2012-01-31 17:01:43 -0800314 true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700315 tabRec->table = table;
316 tabRec->vaddr = mir->offset;
317 int size = table[1];
buzbeeba938cb2012-02-03 14:47:55 -0800318 tabRec->targets = (ArmLIR* *)oatNew(cUnit, size * sizeof(ArmLIR*), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800319 kAllocLIR);
buzbeeba938cb2012-02-03 14:47:55 -0800320 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700321
322 // Get the switch value
323 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
324 int tableBase = oatAllocTemp(cUnit);
325 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700326 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700327 int lowKey = s4FromSwitchData(&table[2]);
328 int keyReg;
329 // Remove the bias, if necessary
330 if (lowKey == 0) {
331 keyReg = rlSrc.lowReg;
332 } else {
333 keyReg = oatAllocTemp(cUnit);
334 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
335 }
336 // Bounds check - if < 0 or >= size continue following switch
337 opRegImm(cUnit, kOpCmp, keyReg, size-1);
338 ArmLIR* branchOver = opCondBranch(cUnit, kArmCondHi);
339
340 // Load the displacement from the switch table
341 int dispReg = oatAllocTemp(cUnit);
342 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
343
344 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
345 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
346 tabRec->bxInst = switchBranch;
347
348 /* branchOver target here */
349 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
350 target->defMask = ENCODE_ALL;
351 branchOver->generic.target = (LIR*)target;
352}
353
354/*
355 * Array data table format:
356 * ushort ident = 0x0300 magic value
357 * ushort width width of each element in the table
358 * uint size number of elements in the table
359 * ubyte data[size*width] table of data values (may contain a single-byte
360 * padding at the end)
361 *
362 * Total size is 4+(width * size + 1)/2 16-bit code units.
363 */
buzbeeed3e9302011-09-23 17:34:19 -0700364STATIC void genFillArrayData(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700365 RegLocation rlSrc)
366{
367 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
368 // Add the table to the list - we'll process it later
369 FillArrayData *tabRec = (FillArrayData *)
buzbeeba938cb2012-02-03 14:47:55 -0800370 oatNew(cUnit, sizeof(FillArrayData), true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700371 tabRec->table = table;
372 tabRec->vaddr = mir->offset;
373 u2 width = tabRec->table[1];
374 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
375 tabRec->size = (size * width) + 8;
376
buzbeeba938cb2012-02-03 14:47:55 -0800377 oatInsertGrowableList(cUnit, &cUnit->fillArrayData, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700378
379 // Making a call - use explicit registers
380 oatFlushAllRegs(cUnit); /* Everything to home location */
381 loadValueDirectFixed(cUnit, rlSrc, r0);
382 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700383 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
buzbeee6d61962011-08-27 11:58:19 -0700384 // Materialize a pointer to the fill data image
buzbee03fa2632011-09-20 17:10:57 -0700385 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
Ian Rogersff1ed472011-09-20 13:46:24 -0700386 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700387}
388
389/*
390 * Mark garbage collection card. Skip if the value we're storing is null.
391 */
buzbeeed3e9302011-09-23 17:34:19 -0700392STATIC void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg)
buzbee67bf8852011-08-17 17:51:35 -0700393{
394 int regCardBase = oatAllocTemp(cUnit);
395 int regCardNo = oatAllocTemp(cUnit);
396 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0);
buzbeec143c552011-08-20 17:38:58 -0700397 loadWordDisp(cUnit, rSELF, Thread::CardTableOffset().Int32Value(),
buzbee67bf8852011-08-17 17:51:35 -0700398 regCardBase);
399 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
400 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
401 kUnsignedByte);
402 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
403 target->defMask = ENCODE_ALL;
404 branchOver->generic.target = (LIR*)target;
405 oatFreeTemp(cUnit, regCardBase);
406 oatFreeTemp(cUnit, regCardNo);
407}
408
buzbeeed3e9302011-09-23 17:34:19 -0700409STATIC void genIGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
Ian Rogers1bddec32012-02-04 12:27:34 -0800410 RegLocation rlDest, RegLocation rlObj,
411 bool isLongOrDouble, bool isObject)
buzbee67bf8852011-08-17 17:51:35 -0700412{
Ian Rogers1bddec32012-02-04 12:27:34 -0800413 int fieldOffset;
414 bool isVolatile;
415 uint32_t fieldIdx = mir->dalvikInsn.vC;
416 bool fastPath =
417 cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, cUnit,
418 fieldOffset, isVolatile);
419 if (fastPath && !SLOW_FIELD_PATH) {
420 RegLocation rlResult;
421 RegisterClass regClass = oatRegClassBySize(size);
422 DCHECK_GE(fieldOffset, 0);
buzbee34cd9e52011-09-08 14:31:52 -0700423 rlObj = loadValue(cUnit, rlObj, kCoreReg);
Ian Rogers1bddec32012-02-04 12:27:34 -0800424 if (isLongOrDouble) {
425 DCHECK(rlDest.wide);
426 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
427 int regPtr = oatAllocTemp(cUnit);
428 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
429 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
430 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
431 if (isVolatile) {
432 oatGenMemBarrier(cUnit, kSY);
433 }
434 oatFreeTemp(cUnit, regPtr);
435 storeValueWide(cUnit, rlDest, rlResult);
436 } else {
437 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
438 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
439 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
440 kWord, rlObj.sRegLow);
441 if (isVolatile) {
442 oatGenMemBarrier(cUnit, kSY);
443 }
444 storeValue(cUnit, rlDest, rlResult);
buzbee34cd9e52011-09-08 14:31:52 -0700445 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800446 } else {
447 int getterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pGet64Instance) :
448 (isObject ? OFFSETOF_MEMBER(Thread, pGetObjInstance)
449 : OFFSETOF_MEMBER(Thread, pGet32Instance));
450 loadWordDisp(cUnit, rSELF, getterOffset, rLR);
451 loadValueDirect(cUnit, rlObj, r1);
452 loadConstant(cUnit, r0, fieldIdx);
453 callRuntimeHelper(cUnit, rLR);
454 if (isLongOrDouble) {
455 RegLocation rlResult = oatGetReturnWide(cUnit);
456 storeValueWide(cUnit, rlDest, rlResult);
457 } else {
458 RegLocation rlResult = oatGetReturn(cUnit);
459 storeValue(cUnit, rlDest, rlResult);
460 }
buzbee67bf8852011-08-17 17:51:35 -0700461 }
buzbee67bf8852011-08-17 17:51:35 -0700462}
463
buzbeeed3e9302011-09-23 17:34:19 -0700464STATIC void genIPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
Ian Rogers1bddec32012-02-04 12:27:34 -0800465 RegLocation rlSrc, RegLocation rlObj,
466 bool isLongOrDouble, bool isObject)
buzbee67bf8852011-08-17 17:51:35 -0700467{
Ian Rogers1bddec32012-02-04 12:27:34 -0800468 int fieldOffset;
469 bool isVolatile;
470 uint32_t fieldIdx = mir->dalvikInsn.vC;
471 bool fastPath =
472 cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, cUnit,
473 fieldOffset, isVolatile);
474 if (fastPath && !SLOW_FIELD_PATH) {
475 RegisterClass regClass = oatRegClassBySize(size);
476 DCHECK_GE(fieldOffset, 0);
buzbee34cd9e52011-09-08 14:31:52 -0700477 rlObj = loadValue(cUnit, rlObj, kCoreReg);
Ian Rogers1bddec32012-02-04 12:27:34 -0800478 if (isLongOrDouble) {
479 int regPtr;
480 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
481 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
482 regPtr = oatAllocTemp(cUnit);
483 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
484 if (isVolatile) {
485 oatGenMemBarrier(cUnit, kST);
486 }
487 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
488 if (isVolatile) {
489 oatGenMemBarrier(cUnit, kSY);
490 }
491 oatFreeTemp(cUnit, regPtr);
492 } else {
493 rlSrc = loadValue(cUnit, rlSrc, regClass);
494 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
495 if (isVolatile) {
496 oatGenMemBarrier(cUnit, kST);
497 }
498 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, kWord);
499 if (isVolatile) {
500 oatGenMemBarrier(cUnit, kSY);
501 }
buzbee34cd9e52011-09-08 14:31:52 -0700502 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800503 } else {
504 int setterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pSet64Instance) :
505 (isObject ? OFFSETOF_MEMBER(Thread, pSetObjInstance)
506 : OFFSETOF_MEMBER(Thread, pSet32Instance));
507 loadWordDisp(cUnit, rSELF, setterOffset, rLR);
508 loadValueDirect(cUnit, rlObj, r1);
509 if (isLongOrDouble) {
510 loadValueDirectWide(cUnit, rlSrc, r2, r3);
511 } else {
512 loadValueDirect(cUnit, rlSrc, r2);
buzbee12246b82011-09-29 14:15:05 -0700513 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800514 loadConstant(cUnit, r0, fieldIdx);
515 callRuntimeHelper(cUnit, rLR);
buzbee34cd9e52011-09-08 14:31:52 -0700516 }
buzbee67bf8852011-08-17 17:51:35 -0700517}
518
buzbeeed3e9302011-09-23 17:34:19 -0700519STATIC void genConstClass(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700520 RegLocation rlDest, RegLocation rlSrc)
521{
Ian Rogers28ad40d2011-10-27 15:19:26 -0700522 uint32_t type_idx = mir->dalvikInsn.vB;
buzbee1b4c8592011-08-31 10:43:51 -0700523 int mReg = loadCurrMethod(cUnit);
524 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700525 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800526 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
527 cUnit->dex_cache,
528 *cUnit->dex_file,
529 type_idx)) {
530 // Call out to helper which resolves type and verifies access.
531 // Resolved type returned in r0.
532 loadWordDisp(cUnit, rSELF,
533 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
534 rLR);
535 genRegCopy(cUnit, r1, mReg);
536 loadConstant(cUnit, r0, type_idx);
537 callRuntimeHelper(cUnit, rLR);
538 RegLocation rlResult = oatGetReturn(cUnit);
539 storeValue(cUnit, rlDest, rlResult);
buzbee1b4c8592011-08-31 10:43:51 -0700540 } else {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700541 // We're don't need access checks, load type from dex cache
542 int32_t dex_cache_offset = Method::DexCacheResolvedTypesOffset().Int32Value();
543 loadWordDisp(cUnit, mReg, dex_cache_offset, resReg);
544 int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
545 loadWordDisp(cUnit, resReg, offset_of_type, rlResult.lowReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800546 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache,
547 type_idx) ||
Ian Rogers28ad40d2011-10-27 15:19:26 -0700548 SLOW_TYPE_PATH) {
549 // Slow path, at runtime test if the type is null and if so initialize
550 oatFlushAllRegs(cUnit);
551 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg, 0);
552 // Resolved, store and hop over following code
553 storeValue(cUnit, rlDest, rlResult);
554 ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
555 // TUNING: move slow path to end & remove unconditional branch
556 ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
557 target1->defMask = ENCODE_ALL;
558 // Call out to helper, which will return resolved type in r0
559 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
560 genRegCopy(cUnit, r1, mReg);
561 loadConstant(cUnit, r0, type_idx);
562 callRuntimeHelper(cUnit, rLR);
563 RegLocation rlResult = oatGetReturn(cUnit);
564 storeValue(cUnit, rlDest, rlResult);
565 // Rejoin code paths
566 ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
567 target2->defMask = ENCODE_ALL;
568 branch1->generic.target = (LIR*)target1;
569 branch2->generic.target = (LIR*)target2;
570 } else {
571 // Fast path, we're done - just store result
572 storeValue(cUnit, rlDest, rlResult);
573 }
buzbee1b4c8592011-08-31 10:43:51 -0700574 }
buzbee67bf8852011-08-17 17:51:35 -0700575}
576
buzbee44b412b2012-02-04 08:50:53 -0800577/*
578 * Generate callout to updateDebugger. Note: genIT will automatically
579 * create a scheduling barrier, which we need to prevent code motion that
580 * might confuse the debugger. Note: Return registers r0/r1 are
581 * handled specially during code generation following function calls.
582 * Typically, temp registers are not live between opcodes, but we keep
583 * r0/r1 live following invokes, where they are consumed by the immediately
584 * following op_move_result_xxx. Thus, we must preserve and restore r0/r1
585 * when making a call to update the debugger. This is handled by the stub.
586 */
587STATIC void genDebuggerUpdate(CompilationUnit* cUnit, int32_t offset)
588{
589 // Following DCHECK verifies that dPC is in range of single load immediate
590 DCHECK((offset == DEBUGGER_METHOD_ENTRY) ||
591 (offset == DEBUGGER_METHOD_EXIT) || ((offset & 0xffff) == offset));
592 oatClobberCalleeSave(cUnit);
593 opRegImm(cUnit, kOpCmp, rSUSPEND, 0);
594 genIT(cUnit, kArmCondNe, "T");
595 loadConstant(cUnit, r2, offset); // arg2 <- Entry code
596 opReg(cUnit, kOpBlx, rSUSPEND);
597 oatFreeTemp(cUnit, r2);
598}
599
buzbeeed3e9302011-09-23 17:34:19 -0700600STATIC void genConstString(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700601 RegLocation rlDest, RegLocation rlSrc)
602{
buzbeece302932011-10-04 14:32:18 -0700603 /* NOTE: Most strings should be available at compile time */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700604 uint32_t string_idx = mir->dalvikInsn.vB;
605 int32_t offset_of_string = Array::DataOffset().Int32Value() + (sizeof(String*) * string_idx);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800606 if (!cUnit->compiler->CanAssumeStringIsPresentInDexCache(cUnit->dex_cache, string_idx) ||
Ian Rogers28ad40d2011-10-27 15:19:26 -0700607 SLOW_STRING_PATH) {
608 // slow path, resolve string if not in dex cache
buzbeece302932011-10-04 14:32:18 -0700609 oatFlushAllRegs(cUnit);
610 oatLockCallTemps(cUnit); // Using explicit registers
611 loadCurrMethodDirect(cUnit, r2);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700612 loadWordDisp(cUnit, r2, Method::DexCacheStringsOffset().Int32Value(), r0);
buzbeece302932011-10-04 14:32:18 -0700613 // Might call out to helper, which will return resolved string in r0
Ian Rogers28ad40d2011-10-27 15:19:26 -0700614 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pResolveStringFromCode), rLR);
615 loadWordDisp(cUnit, r0, offset_of_string, r0);
616 loadConstant(cUnit, r1, string_idx);
buzbeece302932011-10-04 14:32:18 -0700617 opRegImm(cUnit, kOpCmp, r0, 0); // Is resolved?
618 genBarrier(cUnit);
619 // For testing, always force through helper
620 if (!EXERCISE_SLOWEST_STRING_PATH) {
621 genIT(cUnit, kArmCondEq, "T");
622 }
623 genRegCopy(cUnit, r0, r2); // .eq
624 opReg(cUnit, kOpBlx, rLR); // .eq, helper(Method*, string_idx)
625 genBarrier(cUnit);
626 storeValue(cUnit, rlDest, getRetLoc(cUnit));
627 } else {
628 int mReg = loadCurrMethod(cUnit);
629 int resReg = oatAllocTemp(cUnit);
630 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700631 loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(), resReg);
632 loadWordDisp(cUnit, resReg, offset_of_string, rlResult.lowReg);
buzbeece302932011-10-04 14:32:18 -0700633 storeValue(cUnit, rlDest, rlResult);
634 }
buzbee67bf8852011-08-17 17:51:35 -0700635}
636
buzbeedfd3d702011-08-28 12:56:51 -0700637/*
638 * Let helper function take care of everything. Will
639 * call Class::NewInstanceFromCode(type_idx, method);
640 */
buzbeeed3e9302011-09-23 17:34:19 -0700641STATIC void genNewInstance(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700642 RegLocation rlDest)
643{
buzbeedfd3d702011-08-28 12:56:51 -0700644 oatFlushAllRegs(cUnit); /* Everything to home location */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700645 uint32_t type_idx = mir->dalvikInsn.vB;
646 // alloc will always check for resolution, do we also need to verify access because the
647 // verifier was unable to?
Ian Rogersd4135902012-02-03 18:05:08 -0800648 if (cUnit->compiler->CanAccessInstantiableTypeWithoutChecks(cUnit->method_idx,
649 cUnit->dex_cache,
650 *cUnit->dex_file,
651 type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700652 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
653 } else {
654 loadWordDisp(cUnit, rSELF,
655 OFFSETOF_MEMBER(Thread, pAllocObjectFromCodeWithAccessCheck), rLR);
656 }
657 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
658 loadConstant(cUnit, r0, type_idx); // arg0 <- type_idx
Ian Rogersff1ed472011-09-20 13:46:24 -0700659 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700660 RegLocation rlResult = oatGetReturn(cUnit);
661 storeValue(cUnit, rlDest, rlResult);
662}
663
664void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
665{
buzbee6181f792011-09-29 11:14:04 -0700666 oatFlushAllRegs(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700667 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
Ian Rogersbdb03912011-09-14 00:55:44 -0700668 loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
Ian Rogersff1ed472011-09-20 13:46:24 -0700669 callRuntimeHelper(cUnit, rLR); // art_deliver_exception(exception);
buzbee67bf8852011-08-17 17:51:35 -0700670}
671
buzbeeed3e9302011-09-23 17:34:19 -0700672STATIC void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700673 RegLocation rlSrc)
674{
buzbee6181f792011-09-29 11:14:04 -0700675 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700676 // May generate a call - use explicit registers
677 oatLockCallTemps(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700678 uint32_t type_idx = mir->dalvikInsn.vC;
buzbee2a475e72011-09-07 17:19:17 -0700679 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
Ian Rogers28ad40d2011-10-27 15:19:26 -0700680 int classReg = r2; // r2 will hold the Class*
Ian Rogersa3760aa2011-11-14 14:32:37 -0800681 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
682 cUnit->dex_cache,
683 *cUnit->dex_file,
684 type_idx)) {
Ian Rogersb093c6b2011-10-31 16:19:55 -0700685 // Check we have access to type_idx and if not throw IllegalAccessError,
686 // returns Class* in r0
687 loadWordDisp(cUnit, rSELF,
688 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
689 rLR);
690 loadConstant(cUnit, r0, type_idx);
691 callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
692 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers6a996782011-10-31 17:06:39 -0700693 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
Ian Rogers28ad40d2011-10-27 15:19:26 -0700694 } else {
695 // Load dex cache entry into classReg (r2)
Ian Rogers6a996782011-10-31 17:06:39 -0700696 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
Ian Rogers28ad40d2011-10-27 15:19:26 -0700697 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
698 int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
699 loadWordDisp(cUnit, classReg, offset_of_type, classReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800700 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache, type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700701 // Need to test presence of type in dex cache at runtime
702 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
703 // Not resolved
704 // Call out to helper, which will return resolved type in r0
705 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
706 loadConstant(cUnit, r0, type_idx);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700707 callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
Ian Rogers28ad40d2011-10-27 15:19:26 -0700708 genRegCopy(cUnit, r2, r0); // Align usage with fast path
709 loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */
710 // Rejoin code paths
711 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
712 hopTarget->defMask = ENCODE_ALL;
713 hopBranch->generic.target = (LIR*)hopTarget;
714 }
buzbee67bf8852011-08-17 17:51:35 -0700715 }
buzbee991e3ac2011-09-29 15:44:22 -0700716 /* r0 is ref, r2 is class. If ref==null, use directly as bool result */
717 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
buzbee2a475e72011-09-07 17:19:17 -0700718 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700719 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee991e3ac2011-09-29 15:44:22 -0700720 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
721 /* r0 is ref, r1 is ref->clazz, r2 is class */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700722 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
buzbee991e3ac2011-09-29 15:44:22 -0700723 opRegReg(cUnit, kOpCmp, r1, r2); // Same?
724 genBarrier(cUnit);
725 genIT(cUnit, kArmCondEq, "EE"); // if-convert the test
726 loadConstant(cUnit, r0, 1); // .eq case - load true
727 genRegCopy(cUnit, r0, r2); // .ne case - arg0 <= class
728 opReg(cUnit, kOpBlx, rLR); // .ne case: helper(class, ref->class)
729 genBarrier(cUnit);
730 oatClobberCalleeSave(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700731 /* branch target here */
732 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
733 target->defMask = ENCODE_ALL;
buzbee2a475e72011-09-07 17:19:17 -0700734 RegLocation rlResult = oatGetReturn(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700735 storeValue(cUnit, rlDest, rlResult);
736 branch1->generic.target = (LIR*)target;
buzbee67bf8852011-08-17 17:51:35 -0700737}
738
buzbeeed3e9302011-09-23 17:34:19 -0700739STATIC void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbee67bf8852011-08-17 17:51:35 -0700740{
buzbee6181f792011-09-29 11:14:04 -0700741 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700742 // May generate a call - use explicit registers
743 oatLockCallTemps(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700744 uint32_t type_idx = mir->dalvikInsn.vB;
buzbee2a475e72011-09-07 17:19:17 -0700745 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
Ian Rogers28ad40d2011-10-27 15:19:26 -0700746 int classReg = r2; // r2 will hold the Class*
Ian Rogersa3760aa2011-11-14 14:32:37 -0800747 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
748 cUnit->dex_cache,
749 *cUnit->dex_file,
750 type_idx)) {
Ian Rogersb093c6b2011-10-31 16:19:55 -0700751 // Check we have access to type_idx and if not throw IllegalAccessError,
752 // returns Class* in r0
753 loadWordDisp(cUnit, rSELF,
754 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
755 rLR);
756 loadConstant(cUnit, r0, type_idx);
757 callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
758 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers28ad40d2011-10-27 15:19:26 -0700759 } else {
760 // Load dex cache entry into classReg (r2)
761 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
762 int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
763 loadWordDisp(cUnit, classReg, offset_of_type, classReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800764 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache, type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700765 // Need to test presence of type in dex cache at runtime
766 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
767 // Not resolved
768 // Call out to helper, which will return resolved type in r0
769 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
770 loadConstant(cUnit, r0, type_idx);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700771 callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
772 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers28ad40d2011-10-27 15:19:26 -0700773 // Rejoin code paths
774 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
775 hopTarget->defMask = ENCODE_ALL;
776 hopBranch->generic.target = (LIR*)hopTarget;
777 }
buzbee67bf8852011-08-17 17:51:35 -0700778 }
Ian Rogers28ad40d2011-10-27 15:19:26 -0700779 // At this point, classReg (r2) has class
780 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
buzbee2a475e72011-09-07 17:19:17 -0700781 /* Null is OK - continue */
782 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
783 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700784 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee2a475e72011-09-07 17:19:17 -0700785 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
786 /* r1 now contains object->clazz */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700787 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700788 opRegReg(cUnit, kOpCmp, r1, classReg);
buzbee2a475e72011-09-07 17:19:17 -0700789 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
790 genRegCopy(cUnit, r0, r1);
791 genRegCopy(cUnit, r1, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -0700792 callRuntimeHelper(cUnit, rLR);
buzbee2a475e72011-09-07 17:19:17 -0700793 /* branch target here */
buzbee67bf8852011-08-17 17:51:35 -0700794 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
795 target->defMask = ENCODE_ALL;
796 branch1->generic.target = (LIR*)target;
797 branch2->generic.target = (LIR*)target;
798}
799
buzbeeed3e9302011-09-23 17:34:19 -0700800STATIC void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700801 RegLocation rlSrc)
802{
803 RegLocation rlResult;
804 rlSrc = loadValue(cUnit, rlSrc, kFPReg);
805 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
806 newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
807 storeValue(cUnit, rlDest, rlResult);
808}
809
buzbeeed3e9302011-09-23 17:34:19 -0700810STATIC void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700811 RegLocation rlSrc)
812{
813 RegLocation rlResult;
814 rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
815 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
816 newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
817 S2D(rlSrc.lowReg, rlSrc.highReg));
818 storeValueWide(cUnit, rlDest, rlResult);
819}
820
buzbeeed3e9302011-09-23 17:34:19 -0700821STATIC void freeRegLocTemps(CompilationUnit* cUnit, RegLocation rlKeep,
buzbee439c4fa2011-08-27 15:59:07 -0700822 RegLocation rlFree)
buzbee67bf8852011-08-17 17:51:35 -0700823{
buzbee6181f792011-09-29 11:14:04 -0700824 if ((rlFree.lowReg != rlKeep.lowReg) && (rlFree.lowReg != rlKeep.highReg) &&
825 (rlFree.highReg != rlKeep.lowReg) && (rlFree.highReg != rlKeep.highReg)) {
826 // No overlap, free both
buzbee439c4fa2011-08-27 15:59:07 -0700827 oatFreeTemp(cUnit, rlFree.lowReg);
buzbee6181f792011-09-29 11:14:04 -0700828 oatFreeTemp(cUnit, rlFree.highReg);
829 }
buzbee67bf8852011-08-17 17:51:35 -0700830}
831
buzbeeed3e9302011-09-23 17:34:19 -0700832STATIC void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp,
buzbee67bf8852011-08-17 17:51:35 -0700833 OpKind secondOp, RegLocation rlDest,
834 RegLocation rlSrc1, RegLocation rlSrc2)
835{
buzbee9e0f9b02011-08-24 15:32:46 -0700836 /*
837 * NOTE: This is the one place in the code in which we might have
838 * as many as six live temporary registers. There are 5 in the normal
839 * set for Arm. Until we have spill capabilities, temporarily add
840 * lr to the temp set. It is safe to do this locally, but note that
841 * lr is used explicitly elsewhere in the code generator and cannot
842 * normally be used as a general temp register.
843 */
buzbee67bf8852011-08-17 17:51:35 -0700844 RegLocation rlResult;
buzbee9e0f9b02011-08-24 15:32:46 -0700845 oatMarkTemp(cUnit, rLR); // Add lr to the temp pool
846 oatFreeTemp(cUnit, rLR); // and make it available
buzbee67bf8852011-08-17 17:51:35 -0700847 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
848 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
849 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeec0ecd652011-09-25 18:11:54 -0700850 // The longs may overlap - use intermediate temp if so
851 if (rlResult.lowReg == rlSrc1.highReg) {
buzbeec0ecd652011-09-25 18:11:54 -0700852 int tReg = oatAllocTemp(cUnit);
853 genRegCopy(cUnit, tReg, rlSrc1.highReg);
854 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
855 rlSrc2.lowReg);
856 opRegRegReg(cUnit, secondOp, rlResult.highReg, tReg,
857 rlSrc2.highReg);
858 oatFreeTemp(cUnit, tReg);
859 } else {
860 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
861 rlSrc2.lowReg);
862 opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
863 rlSrc2.highReg);
864 }
buzbee439c4fa2011-08-27 15:59:07 -0700865 /*
866 * NOTE: If rlDest refers to a frame variable in a large frame, the
867 * following storeValueWide might need to allocate a temp register.
868 * To further work around the lack of a spill capability, explicitly
869 * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult.
870 * Remove when spill is functional.
871 */
872 freeRegLocTemps(cUnit, rlResult, rlSrc1);
873 freeRegLocTemps(cUnit, rlResult, rlSrc2);
buzbee67bf8852011-08-17 17:51:35 -0700874 storeValueWide(cUnit, rlDest, rlResult);
buzbee9e0f9b02011-08-24 15:32:46 -0700875 oatClobber(cUnit, rLR);
876 oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool
buzbee67bf8852011-08-17 17:51:35 -0700877}
878
879void oatInitializeRegAlloc(CompilationUnit* cUnit)
880{
881 int numRegs = sizeof(coreRegs)/sizeof(*coreRegs);
882 int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs);
883 int numTemps = sizeof(coreTemps)/sizeof(*coreTemps);
884 int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs);
885 int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps);
buzbeeba938cb2012-02-03 14:47:55 -0800886 RegisterPool *pool = (RegisterPool *)oatNew(cUnit, sizeof(*pool), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800887 kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700888 cUnit->regPool = pool;
889 pool->numCoreRegs = numRegs;
890 pool->coreRegs = (RegisterInfo *)
buzbeeba938cb2012-02-03 14:47:55 -0800891 oatNew(cUnit, numRegs * sizeof(*cUnit->regPool->coreRegs),
892 true, kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700893 pool->numFPRegs = numFPRegs;
894 pool->FPRegs = (RegisterInfo *)
buzbeeba938cb2012-02-03 14:47:55 -0800895 oatNew(cUnit, numFPRegs * sizeof(*cUnit->regPool->FPRegs), true,
896 kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700897 oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs);
898 oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs);
899 // Keep special registers from being allocated
900 for (int i = 0; i < numReserved; i++) {
buzbee44b412b2012-02-04 08:50:53 -0800901 if (NO_SUSPEND && !cUnit->genDebugger &&
902 (reservedRegs[i] == rSUSPEND)) {
buzbeec0ecd652011-09-25 18:11:54 -0700903 //To measure cost of suspend check
904 continue;
905 }
buzbee67bf8852011-08-17 17:51:35 -0700906 oatMarkInUse(cUnit, reservedRegs[i]);
907 }
908 // Mark temp regs - all others not in use can be used for promotion
909 for (int i = 0; i < numTemps; i++) {
910 oatMarkTemp(cUnit, coreTemps[i]);
911 }
912 for (int i = 0; i < numFPTemps; i++) {
913 oatMarkTemp(cUnit, fpTemps[i]);
914 }
buzbeec0ecd652011-09-25 18:11:54 -0700915 // Construct the alias map.
buzbeeba938cb2012-02-03 14:47:55 -0800916 cUnit->phiAliasMap = (int*)oatNew(cUnit, cUnit->numSSARegs *
buzbee5abfa3e2012-01-31 17:01:43 -0800917 sizeof(cUnit->phiAliasMap[0]), false,
918 kAllocDFInfo);
buzbeec0ecd652011-09-25 18:11:54 -0700919 for (int i = 0; i < cUnit->numSSARegs; i++) {
920 cUnit->phiAliasMap[i] = i;
921 }
922 for (MIR* phi = cUnit->phiList; phi; phi = phi->meta.phiNext) {
923 int defReg = phi->ssaRep->defs[0];
924 for (int i = 0; i < phi->ssaRep->numUses; i++) {
925 for (int j = 0; j < cUnit->numSSARegs; j++) {
926 if (cUnit->phiAliasMap[j] == phi->ssaRep->uses[i]) {
927 cUnit->phiAliasMap[j] = defReg;
928 }
929 }
930 }
931 }
buzbee67bf8852011-08-17 17:51:35 -0700932}
933
934/*
935 * Handle simple case (thin lock) inline. If it's complicated, bail
936 * out to the heavyweight lock/unlock routines. We'll use dedicated
937 * registers here in order to be in the right position in case we
938 * to bail to dvm[Lock/Unlock]Object(self, object)
939 *
940 * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
941 * r1 -> object [arg1 for dvm[Lock/Unlock]Object
942 * r2 -> intial contents of object->lock, later result of strex
943 * r3 -> self->threadId
944 * r12 -> allow to be used by utilities as general temp
945 *
946 * The result of the strex is 0 if we acquire the lock.
947 *
948 * See comments in Sync.c for the layout of the lock word.
949 * Of particular interest to this code is the test for the
950 * simple case - which we handle inline. For monitor enter, the
951 * simple case is thin lock, held by no-one. For monitor exit,
952 * the simple case is thin lock, held by the unlocking thread with
953 * a recurse count of 0.
954 *
955 * A minor complication is that there is a field in the lock word
956 * unrelated to locking: the hash state. This field must be ignored, but
957 * preserved.
958 *
959 */
buzbeeed3e9302011-09-23 17:34:19 -0700960STATIC void genMonitorEnter(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700961 RegLocation rlSrc)
962{
963 ArmLIR* target;
964 ArmLIR* hopTarget;
965 ArmLIR* branch;
966 ArmLIR* hopBranch;
967
968 oatFlushAllRegs(cUnit);
Elliott Hughes5f791332011-09-15 17:45:30 -0700969 DCHECK_EQ(LW_SHAPE_THIN, 0);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700970 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700971 oatLockCallTemps(cUnit); // Prepare for explicit register usage
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700972 genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
973 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
974 newLIR3(cUnit, kThumb2Ldrex, r1, r0,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700975 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
buzbeec143c552011-08-20 17:38:58 -0700976 // Align owner
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700977 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
buzbee67bf8852011-08-17 17:51:35 -0700978 // Is lock unheld on lock or held by us (==threadId) on unlock?
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700979 newLIR4(cUnit, kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
980 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
981 hopBranch = newLIR2(cUnit, kThumb2Cbnz, r1, 0);
982 newLIR4(cUnit, kThumb2Strex, r1, r2, r0,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700983 Object::MonitorOffset().Int32Value() >> 2);
buzbee67bf8852011-08-17 17:51:35 -0700984 oatGenMemBarrier(cUnit, kSY);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700985 branch = newLIR2(cUnit, kThumb2Cbz, r1, 0);
buzbee67bf8852011-08-17 17:51:35 -0700986
987 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
988 hopTarget->defMask = ENCODE_ALL;
989 hopBranch->generic.target = (LIR*)hopTarget;
990
buzbee1b4c8592011-08-31 10:43:51 -0700991 // Go expensive route - artLockObjectFromCode(self, obj);
992 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700993 rLR);
Ian Rogersff1ed472011-09-20 13:46:24 -0700994 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700995
996 // Resume here
997 target = newLIR0(cUnit, kArmPseudoTargetLabel);
998 target->defMask = ENCODE_ALL;
999 branch->generic.target = (LIR*)target;
1000}
1001
1002/*
1003 * For monitor unlock, we don't have to use ldrex/strex. Once
1004 * we've determined that the lock is thin and that we own it with
1005 * a zero recursion count, it's safe to punch it back to the
1006 * initial, unlock thin state with a store word.
1007 */
buzbeeed3e9302011-09-23 17:34:19 -07001008STATIC void genMonitorExit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001009 RegLocation rlSrc)
1010{
1011 ArmLIR* target;
1012 ArmLIR* branch;
1013 ArmLIR* hopTarget;
1014 ArmLIR* hopBranch;
1015
Elliott Hughes5f791332011-09-15 17:45:30 -07001016 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -07001017 oatFlushAllRegs(cUnit);
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001018 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
buzbee2e748f32011-08-29 21:02:19 -07001019 oatLockCallTemps(cUnit); // Prepare for explicit register usage
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001020 genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
1021 loadWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r1); // Get lock
1022 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
buzbee67bf8852011-08-17 17:51:35 -07001023 // Is lock unheld on lock or held by us (==threadId) on unlock?
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001024 opRegRegImm(cUnit, kOpAnd, r3, r1, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
buzbeec143c552011-08-20 17:38:58 -07001025 // Align owner
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001026 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
1027 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
1028 opRegReg(cUnit, kOpSub, r1, r2);
buzbee67bf8852011-08-17 17:51:35 -07001029 hopBranch = opCondBranch(cUnit, kArmCondNe);
1030 oatGenMemBarrier(cUnit, kSY);
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001031 storeWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -07001032 branch = opNone(cUnit, kOpUncondBr);
1033
1034 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
1035 hopTarget->defMask = ENCODE_ALL;
1036 hopBranch->generic.target = (LIR*)hopTarget;
1037
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001038 // Go expensive route - UnlockObjectFromCode(obj);
buzbee1b4c8592011-08-31 10:43:51 -07001039 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -07001040 rLR);
Ian Rogersff1ed472011-09-20 13:46:24 -07001041 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001042
1043 // Resume here
1044 target = newLIR0(cUnit, kArmPseudoTargetLabel);
1045 target->defMask = ENCODE_ALL;
1046 branch->generic.target = (LIR*)target;
1047}
1048
1049/*
1050 * 64-bit 3way compare function.
1051 * mov rX, #-1
1052 * cmp op1hi, op2hi
1053 * blt done
1054 * bgt flip
1055 * sub rX, op1lo, op2lo (treat as unsigned)
1056 * beq done
1057 * ite hi
1058 * mov(hi) rX, #-1
1059 * mov(!hi) rX, #1
1060 * flip:
1061 * neg rX
1062 * done:
1063 */
buzbeeed3e9302011-09-23 17:34:19 -07001064STATIC void genCmpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001065 RegLocation rlDest, RegLocation rlSrc1,
1066 RegLocation rlSrc2)
1067{
buzbee67bf8852011-08-17 17:51:35 -07001068 ArmLIR* target1;
1069 ArmLIR* target2;
1070 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
1071 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
buzbeeb29e4d12011-09-26 15:05:48 -07001072 int tReg = oatAllocTemp(cUnit);
1073 loadConstant(cUnit, tReg, -1);
buzbee67bf8852011-08-17 17:51:35 -07001074 opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
1075 ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt);
1076 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt);
buzbeeb29e4d12011-09-26 15:05:48 -07001077 opRegRegReg(cUnit, kOpSub, tReg, rlSrc1.lowReg, rlSrc2.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001078 ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq);
1079
1080 genIT(cUnit, kArmCondHi, "E");
buzbeeb29e4d12011-09-26 15:05:48 -07001081 newLIR2(cUnit, kThumb2MovImmShift, tReg, modifiedImmediate(-1));
1082 loadConstant(cUnit, tReg, 1);
buzbee67bf8852011-08-17 17:51:35 -07001083 genBarrier(cUnit);
1084
1085 target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
1086 target2->defMask = -1;
buzbeeb29e4d12011-09-26 15:05:48 -07001087 opRegReg(cUnit, kOpNeg, tReg, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001088
1089 target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
1090 target1->defMask = -1;
1091
buzbeeb29e4d12011-09-26 15:05:48 -07001092 RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
1093 rlTemp.lowReg = tReg;
buzbee67bf8852011-08-17 17:51:35 -07001094 storeValue(cUnit, rlDest, rlTemp);
buzbeeb29e4d12011-09-26 15:05:48 -07001095 oatFreeTemp(cUnit, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001096
1097 branch1->generic.target = (LIR*)target1;
1098 branch2->generic.target = (LIR*)target2;
1099 branch3->generic.target = branch1->generic.target;
1100}
1101
buzbeeed3e9302011-09-23 17:34:19 -07001102STATIC void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001103 RegLocation rlSrc, RegLocation rlResult, int lit,
1104 int firstBit, int secondBit)
1105{
1106 opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
1107 encodeShift(kArmLsl, secondBit - firstBit));
1108 if (firstBit != 0) {
1109 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
1110 }
1111}
1112
buzbeeed3e9302011-09-23 17:34:19 -07001113STATIC bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset,
buzbee67bf8852011-08-17 17:51:35 -07001114 int srcSize, int tgtSize)
1115{
1116 /*
1117 * Don't optimize the register usage since it calls out to support
1118 * functions
1119 */
1120 RegLocation rlSrc;
1121 RegLocation rlDest;
1122 oatFlushAllRegs(cUnit); /* Send everything to home location */
1123 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1124 if (srcSize == 1) {
1125 rlSrc = oatGetSrc(cUnit, mir, 0);
1126 loadValueDirectFixed(cUnit, rlSrc, r0);
1127 } else {
1128 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
1129 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
1130 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001131 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001132 if (tgtSize == 1) {
1133 RegLocation rlResult;
1134 rlDest = oatGetDest(cUnit, mir, 0);
1135 rlResult = oatGetReturn(cUnit);
1136 storeValue(cUnit, rlDest, rlResult);
1137 } else {
1138 RegLocation rlResult;
1139 rlDest = oatGetDestWide(cUnit, mir, 0, 1);
1140 rlResult = oatGetReturnWide(cUnit);
1141 storeValueWide(cUnit, rlDest, rlResult);
1142 }
1143 return false;
1144}
1145
buzbeeed3e9302011-09-23 17:34:19 -07001146STATIC bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001147 RegLocation rlDest, RegLocation rlSrc1,
1148 RegLocation rlSrc2)
1149{
1150 RegLocation rlResult;
1151 int funcOffset;
1152
1153 switch (mir->dalvikInsn.opcode) {
1154 case OP_ADD_FLOAT_2ADDR:
1155 case OP_ADD_FLOAT:
1156 funcOffset = OFFSETOF_MEMBER(Thread, pFadd);
1157 break;
1158 case OP_SUB_FLOAT_2ADDR:
1159 case OP_SUB_FLOAT:
1160 funcOffset = OFFSETOF_MEMBER(Thread, pFsub);
1161 break;
1162 case OP_DIV_FLOAT_2ADDR:
1163 case OP_DIV_FLOAT:
1164 funcOffset = OFFSETOF_MEMBER(Thread, pFdiv);
1165 break;
1166 case OP_MUL_FLOAT_2ADDR:
1167 case OP_MUL_FLOAT:
1168 funcOffset = OFFSETOF_MEMBER(Thread, pFmul);
1169 break;
1170 case OP_REM_FLOAT_2ADDR:
1171 case OP_REM_FLOAT:
1172 funcOffset = OFFSETOF_MEMBER(Thread, pFmodf);
1173 break;
1174 case OP_NEG_FLOAT: {
1175 genNegFloat(cUnit, rlDest, rlSrc1);
1176 return false;
1177 }
1178 default:
1179 return true;
1180 }
1181 oatFlushAllRegs(cUnit); /* Send everything to home location */
1182 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1183 loadValueDirectFixed(cUnit, rlSrc1, r0);
1184 loadValueDirectFixed(cUnit, rlSrc2, r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001185 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001186 rlResult = oatGetReturn(cUnit);
1187 storeValue(cUnit, rlDest, rlResult);
1188 return false;
1189}
1190
buzbeeed3e9302011-09-23 17:34:19 -07001191STATIC bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001192 RegLocation rlDest, RegLocation rlSrc1,
1193 RegLocation rlSrc2)
1194{
1195 RegLocation rlResult;
1196 int funcOffset;
1197
1198 switch (mir->dalvikInsn.opcode) {
1199 case OP_ADD_DOUBLE_2ADDR:
1200 case OP_ADD_DOUBLE:
1201 funcOffset = OFFSETOF_MEMBER(Thread, pDadd);
1202 break;
1203 case OP_SUB_DOUBLE_2ADDR:
1204 case OP_SUB_DOUBLE:
1205 funcOffset = OFFSETOF_MEMBER(Thread, pDsub);
1206 break;
1207 case OP_DIV_DOUBLE_2ADDR:
1208 case OP_DIV_DOUBLE:
1209 funcOffset = OFFSETOF_MEMBER(Thread, pDdiv);
1210 break;
1211 case OP_MUL_DOUBLE_2ADDR:
1212 case OP_MUL_DOUBLE:
1213 funcOffset = OFFSETOF_MEMBER(Thread, pDmul);
1214 break;
1215 case OP_REM_DOUBLE_2ADDR:
1216 case OP_REM_DOUBLE:
1217 funcOffset = OFFSETOF_MEMBER(Thread, pFmod);
1218 break;
1219 case OP_NEG_DOUBLE: {
1220 genNegDouble(cUnit, rlDest, rlSrc1);
1221 return false;
1222 }
1223 default:
1224 return true;
1225 }
1226 oatFlushAllRegs(cUnit); /* Send everything to home location */
1227 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1228 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1229 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
Ian Rogersff1ed472011-09-20 13:46:24 -07001230 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001231 rlResult = oatGetReturnWide(cUnit);
1232 storeValueWide(cUnit, rlDest, rlResult);
1233 return false;
1234}
1235
buzbeeed3e9302011-09-23 17:34:19 -07001236STATIC bool genConversionPortable(CompilationUnit* cUnit, MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -07001237{
1238 Opcode opcode = mir->dalvikInsn.opcode;
1239
1240 switch (opcode) {
1241 case OP_INT_TO_FLOAT:
1242 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f),
1243 1, 1);
1244 case OP_FLOAT_TO_INT:
1245 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz),
1246 1, 1);
1247 case OP_DOUBLE_TO_FLOAT:
1248 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f),
1249 2, 1);
1250 case OP_FLOAT_TO_DOUBLE:
1251 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d),
1252 1, 2);
1253 case OP_INT_TO_DOUBLE:
1254 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d),
1255 1, 2);
1256 case OP_DOUBLE_TO_INT:
1257 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz),
1258 2, 1);
1259 case OP_FLOAT_TO_LONG:
1260 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001261 pF2l), 1, 2);
buzbee67bf8852011-08-17 17:51:35 -07001262 case OP_LONG_TO_FLOAT:
1263 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f),
1264 2, 1);
1265 case OP_DOUBLE_TO_LONG:
1266 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001267 pD2l), 2, 2);
buzbee67bf8852011-08-17 17:51:35 -07001268 case OP_LONG_TO_DOUBLE:
1269 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d),
1270 2, 2);
1271 default:
1272 return true;
1273 }
1274 return false;
1275}
1276
1277/* Generate conditional branch instructions */
buzbeeed3e9302011-09-23 17:34:19 -07001278STATIC ArmLIR* genConditionalBranch(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001279 ArmConditionCode cond,
1280 ArmLIR* target)
1281{
1282 ArmLIR* branch = opCondBranch(cUnit, cond);
1283 branch->generic.target = (LIR*) target;
1284 return branch;
1285}
1286
buzbee67bf8852011-08-17 17:51:35 -07001287/*
1288 * Generate array store
1289 *
1290 */
buzbeeed3e9302011-09-23 17:34:19 -07001291STATIC void genArrayObjPut(CompilationUnit* cUnit, MIR* mir,
buzbee1b4c8592011-08-31 10:43:51 -07001292 RegLocation rlArray, RegLocation rlIndex,
1293 RegLocation rlSrc, int scale)
buzbee67bf8852011-08-17 17:51:35 -07001294{
1295 RegisterClass regClass = oatRegClassBySize(kWord);
buzbeec143c552011-08-20 17:38:58 -07001296 int lenOffset = Array::LengthOffset().Int32Value();
1297 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001298
buzbee6181f792011-09-29 11:14:04 -07001299 oatFlushAllRegs(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001300 /* Make sure it's a legal object Put. Use direct regs at first */
1301 loadValueDirectFixed(cUnit, rlArray, r1);
1302 loadValueDirectFixed(cUnit, rlSrc, r0);
1303
1304 /* null array object? */
buzbee43a36422011-09-14 14:00:13 -07001305 genNullCheck(cUnit, rlArray.sRegLow, r1, mir);
buzbee67bf8852011-08-17 17:51:35 -07001306 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -07001307 OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -07001308 /* Get the array's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001309 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001310 callRuntimeHelper(cUnit, rLR);
buzbee6181f792011-09-29 11:14:04 -07001311 oatFreeTemp(cUnit, r0);
1312 oatFreeTemp(cUnit, r1);
buzbee67bf8852011-08-17 17:51:35 -07001313
1314 // Now, redo loadValues in case they didn't survive the call
1315
1316 int regPtr;
1317 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1318 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1319
1320 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1321 oatClobber(cUnit, rlArray.lowReg);
1322 regPtr = rlArray.lowReg;
1323 } else {
1324 regPtr = oatAllocTemp(cUnit);
1325 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1326 }
1327
buzbee43a36422011-09-14 14:00:13 -07001328 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001329 int regLen = oatAllocTemp(cUnit);
1330 //NOTE: max live temps(4) here.
1331 /* Get len */
1332 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1333 /* regPtr -> array data */
1334 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001335 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001336 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001337 oatFreeTemp(cUnit, regLen);
1338 } else {
1339 /* regPtr -> array data */
1340 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1341 }
1342 /* at this point, regPtr points to array, 2 live temps */
1343 rlSrc = loadValue(cUnit, rlSrc, regClass);
1344 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1345 scale, kWord);
1346}
1347
1348/*
1349 * Generate array load
1350 */
buzbeeed3e9302011-09-23 17:34:19 -07001351STATIC void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001352 RegLocation rlArray, RegLocation rlIndex,
1353 RegLocation rlDest, int scale)
1354{
1355 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001356 int lenOffset = Array::LengthOffset().Int32Value();
1357 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001358 RegLocation rlResult;
1359 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1360 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1361 int regPtr;
1362
1363 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001364 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001365
1366 regPtr = oatAllocTemp(cUnit);
1367
buzbee43a36422011-09-14 14:00:13 -07001368 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001369 int regLen = oatAllocTemp(cUnit);
1370 /* Get len */
1371 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1372 /* regPtr -> array data */
1373 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001374 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001375 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001376 oatFreeTemp(cUnit, regLen);
1377 } else {
1378 /* regPtr -> array data */
1379 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
1380 }
buzbeee9a72f62011-09-04 17:59:07 -07001381 oatFreeTemp(cUnit, rlArray.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001382 if ((size == kLong) || (size == kDouble)) {
1383 if (scale) {
1384 int rNewIndex = oatAllocTemp(cUnit);
1385 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1386 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1387 oatFreeTemp(cUnit, rNewIndex);
1388 } else {
1389 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1390 }
buzbeee9a72f62011-09-04 17:59:07 -07001391 oatFreeTemp(cUnit, rlIndex.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001392 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1393
1394 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
1395
1396 oatFreeTemp(cUnit, regPtr);
1397 storeValueWide(cUnit, rlDest, rlResult);
1398 } else {
1399 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1400
1401 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
1402 scale, size);
1403
1404 oatFreeTemp(cUnit, regPtr);
1405 storeValue(cUnit, rlDest, rlResult);
1406 }
1407}
1408
1409/*
1410 * Generate array store
1411 *
1412 */
buzbeeed3e9302011-09-23 17:34:19 -07001413STATIC void genArrayPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001414 RegLocation rlArray, RegLocation rlIndex,
1415 RegLocation rlSrc, int scale)
1416{
1417 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001418 int lenOffset = Array::LengthOffset().Int32Value();
1419 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001420
1421 int regPtr;
1422 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1423 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1424
1425 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1426 oatClobber(cUnit, rlArray.lowReg);
1427 regPtr = rlArray.lowReg;
1428 } else {
1429 regPtr = oatAllocTemp(cUnit);
1430 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1431 }
1432
1433 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001434 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001435
buzbee43a36422011-09-14 14:00:13 -07001436 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001437 int regLen = oatAllocTemp(cUnit);
1438 //NOTE: max live temps(4) here.
1439 /* Get len */
1440 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1441 /* regPtr -> array data */
1442 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001443 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001444 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001445 oatFreeTemp(cUnit, regLen);
1446 } else {
1447 /* regPtr -> array data */
1448 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1449 }
1450 /* at this point, regPtr points to array, 2 live temps */
1451 if ((size == kLong) || (size == kDouble)) {
buzbee5ade1d22011-09-09 14:44:52 -07001452 //TUNING: specific wide routine that can handle fp regs
buzbee67bf8852011-08-17 17:51:35 -07001453 if (scale) {
1454 int rNewIndex = oatAllocTemp(cUnit);
1455 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1456 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1457 oatFreeTemp(cUnit, rNewIndex);
1458 } else {
1459 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1460 }
1461 rlSrc = loadValueWide(cUnit, rlSrc, regClass);
1462
1463 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
1464
1465 oatFreeTemp(cUnit, regPtr);
1466 } else {
1467 rlSrc = loadValue(cUnit, rlSrc, regClass);
1468
1469 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1470 scale, size);
1471 }
1472}
1473
buzbeeed3e9302011-09-23 17:34:19 -07001474STATIC bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001475 RegLocation rlDest, RegLocation rlSrc1,
1476 RegLocation rlShift)
1477{
buzbee54330722011-08-23 16:46:55 -07001478 int funcOffset;
buzbee67bf8852011-08-17 17:51:35 -07001479
buzbee67bf8852011-08-17 17:51:35 -07001480 switch( mir->dalvikInsn.opcode) {
1481 case OP_SHL_LONG:
1482 case OP_SHL_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001483 funcOffset = OFFSETOF_MEMBER(Thread, pShlLong);
buzbee67bf8852011-08-17 17:51:35 -07001484 break;
1485 case OP_SHR_LONG:
1486 case OP_SHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001487 funcOffset = OFFSETOF_MEMBER(Thread, pShrLong);
buzbee67bf8852011-08-17 17:51:35 -07001488 break;
1489 case OP_USHR_LONG:
1490 case OP_USHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001491 funcOffset = OFFSETOF_MEMBER(Thread, pUshrLong);
buzbee67bf8852011-08-17 17:51:35 -07001492 break;
1493 default:
buzbee54330722011-08-23 16:46:55 -07001494 LOG(FATAL) << "Unexpected case";
buzbee67bf8852011-08-17 17:51:35 -07001495 return true;
1496 }
buzbee54330722011-08-23 16:46:55 -07001497 oatFlushAllRegs(cUnit); /* Send everything to home location */
1498 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1499 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1500 loadValueDirect(cUnit, rlShift, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -07001501 callRuntimeHelper(cUnit, rLR);
buzbee54330722011-08-23 16:46:55 -07001502 RegLocation rlResult = oatGetReturnWide(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001503 storeValueWide(cUnit, rlDest, rlResult);
1504 return false;
1505}
1506
buzbeeed3e9302011-09-23 17:34:19 -07001507STATIC bool genArithOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001508 RegLocation rlDest, RegLocation rlSrc1,
1509 RegLocation rlSrc2)
1510{
1511 RegLocation rlResult;
1512 OpKind firstOp = kOpBkpt;
1513 OpKind secondOp = kOpBkpt;
1514 bool callOut = false;
buzbee58f92742011-10-01 11:22:17 -07001515 bool checkZero = false;
buzbee67bf8852011-08-17 17:51:35 -07001516 int funcOffset;
1517 int retReg = r0;
1518
1519 switch (mir->dalvikInsn.opcode) {
1520 case OP_NOT_LONG:
1521 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1522 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001523 // Check for destructive overlap
1524 if (rlResult.lowReg == rlSrc2.highReg) {
1525 int tReg = oatAllocTemp(cUnit);
1526 genRegCopy(cUnit, tReg, rlSrc2.highReg);
1527 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1528 opRegReg(cUnit, kOpMvn, rlResult.highReg, tReg);
1529 oatFreeTemp(cUnit, tReg);
1530 } else {
1531 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1532 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
1533 }
buzbee67bf8852011-08-17 17:51:35 -07001534 storeValueWide(cUnit, rlDest, rlResult);
1535 return false;
1536 break;
1537 case OP_ADD_LONG:
1538 case OP_ADD_LONG_2ADDR:
1539 firstOp = kOpAdd;
1540 secondOp = kOpAdc;
1541 break;
1542 case OP_SUB_LONG:
1543 case OP_SUB_LONG_2ADDR:
1544 firstOp = kOpSub;
1545 secondOp = kOpSbc;
1546 break;
1547 case OP_MUL_LONG:
1548 case OP_MUL_LONG_2ADDR:
buzbee439c4fa2011-08-27 15:59:07 -07001549 callOut = true;
1550 retReg = r0;
1551 funcOffset = OFFSETOF_MEMBER(Thread, pLmul);
1552 break;
buzbee67bf8852011-08-17 17:51:35 -07001553 case OP_DIV_LONG:
1554 case OP_DIV_LONG_2ADDR:
1555 callOut = true;
buzbee58f92742011-10-01 11:22:17 -07001556 checkZero = true;
buzbee67bf8852011-08-17 17:51:35 -07001557 retReg = r0;
1558 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1559 break;
1560 /* NOTE - result is in r2/r3 instead of r0/r1 */
1561 case OP_REM_LONG:
1562 case OP_REM_LONG_2ADDR:
1563 callOut = true;
buzbee58f92742011-10-01 11:22:17 -07001564 checkZero = true;
buzbee67bf8852011-08-17 17:51:35 -07001565 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1566 retReg = r2;
1567 break;
1568 case OP_AND_LONG_2ADDR:
1569 case OP_AND_LONG:
1570 firstOp = kOpAnd;
1571 secondOp = kOpAnd;
1572 break;
1573 case OP_OR_LONG:
1574 case OP_OR_LONG_2ADDR:
1575 firstOp = kOpOr;
1576 secondOp = kOpOr;
1577 break;
1578 case OP_XOR_LONG:
1579 case OP_XOR_LONG_2ADDR:
1580 firstOp = kOpXor;
1581 secondOp = kOpXor;
1582 break;
1583 case OP_NEG_LONG: {
buzbee67bf8852011-08-17 17:51:35 -07001584 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1585 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001586 int zReg = oatAllocTemp(cUnit);
1587 loadConstantNoClobber(cUnit, zReg, 0);
1588 // Check for destructive overlap
1589 if (rlResult.lowReg == rlSrc2.highReg) {
1590 int tReg = oatAllocTemp(cUnit);
1591 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1592 zReg, rlSrc2.lowReg);
1593 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1594 zReg, tReg);
1595 oatFreeTemp(cUnit, tReg);
1596 } else {
1597 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1598 zReg, rlSrc2.lowReg);
1599 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1600 zReg, rlSrc2.highReg);
1601 }
1602 oatFreeTemp(cUnit, zReg);
buzbee67bf8852011-08-17 17:51:35 -07001603 storeValueWide(cUnit, rlDest, rlResult);
1604 return false;
1605 }
1606 default:
1607 LOG(FATAL) << "Invalid long arith op";
1608 }
1609 if (!callOut) {
1610 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
1611 } else {
buzbee67bf8852011-08-17 17:51:35 -07001612 oatFlushAllRegs(cUnit); /* Send everything to home location */
buzbee58f92742011-10-01 11:22:17 -07001613 if (checkZero) {
1614 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1615 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1616 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1617 int tReg = oatAllocTemp(cUnit);
1618 newLIR4(cUnit, kThumb2OrrRRRs, tReg, r2, r3, 0);
1619 oatFreeTemp(cUnit, tReg);
1620 genCheck(cUnit, kArmCondEq, mir, kArmThrowDivZero);
1621 } else {
1622 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1623 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1624 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1625 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001626 callRuntimeHelper(cUnit, rLR);
buzbee58f92742011-10-01 11:22:17 -07001627 // Adjust return regs in to handle case of rem returning r2/r3
buzbee67bf8852011-08-17 17:51:35 -07001628 if (retReg == r0)
1629 rlResult = oatGetReturnWide(cUnit);
1630 else
1631 rlResult = oatGetReturnWideAlt(cUnit);
1632 storeValueWide(cUnit, rlDest, rlResult);
1633 }
1634 return false;
1635}
1636
buzbeeed3e9302011-09-23 17:34:19 -07001637STATIC bool genArithOpInt(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001638 RegLocation rlDest, RegLocation rlSrc1,
1639 RegLocation rlSrc2)
1640{
1641 OpKind op = kOpBkpt;
1642 bool callOut = false;
1643 bool checkZero = false;
1644 bool unary = false;
1645 int retReg = r0;
1646 int funcOffset;
1647 RegLocation rlResult;
1648 bool shiftOp = false;
1649
1650 switch (mir->dalvikInsn.opcode) {
1651 case OP_NEG_INT:
1652 op = kOpNeg;
1653 unary = true;
1654 break;
1655 case OP_NOT_INT:
1656 op = kOpMvn;
1657 unary = true;
1658 break;
1659 case OP_ADD_INT:
1660 case OP_ADD_INT_2ADDR:
1661 op = kOpAdd;
1662 break;
1663 case OP_SUB_INT:
1664 case OP_SUB_INT_2ADDR:
1665 op = kOpSub;
1666 break;
1667 case OP_MUL_INT:
1668 case OP_MUL_INT_2ADDR:
1669 op = kOpMul;
1670 break;
1671 case OP_DIV_INT:
1672 case OP_DIV_INT_2ADDR:
1673 callOut = true;
1674 checkZero = true;
1675 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1676 retReg = r0;
1677 break;
1678 /* NOTE: returns in r1 */
1679 case OP_REM_INT:
1680 case OP_REM_INT_2ADDR:
1681 callOut = true;
1682 checkZero = true;
1683 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1684 retReg = r1;
1685 break;
1686 case OP_AND_INT:
1687 case OP_AND_INT_2ADDR:
1688 op = kOpAnd;
1689 break;
1690 case OP_OR_INT:
1691 case OP_OR_INT_2ADDR:
1692 op = kOpOr;
1693 break;
1694 case OP_XOR_INT:
1695 case OP_XOR_INT_2ADDR:
1696 op = kOpXor;
1697 break;
1698 case OP_SHL_INT:
1699 case OP_SHL_INT_2ADDR:
1700 shiftOp = true;
1701 op = kOpLsl;
1702 break;
1703 case OP_SHR_INT:
1704 case OP_SHR_INT_2ADDR:
1705 shiftOp = true;
1706 op = kOpAsr;
1707 break;
1708 case OP_USHR_INT:
1709 case OP_USHR_INT_2ADDR:
1710 shiftOp = true;
1711 op = kOpLsr;
1712 break;
1713 default:
1714 LOG(FATAL) << "Invalid word arith op: " <<
1715 (int)mir->dalvikInsn.opcode;
1716 }
1717 if (!callOut) {
1718 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
1719 if (unary) {
1720 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1721 opRegReg(cUnit, op, rlResult.lowReg,
1722 rlSrc1.lowReg);
1723 } else {
1724 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
1725 if (shiftOp) {
1726 int tReg = oatAllocTemp(cUnit);
1727 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
1728 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1729 opRegRegReg(cUnit, op, rlResult.lowReg,
1730 rlSrc1.lowReg, tReg);
1731 oatFreeTemp(cUnit, tReg);
1732 } else {
1733 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1734 opRegRegReg(cUnit, op, rlResult.lowReg,
1735 rlSrc1.lowReg, rlSrc2.lowReg);
1736 }
1737 }
1738 storeValue(cUnit, rlDest, rlResult);
1739 } else {
1740 RegLocation rlResult;
1741 oatFlushAllRegs(cUnit); /* Send everything to home location */
1742 loadValueDirectFixed(cUnit, rlSrc2, r1);
1743 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1744 loadValueDirectFixed(cUnit, rlSrc1, r0);
1745 if (checkZero) {
buzbee5ade1d22011-09-09 14:44:52 -07001746 genImmedCheck(cUnit, kArmCondEq, r1, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001747 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001748 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001749 if (retReg == r0)
1750 rlResult = oatGetReturn(cUnit);
1751 else
1752 rlResult = oatGetReturnAlt(cUnit);
1753 storeValue(cUnit, rlDest, rlResult);
1754 }
1755 return false;
1756}
1757
buzbeec1f45042011-09-21 16:03:19 -07001758/* Check if we need to check for pending suspend request */
buzbeeed3e9302011-09-23 17:34:19 -07001759STATIC void genSuspendTest(CompilationUnit* cUnit, MIR* mir)
buzbeec1f45042011-09-21 16:03:19 -07001760{
Ian Rogersa3760aa2011-11-14 14:32:37 -08001761 if (NO_SUSPEND || (mir->optimizationFlags & MIR_IGNORE_SUSPEND_CHECK)) {
buzbeec1f45042011-09-21 16:03:19 -07001762 return;
1763 }
buzbee6181f792011-09-29 11:14:04 -07001764 oatFlushAllRegs(cUnit);
buzbee44b412b2012-02-04 08:50:53 -08001765 ArmLIR* branch;
1766 if (cUnit->genDebugger) {
1767 // If generating code for the debugger, always check for suspension
1768 branch = genUnconditionalBranch(cUnit, NULL);
1769 } else {
1770 // In non-debug case, only check periodically
1771 newLIR2(cUnit, kThumbSubRI8, rSUSPEND, 1);
1772 branch = opCondBranch(cUnit, kArmCondEq);
1773 }
buzbeec1f45042011-09-21 16:03:19 -07001774 ArmLIR* retLab = newLIR0(cUnit, kArmPseudoTargetLabel);
1775 retLab->defMask = ENCODE_ALL;
buzbeeba938cb2012-02-03 14:47:55 -08001776 ArmLIR* target = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbeec1f45042011-09-21 16:03:19 -07001777 target->generic.dalvikOffset = cUnit->currentDalvikOffset;
1778 target->opcode = kArmPseudoSuspendTarget;
1779 target->operands[0] = (intptr_t)retLab;
1780 target->operands[1] = mir->offset;
1781 branch->generic.target = (LIR*)target;
buzbeeba938cb2012-02-03 14:47:55 -08001782 oatInsertGrowableList(cUnit, &cUnit->suspendLaunchpads, (intptr_t)target);
buzbeec1f45042011-09-21 16:03:19 -07001783}
1784
buzbee67bf8852011-08-17 17:51:35 -07001785/*
1786 * The following are the first-level codegen routines that analyze the format
1787 * of each bytecode then either dispatch special purpose codegen routines
1788 * or produce corresponding Thumb instructions directly.
1789 */
1790
buzbeeed3e9302011-09-23 17:34:19 -07001791STATIC bool isPowerOfTwo(int x)
buzbee67bf8852011-08-17 17:51:35 -07001792{
1793 return (x & (x - 1)) == 0;
1794}
1795
1796// Returns true if no more than two bits are set in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001797STATIC bool isPopCountLE2(unsigned int x)
buzbee67bf8852011-08-17 17:51:35 -07001798{
1799 x &= x - 1;
1800 return (x & (x - 1)) == 0;
1801}
1802
1803// Returns the index of the lowest set bit in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001804STATIC int lowestSetBit(unsigned int x) {
buzbee67bf8852011-08-17 17:51:35 -07001805 int bit_posn = 0;
1806 while ((x & 0xf) == 0) {
1807 bit_posn += 4;
1808 x >>= 4;
1809 }
1810 while ((x & 1) == 0) {
1811 bit_posn++;
1812 x >>= 1;
1813 }
1814 return bit_posn;
1815}
1816
1817// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
1818// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001819STATIC bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode,
buzbee67bf8852011-08-17 17:51:35 -07001820 RegLocation rlSrc, RegLocation rlDest, int lit)
1821{
1822 if (lit < 2 || !isPowerOfTwo(lit)) {
1823 return false;
1824 }
1825 int k = lowestSetBit(lit);
1826 if (k >= 30) {
1827 // Avoid special cases.
1828 return false;
1829 }
1830 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 ||
1831 dalvikOpcode == OP_DIV_INT_LIT16);
1832 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1833 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1834 if (div) {
1835 int tReg = oatAllocTemp(cUnit);
1836 if (lit == 2) {
1837 // Division by 2 is by far the most common division by constant.
1838 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
1839 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1840 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1841 } else {
1842 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
1843 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
1844 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1845 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1846 }
1847 } else {
1848 int cReg = oatAllocTemp(cUnit);
1849 loadConstant(cUnit, cReg, lit - 1);
1850 int tReg1 = oatAllocTemp(cUnit);
1851 int tReg2 = oatAllocTemp(cUnit);
1852 if (lit == 2) {
1853 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
1854 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1855 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1856 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1857 } else {
1858 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
1859 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
1860 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1861 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1862 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1863 }
1864 }
1865 storeValue(cUnit, rlDest, rlResult);
1866 return true;
1867}
1868
1869// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
1870// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001871STATIC bool handleEasyMultiply(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001872 RegLocation rlSrc, RegLocation rlDest, int lit)
1873{
1874 // Can we simplify this multiplication?
1875 bool powerOfTwo = false;
1876 bool popCountLE2 = false;
1877 bool powerOfTwoMinusOne = false;
1878 if (lit < 2) {
1879 // Avoid special cases.
1880 return false;
1881 } else if (isPowerOfTwo(lit)) {
1882 powerOfTwo = true;
1883 } else if (isPopCountLE2(lit)) {
1884 popCountLE2 = true;
1885 } else if (isPowerOfTwo(lit + 1)) {
1886 powerOfTwoMinusOne = true;
1887 } else {
1888 return false;
1889 }
1890 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1891 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1892 if (powerOfTwo) {
1893 // Shift.
1894 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
1895 lowestSetBit(lit));
1896 } else if (popCountLE2) {
1897 // Shift and add and shift.
1898 int firstBit = lowestSetBit(lit);
1899 int secondBit = lowestSetBit(lit ^ (1 << firstBit));
1900 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
1901 firstBit, secondBit);
1902 } else {
1903 // Reverse subtract: (src << (shift + 1)) - src.
buzbeeed3e9302011-09-23 17:34:19 -07001904 DCHECK(powerOfTwoMinusOne);
buzbee5ade1d22011-09-09 14:44:52 -07001905 // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1)
buzbee67bf8852011-08-17 17:51:35 -07001906 int tReg = oatAllocTemp(cUnit);
1907 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
1908 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
1909 }
1910 storeValue(cUnit, rlDest, rlResult);
1911 return true;
1912}
1913
buzbeeed3e9302011-09-23 17:34:19 -07001914STATIC bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001915 RegLocation rlDest, RegLocation rlSrc,
1916 int lit)
1917{
1918 Opcode dalvikOpcode = mir->dalvikInsn.opcode;
1919 RegLocation rlResult;
1920 OpKind op = (OpKind)0; /* Make gcc happy */
1921 int shiftOp = false;
1922 bool isDiv = false;
1923 int funcOffset;
1924
1925 switch (dalvikOpcode) {
1926 case OP_RSUB_INT_LIT8:
1927 case OP_RSUB_INT: {
1928 int tReg;
1929 //TUNING: add support for use of Arm rsub op
1930 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1931 tReg = oatAllocTemp(cUnit);
1932 loadConstant(cUnit, tReg, lit);
1933 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1934 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1935 tReg, rlSrc.lowReg);
1936 storeValue(cUnit, rlDest, rlResult);
1937 return false;
1938 break;
1939 }
1940
1941 case OP_ADD_INT_LIT8:
1942 case OP_ADD_INT_LIT16:
1943 op = kOpAdd;
1944 break;
1945 case OP_MUL_INT_LIT8:
1946 case OP_MUL_INT_LIT16: {
1947 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
1948 return false;
1949 }
1950 op = kOpMul;
1951 break;
1952 }
1953 case OP_AND_INT_LIT8:
1954 case OP_AND_INT_LIT16:
1955 op = kOpAnd;
1956 break;
1957 case OP_OR_INT_LIT8:
1958 case OP_OR_INT_LIT16:
1959 op = kOpOr;
1960 break;
1961 case OP_XOR_INT_LIT8:
1962 case OP_XOR_INT_LIT16:
1963 op = kOpXor;
1964 break;
1965 case OP_SHL_INT_LIT8:
1966 lit &= 31;
1967 shiftOp = true;
1968 op = kOpLsl;
1969 break;
1970 case OP_SHR_INT_LIT8:
1971 lit &= 31;
1972 shiftOp = true;
1973 op = kOpAsr;
1974 break;
1975 case OP_USHR_INT_LIT8:
1976 lit &= 31;
1977 shiftOp = true;
1978 op = kOpLsr;
1979 break;
1980
1981 case OP_DIV_INT_LIT8:
1982 case OP_DIV_INT_LIT16:
1983 case OP_REM_INT_LIT8:
1984 case OP_REM_INT_LIT16:
1985 if (lit == 0) {
buzbee5ade1d22011-09-09 14:44:52 -07001986 genImmedCheck(cUnit, kArmCondAl, 0, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001987 return false;
1988 }
1989 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
1990 return false;
1991 }
1992 oatFlushAllRegs(cUnit); /* Everything to home location */
1993 loadValueDirectFixed(cUnit, rlSrc, r0);
1994 oatClobber(cUnit, r0);
1995 if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
1996 (dalvikOpcode == OP_DIV_INT_LIT16)) {
1997 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1998 isDiv = true;
1999 } else {
2000 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
2001 isDiv = false;
2002 }
2003 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
2004 loadConstant(cUnit, r1, lit);
Ian Rogersff1ed472011-09-20 13:46:24 -07002005 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07002006 if (isDiv)
2007 rlResult = oatGetReturn(cUnit);
2008 else
2009 rlResult = oatGetReturnAlt(cUnit);
2010 storeValue(cUnit, rlDest, rlResult);
2011 return false;
2012 break;
2013 default:
2014 return true;
2015 }
2016 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
2017 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
2018 // Avoid shifts by literal 0 - no support in Thumb. Change to copy
2019 if (shiftOp && (lit == 0)) {
2020 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
2021 } else {
2022 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
2023 }
2024 storeValue(cUnit, rlDest, rlResult);
2025 return false;
2026}
2027
2028/* Architectural-specific debugging helpers go here */
2029void oatArchDump(void)
2030{
2031 /* Print compiled opcode in this VM instance */
2032 int i, start, streak;
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002033 std::string buf;
buzbee67bf8852011-08-17 17:51:35 -07002034
2035 streak = i = 0;
buzbee67bf8852011-08-17 17:51:35 -07002036 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2037 i++;
2038 }
2039 if (i == kNumPackedOpcodes) {
2040 return;
2041 }
2042 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
2043 if (opcodeCoverage[i]) {
2044 streak++;
2045 } else {
2046 if (streak == 1) {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002047 StringAppendF(&buf, "%x,", start);
buzbee67bf8852011-08-17 17:51:35 -07002048 } else {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002049 StringAppendF(&buf, "%x-%x,", start, start + streak - 1);
buzbee67bf8852011-08-17 17:51:35 -07002050 }
2051 streak = 0;
2052 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2053 i++;
2054 }
2055 if (i < kNumPackedOpcodes) {
2056 streak = 1;
2057 start = i;
2058 }
2059 }
2060 }
2061 if (streak) {
2062 if (streak == 1) {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002063 StringAppendF(&buf, "%x", start);
buzbee67bf8852011-08-17 17:51:35 -07002064 } else {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002065 StringAppendF(&buf, "%x-%x", start, start + streak - 1);
buzbee67bf8852011-08-17 17:51:35 -07002066 }
2067 }
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002068 if (!buf.empty()) {
buzbee67bf8852011-08-17 17:51:35 -07002069 LOG(INFO) << "dalvik.vm.oat.op = " << buf;
2070 }
2071}
Elliott Hughes11d1b0c2012-01-23 16:57:47 -08002072
2073} // namespace art