5 #include "src/compiler/escape-analysis-reducer.h" 7 #include "src/compiler/all-nodes.h" 8 #include "src/compiler/simplified-operator.h" 9 #include "src/compiler/type-cache.h" 10 #include "src/frame-constants.h" 19 if (FLAG_trace_turbo_escape) PrintF(__VA_ARGS__); \ 25 EscapeAnalysisReducer::EscapeAnalysisReducer(
26 Editor* editor, JSGraph* jsgraph, EscapeAnalysisResult analysis_result,
28 : AdvancedReducer(editor),
30 analysis_result_(analysis_result),
31 object_id_cache_(zone),
32 node_cache_(jsgraph->graph(), zone),
33 arguments_elements_(zone),
36 Reduction EscapeAnalysisReducer::ReplaceNode(Node* original,
38 const VirtualObject* vobject =
39 analysis_result().GetVirtualObject(replacement);
40 if (replacement->opcode() == IrOpcode::kDead ||
41 (vobject && !vobject->HasEscaped())) {
42 RelaxEffectsAndControls(original);
43 return Replace(replacement);
45 Type const replacement_type = NodeProperties::GetType(replacement);
46 Type const original_type = NodeProperties::GetType(original);
47 if (replacement_type.Is(original_type)) {
48 RelaxEffectsAndControls(original);
49 return Replace(replacement);
53 DCHECK_EQ(1, original->op()->EffectOutputCount());
54 DCHECK_EQ(1, original->op()->EffectInputCount());
55 DCHECK_EQ(1, original->op()->ControlInputCount());
56 Node* effect = NodeProperties::GetEffectInput(original);
57 Node* control = NodeProperties::GetControlInput(original);
58 original->TrimInputCount(0);
59 original->AppendInput(jsgraph()->zone(), replacement);
60 original->AppendInput(jsgraph()->zone(), effect);
61 original->AppendInput(jsgraph()->zone(), control);
62 NodeProperties::SetType(
64 Type::Intersect(original_type, replacement_type, jsgraph()->zone()));
65 NodeProperties::ChangeOp(original,
66 jsgraph()->common()->TypeGuard(original_type));
67 ReplaceWithValue(original, original, original, control);
73 Node* SkipTypeGuards(Node* node) {
74 while (node->opcode() == IrOpcode::kTypeGuard) {
75 node = NodeProperties::GetValueInput(node, 0);
82 Node* EscapeAnalysisReducer::ObjectIdNode(
const VirtualObject* vobject) {
83 VirtualObject::Id
id = vobject->id();
84 if (
id >= object_id_cache_.size()) object_id_cache_.resize(
id + 1);
85 if (!object_id_cache_[
id]) {
86 Node* node = jsgraph()->graph()->NewNode(jsgraph()->common()->ObjectId(
id));
87 NodeProperties::SetType(node, Type::Object());
88 object_id_cache_[id] = node;
90 return object_id_cache_[id];
93 Reduction EscapeAnalysisReducer::Reduce(Node* node) {
94 if (Node* replacement = analysis_result().GetReplacementOf(node)) {
95 DCHECK(node->opcode() != IrOpcode::kAllocate &&
96 node->opcode() != IrOpcode::kFinishRegion);
97 DCHECK_NE(replacement, node);
98 return ReplaceNode(node, replacement);
101 switch (node->opcode()) {
102 case IrOpcode::kAllocate:
103 case IrOpcode::kTypeGuard: {
104 const VirtualObject* vobject = analysis_result().GetVirtualObject(node);
105 if (vobject && !vobject->HasEscaped()) {
106 RelaxEffectsAndControls(node);
110 case IrOpcode::kFinishRegion: {
111 Node* effect = NodeProperties::GetEffectInput(node, 0);
112 if (effect->opcode() == IrOpcode::kBeginRegion) {
113 RelaxEffectsAndControls(effect);
114 RelaxEffectsAndControls(node);
118 case IrOpcode::kNewArgumentsElements:
119 arguments_elements_.insert(node);
125 if (node->op()->EffectInputCount() > 0) {
126 ReduceFrameStateInputs(node);
140 if (
id >= is_duplicate_.size()) {
141 is_duplicate_.resize(
id + 1);
143 bool is_duplicate = is_duplicate_[id];
144 is_duplicate_[id] =
true;
152 void EscapeAnalysisReducer::ReduceFrameStateInputs(
Node* node) {
153 DCHECK_GE(node->op()->EffectInputCount(), 1);
154 for (
int i = 0;
i < node->InputCount(); ++
i) {
155 Node* input = node->InputAt(
i);
156 if (input->opcode() == IrOpcode::kFrameState) {
158 if (
Node* ret = ReduceDeoptState(input, node, &deduplicator)) {
159 node->ReplaceInput(
i, ret);
165 Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect,
166 Deduplicator* deduplicator) {
167 if (node->opcode() == IrOpcode::kFrameState) {
168 NodeHashCache::Constructor new_node(&node_cache_, node);
172 for (
int input_id : {kFrameStateOuterStateInput, kFrameStateFunctionInput,
173 kFrameStateParametersInput, kFrameStateContextInput,
174 kFrameStateLocalsInput, kFrameStateStackInput}) {
175 Node* input = node->InputAt(input_id);
176 new_node.ReplaceInput(ReduceDeoptState(input, effect, deduplicator),
179 return new_node.Get();
180 }
else if (node->opcode() == IrOpcode::kStateValues) {
181 NodeHashCache::Constructor new_node(&node_cache_, node);
182 for (
int i = 0;
i < node->op()->ValueInputCount(); ++
i) {
183 Node* input = NodeProperties::GetValueInput(node,
i);
184 new_node.ReplaceValueInput(ReduceDeoptState(input, effect, deduplicator),
187 return new_node.Get();
188 }
else if (
const VirtualObject* vobject =
189 analysis_result().GetVirtualObject(SkipTypeGuards(node))) {
190 if (vobject->HasEscaped())
return node;
191 if (deduplicator->SeenBefore(vobject)) {
192 return ObjectIdNode(vobject);
194 std::vector<Node*> inputs;
195 for (
int offset = 0; offset < vobject->size(); offset += kPointerSize) {
197 analysis_result().GetVirtualObjectField(vobject, offset, effect);
198 CHECK_NOT_NULL(field);
199 if (field != jsgraph()->Dead()) {
200 inputs.push_back(ReduceDeoptState(field, effect, deduplicator));
203 int num_inputs =
static_cast<int>(inputs.size());
204 NodeHashCache::Constructor new_node(
206 jsgraph()->common()->ObjectState(vobject->id(), num_inputs),
207 num_inputs, &inputs.front(), NodeProperties::GetType(node));
208 return new_node.Get();
215 void EscapeAnalysisReducer::VerifyReplacement()
const {
216 AllNodes all(zone(), jsgraph()->graph());
217 for (Node* node : all.reachable) {
218 if (node->opcode() == IrOpcode::kAllocate) {
219 if (
const VirtualObject* vobject =
220 analysis_result().GetVirtualObject(node)) {
221 if (!vobject->HasEscaped()) {
222 FATAL(
"Escape analysis failed to remove node %s#%d\n",
223 node->op()->mnemonic(), node->id());
230 void EscapeAnalysisReducer::Finalize() {
231 for (Node* node : arguments_elements_) {
232 int mapped_count = NewArgumentsElementsMappedCountOf(node->op());
234 Node* arguments_frame = NodeProperties::GetValueInput(node, 0);
235 if (arguments_frame->opcode() != IrOpcode::kArgumentsFrame)
continue;
236 Node* arguments_length = NodeProperties::GetValueInput(node, 1);
237 if (arguments_length->opcode() != IrOpcode::kArgumentsLength)
continue;
245 mapped_count == FormalParameterCountOf(arguments_length->op()));
246 ArgumentsStateType type = IsRestLengthOf(arguments_length->op())
247 ? ArgumentsStateType::kRestParameter
248 : (mapped_count == 0)
249 ? ArgumentsStateType::kUnmappedArguments
250 : ArgumentsStateType::kMappedArguments;
252 Node* arguments_length_state =
nullptr;
253 for (Edge edge : arguments_length->use_edges()) {
254 Node* use = edge.from();
255 switch (use->opcode()) {
256 case IrOpcode::kObjectState:
257 case IrOpcode::kTypedObjectState:
258 case IrOpcode::kStateValues:
259 case IrOpcode::kTypedStateValues:
260 if (!arguments_length_state) {
261 arguments_length_state = jsgraph()->graph()->NewNode(
262 jsgraph()->common()->ArgumentsLengthState(type));
263 NodeProperties::SetType(arguments_length_state,
264 Type::OtherInternal());
266 edge.UpdateTo(arguments_length_state);
273 bool escaping_use =
false;
274 ZoneVector<Node*> loads(zone());
275 for (Edge edge : node->use_edges()) {
276 Node* use = edge.from();
277 if (!NodeProperties::IsValueEdge(edge))
continue;
278 if (use->use_edges().empty()) {
282 switch (use->opcode()) {
283 case IrOpcode::kStateValues:
284 case IrOpcode::kTypedStateValues:
285 case IrOpcode::kObjectState:
286 case IrOpcode::kTypedObjectState:
288 case IrOpcode::kLoadElement:
289 if (mapped_count == 0) {
290 loads.push_back(use);
295 case IrOpcode::kLoadField:
296 if (FieldAccessOf(use->op()).offset == FixedArray::kLengthOffset) {
297 loads.push_back(use);
308 if (escaping_use)
break;
311 Node* arguments_elements_state = jsgraph()->graph()->NewNode(
312 jsgraph()->common()->ArgumentsElementsState(type));
313 NodeProperties::SetType(arguments_elements_state, Type::OtherInternal());
314 ReplaceWithValue(node, arguments_elements_state);
316 ElementAccess stack_access;
317 stack_access.base_is_tagged = BaseTaggedness::kUntaggedBase;
320 stack_access.header_size =
321 CommonFrameConstants::kFixedFrameSizeAboveFp - kPointerSize;
322 stack_access.type = Type::NonInternal();
323 stack_access.machine_type = MachineType::AnyTagged();
324 stack_access.write_barrier_kind = WriteBarrierKind::kNoWriteBarrier;
325 const Operator* load_stack_op =
326 jsgraph()->simplified()->LoadElement(stack_access);
328 for (Node* load : loads) {
329 switch (load->opcode()) {
330 case IrOpcode::kLoadElement: {
331 Node* index = NodeProperties::GetValueInput(load, 1);
334 Node* offset = jsgraph()->graph()->NewNode(
335 jsgraph()->simplified()->NumberSubtract(), arguments_length,
337 NodeProperties::SetType(offset,
338 TypeCache::Get().kArgumentsLengthType);
339 NodeProperties::ReplaceValueInput(load, arguments_frame, 0);
340 NodeProperties::ReplaceValueInput(load, offset, 1);
341 NodeProperties::ChangeOp(load, load_stack_op);
344 case IrOpcode::kLoadField: {
345 DCHECK_EQ(FieldAccessOf(load->op()).offset,
346 FixedArray::kLengthOffset);
347 Node* length = NodeProperties::GetValueInput(node, 1);
348 ReplaceWithValue(load, length);
359 Node* NodeHashCache::Query(Node* node) {
360 auto it = cache_.find(node);
361 if (it != cache_.end()) {
368 NodeHashCache::Constructor::Constructor(NodeHashCache* cache,
369 const Operator* op,
int input_count,
370 Node** inputs,
Type type)
371 : node_cache_(cache), from_(nullptr) {
372 if (node_cache_->temp_nodes_.size() > 0) {
373 tmp_ = node_cache_->temp_nodes_.back();
374 node_cache_->temp_nodes_.pop_back();
375 int tmp_input_count = tmp_->InputCount();
376 if (input_count <= tmp_input_count) {
377 tmp_->TrimInputCount(input_count);
379 for (
int i = 0;
i < input_count; ++
i) {
380 if (
i < tmp_input_count) {
381 tmp_->ReplaceInput(
i, inputs[
i]);
383 tmp_->AppendInput(node_cache_->graph_->zone(), inputs[
i]);
386 NodeProperties::ChangeOp(tmp_, op);
388 tmp_ = node_cache_->graph_->NewNode(op, input_count, inputs);
390 NodeProperties::SetType(tmp_, type);
393 Node* NodeHashCache::Constructor::Get() {
394 DCHECK(tmp_ || from_);
397 node = node_cache_->Query(from_);
398 if (!node) node = from_;
400 node = node_cache_->Query(tmp_);
402 node_cache_->temp_nodes_.push_back(tmp_);
405 node_cache_->Insert(node);
408 tmp_ = from_ =
nullptr;
412 Node* NodeHashCache::Constructor::MutableNode() {
413 DCHECK(tmp_ || from_);
415 if (node_cache_->temp_nodes_.empty()) {
416 tmp_ = node_cache_->graph_->CloneNode(from_);
418 tmp_ = node_cache_->temp_nodes_.back();
419 node_cache_->temp_nodes_.pop_back();
420 int from_input_count = from_->InputCount();
421 int tmp_input_count = tmp_->InputCount();
422 if (from_input_count <= tmp_input_count) {
423 tmp_->TrimInputCount(from_input_count);
425 for (
int i = 0;
i < from_input_count; ++
i) {
426 if (
i < tmp_input_count) {
427 tmp_->ReplaceInput(
i, from_->InputAt(
i));
429 tmp_->AppendInput(node_cache_->graph_->zone(), from_->InputAt(
i));
432 NodeProperties::SetType(tmp_, NodeProperties::GetType(from_));
433 NodeProperties::ChangeOp(tmp_, from_->op());