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