Dart VM 的相关简介与运行模式解析,这原因我服了

此时 methods 在运行时可以被成功解析和调用,因为已经从内核二进制文件加载了足够的信息,例如它可以解析和调用 main 库中的函数。

package:kernel/ast.dart 定义了描述内核 AST 的类; package:front_end 处理解析 Dart 源代码并从中构建内核 AST。dart::kernel::KernelLoader::LoadEntireProgram是 将内核 AST 反序列化为相应 VM 对象的入口点;pkg/vm/bin/kernel_service.dart 实现了内核服务隔离,runtime/vm/kernel_isolate.cc 将 Dart 实现粘合到 VM 的其余部分; package:vm 承载大多数基于内核的 VM 特定功能,例如各种内核到内核的转换;由于历史原因一些特定于 VM 的转换仍然存在于 package:kernel 中。

最初所有的函数都会有一个占位符,而不是它们的主体的实际可执行代码:它们指向 LazyCompileStub,它只是要求运行时系统为当前函数生成可执行代码,然后 tail-calls 这个新生成的代码。

第一次编译函数时,是通过未优化编译器完成的。

未优化编译器分两遍生成机器代码:

  • 1、遍历函数体的序列化 AST 以生成函数体的控制流图( CFG ),CFG 由填充有中间语言( IL ) 指令的基本块组成。在此阶段使用的 IL 指令类似于基于堆栈的虚拟机的指令:它们从堆栈中获取操作数,执行操作,然后将结果推送到同一堆栈。

实际上并非所有函数都具有实际的 Dart / Kernel AST 主体,例如在 C++ 中定义的本地函数或由 Dart VM 生成的人工 tear-off 函数,在这些情况下,IL 只是凭空创建,而不是从内核 AST 生成。

  • 2、生成的 CFG 使用一对多的底层 IL 指令直接编译为机器代码:每个 IL 指令扩展为多个机器语言指令。

在此阶段没有执行任何优化,未优化编译器的主要目标是快速生成可执行代码。

这也意味着:未优化的编译器不会尝试静态解析内核二进制文件中未解析的任何调用,VM 当前不使用基于虚拟表或接口表的调度,而是使用内联缓存实现动态调用。

内联缓存的原始实现,实际上是修补函数的 native 代码,因此得名内联缓存,内联缓存的想法可以追溯到 Smalltalk-80,请参阅 Smalltalk-80 系统的高效实现。

内联缓存背后的核心思想,是在特定的调用点中缓存方法解析的结果,VM 使用的内联缓存机制包括:

  • 一个调用特定的缓存( dart::UntaggedICData),它将接收者的类映射到一个方法,如果接收者是匹配的类,则应该调用该方法,缓存还存储一些辅助信息,例如调用频率计数器,

  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值