Line data Source code
1 : // Copyright 2016 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/typed-optimization.h"
6 :
7 : #include "src/base/optional.h"
8 : #include "src/compiler/compilation-dependencies.h"
9 : #include "src/compiler/js-graph.h"
10 : #include "src/compiler/js-heap-broker.h"
11 : #include "src/compiler/node-matchers.h"
12 : #include "src/compiler/node-properties.h"
13 : #include "src/compiler/simplified-operator.h"
14 : #include "src/compiler/type-cache.h"
15 : #include "src/isolate-inl.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 : namespace compiler {
20 :
21 928330 : TypedOptimization::TypedOptimization(Editor* editor,
22 : CompilationDependencies* dependencies,
23 : JSGraph* jsgraph, JSHeapBroker* broker)
24 : : AdvancedReducer(editor),
25 : dependencies_(dependencies),
26 : jsgraph_(jsgraph),
27 : broker_(broker),
28 : true_type_(
29 928330 : Type::HeapConstant(broker, factory()->true_value(), graph()->zone())),
30 : false_type_(Type::HeapConstant(broker, factory()->false_value(),
31 928329 : graph()->zone())),
32 4641649 : type_cache_(TypeCache::Get()) {}
33 :
34 : TypedOptimization::~TypedOptimization() = default;
35 :
36 68793221 : Reduction TypedOptimization::Reduce(Node* node) {
37 : DisallowHeapAccess no_heap_access;
38 68793221 : switch (node->opcode()) {
39 : case IrOpcode::kConvertReceiver:
40 73834 : return ReduceConvertReceiver(node);
41 : case IrOpcode::kCheckHeapObject:
42 90654 : return ReduceCheckHeapObject(node);
43 : case IrOpcode::kCheckNotTaggedHole:
44 219 : return ReduceCheckNotTaggedHole(node);
45 : case IrOpcode::kCheckMaps:
46 194555 : return ReduceCheckMaps(node);
47 : case IrOpcode::kCheckNumber:
48 3729 : return ReduceCheckNumber(node);
49 : case IrOpcode::kCheckString:
50 30574 : return ReduceCheckString(node);
51 : case IrOpcode::kCheckEqualsInternalizedString:
52 650 : return ReduceCheckEqualsInternalizedString(node);
53 : case IrOpcode::kCheckEqualsSymbol:
54 324 : return ReduceCheckEqualsSymbol(node);
55 : case IrOpcode::kLoadField:
56 2776438 : return ReduceLoadField(node);
57 : case IrOpcode::kNumberCeil:
58 : case IrOpcode::kNumberRound:
59 : case IrOpcode::kNumberTrunc:
60 31788 : return ReduceNumberRoundop(node);
61 : case IrOpcode::kNumberFloor:
62 58069 : return ReduceNumberFloor(node);
63 : case IrOpcode::kNumberSilenceNaN:
64 1808 : return ReduceNumberSilenceNaN(node);
65 : case IrOpcode::kNumberToUint8Clamped:
66 957 : return ReduceNumberToUint8Clamped(node);
67 : case IrOpcode::kPhi:
68 883797 : return ReducePhi(node);
69 : case IrOpcode::kReferenceEqual:
70 510408 : return ReduceReferenceEqual(node);
71 : case IrOpcode::kStringEqual:
72 : case IrOpcode::kStringLessThan:
73 : case IrOpcode::kStringLessThanOrEqual:
74 31160 : return ReduceStringComparison(node);
75 : case IrOpcode::kStringLength:
76 86286 : return ReduceStringLength(node);
77 : case IrOpcode::kSameValue:
78 373 : return ReduceSameValue(node);
79 : case IrOpcode::kSelect:
80 30071 : return ReduceSelect(node);
81 : case IrOpcode::kTypeOf:
82 72478 : return ReduceTypeOf(node);
83 : case IrOpcode::kToBoolean:
84 142666 : return ReduceToBoolean(node);
85 : case IrOpcode::kSpeculativeToNumber:
86 117710 : return ReduceSpeculativeToNumber(node);
87 : case IrOpcode::kSpeculativeNumberAdd:
88 52030 : return ReduceSpeculativeNumberAdd(node);
89 : case IrOpcode::kSpeculativeNumberSubtract:
90 : case IrOpcode::kSpeculativeNumberMultiply:
91 : case IrOpcode::kSpeculativeNumberDivide:
92 : case IrOpcode::kSpeculativeNumberModulus:
93 90418 : return ReduceSpeculativeNumberBinop(node);
94 : case IrOpcode::kSpeculativeNumberEqual:
95 : case IrOpcode::kSpeculativeNumberLessThan:
96 : case IrOpcode::kSpeculativeNumberLessThanOrEqual:
97 243214 : return ReduceSpeculativeNumberComparison(node);
98 : default:
99 : break;
100 : }
101 : return NoChange();
102 : }
103 :
104 : namespace {
105 :
106 203703 : base::Optional<MapRef> GetStableMapFromObjectType(JSHeapBroker* broker,
107 : Type object_type) {
108 203703 : if (object_type.IsHeapConstant()) {
109 13624 : HeapObjectRef object = object_type.AsHeapConstant()->Ref();
110 13624 : MapRef object_map = object.map();
111 14602 : if (object_map.is_stable()) return object_map;
112 : }
113 202725 : return {};
114 : }
115 :
116 : } // namespace
117 :
118 73834 : Reduction TypedOptimization::ReduceConvertReceiver(Node* node) {
119 73834 : Node* const value = NodeProperties::GetValueInput(node, 0);
120 73834 : Type const value_type = NodeProperties::GetType(value);
121 73834 : Node* const global_proxy = NodeProperties::GetValueInput(node, 1);
122 73834 : if (value_type.Is(Type::Receiver())) {
123 : ReplaceWithValue(node, value);
124 : return Replace(value);
125 73825 : } else if (value_type.Is(Type::NullOrUndefined())) {
126 : ReplaceWithValue(node, global_proxy);
127 : return Replace(global_proxy);
128 : }
129 : return NoChange();
130 : }
131 :
132 90654 : Reduction TypedOptimization::ReduceCheckHeapObject(Node* node) {
133 90654 : Node* const input = NodeProperties::GetValueInput(node, 0);
134 90654 : Type const input_type = NodeProperties::GetType(input);
135 90654 : if (!input_type.Maybe(Type::SignedSmall())) {
136 : ReplaceWithValue(node, input);
137 : return Replace(input);
138 : }
139 : return NoChange();
140 : }
141 :
142 219 : Reduction TypedOptimization::ReduceCheckNotTaggedHole(Node* node) {
143 219 : Node* const input = NodeProperties::GetValueInput(node, 0);
144 219 : Type const input_type = NodeProperties::GetType(input);
145 219 : if (!input_type.Maybe(Type::Hole())) {
146 : ReplaceWithValue(node, input);
147 : return Replace(input);
148 : }
149 : return NoChange();
150 : }
151 :
152 194555 : Reduction TypedOptimization::ReduceCheckMaps(Node* node) {
153 : // The CheckMaps(o, ...map...) can be eliminated if map is stable,
154 : // o has type Constant(object) and map == object->map, and either
155 : // (1) map cannot transition further, or
156 : // (2) we can add a code dependency on the stability of map
157 : // (to guard the Constant type information).
158 194555 : Node* const object = NodeProperties::GetValueInput(node, 0);
159 194555 : Type const object_type = NodeProperties::GetType(object);
160 194555 : Node* const effect = NodeProperties::GetEffectInput(node);
161 : base::Optional<MapRef> object_map =
162 194555 : GetStableMapFromObjectType(broker(), object_type);
163 194555 : if (object_map.has_value()) {
164 957 : for (int i = 1; i < node->op()->ValueInputCount(); ++i) {
165 0 : Node* const map = NodeProperties::GetValueInput(node, i);
166 0 : Type const map_type = NodeProperties::GetType(map);
167 0 : if (map_type.IsHeapConstant() &&
168 0 : map_type.AsHeapConstant()->Ref().equals(*object_map)) {
169 0 : if (object_map->CanTransition()) {
170 0 : dependencies()->DependOnStableMap(*object_map);
171 : }
172 0 : return Replace(effect);
173 : }
174 : }
175 : }
176 : return NoChange();
177 : }
178 :
179 3729 : Reduction TypedOptimization::ReduceCheckNumber(Node* node) {
180 3729 : Node* const input = NodeProperties::GetValueInput(node, 0);
181 3729 : Type const input_type = NodeProperties::GetType(input);
182 3729 : if (input_type.Is(Type::Number())) {
183 : ReplaceWithValue(node, input);
184 : return Replace(input);
185 : }
186 : return NoChange();
187 : }
188 :
189 30574 : Reduction TypedOptimization::ReduceCheckString(Node* node) {
190 30574 : Node* const input = NodeProperties::GetValueInput(node, 0);
191 30574 : Type const input_type = NodeProperties::GetType(input);
192 30574 : if (input_type.Is(Type::String())) {
193 : ReplaceWithValue(node, input);
194 : return Replace(input);
195 : }
196 : return NoChange();
197 : }
198 :
199 650 : Reduction TypedOptimization::ReduceCheckEqualsInternalizedString(Node* node) {
200 650 : Node* const exp = NodeProperties::GetValueInput(node, 0);
201 : Type const exp_type = NodeProperties::GetType(exp);
202 650 : Node* const val = NodeProperties::GetValueInput(node, 1);
203 650 : Type const val_type = NodeProperties::GetType(val);
204 650 : Node* const effect = NodeProperties::GetEffectInput(node);
205 650 : if (val_type.Is(exp_type)) return Replace(effect);
206 : // TODO(turbofan): Should we also try to optimize the
207 : // non-internalized String case for {val} here?
208 : return NoChange();
209 : }
210 :
211 324 : Reduction TypedOptimization::ReduceCheckEqualsSymbol(Node* node) {
212 324 : Node* const exp = NodeProperties::GetValueInput(node, 0);
213 : Type const exp_type = NodeProperties::GetType(exp);
214 324 : Node* const val = NodeProperties::GetValueInput(node, 1);
215 324 : Type const val_type = NodeProperties::GetType(val);
216 324 : Node* const effect = NodeProperties::GetEffectInput(node);
217 324 : if (val_type.Is(exp_type)) return Replace(effect);
218 : return NoChange();
219 : }
220 :
221 2776437 : Reduction TypedOptimization::ReduceLoadField(Node* node) {
222 2776437 : Node* const object = NodeProperties::GetValueInput(node, 0);
223 2776437 : Type const object_type = NodeProperties::GetType(object);
224 2776437 : FieldAccess const& access = FieldAccessOf(node->op());
225 2776437 : if (access.base_is_tagged == kTaggedBase &&
226 : access.offset == HeapObject::kMapOffset) {
227 : // We can replace LoadField[Map](o) with map if is stable, and
228 : // o has type Constant(object) and map == object->map, and either
229 : // (1) map cannot transition further, or
230 : // (2) deoptimization is enabled and we can add a code dependency on the
231 : // stability of map (to guard the Constant type information).
232 : base::Optional<MapRef> object_map =
233 9148 : GetStableMapFromObjectType(broker(), object_type);
234 9148 : if (object_map.has_value()) {
235 21 : dependencies()->DependOnStableMap(*object_map);
236 21 : Node* const value = jsgraph()->Constant(*object_map);
237 : ReplaceWithValue(node, value);
238 21 : return Replace(value);
239 : }
240 : }
241 : return NoChange();
242 : }
243 :
244 58069 : Reduction TypedOptimization::ReduceNumberFloor(Node* node) {
245 58069 : Node* const input = NodeProperties::GetValueInput(node, 0);
246 58069 : Type const input_type = NodeProperties::GetType(input);
247 116138 : if (input_type.Is(type_cache_->kIntegerOrMinusZeroOrNaN)) {
248 : return Replace(input);
249 : }
250 117542 : if (input_type.Is(Type::PlainNumber()) &&
251 1441 : (input->opcode() == IrOpcode::kNumberDivide ||
252 : input->opcode() == IrOpcode::kSpeculativeNumberDivide)) {
253 1366 : Node* const lhs = NodeProperties::GetValueInput(input, 0);
254 1366 : Type const lhs_type = NodeProperties::GetType(lhs);
255 1366 : Node* const rhs = NodeProperties::GetValueInput(input, 1);
256 1366 : Type const rhs_type = NodeProperties::GetType(rhs);
257 1542 : if (lhs_type.Is(Type::Unsigned32()) && rhs_type.Is(Type::Unsigned32())) {
258 : // We can replace
259 : //
260 : // NumberFloor(NumberDivide(lhs: unsigned32,
261 : // rhs: unsigned32)): plain-number
262 : //
263 : // with
264 : //
265 : // NumberToUint32(NumberDivide(lhs, rhs))
266 : //
267 : // and just smash the type [0...lhs.Max] on the {node},
268 : // as the truncated result must be loewr than {lhs}'s maximum
269 : // value (note that {rhs} cannot be less than 1 due to the
270 : // plain-number type constraint on the {node}).
271 176 : NodeProperties::ChangeOp(node, simplified()->NumberToUint32());
272 176 : NodeProperties::SetType(node,
273 : Type::Range(0, lhs_type.Max(), graph()->zone()));
274 176 : return Changed(node);
275 : }
276 : }
277 : return NoChange();
278 : }
279 :
280 31788 : Reduction TypedOptimization::ReduceNumberRoundop(Node* node) {
281 31788 : Node* const input = NodeProperties::GetValueInput(node, 0);
282 31788 : Type const input_type = NodeProperties::GetType(input);
283 63576 : if (input_type.Is(type_cache_->kIntegerOrMinusZeroOrNaN)) {
284 : return Replace(input);
285 : }
286 : return NoChange();
287 : }
288 :
289 1808 : Reduction TypedOptimization::ReduceNumberSilenceNaN(Node* node) {
290 1808 : Node* const input = NodeProperties::GetValueInput(node, 0);
291 1808 : Type const input_type = NodeProperties::GetType(input);
292 1808 : if (input_type.Is(Type::OrderedNumber())) {
293 : return Replace(input);
294 : }
295 : return NoChange();
296 : }
297 :
298 957 : Reduction TypedOptimization::ReduceNumberToUint8Clamped(Node* node) {
299 957 : Node* const input = NodeProperties::GetValueInput(node, 0);
300 957 : Type const input_type = NodeProperties::GetType(input);
301 1914 : if (input_type.Is(type_cache_->kUint8)) {
302 : return Replace(input);
303 : }
304 : return NoChange();
305 : }
306 :
307 883830 : Reduction TypedOptimization::ReducePhi(Node* node) {
308 : // Try to narrow the type of the Phi {node}, which might be more precise now
309 : // after lowering based on types, i.e. a SpeculativeNumberAdd has a more
310 : // precise type than the JSAdd that was in the graph when the Typer was run.
311 : DCHECK_EQ(IrOpcode::kPhi, node->opcode());
312 : int arity = node->op()->ValueInputCount();
313 : Type type = NodeProperties::GetType(node->InputAt(0));
314 9725404 : for (int i = 1; i < arity; ++i) {
315 : type = Type::Union(type, NodeProperties::GetType(node->InputAt(i)),
316 4420716 : graph()->zone());
317 : }
318 883901 : Type const node_type = NodeProperties::GetType(node);
319 883900 : if (!node_type.Is(type)) {
320 65040 : type = Type::Intersect(node_type, type, graph()->zone());
321 : NodeProperties::SetType(node, type);
322 : return Changed(node);
323 : }
324 : return NoChange();
325 : }
326 :
327 510408 : Reduction TypedOptimization::ReduceReferenceEqual(Node* node) {
328 : DCHECK_EQ(IrOpcode::kReferenceEqual, node->opcode());
329 510408 : Node* const lhs = NodeProperties::GetValueInput(node, 0);
330 510408 : Node* const rhs = NodeProperties::GetValueInput(node, 1);
331 510408 : Type const lhs_type = NodeProperties::GetType(lhs);
332 510408 : Type const rhs_type = NodeProperties::GetType(rhs);
333 510408 : if (!lhs_type.Maybe(rhs_type)) {
334 50275 : Node* replacement = jsgraph()->FalseConstant();
335 : // Make sure we do not widen the type.
336 100550 : if (NodeProperties::GetType(replacement)
337 : .Is(NodeProperties::GetType(node))) {
338 50275 : return Replace(jsgraph()->FalseConstant());
339 : }
340 : }
341 : return NoChange();
342 : }
343 :
344 171 : const Operator* TypedOptimization::NumberComparisonFor(const Operator* op) {
345 171 : switch (op->opcode()) {
346 : case IrOpcode::kStringEqual:
347 75 : return simplified()->NumberEqual();
348 : case IrOpcode::kStringLessThan:
349 48 : return simplified()->NumberLessThan();
350 : case IrOpcode::kStringLessThanOrEqual:
351 48 : return simplified()->NumberLessThanOrEqual();
352 : default:
353 : break;
354 : }
355 0 : UNREACHABLE();
356 : }
357 :
358 149 : Reduction TypedOptimization::
359 : TryReduceStringComparisonOfStringFromSingleCharCodeToConstant(
360 : Node* comparison, const StringRef& string, bool inverted) {
361 149 : switch (comparison->opcode()) {
362 : case IrOpcode::kStringEqual:
363 53 : if (string.length() != 1) {
364 : // String.fromCharCode(x) always has length 1.
365 : return Replace(jsgraph()->BooleanConstant(false));
366 : }
367 : break;
368 : case IrOpcode::kStringLessThan:
369 : V8_FALLTHROUGH;
370 : case IrOpcode::kStringLessThanOrEqual:
371 96 : if (string.length() == 0) {
372 : // String.fromCharCode(x) <= "" is always false,
373 : // "" < String.fromCharCode(x) is always true.
374 : return Replace(jsgraph()->BooleanConstant(inverted));
375 : }
376 : break;
377 : default:
378 0 : UNREACHABLE();
379 : }
380 : return NoChange();
381 : }
382 :
383 : // Try to reduces a string comparison of the form
384 : // String.fromCharCode(x) {comparison} {constant} if inverted is false,
385 : // and {constant} {comparison} String.fromCharCode(x) if inverted is true.
386 : Reduction
387 149 : TypedOptimization::TryReduceStringComparisonOfStringFromSingleCharCode(
388 : Node* comparison, Node* from_char_code, Type constant_type, bool inverted) {
389 : DCHECK_EQ(IrOpcode::kStringFromSingleCharCode, from_char_code->opcode());
390 :
391 149 : if (!constant_type.IsHeapConstant()) return NoChange();
392 149 : ObjectRef constant = constant_type.AsHeapConstant()->Ref();
393 :
394 149 : if (!constant.IsString()) return NoChange();
395 149 : StringRef string = constant.AsString();
396 :
397 : // Check if comparison can be resolved statically.
398 : Reduction red = TryReduceStringComparisonOfStringFromSingleCharCodeToConstant(
399 149 : comparison, string, inverted);
400 149 : if (red.Changed()) return red;
401 :
402 101 : const Operator* comparison_op = NumberComparisonFor(comparison->op());
403 101 : Node* from_char_code_repl = NodeProperties::GetValueInput(from_char_code, 0);
404 101 : Type from_char_code_repl_type = NodeProperties::GetType(from_char_code_repl);
405 202 : if (!from_char_code_repl_type.Is(type_cache_->kUint16)) {
406 : // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
407 : from_char_code_repl =
408 7 : graph()->NewNode(simplified()->NumberToInt32(), from_char_code_repl);
409 7 : from_char_code_repl = graph()->NewNode(
410 : simplified()->NumberBitwiseAnd(), from_char_code_repl,
411 : jsgraph()->Constant(std::numeric_limits<uint16_t>::max()));
412 : }
413 101 : Node* constant_repl = jsgraph()->Constant(string.GetFirstChar());
414 :
415 : Node* number_comparison = nullptr;
416 101 : if (inverted) {
417 : // "x..." <= String.fromCharCode(z) is true if x < z.
418 48 : if (string.length() > 1 &&
419 : comparison->opcode() == IrOpcode::kStringLessThanOrEqual) {
420 8 : comparison_op = simplified()->NumberLessThan();
421 : }
422 : number_comparison =
423 : graph()->NewNode(comparison_op, constant_repl, from_char_code_repl);
424 : } else {
425 : // String.fromCharCode(z) < "x..." is true if z <= x.
426 85 : if (string.length() > 1 &&
427 : comparison->opcode() == IrOpcode::kStringLessThan) {
428 8 : comparison_op = simplified()->NumberLessThanOrEqual();
429 : }
430 : number_comparison =
431 : graph()->NewNode(comparison_op, from_char_code_repl, constant_repl);
432 : }
433 : ReplaceWithValue(comparison, number_comparison);
434 : return Replace(number_comparison);
435 : }
436 :
437 31160 : Reduction TypedOptimization::ReduceStringComparison(Node* node) {
438 : DCHECK(IrOpcode::kStringEqual == node->opcode() ||
439 : IrOpcode::kStringLessThan == node->opcode() ||
440 : IrOpcode::kStringLessThanOrEqual == node->opcode());
441 31160 : Node* const lhs = NodeProperties::GetValueInput(node, 0);
442 31160 : Node* const rhs = NodeProperties::GetValueInput(node, 1);
443 31160 : Type lhs_type = NodeProperties::GetType(lhs);
444 31160 : Type rhs_type = NodeProperties::GetType(rhs);
445 31160 : if (lhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
446 171 : if (rhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
447 70 : Node* left = NodeProperties::GetValueInput(lhs, 0);
448 70 : Node* right = NodeProperties::GetValueInput(rhs, 0);
449 70 : Type left_type = NodeProperties::GetType(left);
450 70 : Type right_type = NodeProperties::GetType(right);
451 140 : if (!left_type.Is(type_cache_->kUint16)) {
452 : // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
453 7 : left = graph()->NewNode(simplified()->NumberToInt32(), left);
454 7 : left = graph()->NewNode(
455 : simplified()->NumberBitwiseAnd(), left,
456 : jsgraph()->Constant(std::numeric_limits<uint16_t>::max()));
457 : }
458 140 : if (!right_type.Is(type_cache_->kUint16)) {
459 : // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
460 0 : right = graph()->NewNode(simplified()->NumberToInt32(), right);
461 0 : right = graph()->NewNode(
462 : simplified()->NumberBitwiseAnd(), right,
463 : jsgraph()->Constant(std::numeric_limits<uint16_t>::max()));
464 : }
465 : Node* equal =
466 70 : graph()->NewNode(NumberComparisonFor(node->op()), left, right);
467 : ReplaceWithValue(node, equal);
468 : return Replace(equal);
469 : } else {
470 : return TryReduceStringComparisonOfStringFromSingleCharCode(
471 101 : node, lhs, rhs_type, false);
472 : }
473 30989 : } else if (rhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
474 : return TryReduceStringComparisonOfStringFromSingleCharCode(node, rhs,
475 48 : lhs_type, true);
476 : }
477 : return NoChange();
478 : }
479 :
480 86286 : Reduction TypedOptimization::ReduceStringLength(Node* node) {
481 : DCHECK_EQ(IrOpcode::kStringLength, node->opcode());
482 86286 : Node* const input = NodeProperties::GetValueInput(node, 0);
483 86286 : switch (input->opcode()) {
484 : case IrOpcode::kHeapConstant: {
485 : // Constant-fold the String::length of the {input}.
486 : HeapObjectMatcher m(input);
487 19998 : if (m.Ref(broker()).IsString()) {
488 19998 : uint32_t const length = m.Ref(broker()).AsString().length();
489 19998 : Node* value = jsgraph()->Constant(length);
490 : return Replace(value);
491 : }
492 : break;
493 : }
494 : case IrOpcode::kStringConcat: {
495 : // The first value input to the {input} is the resulting length.
496 : return Replace(input->InputAt(0));
497 : }
498 : default:
499 : break;
500 : }
501 : return NoChange();
502 : }
503 :
504 373 : Reduction TypedOptimization::ReduceSameValue(Node* node) {
505 : DCHECK_EQ(IrOpcode::kSameValue, node->opcode());
506 373 : Node* const lhs = NodeProperties::GetValueInput(node, 0);
507 373 : Node* const rhs = NodeProperties::GetValueInput(node, 1);
508 373 : Type const lhs_type = NodeProperties::GetType(lhs);
509 373 : Type const rhs_type = NodeProperties::GetType(rhs);
510 373 : if (lhs == rhs) {
511 : // SameValue(x,x) => #true
512 8 : return Replace(jsgraph()->TrueConstant());
513 373 : } else if (lhs_type.Is(Type::Unique()) && rhs_type.Is(Type::Unique())) {
514 : // SameValue(x:unique,y:unique) => ReferenceEqual(x,y)
515 8 : NodeProperties::ChangeOp(node, simplified()->ReferenceEqual());
516 : return Changed(node);
517 373 : } else if (lhs_type.Is(Type::String()) && rhs_type.Is(Type::String())) {
518 : // SameValue(x:string,y:string) => StringEqual(x,y)
519 16 : NodeProperties::ChangeOp(node, simplified()->StringEqual());
520 : return Changed(node);
521 341 : } else if (lhs_type.Is(Type::MinusZero())) {
522 : // SameValue(x:minus-zero,y) => ObjectIsMinusZero(y)
523 77 : node->RemoveInput(0);
524 77 : NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero());
525 : return Changed(node);
526 264 : } else if (rhs_type.Is(Type::MinusZero())) {
527 : // SameValue(x,y:minus-zero) => ObjectIsMinusZero(x)
528 44 : node->RemoveInput(1);
529 44 : NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero());
530 : return Changed(node);
531 220 : } else if (lhs_type.Is(Type::NaN())) {
532 : // SameValue(x:nan,y) => ObjectIsNaN(y)
533 40 : node->RemoveInput(0);
534 40 : NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN());
535 : return Changed(node);
536 180 : } else if (rhs_type.Is(Type::NaN())) {
537 : // SameValue(x,y:nan) => ObjectIsNaN(x)
538 16 : node->RemoveInput(1);
539 16 : NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN());
540 : return Changed(node);
541 188 : } else if (lhs_type.Is(Type::PlainNumber()) &&
542 : rhs_type.Is(Type::PlainNumber())) {
543 : // SameValue(x:plain-number,y:plain-number) => NumberEqual(x,y)
544 8 : NodeProperties::ChangeOp(node, simplified()->NumberEqual());
545 : return Changed(node);
546 : }
547 : return NoChange();
548 : }
549 :
550 30071 : Reduction TypedOptimization::ReduceSelect(Node* node) {
551 : DCHECK_EQ(IrOpcode::kSelect, node->opcode());
552 30071 : Node* const condition = NodeProperties::GetValueInput(node, 0);
553 30071 : Type const condition_type = NodeProperties::GetType(condition);
554 30071 : Node* const vtrue = NodeProperties::GetValueInput(node, 1);
555 30071 : Type const vtrue_type = NodeProperties::GetType(vtrue);
556 30071 : Node* const vfalse = NodeProperties::GetValueInput(node, 2);
557 30071 : Type const vfalse_type = NodeProperties::GetType(vfalse);
558 30071 : if (condition_type.Is(true_type_)) {
559 : // Select(condition:true, vtrue, vfalse) => vtrue
560 : return Replace(vtrue);
561 : }
562 29457 : if (condition_type.Is(false_type_)) {
563 : // Select(condition:false, vtrue, vfalse) => vfalse
564 : return Replace(vfalse);
565 : }
566 37267 : if (vtrue_type.Is(true_type_) && vfalse_type.Is(false_type_)) {
567 : // Select(condition, vtrue:true, vfalse:false) => condition
568 : return Replace(condition);
569 : }
570 26675 : if (vtrue_type.Is(false_type_) && vfalse_type.Is(true_type_)) {
571 : // Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition)
572 0 : node->TrimInputCount(1);
573 0 : NodeProperties::ChangeOp(node, simplified()->BooleanNot());
574 : return Changed(node);
575 : }
576 : // Try to narrow the type of the Select {node}, which might be more precise
577 : // now after lowering based on types.
578 24706 : Type type = Type::Union(vtrue_type, vfalse_type, graph()->zone());
579 24706 : Type const node_type = NodeProperties::GetType(node);
580 24706 : if (!node_type.Is(type)) {
581 1031 : type = Type::Intersect(node_type, type, graph()->zone());
582 : NodeProperties::SetType(node, type);
583 : return Changed(node);
584 : }
585 : return NoChange();
586 : }
587 :
588 117710 : Reduction TypedOptimization::ReduceSpeculativeToNumber(Node* node) {
589 : DCHECK_EQ(IrOpcode::kSpeculativeToNumber, node->opcode());
590 117710 : Node* const input = NodeProperties::GetValueInput(node, 0);
591 117710 : Type const input_type = NodeProperties::GetType(input);
592 117710 : if (input_type.Is(Type::Number())) {
593 : // SpeculativeToNumber(x:number) => x
594 : ReplaceWithValue(node, input);
595 : return Replace(input);
596 : }
597 : return NoChange();
598 : }
599 :
600 72478 : Reduction TypedOptimization::ReduceTypeOf(Node* node) {
601 : Node* const input = node->InputAt(0);
602 72478 : Type const type = NodeProperties::GetType(input);
603 : Factory* const f = factory();
604 72478 : if (type.Is(Type::Boolean())) {
605 : return Replace(
606 4160 : jsgraph()->Constant(ObjectRef(broker(), f->boolean_string())));
607 68318 : } else if (type.Is(Type::Number())) {
608 : return Replace(
609 11324 : jsgraph()->Constant(ObjectRef(broker(), f->number_string())));
610 56994 : } else if (type.Is(Type::String())) {
611 : return Replace(
612 1399 : jsgraph()->Constant(ObjectRef(broker(), f->string_string())));
613 55595 : } else if (type.Is(Type::BigInt())) {
614 : return Replace(
615 12 : jsgraph()->Constant(ObjectRef(broker(), f->bigint_string())));
616 55583 : } else if (type.Is(Type::Symbol())) {
617 : return Replace(
618 0 : jsgraph()->Constant(ObjectRef(broker(), f->symbol_string())));
619 55583 : } else if (type.Is(Type::OtherUndetectableOrUndefined())) {
620 : return Replace(
621 239 : jsgraph()->Constant(ObjectRef(broker(), f->undefined_string())));
622 55344 : } else if (type.Is(Type::NonCallableOrNull())) {
623 : return Replace(
624 1932 : jsgraph()->Constant(ObjectRef(broker(), f->object_string())));
625 53412 : } else if (type.Is(Type::Function())) {
626 : return Replace(
627 151 : jsgraph()->Constant(ObjectRef(broker(), f->function_string())));
628 : }
629 : return NoChange();
630 : }
631 :
632 142665 : Reduction TypedOptimization::ReduceToBoolean(Node* node) {
633 : Node* const input = node->InputAt(0);
634 142665 : Type const input_type = NodeProperties::GetType(input);
635 142665 : if (input_type.Is(Type::Boolean())) {
636 : // ToBoolean(x:boolean) => x
637 : return Replace(input);
638 123462 : } else if (input_type.Is(Type::OrderedNumber())) {
639 : // SToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x,#0))
640 2411 : node->ReplaceInput(0, graph()->NewNode(simplified()->NumberEqual(), input,
641 2411 : jsgraph()->ZeroConstant()));
642 2411 : node->TrimInputCount(1);
643 2411 : NodeProperties::ChangeOp(node, simplified()->BooleanNot());
644 : return Changed(node);
645 121051 : } else if (input_type.Is(Type::Number())) {
646 : // ToBoolean(x:number) => NumberToBoolean(x)
647 111 : node->TrimInputCount(1);
648 111 : NodeProperties::ChangeOp(node, simplified()->NumberToBoolean());
649 : return Changed(node);
650 120940 : } else if (input_type.Is(Type::DetectableReceiverOrNull())) {
651 : // ToBoolean(x:detectable receiver \/ null)
652 : // => BooleanNot(ReferenceEqual(x,#null))
653 20 : node->ReplaceInput(0, graph()->NewNode(simplified()->ReferenceEqual(),
654 20 : input, jsgraph()->NullConstant()));
655 20 : node->TrimInputCount(1);
656 20 : NodeProperties::ChangeOp(node, simplified()->BooleanNot());
657 : return Changed(node);
658 120920 : } else if (input_type.Is(Type::ReceiverOrNullOrUndefined())) {
659 : // ToBoolean(x:receiver \/ null \/ undefined)
660 : // => BooleanNot(ObjectIsUndetectable(x))
661 62 : node->ReplaceInput(
662 62 : 0, graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
663 62 : node->TrimInputCount(1);
664 62 : NodeProperties::ChangeOp(node, simplified()->BooleanNot());
665 : return Changed(node);
666 120858 : } else if (input_type.Is(Type::String())) {
667 : // ToBoolean(x:string) => BooleanNot(ReferenceEqual(x,""))
668 17 : node->ReplaceInput(0,
669 : graph()->NewNode(simplified()->ReferenceEqual(), input,
670 17 : jsgraph()->EmptyStringConstant()));
671 17 : node->TrimInputCount(1);
672 17 : NodeProperties::ChangeOp(node, simplified()->BooleanNot());
673 : return Changed(node);
674 : }
675 : return NoChange();
676 : }
677 :
678 : namespace {
679 1490169 : bool BothAre(Type t1, Type t2, Type t3) { return t1.Is(t3) && t2.Is(t3); }
680 :
681 49617 : bool NeitherCanBe(Type t1, Type t2, Type t3) {
682 49617 : return !t1.Maybe(t3) && !t2.Maybe(t3);
683 : }
684 :
685 27164 : const Operator* NumberOpFromSpeculativeNumberOp(
686 : SimplifiedOperatorBuilder* simplified, const Operator* op) {
687 27164 : switch (op->opcode()) {
688 : case IrOpcode::kSpeculativeNumberEqual:
689 5649 : return simplified->NumberEqual();
690 : case IrOpcode::kSpeculativeNumberLessThan:
691 9574 : return simplified->NumberLessThan();
692 : case IrOpcode::kSpeculativeNumberLessThanOrEqual:
693 301 : return simplified->NumberLessThanOrEqual();
694 : case IrOpcode::kSpeculativeNumberAdd:
695 : // Handled by ReduceSpeculativeNumberAdd.
696 0 : UNREACHABLE();
697 : case IrOpcode::kSpeculativeNumberSubtract:
698 694 : return simplified->NumberSubtract();
699 : case IrOpcode::kSpeculativeNumberMultiply:
700 7414 : return simplified->NumberMultiply();
701 : case IrOpcode::kSpeculativeNumberDivide:
702 3150 : return simplified->NumberDivide();
703 : case IrOpcode::kSpeculativeNumberModulus:
704 382 : return simplified->NumberModulus();
705 : default:
706 : break;
707 : }
708 0 : UNREACHABLE();
709 : }
710 :
711 : } // namespace
712 :
713 52030 : Reduction TypedOptimization::ReduceSpeculativeNumberAdd(Node* node) {
714 52030 : Node* const lhs = NodeProperties::GetValueInput(node, 0);
715 52030 : Node* const rhs = NodeProperties::GetValueInput(node, 1);
716 52030 : Type const lhs_type = NodeProperties::GetType(lhs);
717 52030 : Type const rhs_type = NodeProperties::GetType(rhs);
718 52030 : NumberOperationHint hint = NumberOperationHintOf(node->op());
719 104063 : if ((hint == NumberOperationHint::kNumber ||
720 52032 : hint == NumberOperationHint::kNumberOrOddball) &&
721 101648 : BothAre(lhs_type, rhs_type, Type::PlainPrimitive()) &&
722 49617 : NeitherCanBe(lhs_type, rhs_type, Type::StringOrReceiver())) {
723 : // SpeculativeNumberAdd(x:-string, y:-string) =>
724 : // NumberAdd(ToNumber(x), ToNumber(y))
725 49547 : Node* const toNum_lhs = ConvertPlainPrimitiveToNumber(lhs);
726 49547 : Node* const toNum_rhs = ConvertPlainPrimitiveToNumber(rhs);
727 : Node* const value =
728 49547 : graph()->NewNode(simplified()->NumberAdd(), toNum_lhs, toNum_rhs);
729 : ReplaceWithValue(node, value);
730 : return Replace(value);
731 : }
732 : return NoChange();
733 : }
734 :
735 122374 : Reduction TypedOptimization::ReduceJSToNumberInput(Node* input) {
736 : // Try constant-folding of JSToNumber with constant inputs.
737 122374 : Type input_type = NodeProperties::GetType(input);
738 :
739 122374 : if (input_type.Is(Type::String())) {
740 : HeapObjectMatcher m(input);
741 0 : if (m.HasValue() && m.Ref(broker()).IsString()) {
742 0 : StringRef input_value = m.Ref(broker()).AsString();
743 : double number;
744 0 : ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(number, input_value.ToNumber());
745 0 : return Replace(jsgraph()->Constant(number));
746 : }
747 : }
748 122374 : if (input_type.IsHeapConstant()) {
749 243 : HeapObjectRef input_value = input_type.AsHeapConstant()->Ref();
750 : double value;
751 486 : if (input_value.OddballToNumber().To(&value)) {
752 243 : return Replace(jsgraph()->Constant(value));
753 : }
754 : }
755 122130 : if (input_type.Is(Type::Number())) {
756 : // JSToNumber(x:number) => x
757 : return Changed(input);
758 : }
759 290 : if (input_type.Is(Type::Undefined())) {
760 : // JSToNumber(undefined) => #NaN
761 0 : return Replace(jsgraph()->NaNConstant());
762 : }
763 290 : if (input_type.Is(Type::Null())) {
764 : // JSToNumber(null) => #0
765 0 : return Replace(jsgraph()->ZeroConstant());
766 : }
767 : return NoChange();
768 : }
769 :
770 122374 : Node* TypedOptimization::ConvertPlainPrimitiveToNumber(Node* node) {
771 : DCHECK(NodeProperties::GetType(node).Is(Type::PlainPrimitive()));
772 : // Avoid inserting too many eager ToNumber() operations.
773 122374 : Reduction const reduction = ReduceJSToNumberInput(node);
774 122373 : if (reduction.Changed()) return reduction.replacement();
775 580 : if (NodeProperties::GetType(node).Is(Type::Number())) {
776 : return node;
777 : }
778 580 : return graph()->NewNode(simplified()->PlainPrimitiveToNumber(), node);
779 : }
780 :
781 90418 : Reduction TypedOptimization::ReduceSpeculativeNumberBinop(Node* node) {
782 90418 : Node* const lhs = NodeProperties::GetValueInput(node, 0);
783 90418 : Node* const rhs = NodeProperties::GetValueInput(node, 1);
784 90418 : Type const lhs_type = NodeProperties::GetType(lhs);
785 90418 : Type const rhs_type = NodeProperties::GetType(rhs);
786 90418 : NumberOperationHint hint = NumberOperationHintOf(node->op());
787 180836 : if ((hint == NumberOperationHint::kNumber ||
788 120081 : hint == NumberOperationHint::kNumberOrOddball) &&
789 29663 : BothAre(lhs_type, rhs_type, Type::NumberOrUndefinedOrNullOrBoolean())) {
790 : // We intentionally do this only in the Number and NumberOrOddball hint case
791 : // because simplified lowering of these speculative ops may do some clever
792 : // reductions in the other cases.
793 11640 : Node* const toNum_lhs = ConvertPlainPrimitiveToNumber(lhs);
794 11640 : Node* const toNum_rhs = ConvertPlainPrimitiveToNumber(rhs);
795 11640 : Node* const value = graph()->NewNode(
796 : NumberOpFromSpeculativeNumberOp(simplified(), node->op()), toNum_lhs,
797 : toNum_rhs);
798 : ReplaceWithValue(node, value);
799 : return Replace(value);
800 : }
801 : return NoChange();
802 : }
803 :
804 243225 : Reduction TypedOptimization::ReduceSpeculativeNumberComparison(Node* node) {
805 243225 : Node* const lhs = NodeProperties::GetValueInput(node, 0);
806 243217 : Node* const rhs = NodeProperties::GetValueInput(node, 1);
807 243216 : Type const lhs_type = NodeProperties::GetType(lhs);
808 243216 : Type const rhs_type = NodeProperties::GetType(rhs);
809 471190 : if (BothAre(lhs_type, rhs_type, Type::Signed32()) ||
810 227967 : BothAre(lhs_type, rhs_type, Type::Unsigned32())) {
811 15524 : Node* const value = graph()->NewNode(
812 : NumberOpFromSpeculativeNumberOp(simplified(), node->op()), lhs, rhs);
813 : ReplaceWithValue(node, value);
814 : return Replace(value);
815 : }
816 : return NoChange();
817 : }
818 :
819 0 : Factory* TypedOptimization::factory() const {
820 0 : return jsgraph()->isolate()->factory();
821 : }
822 :
823 0 : Graph* TypedOptimization::graph() const { return jsgraph()->graph(); }
824 :
825 0 : SimplifiedOperatorBuilder* TypedOptimization::simplified() const {
826 0 : return jsgraph()->simplified();
827 : }
828 :
829 : } // namespace compiler
830 : } // namespace internal
831 122004 : } // namespace v8
|