Chromium Code Reviews| 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/compiler/bytecode-analysis-liveness.h" | |
| 8 #include "src/interpreter/bytecode-array-iterator.h" | |
| 7 #include "src/interpreter/bytecode-array-reverse-iterator.h" | 9 #include "src/interpreter/bytecode-array-reverse-iterator.h" |
| 8 #include "src/objects-inl.h" | 10 #include "src/objects-inl.h" |
| 9 | 11 |
| 10 namespace v8 { | 12 namespace v8 { |
| 11 namespace internal { | 13 namespace internal { |
| 12 namespace compiler { | 14 namespace compiler { |
| 13 | 15 |
| 16 using namespace interpreter; | |
| 17 | |
| 14 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, | 18 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, |
| 15 Zone* zone) | 19 Zone* zone, bool do_liveness_analysis) |
| 16 : bytecode_array_(bytecode_array), | 20 : bytecode_array_(bytecode_array), |
| 21 do_liveness_analysis_(do_liveness_analysis), | |
| 17 zone_(zone), | 22 zone_(zone), |
| 18 loop_stack_(zone), | 23 loop_stack_(zone), |
| 19 end_to_header_(zone), | 24 end_to_header_(zone), |
| 20 header_to_parent_(zone) {} | 25 header_to_parent_(zone), |
| 26 liveness_map_(bytecode_array->length(), zone) {} | |
| 21 | 27 |
| 22 void BytecodeAnalysis::Analyze() { | 28 void BytecodeAnalysis::Analyze() { |
| 23 loop_stack_.push(-1); | 29 loop_stack_.push(-1); |
| 24 | 30 |
| 25 interpreter::BytecodeArrayReverseIterator iterator(bytecode_array(), zone()); | 31 // The last JumpLoop that we haven't done a guaranteed valid liveness pass |
| 26 while (!iterator.done()) { | 32 // over. See the below wall of text for a more thorough explanation. |
| 27 interpreter::Bytecode bytecode = iterator.current_bytecode(); | 33 int last_invalid_jumploop_offset = -1; |
| 28 if (bytecode == interpreter::Bytecode::kJumpLoop) { | 34 |
| 29 PushLoop(iterator.GetJumpTargetOffset(), iterator.current_offset()); | 35 BytecodeArrayReverseIterator iterator(bytecode_array(), zone()); |
| 30 } else if (iterator.current_offset() == loop_stack_.top()) { | 36 for (; !iterator.done(); iterator.Advance()) { |
| 37 Bytecode bytecode = iterator.current_bytecode(); | |
| 38 int current_offset = iterator.current_offset(); | |
| 39 | |
| 40 if (bytecode == Bytecode::kJumpLoop) { | |
| 41 PushLoop(iterator.GetJumpTargetOffset(), current_offset); | |
| 42 | |
| 43 // Save the last offset so that we can do another pass later. | |
| 44 if (last_invalid_jumploop_offset == -1) { | |
| 45 last_invalid_jumploop_offset = current_offset; | |
| 46 } | |
| 47 } else if (current_offset == loop_stack_.top()) { | |
| 31 loop_stack_.pop(); | 48 loop_stack_.pop(); |
| 32 } | 49 } |
| 33 iterator.Advance(); | 50 |
| 51 if (do_liveness_analysis_) { | |
| 52 Liveness& liveness = liveness_map_.InitializeLiveness( | |
| 53 current_offset, bytecode_array()->register_count() + 1, zone()); | |
|
rmcilroy
2016/11/28 10:00:30
nit - add a comment that the + 1 is for the accumu
Leszek Swirski
2016/11/28 11:07:22
Added a comment. As with another reply, adding met
| |
| 54 | |
| 55 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); | |
| 56 liveness.in->CopyFrom(*liveness.out); | |
| 57 UpdateInLiveness(bytecode, *liveness.in, iterator); | |
| 58 } | |
| 34 } | 59 } |
| 35 | 60 |
| 36 DCHECK_EQ(loop_stack_.size(), 1u); | 61 DCHECK_EQ(loop_stack_.size(), 1u); |
| 37 DCHECK_EQ(loop_stack_.top(), -1); | 62 DCHECK_EQ(loop_stack_.top(), -1); |
| 63 | |
| 64 if (!do_liveness_analysis_) return; | |
| 65 | |
| 66 // At this point, every bytecode has a valid in and out liveness, except for | |
| 67 // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness | |
| 68 // analysis iterations can only add additional liveness bits that are pulled | |
| 69 // across these back edges. | |
| 70 // | |
| 71 // Furthermore, a loop header's in-liveness can only change based on any | |
| 72 // bytecodes *after* the loop end -- it cannot change as a result of the | |
| 73 // JumpLoop liveness being updated, as the only liveness bits than can be | |
| 74 // added to the loop body are those of the loop header. | |
| 75 // | |
| 76 // So, if we know that the liveness of bytecodes after a loop header won't | |
| 77 // change (e.g. because there are no loops in them, or we have already | |
| 78 // ensured those loops are valid), we can safely pass update the loop end and | |
|
rmcilroy
2016/11/28 10:00:31
nit - "pass update the loop end" seems like it's m
Leszek Swirski
2016/11/28 11:07:22
Done -- it was too many words rather than too few.
| |
| 79 // pass over the loop body, and then never have to pass over that loop end | |
| 80 // again, because we have shown that its target, the loop header, can't change | |
| 81 // from the entries after the loop, and can't change from any loop body pass. | |
| 82 // | |
| 83 // This means that in a pass, we can iterate backwards over the bytecode | |
| 84 // array, process any loops that we encounter, and on subsequent passes we can | |
| 85 // skip processing those loops (though we still have to process inner loops). | |
| 86 | |
| 87 while (last_invalid_jumploop_offset != -1) { | |
| 88 // TODO(leszeks): We shouldn't need to iterate here, we should just have a | |
| 89 // random access iterator. | |
| 90 iterator.Reset(); | |
| 91 while (last_invalid_jumploop_offset < iterator.current_offset()) { | |
| 92 iterator.Advance(); | |
| 93 } | |
| 94 last_invalid_jumploop_offset = -1; | |
| 95 | |
| 96 DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop); | |
| 97 | |
| 98 for (; !iterator.done(); iterator.Advance()) { | |
| 99 Bytecode bytecode = iterator.current_bytecode(); | |
| 100 if (bytecode != Bytecode::kJumpLoop) { | |
| 101 // Skip bytecodes until we hit a jumploop. This check isn't needed for | |
| 102 // the first loop we see (thanks to saving its offset), but it is for | |
| 103 // subsequent ones we want to process on this pass. | |
| 104 continue; | |
| 105 } | |
| 106 | |
| 107 int header_offset = iterator.GetJumpTargetOffset(); | |
| 108 int end_offset = iterator.current_offset(); | |
| 109 | |
| 110 Liveness& header_liveness = liveness_map_.GetLiveness(header_offset); | |
| 111 Liveness& end_liveness = liveness_map_.GetLiveness(end_offset); | |
| 112 | |
| 113 if (end_liveness.out->UnionIsChanged(*header_liveness.in)) { | |
| 114 // Only update the loop body if the loop end liveness changed. | |
| 115 end_liveness.in->CopyFrom(*end_liveness.out); | |
| 116 | |
| 117 // Go past loop end. | |
| 118 iterator.Advance(); | |
| 119 for (; iterator.current_offset() > header_offset; iterator.Advance()) { | |
| 120 bytecode = iterator.current_bytecode(); | |
| 121 if (bytecode == Bytecode::kJumpLoop) { | |
| 122 // We can't validate this loop at the moment because we can't | |
| 123 // guarantee that its header is valid yet. Save it for later. | |
|
Jarin
2016/11/28 08:34:43
I am wondering whether it really matters that the
Leszek Swirski
2016/11/28 10:11:01
The problem is, I need to add this new liveness in
Jarin
2016/11/28 12:05:42
You are right. It is unfortunate that it has to be
Leszek Swirski
2016/11/28 14:18:51
Since this was quite a tricky example, I've added
Jarin
2016/11/28 19:47:20
Thanks!
| |
| 124 if (last_invalid_jumploop_offset == -1) { | |
| 125 last_invalid_jumploop_offset = iterator.current_offset(); | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 int current_offset = iterator.current_offset(); | |
| 130 Liveness& liveness = liveness_map_.GetLiveness(current_offset); | |
| 131 | |
| 132 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); | |
| 133 liveness.in->CopyFrom(*liveness.out); | |
| 134 UpdateInLiveness(bytecode, *liveness.in, iterator); | |
| 135 } | |
| 136 // Now we are at the loop header. Since the in-liveness of the header | |
| 137 // can't change, we need only to update the out-liveness. | |
| 138 bytecode = iterator.current_bytecode(); | |
| 139 UpdateOutLiveness(bytecode, *header_liveness.out, iterator, | |
| 140 liveness_map_); | |
| 141 } | |
| 142 | |
| 143 // Keep the iterator going so that we can find other loops. | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 DCHECK(LivenessIsValid()); | |
| 38 } | 148 } |
| 39 | 149 |
| 40 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { | 150 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { |
| 41 DCHECK(loop_header < loop_end); | 151 DCHECK(loop_header < loop_end); |
| 42 DCHECK(loop_stack_.top() < loop_header); | 152 DCHECK(loop_stack_.top() < loop_header); |
| 43 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); | 153 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); |
| 44 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); | 154 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); |
| 45 | 155 |
| 46 end_to_header_.insert(ZoneMap<int, int>::value_type(loop_end, loop_header)); | 156 end_to_header_.insert(ZoneMap<int, int>::value_type(loop_end, loop_header)); |
| 47 header_to_parent_.insert( | 157 header_to_parent_.insert( |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 85 | 195 |
| 86 return header_to_parent_.upper_bound(offset)->second; | 196 return header_to_parent_.upper_bound(offset)->second; |
| 87 } | 197 } |
| 88 | 198 |
| 89 int BytecodeAnalysis::GetParentLoopFor(int header_offset) const { | 199 int BytecodeAnalysis::GetParentLoopFor(int header_offset) const { |
| 90 DCHECK(IsLoopHeader(header_offset)); | 200 DCHECK(IsLoopHeader(header_offset)); |
| 91 | 201 |
| 92 return header_to_parent_.find(header_offset)->second; | 202 return header_to_parent_.find(header_offset)->second; |
| 93 } | 203 } |
| 94 | 204 |
| 205 const BitVector* BytecodeAnalysis::GetInLivenessFor(int offset) const { | |
| 206 if (!do_liveness_analysis_) return nullptr; | |
| 207 | |
| 208 return liveness_map_.GetInLiveness(offset); | |
| 209 } | |
| 210 | |
| 211 const BitVector* BytecodeAnalysis::GetOutLivenessFor(int offset) const { | |
| 212 if (!do_liveness_analysis_) return nullptr; | |
| 213 | |
| 214 return liveness_map_.GetOutLiveness(offset); | |
| 215 } | |
| 216 | |
| 217 #if DEBUG | |
| 218 bool BytecodeAnalysis::LivenessIsValid() { | |
| 219 BytecodeArrayReverseIterator iterator(bytecode_array(), zone()); | |
| 220 | |
| 221 BitVector previous_liveness(bytecode_array()->register_count() + 1, zone()); | |
| 222 | |
| 223 int invalid_offset = -1; | |
| 224 int which_invalid = -1; | |
| 225 | |
| 226 // Ensure that there are no liveness changes if we iterate one more time. | |
| 227 for (iterator.Reset(); !iterator.done(); iterator.Advance()) { | |
| 228 Bytecode bytecode = iterator.current_bytecode(); | |
| 229 | |
| 230 int current_offset = iterator.current_offset(); | |
| 231 | |
| 232 Liveness& liveness = liveness_map_.GetLiveness(current_offset); | |
| 233 | |
| 234 previous_liveness.CopyFrom(*liveness.out); | |
| 235 | |
| 236 UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_); | |
| 237 // UpdateOutLiveness skips kJumpLoop, so we update it manually. | |
| 238 if (bytecode == Bytecode::kJumpLoop) { | |
| 239 int target_offset = iterator.GetJumpTargetOffset(); | |
| 240 liveness.out->Union(*liveness_map_.GetInLiveness(target_offset)); | |
| 241 } | |
| 242 | |
| 243 if (!liveness.out->Equals(previous_liveness)) { | |
| 244 // Reset the invalid liveness. | |
| 245 liveness.out->CopyFrom(previous_liveness); | |
| 246 invalid_offset = current_offset; | |
| 247 which_invalid = 1; | |
| 248 break; | |
| 249 } | |
| 250 | |
| 251 previous_liveness.CopyFrom(*liveness.in); | |
| 252 | |
| 253 liveness.in->CopyFrom(*liveness.out); | |
| 254 UpdateInLiveness(bytecode, *liveness.in, iterator); | |
| 255 | |
| 256 if (!liveness.in->Equals(previous_liveness)) { | |
| 257 // Reset the invalid liveness. | |
| 258 liveness.in->CopyFrom(previous_liveness); | |
| 259 invalid_offset = current_offset; | |
| 260 which_invalid = 0; | |
| 261 break; | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 if (invalid_offset != -1) { | |
| 266 OFStream of(stderr); | |
| 267 of << "Invalid liveness:" << std::endl; | |
| 268 | |
| 269 // Dump the bytecode, annotated with the liveness and marking loops. | |
| 270 | |
| 271 int loop_indent = 0; | |
| 272 | |
| 273 BytecodeArrayIterator forward_iterator(bytecode_array()); | |
| 274 for (; !forward_iterator.done(); forward_iterator.Advance()) { | |
| 275 int current_offset = forward_iterator.current_offset(); | |
| 276 BitVector* in_liveness = liveness_map_.GetInLiveness(current_offset); | |
| 277 BitVector* out_liveness = liveness_map_.GetOutLiveness(current_offset); | |
| 278 | |
| 279 for (int i = 0; i < in_liveness->length(); ++i) { | |
| 280 of << (in_liveness->Contains(i) ? 'L' : '.'); | |
| 281 } | |
| 282 | |
| 283 of << " | "; | |
| 284 | |
| 285 for (int i = 0; i < out_liveness->length(); ++i) { | |
| 286 of << (out_liveness->Contains(i) ? 'L' : '.'); | |
| 287 } | |
| 288 | |
| 289 of << " : " << current_offset << " : "; | |
| 290 | |
| 291 // Draw loop back edges by indentin everything between loop headers and | |
| 292 // jump loop instructions. | |
| 293 if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) { | |
| 294 loop_indent--; | |
| 295 } | |
| 296 for (int i = 0; i < loop_indent; ++i) { | |
| 297 of << " | "; | |
| 298 } | |
| 299 if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) { | |
| 300 of << " `-" << current_offset; | |
| 301 } else if (IsLoopHeader(current_offset)) { | |
| 302 of << " .>" << current_offset; | |
| 303 loop_indent++; | |
| 304 } | |
| 305 forward_iterator.PrintTo(of) << std::endl; | |
| 306 | |
| 307 if (current_offset == invalid_offset) { | |
| 308 // Underline the invalid liveness. | |
| 309 if (which_invalid == 0) { | |
| 310 for (int i = 0; i < in_liveness->length(); ++i) { | |
| 311 of << '^'; | |
| 312 } | |
| 313 } else { | |
| 314 for (int i = 0; i < in_liveness->length() + 3; ++i) { | |
| 315 of << ' '; | |
| 316 } | |
| 317 for (int i = 0; i < out_liveness->length(); ++i) { | |
| 318 of << '^'; | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 // Make sure to draw the loop indentation marks on this additional line. | |
| 323 of << " : " << current_offset << " : "; | |
| 324 for (int i = 0; i < loop_indent; ++i) { | |
| 325 of << " | "; | |
| 326 } | |
| 327 | |
| 328 of << std::endl; | |
| 329 } | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 return invalid_offset == -1; | |
| 334 } | |
| 335 #endif | |
| 336 | |
| 95 } // namespace compiler | 337 } // namespace compiler |
| 96 } // namespace internal | 338 } // namespace internal |
| 97 } // namespace v8 | 339 } // namespace v8 |
| OLD | NEW |