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 437461 : switch (binop_hint) {
23 : case BinaryOperationHint::kSignedSmall:
24 158646 : *number_hint = NumberOperationHint::kSignedSmall;
25 : return true;
26 : case BinaryOperationHint::kSignedSmallInputs:
27 14354 : *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 74620 : *number_hint = NumberOperationHint::kNumber;
34 : return true;
35 : case BinaryOperationHint::kNumberOrOddball:
36 3173 : *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 510196 : slot_(slot) {}
61 :
62 409408 : BinaryOperationHint GetBinaryOperationHint() {
63 : FeedbackNexus nexus(feedback_vector(), slot_);
64 409408 : return nexus.GetBinaryOperationFeedback();
65 : }
66 :
67 100788 : CompareOperationHint GetCompareOperationHint() {
68 : FeedbackNexus nexus(feedback_vector(), slot_);
69 100788 : return nexus.GetCompareOperationFeedback();
70 : }
71 :
72 409408 : bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
73 409408 : return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
74 409408 : hint);
75 : }
76 :
77 100788 : bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
78 100788 : switch (GetCompareOperationHint()) {
79 : case CompareOperationHint::kSignedSmall:
80 54596 : *hint = NumberOperationHint::kSignedSmall;
81 54596 : return true;
82 : case CompareOperationHint::kNumber:
83 1372 : *hint = NumberOperationHint::kNumber;
84 1372 : 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 250793 : const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
102 250793 : switch (op_->opcode()) {
103 : case IrOpcode::kJSAdd:
104 335228 : if (hint == NumberOperationHint::kSignedSmall ||
105 167614 : hint == NumberOperationHint::kSigned32) {
106 117009 : return simplified()->SpeculativeSafeIntegerAdd(hint);
107 : } else {
108 50605 : return simplified()->SpeculativeNumberAdd(hint);
109 : }
110 : case IrOpcode::kJSSubtract:
111 20886 : if (hint == NumberOperationHint::kSignedSmall ||
112 10443 : hint == NumberOperationHint::kSigned32) {
113 9655 : return simplified()->SpeculativeSafeIntegerSubtract(hint);
114 : } else {
115 788 : return simplified()->SpeculativeNumberSubtract(hint);
116 : }
117 : case IrOpcode::kJSMultiply:
118 18795 : return simplified()->SpeculativeNumberMultiply(hint);
119 : case IrOpcode::kJSDivide:
120 20113 : return simplified()->SpeculativeNumberDivide(hint);
121 : case IrOpcode::kJSModulus:
122 3774 : return simplified()->SpeculativeNumberModulus(hint);
123 : case IrOpcode::kJSBitwiseAnd:
124 4622 : return simplified()->SpeculativeNumberBitwiseAnd(hint);
125 : case IrOpcode::kJSBitwiseOr:
126 15885 : return simplified()->SpeculativeNumberBitwiseOr(hint);
127 : case IrOpcode::kJSBitwiseXor:
128 995 : return simplified()->SpeculativeNumberBitwiseXor(hint);
129 : case IrOpcode::kJSShiftLeft:
130 2157 : return simplified()->SpeculativeNumberShiftLeft(hint);
131 : case IrOpcode::kJSShiftRight:
132 4820 : return simplified()->SpeculativeNumberShiftRight(hint);
133 : case IrOpcode::kJSShiftRightLogical:
134 1575 : return simplified()->SpeculativeNumberShiftRightLogical(hint);
135 : default:
136 : break;
137 : }
138 0 : UNREACHABLE();
139 : }
140 :
141 55975 : const Operator* SpeculativeCompareOp(NumberOperationHint hint) {
142 55975 : switch (op_->opcode()) {
143 : case IrOpcode::kJSEqual:
144 9780 : return simplified()->SpeculativeNumberEqual(hint);
145 : case IrOpcode::kJSLessThan:
146 13260 : return simplified()->SpeculativeNumberLessThan(hint);
147 : case IrOpcode::kJSGreaterThan:
148 : std::swap(left_, right_); // a > b => b < a
149 31931 : return simplified()->SpeculativeNumberLessThan(hint);
150 : case IrOpcode::kJSLessThanOrEqual:
151 402 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
152 : case IrOpcode::kJSGreaterThanOrEqual:
153 : std::swap(left_, right_); // a >= b => b <= a
154 602 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
155 : default:
156 : break;
157 : }
158 0 : UNREACHABLE();
159 : }
160 :
161 306768 : 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 613536 : return graph()->NewNode(op, left_, right_, effect_, control_);
170 : }
171 :
172 409408 : Node* TryBuildNumberBinop() {
173 : NumberOperationHint hint;
174 409408 : if (GetBinaryNumberOperationHint(&hint)) {
175 250793 : const Operator* op = SpeculativeNumberOp(hint);
176 250793 : Node* node = BuildSpeculativeOperation(op);
177 250793 : return node;
178 : }
179 : return nullptr;
180 : }
181 :
182 100788 : Node* TryBuildNumberCompare() {
183 : NumberOperationHint hint;
184 100788 : if (GetCompareNumberOperationHint(&hint)) {
185 55975 : const Operator* op = SpeculativeCompareOp(hint);
186 55975 : Node* node = BuildSpeculativeOperation(op);
187 55975 : return node;
188 : }
189 : return nullptr;
190 : }
191 :
192 : JSGraph* jsgraph() const { return lowering_->jsgraph(); }
193 : Isolate* isolate() const { return jsgraph()->isolate(); }
194 : Graph* graph() const { return jsgraph()->graph(); }
195 : JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
196 : 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 529971 : JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
213 : Handle<FeedbackVector> feedback_vector,
214 : Flags flags)
215 529971 : : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
216 :
217 0 : Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
218 :
219 67663 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
220 : const Operator* op, Node* operand, Node* effect, Node* control,
221 : FeedbackSlot slot) const {
222 : DCHECK(!slot.IsInvalid());
223 : FeedbackNexus nexus(feedback_vector(), slot);
224 67663 : if (Node* node = TryBuildSoftDeopt(
225 : nexus, effect, control,
226 67663 : DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
227 : return LoweringResult::Exit(node);
228 : }
229 :
230 : Node* node;
231 66697 : 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 455 : control, slot);
238 455 : 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 8083 : control, slot);
247 8083 : 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 40689 : control, slot);
257 40689 : 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 17470 : control, slot);
266 17470 : node = b.TryBuildNumberBinop();
267 : break;
268 : }
269 : default:
270 0 : UNREACHABLE();
271 : break;
272 : }
273 :
274 66697 : if (node != nullptr) {
275 : return LoweringResult::SideEffectFree(node, node, control);
276 : } else {
277 : return LoweringResult::NoChange();
278 : }
279 : }
280 :
281 636199 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
282 : const Operator* op, Node* left, Node* right, Node* effect, Node* control,
283 : FeedbackSlot slot) const {
284 636199 : switch (op->opcode()) {
285 : case IrOpcode::kJSStrictEqual: {
286 : DCHECK(!slot.IsInvalid());
287 : FeedbackNexus nexus(feedback_vector(), slot);
288 173687 : if (Node* node = TryBuildSoftDeopt(
289 : nexus, effect, control,
290 173687 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
291 13147 : return LoweringResult::Exit(node);
292 : }
293 : // TODO(turbofan): Should we generally support early lowering of
294 : // JSStrictEqual operators here?
295 160540 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
304 101509 : if (Node* node = TryBuildSoftDeopt(
305 : nexus, effect, control,
306 101509 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
307 56696 : return LoweringResult::Exit(node);
308 : }
309 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
310 100788 : if (Node* node = b.TryBuildNumberCompare()) {
311 : return LoweringResult::SideEffectFree(node, node, control);
312 : }
313 44813 : break;
314 : }
315 : case IrOpcode::kJSInstanceOf: {
316 : DCHECK(!slot.IsInvalid());
317 : FeedbackNexus nexus(feedback_vector(), slot);
318 3898 : if (Node* node = TryBuildSoftDeopt(
319 : nexus, effect, control,
320 3898 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
321 54 : return LoweringResult::Exit(node);
322 : }
323 : // TODO(turbofan): Should we generally support early lowering of
324 : // JSInstanceOf operators here?
325 3844 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
340 356921 : if (Node* node = TryBuildSoftDeopt(
341 : nexus, effect, control,
342 356921 : DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
343 224172 : return LoweringResult::Exit(node);
344 : }
345 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
346 342711 : if (Node* node = b.TryBuildNumberBinop()) {
347 : return LoweringResult::SideEffectFree(node, node, control);
348 : }
349 132749 : 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 1557 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
367 1557 : if (Node* node = TryBuildSoftDeopt(
368 : nexus, effect, control,
369 1557 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
370 : return LoweringResult::Exit(node);
371 : }
372 : return LoweringResult::NoChange();
373 : }
374 :
375 : JSTypeHintLowering::LoweringResult
376 1418 : JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
377 : Node* control,
378 : FeedbackSlot slot) const {
379 : DCHECK(!slot.IsInvalid());
380 : FeedbackNexus nexus(feedback_vector(), slot);
381 1418 : if (Node* node = TryBuildSoftDeopt(
382 : nexus, effect, control,
383 1418 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
384 : return LoweringResult::Exit(node);
385 : }
386 : return LoweringResult::NoChange();
387 : }
388 :
389 28053 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation(
390 : Node* input, Node* effect, Node* control, FeedbackSlot slot) const {
391 : DCHECK(!slot.IsInvalid());
392 : FeedbackNexus nexus(feedback_vector(), slot);
393 : NumberOperationHint hint;
394 56106 : if (BinaryOperationHintToNumberOperationHint(
395 : nexus.GetBinaryOperationFeedback(), &hint)) {
396 28006 : Node* node = jsgraph()->graph()->NewNode(
397 : jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()),
398 : input, effect, control);
399 : return LoweringResult::SideEffectFree(node, node, control);
400 : }
401 : return LoweringResult::NoChange();
402 : }
403 :
404 467615 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
411 467616 : if (Node* node = TryBuildSoftDeopt(
412 : nexus, effect, control,
413 467616 : DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
414 : return LoweringResult::Exit(node);
415 : }
416 : return LoweringResult::NoChange();
417 : }
418 :
419 43592 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
426 43592 : if (Node* node = TryBuildSoftDeopt(
427 : nexus, effect, control,
428 43592 : DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
429 : return LoweringResult::Exit(node);
430 : }
431 : return LoweringResult::NoChange();
432 : }
433 :
434 350853 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
440 350853 : if (Node* node = TryBuildSoftDeopt(
441 : nexus, effect, control,
442 350853 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
443 : return LoweringResult::Exit(node);
444 : }
445 : return LoweringResult::NoChange();
446 : }
447 :
448 42943 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
454 42943 : if (Node* node = TryBuildSoftDeopt(
455 : nexus, effect, control,
456 42943 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
457 : return LoweringResult::Exit(node);
458 : }
459 : return LoweringResult::NoChange();
460 : }
461 :
462 : JSTypeHintLowering::LoweringResult
463 118029 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
471 118029 : if (Node* node = TryBuildSoftDeopt(
472 : nexus, effect, control,
473 118032 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
474 : return LoweringResult::Exit(node);
475 : }
476 : return LoweringResult::NoChange();
477 : }
478 :
479 : JSTypeHintLowering::LoweringResult
480 65026 : 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 : FeedbackNexus nexus(feedback_vector(), slot);
488 65026 : if (Node* node = TryBuildSoftDeopt(
489 : nexus, effect, control,
490 65026 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
491 : return LoweringResult::Exit(node);
492 : }
493 : return LoweringResult::NoChange();
494 : }
495 :
496 1794712 : Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
497 : Node* control,
498 : DeoptimizeReason reason) const {
499 2572513 : if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
500 126276 : Node* deoptimize = jsgraph()->graph()->NewNode(
501 : jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
502 : VectorSlotPair()),
503 : jsgraph()->Dead(), effect, control);
504 63138 : Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
505 63138 : deoptimize->ReplaceInput(0, frame_state);
506 63138 : return deoptimize;
507 : }
508 : return nullptr;
509 : }
510 :
511 : } // namespace compiler
512 : } // namespace internal
513 122036 : } // namespace v8
|