| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
| 6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
| 7 #include "src/code-factory.h" | 7 #include "src/code-factory.h" |
| 8 #include "src/code-stub-assembler.h" | |
| 9 #include "src/conversions.h" | 8 #include "src/conversions.h" |
| 10 #include "src/counters.h" | 9 #include "src/counters.h" |
| 11 #include "src/objects-inl.h" | 10 #include "src/objects-inl.h" |
| 12 | 11 |
| 13 namespace v8 { | 12 namespace v8 { |
| 14 namespace internal { | 13 namespace internal { |
| 15 | 14 |
| 16 class NumberBuiltinsAssembler : public CodeStubAssembler { | |
| 17 public: | |
| 18 explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state) | |
| 19 : CodeStubAssembler(state) {} | |
| 20 | |
| 21 protected: | |
| 22 template <Signedness signed_result = kSigned> | |
| 23 void BitwiseOp(std::function<Node*(Node* lhs, Node* rhs)> body) { | |
| 24 Node* left = Parameter(0); | |
| 25 Node* right = Parameter(1); | |
| 26 Node* context = Parameter(2); | |
| 27 | |
| 28 Node* lhs_value = TruncateTaggedToWord32(context, left); | |
| 29 Node* rhs_value = TruncateTaggedToWord32(context, right); | |
| 30 Node* value = body(lhs_value, rhs_value); | |
| 31 Node* result = signed_result == kSigned ? ChangeInt32ToTagged(value) | |
| 32 : ChangeUint32ToTagged(value); | |
| 33 Return(result); | |
| 34 } | |
| 35 | |
| 36 template <Signedness signed_result = kSigned> | |
| 37 void BitwiseShiftOp(std::function<Node*(Node* lhs, Node* shift_count)> body) { | |
| 38 BitwiseOp<signed_result>([this, body](Node* lhs, Node* rhs) { | |
| 39 Node* shift_count = Word32And(rhs, Int32Constant(0x1f)); | |
| 40 return body(lhs, shift_count); | |
| 41 }); | |
| 42 } | |
| 43 | |
| 44 void RelationalComparisonBuiltin(RelationalComparisonMode mode) { | |
| 45 Node* lhs = Parameter(0); | |
| 46 Node* rhs = Parameter(1); | |
| 47 Node* context = Parameter(2); | |
| 48 | |
| 49 Return(RelationalComparison(mode, lhs, rhs, context)); | |
| 50 } | |
| 51 }; | |
| 52 | |
| 53 // ----------------------------------------------------------------------------- | 15 // ----------------------------------------------------------------------------- |
| 54 // ES6 section 20.1 Number Objects | 16 // ES6 section 20.1 Number Objects |
| 55 | 17 |
| 56 // ES6 section 20.1.2.2 Number.isFinite ( number ) | |
| 57 TF_BUILTIN(NumberIsFinite, CodeStubAssembler) { | |
| 58 Node* number = Parameter(1); | |
| 59 | |
| 60 Label return_true(this), return_false(this); | |
| 61 | |
| 62 // Check if {number} is a Smi. | |
| 63 GotoIf(TaggedIsSmi(number), &return_true); | |
| 64 | |
| 65 // Check if {number} is a HeapNumber. | |
| 66 GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false); | |
| 67 | |
| 68 // Check if {number} contains a finite, non-NaN value. | |
| 69 Node* number_value = LoadHeapNumberValue(number); | |
| 70 BranchIfFloat64IsNaN(Float64Sub(number_value, number_value), &return_false, | |
| 71 &return_true); | |
| 72 | |
| 73 Bind(&return_true); | |
| 74 Return(BooleanConstant(true)); | |
| 75 | |
| 76 Bind(&return_false); | |
| 77 Return(BooleanConstant(false)); | |
| 78 } | |
| 79 | |
| 80 // ES6 section 20.1.2.3 Number.isInteger ( number ) | |
| 81 TF_BUILTIN(NumberIsInteger, CodeStubAssembler) { | |
| 82 Node* number = Parameter(1); | |
| 83 | |
| 84 Label return_true(this), return_false(this); | |
| 85 | |
| 86 // Check if {number} is a Smi. | |
| 87 GotoIf(TaggedIsSmi(number), &return_true); | |
| 88 | |
| 89 // Check if {number} is a HeapNumber. | |
| 90 GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false); | |
| 91 | |
| 92 // Load the actual value of {number}. | |
| 93 Node* number_value = LoadHeapNumberValue(number); | |
| 94 | |
| 95 // Truncate the value of {number} to an integer (or an infinity). | |
| 96 Node* integer = Float64Trunc(number_value); | |
| 97 | |
| 98 // Check if {number}s value matches the integer (ruling out the infinities). | |
| 99 Branch(Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)), | |
| 100 &return_true, &return_false); | |
| 101 | |
| 102 Bind(&return_true); | |
| 103 Return(BooleanConstant(true)); | |
| 104 | |
| 105 Bind(&return_false); | |
| 106 Return(BooleanConstant(false)); | |
| 107 } | |
| 108 | |
| 109 // ES6 section 20.1.2.4 Number.isNaN ( number ) | |
| 110 TF_BUILTIN(NumberIsNaN, CodeStubAssembler) { | |
| 111 Node* number = Parameter(1); | |
| 112 | |
| 113 Label return_true(this), return_false(this); | |
| 114 | |
| 115 // Check if {number} is a Smi. | |
| 116 GotoIf(TaggedIsSmi(number), &return_false); | |
| 117 | |
| 118 // Check if {number} is a HeapNumber. | |
| 119 GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false); | |
| 120 | |
| 121 // Check if {number} contains a NaN value. | |
| 122 Node* number_value = LoadHeapNumberValue(number); | |
| 123 BranchIfFloat64IsNaN(number_value, &return_true, &return_false); | |
| 124 | |
| 125 Bind(&return_true); | |
| 126 Return(BooleanConstant(true)); | |
| 127 | |
| 128 Bind(&return_false); | |
| 129 Return(BooleanConstant(false)); | |
| 130 } | |
| 131 | |
| 132 // ES6 section 20.1.2.5 Number.isSafeInteger ( number ) | |
| 133 TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) { | |
| 134 Node* number = Parameter(1); | |
| 135 | |
| 136 Label return_true(this), return_false(this); | |
| 137 | |
| 138 // Check if {number} is a Smi. | |
| 139 GotoIf(TaggedIsSmi(number), &return_true); | |
| 140 | |
| 141 // Check if {number} is a HeapNumber. | |
| 142 GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false); | |
| 143 | |
| 144 // Load the actual value of {number}. | |
| 145 Node* number_value = LoadHeapNumberValue(number); | |
| 146 | |
| 147 // Truncate the value of {number} to an integer (or an infinity). | |
| 148 Node* integer = Float64Trunc(number_value); | |
| 149 | |
| 150 // Check if {number}s value matches the integer (ruling out the infinities). | |
| 151 GotoIfNot( | |
| 152 Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)), | |
| 153 &return_false); | |
| 154 | |
| 155 // Check if the {integer} value is in safe integer range. | |
| 156 Branch(Float64LessThanOrEqual(Float64Abs(integer), | |
| 157 Float64Constant(kMaxSafeInteger)), | |
| 158 &return_true, &return_false); | |
| 159 | |
| 160 Bind(&return_true); | |
| 161 Return(BooleanConstant(true)); | |
| 162 | |
| 163 Bind(&return_false); | |
| 164 Return(BooleanConstant(false)); | |
| 165 } | |
| 166 | |
| 167 // ES6 section 20.1.2.12 Number.parseFloat ( string ) | |
| 168 TF_BUILTIN(NumberParseFloat, CodeStubAssembler) { | |
| 169 Node* context = Parameter(4); | |
| 170 | |
| 171 // We might need to loop once for ToString conversion. | |
| 172 Variable var_input(this, MachineRepresentation::kTagged); | |
| 173 Label loop(this, &var_input); | |
| 174 var_input.Bind(Parameter(1)); | |
| 175 Goto(&loop); | |
| 176 Bind(&loop); | |
| 177 { | |
| 178 // Load the current {input} value. | |
| 179 Node* input = var_input.value(); | |
| 180 | |
| 181 // Check if the {input} is a HeapObject or a Smi. | |
| 182 Label if_inputissmi(this), if_inputisnotsmi(this); | |
| 183 Branch(TaggedIsSmi(input), &if_inputissmi, &if_inputisnotsmi); | |
| 184 | |
| 185 Bind(&if_inputissmi); | |
| 186 { | |
| 187 // The {input} is already a Number, no need to do anything. | |
| 188 Return(input); | |
| 189 } | |
| 190 | |
| 191 Bind(&if_inputisnotsmi); | |
| 192 { | |
| 193 // The {input} is a HeapObject, check if it's already a String. | |
| 194 Label if_inputisstring(this), if_inputisnotstring(this); | |
| 195 Node* input_map = LoadMap(input); | |
| 196 Node* input_instance_type = LoadMapInstanceType(input_map); | |
| 197 Branch(IsStringInstanceType(input_instance_type), &if_inputisstring, | |
| 198 &if_inputisnotstring); | |
| 199 | |
| 200 Bind(&if_inputisstring); | |
| 201 { | |
| 202 // The {input} is already a String, check if {input} contains | |
| 203 // a cached array index. | |
| 204 Label if_inputcached(this), if_inputnotcached(this); | |
| 205 Node* input_hash = LoadNameHashField(input); | |
| 206 Node* input_bit = Word32And( | |
| 207 input_hash, Int32Constant(String::kContainsCachedArrayIndexMask)); | |
| 208 Branch(Word32Equal(input_bit, Int32Constant(0)), &if_inputcached, | |
| 209 &if_inputnotcached); | |
| 210 | |
| 211 Bind(&if_inputcached); | |
| 212 { | |
| 213 // Just return the {input}s cached array index. | |
| 214 Node* input_array_index = | |
| 215 DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash); | |
| 216 Return(SmiTag(input_array_index)); | |
| 217 } | |
| 218 | |
| 219 Bind(&if_inputnotcached); | |
| 220 { | |
| 221 // Need to fall back to the runtime to convert {input} to double. | |
| 222 Return(CallRuntime(Runtime::kStringParseFloat, context, input)); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 Bind(&if_inputisnotstring); | |
| 227 { | |
| 228 // The {input} is neither a String nor a Smi, check for HeapNumber. | |
| 229 Label if_inputisnumber(this), | |
| 230 if_inputisnotnumber(this, Label::kDeferred); | |
| 231 Branch(IsHeapNumberMap(input_map), &if_inputisnumber, | |
| 232 &if_inputisnotnumber); | |
| 233 | |
| 234 Bind(&if_inputisnumber); | |
| 235 { | |
| 236 // The {input} is already a Number, take care of -0. | |
| 237 Label if_inputiszero(this), if_inputisnotzero(this); | |
| 238 Node* input_value = LoadHeapNumberValue(input); | |
| 239 Branch(Float64Equal(input_value, Float64Constant(0.0)), | |
| 240 &if_inputiszero, &if_inputisnotzero); | |
| 241 | |
| 242 Bind(&if_inputiszero); | |
| 243 Return(SmiConstant(0)); | |
| 244 | |
| 245 Bind(&if_inputisnotzero); | |
| 246 Return(input); | |
| 247 } | |
| 248 | |
| 249 Bind(&if_inputisnotnumber); | |
| 250 { | |
| 251 // Need to convert the {input} to String first. | |
| 252 // TODO(bmeurer): This could be more efficient if necessary. | |
| 253 Callable callable = CodeFactory::ToString(isolate()); | |
| 254 var_input.Bind(CallStub(callable, context, input)); | |
| 255 Goto(&loop); | |
| 256 } | |
| 257 } | |
| 258 } | |
| 259 } | |
| 260 } | |
| 261 | |
| 262 // ES6 section 20.1.2.13 Number.parseInt ( string, radix ) | |
| 263 TF_BUILTIN(NumberParseInt, CodeStubAssembler) { | |
| 264 Node* input = Parameter(1); | |
| 265 Node* radix = Parameter(2); | |
| 266 Node* context = Parameter(5); | |
| 267 | |
| 268 // Check if {radix} is treated as 10 (i.e. undefined, 0 or 10). | |
| 269 Label if_radix10(this), if_generic(this, Label::kDeferred); | |
| 270 GotoIf(WordEqual(radix, UndefinedConstant()), &if_radix10); | |
| 271 GotoIf(WordEqual(radix, SmiConstant(Smi::FromInt(10))), &if_radix10); | |
| 272 GotoIf(WordEqual(radix, SmiConstant(Smi::FromInt(0))), &if_radix10); | |
| 273 Goto(&if_generic); | |
| 274 | |
| 275 Bind(&if_radix10); | |
| 276 { | |
| 277 // Check if we can avoid the ToString conversion on {input}. | |
| 278 Label if_inputissmi(this), if_inputisheapnumber(this), | |
| 279 if_inputisstring(this); | |
| 280 GotoIf(TaggedIsSmi(input), &if_inputissmi); | |
| 281 Node* input_map = LoadMap(input); | |
| 282 GotoIf(IsHeapNumberMap(input_map), &if_inputisheapnumber); | |
| 283 Node* input_instance_type = LoadMapInstanceType(input_map); | |
| 284 Branch(IsStringInstanceType(input_instance_type), &if_inputisstring, | |
| 285 &if_generic); | |
| 286 | |
| 287 Bind(&if_inputissmi); | |
| 288 { | |
| 289 // Just return the {input}. | |
| 290 Return(input); | |
| 291 } | |
| 292 | |
| 293 Bind(&if_inputisheapnumber); | |
| 294 { | |
| 295 // Check if the {input} value is in Signed32 range. | |
| 296 Label if_inputissigned32(this); | |
| 297 Node* input_value = LoadHeapNumberValue(input); | |
| 298 Node* input_value32 = TruncateFloat64ToWord32(input_value); | |
| 299 GotoIf(Float64Equal(input_value, ChangeInt32ToFloat64(input_value32)), | |
| 300 &if_inputissigned32); | |
| 301 | |
| 302 // Check if the absolute {input} value is in the ]0.01,1e9[ range. | |
| 303 Node* input_value_abs = Float64Abs(input_value); | |
| 304 | |
| 305 GotoIfNot(Float64LessThan(input_value_abs, Float64Constant(1e9)), | |
| 306 &if_generic); | |
| 307 Branch(Float64LessThan(Float64Constant(0.01), input_value_abs), | |
| 308 &if_inputissigned32, &if_generic); | |
| 309 | |
| 310 // Return the truncated int32 value, and return the tagged result. | |
| 311 Bind(&if_inputissigned32); | |
| 312 Node* result = ChangeInt32ToTagged(input_value32); | |
| 313 Return(result); | |
| 314 } | |
| 315 | |
| 316 Bind(&if_inputisstring); | |
| 317 { | |
| 318 // Check if the String {input} has a cached array index. | |
| 319 Node* input_hash = LoadNameHashField(input); | |
| 320 Node* input_bit = Word32And( | |
| 321 input_hash, Int32Constant(String::kContainsCachedArrayIndexMask)); | |
| 322 GotoIf(Word32NotEqual(input_bit, Int32Constant(0)), &if_generic); | |
| 323 | |
| 324 // Return the cached array index as result. | |
| 325 Node* input_index = | |
| 326 DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash); | |
| 327 Node* result = SmiTag(input_index); | |
| 328 Return(result); | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 Bind(&if_generic); | |
| 333 { | |
| 334 Node* result = CallRuntime(Runtime::kStringParseInt, context, input, radix); | |
| 335 Return(result); | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 // ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits ) | 18 // ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits ) |
| 340 BUILTIN(NumberPrototypeToExponential) { | 19 BUILTIN(NumberPrototypeToExponential) { |
| 341 HandleScope scope(isolate); | 20 HandleScope scope(isolate); |
| 342 Handle<Object> value = args.at(0); | 21 Handle<Object> value = args.at(0); |
| 343 Handle<Object> fraction_digits = args.atOrUndefined(isolate, 1); | 22 Handle<Object> fraction_digits = args.atOrUndefined(isolate, 1); |
| 344 | 23 |
| 345 // Unwrap the receiver {value}. | 24 // Unwrap the receiver {value}. |
| 346 if (value->IsJSValue()) { | 25 if (value->IsJSValue()) { |
| 347 value = handle(Handle<JSValue>::cast(value)->value(), isolate); | 26 value = handle(Handle<JSValue>::cast(value)->value(), isolate); |
| 348 } | 27 } |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 538 return (value_number < 0.0) ? isolate->heap()->minus_infinity_string() | 217 return (value_number < 0.0) ? isolate->heap()->minus_infinity_string() |
| 539 : isolate->heap()->infinity_string(); | 218 : isolate->heap()->infinity_string(); |
| 540 } | 219 } |
| 541 char* const str = | 220 char* const str = |
| 542 DoubleToRadixCString(value_number, static_cast<int>(radix_number)); | 221 DoubleToRadixCString(value_number, static_cast<int>(radix_number)); |
| 543 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); | 222 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); |
| 544 DeleteArray(str); | 223 DeleteArray(str); |
| 545 return *result; | 224 return *result; |
| 546 } | 225 } |
| 547 | 226 |
| 548 // ES6 section 20.1.3.7 Number.prototype.valueOf ( ) | |
| 549 TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) { | |
| 550 Node* receiver = Parameter(0); | |
| 551 Node* context = Parameter(3); | |
| 552 | |
| 553 Node* result = ToThisValue(context, receiver, PrimitiveType::kNumber, | |
| 554 "Number.prototype.valueOf"); | |
| 555 Return(result); | |
| 556 } | |
| 557 | |
| 558 TF_BUILTIN(Add, CodeStubAssembler) { | |
| 559 Node* left = Parameter(0); | |
| 560 Node* right = Parameter(1); | |
| 561 Node* context = Parameter(2); | |
| 562 | |
| 563 // Shared entry for floating point addition. | |
| 564 Label do_fadd(this); | |
| 565 Variable var_fadd_lhs(this, MachineRepresentation::kFloat64), | |
| 566 var_fadd_rhs(this, MachineRepresentation::kFloat64); | |
| 567 | |
| 568 // We might need to loop several times due to ToPrimitive, ToString and/or | |
| 569 // ToNumber conversions. | |
| 570 Variable var_lhs(this, MachineRepresentation::kTagged), | |
| 571 var_rhs(this, MachineRepresentation::kTagged), | |
| 572 var_result(this, MachineRepresentation::kTagged); | |
| 573 Variable* loop_vars[2] = {&var_lhs, &var_rhs}; | |
| 574 Label loop(this, 2, loop_vars), end(this), | |
| 575 string_add_convert_left(this, Label::kDeferred), | |
| 576 string_add_convert_right(this, Label::kDeferred); | |
| 577 var_lhs.Bind(left); | |
| 578 var_rhs.Bind(right); | |
| 579 Goto(&loop); | |
| 580 Bind(&loop); | |
| 581 { | |
| 582 // Load the current {lhs} and {rhs} values. | |
| 583 Node* lhs = var_lhs.value(); | |
| 584 Node* rhs = var_rhs.value(); | |
| 585 | |
| 586 // Check if the {lhs} is a Smi or a HeapObject. | |
| 587 Label if_lhsissmi(this), if_lhsisnotsmi(this); | |
| 588 Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); | |
| 589 | |
| 590 Bind(&if_lhsissmi); | |
| 591 { | |
| 592 // Check if the {rhs} is also a Smi. | |
| 593 Label if_rhsissmi(this), if_rhsisnotsmi(this); | |
| 594 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); | |
| 595 | |
| 596 Bind(&if_rhsissmi); | |
| 597 { | |
| 598 // Try fast Smi addition first. | |
| 599 Node* pair = IntPtrAddWithOverflow(BitcastTaggedToWord(lhs), | |
| 600 BitcastTaggedToWord(rhs)); | |
| 601 Node* overflow = Projection(1, pair); | |
| 602 | |
| 603 // Check if the Smi additon overflowed. | |
| 604 Label if_overflow(this), if_notoverflow(this); | |
| 605 Branch(overflow, &if_overflow, &if_notoverflow); | |
| 606 | |
| 607 Bind(&if_overflow); | |
| 608 { | |
| 609 var_fadd_lhs.Bind(SmiToFloat64(lhs)); | |
| 610 var_fadd_rhs.Bind(SmiToFloat64(rhs)); | |
| 611 Goto(&do_fadd); | |
| 612 } | |
| 613 | |
| 614 Bind(&if_notoverflow); | |
| 615 var_result.Bind(BitcastWordToTaggedSigned(Projection(0, pair))); | |
| 616 Goto(&end); | |
| 617 } | |
| 618 | |
| 619 Bind(&if_rhsisnotsmi); | |
| 620 { | |
| 621 // Load the map of {rhs}. | |
| 622 Node* rhs_map = LoadMap(rhs); | |
| 623 | |
| 624 // Check if the {rhs} is a HeapNumber. | |
| 625 Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred); | |
| 626 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); | |
| 627 | |
| 628 Bind(&if_rhsisnumber); | |
| 629 { | |
| 630 var_fadd_lhs.Bind(SmiToFloat64(lhs)); | |
| 631 var_fadd_rhs.Bind(LoadHeapNumberValue(rhs)); | |
| 632 Goto(&do_fadd); | |
| 633 } | |
| 634 | |
| 635 Bind(&if_rhsisnotnumber); | |
| 636 { | |
| 637 // Load the instance type of {rhs}. | |
| 638 Node* rhs_instance_type = LoadMapInstanceType(rhs_map); | |
| 639 | |
| 640 // Check if the {rhs} is a String. | |
| 641 Label if_rhsisstring(this, Label::kDeferred), | |
| 642 if_rhsisnotstring(this, Label::kDeferred); | |
| 643 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, | |
| 644 &if_rhsisnotstring); | |
| 645 | |
| 646 Bind(&if_rhsisstring); | |
| 647 { | |
| 648 var_lhs.Bind(lhs); | |
| 649 var_rhs.Bind(rhs); | |
| 650 Goto(&string_add_convert_left); | |
| 651 } | |
| 652 | |
| 653 Bind(&if_rhsisnotstring); | |
| 654 { | |
| 655 // Check if {rhs} is a JSReceiver. | |
| 656 Label if_rhsisreceiver(this, Label::kDeferred), | |
| 657 if_rhsisnotreceiver(this, Label::kDeferred); | |
| 658 Branch(IsJSReceiverInstanceType(rhs_instance_type), | |
| 659 &if_rhsisreceiver, &if_rhsisnotreceiver); | |
| 660 | |
| 661 Bind(&if_rhsisreceiver); | |
| 662 { | |
| 663 // Convert {rhs} to a primitive first passing no hint. | |
| 664 Callable callable = | |
| 665 CodeFactory::NonPrimitiveToPrimitive(isolate()); | |
| 666 var_rhs.Bind(CallStub(callable, context, rhs)); | |
| 667 Goto(&loop); | |
| 668 } | |
| 669 | |
| 670 Bind(&if_rhsisnotreceiver); | |
| 671 { | |
| 672 // Convert {rhs} to a Number first. | |
| 673 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 674 var_rhs.Bind(CallStub(callable, context, rhs)); | |
| 675 Goto(&loop); | |
| 676 } | |
| 677 } | |
| 678 } | |
| 679 } | |
| 680 } | |
| 681 | |
| 682 Bind(&if_lhsisnotsmi); | |
| 683 { | |
| 684 // Load the map and instance type of {lhs}. | |
| 685 Node* lhs_instance_type = LoadInstanceType(lhs); | |
| 686 | |
| 687 // Check if {lhs} is a String. | |
| 688 Label if_lhsisstring(this), if_lhsisnotstring(this); | |
| 689 Branch(IsStringInstanceType(lhs_instance_type), &if_lhsisstring, | |
| 690 &if_lhsisnotstring); | |
| 691 | |
| 692 Bind(&if_lhsisstring); | |
| 693 { | |
| 694 var_lhs.Bind(lhs); | |
| 695 var_rhs.Bind(rhs); | |
| 696 Goto(&string_add_convert_right); | |
| 697 } | |
| 698 | |
| 699 Bind(&if_lhsisnotstring); | |
| 700 { | |
| 701 // Check if {rhs} is a Smi. | |
| 702 Label if_rhsissmi(this), if_rhsisnotsmi(this); | |
| 703 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); | |
| 704 | |
| 705 Bind(&if_rhsissmi); | |
| 706 { | |
| 707 // Check if {lhs} is a Number. | |
| 708 Label if_lhsisnumber(this), if_lhsisnotnumber(this, Label::kDeferred); | |
| 709 Branch( | |
| 710 Word32Equal(lhs_instance_type, Int32Constant(HEAP_NUMBER_TYPE)), | |
| 711 &if_lhsisnumber, &if_lhsisnotnumber); | |
| 712 | |
| 713 Bind(&if_lhsisnumber); | |
| 714 { | |
| 715 // The {lhs} is a HeapNumber, the {rhs} is a Smi, just add them. | |
| 716 var_fadd_lhs.Bind(LoadHeapNumberValue(lhs)); | |
| 717 var_fadd_rhs.Bind(SmiToFloat64(rhs)); | |
| 718 Goto(&do_fadd); | |
| 719 } | |
| 720 | |
| 721 Bind(&if_lhsisnotnumber); | |
| 722 { | |
| 723 // The {lhs} is neither a Number nor a String, and the {rhs} is a | |
| 724 // Smi. | |
| 725 Label if_lhsisreceiver(this, Label::kDeferred), | |
| 726 if_lhsisnotreceiver(this, Label::kDeferred); | |
| 727 Branch(IsJSReceiverInstanceType(lhs_instance_type), | |
| 728 &if_lhsisreceiver, &if_lhsisnotreceiver); | |
| 729 | |
| 730 Bind(&if_lhsisreceiver); | |
| 731 { | |
| 732 // Convert {lhs} to a primitive first passing no hint. | |
| 733 Callable callable = | |
| 734 CodeFactory::NonPrimitiveToPrimitive(isolate()); | |
| 735 var_lhs.Bind(CallStub(callable, context, lhs)); | |
| 736 Goto(&loop); | |
| 737 } | |
| 738 | |
| 739 Bind(&if_lhsisnotreceiver); | |
| 740 { | |
| 741 // Convert {lhs} to a Number first. | |
| 742 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 743 var_lhs.Bind(CallStub(callable, context, lhs)); | |
| 744 Goto(&loop); | |
| 745 } | |
| 746 } | |
| 747 } | |
| 748 | |
| 749 Bind(&if_rhsisnotsmi); | |
| 750 { | |
| 751 // Load the instance type of {rhs}. | |
| 752 Node* rhs_instance_type = LoadInstanceType(rhs); | |
| 753 | |
| 754 // Check if {rhs} is a String. | |
| 755 Label if_rhsisstring(this), if_rhsisnotstring(this); | |
| 756 Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, | |
| 757 &if_rhsisnotstring); | |
| 758 | |
| 759 Bind(&if_rhsisstring); | |
| 760 { | |
| 761 var_lhs.Bind(lhs); | |
| 762 var_rhs.Bind(rhs); | |
| 763 Goto(&string_add_convert_left); | |
| 764 } | |
| 765 | |
| 766 Bind(&if_rhsisnotstring); | |
| 767 { | |
| 768 // Check if {lhs} is a HeapNumber. | |
| 769 Label if_lhsisnumber(this), if_lhsisnotnumber(this); | |
| 770 Branch( | |
| 771 Word32Equal(lhs_instance_type, Int32Constant(HEAP_NUMBER_TYPE)), | |
| 772 &if_lhsisnumber, &if_lhsisnotnumber); | |
| 773 | |
| 774 Bind(&if_lhsisnumber); | |
| 775 { | |
| 776 // Check if {rhs} is also a HeapNumber. | |
| 777 Label if_rhsisnumber(this), | |
| 778 if_rhsisnotnumber(this, Label::kDeferred); | |
| 779 Branch(Word32Equal(rhs_instance_type, | |
| 780 Int32Constant(HEAP_NUMBER_TYPE)), | |
| 781 &if_rhsisnumber, &if_rhsisnotnumber); | |
| 782 | |
| 783 Bind(&if_rhsisnumber); | |
| 784 { | |
| 785 // Perform a floating point addition. | |
| 786 var_fadd_lhs.Bind(LoadHeapNumberValue(lhs)); | |
| 787 var_fadd_rhs.Bind(LoadHeapNumberValue(rhs)); | |
| 788 Goto(&do_fadd); | |
| 789 } | |
| 790 | |
| 791 Bind(&if_rhsisnotnumber); | |
| 792 { | |
| 793 // Check if {rhs} is a JSReceiver. | |
| 794 Label if_rhsisreceiver(this, Label::kDeferred), | |
| 795 if_rhsisnotreceiver(this, Label::kDeferred); | |
| 796 Branch(IsJSReceiverInstanceType(rhs_instance_type), | |
| 797 &if_rhsisreceiver, &if_rhsisnotreceiver); | |
| 798 | |
| 799 Bind(&if_rhsisreceiver); | |
| 800 { | |
| 801 // Convert {rhs} to a primitive first passing no hint. | |
| 802 Callable callable = | |
| 803 CodeFactory::NonPrimitiveToPrimitive(isolate()); | |
| 804 var_rhs.Bind(CallStub(callable, context, rhs)); | |
| 805 Goto(&loop); | |
| 806 } | |
| 807 | |
| 808 Bind(&if_rhsisnotreceiver); | |
| 809 { | |
| 810 // Convert {rhs} to a Number first. | |
| 811 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 812 var_rhs.Bind(CallStub(callable, context, rhs)); | |
| 813 Goto(&loop); | |
| 814 } | |
| 815 } | |
| 816 } | |
| 817 | |
| 818 Bind(&if_lhsisnotnumber); | |
| 819 { | |
| 820 // Check if {lhs} is a JSReceiver. | |
| 821 Label if_lhsisreceiver(this, Label::kDeferred), | |
| 822 if_lhsisnotreceiver(this); | |
| 823 Branch(IsJSReceiverInstanceType(lhs_instance_type), | |
| 824 &if_lhsisreceiver, &if_lhsisnotreceiver); | |
| 825 | |
| 826 Bind(&if_lhsisreceiver); | |
| 827 { | |
| 828 // Convert {lhs} to a primitive first passing no hint. | |
| 829 Callable callable = | |
| 830 CodeFactory::NonPrimitiveToPrimitive(isolate()); | |
| 831 var_lhs.Bind(CallStub(callable, context, lhs)); | |
| 832 Goto(&loop); | |
| 833 } | |
| 834 | |
| 835 Bind(&if_lhsisnotreceiver); | |
| 836 { | |
| 837 // Check if {rhs} is a JSReceiver. | |
| 838 Label if_rhsisreceiver(this, Label::kDeferred), | |
| 839 if_rhsisnotreceiver(this, Label::kDeferred); | |
| 840 Branch(IsJSReceiverInstanceType(rhs_instance_type), | |
| 841 &if_rhsisreceiver, &if_rhsisnotreceiver); | |
| 842 | |
| 843 Bind(&if_rhsisreceiver); | |
| 844 { | |
| 845 // Convert {rhs} to a primitive first passing no hint. | |
| 846 Callable callable = | |
| 847 CodeFactory::NonPrimitiveToPrimitive(isolate()); | |
| 848 var_rhs.Bind(CallStub(callable, context, rhs)); | |
| 849 Goto(&loop); | |
| 850 } | |
| 851 | |
| 852 Bind(&if_rhsisnotreceiver); | |
| 853 { | |
| 854 // Convert {lhs} to a Number first. | |
| 855 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 856 var_lhs.Bind(CallStub(callable, context, lhs)); | |
| 857 Goto(&loop); | |
| 858 } | |
| 859 } | |
| 860 } | |
| 861 } | |
| 862 } | |
| 863 } | |
| 864 } | |
| 865 } | |
| 866 Bind(&string_add_convert_left); | |
| 867 { | |
| 868 // Convert {lhs}, which is a Smi, to a String and concatenate the | |
| 869 // resulting string with the String {rhs}. | |
| 870 Callable callable = | |
| 871 CodeFactory::StringAdd(isolate(), STRING_ADD_CONVERT_LEFT, NOT_TENURED); | |
| 872 var_result.Bind( | |
| 873 CallStub(callable, context, var_lhs.value(), var_rhs.value())); | |
| 874 Goto(&end); | |
| 875 } | |
| 876 | |
| 877 Bind(&string_add_convert_right); | |
| 878 { | |
| 879 // Convert {lhs}, which is a Smi, to a String and concatenate the | |
| 880 // resulting string with the String {rhs}. | |
| 881 Callable callable = CodeFactory::StringAdd( | |
| 882 isolate(), STRING_ADD_CONVERT_RIGHT, NOT_TENURED); | |
| 883 var_result.Bind( | |
| 884 CallStub(callable, context, var_lhs.value(), var_rhs.value())); | |
| 885 Goto(&end); | |
| 886 } | |
| 887 | |
| 888 Bind(&do_fadd); | |
| 889 { | |
| 890 Node* lhs_value = var_fadd_lhs.value(); | |
| 891 Node* rhs_value = var_fadd_rhs.value(); | |
| 892 Node* value = Float64Add(lhs_value, rhs_value); | |
| 893 Node* result = AllocateHeapNumberWithValue(value); | |
| 894 var_result.Bind(result); | |
| 895 Goto(&end); | |
| 896 } | |
| 897 Bind(&end); | |
| 898 Return(var_result.value()); | |
| 899 } | |
| 900 | |
| 901 TF_BUILTIN(Subtract, CodeStubAssembler) { | |
| 902 Node* left = Parameter(0); | |
| 903 Node* right = Parameter(1); | |
| 904 Node* context = Parameter(2); | |
| 905 | |
| 906 // Shared entry for floating point subtraction. | |
| 907 Label do_fsub(this), end(this); | |
| 908 Variable var_fsub_lhs(this, MachineRepresentation::kFloat64), | |
| 909 var_fsub_rhs(this, MachineRepresentation::kFloat64); | |
| 910 | |
| 911 // We might need to loop several times due to ToPrimitive and/or ToNumber | |
| 912 // conversions. | |
| 913 Variable var_lhs(this, MachineRepresentation::kTagged), | |
| 914 var_rhs(this, MachineRepresentation::kTagged), | |
| 915 var_result(this, MachineRepresentation::kTagged); | |
| 916 Variable* loop_vars[2] = {&var_lhs, &var_rhs}; | |
| 917 Label loop(this, 2, loop_vars); | |
| 918 var_lhs.Bind(left); | |
| 919 var_rhs.Bind(right); | |
| 920 Goto(&loop); | |
| 921 Bind(&loop); | |
| 922 { | |
| 923 // Load the current {lhs} and {rhs} values. | |
| 924 Node* lhs = var_lhs.value(); | |
| 925 Node* rhs = var_rhs.value(); | |
| 926 | |
| 927 // Check if the {lhs} is a Smi or a HeapObject. | |
| 928 Label if_lhsissmi(this), if_lhsisnotsmi(this); | |
| 929 Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); | |
| 930 | |
| 931 Bind(&if_lhsissmi); | |
| 932 { | |
| 933 // Check if the {rhs} is also a Smi. | |
| 934 Label if_rhsissmi(this), if_rhsisnotsmi(this); | |
| 935 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); | |
| 936 | |
| 937 Bind(&if_rhsissmi); | |
| 938 { | |
| 939 // Try a fast Smi subtraction first. | |
| 940 Node* pair = IntPtrSubWithOverflow(BitcastTaggedToWord(lhs), | |
| 941 BitcastTaggedToWord(rhs)); | |
| 942 Node* overflow = Projection(1, pair); | |
| 943 | |
| 944 // Check if the Smi subtraction overflowed. | |
| 945 Label if_overflow(this), if_notoverflow(this); | |
| 946 Branch(overflow, &if_overflow, &if_notoverflow); | |
| 947 | |
| 948 Bind(&if_overflow); | |
| 949 { | |
| 950 // The result doesn't fit into Smi range. | |
| 951 var_fsub_lhs.Bind(SmiToFloat64(lhs)); | |
| 952 var_fsub_rhs.Bind(SmiToFloat64(rhs)); | |
| 953 Goto(&do_fsub); | |
| 954 } | |
| 955 | |
| 956 Bind(&if_notoverflow); | |
| 957 var_result.Bind(BitcastWordToTaggedSigned(Projection(0, pair))); | |
| 958 Goto(&end); | |
| 959 } | |
| 960 | |
| 961 Bind(&if_rhsisnotsmi); | |
| 962 { | |
| 963 // Load the map of the {rhs}. | |
| 964 Node* rhs_map = LoadMap(rhs); | |
| 965 | |
| 966 // Check if {rhs} is a HeapNumber. | |
| 967 Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred); | |
| 968 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); | |
| 969 | |
| 970 Bind(&if_rhsisnumber); | |
| 971 { | |
| 972 // Perform a floating point subtraction. | |
| 973 var_fsub_lhs.Bind(SmiToFloat64(lhs)); | |
| 974 var_fsub_rhs.Bind(LoadHeapNumberValue(rhs)); | |
| 975 Goto(&do_fsub); | |
| 976 } | |
| 977 | |
| 978 Bind(&if_rhsisnotnumber); | |
| 979 { | |
| 980 // Convert the {rhs} to a Number first. | |
| 981 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 982 var_rhs.Bind(CallStub(callable, context, rhs)); | |
| 983 Goto(&loop); | |
| 984 } | |
| 985 } | |
| 986 } | |
| 987 | |
| 988 Bind(&if_lhsisnotsmi); | |
| 989 { | |
| 990 // Load the map of the {lhs}. | |
| 991 Node* lhs_map = LoadMap(lhs); | |
| 992 | |
| 993 // Check if the {lhs} is a HeapNumber. | |
| 994 Label if_lhsisnumber(this), if_lhsisnotnumber(this, Label::kDeferred); | |
| 995 Branch(IsHeapNumberMap(lhs_map), &if_lhsisnumber, &if_lhsisnotnumber); | |
| 996 | |
| 997 Bind(&if_lhsisnumber); | |
| 998 { | |
| 999 // Check if the {rhs} is a Smi. | |
| 1000 Label if_rhsissmi(this), if_rhsisnotsmi(this); | |
| 1001 Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); | |
| 1002 | |
| 1003 Bind(&if_rhsissmi); | |
| 1004 { | |
| 1005 // Perform a floating point subtraction. | |
| 1006 var_fsub_lhs.Bind(LoadHeapNumberValue(lhs)); | |
| 1007 var_fsub_rhs.Bind(SmiToFloat64(rhs)); | |
| 1008 Goto(&do_fsub); | |
| 1009 } | |
| 1010 | |
| 1011 Bind(&if_rhsisnotsmi); | |
| 1012 { | |
| 1013 // Load the map of the {rhs}. | |
| 1014 Node* rhs_map = LoadMap(rhs); | |
| 1015 | |
| 1016 // Check if the {rhs} is a HeapNumber. | |
| 1017 Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred); | |
| 1018 Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); | |
| 1019 | |
| 1020 Bind(&if_rhsisnumber); | |
| 1021 { | |
| 1022 // Perform a floating point subtraction. | |
| 1023 var_fsub_lhs.Bind(LoadHeapNumberValue(lhs)); | |
| 1024 var_fsub_rhs.Bind(LoadHeapNumberValue(rhs)); | |
| 1025 Goto(&do_fsub); | |
| 1026 } | |
| 1027 | |
| 1028 Bind(&if_rhsisnotnumber); | |
| 1029 { | |
| 1030 // Convert the {rhs} to a Number first. | |
| 1031 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1032 var_rhs.Bind(CallStub(callable, context, rhs)); | |
| 1033 Goto(&loop); | |
| 1034 } | |
| 1035 } | |
| 1036 } | |
| 1037 | |
| 1038 Bind(&if_lhsisnotnumber); | |
| 1039 { | |
| 1040 // Convert the {lhs} to a Number first. | |
| 1041 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1042 var_lhs.Bind(CallStub(callable, context, lhs)); | |
| 1043 Goto(&loop); | |
| 1044 } | |
| 1045 } | |
| 1046 } | |
| 1047 | |
| 1048 Bind(&do_fsub); | |
| 1049 { | |
| 1050 Node* lhs_value = var_fsub_lhs.value(); | |
| 1051 Node* rhs_value = var_fsub_rhs.value(); | |
| 1052 Node* value = Float64Sub(lhs_value, rhs_value); | |
| 1053 var_result.Bind(AllocateHeapNumberWithValue(value)); | |
| 1054 Goto(&end); | |
| 1055 } | |
| 1056 Bind(&end); | |
| 1057 Return(var_result.value()); | |
| 1058 } | |
| 1059 | |
| 1060 TF_BUILTIN(Multiply, CodeStubAssembler) { | |
| 1061 Node* left = Parameter(0); | |
| 1062 Node* right = Parameter(1); | |
| 1063 Node* context = Parameter(2); | |
| 1064 | |
| 1065 // Shared entry point for floating point multiplication. | |
| 1066 Label do_fmul(this), return_result(this); | |
| 1067 Variable var_lhs_float64(this, MachineRepresentation::kFloat64), | |
| 1068 var_rhs_float64(this, MachineRepresentation::kFloat64); | |
| 1069 | |
| 1070 // We might need to loop one or two times due to ToNumber conversions. | |
| 1071 Variable var_lhs(this, MachineRepresentation::kTagged), | |
| 1072 var_rhs(this, MachineRepresentation::kTagged), | |
| 1073 var_result(this, MachineRepresentation::kTagged); | |
| 1074 Variable* loop_variables[] = {&var_lhs, &var_rhs}; | |
| 1075 Label loop(this, 2, loop_variables); | |
| 1076 var_lhs.Bind(left); | |
| 1077 var_rhs.Bind(right); | |
| 1078 Goto(&loop); | |
| 1079 Bind(&loop); | |
| 1080 { | |
| 1081 Node* lhs = var_lhs.value(); | |
| 1082 Node* rhs = var_rhs.value(); | |
| 1083 | |
| 1084 Label lhs_is_smi(this), lhs_is_not_smi(this); | |
| 1085 Branch(TaggedIsSmi(lhs), &lhs_is_smi, &lhs_is_not_smi); | |
| 1086 | |
| 1087 Bind(&lhs_is_smi); | |
| 1088 { | |
| 1089 Label rhs_is_smi(this), rhs_is_not_smi(this); | |
| 1090 Branch(TaggedIsSmi(rhs), &rhs_is_smi, &rhs_is_not_smi); | |
| 1091 | |
| 1092 Bind(&rhs_is_smi); | |
| 1093 { | |
| 1094 // Both {lhs} and {rhs} are Smis. The result is not necessarily a smi, | |
| 1095 // in case of overflow. | |
| 1096 var_result.Bind(SmiMul(lhs, rhs)); | |
| 1097 Goto(&return_result); | |
| 1098 } | |
| 1099 | |
| 1100 Bind(&rhs_is_not_smi); | |
| 1101 { | |
| 1102 Node* rhs_map = LoadMap(rhs); | |
| 1103 | |
| 1104 // Check if {rhs} is a HeapNumber. | |
| 1105 Label rhs_is_number(this), rhs_is_not_number(this, Label::kDeferred); | |
| 1106 Branch(IsHeapNumberMap(rhs_map), &rhs_is_number, &rhs_is_not_number); | |
| 1107 | |
| 1108 Bind(&rhs_is_number); | |
| 1109 { | |
| 1110 // Convert {lhs} to a double and multiply it with the value of {rhs}. | |
| 1111 var_lhs_float64.Bind(SmiToFloat64(lhs)); | |
| 1112 var_rhs_float64.Bind(LoadHeapNumberValue(rhs)); | |
| 1113 Goto(&do_fmul); | |
| 1114 } | |
| 1115 | |
| 1116 Bind(&rhs_is_not_number); | |
| 1117 { | |
| 1118 // Multiplication is commutative, swap {lhs} with {rhs} and loop. | |
| 1119 var_lhs.Bind(rhs); | |
| 1120 var_rhs.Bind(lhs); | |
| 1121 Goto(&loop); | |
| 1122 } | |
| 1123 } | |
| 1124 } | |
| 1125 | |
| 1126 Bind(&lhs_is_not_smi); | |
| 1127 { | |
| 1128 Node* lhs_map = LoadMap(lhs); | |
| 1129 | |
| 1130 // Check if {lhs} is a HeapNumber. | |
| 1131 Label lhs_is_number(this), lhs_is_not_number(this, Label::kDeferred); | |
| 1132 Branch(IsHeapNumberMap(lhs_map), &lhs_is_number, &lhs_is_not_number); | |
| 1133 | |
| 1134 Bind(&lhs_is_number); | |
| 1135 { | |
| 1136 // Check if {rhs} is a Smi. | |
| 1137 Label rhs_is_smi(this), rhs_is_not_smi(this); | |
| 1138 Branch(TaggedIsSmi(rhs), &rhs_is_smi, &rhs_is_not_smi); | |
| 1139 | |
| 1140 Bind(&rhs_is_smi); | |
| 1141 { | |
| 1142 // Convert {rhs} to a double and multiply it with the value of {lhs}. | |
| 1143 var_lhs_float64.Bind(LoadHeapNumberValue(lhs)); | |
| 1144 var_rhs_float64.Bind(SmiToFloat64(rhs)); | |
| 1145 Goto(&do_fmul); | |
| 1146 } | |
| 1147 | |
| 1148 Bind(&rhs_is_not_smi); | |
| 1149 { | |
| 1150 Node* rhs_map = LoadMap(rhs); | |
| 1151 | |
| 1152 // Check if {rhs} is a HeapNumber. | |
| 1153 Label rhs_is_number(this), rhs_is_not_number(this, Label::kDeferred); | |
| 1154 Branch(IsHeapNumberMap(rhs_map), &rhs_is_number, &rhs_is_not_number); | |
| 1155 | |
| 1156 Bind(&rhs_is_number); | |
| 1157 { | |
| 1158 // Both {lhs} and {rhs} are HeapNumbers. Load their values and | |
| 1159 // multiply them. | |
| 1160 var_lhs_float64.Bind(LoadHeapNumberValue(lhs)); | |
| 1161 var_rhs_float64.Bind(LoadHeapNumberValue(rhs)); | |
| 1162 Goto(&do_fmul); | |
| 1163 } | |
| 1164 | |
| 1165 Bind(&rhs_is_not_number); | |
| 1166 { | |
| 1167 // Multiplication is commutative, swap {lhs} with {rhs} and loop. | |
| 1168 var_lhs.Bind(rhs); | |
| 1169 var_rhs.Bind(lhs); | |
| 1170 Goto(&loop); | |
| 1171 } | |
| 1172 } | |
| 1173 } | |
| 1174 | |
| 1175 Bind(&lhs_is_not_number); | |
| 1176 { | |
| 1177 // Convert {lhs} to a Number and loop. | |
| 1178 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1179 var_lhs.Bind(CallStub(callable, context, lhs)); | |
| 1180 Goto(&loop); | |
| 1181 } | |
| 1182 } | |
| 1183 } | |
| 1184 | |
| 1185 Bind(&do_fmul); | |
| 1186 { | |
| 1187 Node* value = Float64Mul(var_lhs_float64.value(), var_rhs_float64.value()); | |
| 1188 Node* result = AllocateHeapNumberWithValue(value); | |
| 1189 var_result.Bind(result); | |
| 1190 Goto(&return_result); | |
| 1191 } | |
| 1192 | |
| 1193 Bind(&return_result); | |
| 1194 Return(var_result.value()); | |
| 1195 } | |
| 1196 | |
| 1197 TF_BUILTIN(Divide, CodeStubAssembler) { | |
| 1198 Node* left = Parameter(0); | |
| 1199 Node* right = Parameter(1); | |
| 1200 Node* context = Parameter(2); | |
| 1201 | |
| 1202 // Shared entry point for floating point division. | |
| 1203 Label do_fdiv(this), end(this); | |
| 1204 Variable var_dividend_float64(this, MachineRepresentation::kFloat64), | |
| 1205 var_divisor_float64(this, MachineRepresentation::kFloat64); | |
| 1206 | |
| 1207 // We might need to loop one or two times due to ToNumber conversions. | |
| 1208 Variable var_dividend(this, MachineRepresentation::kTagged), | |
| 1209 var_divisor(this, MachineRepresentation::kTagged), | |
| 1210 var_result(this, MachineRepresentation::kTagged); | |
| 1211 Variable* loop_variables[] = {&var_dividend, &var_divisor}; | |
| 1212 Label loop(this, 2, loop_variables); | |
| 1213 var_dividend.Bind(left); | |
| 1214 var_divisor.Bind(right); | |
| 1215 Goto(&loop); | |
| 1216 Bind(&loop); | |
| 1217 { | |
| 1218 Node* dividend = var_dividend.value(); | |
| 1219 Node* divisor = var_divisor.value(); | |
| 1220 | |
| 1221 Label dividend_is_smi(this), dividend_is_not_smi(this); | |
| 1222 Branch(TaggedIsSmi(dividend), ÷nd_is_smi, ÷nd_is_not_smi); | |
| 1223 | |
| 1224 Bind(÷nd_is_smi); | |
| 1225 { | |
| 1226 Label divisor_is_smi(this), divisor_is_not_smi(this); | |
| 1227 Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi); | |
| 1228 | |
| 1229 Bind(&divisor_is_smi); | |
| 1230 { | |
| 1231 Label bailout(this); | |
| 1232 | |
| 1233 // Do floating point division if {divisor} is zero. | |
| 1234 GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout); | |
| 1235 | |
| 1236 // Do floating point division {dividend} is zero and {divisor} is | |
| 1237 // negative. | |
| 1238 Label dividend_is_zero(this), dividend_is_not_zero(this); | |
| 1239 Branch(SmiEqual(dividend, SmiConstant(0)), ÷nd_is_zero, | |
| 1240 ÷nd_is_not_zero); | |
| 1241 | |
| 1242 Bind(÷nd_is_zero); | |
| 1243 { | |
| 1244 GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout); | |
| 1245 Goto(÷nd_is_not_zero); | |
| 1246 } | |
| 1247 Bind(÷nd_is_not_zero); | |
| 1248 | |
| 1249 Node* untagged_divisor = SmiToWord32(divisor); | |
| 1250 Node* untagged_dividend = SmiToWord32(dividend); | |
| 1251 | |
| 1252 // Do floating point division if {dividend} is kMinInt (or kMinInt - 1 | |
| 1253 // if the Smi size is 31) and {divisor} is -1. | |
| 1254 Label divisor_is_minus_one(this), divisor_is_not_minus_one(this); | |
| 1255 Branch(Word32Equal(untagged_divisor, Int32Constant(-1)), | |
| 1256 &divisor_is_minus_one, &divisor_is_not_minus_one); | |
| 1257 | |
| 1258 Bind(&divisor_is_minus_one); | |
| 1259 { | |
| 1260 GotoIf( | |
| 1261 Word32Equal(untagged_dividend, | |
| 1262 Int32Constant(kSmiValueSize == 32 ? kMinInt | |
| 1263 : (kMinInt >> 1))), | |
| 1264 &bailout); | |
| 1265 Goto(&divisor_is_not_minus_one); | |
| 1266 } | |
| 1267 Bind(&divisor_is_not_minus_one); | |
| 1268 | |
| 1269 // TODO(epertoso): consider adding a machine instruction that returns | |
| 1270 // both the result and the remainder. | |
| 1271 Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor); | |
| 1272 Node* truncated = Int32Mul(untagged_result, untagged_divisor); | |
| 1273 // Do floating point division if the remainder is not 0. | |
| 1274 GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout); | |
| 1275 var_result.Bind(SmiFromWord32(untagged_result)); | |
| 1276 Goto(&end); | |
| 1277 | |
| 1278 // Bailout: convert {dividend} and {divisor} to double and do double | |
| 1279 // division. | |
| 1280 Bind(&bailout); | |
| 1281 { | |
| 1282 var_dividend_float64.Bind(SmiToFloat64(dividend)); | |
| 1283 var_divisor_float64.Bind(SmiToFloat64(divisor)); | |
| 1284 Goto(&do_fdiv); | |
| 1285 } | |
| 1286 } | |
| 1287 | |
| 1288 Bind(&divisor_is_not_smi); | |
| 1289 { | |
| 1290 Node* divisor_map = LoadMap(divisor); | |
| 1291 | |
| 1292 // Check if {divisor} is a HeapNumber. | |
| 1293 Label divisor_is_number(this), | |
| 1294 divisor_is_not_number(this, Label::kDeferred); | |
| 1295 Branch(IsHeapNumberMap(divisor_map), &divisor_is_number, | |
| 1296 &divisor_is_not_number); | |
| 1297 | |
| 1298 Bind(&divisor_is_number); | |
| 1299 { | |
| 1300 // Convert {dividend} to a double and divide it with the value of | |
| 1301 // {divisor}. | |
| 1302 var_dividend_float64.Bind(SmiToFloat64(dividend)); | |
| 1303 var_divisor_float64.Bind(LoadHeapNumberValue(divisor)); | |
| 1304 Goto(&do_fdiv); | |
| 1305 } | |
| 1306 | |
| 1307 Bind(&divisor_is_not_number); | |
| 1308 { | |
| 1309 // Convert {divisor} to a number and loop. | |
| 1310 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1311 var_divisor.Bind(CallStub(callable, context, divisor)); | |
| 1312 Goto(&loop); | |
| 1313 } | |
| 1314 } | |
| 1315 } | |
| 1316 | |
| 1317 Bind(÷nd_is_not_smi); | |
| 1318 { | |
| 1319 Node* dividend_map = LoadMap(dividend); | |
| 1320 | |
| 1321 // Check if {dividend} is a HeapNumber. | |
| 1322 Label dividend_is_number(this), | |
| 1323 dividend_is_not_number(this, Label::kDeferred); | |
| 1324 Branch(IsHeapNumberMap(dividend_map), ÷nd_is_number, | |
| 1325 ÷nd_is_not_number); | |
| 1326 | |
| 1327 Bind(÷nd_is_number); | |
| 1328 { | |
| 1329 // Check if {divisor} is a Smi. | |
| 1330 Label divisor_is_smi(this), divisor_is_not_smi(this); | |
| 1331 Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi); | |
| 1332 | |
| 1333 Bind(&divisor_is_smi); | |
| 1334 { | |
| 1335 // Convert {divisor} to a double and use it for a floating point | |
| 1336 // division. | |
| 1337 var_dividend_float64.Bind(LoadHeapNumberValue(dividend)); | |
| 1338 var_divisor_float64.Bind(SmiToFloat64(divisor)); | |
| 1339 Goto(&do_fdiv); | |
| 1340 } | |
| 1341 | |
| 1342 Bind(&divisor_is_not_smi); | |
| 1343 { | |
| 1344 Node* divisor_map = LoadMap(divisor); | |
| 1345 | |
| 1346 // Check if {divisor} is a HeapNumber. | |
| 1347 Label divisor_is_number(this), | |
| 1348 divisor_is_not_number(this, Label::kDeferred); | |
| 1349 Branch(IsHeapNumberMap(divisor_map), &divisor_is_number, | |
| 1350 &divisor_is_not_number); | |
| 1351 | |
| 1352 Bind(&divisor_is_number); | |
| 1353 { | |
| 1354 // Both {dividend} and {divisor} are HeapNumbers. Load their values | |
| 1355 // and divide them. | |
| 1356 var_dividend_float64.Bind(LoadHeapNumberValue(dividend)); | |
| 1357 var_divisor_float64.Bind(LoadHeapNumberValue(divisor)); | |
| 1358 Goto(&do_fdiv); | |
| 1359 } | |
| 1360 | |
| 1361 Bind(&divisor_is_not_number); | |
| 1362 { | |
| 1363 // Convert {divisor} to a number and loop. | |
| 1364 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1365 var_divisor.Bind(CallStub(callable, context, divisor)); | |
| 1366 Goto(&loop); | |
| 1367 } | |
| 1368 } | |
| 1369 } | |
| 1370 | |
| 1371 Bind(÷nd_is_not_number); | |
| 1372 { | |
| 1373 // Convert {dividend} to a Number and loop. | |
| 1374 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1375 var_dividend.Bind(CallStub(callable, context, dividend)); | |
| 1376 Goto(&loop); | |
| 1377 } | |
| 1378 } | |
| 1379 } | |
| 1380 | |
| 1381 Bind(&do_fdiv); | |
| 1382 { | |
| 1383 Node* value = | |
| 1384 Float64Div(var_dividend_float64.value(), var_divisor_float64.value()); | |
| 1385 var_result.Bind(AllocateHeapNumberWithValue(value)); | |
| 1386 Goto(&end); | |
| 1387 } | |
| 1388 Bind(&end); | |
| 1389 Return(var_result.value()); | |
| 1390 } | |
| 1391 | |
| 1392 TF_BUILTIN(Modulus, CodeStubAssembler) { | |
| 1393 Node* left = Parameter(0); | |
| 1394 Node* right = Parameter(1); | |
| 1395 Node* context = Parameter(2); | |
| 1396 | |
| 1397 Variable var_result(this, MachineRepresentation::kTagged); | |
| 1398 Label return_result(this, &var_result); | |
| 1399 | |
| 1400 // Shared entry point for floating point modulus. | |
| 1401 Label do_fmod(this); | |
| 1402 Variable var_dividend_float64(this, MachineRepresentation::kFloat64), | |
| 1403 var_divisor_float64(this, MachineRepresentation::kFloat64); | |
| 1404 | |
| 1405 // We might need to loop one or two times due to ToNumber conversions. | |
| 1406 Variable var_dividend(this, MachineRepresentation::kTagged), | |
| 1407 var_divisor(this, MachineRepresentation::kTagged); | |
| 1408 Variable* loop_variables[] = {&var_dividend, &var_divisor}; | |
| 1409 Label loop(this, 2, loop_variables); | |
| 1410 var_dividend.Bind(left); | |
| 1411 var_divisor.Bind(right); | |
| 1412 Goto(&loop); | |
| 1413 Bind(&loop); | |
| 1414 { | |
| 1415 Node* dividend = var_dividend.value(); | |
| 1416 Node* divisor = var_divisor.value(); | |
| 1417 | |
| 1418 Label dividend_is_smi(this), dividend_is_not_smi(this); | |
| 1419 Branch(TaggedIsSmi(dividend), ÷nd_is_smi, ÷nd_is_not_smi); | |
| 1420 | |
| 1421 Bind(÷nd_is_smi); | |
| 1422 { | |
| 1423 Label dividend_is_not_zero(this); | |
| 1424 Label divisor_is_smi(this), divisor_is_not_smi(this); | |
| 1425 Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi); | |
| 1426 | |
| 1427 Bind(&divisor_is_smi); | |
| 1428 { | |
| 1429 // Compute the modulus of two Smis. | |
| 1430 var_result.Bind(SmiMod(dividend, divisor)); | |
| 1431 Goto(&return_result); | |
| 1432 } | |
| 1433 | |
| 1434 Bind(&divisor_is_not_smi); | |
| 1435 { | |
| 1436 Node* divisor_map = LoadMap(divisor); | |
| 1437 | |
| 1438 // Check if {divisor} is a HeapNumber. | |
| 1439 Label divisor_is_number(this), | |
| 1440 divisor_is_not_number(this, Label::kDeferred); | |
| 1441 Branch(IsHeapNumberMap(divisor_map), &divisor_is_number, | |
| 1442 &divisor_is_not_number); | |
| 1443 | |
| 1444 Bind(&divisor_is_number); | |
| 1445 { | |
| 1446 // Convert {dividend} to a double and compute its modulus with the | |
| 1447 // value of {dividend}. | |
| 1448 var_dividend_float64.Bind(SmiToFloat64(dividend)); | |
| 1449 var_divisor_float64.Bind(LoadHeapNumberValue(divisor)); | |
| 1450 Goto(&do_fmod); | |
| 1451 } | |
| 1452 | |
| 1453 Bind(&divisor_is_not_number); | |
| 1454 { | |
| 1455 // Convert {divisor} to a number and loop. | |
| 1456 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1457 var_divisor.Bind(CallStub(callable, context, divisor)); | |
| 1458 Goto(&loop); | |
| 1459 } | |
| 1460 } | |
| 1461 } | |
| 1462 | |
| 1463 Bind(÷nd_is_not_smi); | |
| 1464 { | |
| 1465 Node* dividend_map = LoadMap(dividend); | |
| 1466 | |
| 1467 // Check if {dividend} is a HeapNumber. | |
| 1468 Label dividend_is_number(this), | |
| 1469 dividend_is_not_number(this, Label::kDeferred); | |
| 1470 Branch(IsHeapNumberMap(dividend_map), ÷nd_is_number, | |
| 1471 ÷nd_is_not_number); | |
| 1472 | |
| 1473 Bind(÷nd_is_number); | |
| 1474 { | |
| 1475 // Check if {divisor} is a Smi. | |
| 1476 Label divisor_is_smi(this), divisor_is_not_smi(this); | |
| 1477 Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi); | |
| 1478 | |
| 1479 Bind(&divisor_is_smi); | |
| 1480 { | |
| 1481 // Convert {divisor} to a double and compute {dividend}'s modulus with | |
| 1482 // it. | |
| 1483 var_dividend_float64.Bind(LoadHeapNumberValue(dividend)); | |
| 1484 var_divisor_float64.Bind(SmiToFloat64(divisor)); | |
| 1485 Goto(&do_fmod); | |
| 1486 } | |
| 1487 | |
| 1488 Bind(&divisor_is_not_smi); | |
| 1489 { | |
| 1490 Node* divisor_map = LoadMap(divisor); | |
| 1491 | |
| 1492 // Check if {divisor} is a HeapNumber. | |
| 1493 Label divisor_is_number(this), | |
| 1494 divisor_is_not_number(this, Label::kDeferred); | |
| 1495 Branch(IsHeapNumberMap(divisor_map), &divisor_is_number, | |
| 1496 &divisor_is_not_number); | |
| 1497 | |
| 1498 Bind(&divisor_is_number); | |
| 1499 { | |
| 1500 // Both {dividend} and {divisor} are HeapNumbers. Load their values | |
| 1501 // and compute their modulus. | |
| 1502 var_dividend_float64.Bind(LoadHeapNumberValue(dividend)); | |
| 1503 var_divisor_float64.Bind(LoadHeapNumberValue(divisor)); | |
| 1504 Goto(&do_fmod); | |
| 1505 } | |
| 1506 | |
| 1507 Bind(&divisor_is_not_number); | |
| 1508 { | |
| 1509 // Convert {divisor} to a number and loop. | |
| 1510 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1511 var_divisor.Bind(CallStub(callable, context, divisor)); | |
| 1512 Goto(&loop); | |
| 1513 } | |
| 1514 } | |
| 1515 } | |
| 1516 | |
| 1517 Bind(÷nd_is_not_number); | |
| 1518 { | |
| 1519 // Convert {dividend} to a Number and loop. | |
| 1520 Callable callable = CodeFactory::NonNumberToNumber(isolate()); | |
| 1521 var_dividend.Bind(CallStub(callable, context, dividend)); | |
| 1522 Goto(&loop); | |
| 1523 } | |
| 1524 } | |
| 1525 } | |
| 1526 | |
| 1527 Bind(&do_fmod); | |
| 1528 { | |
| 1529 Node* value = | |
| 1530 Float64Mod(var_dividend_float64.value(), var_divisor_float64.value()); | |
| 1531 var_result.Bind(AllocateHeapNumberWithValue(value)); | |
| 1532 Goto(&return_result); | |
| 1533 } | |
| 1534 | |
| 1535 Bind(&return_result); | |
| 1536 Return(var_result.value()); | |
| 1537 } | |
| 1538 | |
| 1539 TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) { | |
| 1540 BitwiseShiftOp([this](Node* lhs, Node* shift_count) { | |
| 1541 return Word32Shl(lhs, shift_count); | |
| 1542 }); | |
| 1543 } | |
| 1544 | |
| 1545 TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) { | |
| 1546 BitwiseShiftOp([this](Node* lhs, Node* shift_count) { | |
| 1547 return Word32Sar(lhs, shift_count); | |
| 1548 }); | |
| 1549 } | |
| 1550 | |
| 1551 TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) { | |
| 1552 BitwiseShiftOp<kUnsigned>([this](Node* lhs, Node* shift_count) { | |
| 1553 return Word32Shr(lhs, shift_count); | |
| 1554 }); | |
| 1555 } | |
| 1556 | |
| 1557 TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) { | |
| 1558 BitwiseOp([this](Node* lhs, Node* rhs) { return Word32And(lhs, rhs); }); | |
| 1559 } | |
| 1560 | |
| 1561 TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) { | |
| 1562 BitwiseOp([this](Node* lhs, Node* rhs) { return Word32Or(lhs, rhs); }); | |
| 1563 } | |
| 1564 | |
| 1565 TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) { | |
| 1566 BitwiseOp([this](Node* lhs, Node* rhs) { return Word32Xor(lhs, rhs); }); | |
| 1567 } | |
| 1568 | |
| 1569 TF_BUILTIN(LessThan, NumberBuiltinsAssembler) { | |
| 1570 RelationalComparisonBuiltin(kLessThan); | |
| 1571 } | |
| 1572 | |
| 1573 TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) { | |
| 1574 RelationalComparisonBuiltin(kLessThanOrEqual); | |
| 1575 } | |
| 1576 | |
| 1577 TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) { | |
| 1578 RelationalComparisonBuiltin(kGreaterThan); | |
| 1579 } | |
| 1580 | |
| 1581 TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) { | |
| 1582 RelationalComparisonBuiltin(kGreaterThanOrEqual); | |
| 1583 } | |
| 1584 | |
| 1585 TF_BUILTIN(Equal, CodeStubAssembler) { | |
| 1586 Node* lhs = Parameter(0); | |
| 1587 Node* rhs = Parameter(1); | |
| 1588 Node* context = Parameter(2); | |
| 1589 | |
| 1590 Return(Equal(lhs, rhs, context)); | |
| 1591 } | |
| 1592 | |
| 1593 TF_BUILTIN(StrictEqual, CodeStubAssembler) { | |
| 1594 Node* lhs = Parameter(0); | |
| 1595 Node* rhs = Parameter(1); | |
| 1596 | |
| 1597 Return(StrictEqual(lhs, rhs)); | |
| 1598 } | |
| 1599 | |
| 1600 } // namespace internal | 227 } // namespace internal |
| 1601 } // namespace v8 | 228 } // namespace v8 |
| OLD | NEW |