builtins-math-gen.cc 9.22 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
#include "src/objects/fixed-array.h"
12 13 14 15 16 17 18

namespace v8 {
namespace internal {

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

19
// ES6 #sec-math.abs
20
TF_BUILTIN(MathAbs, CodeStubAssembler) {
21
  Node* context = Parameter(Descriptor::kContext);
22 23

  // We might need to loop once for ToNumber conversion.
24
  VARIABLE(var_x, MachineRepresentation::kTagged);
25
  Label loop(this, &var_x);
26
  var_x.Bind(Parameter(Descriptor::kX));
27
  Goto(&loop);
28
  BIND(&loop);
29 30 31 32 33 34 35 36
  {
    // 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);

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

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

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

51 52 53
      } else {
        // Check if {x} is already positive.
        Label if_xispositive(this), if_xisnotpositive(this);
54
        BranchIfSmiLessThanOrEqual(SmiConstant(0), CAST(x), &if_xispositive,
55
                                   &if_xisnotpositive);
56

57
        BIND(&if_xispositive);
58 59 60 61
        {
          // Just return the input {x}.
          Return(x);
        }
62

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

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

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

81
      BIND(&if_xisheapnumber);
82
      {
83 84 85
        TNode<Float64T> x_value = LoadHeapNumberValue(x);
        TNode<Float64T> value = Float64Abs(x_value);
        TNode<HeapNumber> result = AllocateHeapNumberWithValue(value);
86 87 88
        Return(result);
      }

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

void MathBuiltinsAssembler::MathRoundingOperation(
100 101
    Node* context, Node* x,
    TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) {
102
  // We might need to loop once for ToNumber conversion.
103
  VARIABLE(var_x, MachineRepresentation::kTagged, x);
104 105
  Label loop(this, &var_x);
  Goto(&loop);
106
  BIND(&loop);
107 108 109 110 111 112 113 114
  {
    // 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);

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

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

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

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

void MathBuiltinsAssembler::MathMaxMin(
146
    TNode<Context> context, TNode<Int32T> argc,
147 148 149
    TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>,
                                                    SloppyTNode<Float64T>),
    double default_val) {
150
  CodeStubArguments arguments(this, argc);
151

152
  TVARIABLE(Float64T, result, Float64Constant(default_val));
153 154

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

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

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

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

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

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

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

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

  // Load cache index.
210 211
  TVARIABLE(Smi, smi_index);
  smi_index = CAST(
212 213 214 215
      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);
216
  GotoIf(SmiAbove(smi_index.value(), SmiConstant(0)), &if_cached);
217 218

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

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

  // Compute next index by decrement.
232
  BIND(&if_cached);
233
  TNode<Smi> new_smi_index = SmiSub(smi_index.value(), SmiConstant(1));
234 235 236 237
  StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX,
                      new_smi_index);

  // Load and return next cached random number.
238 239 240
  TNode<FixedDoubleArray> array = CAST(
      LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX));
  TNode<Float64T> random = LoadFixedDoubleArrayElement(
241 242 243 244
      array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS);
  Return(AllocateHeapNumberWithValue(random));
}

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

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

259
// ES6 #sec-math.max
260
TF_BUILTIN(MathMax, MathBuiltinsAssembler) {
261 262 263
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
  TNode<Int32T> argc =
      UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
264
  MathMaxMin(context, argc, &CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY);
265 266
}

267
// ES6 #sec-math.min
268
TF_BUILTIN(MathMin, MathBuiltinsAssembler) {
269 270 271
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
  TNode<Int32T> argc =
      UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
272
  MathMaxMin(context, argc, &CodeStubAssembler::Float64Min, V8_INFINITY);
273 274 275 276
}

}  // namespace internal
}  // namespace v8