• 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/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:
JSCallReduction(Node * node)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.
HasBuiltinFunctionId()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.
GetBuiltinFunctionId()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.
InputsMatchZero()43   bool InputsMatchZero() { return GetJSCallArity() == 0; }
44 
45   // Determines whether the call takes one input of the given type.
InputsMatchOne(Type * t1)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.
InputsMatchTwo(Type * t1,Type * t2)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.
InputsMatchAll(Type * t)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 
left()68   Node* left() { return GetJSCallInput(0); }
right()69   Node* right() { return GetJSCallInput(1); }
70 
GetJSCallArity()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 
GetJSCallInput(int index)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 
JSBuiltinReducer(Editor * editor,JSGraph * jsgraph)89 JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph)
90     : AdvancedReducer(editor), jsgraph_(jsgraph) {}
91 
92 
93 // ECMA-262, section 15.8.2.11.
ReduceMathMax(Node * node)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.
ReduceMathImul(Node * node)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.
ReduceMathFround(Node * node)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 
Reduce(Node * node)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 
graph() const173 Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
174 
175 
isolate() const176 Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
177 
178 
common() const179 CommonOperatorBuilder* JSBuiltinReducer::common() const {
180   return jsgraph()->common();
181 }
182 
183 
machine() const184 MachineOperatorBuilder* JSBuiltinReducer::machine() const {
185   return jsgraph()->machine();
186 }
187 
188 
simplified() const189 SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
190   return jsgraph()->simplified();
191 }
192 
193 }  // namespace compiler
194 }  // namespace internal
195 }  // namespace v8
196