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