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,TVariable<Smi> * feedback)597 TNode<Object> BinaryOpAssembler::Generate_BitwiseBinaryOpWithOptionalFeedback(
598     Operation bitwise_op, TNode<Object> left, TNode<Object> right,
599     const LazyNode<Context>& context, TVariable<Smi>* feedback) {
600   TVARIABLE(Object, result);
601   TVARIABLE(Smi, var_left_feedback);
602   TVARIABLE(Smi, var_right_feedback);
603   TVARIABLE(Word32T, var_left_word32);
604   TVARIABLE(Word32T, var_right_word32);
605   TVARIABLE(BigInt, var_left_bigint);
606   TVARIABLE(BigInt, var_right_bigint);
607   // These are the variables that are passed to BigIntBinaryOp. They are not
608   // guaranteed to be BigInts because the Runtime call handles throwing
609   // exceptions when only one side is a BigInt.
610   TVARIABLE(Object, var_left_maybe_bigint, left);
611   TVARIABLE(Numeric, var_right_maybe_bigint);
612   Label done(this);
613   Label if_left_number(this), do_number_op(this);
614   Label if_left_bigint(this), do_bigint_op(this);
615 
616   TaggedToWord32OrBigIntWithFeedback(
617       context(), left, &if_left_number, &var_left_word32, &if_left_bigint,
618       &var_left_bigint, feedback ? &var_left_feedback : nullptr);
619 
620   Label right_is_bigint(this);
621   BIND(&if_left_number);
622   {
623     TaggedToWord32OrBigIntWithFeedback(
624         context(), right, &do_number_op, &var_right_word32, &right_is_bigint,
625         &var_right_bigint, feedback ? &var_right_feedback : nullptr);
626   }
627 
628   BIND(&right_is_bigint);
629   {
630     // At this point it's guaranteed that the op will fail because the RHS is a
631     // BigInt while the LHS is not, but that's ok because the Runtime call will
632     // throw the exception.
633     var_right_maybe_bigint = var_right_bigint.value();
634     Goto(&do_bigint_op);
635   }
636 
637   BIND(&do_number_op);
638   {
639     result = BitwiseOp(var_left_word32.value(), var_right_word32.value(),
640                        bitwise_op);
641 
642     if (feedback) {
643       TNode<Smi> result_type = SelectSmiConstant(
644           TaggedIsSmi(result.value()), BinaryOperationFeedback::kSignedSmall,
645           BinaryOperationFeedback::kNumber);
646       TNode<Smi> input_feedback =
647           SmiOr(var_left_feedback.value(), var_right_feedback.value());
648       *feedback = SmiOr(result_type, input_feedback);
649     }
650     Goto(&done);
651   }
652 
653   // BigInt cases.
654   BIND(&if_left_bigint);
655   {
656     TaggedToNumericWithFeedback(context(), right, &var_right_maybe_bigint,
657                                 &var_right_feedback);
658     var_left_maybe_bigint = var_left_bigint.value();
659     Goto(&do_bigint_op);
660   }
661 
662   BIND(&do_bigint_op);
663   {
664     if (feedback) {
665       *feedback = SmiOr(var_left_feedback.value(), var_right_feedback.value());
666     }
667     result = CallRuntime(
668         Runtime::kBigIntBinaryOp, context(), var_left_maybe_bigint.value(),
669         var_right_maybe_bigint.value(), SmiConstant(bitwise_op));
670     Goto(&done);
671   }
672 
673   BIND(&done);
674   return result.value();
675 }
676 
677 }  // namespace internal
678 }  // namespace v8
679