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(); }
graph() const192   Graph* graph() const { return jsgraph()->graph(); }
javascript()193   JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
simplified()194   SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); }
common()195   CommonOperatorBuilder* common() { return jsgraph()->common(); }
feedback_vector() const196   const Handle<FeedbackVector>& feedback_vector() const {
197     return lowering_->feedback_vector();
198   }
199 
200  private:
201   const JSTypeHintLowering* lowering_;
202   const Operator* op_;
203   Node* left_;
204   Node* right_;
205   Node* effect_;
206   Node* control_;
207   FeedbackSlot slot_;
208 };
209 
JSTypeHintLowering(JSGraph * jsgraph,Handle<FeedbackVector> feedback_vector,Flags flags)210 JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
211                                        Handle<FeedbackVector> feedback_vector,
212                                        Flags flags)
213     : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
214 
ReduceUnaryOperation(const Operator * op,Node * operand,Node * effect,Node * control,FeedbackSlot slot) const215 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
216     const Operator* op, Node* operand, Node* effect, Node* control,
217     FeedbackSlot slot) const {
218   DCHECK(!slot.IsInvalid());
219   FeedbackNexus nexus(feedback_vector(), slot);
220   if (Node* node = TryBuildSoftDeopt(
221           nexus, effect, control,
222           DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
223     return LoweringResult::Exit(node);
224   }
225 
226   Node* node;
227   switch (op->opcode()) {
228     case IrOpcode::kJSBitwiseNot: {
229       // Lower to a speculative xor with -1 if we have some kind of Number
230       // feedback.
231       JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->BitwiseXor(),
232                                   operand, jsgraph()->SmiConstant(-1), effect,
233                                   control, slot);
234       node = b.TryBuildNumberBinop();
235       break;
236     }
237     case IrOpcode::kJSDecrement: {
238       // Lower to a speculative subtraction of 1 if we have some kind of Number
239       // feedback.
240       JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Subtract(),
241                                   operand, jsgraph()->SmiConstant(1), effect,
242                                   control, slot);
243       node = b.TryBuildNumberBinop();
244       break;
245     }
246     case IrOpcode::kJSIncrement: {
247       // Lower to a speculative addition of 1 if we have some kind of Number
248       // feedback.
249       BinaryOperationHint hint = BinaryOperationHint::kAny;  // Dummy.
250       JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Add(hint),
251                                   operand, jsgraph()->SmiConstant(1), effect,
252                                   control, slot);
253       node = b.TryBuildNumberBinop();
254       break;
255     }
256     case IrOpcode::kJSNegate: {
257       // Lower to a speculative multiplication with -1 if we have some kind of
258       // Number feedback.
259       JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Multiply(),
260                                   operand, jsgraph()->SmiConstant(-1), effect,
261                                   control, slot);
262       node = b.TryBuildNumberBinop();
263       break;
264     }
265     default:
266       UNREACHABLE();
267       break;
268   }
269 
270   if (node != nullptr) {
271     return LoweringResult::SideEffectFree(node, node, control);
272   } else {
273     return LoweringResult::NoChange();
274   }
275 }
276 
ReduceBinaryOperation(const Operator * op,Node * left,Node * right,Node * effect,Node * control,FeedbackSlot slot) const277 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
278     const Operator* op, Node* left, Node* right, Node* effect, Node* control,
279     FeedbackSlot slot) const {
280   switch (op->opcode()) {
281     case IrOpcode::kJSStrictEqual: {
282       DCHECK(!slot.IsInvalid());
283       FeedbackNexus nexus(feedback_vector(), slot);
284       if (Node* node = TryBuildSoftDeopt(
285               nexus, effect, control,
286               DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
287         return LoweringResult::Exit(node);
288       }
289       // TODO(turbofan): Should we generally support early lowering of
290       // JSStrictEqual operators here?
291       break;
292     }
293     case IrOpcode::kJSEqual:
294     case IrOpcode::kJSLessThan:
295     case IrOpcode::kJSGreaterThan:
296     case IrOpcode::kJSLessThanOrEqual:
297     case IrOpcode::kJSGreaterThanOrEqual: {
298       DCHECK(!slot.IsInvalid());
299       FeedbackNexus nexus(feedback_vector(), slot);
300       if (Node* node = TryBuildSoftDeopt(
301               nexus, effect, control,
302               DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
303         return LoweringResult::Exit(node);
304       }
305       JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
306       if (Node* node = b.TryBuildNumberCompare()) {
307         return LoweringResult::SideEffectFree(node, node, control);
308       }
309       break;
310     }
311     case IrOpcode::kJSInstanceOf: {
312       DCHECK(!slot.IsInvalid());
313       FeedbackNexus nexus(feedback_vector(), slot);
314       if (Node* node = TryBuildSoftDeopt(
315               nexus, effect, control,
316               DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
317         return LoweringResult::Exit(node);
318       }
319       // TODO(turbofan): Should we generally support early lowering of
320       // JSInstanceOf operators here?
321       break;
322     }
323     case IrOpcode::kJSBitwiseOr:
324     case IrOpcode::kJSBitwiseXor:
325     case IrOpcode::kJSBitwiseAnd:
326     case IrOpcode::kJSShiftLeft:
327     case IrOpcode::kJSShiftRight:
328     case IrOpcode::kJSShiftRightLogical:
329     case IrOpcode::kJSAdd:
330     case IrOpcode::kJSSubtract:
331     case IrOpcode::kJSMultiply:
332     case IrOpcode::kJSDivide:
333     case IrOpcode::kJSModulus: {
334       DCHECK(!slot.IsInvalid());
335       FeedbackNexus nexus(feedback_vector(), slot);
336       if (Node* node = TryBuildSoftDeopt(
337               nexus, effect, control,
338               DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
339         return LoweringResult::Exit(node);
340       }
341       JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
342       if (Node* node = b.TryBuildNumberBinop()) {
343         return LoweringResult::SideEffectFree(node, node, control);
344       }
345       break;
346     }
347     case IrOpcode::kJSExponentiate: {
348       // TODO(neis): Introduce a SpeculativeNumberPow operator?
349       break;
350     }
351     default:
352       UNREACHABLE();
353       break;
354   }
355   return LoweringResult::NoChange();
356 }
357 
ReduceForInNextOperation(Node * receiver,Node * cache_array,Node * cache_type,Node * index,Node * effect,Node * control,FeedbackSlot slot) const358 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceForInNextOperation(
359     Node* receiver, Node* cache_array, Node* cache_type, Node* index,
360     Node* effect, Node* control, FeedbackSlot slot) const {
361   DCHECK(!slot.IsInvalid());
362   FeedbackNexus nexus(feedback_vector(), slot);
363   if (Node* node = TryBuildSoftDeopt(
364           nexus, effect, control,
365           DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
366     return LoweringResult::Exit(node);
367   }
368   return LoweringResult::NoChange();
369 }
370 
371 JSTypeHintLowering::LoweringResult
ReduceForInPrepareOperation(Node * enumerator,Node * effect,Node * control,FeedbackSlot slot) const372 JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
373                                                 Node* control,
374                                                 FeedbackSlot slot) const {
375   DCHECK(!slot.IsInvalid());
376   FeedbackNexus nexus(feedback_vector(), slot);
377   if (Node* node = TryBuildSoftDeopt(
378           nexus, effect, control,
379           DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
380     return LoweringResult::Exit(node);
381   }
382   return LoweringResult::NoChange();
383 }
384 
ReduceToNumberOperation(Node * input,Node * effect,Node * control,FeedbackSlot slot) const385 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation(
386     Node* input, Node* effect, Node* control, FeedbackSlot slot) const {
387   DCHECK(!slot.IsInvalid());
388   FeedbackNexus nexus(feedback_vector(), slot);
389   NumberOperationHint hint;
390   if (BinaryOperationHintToNumberOperationHint(
391           nexus.GetBinaryOperationFeedback(), &hint)) {
392     Node* node = jsgraph()->graph()->NewNode(
393         jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()),
394         input, effect, control);
395     return LoweringResult::SideEffectFree(node, node, control);
396   }
397   return LoweringResult::NoChange();
398 }
399 
ReduceCallOperation(const Operator * op,Node * const * args,int arg_count,Node * effect,Node * control,FeedbackSlot slot) const400 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceCallOperation(
401     const Operator* op, Node* const* args, int arg_count, Node* effect,
402     Node* control, FeedbackSlot slot) const {
403   DCHECK(op->opcode() == IrOpcode::kJSCall ||
404          op->opcode() == IrOpcode::kJSCallWithSpread);
405   DCHECK(!slot.IsInvalid());
406   FeedbackNexus nexus(feedback_vector(), slot);
407   if (Node* node = TryBuildSoftDeopt(
408           nexus, effect, control,
409           DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
410     return LoweringResult::Exit(node);
411   }
412   return LoweringResult::NoChange();
413 }
414 
ReduceConstructOperation(const Operator * op,Node * const * args,int arg_count,Node * effect,Node * control,FeedbackSlot slot) const415 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceConstructOperation(
416     const Operator* op, Node* const* args, int arg_count, Node* effect,
417     Node* control, FeedbackSlot slot) const {
418   DCHECK(op->opcode() == IrOpcode::kJSConstruct ||
419          op->opcode() == IrOpcode::kJSConstructWithSpread);
420   DCHECK(!slot.IsInvalid());
421   FeedbackNexus nexus(feedback_vector(), slot);
422   if (Node* node = TryBuildSoftDeopt(
423           nexus, effect, control,
424           DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
425     return LoweringResult::Exit(node);
426   }
427   return LoweringResult::NoChange();
428 }
429 
ReduceLoadNamedOperation(const Operator * op,Node * receiver,Node * effect,Node * control,FeedbackSlot slot) const430 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation(
431     const Operator* op, Node* receiver, Node* effect, Node* control,
432     FeedbackSlot slot) const {
433   DCHECK_EQ(IrOpcode::kJSLoadNamed, op->opcode());
434   DCHECK(!slot.IsInvalid());
435   FeedbackNexus nexus(feedback_vector(), slot);
436   if (Node* node = TryBuildSoftDeopt(
437           nexus, effect, control,
438           DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
439     return LoweringResult::Exit(node);
440   }
441   return LoweringResult::NoChange();
442 }
443 
ReduceLoadKeyedOperation(const Operator * op,Node * obj,Node * key,Node * effect,Node * control,FeedbackSlot slot) const444 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadKeyedOperation(
445     const Operator* op, Node* obj, Node* key, Node* effect, Node* control,
446     FeedbackSlot slot) const {
447   DCHECK_EQ(IrOpcode::kJSLoadProperty, op->opcode());
448   DCHECK(!slot.IsInvalid());
449   FeedbackNexus nexus(feedback_vector(), slot);
450   if (Node* node = TryBuildSoftDeopt(
451           nexus, effect, control,
452           DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
453     return LoweringResult::Exit(node);
454   }
455   return LoweringResult::NoChange();
456 }
457 
458 JSTypeHintLowering::LoweringResult
ReduceStoreNamedOperation(const Operator * op,Node * obj,Node * val,Node * effect,Node * control,FeedbackSlot slot) const459 JSTypeHintLowering::ReduceStoreNamedOperation(const Operator* op, Node* obj,
460                                               Node* val, Node* effect,
461                                               Node* control,
462                                               FeedbackSlot slot) const {
463   DCHECK(op->opcode() == IrOpcode::kJSStoreNamed ||
464          op->opcode() == IrOpcode::kJSStoreNamedOwn);
465   DCHECK(!slot.IsInvalid());
466   FeedbackNexus nexus(feedback_vector(), slot);
467   if (Node* node = TryBuildSoftDeopt(
468           nexus, effect, control,
469           DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
470     return LoweringResult::Exit(node);
471   }
472   return LoweringResult::NoChange();
473 }
474 
475 JSTypeHintLowering::LoweringResult
ReduceStoreKeyedOperation(const Operator * op,Node * obj,Node * key,Node * val,Node * effect,Node * control,FeedbackSlot slot) const476 JSTypeHintLowering::ReduceStoreKeyedOperation(const Operator* op, Node* obj,
477                                               Node* key, Node* val,
478                                               Node* effect, Node* control,
479                                               FeedbackSlot slot) const {
480   DCHECK(op->opcode() == IrOpcode::kJSStoreProperty ||
481          op->opcode() == IrOpcode::kJSStoreInArrayLiteral);
482   DCHECK(!slot.IsInvalid());
483   FeedbackNexus nexus(feedback_vector(), slot);
484   if (Node* node = TryBuildSoftDeopt(
485           nexus, effect, control,
486           DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
487     return LoweringResult::Exit(node);
488   }
489   return LoweringResult::NoChange();
490 }
491 
TryBuildSoftDeopt(FeedbackNexus & nexus,Node * effect,Node * control,DeoptimizeReason reason) const492 Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
493                                             Node* control,
494                                             DeoptimizeReason reason) const {
495   if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
496     Node* deoptimize = jsgraph()->graph()->NewNode(
497         jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
498                                         VectorSlotPair()),
499         jsgraph()->Dead(), effect, control);
500     Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
501     deoptimize->ReplaceInput(0, frame_state);
502     return deoptimize;
503   }
504   return nullptr;
505 }
506 
507 }  // namespace compiler
508 }  // namespace internal
509 }  // namespace v8
510