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