V8 Project
js-builtin-reducer.cc
Go to the documentation of this file.
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 
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.
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.
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.
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 &&
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 &&
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++) {
71  return false;
72  }
73  }
74  return true;
75  }
76 
77  Node* left() { return GetJSCallInput(0); }
78  Node* right() { return GetJSCallInput(1); }
79 
81  DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
82  // Skip first (i.e. callee) and second (i.e. receiver) operand.
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.1.
99 Reduction JSBuiltinReducer::ReduceMathAbs(Node* node) {
100  JSCallReduction r(node);
101  if (r.InputsMatchOne(Type::Unsigned32())) {
102  // Math.abs(a:uint32) -> a
103  return Replace(r.left());
104  }
105  if (r.InputsMatchOne(Type::Number())) {
106  // Math.abs(a:number) -> (a > 0 ? a : 0 - a)
107  Node* value = r.left();
108  Node* zero = jsgraph()->ZeroConstant();
109  Node* control = graph()->start();
110  Node* tag = graph()->NewNode(simplified()->NumberLessThan(), zero, value);
111 
112  Node* branch = graph()->NewNode(common()->Branch(), tag, control);
113  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
114  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
115  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
116 
117  Node* neg = graph()->NewNode(simplified()->NumberSubtract(), zero, value);
118  value = graph()->NewNode(common()->Phi(kMachNone, 2), value, neg, merge);
119  return Replace(value);
120  }
121  return NoChange();
122 }
123 
124 
125 // ECMA-262, section 15.8.2.17.
126 Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
127  JSCallReduction r(node);
128  if (r.InputsMatchOne(Type::Number())) {
129  // Math.sqrt(a:number) -> Float64Sqrt(a)
130  Node* value = graph()->NewNode(machine()->Float64Sqrt(), r.left());
131  return Replace(value);
132  }
133  return NoChange();
134 }
135 
136 
137 // ECMA-262, section 15.8.2.11.
138 Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
139  JSCallReduction r(node);
140  if (r.InputsMatchZero()) {
141  // Math.max() -> -Infinity
142  return Replace(jsgraph()->Constant(-V8_INFINITY));
143  }
144  if (r.InputsMatchOne(Type::Number())) {
145  // Math.max(a:number) -> a
146  return Replace(r.left());
147  }
148  if (r.InputsMatchAll(Type::Integral32())) {
149  // Math.max(a:int32, b:int32, ...)
150  Node* value = r.GetJSCallInput(0);
151  for (int i = 1; i < r.GetJSCallArity(); i++) {
152  Node* p = r.GetJSCallInput(i);
153  Node* control = graph()->start();
154  Node* tag = graph()->NewNode(simplified()->NumberLessThan(), value, p);
155 
156  Node* branch = graph()->NewNode(common()->Branch(), tag, control);
157  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
158  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
159  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
160 
161  value = graph()->NewNode(common()->Phi(kMachNone, 2), p, value, merge);
162  }
163  return Replace(value);
164  }
165  return NoChange();
166 }
167 
168 
169 // ES6 draft 08-24-14, section 20.2.2.19.
170 Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
171  JSCallReduction r(node);
172  if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
173  // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
174  Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
175  return Replace(value);
176  }
177  return NoChange();
178 }
179 
180 
181 // ES6 draft 08-24-14, section 20.2.2.17.
182 Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
183  JSCallReduction r(node);
184  if (r.InputsMatchOne(Type::Number())) {
185  // Math.fround(a:number) -> TruncateFloat64ToFloat32(a)
186  Node* value =
187  graph()->NewNode(machine()->TruncateFloat64ToFloat32(), r.left());
188  return Replace(value);
189  }
190  return NoChange();
191 }
192 
193 
194 Reduction JSBuiltinReducer::Reduce(Node* node) {
195  JSCallReduction r(node);
196 
197  // Dispatch according to the BuiltinFunctionId if present.
198  if (!r.HasBuiltinFunctionId()) return NoChange();
199  switch (r.GetBuiltinFunctionId()) {
200  case kMathAbs:
201  return ReplaceWithPureReduction(node, ReduceMathAbs(node));
202  case kMathSqrt:
203  return ReplaceWithPureReduction(node, ReduceMathSqrt(node));
204  case kMathMax:
205  return ReplaceWithPureReduction(node, ReduceMathMax(node));
206  case kMathImul:
207  return ReplaceWithPureReduction(node, ReduceMathImul(node));
208  case kMathFround:
209  return ReplaceWithPureReduction(node, ReduceMathFround(node));
210  default:
211  break;
212  }
213  return NoChange();
214 }
215 
216 } // namespace compiler
217 } // namespace internal
218 } // namespace v8
static Handle< T > cast(Handle< S > that)
Definition: handles.h:116
static void ReplaceWithValue(Node *node, Node *value, Node *effect=NULL)
static Node * GetValueInput(Node *node, int index)
static int GetValueInputCount(const Operator *op)
#define V8_INFINITY
Definition: globals.h:25
#define DCHECK_LT(v1, v2)
Definition: logging.h:209
#define DCHECK_EQ(v1, v2)
Definition: logging.h:206
static Reduction ReplaceWithPureReduction(Node *node, Reduction reduction)
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20