graph-reducer.cc 8.15 KB
Newer Older
1 2 3 4 5
// Copyright 2014 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 <functional>
6
#include <limits>
7

8 9 10
#include "src/compiler/graph.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/node.h"
11
#include "src/compiler/node-properties.h"
12
#include "src/compiler/verifier.h"
13 14 15 16 17

namespace v8 {
namespace internal {
namespace compiler {

18 19 20 21 22 23 24 25
enum class GraphReducer::State : uint8_t {
  kUnvisited,
  kRevisit,
  kOnStack,
  kVisited
};


26 27
void Reducer::Finalize() {}

28
GraphReducer::GraphReducer(Zone* zone, Graph* graph, Node* dead)
29
    : graph_(graph),
30
      dead_(dead),
31
      state_(graph, 4),
32 33
      reducers_(zone),
      revisit_(zone),
34 35 36 37 38
      stack_(zone) {
  if (dead != nullptr) {
    NodeProperties::SetType(dead_, Type::None());
  }
}
39

40 41 42
GraphReducer::~GraphReducer() {}


43 44
void GraphReducer::AddReducer(Reducer* reducer) {
  reducers_.push_back(reducer);
45 46 47 48
}


void GraphReducer::ReduceNode(Node* node) {
49 50 51 52 53 54 55 56 57 58
  DCHECK(stack_.empty());
  DCHECK(revisit_.empty());
  Push(node);
  for (;;) {
    if (!stack_.empty()) {
      // Process the node on the top of the stack, potentially pushing more or
      // popping the node off the stack.
      ReduceTop();
    } else if (!revisit_.empty()) {
      // If the stack becomes empty, revisit any nodes in the revisit queue.
59
      Node* const node = revisit_.front();
60
      revisit_.pop();
61
      if (state_.Get(node) == State::kRevisit) {
62 63 64 65
        // state can change while in queue.
        Push(node);
      }
    } else {
66 67 68 69 70
      // Run all finalizers.
      for (Reducer* const reducer : reducers_) reducer->Finalize();

      // Check if we have new nodes to revisit.
      if (revisit_.empty()) break;
71 72 73 74 75 76 77
    }
  }
  DCHECK(revisit_.empty());
  DCHECK(stack_.empty());
}


78
void GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); }
79 80 81 82 83 84


Reduction GraphReducer::Reduce(Node* const node) {
  auto skip = reducers_.end();
  for (auto i = reducers_.begin(); i != reducers_.end();) {
    if (i != skip) {
85
      Reduction reduction = (*i)->Reduce(node);
86
      if (!reduction.Changed()) {
87
        // No change from this reducer.
88 89 90
      } else if (reduction.replacement() == node) {
        // {replacement} == {node} represents an in-place reduction. Rerun
        // all the other reducers for this node, as now there may be more
91
        // opportunities for reduction.
92 93 94 95 96
        if (FLAG_trace_turbo_reduction) {
          OFStream os(stdout);
          os << "- In-place update of " << *node << " by reducer "
             << (*i)->reducer_name() << std::endl;
        }
97 98 99
        skip = i;
        i = reducers_.begin();
        continue;
100
      } else {
101
        // {node} was replaced by another node.
102 103 104 105 106 107
        if (FLAG_trace_turbo_reduction) {
          OFStream os(stdout);
          os << "- Replacement of " << *node << " with "
             << *(reduction.replacement()) << " by reducer "
             << (*i)->reducer_name() << std::endl;
        }
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
        return reduction;
      }
    }
    ++i;
  }
  if (skip == reducers_.end()) {
    // No change from any reducer.
    return Reducer::NoChange();
  }
  // At least one reducer did some in-place reduction.
  return Reducer::Changed(node);
}


void GraphReducer::ReduceTop() {
123 124
  NodeState& entry = stack_.top();
  Node* node = entry.node;
125
  DCHECK_EQ(State::kOnStack, state_.Get(node));
126

127 128
  if (node->IsDead()) return Pop();  // Node was killed while on stack.

129 130
  Node::Inputs node_inputs = node->inputs();

131 132 133 134
  // Recurse on an input if necessary.
  int start = entry.input_index < node_inputs.count() ? entry.input_index : 0;
  for (int i = start; i < node_inputs.count(); ++i) {
    Node* input = node_inputs[i];
135
    if (input != node && Recurse(input)) {
136
      entry.input_index = i + 1;
137 138
      return;
    }
139
  }
140 141
  for (int i = 0; i < start; ++i) {
    Node* input = node_inputs[i];
142
    if (input != node && Recurse(input)) {
143
      entry.input_index = i + 1;
144 145
      return;
    }
146 147
  }

148
  // Remember the max node id before reduction.
149
  NodeId const max_id = static_cast<NodeId>(graph()->NodeCount() - 1);
150 151 152 153

  // All inputs should be visited or on stack. Apply reductions to node.
  Reduction reduction = Reduce(node);

154 155 156 157 158 159 160
  // If there was no reduction, pop {node} and continue.
  if (!reduction.Changed()) return Pop();

  // Check if the reduction is an in-place update of the {node}.
  Node* const replacement = reduction.replacement();
  if (replacement == node) {
    // In-place update of {node}, may need to recurse on an input.
161
    Node::Inputs node_inputs = node->inputs();
162 163
    for (int i = 0; i < node_inputs.count(); ++i) {
      Node* input = node_inputs[i];
164
      if (input != node && Recurse(input)) {
165
        entry.input_index = i + 1;
166 167
        return;
      }
168 169 170
    }
  }

171 172 173
  // After reducing the node, pop it off the stack.
  Pop();

174 175
  // Check if we have a new replacement.
  if (replacement != node) {
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
    Replace(node, replacement, max_id);
  } else {
    // Revisit all uses of the node.
    for (Node* const user : node->uses()) {
      // Don't revisit this node if it refers to itself.
      if (user != node) Revisit(user);
    }
  }
}


void GraphReducer::Replace(Node* node, Node* replacement) {
  Replace(node, replacement, std::numeric_limits<NodeId>::max());
}


void GraphReducer::Replace(Node* node, Node* replacement, NodeId max_id) {
  if (node == graph()->start()) graph()->SetStart(replacement);
  if (node == graph()->end()) graph()->SetEnd(replacement);
  if (replacement->id() <= max_id) {
    // {replacement} is an old node, so unlink {node} and assume that
197
    // {replacement} was already reduced and finish.
198 199
    for (Edge edge : node->use_edges()) {
      Node* const user = edge.from();
200
      Verifier::VerifyEdgeInputReplacement(edge, replacement);
201 202 203 204 205 206 207 208 209 210 211 212 213 214
      edge.UpdateTo(replacement);
      // Don't revisit this node if it refers to itself.
      if (user != node) Revisit(user);
    }
    node->Kill();
  } else {
    // Replace all old uses of {node} with {replacement}, but allow new nodes
    // created by this reduction to use {node}.
    for (Edge edge : node->use_edges()) {
      Node* const user = edge.from();
      if (user->id() <= max_id) {
        edge.UpdateTo(replacement);
        // Don't revisit this node if it refers to itself.
        if (user != node) Revisit(user);
215 216
      }
    }
217 218 219 220 221
    // Unlink {node} if it's no longer used.
    if (node->uses().empty()) node->Kill();

    // If there was a replacement, reduce it after popping {node}.
    Recurse(replacement);
222 223 224 225
  }
}


226 227
void GraphReducer::ReplaceWithValue(Node* node, Node* value, Node* effect,
                                    Node* control) {
228
  if (effect == nullptr && node->op()->EffectInputCount() > 0) {
229 230 231 232 233 234 235 236
    effect = NodeProperties::GetEffectInput(node);
  }
  if (control == nullptr && node->op()->ControlInputCount() > 0) {
    control = NodeProperties::GetControlInput(node);
  }

  // Requires distinguishing between value, effect and control edges.
  for (Edge edge : node->use_edges()) {
237 238
    Node* const user = edge.from();
    DCHECK(!user->IsDead());
239 240 241 242
    if (NodeProperties::IsControlEdge(edge)) {
      if (user->opcode() == IrOpcode::kIfSuccess) {
        Replace(user, control);
      } else if (user->opcode() == IrOpcode::kIfException) {
243 244 245
        DCHECK_NOT_NULL(dead_);
        edge.UpdateTo(dead_);
        Revisit(user);
246
      } else {
247 248 249
        DCHECK_NOT_NULL(control);
        edge.UpdateTo(control);
        Revisit(user);
250 251 252 253 254 255
      }
    } else if (NodeProperties::IsEffectEdge(edge)) {
      DCHECK_NOT_NULL(effect);
      edge.UpdateTo(effect);
      Revisit(user);
    } else {
256
      DCHECK_NOT_NULL(value);
257 258 259 260 261 262 263
      edge.UpdateTo(value);
      Revisit(user);
    }
  }
}


264
void GraphReducer::Pop() {
265 266
  Node* node = stack_.top().node;
  state_.Set(node, State::kVisited);
267 268
  stack_.pop();
}
269 270


271
void GraphReducer::Push(Node* const node) {
272
  DCHECK_NE(State::kOnStack, state_.Get(node));
273
  state_.Set(node, State::kOnStack);
274
  stack_.push({node, 0});
275 276 277
}


278 279
bool GraphReducer::Recurse(Node* node) {
  if (state_.Get(node) > State::kRevisit) return false;
280 281 282 283 284
  Push(node);
  return true;
}


285 286 287
void GraphReducer::Revisit(Node* node) {
  if (state_.Get(node) == State::kVisited) {
    state_.Set(node, State::kRevisit);
288 289 290
    revisit_.push(node);
  }
}
291 292 293 294

}  // namespace compiler
}  // namespace internal
}  // namespace v8