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