Home | History | Annotate | Download | only in compiler
      1 // Copyright 2014 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/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/types.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 namespace compiler {
     16 
     17 
     18 // Helper class to access JSCallFunction nodes that are potential candidates
     19 // for reduction when they have a BuiltinFunctionId associated with them.
     20 class JSCallReduction {
     21  public:
     22   explicit JSCallReduction(Node* node) : node_(node) {}
     23 
     24   // Determines whether the node is a JSCallFunction operation that targets a
     25   // constant callee being a well-known builtin with a BuiltinFunctionId.
     26   bool HasBuiltinFunctionId() {
     27     if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
     28     HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
     29     if (!m.HasValue() || !m.Value()->IsJSFunction()) return false;
     30     Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
     31     return function->shared()->HasBuiltinFunctionId();
     32   }
     33 
     34   // Retrieves the BuiltinFunctionId as described above.
     35   BuiltinFunctionId GetBuiltinFunctionId() {
     36     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
     37     HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
     38     Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
     39     return function->shared()->builtin_function_id();
     40   }
     41 
     42   // Determines whether the call takes zero inputs.
     43   bool InputsMatchZero() { return GetJSCallArity() == 0; }
     44 
     45   // Determines whether the call takes one input of the given type.
     46   bool InputsMatchOne(Type* t1) {
     47     return GetJSCallArity() == 1 &&
     48            NodeProperties::GetType(GetJSCallInput(0))->Is(t1);
     49   }
     50 
     51   // Determines whether the call takes two inputs of the given types.
     52   bool InputsMatchTwo(Type* t1, Type* t2) {
     53     return GetJSCallArity() == 2 &&
     54            NodeProperties::GetType(GetJSCallInput(0))->Is(t1) &&
     55            NodeProperties::GetType(GetJSCallInput(1))->Is(t2);
     56   }
     57 
     58   // Determines whether the call takes inputs all of the given type.
     59   bool InputsMatchAll(Type* t) {
     60     for (int i = 0; i < GetJSCallArity(); i++) {
     61       if (!NodeProperties::GetType(GetJSCallInput(i))->Is(t)) {
     62         return false;
     63       }
     64     }
     65     return true;
     66   }
     67 
     68   Node* left() { return GetJSCallInput(0); }
     69   Node* right() { return GetJSCallInput(1); }
     70 
     71   int GetJSCallArity() {
     72     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
     73     // Skip first (i.e. callee) and second (i.e. receiver) operand.
     74     return node_->op()->ValueInputCount() - 2;
     75   }
     76 
     77   Node* GetJSCallInput(int index) {
     78     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
     79     DCHECK_LT(index, GetJSCallArity());
     80     // Skip first (i.e. callee) and second (i.e. receiver) operand.
     81     return NodeProperties::GetValueInput(node_, index + 2);
     82   }
     83 
     84  private:
     85   Node* node_;
     86 };
     87 
     88 
     89 JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph)
     90     : AdvancedReducer(editor), jsgraph_(jsgraph) {}
     91 
     92 
     93 // ECMA-262, section 15.8.2.11.
     94 Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
     95   JSCallReduction r(node);
     96   if (r.InputsMatchZero()) {
     97     // Math.max() -> -Infinity
     98     return Replace(jsgraph()->Constant(-V8_INFINITY));
     99   }
    100   if (r.InputsMatchOne(Type::Number())) {
    101     // Math.max(a:number) -> a
    102     return Replace(r.left());
    103   }
    104   if (r.InputsMatchAll(Type::Integral32())) {
    105     // Math.max(a:int32, b:int32, ...)
    106     Node* value = r.GetJSCallInput(0);
    107     for (int i = 1; i < r.GetJSCallArity(); i++) {
    108       Node* const input = r.GetJSCallInput(i);
    109       value = graph()->NewNode(
    110           common()->Select(MachineRepresentation::kNone),
    111           graph()->NewNode(simplified()->NumberLessThan(), input, value), value,
    112           input);
    113     }
    114     return Replace(value);
    115   }
    116   return NoChange();
    117 }
    118 
    119 
    120 // ES6 draft 08-24-14, section 20.2.2.19.
    121 Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
    122   JSCallReduction r(node);
    123   if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
    124     // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
    125     Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
    126     return Replace(value);
    127   }
    128   return NoChange();
    129 }
    130 
    131 
    132 // ES6 draft 08-24-14, section 20.2.2.17.
    133 Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
    134   JSCallReduction r(node);
    135   if (r.InputsMatchOne(Type::Number())) {
    136     // Math.fround(a:number) -> TruncateFloat64ToFloat32(a)
    137     Node* value =
    138         graph()->NewNode(machine()->TruncateFloat64ToFloat32(), r.left());
    139     return Replace(value);
    140   }
    141   return NoChange();
    142 }
    143 
    144 
    145 Reduction JSBuiltinReducer::Reduce(Node* node) {
    146   Reduction reduction = NoChange();
    147   JSCallReduction r(node);
    148 
    149   // Dispatch according to the BuiltinFunctionId if present.
    150   if (!r.HasBuiltinFunctionId()) return NoChange();
    151   switch (r.GetBuiltinFunctionId()) {
    152     case kMathMax:
    153       reduction = ReduceMathMax(node);
    154       break;
    155     case kMathImul:
    156       reduction = ReduceMathImul(node);
    157       break;
    158     case kMathFround:
    159       reduction = ReduceMathFround(node);
    160       break;
    161     default:
    162       break;
    163   }
    164 
    165   // Replace builtin call assuming replacement nodes are pure values that don't
    166   // produce an effect. Replaces {node} with {reduction} and relaxes effects.
    167   if (reduction.Changed()) ReplaceWithValue(node, reduction.replacement());
    168 
    169   return reduction;
    170 }
    171 
    172 
    173 Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
    174 
    175 
    176 Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
    177 
    178 
    179 CommonOperatorBuilder* JSBuiltinReducer::common() const {
    180   return jsgraph()->common();
    181 }
    182 
    183 
    184 MachineOperatorBuilder* JSBuiltinReducer::machine() const {
    185   return jsgraph()->machine();
    186 }
    187 
    188 
    189 SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
    190   return jsgraph()->simplified();
    191 }
    192 
    193 }  // namespace compiler
    194 }  // namespace internal
    195 }  // namespace v8
    196