blob: 59a745584ff89d2dbb7fa0bbe5c80e28de9f83a1 [file] [log] [blame]
Ben Chengba4fc8b2009-06-01 13:00:29 -07001/*
2 * Copyright (C) 2009 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#include "Dalvik.h"
18#include "libdex/OpCode.h"
19#include "dexdump/OpCodeNames.h"
20#include "interp/Jit.h"
21#include "CompilerInternals.h"
22
23/*
24 * Parse an instruction, return the length of the instruction
25 */
26static inline int parseInsn(const u2 *codePtr, DecodedInstruction *decInsn,
27 bool printMe)
28{
29 u2 instr = *codePtr;
30 OpCode opcode = instr & 0xff;
31 int insnWidth;
32
33 // Need to check if this is a real NOP or a pseudo opcode
34 if (opcode == OP_NOP && instr != 0) {
35 if (instr == kPackedSwitchSignature) {
36 insnWidth = 4 + codePtr[1] * 2;
37 } else if (instr == kSparseSwitchSignature) {
38 insnWidth = 2 + codePtr[1] * 4;
39 } else if (instr == kArrayDataSignature) {
40 int width = codePtr[1];
41 int size = codePtr[2] | (codePtr[3] << 16);
42 // The plus 1 is to round up for odd size and width
43 insnWidth = 4 + ((size * width) + 1) / 2;
44 }
45 insnWidth = 0;
46 } else {
47 insnWidth = gDvm.instrWidth[opcode];
48 if (insnWidth < 0) {
49 insnWidth = -insnWidth;
50 }
51 }
52
53 dexDecodeInstruction(gDvm.instrFormat, codePtr, decInsn);
54 if (printMe) {
55 LOGD("%p: %#06x %s\n", codePtr, opcode, getOpcodeName(opcode));
56 }
57 return insnWidth;
58}
59
60/*
61 * Identify block-ending instructions and collect supplemental information
62 * regarding the following instructions.
63 */
64static inline bool findBlockBoundary(const Method *caller, MIR *insn,
65 unsigned int curOffset,
66 unsigned int *target, bool *isInvoke,
67 const Method **callee)
68{
69 switch (insn->dalvikInsn.opCode) {
70 /* Target is not compile-time constant */
71 case OP_RETURN_VOID:
72 case OP_RETURN:
73 case OP_RETURN_WIDE:
74 case OP_RETURN_OBJECT:
75 case OP_THROW:
76 case OP_INVOKE_VIRTUAL:
77 case OP_INVOKE_VIRTUAL_RANGE:
78 case OP_INVOKE_INTERFACE:
79 case OP_INVOKE_INTERFACE_RANGE:
80 case OP_INVOKE_VIRTUAL_QUICK:
81 case OP_INVOKE_VIRTUAL_QUICK_RANGE:
82 *isInvoke = true;
83 break;
84 case OP_INVOKE_SUPER:
85 case OP_INVOKE_SUPER_RANGE: {
86 int mIndex = caller->clazz->pDvmDex->
87 pResMethods[insn->dalvikInsn.vB]->methodIndex;
88 const Method *calleeMethod =
89 caller->clazz->super->vtable[mIndex];
90
91 if (!dvmIsNativeMethod(calleeMethod)) {
92 *target = (unsigned int) calleeMethod->insns;
93 }
94 *isInvoke = true;
95 *callee = calleeMethod;
96 break;
97 }
98 case OP_INVOKE_STATIC:
99 case OP_INVOKE_STATIC_RANGE: {
100 const Method *calleeMethod =
101 caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
102
103 if (!dvmIsNativeMethod(calleeMethod)) {
104 *target = (unsigned int) calleeMethod->insns;
105 }
106 *isInvoke = true;
107 *callee = calleeMethod;
108 break;
109 }
110 case OP_INVOKE_SUPER_QUICK:
111 case OP_INVOKE_SUPER_QUICK_RANGE: {
112 const Method *calleeMethod =
113 caller->clazz->super->vtable[insn->dalvikInsn.vB];
114
115 if (!dvmIsNativeMethod(calleeMethod)) {
116 *target = (unsigned int) calleeMethod->insns;
117 }
118 *isInvoke = true;
119 *callee = calleeMethod;
120 break;
121 }
122 case OP_INVOKE_DIRECT:
123 case OP_INVOKE_DIRECT_RANGE: {
124 const Method *calleeMethod =
125 caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
126 if (!dvmIsNativeMethod(calleeMethod)) {
127 *target = (unsigned int) calleeMethod->insns;
128 }
129 *isInvoke = true;
130 *callee = calleeMethod;
131 break;
132 }
133 case OP_GOTO:
134 case OP_GOTO_16:
135 case OP_GOTO_32:
136 *target = curOffset + (int) insn->dalvikInsn.vA;
137 break;
138
139 case OP_IF_EQ:
140 case OP_IF_NE:
141 case OP_IF_LT:
142 case OP_IF_GE:
143 case OP_IF_GT:
144 case OP_IF_LE:
145 *target = curOffset + (int) insn->dalvikInsn.vC;
146 break;
147
148 case OP_IF_EQZ:
149 case OP_IF_NEZ:
150 case OP_IF_LTZ:
151 case OP_IF_GEZ:
152 case OP_IF_GTZ:
153 case OP_IF_LEZ:
154 *target = curOffset + (int) insn->dalvikInsn.vB;
155 break;
156
157 default:
158 return false;
159 } return true;
160}
161
162/*
163 * Identify conditional branch instructions
164 */
165static inline bool isUnconditionalBranch(MIR *insn)
166{
167 switch (insn->dalvikInsn.opCode) {
168 case OP_RETURN_VOID:
169 case OP_RETURN:
170 case OP_RETURN_WIDE:
171 case OP_RETURN_OBJECT:
172 case OP_GOTO:
173 case OP_GOTO_16:
174 case OP_GOTO_32:
175 return true;
176 default:
177 return false;
178 }
179}
180
181/*
182 * Main entry point to start trace compilation. Basic blocks are constructed
183 * first and they will be passed to the codegen routines to convert Dalvik
184 * bytecode into machine code.
185 */
186void *dvmCompileTrace(JitTraceDescription *desc)
187{
188 const DexCode *dexCode = dvmGetMethodCode(desc->method);
189 const JitTraceRun* currRun = &desc->trace[0];
190 bool done = false;
191 unsigned int curOffset = currRun->frag.startOffset;
192 unsigned int numInsts = currRun->frag.numInsts;
193 const u2 *codePtr = dexCode->insns + curOffset;
194 int traceSize = 0;
195 const u2 *startCodePtr = codePtr;
196 BasicBlock *startBB, *curBB, *lastBB;
197 int numBlocks = 0;
198 static int compilationId;
199 CompilationUnit cUnit;
200 memset(&cUnit, 0, sizeof(CompilationUnit));
201
202 /* Initialize the printMe flag */
203 cUnit.printMe = gDvmJit.printMe;
204
205 /* Identify traces that we don't want to compile */
206 if (gDvmJit.methodTable) {
207 int len = strlen(desc->method->clazz->descriptor) +
208 strlen(desc->method->name) + 1;
209 char *fullSignature = dvmCompilerNew(len, true);
210 strcpy(fullSignature, desc->method->clazz->descriptor);
211 strcat(fullSignature, desc->method->name);
212
213 int hashValue = dvmComputeUtf8Hash(fullSignature);
214
215 /*
216 * Doing three levels of screening to see whether we want to skip
217 * compiling this method
218 */
219
220 /* First, check the full "class;method" signature */
221 bool methodFound =
222 dvmHashTableLookup(gDvmJit.methodTable, hashValue,
223 fullSignature, (HashCompareFunc) strcmp,
224 false) !=
225 NULL;
226
227 /* Full signature not found - check the enclosing class */
228 if (methodFound == false) {
229 int hashValue = dvmComputeUtf8Hash(desc->method->clazz->descriptor);
230 methodFound =
231 dvmHashTableLookup(gDvmJit.methodTable, hashValue,
232 (char *) desc->method->clazz->descriptor,
233 (HashCompareFunc) strcmp, false) !=
234 NULL;
235 /* Enclosing class not found - check the method name */
236 if (methodFound == false) {
237 int hashValue = dvmComputeUtf8Hash(desc->method->name);
238 methodFound =
239 dvmHashTableLookup(gDvmJit.methodTable, hashValue,
240 (char *) desc->method->name,
241 (HashCompareFunc) strcmp, false) !=
242 NULL;
243 }
244 }
245
246 /*
247 * Under the following conditions, the trace will be *conservatively*
248 * compiled by only containing single-step instructions to and from the
249 * interpreter.
250 * 1) If includeSelectedMethod == false, the method matches the full or
251 * partial signature stored in the hash table.
252 *
253 * 2) If includeSelectedMethod == true, the method does not match the
254 * full and partial signature stored in the hash table.
255 */
256 if (gDvmJit.includeSelectedMethod != methodFound) {
257 cUnit.allSingleStep = true;
258 } else {
259 /* Compile the trace as normal */
260
261 /* Print the method we cherry picked */
262 if (gDvmJit.includeSelectedMethod == true) {
263 cUnit.printMe = true;
264 }
265 }
266 }
267
268 /* Allocate the first basic block */
269 lastBB = startBB = curBB = dvmCompilerNewBB(DALVIK_BYTECODE);
270 curBB->startOffset = curOffset;
271 curBB->id = numBlocks++;
272
273 if (cUnit.printMe) {
274 LOGD("--------\nCompiler: Building trace for %s, offset 0x%x\n",
275 desc->method->name, curOffset);
276 }
277
278 while (!done) {
279 MIR *insn;
280 int width;
281 insn = dvmCompilerNew(sizeof(MIR),false);
282 insn->offset = curOffset;
283 width = parseInsn(codePtr, &insn->dalvikInsn, cUnit.printMe);
284 insn->width = width;
285 traceSize += width;
286 dvmCompilerAppendMIR(curBB, insn);
287 if (--numInsts==0) {
288 if (currRun->frag.runEnd) {
289 done = true;
290 } else {
291 curBB = dvmCompilerNewBB(DALVIK_BYTECODE);
292 lastBB->next = curBB;
293 lastBB = curBB;
294 curBB->id = numBlocks++;
295 currRun++;
296 curOffset = currRun->frag.startOffset;
297 numInsts = currRun->frag.numInsts;
298 curBB->startOffset = curOffset;
299 codePtr = dexCode->insns + curOffset;
300 }
301 } else {
302 curOffset += width;
303 codePtr += width;
304 }
305 }
306
307 /*
308 * Now scan basic blocks containing real code to connect the
309 * taken/fallthrough links. Also create chaining cells for code not included
310 * in the trace.
311 */
312 for (curBB = startBB; curBB; curBB = curBB->next) {
313 MIR *lastInsn = curBB->lastMIRInsn;
314 /* Hit a pseudo block - exit the search now */
315 if (lastInsn == NULL) {
316 break;
317 }
318 curOffset = lastInsn->offset;
319 unsigned int targetOffset = curOffset;
320 unsigned int fallThroughOffset = curOffset + lastInsn->width;
321 bool isInvoke = false;
322 const Method *callee = NULL;
323
324 findBlockBoundary(desc->method, curBB->lastMIRInsn, curOffset,
325 &targetOffset, &isInvoke, &callee);
326
327 /* Link the taken and fallthrough blocks */
328 BasicBlock *searchBB;
329
330 /* No backward branch in the trace - start searching the next BB */
331 for (searchBB = curBB->next; searchBB; searchBB = searchBB->next) {
332 if (targetOffset == searchBB->startOffset) {
333 curBB->taken = searchBB;
334 }
335 if (fallThroughOffset == searchBB->startOffset) {
336 curBB->fallThrough = searchBB;
337 }
338 }
339
340 /* Target block not included in the trace */
341 if (targetOffset != curOffset && curBB->taken == NULL) {
342 lastBB->next = dvmCompilerNewBB(
343 isInvoke ? CHAINING_CELL_INVOKE : CHAINING_CELL_GENERIC);
344 lastBB = lastBB->next;
345 lastBB->id = numBlocks++;
346 if (isInvoke) {
347 lastBB->startOffset = 0;
348 lastBB->containingMethod = callee;
349 } else {
350 lastBB->startOffset = targetOffset;
351 }
352 curBB->taken = lastBB;
353 }
354
355 /* Fallthrough block not included in the trace */
356 if (!isUnconditionalBranch(lastInsn) && curBB->fallThrough == NULL) {
357 lastBB->next = dvmCompilerNewBB(
358 isInvoke ? CHAINING_CELL_POST_INVOKE : CHAINING_CELL_GENERIC);
359 lastBB = lastBB->next;
360 lastBB->id = numBlocks++;
361 lastBB->startOffset = fallThroughOffset;
362 curBB->fallThrough = lastBB;
363 }
364 }
365
366 /* Now create a special block to host PC reconstruction code */
367 lastBB->next = dvmCompilerNewBB(PC_RECONSTRUCTION);
368 lastBB = lastBB->next;
369 lastBB->id = numBlocks++;
370
371 /* And one final block that publishes the PC and raise the exception */
372 lastBB->next = dvmCompilerNewBB(EXCEPTION_HANDLING);
373 lastBB = lastBB->next;
374 lastBB->id = numBlocks++;
375
376 if (cUnit.printMe) {
377 LOGD("TRACEINFO (%d): 0x%08x %s%s 0x%x %d of %d, %d blocks",
378 compilationId++,
379 (intptr_t) desc->method->insns,
380 desc->method->clazz->descriptor,
381 desc->method->name,
382 desc->trace[0].frag.startOffset,
383 traceSize,
384 dexCode->insnsSize,
385 numBlocks);
386 }
387
388 BasicBlock **blockList;
389
390 cUnit.method = desc->method;
391 cUnit.traceDesc = desc;
392 cUnit.numBlocks = numBlocks;
393 dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
394 blockList = cUnit.blockList =
395 dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true);
396
397 int i;
398
399 for (i = 0, curBB = startBB; i < numBlocks; i++) {
400 blockList[i] = curBB;
401 curBB = curBB->next;
402 }
403 /* Make sure all blocks are added to the cUnit */
404 assert(curBB == NULL);
405
406 if (cUnit.printMe) {
407 dvmCompilerDumpCompilationUnit(&cUnit);
408 }
409
410 /* Convert MIR to LIR, etc. */
411 dvmCompilerMIR2LIR(&cUnit);
412
413 /* Convert LIR into machine code */
414 dvmCompilerAssembleLIR(&cUnit);
415
416 if (cUnit.printMe) {
417 dvmCompilerCodegenDump(&cUnit);
418 LOGD("End %s%s", desc->method->clazz->descriptor, desc->method->name);
419 }
420
421 /* Reset the compiler resource pool */
422 dvmCompilerArenaReset();
423
424 return cUnit.baseAddr;
425}
426
427/*
428 * Similar to dvmCompileTrace, but the entity processed here is the whole
429 * method.
430 *
431 * TODO: implementation will be revisited when the trace builder can provide
432 * whole-method traces.
433 */
434void *dvmCompileMethod(Method *method)
435{
436 const DexCode *dexCode = dvmGetMethodCode(method);
437 const u2 *codePtr = dexCode->insns;
438 const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
439 int blockID = 0;
440 unsigned int curOffset = 0;
441
442 BasicBlock *firstBlock = dvmCompilerNewBB(DALVIK_BYTECODE);
443 firstBlock->id = blockID++;
444
445 /* Allocate the bit-vector to track the beginning of basic blocks */
446 BitVector *bbStartAddr = dvmAllocBitVector(dexCode->insnsSize+1, false);
447 dvmSetBit(bbStartAddr, 0);
448
449 /*
450 * Sequentially go through every instruction first and put them in a single
451 * basic block. Identify block boundaries at the mean time.
452 */
453 while (codePtr < codeEnd) {
454 MIR *insn = dvmCompilerNew(sizeof(MIR), false);
455 insn->offset = curOffset;
456 int width = parseInsn(codePtr, &insn->dalvikInsn, false);
457 bool isInvoke = false;
458 const Method *callee;
459 insn->width = width;
460
461 dvmCompilerAppendMIR(firstBlock, insn);
462 /*
463 * Check whether this is a block ending instruction and whether it
464 * suggests the start of a new block
465 */
466 unsigned int target = curOffset;
467
468 /*
469 * If findBlockBoundary returns true, it means the current instruction
470 * is terminating the current block. If it is a branch, the target
471 * address will be recorded in target.
472 */
473 if (findBlockBoundary(method, insn, curOffset, &target, &isInvoke,
474 &callee)) {
475 dvmSetBit(bbStartAddr, curOffset + width);
476 if (target != curOffset) {
477 dvmSetBit(bbStartAddr, target);
478 }
479 }
480
481 codePtr += width;
482 /* each bit represents 16-bit quantity */
483 curOffset += width;
484 }
485
486 /*
487 * The number of blocks will be equal to the number of bits set to 1 in the
488 * bit vector minus 1, because the bit representing the location after the
489 * last instruction is set to one.
490 */
491 int numBlocks = dvmCountSetBits(bbStartAddr);
492 if (dvmIsBitSet(bbStartAddr, dexCode->insnsSize)) {
493 numBlocks--;
494 }
495
496 CompilationUnit cUnit;
497 BasicBlock **blockList;
498
499 memset(&cUnit, 0, sizeof(CompilationUnit));
500 cUnit.method = method;
501 blockList = cUnit.blockList =
502 dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true);
503
504 /*
505 * Register the first block onto the list and start split it into block
506 * boundaries from there.
507 */
508 blockList[0] = firstBlock;
509 cUnit.numBlocks = 1;
510
511 int i;
512 for (i = 0; i < numBlocks; i++) {
513 MIR *insn;
514 BasicBlock *curBB = blockList[i];
515 curOffset = curBB->lastMIRInsn->offset;
516
517 for (insn = curBB->firstMIRInsn->next; insn; insn = insn->next) {
518 /* Found the beginning of a new block, see if it is created yet */
519 if (dvmIsBitSet(bbStartAddr, insn->offset)) {
520 int j;
521 for (j = 0; j < cUnit.numBlocks; j++) {
522 if (blockList[j]->firstMIRInsn->offset == insn->offset)
523 break;
524 }
525
526 /* Block not split yet - do it now */
527 if (j == cUnit.numBlocks) {
528 BasicBlock *newBB = dvmCompilerNewBB(DALVIK_BYTECODE);
529 newBB->id = blockID++;
530 newBB->firstMIRInsn = insn;
531 newBB->lastMIRInsn = curBB->lastMIRInsn;
532 curBB->lastMIRInsn = insn->prev;
533 insn->prev->next = NULL;
534 insn->prev = NULL;
535
536 /*
537 * If the insn is not an unconditional branch, set up the
538 * fallthrough link.
539 */
540 if (!isUnconditionalBranch(curBB->lastMIRInsn)) {
541 curBB->fallThrough = newBB;
542 }
543
544 /* enqueue the new block */
545 blockList[cUnit.numBlocks++] = newBB;
546 break;
547 }
548 }
549 }
550 }
551
552 if (numBlocks != cUnit.numBlocks) {
553 LOGE("Expect %d vs %d basic blocks\n", numBlocks, cUnit.numBlocks);
554 dvmAbort();
555 }
556
557 dvmFreeBitVector(bbStartAddr);
558
559 /* Connect the basic blocks through the taken links */
560 for (i = 0; i < numBlocks; i++) {
561 BasicBlock *curBB = blockList[i];
562 MIR *insn = curBB->lastMIRInsn;
563 unsigned int target = insn->offset;
564 bool isInvoke;
565 const Method *callee;
566
567 findBlockBoundary(method, insn, target, &target, &isInvoke, &callee);
568
569 /* Found a block ended on a branch */
570 if (target != insn->offset) {
571 int j;
572 /* Forward branch */
573 if (target > insn->offset) {
574 j = i + 1;
575 } else {
576 /* Backward branch */
577 j = 0;
578 }
579 for (; j < numBlocks; j++) {
580 if (blockList[j]->firstMIRInsn->offset == target) {
581 curBB->taken = blockList[j];
582 break;
583 }
584 }
585
586 if (j == numBlocks) {
587 LOGE("Target not found for insn %x: expect target %x\n",
588 curBB->lastMIRInsn->offset, target);
589 dvmAbort();
590 }
591 }
592 }
593
594 dvmCompilerMIR2LIR(&cUnit);
595
596 dvmCompilerAssembleLIR(&cUnit);
597
598 dvmCompilerDumpCompilationUnit(&cUnit);
599
600 dvmCompilerArenaReset();
601
602 return cUnit.baseAddr;
603}