v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
escape-analysis-reducer.cc
Go to the documentation of this file.
1// Copyright 2017 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
6
12
13namespace v8 {
14namespace internal {
15namespace compiler {
16
18 Editor* editor, JSGraph* jsgraph, JSHeapBroker* broker,
19 EscapeAnalysisResult analysis_result, Zone* zone)
20 : AdvancedReducer(editor),
21 jsgraph_(jsgraph),
23 analysis_result_(analysis_result),
24 object_id_cache_(zone),
25 node_cache_(jsgraph->graph(), zone),
26 arguments_elements_(zone),
27 zone_(zone) {}
28
30 Node* replacement) {
31 const VirtualObject* vobject =
32 analysis_result().GetVirtualObject(replacement);
33 if (replacement->opcode() == IrOpcode::kDead ||
34 (vobject && !vobject->HasEscaped())) {
35 RelaxEffectsAndControls(original);
36 return Replace(replacement);
37 }
38 Type const replacement_type = NodeProperties::GetType(replacement);
39 Type const original_type = NodeProperties::GetType(original);
40 if (replacement_type.Is(original_type)) {
41 RelaxEffectsAndControls(original);
42 return Replace(replacement);
43 }
44
45 // We need to guard the replacement if we would widen the type otherwise.
46 DCHECK_EQ(1, original->op()->EffectOutputCount());
47 DCHECK_EQ(1, original->op()->EffectInputCount());
48 DCHECK_EQ(1, original->op()->ControlInputCount());
49 Node* effect = NodeProperties::GetEffectInput(original);
50 Node* control = NodeProperties::GetControlInput(original);
51 original->TrimInputCount(0);
52 original->AppendInput(jsgraph()->zone(), replacement);
53 original->AppendInput(jsgraph()->zone(), effect);
54 original->AppendInput(jsgraph()->zone(), control);
56 original,
57 Type::Intersect(original_type, replacement_type, jsgraph()->zone()));
59 jsgraph()->common()->TypeGuard(original_type));
60 ReplaceWithValue(original, original, original, control);
61 return NoChange();
62}
63
65 VirtualObject::Id id = vobject->id();
66 if (id >= object_id_cache_.size()) object_id_cache_.resize(id + 1);
67 if (!object_id_cache_[id]) {
68 Node* node = jsgraph()->graph()->NewNode(jsgraph()->common()->ObjectId(id));
69 NodeProperties::SetType(node, Type::Object());
71 }
72 return object_id_cache_[id];
73}
74
76 if (Node* replacement = analysis_result().GetReplacementOf(node)) {
77 DCHECK(node->opcode() != IrOpcode::kAllocate &&
78 node->opcode() != IrOpcode::kFinishRegion);
79 DCHECK_NE(replacement, node);
80 return ReplaceNode(node, replacement);
81 }
82
83 switch (node->opcode()) {
84 case IrOpcode::kAllocate:
85 case IrOpcode::kTypeGuard: {
86 const VirtualObject* vobject = analysis_result().GetVirtualObject(node);
87 if (vobject && !vobject->HasEscaped()) {
88 RelaxEffectsAndControls(node);
89 }
90 return NoChange();
91 }
92 case IrOpcode::kFinishRegion: {
93 Node* effect = NodeProperties::GetEffectInput(node, 0);
94 if (effect->opcode() == IrOpcode::kBeginRegion) {
95 RelaxEffectsAndControls(effect);
96 RelaxEffectsAndControls(node);
97 }
98 return NoChange();
99 }
100 case IrOpcode::kNewArgumentsElements:
101 arguments_elements_.insert(node);
102 return NoChange();
103 default: {
104 // TODO(sigurds): Change this to GetFrameStateInputCount once
105 // it is working. For now we use EffectInputCount > 0 to determine
106 // whether a node might have a frame state input.
107 if (node->op()->EffectInputCount() > 0) {
109 }
110 return NoChange();
111 }
112 }
113}
114
115// While doing DFS on the FrameState tree, we have to recognize duplicate
116// occurrences of virtual objects.
118 public:
119 explicit Deduplicator(Zone* zone) : zone_(zone) {}
120 bool SeenBefore(const VirtualObject* vobject) {
121 DCHECK_LE(vobject->id(), std::numeric_limits<int>::max());
122 int id = static_cast<int>(vobject->id());
123 if (id >= is_duplicate_.length()) {
124 is_duplicate_.Resize(id + 1, zone_);
125 }
126 bool is_duplicate = is_duplicate_.Contains(id);
127 is_duplicate_.Add(id);
128 return is_duplicate;
129 }
130
131 private:
134};
135
137 DCHECK_GE(node->op()->EffectInputCount(), 1);
138 for (int i = 0; i < node->InputCount(); ++i) {
139 Node* input = node->InputAt(i);
140 if (input->opcode() == IrOpcode::kFrameState) {
141 Deduplicator deduplicator(zone());
142 if (Node* ret = ReduceDeoptState(input, node, &deduplicator)) {
143 node->ReplaceInput(i, ret);
144 }
145 }
146 }
147}
148
150 Deduplicator* deduplicator) {
151 if (node->opcode() == IrOpcode::kFrameState) {
153 // This input order is important to match the DFS traversal used in the
154 // instruction selector. Otherwise, the instruction selector might find a
155 // duplicate node before the original one.
156 for (int input_id : {FrameState::kFrameStateOuterStateInput,
162 Node* input = node->InputAt(input_id);
163 new_node.ReplaceInput(ReduceDeoptState(input, effect, deduplicator),
164 input_id);
165 }
166 return new_node.Get();
167 } else if (node->opcode() == IrOpcode::kStateValues) {
169 for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
170 Node* input = NodeProperties::GetValueInput(node, i);
171 new_node.ReplaceValueInput(ReduceDeoptState(input, effect, deduplicator),
172 i);
173 }
174 return new_node.Get();
175 } else if (const VirtualObject* vobject = analysis_result().GetVirtualObject(
176 SkipValueIdentities(node))) {
177 if (vobject->HasEscaped()) return node;
178 if (deduplicator->SeenBefore(vobject)) {
179 return ObjectIdNode(vobject);
180 } else {
181 std::vector<Node*> inputs;
182 for (int offset = 0; offset < vobject->size(); offset += kTaggedSize) {
183 Node* field =
184 analysis_result().GetVirtualObjectField(vobject, offset, effect);
185 CHECK_NOT_NULL(field);
186 if (field != jsgraph()->Dead()) {
187 inputs.push_back(ReduceDeoptState(field, effect, deduplicator));
188 }
189 }
190 int num_inputs = static_cast<int>(inputs.size());
193 jsgraph()->common()->ObjectState(vobject->id(), num_inputs),
194 num_inputs, &inputs.front(), NodeProperties::GetType(node));
195 return new_node.Get();
196 }
197 } else {
198 return node;
199 }
200}
201
203 AllNodes all(zone(), jsgraph()->graph());
204 for (Node* node : all.reachable) {
205 if (node->opcode() == IrOpcode::kAllocate) {
206 if (const VirtualObject* vobject =
207 analysis_result().GetVirtualObject(node)) {
208 if (!vobject->HasEscaped()) {
209 FATAL("Escape analysis failed to remove node %s#%d\n",
210 node->op()->mnemonic(), node->id());
211 }
212 }
213 }
214 }
215}
216
218 OperationTyper op_typer(broker_, jsgraph()->graph()->zone());
219 for (Node* node : arguments_elements_) {
220 const NewArgumentsElementsParameters& params =
222 ArgumentsStateType type = params.arguments_type();
223 int mapped_count = type == CreateArgumentsType::kMappedArguments
224 ? params.formal_parameter_count()
225 : 0;
226
227 Node* arguments_length = NodeProperties::GetValueInput(node, 0);
228 if (arguments_length->opcode() != IrOpcode::kArgumentsLength) continue;
229
230 Node* arguments_length_state = nullptr;
231 for (Edge edge : arguments_length->use_edges()) {
232 Node* use = edge.from();
233 switch (use->opcode()) {
234 case IrOpcode::kObjectState:
235 case IrOpcode::kTypedObjectState:
236 case IrOpcode::kStateValues:
237 case IrOpcode::kTypedStateValues:
238 if (!arguments_length_state) {
239 arguments_length_state = jsgraph()->graph()->NewNode(
240 jsgraph()->common()->ArgumentsLengthState());
241 NodeProperties::SetType(arguments_length_state,
242 Type::OtherInternal());
243 }
244 edge.UpdateTo(arguments_length_state);
245 break;
246 default:
247 break;
248 }
249 }
250
251 bool escaping_use = false;
252 ZoneVector<Node*> loads(zone());
253 for (Edge edge : node->use_edges()) {
254 Node* use = edge.from();
255 if (!NodeProperties::IsValueEdge(edge)) continue;
256 if (use->use_edges().empty()) {
257 // A node without uses is dead, so we don't have to care about it.
258 continue;
259 }
260 switch (use->opcode()) {
261 case IrOpcode::kStateValues:
262 case IrOpcode::kTypedStateValues:
263 case IrOpcode::kObjectState:
264 case IrOpcode::kTypedObjectState:
265 break;
266 case IrOpcode::kLoadElement:
267 if (mapped_count == 0) {
268 loads.push_back(use);
269 } else {
270 escaping_use = true;
271 }
272 break;
273 case IrOpcode::kLoadField:
274 if (FieldAccessOf(use->op()).offset ==
275 offsetof(FixedArray, length_)) {
276 loads.push_back(use);
277 } else {
278 escaping_use = true;
279 }
280 break;
281 default:
282 // If the arguments elements node node is used by an unhandled node,
283 // then we cannot remove this allocation.
284 escaping_use = true;
285 break;
286 }
287 if (escaping_use) break;
288 }
289 if (!escaping_use) {
290 Node* arguments_elements_state = jsgraph()->graph()->NewNode(
291 jsgraph()->common()->ArgumentsElementsState(type));
292 NodeProperties::SetType(arguments_elements_state, Type::OtherInternal());
293 ReplaceWithValue(node, arguments_elements_state);
294
295 for (Node* load : loads) {
296 switch (load->opcode()) {
297 case IrOpcode::kLoadElement: {
298 Node* index = NodeProperties::GetValueInput(load, 1);
299 Node* formal_parameter_count =
300 jsgraph()->ConstantNoHole(params.formal_parameter_count());
302 formal_parameter_count,
303 Type::Constant(params.formal_parameter_count(),
304 jsgraph()->graph()->zone()));
305 Node* offset_to_first_elem = jsgraph()->ConstantNoHole(
307 if (!NodeProperties::IsTyped(offset_to_first_elem)) {
309 offset_to_first_elem,
311 jsgraph()->graph()->zone()));
312 }
313
315 jsgraph()->simplified()->NumberAdd(), index,
316 offset_to_first_elem);
317 Type offset_type = op_typer.NumberAdd(
319 NodeProperties::GetType(offset_to_first_elem));
320 NodeProperties::SetType(offset, offset_type);
322 // In the case of rest parameters we should skip the formal
323 // parameters.
324 offset = jsgraph()->graph()->NewNode(
325 jsgraph()->simplified()->NumberAdd(), offset,
326 formal_parameter_count);
328 offset, op_typer.NumberAdd(
329 offset_type,
330 NodeProperties::GetType(formal_parameter_count)));
331 }
332 Node* frame = jsgraph()->graph()->NewNode(
333 jsgraph()->machine()->LoadFramePointer());
334 NodeProperties::SetType(frame, Type::ExternalPointer());
335 NodeProperties::ReplaceValueInput(load, frame, 0);
338 load, jsgraph()->simplified()->LoadStackArgument());
339 break;
340 }
341 case IrOpcode::kLoadField: {
342 DCHECK_EQ(FieldAccessOf(load->op()).offset,
343 offsetof(FixedArray, length_));
344 Node* length = NodeProperties::GetValueInput(node, 0);
345 ReplaceWithValue(load, length);
346 break;
347 }
348 default:
349 UNREACHABLE();
350 }
351 }
352 }
353 }
354}
355
357 auto it = cache_.find(node);
358 if (it != cache_.end()) {
359 return *it;
360 } else {
361 return nullptr;
362 }
363}
364
366 const Operator* op, int input_count,
367 Node** inputs, Type type)
368 : node_cache_(cache), from_(nullptr) {
369 if (!node_cache_->temp_nodes_.empty()) {
370 tmp_ = node_cache_->temp_nodes_.back();
371 node_cache_->temp_nodes_.pop_back();
372 int tmp_input_count = tmp_->InputCount();
373 if (input_count <= tmp_input_count) {
374 tmp_->TrimInputCount(input_count);
375 }
376 for (int i = 0; i < input_count; ++i) {
377 if (i < tmp_input_count) {
378 tmp_->ReplaceInput(i, inputs[i]);
379 } else {
380 tmp_->AppendInput(node_cache_->graph_->zone(), inputs[i]);
381 }
382 }
384 } else {
385 tmp_ = node_cache_->graph_->NewNode(op, input_count, inputs);
386 }
387 NodeProperties::SetType(tmp_, type);
388}
389
391 DCHECK(tmp_ || from_);
392 Node* node;
393 if (!tmp_) {
394 node = node_cache_->Query(from_);
395 if (!node) node = from_;
396 } else {
397 node = node_cache_->Query(tmp_);
398 if (node) {
400 } else {
401 node = tmp_;
402 node_cache_->Insert(node);
403 }
404 }
405 tmp_ = from_ = nullptr;
406 return node;
407}
408
410 DCHECK(tmp_ || from_);
411 if (!tmp_) {
412 if (node_cache_->temp_nodes_.empty()) {
413 tmp_ = node_cache_->graph_->CloneNode(from_);
414 } else {
415 tmp_ = node_cache_->temp_nodes_.back();
416 node_cache_->temp_nodes_.pop_back();
417 int from_input_count = from_->InputCount();
418 int tmp_input_count = tmp_->InputCount();
419 if (from_input_count <= tmp_input_count) {
420 tmp_->TrimInputCount(from_input_count);
421 }
422 for (int i = 0; i < from_input_count; ++i) {
423 if (i < tmp_input_count) {
424 tmp_->ReplaceInput(i, from_->InputAt(i));
425 } else {
426 tmp_->AppendInput(node_cache_->graph_->zone(), from_->InputAt(i));
427 }
428 }
430 NodeProperties::ChangeOp(tmp_, from_->op());
431 }
432 }
433 return tmp_;
434}
435
436} // namespace compiler
437} // namespace internal
438} // namespace v8
TFGraph * graph
JSGraph * jsgraph
SimplifiedOperatorBuilder * simplified
bool Contains(int i) const
Definition bit-vector.h:180
void Resize(int new_length, Zone *zone)
Definition bit-vector.h:161
static constexpr int kFixedSlotCountAboveFp
void resize(size_t new_size)
void push_back(const T &value)
bool SeenBefore(const VirtualObject *vobject)
Reduction ReplaceNode(Node *original, Node *replacement)
Node * ReduceDeoptState(Node *node, Node *effect, Deduplicator *deduplicator)
EscapeAnalysisReducer(Editor *editor, JSGraph *jsgraph, JSHeapBroker *broker, EscapeAnalysisResult analysis_result, Zone *zone)
Node * ObjectIdNode(const VirtualObject *vobject)
Node * GetVirtualObjectField(const VirtualObject *vobject, int field, Node *effect)
const VirtualObject * GetVirtualObject(Node *node)
static constexpr int kFrameStateLocalsInput
static constexpr int kFrameStateContextInput
static constexpr int kFrameStateParametersInput
static constexpr int kFrameStateFunctionInput
static constexpr int kFrameStateOuterStateInput
static constexpr int kFrameStateStackInput
Node * ConstantNoHole(ObjectRef ref, JSHeapBroker *broker)
Definition js-graph.cc:51
ZoneUnorderedSet< Node *, NodeHashCode, NodeEquals > cache_
static void ChangeOp(Node *node, const Operator *new_op)
static Type GetType(const Node *node)
static bool IsTyped(const Node *node)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetValueInput(Node *node, int index)
static void SetType(Node *node, Type type)
static void ReplaceValueInput(Node *node, Node *value, int index)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
void TrimInputCount(int new_input_count)
Definition node.cc:262
const Operator * op() const
Definition node.h:50
void ReplaceInput(int index, Node *new_to)
Definition node.h:76
Node * InputAt(int index) const
Definition node.h:70
void AppendInput(Zone *zone, Node *new_to)
Definition node.cc:154
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
static Type Constant(JSHeapBroker *broker, Handle< i::Object > value, Zone *zone)
static Type Intersect(Type type1, Type type2, Zone *zone)
bool Is(Type that) const
Zone * zone_
JSHeapBroker *const broker_
JSHeapBroker * broker
int32_t offset
Node * node
const int length_
Definition mul-fft.cc:473
const NewArgumentsElementsParameters & NewArgumentsElementsParametersOf(const Operator *op)
const FieldAccess & FieldAccessOf(const Operator *op)
Node * SkipValueIdentities(Node *node)
constexpr int kTaggedSize
Definition globals.h:542
#define FATAL(...)
Definition logging.h:47
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK_NOT_NULL(val)
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485