Android ART虚拟机执行引擎-Interpreter(八)

ART虚拟机是一个Interpreter+JIT+AOT的共存环境。

ART虚拟机中解释器的实现源码在art/runtime/interpreter中,其中与平台相关的汇编代码保存在目录art/runtime/interpreter/mterp中,因为interpreter有两个版本,一种是C语言实现的,一种是汇编版本的。

汇编版本的解释器执行效率高,但是要对不同的硬件架构进行适配。

解释器的任务就是解释字节码并执行,它的输入源可以是原始的编程语言,也可以是类似于java中的byteCode的中间表示。解释型的语言并不代表完全不需要编译,事实上很多解释器的内部实现也是先编译源码,然后才去执行,只是这个编译的过程是透明的。

1)Android虚拟机的解释器,输入源特指的是Android字节码。Android虚拟机的解释器有switch类型和goto类型。

其中switch类型对应的源码如下:

art/runtime/interpreter/Interpreter_switch_impl.cc

其中的参数self表示当前线程,shadow_frame类似于java虚拟机中的stack frame,用于存储方法中的本地变量表、操作栈方法出口等信息。

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) {
  constexpr bool do_assignability_check = do_access_check;
  self->VerifyStack();

  uint32_t dex_pc = shadow_frame.GetDexPC();
  const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
  const uint16_t* const insns = code_item->insns_;
  const Instruction* inst = Instruction::At(insns + dex_pc);
  uint16_t inst_data;
  ArtMethod* method = shadow_frame.GetMethod();
  jit::Jit* jit = Runtime::Current()->GetJit();

  // TODO: collapse capture-variable+create-lambda into one opcode, then we won't need
  // to keep this live for the scope of the entire function call.
  std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder;
  size_t lambda_captured_variable_index = 0;
  do {
    dex_pc = inst->GetDexPc(insns);
    shadow_frame.SetDexPC(dex_pc);
    TraceExecution(shadow_frame, inst, dex_pc);
    inst_data = inst->Fetch16(0);
    switch (inst->Opcode(inst_data)) {
      case Instruction::NOP:
        PREAMBLE();
        inst = inst->Next_1xx();
        break;
      case Instruction::MOVE:
        PREAMBLE();
        shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
                             shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
        inst = inst->Next_1xx();
        break;
......
     }
  } while (!interpret_one_instruction);
  // Record where we stopped.
  shadow_frame.SetDexPC(inst->GetDexPc(insns));
  return result_register;
}  // NOLINT(readability/fn_size)

switch类型的解释器,利用case分别处理每一条字节码,内部结构清晰简洁,但是效率较低,因为每条指令的执行都需要很长的判断过程-从case1开始,直到匹配到正确的目标指令,循环往复。

2)goto类型是对switch类型解释器效率缺陷的改进。goto类型解释器将指令和其处理函数建立了一对一的联系。具体实现如下:

art/runtime/interpreter/Interpreter_goto_table_impl.cc

template<bool do_access_check, bool transaction_active>
JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame,
                       JValue result_register) {
  // Define handler tables:
  // - The main handler table contains execution handlers for each instruction.
  // - The alternative handler table contains prelude handlers which check for thread suspend and
  //   manage instrumentation before jumping to the execution handler.
  static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = {
    {
    // Main handler table.
#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,
#include "dex_instruction_list.h"
      DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUCTION_HANDLER
    }, {
    // Alternative handler table.
#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&alt_op_##code,
#include "dex_instruction_list.h"
      DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUCTION_HANDLER
    }
  };

  constexpr bool do_assignability_check = do_access_check;
  if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
    LOG(FATAL) << "Invalid shadow frame for interpreter use";
    return JValue();
  }
  self->VerifyStack();

  uint32_t dex_pc = shadow_frame.GetDexPC();
  const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
  uint16_t inst_data;
  const void* const* currentHandlersTable;
  UPDATE_HANDLER_TABLE();
  std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder;
  size_t lambda_captured_variable_index = 0;
  const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
  ArtMethod* method = shadow_frame.GetMethod();
  jit::Jit* jit = Runtime::Current()->GetJit();

  // Jump to first instruction.
  ADVANCE(0);
  UNREACHABLE_CODE_CHECK();

  HANDLE_INSTRUCTION_START(NOP)
    ADVANCE(1);
  HANDLE_INSTRUCTION_END();

  HANDLE_INSTRUCTION_START(MOVE)
    shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
    ADVANCE(1);
  HANDLE_INSTRUCTION_END();
......
// Create alternative instruction handlers dedicated to instrumentation.
// Return instructions must not call Instrumentation::DexPcMovedEvent since they already call
// Instrumentation::MethodExited. This is to avoid posting debugger events twice for this location.
// Note: we do not use the kReturn instruction flag here (to test the instruction is a return). The
// compiler seems to not evaluate "(Instruction::FlagsOf(Instruction::code) & kReturn) != 0" to
// a constant condition that would remove the "if" statement so the test is free.
#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v)                        \
  alt_op_##code: {                                                                            \
    if (UNLIKELY(instrumentation->HasDexPcListeners())) {                                     \
      Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);                 \
      instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc);  \
    }                                                                                         \
    UPDATE_HANDLER_TABLE();                                                                   \
    goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code];               \
  }
#include "dex_instruction_list.h"
      DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUMENTATION_INSTRUCTION_HANDLER
}  // NOLINT(readability/fn_size)

首先定义了一个数组handlersTable,这是一个二维数组,第一维是两个Handler Table,其中main Handler table是正常情况下指令处理函数的集合,alternative handler table是在调试情况下其作用,如单步调试,tracer调试等。

#define HANDLE_INSTRUCTION_START(opcode) op_##opcode:  // NOLINT(whitespace/labels)
#define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK()
HANDLE_INSTRUCTION_START这个宏用于定义一个opcode的label,在定义handlerstable时,每一项的取值都是&&op_##code (#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,),这样opcode就和它的处理代码建立了关联。

ADVANCE(1);宏用于取出并跳转到下一条指令去执行。


3)具体使用的解释器是goto,switch,还是assemble,首先由变量kInterpreterImplKind决定,其初始值是kMterpImplKind,汇编解释器。

art/runtime/interpreter/Interpreter.cc

static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;
既然是由变量来决定,就可以在后期运行过程中根据实际情况做调整。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: PHP公式解释器(PHP-Formula-Interpreter)是一个用于解释和计算数学公式的工具。它可以通过解析和评估用户提供的数学表达式来计算结果。 PHP-Formula-Interpreter可以处理包括基本算术运算符(例如加法、减法、乘法和除法)、指数计算(例如乘方)、三角函数、对数函数和常量(如π和e)等各种数学运算。它还可以处理复杂的公式,包括括号和多个运算符的组合。 使用PHP-Formula-Interpreter,开发者可以将数学公式嵌入到他们的PHP应用程序中,并通过调用相应的函数来计算结果。这使得开发者能够在应用程序中实现复杂的数学计算功能,而无需编写繁琐的算法或手动计算。 PHP-Formula-Interpreter的工作原理是通过将数学表达式解析成语法树,然后按照树的结构逐步计算出结果。它使用递归下降解析器的概念来解析表达式,并通过遍历语法树来执行计算。 使用PHP-Formula-Interpreter需要一定的数学和编程知识,因为开发者需要了解数学表达式的语法和运算规则,并使用合适的函数和参数进行调用。然而,一旦掌握了使用方法,在PHP应用程序中实现数学计算功能将变得更加简单和高效。 总而言之,PHP-Formula-Interpreter是一个强大的工具,可以帮助开发者在PHP应用程序中轻松解析和计算数学公式。它不仅简化了数学计算的实现过程,还提供了灵活性和可扩展性,使开发者能够在应用程序中使用各种数学运算。 ### 回答2: PHP公式解释器是一种用于解析和执行数学和逻辑表达式的工具。它可以解析包含变量、常数、操作符和函数的表达式,并返回计算结果。这种解释器通常被用于开发动态网页,特别是在处理表单和用户输入时。 使用PHP公式解释器,我们可以创建各种复杂的计算和逻辑表达式。它允许我们使用算术运算符(如加法、减法、乘法、除法)和逻辑运算符(如与、或、非)来操作和比较数值。 此外,PHP公式解释器还提供了许多内置函数,如求平方根、取整、绝对值等。我们还可以自定义函数,以满足特定的要求和计算需求。 使用PHP公式解释器可以有效地处理用户输入。例如,在一个表单中,我们可以使用公式解释器来验证用户输入的数值是否符合特定的规则。如果输入不符合要求,我们可以返回错误信息,提示用户重新输入。 总而言之,PHP公式解释器是一个重要的工具,可以在动态网页开发中处理各种数学和逻辑运算。通过将变量、常数和运算符结合起来,我们可以创建有趣和功能丰富的表达式,并获得相应的计算结果。它为我们提供了更大的灵活性和控制力,以满足各种计算需求。 ### 回答3: php-formula-interpreter 是一个用于解析和计算数学公式的 PHP 库。它可以帮助开发人员在他们的 PHP 项目中处理复杂的数学计算。 使用 php-formula-interpreter,我们可以轻松地解释和运行包含数学函数、变量和运算符的公式。这个库提供了一种灵活和强大的方式来处理数学表达式,无论是简单的加法和乘法,还是复杂的三角函数和对数等。 该库具有用户友好的界面和简单的使用方法。首先,我们需要加载库的包。然后,我们可以将数学表达式作为字符串传递给库的解释器解释器将分析表达式并返回计算结果。 php-formula-interpreter 还提供了一些额外的功能,使开发人员能够控制解释和计算过程。例如,我们可以定义自己的数学函数和变量,并使用它们来编写更复杂的公式。我们还可以指定数值的精度和规则,以满足特定需求。 总之,php-formula-interpreter 是一个方便和实用的 PHP 库,用于解析和计算数学公式。它提供了简单易用的接口,并支持各种数学函数和运算符。无论是简单的计算还是复杂的数学公式,这个库都可以帮助我们实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值