Commit 6fca2cfa authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[turbofan] Elide redundant {IfSuccess} control projections.

This changes the IR to no longer require single {IfSuccess} projection
nodes unless there is a corresponding {IfException} node that links the
potentially throwing call to an exception handler. This reduces graph
size as well as compilation time when exception handlers aren't present.

The new invariant for potentially throwing nodes is: Nodes that can
potentially throw either have both IfSuccess/IfException projections as
the only control uses and no direct control uses, or no projections at
all and solely direct control uses.

R=jarin@chromium.org

Change-Id: I3d9cd816d74ad5af13e0673da7ec7a98f1ecdc7e
Reviewed-on: https://chromium-review.googlesource.com/449715
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43814}
parent 94b33165
...@@ -2930,12 +2930,6 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count, ...@@ -2930,12 +2930,6 @@ Node* AstGraphBuilder::MakeNode(const Operator* op, int value_input_count,
if (result->op()->EffectOutputCount() > 0) { if (result->op()->EffectOutputCount() > 0) {
environment_->UpdateEffectDependency(result); environment_->UpdateEffectDependency(result);
} }
// Add implicit success continuation for throwing nodes.
if (!result->op()->HasProperty(Operator::kNoThrow)) {
const Operator* op = common()->IfSuccess();
Node* on_success = graph()->NewNode(op, result);
environment_->UpdateControlDependency(on_success);
}
} }
} }
......
...@@ -2425,7 +2425,7 @@ Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count, ...@@ -2425,7 +2425,7 @@ Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count,
set_environment(success_env); set_environment(success_env);
} }
// Add implicit success continuation for throwing nodes. // Add implicit success continuation for throwing nodes.
if (!result->op()->HasProperty(Operator::kNoThrow)) { if (!result->op()->HasProperty(Operator::kNoThrow) && inside_handler) {
const Operator* if_success = common()->IfSuccess(); const Operator* if_success = common()->IfSuccess();
Node* on_success = graph()->NewNode(if_success, result); Node* on_success = graph()->NewNode(if_success, result);
environment()->UpdateControlDependency(on_success); environment()->UpdateControlDependency(on_success);
......
...@@ -484,22 +484,6 @@ void EffectControlLinearizer::Run() { ...@@ -484,22 +484,6 @@ void EffectControlLinearizer::Run() {
} }
} }
namespace {
void TryScheduleCallIfSuccess(Node* node, Node** control) {
// Schedule the call's IfSuccess node if there is no exception use.
if (!NodeProperties::IsExceptionalCall(node)) {
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsControlEdge(edge) &&
edge.from()->opcode() == IrOpcode::kIfSuccess) {
*control = edge.from();
}
}
}
}
} // namespace
void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state, void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state,
Node** effect, Node** control) { Node** effect, Node** control) {
SourcePositionTable::Scope scope(source_positions_, SourcePositionTable::Scope scope(source_positions_,
...@@ -583,13 +567,9 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state, ...@@ -583,13 +567,9 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state,
for (int i = 0; i < node->op()->ControlInputCount(); i++) { for (int i = 0; i < node->op()->ControlInputCount(); i++) {
NodeProperties::ReplaceControlInput(node, *control, i); NodeProperties::ReplaceControlInput(node, *control, i);
} }
// Update the current control and wire IfSuccess right after calls. // Update the current control.
if (node->op()->ControlOutputCount() > 0) { if (node->op()->ControlOutputCount() > 0) {
*control = node; *control = node;
if (node->opcode() == IrOpcode::kCall) {
// Schedule the call's IfSuccess node (if there is no exception use).
TryScheduleCallIfSuccess(node, control);
}
} }
} }
......
...@@ -792,19 +792,19 @@ Reduction JSBuiltinReducer::ReduceArrayIsArray(Node* node) { ...@@ -792,19 +792,19 @@ Reduction JSBuiltinReducer::ReduceArrayIsArray(Node* node) {
control = graph()->NewNode(common()->IfTrue(), control); control = graph()->NewNode(common()->IfTrue(), control);
// Let the %ArrayIsArray runtime function deal with the JSProxy {value}. // Let the %ArrayIsArray runtime function deal with the JSProxy {value}.
value = effect = value = effect = control =
graph()->NewNode(javascript()->CallRuntime(Runtime::kArrayIsArray), value, graph()->NewNode(javascript()->CallRuntime(Runtime::kArrayIsArray), value,
context, frame_state, effect, control); context, frame_state, effect, control);
NodeProperties::SetType(value, Type::Boolean()); NodeProperties::SetType(value, Type::Boolean());
control = graph()->NewNode(common()->IfSuccess(), value);
// Update potential {IfException} uses of {node} to point to the above
// Rewire any IfException edges on {node} to {value}. // %ArrayIsArray runtime call node instead.
for (Edge edge : node->use_edges()) { Node* on_exception = nullptr;
Node* const user = edge.from(); if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
if (user->opcode() == IrOpcode::kIfException) { NodeProperties::ReplaceControlInput(on_exception, control);
edge.UpdateTo(value); NodeProperties::ReplaceEffectInput(on_exception, effect);
Revisit(user); control = graph()->NewNode(common()->IfSuccess(), control);
} Revisit(on_exception);
} }
// The {value} is neither a JSArray nor a JSProxy. // The {value} is neither a JSArray nor a JSProxy.
......
...@@ -646,8 +646,6 @@ Reduction JSCreateLowering::ReduceNewArrayToStubCall( ...@@ -646,8 +646,6 @@ Reduction JSCreateLowering::ReduceNewArrayToStubCall(
graph()->NewNode(common()->Branch(BranchHint::kFalse), equal, control); graph()->NewNode(common()->Branch(BranchHint::kFalse), equal, control);
Node* call_holey; Node* call_holey;
Node* call_packed; Node* call_packed;
Node* if_success_packed;
Node* if_success_holey;
Node* context = NodeProperties::GetContextInput(node); Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node); Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* if_equal = graph()->NewNode(common()->IfTrue(), branch); Node* if_equal = graph()->NewNode(common()->IfTrue(), branch);
...@@ -671,7 +669,6 @@ Reduction JSCreateLowering::ReduceNewArrayToStubCall( ...@@ -671,7 +669,6 @@ Reduction JSCreateLowering::ReduceNewArrayToStubCall(
call_holey = call_holey =
graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs); graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
if_success_holey = graph()->NewNode(common()->IfSuccess(), call_holey);
} }
Node* if_not_equal = graph()->NewNode(common()->IfFalse(), branch); Node* if_not_equal = graph()->NewNode(common()->IfFalse(), branch);
{ {
...@@ -695,10 +692,8 @@ Reduction JSCreateLowering::ReduceNewArrayToStubCall( ...@@ -695,10 +692,8 @@ Reduction JSCreateLowering::ReduceNewArrayToStubCall(
call_packed = call_packed =
graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs); graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
if_success_packed = graph()->NewNode(common()->IfSuccess(), call_packed);
} }
Node* merge = graph()->NewNode(common()->Merge(2), if_success_holey, Node* merge = graph()->NewNode(common()->Merge(2), call_holey, call_packed);
if_success_packed);
Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), call_holey, Node* effect_phi = graph()->NewNode(common()->EffectPhi(2), call_holey,
call_packed, merge); call_packed, merge);
Node* phi = Node* phi =
......
...@@ -654,21 +654,29 @@ void JSGenericLowering::LowerJSStackCheck(Node* node) { ...@@ -654,21 +654,29 @@ void JSGenericLowering::LowerJSStackCheck(Node* node) {
Node* if_false = graph()->NewNode(common()->IfFalse(), branch); Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
NodeProperties::ReplaceControlInput(node, if_false); NodeProperties::ReplaceControlInput(node, if_false);
Node* efalse = node; Node* efalse = if_false = node;
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge); Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
// Wire the new diamond into the graph, {node} can still throw. // Wire the new diamond into the graph, {node} can still throw.
NodeProperties::ReplaceUses(node, node, ephi, node, node); NodeProperties::ReplaceUses(node, node, ephi, merge, merge);
NodeProperties::ReplaceControlInput(merge, if_false, 1);
NodeProperties::ReplaceEffectInput(ephi, efalse, 1); NodeProperties::ReplaceEffectInput(ephi, efalse, 1);
// TODO(mstarzinger): This iteration cuts out the IfSuccess projection from // This iteration cuts out potential {IfSuccess} or {IfException} projection
// the node and places it inside the diamond. Come up with a helper method! // uses of the original node and places them inside the diamond, so that we
for (Node* use : node->uses()) { // can change the original {node} into the slow-path runtime call.
if (use->opcode() == IrOpcode::kIfSuccess) { for (Edge edge : merge->use_edges()) {
use->ReplaceUses(merge); if (!NodeProperties::IsControlEdge(edge)) continue;
merge->ReplaceInput(1, use); if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
NodeProperties::ReplaceUses(edge.from(), nullptr, nullptr, merge);
NodeProperties::ReplaceControlInput(merge, edge.from(), 1);
edge.UpdateTo(node);
}
if (edge.from()->opcode() == IrOpcode::kIfException) {
NodeProperties::ReplaceEffectInput(edge.from(), node);
edge.UpdateTo(node);
} }
} }
......
...@@ -226,26 +226,21 @@ Reduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate) { ...@@ -226,26 +226,21 @@ Reduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate) {
// to the known {target}); the last input is the control dependency. // to the known {target}); the last input is the control dependency.
inputs[0] = target; inputs[0] = target;
inputs[input_count - 1] = if_successes[i]; inputs[input_count - 1] = if_successes[i];
calls[i] = graph()->NewNode(node->op(), input_count, inputs); calls[i] = if_successes[i] =
if_successes[i] = graph()->NewNode(common()->IfSuccess(), calls[i]); graph()->NewNode(node->op(), input_count, inputs);
} }
// Check if we have an exception projection for the call {node}. // Check if we have an exception projection for the call {node}.
Node* if_exception = nullptr; Node* if_exception = nullptr;
for (Edge const edge : node->use_edges()) { if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
if (NodeProperties::IsControlEdge(edge) &&
edge.from()->opcode() == IrOpcode::kIfException) {
if_exception = edge.from();
break;
}
}
if (if_exception != nullptr) {
// Morph the {if_exception} projection into a join.
Node* if_exceptions[kMaxCallPolymorphism + 1]; Node* if_exceptions[kMaxCallPolymorphism + 1];
for (int i = 0; i < num_calls; ++i) { for (int i = 0; i < num_calls; ++i) {
if_successes[i] = graph()->NewNode(common()->IfSuccess(), calls[i]);
if_exceptions[i] = if_exceptions[i] =
graph()->NewNode(common()->IfException(), calls[i], calls[i]); graph()->NewNode(common()->IfException(), calls[i], calls[i]);
} }
// Morph the {if_exception} projection into a join.
Node* exception_control = Node* exception_control =
graph()->NewNode(common()->Merge(num_calls), num_calls, if_exceptions); graph()->NewNode(common()->Merge(num_calls), num_calls, if_exceptions);
if_exceptions[num_calls] = exception_control; if_exceptions[num_calls] = exception_control;
...@@ -258,7 +253,7 @@ Reduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate) { ...@@ -258,7 +253,7 @@ Reduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate) {
exception_control); exception_control);
} }
// Morph the call site into the dispatched call sites. // Morph the original call site into a join of the dispatched call sites.
Node* control = Node* control =
graph()->NewNode(common()->Merge(num_calls), num_calls, if_successes); graph()->NewNode(common()->Merge(num_calls), num_calls, if_successes);
calls[num_calls] = control; calls[num_calls] = control;
......
...@@ -141,12 +141,15 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, ...@@ -141,12 +141,15 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
int subcall_count = static_cast<int>(uncaught_subcalls.size()); int subcall_count = static_cast<int>(uncaught_subcalls.size());
if (subcall_count > 0) { if (subcall_count > 0) {
TRACE( TRACE(
"Inlinee contains %d calls without IfException; " "Inlinee contains %d calls without local exception handler; "
"linking to existing IfException\n", "linking to surrounding exception handler\n",
subcall_count); subcall_count);
} }
NodeVector on_exception_nodes(local_zone_); NodeVector on_exception_nodes(local_zone_);
for (Node* subcall : uncaught_subcalls) { for (Node* subcall : uncaught_subcalls) {
Node* on_success = graph()->NewNode(common()->IfSuccess(), subcall);
NodeProperties::ReplaceUses(subcall, subcall, subcall, on_success);
NodeProperties::ReplaceControlInput(on_success, subcall);
Node* on_exception = Node* on_exception =
graph()->NewNode(common()->IfException(), subcall, subcall); graph()->NewNode(common()->IfException(), subcall, subcall);
on_exception_nodes.push_back(on_exception); on_exception_nodes.push_back(on_exception);
...@@ -578,24 +581,18 @@ Reduction JSInliner::ReduceJSCall(Node* node) { ...@@ -578,24 +581,18 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
end = graph()->end(); end = graph()->end();
} }
// If we are inlining into a surrounding exception handler, we collect all
// potentially throwing nodes within the inlinee that are not handled locally
// by the inlinee itself. They are later wired into the surrounding handler.
NodeVector uncaught_subcalls(local_zone_); NodeVector uncaught_subcalls(local_zone_);
if (exception_target != nullptr) { if (exception_target != nullptr) {
// Find all uncaught 'calls' in the inlinee. // Find all uncaught 'calls' in the inlinee.
AllNodes inlined_nodes(local_zone_, end, graph()); AllNodes inlined_nodes(local_zone_, end, graph());
for (Node* subnode : inlined_nodes.reachable) { for (Node* subnode : inlined_nodes.reachable) {
// Every possibly throwing node with an IfSuccess should get an // Every possibly throwing node should get {IfSuccess} and {IfException}
// IfException. // projections, unless there already is local exception handling.
if (subnode->op()->HasProperty(Operator::kNoThrow)) { if (subnode->op()->HasProperty(Operator::kNoThrow)) continue;
continue; if (!NodeProperties::IsExceptionalCall(subnode)) {
}
bool hasIfException = false;
for (Node* use : subnode->uses()) {
if (use->opcode() == IrOpcode::kIfException) {
hasIfException = true;
break;
}
}
if (!hasIfException) {
DCHECK_EQ(2, subnode->op()->ControlOutputCount()); DCHECK_EQ(2, subnode->op()->ControlOutputCount());
uncaught_subcalls.push_back(subnode); uncaught_subcalls.push_back(subnode);
} }
...@@ -634,9 +631,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) { ...@@ -634,9 +631,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
Node* create = Node* create =
graph()->NewNode(javascript()->Create(), call.target(), new_target, graph()->NewNode(javascript()->Create(), call.target(), new_target,
context, frame_state_inside, effect, control); context, frame_state_inside, effect, control);
Node* success = graph()->NewNode(common()->IfSuccess(), create); uncaught_subcalls.push_back(create); // Adds {IfSuccess} & {IfException}.
uncaught_subcalls.push_back(create); // Adds {IfException}. NodeProperties::ReplaceControlInput(node, create);
NodeProperties::ReplaceControlInput(node, success);
NodeProperties::ReplaceEffectInput(node, create); NodeProperties::ReplaceEffectInput(node, create);
// Insert a check of the return value to determine whether the return // Insert a check of the return value to determine whether the return
// value or the implicit receiver should be selected as a result of the // value or the implicit receiver should be selected as a result of the
......
...@@ -1356,11 +1356,10 @@ JSNativeContextSpecialization::BuildPropertyAccess( ...@@ -1356,11 +1356,10 @@ JSNativeContextSpecialization::BuildPropertyAccess(
// Introduce the call to the getter function. // Introduce the call to the getter function.
if (access_info.constant()->IsJSFunction()) { if (access_info.constant()->IsJSFunction()) {
value = effect = graph()->NewNode( value = effect = control = graph()->NewNode(
javascript()->Call(2, 0.0f, VectorSlotPair(), javascript()->Call(2, 0.0f, VectorSlotPair(),
ConvertReceiverMode::kNotNullOrUndefined), ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, context, frame_state0, effect, control); target, receiver, context, frame_state0, effect, control);
control = graph()->NewNode(common()->IfSuccess(), value);
} else { } else {
DCHECK(access_info.constant()->IsFunctionTemplateInfo()); DCHECK(access_info.constant()->IsFunctionTemplateInfo());
Handle<FunctionTemplateInfo> function_template_info( Handle<FunctionTemplateInfo> function_template_info(
...@@ -1393,11 +1392,10 @@ JSNativeContextSpecialization::BuildPropertyAccess( ...@@ -1393,11 +1392,10 @@ JSNativeContextSpecialization::BuildPropertyAccess(
// Introduce the call to the setter function. // Introduce the call to the setter function.
if (access_info.constant()->IsJSFunction()) { if (access_info.constant()->IsJSFunction()) {
effect = graph()->NewNode( effect = control = graph()->NewNode(
javascript()->Call(3, 0.0f, VectorSlotPair(), javascript()->Call(3, 0.0f, VectorSlotPair(),
ConvertReceiverMode::kNotNullOrUndefined), ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, value, context, frame_state0, effect, control); target, receiver, value, context, frame_state0, effect, control);
control = graph()->NewNode(common()->IfSuccess(), effect);
} else { } else {
DCHECK(access_info.constant()->IsFunctionTemplateInfo()); DCHECK(access_info.constant()->IsFunctionTemplateInfo());
Handle<FunctionTemplateInfo> function_template_info( Handle<FunctionTemplateInfo> function_template_info(
...@@ -1662,7 +1660,6 @@ JSNativeContextSpecialization::BuildPropertyAccess( ...@@ -1662,7 +1660,6 @@ JSNativeContextSpecialization::BuildPropertyAccess(
value = effect = control = value = effect = control =
graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs); graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs);
control = graph()->NewNode(common()->IfSuccess(), control);
} }
return ValueEffectControl(value, effect, control); return ValueEffectControl(value, effect, control);
...@@ -2105,10 +2102,10 @@ JSNativeContextSpecialization::InlineApiCall( ...@@ -2105,10 +2102,10 @@ JSNativeContextSpecialization::InlineApiCall(
inputs[6] = value; inputs[6] = value;
} }
Node* control0;
Node* effect0; Node* effect0;
Node* value0 = effect0 = Node* value0 = effect0 = control0 =
graph()->NewNode(common()->Call(call_descriptor), index, inputs); graph()->NewNode(common()->Call(call_descriptor), index, inputs);
Node* control0 = graph()->NewNode(common()->IfSuccess(), value0);
return ValueEffectControl(value0, effect0, control0); return ValueEffectControl(value0, effect0, control0);
} }
......
...@@ -261,19 +261,7 @@ class JSBinopReduction final { ...@@ -261,19 +261,7 @@ class JSBinopReduction final {
// Reconnect the control output to bypass the IfSuccess node and // Reconnect the control output to bypass the IfSuccess node and
// possibly disconnect from the IfException node. // possibly disconnect from the IfException node.
for (Edge edge : node_->use_edges()) { lowering_->RelaxControls(node_);
Node* const user = edge.from();
DCHECK(!user->IsDead());
if (NodeProperties::IsControlEdge(edge)) {
if (user->opcode() == IrOpcode::kIfSuccess) {
user->ReplaceUses(NodeProperties::GetControlInput(node_));
user->Kill();
} else {
DCHECK_EQ(user->opcode(), IrOpcode::kIfException);
edge.UpdateTo(jsgraph()->Dead());
}
}
}
// Remove the frame state and the context. // Remove the frame state and the context.
if (OperatorProperties::HasFrameStateInput(node_->op())) { if (OperatorProperties::HasFrameStateInput(node_->op())) {
...@@ -414,9 +402,7 @@ class JSBinopReduction final { ...@@ -414,9 +402,7 @@ class JSBinopReduction final {
DCHECK(!NodeProperties::GetType(node)->Is(Type::PlainPrimitive())); DCHECK(!NodeProperties::GetType(node)->Is(Type::PlainPrimitive()));
Node* const n = graph()->NewNode(javascript()->ToNumber(), node, context(), Node* const n = graph()->NewNode(javascript()->ToNumber(), node, context(),
frame_state, effect(), control()); frame_state, effect(), control());
Node* const if_success = graph()->NewNode(common()->IfSuccess(), n); NodeProperties::ReplaceControlInput(node_, n);
NodeProperties::ReplaceControlInput(node_, if_success);
NodeProperties::ReplaceUses(node_, node_, node_, node_, n);
update_effect(n); update_effect(n);
return n; return n;
} }
...@@ -688,25 +674,27 @@ Reduction JSTypedLowering::ReduceCreateConsString(Node* node) { ...@@ -688,25 +674,27 @@ Reduction JSTypedLowering::ReduceCreateConsString(Node* node) {
Node* efalse = effect; Node* efalse = effect;
{ {
// Throw a RangeError in case of overflow. // Throw a RangeError in case of overflow.
Node* vfalse = efalse = graph()->NewNode( Node* vfalse = efalse = if_false = graph()->NewNode(
javascript()->CallRuntime(Runtime::kThrowInvalidStringLength), javascript()->CallRuntime(Runtime::kThrowInvalidStringLength),
context, frame_state, efalse, if_false); context, frame_state, efalse, if_false);
if_false = graph()->NewNode(common()->IfSuccess(), vfalse);
if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), if_false);
Revisit(graph()->end());
// Update potential {IfException} uses of {node} to point to the // Update potential {IfException} uses of {node} to point to the
// %ThrowInvalidStringLength runtime call node instead. // %ThrowInvalidStringLength runtime call node instead.
for (Edge edge : node->use_edges()) { Node* on_exception = nullptr;
if (edge.from()->opcode() == IrOpcode::kIfException) { if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
DCHECK(NodeProperties::IsControlEdge(edge) || NodeProperties::ReplaceControlInput(on_exception, vfalse);
NodeProperties::IsEffectEdge(edge)); NodeProperties::ReplaceEffectInput(on_exception, efalse);
edge.UpdateTo(vfalse); if_false = graph()->NewNode(common()->IfSuccess(), vfalse);
Revisit(edge.from()); Revisit(on_exception);
}
} }
// The above %ThrowInvalidStringLength runtime call is an unconditional
// throw, making it impossible to return a successful completion in this
// case. We simply connect the successful completion to the graph end.
if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), if_false);
Revisit(graph()->end());
} }
control = graph()->NewNode(common()->IfTrue(), branch); control = graph()->NewNode(common()->IfTrue(), branch);
} }
...@@ -1233,10 +1221,9 @@ Reduction JSTypedLowering::ReduceJSToObject(Node* node) { ...@@ -1233,10 +1221,9 @@ Reduction JSTypedLowering::ReduceJSToObject(Node* node) {
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNeedsFrameState, node->op()->properties()); CallDescriptor::kNeedsFrameState, node->op()->properties());
rfalse = efalse = graph()->NewNode( rfalse = efalse = if_false = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()), common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
receiver, context, frame_state, efalse, if_false); receiver, context, frame_state, efalse, if_false);
if_false = graph()->NewNode(common()->IfSuccess(), rfalse);
} }
control = graph()->NewNode(common()->Merge(2), if_true, if_false); control = graph()->NewNode(common()->Merge(2), if_true, if_false);
...@@ -1506,18 +1493,18 @@ Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node) { ...@@ -1506,18 +1493,18 @@ Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node) {
Node* vfalse1; Node* vfalse1;
{ {
// Slow path, need to call the %HasInPrototypeChain runtime function. // Slow path, need to call the %HasInPrototypeChain runtime function.
vfalse1 = efalse1 = graph()->NewNode( vfalse1 = efalse1 = if_false1 = graph()->NewNode(
javascript()->CallRuntime(Runtime::kHasInPrototypeChain), object, javascript()->CallRuntime(Runtime::kHasInPrototypeChain), object,
prototype, context, frame_state, efalse1, if_false1); prototype, context, frame_state, efalse1, if_false1);
if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1);
// Replace any potential IfException on {node} to catch exceptions // Replace any potential {IfException} uses of {node} to catch exceptions
// from this %HasInPrototypeChain runtime call instead. // from this %HasInPrototypeChain runtime call instead.
for (Edge edge : node->use_edges()) { Node* on_exception = nullptr;
if (edge.from()->opcode() == IrOpcode::kIfException) { if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
edge.UpdateTo(vfalse1); NodeProperties::ReplaceControlInput(on_exception, vfalse1);
Revisit(edge.from()); NodeProperties::ReplaceEffectInput(on_exception, efalse1);
} if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1);
Revisit(on_exception);
} }
} }
...@@ -2211,10 +2198,9 @@ Reduction JSTypedLowering::ReduceJSForInNext(Node* node) { ...@@ -2211,10 +2198,9 @@ Reduction JSTypedLowering::ReduceJSForInNext(Node* node) {
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNeedsFrameState); CallDescriptor::kNeedsFrameState);
vfalse0 = efalse0 = graph()->NewNode( vfalse0 = efalse0 = if_false0 = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()), key, common()->Call(desc), jsgraph()->HeapConstant(callable.code()), key,
receiver, context, frame_state, effect, if_false0); receiver, context, frame_state, effect, if_false0);
if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0);
} }
control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
......
...@@ -2909,7 +2909,6 @@ void SimplifiedLowering::DoJSToNumberTruncatesToFloat64( ...@@ -2909,7 +2909,6 @@ void SimplifiedLowering::DoJSToNumberTruncatesToFloat64(
Node* frame_state = node->InputAt(2); Node* frame_state = node->InputAt(2);
Node* effect = node->InputAt(3); Node* effect = node->InputAt(3);
Node* control = node->InputAt(4); Node* control = node->InputAt(4);
Node* throwing;
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value); Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
Node* branch0 = Node* branch0 =
...@@ -2927,10 +2926,18 @@ void SimplifiedLowering::DoJSToNumberTruncatesToFloat64( ...@@ -2927,10 +2926,18 @@ void SimplifiedLowering::DoJSToNumberTruncatesToFloat64(
Node* efalse0 = effect; Node* efalse0 = effect;
Node* vfalse0; Node* vfalse0;
{ {
throwing = vfalse0 = efalse0 = vfalse0 = efalse0 = if_false0 =
graph()->NewNode(ToNumberOperator(), ToNumberCode(), value, context, graph()->NewNode(ToNumberOperator(), ToNumberCode(), value, context,
frame_state, efalse0, if_false0); frame_state, efalse0, if_false0);
if_false0 = graph()->NewNode(common()->IfSuccess(), throwing);
// Update potential {IfException} uses of {node} to point to the above
// {ToNumber} stub call node instead.
Node* on_exception = nullptr;
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
NodeProperties::ReplaceControlInput(on_exception, vfalse0);
NodeProperties::ReplaceEffectInput(on_exception, efalse0);
if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0);
}
Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0); Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0);
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0); Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
...@@ -2972,10 +2979,9 @@ void SimplifiedLowering::DoJSToNumberTruncatesToFloat64( ...@@ -2972,10 +2979,9 @@ void SimplifiedLowering::DoJSToNumberTruncatesToFloat64(
if (edge.from()->opcode() == IrOpcode::kIfSuccess) { if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
edge.from()->ReplaceUses(control); edge.from()->ReplaceUses(control);
edge.from()->Kill(); edge.from()->Kill();
} else if (edge.from()->opcode() == IrOpcode::kIfException) {
edge.UpdateTo(throwing);
} else { } else {
UNREACHABLE(); DCHECK(edge.from()->opcode() != IrOpcode::kIfException);
edge.UpdateTo(control);
} }
} else if (NodeProperties::IsEffectEdge(edge)) { } else if (NodeProperties::IsEffectEdge(edge)) {
edge.UpdateTo(effect); edge.UpdateTo(effect);
...@@ -2993,7 +2999,6 @@ void SimplifiedLowering::DoJSToNumberTruncatesToWord32( ...@@ -2993,7 +2999,6 @@ void SimplifiedLowering::DoJSToNumberTruncatesToWord32(
Node* frame_state = node->InputAt(2); Node* frame_state = node->InputAt(2);
Node* effect = node->InputAt(3); Node* effect = node->InputAt(3);
Node* control = node->InputAt(4); Node* control = node->InputAt(4);
Node* throwing;
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value); Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
Node* branch0 = Node* branch0 =
...@@ -3008,10 +3013,18 @@ void SimplifiedLowering::DoJSToNumberTruncatesToWord32( ...@@ -3008,10 +3013,18 @@ void SimplifiedLowering::DoJSToNumberTruncatesToWord32(
Node* efalse0 = effect; Node* efalse0 = effect;
Node* vfalse0; Node* vfalse0;
{ {
throwing = vfalse0 = efalse0 = vfalse0 = efalse0 = if_false0 =
graph()->NewNode(ToNumberOperator(), ToNumberCode(), value, context, graph()->NewNode(ToNumberOperator(), ToNumberCode(), value, context,
frame_state, efalse0, if_false0); frame_state, efalse0, if_false0);
if_false0 = graph()->NewNode(common()->IfSuccess(), throwing);
// Update potential {IfException} uses of {node} to point to the above
// {ToNumber} stub call node instead.
Node* on_exception = nullptr;
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
NodeProperties::ReplaceControlInput(on_exception, vfalse0);
NodeProperties::ReplaceEffectInput(on_exception, efalse0);
if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0);
}
Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0); Node* check1 = graph()->NewNode(simplified()->ObjectIsSmi(), vfalse0);
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0); Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
...@@ -3049,10 +3062,9 @@ void SimplifiedLowering::DoJSToNumberTruncatesToWord32( ...@@ -3049,10 +3062,9 @@ void SimplifiedLowering::DoJSToNumberTruncatesToWord32(
if (edge.from()->opcode() == IrOpcode::kIfSuccess) { if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
edge.from()->ReplaceUses(control); edge.from()->ReplaceUses(control);
edge.from()->Kill(); edge.from()->Kill();
} else if (edge.from()->opcode() == IrOpcode::kIfException) {
edge.UpdateTo(throwing);
} else { } else {
UNREACHABLE(); DCHECK(edge.from()->opcode() != IrOpcode::kIfException);
edge.UpdateTo(control);
} }
} else if (NodeProperties::IsEffectEdge(edge)) { } else if (NodeProperties::IsEffectEdge(edge)) {
edge.UpdateTo(effect); edge.UpdateTo(effect);
......
...@@ -18,67 +18,59 @@ Reduction TailCallOptimization::Reduce(Node* node) { ...@@ -18,67 +18,59 @@ Reduction TailCallOptimization::Reduce(Node* node) {
if (node->opcode() != IrOpcode::kReturn) return NoChange(); if (node->opcode() != IrOpcode::kReturn) return NoChange();
// The value which is returned must be the result of a potential tail call, // The value which is returned must be the result of a potential tail call,
// there must be no try/catch/finally around the Call, and there must be no // there must be no try/catch/finally around the Call, and there must be no
// other effect between the Call and the Return nodes. // other effect or control between the Call and the Return nodes.
Node* const call = NodeProperties::GetValueInput(node, 1); Node* const call = NodeProperties::GetValueInput(node, 1);
if (call->opcode() == IrOpcode::kCall && if (call->opcode() == IrOpcode::kCall &&
CallDescriptorOf(call->op())->SupportsTailCalls() && CallDescriptorOf(call->op())->SupportsTailCalls() &&
NodeProperties::GetEffectInput(node) == call && NodeProperties::GetEffectInput(node) == call &&
!NodeProperties::IsExceptionalCall(call)) { NodeProperties::GetControlInput(node) == call &&
Node* const control = NodeProperties::GetControlInput(node); !NodeProperties::IsExceptionalCall(call) && call->UseCount() == 3) {
// Ensure that no additional arguments are being popped other than those in // Ensure that no additional arguments are being popped other than those in
// the CallDescriptor, otherwise the tail call transformation is invalid. // the CallDescriptor, otherwise the tail call transformation is invalid.
DCHECK_EQ(0, Int32Matcher(NodeProperties::GetValueInput(node, 0)).Value()); DCHECK_EQ(0, Int32Matcher(NodeProperties::GetValueInput(node, 0)).Value());
if (control->opcode() == IrOpcode::kIfSuccess && // Furthermore, the Return node value, effect, and control depends
call->OwnedBy(node, control) && control->OwnedBy(node)) { // directly on the Call, no other uses of the Call node exist.
// Furthermore, control has to flow via an IfSuccess from the Call, so //
// the Return node value and effect depends directly on the Call node, // The input graph looks as follows:
// and indirectly control depends on the Call via an IfSuccess.
// Value1 ... ValueN Effect Control // Value1 ... ValueN Effect Control
// ^ ^ ^ ^ // ^ ^ ^ ^
// | | | | // | | | |
// | +--+ +-+ | // | +--+ +-+ |
// +----------+ | | +------+ // +----------+ | | +------+
// \ | | / // \ | | /
// Call[Descriptor] // Call[Descriptor]
// ^ ^ ^ // ^ ^ ^
// | | | // Int32(0) <-+ | | |
// +-+ | | // \ | | |
// | | | // Return
// | +-+ | // ^
// | | IfSuccess // |
// | | ^
// | | |
// Return
// ^
// |
// The resulting graph looks like this: // The resulting graph looks like this:
// Value1 ... ValueN Effect Control // Value1 ... ValueN Effect Control
// ^ ^ ^ ^ // ^ ^ ^ ^
// | | | | // | | | |
// | +--+ +-+ | // | +--+ +-+ |
// +----------+ | | +------+ // +----------+ | | +------+
// \ | | / // \ | | /
// TailCall[Descriptor] // TailCall[Descriptor]
// ^ // ^
// | // |
DCHECK_EQ(call, NodeProperties::GetControlInput(control, 0)); DCHECK_EQ(4, node->InputCount());
DCHECK_EQ(4, node->InputCount()); node->ReplaceInput(0, NodeProperties::GetEffectInput(call));
node->ReplaceInput(0, NodeProperties::GetEffectInput(call)); node->ReplaceInput(1, NodeProperties::GetControlInput(call));
node->ReplaceInput(1, NodeProperties::GetControlInput(call)); node->RemoveInput(3);
node->RemoveInput(3); node->RemoveInput(2);
node->RemoveInput(2); for (int index = 0; index < call->op()->ValueInputCount(); ++index) {
for (int index = 0; index < call->op()->ValueInputCount(); ++index) { node->InsertInput(graph()->zone(), index,
node->InsertInput(graph()->zone(), index, NodeProperties::GetValueInput(call, index));
NodeProperties::GetValueInput(call, index));
}
NodeProperties::ChangeOp(
node, common()->TailCall(CallDescriptorOf(call->op())));
return Changed(node);
} }
NodeProperties::ChangeOp(node,
common()->TailCall(CallDescriptorOf(call->op())));
return Changed(node);
} }
return NoChange(); return NoChange();
} }
......
...@@ -152,27 +152,45 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -152,27 +152,45 @@ void Verifier::Visitor::Check(Node* node) {
"control"); "control");
} }
// Verify that nodes that can throw only have IfSuccess/IfException control // Verify that nodes that can throw either have both IfSuccess/IfException
// uses. // projections as the only control uses or no projections at all.
if (!node->op()->HasProperty(Operator::kNoThrow)) { if (!node->op()->HasProperty(Operator::kNoThrow)) {
int count_success = 0, count_exception = 0; Node* discovered_if_exception = nullptr;
Node* discovered_if_success = nullptr;
int total_number_of_control_uses = 0;
for (Edge edge : node->use_edges()) { for (Edge edge : node->use_edges()) {
if (!NodeProperties::IsControlEdge(edge)) { if (!NodeProperties::IsControlEdge(edge)) {
continue; continue;
} }
total_number_of_control_uses++;
Node* control_use = edge.from(); Node* control_use = edge.from();
if (control_use->opcode() != IrOpcode::kIfSuccess && if (control_use->opcode() == IrOpcode::kIfSuccess) {
control_use->opcode() != IrOpcode::kIfException) { CHECK_NULL(discovered_if_success); // Only one allowed.
V8_Fatal(__FILE__, __LINE__, discovered_if_success = control_use;
"#%d:%s should be followed by IfSuccess/IfException, but is " }
"followed by #%d:%s", if (control_use->opcode() == IrOpcode::kIfException) {
node->id(), node->op()->mnemonic(), control_use->id(), CHECK_NULL(discovered_if_exception); // Only one allowed.
control_use->op()->mnemonic()); discovered_if_exception = control_use;
} }
if (control_use->opcode() == IrOpcode::kIfSuccess) ++count_success; }
if (control_use->opcode() == IrOpcode::kIfException) ++count_exception; if (discovered_if_success && !discovered_if_exception) {
CHECK_LE(count_success, 1); V8_Fatal(__FILE__, __LINE__,
CHECK_LE(count_exception, 1); "#%d:%s should be followed by IfSuccess/IfException, but is "
"only followed by single #%d:%s",
node->id(), node->op()->mnemonic(),
discovered_if_success->id(),
discovered_if_success->op()->mnemonic());
}
if (discovered_if_exception && !discovered_if_success) {
V8_Fatal(__FILE__, __LINE__,
"#%d:%s should be followed by IfSuccess/IfException, but is "
"only followed by single #%d:%s",
node->id(), node->op()->mnemonic(),
discovered_if_exception->id(),
discovered_if_exception->op()->mnemonic());
}
if (discovered_if_success || discovered_if_exception) {
CHECK_EQ(2, total_number_of_control_uses);
} }
} }
} }
......
...@@ -2525,65 +2525,6 @@ Node* WasmGraphBuilder::BuildChangeTaggedToFloat64(Node* value) { ...@@ -2525,65 +2525,6 @@ Node* WasmGraphBuilder::BuildChangeTaggedToFloat64(Node* value) {
MachineOperatorBuilder* machine = jsgraph()->machine(); MachineOperatorBuilder* machine = jsgraph()->machine();
CommonOperatorBuilder* common = jsgraph()->common(); CommonOperatorBuilder* common = jsgraph()->common();
if (CanCover(value, IrOpcode::kJSToNumber)) {
// ChangeTaggedToFloat64(JSToNumber(x)) =>
// if IsSmi(x) then ChangeSmiToFloat64(x)
// else let y = JSToNumber(x) in
// if IsSmi(y) then ChangeSmiToFloat64(y)
// else BuildLoadHeapNumberValue(y)
Node* object = NodeProperties::GetValueInput(value, 0);
Node* context = NodeProperties::GetContextInput(value);
Node* frame_state = NodeProperties::GetFrameStateInput(value);
Node* effect = NodeProperties::GetEffectInput(value);
Node* control = NodeProperties::GetControlInput(value);
const Operator* merge_op = common->Merge(2);
const Operator* ephi_op = common->EffectPhi(2);
const Operator* phi_op = common->Phi(MachineRepresentation::kFloat64, 2);
Node* check1 = BuildTestNotSmi(object);
Node* branch1 =
graph()->NewNode(common->Branch(BranchHint::kFalse), check1, control);
Node* if_true1 = graph()->NewNode(common->IfTrue(), branch1);
Node* vtrue1 = graph()->NewNode(value->op(), object, context, frame_state,
effect, if_true1);
Node* etrue1 = vtrue1;
Node* check2 = BuildTestNotSmi(vtrue1);
Node* branch2 = graph()->NewNode(common->Branch(), check2, if_true1);
Node* if_true2 = graph()->NewNode(common->IfTrue(), branch2);
Node* vtrue2 = BuildLoadHeapNumberValue(vtrue1, if_true2);
Node* if_false2 = graph()->NewNode(common->IfFalse(), branch2);
Node* vfalse2 = BuildChangeSmiToFloat64(vtrue1);
if_true1 = graph()->NewNode(merge_op, if_true2, if_false2);
vtrue1 = graph()->NewNode(phi_op, vtrue2, vfalse2, if_true1);
Node* if_false1 = graph()->NewNode(common->IfFalse(), branch1);
Node* vfalse1 = BuildChangeSmiToFloat64(object);
Node* efalse1 = effect;
Node* merge1 = graph()->NewNode(merge_op, if_true1, if_false1);
Node* ephi1 = graph()->NewNode(ephi_op, etrue1, efalse1, merge1);
Node* phi1 = graph()->NewNode(phi_op, vtrue1, vfalse1, merge1);
// Wire the new diamond into the graph, {JSToNumber} can still throw.
NodeProperties::ReplaceUses(value, phi1, ephi1, etrue1, etrue1);
// TODO(mstarzinger): This iteration cuts out the IfSuccess projection from
// the node and places it inside the diamond. Come up with a helper method!
for (Node* use : etrue1->uses()) {
if (use->opcode() == IrOpcode::kIfSuccess) {
use->ReplaceUses(merge1);
NodeProperties::ReplaceControlInput(branch2, use);
}
}
return phi1;
}
Node* check = BuildTestNotSmi(value); Node* check = BuildTestNotSmi(value);
Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), check, Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), check,
graph()->start()); graph()->start());
......
...@@ -153,127 +153,6 @@ TEST_F(EffectControlLinearizerTest, DiamondLoad) { ...@@ -153,127 +153,6 @@ TEST_F(EffectControlLinearizerTest, DiamondLoad) {
ret, IsReturn(phi, IsEffectPhi(vtrue, graph()->start(), merge), merge)); ret, IsReturn(phi, IsEffectPhi(vtrue, graph()->start(), merge), merge));
} }
TEST_F(EffectControlLinearizerTest, FloatingDiamondsControlWiring) {
Schedule schedule(zone());
// Create the graph and schedule. Roughly (omitting effects and unimportant
// nodes):
//
// BLOCK 0:
// r1: Start
// c1: Call
// b1: Branch(const0, s1)
// |
// +-------+------+
// | |
// BLOCK 1: BLOCK 2:
// t1: IfTrue(b1) f1: IfFalse(b1)
// | |
// +-------+------+
// |
// BLOCK 3:
// m1: Merge(t1, f1)
// c2: IfSuccess(c1)
// b2: Branch(const0 , s1)
// |
// +-------+------+
// | |
// BLOCK 4: BLOCK 5:
// t2: IfTrue(b2) f2:IfFalse(b2)
// | |
// +-------+------+
// |
// BLOCK 6:
// m2: Merge(t2, f2)
// r1: Return(c1, c2)
LinkageLocation kLocationSignature[] = {
LinkageLocation::ForRegister(0, MachineType::Pointer()),
LinkageLocation::ForRegister(1, MachineType::Pointer())};
const CallDescriptor* kCallDescriptor = new (zone()) CallDescriptor(
CallDescriptor::kCallCodeObject, MachineType::AnyTagged(),
LinkageLocation::ForRegister(0, MachineType::Pointer()),
new (zone()) LocationSignature(1, 1, kLocationSignature), 0,
Operator::kNoProperties, 0, 0, CallDescriptor::kNoFlags);
Node* p0 = Parameter(0);
Node* p1 = Parameter(1);
Node* const0 = Int32Constant(0);
Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1,
graph()->start(), graph()->start());
Node* if_success = graph()->NewNode(common()->IfSuccess(), call);
// First Floating diamond.
Node* branch1 =
graph()->NewNode(common()->Branch(), const0, graph()->start());
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* merge1 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
// Second floating diamond.
Node* branch2 =
graph()->NewNode(common()->Branch(), const0, graph()->start());
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
Node* merge2 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = graph()->NewNode(common()->Return(), zero, call, graph()->start(),
if_success);
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* t1block = AddBlockToSchedule(&schedule);
BasicBlock* f1block = AddBlockToSchedule(&schedule);
BasicBlock* m1block = AddBlockToSchedule(&schedule);
BasicBlock* t2block = AddBlockToSchedule(&schedule);
BasicBlock* f2block = AddBlockToSchedule(&schedule);
BasicBlock* m2block = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddNode(start, p0);
schedule.AddNode(start, p1);
schedule.AddNode(start, const0);
schedule.AddNode(start, call);
schedule.AddBranch(start, branch1, t1block, f1block);
schedule.AddNode(t1block, if_true1);
schedule.AddGoto(t1block, m1block);
schedule.AddNode(f1block, if_false1);
schedule.AddGoto(f1block, m1block);
schedule.AddNode(m1block, merge1);
// The scheduler does not always put the IfSuccess node to the corresponding
// call's block, simulate that here.
schedule.AddNode(m1block, if_success);
schedule.AddBranch(m1block, branch2, t2block, f2block);
schedule.AddNode(t2block, if_true2);
schedule.AddGoto(t2block, m2block);
schedule.AddNode(f2block, if_false2);
schedule.AddGoto(f2block, m2block);
schedule.AddNode(m2block, merge2);
schedule.AddReturn(m2block, ret);
// Run the state effect introducer.
EffectControlLinearizer introducer(jsgraph(), &schedule, zone(),
source_positions());
introducer.Run();
// The effect input to the return should be an effect phi with the
// newly introduced effectful change operators.
ASSERT_THAT(ret, IsReturn(call, call, merge2));
ASSERT_THAT(branch2, IsBranch(const0, merge1));
ASSERT_THAT(branch1, IsBranch(const0, if_success));
ASSERT_THAT(if_success, IsIfSuccess(call));
}
TEST_F(EffectControlLinearizerTest, LoopLoad) { TEST_F(EffectControlLinearizerTest, LoopLoad) {
Schedule schedule(zone()); Schedule schedule(zone());
......
...@@ -38,10 +38,8 @@ TEST_F(TailCallOptimizationTest, CallCodeObject0) { ...@@ -38,10 +38,8 @@ TEST_F(TailCallOptimizationTest, CallCodeObject0) {
Node* p1 = Parameter(1); Node* p1 = Parameter(1);
Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1, Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1,
graph()->start(), graph()->start()); graph()->start(), graph()->start());
Node* if_success = graph()->NewNode(common()->IfSuccess(), call);
Node* zero = graph()->NewNode(common()->Int32Constant(0)); Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = Node* ret = graph()->NewNode(common()->Return(), zero, call, call, call);
graph()->NewNode(common()->Return(), zero, call, call, if_success);
Reduction r = Reduce(ret); Reduction r = Reduce(ret);
ASSERT_FALSE(r.Changed()); ASSERT_FALSE(r.Changed());
} }
...@@ -85,10 +83,8 @@ TEST_F(TailCallOptimizationTest, CallCodeObject2) { ...@@ -85,10 +83,8 @@ TEST_F(TailCallOptimizationTest, CallCodeObject2) {
Node* p1 = Parameter(1); Node* p1 = Parameter(1);
Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1, Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1,
graph()->start(), graph()->start()); graph()->start(), graph()->start());
Node* if_success = graph()->NewNode(common()->IfSuccess(), call);
Node* zero = graph()->NewNode(common()->Int32Constant(0)); Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = Node* ret = graph()->NewNode(common()->Return(), zero, call, call, call);
graph()->NewNode(common()->Return(), zero, call, call, if_success);
Reduction r = Reduce(ret); Reduction r = Reduce(ret);
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTailCall(kCallDescriptor, p0, p1, EXPECT_THAT(r.replacement(), IsTailCall(kCallDescriptor, p0, p1,
...@@ -109,10 +105,8 @@ TEST_F(TailCallOptimizationTest, CallJSFunction0) { ...@@ -109,10 +105,8 @@ TEST_F(TailCallOptimizationTest, CallJSFunction0) {
Node* p1 = Parameter(1); Node* p1 = Parameter(1);
Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1, Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1,
graph()->start(), graph()->start()); graph()->start(), graph()->start());
Node* if_success = graph()->NewNode(common()->IfSuccess(), call);
Node* zero = graph()->NewNode(common()->Int32Constant(0)); Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = Node* ret = graph()->NewNode(common()->Return(), zero, call, call, call);
graph()->NewNode(common()->Return(), zero, call, call, if_success);
Reduction r = Reduce(ret); Reduction r = Reduce(ret);
ASSERT_FALSE(r.Changed()); ASSERT_FALSE(r.Changed());
} }
...@@ -155,10 +149,8 @@ TEST_F(TailCallOptimizationTest, CallJSFunction2) { ...@@ -155,10 +149,8 @@ TEST_F(TailCallOptimizationTest, CallJSFunction2) {
Node* p1 = Parameter(1); Node* p1 = Parameter(1);
Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1, Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1,
graph()->start(), graph()->start()); graph()->start(), graph()->start());
Node* if_success = graph()->NewNode(common()->IfSuccess(), call);
Node* zero = graph()->NewNode(common()->Int32Constant(0)); Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = Node* ret = graph()->NewNode(common()->Return(), zero, call, call, call);
graph()->NewNode(common()->Return(), zero, call, call, if_success);
Reduction r = Reduce(ret); Reduction r = Reduce(ret);
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTailCall(kCallDescriptor, p0, p1, EXPECT_THAT(r.replacement(), IsTailCall(kCallDescriptor, p0, p1,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment