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