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 453996 : switch (binop_hint) {
23 : case BinaryOperationHint::kSignedSmall:
24 150989 : *number_hint = NumberOperationHint::kSignedSmall;
25 : return true;
26 : case BinaryOperationHint::kSignedSmallInputs:
27 13967 : *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 83937 : *number_hint = NumberOperationHint::kNumber;
34 : return true;
35 : case BinaryOperationHint::kNumberOrOddball:
36 3195 : *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 526594 : slot_(slot) {}
64 :
65 425956 : BinaryOperationHint GetBinaryOperationHint() {
66 : FeedbackNexus nexus(feedback_vector(), slot_);
67 425956 : return nexus.GetBinaryOperationFeedback();
68 : }
69 :
70 100638 : CompareOperationHint GetCompareOperationHint() {
71 : FeedbackNexus nexus(feedback_vector(), slot_);
72 100638 : return nexus.GetCompareOperationFeedback();
73 : }
74 :
75 425956 : bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
76 425956 : return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
77 425956 : hint);
78 : }
79 :
80 100638 : bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
81 100638 : switch (GetCompareOperationHint()) {
82 : case CompareOperationHint::kSignedSmall:
83 54891 : *hint = NumberOperationHint::kSignedSmall;
84 54891 : return true;
85 : case CompareOperationHint::kNumber:
86 1493 : *hint = NumberOperationHint::kNumber;
87 1493 : 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 252088 : const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
105 252088 : switch (op_->opcode()) {
106 : case IrOpcode::kJSAdd:
107 335548 : if (hint == NumberOperationHint::kSignedSmall ||
108 167774 : hint == NumberOperationHint::kSigned32) {
109 116933 : return simplified()->SpeculativeSafeIntegerAdd(hint);
110 : } else {
111 50841 : return simplified()->SpeculativeNumberAdd(hint);
112 : }
113 : case IrOpcode::kJSSubtract:
114 21668 : if (hint == NumberOperationHint::kSignedSmall ||
115 10834 : hint == NumberOperationHint::kSigned32) {
116 9922 : return simplified()->SpeculativeSafeIntegerSubtract(hint);
117 : } else {
118 912 : return simplified()->SpeculativeNumberSubtract(hint);
119 : }
120 : case IrOpcode::kJSMultiply:
121 19043 : return simplified()->SpeculativeNumberMultiply(hint);
122 : case IrOpcode::kJSDivide:
123 19975 : return simplified()->SpeculativeNumberDivide(hint);
124 : case IrOpcode::kJSModulus:
125 3973 : return simplified()->SpeculativeNumberModulus(hint);
126 : case IrOpcode::kJSBitwiseAnd:
127 4711 : return simplified()->SpeculativeNumberBitwiseAnd(hint);
128 : case IrOpcode::kJSBitwiseOr:
129 16000 : return simplified()->SpeculativeNumberBitwiseOr(hint);
130 : case IrOpcode::kJSBitwiseXor:
131 1102 : return simplified()->SpeculativeNumberBitwiseXor(hint);
132 : case IrOpcode::kJSShiftLeft:
133 2193 : return simplified()->SpeculativeNumberShiftLeft(hint);
134 : case IrOpcode::kJSShiftRight:
135 4873 : return simplified()->SpeculativeNumberShiftRight(hint);
136 : case IrOpcode::kJSShiftRightLogical:
137 1610 : return simplified()->SpeculativeNumberShiftRightLogical(hint);
138 : default:
139 : break;
140 : }
141 0 : UNREACHABLE();
142 : }
143 :
144 56391 : const Operator* SpeculativeCompareOp(NumberOperationHint hint) {
145 56391 : switch (op_->opcode()) {
146 : case IrOpcode::kJSEqual:
147 9800 : return simplified()->SpeculativeNumberEqual(hint);
148 : case IrOpcode::kJSLessThan:
149 13459 : return simplified()->SpeculativeNumberLessThan(hint);
150 : case IrOpcode::kJSGreaterThan:
151 : std::swap(left_, right_); // a > b => b < a
152 31939 : return simplified()->SpeculativeNumberLessThan(hint);
153 : case IrOpcode::kJSLessThanOrEqual:
154 520 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
155 : case IrOpcode::kJSGreaterThanOrEqual:
156 : std::swap(left_, right_); // a >= b => b <= a
157 673 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
158 : default:
159 : break;
160 : }
161 0 : UNREACHABLE();
162 : }
163 :
164 308479 : 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 616958 : return graph()->NewNode(op, left_, right_, effect_, control_);
173 : }
174 :
175 425956 : Node* TryBuildNumberBinop() {
176 : NumberOperationHint hint;
177 425956 : if (GetBinaryNumberOperationHint(&hint)) {
178 252088 : const Operator* op = SpeculativeNumberOp(hint);
179 252088 : Node* node = BuildSpeculativeOperation(op);
180 252088 : return node;
181 : }
182 : return nullptr;
183 : }
184 :
185 100638 : Node* TryBuildNumberCompare() {
186 : NumberOperationHint hint;
187 100638 : if (GetCompareNumberOperationHint(&hint)) {
188 56391 : const Operator* op = SpeculativeCompareOp(hint);
189 56391 : Node* node = BuildSpeculativeOperation(op);
190 56391 : 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 528981 : JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
216 : Handle<FeedbackVector> feedback_vector,
217 : Flags flags)
218 528981 : : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
219 :
220 0 : Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
221 :
222 67702 : 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 67702 : if (Node* node = TryBuildSoftDeopt(
228 : nexus, effect, control,
229 67702 : DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
230 : return LoweringResult::Exit(node);
231 : }
232 :
233 : Node* node;
234 66741 : 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 459 : control, slot);
241 459 : 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 8077 : control, slot);
250 8077 : 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 40697 : control, slot);
260 40697 : 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 17508 : control, slot);
269 17508 : node = b.TryBuildNumberBinop();
270 : break;
271 : }
272 : default:
273 0 : UNREACHABLE();
274 : break;
275 : }
276 :
277 66741 : if (node != nullptr) {
278 : return LoweringResult::SideEffectFree(node, node, control);
279 : } else {
280 : return LoweringResult::NoChange();
281 : }
282 : }
283 :
284 652069 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
285 : const Operator* op, Node* left, Node* right, Node* effect, Node* control,
286 : FeedbackSlot slot) const {
287 652069 : switch (op->opcode()) {
288 : case IrOpcode::kJSStrictEqual: {
289 : DCHECK(!slot.IsInvalid());
290 : FeedbackNexus nexus(feedback_vector(), slot);
291 173018 : if (Node* node = TryBuildSoftDeopt(
292 : nexus, effect, control,
293 173018 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
294 13131 : return LoweringResult::Exit(node);
295 : }
296 : // TODO(turbofan): Should we generally support early lowering of
297 : // JSStrictEqual operators here?
298 159887 : 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 101388 : if (Node* node = TryBuildSoftDeopt(
308 : nexus, effect, control,
309 101388 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
310 57141 : return LoweringResult::Exit(node);
311 : }
312 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
313 100638 : if (Node* node = b.TryBuildNumberCompare()) {
314 : return LoweringResult::SideEffectFree(node, node, control);
315 : }
316 44247 : break;
317 : }
318 : case IrOpcode::kJSInstanceOf: {
319 : DCHECK(!slot.IsInvalid());
320 : FeedbackNexus nexus(feedback_vector(), slot);
321 3848 : if (Node* node = TryBuildSoftDeopt(
322 : nexus, effect, control,
323 3848 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
324 50 : return LoweringResult::Exit(node);
325 : }
326 : // TODO(turbofan): Should we generally support early lowering of
327 : // JSInstanceOf operators here?
328 3798 : 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 373631 : if (Node* node = TryBuildSoftDeopt(
344 : nexus, effect, control,
345 373631 : DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
346 225489 : return LoweringResult::Exit(node);
347 : }
348 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
349 359215 : if (Node* node = b.TryBuildNumberBinop()) {
350 : return LoweringResult::SideEffectFree(node, node, control);
351 : }
352 148142 : 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 1553 : 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 1553 : if (Node* node = TryBuildSoftDeopt(
371 : nexus, effect, control,
372 1553 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
373 : return LoweringResult::Exit(node);
374 : }
375 : return LoweringResult::NoChange();
376 : }
377 :
378 : JSTypeHintLowering::LoweringResult
379 1404 : JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
380 : Node* control,
381 : FeedbackSlot slot) const {
382 : DCHECK(!slot.IsInvalid());
383 : FeedbackNexus nexus(feedback_vector(), slot);
384 1404 : if (Node* node = TryBuildSoftDeopt(
385 : nexus, effect, control,
386 1404 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
387 : return LoweringResult::Exit(node);
388 : }
389 : return LoweringResult::NoChange();
390 : }
391 :
392 28040 : 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 56080 : if (BinaryOperationHintToNumberOperationHint(
398 : nexus.GetBinaryOperationFeedback(), &hint)) {
399 28074 : 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 465469 : 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 465469 : if (Node* node = TryBuildSoftDeopt(
415 : nexus, effect, control,
416 465469 : DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
417 : return LoweringResult::Exit(node);
418 : }
419 : return LoweringResult::NoChange();
420 : }
421 :
422 41615 : 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 41615 : if (Node* node = TryBuildSoftDeopt(
430 : nexus, effect, control,
431 41615 : DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
432 : return LoweringResult::Exit(node);
433 : }
434 : return LoweringResult::NoChange();
435 : }
436 :
437 343643 : 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 343643 : if (Node* node = TryBuildSoftDeopt(
444 : nexus, effect, control,
445 343644 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
446 : return LoweringResult::Exit(node);
447 : }
448 : return LoweringResult::NoChange();
449 : }
450 :
451 42610 : 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 42610 : if (Node* node = TryBuildSoftDeopt(
458 : nexus, effect, control,
459 42610 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
460 : return LoweringResult::Exit(node);
461 : }
462 : return LoweringResult::NoChange();
463 : }
464 :
465 : JSTypeHintLowering::LoweringResult
466 119430 : 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 119430 : if (Node* node = TryBuildSoftDeopt(
475 : nexus, effect, control,
476 119430 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
477 : return LoweringResult::Exit(node);
478 : }
479 : return LoweringResult::NoChange();
480 : }
481 :
482 : JSTypeHintLowering::LoweringResult
483 62501 : 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 62501 : if (Node* node = TryBuildSoftDeopt(
492 : nexus, effect, control,
493 62501 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
494 : return LoweringResult::Exit(node);
495 : }
496 : return LoweringResult::NoChange();
497 : }
498 :
499 1797810 : Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
500 : Node* control,
501 : DeoptimizeReason reason) const {
502 2572947 : if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
503 126772 : Node* deoptimize = jsgraph()->graph()->NewNode(
504 : jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
505 : VectorSlotPair()),
506 : jsgraph()->Dead(), effect, control);
507 63386 : Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
508 63386 : deoptimize->ReplaceInput(0, frame_state);
509 63386 : return deoptimize;
510 : }
511 : return nullptr;
512 : }
513 :
514 : } // namespace compiler
515 : } // namespace internal
516 120216 : } // namespace v8
|