Line data Source code
1 : // Copyright 2017 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/js-type-hint-lowering.h"
6 :
7 : #include "src/compiler/access-builder.h"
8 : #include "src/compiler/js-graph.h"
9 : #include "src/compiler/operator-properties.h"
10 : #include "src/compiler/simplified-operator.h"
11 : #include "src/feedback-vector.h"
12 : #include "src/type-hints.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 : namespace compiler {
17 :
18 : namespace {
19 :
20 : bool BinaryOperationHintToNumberOperationHint(
21 : BinaryOperationHint binop_hint, NumberOperationHint* number_hint) {
22 440472 : switch (binop_hint) {
23 : case BinaryOperationHint::kSignedSmall:
24 159202 : *number_hint = NumberOperationHint::kSignedSmall;
25 : return true;
26 : case BinaryOperationHint::kSignedSmallInputs:
27 13770 : *number_hint = NumberOperationHint::kSignedSmallInputs;
28 : return true;
29 : case BinaryOperationHint::kSigned32:
30 0 : *number_hint = NumberOperationHint::kSigned32;
31 : return true;
32 : case BinaryOperationHint::kNumber:
33 75101 : *number_hint = NumberOperationHint::kNumber;
34 : return true;
35 : case BinaryOperationHint::kNumberOrOddball:
36 3242 : *number_hint = NumberOperationHint::kNumberOrOddball;
37 : return true;
38 : case BinaryOperationHint::kAny:
39 : case BinaryOperationHint::kNone:
40 : case BinaryOperationHint::kString:
41 : case BinaryOperationHint::kBigInt:
42 : break;
43 : }
44 : return false;
45 : }
46 :
47 : } // namespace
48 :
49 : class JSSpeculativeBinopBuilder final {
50 : public:
51 : JSSpeculativeBinopBuilder(const JSTypeHintLowering* lowering,
52 : const Operator* op, Node* left, Node* right,
53 : Node* effect, Node* control, FeedbackSlot slot)
54 : : lowering_(lowering),
55 : op_(op),
56 : left_(left),
57 : right_(right),
58 : effect_(effect),
59 : control_(control),
60 514041 : slot_(slot) {}
61 :
62 413488 : BinaryOperationHint GetBinaryOperationHint() {
63 413488 : FeedbackNexus nexus(feedback_vector(), slot_);
64 413488 : return nexus.GetBinaryOperationFeedback();
65 : }
66 :
67 100553 : CompareOperationHint GetCompareOperationHint() {
68 100553 : FeedbackNexus nexus(feedback_vector(), slot_);
69 100553 : return nexus.GetCompareOperationFeedback();
70 : }
71 :
72 413488 : bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
73 : return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
74 826977 : hint);
75 : }
76 :
77 100553 : bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
78 100553 : switch (GetCompareOperationHint()) {
79 : case CompareOperationHint::kSignedSmall:
80 56078 : *hint = NumberOperationHint::kSignedSmall;
81 56078 : return true;
82 : case CompareOperationHint::kNumber:
83 1431 : *hint = NumberOperationHint::kNumber;
84 1431 : return true;
85 : case CompareOperationHint::kNumberOrOddball:
86 7 : *hint = NumberOperationHint::kNumberOrOddball;
87 7 : return true;
88 : case CompareOperationHint::kAny:
89 : case CompareOperationHint::kNone:
90 : case CompareOperationHint::kString:
91 : case CompareOperationHint::kSymbol:
92 : case CompareOperationHint::kBigInt:
93 : case CompareOperationHint::kReceiver:
94 : case CompareOperationHint::kReceiverOrNullOrUndefined:
95 : case CompareOperationHint::kInternalizedString:
96 : break;
97 : }
98 : return false;
99 : }
100 :
101 251315 : const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
102 251315 : switch (op_->opcode()) {
103 : case IrOpcode::kJSAdd:
104 336950 : if (hint == NumberOperationHint::kSignedSmall ||
105 168475 : hint == NumberOperationHint::kSigned32) {
106 117654 : return simplified()->SpeculativeSafeIntegerAdd(hint);
107 : } else {
108 50821 : return simplified()->SpeculativeNumberAdd(hint);
109 : }
110 : case IrOpcode::kJSSubtract:
111 20918 : if (hint == NumberOperationHint::kSignedSmall ||
112 10459 : hint == NumberOperationHint::kSigned32) {
113 9583 : return simplified()->SpeculativeSafeIntegerSubtract(hint);
114 : } else {
115 876 : return simplified()->SpeculativeNumberSubtract(hint);
116 : }
117 : case IrOpcode::kJSMultiply:
118 18839 : return simplified()->SpeculativeNumberMultiply(hint);
119 : case IrOpcode::kJSDivide:
120 19682 : return simplified()->SpeculativeNumberDivide(hint);
121 : case IrOpcode::kJSModulus:
122 3531 : return simplified()->SpeculativeNumberModulus(hint);
123 : case IrOpcode::kJSBitwiseAnd:
124 5329 : return simplified()->SpeculativeNumberBitwiseAnd(hint);
125 : case IrOpcode::kJSBitwiseOr:
126 14856 : return simplified()->SpeculativeNumberBitwiseOr(hint);
127 : case IrOpcode::kJSBitwiseXor:
128 1069 : return simplified()->SpeculativeNumberBitwiseXor(hint);
129 : case IrOpcode::kJSShiftLeft:
130 2154 : return simplified()->SpeculativeNumberShiftLeft(hint);
131 : case IrOpcode::kJSShiftRight:
132 4778 : return simplified()->SpeculativeNumberShiftRight(hint);
133 : case IrOpcode::kJSShiftRightLogical:
134 2143 : return simplified()->SpeculativeNumberShiftRightLogical(hint);
135 : default:
136 : break;
137 : }
138 0 : UNREACHABLE();
139 : }
140 :
141 57516 : const Operator* SpeculativeCompareOp(NumberOperationHint hint) {
142 57516 : switch (op_->opcode()) {
143 : case IrOpcode::kJSEqual:
144 10547 : return simplified()->SpeculativeNumberEqual(hint);
145 : case IrOpcode::kJSLessThan:
146 13609 : return simplified()->SpeculativeNumberLessThan(hint);
147 : case IrOpcode::kJSGreaterThan:
148 : std::swap(left_, right_); // a > b => b < a
149 32286 : return simplified()->SpeculativeNumberLessThan(hint);
150 : case IrOpcode::kJSLessThanOrEqual:
151 512 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
152 : case IrOpcode::kJSGreaterThanOrEqual:
153 : std::swap(left_, right_); // a >= b => b <= a
154 562 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
155 : default:
156 : break;
157 : }
158 0 : UNREACHABLE();
159 : }
160 :
161 308831 : Node* BuildSpeculativeOperation(const Operator* op) {
162 : DCHECK_EQ(2, op->ValueInputCount());
163 : DCHECK_EQ(1, op->EffectInputCount());
164 : DCHECK_EQ(1, op->ControlInputCount());
165 : DCHECK_EQ(false, OperatorProperties::HasFrameStateInput(op));
166 : DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
167 : DCHECK_EQ(1, op->EffectOutputCount());
168 : DCHECK_EQ(0, op->ControlOutputCount());
169 617662 : return graph()->NewNode(op, left_, right_, effect_, control_);
170 : }
171 :
172 413488 : Node* TryBuildNumberBinop() {
173 : NumberOperationHint hint;
174 413488 : if (GetBinaryNumberOperationHint(&hint)) {
175 251315 : const Operator* op = SpeculativeNumberOp(hint);
176 251315 : Node* node = BuildSpeculativeOperation(op);
177 251315 : return node;
178 : }
179 : return nullptr;
180 : }
181 :
182 100553 : Node* TryBuildNumberCompare() {
183 : NumberOperationHint hint;
184 100553 : if (GetCompareNumberOperationHint(&hint)) {
185 57516 : const Operator* op = SpeculativeCompareOp(hint);
186 57516 : Node* node = BuildSpeculativeOperation(op);
187 57516 : return node;
188 : }
189 : return nullptr;
190 : }
191 :
192 617662 : JSGraph* jsgraph() const { return lowering_->jsgraph(); }
193 : Isolate* isolate() const { return jsgraph()->isolate(); }
194 617662 : Graph* graph() const { return jsgraph()->graph(); }
195 : JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
196 617662 : SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); }
197 : CommonOperatorBuilder* common() { return jsgraph()->common(); }
198 : const Handle<FeedbackVector>& feedback_vector() const {
199 : return lowering_->feedback_vector();
200 : }
201 :
202 : private:
203 : const JSTypeHintLowering* lowering_;
204 : const Operator* op_;
205 : Node* left_;
206 : Node* right_;
207 : Node* effect_;
208 : Node* control_;
209 : FeedbackSlot slot_;
210 : };
211 :
212 523129 : JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
213 : Handle<FeedbackVector> feedback_vector,
214 : Flags flags)
215 523129 : : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
216 :
217 0 : Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
218 :
219 66785 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
220 65782 : const Operator* op, Node* operand, Node* effect, Node* control,
221 131564 : FeedbackSlot slot) const {
222 : DCHECK(!slot.IsInvalid());
223 66785 : FeedbackNexus nexus(feedback_vector(), slot);
224 66785 : if (Node* node = TryBuildSoftDeopt(
225 : nexus, effect, control,
226 66785 : DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
227 : return LoweringResult::Exit(node);
228 : }
229 :
230 : Node* node;
231 65782 : switch (op->opcode()) {
232 : case IrOpcode::kJSBitwiseNot: {
233 : // Lower to a speculative xor with -1 if we have some kind of Number
234 : // feedback.
235 : JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->BitwiseXor(),
236 : operand, jsgraph()->SmiConstant(-1), effect,
237 461 : control, slot);
238 461 : node = b.TryBuildNumberBinop();
239 : break;
240 : }
241 : case IrOpcode::kJSDecrement: {
242 : // Lower to a speculative subtraction of 1 if we have some kind of Number
243 : // feedback.
244 : JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Subtract(),
245 : operand, jsgraph()->SmiConstant(1), effect,
246 8207 : control, slot);
247 8207 : node = b.TryBuildNumberBinop();
248 : break;
249 : }
250 : case IrOpcode::kJSIncrement: {
251 : // Lower to a speculative addition of 1 if we have some kind of Number
252 : // feedback.
253 : BinaryOperationHint hint = BinaryOperationHint::kAny; // Dummy.
254 : JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Add(hint),
255 : operand, jsgraph()->SmiConstant(1), effect,
256 39532 : control, slot);
257 39532 : node = b.TryBuildNumberBinop();
258 : break;
259 : }
260 : case IrOpcode::kJSNegate: {
261 : // Lower to a speculative multiplication with -1 if we have some kind of
262 : // Number feedback.
263 : JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Multiply(),
264 : operand, jsgraph()->SmiConstant(-1), effect,
265 17582 : control, slot);
266 17582 : node = b.TryBuildNumberBinop();
267 : break;
268 : }
269 : default:
270 0 : UNREACHABLE();
271 : break;
272 : }
273 :
274 65782 : if (node != nullptr) {
275 : return LoweringResult::SideEffectFree(node, node, control);
276 : } else {
277 : return LoweringResult::NoChange();
278 : }
279 : }
280 :
281 639085 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
282 639085 : const Operator* op, Node* left, Node* right, Node* effect, Node* control,
283 : FeedbackSlot slot) const {
284 639085 : switch (op->opcode()) {
285 : case IrOpcode::kJSStrictEqual: {
286 : DCHECK(!slot.IsInvalid());
287 171353 : FeedbackNexus nexus(feedback_vector(), slot);
288 171353 : if (Node* node = TryBuildSoftDeopt(
289 : nexus, effect, control,
290 171353 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
291 13367 : return LoweringResult::Exit(node);
292 : }
293 : // TODO(turbofan): Should we generally support early lowering of
294 : // JSStrictEqual operators here?
295 157986 : break;
296 : }
297 : case IrOpcode::kJSEqual:
298 : case IrOpcode::kJSLessThan:
299 : case IrOpcode::kJSGreaterThan:
300 : case IrOpcode::kJSLessThanOrEqual:
301 : case IrOpcode::kJSGreaterThanOrEqual: {
302 : DCHECK(!slot.IsInvalid());
303 101342 : FeedbackNexus nexus(feedback_vector(), slot);
304 101342 : if (Node* node = TryBuildSoftDeopt(
305 : nexus, effect, control,
306 101342 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
307 58305 : return LoweringResult::Exit(node);
308 : }
309 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
310 100553 : if (Node* node = b.TryBuildNumberCompare()) {
311 : return LoweringResult::SideEffectFree(node, node, control);
312 : }
313 43037 : break;
314 : }
315 : case IrOpcode::kJSInstanceOf: {
316 : DCHECK(!slot.IsInvalid());
317 3672 : FeedbackNexus nexus(feedback_vector(), slot);
318 3672 : if (Node* node = TryBuildSoftDeopt(
319 : nexus, effect, control,
320 3672 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
321 182 : return LoweringResult::Exit(node);
322 : }
323 : // TODO(turbofan): Should we generally support early lowering of
324 : // JSInstanceOf operators here?
325 3490 : break;
326 : }
327 : case IrOpcode::kJSBitwiseOr:
328 : case IrOpcode::kJSBitwiseXor:
329 : case IrOpcode::kJSBitwiseAnd:
330 : case IrOpcode::kJSShiftLeft:
331 : case IrOpcode::kJSShiftRight:
332 : case IrOpcode::kJSShiftRightLogical:
333 : case IrOpcode::kJSAdd:
334 : case IrOpcode::kJSSubtract:
335 : case IrOpcode::kJSMultiply:
336 : case IrOpcode::kJSDivide:
337 : case IrOpcode::kJSModulus: {
338 : DCHECK(!slot.IsInvalid());
339 362541 : FeedbackNexus nexus(feedback_vector(), slot);
340 362541 : if (Node* node = TryBuildSoftDeopt(
341 : nexus, effect, control,
342 362541 : DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
343 224834 : return LoweringResult::Exit(node);
344 : }
345 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
346 347706 : if (Node* node = b.TryBuildNumberBinop()) {
347 : return LoweringResult::SideEffectFree(node, node, control);
348 : }
349 137708 : break;
350 : }
351 : case IrOpcode::kJSExponentiate: {
352 : // TODO(neis): Introduce a SpeculativeNumberPow operator?
353 : break;
354 : }
355 : default:
356 0 : UNREACHABLE();
357 : break;
358 : }
359 : return LoweringResult::NoChange();
360 : }
361 :
362 1577 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceForInNextOperation(
363 : Node* receiver, Node* cache_array, Node* cache_type, Node* index,
364 : Node* effect, Node* control, FeedbackSlot slot) const {
365 : DCHECK(!slot.IsInvalid());
366 1577 : FeedbackNexus nexus(feedback_vector(), slot);
367 1577 : if (Node* node = TryBuildSoftDeopt(
368 : nexus, effect, control,
369 1577 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
370 : return LoweringResult::Exit(node);
371 : }
372 : return LoweringResult::NoChange();
373 : }
374 :
375 : JSTypeHintLowering::LoweringResult
376 1416 : JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
377 : Node* control,
378 : FeedbackSlot slot) const {
379 : DCHECK(!slot.IsInvalid());
380 1416 : FeedbackNexus nexus(feedback_vector(), slot);
381 1416 : if (Node* node = TryBuildSoftDeopt(
382 : nexus, effect, control,
383 1416 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
384 : return LoweringResult::Exit(node);
385 : }
386 : return LoweringResult::NoChange();
387 : }
388 :
389 26983 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation(
390 27528 : Node* input, Node* effect, Node* control, FeedbackSlot slot) const {
391 : DCHECK(!slot.IsInvalid());
392 26983 : FeedbackNexus nexus(feedback_vector(), slot);
393 : NumberOperationHint hint;
394 26983 : if (BinaryOperationHintToNumberOperationHint(
395 26983 : nexus.GetBinaryOperationFeedback(), &hint)) {
396 : Node* node = jsgraph()->graph()->NewNode(
397 : jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()),
398 55056 : input, effect, control);
399 : return LoweringResult::SideEffectFree(node, node, control);
400 : }
401 : return LoweringResult::NoChange();
402 : }
403 :
404 456319 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceCallOperation(
405 : const Operator* op, Node* const* args, int arg_count, Node* effect,
406 : Node* control, FeedbackSlot slot) const {
407 : DCHECK(op->opcode() == IrOpcode::kJSCall ||
408 : op->opcode() == IrOpcode::kJSCallWithSpread);
409 : DCHECK(!slot.IsInvalid());
410 456319 : FeedbackNexus nexus(feedback_vector(), slot);
411 456319 : if (Node* node = TryBuildSoftDeopt(
412 : nexus, effect, control,
413 456319 : DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
414 : return LoweringResult::Exit(node);
415 : }
416 : return LoweringResult::NoChange();
417 : }
418 :
419 40730 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceConstructOperation(
420 : const Operator* op, Node* const* args, int arg_count, Node* effect,
421 : Node* control, FeedbackSlot slot) const {
422 : DCHECK(op->opcode() == IrOpcode::kJSConstruct ||
423 : op->opcode() == IrOpcode::kJSConstructWithSpread);
424 : DCHECK(!slot.IsInvalid());
425 40730 : FeedbackNexus nexus(feedback_vector(), slot);
426 40730 : if (Node* node = TryBuildSoftDeopt(
427 : nexus, effect, control,
428 40731 : DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
429 : return LoweringResult::Exit(node);
430 : }
431 : return LoweringResult::NoChange();
432 : }
433 :
434 327932 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation(
435 : const Operator* op, Node* receiver, Node* effect, Node* control,
436 : FeedbackSlot slot) const {
437 : DCHECK_EQ(IrOpcode::kJSLoadNamed, op->opcode());
438 : DCHECK(!slot.IsInvalid());
439 327932 : FeedbackNexus nexus(feedback_vector(), slot);
440 327931 : if (Node* node = TryBuildSoftDeopt(
441 : nexus, effect, control,
442 327932 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
443 : return LoweringResult::Exit(node);
444 : }
445 : return LoweringResult::NoChange();
446 : }
447 :
448 43169 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadKeyedOperation(
449 : const Operator* op, Node* obj, Node* key, Node* effect, Node* control,
450 : FeedbackSlot slot) const {
451 : DCHECK_EQ(IrOpcode::kJSLoadProperty, op->opcode());
452 : DCHECK(!slot.IsInvalid());
453 43169 : FeedbackNexus nexus(feedback_vector(), slot);
454 43169 : if (Node* node = TryBuildSoftDeopt(
455 : nexus, effect, control,
456 43169 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
457 : return LoweringResult::Exit(node);
458 : }
459 : return LoweringResult::NoChange();
460 : }
461 :
462 : JSTypeHintLowering::LoweringResult
463 112225 : JSTypeHintLowering::ReduceStoreNamedOperation(const Operator* op, Node* obj,
464 : Node* val, Node* effect,
465 : Node* control,
466 : FeedbackSlot slot) const {
467 : DCHECK(op->opcode() == IrOpcode::kJSStoreNamed ||
468 : op->opcode() == IrOpcode::kJSStoreNamedOwn);
469 : DCHECK(!slot.IsInvalid());
470 112225 : FeedbackNexus nexus(feedback_vector(), slot);
471 112225 : if (Node* node = TryBuildSoftDeopt(
472 : nexus, effect, control,
473 112225 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
474 : return LoweringResult::Exit(node);
475 : }
476 : return LoweringResult::NoChange();
477 : }
478 :
479 : JSTypeHintLowering::LoweringResult
480 54843 : JSTypeHintLowering::ReduceStoreKeyedOperation(const Operator* op, Node* obj,
481 : Node* key, Node* val,
482 : Node* effect, Node* control,
483 : FeedbackSlot slot) const {
484 : DCHECK(op->opcode() == IrOpcode::kJSStoreProperty ||
485 : op->opcode() == IrOpcode::kJSStoreInArrayLiteral);
486 : DCHECK(!slot.IsInvalid());
487 54843 : FeedbackNexus nexus(feedback_vector(), slot);
488 54843 : if (Node* node = TryBuildSoftDeopt(
489 : nexus, effect, control,
490 54843 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
491 : return LoweringResult::Exit(node);
492 : }
493 : return LoweringResult::NoChange();
494 : }
495 :
496 1743903 : Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
497 : Node* control,
498 197058 : DeoptimizeReason reason) const {
499 2538032 : if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
500 : Node* deoptimize = jsgraph()->graph()->NewNode(
501 : jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
502 : VectorSlotPair()),
503 262744 : jsgraph()->Dead(), effect, control);
504 65686 : Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
505 65686 : deoptimize->ReplaceInput(0, frame_state);
506 65686 : return deoptimize;
507 : }
508 : return nullptr;
509 : }
510 :
511 : } // namespace compiler
512 : } // namespace internal
513 183867 : } // namespace v8
|