blob: 2404ca7524c10ed3d5734652c134553fdc94d29f [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);
Elliott Hughes2bb97f92011-09-11 15:43:37 -070035 std::string class_name = dex_file.dexStringByTypeIdx(field_id.class_idx_);
buzbee34cd9e52011-09-08 14:31:52 -070036 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
buzbeeec5adf32011-09-11 15:25:43 -070058/*
59 * If a helper routine might need to unwind, let it know the top
60 * of the managed stack.
61 */
62static ArmLIR* callUnwindableHelper(CompilationUnit* cUnit, int reg)
63{
64 // Starting point for managed traceback if we throw
65 storeWordDisp(cUnit, rSELF,
66 art::Thread::TopOfManagedStackOffset().Int32Value(), rSP);
67 return opReg(cUnit, kOpBlx, reg);
68}
69
70static ArmLIR* callNoUnwindHelper(CompilationUnit* cUnit, int reg)
71{
72 return opReg(cUnit, kOpBlx, reg);
73}
74
buzbee1b4c8592011-08-31 10:43:51 -070075/* Generate unconditional branch instructions */
76static ArmLIR* genUnconditionalBranch(CompilationUnit* cUnit, ArmLIR* target)
77{
78 ArmLIR* branch = opNone(cUnit, kOpUncondBr);
79 branch->generic.target = (LIR*) target;
80 return branch;
81}
82
buzbee67bf8852011-08-17 17:51:35 -070083/*
84 * Generate a Thumb2 IT instruction, which can nullify up to
85 * four subsequent instructions based on a condition and its
86 * inverse. The condition applies to the first instruction, which
87 * is executed if the condition is met. The string "guide" consists
88 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
89 * A "T" means the instruction is executed if the condition is
90 * met, and an "E" means the instruction is executed if the condition
91 * is not met.
92 */
93static ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code,
94 const char* guide)
95{
96 int mask;
97 int condBit = code & 1;
98 int altBit = condBit ^ 1;
99 int mask3 = 0;
100 int mask2 = 0;
101 int mask1 = 0;
102
103 //Note: case fallthroughs intentional
104 switch(strlen(guide)) {
105 case 3:
106 mask1 = (guide[2] == 'T') ? condBit : altBit;
107 case 2:
108 mask2 = (guide[1] == 'T') ? condBit : altBit;
109 case 1:
110 mask3 = (guide[0] == 'T') ? condBit : altBit;
111 break;
112 case 0:
113 break;
114 default:
115 LOG(FATAL) << "OAT: bad case in genIT";
116 }
117 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
118 (1 << (3 - strlen(guide)));
119 return newLIR2(cUnit, kThumb2It, code, mask);
120}
121
122/*
123 * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik
124 * offset vaddr. This label will be used to fix up the case
125 * branch table during the assembly phase. Be sure to set
126 * all resource flags on this to prevent code motion across
127 * target boundaries. KeyVal is just there for debugging.
128 */
129static ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
130{
131 ArmLIR* lir;
132 for (lir = (ArmLIR*)cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
133 if ((lir->opcode == kArmPseudoDalvikByteCodeBoundary) &&
134 (lir->generic.dalvikOffset == vaddr)) {
135 ArmLIR* newLabel = (ArmLIR*)oatNew(sizeof(ArmLIR), true);
136 newLabel->generic.dalvikOffset = vaddr;
137 newLabel->opcode = kArmPseudoCaseLabel;
138 newLabel->operands[0] = keyVal;
139 oatInsertLIRAfter((LIR*)lir, (LIR*)newLabel);
140 return newLabel;
141 }
142 }
143 oatCodegenDump(cUnit);
144 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
145 return NULL; // Quiet gcc
146}
147
148static void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
149{
150 const u2* table = tabRec->table;
151 int baseVaddr = tabRec->vaddr;
152 int *targets = (int*)&table[4];
153 int entries = table[1];
154 int lowKey = s4FromSwitchData(&table[2]);
155 for (int i = 0; i < entries; i++) {
156 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
157 i + lowKey);
158 }
159}
160
161static void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
162{
163 const u2* table = tabRec->table;
164 int baseVaddr = tabRec->vaddr;
165 int entries = table[1];
166 int* keys = (int*)&table[2];
167 int* targets = &keys[entries];
168 for (int i = 0; i < entries; i++) {
169 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
170 keys[i]);
171 }
172}
173
174void oatProcessSwitchTables(CompilationUnit* cUnit)
175{
176 GrowableListIterator iterator;
177 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
178 while (true) {
179 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
180 &iterator);
181 if (tabRec == NULL) break;
182 if (tabRec->table[0] == kPackedSwitchSignature)
183 markPackedCaseLabels(cUnit, tabRec);
184 else if (tabRec->table[0] == kSparseSwitchSignature)
185 markSparseCaseLabels(cUnit, tabRec);
186 else {
187 LOG(FATAL) << "Invalid switch table";
188 }
189 }
190}
191
192static void dumpSparseSwitchTable(const u2* table)
193 /*
194 * Sparse switch data format:
195 * ushort ident = 0x0200 magic value
196 * ushort size number of entries in the table; > 0
197 * int keys[size] keys, sorted low-to-high; 32-bit aligned
198 * int targets[size] branch targets, relative to switch opcode
199 *
200 * Total size is (2+size*4) 16-bit code units.
201 */
202{
203 u2 ident = table[0];
204 int entries = table[1];
205 int* keys = (int*)&table[2];
206 int* targets = &keys[entries];
207 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
208 ", entries: " << std::dec << entries;
209 for (int i = 0; i < entries; i++) {
210 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
211 targets[i];
212 }
213}
214
215static void dumpPackedSwitchTable(const u2* table)
216 /*
217 * Packed switch data format:
218 * ushort ident = 0x0100 magic value
219 * ushort size number of entries in the table
220 * int first_key first (and lowest) switch case value
221 * int targets[size] branch targets, relative to switch opcode
222 *
223 * Total size is (4+size*2) 16-bit code units.
224 */
225{
226 u2 ident = table[0];
227 int* targets = (int*)&table[4];
228 int entries = table[1];
229 int lowKey = s4FromSwitchData(&table[2]);
230 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
231 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
232 for (int i = 0; i < entries; i++) {
233 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
234 targets[i];
235 }
236}
237
238/*
239 * The sparse table in the literal pool is an array of <key,displacement>
240 * pairs. For each set, we'll load them as a pair using ldmia.
241 * This means that the register number of the temp we use for the key
242 * must be lower than the reg for the displacement.
243 *
244 * The test loop will look something like:
245 *
246 * adr rBase, <table>
247 * ldr rVal, [rSP, vRegOff]
248 * mov rIdx, #tableSize
249 * lp:
250 * ldmia rBase!, {rKey, rDisp}
251 * sub rIdx, #1
252 * cmp rVal, rKey
253 * ifeq
254 * add rPC, rDisp ; This is the branch from which we compute displacement
255 * cbnz rIdx, lp
256 */
257static void genSparseSwitch(CompilationUnit* cUnit, MIR* mir,
258 RegLocation rlSrc)
259{
260 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
261 if (cUnit->printMe) {
262 dumpSparseSwitchTable(table);
263 }
264 // Add the table to the list - we'll process it later
265 SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable),
266 true);
267 tabRec->table = table;
268 tabRec->vaddr = mir->offset;
269 int size = table[1];
270 tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true);
271 oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec);
272
273 // Get the switch value
274 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
275 int rBase = oatAllocTemp(cUnit);
276 /* Allocate key and disp temps */
277 int rKey = oatAllocTemp(cUnit);
278 int rDisp = oatAllocTemp(cUnit);
279 // Make sure rKey's register number is less than rDisp's number for ldmia
280 if (rKey > rDisp) {
281 int tmp = rDisp;
282 rDisp = rKey;
283 rKey = tmp;
284 }
285 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700286 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700287 // Set up rIdx
288 int rIdx = oatAllocTemp(cUnit);
289 loadConstant(cUnit, rIdx, size);
290 // Establish loop branch target
291 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
292 target->defMask = ENCODE_ALL;
293 // Load next key/disp
294 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
295 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
296 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
297 genIT(cUnit, kArmCondEq, "");
298 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
299 tabRec->bxInst = switchBranch;
300 // Needs to use setflags encoding here
301 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
302 ArmLIR* branch = opCondBranch(cUnit, kArmCondNe);
303 branch->generic.target = (LIR*)target;
304}
305
306
307static void genPackedSwitch(CompilationUnit* cUnit, MIR* mir,
308 RegLocation rlSrc)
309{
310 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
311 if (cUnit->printMe) {
312 dumpPackedSwitchTable(table);
313 }
314 // Add the table to the list - we'll process it later
315 SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable),
316 true);
317 tabRec->table = table;
318 tabRec->vaddr = mir->offset;
319 int size = table[1];
320 tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true);
321 oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec);
322
323 // Get the switch value
324 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
325 int tableBase = oatAllocTemp(cUnit);
326 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700327 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700328 int lowKey = s4FromSwitchData(&table[2]);
329 int keyReg;
330 // Remove the bias, if necessary
331 if (lowKey == 0) {
332 keyReg = rlSrc.lowReg;
333 } else {
334 keyReg = oatAllocTemp(cUnit);
335 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
336 }
337 // Bounds check - if < 0 or >= size continue following switch
338 opRegImm(cUnit, kOpCmp, keyReg, size-1);
339 ArmLIR* branchOver = opCondBranch(cUnit, kArmCondHi);
340
341 // Load the displacement from the switch table
342 int dispReg = oatAllocTemp(cUnit);
343 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
344
345 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
346 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
347 tabRec->bxInst = switchBranch;
348
349 /* branchOver target here */
350 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
351 target->defMask = ENCODE_ALL;
352 branchOver->generic.target = (LIR*)target;
353}
354
355/*
356 * Array data table format:
357 * ushort ident = 0x0300 magic value
358 * ushort width width of each element in the table
359 * uint size number of elements in the table
360 * ubyte data[size*width] table of data values (may contain a single-byte
361 * padding at the end)
362 *
363 * Total size is 4+(width * size + 1)/2 16-bit code units.
364 */
365static void genFillArrayData(CompilationUnit* cUnit, MIR* mir,
366 RegLocation rlSrc)
367{
368 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
369 // Add the table to the list - we'll process it later
370 FillArrayData *tabRec = (FillArrayData *)
371 oatNew(sizeof(FillArrayData), true);
372 tabRec->table = table;
373 tabRec->vaddr = mir->offset;
374 u2 width = tabRec->table[1];
375 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
376 tabRec->size = (size * width) + 8;
377
378 oatInsertGrowableList(&cUnit->fillArrayData, (intptr_t)tabRec);
379
380 // Making a call - use explicit registers
381 oatFlushAllRegs(cUnit); /* Everything to home location */
382 loadValueDirectFixed(cUnit, rlSrc, r0);
383 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700384 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
buzbeee6d61962011-08-27 11:58:19 -0700385 // Materialize a pointer to the fill data image
buzbee03fa2632011-09-20 17:10:57 -0700386 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
buzbeeec5adf32011-09-11 15:25:43 -0700387 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700388 oatClobberCallRegs(cUnit);
389}
390
391/*
392 * Mark garbage collection card. Skip if the value we're storing is null.
393 */
394static void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg)
395{
Elliott Hughes5ee7a8b2011-09-13 16:40:07 -0700396#ifdef CONCURRENT_GARBAGE_COLLECTOR
buzbee0d966cf2011-09-08 17:34:58 -0700397 // TODO: re-enable when concurrent collector is active
buzbee67bf8852011-08-17 17:51:35 -0700398 int regCardBase = oatAllocTemp(cUnit);
399 int regCardNo = oatAllocTemp(cUnit);
400 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0);
buzbeec143c552011-08-20 17:38:58 -0700401 loadWordDisp(cUnit, rSELF, Thread::CardTableOffset().Int32Value(),
buzbee67bf8852011-08-17 17:51:35 -0700402 regCardBase);
403 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
404 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
405 kUnsignedByte);
406 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
407 target->defMask = ENCODE_ALL;
408 branchOver->generic.target = (LIR*)target;
409 oatFreeTemp(cUnit, regCardBase);
410 oatFreeTemp(cUnit, regCardNo);
Elliott Hughes0f4c41d2011-09-04 14:58:03 -0700411#endif
buzbee67bf8852011-08-17 17:51:35 -0700412}
413
buzbee34cd9e52011-09-08 14:31:52 -0700414/*
415 * Helper function for Iget/put when field not resolved at compile time.
416 * Will trash call temps and return with the field offset in r0.
417 */
418static void getFieldOffset(CompilationUnit* cUnit, MIR* mir)
419{
420 int fieldIdx = mir->dalvikInsn.vC;
421 LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
422 << " unresolved at compile time";
423 oatLockCallTemps(cUnit); // Explicit register usage
424 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
425 loadWordDisp(cUnit, r1,
426 Method::DexCacheResolvedFieldsOffset().Int32Value(), r0);
427 loadWordDisp(cUnit, r0, art::Array::DataOffset().Int32Value() +
428 sizeof(int32_t*)* fieldIdx, r0);
429 /*
430 * For testing, omit the test for run-time resolution. This will
431 * force all accesses to go through the runtime resolution path.
432 */
433#ifndef EXERCISE_SLOWEST_FIELD_PATH
434 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
435#endif
436 // Resolve
437 loadWordDisp(cUnit, rSELF,
Brian Carlstrom845490b2011-09-19 15:56:53 -0700438 OFFSETOF_MEMBER(Thread, pFindInstanceFieldFromCode), rLR);
buzbee34cd9e52011-09-08 14:31:52 -0700439 loadConstant(cUnit, r0, fieldIdx);
buzbeeec5adf32011-09-11 15:25:43 -0700440 callUnwindableHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee34cd9e52011-09-08 14:31:52 -0700441 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
442 target->defMask = ENCODE_ALL;
443#ifndef EXERCISE_SLOWEST_FIELD_PATH
444 branchOver->generic.target = (LIR*)target;
445#endif
446 // Free temps (except for r0)
447 oatFreeTemp(cUnit, r1);
448 oatFreeTemp(cUnit, r2);
449 oatFreeTemp(cUnit, r3);
450 loadWordDisp(cUnit, r0, art::Field::OffsetOffset().Int32Value(), r0);
451}
452
buzbee43a36422011-09-14 14:00:13 -0700453static void genIGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -0700454 RegLocation rlDest, RegLocation rlObj)
455{
buzbeec143c552011-08-20 17:38:58 -0700456 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
457 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700458 RegLocation rlResult;
459 RegisterClass regClass = oatRegClassBySize(size);
buzbee34cd9e52011-09-08 14:31:52 -0700460 if (SLOW_FIELD_PATH || fieldPtr == NULL) {
461 getFieldOffset(cUnit, mir);
462 // Field offset in r0
463 rlObj = loadValue(cUnit, rlObj, kCoreReg);
464 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
buzbee5ade1d22011-09-09 14:44:52 -0700465 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee34cd9e52011-09-08 14:31:52 -0700466 loadBaseIndexed(cUnit, rlObj.lowReg, r0, rlResult.lowReg, 0, size);
buzbee67bf8852011-08-17 17:51:35 -0700467 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700468 storeValue(cUnit, rlDest, rlResult);
469 } else {
470#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700471 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700472#else
473 bool isVolatile = false;
474#endif
475 int fieldOffset = fieldPtr->GetOffset().Int32Value();
476 rlObj = loadValue(cUnit, rlObj, kCoreReg);
477 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
buzbee5ade1d22011-09-09 14:44:52 -0700478 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee34cd9e52011-09-08 14:31:52 -0700479 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
480 size, rlObj.sRegLow);
481 if (isVolatile) {
482 oatGenMemBarrier(cUnit, kSY);
483 }
484 storeValue(cUnit, rlDest, rlResult);
buzbee67bf8852011-08-17 17:51:35 -0700485 }
buzbee67bf8852011-08-17 17:51:35 -0700486}
487
buzbee43a36422011-09-14 14:00:13 -0700488static void genIPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -0700489 RegLocation rlSrc, RegLocation rlObj, bool isObject)
490{
buzbeec143c552011-08-20 17:38:58 -0700491 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
492 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700493 RegisterClass regClass = oatRegClassBySize(size);
buzbee34cd9e52011-09-08 14:31:52 -0700494 if (SLOW_FIELD_PATH || fieldPtr == NULL) {
495 getFieldOffset(cUnit, mir);
496 // Field offset in r0
497 rlObj = loadValue(cUnit, rlObj, kCoreReg);
498 rlSrc = loadValue(cUnit, rlSrc, regClass);
buzbee5ade1d22011-09-09 14:44:52 -0700499 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee67bf8852011-08-17 17:51:35 -0700500 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700501 storeBaseIndexed(cUnit, rlObj.lowReg, r0, rlSrc.lowReg, 0, size);
502 } else {
503#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700504 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700505#else
506 bool isVolatile = false;
507#endif
508 int fieldOffset = fieldPtr->GetOffset().Int32Value();
509 rlObj = loadValue(cUnit, rlObj, kCoreReg);
510 rlSrc = loadValue(cUnit, rlSrc, regClass);
buzbee5ade1d22011-09-09 14:44:52 -0700511 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700512
513 if (isVolatile) {
514 oatGenMemBarrier(cUnit, kSY);
515 }
516 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
buzbee67bf8852011-08-17 17:51:35 -0700517 }
buzbee67bf8852011-08-17 17:51:35 -0700518 if (isObject) {
519 /* NOTE: marking card based on object head */
520 markGCCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
521 }
522}
523
buzbee43a36422011-09-14 14:00:13 -0700524static void genIGetWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700525 RegLocation rlObj)
526{
buzbeec143c552011-08-20 17:38:58 -0700527 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
528 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700529 RegLocation rlResult;
buzbee34cd9e52011-09-08 14:31:52 -0700530 if (fieldPtr == NULL) {
531 getFieldOffset(cUnit, mir);
532 // Field offset in r0
533 rlObj = loadValue(cUnit, rlObj, kCoreReg);
534 rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
buzbee5ade1d22011-09-09 14:44:52 -0700535 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700536 opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
537 loadPair(cUnit, r0, rlResult.lowReg, rlResult.highReg);
buzbee67bf8852011-08-17 17:51:35 -0700538 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700539 storeValue(cUnit, rlDest, rlResult);
540 } else {
541#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700542 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700543#else
544 bool isVolatile = false;
545#endif
546 int fieldOffset = fieldPtr->GetOffset().Int32Value();
547 rlObj = loadValue(cUnit, rlObj, kCoreReg);
548 int regPtr = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700549
buzbee34cd9e52011-09-08 14:31:52 -0700550 assert(rlDest.wide);
551
buzbee5ade1d22011-09-09 14:44:52 -0700552 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700553 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
554 rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
555
556 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
557
558 if (isVolatile) {
559 oatGenMemBarrier(cUnit, kSY);
560 }
561
562 oatFreeTemp(cUnit, regPtr);
563 storeValueWide(cUnit, rlDest, rlResult);
564 }
buzbee67bf8852011-08-17 17:51:35 -0700565}
566
buzbee43a36422011-09-14 14:00:13 -0700567static void genIPutWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc,
buzbee67bf8852011-08-17 17:51:35 -0700568 RegLocation rlObj)
569{
buzbeec143c552011-08-20 17:38:58 -0700570 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
571 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700572 if (fieldPtr == NULL) {
buzbee34cd9e52011-09-08 14:31:52 -0700573 getFieldOffset(cUnit, mir);
574 // Field offset in r0
575 rlObj = loadValue(cUnit, rlObj, kCoreReg);
576 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
buzbee5ade1d22011-09-09 14:44:52 -0700577 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700578 opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700579 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700580 storePair(cUnit, r0, rlSrc.lowReg, rlSrc.highReg);
581 } else {
582#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700583 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700584#else
585 bool isVolatile = false;
586#endif
587 int fieldOffset = fieldPtr->GetOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -0700588
buzbee34cd9e52011-09-08 14:31:52 -0700589 rlObj = loadValue(cUnit, rlObj, kCoreReg);
590 int regPtr;
591 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
buzbee5ade1d22011-09-09 14:44:52 -0700592 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700593 regPtr = oatAllocTemp(cUnit);
594 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
595
596 if (isVolatile) {
597 oatGenMemBarrier(cUnit, kSY);
598 }
599 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
600
601 oatFreeTemp(cUnit, regPtr);
602 }
buzbee67bf8852011-08-17 17:51:35 -0700603}
604
605static void genConstClass(CompilationUnit* cUnit, MIR* mir,
606 RegLocation rlDest, RegLocation rlSrc)
607{
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700608 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
buzbee1b4c8592011-08-31 10:43:51 -0700609 Get(mir->dalvikInsn.vB);
610 int mReg = loadCurrMethod(cUnit);
611 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700612 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbee2a475e72011-09-07 17:19:17 -0700613 loadWordDisp(cUnit, mReg, Method::DexCacheResolvedTypesOffset().Int32Value(),
buzbee1b4c8592011-08-31 10:43:51 -0700614 resReg);
615 loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
616 (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
617 if (classPtr != NULL) {
618 // Fast path, we're done - just store result
619 storeValue(cUnit, rlDest, rlResult);
620 } else {
621 // Slow path. Must test at runtime
622 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg,
623 0);
624 // Resolved, store and hop over following code
625 storeValue(cUnit, rlDest, rlResult);
626 ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
627 // TUNING: move slow path to end & remove unconditional branch
628 ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
629 target1->defMask = ENCODE_ALL;
630 // Call out to helper, which will return resolved type in r0
631 loadWordDisp(cUnit, rSELF,
632 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
633 genRegCopy(cUnit, r1, mReg);
634 loadConstant(cUnit, r0, mir->dalvikInsn.vB);
buzbeeec5adf32011-09-11 15:25:43 -0700635 callUnwindableHelper(cUnit, rLR);
buzbee1b4c8592011-08-31 10:43:51 -0700636 oatClobberCallRegs(cUnit);
637 RegLocation rlResult = oatGetReturn(cUnit);
638 storeValue(cUnit, rlDest, rlResult);
639 // Rejoin code paths
640 ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
641 target2->defMask = ENCODE_ALL;
642 branch1->generic.target = (LIR*)target1;
643 branch2->generic.target = (LIR*)target2;
644 }
buzbee67bf8852011-08-17 17:51:35 -0700645}
646
647static void genConstString(CompilationUnit* cUnit, MIR* mir,
648 RegLocation rlDest, RegLocation rlSrc)
649{
buzbee1b4c8592011-08-31 10:43:51 -0700650 /* All strings should be available at compile time */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700651 const art::String* str = cUnit->method->GetDexCacheStrings()->
buzbee1b4c8592011-08-31 10:43:51 -0700652 Get(mir->dalvikInsn.vB);
653 DCHECK(str != NULL);
buzbee67bf8852011-08-17 17:51:35 -0700654
buzbee1b4c8592011-08-31 10:43:51 -0700655 int mReg = loadCurrMethod(cUnit);
656 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700657 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700658 loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(),
buzbee1b4c8592011-08-31 10:43:51 -0700659 resReg);
660 loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
661 (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700662 storeValue(cUnit, rlDest, rlResult);
663}
664
buzbeedfd3d702011-08-28 12:56:51 -0700665/*
666 * Let helper function take care of everything. Will
667 * call Class::NewInstanceFromCode(type_idx, method);
668 */
buzbee67bf8852011-08-17 17:51:35 -0700669static void genNewInstance(CompilationUnit* cUnit, MIR* mir,
670 RegLocation rlDest)
671{
buzbeedfd3d702011-08-28 12:56:51 -0700672 oatFlushAllRegs(cUnit); /* Everything to home location */
buzbee67bf8852011-08-17 17:51:35 -0700673 loadWordDisp(cUnit, rSELF,
Brian Carlstrom1f870082011-08-23 16:02:11 -0700674 OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
buzbeedfd3d702011-08-28 12:56:51 -0700675 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
676 loadConstant(cUnit, r0, mir->dalvikInsn.vB); // arg0 <- type_id
buzbeeec5adf32011-09-11 15:25:43 -0700677 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700678 oatClobberCallRegs(cUnit);
679 RegLocation rlResult = oatGetReturn(cUnit);
680 storeValue(cUnit, rlDest, rlResult);
681}
682
683void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
684{
685 loadWordDisp(cUnit, rSELF,
Ian Rogers67375ac2011-09-14 00:55:44 -0700686 OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
Ian Rogersbdb03912011-09-14 00:55:44 -0700687 loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
Ian Rogers67375ac2011-09-14 00:55:44 -0700688 callNoUnwindHelper(cUnit, rLR); // art_deliver_exception(exception);
buzbee67bf8852011-08-17 17:51:35 -0700689}
690
691static void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
692 RegLocation rlSrc)
693{
buzbee2a475e72011-09-07 17:19:17 -0700694 // May generate a call - use explicit registers
695 oatLockCallTemps(cUnit);
696 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
697 Get(mir->dalvikInsn.vC);
698 int classReg = r2; // Fixed usage
699 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
700 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
701 classReg);
702 loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
703 (sizeof(String*) * mir->dalvikInsn.vC), classReg);
buzbee67bf8852011-08-17 17:51:35 -0700704 if (classPtr == NULL) {
buzbee2a475e72011-09-07 17:19:17 -0700705 // Generate a runtime test
706 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
707 // Not resolved
708 // Call out to helper, which will return resolved type in r0
709 loadWordDisp(cUnit, rSELF,
710 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
711 loadConstant(cUnit, r0, mir->dalvikInsn.vC);
buzbeeec5adf32011-09-11 15:25:43 -0700712 callUnwindableHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee2a475e72011-09-07 17:19:17 -0700713 genRegCopy(cUnit, r2, r0); // Align usage with fast path
714 // Rejoin code paths
715 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
716 hopTarget->defMask = ENCODE_ALL;
717 hopBranch->generic.target = (LIR*)hopTarget;
buzbee67bf8852011-08-17 17:51:35 -0700718 }
buzbee2a475e72011-09-07 17:19:17 -0700719 // At this point, r2 has class
720 loadValueDirectFixed(cUnit, rlSrc, r3); /* Ref */
buzbee67bf8852011-08-17 17:51:35 -0700721 /* When taken r0 has NULL which can be used for store directly */
buzbee2a475e72011-09-07 17:19:17 -0700722 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r3, 0);
723 /* load object->clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700724 assert(Object::ClassOffset().Int32Value() == 0);
buzbee2a475e72011-09-07 17:19:17 -0700725 loadWordDisp(cUnit, r3, Object::ClassOffset().Int32Value(), r1);
buzbee67bf8852011-08-17 17:51:35 -0700726 /* r1 now contains object->clazz */
727 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700728 OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -0700729 loadConstant(cUnit, r0, 1); /* Assume true */
730 opRegReg(cUnit, kOpCmp, r1, r2);
731 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq);
buzbee2a475e72011-09-07 17:19:17 -0700732 genRegCopy(cUnit, r0, r3);
buzbee67bf8852011-08-17 17:51:35 -0700733 genRegCopy(cUnit, r1, r2);
buzbeeec5adf32011-09-11 15:25:43 -0700734 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700735 oatClobberCallRegs(cUnit);
736 /* branch target here */
737 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
738 target->defMask = ENCODE_ALL;
buzbee2a475e72011-09-07 17:19:17 -0700739 RegLocation rlResult = oatGetReturn(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700740 storeValue(cUnit, rlDest, rlResult);
741 branch1->generic.target = (LIR*)target;
742 branch2->generic.target = (LIR*)target;
743}
744
745static void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
746{
buzbee2a475e72011-09-07 17:19:17 -0700747 // May generate a call - use explicit registers
748 oatLockCallTemps(cUnit);
749 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
750 Get(mir->dalvikInsn.vB);
751 int classReg = r2; // Fixed usage
752 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
753 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
754 classReg);
755 loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
756 (sizeof(String*) * mir->dalvikInsn.vB), classReg);
buzbee67bf8852011-08-17 17:51:35 -0700757 if (classPtr == NULL) {
buzbee2a475e72011-09-07 17:19:17 -0700758 // Generate a runtime test
759 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
760 // Not resolved
761 // Call out to helper, which will return resolved type in r0
762 loadWordDisp(cUnit, rSELF,
763 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
764 loadConstant(cUnit, r0, mir->dalvikInsn.vB);
buzbeeec5adf32011-09-11 15:25:43 -0700765 callUnwindableHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee2a475e72011-09-07 17:19:17 -0700766 genRegCopy(cUnit, r2, r0); // Align usage with fast path
767 // Rejoin code paths
768 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
769 hopTarget->defMask = ENCODE_ALL;
770 hopBranch->generic.target = (LIR*)hopTarget;
buzbee67bf8852011-08-17 17:51:35 -0700771 }
buzbee2a475e72011-09-07 17:19:17 -0700772 // At this point, r2 has class
773 loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */
774 /* Null is OK - continue */
775 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
776 /* load object->clazz */
777 assert(Object::ClassOffset().Int32Value() == 0);
778 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
779 /* r1 now contains object->clazz */
buzbee67bf8852011-08-17 17:51:35 -0700780 loadWordDisp(cUnit, rSELF,
buzbee2a475e72011-09-07 17:19:17 -0700781 OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
782 opRegReg(cUnit, kOpCmp, r1, r2);
783 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
784 genRegCopy(cUnit, r0, r1);
785 genRegCopy(cUnit, r1, r2);
buzbeeec5adf32011-09-11 15:25:43 -0700786 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700787 oatClobberCallRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700788 /* branch target here */
buzbee67bf8852011-08-17 17:51:35 -0700789 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
790 target->defMask = ENCODE_ALL;
791 branch1->generic.target = (LIR*)target;
792 branch2->generic.target = (LIR*)target;
793}
794
795static void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest,
796 RegLocation rlSrc)
797{
798 RegLocation rlResult;
799 rlSrc = loadValue(cUnit, rlSrc, kFPReg);
800 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
801 newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
802 storeValue(cUnit, rlDest, rlResult);
803}
804
805static void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest,
806 RegLocation rlSrc)
807{
808 RegLocation rlResult;
809 rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
810 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
811 newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
812 S2D(rlSrc.lowReg, rlSrc.highReg));
813 storeValueWide(cUnit, rlDest, rlResult);
814}
815
buzbee439c4fa2011-08-27 15:59:07 -0700816static void freeRegLocTemps(CompilationUnit* cUnit, RegLocation rlKeep,
817 RegLocation rlFree)
buzbee67bf8852011-08-17 17:51:35 -0700818{
buzbee439c4fa2011-08-27 15:59:07 -0700819 if ((rlFree.lowReg != rlKeep.lowReg) && (rlFree.lowReg != rlKeep.highReg))
820 oatFreeTemp(cUnit, rlFree.lowReg);
821 if ((rlFree.highReg != rlKeep.lowReg) && (rlFree.highReg != rlKeep.highReg))
822 oatFreeTemp(cUnit, rlFree.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700823}
824
825static void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp,
826 OpKind secondOp, RegLocation rlDest,
827 RegLocation rlSrc1, RegLocation rlSrc2)
828{
buzbee9e0f9b02011-08-24 15:32:46 -0700829 /*
830 * NOTE: This is the one place in the code in which we might have
831 * as many as six live temporary registers. There are 5 in the normal
832 * set for Arm. Until we have spill capabilities, temporarily add
833 * lr to the temp set. It is safe to do this locally, but note that
834 * lr is used explicitly elsewhere in the code generator and cannot
835 * normally be used as a general temp register.
836 */
buzbee67bf8852011-08-17 17:51:35 -0700837 RegLocation rlResult;
buzbee9e0f9b02011-08-24 15:32:46 -0700838 oatMarkTemp(cUnit, rLR); // Add lr to the temp pool
839 oatFreeTemp(cUnit, rLR); // and make it available
buzbee67bf8852011-08-17 17:51:35 -0700840 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
841 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
842 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
843 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
844 opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
845 rlSrc2.highReg);
buzbee439c4fa2011-08-27 15:59:07 -0700846 /*
847 * NOTE: If rlDest refers to a frame variable in a large frame, the
848 * following storeValueWide might need to allocate a temp register.
849 * To further work around the lack of a spill capability, explicitly
850 * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult.
851 * Remove when spill is functional.
852 */
853 freeRegLocTemps(cUnit, rlResult, rlSrc1);
854 freeRegLocTemps(cUnit, rlResult, rlSrc2);
buzbee67bf8852011-08-17 17:51:35 -0700855 storeValueWide(cUnit, rlDest, rlResult);
buzbee9e0f9b02011-08-24 15:32:46 -0700856 oatClobber(cUnit, rLR);
857 oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool
buzbee67bf8852011-08-17 17:51:35 -0700858}
859
860void oatInitializeRegAlloc(CompilationUnit* cUnit)
861{
862 int numRegs = sizeof(coreRegs)/sizeof(*coreRegs);
863 int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs);
864 int numTemps = sizeof(coreTemps)/sizeof(*coreTemps);
865 int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs);
866 int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps);
867 RegisterPool *pool = (RegisterPool *)oatNew(sizeof(*pool), true);
868 cUnit->regPool = pool;
869 pool->numCoreRegs = numRegs;
870 pool->coreRegs = (RegisterInfo *)
871 oatNew(numRegs * sizeof(*cUnit->regPool->coreRegs), true);
872 pool->numFPRegs = numFPRegs;
873 pool->FPRegs = (RegisterInfo *)
874 oatNew(numFPRegs * sizeof(*cUnit->regPool->FPRegs), true);
875 oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs);
876 oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs);
877 // Keep special registers from being allocated
878 for (int i = 0; i < numReserved; i++) {
879 oatMarkInUse(cUnit, reservedRegs[i]);
880 }
881 // Mark temp regs - all others not in use can be used for promotion
882 for (int i = 0; i < numTemps; i++) {
883 oatMarkTemp(cUnit, coreTemps[i]);
884 }
885 for (int i = 0; i < numFPTemps; i++) {
886 oatMarkTemp(cUnit, fpTemps[i]);
887 }
buzbee67bf8852011-08-17 17:51:35 -0700888}
889
890/*
891 * Handle simple case (thin lock) inline. If it's complicated, bail
892 * out to the heavyweight lock/unlock routines. We'll use dedicated
893 * registers here in order to be in the right position in case we
894 * to bail to dvm[Lock/Unlock]Object(self, object)
895 *
896 * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
897 * r1 -> object [arg1 for dvm[Lock/Unlock]Object
898 * r2 -> intial contents of object->lock, later result of strex
899 * r3 -> self->threadId
900 * r12 -> allow to be used by utilities as general temp
901 *
902 * The result of the strex is 0 if we acquire the lock.
903 *
904 * See comments in Sync.c for the layout of the lock word.
905 * Of particular interest to this code is the test for the
906 * simple case - which we handle inline. For monitor enter, the
907 * simple case is thin lock, held by no-one. For monitor exit,
908 * the simple case is thin lock, held by the unlocking thread with
909 * a recurse count of 0.
910 *
911 * A minor complication is that there is a field in the lock word
912 * unrelated to locking: the hash state. This field must be ignored, but
913 * preserved.
914 *
915 */
916static void genMonitorEnter(CompilationUnit* cUnit, MIR* mir,
917 RegLocation rlSrc)
918{
919 ArmLIR* target;
920 ArmLIR* hopTarget;
921 ArmLIR* branch;
922 ArmLIR* hopBranch;
923
924 oatFlushAllRegs(cUnit);
Elliott Hughes5f791332011-09-15 17:45:30 -0700925 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -0700926 loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700927 oatLockCallTemps(cUnit); // Prepare for explicit register usage
buzbee5ade1d22011-09-09 14:44:52 -0700928 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
Elliott Hughes54e7df12011-09-16 11:47:04 -0700929 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -0700930 newLIR3(cUnit, kThumb2Ldrex, r2, r1,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700931 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
buzbeec143c552011-08-20 17:38:58 -0700932 // Align owner
Elliott Hughes5f791332011-09-15 17:45:30 -0700933 opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT);
buzbee67bf8852011-08-17 17:51:35 -0700934 // Is lock unheld on lock or held by us (==threadId) on unlock?
Elliott Hughes5f791332011-09-15 17:45:30 -0700935 newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, LW_LOCK_OWNER_SHIFT - 1);
936 newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
buzbee67bf8852011-08-17 17:51:35 -0700937 hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0);
buzbeec143c552011-08-20 17:38:58 -0700938 newLIR4(cUnit, kThumb2Strex, r2, r3, r1,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700939 Object::MonitorOffset().Int32Value() >> 2);
buzbee67bf8852011-08-17 17:51:35 -0700940 oatGenMemBarrier(cUnit, kSY);
941 branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
942
943 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
944 hopTarget->defMask = ENCODE_ALL;
945 hopBranch->generic.target = (LIR*)hopTarget;
946
buzbee1b4c8592011-08-31 10:43:51 -0700947 // Go expensive route - artLockObjectFromCode(self, obj);
948 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700949 rLR);
950 genRegCopy(cUnit, r0, rSELF);
buzbeeec5adf32011-09-11 15:25:43 -0700951 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700952
953 // Resume here
954 target = newLIR0(cUnit, kArmPseudoTargetLabel);
955 target->defMask = ENCODE_ALL;
956 branch->generic.target = (LIR*)target;
957}
958
959/*
960 * For monitor unlock, we don't have to use ldrex/strex. Once
961 * we've determined that the lock is thin and that we own it with
962 * a zero recursion count, it's safe to punch it back to the
963 * initial, unlock thin state with a store word.
964 */
965static void genMonitorExit(CompilationUnit* cUnit, MIR* mir,
966 RegLocation rlSrc)
967{
968 ArmLIR* target;
969 ArmLIR* branch;
970 ArmLIR* hopTarget;
971 ArmLIR* hopBranch;
972
Elliott Hughes5f791332011-09-15 17:45:30 -0700973 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -0700974 oatFlushAllRegs(cUnit);
975 loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700976 oatLockCallTemps(cUnit); // Prepare for explicit register usage
buzbee5ade1d22011-09-09 14:44:52 -0700977 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700978 loadWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r2); // Get lock
Elliott Hughes54e7df12011-09-16 11:47:04 -0700979 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -0700980 // Is lock unheld on lock or held by us (==threadId) on unlock?
Elliott Hughes5f791332011-09-15 17:45:30 -0700981 opRegRegImm(cUnit, kOpAnd, r12, r2, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
buzbeec143c552011-08-20 17:38:58 -0700982 // Align owner
Elliott Hughes5f791332011-09-15 17:45:30 -0700983 opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT);
984 newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
buzbee67bf8852011-08-17 17:51:35 -0700985 opRegReg(cUnit, kOpSub, r2, r3);
986 hopBranch = opCondBranch(cUnit, kArmCondNe);
987 oatGenMemBarrier(cUnit, kSY);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700988 storeWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r12);
buzbee67bf8852011-08-17 17:51:35 -0700989 branch = opNone(cUnit, kOpUncondBr);
990
991 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
992 hopTarget->defMask = ENCODE_ALL;
993 hopBranch->generic.target = (LIR*)hopTarget;
994
buzbee1b4c8592011-08-31 10:43:51 -0700995 // Go expensive route - UnlockObjectFromCode(self, obj);
996 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700997 rLR);
998 genRegCopy(cUnit, r0, rSELF);
buzbeeec5adf32011-09-11 15:25:43 -0700999 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001000
1001 // Resume here
1002 target = newLIR0(cUnit, kArmPseudoTargetLabel);
1003 target->defMask = ENCODE_ALL;
1004 branch->generic.target = (LIR*)target;
1005}
1006
1007/*
1008 * 64-bit 3way compare function.
1009 * mov rX, #-1
1010 * cmp op1hi, op2hi
1011 * blt done
1012 * bgt flip
1013 * sub rX, op1lo, op2lo (treat as unsigned)
1014 * beq done
1015 * ite hi
1016 * mov(hi) rX, #-1
1017 * mov(!hi) rX, #1
1018 * flip:
1019 * neg rX
1020 * done:
1021 */
1022static void genCmpLong(CompilationUnit* cUnit, MIR* mir,
1023 RegLocation rlDest, RegLocation rlSrc1,
1024 RegLocation rlSrc2)
1025{
1026 RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
1027 ArmLIR* target1;
1028 ArmLIR* target2;
1029 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
1030 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1031 rlTemp.lowReg = oatAllocTemp(cUnit);
1032 loadConstant(cUnit, rlTemp.lowReg, -1);
1033 opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
1034 ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt);
1035 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt);
1036 opRegRegReg(cUnit, kOpSub, rlTemp.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
1037 ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq);
1038
1039 genIT(cUnit, kArmCondHi, "E");
1040 newLIR2(cUnit, kThumb2MovImmShift, rlTemp.lowReg, modifiedImmediate(-1));
1041 loadConstant(cUnit, rlTemp.lowReg, 1);
1042 genBarrier(cUnit);
1043
1044 target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
1045 target2->defMask = -1;
1046 opRegReg(cUnit, kOpNeg, rlTemp.lowReg, rlTemp.lowReg);
1047
1048 target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
1049 target1->defMask = -1;
1050
1051 storeValue(cUnit, rlDest, rlTemp);
1052
1053 branch1->generic.target = (LIR*)target1;
1054 branch2->generic.target = (LIR*)target2;
1055 branch3->generic.target = branch1->generic.target;
1056}
1057
1058static void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit,
1059 RegLocation rlSrc, RegLocation rlResult, int lit,
1060 int firstBit, int secondBit)
1061{
1062 opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
1063 encodeShift(kArmLsl, secondBit - firstBit));
1064 if (firstBit != 0) {
1065 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
1066 }
1067}
1068
1069static bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset,
1070 int srcSize, int tgtSize)
1071{
1072 /*
1073 * Don't optimize the register usage since it calls out to support
1074 * functions
1075 */
1076 RegLocation rlSrc;
1077 RegLocation rlDest;
1078 oatFlushAllRegs(cUnit); /* Send everything to home location */
1079 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1080 if (srcSize == 1) {
1081 rlSrc = oatGetSrc(cUnit, mir, 0);
1082 loadValueDirectFixed(cUnit, rlSrc, r0);
1083 } else {
1084 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
1085 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
1086 }
buzbeeec5adf32011-09-11 15:25:43 -07001087 callNoUnwindHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001088 oatClobberCallRegs(cUnit);
1089 if (tgtSize == 1) {
1090 RegLocation rlResult;
1091 rlDest = oatGetDest(cUnit, mir, 0);
1092 rlResult = oatGetReturn(cUnit);
1093 storeValue(cUnit, rlDest, rlResult);
1094 } else {
1095 RegLocation rlResult;
1096 rlDest = oatGetDestWide(cUnit, mir, 0, 1);
1097 rlResult = oatGetReturnWide(cUnit);
1098 storeValueWide(cUnit, rlDest, rlResult);
1099 }
1100 return false;
1101}
1102
1103static bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir,
1104 RegLocation rlDest, RegLocation rlSrc1,
1105 RegLocation rlSrc2)
1106{
1107 RegLocation rlResult;
1108 int funcOffset;
1109
1110 switch (mir->dalvikInsn.opcode) {
1111 case OP_ADD_FLOAT_2ADDR:
1112 case OP_ADD_FLOAT:
1113 funcOffset = OFFSETOF_MEMBER(Thread, pFadd);
1114 break;
1115 case OP_SUB_FLOAT_2ADDR:
1116 case OP_SUB_FLOAT:
1117 funcOffset = OFFSETOF_MEMBER(Thread, pFsub);
1118 break;
1119 case OP_DIV_FLOAT_2ADDR:
1120 case OP_DIV_FLOAT:
1121 funcOffset = OFFSETOF_MEMBER(Thread, pFdiv);
1122 break;
1123 case OP_MUL_FLOAT_2ADDR:
1124 case OP_MUL_FLOAT:
1125 funcOffset = OFFSETOF_MEMBER(Thread, pFmul);
1126 break;
1127 case OP_REM_FLOAT_2ADDR:
1128 case OP_REM_FLOAT:
1129 funcOffset = OFFSETOF_MEMBER(Thread, pFmodf);
1130 break;
1131 case OP_NEG_FLOAT: {
1132 genNegFloat(cUnit, rlDest, rlSrc1);
1133 return false;
1134 }
1135 default:
1136 return true;
1137 }
1138 oatFlushAllRegs(cUnit); /* Send everything to home location */
1139 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1140 loadValueDirectFixed(cUnit, rlSrc1, r0);
1141 loadValueDirectFixed(cUnit, rlSrc2, r1);
buzbeeec5adf32011-09-11 15:25:43 -07001142 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001143 oatClobberCallRegs(cUnit);
1144 rlResult = oatGetReturn(cUnit);
1145 storeValue(cUnit, rlDest, rlResult);
1146 return false;
1147}
1148
1149static bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir,
1150 RegLocation rlDest, RegLocation rlSrc1,
1151 RegLocation rlSrc2)
1152{
1153 RegLocation rlResult;
1154 int funcOffset;
1155
1156 switch (mir->dalvikInsn.opcode) {
1157 case OP_ADD_DOUBLE_2ADDR:
1158 case OP_ADD_DOUBLE:
1159 funcOffset = OFFSETOF_MEMBER(Thread, pDadd);
1160 break;
1161 case OP_SUB_DOUBLE_2ADDR:
1162 case OP_SUB_DOUBLE:
1163 funcOffset = OFFSETOF_MEMBER(Thread, pDsub);
1164 break;
1165 case OP_DIV_DOUBLE_2ADDR:
1166 case OP_DIV_DOUBLE:
1167 funcOffset = OFFSETOF_MEMBER(Thread, pDdiv);
1168 break;
1169 case OP_MUL_DOUBLE_2ADDR:
1170 case OP_MUL_DOUBLE:
1171 funcOffset = OFFSETOF_MEMBER(Thread, pDmul);
1172 break;
1173 case OP_REM_DOUBLE_2ADDR:
1174 case OP_REM_DOUBLE:
1175 funcOffset = OFFSETOF_MEMBER(Thread, pFmod);
1176 break;
1177 case OP_NEG_DOUBLE: {
1178 genNegDouble(cUnit, rlDest, rlSrc1);
1179 return false;
1180 }
1181 default:
1182 return true;
1183 }
1184 oatFlushAllRegs(cUnit); /* Send everything to home location */
1185 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1186 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1187 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
buzbeeec5adf32011-09-11 15:25:43 -07001188 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001189 oatClobberCallRegs(cUnit);
1190 rlResult = oatGetReturnWide(cUnit);
1191 storeValueWide(cUnit, rlDest, rlResult);
1192 return false;
1193}
1194
1195static bool genConversionPortable(CompilationUnit* cUnit, MIR* mir)
1196{
1197 Opcode opcode = mir->dalvikInsn.opcode;
1198
1199 switch (opcode) {
1200 case OP_INT_TO_FLOAT:
1201 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f),
1202 1, 1);
1203 case OP_FLOAT_TO_INT:
1204 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz),
1205 1, 1);
1206 case OP_DOUBLE_TO_FLOAT:
1207 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f),
1208 2, 1);
1209 case OP_FLOAT_TO_DOUBLE:
1210 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d),
1211 1, 2);
1212 case OP_INT_TO_DOUBLE:
1213 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d),
1214 1, 2);
1215 case OP_DOUBLE_TO_INT:
1216 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz),
1217 2, 1);
1218 case OP_FLOAT_TO_LONG:
1219 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001220 pF2l), 1, 2);
buzbee67bf8852011-08-17 17:51:35 -07001221 case OP_LONG_TO_FLOAT:
1222 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f),
1223 2, 1);
1224 case OP_DOUBLE_TO_LONG:
1225 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001226 pD2l), 2, 2);
buzbee67bf8852011-08-17 17:51:35 -07001227 case OP_LONG_TO_DOUBLE:
1228 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d),
1229 2, 2);
1230 default:
1231 return true;
1232 }
1233 return false;
1234}
1235
1236/* Generate conditional branch instructions */
1237static ArmLIR* genConditionalBranch(CompilationUnit* cUnit,
1238 ArmConditionCode cond,
1239 ArmLIR* target)
1240{
1241 ArmLIR* branch = opCondBranch(cUnit, cond);
1242 branch->generic.target = (LIR*) target;
1243 return branch;
1244}
1245
buzbee67bf8852011-08-17 17:51:35 -07001246/*
1247 * Generate array store
1248 *
1249 */
buzbee1b4c8592011-08-31 10:43:51 -07001250static void genArrayObjPut(CompilationUnit* cUnit, MIR* mir,
1251 RegLocation rlArray, RegLocation rlIndex,
1252 RegLocation rlSrc, int scale)
buzbee67bf8852011-08-17 17:51:35 -07001253{
1254 RegisterClass regClass = oatRegClassBySize(kWord);
buzbeec143c552011-08-20 17:38:58 -07001255 int lenOffset = Array::LengthOffset().Int32Value();
1256 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001257
1258 /* Make sure it's a legal object Put. Use direct regs at first */
1259 loadValueDirectFixed(cUnit, rlArray, r1);
1260 loadValueDirectFixed(cUnit, rlSrc, r0);
1261
1262 /* null array object? */
buzbee43a36422011-09-14 14:00:13 -07001263 genNullCheck(cUnit, rlArray.sRegLow, r1, mir);
buzbee67bf8852011-08-17 17:51:35 -07001264 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -07001265 OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -07001266 /* Get the array's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001267 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), r1);
buzbeeec5adf32011-09-11 15:25:43 -07001268 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001269 oatClobberCallRegs(cUnit);
1270
1271 // Now, redo loadValues in case they didn't survive the call
1272
1273 int regPtr;
1274 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1275 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1276
1277 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1278 oatClobber(cUnit, rlArray.lowReg);
1279 regPtr = rlArray.lowReg;
1280 } else {
1281 regPtr = oatAllocTemp(cUnit);
1282 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1283 }
1284
buzbee43a36422011-09-14 14:00:13 -07001285 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001286 int regLen = oatAllocTemp(cUnit);
1287 //NOTE: max live temps(4) here.
1288 /* Get len */
1289 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1290 /* regPtr -> array data */
1291 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001292 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001293 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001294 oatFreeTemp(cUnit, regLen);
1295 } else {
1296 /* regPtr -> array data */
1297 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1298 }
1299 /* at this point, regPtr points to array, 2 live temps */
1300 rlSrc = loadValue(cUnit, rlSrc, regClass);
1301 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1302 scale, kWord);
1303}
1304
1305/*
1306 * Generate array load
1307 */
1308static void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
1309 RegLocation rlArray, RegLocation rlIndex,
1310 RegLocation rlDest, int scale)
1311{
1312 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001313 int lenOffset = Array::LengthOffset().Int32Value();
1314 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001315 RegLocation rlResult;
1316 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1317 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1318 int regPtr;
1319
1320 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001321 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001322
1323 regPtr = oatAllocTemp(cUnit);
1324
buzbee43a36422011-09-14 14:00:13 -07001325 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001326 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);
buzbeeec5adf32011-09-11 15:25:43 -07001331 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001332 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? */
buzbee43a36422011-09-14 14:00:13 -07001391 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001392
buzbee43a36422011-09-14 14:00:13 -07001393 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001394 int regLen = oatAllocTemp(cUnit);
1395 //NOTE: max live temps(4) here.
1396 /* Get len */
1397 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1398 /* regPtr -> array data */
1399 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001400 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001401 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001402 oatFreeTemp(cUnit, regLen);
1403 } else {
1404 /* regPtr -> array data */
1405 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1406 }
1407 /* at this point, regPtr points to array, 2 live temps */
1408 if ((size == kLong) || (size == kDouble)) {
buzbee5ade1d22011-09-09 14:44:52 -07001409 //TUNING: specific wide routine that can handle fp regs
buzbee67bf8852011-08-17 17:51:35 -07001410 if (scale) {
1411 int rNewIndex = oatAllocTemp(cUnit);
1412 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1413 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1414 oatFreeTemp(cUnit, rNewIndex);
1415 } else {
1416 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1417 }
1418 rlSrc = loadValueWide(cUnit, rlSrc, regClass);
1419
1420 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
1421
1422 oatFreeTemp(cUnit, regPtr);
1423 } else {
1424 rlSrc = loadValue(cUnit, rlSrc, regClass);
1425
1426 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1427 scale, size);
1428 }
1429}
1430
1431static bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir,
1432 RegLocation rlDest, RegLocation rlSrc1,
1433 RegLocation rlShift)
1434{
buzbee54330722011-08-23 16:46:55 -07001435 int funcOffset;
buzbee67bf8852011-08-17 17:51:35 -07001436
buzbee67bf8852011-08-17 17:51:35 -07001437 switch( mir->dalvikInsn.opcode) {
1438 case OP_SHL_LONG:
1439 case OP_SHL_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001440 funcOffset = OFFSETOF_MEMBER(Thread, pShlLong);
buzbee67bf8852011-08-17 17:51:35 -07001441 break;
1442 case OP_SHR_LONG:
1443 case OP_SHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001444 funcOffset = OFFSETOF_MEMBER(Thread, pShrLong);
buzbee67bf8852011-08-17 17:51:35 -07001445 break;
1446 case OP_USHR_LONG:
1447 case OP_USHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001448 funcOffset = OFFSETOF_MEMBER(Thread, pUshrLong);
buzbee67bf8852011-08-17 17:51:35 -07001449 break;
1450 default:
buzbee54330722011-08-23 16:46:55 -07001451 LOG(FATAL) << "Unexpected case";
buzbee67bf8852011-08-17 17:51:35 -07001452 return true;
1453 }
buzbee54330722011-08-23 16:46:55 -07001454 oatFlushAllRegs(cUnit); /* Send everything to home location */
1455 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1456 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1457 loadValueDirect(cUnit, rlShift, r2);
buzbeeec5adf32011-09-11 15:25:43 -07001458 callNoUnwindHelper(cUnit, rLR);
buzbee54330722011-08-23 16:46:55 -07001459 oatClobberCallRegs(cUnit);
1460 RegLocation rlResult = oatGetReturnWide(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001461 storeValueWide(cUnit, rlDest, rlResult);
1462 return false;
1463}
1464
1465static bool genArithOpLong(CompilationUnit* cUnit, MIR* mir,
1466 RegLocation rlDest, RegLocation rlSrc1,
1467 RegLocation rlSrc2)
1468{
1469 RegLocation rlResult;
1470 OpKind firstOp = kOpBkpt;
1471 OpKind secondOp = kOpBkpt;
1472 bool callOut = false;
1473 int funcOffset;
1474 int retReg = r0;
1475
1476 switch (mir->dalvikInsn.opcode) {
1477 case OP_NOT_LONG:
1478 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1479 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1480 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1481 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
1482 storeValueWide(cUnit, rlDest, rlResult);
1483 return false;
1484 break;
1485 case OP_ADD_LONG:
1486 case OP_ADD_LONG_2ADDR:
1487 firstOp = kOpAdd;
1488 secondOp = kOpAdc;
1489 break;
1490 case OP_SUB_LONG:
1491 case OP_SUB_LONG_2ADDR:
1492 firstOp = kOpSub;
1493 secondOp = kOpSbc;
1494 break;
1495 case OP_MUL_LONG:
1496 case OP_MUL_LONG_2ADDR:
buzbee439c4fa2011-08-27 15:59:07 -07001497 callOut = true;
1498 retReg = r0;
1499 funcOffset = OFFSETOF_MEMBER(Thread, pLmul);
1500 break;
buzbee67bf8852011-08-17 17:51:35 -07001501 case OP_DIV_LONG:
1502 case OP_DIV_LONG_2ADDR:
1503 callOut = true;
1504 retReg = r0;
1505 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1506 break;
1507 /* NOTE - result is in r2/r3 instead of r0/r1 */
1508 case OP_REM_LONG:
1509 case OP_REM_LONG_2ADDR:
1510 callOut = true;
1511 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1512 retReg = r2;
1513 break;
1514 case OP_AND_LONG_2ADDR:
1515 case OP_AND_LONG:
1516 firstOp = kOpAnd;
1517 secondOp = kOpAnd;
1518 break;
1519 case OP_OR_LONG:
1520 case OP_OR_LONG_2ADDR:
1521 firstOp = kOpOr;
1522 secondOp = kOpOr;
1523 break;
1524 case OP_XOR_LONG:
1525 case OP_XOR_LONG_2ADDR:
1526 firstOp = kOpXor;
1527 secondOp = kOpXor;
1528 break;
1529 case OP_NEG_LONG: {
1530 //TUNING: can improve this using Thumb2 code
1531 int tReg = oatAllocTemp(cUnit);
1532 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1533 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1534 loadConstantNoClobber(cUnit, tReg, 0);
1535 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1536 tReg, rlSrc2.lowReg);
1537 opRegReg(cUnit, kOpSbc, tReg, rlSrc2.highReg);
1538 genRegCopy(cUnit, rlResult.highReg, tReg);
1539 storeValueWide(cUnit, rlDest, rlResult);
1540 return false;
1541 }
1542 default:
1543 LOG(FATAL) << "Invalid long arith op";
1544 }
1545 if (!callOut) {
1546 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
1547 } else {
1548 // Adjust return regs in to handle case of rem returning r2/r3
1549 oatFlushAllRegs(cUnit); /* Send everything to home location */
1550 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1551 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1552 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
buzbeeec5adf32011-09-11 15:25:43 -07001553 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001554 oatClobberCallRegs(cUnit);
1555 if (retReg == r0)
1556 rlResult = oatGetReturnWide(cUnit);
1557 else
1558 rlResult = oatGetReturnWideAlt(cUnit);
1559 storeValueWide(cUnit, rlDest, rlResult);
1560 }
1561 return false;
1562}
1563
1564static bool genArithOpInt(CompilationUnit* cUnit, MIR* mir,
1565 RegLocation rlDest, RegLocation rlSrc1,
1566 RegLocation rlSrc2)
1567{
1568 OpKind op = kOpBkpt;
1569 bool callOut = false;
1570 bool checkZero = false;
1571 bool unary = false;
1572 int retReg = r0;
1573 int funcOffset;
1574 RegLocation rlResult;
1575 bool shiftOp = false;
1576
1577 switch (mir->dalvikInsn.opcode) {
1578 case OP_NEG_INT:
1579 op = kOpNeg;
1580 unary = true;
1581 break;
1582 case OP_NOT_INT:
1583 op = kOpMvn;
1584 unary = true;
1585 break;
1586 case OP_ADD_INT:
1587 case OP_ADD_INT_2ADDR:
1588 op = kOpAdd;
1589 break;
1590 case OP_SUB_INT:
1591 case OP_SUB_INT_2ADDR:
1592 op = kOpSub;
1593 break;
1594 case OP_MUL_INT:
1595 case OP_MUL_INT_2ADDR:
1596 op = kOpMul;
1597 break;
1598 case OP_DIV_INT:
1599 case OP_DIV_INT_2ADDR:
1600 callOut = true;
1601 checkZero = true;
1602 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1603 retReg = r0;
1604 break;
1605 /* NOTE: returns in r1 */
1606 case OP_REM_INT:
1607 case OP_REM_INT_2ADDR:
1608 callOut = true;
1609 checkZero = true;
1610 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1611 retReg = r1;
1612 break;
1613 case OP_AND_INT:
1614 case OP_AND_INT_2ADDR:
1615 op = kOpAnd;
1616 break;
1617 case OP_OR_INT:
1618 case OP_OR_INT_2ADDR:
1619 op = kOpOr;
1620 break;
1621 case OP_XOR_INT:
1622 case OP_XOR_INT_2ADDR:
1623 op = kOpXor;
1624 break;
1625 case OP_SHL_INT:
1626 case OP_SHL_INT_2ADDR:
1627 shiftOp = true;
1628 op = kOpLsl;
1629 break;
1630 case OP_SHR_INT:
1631 case OP_SHR_INT_2ADDR:
1632 shiftOp = true;
1633 op = kOpAsr;
1634 break;
1635 case OP_USHR_INT:
1636 case OP_USHR_INT_2ADDR:
1637 shiftOp = true;
1638 op = kOpLsr;
1639 break;
1640 default:
1641 LOG(FATAL) << "Invalid word arith op: " <<
1642 (int)mir->dalvikInsn.opcode;
1643 }
1644 if (!callOut) {
1645 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
1646 if (unary) {
1647 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1648 opRegReg(cUnit, op, rlResult.lowReg,
1649 rlSrc1.lowReg);
1650 } else {
1651 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
1652 if (shiftOp) {
1653 int tReg = oatAllocTemp(cUnit);
1654 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
1655 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1656 opRegRegReg(cUnit, op, rlResult.lowReg,
1657 rlSrc1.lowReg, tReg);
1658 oatFreeTemp(cUnit, tReg);
1659 } else {
1660 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1661 opRegRegReg(cUnit, op, rlResult.lowReg,
1662 rlSrc1.lowReg, rlSrc2.lowReg);
1663 }
1664 }
1665 storeValue(cUnit, rlDest, rlResult);
1666 } else {
1667 RegLocation rlResult;
1668 oatFlushAllRegs(cUnit); /* Send everything to home location */
1669 loadValueDirectFixed(cUnit, rlSrc2, r1);
1670 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1671 loadValueDirectFixed(cUnit, rlSrc1, r0);
1672 if (checkZero) {
buzbee5ade1d22011-09-09 14:44:52 -07001673 genImmedCheck(cUnit, kArmCondEq, r1, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001674 }
buzbeeec5adf32011-09-11 15:25:43 -07001675 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001676 oatClobberCallRegs(cUnit);
1677 if (retReg == r0)
1678 rlResult = oatGetReturn(cUnit);
1679 else
1680 rlResult = oatGetReturnAlt(cUnit);
1681 storeValue(cUnit, rlDest, rlResult);
1682 }
1683 return false;
1684}
1685
buzbee0d966cf2011-09-08 17:34:58 -07001686/* Check for pending suspend request. */
buzbee67bf8852011-08-17 17:51:35 -07001687static void genSuspendPoll(CompilationUnit* cUnit, MIR* mir)
1688{
buzbee0d966cf2011-09-08 17:34:58 -07001689 oatLockCallTemps(cUnit); // Explicit register usage
1690 int rSuspendCount = r1;
buzbee67bf8852011-08-17 17:51:35 -07001691 ArmLIR* ld;
buzbee0d966cf2011-09-08 17:34:58 -07001692 ld = loadWordDisp(cUnit, rSELF,
1693 art::Thread::SuspendCountOffset().Int32Value(), rSuspendCount);
buzbee67bf8852011-08-17 17:51:35 -07001694 setMemRefType(ld, true /* isLoad */, kMustNotAlias);
buzbee0d966cf2011-09-08 17:34:58 -07001695 loadWordDisp(cUnit, rSELF,
1696 OFFSETOF_MEMBER(Thread, pCheckSuspendFromCode), rLR);
1697 genRegCopy(cUnit, r0, rSELF);
1698 opRegImm(cUnit, kOpCmp, rSuspendCount, 0);
buzbeeb0ebba02011-09-17 10:52:59 -07001699 /*
1700 * FIXME: for efficiency we should use an if-converted suspend
1701 * test here. However, support for IT is a bit weak at the
1702 * moment, and requires knowledge of the exact number of instructions
1703 * to fall in the skip shadow. While the exception mechanism
1704 * remains in flux, use a compare and branch sequence. Once
1705 * things firm up, restore the conditional skip (and perhaps
1706 * fix the utility to handle variable-sized shadows).
1707 */
1708#if 0
buzbee0d966cf2011-09-08 17:34:58 -07001709 genIT(cUnit, kArmCondNe, "");
buzbeeec5adf32011-09-11 15:25:43 -07001710 callUnwindableHelper(cUnit, rLR); // CheckSuspendFromCode(self)
buzbeeb0ebba02011-09-17 10:52:59 -07001711#else
1712 ArmLIR* branch = opCondBranch(cUnit, kArmCondEq);
1713 callUnwindableHelper(cUnit, rLR); // CheckSuspendFromCode(self)
1714 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
1715 target->defMask = ENCODE_ALL;
1716 branch->generic.target = (LIR*)target;
1717#endif
buzbee0d966cf2011-09-08 17:34:58 -07001718 oatFreeCallTemps(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001719}
1720
1721/*
1722 * The following are the first-level codegen routines that analyze the format
1723 * of each bytecode then either dispatch special purpose codegen routines
1724 * or produce corresponding Thumb instructions directly.
1725 */
1726
1727static bool isPowerOfTwo(int x)
1728{
1729 return (x & (x - 1)) == 0;
1730}
1731
1732// Returns true if no more than two bits are set in 'x'.
1733static bool isPopCountLE2(unsigned int x)
1734{
1735 x &= x - 1;
1736 return (x & (x - 1)) == 0;
1737}
1738
1739// Returns the index of the lowest set bit in 'x'.
1740static int lowestSetBit(unsigned int x) {
1741 int bit_posn = 0;
1742 while ((x & 0xf) == 0) {
1743 bit_posn += 4;
1744 x >>= 4;
1745 }
1746 while ((x & 1) == 0) {
1747 bit_posn++;
1748 x >>= 1;
1749 }
1750 return bit_posn;
1751}
1752
1753// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
1754// and store the result in 'rlDest'.
1755static bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode,
1756 RegLocation rlSrc, RegLocation rlDest, int lit)
1757{
1758 if (lit < 2 || !isPowerOfTwo(lit)) {
1759 return false;
1760 }
1761 int k = lowestSetBit(lit);
1762 if (k >= 30) {
1763 // Avoid special cases.
1764 return false;
1765 }
1766 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 ||
1767 dalvikOpcode == OP_DIV_INT_LIT16);
1768 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1769 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1770 if (div) {
1771 int tReg = oatAllocTemp(cUnit);
1772 if (lit == 2) {
1773 // Division by 2 is by far the most common division by constant.
1774 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
1775 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1776 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1777 } else {
1778 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
1779 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
1780 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1781 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1782 }
1783 } else {
1784 int cReg = oatAllocTemp(cUnit);
1785 loadConstant(cUnit, cReg, lit - 1);
1786 int tReg1 = oatAllocTemp(cUnit);
1787 int tReg2 = oatAllocTemp(cUnit);
1788 if (lit == 2) {
1789 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
1790 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1791 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1792 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1793 } else {
1794 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
1795 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
1796 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1797 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1798 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1799 }
1800 }
1801 storeValue(cUnit, rlDest, rlResult);
1802 return true;
1803}
1804
1805// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
1806// and store the result in 'rlDest'.
1807static bool handleEasyMultiply(CompilationUnit* cUnit,
1808 RegLocation rlSrc, RegLocation rlDest, int lit)
1809{
1810 // Can we simplify this multiplication?
1811 bool powerOfTwo = false;
1812 bool popCountLE2 = false;
1813 bool powerOfTwoMinusOne = false;
1814 if (lit < 2) {
1815 // Avoid special cases.
1816 return false;
1817 } else if (isPowerOfTwo(lit)) {
1818 powerOfTwo = true;
1819 } else if (isPopCountLE2(lit)) {
1820 popCountLE2 = true;
1821 } else if (isPowerOfTwo(lit + 1)) {
1822 powerOfTwoMinusOne = true;
1823 } else {
1824 return false;
1825 }
1826 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1827 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1828 if (powerOfTwo) {
1829 // Shift.
1830 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
1831 lowestSetBit(lit));
1832 } else if (popCountLE2) {
1833 // Shift and add and shift.
1834 int firstBit = lowestSetBit(lit);
1835 int secondBit = lowestSetBit(lit ^ (1 << firstBit));
1836 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
1837 firstBit, secondBit);
1838 } else {
1839 // Reverse subtract: (src << (shift + 1)) - src.
1840 assert(powerOfTwoMinusOne);
buzbee5ade1d22011-09-09 14:44:52 -07001841 // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1)
buzbee67bf8852011-08-17 17:51:35 -07001842 int tReg = oatAllocTemp(cUnit);
1843 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
1844 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
1845 }
1846 storeValue(cUnit, rlDest, rlResult);
1847 return true;
1848}
1849
1850static bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir,
1851 RegLocation rlDest, RegLocation rlSrc,
1852 int lit)
1853{
1854 Opcode dalvikOpcode = mir->dalvikInsn.opcode;
1855 RegLocation rlResult;
1856 OpKind op = (OpKind)0; /* Make gcc happy */
1857 int shiftOp = false;
1858 bool isDiv = false;
1859 int funcOffset;
1860
1861 switch (dalvikOpcode) {
1862 case OP_RSUB_INT_LIT8:
1863 case OP_RSUB_INT: {
1864 int tReg;
1865 //TUNING: add support for use of Arm rsub op
1866 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1867 tReg = oatAllocTemp(cUnit);
1868 loadConstant(cUnit, tReg, lit);
1869 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1870 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1871 tReg, rlSrc.lowReg);
1872 storeValue(cUnit, rlDest, rlResult);
1873 return false;
1874 break;
1875 }
1876
1877 case OP_ADD_INT_LIT8:
1878 case OP_ADD_INT_LIT16:
1879 op = kOpAdd;
1880 break;
1881 case OP_MUL_INT_LIT8:
1882 case OP_MUL_INT_LIT16: {
1883 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
1884 return false;
1885 }
1886 op = kOpMul;
1887 break;
1888 }
1889 case OP_AND_INT_LIT8:
1890 case OP_AND_INT_LIT16:
1891 op = kOpAnd;
1892 break;
1893 case OP_OR_INT_LIT8:
1894 case OP_OR_INT_LIT16:
1895 op = kOpOr;
1896 break;
1897 case OP_XOR_INT_LIT8:
1898 case OP_XOR_INT_LIT16:
1899 op = kOpXor;
1900 break;
1901 case OP_SHL_INT_LIT8:
1902 lit &= 31;
1903 shiftOp = true;
1904 op = kOpLsl;
1905 break;
1906 case OP_SHR_INT_LIT8:
1907 lit &= 31;
1908 shiftOp = true;
1909 op = kOpAsr;
1910 break;
1911 case OP_USHR_INT_LIT8:
1912 lit &= 31;
1913 shiftOp = true;
1914 op = kOpLsr;
1915 break;
1916
1917 case OP_DIV_INT_LIT8:
1918 case OP_DIV_INT_LIT16:
1919 case OP_REM_INT_LIT8:
1920 case OP_REM_INT_LIT16:
1921 if (lit == 0) {
buzbee5ade1d22011-09-09 14:44:52 -07001922 genImmedCheck(cUnit, kArmCondAl, 0, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001923 return false;
1924 }
1925 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
1926 return false;
1927 }
1928 oatFlushAllRegs(cUnit); /* Everything to home location */
1929 loadValueDirectFixed(cUnit, rlSrc, r0);
1930 oatClobber(cUnit, r0);
1931 if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
1932 (dalvikOpcode == OP_DIV_INT_LIT16)) {
1933 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1934 isDiv = true;
1935 } else {
1936 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1937 isDiv = false;
1938 }
1939 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1940 loadConstant(cUnit, r1, lit);
buzbeeec5adf32011-09-11 15:25:43 -07001941 callUnwindableHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001942 oatClobberCallRegs(cUnit);
1943 if (isDiv)
1944 rlResult = oatGetReturn(cUnit);
1945 else
1946 rlResult = oatGetReturnAlt(cUnit);
1947 storeValue(cUnit, rlDest, rlResult);
1948 return false;
1949 break;
1950 default:
1951 return true;
1952 }
1953 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1954 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1955 // Avoid shifts by literal 0 - no support in Thumb. Change to copy
1956 if (shiftOp && (lit == 0)) {
1957 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
1958 } else {
1959 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
1960 }
1961 storeValue(cUnit, rlDest, rlResult);
1962 return false;
1963}
1964
1965/* Architectural-specific debugging helpers go here */
1966void oatArchDump(void)
1967{
1968 /* Print compiled opcode in this VM instance */
1969 int i, start, streak;
1970 char buf[1024];
1971
1972 streak = i = 0;
1973 buf[0] = 0;
1974 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
1975 i++;
1976 }
1977 if (i == kNumPackedOpcodes) {
1978 return;
1979 }
1980 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
1981 if (opcodeCoverage[i]) {
1982 streak++;
1983 } else {
1984 if (streak == 1) {
1985 sprintf(buf+strlen(buf), "%x,", start);
1986 } else {
1987 sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
1988 }
1989 streak = 0;
1990 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
1991 i++;
1992 }
1993 if (i < kNumPackedOpcodes) {
1994 streak = 1;
1995 start = i;
1996 }
1997 }
1998 }
1999 if (streak) {
2000 if (streak == 1) {
2001 sprintf(buf+strlen(buf), "%x", start);
2002 } else {
2003 sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
2004 }
2005 }
2006 if (strlen(buf)) {
2007 LOG(INFO) << "dalvik.vm.oat.op = " << buf;
2008 }
2009}