js-builtin-reducer.cc revision bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8
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/type-cache.h" 12#include "src/types.h" 13 14namespace v8 { 15namespace internal { 16namespace compiler { 17 18 19// Helper class to access JSCallFunction nodes that are potential candidates 20// for reduction when they have a BuiltinFunctionId associated with them. 21class JSCallReduction { 22 public: 23 explicit JSCallReduction(Node* node) : node_(node) {} 24 25 // Determines whether the node is a JSCallFunction operation that targets a 26 // constant callee being a well-known builtin with a BuiltinFunctionId. 27 bool HasBuiltinFunctionId() { 28 if (node_->opcode() != IrOpcode::kJSCallFunction) return false; 29 HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0)); 30 if (!m.HasValue() || !m.Value()->IsJSFunction()) return false; 31 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 32 return function->shared()->HasBuiltinFunctionId(); 33 } 34 35 // Retrieves the BuiltinFunctionId as described above. 36 BuiltinFunctionId GetBuiltinFunctionId() { 37 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode()); 38 HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0)); 39 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 40 return function->shared()->builtin_function_id(); 41 } 42 43 // Determines whether the call takes zero inputs. 44 bool InputsMatchZero() { return GetJSCallArity() == 0; } 45 46 // Determines whether the call takes one input of the given type. 47 bool InputsMatchOne(Type* t1) { 48 return GetJSCallArity() == 1 && 49 NodeProperties::GetType(GetJSCallInput(0))->Is(t1); 50 } 51 52 // Determines whether the call takes two inputs of the given types. 53 bool InputsMatchTwo(Type* t1, Type* t2) { 54 return GetJSCallArity() == 2 && 55 NodeProperties::GetType(GetJSCallInput(0))->Is(t1) && 56 NodeProperties::GetType(GetJSCallInput(1))->Is(t2); 57 } 58 59 // Determines whether the call takes inputs all of the given type. 60 bool InputsMatchAll(Type* t) { 61 for (int i = 0; i < GetJSCallArity(); i++) { 62 if (!NodeProperties::GetType(GetJSCallInput(i))->Is(t)) { 63 return false; 64 } 65 } 66 return true; 67 } 68 69 Node* left() { return GetJSCallInput(0); } 70 Node* right() { return GetJSCallInput(1); } 71 72 int GetJSCallArity() { 73 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode()); 74 // Skip first (i.e. callee) and second (i.e. receiver) operand. 75 return node_->op()->ValueInputCount() - 2; 76 } 77 78 Node* GetJSCallInput(int index) { 79 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode()); 80 DCHECK_LT(index, GetJSCallArity()); 81 // Skip first (i.e. callee) and second (i.e. receiver) operand. 82 return NodeProperties::GetValueInput(node_, index + 2); 83 } 84 85 private: 86 Node* node_; 87}; 88 89JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph) 90 : AdvancedReducer(editor), 91 jsgraph_(jsgraph), 92 type_cache_(TypeCache::Get()) {} 93 94// ECMA-262, section 15.8.2.11. 95Reduction JSBuiltinReducer::ReduceMathMax(Node* node) { 96 JSCallReduction r(node); 97 if (r.InputsMatchZero()) { 98 // Math.max() -> -Infinity 99 return Replace(jsgraph()->Constant(-V8_INFINITY)); 100 } 101 if (r.InputsMatchOne(Type::Number())) { 102 // Math.max(a:number) -> a 103 return Replace(r.left()); 104 } 105 if (r.InputsMatchAll(Type::Integral32())) { 106 // Math.max(a:int32, b:int32, ...) 107 Node* value = r.GetJSCallInput(0); 108 for (int i = 1; i < r.GetJSCallArity(); i++) { 109 Node* const input = r.GetJSCallInput(i); 110 value = graph()->NewNode( 111 common()->Select(MachineRepresentation::kNone), 112 graph()->NewNode(simplified()->NumberLessThan(), input, value), value, 113 input); 114 } 115 return Replace(value); 116 } 117 return NoChange(); 118} 119 120// ES6 section 20.2.2.19 Math.imul ( x, y ) 121Reduction JSBuiltinReducer::ReduceMathImul(Node* node) { 122 JSCallReduction r(node); 123 if (r.InputsMatchTwo(Type::Number(), Type::Number())) { 124 // Math.imul(a:number, b:number) -> NumberImul(NumberToUint32(a), 125 // NumberToUint32(b)) 126 Node* a = graph()->NewNode(simplified()->NumberToUint32(), r.left()); 127 Node* b = graph()->NewNode(simplified()->NumberToUint32(), r.right()); 128 Node* value = graph()->NewNode(simplified()->NumberImul(), a, b); 129 return Replace(value); 130 } 131 return NoChange(); 132} 133 134// ES6 section 20.2.2.10 Math.ceil ( x ) 135Reduction JSBuiltinReducer::ReduceMathCeil(Node* node) { 136 JSCallReduction r(node); 137 if (r.InputsMatchOne(Type::Number())) { 138 // Math.ceil(a:number) -> NumberCeil(a) 139 Node* value = graph()->NewNode(simplified()->NumberCeil(), r.left()); 140 return Replace(value); 141 } 142 return NoChange(); 143} 144 145// ES6 section 20.2.2.11 Math.clz32 ( x ) 146Reduction JSBuiltinReducer::ReduceMathClz32(Node* node) { 147 JSCallReduction r(node); 148 if (r.InputsMatchOne(Type::Unsigned32())) { 149 // Math.clz32(a:unsigned32) -> NumberClz32(a) 150 Node* value = graph()->NewNode(simplified()->NumberClz32(), r.left()); 151 return Replace(value); 152 } 153 if (r.InputsMatchOne(Type::Number())) { 154 // Math.clz32(a:number) -> NumberClz32(NumberToUint32(a)) 155 Node* value = graph()->NewNode( 156 simplified()->NumberClz32(), 157 graph()->NewNode(simplified()->NumberToUint32(), r.left())); 158 return Replace(value); 159 } 160 return NoChange(); 161} 162 163// ES6 draft 08-24-14, section 20.2.2.16. 164Reduction JSBuiltinReducer::ReduceMathFloor(Node* node) { 165 JSCallReduction r(node); 166 if (r.InputsMatchOne(Type::Number())) { 167 // Math.floor(a:number) -> NumberFloor(a) 168 Node* value = graph()->NewNode(simplified()->NumberFloor(), r.left()); 169 return Replace(value); 170 } 171 return NoChange(); 172} 173 174// ES6 draft 08-24-14, section 20.2.2.17. 175Reduction JSBuiltinReducer::ReduceMathFround(Node* node) { 176 JSCallReduction r(node); 177 if (r.InputsMatchOne(Type::NumberOrUndefined())) { 178 // Math.fround(a:number) -> TruncateFloat64ToFloat32(a) 179 Node* value = 180 graph()->NewNode(machine()->TruncateFloat64ToFloat32(), r.left()); 181 return Replace(value); 182 } 183 return NoChange(); 184} 185 186// ES6 section 20.2.2.28 Math.round ( x ) 187Reduction JSBuiltinReducer::ReduceMathRound(Node* node) { 188 JSCallReduction r(node); 189 if (r.InputsMatchOne(Type::Number())) { 190 // Math.round(a:number) -> NumberRound(a) 191 Node* value = graph()->NewNode(simplified()->NumberRound(), r.left()); 192 return Replace(value); 193 } 194 return NoChange(); 195} 196 197// ES6 section 20.2.2.32 Math.sqrt ( x ) 198Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) { 199 JSCallReduction r(node); 200 if (r.InputsMatchOne(Type::Number())) { 201 // Math.sqrt(a:number) -> Float64Sqrt(a) 202 Node* value = graph()->NewNode(machine()->Float64Sqrt(), r.left()); 203 return Replace(value); 204 } 205 return NoChange(); 206} 207 208// ES6 section 20.2.2.35 Math.trunc ( x ) 209Reduction JSBuiltinReducer::ReduceMathTrunc(Node* node) { 210 JSCallReduction r(node); 211 if (r.InputsMatchOne(Type::Number())) { 212 // Math.trunc(a:number) -> NumberTrunc(a) 213 Node* value = graph()->NewNode(simplified()->NumberTrunc(), r.left()); 214 return Replace(value); 215 } 216 return NoChange(); 217} 218 219Reduction JSBuiltinReducer::Reduce(Node* node) { 220 Reduction reduction = NoChange(); 221 JSCallReduction r(node); 222 223 // Dispatch according to the BuiltinFunctionId if present. 224 if (!r.HasBuiltinFunctionId()) return NoChange(); 225 switch (r.GetBuiltinFunctionId()) { 226 case kMathMax: 227 reduction = ReduceMathMax(node); 228 break; 229 case kMathImul: 230 reduction = ReduceMathImul(node); 231 break; 232 case kMathClz32: 233 reduction = ReduceMathClz32(node); 234 break; 235 case kMathCeil: 236 reduction = ReduceMathCeil(node); 237 break; 238 case kMathFloor: 239 reduction = ReduceMathFloor(node); 240 break; 241 case kMathFround: 242 reduction = ReduceMathFround(node); 243 break; 244 case kMathRound: 245 reduction = ReduceMathRound(node); 246 break; 247 case kMathSqrt: 248 reduction = ReduceMathSqrt(node); 249 break; 250 case kMathTrunc: 251 reduction = ReduceMathTrunc(node); 252 break; 253 default: 254 break; 255 } 256 257 // Replace builtin call assuming replacement nodes are pure values that don't 258 // produce an effect. Replaces {node} with {reduction} and relaxes effects. 259 if (reduction.Changed()) ReplaceWithValue(node, reduction.replacement()); 260 261 return reduction; 262} 263 264 265Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); } 266 267 268Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); } 269 270 271CommonOperatorBuilder* JSBuiltinReducer::common() const { 272 return jsgraph()->common(); 273} 274 275 276MachineOperatorBuilder* JSBuiltinReducer::machine() const { 277 return jsgraph()->machine(); 278} 279 280 281SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const { 282 return jsgraph()->simplified(); 283} 284 285} // namespace compiler 286} // namespace internal 287} // namespace v8 288