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 11namespace v8 { 12namespace internal { 13namespace 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. 18static 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. 29class 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. 99Reduction 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. 111Reduction 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. 143Reduction 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 154Reduction 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