| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 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/bytecode-analysis.h" | 5 #include "src/compiler/bytecode-analysis.h" |
| 6 | 6 |
| 7 #include "src/interpreter/bytecode-array-iterator.h" | 7 #include "src/interpreter/bytecode-array-iterator.h" |
| 8 #include "src/interpreter/bytecode-array-reverse-iterator.h" | 8 #include "src/interpreter/bytecode-array-random-iterator.h" |
| 9 #include "src/objects-inl.h" | 9 #include "src/objects-inl.h" |
| 10 | 10 |
| 11 namespace v8 { | 11 namespace v8 { |
| 12 namespace internal { | 12 namespace internal { |
| 13 namespace compiler { | 13 namespace compiler { |
| 14 | 14 |
| 15 using namespace interpreter; | 15 using namespace interpreter; |
| 16 | 16 |
| 17 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, | 17 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, |
| 18 Zone* zone, bool do_liveness_analysis) | 18 Zone* zone, bool do_liveness_analysis) |
| 19 : bytecode_array_(bytecode_array), | 19 : bytecode_array_(bytecode_array), |
| 20 do_liveness_analysis_(do_liveness_analysis), | 20 do_liveness_analysis_(do_liveness_analysis), |
| 21 zone_(zone), | 21 zone_(zone), |
| 22 loop_stack_(zone), | 22 loop_stack_(zone), |
| 23 loop_end_index_queue_(zone), |
| 23 end_to_header_(zone), | 24 end_to_header_(zone), |
| 24 header_to_parent_(zone), | 25 header_to_parent_(zone), |
| 25 liveness_map_(bytecode_array->length(), zone) {} | 26 liveness_map_(bytecode_array->length(), zone) {} |
| 26 | 27 |
| 27 namespace { | 28 namespace { |
| 28 | 29 |
| 29 void UpdateInLiveness(Bytecode bytecode, BitVector& in_liveness, | 30 void UpdateInLiveness(Bytecode bytecode, BitVector& in_liveness, |
| 30 const BytecodeArrayAccessor& accessor) { | 31 const BytecodeArrayAccessor& accessor) { |
| 31 int num_operands = Bytecodes::NumberOfOperands(bytecode); | 32 int num_operands = Bytecodes::NumberOfOperands(bytecode); |
| 32 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); | 33 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 141 out_liveness.Add(handler_context); | 142 out_liveness.Add(handler_context); |
| 142 } | 143 } |
| 143 } | 144 } |
| 144 } | 145 } |
| 145 | 146 |
| 146 } // namespace | 147 } // namespace |
| 147 | 148 |
| 148 void BytecodeAnalysis::Analyze() { | 149 void BytecodeAnalysis::Analyze() { |
| 149 loop_stack_.push(-1); | 150 loop_stack_.push(-1); |
| 150 | 151 |
| 151 // The last JumpLoop that we haven't done a guaranteed valid liveness pass | 152 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); |
| 152 // over. See the below wall of text for a more thorough explanation. | 153 for (iterator.GoToEnd(); iterator.Valid(); --iterator) { |
| 153 int last_invalid_jumploop_offset = -1; | |
| 154 | |
| 155 BytecodeArrayReverseIterator iterator(bytecode_array(), zone()); | |
| 156 for (; !iterator.done(); iterator.Advance()) { | |
| 157 Bytecode bytecode = iterator.current_bytecode(); | 154 Bytecode bytecode = iterator.current_bytecode(); |
| 158 int current_offset = iterator.current_offset(); | 155 int current_offset = iterator.current_offset(); |
| 159 | 156 |
| 160 if (bytecode == Bytecode::kJumpLoop) { | 157 if (bytecode == Bytecode::kJumpLoop) { |
| 161 PushLoop(iterator.GetJumpTargetOffset(), current_offset); | 158 PushLoop(iterator.GetJumpTargetOffset(), current_offset); |
| 162 | 159 |
| 163 // Save the last offset so that we can do another pass later. | 160 // Save the index so that we can do another pass later. |
| 164 if (last_invalid_jumploop_offset == -1) { | 161 if (do_liveness_analysis_) { |
| 165 last_invalid_jumploop_offset = current_offset; | 162 loop_end_index_queue_.push_back(iterator.current_index()); |
| 166 } | 163 } |
| 167 } else if (current_offset == loop_stack_.top()) { | 164 } else if (current_offset == loop_stack_.top()) { |
| 168 loop_stack_.pop(); | 165 loop_stack_.pop(); |
| 169 } | 166 } |
| 170 | 167 |
| 171 if (do_liveness_analysis_) { | 168 if (do_liveness_analysis_) { |
| 172 // The liveness vector had bits for the liveness of the registers, and one | 169 // The liveness vector had bits for the liveness of the registers, and one |
| 173 // more bit for the liveness of the accumulator. | 170 // more bit for the liveness of the accumulator. |
| 174 Liveness& liveness = liveness_map_.InitializeLiveness( | 171 Liveness& liveness = liveness_map_.InitializeLiveness( |
| 175 current_offset, bytecode_array()->register_count() + 1, zone()); | 172 current_offset, bytecode_array()->register_count() + 1, zone()); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 198 // So, if we know that the liveness of bytecodes after a loop header won't | 195 // So, if we know that the liveness of bytecodes after a loop header won't |
| 199 // change (e.g. because there are no loops in them, or we have already ensured | 196 // change (e.g. because there are no loops in them, or we have already ensured |
| 200 // those loops are valid), we can safely update the loop end and pass over the | 197 // those loops are valid), we can safely update the loop end and pass over the |
| 201 // loop body, and then never have to pass over that loop end again, because we | 198 // loop body, and then never have to pass over that loop end again, because we |
| 202 // have shown that its target, the loop header, can't change from the entries | 199 // have shown that its target, the loop header, can't change from the entries |
| 203 // after the loop, and can't change from any loop body pass. | 200 // after the loop, and can't change from any loop body pass. |
| 204 // | 201 // |
| 205 // This means that in a pass, we can iterate backwards over the bytecode | 202 // This means that in a pass, we can iterate backwards over the bytecode |
| 206 // array, process any loops that we encounter, and on subsequent passes we can | 203 // array, process any loops that we encounter, and on subsequent passes we can |
| 207 // skip processing those loops (though we still have to process inner loops). | 204 // skip processing those loops (though we still have to process inner loops). |
| 205 // |
| 206 // Equivalently, we can queue up loop ends from back to front, and pass over |
| 207 // the loops in that order, as this preserves both the bottom-to-top and |
| 208 // outer-to-inner requirements. |
| 208 | 209 |
| 209 while (last_invalid_jumploop_offset != -1) { | 210 for (int loop_end_index : loop_end_index_queue_) { |
| 210 // TODO(leszeks): We shouldn't need to iterate here, we should just have a | 211 iterator.GoToIndex(loop_end_index); |
| 211 // random access iterator. | |
| 212 iterator.Reset(); | |
| 213 while (last_invalid_jumploop_offset < iterator.current_offset()) { | |
| 214 iterator.Advance(); | |
| 215 } | |
| 216 last_invalid_jumploop_offset = -1; | |
| 217 | 212 |
| 218 DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop); | 213 DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop); |
| 219 | 214 |
| 220 for (; !iterator.done(); iterator.Advance()) { | 215 int header_offset = iterator.GetJumpTargetOffset(); |
| 216 int end_offset = iterator.current_offset(); |
| 217 |
| 218 Liveness& header_liveness = liveness_map_.GetLiveness(header_offset); |
| 219 Liveness& end_liveness = liveness_map_.GetLiveness(end_offset); |
| 220 |
| 221 if (!end_liveness.out->UnionIsChanged(*header_liveness.in)) { |
| 222 // Only update the loop body if the loop end liveness changed. |
| 223 continue; |
| 224 } |
| 225 end_liveness.in->CopyFrom(*end_liveness.out); |
| 226 |
| 227 // Advance into the loop body. |
| 228 --iterator; |
| 229 for (; iterator.current_offset() > header_offset; --iterator) { |
| 221 Bytecode bytecode = iterator.current_bytecode(); | 230 Bytecode bytecode = iterator.current_bytecode(); |
| 222 if (bytecode != Bytecode::kJumpLoop) { | |
| 223 // Skip bytecodes until we hit a JumpLoop. This check isn't needed for | |
| 224 // the first loop we see (thanks to saving its offset), but it is for | |
| 225 // subsequent ones we want to process on this pass. | |
| 226 continue; | |
| 227 } | |
| 228 | 231 |
| 229 int header_offset = iterator.GetJumpTargetOffset(); | 232 int current_offset = iterator.current_offset(); |
| 230 int end_offset = iterator.current_offset(); | 233 Liveness& liveness = liveness_map_.GetLiveness(current_offset); |
| 231 | 234 |
| 232 Liveness& header_liveness = liveness_map_.GetLiveness(header_offset); | 235 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); |
| 233 Liveness& end_liveness = liveness_map_.GetLiveness(end_offset); | 236 liveness.in->CopyFrom(*liveness.out); |
| 234 | 237 UpdateInLiveness(bytecode, *liveness.in, iterator); |
| 235 if (end_liveness.out->UnionIsChanged(*header_liveness.in)) { | |
| 236 // Only update the loop body if the loop end liveness changed. | |
| 237 end_liveness.in->CopyFrom(*end_liveness.out); | |
| 238 | |
| 239 // Advance into the loop body. | |
| 240 iterator.Advance(); | |
| 241 for (; iterator.current_offset() > header_offset; iterator.Advance()) { | |
| 242 bytecode = iterator.current_bytecode(); | |
| 243 if (bytecode == Bytecode::kJumpLoop) { | |
| 244 // We can't validate this loop at the moment because we can't | |
| 245 // guarantee that its header is valid yet. Save it for later. | |
| 246 if (last_invalid_jumploop_offset == -1) { | |
| 247 last_invalid_jumploop_offset = iterator.current_offset(); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 int current_offset = iterator.current_offset(); | |
| 252 Liveness& liveness = liveness_map_.GetLiveness(current_offset); | |
| 253 | |
| 254 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); | |
| 255 liveness.in->CopyFrom(*liveness.out); | |
| 256 UpdateInLiveness(bytecode, *liveness.in, iterator); | |
| 257 } | |
| 258 // Now we are at the loop header. Since the in-liveness of the header | |
| 259 // can't change, we need only to update the out-liveness. | |
| 260 bytecode = iterator.current_bytecode(); | |
| 261 UpdateOutLiveness(bytecode, *header_liveness.out, iterator, | |
| 262 liveness_map_); | |
| 263 } | |
| 264 | |
| 265 // Keep the iterator going so that we can find other loops. | |
| 266 } | 238 } |
| 239 // Now we are at the loop header. Since the in-liveness of the header |
| 240 // can't change, we need only to update the out-liveness. |
| 241 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out, |
| 242 iterator, liveness_map_); |
| 267 } | 243 } |
| 268 | 244 |
| 269 DCHECK(LivenessIsValid()); | 245 DCHECK(LivenessIsValid()); |
| 270 } | 246 } |
| 271 | 247 |
| 272 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { | 248 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { |
| 273 DCHECK(loop_header < loop_end); | 249 DCHECK(loop_header < loop_end); |
| 274 DCHECK(loop_stack_.top() < loop_header); | 250 DCHECK(loop_stack_.top() < loop_header); |
| 275 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); | 251 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); |
| 276 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); | 252 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 356 | 332 |
| 357 os << " | " << current_offset << ": "; | 333 os << " | " << current_offset << ": "; |
| 358 iterator.PrintTo(os) << std::endl; | 334 iterator.PrintTo(os) << std::endl; |
| 359 } | 335 } |
| 360 | 336 |
| 361 return os; | 337 return os; |
| 362 } | 338 } |
| 363 | 339 |
| 364 #if DEBUG | 340 #if DEBUG |
| 365 bool BytecodeAnalysis::LivenessIsValid() { | 341 bool BytecodeAnalysis::LivenessIsValid() { |
| 366 BytecodeArrayReverseIterator iterator(bytecode_array(), zone()); | 342 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); |
| 367 | 343 |
| 368 BitVector previous_liveness(bytecode_array()->register_count() + 1, zone()); | 344 BitVector previous_liveness(bytecode_array()->register_count() + 1, zone()); |
| 369 | 345 |
| 370 int invalid_offset = -1; | 346 int invalid_offset = -1; |
| 371 int which_invalid = -1; | 347 int which_invalid = -1; |
| 372 | 348 |
| 373 // Ensure that there are no liveness changes if we iterate one more time. | 349 // Ensure that there are no liveness changes if we iterate one more time. |
| 374 for (iterator.Reset(); !iterator.done(); iterator.Advance()) { | 350 for (iterator.GoToEnd(); iterator.Valid(); --iterator) { |
| 375 Bytecode bytecode = iterator.current_bytecode(); | 351 Bytecode bytecode = iterator.current_bytecode(); |
| 376 | 352 |
| 377 int current_offset = iterator.current_offset(); | 353 int current_offset = iterator.current_offset(); |
| 378 | 354 |
| 379 Liveness& liveness = liveness_map_.GetLiveness(current_offset); | 355 Liveness& liveness = liveness_map_.GetLiveness(current_offset); |
| 380 | 356 |
| 381 previous_liveness.CopyFrom(*liveness.out); | 357 previous_liveness.CopyFrom(*liveness.out); |
| 382 | 358 |
| 383 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); | 359 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); |
| 384 // UpdateOutLiveness skips kJumpLoop, so we update it manually. | 360 // UpdateOutLiveness skips kJumpLoop, so we update it manually. |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 477 } | 453 } |
| 478 } | 454 } |
| 479 | 455 |
| 480 return invalid_offset == -1; | 456 return invalid_offset == -1; |
| 481 } | 457 } |
| 482 #endif | 458 #endif |
| 483 | 459 |
| 484 } // namespace compiler | 460 } // namespace compiler |
| 485 } // namespace internal | 461 } // namespace internal |
| 486 } // namespace v8 | 462 } // namespace v8 |
| OLD | NEW |