• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 namespace v8 {
19 namespace internal {
20 namespace compiler {
21 
CommonOperatorReducer(Editor * editor,Graph * graph,JSHeapBroker * broker,CommonOperatorBuilder * common,MachineOperatorBuilder * machine,Zone * temp_zone,BranchSemantics branch_semantics)22 CommonOperatorReducer::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 
Reduce(Node * node)39 Reduction 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 
DecideCondition(Node * const cond)70 Decision 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 
ReduceBranch(Node * node)92 Reduction 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 
ReduceDeoptimizeConditional(Node * node)143 Reduction 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 
ReduceMerge(Node * node)178 Reduction 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 
ReduceEffectPhi(Node * node)214 Reduction 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 
ReducePhi(Node * node)239 Reduction 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 
ReduceReturn(Node * node)305 Reduction 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 
ReduceSelect(Node * node)389 Reduction 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 
ReduceSwitch(Node * node)432 Reduction 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 
ReduceStaticAssert(Node * node)469 Reduction 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 
ReduceTrapConditional(Node * trap)481 Reduction 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 
Change(Node * node,Operator const * op,Node * a)505 Reduction 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 
Change(Node * node,Operator const * op,Node * a,Node * b)514 Reduction 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