V8 TurboFan 生成图简析

 

一、简介

v8 turbolizer 有助于我们分析 JIT turbofan 的优化方式以及优化过程。但我们常常对于 turbolizer 生成的 IR 图一知半解,不清楚具体符号所代表的意思。以下为笔者阅读相关代码后所做的笔记。

 

二、TurboFan Json 格式

  • --trace-turbo 参数将会生成一个 JSON 格式的数据。通过在 turbolizer 上加载该 JSON,可以得到一个这样的IR图:
  • 其中,该 JSON 的格式如下:
    {
        "function": "opt_me",
        "sourcePosition": 109,
        "source": [js source],
        "phases": [
            {
                "name": "Typed",
                "type": "graph",
                "data": {
                    "nodes": [
                        [...],
                        {
                            "id": 20,
                            "label": "FrameState[INTERPRETED_FRAME, 11, Ignore, 0x1a5acd4aa5e9 <SharedFunctionInfo opt_me>]",
                            "title": "FrameState[INTERPRETED_FRAME, 11, Ignore, 0x1a5acd4aa5e9 <SharedFunctionInfo opt_me>]",
                            "live": true,
                            "properties": "Idempotent, NoRead, NoWrite, NoThrow, NoDeopt",
                            "pos": 178,
                            "opcode": "FrameState",
                            "control": false,
                            "opinfo": "5 v 0 eff 0 ctrl in, 1 v 0 eff 0 ctrl out",
                            "type": "Internal"
                        },
                        [...]
                    ],
                    "edges": [
                        {
                            "source": 100,
                            "target": 101,
                            "index": 0,
                            "type": "control"
                        },
                        [...]
                    ]
                }
            },
            [...]
        ],
        "nodePositions": {
            [...]
        }
    }
    

    简单的概括一下,就是:

    • function: 函数名称
    • sourcePosition:代码的起始位置。
    • source: 当前 turboFan 优化的 JS 代码
    • phases: turboFan 的各个优化阶段
      • 优化阶段1
        • name: 当前优化阶段的名称
        • type:显示的形式,是 graph IR 图还是 文本。
        • data: 当前阶段真正存放的结点与边的数据。
          • nodes: 结点数据
            • 结点1
              • id: 结点ID,通常是一个数字
              • label:结点标签
              • title:结点主题
              • live: 当前结点是否是活结点,为 true / false
              • properties:当前结点的属性
              • pos:暂且不说
              • opcode:当前结点的操作码,例如End
              • control:当前是否是控制结点,为 true / false
              • opinfo:具体的结点信息,通常表示当前结点的ValueInputCount、EffectInputCount、ControlInputCount、ValueOutputCount、EffectOutputCount、ControlOutputCount

                表示方式如下:

                “\<ValueInputCount\> v

                \<EffectInputCount\>    eff
                \<ControlInputCount\>   ctrl in,
                \<ValueOutputCount\>    v
                \<EffectOutputCount\>   eff
                \<ControlOutputCount\>  ctrl out"
                

                例如:”0 v 1 eff 1 ctrl in, 0 v 1 eff 0 ctrl out”

            • [其他结点]
          • edges:边的数据
            • 边1
              • source:边的源节点 ID
              • target:边的目标节点ID
              • index:当前边连接到目标节点的哪个输入
              • type:当前边的类型,例如 control、value、effect等等
            • [其他边]
      • [其他优化阶段]
    • nodePositions:每个结点在 JS 源码中所对应的代码位置

 

三、Node

a. 属性说明

以下是截取出的一个 Node 示例:

{
    "id": 128,
    "label": "LoadField[+16]",
    "title": "LoadField[tagged base, 16, Internal, kRepTaggedPointer|kTypeAny, PointerWriteBarrier]",
    "live": true,
    "properties": "NoWrite, NoThrow, NoDeopt",
    "pos": 388,
    "opcode": "LoadField",
    "control": false,
    "opinfo": "1 v 1 eff 1 ctrl in, 1 v 1 eff 0 ctrl out",
    "type": "Internal"
}

对应的结点如下:

一一对应以下便可以看出,其中的 id、label、title、properties、opinfo 以及 type 均显现在图中。

而 live、pos、opcode 以及 control 字段则是给 turbolizer.js 使用的。

注意到上图中的 “Inplace update in phase: Typed”,其中的 phase 则是 turbolizer.js 动态分析出的,不在 JSON 中记录。

b. 颜色

我们可以注意到,IR图中的结点都有颜色,其中颜色貌似符合某种规律。

通过查阅 turbolizer.js 以及 在线 turbolizer 的 css 代码,turbolizer 将结点分为了以下几种结点,并设置了不同的颜色加以区分:

  • Control 结点:对于那些控制结点, 即 JSON 数据中 control 字段为 true 的结点,其颜色为黄色
  • Input 结点:那些 opcode 为 Parameter 或 Constant 结点,其颜色为浅蓝色
  • Live 结点(这其实不能算一类结点):即 live 字段为 true 的结点。其反向结点——DeadNode——的颜色会在原先颜色的基础上进行浅色化处理,例如以下图片。图片中的两个结点其类型相同,所不同的是左边的结点是 Dead,右边结点是 Live。
  • JavaScript结点:那些 opcode 以 JS 开头的结点,其颜色为橙红色
  • Simplified 结点:那些 opcode 包含 Phi、Boolean、Number、String、Change、Object、Reference、Any、ToNumber、AnyToBoolean、Load、Store,但不是 JavaScript类型的结点。其颜色如下所示:
  • Machine 结点:除了上述四种结点以外,剩余的结点。颜色如下所示:

 

四、Edge

Edge 中的 Type 共有五种,分别是 valuecontextframe-stateeffectcontrol 以及最后一个 unknown。

以下是这些边的一些例子:

a. value 边

对于该边:

{
    "source": 80,
    "target": 83,
    "index": 4,
    "type": "value"
}

其边的视觉效果如下:

可以看到,对于 Value 边来说,是一条实线

b. context 边

对于该边:

{
    "source": 4,
    "target": 49,
    "index": 3,
    "type": "context"
}

视觉效果如下:

可以看到,Context边也是一条实线。但在当前这个例子中,由于 Context 边只会由 Parameter[%context#4]结点发出,因此不会与 Value 边混淆

这里需要注意一下,Context 边只会存在于某个 Context 结点发出的所有边,即不会出现结点既发出 Context 边又发出 Value 边的情况。

如果有还请指正。

c. frame-state 边

例子:

{
    "source": 50,
    "target": 49,
    "index": 4,
    "type": "frame-state"
}

视觉效果:

可以看到,对于一条 frame-state 边,其视觉效果是一条 疏虚线

frame-state 边一定是由一个 FrameState 结点发出的。

上图的另一条虚线是密虚线,所不同的是虚线的疏密程度

d. effect 边

例子:

{
    "source": 114,
    "target": 49,
    "index": 5,
    "type": "effect"
}

视觉效果:

effect 边的显示效果是 密虚线

e. control 边

例子:

{
    "source": 31,
    "target": 49,
    "index": 6,
    "type": "control"
}

视觉效果:

注意:与 value 边相同,control 边的显示效果也是一条实线。这意味着单单只看 IR 图的话,是无法将 Control 边和 Value 边区分开的。

 

五、参考的源码

  • v8/tools/turbolizer/build/turbolizer.js
  • v8/src/compiler/graph-visualizer.cc
(完)