| OLD | NEW |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/escape-analysis-reducer.h" | 5 #include "src/compiler/escape-analysis-reducer.h" |
| 6 | 6 |
| 7 #include "src/compiler/js-graph.h" | 7 #include "src/compiler/js-graph.h" |
| 8 #include "src/counters.h" | 8 #include "src/counters.h" |
| 9 | 9 |
| 10 namespace v8 { | 10 namespace v8 { |
| 11 namespace internal { | 11 namespace internal { |
| 12 namespace compiler { | 12 namespace compiler { |
| 13 | 13 |
| 14 EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph, | 14 EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph, |
| 15 EscapeAnalysis* escape_analysis, | 15 EscapeAnalysis* escape_analysis, |
| 16 Zone* zone) | 16 Zone* zone) |
| 17 : AdvancedReducer(editor), | 17 : AdvancedReducer(editor), |
| 18 jsgraph_(jsgraph), | 18 jsgraph_(jsgraph), |
| 19 escape_analysis_(escape_analysis), | 19 escape_analysis_(escape_analysis), |
| 20 zone_(zone), | 20 zone_(zone), |
| 21 visited_(static_cast<int>(jsgraph->graph()->NodeCount()), zone) {} | 21 visited_(static_cast<int>(jsgraph->graph()->NodeCount() * 2), zone), |
| 22 exists_virtual_allocate_(true) {} |
| 22 | 23 |
| 23 | 24 |
| 24 Reduction EscapeAnalysisReducer::Reduce(Node* node) { | 25 Reduction EscapeAnalysisReducer::Reduce(Node* node) { |
| 25 switch (node->opcode()) { | 26 switch (node->opcode()) { |
| 26 case IrOpcode::kLoadField: | 27 case IrOpcode::kLoadField: |
| 27 case IrOpcode::kLoadElement: | 28 case IrOpcode::kLoadElement: |
| 28 return ReduceLoad(node); | 29 return ReduceLoad(node); |
| 29 case IrOpcode::kStoreField: | 30 case IrOpcode::kStoreField: |
| 30 case IrOpcode::kStoreElement: | 31 case IrOpcode::kStoreElement: |
| 31 return ReduceStore(node); | 32 return ReduceStore(node); |
| 32 case IrOpcode::kAllocate: | 33 case IrOpcode::kAllocate: |
| 33 return ReduceAllocate(node); | 34 return ReduceAllocate(node); |
| 34 case IrOpcode::kFinishRegion: | 35 case IrOpcode::kFinishRegion: |
| 35 return ReduceFinishRegion(node); | 36 return ReduceFinishRegion(node); |
| 36 case IrOpcode::kReferenceEqual: | 37 case IrOpcode::kReferenceEqual: |
| 37 return ReduceReferenceEqual(node); | 38 return ReduceReferenceEqual(node); |
| 38 case IrOpcode::kObjectIsSmi: | 39 case IrOpcode::kObjectIsSmi: |
| 39 return ReduceObjectIsSmi(node); | 40 return ReduceObjectIsSmi(node); |
| 41 case IrOpcode::kFrameState: |
| 42 case IrOpcode::kStateValues: { |
| 43 if (node->id() >= static_cast<NodeId>(visited_.length()) || |
| 44 visited_.Contains(node->id())) { |
| 45 break; |
| 46 } |
| 47 bool needs_visit = false; |
| 48 for (int i = 0; i < node->InputCount(); i++) { |
| 49 Node* input = node->InputAt(i); |
| 50 switch (input->opcode()) { |
| 51 case IrOpcode::kAllocate: |
| 52 case IrOpcode::kFinishRegion: |
| 53 needs_visit = needs_visit || escape_analysis()->IsVirtual(input); |
| 54 break; |
| 55 case IrOpcode::kFrameState: |
| 56 case IrOpcode::kStateValues: |
| 57 needs_visit = |
| 58 needs_visit || |
| 59 input->id() >= static_cast<NodeId>(visited_.length()) || |
| 60 !visited_.Contains(input->id()); |
| 61 break; |
| 62 default: |
| 63 break; |
| 64 } |
| 65 } |
| 66 if (!needs_visit) { |
| 67 visited_.Add(node->id()); |
| 68 } |
| 69 break; |
| 70 } |
| 40 default: | 71 default: |
| 41 // TODO(sigurds): Change this to GetFrameStateInputCount once | 72 // TODO(sigurds): Change this to GetFrameStateInputCount once |
| 42 // it is working. For now we use EffectInputCount > 0 to determine | 73 // it is working. For now we use EffectInputCount > 0 to determine |
| 43 // whether a node might have a frame state input. | 74 // whether a node might have a frame state input. |
| 44 if (node->op()->EffectInputCount() > 0) { | 75 if (exists_virtual_allocate_ && node->op()->EffectInputCount() > 0) { |
| 45 return ReduceFrameStateUses(node); | 76 return ReduceFrameStateUses(node); |
| 46 } | 77 } |
| 47 break; | 78 break; |
| 48 } | 79 } |
| 49 return NoChange(); | 80 return NoChange(); |
| 50 } | 81 } |
| 51 | 82 |
| 52 | 83 |
| 53 Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) { | 84 Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) { |
| 54 DCHECK(node->opcode() == IrOpcode::kLoadField || | 85 DCHECK(node->opcode() == IrOpcode::kLoadField || |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 | 198 |
| 168 | 199 |
| 169 Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) { | 200 Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) { |
| 170 if (visited_.Contains(node->id())) return NoChange(); | 201 if (visited_.Contains(node->id())) return NoChange(); |
| 171 visited_.Add(node->id()); | 202 visited_.Add(node->id()); |
| 172 DCHECK_GE(node->op()->EffectInputCount(), 1); | 203 DCHECK_GE(node->op()->EffectInputCount(), 1); |
| 173 bool changed = false; | 204 bool changed = false; |
| 174 for (int i = 0; i < node->InputCount(); ++i) { | 205 for (int i = 0; i < node->InputCount(); ++i) { |
| 175 Node* input = node->InputAt(i); | 206 Node* input = node->InputAt(i); |
| 176 if (input->opcode() == IrOpcode::kFrameState) { | 207 if (input->opcode() == IrOpcode::kFrameState) { |
| 177 if (Node* ret = ReduceFrameState(input, node, false)) { | 208 if (Node* ret = ReduceDeoptState(input, node, false)) { |
| 178 node->ReplaceInput(i, ret); | 209 node->ReplaceInput(i, ret); |
| 179 changed = true; | 210 changed = true; |
| 180 } | 211 } |
| 181 } | 212 } |
| 182 } | 213 } |
| 183 if (changed) { | 214 if (changed) { |
| 184 return Changed(node); | 215 return Changed(node); |
| 185 } | 216 } |
| 186 return NoChange(); | 217 return NoChange(); |
| 187 } | 218 } |
| 188 | 219 |
| 189 | 220 |
| 190 // Returns the clone if it duplicated the node, and null otherwise. | 221 // Returns the clone if it duplicated the node, and null otherwise. |
| 191 Node* EscapeAnalysisReducer::ReduceFrameState(Node* node, Node* effect, | 222 Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect, |
| 192 bool multiple_users) { | 223 bool multiple_users) { |
| 193 DCHECK(node->opcode() == IrOpcode::kFrameState); | 224 DCHECK(node->opcode() == IrOpcode::kFrameState || |
| 225 node->opcode() == IrOpcode::kStateValues); |
| 226 if (node->id() < static_cast<NodeId>(visited_.length()) && |
| 227 visited_.Contains(node->id())) { |
| 228 return nullptr; |
| 229 } |
| 194 if (FLAG_trace_turbo_escape) { | 230 if (FLAG_trace_turbo_escape) { |
| 195 PrintF("Reducing FrameState %d\n", node->id()); | 231 PrintF("Reducing %s %d\n", node->op()->mnemonic(), node->id()); |
| 196 } | 232 } |
| 197 Node* clone = nullptr; | 233 Node* clone = nullptr; |
| 234 bool node_multiused = node->UseCount() > 1; |
| 235 bool multiple_users_rec = multiple_users || node_multiused; |
| 198 for (int i = 0; i < node->op()->ValueInputCount(); ++i) { | 236 for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
| 199 Node* input = NodeProperties::GetValueInput(node, i); | 237 Node* input = NodeProperties::GetValueInput(node, i); |
| 200 Node* ret = | 238 if (input->opcode() == IrOpcode::kStateValues) { |
| 201 input->opcode() == IrOpcode::kStateValues | 239 if (Node* ret = ReduceDeoptState(input, effect, multiple_users_rec)) { |
| 202 ? ReduceStateValueInputs(input, effect, node->UseCount() > 1) | 240 if (node_multiused || (multiple_users && !clone)) { |
| 203 : ReduceStateValueInput(node, i, effect, node->UseCount() > 1); | 241 if (FLAG_trace_turbo_escape) { |
| 204 if (ret) { | 242 PrintF(" Cloning #%d", node->id()); |
| 205 if (node->UseCount() > 1 || multiple_users) { | 243 } |
| 206 if (FLAG_trace_turbo_escape) { | 244 node = clone = jsgraph()->graph()->CloneNode(node); |
| 207 PrintF(" Cloning #%d", node->id()); | 245 if (FLAG_trace_turbo_escape) { |
| 246 PrintF(" to #%d\n", node->id()); |
| 247 } |
| 248 node_multiused = false; |
| 208 } | 249 } |
| 209 node = clone = jsgraph()->graph()->CloneNode(node); | 250 NodeProperties::ReplaceValueInput(node, ret, i); |
| 210 if (FLAG_trace_turbo_escape) { | |
| 211 PrintF(" to #%d\n", node->id()); | |
| 212 } | |
| 213 multiple_users = false; // Don't clone anymore. | |
| 214 } | 251 } |
| 215 NodeProperties::ReplaceValueInput(node, ret, i); | 252 } else { |
| 253 if (Node* ret = ReduceStateValueInput(node, i, effect, node_multiused, |
| 254 clone, multiple_users)) { |
| 255 DCHECK_NULL(clone); |
| 256 node_multiused = false; // Don't clone anymore. |
| 257 node = clone = ret; |
| 258 } |
| 216 } | 259 } |
| 217 } | 260 } |
| 218 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node, 0); | 261 if (node->opcode() == IrOpcode::kFrameState) { |
| 219 if (outer_frame_state->opcode() == IrOpcode::kFrameState) { | 262 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| 220 if (Node* ret = | 263 if (outer_frame_state->opcode() == IrOpcode::kFrameState) { |
| 221 ReduceFrameState(outer_frame_state, effect, node->UseCount() > 1)) { | 264 if (Node* ret = |
| 222 if (node->UseCount() > 1 || multiple_users) { | 265 ReduceDeoptState(outer_frame_state, effect, multiple_users_rec)) { |
| 223 if (FLAG_trace_turbo_escape) { | 266 if (node_multiused || (multiple_users && !clone)) { |
| 224 PrintF(" Cloning #%d", node->id()); | 267 if (FLAG_trace_turbo_escape) { |
| 268 PrintF(" Cloning #%d", node->id()); |
| 269 } |
| 270 node = clone = jsgraph()->graph()->CloneNode(node); |
| 271 if (FLAG_trace_turbo_escape) { |
| 272 PrintF(" to #%d\n", node->id()); |
| 273 } |
| 225 } | 274 } |
| 226 node = clone = jsgraph()->graph()->CloneNode(node); | 275 NodeProperties::ReplaceFrameStateInput(node, 0, ret); |
| 227 if (FLAG_trace_turbo_escape) { | |
| 228 PrintF(" to #%d\n", node->id()); | |
| 229 } | |
| 230 multiple_users = false; | |
| 231 } | 276 } |
| 232 NodeProperties::ReplaceFrameStateInput(node, 0, ret); | |
| 233 } | 277 } |
| 234 } | 278 } |
| 235 return clone; | 279 return clone; |
| 236 } | |
| 237 | |
| 238 | |
| 239 // Returns the clone if it duplicated the node, and null otherwise. | |
| 240 Node* EscapeAnalysisReducer::ReduceStateValueInputs(Node* node, Node* effect, | |
| 241 bool multiple_users) { | |
| 242 if (FLAG_trace_turbo_escape) { | |
| 243 PrintF("Reducing StateValue #%d\n", node->id()); | |
| 244 } | |
| 245 DCHECK(node->opcode() == IrOpcode::kStateValues); | |
| 246 DCHECK_NOT_NULL(effect); | |
| 247 Node* clone = nullptr; | |
| 248 for (int i = 0; i < node->op()->ValueInputCount(); ++i) { | |
| 249 Node* input = NodeProperties::GetValueInput(node, i); | |
| 250 Node* ret = nullptr; | |
| 251 if (input->opcode() == IrOpcode::kStateValues) { | |
| 252 ret = ReduceStateValueInputs(input, effect, multiple_users); | |
| 253 } else { | |
| 254 ret = ReduceStateValueInput(node, i, effect, multiple_users); | |
| 255 } | |
| 256 if (ret) { | |
| 257 node = ret; | |
| 258 DCHECK_NULL(clone); | |
| 259 clone = ret; | |
| 260 multiple_users = false; | |
| 261 } | |
| 262 } | |
| 263 return clone; | |
| 264 } | 280 } |
| 265 | 281 |
| 266 | 282 |
| 267 // Returns the clone if it duplicated the node, and null otherwise. | 283 // Returns the clone if it duplicated the node, and null otherwise. |
| 268 Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index, | 284 Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index, |
| 269 Node* effect, | 285 Node* effect, |
| 286 bool node_multiused, |
| 287 bool already_cloned, |
| 270 bool multiple_users) { | 288 bool multiple_users) { |
| 271 Node* input = NodeProperties::GetValueInput(node, node_index); | 289 Node* input = NodeProperties::GetValueInput(node, node_index); |
| 272 if (FLAG_trace_turbo_escape) { | 290 if (FLAG_trace_turbo_escape) { |
| 273 PrintF("Reducing State Input #%d (%s)\n", input->id(), | 291 PrintF("Reducing State Input #%d (%s)\n", input->id(), |
| 274 input->op()->mnemonic()); | 292 input->op()->mnemonic()); |
| 275 } | 293 } |
| 276 Node* clone = nullptr; | 294 Node* clone = nullptr; |
| 277 if (input->opcode() == IrOpcode::kFinishRegion || | 295 if (input->opcode() == IrOpcode::kFinishRegion || |
| 278 input->opcode() == IrOpcode::kAllocate) { | 296 input->opcode() == IrOpcode::kAllocate) { |
| 279 if (escape_analysis()->IsVirtual(input)) { | 297 if (escape_analysis()->IsVirtual(input)) { |
| 280 if (Node* object_state = | 298 if (Node* object_state = |
| 281 escape_analysis()->GetOrCreateObjectState(effect, input)) { | 299 escape_analysis()->GetOrCreateObjectState(effect, input)) { |
| 282 if (node->UseCount() > 1 || multiple_users) { | 300 if (node_multiused || (multiple_users && !already_cloned)) { |
| 283 if (FLAG_trace_turbo_escape) { | 301 if (FLAG_trace_turbo_escape) { |
| 284 PrintF("Cloning #%d", node->id()); | 302 PrintF("Cloning #%d", node->id()); |
| 285 } | 303 } |
| 286 node = clone = jsgraph()->graph()->CloneNode(node); | 304 node = clone = jsgraph()->graph()->CloneNode(node); |
| 287 if (FLAG_trace_turbo_escape) { | 305 if (FLAG_trace_turbo_escape) { |
| 288 PrintF(" to #%d\n", node->id()); | 306 PrintF(" to #%d\n", node->id()); |
| 289 } | 307 } |
| 308 node_multiused = false; |
| 309 already_cloned = true; |
| 290 } | 310 } |
| 291 NodeProperties::ReplaceValueInput(node, object_state, node_index); | 311 NodeProperties::ReplaceValueInput(node, object_state, node_index); |
| 292 if (FLAG_trace_turbo_escape) { | 312 if (FLAG_trace_turbo_escape) { |
| 293 PrintF("Replaced state #%d input #%d with object state #%d\n", | 313 PrintF("Replaced state #%d input #%d with object state #%d\n", |
| 294 node->id(), input->id(), object_state->id()); | 314 node->id(), input->id(), object_state->id()); |
| 295 } | 315 } |
| 296 } else { | 316 } else { |
| 297 if (FLAG_trace_turbo_escape) { | 317 if (FLAG_trace_turbo_escape) { |
| 298 PrintF("No object state replacement available.\n"); | 318 PrintF("No object state replacement available.\n"); |
| 299 } | 319 } |
| 300 } | 320 } |
| 301 } | 321 } |
| 302 } | 322 } |
| 303 return clone; | 323 return clone; |
| 304 } | 324 } |
| 305 | 325 |
| 306 | 326 |
| 307 Counters* EscapeAnalysisReducer::counters() const { | 327 Counters* EscapeAnalysisReducer::counters() const { |
| 308 return jsgraph_->isolate()->counters(); | 328 return jsgraph_->isolate()->counters(); |
| 309 } | 329 } |
| 310 | 330 |
| 311 } // namespace compiler | 331 } // namespace compiler |
| 312 } // namespace internal | 332 } // namespace internal |
| 313 } // namespace v8 | 333 } // namespace v8 |
| OLD | NEW |