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