blob: 173aa64e44a689a7018a0929f0148e82490db2ce [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{
buzbee6181f792011-09-29 11:14:04 -070060 oatClobberCalleeSave(cUnit);
buzbeeec5adf32011-09-11 15:25:43 -070061 return opReg(cUnit, kOpBlx, reg);
62}
63
buzbee1b4c8592011-08-31 10:43:51 -070064/* Generate unconditional branch instructions */
buzbeeed3e9302011-09-23 17:34:19 -070065STATIC ArmLIR* genUnconditionalBranch(CompilationUnit* cUnit, ArmLIR* target)
buzbee1b4c8592011-08-31 10:43:51 -070066{
67 ArmLIR* branch = opNone(cUnit, kOpUncondBr);
68 branch->generic.target = (LIR*) target;
69 return branch;
70}
71
buzbee67bf8852011-08-17 17:51:35 -070072/*
73 * Generate a Thumb2 IT instruction, which can nullify up to
74 * four subsequent instructions based on a condition and its
75 * inverse. The condition applies to the first instruction, which
76 * is executed if the condition is met. The string "guide" consists
77 * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
78 * A "T" means the instruction is executed if the condition is
79 * met, and an "E" means the instruction is executed if the condition
80 * is not met.
81 */
buzbeeed3e9302011-09-23 17:34:19 -070082STATIC ArmLIR* genIT(CompilationUnit* cUnit, ArmConditionCode code,
buzbee67bf8852011-08-17 17:51:35 -070083 const char* guide)
84{
85 int mask;
86 int condBit = code & 1;
87 int altBit = condBit ^ 1;
88 int mask3 = 0;
89 int mask2 = 0;
90 int mask1 = 0;
91
92 //Note: case fallthroughs intentional
93 switch(strlen(guide)) {
94 case 3:
95 mask1 = (guide[2] == 'T') ? condBit : altBit;
96 case 2:
97 mask2 = (guide[1] == 'T') ? condBit : altBit;
98 case 1:
99 mask3 = (guide[0] == 'T') ? condBit : altBit;
100 break;
101 case 0:
102 break;
103 default:
104 LOG(FATAL) << "OAT: bad case in genIT";
105 }
106 mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
107 (1 << (3 - strlen(guide)));
108 return newLIR2(cUnit, kThumb2It, code, mask);
109}
110
111/*
112 * Insert a kArmPseudoCaseLabel at the beginning of the Dalvik
113 * offset vaddr. This label will be used to fix up the case
114 * branch table during the assembly phase. Be sure to set
115 * all resource flags on this to prevent code motion across
116 * target boundaries. KeyVal is just there for debugging.
117 */
buzbeeed3e9302011-09-23 17:34:19 -0700118STATIC ArmLIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
buzbee67bf8852011-08-17 17:51:35 -0700119{
120 ArmLIR* lir;
121 for (lir = (ArmLIR*)cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
122 if ((lir->opcode == kArmPseudoDalvikByteCodeBoundary) &&
123 (lir->generic.dalvikOffset == vaddr)) {
124 ArmLIR* newLabel = (ArmLIR*)oatNew(sizeof(ArmLIR), true);
125 newLabel->generic.dalvikOffset = vaddr;
126 newLabel->opcode = kArmPseudoCaseLabel;
127 newLabel->operands[0] = keyVal;
128 oatInsertLIRAfter((LIR*)lir, (LIR*)newLabel);
129 return newLabel;
130 }
131 }
132 oatCodegenDump(cUnit);
133 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
134 return NULL; // Quiet gcc
135}
136
buzbeeed3e9302011-09-23 17:34:19 -0700137STATIC void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700138{
139 const u2* table = tabRec->table;
140 int baseVaddr = tabRec->vaddr;
141 int *targets = (int*)&table[4];
142 int entries = table[1];
143 int lowKey = s4FromSwitchData(&table[2]);
144 for (int i = 0; i < entries; i++) {
145 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
146 i + lowKey);
147 }
148}
149
buzbeeed3e9302011-09-23 17:34:19 -0700150STATIC void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
buzbee67bf8852011-08-17 17:51:35 -0700151{
152 const u2* table = tabRec->table;
153 int baseVaddr = tabRec->vaddr;
154 int entries = table[1];
155 int* keys = (int*)&table[2];
156 int* targets = &keys[entries];
157 for (int i = 0; i < entries; i++) {
158 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
159 keys[i]);
160 }
161}
162
163void oatProcessSwitchTables(CompilationUnit* cUnit)
164{
165 GrowableListIterator iterator;
166 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
167 while (true) {
168 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
169 &iterator);
170 if (tabRec == NULL) break;
171 if (tabRec->table[0] == kPackedSwitchSignature)
172 markPackedCaseLabels(cUnit, tabRec);
173 else if (tabRec->table[0] == kSparseSwitchSignature)
174 markSparseCaseLabels(cUnit, tabRec);
175 else {
176 LOG(FATAL) << "Invalid switch table";
177 }
178 }
179}
180
buzbeeed3e9302011-09-23 17:34:19 -0700181STATIC void dumpSparseSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700182 /*
183 * Sparse switch data format:
184 * ushort ident = 0x0200 magic value
185 * ushort size number of entries in the table; > 0
186 * int keys[size] keys, sorted low-to-high; 32-bit aligned
187 * int targets[size] branch targets, relative to switch opcode
188 *
189 * Total size is (2+size*4) 16-bit code units.
190 */
191{
192 u2 ident = table[0];
193 int entries = table[1];
194 int* keys = (int*)&table[2];
195 int* targets = &keys[entries];
196 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
197 ", entries: " << std::dec << entries;
198 for (int i = 0; i < entries; i++) {
199 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
200 targets[i];
201 }
202}
203
buzbeeed3e9302011-09-23 17:34:19 -0700204STATIC void dumpPackedSwitchTable(const u2* table)
buzbee67bf8852011-08-17 17:51:35 -0700205 /*
206 * Packed switch data format:
207 * ushort ident = 0x0100 magic value
208 * ushort size number of entries in the table
209 * int first_key first (and lowest) switch case value
210 * int targets[size] branch targets, relative to switch opcode
211 *
212 * Total size is (4+size*2) 16-bit code units.
213 */
214{
215 u2 ident = table[0];
216 int* targets = (int*)&table[4];
217 int entries = table[1];
218 int lowKey = s4FromSwitchData(&table[2]);
219 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
220 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
221 for (int i = 0; i < entries; i++) {
222 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
223 targets[i];
224 }
225}
226
227/*
228 * The sparse table in the literal pool is an array of <key,displacement>
229 * pairs. For each set, we'll load them as a pair using ldmia.
230 * This means that the register number of the temp we use for the key
231 * must be lower than the reg for the displacement.
232 *
233 * The test loop will look something like:
234 *
235 * adr rBase, <table>
236 * ldr rVal, [rSP, vRegOff]
237 * mov rIdx, #tableSize
238 * lp:
239 * ldmia rBase!, {rKey, rDisp}
240 * sub rIdx, #1
241 * cmp rVal, rKey
242 * ifeq
243 * add rPC, rDisp ; This is the branch from which we compute displacement
244 * cbnz rIdx, lp
245 */
buzbeeed3e9302011-09-23 17:34:19 -0700246STATIC void genSparseSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700247 RegLocation rlSrc)
248{
249 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
250 if (cUnit->printMe) {
251 dumpSparseSwitchTable(table);
252 }
253 // Add the table to the list - we'll process it later
254 SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable),
255 true);
256 tabRec->table = table;
257 tabRec->vaddr = mir->offset;
258 int size = table[1];
259 tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true);
260 oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec);
261
262 // Get the switch value
263 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
264 int rBase = oatAllocTemp(cUnit);
265 /* Allocate key and disp temps */
266 int rKey = oatAllocTemp(cUnit);
267 int rDisp = oatAllocTemp(cUnit);
268 // Make sure rKey's register number is less than rDisp's number for ldmia
269 if (rKey > rDisp) {
270 int tmp = rDisp;
271 rDisp = rKey;
272 rKey = tmp;
273 }
274 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700275 newLIR3(cUnit, kThumb2Adr, rBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700276 // Set up rIdx
277 int rIdx = oatAllocTemp(cUnit);
278 loadConstant(cUnit, rIdx, size);
279 // Establish loop branch target
280 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
281 target->defMask = ENCODE_ALL;
282 // Load next key/disp
283 newLIR2(cUnit, kThumb2LdmiaWB, rBase, (1 << rKey) | (1 << rDisp));
284 opRegReg(cUnit, kOpCmp, rKey, rlSrc.lowReg);
285 // Go if match. NOTE: No instruction set switch here - must stay Thumb2
286 genIT(cUnit, kArmCondEq, "");
287 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, rDisp);
288 tabRec->bxInst = switchBranch;
289 // Needs to use setflags encoding here
290 newLIR3(cUnit, kThumb2SubsRRI12, rIdx, rIdx, 1);
291 ArmLIR* branch = opCondBranch(cUnit, kArmCondNe);
292 branch->generic.target = (LIR*)target;
293}
294
295
buzbeeed3e9302011-09-23 17:34:19 -0700296STATIC void genPackedSwitch(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700297 RegLocation rlSrc)
298{
299 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
300 if (cUnit->printMe) {
301 dumpPackedSwitchTable(table);
302 }
303 // Add the table to the list - we'll process it later
304 SwitchTable *tabRec = (SwitchTable *)oatNew(sizeof(SwitchTable),
305 true);
306 tabRec->table = table;
307 tabRec->vaddr = mir->offset;
308 int size = table[1];
309 tabRec->targets = (ArmLIR* *)oatNew(size * sizeof(ArmLIR*), true);
310 oatInsertGrowableList(&cUnit->switchTables, (intptr_t)tabRec);
311
312 // Get the switch value
313 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
314 int tableBase = oatAllocTemp(cUnit);
315 // Materialize a pointer to the switch table
buzbee03fa2632011-09-20 17:10:57 -0700316 newLIR3(cUnit, kThumb2Adr, tableBase, 0, (intptr_t)tabRec);
buzbee67bf8852011-08-17 17:51:35 -0700317 int lowKey = s4FromSwitchData(&table[2]);
318 int keyReg;
319 // Remove the bias, if necessary
320 if (lowKey == 0) {
321 keyReg = rlSrc.lowReg;
322 } else {
323 keyReg = oatAllocTemp(cUnit);
324 opRegRegImm(cUnit, kOpSub, keyReg, rlSrc.lowReg, lowKey);
325 }
326 // Bounds check - if < 0 or >= size continue following switch
327 opRegImm(cUnit, kOpCmp, keyReg, size-1);
328 ArmLIR* branchOver = opCondBranch(cUnit, kArmCondHi);
329
330 // Load the displacement from the switch table
331 int dispReg = oatAllocTemp(cUnit);
332 loadBaseIndexed(cUnit, tableBase, keyReg, dispReg, 2, kWord);
333
334 // ..and go! NOTE: No instruction set switch here - must stay Thumb2
335 ArmLIR* switchBranch = newLIR1(cUnit, kThumb2AddPCR, dispReg);
336 tabRec->bxInst = switchBranch;
337
338 /* branchOver target here */
339 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
340 target->defMask = ENCODE_ALL;
341 branchOver->generic.target = (LIR*)target;
342}
343
344/*
345 * Array data table format:
346 * ushort ident = 0x0300 magic value
347 * ushort width width of each element in the table
348 * uint size number of elements in the table
349 * ubyte data[size*width] table of data values (may contain a single-byte
350 * padding at the end)
351 *
352 * Total size is 4+(width * size + 1)/2 16-bit code units.
353 */
buzbeeed3e9302011-09-23 17:34:19 -0700354STATIC void genFillArrayData(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700355 RegLocation rlSrc)
356{
357 const u2* table = cUnit->insns + mir->offset + mir->dalvikInsn.vB;
358 // Add the table to the list - we'll process it later
359 FillArrayData *tabRec = (FillArrayData *)
360 oatNew(sizeof(FillArrayData), true);
361 tabRec->table = table;
362 tabRec->vaddr = mir->offset;
363 u2 width = tabRec->table[1];
364 u4 size = tabRec->table[2] | (((u4)tabRec->table[3]) << 16);
365 tabRec->size = (size * width) + 8;
366
367 oatInsertGrowableList(&cUnit->fillArrayData, (intptr_t)tabRec);
368
369 // Making a call - use explicit registers
370 oatFlushAllRegs(cUnit); /* Everything to home location */
371 loadValueDirectFixed(cUnit, rlSrc, r0);
372 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700373 OFFSETOF_MEMBER(Thread, pHandleFillArrayDataFromCode), rLR);
buzbeee6d61962011-08-27 11:58:19 -0700374 // Materialize a pointer to the fill data image
buzbee03fa2632011-09-20 17:10:57 -0700375 newLIR3(cUnit, kThumb2Adr, r1, 0, (intptr_t)tabRec);
Ian Rogersff1ed472011-09-20 13:46:24 -0700376 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700377}
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;
buzbee6181f792011-09-29 11:14:04 -0700409 oatFlushAllRegs(cUnit);
buzbee34cd9e52011-09-08 14:31:52 -0700410 LOG(INFO) << "Field " << fieldNameFromIndex(cUnit->method, fieldIdx)
411 << " unresolved at compile time";
412 oatLockCallTemps(cUnit); // Explicit register usage
413 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
414 loadWordDisp(cUnit, r1,
415 Method::DexCacheResolvedFieldsOffset().Int32Value(), r0);
416 loadWordDisp(cUnit, r0, art::Array::DataOffset().Int32Value() +
417 sizeof(int32_t*)* fieldIdx, r0);
418 /*
419 * For testing, omit the test for run-time resolution. This will
420 * force all accesses to go through the runtime resolution path.
421 */
422#ifndef EXERCISE_SLOWEST_FIELD_PATH
423 ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0);
424#endif
425 // Resolve
426 loadWordDisp(cUnit, rSELF,
Brian Carlstrom845490b2011-09-19 15:56:53 -0700427 OFFSETOF_MEMBER(Thread, pFindInstanceFieldFromCode), rLR);
buzbee34cd9e52011-09-08 14:31:52 -0700428 loadConstant(cUnit, r0, fieldIdx);
Ian Rogersff1ed472011-09-20 13:46:24 -0700429 callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee34cd9e52011-09-08 14:31:52 -0700430 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
431 target->defMask = ENCODE_ALL;
432#ifndef EXERCISE_SLOWEST_FIELD_PATH
433 branchOver->generic.target = (LIR*)target;
434#endif
435 // Free temps (except for r0)
436 oatFreeTemp(cUnit, r1);
437 oatFreeTemp(cUnit, r2);
438 oatFreeTemp(cUnit, r3);
439 loadWordDisp(cUnit, r0, art::Field::OffsetOffset().Int32Value(), r0);
440}
441
buzbeeed3e9302011-09-23 17:34:19 -0700442STATIC void genIGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -0700443 RegLocation rlDest, RegLocation rlObj)
444{
buzbeec143c552011-08-20 17:38:58 -0700445 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
446 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700447 RegLocation rlResult;
448 RegisterClass regClass = oatRegClassBySize(size);
buzbee34cd9e52011-09-08 14:31:52 -0700449 if (SLOW_FIELD_PATH || fieldPtr == NULL) {
450 getFieldOffset(cUnit, mir);
451 // Field offset in r0
452 rlObj = loadValue(cUnit, rlObj, kCoreReg);
453 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
buzbee5ade1d22011-09-09 14:44:52 -0700454 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee34cd9e52011-09-08 14:31:52 -0700455 loadBaseIndexed(cUnit, rlObj.lowReg, r0, rlResult.lowReg, 0, size);
buzbee67bf8852011-08-17 17:51:35 -0700456 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700457 storeValue(cUnit, rlDest, rlResult);
458 } else {
459#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700460 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700461#else
462 bool isVolatile = false;
463#endif
464 int fieldOffset = fieldPtr->GetOffset().Int32Value();
465 rlObj = loadValue(cUnit, rlObj, kCoreReg);
466 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
buzbee5ade1d22011-09-09 14:44:52 -0700467 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee34cd9e52011-09-08 14:31:52 -0700468 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
469 size, rlObj.sRegLow);
470 if (isVolatile) {
471 oatGenMemBarrier(cUnit, kSY);
472 }
473 storeValue(cUnit, rlDest, rlResult);
buzbee67bf8852011-08-17 17:51:35 -0700474 }
buzbee67bf8852011-08-17 17:51:35 -0700475}
476
buzbeeed3e9302011-09-23 17:34:19 -0700477STATIC void genIPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -0700478 RegLocation rlSrc, RegLocation rlObj, bool isObject)
479{
buzbeec143c552011-08-20 17:38:58 -0700480 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
481 GetResolvedField(mir->dalvikInsn.vC);
buzbee67bf8852011-08-17 17:51:35 -0700482 RegisterClass regClass = oatRegClassBySize(size);
buzbee34cd9e52011-09-08 14:31:52 -0700483 if (SLOW_FIELD_PATH || fieldPtr == NULL) {
484 getFieldOffset(cUnit, mir);
485 // Field offset in r0
486 rlObj = loadValue(cUnit, rlObj, kCoreReg);
487 rlSrc = loadValue(cUnit, rlSrc, regClass);
buzbee5ade1d22011-09-09 14:44:52 -0700488 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
buzbee67bf8852011-08-17 17:51:35 -0700489 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700490 storeBaseIndexed(cUnit, rlObj.lowReg, r0, rlSrc.lowReg, 0, size);
491 } else {
492#if ANDROID_SMP != 0
Elliott Hughes1d3f1142011-09-13 12:00:00 -0700493 bool isVolatile = fieldPtr->IsVolatile();
buzbee34cd9e52011-09-08 14:31:52 -0700494#else
495 bool isVolatile = false;
496#endif
497 int fieldOffset = fieldPtr->GetOffset().Int32Value();
498 rlObj = loadValue(cUnit, rlObj, kCoreReg);
499 rlSrc = loadValue(cUnit, rlSrc, regClass);
buzbee5ade1d22011-09-09 14:44:52 -0700500 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700501
502 if (isVolatile) {
buzbee12246b82011-09-29 14:15:05 -0700503 oatGenMemBarrier(cUnit, kST);
buzbee34cd9e52011-09-08 14:31:52 -0700504 }
505 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
buzbee12246b82011-09-29 14:15:05 -0700506 if (isVolatile) {
507 oatGenMemBarrier(cUnit, kSY);
508 }
buzbee67bf8852011-08-17 17:51:35 -0700509 }
buzbee67bf8852011-08-17 17:51:35 -0700510 if (isObject) {
511 /* NOTE: marking card based on object head */
512 markGCCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
513 }
514}
515
buzbeeed3e9302011-09-23 17:34:19 -0700516STATIC void genIGetWide(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700517 RegLocation rlObj)
518{
buzbee12246b82011-09-29 14:15:05 -0700519 RegLocation rlResult;
buzbeec143c552011-08-20 17:38:58 -0700520 Field* fieldPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
521 GetResolvedField(mir->dalvikInsn.vC);
buzbee12246b82011-09-29 14:15:05 -0700522#if ANDROID_SMP != 0
523 bool isVolatile = (fieldPtr == NULL) || fieldPtr->IsVolatile();
524#else
525 bool isVolatile = false;
526#endif
527 if ((fieldPtr == NULL) || isVolatile) {
buzbee34cd9e52011-09-08 14:31:52 -0700528 getFieldOffset(cUnit, mir);
529 // Field offset in r0
530 rlObj = loadValue(cUnit, rlObj, kCoreReg);
531 rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
buzbee5ade1d22011-09-09 14:44:52 -0700532 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700533 opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
534 loadPair(cUnit, r0, rlResult.lowReg, rlResult.highReg);
buzbee67bf8852011-08-17 17:51:35 -0700535 oatGenMemBarrier(cUnit, kSY);
buzbee12246b82011-09-29 14:15:05 -0700536 storeValueWide(cUnit, rlDest, rlResult);
buzbee34cd9e52011-09-08 14:31:52 -0700537 } else {
buzbee34cd9e52011-09-08 14:31:52 -0700538 int fieldOffset = fieldPtr->GetOffset().Int32Value();
539 rlObj = loadValue(cUnit, rlObj, kCoreReg);
540 int regPtr = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700541
buzbeeed3e9302011-09-23 17:34:19 -0700542 DCHECK(rlDest.wide);
buzbee34cd9e52011-09-08 14:31:52 -0700543
buzbee5ade1d22011-09-09 14:44:52 -0700544 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700545 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
546 rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
547
548 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
549
buzbee34cd9e52011-09-08 14:31:52 -0700550 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);
buzbee12246b82011-09-29 14:15:05 -0700560#if ANDROID_SMP != 0
561 bool isVolatile = (fieldPtr == NULL) || fieldPtr->IsVolatile();
562#else
563 bool isVolatile = false;
564#endif
565 if ((fieldPtr == NULL) || isVolatile) {
buzbee34cd9e52011-09-08 14:31:52 -0700566 getFieldOffset(cUnit, mir);
567 // Field offset in r0
568 rlObj = loadValue(cUnit, rlObj, kCoreReg);
569 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
buzbee5ade1d22011-09-09 14:44:52 -0700570 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
buzbee34cd9e52011-09-08 14:31:52 -0700571 opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700572 oatGenMemBarrier(cUnit, kSY);
buzbee34cd9e52011-09-08 14:31:52 -0700573 storePair(cUnit, r0, rlSrc.lowReg, rlSrc.highReg);
574 } else {
buzbee34cd9e52011-09-08 14:31:52 -0700575 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
buzbee34cd9e52011-09-08 14:31:52 -0700584 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
585
586 oatFreeTemp(cUnit, regPtr);
587 }
buzbee67bf8852011-08-17 17:51:35 -0700588}
589
buzbeeed3e9302011-09-23 17:34:19 -0700590STATIC void genConstClass(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700591 RegLocation rlDest, RegLocation rlSrc)
592{
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700593 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
buzbee1b4c8592011-08-31 10:43:51 -0700594 Get(mir->dalvikInsn.vB);
595 int mReg = loadCurrMethod(cUnit);
596 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700597 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbee2a475e72011-09-07 17:19:17 -0700598 loadWordDisp(cUnit, mReg, Method::DexCacheResolvedTypesOffset().Int32Value(),
buzbee1b4c8592011-08-31 10:43:51 -0700599 resReg);
600 loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
601 (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
602 if (classPtr != NULL) {
603 // Fast path, we're done - just store result
604 storeValue(cUnit, rlDest, rlResult);
605 } else {
606 // Slow path. Must test at runtime
buzbee6181f792011-09-29 11:14:04 -0700607 oatFlushAllRegs(cUnit);
buzbee1b4c8592011-08-31 10:43:51 -0700608 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg,
609 0);
610 // Resolved, store and hop over following code
611 storeValue(cUnit, rlDest, rlResult);
612 ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
613 // TUNING: move slow path to end & remove unconditional branch
614 ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
615 target1->defMask = ENCODE_ALL;
616 // Call out to helper, which will return resolved type in r0
617 loadWordDisp(cUnit, rSELF,
618 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
619 genRegCopy(cUnit, r1, mReg);
620 loadConstant(cUnit, r0, mir->dalvikInsn.vB);
Ian Rogersff1ed472011-09-20 13:46:24 -0700621 callRuntimeHelper(cUnit, rLR);
buzbee1b4c8592011-08-31 10:43:51 -0700622 RegLocation rlResult = oatGetReturn(cUnit);
623 storeValue(cUnit, rlDest, rlResult);
624 // Rejoin code paths
625 ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
626 target2->defMask = ENCODE_ALL;
627 branch1->generic.target = (LIR*)target1;
628 branch2->generic.target = (LIR*)target2;
629 }
buzbee67bf8852011-08-17 17:51:35 -0700630}
631
buzbeeed3e9302011-09-23 17:34:19 -0700632STATIC void genConstString(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700633 RegLocation rlDest, RegLocation rlSrc)
634{
buzbee1b4c8592011-08-31 10:43:51 -0700635 /* All strings should be available at compile time */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700636 const art::String* str = cUnit->method->GetDexCacheStrings()->
buzbee1b4c8592011-08-31 10:43:51 -0700637 Get(mir->dalvikInsn.vB);
638 DCHECK(str != NULL);
buzbee67bf8852011-08-17 17:51:35 -0700639
buzbee1b4c8592011-08-31 10:43:51 -0700640 int mReg = loadCurrMethod(cUnit);
641 int resReg = oatAllocTemp(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700642 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700643 loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(),
buzbee1b4c8592011-08-31 10:43:51 -0700644 resReg);
645 loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
646 (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
buzbee67bf8852011-08-17 17:51:35 -0700647 storeValue(cUnit, rlDest, rlResult);
648}
649
buzbeedfd3d702011-08-28 12:56:51 -0700650/*
651 * Let helper function take care of everything. Will
652 * call Class::NewInstanceFromCode(type_idx, method);
653 */
buzbeeed3e9302011-09-23 17:34:19 -0700654STATIC void genNewInstance(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700655 RegLocation rlDest)
656{
buzbeedfd3d702011-08-28 12:56:51 -0700657 oatFlushAllRegs(cUnit); /* Everything to home location */
buzbee67bf8852011-08-17 17:51:35 -0700658 loadWordDisp(cUnit, rSELF,
Brian Carlstrom1f870082011-08-23 16:02:11 -0700659 OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
buzbeedfd3d702011-08-28 12:56:51 -0700660 loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
661 loadConstant(cUnit, r0, mir->dalvikInsn.vB); // arg0 <- type_id
Ian Rogersff1ed472011-09-20 13:46:24 -0700662 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700663 RegLocation rlResult = oatGetReturn(cUnit);
664 storeValue(cUnit, rlDest, rlResult);
665}
666
667void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
668{
buzbee6181f792011-09-29 11:14:04 -0700669 oatFlushAllRegs(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700670 loadWordDisp(cUnit, rSELF,
Ian Rogers67375ac2011-09-14 00:55:44 -0700671 OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
Ian Rogersbdb03912011-09-14 00:55:44 -0700672 loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
Ian Rogersff1ed472011-09-20 13:46:24 -0700673 callRuntimeHelper(cUnit, rLR); // art_deliver_exception(exception);
buzbee67bf8852011-08-17 17:51:35 -0700674}
675
buzbeeed3e9302011-09-23 17:34:19 -0700676STATIC void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700677 RegLocation rlSrc)
678{
buzbee6181f792011-09-29 11:14:04 -0700679 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700680 // May generate a call - use explicit registers
681 oatLockCallTemps(cUnit);
682 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
683 Get(mir->dalvikInsn.vC);
684 int classReg = r2; // Fixed usage
685 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
buzbee991e3ac2011-09-29 15:44:22 -0700686 loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */
buzbee2a475e72011-09-07 17:19:17 -0700687 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
688 classReg);
689 loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
690 (sizeof(String*) * mir->dalvikInsn.vC), classReg);
buzbee67bf8852011-08-17 17:51:35 -0700691 if (classPtr == NULL) {
buzbee2a475e72011-09-07 17:19:17 -0700692 // Generate a runtime test
693 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
694 // Not resolved
695 // Call out to helper, which will return resolved type in r0
696 loadWordDisp(cUnit, rSELF,
697 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
698 loadConstant(cUnit, r0, mir->dalvikInsn.vC);
Ian Rogersff1ed472011-09-20 13:46:24 -0700699 callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee2a475e72011-09-07 17:19:17 -0700700 genRegCopy(cUnit, r2, r0); // Align usage with fast path
buzbee991e3ac2011-09-29 15:44:22 -0700701 loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */
buzbee2a475e72011-09-07 17:19:17 -0700702 // 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 }
buzbee991e3ac2011-09-29 15:44:22 -0700707 /* r0 is ref, r2 is class. If ref==null, use directly as bool result */
708 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
buzbee2a475e72011-09-07 17:19:17 -0700709 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700710 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee991e3ac2011-09-29 15:44:22 -0700711 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
712 /* r0 is ref, r1 is ref->clazz, r2 is class */
buzbee67bf8852011-08-17 17:51:35 -0700713 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -0700714 OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
buzbee991e3ac2011-09-29 15:44:22 -0700715 opRegReg(cUnit, kOpCmp, r1, r2); // Same?
716 genBarrier(cUnit);
717 genIT(cUnit, kArmCondEq, "EE"); // if-convert the test
718 loadConstant(cUnit, r0, 1); // .eq case - load true
719 genRegCopy(cUnit, r0, r2); // .ne case - arg0 <= class
720 opReg(cUnit, kOpBlx, rLR); // .ne case: helper(class, ref->class)
721 genBarrier(cUnit);
722 oatClobberCalleeSave(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700723 /* branch target here */
724 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
725 target->defMask = ENCODE_ALL;
buzbee2a475e72011-09-07 17:19:17 -0700726 RegLocation rlResult = oatGetReturn(cUnit);
buzbee67bf8852011-08-17 17:51:35 -0700727 storeValue(cUnit, rlDest, rlResult);
728 branch1->generic.target = (LIR*)target;
buzbee67bf8852011-08-17 17:51:35 -0700729}
730
buzbeeed3e9302011-09-23 17:34:19 -0700731STATIC void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
buzbee67bf8852011-08-17 17:51:35 -0700732{
buzbee6181f792011-09-29 11:14:04 -0700733 oatFlushAllRegs(cUnit);
buzbee2a475e72011-09-07 17:19:17 -0700734 // May generate a call - use explicit registers
735 oatLockCallTemps(cUnit);
736 art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
737 Get(mir->dalvikInsn.vB);
738 int classReg = r2; // Fixed usage
739 loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
740 loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
741 classReg);
742 loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
743 (sizeof(String*) * mir->dalvikInsn.vB), classReg);
buzbee67bf8852011-08-17 17:51:35 -0700744 if (classPtr == NULL) {
buzbee2a475e72011-09-07 17:19:17 -0700745 // Generate a runtime test
746 ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
747 // Not resolved
748 // Call out to helper, which will return resolved type in r0
749 loadWordDisp(cUnit, rSELF,
750 OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
751 loadConstant(cUnit, r0, mir->dalvikInsn.vB);
Ian Rogersff1ed472011-09-20 13:46:24 -0700752 callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
buzbee2a475e72011-09-07 17:19:17 -0700753 genRegCopy(cUnit, r2, r0); // Align usage with fast path
754 // Rejoin code paths
755 ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
756 hopTarget->defMask = ENCODE_ALL;
757 hopBranch->generic.target = (LIR*)hopTarget;
buzbee67bf8852011-08-17 17:51:35 -0700758 }
buzbee2a475e72011-09-07 17:19:17 -0700759 // At this point, r2 has class
760 loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */
761 /* Null is OK - continue */
762 ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
763 /* load object->clazz */
buzbeeed3e9302011-09-23 17:34:19 -0700764 DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
buzbee2a475e72011-09-07 17:19:17 -0700765 loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
766 /* r1 now contains object->clazz */
buzbee67bf8852011-08-17 17:51:35 -0700767 loadWordDisp(cUnit, rSELF,
buzbee2a475e72011-09-07 17:19:17 -0700768 OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
769 opRegReg(cUnit, kOpCmp, r1, r2);
770 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
771 genRegCopy(cUnit, r0, r1);
772 genRegCopy(cUnit, r1, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -0700773 callRuntimeHelper(cUnit, rLR);
buzbee2a475e72011-09-07 17:19:17 -0700774 /* branch target here */
buzbee67bf8852011-08-17 17:51:35 -0700775 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
776 target->defMask = ENCODE_ALL;
777 branch1->generic.target = (LIR*)target;
778 branch2->generic.target = (LIR*)target;
779}
780
buzbeeed3e9302011-09-23 17:34:19 -0700781STATIC void genNegFloat(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700782 RegLocation rlSrc)
783{
784 RegLocation rlResult;
785 rlSrc = loadValue(cUnit, rlSrc, kFPReg);
786 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
787 newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
788 storeValue(cUnit, rlDest, rlResult);
789}
790
buzbeeed3e9302011-09-23 17:34:19 -0700791STATIC void genNegDouble(CompilationUnit* cUnit, RegLocation rlDest,
buzbee67bf8852011-08-17 17:51:35 -0700792 RegLocation rlSrc)
793{
794 RegLocation rlResult;
795 rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
796 rlResult = oatEvalLoc(cUnit, rlDest, kFPReg, true);
797 newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
798 S2D(rlSrc.lowReg, rlSrc.highReg));
799 storeValueWide(cUnit, rlDest, rlResult);
800}
801
buzbeeed3e9302011-09-23 17:34:19 -0700802STATIC void freeRegLocTemps(CompilationUnit* cUnit, RegLocation rlKeep,
buzbee439c4fa2011-08-27 15:59:07 -0700803 RegLocation rlFree)
buzbee67bf8852011-08-17 17:51:35 -0700804{
buzbee6181f792011-09-29 11:14:04 -0700805 if ((rlFree.lowReg != rlKeep.lowReg) && (rlFree.lowReg != rlKeep.highReg) &&
806 (rlFree.highReg != rlKeep.lowReg) && (rlFree.highReg != rlKeep.highReg)) {
807 // No overlap, free both
buzbee439c4fa2011-08-27 15:59:07 -0700808 oatFreeTemp(cUnit, rlFree.lowReg);
buzbee6181f792011-09-29 11:14:04 -0700809 oatFreeTemp(cUnit, rlFree.highReg);
810 }
buzbee67bf8852011-08-17 17:51:35 -0700811}
812
buzbeeed3e9302011-09-23 17:34:19 -0700813STATIC void genLong3Addr(CompilationUnit* cUnit, MIR* mir, OpKind firstOp,
buzbee67bf8852011-08-17 17:51:35 -0700814 OpKind secondOp, RegLocation rlDest,
815 RegLocation rlSrc1, RegLocation rlSrc2)
816{
buzbee9e0f9b02011-08-24 15:32:46 -0700817 /*
818 * NOTE: This is the one place in the code in which we might have
819 * as many as six live temporary registers. There are 5 in the normal
820 * set for Arm. Until we have spill capabilities, temporarily add
821 * lr to the temp set. It is safe to do this locally, but note that
822 * lr is used explicitly elsewhere in the code generator and cannot
823 * normally be used as a general temp register.
824 */
buzbee67bf8852011-08-17 17:51:35 -0700825 RegLocation rlResult;
buzbee9e0f9b02011-08-24 15:32:46 -0700826 oatMarkTemp(cUnit, rLR); // Add lr to the temp pool
827 oatFreeTemp(cUnit, rLR); // and make it available
buzbee67bf8852011-08-17 17:51:35 -0700828 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
829 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
830 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeec0ecd652011-09-25 18:11:54 -0700831 // The longs may overlap - use intermediate temp if so
832 if (rlResult.lowReg == rlSrc1.highReg) {
buzbeec0ecd652011-09-25 18:11:54 -0700833 int tReg = oatAllocTemp(cUnit);
834 genRegCopy(cUnit, tReg, rlSrc1.highReg);
835 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
836 rlSrc2.lowReg);
837 opRegRegReg(cUnit, secondOp, rlResult.highReg, tReg,
838 rlSrc2.highReg);
839 oatFreeTemp(cUnit, tReg);
840 } else {
841 opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg,
842 rlSrc2.lowReg);
843 opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
844 rlSrc2.highReg);
845 }
buzbee439c4fa2011-08-27 15:59:07 -0700846 /*
847 * NOTE: If rlDest refers to a frame variable in a large frame, the
848 * following storeValueWide might need to allocate a temp register.
849 * To further work around the lack of a spill capability, explicitly
850 * free any temps from rlSrc1 & rlSrc2 that aren't still live in rlResult.
851 * Remove when spill is functional.
852 */
853 freeRegLocTemps(cUnit, rlResult, rlSrc1);
854 freeRegLocTemps(cUnit, rlResult, rlSrc2);
buzbee67bf8852011-08-17 17:51:35 -0700855 storeValueWide(cUnit, rlDest, rlResult);
buzbee9e0f9b02011-08-24 15:32:46 -0700856 oatClobber(cUnit, rLR);
857 oatUnmarkTemp(cUnit, rLR); // Remove lr from the temp pool
buzbee67bf8852011-08-17 17:51:35 -0700858}
859
860void oatInitializeRegAlloc(CompilationUnit* cUnit)
861{
862 int numRegs = sizeof(coreRegs)/sizeof(*coreRegs);
863 int numReserved = sizeof(reservedRegs)/sizeof(*reservedRegs);
864 int numTemps = sizeof(coreTemps)/sizeof(*coreTemps);
865 int numFPRegs = sizeof(fpRegs)/sizeof(*fpRegs);
866 int numFPTemps = sizeof(fpTemps)/sizeof(*fpTemps);
867 RegisterPool *pool = (RegisterPool *)oatNew(sizeof(*pool), true);
868 cUnit->regPool = pool;
869 pool->numCoreRegs = numRegs;
870 pool->coreRegs = (RegisterInfo *)
871 oatNew(numRegs * sizeof(*cUnit->regPool->coreRegs), true);
872 pool->numFPRegs = numFPRegs;
873 pool->FPRegs = (RegisterInfo *)
874 oatNew(numFPRegs * sizeof(*cUnit->regPool->FPRegs), true);
875 oatInitPool(pool->coreRegs, coreRegs, pool->numCoreRegs);
876 oatInitPool(pool->FPRegs, fpRegs, pool->numFPRegs);
877 // Keep special registers from being allocated
878 for (int i = 0; i < numReserved; i++) {
buzbeec0ecd652011-09-25 18:11:54 -0700879 if (NO_SUSPEND && (reservedRegs[i] == rSUSPEND)) {
880 //To measure cost of suspend check
881 continue;
882 }
buzbee67bf8852011-08-17 17:51:35 -0700883 oatMarkInUse(cUnit, reservedRegs[i]);
884 }
885 // Mark temp regs - all others not in use can be used for promotion
886 for (int i = 0; i < numTemps; i++) {
887 oatMarkTemp(cUnit, coreTemps[i]);
888 }
889 for (int i = 0; i < numFPTemps; i++) {
890 oatMarkTemp(cUnit, fpTemps[i]);
891 }
buzbeec0ecd652011-09-25 18:11:54 -0700892 // Construct the alias map.
893 cUnit->phiAliasMap = (int*)oatNew(cUnit->numSSARegs *
894 sizeof(cUnit->phiAliasMap[0]), false);
895 for (int i = 0; i < cUnit->numSSARegs; i++) {
896 cUnit->phiAliasMap[i] = i;
897 }
898 for (MIR* phi = cUnit->phiList; phi; phi = phi->meta.phiNext) {
899 int defReg = phi->ssaRep->defs[0];
900 for (int i = 0; i < phi->ssaRep->numUses; i++) {
901 for (int j = 0; j < cUnit->numSSARegs; j++) {
902 if (cUnit->phiAliasMap[j] == phi->ssaRep->uses[i]) {
903 cUnit->phiAliasMap[j] = defReg;
904 }
905 }
906 }
907 }
buzbee67bf8852011-08-17 17:51:35 -0700908}
909
910/*
911 * Handle simple case (thin lock) inline. If it's complicated, bail
912 * out to the heavyweight lock/unlock routines. We'll use dedicated
913 * registers here in order to be in the right position in case we
914 * to bail to dvm[Lock/Unlock]Object(self, object)
915 *
916 * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
917 * r1 -> object [arg1 for dvm[Lock/Unlock]Object
918 * r2 -> intial contents of object->lock, later result of strex
919 * r3 -> self->threadId
920 * r12 -> allow to be used by utilities as general temp
921 *
922 * The result of the strex is 0 if we acquire the lock.
923 *
924 * See comments in Sync.c for the layout of the lock word.
925 * Of particular interest to this code is the test for the
926 * simple case - which we handle inline. For monitor enter, the
927 * simple case is thin lock, held by no-one. For monitor exit,
928 * the simple case is thin lock, held by the unlocking thread with
929 * a recurse count of 0.
930 *
931 * A minor complication is that there is a field in the lock word
932 * unrelated to locking: the hash state. This field must be ignored, but
933 * preserved.
934 *
935 */
buzbeeed3e9302011-09-23 17:34:19 -0700936STATIC void genMonitorEnter(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700937 RegLocation rlSrc)
938{
939 ArmLIR* target;
940 ArmLIR* hopTarget;
941 ArmLIR* branch;
942 ArmLIR* hopBranch;
943
944 oatFlushAllRegs(cUnit);
Elliott Hughes5f791332011-09-15 17:45:30 -0700945 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -0700946 loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700947 oatLockCallTemps(cUnit); // Prepare for explicit register usage
buzbee5ade1d22011-09-09 14:44:52 -0700948 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
Elliott Hughes54e7df12011-09-16 11:47:04 -0700949 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -0700950 newLIR3(cUnit, kThumb2Ldrex, r2, r1,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700951 Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
buzbeec143c552011-08-20 17:38:58 -0700952 // Align owner
Elliott Hughes5f791332011-09-15 17:45:30 -0700953 opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT);
buzbee67bf8852011-08-17 17:51:35 -0700954 // Is lock unheld on lock or held by us (==threadId) on unlock?
Elliott Hughes5f791332011-09-15 17:45:30 -0700955 newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, LW_LOCK_OWNER_SHIFT - 1);
956 newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
buzbee67bf8852011-08-17 17:51:35 -0700957 hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0);
buzbeec143c552011-08-20 17:38:58 -0700958 newLIR4(cUnit, kThumb2Strex, r2, r3, r1,
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700959 Object::MonitorOffset().Int32Value() >> 2);
buzbee67bf8852011-08-17 17:51:35 -0700960 oatGenMemBarrier(cUnit, kSY);
961 branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
962
963 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
964 hopTarget->defMask = ENCODE_ALL;
965 hopBranch->generic.target = (LIR*)hopTarget;
966
buzbee1b4c8592011-08-31 10:43:51 -0700967 // Go expensive route - artLockObjectFromCode(self, obj);
968 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -0700969 rLR);
970 genRegCopy(cUnit, r0, rSELF);
Ian Rogersff1ed472011-09-20 13:46:24 -0700971 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -0700972
973 // Resume here
974 target = newLIR0(cUnit, kArmPseudoTargetLabel);
975 target->defMask = ENCODE_ALL;
976 branch->generic.target = (LIR*)target;
977}
978
979/*
980 * For monitor unlock, we don't have to use ldrex/strex. Once
981 * we've determined that the lock is thin and that we own it with
982 * a zero recursion count, it's safe to punch it back to the
983 * initial, unlock thin state with a store word.
984 */
buzbeeed3e9302011-09-23 17:34:19 -0700985STATIC void genMonitorExit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -0700986 RegLocation rlSrc)
987{
988 ArmLIR* target;
989 ArmLIR* branch;
990 ArmLIR* hopTarget;
991 ArmLIR* hopBranch;
992
Elliott Hughes5f791332011-09-15 17:45:30 -0700993 DCHECK_EQ(LW_SHAPE_THIN, 0);
buzbee67bf8852011-08-17 17:51:35 -0700994 oatFlushAllRegs(cUnit);
995 loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
buzbee2e748f32011-08-29 21:02:19 -0700996 oatLockCallTemps(cUnit); // Prepare for explicit register usage
buzbee5ade1d22011-09-09 14:44:52 -0700997 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -0700998 loadWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r2); // Get lock
Elliott Hughes54e7df12011-09-16 11:47:04 -0700999 loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r3);
buzbee67bf8852011-08-17 17:51:35 -07001000 // Is lock unheld on lock or held by us (==threadId) on unlock?
Elliott Hughes5f791332011-09-15 17:45:30 -07001001 opRegRegImm(cUnit, kOpAnd, r12, r2, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
buzbeec143c552011-08-20 17:38:58 -07001002 // Align owner
Elliott Hughes5f791332011-09-15 17:45:30 -07001003 opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT);
1004 newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
buzbee67bf8852011-08-17 17:51:35 -07001005 opRegReg(cUnit, kOpSub, r2, r3);
1006 hopBranch = opCondBranch(cUnit, kArmCondNe);
1007 oatGenMemBarrier(cUnit, kSY);
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001008 storeWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r12);
buzbee67bf8852011-08-17 17:51:35 -07001009 branch = opNone(cUnit, kOpUncondBr);
1010
1011 hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
1012 hopTarget->defMask = ENCODE_ALL;
1013 hopBranch->generic.target = (LIR*)hopTarget;
1014
buzbee1b4c8592011-08-31 10:43:51 -07001015 // Go expensive route - UnlockObjectFromCode(self, obj);
1016 loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
buzbee67bf8852011-08-17 17:51:35 -07001017 rLR);
1018 genRegCopy(cUnit, r0, rSELF);
Ian Rogersff1ed472011-09-20 13:46:24 -07001019 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001020
1021 // Resume here
1022 target = newLIR0(cUnit, kArmPseudoTargetLabel);
1023 target->defMask = ENCODE_ALL;
1024 branch->generic.target = (LIR*)target;
1025}
1026
1027/*
1028 * 64-bit 3way compare function.
1029 * mov rX, #-1
1030 * cmp op1hi, op2hi
1031 * blt done
1032 * bgt flip
1033 * sub rX, op1lo, op2lo (treat as unsigned)
1034 * beq done
1035 * ite hi
1036 * mov(hi) rX, #-1
1037 * mov(!hi) rX, #1
1038 * flip:
1039 * neg rX
1040 * done:
1041 */
buzbeeed3e9302011-09-23 17:34:19 -07001042STATIC void genCmpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001043 RegLocation rlDest, RegLocation rlSrc1,
1044 RegLocation rlSrc2)
1045{
buzbee67bf8852011-08-17 17:51:35 -07001046 ArmLIR* target1;
1047 ArmLIR* target2;
1048 rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
1049 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
buzbeeb29e4d12011-09-26 15:05:48 -07001050 int tReg = oatAllocTemp(cUnit);
1051 loadConstant(cUnit, tReg, -1);
buzbee67bf8852011-08-17 17:51:35 -07001052 opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
1053 ArmLIR* branch1 = opCondBranch(cUnit, kArmCondLt);
1054 ArmLIR* branch2 = opCondBranch(cUnit, kArmCondGt);
buzbeeb29e4d12011-09-26 15:05:48 -07001055 opRegRegReg(cUnit, kOpSub, tReg, rlSrc1.lowReg, rlSrc2.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001056 ArmLIR* branch3 = opCondBranch(cUnit, kArmCondEq);
1057
1058 genIT(cUnit, kArmCondHi, "E");
buzbeeb29e4d12011-09-26 15:05:48 -07001059 newLIR2(cUnit, kThumb2MovImmShift, tReg, modifiedImmediate(-1));
1060 loadConstant(cUnit, tReg, 1);
buzbee67bf8852011-08-17 17:51:35 -07001061 genBarrier(cUnit);
1062
1063 target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
1064 target2->defMask = -1;
buzbeeb29e4d12011-09-26 15:05:48 -07001065 opRegReg(cUnit, kOpNeg, tReg, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001066
1067 target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
1068 target1->defMask = -1;
1069
buzbeeb29e4d12011-09-26 15:05:48 -07001070 RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
1071 rlTemp.lowReg = tReg;
buzbee67bf8852011-08-17 17:51:35 -07001072 storeValue(cUnit, rlDest, rlTemp);
buzbeeb29e4d12011-09-26 15:05:48 -07001073 oatFreeTemp(cUnit, tReg);
buzbee67bf8852011-08-17 17:51:35 -07001074
1075 branch1->generic.target = (LIR*)target1;
1076 branch2->generic.target = (LIR*)target2;
1077 branch3->generic.target = branch1->generic.target;
1078}
1079
buzbeeed3e9302011-09-23 17:34:19 -07001080STATIC void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001081 RegLocation rlSrc, RegLocation rlResult, int lit,
1082 int firstBit, int secondBit)
1083{
1084 opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
1085 encodeShift(kArmLsl, secondBit - firstBit));
1086 if (firstBit != 0) {
1087 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
1088 }
1089}
1090
buzbeeed3e9302011-09-23 17:34:19 -07001091STATIC bool genConversionCall(CompilationUnit* cUnit, MIR* mir, int funcOffset,
buzbee67bf8852011-08-17 17:51:35 -07001092 int srcSize, int tgtSize)
1093{
1094 /*
1095 * Don't optimize the register usage since it calls out to support
1096 * functions
1097 */
1098 RegLocation rlSrc;
1099 RegLocation rlDest;
1100 oatFlushAllRegs(cUnit); /* Send everything to home location */
1101 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1102 if (srcSize == 1) {
1103 rlSrc = oatGetSrc(cUnit, mir, 0);
1104 loadValueDirectFixed(cUnit, rlSrc, r0);
1105 } else {
1106 rlSrc = oatGetSrcWide(cUnit, mir, 0, 1);
1107 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
1108 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001109 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001110 if (tgtSize == 1) {
1111 RegLocation rlResult;
1112 rlDest = oatGetDest(cUnit, mir, 0);
1113 rlResult = oatGetReturn(cUnit);
1114 storeValue(cUnit, rlDest, rlResult);
1115 } else {
1116 RegLocation rlResult;
1117 rlDest = oatGetDestWide(cUnit, mir, 0, 1);
1118 rlResult = oatGetReturnWide(cUnit);
1119 storeValueWide(cUnit, rlDest, rlResult);
1120 }
1121 return false;
1122}
1123
buzbeeed3e9302011-09-23 17:34:19 -07001124STATIC bool genArithOpFloatPortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001125 RegLocation rlDest, RegLocation rlSrc1,
1126 RegLocation rlSrc2)
1127{
1128 RegLocation rlResult;
1129 int funcOffset;
1130
1131 switch (mir->dalvikInsn.opcode) {
1132 case OP_ADD_FLOAT_2ADDR:
1133 case OP_ADD_FLOAT:
1134 funcOffset = OFFSETOF_MEMBER(Thread, pFadd);
1135 break;
1136 case OP_SUB_FLOAT_2ADDR:
1137 case OP_SUB_FLOAT:
1138 funcOffset = OFFSETOF_MEMBER(Thread, pFsub);
1139 break;
1140 case OP_DIV_FLOAT_2ADDR:
1141 case OP_DIV_FLOAT:
1142 funcOffset = OFFSETOF_MEMBER(Thread, pFdiv);
1143 break;
1144 case OP_MUL_FLOAT_2ADDR:
1145 case OP_MUL_FLOAT:
1146 funcOffset = OFFSETOF_MEMBER(Thread, pFmul);
1147 break;
1148 case OP_REM_FLOAT_2ADDR:
1149 case OP_REM_FLOAT:
1150 funcOffset = OFFSETOF_MEMBER(Thread, pFmodf);
1151 break;
1152 case OP_NEG_FLOAT: {
1153 genNegFloat(cUnit, rlDest, rlSrc1);
1154 return false;
1155 }
1156 default:
1157 return true;
1158 }
1159 oatFlushAllRegs(cUnit); /* Send everything to home location */
1160 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1161 loadValueDirectFixed(cUnit, rlSrc1, r0);
1162 loadValueDirectFixed(cUnit, rlSrc2, r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001163 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001164 rlResult = oatGetReturn(cUnit);
1165 storeValue(cUnit, rlDest, rlResult);
1166 return false;
1167}
1168
buzbeeed3e9302011-09-23 17:34:19 -07001169STATIC bool genArithOpDoublePortable(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001170 RegLocation rlDest, RegLocation rlSrc1,
1171 RegLocation rlSrc2)
1172{
1173 RegLocation rlResult;
1174 int funcOffset;
1175
1176 switch (mir->dalvikInsn.opcode) {
1177 case OP_ADD_DOUBLE_2ADDR:
1178 case OP_ADD_DOUBLE:
1179 funcOffset = OFFSETOF_MEMBER(Thread, pDadd);
1180 break;
1181 case OP_SUB_DOUBLE_2ADDR:
1182 case OP_SUB_DOUBLE:
1183 funcOffset = OFFSETOF_MEMBER(Thread, pDsub);
1184 break;
1185 case OP_DIV_DOUBLE_2ADDR:
1186 case OP_DIV_DOUBLE:
1187 funcOffset = OFFSETOF_MEMBER(Thread, pDdiv);
1188 break;
1189 case OP_MUL_DOUBLE_2ADDR:
1190 case OP_MUL_DOUBLE:
1191 funcOffset = OFFSETOF_MEMBER(Thread, pDmul);
1192 break;
1193 case OP_REM_DOUBLE_2ADDR:
1194 case OP_REM_DOUBLE:
1195 funcOffset = OFFSETOF_MEMBER(Thread, pFmod);
1196 break;
1197 case OP_NEG_DOUBLE: {
1198 genNegDouble(cUnit, rlDest, rlSrc1);
1199 return false;
1200 }
1201 default:
1202 return true;
1203 }
1204 oatFlushAllRegs(cUnit); /* Send everything to home location */
1205 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1206 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1207 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
Ian Rogersff1ed472011-09-20 13:46:24 -07001208 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001209 rlResult = oatGetReturnWide(cUnit);
1210 storeValueWide(cUnit, rlDest, rlResult);
1211 return false;
1212}
1213
buzbeeed3e9302011-09-23 17:34:19 -07001214STATIC bool genConversionPortable(CompilationUnit* cUnit, MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -07001215{
1216 Opcode opcode = mir->dalvikInsn.opcode;
1217
1218 switch (opcode) {
1219 case OP_INT_TO_FLOAT:
1220 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2f),
1221 1, 1);
1222 case OP_FLOAT_TO_INT:
1223 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2iz),
1224 1, 1);
1225 case OP_DOUBLE_TO_FLOAT:
1226 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2f),
1227 2, 1);
1228 case OP_FLOAT_TO_DOUBLE:
1229 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pF2d),
1230 1, 2);
1231 case OP_INT_TO_DOUBLE:
1232 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pI2d),
1233 1, 2);
1234 case OP_DOUBLE_TO_INT:
1235 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pD2iz),
1236 2, 1);
1237 case OP_FLOAT_TO_LONG:
1238 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001239 pF2l), 1, 2);
buzbee67bf8852011-08-17 17:51:35 -07001240 case OP_LONG_TO_FLOAT:
1241 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2f),
1242 2, 1);
1243 case OP_DOUBLE_TO_LONG:
1244 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread,
buzbee1b4c8592011-08-31 10:43:51 -07001245 pD2l), 2, 2);
buzbee67bf8852011-08-17 17:51:35 -07001246 case OP_LONG_TO_DOUBLE:
1247 return genConversionCall(cUnit, mir, OFFSETOF_MEMBER(Thread, pL2d),
1248 2, 2);
1249 default:
1250 return true;
1251 }
1252 return false;
1253}
1254
1255/* Generate conditional branch instructions */
buzbeeed3e9302011-09-23 17:34:19 -07001256STATIC ArmLIR* genConditionalBranch(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001257 ArmConditionCode cond,
1258 ArmLIR* target)
1259{
1260 ArmLIR* branch = opCondBranch(cUnit, cond);
1261 branch->generic.target = (LIR*) target;
1262 return branch;
1263}
1264
buzbee67bf8852011-08-17 17:51:35 -07001265/*
1266 * Generate array store
1267 *
1268 */
buzbeeed3e9302011-09-23 17:34:19 -07001269STATIC void genArrayObjPut(CompilationUnit* cUnit, MIR* mir,
buzbee1b4c8592011-08-31 10:43:51 -07001270 RegLocation rlArray, RegLocation rlIndex,
1271 RegLocation rlSrc, int scale)
buzbee67bf8852011-08-17 17:51:35 -07001272{
1273 RegisterClass regClass = oatRegClassBySize(kWord);
buzbeec143c552011-08-20 17:38:58 -07001274 int lenOffset = Array::LengthOffset().Int32Value();
1275 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001276
buzbee6181f792011-09-29 11:14:04 -07001277 oatFlushAllRegs(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001278 /* Make sure it's a legal object Put. Use direct regs at first */
1279 loadValueDirectFixed(cUnit, rlArray, r1);
1280 loadValueDirectFixed(cUnit, rlSrc, r0);
1281
1282 /* null array object? */
buzbee43a36422011-09-14 14:00:13 -07001283 genNullCheck(cUnit, rlArray.sRegLow, r1, mir);
buzbee67bf8852011-08-17 17:51:35 -07001284 loadWordDisp(cUnit, rSELF,
buzbee1b4c8592011-08-31 10:43:51 -07001285 OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
buzbee67bf8852011-08-17 17:51:35 -07001286 /* Get the array's clazz */
Ian Rogers0cfe1fb2011-08-26 03:29:44 -07001287 loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), r1);
Ian Rogersff1ed472011-09-20 13:46:24 -07001288 callRuntimeHelper(cUnit, rLR);
buzbee6181f792011-09-29 11:14:04 -07001289 oatFreeTemp(cUnit, r0);
1290 oatFreeTemp(cUnit, r1);
buzbee67bf8852011-08-17 17:51:35 -07001291
1292 // Now, redo loadValues in case they didn't survive the call
1293
1294 int regPtr;
1295 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1296 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1297
1298 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1299 oatClobber(cUnit, rlArray.lowReg);
1300 regPtr = rlArray.lowReg;
1301 } else {
1302 regPtr = oatAllocTemp(cUnit);
1303 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1304 }
1305
buzbee43a36422011-09-14 14:00:13 -07001306 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001307 int regLen = oatAllocTemp(cUnit);
1308 //NOTE: max live temps(4) here.
1309 /* Get len */
1310 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1311 /* regPtr -> array data */
1312 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001313 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001314 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001315 oatFreeTemp(cUnit, regLen);
1316 } else {
1317 /* regPtr -> array data */
1318 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1319 }
1320 /* at this point, regPtr points to array, 2 live temps */
1321 rlSrc = loadValue(cUnit, rlSrc, regClass);
1322 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1323 scale, kWord);
1324}
1325
1326/*
1327 * Generate array load
1328 */
buzbeeed3e9302011-09-23 17:34:19 -07001329STATIC void genArrayGet(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001330 RegLocation rlArray, RegLocation rlIndex,
1331 RegLocation rlDest, int scale)
1332{
1333 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001334 int lenOffset = Array::LengthOffset().Int32Value();
1335 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001336 RegLocation rlResult;
1337 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1338 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1339 int regPtr;
1340
1341 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001342 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001343
1344 regPtr = oatAllocTemp(cUnit);
1345
buzbee43a36422011-09-14 14:00:13 -07001346 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001347 int regLen = oatAllocTemp(cUnit);
1348 /* Get len */
1349 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1350 /* regPtr -> array data */
1351 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001352 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001353 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001354 oatFreeTemp(cUnit, regLen);
1355 } else {
1356 /* regPtr -> array data */
1357 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
1358 }
buzbeee9a72f62011-09-04 17:59:07 -07001359 oatFreeTemp(cUnit, rlArray.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001360 if ((size == kLong) || (size == kDouble)) {
1361 if (scale) {
1362 int rNewIndex = oatAllocTemp(cUnit);
1363 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1364 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1365 oatFreeTemp(cUnit, rNewIndex);
1366 } else {
1367 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1368 }
buzbeee9a72f62011-09-04 17:59:07 -07001369 oatFreeTemp(cUnit, rlIndex.lowReg);
buzbee67bf8852011-08-17 17:51:35 -07001370 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1371
1372 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
1373
1374 oatFreeTemp(cUnit, regPtr);
1375 storeValueWide(cUnit, rlDest, rlResult);
1376 } else {
1377 rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
1378
1379 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
1380 scale, size);
1381
1382 oatFreeTemp(cUnit, regPtr);
1383 storeValue(cUnit, rlDest, rlResult);
1384 }
1385}
1386
1387/*
1388 * Generate array store
1389 *
1390 */
buzbeeed3e9302011-09-23 17:34:19 -07001391STATIC void genArrayPut(CompilationUnit* cUnit, MIR* mir, OpSize size,
buzbee67bf8852011-08-17 17:51:35 -07001392 RegLocation rlArray, RegLocation rlIndex,
1393 RegLocation rlSrc, int scale)
1394{
1395 RegisterClass regClass = oatRegClassBySize(size);
buzbeec143c552011-08-20 17:38:58 -07001396 int lenOffset = Array::LengthOffset().Int32Value();
1397 int dataOffset = Array::DataOffset().Int32Value();
buzbee67bf8852011-08-17 17:51:35 -07001398
1399 int regPtr;
1400 rlArray = loadValue(cUnit, rlArray, kCoreReg);
1401 rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
1402
1403 if (oatIsTemp(cUnit, rlArray.lowReg)) {
1404 oatClobber(cUnit, rlArray.lowReg);
1405 regPtr = rlArray.lowReg;
1406 } else {
1407 regPtr = oatAllocTemp(cUnit);
1408 genRegCopy(cUnit, regPtr, rlArray.lowReg);
1409 }
1410
1411 /* null object? */
buzbee43a36422011-09-14 14:00:13 -07001412 genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
buzbee67bf8852011-08-17 17:51:35 -07001413
buzbee43a36422011-09-14 14:00:13 -07001414 if (!(mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
buzbee67bf8852011-08-17 17:51:35 -07001415 int regLen = oatAllocTemp(cUnit);
1416 //NOTE: max live temps(4) here.
1417 /* Get len */
1418 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
1419 /* regPtr -> array data */
1420 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
buzbeeec5adf32011-09-11 15:25:43 -07001421 genRegRegCheck(cUnit, kArmCondCs, rlIndex.lowReg, regLen, mir,
buzbee5ade1d22011-09-09 14:44:52 -07001422 kArmThrowArrayBounds);
buzbee67bf8852011-08-17 17:51:35 -07001423 oatFreeTemp(cUnit, regLen);
1424 } else {
1425 /* regPtr -> array data */
1426 opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
1427 }
1428 /* at this point, regPtr points to array, 2 live temps */
1429 if ((size == kLong) || (size == kDouble)) {
buzbee5ade1d22011-09-09 14:44:52 -07001430 //TUNING: specific wide routine that can handle fp regs
buzbee67bf8852011-08-17 17:51:35 -07001431 if (scale) {
1432 int rNewIndex = oatAllocTemp(cUnit);
1433 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
1434 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
1435 oatFreeTemp(cUnit, rNewIndex);
1436 } else {
1437 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
1438 }
1439 rlSrc = loadValueWide(cUnit, rlSrc, regClass);
1440
1441 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
1442
1443 oatFreeTemp(cUnit, regPtr);
1444 } else {
1445 rlSrc = loadValue(cUnit, rlSrc, regClass);
1446
1447 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
1448 scale, size);
1449 }
1450}
1451
buzbeeed3e9302011-09-23 17:34:19 -07001452STATIC bool genShiftOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001453 RegLocation rlDest, RegLocation rlSrc1,
1454 RegLocation rlShift)
1455{
buzbee54330722011-08-23 16:46:55 -07001456 int funcOffset;
buzbee67bf8852011-08-17 17:51:35 -07001457
buzbee67bf8852011-08-17 17:51:35 -07001458 switch( mir->dalvikInsn.opcode) {
1459 case OP_SHL_LONG:
1460 case OP_SHL_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001461 funcOffset = OFFSETOF_MEMBER(Thread, pShlLong);
buzbee67bf8852011-08-17 17:51:35 -07001462 break;
1463 case OP_SHR_LONG:
1464 case OP_SHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001465 funcOffset = OFFSETOF_MEMBER(Thread, pShrLong);
buzbee67bf8852011-08-17 17:51:35 -07001466 break;
1467 case OP_USHR_LONG:
1468 case OP_USHR_LONG_2ADDR:
buzbee54330722011-08-23 16:46:55 -07001469 funcOffset = OFFSETOF_MEMBER(Thread, pUshrLong);
buzbee67bf8852011-08-17 17:51:35 -07001470 break;
1471 default:
buzbee54330722011-08-23 16:46:55 -07001472 LOG(FATAL) << "Unexpected case";
buzbee67bf8852011-08-17 17:51:35 -07001473 return true;
1474 }
buzbee54330722011-08-23 16:46:55 -07001475 oatFlushAllRegs(cUnit); /* Send everything to home location */
1476 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1477 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1478 loadValueDirect(cUnit, rlShift, r2);
Ian Rogersff1ed472011-09-20 13:46:24 -07001479 callRuntimeHelper(cUnit, rLR);
buzbee54330722011-08-23 16:46:55 -07001480 RegLocation rlResult = oatGetReturnWide(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001481 storeValueWide(cUnit, rlDest, rlResult);
1482 return false;
1483}
1484
buzbeeed3e9302011-09-23 17:34:19 -07001485STATIC bool genArithOpLong(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001486 RegLocation rlDest, RegLocation rlSrc1,
1487 RegLocation rlSrc2)
1488{
1489 RegLocation rlResult;
1490 OpKind firstOp = kOpBkpt;
1491 OpKind secondOp = kOpBkpt;
1492 bool callOut = false;
1493 int funcOffset;
1494 int retReg = r0;
1495
1496 switch (mir->dalvikInsn.opcode) {
1497 case OP_NOT_LONG:
1498 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1499 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001500 // Check for destructive overlap
1501 if (rlResult.lowReg == rlSrc2.highReg) {
1502 int tReg = oatAllocTemp(cUnit);
1503 genRegCopy(cUnit, tReg, rlSrc2.highReg);
1504 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1505 opRegReg(cUnit, kOpMvn, rlResult.highReg, tReg);
1506 oatFreeTemp(cUnit, tReg);
1507 } else {
1508 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
1509 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
1510 }
buzbee67bf8852011-08-17 17:51:35 -07001511 storeValueWide(cUnit, rlDest, rlResult);
1512 return false;
1513 break;
1514 case OP_ADD_LONG:
1515 case OP_ADD_LONG_2ADDR:
1516 firstOp = kOpAdd;
1517 secondOp = kOpAdc;
1518 break;
1519 case OP_SUB_LONG:
1520 case OP_SUB_LONG_2ADDR:
1521 firstOp = kOpSub;
1522 secondOp = kOpSbc;
1523 break;
1524 case OP_MUL_LONG:
1525 case OP_MUL_LONG_2ADDR:
buzbee439c4fa2011-08-27 15:59:07 -07001526 callOut = true;
1527 retReg = r0;
1528 funcOffset = OFFSETOF_MEMBER(Thread, pLmul);
1529 break;
buzbee67bf8852011-08-17 17:51:35 -07001530 case OP_DIV_LONG:
1531 case OP_DIV_LONG_2ADDR:
1532 callOut = true;
1533 retReg = r0;
1534 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1535 break;
1536 /* NOTE - result is in r2/r3 instead of r0/r1 */
1537 case OP_REM_LONG:
1538 case OP_REM_LONG_2ADDR:
1539 callOut = true;
1540 funcOffset = OFFSETOF_MEMBER(Thread, pLdivmod);
1541 retReg = r2;
1542 break;
1543 case OP_AND_LONG_2ADDR:
1544 case OP_AND_LONG:
1545 firstOp = kOpAnd;
1546 secondOp = kOpAnd;
1547 break;
1548 case OP_OR_LONG:
1549 case OP_OR_LONG_2ADDR:
1550 firstOp = kOpOr;
1551 secondOp = kOpOr;
1552 break;
1553 case OP_XOR_LONG:
1554 case OP_XOR_LONG_2ADDR:
1555 firstOp = kOpXor;
1556 secondOp = kOpXor;
1557 break;
1558 case OP_NEG_LONG: {
buzbee67bf8852011-08-17 17:51:35 -07001559 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
1560 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
buzbeeb29e4d12011-09-26 15:05:48 -07001561 int zReg = oatAllocTemp(cUnit);
1562 loadConstantNoClobber(cUnit, zReg, 0);
1563 // Check for destructive overlap
1564 if (rlResult.lowReg == rlSrc2.highReg) {
1565 int tReg = oatAllocTemp(cUnit);
1566 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1567 zReg, rlSrc2.lowReg);
1568 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1569 zReg, tReg);
1570 oatFreeTemp(cUnit, tReg);
1571 } else {
1572 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1573 zReg, rlSrc2.lowReg);
1574 opRegRegReg(cUnit, kOpSbc, rlResult.highReg,
1575 zReg, rlSrc2.highReg);
1576 }
1577 oatFreeTemp(cUnit, zReg);
buzbee67bf8852011-08-17 17:51:35 -07001578 storeValueWide(cUnit, rlDest, rlResult);
1579 return false;
1580 }
1581 default:
1582 LOG(FATAL) << "Invalid long arith op";
1583 }
1584 if (!callOut) {
1585 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
1586 } else {
1587 // Adjust return regs in to handle case of rem returning r2/r3
1588 oatFlushAllRegs(cUnit); /* Send everything to home location */
1589 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1590 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
1591 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
Ian Rogersff1ed472011-09-20 13:46:24 -07001592 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001593 if (retReg == r0)
1594 rlResult = oatGetReturnWide(cUnit);
1595 else
1596 rlResult = oatGetReturnWideAlt(cUnit);
1597 storeValueWide(cUnit, rlDest, rlResult);
1598 }
1599 return false;
1600}
1601
buzbeeed3e9302011-09-23 17:34:19 -07001602STATIC bool genArithOpInt(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001603 RegLocation rlDest, RegLocation rlSrc1,
1604 RegLocation rlSrc2)
1605{
1606 OpKind op = kOpBkpt;
1607 bool callOut = false;
1608 bool checkZero = false;
1609 bool unary = false;
1610 int retReg = r0;
1611 int funcOffset;
1612 RegLocation rlResult;
1613 bool shiftOp = false;
1614
1615 switch (mir->dalvikInsn.opcode) {
1616 case OP_NEG_INT:
1617 op = kOpNeg;
1618 unary = true;
1619 break;
1620 case OP_NOT_INT:
1621 op = kOpMvn;
1622 unary = true;
1623 break;
1624 case OP_ADD_INT:
1625 case OP_ADD_INT_2ADDR:
1626 op = kOpAdd;
1627 break;
1628 case OP_SUB_INT:
1629 case OP_SUB_INT_2ADDR:
1630 op = kOpSub;
1631 break;
1632 case OP_MUL_INT:
1633 case OP_MUL_INT_2ADDR:
1634 op = kOpMul;
1635 break;
1636 case OP_DIV_INT:
1637 case OP_DIV_INT_2ADDR:
1638 callOut = true;
1639 checkZero = true;
1640 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1641 retReg = r0;
1642 break;
1643 /* NOTE: returns in r1 */
1644 case OP_REM_INT:
1645 case OP_REM_INT_2ADDR:
1646 callOut = true;
1647 checkZero = true;
1648 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1649 retReg = r1;
1650 break;
1651 case OP_AND_INT:
1652 case OP_AND_INT_2ADDR:
1653 op = kOpAnd;
1654 break;
1655 case OP_OR_INT:
1656 case OP_OR_INT_2ADDR:
1657 op = kOpOr;
1658 break;
1659 case OP_XOR_INT:
1660 case OP_XOR_INT_2ADDR:
1661 op = kOpXor;
1662 break;
1663 case OP_SHL_INT:
1664 case OP_SHL_INT_2ADDR:
1665 shiftOp = true;
1666 op = kOpLsl;
1667 break;
1668 case OP_SHR_INT:
1669 case OP_SHR_INT_2ADDR:
1670 shiftOp = true;
1671 op = kOpAsr;
1672 break;
1673 case OP_USHR_INT:
1674 case OP_USHR_INT_2ADDR:
1675 shiftOp = true;
1676 op = kOpLsr;
1677 break;
1678 default:
1679 LOG(FATAL) << "Invalid word arith op: " <<
1680 (int)mir->dalvikInsn.opcode;
1681 }
1682 if (!callOut) {
1683 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
1684 if (unary) {
1685 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1686 opRegReg(cUnit, op, rlResult.lowReg,
1687 rlSrc1.lowReg);
1688 } else {
1689 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
1690 if (shiftOp) {
1691 int tReg = oatAllocTemp(cUnit);
1692 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
1693 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1694 opRegRegReg(cUnit, op, rlResult.lowReg,
1695 rlSrc1.lowReg, tReg);
1696 oatFreeTemp(cUnit, tReg);
1697 } else {
1698 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1699 opRegRegReg(cUnit, op, rlResult.lowReg,
1700 rlSrc1.lowReg, rlSrc2.lowReg);
1701 }
1702 }
1703 storeValue(cUnit, rlDest, rlResult);
1704 } else {
1705 RegLocation rlResult;
1706 oatFlushAllRegs(cUnit); /* Send everything to home location */
1707 loadValueDirectFixed(cUnit, rlSrc2, r1);
1708 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
1709 loadValueDirectFixed(cUnit, rlSrc1, r0);
1710 if (checkZero) {
buzbee5ade1d22011-09-09 14:44:52 -07001711 genImmedCheck(cUnit, kArmCondEq, r1, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001712 }
Ian Rogersff1ed472011-09-20 13:46:24 -07001713 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07001714 if (retReg == r0)
1715 rlResult = oatGetReturn(cUnit);
1716 else
1717 rlResult = oatGetReturnAlt(cUnit);
1718 storeValue(cUnit, rlDest, rlResult);
1719 }
1720 return false;
1721}
1722
buzbeec1f45042011-09-21 16:03:19 -07001723/* Check if we need to check for pending suspend request */
buzbeeed3e9302011-09-23 17:34:19 -07001724STATIC void genSuspendTest(CompilationUnit* cUnit, MIR* mir)
buzbeec1f45042011-09-21 16:03:19 -07001725{
buzbeec0ecd652011-09-25 18:11:54 -07001726 if (NO_SUSPEND || mir->optimizationFlags & MIR_IGNORE_SUSPEND_CHECK) {
buzbeec1f45042011-09-21 16:03:19 -07001727 return;
1728 }
buzbee6181f792011-09-29 11:14:04 -07001729 oatFlushAllRegs(cUnit);
buzbeec1f45042011-09-21 16:03:19 -07001730 newLIR2(cUnit, kThumbSubRI8, rSUSPEND, 1);
1731 ArmLIR* branch = opCondBranch(cUnit, kArmCondEq);
1732 ArmLIR* retLab = newLIR0(cUnit, kArmPseudoTargetLabel);
1733 retLab->defMask = ENCODE_ALL;
1734 ArmLIR* target = (ArmLIR*)oatNew(sizeof(ArmLIR), true);
1735 target->generic.dalvikOffset = cUnit->currentDalvikOffset;
1736 target->opcode = kArmPseudoSuspendTarget;
1737 target->operands[0] = (intptr_t)retLab;
1738 target->operands[1] = mir->offset;
1739 branch->generic.target = (LIR*)target;
1740 oatInsertGrowableList(&cUnit->suspendLaunchpads, (intptr_t)target);
1741}
1742
buzbee0d966cf2011-09-08 17:34:58 -07001743/* Check for pending suspend request. */
buzbeeed3e9302011-09-23 17:34:19 -07001744STATIC void genSuspendPoll(CompilationUnit* cUnit, MIR* mir)
buzbee67bf8852011-08-17 17:51:35 -07001745{
buzbeec0ecd652011-09-25 18:11:54 -07001746 if (NO_SUSPEND || mir->optimizationFlags & MIR_IGNORE_SUSPEND_CHECK) {
buzbeec1f45042011-09-21 16:03:19 -07001747 return;
1748 }
buzbee6181f792011-09-29 11:14:04 -07001749 oatFlushAllRegs(cUnit);
buzbee0d966cf2011-09-08 17:34:58 -07001750 oatLockCallTemps(cUnit); // Explicit register usage
1751 int rSuspendCount = r1;
buzbee67bf8852011-08-17 17:51:35 -07001752 ArmLIR* ld;
buzbee0d966cf2011-09-08 17:34:58 -07001753 ld = loadWordDisp(cUnit, rSELF,
1754 art::Thread::SuspendCountOffset().Int32Value(), rSuspendCount);
buzbee67bf8852011-08-17 17:51:35 -07001755 setMemRefType(ld, true /* isLoad */, kMustNotAlias);
buzbee0d966cf2011-09-08 17:34:58 -07001756 loadWordDisp(cUnit, rSELF,
1757 OFFSETOF_MEMBER(Thread, pCheckSuspendFromCode), rLR);
1758 genRegCopy(cUnit, r0, rSELF);
1759 opRegImm(cUnit, kOpCmp, rSuspendCount, 0);
buzbeeb0ebba02011-09-17 10:52:59 -07001760 /*
1761 * FIXME: for efficiency we should use an if-converted suspend
1762 * test here. However, support for IT is a bit weak at the
1763 * moment, and requires knowledge of the exact number of instructions
1764 * to fall in the skip shadow. While the exception mechanism
1765 * remains in flux, use a compare and branch sequence. Once
1766 * things firm up, restore the conditional skip (and perhaps
1767 * fix the utility to handle variable-sized shadows).
1768 */
1769#if 0
buzbee0d966cf2011-09-08 17:34:58 -07001770 genIT(cUnit, kArmCondNe, "");
buzbeeec5adf32011-09-11 15:25:43 -07001771 callUnwindableHelper(cUnit, rLR); // CheckSuspendFromCode(self)
buzbeeb0ebba02011-09-17 10:52:59 -07001772#else
1773 ArmLIR* branch = opCondBranch(cUnit, kArmCondEq);
Ian Rogersff1ed472011-09-20 13:46:24 -07001774 callRuntimeHelper(cUnit, rLR); // CheckSuspendFromCode(self)
buzbeeb0ebba02011-09-17 10:52:59 -07001775 ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
1776 target->defMask = ENCODE_ALL;
1777 branch->generic.target = (LIR*)target;
1778#endif
buzbee0d966cf2011-09-08 17:34:58 -07001779 oatFreeCallTemps(cUnit);
buzbee67bf8852011-08-17 17:51:35 -07001780}
1781
1782/*
1783 * The following are the first-level codegen routines that analyze the format
1784 * of each bytecode then either dispatch special purpose codegen routines
1785 * or produce corresponding Thumb instructions directly.
1786 */
1787
buzbeeed3e9302011-09-23 17:34:19 -07001788STATIC bool isPowerOfTwo(int x)
buzbee67bf8852011-08-17 17:51:35 -07001789{
1790 return (x & (x - 1)) == 0;
1791}
1792
1793// Returns true if no more than two bits are set in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001794STATIC bool isPopCountLE2(unsigned int x)
buzbee67bf8852011-08-17 17:51:35 -07001795{
1796 x &= x - 1;
1797 return (x & (x - 1)) == 0;
1798}
1799
1800// Returns the index of the lowest set bit in 'x'.
buzbeeed3e9302011-09-23 17:34:19 -07001801STATIC int lowestSetBit(unsigned int x) {
buzbee67bf8852011-08-17 17:51:35 -07001802 int bit_posn = 0;
1803 while ((x & 0xf) == 0) {
1804 bit_posn += 4;
1805 x >>= 4;
1806 }
1807 while ((x & 1) == 0) {
1808 bit_posn++;
1809 x >>= 1;
1810 }
1811 return bit_posn;
1812}
1813
1814// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
1815// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001816STATIC bool handleEasyDivide(CompilationUnit* cUnit, Opcode dalvikOpcode,
buzbee67bf8852011-08-17 17:51:35 -07001817 RegLocation rlSrc, RegLocation rlDest, int lit)
1818{
1819 if (lit < 2 || !isPowerOfTwo(lit)) {
1820 return false;
1821 }
1822 int k = lowestSetBit(lit);
1823 if (k >= 30) {
1824 // Avoid special cases.
1825 return false;
1826 }
1827 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 ||
1828 dalvikOpcode == OP_DIV_INT_LIT16);
1829 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1830 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1831 if (div) {
1832 int tReg = oatAllocTemp(cUnit);
1833 if (lit == 2) {
1834 // Division by 2 is by far the most common division by constant.
1835 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
1836 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1837 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1838 } else {
1839 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
1840 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
1841 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
1842 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
1843 }
1844 } else {
1845 int cReg = oatAllocTemp(cUnit);
1846 loadConstant(cUnit, cReg, lit - 1);
1847 int tReg1 = oatAllocTemp(cUnit);
1848 int tReg2 = oatAllocTemp(cUnit);
1849 if (lit == 2) {
1850 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
1851 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1852 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1853 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1854 } else {
1855 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
1856 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
1857 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
1858 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
1859 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
1860 }
1861 }
1862 storeValue(cUnit, rlDest, rlResult);
1863 return true;
1864}
1865
1866// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
1867// and store the result in 'rlDest'.
buzbeeed3e9302011-09-23 17:34:19 -07001868STATIC bool handleEasyMultiply(CompilationUnit* cUnit,
buzbee67bf8852011-08-17 17:51:35 -07001869 RegLocation rlSrc, RegLocation rlDest, int lit)
1870{
1871 // Can we simplify this multiplication?
1872 bool powerOfTwo = false;
1873 bool popCountLE2 = false;
1874 bool powerOfTwoMinusOne = false;
1875 if (lit < 2) {
1876 // Avoid special cases.
1877 return false;
1878 } else if (isPowerOfTwo(lit)) {
1879 powerOfTwo = true;
1880 } else if (isPopCountLE2(lit)) {
1881 popCountLE2 = true;
1882 } else if (isPowerOfTwo(lit + 1)) {
1883 powerOfTwoMinusOne = true;
1884 } else {
1885 return false;
1886 }
1887 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1888 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1889 if (powerOfTwo) {
1890 // Shift.
1891 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
1892 lowestSetBit(lit));
1893 } else if (popCountLE2) {
1894 // Shift and add and shift.
1895 int firstBit = lowestSetBit(lit);
1896 int secondBit = lowestSetBit(lit ^ (1 << firstBit));
1897 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
1898 firstBit, secondBit);
1899 } else {
1900 // Reverse subtract: (src << (shift + 1)) - src.
buzbeeed3e9302011-09-23 17:34:19 -07001901 DCHECK(powerOfTwoMinusOne);
buzbee5ade1d22011-09-09 14:44:52 -07001902 // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1)
buzbee67bf8852011-08-17 17:51:35 -07001903 int tReg = oatAllocTemp(cUnit);
1904 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
1905 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
1906 }
1907 storeValue(cUnit, rlDest, rlResult);
1908 return true;
1909}
1910
buzbeeed3e9302011-09-23 17:34:19 -07001911STATIC bool genArithOpIntLit(CompilationUnit* cUnit, MIR* mir,
buzbee67bf8852011-08-17 17:51:35 -07001912 RegLocation rlDest, RegLocation rlSrc,
1913 int lit)
1914{
1915 Opcode dalvikOpcode = mir->dalvikInsn.opcode;
1916 RegLocation rlResult;
1917 OpKind op = (OpKind)0; /* Make gcc happy */
1918 int shiftOp = false;
1919 bool isDiv = false;
1920 int funcOffset;
1921
1922 switch (dalvikOpcode) {
1923 case OP_RSUB_INT_LIT8:
1924 case OP_RSUB_INT: {
1925 int tReg;
1926 //TUNING: add support for use of Arm rsub op
1927 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
1928 tReg = oatAllocTemp(cUnit);
1929 loadConstant(cUnit, tReg, lit);
1930 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
1931 opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
1932 tReg, rlSrc.lowReg);
1933 storeValue(cUnit, rlDest, rlResult);
1934 return false;
1935 break;
1936 }
1937
1938 case OP_ADD_INT_LIT8:
1939 case OP_ADD_INT_LIT16:
1940 op = kOpAdd;
1941 break;
1942 case OP_MUL_INT_LIT8:
1943 case OP_MUL_INT_LIT16: {
1944 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
1945 return false;
1946 }
1947 op = kOpMul;
1948 break;
1949 }
1950 case OP_AND_INT_LIT8:
1951 case OP_AND_INT_LIT16:
1952 op = kOpAnd;
1953 break;
1954 case OP_OR_INT_LIT8:
1955 case OP_OR_INT_LIT16:
1956 op = kOpOr;
1957 break;
1958 case OP_XOR_INT_LIT8:
1959 case OP_XOR_INT_LIT16:
1960 op = kOpXor;
1961 break;
1962 case OP_SHL_INT_LIT8:
1963 lit &= 31;
1964 shiftOp = true;
1965 op = kOpLsl;
1966 break;
1967 case OP_SHR_INT_LIT8:
1968 lit &= 31;
1969 shiftOp = true;
1970 op = kOpAsr;
1971 break;
1972 case OP_USHR_INT_LIT8:
1973 lit &= 31;
1974 shiftOp = true;
1975 op = kOpLsr;
1976 break;
1977
1978 case OP_DIV_INT_LIT8:
1979 case OP_DIV_INT_LIT16:
1980 case OP_REM_INT_LIT8:
1981 case OP_REM_INT_LIT16:
1982 if (lit == 0) {
buzbee5ade1d22011-09-09 14:44:52 -07001983 genImmedCheck(cUnit, kArmCondAl, 0, 0, mir, kArmThrowDivZero);
buzbee67bf8852011-08-17 17:51:35 -07001984 return false;
1985 }
1986 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
1987 return false;
1988 }
1989 oatFlushAllRegs(cUnit); /* Everything to home location */
1990 loadValueDirectFixed(cUnit, rlSrc, r0);
1991 oatClobber(cUnit, r0);
1992 if ((dalvikOpcode == OP_DIV_INT_LIT8) ||
1993 (dalvikOpcode == OP_DIV_INT_LIT16)) {
1994 funcOffset = OFFSETOF_MEMBER(Thread, pIdiv);
1995 isDiv = true;
1996 } else {
1997 funcOffset = OFFSETOF_MEMBER(Thread, pIdivmod);
1998 isDiv = false;
1999 }
2000 loadWordDisp(cUnit, rSELF, funcOffset, rLR);
2001 loadConstant(cUnit, r1, lit);
Ian Rogersff1ed472011-09-20 13:46:24 -07002002 callRuntimeHelper(cUnit, rLR);
buzbee67bf8852011-08-17 17:51:35 -07002003 if (isDiv)
2004 rlResult = oatGetReturn(cUnit);
2005 else
2006 rlResult = oatGetReturnAlt(cUnit);
2007 storeValue(cUnit, rlDest, rlResult);
2008 return false;
2009 break;
2010 default:
2011 return true;
2012 }
2013 rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
2014 rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
2015 // Avoid shifts by literal 0 - no support in Thumb. Change to copy
2016 if (shiftOp && (lit == 0)) {
2017 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
2018 } else {
2019 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
2020 }
2021 storeValue(cUnit, rlDest, rlResult);
2022 return false;
2023}
2024
2025/* Architectural-specific debugging helpers go here */
2026void oatArchDump(void)
2027{
2028 /* Print compiled opcode in this VM instance */
2029 int i, start, streak;
2030 char buf[1024];
2031
2032 streak = i = 0;
2033 buf[0] = 0;
2034 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2035 i++;
2036 }
2037 if (i == kNumPackedOpcodes) {
2038 return;
2039 }
2040 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) {
2041 if (opcodeCoverage[i]) {
2042 streak++;
2043 } else {
2044 if (streak == 1) {
2045 sprintf(buf+strlen(buf), "%x,", start);
2046 } else {
2047 sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
2048 }
2049 streak = 0;
2050 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) {
2051 i++;
2052 }
2053 if (i < kNumPackedOpcodes) {
2054 streak = 1;
2055 start = i;
2056 }
2057 }
2058 }
2059 if (streak) {
2060 if (streak == 1) {
2061 sprintf(buf+strlen(buf), "%x", start);
2062 } else {
2063 sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
2064 }
2065 }
2066 if (strlen(buf)) {
2067 LOG(INFO) << "dalvik.vm.oat.op = " << buf;
2068 }
2069}