Line data Source code
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 469324 : switch (binop_hint) {
23 : case BinaryOperationHint::kSignedSmall:
24 172736 : *number_hint = NumberOperationHint::kSignedSmall;
25 : return true;
26 : case BinaryOperationHint::kSignedSmallInputs:
27 12341 : *number_hint = NumberOperationHint::kSignedSmallInputs;
28 : return true;
29 : case BinaryOperationHint::kSigned32:
30 0 : *number_hint = NumberOperationHint::kSigned32;
31 : return true;
32 : case BinaryOperationHint::kNumber:
33 78500 : *number_hint = NumberOperationHint::kNumber;
34 : return true;
35 : case BinaryOperationHint::kNumberOrOddball:
36 3235 : *number_hint = NumberOperationHint::kNumberOrOddball;
37 : return true;
38 : case BinaryOperationHint::kAny:
39 : case BinaryOperationHint::kNone:
40 : case BinaryOperationHint::kString:
41 : break;
42 : }
43 : return false;
44 : }
45 :
46 : } // namespace
47 :
48 : class JSSpeculativeBinopBuilder final {
49 : public:
50 : JSSpeculativeBinopBuilder(const JSTypeHintLowering* lowering,
51 : const Operator* op, Node* left, Node* right,
52 : Node* effect, Node* control, FeedbackSlot slot)
53 : : lowering_(lowering),
54 : op_(op),
55 : left_(left),
56 : right_(right),
57 : effect_(effect),
58 : control_(control),
59 549615 : slot_(slot) {}
60 :
61 437936 : BinaryOperationHint GetBinaryOperationHint() {
62 : DCHECK_EQ(FeedbackSlotKind::kBinaryOp, feedback_vector()->GetKind(slot_));
63 : BinaryOpICNexus nexus(feedback_vector(), slot_);
64 437936 : return nexus.GetBinaryOperationFeedback();
65 : }
66 :
67 111679 : CompareOperationHint GetCompareOperationHint() {
68 : DCHECK_EQ(FeedbackSlotKind::kCompareOp, feedback_vector()->GetKind(slot_));
69 : CompareICNexus nexus(feedback_vector(), slot_);
70 111679 : return nexus.GetCompareOperationFeedback();
71 : }
72 :
73 437936 : bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
74 : return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
75 875872 : hint);
76 : }
77 :
78 111679 : bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
79 111679 : switch (GetCompareOperationHint()) {
80 : case CompareOperationHint::kSignedSmall:
81 63045 : *hint = NumberOperationHint::kSignedSmall;
82 63045 : return true;
83 : case CompareOperationHint::kNumber:
84 1727 : *hint = NumberOperationHint::kNumber;
85 1727 : return true;
86 : case CompareOperationHint::kNumberOrOddball:
87 8 : *hint = NumberOperationHint::kNumberOrOddball;
88 8 : return true;
89 : case CompareOperationHint::kAny:
90 : case CompareOperationHint::kNone:
91 : case CompareOperationHint::kString:
92 : case CompareOperationHint::kSymbol:
93 : case CompareOperationHint::kReceiver:
94 : case CompareOperationHint::kInternalizedString:
95 : break;
96 : }
97 : return false;
98 : }
99 :
100 266812 : const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
101 266812 : switch (op_->opcode()) {
102 : case IrOpcode::kJSAdd:
103 301288 : if (hint == NumberOperationHint::kSignedSmall ||
104 150644 : hint == NumberOperationHint::kSigned32) {
105 98090 : return simplified()->SpeculativeSafeIntegerAdd(hint);
106 : } else {
107 52554 : return simplified()->SpeculativeNumberAdd(hint);
108 : }
109 : case IrOpcode::kJSSubtract:
110 89494 : if (hint == NumberOperationHint::kSignedSmall ||
111 44747 : hint == NumberOperationHint::kSigned32) {
112 43315 : return simplified()->SpeculativeSafeIntegerSubtract(hint);
113 : } else {
114 1432 : return simplified()->SpeculativeNumberSubtract(hint);
115 : }
116 : case IrOpcode::kJSMultiply:
117 18720 : return simplified()->SpeculativeNumberMultiply(hint);
118 : case IrOpcode::kJSDivide:
119 18624 : return simplified()->SpeculativeNumberDivide(hint);
120 : case IrOpcode::kJSModulus:
121 3586 : return simplified()->SpeculativeNumberModulus(hint);
122 : case IrOpcode::kJSBitwiseAnd:
123 4906 : return simplified()->SpeculativeNumberBitwiseAnd(hint);
124 : case IrOpcode::kJSBitwiseOr:
125 15798 : return simplified()->SpeculativeNumberBitwiseOr(hint);
126 : case IrOpcode::kJSBitwiseXor:
127 974 : return simplified()->SpeculativeNumberBitwiseXor(hint);
128 : case IrOpcode::kJSShiftLeft:
129 2074 : return simplified()->SpeculativeNumberShiftLeft(hint);
130 : case IrOpcode::kJSShiftRight:
131 4866 : return simplified()->SpeculativeNumberShiftRight(hint);
132 : case IrOpcode::kJSShiftRightLogical:
133 1873 : return simplified()->SpeculativeNumberShiftRightLogical(hint);
134 : default:
135 : break;
136 : }
137 0 : UNREACHABLE();
138 : }
139 :
140 64780 : const Operator* SpeculativeCompareOp(NumberOperationHint hint) {
141 64780 : switch (op_->opcode()) {
142 : case IrOpcode::kJSEqual:
143 16449 : return simplified()->SpeculativeNumberEqual(hint);
144 : case IrOpcode::kJSLessThan:
145 14590 : return simplified()->SpeculativeNumberLessThan(hint);
146 : case IrOpcode::kJSGreaterThan:
147 : std::swap(left_, right_); // a > b => b < a
148 32103 : return simplified()->SpeculativeNumberLessThan(hint);
149 : case IrOpcode::kJSLessThanOrEqual:
150 744 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
151 : case IrOpcode::kJSGreaterThanOrEqual:
152 : std::swap(left_, right_); // a >= b => b <= a
153 894 : return simplified()->SpeculativeNumberLessThanOrEqual(hint);
154 : default:
155 : break;
156 : }
157 0 : UNREACHABLE();
158 : }
159 :
160 331592 : 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 663184 : return graph()->NewNode(op, left_, right_, effect_, control_);
169 : }
170 :
171 437936 : Node* TryBuildNumberBinop() {
172 : NumberOperationHint hint;
173 437936 : if (GetBinaryNumberOperationHint(&hint)) {
174 266812 : const Operator* op = SpeculativeNumberOp(hint);
175 266812 : Node* node = BuildSpeculativeOperation(op);
176 266812 : return node;
177 : }
178 : return nullptr;
179 : }
180 :
181 111679 : Node* TryBuildNumberCompare() {
182 : NumberOperationHint hint;
183 111679 : if (GetCompareNumberOperationHint(&hint)) {
184 64780 : const Operator* op = SpeculativeCompareOp(hint);
185 64780 : Node* node = BuildSpeculativeOperation(op);
186 64780 : return node;
187 : }
188 : return nullptr;
189 : }
190 :
191 663184 : JSGraph* jsgraph() const { return lowering_->jsgraph(); }
192 663184 : Graph* graph() const { return jsgraph()->graph(); }
193 : JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
194 663184 : SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); }
195 : CommonOperatorBuilder* common() { return jsgraph()->common(); }
196 : 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 :
210 504909 : JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
211 : Handle<FeedbackVector> feedback_vector,
212 : Flags flags)
213 504909 : : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
214 :
215 737452 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
216 737452 : const Operator* op, Node* left, Node* right, Node* effect, Node* control,
217 : FeedbackSlot slot) const {
218 : switch (op->opcode()) {
219 : case IrOpcode::kJSStrictEqual: {
220 : DCHECK(!slot.IsInvalid());
221 : CompareICNexus nexus(feedback_vector(), slot);
222 173243 : if (Node* node = TryBuildSoftDeopt(
223 : nexus, effect, control,
224 173243 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
225 : return LoweringResult::Exit(node);
226 : }
227 : // TODO(turbofan): Should we generally support early lowering of
228 : // JSStrictEqual operators here?
229 : break;
230 : }
231 : case IrOpcode::kJSEqual:
232 : case IrOpcode::kJSLessThan:
233 : case IrOpcode::kJSGreaterThan:
234 : case IrOpcode::kJSLessThanOrEqual:
235 : case IrOpcode::kJSGreaterThanOrEqual: {
236 : DCHECK(!slot.IsInvalid());
237 : CompareICNexus nexus(feedback_vector(), slot);
238 112322 : if (Node* node = TryBuildSoftDeopt(
239 : nexus, effect, control,
240 112322 : DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
241 : return LoweringResult::Exit(node);
242 : }
243 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
244 111679 : if (Node* node = b.TryBuildNumberCompare()) {
245 : return LoweringResult::SideEffectFree(node, node, control);
246 : }
247 : break;
248 : }
249 : case IrOpcode::kJSBitwiseOr:
250 : case IrOpcode::kJSBitwiseXor:
251 : case IrOpcode::kJSBitwiseAnd:
252 : case IrOpcode::kJSShiftLeft:
253 : case IrOpcode::kJSShiftRight:
254 : case IrOpcode::kJSShiftRightLogical:
255 : case IrOpcode::kJSAdd:
256 : case IrOpcode::kJSSubtract:
257 : case IrOpcode::kJSMultiply:
258 : case IrOpcode::kJSDivide:
259 : case IrOpcode::kJSModulus: {
260 : DCHECK(!slot.IsInvalid());
261 : BinaryOpICNexus nexus(feedback_vector(), slot);
262 451887 : if (Node* node = TryBuildSoftDeopt(
263 : nexus, effect, control,
264 451887 : DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
265 : return LoweringResult::Exit(node);
266 : }
267 : JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
268 437936 : if (Node* node = b.TryBuildNumberBinop()) {
269 : return LoweringResult::SideEffectFree(node, node, control);
270 : }
271 : break;
272 : }
273 : default:
274 0 : UNREACHABLE();
275 : break;
276 : }
277 : return LoweringResult::NoChange();
278 : }
279 :
280 1710 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceForInNextOperation(
281 : Node* receiver, Node* cache_array, Node* cache_type, Node* index,
282 : Node* effect, Node* control, FeedbackSlot slot) const {
283 : DCHECK(!slot.IsInvalid());
284 : ForInICNexus nexus(feedback_vector(), slot);
285 1710 : if (Node* node = TryBuildSoftDeopt(
286 : nexus, effect, control,
287 1710 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
288 : return LoweringResult::Exit(node);
289 : }
290 : return LoweringResult::NoChange();
291 : }
292 :
293 : JSTypeHintLowering::LoweringResult
294 1548 : JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
295 : Node* control,
296 : FeedbackSlot slot) const {
297 : DCHECK(!slot.IsInvalid());
298 : ForInICNexus nexus(feedback_vector(), slot);
299 1548 : if (Node* node = TryBuildSoftDeopt(
300 : nexus, effect, control,
301 1548 : DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
302 : return LoweringResult::Exit(node);
303 : }
304 : return LoweringResult::NoChange();
305 : }
306 :
307 31388 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation(
308 39852 : Node* input, Node* effect, Node* control, FeedbackSlot slot) const {
309 : DCHECK(!slot.IsInvalid());
310 : BinaryOpICNexus nexus(feedback_vector(), slot);
311 : NumberOperationHint hint;
312 31388 : if (BinaryOperationHintToNumberOperationHint(
313 31388 : nexus.GetBinaryOperationFeedback(), &hint)) {
314 : Node* node = jsgraph()->graph()->NewNode(
315 : jsgraph()->simplified()->SpeculativeToNumber(hint), input, effect,
316 39852 : control);
317 : return LoweringResult::SideEffectFree(node, node, control);
318 : }
319 : return LoweringResult::NoChange();
320 : }
321 :
322 581777 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceCallOperation(
323 : const Operator* op, Node* const* args, int arg_count, Node* effect,
324 : Node* control, FeedbackSlot slot) const {
325 : DCHECK(op->opcode() == IrOpcode::kJSCall ||
326 : op->opcode() == IrOpcode::kJSCallWithSpread);
327 : DCHECK(!slot.IsInvalid());
328 : CallICNexus nexus(feedback_vector(), slot);
329 581777 : if (Node* node = TryBuildSoftDeopt(
330 : nexus, effect, control,
331 581777 : DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
332 : return LoweringResult::Exit(node);
333 : }
334 : return LoweringResult::NoChange();
335 : }
336 :
337 38495 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceConstructOperation(
338 : const Operator* op, Node* const* args, int arg_count, Node* effect,
339 : Node* control, FeedbackSlot slot) const {
340 : DCHECK(op->opcode() == IrOpcode::kJSConstruct ||
341 : op->opcode() == IrOpcode::kJSConstructWithSpread);
342 : DCHECK(!slot.IsInvalid());
343 : CallICNexus nexus(feedback_vector(), slot);
344 38495 : if (Node* node = TryBuildSoftDeopt(
345 : nexus, effect, control,
346 38495 : DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
347 : return LoweringResult::Exit(node);
348 : }
349 : return LoweringResult::NoChange();
350 : }
351 :
352 598905 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation(
353 : const Operator* op, Node* receiver, Node* effect, Node* control,
354 : FeedbackSlot slot) const {
355 : DCHECK_EQ(IrOpcode::kJSLoadNamed, op->opcode());
356 : DCHECK(!slot.IsInvalid());
357 : LoadICNexus nexus(feedback_vector(), slot);
358 598905 : if (Node* node = TryBuildSoftDeopt(
359 : nexus, effect, control,
360 598905 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
361 : return LoweringResult::Exit(node);
362 : }
363 : return LoweringResult::NoChange();
364 : }
365 :
366 55016 : JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadKeyedOperation(
367 : const Operator* op, Node* obj, Node* key, Node* effect, Node* control,
368 : FeedbackSlot slot) const {
369 : DCHECK_EQ(IrOpcode::kJSLoadProperty, op->opcode());
370 : DCHECK(!slot.IsInvalid());
371 : KeyedLoadICNexus nexus(feedback_vector(), slot);
372 55016 : if (Node* node = TryBuildSoftDeopt(
373 : nexus, effect, control,
374 55016 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
375 : return LoweringResult::Exit(node);
376 : }
377 : return LoweringResult::NoChange();
378 : }
379 :
380 : JSTypeHintLowering::LoweringResult
381 310724 : JSTypeHintLowering::ReduceStoreNamedOperation(const Operator* op, Node* obj,
382 : Node* val, Node* effect,
383 : Node* control,
384 : FeedbackSlot slot) const {
385 : DCHECK(op->opcode() == IrOpcode::kJSStoreNamed ||
386 : op->opcode() == IrOpcode::kJSStoreNamedOwn);
387 : DCHECK(!slot.IsInvalid());
388 : StoreICNexus nexus(feedback_vector(), slot);
389 310724 : if (Node* node = TryBuildSoftDeopt(
390 : nexus, effect, control,
391 310724 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
392 : return LoweringResult::Exit(node);
393 : }
394 : return LoweringResult::NoChange();
395 : }
396 :
397 : JSTypeHintLowering::LoweringResult
398 63175 : JSTypeHintLowering::ReduceStoreKeyedOperation(const Operator* op, Node* obj,
399 : Node* key, Node* val,
400 : Node* effect, Node* control,
401 : FeedbackSlot slot) const {
402 : DCHECK_EQ(IrOpcode::kJSStoreProperty, op->opcode());
403 : DCHECK(!slot.IsInvalid());
404 : KeyedStoreICNexus nexus(feedback_vector(), slot);
405 63175 : if (Node* node = TryBuildSoftDeopt(
406 : nexus, effect, control,
407 63175 : DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
408 : return LoweringResult::Exit(node);
409 : }
410 : return LoweringResult::NoChange();
411 : }
412 :
413 2388797 : Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect,
414 : Node* control,
415 187374 : DeoptimizeReason reason) const {
416 3146678 : if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
417 : Node* deoptimize = jsgraph()->graph()->NewNode(
418 : jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason),
419 187374 : jsgraph()->Dead(), effect, control);
420 62458 : Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize);
421 62458 : deoptimize->ReplaceInput(0, frame_state);
422 62458 : return deoptimize;
423 : }
424 : return nullptr;
425 : }
426 :
427 : } // namespace compiler
428 : } // namespace internal
429 : } // namespace v8
|