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 437242 : switch (binop_hint) {
23 : case BinaryOperationHint::kSignedSmall:
24 158064 : *number_hint = NumberOperationHint::kSignedSmall;
25 : return true;
26 : case BinaryOperationHint::kSignedSmallInputs:
27 14309 : *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 74387 : *number_hint = NumberOperationHint::kNumber;
34 : return true;
35 : case BinaryOperationHint::kNumberOrOddball:
36 3175 : *number_hint = NumberOperationHint::kNumberOrOddball;
37 : return true;
38 : case BinaryOperationHint::kAny:
39 : case BinaryOperationHint::kNone:
40 : case BinaryOperationHint::kConsOneByteString:
41 : case BinaryOperationHint::kConsTwoByteString:
42 : case BinaryOperationHint::kConsString:
43 : case BinaryOperationHint::kString:
44 : case BinaryOperationHint::kBigInt:
45 : break;
46 : }
47 : return false;
48 : }
49 :
50 : } // namespace
51 :
52 : class JSSpeculativeBinopBuilder final {
53 : public:
54 : JSSpeculativeBinopBuilder(const JSTypeHintLowering* lowering,
55 : const Operator* op, Node* left, Node* right,
56 : Node* effect, Node* control, FeedbackSlot slot)
57 : : lowering_(lowering),
58 : op_(op),
59 : left_(left),
60 : right_(right),
61 : effect_(effect),
62 : control_(control),
63 509751 : slot_(slot) {}
64 :
65 409147 : BinaryOperationHint GetBinaryOperationHint() {
66 : FeedbackNexus nexus(feedback_vector(), slot_);
67 409147 : return nexus.GetBinaryOperationFeedback();
68 : }
69 :
70 100604 : CompareOperationHint GetCompareOperationHint() {
71 : FeedbackNexus nexus(feedback_vector(), slot_);
72 100604 : return nexus.GetCompareOperationFeedback();
73 : }
74 :
75 409147 : bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
76 409147 : return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
77 409147 : hint);
78 : }
79 :
80 100604 : bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
81 100604 : switch (GetCompareOperationHint()) {
82 : case CompareOperationHint::kSignedSmall:
83 54342 : *hint = NumberOperationHint::kSignedSmall;
84 54342 : return true;
85 : case CompareOperationHint::kNumber:
86 1397 : *hint = NumberOperationHint::kNumber;
87 1397 : return true;
88 : case CompareOperationHint::kNumberOrOddball:
89 7 : *hint = NumberOperationHint::kNumberOrOddball;
90 7 : return true;
91 : case CompareOperationHint::kAny:
92 : case CompareOperationHint::kNone:
93 : case CompareOperationHint::kString:
94 : case CompareOperationHint::kSymbol:
95 : case CompareOperationHint::kBigInt:
96 : case CompareOperationHint::kReceiver:
97 : case CompareOperationHint::kReceiverOrNullOrUndefined:
98 : case CompareOperationHint::kInternalizedString:
99 : break;
100 : }
101 : return false;
102 : }
103 :
104 249935 : const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
105 249935 : switch (op_->opcode()) {
106 : case IrOpcode::kJSAdd:
107 332520 : if (hint == NumberOperationHint::kSignedSmall ||
108 166260 : hint == NumberOperationHint::kSigned32) {
109 115954 : return simplified()->SpeculativeSafeIntegerAdd(hint);
110 : } else {
111 50306 : return simplified()->SpeculativeNumberAdd(hint);
112 : }
113 : case IrOpcode::kJSSubtract:
114 20988 : if (hint == NumberOperationHint::kSignedSmall ||
115 10494 : hint == NumberOperationHint::kSigned32) {
116 9703 : return simplified()->SpeculativeSafeIntegerSubtract(hint);
117 : } else {
118 791 : return simplified()->SpeculativeNumberSubtract(hint);
119 : }
120 : case IrOpcode::kJSMultiply:
121 18988 : return simplified()->SpeculativeNumberMultiply(hint);
122 : case IrOpcode::kJSDivide:
123 20111 : return simplified()->SpeculativeNumberDivide(hint);
124 : case IrOpcode::kJSModulus:
125 3962 : return simplified()->SpeculativeNumberModulus(hint);
126 : case IrOpcode::kJSBitwiseAnd:
127 4649 : return simplified()->SpeculativeNumberBitwiseAnd(hint);
128 : case IrOpcode::kJSBitwiseOr:
129 15908 : return simplified()->SpeculativeNumberBitwiseOr(hint);
130 : case IrOpcode::kJSBitwiseXor:
131 994 : return simplified()->SpeculativeNumberBitwiseXor(hint);
132 : case IrOpcode::kJSShiftLeft:
133 2168 : return simplified()->SpeculativeNumberShiftLeft(hint);
134 : case IrOpcode::kJSShiftRight:
135 4817 : return simplified()->SpeculativeNumberShiftRight(hint);
136 : case IrOpcode::kJSShiftRightLogical:
137 1584 : return simplified()->SpeculativeNumberShiftRightLogical(hint);
138 : default:
139 : break;
140 : }
141 0 : UNREACHABLE();
142 : }
143 :
144 55746 : const Operator* SpeculativeCompareOp(NumberOperationHint hint) {
145 55746 : switch (op_->opcode()) {
146 : case IrOpcode::kJSEqual:
147 9796 : return simplified()->SpeculativeNumberEqual(hint);
148 : case IrOpcode::kJSLessThan:
149 13344 : return simplified()->SpeculativeNumberLessThan(hint);
150 : case IrOpcode::kJSGreaterThan:
151 : std::swap(left_, right_); // a > b => b < a
152 31566 : return simplified()->SpeculativeNumberLessThan(hint);
153 : case IrOpcode::kJSLessThanOrEqual:
154 409 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
155 : case IrOpcode::kJSGreaterThanOrEqual:
156 : std::swap(left_, right_); // a >= b => b <= a
157 631 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
158 : default:
159 : break;
160 : }
161 0 : UNREACHABLE();
162 : }
163 :
164 305681 : Node* BuildSpeculativeOperation(const Operator* op) {
165 : DCHECK_EQ(2, op->ValueInputCount());
166 : DCHECK_EQ(1, op->EffectInputCount());
167 : DCHECK_EQ(1, op->ControlInputCount());
168 : DCHECK_EQ(false, OperatorProperties::HasFrameStateInput(op));
169 : DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
170 : DCHECK_EQ(1, op->EffectOutputCount());
171 : DCHECK_EQ(0, op->ControlOutputCount());
172 611362 : return graph()->NewNode(op, left_, right_, effect_, control_);
173 : }
174 :
175 409147 : Node* TryBuildNumberBinop() {
176 : NumberOperationHint hint;
177 409147 : if (GetBinaryNumberOperationHint(&hint)) {
178 249935 : const Operator* op = SpeculativeNumberOp(hint);
179 249935 : Node* node = BuildSpeculativeOperation(op);
180 249935 : return node;
181 : }
182 : return nullptr;
183 : }
184 :
185 100604 : Node* TryBuildNumberCompare() {
186 : NumberOperationHint hint;
187 100604 : if (GetCompareNumberOperationHint(&hint)) {
188 55746 : const Operator* op = SpeculativeCompareOp(hint);
189 55746 : Node* node = BuildSpeculativeOperation(op);
190 55746 : return node;
191 : }
192 : return nullptr;
193 : }
194 :
195 : JSGraph* jsgraph() const { return lowering_->jsgraph(); }
196 : Isolate* isolate() const { return jsgraph()->isolate(); }
197 : Graph* graph() const { return jsgraph()->graph(); }
198 : JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
199 : SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); }
200 : CommonOperatorBuilder* common() { return jsgraph()->common(); }
201 : const Handle<FeedbackVector>& feedback_vector() const {
202 : return lowering_->feedback_vector();
203 : }
204 :
205 : private:
206 : const JSTypeHintLowering* lowering_;
207 : const Operator* op_;
208 : Node* left_;
209 : Node* right_;
210 : Node* effect_;
211 : Node* control_;
212 : FeedbackSlot slot_;
213 : };
214 :
215 530209 : JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
216 : Handle<FeedbackVector> feedback_vector,
217 : Flags flags)
218 530209 : : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
219 :
220 0 : Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
221 :
222 67786 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
223 : const Operator* op, Node* operand, Node* effect, Node* control,
224 : FeedbackSlot slot) const {
225 : DCHECK(!slot.IsInvalid());
226 : FeedbackNexus nexus(feedback_vector(), slot);
227 67786 : if (Node* node = TryBuildSoftDeopt(
228 : nexus, effect, control,
229 67786 : DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
230 : return LoweringResult::Exit(node);
231 : }
232 :
233 : Node* node;
234 66827 : switch (op->opcode()) {
235 : case IrOpcode::kJSBitwiseNot: {
236 : // Lower to a speculative xor with -1 if we have some kind of Number
237 : // feedback.
238 : JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->BitwiseXor(),
239 : operand, jsgraph()->SmiConstant(-1), effect,
240 456 : control, slot);
241 456 : node = b.TryBuildNumberBinop();
242 : break;
243 : }
244 : case IrOpcode::kJSDecrement: {
245 : // Lower to a speculative subtraction of 1 if we have some kind of Number
246 : // feedback.
247 : JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Subtract(),
248 : operand, jsgraph()->SmiConstant(1), effect,
249 8080 : control, slot);
250 8080 : node = b.TryBuildNumberBinop();
251 : break;
252 : }
253 : case IrOpcode::kJSIncrement: {
254 : // Lower to a speculative addition of 1 if we have some kind of Number
255 : // feedback.
256 : BinaryOperationHint hint = BinaryOperationHint::kAny; // Dummy.
257 : JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Add(hint),
258 : operand, jsgraph()->SmiConstant(1), effect,
259 40821 : control, slot);
260 40821 : node = b.TryBuildNumberBinop();
261 : break;
262 : }
263 : case IrOpcode::kJSNegate: {
264 : // Lower to a speculative multiplication with -1 if we have some kind of
265 : // Number feedback.
266 : JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Multiply(),
267 : operand, jsgraph()->SmiConstant(-1), effect,
268 17470 : control, slot);
269 17470 : node = b.TryBuildNumberBinop();
270 : break;
271 : }
272 : default:
273 0 : UNREACHABLE();
274 : break;
275 : }
276 :
277 66827 : if (node != nullptr) {
278 : return LoweringResult::SideEffectFree(node, node, control);
279 : } else {
280 : return LoweringResult::NoChange();
281 : }
282 : }
283 :
284 635751 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
285 : const Operator* op, Node* left, Node* right, Node* effect, Node* control,
286 : FeedbackSlot slot) const {
287 635751 : switch (op->opcode()) {
288 : case IrOpcode::kJSStrictEqual: {
289 : DCHECK(!slot.IsInvalid());
290 : FeedbackNexus nexus(feedback_vector(), slot);
291 173758 : if (Node* node = TryBuildSoftDeopt(
292 : nexus, effect, control,
293 173758 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
294 13164 : return LoweringResult::Exit(node);
295 : }
296 : // TODO(turbofan): Should we generally support early lowering of
297 : // JSStrictEqual operators here?
298 160594 : break;
299 : }
300 : case IrOpcode::kJSEqual:
301 : case IrOpcode::kJSLessThan:
302 : case IrOpcode::kJSGreaterThan:
303 : case IrOpcode::kJSLessThanOrEqual:
304 : case IrOpcode::kJSGreaterThanOrEqual: {
305 : DCHECK(!slot.IsInvalid());
306 : FeedbackNexus nexus(feedback_vector(), slot);
307 101312 : if (Node* node = TryBuildSoftDeopt(
308 : nexus, effect, control,
309 101312 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
310 56454 : return LoweringResult::Exit(node);
311 : }
312 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
313 100604 : if (Node* node = b.TryBuildNumberCompare()) {
314 : return LoweringResult::SideEffectFree(node, node, control);
315 : }
316 44858 : break;
317 : }
318 : case IrOpcode::kJSInstanceOf: {
319 : DCHECK(!slot.IsInvalid());
320 : FeedbackNexus nexus(feedback_vector(), slot);
321 3911 : if (Node* node = TryBuildSoftDeopt(
322 : nexus, effect, control,
323 3911 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
324 50 : return LoweringResult::Exit(node);
325 : }
326 : // TODO(turbofan): Should we generally support early lowering of
327 : // JSInstanceOf operators here?
328 3861 : break;
329 : }
330 : case IrOpcode::kJSBitwiseOr:
331 : case IrOpcode::kJSBitwiseXor:
332 : case IrOpcode::kJSBitwiseAnd:
333 : case IrOpcode::kJSShiftLeft:
334 : case IrOpcode::kJSShiftRight:
335 : case IrOpcode::kJSShiftRightLogical:
336 : case IrOpcode::kJSAdd:
337 : case IrOpcode::kJSSubtract:
338 : case IrOpcode::kJSMultiply:
339 : case IrOpcode::kJSDivide:
340 : case IrOpcode::kJSModulus: {
341 : DCHECK(!slot.IsInvalid());
342 : FeedbackNexus nexus(feedback_vector(), slot);
343 356586 : if (Node* node = TryBuildSoftDeopt(
344 : nexus, effect, control,
345 356586 : DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
346 223277 : return LoweringResult::Exit(node);
347 : }
348 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
349 342320 : if (Node* node = b.TryBuildNumberBinop()) {
350 : return LoweringResult::SideEffectFree(node, node, control);
351 : }
352 133309 : break;
353 : }
354 : case IrOpcode::kJSExponentiate: {
355 : // TODO(neis): Introduce a SpeculativeNumberPow operator?
356 : break;
357 : }
358 : default:
359 0 : UNREACHABLE();
360 : break;
361 : }
362 : return LoweringResult::NoChange();
363 : }
364 :
365 1558 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceForInNextOperation(
366 : Node* receiver, Node* cache_array, Node* cache_type, Node* index,
367 : Node* effect, Node* control, FeedbackSlot slot) const {
368 : DCHECK(!slot.IsInvalid());
369 : FeedbackNexus nexus(feedback_vector(), slot);
370 1558 : if (Node* node = TryBuildSoftDeopt(
371 : nexus, effect, control,
372 1558 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
373 : return LoweringResult::Exit(node);
374 : }
375 : return LoweringResult::NoChange();
376 : }
377 :
378 : JSTypeHintLowering::LoweringResult
379 1414 : JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
380 : Node* control,
381 : FeedbackSlot slot) const {
382 : DCHECK(!slot.IsInvalid());
383 : FeedbackNexus nexus(feedback_vector(), slot);
384 1414 : if (Node* node = TryBuildSoftDeopt(
385 : nexus, effect, control,
386 1414 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
387 : return LoweringResult::Exit(node);
388 : }
389 : return LoweringResult::NoChange();
390 : }
391 :
392 28095 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation(
393 : Node* input, Node* effect, Node* control, FeedbackSlot slot) const {
394 : DCHECK(!slot.IsInvalid());
395 : FeedbackNexus nexus(feedback_vector(), slot);
396 : NumberOperationHint hint;
397 56190 : if (BinaryOperationHintToNumberOperationHint(
398 : nexus.GetBinaryOperationFeedback(), &hint)) {
399 28054 : Node* node = jsgraph()->graph()->NewNode(
400 : jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()),
401 : input, effect, control);
402 : return LoweringResult::SideEffectFree(node, node, control);
403 : }
404 : return LoweringResult::NoChange();
405 : }
406 :
407 468665 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceCallOperation(
408 : const Operator* op, Node* const* args, int arg_count, Node* effect,
409 : Node* control, FeedbackSlot slot) const {
410 : DCHECK(op->opcode() == IrOpcode::kJSCall ||
411 : op->opcode() == IrOpcode::kJSCallWithSpread);
412 : DCHECK(!slot.IsInvalid());
413 : FeedbackNexus nexus(feedback_vector(), slot);
414 468665 : if (Node* node = TryBuildSoftDeopt(
415 : nexus, effect, control,
416 468665 : DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
417 : return LoweringResult::Exit(node);
418 : }
419 : return LoweringResult::NoChange();
420 : }
421 :
422 43629 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceConstructOperation(
423 : const Operator* op, Node* const* args, int arg_count, Node* effect,
424 : Node* control, FeedbackSlot slot) const {
425 : DCHECK(op->opcode() == IrOpcode::kJSConstruct ||
426 : op->opcode() == IrOpcode::kJSConstructWithSpread);
427 : DCHECK(!slot.IsInvalid());
428 : FeedbackNexus nexus(feedback_vector(), slot);
429 43629 : if (Node* node = TryBuildSoftDeopt(
430 : nexus, effect, control,
431 43629 : DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
432 : return LoweringResult::Exit(node);
433 : }
434 : return LoweringResult::NoChange();
435 : }
436 :
437 352581 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation(
438 : const Operator* op, Node* receiver, Node* effect, Node* control,
439 : FeedbackSlot slot) const {
440 : DCHECK_EQ(IrOpcode::kJSLoadNamed, op->opcode());
441 : DCHECK(!slot.IsInvalid());
442 : FeedbackNexus nexus(feedback_vector(), slot);
443 352581 : if (Node* node = TryBuildSoftDeopt(
444 : nexus, effect, control,
445 352581 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
446 : return LoweringResult::Exit(node);
447 : }
448 : return LoweringResult::NoChange();
449 : }
450 :
451 42993 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadKeyedOperation(
452 : const Operator* op, Node* obj, Node* key, Node* effect, Node* control,
453 : FeedbackSlot slot) const {
454 : DCHECK_EQ(IrOpcode::kJSLoadProperty, op->opcode());
455 : DCHECK(!slot.IsInvalid());
456 : FeedbackNexus nexus(feedback_vector(), slot);
457 42993 : if (Node* node = TryBuildSoftDeopt(
458 : nexus, effect, control,
459 42993 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
460 : return LoweringResult::Exit(node);
461 : }
462 : return LoweringResult::NoChange();
463 : }
464 :
465 : JSTypeHintLowering::LoweringResult
466 118104 : JSTypeHintLowering::ReduceStoreNamedOperation(const Operator* op, Node* obj,
467 : Node* val, Node* effect,
468 : Node* control,
469 : FeedbackSlot slot) const {
470 : DCHECK(op->opcode() == IrOpcode::kJSStoreNamed ||
471 : op->opcode() == IrOpcode::kJSStoreNamedOwn);
472 : DCHECK(!slot.IsInvalid());
473 : FeedbackNexus nexus(feedback_vector(), slot);
474 118104 : if (Node* node = TryBuildSoftDeopt(
475 : nexus, effect, control,
476 118104 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
477 : return LoweringResult::Exit(node);
478 : }
479 : return LoweringResult::NoChange();
480 : }
481 :
482 : JSTypeHintLowering::LoweringResult
483 65065 : JSTypeHintLowering::ReduceStoreKeyedOperation(const Operator* op, Node* obj,
484 : Node* key, Node* val,
485 : Node* effect, Node* control,
486 : FeedbackSlot slot) const {
487 : DCHECK(op->opcode() == IrOpcode::kJSStoreProperty ||
488 : op->opcode() == IrOpcode::kJSStoreInArrayLiteral);
489 : DCHECK(!slot.IsInvalid());
490 : FeedbackNexus nexus(feedback_vector(), slot);
491 65065 : if (Node* node = TryBuildSoftDeopt(
492 : nexus, effect, control,
493 65065 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
494 : return LoweringResult::Exit(node);
495 : }
496 : return LoweringResult::NoChange();
497 : }
498 :
499 1797360 : Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
500 : Node* control,
501 : DeoptimizeReason reason) const {
502 2575951 : if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
503 126430 : Node* deoptimize = jsgraph()->graph()->NewNode(
504 : jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
505 : VectorSlotPair()),
506 : jsgraph()->Dead(), effect, control);
507 63215 : Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
508 63215 : deoptimize->ReplaceInput(0, frame_state);
509 63215 : return deoptimize;
510 : }
511 : return nullptr;
512 : }
513 :
514 : } // namespace compiler
515 : } // namespace internal
516 122004 : } // namespace v8
|