[turbofan] Factor out and templatize path conditions

We factor out the path-state part of branch elimination, to reuse it for
wasm path-based type optimizations. The node state becomes a template
parameter for the {ControlPathState} and
{AdvancedReducerWithControlPathState} classes.

Change-Id: I5e9811ced0b71140ec73ba26fae358ac7d56c982
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3714238
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81270}
This commit is contained in:
Manos Koukoutos 2022-06-21 09:22:47 +00:00 committed by V8 LUCI CQ
parent 6f61142428
commit 8df4e9be52
5 changed files with 294 additions and 246 deletions

View File

@ -2686,6 +2686,7 @@ filegroup(
"src/compiler/control-equivalence.h",
"src/compiler/control-flow-optimizer.cc",
"src/compiler/control-flow-optimizer.h",
"src/compiler/control-path-state.h",
"src/compiler/csa-load-elimination.cc",
"src/compiler/csa-load-elimination.h",
"src/compiler/dead-code-elimination.cc",

View File

@ -2832,6 +2832,7 @@ v8_header_set("v8_internal_headers") {
"src/compiler/constant-folding-reducer.h",
"src/compiler/control-equivalence.h",
"src/compiler/control-flow-optimizer.h",
"src/compiler/control-path-state.h",
"src/compiler/csa-load-elimination.h",
"src/compiler/dead-code-elimination.h",
"src/compiler/decompression-optimizer.h",

View File

@ -16,11 +16,8 @@ namespace compiler {
BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
Zone* zone, Phase phase)
: AdvancedReducer(editor),
: AdvancedReducerWithControlPathState(editor, zone, js_graph->graph()),
jsgraph_(js_graph),
node_conditions_(js_graph->graph()->NodeCount(), zone),
reduced_(js_graph->graph()->NodeCount(), zone),
zone_(zone),
dead_(js_graph->Dead()),
phase_(phase) {}
@ -51,10 +48,14 @@ Reduction BranchElimination::Reduce(Node* node) {
default:
if (node->op()->ControlOutputCount() > 0) {
return ReduceOtherControl(node);
} else {
return NoChange();
}
break;
}
return NoChange();
}
namespace {
using ControlPathConditions = ControlPathState<BranchCondition>;
}
void BranchElimination::SimplifyBranchCondition(Node* branch) {
@ -90,9 +91,7 @@ void BranchElimination::SimplifyBranchCondition(Node* branch) {
Node* merge = NodeProperties::GetControlInput(branch);
if (merge->opcode() != IrOpcode::kMerge) return;
Node* branch_condition = branch->InputAt(0);
Node* previous_branch;
bool condition_value;
Node* condition = branch->InputAt(0);
Graph* graph = jsgraph()->graph();
base::SmallVector<Node*, 2> phi_inputs;
@ -100,11 +99,11 @@ void BranchElimination::SimplifyBranchCondition(Node* branch) {
int input_count = inputs.count();
for (int i = 0; i != input_count; ++i) {
Node* input = inputs[i];
ControlPathConditions from_input = node_conditions_.Get(input);
if (!from_input.LookupCondition(branch_condition, &previous_branch,
&condition_value)) {
return;
}
ControlPathConditions from_input = GetState(input);
BranchCondition branch_condition = from_input.LookupState(condition);
if (!branch_condition.IsSet()) return;
bool condition_value = branch_condition.is_true;
if (phase_ == kEARLY) {
phi_inputs.emplace_back(condition_value ? jsgraph()->TrueConstant()
@ -130,12 +129,12 @@ void BranchElimination::SimplifyBranchCondition(Node* branch) {
Reduction BranchElimination::ReduceBranch(Node* node) {
Node* condition = node->InputAt(0);
Node* control_input = NodeProperties::GetControlInput(node, 0);
if (!reduced_.Get(control_input)) return NoChange();
ControlPathConditions from_input = node_conditions_.Get(control_input);
Node* branch;
bool condition_value;
if (!IsReduced(control_input)) return NoChange();
ControlPathConditions from_input = GetState(control_input);
// If we know the condition we can discard the branch.
if (from_input.LookupCondition(condition, &branch, &condition_value)) {
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:
@ -156,7 +155,7 @@ Reduction BranchElimination::ReduceBranch(Node* node) {
for (Node* const use : node->uses()) {
Revisit(use);
}
return TakeConditionsFromFirstControl(node);
return TakeStatesFromFirstControl(node);
}
Reduction BranchElimination::ReduceTrapConditional(Node* node) {
@ -168,14 +167,13 @@ Reduction BranchElimination::ReduceTrapConditional(Node* 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 (!reduced_.Get(control_input)) return NoChange();
if (!IsReduced(control_input)) return NoChange();
ControlPathConditions from_input = node_conditions_.Get(control_input);
Node* previous_branch;
bool condition_value;
ControlPathConditions from_input = GetState(control_input);
if (from_input.LookupCondition(condition, &previous_branch,
&condition_value)) {
BranchCondition branch_condition = from_input.LookupState(condition);
if (branch_condition.IsSet()) {
bool condition_value = branch_condition.is_true;
if (condition_value == trapping_condition) {
// Special case: Trap directly inside a branch without sibling nodes.
// Replace the branch with the trap.
@ -227,8 +225,8 @@ Reduction BranchElimination::ReduceTrapConditional(Node* node) {
return Replace(control_input);
}
}
return UpdateConditions(node, from_input, condition, node,
!trapping_condition, false);
return UpdateStatesHelper(node, from_input, condition, node,
!trapping_condition, false);
}
Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
@ -243,15 +241,15 @@ Reduction BranchElimination::ReduceDeoptimizeConditional(Node* 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 (!reduced_.Get(control)) {
if (!IsReduced(control)) {
return NoChange();
}
ControlPathConditions conditions = node_conditions_.Get(control);
bool condition_value;
Node* branch;
// If we know the condition we can discard the branch.
if (conditions.LookupCondition(condition, &branch, &condition_value)) {
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.
@ -265,30 +263,30 @@ Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
}
return Replace(dead());
}
return UpdateConditions(node, conditions, condition, node, condition_is_true,
false);
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 = node_conditions_.Get(branch);
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 (!reduced_.Get(branch)) {
if (!IsReduced(branch)) {
return NoChange();
}
Node* condition = branch->InputAt(0);
return UpdateConditions(node, from_branch, condition, branch, is_true_branch,
true);
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 TakeConditionsFromFirstControl(node);
return TakeStatesFromFirstControl(node);
}
Reduction BranchElimination::ReduceMerge(Node* node) {
@ -296,7 +294,7 @@ Reduction BranchElimination::ReduceMerge(Node* node) {
// input.
Node::Inputs inputs = node->inputs();
for (Node* input : inputs) {
if (!reduced_.Get(input)) {
if (!IsReduced(input)) {
return NoChange();
}
}
@ -305,7 +303,7 @@ Reduction BranchElimination::ReduceMerge(Node* node) {
DCHECK_GT(inputs.count(), 0);
ControlPathConditions conditions = node_conditions_.Get(*input_it);
ControlPathConditions conditions = GetState(*input_it);
++input_it;
// Merge the first input's conditions with the conditions from the other
// inputs.
@ -314,139 +312,20 @@ Reduction BranchElimination::ReduceMerge(Node* node) {
// 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(node_conditions_.Get(*input_it));
conditions.ResetToCommonAncestor(GetState(*input_it));
}
return UpdateConditions(node, conditions);
return UpdateStates(node, conditions);
}
Reduction BranchElimination::ReduceStart(Node* node) {
return UpdateConditions(node, ControlPathConditions(zone_));
return UpdateStates(node, ControlPathConditions(zone()));
}
Reduction BranchElimination::ReduceOtherControl(Node* node) {
DCHECK_EQ(1, node->op()->ControlInputCount());
return TakeConditionsFromFirstControl(node);
return TakeStatesFromFirstControl(node);
}
Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) {
// We just propagate the information from the control input (ideally,
// we would only revisit control uses if there is change).
Node* input = NodeProperties::GetControlInput(node, 0);
if (!reduced_.Get(input)) return NoChange();
return UpdateConditions(node, node_conditions_.Get(input));
}
Reduction BranchElimination::UpdateConditions(
Node* node, ControlPathConditions conditions) {
// Only signal that the node has Changed if the condition information has
// changed.
bool reduced_changed = reduced_.Set(node, true);
bool node_conditions_changed = node_conditions_.Set(node, conditions);
if (reduced_changed || node_conditions_changed) {
return Changed(node);
}
return NoChange();
}
Reduction BranchElimination::UpdateConditions(
Node* node, ControlPathConditions prev_conditions, Node* current_condition,
Node* current_branch, bool is_true_branch, bool in_new_block) {
// The control path for the node is the path obtained by appending the
// current_condition to the prev_conditions. Use the original control path as
// a hint to avoid allocations.
if (in_new_block || prev_conditions.blocks_.Size() == 0) {
prev_conditions.AddConditionInNewBlock(zone_, current_condition,
current_branch, is_true_branch);
} else {
ControlPathConditions original = node_conditions_.Get(node);
prev_conditions.AddCondition(zone_, current_condition, current_branch,
is_true_branch, original);
}
return UpdateConditions(node, prev_conditions);
}
void BranchElimination::ControlPathConditions::AddCondition(
Zone* zone, Node* condition, Node* branch, bool is_true,
ControlPathConditions hint) {
if (!LookupCondition(condition)) {
BranchCondition branch_condition(condition, branch, is_true);
FunctionalList<BranchCondition> prev_front = blocks_.Front();
if (hint.blocks_.Size() > 0) {
prev_front.PushFront(branch_condition, zone, hint.blocks_.Front());
} else {
prev_front.PushFront(branch_condition, zone);
}
blocks_.DropFront();
blocks_.PushFront(prev_front, zone);
conditions_.Set(condition, branch_condition);
SLOW_DCHECK(BlocksAndConditionsInvariant());
}
}
void BranchElimination::ControlPathConditions::AddConditionInNewBlock(
Zone* zone, Node* condition, Node* branch, bool is_true) {
FunctionalList<BranchCondition> new_block;
if (!LookupCondition(condition)) {
BranchCondition branch_condition(condition, branch, is_true);
new_block.PushFront(branch_condition, zone);
conditions_.Set(condition, branch_condition);
}
blocks_.PushFront(new_block, zone);
SLOW_DCHECK(BlocksAndConditionsInvariant());
}
bool BranchElimination::ControlPathConditions::LookupCondition(
Node* condition) const {
return conditions_.Get(condition).IsSet();
}
bool BranchElimination::ControlPathConditions::LookupCondition(
Node* condition, Node** branch, bool* is_true) const {
const BranchCondition& element = conditions_.Get(condition);
if (element.IsSet()) {
*is_true = element.is_true;
*branch = element.branch;
return true;
}
return false;
}
void BranchElimination::ControlPathConditions::ResetToCommonAncestor(
ControlPathConditions other) {
while (other.blocks_.Size() > blocks_.Size()) other.blocks_.DropFront();
while (blocks_.Size() > other.blocks_.Size()) {
for (BranchCondition branch_condition : blocks_.Front()) {
conditions_.Set(branch_condition.condition, {});
}
blocks_.DropFront();
}
while (blocks_ != other.blocks_) {
for (BranchCondition branch_condition : blocks_.Front()) {
conditions_.Set(branch_condition.condition, {});
}
blocks_.DropFront();
other.blocks_.DropFront();
}
SLOW_DCHECK(BlocksAndConditionsInvariant());
}
#if DEBUG
bool BranchElimination::ControlPathConditions::BlocksAndConditionsInvariant() {
PersistentMap<Node*, BranchCondition> conditions_copy(conditions_);
for (auto block : blocks_) {
for (BranchCondition condition : block) {
// Every element of blocks_ has to be in conditions_.
if (conditions_copy.Get(condition.condition) != condition) return false;
conditions_copy.Set(condition.condition, {});
}
}
// Every element of {conditions_} has to be in {blocks_}. We removed all
// elements of blocks_ from condition_copy, so if it is not empty, the
// invariant fails.
return conditions_copy.begin() == conditions_copy.end();
}
#endif
Graph* BranchElimination::graph() const { return jsgraph()->graph(); }
Isolate* BranchElimination::isolate() const { return jsgraph()->isolate(); }

View File

@ -7,10 +7,9 @@
#include "src/base/compiler-specific.h"
#include "src/common/globals.h"
#include "src/compiler/functional-list.h"
#include "src/compiler/control-path-state.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/node-aux-data.h"
#include "src/compiler/persistent-map.h"
namespace v8 {
namespace internal {
@ -21,8 +20,30 @@ class CommonOperatorBuilder;
class JSGraph;
class SourcePositionTable;
// Represents a condition along with its value in the current control path.
// Also stores the node that branched on this condition.
struct BranchCondition {
BranchCondition() : node(nullptr), branch(nullptr), is_true(false) {}
BranchCondition(Node* condition, Node* branch, bool is_true)
: node(condition), branch(branch), is_true(is_true) {}
Node* node;
Node* branch;
bool is_true;
bool operator==(const BranchCondition& other) const {
return node == other.node && branch == other.branch &&
is_true == other.is_true;
}
bool operator!=(const BranchCondition& other) const {
return !(*this == other);
}
bool IsSet() { return node != nullptr; }
};
class V8_EXPORT_PRIVATE BranchElimination final
: public NON_EXPORTED_BASE(AdvancedReducer) {
: public NON_EXPORTED_BASE(
AdvancedReducerWithControlPathState<BranchCondition>) {
public:
enum Phase {
kEARLY,
@ -37,69 +58,6 @@ class V8_EXPORT_PRIVATE BranchElimination final
Reduction Reduce(Node* node) final;
private:
// Represents a condition along with its value in the current control path.
// Also stores the node that branched on this condition.
struct BranchCondition {
BranchCondition() : condition(nullptr), branch(nullptr), is_true(false) {}
BranchCondition(Node* condition, Node* branch, bool is_true)
: condition(condition), branch(branch), is_true(is_true) {}
Node* condition;
Node* branch;
bool is_true;
bool operator==(BranchCondition other) const {
return condition == other.condition && branch == other.branch &&
is_true == other.is_true;
}
bool operator!=(BranchCondition other) const { return !(*this == other); }
bool IsSet() const { return branch != nullptr; }
};
// Class for tracking information about branch conditions. It is represented
// as a linked list of condition blocks, each of which corresponds to a block
// of code bewteen an IfTrue/IfFalse and a Merge. Each block is in turn
// represented as a linked list of {BranchCondition}s.
class ControlPathConditions {
public:
explicit ControlPathConditions(Zone* zone) : conditions_(zone) {}
// Checks if {condition} is present in this {ControlPathConditions}.
bool LookupCondition(Node* condition) const;
// Checks if {condition} is present in this {ControlPathConditions} and
// copies its {branch} and {is_true} fields.
bool LookupCondition(Node* condition, Node** branch, bool* is_true) const;
// Adds a condition in the current code block, or a new block if the block
// list is empty.
void AddCondition(Zone* zone, Node* condition, Node* branch, bool is_true,
ControlPathConditions hint);
// Adds a condition in a new block.
void AddConditionInNewBlock(Zone* zone, Node* condition, Node* branch,
bool is_true);
// Reset this {ControlPathConditions} to the longest prefix that is common
// with {other}.
void ResetToCommonAncestor(ControlPathConditions other);
bool operator==(const ControlPathConditions& other) const {
return blocks_ == other.blocks_;
}
bool operator!=(const ControlPathConditions& other) const {
return blocks_ != other.blocks_;
}
friend class BranchElimination;
private:
FunctionalList<FunctionalList<BranchCondition>> blocks_;
// This is an auxilliary data structure that provides fast lookups in the
// set of conditions. It should hold at any point that the contents of
// {blocks_} and {conditions_} is the same, which is implemented in
// {BlocksAndConditionsInvariant}.
PersistentMap<Node*, BranchCondition> conditions_;
#if DEBUG
bool BlocksAndConditionsInvariant();
#endif
};
Reduction ReduceBranch(Node* node);
Reduction ReduceDeoptimizeConditional(Node* node);
Reduction ReduceIf(Node* node, bool is_true_branch);
@ -109,12 +67,15 @@ class V8_EXPORT_PRIVATE BranchElimination final
Reduction ReduceStart(Node* node);
Reduction ReduceOtherControl(Node* node);
void SimplifyBranchCondition(Node* branch);
Reduction TakeConditionsFromFirstControl(Node* node);
Reduction UpdateConditions(Node* node, ControlPathConditions conditions);
Reduction UpdateConditions(Node* node, ControlPathConditions prev_conditions,
Node* current_condition, Node* current_branch,
bool is_true_branch, bool in_new_block);
Reduction UpdateStatesHelper(
Node* node, ControlPathState<BranchCondition> prev_conditions,
Node* current_condition, Node* current_branch, bool is_true_branch,
bool in_new_block) {
return UpdateStates(
node, prev_conditions, current_condition,
BranchCondition(current_condition, current_branch, is_true_branch),
in_new_block);
}
Node* dead() const { return dead_; }
Graph* graph() const;
@ -124,14 +85,6 @@ class V8_EXPORT_PRIVATE BranchElimination final
JSGraph* const jsgraph_;
// Maps each control node to the condition information known about the node.
// If the information is nullptr, then we have not calculated the information
// yet.
NodeAuxData<ControlPathConditions, ZoneConstruct<ControlPathConditions>>
node_conditions_;
NodeAuxData<bool> reduced_;
Zone* zone_;
Node* dead_;
Phase phase_;
};

View File

@ -0,0 +1,214 @@
// Copyright 2022 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.
#ifndef V8_COMPILER_CONTROL_PATH_STATE_H_
#define V8_COMPILER_CONTROL_PATH_STATE_H_
#include "src/compiler/functional-list.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/graph.h"
#include "src/compiler/node-aux-data.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/persistent-map.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
// Class for tracking information about path state. It is represented
// as a linked list of {NodeState} blocks, each of which corresponds to a block
// of code bewteen an IfTrue/IfFalse and a Merge. Each block is in turn
// represented as a linked list of {NodeState}s.
template <typename NodeState>
class ControlPathState {
public:
static_assert(
std::is_member_function_pointer<decltype(&NodeState::IsSet)>::value,
"{NodeState} needs an {IsSet} method");
static_assert(
std::is_member_object_pointer<decltype(&NodeState::node)>::value,
"{NodeState} needs to hold a pointer to the {Node*} owner of the state");
explicit ControlPathState(Zone* zone) : states_(zone) {}
// Returns the {NodeState} assigned to node, or the default value
// {NodeState()} if it is not assigned.
NodeState LookupState(Node* node) const { return states_.Get(node); }
// Adds a state in the current code block, or a new block if the block list is
// empty.
void AddState(Zone* zone, Node* node, NodeState state, ControlPathState hint);
// Adds a state in a new block.
void AddStateInNewBlock(Zone* zone, Node* node, NodeState state);
// Reset this {ControlPathState} to its longest prefix that is common with
// {other}.
void ResetToCommonAncestor(ControlPathState other);
bool IsEmpty() { return blocks_.Size() == 0; }
bool operator==(const ControlPathState& other) const {
return blocks_ == other.blocks_;
}
bool operator!=(const ControlPathState& other) const {
return blocks_ != other.blocks_;
}
private:
#if DEBUG
bool BlocksAndStatesInvariant() {
PersistentMap<Node*, NodeState> states_copy(states_);
for (auto block : blocks_) {
for (NodeState state : block) {
// Every element of blocks_ has to be in states_.
if (states_copy.Get(state.node) != state) return false;
states_copy.Set(state.node, {});
}
}
// Every element of {states_} has to be in {blocks_}. We removed all
// elements of blocks_ from states_copy, so if it is not empty, the
// invariant fails.
return states_copy.begin() == states_copy.end();
}
#endif
FunctionalList<FunctionalList<NodeState>> blocks_;
// This is an auxilliary data structure that provides fast lookups in the
// set of states. It should hold at any point that the contents of {blocks_}
// and {states_} is the same, which is implemented in
// {BlocksAndStatesInvariant}.
PersistentMap<Node*, NodeState> states_;
};
template <typename NodeState>
class AdvancedReducerWithControlPathState : public AdvancedReducer {
protected:
AdvancedReducerWithControlPathState(Editor* editor, Zone* zone, Graph* graph)
: AdvancedReducer(editor),
zone_(zone),
node_states_(graph->NodeCount(), zone),
reduced_(graph->NodeCount(), zone) {}
Reduction TakeStatesFromFirstControl(Node* node);
// Update the state of {state_owner} to {new_state}.
Reduction UpdateStates(Node* state_owner,
ControlPathState<NodeState> new_state);
// Update the state of {state_owner} to {prev_states}, plus {additional_state}
// assigned to {additional_node}. Force the new state in a new block if
// {in_new_block}.
Reduction UpdateStates(Node* state_owner,
ControlPathState<NodeState> prev_states,
Node* additional_node, NodeState additional_state,
bool in_new_block);
Zone* zone() { return zone_; }
ControlPathState<NodeState> GetState(Node* node) {
return node_states_.Get(node);
}
bool IsReduced(Node* node) { return reduced_.Get(node); }
private:
Zone* zone_;
// Maps each control node to the node's current state.
// If the information is nullptr, then we have not calculated the information
// yet.
NodeAuxData<ControlPathState<NodeState>,
ZoneConstruct<ControlPathState<NodeState>>>
node_states_;
NodeAuxData<bool> reduced_;
};
template <typename NodeState>
void ControlPathState<NodeState>::AddState(Zone* zone, Node* node,
NodeState state,
ControlPathState<NodeState> hint) {
if (LookupState(node).IsSet()) return;
FunctionalList<NodeState> prev_front = blocks_.Front();
if (hint.blocks_.Size() > 0) {
prev_front.PushFront(state, zone, hint.blocks_.Front());
} else {
prev_front.PushFront(state, zone);
}
blocks_.DropFront();
blocks_.PushFront(prev_front, zone);
states_.Set(node, state);
SLOW_DCHECK(BlocksAndStatesInvariant());
}
template <typename NodeState>
void ControlPathState<NodeState>::AddStateInNewBlock(Zone* zone, Node* node,
NodeState state) {
FunctionalList<NodeState> new_block;
if (!LookupState(node).IsSet()) {
new_block.PushFront(state, zone);
states_.Set(node, state);
}
blocks_.PushFront(new_block, zone);
SLOW_DCHECK(BlocksAndStatesInvariant());
}
template <typename NodeState>
void ControlPathState<NodeState>::ResetToCommonAncestor(
ControlPathState<NodeState> other) {
while (other.blocks_.Size() > blocks_.Size()) other.blocks_.DropFront();
while (blocks_.Size() > other.blocks_.Size()) {
for (NodeState state : blocks_.Front()) {
states_.Set(state.node, {});
}
blocks_.DropFront();
}
while (blocks_ != other.blocks_) {
for (NodeState state : blocks_.Front()) {
states_.Set(state.node, {});
}
blocks_.DropFront();
other.blocks_.DropFront();
}
SLOW_DCHECK(BlocksAndStatesInvariant());
}
template <typename NodeState>
Reduction
AdvancedReducerWithControlPathState<NodeState>::TakeStatesFromFirstControl(
Node* node) {
// We just propagate the information from the control input (ideally,
// we would only revisit control uses if there is change).
Node* input = NodeProperties::GetControlInput(node, 0);
if (!reduced_.Get(input)) return NoChange();
return UpdateStates(node, node_states_.Get(input));
}
template <typename NodeState>
Reduction AdvancedReducerWithControlPathState<NodeState>::UpdateStates(
Node* state_owner, ControlPathState<NodeState> new_state) {
// Only signal that the node has {Changed} if its state has changed.
bool reduced_changed = reduced_.Set(state_owner, true);
bool node_states_changed = node_states_.Set(state_owner, new_state);
if (reduced_changed || node_states_changed) {
return Changed(state_owner);
}
return NoChange();
}
template <typename NodeState>
Reduction AdvancedReducerWithControlPathState<NodeState>::UpdateStates(
Node* state_owner, ControlPathState<NodeState> prev_states,
Node* additional_node, NodeState additional_state, bool in_new_block) {
if (in_new_block || prev_states.IsEmpty()) {
prev_states.AddStateInNewBlock(zone_, additional_node, additional_state);
} else {
ControlPathState<NodeState> original = node_states_.Get(state_owner);
prev_states.AddState(zone_, additional_node, additional_state, original);
}
return UpdateStates(state_owner, prev_states);
}
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_CONTROL_PATH_STATE_H_