builtins-math-gen.cc 9.14 KB
Newer Older
1 2 3 4
// Copyright 2017 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.

5 6
#include "src/builtins/builtins-math-gen.h"

7 8
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
9 10
#include "src/codegen/code-factory.h"
#include "src/codegen/code-stub-assembler.h"
11 12 13 14 15 16 17

namespace v8 {
namespace internal {

// -----------------------------------------------------------------------------
// ES6 section 20.2.2 Function Properties of the Math Object

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 30 31 32 33 34 35
  {
    // Load the current {x} value.
    Node* x = var_x.value();

    // Check if {x} is a Smi or a HeapObject.
    Label if_xissmi(this), if_xisnotsmi(this);
    Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);

36
    BIND(&if_xissmi);
37
    {
38
      Label if_overflow(this, Label::kDeferred);
39

40 41
      // check if support abs function
      if (IsIntPtrAbsWithOverflowSupported()) {
42
        Node* pair = IntPtrAbsWithOverflow(x);
43
        Node* overflow = Projection(1, pair);
44 45 46 47 48 49
        GotoIf(overflow, &if_overflow);

        // There is a Smi representation for negated {x}.
        Node* result = Projection(0, pair);
        Return(BitcastWordToTagged(result));

50 51 52
      } else {
        // Check if {x} is already positive.
        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 59 60
        {
          // Just return the input {x}.
          Return(x);
        }
61

62
        BIND(&if_xisnotpositive);
63
        {
64
          // Try to negate the {x} value.
65 66
          TNode<Smi> result = TrySmiSub(SmiConstant(0), CAST(x), &if_overflow);
          Return(result);
67
        }
68
      }
69

70
      BIND(&if_overflow);
71
      { Return(NumberConstant(0.0 - Smi::kMinValue)); }
72 73
    }

74
    BIND(&if_xisnotsmi);
75 76 77
    {
      // Check if {x} is a HeapNumber.
      Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
78
      Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
79

80
      BIND(&if_xisheapnumber);
81 82 83 84 85 86 87
      {
        Node* x_value = LoadHeapNumberValue(x);
        Node* value = Float64Abs(x_value);
        Node* result = AllocateHeapNumberWithValue(value);
        Return(result);
      }

88
      BIND(&if_xisnotheapnumber);
89 90
      {
        // Need to convert {x} to a Number first.
91
        var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
92 93 94 95 96 97 98
        Goto(&loop);
      }
    }
  }
}

void MathBuiltinsAssembler::MathRoundingOperation(
99 100
    Node* context, Node* x,
    TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) {
101
  // We might need to loop once for ToNumber conversion.
102
  VARIABLE(var_x, MachineRepresentation::kTagged, x);
103 104
  Label loop(this, &var_x);
  Goto(&loop);
105
  BIND(&loop);
106 107 108 109 110 111 112 113
  {
    // Load the current {x} value.
    Node* x = var_x.value();

    // Check if {x} is a Smi or a HeapObject.
    Label if_xissmi(this), if_xisnotsmi(this);
    Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);

114
    BIND(&if_xissmi);
115 116 117 118 119
    {
      // Nothing to do when {x} is a Smi.
      Return(x);
    }

120
    BIND(&if_xisnotsmi);
121 122 123
    {
      // Check if {x} is a HeapNumber.
      Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
124
      Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
125

126
      BIND(&if_xisheapnumber);
127 128 129 130 131 132 133
      {
        Node* x_value = LoadHeapNumberValue(x);
        Node* value = (this->*float64op)(x_value);
        Node* result = ChangeFloat64ToTagged(value);
        Return(result);
      }

134
      BIND(&if_xisnotheapnumber);
135 136
      {
        // Need to convert {x} to a Number first.
137
        var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
138 139 140 141 142 143 144
        Goto(&loop);
      }
    }
  }
}

void MathBuiltinsAssembler::MathMaxMin(
145
    Node* context, Node* argc,
146 147 148
    TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>,
                                                    SloppyTNode<Float64T>),
    double default_val) {
149
  CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
150
  argc = arguments.GetLength(INTPTR_PARAMETERS);
151

152
  VARIABLE(result, MachineRepresentation::kFloat64);
153 154 155
  result.Bind(Float64Constant(default_val));

  CodeStubAssembler::VariableList vars({&result}, zone());
156
  arguments.ForEach(vars, [=, &result](Node* arg) {
157 158 159 160 161 162 163
    Node* float_value = TruncateTaggedToFloat64(context, arg);
    result.Bind((this->*float64op)(result.value(), float_value));
  });

  arguments.PopAndReturn(ChangeFloat64ToTagged(result.value()));
}

164
// ES6 #sec-math.ceil
165
TF_BUILTIN(MathCeil, MathBuiltinsAssembler) {
166 167 168
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathRoundingOperation(context, x, &CodeStubAssembler::Float64Ceil);
169 170
}

171
// ES6 #sec-math.floor
172
TF_BUILTIN(MathFloor, MathBuiltinsAssembler) {
173 174 175
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathRoundingOperation(context, x, &CodeStubAssembler::Float64Floor);
176 177
}

178
// ES6 #sec-math.imul
179
TF_BUILTIN(MathImul, CodeStubAssembler) {
180 181 182
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  Node* y = Parameter(Descriptor::kY);
183 184 185 186 187 188 189
  Node* x_value = TruncateTaggedToWord32(context, x);
  Node* y_value = TruncateTaggedToWord32(context, y);
  Node* value = Int32Mul(x_value, y_value);
  Node* result = ChangeInt32ToTagged(value);
  Return(result);
}

190 191 192 193 194 195 196 197 198
CodeStubAssembler::Node* MathBuiltinsAssembler::MathPow(Node* context,
                                                        Node* base,
                                                        Node* exponent) {
  Node* base_value = TruncateTaggedToFloat64(context, base);
  Node* exponent_value = TruncateTaggedToFloat64(context, exponent);
  Node* value = Float64Pow(base_value, exponent_value);
  return ChangeFloat64ToTagged(value);
}

199
// ES6 #sec-math.pow
200 201 202
TF_BUILTIN(MathPow, MathBuiltinsAssembler) {
  Return(MathPow(Parameter(Descriptor::kContext), Parameter(Descriptor::kBase),
                 Parameter(Descriptor::kExponent)));
203 204
}

205
// ES6 #sec-math.random
206
TF_BUILTIN(MathRandom, CodeStubAssembler) {
207
  Node* context = Parameter(Descriptor::kContext);
208 209 210
  Node* native_context = LoadNativeContext(context);

  // Load cache index.
211 212
  TVARIABLE(Smi, smi_index);
  smi_index = CAST(
213 214 215 216
      LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX));

  // Cached random numbers are exhausted if index is 0. Go to slow path.
  Label if_cached(this);
217
  GotoIf(SmiAbove(smi_index.value(), SmiConstant(0)), &if_cached);
218 219

  // Cache exhausted, populate the cache. Return value is the new index.
220 221 222 223 224 225 226
  Node* const refill_math_random =
      ExternalConstant(ExternalReference::refill_math_random());
  Node* const isolate_ptr =
      ExternalConstant(ExternalReference::isolate_address(isolate()));
  MachineType type_tagged = MachineType::AnyTagged();
  MachineType type_ptr = MachineType::Pointer();

227 228 229
  smi_index = CAST(CallCFunction(refill_math_random, type_tagged,
                                 std::make_pair(type_ptr, isolate_ptr),
                                 std::make_pair(type_tagged, native_context)));
230 231 232
  Goto(&if_cached);

  // Compute next index by decrement.
233
  BIND(&if_cached);
234
  TNode<Smi> new_smi_index = SmiSub(smi_index.value(), SmiConstant(1));
235 236 237 238 239 240 241 242 243 244 245
  StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX,
                      new_smi_index);

  // Load and return next cached random number.
  Node* array =
      LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX);
  Node* random = LoadFixedDoubleArrayElement(
      array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS);
  Return(AllocateHeapNumberWithValue(random));
}

246
// ES6 #sec-math.round
247
TF_BUILTIN(MathRound, MathBuiltinsAssembler) {
248 249 250
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathRoundingOperation(context, x, &CodeStubAssembler::Float64Round);
251 252
}

253
// ES6 #sec-math.trunc
254
TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) {
255 256 257
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathRoundingOperation(context, x, &CodeStubAssembler::Float64Trunc);
258 259
}

260
// ES6 #sec-math.max
261
TF_BUILTIN(MathMax, MathBuiltinsAssembler) {
262 263
  // TODO(ishell): use constants from Descriptor once the JSFunction linkage
  // arguments are reordered.
264 265
  Node* context = Parameter(Descriptor::kContext);
  Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);
266
  MathMaxMin(context, argc, &CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY);
267 268
}

269
// ES6 #sec-math.min
270
TF_BUILTIN(MathMin, MathBuiltinsAssembler) {
271 272
  // TODO(ishell): use constants from Descriptor once the JSFunction linkage
  // arguments are reordered.
273 274
  Node* context = Parameter(Descriptor::kContext);
  Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);
275
  MathMaxMin(context, argc, &CodeStubAssembler::Float64Min, V8_INFINITY);
276 277 278 279
}

}  // namespace internal
}  // namespace v8