[WebKit] JavaScriptCore解析--高级篇(二) 类型推导(Type Inference)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 类型推导是DFG JIT最重要的一个基础,WebKit官网对此做了一点解释,翻译如下做为学习参考。 Type inference通过profiling values来做到的,先是预测对哪些类型操作进行分析,再添加类型检查,最后基于类型检查的结果建立类型统计数据。

类型推导是DFG JIT最重要的一个基础,WebKit官网对此做了一点解释,翻译如下做为学习参考。


Type inference通过profiling values来做到的,先是预测对哪些类型操作进行分析,再添加类型检查,最后基于类型检查的结果建立类型统计数据。


用下面的例子来说明这个过程:

o.x * o.x + o.y * o.y


其中o是一个对象,x和y是它的属性,它们不是访问器(accessor),只是一般的属性。我们也可以说这两个属性值会返回double类型数值,但也有时会返回整型数据。JavaScriptCore通过用int32来表现整型数据,而不是用double类型。

  1. 对于表达式o.x,先要检查o有没有任何特殊的访问属性的处理。比如是一个DOM对象,那么它的属性访问操作就不是可见的。如果没有特殊的处理,JSC会在对象的属性中找出名字为'x'的属性。对象都一个将字串映射到值或访问器(accessor)的表,如果所找的字串指向一个访问器,那么这个访问器就会被调用,如果指一个值,那就直接返回。如'x'没能在对象o中找到,就到它的原型中依次找下去。类型推导对这部分操作没有什么作用。


  2. 二元的相乘操作,如'o.x * o.x',首先检查操作数(operands)的类型。如果操作数是个对象,就是调用它的valueOf方法。如果操作数是个字串,就要先转换为数值。当两个操作数已经被转为等价的数值(如果可以的话)后,JSC会再检查它们是不是全是整数,如果是则执行整型的相乘。如果溢出,就会用double类型的相乘再算一次。如果有一个操作数是double数值,它们都会转为double型,并执行double型的相乘操作。因此'o.x*o.x'所返回的结果,要么是整数,要么是浮点数(double)。


  3. 对于表达式'o.x*o.x + o.y*o.y'和上面类似,只是要多考虑一下操作数是字串的情况,中间的'+'可能会是字串合并的操作。不过在这里,我们可以很容易地确定,返回值仍然要么是整数,要么是浮点数(double)。


JSC的类型推导就是如果我可以猜到输入的数据类型,就可以给出数值操作后最可能返回的类型,以及其路径。这里使用了一系列归纳步骤, 如果我们可以预计它的输入就可以预计它的输出。对于一些非局部变量,比如从堆中取出的值(如o.x),和函数返回值,我们都称为堆值(heap values), 所有将heap values赋给局部变量的操作都视为堆操作(heap oerpations). 类型预测使用到了value profiling, LLInt和Baseline JIT都会记录在任何heap oerpation中记录下最常用的数值。每个堆操作都有一个对应的value profile bucket, 每一个value profile bucket都会存储一个最近值。


简单的看,JSC的类型推导就是把value profile中最常用的值的类型作为其以后要使用的类型。这样所有的变量都变成是可以进行类型预测的了。 事实上,在每个value profile中还有第二个内容,它是能包括已出现的一部分数据值的数据类型。这个类型使用了SpeculatedType(或SpecType)类型系统,实现在SpeculatedType.h中。每个value profile中这个类型会先设为SpecNone(就是没有数据)。当Baseline JIT执行次数超出阀值(JIT.cpp中的JIT::emitOptimizationCheck),它会生成一个新的类型,可以同时让最后一次的类型和最常用的数值符合这个类型。它或许会触发DFG,也可以让Baseline再多执行几次。当进入DFG JITF后,每个value profile通常会有一个这样可以包容多个不同值的类型。


SpecTypes之所以可行,源自于函数中的操作和变量都会使用标准的前置数据流( forward data flow )规范,实现了所谓的流程无关的不动点(fixpoint). 这是DFG编译的第一个阶段,由Baseline JIT基于执行次数决定是否激活(DFGPredictionPropagationPhase.cpp)。


在每个使用了预测数据类型的函数中,我们插入了基于预测的类型检查操作。如果类型检查失败,就会回退到Baseline JIT中。下面分解下'a+b'的附加操作是如何执行的, 假设a和b都被预测为SpecInt32类型:


check if a is Int32 -> 否则 OSR exit to Baseline JIT

check if b is Int32 -> 否则 OSR exit to Baseline JIT

result = a + b // integer addition

check if 溢出 -> 否则 OSR exit to Baseline JIT


操作执行完成后,我们可以知道:

  • 'a' 是整数.
  • 'b' 是整理.
  • 结果也是整数.

后面的操作就不用再检查'a' or 'b'的类型了这有一个消除类型检查的操作,是通过第二个数据流分析来完成的,被称为DFG CFA (DFGCFAPhase.cpp DFGAbstractState.cpp) . 它也实现了稀疏条件传播(sparse conditional constant propagation),让它可以有能力像确定类型一样确定某些值是不是常量。


对于表达式 'o.x * o.x + o.y * o.y',只需要在取o.x和o.y时进行类型检查。然后我们就知道它们的值是doubles, 就是只需触发double的相乘和相加。DFG绝大多数的类型检查通常是在加载堆数据时发生的。


深入阅读:

 Fast and Precise Hybrid Type Inference for JavaScript

 动态查看Type Inference的结果

 Type Inference in SpiderMonkey


原文地址: http://trac.webkit.org/wiki/JavaScriptCore

转载请注明出处:http://blog.csdn.net/horkychen

系列索引:

基础篇 (一)JSC与WebCore

基础篇(二)解释器基础与JSC核心组件

基础篇(三)从脚本代码到JIT编译的代码实现

基础篇(四) 页面解析与JavaScript元素的执行

高级篇(一) SSA (static single assignment)

高级篇(二) 类型推导(Type Inference)

高级篇(三) Register Allocation & Trampoline



目录
相关文章
|
5天前
|
开发框架 供应链 监控
并行开发模型详解:类型、步骤及其应用解析
在现代研发环境中,企业需要在有限时间内推出高质量的产品,以满足客户不断变化的需求。传统的线性开发模式往往拖慢进度,导致资源浪费和延迟交付。并行开发模型通过允许多个开发阶段同时进行,极大提高了产品开发的效率和响应能力。本文将深入解析并行开发模型,涵盖其类型、步骤及如何通过辅助工具优化团队协作和管理工作流。
|
4月前
|
存储 监控 NoSQL
MongoDB索引解析:工作原理、类型选择及优化策略
MongoDB索引解析:工作原理、类型选择及优化策略
|
16天前
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
|
2月前
|
存储 数据挖掘 大数据
深度解析Hologres计算资源配置:如何根据业务场景选择合适的计算类型?
【8月更文挑战第22天】Hologres是一款由阿里云提供的分布式分析型数据库,支持高效的大数据处理与分析。本文通过电商优化商品推荐策略的案例,介绍了Hologres中的计算组型与通用型配置。计算组型提供弹性扩展资源,适合大规模数据及高并发查询;通用型则适用于多数数据分析场景,具备良好计算性能。通过实例创建、数据加载、计算任务建立及结果查询的步骤展示,读者可理解两种配置的差异并根据业务需求灵活选择。
45 2
|
2月前
|
缓存 JavaScript 前端开发
|
2月前
|
关系型数据库 MySQL 数据管理
深入解析 MySQL 中的关系类型
【8月更文挑战第31天】
38 0
|
2月前
|
存储 安全 程序员
|
2月前
|
SQL 存储 数据库
|
2月前
|
SQL 关系型数据库 数据处理
|
2月前
|
SQL 存储 数据库
SQL中的不同关系类型:深入解析
【8月更文挑战第31天】
51 0

推荐镜像

更多