V8 TurboFan 生成图简析

一、简介

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

二、TurboFan Json 格式

  • --trace-turbo 参数将会生成一个 JSON 格式的数据。通过在 turbolizer 上加载该 JSON,可以得到一个这样的IR图:
    img

  • 其中,该 JSON 的格式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    {
    "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 示例:

1
2
3
4
5
6
7
8
9
10
11
12
{
"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"
}

对应的结点如下:

img

一一对应以下便可以看出,其中的 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 的结点,其颜色为黄色

    img

  • Input 结点:那些 opcode 为 Parameter 或 Constant 结点,其颜色为浅蓝色

    img

  • Live 结点(这其实不能算一类结点):即 live 字段为 true 的结点。其反向结点——DeadNode——的颜色会在原先颜色的基础上进行浅色化处理,例如以下图片。图片中的两个结点其类型相同,所不同的是左边的结点是 Dead,右边结点是 Live。

    img

  • JavaScript结点:那些 opcode 以 JS 开头的结点,其颜色为橙红色

    img

  • Simplified 结点:那些 opcode 包含 Phi、Boolean、Number、String、Change、Object、Reference、Any、ToNumber、AnyToBoolean、Load、Store,但不是 JavaScript类型的结点。其颜色如下所示:

    img

  • Machine 结点:除了上述四种结点以外,剩余的结点。颜色如下所示:

    img

四、Edge

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

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

a. value 边

对于该边:

1
2
3
4
5
6
{
"source": 80,
"target": 83,
"index": 4,
"type": "value"
}

其边的视觉效果如下:

img

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

b. context 边

对于该边:

1
2
3
4
5
6
{
"source": 4,
"target": 49,
"index": 3,
"type": "context"
}

视觉效果如下:

img

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

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

如果有还请指正。

c. frame-state 边

例子:

1
2
3
4
5
6
{
"source": 50,
"target": 49,
"index": 4,
"type": "frame-state"
}

视觉效果:

img

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

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

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

d. effect 边

例子:

1
2
3
4
5
6
{
"source": 114,
"target": 49,
"index": 5,
"type": "effect"
}

视觉效果:

img

effect 边的显示效果是 密虚线

e. control 边

例子:

1
2
3
4
5
6
{
"source": 31,
"target": 49,
"index": 6,
"type": "control"
}

视觉效果:

img

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

五、参考的源码

  • v8/tools/turbolizer/build/turbolizer.js
  • v8/src/compiler/graph-visualizer.cc
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2020-2021 Kiprey
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~