【问题标题】:In V8, what is lazy deoptimization, and how does it happen?在 V8 中,什么是惰性去优化,它是如何发生的?
【发布时间】:2021-12-29 02:01:22
【问题描述】:

根据V8源码和turbofan资料,有一种去优化叫lazy deoptimization,描述如下(v8/src/common/globals.h):

惰性:代码已被标记为依赖于在其他地方检查的某些假设,并且可能会在下次执行代码时触发去优化。

但是,当使用 d8 观察 'v8/test/mjsunit/compiler/deopt-lazy-shape-mutation.js' 的执行时,我发现从函数 change_o 返回时发生了去优化立即。我猜这是因为fo 的映射依赖被执行change_o 破坏了,该change_o 操纵了o 的形状。

> d8/d8 --trace-deopt --allow-natives-syntax test/deopt-lazy-shape-mutation.js
[marking dependent code 0x3d7d00044001 (0x3d7d08293535 <SharedFunctionInfo f>) (opt id 0) for deoptimization, reason: code dependencies]
[bailout (kind: deopt-lazy, reason: (unknown)): begin. deoptimizing 0x3d7d08293779 <JSFunction f (sfi = 0x3d7d08293535)>, opt id 0, node id 20, bytecode offset 4, deopt exit 0, FP to SP delta 32, caller SP 0x7ffdaa56ff68, pc 0x3d7d00044111]

我的问题是:

  1. 究竟什么是惰性去优化? 在上面的例子中,可以理解为什么fchange_o返回就被去优化的原因是change_o标志着f的一些假设已经被破坏了?

  2. 惰性去优化是如何发生的?渴望去优化的情况下,我看到有名为Deoptimize* 的节点明确表示立即去优化条件,并使用call 和条件跳转(例如jnz)组装成机器代码、ja 等。但是,我无法弄清楚 惰性去优化 是如何进入执行流程的。是否有监督者监控call-ret 操作,并在callee 破坏caller 的依赖关系时触发反优化?

【问题讨论】:

    标签: javascript compilation v8 jit


    【解决方案1】:

    (这里是 V8 开发人员。)

    1. 究竟什么是惰性去优化?

    这是对当前在堆栈上具有一个或多个激活但不是当前正在执行的函数的函数的“预定”去优化(它将拥有最顶层的堆栈帧,并且如果它会执行“急切的去优​​化”必须)。去优化意味着必须重写堆栈帧的内容,这对于任何非最顶层的堆栈帧来说都非常困难,因此这些函数被标记为去优化,并且一旦控制权返回它们就会被去优化(即当它们成为最顶层时)堆栈帧)。

    请注意,同一个函数既可以急切(针对其当前执行的激活)也可以延迟(针对堆栈中的任何其他激活)进行去优化。

    在上面的例子中,可以理解为什么f一从change_o返回就被去优化的原因是change_o标志着f的一些假设已经被破坏了吗?

    是的。 change_o 使早先优化 f 时所做的假设无效。 (f 的任何后续优化都不会做出同样的假设。)

    1. 惰性去优化是如何发生的?

    堆栈上的返回地址被重写,因此不是恢复执行原始代码,而是启动反优化序列。如果您想深入了解详情,请参阅 deoptimizer.cc 中的 class ActivationsFinder

    【讨论】:

    • 感谢您的友好回答。这很有帮助。
    猜你喜欢
    • 2015-05-01
    • 2011-10-15
    • 2011-01-10
    • 1970-01-01
    • 2016-10-27
    • 2014-09-02
    • 2018-03-26
    • 2021-05-10
    相关资源
    最近更新 更多