HTML5技术

CoreCLR源码探索(二) new是什么 - q303248153(2)

字号+ 作者:H5之家 来源:H5之家 2017-01-12 09:04 我要评论( )

Compiler::compCompile( void ** methodCodePtr, ULONG* methodCodeSize, JitFlags* compileFlags){ // 省略部分代码...... // 转换BasicBlock(基本代码块)到GenTree(语句树) fgImport();ObjectAllocator objectAll

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用于分配内存
  • 节点GT_CALL用于调用构造函数
  • 在上面的代码我们可以看到在生成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; }

    到这里,我们可以知道新建的两个节点带有以下的信息

  • GT_ALLOCOBJ节点
  • 分配内存的帮助函数标识,默认是CORINFO_HELP_NEWFAST
  • GT_CALL节点
  • 构造函数的句柄
  • 在使用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; }

    到这里,我们可以知道新建的两个节点变成了这样

  • GT_CALL节点 (调用帮助函数)
  • 分配内存的帮助函数的句柄
  • GT_CALL节点 (调用Managed函数)
  • 构造函数的句柄
  • 接下来JIT还会对GenTree(语句树)做出大量处理,这里省略说明,接下来我们来看机器码的生成
    函数CodeGen::genCallInstruction负责把GT_CALL节点转换为汇编
    源代码:

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • CoreCLR源码探索(一) Object是什么 - q303248153

      CoreCLR源码探索(一) Object是什么 - q303248153

      2017-01-06 11:04

    • 升讯威ADO.NET增强组件(源码):送给喜欢原生ADO.NET的你 - sheng.chao

      升讯威ADO.NET增强组件(源码):送给喜欢原生ADO.NET的你 - sheng.c

      2016-12-20 11:01

    • html5 Sortable.js 拖拽排序源码分析 - qq281113270

      html5 Sortable.js 拖拽排序源码分析 - qq281113270

      2016-11-04 15:00

    • MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则) - 懒得安分

      MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

      2016-11-03 18:00

    网友点评
    e