branch-elimination.cc 9.01 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Copyright 2015 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 "src/compiler/branch-elimination.h"

#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h"

namespace v8 {
namespace internal {
namespace compiler {

BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
                                     Zone* zone)
    : AdvancedReducer(editor),
18
      jsgraph_(js_graph),
19 20
      node_conditions_(js_graph->graph()->NodeCount(), zone),
      reduced_(js_graph->graph()->NodeCount(), zone),
21
      zone_(zone),
22
      dead_(js_graph->Dead()) {}
23 24 25 26 27 28 29 30

BranchElimination::~BranchElimination() {}


Reduction BranchElimination::Reduce(Node* node) {
  switch (node->opcode()) {
    case IrOpcode::kDead:
      return NoChange();
31 32 33
    case IrOpcode::kDeoptimizeIf:
    case IrOpcode::kDeoptimizeUnless:
      return ReduceDeoptimizeConditional(node);
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    case IrOpcode::kMerge:
      return ReduceMerge(node);
    case IrOpcode::kLoop:
      return ReduceLoop(node);
    case IrOpcode::kBranch:
      return ReduceBranch(node);
    case IrOpcode::kIfFalse:
      return ReduceIf(node, false);
    case IrOpcode::kIfTrue:
      return ReduceIf(node, true);
    case IrOpcode::kStart:
      return ReduceStart(node);
    default:
      if (node->op()->ControlOutputCount() > 0) {
        return ReduceOtherControl(node);
      }
      break;
  }
  return NoChange();
}


Reduction BranchElimination::ReduceBranch(Node* node) {
  Node* condition = node->InputAt(0);
  Node* control_input = NodeProperties::GetControlInput(node, 0);
59
  ControlPathConditions from_input = node_conditions_.Get(control_input);
60 61
  Node* branch;
  bool condition_value;
62
  // If we know the condition we can discard the branch.
63
  if (from_input.LookupCondition(condition, &branch, &condition_value)) {
64
    // Mark the branch as a safety check if necessary.
65
    // Check if {branch} is dead because we might have a stale side-table entry.
66 67 68 69 70 71 72 73
    if (!branch->IsDead()) {
      IsSafetyCheck branch_safety = IsSafetyCheckOf(branch->op());
      IsSafetyCheck combined_safety =
          CombineSafetyChecks(branch_safety, IsSafetyCheckOf(node->op()));
      if (branch_safety != combined_safety) {
        NodeProperties::ChangeOp(
            branch, common()->MarkAsSafetyCheck(branch->op(), combined_safety));
      }
74 75
    }

76 77 78
    for (Node* const use : node->uses()) {
      switch (use->opcode()) {
        case IrOpcode::kIfTrue:
79
          Replace(use, condition_value ? control_input : dead());
80 81
          break;
        case IrOpcode::kIfFalse:
82
          Replace(use, condition_value ? dead() : control_input);
83 84 85
          break;
        default:
          UNREACHABLE();
86 87
      }
    }
88
    return Replace(dead());
89 90 91 92
  }
  return TakeConditionsFromFirstControl(node);
}

93 94 95 96
Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
  DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
         node->opcode() == IrOpcode::kDeoptimizeUnless);
  bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
97
  DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
98 99 100 101 102 103 104
  Node* condition = NodeProperties::GetValueInput(node, 0);
  Node* frame_state = NodeProperties::GetValueInput(node, 1);
  Node* effect = NodeProperties::GetEffectInput(node);
  Node* control = NodeProperties::GetControlInput(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.
105 106
  if (!reduced_.Get(control)) {
    return NoChange();
107
  }
108 109

  ControlPathConditions conditions = node_conditions_.Get(control);
110 111 112 113
  bool condition_value;
  Node* branch;
  if (conditions.LookupCondition(condition, &branch, &condition_value)) {
    // Mark the branch as a safety check.
114 115 116 117 118 119
    IsSafetyCheck branch_safety = IsSafetyCheckOf(branch->op());
    IsSafetyCheck combined_safety =
        CombineSafetyChecks(branch_safety, p.is_safety_check());
    if (branch_safety != combined_safety) {
      NodeProperties::ChangeOp(
          branch, common()->MarkAsSafetyCheck(branch->op(), combined_safety));
120 121
    }

122
    // If we know the condition we can discard the branch.
123
    if (condition_is_true == condition_value) {
124 125 126
      // We don't update the conditions here, because we're replacing {node}
      // with the {control} node that already contains the right information.
      ReplaceWithValue(node, dead(), effect, control);
127
    } else {
128
      control = graph()->NewNode(
129 130
          common()->Deoptimize(p.kind(), p.reason(), p.feedback()), frame_state,
          effect, control);
131 132 133 134
      // TODO(bmeurer): This should be on the AdvancedReducer somehow.
      NodeProperties::MergeControlToEnd(graph(), common(), control);
      Revisit(graph()->end());
    }
135
    return Replace(dead());
136
  }
137
  return UpdateConditions(node, conditions, condition, node, condition_is_true);
138
}
139 140 141 142

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);
143
  ControlPathConditions from_branch = node_conditions_.Get(branch);
144 145 146
  // 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.
147 148
  if (!reduced_.Get(branch)) {
    return NoChange();
149 150
  }
  Node* condition = branch->InputAt(0);
151
  return UpdateConditions(node, from_branch, condition, branch, is_true_branch);
152 153 154 155 156 157 158 159 160 161 162 163 164 165
}


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);
}


Reduction BranchElimination::ReduceMerge(Node* node) {
  // Shortcut for the case when we do not know anything about some
  // input.
166 167
  Node::Inputs inputs = node->inputs();
  for (Node* input : inputs) {
168 169
    if (!reduced_.Get(input)) {
      return NoChange();
170 171 172
    }
  }

173 174 175 176
  auto input_it = inputs.begin();

  DCHECK_GT(inputs.count(), 0);

177
  ControlPathConditions conditions = node_conditions_.Get(*input_it);
178
  ++input_it;
179 180
  // Merge the first input's conditions with the conditions from the other
  // inputs.
181 182
  auto input_end = inputs.end();
  for (; input_it != input_end; ++input_it) {
183 184 185 186
    // Change the current condition 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));
187 188 189 190 191 192
  }
  return UpdateConditions(node, conditions);
}


Reduction BranchElimination::ReduceStart(Node* node) {
193
  return UpdateConditions(node, {});
194 195 196 197 198 199 200 201 202 203 204 205
}


Reduction BranchElimination::ReduceOtherControl(Node* node) {
  DCHECK_EQ(1, node->op()->ControlInputCount());
  return TakeConditionsFromFirstControl(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).
206 207 208
  Node* input = NodeProperties::GetControlInput(node, 0);
  if (!reduced_.Get(input)) return NoChange();
  return UpdateConditions(node, node_conditions_.Get(input));
209 210 211
}

Reduction BranchElimination::UpdateConditions(
212
    Node* node, ControlPathConditions conditions) {
213 214
  // Only signal that the node has Changed if the condition information has
  // changed.
215 216
  if (reduced_.Set(node, true) | node_conditions_.Set(node, conditions)) {
    return Changed(node);
217 218 219 220
  }
  return NoChange();
}

221
Reduction BranchElimination::UpdateConditions(
222
    Node* node, ControlPathConditions prev_conditions, Node* current_condition,
223
    Node* current_branch, bool is_true_branch) {
224
  ControlPathConditions original = node_conditions_.Get(node);
225
  // The control path for the node is the path obtained by appending the
226 227
  // current_condition to the prev_conditions. Use the original control path as
  // a hint to avoid allocations.
228 229
  prev_conditions.AddCondition(zone_, current_condition, current_branch,
                               is_true_branch, original);
230
  return UpdateConditions(node, prev_conditions);
231
}
232

233
void BranchElimination::ControlPathConditions::AddCondition(
234 235 236 237
    Zone* zone, Node* condition, Node* branch, bool is_true,
    ControlPathConditions hint) {
  DCHECK_EQ(false, LookupCondition(condition, nullptr, nullptr));
  PushFront({condition, branch, is_true}, zone, hint);
238 239
}

240 241
bool BranchElimination::ControlPathConditions::LookupCondition(
    Node* condition, Node** branch, bool* is_true) const {
242
  for (BranchCondition element : *this) {
243 244 245 246 247 248 249
    if (element.condition == condition) {
      *is_true = element.is_true;
      *branch = element.branch;
      return true;
    }
  }
  return false;
250 251
  }

252
  Graph* BranchElimination::graph() const { return jsgraph()->graph(); }
253

254 255 256
  CommonOperatorBuilder* BranchElimination::common() const {
    return jsgraph()->common();
  }
257

258 259 260
}  // namespace compiler
}  // namespace internal
}  // namespace v8