blob: c385f358fcb374e0dd589c062dd81704bbee1d50 [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
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080025namespace art {
26
buzbeee3acd072012-02-25 17:03:10 -080027/*
28 * Return most flexible allowed register class based on size.
29 * Bug: 2813841
30 * Must use a core register for data types narrower than word (due
31 * to possible unaligned load/store.
32 */
33STATIC inline RegisterClass oatRegClassBySize(OpSize size)
34{
35 return (size == kUnsignedHalf ||
36 size == kSignedHalf ||
37 size == kUnsignedByte ||
38 size == kSignedByte ) ? kCoreReg : kAnyReg;
39}
40
buzbeece302932011-10-04 14:32:18 -070041STATIC RegLocation getRetLoc(CompilationUnit* cUnit);
buzbee34cd9e52011-09-08 14:31:52 -070042
Elliott Hughes81bc5092011-09-30 17:25:59 -070043void warnIfUnresolved(CompilationUnit* cUnit, int fieldIdx, Field* field) {
44 if (field == NULL) {
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080045 const DexFile::FieldId& field_id = cUnit->dex_file->GetFieldId(fieldIdx);
Elliott Hughes95572412011-12-13 18:14:20 -080046 std::string class_name(cUnit->dex_file->GetFieldDeclaringClassDescriptor(field_id));
47 std::string field_name(cUnit->dex_file->GetFieldName(field_id));
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080048 LOG(INFO) << "Field " << PrettyDescriptor(class_name) << "." << field_name
Elliott Hughes95572412011-12-13 18:14:20 -080049 << " unresolved at compile time";
Elliott Hughes81bc5092011-09-30 17:25:59 -070050 } else {
51 // We also use the slow path for wide volatile fields.
52 }
53}
54
buzbee67bf8852011-08-17 17:51:35 -070055/*
56 * Construct an s4 from two consecutive half-words of switch data.
57 * This needs to check endianness because the DEX optimizer only swaps
58 * half-words in instruction stream.
59 *
60 * "switchData" must be 32-bit aligned.
61 */
62#if __BYTE_ORDER == __LITTLE_ENDIAN
buzbeeed3e9302011-09-23 17:34:19 -070063STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070064 return *(s4*) switchData;
65}
66#else
buzbeeed3e9302011-09-23 17:34:19 -070067STATIC inline s4 s4FromSwitchData(const void* switchData) {
buzbee67bf8852011-08-17 17:51:35 -070068 u2* data = switchData;
69 return data[0] | (((s4) data[1]) << 16);
70}
71#endif
72
73/*
74 * Generate a Thumb2 IT instruction, which can nullify up to
75 * four subsequent instructions based on a condition and its
76 * inverse. The condition applies to the first instruction, which
77 * is executed if the condition is met. The string "guide" consists
78 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
79 * A "T" means the instruction is executed if the condition is
80 * met, and an "E" means the instruction is executed if the condition
81 * is not met.
82 */
buzbeeed3e9302011-09-23 17:34:19 -070083STATIC ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code,
buzbee67bf8852011-08-17 17:51:35 -070084 const char* guide)
85{
86 int mask;
87 int condBit = code & 1;
88 int altBit = condBit ^ 1;
89 int mask3 = 0;
90 int mask2 = 0;
91 int mask1 = 0;
92
93 //Note: case fallthroughs intentional
94 switch(strlen(guide)) {
95 case 3:
96 mask1 = (guide[2] == 'T') ? condBit : altBit;
97 case 2:
98 mask2 = (guide[1] == 'T') ? condBit : altBit;
99 case 1:
100 mask3 = (guide[0] == 'T') ? condBit : altBit;
101 break;
102 case 0:
103 break;
104 default:
105 LOG(FATAL) << "OAT: bad case in genIT";
106 }
107 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
108 (1 << (3 - strlen(guide)));
109 return newLIR2(cUnit, kThumb2It, code, mask);
110}
111
112/*
113 * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik
114 * offset vaddr. This label will be used to fix up the case
115 * branch table during the assembly phase. Be sure to set
116 * all resource flags on this to prevent code motion across
117 * target boundaries. KeyVal is just there for debugging.
118 */
buzbeeed3e9302011-09-23 17:34:19 -0700119STATIC ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
buzbee67bf8852011-08-17 17:51:35 -0700120{
buzbee85d8c1e2012-01-27 15:52:35 -0800121 std::map<unsigned int, LIR*>::iterator it;
122 it = cUnit->boundaryMap.find(vaddr);
123 if (it == cUnit->boundaryMap.end()) {
124 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
buzbee67bf8852011-08-17 17:51:35 -0700125 }
buzbeeba938cb2012-02-03 14:47:55 -0800126 ArmLIR* newLabel = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbee85d8c1e2012-01-27 15:52:35 -0800127 newLabel->generic.dalvikOffset = vaddr;
128 newLabel->opcode = kArmPseudoCaseLabel;
129 newLabel->operands[0] = keyVal;
130 oatInsertLIRAfter(it->second, (LIR*)newLabel);
131 return newLabel;
buzbee67bf8852011-08-17 17:51:35 -0700132}
133
buzbeeed3e9302011-09-23 17:34:19 -0700134STATIC void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700135{
136 const u2* table = tabRec->table;
137 int baseVaddr = tabRec->vaddr;
138 int *targets = (int*)&table[4];
139 int entries = table[1];
140 int lowKey = s4FromSwitchData(&table[2]);
141 for (int i = 0; i < entries; i++) {
142 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
143 i + lowKey);
144 }
145}
146
buzbeeed3e9302011-09-23 17:34:19 -0700147STATIC void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700148{
149 const u2* table = tabRec->table;
150 int baseVaddr = tabRec->vaddr;
151 int entries = table[1];
152 int* keys = (int*)&table[2];
153 int* targets = &keys[entries];
154 for (int i = 0; i < entries; i++) {
155 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
156 keys[i]);
157 }
158}
159
160void oatProcessSwitchTables(CompilationUnit* cUnit)
161{
162 GrowableListIterator iterator;
163 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
164 while (true) {
165 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
166 &iterator);
167 if (tabRec == NULL) break;
168 if (tabRec->table[0] == kPackedSwitchSignature)
169 markPackedCaseLabels(cUnit, tabRec);
170 else if (tabRec->table[0] == kSparseSwitchSignature)
171 markSparseCaseLabels(cUnit, tabRec);
172 else {
173 LOG(FATAL) << "Invalid switch table";
174 }
175 }
176}
177
buzbeeed3e9302011-09-23 17:34:19 -0700178STATIC void dumpSparseSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700179 /*
180 * Sparse switch data format:
181 * ushort ident = 0x0200 magic value
182 * ushort size number of entries in the table; > 0
183 * int keys[size] keys, sorted low-to-high; 32-bit aligned
184 * int targets[size] branch targets, relative to switch opcode
185 *
186 * Total size is (2+size*4) 16-bit code units.
187 */
188{
189 u2 ident = table[0];
190 int entries = table[1];
191 int* keys = (int*)&table[2];
192 int* targets = &keys[entries];
193 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
194 ", entries: " << std::dec << entries;
195 for (int i = 0; i < entries; i++) {
196 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
197 targets[i];
198 }
199}
200
buzbeeed3e9302011-09-23 17:34:19 -0700201STATIC void dumpPackedSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700202 /*
203 * Packed switch data format:
204 * ushort ident = 0x0100 magic value
205 * ushort size number of entries in the table
206 * int first_key first (and lowest) switch case value
207 * int targets[size] branch targets, relative to switch opcode
208 *
209 * Total size is (4+size*2) 16-bit code units.
210 */
211{
212 u2 ident = table[0];
213 int* targets = (int*)&table[4];
214 int entries = table[1];
215 int lowKey = s4FromSwitchData(&table[2]);
216 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
217 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
218 for (int i = 0; i < entries; i++) {
219 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
220 targets[i];
221 }
222}
223
224/*
225 * The sparse table in the literal pool is an array of <key,displacement>
226 * pairs. For each set, we'll load them as a pair using ldmia.
227 * This means that the register number of the temp we use for the key
228 * must be lower than the reg for the displacement.
229 *
230 * The test loop will look something like:
231 *
232 * adr rBase, <table>
233 * ldr rVal, [rSP, vRegOff]
234 * mov rIdx, #tableSize
235 * lp:
236 * ldmia rBase!, {rKey, rDisp}
237 * sub rIdx, #1
238 * cmp rVal, rKey
239 * ifeq
240 * add rPC, rDisp ; This is the branch from which we compute displacement
241 * cbnz rIdx, lp
242 */
buzbeeed3e9302011-09-23 17:34:19 -0700243STATIC void genSparseSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700244 RegLocation rlSrc)
245{
246 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
247 if (cUnit->printMe) {
248 dumpSparseSwitchTable(table);
249 }
250 // Add the table to the list - we'll process it later
buzbeeba938cb2012-02-03 14:47:55 -0800251 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
buzbee5abfa3e2012-01-31 17:01:43 -0800252 true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700253 tabRec->table = table;
254 tabRec->vaddr = mir->offset;
255 int size = table[1];
buzbeeba938cb2012-02-03 14:47:55 -0800256 tabRec->targets = (ArmLIR* *)oatNew(cUnit, size * sizeof(ArmLIR*), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800257 kAllocLIR);
buzbeeba938cb2012-02-03 14:47:55 -0800258 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700259
260 // Get the switch value
261 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
262 int rBase = oatAllocTemp(cUnit);
263 /* Allocate key and disp temps */
264 int rKey = oatAllocTemp(cUnit);
265 int rDisp = oatAllocTemp(cUnit);
266 // Make sure rKey's register number is less than rDisp's number for ldmia
267 if (rKey > rDisp) {
268 int tmp = rDisp;
269 rDisp = rKey;
270 rKey = tmp;
271 }
272 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700273 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700274 // Set up rIdx
275 int rIdx = oatAllocTemp(cUnit);
276 loadConstant(cUnit, rIdx, size);
277 // Establish loop branch target
278 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
279 target->defMask = ENCODE_ALL;
280 // Load next key/disp
281 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
282 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
283 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
284 genIT(cUnit, kArmCondEq, "");
285 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
286 tabRec->bxInst = switchBranch;
287 // Needs to use setflags encoding here
288 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
289 ArmLIR* branch = opCondBranch(cUnit, kArmCondNe);
290 branch->generic.target = (LIR*)target;
291}
292
293
buzbeeed3e9302011-09-23 17:34:19 -0700294STATIC void genPackedSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700295 RegLocation rlSrc)
296{
297 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
298 if (cUnit->printMe) {
299 dumpPackedSwitchTable(table);
300 }
301 // Add the table to the list - we'll process it later
buzbeeba938cb2012-02-03 14:47:55 -0800302 SwitchTable *tabRec = (SwitchTable *)oatNew(cUnit, sizeof(SwitchTable),
buzbee5abfa3e2012-01-31 17:01:43 -0800303 true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700304 tabRec->table = table;
305 tabRec->vaddr = mir->offset;
306 int size = table[1];
buzbeeba938cb2012-02-03 14:47:55 -0800307 tabRec->targets = (ArmLIR* *)oatNew(cUnit, size * sizeof(ArmLIR*), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800308 kAllocLIR);
buzbeeba938cb2012-02-03 14:47:55 -0800309 oatInsertGrowableList(cUnit, &cUnit->switchTables, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700310
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 *)
buzbeeba938cb2012-02-03 14:47:55 -0800359 oatNew(cUnit, sizeof(FillArrayData), true, kAllocData);
buzbee67bf8852011-08-17 17:51:35 -0700360 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
buzbeeba938cb2012-02-03 14:47:55 -0800366 oatInsertGrowableList(cUnit, &cUnit->fillArrayData, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700367
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}
377
buzbeeed3e9302011-09-23 17:34:19 -0700378STATIC void genIGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
Ian Rogers1bddec32012-02-04 12:27:34 -0800379 RegLocation rlDest, RegLocation rlObj,
380 bool isLongOrDouble, bool isObject)
buzbee67bf8852011-08-17 17:51:35 -0700381{
Ian Rogers1bddec32012-02-04 12:27:34 -0800382 int fieldOffset;
383 bool isVolatile;
384 uint32_t fieldIdx = mir->dalvikInsn.vC;
385 bool fastPath =
386 cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, cUnit,
jeffhao8cd6dda2012-02-22 10:15:34 -0800387 fieldOffset, isVolatile, false);
Ian Rogers1bddec32012-02-04 12:27:34 -0800388 if (fastPath && !SLOW_FIELD_PATH) {
389 RegLocation rlResult;
390 RegisterClass regClass = oatRegClassBySize(size);
391 DCHECK_GE(fieldOffset, 0);
buzbee34cd9e52011-09-08 14:31:52 -0700392 rlObj = loadValue(cUnit, rlObj, kCoreReg);
Ian Rogers1bddec32012-02-04 12:27:34 -0800393 if (isLongOrDouble) {
394 DCHECK(rlDest.wide);
395 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
396 int regPtr = oatAllocTemp(cUnit);
397 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
398 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
399 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
400 if (isVolatile) {
401 oatGenMemBarrier(cUnit, kSY);
402 }
403 oatFreeTemp(cUnit, regPtr);
404 storeValueWide(cUnit, rlDest, rlResult);
405 } else {
406 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
407 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
408 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
409 kWord, rlObj.sRegLow);
410 if (isVolatile) {
411 oatGenMemBarrier(cUnit, kSY);
412 }
413 storeValue(cUnit, rlDest, rlResult);
buzbee34cd9e52011-09-08 14:31:52 -0700414 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800415 } else {
416 int getterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pGet64Instance) :
417 (isObject ? OFFSETOF_MEMBER(Thread, pGetObjInstance)
418 : OFFSETOF_MEMBER(Thread, pGet32Instance));
419 loadWordDisp(cUnit, rSELF, getterOffset, rLR);
420 loadValueDirect(cUnit, rlObj, r1);
421 loadConstant(cUnit, r0, fieldIdx);
422 callRuntimeHelper(cUnit, rLR);
423 if (isLongOrDouble) {
424 RegLocation rlResult = oatGetReturnWide(cUnit);
425 storeValueWide(cUnit, rlDest, rlResult);
426 } else {
427 RegLocation rlResult = oatGetReturn(cUnit);
428 storeValue(cUnit, rlDest, rlResult);
429 }
buzbee67bf8852011-08-17 17:51:35 -0700430 }
buzbee67bf8852011-08-17 17:51:35 -0700431}
432
buzbeeed3e9302011-09-23 17:34:19 -0700433STATIC void genIPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
Ian Rogers1bddec32012-02-04 12:27:34 -0800434 RegLocation rlSrc, RegLocation rlObj,
435 bool isLongOrDouble, bool isObject)
buzbee67bf8852011-08-17 17:51:35 -0700436{
Ian Rogers1bddec32012-02-04 12:27:34 -0800437 int fieldOffset;
438 bool isVolatile;
439 uint32_t fieldIdx = mir->dalvikInsn.vC;
440 bool fastPath =
441 cUnit->compiler->ComputeInstanceFieldInfo(fieldIdx, cUnit,
jeffhao8cd6dda2012-02-22 10:15:34 -0800442 fieldOffset, isVolatile, true);
Ian Rogers1bddec32012-02-04 12:27:34 -0800443 if (fastPath && !SLOW_FIELD_PATH) {
444 RegisterClass regClass = oatRegClassBySize(size);
445 DCHECK_GE(fieldOffset, 0);
buzbee34cd9e52011-09-08 14:31:52 -0700446 rlObj = loadValue(cUnit, rlObj, kCoreReg);
Ian Rogers1bddec32012-02-04 12:27:34 -0800447 if (isLongOrDouble) {
448 int regPtr;
449 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
450 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
451 regPtr = oatAllocTemp(cUnit);
452 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
453 if (isVolatile) {
454 oatGenMemBarrier(cUnit, kST);
455 }
456 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
457 if (isVolatile) {
458 oatGenMemBarrier(cUnit, kSY);
459 }
460 oatFreeTemp(cUnit, regPtr);
461 } else {
462 rlSrc = loadValue(cUnit, rlSrc, regClass);
463 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
464 if (isVolatile) {
465 oatGenMemBarrier(cUnit, kST);
466 }
467 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, kWord);
468 if (isVolatile) {
469 oatGenMemBarrier(cUnit, kSY);
470 }
buzbee34cd9e52011-09-08 14:31:52 -0700471 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800472 } else {
473 int setterOffset = isLongOrDouble ? OFFSETOF_MEMBER(Thread, pSet64Instance) :
474 (isObject ? OFFSETOF_MEMBER(Thread, pSetObjInstance)
475 : OFFSETOF_MEMBER(Thread, pSet32Instance));
476 loadWordDisp(cUnit, rSELF, setterOffset, rLR);
477 loadValueDirect(cUnit, rlObj, r1);
478 if (isLongOrDouble) {
479 loadValueDirectWide(cUnit, rlSrc, r2, r3);
480 } else {
481 loadValueDirect(cUnit, rlSrc, r2);
buzbee12246b82011-09-29 14:15:05 -0700482 }
Ian Rogers1bddec32012-02-04 12:27:34 -0800483 loadConstant(cUnit, r0, fieldIdx);
484 callRuntimeHelper(cUnit, rLR);
buzbee34cd9e52011-09-08 14:31:52 -0700485 }
buzbee67bf8852011-08-17 17:51:35 -0700486}
487
buzbeeed3e9302011-09-23 17:34:19 -0700488STATIC void genConstClass(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700489 RegLocation rlDest, RegLocation rlSrc)
490{
Ian Rogers28ad40d2011-10-27 15:19:26 -0700491 uint32_t type_idx = mir->dalvikInsn.vB;
buzbee1b4c8592011-08-31 10:43:51 -0700492 int mReg = loadCurrMethod(cUnit);
493 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700494 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800495 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
496 cUnit->dex_cache,
497 *cUnit->dex_file,
498 type_idx)) {
499 // Call out to helper which resolves type and verifies access.
500 // Resolved type returned in r0.
501 loadWordDisp(cUnit, rSELF,
502 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
503 rLR);
504 genRegCopy(cUnit, r1, mReg);
505 loadConstant(cUnit, r0, type_idx);
506 callRuntimeHelper(cUnit, rLR);
507 RegLocation rlResult = oatGetReturn(cUnit);
508 storeValue(cUnit, rlDest, rlResult);
buzbee1b4c8592011-08-31 10:43:51 -0700509 } else {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700510 // We're don't need access checks, load type from dex cache
511 int32_t dex_cache_offset = Method::DexCacheResolvedTypesOffset().Int32Value();
512 loadWordDisp(cUnit, mReg, dex_cache_offset, resReg);
513 int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
514 loadWordDisp(cUnit, resReg, offset_of_type, rlResult.lowReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800515 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache,
516 type_idx) ||
Ian Rogers28ad40d2011-10-27 15:19:26 -0700517 SLOW_TYPE_PATH) {
518 // Slow path, at runtime test if the type is null and if so initialize
519 oatFlushAllRegs(cUnit);
520 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg, 0);
521 // Resolved, store and hop over following code
522 storeValue(cUnit, rlDest, rlResult);
523 ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
524 // TUNING: move slow path to end & remove unconditional branch
525 ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
526 target1->defMask = ENCODE_ALL;
527 // Call out to helper, which will return resolved type in r0
528 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
529 genRegCopy(cUnit, r1, mReg);
530 loadConstant(cUnit, r0, type_idx);
531 callRuntimeHelper(cUnit, rLR);
532 RegLocation rlResult = oatGetReturn(cUnit);
533 storeValue(cUnit, rlDest, rlResult);
534 // Rejoin code paths
535 ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
536 target2->defMask = ENCODE_ALL;
537 branch1->generic.target = (LIR*)target1;
538 branch2->generic.target = (LIR*)target2;
539 } else {
540 // Fast path, we're done - just store result
541 storeValue(cUnit, rlDest, rlResult);
542 }
buzbee1b4c8592011-08-31 10:43:51 -0700543 }
buzbee67bf8852011-08-17 17:51:35 -0700544}
545
buzbee44b412b2012-02-04 08:50:53 -0800546/*
547 * Generate callout to updateDebugger. Note: genIT will automatically
548 * create a scheduling barrier, which we need to prevent code motion that
549 * might confuse the debugger. Note: Return registers r0/r1 are
550 * handled specially during code generation following function calls.
551 * Typically, temp registers are not live between opcodes, but we keep
552 * r0/r1 live following invokes, where they are consumed by the immediately
553 * following op_move_result_xxx. Thus, we must preserve and restore r0/r1
554 * when making a call to update the debugger. This is handled by the stub.
555 */
556STATIC void genDebuggerUpdate(CompilationUnit* cUnit, int32_t offset)
557{
558 // Following DCHECK verifies that dPC is in range of single load immediate
559 DCHECK((offset == DEBUGGER_METHOD_ENTRY) ||
560 (offset == DEBUGGER_METHOD_EXIT) || ((offset & 0xffff) == offset));
561 oatClobberCalleeSave(cUnit);
562 opRegImm(cUnit, kOpCmp, rSUSPEND, 0);
563 genIT(cUnit, kArmCondNe, "T");
564 loadConstant(cUnit, r2, offset); // arg2 <- Entry code
565 opReg(cUnit, kOpBlx, rSUSPEND);
566 oatFreeTemp(cUnit, r2);
567}
568
buzbeeed3e9302011-09-23 17:34:19 -0700569STATIC void genConstString(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700570 RegLocation rlDest, RegLocation rlSrc)
571{
buzbeece302932011-10-04 14:32:18 -0700572 /* NOTE: Most strings should be available at compile time */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700573 uint32_t string_idx = mir->dalvikInsn.vB;
574 int32_t offset_of_string = Array::DataOffset().Int32Value() + (sizeof(String*) * string_idx);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800575 if (!cUnit->compiler->CanAssumeStringIsPresentInDexCache(cUnit->dex_cache, string_idx) ||
Ian Rogers28ad40d2011-10-27 15:19:26 -0700576 SLOW_STRING_PATH) {
577 // slow path, resolve string if not in dex cache
buzbeece302932011-10-04 14:32:18 -0700578 oatFlushAllRegs(cUnit);
579 oatLockCallTemps(cUnit); // Using explicit registers
580 loadCurrMethodDirect(cUnit, r2);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700581 loadWordDisp(cUnit, r2, Method::DexCacheStringsOffset().Int32Value(), r0);
buzbeece302932011-10-04 14:32:18 -0700582 // Might call out to helper, which will return resolved string in r0
Ian Rogers28ad40d2011-10-27 15:19:26 -0700583 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pResolveStringFromCode), rLR);
584 loadWordDisp(cUnit, r0, offset_of_string, r0);
585 loadConstant(cUnit, r1, string_idx);
buzbeece302932011-10-04 14:32:18 -0700586 opRegImm(cUnit, kOpCmp, r0, 0); // Is resolved?
587 genBarrier(cUnit);
588 // For testing, always force through helper
589 if (!EXERCISE_SLOWEST_STRING_PATH) {
590 genIT(cUnit, kArmCondEq, "T");
591 }
592 genRegCopy(cUnit, r0, r2); // .eq
593 opReg(cUnit, kOpBlx, rLR); // .eq, helper(Method*, string_idx)
594 genBarrier(cUnit);
595 storeValue(cUnit, rlDest, getRetLoc(cUnit));
596 } else {
597 int mReg = loadCurrMethod(cUnit);
598 int resReg = oatAllocTemp(cUnit);
599 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700600 loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(), resReg);
601 loadWordDisp(cUnit, resReg, offset_of_string, rlResult.lowReg);
buzbeece302932011-10-04 14:32:18 -0700602 storeValue(cUnit, rlDest, rlResult);
603 }
buzbee67bf8852011-08-17 17:51:35 -0700604}
605
buzbeedfd3d702011-08-28 12:56:51 -0700606/*
607 * Let helper function take care of everything. Will
608 * call Class::NewInstanceFromCode(type_idx, method);
609 */
buzbeeed3e9302011-09-23 17:34:19 -0700610STATIC void genNewInstance(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700611 RegLocation rlDest)
612{
buzbeedfd3d702011-08-28 12:56:51 -0700613 oatFlushAllRegs(cUnit); /* Everything to home location */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700614 uint32_t type_idx = mir->dalvikInsn.vB;
615 // alloc will always check for resolution, do we also need to verify access because the
616 // verifier was unable to?
Ian Rogersd4135902012-02-03 18:05:08 -0800617 if (cUnit->compiler->CanAccessInstantiableTypeWithoutChecks(cUnit->method_idx,
618 cUnit->dex_cache,
619 *cUnit->dex_file,
620 type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700621 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
622 } else {
623 loadWordDisp(cUnit, rSELF,
624 OFFSETOF_MEMBER(Thread, pAllocObjectFromCodeWithAccessCheck), rLR);
625 }
626 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
627 loadConstant(cUnit, r0, type_idx); // arg0 <- type_idx
Ian Rogersff1ed472011-09-20 13:46:24 -0700628 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700629 RegLocation rlResult = oatGetReturn(cUnit);
630 storeValue(cUnit, rlDest, rlResult);
631}
632
633void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
634{
buzbee6181f792011-09-29 11:14:04 -0700635 oatFlushAllRegs(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700636 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
Ian Rogersbdb03912011-09-14 00:55:44 -0700637 loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
Ian Rogersff1ed472011-09-20 13:46:24 -0700638 callRuntimeHelper(cUnit, rLR); // art_deliver_exception(exception);
buzbee67bf8852011-08-17 17:51:35 -0700639}
640
buzbeeed3e9302011-09-23 17:34:19 -0700641STATIC void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700642 RegLocation rlSrc)
643{
buzbee6181f792011-09-29 11:14:04 -0700644 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700645 // May generate a call - use explicit registers
646 oatLockCallTemps(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700647 uint32_t type_idx = mir->dalvikInsn.vC;
buzbee2a475e72011-09-07 17:19:17 -0700648 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
Ian Rogers28ad40d2011-10-27 15:19:26 -0700649 int classReg = r2; // r2 will hold the Class*
Ian Rogersa3760aa2011-11-14 14:32:37 -0800650 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
651 cUnit->dex_cache,
652 *cUnit->dex_file,
653 type_idx)) {
Ian Rogersb093c6b2011-10-31 16:19:55 -0700654 // Check we have access to type_idx and if not throw IllegalAccessError,
655 // returns Class* in r0
656 loadWordDisp(cUnit, rSELF,
657 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
658 rLR);
659 loadConstant(cUnit, r0, type_idx);
660 callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
661 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers6a996782011-10-31 17:06:39 -0700662 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
Ian Rogers28ad40d2011-10-27 15:19:26 -0700663 } else {
664 // Load dex cache entry into classReg (r2)
Ian Rogers6a996782011-10-31 17:06:39 -0700665 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
Ian Rogers28ad40d2011-10-27 15:19:26 -0700666 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
667 int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
668 loadWordDisp(cUnit, classReg, offset_of_type, classReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800669 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache, type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700670 // Need to test presence of type in dex cache at runtime
671 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
672 // Not resolved
673 // Call out to helper, which will return resolved type in r0
674 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
675 loadConstant(cUnit, r0, type_idx);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700676 callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
Ian Rogers28ad40d2011-10-27 15:19:26 -0700677 genRegCopy(cUnit, r2, r0); // Align usage with fast path
678 loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */
679 // Rejoin code paths
680 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
681 hopTarget->defMask = ENCODE_ALL;
682 hopBranch->generic.target = (LIR*)hopTarget;
683 }
buzbee67bf8852011-08-17 17:51:35 -0700684 }
buzbee991e3ac2011-09-29 15:44:22 -0700685 /* r0 is ref, r2 is class. If ref==null, use directly as bool result */
686 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
buzbee2a475e72011-09-07 17:19:17 -0700687 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700688 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee991e3ac2011-09-29 15:44:22 -0700689 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
690 /* r0 is ref, r1 is ref->clazz, r2 is class */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700691 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
buzbee991e3ac2011-09-29 15:44:22 -0700692 opRegReg(cUnit, kOpCmp, r1, r2); // Same?
693 genBarrier(cUnit);
694 genIT(cUnit, kArmCondEq, "EE"); // if-convert the test
695 loadConstant(cUnit, r0, 1); // .eq case - load true
696 genRegCopy(cUnit, r0, r2); // .ne case - arg0 <= class
697 opReg(cUnit, kOpBlx, rLR); // .ne case: helper(class, ref->class)
698 genBarrier(cUnit);
699 oatClobberCalleeSave(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700700 /* branch target here */
701 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
702 target->defMask = ENCODE_ALL;
buzbee2a475e72011-09-07 17:19:17 -0700703 RegLocation rlResult = oatGetReturn(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700704 storeValue(cUnit, rlDest, rlResult);
705 branch1->generic.target = (LIR*)target;
buzbee67bf8852011-08-17 17:51:35 -0700706}
707
buzbeeed3e9302011-09-23 17:34:19 -0700708STATIC void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbee67bf8852011-08-17 17:51:35 -0700709{
buzbee6181f792011-09-29 11:14:04 -0700710 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700711 // May generate a call - use explicit registers
712 oatLockCallTemps(cUnit);
Ian Rogers28ad40d2011-10-27 15:19:26 -0700713 uint32_t type_idx = mir->dalvikInsn.vB;
buzbee2a475e72011-09-07 17:19:17 -0700714 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
Ian Rogers28ad40d2011-10-27 15:19:26 -0700715 int classReg = r2; // r2 will hold the Class*
Ian Rogersa3760aa2011-11-14 14:32:37 -0800716 if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method_idx,
717 cUnit->dex_cache,
718 *cUnit->dex_file,
719 type_idx)) {
Ian Rogersb093c6b2011-10-31 16:19:55 -0700720 // Check we have access to type_idx and if not throw IllegalAccessError,
721 // returns Class* in r0
722 loadWordDisp(cUnit, rSELF,
723 OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
724 rLR);
725 loadConstant(cUnit, r0, type_idx);
726 callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
727 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers28ad40d2011-10-27 15:19:26 -0700728 } else {
729 // Load dex cache entry into classReg (r2)
730 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
731 int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
732 loadWordDisp(cUnit, classReg, offset_of_type, classReg);
Ian Rogersa3760aa2011-11-14 14:32:37 -0800733 if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->dex_cache, type_idx)) {
Ian Rogers28ad40d2011-10-27 15:19:26 -0700734 // Need to test presence of type in dex cache at runtime
735 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
736 // Not resolved
737 // Call out to helper, which will return resolved type in r0
738 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
739 loadConstant(cUnit, r0, type_idx);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700740 callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
741 genRegCopy(cUnit, classReg, r0); // Align usage with fast path
Ian Rogers28ad40d2011-10-27 15:19:26 -0700742 // Rejoin code paths
743 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
744 hopTarget->defMask = ENCODE_ALL;
745 hopBranch->generic.target = (LIR*)hopTarget;
746 }
buzbee67bf8852011-08-17 17:51:35 -0700747 }
Ian Rogers28ad40d2011-10-27 15:19:26 -0700748 // At this point, classReg (r2) has class
749 loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
buzbee2a475e72011-09-07 17:19:17 -0700750 /* Null is OK - continue */
751 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
752 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700753 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee2a475e72011-09-07 17:19:17 -0700754 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
755 /* r1 now contains object->clazz */
Ian Rogers28ad40d2011-10-27 15:19:26 -0700756 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
Ian Rogersb093c6b2011-10-31 16:19:55 -0700757 opRegReg(cUnit, kOpCmp, r1, classReg);
buzbee2a475e72011-09-07 17:19:17 -0700758 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
759 genRegCopy(cUnit, r0, r1);
760 genRegCopy(cUnit, r1, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -0700761 callRuntimeHelper(cUnit, rLR);
buzbee2a475e72011-09-07 17:19:17 -0700762 /* branch target here */
buzbee67bf8852011-08-17 17:51:35 -0700763 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
764 target->defMask = ENCODE_ALL;
765 branch1->generic.target = (LIR*)target;
766 branch2->generic.target = (LIR*)target;
767}
768
buzbeeed3e9302011-09-23 17:34:19 -0700769STATIC void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700770 RegLocation rlSrc)
771{
772 RegLocation rlResult;
773 rlSrc = loadValue(cUnit, rlSrc, kFPReg);
774 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
775 newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
776 storeValue(cUnit, rlDest, rlResult);
777}
778
buzbeeed3e9302011-09-23 17:34:19 -0700779STATIC void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700780 RegLocation rlSrc)
781{
782 RegLocation rlResult;
783 rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
784 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
785 newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
786 S2D(rlSrc.lowReg, rlSrc.highReg));
787 storeValueWide(cUnit, rlDest, rlResult);
788}
789
buzbeeed3e9302011-09-23 17:34:19 -0700790STATIC void freeRegLocTemps(CompilationUnit* cUnit, RegLocation rlKeep,
buzbee439c4fa2011-08-27 15:59:07 -0700791 RegLocation rlFree)
buzbee67bf8852011-08-17 17:51:35 -0700792{
buzbee6181f792011-09-29 11:14:04 -0700793 if ((rlFree.lowReg != rlKeep.lowReg) && (rlFree.lowReg != rlKeep.highReg) &&
794 (rlFree.highReg != rlKeep.lowReg) && (rlFree.highReg != rlKeep.highReg)) {
795 // No overlap, free both
buzbee439c4fa2011-08-27 15:59:07 -0700796 oatFreeTemp(cUnit, rlFree.lowReg);
buzbee6181f792011-09-29 11:14:04 -0700797 oatFreeTemp(cUnit, rlFree.highReg);
798 }
buzbee67bf8852011-08-17 17:51:35 -0700799}
800
buzbeeed3e9302011-09-23 17:34:19 -0700801STATIC void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp,
buzbee67bf8852011-08-17 17:51:35 -0700802 OpKind secondOp, RegLocation rlDest,
803 RegLocation rlSrc1, RegLocation rlSrc2)
804{
buzbee9e0f9b02011-08-24 15:32:46 -0700805 /*
806 * NOTE: This is the one place in the code in which we might have
807 * as many as six live temporary registers. There are 5 in the normal
808 * set for Arm. Until we have spill capabilities, temporarily add
809 * lr to the temp set. It is safe to do this locally, but note that
810 * lr is used explicitly elsewhere in the code generator and cannot
811 * normally be used as a general temp register.
812 */
buzbee67bf8852011-08-17 17:51:35 -0700813 RegLocation rlResult;
buzbee9e0f9b02011-08-24 15:32:46 -0700814 oatMarkTemp(cUnit, rLR); // Add lr to the temp pool
815 oatFreeTemp(cUnit, rLR); // and make it available
buzbee67bf8852011-08-17 17:51:35 -0700816 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
817 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
818 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeec0ecd652011-09-25 18:11:54 -0700819 // The longs may overlap - use intermediate temp if so
820 if (rlResult.lowReg == rlSrc1.highReg) {
buzbeec0ecd652011-09-25 18:11:54 -0700821 int tReg = oatAllocTemp(cUnit);
822 genRegCopy(cUnit, tReg, rlSrc1.highReg);
823 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
824 rlSrc2.lowReg);
825 opRegRegReg(cUnit, secondOp, rlResult.highReg, tReg,
826 rlSrc2.highReg);
827 oatFreeTemp(cUnit, tReg);
828 } else {
829 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
830 rlSrc2.lowReg);
831 opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
832 rlSrc2.highReg);
833 }
buzbee439c4fa2011-08-27 15:59:07 -0700834 /*
835 * NOTE: If rlDest refers to a frame variable in a large frame, the
836 * following storeValueWide might need to allocate a temp register.
837 * To further work around the lack of a spill capability, explicitly
838 * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult.
839 * Remove when spill is functional.
840 */
841 freeRegLocTemps(cUnit, rlResult, rlSrc1);
842 freeRegLocTemps(cUnit, rlResult, rlSrc2);
buzbee67bf8852011-08-17 17:51:35 -0700843 storeValueWide(cUnit, rlDest, rlResult);
buzbee9e0f9b02011-08-24 15:32:46 -0700844 oatClobber(cUnit, rLR);
845 oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool
buzbee67bf8852011-08-17 17:51:35 -0700846}
847
848void oatInitializeRegAlloc(CompilationUnit* cUnit)
849{
850 int numRegs = sizeof(coreRegs)/sizeof(*coreRegs);
851 int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs);
852 int numTemps = sizeof(coreTemps)/sizeof(*coreTemps);
853 int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs);
854 int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps);
buzbeeba938cb2012-02-03 14:47:55 -0800855 RegisterPool *pool = (RegisterPool *)oatNew(cUnit, sizeof(*pool), true,
buzbee5abfa3e2012-01-31 17:01:43 -0800856 kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700857 cUnit->regPool = pool;
858 pool->numCoreRegs = numRegs;
859 pool->coreRegs = (RegisterInfo *)
buzbeeba938cb2012-02-03 14:47:55 -0800860 oatNew(cUnit, numRegs * sizeof(*cUnit->regPool->coreRegs),
861 true, kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700862 pool->numFPRegs = numFPRegs;
863 pool->FPRegs = (RegisterInfo *)
buzbeeba938cb2012-02-03 14:47:55 -0800864 oatNew(cUnit, numFPRegs * sizeof(*cUnit->regPool->FPRegs), true,
865 kAllocRegAlloc);
buzbee67bf8852011-08-17 17:51:35 -0700866 oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs);
867 oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs);
868 // Keep special registers from being allocated
869 for (int i = 0; i < numReserved; i++) {
buzbee44b412b2012-02-04 08:50:53 -0800870 if (NO_SUSPEND && !cUnit->genDebugger &&
871 (reservedRegs[i] == rSUSPEND)) {
buzbeec0ecd652011-09-25 18:11:54 -0700872 //To measure cost of suspend check
873 continue;
874 }
buzbee67bf8852011-08-17 17:51:35 -0700875 oatMarkInUse(cUnit, reservedRegs[i]);
876 }
877 // Mark temp regs - all others not in use can be used for promotion
878 for (int i = 0; i < numTemps; i++) {
879 oatMarkTemp(cUnit, coreTemps[i]);
880 }
881 for (int i = 0; i < numFPTemps; i++) {
882 oatMarkTemp(cUnit, fpTemps[i]);
883 }
buzbeec0ecd652011-09-25 18:11:54 -0700884 // Construct the alias map.
buzbeeba938cb2012-02-03 14:47:55 -0800885 cUnit->phiAliasMap = (int*)oatNew(cUnit, cUnit->numSSARegs *
buzbee5abfa3e2012-01-31 17:01:43 -0800886 sizeof(cUnit->phiAliasMap[0]), false,
887 kAllocDFInfo);
buzbeec0ecd652011-09-25 18:11:54 -0700888 for (int i = 0; i < cUnit->numSSARegs; i++) {
889 cUnit->phiAliasMap[i] = i;
890 }
891 for (MIR* phi = cUnit->phiList; phi; phi = phi->meta.phiNext) {
892 int defReg = phi->ssaRep->defs[0];
893 for (int i = 0; i < phi->ssaRep->numUses; i++) {
894 for (int j = 0; j < cUnit->numSSARegs; j++) {
895 if (cUnit->phiAliasMap[j] == phi->ssaRep->uses[i]) {
896 cUnit->phiAliasMap[j] = defReg;
897 }
898 }
899 }
900 }
buzbee67bf8852011-08-17 17:51:35 -0700901}
902
903/*
904 * Handle simple case (thin lock) inline. If it's complicated, bail
905 * out to the heavyweight lock/unlock routines. We'll use dedicated
906 * registers here in order to be in the right position in case we
907 * to bail to dvm[Lock/Unlock]Object(self, object)
908 *
909 * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
910 * r1 -> object [arg1 for dvm[Lock/Unlock]Object
911 * r2 -> intial contents of object->lock, later result of strex
912 * r3 -> self->threadId
913 * r12 -> allow to be used by utilities as general temp
914 *
915 * The result of the strex is 0 if we acquire the lock.
916 *
917 * See comments in Sync.c for the layout of the lock word.
918 * Of particular interest to this code is the test for the
919 * simple case - which we handle inline. For monitor enter, the
920 * simple case is thin lock, held by no-one. For monitor exit,
921 * the simple case is thin lock, held by the unlocking thread with
922 * a recurse count of 0.
923 *
924 * A minor complication is that there is a field in the lock word
925 * unrelated to locking: the hash state. This field must be ignored, but
926 * preserved.
927 *
928 */
buzbeeed3e9302011-09-23 17:34:19 -0700929STATIC void genMonitorEnter(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700930 RegLocation rlSrc)
931{
932 ArmLIR* target;
933 ArmLIR* hopTarget;
934 ArmLIR* branch;
935 ArmLIR* hopBranch;
936
937 oatFlushAllRegs(cUnit);
Elliott Hughes5f791332011-09-15 17:45:30 -0700938 DCHECK_EQ(LW_SHAPE_THIN, 0);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700939 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700940 oatLockCallTemps(cUnit); // Prepare for explicit register usage
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700941 genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
942 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
943 newLIR3(cUnit, kThumb2Ldrex, r1, r0,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700944 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
buzbeec143c552011-08-20 17:38:58 -0700945 // Align owner
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700946 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
buzbee67bf8852011-08-17 17:51:35 -0700947 // Is lock unheld on lock or held by us (==threadId) on unlock?
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700948 newLIR4(cUnit, kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
949 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
950 hopBranch = newLIR2(cUnit, kThumb2Cbnz, r1, 0);
951 newLIR4(cUnit, kThumb2Strex, r1, r2, r0,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700952 Object::MonitorOffset().Int32Value() >> 2);
buzbee67bf8852011-08-17 17:51:35 -0700953 oatGenMemBarrier(cUnit, kSY);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700954 branch = newLIR2(cUnit, kThumb2Cbz, r1, 0);
buzbee67bf8852011-08-17 17:51:35 -0700955
956 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
957 hopTarget->defMask = ENCODE_ALL;
958 hopBranch->generic.target = (LIR*)hopTarget;
959
buzbee1b4c8592011-08-31 10:43:51 -0700960 // Go expensive route - artLockObjectFromCode(self, obj);
961 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700962 rLR);
Ian Rogersff1ed472011-09-20 13:46:24 -0700963 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700964
965 // Resume here
966 target = newLIR0(cUnit, kArmPseudoTargetLabel);
967 target->defMask = ENCODE_ALL;
968 branch->generic.target = (LIR*)target;
969}
970
971/*
972 * For monitor unlock, we don't have to use ldrex/strex. Once
973 * we've determined that the lock is thin and that we own it with
974 * a zero recursion count, it's safe to punch it back to the
975 * initial, unlock thin state with a store word.
976 */
buzbeeed3e9302011-09-23 17:34:19 -0700977STATIC void genMonitorExit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700978 RegLocation rlSrc)
979{
980 ArmLIR* target;
981 ArmLIR* branch;
982 ArmLIR* hopTarget;
983 ArmLIR* hopBranch;
984
Elliott Hughes5f791332011-09-15 17:45:30 -0700985 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -0700986 oatFlushAllRegs(cUnit);
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700987 loadValueDirectFixed(cUnit, rlSrc, r0); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700988 oatLockCallTemps(cUnit); // Prepare for explicit register usage
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700989 genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
990 loadWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r1); // Get lock
991 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
buzbee67bf8852011-08-17 17:51:35 -0700992 // Is lock unheld on lock or held by us (==threadId) on unlock?
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700993 opRegRegImm(cUnit, kOpAnd, r3, r1, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
buzbeec143c552011-08-20 17:38:58 -0700994 // Align owner
Ian Rogers4f0d07c2011-10-06 23:38:47 -0700995 opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
996 newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
997 opRegReg(cUnit, kOpSub, r1, r2);
buzbee67bf8852011-08-17 17:51:35 -0700998 hopBranch = opCondBranch(cUnit, kArmCondNe);
999 oatGenMemBarrier(cUnit, kSY);
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001000 storeWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -07001001 branch = opNone(cUnit, kOpUncondBr);
1002
1003 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
1004 hopTarget->defMask = ENCODE_ALL;
1005 hopBranch->generic.target = (LIR*)hopTarget;
1006
Ian Rogers4f0d07c2011-10-06 23:38:47 -07001007 // Go expensive route - UnlockObjectFromCode(obj);
buzbee1b4c8592011-08-31 10:43:51 -07001008 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -07001009 rLR);
Ian Rogersff1ed472011-09-20 13:46:24 -07001010 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001011
1012 // Resume here
1013 target = newLIR0(cUnit, kArmPseudoTargetLabel);
1014 target->defMask = ENCODE_ALL;
1015 branch->generic.target = (LIR*)target;
1016}
1017
1018/*
1019 * 64-bit 3way compare function.
1020 * mov rX, #-1
1021 * cmp op1hi, op2hi
1022 * blt done
1023 * bgt flip
1024 * sub rX, op1lo, op2lo (treat as unsigned)
1025 * beq done
1026 * ite hi
1027 * mov(hi) rX, #-1
1028 * mov(!hi) rX, #1
1029 * flip:
1030 * neg rX
1031 * done:
1032 */
buzbeeed3e9302011-09-23 17:34:19 -07001033STATIC void genCmpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001034 RegLocation rlDest, RegLocation rlSrc1,
1035 RegLocation rlSrc2)
1036{
buzbee67bf8852011-08-17 17:51:35 -07001037 ArmLIR* target1;
1038 ArmLIR* target2;
1039 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
1040 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
buzbeeb29e4d12011-09-26 15:05:48 -07001041 int tReg = oatAllocTemp(cUnit);
1042 loadConstant(cUnit, tReg, -1);
buzbee67bf8852011-08-17 17:51:35 -07001043 opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
1044 ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt);
1045 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt);
buzbeeb29e4d12011-09-26 15:05:48 -07001046 opRegRegReg(cUnit, kOpSub, tReg, rlSrc1.lowReg, rlSrc2.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001047 ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq);
1048
1049 genIT(cUnit, kArmCondHi, "E");
buzbeeb29e4d12011-09-26 15:05:48 -07001050 newLIR2(cUnit, kThumb2MovImmShift, tReg, modifiedImmediate(-1));
1051 loadConstant(cUnit, tReg, 1);
buzbee67bf8852011-08-17 17:51:35 -07001052 genBarrier(cUnit);
1053
1054 target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
1055 target2->defMask = -1;
buzbeeb29e4d12011-09-26 15:05:48 -07001056 opRegReg(cUnit, kOpNeg, tReg, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001057
1058 target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
1059 target1->defMask = -1;
1060
buzbeeb29e4d12011-09-26 15:05:48 -07001061 RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
1062 rlTemp.lowReg = tReg;
buzbee67bf8852011-08-17 17:51:35 -07001063 storeValue(cUnit, rlDest, rlTemp);
buzbeeb29e4d12011-09-26 15:05:48 -07001064 oatFreeTemp(cUnit, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001065
1066 branch1->generic.target = (LIR*)target1;
1067 branch2->generic.target = (LIR*)target2;
1068 branch3->generic.target = branch1->generic.target;
1069}
1070
buzbeeed3e9302011-09-23 17:34:19 -07001071STATIC void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001072 RegLocation rlSrc, RegLocation rlResult, int lit,
1073 int firstBit, int secondBit)
1074{
1075 opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
1076 encodeShift(kArmLsl, secondBit - firstBit));
1077 if (firstBit != 0) {
1078 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
1079 }
1080}
1081
buzbeeed3e9302011-09-23 17:34:19 -07001082STATIC bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset,
buzbee67bf8852011-08-17 17:51:35 -07001083 int srcSize, int tgtSize)
1084{
1085 /*
1086 * Don't optimize the register usage since it calls out to support
1087 * functions
1088 */
1089 RegLocation rlSrc;
1090 RegLocation rlDest;
1091 oatFlushAllRegs(cUnit); /* Send everything to home location */
1092 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1093 if (srcSize == 1) {
1094 rlSrc = oatGetSrc(cUnit, mir, 0);
1095 loadValueDirectFixed(cUnit, rlSrc, r0);
1096 } else {
1097 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
1098 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
1099 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001100 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001101 if (tgtSize == 1) {
1102 RegLocation rlResult;
1103 rlDest = oatGetDest(cUnit, mir, 0);
1104 rlResult = oatGetReturn(cUnit);
1105 storeValue(cUnit, rlDest, rlResult);
1106 } else {
1107 RegLocation rlResult;
1108 rlDest = oatGetDestWide(cUnit, mir, 0, 1);
1109 rlResult = oatGetReturnWide(cUnit);
1110 storeValueWide(cUnit, rlDest, rlResult);
1111 }
1112 return false;
1113}
1114
buzbeeed3e9302011-09-23 17:34:19 -07001115STATIC bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001116 RegLocation rlDest, RegLocation rlSrc1,
1117 RegLocation rlSrc2)
1118{
1119 RegLocation rlResult;
1120 int funcOffset;
1121
1122 switch (mir->dalvikInsn.opcode) {
1123 case OP_ADD_FLOAT_2ADDR:
1124 case OP_ADD_FLOAT:
1125 funcOffset = OFFSETOF_MEMBER(Thread, pFadd);
1126 break;
1127 case OP_SUB_FLOAT_2ADDR:
1128 case OP_SUB_FLOAT:
1129 funcOffset = OFFSETOF_MEMBER(Thread, pFsub);
1130 break;
1131 case OP_DIV_FLOAT_2ADDR:
1132 case OP_DIV_FLOAT:
1133 funcOffset = OFFSETOF_MEMBER(Thread, pFdiv);
1134 break;
1135 case OP_MUL_FLOAT_2ADDR:
1136 case OP_MUL_FLOAT:
1137 funcOffset = OFFSETOF_MEMBER(Thread, pFmul);
1138 break;
1139 case OP_REM_FLOAT_2ADDR:
1140 case OP_REM_FLOAT:
1141 funcOffset = OFFSETOF_MEMBER(Thread, pFmodf);
1142 break;
1143 case OP_NEG_FLOAT: {
1144 genNegFloat(cUnit, rlDest, rlSrc1);
1145 return false;
1146 }
1147 default:
1148 return true;
1149 }
1150 oatFlushAllRegs(cUnit); /* Send everything to home location */
1151 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1152 loadValueDirectFixed(cUnit, rlSrc1, r0);
1153 loadValueDirectFixed(cUnit, rlSrc2, r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001154 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001155 rlResult = oatGetReturn(cUnit);
1156 storeValue(cUnit, rlDest, rlResult);
1157 return false;
1158}
1159
buzbeeed3e9302011-09-23 17:34:19 -07001160STATIC bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001161 RegLocation rlDest, RegLocation rlSrc1,
1162 RegLocation rlSrc2)
1163{
1164 RegLocation rlResult;
1165 int funcOffset;
1166
1167 switch (mir->dalvikInsn.opcode) {
1168 case OP_ADD_DOUBLE_2ADDR:
1169 case OP_ADD_DOUBLE:
1170 funcOffset = OFFSETOF_MEMBER(Thread, pDadd);
1171 break;
1172 case OP_SUB_DOUBLE_2ADDR:
1173 case OP_SUB_DOUBLE:
1174 funcOffset = OFFSETOF_MEMBER(Thread, pDsub);
1175 break;
1176 case OP_DIV_DOUBLE_2ADDR:
1177 case OP_DIV_DOUBLE:
1178 funcOffset = OFFSETOF_MEMBER(Thread, pDdiv);
1179 break;
1180 case OP_MUL_DOUBLE_2ADDR:
1181 case OP_MUL_DOUBLE:
1182 funcOffset = OFFSETOF_MEMBER(Thread, pDmul);
1183 break;
1184 case OP_REM_DOUBLE_2ADDR:
1185 case OP_REM_DOUBLE:
1186 funcOffset = OFFSETOF_MEMBER(Thread, pFmod);
1187 break;
1188 case OP_NEG_DOUBLE: {
1189 genNegDouble(cUnit, rlDest, rlSrc1);
1190 return false;
1191 }
1192 default:
1193 return true;
1194 }
1195 oatFlushAllRegs(cUnit); /* Send everything to home location */
1196 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1197 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1198 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
Ian Rogersff1ed472011-09-20 13:46:24 -07001199 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001200 rlResult = oatGetReturnWide(cUnit);
1201 storeValueWide(cUnit, rlDest, rlResult);
1202 return false;
1203}
1204
buzbeeed3e9302011-09-23 17:34:19 -07001205STATIC bool genConversionPortable(CompilationUnit* cUnit, MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -07001206{
1207 Opcode opcode = mir->dalvikInsn.opcode;
1208
1209 switch (opcode) {
1210 case OP_INT_TO_FLOAT:
1211 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f),
1212 1, 1);
1213 case OP_FLOAT_TO_INT:
1214 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz),
1215 1, 1);
1216 case OP_DOUBLE_TO_FLOAT:
1217 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f),
1218 2, 1);
1219 case OP_FLOAT_TO_DOUBLE:
1220 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d),
1221 1, 2);
1222 case OP_INT_TO_DOUBLE:
1223 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d),
1224 1, 2);
1225 case OP_DOUBLE_TO_INT:
1226 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz),
1227 2, 1);
1228 case OP_FLOAT_TO_LONG:
1229 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001230 pF2l), 1, 2);
buzbee67bf8852011-08-17 17:51:35 -07001231 case OP_LONG_TO_FLOAT:
1232 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f),
1233 2, 1);
1234 case OP_DOUBLE_TO_LONG:
1235 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001236 pD2l), 2, 2);
buzbee67bf8852011-08-17 17:51:35 -07001237 case OP_LONG_TO_DOUBLE:
1238 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d),
1239 2, 2);
1240 default:
1241 return true;
1242 }
1243 return false;
1244}
1245
buzbee67bf8852011-08-17 17:51:35 -07001246/*
1247 * Generate array store
1248 *
1249 */
buzbeeed3e9302011-09-23 17:34:19 -07001250STATIC void genArrayObjPut(CompilationUnit* cUnit, MIR* mir,
buzbee1b4c8592011-08-31 10:43:51 -07001251 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
buzbee6181f792011-09-29 11:14:04 -07001258 oatFlushAllRegs(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001259 /* Make sure it's a legal object Put. Use direct regs at first */
1260 loadValueDirectFixed(cUnit, rlArray, r1);
1261 loadValueDirectFixed(cUnit, rlSrc, r0);
1262
1263 /* null array object? */
buzbee43a36422011-09-14 14:00:13 -07001264 genNullCheck(cUnit, rlArray.sRegLow, r1, mir);
buzbee67bf8852011-08-17 17:51:35 -07001265 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -07001266 OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -07001267 /* Get the array's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001268 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001269 callRuntimeHelper(cUnit, rLR);
buzbee6181f792011-09-29 11:14:04 -07001270 oatFreeTemp(cUnit, r0);
1271 oatFreeTemp(cUnit, r1);
buzbee67bf8852011-08-17 17:51:35 -07001272
1273 // Now, redo loadValues in case they didn't survive the call
1274
1275 int regPtr;
1276 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1277 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1278
1279 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1280 oatClobber(cUnit, rlArray.lowReg);
1281 regPtr = rlArray.lowReg;
1282 } else {
1283 regPtr = oatAllocTemp(cUnit);
1284 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1285 }
1286
buzbee43a36422011-09-14 14:00:13 -07001287 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001288 int regLen = oatAllocTemp(cUnit);
1289 //NOTE: max live temps(4) here.
1290 /* Get len */
1291 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1292 /* regPtr -> array data */
1293 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001294 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001295 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001296 oatFreeTemp(cUnit, regLen);
1297 } else {
1298 /* regPtr -> array data */
1299 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1300 }
1301 /* at this point, regPtr points to array, 2 live temps */
1302 rlSrc = loadValue(cUnit, rlSrc, regClass);
1303 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1304 scale, kWord);
1305}
1306
1307/*
1308 * Generate array load
1309 */
buzbeeed3e9302011-09-23 17:34:19 -07001310STATIC void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001311 RegLocation rlArray, RegLocation rlIndex,
1312 RegLocation rlDest, int scale)
1313{
1314 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001315 int lenOffset = Array::LengthOffset().Int32Value();
1316 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001317 RegLocation rlResult;
1318 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1319 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1320 int regPtr;
1321
1322 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001323 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001324
1325 regPtr = oatAllocTemp(cUnit);
1326
buzbee43a36422011-09-14 14:00:13 -07001327 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001328 int regLen = oatAllocTemp(cUnit);
1329 /* Get len */
1330 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1331 /* regPtr -> array data */
1332 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001333 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001334 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001335 oatFreeTemp(cUnit, regLen);
1336 } else {
1337 /* regPtr -> array data */
1338 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
1339 }
buzbeee9a72f62011-09-04 17:59:07 -07001340 oatFreeTemp(cUnit, rlArray.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001341 if ((size == kLong) || (size == kDouble)) {
1342 if (scale) {
1343 int rNewIndex = oatAllocTemp(cUnit);
1344 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1345 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1346 oatFreeTemp(cUnit, rNewIndex);
1347 } else {
1348 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1349 }
buzbeee9a72f62011-09-04 17:59:07 -07001350 oatFreeTemp(cUnit, rlIndex.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001351 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1352
1353 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
1354
1355 oatFreeTemp(cUnit, regPtr);
1356 storeValueWide(cUnit, rlDest, rlResult);
1357 } else {
1358 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1359
1360 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
1361 scale, size);
1362
1363 oatFreeTemp(cUnit, regPtr);
1364 storeValue(cUnit, rlDest, rlResult);
1365 }
1366}
1367
1368/*
1369 * Generate array store
1370 *
1371 */
buzbeeed3e9302011-09-23 17:34:19 -07001372STATIC void genArrayPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001373 RegLocation rlArray, RegLocation rlIndex,
1374 RegLocation rlSrc, int scale)
1375{
1376 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001377 int lenOffset = Array::LengthOffset().Int32Value();
1378 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001379
1380 int regPtr;
1381 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1382 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1383
1384 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1385 oatClobber(cUnit, rlArray.lowReg);
1386 regPtr = rlArray.lowReg;
1387 } else {
1388 regPtr = oatAllocTemp(cUnit);
1389 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1390 }
1391
1392 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001393 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001394
buzbee43a36422011-09-14 14:00:13 -07001395 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001396 int regLen = oatAllocTemp(cUnit);
1397 //NOTE: max live temps(4) here.
1398 /* Get len */
1399 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1400 /* regPtr -> array data */
1401 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001402 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001403 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001404 oatFreeTemp(cUnit, regLen);
1405 } else {
1406 /* regPtr -> array data */
1407 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1408 }
1409 /* at this point, regPtr points to array, 2 live temps */
1410 if ((size == kLong) || (size == kDouble)) {
buzbee5ade1d22011-09-09 14:44:52 -07001411 //TUNING: specific wide routine that can handle fp regs
buzbee67bf8852011-08-17 17:51:35 -07001412 if (scale) {
1413 int rNewIndex = oatAllocTemp(cUnit);
1414 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1415 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1416 oatFreeTemp(cUnit, rNewIndex);
1417 } else {
1418 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1419 }
1420 rlSrc = loadValueWide(cUnit, rlSrc, regClass);
1421
1422 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
1423
1424 oatFreeTemp(cUnit, regPtr);
1425 } else {
1426 rlSrc = loadValue(cUnit, rlSrc, regClass);
1427
1428 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1429 scale, size);
1430 }
1431}
1432
buzbeeed3e9302011-09-23 17:34:19 -07001433STATIC bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001434 RegLocation rlDest, RegLocation rlSrc1,
1435 RegLocation rlShift)
1436{
buzbee54330722011-08-23 16:46:55 -07001437 int funcOffset;
buzbee67bf8852011-08-17 17:51:35 -07001438
buzbee67bf8852011-08-17 17:51:35 -07001439 switch( mir->dalvikInsn.opcode) {
1440 case OP_SHL_LONG:
1441 case OP_SHL_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001442 funcOffset = OFFSETOF_MEMBER(Thread, pShlLong);
buzbee67bf8852011-08-17 17:51:35 -07001443 break;
1444 case OP_SHR_LONG:
1445 case OP_SHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001446 funcOffset = OFFSETOF_MEMBER(Thread, pShrLong);
buzbee67bf8852011-08-17 17:51:35 -07001447 break;
1448 case OP_USHR_LONG:
1449 case OP_USHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001450 funcOffset = OFFSETOF_MEMBER(Thread, pUshrLong);
buzbee67bf8852011-08-17 17:51:35 -07001451 break;
1452 default:
buzbee54330722011-08-23 16:46:55 -07001453 LOG(FATAL) << "Unexpected case";
buzbee67bf8852011-08-17 17:51:35 -07001454 return true;
1455 }
buzbee54330722011-08-23 16:46:55 -07001456 oatFlushAllRegs(cUnit); /* Send everything to home location */
1457 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1458 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1459 loadValueDirect(cUnit, rlShift, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -07001460 callRuntimeHelper(cUnit, rLR);
buzbee54330722011-08-23 16:46:55 -07001461 RegLocation rlResult = oatGetReturnWide(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001462 storeValueWide(cUnit, rlDest, rlResult);
1463 return false;
1464}
1465
buzbeeed3e9302011-09-23 17:34:19 -07001466STATIC bool genArithOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001467 RegLocation rlDest, RegLocation rlSrc1,
1468 RegLocation rlSrc2)
1469{
1470 RegLocation rlResult;
1471 OpKind firstOp = kOpBkpt;
1472 OpKind secondOp = kOpBkpt;
1473 bool callOut = false;
buzbee58f92742011-10-01 11:22:17 -07001474 bool checkZero = false;
buzbee67bf8852011-08-17 17:51:35 -07001475 int funcOffset;
1476 int retReg = r0;
1477
1478 switch (mir->dalvikInsn.opcode) {
1479 case OP_NOT_LONG:
1480 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1481 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001482 // Check for destructive overlap
1483 if (rlResult.lowReg == rlSrc2.highReg) {
1484 int tReg = oatAllocTemp(cUnit);
1485 genRegCopy(cUnit, tReg, rlSrc2.highReg);
1486 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1487 opRegReg(cUnit, kOpMvn, rlResult.highReg, tReg);
1488 oatFreeTemp(cUnit, tReg);
1489 } else {
1490 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1491 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
1492 }
buzbee67bf8852011-08-17 17:51:35 -07001493 storeValueWide(cUnit, rlDest, rlResult);
1494 return false;
1495 break;
1496 case OP_ADD_LONG:
1497 case OP_ADD_LONG_2ADDR:
1498 firstOp = kOpAdd;
1499 secondOp = kOpAdc;
1500 break;
1501 case OP_SUB_LONG:
1502 case OP_SUB_LONG_2ADDR:
1503 firstOp = kOpSub;
1504 secondOp = kOpSbc;
1505 break;
1506 case OP_MUL_LONG:
1507 case OP_MUL_LONG_2ADDR:
buzbee439c4fa2011-08-27 15:59:07 -07001508 callOut = true;
1509 retReg = r0;
1510 funcOffset = OFFSETOF_MEMBER(Thread, pLmul);
1511 break;
buzbee67bf8852011-08-17 17:51:35 -07001512 case OP_DIV_LONG:
1513 case OP_DIV_LONG_2ADDR:
1514 callOut = true;
buzbee58f92742011-10-01 11:22:17 -07001515 checkZero = true;
buzbee67bf8852011-08-17 17:51:35 -07001516 retReg = r0;
1517 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1518 break;
1519 /* NOTE - result is in r2/r3 instead of r0/r1 */
1520 case OP_REM_LONG:
1521 case OP_REM_LONG_2ADDR:
1522 callOut = true;
buzbee58f92742011-10-01 11:22:17 -07001523 checkZero = true;
buzbee67bf8852011-08-17 17:51:35 -07001524 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1525 retReg = r2;
1526 break;
1527 case OP_AND_LONG_2ADDR:
1528 case OP_AND_LONG:
1529 firstOp = kOpAnd;
1530 secondOp = kOpAnd;
1531 break;
1532 case OP_OR_LONG:
1533 case OP_OR_LONG_2ADDR:
1534 firstOp = kOpOr;
1535 secondOp = kOpOr;
1536 break;
1537 case OP_XOR_LONG:
1538 case OP_XOR_LONG_2ADDR:
1539 firstOp = kOpXor;
1540 secondOp = kOpXor;
1541 break;
1542 case OP_NEG_LONG: {
buzbee67bf8852011-08-17 17:51:35 -07001543 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1544 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001545 int zReg = oatAllocTemp(cUnit);
1546 loadConstantNoClobber(cUnit, zReg, 0);
1547 // Check for destructive overlap
1548 if (rlResult.lowReg == rlSrc2.highReg) {
1549 int tReg = oatAllocTemp(cUnit);
1550 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1551 zReg, rlSrc2.lowReg);
1552 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1553 zReg, tReg);
1554 oatFreeTemp(cUnit, tReg);
1555 } else {
1556 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1557 zReg, rlSrc2.lowReg);
1558 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1559 zReg, rlSrc2.highReg);
1560 }
1561 oatFreeTemp(cUnit, zReg);
buzbee67bf8852011-08-17 17:51:35 -07001562 storeValueWide(cUnit, rlDest, rlResult);
1563 return false;
1564 }
1565 default:
1566 LOG(FATAL) << "Invalid long arith op";
1567 }
1568 if (!callOut) {
1569 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
1570 } else {
buzbee67bf8852011-08-17 17:51:35 -07001571 oatFlushAllRegs(cUnit); /* Send everything to home location */
buzbee58f92742011-10-01 11:22:17 -07001572 if (checkZero) {
1573 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1574 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1575 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1576 int tReg = oatAllocTemp(cUnit);
1577 newLIR4(cUnit, kThumb2OrrRRRs, tReg, r2, r3, 0);
1578 oatFreeTemp(cUnit, tReg);
1579 genCheck(cUnit, kArmCondEq, mir, kArmThrowDivZero);
1580 } else {
1581 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1582 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1583 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
1584 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001585 callRuntimeHelper(cUnit, rLR);
buzbee58f92742011-10-01 11:22:17 -07001586 // Adjust return regs in to handle case of rem returning r2/r3
buzbee67bf8852011-08-17 17:51:35 -07001587 if (retReg == r0)
1588 rlResult = oatGetReturnWide(cUnit);
1589 else
1590 rlResult = oatGetReturnWideAlt(cUnit);
1591 storeValueWide(cUnit, rlDest, rlResult);
1592 }
1593 return false;
1594}
1595
buzbeeed3e9302011-09-23 17:34:19 -07001596STATIC bool genArithOpInt(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001597 RegLocation rlDest, RegLocation rlSrc1,
1598 RegLocation rlSrc2)
1599{
1600 OpKind op = kOpBkpt;
1601 bool callOut = false;
1602 bool checkZero = false;
1603 bool unary = false;
1604 int retReg = r0;
1605 int funcOffset;
1606 RegLocation rlResult;
1607 bool shiftOp = false;
1608
1609 switch (mir->dalvikInsn.opcode) {
1610 case OP_NEG_INT:
1611 op = kOpNeg;
1612 unary = true;
1613 break;
1614 case OP_NOT_INT:
1615 op = kOpMvn;
1616 unary = true;
1617 break;
1618 case OP_ADD_INT:
1619 case OP_ADD_INT_2ADDR:
1620 op = kOpAdd;
1621 break;
1622 case OP_SUB_INT:
1623 case OP_SUB_INT_2ADDR:
1624 op = kOpSub;
1625 break;
1626 case OP_MUL_INT:
1627 case OP_MUL_INT_2ADDR:
1628 op = kOpMul;
1629 break;
1630 case OP_DIV_INT:
1631 case OP_DIV_INT_2ADDR:
1632 callOut = true;
1633 checkZero = true;
1634 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1635 retReg = r0;
1636 break;
1637 /* NOTE: returns in r1 */
1638 case OP_REM_INT:
1639 case OP_REM_INT_2ADDR:
1640 callOut = true;
1641 checkZero = true;
1642 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1643 retReg = r1;
1644 break;
1645 case OP_AND_INT:
1646 case OP_AND_INT_2ADDR:
1647 op = kOpAnd;
1648 break;
1649 case OP_OR_INT:
1650 case OP_OR_INT_2ADDR:
1651 op = kOpOr;
1652 break;
1653 case OP_XOR_INT:
1654 case OP_XOR_INT_2ADDR:
1655 op = kOpXor;
1656 break;
1657 case OP_SHL_INT:
1658 case OP_SHL_INT_2ADDR:
1659 shiftOp = true;
1660 op = kOpLsl;
1661 break;
1662 case OP_SHR_INT:
1663 case OP_SHR_INT_2ADDR:
1664 shiftOp = true;
1665 op = kOpAsr;
1666 break;
1667 case OP_USHR_INT:
1668 case OP_USHR_INT_2ADDR:
1669 shiftOp = true;
1670 op = kOpLsr;
1671 break;
1672 default:
1673 LOG(FATAL) << "Invalid word arith op: " <<
1674 (int)mir->dalvikInsn.opcode;
1675 }
1676 if (!callOut) {
1677 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
1678 if (unary) {
1679 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1680 opRegReg(cUnit, op, rlResult.lowReg,
1681 rlSrc1.lowReg);
1682 } else {
1683 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
1684 if (shiftOp) {
1685 int tReg = oatAllocTemp(cUnit);
1686 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
1687 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1688 opRegRegReg(cUnit, op, rlResult.lowReg,
1689 rlSrc1.lowReg, tReg);
1690 oatFreeTemp(cUnit, tReg);
1691 } else {
1692 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1693 opRegRegReg(cUnit, op, rlResult.lowReg,
1694 rlSrc1.lowReg, rlSrc2.lowReg);
1695 }
1696 }
1697 storeValue(cUnit, rlDest, rlResult);
1698 } else {
1699 RegLocation rlResult;
1700 oatFlushAllRegs(cUnit); /* Send everything to home location */
1701 loadValueDirectFixed(cUnit, rlSrc2, r1);
1702 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1703 loadValueDirectFixed(cUnit, rlSrc1, r0);
1704 if (checkZero) {
buzbee5ade1d22011-09-09 14:44:52 -07001705 genImmedCheck(cUnit, kArmCondEq, r1, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001706 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001707 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001708 if (retReg == r0)
1709 rlResult = oatGetReturn(cUnit);
1710 else
1711 rlResult = oatGetReturnAlt(cUnit);
1712 storeValue(cUnit, rlDest, rlResult);
1713 }
1714 return false;
1715}
1716
buzbeec1f45042011-09-21 16:03:19 -07001717/* Check if we need to check for pending suspend request */
buzbeeed3e9302011-09-23 17:34:19 -07001718STATIC void genSuspendTest(CompilationUnit* cUnit, MIR* mir)
buzbeec1f45042011-09-21 16:03:19 -07001719{
Ian Rogersa3760aa2011-11-14 14:32:37 -08001720 if (NO_SUSPEND || (mir->optimizationFlags & MIR_IGNORE_SUSPEND_CHECK)) {
buzbeec1f45042011-09-21 16:03:19 -07001721 return;
1722 }
buzbee6181f792011-09-29 11:14:04 -07001723 oatFlushAllRegs(cUnit);
buzbee44b412b2012-02-04 08:50:53 -08001724 ArmLIR* branch;
1725 if (cUnit->genDebugger) {
1726 // If generating code for the debugger, always check for suspension
1727 branch = genUnconditionalBranch(cUnit, NULL);
1728 } else {
1729 // In non-debug case, only check periodically
1730 newLIR2(cUnit, kThumbSubRI8, rSUSPEND, 1);
1731 branch = opCondBranch(cUnit, kArmCondEq);
1732 }
buzbeec1f45042011-09-21 16:03:19 -07001733 ArmLIR* retLab = newLIR0(cUnit, kArmPseudoTargetLabel);
1734 retLab->defMask = ENCODE_ALL;
buzbeeba938cb2012-02-03 14:47:55 -08001735 ArmLIR* target = (ArmLIR*)oatNew(cUnit, sizeof(ArmLIR), true, kAllocLIR);
buzbeec1f45042011-09-21 16:03:19 -07001736 target->generic.dalvikOffset = cUnit->currentDalvikOffset;
1737 target->opcode = kArmPseudoSuspendTarget;
1738 target->operands[0] = (intptr_t)retLab;
1739 target->operands[1] = mir->offset;
1740 branch->generic.target = (LIR*)target;
buzbeeba938cb2012-02-03 14:47:55 -08001741 oatInsertGrowableList(cUnit, &cUnit->suspendLaunchpads, (intptr_t)target);
buzbeec1f45042011-09-21 16:03:19 -07001742}
1743
buzbee67bf8852011-08-17 17:51:35 -07001744/*
1745 * The following are the first-level codegen routines that analyze the format
1746 * of each bytecode then either dispatch special purpose codegen routines
1747 * or produce corresponding Thumb instructions directly.
1748 */
1749
buzbeeed3e9302011-09-23 17:34:19 -07001750STATIC bool isPowerOfTwo(int x)
buzbee67bf8852011-08-17 17:51:35 -07001751{
1752 return (x & (x - 1)) == 0;
1753}
1754
1755// Returns true if no more than two bits are set in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001756STATIC bool isPopCountLE2(unsigned int x)
buzbee67bf8852011-08-17 17:51:35 -07001757{
1758 x &= x - 1;
1759 return (x & (x - 1)) == 0;
1760}
1761
1762// Returns the index of the lowest set bit in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001763STATIC int lowestSetBit(unsigned int x) {
buzbee67bf8852011-08-17 17:51:35 -07001764 int bit_posn = 0;
1765 while ((x & 0xf) == 0) {
1766 bit_posn += 4;
1767 x >>= 4;
1768 }
1769 while ((x & 1) == 0) {
1770 bit_posn++;
1771 x >>= 1;
1772 }
1773 return bit_posn;
1774}
1775
1776// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
1777// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001778STATIC bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode,
buzbee67bf8852011-08-17 17:51:35 -07001779 RegLocation rlSrc, RegLocation rlDest, int lit)
1780{
1781 if (lit < 2 || !isPowerOfTwo(lit)) {
1782 return false;
1783 }
1784 int k = lowestSetBit(lit);
1785 if (k >= 30) {
1786 // Avoid special cases.
1787 return false;
1788 }
1789 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 ||
1790 dalvikOpcode == OP_DIV_INT_LIT16);
1791 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1792 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1793 if (div) {
1794 int tReg = oatAllocTemp(cUnit);
1795 if (lit == 2) {
1796 // Division by 2 is by far the most common division by constant.
1797 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
1798 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1799 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1800 } else {
1801 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
1802 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
1803 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1804 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1805 }
1806 } else {
1807 int cReg = oatAllocTemp(cUnit);
1808 loadConstant(cUnit, cReg, lit - 1);
1809 int tReg1 = oatAllocTemp(cUnit);
1810 int tReg2 = oatAllocTemp(cUnit);
1811 if (lit == 2) {
1812 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
1813 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1814 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1815 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1816 } else {
1817 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
1818 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
1819 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1820 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1821 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1822 }
1823 }
1824 storeValue(cUnit, rlDest, rlResult);
1825 return true;
1826}
1827
1828// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
1829// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001830STATIC bool handleEasyMultiply(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001831 RegLocation rlSrc, RegLocation rlDest, int lit)
1832{
1833 // Can we simplify this multiplication?
1834 bool powerOfTwo = false;
1835 bool popCountLE2 = false;
1836 bool powerOfTwoMinusOne = false;
1837 if (lit < 2) {
1838 // Avoid special cases.
1839 return false;
1840 } else if (isPowerOfTwo(lit)) {
1841 powerOfTwo = true;
1842 } else if (isPopCountLE2(lit)) {
1843 popCountLE2 = true;
1844 } else if (isPowerOfTwo(lit + 1)) {
1845 powerOfTwoMinusOne = true;
1846 } else {
1847 return false;
1848 }
1849 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1850 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1851 if (powerOfTwo) {
1852 // Shift.
1853 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
1854 lowestSetBit(lit));
1855 } else if (popCountLE2) {
1856 // Shift and add and shift.
1857 int firstBit = lowestSetBit(lit);
1858 int secondBit = lowestSetBit(lit ^ (1 << firstBit));
1859 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
1860 firstBit, secondBit);
1861 } else {
1862 // Reverse subtract: (src << (shift + 1)) - src.
buzbeeed3e9302011-09-23 17:34:19 -07001863 DCHECK(powerOfTwoMinusOne);
buzbee5ade1d22011-09-09 14:44:52 -07001864 // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1)
buzbee67bf8852011-08-17 17:51:35 -07001865 int tReg = oatAllocTemp(cUnit);
1866 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
1867 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
1868 }
1869 storeValue(cUnit, rlDest, rlResult);
1870 return true;
1871}
1872
buzbeeed3e9302011-09-23 17:34:19 -07001873STATIC bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001874 RegLocation rlDest, RegLocation rlSrc,
1875 int lit)
1876{
1877 Opcode dalvikOpcode = mir->dalvikInsn.opcode;
1878 RegLocation rlResult;
1879 OpKind op = (OpKind)0; /* Make gcc happy */
1880 int shiftOp = false;
1881 bool isDiv = false;
1882 int funcOffset;
1883
1884 switch (dalvikOpcode) {
1885 case OP_RSUB_INT_LIT8:
1886 case OP_RSUB_INT: {
1887 int tReg;
1888 //TUNING: add support for use of Arm rsub op
1889 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1890 tReg = oatAllocTemp(cUnit);
1891 loadConstant(cUnit, tReg, lit);
1892 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1893 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1894 tReg, rlSrc.lowReg);
1895 storeValue(cUnit, rlDest, rlResult);
1896 return false;
1897 break;
1898 }
1899
1900 case OP_ADD_INT_LIT8:
1901 case OP_ADD_INT_LIT16:
1902 op = kOpAdd;
1903 break;
1904 case OP_MUL_INT_LIT8:
1905 case OP_MUL_INT_LIT16: {
1906 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
1907 return false;
1908 }
1909 op = kOpMul;
1910 break;
1911 }
1912 case OP_AND_INT_LIT8:
1913 case OP_AND_INT_LIT16:
1914 op = kOpAnd;
1915 break;
1916 case OP_OR_INT_LIT8:
1917 case OP_OR_INT_LIT16:
1918 op = kOpOr;
1919 break;
1920 case OP_XOR_INT_LIT8:
1921 case OP_XOR_INT_LIT16:
1922 op = kOpXor;
1923 break;
1924 case OP_SHL_INT_LIT8:
1925 lit &= 31;
1926 shiftOp = true;
1927 op = kOpLsl;
1928 break;
1929 case OP_SHR_INT_LIT8:
1930 lit &= 31;
1931 shiftOp = true;
1932 op = kOpAsr;
1933 break;
1934 case OP_USHR_INT_LIT8:
1935 lit &= 31;
1936 shiftOp = true;
1937 op = kOpLsr;
1938 break;
1939
1940 case OP_DIV_INT_LIT8:
1941 case OP_DIV_INT_LIT16:
1942 case OP_REM_INT_LIT8:
1943 case OP_REM_INT_LIT16:
1944 if (lit == 0) {
buzbee5ade1d22011-09-09 14:44:52 -07001945 genImmedCheck(cUnit, kArmCondAl, 0, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001946 return false;
1947 }
1948 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
1949 return false;
1950 }
1951 oatFlushAllRegs(cUnit); /* Everything to home location */
1952 loadValueDirectFixed(cUnit, rlSrc, r0);
1953 oatClobber(cUnit, r0);
1954 if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
1955 (dalvikOpcode == OP_DIV_INT_LIT16)) {
1956 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1957 isDiv = true;
1958 } else {
1959 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1960 isDiv = false;
1961 }
1962 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1963 loadConstant(cUnit, r1, lit);
Ian Rogersff1ed472011-09-20 13:46:24 -07001964 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001965 if (isDiv)
1966 rlResult = oatGetReturn(cUnit);
1967 else
1968 rlResult = oatGetReturnAlt(cUnit);
1969 storeValue(cUnit, rlDest, rlResult);
1970 return false;
1971 break;
1972 default:
1973 return true;
1974 }
1975 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1976 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1977 // Avoid shifts by literal 0 - no support in Thumb. Change to copy
1978 if (shiftOp && (lit == 0)) {
1979 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
1980 } else {
1981 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
1982 }
1983 storeValue(cUnit, rlDest, rlResult);
1984 return false;
1985}
1986
1987/* Architectural-specific debugging helpers go here */
1988void oatArchDump(void)
1989{
1990 /* Print compiled opcode in this VM instance */
1991 int i, start, streak;
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07001992 std::string buf;
buzbee67bf8852011-08-17 17:51:35 -07001993
1994 streak = i = 0;
buzbee67bf8852011-08-17 17:51:35 -07001995 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
1996 i++;
1997 }
1998 if (i == kNumPackedOpcodes) {
1999 return;
2000 }
2001 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
2002 if (opcodeCoverage[i]) {
2003 streak++;
2004 } else {
2005 if (streak == 1) {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002006 StringAppendF(&buf, "%x,", start);
buzbee67bf8852011-08-17 17:51:35 -07002007 } else {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002008 StringAppendF(&buf, "%x-%x,", start, start + streak - 1);
buzbee67bf8852011-08-17 17:51:35 -07002009 }
2010 streak = 0;
2011 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2012 i++;
2013 }
2014 if (i < kNumPackedOpcodes) {
2015 streak = 1;
2016 start = i;
2017 }
2018 }
2019 }
2020 if (streak) {
2021 if (streak == 1) {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002022 StringAppendF(&buf, "%x", start);
buzbee67bf8852011-08-17 17:51:35 -07002023 } else {
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002024 StringAppendF(&buf, "%x-%x", start, start + streak - 1);
buzbee67bf8852011-08-17 17:51:35 -07002025 }
2026 }
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07002027 if (!buf.empty()) {
buzbee67bf8852011-08-17 17:51:35 -07002028 LOG(INFO) << "dalvik.vm.oat.op = " << buf;
2029 }
2030}
Elliott Hughes11d1b0c2012-01-23 16:57:47 -08002031
2032} // namespace art