blob: e20e19ba5bd7f930dabd164a4a787fb1ec7ce3c6 [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
Logan Chien4dd96f52012-02-29 01:26:58 +080025#include "oat_compilation_unit.h"
26
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080027namespace art {
28
buzbeee3acd072012-02-25 17:03:10 -080029/*
30 * Return most flexible allowed register class based on size.
31 * Bug: 2813841
32 * Must use a core register for data types narrower than word (due
33 * to possible unaligned load/store.
34 */
35STATIC inline RegisterClass oatRegClassBySize(OpSize size)
36{
37 return (size == kUnsignedHalf ||
38 size == kSignedHalf ||
39 size == kUnsignedByte ||
40 size == kSignedByte ) ? kCoreReg : kAnyReg;
41}
42
buzbeece302932011-10-04 14:32:18 -070043STATIC RegLocation getRetLoc(CompilationUnit* cUnit);
buzbee34cd9e52011-09-08 14:31:52 -070044
Elliott Hughes81bc5092011-09-30 17:25:59 -070045void warnIfUnresolved(CompilationUnit* cUnit, int fieldIdx, Field* field) {
46 if (field == NULL) {
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080047 const DexFile::FieldId& field_id = cUnit->dex_file->GetFieldId(fieldIdx);
Elliott Hughes95572412011-12-13 18:14:20 -080048 std::string class_name(cUnit->dex_file->GetFieldDeclaringClassDescriptor(field_id));
49 std::string field_name(cUnit->dex_file->GetFieldName(field_id));
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080050 LOG(INFO) << "Field " << PrettyDescriptor(class_name) << "." << field_name
Elliott Hughes95572412011-12-13 18:14:20 -080051 << " unresolved at compile time";
Elliott Hughes81bc5092011-09-30 17:25:59 -070052 } else {
53 // We also use the slow path for wide volatile fields.
54 }
55}
56
buzbee67bf8852011-08-17 17:51:35 -070057/*
58 * Construct an s4 from two consecutive half-words of switch data.
59 * This needs to check endianness because the DEX optimizer only swaps
60 * half-words in instruction stream.
61 *
62 * "switchData" must be 32-bit aligned.
63 */
64#if __BYTE_ORDER == __LITTLE_ENDIAN
buzbeeed3e9302011-09-23 17:34:19 -070065STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070066 return *(s4*) switchData;
67}
68#else
buzbeeed3e9302011-09-23 17:34:19 -070069STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070070 u2* data = switchData;
71 return data[0] | (((s4) data[1]) << 16);
72}
73#endif
74
75/*
76 * Generate a Thumb2 IT instruction, which can nullify up to
77 * four subsequent instructions based on a condition and its
78 * inverse. The condition applies to the first instruction, which
79 * is executed if the condition is met. The string "guide" consists
80 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
81 * A "T" means the instruction is executed if the condition is
82 * met, and an "E" means the instruction is executed if the condition
83 * is not met.
84 */
buzbeeed3e9302011-09-23 17:34:19 -070085STATIC ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code,
buzbee67bf8852011-08-17 17:51:35 -070086 const char* guide)
87{
88 int mask;
89 int condBit = code & 1;
90 int altBit = condBit ^ 1;
91 int mask3 = 0;
92 int mask2 = 0;
93 int mask1 = 0;
94
95 //Note: case fallthroughs intentional
96 switch(strlen(guide)) {
97 case 3:
98 mask1 = (guide[2] == 'T') ? condBit : altBit;
99 case 2:
100 mask2 = (guide[1] == 'T') ? condBit : altBit;
101 case 1:
102 mask3 = (guide[0] == 'T') ? condBit : altBit;
103 break;
104 case 0:
105 break;
106 default:
107 LOG(FATAL) << "OAT: bad case in genIT";
108 }
109 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
110 (1 << (3 - strlen(guide)));
111 return newLIR2(cUnit, kThumb2It, code, mask);
112}
113
114/*
115 * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik
116 * offset vaddr. This label will be used to fix up the case
117 * branch table during the assembly phase. Be sure to set
118 * all resource flags on this to prevent code motion across
119 * target boundaries. KeyVal is just there for debugging.
120 */
buzbeeed3e9302011-09-23 17:34:19 -0700121STATIC ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
buzbee67bf8852011-08-17 17:51:35 -0700122{
buzbee85d8c1e2012-01-27 15:52:35 -0800123 std::map<unsigned int, LIR*>::iterator it;
124 it = cUnit->boundaryMap.find(vaddr);
125 if (it == cUnit->boundaryMap.end()) {
126 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
buzbee67bf8852011-08-17 17:51:35 -0700127 }
buzbeeba938cb2012-02-03 14:47:55 -0800128 ArmLIR* newLabel = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbee85d8c1e2012-01-27 15:52:35 -0800129 newLabel->generic.dalvikOffset = vaddr;
130 newLabel->opcode = kArmPseudoCaseLabel;
131 newLabel->operands[0] = keyVal;
132 oatInsertLIRAfter(it->second, (LIR*)newLabel);
133 return newLabel;
buzbee67bf8852011-08-17 17:51:35 -0700134}
135
buzbeeed3e9302011-09-23 17:34:19 -0700136STATIC void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700137{
138 const u2* table = tabRec->table;
139 int baseVaddr = tabRec->vaddr;
140 int *targets = (int*)&table[4];
141 int entries = table[1];
142 int lowKey = s4FromSwitchData(&table[2]);
143 for (int i = 0; i < entries; i++) {
144 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
145 i + lowKey);
146 }
147}
148
buzbeeed3e9302011-09-23 17:34:19 -0700149STATIC void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700150{
151 const u2* table = tabRec->table;
152 int baseVaddr = tabRec->vaddr;
153 int entries = table[1];
154 int* keys = (int*)&table[2];
155 int* targets = &keys[entries];
156 for (int i = 0; i < entries; i++) {
157 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
158 keys[i]);
159 }
160}
161
162void oatProcessSwitchTables(CompilationUnit* cUnit)
163{
164 GrowableListIterator iterator;
165 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
166 while (true) {
167 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
168 &iterator);
169 if (tabRec == NULL) break;
170 if (tabRec->table[0] == kPackedSwitchSignature)
171 markPackedCaseLabels(cUnit, tabRec);
172 else if (tabRec->table[0] == kSparseSwitchSignature)
173 markSparseCaseLabels(cUnit, tabRec);
174 else {
175 LOG(FATAL) << "Invalid switch table";
176 }
177 }
178}
179
buzbeeed3e9302011-09-23 17:34:19 -0700180STATIC void dumpSparseSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700181 /*
182 * Sparse switch data format:
183 * ushort ident = 0x0200 magic value
184 * ushort size number of entries in the table; > 0
185 * int keys[size] keys, sorted low-to-high; 32-bit aligned
186 * int targets[size] branch targets, relative to switch opcode
187 *
188 * Total size is (2+size*4) 16-bit code units.
189 */
190{
191 u2 ident = table[0];
192 int entries = table[1];
193 int* keys = (int*)&table[2];
194 int* targets = &keys[entries];
195 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
196 ", entries: " << std::dec << entries;
197 for (int i = 0; i < entries; i++) {
198 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
199 targets[i];
200 }
201}
202
buzbeeed3e9302011-09-23 17:34:19 -0700203STATIC void dumpPackedSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700204 /*
205 * Packed switch data format:
206 * ushort ident = 0x0100 magic value
207 * ushort size number of entries in the table
208 * int first_key first (and lowest) switch case value
209 * int targets[size] branch targets, relative to switch opcode
210 *
211 * Total size is (4+size*2) 16-bit code units.
212 */
213{
214 u2 ident = table[0];
215 int* targets = (int*)&table[4];
216 int entries = table[1];
217 int lowKey = s4FromSwitchData(&table[2]);
218 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
219 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
220 for (int i = 0; i < entries; i++) {
221 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
222 targets[i];
223 }
224}
225
226/*
227 * The sparse table in the literal pool is an array of <key,displacement>
228 * pairs. For each set, we'll load them as a pair using ldmia.
229 * This means that the register number of the temp we use for the key
230 * must be lower than the reg for the displacement.
231 *
232 * The test loop will look something like:
233 *
234 * adr rBase, <table>
235 * ldr rVal, [rSP, vRegOff]
236 * mov rIdx, #tableSize
237 * lp:
238 * ldmia rBase!, {rKey, rDisp}
239 * sub rIdx, #1
240 * cmp rVal, rKey
241 * ifeq
242 * add rPC, rDisp ; This is the branch from which we compute displacement
243 * cbnz rIdx, lp
244 */
buzbeeed3e9302011-09-23 17:34:19 -0700245STATIC void genSparseSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700246 RegLocation rlSrc)
247{
248 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
249 if (cUnit->printMe) {
250 dumpSparseSwitchTable(table);
251 }
252 // Add the table to the list - we'll process it later
buzbeeba938cb2012-02-03 14:47:55 -0800253 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
buzbee5abfa3e2012-01-31 17:01:43 -0800254 true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700255 tabRec->table = table;
256 tabRec->vaddr = mir->offset;
257 int size = table[1];
buzbeeba938cb2012-02-03 14:47:55 -0800258 tabRec->targets = (ArmLIR* *)oatNew(cUnit, size * sizeof(ArmLIR*), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800259 kAllocLIR);
buzbeeba938cb2012-02-03 14:47:55 -0800260 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700261
262 // Get the switch value
263 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
264 int rBase = oatAllocTemp(cUnit);
265 /* Allocate key and disp temps */
266 int rKey = oatAllocTemp(cUnit);
267 int rDisp = oatAllocTemp(cUnit);
268 // Make sure rKey's register number is less than rDisp's number for ldmia
269 if (rKey > rDisp) {
270 int tmp = rDisp;
271 rDisp = rKey;
272 rKey = tmp;
273 }
274 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700275 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700276 // Set up rIdx
277 int rIdx = oatAllocTemp(cUnit);
278 loadConstant(cUnit, rIdx, size);
279 // Establish loop branch target
280 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
281 target->defMask = ENCODE_ALL;
282 // Load next key/disp
283 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
284 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
285 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
286 genIT(cUnit, kArmCondEq, "");
287 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
288 tabRec->bxInst = switchBranch;
289 // Needs to use setflags encoding here
290 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
291 ArmLIR* branch = opCondBranch(cUnit, kArmCondNe);
292 branch->generic.target = (LIR*)target;
293}
294
295
buzbeeed3e9302011-09-23 17:34:19 -0700296STATIC void genPackedSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700297 RegLocation rlSrc)
298{
299 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
300 if (cUnit->printMe) {
301 dumpPackedSwitchTable(table);
302 }
303 // Add the table to the list - we'll process it later
buzbeeba938cb2012-02-03 14:47:55 -0800304 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
buzbee5abfa3e2012-01-31 17:01:43 -0800305 true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700306 tabRec->table = table;
307 tabRec->vaddr = mir->offset;
308 int size = table[1];
buzbeeba938cb2012-02-03 14:47:55 -0800309 tabRec->targets = (ArmLIR* *)oatNew(cUnit, size * sizeof(ArmLIR*), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800310 kAllocLIR);
buzbeeba938cb2012-02-03 14:47:55 -0800311 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700312
313 // Get the switch value
314 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
315 int tableBase = oatAllocTemp(cUnit);
316 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700317 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700318 int lowKey = s4FromSwitchData(&table[2]);
319 int keyReg;
320 // Remove the bias, if necessary
321 if (lowKey == 0) {
322 keyReg = rlSrc.lowReg;
323 } else {
324 keyReg = oatAllocTemp(cUnit);
325 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
326 }
327 // Bounds check - if < 0 or >= size continue following switch
328 opRegImm(cUnit, kOpCmp, keyReg, size-1);
329 ArmLIR* branchOver = opCondBranch(cUnit, kArmCondHi);
330
331 // Load the displacement from the switch table
332 int dispReg = oatAllocTemp(cUnit);
333 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
334
335 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
336 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
337 tabRec->bxInst = switchBranch;
338
339 /* branchOver target here */
340 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
341 target->defMask = ENCODE_ALL;
342 branchOver->generic.target = (LIR*)target;
343}
344
345/*
346 * Array data table format:
347 * ushort ident = 0x0300 magic value
348 * ushort width width of each element in the table
349 * uint size number of elements in the table
350 * ubyte data[size*width] table of data values (may contain a single-byte
351 * padding at the end)
352 *
353 * Total size is 4+(width * size + 1)/2 16-bit code units.
354 */
buzbeeed3e9302011-09-23 17:34:19 -0700355STATIC void genFillArrayData(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700356 RegLocation rlSrc)
357{
358 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
359 // Add the table to the list - we'll process it later
360 FillArrayData *tabRec = (FillArrayData *)
buzbeeba938cb2012-02-03 14:47:55 -0800361 oatNew(cUnit, sizeof(FillArrayData), true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700362 tabRec->table = table;
363 tabRec->vaddr = mir->offset;
364 u2 width = tabRec->table[1];
365 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
366 tabRec->size = (size * width) + 8;
367
buzbeeba938cb2012-02-03 14:47:55 -0800368 oatInsertGrowableList(cUnit, &cUnit->fillArrayData, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700369
370 // Making a call - use explicit registers
371 oatFlushAllRegs(cUnit); /* Everything to home location */
372 loadValueDirectFixed(cUnit, rlSrc, r0);
373 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700374 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
buzbeee6d61962011-08-27 11:58:19 -0700375 // Materialize a pointer to the fill data image
buzbee03fa2632011-09-20 17:10:57 -0700376 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
Ian Rogersff1ed472011-09-20 13:46:24 -0700377 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700378}
379
buzbeeed3e9302011-09-23 17:34:19 -0700380STATIC void genIGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
Ian Rogers1bddec32012-02-04 12:27:34 -0800381 RegLocation rlDest, RegLocation rlObj,
382 bool isLongOrDouble, bool isObject)
buzbee67bf8852011-08-17 17:51:35 -0700383{
Ian Rogers1bddec32012-02-04 12:27:34 -0800384 int fieldOffset;
385 bool isVolatile;
386 uint32_t fieldIdx = mir->dalvikInsn.vC;
Logan Chien4dd96f52012-02-29 01:26:58 +0800387
388 OatCompilationUnit mUnit(cUnit->class_loader, cUnit->class_linker,
389 *cUnit->dex_file, *cUnit->dex_cache, cUnit->code_item,
390 cUnit->method_idx, cUnit->access_flags);
391
Ian Rogers1bddec32012-02-04 12:27:34 -0800392 bool fastPath =
Logan Chien4dd96f52012-02-29 01:26:58 +0800393 cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, &mUnit,
jeffhao8cd6dda2012-02-22 10:15:34 -0800394 fieldOffset, isVolatile, false);
Ian Rogers1bddec32012-02-04 12:27:34 -0800395 if (fastPath && !SLOW_FIELD_PATH) {
396 RegLocation rlResult;
397 RegisterClass regClass = oatRegClassBySize(size);
398 DCHECK_GE(fieldOffset, 0);
buzbee34cd9e52011-09-08 14:31:52 -0700399 rlObj = loadValue(cUnit, rlObj, kCoreReg);
Ian Rogers1bddec32012-02-04 12:27:34 -0800400 if (isLongOrDouble) {
401 DCHECK(rlDest.wide);
402 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
403 int regPtr = oatAllocTemp(cUnit);
404 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
405 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
406 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
407 if (isVolatile) {
408 oatGenMemBarrier(cUnit, kSY);
409 }
410 oatFreeTemp(cUnit, regPtr);
411 storeValueWide(cUnit, rlDest, rlResult);
412 } else {
413 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
414 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
415 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
416 kWord, rlObj.sRegLow);
417 if (isVolatile) {
418 oatGenMemBarrier(cUnit, kSY);
419 }
420 storeValue(cUnit, rlDest, rlResult);
buzbee34cd9e52011-09-08 14:31:52 -0700421 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800422 } else {
423 int getterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pGet64Instance) :
424 (isObject ? OFFSETOF_MEMBER(Thread, pGetObjInstance)
425 : OFFSETOF_MEMBER(Thread, pGet32Instance));
426 loadWordDisp(cUnit, rSELF, getterOffset, rLR);
427 loadValueDirect(cUnit, rlObj, r1);
428 loadConstant(cUnit, r0, fieldIdx);
429 callRuntimeHelper(cUnit, rLR);
430 if (isLongOrDouble) {
431 RegLocation rlResult = oatGetReturnWide(cUnit);
432 storeValueWide(cUnit, rlDest, rlResult);
433 } else {
434 RegLocation rlResult = oatGetReturn(cUnit);
435 storeValue(cUnit, rlDest, rlResult);
436 }
buzbee67bf8852011-08-17 17:51:35 -0700437 }
buzbee67bf8852011-08-17 17:51:35 -0700438}
439
buzbeeed3e9302011-09-23 17:34:19 -0700440STATIC void genIPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
Ian Rogers1bddec32012-02-04 12:27:34 -0800441 RegLocation rlSrc, RegLocation rlObj,
442 bool isLongOrDouble, bool isObject)
buzbee67bf8852011-08-17 17:51:35 -0700443{
Ian Rogers1bddec32012-02-04 12:27:34 -0800444 int fieldOffset;
445 bool isVolatile;
446 uint32_t fieldIdx = mir->dalvikInsn.vC;
Logan Chien4dd96f52012-02-29 01:26:58 +0800447
448 OatCompilationUnit mUnit(cUnit->class_loader, cUnit->class_linker,
449 *cUnit->dex_file, *cUnit->dex_cache, cUnit->code_item,
450 cUnit->method_idx, cUnit->access_flags);
451
Ian Rogers1bddec32012-02-04 12:27:34 -0800452 bool fastPath =
Logan Chien4dd96f52012-02-29 01:26:58 +0800453 cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, &mUnit,
jeffhao8cd6dda2012-02-22 10:15:34 -0800454 fieldOffset, isVolatile, true);
Ian Rogers1bddec32012-02-04 12:27:34 -0800455 if (fastPath && !SLOW_FIELD_PATH) {
456 RegisterClass regClass = oatRegClassBySize(size);
457 DCHECK_GE(fieldOffset, 0);
buzbee34cd9e52011-09-08 14:31:52 -0700458 rlObj = loadValue(cUnit, rlObj, kCoreReg);
Ian Rogers1bddec32012-02-04 12:27:34 -0800459 if (isLongOrDouble) {
460 int regPtr;
461 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
462 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
463 regPtr = oatAllocTemp(cUnit);
464 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
465 if (isVolatile) {
466 oatGenMemBarrier(cUnit, kST);
467 }
468 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
469 if (isVolatile) {
470 oatGenMemBarrier(cUnit, kSY);
471 }
472 oatFreeTemp(cUnit, regPtr);
473 } else {
474 rlSrc = loadValue(cUnit, rlSrc, regClass);
475 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
476 if (isVolatile) {
477 oatGenMemBarrier(cUnit, kST);
478 }
479 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, kWord);
480 if (isVolatile) {
481 oatGenMemBarrier(cUnit, kSY);
482 }
buzbee34cd9e52011-09-08 14:31:52 -0700483 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800484 } else {
485 int setterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pSet64Instance) :
486 (isObject ? OFFSETOF_MEMBER(Thread, pSetObjInstance)
487 : OFFSETOF_MEMBER(Thread, pSet32Instance));
488 loadWordDisp(cUnit, rSELF, setterOffset, rLR);
489 loadValueDirect(cUnit, rlObj, r1);
490 if (isLongOrDouble) {
491 loadValueDirectWide(cUnit, rlSrc, r2, r3);
492 } else {
493 loadValueDirect(cUnit, rlSrc, r2);
buzbee12246b82011-09-29 14:15:05 -0700494 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800495 loadConstant(cUnit, r0, fieldIdx);
496 callRuntimeHelper(cUnit, rLR);
buzbee34cd9e52011-09-08 14:31:52 -0700497 }
buzbee67bf8852011-08-17 17:51:35 -0700498}
499
buzbeeed3e9302011-09-23 17:34:19 -0700500STATIC void genConstClass(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700501 RegLocation rlDest, RegLocation rlSrc)
502{
Ian Rogers28ad40d2011-10-27 15:19:26 -0700503 uint32_t type_idx = mir->dalvikInsn.vB;
buzbee1b4c8592011-08-31 10:43:51 -0700504 int mReg = loadCurrMethod(cUnit);
505 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700506 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800507 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
508 cUnit->dex_cache,
509 *cUnit->dex_file,
510 type_idx)) {
511 // Call out to helper which resolves type and verifies access.
512 // Resolved type returned in r0.
513 loadWordDisp(cUnit, rSELF,
514 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
515 rLR);
516 genRegCopy(cUnit, r1, mReg);
517 loadConstant(cUnit, r0, type_idx);
518 callRuntimeHelper(cUnit, rLR);
519 RegLocation rlResult = oatGetReturn(cUnit);
520 storeValue(cUnit, rlDest, rlResult);
buzbee1b4c8592011-08-31 10:43:51 -0700521 } else {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700522 // We're don't need access checks, load type from dex cache
523 int32_t dex_cache_offset = Method::DexCacheResolvedTypesOffset().Int32Value();
524 loadWordDisp(cUnit, mReg, dex_cache_offset, resReg);
Ian Rogersa15e67d2012-02-28 13:51:55 -0800525 int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() +
526 (sizeof(Class*) * type_idx);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700527 loadWordDisp(cUnit, resReg, offset_of_type, rlResult.lowReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800528 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache,
529 type_idx) ||
Ian Rogers28ad40d2011-10-27 15:19:26 -0700530 SLOW_TYPE_PATH) {
531 // Slow path, at runtime test if the type is null and if so initialize
532 oatFlushAllRegs(cUnit);
533 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg, 0);
534 // Resolved, store and hop over following code
535 storeValue(cUnit, rlDest, rlResult);
536 ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
537 // TUNING: move slow path to end & remove unconditional branch
538 ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
539 target1->defMask = ENCODE_ALL;
540 // Call out to helper, which will return resolved type in r0
541 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
542 genRegCopy(cUnit, r1, mReg);
543 loadConstant(cUnit, r0, type_idx);
544 callRuntimeHelper(cUnit, rLR);
545 RegLocation rlResult = oatGetReturn(cUnit);
546 storeValue(cUnit, rlDest, rlResult);
547 // Rejoin code paths
548 ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
549 target2->defMask = ENCODE_ALL;
550 branch1->generic.target = (LIR*)target1;
551 branch2->generic.target = (LIR*)target2;
552 } else {
553 // Fast path, we're done - just store result
554 storeValue(cUnit, rlDest, rlResult);
555 }
buzbee1b4c8592011-08-31 10:43:51 -0700556 }
buzbee67bf8852011-08-17 17:51:35 -0700557}
558
buzbee44b412b2012-02-04 08:50:53 -0800559/*
560 * Generate callout to updateDebugger. Note: genIT will automatically
561 * create a scheduling barrier, which we need to prevent code motion that
562 * might confuse the debugger. Note: Return registers r0/r1 are
563 * handled specially during code generation following function calls.
564 * Typically, temp registers are not live between opcodes, but we keep
565 * r0/r1 live following invokes, where they are consumed by the immediately
566 * following op_move_result_xxx. Thus, we must preserve and restore r0/r1
567 * when making a call to update the debugger. This is handled by the stub.
568 */
569STATIC void genDebuggerUpdate(CompilationUnit* cUnit, int32_t offset)
570{
571 // Following DCHECK verifies that dPC is in range of single load immediate
572 DCHECK((offset == DEBUGGER_METHOD_ENTRY) ||
573 (offset == DEBUGGER_METHOD_EXIT) || ((offset & 0xffff) == offset));
574 oatClobberCalleeSave(cUnit);
575 opRegImm(cUnit, kOpCmp, rSUSPEND, 0);
576 genIT(cUnit, kArmCondNe, "T");
577 loadConstant(cUnit, r2, offset); // arg2 <- Entry code
578 opReg(cUnit, kOpBlx, rSUSPEND);
579 oatFreeTemp(cUnit, r2);
580}
581
buzbeeed3e9302011-09-23 17:34:19 -0700582STATIC void genConstString(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700583 RegLocation rlDest, RegLocation rlSrc)
584{
buzbeece302932011-10-04 14:32:18 -0700585 /* NOTE: Most strings should be available at compile time */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700586 uint32_t string_idx = mir->dalvikInsn.vB;
Ian Rogersa15e67d2012-02-28 13:51:55 -0800587 int32_t offset_of_string = Array::DataOffset(sizeof(String*)).Int32Value() +
588 (sizeof(String*) * string_idx);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800589 if (!cUnit->compiler->CanAssumeStringIsPresentInDexCache(cUnit->dex_cache, string_idx) ||
Ian Rogers28ad40d2011-10-27 15:19:26 -0700590 SLOW_STRING_PATH) {
591 // slow path, resolve string if not in dex cache
buzbeece302932011-10-04 14:32:18 -0700592 oatFlushAllRegs(cUnit);
593 oatLockCallTemps(cUnit); // Using explicit registers
594 loadCurrMethodDirect(cUnit, r2);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700595 loadWordDisp(cUnit, r2, Method::DexCacheStringsOffset().Int32Value(), r0);
buzbeece302932011-10-04 14:32:18 -0700596 // Might call out to helper, which will return resolved string in r0
Ian Rogers28ad40d2011-10-27 15:19:26 -0700597 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pResolveStringFromCode), rLR);
598 loadWordDisp(cUnit, r0, offset_of_string, r0);
599 loadConstant(cUnit, r1, string_idx);
buzbeece302932011-10-04 14:32:18 -0700600 opRegImm(cUnit, kOpCmp, r0, 0); // Is resolved?
601 genBarrier(cUnit);
602 // For testing, always force through helper
603 if (!EXERCISE_SLOWEST_STRING_PATH) {
604 genIT(cUnit, kArmCondEq, "T");
605 }
606 genRegCopy(cUnit, r0, r2); // .eq
607 opReg(cUnit, kOpBlx, rLR); // .eq, helper(Method*, string_idx)
608 genBarrier(cUnit);
609 storeValue(cUnit, rlDest, getRetLoc(cUnit));
610 } else {
611 int mReg = loadCurrMethod(cUnit);
612 int resReg = oatAllocTemp(cUnit);
613 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700614 loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(), resReg);
615 loadWordDisp(cUnit, resReg, offset_of_string, rlResult.lowReg);
buzbeece302932011-10-04 14:32:18 -0700616 storeValue(cUnit, rlDest, rlResult);
617 }
buzbee67bf8852011-08-17 17:51:35 -0700618}
619
buzbeedfd3d702011-08-28 12:56:51 -0700620/*
621 * Let helper function take care of everything. Will
622 * call Class::NewInstanceFromCode(type_idx, method);
623 */
buzbeeed3e9302011-09-23 17:34:19 -0700624STATIC void genNewInstance(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700625 RegLocation rlDest)
626{
buzbeedfd3d702011-08-28 12:56:51 -0700627 oatFlushAllRegs(cUnit); /* Everything to home location */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700628 uint32_t type_idx = mir->dalvikInsn.vB;
629 // alloc will always check for resolution, do we also need to verify access because the
630 // verifier was unable to?
Ian Rogersd4135902012-02-03 18:05:08 -0800631 if (cUnit->compiler->CanAccessInstantiableTypeWithoutChecks(cUnit->method_idx,
632 cUnit->dex_cache,
633 *cUnit->dex_file,
634 type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700635 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
636 } else {
637 loadWordDisp(cUnit, rSELF,
638 OFFSETOF_MEMBER(Thread, pAllocObjectFromCodeWithAccessCheck), rLR);
639 }
640 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
641 loadConstant(cUnit, r0, type_idx); // arg0 <- type_idx
Ian Rogersff1ed472011-09-20 13:46:24 -0700642 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700643 RegLocation rlResult = oatGetReturn(cUnit);
644 storeValue(cUnit, rlDest, rlResult);
645}
646
647void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
648{
buzbee6181f792011-09-29 11:14:04 -0700649 oatFlushAllRegs(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700650 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
Ian Rogersbdb03912011-09-14 00:55:44 -0700651 loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
Ian Rogersff1ed472011-09-20 13:46:24 -0700652 callRuntimeHelper(cUnit, rLR); // art_deliver_exception(exception);
buzbee67bf8852011-08-17 17:51:35 -0700653}
654
buzbeeed3e9302011-09-23 17:34:19 -0700655STATIC void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700656 RegLocation rlSrc)
657{
buzbee6181f792011-09-29 11:14:04 -0700658 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700659 // May generate a call - use explicit registers
660 oatLockCallTemps(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700661 uint32_t type_idx = mir->dalvikInsn.vC;
buzbee2a475e72011-09-07 17:19:17 -0700662 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
Ian Rogers28ad40d2011-10-27 15:19:26 -0700663 int classReg = r2; // r2 will hold the Class*
Ian Rogersa3760aa2011-11-14 14:32:37 -0800664 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
665 cUnit->dex_cache,
666 *cUnit->dex_file,
667 type_idx)) {
Ian Rogersb093c6b2011-10-31 16:19:55 -0700668 // Check we have access to type_idx and if not throw IllegalAccessError,
669 // returns Class* in r0
670 loadWordDisp(cUnit, rSELF,
671 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
672 rLR);
673 loadConstant(cUnit, r0, type_idx);
674 callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
675 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers6a996782011-10-31 17:06:39 -0700676 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
Ian Rogers28ad40d2011-10-27 15:19:26 -0700677 } else {
678 // Load dex cache entry into classReg (r2)
Ian Rogers6a996782011-10-31 17:06:39 -0700679 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
Ian Rogers28ad40d2011-10-27 15:19:26 -0700680 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
Ian Rogersa15e67d2012-02-28 13:51:55 -0800681 int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() +
682 (sizeof(Class*) * type_idx);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700683 loadWordDisp(cUnit, classReg, offset_of_type, classReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800684 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache, type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700685 // Need to test presence of type in dex cache at runtime
686 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
687 // Not resolved
688 // Call out to helper, which will return resolved type in r0
689 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
690 loadConstant(cUnit, r0, type_idx);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700691 callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
Ian Rogers28ad40d2011-10-27 15:19:26 -0700692 genRegCopy(cUnit, r2, r0); // Align usage with fast path
693 loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */
694 // Rejoin code paths
695 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
696 hopTarget->defMask = ENCODE_ALL;
697 hopBranch->generic.target = (LIR*)hopTarget;
698 }
buzbee67bf8852011-08-17 17:51:35 -0700699 }
buzbee991e3ac2011-09-29 15:44:22 -0700700 /* r0 is ref, r2 is class. If ref==null, use directly as bool result */
701 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
buzbee2a475e72011-09-07 17:19:17 -0700702 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700703 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee991e3ac2011-09-29 15:44:22 -0700704 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
705 /* r0 is ref, r1 is ref->clazz, r2 is class */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700706 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
buzbee991e3ac2011-09-29 15:44:22 -0700707 opRegReg(cUnit, kOpCmp, r1, r2); // Same?
708 genBarrier(cUnit);
709 genIT(cUnit, kArmCondEq, "EE"); // if-convert the test
710 loadConstant(cUnit, r0, 1); // .eq case - load true
711 genRegCopy(cUnit, r0, r2); // .ne case - arg0 <= class
712 opReg(cUnit, kOpBlx, rLR); // .ne case: helper(class, ref->class)
713 genBarrier(cUnit);
714 oatClobberCalleeSave(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700715 /* branch target here */
716 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
717 target->defMask = ENCODE_ALL;
buzbee2a475e72011-09-07 17:19:17 -0700718 RegLocation rlResult = oatGetReturn(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700719 storeValue(cUnit, rlDest, rlResult);
720 branch1->generic.target = (LIR*)target;
buzbee67bf8852011-08-17 17:51:35 -0700721}
722
buzbeeed3e9302011-09-23 17:34:19 -0700723STATIC void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbee67bf8852011-08-17 17:51:35 -0700724{
buzbee6181f792011-09-29 11:14:04 -0700725 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700726 // May generate a call - use explicit registers
727 oatLockCallTemps(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700728 uint32_t type_idx = mir->dalvikInsn.vB;
buzbee2a475e72011-09-07 17:19:17 -0700729 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
Ian Rogers28ad40d2011-10-27 15:19:26 -0700730 int classReg = r2; // r2 will hold the Class*
Ian Rogersa3760aa2011-11-14 14:32:37 -0800731 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
732 cUnit->dex_cache,
733 *cUnit->dex_file,
734 type_idx)) {
Ian Rogersb093c6b2011-10-31 16:19:55 -0700735 // Check we have access to type_idx and if not throw IllegalAccessError,
736 // returns Class* in r0
737 loadWordDisp(cUnit, rSELF,
738 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
739 rLR);
740 loadConstant(cUnit, r0, type_idx);
741 callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
742 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers28ad40d2011-10-27 15:19:26 -0700743 } else {
744 // Load dex cache entry into classReg (r2)
745 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
Ian Rogersa15e67d2012-02-28 13:51:55 -0800746 int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() +
747 (sizeof(Class*) * type_idx);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700748 loadWordDisp(cUnit, classReg, offset_of_type, classReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800749 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache, type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700750 // Need to test presence of type in dex cache at runtime
751 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
752 // Not resolved
753 // Call out to helper, which will return resolved type in r0
754 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
755 loadConstant(cUnit, r0, type_idx);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700756 callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
757 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers28ad40d2011-10-27 15:19:26 -0700758 // Rejoin code paths
759 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
760 hopTarget->defMask = ENCODE_ALL;
761 hopBranch->generic.target = (LIR*)hopTarget;
762 }
buzbee67bf8852011-08-17 17:51:35 -0700763 }
Ian Rogers28ad40d2011-10-27 15:19:26 -0700764 // At this point, classReg (r2) has class
765 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
buzbee2a475e72011-09-07 17:19:17 -0700766 /* Null is OK - continue */
767 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
768 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700769 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee2a475e72011-09-07 17:19:17 -0700770 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
771 /* r1 now contains object->clazz */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700772 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700773 opRegReg(cUnit, kOpCmp, r1, classReg);
buzbee2a475e72011-09-07 17:19:17 -0700774 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
775 genRegCopy(cUnit, r0, r1);
776 genRegCopy(cUnit, r1, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -0700777 callRuntimeHelper(cUnit, rLR);
buzbee2a475e72011-09-07 17:19:17 -0700778 /* branch target here */
buzbee67bf8852011-08-17 17:51:35 -0700779 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
780 target->defMask = ENCODE_ALL;
781 branch1->generic.target = (LIR*)target;
782 branch2->generic.target = (LIR*)target;
783}
784
buzbeeed3e9302011-09-23 17:34:19 -0700785STATIC void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700786 RegLocation rlSrc)
787{
788 RegLocation rlResult;
789 rlSrc = loadValue(cUnit, rlSrc, kFPReg);
790 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
791 newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
792 storeValue(cUnit, rlDest, rlResult);
793}
794
buzbeeed3e9302011-09-23 17:34:19 -0700795STATIC void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700796 RegLocation rlSrc)
797{
798 RegLocation rlResult;
799 rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
800 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
801 newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
802 S2D(rlSrc.lowReg, rlSrc.highReg));
803 storeValueWide(cUnit, rlDest, rlResult);
804}
805
buzbeeed3e9302011-09-23 17:34:19 -0700806STATIC void freeRegLocTemps(CompilationUnit* cUnit, RegLocation rlKeep,
buzbee439c4fa2011-08-27 15:59:07 -0700807 RegLocation rlFree)
buzbee67bf8852011-08-17 17:51:35 -0700808{
buzbee6181f792011-09-29 11:14:04 -0700809 if ((rlFree.lowReg != rlKeep.lowReg) && (rlFree.lowReg != rlKeep.highReg) &&
810 (rlFree.highReg != rlKeep.lowReg) && (rlFree.highReg != rlKeep.highReg)) {
811 // No overlap, free both
buzbee439c4fa2011-08-27 15:59:07 -0700812 oatFreeTemp(cUnit, rlFree.lowReg);
buzbee6181f792011-09-29 11:14:04 -0700813 oatFreeTemp(cUnit, rlFree.highReg);
814 }
buzbee67bf8852011-08-17 17:51:35 -0700815}
816
buzbeeed3e9302011-09-23 17:34:19 -0700817STATIC void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp,
buzbee67bf8852011-08-17 17:51:35 -0700818 OpKind secondOp, RegLocation rlDest,
819 RegLocation rlSrc1, RegLocation rlSrc2)
820{
buzbee9e0f9b02011-08-24 15:32:46 -0700821 /*
822 * NOTE: This is the one place in the code in which we might have
823 * as many as six live temporary registers. There are 5 in the normal
824 * set for Arm. Until we have spill capabilities, temporarily add
825 * lr to the temp set. It is safe to do this locally, but note that
826 * lr is used explicitly elsewhere in the code generator and cannot
827 * normally be used as a general temp register.
828 */
buzbee67bf8852011-08-17 17:51:35 -0700829 RegLocation rlResult;
buzbee9e0f9b02011-08-24 15:32:46 -0700830 oatMarkTemp(cUnit, rLR); // Add lr to the temp pool
831 oatFreeTemp(cUnit, rLR); // and make it available
buzbee67bf8852011-08-17 17:51:35 -0700832 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
833 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
834 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeec0ecd652011-09-25 18:11:54 -0700835 // The longs may overlap - use intermediate temp if so
836 if (rlResult.lowReg == rlSrc1.highReg) {
buzbeec0ecd652011-09-25 18:11:54 -0700837 int tReg = oatAllocTemp(cUnit);
838 genRegCopy(cUnit, tReg, rlSrc1.highReg);
839 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
840 rlSrc2.lowReg);
841 opRegRegReg(cUnit, secondOp, rlResult.highReg, tReg,
842 rlSrc2.highReg);
843 oatFreeTemp(cUnit, tReg);
844 } else {
845 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
846 rlSrc2.lowReg);
847 opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
848 rlSrc2.highReg);
849 }
buzbee439c4fa2011-08-27 15:59:07 -0700850 /*
851 * NOTE: If rlDest refers to a frame variable in a large frame, the
852 * following storeValueWide might need to allocate a temp register.
853 * To further work around the lack of a spill capability, explicitly
854 * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult.
855 * Remove when spill is functional.
856 */
857 freeRegLocTemps(cUnit, rlResult, rlSrc1);
858 freeRegLocTemps(cUnit, rlResult, rlSrc2);
buzbee67bf8852011-08-17 17:51:35 -0700859 storeValueWide(cUnit, rlDest, rlResult);
buzbee9e0f9b02011-08-24 15:32:46 -0700860 oatClobber(cUnit, rLR);
861 oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool
buzbee67bf8852011-08-17 17:51:35 -0700862}
863
864void oatInitializeRegAlloc(CompilationUnit* cUnit)
865{
866 int numRegs = sizeof(coreRegs)/sizeof(*coreRegs);
867 int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs);
868 int numTemps = sizeof(coreTemps)/sizeof(*coreTemps);
869 int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs);
870 int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps);
buzbeeba938cb2012-02-03 14:47:55 -0800871 RegisterPool *pool = (RegisterPool *)oatNew(cUnit, sizeof(*pool), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800872 kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700873 cUnit->regPool = pool;
874 pool->numCoreRegs = numRegs;
875 pool->coreRegs = (RegisterInfo *)
buzbeeba938cb2012-02-03 14:47:55 -0800876 oatNew(cUnit, numRegs * sizeof(*cUnit->regPool->coreRegs),
877 true, kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700878 pool->numFPRegs = numFPRegs;
879 pool->FPRegs = (RegisterInfo *)
buzbeeba938cb2012-02-03 14:47:55 -0800880 oatNew(cUnit, numFPRegs * sizeof(*cUnit->regPool->FPRegs), true,
881 kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700882 oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs);
883 oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs);
884 // Keep special registers from being allocated
885 for (int i = 0; i < numReserved; i++) {
buzbee44b412b2012-02-04 08:50:53 -0800886 if (NO_SUSPEND && !cUnit->genDebugger &&
887 (reservedRegs[i] == rSUSPEND)) {
buzbeec0ecd652011-09-25 18:11:54 -0700888 //To measure cost of suspend check
889 continue;
890 }
buzbee67bf8852011-08-17 17:51:35 -0700891 oatMarkInUse(cUnit, reservedRegs[i]);
892 }
893 // Mark temp regs - all others not in use can be used for promotion
894 for (int i = 0; i < numTemps; i++) {
895 oatMarkTemp(cUnit, coreTemps[i]);
896 }
897 for (int i = 0; i < numFPTemps; i++) {
898 oatMarkTemp(cUnit, fpTemps[i]);
899 }
buzbeec0ecd652011-09-25 18:11:54 -0700900 // Construct the alias map.
buzbeeba938cb2012-02-03 14:47:55 -0800901 cUnit->phiAliasMap = (int*)oatNew(cUnit, cUnit->numSSARegs *
buzbee5abfa3e2012-01-31 17:01:43 -0800902 sizeof(cUnit->phiAliasMap[0]), false,
903 kAllocDFInfo);
buzbeec0ecd652011-09-25 18:11:54 -0700904 for (int i = 0; i < cUnit->numSSARegs; i++) {
905 cUnit->phiAliasMap[i] = i;
906 }
907 for (MIR* phi = cUnit->phiList; phi; phi = phi->meta.phiNext) {
908 int defReg = phi->ssaRep->defs[0];
909 for (int i = 0; i < phi->ssaRep->numUses; i++) {
910 for (int j = 0; j < cUnit->numSSARegs; j++) {
911 if (cUnit->phiAliasMap[j] == phi->ssaRep->uses[i]) {
912 cUnit->phiAliasMap[j] = defReg;
913 }
914 }
915 }
916 }
buzbee67bf8852011-08-17 17:51:35 -0700917}
918
919/*
920 * Handle simple case (thin lock) inline. If it's complicated, bail
921 * out to the heavyweight lock/unlock routines. We'll use dedicated
922 * registers here in order to be in the right position in case we
923 * to bail to dvm[Lock/Unlock]Object(self, object)
924 *
925 * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
926 * r1 -> object [arg1 for dvm[Lock/Unlock]Object
927 * r2 -> intial contents of object->lock, later result of strex
928 * r3 -> self->threadId
929 * r12 -> allow to be used by utilities as general temp
930 *
931 * The result of the strex is 0 if we acquire the lock.
932 *
933 * See comments in Sync.c for the layout of the lock word.
934 * Of particular interest to this code is the test for the
935 * simple case - which we handle inline. For monitor enter, the
936 * simple case is thin lock, held by no-one. For monitor exit,
937 * the simple case is thin lock, held by the unlocking thread with
938 * a recurse count of 0.
939 *
940 * A minor complication is that there is a field in the lock word
941 * unrelated to locking: the hash state. This field must be ignored, but
942 * preserved.
943 *
944 */
buzbeeed3e9302011-09-23 17:34:19 -0700945STATIC void genMonitorEnter(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700946 RegLocation rlSrc)
947{
948 ArmLIR* target;
949 ArmLIR* hopTarget;
950 ArmLIR* branch;
951 ArmLIR* hopBranch;
952
953 oatFlushAllRegs(cUnit);
Elliott Hughes5f791332011-09-15 17:45:30 -0700954 DCHECK_EQ(LW_SHAPE_THIN, 0);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700955 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700956 oatLockCallTemps(cUnit); // Prepare for explicit register usage
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700957 genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
958 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
959 newLIR3(cUnit, kThumb2Ldrex, r1, r0,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700960 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
buzbeec143c552011-08-20 17:38:58 -0700961 // Align owner
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700962 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
buzbee67bf8852011-08-17 17:51:35 -0700963 // Is lock unheld on lock or held by us (==threadId) on unlock?
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700964 newLIR4(cUnit, kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
965 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
966 hopBranch = newLIR2(cUnit, kThumb2Cbnz, r1, 0);
967 newLIR4(cUnit, kThumb2Strex, r1, r2, r0,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700968 Object::MonitorOffset().Int32Value() >> 2);
buzbee67bf8852011-08-17 17:51:35 -0700969 oatGenMemBarrier(cUnit, kSY);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700970 branch = newLIR2(cUnit, kThumb2Cbz, r1, 0);
buzbee67bf8852011-08-17 17:51:35 -0700971
972 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
973 hopTarget->defMask = ENCODE_ALL;
974 hopBranch->generic.target = (LIR*)hopTarget;
975
buzbee1b4c8592011-08-31 10:43:51 -0700976 // Go expensive route - artLockObjectFromCode(self, obj);
977 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700978 rLR);
Ian Rogersff1ed472011-09-20 13:46:24 -0700979 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700980
981 // Resume here
982 target = newLIR0(cUnit, kArmPseudoTargetLabel);
983 target->defMask = ENCODE_ALL;
984 branch->generic.target = (LIR*)target;
985}
986
987/*
988 * For monitor unlock, we don't have to use ldrex/strex. Once
989 * we've determined that the lock is thin and that we own it with
990 * a zero recursion count, it's safe to punch it back to the
991 * initial, unlock thin state with a store word.
992 */
buzbeeed3e9302011-09-23 17:34:19 -0700993STATIC void genMonitorExit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700994 RegLocation rlSrc)
995{
996 ArmLIR* target;
997 ArmLIR* branch;
998 ArmLIR* hopTarget;
999 ArmLIR* hopBranch;
1000
Elliott Hughes5f791332011-09-15 17:45:30 -07001001 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -07001002 oatFlushAllRegs(cUnit);
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001003 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
buzbee2e748f32011-08-29 21:02:19 -07001004 oatLockCallTemps(cUnit); // Prepare for explicit register usage
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001005 genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
1006 loadWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r1); // Get lock
1007 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
buzbee67bf8852011-08-17 17:51:35 -07001008 // Is lock unheld on lock or held by us (==threadId) on unlock?
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001009 opRegRegImm(cUnit, kOpAnd, r3, r1, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
buzbeec143c552011-08-20 17:38:58 -07001010 // Align owner
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001011 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
1012 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
1013 opRegReg(cUnit, kOpSub, r1, r2);
buzbee67bf8852011-08-17 17:51:35 -07001014 hopBranch = opCondBranch(cUnit, kArmCondNe);
1015 oatGenMemBarrier(cUnit, kSY);
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001016 storeWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -07001017 branch = opNone(cUnit, kOpUncondBr);
1018
1019 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
1020 hopTarget->defMask = ENCODE_ALL;
1021 hopBranch->generic.target = (LIR*)hopTarget;
1022
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001023 // Go expensive route - UnlockObjectFromCode(obj);
buzbee1b4c8592011-08-31 10:43:51 -07001024 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -07001025 rLR);
Ian Rogersff1ed472011-09-20 13:46:24 -07001026 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001027
1028 // Resume here
1029 target = newLIR0(cUnit, kArmPseudoTargetLabel);
1030 target->defMask = ENCODE_ALL;
1031 branch->generic.target = (LIR*)target;
1032}
1033
1034/*
1035 * 64-bit 3way compare function.
1036 * mov rX, #-1
1037 * cmp op1hi, op2hi
1038 * blt done
1039 * bgt flip
1040 * sub rX, op1lo, op2lo (treat as unsigned)
1041 * beq done
1042 * ite hi
1043 * mov(hi) rX, #-1
1044 * mov(!hi) rX, #1
1045 * flip:
1046 * neg rX
1047 * done:
1048 */
buzbeeed3e9302011-09-23 17:34:19 -07001049STATIC void genCmpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001050 RegLocation rlDest, RegLocation rlSrc1,
1051 RegLocation rlSrc2)
1052{
buzbee67bf8852011-08-17 17:51:35 -07001053 ArmLIR* target1;
1054 ArmLIR* target2;
1055 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
1056 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
buzbeeb29e4d12011-09-26 15:05:48 -07001057 int tReg = oatAllocTemp(cUnit);
1058 loadConstant(cUnit, tReg, -1);
buzbee67bf8852011-08-17 17:51:35 -07001059 opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
1060 ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt);
1061 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt);
buzbeeb29e4d12011-09-26 15:05:48 -07001062 opRegRegReg(cUnit, kOpSub, tReg, rlSrc1.lowReg, rlSrc2.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001063 ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq);
1064
1065 genIT(cUnit, kArmCondHi, "E");
buzbeeb29e4d12011-09-26 15:05:48 -07001066 newLIR2(cUnit, kThumb2MovImmShift, tReg, modifiedImmediate(-1));
1067 loadConstant(cUnit, tReg, 1);
buzbee67bf8852011-08-17 17:51:35 -07001068 genBarrier(cUnit);
1069
1070 target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
1071 target2->defMask = -1;
buzbeeb29e4d12011-09-26 15:05:48 -07001072 opRegReg(cUnit, kOpNeg, tReg, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001073
1074 target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
1075 target1->defMask = -1;
1076
buzbeeb29e4d12011-09-26 15:05:48 -07001077 RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
1078 rlTemp.lowReg = tReg;
buzbee67bf8852011-08-17 17:51:35 -07001079 storeValue(cUnit, rlDest, rlTemp);
buzbeeb29e4d12011-09-26 15:05:48 -07001080 oatFreeTemp(cUnit, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001081
1082 branch1->generic.target = (LIR*)target1;
1083 branch2->generic.target = (LIR*)target2;
1084 branch3->generic.target = branch1->generic.target;
1085}
1086
buzbeeed3e9302011-09-23 17:34:19 -07001087STATIC void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001088 RegLocation rlSrc, RegLocation rlResult, int lit,
1089 int firstBit, int secondBit)
1090{
1091 opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
1092 encodeShift(kArmLsl, secondBit - firstBit));
1093 if (firstBit != 0) {
1094 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
1095 }
1096}
1097
buzbeeed3e9302011-09-23 17:34:19 -07001098STATIC bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset,
buzbee67bf8852011-08-17 17:51:35 -07001099 int srcSize, int tgtSize)
1100{
1101 /*
1102 * Don't optimize the register usage since it calls out to support
1103 * functions
1104 */
1105 RegLocation rlSrc;
1106 RegLocation rlDest;
1107 oatFlushAllRegs(cUnit); /* Send everything to home location */
1108 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1109 if (srcSize == 1) {
1110 rlSrc = oatGetSrc(cUnit, mir, 0);
1111 loadValueDirectFixed(cUnit, rlSrc, r0);
1112 } else {
1113 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
1114 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
1115 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001116 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001117 if (tgtSize == 1) {
1118 RegLocation rlResult;
1119 rlDest = oatGetDest(cUnit, mir, 0);
1120 rlResult = oatGetReturn(cUnit);
1121 storeValue(cUnit, rlDest, rlResult);
1122 } else {
1123 RegLocation rlResult;
1124 rlDest = oatGetDestWide(cUnit, mir, 0, 1);
1125 rlResult = oatGetReturnWide(cUnit);
1126 storeValueWide(cUnit, rlDest, rlResult);
1127 }
1128 return false;
1129}
1130
buzbeeed3e9302011-09-23 17:34:19 -07001131STATIC bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001132 RegLocation rlDest, RegLocation rlSrc1,
1133 RegLocation rlSrc2)
1134{
1135 RegLocation rlResult;
1136 int funcOffset;
1137
1138 switch (mir->dalvikInsn.opcode) {
1139 case OP_ADD_FLOAT_2ADDR:
1140 case OP_ADD_FLOAT:
1141 funcOffset = OFFSETOF_MEMBER(Thread, pFadd);
1142 break;
1143 case OP_SUB_FLOAT_2ADDR:
1144 case OP_SUB_FLOAT:
1145 funcOffset = OFFSETOF_MEMBER(Thread, pFsub);
1146 break;
1147 case OP_DIV_FLOAT_2ADDR:
1148 case OP_DIV_FLOAT:
1149 funcOffset = OFFSETOF_MEMBER(Thread, pFdiv);
1150 break;
1151 case OP_MUL_FLOAT_2ADDR:
1152 case OP_MUL_FLOAT:
1153 funcOffset = OFFSETOF_MEMBER(Thread, pFmul);
1154 break;
1155 case OP_REM_FLOAT_2ADDR:
1156 case OP_REM_FLOAT:
1157 funcOffset = OFFSETOF_MEMBER(Thread, pFmodf);
1158 break;
1159 case OP_NEG_FLOAT: {
1160 genNegFloat(cUnit, rlDest, rlSrc1);
1161 return false;
1162 }
1163 default:
1164 return true;
1165 }
1166 oatFlushAllRegs(cUnit); /* Send everything to home location */
1167 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1168 loadValueDirectFixed(cUnit, rlSrc1, r0);
1169 loadValueDirectFixed(cUnit, rlSrc2, r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001170 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001171 rlResult = oatGetReturn(cUnit);
1172 storeValue(cUnit, rlDest, rlResult);
1173 return false;
1174}
1175
buzbeeed3e9302011-09-23 17:34:19 -07001176STATIC bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001177 RegLocation rlDest, RegLocation rlSrc1,
1178 RegLocation rlSrc2)
1179{
1180 RegLocation rlResult;
1181 int funcOffset;
1182
1183 switch (mir->dalvikInsn.opcode) {
1184 case OP_ADD_DOUBLE_2ADDR:
1185 case OP_ADD_DOUBLE:
1186 funcOffset = OFFSETOF_MEMBER(Thread, pDadd);
1187 break;
1188 case OP_SUB_DOUBLE_2ADDR:
1189 case OP_SUB_DOUBLE:
1190 funcOffset = OFFSETOF_MEMBER(Thread, pDsub);
1191 break;
1192 case OP_DIV_DOUBLE_2ADDR:
1193 case OP_DIV_DOUBLE:
1194 funcOffset = OFFSETOF_MEMBER(Thread, pDdiv);
1195 break;
1196 case OP_MUL_DOUBLE_2ADDR:
1197 case OP_MUL_DOUBLE:
1198 funcOffset = OFFSETOF_MEMBER(Thread, pDmul);
1199 break;
1200 case OP_REM_DOUBLE_2ADDR:
1201 case OP_REM_DOUBLE:
1202 funcOffset = OFFSETOF_MEMBER(Thread, pFmod);
1203 break;
1204 case OP_NEG_DOUBLE: {
1205 genNegDouble(cUnit, rlDest, rlSrc1);
1206 return false;
1207 }
1208 default:
1209 return true;
1210 }
1211 oatFlushAllRegs(cUnit); /* Send everything to home location */
1212 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1213 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1214 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
Ian Rogersff1ed472011-09-20 13:46:24 -07001215 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001216 rlResult = oatGetReturnWide(cUnit);
1217 storeValueWide(cUnit, rlDest, rlResult);
1218 return false;
1219}
1220
buzbeeed3e9302011-09-23 17:34:19 -07001221STATIC bool genConversionPortable(CompilationUnit* cUnit, MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -07001222{
1223 Opcode opcode = mir->dalvikInsn.opcode;
1224
1225 switch (opcode) {
1226 case OP_INT_TO_FLOAT:
1227 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f),
1228 1, 1);
1229 case OP_FLOAT_TO_INT:
1230 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz),
1231 1, 1);
1232 case OP_DOUBLE_TO_FLOAT:
1233 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f),
1234 2, 1);
1235 case OP_FLOAT_TO_DOUBLE:
1236 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d),
1237 1, 2);
1238 case OP_INT_TO_DOUBLE:
1239 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d),
1240 1, 2);
1241 case OP_DOUBLE_TO_INT:
1242 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz),
1243 2, 1);
1244 case OP_FLOAT_TO_LONG:
1245 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001246 pF2l), 1, 2);
buzbee67bf8852011-08-17 17:51:35 -07001247 case OP_LONG_TO_FLOAT:
1248 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f),
1249 2, 1);
1250 case OP_DOUBLE_TO_LONG:
1251 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001252 pD2l), 2, 2);
buzbee67bf8852011-08-17 17:51:35 -07001253 case OP_LONG_TO_DOUBLE:
1254 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d),
1255 2, 2);
1256 default:
1257 return true;
1258 }
1259 return false;
1260}
1261
buzbee67bf8852011-08-17 17:51:35 -07001262/*
1263 * Generate array store
1264 *
1265 */
buzbeeed3e9302011-09-23 17:34:19 -07001266STATIC void genArrayObjPut(CompilationUnit* cUnit, MIR* mir,
buzbee1b4c8592011-08-31 10:43:51 -07001267 RegLocation rlArray, RegLocation rlIndex,
1268 RegLocation rlSrc, int scale)
buzbee67bf8852011-08-17 17:51:35 -07001269{
1270 RegisterClass regClass = oatRegClassBySize(kWord);
buzbeec143c552011-08-20 17:38:58 -07001271 int lenOffset = Array::LengthOffset().Int32Value();
Ian Rogersa15e67d2012-02-28 13:51:55 -08001272 int dataOffset = Array::DataOffset(sizeof(Object*)).Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001273
buzbee6181f792011-09-29 11:14:04 -07001274 oatFlushAllRegs(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001275 /* Make sure it's a legal object Put. Use direct regs at first */
1276 loadValueDirectFixed(cUnit, rlArray, r1);
1277 loadValueDirectFixed(cUnit, rlSrc, r0);
1278
1279 /* null array object? */
buzbee43a36422011-09-14 14:00:13 -07001280 genNullCheck(cUnit, rlArray.sRegLow, r1, mir);
buzbee67bf8852011-08-17 17:51:35 -07001281 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -07001282 OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -07001283 /* Get the array's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001284 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001285 callRuntimeHelper(cUnit, rLR);
buzbee6181f792011-09-29 11:14:04 -07001286 oatFreeTemp(cUnit, r0);
1287 oatFreeTemp(cUnit, r1);
buzbee67bf8852011-08-17 17:51:35 -07001288
1289 // Now, redo loadValues in case they didn't survive the call
1290
1291 int regPtr;
1292 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1293 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1294
1295 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1296 oatClobber(cUnit, rlArray.lowReg);
1297 regPtr = rlArray.lowReg;
1298 } else {
1299 regPtr = oatAllocTemp(cUnit);
1300 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1301 }
1302
buzbee43a36422011-09-14 14:00:13 -07001303 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001304 int regLen = oatAllocTemp(cUnit);
1305 //NOTE: max live temps(4) here.
1306 /* Get len */
1307 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1308 /* regPtr -> array data */
1309 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001310 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001311 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001312 oatFreeTemp(cUnit, regLen);
1313 } else {
1314 /* regPtr -> array data */
1315 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1316 }
1317 /* at this point, regPtr points to array, 2 live temps */
1318 rlSrc = loadValue(cUnit, rlSrc, regClass);
1319 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1320 scale, kWord);
1321}
1322
1323/*
1324 * Generate array load
1325 */
buzbeeed3e9302011-09-23 17:34:19 -07001326STATIC void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001327 RegLocation rlArray, RegLocation rlIndex,
1328 RegLocation rlDest, int scale)
1329{
1330 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001331 int lenOffset = Array::LengthOffset().Int32Value();
Ian Rogersa15e67d2012-02-28 13:51:55 -08001332 int dataOffset;
buzbee67bf8852011-08-17 17:51:35 -07001333 RegLocation rlResult;
1334 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1335 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1336 int regPtr;
1337
Ian Rogersa15e67d2012-02-28 13:51:55 -08001338 if (size == kLong || size == kDouble) {
1339 dataOffset = Array::DataOffset(sizeof(int64_t)).Int32Value();
1340 } else {
1341 dataOffset = Array::DataOffset(sizeof(int32_t)).Int32Value();
1342 }
1343
buzbee67bf8852011-08-17 17:51:35 -07001344 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001345 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001346
1347 regPtr = oatAllocTemp(cUnit);
1348
buzbee43a36422011-09-14 14:00:13 -07001349 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001350 int regLen = oatAllocTemp(cUnit);
1351 /* Get len */
1352 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1353 /* regPtr -> array data */
1354 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001355 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001356 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001357 oatFreeTemp(cUnit, regLen);
1358 } else {
1359 /* regPtr -> array data */
1360 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
1361 }
buzbeee9a72f62011-09-04 17:59:07 -07001362 oatFreeTemp(cUnit, rlArray.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001363 if ((size == kLong) || (size == kDouble)) {
1364 if (scale) {
1365 int rNewIndex = oatAllocTemp(cUnit);
1366 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1367 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1368 oatFreeTemp(cUnit, rNewIndex);
1369 } else {
1370 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1371 }
buzbeee9a72f62011-09-04 17:59:07 -07001372 oatFreeTemp(cUnit, rlIndex.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001373 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1374
1375 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
1376
1377 oatFreeTemp(cUnit, regPtr);
1378 storeValueWide(cUnit, rlDest, rlResult);
1379 } else {
1380 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1381
1382 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
1383 scale, size);
1384
1385 oatFreeTemp(cUnit, regPtr);
1386 storeValue(cUnit, rlDest, rlResult);
1387 }
1388}
1389
1390/*
1391 * Generate array store
1392 *
1393 */
buzbeeed3e9302011-09-23 17:34:19 -07001394STATIC void genArrayPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001395 RegLocation rlArray, RegLocation rlIndex,
1396 RegLocation rlSrc, int scale)
1397{
1398 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001399 int lenOffset = Array::LengthOffset().Int32Value();
Ian Rogersa15e67d2012-02-28 13:51:55 -08001400 int dataOffset;
1401
1402 if (size == kLong || size == kDouble) {
1403 dataOffset = Array::DataOffset(sizeof(int64_t)).Int32Value();
1404 } else {
1405 dataOffset = Array::DataOffset(sizeof(int32_t)).Int32Value();
1406 }
buzbee67bf8852011-08-17 17:51:35 -07001407
1408 int regPtr;
1409 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1410 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1411
1412 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1413 oatClobber(cUnit, rlArray.lowReg);
1414 regPtr = rlArray.lowReg;
1415 } else {
1416 regPtr = oatAllocTemp(cUnit);
1417 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1418 }
1419
1420 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001421 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001422
buzbee43a36422011-09-14 14:00:13 -07001423 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001424 int regLen = oatAllocTemp(cUnit);
1425 //NOTE: max live temps(4) here.
1426 /* Get len */
1427 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1428 /* regPtr -> array data */
1429 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001430 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001431 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001432 oatFreeTemp(cUnit, regLen);
1433 } else {
1434 /* regPtr -> array data */
1435 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1436 }
1437 /* at this point, regPtr points to array, 2 live temps */
1438 if ((size == kLong) || (size == kDouble)) {
buzbee5ade1d22011-09-09 14:44:52 -07001439 //TUNING: specific wide routine that can handle fp regs
buzbee67bf8852011-08-17 17:51:35 -07001440 if (scale) {
1441 int rNewIndex = oatAllocTemp(cUnit);
1442 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1443 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1444 oatFreeTemp(cUnit, rNewIndex);
1445 } else {
1446 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1447 }
1448 rlSrc = loadValueWide(cUnit, rlSrc, regClass);
1449
1450 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
1451
1452 oatFreeTemp(cUnit, regPtr);
1453 } else {
1454 rlSrc = loadValue(cUnit, rlSrc, regClass);
1455
1456 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1457 scale, size);
1458 }
1459}
1460
buzbeeed3e9302011-09-23 17:34:19 -07001461STATIC bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001462 RegLocation rlDest, RegLocation rlSrc1,
1463 RegLocation rlShift)
1464{
buzbee54330722011-08-23 16:46:55 -07001465 int funcOffset;
buzbee67bf8852011-08-17 17:51:35 -07001466
buzbee67bf8852011-08-17 17:51:35 -07001467 switch( mir->dalvikInsn.opcode) {
1468 case OP_SHL_LONG:
1469 case OP_SHL_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001470 funcOffset = OFFSETOF_MEMBER(Thread, pShlLong);
buzbee67bf8852011-08-17 17:51:35 -07001471 break;
1472 case OP_SHR_LONG:
1473 case OP_SHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001474 funcOffset = OFFSETOF_MEMBER(Thread, pShrLong);
buzbee67bf8852011-08-17 17:51:35 -07001475 break;
1476 case OP_USHR_LONG:
1477 case OP_USHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001478 funcOffset = OFFSETOF_MEMBER(Thread, pUshrLong);
buzbee67bf8852011-08-17 17:51:35 -07001479 break;
1480 default:
buzbee54330722011-08-23 16:46:55 -07001481 LOG(FATAL) << "Unexpected case";
buzbee67bf8852011-08-17 17:51:35 -07001482 return true;
1483 }
buzbee54330722011-08-23 16:46:55 -07001484 oatFlushAllRegs(cUnit); /* Send everything to home location */
1485 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1486 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1487 loadValueDirect(cUnit, rlShift, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -07001488 callRuntimeHelper(cUnit, rLR);
buzbee54330722011-08-23 16:46:55 -07001489 RegLocation rlResult = oatGetReturnWide(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001490 storeValueWide(cUnit, rlDest, rlResult);
1491 return false;
1492}
1493
buzbeeed3e9302011-09-23 17:34:19 -07001494STATIC bool genArithOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001495 RegLocation rlDest, RegLocation rlSrc1,
1496 RegLocation rlSrc2)
1497{
1498 RegLocation rlResult;
1499 OpKind firstOp = kOpBkpt;
1500 OpKind secondOp = kOpBkpt;
1501 bool callOut = false;
buzbee58f92742011-10-01 11:22:17 -07001502 bool checkZero = false;
buzbee67bf8852011-08-17 17:51:35 -07001503 int funcOffset;
1504 int retReg = r0;
1505
1506 switch (mir->dalvikInsn.opcode) {
1507 case OP_NOT_LONG:
1508 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1509 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001510 // Check for destructive overlap
1511 if (rlResult.lowReg == rlSrc2.highReg) {
1512 int tReg = oatAllocTemp(cUnit);
1513 genRegCopy(cUnit, tReg, rlSrc2.highReg);
1514 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1515 opRegReg(cUnit, kOpMvn, rlResult.highReg, tReg);
1516 oatFreeTemp(cUnit, tReg);
1517 } else {
1518 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1519 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
1520 }
buzbee67bf8852011-08-17 17:51:35 -07001521 storeValueWide(cUnit, rlDest, rlResult);
1522 return false;
1523 break;
1524 case OP_ADD_LONG:
1525 case OP_ADD_LONG_2ADDR:
1526 firstOp = kOpAdd;
1527 secondOp = kOpAdc;
1528 break;
1529 case OP_SUB_LONG:
1530 case OP_SUB_LONG_2ADDR:
1531 firstOp = kOpSub;
1532 secondOp = kOpSbc;
1533 break;
1534 case OP_MUL_LONG:
1535 case OP_MUL_LONG_2ADDR:
buzbee439c4fa2011-08-27 15:59:07 -07001536 callOut = true;
1537 retReg = r0;
1538 funcOffset = OFFSETOF_MEMBER(Thread, pLmul);
1539 break;
buzbee67bf8852011-08-17 17:51:35 -07001540 case OP_DIV_LONG:
1541 case OP_DIV_LONG_2ADDR:
1542 callOut = true;
buzbee58f92742011-10-01 11:22:17 -07001543 checkZero = true;
buzbee67bf8852011-08-17 17:51:35 -07001544 retReg = r0;
1545 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1546 break;
1547 /* NOTE - result is in r2/r3 instead of r0/r1 */
1548 case OP_REM_LONG:
1549 case OP_REM_LONG_2ADDR:
1550 callOut = true;
buzbee58f92742011-10-01 11:22:17 -07001551 checkZero = true;
buzbee67bf8852011-08-17 17:51:35 -07001552 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1553 retReg = r2;
1554 break;
1555 case OP_AND_LONG_2ADDR:
1556 case OP_AND_LONG:
1557 firstOp = kOpAnd;
1558 secondOp = kOpAnd;
1559 break;
1560 case OP_OR_LONG:
1561 case OP_OR_LONG_2ADDR:
1562 firstOp = kOpOr;
1563 secondOp = kOpOr;
1564 break;
1565 case OP_XOR_LONG:
1566 case OP_XOR_LONG_2ADDR:
1567 firstOp = kOpXor;
1568 secondOp = kOpXor;
1569 break;
1570 case OP_NEG_LONG: {
buzbee67bf8852011-08-17 17:51:35 -07001571 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1572 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001573 int zReg = oatAllocTemp(cUnit);
1574 loadConstantNoClobber(cUnit, zReg, 0);
1575 // Check for destructive overlap
1576 if (rlResult.lowReg == rlSrc2.highReg) {
1577 int tReg = oatAllocTemp(cUnit);
1578 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1579 zReg, rlSrc2.lowReg);
1580 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1581 zReg, tReg);
1582 oatFreeTemp(cUnit, tReg);
1583 } else {
1584 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1585 zReg, rlSrc2.lowReg);
1586 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1587 zReg, rlSrc2.highReg);
1588 }
1589 oatFreeTemp(cUnit, zReg);
buzbee67bf8852011-08-17 17:51:35 -07001590 storeValueWide(cUnit, rlDest, rlResult);
1591 return false;
1592 }
1593 default:
1594 LOG(FATAL) << "Invalid long arith op";
1595 }
1596 if (!callOut) {
1597 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
1598 } else {
buzbee67bf8852011-08-17 17:51:35 -07001599 oatFlushAllRegs(cUnit); /* Send everything to home location */
buzbee58f92742011-10-01 11:22:17 -07001600 if (checkZero) {
1601 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1602 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1603 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1604 int tReg = oatAllocTemp(cUnit);
1605 newLIR4(cUnit, kThumb2OrrRRRs, tReg, r2, r3, 0);
1606 oatFreeTemp(cUnit, tReg);
1607 genCheck(cUnit, kArmCondEq, mir, kArmThrowDivZero);
1608 } else {
1609 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1610 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1611 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1612 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001613 callRuntimeHelper(cUnit, rLR);
buzbee58f92742011-10-01 11:22:17 -07001614 // Adjust return regs in to handle case of rem returning r2/r3
buzbee67bf8852011-08-17 17:51:35 -07001615 if (retReg == r0)
1616 rlResult = oatGetReturnWide(cUnit);
1617 else
1618 rlResult = oatGetReturnWideAlt(cUnit);
1619 storeValueWide(cUnit, rlDest, rlResult);
1620 }
1621 return false;
1622}
1623
buzbeeed3e9302011-09-23 17:34:19 -07001624STATIC bool genArithOpInt(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001625 RegLocation rlDest, RegLocation rlSrc1,
1626 RegLocation rlSrc2)
1627{
1628 OpKind op = kOpBkpt;
1629 bool callOut = false;
1630 bool checkZero = false;
1631 bool unary = false;
1632 int retReg = r0;
1633 int funcOffset;
1634 RegLocation rlResult;
1635 bool shiftOp = false;
1636
1637 switch (mir->dalvikInsn.opcode) {
1638 case OP_NEG_INT:
1639 op = kOpNeg;
1640 unary = true;
1641 break;
1642 case OP_NOT_INT:
1643 op = kOpMvn;
1644 unary = true;
1645 break;
1646 case OP_ADD_INT:
1647 case OP_ADD_INT_2ADDR:
1648 op = kOpAdd;
1649 break;
1650 case OP_SUB_INT:
1651 case OP_SUB_INT_2ADDR:
1652 op = kOpSub;
1653 break;
1654 case OP_MUL_INT:
1655 case OP_MUL_INT_2ADDR:
1656 op = kOpMul;
1657 break;
1658 case OP_DIV_INT:
1659 case OP_DIV_INT_2ADDR:
1660 callOut = true;
1661 checkZero = true;
1662 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1663 retReg = r0;
1664 break;
1665 /* NOTE: returns in r1 */
1666 case OP_REM_INT:
1667 case OP_REM_INT_2ADDR:
1668 callOut = true;
1669 checkZero = true;
1670 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1671 retReg = r1;
1672 break;
1673 case OP_AND_INT:
1674 case OP_AND_INT_2ADDR:
1675 op = kOpAnd;
1676 break;
1677 case OP_OR_INT:
1678 case OP_OR_INT_2ADDR:
1679 op = kOpOr;
1680 break;
1681 case OP_XOR_INT:
1682 case OP_XOR_INT_2ADDR:
1683 op = kOpXor;
1684 break;
1685 case OP_SHL_INT:
1686 case OP_SHL_INT_2ADDR:
1687 shiftOp = true;
1688 op = kOpLsl;
1689 break;
1690 case OP_SHR_INT:
1691 case OP_SHR_INT_2ADDR:
1692 shiftOp = true;
1693 op = kOpAsr;
1694 break;
1695 case OP_USHR_INT:
1696 case OP_USHR_INT_2ADDR:
1697 shiftOp = true;
1698 op = kOpLsr;
1699 break;
1700 default:
1701 LOG(FATAL) << "Invalid word arith op: " <<
1702 (int)mir->dalvikInsn.opcode;
1703 }
1704 if (!callOut) {
1705 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
1706 if (unary) {
1707 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1708 opRegReg(cUnit, op, rlResult.lowReg,
1709 rlSrc1.lowReg);
1710 } else {
1711 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
1712 if (shiftOp) {
1713 int tReg = oatAllocTemp(cUnit);
1714 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
1715 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1716 opRegRegReg(cUnit, op, rlResult.lowReg,
1717 rlSrc1.lowReg, tReg);
1718 oatFreeTemp(cUnit, tReg);
1719 } else {
1720 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1721 opRegRegReg(cUnit, op, rlResult.lowReg,
1722 rlSrc1.lowReg, rlSrc2.lowReg);
1723 }
1724 }
1725 storeValue(cUnit, rlDest, rlResult);
1726 } else {
1727 RegLocation rlResult;
1728 oatFlushAllRegs(cUnit); /* Send everything to home location */
1729 loadValueDirectFixed(cUnit, rlSrc2, r1);
1730 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1731 loadValueDirectFixed(cUnit, rlSrc1, r0);
1732 if (checkZero) {
buzbee5ade1d22011-09-09 14:44:52 -07001733 genImmedCheck(cUnit, kArmCondEq, r1, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001734 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001735 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001736 if (retReg == r0)
1737 rlResult = oatGetReturn(cUnit);
1738 else
1739 rlResult = oatGetReturnAlt(cUnit);
1740 storeValue(cUnit, rlDest, rlResult);
1741 }
1742 return false;
1743}
1744
buzbeec1f45042011-09-21 16:03:19 -07001745/* Check if we need to check for pending suspend request */
buzbeeed3e9302011-09-23 17:34:19 -07001746STATIC void genSuspendTest(CompilationUnit* cUnit, MIR* mir)
buzbeec1f45042011-09-21 16:03:19 -07001747{
Ian Rogersa3760aa2011-11-14 14:32:37 -08001748 if (NO_SUSPEND || (mir->optimizationFlags & MIR_IGNORE_SUSPEND_CHECK)) {
buzbeec1f45042011-09-21 16:03:19 -07001749 return;
1750 }
buzbee6181f792011-09-29 11:14:04 -07001751 oatFlushAllRegs(cUnit);
buzbee44b412b2012-02-04 08:50:53 -08001752 ArmLIR* branch;
1753 if (cUnit->genDebugger) {
1754 // If generating code for the debugger, always check for suspension
1755 branch = genUnconditionalBranch(cUnit, NULL);
1756 } else {
1757 // In non-debug case, only check periodically
1758 newLIR2(cUnit, kThumbSubRI8, rSUSPEND, 1);
1759 branch = opCondBranch(cUnit, kArmCondEq);
1760 }
buzbeec1f45042011-09-21 16:03:19 -07001761 ArmLIR* retLab = newLIR0(cUnit, kArmPseudoTargetLabel);
1762 retLab->defMask = ENCODE_ALL;
buzbeeba938cb2012-02-03 14:47:55 -08001763 ArmLIR* target = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbeec1f45042011-09-21 16:03:19 -07001764 target->generic.dalvikOffset = cUnit->currentDalvikOffset;
1765 target->opcode = kArmPseudoSuspendTarget;
1766 target->operands[0] = (intptr_t)retLab;
1767 target->operands[1] = mir->offset;
1768 branch->generic.target = (LIR*)target;
buzbeeba938cb2012-02-03 14:47:55 -08001769 oatInsertGrowableList(cUnit, &cUnit->suspendLaunchpads, (intptr_t)target);
buzbeec1f45042011-09-21 16:03:19 -07001770}
1771
buzbee67bf8852011-08-17 17:51:35 -07001772/*
1773 * The following are the first-level codegen routines that analyze the format
1774 * of each bytecode then either dispatch special purpose codegen routines
1775 * or produce corresponding Thumb instructions directly.
1776 */
1777
buzbeeed3e9302011-09-23 17:34:19 -07001778STATIC bool isPowerOfTwo(int x)
buzbee67bf8852011-08-17 17:51:35 -07001779{
1780 return (x & (x - 1)) == 0;
1781}
1782
1783// Returns true if no more than two bits are set in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001784STATIC bool isPopCountLE2(unsigned int x)
buzbee67bf8852011-08-17 17:51:35 -07001785{
1786 x &= x - 1;
1787 return (x & (x - 1)) == 0;
1788}
1789
1790// Returns the index of the lowest set bit in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001791STATIC int lowestSetBit(unsigned int x) {
buzbee67bf8852011-08-17 17:51:35 -07001792 int bit_posn = 0;
1793 while ((x & 0xf) == 0) {
1794 bit_posn += 4;
1795 x >>= 4;
1796 }
1797 while ((x & 1) == 0) {
1798 bit_posn++;
1799 x >>= 1;
1800 }
1801 return bit_posn;
1802}
1803
1804// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
1805// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001806STATIC bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode,
buzbee67bf8852011-08-17 17:51:35 -07001807 RegLocation rlSrc, RegLocation rlDest, int lit)
1808{
1809 if (lit < 2 || !isPowerOfTwo(lit)) {
1810 return false;
1811 }
1812 int k = lowestSetBit(lit);
1813 if (k >= 30) {
1814 // Avoid special cases.
1815 return false;
1816 }
1817 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 ||
1818 dalvikOpcode == OP_DIV_INT_LIT16);
1819 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1820 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1821 if (div) {
1822 int tReg = oatAllocTemp(cUnit);
1823 if (lit == 2) {
1824 // Division by 2 is by far the most common division by constant.
1825 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
1826 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1827 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1828 } else {
1829 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
1830 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
1831 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1832 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1833 }
1834 } else {
1835 int cReg = oatAllocTemp(cUnit);
1836 loadConstant(cUnit, cReg, lit - 1);
1837 int tReg1 = oatAllocTemp(cUnit);
1838 int tReg2 = oatAllocTemp(cUnit);
1839 if (lit == 2) {
1840 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
1841 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1842 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1843 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1844 } else {
1845 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
1846 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
1847 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1848 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1849 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1850 }
1851 }
1852 storeValue(cUnit, rlDest, rlResult);
1853 return true;
1854}
1855
1856// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
1857// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001858STATIC bool handleEasyMultiply(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001859 RegLocation rlSrc, RegLocation rlDest, int lit)
1860{
1861 // Can we simplify this multiplication?
1862 bool powerOfTwo = false;
1863 bool popCountLE2 = false;
1864 bool powerOfTwoMinusOne = false;
1865 if (lit < 2) {
1866 // Avoid special cases.
1867 return false;
1868 } else if (isPowerOfTwo(lit)) {
1869 powerOfTwo = true;
1870 } else if (isPopCountLE2(lit)) {
1871 popCountLE2 = true;
1872 } else if (isPowerOfTwo(lit + 1)) {
1873 powerOfTwoMinusOne = true;
1874 } else {
1875 return false;
1876 }
1877 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1878 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1879 if (powerOfTwo) {
1880 // Shift.
1881 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
1882 lowestSetBit(lit));
1883 } else if (popCountLE2) {
1884 // Shift and add and shift.
1885 int firstBit = lowestSetBit(lit);
1886 int secondBit = lowestSetBit(lit ^ (1 << firstBit));
1887 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
1888 firstBit, secondBit);
1889 } else {
1890 // Reverse subtract: (src << (shift + 1)) - src.
buzbeeed3e9302011-09-23 17:34:19 -07001891 DCHECK(powerOfTwoMinusOne);
buzbee5ade1d22011-09-09 14:44:52 -07001892 // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1)
buzbee67bf8852011-08-17 17:51:35 -07001893 int tReg = oatAllocTemp(cUnit);
1894 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
1895 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
1896 }
1897 storeValue(cUnit, rlDest, rlResult);
1898 return true;
1899}
1900
buzbeeed3e9302011-09-23 17:34:19 -07001901STATIC bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001902 RegLocation rlDest, RegLocation rlSrc,
1903 int lit)
1904{
1905 Opcode dalvikOpcode = mir->dalvikInsn.opcode;
1906 RegLocation rlResult;
1907 OpKind op = (OpKind)0; /* Make gcc happy */
1908 int shiftOp = false;
1909 bool isDiv = false;
1910 int funcOffset;
1911
1912 switch (dalvikOpcode) {
1913 case OP_RSUB_INT_LIT8:
1914 case OP_RSUB_INT: {
1915 int tReg;
1916 //TUNING: add support for use of Arm rsub op
1917 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1918 tReg = oatAllocTemp(cUnit);
1919 loadConstant(cUnit, tReg, lit);
1920 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1921 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1922 tReg, rlSrc.lowReg);
1923 storeValue(cUnit, rlDest, rlResult);
1924 return false;
1925 break;
1926 }
1927
1928 case OP_ADD_INT_LIT8:
1929 case OP_ADD_INT_LIT16:
1930 op = kOpAdd;
1931 break;
1932 case OP_MUL_INT_LIT8:
1933 case OP_MUL_INT_LIT16: {
1934 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
1935 return false;
1936 }
1937 op = kOpMul;
1938 break;
1939 }
1940 case OP_AND_INT_LIT8:
1941 case OP_AND_INT_LIT16:
1942 op = kOpAnd;
1943 break;
1944 case OP_OR_INT_LIT8:
1945 case OP_OR_INT_LIT16:
1946 op = kOpOr;
1947 break;
1948 case OP_XOR_INT_LIT8:
1949 case OP_XOR_INT_LIT16:
1950 op = kOpXor;
1951 break;
1952 case OP_SHL_INT_LIT8:
1953 lit &= 31;
1954 shiftOp = true;
1955 op = kOpLsl;
1956 break;
1957 case OP_SHR_INT_LIT8:
1958 lit &= 31;
1959 shiftOp = true;
1960 op = kOpAsr;
1961 break;
1962 case OP_USHR_INT_LIT8:
1963 lit &= 31;
1964 shiftOp = true;
1965 op = kOpLsr;
1966 break;
1967
1968 case OP_DIV_INT_LIT8:
1969 case OP_DIV_INT_LIT16:
1970 case OP_REM_INT_LIT8:
1971 case OP_REM_INT_LIT16:
1972 if (lit == 0) {
buzbee5ade1d22011-09-09 14:44:52 -07001973 genImmedCheck(cUnit, kArmCondAl, 0, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001974 return false;
1975 }
1976 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
1977 return false;
1978 }
1979 oatFlushAllRegs(cUnit); /* Everything to home location */
1980 loadValueDirectFixed(cUnit, rlSrc, r0);
1981 oatClobber(cUnit, r0);
1982 if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
1983 (dalvikOpcode == OP_DIV_INT_LIT16)) {
1984 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1985 isDiv = true;
1986 } else {
1987 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1988 isDiv = false;
1989 }
1990 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1991 loadConstant(cUnit, r1, lit);
Ian Rogersff1ed472011-09-20 13:46:24 -07001992 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001993 if (isDiv)
1994 rlResult = oatGetReturn(cUnit);
1995 else
1996 rlResult = oatGetReturnAlt(cUnit);
1997 storeValue(cUnit, rlDest, rlResult);
1998 return false;
1999 break;
2000 default:
2001 return true;
2002 }
2003 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
2004 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
2005 // Avoid shifts by literal 0 - no support in Thumb. Change to copy
2006 if (shiftOp && (lit == 0)) {
2007 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
2008 } else {
2009 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
2010 }
2011 storeValue(cUnit, rlDest, rlResult);
2012 return false;
2013}
2014
2015/* Architectural-specific debugging helpers go here */
2016void oatArchDump(void)
2017{
2018 /* Print compiled opcode in this VM instance */
2019 int i, start, streak;
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002020 std::string buf;
buzbee67bf8852011-08-17 17:51:35 -07002021
2022 streak = i = 0;
buzbee67bf8852011-08-17 17:51:35 -07002023 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2024 i++;
2025 }
2026 if (i == kNumPackedOpcodes) {
2027 return;
2028 }
2029 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
2030 if (opcodeCoverage[i]) {
2031 streak++;
2032 } else {
2033 if (streak == 1) {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002034 StringAppendF(&buf, "%x,", start);
buzbee67bf8852011-08-17 17:51:35 -07002035 } else {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002036 StringAppendF(&buf, "%x-%x,", start, start + streak - 1);
buzbee67bf8852011-08-17 17:51:35 -07002037 }
2038 streak = 0;
2039 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2040 i++;
2041 }
2042 if (i < kNumPackedOpcodes) {
2043 streak = 1;
2044 start = i;
2045 }
2046 }
2047 }
2048 if (streak) {
2049 if (streak == 1) {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002050 StringAppendF(&buf, "%x", start);
buzbee67bf8852011-08-17 17:51:35 -07002051 } else {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002052 StringAppendF(&buf, "%x-%x", start, start + streak - 1);
buzbee67bf8852011-08-17 17:51:35 -07002053 }
2054 }
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002055 if (!buf.empty()) {
buzbee67bf8852011-08-17 17:51:35 -07002056 LOG(INFO) << "dalvik.vm.oat.op = " << buf;
2057 }
2058}
Elliott Hughes11d1b0c2012-01-23 16:57:47 -08002059
2060} // namespace art