解释执行7_0

quick_entrypoints_x86.S

art_quick_invoke_stub

/*这段注释来自于源码,它展示了调用art_quick_invoke_stub函数时,相关参数在栈中的布局
    * Quick invocation stub (non-static).
    * On entry:
    *  [sp] = return address 返回值地址,这是由函数调用指令自动压入栈的
    *  [sp + 4] = method pointer 代表方法C的ArtMethod对象
    *  [sp + 8] = argument array or null for no argument methods
    *  [sp + 12] = size of argument array in bytes
    *  [sp + 16] = (managed) thread pointer 这是代表调用线程的Thread对象
    *  [sp + 20] = JValue* result
    *  [sp + 24] = shorty
    */
DEFINE_FUNCTION art_quick_invoke_stub      #定义art_quick_invoke_stub函数
    PUSH ebp                        // save ebp
    PUSH ebx                        // save ebx
    PUSH esi                        // save esi
    PUSH edi                        // save edi
    ......                              //处理浮点寄存器、扩展栈空间等
    //下面的循环用于从args中拷贝参数到栈上。此处保留代码中原有的注释
    movl 28(%ebp), %ecx                // ECX = size of args
    movl 24(%ebp), %esi                // ESI = argument array
    leal 4(%esp), %edi                 // EDI = just after Method* in stack arguments
    rep movsb                          // while (ecx--) { *edi++ = *esi++ }

    ......                             //略过其他代码
.Lgpr_setup_finished:                  #至此,参数已经准备好。下面将进入ArtMethod对象的机器码入口
    mov 20(%ebp), %eax                     //EBP+20处保存着ArtMethod* C对象,将其拷贝
                                      EAX中
    #跳转到这个ArtMethod对象机器码入口对应的地方。main
    call *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)
    .....                              //恢复栈,设置返回值到result中
    ret
END_FUNCTION art_quick_invoke_stub

art_quick_to_interpreter_bridge

#DEFINE_FUNCTION是一个宏,用于定义一个函数。下面将定义
#art_quick_to_interpreter_bridge函数
DEFINE_FUNCTION art_quick_to_interpreter_bridge
    #下面这个宏在10.1.3.1.3节介绍过了,执行其中的汇编指令后,栈的布局将变成如图10-4所示的样子。
    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME  ebx, ebx
    mov %esp, %edx                  #将ESP保存到EDX中
    PUSH eax                    #EAX入栈,EAX寄存器的值代表被调用方法的ArtMethod对象。
    PUSH edx                    #EDX入栈,
    pushl %fs:THREAD_SELF_OFFSET      #获取当前线程的Thread对象,并压入栈中
    PUSH eax                      #EAX入栈。
    call SYMBOL(artQuickToInterpreterBridge) #main
                              #调用目标函数
    #参数出栈,恢复到SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME执行后的栈状态
    addl LITERAL(16), %esp
    ......
    #下面三行代码用于处理返回值,xmm为浮点寄存,64位长,而eax,edx为32位长。
    #下面这三行代码执行往后,xmm0的低32位的值来自EAX,高32位的值来自EDX。
    movd %eax, %xmm0
    movd %edx, %xmm1
    punpckldq %xmm1, %xmm0             #将xmm1和xmm0低32位的值组合起来存储到xmm0中。
    #调整栈顶位置
    addl LITERAL(48), %esp
    POP ebp
    POP esi
    POP edi
    RETURN_OR_DELIVER_PENDING_EXCEPTION      #函数返回或抛异常(10.6节将介绍它),main
END_FUNCTION art_quick_to_interpreter_bridge

quick_trampoline_entrypoints.cc

artQuickToInterpreterBridge

extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method,
                    Thread* self, ArtMethod** sp) {
    //参数method代表当前被调用的Java方法,我们用图10-7中的ArtMethod* B表示它
    ScopedQuickEntrypointChecks sqec(self);
    JValue tmp_value;
    /*PopStackedShadowFrame和Thread对栈的管理有关。此处假设是从机器码跳转到解释执行模式,
      并且不是HDeoptimize的情况,那么,该函数返回值deopt_frame为nullptr。 */
    ShadowFrame* deopt_frame = self->PopStackedShadowFrame(
        StackedShadowFrameType::kSingleFrameDeoptimizationShadowFrame, false);

    ManagedStack fragment; //重要:构造一个ManagedStack对象。
    uint32_t shorty_len = 0;
    //如果不是代理方法的话,non_proxy_method就是ArtMethod* B本身。
    ArtMethod* non_proxy_method =
                method->GetInterfaceMethodIfProxy(sizeof(void*));
    const DexFile::CodeItem* code_item = non_proxy_method->GetCodeItem();
    const char* shorty = non_proxy_method->GetShorty(&shorty_len);

    JValue result; //存储方法调用的返回值
    if (deopt_frame != nullptr) {
        ..... //和HDeoptimize有关,后续章节再介绍它
    } else {
        const char* old_cause = ......;
        uint16_t num_regs = code_item->registers_size_;
        //创建代表ArtMethod B的栈帧对象ShawFrame。注意,它的link_取值为nullptr,
        //dex_pc_取值为0
        ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
            CREATE_SHADOW_FRAME(num_regs, /* link */ nullptr, method,
                            /* dex pc */ 0);
        ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
        size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_;
        //借助BuildQuickShadowFrameVisitor将调用参数放到shadow_frame对象中
        BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
                        method->IsStatic(), shorty, shorty_len,
                        shadow_frame, first_arg_reg);
        shadow_frame_builder.VisitArguments();
        //判断ArtMethod* B所属的类是否已经初始化
        const bool needs_initialization =
            method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
        //重要:下面两行代码将fragment和shadow_frame放到Thread类对应的成员变量中去处理
        //我们后续再讨论这部分内容
        self->PushManagedStackFragment(&fragment);
        self->PushShadowFrame(shadow_frame);
        ......
        //如果ArtMethod B所属类没有初始化,则先初始化它。类初始化就是调用ClassLinker的Ensure-
        //Initialized函数
        if (needs_initialization) {
                StackHandleScope<1> hs(self);
            Handle<mirror::Class> h_class(hs.NewHandle(
                    shadow_frame->GetMethod()->GetDeclaringClass()));
            if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(
                        self, h_class, true, true)) {......}
        }
        //解释执行的入口函数
        result = interpreter::EnterInterpreterFromEntryPoint(self, code_item, shadow_frame);//main
        }
        //和Thread对栈的管理有关
        self->PopManagedStackFragment(fragment);
        //根据sp的位置找到本方法的调用者,以图10-7为例,即找到ArtMethod* A,是它调用了本方
        //法(对应为ArtMethod* B)。
        ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
        if (UNLIKELY(Dbg::IsForcedInterpreterNeededForUpcall(self, caller))) {
            //和HDeoptimize有关
         //和HDeoptimize有关
            self->PushDeoptimizationContext(result, shorty[0] == 'L',
                        /* from_code */ false, self->GetException());
            self->SetException(Thread::GetDeoptimizationException());
        }
        return result.GetJ();      //artQuickToInterpreterBridge返回
    }
}

interpreter.h/cc

EnterInterpreterFromEntryPoint

extern JValue EnterInterpreterFromEntryPoint(
        Thread* self,                        //代表调用线程的Thread对象
    const DexFile::CodeItem* code_item,      //方法B的dex指令码内容
    ShadowFrame* shadow_frame                //方法B所需的参数
);

JValue EnterInterpreterFromEntryPoint(Thread* self,
            const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame) {
    ......
    //下面这段代码和JIT有关,相关知识见本章后续对JIT的介绍
    jit::Jit* jit = Runtime::Current()->GetJit();
    if (jit != nullptr) {
        jit->NotifyCompiledCodeToInterpreterTransition(self,
                        shadow_frame->GetMethod());
    }
    //关键函数
    return Execute(self, code_item, *shadow_frame, JValue());//main
}

Execute

static inline JValue Execute(Thread* self,
        const DexFile::CodeItem* code_item,
        ShadowFrame& shadow_frame,JValue result_register,
        bool stay_in_interpreter = false) {
    /*注意stay_in_interpreter参数,它表示是否强制使用解释执行模式。默认为false,它表示如果
      方法B存在jit编译得到的机器码,则转到jit去执行。  */
    /*下面这个if条件的判断很有深意。我们在本章解释图10-5里ShadowFrame成员变量时曾说过,如果
      是HDeoptimize的情况,ShadowFrame的dex_pc_不是0(这表示有一部分指令以机器码方式执
      行)。如果dex_pc_为0的话,则表示该方法从一开始就将以解释方式执行。我们称这种情况为纯解
      释执行的方法,此时,我们就需要检查它是否存在JIT的情况。  */
    if (LIKELY(shadow_frame.GetDexPC() == 0)) {
        instrumentation::Instrumentation* instrumentation =
                    Runtime::Current()->GetInstrumentation();
        ArtMethod *method = shadow_frame.GetMethod();
        if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
            instrumentation->MethodEnterEvent(self,
                shadow_frame.GetThisObject(code_item->ins_size_),
                method, 0);
        }
        //判断这个需要纯解释执行的方法是否经过JIT编译了
        if (!stay_in_interpreter) {
            jit::Jit* jit = Runtime::Current()->GetJit();
            if (jit != nullptr) {
                jit->MethodEntered(self, shadow_frame.GetMethod());
                if (jit->CanInvokeCompiledCode(method)) {
                    ...... //转入jit编译的机器码去执行并返回结果
                }
     } }
    } //dex_pc_是否为0判断结束

    ...... //下面是解释执行的处理逻辑
    ArtMethod* method = shadow_frame.GetMethod();
    //transaction_active和dex2oat编译逻辑有关,完整虚拟机运行时候返回false
    bool transaction_active = Runtime::Current()->IsActiveTransaction();
    //是否略过Access检查,即判断是否有权限执行本方法。大部分情况下该if条件是满足的
    if (LIKELY(method->SkipAccessChecks())) {
        /*main,在ART虚拟机中,解释执行的实现方式有三种,由kInterpreterImplKind取值来控制:
          (1)kMterpImplKind:根据不同CPU平台,采用对应汇编语言编写的,基于goto逻辑的实现。
              这也是kInterpreterImplKind的默认取值。
          (2)kSwitchImplKind:由C++编写,基于switch/case逻辑实现。
          (3)kComputedGotoImplKind:由C++编写,基于goto逻辑实现。根据代码中的注释所述,
              这种实现的代码不支持使用clang编译器。
           这三种实现的思路大同小异,首选自然是速度更快的汇编处理kMterpImplKind模式。
           为了展示一些dex指令的处理逻辑,笔者拟讨论kSwtichImplKind模式的相关代码。 */
    if (kInterpreterImplKind == kMterpImplKind) {
        if (transaction_active) {.....}
        else if (UNLIKELY(!Runtime::Current()->IsStarted())) {
            ...... //针对dex2oat的情况
        } else {
            ......
            //ExecuteMterpImpl函数的定义由汇编代码实现,main
            bool returned = ExecuteMterpImpl(self, code_item, &shadow_frame,
                                &result_register);
            }
        } else if (kInterpreterImplKind == kSwitchImplKind) {
            if (transaction_active) {......
            } else {
                //kSwitchImplKind的入口函数。注意,最后一个参数的值为false。main
                return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame,
                                    result_register, false);
            }
        } else { //kInterpreterImplKind取值为kComputedGotoImplKind的情况,main
            if (transaction_active) {......}
        else {
                return ExecuteGotoImpl<false, false>(self, code_item, shadow_frame,
                            result_register);
            }
        }
    }
    ......
}

interpreter_switch_impl.cc

ExecuteSwitchImpl

template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
                    ShadowFrame& shadow_frame, JValue result_register,
                    bool interpret_one_instruction) {
    //注意上文Execute代码中调用ExeucteSwitchImpl时设置的最后一个参数为false,所以此处inter-
    //pret_one_instruction为false。
    constexpr bool do_assignability_check = do_access_check;
    ......
    //dex_pc指向要执行的dex指令
    uint32_t dex_pc = shadow_frame.GetDexPC();
    const auto* const instrumentation =
                Runtime::Current()->GetInstrumentation();
    //insns代表方法B的dex指令码数组
    const uint16_t* const insns = code_item->insns_;
    const Instruction* inst = Instruction::At(insns + dex_pc);
    uint16_t inst_data;
    //方法B对应的ArtMethod对象
    ArtMethod* method = shadow_frame.GetMethod();
    jit::Jit* jit = Runtime::Current()->GetJit();
    ......
    do { //遍历方法B的dex指令码数组,main
        dex_pc = inst->GetDexPc(insns);
        shadow_frame.SetDexPC(dex_pc);
        ......
        inst_data = inst->Fetch16(0);
        /*main,借助switch/case,针对每一种dex指令进行处理。注意,处理每种dex指令前,都有一个PREAMBLE
          宏,该宏就是调用instrumentation的DexPcMovedEvent函数。10.5节将单独介绍和instrumentation相关的内容。  */
        switch (inst->Opcode(inst_data)) {//main
            case Instruction::NOP: //处理NOP指令
                PREAMBLE();
                //Next_1xx是Instruction类的成员函数,用于跳过本指令的参数,使之指向下一条
                //指令的开头。1xx是dex指令码存储格式的一种。读者可不用管它。
                inst = inst->Next_1xx();
                break;
            ...... //其他dex指令码的处理
        case Instruction::INVOKE_DIRECT: { //invoke-direct指令码的处理
            PREAMBLE();
            //DoInvoke的分析见下文。main
            bool success = DoInvoke<kDirect, false, do_access_check>(
                self, shadow_frame, inst, inst_data, &result_register);
            /*Next_3xx也是Instruction类的成员函数。下面的POSSIBLY_HANDLE_PENDING_EXCEPTION
              是一个宏,如果有异常发生,则进入异常处理,否则将调用Next_3xx函数使得inst指向
              下一条指令。整个解释执行的流程就这样循环直到所有指令码处理完毕。main   */
              POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
              break;
                }
            ......
        }
    } while (!interpret_one_instruction); //循环
    //记录dex指令执行的位置并更新到shadow_frame中
    shadow_frame.SetDexPC(inst->GetDexPc(insns));
    return result_register;
}

interpreter_common.h/cc

DoInvoke

template<InvokeType type, bool is_range, bool do_access_check>
static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame,
            const Instruction* inst, uint16_t inst_data, JValue* result) {
    /*先观察DoInvoke的参数:
     (1)模板参数type:指明调用类型,比如kStatic、kDirect等。
     (2)模板参数is_range:如果该方法有多于五个参数的话,则需要使用invoke-xxx-range这样
         的指令。
     (3)模板参数do_access_check:是否需要访问检查。即检查是否有权限调用invoke指令的目标
         方法C。
     (4)shadow_frame:方法B对应的ShadowFrame对象。
     (5)inst:invoke指令对应的Instruction对象。
     (6)inst_data:invoke指令对应的参数。
     (7)result:用于存储方法C执行的结果。 */
    //method_idx为方法C在dex文件里method_ids数组中的索引
    const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() :
                inst->VRegB_35c();
    //找到方法C对应的对象。它作为参数存储在方法B的ShawdowFrame对象中。
    const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
    Object* receiver = (type == kStatic) ? nullptr :
                shadow_frame.GetVRegReference(vregC);
    //sf_method代表ArtMethod* B。
    ArtMethod* sf_method = shadow_frame.GetMethod();
    /*FindMethodFromCode用于查找代表目标方法C对应的ArtMethod对象,即ArtMethod* C。其内
      部会根据do_access_check的情况检查方法B是否有权限调用方法C。
      注意,FindMethodFromCode函数是根据不同调用类型(kStatic、kDirect、kVirtual、kSuper、
      kInterface)以找到对应的ArtMethod对象的关键代码。这部分内容请读者自行阅读。*/
    ArtMethod* const called_method = FindMethodFromCode<type,
            do_access_check>( method_idx, &receiver, sf_method, self);
    //假设方法C对应的ArtMethod对象找到了,所以,called_method不为空。
    if (UNLIKELY(called_method == nullptr)) {.......}
    else if (UNLIKELY(!called_method->IsInvokable())) {......}
    else {
        //下面这段代码和JIT有关,我们留待后续章节再来介绍。
        jit::Jit* jit = Runtime::Current()->GetJit();
        if (jit != nullptr) {......}
        ......            //instrumentation的处理
        return DoCall<is_range, do_access_check>(called_method, self,
                        shadow_frame, inst, inst_data,result);
    }
}

DoCall

template<bool is_range, bool do_assignability_check>
bool DoCall(ArtMethod* called_method, Thread* self,
        ShadowFrame& shadow_frame,const Instruction* inst,
        uint16_t inst_data, JValue* result) {
    const uint16_t number_of_inputs =
        (is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data);
    //kMaxVarArgsRegs为编译常量,值为5
    uint32_t arg[Instruction::kMaxVarArgRegs] = {};
    uint32_t vregC = 0;
    if (is_range) {......}
        else {
            vregC = inst->VRegC_35c();
            inst->GetVarArgs(arg, inst_data); //将调用方法C的参数存储到arg数组中,main
    }
    //调用DoCallCommon,我们接着看这个函数
    return DoCallCommon<is_range, do_assignability_check>( called_method,
            self, shadow_frame,result, number_of_inputs, arg, vregC);
}

DoCallCommon

template <bool is_range, bool do_assignability_check, size_t kVarArgMax>
static inline bool DoCallCommon(ArtMethod* called_method,
        Thread* self, ShadowFrame& shadow_frame, JValue* result,
        uint16_t number_of_inputs,uint32_t (&arg)[kVarArgMax],uint32_t vregC) {
    bool string_init = false;
    //和String类的构造函数有关。此处不拟讨论。
    if (UNLIKELY(called_method->GetDeclaringClass()->IsStringClass()
                && called_method->IsConstructor())) {.....}

    const DexFile::CodeItem* code_item = called_method->GetCodeItem();
    uint16_t num_regs;
    if (LIKELY(code_item != nullptr)) {
        num_regs = code_item->registers_size_;
    } else {
        num_regs = number_of_inputs;
    }
    uint32_t string_init_vreg_this = is_range ? vregC : arg[0];
    if (UNLIKELY(string_init)) {......}

    size_t first_dest_reg = num_regs - number_of_inputs;
    ......
    //创建方法C所需的ShadowFrame对象。
    ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
            CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, 0);
    ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
    if (do_assignability_check) {
        ...... //不考虑这种情况,读者可自行阅读
    } else {
        size_t arg_index = 0;
        if (is_range) {......}
        else {
        //从调用方法B的ShadowFrame对象中拷贝方法C所需的参数到C的ShadowFrame对象里
        for (; arg_index < number_of_inputs; ++arg_index) {
            AssignRegister(new_shadow_frame, shadow_frame,
                        first_dest_reg + arg_index, arg[arg_index]);
            }
        }
        ......
    }
    //准备方法C对应的ShadowFrame对象后,现在将考虑如何跳转到目标方法C。
    if (LIKELY(Runtime::Current()->IsStarted())) {
        ArtMethod* target = new_shadow_frame->GetMethod();
        //如果处于调试模式,或者方法C不存在机器码,则调用
        //ArtInterpreterToInterpreterBridge函数,显然,它是解释执行的继续。
        if (ClassLinker::ShouldUseInterpreterEntrypoint(
                target, target->GetEntryPointFromQuickCompiledCode())) {
            ArtInterpreterToInterpreterBridge(self, code_item,
                                new_shadow_frame,result);//main
        } else {
            //如果可以用机器码方式执行方法C,则调用ArtInterpreterToCompiledCodeBridge,
            //它将从解释执行模式进入机器码执行模式。
            ArtInterpreterToCompiledCodeBridge(
                self, shadow_frame.GetMethod(), code_item,
                new_shadow_frame, result);
        }
    } else { //dex2oat中的处理。因为dex2oat要执行诸如类的初始化方法"<clinit>",这些方法都
             //采用解释执行模式来处理的。
        UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result,
                                first_dest_reg);
    }
    }
    ......
    return !self->IsExceptionPending();
}

ArtInterpreterToInterpreterBridge

void ArtInterpreterToInterpreterBridge(Thread* self,
                const DexFile::CodeItem* code_item,
                ShadowFrame* shadow_frame, JValue* result) {
    ......
    self->PushShadowFrame(shadow_frame); //方法C对应的ShadowFrame对象入栈
    ArtMethod* method = shadow_frame->GetMethod();
    const bool is_static = method->IsStatic();
    if (is_static) {
        //如果方法C为静态方法,则判断该方法所属的类是否初始化过了,如果没有,则先初始化这个类。
        mirror::Class* declaring_class = method->GetDeclaringClass();
        if (UNLIKELY(!declaring_class->IsInitialized())) {
            StackHandleScope<1> hs(self);
            HandleWrapper<Class> h_declaring_class(hs.NewHandleWrapper(
                                &declaring_class));
            if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
                self, h_declaring_class, true, true))) {......}
        }
    }
    //如果不是JNI方法,则调用Execute执行该方法。Execute函数我们在10.2.3节介绍过它了。
    if (LIKELY(!shadow_frame->GetMethod()->IsNative())) {
        result->SetJ(Execute(self, code_item, *shadow_frame,//main
            JValue()).GetJ());
    } else {......                  /*dex2oat中的处理*/ }
    self->PopShadowFrame();      //方法C对应的ShadowFrame出栈
}

ArtInterpreterToCompiledCodeBridge

void ArtInterpreterToCompiledCodeBridge(Thread* self,
            ArtMethod* caller, const DexFile::CodeItem* code_item,
                ShadowFrame* shadow_frame, JValue* result) {
    ArtMethod* method = shadow_frame->GetMethod();
    if (method->IsStatic()) {
        //检查方法C所属类是否完成了初始化,如果没有,则先初始化该类。
        ......
    }
    uint16_t arg_offset = (code_item == nullptr) ? 0 :
                    code_item->registers_size_ - code_item->ins_size_;
    jit::Jit* jit = Runtime::Current()->GetJit();
    ...... //JIT相关,此处先略过
    //调用ArtMethod* C的Invoke函数。直接来看这个函数的代码。
    method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
            (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
            result,
            method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty());
}

art_method.cc

Invoke

void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size,
                JValue* result,const char* shorty) {
    /* 注意参数
       (1)args:方法C所需的参数。它是一个数组,元素个数为args_size。
       (2)result:存储方法C调用结果的对象。
       (3)shorty:方法C的简短描述。    */
    //栈操作,详情见下文分析
    ManagedStack fragment;
    self->PushManagedStackFragment(&fragment);//

    Runtime* runtime = Runtime::Current();
    if (UNLIKELY(!runtime->IsStarted() ||
        Dbg::IsForcedInterpreterNeededForCalling(self, this))) {......}
    else {
        //再次判断方法C是否存在机器码
        bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
        if (LIKELY(have_quick_code)) {
            //如果是非静态函数,则调用art_quick_invoke_stub函数,否则调用
            //art_quick_invoke_static_stub函数。这两个函数也是由汇编代码编写。我们看
            //其中的art_quick_invoke_stub函数。
            if (!IsStatic()) {
            (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
            } else {
                (*art_quick_invoke_static_stub)(this, args, args_size, self,
                    result, shorty);
            }
            //和HDeoptimize有关。详情见下文。
            if (UNLIKELY(self->GetException() ==
                        Thread::GetDeoptimizationException())) {
                self->DeoptimizeWithDeoptimizationException(result);
            }
        }
    ......
    self->PopManagedStackFragment(fragment);
}