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/builtins/builtins.h" 6 #include "src/builtins/builtins-utils.h" 7 8 #include "src/code-factory.h" 9 10 namespace v8 { 11 namespace internal { 12 13 // ----------------------------------------------------------------------------- 14 // ES6 section 20.2.2 Function Properties of the Math Object 15 16 // ES6 section - 20.2.2.1 Math.abs ( x ) 17 void Builtins::Generate_MathAbs(CodeStubAssembler* assembler) { 18 typedef CodeStubAssembler::Label Label; 19 typedef compiler::Node Node; 20 typedef CodeStubAssembler::Variable Variable; 21 22 Node* context = assembler->Parameter(4); 23 24 // We might need to loop once for ToNumber conversion. 25 Variable var_x(assembler, MachineRepresentation::kTagged); 26 Label loop(assembler, &var_x); 27 var_x.Bind(assembler->Parameter(1)); 28 assembler->Goto(&loop); 29 assembler->Bind(&loop); 30 { 31 // Load the current {x} value. 32 Node* x = var_x.value(); 33 34 // Check if {x} is a Smi or a HeapObject. 35 Label if_xissmi(assembler), if_xisnotsmi(assembler); 36 assembler->Branch(assembler->TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); 37 38 assembler->Bind(&if_xissmi); 39 { 40 // Check if {x} is already positive. 41 Label if_xispositive(assembler), if_xisnotpositive(assembler); 42 assembler->BranchIfSmiLessThanOrEqual( 43 assembler->SmiConstant(Smi::FromInt(0)), x, &if_xispositive, 44 &if_xisnotpositive); 45 46 assembler->Bind(&if_xispositive); 47 { 48 // Just return the input {x}. 49 assembler->Return(x); 50 } 51 52 assembler->Bind(&if_xisnotpositive); 53 { 54 // Try to negate the {x} value. 55 Node* pair = assembler->IntPtrSubWithOverflow( 56 assembler->IntPtrConstant(0), assembler->BitcastTaggedToWord(x)); 57 Node* overflow = assembler->Projection(1, pair); 58 Label if_overflow(assembler, Label::kDeferred), 59 if_notoverflow(assembler); 60 assembler->Branch(overflow, &if_overflow, &if_notoverflow); 61 62 assembler->Bind(&if_notoverflow); 63 { 64 // There is a Smi representation for negated {x}. 65 Node* result = assembler->Projection(0, pair); 66 result = assembler->BitcastWordToTagged(result); 67 assembler->Return(result); 68 } 69 70 assembler->Bind(&if_overflow); 71 { 72 Node* result = assembler->NumberConstant(0.0 - Smi::kMinValue); 73 assembler->Return(result); 74 } 75 } 76 } 77 78 assembler->Bind(&if_xisnotsmi); 79 { 80 // Check if {x} is a HeapNumber. 81 Label if_xisheapnumber(assembler), 82 if_xisnotheapnumber(assembler, Label::kDeferred); 83 assembler->Branch( 84 assembler->WordEqual(assembler->LoadMap(x), 85 assembler->HeapNumberMapConstant()), 86 &if_xisheapnumber, &if_xisnotheapnumber); 87 88 assembler->Bind(&if_xisheapnumber); 89 { 90 Node* x_value = assembler->LoadHeapNumberValue(x); 91 Node* value = assembler->Float64Abs(x_value); 92 Node* result = assembler->AllocateHeapNumberWithValue(value); 93 assembler->Return(result); 94 } 95 96 assembler->Bind(&if_xisnotheapnumber); 97 { 98 // Need to convert {x} to a Number first. 99 Callable callable = 100 CodeFactory::NonNumberToNumber(assembler->isolate()); 101 var_x.Bind(assembler->CallStub(callable, context, x)); 102 assembler->Goto(&loop); 103 } 104 } 105 } 106 } 107 108 namespace { 109 110 void Generate_MathRoundingOperation( 111 CodeStubAssembler* assembler, 112 compiler::Node* (CodeStubAssembler::*float64op)(compiler::Node*)) { 113 typedef CodeStubAssembler::Label Label; 114 typedef compiler::Node Node; 115 typedef CodeStubAssembler::Variable Variable; 116 117 Node* context = assembler->Parameter(4); 118 119 // We might need to loop once for ToNumber conversion. 120 Variable var_x(assembler, MachineRepresentation::kTagged); 121 Label loop(assembler, &var_x); 122 var_x.Bind(assembler->Parameter(1)); 123 assembler->Goto(&loop); 124 assembler->Bind(&loop); 125 { 126 // Load the current {x} value. 127 Node* x = var_x.value(); 128 129 // Check if {x} is a Smi or a HeapObject. 130 Label if_xissmi(assembler), if_xisnotsmi(assembler); 131 assembler->Branch(assembler->TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); 132 133 assembler->Bind(&if_xissmi); 134 { 135 // Nothing to do when {x} is a Smi. 136 assembler->Return(x); 137 } 138 139 assembler->Bind(&if_xisnotsmi); 140 { 141 // Check if {x} is a HeapNumber. 142 Label if_xisheapnumber(assembler), 143 if_xisnotheapnumber(assembler, Label::kDeferred); 144 assembler->Branch( 145 assembler->WordEqual(assembler->LoadMap(x), 146 assembler->HeapNumberMapConstant()), 147 &if_xisheapnumber, &if_xisnotheapnumber); 148 149 assembler->Bind(&if_xisheapnumber); 150 { 151 Node* x_value = assembler->LoadHeapNumberValue(x); 152 Node* value = (assembler->*float64op)(x_value); 153 Node* result = assembler->ChangeFloat64ToTagged(value); 154 assembler->Return(result); 155 } 156 157 assembler->Bind(&if_xisnotheapnumber); 158 { 159 // Need to convert {x} to a Number first. 160 Callable callable = 161 CodeFactory::NonNumberToNumber(assembler->isolate()); 162 var_x.Bind(assembler->CallStub(callable, context, x)); 163 assembler->Goto(&loop); 164 } 165 } 166 } 167 } 168 169 void Generate_MathUnaryOperation( 170 CodeStubAssembler* assembler, 171 compiler::Node* (CodeStubAssembler::*float64op)(compiler::Node*)) { 172 typedef compiler::Node Node; 173 174 Node* x = assembler->Parameter(1); 175 Node* context = assembler->Parameter(4); 176 Node* x_value = assembler->TruncateTaggedToFloat64(context, x); 177 Node* value = (assembler->*float64op)(x_value); 178 Node* result = assembler->AllocateHeapNumberWithValue(value); 179 assembler->Return(result); 180 } 181 182 } // namespace 183 184 // ES6 section 20.2.2.2 Math.acos ( x ) 185 void Builtins::Generate_MathAcos(CodeStubAssembler* assembler) { 186 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Acos); 187 } 188 189 // ES6 section 20.2.2.3 Math.acosh ( x ) 190 void Builtins::Generate_MathAcosh(CodeStubAssembler* assembler) { 191 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Acosh); 192 } 193 194 // ES6 section 20.2.2.4 Math.asin ( x ) 195 void Builtins::Generate_MathAsin(CodeStubAssembler* assembler) { 196 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Asin); 197 } 198 199 // ES6 section 20.2.2.5 Math.asinh ( x ) 200 void Builtins::Generate_MathAsinh(CodeStubAssembler* assembler) { 201 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Asinh); 202 } 203 204 // ES6 section 20.2.2.6 Math.atan ( x ) 205 void Builtins::Generate_MathAtan(CodeStubAssembler* assembler) { 206 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Atan); 207 } 208 209 // ES6 section 20.2.2.7 Math.atanh ( x ) 210 void Builtins::Generate_MathAtanh(CodeStubAssembler* assembler) { 211 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Atanh); 212 } 213 214 // ES6 section 20.2.2.8 Math.atan2 ( y, x ) 215 void Builtins::Generate_MathAtan2(CodeStubAssembler* assembler) { 216 using compiler::Node; 217 218 Node* y = assembler->Parameter(1); 219 Node* x = assembler->Parameter(2); 220 Node* context = assembler->Parameter(5); 221 Node* y_value = assembler->TruncateTaggedToFloat64(context, y); 222 Node* x_value = assembler->TruncateTaggedToFloat64(context, x); 223 Node* value = assembler->Float64Atan2(y_value, x_value); 224 Node* result = assembler->AllocateHeapNumberWithValue(value); 225 assembler->Return(result); 226 } 227 228 // ES6 section 20.2.2.10 Math.ceil ( x ) 229 void Builtins::Generate_MathCeil(CodeStubAssembler* assembler) { 230 Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Ceil); 231 } 232 233 // ES6 section 20.2.2.9 Math.cbrt ( x ) 234 void Builtins::Generate_MathCbrt(CodeStubAssembler* assembler) { 235 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Cbrt); 236 } 237 238 // ES6 section 20.2.2.11 Math.clz32 ( x ) 239 void Builtins::Generate_MathClz32(CodeStubAssembler* assembler) { 240 typedef CodeStubAssembler::Label Label; 241 typedef compiler::Node Node; 242 typedef CodeStubAssembler::Variable Variable; 243 244 Node* context = assembler->Parameter(4); 245 246 // Shared entry point for the clz32 operation. 247 Variable var_clz32_x(assembler, MachineRepresentation::kWord32); 248 Label do_clz32(assembler); 249 250 // We might need to loop once for ToNumber conversion. 251 Variable var_x(assembler, MachineRepresentation::kTagged); 252 Label loop(assembler, &var_x); 253 var_x.Bind(assembler->Parameter(1)); 254 assembler->Goto(&loop); 255 assembler->Bind(&loop); 256 { 257 // Load the current {x} value. 258 Node* x = var_x.value(); 259 260 // Check if {x} is a Smi or a HeapObject. 261 Label if_xissmi(assembler), if_xisnotsmi(assembler); 262 assembler->Branch(assembler->TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); 263 264 assembler->Bind(&if_xissmi); 265 { 266 var_clz32_x.Bind(assembler->SmiToWord32(x)); 267 assembler->Goto(&do_clz32); 268 } 269 270 assembler->Bind(&if_xisnotsmi); 271 { 272 // Check if {x} is a HeapNumber. 273 Label if_xisheapnumber(assembler), 274 if_xisnotheapnumber(assembler, Label::kDeferred); 275 assembler->Branch( 276 assembler->WordEqual(assembler->LoadMap(x), 277 assembler->HeapNumberMapConstant()), 278 &if_xisheapnumber, &if_xisnotheapnumber); 279 280 assembler->Bind(&if_xisheapnumber); 281 { 282 var_clz32_x.Bind(assembler->TruncateHeapNumberValueToWord32(x)); 283 assembler->Goto(&do_clz32); 284 } 285 286 assembler->Bind(&if_xisnotheapnumber); 287 { 288 // Need to convert {x} to a Number first. 289 Callable callable = 290 CodeFactory::NonNumberToNumber(assembler->isolate()); 291 var_x.Bind(assembler->CallStub(callable, context, x)); 292 assembler->Goto(&loop); 293 } 294 } 295 } 296 297 assembler->Bind(&do_clz32); 298 { 299 Node* x_value = var_clz32_x.value(); 300 Node* value = assembler->Word32Clz(x_value); 301 Node* result = assembler->ChangeInt32ToTagged(value); 302 assembler->Return(result); 303 } 304 } 305 306 // ES6 section 20.2.2.12 Math.cos ( x ) 307 void Builtins::Generate_MathCos(CodeStubAssembler* assembler) { 308 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Cos); 309 } 310 311 // ES6 section 20.2.2.13 Math.cosh ( x ) 312 void Builtins::Generate_MathCosh(CodeStubAssembler* assembler) { 313 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Cosh); 314 } 315 316 // ES6 section 20.2.2.14 Math.exp ( x ) 317 void Builtins::Generate_MathExp(CodeStubAssembler* assembler) { 318 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Exp); 319 } 320 321 // ES6 section 20.2.2.15 Math.expm1 ( x ) 322 void Builtins::Generate_MathExpm1(CodeStubAssembler* assembler) { 323 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Expm1); 324 } 325 326 // ES6 section 20.2.2.16 Math.floor ( x ) 327 void Builtins::Generate_MathFloor(CodeStubAssembler* assembler) { 328 Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Floor); 329 } 330 331 // ES6 section 20.2.2.17 Math.fround ( x ) 332 void Builtins::Generate_MathFround(CodeStubAssembler* assembler) { 333 using compiler::Node; 334 335 Node* x = assembler->Parameter(1); 336 Node* context = assembler->Parameter(4); 337 Node* x_value = assembler->TruncateTaggedToFloat64(context, x); 338 Node* value32 = assembler->TruncateFloat64ToFloat32(x_value); 339 Node* value = assembler->ChangeFloat32ToFloat64(value32); 340 Node* result = assembler->AllocateHeapNumberWithValue(value); 341 assembler->Return(result); 342 } 343 344 // ES6 section 20.2.2.18 Math.hypot ( value1, value2, ...values ) 345 BUILTIN(MathHypot) { 346 HandleScope scope(isolate); 347 int const length = args.length() - 1; 348 if (length == 0) return Smi::kZero; 349 DCHECK_LT(0, length); 350 double max = 0; 351 bool one_arg_is_nan = false; 352 List<double> abs_values(length); 353 for (int i = 0; i < length; i++) { 354 Handle<Object> x = args.at<Object>(i + 1); 355 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, Object::ToNumber(x)); 356 double abs_value = std::abs(x->Number()); 357 358 if (std::isnan(abs_value)) { 359 one_arg_is_nan = true; 360 } else { 361 abs_values.Add(abs_value); 362 if (max < abs_value) { 363 max = abs_value; 364 } 365 } 366 } 367 368 if (max == V8_INFINITY) { 369 return *isolate->factory()->NewNumber(V8_INFINITY); 370 } 371 372 if (one_arg_is_nan) { 373 return isolate->heap()->nan_value(); 374 } 375 376 if (max == 0) { 377 return Smi::kZero; 378 } 379 DCHECK_GT(max, 0); 380 381 // Kahan summation to avoid rounding errors. 382 // Normalize the numbers to the largest one to avoid overflow. 383 double sum = 0; 384 double compensation = 0; 385 for (int i = 0; i < length; i++) { 386 double n = abs_values.at(i) / max; 387 double summand = n * n - compensation; 388 double preliminary = sum + summand; 389 compensation = (preliminary - sum) - summand; 390 sum = preliminary; 391 } 392 393 return *isolate->factory()->NewNumber(std::sqrt(sum) * max); 394 } 395 396 // ES6 section 20.2.2.19 Math.imul ( x, y ) 397 void Builtins::Generate_MathImul(CodeStubAssembler* assembler) { 398 using compiler::Node; 399 400 Node* x = assembler->Parameter(1); 401 Node* y = assembler->Parameter(2); 402 Node* context = assembler->Parameter(5); 403 Node* x_value = assembler->TruncateTaggedToWord32(context, x); 404 Node* y_value = assembler->TruncateTaggedToWord32(context, y); 405 Node* value = assembler->Int32Mul(x_value, y_value); 406 Node* result = assembler->ChangeInt32ToTagged(value); 407 assembler->Return(result); 408 } 409 410 // ES6 section 20.2.2.20 Math.log ( x ) 411 void Builtins::Generate_MathLog(CodeStubAssembler* assembler) { 412 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Log); 413 } 414 415 // ES6 section 20.2.2.21 Math.log1p ( x ) 416 void Builtins::Generate_MathLog1p(CodeStubAssembler* assembler) { 417 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Log1p); 418 } 419 420 // ES6 section 20.2.2.22 Math.log10 ( x ) 421 void Builtins::Generate_MathLog10(CodeStubAssembler* assembler) { 422 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Log10); 423 } 424 425 // ES6 section 20.2.2.23 Math.log2 ( x ) 426 void Builtins::Generate_MathLog2(CodeStubAssembler* assembler) { 427 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Log2); 428 } 429 430 // ES6 section 20.2.2.26 Math.pow ( x, y ) 431 void Builtins::Generate_MathPow(CodeStubAssembler* assembler) { 432 using compiler::Node; 433 434 Node* x = assembler->Parameter(1); 435 Node* y = assembler->Parameter(2); 436 Node* context = assembler->Parameter(5); 437 Node* x_value = assembler->TruncateTaggedToFloat64(context, x); 438 Node* y_value = assembler->TruncateTaggedToFloat64(context, y); 439 Node* value = assembler->Float64Pow(x_value, y_value); 440 Node* result = assembler->ChangeFloat64ToTagged(value); 441 assembler->Return(result); 442 } 443 444 // ES6 section 20.2.2.27 Math.random ( ) 445 void Builtins::Generate_MathRandom(CodeStubAssembler* assembler) { 446 using compiler::Node; 447 448 Node* context = assembler->Parameter(3); 449 Node* native_context = assembler->LoadNativeContext(context); 450 451 // Load cache index. 452 CodeStubAssembler::Variable smi_index(assembler, 453 MachineRepresentation::kTagged); 454 smi_index.Bind(assembler->LoadContextElement( 455 native_context, Context::MATH_RANDOM_INDEX_INDEX)); 456 457 // Cached random numbers are exhausted if index is 0. Go to slow path. 458 CodeStubAssembler::Label if_cached(assembler); 459 assembler->GotoIf(assembler->SmiAbove(smi_index.value(), 460 assembler->SmiConstant(Smi::kZero)), 461 &if_cached); 462 463 // Cache exhausted, populate the cache. Return value is the new index. 464 smi_index.Bind( 465 assembler->CallRuntime(Runtime::kGenerateRandomNumbers, context)); 466 assembler->Goto(&if_cached); 467 468 // Compute next index by decrement. 469 assembler->Bind(&if_cached); 470 Node* new_smi_index = assembler->SmiSub( 471 smi_index.value(), assembler->SmiConstant(Smi::FromInt(1))); 472 assembler->StoreContextElement( 473 native_context, Context::MATH_RANDOM_INDEX_INDEX, new_smi_index); 474 475 // Load and return next cached random number. 476 Node* array = assembler->LoadContextElement(native_context, 477 Context::MATH_RANDOM_CACHE_INDEX); 478 Node* random = assembler->LoadFixedDoubleArrayElement( 479 array, new_smi_index, MachineType::Float64(), 0, 480 CodeStubAssembler::SMI_PARAMETERS); 481 assembler->Return(assembler->AllocateHeapNumberWithValue(random)); 482 } 483 484 // ES6 section 20.2.2.28 Math.round ( x ) 485 void Builtins::Generate_MathRound(CodeStubAssembler* assembler) { 486 Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Round); 487 } 488 489 // ES6 section 20.2.2.29 Math.sign ( x ) 490 void Builtins::Generate_MathSign(CodeStubAssembler* assembler) { 491 typedef CodeStubAssembler::Label Label; 492 using compiler::Node; 493 494 // Convert the {x} value to a Number. 495 Node* x = assembler->Parameter(1); 496 Node* context = assembler->Parameter(4); 497 Node* x_value = assembler->TruncateTaggedToFloat64(context, x); 498 499 // Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself. 500 Label if_xisnegative(assembler), if_xispositive(assembler); 501 assembler->GotoIf( 502 assembler->Float64LessThan(x_value, assembler->Float64Constant(0.0)), 503 &if_xisnegative); 504 assembler->GotoIf( 505 assembler->Float64LessThan(assembler->Float64Constant(0.0), x_value), 506 &if_xispositive); 507 assembler->Return(assembler->ChangeFloat64ToTagged(x_value)); 508 509 assembler->Bind(&if_xisnegative); 510 assembler->Return(assembler->SmiConstant(Smi::FromInt(-1))); 511 512 assembler->Bind(&if_xispositive); 513 assembler->Return(assembler->SmiConstant(Smi::FromInt(1))); 514 } 515 516 // ES6 section 20.2.2.30 Math.sin ( x ) 517 void Builtins::Generate_MathSin(CodeStubAssembler* assembler) { 518 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Sin); 519 } 520 521 // ES6 section 20.2.2.31 Math.sinh ( x ) 522 void Builtins::Generate_MathSinh(CodeStubAssembler* assembler) { 523 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Sinh); 524 } 525 526 // ES6 section 20.2.2.32 Math.sqrt ( x ) 527 void Builtins::Generate_MathSqrt(CodeStubAssembler* assembler) { 528 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Sqrt); 529 } 530 531 // ES6 section 20.2.2.33 Math.tan ( x ) 532 void Builtins::Generate_MathTan(CodeStubAssembler* assembler) { 533 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Tan); 534 } 535 536 // ES6 section 20.2.2.34 Math.tanh ( x ) 537 void Builtins::Generate_MathTanh(CodeStubAssembler* assembler) { 538 Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Tanh); 539 } 540 541 // ES6 section 20.2.2.35 Math.trunc ( x ) 542 void Builtins::Generate_MathTrunc(CodeStubAssembler* assembler) { 543 Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Trunc); 544 } 545 546 void Builtins::Generate_MathMax(MacroAssembler* masm) { 547 Generate_MathMaxMin(masm, MathMaxMinKind::kMax); 548 } 549 550 void Builtins::Generate_MathMin(MacroAssembler* masm) { 551 Generate_MathMaxMin(masm, MathMaxMinKind::kMin); 552 } 553 554 } // namespace internal 555 } // namespace v8 556