| Index: src/compiler/effect-control-linearizer.cc
|
| diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc
|
| index af52b6d626b5c32c17aca8326080f438a8c7e077..f671207cefaf39045766195e0a54255edef28ad8 100644
|
| --- a/src/compiler/effect-control-linearizer.cc
|
| +++ b/src/compiler/effect-control-linearizer.cc
|
| @@ -8,6 +8,7 @@
|
| #include "src/compiler/access-builder.h"
|
| #include "src/compiler/js-graph.h"
|
| #include "src/compiler/linkage.h"
|
| +#include "src/compiler/node-matchers.h"
|
| #include "src/compiler/node-properties.h"
|
| #include "src/compiler/node.h"
|
| #include "src/compiler/schedule.h"
|
| @@ -35,11 +36,30 @@ MachineOperatorBuilder* EffectControlLinearizer::machine() const {
|
| namespace {
|
|
|
| struct BlockEffectControlData {
|
| - Node* current_effect = nullptr; // New effect.
|
| - Node* current_control = nullptr; // New control.
|
| + Node* current_effect = nullptr; // New effect.
|
| + Node* current_control = nullptr; // New control.
|
| Node* current_frame_state = nullptr; // New frame state.
|
| };
|
|
|
| +class BlockEffectControlMap {
|
| + public:
|
| + explicit BlockEffectControlMap(Zone* temp_zone) : map_(temp_zone) {}
|
| +
|
| + BlockEffectControlData& For(BasicBlock* from, BasicBlock* to) {
|
| + return map_[std::make_pair(from->rpo_number(), to->rpo_number())];
|
| + }
|
| +
|
| + const BlockEffectControlData& For(BasicBlock* from, BasicBlock* to) const {
|
| + return map_.at(std::make_pair(from->rpo_number(), to->rpo_number()));
|
| + }
|
| +
|
| + private:
|
| + typedef std::pair<int32_t, int32_t> Key;
|
| + typedef ZoneMap<Key, BlockEffectControlData> Map;
|
| +
|
| + Map map_;
|
| +};
|
| +
|
| // Effect phis that need to be updated after the first pass.
|
| struct PendingEffectPhi {
|
| Node* effect_phi;
|
| @@ -50,7 +70,7 @@ struct PendingEffectPhi {
|
| };
|
|
|
| void UpdateEffectPhi(Node* node, BasicBlock* block,
|
| - ZoneVector<BlockEffectControlData>* block_effects) {
|
| + BlockEffectControlMap* block_effects) {
|
| // Update all inputs to an effect phi with the effects from the given
|
| // block->effect map.
|
| DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
|
| @@ -58,16 +78,16 @@ void UpdateEffectPhi(Node* node, BasicBlock* block,
|
| for (int i = 0; i < node->op()->EffectInputCount(); i++) {
|
| Node* input = node->InputAt(i);
|
| BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i));
|
| - Node* input_effect =
|
| - (*block_effects)[predecessor->rpo_number()].current_effect;
|
| - if (input != input_effect) {
|
| - node->ReplaceInput(i, input_effect);
|
| + const BlockEffectControlData& block_effect =
|
| + block_effects->For(predecessor, block);
|
| + if (input != block_effect.current_effect) {
|
| + node->ReplaceInput(i, block_effect.current_effect);
|
| }
|
| }
|
| }
|
|
|
| void UpdateBlockControl(BasicBlock* block,
|
| - ZoneVector<BlockEffectControlData>* block_effects) {
|
| + BlockEffectControlMap* block_effects) {
|
| Node* control = block->NodeAt(0);
|
| DCHECK(NodeProperties::IsControl(control));
|
|
|
| @@ -75,14 +95,19 @@ void UpdateBlockControl(BasicBlock* block,
|
| if (control->opcode() == IrOpcode::kEnd) return;
|
|
|
| // Update all inputs to the given control node with the correct control.
|
| - DCHECK_EQ(control->op()->ControlInputCount(), block->PredecessorCount());
|
| + DCHECK(control->opcode() == IrOpcode::kMerge ||
|
| + control->op()->ControlInputCount() == block->PredecessorCount());
|
| + if (control->op()->ControlInputCount() != block->PredecessorCount()) {
|
| + return; // We already re-wired the control inputs of this node.
|
| + }
|
| for (int i = 0; i < control->op()->ControlInputCount(); i++) {
|
| Node* input = NodeProperties::GetControlInput(control, i);
|
| BasicBlock* predecessor = block->PredecessorAt(static_cast<size_t>(i));
|
| - Node* input_control =
|
| - (*block_effects)[predecessor->rpo_number()].current_control;
|
| - if (input != input_control) {
|
| - NodeProperties::ReplaceControlInput(control, input_control, i);
|
| + const BlockEffectControlData& block_effect =
|
| + block_effects->For(predecessor, block);
|
| + if (input != block_effect.current_control) {
|
| + NodeProperties::ReplaceControlInput(control, block_effect.current_control,
|
| + i);
|
| }
|
| }
|
| }
|
| @@ -114,13 +139,162 @@ void RemoveRegionNode(Node* node) {
|
| node->Kill();
|
| }
|
|
|
| +void TryCloneBranch(Node* node, BasicBlock* block, Graph* graph,
|
| + CommonOperatorBuilder* common,
|
| + BlockEffectControlMap* block_effects) {
|
| + DCHECK_EQ(IrOpcode::kBranch, node->opcode());
|
| +
|
| + // This optimization is a special case of (super)block cloning. It takes an
|
| + // input graph as shown below and clones the Branch node for every predecessor
|
| + // to the Merge, essentially removing the Merge completely. This avoids
|
| + // materializing the bit for the Phi and may offer potential for further
|
| + // branch folding optimizations (i.e. because one or more inputs to the Phi is
|
| + // a constant). Note that there may be more Phi nodes hanging off the Merge,
|
| + // but we can only a certain subset of them currently (actually only Phi and
|
| + // EffectPhi nodes whose uses have either the IfTrue or IfFalse as control
|
| + // input).
|
| +
|
| + // Control1 ... ControlN
|
| + // ^ ^
|
| + // | | Cond1 ... CondN
|
| + // +----+ +----+ ^ ^
|
| + // | | | |
|
| + // | | +----+ |
|
| + // Merge<--+ | +------------+
|
| + // ^ \|/
|
| + // | Phi
|
| + // | |
|
| + // Branch----+
|
| + // ^
|
| + // |
|
| + // +-----+-----+
|
| + // | |
|
| + // IfTrue IfFalse
|
| + // ^ ^
|
| + // | |
|
| +
|
| + // The resulting graph (modulo the Phi and EffectPhi nodes) looks like this:
|
| +
|
| + // Control1 Cond1 ... ControlN CondN
|
| + // ^ ^ ^ ^
|
| + // \ / \ /
|
| + // Branch ... Branch
|
| + // ^ ^
|
| + // | |
|
| + // +---+---+ +---+----+
|
| + // | | | |
|
| + // IfTrue IfFalse ... IfTrue IfFalse
|
| + // ^ ^ ^ ^
|
| + // | | | |
|
| + // +--+ +-------------+ |
|
| + // | | +--------------+ +--+
|
| + // | | | |
|
| + // Merge Merge
|
| + // ^ ^
|
| + // | |
|
| +
|
| + Node* branch = node;
|
| + Node* cond = NodeProperties::GetValueInput(branch, 0);
|
| + if (!cond->OwnedBy(branch) || cond->opcode() != IrOpcode::kPhi) return;
|
| + Node* merge = NodeProperties::GetControlInput(branch);
|
| + if (merge->opcode() != IrOpcode::kMerge ||
|
| + NodeProperties::GetControlInput(cond) != merge) {
|
| + return;
|
| + }
|
| + // Grab the IfTrue/IfFalse projections of the Branch.
|
| + BranchMatcher matcher(branch);
|
| + // Check/collect other Phi/EffectPhi nodes hanging off the Merge.
|
| + NodeVector phis(graph->zone());
|
| + for (Node* const use : merge->uses()) {
|
| + if (use == branch || use == cond) continue;
|
| + // We cannot currently deal with non-Phi/EffectPhi nodes hanging off the
|
| + // Merge. Ideally, we would just clone the nodes (and everything that
|
| + // depends on it to some distant join point), but that requires knowledge
|
| + // about dominance/post-dominance.
|
| + if (!NodeProperties::IsPhi(use)) return;
|
| + for (Edge edge : use->use_edges()) {
|
| + // Right now we can only handle Phi/EffectPhi nodes whose uses are
|
| + // directly control-dependend on either the IfTrue or the IfFalse
|
| + // successor, because we know exactly how to update those uses.
|
| + if (edge.from()->op()->ControlInputCount() != 1) return;
|
| + Node* control = NodeProperties::GetControlInput(edge.from());
|
| + if (NodeProperties::IsPhi(edge.from())) {
|
| + control = NodeProperties::GetControlInput(control, edge.index());
|
| + }
|
| + if (control != matcher.IfTrue() && control != matcher.IfFalse()) return;
|
| + }
|
| + phis.push_back(use);
|
| + }
|
| + BranchHint const hint = BranchHintOf(branch->op());
|
| + int const input_count = merge->op()->ControlInputCount();
|
| + DCHECK_LE(1, input_count);
|
| + Node** const inputs = graph->zone()->NewArray<Node*>(2 * input_count);
|
| + Node** const merge_true_inputs = &inputs[0];
|
| + Node** const merge_false_inputs = &inputs[input_count];
|
| + for (int index = 0; index < input_count; ++index) {
|
| + Node* cond1 = NodeProperties::GetValueInput(cond, index);
|
| + Node* control1 = NodeProperties::GetControlInput(merge, index);
|
| + Node* branch1 = graph->NewNode(common->Branch(hint), cond1, control1);
|
| + merge_true_inputs[index] = graph->NewNode(common->IfTrue(), branch1);
|
| + merge_false_inputs[index] = graph->NewNode(common->IfFalse(), branch1);
|
| + }
|
| + Node* const merge_true = matcher.IfTrue();
|
| + Node* const merge_false = matcher.IfFalse();
|
| + merge_true->TrimInputCount(0);
|
| + merge_false->TrimInputCount(0);
|
| + for (int i = 0; i < input_count; ++i) {
|
| + merge_true->AppendInput(graph->zone(), merge_true_inputs[i]);
|
| + merge_false->AppendInput(graph->zone(), merge_false_inputs[i]);
|
| + }
|
| + DCHECK_EQ(2, block->SuccessorCount());
|
| + NodeProperties::ChangeOp(matcher.IfTrue(), common->Merge(input_count));
|
| + NodeProperties::ChangeOp(matcher.IfFalse(), common->Merge(input_count));
|
| + int const true_index =
|
| + block->SuccessorAt(0)->NodeAt(0) == matcher.IfTrue() ? 0 : 1;
|
| + BlockEffectControlData* true_block_data =
|
| + &block_effects->For(block, block->SuccessorAt(true_index));
|
| + BlockEffectControlData* false_block_data =
|
| + &block_effects->For(block, block->SuccessorAt(true_index ^ 1));
|
| + for (Node* const phi : phis) {
|
| + for (int index = 0; index < input_count; ++index) {
|
| + inputs[index] = phi->InputAt(index);
|
| + }
|
| + inputs[input_count] = merge_true;
|
| + Node* phi_true = graph->NewNode(phi->op(), input_count + 1, inputs);
|
| + inputs[input_count] = merge_false;
|
| + Node* phi_false = graph->NewNode(phi->op(), input_count + 1, inputs);
|
| + if (phi->UseCount() == 0) {
|
| + DCHECK_EQ(phi->opcode(), IrOpcode::kEffectPhi);
|
| + DCHECK_EQ(input_count, block->SuccessorCount());
|
| + } else {
|
| + for (Edge edge : phi->use_edges()) {
|
| + Node* control = NodeProperties::GetControlInput(edge.from());
|
| + if (NodeProperties::IsPhi(edge.from())) {
|
| + control = NodeProperties::GetControlInput(control, edge.index());
|
| + }
|
| + DCHECK(control == matcher.IfTrue() || control == matcher.IfFalse());
|
| + edge.UpdateTo((control == matcher.IfTrue()) ? phi_true : phi_false);
|
| + }
|
| + }
|
| + true_block_data->current_effect = phi_true;
|
| + false_block_data->current_effect = phi_false;
|
| + phi->Kill();
|
| + }
|
| + // Fix up IfTrue and IfFalse and kill all dead nodes.
|
| + if (branch == block->control_input()) {
|
| + true_block_data->current_control = merge_true;
|
| + false_block_data->current_control = merge_false;
|
| + }
|
| + branch->Kill();
|
| + cond->Kill();
|
| + merge->Kill();
|
| +}
|
| } // namespace
|
|
|
| void EffectControlLinearizer::Run() {
|
| - ZoneVector<BlockEffectControlData> block_effects(temp_zone());
|
| + BlockEffectControlMap block_effects(temp_zone());
|
| ZoneVector<PendingEffectPhi> pending_effect_phis(temp_zone());
|
| ZoneVector<BasicBlock*> pending_block_controls(temp_zone());
|
| - block_effects.resize(schedule()->RpoBlockCount());
|
| NodeVector inputs_buffer(temp_zone());
|
|
|
| for (BasicBlock* block : *(schedule()->rpo_order())) {
|
| @@ -186,13 +360,13 @@ void EffectControlLinearizer::Run() {
|
| DCHECK_EQ(1u, block->size());
|
| effect = nullptr;
|
| } else {
|
| - // If all the predecessors have the same effect, we can use it
|
| - // as our current effect.
|
| - int rpo_number = block->PredecessorAt(0)->rpo_number();
|
| - effect = block_effects[rpo_number].current_effect;
|
| - for (size_t i = 1; i < block->PredecessorCount(); i++) {
|
| - int rpo_number = block->PredecessorAt(i)->rpo_number();
|
| - if (block_effects[rpo_number].current_effect != effect) {
|
| + // If all the predecessors have the same effect, we can use it as our
|
| + // current effect.
|
| + effect =
|
| + block_effects.For(block->PredecessorAt(0), block).current_effect;
|
| + for (size_t i = 1; i < block->PredecessorCount(); ++i) {
|
| + if (block_effects.For(block->PredecessorAt(i), block)
|
| + .current_effect != effect) {
|
| effect = nullptr;
|
| break;
|
| }
|
| @@ -232,11 +406,11 @@ void EffectControlLinearizer::Run() {
|
| if (block != schedule()->start()) {
|
| // If all the predecessors have the same effect, we can use it
|
| // as our current effect.
|
| - int rpo_number = block->PredecessorAt(0)->rpo_number();
|
| - frame_state = block_effects[rpo_number].current_frame_state;
|
| + frame_state =
|
| + block_effects.For(block->PredecessorAt(0), block).current_frame_state;
|
| for (size_t i = 1; i < block->PredecessorCount(); i++) {
|
| - int rpo_number = block->PredecessorAt(i)->rpo_number();
|
| - if (block_effects[rpo_number].current_frame_state != frame_state) {
|
| + if (block_effects.For(block->PredecessorAt(i), block)
|
| + .current_frame_state != frame_state) {
|
| frame_state = nullptr;
|
| break;
|
| }
|
| @@ -256,19 +430,31 @@ void EffectControlLinearizer::Run() {
|
|
|
| case BasicBlock::kCall:
|
| case BasicBlock::kTailCall:
|
| - case BasicBlock::kBranch:
|
| case BasicBlock::kSwitch:
|
| case BasicBlock::kReturn:
|
| case BasicBlock::kDeoptimize:
|
| case BasicBlock::kThrow:
|
| ProcessNode(block->control_input(), &frame_state, &effect, &control);
|
| break;
|
| +
|
| + case BasicBlock::kBranch:
|
| + ProcessNode(block->control_input(), &frame_state, &effect, &control);
|
| + TryCloneBranch(block->control_input(), block, graph(), common(),
|
| + &block_effects);
|
| + break;
|
| }
|
|
|
| - // Store the effect for later use.
|
| - block_effects[block->rpo_number()].current_effect = effect;
|
| - block_effects[block->rpo_number()].current_control = control;
|
| - block_effects[block->rpo_number()].current_frame_state = frame_state;
|
| + // Store the effect, control and frame state for later use.
|
| + for (BasicBlock* successor : block->successors()) {
|
| + BlockEffectControlData* data = &block_effects.For(block, successor);
|
| + if (data->current_effect == nullptr) {
|
| + data->current_effect = effect;
|
| + }
|
| + if (data->current_control == nullptr) {
|
| + data->current_control = control;
|
| + }
|
| + data->current_frame_state = frame_state;
|
| + }
|
| }
|
|
|
| // Update the incoming edges of the effect phis that could not be processed
|
|
|