@@ -128,7 +128,7 @@ <h2 id="分析">分析</h2>
128128
129129< p > 这样的话在临近位置,连续调用函数 C 返回的应该就是同一层栈帧对应的 this 对象。e2.M === e2.C 应该像注释中描述的也为 true 才对。继续分析 opt 函数的 JIT 编译做了哪些优化,为什么改变了这个结果。</ p >
130130
131- < p > 用 ./Chrome.exe –js-flags=”–allow-natives-syntax –trace-turbo” –no-sandbox –enable-logging=stderr 命令重新启动 chrome,访问 POC 页面后,得到编译过程的 trace 日志 ![[ turbo-000000B00023E8B4-0 1.json]] ,用 < a href ="https://v8.github.io/tools/head/turbolizer/index.html "> v8 turbolizer</ a > 打开</ p >
131+ < p > 用 ./Chrome.exe –js-flags=”–allow-natives-syntax –trace-turbo” –no-sandbox –enable-logging=stderr 命令重新启动 chrome,访问 POC 页面后,得到编译过程的 trace 日志 < img src =" /assets/images/ turbo-000000B00023E8B4-0%201.json " alt =" turbo-000000B00023E8B4-0 1.json" /> ,用 < a href ="https://v8.github.io/tools/head/turbolizer/index.html "> v8 turbolizer</ a > 打开</ p >
132132
133133< p > opt 函数整理一下,可以写成下面的形式:</ p >
134134< div class ="language-javascript highlighter-rouge "> < div class ="highlight "> < pre class ="highlight "> < code > < span class ="kd "> function</ span > < span class ="nx "> opt</ span > < span class ="p "> (</ span > < span class ="nx "> a</ span > < span class ="p "> ,</ span > < span class ="nx "> b</ span > < span class ="p "> ,</ span > < span class ="nx "> c</ span > < span class ="p "> )</ span > < span class ="p "> {</ span >
@@ -1432,13 +1432,7 @@ <h2 id="分析">分析</h2>
14321432}
14331433</ code > </ pre >
14341434
1435- < pre > < code class ="language-plantuml "> @startuml
1436- skinparam handwritten true
1437- [FrameState] -> [FrameDescriptor] : AppendDeoptimizeArguments
1438- [FrameDescriptor] -> [TranslationArray] : TranslateFrameStateDescriptorOperands
1439- [TramslationArray] -> [JavascriptFrameSummary] : OptimizedFrame::Summarize
1440- @enduml
1441- </ code > </ pre >
1435+ < p > < img src ="https://www.plantuml.com/plantuml/png/NOz13i8W44Ntd88BU84kJ9iqBjnqeJlJnKGcIkmCP1WqUdffqTIuvF__7j0c1T5Caqatpn44o5w1tKEyAh9LoMXEknBZGK5nj9kjhUSUqrbyr22ZRMmN8xBhCKJrv5_OoIKJiYRplwwAem2d2TG7xaJWEJk-6QxthTWGiTGkcHcbNxGAzt27kgrq9W9PjCFdIMufcgPM9J0jiYj_VmC0 " alt ="plantum-diagram " /> </ p >
14421436
14431437< p > StateValues</ p >
14441438
@@ -1884,25 +1878,25 @@ <h2 id="分析">分析</h2>
18841878
18851879< p > FrameState 节点是 < code class ="language-plaintext highlighter-rouge "> BytecodeGraphBuilder</ code > 在生成 turbofan 图 IR 时创建的,用 < a href ="https://v8.github.io/tools/head/turbolizer/index.html "> turbolizer</ a > 打开 turbofan 输出的 trace 文件,从后往前分析 IR 后发现,< code class ="language-plaintext highlighter-rouge "> kObjectId</ code > 节点对应的是 < code class ="language-plaintext highlighter-rouge "> b.a.call(arguments,b)</ code > 函数调用的 FrameState 中的 receiver 参数</ p >
18861880
1887- < p > ![[ Pasted image 20230811003541.png]]
1881+ < p > < img src =" /assets/images/ Pasted%20image%2020230811003541.png " alt =" Pasted image 20230811003541.png" />
18881882kObjectId 节点是在 EscapeAnalysis 阶段创建的
1889- ![[ Pasted image 20230811003746.png]] </ p >
1883+ < img src =" /assets/images/ Pasted%20image%2020230811003746.png " alt =" Pasted image 20230811003746.png" /> </ p >
18901884
18911885< p > 再看一下 EscapeAnalysis 之前的 LoadElimination 阶段, 对应的位置是一个 kFinishRegion 节点,浏览相关代码后得知这个节点是 < code class ="language-plaintext highlighter-rouge "> AllocationBuilder</ code > 创建的,是一系列对象创建操作的结尾,结合 POC 源码推测,这里代表的是 arguments 对象的创建。</ p >
18921886
1893- < p > ![[ Pasted image 20230811005830.png]] </ p >
1887+ < p > < img src =" /assets/images/ Pasted%20image%2020230811005830.png " alt =" Pasted image 20230811005830.png" /> </ p >
18941888
18951889< p > 再往前追溯到 Typer 阶段,发现这里变成了 JSCrateArguments 节点,就刚好可以证实之前的推测。</ p >
18961890
18971891< p > ![[Pasted image 20230814232830.png]]</ p >
18981892
18991893< p > 再往前追溯,下次变化在 BytecodeGraphBuilder 阶段,这个是因为紧随其后的 Inlining 阶段把 JSCall</ p >
19001894
1901- < p > ![[ Pasted image 20230815221029.png]] </ p >
1895+ < p > < img src =" /assets/images/ Pasted%20image%2020230815221029.png " alt =" Pasted image 20230815221029.png" /> </ p >
19021896
19031897< p > 从 turbofan 的源码视图也可以看出来两个子函数都被 inline 进了最外层的函数</ p >
19041898
1905- < p > ![[ Pasted image 20230815221141.png]] </ p >
1899+ < p > < img src =" /assets/images/ Pasted%20image%2020230815221141.png " alt =" Pasted image 20230815221141.png" /> </ p >
19061900
19071901< p > 在选定好 inline 的目标后,是在 < code class ="language-plaintext highlighter-rouge "> JSInliner::ReduceJSCall(Node* node)</ code > 函数中完成实际的 inline 操作。主要是创建子函数的 IR 图,之后把它连接到父函数的 IR 图中。连接过程从,有两个操作引起了我的注意:</ p >
19081902< ul >
@@ -1912,7 +1906,7 @@ <h2 id="分析">分析</h2>
19121906
19131907< p > 了解了这些再来仔细看下 inline 之后的情况</ p >
19141908
1915- < p > ![[ Pasted image 20230816004600.png]] </ p >
1909+ < p > < img src =" /assets/images/ Pasted%20image%2020230816004600.png " alt =" Pasted image 20230816004600.png" /> </ p >
19161910
19171911< p > 此时这个 JSCall 对应的已经是 a.b(0, a) 这个函数调用, 所以这个 FrameState 对应的是</ p >
19181912< div class ="language-plaintext highlighter-rouge "> < div class ="highlight "> < pre class ="highlight "> < code > (a) {
@@ -2103,13 +2097,13 @@ <h2 id="分析">分析</h2>
21032097
21042098< p > 优化过后 JSCreateArguments 就被展开成了下面这些 Allocate 和 StoreField 节点。</ p >
21052099
2106- < p > ![[ Pasted image 20230816223925.png]] </ p >
2100+ < p > < img src =" /assets/images/ Pasted%20image%2020230816223925.png " alt =" Pasted image 20230816223925.png" /> </ p >
21072101
2108- < p > ![[ Pasted image 20230816224756.png]] </ p >
2102+ < p > < img src =" /assets/images/ Pasted%20image%2020230816224756.png " alt =" Pasted image 20230816224756.png" /> </ p >
21092103
21102104< p > 之后这个状态一直保持到了逃逸分析 EscapeAnalysis 阶段,在逃逸分析阶段被优化成了下图的样子。</ p >
21112105
2112- < p > ![[ Pasted image 20230816232212.png]] </ p >
2106+ < p > < img src =" /assets/images/ Pasted%20image%2020230816232212.png " alt =" Pasted image 20230816232212.png" /> </ p >
21132107
21142108< p > < a href ="https://en.wikipedia.org/wiki/Escape_analysis "> 逃逸分析</ a > 是一种编译优化手段,简单来说如果编译器分析出一个对象只在某个范围内,例如一个函数内部,被创建并使用,那么就可以对它进行一些优化,例如本来应该在堆上分配的对象,如果只在函数内部使用就可以优化到栈上,这种情况可以称为被捕获。反过来如果编译器不能分析出对象只在特定范围内被使用,就可以说对象是逃逸的。推测是编译器分析出 arguments 对象只在函数内部使用,所以对它进行了一些优化。</ p >
21152109
0 commit comments