[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:
parent
6f61142428
commit
8df4e9be52
@ -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",
|
||||
|
1
BUILD.gn
1
BUILD.gn
@ -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",
|
||||
|
@ -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(); }
|
||||
|
@ -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_;
|
||||
};
|
||||
|
214
src/compiler/control-path-state.h
Normal file
214
src/compiler/control-path-state.h
Normal 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_
|
Loading…
Reference in New Issue
Block a user