V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
binary-op-assembler.cc
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/globals.h"
8 
9 namespace v8 {
10 namespace internal {
11 
12 using compiler::Node;
13 
14 Node* BinaryOpAssembler::Generate_AddWithFeedback(Node* context, Node* lhs,
15  Node* rhs, Node* slot_id,
16  Node* feedback_vector,
17  bool rhs_is_smi) {
18  // Shared entry for floating point addition.
19  Label do_fadd(this), if_lhsisnotnumber(this, Label::kDeferred),
20  check_rhsisoddball(this, Label::kDeferred),
21  call_with_oddball_feedback(this), call_with_any_feedback(this),
22  call_add_stub(this), end(this), bigint(this, Label::kDeferred);
23  VARIABLE(var_fadd_lhs, MachineRepresentation::kFloat64);
24  VARIABLE(var_fadd_rhs, MachineRepresentation::kFloat64);
25  VARIABLE(var_type_feedback, MachineRepresentation::kTaggedSigned);
26  VARIABLE(var_result, MachineRepresentation::kTagged);
27 
28  // Check if the {lhs} is a Smi or a HeapObject.
29  Label if_lhsissmi(this);
30  // If rhs is known to be an Smi we want to fast path Smi operation. This is
31  // for AddSmi operation. For the normal Add operation, we want to fast path
32  // both Smi and Number operations, so this path should not be marked as
33  // Deferred.
34  Label if_lhsisnotsmi(this,
35  rhs_is_smi ? Label::kDeferred : Label::kNonDeferred);
36  Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi);
37 
38  BIND(&if_lhsissmi);
39  {
40  Comment("lhs is Smi");
41  if (!rhs_is_smi) {
42  // Check if the {rhs} is also a Smi.
43  Label if_rhsissmi(this), if_rhsisnotsmi(this);
44  Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
45 
46  BIND(&if_rhsisnotsmi);
47  {
48  // Check if the {rhs} is a HeapNumber.
49  GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);
50 
51  var_fadd_lhs.Bind(SmiToFloat64(lhs));
52  var_fadd_rhs.Bind(LoadHeapNumberValue(rhs));
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  Label if_overflow(this,
66  rhs_is_smi ? Label::kDeferred : Label::kNonDeferred);
67  TNode<Smi> smi_result = TrySmiAdd(CAST(lhs), CAST(rhs), &if_overflow);
68  // Not overflowed.
69  {
70  var_type_feedback.Bind(
71  SmiConstant(BinaryOperationFeedback::kSignedSmall));
72  var_result.Bind(smi_result);
73  Goto(&end);
74  }
75 
76  BIND(&if_overflow);
77  {
78  var_fadd_lhs.Bind(SmiToFloat64(lhs));
79  var_fadd_rhs.Bind(SmiToFloat64(rhs));
80  Goto(&do_fadd);
81  }
82  }
83  }
84 
85  BIND(&if_lhsisnotsmi);
86  {
87  // Check if {lhs} is a HeapNumber.
88  GotoIfNot(IsHeapNumber(lhs), &if_lhsisnotnumber);
89 
90  if (!rhs_is_smi) {
91  // Check if the {rhs} is Smi.
92  Label if_rhsissmi(this), if_rhsisnotsmi(this);
93  Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
94 
95  BIND(&if_rhsisnotsmi);
96  {
97  // Check if the {rhs} is a HeapNumber.
98  GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);
99 
100  var_fadd_lhs.Bind(LoadHeapNumberValue(lhs));
101  var_fadd_rhs.Bind(LoadHeapNumberValue(rhs));
102  Goto(&do_fadd);
103  }
104 
105  BIND(&if_rhsissmi);
106  }
107  {
108  var_fadd_lhs.Bind(LoadHeapNumberValue(lhs));
109  var_fadd_rhs.Bind(SmiToFloat64(rhs));
110  Goto(&do_fadd);
111  }
112  }
113 
114  BIND(&do_fadd);
115  {
116  var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNumber));
117  Node* value = Float64Add(var_fadd_lhs.value(), var_fadd_rhs.value());
118  Node* result = AllocateHeapNumberWithValue(value);
119  var_result.Bind(result);
120  Goto(&end);
121  }
122 
123  BIND(&if_lhsisnotnumber);
124  {
125  // No checks on rhs are done yet. We just know lhs is not a number or Smi.
126  Label if_lhsisoddball(this), if_lhsisnotoddball(this);
127  Node* lhs_instance_type = LoadInstanceType(lhs);
128  Node* lhs_is_oddball = InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE);
129  Branch(lhs_is_oddball, &if_lhsisoddball, &if_lhsisnotoddball);
130 
131  BIND(&if_lhsisoddball);
132  {
133  GotoIf(TaggedIsSmi(rhs), &call_with_oddball_feedback);
134 
135  // Check if {rhs} is a HeapNumber.
136  Branch(IsHeapNumber(rhs), &call_with_oddball_feedback,
137  &check_rhsisoddball);
138  }
139 
140  BIND(&if_lhsisnotoddball);
141  {
142  Label lhs_is_string(this), lhs_is_bigint(this);
143  GotoIf(IsStringInstanceType(lhs_instance_type), &lhs_is_string);
144  GotoIf(IsBigIntInstanceType(lhs_instance_type), &lhs_is_bigint);
145  Goto(&call_with_any_feedback);
146 
147  BIND(&lhs_is_bigint);
148  {
149  GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
150  Branch(IsBigInt(rhs), &bigint, &call_with_any_feedback);
151  }
152 
153  BIND(&lhs_is_string);
154  // Check if the {rhs} is a smi, and exit the string check early if it is.
155  GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
156 
157  Node* rhs_instance_type = LoadInstanceType(rhs);
158 
159  // Exit unless {rhs} is a string. Since {lhs} is a string we no longer
160  // need an Oddball check.
161  GotoIfNot(IsStringInstanceType(rhs_instance_type),
162  &call_with_any_feedback);
163 
164  var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kString));
165  var_result.Bind(
166  CallBuiltin(Builtins::kStringAdd_CheckNone, context, lhs, rhs));
167 
168  Goto(&end);
169  }
170  }
171 
172  BIND(&check_rhsisoddball);
173  {
174  // Check if rhs is an oddball. At this point we know lhs is either a
175  // Smi or number or oddball and rhs is not a number or Smi.
176  Node* rhs_instance_type = LoadInstanceType(rhs);
177  Node* rhs_is_oddball = InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE);
178  GotoIf(rhs_is_oddball, &call_with_oddball_feedback);
179  Branch(IsBigIntInstanceType(rhs_instance_type), &bigint,
180  &call_with_any_feedback);
181  }
182 
183  BIND(&bigint);
184  {
185  var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kBigInt));
186  var_result.Bind(CallRuntime(Runtime::kBigIntBinaryOp, context, lhs, rhs,
187  SmiConstant(Operation::kAdd)));
188  Goto(&end);
189  }
190 
191  BIND(&call_with_oddball_feedback);
192  {
193  var_type_feedback.Bind(
194  SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
195  Goto(&call_add_stub);
196  }
197 
198  BIND(&call_with_any_feedback);
199  {
200  var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny));
201  Goto(&call_add_stub);
202  }
203 
204  BIND(&call_add_stub);
205  {
206  var_result.Bind(CallBuiltin(Builtins::kAdd, context, lhs, rhs));
207  Goto(&end);
208  }
209 
210  BIND(&end);
211  UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_id);
212  return var_result.value();
213 }
214 
215 Node* BinaryOpAssembler::Generate_BinaryOperationWithFeedback(
216  Node* context, Node* lhs, Node* rhs, Node* slot_id, Node* feedback_vector,
217  const SmiOperation& smiOperation, const FloatOperation& floatOperation,
218  Operation op, bool rhs_is_smi) {
219  Label do_float_operation(this), end(this), call_stub(this),
220  check_rhsisoddball(this, Label::kDeferred), call_with_any_feedback(this),
221  if_lhsisnotnumber(this, Label::kDeferred),
222  if_bigint(this, Label::kDeferred);
223  VARIABLE(var_float_lhs, MachineRepresentation::kFloat64);
224  VARIABLE(var_float_rhs, MachineRepresentation::kFloat64);
225  VARIABLE(var_type_feedback, MachineRepresentation::kTaggedSigned);
226  VARIABLE(var_result, MachineRepresentation::kTagged);
227 
228  Label if_lhsissmi(this);
229  // If rhs is known to be an Smi (in the SubSmi, MulSmi, DivSmi, ModSmi
230  // bytecode handlers) we want to fast path Smi operation. For the normal
231  // operation, we want to fast path both Smi and Number operations, so this
232  // path should not be marked as Deferred.
233  Label if_lhsisnotsmi(this,
234  rhs_is_smi ? Label::kDeferred : Label::kNonDeferred);
235  Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi);
236 
237  // Check if the {lhs} is a Smi or a HeapObject.
238  BIND(&if_lhsissmi);
239  {
240  Comment("lhs is Smi");
241  if (!rhs_is_smi) {
242  // Check if the {rhs} is also a Smi.
243  Label if_rhsissmi(this), if_rhsisnotsmi(this);
244  Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
245  BIND(&if_rhsisnotsmi);
246  {
247  // Check if {rhs} is a HeapNumber.
248  GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);
249 
250  // Perform a floating point operation.
251  var_float_lhs.Bind(SmiToFloat64(lhs));
252  var_float_rhs.Bind(LoadHeapNumberValue(rhs));
253  Goto(&do_float_operation);
254  }
255 
256  BIND(&if_rhsissmi);
257  }
258 
259  {
260  Comment("perform smi operation");
261  var_result.Bind(smiOperation(lhs, rhs, &var_type_feedback));
262  Goto(&end);
263  }
264  }
265 
266  BIND(&if_lhsisnotsmi);
267  {
268  Comment("lhs is not Smi");
269  // Check if the {lhs} is a HeapNumber.
270  GotoIfNot(IsHeapNumber(lhs), &if_lhsisnotnumber);
271 
272  if (!rhs_is_smi) {
273  // Check if the {rhs} is a Smi.
274  Label if_rhsissmi(this), if_rhsisnotsmi(this);
275  Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
276 
277  BIND(&if_rhsisnotsmi);
278  {
279  // Check if the {rhs} is a HeapNumber.
280  GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);
281 
282  // Perform a floating point operation.
283  var_float_lhs.Bind(LoadHeapNumberValue(lhs));
284  var_float_rhs.Bind(LoadHeapNumberValue(rhs));
285  Goto(&do_float_operation);
286  }
287 
288  BIND(&if_rhsissmi);
289  }
290 
291  {
292  // Perform floating point operation.
293  var_float_lhs.Bind(LoadHeapNumberValue(lhs));
294  var_float_rhs.Bind(SmiToFloat64(rhs));
295  Goto(&do_float_operation);
296  }
297  }
298 
299  BIND(&do_float_operation);
300  {
301  var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNumber));
302  Node* lhs_value = var_float_lhs.value();
303  Node* rhs_value = var_float_rhs.value();
304  Node* value = floatOperation(lhs_value, rhs_value);
305  var_result.Bind(AllocateHeapNumberWithValue(value));
306  Goto(&end);
307  }
308 
309  BIND(&if_lhsisnotnumber);
310  {
311  // No checks on rhs are done yet. We just know lhs is not a number or Smi.
312  Label if_left_bigint(this), if_left_oddball(this);
313  Node* lhs_instance_type = LoadInstanceType(lhs);
314  GotoIf(IsBigIntInstanceType(lhs_instance_type), &if_left_bigint);
315  Node* lhs_is_oddball = InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE);
316  Branch(lhs_is_oddball, &if_left_oddball, &call_with_any_feedback);
317 
318  BIND(&if_left_oddball);
319  {
320  Label if_rhsissmi(this), if_rhsisnotsmi(this);
321  Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
322 
323  BIND(&if_rhsissmi);
324  {
325  var_type_feedback.Bind(
326  SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
327  Goto(&call_stub);
328  }
329 
330  BIND(&if_rhsisnotsmi);
331  {
332  // Check if {rhs} is a HeapNumber.
333  GotoIfNot(IsHeapNumber(rhs), &check_rhsisoddball);
334 
335  var_type_feedback.Bind(
336  SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
337  Goto(&call_stub);
338  }
339  }
340 
341  BIND(&if_left_bigint);
342  {
343  GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
344  Branch(IsBigInt(rhs), &if_bigint, &call_with_any_feedback);
345  }
346  }
347 
348  BIND(&check_rhsisoddball);
349  {
350  // Check if rhs is an oddball. At this point we know lhs is either a
351  // Smi or number or oddball and rhs is not a number or Smi.
352  Node* rhs_instance_type = LoadInstanceType(rhs);
353  GotoIf(IsBigIntInstanceType(rhs_instance_type), &if_bigint);
354  Node* rhs_is_oddball = InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE);
355  GotoIfNot(rhs_is_oddball, &call_with_any_feedback);
356 
357  var_type_feedback.Bind(
358  SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
359  Goto(&call_stub);
360  }
361 
362  // This handles the case where at least one input is a BigInt.
363  BIND(&if_bigint);
364  {
365  var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kBigInt));
366  var_result.Bind(CallRuntime(Runtime::kBigIntBinaryOp, context, lhs, rhs,
367  SmiConstant(op)));
368  Goto(&end);
369  }
370 
371  BIND(&call_with_any_feedback);
372  {
373  var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny));
374  Goto(&call_stub);
375  }
376 
377  BIND(&call_stub);
378  {
379  Node* result;
380  switch (op) {
381  case Operation::kSubtract:
382  result = CallBuiltin(Builtins::kSubtract, context, lhs, rhs);
383  break;
384  case Operation::kMultiply:
385  result = CallBuiltin(Builtins::kMultiply, context, lhs, rhs);
386  break;
387  case Operation::kDivide:
388  result = CallBuiltin(Builtins::kDivide, context, lhs, rhs);
389  break;
390  case Operation::kModulus:
391  result = CallBuiltin(Builtins::kModulus, context, lhs, rhs);
392  break;
393  default:
394  UNREACHABLE();
395  }
396  var_result.Bind(result);
397  Goto(&end);
398  }
399 
400  BIND(&end);
401  UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_id);
402  return var_result.value();
403 }
404 
405 Node* BinaryOpAssembler::Generate_SubtractWithFeedback(Node* context, Node* lhs,
406  Node* rhs, Node* slot_id,
407  Node* feedback_vector,
408  bool rhs_is_smi) {
409  auto smiFunction = [=](Node* lhs, Node* rhs, Variable* var_type_feedback) {
410  Label end(this);
411  TVARIABLE(Number, var_result);
412  // If rhs is known to be an Smi (for SubSmi) we want to fast path Smi
413  // operation. For the normal Sub operation, we want to fast path both
414  // Smi and Number operations, so this path should not be marked as Deferred.
415  Label if_overflow(this,
416  rhs_is_smi ? Label::kDeferred : Label::kNonDeferred);
417  var_result = TrySmiSub(CAST(lhs), CAST(rhs), &if_overflow);
418  var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kSignedSmall));
419  Goto(&end);
420 
421  BIND(&if_overflow);
422  {
423  var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kNumber));
424  Node* value = Float64Sub(SmiToFloat64(lhs), SmiToFloat64(rhs));
425  var_result = AllocateHeapNumberWithValue(value);
426  Goto(&end);
427  }
428 
429  BIND(&end);
430  return var_result.value();
431  };
432  auto floatFunction = [=](Node* lhs, Node* rhs) {
433  return Float64Sub(lhs, rhs);
434  };
435  return Generate_BinaryOperationWithFeedback(
436  context, lhs, rhs, slot_id, feedback_vector, smiFunction, floatFunction,
437  Operation::kSubtract, rhs_is_smi);
438 }
439 
440 Node* BinaryOpAssembler::Generate_MultiplyWithFeedback(Node* context, Node* lhs,
441  Node* rhs, Node* slot_id,
442  Node* feedback_vector,
443  bool rhs_is_smi) {
444  auto smiFunction = [=](Node* lhs, Node* rhs, Variable* var_type_feedback) {
445  TNode<Number> result = SmiMul(CAST(lhs), CAST(rhs));
446  var_type_feedback->Bind(SelectSmiConstant(
447  TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall,
448  BinaryOperationFeedback::kNumber));
449  return result;
450  };
451  auto floatFunction = [=](Node* lhs, Node* rhs) {
452  return Float64Mul(lhs, rhs);
453  };
454  return Generate_BinaryOperationWithFeedback(
455  context, lhs, rhs, slot_id, feedback_vector, smiFunction, floatFunction,
456  Operation::kMultiply, rhs_is_smi);
457 }
458 
459 Node* BinaryOpAssembler::Generate_DivideWithFeedback(
460  Node* context, Node* dividend, Node* divisor, Node* slot_id,
461  Node* feedback_vector, bool rhs_is_smi) {
462  auto smiFunction = [=](Node* lhs, Node* rhs, Variable* var_type_feedback) {
463  VARIABLE(var_result, MachineRepresentation::kTagged);
464  // If rhs is known to be an Smi (for DivSmi) we want to fast path Smi
465  // operation. For the normal Div operation, we want to fast path both
466  // Smi and Number operations, so this path should not be marked as Deferred.
467  Label bailout(this, rhs_is_smi ? Label::kDeferred : Label::kNonDeferred),
468  end(this);
469  var_result.Bind(TrySmiDiv(CAST(lhs), CAST(rhs), &bailout));
470  var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kSignedSmall));
471  Goto(&end);
472 
473  BIND(&bailout);
474  {
475  var_type_feedback->Bind(
476  SmiConstant(BinaryOperationFeedback::kSignedSmallInputs));
477  Node* value = Float64Div(SmiToFloat64(lhs), SmiToFloat64(rhs));
478  var_result.Bind(AllocateHeapNumberWithValue(value));
479  Goto(&end);
480  }
481 
482  BIND(&end);
483  return var_result.value();
484  };
485  auto floatFunction = [=](Node* lhs, Node* rhs) {
486  return Float64Div(lhs, rhs);
487  };
488  return Generate_BinaryOperationWithFeedback(
489  context, dividend, divisor, slot_id, feedback_vector, smiFunction,
490  floatFunction, Operation::kDivide, rhs_is_smi);
491 }
492 
493 Node* BinaryOpAssembler::Generate_ModulusWithFeedback(
494  Node* context, Node* dividend, Node* divisor, Node* slot_id,
495  Node* feedback_vector, bool rhs_is_smi) {
496  auto smiFunction = [=](Node* lhs, Node* rhs, Variable* var_type_feedback) {
497  TNode<Number> result = SmiMod(CAST(lhs), CAST(rhs));
498  var_type_feedback->Bind(SelectSmiConstant(
499  TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall,
500  BinaryOperationFeedback::kNumber));
501  return result;
502  };
503  auto floatFunction = [=](Node* lhs, Node* rhs) {
504  return Float64Mod(lhs, rhs);
505  };
506  return Generate_BinaryOperationWithFeedback(
507  context, dividend, divisor, slot_id, feedback_vector, smiFunction,
508  floatFunction, Operation::kModulus, rhs_is_smi);
509 }
510 
511 Node* BinaryOpAssembler::Generate_ExponentiateWithFeedback(
512  Node* context, Node* base, Node* exponent, Node* slot_id,
513  Node* feedback_vector, bool rhs_is_smi) {
514  // We currently don't optimize exponentiation based on feedback.
515  Node* dummy_feedback = SmiConstant(BinaryOperationFeedback::kAny);
516  UpdateFeedback(dummy_feedback, feedback_vector, slot_id);
517  return CallBuiltin(Builtins::kExponentiate, context, base, exponent);
518 }
519 
520 } // namespace internal
521 } // namespace v8
Definition: libplatform.h:13