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 437228 : switch (binop_hint) {
23 : case BinaryOperationHint::kSignedSmall:
24 158157 : *number_hint = NumberOperationHint::kSignedSmall;
25 : return true;
26 : case BinaryOperationHint::kSignedSmallInputs:
27 14435 : *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 74472 : *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 509790 : slot_(slot) {}
61 :
62 409211 : BinaryOperationHint GetBinaryOperationHint() {
63 : FeedbackNexus nexus(feedback_vector(), slot_);
64 409211 : return nexus.GetBinaryOperationFeedback();
65 : }
66 :
67 100579 : CompareOperationHint GetCompareOperationHint() {
68 : FeedbackNexus nexus(feedback_vector(), slot_);
69 100579 : return nexus.GetCompareOperationFeedback();
70 : }
71 :
72 409211 : bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
73 409211 : return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
74 409211 : hint);
75 : }
76 :
77 100579 : bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
78 100579 : switch (GetCompareOperationHint()) {
79 : case CompareOperationHint::kSignedSmall:
80 54402 : *hint = NumberOperationHint::kSignedSmall;
81 54402 : return true;
82 : case CompareOperationHint::kNumber:
83 1384 : *hint = NumberOperationHint::kNumber;
84 1384 : 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 250237 : const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
102 250237 : switch (op_->opcode()) {
103 : case IrOpcode::kJSAdd:
104 334002 : if (hint == NumberOperationHint::kSignedSmall ||
105 167001 : hint == NumberOperationHint::kSigned32) {
106 116551 : return simplified()->SpeculativeSafeIntegerAdd(hint);
107 : } else {
108 50450 : return simplified()->SpeculativeNumberAdd(hint);
109 : }
110 : case IrOpcode::kJSSubtract:
111 20862 : if (hint == NumberOperationHint::kSignedSmall ||
112 10431 : hint == NumberOperationHint::kSigned32) {
113 9640 : return simplified()->SpeculativeSafeIntegerSubtract(hint);
114 : } else {
115 791 : return simplified()->SpeculativeNumberSubtract(hint);
116 : }
117 : case IrOpcode::kJSMultiply:
118 18777 : return simplified()->SpeculativeNumberMultiply(hint);
119 : case IrOpcode::kJSDivide:
120 20242 : return simplified()->SpeculativeNumberDivide(hint);
121 : case IrOpcode::kJSModulus:
122 3785 : return simplified()->SpeculativeNumberModulus(hint);
123 : case IrOpcode::kJSBitwiseAnd:
124 4614 : return simplified()->SpeculativeNumberBitwiseAnd(hint);
125 : case IrOpcode::kJSBitwiseOr:
126 15864 : return simplified()->SpeculativeNumberBitwiseOr(hint);
127 : case IrOpcode::kJSBitwiseXor:
128 992 : return simplified()->SpeculativeNumberBitwiseXor(hint);
129 : case IrOpcode::kJSShiftLeft:
130 2165 : return simplified()->SpeculativeNumberShiftLeft(hint);
131 : case IrOpcode::kJSShiftRight:
132 4813 : return simplified()->SpeculativeNumberShiftRight(hint);
133 : case IrOpcode::kJSShiftRightLogical:
134 1553 : return simplified()->SpeculativeNumberShiftRightLogical(hint);
135 : default:
136 : break;
137 : }
138 0 : UNREACHABLE();
139 : }
140 :
141 55793 : const Operator* SpeculativeCompareOp(NumberOperationHint hint) {
142 55793 : switch (op_->opcode()) {
143 : case IrOpcode::kJSEqual:
144 9740 : return simplified()->SpeculativeNumberEqual(hint);
145 : case IrOpcode::kJSLessThan:
146 13251 : return simplified()->SpeculativeNumberLessThan(hint);
147 : case IrOpcode::kJSGreaterThan:
148 : std::swap(left_, right_); // a > b => b < a
149 31778 : return simplified()->SpeculativeNumberLessThan(hint);
150 : case IrOpcode::kJSLessThanOrEqual:
151 399 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
152 : case IrOpcode::kJSGreaterThanOrEqual:
153 : std::swap(left_, right_); // a >= b => b <= a
154 625 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
155 : default:
156 : break;
157 : }
158 0 : UNREACHABLE();
159 : }
160 :
161 306030 : 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 612060 : return graph()->NewNode(op, left_, right_, effect_, control_);
170 : }
171 :
172 409211 : Node* TryBuildNumberBinop() {
173 : NumberOperationHint hint;
174 409211 : if (GetBinaryNumberOperationHint(&hint)) {
175 250237 : const Operator* op = SpeculativeNumberOp(hint);
176 250237 : Node* node = BuildSpeculativeOperation(op);
177 250237 : return node;
178 : }
179 : return nullptr;
180 : }
181 :
182 100579 : Node* TryBuildNumberCompare() {
183 : NumberOperationHint hint;
184 100579 : if (GetCompareNumberOperationHint(&hint)) {
185 55793 : const Operator* op = SpeculativeCompareOp(hint);
186 55793 : Node* node = BuildSpeculativeOperation(op);
187 55793 : 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 529745 : JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
213 : Handle<FeedbackVector> feedback_vector,
214 : Flags flags)
215 529745 : : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
216 :
217 0 : Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
218 :
219 67650 : 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 67650 : if (Node* node = TryBuildSoftDeopt(
225 : nexus, effect, control,
226 67650 : DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
227 : return LoweringResult::Exit(node);
228 : }
229 :
230 : Node* node;
231 66698 : 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 454 : control, slot);
238 454 : 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 8084 : control, slot);
247 8084 : 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 40691 : control, slot);
257 40691 : 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 17469 : control, slot);
266 17469 : node = b.TryBuildNumberBinop();
267 : break;
268 : }
269 : default:
270 0 : UNREACHABLE();
271 : break;
272 : }
273 :
274 66698 : if (node != nullptr) {
275 : return LoweringResult::SideEffectFree(node, node, control);
276 : } else {
277 : return LoweringResult::NoChange();
278 : }
279 : }
280 :
281 636205 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
282 : const Operator* op, Node* left, Node* right, Node* effect, Node* control,
283 : FeedbackSlot slot) const {
284 636205 : switch (op->opcode()) {
285 : case IrOpcode::kJSStrictEqual: {
286 : DCHECK(!slot.IsInvalid());
287 : FeedbackNexus nexus(feedback_vector(), slot);
288 174068 : if (Node* node = TryBuildSoftDeopt(
289 : nexus, effect, control,
290 174068 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
291 13212 : return LoweringResult::Exit(node);
292 : }
293 : // TODO(turbofan): Should we generally support early lowering of
294 : // JSStrictEqual operators here?
295 160856 : 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 101291 : if (Node* node = TryBuildSoftDeopt(
305 : nexus, effect, control,
306 101291 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
307 56505 : return LoweringResult::Exit(node);
308 : }
309 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
310 100579 : if (Node* node = b.TryBuildNumberCompare()) {
311 : return LoweringResult::SideEffectFree(node, node, control);
312 : }
313 44786 : break;
314 : }
315 : case IrOpcode::kJSInstanceOf: {
316 : DCHECK(!slot.IsInvalid());
317 : FeedbackNexus nexus(feedback_vector(), slot);
318 3886 : if (Node* node = TryBuildSoftDeopt(
319 : nexus, effect, control,
320 3886 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
321 50 : return LoweringResult::Exit(node);
322 : }
323 : // TODO(turbofan): Should we generally support early lowering of
324 : // JSInstanceOf operators here?
325 3836 : 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 356776 : if (Node* node = TryBuildSoftDeopt(
341 : nexus, effect, control,
342 356776 : DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
343 223668 : return LoweringResult::Exit(node);
344 : }
345 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
346 342513 : if (Node* node = b.TryBuildNumberBinop()) {
347 : return LoweringResult::SideEffectFree(node, node, control);
348 : }
349 133108 : 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 1411 : JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
377 : Node* control,
378 : FeedbackSlot slot) const {
379 : DCHECK(!slot.IsInvalid());
380 : FeedbackNexus nexus(feedback_vector(), slot);
381 1411 : if (Node* node = TryBuildSoftDeopt(
382 : nexus, effect, control,
383 1411 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
384 : return LoweringResult::Exit(node);
385 : }
386 : return LoweringResult::NoChange();
387 : }
388 :
389 28017 : 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 56034 : if (BinaryOperationHintToNumberOperationHint(
395 : nexus.GetBinaryOperationFeedback(), &hint)) {
396 27950 : 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 468105 : 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 468105 : if (Node* node = TryBuildSoftDeopt(
412 : nexus, effect, control,
413 468105 : DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
414 : return LoweringResult::Exit(node);
415 : }
416 : return LoweringResult::NoChange();
417 : }
418 :
419 43637 : 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 43637 : if (Node* node = TryBuildSoftDeopt(
427 : nexus, effect, control,
428 43637 : DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
429 : return LoweringResult::Exit(node);
430 : }
431 : return LoweringResult::NoChange();
432 : }
433 :
434 350766 : 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 350766 : if (Node* node = TryBuildSoftDeopt(
441 : nexus, effect, control,
442 350766 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
443 : return LoweringResult::Exit(node);
444 : }
445 : return LoweringResult::NoChange();
446 : }
447 :
448 43074 : 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 43074 : if (Node* node = TryBuildSoftDeopt(
455 : nexus, effect, control,
456 43074 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
457 : return LoweringResult::Exit(node);
458 : }
459 : return LoweringResult::NoChange();
460 : }
461 :
462 : JSTypeHintLowering::LoweringResult
463 117634 : 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 117634 : if (Node* node = TryBuildSoftDeopt(
472 : nexus, effect, control,
473 117634 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
474 : return LoweringResult::Exit(node);
475 : }
476 : return LoweringResult::NoChange();
477 : }
478 :
479 : JSTypeHintLowering::LoweringResult
480 65164 : 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 65164 : if (Node* node = TryBuildSoftDeopt(
489 : nexus, effect, control,
490 65164 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
491 : return LoweringResult::Exit(node);
492 : }
493 : return LoweringResult::NoChange();
494 : }
495 :
496 1795017 : Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
497 : Node* control,
498 : DeoptimizeReason reason) const {
499 2573032 : if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
500 126496 : Node* deoptimize = jsgraph()->graph()->NewNode(
501 : jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
502 : VectorSlotPair()),
503 : jsgraph()->Dead(), effect, control);
504 63248 : Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
505 63248 : deoptimize->ReplaceInput(0, frame_state);
506 63248 : return deoptimize;
507 : }
508 : return nullptr;
509 : }
510 :
511 : } // namespace compiler
512 : } // namespace internal
513 122036 : } // namespace v8
|