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 446189 : switch (binop_hint) {
23 : case BinaryOperationHint::kSignedSmall:
24 158814 : *number_hint = NumberOperationHint::kSignedSmall;
25 : return true;
26 : case BinaryOperationHint::kSignedSmallInputs:
27 13780 : *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 74805 : *number_hint = NumberOperationHint::kNumber;
34 : return true;
35 : case BinaryOperationHint::kNumberOrOddball:
36 3339 : *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 519452 : slot_(slot) {}
61 :
62 419314 : BinaryOperationHint GetBinaryOperationHint() {
63 419314 : FeedbackNexus nexus(feedback_vector(), slot_);
64 419314 : return nexus.GetBinaryOperationFeedback();
65 : }
66 :
67 100138 : CompareOperationHint GetCompareOperationHint() {
68 100138 : FeedbackNexus nexus(feedback_vector(), slot_);
69 100138 : return nexus.GetCompareOperationFeedback();
70 : }
71 :
72 419314 : bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
73 : return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
74 838628 : hint);
75 : }
76 :
77 100138 : bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
78 100138 : switch (GetCompareOperationHint()) {
79 : case CompareOperationHint::kSignedSmall:
80 55810 : *hint = NumberOperationHint::kSignedSmall;
81 55810 : return true;
82 : case CompareOperationHint::kNumber:
83 1409 : *hint = NumberOperationHint::kNumber;
84 1409 : 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 250738 : const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
102 250738 : switch (op_->opcode()) {
103 : case IrOpcode::kJSAdd:
104 336160 : if (hint == NumberOperationHint::kSignedSmall ||
105 168080 : hint == NumberOperationHint::kSigned32) {
106 117358 : return simplified()->SpeculativeSafeIntegerAdd(hint);
107 : } else {
108 50722 : return simplified()->SpeculativeNumberAdd(hint);
109 : }
110 : case IrOpcode::kJSSubtract:
111 20864 : if (hint == NumberOperationHint::kSignedSmall ||
112 10432 : hint == NumberOperationHint::kSigned32) {
113 9573 : return simplified()->SpeculativeSafeIntegerSubtract(hint);
114 : } else {
115 859 : return simplified()->SpeculativeNumberSubtract(hint);
116 : }
117 : case IrOpcode::kJSMultiply:
118 18667 : return simplified()->SpeculativeNumberMultiply(hint);
119 : case IrOpcode::kJSDivide:
120 19674 : return simplified()->SpeculativeNumberDivide(hint);
121 : case IrOpcode::kJSModulus:
122 3520 : return simplified()->SpeculativeNumberModulus(hint);
123 : case IrOpcode::kJSBitwiseAnd:
124 5450 : return simplified()->SpeculativeNumberBitwiseAnd(hint);
125 : case IrOpcode::kJSBitwiseOr:
126 14803 : return simplified()->SpeculativeNumberBitwiseOr(hint);
127 : case IrOpcode::kJSBitwiseXor:
128 1059 : return simplified()->SpeculativeNumberBitwiseXor(hint);
129 : case IrOpcode::kJSShiftLeft:
130 2156 : return simplified()->SpeculativeNumberShiftLeft(hint);
131 : case IrOpcode::kJSShiftRight:
132 4763 : return simplified()->SpeculativeNumberShiftRight(hint);
133 : case IrOpcode::kJSShiftRightLogical:
134 2134 : return simplified()->SpeculativeNumberShiftRightLogical(hint);
135 : default:
136 : break;
137 : }
138 0 : UNREACHABLE();
139 : }
140 :
141 57226 : const Operator* SpeculativeCompareOp(NumberOperationHint hint) {
142 57226 : switch (op_->opcode()) {
143 : case IrOpcode::kJSEqual:
144 10423 : return simplified()->SpeculativeNumberEqual(hint);
145 : case IrOpcode::kJSLessThan:
146 13608 : return simplified()->SpeculativeNumberLessThan(hint);
147 : case IrOpcode::kJSGreaterThan:
148 : std::swap(left_, right_); // a > b => b < a
149 32244 : return simplified()->SpeculativeNumberLessThan(hint);
150 : case IrOpcode::kJSLessThanOrEqual:
151 517 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
152 : case IrOpcode::kJSGreaterThanOrEqual:
153 : std::swap(left_, right_); // a >= b => b <= a
154 434 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
155 : default:
156 : break;
157 : }
158 0 : UNREACHABLE();
159 : }
160 :
161 307964 : 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 615928 : return graph()->NewNode(op, left_, right_, effect_, control_);
170 : }
171 :
172 419314 : Node* TryBuildNumberBinop() {
173 : NumberOperationHint hint;
174 419314 : if (GetBinaryNumberOperationHint(&hint)) {
175 250738 : const Operator* op = SpeculativeNumberOp(hint);
176 250738 : Node* node = BuildSpeculativeOperation(op);
177 250738 : return node;
178 : }
179 : return nullptr;
180 : }
181 :
182 100138 : Node* TryBuildNumberCompare() {
183 : NumberOperationHint hint;
184 100138 : if (GetCompareNumberOperationHint(&hint)) {
185 57226 : const Operator* op = SpeculativeCompareOp(hint);
186 57226 : Node* node = BuildSpeculativeOperation(op);
187 57226 : return node;
188 : }
189 : return nullptr;
190 : }
191 :
192 615928 : JSGraph* jsgraph() const { return lowering_->jsgraph(); }
193 : Isolate* isolate() const { return jsgraph()->isolate(); }
194 615928 : Graph* graph() const { return jsgraph()->graph(); }
195 : JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
196 615928 : 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 522833 : JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
213 : Handle<FeedbackVector> feedback_vector,
214 : Flags flags)
215 522833 : : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
216 :
217 0 : Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
218 :
219 66761 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
220 65804 : const Operator* op, Node* operand, Node* effect, Node* control,
221 131608 : FeedbackSlot slot) const {
222 : DCHECK(!slot.IsInvalid());
223 66761 : FeedbackNexus nexus(feedback_vector(), slot);
224 66761 : if (Node* node = TryBuildSoftDeopt(
225 : nexus, effect, control,
226 66761 : DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
227 : return LoweringResult::Exit(node);
228 : }
229 :
230 : Node* node;
231 65804 : 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 457 : control, slot);
238 457 : 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 8070 : control, slot);
247 8070 : 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 39739 : control, slot);
257 39739 : 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 17538 : control, slot);
266 17538 : node = b.TryBuildNumberBinop();
267 : break;
268 : }
269 : default:
270 0 : UNREACHABLE();
271 : break;
272 : }
273 :
274 65804 : if (node != nullptr) {
275 : return LoweringResult::SideEffectFree(node, node, control);
276 : } else {
277 : return LoweringResult::NoChange();
278 : }
279 : }
280 :
281 645501 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
282 645501 : const Operator* op, Node* left, Node* right, Node* effect, Node* control,
283 : FeedbackSlot slot) const {
284 645501 : switch (op->opcode()) {
285 : case IrOpcode::kJSStrictEqual: {
286 : DCHECK(!slot.IsInvalid());
287 172820 : FeedbackNexus nexus(feedback_vector(), slot);
288 172820 : if (Node* node = TryBuildSoftDeopt(
289 : nexus, effect, control,
290 172820 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
291 13212 : return LoweringResult::Exit(node);
292 : }
293 : // TODO(turbofan): Should we generally support early lowering of
294 : // JSStrictEqual operators here?
295 159608 : 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 100912 : FeedbackNexus nexus(feedback_vector(), slot);
304 100912 : if (Node* node = TryBuildSoftDeopt(
305 : nexus, effect, control,
306 100912 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
307 58000 : return LoweringResult::Exit(node);
308 : }
309 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
310 100138 : if (Node* node = b.TryBuildNumberCompare()) {
311 : return LoweringResult::SideEffectFree(node, node, control);
312 : }
313 42912 : break;
314 : }
315 : case IrOpcode::kJSInstanceOf: {
316 : DCHECK(!slot.IsInvalid());
317 3835 : FeedbackNexus nexus(feedback_vector(), slot);
318 3835 : if (Node* node = TryBuildSoftDeopt(
319 : nexus, effect, control,
320 3835 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
321 51 : return LoweringResult::Exit(node);
322 : }
323 : // TODO(turbofan): Should we generally support early lowering of
324 : // JSInstanceOf operators here?
325 3784 : 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 367751 : FeedbackNexus nexus(feedback_vector(), slot);
340 367751 : if (Node* node = TryBuildSoftDeopt(
341 : nexus, effect, control,
342 367751 : DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
343 223837 : return LoweringResult::Exit(node);
344 : }
345 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
346 353510 : if (Node* node = b.TryBuildNumberBinop()) {
347 : return LoweringResult::SideEffectFree(node, node, control);
348 : }
349 143914 : 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 1564 : 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 1564 : FeedbackNexus nexus(feedback_vector(), slot);
367 1564 : if (Node* node = TryBuildSoftDeopt(
368 : nexus, effect, control,
369 1564 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
370 : return LoweringResult::Exit(node);
371 : }
372 : return LoweringResult::NoChange();
373 : }
374 :
375 : JSTypeHintLowering::LoweringResult
376 1417 : JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
377 : Node* control,
378 : FeedbackSlot slot) const {
379 : DCHECK(!slot.IsInvalid());
380 1417 : FeedbackNexus nexus(feedback_vector(), slot);
381 1417 : if (Node* node = TryBuildSoftDeopt(
382 : nexus, effect, control,
383 1417 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
384 : return LoweringResult::Exit(node);
385 : }
386 : return LoweringResult::NoChange();
387 : }
388 :
389 26875 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation(
390 27380 : Node* input, Node* effect, Node* control, FeedbackSlot slot) const {
391 : DCHECK(!slot.IsInvalid());
392 26875 : FeedbackNexus nexus(feedback_vector(), slot);
393 : NumberOperationHint hint;
394 26875 : if (BinaryOperationHintToNumberOperationHint(
395 26875 : nexus.GetBinaryOperationFeedback(), &hint)) {
396 : Node* node = jsgraph()->graph()->NewNode(
397 : jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()),
398 54760 : input, effect, control);
399 : return LoweringResult::SideEffectFree(node, node, control);
400 : }
401 : return LoweringResult::NoChange();
402 : }
403 :
404 463151 : 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 463151 : FeedbackNexus nexus(feedback_vector(), slot);
411 463151 : if (Node* node = TryBuildSoftDeopt(
412 : nexus, effect, control,
413 463151 : DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
414 : return LoweringResult::Exit(node);
415 : }
416 : return LoweringResult::NoChange();
417 : }
418 :
419 40839 : 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 40839 : FeedbackNexus nexus(feedback_vector(), slot);
426 40839 : if (Node* node = TryBuildSoftDeopt(
427 : nexus, effect, control,
428 40839 : DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
429 : return LoweringResult::Exit(node);
430 : }
431 : return LoweringResult::NoChange();
432 : }
433 :
434 333653 : 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 333653 : FeedbackNexus nexus(feedback_vector(), slot);
440 333653 : if (Node* node = TryBuildSoftDeopt(
441 : nexus, effect, control,
442 333653 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
443 : return LoweringResult::Exit(node);
444 : }
445 : return LoweringResult::NoChange();
446 : }
447 :
448 43238 : 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 43238 : FeedbackNexus nexus(feedback_vector(), slot);
454 43238 : if (Node* node = TryBuildSoftDeopt(
455 : nexus, effect, control,
456 43238 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
457 : return LoweringResult::Exit(node);
458 : }
459 : return LoweringResult::NoChange();
460 : }
461 :
462 : JSTypeHintLowering::LoweringResult
463 112618 : 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 112618 : FeedbackNexus nexus(feedback_vector(), slot);
471 112618 : if (Node* node = TryBuildSoftDeopt(
472 : nexus, effect, control,
473 112618 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
474 : return LoweringResult::Exit(node);
475 : }
476 : return LoweringResult::NoChange();
477 : }
478 :
479 : JSTypeHintLowering::LoweringResult
480 55660 : 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 55660 : FeedbackNexus nexus(feedback_vector(), slot);
488 55660 : if (Node* node = TryBuildSoftDeopt(
489 : nexus, effect, control,
490 55660 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
491 : return LoweringResult::Exit(node);
492 : }
493 : return LoweringResult::NoChange();
494 : }
495 :
496 1764218 : Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
497 : Node* control,
498 194757 : DeoptimizeReason reason) const {
499 2553006 : if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
500 : Node* deoptimize = jsgraph()->graph()->NewNode(
501 : jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
502 : VectorSlotPair()),
503 259676 : jsgraph()->Dead(), effect, control);
504 64919 : Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
505 64919 : deoptimize->ReplaceInput(0, frame_state);
506 64919 : return deoptimize;
507 : }
508 : return nullptr;
509 : }
510 :
511 : } // namespace compiler
512 : } // namespace internal
513 178779 : } // namespace v8
|