• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ReplaceWithPureReduction(Node * node,Reduction reduction)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:
JSCallReduction(Node * node)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.
HasBuiltinFunctionId()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.
GetBuiltinFunctionId()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.
InputsMatchZero()52   bool InputsMatchZero() { return GetJSCallArity() == 0; }
53 
54   // Determines whether the call takes one input of the given type.
InputsMatchOne(Type * t1)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.
InputsMatchTwo(Type * t1,Type * t2)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.
InputsMatchAll(Type * t)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 
left()77   Node* left() { return GetJSCallInput(0); }
right()78   Node* right() { return GetJSCallInput(1); }
79 
GetJSCallArity()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 
GetJSCallInput(int index)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.
ReduceMathSqrt(Node * node)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.
ReduceMathMax(Node * node)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.
ReduceMathImul(Node * node)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 
Reduce(Node * node)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