blob: 777cf57345143107fdfac8f65e858c36f990789c [file] [log] [blame]
buzbeee3acd072012-02-25 17:03:10 -08001/*
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
17namespace art {
18
buzbee31a4a6f2012-02-28 15:36:15 -080019void setMemRefType(LIR* lir, bool isLoad, int memType)
20{
21 u8 *maskPtr;
22 u8 mask = ENCODE_MEM;;
23 DCHECK(EncodingMap[lir->opcode].flags & (IS_LOAD | IS_STORE));
24 if (isLoad) {
25 maskPtr = &lir->useMask;
26 } else {
27 maskPtr = &lir->defMask;
28 }
29 /* Clear out the memref flags */
30 *maskPtr &= ~mask;
31 /* ..and then add back the one we need */
Elliott Hughesb25c3f62012-03-26 16:35:06 -070032 switch (memType) {
buzbee31a4a6f2012-02-28 15:36:15 -080033 case kLiteral:
34 DCHECK(isLoad);
35 *maskPtr |= ENCODE_LITERAL;
36 break;
37 case kDalvikReg:
38 *maskPtr |= ENCODE_DALVIK_REG;
39 break;
40 case kHeapRef:
41 *maskPtr |= ENCODE_HEAP_REF;
42 break;
43 case kMustNotAlias:
44 /* Currently only loads can be marked as kMustNotAlias */
45 DCHECK(!(EncodingMap[lir->opcode].flags & IS_STORE));
46 *maskPtr |= ENCODE_MUST_NOT_ALIAS;
47 break;
48 default:
49 LOG(FATAL) << "Oat: invalid memref kind - " << memType;
50 }
51}
52
53/*
Ian Rogersb5d09b22012-03-06 22:14:17 -080054 * Mark load/store instructions that access Dalvik registers through the stack.
buzbee31a4a6f2012-02-28 15:36:15 -080055 */
Ian Rogersb5d09b22012-03-06 22:14:17 -080056void annotateDalvikRegAccess(LIR* lir, int regId, bool isLoad, bool is64bit)
buzbee31a4a6f2012-02-28 15:36:15 -080057{
58 setMemRefType(lir, isLoad, kDalvikReg);
59
60 /*
Ian Rogersb5d09b22012-03-06 22:14:17 -080061 * Store the Dalvik register id in aliasInfo. Mark the MSB if it is a 64-bit
buzbee31a4a6f2012-02-28 15:36:15 -080062 * access.
63 */
64 lir->aliasInfo = regId;
Ian Rogersb5d09b22012-03-06 22:14:17 -080065 if (is64bit) {
buzbee31a4a6f2012-02-28 15:36:15 -080066 lir->aliasInfo |= 0x80000000;
67 }
68}
69
70/*
71 * Decode the register id.
72 */
73inline u8 getRegMaskCommon(int reg)
74{
75 u8 seed;
76 int shift;
77 int regId = reg & 0x1f;
78
79 /*
80 * Each double register is equal to a pair of single-precision FP registers
81 */
82 seed = DOUBLEREG(reg) ? 3 : 1;
83 /* FP register starts at bit position 16 */
84 shift = FPREG(reg) ? kFPReg0 : 0;
85 /* Expand the double register id into single offset */
86 shift += regId;
87 return (seed << shift);
88}
89
90/*
91 * Mark the corresponding bit(s).
92 */
93inline void setupRegMask(u8* mask, int reg)
94{
95 *mask |= getRegMaskCommon(reg);
96}
97
98/*
99 * Set up the proper fields in the resource mask
100 */
101void setupResourceMasks(LIR* lir)
102{
103 int opcode = lir->opcode;
104 int flags;
105
106 if (opcode <= 0) {
107 lir->useMask = lir->defMask = 0;
108 return;
109 }
110
111 flags = EncodingMap[lir->opcode].flags;
112
113 if (flags & NEEDS_FIXUP) {
114 lir->flags.pcRelFixup = true;
115 }
116
buzbeee88dfbf2012-03-05 11:19:57 -0800117 /* Get the starting size of the instruction's template */
118 lir->flags.size = oatGetInsnSize(lir);
119
buzbee31a4a6f2012-02-28 15:36:15 -0800120 /* Set up the mask for resources that are updated */
121 if (flags & (IS_LOAD | IS_STORE)) {
122 /* Default to heap - will catch specialized classes later */
123 setMemRefType(lir, flags & IS_LOAD, kHeapRef);
124 }
125
126 /*
127 * Conservatively assume the branch here will call out a function that in
128 * turn will trash everything.
129 */
130 if (flags & IS_BRANCH) {
131 lir->defMask = lir->useMask = ENCODE_ALL;
132 return;
133 }
134
135 if (flags & REG_DEF0) {
136 setupRegMask(&lir->defMask, lir->operands[0]);
137 }
138
139 if (flags & REG_DEF1) {
140 setupRegMask(&lir->defMask, lir->operands[1]);
141 }
142
143 if (flags & REG_DEF_SP) {
144 lir->defMask |= ENCODE_REG_SP;
145 }
146
buzbeea7678db2012-03-05 15:35:46 -0800147#if !defined(TARGET_X86)
buzbee31a4a6f2012-02-28 15:36:15 -0800148 if (flags & REG_DEF_LR) {
149 lir->defMask |= ENCODE_REG_LR;
150 }
buzbeea7678db2012-03-05 15:35:46 -0800151#endif
buzbee31a4a6f2012-02-28 15:36:15 -0800152
153 if (flags & REG_DEF_LIST0) {
154 lir->defMask |= ENCODE_REG_LIST(lir->operands[0]);
155 }
156
157 if (flags & REG_DEF_LIST1) {
158 lir->defMask |= ENCODE_REG_LIST(lir->operands[1]);
159 }
160
buzbee5de34942012-03-01 14:51:57 -0800161#if defined(TARGET_ARM)
buzbee31a4a6f2012-02-28 15:36:15 -0800162 if (flags & REG_DEF_FPCS_LIST0) {
163 lir->defMask |= ENCODE_REG_FPCS_LIST(lir->operands[0]);
164 }
165
166 if (flags & REG_DEF_FPCS_LIST2) {
167 for (int i = 0; i < lir->operands[2]; i++) {
168 setupRegMask(&lir->defMask, lir->operands[1] + i);
169 }
170 }
buzbee5de34942012-03-01 14:51:57 -0800171#endif
buzbee31a4a6f2012-02-28 15:36:15 -0800172
173 if (flags & SETS_CCODES) {
174 lir->defMask |= ENCODE_CCODE;
175 }
176
177#if defined(TARGET_ARM)
178 /* Conservatively treat the IT block */
179 if (flags & IS_IT) {
180 lir->defMask = ENCODE_ALL;
181 }
182#endif
183
184 if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
185 int i;
186
187 for (i = 0; i < 4; i++) {
188 if (flags & (1 << (kRegUse0 + i))) {
189 setupRegMask(&lir->useMask, lir->operands[i]);
190 }
191 }
192 }
193
buzbeea7678db2012-03-05 15:35:46 -0800194#if defined(TARGET_ARM)
buzbee31a4a6f2012-02-28 15:36:15 -0800195 if (flags & REG_USE_PC) {
196 lir->useMask |= ENCODE_REG_PC;
197 }
buzbeea7678db2012-03-05 15:35:46 -0800198#endif
buzbee31a4a6f2012-02-28 15:36:15 -0800199
200 if (flags & REG_USE_SP) {
201 lir->useMask |= ENCODE_REG_SP;
202 }
203
204 if (flags & REG_USE_LIST0) {
205 lir->useMask |= ENCODE_REG_LIST(lir->operands[0]);
206 }
207
208 if (flags & REG_USE_LIST1) {
209 lir->useMask |= ENCODE_REG_LIST(lir->operands[1]);
210 }
211
buzbee5de34942012-03-01 14:51:57 -0800212#if defined(TARGET_ARM)
buzbee31a4a6f2012-02-28 15:36:15 -0800213 if (flags & REG_USE_FPCS_LIST0) {
214 lir->useMask |= ENCODE_REG_FPCS_LIST(lir->operands[0]);
215 }
216
217 if (flags & REG_USE_FPCS_LIST2) {
218 for (int i = 0; i < lir->operands[2]; i++) {
219 setupRegMask(&lir->useMask, lir->operands[1] + i);
220 }
221 }
buzbee5de34942012-03-01 14:51:57 -0800222#endif
buzbee31a4a6f2012-02-28 15:36:15 -0800223
224 if (flags & USES_CCODES) {
225 lir->useMask |= ENCODE_CCODE;
226 }
227
228#if defined(TARGET_ARM)
229 /* Fixup for kThumbPush/lr and kThumbPop/pc */
230 if (opcode == kThumbPush || opcode == kThumbPop) {
231 u8 r8Mask = getRegMaskCommon(r8);
232 if ((opcode == kThumbPush) && (lir->useMask & r8Mask)) {
233 lir->useMask &= ~r8Mask;
234 lir->useMask |= ENCODE_REG_LR;
235 } else if ((opcode == kThumbPop) && (lir->defMask & r8Mask)) {
236 lir->defMask &= ~r8Mask;
237 lir->defMask |= ENCODE_REG_PC;
238 }
239 }
240#endif
241}
242
243/*
buzbee5de34942012-03-01 14:51:57 -0800244 * Debugging macros
245 */
246#define DUMP_RESOURCE_MASK(X)
247#define DUMP_SSA_REP(X)
248
249/* Pretty-print a LIR instruction */
250void oatDumpLIRInsn(CompilationUnit* cUnit, LIR* arg, unsigned char* baseAddr)
251{
252 LIR* lir = (LIR*) arg;
253 int offset = lir->offset;
254 int dest = lir->operands[0];
buzbee86a4bce2012-03-06 18:15:00 -0800255 const bool dumpNop = (cUnit->enableDebug & (1 << kDebugShowNops));
buzbee5de34942012-03-01 14:51:57 -0800256
257 /* Handle pseudo-ops individually, and all regular insns as a group */
Elliott Hughesb25c3f62012-03-26 16:35:06 -0700258 switch (lir->opcode) {
buzbee5de34942012-03-01 14:51:57 -0800259 case kPseudoMethodEntry:
260 LOG(INFO) << "-------- method entry " <<
261 PrettyMethod(cUnit->method_idx, *cUnit->dex_file);
262 break;
263 case kPseudoMethodExit:
264 LOG(INFO) << "-------- Method_Exit";
265 break;
266 case kPseudoBarrier:
267 LOG(INFO) << "-------- BARRIER";
268 break;
269 case kPseudoExtended:
270 LOG(INFO) << "-------- " << (char* ) dest;
271 break;
272 case kPseudoSSARep:
273 DUMP_SSA_REP(LOG(INFO) << "-------- kMirOpPhi: " << (char* ) dest);
274 break;
275 case kPseudoEntryBlock:
276 LOG(INFO) << "-------- entry offset: 0x" << std::hex << dest;
277 break;
278 case kPseudoDalvikByteCodeBoundary:
279 LOG(INFO) << "-------- dalvik offset: 0x" << std::hex <<
280 lir->dalvikOffset << " @ " << (char* )lir->operands[0];
281 break;
282 case kPseudoExitBlock:
283 LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest;
284 break;
285 case kPseudoPseudoAlign4:
286 LOG(INFO) << (intptr_t)baseAddr + offset << " (0x" << std::hex <<
287 offset << "): .align4";
288 break;
289 case kPseudoEHBlockLabel:
290 LOG(INFO) << "Exception_Handling:";
291 break;
292 case kPseudoTargetLabel:
293 case kPseudoNormalBlockLabel:
Ian Rogersb3ab25b2012-03-19 01:12:01 -0700294 LOG(INFO) << "L" << (void*)lir << ":";
buzbee5de34942012-03-01 14:51:57 -0800295 break;
296 case kPseudoThrowTarget:
Ian Rogersb3ab25b2012-03-19 01:12:01 -0700297 LOG(INFO) << "LT" << (void*)lir << ":";
buzbee5de34942012-03-01 14:51:57 -0800298 break;
buzbeefc9e6fa2012-03-23 15:14:29 -0700299 case kPseudoIntrinsicRetry:
300 LOG(INFO) << "IR" << (void*)lir << ":";
301 break;
buzbee5de34942012-03-01 14:51:57 -0800302 case kPseudoSuspendTarget:
Ian Rogersb3ab25b2012-03-19 01:12:01 -0700303 LOG(INFO) << "LS" << (void*)lir << ":";
buzbee5de34942012-03-01 14:51:57 -0800304 break;
305 case kPseudoCaseLabel:
Ian Rogersb3ab25b2012-03-19 01:12:01 -0700306 LOG(INFO) << "LC" << (void*)lir << ": Case target 0x" <<
buzbee5de34942012-03-01 14:51:57 -0800307 std::hex << lir->operands[0] << "|" << std::dec <<
308 lir->operands[0];
309 break;
310 default:
311 if (lir->flags.isNop && !dumpNop) {
312 break;
313 } else {
314 std::string op_name(buildInsnString(EncodingMap[lir->opcode].name, lir, baseAddr));
315 std::string op_operands(buildInsnString(EncodingMap[lir->opcode].fmt, lir, baseAddr));
buzbeebe003642012-03-02 15:28:37 -0800316 LOG(INFO) << StringPrintf("%05x: %-9s%s%s", (unsigned int)(baseAddr + offset),
buzbee5de34942012-03-01 14:51:57 -0800317 op_name.c_str(), op_operands.c_str(), lir->flags.isNop ? "(nop)" : "");
318 }
319 break;
320 }
321
322 if (lir->useMask && (!lir->flags.isNop || dumpNop)) {
323 DUMP_RESOURCE_MASK(oatDumpResourceMask((LIR* ) lir,
324 lir->useMask, "use"));
325 }
326 if (lir->defMask && (!lir->flags.isNop || dumpNop)) {
327 DUMP_RESOURCE_MASK(oatDumpResourceMask((LIR* ) lir,
328 lir->defMask, "def"));
329 }
330}
331
332void oatDumpPromotionMap(CompilationUnit *cUnit)
333{
buzbee9c044ce2012-03-18 13:24:07 -0700334 int numRegs = cUnit->numDalvikRegisters + cUnit->numCompilerTemps + 1;
335 for (int i = 0; i < numRegs; i++) {
buzbee5de34942012-03-01 14:51:57 -0800336 PromotionMap vRegMap = cUnit->promotionMap[i];
Elliott Hughesbdf6c3d2012-03-20 13:43:53 -0700337 std::string buf;
buzbee5de34942012-03-01 14:51:57 -0800338 if (vRegMap.fpLocation == kLocPhysReg) {
Elliott Hughesbdf6c3d2012-03-20 13:43:53 -0700339 StringAppendF(&buf, " : s%d", vRegMap.fpReg & FP_REG_MASK);
buzbee9c044ce2012-03-18 13:24:07 -0700340 }
341
Elliott Hughesbdf6c3d2012-03-20 13:43:53 -0700342 std::string buf3;
343 if (i < cUnit->numDalvikRegisters) {
344 StringAppendF(&buf3, "%02d", i);
345 } else if (i == cUnit->methodSReg) {
346 buf3 = "Method*";
347 } else {
348 StringAppendF(&buf3, "ct%d", i - cUnit->numDalvikRegisters);
349 }
350
351 LOG(INFO) << StringPrintf("V[%s] -> %s%d%s", buf3.c_str(),
buzbee5de34942012-03-01 14:51:57 -0800352 vRegMap.coreLocation == kLocPhysReg ?
353 "r" : "SP+", vRegMap.coreLocation == kLocPhysReg ?
Elliott Hughesbdf6c3d2012-03-20 13:43:53 -0700354 vRegMap.coreReg : oatSRegOffset(cUnit, i), buf.c_str());
buzbee5de34942012-03-01 14:51:57 -0800355 }
356}
357
buzbee5de34942012-03-01 14:51:57 -0800358/* Dump instructions and constant pool contents */
359void oatCodegenDump(CompilationUnit* cUnit)
360{
361 LOG(INFO) << "/*";
362 LOG(INFO) << "Dumping LIR insns for "
363 << PrettyMethod(cUnit->method_idx, *cUnit->dex_file);
364 LIR* lirInsn;
365 LIR* thisLIR;
366 int insnsSize = cUnit->insnsSize;
367
368 LOG(INFO) << "Regs (excluding ins) : " << cUnit->numRegs;
369 LOG(INFO) << "Ins : " << cUnit->numIns;
370 LOG(INFO) << "Outs : " << cUnit->numOuts;
371 LOG(INFO) << "CoreSpills : " << cUnit->numCoreSpills;
372 LOG(INFO) << "FPSpills : " << cUnit->numFPSpills;
buzbee239c4e72012-03-16 08:42:29 -0700373 LOG(INFO) << "CompilerTemps : " << cUnit->numCompilerTemps;
buzbee5de34942012-03-01 14:51:57 -0800374 LOG(INFO) << "Frame size : " << cUnit->frameSize;
buzbee5de34942012-03-01 14:51:57 -0800375 LOG(INFO) << "code size is " << cUnit->totalSize <<
376 " bytes, Dalvik size is " << insnsSize * 2;
377 LOG(INFO) << "expansion factor: " <<
378 (float)cUnit->totalSize / (float)(insnsSize * 2);
379 oatDumpPromotionMap(cUnit);
380 for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
381 oatDumpLIRInsn(cUnit, lirInsn, 0);
382 }
383 for (lirInsn = cUnit->classPointerList; lirInsn; lirInsn = lirInsn->next) {
384 thisLIR = (LIR*) lirInsn;
385 LOG(INFO) << StringPrintf("%x (%04x): .class (%s)",
386 thisLIR->offset, thisLIR->offset,
387 ((CallsiteInfo *) thisLIR->operands[0])->classDescriptor);
388 }
389 for (lirInsn = cUnit->literalList; lirInsn; lirInsn = lirInsn->next) {
390 thisLIR = (LIR*) lirInsn;
391 LOG(INFO) << StringPrintf("%x (%04x): .word (%#x)",
392 thisLIR->offset, thisLIR->offset, thisLIR->operands[0]);
393 }
394
395 const DexFile::MethodId& method_id =
396 cUnit->dex_file->GetMethodId(cUnit->method_idx);
397 std::string signature(cUnit->dex_file->GetMethodSignature(method_id));
398 std::string name(cUnit->dex_file->GetMethodName(method_id));
399 std::string descriptor(cUnit->dex_file->GetMethodDeclaringClassDescriptor(method_id));
400
401 // Dump mapping table
402 if (cUnit->mappingTable.size() > 0) {
403 std::string line(StringPrintf("\n MappingTable %s%s_%s_mappingTable[%zu] = {",
404 descriptor.c_str(), name.c_str(), signature.c_str(), cUnit->mappingTable.size()));
405 std::replace(line.begin(), line.end(), ';', '_');
406 LOG(INFO) << line;
407 for (uint32_t i = 0; i < cUnit->mappingTable.size(); i+=2) {
buzbee82488f52012-03-02 08:20:26 -0800408 line = StringPrintf(" {0x%05x, 0x%04x},",
buzbee5de34942012-03-01 14:51:57 -0800409 cUnit->mappingTable[i], cUnit->mappingTable[i+1]);
410 LOG(INFO) << line;
411 }
412 LOG(INFO) <<" };\n\n";
413 }
414}
415
buzbeea2ebdd72012-03-04 14:57:06 -0800416
417LIR* rawLIR(CompilationUnit* cUnit, int dalvikOffset, int opcode, int op0,
Ian Rogersb5d09b22012-03-06 22:14:17 -0800418 int op1, int op2, int op3, int op4, LIR* target)
buzbeea2ebdd72012-03-04 14:57:06 -0800419{
420 LIR* insn = (LIR* ) oatNew(cUnit, sizeof(LIR), true, kAllocLIR);
421 insn->dalvikOffset = dalvikOffset;
422 insn->opcode = opcode;
423 insn->operands[0] = op0;
424 insn->operands[1] = op1;
425 insn->operands[2] = op2;
426 insn->operands[3] = op3;
Ian Rogersb5d09b22012-03-06 22:14:17 -0800427 insn->operands[4] = op4;
buzbeea2ebdd72012-03-04 14:57:06 -0800428 insn->target = target;
429 oatSetupResourceMasks(insn);
430 if (opcode == kPseudoTargetLabel) {
431 // Always make labels scheduling barriers
432 insn->defMask = ENCODE_ALL;
433 }
434 return insn;
435}
436
buzbee5de34942012-03-01 14:51:57 -0800437/*
buzbee31a4a6f2012-02-28 15:36:15 -0800438 * The following are building blocks to construct low-level IRs with 0 - 4
439 * operands.
440 */
buzbee5de34942012-03-01 14:51:57 -0800441LIR* newLIR0(CompilationUnit* cUnit, int opcode)
buzbee31a4a6f2012-02-28 15:36:15 -0800442{
Ian Rogersb5d09b22012-03-06 22:14:17 -0800443 DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & NO_OPERAND))
444 << EncodingMap[opcode].name << " " << (int)opcode << " "
445 << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " "
446 << cUnit->currentDalvikOffset;
buzbeea2ebdd72012-03-04 14:57:06 -0800447 LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode);
buzbee31a4a6f2012-02-28 15:36:15 -0800448 oatAppendLIR(cUnit, (LIR*) insn);
449 return insn;
450}
451
buzbee5de34942012-03-01 14:51:57 -0800452LIR* newLIR1(CompilationUnit* cUnit, int opcode,
buzbee31a4a6f2012-02-28 15:36:15 -0800453 int dest)
454{
Ian Rogersb5d09b22012-03-06 22:14:17 -0800455 DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_UNARY_OP))
456 << EncodingMap[opcode].name << " " << (int)opcode << " "
457 << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " "
458 << cUnit->currentDalvikOffset;
buzbeea2ebdd72012-03-04 14:57:06 -0800459 LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest);
buzbee31a4a6f2012-02-28 15:36:15 -0800460 oatAppendLIR(cUnit, (LIR*) insn);
461 return insn;
462}
463
buzbee5de34942012-03-01 14:51:57 -0800464LIR* newLIR2(CompilationUnit* cUnit, int opcode,
buzbee31a4a6f2012-02-28 15:36:15 -0800465 int dest, int src1)
466{
Ian Rogersb5d09b22012-03-06 22:14:17 -0800467 DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_BINARY_OP))
468 << EncodingMap[opcode].name << " " << (int)opcode << " "
469 << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " "
470 << cUnit->currentDalvikOffset;
buzbeea2ebdd72012-03-04 14:57:06 -0800471 LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest, src1);
buzbee31a4a6f2012-02-28 15:36:15 -0800472 oatAppendLIR(cUnit, (LIR*) insn);
473 return insn;
474}
475
buzbee5de34942012-03-01 14:51:57 -0800476LIR* newLIR3(CompilationUnit* cUnit, int opcode,
buzbee31a4a6f2012-02-28 15:36:15 -0800477 int dest, int src1, int src2)
478{
Ian Rogersb5d09b22012-03-06 22:14:17 -0800479 DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_TERTIARY_OP))
480 << EncodingMap[opcode].name << " " << (int)opcode << " "
481 << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " "
482 << cUnit->currentDalvikOffset;
buzbeea2ebdd72012-03-04 14:57:06 -0800483 LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest, src1,
484 src2);
buzbee31a4a6f2012-02-28 15:36:15 -0800485 oatAppendLIR(cUnit, (LIR*) insn);
486 return insn;
487}
488
buzbee5de34942012-03-01 14:51:57 -0800489LIR* newLIR4(CompilationUnit* cUnit, int opcode,
Ian Rogersb5d09b22012-03-06 22:14:17 -0800490 int dest, int src1, int src2, int info)
buzbee31a4a6f2012-02-28 15:36:15 -0800491{
Ian Rogersb5d09b22012-03-06 22:14:17 -0800492 DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_QUAD_OP))
493 << EncodingMap[opcode].name << " " << (int)opcode << " "
494 << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " "
495 << cUnit->currentDalvikOffset;
buzbeea2ebdd72012-03-04 14:57:06 -0800496 LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest, src1,
497 src2, info);
buzbee31a4a6f2012-02-28 15:36:15 -0800498 oatAppendLIR(cUnit, (LIR*) insn);
499 return insn;
500}
buzbee31a4a6f2012-02-28 15:36:15 -0800501
Ian Rogersb5d09b22012-03-06 22:14:17 -0800502LIR* newLIR5(CompilationUnit* cUnit, int opcode,
503 int dest, int src1, int src2, int info1, int info2)
504{
505 DCHECK(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_QUIN_OP))
506 << EncodingMap[opcode].name << " " << (int)opcode << " "
507 << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " "
508 << cUnit->currentDalvikOffset;
509 LIR* insn = rawLIR(cUnit, cUnit->currentDalvikOffset, opcode, dest, src1,
510 src2, info1, info2);
511 oatAppendLIR(cUnit, (LIR*) insn);
512 return insn;
513}
514
buzbee31a4a6f2012-02-28 15:36:15 -0800515/*
516 * Search the existing constants in the literal pool for an exact or close match
517 * within specified delta (greater or equal to 0).
518 */
519LIR* scanLiteralPool(LIR* dataTarget, int value, unsigned int delta)
520{
521 while (dataTarget) {
522 if (((unsigned) (value - ((LIR* ) dataTarget)->operands[0])) <=
523 delta)
524 return (LIR* ) dataTarget;
525 dataTarget = dataTarget->next;
526 }
527 return NULL;
528}
529
530/* Search the existing constants in the literal pool for an exact wide match */
531LIR* scanLiteralPoolWide(LIR* dataTarget, int valLo, int valHi)
532{
533 bool loMatch = false;
534 LIR* loTarget = NULL;
535 while (dataTarget) {
536 if (loMatch && (((LIR*)dataTarget)->operands[0] == valHi)) {
537 return (LIR*)loTarget;
538 }
539 loMatch = false;
540 if (((LIR*)dataTarget)->operands[0] == valLo) {
541 loMatch = true;
542 loTarget = dataTarget;
543 }
544 dataTarget = dataTarget->next;
545 }
546 return NULL;
547}
548
549/*
550 * The following are building blocks to insert constants into the pool or
551 * instruction streams.
552 */
553
buzbee5de34942012-03-01 14:51:57 -0800554/* Add a 32-bit constant either in the constant pool */
Ian Rogers3fa13792012-03-18 15:53:45 -0700555LIR* addWordData(CompilationUnit* cUnit, LIR* *constantListP, int value)
buzbee31a4a6f2012-02-28 15:36:15 -0800556{
557 /* Add the constant to the literal pool */
558 if (constantListP) {
559 LIR* newValue = (LIR* ) oatNew(cUnit, sizeof(LIR), true,
560 kAllocData);
561 newValue->operands[0] = value;
562 newValue->next = *constantListP;
563 *constantListP = (LIR*) newValue;
564 return newValue;
buzbee31a4a6f2012-02-28 15:36:15 -0800565 }
566 return NULL;
567}
568
569/* Add a 64-bit constant to the constant pool or mixed with code */
570LIR* addWideData(CompilationUnit* cUnit, LIR* *constantListP,
571 int valLo, int valHi)
572{
buzbee31a4a6f2012-02-28 15:36:15 -0800573 //FIXME: hard-coded little endian, need BE variant
buzbee5de34942012-03-01 14:51:57 -0800574 // Insert high word into list first
575 addWordData(cUnit, constantListP, valHi);
576 return addWordData(cUnit, constantListP, valLo);
buzbee31a4a6f2012-02-28 15:36:15 -0800577}
578
Ian Rogersab058bb2012-03-11 22:19:38 -0700579void pushWord(std::vector<uint8_t>&buf, int data) {
580 buf.push_back( data & 0xff);
581 buf.push_back( (data >> 8) & 0xff);
582 buf.push_back( (data >> 16) & 0xff);
583 buf.push_back( (data >> 24) & 0xff);
buzbeee3acd072012-02-25 17:03:10 -0800584}
585
Ian Rogersab058bb2012-03-11 22:19:38 -0700586void alignBuffer(std::vector<uint8_t>&buf, size_t offset) {
587 while (buf.size() < offset) {
buzbeee3acd072012-02-25 17:03:10 -0800588 buf.push_back(0);
Ian Rogersab058bb2012-03-11 22:19:38 -0700589 }
buzbeee3acd072012-02-25 17:03:10 -0800590}
591
Brian Carlstromf5822582012-03-19 22:34:31 -0700592bool IsDirect(int invokeType) {
593 InvokeType type = static_cast<InvokeType>(invokeType);
594 return type == kStatic || type == kDirect;
595}
596
buzbeee3acd072012-02-25 17:03:10 -0800597/* Write the literal pool to the output stream */
buzbee31a4a6f2012-02-28 15:36:15 -0800598void installLiteralPools(CompilationUnit* cUnit)
buzbeee3acd072012-02-25 17:03:10 -0800599{
600 alignBuffer(cUnit->codeBuffer, cUnit->dataOffset);
Ian Rogers3fa13792012-03-18 15:53:45 -0700601 LIR* dataLIR = cUnit->literalList;
buzbeee3acd072012-02-25 17:03:10 -0800602 while (dataLIR != NULL) {
603 pushWord(cUnit->codeBuffer, dataLIR->operands[0]);
604 dataLIR = NEXT_LIR(dataLIR);
605 }
Ian Rogers3fa13792012-03-18 15:53:45 -0700606 // Push code and method literals, record offsets for the compiler to patch.
607 dataLIR = cUnit->codeLiteralList;
608 if (dataLIR != NULL) {
609 while (dataLIR != NULL) {
Brian Carlstromf5822582012-03-19 22:34:31 -0700610 uint32_t target = dataLIR->operands[0];
Ian Rogers3fa13792012-03-18 15:53:45 -0700611 cUnit->compiler->AddCodePatch(cUnit->dex_cache, cUnit->dex_file,
612 cUnit->method_idx,
Brian Carlstromf5822582012-03-19 22:34:31 -0700613 cUnit->access_flags,
614 target,
615 IsDirect(dataLIR->operands[1]),
Ian Rogers3fa13792012-03-18 15:53:45 -0700616 cUnit->codeBuffer.size());
Brian Carlstromf5822582012-03-19 22:34:31 -0700617 const DexFile::MethodId& id = cUnit->dex_file->GetMethodId(target);
618 // unique based on target to ensure code deduplication works
619 uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
620 pushWord(cUnit->codeBuffer, unique_patch_value);
Ian Rogers3fa13792012-03-18 15:53:45 -0700621 dataLIR = NEXT_LIR(dataLIR);
622 }
623 dataLIR = cUnit->methodLiteralList;
624 while (dataLIR != NULL) {
Brian Carlstromf5822582012-03-19 22:34:31 -0700625 uint32_t target = dataLIR->operands[0];
Ian Rogers3fa13792012-03-18 15:53:45 -0700626 cUnit->compiler->AddMethodPatch(cUnit->dex_cache, cUnit->dex_file,
627 cUnit->method_idx,
Brian Carlstromf5822582012-03-19 22:34:31 -0700628 cUnit->access_flags,
629 target,
630 IsDirect(dataLIR->operands[1]),
Ian Rogers3fa13792012-03-18 15:53:45 -0700631 cUnit->codeBuffer.size());
Brian Carlstromf5822582012-03-19 22:34:31 -0700632 const DexFile::MethodId& id = cUnit->dex_file->GetMethodId(target);
633 // unique based on target to ensure code deduplication works
634 uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
635 pushWord(cUnit->codeBuffer, unique_patch_value);
Ian Rogers3fa13792012-03-18 15:53:45 -0700636 dataLIR = NEXT_LIR(dataLIR);
637 }
638 }
639
buzbeee3acd072012-02-25 17:03:10 -0800640}
641
642/* Write the switch tables to the output stream */
buzbee31a4a6f2012-02-28 15:36:15 -0800643void installSwitchTables(CompilationUnit* cUnit)
buzbeee3acd072012-02-25 17:03:10 -0800644{
645 GrowableListIterator iterator;
646 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
647 while (true) {
648 SwitchTable* tabRec = (SwitchTable *) oatGrowableListIteratorNext(
649 &iterator);
650 if (tabRec == NULL) break;
651 alignBuffer(cUnit->codeBuffer, tabRec->offset);
buzbeec5159d52012-03-03 11:48:39 -0800652 /*
653 * For Arm, our reference point is the address of the bx
654 * instruction that does the launch, so we have to subtract
655 * the auto pc-advance. For other targets the reference point
656 * is a label, so we can use the offset as-is.
657 */
658#if defined(TARGET_ARM)
659 int bxOffset = tabRec->anchor->offset + 4;
Ian Rogers7caad772012-03-30 01:07:54 -0700660#elif defined(TARGET_X86)
661 int bxOffset = 0;
buzbeec5159d52012-03-03 11:48:39 -0800662#else
663 int bxOffset = tabRec->anchor->offset;
664#endif
buzbeee3acd072012-02-25 17:03:10 -0800665 if (cUnit->printMe) {
666 LOG(INFO) << "Switch table for offset 0x" << std::hex << bxOffset;
667 }
Elliott Hughesadb8c672012-03-06 16:49:32 -0800668 if (tabRec->table[0] == Instruction::kSparseSwitchSignature) {
buzbeee3acd072012-02-25 17:03:10 -0800669 int* keys = (int*)&(tabRec->table[2]);
670 for (int elems = 0; elems < tabRec->table[1]; elems++) {
buzbee31a4a6f2012-02-28 15:36:15 -0800671 int disp = tabRec->targets[elems]->offset - bxOffset;
buzbeee3acd072012-02-25 17:03:10 -0800672 if (cUnit->printMe) {
673 LOG(INFO) << " Case[" << elems << "] key: 0x" <<
674 std::hex << keys[elems] << ", disp: 0x" <<
675 std::hex << disp;
676 }
677 pushWord(cUnit->codeBuffer, keys[elems]);
678 pushWord(cUnit->codeBuffer,
buzbee31a4a6f2012-02-28 15:36:15 -0800679 tabRec->targets[elems]->offset - bxOffset);
buzbeee3acd072012-02-25 17:03:10 -0800680 }
681 } else {
Elliott Hughesadb8c672012-03-06 16:49:32 -0800682 DCHECK_EQ(static_cast<int>(tabRec->table[0]), static_cast<int>(Instruction::kPackedSwitchSignature));
buzbeee3acd072012-02-25 17:03:10 -0800683 for (int elems = 0; elems < tabRec->table[1]; elems++) {
buzbee31a4a6f2012-02-28 15:36:15 -0800684 int disp = tabRec->targets[elems]->offset - bxOffset;
buzbeee3acd072012-02-25 17:03:10 -0800685 if (cUnit->printMe) {
686 LOG(INFO) << " Case[" << elems << "] disp: 0x" <<
687 std::hex << disp;
688 }
689 pushWord(cUnit->codeBuffer,
buzbee31a4a6f2012-02-28 15:36:15 -0800690 tabRec->targets[elems]->offset - bxOffset);
buzbeee3acd072012-02-25 17:03:10 -0800691 }
692 }
693 }
694}
695
696/* Write the fill array dta to the output stream */
buzbee31a4a6f2012-02-28 15:36:15 -0800697void installFillArrayData(CompilationUnit* cUnit)
buzbeee3acd072012-02-25 17:03:10 -0800698{
699 GrowableListIterator iterator;
700 oatGrowableListIteratorInit(&cUnit->fillArrayData, &iterator);
701 while (true) {
702 FillArrayData *tabRec = (FillArrayData *) oatGrowableListIteratorNext(
703 &iterator);
704 if (tabRec == NULL) break;
705 alignBuffer(cUnit->codeBuffer, tabRec->offset);
Ian Rogersab058bb2012-03-11 22:19:38 -0700706 for (int i = 0; i < (tabRec->size + 1) / 2; i++) {
707 cUnit->codeBuffer.push_back( tabRec->table[i] & 0xFF);
708 cUnit->codeBuffer.push_back( (tabRec->table[i] >> 8) & 0xFF);
buzbeee3acd072012-02-25 17:03:10 -0800709 }
710 }
711}
712
buzbee31a4a6f2012-02-28 15:36:15 -0800713int assignLiteralOffsetCommon(LIR* lir, int offset)
buzbeee3acd072012-02-25 17:03:10 -0800714{
715 for (;lir != NULL; lir = lir->next) {
716 lir->offset = offset;
717 offset += 4;
718 }
719 return offset;
720}
721
buzbee31a4a6f2012-02-28 15:36:15 -0800722void createMappingTable(CompilationUnit* cUnit)
buzbeee3acd072012-02-25 17:03:10 -0800723{
buzbee31a4a6f2012-02-28 15:36:15 -0800724 LIR* tgtLIR;
buzbeee3acd072012-02-25 17:03:10 -0800725 int currentDalvikOffset = -1;
726
buzbee31a4a6f2012-02-28 15:36:15 -0800727 for (tgtLIR = (LIR *) cUnit->firstLIRInsn;
buzbeee3acd072012-02-25 17:03:10 -0800728 tgtLIR;
729 tgtLIR = NEXT_LIR(tgtLIR)) {
730 if ((tgtLIR->opcode >= 0) && !tgtLIR->flags.isNop &&
buzbee31a4a6f2012-02-28 15:36:15 -0800731 (currentDalvikOffset != tgtLIR->dalvikOffset)) {
buzbeee3acd072012-02-25 17:03:10 -0800732 // Changed - need to emit a record
buzbee31a4a6f2012-02-28 15:36:15 -0800733 cUnit->mappingTable.push_back(tgtLIR->offset);
734 cUnit->mappingTable.push_back(tgtLIR->dalvikOffset);
735 currentDalvikOffset = tgtLIR->dalvikOffset;
buzbeee3acd072012-02-25 17:03:10 -0800736 }
737 }
738}
739
740/* Determine the offset of each literal field */
buzbee31a4a6f2012-02-28 15:36:15 -0800741int assignLiteralOffset(CompilationUnit* cUnit, int offset)
buzbeee3acd072012-02-25 17:03:10 -0800742{
743 offset = assignLiteralOffsetCommon(cUnit->literalList, offset);
Ian Rogers3fa13792012-03-18 15:53:45 -0700744 offset = assignLiteralOffsetCommon(cUnit->codeLiteralList, offset);
745 offset = assignLiteralOffsetCommon(cUnit->methodLiteralList, offset);
buzbeee3acd072012-02-25 17:03:10 -0800746 return offset;
747}
748
buzbee31a4a6f2012-02-28 15:36:15 -0800749int assignSwitchTablesOffset(CompilationUnit* cUnit, int offset)
buzbeee3acd072012-02-25 17:03:10 -0800750{
751 GrowableListIterator iterator;
752 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
753 while (true) {
754 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
755 &iterator);
756 if (tabRec == NULL) break;
757 tabRec->offset = offset;
Elliott Hughesadb8c672012-03-06 16:49:32 -0800758 if (tabRec->table[0] == Instruction::kSparseSwitchSignature) {
buzbeee3acd072012-02-25 17:03:10 -0800759 offset += tabRec->table[1] * (sizeof(int) * 2);
760 } else {
Elliott Hughesadb8c672012-03-06 16:49:32 -0800761 DCHECK_EQ(static_cast<int>(tabRec->table[0]), static_cast<int>(Instruction::kPackedSwitchSignature));
buzbeee3acd072012-02-25 17:03:10 -0800762 offset += tabRec->table[1] * sizeof(int);
763 }
764 }
765 return offset;
766}
767
buzbee31a4a6f2012-02-28 15:36:15 -0800768int assignFillArrayDataOffset(CompilationUnit* cUnit, int offset)
buzbeee3acd072012-02-25 17:03:10 -0800769{
770 GrowableListIterator iterator;
771 oatGrowableListIteratorInit(&cUnit->fillArrayData, &iterator);
772 while (true) {
773 FillArrayData *tabRec = (FillArrayData *) oatGrowableListIteratorNext(
774 &iterator);
775 if (tabRec == NULL) break;
776 tabRec->offset = offset;
777 offset += tabRec->size;
778 // word align
779 offset = (offset + 3) & ~3;
780 }
781 return offset;
782}
783
784/*
785 * Walk the compilation unit and assign offsets to instructions
786 * and literals and compute the total size of the compiled unit.
787 */
788void oatAssignOffsets(CompilationUnit* cUnit)
789{
790 int offset = oatAssignInsnOffsets(cUnit);
791
792 /* Const values have to be word aligned */
793 offset = (offset + 3) & ~3;
794
795 /* Set up offsets for literals */
796 cUnit->dataOffset = offset;
797
798 offset = assignLiteralOffset(cUnit, offset);
799
800 offset = assignSwitchTablesOffset(cUnit, offset);
801
802 offset = assignFillArrayDataOffset(cUnit, offset);
803
804 cUnit->totalSize = offset;
805}
806
807/*
808 * Go over each instruction in the list and calculate the offset from the top
809 * before sending them off to the assembler. If out-of-range branch distance is
810 * seen rearrange the instructions a bit to correct it.
811 */
812void oatAssembleLIR(CompilationUnit* cUnit)
813{
814 oatAssignOffsets(cUnit);
815 /*
816 * Assemble here. Note that we generate code with optimistic assumptions
817 * and if found now to work, we'll have to redo the sequence and retry.
818 */
819
820 while (true) {
821 AssemblerStatus res = oatAssembleInstructions(cUnit, 0);
822 if (res == kSuccess) {
823 break;
824 } else {
825 cUnit->assemblerRetries++;
826 if (cUnit->assemblerRetries > MAX_ASSEMBLER_RETRIES) {
Ian Rogersb41b33b2012-03-20 14:22:54 -0700827 oatCodegenDump(cUnit);
828 LOG(FATAL) << "Assembler error - too many retries";
buzbeee3acd072012-02-25 17:03:10 -0800829 }
830 // Redo offsets and try again
831 oatAssignOffsets(cUnit);
832 cUnit->codeBuffer.clear();
833 }
834 }
835
836 // Install literals
837 installLiteralPools(cUnit);
838
839 // Install switch tables
840 installSwitchTables(cUnit);
841
842 // Install fill array data
843 installFillArrayData(cUnit);
844
845 /*
846 * Create the mapping table
847 */
848 createMappingTable(cUnit);
849}
850
buzbee31a4a6f2012-02-28 15:36:15 -0800851/*
852 * Insert a kPseudoCaseLabel at the beginning of the Dalvik
853 * offset vaddr. This label will be used to fix up the case
854 * branch table during the assembly phase. Be sure to set
855 * all resource flags on this to prevent code motion across
856 * target boundaries. KeyVal is just there for debugging.
857 */
858LIR* insertCaseLabel(CompilationUnit* cUnit, int vaddr, int keyVal)
859{
Elliott Hughesa0e18062012-04-13 15:59:59 -0700860 SafeMap<unsigned int, LIR*>::iterator it;
buzbee31a4a6f2012-02-28 15:36:15 -0800861 it = cUnit->boundaryMap.find(vaddr);
862 if (it == cUnit->boundaryMap.end()) {
863 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
864 }
865 LIR* newLabel = (LIR*)oatNew(cUnit, sizeof(LIR), true, kAllocLIR);
866 newLabel->dalvikOffset = vaddr;
867 newLabel->opcode = kPseudoCaseLabel;
868 newLabel->operands[0] = keyVal;
869 oatInsertLIRAfter(it->second, (LIR*)newLabel);
870 return newLabel;
871}
872
873void markPackedCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
874{
875 const u2* table = tabRec->table;
876 int baseVaddr = tabRec->vaddr;
877 int *targets = (int*)&table[4];
878 int entries = table[1];
879 int lowKey = s4FromSwitchData(&table[2]);
880 for (int i = 0; i < entries; i++) {
881 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
882 i + lowKey);
883 }
884}
885
886void markSparseCaseLabels(CompilationUnit* cUnit, SwitchTable *tabRec)
887{
888 const u2* table = tabRec->table;
889 int baseVaddr = tabRec->vaddr;
890 int entries = table[1];
891 int* keys = (int*)&table[2];
892 int* targets = &keys[entries];
893 for (int i = 0; i < entries; i++) {
894 tabRec->targets[i] = insertCaseLabel(cUnit, baseVaddr + targets[i],
895 keys[i]);
896 }
897}
898
899void oatProcessSwitchTables(CompilationUnit* cUnit)
900{
901 GrowableListIterator iterator;
902 oatGrowableListIteratorInit(&cUnit->switchTables, &iterator);
903 while (true) {
904 SwitchTable *tabRec = (SwitchTable *) oatGrowableListIteratorNext(
905 &iterator);
906 if (tabRec == NULL) break;
Elliott Hughesadb8c672012-03-06 16:49:32 -0800907 if (tabRec->table[0] == Instruction::kPackedSwitchSignature) {
buzbee31a4a6f2012-02-28 15:36:15 -0800908 markPackedCaseLabels(cUnit, tabRec);
Elliott Hughesadb8c672012-03-06 16:49:32 -0800909 } else if (tabRec->table[0] == Instruction::kSparseSwitchSignature) {
buzbee31a4a6f2012-02-28 15:36:15 -0800910 markSparseCaseLabels(cUnit, tabRec);
Elliott Hughesadb8c672012-03-06 16:49:32 -0800911 } else {
buzbee31a4a6f2012-02-28 15:36:15 -0800912 LOG(FATAL) << "Invalid switch table";
913 }
914 }
915}
916
917//FIXME: Do we have endian issues here?
918
919void dumpSparseSwitchTable(const u2* table)
920 /*
921 * Sparse switch data format:
922 * ushort ident = 0x0200 magic value
923 * ushort size number of entries in the table; > 0
924 * int keys[size] keys, sorted low-to-high; 32-bit aligned
925 * int targets[size] branch targets, relative to switch opcode
926 *
927 * Total size is (2+size*4) 16-bit code units.
928 */
929{
930 u2 ident = table[0];
931 int entries = table[1];
932 int* keys = (int*)&table[2];
933 int* targets = &keys[entries];
934 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident <<
935 ", entries: " << std::dec << entries;
936 for (int i = 0; i < entries; i++) {
937 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex <<
938 targets[i];
939 }
940}
941
942void dumpPackedSwitchTable(const u2* table)
943 /*
944 * Packed switch data format:
945 * ushort ident = 0x0100 magic value
946 * ushort size number of entries in the table
947 * int first_key first (and lowest) switch case value
948 * int targets[size] branch targets, relative to switch opcode
949 *
950 * Total size is (4+size*2) 16-bit code units.
951 */
952{
953 u2 ident = table[0];
954 int* targets = (int*)&table[4];
955 int entries = table[1];
956 int lowKey = s4FromSwitchData(&table[2]);
957 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident <<
958 ", entries: " << std::dec << entries << ", lowKey: " << lowKey;
959 for (int i = 0; i < entries; i++) {
960 LOG(INFO) << " Key[" << (i + lowKey) << "] -> 0x" << std::hex <<
961 targets[i];
962 }
963}
buzbeee3acd072012-02-25 17:03:10 -0800964
965
966} // namespace art