Commit 8df4e9be authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[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/+/3714238Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81270}
parent 6f611424
...@@ -2686,6 +2686,7 @@ filegroup( ...@@ -2686,6 +2686,7 @@ filegroup(
"src/compiler/control-equivalence.h", "src/compiler/control-equivalence.h",
"src/compiler/control-flow-optimizer.cc", "src/compiler/control-flow-optimizer.cc",
"src/compiler/control-flow-optimizer.h", "src/compiler/control-flow-optimizer.h",
"src/compiler/control-path-state.h",
"src/compiler/csa-load-elimination.cc", "src/compiler/csa-load-elimination.cc",
"src/compiler/csa-load-elimination.h", "src/compiler/csa-load-elimination.h",
"src/compiler/dead-code-elimination.cc", "src/compiler/dead-code-elimination.cc",
......
...@@ -2832,6 +2832,7 @@ v8_header_set("v8_internal_headers") { ...@@ -2832,6 +2832,7 @@ v8_header_set("v8_internal_headers") {
"src/compiler/constant-folding-reducer.h", "src/compiler/constant-folding-reducer.h",
"src/compiler/control-equivalence.h", "src/compiler/control-equivalence.h",
"src/compiler/control-flow-optimizer.h", "src/compiler/control-flow-optimizer.h",
"src/compiler/control-path-state.h",
"src/compiler/csa-load-elimination.h", "src/compiler/csa-load-elimination.h",
"src/compiler/dead-code-elimination.h", "src/compiler/dead-code-elimination.h",
"src/compiler/decompression-optimizer.h", "src/compiler/decompression-optimizer.h",
......
This diff is collapsed.
...@@ -7,10 +7,9 @@ ...@@ -7,10 +7,9 @@
#include "src/base/compiler-specific.h" #include "src/base/compiler-specific.h"
#include "src/common/globals.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/graph-reducer.h"
#include "src/compiler/node-aux-data.h" #include "src/compiler/node-aux-data.h"
#include "src/compiler/persistent-map.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -21,8 +20,30 @@ class CommonOperatorBuilder; ...@@ -21,8 +20,30 @@ class CommonOperatorBuilder;
class JSGraph; class JSGraph;
class SourcePositionTable; 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 class V8_EXPORT_PRIVATE BranchElimination final
: public NON_EXPORTED_BASE(AdvancedReducer) { : public NON_EXPORTED_BASE(
AdvancedReducerWithControlPathState<BranchCondition>) {
public: public:
enum Phase { enum Phase {
kEARLY, kEARLY,
...@@ -37,69 +58,6 @@ class V8_EXPORT_PRIVATE BranchElimination final ...@@ -37,69 +58,6 @@ class V8_EXPORT_PRIVATE BranchElimination final
Reduction Reduce(Node* node) final; Reduction Reduce(Node* node) final;
private: 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 ReduceBranch(Node* node);
Reduction ReduceDeoptimizeConditional(Node* node); Reduction ReduceDeoptimizeConditional(Node* node);
Reduction ReduceIf(Node* node, bool is_true_branch); Reduction ReduceIf(Node* node, bool is_true_branch);
...@@ -109,12 +67,15 @@ class V8_EXPORT_PRIVATE BranchElimination final ...@@ -109,12 +67,15 @@ class V8_EXPORT_PRIVATE BranchElimination final
Reduction ReduceStart(Node* node); Reduction ReduceStart(Node* node);
Reduction ReduceOtherControl(Node* node); Reduction ReduceOtherControl(Node* node);
void SimplifyBranchCondition(Node* branch); void SimplifyBranchCondition(Node* branch);
Reduction UpdateStatesHelper(
Reduction TakeConditionsFromFirstControl(Node* node); Node* node, ControlPathState<BranchCondition> prev_conditions,
Reduction UpdateConditions(Node* node, ControlPathConditions conditions); Node* current_condition, Node* current_branch, bool is_true_branch,
Reduction UpdateConditions(Node* node, ControlPathConditions prev_conditions, bool in_new_block) {
Node* current_condition, Node* current_branch, return UpdateStates(
bool is_true_branch, bool in_new_block); node, prev_conditions, current_condition,
BranchCondition(current_condition, current_branch, is_true_branch),
in_new_block);
}
Node* dead() const { return dead_; } Node* dead() const { return dead_; }
Graph* graph() const; Graph* graph() const;
...@@ -124,14 +85,6 @@ class V8_EXPORT_PRIVATE BranchElimination final ...@@ -124,14 +85,6 @@ class V8_EXPORT_PRIVATE BranchElimination final
JSGraph* const jsgraph_; 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_; Node* dead_;
Phase phase_; Phase phase_;
}; };
......
// 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_
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