js-builtin-reducer.cc 7.13 KB
Newer Older
1 2 3 4
// Copyright 2014 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
#include "src/compiler/js-builtin-reducer.h"
6
#include "src/compiler/js-graph.h"
7
#include "src/compiler/node-matchers.h"
8
#include "src/compiler/node-properties.h"
9
#include "src/compiler/simplified-operator.h"
10
#include "src/objects-inl.h"
11
#include "src/type-cache.h"
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#include "src/types.h"

namespace v8 {
namespace internal {
namespace compiler {


// Helper class to access JSCallFunction nodes that are potential candidates
// for reduction when they have a BuiltinFunctionId associated with them.
class JSCallReduction {
 public:
  explicit JSCallReduction(Node* node) : node_(node) {}

  // Determines whether the node is a JSCallFunction operation that targets a
  // constant callee being a well-known builtin with a BuiltinFunctionId.
  bool HasBuiltinFunctionId() {
    if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
29
    HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
30 31
    if (!m.HasValue() || !m.Value()->IsJSFunction()) return false;
    Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
32
    return function->shared()->HasBuiltinFunctionId();
33 34 35 36 37
  }

  // Retrieves the BuiltinFunctionId as described above.
  BuiltinFunctionId GetBuiltinFunctionId() {
    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
38
    HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
39
    Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
40
    return function->shared()->builtin_function_id();
41 42
  }

43 44 45
  // Determines whether the call takes zero inputs.
  bool InputsMatchZero() { return GetJSCallArity() == 0; }

46
  // Determines whether the call takes one input of the given type.
47
  bool InputsMatchOne(Type* t1) {
48
    return GetJSCallArity() == 1 &&
49
           NodeProperties::GetType(GetJSCallInput(0))->Is(t1);
50 51 52
  }

  // Determines whether the call takes two inputs of the given types.
53
  bool InputsMatchTwo(Type* t1, Type* t2) {
54
    return GetJSCallArity() == 2 &&
55 56
           NodeProperties::GetType(GetJSCallInput(0))->Is(t1) &&
           NodeProperties::GetType(GetJSCallInput(1))->Is(t2);
57 58
  }

59 60 61
  // Determines whether the call takes inputs all of the given type.
  bool InputsMatchAll(Type* t) {
    for (int i = 0; i < GetJSCallArity(); i++) {
62
      if (!NodeProperties::GetType(GetJSCallInput(i))->Is(t)) {
63 64 65 66 67 68
        return false;
      }
    }
    return true;
  }

69 70 71 72 73 74
  Node* left() { return GetJSCallInput(0); }
  Node* right() { return GetJSCallInput(1); }

  int GetJSCallArity() {
    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
    // Skip first (i.e. callee) and second (i.e. receiver) operand.
75
    return node_->op()->ValueInputCount() - 2;
76 77 78 79 80 81 82 83 84 85 86 87 88
  }

  Node* GetJSCallInput(int index) {
    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
    DCHECK_LT(index, GetJSCallArity());
    // Skip first (i.e. callee) and second (i.e. receiver) operand.
    return NodeProperties::GetValueInput(node_, index + 2);
  }

 private:
  Node* node_;
};

89
JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph)
90 91 92
    : AdvancedReducer(editor),
      jsgraph_(jsgraph),
      type_cache_(TypeCache::Get()) {}
93

94 95 96 97 98
// ECMA-262, section 15.8.2.11.
Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
  JSCallReduction r(node);
  if (r.InputsMatchZero()) {
    // Math.max() -> -Infinity
99
    return Replace(jsgraph()->Constant(-V8_INFINITY));
100 101 102 103 104 105 106 107 108
  }
  if (r.InputsMatchOne(Type::Number())) {
    // Math.max(a:number) -> a
    return Replace(r.left());
  }
  if (r.InputsMatchAll(Type::Integral32())) {
    // Math.max(a:int32, b:int32, ...)
    Node* value = r.GetJSCallInput(0);
    for (int i = 1; i < r.GetJSCallArity(); i++) {
109 110
      Node* const input = r.GetJSCallInput(i);
      value = graph()->NewNode(
111
          common()->Select(MachineRepresentation::kNone),
112 113
          graph()->NewNode(simplified()->NumberLessThan(), input, value), value,
          input);
114 115 116 117 118 119 120
    }
    return Replace(value);
  }
  return NoChange();
}


121 122 123
// ES6 draft 08-24-14, section 20.2.2.19.
Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
  JSCallReduction r(node);
124
  if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
125 126 127 128 129 130 131 132
    // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
    Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
    return Replace(value);
  }
  return NoChange();
}


133 134 135 136 137 138 139 140 141 142 143 144
// ES6 draft 08-24-14, section 20.2.2.17.
Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
  JSCallReduction r(node);
  if (r.InputsMatchOne(Type::Number())) {
    // Math.fround(a:number) -> TruncateFloat64ToFloat32(a)
    Node* value =
        graph()->NewNode(machine()->TruncateFloat64ToFloat32(), r.left());
    return Replace(value);
  }
  return NoChange();
}

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
// ES6 section 20.2.2.28 Math.round ( x )
Reduction JSBuiltinReducer::ReduceMathRound(Node* node) {
  JSCallReduction r(node);
  if (r.InputsMatchOne(type_cache_.kIntegerOrMinusZeroOrNaN)) {
    // Math.round(a:integer \/ -0 \/ NaN) -> a
    return Replace(r.left());
  }
  if (r.InputsMatchOne(Type::Number()) &&
      machine()->Float64RoundUp().IsSupported()) {
    // Math.round(a:number) -> Select(Float64LessThan(#0.5, Float64Sub(i, a)),
    //                                Float64Sub(i, #1.0), i)
    //   where i = Float64RoundUp(a)
    Node* value = r.left();
    Node* integer = graph()->NewNode(machine()->Float64RoundUp().op(), value);
    Node* real = graph()->NewNode(machine()->Float64Sub(), integer, value);
    return Replace(graph()->NewNode(
        common()->Select(MachineRepresentation::kFloat64),
        graph()->NewNode(machine()->Float64LessThan(),
                         jsgraph()->Float64Constant(0.5), real),
        graph()->NewNode(machine()->Float64Sub(), integer,
                         jsgraph()->Float64Constant(1.0)),
        integer));
  }
  return NoChange();
}
170

171
Reduction JSBuiltinReducer::Reduce(Node* node) {
172
  Reduction reduction = NoChange();
173 174 175 176 177
  JSCallReduction r(node);

  // Dispatch according to the BuiltinFunctionId if present.
  if (!r.HasBuiltinFunctionId()) return NoChange();
  switch (r.GetBuiltinFunctionId()) {
178
    case kMathMax:
179 180
      reduction = ReduceMathMax(node);
      break;
181
    case kMathImul:
182 183
      reduction = ReduceMathImul(node);
      break;
184
    case kMathFround:
185 186
      reduction = ReduceMathFround(node);
      break;
187 188 189
    case kMathRound:
      reduction = ReduceMathRound(node);
      break;
190 191 192
    default:
      break;
  }
193 194 195 196 197 198

  // Replace builtin call assuming replacement nodes are pure values that don't
  // produce an effect. Replaces {node} with {reduction} and relaxes effects.
  if (reduction.Changed()) ReplaceWithValue(node, reduction.replacement());

  return reduction;
199 200
}

201 202 203 204

Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }


205 206 207
Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }


208 209 210 211 212 213 214 215 216
CommonOperatorBuilder* JSBuiltinReducer::common() const {
  return jsgraph()->common();
}


MachineOperatorBuilder* JSBuiltinReducer::machine() const {
  return jsgraph()->machine();
}

217 218 219 220 221

SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
  return jsgraph()->simplified();
}

222 223 224
}  // namespace compiler
}  // namespace internal
}  // namespace v8