blob: e3893d37a116672f27fc0fb3f6563d81263350b7 [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
buzbeeed3e9302011-09-23 17:34:19 -070048STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070049 return *(s4*) switchData;
50}
51#else
buzbeeed3e9302011-09-23 17:34:19 -070052STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070053 u2* data = switchData;
54 return data[0] | (((s4) data[1]) << 16);
55}
56#endif
57
buzbeeed3e9302011-09-23 17:34:19 -070058STATIC ArmLIR* callRuntimeHelper(CompilationUnit* cUnit, int reg)
buzbeeec5adf32011-09-11 15:25:43 -070059{
60 return opReg(cUnit, kOpBlx, reg);
61}
62
buzbee1b4c8592011-08-31 10:43:51 -070063/* Generate unconditional branch instructions */
buzbeeed3e9302011-09-23 17:34:19 -070064STATIC ArmLIR* genUnconditionalBranch(CompilationUnit* cUnit, ArmLIR* target)
buzbee1b4c8592011-08-31 10:43:51 -070065{
66 ArmLIR* branch = opNone(cUnit, kOpUncondBr);
67 branch->generic.target = (LIR*) target;
68 return branch;
69}
70
buzbee67bf8852011-08-17 17:51:35 -070071/*
72 * Generate a Thumb2 IT instruction, which can nullify up to
73 * four subsequent instructions based on a condition and its
74 * inverse. The condition applies to the first instruction, which
75 * is executed if the condition is met. The string "guide" consists
76 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
77 * A "T" means the instruction is executed if the condition is
78 * met, and an "E" means the instruction is executed if the condition
79 * is not met.
80 */
buzbeeed3e9302011-09-23 17:34:19 -070081STATIC ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code,
buzbee67bf8852011-08-17 17:51:35 -070082 const char* guide)
83{
84 int mask;
85 int condBit = code & 1;
86 int altBit = condBit ^ 1;
87 int mask3 = 0;
88 int mask2 = 0;
89 int mask1 = 0;
90
91 //Note: case fallthroughs intentional
92 switch(strlen(guide)) {
93 case 3:
94 mask1 = (guide[2] == 'T') ? condBit : altBit;
95 case 2:
96 mask2 = (guide[1] == 'T') ? condBit : altBit;
97 case 1:
98 mask3 = (guide[0] == 'T') ? condBit : altBit;
99 break;
100 case 0:
101 break;
102 default:
103 LOG(FATAL) << "OAT: bad case in genIT";
104 }
105 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
106 (1 << (3 - strlen(guide)));
107 return newLIR2(cUnit, kThumb2It, code, mask);
108}
109
110/*
111 * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik
112 * offset vaddr. This label will be used to fix up the case
113 * branch table during the assembly phase. Be sure to set
114 * all resource flags on this to prevent code motion across
115 * target boundaries. KeyVal is just there for debugging.
116 */
buzbeeed3e9302011-09-23 17:34:19 -0700117STATIC ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
buzbee67bf8852011-08-17 17:51:35 -0700118{
119 ArmLIR* lir;
120 for (lir = (ArmLIR*)cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
121 if ((lir->opcode == kArmPseudoDalvikByteCodeBoundary) &&
122 (lir->generic.dalvikOffset == vaddr)) {
123 ArmLIR* newLabel = (ArmLIR*)oatNew(sizeof(ArmLIR), true);
124 newLabel->generic.dalvikOffset = vaddr;
125 newLabel->opcode = kArmPseudoCaseLabel;
126 newLabel->operands[0] = keyVal;
127 oatInsertLIRAfter((LIR*)lir, (LIR*)newLabel);
128 return newLabel;
129 }
130 }
131 oatCodegenDump(cUnit);
132 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
133 return NULL; // Quiet gcc
134}
135
buzbeeed3e9302011-09-23 17:34:19 -0700136STATIC void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700137{
138 const u2* table = tabRec->table;
139 int baseVaddr = tabRec->vaddr;
140 int *targets = (int*)&table[4];
141 int entries = table[1];
142 int lowKey = s4FromSwitchData(&table[2]);
143 for (int i = 0; i < entries; i++) {
144 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
145 i + lowKey);
146 }
147}
148
buzbeeed3e9302011-09-23 17:34:19 -0700149STATIC void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700150{
151 const u2* table = tabRec->table;
152 int baseVaddr = tabRec->vaddr;
153 int entries = table[1];
154 int* keys = (int*)&table[2];
155 int* targets = &keys[entries];
156 for (int i = 0; i < entries; i++) {
157 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
158 keys[i]);
159 }
160}
161
162void oatProcessSwitchTables(CompilationUnit* cUnit)
163{
164 GrowableListIterator iterator;
165 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
166 while (true) {
167 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
168 &iterator);
169 if (tabRec == NULL) break;
170 if (tabRec->table[0] == kPackedSwitchSignature)
171 markPackedCaseLabels(cUnit, tabRec);
172 else if (tabRec->table[0] == kSparseSwitchSignature)
173 markSparseCaseLabels(cUnit, tabRec);
174 else {
175 LOG(FATAL) << "Invalid switch table";
176 }
177 }
178}
179
buzbeeed3e9302011-09-23 17:34:19 -0700180STATIC void dumpSparseSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700181 /*
182 * Sparse switch data format:
183 * ushort ident = 0x0200 magic value
184 * ushort size number of entries in the table; > 0
185 * int keys[size] keys, sorted low-to-high; 32-bit aligned
186 * int targets[size] branch targets, relative to switch opcode
187 *
188 * Total size is (2+size*4) 16-bit code units.
189 */
190{
191 u2 ident = table[0];
192 int entries = table[1];
193 int* keys = (int*)&table[2];
194 int* targets = &keys[entries];
195 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
196 ", entries: " << std::dec << entries;
197 for (int i = 0; i < entries; i++) {
198 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
199 targets[i];
200 }
201}
202
buzbeeed3e9302011-09-23 17:34:19 -0700203STATIC void dumpPackedSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700204 /*
205 * Packed switch data format:
206 * ushort ident = 0x0100 magic value
207 * ushort size number of entries in the table
208 * int first_key first (and lowest) switch case value
209 * int targets[size] branch targets, relative to switch opcode
210 *
211 * Total size is (4+size*2) 16-bit code units.
212 */
213{
214 u2 ident = table[0];
215 int* targets = (int*)&table[4];
216 int entries = table[1];
217 int lowKey = s4FromSwitchData(&table[2]);
218 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
219 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
220 for (int i = 0; i < entries; i++) {
221 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
222 targets[i];
223 }
224}
225
226/*
227 * The sparse table in the literal pool is an array of <key,displacement>
228 * pairs. For each set, we'll load them as a pair using ldmia.
229 * This means that the register number of the temp we use for the key
230 * must be lower than the reg for the displacement.
231 *
232 * The test loop will look something like:
233 *
234 * adr rBase, <table>
235 * ldr rVal, [rSP, vRegOff]
236 * mov rIdx, #tableSize
237 * lp:
238 * ldmia rBase!, {rKey, rDisp}
239 * sub rIdx, #1
240 * cmp rVal, rKey
241 * ifeq
242 * add rPC, rDisp ; This is the branch from which we compute displacement
243 * cbnz rIdx, lp
244 */
buzbeeed3e9302011-09-23 17:34:19 -0700245STATIC void genSparseSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700246 RegLocation rlSrc)
247{
248 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
249 if (cUnit->printMe) {
250 dumpSparseSwitchTable(table);
251 }
252 // Add the table to the list - we'll process it later
253 SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable),
254 true);
255 tabRec->table = table;
256 tabRec->vaddr = mir->offset;
257 int size = table[1];
258 tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true);
259 oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec);
260
261 // Get the switch value
262 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
263 int rBase = oatAllocTemp(cUnit);
264 /* Allocate key and disp temps */
265 int rKey = oatAllocTemp(cUnit);
266 int rDisp = oatAllocTemp(cUnit);
267 // Make sure rKey's register number is less than rDisp's number for ldmia
268 if (rKey > rDisp) {
269 int tmp = rDisp;
270 rDisp = rKey;
271 rKey = tmp;
272 }
273 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700274 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700275 // Set up rIdx
276 int rIdx = oatAllocTemp(cUnit);
277 loadConstant(cUnit, rIdx, size);
278 // Establish loop branch target
279 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
280 target->defMask = ENCODE_ALL;
281 // Load next key/disp
282 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
283 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
284 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
285 genIT(cUnit, kArmCondEq, "");
286 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
287 tabRec->bxInst = switchBranch;
288 // Needs to use setflags encoding here
289 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
290 ArmLIR* branch = opCondBranch(cUnit, kArmCondNe);
291 branch->generic.target = (LIR*)target;
292}
293
294
buzbeeed3e9302011-09-23 17:34:19 -0700295STATIC void genPackedSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700296 RegLocation rlSrc)
297{
298 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
299 if (cUnit->printMe) {
300 dumpPackedSwitchTable(table);
301 }
302 // Add the table to the list - we'll process it later
303 SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable),
304 true);
305 tabRec->table = table;
306 tabRec->vaddr = mir->offset;
307 int size = table[1];
308 tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true);
309 oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec);
310
311 // Get the switch value
312 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
313 int tableBase = oatAllocTemp(cUnit);
314 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700315 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700316 int lowKey = s4FromSwitchData(&table[2]);
317 int keyReg;
318 // Remove the bias, if necessary
319 if (lowKey == 0) {
320 keyReg = rlSrc.lowReg;
321 } else {
322 keyReg = oatAllocTemp(cUnit);
323 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
324 }
325 // Bounds check - if < 0 or >= size continue following switch
326 opRegImm(cUnit, kOpCmp, keyReg, size-1);
327 ArmLIR* branchOver = opCondBranch(cUnit, kArmCondHi);
328
329 // Load the displacement from the switch table
330 int dispReg = oatAllocTemp(cUnit);
331 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
332
333 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
334 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
335 tabRec->bxInst = switchBranch;
336
337 /* branchOver target here */
338 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
339 target->defMask = ENCODE_ALL;
340 branchOver->generic.target = (LIR*)target;
341}
342
343/*
344 * Array data table format:
345 * ushort ident = 0x0300 magic value
346 * ushort width width of each element in the table
347 * uint size number of elements in the table
348 * ubyte data[size*width] table of data values (may contain a single-byte
349 * padding at the end)
350 *
351 * Total size is 4+(width * size + 1)/2 16-bit code units.
352 */
buzbeeed3e9302011-09-23 17:34:19 -0700353STATIC void genFillArrayData(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700354 RegLocation rlSrc)
355{
356 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
357 // Add the table to the list - we'll process it later
358 FillArrayData *tabRec = (FillArrayData *)
359 oatNew(sizeof(FillArrayData), true);
360 tabRec->table = table;
361 tabRec->vaddr = mir->offset;
362 u2 width = tabRec->table[1];
363 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
364 tabRec->size = (size * width) + 8;
365
366 oatInsertGrowableList(&cUnit->fillArrayData, (intptr_t)tabRec);
367
368 // Making a call - use explicit registers
369 oatFlushAllRegs(cUnit); /* Everything to home location */
370 loadValueDirectFixed(cUnit, rlSrc, r0);
371 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700372 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
buzbeee6d61962011-08-27 11:58:19 -0700373 // Materialize a pointer to the fill data image
buzbee03fa2632011-09-20 17:10:57 -0700374 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
Ian Rogersff1ed472011-09-20 13:46:24 -0700375 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700376 oatClobberCallRegs(cUnit);
377}
378
379/*
380 * Mark garbage collection card. Skip if the value we're storing is null.
381 */
buzbeeed3e9302011-09-23 17:34:19 -0700382STATIC void markGCCard(CompilationUnit* cUnit, int valReg, int tgtAddrReg)
buzbee67bf8852011-08-17 17:51:35 -0700383{
Elliott Hughes5ee7a8b2011-09-13 16:40:07 -0700384#ifdef CONCURRENT_GARBAGE_COLLECTOR
buzbee0d966cf2011-09-08 17:34:58 -0700385 // TODO: re-enable when concurrent collector is active
buzbee67bf8852011-08-17 17:51:35 -0700386 int regCardBase = oatAllocTemp(cUnit);
387 int regCardNo = oatAllocTemp(cUnit);
388 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0);
buzbeec143c552011-08-20 17:38:58 -0700389 loadWordDisp(cUnit, rSELF, Thread::CardTableOffset().Int32Value(),
buzbee67bf8852011-08-17 17:51:35 -0700390 regCardBase);
391 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
392 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
393 kUnsignedByte);
394 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
395 target->defMask = ENCODE_ALL;
396 branchOver->generic.target = (LIR*)target;
397 oatFreeTemp(cUnit, regCardBase);
398 oatFreeTemp(cUnit, regCardNo);
Elliott Hughes0f4c41d2011-09-04 14:58:03 -0700399#endif
buzbee67bf8852011-08-17 17:51:35 -0700400}
401
buzbee34cd9e52011-09-08 14:31:52 -0700402/*
403 * Helper function for Iget/put when field not resolved at compile time.
404 * Will trash call temps and return with the field offset in r0.
405 */
buzbeeed3e9302011-09-23 17:34:19 -0700406STATIC void getFieldOffset(CompilationUnit* cUnit, MIR* mir)
buzbee34cd9e52011-09-08 14:31:52 -0700407{
408 int fieldIdx = mir->dalvikInsn.vC;
409 LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
410 << " unresolved at compile time";
411 oatLockCallTemps(cUnit); // Explicit register usage
412 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
413 loadWordDisp(cUnit, r1,
414 Method::DexCacheResolvedFieldsOffset().Int32Value(), r0);
415 loadWordDisp(cUnit, r0, art::Array::DataOffset().Int32Value() +
416 sizeof(int32_t*)* fieldIdx, r0);
417 /*
418 * For testing, omit the test for run-time resolution. This will
419 * force all accesses to go through the runtime resolution path.
420 */
421#ifndef EXERCISE_SLOWEST_FIELD_PATH
422 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
423#endif
424 // Resolve
425 loadWordDisp(cUnit, rSELF,
Brian Carlstrom845490b2011-09-19 15:56:53 -0700426 OFFSETOF_MEMBER(Thread, pFindInstanceFieldFromCode), rLR);
buzbee34cd9e52011-09-08 14:31:52 -0700427 loadConstant(cUnit, r0, fieldIdx);
Ian Rogersff1ed472011-09-20 13:46:24 -0700428 callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee34cd9e52011-09-08 14:31:52 -0700429 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
430 target->defMask = ENCODE_ALL;
431#ifndef EXERCISE_SLOWEST_FIELD_PATH
432 branchOver->generic.target = (LIR*)target;
433#endif
434 // Free temps (except for r0)
435 oatFreeTemp(cUnit, r1);
436 oatFreeTemp(cUnit, r2);
437 oatFreeTemp(cUnit, r3);
438 loadWordDisp(cUnit, r0, art::Field::OffsetOffset().Int32Value(), r0);
439}
440
buzbeeed3e9302011-09-23 17:34:19 -0700441STATIC void genIGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -0700442 RegLocation rlDest, RegLocation rlObj)
443{
buzbeec143c552011-08-20 17:38:58 -0700444 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
445 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700446 RegLocation rlResult;
447 RegisterClass regClass = oatRegClassBySize(size);
buzbee34cd9e52011-09-08 14:31:52 -0700448 if (SLOW_FIELD_PATH || fieldPtr == NULL) {
449 getFieldOffset(cUnit, mir);
450 // Field offset in r0
451 rlObj = loadValue(cUnit, rlObj, kCoreReg);
452 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
buzbee5ade1d22011-09-09 14:44:52 -0700453 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee34cd9e52011-09-08 14:31:52 -0700454 loadBaseIndexed(cUnit, rlObj.lowReg, r0, rlResult.lowReg, 0, size);
buzbee67bf8852011-08-17 17:51:35 -0700455 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700456 storeValue(cUnit, rlDest, rlResult);
457 } else {
458#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700459 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700460#else
461 bool isVolatile = false;
462#endif
463 int fieldOffset = fieldPtr->GetOffset().Int32Value();
464 rlObj = loadValue(cUnit, rlObj, kCoreReg);
465 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
buzbee5ade1d22011-09-09 14:44:52 -0700466 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee34cd9e52011-09-08 14:31:52 -0700467 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
468 size, rlObj.sRegLow);
469 if (isVolatile) {
470 oatGenMemBarrier(cUnit, kSY);
471 }
472 storeValue(cUnit, rlDest, rlResult);
buzbee67bf8852011-08-17 17:51:35 -0700473 }
buzbee67bf8852011-08-17 17:51:35 -0700474}
475
buzbeeed3e9302011-09-23 17:34:19 -0700476STATIC void genIPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -0700477 RegLocation rlSrc, RegLocation rlObj, bool isObject)
478{
buzbeec143c552011-08-20 17:38:58 -0700479 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
480 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700481 RegisterClass regClass = oatRegClassBySize(size);
buzbee34cd9e52011-09-08 14:31:52 -0700482 if (SLOW_FIELD_PATH || fieldPtr == NULL) {
483 getFieldOffset(cUnit, mir);
484 // Field offset in r0
485 rlObj = loadValue(cUnit, rlObj, kCoreReg);
486 rlSrc = loadValue(cUnit, rlSrc, regClass);
buzbee5ade1d22011-09-09 14:44:52 -0700487 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee67bf8852011-08-17 17:51:35 -0700488 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700489 storeBaseIndexed(cUnit, rlObj.lowReg, r0, rlSrc.lowReg, 0, size);
490 } else {
491#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700492 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700493#else
494 bool isVolatile = false;
495#endif
496 int fieldOffset = fieldPtr->GetOffset().Int32Value();
497 rlObj = loadValue(cUnit, rlObj, kCoreReg);
498 rlSrc = loadValue(cUnit, rlSrc, regClass);
buzbee5ade1d22011-09-09 14:44:52 -0700499 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700500
501 if (isVolatile) {
502 oatGenMemBarrier(cUnit, kSY);
503 }
504 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
buzbee67bf8852011-08-17 17:51:35 -0700505 }
buzbee67bf8852011-08-17 17:51:35 -0700506 if (isObject) {
507 /* NOTE: marking card based on object head */
508 markGCCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
509 }
510}
511
buzbeeed3e9302011-09-23 17:34:19 -0700512STATIC void genIGetWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700513 RegLocation rlObj)
514{
buzbeec143c552011-08-20 17:38:58 -0700515 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
516 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700517 RegLocation rlResult;
buzbee34cd9e52011-09-08 14:31:52 -0700518 if (fieldPtr == NULL) {
519 getFieldOffset(cUnit, mir);
520 // Field offset in r0
521 rlObj = loadValue(cUnit, rlObj, kCoreReg);
522 rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
buzbee5ade1d22011-09-09 14:44:52 -0700523 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700524 opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
525 loadPair(cUnit, r0, rlResult.lowReg, rlResult.highReg);
buzbee67bf8852011-08-17 17:51:35 -0700526 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700527 storeValue(cUnit, rlDest, rlResult);
528 } else {
529#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700530 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700531#else
532 bool isVolatile = false;
533#endif
534 int fieldOffset = fieldPtr->GetOffset().Int32Value();
535 rlObj = loadValue(cUnit, rlObj, kCoreReg);
536 int regPtr = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700537
buzbeeed3e9302011-09-23 17:34:19 -0700538 DCHECK(rlDest.wide);
buzbee34cd9e52011-09-08 14:31:52 -0700539
buzbee5ade1d22011-09-09 14:44:52 -0700540 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700541 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
542 rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
543
544 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
545
546 if (isVolatile) {
547 oatGenMemBarrier(cUnit, kSY);
548 }
549
550 oatFreeTemp(cUnit, regPtr);
551 storeValueWide(cUnit, rlDest, rlResult);
552 }
buzbee67bf8852011-08-17 17:51:35 -0700553}
554
buzbeeed3e9302011-09-23 17:34:19 -0700555STATIC void genIPutWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc,
buzbee67bf8852011-08-17 17:51:35 -0700556 RegLocation rlObj)
557{
buzbeec143c552011-08-20 17:38:58 -0700558 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
559 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700560 if (fieldPtr == NULL) {
buzbee34cd9e52011-09-08 14:31:52 -0700561 getFieldOffset(cUnit, mir);
562 // Field offset in r0
563 rlObj = loadValue(cUnit, rlObj, kCoreReg);
564 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
buzbee5ade1d22011-09-09 14:44:52 -0700565 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700566 opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700567 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700568 storePair(cUnit, r0, rlSrc.lowReg, rlSrc.highReg);
569 } else {
570#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700571 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700572#else
573 bool isVolatile = false;
574#endif
575 int fieldOffset = fieldPtr->GetOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -0700576
buzbee34cd9e52011-09-08 14:31:52 -0700577 rlObj = loadValue(cUnit, rlObj, kCoreReg);
578 int regPtr;
579 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
buzbee5ade1d22011-09-09 14:44:52 -0700580 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700581 regPtr = oatAllocTemp(cUnit);
582 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
583
584 if (isVolatile) {
585 oatGenMemBarrier(cUnit, kSY);
586 }
587 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
588
589 oatFreeTemp(cUnit, regPtr);
590 }
buzbee67bf8852011-08-17 17:51:35 -0700591}
592
buzbeeed3e9302011-09-23 17:34:19 -0700593STATIC void genConstClass(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700594 RegLocation rlDest, RegLocation rlSrc)
595{
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700596 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
buzbee1b4c8592011-08-31 10:43:51 -0700597 Get(mir->dalvikInsn.vB);
598 int mReg = loadCurrMethod(cUnit);
599 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700600 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbee2a475e72011-09-07 17:19:17 -0700601 loadWordDisp(cUnit, mReg, Method::DexCacheResolvedTypesOffset().Int32Value(),
buzbee1b4c8592011-08-31 10:43:51 -0700602 resReg);
603 loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
604 (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
605 if (classPtr != NULL) {
606 // Fast path, we're done - just store result
607 storeValue(cUnit, rlDest, rlResult);
608 } else {
609 // Slow path. Must test at runtime
610 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg,
611 0);
612 // Resolved, store and hop over following code
613 storeValue(cUnit, rlDest, rlResult);
614 ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
615 // TUNING: move slow path to end & remove unconditional branch
616 ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
617 target1->defMask = ENCODE_ALL;
618 // Call out to helper, which will return resolved type in r0
619 loadWordDisp(cUnit, rSELF,
620 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
621 genRegCopy(cUnit, r1, mReg);
622 loadConstant(cUnit, r0, mir->dalvikInsn.vB);
Ian Rogersff1ed472011-09-20 13:46:24 -0700623 callRuntimeHelper(cUnit, rLR);
buzbee1b4c8592011-08-31 10:43:51 -0700624 oatClobberCallRegs(cUnit);
625 RegLocation rlResult = oatGetReturn(cUnit);
626 storeValue(cUnit, rlDest, rlResult);
627 // Rejoin code paths
628 ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
629 target2->defMask = ENCODE_ALL;
630 branch1->generic.target = (LIR*)target1;
631 branch2->generic.target = (LIR*)target2;
632 }
buzbee67bf8852011-08-17 17:51:35 -0700633}
634
buzbeeed3e9302011-09-23 17:34:19 -0700635STATIC void genConstString(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700636 RegLocation rlDest, RegLocation rlSrc)
637{
buzbee1b4c8592011-08-31 10:43:51 -0700638 /* All strings should be available at compile time */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700639 const art::String* str = cUnit->method->GetDexCacheStrings()->
buzbee1b4c8592011-08-31 10:43:51 -0700640 Get(mir->dalvikInsn.vB);
641 DCHECK(str != NULL);
buzbee67bf8852011-08-17 17:51:35 -0700642
buzbee1b4c8592011-08-31 10:43:51 -0700643 int mReg = loadCurrMethod(cUnit);
644 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700645 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700646 loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(),
buzbee1b4c8592011-08-31 10:43:51 -0700647 resReg);
648 loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
649 (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700650 storeValue(cUnit, rlDest, rlResult);
651}
652
buzbeedfd3d702011-08-28 12:56:51 -0700653/*
654 * Let helper function take care of everything. Will
655 * call Class::NewInstanceFromCode(type_idx, method);
656 */
buzbeeed3e9302011-09-23 17:34:19 -0700657STATIC void genNewInstance(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700658 RegLocation rlDest)
659{
buzbeedfd3d702011-08-28 12:56:51 -0700660 oatFlushAllRegs(cUnit); /* Everything to home location */
buzbee67bf8852011-08-17 17:51:35 -0700661 loadWordDisp(cUnit, rSELF,
Brian Carlstrom1f870082011-08-23 16:02:11 -0700662 OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
buzbeedfd3d702011-08-28 12:56:51 -0700663 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
664 loadConstant(cUnit, r0, mir->dalvikInsn.vB); // arg0 <- type_id
Ian Rogersff1ed472011-09-20 13:46:24 -0700665 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700666 oatClobberCallRegs(cUnit);
667 RegLocation rlResult = oatGetReturn(cUnit);
668 storeValue(cUnit, rlDest, rlResult);
669}
670
671void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
672{
673 loadWordDisp(cUnit, rSELF,
Ian Rogers67375ac2011-09-14 00:55:44 -0700674 OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
Ian Rogersbdb03912011-09-14 00:55:44 -0700675 loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
Ian Rogersff1ed472011-09-20 13:46:24 -0700676 callRuntimeHelper(cUnit, rLR); // art_deliver_exception(exception);
buzbee67bf8852011-08-17 17:51:35 -0700677}
678
buzbeeed3e9302011-09-23 17:34:19 -0700679STATIC void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700680 RegLocation rlSrc)
681{
buzbee2a475e72011-09-07 17:19:17 -0700682 // May generate a call - use explicit registers
683 oatLockCallTemps(cUnit);
684 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
685 Get(mir->dalvikInsn.vC);
686 int classReg = r2; // Fixed usage
687 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
688 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
689 classReg);
690 loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
691 (sizeof(String*) * mir->dalvikInsn.vC), classReg);
buzbee67bf8852011-08-17 17:51:35 -0700692 if (classPtr == NULL) {
buzbee2a475e72011-09-07 17:19:17 -0700693 // Generate a runtime test
694 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
695 // Not resolved
696 // Call out to helper, which will return resolved type in r0
697 loadWordDisp(cUnit, rSELF,
698 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
699 loadConstant(cUnit, r0, mir->dalvikInsn.vC);
Ian Rogersff1ed472011-09-20 13:46:24 -0700700 callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee2a475e72011-09-07 17:19:17 -0700701 genRegCopy(cUnit, r2, r0); // Align usage with fast path
702 // Rejoin code paths
703 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
704 hopTarget->defMask = ENCODE_ALL;
705 hopBranch->generic.target = (LIR*)hopTarget;
buzbee67bf8852011-08-17 17:51:35 -0700706 }
buzbee2a475e72011-09-07 17:19:17 -0700707 // At this point, r2 has class
708 loadValueDirectFixed(cUnit, rlSrc, r3); /* Ref */
buzbee67bf8852011-08-17 17:51:35 -0700709 /* When taken r0 has NULL which can be used for store directly */
Brian Carlstrom5d40f182011-09-26 22:29:18 -0700710 loadConstant(cUnit, r0, 0); /* Assume false */
buzbee2a475e72011-09-07 17:19:17 -0700711 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r3, 0);
712 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700713 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee2a475e72011-09-07 17:19:17 -0700714 loadWordDisp(cUnit, r3, Object::ClassOffset().Int32Value(), r1);
buzbee67bf8852011-08-17 17:51:35 -0700715 /* r1 now contains object->clazz */
716 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700717 OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -0700718 loadConstant(cUnit, r0, 1); /* Assume true */
719 opRegReg(cUnit, kOpCmp, r1, r2);
720 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq);
buzbee2a475e72011-09-07 17:19:17 -0700721 genRegCopy(cUnit, r0, r3);
buzbee67bf8852011-08-17 17:51:35 -0700722 genRegCopy(cUnit, r1, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -0700723 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700724 oatClobberCallRegs(cUnit);
725 /* branch target here */
726 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
727 target->defMask = ENCODE_ALL;
buzbee2a475e72011-09-07 17:19:17 -0700728 RegLocation rlResult = oatGetReturn(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700729 storeValue(cUnit, rlDest, rlResult);
730 branch1->generic.target = (LIR*)target;
731 branch2->generic.target = (LIR*)target;
732}
733
buzbeeed3e9302011-09-23 17:34:19 -0700734STATIC void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbee67bf8852011-08-17 17:51:35 -0700735{
buzbee2a475e72011-09-07 17:19:17 -0700736 // May generate a call - use explicit registers
737 oatLockCallTemps(cUnit);
738 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
739 Get(mir->dalvikInsn.vB);
740 int classReg = r2; // Fixed usage
741 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
742 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
743 classReg);
744 loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
745 (sizeof(String*) * mir->dalvikInsn.vB), classReg);
buzbee67bf8852011-08-17 17:51:35 -0700746 if (classPtr == NULL) {
buzbee2a475e72011-09-07 17:19:17 -0700747 // Generate a runtime test
748 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
749 // Not resolved
750 // Call out to helper, which will return resolved type in r0
751 loadWordDisp(cUnit, rSELF,
752 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
753 loadConstant(cUnit, r0, mir->dalvikInsn.vB);
Ian Rogersff1ed472011-09-20 13:46:24 -0700754 callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee2a475e72011-09-07 17:19:17 -0700755 genRegCopy(cUnit, r2, r0); // Align usage with fast path
756 // Rejoin code paths
757 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
758 hopTarget->defMask = ENCODE_ALL;
759 hopBranch->generic.target = (LIR*)hopTarget;
buzbee67bf8852011-08-17 17:51:35 -0700760 }
buzbee2a475e72011-09-07 17:19:17 -0700761 // At this point, r2 has class
762 loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */
763 /* Null is OK - continue */
764 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
765 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700766 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee2a475e72011-09-07 17:19:17 -0700767 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
768 /* r1 now contains object->clazz */
buzbee67bf8852011-08-17 17:51:35 -0700769 loadWordDisp(cUnit, rSELF,
buzbee2a475e72011-09-07 17:19:17 -0700770 OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
771 opRegReg(cUnit, kOpCmp, r1, r2);
772 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
773 genRegCopy(cUnit, r0, r1);
774 genRegCopy(cUnit, r1, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -0700775 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700776 oatClobberCallRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700777 /* branch target here */
buzbee67bf8852011-08-17 17:51:35 -0700778 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
779 target->defMask = ENCODE_ALL;
780 branch1->generic.target = (LIR*)target;
781 branch2->generic.target = (LIR*)target;
782}
783
buzbeeed3e9302011-09-23 17:34:19 -0700784STATIC void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700785 RegLocation rlSrc)
786{
787 RegLocation rlResult;
788 rlSrc = loadValue(cUnit, rlSrc, kFPReg);
789 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
790 newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
791 storeValue(cUnit, rlDest, rlResult);
792}
793
buzbeeed3e9302011-09-23 17:34:19 -0700794STATIC void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700795 RegLocation rlSrc)
796{
797 RegLocation rlResult;
798 rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
799 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
800 newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
801 S2D(rlSrc.lowReg, rlSrc.highReg));
802 storeValueWide(cUnit, rlDest, rlResult);
803}
804
buzbeeed3e9302011-09-23 17:34:19 -0700805STATIC void freeRegLocTemps(CompilationUnit* cUnit, RegLocation rlKeep,
buzbee439c4fa2011-08-27 15:59:07 -0700806 RegLocation rlFree)
buzbee67bf8852011-08-17 17:51:35 -0700807{
buzbee439c4fa2011-08-27 15:59:07 -0700808 if ((rlFree.lowReg != rlKeep.lowReg) && (rlFree.lowReg != rlKeep.highReg))
809 oatFreeTemp(cUnit, rlFree.lowReg);
810 if ((rlFree.highReg != rlKeep.lowReg) && (rlFree.highReg != rlKeep.highReg))
811 oatFreeTemp(cUnit, rlFree.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700812}
813
buzbeeed3e9302011-09-23 17:34:19 -0700814STATIC void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp,
buzbee67bf8852011-08-17 17:51:35 -0700815 OpKind secondOp, RegLocation rlDest,
816 RegLocation rlSrc1, RegLocation rlSrc2)
817{
buzbee9e0f9b02011-08-24 15:32:46 -0700818 /*
819 * NOTE: This is the one place in the code in which we might have
820 * as many as six live temporary registers. There are 5 in the normal
821 * set for Arm. Until we have spill capabilities, temporarily add
822 * lr to the temp set. It is safe to do this locally, but note that
823 * lr is used explicitly elsewhere in the code generator and cannot
824 * normally be used as a general temp register.
825 */
buzbee67bf8852011-08-17 17:51:35 -0700826 RegLocation rlResult;
buzbee9e0f9b02011-08-24 15:32:46 -0700827 oatMarkTemp(cUnit, rLR); // Add lr to the temp pool
828 oatFreeTemp(cUnit, rLR); // and make it available
buzbee67bf8852011-08-17 17:51:35 -0700829 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
830 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
831 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeec0ecd652011-09-25 18:11:54 -0700832 // The longs may overlap - use intermediate temp if so
833 if (rlResult.lowReg == rlSrc1.highReg) {
buzbeec0ecd652011-09-25 18:11:54 -0700834 int tReg = oatAllocTemp(cUnit);
835 genRegCopy(cUnit, tReg, rlSrc1.highReg);
836 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
837 rlSrc2.lowReg);
838 opRegRegReg(cUnit, secondOp, rlResult.highReg, tReg,
839 rlSrc2.highReg);
840 oatFreeTemp(cUnit, tReg);
841 } else {
842 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
843 rlSrc2.lowReg);
844 opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
845 rlSrc2.highReg);
846 }
buzbee439c4fa2011-08-27 15:59:07 -0700847 /*
848 * NOTE: If rlDest refers to a frame variable in a large frame, the
849 * following storeValueWide might need to allocate a temp register.
850 * To further work around the lack of a spill capability, explicitly
851 * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult.
852 * Remove when spill is functional.
853 */
854 freeRegLocTemps(cUnit, rlResult, rlSrc1);
855 freeRegLocTemps(cUnit, rlResult, rlSrc2);
buzbee67bf8852011-08-17 17:51:35 -0700856 storeValueWide(cUnit, rlDest, rlResult);
buzbee9e0f9b02011-08-24 15:32:46 -0700857 oatClobber(cUnit, rLR);
858 oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool
buzbee67bf8852011-08-17 17:51:35 -0700859}
860
861void oatInitializeRegAlloc(CompilationUnit* cUnit)
862{
863 int numRegs = sizeof(coreRegs)/sizeof(*coreRegs);
864 int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs);
865 int numTemps = sizeof(coreTemps)/sizeof(*coreTemps);
866 int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs);
867 int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps);
868 RegisterPool *pool = (RegisterPool *)oatNew(sizeof(*pool), true);
869 cUnit->regPool = pool;
870 pool->numCoreRegs = numRegs;
871 pool->coreRegs = (RegisterInfo *)
872 oatNew(numRegs * sizeof(*cUnit->regPool->coreRegs), true);
873 pool->numFPRegs = numFPRegs;
874 pool->FPRegs = (RegisterInfo *)
875 oatNew(numFPRegs * sizeof(*cUnit->regPool->FPRegs), true);
876 oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs);
877 oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs);
878 // Keep special registers from being allocated
879 for (int i = 0; i < numReserved; i++) {
buzbeec0ecd652011-09-25 18:11:54 -0700880 if (NO_SUSPEND && (reservedRegs[i] == rSUSPEND)) {
881 //To measure cost of suspend check
882 continue;
883 }
buzbee67bf8852011-08-17 17:51:35 -0700884 oatMarkInUse(cUnit, reservedRegs[i]);
885 }
886 // Mark temp regs - all others not in use can be used for promotion
887 for (int i = 0; i < numTemps; i++) {
888 oatMarkTemp(cUnit, coreTemps[i]);
889 }
890 for (int i = 0; i < numFPTemps; i++) {
891 oatMarkTemp(cUnit, fpTemps[i]);
892 }
buzbeec0ecd652011-09-25 18:11:54 -0700893 // Construct the alias map.
894 cUnit->phiAliasMap = (int*)oatNew(cUnit->numSSARegs *
895 sizeof(cUnit->phiAliasMap[0]), false);
896 for (int i = 0; i < cUnit->numSSARegs; i++) {
897 cUnit->phiAliasMap[i] = i;
898 }
899 for (MIR* phi = cUnit->phiList; phi; phi = phi->meta.phiNext) {
900 int defReg = phi->ssaRep->defs[0];
901 for (int i = 0; i < phi->ssaRep->numUses; i++) {
902 for (int j = 0; j < cUnit->numSSARegs; j++) {
903 if (cUnit->phiAliasMap[j] == phi->ssaRep->uses[i]) {
904 cUnit->phiAliasMap[j] = defReg;
905 }
906 }
907 }
908 }
buzbee67bf8852011-08-17 17:51:35 -0700909}
910
911/*
912 * Handle simple case (thin lock) inline. If it's complicated, bail
913 * out to the heavyweight lock/unlock routines. We'll use dedicated
914 * registers here in order to be in the right position in case we
915 * to bail to dvm[Lock/Unlock]Object(self, object)
916 *
917 * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
918 * r1 -> object [arg1 for dvm[Lock/Unlock]Object
919 * r2 -> intial contents of object->lock, later result of strex
920 * r3 -> self->threadId
921 * r12 -> allow to be used by utilities as general temp
922 *
923 * The result of the strex is 0 if we acquire the lock.
924 *
925 * See comments in Sync.c for the layout of the lock word.
926 * Of particular interest to this code is the test for the
927 * simple case - which we handle inline. For monitor enter, the
928 * simple case is thin lock, held by no-one. For monitor exit,
929 * the simple case is thin lock, held by the unlocking thread with
930 * a recurse count of 0.
931 *
932 * A minor complication is that there is a field in the lock word
933 * unrelated to locking: the hash state. This field must be ignored, but
934 * preserved.
935 *
936 */
buzbeeed3e9302011-09-23 17:34:19 -0700937STATIC void genMonitorEnter(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700938 RegLocation rlSrc)
939{
940 ArmLIR* target;
941 ArmLIR* hopTarget;
942 ArmLIR* branch;
943 ArmLIR* hopBranch;
944
945 oatFlushAllRegs(cUnit);
Elliott Hughes5f791332011-09-15 17:45:30 -0700946 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -0700947 loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700948 oatLockCallTemps(cUnit); // Prepare for explicit register usage
buzbee5ade1d22011-09-09 14:44:52 -0700949 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
Elliott Hughes54e7df12011-09-16 11:47:04 -0700950 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -0700951 newLIR3(cUnit, kThumb2Ldrex, r2, r1,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700952 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
buzbeec143c552011-08-20 17:38:58 -0700953 // Align owner
Elliott Hughes5f791332011-09-15 17:45:30 -0700954 opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT);
buzbee67bf8852011-08-17 17:51:35 -0700955 // Is lock unheld on lock or held by us (==threadId) on unlock?
Elliott Hughes5f791332011-09-15 17:45:30 -0700956 newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, LW_LOCK_OWNER_SHIFT - 1);
957 newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
buzbee67bf8852011-08-17 17:51:35 -0700958 hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0);
buzbeec143c552011-08-20 17:38:58 -0700959 newLIR4(cUnit, kThumb2Strex, r2, r3, r1,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700960 Object::MonitorOffset().Int32Value() >> 2);
buzbee67bf8852011-08-17 17:51:35 -0700961 oatGenMemBarrier(cUnit, kSY);
962 branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
963
964 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
965 hopTarget->defMask = ENCODE_ALL;
966 hopBranch->generic.target = (LIR*)hopTarget;
967
buzbee1b4c8592011-08-31 10:43:51 -0700968 // Go expensive route - artLockObjectFromCode(self, obj);
969 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700970 rLR);
971 genRegCopy(cUnit, r0, rSELF);
Ian Rogersff1ed472011-09-20 13:46:24 -0700972 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700973
974 // Resume here
975 target = newLIR0(cUnit, kArmPseudoTargetLabel);
976 target->defMask = ENCODE_ALL;
977 branch->generic.target = (LIR*)target;
978}
979
980/*
981 * For monitor unlock, we don't have to use ldrex/strex. Once
982 * we've determined that the lock is thin and that we own it with
983 * a zero recursion count, it's safe to punch it back to the
984 * initial, unlock thin state with a store word.
985 */
buzbeeed3e9302011-09-23 17:34:19 -0700986STATIC void genMonitorExit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700987 RegLocation rlSrc)
988{
989 ArmLIR* target;
990 ArmLIR* branch;
991 ArmLIR* hopTarget;
992 ArmLIR* hopBranch;
993
Elliott Hughes5f791332011-09-15 17:45:30 -0700994 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -0700995 oatFlushAllRegs(cUnit);
996 loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700997 oatLockCallTemps(cUnit); // Prepare for explicit register usage
buzbee5ade1d22011-09-09 14:44:52 -0700998 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700999 loadWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r2); // Get lock
Elliott Hughes54e7df12011-09-16 11:47:04 -07001000 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -07001001 // Is lock unheld on lock or held by us (==threadId) on unlock?
Elliott Hughes5f791332011-09-15 17:45:30 -07001002 opRegRegImm(cUnit, kOpAnd, r12, r2, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
buzbeec143c552011-08-20 17:38:58 -07001003 // Align owner
Elliott Hughes5f791332011-09-15 17:45:30 -07001004 opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT);
1005 newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
buzbee67bf8852011-08-17 17:51:35 -07001006 opRegReg(cUnit, kOpSub, r2, r3);
1007 hopBranch = opCondBranch(cUnit, kArmCondNe);
1008 oatGenMemBarrier(cUnit, kSY);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001009 storeWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r12);
buzbee67bf8852011-08-17 17:51:35 -07001010 branch = opNone(cUnit, kOpUncondBr);
1011
1012 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
1013 hopTarget->defMask = ENCODE_ALL;
1014 hopBranch->generic.target = (LIR*)hopTarget;
1015
buzbee1b4c8592011-08-31 10:43:51 -07001016 // Go expensive route - UnlockObjectFromCode(self, obj);
1017 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -07001018 rLR);
1019 genRegCopy(cUnit, r0, rSELF);
Ian Rogersff1ed472011-09-20 13:46:24 -07001020 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001021
1022 // Resume here
1023 target = newLIR0(cUnit, kArmPseudoTargetLabel);
1024 target->defMask = ENCODE_ALL;
1025 branch->generic.target = (LIR*)target;
1026}
1027
1028/*
1029 * 64-bit 3way compare function.
1030 * mov rX, #-1
1031 * cmp op1hi, op2hi
1032 * blt done
1033 * bgt flip
1034 * sub rX, op1lo, op2lo (treat as unsigned)
1035 * beq done
1036 * ite hi
1037 * mov(hi) rX, #-1
1038 * mov(!hi) rX, #1
1039 * flip:
1040 * neg rX
1041 * done:
1042 */
buzbeeed3e9302011-09-23 17:34:19 -07001043STATIC void genCmpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001044 RegLocation rlDest, RegLocation rlSrc1,
1045 RegLocation rlSrc2)
1046{
buzbee67bf8852011-08-17 17:51:35 -07001047 ArmLIR* target1;
1048 ArmLIR* target2;
1049 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
1050 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
buzbeeb29e4d12011-09-26 15:05:48 -07001051 int tReg = oatAllocTemp(cUnit);
1052 loadConstant(cUnit, tReg, -1);
buzbee67bf8852011-08-17 17:51:35 -07001053 opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
1054 ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt);
1055 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt);
buzbeeb29e4d12011-09-26 15:05:48 -07001056 opRegRegReg(cUnit, kOpSub, tReg, rlSrc1.lowReg, rlSrc2.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001057 ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq);
1058
1059 genIT(cUnit, kArmCondHi, "E");
buzbeeb29e4d12011-09-26 15:05:48 -07001060 newLIR2(cUnit, kThumb2MovImmShift, tReg, modifiedImmediate(-1));
1061 loadConstant(cUnit, tReg, 1);
buzbee67bf8852011-08-17 17:51:35 -07001062 genBarrier(cUnit);
1063
1064 target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
1065 target2->defMask = -1;
buzbeeb29e4d12011-09-26 15:05:48 -07001066 opRegReg(cUnit, kOpNeg, tReg, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001067
1068 target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
1069 target1->defMask = -1;
1070
buzbeeb29e4d12011-09-26 15:05:48 -07001071 RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
1072 rlTemp.lowReg = tReg;
buzbee67bf8852011-08-17 17:51:35 -07001073 storeValue(cUnit, rlDest, rlTemp);
buzbeeb29e4d12011-09-26 15:05:48 -07001074 oatFreeTemp(cUnit, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001075
1076 branch1->generic.target = (LIR*)target1;
1077 branch2->generic.target = (LIR*)target2;
1078 branch3->generic.target = branch1->generic.target;
1079}
1080
buzbeeed3e9302011-09-23 17:34:19 -07001081STATIC void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001082 RegLocation rlSrc, RegLocation rlResult, int lit,
1083 int firstBit, int secondBit)
1084{
1085 opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
1086 encodeShift(kArmLsl, secondBit - firstBit));
1087 if (firstBit != 0) {
1088 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
1089 }
1090}
1091
buzbeeed3e9302011-09-23 17:34:19 -07001092STATIC bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset,
buzbee67bf8852011-08-17 17:51:35 -07001093 int srcSize, int tgtSize)
1094{
1095 /*
1096 * Don't optimize the register usage since it calls out to support
1097 * functions
1098 */
1099 RegLocation rlSrc;
1100 RegLocation rlDest;
1101 oatFlushAllRegs(cUnit); /* Send everything to home location */
1102 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1103 if (srcSize == 1) {
1104 rlSrc = oatGetSrc(cUnit, mir, 0);
1105 loadValueDirectFixed(cUnit, rlSrc, r0);
1106 } else {
1107 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
1108 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
1109 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001110 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001111 oatClobberCallRegs(cUnit);
1112 if (tgtSize == 1) {
1113 RegLocation rlResult;
1114 rlDest = oatGetDest(cUnit, mir, 0);
1115 rlResult = oatGetReturn(cUnit);
1116 storeValue(cUnit, rlDest, rlResult);
1117 } else {
1118 RegLocation rlResult;
1119 rlDest = oatGetDestWide(cUnit, mir, 0, 1);
1120 rlResult = oatGetReturnWide(cUnit);
1121 storeValueWide(cUnit, rlDest, rlResult);
1122 }
1123 return false;
1124}
1125
buzbeeed3e9302011-09-23 17:34:19 -07001126STATIC bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001127 RegLocation rlDest, RegLocation rlSrc1,
1128 RegLocation rlSrc2)
1129{
1130 RegLocation rlResult;
1131 int funcOffset;
1132
1133 switch (mir->dalvikInsn.opcode) {
1134 case OP_ADD_FLOAT_2ADDR:
1135 case OP_ADD_FLOAT:
1136 funcOffset = OFFSETOF_MEMBER(Thread, pFadd);
1137 break;
1138 case OP_SUB_FLOAT_2ADDR:
1139 case OP_SUB_FLOAT:
1140 funcOffset = OFFSETOF_MEMBER(Thread, pFsub);
1141 break;
1142 case OP_DIV_FLOAT_2ADDR:
1143 case OP_DIV_FLOAT:
1144 funcOffset = OFFSETOF_MEMBER(Thread, pFdiv);
1145 break;
1146 case OP_MUL_FLOAT_2ADDR:
1147 case OP_MUL_FLOAT:
1148 funcOffset = OFFSETOF_MEMBER(Thread, pFmul);
1149 break;
1150 case OP_REM_FLOAT_2ADDR:
1151 case OP_REM_FLOAT:
1152 funcOffset = OFFSETOF_MEMBER(Thread, pFmodf);
1153 break;
1154 case OP_NEG_FLOAT: {
1155 genNegFloat(cUnit, rlDest, rlSrc1);
1156 return false;
1157 }
1158 default:
1159 return true;
1160 }
1161 oatFlushAllRegs(cUnit); /* Send everything to home location */
1162 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1163 loadValueDirectFixed(cUnit, rlSrc1, r0);
1164 loadValueDirectFixed(cUnit, rlSrc2, r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001165 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001166 oatClobberCallRegs(cUnit);
1167 rlResult = oatGetReturn(cUnit);
1168 storeValue(cUnit, rlDest, rlResult);
1169 return false;
1170}
1171
buzbeeed3e9302011-09-23 17:34:19 -07001172STATIC bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001173 RegLocation rlDest, RegLocation rlSrc1,
1174 RegLocation rlSrc2)
1175{
1176 RegLocation rlResult;
1177 int funcOffset;
1178
1179 switch (mir->dalvikInsn.opcode) {
1180 case OP_ADD_DOUBLE_2ADDR:
1181 case OP_ADD_DOUBLE:
1182 funcOffset = OFFSETOF_MEMBER(Thread, pDadd);
1183 break;
1184 case OP_SUB_DOUBLE_2ADDR:
1185 case OP_SUB_DOUBLE:
1186 funcOffset = OFFSETOF_MEMBER(Thread, pDsub);
1187 break;
1188 case OP_DIV_DOUBLE_2ADDR:
1189 case OP_DIV_DOUBLE:
1190 funcOffset = OFFSETOF_MEMBER(Thread, pDdiv);
1191 break;
1192 case OP_MUL_DOUBLE_2ADDR:
1193 case OP_MUL_DOUBLE:
1194 funcOffset = OFFSETOF_MEMBER(Thread, pDmul);
1195 break;
1196 case OP_REM_DOUBLE_2ADDR:
1197 case OP_REM_DOUBLE:
1198 funcOffset = OFFSETOF_MEMBER(Thread, pFmod);
1199 break;
1200 case OP_NEG_DOUBLE: {
1201 genNegDouble(cUnit, rlDest, rlSrc1);
1202 return false;
1203 }
1204 default:
1205 return true;
1206 }
1207 oatFlushAllRegs(cUnit); /* Send everything to home location */
1208 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1209 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1210 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
Ian Rogersff1ed472011-09-20 13:46:24 -07001211 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001212 oatClobberCallRegs(cUnit);
1213 rlResult = oatGetReturnWide(cUnit);
1214 storeValueWide(cUnit, rlDest, rlResult);
1215 return false;
1216}
1217
buzbeeed3e9302011-09-23 17:34:19 -07001218STATIC bool genConversionPortable(CompilationUnit* cUnit, MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -07001219{
1220 Opcode opcode = mir->dalvikInsn.opcode;
1221
1222 switch (opcode) {
1223 case OP_INT_TO_FLOAT:
1224 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f),
1225 1, 1);
1226 case OP_FLOAT_TO_INT:
1227 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz),
1228 1, 1);
1229 case OP_DOUBLE_TO_FLOAT:
1230 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f),
1231 2, 1);
1232 case OP_FLOAT_TO_DOUBLE:
1233 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d),
1234 1, 2);
1235 case OP_INT_TO_DOUBLE:
1236 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d),
1237 1, 2);
1238 case OP_DOUBLE_TO_INT:
1239 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz),
1240 2, 1);
1241 case OP_FLOAT_TO_LONG:
1242 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001243 pF2l), 1, 2);
buzbee67bf8852011-08-17 17:51:35 -07001244 case OP_LONG_TO_FLOAT:
1245 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f),
1246 2, 1);
1247 case OP_DOUBLE_TO_LONG:
1248 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001249 pD2l), 2, 2);
buzbee67bf8852011-08-17 17:51:35 -07001250 case OP_LONG_TO_DOUBLE:
1251 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d),
1252 2, 2);
1253 default:
1254 return true;
1255 }
1256 return false;
1257}
1258
1259/* Generate conditional branch instructions */
buzbeeed3e9302011-09-23 17:34:19 -07001260STATIC ArmLIR* genConditionalBranch(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001261 ArmConditionCode cond,
1262 ArmLIR* target)
1263{
1264 ArmLIR* branch = opCondBranch(cUnit, cond);
1265 branch->generic.target = (LIR*) target;
1266 return branch;
1267}
1268
buzbee67bf8852011-08-17 17:51:35 -07001269/*
1270 * Generate array store
1271 *
1272 */
buzbeeed3e9302011-09-23 17:34:19 -07001273STATIC void genArrayObjPut(CompilationUnit* cUnit, MIR* mir,
buzbee1b4c8592011-08-31 10:43:51 -07001274 RegLocation rlArray, RegLocation rlIndex,
1275 RegLocation rlSrc, int scale)
buzbee67bf8852011-08-17 17:51:35 -07001276{
1277 RegisterClass regClass = oatRegClassBySize(kWord);
buzbeec143c552011-08-20 17:38:58 -07001278 int lenOffset = Array::LengthOffset().Int32Value();
1279 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001280
1281 /* Make sure it's a legal object Put. Use direct regs at first */
1282 loadValueDirectFixed(cUnit, rlArray, r1);
1283 loadValueDirectFixed(cUnit, rlSrc, r0);
1284
1285 /* null array object? */
buzbee43a36422011-09-14 14:00:13 -07001286 genNullCheck(cUnit, rlArray.sRegLow, r1, mir);
buzbee67bf8852011-08-17 17:51:35 -07001287 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -07001288 OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -07001289 /* Get the array's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001290 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001291 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001292 oatClobberCallRegs(cUnit);
1293
1294 // Now, redo loadValues in case they didn't survive the call
1295
1296 int regPtr;
1297 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1298 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1299
1300 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1301 oatClobber(cUnit, rlArray.lowReg);
1302 regPtr = rlArray.lowReg;
1303 } else {
1304 regPtr = oatAllocTemp(cUnit);
1305 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1306 }
1307
buzbee43a36422011-09-14 14:00:13 -07001308 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001309 int regLen = oatAllocTemp(cUnit);
1310 //NOTE: max live temps(4) here.
1311 /* Get len */
1312 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1313 /* regPtr -> array data */
1314 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001315 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001316 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001317 oatFreeTemp(cUnit, regLen);
1318 } else {
1319 /* regPtr -> array data */
1320 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1321 }
1322 /* at this point, regPtr points to array, 2 live temps */
1323 rlSrc = loadValue(cUnit, rlSrc, regClass);
1324 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1325 scale, kWord);
1326}
1327
1328/*
1329 * Generate array load
1330 */
buzbeeed3e9302011-09-23 17:34:19 -07001331STATIC void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001332 RegLocation rlArray, RegLocation rlIndex,
1333 RegLocation rlDest, int scale)
1334{
1335 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001336 int lenOffset = Array::LengthOffset().Int32Value();
1337 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001338 RegLocation rlResult;
1339 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1340 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1341 int regPtr;
1342
1343 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001344 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001345
1346 regPtr = oatAllocTemp(cUnit);
1347
buzbee43a36422011-09-14 14:00:13 -07001348 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001349 int regLen = oatAllocTemp(cUnit);
1350 /* Get len */
1351 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1352 /* regPtr -> array data */
1353 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001354 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001355 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001356 oatFreeTemp(cUnit, regLen);
1357 } else {
1358 /* regPtr -> array data */
1359 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
1360 }
buzbeee9a72f62011-09-04 17:59:07 -07001361 oatFreeTemp(cUnit, rlArray.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001362 if ((size == kLong) || (size == kDouble)) {
1363 if (scale) {
1364 int rNewIndex = oatAllocTemp(cUnit);
1365 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1366 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1367 oatFreeTemp(cUnit, rNewIndex);
1368 } else {
1369 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1370 }
buzbeee9a72f62011-09-04 17:59:07 -07001371 oatFreeTemp(cUnit, rlIndex.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001372 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1373
1374 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
1375
1376 oatFreeTemp(cUnit, regPtr);
1377 storeValueWide(cUnit, rlDest, rlResult);
1378 } else {
1379 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1380
1381 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
1382 scale, size);
1383
1384 oatFreeTemp(cUnit, regPtr);
1385 storeValue(cUnit, rlDest, rlResult);
1386 }
1387}
1388
1389/*
1390 * Generate array store
1391 *
1392 */
buzbeeed3e9302011-09-23 17:34:19 -07001393STATIC void genArrayPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001394 RegLocation rlArray, RegLocation rlIndex,
1395 RegLocation rlSrc, int scale)
1396{
1397 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001398 int lenOffset = Array::LengthOffset().Int32Value();
1399 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001400
1401 int regPtr;
1402 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1403 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1404
1405 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1406 oatClobber(cUnit, rlArray.lowReg);
1407 regPtr = rlArray.lowReg;
1408 } else {
1409 regPtr = oatAllocTemp(cUnit);
1410 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1411 }
1412
1413 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001414 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001415
buzbee43a36422011-09-14 14:00:13 -07001416 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001417 int regLen = oatAllocTemp(cUnit);
1418 //NOTE: max live temps(4) here.
1419 /* Get len */
1420 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1421 /* regPtr -> array data */
1422 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001423 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001424 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001425 oatFreeTemp(cUnit, regLen);
1426 } else {
1427 /* regPtr -> array data */
1428 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1429 }
1430 /* at this point, regPtr points to array, 2 live temps */
1431 if ((size == kLong) || (size == kDouble)) {
buzbee5ade1d22011-09-09 14:44:52 -07001432 //TUNING: specific wide routine that can handle fp regs
buzbee67bf8852011-08-17 17:51:35 -07001433 if (scale) {
1434 int rNewIndex = oatAllocTemp(cUnit);
1435 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1436 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1437 oatFreeTemp(cUnit, rNewIndex);
1438 } else {
1439 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1440 }
1441 rlSrc = loadValueWide(cUnit, rlSrc, regClass);
1442
1443 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
1444
1445 oatFreeTemp(cUnit, regPtr);
1446 } else {
1447 rlSrc = loadValue(cUnit, rlSrc, regClass);
1448
1449 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1450 scale, size);
1451 }
1452}
1453
buzbeeed3e9302011-09-23 17:34:19 -07001454STATIC bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001455 RegLocation rlDest, RegLocation rlSrc1,
1456 RegLocation rlShift)
1457{
buzbee54330722011-08-23 16:46:55 -07001458 int funcOffset;
buzbee67bf8852011-08-17 17:51:35 -07001459
buzbee67bf8852011-08-17 17:51:35 -07001460 switch( mir->dalvikInsn.opcode) {
1461 case OP_SHL_LONG:
1462 case OP_SHL_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001463 funcOffset = OFFSETOF_MEMBER(Thread, pShlLong);
buzbee67bf8852011-08-17 17:51:35 -07001464 break;
1465 case OP_SHR_LONG:
1466 case OP_SHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001467 funcOffset = OFFSETOF_MEMBER(Thread, pShrLong);
buzbee67bf8852011-08-17 17:51:35 -07001468 break;
1469 case OP_USHR_LONG:
1470 case OP_USHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001471 funcOffset = OFFSETOF_MEMBER(Thread, pUshrLong);
buzbee67bf8852011-08-17 17:51:35 -07001472 break;
1473 default:
buzbee54330722011-08-23 16:46:55 -07001474 LOG(FATAL) << "Unexpected case";
buzbee67bf8852011-08-17 17:51:35 -07001475 return true;
1476 }
buzbee54330722011-08-23 16:46:55 -07001477 oatFlushAllRegs(cUnit); /* Send everything to home location */
1478 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1479 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1480 loadValueDirect(cUnit, rlShift, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -07001481 callRuntimeHelper(cUnit, rLR);
buzbee54330722011-08-23 16:46:55 -07001482 oatClobberCallRegs(cUnit);
1483 RegLocation rlResult = oatGetReturnWide(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001484 storeValueWide(cUnit, rlDest, rlResult);
1485 return false;
1486}
1487
buzbeeed3e9302011-09-23 17:34:19 -07001488STATIC bool genArithOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001489 RegLocation rlDest, RegLocation rlSrc1,
1490 RegLocation rlSrc2)
1491{
1492 RegLocation rlResult;
1493 OpKind firstOp = kOpBkpt;
1494 OpKind secondOp = kOpBkpt;
1495 bool callOut = false;
1496 int funcOffset;
1497 int retReg = r0;
1498
1499 switch (mir->dalvikInsn.opcode) {
1500 case OP_NOT_LONG:
1501 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1502 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001503 // Check for destructive overlap
1504 if (rlResult.lowReg == rlSrc2.highReg) {
1505 int tReg = oatAllocTemp(cUnit);
1506 genRegCopy(cUnit, tReg, rlSrc2.highReg);
1507 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1508 opRegReg(cUnit, kOpMvn, rlResult.highReg, tReg);
1509 oatFreeTemp(cUnit, tReg);
1510 } else {
1511 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1512 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
1513 }
buzbee67bf8852011-08-17 17:51:35 -07001514 storeValueWide(cUnit, rlDest, rlResult);
1515 return false;
1516 break;
1517 case OP_ADD_LONG:
1518 case OP_ADD_LONG_2ADDR:
1519 firstOp = kOpAdd;
1520 secondOp = kOpAdc;
1521 break;
1522 case OP_SUB_LONG:
1523 case OP_SUB_LONG_2ADDR:
1524 firstOp = kOpSub;
1525 secondOp = kOpSbc;
1526 break;
1527 case OP_MUL_LONG:
1528 case OP_MUL_LONG_2ADDR:
buzbee439c4fa2011-08-27 15:59:07 -07001529 callOut = true;
1530 retReg = r0;
1531 funcOffset = OFFSETOF_MEMBER(Thread, pLmul);
1532 break;
buzbee67bf8852011-08-17 17:51:35 -07001533 case OP_DIV_LONG:
1534 case OP_DIV_LONG_2ADDR:
1535 callOut = true;
1536 retReg = r0;
1537 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1538 break;
1539 /* NOTE - result is in r2/r3 instead of r0/r1 */
1540 case OP_REM_LONG:
1541 case OP_REM_LONG_2ADDR:
1542 callOut = true;
1543 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1544 retReg = r2;
1545 break;
1546 case OP_AND_LONG_2ADDR:
1547 case OP_AND_LONG:
1548 firstOp = kOpAnd;
1549 secondOp = kOpAnd;
1550 break;
1551 case OP_OR_LONG:
1552 case OP_OR_LONG_2ADDR:
1553 firstOp = kOpOr;
1554 secondOp = kOpOr;
1555 break;
1556 case OP_XOR_LONG:
1557 case OP_XOR_LONG_2ADDR:
1558 firstOp = kOpXor;
1559 secondOp = kOpXor;
1560 break;
1561 case OP_NEG_LONG: {
buzbee67bf8852011-08-17 17:51:35 -07001562 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1563 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001564 int zReg = oatAllocTemp(cUnit);
1565 loadConstantNoClobber(cUnit, zReg, 0);
1566 // Check for destructive overlap
1567 if (rlResult.lowReg == rlSrc2.highReg) {
1568 int tReg = oatAllocTemp(cUnit);
1569 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1570 zReg, rlSrc2.lowReg);
1571 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1572 zReg, tReg);
1573 oatFreeTemp(cUnit, tReg);
1574 } else {
1575 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1576 zReg, rlSrc2.lowReg);
1577 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1578 zReg, rlSrc2.highReg);
1579 }
1580 oatFreeTemp(cUnit, zReg);
buzbee67bf8852011-08-17 17:51:35 -07001581 storeValueWide(cUnit, rlDest, rlResult);
1582 return false;
1583 }
1584 default:
1585 LOG(FATAL) << "Invalid long arith op";
1586 }
1587 if (!callOut) {
1588 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
1589 } else {
1590 // Adjust return regs in to handle case of rem returning r2/r3
1591 oatFlushAllRegs(cUnit); /* Send everything to home location */
1592 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1593 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1594 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
Ian Rogersff1ed472011-09-20 13:46:24 -07001595 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001596 oatClobberCallRegs(cUnit);
1597 if (retReg == r0)
1598 rlResult = oatGetReturnWide(cUnit);
1599 else
1600 rlResult = oatGetReturnWideAlt(cUnit);
1601 storeValueWide(cUnit, rlDest, rlResult);
1602 }
1603 return false;
1604}
1605
buzbeeed3e9302011-09-23 17:34:19 -07001606STATIC bool genArithOpInt(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001607 RegLocation rlDest, RegLocation rlSrc1,
1608 RegLocation rlSrc2)
1609{
1610 OpKind op = kOpBkpt;
1611 bool callOut = false;
1612 bool checkZero = false;
1613 bool unary = false;
1614 int retReg = r0;
1615 int funcOffset;
1616 RegLocation rlResult;
1617 bool shiftOp = false;
1618
1619 switch (mir->dalvikInsn.opcode) {
1620 case OP_NEG_INT:
1621 op = kOpNeg;
1622 unary = true;
1623 break;
1624 case OP_NOT_INT:
1625 op = kOpMvn;
1626 unary = true;
1627 break;
1628 case OP_ADD_INT:
1629 case OP_ADD_INT_2ADDR:
1630 op = kOpAdd;
1631 break;
1632 case OP_SUB_INT:
1633 case OP_SUB_INT_2ADDR:
1634 op = kOpSub;
1635 break;
1636 case OP_MUL_INT:
1637 case OP_MUL_INT_2ADDR:
1638 op = kOpMul;
1639 break;
1640 case OP_DIV_INT:
1641 case OP_DIV_INT_2ADDR:
1642 callOut = true;
1643 checkZero = true;
1644 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1645 retReg = r0;
1646 break;
1647 /* NOTE: returns in r1 */
1648 case OP_REM_INT:
1649 case OP_REM_INT_2ADDR:
1650 callOut = true;
1651 checkZero = true;
1652 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1653 retReg = r1;
1654 break;
1655 case OP_AND_INT:
1656 case OP_AND_INT_2ADDR:
1657 op = kOpAnd;
1658 break;
1659 case OP_OR_INT:
1660 case OP_OR_INT_2ADDR:
1661 op = kOpOr;
1662 break;
1663 case OP_XOR_INT:
1664 case OP_XOR_INT_2ADDR:
1665 op = kOpXor;
1666 break;
1667 case OP_SHL_INT:
1668 case OP_SHL_INT_2ADDR:
1669 shiftOp = true;
1670 op = kOpLsl;
1671 break;
1672 case OP_SHR_INT:
1673 case OP_SHR_INT_2ADDR:
1674 shiftOp = true;
1675 op = kOpAsr;
1676 break;
1677 case OP_USHR_INT:
1678 case OP_USHR_INT_2ADDR:
1679 shiftOp = true;
1680 op = kOpLsr;
1681 break;
1682 default:
1683 LOG(FATAL) << "Invalid word arith op: " <<
1684 (int)mir->dalvikInsn.opcode;
1685 }
1686 if (!callOut) {
1687 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
1688 if (unary) {
1689 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1690 opRegReg(cUnit, op, rlResult.lowReg,
1691 rlSrc1.lowReg);
1692 } else {
1693 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
1694 if (shiftOp) {
1695 int tReg = oatAllocTemp(cUnit);
1696 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
1697 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1698 opRegRegReg(cUnit, op, rlResult.lowReg,
1699 rlSrc1.lowReg, tReg);
1700 oatFreeTemp(cUnit, tReg);
1701 } else {
1702 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1703 opRegRegReg(cUnit, op, rlResult.lowReg,
1704 rlSrc1.lowReg, rlSrc2.lowReg);
1705 }
1706 }
1707 storeValue(cUnit, rlDest, rlResult);
1708 } else {
1709 RegLocation rlResult;
1710 oatFlushAllRegs(cUnit); /* Send everything to home location */
1711 loadValueDirectFixed(cUnit, rlSrc2, r1);
1712 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1713 loadValueDirectFixed(cUnit, rlSrc1, r0);
1714 if (checkZero) {
buzbee5ade1d22011-09-09 14:44:52 -07001715 genImmedCheck(cUnit, kArmCondEq, r1, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001716 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001717 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001718 oatClobberCallRegs(cUnit);
1719 if (retReg == r0)
1720 rlResult = oatGetReturn(cUnit);
1721 else
1722 rlResult = oatGetReturnAlt(cUnit);
1723 storeValue(cUnit, rlDest, rlResult);
1724 }
1725 return false;
1726}
1727
buzbeec1f45042011-09-21 16:03:19 -07001728/* Check if we need to check for pending suspend request */
buzbeeed3e9302011-09-23 17:34:19 -07001729STATIC void genSuspendTest(CompilationUnit* cUnit, MIR* mir)
buzbeec1f45042011-09-21 16:03:19 -07001730{
buzbeec0ecd652011-09-25 18:11:54 -07001731 if (NO_SUSPEND || mir->optimizationFlags & MIR_IGNORE_SUSPEND_CHECK) {
buzbeec1f45042011-09-21 16:03:19 -07001732 return;
1733 }
1734 newLIR2(cUnit, kThumbSubRI8, rSUSPEND, 1);
1735 ArmLIR* branch = opCondBranch(cUnit, kArmCondEq);
1736 ArmLIR* retLab = newLIR0(cUnit, kArmPseudoTargetLabel);
1737 retLab->defMask = ENCODE_ALL;
1738 ArmLIR* target = (ArmLIR*)oatNew(sizeof(ArmLIR), true);
1739 target->generic.dalvikOffset = cUnit->currentDalvikOffset;
1740 target->opcode = kArmPseudoSuspendTarget;
1741 target->operands[0] = (intptr_t)retLab;
1742 target->operands[1] = mir->offset;
1743 branch->generic.target = (LIR*)target;
1744 oatInsertGrowableList(&cUnit->suspendLaunchpads, (intptr_t)target);
1745}
1746
buzbee0d966cf2011-09-08 17:34:58 -07001747/* Check for pending suspend request. */
buzbeeed3e9302011-09-23 17:34:19 -07001748STATIC void genSuspendPoll(CompilationUnit* cUnit, MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -07001749{
buzbeec0ecd652011-09-25 18:11:54 -07001750 if (NO_SUSPEND || mir->optimizationFlags & MIR_IGNORE_SUSPEND_CHECK) {
buzbeec1f45042011-09-21 16:03:19 -07001751 return;
1752 }
buzbee0d966cf2011-09-08 17:34:58 -07001753 oatLockCallTemps(cUnit); // Explicit register usage
1754 int rSuspendCount = r1;
buzbee67bf8852011-08-17 17:51:35 -07001755 ArmLIR* ld;
buzbee0d966cf2011-09-08 17:34:58 -07001756 ld = loadWordDisp(cUnit, rSELF,
1757 art::Thread::SuspendCountOffset().Int32Value(), rSuspendCount);
buzbee67bf8852011-08-17 17:51:35 -07001758 setMemRefType(ld, true /* isLoad */, kMustNotAlias);
buzbee0d966cf2011-09-08 17:34:58 -07001759 loadWordDisp(cUnit, rSELF,
1760 OFFSETOF_MEMBER(Thread, pCheckSuspendFromCode), rLR);
1761 genRegCopy(cUnit, r0, rSELF);
1762 opRegImm(cUnit, kOpCmp, rSuspendCount, 0);
buzbeeb0ebba02011-09-17 10:52:59 -07001763 /*
1764 * FIXME: for efficiency we should use an if-converted suspend
1765 * test here. However, support for IT is a bit weak at the
1766 * moment, and requires knowledge of the exact number of instructions
1767 * to fall in the skip shadow. While the exception mechanism
1768 * remains in flux, use a compare and branch sequence. Once
1769 * things firm up, restore the conditional skip (and perhaps
1770 * fix the utility to handle variable-sized shadows).
1771 */
1772#if 0
buzbee0d966cf2011-09-08 17:34:58 -07001773 genIT(cUnit, kArmCondNe, "");
buzbeeec5adf32011-09-11 15:25:43 -07001774 callUnwindableHelper(cUnit, rLR); // CheckSuspendFromCode(self)
buzbeeb0ebba02011-09-17 10:52:59 -07001775#else
1776 ArmLIR* branch = opCondBranch(cUnit, kArmCondEq);
Ian Rogersff1ed472011-09-20 13:46:24 -07001777 callRuntimeHelper(cUnit, rLR); // CheckSuspendFromCode(self)
buzbeeb0ebba02011-09-17 10:52:59 -07001778 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
1779 target->defMask = ENCODE_ALL;
1780 branch->generic.target = (LIR*)target;
1781#endif
buzbee0d966cf2011-09-08 17:34:58 -07001782 oatFreeCallTemps(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001783}
1784
1785/*
1786 * The following are the first-level codegen routines that analyze the format
1787 * of each bytecode then either dispatch special purpose codegen routines
1788 * or produce corresponding Thumb instructions directly.
1789 */
1790
buzbeeed3e9302011-09-23 17:34:19 -07001791STATIC bool isPowerOfTwo(int x)
buzbee67bf8852011-08-17 17:51:35 -07001792{
1793 return (x & (x - 1)) == 0;
1794}
1795
1796// Returns true if no more than two bits are set in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001797STATIC bool isPopCountLE2(unsigned int x)
buzbee67bf8852011-08-17 17:51:35 -07001798{
1799 x &= x - 1;
1800 return (x & (x - 1)) == 0;
1801}
1802
1803// Returns the index of the lowest set bit in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001804STATIC int lowestSetBit(unsigned int x) {
buzbee67bf8852011-08-17 17:51:35 -07001805 int bit_posn = 0;
1806 while ((x & 0xf) == 0) {
1807 bit_posn += 4;
1808 x >>= 4;
1809 }
1810 while ((x & 1) == 0) {
1811 bit_posn++;
1812 x >>= 1;
1813 }
1814 return bit_posn;
1815}
1816
1817// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
1818// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001819STATIC bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode,
buzbee67bf8852011-08-17 17:51:35 -07001820 RegLocation rlSrc, RegLocation rlDest, int lit)
1821{
1822 if (lit < 2 || !isPowerOfTwo(lit)) {
1823 return false;
1824 }
1825 int k = lowestSetBit(lit);
1826 if (k >= 30) {
1827 // Avoid special cases.
1828 return false;
1829 }
1830 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 ||
1831 dalvikOpcode == OP_DIV_INT_LIT16);
1832 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1833 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1834 if (div) {
1835 int tReg = oatAllocTemp(cUnit);
1836 if (lit == 2) {
1837 // Division by 2 is by far the most common division by constant.
1838 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
1839 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1840 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1841 } else {
1842 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
1843 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
1844 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1845 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1846 }
1847 } else {
1848 int cReg = oatAllocTemp(cUnit);
1849 loadConstant(cUnit, cReg, lit - 1);
1850 int tReg1 = oatAllocTemp(cUnit);
1851 int tReg2 = oatAllocTemp(cUnit);
1852 if (lit == 2) {
1853 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
1854 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1855 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1856 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1857 } else {
1858 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
1859 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
1860 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1861 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1862 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1863 }
1864 }
1865 storeValue(cUnit, rlDest, rlResult);
1866 return true;
1867}
1868
1869// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
1870// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001871STATIC bool handleEasyMultiply(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001872 RegLocation rlSrc, RegLocation rlDest, int lit)
1873{
1874 // Can we simplify this multiplication?
1875 bool powerOfTwo = false;
1876 bool popCountLE2 = false;
1877 bool powerOfTwoMinusOne = false;
1878 if (lit < 2) {
1879 // Avoid special cases.
1880 return false;
1881 } else if (isPowerOfTwo(lit)) {
1882 powerOfTwo = true;
1883 } else if (isPopCountLE2(lit)) {
1884 popCountLE2 = true;
1885 } else if (isPowerOfTwo(lit + 1)) {
1886 powerOfTwoMinusOne = true;
1887 } else {
1888 return false;
1889 }
1890 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1891 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1892 if (powerOfTwo) {
1893 // Shift.
1894 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
1895 lowestSetBit(lit));
1896 } else if (popCountLE2) {
1897 // Shift and add and shift.
1898 int firstBit = lowestSetBit(lit);
1899 int secondBit = lowestSetBit(lit ^ (1 << firstBit));
1900 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
1901 firstBit, secondBit);
1902 } else {
1903 // Reverse subtract: (src << (shift + 1)) - src.
buzbeeed3e9302011-09-23 17:34:19 -07001904 DCHECK(powerOfTwoMinusOne);
buzbee5ade1d22011-09-09 14:44:52 -07001905 // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1)
buzbee67bf8852011-08-17 17:51:35 -07001906 int tReg = oatAllocTemp(cUnit);
1907 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
1908 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
1909 }
1910 storeValue(cUnit, rlDest, rlResult);
1911 return true;
1912}
1913
buzbeeed3e9302011-09-23 17:34:19 -07001914STATIC bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001915 RegLocation rlDest, RegLocation rlSrc,
1916 int lit)
1917{
1918 Opcode dalvikOpcode = mir->dalvikInsn.opcode;
1919 RegLocation rlResult;
1920 OpKind op = (OpKind)0; /* Make gcc happy */
1921 int shiftOp = false;
1922 bool isDiv = false;
1923 int funcOffset;
1924
1925 switch (dalvikOpcode) {
1926 case OP_RSUB_INT_LIT8:
1927 case OP_RSUB_INT: {
1928 int tReg;
1929 //TUNING: add support for use of Arm rsub op
1930 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1931 tReg = oatAllocTemp(cUnit);
1932 loadConstant(cUnit, tReg, lit);
1933 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1934 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1935 tReg, rlSrc.lowReg);
1936 storeValue(cUnit, rlDest, rlResult);
1937 return false;
1938 break;
1939 }
1940
1941 case OP_ADD_INT_LIT8:
1942 case OP_ADD_INT_LIT16:
1943 op = kOpAdd;
1944 break;
1945 case OP_MUL_INT_LIT8:
1946 case OP_MUL_INT_LIT16: {
1947 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
1948 return false;
1949 }
1950 op = kOpMul;
1951 break;
1952 }
1953 case OP_AND_INT_LIT8:
1954 case OP_AND_INT_LIT16:
1955 op = kOpAnd;
1956 break;
1957 case OP_OR_INT_LIT8:
1958 case OP_OR_INT_LIT16:
1959 op = kOpOr;
1960 break;
1961 case OP_XOR_INT_LIT8:
1962 case OP_XOR_INT_LIT16:
1963 op = kOpXor;
1964 break;
1965 case OP_SHL_INT_LIT8:
1966 lit &= 31;
1967 shiftOp = true;
1968 op = kOpLsl;
1969 break;
1970 case OP_SHR_INT_LIT8:
1971 lit &= 31;
1972 shiftOp = true;
1973 op = kOpAsr;
1974 break;
1975 case OP_USHR_INT_LIT8:
1976 lit &= 31;
1977 shiftOp = true;
1978 op = kOpLsr;
1979 break;
1980
1981 case OP_DIV_INT_LIT8:
1982 case OP_DIV_INT_LIT16:
1983 case OP_REM_INT_LIT8:
1984 case OP_REM_INT_LIT16:
1985 if (lit == 0) {
buzbee5ade1d22011-09-09 14:44:52 -07001986 genImmedCheck(cUnit, kArmCondAl, 0, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001987 return false;
1988 }
1989 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
1990 return false;
1991 }
1992 oatFlushAllRegs(cUnit); /* Everything to home location */
1993 loadValueDirectFixed(cUnit, rlSrc, r0);
1994 oatClobber(cUnit, r0);
1995 if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
1996 (dalvikOpcode == OP_DIV_INT_LIT16)) {
1997 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1998 isDiv = true;
1999 } else {
2000 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
2001 isDiv = false;
2002 }
2003 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
2004 loadConstant(cUnit, r1, lit);
Ian Rogersff1ed472011-09-20 13:46:24 -07002005 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07002006 oatClobberCallRegs(cUnit);
2007 if (isDiv)
2008 rlResult = oatGetReturn(cUnit);
2009 else
2010 rlResult = oatGetReturnAlt(cUnit);
2011 storeValue(cUnit, rlDest, rlResult);
2012 return false;
2013 break;
2014 default:
2015 return true;
2016 }
2017 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
2018 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
2019 // Avoid shifts by literal 0 - no support in Thumb. Change to copy
2020 if (shiftOp && (lit == 0)) {
2021 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
2022 } else {
2023 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
2024 }
2025 storeValue(cUnit, rlDest, rlResult);
2026 return false;
2027}
2028
2029/* Architectural-specific debugging helpers go here */
2030void oatArchDump(void)
2031{
2032 /* Print compiled opcode in this VM instance */
2033 int i, start, streak;
2034 char buf[1024];
2035
2036 streak = i = 0;
2037 buf[0] = 0;
2038 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2039 i++;
2040 }
2041 if (i == kNumPackedOpcodes) {
2042 return;
2043 }
2044 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
2045 if (opcodeCoverage[i]) {
2046 streak++;
2047 } else {
2048 if (streak == 1) {
2049 sprintf(buf+strlen(buf), "%x,", start);
2050 } else {
2051 sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
2052 }
2053 streak = 0;
2054 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2055 i++;
2056 }
2057 if (i < kNumPackedOpcodes) {
2058 streak = 1;
2059 start = i;
2060 }
2061 }
2062 }
2063 if (streak) {
2064 if (streak == 1) {
2065 sprintf(buf+strlen(buf), "%x", start);
2066 } else {
2067 sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
2068 }
2069 }
2070 if (strlen(buf)) {
2071 LOG(INFO) << "dalvik.vm.oat.op = " << buf;
2072 }
2073}