dalvik还是要显得与众不同。
在一个从Java源码编译到JVM字节码的编译器(如javac、ECJ)里,一个“编译单元”(CompilationUnit)指的是一个Java源文件。而在Dalvik VM的JIT里也有一个结构体名为“CompilationUnit”,这个千万不能跟Java源码级的编译单元弄混了——它在这里指的就是一个“trace”。
http://hllvm.group.iteye.com/group/topic/17798
万能的WIKI是这么说的:
http://en.wikipedia.org/wiki/Single_Compilation_Unit
看起来WIKI的解释和java JVM的定义差不多。
在源代码里,它是这么定义的:
typedef struct CompilationUnit {
int numInsts;
int numBlocks;
GrowableList blockList;
const Method *method;
#ifdef ARCH_IA32
int exceptionBlockId; // the block corresponding to exception handling
#endif
const JitTraceDescription *traceDesc;
LIR *firstLIRInsn;
LIR *lastLIRInsn;
LIR *literalList; // Constants
LIR *classPointerList; // Relocatable
int numClassPointers;
LIR *chainCellOffsetLIR;
GrowableList pcReconstructionList;
int headerSize; // bytes before the first code ptr
int dataOffset; // starting offset of literal pool
int totalSize; // header + code size
AssemblerStatus assemblerStatus; // Success or fix and retry
int assemblerRetries; // How many times tried to fix assembly
unsigned char *codeBuffer;
void *baseAddr;
bool printMe;
bool allSingleStep;
bool hasClassLiterals; // Contains class ptrs used as literals
bool hasLoop; // Contains a loop
bool hasInvoke; // Contains an invoke instruction
bool heapMemOp; // Mark mem ops for self verification
bool usesLinkRegister; // For self-verification only
int profileCodeSize; // Size of the profile prefix in bytes
int numChainingCells[kChainingCellGap];
LIR *firstChainingLIR[kChainingCellGap];
LIR *chainingCellBottom;
struct RegisterPool *regPool;
int optRound; // round number to tell an LIR's age
jmp_buf *bailPtr;
JitInstructionSetType instructionSet;
/* Number of total regs used in the whole cUnit after SSA transformation */
int numSSARegs;
/* Map SSA reg i to the Dalvik[15..0]/Sub[31..16] pair. */
GrowableList *ssaToDalvikMap;
/* The following are new data structures to support SSA representations */
/* Map original Dalvik reg i to the SSA[15..0]/Sub[31..16] pair */
int *dalvikToSSAMap; // length == method->registersSize
BitVector *isConstantV; // length == numSSAReg
int *constantValues; // length == numSSAReg
/* Data structure for loop analysis and optimizations */
struct LoopAnalysis *loopAnalysis;
/* Map SSA names to location */
RegLocation *regLocation;
int sequenceNumber;
/*
* Set to the Dalvik PC of the switch instruction if it has more than
* MAX_CHAINED_SWITCH_CASES cases.
*/
const u2 *switchOverflowPad;
JitMode jitMode;
int numReachableBlocks;
int numDalvikRegisters; // method->registersSize + inlined
BasicBlock *entryBlock;
BasicBlock *exitBlock;
BasicBlock *puntBlock; // punting to interp for exceptions
BasicBlock *backChainBlock; // for loop-trace
BasicBlock *curBlock;
BasicBlock *nextCodegenBlock; // for extended trace codegen
GrowableList dfsOrder;
GrowableList domPostOrderTraversal;
BitVector *tryBlockAddr;
BitVector **defBlockMatrix; // numDalvikRegister x numBlocks
BitVector *tempBlockV;
BitVector *tempDalvikRegisterV;
BitVector *tempSSARegisterV; // numSSARegs
bool printSSANames;
void *blockLabelList;
bool quitLoopMode; // cold path/complex bytecode
} CompilationUnit;
不得不说,这个数据结构实在是太大了,这样真的好维护吗?
但对于程序来说,它复杂度和它重要性一般是成正比的。
CU是一个贯穿整个程序的结构体,一个trace包含的几乎所有信息,都被CU整合了在一次,这也是因此为何CU如此复杂和臃肿的原因。
你若想了解一个软件的执行思路,必须的吃透它最主要的数据结构,以及它的组织方式。因为数据结构的组织方式,决定了软件该如何处理它。例如,使用了大量指针的数据结构,很明显是必须要使用动态链表来保存数据的。而之所以使用了大量的动态链表,是因为数据结构要描述的对象大小的不确定性。例如对于dalvik来说,trace的长度不一定,你不能假定它是64K或者是128K。静态减少了复杂度,但是对于不规则的对象却无法处理;动态引入了弹性,但是必然会引入复杂度。
从结构体开头就可以看到,”const Method *method;“。这是一个指针,那么对于一个CU来说,是必须要对应一个method,但是一个method可能包含了几个CU.
比较重要的还有,这是整个CU的入口,出口对应的LIR:
LIR *firstLIRInsn;
LIR *lastLIRInsn;
LIR *literalList; // Constants
LIR *classPointerList; // Relocatable
当CU被编译成为LIR的时候,LIR是保存成为链表形式的。
除此之外,CU还会包含大量的BB,整个CU的入口,出口等。
BasicBlock *entryBlock;
BasicBlock *exitBlock;
BasicBlock *puntBlock; // punting to interp for exceptions
BasicBlock *backChainBlock; // for loop-trace
BasicBlock *curBlock;
BasicBlock *nextCodegenBlock; // for extended trace codegen
之前所说过,BB是只有一个IN/OUT的branch,它自然也包含了MIR。因此,BB,MIR,LIR在CU这里,一家都齐全了。