blob: 5a9750a3ac54c12b199e40977f51247bac480012 [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
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080025namespace art {
26
buzbeee3acd072012-02-25 17:03:10 -080027/*
28 * Return most flexible allowed register class based on size.
29 * Bug: 2813841
30 * Must use a core register for data types narrower than word (due
31 * to possible unaligned load/store.
32 */
33STATIC inline RegisterClass oatRegClassBySize(OpSize size)
34{
35 return (size == kUnsignedHalf ||
36 size == kSignedHalf ||
37 size == kUnsignedByte ||
38 size == kSignedByte ) ? kCoreReg : kAnyReg;
39}
40
buzbeece302932011-10-04 14:32:18 -070041STATIC RegLocation getRetLoc(CompilationUnit* cUnit);
buzbee34cd9e52011-09-08 14:31:52 -070042
Elliott Hughes81bc5092011-09-30 17:25:59 -070043void warnIfUnresolved(CompilationUnit* cUnit, int fieldIdx, Field* field) {
44 if (field == NULL) {
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080045 const DexFile::FieldId& field_id = cUnit->dex_file->GetFieldId(fieldIdx);
Elliott Hughes95572412011-12-13 18:14:20 -080046 std::string class_name(cUnit->dex_file->GetFieldDeclaringClassDescriptor(field_id));
47 std::string field_name(cUnit->dex_file->GetFieldName(field_id));
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080048 LOG(INFO) << "Field " << PrettyDescriptor(class_name) << "." << field_name
Elliott Hughes95572412011-12-13 18:14:20 -080049 << " unresolved at compile time";
Elliott Hughes81bc5092011-09-30 17:25:59 -070050 } else {
51 // We also use the slow path for wide volatile fields.
52 }
53}
54
buzbee67bf8852011-08-17 17:51:35 -070055/*
56 * Construct an s4 from two consecutive half-words of switch data.
57 * This needs to check endianness because the DEX optimizer only swaps
58 * half-words in instruction stream.
59 *
60 * "switchData" must be 32-bit aligned.
61 */
62#if __BYTE_ORDER == __LITTLE_ENDIAN
buzbeeed3e9302011-09-23 17:34:19 -070063STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070064 return *(s4*) switchData;
65}
66#else
buzbeeed3e9302011-09-23 17:34:19 -070067STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070068 u2* data = switchData;
69 return data[0] | (((s4) data[1]) << 16);
70}
71#endif
72
73/*
74 * Generate a Thumb2 IT instruction, which can nullify up to
75 * four subsequent instructions based on a condition and its
76 * inverse. The condition applies to the first instruction, which
77 * is executed if the condition is met. The string "guide" consists
78 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
79 * A "T" means the instruction is executed if the condition is
80 * met, and an "E" means the instruction is executed if the condition
81 * is not met.
82 */
buzbeeed3e9302011-09-23 17:34:19 -070083STATIC ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code,
buzbee67bf8852011-08-17 17:51:35 -070084 const char* guide)
85{
86 int mask;
87 int condBit = code & 1;
88 int altBit = condBit ^ 1;
89 int mask3 = 0;
90 int mask2 = 0;
91 int mask1 = 0;
92
93 //Note: case fallthroughs intentional
94 switch(strlen(guide)) {
95 case 3:
96 mask1 = (guide[2] == 'T') ? condBit : altBit;
97 case 2:
98 mask2 = (guide[1] == 'T') ? condBit : altBit;
99 case 1:
100 mask3 = (guide[0] == 'T') ? condBit : altBit;
101 break;
102 case 0:
103 break;
104 default:
105 LOG(FATAL) << "OAT: bad case in genIT";
106 }
107 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
108 (1 << (3 - strlen(guide)));
109 return newLIR2(cUnit, kThumb2It, code, mask);
110}
111
112/*
113 * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik
114 * offset vaddr. This label will be used to fix up the case
115 * branch table during the assembly phase. Be sure to set
116 * all resource flags on this to prevent code motion across
117 * target boundaries. KeyVal is just there for debugging.
118 */
buzbeeed3e9302011-09-23 17:34:19 -0700119STATIC ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
buzbee67bf8852011-08-17 17:51:35 -0700120{
buzbee85d8c1e2012-01-27 15:52:35 -0800121 std::map<unsigned int, LIR*>::iterator it;
122 it = cUnit->boundaryMap.find(vaddr);
123 if (it == cUnit->boundaryMap.end()) {
124 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
buzbee67bf8852011-08-17 17:51:35 -0700125 }
buzbeeba938cb2012-02-03 14:47:55 -0800126 ArmLIR* newLabel = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbee85d8c1e2012-01-27 15:52:35 -0800127 newLabel->generic.dalvikOffset = vaddr;
128 newLabel->opcode = kArmPseudoCaseLabel;
129 newLabel->operands[0] = keyVal;
130 oatInsertLIRAfter(it->second, (LIR*)newLabel);
131 return newLabel;
buzbee67bf8852011-08-17 17:51:35 -0700132}
133
buzbeeed3e9302011-09-23 17:34:19 -0700134STATIC void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700135{
136 const u2* table = tabRec->table;
137 int baseVaddr = tabRec->vaddr;
138 int *targets = (int*)&table[4];
139 int entries = table[1];
140 int lowKey = s4FromSwitchData(&table[2]);
141 for (int i = 0; i < entries; i++) {
142 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
143 i + lowKey);
144 }
145}
146
buzbeeed3e9302011-09-23 17:34:19 -0700147STATIC void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700148{
149 const u2* table = tabRec->table;
150 int baseVaddr = tabRec->vaddr;
151 int entries = table[1];
152 int* keys = (int*)&table[2];
153 int* targets = &keys[entries];
154 for (int i = 0; i < entries; i++) {
155 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
156 keys[i]);
157 }
158}
159
160void oatProcessSwitchTables(CompilationUnit* cUnit)
161{
162 GrowableListIterator iterator;
163 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
164 while (true) {
165 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
166 &iterator);
167 if (tabRec == NULL) break;
168 if (tabRec->table[0] == kPackedSwitchSignature)
169 markPackedCaseLabels(cUnit, tabRec);
170 else if (tabRec->table[0] == kSparseSwitchSignature)
171 markSparseCaseLabels(cUnit, tabRec);
172 else {
173 LOG(FATAL) << "Invalid switch table";
174 }
175 }
176}
177
buzbeeed3e9302011-09-23 17:34:19 -0700178STATIC void dumpSparseSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700179 /*
180 * Sparse switch data format:
181 * ushort ident = 0x0200 magic value
182 * ushort size number of entries in the table; > 0
183 * int keys[size] keys, sorted low-to-high; 32-bit aligned
184 * int targets[size] branch targets, relative to switch opcode
185 *
186 * Total size is (2+size*4) 16-bit code units.
187 */
188{
189 u2 ident = table[0];
190 int entries = table[1];
191 int* keys = (int*)&table[2];
192 int* targets = &keys[entries];
193 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
194 ", entries: " << std::dec << entries;
195 for (int i = 0; i < entries; i++) {
196 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
197 targets[i];
198 }
199}
200
buzbeeed3e9302011-09-23 17:34:19 -0700201STATIC void dumpPackedSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700202 /*
203 * Packed switch data format:
204 * ushort ident = 0x0100 magic value
205 * ushort size number of entries in the table
206 * int first_key first (and lowest) switch case value
207 * int targets[size] branch targets, relative to switch opcode
208 *
209 * Total size is (4+size*2) 16-bit code units.
210 */
211{
212 u2 ident = table[0];
213 int* targets = (int*)&table[4];
214 int entries = table[1];
215 int lowKey = s4FromSwitchData(&table[2]);
216 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
217 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
218 for (int i = 0; i < entries; i++) {
219 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
220 targets[i];
221 }
222}
223
224/*
225 * The sparse table in the literal pool is an array of <key,displacement>
226 * pairs. For each set, we'll load them as a pair using ldmia.
227 * This means that the register number of the temp we use for the key
228 * must be lower than the reg for the displacement.
229 *
230 * The test loop will look something like:
231 *
232 * adr rBase, <table>
233 * ldr rVal, [rSP, vRegOff]
234 * mov rIdx, #tableSize
235 * lp:
236 * ldmia rBase!, {rKey, rDisp}
237 * sub rIdx, #1
238 * cmp rVal, rKey
239 * ifeq
240 * add rPC, rDisp ; This is the branch from which we compute displacement
241 * cbnz rIdx, lp
242 */
buzbeeed3e9302011-09-23 17:34:19 -0700243STATIC void genSparseSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700244 RegLocation rlSrc)
245{
246 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
247 if (cUnit->printMe) {
248 dumpSparseSwitchTable(table);
249 }
250 // Add the table to the list - we'll process it later
buzbeeba938cb2012-02-03 14:47:55 -0800251 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
buzbee5abfa3e2012-01-31 17:01:43 -0800252 true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700253 tabRec->table = table;
254 tabRec->vaddr = mir->offset;
255 int size = table[1];
buzbeeba938cb2012-02-03 14:47:55 -0800256 tabRec->targets = (ArmLIR* *)oatNew(cUnit, size * sizeof(ArmLIR*), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800257 kAllocLIR);
buzbeeba938cb2012-02-03 14:47:55 -0800258 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700259
260 // Get the switch value
261 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
262 int rBase = oatAllocTemp(cUnit);
263 /* Allocate key and disp temps */
264 int rKey = oatAllocTemp(cUnit);
265 int rDisp = oatAllocTemp(cUnit);
266 // Make sure rKey's register number is less than rDisp's number for ldmia
267 if (rKey > rDisp) {
268 int tmp = rDisp;
269 rDisp = rKey;
270 rKey = tmp;
271 }
272 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700273 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700274 // Set up rIdx
275 int rIdx = oatAllocTemp(cUnit);
276 loadConstant(cUnit, rIdx, size);
277 // Establish loop branch target
278 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
279 target->defMask = ENCODE_ALL;
280 // Load next key/disp
281 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
282 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
283 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
284 genIT(cUnit, kArmCondEq, "");
285 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
286 tabRec->bxInst = switchBranch;
287 // Needs to use setflags encoding here
288 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
289 ArmLIR* branch = opCondBranch(cUnit, kArmCondNe);
290 branch->generic.target = (LIR*)target;
291}
292
293
buzbeeed3e9302011-09-23 17:34:19 -0700294STATIC void genPackedSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700295 RegLocation rlSrc)
296{
297 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
298 if (cUnit->printMe) {
299 dumpPackedSwitchTable(table);
300 }
301 // Add the table to the list - we'll process it later
buzbeeba938cb2012-02-03 14:47:55 -0800302 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
buzbee5abfa3e2012-01-31 17:01:43 -0800303 true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700304 tabRec->table = table;
305 tabRec->vaddr = mir->offset;
306 int size = table[1];
buzbeeba938cb2012-02-03 14:47:55 -0800307 tabRec->targets = (ArmLIR* *)oatNew(cUnit, size * sizeof(ArmLIR*), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800308 kAllocLIR);
buzbeeba938cb2012-02-03 14:47:55 -0800309 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700310
311 // Get the switch value
312 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
313 int tableBase = oatAllocTemp(cUnit);
314 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700315 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700316 int lowKey = s4FromSwitchData(&table[2]);
317 int keyReg;
318 // Remove the bias, if necessary
319 if (lowKey == 0) {
320 keyReg = rlSrc.lowReg;
321 } else {
322 keyReg = oatAllocTemp(cUnit);
323 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
324 }
325 // Bounds check - if < 0 or >= size continue following switch
326 opRegImm(cUnit, kOpCmp, keyReg, size-1);
327 ArmLIR* branchOver = opCondBranch(cUnit, kArmCondHi);
328
329 // Load the displacement from the switch table
330 int dispReg = oatAllocTemp(cUnit);
331 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
332
333 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
334 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
335 tabRec->bxInst = switchBranch;
336
337 /* branchOver target here */
338 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
339 target->defMask = ENCODE_ALL;
340 branchOver->generic.target = (LIR*)target;
341}
342
343/*
344 * Array data table format:
345 * ushort ident = 0x0300 magic value
346 * ushort width width of each element in the table
347 * uint size number of elements in the table
348 * ubyte data[size*width] table of data values (may contain a single-byte
349 * padding at the end)
350 *
351 * Total size is 4+(width * size + 1)/2 16-bit code units.
352 */
buzbeeed3e9302011-09-23 17:34:19 -0700353STATIC void genFillArrayData(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700354 RegLocation rlSrc)
355{
356 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
357 // Add the table to the list - we'll process it later
358 FillArrayData *tabRec = (FillArrayData *)
buzbeeba938cb2012-02-03 14:47:55 -0800359 oatNew(cUnit, sizeof(FillArrayData), true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700360 tabRec->table = table;
361 tabRec->vaddr = mir->offset;
362 u2 width = tabRec->table[1];
363 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
364 tabRec->size = (size * width) + 8;
365
buzbeeba938cb2012-02-03 14:47:55 -0800366 oatInsertGrowableList(cUnit, &cUnit->fillArrayData, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700367
368 // Making a call - use explicit registers
369 oatFlushAllRegs(cUnit); /* Everything to home location */
370 loadValueDirectFixed(cUnit, rlSrc, r0);
371 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700372 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
buzbeee6d61962011-08-27 11:58:19 -0700373 // Materialize a pointer to the fill data image
buzbee03fa2632011-09-20 17:10:57 -0700374 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
Ian Rogersff1ed472011-09-20 13:46:24 -0700375 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700376}
377
buzbeeed3e9302011-09-23 17:34:19 -0700378STATIC void genIGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
Ian Rogers1bddec32012-02-04 12:27:34 -0800379 RegLocation rlDest, RegLocation rlObj,
380 bool isLongOrDouble, bool isObject)
buzbee67bf8852011-08-17 17:51:35 -0700381{
Ian Rogers1bddec32012-02-04 12:27:34 -0800382 int fieldOffset;
383 bool isVolatile;
384 uint32_t fieldIdx = mir->dalvikInsn.vC;
385 bool fastPath =
386 cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, cUnit,
jeffhao8cd6dda2012-02-22 10:15:34 -0800387 fieldOffset, isVolatile, false);
Ian Rogers1bddec32012-02-04 12:27:34 -0800388 if (fastPath && !SLOW_FIELD_PATH) {
389 RegLocation rlResult;
390 RegisterClass regClass = oatRegClassBySize(size);
391 DCHECK_GE(fieldOffset, 0);
buzbee34cd9e52011-09-08 14:31:52 -0700392 rlObj = loadValue(cUnit, rlObj, kCoreReg);
Ian Rogers1bddec32012-02-04 12:27:34 -0800393 if (isLongOrDouble) {
394 DCHECK(rlDest.wide);
395 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
396 int regPtr = oatAllocTemp(cUnit);
397 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
398 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
399 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
400 if (isVolatile) {
401 oatGenMemBarrier(cUnit, kSY);
402 }
403 oatFreeTemp(cUnit, regPtr);
404 storeValueWide(cUnit, rlDest, rlResult);
405 } else {
406 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
407 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
408 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
409 kWord, rlObj.sRegLow);
410 if (isVolatile) {
411 oatGenMemBarrier(cUnit, kSY);
412 }
413 storeValue(cUnit, rlDest, rlResult);
buzbee34cd9e52011-09-08 14:31:52 -0700414 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800415 } else {
416 int getterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pGet64Instance) :
417 (isObject ? OFFSETOF_MEMBER(Thread, pGetObjInstance)
418 : OFFSETOF_MEMBER(Thread, pGet32Instance));
419 loadWordDisp(cUnit, rSELF, getterOffset, rLR);
420 loadValueDirect(cUnit, rlObj, r1);
421 loadConstant(cUnit, r0, fieldIdx);
422 callRuntimeHelper(cUnit, rLR);
423 if (isLongOrDouble) {
424 RegLocation rlResult = oatGetReturnWide(cUnit);
425 storeValueWide(cUnit, rlDest, rlResult);
426 } else {
427 RegLocation rlResult = oatGetReturn(cUnit);
428 storeValue(cUnit, rlDest, rlResult);
429 }
buzbee67bf8852011-08-17 17:51:35 -0700430 }
buzbee67bf8852011-08-17 17:51:35 -0700431}
432
buzbeeed3e9302011-09-23 17:34:19 -0700433STATIC void genIPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
Ian Rogers1bddec32012-02-04 12:27:34 -0800434 RegLocation rlSrc, RegLocation rlObj,
435 bool isLongOrDouble, bool isObject)
buzbee67bf8852011-08-17 17:51:35 -0700436{
Ian Rogers1bddec32012-02-04 12:27:34 -0800437 int fieldOffset;
438 bool isVolatile;
439 uint32_t fieldIdx = mir->dalvikInsn.vC;
440 bool fastPath =
441 cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, cUnit,
jeffhao8cd6dda2012-02-22 10:15:34 -0800442 fieldOffset, isVolatile, true);
Ian Rogers1bddec32012-02-04 12:27:34 -0800443 if (fastPath && !SLOW_FIELD_PATH) {
444 RegisterClass regClass = oatRegClassBySize(size);
445 DCHECK_GE(fieldOffset, 0);
buzbee34cd9e52011-09-08 14:31:52 -0700446 rlObj = loadValue(cUnit, rlObj, kCoreReg);
Ian Rogers1bddec32012-02-04 12:27:34 -0800447 if (isLongOrDouble) {
448 int regPtr;
449 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
450 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
451 regPtr = oatAllocTemp(cUnit);
452 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
453 if (isVolatile) {
454 oatGenMemBarrier(cUnit, kST);
455 }
456 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
457 if (isVolatile) {
458 oatGenMemBarrier(cUnit, kSY);
459 }
460 oatFreeTemp(cUnit, regPtr);
461 } else {
462 rlSrc = loadValue(cUnit, rlSrc, regClass);
463 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
464 if (isVolatile) {
465 oatGenMemBarrier(cUnit, kST);
466 }
467 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, kWord);
468 if (isVolatile) {
469 oatGenMemBarrier(cUnit, kSY);
470 }
buzbee34cd9e52011-09-08 14:31:52 -0700471 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800472 } else {
473 int setterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pSet64Instance) :
474 (isObject ? OFFSETOF_MEMBER(Thread, pSetObjInstance)
475 : OFFSETOF_MEMBER(Thread, pSet32Instance));
476 loadWordDisp(cUnit, rSELF, setterOffset, rLR);
477 loadValueDirect(cUnit, rlObj, r1);
478 if (isLongOrDouble) {
479 loadValueDirectWide(cUnit, rlSrc, r2, r3);
480 } else {
481 loadValueDirect(cUnit, rlSrc, r2);
buzbee12246b82011-09-29 14:15:05 -0700482 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800483 loadConstant(cUnit, r0, fieldIdx);
484 callRuntimeHelper(cUnit, rLR);
buzbee34cd9e52011-09-08 14:31:52 -0700485 }
buzbee67bf8852011-08-17 17:51:35 -0700486}
487
buzbeeed3e9302011-09-23 17:34:19 -0700488STATIC void genConstClass(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700489 RegLocation rlDest, RegLocation rlSrc)
490{
Ian Rogers28ad40d2011-10-27 15:19:26 -0700491 uint32_t type_idx = mir->dalvikInsn.vB;
buzbee1b4c8592011-08-31 10:43:51 -0700492 int mReg = loadCurrMethod(cUnit);
493 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700494 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800495 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
496 cUnit->dex_cache,
497 *cUnit->dex_file,
498 type_idx)) {
499 // Call out to helper which resolves type and verifies access.
500 // Resolved type returned in r0.
501 loadWordDisp(cUnit, rSELF,
502 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
503 rLR);
504 genRegCopy(cUnit, r1, mReg);
505 loadConstant(cUnit, r0, type_idx);
506 callRuntimeHelper(cUnit, rLR);
507 RegLocation rlResult = oatGetReturn(cUnit);
508 storeValue(cUnit, rlDest, rlResult);
buzbee1b4c8592011-08-31 10:43:51 -0700509 } else {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700510 // We're don't need access checks, load type from dex cache
511 int32_t dex_cache_offset = Method::DexCacheResolvedTypesOffset().Int32Value();
512 loadWordDisp(cUnit, mReg, dex_cache_offset, resReg);
Ian Rogersa15e67d2012-02-28 13:51:55 -0800513 int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() +
514 (sizeof(Class*) * type_idx);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700515 loadWordDisp(cUnit, resReg, offset_of_type, rlResult.lowReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800516 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache,
517 type_idx) ||
Ian Rogers28ad40d2011-10-27 15:19:26 -0700518 SLOW_TYPE_PATH) {
519 // Slow path, at runtime test if the type is null and if so initialize
520 oatFlushAllRegs(cUnit);
521 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg, 0);
522 // Resolved, store and hop over following code
523 storeValue(cUnit, rlDest, rlResult);
524 ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
525 // TUNING: move slow path to end & remove unconditional branch
526 ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
527 target1->defMask = ENCODE_ALL;
528 // Call out to helper, which will return resolved type in r0
529 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
530 genRegCopy(cUnit, r1, mReg);
531 loadConstant(cUnit, r0, type_idx);
532 callRuntimeHelper(cUnit, rLR);
533 RegLocation rlResult = oatGetReturn(cUnit);
534 storeValue(cUnit, rlDest, rlResult);
535 // Rejoin code paths
536 ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
537 target2->defMask = ENCODE_ALL;
538 branch1->generic.target = (LIR*)target1;
539 branch2->generic.target = (LIR*)target2;
540 } else {
541 // Fast path, we're done - just store result
542 storeValue(cUnit, rlDest, rlResult);
543 }
buzbee1b4c8592011-08-31 10:43:51 -0700544 }
buzbee67bf8852011-08-17 17:51:35 -0700545}
546
buzbee44b412b2012-02-04 08:50:53 -0800547/*
548 * Generate callout to updateDebugger. Note: genIT will automatically
549 * create a scheduling barrier, which we need to prevent code motion that
550 * might confuse the debugger. Note: Return registers r0/r1 are
551 * handled specially during code generation following function calls.
552 * Typically, temp registers are not live between opcodes, but we keep
553 * r0/r1 live following invokes, where they are consumed by the immediately
554 * following op_move_result_xxx. Thus, we must preserve and restore r0/r1
555 * when making a call to update the debugger. This is handled by the stub.
556 */
557STATIC void genDebuggerUpdate(CompilationUnit* cUnit, int32_t offset)
558{
559 // Following DCHECK verifies that dPC is in range of single load immediate
560 DCHECK((offset == DEBUGGER_METHOD_ENTRY) ||
561 (offset == DEBUGGER_METHOD_EXIT) || ((offset & 0xffff) == offset));
562 oatClobberCalleeSave(cUnit);
563 opRegImm(cUnit, kOpCmp, rSUSPEND, 0);
564 genIT(cUnit, kArmCondNe, "T");
565 loadConstant(cUnit, r2, offset); // arg2 <- Entry code
566 opReg(cUnit, kOpBlx, rSUSPEND);
567 oatFreeTemp(cUnit, r2);
568}
569
buzbeeed3e9302011-09-23 17:34:19 -0700570STATIC void genConstString(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700571 RegLocation rlDest, RegLocation rlSrc)
572{
buzbeece302932011-10-04 14:32:18 -0700573 /* NOTE: Most strings should be available at compile time */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700574 uint32_t string_idx = mir->dalvikInsn.vB;
Ian Rogersa15e67d2012-02-28 13:51:55 -0800575 int32_t offset_of_string = Array::DataOffset(sizeof(String*)).Int32Value() +
576 (sizeof(String*) * string_idx);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800577 if (!cUnit->compiler->CanAssumeStringIsPresentInDexCache(cUnit->dex_cache, string_idx) ||
Ian Rogers28ad40d2011-10-27 15:19:26 -0700578 SLOW_STRING_PATH) {
579 // slow path, resolve string if not in dex cache
buzbeece302932011-10-04 14:32:18 -0700580 oatFlushAllRegs(cUnit);
581 oatLockCallTemps(cUnit); // Using explicit registers
582 loadCurrMethodDirect(cUnit, r2);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700583 loadWordDisp(cUnit, r2, Method::DexCacheStringsOffset().Int32Value(), r0);
buzbeece302932011-10-04 14:32:18 -0700584 // Might call out to helper, which will return resolved string in r0
Ian Rogers28ad40d2011-10-27 15:19:26 -0700585 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pResolveStringFromCode), rLR);
586 loadWordDisp(cUnit, r0, offset_of_string, r0);
587 loadConstant(cUnit, r1, string_idx);
buzbeece302932011-10-04 14:32:18 -0700588 opRegImm(cUnit, kOpCmp, r0, 0); // Is resolved?
589 genBarrier(cUnit);
590 // For testing, always force through helper
591 if (!EXERCISE_SLOWEST_STRING_PATH) {
592 genIT(cUnit, kArmCondEq, "T");
593 }
594 genRegCopy(cUnit, r0, r2); // .eq
595 opReg(cUnit, kOpBlx, rLR); // .eq, helper(Method*, string_idx)
596 genBarrier(cUnit);
597 storeValue(cUnit, rlDest, getRetLoc(cUnit));
598 } else {
599 int mReg = loadCurrMethod(cUnit);
600 int resReg = oatAllocTemp(cUnit);
601 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700602 loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(), resReg);
603 loadWordDisp(cUnit, resReg, offset_of_string, rlResult.lowReg);
buzbeece302932011-10-04 14:32:18 -0700604 storeValue(cUnit, rlDest, rlResult);
605 }
buzbee67bf8852011-08-17 17:51:35 -0700606}
607
buzbeedfd3d702011-08-28 12:56:51 -0700608/*
609 * Let helper function take care of everything. Will
610 * call Class::NewInstanceFromCode(type_idx, method);
611 */
buzbeeed3e9302011-09-23 17:34:19 -0700612STATIC void genNewInstance(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700613 RegLocation rlDest)
614{
buzbeedfd3d702011-08-28 12:56:51 -0700615 oatFlushAllRegs(cUnit); /* Everything to home location */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700616 uint32_t type_idx = mir->dalvikInsn.vB;
617 // alloc will always check for resolution, do we also need to verify access because the
618 // verifier was unable to?
Ian Rogersd4135902012-02-03 18:05:08 -0800619 if (cUnit->compiler->CanAccessInstantiableTypeWithoutChecks(cUnit->method_idx,
620 cUnit->dex_cache,
621 *cUnit->dex_file,
622 type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700623 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
624 } else {
625 loadWordDisp(cUnit, rSELF,
626 OFFSETOF_MEMBER(Thread, pAllocObjectFromCodeWithAccessCheck), rLR);
627 }
628 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
629 loadConstant(cUnit, r0, type_idx); // arg0 <- type_idx
Ian Rogersff1ed472011-09-20 13:46:24 -0700630 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700631 RegLocation rlResult = oatGetReturn(cUnit);
632 storeValue(cUnit, rlDest, rlResult);
633}
634
635void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
636{
buzbee6181f792011-09-29 11:14:04 -0700637 oatFlushAllRegs(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700638 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
Ian Rogersbdb03912011-09-14 00:55:44 -0700639 loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
Ian Rogersff1ed472011-09-20 13:46:24 -0700640 callRuntimeHelper(cUnit, rLR); // art_deliver_exception(exception);
buzbee67bf8852011-08-17 17:51:35 -0700641}
642
buzbeeed3e9302011-09-23 17:34:19 -0700643STATIC void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700644 RegLocation rlSrc)
645{
buzbee6181f792011-09-29 11:14:04 -0700646 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700647 // May generate a call - use explicit registers
648 oatLockCallTemps(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700649 uint32_t type_idx = mir->dalvikInsn.vC;
buzbee2a475e72011-09-07 17:19:17 -0700650 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
Ian Rogers28ad40d2011-10-27 15:19:26 -0700651 int classReg = r2; // r2 will hold the Class*
Ian Rogersa3760aa2011-11-14 14:32:37 -0800652 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
653 cUnit->dex_cache,
654 *cUnit->dex_file,
655 type_idx)) {
Ian Rogersb093c6b2011-10-31 16:19:55 -0700656 // Check we have access to type_idx and if not throw IllegalAccessError,
657 // returns Class* in r0
658 loadWordDisp(cUnit, rSELF,
659 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
660 rLR);
661 loadConstant(cUnit, r0, type_idx);
662 callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
663 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers6a996782011-10-31 17:06:39 -0700664 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
Ian Rogers28ad40d2011-10-27 15:19:26 -0700665 } else {
666 // Load dex cache entry into classReg (r2)
Ian Rogers6a996782011-10-31 17:06:39 -0700667 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
Ian Rogers28ad40d2011-10-27 15:19:26 -0700668 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
Ian Rogersa15e67d2012-02-28 13:51:55 -0800669 int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() +
670 (sizeof(Class*) * type_idx);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700671 loadWordDisp(cUnit, classReg, offset_of_type, classReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800672 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache, type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700673 // Need to test presence of type in dex cache at runtime
674 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
675 // Not resolved
676 // Call out to helper, which will return resolved type in r0
677 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
678 loadConstant(cUnit, r0, type_idx);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700679 callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
Ian Rogers28ad40d2011-10-27 15:19:26 -0700680 genRegCopy(cUnit, r2, r0); // Align usage with fast path
681 loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */
682 // Rejoin code paths
683 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
684 hopTarget->defMask = ENCODE_ALL;
685 hopBranch->generic.target = (LIR*)hopTarget;
686 }
buzbee67bf8852011-08-17 17:51:35 -0700687 }
buzbee991e3ac2011-09-29 15:44:22 -0700688 /* r0 is ref, r2 is class. If ref==null, use directly as bool result */
689 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
buzbee2a475e72011-09-07 17:19:17 -0700690 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700691 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee991e3ac2011-09-29 15:44:22 -0700692 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
693 /* r0 is ref, r1 is ref->clazz, r2 is class */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700694 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
buzbee991e3ac2011-09-29 15:44:22 -0700695 opRegReg(cUnit, kOpCmp, r1, r2); // Same?
696 genBarrier(cUnit);
697 genIT(cUnit, kArmCondEq, "EE"); // if-convert the test
698 loadConstant(cUnit, r0, 1); // .eq case - load true
699 genRegCopy(cUnit, r0, r2); // .ne case - arg0 <= class
700 opReg(cUnit, kOpBlx, rLR); // .ne case: helper(class, ref->class)
701 genBarrier(cUnit);
702 oatClobberCalleeSave(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700703 /* branch target here */
704 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
705 target->defMask = ENCODE_ALL;
buzbee2a475e72011-09-07 17:19:17 -0700706 RegLocation rlResult = oatGetReturn(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700707 storeValue(cUnit, rlDest, rlResult);
708 branch1->generic.target = (LIR*)target;
buzbee67bf8852011-08-17 17:51:35 -0700709}
710
buzbeeed3e9302011-09-23 17:34:19 -0700711STATIC void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbee67bf8852011-08-17 17:51:35 -0700712{
buzbee6181f792011-09-29 11:14:04 -0700713 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700714 // May generate a call - use explicit registers
715 oatLockCallTemps(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700716 uint32_t type_idx = mir->dalvikInsn.vB;
buzbee2a475e72011-09-07 17:19:17 -0700717 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
Ian Rogers28ad40d2011-10-27 15:19:26 -0700718 int classReg = r2; // r2 will hold the Class*
Ian Rogersa3760aa2011-11-14 14:32:37 -0800719 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
720 cUnit->dex_cache,
721 *cUnit->dex_file,
722 type_idx)) {
Ian Rogersb093c6b2011-10-31 16:19:55 -0700723 // Check we have access to type_idx and if not throw IllegalAccessError,
724 // returns Class* in r0
725 loadWordDisp(cUnit, rSELF,
726 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
727 rLR);
728 loadConstant(cUnit, r0, type_idx);
729 callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
730 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers28ad40d2011-10-27 15:19:26 -0700731 } else {
732 // Load dex cache entry into classReg (r2)
733 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
Ian Rogersa15e67d2012-02-28 13:51:55 -0800734 int32_t offset_of_type = Array::DataOffset(sizeof(Class*)).Int32Value() +
735 (sizeof(Class*) * type_idx);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700736 loadWordDisp(cUnit, classReg, offset_of_type, classReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800737 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache, type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700738 // Need to test presence of type in dex cache at runtime
739 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
740 // Not resolved
741 // Call out to helper, which will return resolved type in r0
742 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
743 loadConstant(cUnit, r0, type_idx);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700744 callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
745 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers28ad40d2011-10-27 15:19:26 -0700746 // Rejoin code paths
747 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
748 hopTarget->defMask = ENCODE_ALL;
749 hopBranch->generic.target = (LIR*)hopTarget;
750 }
buzbee67bf8852011-08-17 17:51:35 -0700751 }
Ian Rogers28ad40d2011-10-27 15:19:26 -0700752 // At this point, classReg (r2) has class
753 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
buzbee2a475e72011-09-07 17:19:17 -0700754 /* Null is OK - continue */
755 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
756 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700757 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee2a475e72011-09-07 17:19:17 -0700758 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
759 /* r1 now contains object->clazz */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700760 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700761 opRegReg(cUnit, kOpCmp, r1, classReg);
buzbee2a475e72011-09-07 17:19:17 -0700762 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
763 genRegCopy(cUnit, r0, r1);
764 genRegCopy(cUnit, r1, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -0700765 callRuntimeHelper(cUnit, rLR);
buzbee2a475e72011-09-07 17:19:17 -0700766 /* branch target here */
buzbee67bf8852011-08-17 17:51:35 -0700767 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
768 target->defMask = ENCODE_ALL;
769 branch1->generic.target = (LIR*)target;
770 branch2->generic.target = (LIR*)target;
771}
772
buzbeeed3e9302011-09-23 17:34:19 -0700773STATIC void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700774 RegLocation rlSrc)
775{
776 RegLocation rlResult;
777 rlSrc = loadValue(cUnit, rlSrc, kFPReg);
778 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
779 newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
780 storeValue(cUnit, rlDest, rlResult);
781}
782
buzbeeed3e9302011-09-23 17:34:19 -0700783STATIC void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700784 RegLocation rlSrc)
785{
786 RegLocation rlResult;
787 rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
788 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
789 newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
790 S2D(rlSrc.lowReg, rlSrc.highReg));
791 storeValueWide(cUnit, rlDest, rlResult);
792}
793
buzbeeed3e9302011-09-23 17:34:19 -0700794STATIC void freeRegLocTemps(CompilationUnit* cUnit, RegLocation rlKeep,
buzbee439c4fa2011-08-27 15:59:07 -0700795 RegLocation rlFree)
buzbee67bf8852011-08-17 17:51:35 -0700796{
buzbee6181f792011-09-29 11:14:04 -0700797 if ((rlFree.lowReg != rlKeep.lowReg) && (rlFree.lowReg != rlKeep.highReg) &&
798 (rlFree.highReg != rlKeep.lowReg) && (rlFree.highReg != rlKeep.highReg)) {
799 // No overlap, free both
buzbee439c4fa2011-08-27 15:59:07 -0700800 oatFreeTemp(cUnit, rlFree.lowReg);
buzbee6181f792011-09-29 11:14:04 -0700801 oatFreeTemp(cUnit, rlFree.highReg);
802 }
buzbee67bf8852011-08-17 17:51:35 -0700803}
804
buzbeeed3e9302011-09-23 17:34:19 -0700805STATIC void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp,
buzbee67bf8852011-08-17 17:51:35 -0700806 OpKind secondOp, RegLocation rlDest,
807 RegLocation rlSrc1, RegLocation rlSrc2)
808{
buzbee9e0f9b02011-08-24 15:32:46 -0700809 /*
810 * NOTE: This is the one place in the code in which we might have
811 * as many as six live temporary registers. There are 5 in the normal
812 * set for Arm. Until we have spill capabilities, temporarily add
813 * lr to the temp set. It is safe to do this locally, but note that
814 * lr is used explicitly elsewhere in the code generator and cannot
815 * normally be used as a general temp register.
816 */
buzbee67bf8852011-08-17 17:51:35 -0700817 RegLocation rlResult;
buzbee9e0f9b02011-08-24 15:32:46 -0700818 oatMarkTemp(cUnit, rLR); // Add lr to the temp pool
819 oatFreeTemp(cUnit, rLR); // and make it available
buzbee67bf8852011-08-17 17:51:35 -0700820 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
821 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
822 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeec0ecd652011-09-25 18:11:54 -0700823 // The longs may overlap - use intermediate temp if so
824 if (rlResult.lowReg == rlSrc1.highReg) {
buzbeec0ecd652011-09-25 18:11:54 -0700825 int tReg = oatAllocTemp(cUnit);
826 genRegCopy(cUnit, tReg, rlSrc1.highReg);
827 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
828 rlSrc2.lowReg);
829 opRegRegReg(cUnit, secondOp, rlResult.highReg, tReg,
830 rlSrc2.highReg);
831 oatFreeTemp(cUnit, tReg);
832 } else {
833 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
834 rlSrc2.lowReg);
835 opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
836 rlSrc2.highReg);
837 }
buzbee439c4fa2011-08-27 15:59:07 -0700838 /*
839 * NOTE: If rlDest refers to a frame variable in a large frame, the
840 * following storeValueWide might need to allocate a temp register.
841 * To further work around the lack of a spill capability, explicitly
842 * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult.
843 * Remove when spill is functional.
844 */
845 freeRegLocTemps(cUnit, rlResult, rlSrc1);
846 freeRegLocTemps(cUnit, rlResult, rlSrc2);
buzbee67bf8852011-08-17 17:51:35 -0700847 storeValueWide(cUnit, rlDest, rlResult);
buzbee9e0f9b02011-08-24 15:32:46 -0700848 oatClobber(cUnit, rLR);
849 oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool
buzbee67bf8852011-08-17 17:51:35 -0700850}
851
852void oatInitializeRegAlloc(CompilationUnit* cUnit)
853{
854 int numRegs = sizeof(coreRegs)/sizeof(*coreRegs);
855 int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs);
856 int numTemps = sizeof(coreTemps)/sizeof(*coreTemps);
857 int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs);
858 int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps);
buzbeeba938cb2012-02-03 14:47:55 -0800859 RegisterPool *pool = (RegisterPool *)oatNew(cUnit, sizeof(*pool), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800860 kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700861 cUnit->regPool = pool;
862 pool->numCoreRegs = numRegs;
863 pool->coreRegs = (RegisterInfo *)
buzbeeba938cb2012-02-03 14:47:55 -0800864 oatNew(cUnit, numRegs * sizeof(*cUnit->regPool->coreRegs),
865 true, kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700866 pool->numFPRegs = numFPRegs;
867 pool->FPRegs = (RegisterInfo *)
buzbeeba938cb2012-02-03 14:47:55 -0800868 oatNew(cUnit, numFPRegs * sizeof(*cUnit->regPool->FPRegs), true,
869 kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700870 oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs);
871 oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs);
872 // Keep special registers from being allocated
873 for (int i = 0; i < numReserved; i++) {
buzbee44b412b2012-02-04 08:50:53 -0800874 if (NO_SUSPEND && !cUnit->genDebugger &&
875 (reservedRegs[i] == rSUSPEND)) {
buzbeec0ecd652011-09-25 18:11:54 -0700876 //To measure cost of suspend check
877 continue;
878 }
buzbee67bf8852011-08-17 17:51:35 -0700879 oatMarkInUse(cUnit, reservedRegs[i]);
880 }
881 // Mark temp regs - all others not in use can be used for promotion
882 for (int i = 0; i < numTemps; i++) {
883 oatMarkTemp(cUnit, coreTemps[i]);
884 }
885 for (int i = 0; i < numFPTemps; i++) {
886 oatMarkTemp(cUnit, fpTemps[i]);
887 }
buzbeec0ecd652011-09-25 18:11:54 -0700888 // Construct the alias map.
buzbeeba938cb2012-02-03 14:47:55 -0800889 cUnit->phiAliasMap = (int*)oatNew(cUnit, cUnit->numSSARegs *
buzbee5abfa3e2012-01-31 17:01:43 -0800890 sizeof(cUnit->phiAliasMap[0]), false,
891 kAllocDFInfo);
buzbeec0ecd652011-09-25 18:11:54 -0700892 for (int i = 0; i < cUnit->numSSARegs; i++) {
893 cUnit->phiAliasMap[i] = i;
894 }
895 for (MIR* phi = cUnit->phiList; phi; phi = phi->meta.phiNext) {
896 int defReg = phi->ssaRep->defs[0];
897 for (int i = 0; i < phi->ssaRep->numUses; i++) {
898 for (int j = 0; j < cUnit->numSSARegs; j++) {
899 if (cUnit->phiAliasMap[j] == phi->ssaRep->uses[i]) {
900 cUnit->phiAliasMap[j] = defReg;
901 }
902 }
903 }
904 }
buzbee67bf8852011-08-17 17:51:35 -0700905}
906
907/*
908 * Handle simple case (thin lock) inline. If it's complicated, bail
909 * out to the heavyweight lock/unlock routines. We'll use dedicated
910 * registers here in order to be in the right position in case we
911 * to bail to dvm[Lock/Unlock]Object(self, object)
912 *
913 * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
914 * r1 -> object [arg1 for dvm[Lock/Unlock]Object
915 * r2 -> intial contents of object->lock, later result of strex
916 * r3 -> self->threadId
917 * r12 -> allow to be used by utilities as general temp
918 *
919 * The result of the strex is 0 if we acquire the lock.
920 *
921 * See comments in Sync.c for the layout of the lock word.
922 * Of particular interest to this code is the test for the
923 * simple case - which we handle inline. For monitor enter, the
924 * simple case is thin lock, held by no-one. For monitor exit,
925 * the simple case is thin lock, held by the unlocking thread with
926 * a recurse count of 0.
927 *
928 * A minor complication is that there is a field in the lock word
929 * unrelated to locking: the hash state. This field must be ignored, but
930 * preserved.
931 *
932 */
buzbeeed3e9302011-09-23 17:34:19 -0700933STATIC void genMonitorEnter(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700934 RegLocation rlSrc)
935{
936 ArmLIR* target;
937 ArmLIR* hopTarget;
938 ArmLIR* branch;
939 ArmLIR* hopBranch;
940
941 oatFlushAllRegs(cUnit);
Elliott Hughes5f791332011-09-15 17:45:30 -0700942 DCHECK_EQ(LW_SHAPE_THIN, 0);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700943 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700944 oatLockCallTemps(cUnit); // Prepare for explicit register usage
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700945 genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
946 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
947 newLIR3(cUnit, kThumb2Ldrex, r1, r0,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700948 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
buzbeec143c552011-08-20 17:38:58 -0700949 // Align owner
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700950 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
buzbee67bf8852011-08-17 17:51:35 -0700951 // Is lock unheld on lock or held by us (==threadId) on unlock?
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700952 newLIR4(cUnit, kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
953 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
954 hopBranch = newLIR2(cUnit, kThumb2Cbnz, r1, 0);
955 newLIR4(cUnit, kThumb2Strex, r1, r2, r0,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700956 Object::MonitorOffset().Int32Value() >> 2);
buzbee67bf8852011-08-17 17:51:35 -0700957 oatGenMemBarrier(cUnit, kSY);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700958 branch = newLIR2(cUnit, kThumb2Cbz, r1, 0);
buzbee67bf8852011-08-17 17:51:35 -0700959
960 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
961 hopTarget->defMask = ENCODE_ALL;
962 hopBranch->generic.target = (LIR*)hopTarget;
963
buzbee1b4c8592011-08-31 10:43:51 -0700964 // Go expensive route - artLockObjectFromCode(self, obj);
965 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700966 rLR);
Ian Rogersff1ed472011-09-20 13:46:24 -0700967 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700968
969 // Resume here
970 target = newLIR0(cUnit, kArmPseudoTargetLabel);
971 target->defMask = ENCODE_ALL;
972 branch->generic.target = (LIR*)target;
973}
974
975/*
976 * For monitor unlock, we don't have to use ldrex/strex. Once
977 * we've determined that the lock is thin and that we own it with
978 * a zero recursion count, it's safe to punch it back to the
979 * initial, unlock thin state with a store word.
980 */
buzbeeed3e9302011-09-23 17:34:19 -0700981STATIC void genMonitorExit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700982 RegLocation rlSrc)
983{
984 ArmLIR* target;
985 ArmLIR* branch;
986 ArmLIR* hopTarget;
987 ArmLIR* hopBranch;
988
Elliott Hughes5f791332011-09-15 17:45:30 -0700989 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -0700990 oatFlushAllRegs(cUnit);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700991 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700992 oatLockCallTemps(cUnit); // Prepare for explicit register usage
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700993 genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
994 loadWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r1); // Get lock
995 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
buzbee67bf8852011-08-17 17:51:35 -0700996 // Is lock unheld on lock or held by us (==threadId) on unlock?
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700997 opRegRegImm(cUnit, kOpAnd, r3, r1, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
buzbeec143c552011-08-20 17:38:58 -0700998 // Align owner
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700999 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
1000 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
1001 opRegReg(cUnit, kOpSub, r1, r2);
buzbee67bf8852011-08-17 17:51:35 -07001002 hopBranch = opCondBranch(cUnit, kArmCondNe);
1003 oatGenMemBarrier(cUnit, kSY);
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001004 storeWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -07001005 branch = opNone(cUnit, kOpUncondBr);
1006
1007 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
1008 hopTarget->defMask = ENCODE_ALL;
1009 hopBranch->generic.target = (LIR*)hopTarget;
1010
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001011 // Go expensive route - UnlockObjectFromCode(obj);
buzbee1b4c8592011-08-31 10:43:51 -07001012 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -07001013 rLR);
Ian Rogersff1ed472011-09-20 13:46:24 -07001014 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001015
1016 // Resume here
1017 target = newLIR0(cUnit, kArmPseudoTargetLabel);
1018 target->defMask = ENCODE_ALL;
1019 branch->generic.target = (LIR*)target;
1020}
1021
1022/*
1023 * 64-bit 3way compare function.
1024 * mov rX, #-1
1025 * cmp op1hi, op2hi
1026 * blt done
1027 * bgt flip
1028 * sub rX, op1lo, op2lo (treat as unsigned)
1029 * beq done
1030 * ite hi
1031 * mov(hi) rX, #-1
1032 * mov(!hi) rX, #1
1033 * flip:
1034 * neg rX
1035 * done:
1036 */
buzbeeed3e9302011-09-23 17:34:19 -07001037STATIC void genCmpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001038 RegLocation rlDest, RegLocation rlSrc1,
1039 RegLocation rlSrc2)
1040{
buzbee67bf8852011-08-17 17:51:35 -07001041 ArmLIR* target1;
1042 ArmLIR* target2;
1043 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
1044 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
buzbeeb29e4d12011-09-26 15:05:48 -07001045 int tReg = oatAllocTemp(cUnit);
1046 loadConstant(cUnit, tReg, -1);
buzbee67bf8852011-08-17 17:51:35 -07001047 opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
1048 ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt);
1049 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt);
buzbeeb29e4d12011-09-26 15:05:48 -07001050 opRegRegReg(cUnit, kOpSub, tReg, rlSrc1.lowReg, rlSrc2.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001051 ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq);
1052
1053 genIT(cUnit, kArmCondHi, "E");
buzbeeb29e4d12011-09-26 15:05:48 -07001054 newLIR2(cUnit, kThumb2MovImmShift, tReg, modifiedImmediate(-1));
1055 loadConstant(cUnit, tReg, 1);
buzbee67bf8852011-08-17 17:51:35 -07001056 genBarrier(cUnit);
1057
1058 target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
1059 target2->defMask = -1;
buzbeeb29e4d12011-09-26 15:05:48 -07001060 opRegReg(cUnit, kOpNeg, tReg, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001061
1062 target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
1063 target1->defMask = -1;
1064
buzbeeb29e4d12011-09-26 15:05:48 -07001065 RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
1066 rlTemp.lowReg = tReg;
buzbee67bf8852011-08-17 17:51:35 -07001067 storeValue(cUnit, rlDest, rlTemp);
buzbeeb29e4d12011-09-26 15:05:48 -07001068 oatFreeTemp(cUnit, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001069
1070 branch1->generic.target = (LIR*)target1;
1071 branch2->generic.target = (LIR*)target2;
1072 branch3->generic.target = branch1->generic.target;
1073}
1074
buzbeeed3e9302011-09-23 17:34:19 -07001075STATIC void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001076 RegLocation rlSrc, RegLocation rlResult, int lit,
1077 int firstBit, int secondBit)
1078{
1079 opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
1080 encodeShift(kArmLsl, secondBit - firstBit));
1081 if (firstBit != 0) {
1082 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
1083 }
1084}
1085
buzbeeed3e9302011-09-23 17:34:19 -07001086STATIC bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset,
buzbee67bf8852011-08-17 17:51:35 -07001087 int srcSize, int tgtSize)
1088{
1089 /*
1090 * Don't optimize the register usage since it calls out to support
1091 * functions
1092 */
1093 RegLocation rlSrc;
1094 RegLocation rlDest;
1095 oatFlushAllRegs(cUnit); /* Send everything to home location */
1096 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1097 if (srcSize == 1) {
1098 rlSrc = oatGetSrc(cUnit, mir, 0);
1099 loadValueDirectFixed(cUnit, rlSrc, r0);
1100 } else {
1101 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
1102 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
1103 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001104 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001105 if (tgtSize == 1) {
1106 RegLocation rlResult;
1107 rlDest = oatGetDest(cUnit, mir, 0);
1108 rlResult = oatGetReturn(cUnit);
1109 storeValue(cUnit, rlDest, rlResult);
1110 } else {
1111 RegLocation rlResult;
1112 rlDest = oatGetDestWide(cUnit, mir, 0, 1);
1113 rlResult = oatGetReturnWide(cUnit);
1114 storeValueWide(cUnit, rlDest, rlResult);
1115 }
1116 return false;
1117}
1118
buzbeeed3e9302011-09-23 17:34:19 -07001119STATIC bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001120 RegLocation rlDest, RegLocation rlSrc1,
1121 RegLocation rlSrc2)
1122{
1123 RegLocation rlResult;
1124 int funcOffset;
1125
1126 switch (mir->dalvikInsn.opcode) {
1127 case OP_ADD_FLOAT_2ADDR:
1128 case OP_ADD_FLOAT:
1129 funcOffset = OFFSETOF_MEMBER(Thread, pFadd);
1130 break;
1131 case OP_SUB_FLOAT_2ADDR:
1132 case OP_SUB_FLOAT:
1133 funcOffset = OFFSETOF_MEMBER(Thread, pFsub);
1134 break;
1135 case OP_DIV_FLOAT_2ADDR:
1136 case OP_DIV_FLOAT:
1137 funcOffset = OFFSETOF_MEMBER(Thread, pFdiv);
1138 break;
1139 case OP_MUL_FLOAT_2ADDR:
1140 case OP_MUL_FLOAT:
1141 funcOffset = OFFSETOF_MEMBER(Thread, pFmul);
1142 break;
1143 case OP_REM_FLOAT_2ADDR:
1144 case OP_REM_FLOAT:
1145 funcOffset = OFFSETOF_MEMBER(Thread, pFmodf);
1146 break;
1147 case OP_NEG_FLOAT: {
1148 genNegFloat(cUnit, rlDest, rlSrc1);
1149 return false;
1150 }
1151 default:
1152 return true;
1153 }
1154 oatFlushAllRegs(cUnit); /* Send everything to home location */
1155 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1156 loadValueDirectFixed(cUnit, rlSrc1, r0);
1157 loadValueDirectFixed(cUnit, rlSrc2, r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001158 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001159 rlResult = oatGetReturn(cUnit);
1160 storeValue(cUnit, rlDest, rlResult);
1161 return false;
1162}
1163
buzbeeed3e9302011-09-23 17:34:19 -07001164STATIC bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001165 RegLocation rlDest, RegLocation rlSrc1,
1166 RegLocation rlSrc2)
1167{
1168 RegLocation rlResult;
1169 int funcOffset;
1170
1171 switch (mir->dalvikInsn.opcode) {
1172 case OP_ADD_DOUBLE_2ADDR:
1173 case OP_ADD_DOUBLE:
1174 funcOffset = OFFSETOF_MEMBER(Thread, pDadd);
1175 break;
1176 case OP_SUB_DOUBLE_2ADDR:
1177 case OP_SUB_DOUBLE:
1178 funcOffset = OFFSETOF_MEMBER(Thread, pDsub);
1179 break;
1180 case OP_DIV_DOUBLE_2ADDR:
1181 case OP_DIV_DOUBLE:
1182 funcOffset = OFFSETOF_MEMBER(Thread, pDdiv);
1183 break;
1184 case OP_MUL_DOUBLE_2ADDR:
1185 case OP_MUL_DOUBLE:
1186 funcOffset = OFFSETOF_MEMBER(Thread, pDmul);
1187 break;
1188 case OP_REM_DOUBLE_2ADDR:
1189 case OP_REM_DOUBLE:
1190 funcOffset = OFFSETOF_MEMBER(Thread, pFmod);
1191 break;
1192 case OP_NEG_DOUBLE: {
1193 genNegDouble(cUnit, rlDest, rlSrc1);
1194 return false;
1195 }
1196 default:
1197 return true;
1198 }
1199 oatFlushAllRegs(cUnit); /* Send everything to home location */
1200 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1201 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1202 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
Ian Rogersff1ed472011-09-20 13:46:24 -07001203 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001204 rlResult = oatGetReturnWide(cUnit);
1205 storeValueWide(cUnit, rlDest, rlResult);
1206 return false;
1207}
1208
buzbeeed3e9302011-09-23 17:34:19 -07001209STATIC bool genConversionPortable(CompilationUnit* cUnit, MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -07001210{
1211 Opcode opcode = mir->dalvikInsn.opcode;
1212
1213 switch (opcode) {
1214 case OP_INT_TO_FLOAT:
1215 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f),
1216 1, 1);
1217 case OP_FLOAT_TO_INT:
1218 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz),
1219 1, 1);
1220 case OP_DOUBLE_TO_FLOAT:
1221 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f),
1222 2, 1);
1223 case OP_FLOAT_TO_DOUBLE:
1224 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d),
1225 1, 2);
1226 case OP_INT_TO_DOUBLE:
1227 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d),
1228 1, 2);
1229 case OP_DOUBLE_TO_INT:
1230 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz),
1231 2, 1);
1232 case OP_FLOAT_TO_LONG:
1233 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001234 pF2l), 1, 2);
buzbee67bf8852011-08-17 17:51:35 -07001235 case OP_LONG_TO_FLOAT:
1236 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f),
1237 2, 1);
1238 case OP_DOUBLE_TO_LONG:
1239 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001240 pD2l), 2, 2);
buzbee67bf8852011-08-17 17:51:35 -07001241 case OP_LONG_TO_DOUBLE:
1242 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d),
1243 2, 2);
1244 default:
1245 return true;
1246 }
1247 return false;
1248}
1249
buzbee67bf8852011-08-17 17:51:35 -07001250/*
1251 * Generate array store
1252 *
1253 */
buzbeeed3e9302011-09-23 17:34:19 -07001254STATIC void genArrayObjPut(CompilationUnit* cUnit, MIR* mir,
buzbee1b4c8592011-08-31 10:43:51 -07001255 RegLocation rlArray, RegLocation rlIndex,
1256 RegLocation rlSrc, int scale)
buzbee67bf8852011-08-17 17:51:35 -07001257{
1258 RegisterClass regClass = oatRegClassBySize(kWord);
buzbeec143c552011-08-20 17:38:58 -07001259 int lenOffset = Array::LengthOffset().Int32Value();
Ian Rogersa15e67d2012-02-28 13:51:55 -08001260 int dataOffset = Array::DataOffset(sizeof(Object*)).Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001261
buzbee6181f792011-09-29 11:14:04 -07001262 oatFlushAllRegs(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001263 /* Make sure it's a legal object Put. Use direct regs at first */
1264 loadValueDirectFixed(cUnit, rlArray, r1);
1265 loadValueDirectFixed(cUnit, rlSrc, r0);
1266
1267 /* null array object? */
buzbee43a36422011-09-14 14:00:13 -07001268 genNullCheck(cUnit, rlArray.sRegLow, r1, mir);
buzbee67bf8852011-08-17 17:51:35 -07001269 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -07001270 OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -07001271 /* Get the array's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001272 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001273 callRuntimeHelper(cUnit, rLR);
buzbee6181f792011-09-29 11:14:04 -07001274 oatFreeTemp(cUnit, r0);
1275 oatFreeTemp(cUnit, r1);
buzbee67bf8852011-08-17 17:51:35 -07001276
1277 // Now, redo loadValues in case they didn't survive the call
1278
1279 int regPtr;
1280 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1281 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1282
1283 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1284 oatClobber(cUnit, rlArray.lowReg);
1285 regPtr = rlArray.lowReg;
1286 } else {
1287 regPtr = oatAllocTemp(cUnit);
1288 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1289 }
1290
buzbee43a36422011-09-14 14:00:13 -07001291 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001292 int regLen = oatAllocTemp(cUnit);
1293 //NOTE: max live temps(4) here.
1294 /* Get len */
1295 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1296 /* regPtr -> array data */
1297 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001298 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001299 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001300 oatFreeTemp(cUnit, regLen);
1301 } else {
1302 /* regPtr -> array data */
1303 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1304 }
1305 /* at this point, regPtr points to array, 2 live temps */
1306 rlSrc = loadValue(cUnit, rlSrc, regClass);
1307 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1308 scale, kWord);
1309}
1310
1311/*
1312 * Generate array load
1313 */
buzbeeed3e9302011-09-23 17:34:19 -07001314STATIC void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001315 RegLocation rlArray, RegLocation rlIndex,
1316 RegLocation rlDest, int scale)
1317{
1318 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001319 int lenOffset = Array::LengthOffset().Int32Value();
Ian Rogersa15e67d2012-02-28 13:51:55 -08001320 int dataOffset;
buzbee67bf8852011-08-17 17:51:35 -07001321 RegLocation rlResult;
1322 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1323 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1324 int regPtr;
1325
Ian Rogersa15e67d2012-02-28 13:51:55 -08001326 if (size == kLong || size == kDouble) {
1327 dataOffset = Array::DataOffset(sizeof(int64_t)).Int32Value();
1328 } else {
1329 dataOffset = Array::DataOffset(sizeof(int32_t)).Int32Value();
1330 }
1331
buzbee67bf8852011-08-17 17:51:35 -07001332 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001333 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001334
1335 regPtr = oatAllocTemp(cUnit);
1336
buzbee43a36422011-09-14 14:00:13 -07001337 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001338 int regLen = oatAllocTemp(cUnit);
1339 /* Get len */
1340 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1341 /* regPtr -> array data */
1342 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001343 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001344 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001345 oatFreeTemp(cUnit, regLen);
1346 } else {
1347 /* regPtr -> array data */
1348 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
1349 }
buzbeee9a72f62011-09-04 17:59:07 -07001350 oatFreeTemp(cUnit, rlArray.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001351 if ((size == kLong) || (size == kDouble)) {
1352 if (scale) {
1353 int rNewIndex = oatAllocTemp(cUnit);
1354 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1355 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1356 oatFreeTemp(cUnit, rNewIndex);
1357 } else {
1358 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1359 }
buzbeee9a72f62011-09-04 17:59:07 -07001360 oatFreeTemp(cUnit, rlIndex.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001361 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1362
1363 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
1364
1365 oatFreeTemp(cUnit, regPtr);
1366 storeValueWide(cUnit, rlDest, rlResult);
1367 } else {
1368 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1369
1370 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
1371 scale, size);
1372
1373 oatFreeTemp(cUnit, regPtr);
1374 storeValue(cUnit, rlDest, rlResult);
1375 }
1376}
1377
1378/*
1379 * Generate array store
1380 *
1381 */
buzbeeed3e9302011-09-23 17:34:19 -07001382STATIC void genArrayPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001383 RegLocation rlArray, RegLocation rlIndex,
1384 RegLocation rlSrc, int scale)
1385{
1386 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001387 int lenOffset = Array::LengthOffset().Int32Value();
Ian Rogersa15e67d2012-02-28 13:51:55 -08001388 int dataOffset;
1389
1390 if (size == kLong || size == kDouble) {
1391 dataOffset = Array::DataOffset(sizeof(int64_t)).Int32Value();
1392 } else {
1393 dataOffset = Array::DataOffset(sizeof(int32_t)).Int32Value();
1394 }
buzbee67bf8852011-08-17 17:51:35 -07001395
1396 int regPtr;
1397 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1398 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1399
1400 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1401 oatClobber(cUnit, rlArray.lowReg);
1402 regPtr = rlArray.lowReg;
1403 } else {
1404 regPtr = oatAllocTemp(cUnit);
1405 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1406 }
1407
1408 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001409 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001410
buzbee43a36422011-09-14 14:00:13 -07001411 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001412 int regLen = oatAllocTemp(cUnit);
1413 //NOTE: max live temps(4) here.
1414 /* Get len */
1415 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1416 /* regPtr -> array data */
1417 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001418 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001419 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001420 oatFreeTemp(cUnit, regLen);
1421 } else {
1422 /* regPtr -> array data */
1423 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1424 }
1425 /* at this point, regPtr points to array, 2 live temps */
1426 if ((size == kLong) || (size == kDouble)) {
buzbee5ade1d22011-09-09 14:44:52 -07001427 //TUNING: specific wide routine that can handle fp regs
buzbee67bf8852011-08-17 17:51:35 -07001428 if (scale) {
1429 int rNewIndex = oatAllocTemp(cUnit);
1430 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1431 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1432 oatFreeTemp(cUnit, rNewIndex);
1433 } else {
1434 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1435 }
1436 rlSrc = loadValueWide(cUnit, rlSrc, regClass);
1437
1438 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
1439
1440 oatFreeTemp(cUnit, regPtr);
1441 } else {
1442 rlSrc = loadValue(cUnit, rlSrc, regClass);
1443
1444 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1445 scale, size);
1446 }
1447}
1448
buzbeeed3e9302011-09-23 17:34:19 -07001449STATIC bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001450 RegLocation rlDest, RegLocation rlSrc1,
1451 RegLocation rlShift)
1452{
buzbee54330722011-08-23 16:46:55 -07001453 int funcOffset;
buzbee67bf8852011-08-17 17:51:35 -07001454
buzbee67bf8852011-08-17 17:51:35 -07001455 switch( mir->dalvikInsn.opcode) {
1456 case OP_SHL_LONG:
1457 case OP_SHL_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001458 funcOffset = OFFSETOF_MEMBER(Thread, pShlLong);
buzbee67bf8852011-08-17 17:51:35 -07001459 break;
1460 case OP_SHR_LONG:
1461 case OP_SHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001462 funcOffset = OFFSETOF_MEMBER(Thread, pShrLong);
buzbee67bf8852011-08-17 17:51:35 -07001463 break;
1464 case OP_USHR_LONG:
1465 case OP_USHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001466 funcOffset = OFFSETOF_MEMBER(Thread, pUshrLong);
buzbee67bf8852011-08-17 17:51:35 -07001467 break;
1468 default:
buzbee54330722011-08-23 16:46:55 -07001469 LOG(FATAL) << "Unexpected case";
buzbee67bf8852011-08-17 17:51:35 -07001470 return true;
1471 }
buzbee54330722011-08-23 16:46:55 -07001472 oatFlushAllRegs(cUnit); /* Send everything to home location */
1473 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1474 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1475 loadValueDirect(cUnit, rlShift, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -07001476 callRuntimeHelper(cUnit, rLR);
buzbee54330722011-08-23 16:46:55 -07001477 RegLocation rlResult = oatGetReturnWide(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001478 storeValueWide(cUnit, rlDest, rlResult);
1479 return false;
1480}
1481
buzbeeed3e9302011-09-23 17:34:19 -07001482STATIC bool genArithOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001483 RegLocation rlDest, RegLocation rlSrc1,
1484 RegLocation rlSrc2)
1485{
1486 RegLocation rlResult;
1487 OpKind firstOp = kOpBkpt;
1488 OpKind secondOp = kOpBkpt;
1489 bool callOut = false;
buzbee58f92742011-10-01 11:22:17 -07001490 bool checkZero = false;
buzbee67bf8852011-08-17 17:51:35 -07001491 int funcOffset;
1492 int retReg = r0;
1493
1494 switch (mir->dalvikInsn.opcode) {
1495 case OP_NOT_LONG:
1496 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1497 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001498 // Check for destructive overlap
1499 if (rlResult.lowReg == rlSrc2.highReg) {
1500 int tReg = oatAllocTemp(cUnit);
1501 genRegCopy(cUnit, tReg, rlSrc2.highReg);
1502 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1503 opRegReg(cUnit, kOpMvn, rlResult.highReg, tReg);
1504 oatFreeTemp(cUnit, tReg);
1505 } else {
1506 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1507 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
1508 }
buzbee67bf8852011-08-17 17:51:35 -07001509 storeValueWide(cUnit, rlDest, rlResult);
1510 return false;
1511 break;
1512 case OP_ADD_LONG:
1513 case OP_ADD_LONG_2ADDR:
1514 firstOp = kOpAdd;
1515 secondOp = kOpAdc;
1516 break;
1517 case OP_SUB_LONG:
1518 case OP_SUB_LONG_2ADDR:
1519 firstOp = kOpSub;
1520 secondOp = kOpSbc;
1521 break;
1522 case OP_MUL_LONG:
1523 case OP_MUL_LONG_2ADDR:
buzbee439c4fa2011-08-27 15:59:07 -07001524 callOut = true;
1525 retReg = r0;
1526 funcOffset = OFFSETOF_MEMBER(Thread, pLmul);
1527 break;
buzbee67bf8852011-08-17 17:51:35 -07001528 case OP_DIV_LONG:
1529 case OP_DIV_LONG_2ADDR:
1530 callOut = true;
buzbee58f92742011-10-01 11:22:17 -07001531 checkZero = true;
buzbee67bf8852011-08-17 17:51:35 -07001532 retReg = r0;
1533 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1534 break;
1535 /* NOTE - result is in r2/r3 instead of r0/r1 */
1536 case OP_REM_LONG:
1537 case OP_REM_LONG_2ADDR:
1538 callOut = true;
buzbee58f92742011-10-01 11:22:17 -07001539 checkZero = true;
buzbee67bf8852011-08-17 17:51:35 -07001540 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1541 retReg = r2;
1542 break;
1543 case OP_AND_LONG_2ADDR:
1544 case OP_AND_LONG:
1545 firstOp = kOpAnd;
1546 secondOp = kOpAnd;
1547 break;
1548 case OP_OR_LONG:
1549 case OP_OR_LONG_2ADDR:
1550 firstOp = kOpOr;
1551 secondOp = kOpOr;
1552 break;
1553 case OP_XOR_LONG:
1554 case OP_XOR_LONG_2ADDR:
1555 firstOp = kOpXor;
1556 secondOp = kOpXor;
1557 break;
1558 case OP_NEG_LONG: {
buzbee67bf8852011-08-17 17:51:35 -07001559 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1560 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001561 int zReg = oatAllocTemp(cUnit);
1562 loadConstantNoClobber(cUnit, zReg, 0);
1563 // Check for destructive overlap
1564 if (rlResult.lowReg == rlSrc2.highReg) {
1565 int tReg = oatAllocTemp(cUnit);
1566 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1567 zReg, rlSrc2.lowReg);
1568 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1569 zReg, tReg);
1570 oatFreeTemp(cUnit, tReg);
1571 } else {
1572 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1573 zReg, rlSrc2.lowReg);
1574 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1575 zReg, rlSrc2.highReg);
1576 }
1577 oatFreeTemp(cUnit, zReg);
buzbee67bf8852011-08-17 17:51:35 -07001578 storeValueWide(cUnit, rlDest, rlResult);
1579 return false;
1580 }
1581 default:
1582 LOG(FATAL) << "Invalid long arith op";
1583 }
1584 if (!callOut) {
1585 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
1586 } else {
buzbee67bf8852011-08-17 17:51:35 -07001587 oatFlushAllRegs(cUnit); /* Send everything to home location */
buzbee58f92742011-10-01 11:22:17 -07001588 if (checkZero) {
1589 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1590 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1591 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1592 int tReg = oatAllocTemp(cUnit);
1593 newLIR4(cUnit, kThumb2OrrRRRs, tReg, r2, r3, 0);
1594 oatFreeTemp(cUnit, tReg);
1595 genCheck(cUnit, kArmCondEq, mir, kArmThrowDivZero);
1596 } else {
1597 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1598 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1599 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1600 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001601 callRuntimeHelper(cUnit, rLR);
buzbee58f92742011-10-01 11:22:17 -07001602 // Adjust return regs in to handle case of rem returning r2/r3
buzbee67bf8852011-08-17 17:51:35 -07001603 if (retReg == r0)
1604 rlResult = oatGetReturnWide(cUnit);
1605 else
1606 rlResult = oatGetReturnWideAlt(cUnit);
1607 storeValueWide(cUnit, rlDest, rlResult);
1608 }
1609 return false;
1610}
1611
buzbeeed3e9302011-09-23 17:34:19 -07001612STATIC bool genArithOpInt(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001613 RegLocation rlDest, RegLocation rlSrc1,
1614 RegLocation rlSrc2)
1615{
1616 OpKind op = kOpBkpt;
1617 bool callOut = false;
1618 bool checkZero = false;
1619 bool unary = false;
1620 int retReg = r0;
1621 int funcOffset;
1622 RegLocation rlResult;
1623 bool shiftOp = false;
1624
1625 switch (mir->dalvikInsn.opcode) {
1626 case OP_NEG_INT:
1627 op = kOpNeg;
1628 unary = true;
1629 break;
1630 case OP_NOT_INT:
1631 op = kOpMvn;
1632 unary = true;
1633 break;
1634 case OP_ADD_INT:
1635 case OP_ADD_INT_2ADDR:
1636 op = kOpAdd;
1637 break;
1638 case OP_SUB_INT:
1639 case OP_SUB_INT_2ADDR:
1640 op = kOpSub;
1641 break;
1642 case OP_MUL_INT:
1643 case OP_MUL_INT_2ADDR:
1644 op = kOpMul;
1645 break;
1646 case OP_DIV_INT:
1647 case OP_DIV_INT_2ADDR:
1648 callOut = true;
1649 checkZero = true;
1650 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1651 retReg = r0;
1652 break;
1653 /* NOTE: returns in r1 */
1654 case OP_REM_INT:
1655 case OP_REM_INT_2ADDR:
1656 callOut = true;
1657 checkZero = true;
1658 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1659 retReg = r1;
1660 break;
1661 case OP_AND_INT:
1662 case OP_AND_INT_2ADDR:
1663 op = kOpAnd;
1664 break;
1665 case OP_OR_INT:
1666 case OP_OR_INT_2ADDR:
1667 op = kOpOr;
1668 break;
1669 case OP_XOR_INT:
1670 case OP_XOR_INT_2ADDR:
1671 op = kOpXor;
1672 break;
1673 case OP_SHL_INT:
1674 case OP_SHL_INT_2ADDR:
1675 shiftOp = true;
1676 op = kOpLsl;
1677 break;
1678 case OP_SHR_INT:
1679 case OP_SHR_INT_2ADDR:
1680 shiftOp = true;
1681 op = kOpAsr;
1682 break;
1683 case OP_USHR_INT:
1684 case OP_USHR_INT_2ADDR:
1685 shiftOp = true;
1686 op = kOpLsr;
1687 break;
1688 default:
1689 LOG(FATAL) << "Invalid word arith op: " <<
1690 (int)mir->dalvikInsn.opcode;
1691 }
1692 if (!callOut) {
1693 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
1694 if (unary) {
1695 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1696 opRegReg(cUnit, op, rlResult.lowReg,
1697 rlSrc1.lowReg);
1698 } else {
1699 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
1700 if (shiftOp) {
1701 int tReg = oatAllocTemp(cUnit);
1702 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
1703 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1704 opRegRegReg(cUnit, op, rlResult.lowReg,
1705 rlSrc1.lowReg, tReg);
1706 oatFreeTemp(cUnit, tReg);
1707 } else {
1708 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1709 opRegRegReg(cUnit, op, rlResult.lowReg,
1710 rlSrc1.lowReg, rlSrc2.lowReg);
1711 }
1712 }
1713 storeValue(cUnit, rlDest, rlResult);
1714 } else {
1715 RegLocation rlResult;
1716 oatFlushAllRegs(cUnit); /* Send everything to home location */
1717 loadValueDirectFixed(cUnit, rlSrc2, r1);
1718 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1719 loadValueDirectFixed(cUnit, rlSrc1, r0);
1720 if (checkZero) {
buzbee5ade1d22011-09-09 14:44:52 -07001721 genImmedCheck(cUnit, kArmCondEq, r1, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001722 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001723 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001724 if (retReg == r0)
1725 rlResult = oatGetReturn(cUnit);
1726 else
1727 rlResult = oatGetReturnAlt(cUnit);
1728 storeValue(cUnit, rlDest, rlResult);
1729 }
1730 return false;
1731}
1732
buzbeec1f45042011-09-21 16:03:19 -07001733/* Check if we need to check for pending suspend request */
buzbeeed3e9302011-09-23 17:34:19 -07001734STATIC void genSuspendTest(CompilationUnit* cUnit, MIR* mir)
buzbeec1f45042011-09-21 16:03:19 -07001735{
Ian Rogersa3760aa2011-11-14 14:32:37 -08001736 if (NO_SUSPEND || (mir->optimizationFlags & MIR_IGNORE_SUSPEND_CHECK)) {
buzbeec1f45042011-09-21 16:03:19 -07001737 return;
1738 }
buzbee6181f792011-09-29 11:14:04 -07001739 oatFlushAllRegs(cUnit);
buzbee44b412b2012-02-04 08:50:53 -08001740 ArmLIR* branch;
1741 if (cUnit->genDebugger) {
1742 // If generating code for the debugger, always check for suspension
1743 branch = genUnconditionalBranch(cUnit, NULL);
1744 } else {
1745 // In non-debug case, only check periodically
1746 newLIR2(cUnit, kThumbSubRI8, rSUSPEND, 1);
1747 branch = opCondBranch(cUnit, kArmCondEq);
1748 }
buzbeec1f45042011-09-21 16:03:19 -07001749 ArmLIR* retLab = newLIR0(cUnit, kArmPseudoTargetLabel);
1750 retLab->defMask = ENCODE_ALL;
buzbeeba938cb2012-02-03 14:47:55 -08001751 ArmLIR* target = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbeec1f45042011-09-21 16:03:19 -07001752 target->generic.dalvikOffset = cUnit->currentDalvikOffset;
1753 target->opcode = kArmPseudoSuspendTarget;
1754 target->operands[0] = (intptr_t)retLab;
1755 target->operands[1] = mir->offset;
1756 branch->generic.target = (LIR*)target;
buzbeeba938cb2012-02-03 14:47:55 -08001757 oatInsertGrowableList(cUnit, &cUnit->suspendLaunchpads, (intptr_t)target);
buzbeec1f45042011-09-21 16:03:19 -07001758}
1759
buzbee67bf8852011-08-17 17:51:35 -07001760/*
1761 * The following are the first-level codegen routines that analyze the format
1762 * of each bytecode then either dispatch special purpose codegen routines
1763 * or produce corresponding Thumb instructions directly.
1764 */
1765
buzbeeed3e9302011-09-23 17:34:19 -07001766STATIC bool isPowerOfTwo(int x)
buzbee67bf8852011-08-17 17:51:35 -07001767{
1768 return (x & (x - 1)) == 0;
1769}
1770
1771// Returns true if no more than two bits are set in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001772STATIC bool isPopCountLE2(unsigned int x)
buzbee67bf8852011-08-17 17:51:35 -07001773{
1774 x &= x - 1;
1775 return (x & (x - 1)) == 0;
1776}
1777
1778// Returns the index of the lowest set bit in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001779STATIC int lowestSetBit(unsigned int x) {
buzbee67bf8852011-08-17 17:51:35 -07001780 int bit_posn = 0;
1781 while ((x & 0xf) == 0) {
1782 bit_posn += 4;
1783 x >>= 4;
1784 }
1785 while ((x & 1) == 0) {
1786 bit_posn++;
1787 x >>= 1;
1788 }
1789 return bit_posn;
1790}
1791
1792// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
1793// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001794STATIC bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode,
buzbee67bf8852011-08-17 17:51:35 -07001795 RegLocation rlSrc, RegLocation rlDest, int lit)
1796{
1797 if (lit < 2 || !isPowerOfTwo(lit)) {
1798 return false;
1799 }
1800 int k = lowestSetBit(lit);
1801 if (k >= 30) {
1802 // Avoid special cases.
1803 return false;
1804 }
1805 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 ||
1806 dalvikOpcode == OP_DIV_INT_LIT16);
1807 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1808 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1809 if (div) {
1810 int tReg = oatAllocTemp(cUnit);
1811 if (lit == 2) {
1812 // Division by 2 is by far the most common division by constant.
1813 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
1814 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1815 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1816 } else {
1817 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
1818 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
1819 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1820 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1821 }
1822 } else {
1823 int cReg = oatAllocTemp(cUnit);
1824 loadConstant(cUnit, cReg, lit - 1);
1825 int tReg1 = oatAllocTemp(cUnit);
1826 int tReg2 = oatAllocTemp(cUnit);
1827 if (lit == 2) {
1828 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
1829 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1830 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1831 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1832 } else {
1833 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
1834 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
1835 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1836 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1837 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1838 }
1839 }
1840 storeValue(cUnit, rlDest, rlResult);
1841 return true;
1842}
1843
1844// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
1845// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001846STATIC bool handleEasyMultiply(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001847 RegLocation rlSrc, RegLocation rlDest, int lit)
1848{
1849 // Can we simplify this multiplication?
1850 bool powerOfTwo = false;
1851 bool popCountLE2 = false;
1852 bool powerOfTwoMinusOne = false;
1853 if (lit < 2) {
1854 // Avoid special cases.
1855 return false;
1856 } else if (isPowerOfTwo(lit)) {
1857 powerOfTwo = true;
1858 } else if (isPopCountLE2(lit)) {
1859 popCountLE2 = true;
1860 } else if (isPowerOfTwo(lit + 1)) {
1861 powerOfTwoMinusOne = true;
1862 } else {
1863 return false;
1864 }
1865 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1866 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1867 if (powerOfTwo) {
1868 // Shift.
1869 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
1870 lowestSetBit(lit));
1871 } else if (popCountLE2) {
1872 // Shift and add and shift.
1873 int firstBit = lowestSetBit(lit);
1874 int secondBit = lowestSetBit(lit ^ (1 << firstBit));
1875 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
1876 firstBit, secondBit);
1877 } else {
1878 // Reverse subtract: (src << (shift + 1)) - src.
buzbeeed3e9302011-09-23 17:34:19 -07001879 DCHECK(powerOfTwoMinusOne);
buzbee5ade1d22011-09-09 14:44:52 -07001880 // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1)
buzbee67bf8852011-08-17 17:51:35 -07001881 int tReg = oatAllocTemp(cUnit);
1882 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
1883 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
1884 }
1885 storeValue(cUnit, rlDest, rlResult);
1886 return true;
1887}
1888
buzbeeed3e9302011-09-23 17:34:19 -07001889STATIC bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001890 RegLocation rlDest, RegLocation rlSrc,
1891 int lit)
1892{
1893 Opcode dalvikOpcode = mir->dalvikInsn.opcode;
1894 RegLocation rlResult;
1895 OpKind op = (OpKind)0; /* Make gcc happy */
1896 int shiftOp = false;
1897 bool isDiv = false;
1898 int funcOffset;
1899
1900 switch (dalvikOpcode) {
1901 case OP_RSUB_INT_LIT8:
1902 case OP_RSUB_INT: {
1903 int tReg;
1904 //TUNING: add support for use of Arm rsub op
1905 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1906 tReg = oatAllocTemp(cUnit);
1907 loadConstant(cUnit, tReg, lit);
1908 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1909 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1910 tReg, rlSrc.lowReg);
1911 storeValue(cUnit, rlDest, rlResult);
1912 return false;
1913 break;
1914 }
1915
1916 case OP_ADD_INT_LIT8:
1917 case OP_ADD_INT_LIT16:
1918 op = kOpAdd;
1919 break;
1920 case OP_MUL_INT_LIT8:
1921 case OP_MUL_INT_LIT16: {
1922 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
1923 return false;
1924 }
1925 op = kOpMul;
1926 break;
1927 }
1928 case OP_AND_INT_LIT8:
1929 case OP_AND_INT_LIT16:
1930 op = kOpAnd;
1931 break;
1932 case OP_OR_INT_LIT8:
1933 case OP_OR_INT_LIT16:
1934 op = kOpOr;
1935 break;
1936 case OP_XOR_INT_LIT8:
1937 case OP_XOR_INT_LIT16:
1938 op = kOpXor;
1939 break;
1940 case OP_SHL_INT_LIT8:
1941 lit &= 31;
1942 shiftOp = true;
1943 op = kOpLsl;
1944 break;
1945 case OP_SHR_INT_LIT8:
1946 lit &= 31;
1947 shiftOp = true;
1948 op = kOpAsr;
1949 break;
1950 case OP_USHR_INT_LIT8:
1951 lit &= 31;
1952 shiftOp = true;
1953 op = kOpLsr;
1954 break;
1955
1956 case OP_DIV_INT_LIT8:
1957 case OP_DIV_INT_LIT16:
1958 case OP_REM_INT_LIT8:
1959 case OP_REM_INT_LIT16:
1960 if (lit == 0) {
buzbee5ade1d22011-09-09 14:44:52 -07001961 genImmedCheck(cUnit, kArmCondAl, 0, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001962 return false;
1963 }
1964 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
1965 return false;
1966 }
1967 oatFlushAllRegs(cUnit); /* Everything to home location */
1968 loadValueDirectFixed(cUnit, rlSrc, r0);
1969 oatClobber(cUnit, r0);
1970 if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
1971 (dalvikOpcode == OP_DIV_INT_LIT16)) {
1972 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1973 isDiv = true;
1974 } else {
1975 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1976 isDiv = false;
1977 }
1978 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1979 loadConstant(cUnit, r1, lit);
Ian Rogersff1ed472011-09-20 13:46:24 -07001980 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001981 if (isDiv)
1982 rlResult = oatGetReturn(cUnit);
1983 else
1984 rlResult = oatGetReturnAlt(cUnit);
1985 storeValue(cUnit, rlDest, rlResult);
1986 return false;
1987 break;
1988 default:
1989 return true;
1990 }
1991 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1992 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1993 // Avoid shifts by literal 0 - no support in Thumb. Change to copy
1994 if (shiftOp && (lit == 0)) {
1995 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
1996 } else {
1997 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
1998 }
1999 storeValue(cUnit, rlDest, rlResult);
2000 return false;
2001}
2002
2003/* Architectural-specific debugging helpers go here */
2004void oatArchDump(void)
2005{
2006 /* Print compiled opcode in this VM instance */
2007 int i, start, streak;
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002008 std::string buf;
buzbee67bf8852011-08-17 17:51:35 -07002009
2010 streak = i = 0;
buzbee67bf8852011-08-17 17:51:35 -07002011 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2012 i++;
2013 }
2014 if (i == kNumPackedOpcodes) {
2015 return;
2016 }
2017 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
2018 if (opcodeCoverage[i]) {
2019 streak++;
2020 } else {
2021 if (streak == 1) {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002022 StringAppendF(&buf, "%x,", start);
buzbee67bf8852011-08-17 17:51:35 -07002023 } else {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002024 StringAppendF(&buf, "%x-%x,", start, start + streak - 1);
buzbee67bf8852011-08-17 17:51:35 -07002025 }
2026 streak = 0;
2027 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2028 i++;
2029 }
2030 if (i < kNumPackedOpcodes) {
2031 streak = 1;
2032 start = i;
2033 }
2034 }
2035 }
2036 if (streak) {
2037 if (streak == 1) {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002038 StringAppendF(&buf, "%x", start);
buzbee67bf8852011-08-17 17:51:35 -07002039 } else {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002040 StringAppendF(&buf, "%x-%x", start, start + streak - 1);
buzbee67bf8852011-08-17 17:51:35 -07002041 }
2042 }
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002043 if (!buf.empty()) {
buzbee67bf8852011-08-17 17:51:35 -07002044 LOG(INFO) << "dalvik.vm.oat.op = " << buf;
2045 }
2046}
Elliott Hughes11d1b0c2012-01-23 16:57:47 -08002047
2048} // namespace art