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