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