Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags* compileFlags) { // 省略部分代码...... // 转换BasicBlock(基本代码块)到GenTree(语句树) fgImport(); ObjectAllocator objectAllocator(this); objectAllocator.Run(); // 省略部分代码...... // 创建本地变量表和计算各个变量的引用计数 lvaMarkLocalVars(); // 省略部分代码...... // 具体化语句树 Lowering lower(this, m_pLinearScan); // PHASE_LOWERING lower.Run(); // 省略部分代码...... // 生成机器码 codeGen->genGenerateCode(methodCodePtr, methodCodeSize); }
到这里你应该大概知道JIT在总体上做了什么事情
接下来我们来看Compiler::fgImport函数,这个函数负责把BasicBlock(基本代码块)转换到GenTree(语句树)
源代码:
void Compiler::fgImport() { // 省略部分代码...... impImport(fgFirstBB); // 省略部分代码...... }
再看Compiler::impImport
源代码:
void Compiler::impImport(BasicBlock* method) { (impPendingList) { PendingDsc* dsc = impPendingList; impPendingList = impPendingList->pdNext; // 省略部分代码...... /* Now import the block */ impImportBlock(dsc->pdBB); } }
再看Compiler::impImportBlock
源代码:
Compiler::impImportBlock(BasicBlock* block) { // 省略部分代码...... pParam->pThis->impImportBlockCode(pParam->block); }
在接下来的Compiler::impImportBlockCode函数里面我们终于可以看到对CEE_NEWOBJ指令的处理了
这个函数有5000多行,推荐直接搜索case CEE_NEWOBJ来看以下的部分
源代码:
Compiler::impImportBlockCode(BasicBlock* block) { CEE_NEWOBJ: op1 = gtNewAllocObjNode(info.compCompHnd->getNewHelper(&resolvedToken, info.compMethodHnd), resolvedToken.hClass, TYP_REF, op1); CALL; // 省略部分代码...... CALL: callTyp = impImportCall(opcode, &resolvedToken, constraintCall ? &constrainedResolvedToken : nullptr, newObjThisPtr, prefixFlags, &callInfo, opcodeOffs);
请记住上面代码中新建的两个GenTree(语句树)节点
在上面的代码我们可以看到在生成GT_ALLOCOBJ类型的节点时还传入了一个newHelper参数,这个newHelper正是分配内存函数的一个标识(索引值)
在CoreCLR中有很多HelperFunc(帮助函数)供JIT生成的代码调用
源代码:
CorInfoHelpFunc CEEInfo::getNewHelper(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_METHOD_HANDLE callerHandle) { // 省略部分代码...... MethodTable* pMT = VMClsHnd.AsMethodTable(); // 省略部分代码...... result = getNewHelperStatic(pMT); // 省略部分代码...... return result; }
看CEEInfo::getNewHelperStatic
源代码:
CorInfoHelpFunc CEEInfo::getNewHelperStatic(MethodTable * pMT) { CorInfoHelpFunc helper = CORINFO_HELP_NEWFAST; // 省略部分代码...... return helper; }
到这里,我们可以知道新建的两个节点带有以下的信息
在使用fgImport生成了GenTree(语句树)以后,还不能直接用这个树来生成机器代码,需要经过很多步的变换
其中的一步变换会把GT_ALLOCOBJ节点转换为GT_CALL节点,因为分配内存实际上是一个对JIT专用的帮助函数的调用
这个变换在ObjectAllocator中实现,ObjectAllocator是JIT编译过程中的一个阶段(Phase)
源代码:
void ObjectAllocator::DoPhase() { // 省略部分代码...... MorphAllocObjNodes(); }
MorphAllocObjNodes用于查找所有节点,如果是GT_ALLOCOBJ则进行转换
源代码:
void ObjectAllocator::MorphAllocObjNodes() { // 省略部分代码...... for (GenTreeStmt* stmt = block->firstStmt(); stmt; stmt = stmt->gtNextStmt) { // 省略部分代码...... bool canonicalAllocObjFound = false; // 省略部分代码...... if (op2->OperGet() == GT_ALLOCOBJ) canonicalAllocObjFound = true; // 省略部分代码...... if (canonicalAllocObjFound) { // 省略部分代码...... op2 = MorphAllocObjNodeIntoHelperCall(asAllocObj); } } }
MorphAllocObjNodeIntoHelperCall的定义
源代码:
// MorphAllocObjNodeIntoHelperCall: Morph a GT_ALLOCOBJ node into an // allocation helper call. GenTreePtr ObjectAllocator::MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj) { // 省略部分代码...... GenTreePtr helperCall = comp->fgMorphIntoHelperCall(allocObj, allocObj->gtNewHelper, comp->gtNewArgList(op1)); return helperCall; }
fgMorphIntoHelperCall的定义
这个函数转换GT_ALLOCOBJ节点到GT_CALL节点,并且获取指向分配内存的函数的指针
源代码:
GenTreePtr Compiler::fgMorphIntoHelperCall(GenTreePtr tree, int helper, GenTreeArgList* args) { tree->ChangeOper(GT_CALL); tree->gtFlags |= GTF_CALL; tree->gtCall.gtCallType = CT_HELPER; tree->gtCall.gtCallMethHnd = eeFindHelper(helper); // 省略部分代码...... tree = fgMorphArgs(tree->AsCall()); return tree; }
到这里,我们可以知道新建的两个节点变成了这样
接下来JIT还会对GenTree(语句树)做出大量处理,这里省略说明,接下来我们来看机器码的生成
函数CodeGen::genCallInstruction负责把GT_CALL节点转换为汇编
源代码: