Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: src/compiler/bytecode-analysis.cc

Issue 2523893003: Reland of [ignition/turbo] Perform liveness analysis on the bytecodes (Closed)
Patch Set: Fix debugger special casing and liveness disabling Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698