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 7 #include "src/builtins/builtins-utils-gen.h" 8 #include "src/builtins/builtins.h" 9 #include "src/code-factory.h" 10 #include "src/code-stub-assembler.h" 11 12 namespace v8 { 13 namespace internal { 14 15 // ----------------------------------------------------------------------------- 16 // ES6 section 20.2.2 Function Properties of the Math Object 17 18 // ES6 #sec-math.abs 19 TF_BUILTIN(MathAbs, CodeStubAssembler) { 20 Node* context = Parameter(Descriptor::kContext); 21 22 // We might need to loop once for ToNumber conversion. 23 VARIABLE(var_x, MachineRepresentation::kTagged); 24 Label loop(this, &var_x); 25 var_x.Bind(Parameter(Descriptor::kX)); 26 Goto(&loop); 27 BIND(&loop); 28 { 29 // Load the current {x} value. 30 Node* x = var_x.value(); 31 32 // Check if {x} is a Smi or a HeapObject. 33 Label if_xissmi(this), if_xisnotsmi(this); 34 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); 35 36 BIND(&if_xissmi); 37 { 38 Label if_overflow(this, Label::kDeferred); 39 40 // check if support abs function 41 if (IsIntPtrAbsWithOverflowSupported()) { 42 Node* pair = IntPtrAbsWithOverflow(x); 43 Node* overflow = Projection(1, pair); 44 GotoIf(overflow, &if_overflow); 45 46 // There is a Smi representation for negated {x}. 47 Node* result = Projection(0, pair); 48 Return(BitcastWordToTagged(result)); 49 50 } else { 51 // Check if {x} is already positive. 52 Label if_xispositive(this), if_xisnotpositive(this); 53 BranchIfSmiLessThanOrEqual(SmiConstant(0), CAST(x), &if_xispositive, 54 &if_xisnotpositive); 55 56 BIND(&if_xispositive); 57 { 58 // Just return the input {x}. 59 Return(x); 60 } 61 62 BIND(&if_xisnotpositive); 63 { 64 // Try to negate the {x} value. 65 TNode<Smi> result = TrySmiSub(SmiConstant(0), CAST(x), &if_overflow); 66 Return(result); 67 } 68 } 69 70 BIND(&if_overflow); 71 { Return(NumberConstant(0.0 - Smi::kMinValue)); } 72 } 73 74 BIND(&if_xisnotsmi); 75 { 76 // Check if {x} is a HeapNumber. 77 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred); 78 Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber); 79 80 BIND(&if_xisheapnumber); 81 { 82 Node* x_value = LoadHeapNumberValue(x); 83 Node* value = Float64Abs(x_value); 84 Node* result = AllocateHeapNumberWithValue(value); 85 Return(result); 86 } 87 88 BIND(&if_xisnotheapnumber); 89 { 90 // Need to convert {x} to a Number first. 91 var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x)); 92 Goto(&loop); 93 } 94 } 95 } 96 } 97 98 void MathBuiltinsAssembler::MathRoundingOperation( 99 Node* context, Node* x, 100 TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) { 101 // We might need to loop once for ToNumber conversion. 102 VARIABLE(var_x, MachineRepresentation::kTagged, x); 103 Label loop(this, &var_x); 104 Goto(&loop); 105 BIND(&loop); 106 { 107 // Load the current {x} value. 108 Node* x = var_x.value(); 109 110 // Check if {x} is a Smi or a HeapObject. 111 Label if_xissmi(this), if_xisnotsmi(this); 112 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); 113 114 BIND(&if_xissmi); 115 { 116 // Nothing to do when {x} is a Smi. 117 Return(x); 118 } 119 120 BIND(&if_xisnotsmi); 121 { 122 // Check if {x} is a HeapNumber. 123 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred); 124 Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber); 125 126 BIND(&if_xisheapnumber); 127 { 128 Node* x_value = LoadHeapNumberValue(x); 129 Node* value = (this->*float64op)(x_value); 130 Node* result = ChangeFloat64ToTagged(value); 131 Return(result); 132 } 133 134 BIND(&if_xisnotheapnumber); 135 { 136 // Need to convert {x} to a Number first. 137 var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x)); 138 Goto(&loop); 139 } 140 } 141 } 142 } 143 144 void MathBuiltinsAssembler::MathUnaryOperation( 145 Node* context, Node* x, 146 TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) { 147 Node* x_value = TruncateTaggedToFloat64(context, x); 148 Node* value = (this->*float64op)(x_value); 149 Node* result = AllocateHeapNumberWithValue(value); 150 Return(result); 151 } 152 153 void MathBuiltinsAssembler::MathMaxMin( 154 Node* context, Node* argc, 155 TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>, 156 SloppyTNode<Float64T>), 157 double default_val) { 158 CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc)); 159 argc = arguments.GetLength(INTPTR_PARAMETERS); 160 161 VARIABLE(result, MachineRepresentation::kFloat64); 162 result.Bind(Float64Constant(default_val)); 163 164 CodeStubAssembler::VariableList vars({&result}, zone()); 165 arguments.ForEach(vars, [=, &result](Node* arg) { 166 Node* float_value = TruncateTaggedToFloat64(context, arg); 167 result.Bind((this->*float64op)(result.value(), float_value)); 168 }); 169 170 arguments.PopAndReturn(ChangeFloat64ToTagged(result.value())); 171 } 172 173 // ES6 #sec-math.acos 174 TF_BUILTIN(MathAcos, MathBuiltinsAssembler) { 175 Node* context = Parameter(Descriptor::kContext); 176 Node* x = Parameter(Descriptor::kX); 177 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Acos); 178 } 179 180 // ES6 #sec-math.acosh 181 TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) { 182 Node* context = Parameter(Descriptor::kContext); 183 Node* x = Parameter(Descriptor::kX); 184 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Acosh); 185 } 186 187 // ES6 #sec-math.asin 188 TF_BUILTIN(MathAsin, MathBuiltinsAssembler) { 189 Node* context = Parameter(Descriptor::kContext); 190 Node* x = Parameter(Descriptor::kX); 191 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Asin); 192 } 193 194 // ES6 #sec-math.asinh 195 TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) { 196 Node* context = Parameter(Descriptor::kContext); 197 Node* x = Parameter(Descriptor::kX); 198 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Asinh); 199 } 200 201 // ES6 #sec-math.atan 202 TF_BUILTIN(MathAtan, MathBuiltinsAssembler) { 203 Node* context = Parameter(Descriptor::kContext); 204 Node* x = Parameter(Descriptor::kX); 205 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Atan); 206 } 207 208 // ES6 #sec-math.atanh 209 TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) { 210 Node* context = Parameter(Descriptor::kContext); 211 Node* x = Parameter(Descriptor::kX); 212 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Atanh); 213 } 214 215 // ES6 #sec-math.atan2 216 TF_BUILTIN(MathAtan2, CodeStubAssembler) { 217 Node* context = Parameter(Descriptor::kContext); 218 Node* y = Parameter(Descriptor::kY); 219 Node* x = Parameter(Descriptor::kX); 220 221 Node* y_value = TruncateTaggedToFloat64(context, y); 222 Node* x_value = TruncateTaggedToFloat64(context, x); 223 Node* value = Float64Atan2(y_value, x_value); 224 Node* result = AllocateHeapNumberWithValue(value); 225 Return(result); 226 } 227 228 // ES6 #sec-math.ceil 229 TF_BUILTIN(MathCeil, MathBuiltinsAssembler) { 230 Node* context = Parameter(Descriptor::kContext); 231 Node* x = Parameter(Descriptor::kX); 232 MathRoundingOperation(context, x, &CodeStubAssembler::Float64Ceil); 233 } 234 235 // ES6 #sec-math.cbrt 236 TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) { 237 Node* context = Parameter(Descriptor::kContext); 238 Node* x = Parameter(Descriptor::kX); 239 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cbrt); 240 } 241 242 // ES6 #sec-math.clz32 243 TF_BUILTIN(MathClz32, CodeStubAssembler) { 244 Node* context = Parameter(Descriptor::kContext); 245 246 // Shared entry point for the clz32 operation. 247 VARIABLE(var_clz32_x, MachineRepresentation::kWord32); 248 Label do_clz32(this); 249 250 // We might need to loop once for ToNumber conversion. 251 VARIABLE(var_x, MachineRepresentation::kTagged); 252 Label loop(this, &var_x); 253 var_x.Bind(Parameter(Descriptor::kX)); 254 Goto(&loop); 255 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(this), if_xisnotsmi(this); 262 Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi); 263 264 BIND(&if_xissmi); 265 { 266 var_clz32_x.Bind(SmiToInt32(x)); 267 Goto(&do_clz32); 268 } 269 270 BIND(&if_xisnotsmi); 271 { 272 // Check if {x} is a HeapNumber. 273 Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred); 274 Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber); 275 276 BIND(&if_xisheapnumber); 277 { 278 var_clz32_x.Bind(TruncateHeapNumberValueToWord32(x)); 279 Goto(&do_clz32); 280 } 281 282 BIND(&if_xisnotheapnumber); 283 { 284 // Need to convert {x} to a Number first. 285 var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x)); 286 Goto(&loop); 287 } 288 } 289 } 290 291 BIND(&do_clz32); 292 { 293 Node* x_value = var_clz32_x.value(); 294 Node* value = Word32Clz(x_value); 295 Node* result = ChangeInt32ToTagged(value); 296 Return(result); 297 } 298 } 299 300 // ES6 #sec-math.cos 301 TF_BUILTIN(MathCos, MathBuiltinsAssembler) { 302 Node* context = Parameter(Descriptor::kContext); 303 Node* x = Parameter(Descriptor::kX); 304 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cos); 305 } 306 307 // ES6 #sec-math.cosh 308 TF_BUILTIN(MathCosh, MathBuiltinsAssembler) { 309 Node* context = Parameter(Descriptor::kContext); 310 Node* x = Parameter(Descriptor::kX); 311 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cosh); 312 } 313 314 // ES6 #sec-math.exp 315 TF_BUILTIN(MathExp, MathBuiltinsAssembler) { 316 Node* context = Parameter(Descriptor::kContext); 317 Node* x = Parameter(Descriptor::kX); 318 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Exp); 319 } 320 321 // ES6 #sec-math.expm1 322 TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) { 323 Node* context = Parameter(Descriptor::kContext); 324 Node* x = Parameter(Descriptor::kX); 325 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Expm1); 326 } 327 328 // ES6 #sec-math.floor 329 TF_BUILTIN(MathFloor, MathBuiltinsAssembler) { 330 Node* context = Parameter(Descriptor::kContext); 331 Node* x = Parameter(Descriptor::kX); 332 MathRoundingOperation(context, x, &CodeStubAssembler::Float64Floor); 333 } 334 335 // ES6 #sec-math.fround 336 TF_BUILTIN(MathFround, CodeStubAssembler) { 337 Node* context = Parameter(Descriptor::kContext); 338 Node* x = Parameter(Descriptor::kX); 339 Node* x_value = TruncateTaggedToFloat64(context, x); 340 Node* value32 = TruncateFloat64ToFloat32(x_value); 341 Node* value = ChangeFloat32ToFloat64(value32); 342 Node* result = AllocateHeapNumberWithValue(value); 343 Return(result); 344 } 345 346 // ES6 #sec-math.imul 347 TF_BUILTIN(MathImul, CodeStubAssembler) { 348 Node* context = Parameter(Descriptor::kContext); 349 Node* x = Parameter(Descriptor::kX); 350 Node* y = Parameter(Descriptor::kY); 351 Node* x_value = TruncateTaggedToWord32(context, x); 352 Node* y_value = TruncateTaggedToWord32(context, y); 353 Node* value = Int32Mul(x_value, y_value); 354 Node* result = ChangeInt32ToTagged(value); 355 Return(result); 356 } 357 358 // ES6 #sec-math.log 359 TF_BUILTIN(MathLog, MathBuiltinsAssembler) { 360 Node* context = Parameter(Descriptor::kContext); 361 Node* x = Parameter(Descriptor::kX); 362 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log); 363 } 364 365 // ES6 #sec-math.log1p 366 TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) { 367 Node* context = Parameter(Descriptor::kContext); 368 Node* x = Parameter(Descriptor::kX); 369 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log1p); 370 } 371 372 // ES6 #sec-math.log10 373 TF_BUILTIN(MathLog10, MathBuiltinsAssembler) { 374 Node* context = Parameter(Descriptor::kContext); 375 Node* x = Parameter(Descriptor::kX); 376 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log10); 377 } 378 379 // ES6 #sec-math.log2 380 TF_BUILTIN(MathLog2, MathBuiltinsAssembler) { 381 Node* context = Parameter(Descriptor::kContext); 382 Node* x = Parameter(Descriptor::kX); 383 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log2); 384 } 385 386 CodeStubAssembler::Node* MathBuiltinsAssembler::MathPow(Node* context, 387 Node* base, 388 Node* exponent) { 389 Node* base_value = TruncateTaggedToFloat64(context, base); 390 Node* exponent_value = TruncateTaggedToFloat64(context, exponent); 391 Node* value = Float64Pow(base_value, exponent_value); 392 return ChangeFloat64ToTagged(value); 393 } 394 395 // ES6 #sec-math.pow 396 TF_BUILTIN(MathPow, MathBuiltinsAssembler) { 397 Return(MathPow(Parameter(Descriptor::kContext), Parameter(Descriptor::kBase), 398 Parameter(Descriptor::kExponent))); 399 } 400 401 // ES6 #sec-math.random 402 TF_BUILTIN(MathRandom, CodeStubAssembler) { 403 Node* context = Parameter(Descriptor::kContext); 404 Node* native_context = LoadNativeContext(context); 405 406 // Load cache index. 407 TVARIABLE(Smi, smi_index); 408 smi_index = CAST( 409 LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX)); 410 411 // Cached random numbers are exhausted if index is 0. Go to slow path. 412 Label if_cached(this); 413 GotoIf(SmiAbove(smi_index.value(), SmiConstant(0)), &if_cached); 414 415 // Cache exhausted, populate the cache. Return value is the new index. 416 smi_index = CAST(CallRuntime(Runtime::kGenerateRandomNumbers, context)); 417 Goto(&if_cached); 418 419 // Compute next index by decrement. 420 BIND(&if_cached); 421 TNode<Smi> new_smi_index = SmiSub(smi_index.value(), SmiConstant(1)); 422 StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX, 423 new_smi_index); 424 425 // Load and return next cached random number. 426 Node* array = 427 LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX); 428 Node* random = LoadFixedDoubleArrayElement( 429 array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS); 430 Return(AllocateHeapNumberWithValue(random)); 431 } 432 433 // ES6 #sec-math.round 434 TF_BUILTIN(MathRound, MathBuiltinsAssembler) { 435 Node* context = Parameter(Descriptor::kContext); 436 Node* x = Parameter(Descriptor::kX); 437 MathRoundingOperation(context, x, &CodeStubAssembler::Float64Round); 438 } 439 440 // ES6 #sec-math.sign 441 TF_BUILTIN(MathSign, CodeStubAssembler) { 442 // Convert the {x} value to a Number. 443 Node* context = Parameter(Descriptor::kContext); 444 Node* x = Parameter(Descriptor::kX); 445 Node* x_value = TruncateTaggedToFloat64(context, x); 446 447 // Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself. 448 Label if_xisnegative(this), if_xispositive(this); 449 GotoIf(Float64LessThan(x_value, Float64Constant(0.0)), &if_xisnegative); 450 GotoIf(Float64LessThan(Float64Constant(0.0), x_value), &if_xispositive); 451 Return(ChangeFloat64ToTagged(x_value)); 452 453 BIND(&if_xisnegative); 454 Return(SmiConstant(-1)); 455 456 BIND(&if_xispositive); 457 Return(SmiConstant(1)); 458 } 459 460 // ES6 #sec-math.sin 461 TF_BUILTIN(MathSin, MathBuiltinsAssembler) { 462 Node* context = Parameter(Descriptor::kContext); 463 Node* x = Parameter(Descriptor::kX); 464 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sin); 465 } 466 467 // ES6 #sec-math.sinh 468 TF_BUILTIN(MathSinh, MathBuiltinsAssembler) { 469 Node* context = Parameter(Descriptor::kContext); 470 Node* x = Parameter(Descriptor::kX); 471 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sinh); 472 } 473 474 // ES6 #sec-math.sqrt 475 TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) { 476 Node* context = Parameter(Descriptor::kContext); 477 Node* x = Parameter(Descriptor::kX); 478 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sqrt); 479 } 480 481 // ES6 #sec-math.tan 482 TF_BUILTIN(MathTan, MathBuiltinsAssembler) { 483 Node* context = Parameter(Descriptor::kContext); 484 Node* x = Parameter(Descriptor::kX); 485 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Tan); 486 } 487 488 // ES6 #sec-math.tanh 489 TF_BUILTIN(MathTanh, MathBuiltinsAssembler) { 490 Node* context = Parameter(Descriptor::kContext); 491 Node* x = Parameter(Descriptor::kX); 492 MathUnaryOperation(context, x, &CodeStubAssembler::Float64Tanh); 493 } 494 495 // ES6 #sec-math.trunc 496 TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) { 497 Node* context = Parameter(Descriptor::kContext); 498 Node* x = Parameter(Descriptor::kX); 499 MathRoundingOperation(context, x, &CodeStubAssembler::Float64Trunc); 500 } 501 502 // ES6 #sec-math.max 503 TF_BUILTIN(MathMax, MathBuiltinsAssembler) { 504 // TODO(ishell): use constants from Descriptor once the JSFunction linkage 505 // arguments are reordered. 506 Node* context = Parameter(Descriptor::kContext); 507 Node* argc = Parameter(Descriptor::kJSActualArgumentsCount); 508 MathMaxMin(context, argc, &CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY); 509 } 510 511 // ES6 #sec-math.min 512 TF_BUILTIN(MathMin, MathBuiltinsAssembler) { 513 // TODO(ishell): use constants from Descriptor once the JSFunction linkage 514 // arguments are reordered. 515 Node* context = Parameter(Descriptor::kContext); 516 Node* argc = Parameter(Descriptor::kJSActualArgumentsCount); 517 MathMaxMin(context, argc, &CodeStubAssembler::Float64Min, V8_INFINITY); 518 } 519 520 } // namespace internal 521 } // namespace v8 522