1 // Copyright 2016 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/ic/binary-op-assembler.h"
6
7 #include "src/common/globals.h"
8
9 namespace v8 {
10 namespace internal {
11
Generate_AddWithFeedback(const LazyNode<Context> & context,TNode<Object> lhs,TNode<Object> rhs,TNode<UintPtrT> slot_id,const LazyNode<HeapObject> & maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode,bool rhs_known_smi)12 TNode<Object> BinaryOpAssembler::Generate_AddWithFeedback(
13 const LazyNode<Context>& context, TNode<Object> lhs, TNode<Object> rhs,
14 TNode<UintPtrT> slot_id, const LazyNode<HeapObject>& maybe_feedback_vector,
15 UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) {
16 // Shared entry for floating point addition.
17 Label do_fadd(this), if_lhsisnotnumber(this, Label::kDeferred),
18 check_rhsisoddball(this, Label::kDeferred),
19 call_with_oddball_feedback(this), call_with_any_feedback(this),
20 call_add_stub(this), end(this), bigint(this, Label::kDeferred);
21 TVARIABLE(Float64T, var_fadd_lhs);
22 TVARIABLE(Float64T, var_fadd_rhs);
23 TVARIABLE(Smi, var_type_feedback);
24 TVARIABLE(Object, var_result);
25
26 // Check if the {lhs} is a Smi or a HeapObject.
27 Label if_lhsissmi(this);
28 // If rhs is known to be an Smi we want to fast path Smi operation. This is
29 // for AddSmi operation. For the normal Add operation, we want to fast path
30 // both Smi and Number operations, so this path should not be marked as
31 // Deferred.
32 Label if_lhsisnotsmi(this,
33 rhs_known_smi ? Label::kDeferred : Label::kNonDeferred);
34 Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi);
35
36 BIND(&if_lhsissmi);
37 {
38 Comment("lhs is Smi");
39 TNode<Smi> lhs_smi = CAST(lhs);
40 if (!rhs_known_smi) {
41 // Check if the {rhs} is also a Smi.
42 Label if_rhsissmi(this), if_rhsisnotsmi(this);
43 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
44
45 BIND(&if_rhsisnotsmi);
46 {
47 // Check if the {rhs} is a HeapNumber.
48 TNode<HeapObject> rhs_heap_object = CAST(rhs);
49 GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball);
50
51 var_fadd_lhs = SmiToFloat64(lhs_smi);
52 var_fadd_rhs = LoadHeapNumberValue(rhs_heap_object);
53 Goto(&do_fadd);
54 }
55
56 BIND(&if_rhsissmi);
57 }
58
59 {
60 Comment("perform smi operation");
61 // If rhs is known to be an Smi we want to fast path Smi operation. This
62 // is for AddSmi operation. For the normal Add operation, we want to fast
63 // path both Smi and Number operations, so this path should not be marked
64 // as Deferred.
65 TNode<Smi> rhs_smi = CAST(rhs);
66 Label if_overflow(this,
67 rhs_known_smi ? Label::kDeferred : Label::kNonDeferred);
68 TNode<Smi> smi_result = TrySmiAdd(lhs_smi, rhs_smi, &if_overflow);
69 // Not overflowed.
70 {
71 var_type_feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall);
72 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(),
73 slot_id, update_feedback_mode);
74 var_result = smi_result;
75 Goto(&end);
76 }
77
78 BIND(&if_overflow);
79 {
80 var_fadd_lhs = SmiToFloat64(lhs_smi);
81 var_fadd_rhs = SmiToFloat64(rhs_smi);
82 Goto(&do_fadd);
83 }
84 }
85 }
86
87 BIND(&if_lhsisnotsmi);
88 {
89 // Check if {lhs} is a HeapNumber.
90 TNode<HeapObject> lhs_heap_object = CAST(lhs);
91 GotoIfNot(IsHeapNumber(lhs_heap_object), &if_lhsisnotnumber);
92
93 if (!rhs_known_smi) {
94 // Check if the {rhs} is Smi.
95 Label if_rhsissmi(this), if_rhsisnotsmi(this);
96 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
97
98 BIND(&if_rhsisnotsmi);
99 {
100 // Check if the {rhs} is a HeapNumber.
101 TNode<HeapObject> rhs_heap_object = CAST(rhs);
102 GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball);
103
104 var_fadd_lhs = LoadHeapNumberValue(lhs_heap_object);
105 var_fadd_rhs = LoadHeapNumberValue(rhs_heap_object);
106 Goto(&do_fadd);
107 }
108
109 BIND(&if_rhsissmi);
110 }
111 {
112 var_fadd_lhs = LoadHeapNumberValue(lhs_heap_object);
113 var_fadd_rhs = SmiToFloat64(CAST(rhs));
114 Goto(&do_fadd);
115 }
116 }
117
118 BIND(&do_fadd);
119 {
120 var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber);
121 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
122 update_feedback_mode);
123 TNode<Float64T> value =
124 Float64Add(var_fadd_lhs.value(), var_fadd_rhs.value());
125 TNode<HeapNumber> result = AllocateHeapNumberWithValue(value);
126 var_result = result;
127 Goto(&end);
128 }
129
130 BIND(&if_lhsisnotnumber);
131 {
132 // No checks on rhs are done yet. We just know lhs is not a number or Smi.
133 Label if_lhsisoddball(this), if_lhsisnotoddball(this);
134 TNode<Uint16T> lhs_instance_type = LoadInstanceType(CAST(lhs));
135 TNode<BoolT> lhs_is_oddball =
136 InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE);
137 Branch(lhs_is_oddball, &if_lhsisoddball, &if_lhsisnotoddball);
138
139 BIND(&if_lhsisoddball);
140 {
141 GotoIf(TaggedIsSmi(rhs), &call_with_oddball_feedback);
142
143 // Check if {rhs} is a HeapNumber.
144 Branch(IsHeapNumber(CAST(rhs)), &call_with_oddball_feedback,
145 &check_rhsisoddball);
146 }
147
148 BIND(&if_lhsisnotoddball);
149 {
150 // Check if the {rhs} is a smi, and exit the string and bigint check early
151 // if it is.
152 GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
153 TNode<HeapObject> rhs_heap_object = CAST(rhs);
154
155 Label lhs_is_string(this), lhs_is_bigint(this);
156 GotoIf(IsStringInstanceType(lhs_instance_type), &lhs_is_string);
157 GotoIf(IsBigIntInstanceType(lhs_instance_type), &lhs_is_bigint);
158 Goto(&call_with_any_feedback);
159
160 BIND(&lhs_is_bigint);
161 Branch(IsBigInt(rhs_heap_object), &bigint, &call_with_any_feedback);
162
163 BIND(&lhs_is_string);
164 {
165 TNode<Uint16T> rhs_instance_type = LoadInstanceType(rhs_heap_object);
166
167 // Exit unless {rhs} is a string. Since {lhs} is a string we no longer
168 // need an Oddball check.
169 GotoIfNot(IsStringInstanceType(rhs_instance_type),
170 &call_with_any_feedback);
171
172 var_type_feedback = SmiConstant(BinaryOperationFeedback::kString);
173 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(),
174 slot_id, update_feedback_mode);
175 var_result =
176 CallBuiltin(Builtin::kStringAdd_CheckNone, context(), lhs, rhs);
177
178 Goto(&end);
179 }
180 }
181 }
182
183 BIND(&check_rhsisoddball);
184 {
185 // Check if rhs is an oddball. At this point we know lhs is either a
186 // Smi or number or oddball and rhs is not a number or Smi.
187 TNode<Uint16T> rhs_instance_type = LoadInstanceType(CAST(rhs));
188 TNode<BoolT> rhs_is_oddball =
189 InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE);
190 GotoIf(rhs_is_oddball, &call_with_oddball_feedback);
191 Goto(&call_with_any_feedback);
192 }
193
194 BIND(&bigint);
195 {
196 // Both {lhs} and {rhs} are of BigInt type.
197 Label bigint_too_big(this);
198 var_result = CallBuiltin(Builtin::kBigIntAddNoThrow, context(), lhs, rhs);
199 // Check for sentinel that signals BigIntTooBig exception.
200 GotoIf(TaggedIsSmi(var_result.value()), &bigint_too_big);
201
202 var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt);
203 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
204 update_feedback_mode);
205 Goto(&end);
206
207 BIND(&bigint_too_big);
208 {
209 // Update feedback to prevent deopt loop.
210 UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny),
211 maybe_feedback_vector(), slot_id, update_feedback_mode);
212 ThrowRangeError(context(), MessageTemplate::kBigIntTooBig);
213 }
214 }
215
216 BIND(&call_with_oddball_feedback);
217 {
218 var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumberOrOddball);
219 Goto(&call_add_stub);
220 }
221
222 BIND(&call_with_any_feedback);
223 {
224 var_type_feedback = SmiConstant(BinaryOperationFeedback::kAny);
225 Goto(&call_add_stub);
226 }
227
228 BIND(&call_add_stub);
229 {
230 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
231 update_feedback_mode);
232 var_result = CallBuiltin(Builtin::kAdd, context(), lhs, rhs);
233 Goto(&end);
234 }
235
236 BIND(&end);
237 return var_result.value();
238 }
239
Generate_BinaryOperationWithFeedback(const LazyNode<Context> & context,TNode<Object> lhs,TNode<Object> rhs,TNode<UintPtrT> slot_id,const LazyNode<HeapObject> & maybe_feedback_vector,const SmiOperation & smiOperation,const FloatOperation & floatOperation,Operation op,UpdateFeedbackMode update_feedback_mode,bool rhs_known_smi)240 TNode<Object> BinaryOpAssembler::Generate_BinaryOperationWithFeedback(
241 const LazyNode<Context>& context, TNode<Object> lhs, TNode<Object> rhs,
242 TNode<UintPtrT> slot_id, const LazyNode<HeapObject>& maybe_feedback_vector,
243 const SmiOperation& smiOperation, const FloatOperation& floatOperation,
244 Operation op, UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) {
245 Label do_float_operation(this), end(this), call_stub(this),
246 check_rhsisoddball(this, Label::kDeferred), call_with_any_feedback(this),
247 if_lhsisnotnumber(this, Label::kDeferred),
248 if_both_bigint(this, Label::kDeferred);
249 TVARIABLE(Float64T, var_float_lhs);
250 TVARIABLE(Float64T, var_float_rhs);
251 TVARIABLE(Smi, var_type_feedback);
252 TVARIABLE(Object, var_result);
253
254 Label if_lhsissmi(this);
255 // If rhs is known to be an Smi (in the SubSmi, MulSmi, DivSmi, ModSmi
256 // bytecode handlers) we want to fast path Smi operation. For the normal
257 // operation, we want to fast path both Smi and Number operations, so this
258 // path should not be marked as Deferred.
259 Label if_lhsisnotsmi(this,
260 rhs_known_smi ? Label::kDeferred : Label::kNonDeferred);
261 Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi);
262
263 // Check if the {lhs} is a Smi or a HeapObject.
264 BIND(&if_lhsissmi);
265 {
266 Comment("lhs is Smi");
267 TNode<Smi> lhs_smi = CAST(lhs);
268 if (!rhs_known_smi) {
269 // Check if the {rhs} is also a Smi.
270 Label if_rhsissmi(this), if_rhsisnotsmi(this);
271 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
272
273 BIND(&if_rhsisnotsmi);
274 {
275 // Check if {rhs} is a HeapNumber.
276 TNode<HeapObject> rhs_heap_object = CAST(rhs);
277 GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball);
278
279 // Perform a floating point operation.
280 var_float_lhs = SmiToFloat64(lhs_smi);
281 var_float_rhs = LoadHeapNumberValue(rhs_heap_object);
282 Goto(&do_float_operation);
283 }
284
285 BIND(&if_rhsissmi);
286 }
287
288 {
289 Comment("perform smi operation");
290 var_result = smiOperation(lhs_smi, CAST(rhs), &var_type_feedback);
291 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(),
292 slot_id, update_feedback_mode);
293 Goto(&end);
294 }
295 }
296
297 BIND(&if_lhsisnotsmi);
298 {
299 Comment("lhs is not Smi");
300 // Check if the {lhs} is a HeapNumber.
301 TNode<HeapObject> lhs_heap_object = CAST(lhs);
302 GotoIfNot(IsHeapNumber(lhs_heap_object), &if_lhsisnotnumber);
303
304 if (!rhs_known_smi) {
305 // Check if the {rhs} is a Smi.
306 Label if_rhsissmi(this), if_rhsisnotsmi(this);
307 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
308
309 BIND(&if_rhsisnotsmi);
310 {
311 // Check if the {rhs} is a HeapNumber.
312 TNode<HeapObject> rhs_heap_object = CAST(rhs);
313 GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball);
314
315 // Perform a floating point operation.
316 var_float_lhs = LoadHeapNumberValue(lhs_heap_object);
317 var_float_rhs = LoadHeapNumberValue(rhs_heap_object);
318 Goto(&do_float_operation);
319 }
320
321 BIND(&if_rhsissmi);
322 }
323
324 {
325 // Perform floating point operation.
326 var_float_lhs = LoadHeapNumberValue(lhs_heap_object);
327 var_float_rhs = SmiToFloat64(CAST(rhs));
328 Goto(&do_float_operation);
329 }
330 }
331
332 BIND(&do_float_operation);
333 {
334 var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber);
335 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
336 update_feedback_mode);
337 TNode<Float64T> lhs_value = var_float_lhs.value();
338 TNode<Float64T> rhs_value = var_float_rhs.value();
339 TNode<Float64T> value = floatOperation(lhs_value, rhs_value);
340 var_result = AllocateHeapNumberWithValue(value);
341 Goto(&end);
342 }
343
344 BIND(&if_lhsisnotnumber);
345 {
346 // No checks on rhs are done yet. We just know lhs is not a number or Smi.
347 Label if_left_bigint(this), if_left_oddball(this);
348 TNode<Uint16T> lhs_instance_type = LoadInstanceType(CAST(lhs));
349 GotoIf(IsBigIntInstanceType(lhs_instance_type), &if_left_bigint);
350 TNode<BoolT> lhs_is_oddball =
351 InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE);
352 Branch(lhs_is_oddball, &if_left_oddball, &call_with_any_feedback);
353
354 BIND(&if_left_oddball);
355 {
356 Label if_rhsissmi(this), if_rhsisnotsmi(this);
357 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
358
359 BIND(&if_rhsissmi);
360 {
361 var_type_feedback =
362 SmiConstant(BinaryOperationFeedback::kNumberOrOddball);
363 Goto(&call_stub);
364 }
365
366 BIND(&if_rhsisnotsmi);
367 {
368 // Check if {rhs} is a HeapNumber.
369 GotoIfNot(IsHeapNumber(CAST(rhs)), &check_rhsisoddball);
370
371 var_type_feedback =
372 SmiConstant(BinaryOperationFeedback::kNumberOrOddball);
373 Goto(&call_stub);
374 }
375 }
376
377 BIND(&if_left_bigint);
378 {
379 GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
380 Branch(IsBigInt(CAST(rhs)), &if_both_bigint, &call_with_any_feedback);
381 }
382 }
383
384 BIND(&check_rhsisoddball);
385 {
386 // Check if rhs is an oddball. At this point we know lhs is either a
387 // Smi or number or oddball and rhs is not a number or Smi.
388 TNode<Uint16T> rhs_instance_type = LoadInstanceType(CAST(rhs));
389 TNode<BoolT> rhs_is_oddball =
390 InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE);
391 GotoIfNot(rhs_is_oddball, &call_with_any_feedback);
392
393 var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumberOrOddball);
394 Goto(&call_stub);
395 }
396
397 BIND(&if_both_bigint);
398 {
399 var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt);
400 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
401 update_feedback_mode);
402 if (op == Operation::kSubtract) {
403 Label bigint_too_big(this);
404 var_result =
405 CallBuiltin(Builtin::kBigIntSubtractNoThrow, context(), lhs, rhs);
406
407 // Check for sentinel that signals BigIntTooBig exception.
408 GotoIf(TaggedIsSmi(var_result.value()), &bigint_too_big);
409 Goto(&end);
410
411 BIND(&bigint_too_big);
412 {
413 // Update feedback to prevent deopt loop.
414 UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny),
415 maybe_feedback_vector(), slot_id, update_feedback_mode);
416 ThrowRangeError(context(), MessageTemplate::kBigIntTooBig);
417 }
418 } else {
419 var_result = CallRuntime(Runtime::kBigIntBinaryOp, context(), lhs, rhs,
420 SmiConstant(op));
421 Goto(&end);
422 }
423 }
424
425 BIND(&call_with_any_feedback);
426 {
427 var_type_feedback = SmiConstant(BinaryOperationFeedback::kAny);
428 Goto(&call_stub);
429 }
430
431 BIND(&call_stub);
432 {
433 UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id,
434 update_feedback_mode);
435 TNode<Object> result;
436 switch (op) {
437 case Operation::kSubtract:
438 result = CallBuiltin(Builtin::kSubtract, context(), lhs, rhs);
439 break;
440 case Operation::kMultiply:
441 result = CallBuiltin(Builtin::kMultiply, context(), lhs, rhs);
442 break;
443 case Operation::kDivide:
444 result = CallBuiltin(Builtin::kDivide, context(), lhs, rhs);
445 break;
446 case Operation::kModulus:
447 result = CallBuiltin(Builtin::kModulus, context(), lhs, rhs);
448 break;
449 case Operation::kExponentiate:
450 result = CallBuiltin(Builtin::kExponentiate, context(), lhs, rhs);
451 break;
452 default:
453 UNREACHABLE();
454 }
455 var_result = result;
456 Goto(&end);
457 }
458
459 BIND(&end);
460 return var_result.value();
461 }
462
Generate_SubtractWithFeedback(const LazyNode<Context> & context,TNode<Object> lhs,TNode<Object> rhs,TNode<UintPtrT> slot_id,const LazyNode<HeapObject> & maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode,bool rhs_known_smi)463 TNode<Object> BinaryOpAssembler::Generate_SubtractWithFeedback(
464 const LazyNode<Context>& context, TNode<Object> lhs, TNode<Object> rhs,
465 TNode<UintPtrT> slot_id, const LazyNode<HeapObject>& maybe_feedback_vector,
466 UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) {
467 auto smiFunction = [=](TNode<Smi> lhs, TNode<Smi> rhs,
468 TVariable<Smi>* var_type_feedback) {
469 Label end(this);
470 TVARIABLE(Number, var_result);
471 // If rhs is known to be an Smi (for SubSmi) we want to fast path Smi
472 // operation. For the normal Sub operation, we want to fast path both
473 // Smi and Number operations, so this path should not be marked as Deferred.
474 Label if_overflow(this,
475 rhs_known_smi ? Label::kDeferred : Label::kNonDeferred);
476 var_result = TrySmiSub(lhs, rhs, &if_overflow);
477 *var_type_feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall);
478 Goto(&end);
479
480 BIND(&if_overflow);
481 {
482 *var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber);
483 TNode<Float64T> value = Float64Sub(SmiToFloat64(lhs), SmiToFloat64(rhs));
484 var_result = AllocateHeapNumberWithValue(value);
485 Goto(&end);
486 }
487
488 BIND(&end);
489 return var_result.value();
490 };
491 auto floatFunction = [=](TNode<Float64T> lhs, TNode<Float64T> rhs) {
492 return Float64Sub(lhs, rhs);
493 };
494 return Generate_BinaryOperationWithFeedback(
495 context, lhs, rhs, slot_id, maybe_feedback_vector, smiFunction,
496 floatFunction, Operation::kSubtract, update_feedback_mode, rhs_known_smi);
497 }
498
Generate_MultiplyWithFeedback(const LazyNode<Context> & context,TNode<Object> lhs,TNode<Object> rhs,TNode<UintPtrT> slot_id,const LazyNode<HeapObject> & maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode,bool rhs_known_smi)499 TNode<Object> BinaryOpAssembler::Generate_MultiplyWithFeedback(
500 const LazyNode<Context>& context, TNode<Object> lhs, TNode<Object> rhs,
501 TNode<UintPtrT> slot_id, const LazyNode<HeapObject>& maybe_feedback_vector,
502 UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) {
503 auto smiFunction = [=](TNode<Smi> lhs, TNode<Smi> rhs,
504 TVariable<Smi>* var_type_feedback) {
505 TNode<Number> result = SmiMul(lhs, rhs);
506 *var_type_feedback = SelectSmiConstant(
507 TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall,
508 BinaryOperationFeedback::kNumber);
509 return result;
510 };
511 auto floatFunction = [=](TNode<Float64T> lhs, TNode<Float64T> rhs) {
512 return Float64Mul(lhs, rhs);
513 };
514 return Generate_BinaryOperationWithFeedback(
515 context, lhs, rhs, slot_id, maybe_feedback_vector, smiFunction,
516 floatFunction, Operation::kMultiply, update_feedback_mode, rhs_known_smi);
517 }
518
Generate_DivideWithFeedback(const LazyNode<Context> & context,TNode<Object> dividend,TNode<Object> divisor,TNode<UintPtrT> slot_id,const LazyNode<HeapObject> & maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode,bool rhs_known_smi)519 TNode<Object> BinaryOpAssembler::Generate_DivideWithFeedback(
520 const LazyNode<Context>& context, TNode<Object> dividend,
521 TNode<Object> divisor, TNode<UintPtrT> slot_id,
522 const LazyNode<HeapObject>& maybe_feedback_vector,
523 UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) {
524 auto smiFunction = [=](TNode<Smi> lhs, TNode<Smi> rhs,
525 TVariable<Smi>* var_type_feedback) {
526 TVARIABLE(Object, var_result);
527 // If rhs is known to be an Smi (for DivSmi) we want to fast path Smi
528 // operation. For the normal Div operation, we want to fast path both
529 // Smi and Number operations, so this path should not be marked as Deferred.
530 Label bailout(this, rhs_known_smi ? Label::kDeferred : Label::kNonDeferred),
531 end(this);
532 var_result = TrySmiDiv(lhs, rhs, &bailout);
533 *var_type_feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall);
534 Goto(&end);
535
536 BIND(&bailout);
537 {
538 *var_type_feedback =
539 SmiConstant(BinaryOperationFeedback::kSignedSmallInputs);
540 TNode<Float64T> value = Float64Div(SmiToFloat64(lhs), SmiToFloat64(rhs));
541 var_result = AllocateHeapNumberWithValue(value);
542 Goto(&end);
543 }
544
545 BIND(&end);
546 return var_result.value();
547 };
548 auto floatFunction = [=](TNode<Float64T> lhs, TNode<Float64T> rhs) {
549 return Float64Div(lhs, rhs);
550 };
551 return Generate_BinaryOperationWithFeedback(
552 context, dividend, divisor, slot_id, maybe_feedback_vector, smiFunction,
553 floatFunction, Operation::kDivide, update_feedback_mode, rhs_known_smi);
554 }
555
Generate_ModulusWithFeedback(const LazyNode<Context> & context,TNode<Object> dividend,TNode<Object> divisor,TNode<UintPtrT> slot_id,const LazyNode<HeapObject> & maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode,bool rhs_known_smi)556 TNode<Object> BinaryOpAssembler::Generate_ModulusWithFeedback(
557 const LazyNode<Context>& context, TNode<Object> dividend,
558 TNode<Object> divisor, TNode<UintPtrT> slot_id,
559 const LazyNode<HeapObject>& maybe_feedback_vector,
560 UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) {
561 auto smiFunction = [=](TNode<Smi> lhs, TNode<Smi> rhs,
562 TVariable<Smi>* var_type_feedback) {
563 TNode<Number> result = SmiMod(lhs, rhs);
564 *var_type_feedback = SelectSmiConstant(
565 TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall,
566 BinaryOperationFeedback::kNumber);
567 return result;
568 };
569 auto floatFunction = [=](TNode<Float64T> lhs, TNode<Float64T> rhs) {
570 return Float64Mod(lhs, rhs);
571 };
572 return Generate_BinaryOperationWithFeedback(
573 context, dividend, divisor, slot_id, maybe_feedback_vector, smiFunction,
574 floatFunction, Operation::kModulus, update_feedback_mode, rhs_known_smi);
575 }
576
Generate_ExponentiateWithFeedback(const LazyNode<Context> & context,TNode<Object> base,TNode<Object> exponent,TNode<UintPtrT> slot_id,const LazyNode<HeapObject> & maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode,bool rhs_known_smi)577 TNode<Object> BinaryOpAssembler::Generate_ExponentiateWithFeedback(
578 const LazyNode<Context>& context, TNode<Object> base,
579 TNode<Object> exponent, TNode<UintPtrT> slot_id,
580 const LazyNode<HeapObject>& maybe_feedback_vector,
581 UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) {
582 auto smiFunction = [=](TNode<Smi> base, TNode<Smi> exponent,
583 TVariable<Smi>* var_type_feedback) {
584 *var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber);
585 return AllocateHeapNumberWithValue(
586 Float64Pow(SmiToFloat64(base), SmiToFloat64(exponent)));
587 };
588 auto floatFunction = [=](TNode<Float64T> base, TNode<Float64T> exponent) {
589 return Float64Pow(base, exponent);
590 };
591 return Generate_BinaryOperationWithFeedback(
592 context, base, exponent, slot_id, maybe_feedback_vector, smiFunction,
593 floatFunction, Operation::kExponentiate, update_feedback_mode,
594 rhs_known_smi);
595 }
596
Generate_BitwiseBinaryOpWithOptionalFeedback(Operation bitwise_op,TNode<Object> left,TNode<Object> right,const LazyNode<Context> & context,TNode<UintPtrT> * slot,const LazyNode<HeapObject> * maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)597 TNode<Object> BinaryOpAssembler::Generate_BitwiseBinaryOpWithOptionalFeedback(
598 Operation bitwise_op, TNode<Object> left, TNode<Object> right,
599 const LazyNode<Context>& context, TNode<UintPtrT>* slot,
600 const LazyNode<HeapObject>* maybe_feedback_vector,
601 UpdateFeedbackMode update_feedback_mode) {
602 TVARIABLE(Object, result);
603 TVARIABLE(Smi, var_left_feedback);
604 TVARIABLE(Smi, var_right_feedback);
605 TVARIABLE(Word32T, var_left_word32);
606 TVARIABLE(Word32T, var_right_word32);
607 TVARIABLE(BigInt, var_left_bigint);
608 TVARIABLE(BigInt, var_right_bigint);
609 // These are the variables that are passed to BigIntBinaryOp. They are not
610 // guaranteed to be BigInts because the Runtime call handles throwing
611 // exceptions when only one side is a BigInt.
612 TVARIABLE(Object, var_left_maybe_bigint, left);
613 TVARIABLE(Numeric, var_right_maybe_bigint);
614 Label done(this);
615 Label if_left_number(this), do_number_op(this);
616 Label if_left_bigint(this), do_bigint_op(this);
617
618 TaggedToWord32OrBigIntWithFeedback(
619 context(), left, &if_left_number, &var_left_word32, &if_left_bigint,
620 &var_left_bigint, slot ? &var_left_feedback : nullptr);
621
622 Label right_is_bigint(this);
623 BIND(&if_left_number);
624 {
625 TaggedToWord32OrBigIntWithFeedback(
626 context(), right, &do_number_op, &var_right_word32, &right_is_bigint,
627 &var_right_bigint, slot ? &var_right_feedback : nullptr);
628 }
629
630 BIND(&right_is_bigint);
631 {
632 // At this point it's guaranteed that the op will fail because the RHS is a
633 // BigInt while the LHS is not, but that's ok because the Runtime call will
634 // throw the exception.
635 var_right_maybe_bigint = var_right_bigint.value();
636 Goto(&do_bigint_op);
637 }
638
639 BIND(&do_number_op);
640 {
641 result = BitwiseOp(var_left_word32.value(), var_right_word32.value(),
642 bitwise_op);
643
644 if (slot) {
645 TNode<Smi> result_type = SelectSmiConstant(
646 TaggedIsSmi(result.value()), BinaryOperationFeedback::kSignedSmall,
647 BinaryOperationFeedback::kNumber);
648 TNode<Smi> input_feedback =
649 SmiOr(var_left_feedback.value(), var_right_feedback.value());
650 TNode<Smi> feedback = SmiOr(result_type, input_feedback);
651 UpdateFeedback(feedback, (*maybe_feedback_vector)(), *slot,
652 update_feedback_mode);
653 }
654 Goto(&done);
655 }
656
657 // BigInt cases.
658 BIND(&if_left_bigint);
659 {
660 TaggedToNumericWithFeedback(context(), right, &var_right_maybe_bigint,
661 &var_right_feedback);
662 var_left_maybe_bigint = var_left_bigint.value();
663 Goto(&do_bigint_op);
664 }
665
666 BIND(&do_bigint_op);
667 {
668 if (slot) {
669 // Ensure that the feedback is updated even if the runtime call below
670 // would throw.
671 TNode<Smi> feedback =
672 SmiOr(var_left_feedback.value(), var_right_feedback.value());
673 UpdateFeedback(feedback, (*maybe_feedback_vector)(), *slot,
674 update_feedback_mode);
675 }
676
677 result = CallRuntime(
678 Runtime::kBigIntBinaryOp, context(), var_left_maybe_bigint.value(),
679 var_right_maybe_bigint.value(), SmiConstant(bitwise_op));
680 Goto(&done);
681 }
682
683 BIND(&done);
684 return result.value();
685 }
686
687 TNode<Object>
Generate_BitwiseBinaryOpWithSmiOperandAndOptionalFeedback(Operation bitwise_op,TNode<Object> left,TNode<Object> right,const LazyNode<Context> & context,TNode<UintPtrT> * slot,const LazyNode<HeapObject> * maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)688 BinaryOpAssembler::Generate_BitwiseBinaryOpWithSmiOperandAndOptionalFeedback(
689 Operation bitwise_op, TNode<Object> left, TNode<Object> right,
690 const LazyNode<Context>& context, TNode<UintPtrT>* slot,
691 const LazyNode<HeapObject>* maybe_feedback_vector,
692 UpdateFeedbackMode update_feedback_mode) {
693 TNode<Smi> right_smi = CAST(right);
694 TVARIABLE(Object, result);
695 TVARIABLE(Smi, var_left_feedback);
696 TVARIABLE(Word32T, var_left_word32);
697 TVARIABLE(BigInt, var_left_bigint);
698 TVARIABLE(Smi, feedback);
699 // Check if the {lhs} is a Smi or a HeapObject.
700 Label if_lhsissmi(this), if_lhsisnotsmi(this, Label::kDeferred);
701 Label do_number_op(this), if_bigint_mix(this), done(this);
702
703 Branch(TaggedIsSmi(left), &if_lhsissmi, &if_lhsisnotsmi);
704
705 BIND(&if_lhsissmi);
706 {
707 TNode<Smi> left_smi = CAST(left);
708 result = BitwiseSmiOp(left_smi, right_smi, bitwise_op);
709 if (slot) {
710 if (IsBitwiseOutputKnownSmi(bitwise_op)) {
711 feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall);
712 } else {
713 feedback = SelectSmiConstant(TaggedIsSmi(result.value()),
714 BinaryOperationFeedback::kSignedSmall,
715 BinaryOperationFeedback::kNumber);
716 }
717 }
718 Goto(&done);
719 }
720
721 BIND(&if_lhsisnotsmi);
722 {
723 TNode<HeapObject> left_pointer = CAST(left);
724 TaggedPointerToWord32OrBigIntWithFeedback(
725 context(), left_pointer, &do_number_op, &var_left_word32,
726 &if_bigint_mix, &var_left_bigint, &var_left_feedback);
727 BIND(&do_number_op);
728 {
729 result =
730 BitwiseOp(var_left_word32.value(), SmiToInt32(right_smi), bitwise_op);
731 if (slot) {
732 TNode<Smi> result_type = SelectSmiConstant(
733 TaggedIsSmi(result.value()), BinaryOperationFeedback::kSignedSmall,
734 BinaryOperationFeedback::kNumber);
735 feedback = SmiOr(result_type, var_left_feedback.value());
736 }
737 Goto(&done);
738 }
739
740 BIND(&if_bigint_mix);
741 {
742 if (slot) {
743 // Ensure that the feedback is updated before we throw.
744 feedback = var_left_feedback.value();
745 UpdateFeedback(feedback.value(), (*maybe_feedback_vector)(), *slot,
746 update_feedback_mode);
747 }
748 ThrowTypeError(context(), MessageTemplate::kBigIntMixedTypes);
749 }
750 }
751
752 BIND(&done);
753 UpdateFeedback(feedback.value(), (*maybe_feedback_vector)(), *slot,
754 update_feedback_mode);
755 return result.value();
756 }
757
758 } // namespace internal
759 } // namespace v8
760