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