在 V8 中,什么是惰性反优化,它是如何发生的?
In V8, what is lazy deoptimization, and how does it happen?
根据V8源码和turbofan资料,有一种反优化叫做lazy deoptimization,描述如下(v8/src/common/globals.h):
Lazy: the code has been marked as dependent on some assumption which is checked elsewhere and can trigger deoptimization the next time the code is executed.
然而,当观察'v8/test/mjsunit/compiler/deopt-lazy-shape-mutation.js'与d8的执行时,我发现返回时立即发生去优化来自函数 change_o
。我想这是因为执行 change_o
操作 o
.
的形状,破坏了对 f
的 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]
我的问题是:
惰性反优化到底是什么?
在上面的示例中,是否可以理解 f
从 change_o
返回后立即取消优化的原因是 change_o
标志着 f
的某些假设具有遭到入侵?
惰性反优化是如何发生的?
在 eager deoptimization 的情况下,我看到有名为 Deoptimize*
的节点明确表示立即去优化条件,并使用 call
和条件跳转,例如 jnz
、ja
等。但是,我无法弄清楚 lazy deoptimization 如何进入执行流程。是否有一些主管监视 call-ret
操作,并在 callee
损害 caller
的依赖性时触发去优化?
(此处为 V8 开发人员。)
- What exactly is lazy deoptimization?
这是一个函数的“预定”反优化,当前在堆栈上有一个或多个激活,但不是当前正在执行的函数(它将拥有最顶层的堆栈帧,并将执行“急切的反优化”如果必须的话)。取消优化意味着必须重写堆栈帧的内容,这对于任何非最顶层堆栈帧来说都非常困难,因此此类函数被标记为取消优化,并且一旦控制 return 对它们进行取消优化(即当它们成为最顶层的堆栈框架时)。
请注意,同一个函数可以急切地(针对其当前正在执行的激活)和惰性地(针对堆栈中更下方的任何其他激活)进行去优化。
In the example above, is it ok to understand the reason why f
was deoptimized as soon as it is returned from change_o
is that change_o
marks that some assumption of f
has been compromised?
是的。 change_o
使之前优化 f
时所做的假设无效。 (f
的任何后续优化都不会做出相同的假设。)
- How does lazy deoptimization occur?
堆栈上的return地址被重写,因此不是恢复执行原始代码,而是开始反优化序列。如果您想深入了解详细信息,请参阅 deoptimizer.cc
中的 class ActivationsFinder
。
根据V8源码和turbofan资料,有一种反优化叫做lazy deoptimization,描述如下(v8/src/common/globals.h):
Lazy: the code has been marked as dependent on some assumption which is checked elsewhere and can trigger deoptimization the next time the code is executed.
然而,当观察'v8/test/mjsunit/compiler/deopt-lazy-shape-mutation.js'与d8的执行时,我发现返回时立即发生去优化来自函数 change_o
。我想这是因为执行 change_o
操作 o
.
f
的 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]
我的问题是:
惰性反优化到底是什么? 在上面的示例中,是否可以理解
f
从change_o
返回后立即取消优化的原因是change_o
标志着f
的某些假设具有遭到入侵?惰性反优化是如何发生的? 在 eager deoptimization 的情况下,我看到有名为
Deoptimize*
的节点明确表示立即去优化条件,并使用call
和条件跳转,例如jnz
、ja
等。但是,我无法弄清楚 lazy deoptimization 如何进入执行流程。是否有一些主管监视call-ret
操作,并在callee
损害caller
的依赖性时触发去优化?
(此处为 V8 开发人员。)
- What exactly is lazy deoptimization?
这是一个函数的“预定”反优化,当前在堆栈上有一个或多个激活,但不是当前正在执行的函数(它将拥有最顶层的堆栈帧,并将执行“急切的反优化”如果必须的话)。取消优化意味着必须重写堆栈帧的内容,这对于任何非最顶层堆栈帧来说都非常困难,因此此类函数被标记为取消优化,并且一旦控制 return 对它们进行取消优化(即当它们成为最顶层的堆栈框架时)。
请注意,同一个函数可以急切地(针对其当前正在执行的激活)和惰性地(针对堆栈中更下方的任何其他激活)进行去优化。
In the example above, is it ok to understand the reason why
f
was deoptimized as soon as it is returned fromchange_o
is thatchange_o
marks that some assumption off
has been compromised?
是的。 change_o
使之前优化 f
时所做的假设无效。 (f
的任何后续优化都不会做出相同的假设。)
- How does lazy deoptimization occur?
堆栈上的return地址被重写,因此不是恢复执行原始代码,而是开始反优化序列。如果您想深入了解详细信息,请参阅 deoptimizer.cc
中的 class ActivationsFinder
。