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
TypedOptimization(Editor * editor,CompilationDependencies * dependencies,JSGraph * jsgraph,JSHeapBroker * js_heap_broker)21 TypedOptimization::TypedOptimization(Editor* editor,
22 CompilationDependencies* dependencies,
23 JSGraph* jsgraph,
24 JSHeapBroker* js_heap_broker)
25 : AdvancedReducer(editor),
26 dependencies_(dependencies),
27 jsgraph_(jsgraph),
28 js_heap_broker_(js_heap_broker),
29 true_type_(Type::HeapConstant(js_heap_broker, factory()->true_value(),
30 graph()->zone())),
31 false_type_(Type::HeapConstant(js_heap_broker, factory()->false_value(),
32 graph()->zone())),
33 type_cache_(TypeCache::Get()) {}
34
~TypedOptimization()35 TypedOptimization::~TypedOptimization() {}
36
Reduce(Node * node)37 Reduction TypedOptimization::Reduce(Node* node) {
38 DisallowHeapAccess no_heap_access;
39 switch (node->opcode()) {
40 case IrOpcode::kConvertReceiver:
41 return ReduceConvertReceiver(node);
42 case IrOpcode::kCheckHeapObject:
43 return ReduceCheckHeapObject(node);
44 case IrOpcode::kCheckNotTaggedHole:
45 return ReduceCheckNotTaggedHole(node);
46 case IrOpcode::kCheckMaps:
47 return ReduceCheckMaps(node);
48 case IrOpcode::kCheckNumber:
49 return ReduceCheckNumber(node);
50 case IrOpcode::kCheckString:
51 return ReduceCheckString(node);
52 case IrOpcode::kCheckEqualsInternalizedString:
53 return ReduceCheckEqualsInternalizedString(node);
54 case IrOpcode::kCheckEqualsSymbol:
55 return ReduceCheckEqualsSymbol(node);
56 case IrOpcode::kLoadField:
57 return ReduceLoadField(node);
58 case IrOpcode::kNumberCeil:
59 case IrOpcode::kNumberRound:
60 case IrOpcode::kNumberTrunc:
61 return ReduceNumberRoundop(node);
62 case IrOpcode::kNumberFloor:
63 return ReduceNumberFloor(node);
64 case IrOpcode::kNumberToUint8Clamped:
65 return ReduceNumberToUint8Clamped(node);
66 case IrOpcode::kPhi:
67 return ReducePhi(node);
68 case IrOpcode::kReferenceEqual:
69 return ReduceReferenceEqual(node);
70 case IrOpcode::kStringEqual:
71 case IrOpcode::kStringLessThan:
72 case IrOpcode::kStringLessThanOrEqual:
73 return ReduceStringComparison(node);
74 case IrOpcode::kSameValue:
75 return ReduceSameValue(node);
76 case IrOpcode::kSelect:
77 return ReduceSelect(node);
78 case IrOpcode::kTypeOf:
79 return ReduceTypeOf(node);
80 case IrOpcode::kToBoolean:
81 return ReduceToBoolean(node);
82 case IrOpcode::kSpeculativeToNumber:
83 return ReduceSpeculativeToNumber(node);
84 default:
85 break;
86 }
87 return NoChange();
88 }
89
90 namespace {
91
GetStableMapFromObjectType(JSHeapBroker * js_heap_broker,Type object_type)92 base::Optional<MapRef> GetStableMapFromObjectType(JSHeapBroker* js_heap_broker,
93 Type object_type) {
94 if (object_type.IsHeapConstant()) {
95 HeapObjectRef object = object_type.AsHeapConstant()->Ref();
96 MapRef object_map = object.map();
97 if (object_map.is_stable()) return object_map;
98 }
99 return {};
100 }
101
102 } // namespace
103
ReduceConvertReceiver(Node * node)104 Reduction TypedOptimization::ReduceConvertReceiver(Node* node) {
105 Node* const value = NodeProperties::GetValueInput(node, 0);
106 Type const value_type = NodeProperties::GetType(value);
107 Node* const global_proxy = NodeProperties::GetValueInput(node, 1);
108 if (value_type.Is(Type::Receiver())) {
109 ReplaceWithValue(node, value);
110 return Replace(value);
111 } else if (value_type.Is(Type::NullOrUndefined())) {
112 ReplaceWithValue(node, global_proxy);
113 return Replace(global_proxy);
114 }
115 return NoChange();
116 }
117
ReduceCheckHeapObject(Node * node)118 Reduction TypedOptimization::ReduceCheckHeapObject(Node* node) {
119 Node* const input = NodeProperties::GetValueInput(node, 0);
120 Type const input_type = NodeProperties::GetType(input);
121 if (!input_type.Maybe(Type::SignedSmall())) {
122 ReplaceWithValue(node, input);
123 return Replace(input);
124 }
125 return NoChange();
126 }
127
ReduceCheckNotTaggedHole(Node * node)128 Reduction TypedOptimization::ReduceCheckNotTaggedHole(Node* node) {
129 Node* const input = NodeProperties::GetValueInput(node, 0);
130 Type const input_type = NodeProperties::GetType(input);
131 if (!input_type.Maybe(Type::Hole())) {
132 ReplaceWithValue(node, input);
133 return Replace(input);
134 }
135 return NoChange();
136 }
137
ReduceCheckMaps(Node * node)138 Reduction TypedOptimization::ReduceCheckMaps(Node* node) {
139 // The CheckMaps(o, ...map...) can be eliminated if map is stable,
140 // o has type Constant(object) and map == object->map, and either
141 // (1) map cannot transition further, or
142 // (2) we can add a code dependency on the stability of map
143 // (to guard the Constant type information).
144 Node* const object = NodeProperties::GetValueInput(node, 0);
145 Type const object_type = NodeProperties::GetType(object);
146 Node* const effect = NodeProperties::GetEffectInput(node);
147 base::Optional<MapRef> object_map =
148 GetStableMapFromObjectType(js_heap_broker(), object_type);
149 if (object_map.has_value()) {
150 for (int i = 1; i < node->op()->ValueInputCount(); ++i) {
151 Node* const map = NodeProperties::GetValueInput(node, i);
152 Type const map_type = NodeProperties::GetType(map);
153 if (map_type.IsHeapConstant() &&
154 map_type.AsHeapConstant()->Ref().equals(*object_map)) {
155 if (object_map->CanTransition()) {
156 dependencies()->DependOnStableMap(*object_map);
157 }
158 return Replace(effect);
159 }
160 }
161 }
162 return NoChange();
163 }
164
ReduceCheckNumber(Node * node)165 Reduction TypedOptimization::ReduceCheckNumber(Node* node) {
166 Node* const input = NodeProperties::GetValueInput(node, 0);
167 Type const input_type = NodeProperties::GetType(input);
168 if (input_type.Is(Type::Number())) {
169 ReplaceWithValue(node, input);
170 return Replace(input);
171 }
172 return NoChange();
173 }
174
ReduceCheckString(Node * node)175 Reduction TypedOptimization::ReduceCheckString(Node* node) {
176 Node* const input = NodeProperties::GetValueInput(node, 0);
177 Type const input_type = NodeProperties::GetType(input);
178 if (input_type.Is(Type::String())) {
179 ReplaceWithValue(node, input);
180 return Replace(input);
181 }
182 return NoChange();
183 }
184
ReduceCheckEqualsInternalizedString(Node * node)185 Reduction TypedOptimization::ReduceCheckEqualsInternalizedString(Node* node) {
186 Node* const exp = NodeProperties::GetValueInput(node, 0);
187 Type const exp_type = NodeProperties::GetType(exp);
188 Node* const val = NodeProperties::GetValueInput(node, 1);
189 Type const val_type = NodeProperties::GetType(val);
190 Node* const effect = NodeProperties::GetEffectInput(node);
191 if (val_type.Is(exp_type)) return Replace(effect);
192 // TODO(turbofan): Should we also try to optimize the
193 // non-internalized String case for {val} here?
194 return NoChange();
195 }
196
ReduceCheckEqualsSymbol(Node * node)197 Reduction TypedOptimization::ReduceCheckEqualsSymbol(Node* node) {
198 Node* const exp = NodeProperties::GetValueInput(node, 0);
199 Type const exp_type = NodeProperties::GetType(exp);
200 Node* const val = NodeProperties::GetValueInput(node, 1);
201 Type const val_type = NodeProperties::GetType(val);
202 Node* const effect = NodeProperties::GetEffectInput(node);
203 if (val_type.Is(exp_type)) return Replace(effect);
204 return NoChange();
205 }
206
ReduceLoadField(Node * node)207 Reduction TypedOptimization::ReduceLoadField(Node* node) {
208 Node* const object = NodeProperties::GetValueInput(node, 0);
209 Type const object_type = NodeProperties::GetType(object);
210 FieldAccess const& access = FieldAccessOf(node->op());
211 if (access.base_is_tagged == kTaggedBase &&
212 access.offset == HeapObject::kMapOffset) {
213 // We can replace LoadField[Map](o) with map if is stable, and
214 // o has type Constant(object) and map == object->map, and either
215 // (1) map cannot transition further, or
216 // (2) deoptimization is enabled and we can add a code dependency on the
217 // stability of map (to guard the Constant type information).
218 base::Optional<MapRef> object_map =
219 GetStableMapFromObjectType(js_heap_broker(), object_type);
220 if (object_map.has_value()) {
221 dependencies()->DependOnStableMap(*object_map);
222 Node* const value = jsgraph()->Constant(*object_map);
223 ReplaceWithValue(node, value);
224 return Replace(value);
225 }
226 }
227 return NoChange();
228 }
229
ReduceNumberFloor(Node * node)230 Reduction TypedOptimization::ReduceNumberFloor(Node* node) {
231 Node* const input = NodeProperties::GetValueInput(node, 0);
232 Type const input_type = NodeProperties::GetType(input);
233 if (input_type.Is(type_cache_.kIntegerOrMinusZeroOrNaN)) {
234 return Replace(input);
235 }
236 if (input_type.Is(Type::PlainNumber()) &&
237 (input->opcode() == IrOpcode::kNumberDivide ||
238 input->opcode() == IrOpcode::kSpeculativeNumberDivide)) {
239 Node* const lhs = NodeProperties::GetValueInput(input, 0);
240 Type const lhs_type = NodeProperties::GetType(lhs);
241 Node* const rhs = NodeProperties::GetValueInput(input, 1);
242 Type const rhs_type = NodeProperties::GetType(rhs);
243 if (lhs_type.Is(Type::Unsigned32()) && rhs_type.Is(Type::Unsigned32())) {
244 // We can replace
245 //
246 // NumberFloor(NumberDivide(lhs: unsigned32,
247 // rhs: unsigned32)): plain-number
248 //
249 // with
250 //
251 // NumberToUint32(NumberDivide(lhs, rhs))
252 //
253 // and just smash the type [0...lhs.Max] on the {node},
254 // as the truncated result must be loewr than {lhs}'s maximum
255 // value (note that {rhs} cannot be less than 1 due to the
256 // plain-number type constraint on the {node}).
257 NodeProperties::ChangeOp(node, simplified()->NumberToUint32());
258 NodeProperties::SetType(node,
259 Type::Range(0, lhs_type.Max(), graph()->zone()));
260 return Changed(node);
261 }
262 }
263 return NoChange();
264 }
265
ReduceNumberRoundop(Node * node)266 Reduction TypedOptimization::ReduceNumberRoundop(Node* node) {
267 Node* const input = NodeProperties::GetValueInput(node, 0);
268 Type const input_type = NodeProperties::GetType(input);
269 if (input_type.Is(type_cache_.kIntegerOrMinusZeroOrNaN)) {
270 return Replace(input);
271 }
272 return NoChange();
273 }
274
ReduceNumberToUint8Clamped(Node * node)275 Reduction TypedOptimization::ReduceNumberToUint8Clamped(Node* node) {
276 Node* const input = NodeProperties::GetValueInput(node, 0);
277 Type const input_type = NodeProperties::GetType(input);
278 if (input_type.Is(type_cache_.kUint8)) {
279 return Replace(input);
280 }
281 return NoChange();
282 }
283
ReducePhi(Node * node)284 Reduction TypedOptimization::ReducePhi(Node* node) {
285 // Try to narrow the type of the Phi {node}, which might be more precise now
286 // after lowering based on types, i.e. a SpeculativeNumberAdd has a more
287 // precise type than the JSAdd that was in the graph when the Typer was run.
288 DCHECK_EQ(IrOpcode::kPhi, node->opcode());
289 int arity = node->op()->ValueInputCount();
290 Type type = NodeProperties::GetType(node->InputAt(0));
291 for (int i = 1; i < arity; ++i) {
292 type = Type::Union(type, NodeProperties::GetType(node->InputAt(i)),
293 graph()->zone());
294 }
295 Type const node_type = NodeProperties::GetType(node);
296 if (!node_type.Is(type)) {
297 type = Type::Intersect(node_type, type, graph()->zone());
298 NodeProperties::SetType(node, type);
299 return Changed(node);
300 }
301 return NoChange();
302 }
303
ReduceReferenceEqual(Node * node)304 Reduction TypedOptimization::ReduceReferenceEqual(Node* node) {
305 DCHECK_EQ(IrOpcode::kReferenceEqual, node->opcode());
306 Node* const lhs = NodeProperties::GetValueInput(node, 0);
307 Node* const rhs = NodeProperties::GetValueInput(node, 1);
308 Type const lhs_type = NodeProperties::GetType(lhs);
309 Type const rhs_type = NodeProperties::GetType(rhs);
310 if (!lhs_type.Maybe(rhs_type)) {
311 Node* replacement = jsgraph()->FalseConstant();
312 // Make sure we do not widen the type.
313 if (NodeProperties::GetType(replacement)
314 .Is(NodeProperties::GetType(node))) {
315 return Replace(jsgraph()->FalseConstant());
316 }
317 }
318 return NoChange();
319 }
320
NumberComparisonFor(const Operator * op)321 const Operator* TypedOptimization::NumberComparisonFor(const Operator* op) {
322 switch (op->opcode()) {
323 case IrOpcode::kStringEqual:
324 return simplified()->NumberEqual();
325 case IrOpcode::kStringLessThan:
326 return simplified()->NumberLessThan();
327 case IrOpcode::kStringLessThanOrEqual:
328 return simplified()->NumberLessThanOrEqual();
329 default:
330 break;
331 }
332 UNREACHABLE();
333 }
334
335 Reduction TypedOptimization::
TryReduceStringComparisonOfStringFromSingleCharCodeToConstant(Node * comparison,const StringRef & string,bool inverted)336 TryReduceStringComparisonOfStringFromSingleCharCodeToConstant(
337 Node* comparison, const StringRef& string, bool inverted) {
338 switch (comparison->opcode()) {
339 case IrOpcode::kStringEqual:
340 if (string.length() != 1) {
341 // String.fromCharCode(x) always has length 1.
342 return Replace(jsgraph()->BooleanConstant(false));
343 }
344 break;
345 case IrOpcode::kStringLessThan:
346 V8_FALLTHROUGH;
347 case IrOpcode::kStringLessThanOrEqual:
348 if (string.length() == 0) {
349 // String.fromCharCode(x) <= "" is always false,
350 // "" < String.fromCharCode(x) is always true.
351 return Replace(jsgraph()->BooleanConstant(inverted));
352 }
353 break;
354 default:
355 UNREACHABLE();
356 }
357 return NoChange();
358 }
359
360 // Try to reduces a string comparison of the form
361 // String.fromCharCode(x) {comparison} {constant} if inverted is false,
362 // and {constant} {comparison} String.fromCharCode(x) if inverted is true.
363 Reduction
TryReduceStringComparisonOfStringFromSingleCharCode(Node * comparison,Node * from_char_code,Type constant_type,bool inverted)364 TypedOptimization::TryReduceStringComparisonOfStringFromSingleCharCode(
365 Node* comparison, Node* from_char_code, Type constant_type, bool inverted) {
366 DCHECK_EQ(IrOpcode::kStringFromSingleCharCode, from_char_code->opcode());
367
368 if (!constant_type.IsHeapConstant()) return NoChange();
369 ObjectRef constant = constant_type.AsHeapConstant()->Ref();
370
371 if (!constant.IsString()) return NoChange();
372 StringRef string = constant.AsString();
373
374 // Check if comparison can be resolved statically.
375 Reduction red = TryReduceStringComparisonOfStringFromSingleCharCodeToConstant(
376 comparison, string, inverted);
377 if (red.Changed()) return red;
378
379 const Operator* comparison_op = NumberComparisonFor(comparison->op());
380 Node* from_char_code_repl = NodeProperties::GetValueInput(from_char_code, 0);
381 Type from_char_code_repl_type = NodeProperties::GetType(from_char_code_repl);
382 if (!from_char_code_repl_type.Is(type_cache_.kUint16)) {
383 // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
384 from_char_code_repl =
385 graph()->NewNode(simplified()->NumberToInt32(), from_char_code_repl);
386 from_char_code_repl = graph()->NewNode(
387 simplified()->NumberBitwiseAnd(), from_char_code_repl,
388 jsgraph()->Constant(std::numeric_limits<uint16_t>::max()));
389 }
390 Node* constant_repl = jsgraph()->Constant(string.GetFirstChar());
391
392 Node* number_comparison = nullptr;
393 if (inverted) {
394 // "x..." <= String.fromCharCode(z) is true if x < z.
395 if (string.length() > 1 &&
396 comparison->opcode() == IrOpcode::kStringLessThanOrEqual) {
397 comparison_op = simplified()->NumberLessThan();
398 }
399 number_comparison =
400 graph()->NewNode(comparison_op, constant_repl, from_char_code_repl);
401 } else {
402 // String.fromCharCode(z) < "x..." is true if z <= x.
403 if (string.length() > 1 &&
404 comparison->opcode() == IrOpcode::kStringLessThan) {
405 comparison_op = simplified()->NumberLessThanOrEqual();
406 }
407 number_comparison =
408 graph()->NewNode(comparison_op, from_char_code_repl, constant_repl);
409 }
410 ReplaceWithValue(comparison, number_comparison);
411 return Replace(number_comparison);
412 }
413
ReduceStringComparison(Node * node)414 Reduction TypedOptimization::ReduceStringComparison(Node* node) {
415 DCHECK(IrOpcode::kStringEqual == node->opcode() ||
416 IrOpcode::kStringLessThan == node->opcode() ||
417 IrOpcode::kStringLessThanOrEqual == node->opcode());
418 Node* const lhs = NodeProperties::GetValueInput(node, 0);
419 Node* const rhs = NodeProperties::GetValueInput(node, 1);
420 Type lhs_type = NodeProperties::GetType(lhs);
421 Type rhs_type = NodeProperties::GetType(rhs);
422 if (lhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
423 if (rhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
424 Node* left = NodeProperties::GetValueInput(lhs, 0);
425 Node* right = NodeProperties::GetValueInput(rhs, 0);
426 Type left_type = NodeProperties::GetType(left);
427 Type right_type = NodeProperties::GetType(right);
428 if (!left_type.Is(type_cache_.kUint16)) {
429 // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
430 left = graph()->NewNode(simplified()->NumberToInt32(), left);
431 left = graph()->NewNode(
432 simplified()->NumberBitwiseAnd(), left,
433 jsgraph()->Constant(std::numeric_limits<uint16_t>::max()));
434 }
435 if (!right_type.Is(type_cache_.kUint16)) {
436 // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
437 right = graph()->NewNode(simplified()->NumberToInt32(), right);
438 right = graph()->NewNode(
439 simplified()->NumberBitwiseAnd(), right,
440 jsgraph()->Constant(std::numeric_limits<uint16_t>::max()));
441 }
442 Node* equal =
443 graph()->NewNode(NumberComparisonFor(node->op()), left, right);
444 ReplaceWithValue(node, equal);
445 return Replace(equal);
446 } else {
447 return TryReduceStringComparisonOfStringFromSingleCharCode(
448 node, lhs, rhs_type, false);
449 }
450 } else if (rhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
451 return TryReduceStringComparisonOfStringFromSingleCharCode(node, rhs,
452 lhs_type, true);
453 }
454 return NoChange();
455 }
456
ReduceSameValue(Node * node)457 Reduction TypedOptimization::ReduceSameValue(Node* node) {
458 DCHECK_EQ(IrOpcode::kSameValue, node->opcode());
459 Node* const lhs = NodeProperties::GetValueInput(node, 0);
460 Node* const rhs = NodeProperties::GetValueInput(node, 1);
461 Type const lhs_type = NodeProperties::GetType(lhs);
462 Type const rhs_type = NodeProperties::GetType(rhs);
463 if (lhs == rhs) {
464 // SameValue(x,x) => #true
465 return Replace(jsgraph()->TrueConstant());
466 } else if (lhs_type.Is(Type::Unique()) && rhs_type.Is(Type::Unique())) {
467 // SameValue(x:unique,y:unique) => ReferenceEqual(x,y)
468 NodeProperties::ChangeOp(node, simplified()->ReferenceEqual());
469 return Changed(node);
470 } else if (lhs_type.Is(Type::String()) && rhs_type.Is(Type::String())) {
471 // SameValue(x:string,y:string) => StringEqual(x,y)
472 NodeProperties::ChangeOp(node, simplified()->StringEqual());
473 return Changed(node);
474 } else if (lhs_type.Is(Type::MinusZero())) {
475 // SameValue(x:minus-zero,y) => ObjectIsMinusZero(y)
476 node->RemoveInput(0);
477 NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero());
478 return Changed(node);
479 } else if (rhs_type.Is(Type::MinusZero())) {
480 // SameValue(x,y:minus-zero) => ObjectIsMinusZero(x)
481 node->RemoveInput(1);
482 NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero());
483 return Changed(node);
484 } else if (lhs_type.Is(Type::NaN())) {
485 // SameValue(x:nan,y) => ObjectIsNaN(y)
486 node->RemoveInput(0);
487 NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN());
488 return Changed(node);
489 } else if (rhs_type.Is(Type::NaN())) {
490 // SameValue(x,y:nan) => ObjectIsNaN(x)
491 node->RemoveInput(1);
492 NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN());
493 return Changed(node);
494 } else if (lhs_type.Is(Type::PlainNumber()) &&
495 rhs_type.Is(Type::PlainNumber())) {
496 // SameValue(x:plain-number,y:plain-number) => NumberEqual(x,y)
497 NodeProperties::ChangeOp(node, simplified()->NumberEqual());
498 return Changed(node);
499 }
500 return NoChange();
501 }
502
ReduceSelect(Node * node)503 Reduction TypedOptimization::ReduceSelect(Node* node) {
504 DCHECK_EQ(IrOpcode::kSelect, node->opcode());
505 Node* const condition = NodeProperties::GetValueInput(node, 0);
506 Type const condition_type = NodeProperties::GetType(condition);
507 Node* const vtrue = NodeProperties::GetValueInput(node, 1);
508 Type const vtrue_type = NodeProperties::GetType(vtrue);
509 Node* const vfalse = NodeProperties::GetValueInput(node, 2);
510 Type const vfalse_type = NodeProperties::GetType(vfalse);
511 if (condition_type.Is(true_type_)) {
512 // Select(condition:true, vtrue, vfalse) => vtrue
513 return Replace(vtrue);
514 }
515 if (condition_type.Is(false_type_)) {
516 // Select(condition:false, vtrue, vfalse) => vfalse
517 return Replace(vfalse);
518 }
519 if (vtrue_type.Is(true_type_) && vfalse_type.Is(false_type_)) {
520 // Select(condition, vtrue:true, vfalse:false) => condition
521 return Replace(condition);
522 }
523 if (vtrue_type.Is(false_type_) && vfalse_type.Is(true_type_)) {
524 // Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition)
525 node->TrimInputCount(1);
526 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
527 return Changed(node);
528 }
529 // Try to narrow the type of the Select {node}, which might be more precise
530 // now after lowering based on types.
531 Type type = Type::Union(vtrue_type, vfalse_type, graph()->zone());
532 Type const node_type = NodeProperties::GetType(node);
533 if (!node_type.Is(type)) {
534 type = Type::Intersect(node_type, type, graph()->zone());
535 NodeProperties::SetType(node, type);
536 return Changed(node);
537 }
538 return NoChange();
539 }
540
ReduceSpeculativeToNumber(Node * node)541 Reduction TypedOptimization::ReduceSpeculativeToNumber(Node* node) {
542 DCHECK_EQ(IrOpcode::kSpeculativeToNumber, node->opcode());
543 Node* const input = NodeProperties::GetValueInput(node, 0);
544 Type const input_type = NodeProperties::GetType(input);
545 if (input_type.Is(Type::Number())) {
546 // SpeculativeToNumber(x:number) => x
547 ReplaceWithValue(node, input);
548 return Replace(input);
549 }
550 return NoChange();
551 }
552
ReduceTypeOf(Node * node)553 Reduction TypedOptimization::ReduceTypeOf(Node* node) {
554 Node* const input = node->InputAt(0);
555 Type const type = NodeProperties::GetType(input);
556 Factory* const f = factory();
557 if (type.Is(Type::Boolean())) {
558 return Replace(
559 jsgraph()->Constant(ObjectRef(js_heap_broker(), f->boolean_string())));
560 } else if (type.Is(Type::Number())) {
561 return Replace(
562 jsgraph()->Constant(ObjectRef(js_heap_broker(), f->number_string())));
563 } else if (type.Is(Type::String())) {
564 return Replace(
565 jsgraph()->Constant(ObjectRef(js_heap_broker(), f->string_string())));
566 } else if (type.Is(Type::BigInt())) {
567 return Replace(
568 jsgraph()->Constant(ObjectRef(js_heap_broker(), f->bigint_string())));
569 } else if (type.Is(Type::Symbol())) {
570 return Replace(
571 jsgraph()->Constant(ObjectRef(js_heap_broker(), f->symbol_string())));
572 } else if (type.Is(Type::OtherUndetectableOrUndefined())) {
573 return Replace(jsgraph()->Constant(
574 ObjectRef(js_heap_broker(), f->undefined_string())));
575 } else if (type.Is(Type::NonCallableOrNull())) {
576 return Replace(
577 jsgraph()->Constant(ObjectRef(js_heap_broker(), f->object_string())));
578 } else if (type.Is(Type::Function())) {
579 return Replace(
580 jsgraph()->Constant(ObjectRef(js_heap_broker(), f->function_string())));
581 } else if (type.IsHeapConstant()) {
582 return Replace(jsgraph()->Constant(type.AsHeapConstant()->Ref().TypeOf()));
583 }
584 return NoChange();
585 }
586
ReduceToBoolean(Node * node)587 Reduction TypedOptimization::ReduceToBoolean(Node* node) {
588 Node* const input = node->InputAt(0);
589 Type const input_type = NodeProperties::GetType(input);
590 if (input_type.Is(Type::Boolean())) {
591 // ToBoolean(x:boolean) => x
592 return Replace(input);
593 } else if (input_type.Is(Type::OrderedNumber())) {
594 // SToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x,#0))
595 node->ReplaceInput(0, graph()->NewNode(simplified()->NumberEqual(), input,
596 jsgraph()->ZeroConstant()));
597 node->TrimInputCount(1);
598 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
599 return Changed(node);
600 } else if (input_type.Is(Type::Number())) {
601 // ToBoolean(x:number) => NumberToBoolean(x)
602 node->TrimInputCount(1);
603 NodeProperties::ChangeOp(node, simplified()->NumberToBoolean());
604 return Changed(node);
605 } else if (input_type.Is(Type::DetectableReceiverOrNull())) {
606 // ToBoolean(x:detectable receiver \/ null)
607 // => BooleanNot(ReferenceEqual(x,#null))
608 node->ReplaceInput(0, graph()->NewNode(simplified()->ReferenceEqual(),
609 input, jsgraph()->NullConstant()));
610 node->TrimInputCount(1);
611 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
612 return Changed(node);
613 } else if (input_type.Is(Type::ReceiverOrNullOrUndefined())) {
614 // ToBoolean(x:receiver \/ null \/ undefined)
615 // => BooleanNot(ObjectIsUndetectable(x))
616 node->ReplaceInput(
617 0, graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
618 node->TrimInputCount(1);
619 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
620 return Changed(node);
621 } else if (input_type.Is(Type::String())) {
622 // ToBoolean(x:string) => BooleanNot(ReferenceEqual(x,""))
623 node->ReplaceInput(0,
624 graph()->NewNode(simplified()->ReferenceEqual(), input,
625 jsgraph()->EmptyStringConstant()));
626 node->TrimInputCount(1);
627 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
628 return Changed(node);
629 }
630 return NoChange();
631 }
632
factory() const633 Factory* TypedOptimization::factory() const { return isolate()->factory(); }
634
graph() const635 Graph* TypedOptimization::graph() const { return jsgraph()->graph(); }
636
isolate() const637 Isolate* TypedOptimization::isolate() const { return jsgraph()->isolate(); }
638
simplified() const639 SimplifiedOperatorBuilder* TypedOptimization::simplified() const {
640 return jsgraph()->simplified();
641 }
642
643 } // namespace compiler
644 } // namespace internal
645 } // namespace v8
646