blob: 9527c754e4ebdc4fd4400b97de82d997fbae0cc9 [file] [log] [blame]
Emily Bernier958fae72015-03-24 16:35:39 -04001// 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
Ben Murdoch014dc512016-03-22 12:00:34 +00007#include <algorithm>
8
Emily Bernier958fae72015-03-24 16:35:39 -04009#include "src/compiler/common-operator.h"
Ben Murdoch014dc512016-03-22 12:00:34 +000010#include "src/compiler/graph.h"
11#include "src/compiler/machine-operator.h"
12#include "src/compiler/node.h"
13#include "src/compiler/node-matchers.h"
14#include "src/compiler/node-properties.h"
Emily Bernier958fae72015-03-24 16:35:39 -040015
16namespace v8 {
17namespace internal {
18namespace compiler {
19
Ben Murdoch014dc512016-03-22 12:00:34 +000020namespace {
21
Ben Murdoch014dc512016-03-22 12:00:34 +000022Decision DecideCondition(Node* const cond) {
23 switch (cond->opcode()) {
24 case IrOpcode::kInt32Constant: {
25 Int32Matcher mcond(cond);
26 return mcond.Value() ? Decision::kTrue : Decision::kFalse;
27 }
Ben Murdoch014dc512016-03-22 12:00:34 +000028 case IrOpcode::kHeapConstant: {
29 HeapObjectMatcher mcond(cond);
30 return mcond.Value()->BooleanValue() ? Decision::kTrue : Decision::kFalse;
31 }
32 default:
33 return Decision::kUnknown;
34 }
35}
36
37} // namespace
38
39
40CommonOperatorReducer::CommonOperatorReducer(Editor* editor, Graph* graph,
41 CommonOperatorBuilder* common,
42 MachineOperatorBuilder* machine)
43 : AdvancedReducer(editor),
44 graph_(graph),
45 common_(common),
46 machine_(machine),
47 dead_(graph->NewNode(common->Dead())) {}
48
49
Emily Bernier958fae72015-03-24 16:35:39 -040050Reduction CommonOperatorReducer::Reduce(Node* node) {
51 switch (node->opcode()) {
Ben Murdoch014dc512016-03-22 12:00:34 +000052 case IrOpcode::kBranch:
53 return ReduceBranch(node);
Ben Murdoch3b9bc312016-06-02 14:46:10 +010054 case IrOpcode::kDeoptimizeIf:
55 case IrOpcode::kDeoptimizeUnless:
56 return ReduceDeoptimizeConditional(node);
Ben Murdoch014dc512016-03-22 12:00:34 +000057 case IrOpcode::kMerge:
58 return ReduceMerge(node);
Emily Bernier958fae72015-03-24 16:35:39 -040059 case IrOpcode::kEffectPhi:
Ben Murdoch014dc512016-03-22 12:00:34 +000060 return ReduceEffectPhi(node);
61 case IrOpcode::kPhi:
62 return ReducePhi(node);
63 case IrOpcode::kReturn:
64 return ReduceReturn(node);
65 case IrOpcode::kSelect:
66 return ReduceSelect(node);
Ben Murdoch014dc512016-03-22 12:00:34 +000067 default:
68 break;
69 }
70 return NoChange();
71}
72
73
74Reduction CommonOperatorReducer::ReduceBranch(Node* node) {
75 DCHECK_EQ(IrOpcode::kBranch, node->opcode());
76 Node* const cond = node->InputAt(0);
77 // Swap IfTrue/IfFalse on {branch} if {cond} is a BooleanNot and use the input
78 // to BooleanNot as new condition for {branch}. Note we assume that {cond} was
79 // already properly optimized before we get here (as guaranteed by the graph
80 // reduction logic).
81 if (cond->opcode() == IrOpcode::kBooleanNot) {
82 for (Node* const use : node->uses()) {
83 switch (use->opcode()) {
84 case IrOpcode::kIfTrue:
85 NodeProperties::ChangeOp(use, common()->IfFalse());
86 break;
87 case IrOpcode::kIfFalse:
88 NodeProperties::ChangeOp(use, common()->IfTrue());
89 break;
90 default:
91 UNREACHABLE();
92 }
93 }
94 // Update the condition of {branch}. No need to mark the uses for revisit,
95 // since we tell the graph reducer that the {branch} was changed and the
96 // graph reduction logic will ensure that the uses are revisited properly.
97 node->ReplaceInput(0, cond->InputAt(0));
98 // Negate the hint for {branch}.
99 NodeProperties::ChangeOp(
100 node, common()->Branch(NegateBranchHint(BranchHintOf(node->op()))));
101 return Changed(node);
102 }
103 Decision const decision = DecideCondition(cond);
104 if (decision == Decision::kUnknown) return NoChange();
105 Node* const control = node->InputAt(1);
106 for (Node* const use : node->uses()) {
107 switch (use->opcode()) {
108 case IrOpcode::kIfTrue:
109 Replace(use, (decision == Decision::kTrue) ? control : dead());
110 break;
111 case IrOpcode::kIfFalse:
112 Replace(use, (decision == Decision::kFalse) ? control : dead());
113 break;
114 default:
115 UNREACHABLE();
116 }
117 }
118 return Replace(dead());
119}
120
Ben Murdoch3b9bc312016-06-02 14:46:10 +0100121Reduction CommonOperatorReducer::ReduceDeoptimizeConditional(Node* node) {
122 DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
123 node->opcode() == IrOpcode::kDeoptimizeUnless);
124 bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
Ben Murdochf91f0612016-11-29 16:50:11 +0000125 DeoptimizeReason reason = DeoptimizeReasonOf(node->op());
Ben Murdoch3b9bc312016-06-02 14:46:10 +0100126 Node* condition = NodeProperties::GetValueInput(node, 0);
127 Node* frame_state = NodeProperties::GetValueInput(node, 1);
128 Node* effect = NodeProperties::GetEffectInput(node);
129 Node* control = NodeProperties::GetControlInput(node);
130 // Swap DeoptimizeIf/DeoptimizeUnless on {node} if {cond} is a BooleaNot
131 // and use the input to BooleanNot as new condition for {node}. Note we
132 // assume that {cond} was already properly optimized before we get here
133 // (as guaranteed by the graph reduction logic).
134 if (condition->opcode() == IrOpcode::kBooleanNot) {
135 NodeProperties::ReplaceValueInput(node, condition->InputAt(0), 0);
136 NodeProperties::ChangeOp(node, condition_is_true
Ben Murdochf91f0612016-11-29 16:50:11 +0000137 ? common()->DeoptimizeIf(reason)
138 : common()->DeoptimizeUnless(reason));
Ben Murdoch3b9bc312016-06-02 14:46:10 +0100139 return Changed(node);
140 }
141 Decision const decision = DecideCondition(condition);
142 if (decision == Decision::kUnknown) return NoChange();
143 if (condition_is_true == (decision == Decision::kTrue)) {
Ben Murdoch13e2dad2016-09-16 13:49:30 +0100144 ReplaceWithValue(node, dead(), effect, control);
145 } else {
Ben Murdochf91f0612016-11-29 16:50:11 +0000146 control =
147 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager, reason),
148 frame_state, effect, control);
Ben Murdoch13e2dad2016-09-16 13:49:30 +0100149 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
150 NodeProperties::MergeControlToEnd(graph(), common(), control);
151 Revisit(graph()->end());
Ben Murdoch3b9bc312016-06-02 14:46:10 +0100152 }
Ben Murdoch3b9bc312016-06-02 14:46:10 +0100153 return Replace(dead());
154}
Ben Murdoch014dc512016-03-22 12:00:34 +0000155
156Reduction CommonOperatorReducer::ReduceMerge(Node* node) {
157 DCHECK_EQ(IrOpcode::kMerge, node->opcode());
158 //
159 // Check if this is a merge that belongs to an unused diamond, which means
160 // that:
161 //
162 // a) the {Merge} has no {Phi} or {EffectPhi} uses, and
163 // b) the {Merge} has two inputs, one {IfTrue} and one {IfFalse}, which are
164 // both owned by the Merge, and
165 // c) and the {IfTrue} and {IfFalse} nodes point to the same {Branch}.
166 //
167 if (node->InputCount() == 2) {
168 for (Node* const use : node->uses()) {
169 if (IrOpcode::IsPhiOpcode(use->opcode())) return NoChange();
170 }
171 Node* if_true = node->InputAt(0);
172 Node* if_false = node->InputAt(1);
173 if (if_true->opcode() != IrOpcode::kIfTrue) std::swap(if_true, if_false);
174 if (if_true->opcode() == IrOpcode::kIfTrue &&
175 if_false->opcode() == IrOpcode::kIfFalse &&
176 if_true->InputAt(0) == if_false->InputAt(0) && if_true->OwnedBy(node) &&
177 if_false->OwnedBy(node)) {
178 Node* const branch = if_true->InputAt(0);
179 DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
180 DCHECK(branch->OwnedBy(if_true, if_false));
181 Node* const control = branch->InputAt(1);
182 // Mark the {branch} as {Dead}.
183 branch->TrimInputCount(0);
184 NodeProperties::ChangeOp(branch, common()->Dead());
185 return Replace(control);
186 }
187 }
188 return NoChange();
189}
190
191
192Reduction CommonOperatorReducer::ReduceEffectPhi(Node* node) {
193 DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
194 int const input_count = node->InputCount() - 1;
195 DCHECK_LE(1, input_count);
196 Node* const merge = node->InputAt(input_count);
197 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
198 DCHECK_EQ(input_count, merge->InputCount());
199 Node* const effect = node->InputAt(0);
200 DCHECK_NE(node, effect);
201 for (int i = 1; i < input_count; ++i) {
202 Node* const input = node->InputAt(i);
203 if (input == node) {
204 // Ignore redundant inputs.
205 DCHECK_EQ(IrOpcode::kLoop, merge->opcode());
206 continue;
207 }
208 if (input != effect) return NoChange();
209 }
210 // We might now be able to further reduce the {merge} node.
211 Revisit(merge);
212 return Replace(effect);
213}
214
215
216Reduction CommonOperatorReducer::ReducePhi(Node* node) {
217 DCHECK_EQ(IrOpcode::kPhi, node->opcode());
218 int const input_count = node->InputCount() - 1;
219 DCHECK_LE(1, input_count);
220 Node* const merge = node->InputAt(input_count);
221 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
222 DCHECK_EQ(input_count, merge->InputCount());
223 if (input_count == 2) {
224 Node* vtrue = node->InputAt(0);
225 Node* vfalse = node->InputAt(1);
226 Node* if_true = merge->InputAt(0);
227 Node* if_false = merge->InputAt(1);
228 if (if_true->opcode() != IrOpcode::kIfTrue) {
229 std::swap(if_true, if_false);
230 std::swap(vtrue, vfalse);
231 }
232 if (if_true->opcode() == IrOpcode::kIfTrue &&
233 if_false->opcode() == IrOpcode::kIfFalse &&
234 if_true->InputAt(0) == if_false->InputAt(0)) {
235 Node* const branch = if_true->InputAt(0);
236 // Check that the branch is not dead already.
237 if (branch->opcode() != IrOpcode::kBranch) return NoChange();
238 Node* const cond = branch->InputAt(0);
239 if (cond->opcode() == IrOpcode::kFloat32LessThan) {
240 Float32BinopMatcher mcond(cond);
241 if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
242 vfalse->opcode() == IrOpcode::kFloat32Sub) {
243 Float32BinopMatcher mvfalse(vfalse);
244 if (mvfalse.left().IsZero() && mvfalse.right().Equals(vtrue)) {
245 // We might now be able to further reduce the {merge} node.
246 Revisit(merge);
247 return Change(node, machine()->Float32Abs(), vtrue);
248 }
Emily Bernier958fae72015-03-24 16:35:39 -0400249 }
Ben Murdoch014dc512016-03-22 12:00:34 +0000250 } else if (cond->opcode() == IrOpcode::kFloat64LessThan) {
251 Float64BinopMatcher mcond(cond);
252 if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
253 vfalse->opcode() == IrOpcode::kFloat64Sub) {
254 Float64BinopMatcher mvfalse(vfalse);
255 if (mvfalse.left().IsZero() && mvfalse.right().Equals(vtrue)) {
256 // We might now be able to further reduce the {merge} node.
257 Revisit(merge);
258 return Change(node, machine()->Float64Abs(), vtrue);
259 }
260 }
Ben Murdoch014dc512016-03-22 12:00:34 +0000261 }
262 }
263 }
264 Node* const value = node->InputAt(0);
265 DCHECK_NE(node, value);
266 for (int i = 1; i < input_count; ++i) {
267 Node* const input = node->InputAt(i);
268 if (input == node) {
269 // Ignore redundant inputs.
270 DCHECK_EQ(IrOpcode::kLoop, merge->opcode());
271 continue;
272 }
273 if (input != value) return NoChange();
274 }
275 // We might now be able to further reduce the {merge} node.
276 Revisit(merge);
277 return Replace(value);
278}
279
280
281Reduction CommonOperatorReducer::ReduceReturn(Node* node) {
282 DCHECK_EQ(IrOpcode::kReturn, node->opcode());
283 Node* const value = node->InputAt(0);
Ben Murdochf91f0612016-11-29 16:50:11 +0000284 Node* effect = NodeProperties::GetEffectInput(node);
285 Node* const control = NodeProperties::GetControlInput(node);
286 bool changed = false;
287 if (effect->opcode() == IrOpcode::kCheckpoint) {
288 // Any {Return} node can never be used to insert a deoptimization point,
289 // hence checkpoints can be cut out of the effect chain flowing into it.
290 effect = NodeProperties::GetEffectInput(effect);
291 NodeProperties::ReplaceEffectInput(node, effect);
292 changed = true;
293 }
Ben Murdoch014dc512016-03-22 12:00:34 +0000294 if (value->opcode() == IrOpcode::kPhi &&
295 NodeProperties::GetControlInput(value) == control &&
296 effect->opcode() == IrOpcode::kEffectPhi &&
297 NodeProperties::GetControlInput(effect) == control &&
298 control->opcode() == IrOpcode::kMerge) {
299 int const control_input_count = control->InputCount();
300 DCHECK_NE(0, control_input_count);
301 DCHECK_EQ(control_input_count, value->InputCount() - 1);
302 DCHECK_EQ(control_input_count, effect->InputCount() - 1);
303 DCHECK_EQ(IrOpcode::kEnd, graph()->end()->opcode());
304 DCHECK_NE(0, graph()->end()->InputCount());
305 for (int i = 0; i < control_input_count; ++i) {
306 // Create a new {Return} and connect it to {end}. We don't need to mark
307 // {end} as revisit, because we mark {node} as {Dead} below, which was
308 // previously connected to {end}, so we know for sure that at some point
309 // the reducer logic will visit {end} again.
310 Node* ret = graph()->NewNode(common()->Return(), value->InputAt(i),
311 effect->InputAt(i), control->InputAt(i));
312 NodeProperties::MergeControlToEnd(graph(), common(), ret);
313 }
314 // Mark the merge {control} and return {node} as {dead}.
315 Replace(control, dead());
316 return Replace(dead());
317 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000318 return changed ? Changed(node) : NoChange();
Ben Murdoch014dc512016-03-22 12:00:34 +0000319}
320
321
322Reduction CommonOperatorReducer::ReduceSelect(Node* node) {
323 DCHECK_EQ(IrOpcode::kSelect, node->opcode());
324 Node* const cond = node->InputAt(0);
325 Node* const vtrue = node->InputAt(1);
326 Node* const vfalse = node->InputAt(2);
327 if (vtrue == vfalse) return Replace(vtrue);
328 switch (DecideCondition(cond)) {
329 case Decision::kTrue:
330 return Replace(vtrue);
331 case Decision::kFalse:
332 return Replace(vfalse);
333 case Decision::kUnknown:
334 break;
335 }
336 switch (cond->opcode()) {
337 case IrOpcode::kFloat32LessThan: {
338 Float32BinopMatcher mcond(cond);
339 if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
340 vfalse->opcode() == IrOpcode::kFloat32Sub) {
341 Float32BinopMatcher mvfalse(vfalse);
342 if (mvfalse.left().IsZero() && mvfalse.right().Equals(vtrue)) {
343 return Change(node, machine()->Float32Abs(), vtrue);
344 }
345 }
Emily Bernier958fae72015-03-24 16:35:39 -0400346 break;
347 }
Ben Murdoch014dc512016-03-22 12:00:34 +0000348 case IrOpcode::kFloat64LessThan: {
349 Float64BinopMatcher mcond(cond);
350 if (mcond.left().Is(0.0) && mcond.right().Equals(vtrue) &&
351 vfalse->opcode() == IrOpcode::kFloat64Sub) {
352 Float64BinopMatcher mvfalse(vfalse);
353 if (mvfalse.left().IsZero() && mvfalse.right().Equals(vtrue)) {
354 return Change(node, machine()->Float64Abs(), vtrue);
355 }
356 }
Emily Bernier958fae72015-03-24 16:35:39 -0400357 break;
358 }
359 default:
360 break;
361 }
362 return NoChange();
363}
364
Ben Murdoch014dc512016-03-22 12:00:34 +0000365
Ben Murdoch014dc512016-03-22 12:00:34 +0000366Reduction CommonOperatorReducer::Change(Node* node, Operator const* op,
367 Node* a) {
368 node->ReplaceInput(0, a);
369 node->TrimInputCount(1);
370 NodeProperties::ChangeOp(node, op);
371 return Changed(node);
372}
373
374
375Reduction CommonOperatorReducer::Change(Node* node, Operator const* op, Node* a,
376 Node* b) {
377 node->ReplaceInput(0, a);
378 node->ReplaceInput(1, b);
379 node->TrimInputCount(2);
380 NodeProperties::ChangeOp(node, op);
381 return Changed(node);
382}
383
Emily Bernier958fae72015-03-24 16:35:39 -0400384} // namespace compiler
385} // namespace internal
386} // namespace v8