1 // Copyright 2016 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/typed-optimization.h"
6
7 #include "src/compilation-dependencies.h"
8 #include "src/compiler/js-graph.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/simplified-operator.h"
11 #include "src/compiler/type-cache.h"
12 #include "src/isolate-inl.h"
13
14 namespace v8 {
15 namespace internal {
16 namespace compiler {
17
TypedOptimization(Editor * editor,CompilationDependencies * dependencies,Flags flags,JSGraph * jsgraph)18 TypedOptimization::TypedOptimization(Editor* editor,
19 CompilationDependencies* dependencies,
20 Flags flags, JSGraph* jsgraph)
21 : AdvancedReducer(editor),
22 dependencies_(dependencies),
23 flags_(flags),
24 jsgraph_(jsgraph),
25 true_type_(Type::HeapConstant(factory()->true_value(), graph()->zone())),
26 false_type_(
27 Type::HeapConstant(factory()->false_value(), graph()->zone())),
28 type_cache_(TypeCache::Get()) {}
29
~TypedOptimization()30 TypedOptimization::~TypedOptimization() {}
31
Reduce(Node * node)32 Reduction TypedOptimization::Reduce(Node* node) {
33 // Check if the output type is a singleton. In that case we already know the
34 // result value and can simply replace the node if it's eliminable.
35 if (!NodeProperties::IsConstant(node) && NodeProperties::IsTyped(node) &&
36 node->op()->HasProperty(Operator::kEliminatable)) {
37 // TODO(v8:5303): We must not eliminate FinishRegion here. This special
38 // case can be removed once we have separate operators for value and
39 // effect regions.
40 if (node->opcode() == IrOpcode::kFinishRegion) return NoChange();
41 // We can only constant-fold nodes here, that are known to not cause any
42 // side-effect, may it be a JavaScript observable side-effect or a possible
43 // eager deoptimization exit (i.e. {node} has an operator that doesn't have
44 // the Operator::kNoDeopt property).
45 Type* upper = NodeProperties::GetType(node);
46 if (upper->IsInhabited()) {
47 if (upper->IsHeapConstant()) {
48 Node* replacement =
49 jsgraph()->Constant(upper->AsHeapConstant()->Value());
50 ReplaceWithValue(node, replacement);
51 return Changed(replacement);
52 } else if (upper->Is(Type::MinusZero())) {
53 Node* replacement = jsgraph()->Constant(factory()->minus_zero_value());
54 ReplaceWithValue(node, replacement);
55 return Changed(replacement);
56 } else if (upper->Is(Type::NaN())) {
57 Node* replacement = jsgraph()->NaNConstant();
58 ReplaceWithValue(node, replacement);
59 return Changed(replacement);
60 } else if (upper->Is(Type::Null())) {
61 Node* replacement = jsgraph()->NullConstant();
62 ReplaceWithValue(node, replacement);
63 return Changed(replacement);
64 } else if (upper->Is(Type::PlainNumber()) &&
65 upper->Min() == upper->Max()) {
66 Node* replacement = jsgraph()->Constant(upper->Min());
67 ReplaceWithValue(node, replacement);
68 return Changed(replacement);
69 } else if (upper->Is(Type::Undefined())) {
70 Node* replacement = jsgraph()->UndefinedConstant();
71 ReplaceWithValue(node, replacement);
72 return Changed(replacement);
73 }
74 }
75 }
76 switch (node->opcode()) {
77 case IrOpcode::kCheckHeapObject:
78 return ReduceCheckHeapObject(node);
79 case IrOpcode::kCheckMaps:
80 return ReduceCheckMaps(node);
81 case IrOpcode::kCheckString:
82 return ReduceCheckString(node);
83 case IrOpcode::kLoadField:
84 return ReduceLoadField(node);
85 case IrOpcode::kNumberCeil:
86 case IrOpcode::kNumberFloor:
87 case IrOpcode::kNumberRound:
88 case IrOpcode::kNumberTrunc:
89 return ReduceNumberRoundop(node);
90 case IrOpcode::kNumberToUint8Clamped:
91 return ReduceNumberToUint8Clamped(node);
92 case IrOpcode::kPhi:
93 return ReducePhi(node);
94 case IrOpcode::kSelect:
95 return ReduceSelect(node);
96 default:
97 break;
98 }
99 return NoChange();
100 }
101
102 namespace {
103
GetStableMapFromObjectType(Type * object_type)104 MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
105 if (object_type->IsHeapConstant()) {
106 Handle<Map> object_map(object_type->AsHeapConstant()->Value()->map());
107 if (object_map->is_stable()) return object_map;
108 }
109 return MaybeHandle<Map>();
110 }
111
112 } // namespace
113
ReduceCheckHeapObject(Node * node)114 Reduction TypedOptimization::ReduceCheckHeapObject(Node* node) {
115 Node* const input = NodeProperties::GetValueInput(node, 0);
116 Type* const input_type = NodeProperties::GetType(input);
117 if (!input_type->Maybe(Type::SignedSmall())) {
118 ReplaceWithValue(node, input);
119 return Replace(input);
120 }
121 return NoChange();
122 }
123
ReduceCheckMaps(Node * node)124 Reduction TypedOptimization::ReduceCheckMaps(Node* node) {
125 // The CheckMaps(o, ...map...) can be eliminated if map is stable,
126 // o has type Constant(object) and map == object->map, and either
127 // (1) map cannot transition further, or
128 // (2) we can add a code dependency on the stability of map
129 // (to guard the Constant type information).
130 Node* const object = NodeProperties::GetValueInput(node, 0);
131 Type* const object_type = NodeProperties::GetType(object);
132 Node* const effect = NodeProperties::GetEffectInput(node);
133 Handle<Map> object_map;
134 if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
135 for (int i = 1; i < node->op()->ValueInputCount(); ++i) {
136 Node* const map = NodeProperties::GetValueInput(node, i);
137 Type* const map_type = NodeProperties::GetType(map);
138 if (map_type->IsHeapConstant() &&
139 map_type->AsHeapConstant()->Value().is_identical_to(object_map)) {
140 if (object_map->CanTransition()) {
141 dependencies()->AssumeMapStable(object_map);
142 }
143 return Replace(effect);
144 }
145 }
146 }
147 return NoChange();
148 }
149
ReduceCheckString(Node * node)150 Reduction TypedOptimization::ReduceCheckString(Node* node) {
151 Node* const input = NodeProperties::GetValueInput(node, 0);
152 Type* const input_type = NodeProperties::GetType(input);
153 if (input_type->Is(Type::String())) {
154 ReplaceWithValue(node, input);
155 return Replace(input);
156 }
157 return NoChange();
158 }
159
ReduceLoadField(Node * node)160 Reduction TypedOptimization::ReduceLoadField(Node* node) {
161 Node* const object = NodeProperties::GetValueInput(node, 0);
162 Type* const object_type = NodeProperties::GetType(object);
163 FieldAccess const& access = FieldAccessOf(node->op());
164 if (access.base_is_tagged == kTaggedBase &&
165 access.offset == HeapObject::kMapOffset) {
166 // We can replace LoadField[Map](o) with map if is stable, and
167 // o has type Constant(object) and map == object->map, and either
168 // (1) map cannot transition further, or
169 // (2) deoptimization is enabled and we can add a code dependency on the
170 // stability of map (to guard the Constant type information).
171 Handle<Map> object_map;
172 if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
173 if (object_map->CanTransition()) {
174 if (flags() & kDeoptimizationEnabled) {
175 dependencies()->AssumeMapStable(object_map);
176 } else {
177 return NoChange();
178 }
179 }
180 Node* const value = jsgraph()->HeapConstant(object_map);
181 ReplaceWithValue(node, value);
182 return Replace(value);
183 }
184 }
185 return NoChange();
186 }
187
ReduceNumberRoundop(Node * node)188 Reduction TypedOptimization::ReduceNumberRoundop(Node* node) {
189 Node* const input = NodeProperties::GetValueInput(node, 0);
190 Type* const input_type = NodeProperties::GetType(input);
191 if (input_type->Is(type_cache_.kIntegerOrMinusZeroOrNaN)) {
192 return Replace(input);
193 }
194 return NoChange();
195 }
196
ReduceNumberToUint8Clamped(Node * node)197 Reduction TypedOptimization::ReduceNumberToUint8Clamped(Node* node) {
198 Node* const input = NodeProperties::GetValueInput(node, 0);
199 Type* const input_type = NodeProperties::GetType(input);
200 if (input_type->Is(type_cache_.kUint8)) {
201 return Replace(input);
202 }
203 return NoChange();
204 }
205
ReducePhi(Node * node)206 Reduction TypedOptimization::ReducePhi(Node* node) {
207 // Try to narrow the type of the Phi {node}, which might be more precise now
208 // after lowering based on types, i.e. a SpeculativeNumberAdd has a more
209 // precise type than the JSAdd that was in the graph when the Typer was run.
210 DCHECK_EQ(IrOpcode::kPhi, node->opcode());
211 int arity = node->op()->ValueInputCount();
212 Type* type = NodeProperties::GetType(node->InputAt(0));
213 for (int i = 1; i < arity; ++i) {
214 type = Type::Union(type, NodeProperties::GetType(node->InputAt(i)),
215 graph()->zone());
216 }
217 Type* const node_type = NodeProperties::GetType(node);
218 if (!node_type->Is(type)) {
219 type = Type::Intersect(node_type, type, graph()->zone());
220 NodeProperties::SetType(node, type);
221 return Changed(node);
222 }
223 return NoChange();
224 }
225
ReduceSelect(Node * node)226 Reduction TypedOptimization::ReduceSelect(Node* node) {
227 DCHECK_EQ(IrOpcode::kSelect, node->opcode());
228 Node* const condition = NodeProperties::GetValueInput(node, 0);
229 Type* const condition_type = NodeProperties::GetType(condition);
230 Node* const vtrue = NodeProperties::GetValueInput(node, 1);
231 Type* const vtrue_type = NodeProperties::GetType(vtrue);
232 Node* const vfalse = NodeProperties::GetValueInput(node, 2);
233 Type* const vfalse_type = NodeProperties::GetType(vfalse);
234 if (condition_type->Is(true_type_)) {
235 // Select(condition:true, vtrue, vfalse) => vtrue
236 return Replace(vtrue);
237 }
238 if (condition_type->Is(false_type_)) {
239 // Select(condition:false, vtrue, vfalse) => vfalse
240 return Replace(vfalse);
241 }
242 if (vtrue_type->Is(true_type_) && vfalse_type->Is(false_type_)) {
243 // Select(condition, vtrue:true, vfalse:false) => condition
244 return Replace(condition);
245 }
246 if (vtrue_type->Is(false_type_) && vfalse_type->Is(true_type_)) {
247 // Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition)
248 node->TrimInputCount(1);
249 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
250 return Changed(node);
251 }
252 // Try to narrow the type of the Select {node}, which might be more precise
253 // now after lowering based on types.
254 Type* type = Type::Union(vtrue_type, vfalse_type, graph()->zone());
255 Type* const node_type = NodeProperties::GetType(node);
256 if (!node_type->Is(type)) {
257 type = Type::Intersect(node_type, type, graph()->zone());
258 NodeProperties::SetType(node, type);
259 return Changed(node);
260 }
261 return NoChange();
262 }
263
factory() const264 Factory* TypedOptimization::factory() const { return isolate()->factory(); }
265
graph() const266 Graph* TypedOptimization::graph() const { return jsgraph()->graph(); }
267
isolate() const268 Isolate* TypedOptimization::isolate() const { return jsgraph()->isolate(); }
269
simplified() const270 SimplifiedOperatorBuilder* TypedOptimization::simplified() const {
271 return jsgraph()->simplified();
272 }
273
274 } // namespace compiler
275 } // namespace internal
276 } // namespace v8
277