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