blob: 4ae254bdf7b2ed8ef30614f657615b6dbbd1b201 [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
buzbee34cd9e52011-09-08 14:31:52 -070025#define SLOW_FIELD_PATH 0
26#define SLOW_INVOKE_PATH 0
27#define DISPLAY_MISSING_TARGETS
28//#define EXERCISE_SLOWEST_FIELD_PATH
29
30std::string fieldNameFromIndex(const Method* method, uint32_t fieldIdx)
31{
32 art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
33 const art::DexFile& dex_file = class_linker->FindDexFile(
34 method->GetDeclaringClass()->GetDexCache());
35 const art::DexFile::FieldId& field_id = dex_file.GetFieldId(fieldIdx);
36 std::string class_name = dex_file.dexStringById(field_id.class_idx_);
37 std::string field_name = dex_file.dexStringById(field_id.name_idx_);
38 return class_name + "." + field_name;
39}
40
buzbee67bf8852011-08-17 17:51:35 -070041/*
42 * Construct an s4 from two consecutive half-words of switch data.
43 * This needs to check endianness because the DEX optimizer only swaps
44 * half-words in instruction stream.
45 *
46 * "switchData" must be 32-bit aligned.
47 */
48#if __BYTE_ORDER == __LITTLE_ENDIAN
49static inline s4 s4FromSwitchData(const void* switchData) {
50 return *(s4*) switchData;
51}
52#else
53static inline s4 s4FromSwitchData(const void* switchData) {
54 u2* data = switchData;
55 return data[0] | (((s4) data[1]) << 16);
56}
57#endif
58
buzbee1b4c8592011-08-31 10:43:51 -070059/* Generate unconditional branch instructions */
60static ArmLIR* genUnconditionalBranch(CompilationUnit* cUnit, ArmLIR* target)
61{
62 ArmLIR* branch = opNone(cUnit, kOpUncondBr);
63 branch->generic.target = (LIR*) target;
64 return branch;
65}
66
buzbee67bf8852011-08-17 17:51:35 -070067/*
68 * Generate a Thumb2 IT instruction, which can nullify up to
69 * four subsequent instructions based on a condition and its
70 * inverse. The condition applies to the first instruction, which
71 * is executed if the condition is met. The string "guide" consists
72 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
73 * A "T" means the instruction is executed if the condition is
74 * met, and an "E" means the instruction is executed if the condition
75 * is not met.
76 */
77static ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code,
78 const char* guide)
79{
80 int mask;
81 int condBit = code & 1;
82 int altBit = condBit ^ 1;
83 int mask3 = 0;
84 int mask2 = 0;
85 int mask1 = 0;
86
87 //Note: case fallthroughs intentional
88 switch(strlen(guide)) {
89 case 3:
90 mask1 = (guide[2] == 'T') ? condBit : altBit;
91 case 2:
92 mask2 = (guide[1] == 'T') ? condBit : altBit;
93 case 1:
94 mask3 = (guide[0] == 'T') ? condBit : altBit;
95 break;
96 case 0:
97 break;
98 default:
99 LOG(FATAL) << "OAT: bad case in genIT";
100 }
101 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
102 (1 << (3 - strlen(guide)));
103 return newLIR2(cUnit, kThumb2It, code, mask);
104}
105
106/*
107 * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik
108 * offset vaddr. This label will be used to fix up the case
109 * branch table during the assembly phase. Be sure to set
110 * all resource flags on this to prevent code motion across
111 * target boundaries. KeyVal is just there for debugging.
112 */
113static ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
114{
115 ArmLIR* lir;
116 for (lir = (ArmLIR*)cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
117 if ((lir->opcode == kArmPseudoDalvikByteCodeBoundary) &&
118 (lir->generic.dalvikOffset == vaddr)) {
119 ArmLIR* newLabel = (ArmLIR*)oatNew(sizeof(ArmLIR), true);
120 newLabel->generic.dalvikOffset = vaddr;
121 newLabel->opcode = kArmPseudoCaseLabel;
122 newLabel->operands[0] = keyVal;
123 oatInsertLIRAfter((LIR*)lir, (LIR*)newLabel);
124 return newLabel;
125 }
126 }
127 oatCodegenDump(cUnit);
128 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
129 return NULL; // Quiet gcc
130}
131
132static void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
133{
134 const u2* table = tabRec->table;
135 int baseVaddr = tabRec->vaddr;
136 int *targets = (int*)&table[4];
137 int entries = table[1];
138 int lowKey = s4FromSwitchData(&table[2]);
139 for (int i = 0; i < entries; i++) {
140 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
141 i + lowKey);
142 }
143}
144
145static void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
146{
147 const u2* table = tabRec->table;
148 int baseVaddr = tabRec->vaddr;
149 int entries = table[1];
150 int* keys = (int*)&table[2];
151 int* targets = &keys[entries];
152 for (int i = 0; i < entries; i++) {
153 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
154 keys[i]);
155 }
156}
157
158void oatProcessSwitchTables(CompilationUnit* cUnit)
159{
160 GrowableListIterator iterator;
161 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
162 while (true) {
163 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
164 &iterator);
165 if (tabRec == NULL) break;
166 if (tabRec->table[0] == kPackedSwitchSignature)
167 markPackedCaseLabels(cUnit, tabRec);
168 else if (tabRec->table[0] == kSparseSwitchSignature)
169 markSparseCaseLabels(cUnit, tabRec);
170 else {
171 LOG(FATAL) << "Invalid switch table";
172 }
173 }
174}
175
176static void dumpSparseSwitchTable(const u2* table)
177 /*
178 * Sparse switch data format:
179 * ushort ident = 0x0200 magic value
180 * ushort size number of entries in the table; > 0
181 * int keys[size] keys, sorted low-to-high; 32-bit aligned
182 * int targets[size] branch targets, relative to switch opcode
183 *
184 * Total size is (2+size*4) 16-bit code units.
185 */
186{
187 u2 ident = table[0];
188 int entries = table[1];
189 int* keys = (int*)&table[2];
190 int* targets = &keys[entries];
191 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
192 ", entries: " << std::dec << entries;
193 for (int i = 0; i < entries; i++) {
194 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
195 targets[i];
196 }
197}
198
199static void dumpPackedSwitchTable(const u2* table)
200 /*
201 * Packed switch data format:
202 * ushort ident = 0x0100 magic value
203 * ushort size number of entries in the table
204 * int first_key first (and lowest) switch case value
205 * int targets[size] branch targets, relative to switch opcode
206 *
207 * Total size is (4+size*2) 16-bit code units.
208 */
209{
210 u2 ident = table[0];
211 int* targets = (int*)&table[4];
212 int entries = table[1];
213 int lowKey = s4FromSwitchData(&table[2]);
214 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
215 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
216 for (int i = 0; i < entries; i++) {
217 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
218 targets[i];
219 }
220}
221
222/*
223 * The sparse table in the literal pool is an array of <key,displacement>
224 * pairs. For each set, we'll load them as a pair using ldmia.
225 * This means that the register number of the temp we use for the key
226 * must be lower than the reg for the displacement.
227 *
228 * The test loop will look something like:
229 *
230 * adr rBase, <table>
231 * ldr rVal, [rSP, vRegOff]
232 * mov rIdx, #tableSize
233 * lp:
234 * ldmia rBase!, {rKey, rDisp}
235 * sub rIdx, #1
236 * cmp rVal, rKey
237 * ifeq
238 * add rPC, rDisp ; This is the branch from which we compute displacement
239 * cbnz rIdx, lp
240 */
241static void genSparseSwitch(CompilationUnit* cUnit, MIR* mir,
242 RegLocation rlSrc)
243{
244 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
245 if (cUnit->printMe) {
246 dumpSparseSwitchTable(table);
247 }
248 // Add the table to the list - we'll process it later
249 SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable),
250 true);
251 tabRec->table = table;
252 tabRec->vaddr = mir->offset;
253 int size = table[1];
254 tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true);
255 oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec);
256
257 // Get the switch value
258 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
259 int rBase = oatAllocTemp(cUnit);
260 /* Allocate key and disp temps */
261 int rKey = oatAllocTemp(cUnit);
262 int rDisp = oatAllocTemp(cUnit);
263 // Make sure rKey's register number is less than rDisp's number for ldmia
264 if (rKey > rDisp) {
265 int tmp = rDisp;
266 rDisp = rKey;
267 rKey = tmp;
268 }
269 // Materialize a pointer to the switch table
270 newLIR3(cUnit, kThumb2AdrST, rBase, 0, (intptr_t)tabRec);
271 // Set up rIdx
272 int rIdx = oatAllocTemp(cUnit);
273 loadConstant(cUnit, rIdx, size);
274 // Establish loop branch target
275 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
276 target->defMask = ENCODE_ALL;
277 // Load next key/disp
278 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
279 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
280 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
281 genIT(cUnit, kArmCondEq, "");
282 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
283 tabRec->bxInst = switchBranch;
284 // Needs to use setflags encoding here
285 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
286 ArmLIR* branch = opCondBranch(cUnit, kArmCondNe);
287 branch->generic.target = (LIR*)target;
288}
289
290
291static void genPackedSwitch(CompilationUnit* cUnit, MIR* mir,
292 RegLocation rlSrc)
293{
294 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
295 if (cUnit->printMe) {
296 dumpPackedSwitchTable(table);
297 }
298 // Add the table to the list - we'll process it later
299 SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable),
300 true);
301 tabRec->table = table;
302 tabRec->vaddr = mir->offset;
303 int size = table[1];
304 tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true);
305 oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec);
306
307 // Get the switch value
308 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
309 int tableBase = oatAllocTemp(cUnit);
310 // Materialize a pointer to the switch table
311 newLIR3(cUnit, kThumb2AdrST, tableBase, 0, (intptr_t)tabRec);
312 int lowKey = s4FromSwitchData(&table[2]);
313 int keyReg;
314 // Remove the bias, if necessary
315 if (lowKey == 0) {
316 keyReg = rlSrc.lowReg;
317 } else {
318 keyReg = oatAllocTemp(cUnit);
319 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
320 }
321 // Bounds check - if < 0 or >= size continue following switch
322 opRegImm(cUnit, kOpCmp, keyReg, size-1);
323 ArmLIR* branchOver = opCondBranch(cUnit, kArmCondHi);
324
325 // Load the displacement from the switch table
326 int dispReg = oatAllocTemp(cUnit);
327 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
328
329 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
330 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
331 tabRec->bxInst = switchBranch;
332
333 /* branchOver target here */
334 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
335 target->defMask = ENCODE_ALL;
336 branchOver->generic.target = (LIR*)target;
337}
338
339/*
340 * Array data table format:
341 * ushort ident = 0x0300 magic value
342 * ushort width width of each element in the table
343 * uint size number of elements in the table
344 * ubyte data[size*width] table of data values (may contain a single-byte
345 * padding at the end)
346 *
347 * Total size is 4+(width * size + 1)/2 16-bit code units.
348 */
349static void genFillArrayData(CompilationUnit* cUnit, MIR* mir,
350 RegLocation rlSrc)
351{
352 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
353 // Add the table to the list - we'll process it later
354 FillArrayData *tabRec = (FillArrayData *)
355 oatNew(sizeof(FillArrayData), true);
356 tabRec->table = table;
357 tabRec->vaddr = mir->offset;
358 u2 width = tabRec->table[1];
359 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
360 tabRec->size = (size * width) + 8;
361
362 oatInsertGrowableList(&cUnit->fillArrayData, (intptr_t)tabRec);
363
364 // Making a call - use explicit registers
365 oatFlushAllRegs(cUnit); /* Everything to home location */
366 loadValueDirectFixed(cUnit, rlSrc, r0);
367 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700368 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
buzbeee6d61962011-08-27 11:58:19 -0700369 // Materialize a pointer to the fill data image
buzbee67bf8852011-08-17 17:51:35 -0700370 newLIR3(cUnit, kThumb2AdrST, r1, 0, (intptr_t)tabRec);
371 opReg(cUnit, kOpBlx, rLR);
372 oatClobberCallRegs(cUnit);
373}
374
375/*
376 * Mark garbage collection card. Skip if the value we're storing is null.
377 */
378static void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg)
379{
Elliott Hughes0f4c41d2011-09-04 14:58:03 -0700380#if 1
381 UNIMPLEMENTED(WARNING);
382#else
buzbee67bf8852011-08-17 17:51:35 -0700383 int regCardBase = oatAllocTemp(cUnit);
384 int regCardNo = oatAllocTemp(cUnit);
385 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0);
buzbeec143c552011-08-20 17:38:58 -0700386 loadWordDisp(cUnit, rSELF, Thread::CardTableOffset().Int32Value(),
buzbee67bf8852011-08-17 17:51:35 -0700387 regCardBase);
388 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
389 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
390 kUnsignedByte);
391 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
392 target->defMask = ENCODE_ALL;
393 branchOver->generic.target = (LIR*)target;
394 oatFreeTemp(cUnit, regCardBase);
395 oatFreeTemp(cUnit, regCardNo);
Elliott Hughes0f4c41d2011-09-04 14:58:03 -0700396#endif
buzbee67bf8852011-08-17 17:51:35 -0700397}
398
buzbee34cd9e52011-09-08 14:31:52 -0700399/*
400 * Helper function for Iget/put when field not resolved at compile time.
401 * Will trash call temps and return with the field offset in r0.
402 */
403static void getFieldOffset(CompilationUnit* cUnit, MIR* mir)
404{
405 int fieldIdx = mir->dalvikInsn.vC;
406 LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
407 << " unresolved at compile time";
408 oatLockCallTemps(cUnit); // Explicit register usage
409 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
410 loadWordDisp(cUnit, r1,
411 Method::DexCacheResolvedFieldsOffset().Int32Value(), r0);
412 loadWordDisp(cUnit, r0, art::Array::DataOffset().Int32Value() +
413 sizeof(int32_t*)* fieldIdx, r0);
414 /*
415 * For testing, omit the test for run-time resolution. This will
416 * force all accesses to go through the runtime resolution path.
417 */
418#ifndef EXERCISE_SLOWEST_FIELD_PATH
419 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
420#endif
421 // Resolve
422 loadWordDisp(cUnit, rSELF,
423 OFFSETOF_MEMBER(Thread, pFindFieldFromCode), rLR);
424 loadConstant(cUnit, r0, fieldIdx);
425 opReg(cUnit, kOpBlx, rLR); // resolveTypeFromCode(idx, method)
426 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
427 target->defMask = ENCODE_ALL;
428#ifndef EXERCISE_SLOWEST_FIELD_PATH
429 branchOver->generic.target = (LIR*)target;
430#endif
431 // Free temps (except for r0)
432 oatFreeTemp(cUnit, r1);
433 oatFreeTemp(cUnit, r2);
434 oatFreeTemp(cUnit, r3);
435 loadWordDisp(cUnit, r0, art::Field::OffsetOffset().Int32Value(), r0);
436}
437
buzbee67bf8852011-08-17 17:51:35 -0700438static void genIGetX(CompilationUnit* cUnit, MIR* mir, OpSize size,
439 RegLocation rlDest, RegLocation rlObj)
440{
buzbeec143c552011-08-20 17:38:58 -0700441 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
442 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700443 RegLocation rlResult;
444 RegisterClass regClass = oatRegClassBySize(size);
buzbee34cd9e52011-09-08 14:31:52 -0700445 if (SLOW_FIELD_PATH || fieldPtr == NULL) {
446 getFieldOffset(cUnit, mir);
447 // Field offset in r0
448 rlObj = loadValue(cUnit, rlObj, kCoreReg);
449 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
450 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
451 NULL);/* null object? */
452 loadBaseIndexed(cUnit, rlObj.lowReg, r0, rlResult.lowReg, 0, size);
buzbee67bf8852011-08-17 17:51:35 -0700453 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700454 storeValue(cUnit, rlDest, rlResult);
455 } else {
456#if ANDROID_SMP != 0
457 bool isVolatile = dvmIsVolatileField(fieldPtr);
458#else
459 bool isVolatile = false;
460#endif
461 int fieldOffset = fieldPtr->GetOffset().Int32Value();
462 rlObj = loadValue(cUnit, rlObj, kCoreReg);
463 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
464 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
465 NULL);/* null object? */
466 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
467 size, rlObj.sRegLow);
468 if (isVolatile) {
469 oatGenMemBarrier(cUnit, kSY);
470 }
471 storeValue(cUnit, rlDest, rlResult);
buzbee67bf8852011-08-17 17:51:35 -0700472 }
buzbee67bf8852011-08-17 17:51:35 -0700473}
474
475static void genIPutX(CompilationUnit* cUnit, MIR* mir, OpSize size,
476 RegLocation rlSrc, RegLocation rlObj, bool isObject)
477{
buzbeec143c552011-08-20 17:38:58 -0700478 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
479 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700480 RegisterClass regClass = oatRegClassBySize(size);
buzbee34cd9e52011-09-08 14:31:52 -0700481 if (SLOW_FIELD_PATH || fieldPtr == NULL) {
482 getFieldOffset(cUnit, mir);
483 // Field offset in r0
484 rlObj = loadValue(cUnit, rlObj, kCoreReg);
485 rlSrc = loadValue(cUnit, rlSrc, regClass);
486 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
487 NULL);/* null object? */
buzbee67bf8852011-08-17 17:51:35 -0700488 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700489 storeBaseIndexed(cUnit, rlObj.lowReg, r0, rlSrc.lowReg, 0, size);
490 } else {
491#if ANDROID_SMP != 0
492 bool isVolatile = dvmIsVolatileField(fieldPtr);
493#else
494 bool isVolatile = false;
495#endif
496 int fieldOffset = fieldPtr->GetOffset().Int32Value();
497 rlObj = loadValue(cUnit, rlObj, kCoreReg);
498 rlSrc = loadValue(cUnit, rlSrc, regClass);
499 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
500 NULL);/* null object? */
501
502 if (isVolatile) {
503 oatGenMemBarrier(cUnit, kSY);
504 }
505 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
buzbee67bf8852011-08-17 17:51:35 -0700506 }
buzbee67bf8852011-08-17 17:51:35 -0700507 if (isObject) {
508 /* NOTE: marking card based on object head */
509 markGCCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
510 }
511}
512
513static void genIGetWideX(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
514 RegLocation rlObj)
515{
buzbeec143c552011-08-20 17:38:58 -0700516 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
517 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700518 RegLocation rlResult;
buzbee34cd9e52011-09-08 14:31:52 -0700519 if (fieldPtr == NULL) {
520 getFieldOffset(cUnit, mir);
521 // Field offset in r0
522 rlObj = loadValue(cUnit, rlObj, kCoreReg);
523 rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
524 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
525 NULL);/* null object? */
526 opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
527 loadPair(cUnit, r0, rlResult.lowReg, rlResult.highReg);
buzbee67bf8852011-08-17 17:51:35 -0700528 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700529 storeValue(cUnit, rlDest, rlResult);
530 } else {
531#if ANDROID_SMP != 0
532 bool isVolatile = dvmIsVolatileField(fieldPtr);
533#else
534 bool isVolatile = false;
535#endif
536 int fieldOffset = fieldPtr->GetOffset().Int32Value();
537 rlObj = loadValue(cUnit, rlObj, kCoreReg);
538 int regPtr = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700539
buzbee34cd9e52011-09-08 14:31:52 -0700540 assert(rlDest.wide);
541
542 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
543 NULL);/* null object? */
544 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
545 rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
546
547 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
548
549 if (isVolatile) {
550 oatGenMemBarrier(cUnit, kSY);
551 }
552
553 oatFreeTemp(cUnit, regPtr);
554 storeValueWide(cUnit, rlDest, rlResult);
555 }
buzbee67bf8852011-08-17 17:51:35 -0700556}
557
558static void genIPutWideX(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc,
559 RegLocation rlObj)
560{
buzbeec143c552011-08-20 17:38:58 -0700561 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
562 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700563 if (fieldPtr == NULL) {
buzbee34cd9e52011-09-08 14:31:52 -0700564 getFieldOffset(cUnit, mir);
565 // Field offset in r0
566 rlObj = loadValue(cUnit, rlObj, kCoreReg);
567 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
568 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
569 NULL);/* null object? */
570 opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700571 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700572 storePair(cUnit, r0, rlSrc.lowReg, rlSrc.highReg);
573 } else {
574#if ANDROID_SMP != 0
575 bool isVolatile = dvmIsVolatileField(fieldPtr);
576#else
577 bool isVolatile = false;
578#endif
579 int fieldOffset = fieldPtr->GetOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -0700580
buzbee34cd9e52011-09-08 14:31:52 -0700581 rlObj = loadValue(cUnit, rlObj, kCoreReg);
582 int regPtr;
583 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
584 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
585 NULL);/* null object? */
586 regPtr = oatAllocTemp(cUnit);
587 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
588
589 if (isVolatile) {
590 oatGenMemBarrier(cUnit, kSY);
591 }
592 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
593
594 oatFreeTemp(cUnit, regPtr);
595 }
buzbee67bf8852011-08-17 17:51:35 -0700596}
597
598static void genConstClass(CompilationUnit* cUnit, MIR* mir,
599 RegLocation rlDest, RegLocation rlSrc)
600{
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700601 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
buzbee1b4c8592011-08-31 10:43:51 -0700602 Get(mir->dalvikInsn.vB);
603 int mReg = loadCurrMethod(cUnit);
604 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700605 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbee2a475e72011-09-07 17:19:17 -0700606 loadWordDisp(cUnit, mReg, Method::DexCacheResolvedTypesOffset().Int32Value(),
buzbee1b4c8592011-08-31 10:43:51 -0700607 resReg);
608 loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
609 (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
610 if (classPtr != NULL) {
611 // Fast path, we're done - just store result
612 storeValue(cUnit, rlDest, rlResult);
613 } else {
614 // Slow path. Must test at runtime
615 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg,
616 0);
617 // Resolved, store and hop over following code
618 storeValue(cUnit, rlDest, rlResult);
619 ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
620 // TUNING: move slow path to end & remove unconditional branch
621 ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
622 target1->defMask = ENCODE_ALL;
623 // Call out to helper, which will return resolved type in r0
624 loadWordDisp(cUnit, rSELF,
625 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
626 genRegCopy(cUnit, r1, mReg);
627 loadConstant(cUnit, r0, mir->dalvikInsn.vB);
628 opReg(cUnit, kOpBlx, rLR); // resolveTypeFromCode(idx, method)
629 oatClobberCallRegs(cUnit);
630 RegLocation rlResult = oatGetReturn(cUnit);
631 storeValue(cUnit, rlDest, rlResult);
632 // Rejoin code paths
633 ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
634 target2->defMask = ENCODE_ALL;
635 branch1->generic.target = (LIR*)target1;
636 branch2->generic.target = (LIR*)target2;
637 }
buzbee67bf8852011-08-17 17:51:35 -0700638}
639
640static void genConstString(CompilationUnit* cUnit, MIR* mir,
641 RegLocation rlDest, RegLocation rlSrc)
642{
buzbee1b4c8592011-08-31 10:43:51 -0700643 /* All strings should be available at compile time */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700644 const art::String* str = cUnit->method->GetDexCacheStrings()->
buzbee1b4c8592011-08-31 10:43:51 -0700645 Get(mir->dalvikInsn.vB);
646 DCHECK(str != NULL);
buzbee67bf8852011-08-17 17:51:35 -0700647
buzbee1b4c8592011-08-31 10:43:51 -0700648 int mReg = loadCurrMethod(cUnit);
649 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700650 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700651 loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(),
buzbee1b4c8592011-08-31 10:43:51 -0700652 resReg);
653 loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
654 (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700655 storeValue(cUnit, rlDest, rlResult);
656}
657
buzbeedfd3d702011-08-28 12:56:51 -0700658/*
659 * Let helper function take care of everything. Will
660 * call Class::NewInstanceFromCode(type_idx, method);
661 */
buzbee67bf8852011-08-17 17:51:35 -0700662static void genNewInstance(CompilationUnit* cUnit, MIR* mir,
663 RegLocation rlDest)
664{
buzbeedfd3d702011-08-28 12:56:51 -0700665 oatFlushAllRegs(cUnit); /* Everything to home location */
buzbee67bf8852011-08-17 17:51:35 -0700666 loadWordDisp(cUnit, rSELF,
Brian Carlstrom1f870082011-08-23 16:02:11 -0700667 OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
buzbeedfd3d702011-08-28 12:56:51 -0700668 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
669 loadConstant(cUnit, r0, mir->dalvikInsn.vB); // arg0 <- type_id
buzbee67bf8852011-08-17 17:51:35 -0700670 opReg(cUnit, kOpBlx, rLR);
671 oatClobberCallRegs(cUnit);
672 RegLocation rlResult = oatGetReturn(cUnit);
673 storeValue(cUnit, rlDest, rlResult);
674}
675
676void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
677{
678 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700679 OFFSETOF_MEMBER(Thread, pThrowException), rLR);
680 loadValueDirectFixed(cUnit, rlSrc, r1); // Get exception object
buzbee67bf8852011-08-17 17:51:35 -0700681 genRegCopy(cUnit, r0, rSELF);
buzbee1b4c8592011-08-31 10:43:51 -0700682 opReg(cUnit, kOpBlx, rLR); // artThrowException(thread, exception);
buzbee67bf8852011-08-17 17:51:35 -0700683}
684
685static void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
686 RegLocation rlSrc)
687{
buzbee2a475e72011-09-07 17:19:17 -0700688 // May generate a call - use explicit registers
689 oatLockCallTemps(cUnit);
690 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
691 Get(mir->dalvikInsn.vC);
692 int classReg = r2; // Fixed usage
693 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
694 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
695 classReg);
696 loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
697 (sizeof(String*) * mir->dalvikInsn.vC), classReg);
buzbee67bf8852011-08-17 17:51:35 -0700698 if (classPtr == NULL) {
buzbee2a475e72011-09-07 17:19:17 -0700699 // Generate a runtime test
700 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
701 // Not resolved
702 // Call out to helper, which will return resolved type in r0
703 loadWordDisp(cUnit, rSELF,
704 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
705 loadConstant(cUnit, r0, mir->dalvikInsn.vC);
706 opReg(cUnit, kOpBlx, rLR); // resolveTypeFromCode(idx, method)
707 genRegCopy(cUnit, r2, r0); // Align usage with fast path
708 // Rejoin code paths
709 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
710 hopTarget->defMask = ENCODE_ALL;
711 hopBranch->generic.target = (LIR*)hopTarget;
buzbee67bf8852011-08-17 17:51:35 -0700712 }
buzbee2a475e72011-09-07 17:19:17 -0700713 // At this point, r2 has class
714 loadValueDirectFixed(cUnit, rlSrc, r3); /* Ref */
buzbee67bf8852011-08-17 17:51:35 -0700715 /* When taken r0 has NULL which can be used for store directly */
buzbee2a475e72011-09-07 17:19:17 -0700716 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r3, 0);
717 /* load object->clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700718 assert(Object::ClassOffset().Int32Value() == 0);
buzbee2a475e72011-09-07 17:19:17 -0700719 loadWordDisp(cUnit, r3, Object::ClassOffset().Int32Value(), r1);
buzbee67bf8852011-08-17 17:51:35 -0700720 /* r1 now contains object->clazz */
721 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700722 OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -0700723 loadConstant(cUnit, r0, 1); /* Assume true */
724 opRegReg(cUnit, kOpCmp, r1, r2);
725 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq);
buzbee2a475e72011-09-07 17:19:17 -0700726 genRegCopy(cUnit, r0, r3);
buzbee67bf8852011-08-17 17:51:35 -0700727 genRegCopy(cUnit, r1, r2);
728 opReg(cUnit, kOpBlx, rLR);
729 oatClobberCallRegs(cUnit);
730 /* branch target here */
731 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
732 target->defMask = ENCODE_ALL;
buzbee2a475e72011-09-07 17:19:17 -0700733 RegLocation rlResult = oatGetReturn(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700734 storeValue(cUnit, rlDest, rlResult);
735 branch1->generic.target = (LIR*)target;
736 branch2->generic.target = (LIR*)target;
737}
738
739static void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
740{
buzbee2a475e72011-09-07 17:19:17 -0700741 // May generate a call - use explicit registers
742 oatLockCallTemps(cUnit);
743 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
744 Get(mir->dalvikInsn.vB);
745 int classReg = r2; // Fixed usage
746 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
747 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
748 classReg);
749 loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
750 (sizeof(String*) * mir->dalvikInsn.vB), classReg);
buzbee67bf8852011-08-17 17:51:35 -0700751 if (classPtr == NULL) {
buzbee2a475e72011-09-07 17:19:17 -0700752 // Generate a runtime test
753 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
754 // Not resolved
755 // Call out to helper, which will return resolved type in r0
756 loadWordDisp(cUnit, rSELF,
757 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
758 loadConstant(cUnit, r0, mir->dalvikInsn.vB);
759 opReg(cUnit, kOpBlx, rLR); // resolveTypeFromCode(idx, method)
760 genRegCopy(cUnit, r2, r0); // Align usage with fast path
761 // Rejoin code paths
762 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
763 hopTarget->defMask = ENCODE_ALL;
764 hopBranch->generic.target = (LIR*)hopTarget;
buzbee67bf8852011-08-17 17:51:35 -0700765 }
buzbee2a475e72011-09-07 17:19:17 -0700766 // At this point, r2 has class
767 loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */
768 /* Null is OK - continue */
769 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
770 /* load object->clazz */
771 assert(Object::ClassOffset().Int32Value() == 0);
772 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
773 /* r1 now contains object->clazz */
buzbee67bf8852011-08-17 17:51:35 -0700774 loadWordDisp(cUnit, rSELF,
buzbee2a475e72011-09-07 17:19:17 -0700775 OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
776 opRegReg(cUnit, kOpCmp, r1, r2);
777 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
778 genRegCopy(cUnit, r0, r1);
779 genRegCopy(cUnit, r1, r2);
buzbee67bf8852011-08-17 17:51:35 -0700780 opReg(cUnit, kOpBlx, rLR);
781 oatClobberCallRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700782 /* branch target here */
buzbee67bf8852011-08-17 17:51:35 -0700783 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
784 target->defMask = ENCODE_ALL;
785 branch1->generic.target = (LIR*)target;
786 branch2->generic.target = (LIR*)target;
787}
788
789static void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest,
790 RegLocation rlSrc)
791{
792 RegLocation rlResult;
793 rlSrc = loadValue(cUnit, rlSrc, kFPReg);
794 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
795 newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
796 storeValue(cUnit, rlDest, rlResult);
797}
798
799static void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest,
800 RegLocation rlSrc)
801{
802 RegLocation rlResult;
803 rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
804 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
805 newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
806 S2D(rlSrc.lowReg, rlSrc.highReg));
807 storeValueWide(cUnit, rlDest, rlResult);
808}
809
buzbee439c4fa2011-08-27 15:59:07 -0700810static void freeRegLocTemps(CompilationUnit* cUnit, RegLocation rlKeep,
811 RegLocation rlFree)
buzbee67bf8852011-08-17 17:51:35 -0700812{
buzbee439c4fa2011-08-27 15:59:07 -0700813 if ((rlFree.lowReg != rlKeep.lowReg) && (rlFree.lowReg != rlKeep.highReg))
814 oatFreeTemp(cUnit, rlFree.lowReg);
815 if ((rlFree.highReg != rlKeep.lowReg) && (rlFree.highReg != rlKeep.highReg))
816 oatFreeTemp(cUnit, rlFree.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700817}
818
819static void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp,
820 OpKind secondOp, RegLocation rlDest,
821 RegLocation rlSrc1, RegLocation rlSrc2)
822{
buzbee9e0f9b02011-08-24 15:32:46 -0700823 /*
824 * NOTE: This is the one place in the code in which we might have
825 * as many as six live temporary registers. There are 5 in the normal
826 * set for Arm. Until we have spill capabilities, temporarily add
827 * lr to the temp set. It is safe to do this locally, but note that
828 * lr is used explicitly elsewhere in the code generator and cannot
829 * normally be used as a general temp register.
830 */
buzbee67bf8852011-08-17 17:51:35 -0700831 RegLocation rlResult;
buzbee9e0f9b02011-08-24 15:32:46 -0700832 oatMarkTemp(cUnit, rLR); // Add lr to the temp pool
833 oatFreeTemp(cUnit, rLR); // and make it available
buzbee67bf8852011-08-17 17:51:35 -0700834 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
835 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
836 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
837 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
838 opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
839 rlSrc2.highReg);
buzbee439c4fa2011-08-27 15:59:07 -0700840 /*
841 * NOTE: If rlDest refers to a frame variable in a large frame, the
842 * following storeValueWide might need to allocate a temp register.
843 * To further work around the lack of a spill capability, explicitly
844 * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult.
845 * Remove when spill is functional.
846 */
847 freeRegLocTemps(cUnit, rlResult, rlSrc1);
848 freeRegLocTemps(cUnit, rlResult, rlSrc2);
buzbee67bf8852011-08-17 17:51:35 -0700849 storeValueWide(cUnit, rlDest, rlResult);
buzbee9e0f9b02011-08-24 15:32:46 -0700850 oatClobber(cUnit, rLR);
851 oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool
buzbee67bf8852011-08-17 17:51:35 -0700852}
853
854void oatInitializeRegAlloc(CompilationUnit* cUnit)
855{
856 int numRegs = sizeof(coreRegs)/sizeof(*coreRegs);
857 int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs);
858 int numTemps = sizeof(coreTemps)/sizeof(*coreTemps);
859 int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs);
860 int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps);
861 RegisterPool *pool = (RegisterPool *)oatNew(sizeof(*pool), true);
862 cUnit->regPool = pool;
863 pool->numCoreRegs = numRegs;
864 pool->coreRegs = (RegisterInfo *)
865 oatNew(numRegs * sizeof(*cUnit->regPool->coreRegs), true);
866 pool->numFPRegs = numFPRegs;
867 pool->FPRegs = (RegisterInfo *)
868 oatNew(numFPRegs * sizeof(*cUnit->regPool->FPRegs), true);
869 oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs);
870 oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs);
871 // Keep special registers from being allocated
872 for (int i = 0; i < numReserved; i++) {
873 oatMarkInUse(cUnit, reservedRegs[i]);
874 }
875 // Mark temp regs - all others not in use can be used for promotion
876 for (int i = 0; i < numTemps; i++) {
877 oatMarkTemp(cUnit, coreTemps[i]);
878 }
879 for (int i = 0; i < numFPTemps; i++) {
880 oatMarkTemp(cUnit, fpTemps[i]);
881 }
882 pool->nullCheckedRegs =
883 oatAllocBitVector(cUnit->numSSARegs, false);
884}
885
886/*
887 * Handle simple case (thin lock) inline. If it's complicated, bail
888 * out to the heavyweight lock/unlock routines. We'll use dedicated
889 * registers here in order to be in the right position in case we
890 * to bail to dvm[Lock/Unlock]Object(self, object)
891 *
892 * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
893 * r1 -> object [arg1 for dvm[Lock/Unlock]Object
894 * r2 -> intial contents of object->lock, later result of strex
895 * r3 -> self->threadId
896 * r12 -> allow to be used by utilities as general temp
897 *
898 * The result of the strex is 0 if we acquire the lock.
899 *
900 * See comments in Sync.c for the layout of the lock word.
901 * Of particular interest to this code is the test for the
902 * simple case - which we handle inline. For monitor enter, the
903 * simple case is thin lock, held by no-one. For monitor exit,
904 * the simple case is thin lock, held by the unlocking thread with
905 * a recurse count of 0.
906 *
907 * A minor complication is that there is a field in the lock word
908 * unrelated to locking: the hash state. This field must be ignored, but
909 * preserved.
910 *
911 */
912static void genMonitorEnter(CompilationUnit* cUnit, MIR* mir,
913 RegLocation rlSrc)
914{
915 ArmLIR* target;
916 ArmLIR* hopTarget;
917 ArmLIR* branch;
918 ArmLIR* hopBranch;
919
920 oatFlushAllRegs(cUnit);
buzbeec143c552011-08-20 17:38:58 -0700921 assert(art::Monitor::kLwShapeThin == 0);
buzbee67bf8852011-08-17 17:51:35 -0700922 loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700923 oatLockCallTemps(cUnit); // Prepare for explicit register usage
buzbee67bf8852011-08-17 17:51:35 -0700924 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
buzbeec143c552011-08-20 17:38:58 -0700925 loadWordDisp(cUnit, rSELF, Thread::IdOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -0700926 newLIR3(cUnit, kThumb2Ldrex, r2, r1,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700927 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
buzbeec143c552011-08-20 17:38:58 -0700928 // Align owner
929 opRegImm(cUnit, kOpLsl, r3, art::Monitor::kLwLockOwnerShift);
buzbee67bf8852011-08-17 17:51:35 -0700930 // Is lock unheld on lock or held by us (==threadId) on unlock?
buzbeec143c552011-08-20 17:38:58 -0700931 newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, art::Monitor::kLwLockOwnerShift
932 - 1);
933 newLIR3(cUnit, kThumb2Bfc, r2, art::Monitor::kLwHashStateShift,
934 art::Monitor::kLwLockOwnerShift - 1);
buzbee67bf8852011-08-17 17:51:35 -0700935 hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0);
buzbeec143c552011-08-20 17:38:58 -0700936 newLIR4(cUnit, kThumb2Strex, r2, r3, r1,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700937 Object::MonitorOffset().Int32Value() >> 2);
buzbee67bf8852011-08-17 17:51:35 -0700938 oatGenMemBarrier(cUnit, kSY);
939 branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
940
941 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
942 hopTarget->defMask = ENCODE_ALL;
943 hopBranch->generic.target = (LIR*)hopTarget;
944
buzbee1b4c8592011-08-31 10:43:51 -0700945 // Go expensive route - artLockObjectFromCode(self, obj);
946 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700947 rLR);
948 genRegCopy(cUnit, r0, rSELF);
949 newLIR1(cUnit, kThumbBlxR, rLR);
950
951 // Resume here
952 target = newLIR0(cUnit, kArmPseudoTargetLabel);
953 target->defMask = ENCODE_ALL;
954 branch->generic.target = (LIR*)target;
955}
956
957/*
958 * For monitor unlock, we don't have to use ldrex/strex. Once
959 * we've determined that the lock is thin and that we own it with
960 * a zero recursion count, it's safe to punch it back to the
961 * initial, unlock thin state with a store word.
962 */
963static void genMonitorExit(CompilationUnit* cUnit, MIR* mir,
964 RegLocation rlSrc)
965{
966 ArmLIR* target;
967 ArmLIR* branch;
968 ArmLIR* hopTarget;
969 ArmLIR* hopBranch;
970
buzbeec143c552011-08-20 17:38:58 -0700971 assert(art::Monitor::kLwShapeThin == 0);
buzbee67bf8852011-08-17 17:51:35 -0700972 oatFlushAllRegs(cUnit);
973 loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700974 oatLockCallTemps(cUnit); // Prepare for explicit register usage
buzbee67bf8852011-08-17 17:51:35 -0700975 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700976 loadWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r2); // Get lock
buzbeec143c552011-08-20 17:38:58 -0700977 loadWordDisp(cUnit, rSELF, Thread::IdOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -0700978 // Is lock unheld on lock or held by us (==threadId) on unlock?
buzbeec143c552011-08-20 17:38:58 -0700979 opRegRegImm(cUnit, kOpAnd, r12, r2, (art::Monitor::kLwHashStateMask <<
980 art::Monitor::kLwHashStateShift));
981 // Align owner
982 opRegImm(cUnit, kOpLsl, r3, art::Monitor::kLwLockOwnerShift);
983 newLIR3(cUnit, kThumb2Bfc, r2, art::Monitor::kLwHashStateShift,
984 art::Monitor::kLwLockOwnerShift - 1);
buzbee67bf8852011-08-17 17:51:35 -0700985 opRegReg(cUnit, kOpSub, r2, r3);
986 hopBranch = opCondBranch(cUnit, kArmCondNe);
987 oatGenMemBarrier(cUnit, kSY);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700988 storeWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r12);
buzbee67bf8852011-08-17 17:51:35 -0700989 branch = opNone(cUnit, kOpUncondBr);
990
991 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
992 hopTarget->defMask = ENCODE_ALL;
993 hopBranch->generic.target = (LIR*)hopTarget;
994
buzbee1b4c8592011-08-31 10:43:51 -0700995 // Go expensive route - UnlockObjectFromCode(self, obj);
996 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700997 rLR);
998 genRegCopy(cUnit, r0, rSELF);
999 newLIR1(cUnit, kThumbBlxR, rLR);
1000
1001 // Resume here
1002 target = newLIR0(cUnit, kArmPseudoTargetLabel);
1003 target->defMask = ENCODE_ALL;
1004 branch->generic.target = (LIR*)target;
1005}
1006
1007/*
1008 * 64-bit 3way compare function.
1009 * mov rX, #-1
1010 * cmp op1hi, op2hi
1011 * blt done
1012 * bgt flip
1013 * sub rX, op1lo, op2lo (treat as unsigned)
1014 * beq done
1015 * ite hi
1016 * mov(hi) rX, #-1
1017 * mov(!hi) rX, #1
1018 * flip:
1019 * neg rX
1020 * done:
1021 */
1022static void genCmpLong(CompilationUnit* cUnit, MIR* mir,
1023 RegLocation rlDest, RegLocation rlSrc1,
1024 RegLocation rlSrc2)
1025{
1026 RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
1027 ArmLIR* target1;
1028 ArmLIR* target2;
1029 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
1030 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1031 rlTemp.lowReg = oatAllocTemp(cUnit);
1032 loadConstant(cUnit, rlTemp.lowReg, -1);
1033 opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
1034 ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt);
1035 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt);
1036 opRegRegReg(cUnit, kOpSub, rlTemp.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
1037 ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq);
1038
1039 genIT(cUnit, kArmCondHi, "E");
1040 newLIR2(cUnit, kThumb2MovImmShift, rlTemp.lowReg, modifiedImmediate(-1));
1041 loadConstant(cUnit, rlTemp.lowReg, 1);
1042 genBarrier(cUnit);
1043
1044 target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
1045 target2->defMask = -1;
1046 opRegReg(cUnit, kOpNeg, rlTemp.lowReg, rlTemp.lowReg);
1047
1048 target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
1049 target1->defMask = -1;
1050
1051 storeValue(cUnit, rlDest, rlTemp);
1052
1053 branch1->generic.target = (LIR*)target1;
1054 branch2->generic.target = (LIR*)target2;
1055 branch3->generic.target = branch1->generic.target;
1056}
1057
1058static void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit,
1059 RegLocation rlSrc, RegLocation rlResult, int lit,
1060 int firstBit, int secondBit)
1061{
1062 opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
1063 encodeShift(kArmLsl, secondBit - firstBit));
1064 if (firstBit != 0) {
1065 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
1066 }
1067}
1068
1069static bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset,
1070 int srcSize, int tgtSize)
1071{
1072 /*
1073 * Don't optimize the register usage since it calls out to support
1074 * functions
1075 */
1076 RegLocation rlSrc;
1077 RegLocation rlDest;
1078 oatFlushAllRegs(cUnit); /* Send everything to home location */
1079 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1080 if (srcSize == 1) {
1081 rlSrc = oatGetSrc(cUnit, mir, 0);
1082 loadValueDirectFixed(cUnit, rlSrc, r0);
1083 } else {
1084 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
1085 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
1086 }
1087 opReg(cUnit, kOpBlx, rLR);
1088 oatClobberCallRegs(cUnit);
1089 if (tgtSize == 1) {
1090 RegLocation rlResult;
1091 rlDest = oatGetDest(cUnit, mir, 0);
1092 rlResult = oatGetReturn(cUnit);
1093 storeValue(cUnit, rlDest, rlResult);
1094 } else {
1095 RegLocation rlResult;
1096 rlDest = oatGetDestWide(cUnit, mir, 0, 1);
1097 rlResult = oatGetReturnWide(cUnit);
1098 storeValueWide(cUnit, rlDest, rlResult);
1099 }
1100 return false;
1101}
1102
1103static bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir,
1104 RegLocation rlDest, RegLocation rlSrc1,
1105 RegLocation rlSrc2)
1106{
1107 RegLocation rlResult;
1108 int funcOffset;
1109
1110 switch (mir->dalvikInsn.opcode) {
1111 case OP_ADD_FLOAT_2ADDR:
1112 case OP_ADD_FLOAT:
1113 funcOffset = OFFSETOF_MEMBER(Thread, pFadd);
1114 break;
1115 case OP_SUB_FLOAT_2ADDR:
1116 case OP_SUB_FLOAT:
1117 funcOffset = OFFSETOF_MEMBER(Thread, pFsub);
1118 break;
1119 case OP_DIV_FLOAT_2ADDR:
1120 case OP_DIV_FLOAT:
1121 funcOffset = OFFSETOF_MEMBER(Thread, pFdiv);
1122 break;
1123 case OP_MUL_FLOAT_2ADDR:
1124 case OP_MUL_FLOAT:
1125 funcOffset = OFFSETOF_MEMBER(Thread, pFmul);
1126 break;
1127 case OP_REM_FLOAT_2ADDR:
1128 case OP_REM_FLOAT:
1129 funcOffset = OFFSETOF_MEMBER(Thread, pFmodf);
1130 break;
1131 case OP_NEG_FLOAT: {
1132 genNegFloat(cUnit, rlDest, rlSrc1);
1133 return false;
1134 }
1135 default:
1136 return true;
1137 }
1138 oatFlushAllRegs(cUnit); /* Send everything to home location */
1139 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1140 loadValueDirectFixed(cUnit, rlSrc1, r0);
1141 loadValueDirectFixed(cUnit, rlSrc2, r1);
1142 opReg(cUnit, kOpBlx, rLR);
1143 oatClobberCallRegs(cUnit);
1144 rlResult = oatGetReturn(cUnit);
1145 storeValue(cUnit, rlDest, rlResult);
1146 return false;
1147}
1148
1149static bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir,
1150 RegLocation rlDest, RegLocation rlSrc1,
1151 RegLocation rlSrc2)
1152{
1153 RegLocation rlResult;
1154 int funcOffset;
1155
1156 switch (mir->dalvikInsn.opcode) {
1157 case OP_ADD_DOUBLE_2ADDR:
1158 case OP_ADD_DOUBLE:
1159 funcOffset = OFFSETOF_MEMBER(Thread, pDadd);
1160 break;
1161 case OP_SUB_DOUBLE_2ADDR:
1162 case OP_SUB_DOUBLE:
1163 funcOffset = OFFSETOF_MEMBER(Thread, pDsub);
1164 break;
1165 case OP_DIV_DOUBLE_2ADDR:
1166 case OP_DIV_DOUBLE:
1167 funcOffset = OFFSETOF_MEMBER(Thread, pDdiv);
1168 break;
1169 case OP_MUL_DOUBLE_2ADDR:
1170 case OP_MUL_DOUBLE:
1171 funcOffset = OFFSETOF_MEMBER(Thread, pDmul);
1172 break;
1173 case OP_REM_DOUBLE_2ADDR:
1174 case OP_REM_DOUBLE:
1175 funcOffset = OFFSETOF_MEMBER(Thread, pFmod);
1176 break;
1177 case OP_NEG_DOUBLE: {
1178 genNegDouble(cUnit, rlDest, rlSrc1);
1179 return false;
1180 }
1181 default:
1182 return true;
1183 }
1184 oatFlushAllRegs(cUnit); /* Send everything to home location */
1185 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1186 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1187 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1188 opReg(cUnit, kOpBlx, rLR);
1189 oatClobberCallRegs(cUnit);
1190 rlResult = oatGetReturnWide(cUnit);
1191 storeValueWide(cUnit, rlDest, rlResult);
1192 return false;
1193}
1194
1195static bool genConversionPortable(CompilationUnit* cUnit, MIR* mir)
1196{
1197 Opcode opcode = mir->dalvikInsn.opcode;
1198
1199 switch (opcode) {
1200 case OP_INT_TO_FLOAT:
1201 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f),
1202 1, 1);
1203 case OP_FLOAT_TO_INT:
1204 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz),
1205 1, 1);
1206 case OP_DOUBLE_TO_FLOAT:
1207 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f),
1208 2, 1);
1209 case OP_FLOAT_TO_DOUBLE:
1210 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d),
1211 1, 2);
1212 case OP_INT_TO_DOUBLE:
1213 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d),
1214 1, 2);
1215 case OP_DOUBLE_TO_INT:
1216 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz),
1217 2, 1);
1218 case OP_FLOAT_TO_LONG:
1219 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001220 pF2l), 1, 2);
buzbee67bf8852011-08-17 17:51:35 -07001221 case OP_LONG_TO_FLOAT:
1222 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f),
1223 2, 1);
1224 case OP_DOUBLE_TO_LONG:
1225 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001226 pD2l), 2, 2);
buzbee67bf8852011-08-17 17:51:35 -07001227 case OP_LONG_TO_DOUBLE:
1228 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d),
1229 2, 2);
1230 default:
1231 return true;
1232 }
1233 return false;
1234}
1235
1236/* Generate conditional branch instructions */
1237static ArmLIR* genConditionalBranch(CompilationUnit* cUnit,
1238 ArmConditionCode cond,
1239 ArmLIR* target)
1240{
1241 ArmLIR* branch = opCondBranch(cUnit, cond);
1242 branch->generic.target = (LIR*) target;
1243 return branch;
1244}
1245
1246/* Generate a unconditional branch to go to the interpreter */
1247static inline ArmLIR* genTrap(CompilationUnit* cUnit, int dOffset,
1248 ArmLIR* pcrLabel)
1249{
1250 ArmLIR* branch = opNone(cUnit, kOpUncondBr);
1251 return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
1252}
1253
1254/*
1255 * Generate array store
1256 *
1257 */
buzbee1b4c8592011-08-31 10:43:51 -07001258static void genArrayObjPut(CompilationUnit* cUnit, MIR* mir,
1259 RegLocation rlArray, RegLocation rlIndex,
1260 RegLocation rlSrc, int scale)
buzbee67bf8852011-08-17 17:51:35 -07001261{
1262 RegisterClass regClass = oatRegClassBySize(kWord);
buzbeec143c552011-08-20 17:38:58 -07001263 int lenOffset = Array::LengthOffset().Int32Value();
1264 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001265
1266 /* Make sure it's a legal object Put. Use direct regs at first */
1267 loadValueDirectFixed(cUnit, rlArray, r1);
1268 loadValueDirectFixed(cUnit, rlSrc, r0);
1269
1270 /* null array object? */
1271 ArmLIR* pcrLabel = NULL;
1272
1273 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
1274 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, r1,
1275 mir->offset, NULL);
1276 }
1277 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -07001278 OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -07001279 /* Get the array's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001280 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), r1);
buzbee67bf8852011-08-17 17:51:35 -07001281 /* Get the object's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001282 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r0);
buzbee67bf8852011-08-17 17:51:35 -07001283 opReg(cUnit, kOpBlx, rLR);
1284 oatClobberCallRegs(cUnit);
1285
1286 // Now, redo loadValues in case they didn't survive the call
1287
1288 int regPtr;
1289 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1290 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1291
1292 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1293 oatClobber(cUnit, rlArray.lowReg);
1294 regPtr = rlArray.lowReg;
1295 } else {
1296 regPtr = oatAllocTemp(cUnit);
1297 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1298 }
1299
1300 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
1301 int regLen = oatAllocTemp(cUnit);
1302 //NOTE: max live temps(4) here.
1303 /* Get len */
1304 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1305 /* regPtr -> array data */
1306 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1307 genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
1308 pcrLabel);
1309 oatFreeTemp(cUnit, regLen);
1310 } else {
1311 /* regPtr -> array data */
1312 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1313 }
1314 /* at this point, regPtr points to array, 2 live temps */
1315 rlSrc = loadValue(cUnit, rlSrc, regClass);
1316 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1317 scale, kWord);
1318}
1319
1320/*
1321 * Generate array load
1322 */
1323static void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
1324 RegLocation rlArray, RegLocation rlIndex,
1325 RegLocation rlDest, int scale)
1326{
1327 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001328 int lenOffset = Array::LengthOffset().Int32Value();
1329 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001330 RegLocation rlResult;
1331 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1332 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1333 int regPtr;
1334
1335 /* null object? */
1336 ArmLIR* pcrLabel = NULL;
1337
1338 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
1339 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow,
1340 rlArray.lowReg, mir->offset, NULL);
1341 }
1342
1343 regPtr = oatAllocTemp(cUnit);
1344
1345 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
1346 int regLen = oatAllocTemp(cUnit);
1347 /* Get len */
1348 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1349 /* regPtr -> array data */
1350 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
1351 genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
1352 pcrLabel);
1353 oatFreeTemp(cUnit, regLen);
1354 } else {
1355 /* regPtr -> array data */
1356 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
1357 }
buzbeee9a72f62011-09-04 17:59:07 -07001358 oatFreeTemp(cUnit, rlArray.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001359 if ((size == kLong) || (size == kDouble)) {
1360 if (scale) {
1361 int rNewIndex = oatAllocTemp(cUnit);
1362 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1363 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1364 oatFreeTemp(cUnit, rNewIndex);
1365 } else {
1366 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1367 }
buzbeee9a72f62011-09-04 17:59:07 -07001368 oatFreeTemp(cUnit, rlIndex.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001369 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1370
1371 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
1372
1373 oatFreeTemp(cUnit, regPtr);
1374 storeValueWide(cUnit, rlDest, rlResult);
1375 } else {
1376 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1377
1378 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
1379 scale, size);
1380
1381 oatFreeTemp(cUnit, regPtr);
1382 storeValue(cUnit, rlDest, rlResult);
1383 }
1384}
1385
1386/*
1387 * Generate array store
1388 *
1389 */
1390static void genArrayPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
1391 RegLocation rlArray, RegLocation rlIndex,
1392 RegLocation rlSrc, int scale)
1393{
1394 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001395 int lenOffset = Array::LengthOffset().Int32Value();
1396 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001397
1398 int regPtr;
1399 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1400 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1401
1402 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1403 oatClobber(cUnit, rlArray.lowReg);
1404 regPtr = rlArray.lowReg;
1405 } else {
1406 regPtr = oatAllocTemp(cUnit);
1407 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1408 }
1409
1410 /* null object? */
1411 ArmLIR* pcrLabel = NULL;
1412
1413 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
1414 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg,
1415 mir->offset, NULL);
1416 }
1417
1418 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
1419 int regLen = oatAllocTemp(cUnit);
1420 //NOTE: max live temps(4) here.
1421 /* Get len */
1422 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1423 /* regPtr -> array data */
1424 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1425 genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
1426 pcrLabel);
1427 oatFreeTemp(cUnit, regLen);
1428 } else {
1429 /* regPtr -> array data */
1430 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1431 }
1432 /* at this point, regPtr points to array, 2 live temps */
1433 if ((size == kLong) || (size == kDouble)) {
1434 //TODO: need specific wide routine that can handle fp regs
1435 if (scale) {
1436 int rNewIndex = oatAllocTemp(cUnit);
1437 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1438 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1439 oatFreeTemp(cUnit, rNewIndex);
1440 } else {
1441 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1442 }
1443 rlSrc = loadValueWide(cUnit, rlSrc, regClass);
1444
1445 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
1446
1447 oatFreeTemp(cUnit, regPtr);
1448 } else {
1449 rlSrc = loadValue(cUnit, rlSrc, regClass);
1450
1451 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1452 scale, size);
1453 }
1454}
1455
1456static bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir,
1457 RegLocation rlDest, RegLocation rlSrc1,
1458 RegLocation rlShift)
1459{
buzbee54330722011-08-23 16:46:55 -07001460 int funcOffset;
buzbee67bf8852011-08-17 17:51:35 -07001461
buzbee67bf8852011-08-17 17:51:35 -07001462 switch( mir->dalvikInsn.opcode) {
1463 case OP_SHL_LONG:
1464 case OP_SHL_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001465 funcOffset = OFFSETOF_MEMBER(Thread, pShlLong);
buzbee67bf8852011-08-17 17:51:35 -07001466 break;
1467 case OP_SHR_LONG:
1468 case OP_SHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001469 funcOffset = OFFSETOF_MEMBER(Thread, pShrLong);
buzbee67bf8852011-08-17 17:51:35 -07001470 break;
1471 case OP_USHR_LONG:
1472 case OP_USHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001473 funcOffset = OFFSETOF_MEMBER(Thread, pUshrLong);
buzbee67bf8852011-08-17 17:51:35 -07001474 break;
1475 default:
buzbee54330722011-08-23 16:46:55 -07001476 LOG(FATAL) << "Unexpected case";
buzbee67bf8852011-08-17 17:51:35 -07001477 return true;
1478 }
buzbee54330722011-08-23 16:46:55 -07001479 oatFlushAllRegs(cUnit); /* Send everything to home location */
1480 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1481 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1482 loadValueDirect(cUnit, rlShift, r2);
1483 opReg(cUnit, kOpBlx, rLR);
1484 oatClobberCallRegs(cUnit);
1485 RegLocation rlResult = oatGetReturnWide(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001486 storeValueWide(cUnit, rlDest, rlResult);
1487 return false;
1488}
1489
1490static bool genArithOpLong(CompilationUnit* cUnit, MIR* mir,
1491 RegLocation rlDest, RegLocation rlSrc1,
1492 RegLocation rlSrc2)
1493{
1494 RegLocation rlResult;
1495 OpKind firstOp = kOpBkpt;
1496 OpKind secondOp = kOpBkpt;
1497 bool callOut = false;
1498 int funcOffset;
1499 int retReg = r0;
1500
1501 switch (mir->dalvikInsn.opcode) {
1502 case OP_NOT_LONG:
1503 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1504 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1505 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1506 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
1507 storeValueWide(cUnit, rlDest, rlResult);
1508 return false;
1509 break;
1510 case OP_ADD_LONG:
1511 case OP_ADD_LONG_2ADDR:
1512 firstOp = kOpAdd;
1513 secondOp = kOpAdc;
1514 break;
1515 case OP_SUB_LONG:
1516 case OP_SUB_LONG_2ADDR:
1517 firstOp = kOpSub;
1518 secondOp = kOpSbc;
1519 break;
1520 case OP_MUL_LONG:
1521 case OP_MUL_LONG_2ADDR:
buzbee439c4fa2011-08-27 15:59:07 -07001522 callOut = true;
1523 retReg = r0;
1524 funcOffset = OFFSETOF_MEMBER(Thread, pLmul);
1525 break;
buzbee67bf8852011-08-17 17:51:35 -07001526 case OP_DIV_LONG:
1527 case OP_DIV_LONG_2ADDR:
1528 callOut = true;
1529 retReg = r0;
1530 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1531 break;
1532 /* NOTE - result is in r2/r3 instead of r0/r1 */
1533 case OP_REM_LONG:
1534 case OP_REM_LONG_2ADDR:
1535 callOut = true;
1536 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1537 retReg = r2;
1538 break;
1539 case OP_AND_LONG_2ADDR:
1540 case OP_AND_LONG:
1541 firstOp = kOpAnd;
1542 secondOp = kOpAnd;
1543 break;
1544 case OP_OR_LONG:
1545 case OP_OR_LONG_2ADDR:
1546 firstOp = kOpOr;
1547 secondOp = kOpOr;
1548 break;
1549 case OP_XOR_LONG:
1550 case OP_XOR_LONG_2ADDR:
1551 firstOp = kOpXor;
1552 secondOp = kOpXor;
1553 break;
1554 case OP_NEG_LONG: {
1555 //TUNING: can improve this using Thumb2 code
1556 int tReg = oatAllocTemp(cUnit);
1557 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1558 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1559 loadConstantNoClobber(cUnit, tReg, 0);
1560 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1561 tReg, rlSrc2.lowReg);
1562 opRegReg(cUnit, kOpSbc, tReg, rlSrc2.highReg);
1563 genRegCopy(cUnit, rlResult.highReg, tReg);
1564 storeValueWide(cUnit, rlDest, rlResult);
1565 return false;
1566 }
1567 default:
1568 LOG(FATAL) << "Invalid long arith op";
1569 }
1570 if (!callOut) {
1571 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
1572 } else {
1573 // Adjust return regs in to handle case of rem returning r2/r3
1574 oatFlushAllRegs(cUnit); /* Send everything to home location */
1575 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1576 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1577 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1578 opReg(cUnit, kOpBlx, rLR);
1579 oatClobberCallRegs(cUnit);
1580 if (retReg == r0)
1581 rlResult = oatGetReturnWide(cUnit);
1582 else
1583 rlResult = oatGetReturnWideAlt(cUnit);
1584 storeValueWide(cUnit, rlDest, rlResult);
1585 }
1586 return false;
1587}
1588
1589static bool genArithOpInt(CompilationUnit* cUnit, MIR* mir,
1590 RegLocation rlDest, RegLocation rlSrc1,
1591 RegLocation rlSrc2)
1592{
1593 OpKind op = kOpBkpt;
1594 bool callOut = false;
1595 bool checkZero = false;
1596 bool unary = false;
1597 int retReg = r0;
1598 int funcOffset;
1599 RegLocation rlResult;
1600 bool shiftOp = false;
1601
1602 switch (mir->dalvikInsn.opcode) {
1603 case OP_NEG_INT:
1604 op = kOpNeg;
1605 unary = true;
1606 break;
1607 case OP_NOT_INT:
1608 op = kOpMvn;
1609 unary = true;
1610 break;
1611 case OP_ADD_INT:
1612 case OP_ADD_INT_2ADDR:
1613 op = kOpAdd;
1614 break;
1615 case OP_SUB_INT:
1616 case OP_SUB_INT_2ADDR:
1617 op = kOpSub;
1618 break;
1619 case OP_MUL_INT:
1620 case OP_MUL_INT_2ADDR:
1621 op = kOpMul;
1622 break;
1623 case OP_DIV_INT:
1624 case OP_DIV_INT_2ADDR:
1625 callOut = true;
1626 checkZero = true;
1627 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1628 retReg = r0;
1629 break;
1630 /* NOTE: returns in r1 */
1631 case OP_REM_INT:
1632 case OP_REM_INT_2ADDR:
1633 callOut = true;
1634 checkZero = true;
1635 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1636 retReg = r1;
1637 break;
1638 case OP_AND_INT:
1639 case OP_AND_INT_2ADDR:
1640 op = kOpAnd;
1641 break;
1642 case OP_OR_INT:
1643 case OP_OR_INT_2ADDR:
1644 op = kOpOr;
1645 break;
1646 case OP_XOR_INT:
1647 case OP_XOR_INT_2ADDR:
1648 op = kOpXor;
1649 break;
1650 case OP_SHL_INT:
1651 case OP_SHL_INT_2ADDR:
1652 shiftOp = true;
1653 op = kOpLsl;
1654 break;
1655 case OP_SHR_INT:
1656 case OP_SHR_INT_2ADDR:
1657 shiftOp = true;
1658 op = kOpAsr;
1659 break;
1660 case OP_USHR_INT:
1661 case OP_USHR_INT_2ADDR:
1662 shiftOp = true;
1663 op = kOpLsr;
1664 break;
1665 default:
1666 LOG(FATAL) << "Invalid word arith op: " <<
1667 (int)mir->dalvikInsn.opcode;
1668 }
1669 if (!callOut) {
1670 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
1671 if (unary) {
1672 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1673 opRegReg(cUnit, op, rlResult.lowReg,
1674 rlSrc1.lowReg);
1675 } else {
1676 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
1677 if (shiftOp) {
1678 int tReg = oatAllocTemp(cUnit);
1679 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
1680 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1681 opRegRegReg(cUnit, op, rlResult.lowReg,
1682 rlSrc1.lowReg, tReg);
1683 oatFreeTemp(cUnit, tReg);
1684 } else {
1685 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1686 opRegRegReg(cUnit, op, rlResult.lowReg,
1687 rlSrc1.lowReg, rlSrc2.lowReg);
1688 }
1689 }
1690 storeValue(cUnit, rlDest, rlResult);
1691 } else {
1692 RegLocation rlResult;
1693 oatFlushAllRegs(cUnit); /* Send everything to home location */
1694 loadValueDirectFixed(cUnit, rlSrc2, r1);
1695 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1696 loadValueDirectFixed(cUnit, rlSrc1, r0);
1697 if (checkZero) {
1698 genNullCheck(cUnit, rlSrc2.sRegLow, r1, mir->offset, NULL);
1699 }
1700 opReg(cUnit, kOpBlx, rLR);
1701 oatClobberCallRegs(cUnit);
1702 if (retReg == r0)
1703 rlResult = oatGetReturn(cUnit);
1704 else
1705 rlResult = oatGetReturnAlt(cUnit);
1706 storeValue(cUnit, rlDest, rlResult);
1707 }
1708 return false;
1709}
1710
buzbee67bf8852011-08-17 17:51:35 -07001711/*
1712 * Fetch *self->info.breakFlags. If the breakFlags are non-zero,
1713 * punt to the interpreter.
1714 */
1715static void genSuspendPoll(CompilationUnit* cUnit, MIR* mir)
1716{
1717 UNIMPLEMENTED(WARNING);
1718#if 0
1719 int rTemp = oatAllocTemp(cUnit);
1720 ArmLIR* ld;
1721 ld = loadBaseDisp(cUnit, NULL, rSELF,
1722 offsetof(Thread, interpBreak.ctl.breakFlags),
1723 rTemp, kUnsignedByte, INVALID_SREG);
1724 setMemRefType(ld, true /* isLoad */, kMustNotAlias);
1725 genRegImmCheck(cUnit, kArmCondNe, rTemp, 0, mir->offset, NULL);
1726#endif
1727}
1728
1729/*
1730 * The following are the first-level codegen routines that analyze the format
1731 * of each bytecode then either dispatch special purpose codegen routines
1732 * or produce corresponding Thumb instructions directly.
1733 */
1734
1735static bool isPowerOfTwo(int x)
1736{
1737 return (x & (x - 1)) == 0;
1738}
1739
1740// Returns true if no more than two bits are set in 'x'.
1741static bool isPopCountLE2(unsigned int x)
1742{
1743 x &= x - 1;
1744 return (x & (x - 1)) == 0;
1745}
1746
1747// Returns the index of the lowest set bit in 'x'.
1748static int lowestSetBit(unsigned int x) {
1749 int bit_posn = 0;
1750 while ((x & 0xf) == 0) {
1751 bit_posn += 4;
1752 x >>= 4;
1753 }
1754 while ((x & 1) == 0) {
1755 bit_posn++;
1756 x >>= 1;
1757 }
1758 return bit_posn;
1759}
1760
1761// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
1762// and store the result in 'rlDest'.
1763static bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode,
1764 RegLocation rlSrc, RegLocation rlDest, int lit)
1765{
1766 if (lit < 2 || !isPowerOfTwo(lit)) {
1767 return false;
1768 }
1769 int k = lowestSetBit(lit);
1770 if (k >= 30) {
1771 // Avoid special cases.
1772 return false;
1773 }
1774 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 ||
1775 dalvikOpcode == OP_DIV_INT_LIT16);
1776 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1777 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1778 if (div) {
1779 int tReg = oatAllocTemp(cUnit);
1780 if (lit == 2) {
1781 // Division by 2 is by far the most common division by constant.
1782 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
1783 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1784 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1785 } else {
1786 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
1787 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
1788 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1789 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1790 }
1791 } else {
1792 int cReg = oatAllocTemp(cUnit);
1793 loadConstant(cUnit, cReg, lit - 1);
1794 int tReg1 = oatAllocTemp(cUnit);
1795 int tReg2 = oatAllocTemp(cUnit);
1796 if (lit == 2) {
1797 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
1798 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1799 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1800 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1801 } else {
1802 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
1803 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
1804 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1805 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1806 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1807 }
1808 }
1809 storeValue(cUnit, rlDest, rlResult);
1810 return true;
1811}
1812
1813// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
1814// and store the result in 'rlDest'.
1815static bool handleEasyMultiply(CompilationUnit* cUnit,
1816 RegLocation rlSrc, RegLocation rlDest, int lit)
1817{
1818 // Can we simplify this multiplication?
1819 bool powerOfTwo = false;
1820 bool popCountLE2 = false;
1821 bool powerOfTwoMinusOne = false;
1822 if (lit < 2) {
1823 // Avoid special cases.
1824 return false;
1825 } else if (isPowerOfTwo(lit)) {
1826 powerOfTwo = true;
1827 } else if (isPopCountLE2(lit)) {
1828 popCountLE2 = true;
1829 } else if (isPowerOfTwo(lit + 1)) {
1830 powerOfTwoMinusOne = true;
1831 } else {
1832 return false;
1833 }
1834 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1835 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1836 if (powerOfTwo) {
1837 // Shift.
1838 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
1839 lowestSetBit(lit));
1840 } else if (popCountLE2) {
1841 // Shift and add and shift.
1842 int firstBit = lowestSetBit(lit);
1843 int secondBit = lowestSetBit(lit ^ (1 << firstBit));
1844 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
1845 firstBit, secondBit);
1846 } else {
1847 // Reverse subtract: (src << (shift + 1)) - src.
1848 assert(powerOfTwoMinusOne);
1849 // TODO: rsb dst, src, src lsl#lowestSetBit(lit + 1)
1850 int tReg = oatAllocTemp(cUnit);
1851 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
1852 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
1853 }
1854 storeValue(cUnit, rlDest, rlResult);
1855 return true;
1856}
1857
1858static bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir,
1859 RegLocation rlDest, RegLocation rlSrc,
1860 int lit)
1861{
1862 Opcode dalvikOpcode = mir->dalvikInsn.opcode;
1863 RegLocation rlResult;
1864 OpKind op = (OpKind)0; /* Make gcc happy */
1865 int shiftOp = false;
1866 bool isDiv = false;
1867 int funcOffset;
1868
1869 switch (dalvikOpcode) {
1870 case OP_RSUB_INT_LIT8:
1871 case OP_RSUB_INT: {
1872 int tReg;
1873 //TUNING: add support for use of Arm rsub op
1874 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1875 tReg = oatAllocTemp(cUnit);
1876 loadConstant(cUnit, tReg, lit);
1877 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1878 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1879 tReg, rlSrc.lowReg);
1880 storeValue(cUnit, rlDest, rlResult);
1881 return false;
1882 break;
1883 }
1884
1885 case OP_ADD_INT_LIT8:
1886 case OP_ADD_INT_LIT16:
1887 op = kOpAdd;
1888 break;
1889 case OP_MUL_INT_LIT8:
1890 case OP_MUL_INT_LIT16: {
1891 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
1892 return false;
1893 }
1894 op = kOpMul;
1895 break;
1896 }
1897 case OP_AND_INT_LIT8:
1898 case OP_AND_INT_LIT16:
1899 op = kOpAnd;
1900 break;
1901 case OP_OR_INT_LIT8:
1902 case OP_OR_INT_LIT16:
1903 op = kOpOr;
1904 break;
1905 case OP_XOR_INT_LIT8:
1906 case OP_XOR_INT_LIT16:
1907 op = kOpXor;
1908 break;
1909 case OP_SHL_INT_LIT8:
1910 lit &= 31;
1911 shiftOp = true;
1912 op = kOpLsl;
1913 break;
1914 case OP_SHR_INT_LIT8:
1915 lit &= 31;
1916 shiftOp = true;
1917 op = kOpAsr;
1918 break;
1919 case OP_USHR_INT_LIT8:
1920 lit &= 31;
1921 shiftOp = true;
1922 op = kOpLsr;
1923 break;
1924
1925 case OP_DIV_INT_LIT8:
1926 case OP_DIV_INT_LIT16:
1927 case OP_REM_INT_LIT8:
1928 case OP_REM_INT_LIT16:
1929 if (lit == 0) {
1930 UNIMPLEMENTED(FATAL);
1931 // FIXME: generate an explicit throw here
1932 return false;
1933 }
1934 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
1935 return false;
1936 }
1937 oatFlushAllRegs(cUnit); /* Everything to home location */
1938 loadValueDirectFixed(cUnit, rlSrc, r0);
1939 oatClobber(cUnit, r0);
1940 if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
1941 (dalvikOpcode == OP_DIV_INT_LIT16)) {
1942 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1943 isDiv = true;
1944 } else {
1945 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1946 isDiv = false;
1947 }
1948 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1949 loadConstant(cUnit, r1, lit);
1950 opReg(cUnit, kOpBlx, rLR);
1951 oatClobberCallRegs(cUnit);
1952 if (isDiv)
1953 rlResult = oatGetReturn(cUnit);
1954 else
1955 rlResult = oatGetReturnAlt(cUnit);
1956 storeValue(cUnit, rlDest, rlResult);
1957 return false;
1958 break;
1959 default:
1960 return true;
1961 }
1962 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1963 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1964 // Avoid shifts by literal 0 - no support in Thumb. Change to copy
1965 if (shiftOp && (lit == 0)) {
1966 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
1967 } else {
1968 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
1969 }
1970 storeValue(cUnit, rlDest, rlResult);
1971 return false;
1972}
1973
1974/* Architectural-specific debugging helpers go here */
1975void oatArchDump(void)
1976{
1977 /* Print compiled opcode in this VM instance */
1978 int i, start, streak;
1979 char buf[1024];
1980
1981 streak = i = 0;
1982 buf[0] = 0;
1983 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
1984 i++;
1985 }
1986 if (i == kNumPackedOpcodes) {
1987 return;
1988 }
1989 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
1990 if (opcodeCoverage[i]) {
1991 streak++;
1992 } else {
1993 if (streak == 1) {
1994 sprintf(buf+strlen(buf), "%x,", start);
1995 } else {
1996 sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
1997 }
1998 streak = 0;
1999 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2000 i++;
2001 }
2002 if (i < kNumPackedOpcodes) {
2003 streak = 1;
2004 start = i;
2005 }
2006 }
2007 }
2008 if (streak) {
2009 if (streak == 1) {
2010 sprintf(buf+strlen(buf), "%x", start);
2011 } else {
2012 sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
2013 }
2014 }
2015 if (strlen(buf)) {
2016 LOG(INFO) << "dalvik.vm.oat.op = " << buf;
2017 }
2018}