一
前言
二
正文
# 推荐香港服务器,可以避免网络问题导致的编译失败。
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=/path/to/depot_tools:$PATHmkdir ~/v8
cd ~/v8
fetch v8
cd v8# 补丁前一笔提交
git checkout 24861cbefe4
gclient sync
alias gm=~/v8/tools/dev/gm.py
gm x64.release
gm x64.debug# test
./out/x64.release/d8 --help
// test.js
const o13 = {
"maxByteLength": 5368789,
};
const v14 = new ArrayBuffer(129, o13);
const v16 = new Uint16Array(v14);function f3(param) {
for (let i = 0; i < 5; i++) {
try {"resize".includes(v14); } catch (e) {}
v14.resize(3.0, ..."resize", ...v16);
}let f = function() { return param; }
}%PrepareFunctionForOptimization(f3);
f3();
%OptimizeFunctionOnNextCall(f3);
f3();
// 运行./out/x64.deubg/d8 --allow-natives-syntax test.js,将会得到崩溃堆栈
三
背景
function get_number(x) {
return x > 0 ? 1 : 0;
}
// 多次执行get_number,触发优化
for (var i = 0; i < 20000; i++) {
get_number(3);
}get_number(3);
/*
./out/x64.deubg/d8 --allow-natives-syntax --trace-turbo test.js
--trace-turbo 将生成turbo-get_number-1.json文件, v8提供了一个在线查看工具:https://v8.github.io/tools/head/turbolizer/index.html,加载turbo-get_number-1.json 可以查看sea of nodes
*/
function get_number(x) {
return x > 0 ? 1 : 0;
}
// common-operator-reducer.cc
// ReduceReturn 优化return 节点
Reduction CommonOperatorReducer::ReduceReturn(Node* node) {
...
// return节点的effect输入
Node* effect = NodeProperties::GetEffectInput(node);
// return节点的第一个value输入
Node* pop_count = NodeProperties::GetValueInput(node, 0);
// return节点的第二个value输入,get_number函数里它为phi(1|0)
Node* value = NodeProperties::GetValueInput(node, 1);
// control输入
Node* control = NodeProperties::GetControlInput(node);
if (value->opcode() == IrOpcode::kPhi &&
NodeProperties::GetControlInput(value) == control &&
control->opcode() == IrOpcode::kMerge) {
/* 3个条件均满足,参考sea of nodes图
1. value类型为phi
2. value的control输入 跟 return的control输入 是同一个节点
3. control输入是一个merge节点
*/// control为merge节点(25),control_inputs代表了merge节点的两个输入:iftrue, iffalse
Node::Inputs control_inputs = control->inputs();
Node::Inputs value_inputs = value->inputs();
DCHECK_NE(0, control_inputs.count());
DCHECK_EQ(control_inputs.count(), value_inputs.count() - 1);
DCHECK_EQ(IrOpcode::kEnd, graph()->end()->opcode());
DCHECK_NE(0, graph()->end()->InputCount());
// control作为node和value的输入,满足
// value作为node的输入,满足
if (control->OwnedBy(node, value) && value->OwnedBy(node)) {
// control_inputs: iftrue, iffalse两个分支
for (int i = 0; i < control_inputs.count(); ++i) {
// newNode(操作码,pop_count, value_input, effect_input, control_input)
// newNode函数参数如上,使用newNode创建两个新的return节点
Node* ret = graph()->NewNode(node->op(), pop_count, value_inputs[i],
effect, control_inputs[i]);
// 将新建return节点的输出control指向end节点
MergeControlToEnd(graph(), common(), ret);
}
// merge节点丢弃
Replace(control, dead());
// 原来的return节点丢弃
return Replace(dead());
}
...
}
return NoChange();
}
四
issue分析
void RelaxControls(Node* node) {
ReplaceWithValue(node, node, node, nullptr);
}
/*
也就是说patch是将
ReplaceWithValue(node, node, node, nullptr);
替换成
ReplaceWithValue(node, node, node, control);
*/
void ReplaceWithValue(Node* node, Node* value, Node* effect = nullptr,
Node* control = nullptr) {
DCHECK_NOT_NULL(editor_);
editor_->ReplaceWithValue(node, value, effect, control);
}
Reduction JSCallReducer::ReduceArrayIterator(Node* node,
ArrayIteratorKind array_kind,
IterationKind iteration_kind) {
...
if (array_kind == ArrayIteratorKind::kTypedArray) {
// Make sure we deopt when the JSArrayBuffer is detached.
if (!dependencies()->DependOnArrayBufferDetachingProtector()) {
CallParameters const& p = CallParametersOf(node->op());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}// 在经过两个if的条件下,进入到这里
JSCallReducerAssembler a(this, node);
a.CheckIfTypedArrayWasDetached(
TNode<JSTypedArray>::UncheckedCast(receiver),
std::move(elements_kinds), p.feedback());
// 此时修改了contorl和effect,它们将作为node节点新的输出 <--- label 1
std::tie(effect, control) = ReleaseEffectAndControlFromAssembler(&a);
}
}
/*
patch前:
RelaxControls(node); 即为ReplaceWithValue(node, node, node, nullptr); 也就是说control输出不发生变化。"label 1" 处,control被赋予了新的节点,本意是需要修改当前节点的control输出,但patch前RelaxControls(node);没有修改conctrol输出。
patch后:
ReplaceWithValue(node, node, node, control); 使用赋值过的"control变量"作为control输出,符合预期。
*/
RelaxControls(node);// 当前节点的输入依次换为receiver、context、effect、control
node->ReplaceInput(0, receiver);
node->ReplaceInput(1, context);
node->ReplaceInput(2, effect);
node->ReplaceInput(3, control);
node->TrimInputCount(4);
// 修改当前节点的操作符为CreateArrayIterator
NodeProperties::ChangeOp(node,
javascript()->CreateArrayIterator(iteration_kind));
return Changed(node);
}
五
思考
看雪ID:coolboyme
https://bbs.kanxue.com/user-home-492418.htm
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多