// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins.h"
#include "src/builtins/builtins-utils.h"
#include "src/code-factory.h"
namespace v8 {
namespace internal {
// -----------------------------------------------------------------------------
// ES6 section 20.2.2 Function Properties of the Math Object
// ES6 section - 20.2.2.1 Math.abs ( x )
void Builtins::Generate_MathAbs(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(4);
// We might need to loop once for ToNumber conversion.
Variable var_x(assembler, MachineRepresentation::kTagged);
Label loop(assembler, &var_x);
var_x.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {x} value.
Node* x = var_x.value();
// Check if {x} is a Smi or a HeapObject.
Label if_xissmi(assembler), if_xisnotsmi(assembler);
assembler->Branch(assembler->TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
assembler->Bind(&if_xissmi);
{
// Check if {x} is already positive.
Label if_xispositive(assembler), if_xisnotpositive(assembler);
assembler->BranchIfSmiLessThanOrEqual(
assembler->SmiConstant(Smi::FromInt(0)), x, &if_xispositive,
&if_xisnotpositive);
assembler->Bind(&if_xispositive);
{
// Just return the input {x}.
assembler->Return(x);
}
assembler->Bind(&if_xisnotpositive);
{
// Try to negate the {x} value.
Node* pair = assembler->IntPtrSubWithOverflow(
assembler->IntPtrConstant(0), assembler->BitcastTaggedToWord(x));
Node* overflow = assembler->Projection(1, pair);
Label if_overflow(assembler, Label::kDeferred),
if_notoverflow(assembler);
assembler->Branch(overflow, &if_overflow, &if_notoverflow);
assembler->Bind(&if_notoverflow);
{
// There is a Smi representation for negated {x}.
Node* result = assembler->Projection(0, pair);
result = assembler->BitcastWordToTagged(result);
assembler->Return(result);
}
assembler->Bind(&if_overflow);
{
Node* result = assembler->NumberConstant(0.0 - Smi::kMinValue);
assembler->Return(result);
}
}
}
assembler->Bind(&if_xisnotsmi);
{
// Check if {x} is a HeapNumber.
Label if_xisheapnumber(assembler),
if_xisnotheapnumber(assembler, Label::kDeferred);
assembler->Branch(
assembler->WordEqual(assembler->LoadMap(x),
assembler->HeapNumberMapConstant()),
&if_xisheapnumber, &if_xisnotheapnumber);
assembler->Bind(&if_xisheapnumber);
{
Node* x_value = assembler->LoadHeapNumberValue(x);
Node* value = assembler->Float64Abs(x_value);
Node* result = assembler->AllocateHeapNumberWithValue(value);
assembler->Return(result);
}
assembler->Bind(&if_xisnotheapnumber);
{
// Need to convert {x} to a Number first.
Callable callable =
CodeFactory::NonNumberToNumber(assembler->isolate());
var_x.Bind(assembler->CallStub(callable, context, x));
assembler->Goto(&loop);
}
}
}
}
namespace {
void Generate_MathRoundingOperation(
CodeStubAssembler* assembler,
compiler::Node* (CodeStubAssembler::*float64op)(compiler::Node*)) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(4);
// We might need to loop once for ToNumber conversion.
Variable var_x(assembler, MachineRepresentation::kTagged);
Label loop(assembler, &var_x);
var_x.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {x} value.
Node* x = var_x.value();
// Check if {x} is a Smi or a HeapObject.
Label if_xissmi(assembler), if_xisnotsmi(assembler);
assembler->Branch(assembler->TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
assembler->Bind(&if_xissmi);
{
// Nothing to do when {x} is a Smi.
assembler->Return(x);
}
assembler->Bind(&if_xisnotsmi);
{
// Check if {x} is a HeapNumber.
Label if_xisheapnumber(assembler),
if_xisnotheapnumber(assembler, Label::kDeferred);
assembler->Branch(
assembler->WordEqual(assembler->LoadMap(x),
assembler->HeapNumberMapConstant()),
&if_xisheapnumber, &if_xisnotheapnumber);
assembler->Bind(&if_xisheapnumber);
{
Node* x_value = assembler->LoadHeapNumberValue(x);
Node* value = (assembler->*float64op)(x_value);
Node* result = assembler->ChangeFloat64ToTagged(value);
assembler->Return(result);
}
assembler->Bind(&if_xisnotheapnumber);
{
// Need to convert {x} to a Number first.
Callable callable =
CodeFactory::NonNumberToNumber(assembler->isolate());
var_x.Bind(assembler->CallStub(callable, context, x));
assembler->Goto(&loop);
}
}
}
}
void Generate_MathUnaryOperation(
CodeStubAssembler* assembler,
compiler::Node* (CodeStubAssembler::*float64op)(compiler::Node*)) {
typedef compiler::Node Node;
Node* x = assembler->Parameter(1);
Node* context = assembler->Parameter(4);
Node* x_value = assembler->TruncateTaggedToFloat64(context, x);
Node* value = (assembler->*float64op)(x_value);
Node* result = assembler->AllocateHeapNumberWithValue(value);
assembler->Return(result);
}
} // namespace
// ES6 section 20.2.2.2 Math.acos ( x )
void Builtins::Generate_MathAcos(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Acos);
}
// ES6 section 20.2.2.3 Math.acosh ( x )
void Builtins::Generate_MathAcosh(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Acosh);
}
// ES6 section 20.2.2.4 Math.asin ( x )
void Builtins::Generate_MathAsin(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Asin);
}
// ES6 section 20.2.2.5 Math.asinh ( x )
void Builtins::Generate_MathAsinh(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Asinh);
}
// ES6 section 20.2.2.6 Math.atan ( x )
void Builtins::Generate_MathAtan(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Atan);
}
// ES6 section 20.2.2.7 Math.atanh ( x )
void Builtins::Generate_MathAtanh(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Atanh);
}
// ES6 section 20.2.2.8 Math.atan2 ( y, x )
void Builtins::Generate_MathAtan2(CodeStubAssembler* assembler) {
using compiler::Node;
Node* y = assembler->Parameter(1);
Node* x = assembler->Parameter(2);
Node* context = assembler->Parameter(5);
Node* y_value = assembler->TruncateTaggedToFloat64(context, y);
Node* x_value = assembler->TruncateTaggedToFloat64(context, x);
Node* value = assembler->Float64Atan2(y_value, x_value);
Node* result = assembler->AllocateHeapNumberWithValue(value);
assembler->Return(result);
}
// ES6 section 20.2.2.10 Math.ceil ( x )
void Builtins::Generate_MathCeil(CodeStubAssembler* assembler) {
Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Ceil);
}
// ES6 section 20.2.2.9 Math.cbrt ( x )
void Builtins::Generate_MathCbrt(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Cbrt);
}
// ES6 section 20.2.2.11 Math.clz32 ( x )
void Builtins::Generate_MathClz32(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(4);
// Shared entry point for the clz32 operation.
Variable var_clz32_x(assembler, MachineRepresentation::kWord32);
Label do_clz32(assembler);
// We might need to loop once for ToNumber conversion.
Variable var_x(assembler, MachineRepresentation::kTagged);
Label loop(assembler, &var_x);
var_x.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {x} value.
Node* x = var_x.value();
// Check if {x} is a Smi or a HeapObject.
Label if_xissmi(assembler), if_xisnotsmi(assembler);
assembler->Branch(assembler->TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
assembler->Bind(&if_xissmi);
{
var_clz32_x.Bind(assembler->SmiToWord32(x));
assembler->Goto(&do_clz32);
}
assembler->Bind(&if_xisnotsmi);
{
// Check if {x} is a HeapNumber.
Label if_xisheapnumber(assembler),
if_xisnotheapnumber(assembler, Label::kDeferred);
assembler->Branch(
assembler->WordEqual(assembler->LoadMap(x),
assembler->HeapNumberMapConstant()),
&if_xisheapnumber, &if_xisnotheapnumber);
assembler->Bind(&if_xisheapnumber);
{
var_clz32_x.Bind(assembler->TruncateHeapNumberValueToWord32(x));
assembler->Goto(&do_clz32);
}
assembler->Bind(&if_xisnotheapnumber);
{
// Need to convert {x} to a Number first.
Callable callable =
CodeFactory::NonNumberToNumber(assembler->isolate());
var_x.Bind(assembler->CallStub(callable, context, x));
assembler->Goto(&loop);
}
}
}
assembler->Bind(&do_clz32);
{
Node* x_value = var_clz32_x.value();
Node* value = assembler->Word32Clz(x_value);
Node* result = assembler->ChangeInt32ToTagged(value);
assembler->Return(result);
}
}
// ES6 section 20.2.2.12 Math.cos ( x )
void Builtins::Generate_MathCos(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Cos);
}
// ES6 section 20.2.2.13 Math.cosh ( x )
void Builtins::Generate_MathCosh(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Cosh);
}
// ES6 section 20.2.2.14 Math.exp ( x )
void Builtins::Generate_MathExp(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Exp);
}
// ES6 section 20.2.2.15 Math.expm1 ( x )
void Builtins::Generate_MathExpm1(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Expm1);
}
// ES6 section 20.2.2.16 Math.floor ( x )
void Builtins::Generate_MathFloor(CodeStubAssembler* assembler) {
Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Floor);
}
// ES6 section 20.2.2.17 Math.fround ( x )
void Builtins::Generate_MathFround(CodeStubAssembler* assembler) {
using compiler::Node;
Node* x = assembler->Parameter(1);
Node* context = assembler->Parameter(4);
Node* x_value = assembler->TruncateTaggedToFloat64(context, x);
Node* value32 = assembler->TruncateFloat64ToFloat32(x_value);
Node* value = assembler->ChangeFloat32ToFloat64(value32);
Node* result = assembler->AllocateHeapNumberWithValue(value);
assembler->Return(result);
}
// ES6 section 20.2.2.18 Math.hypot ( value1, value2, ...values )
BUILTIN(MathHypot) {
HandleScope scope(isolate);
int const length = args.length() - 1;
if (length == 0) return Smi::kZero;
DCHECK_LT(0, length);
double max = 0;
bool one_arg_is_nan = false;
List<double> abs_values(length);
for (int i = 0; i < length; i++) {
Handle<Object> x = args.at<Object>(i + 1);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, Object::ToNumber(x));
double abs_value = std::abs(x->Number());
if (std::isnan(abs_value)) {
one_arg_is_nan = true;
} else {
abs_values.Add(abs_value);
if (max < abs_value) {
max = abs_value;
}
}
}
if (max == V8_INFINITY) {
return *isolate->factory()->NewNumber(V8_INFINITY);
}
if (one_arg_is_nan) {
return isolate->heap()->nan_value();
}
if (max == 0) {
return Smi::kZero;
}
DCHECK_GT(max, 0);
// Kahan summation to avoid rounding errors.
// Normalize the numbers to the largest one to avoid overflow.
double sum = 0;
double compensation = 0;
for (int i = 0; i < length; i++) {
double n = abs_values.at(i) / max;
double summand = n * n - compensation;
double preliminary = sum + summand;
compensation = (preliminary - sum) - summand;
sum = preliminary;
}
return *isolate->factory()->NewNumber(std::sqrt(sum) * max);
}
// ES6 section 20.2.2.19 Math.imul ( x, y )
void Builtins::Generate_MathImul(CodeStubAssembler* assembler) {
using compiler::Node;
Node* x = assembler->Parameter(1);
Node* y = assembler->Parameter(2);
Node* context = assembler->Parameter(5);
Node* x_value = assembler->TruncateTaggedToWord32(context, x);
Node* y_value = assembler->TruncateTaggedToWord32(context, y);
Node* value = assembler->Int32Mul(x_value, y_value);
Node* result = assembler->ChangeInt32ToTagged(value);
assembler->Return(result);
}
// ES6 section 20.2.2.20 Math.log ( x )
void Builtins::Generate_MathLog(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Log);
}
// ES6 section 20.2.2.21 Math.log1p ( x )
void Builtins::Generate_MathLog1p(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Log1p);
}
// ES6 section 20.2.2.22 Math.log10 ( x )
void Builtins::Generate_MathLog10(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Log10);
}
// ES6 section 20.2.2.23 Math.log2 ( x )
void Builtins::Generate_MathLog2(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Log2);
}
// ES6 section 20.2.2.26 Math.pow ( x, y )
void Builtins::Generate_MathPow(CodeStubAssembler* assembler) {
using compiler::Node;
Node* x = assembler->Parameter(1);
Node* y = assembler->Parameter(2);
Node* context = assembler->Parameter(5);
Node* x_value = assembler->TruncateTaggedToFloat64(context, x);
Node* y_value = assembler->TruncateTaggedToFloat64(context, y);
Node* value = assembler->Float64Pow(x_value, y_value);
Node* result = assembler->ChangeFloat64ToTagged(value);
assembler->Return(result);
}
// ES6 section 20.2.2.27 Math.random ( )
void Builtins::Generate_MathRandom(CodeStubAssembler* assembler) {
using compiler::Node;
Node* context = assembler->Parameter(3);
Node* native_context = assembler->LoadNativeContext(context);
// Load cache index.
CodeStubAssembler::Variable smi_index(assembler,
MachineRepresentation::kTagged);
smi_index.Bind(assembler->LoadContextElement(
native_context, Context::MATH_RANDOM_INDEX_INDEX));
// Cached random numbers are exhausted if index is 0. Go to slow path.
CodeStubAssembler::Label if_cached(assembler);
assembler->GotoIf(assembler->SmiAbove(smi_index.value(),
assembler->SmiConstant(Smi::kZero)),
&if_cached);
// Cache exhausted, populate the cache. Return value is the new index.
smi_index.Bind(
assembler->CallRuntime(Runtime::kGenerateRandomNumbers, context));
assembler->Goto(&if_cached);
// Compute next index by decrement.
assembler->Bind(&if_cached);
Node* new_smi_index = assembler->SmiSub(
smi_index.value(), assembler->SmiConstant(Smi::FromInt(1)));
assembler->StoreContextElement(
native_context, Context::MATH_RANDOM_INDEX_INDEX, new_smi_index);
// Load and return next cached random number.
Node* array = assembler->LoadContextElement(native_context,
Context::MATH_RANDOM_CACHE_INDEX);
Node* random = assembler->LoadFixedDoubleArrayElement(
array, new_smi_index, MachineType::Float64(), 0,
CodeStubAssembler::SMI_PARAMETERS);
assembler->Return(assembler->AllocateHeapNumberWithValue(random));
}
// ES6 section 20.2.2.28 Math.round ( x )
void Builtins::Generate_MathRound(CodeStubAssembler* assembler) {
Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Round);
}
// ES6 section 20.2.2.29 Math.sign ( x )
void Builtins::Generate_MathSign(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
using compiler::Node;
// Convert the {x} value to a Number.
Node* x = assembler->Parameter(1);
Node* context = assembler->Parameter(4);
Node* x_value = assembler->TruncateTaggedToFloat64(context, x);
// Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself.
Label if_xisnegative(assembler), if_xispositive(assembler);
assembler->GotoIf(
assembler->Float64LessThan(x_value, assembler->Float64Constant(0.0)),
&if_xisnegative);
assembler->GotoIf(
assembler->Float64LessThan(assembler->Float64Constant(0.0), x_value),
&if_xispositive);
assembler->Return(assembler->ChangeFloat64ToTagged(x_value));
assembler->Bind(&if_xisnegative);
assembler->Return(assembler->SmiConstant(Smi::FromInt(-1)));
assembler->Bind(&if_xispositive);
assembler->Return(assembler->SmiConstant(Smi::FromInt(1)));
}
// ES6 section 20.2.2.30 Math.sin ( x )
void Builtins::Generate_MathSin(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Sin);
}
// ES6 section 20.2.2.31 Math.sinh ( x )
void Builtins::Generate_MathSinh(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Sinh);
}
// ES6 section 20.2.2.32 Math.sqrt ( x )
void Builtins::Generate_MathSqrt(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Sqrt);
}
// ES6 section 20.2.2.33 Math.tan ( x )
void Builtins::Generate_MathTan(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Tan);
}
// ES6 section 20.2.2.34 Math.tanh ( x )
void Builtins::Generate_MathTanh(CodeStubAssembler* assembler) {
Generate_MathUnaryOperation(assembler, &CodeStubAssembler::Float64Tanh);
}
// ES6 section 20.2.2.35 Math.trunc ( x )
void Builtins::Generate_MathTrunc(CodeStubAssembler* assembler) {
Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Trunc);
}
void Builtins::Generate_MathMax(MacroAssembler* masm) {
Generate_MathMaxMin(masm, MathMaxMinKind::kMax);
}
void Builtins::Generate_MathMin(MacroAssembler* masm) {
Generate_MathMaxMin(masm, MathMaxMinKind::kMin);
}
} // namespace internal
} // namespace v8