%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/compiler/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/compiler/branch-elimination.cc |
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/branch-elimination.h"
#include "src/base/small-vector.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/opcodes.h"
namespace v8 {
namespace internal {
namespace compiler {
BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
Zone* zone, Phase phase)
: AdvancedReducerWithControlPathState(editor, zone, js_graph->graph()),
jsgraph_(js_graph),
dead_(js_graph->Dead()),
phase_(phase) {}
BranchElimination::~BranchElimination() = default;
Reduction BranchElimination::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kDead:
return NoChange();
case IrOpcode::kDeoptimizeIf:
case IrOpcode::kDeoptimizeUnless:
return ReduceDeoptimizeConditional(node);
case IrOpcode::kMerge:
return ReduceMerge(node);
case IrOpcode::kLoop:
return ReduceLoop(node);
case IrOpcode::kBranch:
return ReduceBranch(node);
case IrOpcode::kIfFalse:
return ReduceIf(node, false);
case IrOpcode::kIfTrue:
return ReduceIf(node, true);
case IrOpcode::kTrapIf:
case IrOpcode::kTrapUnless:
return ReduceTrapConditional(node);
case IrOpcode::kStart:
return ReduceStart(node);
default:
if (node->op()->ControlOutputCount() > 0) {
return ReduceOtherControl(node);
} else {
return NoChange();
}
}
}
void BranchElimination::SimplifyBranchCondition(Node* branch) {
// Try to use a phi as a branch condition if the control flow from the branch
// is known from previous branches. For example, in the graph below, the
// control flow of the second_branch is predictable because the first_branch
// use the same branch condition. In such case, create a new phi with constant
// inputs and let the second branch use the phi as its branch condition. From
// this transformation, more branch folding opportunities would be exposed to
// later passes through branch cloning in effect-control-linearizer.
//
// condition condition
// | \ |
// | first_branch first_branch
// | / \ / \
// | / \ / \
// |first_true first_false first_true first_false
// | \ / \ /
// | \ / \ /
// | first_merge ==> first_merge
// | | / |
// second_branch 1 0 / |
// / \ \ | / |
// / \ phi |
// second_true second_false \ |
// second_branch
// / \
// / \
// second_true second_false
//
auto SemanticsOf = [phase = this->phase_](Node* branch) {
BranchSemantics semantics = BranchSemantics::kUnspecified;
if (branch->opcode() == IrOpcode::kBranch) {
semantics = BranchParametersOf(branch->op()).semantics();
}
if (semantics == BranchSemantics::kUnspecified) {
semantics =
(phase == kEARLY ? BranchSemantics::kJS : BranchSemantics::kMachine);
}
return semantics;
};
DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
Node* merge = NodeProperties::GetControlInput(branch);
if (merge->opcode() != IrOpcode::kMerge) return;
Node* condition = branch->InputAt(0);
BranchSemantics semantics = SemanticsOf(branch);
Graph* graph = jsgraph()->graph();
base::SmallVector<Node*, 2> phi_inputs;
Node::Inputs inputs = merge->inputs();
int input_count = inputs.count();
for (int i = 0; i != input_count; ++i) {
Node* input = inputs[i];
ControlPathConditions from_input = GetState(input);
BranchCondition branch_condition = from_input.LookupState(condition);
if (!branch_condition.IsSet()) return;
if (SemanticsOf(branch_condition.branch) != semantics) return;
bool condition_value = branch_condition.is_true;
if (semantics == BranchSemantics::kJS) {
phi_inputs.emplace_back(jsgraph()->BooleanConstant(condition_value));
} else {
DCHECK_EQ(semantics, BranchSemantics::kMachine);
phi_inputs.emplace_back(
condition_value
? graph->NewNode(jsgraph()->common()->Int32Constant(1))
: graph->NewNode(jsgraph()->common()->Int32Constant(0)));
}
}
phi_inputs.emplace_back(merge);
Node* new_phi =
graph->NewNode(common()->Phi(semantics == BranchSemantics::kJS
? MachineRepresentation::kTagged
: MachineRepresentation::kWord32,
input_count),
input_count + 1, &phi_inputs.at(0));
// Replace the branch condition with the new phi.
NodeProperties::ReplaceValueInput(branch, new_phi, 0);
}
bool BranchElimination::TryEliminateBranchWithPhiCondition(Node* branch,
Node* phi,
Node* merge) {
// If the condition of the branch comes from two constant values,
// then try to merge the branches successors into its predecessors,
// and eliminate the (branch, phi, merge) nodes.
//
// pred0 pred1
// \ /
// merge 0 1
// | \___________ | /
// | \ | / pred0 pred1
// | phi | |
// | _____________/ => | |
// | / | |
// branch succ0 succ1
// / \
// false true
// | |
// succ0 succ1
//
DCHECK_EQ(branch->opcode(), IrOpcode::kBranch);
DCHECK_EQ(phi->opcode(), IrOpcode::kPhi);
DCHECK_EQ(merge->opcode(), IrOpcode::kMerge);
DCHECK_EQ(NodeProperties::GetControlInput(branch, 0), merge);
if (!phi->OwnedBy(branch)) return false;
if (phi->InputCount() != 3) return false;
if (phi->InputAt(2) != merge) return false;
if (merge->UseCount() != 2) return false;
Node::Inputs phi_inputs = phi->inputs();
Node* first_value = phi_inputs[0];
Node* second_value = phi_inputs[1];
if (first_value->opcode() != IrOpcode::kInt32Constant ||
second_value->opcode() != IrOpcode::kInt32Constant) {
return false;
}
Node::Inputs merge_inputs = merge->inputs();
Node* predecessor0 = merge_inputs[0];
Node* predecessor1 = merge_inputs[1];
DCHECK_EQ(branch->op()->ControlOutputCount(), 2);
Node** projections = zone()->AllocateArray<Node*>(2);
NodeProperties::CollectControlProjections(branch, projections, 2);
Node* branch_true = projections[0];
Node* branch_false = projections[1];
DCHECK_EQ(branch_true->opcode(), IrOpcode::kIfTrue);
DCHECK_EQ(branch_false->opcode(), IrOpcode::kIfFalse);
// The input values of phi should be true(1) and false(0).
Int32Matcher mfirst_value(first_value);
Int32Matcher msecond_value(second_value);
Node* predecessor_true = nullptr;
Node* predecessor_false = nullptr;
if (mfirst_value.Is(1) && msecond_value.Is(0)) {
predecessor_true = predecessor0;
predecessor_false = predecessor1;
} else if (mfirst_value.Is(0) && msecond_value.Is(1)) {
predecessor_true = predecessor1;
predecessor_false = predecessor0;
} else {
return false;
}
// Merge the branches successors into its predecessors.
for (Edge edge : branch_true->use_edges()) {
edge.UpdateTo(predecessor_true);
}
for (Edge edge : branch_false->use_edges()) {
edge.UpdateTo(predecessor_false);
}
branch_true->Kill();
branch_false->Kill();
branch->Kill();
phi->Kill();
merge->Kill();
return true;
}
Reduction BranchElimination::ReduceBranch(Node* node) {
Node* condition = node->InputAt(0);
Node* control_input = NodeProperties::GetControlInput(node, 0);
if (!IsReduced(control_input)) return NoChange();
ControlPathConditions from_input = GetState(control_input);
// If we know the condition we can discard the branch.
BranchCondition branch_condition = from_input.LookupState(condition);
if (branch_condition.IsSet()) {
bool condition_value = branch_condition.is_true;
for (Node* const use : node->uses()) {
switch (use->opcode()) {
case IrOpcode::kIfTrue:
Replace(use, condition_value ? control_input : dead());
break;
case IrOpcode::kIfFalse:
Replace(use, condition_value ? dead() : control_input);
break;
default:
UNREACHABLE();
}
}
return Replace(dead());
}
SimplifyBranchCondition(node);
// Try to reduce the pattern that branch condition comes from phi node.
if (condition->opcode() == IrOpcode::kPhi &&
control_input->opcode() == IrOpcode::kMerge) {
if (TryEliminateBranchWithPhiCondition(node, condition, control_input)) {
return Replace(dead());
}
}
// Trigger revisits of the IfTrue/IfFalse projections, since they depend on
// the branch condition.
for (Node* const use : node->uses()) {
Revisit(use);
}
return TakeStatesFromFirstControl(node);
}
Reduction BranchElimination::ReduceTrapConditional(Node* node) {
DCHECK(node->opcode() == IrOpcode::kTrapIf ||
node->opcode() == IrOpcode::kTrapUnless);
bool trapping_condition = node->opcode() == IrOpcode::kTrapIf;
Node* condition = node->InputAt(0);
Node* control_input = NodeProperties::GetControlInput(node, 0);
// If we do not know anything about the predecessor, do not propagate just
// yet because we will have to recompute anyway once we compute the
// predecessor.
if (!IsReduced(control_input)) return NoChange();
ControlPathConditions from_input = GetState(control_input);
BranchCondition branch_condition = from_input.LookupState(condition);
if (branch_condition.IsSet()) {
bool condition_value = branch_condition.is_true;
if (condition_value == trapping_condition) {
// This will always trap. Mark its outputs as dead and connect it to
// graph()->end().
ReplaceWithValue(node, dead(), dead(), dead());
Node* control = graph()->NewNode(common()->Throw(), node, node);
MergeControlToEnd(graph(), common(), control);
return Changed(node);
} else {
// This will not trap, remove it by relaxing effect/control.
RelaxEffectsAndControls(node);
Node* control = NodeProperties::GetControlInput(node);
node->Kill();
return Replace(control); // Irrelevant argument
}
}
return UpdateStatesHelper(node, from_input, condition, node,
!trapping_condition, false);
}
Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
node->opcode() == IrOpcode::kDeoptimizeUnless);
bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
Node* condition = NodeProperties::GetValueInput(node, 0);
Node* frame_state = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// If we do not know anything about the predecessor, do not propagate just
// yet because we will have to recompute anyway once we compute the
// predecessor.
if (!IsReduced(control)) {
return NoChange();
}
ControlPathConditions conditions = GetState(control);
BranchCondition branch_condition = conditions.LookupState(condition);
if (branch_condition.IsSet()) {
// If we know the condition we can discard the branch.
bool condition_value = branch_condition.is_true;
if (condition_is_true == condition_value) {
// We don't update the conditions here, because we're replacing {node}
// with the {control} node that already contains the right information.
ReplaceWithValue(node, dead(), effect, control);
} else {
control = graph()->NewNode(common()->Deoptimize(p.reason(), p.feedback()),
frame_state, effect, control);
MergeControlToEnd(graph(), common(), control);
}
return Replace(dead());
}
return UpdateStatesHelper(node, conditions, condition, node,
condition_is_true, false);
}
Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
// Add the condition to the list arriving from the input branch.
Node* branch = NodeProperties::GetControlInput(node, 0);
ControlPathConditions from_branch = GetState(branch);
// If we do not know anything about the predecessor, do not propagate just
// yet because we will have to recompute anyway once we compute the
// predecessor.
if (!IsReduced(branch)) {
return NoChange();
}
Node* condition = branch->InputAt(0);
return UpdateStatesHelper(node, from_branch, condition, branch,
is_true_branch, true);
}
Reduction BranchElimination::ReduceLoop(Node* node) {
// Here we rely on having only reducible loops:
// The loop entry edge always dominates the header, so we can just use
// the information from the loop entry edge.
return TakeStatesFromFirstControl(node);
}
Reduction BranchElimination::ReduceMerge(Node* node) {
// Shortcut for the case when we do not know anything about some
// input.
Node::Inputs inputs = node->inputs();
for (Node* input : inputs) {
if (!IsReduced(input)) {
return NoChange();
}
}
auto input_it = inputs.begin();
DCHECK_GT(inputs.count(), 0);
ControlPathConditions conditions = GetState(*input_it);
++input_it;
// Merge the first input's conditions with the conditions from the other
// inputs.
auto input_end = inputs.end();
for (; input_it != input_end; ++input_it) {
// Change the current condition block list to a longest common tail of this
// condition list and the other list. (The common tail should correspond to
// the list from the common dominator.)
conditions.ResetToCommonAncestor(GetState(*input_it));
}
return UpdateStates(node, conditions);
}
Reduction BranchElimination::ReduceStart(Node* node) {
return UpdateStates(node, ControlPathConditions(zone()));
}
Reduction BranchElimination::ReduceOtherControl(Node* node) {
DCHECK_EQ(1, node->op()->ControlInputCount());
return TakeStatesFromFirstControl(node);
}
Graph* BranchElimination::graph() const { return jsgraph()->graph(); }
Isolate* BranchElimination::isolate() const { return jsgraph()->isolate(); }
CommonOperatorBuilder* BranchElimination::common() const {
return jsgraph()->common();
}
// Workaround a gcc bug causing link errors.
// Related issue: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105848
template bool DefaultConstruct<bool>(Zone* zone);
} // namespace compiler
} // namespace internal
} // namespace v8