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/builtins/builtins-math-gen.h"
6 : #include "src/builtins/builtins-utils-gen.h"
7 : #include "src/builtins/builtins.h"
8 : #include "src/code-stub-assembler.h"
9 : #include "src/ic/binary-op-assembler.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 :
14 : // -----------------------------------------------------------------------------
15 : // ES6 section 20.1 Number Objects
16 :
17 : class NumberBuiltinsAssembler : public CodeStubAssembler {
18 : public:
19 : explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state)
20 1064 : : CodeStubAssembler(state) {}
21 :
22 : protected:
23 : template <typename Descriptor>
24 336 : void EmitBitwiseOp(Operation op) {
25 336 : Node* left = Parameter(Descriptor::kLeft);
26 336 : Node* right = Parameter(Descriptor::kRight);
27 336 : Node* context = Parameter(Descriptor::kContext);
28 :
29 336 : VARIABLE(var_left_word32, MachineRepresentation::kWord32);
30 672 : VARIABLE(var_right_word32, MachineRepresentation::kWord32);
31 672 : VARIABLE(var_left_bigint, MachineRepresentation::kTagged, left);
32 672 : VARIABLE(var_right_bigint, MachineRepresentation::kTagged);
33 336 : Label if_left_number(this), do_number_op(this);
34 336 : Label if_left_bigint(this), do_bigint_op(this);
35 :
36 336 : TaggedToWord32OrBigInt(context, left, &if_left_number, &var_left_word32,
37 336 : &if_left_bigint, &var_left_bigint);
38 336 : BIND(&if_left_number);
39 336 : TaggedToWord32OrBigInt(context, right, &do_number_op, &var_right_word32,
40 : &do_bigint_op, &var_right_bigint);
41 336 : BIND(&do_number_op);
42 672 : Return(BitwiseOp(var_left_word32.value(), var_right_word32.value(), op));
43 :
44 : // BigInt cases.
45 336 : BIND(&if_left_bigint);
46 336 : TaggedToNumeric(context, right, &do_bigint_op, &var_right_bigint);
47 :
48 336 : BIND(&do_bigint_op);
49 336 : Return(CallRuntime(Runtime::kBigIntBinaryOp, context,
50 : var_left_bigint.value(), var_right_bigint.value(),
51 1008 : SmiConstant(op)));
52 336 : }
53 :
54 : template <typename Descriptor>
55 224 : void RelationalComparisonBuiltin(Operation op) {
56 224 : Node* lhs = Parameter(Descriptor::kLeft);
57 224 : Node* rhs = Parameter(Descriptor::kRight);
58 224 : Node* context = Parameter(Descriptor::kContext);
59 :
60 448 : Return(RelationalComparison(op, lhs, rhs, context));
61 224 : }
62 :
63 : template <typename Descriptor>
64 : void UnaryOp(Variable* var_input, Label* do_smi, Label* do_double,
65 : Variable* var_input_double, Label* do_bigint);
66 :
67 : template <typename Descriptor>
68 : void BinaryOp(Label* smis, Variable* var_left, Variable* var_right,
69 : Label* doubles, Variable* var_left_double,
70 : Variable* var_right_double, Label* bigints);
71 : };
72 :
73 : // ES6 #sec-number.isfinite
74 168 : TF_BUILTIN(NumberIsFinite, CodeStubAssembler) {
75 : Node* number = Parameter(Descriptor::kNumber);
76 :
77 56 : Label return_true(this), return_false(this);
78 :
79 : // Check if {number} is a Smi.
80 112 : GotoIf(TaggedIsSmi(number), &return_true);
81 :
82 : // Check if {number} is a HeapNumber.
83 112 : GotoIfNot(IsHeapNumber(number), &return_false);
84 :
85 : // Check if {number} contains a finite, non-NaN value.
86 112 : Node* number_value = LoadHeapNumberValue(number);
87 56 : BranchIfFloat64IsNaN(Float64Sub(number_value, number_value), &return_false,
88 112 : &return_true);
89 :
90 56 : BIND(&return_true);
91 112 : Return(TrueConstant());
92 :
93 56 : BIND(&return_false);
94 168 : Return(FalseConstant());
95 56 : }
96 :
97 168 : TF_BUILTIN(AllocateHeapNumber, CodeStubAssembler) {
98 112 : Node* result = AllocateHeapNumber();
99 56 : Return(result);
100 56 : }
101 :
102 : // ES6 #sec-number.isinteger
103 168 : TF_BUILTIN(NumberIsInteger, CodeStubAssembler) {
104 56 : TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
105 168 : Return(SelectBooleanConstant(IsInteger(number)));
106 56 : }
107 :
108 : // ES6 #sec-number.isnan
109 168 : TF_BUILTIN(NumberIsNaN, CodeStubAssembler) {
110 : Node* number = Parameter(Descriptor::kNumber);
111 :
112 56 : Label return_true(this), return_false(this);
113 :
114 : // Check if {number} is a Smi.
115 112 : GotoIf(TaggedIsSmi(number), &return_false);
116 :
117 : // Check if {number} is a HeapNumber.
118 112 : GotoIfNot(IsHeapNumber(number), &return_false);
119 :
120 : // Check if {number} contains a NaN value.
121 112 : Node* number_value = LoadHeapNumberValue(number);
122 56 : BranchIfFloat64IsNaN(number_value, &return_true, &return_false);
123 :
124 56 : BIND(&return_true);
125 112 : Return(TrueConstant());
126 :
127 56 : BIND(&return_false);
128 168 : Return(FalseConstant());
129 56 : }
130 :
131 : // ES6 #sec-number.issafeinteger
132 168 : TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) {
133 56 : TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
134 168 : Return(SelectBooleanConstant(IsSafeInteger(number)));
135 56 : }
136 :
137 : // ES6 #sec-number.parsefloat
138 168 : TF_BUILTIN(NumberParseFloat, CodeStubAssembler) {
139 : Node* context = Parameter(Descriptor::kContext);
140 :
141 : // We might need to loop once for ToString conversion.
142 56 : VARIABLE(var_input, MachineRepresentation::kTagged,
143 : Parameter(Descriptor::kString));
144 56 : Label loop(this, &var_input);
145 56 : Goto(&loop);
146 56 : BIND(&loop);
147 : {
148 : // Load the current {input} value.
149 56 : Node* input = var_input.value();
150 :
151 : // Check if the {input} is a HeapObject or a Smi.
152 56 : Label if_inputissmi(this), if_inputisnotsmi(this);
153 112 : Branch(TaggedIsSmi(input), &if_inputissmi, &if_inputisnotsmi);
154 :
155 56 : BIND(&if_inputissmi);
156 : {
157 : // The {input} is already a Number, no need to do anything.
158 56 : Return(input);
159 : }
160 :
161 56 : BIND(&if_inputisnotsmi);
162 : {
163 : // The {input} is a HeapObject, check if it's already a String.
164 56 : Label if_inputisstring(this), if_inputisnotstring(this);
165 112 : Node* input_map = LoadMap(input);
166 112 : Node* input_instance_type = LoadMapInstanceType(input_map);
167 56 : Branch(IsStringInstanceType(input_instance_type), &if_inputisstring,
168 112 : &if_inputisnotstring);
169 :
170 56 : BIND(&if_inputisstring);
171 : {
172 : // The {input} is already a String, check if {input} contains
173 : // a cached array index.
174 56 : Label if_inputcached(this), if_inputnotcached(this);
175 112 : Node* input_hash = LoadNameHashField(input);
176 : Branch(IsClearWord32(input_hash,
177 56 : Name::kDoesNotContainCachedArrayIndexMask),
178 112 : &if_inputcached, &if_inputnotcached);
179 :
180 56 : BIND(&if_inputcached);
181 : {
182 : // Just return the {input}s cached array index.
183 : Node* input_array_index =
184 112 : DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash);
185 112 : Return(SmiTag(input_array_index));
186 : }
187 :
188 56 : BIND(&if_inputnotcached);
189 : {
190 : // Need to fall back to the runtime to convert {input} to double.
191 56 : Return(CallRuntime(Runtime::kStringParseFloat, context, input));
192 56 : }
193 : }
194 :
195 56 : BIND(&if_inputisnotstring);
196 : {
197 : // The {input} is neither a String nor a Smi, check for HeapNumber.
198 : Label if_inputisnumber(this),
199 56 : if_inputisnotnumber(this, Label::kDeferred);
200 56 : Branch(IsHeapNumberMap(input_map), &if_inputisnumber,
201 112 : &if_inputisnotnumber);
202 :
203 56 : BIND(&if_inputisnumber);
204 : {
205 : // The {input} is already a Number, take care of -0.
206 56 : Label if_inputiszero(this), if_inputisnotzero(this);
207 112 : Node* input_value = LoadHeapNumberValue(input);
208 112 : Branch(Float64Equal(input_value, Float64Constant(0.0)),
209 112 : &if_inputiszero, &if_inputisnotzero);
210 :
211 56 : BIND(&if_inputiszero);
212 112 : Return(SmiConstant(0));
213 :
214 56 : BIND(&if_inputisnotzero);
215 112 : Return(input);
216 : }
217 :
218 56 : BIND(&if_inputisnotnumber);
219 : {
220 : // Need to convert the {input} to String first.
221 : // TODO(bmeurer): This could be more efficient if necessary.
222 112 : var_input.Bind(CallBuiltin(Builtins::kToString, context, input));
223 56 : Goto(&loop);
224 56 : }
225 56 : }
226 56 : }
227 56 : }
228 56 : }
229 :
230 : // ES6 #sec-number.parseint
231 168 : TF_BUILTIN(ParseInt, CodeStubAssembler) {
232 : Node* context = Parameter(Descriptor::kContext);
233 : Node* input = Parameter(Descriptor::kString);
234 : Node* radix = Parameter(Descriptor::kRadix);
235 :
236 : // Check if {radix} is treated as 10 (i.e. undefined, 0 or 10).
237 56 : Label if_radix10(this), if_generic(this, Label::kDeferred);
238 112 : GotoIf(IsUndefined(radix), &if_radix10);
239 112 : GotoIf(WordEqual(radix, SmiConstant(10)), &if_radix10);
240 112 : GotoIf(WordEqual(radix, SmiConstant(0)), &if_radix10);
241 56 : Goto(&if_generic);
242 :
243 56 : BIND(&if_radix10);
244 : {
245 : // Check if we can avoid the ToString conversion on {input}.
246 56 : Label if_inputissmi(this), if_inputisheapnumber(this),
247 56 : if_inputisstring(this);
248 112 : GotoIf(TaggedIsSmi(input), &if_inputissmi);
249 112 : Node* input_map = LoadMap(input);
250 112 : GotoIf(IsHeapNumberMap(input_map), &if_inputisheapnumber);
251 112 : Node* input_instance_type = LoadMapInstanceType(input_map);
252 56 : Branch(IsStringInstanceType(input_instance_type), &if_inputisstring,
253 112 : &if_generic);
254 :
255 56 : BIND(&if_inputissmi);
256 : {
257 : // Just return the {input}.
258 56 : Return(input);
259 : }
260 :
261 56 : BIND(&if_inputisheapnumber);
262 : {
263 : // Check if the {input} value is in Signed32 range.
264 : Label if_inputissigned32(this);
265 112 : Node* input_value = LoadHeapNumberValue(input);
266 112 : Node* input_value32 = TruncateFloat64ToWord32(input_value);
267 112 : GotoIf(Float64Equal(input_value, ChangeInt32ToFloat64(input_value32)),
268 112 : &if_inputissigned32);
269 :
270 : // Check if the absolute {input} value is in the [1,1<<31[ range.
271 : // Take the generic path for the range [0,1[ because the result
272 : // could be -0.
273 112 : Node* input_value_abs = Float64Abs(input_value);
274 :
275 112 : GotoIfNot(Float64LessThan(input_value_abs, Float64Constant(1u << 31)),
276 112 : &if_generic);
277 112 : Branch(Float64LessThanOrEqual(Float64Constant(1), input_value_abs),
278 112 : &if_inputissigned32, &if_generic);
279 :
280 : // Return the truncated int32 value, and return the tagged result.
281 56 : BIND(&if_inputissigned32);
282 112 : Node* result = ChangeInt32ToTagged(input_value32);
283 56 : Return(result);
284 : }
285 :
286 56 : BIND(&if_inputisstring);
287 : {
288 : // Check if the String {input} has a cached array index.
289 112 : Node* input_hash = LoadNameHashField(input);
290 56 : GotoIf(IsSetWord32(input_hash, Name::kDoesNotContainCachedArrayIndexMask),
291 112 : &if_generic);
292 :
293 : // Return the cached array index as result.
294 : Node* input_index =
295 112 : DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash);
296 112 : Node* result = SmiTag(input_index);
297 56 : Return(result);
298 56 : }
299 : }
300 :
301 56 : BIND(&if_generic);
302 : {
303 : Node* result = CallRuntime(Runtime::kStringParseInt, context, input, radix);
304 56 : Return(result);
305 56 : }
306 56 : }
307 :
308 : // ES6 #sec-number.parseint
309 168 : TF_BUILTIN(NumberParseInt, CodeStubAssembler) {
310 : Node* context = Parameter(Descriptor::kContext);
311 : Node* input = Parameter(Descriptor::kString);
312 : Node* radix = Parameter(Descriptor::kRadix);
313 112 : Return(CallBuiltin(Builtins::kParseInt, context, input, radix));
314 56 : }
315 :
316 : // ES6 #sec-number.prototype.valueof
317 168 : TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) {
318 : Node* context = Parameter(Descriptor::kContext);
319 : Node* receiver = Parameter(Descriptor::kReceiver);
320 :
321 : Node* result = ToThisValue(context, receiver, PrimitiveType::kNumber,
322 56 : "Number.prototype.valueOf");
323 56 : Return(result);
324 56 : }
325 :
326 : class AddStubAssembler : public CodeStubAssembler {
327 : public:
328 : explicit AddStubAssembler(compiler::CodeAssemblerState* state)
329 56 : : CodeStubAssembler(state) {}
330 :
331 : protected:
332 336 : void ConvertReceiverAndLoop(Variable* var_value, Label* loop, Node* context) {
333 : // Call ToPrimitive explicitly without hint (whereas ToNumber
334 : // would pass a "number" hint).
335 336 : Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate());
336 1008 : var_value->Bind(CallStub(callable, context, var_value->value()));
337 336 : Goto(loop);
338 336 : }
339 :
340 280 : void ConvertNonReceiverAndLoop(Variable* var_value, Label* loop,
341 : Node* context) {
342 : var_value->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
343 840 : var_value->value()));
344 280 : Goto(loop);
345 280 : }
346 :
347 224 : void ConvertAndLoop(Variable* var_value, Node* instance_type, Label* loop,
348 : Node* context) {
349 224 : Label is_not_receiver(this, Label::kDeferred);
350 448 : GotoIfNot(IsJSReceiverInstanceType(instance_type), &is_not_receiver);
351 :
352 224 : ConvertReceiverAndLoop(var_value, loop, context);
353 :
354 224 : BIND(&is_not_receiver);
355 224 : ConvertNonReceiverAndLoop(var_value, loop, context);
356 224 : }
357 : };
358 :
359 280 : TF_BUILTIN(Add, AddStubAssembler) {
360 : Node* context = Parameter(Descriptor::kContext);
361 56 : VARIABLE(var_left, MachineRepresentation::kTagged,
362 : Parameter(Descriptor::kLeft));
363 112 : VARIABLE(var_right, MachineRepresentation::kTagged,
364 : Parameter(Descriptor::kRight));
365 :
366 : // Shared entry for floating point addition.
367 56 : Label do_double_add(this);
368 112 : VARIABLE(var_left_double, MachineRepresentation::kFloat64);
369 112 : VARIABLE(var_right_double, MachineRepresentation::kFloat64);
370 :
371 : // We might need to loop several times due to ToPrimitive, ToString and/or
372 : // ToNumeric conversions.
373 112 : VARIABLE(var_result, MachineRepresentation::kTagged);
374 56 : Variable* loop_vars[2] = {&var_left, &var_right};
375 112 : Label loop(this, 2, loop_vars),
376 56 : string_add_convert_left(this, Label::kDeferred),
377 56 : string_add_convert_right(this, Label::kDeferred),
378 56 : do_bigint_add(this, Label::kDeferred);
379 56 : Goto(&loop);
380 56 : BIND(&loop);
381 : {
382 56 : Node* left = var_left.value();
383 56 : Node* right = var_right.value();
384 :
385 56 : Label if_left_smi(this), if_left_heapobject(this);
386 112 : Branch(TaggedIsSmi(left), &if_left_smi, &if_left_heapobject);
387 :
388 56 : BIND(&if_left_smi);
389 : {
390 56 : Label if_right_smi(this), if_right_heapobject(this);
391 112 : Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject);
392 :
393 56 : BIND(&if_right_smi);
394 : {
395 : Label if_overflow(this);
396 56 : TNode<Smi> result = TrySmiAdd(CAST(left), CAST(right), &if_overflow);
397 56 : Return(result);
398 :
399 56 : BIND(&if_overflow);
400 : {
401 112 : var_left_double.Bind(SmiToFloat64(left));
402 112 : var_right_double.Bind(SmiToFloat64(right));
403 56 : Goto(&do_double_add);
404 56 : }
405 : } // if_right_smi
406 :
407 56 : BIND(&if_right_heapobject);
408 : {
409 112 : Node* right_map = LoadMap(right);
410 :
411 : Label if_right_not_number(this, Label::kDeferred);
412 112 : GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number);
413 :
414 : // {right} is a HeapNumber.
415 112 : var_left_double.Bind(SmiToFloat64(left));
416 112 : var_right_double.Bind(LoadHeapNumberValue(right));
417 56 : Goto(&do_double_add);
418 :
419 56 : BIND(&if_right_not_number);
420 : {
421 112 : Node* right_instance_type = LoadMapInstanceType(right_map);
422 56 : GotoIf(IsStringInstanceType(right_instance_type),
423 112 : &string_add_convert_left);
424 112 : GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
425 56 : ConvertAndLoop(&var_right, right_instance_type, &loop, context);
426 56 : }
427 56 : } // if_right_heapobject
428 : } // if_left_smi
429 :
430 56 : BIND(&if_left_heapobject);
431 : {
432 112 : Node* left_map = LoadMap(left);
433 56 : Label if_right_smi(this), if_right_heapobject(this);
434 112 : Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject);
435 :
436 56 : BIND(&if_right_smi);
437 : {
438 : Label if_left_not_number(this, Label::kDeferred);
439 112 : GotoIfNot(IsHeapNumberMap(left_map), &if_left_not_number);
440 :
441 : // {left} is a HeapNumber, {right} is a Smi.
442 112 : var_left_double.Bind(LoadHeapNumberValue(left));
443 112 : var_right_double.Bind(SmiToFloat64(right));
444 56 : Goto(&do_double_add);
445 :
446 56 : BIND(&if_left_not_number);
447 : {
448 112 : Node* left_instance_type = LoadMapInstanceType(left_map);
449 56 : GotoIf(IsStringInstanceType(left_instance_type),
450 112 : &string_add_convert_right);
451 112 : GotoIf(IsBigIntInstanceType(left_instance_type), &do_bigint_add);
452 : // {left} is neither a Numeric nor a String, and {right} is a Smi.
453 56 : ConvertAndLoop(&var_left, left_instance_type, &loop, context);
454 56 : }
455 : } // if_right_smi
456 :
457 56 : BIND(&if_right_heapobject);
458 : {
459 112 : Node* right_map = LoadMap(right);
460 :
461 56 : Label if_left_number(this), if_left_not_number(this, Label::kDeferred);
462 112 : Branch(IsHeapNumberMap(left_map), &if_left_number, &if_left_not_number);
463 :
464 56 : BIND(&if_left_number);
465 : {
466 : Label if_right_not_number(this, Label::kDeferred);
467 112 : GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number);
468 :
469 : // Both {left} and {right} are HeapNumbers.
470 112 : var_left_double.Bind(LoadHeapNumberValue(left));
471 112 : var_right_double.Bind(LoadHeapNumberValue(right));
472 56 : Goto(&do_double_add);
473 :
474 56 : BIND(&if_right_not_number);
475 : {
476 112 : Node* right_instance_type = LoadMapInstanceType(right_map);
477 56 : GotoIf(IsStringInstanceType(right_instance_type),
478 112 : &string_add_convert_left);
479 112 : GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
480 : // {left} is a HeapNumber, {right} is neither Number nor String.
481 56 : ConvertAndLoop(&var_right, right_instance_type, &loop, context);
482 56 : }
483 : } // if_left_number
484 :
485 56 : BIND(&if_left_not_number);
486 : {
487 : Label if_left_bigint(this);
488 112 : Node* left_instance_type = LoadMapInstanceType(left_map);
489 56 : GotoIf(IsStringInstanceType(left_instance_type),
490 112 : &string_add_convert_right);
491 112 : Node* right_instance_type = LoadMapInstanceType(right_map);
492 56 : GotoIf(IsStringInstanceType(right_instance_type),
493 112 : &string_add_convert_left);
494 112 : GotoIf(IsBigIntInstanceType(left_instance_type), &if_left_bigint);
495 56 : Label if_left_not_receiver(this, Label::kDeferred);
496 56 : Label if_right_not_receiver(this, Label::kDeferred);
497 56 : GotoIfNot(IsJSReceiverInstanceType(left_instance_type),
498 112 : &if_left_not_receiver);
499 : // {left} is a JSReceiver, convert it first.
500 56 : ConvertReceiverAndLoop(&var_left, &loop, context);
501 :
502 56 : BIND(&if_left_bigint);
503 : {
504 : // {right} is a HeapObject, but not a String. Jump to
505 : // {do_bigint_add} if {right} is already a Numeric.
506 112 : GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
507 112 : GotoIf(IsHeapNumberMap(right_map), &do_bigint_add);
508 56 : ConvertAndLoop(&var_right, right_instance_type, &loop, context);
509 : }
510 :
511 56 : BIND(&if_left_not_receiver);
512 56 : GotoIfNot(IsJSReceiverInstanceType(right_instance_type),
513 112 : &if_right_not_receiver);
514 : // {left} is a Primitive, but {right} is a JSReceiver, so convert
515 : // {right} with priority.
516 56 : ConvertReceiverAndLoop(&var_right, &loop, context);
517 :
518 56 : BIND(&if_right_not_receiver);
519 : // Neither {left} nor {right} are JSReceivers.
520 112 : ConvertNonReceiverAndLoop(&var_left, &loop, context);
521 56 : }
522 56 : } // if_right_heapobject
523 56 : } // if_left_heapobject
524 : }
525 56 : BIND(&string_add_convert_left);
526 : {
527 : // Convert {left} to a String and concatenate it with the String {right}.
528 : TailCallBuiltin(Builtins::kStringAdd_ConvertLeft, context, var_left.value(),
529 112 : var_right.value());
530 : }
531 :
532 56 : BIND(&string_add_convert_right);
533 : {
534 : // Convert {right} to a String and concatenate it with the String {left}.
535 : TailCallBuiltin(Builtins::kStringAdd_ConvertRight, context,
536 112 : var_left.value(), var_right.value());
537 : }
538 :
539 56 : BIND(&do_bigint_add);
540 : {
541 : Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
542 112 : var_right.value(), SmiConstant(Operation::kAdd)));
543 : }
544 :
545 56 : BIND(&do_double_add);
546 : {
547 224 : Node* value = Float64Add(var_left_double.value(), var_right_double.value());
548 112 : Return(AllocateHeapNumberWithValue(value));
549 56 : }
550 56 : }
551 :
552 : template <typename Descriptor>
553 224 : void NumberBuiltinsAssembler::UnaryOp(Variable* var_input, Label* do_smi,
554 : Label* do_double,
555 : Variable* var_input_double,
556 : Label* do_bigint) {
557 : DCHECK_EQ(var_input->rep(), MachineRepresentation::kTagged);
558 : DCHECK_IMPLIES(var_input_double != nullptr,
559 : var_input_double->rep() == MachineRepresentation::kFloat64);
560 :
561 224 : Node* context = Parameter(Descriptor::kContext);
562 224 : var_input->Bind(Parameter(Descriptor::kValue));
563 :
564 : // We might need to loop for ToNumeric conversion.
565 448 : Label loop(this, {var_input});
566 224 : Goto(&loop);
567 224 : BIND(&loop);
568 224 : Node* input = var_input->value();
569 :
570 224 : Label not_number(this);
571 448 : GotoIf(TaggedIsSmi(input), do_smi);
572 448 : GotoIfNot(IsHeapNumber(input), ¬_number);
573 224 : if (var_input_double != nullptr) {
574 112 : var_input_double->Bind(LoadHeapNumberValue(input));
575 : }
576 224 : Goto(do_double);
577 :
578 224 : BIND(¬_number);
579 448 : GotoIf(IsBigInt(input), do_bigint);
580 448 : var_input->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, input));
581 448 : Goto(&loop);
582 224 : }
583 :
584 : template <typename Descriptor>
585 280 : void NumberBuiltinsAssembler::BinaryOp(Label* smis, Variable* var_left,
586 : Variable* var_right, Label* doubles,
587 : Variable* var_left_double,
588 : Variable* var_right_double,
589 : Label* bigints) {
590 : DCHECK_EQ(var_left->rep(), MachineRepresentation::kTagged);
591 : DCHECK_EQ(var_right->rep(), MachineRepresentation::kTagged);
592 : DCHECK_IMPLIES(var_left_double != nullptr,
593 : var_left_double->rep() == MachineRepresentation::kFloat64);
594 : DCHECK_IMPLIES(var_right_double != nullptr,
595 : var_right_double->rep() == MachineRepresentation::kFloat64);
596 : DCHECK_EQ(var_left_double == nullptr, var_right_double == nullptr);
597 :
598 280 : Node* context = Parameter(Descriptor::kContext);
599 280 : var_left->Bind(Parameter(Descriptor::kLeft));
600 280 : var_right->Bind(Parameter(Descriptor::kRight));
601 :
602 : // We might need to loop for ToNumeric conversions.
603 560 : Label loop(this, {var_left, var_right});
604 280 : Goto(&loop);
605 280 : BIND(&loop);
606 :
607 280 : Label left_not_smi(this), right_not_smi(this);
608 280 : Label left_not_number(this), right_not_number(this);
609 840 : GotoIfNot(TaggedIsSmi(var_left->value()), &left_not_smi);
610 840 : GotoIf(TaggedIsSmi(var_right->value()), smis);
611 :
612 : // At this point, var_left is a Smi but var_right is not.
613 840 : GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number);
614 280 : if (var_left_double != nullptr) {
615 672 : var_left_double->Bind(SmiToFloat64(var_left->value()));
616 672 : var_right_double->Bind(LoadHeapNumberValue(var_right->value()));
617 : }
618 280 : Goto(doubles);
619 :
620 280 : BIND(&left_not_smi);
621 : {
622 840 : GotoIfNot(IsHeapNumber(var_left->value()), &left_not_number);
623 840 : GotoIfNot(TaggedIsSmi(var_right->value()), &right_not_smi);
624 :
625 : // At this point, var_left is a HeapNumber and var_right is a Smi.
626 280 : if (var_left_double != nullptr) {
627 672 : var_left_double->Bind(LoadHeapNumberValue(var_left->value()));
628 672 : var_right_double->Bind(SmiToFloat64(var_right->value()));
629 : }
630 280 : Goto(doubles);
631 : }
632 :
633 280 : BIND(&right_not_smi);
634 : {
635 840 : GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number);
636 280 : if (var_left_double != nullptr) {
637 672 : var_left_double->Bind(LoadHeapNumberValue(var_left->value()));
638 672 : var_right_double->Bind(LoadHeapNumberValue(var_right->value()));
639 : }
640 280 : Goto(doubles);
641 : }
642 :
643 280 : BIND(&left_not_number);
644 : {
645 : Label left_bigint(this);
646 840 : GotoIf(IsBigInt(var_left->value()), &left_bigint);
647 280 : var_left->Bind(
648 840 : CallBuiltin(Builtins::kNonNumberToNumeric, context, var_left->value()));
649 280 : Goto(&loop);
650 :
651 280 : BIND(&left_bigint);
652 : {
653 : // Jump to {bigints} if {var_right} is already a Numeric.
654 840 : GotoIf(TaggedIsSmi(var_right->value()), bigints);
655 840 : GotoIf(IsBigInt(var_right->value()), bigints);
656 840 : GotoIf(IsHeapNumber(var_right->value()), bigints);
657 280 : var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
658 840 : var_right->value()));
659 280 : Goto(&loop);
660 280 : }
661 : }
662 :
663 280 : BIND(&right_not_number);
664 : {
665 840 : GotoIf(IsBigInt(var_right->value()), bigints);
666 280 : var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
667 840 : var_right->value()));
668 280 : Goto(&loop);
669 280 : }
670 280 : }
671 :
672 280 : TF_BUILTIN(Subtract, NumberBuiltinsAssembler) {
673 56 : VARIABLE(var_left, MachineRepresentation::kTagged);
674 112 : VARIABLE(var_right, MachineRepresentation::kTagged);
675 112 : VARIABLE(var_left_double, MachineRepresentation::kFloat64);
676 112 : VARIABLE(var_right_double, MachineRepresentation::kFloat64);
677 56 : Label do_smi_sub(this), do_double_sub(this), do_bigint_sub(this);
678 :
679 : BinaryOp<Descriptor>(&do_smi_sub, &var_left, &var_right, &do_double_sub,
680 56 : &var_left_double, &var_right_double, &do_bigint_sub);
681 :
682 56 : BIND(&do_smi_sub);
683 : {
684 : Label if_overflow(this);
685 56 : TNode<Smi> result = TrySmiSub(CAST(var_left.value()),
686 112 : CAST(var_right.value()), &if_overflow);
687 56 : Return(result);
688 :
689 56 : BIND(&if_overflow);
690 : {
691 168 : var_left_double.Bind(SmiToFloat64(var_left.value()));
692 168 : var_right_double.Bind(SmiToFloat64(var_right.value()));
693 56 : Goto(&do_double_sub);
694 56 : }
695 : }
696 :
697 56 : BIND(&do_double_sub);
698 : {
699 224 : Node* value = Float64Sub(var_left_double.value(), var_right_double.value());
700 112 : Return(AllocateHeapNumberWithValue(value));
701 : }
702 :
703 56 : BIND(&do_bigint_sub);
704 : {
705 : Node* context = Parameter(Descriptor::kContext);
706 : Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
707 112 : var_right.value(), SmiConstant(Operation::kSubtract)));
708 56 : }
709 56 : }
710 :
711 224 : TF_BUILTIN(BitwiseNot, NumberBuiltinsAssembler) {
712 : Node* context = Parameter(Descriptor::kContext);
713 56 : VARIABLE(var_input, MachineRepresentation::kTagged);
714 56 : Label do_number(this), do_bigint(this);
715 :
716 56 : UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
717 :
718 56 : BIND(&do_number);
719 : {
720 : TailCallBuiltin(Builtins::kBitwiseXor, context, var_input.value(),
721 112 : SmiConstant(-1));
722 : }
723 :
724 56 : BIND(&do_bigint);
725 : {
726 : Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
727 112 : SmiConstant(Operation::kBitwiseNot)));
728 56 : }
729 56 : }
730 :
731 224 : TF_BUILTIN(Decrement, NumberBuiltinsAssembler) {
732 : Node* context = Parameter(Descriptor::kContext);
733 56 : VARIABLE(var_input, MachineRepresentation::kTagged);
734 56 : Label do_number(this), do_bigint(this);
735 :
736 56 : UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
737 :
738 56 : BIND(&do_number);
739 : {
740 : TailCallBuiltin(Builtins::kSubtract, context, var_input.value(),
741 112 : SmiConstant(1));
742 : }
743 :
744 56 : BIND(&do_bigint);
745 : {
746 : Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
747 112 : SmiConstant(Operation::kDecrement)));
748 56 : }
749 56 : }
750 :
751 224 : TF_BUILTIN(Increment, NumberBuiltinsAssembler) {
752 : Node* context = Parameter(Descriptor::kContext);
753 56 : VARIABLE(var_input, MachineRepresentation::kTagged);
754 56 : Label do_number(this), do_bigint(this);
755 :
756 56 : UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
757 :
758 56 : BIND(&do_number);
759 : {
760 112 : TailCallBuiltin(Builtins::kAdd, context, var_input.value(), SmiConstant(1));
761 : }
762 :
763 56 : BIND(&do_bigint);
764 : {
765 : Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
766 112 : SmiConstant(Operation::kIncrement)));
767 56 : }
768 56 : }
769 :
770 280 : TF_BUILTIN(Negate, NumberBuiltinsAssembler) {
771 56 : VARIABLE(var_input, MachineRepresentation::kTagged);
772 112 : VARIABLE(var_input_double, MachineRepresentation::kFloat64);
773 56 : Label do_smi(this), do_double(this), do_bigint(this);
774 :
775 : UnaryOp<Descriptor>(&var_input, &do_smi, &do_double, &var_input_double,
776 56 : &do_bigint);
777 :
778 56 : BIND(&do_smi);
779 112 : { Return(SmiMul(CAST(var_input.value()), SmiConstant(-1))); }
780 :
781 56 : BIND(&do_double);
782 : {
783 224 : Node* value = Float64Mul(var_input_double.value(), Float64Constant(-1));
784 112 : Return(AllocateHeapNumberWithValue(value));
785 : }
786 :
787 56 : BIND(&do_bigint);
788 : {
789 : Node* context = Parameter(Descriptor::kContext);
790 : Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
791 112 : SmiConstant(Operation::kNegate)));
792 56 : }
793 56 : }
794 :
795 280 : TF_BUILTIN(Multiply, NumberBuiltinsAssembler) {
796 56 : VARIABLE(var_left, MachineRepresentation::kTagged);
797 112 : VARIABLE(var_right, MachineRepresentation::kTagged);
798 112 : VARIABLE(var_left_double, MachineRepresentation::kFloat64);
799 112 : VARIABLE(var_right_double, MachineRepresentation::kFloat64);
800 56 : Label do_smi_mul(this), do_double_mul(this), do_bigint_mul(this);
801 :
802 : BinaryOp<Descriptor>(&do_smi_mul, &var_left, &var_right, &do_double_mul,
803 56 : &var_left_double, &var_right_double, &do_bigint_mul);
804 :
805 56 : BIND(&do_smi_mul);
806 : // The result is not necessarily a smi, in case of overflow.
807 112 : Return(SmiMul(CAST(var_left.value()), CAST(var_right.value())));
808 :
809 56 : BIND(&do_double_mul);
810 224 : Node* value = Float64Mul(var_left_double.value(), var_right_double.value());
811 112 : Return(AllocateHeapNumberWithValue(value));
812 :
813 56 : BIND(&do_bigint_mul);
814 : {
815 : Node* context = Parameter(Descriptor::kContext);
816 : Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
817 112 : var_right.value(), SmiConstant(Operation::kMultiply)));
818 56 : }
819 56 : }
820 :
821 280 : TF_BUILTIN(Divide, NumberBuiltinsAssembler) {
822 56 : VARIABLE(var_left, MachineRepresentation::kTagged);
823 112 : VARIABLE(var_right, MachineRepresentation::kTagged);
824 112 : VARIABLE(var_left_double, MachineRepresentation::kFloat64);
825 112 : VARIABLE(var_right_double, MachineRepresentation::kFloat64);
826 56 : Label do_smi_div(this), do_double_div(this), do_bigint_div(this);
827 :
828 : BinaryOp<Descriptor>(&do_smi_div, &var_left, &var_right, &do_double_div,
829 56 : &var_left_double, &var_right_double, &do_bigint_div);
830 :
831 56 : BIND(&do_smi_div);
832 : {
833 : // TODO(jkummerow): Consider just always doing a double division.
834 : Label bailout(this);
835 56 : TNode<Smi> dividend = CAST(var_left.value());
836 56 : TNode<Smi> divisor = CAST(var_right.value());
837 :
838 : // Do floating point division if {divisor} is zero.
839 112 : GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout);
840 :
841 : // Do floating point division if {dividend} is zero and {divisor} is
842 : // negative.
843 56 : Label dividend_is_zero(this), dividend_is_not_zero(this);
844 56 : Branch(SmiEqual(dividend, SmiConstant(0)), ÷nd_is_zero,
845 112 : ÷nd_is_not_zero);
846 :
847 56 : BIND(÷nd_is_zero);
848 : {
849 112 : GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout);
850 56 : Goto(÷nd_is_not_zero);
851 : }
852 56 : BIND(÷nd_is_not_zero);
853 :
854 112 : Node* untagged_divisor = SmiToInt32(divisor);
855 112 : Node* untagged_dividend = SmiToInt32(dividend);
856 :
857 : // Do floating point division if {dividend} is kMinInt (or kMinInt - 1
858 : // if the Smi size is 31) and {divisor} is -1.
859 56 : Label divisor_is_minus_one(this), divisor_is_not_minus_one(this);
860 112 : Branch(Word32Equal(untagged_divisor, Int32Constant(-1)),
861 112 : &divisor_is_minus_one, &divisor_is_not_minus_one);
862 :
863 56 : BIND(&divisor_is_minus_one);
864 : {
865 : GotoIf(Word32Equal(
866 : untagged_dividend,
867 112 : Int32Constant(kSmiValueSize == 32 ? kMinInt : (kMinInt >> 1))),
868 112 : &bailout);
869 56 : Goto(&divisor_is_not_minus_one);
870 : }
871 56 : BIND(&divisor_is_not_minus_one);
872 :
873 : // TODO(epertoso): consider adding a machine instruction that returns
874 : // both the result and the remainder.
875 112 : Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor);
876 112 : Node* truncated = Int32Mul(untagged_result, untagged_divisor);
877 : // Do floating point division if the remainder is not 0.
878 112 : GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout);
879 112 : Return(SmiFromInt32(untagged_result));
880 :
881 : // Bailout: convert {dividend} and {divisor} to double and do double
882 : // division.
883 56 : BIND(&bailout);
884 : {
885 112 : var_left_double.Bind(SmiToFloat64(dividend));
886 112 : var_right_double.Bind(SmiToFloat64(divisor));
887 56 : Goto(&do_double_div);
888 56 : }
889 : }
890 :
891 56 : BIND(&do_double_div);
892 : {
893 224 : Node* value = Float64Div(var_left_double.value(), var_right_double.value());
894 112 : Return(AllocateHeapNumberWithValue(value));
895 : }
896 :
897 56 : BIND(&do_bigint_div);
898 : {
899 : Node* context = Parameter(Descriptor::kContext);
900 : Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
901 112 : var_right.value(), SmiConstant(Operation::kDivide)));
902 56 : }
903 56 : }
904 :
905 280 : TF_BUILTIN(Modulus, NumberBuiltinsAssembler) {
906 56 : VARIABLE(var_left, MachineRepresentation::kTagged);
907 112 : VARIABLE(var_right, MachineRepresentation::kTagged);
908 112 : VARIABLE(var_left_double, MachineRepresentation::kFloat64);
909 112 : VARIABLE(var_right_double, MachineRepresentation::kFloat64);
910 56 : Label do_smi_mod(this), do_double_mod(this), do_bigint_mod(this);
911 :
912 : BinaryOp<Descriptor>(&do_smi_mod, &var_left, &var_right, &do_double_mod,
913 56 : &var_left_double, &var_right_double, &do_bigint_mod);
914 :
915 56 : BIND(&do_smi_mod);
916 112 : Return(SmiMod(CAST(var_left.value()), CAST(var_right.value())));
917 :
918 56 : BIND(&do_double_mod);
919 224 : Node* value = Float64Mod(var_left_double.value(), var_right_double.value());
920 112 : Return(AllocateHeapNumberWithValue(value));
921 :
922 56 : BIND(&do_bigint_mod);
923 : {
924 : Node* context = Parameter(Descriptor::kContext);
925 : Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
926 112 : var_right.value(), SmiConstant(Operation::kModulus)));
927 56 : }
928 56 : }
929 :
930 280 : TF_BUILTIN(Exponentiate, NumberBuiltinsAssembler) {
931 56 : VARIABLE(var_left, MachineRepresentation::kTagged);
932 112 : VARIABLE(var_right, MachineRepresentation::kTagged);
933 56 : Label do_number_exp(this), do_bigint_exp(this);
934 : Node* context = Parameter(Descriptor::kContext);
935 :
936 : BinaryOp<Descriptor>(&do_number_exp, &var_left, &var_right, &do_number_exp,
937 56 : nullptr, nullptr, &do_bigint_exp);
938 :
939 56 : BIND(&do_number_exp);
940 : {
941 : MathBuiltinsAssembler math_asm(state());
942 112 : Return(math_asm.MathPow(context, var_left.value(), var_right.value()));
943 : }
944 :
945 56 : BIND(&do_bigint_exp);
946 : Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
947 168 : var_right.value(), SmiConstant(Operation::kExponentiate)));
948 56 : }
949 :
950 168 : TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) {
951 56 : EmitBitwiseOp<Descriptor>(Operation::kShiftLeft);
952 0 : }
953 :
954 168 : TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) {
955 56 : EmitBitwiseOp<Descriptor>(Operation::kShiftRight);
956 0 : }
957 :
958 168 : TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) {
959 56 : EmitBitwiseOp<Descriptor>(Operation::kShiftRightLogical);
960 0 : }
961 :
962 168 : TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) {
963 56 : EmitBitwiseOp<Descriptor>(Operation::kBitwiseAnd);
964 0 : }
965 :
966 168 : TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) {
967 56 : EmitBitwiseOp<Descriptor>(Operation::kBitwiseOr);
968 0 : }
969 :
970 168 : TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) {
971 56 : EmitBitwiseOp<Descriptor>(Operation::kBitwiseXor);
972 0 : }
973 :
974 168 : TF_BUILTIN(LessThan, NumberBuiltinsAssembler) {
975 56 : RelationalComparisonBuiltin<Descriptor>(Operation::kLessThan);
976 0 : }
977 :
978 168 : TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) {
979 56 : RelationalComparisonBuiltin<Descriptor>(Operation::kLessThanOrEqual);
980 0 : }
981 :
982 168 : TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) {
983 56 : RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThan);
984 0 : }
985 :
986 168 : TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) {
987 56 : RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThanOrEqual);
988 0 : }
989 :
990 168 : TF_BUILTIN(Equal, CodeStubAssembler) {
991 : Node* lhs = Parameter(Descriptor::kLeft);
992 : Node* rhs = Parameter(Descriptor::kRight);
993 : Node* context = Parameter(Descriptor::kContext);
994 :
995 112 : Return(Equal(lhs, rhs, context));
996 56 : }
997 :
998 168 : TF_BUILTIN(StrictEqual, CodeStubAssembler) {
999 : Node* lhs = Parameter(Descriptor::kLeft);
1000 : Node* rhs = Parameter(Descriptor::kRight);
1001 :
1002 112 : Return(StrictEqual(lhs, rhs));
1003 56 : }
1004 :
1005 : } // namespace internal
1006 94089 : } // namespace v8
|