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/graph-inl.h"
      6 #include "src/compiler/js-builtin-reducer.h"
      7 #include "src/compiler/node-matchers.h"
      8 #include "src/compiler/node-properties-inl.h"
      9 #include "src/types.h"
     10 
     11 namespace v8 {
     12 namespace internal {
     13 namespace compiler {
     14 
     15 
     16 // Helper method that assumes replacement nodes are pure values that don't
     17 // produce an effect. Replaces {node} with {reduction} and relaxes effects.
     18 static Reduction ReplaceWithPureReduction(Node* node, Reduction reduction) {
     19   if (reduction.Changed()) {
     20     NodeProperties::ReplaceWithValue(node, reduction.replacement());
     21     return reduction;
     22   }
     23   return Reducer::NoChange();
     24 }
     25 
     26 
     27 // Helper class to access JSCallFunction nodes that are potential candidates
     28 // for reduction when they have a BuiltinFunctionId associated with them.
     29 class JSCallReduction {
     30  public:
     31   explicit JSCallReduction(Node* node) : node_(node) {}
     32 
     33   // Determines whether the node is a JSCallFunction operation that targets a
     34   // constant callee being a well-known builtin with a BuiltinFunctionId.
     35   bool HasBuiltinFunctionId() {
     36     if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
     37     HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
     38     if (!m.HasValue() || !m.Value().handle()->IsJSFunction()) return false;
     39     Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
     40     return function->shared()->HasBuiltinFunctionId();
     41   }
     42 
     43   // Retrieves the BuiltinFunctionId as described above.
     44   BuiltinFunctionId GetBuiltinFunctionId() {
     45     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
     46     HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
     47     Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
     48     return function->shared()->builtin_function_id();
     49   }
     50 
     51   // Determines whether the call takes zero inputs.
     52   bool InputsMatchZero() { return GetJSCallArity() == 0; }
     53 
     54   // Determines whether the call takes one input of the given type.
     55   bool InputsMatchOne(Type* t1) {
     56     return GetJSCallArity() == 1 &&
     57            NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1);
     58   }
     59 
     60   // Determines whether the call takes two inputs of the given types.
     61   bool InputsMatchTwo(Type* t1, Type* t2) {
     62     return GetJSCallArity() == 2 &&
     63            NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1) &&
     64            NodeProperties::GetBounds(GetJSCallInput(1)).upper->Is(t2);
     65   }
     66 
     67   // Determines whether the call takes inputs all of the given type.
     68   bool InputsMatchAll(Type* t) {
     69     for (int i = 0; i < GetJSCallArity(); i++) {
     70       if (!NodeProperties::GetBounds(GetJSCallInput(i)).upper->Is(t)) {
     71         return false;
     72       }
     73     }
     74     return true;
     75   }
     76 
     77   Node* left() { return GetJSCallInput(0); }
     78   Node* right() { return GetJSCallInput(1); }
     79 
     80   int GetJSCallArity() {
     81     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
     82     // Skip first (i.e. callee) and second (i.e. receiver) operand.
     83     return OperatorProperties::GetValueInputCount(node_->op()) - 2;
     84   }
     85 
     86   Node* GetJSCallInput(int index) {
     87     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
     88     DCHECK_LT(index, GetJSCallArity());
     89     // Skip first (i.e. callee) and second (i.e. receiver) operand.
     90     return NodeProperties::GetValueInput(node_, index + 2);
     91   }
     92 
     93  private:
     94   Node* node_;
     95 };
     96 
     97 
     98 // ECMA-262, section 15.8.2.17.
     99 Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
    100   JSCallReduction r(node);
    101   if (r.InputsMatchOne(Type::Number())) {
    102     // Math.sqrt(a:number) -> Float64Sqrt(a)
    103     Node* value = graph()->NewNode(machine()->Float64Sqrt(), r.left());
    104     return Replace(value);
    105   }
    106   return NoChange();
    107 }
    108 
    109 
    110 // ECMA-262, section 15.8.2.11.
    111 Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
    112   JSCallReduction r(node);
    113   if (r.InputsMatchZero()) {
    114     // Math.max() -> -Infinity
    115     return Replace(jsgraph()->Constant(-V8_INFINITY));
    116   }
    117   if (r.InputsMatchOne(Type::Number())) {
    118     // Math.max(a:number) -> a
    119     return Replace(r.left());
    120   }
    121   if (r.InputsMatchAll(Type::Integral32())) {
    122     // Math.max(a:int32, b:int32, ...)
    123     Node* value = r.GetJSCallInput(0);
    124     for (int i = 1; i < r.GetJSCallArity(); i++) {
    125       Node* p = r.GetJSCallInput(i);
    126       Node* control = graph()->start();
    127       Node* tag = graph()->NewNode(simplified()->NumberLessThan(), value, p);
    128 
    129       Node* branch = graph()->NewNode(common()->Branch(), tag, control);
    130       Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
    131       Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    132       Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
    133 
    134       value = graph()->NewNode(common()->Phi(kMachNone, 2), p, value, merge);
    135     }
    136     return Replace(value);
    137   }
    138   return NoChange();
    139 }
    140 
    141 
    142 // ES6 draft 08-24-14, section 20.2.2.19.
    143 Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
    144   JSCallReduction r(node);
    145   if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
    146     // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
    147     Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
    148     return Replace(value);
    149   }
    150   return NoChange();
    151 }
    152 
    153 
    154 Reduction JSBuiltinReducer::Reduce(Node* node) {
    155   JSCallReduction r(node);
    156 
    157   // Dispatch according to the BuiltinFunctionId if present.
    158   if (!r.HasBuiltinFunctionId()) return NoChange();
    159   switch (r.GetBuiltinFunctionId()) {
    160     case kMathSqrt:
    161       return ReplaceWithPureReduction(node, ReduceMathSqrt(node));
    162     case kMathMax:
    163       return ReplaceWithPureReduction(node, ReduceMathMax(node));
    164     case kMathImul:
    165       return ReplaceWithPureReduction(node, ReduceMathImul(node));
    166     default:
    167       break;
    168   }
    169   return NoChange();
    170 }
    171 
    172 }  // namespace compiler
    173 }  // namespace internal
    174 }  // namespace v8
    175