V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
js-type-hint-lowering.cc
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  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 
50  public:
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 
62  BinaryOperationHint GetBinaryOperationHint() {
63  FeedbackNexus nexus(feedback_vector(), slot_);
64  return nexus.GetBinaryOperationFeedback();
65  }
66 
67  CompareOperationHint GetCompareOperationHint() {
68  FeedbackNexus nexus(feedback_vector(), slot_);
69  return nexus.GetCompareOperationFeedback();
70  }
71 
72  bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
73  return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
74  hint);
75  }
76 
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::kReceiverOrNullOrUndefined:
95  case CompareOperationHint::kInternalizedString:
96  break;
97  }
98  return false;
99  }
100 
101  const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
102  switch (op_->opcode()) {
103  case IrOpcode::kJSAdd:
104  if (hint == NumberOperationHint::kSignedSmall ||
105  hint == NumberOperationHint::kSigned32) {
106  return simplified()->SpeculativeSafeIntegerAdd(hint);
107  } else {
108  return simplified()->SpeculativeNumberAdd(hint);
109  }
110  case IrOpcode::kJSSubtract:
111  if (hint == NumberOperationHint::kSignedSmall ||
112  hint == NumberOperationHint::kSigned32) {
113  return simplified()->SpeculativeSafeIntegerSubtract(hint);
114  } else {
115  return simplified()->SpeculativeNumberSubtract(hint);
116  }
117  case IrOpcode::kJSMultiply:
118  return simplified()->SpeculativeNumberMultiply(hint);
119  case IrOpcode::kJSDivide:
120  return simplified()->SpeculativeNumberDivide(hint);
121  case IrOpcode::kJSModulus:
122  return simplified()->SpeculativeNumberModulus(hint);
123  case IrOpcode::kJSBitwiseAnd:
124  return simplified()->SpeculativeNumberBitwiseAnd(hint);
125  case IrOpcode::kJSBitwiseOr:
126  return simplified()->SpeculativeNumberBitwiseOr(hint);
127  case IrOpcode::kJSBitwiseXor:
128  return simplified()->SpeculativeNumberBitwiseXor(hint);
129  case IrOpcode::kJSShiftLeft:
130  return simplified()->SpeculativeNumberShiftLeft(hint);
131  case IrOpcode::kJSShiftRight:
132  return simplified()->SpeculativeNumberShiftRight(hint);
133  case IrOpcode::kJSShiftRightLogical:
134  return simplified()->SpeculativeNumberShiftRightLogical(hint);
135  default:
136  break;
137  }
138  UNREACHABLE();
139  }
140 
141  const Operator* SpeculativeCompareOp(NumberOperationHint hint) {
142  switch (op_->opcode()) {
143  case IrOpcode::kJSEqual:
144  return simplified()->SpeculativeNumberEqual(hint);
145  case IrOpcode::kJSLessThan:
146  return simplified()->SpeculativeNumberLessThan(hint);
147  case IrOpcode::kJSGreaterThan:
148  std::swap(left_, right_); // a > b => b < a
149  return simplified()->SpeculativeNumberLessThan(hint);
150  case IrOpcode::kJSLessThanOrEqual:
151  return simplified()->SpeculativeNumberLessThanOrEqual(hint);
152  case IrOpcode::kJSGreaterThanOrEqual:
153  std::swap(left_, right_); // a >= b => b <= a
154  return simplified()->SpeculativeNumberLessThanOrEqual(hint);
155  default:
156  break;
157  }
158  UNREACHABLE();
159  }
160 
161  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  return graph()->NewNode(op, left_, right_, effect_, control_);
170  }
171 
172  Node* TryBuildNumberBinop() {
173  NumberOperationHint hint;
174  if (GetBinaryNumberOperationHint(&hint)) {
175  const Operator* op = SpeculativeNumberOp(hint);
176  Node* node = BuildSpeculativeOperation(op);
177  return node;
178  }
179  return nullptr;
180  }
181 
182  Node* TryBuildNumberCompare() {
183  NumberOperationHint hint;
184  if (GetCompareNumberOperationHint(&hint)) {
185  const Operator* op = SpeculativeCompareOp(hint);
186  Node* node = BuildSpeculativeOperation(op);
187  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 JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
213  Handle<FeedbackVector> feedback_vector,
214  Flags flags)
215  : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
216 
217 Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
218 
219 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  if (Node* node = TryBuildSoftDeopt(
225  nexus, effect, control,
226  DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
227  return LoweringResult::Exit(node);
228  }
229 
230  Node* node;
231  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  control, slot);
238  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  control, slot);
247  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  control, slot);
257  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  control, slot);
266  node = b.TryBuildNumberBinop();
267  break;
268  }
269  default:
270  UNREACHABLE();
271  break;
272  }
273 
274  if (node != nullptr) {
275  return LoweringResult::SideEffectFree(node, node, control);
276  } else {
277  return LoweringResult::NoChange();
278  }
279 }
280 
281 JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
282  const Operator* op, Node* left, Node* right, Node* effect, Node* control,
283  FeedbackSlot slot) const {
284  switch (op->opcode()) {
285  case IrOpcode::kJSStrictEqual: {
286  DCHECK(!slot.IsInvalid());
287  FeedbackNexus nexus(feedback_vector(), slot);
288  if (Node* node = TryBuildSoftDeopt(
289  nexus, effect, control,
290  DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
291  return LoweringResult::Exit(node);
292  }
293  // TODO(turbofan): Should we generally support early lowering of
294  // JSStrictEqual operators here?
295  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  if (Node* node = TryBuildSoftDeopt(
305  nexus, effect, control,
306  DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
307  return LoweringResult::Exit(node);
308  }
309  JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
310  if (Node* node = b.TryBuildNumberCompare()) {
311  return LoweringResult::SideEffectFree(node, node, control);
312  }
313  break;
314  }
315  case IrOpcode::kJSInstanceOf: {
316  DCHECK(!slot.IsInvalid());
317  FeedbackNexus nexus(feedback_vector(), slot);
318  if (Node* node = TryBuildSoftDeopt(
319  nexus, effect, control,
320  DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
321  return LoweringResult::Exit(node);
322  }
323  // TODO(turbofan): Should we generally support early lowering of
324  // JSInstanceOf operators here?
325  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  if (Node* node = TryBuildSoftDeopt(
341  nexus, effect, control,
342  DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
343  return LoweringResult::Exit(node);
344  }
345  JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
346  if (Node* node = b.TryBuildNumberBinop()) {
347  return LoweringResult::SideEffectFree(node, node, control);
348  }
349  break;
350  }
351  case IrOpcode::kJSExponentiate: {
352  // TODO(neis): Introduce a SpeculativeNumberPow operator?
353  break;
354  }
355  default:
356  UNREACHABLE();
357  break;
358  }
359  return LoweringResult::NoChange();
360 }
361 
362 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  if (Node* node = TryBuildSoftDeopt(
368  nexus, effect, control,
369  DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
370  return LoweringResult::Exit(node);
371  }
372  return LoweringResult::NoChange();
373 }
374 
375 JSTypeHintLowering::LoweringResult
376 JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
377  Node* control,
378  FeedbackSlot slot) const {
379  DCHECK(!slot.IsInvalid());
380  FeedbackNexus nexus(feedback_vector(), slot);
381  if (Node* node = TryBuildSoftDeopt(
382  nexus, effect, control,
383  DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
384  return LoweringResult::Exit(node);
385  }
386  return LoweringResult::NoChange();
387 }
388 
389 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  if (BinaryOperationHintToNumberOperationHint(
395  nexus.GetBinaryOperationFeedback(), &hint)) {
396  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 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  if (Node* node = TryBuildSoftDeopt(
412  nexus, effect, control,
413  DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
414  return LoweringResult::Exit(node);
415  }
416  return LoweringResult::NoChange();
417 }
418 
419 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  if (Node* node = TryBuildSoftDeopt(
427  nexus, effect, control,
428  DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
429  return LoweringResult::Exit(node);
430  }
431  return LoweringResult::NoChange();
432 }
433 
434 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  if (Node* node = TryBuildSoftDeopt(
441  nexus, effect, control,
442  DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
443  return LoweringResult::Exit(node);
444  }
445  return LoweringResult::NoChange();
446 }
447 
448 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  if (Node* node = TryBuildSoftDeopt(
455  nexus, effect, control,
456  DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
457  return LoweringResult::Exit(node);
458  }
459  return LoweringResult::NoChange();
460 }
461 
462 JSTypeHintLowering::LoweringResult
463 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  if (Node* node = TryBuildSoftDeopt(
472  nexus, effect, control,
473  DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
474  return LoweringResult::Exit(node);
475  }
476  return LoweringResult::NoChange();
477 }
478 
479 JSTypeHintLowering::LoweringResult
480 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  if (Node* node = TryBuildSoftDeopt(
489  nexus, effect, control,
490  DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
491  return LoweringResult::Exit(node);
492  }
493  return LoweringResult::NoChange();
494 }
495 
496 Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
497  Node* control,
498  DeoptimizeReason reason) const {
499  if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
500  Node* deoptimize = jsgraph()->graph()->NewNode(
501  jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
502  VectorSlotPair()),
503  jsgraph()->Dead(), effect, control);
504  Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
505  deoptimize->ReplaceInput(0, frame_state);
506  return deoptimize;
507  }
508  return nullptr;
509 }
510 
511 } // namespace compiler
512 } // namespace internal
513 } // namespace v8
Definition: libplatform.h:13