| 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 #ifdef DEBUG |
| 15 #define TRACE(...) \ |
| 16 do { \ |
| 17 if (FLAG_trace_turbo_escape) PrintF(__VA_ARGS__); \ |
| 18 } while (false) |
| 19 #else |
| 20 #define TRACE(...) |
| 21 #endif // DEBUG |
| 22 |
| 14 EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph, | 23 EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph, |
| 15 EscapeAnalysis* escape_analysis, | 24 EscapeAnalysis* escape_analysis, |
| 16 Zone* zone) | 25 Zone* zone) |
| 17 : AdvancedReducer(editor), | 26 : AdvancedReducer(editor), |
| 18 jsgraph_(jsgraph), | 27 jsgraph_(jsgraph), |
| 19 escape_analysis_(escape_analysis), | 28 escape_analysis_(escape_analysis), |
| 20 zone_(zone), | 29 zone_(zone), |
| 21 visited_(static_cast<int>(jsgraph->graph()->NodeCount() * 2), zone), | 30 visited_(static_cast<int>(jsgraph->graph()->NodeCount() * 2), zone), |
| 22 exists_virtual_allocate_(true) {} | 31 exists_virtual_allocate_(true) {} |
| 23 | 32 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 82 | 91 |
| 83 | 92 |
| 84 Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) { | 93 Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) { |
| 85 DCHECK(node->opcode() == IrOpcode::kLoadField || | 94 DCHECK(node->opcode() == IrOpcode::kLoadField || |
| 86 node->opcode() == IrOpcode::kLoadElement); | 95 node->opcode() == IrOpcode::kLoadElement); |
| 87 if (visited_.Contains(node->id())) return NoChange(); | 96 if (visited_.Contains(node->id())) return NoChange(); |
| 88 visited_.Add(node->id()); | 97 visited_.Add(node->id()); |
| 89 if (Node* rep = escape_analysis()->GetReplacement(node)) { | 98 if (Node* rep = escape_analysis()->GetReplacement(node)) { |
| 90 visited_.Add(node->id()); | 99 visited_.Add(node->id()); |
| 91 counters()->turbo_escape_loads_replaced()->Increment(); | 100 counters()->turbo_escape_loads_replaced()->Increment(); |
| 92 if (FLAG_trace_turbo_escape) { | 101 TRACE("Replaced #%d (%s) with #%d (%s)\n", node->id(), |
| 93 PrintF("Replaced #%d (%s) with #%d (%s)\n", node->id(), | 102 node->op()->mnemonic(), rep->id(), rep->op()->mnemonic()); |
| 94 node->op()->mnemonic(), rep->id(), rep->op()->mnemonic()); | |
| 95 } | |
| 96 ReplaceWithValue(node, rep); | 103 ReplaceWithValue(node, rep); |
| 97 return Changed(rep); | 104 return Changed(rep); |
| 98 } | 105 } |
| 99 return NoChange(); | 106 return NoChange(); |
| 100 } | 107 } |
| 101 | 108 |
| 102 | 109 |
| 103 Reduction EscapeAnalysisReducer::ReduceStore(Node* node) { | 110 Reduction EscapeAnalysisReducer::ReduceStore(Node* node) { |
| 104 DCHECK(node->opcode() == IrOpcode::kStoreField || | 111 DCHECK(node->opcode() == IrOpcode::kStoreField || |
| 105 node->opcode() == IrOpcode::kStoreElement); | 112 node->opcode() == IrOpcode::kStoreElement); |
| 106 if (visited_.Contains(node->id())) return NoChange(); | 113 if (visited_.Contains(node->id())) return NoChange(); |
| 107 visited_.Add(node->id()); | 114 visited_.Add(node->id()); |
| 108 if (escape_analysis()->IsVirtual(NodeProperties::GetValueInput(node, 0))) { | 115 if (escape_analysis()->IsVirtual(NodeProperties::GetValueInput(node, 0))) { |
| 109 if (FLAG_trace_turbo_escape) { | 116 TRACE("Removed #%d (%s) from effect chain\n", node->id(), |
| 110 PrintF("Removed #%d (%s) from effect chain\n", node->id(), | 117 node->op()->mnemonic()); |
| 111 node->op()->mnemonic()); | |
| 112 } | |
| 113 RelaxEffectsAndControls(node); | 118 RelaxEffectsAndControls(node); |
| 114 return Changed(node); | 119 return Changed(node); |
| 115 } | 120 } |
| 116 return NoChange(); | 121 return NoChange(); |
| 117 } | 122 } |
| 118 | 123 |
| 119 | 124 |
| 120 Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) { | 125 Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) { |
| 121 DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); | 126 DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); |
| 122 if (visited_.Contains(node->id())) return NoChange(); | 127 if (visited_.Contains(node->id())) return NoChange(); |
| 123 visited_.Add(node->id()); | 128 visited_.Add(node->id()); |
| 124 if (escape_analysis()->IsVirtual(node)) { | 129 if (escape_analysis()->IsVirtual(node)) { |
| 125 RelaxEffectsAndControls(node); | 130 RelaxEffectsAndControls(node); |
| 126 counters()->turbo_escape_allocs_replaced()->Increment(); | 131 counters()->turbo_escape_allocs_replaced()->Increment(); |
| 127 if (FLAG_trace_turbo_escape) { | 132 TRACE("Removed allocate #%d from effect chain\n", node->id()); |
| 128 PrintF("Removed allocate #%d from effect chain\n", node->id()); | |
| 129 } | |
| 130 return Changed(node); | 133 return Changed(node); |
| 131 } | 134 } |
| 132 return NoChange(); | 135 return NoChange(); |
| 133 } | 136 } |
| 134 | 137 |
| 135 | 138 |
| 136 Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) { | 139 Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) { |
| 137 DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion); | 140 DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion); |
| 138 Node* effect = NodeProperties::GetEffectInput(node, 0); | 141 Node* effect = NodeProperties::GetEffectInput(node, 0); |
| 139 if (effect->opcode() == IrOpcode::kBeginRegion) { | 142 if (effect->opcode() == IrOpcode::kBeginRegion) { |
| 140 RelaxEffectsAndControls(effect); | 143 RelaxEffectsAndControls(effect); |
| 141 RelaxEffectsAndControls(node); | 144 RelaxEffectsAndControls(node); |
| 145 #ifdef DEBUG |
| 142 if (FLAG_trace_turbo_escape) { | 146 if (FLAG_trace_turbo_escape) { |
| 143 PrintF("Removed region #%d / #%d from effect chain,", effect->id(), | 147 PrintF("Removed region #%d / #%d from effect chain,", effect->id(), |
| 144 node->id()); | 148 node->id()); |
| 145 PrintF(" %d user(s) of #%d remain(s):", node->UseCount(), node->id()); | 149 PrintF(" %d user(s) of #%d remain(s):", node->UseCount(), node->id()); |
| 146 for (Edge edge : node->use_edges()) { | 150 for (Edge edge : node->use_edges()) { |
| 147 PrintF(" #%d", edge.from()->id()); | 151 PrintF(" #%d", edge.from()->id()); |
| 148 } | 152 } |
| 149 PrintF("\n"); | 153 PrintF("\n"); |
| 150 } | 154 } |
| 155 #endif // DEBUG |
| 151 return Changed(node); | 156 return Changed(node); |
| 152 } | 157 } |
| 153 return NoChange(); | 158 return NoChange(); |
| 154 } | 159 } |
| 155 | 160 |
| 156 | 161 |
| 157 Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) { | 162 Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) { |
| 158 DCHECK_EQ(node->opcode(), IrOpcode::kReferenceEqual); | 163 DCHECK_EQ(node->opcode(), IrOpcode::kReferenceEqual); |
| 159 Node* left = NodeProperties::GetValueInput(node, 0); | 164 Node* left = NodeProperties::GetValueInput(node, 0); |
| 160 Node* right = NodeProperties::GetValueInput(node, 1); | 165 Node* right = NodeProperties::GetValueInput(node, 1); |
| 161 if (escape_analysis()->IsVirtual(left)) { | 166 if (escape_analysis()->IsVirtual(left)) { |
| 162 if (escape_analysis()->IsVirtual(right) && | 167 if (escape_analysis()->IsVirtual(right) && |
| 163 escape_analysis()->CompareVirtualObjects(left, right)) { | 168 escape_analysis()->CompareVirtualObjects(left, right)) { |
| 164 ReplaceWithValue(node, jsgraph()->TrueConstant()); | 169 ReplaceWithValue(node, jsgraph()->TrueConstant()); |
| 165 if (FLAG_trace_turbo_escape) { | 170 TRACE("Replaced ref eq #%d with true\n", node->id()); |
| 166 PrintF("Replaced ref eq #%d with true\n", node->id()); | |
| 167 } | |
| 168 } | 171 } |
| 169 // Right-hand side is not a virtual object, or a different one. | 172 // Right-hand side is not a virtual object, or a different one. |
| 170 ReplaceWithValue(node, jsgraph()->FalseConstant()); | 173 ReplaceWithValue(node, jsgraph()->FalseConstant()); |
| 171 if (FLAG_trace_turbo_escape) { | 174 TRACE("Replaced ref eq #%d with false\n", node->id()); |
| 172 PrintF("Replaced ref eq #%d with false\n", node->id()); | |
| 173 } | |
| 174 return Replace(node); | 175 return Replace(node); |
| 175 } else if (escape_analysis()->IsVirtual(right)) { | 176 } else if (escape_analysis()->IsVirtual(right)) { |
| 176 // Left-hand side is not a virtual object. | 177 // Left-hand side is not a virtual object. |
| 177 ReplaceWithValue(node, jsgraph()->FalseConstant()); | 178 ReplaceWithValue(node, jsgraph()->FalseConstant()); |
| 178 if (FLAG_trace_turbo_escape) { | 179 TRACE("Replaced ref eq #%d with false\n", node->id()); |
| 179 PrintF("Replaced ref eq #%d with false\n", node->id()); | |
| 180 } | |
| 181 } | 180 } |
| 182 return NoChange(); | 181 return NoChange(); |
| 183 } | 182 } |
| 184 | 183 |
| 185 | 184 |
| 186 Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) { | 185 Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) { |
| 187 DCHECK_EQ(node->opcode(), IrOpcode::kObjectIsSmi); | 186 DCHECK_EQ(node->opcode(), IrOpcode::kObjectIsSmi); |
| 188 Node* input = NodeProperties::GetValueInput(node, 0); | 187 Node* input = NodeProperties::GetValueInput(node, 0); |
| 189 if (escape_analysis()->IsVirtual(input)) { | 188 if (escape_analysis()->IsVirtual(input)) { |
| 190 ReplaceWithValue(node, jsgraph()->FalseConstant()); | 189 ReplaceWithValue(node, jsgraph()->FalseConstant()); |
| 191 if (FLAG_trace_turbo_escape) { | 190 TRACE("Replaced ObjectIsSmi #%d with false\n", node->id()); |
| 192 PrintF("Replaced ObjectIsSmi #%d with false\n", node->id()); | |
| 193 } | |
| 194 return Replace(node); | 191 return Replace(node); |
| 195 } | 192 } |
| 196 return NoChange(); | 193 return NoChange(); |
| 197 } | 194 } |
| 198 | 195 |
| 199 | 196 |
| 200 Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) { | 197 Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) { |
| 201 if (visited_.Contains(node->id())) return NoChange(); | 198 if (visited_.Contains(node->id())) return NoChange(); |
| 202 visited_.Add(node->id()); | 199 visited_.Add(node->id()); |
| 203 DCHECK_GE(node->op()->EffectInputCount(), 1); | 200 DCHECK_GE(node->op()->EffectInputCount(), 1); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 220 | 217 |
| 221 // Returns the clone if it duplicated the node, and null otherwise. | 218 // Returns the clone if it duplicated the node, and null otherwise. |
| 222 Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect, | 219 Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect, |
| 223 bool multiple_users) { | 220 bool multiple_users) { |
| 224 DCHECK(node->opcode() == IrOpcode::kFrameState || | 221 DCHECK(node->opcode() == IrOpcode::kFrameState || |
| 225 node->opcode() == IrOpcode::kStateValues); | 222 node->opcode() == IrOpcode::kStateValues); |
| 226 if (node->id() < static_cast<NodeId>(visited_.length()) && | 223 if (node->id() < static_cast<NodeId>(visited_.length()) && |
| 227 visited_.Contains(node->id())) { | 224 visited_.Contains(node->id())) { |
| 228 return nullptr; | 225 return nullptr; |
| 229 } | 226 } |
| 230 if (FLAG_trace_turbo_escape) { | 227 TRACE("Reducing %s %d\n", node->op()->mnemonic(), node->id()); |
| 231 PrintF("Reducing %s %d\n", node->op()->mnemonic(), node->id()); | |
| 232 } | |
| 233 Node* clone = nullptr; | 228 Node* clone = nullptr; |
| 234 bool node_multiused = node->UseCount() > 1; | 229 bool node_multiused = node->UseCount() > 1; |
| 235 bool multiple_users_rec = multiple_users || node_multiused; | 230 bool multiple_users_rec = multiple_users || node_multiused; |
| 236 for (int i = 0; i < node->op()->ValueInputCount(); ++i) { | 231 for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
| 237 Node* input = NodeProperties::GetValueInput(node, i); | 232 Node* input = NodeProperties::GetValueInput(node, i); |
| 238 if (input->opcode() == IrOpcode::kStateValues) { | 233 if (input->opcode() == IrOpcode::kStateValues) { |
| 239 if (Node* ret = ReduceDeoptState(input, effect, multiple_users_rec)) { | 234 if (Node* ret = ReduceDeoptState(input, effect, multiple_users_rec)) { |
| 240 if (node_multiused || (multiple_users && !clone)) { | 235 if (node_multiused || (multiple_users && !clone)) { |
| 241 if (FLAG_trace_turbo_escape) { | 236 TRACE(" Cloning #%d", node->id()); |
| 242 PrintF(" Cloning #%d", node->id()); | |
| 243 } | |
| 244 node = clone = jsgraph()->graph()->CloneNode(node); | 237 node = clone = jsgraph()->graph()->CloneNode(node); |
| 245 if (FLAG_trace_turbo_escape) { | 238 TRACE(" to #%d\n", node->id()); |
| 246 PrintF(" to #%d\n", node->id()); | |
| 247 } | |
| 248 node_multiused = false; | 239 node_multiused = false; |
| 249 } | 240 } |
| 250 NodeProperties::ReplaceValueInput(node, ret, i); | 241 NodeProperties::ReplaceValueInput(node, ret, i); |
| 251 } | 242 } |
| 252 } else { | 243 } else { |
| 253 if (Node* ret = ReduceStateValueInput(node, i, effect, node_multiused, | 244 if (Node* ret = ReduceStateValueInput(node, i, effect, node_multiused, |
| 254 clone, multiple_users)) { | 245 clone, multiple_users)) { |
| 255 DCHECK_NULL(clone); | 246 DCHECK_NULL(clone); |
| 256 node_multiused = false; // Don't clone anymore. | 247 node_multiused = false; // Don't clone anymore. |
| 257 node = clone = ret; | 248 node = clone = ret; |
| 258 } | 249 } |
| 259 } | 250 } |
| 260 } | 251 } |
| 261 if (node->opcode() == IrOpcode::kFrameState) { | 252 if (node->opcode() == IrOpcode::kFrameState) { |
| 262 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node, 0); | 253 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| 263 if (outer_frame_state->opcode() == IrOpcode::kFrameState) { | 254 if (outer_frame_state->opcode() == IrOpcode::kFrameState) { |
| 264 if (Node* ret = | 255 if (Node* ret = |
| 265 ReduceDeoptState(outer_frame_state, effect, multiple_users_rec)) { | 256 ReduceDeoptState(outer_frame_state, effect, multiple_users_rec)) { |
| 266 if (node_multiused || (multiple_users && !clone)) { | 257 if (node_multiused || (multiple_users && !clone)) { |
| 267 if (FLAG_trace_turbo_escape) { | 258 TRACE(" Cloning #%d", node->id()); |
| 268 PrintF(" Cloning #%d", node->id()); | |
| 269 } | |
| 270 node = clone = jsgraph()->graph()->CloneNode(node); | 259 node = clone = jsgraph()->graph()->CloneNode(node); |
| 271 if (FLAG_trace_turbo_escape) { | 260 TRACE(" to #%d\n", node->id()); |
| 272 PrintF(" to #%d\n", node->id()); | |
| 273 } | |
| 274 } | 261 } |
| 275 NodeProperties::ReplaceFrameStateInput(node, 0, ret); | 262 NodeProperties::ReplaceFrameStateInput(node, 0, ret); |
| 276 } | 263 } |
| 277 } | 264 } |
| 278 } | 265 } |
| 279 return clone; | 266 return clone; |
| 280 } | 267 } |
| 281 | 268 |
| 282 | 269 |
| 283 // Returns the clone if it duplicated the node, and null otherwise. | 270 // Returns the clone if it duplicated the node, and null otherwise. |
| 284 Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index, | 271 Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index, |
| 285 Node* effect, | 272 Node* effect, |
| 286 bool node_multiused, | 273 bool node_multiused, |
| 287 bool already_cloned, | 274 bool already_cloned, |
| 288 bool multiple_users) { | 275 bool multiple_users) { |
| 289 Node* input = NodeProperties::GetValueInput(node, node_index); | 276 Node* input = NodeProperties::GetValueInput(node, node_index); |
| 290 if (FLAG_trace_turbo_escape) { | 277 TRACE("Reducing State Input #%d (%s)\n", input->id(), |
| 291 PrintF("Reducing State Input #%d (%s)\n", input->id(), | 278 input->op()->mnemonic()); |
| 292 input->op()->mnemonic()); | |
| 293 } | |
| 294 Node* clone = nullptr; | 279 Node* clone = nullptr; |
| 295 if (input->opcode() == IrOpcode::kFinishRegion || | 280 if (input->opcode() == IrOpcode::kFinishRegion || |
| 296 input->opcode() == IrOpcode::kAllocate) { | 281 input->opcode() == IrOpcode::kAllocate) { |
| 297 if (escape_analysis()->IsVirtual(input)) { | 282 if (escape_analysis()->IsVirtual(input)) { |
| 298 if (Node* object_state = | 283 if (Node* object_state = |
| 299 escape_analysis()->GetOrCreateObjectState(effect, input)) { | 284 escape_analysis()->GetOrCreateObjectState(effect, input)) { |
| 300 if (node_multiused || (multiple_users && !already_cloned)) { | 285 if (node_multiused || (multiple_users && !already_cloned)) { |
| 301 if (FLAG_trace_turbo_escape) { | 286 TRACE("Cloning #%d", node->id()); |
| 302 PrintF("Cloning #%d", node->id()); | |
| 303 } | |
| 304 node = clone = jsgraph()->graph()->CloneNode(node); | 287 node = clone = jsgraph()->graph()->CloneNode(node); |
| 305 if (FLAG_trace_turbo_escape) { | 288 TRACE(" to #%d\n", node->id()); |
| 306 PrintF(" to #%d\n", node->id()); | |
| 307 } | |
| 308 node_multiused = false; | 289 node_multiused = false; |
| 309 already_cloned = true; | 290 already_cloned = true; |
| 310 } | 291 } |
| 311 NodeProperties::ReplaceValueInput(node, object_state, node_index); | 292 NodeProperties::ReplaceValueInput(node, object_state, node_index); |
| 312 if (FLAG_trace_turbo_escape) { | 293 TRACE("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()); | |
| 315 } | |
| 316 } else { | 295 } else { |
| 317 if (FLAG_trace_turbo_escape) { | 296 TRACE("No object state replacement for #%d at effect #%d available.\n", |
| 318 PrintF("No object state replacement available.\n"); | 297 input->id(), effect->id()); |
| 319 } | 298 UNREACHABLE(); |
| 320 } | 299 } |
| 321 } | 300 } |
| 322 } | 301 } |
| 323 return clone; | 302 return clone; |
| 324 } | 303 } |
| 325 | 304 |
| 326 | 305 |
| 327 Counters* EscapeAnalysisReducer::counters() const { | 306 Counters* EscapeAnalysisReducer::counters() const { |
| 328 return jsgraph_->isolate()->counters(); | 307 return jsgraph_->isolate()->counters(); |
| 329 } | 308 } |
| 330 | 309 |
| 331 } // namespace compiler | 310 } // namespace compiler |
| 332 } // namespace internal | 311 } // namespace internal |
| 333 } // namespace v8 | 312 } // namespace v8 |
| OLD | NEW |