1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/compiler/common-operator-reducer.h"
6
7#include <algorithm>
8
9#include "src/compiler/common-operator.h"
10#include "src/compiler/graph.h"
11#include "src/compiler/js-heap-broker.h"
12#include "src/compiler/machine-operator.h"
13#include "src/compiler/node-matchers.h"
14#include "src/compiler/node-properties.h"
15#include "src/compiler/node.h"
16#include "src/compiler/opcodes.h"
17
18namespace v8 {
19namespace internal {
20namespace compiler {
21
22CommonOperatorReducer::CommonOperatorReducer(Editor* editor, Graph* graph,
23                                             JSHeapBroker* broker,
24                                             CommonOperatorBuilder* common,
25                                             MachineOperatorBuilder* machine,
26                                             Zone* temp_zone,
27                                             BranchSemantics branch_semantics)
28    : AdvancedReducer(editor),
29      graph_(graph),
30      broker_(broker),
31      common_(common),
32      machine_(machine),
33      dead_(graph->NewNode(common->Dead())),
34      zone_(temp_zone),
35      branch_semantics_(branch_semantics) {
36  NodeProperties::SetType(dead_, Type::None());
37}
38
39Reduction CommonOperatorReducer::Reduce(Node* node) {
40  DisallowHeapAccessIf no_heap_access(broker() == nullptr);
41  switch (node->opcode()) {
42    case IrOpcode::kBranch:
43      return ReduceBranch(node);
44    case IrOpcode::kDeoptimizeIf:
45    case IrOpcode::kDeoptimizeUnless:
46      return ReduceDeoptimizeConditional(node);
47    case IrOpcode::kMerge:
48      return ReduceMerge(node);
49    case IrOpcode::kEffectPhi:
50      return ReduceEffectPhi(node);
51    case IrOpcode::kPhi:
52      return ReducePhi(node);
53    case IrOpcode::kReturn:
54      return ReduceReturn(node);
55    case IrOpcode::kSelect:
56      return ReduceSelect(node);
57    case IrOpcode::kSwitch:
58      return ReduceSwitch(node);
59    case IrOpcode::kStaticAssert:
60      return ReduceStaticAssert(node);
61    case IrOpcode::kTrapIf:
62    case IrOpcode::kTrapUnless:
63      return ReduceTrapConditional(node);
64    default:
65      break;
66  }
67  return NoChange();
68}
69
70Decision CommonOperatorReducer::DecideCondition(Node* const cond) {
71  Node* unwrapped = SkipValueIdentities(cond);
72  switch (unwrapped->opcode()) {
73    case IrOpcode::kInt32Constant: {
74      DCHECK_EQ(branch_semantics_, BranchSemantics::kMachine);
75      Int32Matcher m(unwrapped);
76      return m.ResolvedValue() ? Decision::kTrue : Decision::kFalse;
77    }
78    case IrOpcode::kHeapConstant: {
79      if (branch_semantics_ == BranchSemantics::kMachine) {
80        return Decision::kTrue;
81      }
82      HeapObjectMatcher m(unwrapped);
83      base::Optional<bool> maybe_result = m.Ref(broker_).TryGetBooleanValue();
84      if (!maybe_result.has_value()) return Decision::kUnknown;
85      return *maybe_result ? Decision::kTrue : Decision::kFalse;
86    }
87    default:
88      return Decision::kUnknown;
89  }
90}
91
92Reduction CommonOperatorReducer::ReduceBranch(Node* node) {
93  DCHECK_EQ(IrOpcode::kBranch, node->opcode());
94  Node* const cond = node->InputAt(0);
95  // Swap IfTrue/IfFalse on {branch} if {cond} is a BooleanNot and use the input
96  // to BooleanNot as new condition for {branch}. Note we assume that {cond} was
97  // already properly optimized before we get here (as guaranteed by the graph
98  // reduction logic). The same applies if {cond} is a Select acting as boolean
99  // not (i.e. true being returned in the false case and vice versa).
100  if (cond->opcode() == IrOpcode::kBooleanNot ||
101      (cond->opcode() == IrOpcode::kSelect &&
102       DecideCondition(cond->InputAt(1)) == Decision::kFalse &&
103       DecideCondition(cond->InputAt(2)) == Decision::kTrue)) {
104    for (Node* const use : node->uses()) {
105      switch (use->opcode()) {
106        case IrOpcode::kIfTrue:
107          NodeProperties::ChangeOp(use, common()->IfFalse());
108          break;
109        case IrOpcode::kIfFalse:
110          NodeProperties::ChangeOp(use, common()->IfTrue());
111          break;
112        default:
113          UNREACHABLE();
114      }
115    }
116    // Update the condition of {branch}. No need to mark the uses for revisit,
117    // since we tell the graph reducer that the {branch} was changed and the
118    // graph reduction logic will ensure that the uses are revisited properly.
119    node->ReplaceInput(0, cond->InputAt(0));
120    // Negate the hint for {branch}.
121    NodeProperties::ChangeOp(
122        node, common()->Branch(NegateBranchHint(BranchHintOf(node->op()))));
123    return Changed(node);
124  }
125  Decision const decision = DecideCondition(cond);
126  if (decision == Decision::kUnknown) return NoChange();
127  Node* const control = node->InputAt(1);
128  for (Node* const use : node->uses()) {
129    switch (use->opcode()) {
130      case IrOpcode::kIfTrue:
131        Replace(use, (decision == Decision::kTrue) ? control : dead());
132        break;
133      case IrOpcode::kIfFalse:
134        Replace(use, (decision == Decision::kFalse) ? control : dead());
135        break;
136      default:
137        UNREACHABLE();
138    }
139  }
140  return Replace(dead());
141}
142
143Reduction CommonOperatorReducer::ReduceDeoptimizeConditional(Node* node) {
144  DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
145         node->opcode() == IrOpcode::kDeoptimizeUnless);
146  bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
147  DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
148  Node* condition = NodeProperties::GetValueInput(node, 0);
149  Node* frame_state = NodeProperties::GetValueInput(node, 1);
150  Node* effect = NodeProperties::GetEffectInput(node);
151  Node* control = NodeProperties::GetControlInput(node);
152  // Swap DeoptimizeIf/DeoptimizeUnless on {node} if {cond} is a BooleaNot
153  // and use the input to BooleanNot as new condition for {node}.  Note we
154  // assume that {cond} was already properly optimized before we get here
155  // (as guaranteed by the graph reduction logic).
156  if (condition->opcode() == IrOpcode::kBooleanNot) {
157    NodeProperties::ReplaceValueInput(node, condition->InputAt(0), 0);
158    NodeProperties::ChangeOp(
159        node, condition_is_true
160                  ? common()->DeoptimizeIf(p.reason(), p.feedback())
161                  : common()->DeoptimizeUnless(p.reason(), p.feedback()));
162    return Changed(node);
163  }
164  Decision const decision = DecideCondition(condition);
165  if (decision == Decision::kUnknown) return NoChange();
166  if (condition_is_true == (decision == Decision::kTrue)) {
167    ReplaceWithValue(node, dead(), effect, control);
168  } else {
169    control = graph()->NewNode(common()->Deoptimize(p.reason(), p.feedback()),
170                               frame_state, effect, control);
171    // TODO(bmeurer): This should be on the AdvancedReducer somehow.
172    NodeProperties::MergeControlToEnd(graph(), common(), control);
173    Revisit(graph()->end());
174  }
175  return Replace(dead());
176}
177
178Reduction CommonOperatorReducer::ReduceMerge(Node* node) {
179  DCHECK_EQ(IrOpcode::kMerge, node->opcode());
180  //
181  // Check if this is a merge that belongs to an unused diamond, which means
182  // that:
183  //
184  //  a) the {Merge} has no {Phi} or {EffectPhi} uses, and
185  //  b) the {Merge} has two inputs, one {IfTrue} and one {IfFalse}, which are
186  //     both owned by the Merge, and
187  //  c) and the {IfTrue} and {IfFalse} nodes point to the same {Branch}.
188  //
189  if (node->InputCount() == 2) {
190    for (Node* const use : node->uses()) {
191      if (IrOpcode::IsPhiOpcode(use->opcode())) return NoChange();
192    }
193    Node* if_true = node->InputAt(0);
194    Node* if_false = node->InputAt(1);
195    if (if_true->opcode() != IrOpcode::kIfTrue) std::swap(if_true, if_false);
196    if (if_true->opcode() == IrOpcode::kIfTrue &&
197        if_false->opcode() == IrOpcode::kIfFalse &&
198        if_true->InputAt(0) == if_false->InputAt(0) && if_true->OwnedBy(node) &&
199        if_false->OwnedBy(node)) {
200      Node* const branch = if_true->InputAt(0);
201      DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
202      DCHECK(branch->OwnedBy(if_true, if_false));
203      Node* const control = branch->InputAt(1);
204      // Mark the {branch} as {Dead}.
205      branch->TrimInputCount(0);
206      NodeProperties::ChangeOp(branch, common()->Dead());
207      return Replace(control);
208    }
209  }
210  return NoChange();
211}
212
213
214Reduction CommonOperatorReducer::ReduceEffectPhi(Node* node) {
215  DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
216  Node::Inputs inputs = node->inputs();
217  int const effect_input_count = inputs.count() - 1;
218  DCHECK_LE(1, effect_input_count);
219  Node* const merge = inputs[effect_input_count];
220  DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
221  DCHECK_EQ(effect_input_count, merge->InputCount());
222  Node* const effect = inputs[0];
223  DCHECK_NE(node, effect);
224  for (int i = 1; i < effect_input_count; ++i) {
225    Node* const input = inputs[i];
226    if (input == node) {
227      // Ignore redundant inputs.
228      DCHECK_EQ(IrOpcode::kLoop, merge->opcode());
229      continue;
230    }
231    if (input != effect) return NoChange();
232  }
233  // We might now be able to further reduce the {merge} node.
234  Revisit(merge);
235  return Replace(effect);
236}
237
238
239Reduction CommonOperatorReducer::ReducePhi(Node* node) {
240  DCHECK_EQ(IrOpcode::kPhi, node->opcode());
241  Node::Inputs inputs = node->inputs();
242  int const value_input_count = inputs.count() - 1;
243  DCHECK_LE(1, value_input_count);
244  Node* const merge = inputs[value_input_count];
245  DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
246  DCHECK_EQ(value_input_count, merge->InputCount());
247  if (value_input_count == 2) {
248    Node* vtrue = inputs[0];
249    Node* vfalse = inputs[1];
250    Node::Inputs merge_inputs = merge->inputs();
251    Node* if_true = merge_inputs[0];
252    Node* if_false = merge_inputs[1];
253    if (if_true->opcode() != IrOpcode::kIfTrue) {
254      std::swap(if_true, if_false);
255      std::swap(vtrue, vfalse);
256    }
257    if (if_true->opcode() == IrOpcode::kIfTrue &&
258        if_false->opcode() == IrOpcode::kIfFalse &&
259        if_true->InputAt(0) == if_false->InputAt(0)) {
260      Node* const branch = if_true->InputAt(0);
261      // Check that the branch is not dead already.
262      if (branch->opcode() != IrOpcode::kBranch) return NoChange();
263      Node* const cond = branch->InputAt(0);
264      if (cond->opcode() == IrOpcode::kFloat32LessThan) {
265        Float32BinopMatcher mcond(cond);
266        if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
267            vfalse->opcode() == IrOpcode::kFloat32Sub) {
268          Float32BinopMatcher mvfalse(vfalse);
269          if (mvfalse.left().IsZero() && mvfalse.right().Equals(vtrue)) {
270            // We might now be able to further reduce the {merge} node.
271            Revisit(merge);
272            return Change(node, machine()->Float32Abs(), vtrue);
273          }
274        }
275      } else if (cond->opcode() == IrOpcode::kFloat64LessThan) {
276        Float64BinopMatcher mcond(cond);
277        if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
278            vfalse->opcode() == IrOpcode::kFloat64Sub) {
279          Float64BinopMatcher mvfalse(vfalse);
280          if (mvfalse.left().IsZero() && mvfalse.right().Equals(vtrue)) {
281            // We might now be able to further reduce the {merge} node.
282            Revisit(merge);
283            return Change(node, machine()->Float64Abs(), vtrue);
284          }
285        }
286      }
287    }
288  }
289  Node* const value = inputs[0];
290  DCHECK_NE(node, value);
291  for (int i = 1; i < value_input_count; ++i) {
292    Node* const input = inputs[i];
293    if (input == node) {
294      // Ignore redundant inputs.
295      DCHECK_EQ(IrOpcode::kLoop, merge->opcode());
296      continue;
297    }
298    if (input != value) return NoChange();
299  }
300  // We might now be able to further reduce the {merge} node.
301  Revisit(merge);
302  return Replace(value);
303}
304
305Reduction CommonOperatorReducer::ReduceReturn(Node* node) {
306  DCHECK_EQ(IrOpcode::kReturn, node->opcode());
307  Node* effect = NodeProperties::GetEffectInput(node);
308  if (effect->opcode() == IrOpcode::kCheckpoint) {
309    // Any {Return} node can never be used to insert a deoptimization point,
310    // hence checkpoints can be cut out of the effect chain flowing into it.
311    effect = NodeProperties::GetEffectInput(effect);
312    NodeProperties::ReplaceEffectInput(node, effect);
313    return Changed(node).FollowedBy(ReduceReturn(node));
314  }
315  // TODO(ahaas): Extend the reduction below to multiple return values.
316  if (ValueInputCountOfReturn(node->op()) != 1) {
317    return NoChange();
318  }
319  Node* pop_count = NodeProperties::GetValueInput(node, 0);
320  Node* value = NodeProperties::GetValueInput(node, 1);
321  Node* control = NodeProperties::GetControlInput(node);
322  if (value->opcode() == IrOpcode::kPhi &&
323      NodeProperties::GetControlInput(value) == control &&
324      control->opcode() == IrOpcode::kMerge) {
325    // This optimization pushes {Return} nodes through merges. It checks that
326    // the return value is actually a {Phi} and the return control dependency
327    // is the {Merge} to which the {Phi} belongs.
328
329    // Value1 ... ValueN Control1 ... ControlN
330    //   ^          ^       ^            ^
331    //   |          |       |            |
332    //   +----+-----+       +------+-----+
333    //        |                    |
334    //       Phi --------------> Merge
335    //        ^                    ^
336    //        |                    |
337    //        |  +-----------------+
338    //        |  |
339    //       Return -----> Effect
340    //         ^
341    //         |
342    //        End
343
344    // Now the effect input to the {Return} node can be either an {EffectPhi}
345    // hanging off the same {Merge}, or the effect chain doesn't depend on the
346    // {Phi} or the {Merge}, in which case we know that the effect input must
347    // somehow dominate all merged branches.
348
349    Node::Inputs control_inputs = control->inputs();
350    Node::Inputs value_inputs = value->inputs();
351    DCHECK_NE(0, control_inputs.count());
352    DCHECK_EQ(control_inputs.count(), value_inputs.count() - 1);
353    DCHECK_EQ(IrOpcode::kEnd, graph()->end()->opcode());
354    DCHECK_NE(0, graph()->end()->InputCount());
355    if (control->OwnedBy(node, value) && value->OwnedBy(node)) {
356      for (int i = 0; i < control_inputs.count(); ++i) {
357        // Create a new {Return} and connect it to {end}. We don't need to mark
358        // {end} as revisit, because we mark {node} as {Dead} below, which was
359        // previously connected to {end}, so we know for sure that at some point
360        // the reducer logic will visit {end} again.
361        Node* ret = graph()->NewNode(node->op(), pop_count, value_inputs[i],
362                                     effect, control_inputs[i]);
363        NodeProperties::MergeControlToEnd(graph(), common(), ret);
364      }
365      // Mark the Merge {control} and Return {node} as {dead}.
366      Replace(control, dead());
367      return Replace(dead());
368    } else if (effect->opcode() == IrOpcode::kEffectPhi &&
369               NodeProperties::GetControlInput(effect) == control) {
370      Node::Inputs effect_inputs = effect->inputs();
371      DCHECK_EQ(control_inputs.count(), effect_inputs.count() - 1);
372      for (int i = 0; i < control_inputs.count(); ++i) {
373        // Create a new {Return} and connect it to {end}. We don't need to mark
374        // {end} as revisit, because we mark {node} as {Dead} below, which was
375        // previously connected to {end}, so we know for sure that at some point
376        // the reducer logic will visit {end} again.
377        Node* ret = graph()->NewNode(node->op(), pop_count, value_inputs[i],
378                                     effect_inputs[i], control_inputs[i]);
379        NodeProperties::MergeControlToEnd(graph(), common(), ret);
380      }
381      // Mark the Merge {control} and Return {node} as {dead}.
382      Replace(control, dead());
383      return Replace(dead());
384    }
385  }
386  return NoChange();
387}
388
389Reduction CommonOperatorReducer::ReduceSelect(Node* node) {
390  DCHECK_EQ(IrOpcode::kSelect, node->opcode());
391  Node* const cond = node->InputAt(0);
392  Node* const vtrue = node->InputAt(1);
393  Node* const vfalse = node->InputAt(2);
394  if (vtrue == vfalse) return Replace(vtrue);
395  switch (DecideCondition(cond)) {
396    case Decision::kTrue:
397      return Replace(vtrue);
398    case Decision::kFalse:
399      return Replace(vfalse);
400    case Decision::kUnknown:
401      break;
402  }
403  switch (cond->opcode()) {
404    case IrOpcode::kFloat32LessThan: {
405      Float32BinopMatcher mcond(cond);
406      if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
407          vfalse->opcode() == IrOpcode::kFloat32Sub) {
408        Float32BinopMatcher mvfalse(vfalse);
409        if (mvfalse.left().IsZero() && mvfalse.right().Equals(vtrue)) {
410          return Change(node, machine()->Float32Abs(), vtrue);
411        }
412      }
413      break;
414    }
415    case IrOpcode::kFloat64LessThan: {
416      Float64BinopMatcher mcond(cond);
417      if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
418          vfalse->opcode() == IrOpcode::kFloat64Sub) {
419        Float64BinopMatcher mvfalse(vfalse);
420        if (mvfalse.left().IsZero() && mvfalse.right().Equals(vtrue)) {
421          return Change(node, machine()->Float64Abs(), vtrue);
422        }
423      }
424      break;
425    }
426    default:
427      break;
428  }
429  return NoChange();
430}
431
432Reduction CommonOperatorReducer::ReduceSwitch(Node* node) {
433  DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
434  Node* const switched_value = node->InputAt(0);
435  Node* const control = node->InputAt(1);
436
437  // Attempt to constant match the switched value against the IfValue cases. If
438  // no case matches, then use the IfDefault. We don't bother marking
439  // non-matching cases as dead code (same for an unused IfDefault), because the
440  // Switch itself will be marked as dead code.
441  Int32Matcher mswitched(switched_value);
442  if (mswitched.HasResolvedValue()) {
443    bool matched = false;
444
445    size_t const projection_count = node->op()->ControlOutputCount();
446    Node** projections = zone_->NewArray<Node*>(projection_count);
447    NodeProperties::CollectControlProjections(node, projections,
448                                              projection_count);
449    for (size_t i = 0; i < projection_count - 1; i++) {
450      Node* if_value = projections[i];
451      DCHECK_EQ(IrOpcode::kIfValue, if_value->opcode());
452      const IfValueParameters& p = IfValueParametersOf(if_value->op());
453      if (p.value() == mswitched.ResolvedValue()) {
454        matched = true;
455        Replace(if_value, control);
456        break;
457      }
458    }
459    if (!matched) {
460      Node* if_default = projections[projection_count - 1];
461      DCHECK_EQ(IrOpcode::kIfDefault, if_default->opcode());
462      Replace(if_default, control);
463    }
464    return Replace(dead());
465  }
466  return NoChange();
467}
468
469Reduction CommonOperatorReducer::ReduceStaticAssert(Node* node) {
470  DCHECK_EQ(IrOpcode::kStaticAssert, node->opcode());
471  Node* const cond = node->InputAt(0);
472  Decision decision = DecideCondition(cond);
473  if (decision == Decision::kTrue) {
474    RelaxEffectsAndControls(node);
475    return Changed(node);
476  } else {
477    return NoChange();
478  }
479}
480
481Reduction CommonOperatorReducer::ReduceTrapConditional(Node* trap) {
482  DCHECK(trap->opcode() == IrOpcode::kTrapIf ||
483         trap->opcode() == IrOpcode::kTrapUnless);
484  bool trapping_condition = trap->opcode() == IrOpcode::kTrapIf;
485  Node* const cond = trap->InputAt(0);
486  Decision decision = DecideCondition(cond);
487
488  if (decision == Decision::kUnknown) {
489    return NoChange();
490  } else if ((decision == Decision::kTrue) == trapping_condition) {
491    // This will always trap. Mark its outputs as dead and connect it to
492    // graph()->end().
493    ReplaceWithValue(trap, dead(), dead(), dead());
494    Node* effect = NodeProperties::GetEffectInput(trap);
495    Node* control = graph()->NewNode(common()->Throw(), effect, trap);
496    NodeProperties::MergeControlToEnd(graph(), common(), control);
497    Revisit(graph()->end());
498    return Changed(trap);
499  } else {
500    // This will not trap, remove it.
501    return Replace(NodeProperties::GetControlInput(trap));
502  }
503}
504
505Reduction CommonOperatorReducer::Change(Node* node, Operator const* op,
506                                        Node* a) {
507  node->ReplaceInput(0, a);
508  node->TrimInputCount(1);
509  NodeProperties::ChangeOp(node, op);
510  return Changed(node);
511}
512
513
514Reduction CommonOperatorReducer::Change(Node* node, Operator const* op, Node* a,
515                                        Node* b) {
516  node->ReplaceInput(0, a);
517  node->ReplaceInput(1, b);
518  node->TrimInputCount(2);
519  NodeProperties::ChangeOp(node, op);
520  return Changed(node);
521}
522
523}  // namespace compiler
524}  // namespace internal
525}  // namespace v8
526