@@ -75,7 +75,7 @@ console.log(e2.M === e2.C); // should be true as above but prints false.
7575
7676这样的话在临近位置,连续调用函数 C 返回的应该就是同一层栈帧对应的 this 对象。e2.M === e2.C 应该像注释中描述的也为 true 才对。继续分析 opt 函数的 JIT 编译做了哪些优化,为什么改变了这个结果。
7777
78- 用 ./Chrome.exe --js-flags="--allow-natives-syntax --trace-turbo" --no-sandbox --enable-logging=stderr 命令重新启动 chrome,访问 POC 页面后,得到编译过程的 trace 日志 ![ [ turbo-000000B00023E8B4-0 1.json]] ,用 [ v8 turbolizer] ( https://v8.github.io/tools/head/turbolizer/index.html ) 打开
78+ 用 ./Chrome.exe --js-flags="--allow-natives-syntax --trace-turbo" --no-sandbox --enable-logging=stderr 命令重新启动 chrome,访问 POC 页面后,得到编译过程的 trace 日志 ![ turbo-000000B00023E8B4-0 1.json] ( /assets/images/turbo-000000B00023E8B4-0%201.json ) ,用 [ v8 turbolizer] ( https://v8.github.io/tools/head/turbolizer/index.html ) 打开
7979
8080opt 函数整理一下,可以写成下面的形式:
8181``` javascript
@@ -1409,17 +1409,7 @@ int TranslatedState::CreateNextTranslatedValue(
14091409}
14101410```
14111411
1412-
1413-
1414- ```plantuml
1415- @startuml
1416- skinparam handwritten true
1417- [FrameState] -> [FrameDescriptor] : AppendDeoptimizeArguments
1418- [FrameDescriptor] -> [TranslationArray] : TranslateFrameStateDescriptorOperands
1419- [TramslationArray] -> [JavascriptFrameSummary] : OptimizedFrame::Summarize
1420- @enduml
1421- ```
1422-
1412+ 
14231413
14241414StateValues
14251415
@@ -1868,33 +1858,33 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
18681858
18691859FrameState 节点是 ` BytecodeGraphBuilder ` 在生成 turbofan 图 IR 时创建的,用 [ turbolizer] ( https://v8.github.io/tools/head/turbolizer/index.html ) 打开 turbofan 输出的 trace 文件,从后往前分析 IR 后发现,` kObjectId ` 节点对应的是 ` b.a.call(arguments,b) ` 函数调用的 FrameState 中的 receiver 参数
18701860
1871- ![[ Pasted image 20230811003541.png]]
1861+ ![ Pasted image 20230811003541.png] ( /assets/images/Pasted%20image%2020230811003541.png )
18721862kObjectId 节点是在 EscapeAnalysis 阶段创建的
1873- ![[ Pasted image 20230811003746.png]]
1863+ ![ Pasted image 20230811003746.png] ( /assets/images/Pasted%20image%2020230811003746.png )
18741864
18751865再看一下 EscapeAnalysis 之前的 LoadElimination 阶段, 对应的位置是一个 kFinishRegion 节点,浏览相关代码后得知这个节点是 ` AllocationBuilder ` 创建的,是一系列对象创建操作的结尾,结合 POC 源码推测,这里代表的是 arguments 对象的创建。
18761866
1877- ![[ Pasted image 20230811005830.png]]
1867+ ![ Pasted image 20230811005830.png] ( /assets/images/Pasted%20image%2020230811005830.png )
18781868
18791869再往前追溯到 Typer 阶段,发现这里变成了 JSCrateArguments 节点,就刚好可以证实之前的推测。
18801870
18811871![ [ Pasted image 20230814232830.png]]
18821872
18831873再往前追溯,下次变化在 BytecodeGraphBuilder 阶段,这个是因为紧随其后的 Inlining 阶段把 JSCall
18841874
1885- ![[ Pasted image 20230815221029.png]]
1875+ ![ Pasted image 20230815221029.png] ( /assets/images/Pasted%20image%2020230815221029.png )
18861876
18871877从 turbofan 的源码视图也可以看出来两个子函数都被 inline 进了最外层的函数
18881878
1889- ![[ Pasted image 20230815221141.png]]
1879+ ![ Pasted image 20230815221141.png] ( /assets/images/Pasted%20image%2020230815221141.png )
18901880
18911881在选定好 inline 的目标后,是在 ` JSInliner::ReduceJSCall(Node* node) ` 函数中完成实际的 inline 操作。主要是创建子函数的 IR 图,之后把它连接到父函数的 IR 图中。连接过程从,有两个操作引起了我的注意:
18921882- 用调用点 (JSCall节点) 的 FrameState 替换子函数内部的 FrameState 的 outer_frame_state 输入
18931883- 用调用点 (JSCall节点) 的实参 (inputs) 替代子函数的 形参 (Parameters) 节点
18941884
18951885了解了这些再来仔细看下 inline 之后的情况
18961886
1897- ![[ Pasted image 20230816004600.png]]
1887+ ![ Pasted image 20230816004600.png] ( /assets/images/Pasted%20image%2020230816004600.png )
18981888
18991889此时这个 JSCall 对应的已经是 a.b(0, a) 这个函数调用, 所以这个 FrameState 对应的是
19001890```
@@ -2088,13 +2078,13 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
20882078
20892079优化过后 JSCreateArguments 就被展开成了下面这些 Allocate 和 StoreField 节点。
20902080
2091- ![[ Pasted image 20230816223925.png]]
2081+ ![ Pasted image 20230816223925.png] ( /assets/images/Pasted%20image%2020230816223925.png )
20922082
2093- ![[ Pasted image 20230816224756.png]]
2083+ ![ Pasted image 20230816224756.png] ( /assets/images/Pasted%20image%2020230816224756.png )
20942084
20952085之后这个状态一直保持到了逃逸分析 EscapeAnalysis 阶段,在逃逸分析阶段被优化成了下图的样子。
20962086
2097- ![[ Pasted image 20230816232212.png]]
2087+ ![ Pasted image 20230816232212.png] ( /assets/images/Pasted%20image%2020230816232212.png )
20982088
20992089[ 逃逸分析] ( https://en.wikipedia.org/wiki/Escape_analysis ) 是一种编译优化手段,简单来说如果编译器分析出一个对象只在某个范围内,例如一个函数内部,被创建并使用,那么就可以对它进行一些优化,例如本来应该在堆上分配的对象,如果只在函数内部使用就可以优化到栈上,这种情况可以称为被捕获。反过来如果编译器不能分析出对象只在特定范围内被使用,就可以说对象是逃逸的。推测是编译器分析出 arguments 对象只在函数内部使用,所以对它进行了一些优化。
21002090
0 commit comments