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

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

Issue 2558093005: [turbofan] Add and use bytecode loop assigment analysis (Closed)
Patch Set: Fix OSR check and address Jaro's comments. 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/interpreter/bytecode-array-iterator.h" 7 #include "src/interpreter/bytecode-array-iterator.h"
8 #include "src/interpreter/bytecode-array-random-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 BytecodeLoopAssignments::BytecodeLoopAssignments(int parameter_count,
18 int register_count, Zone* zone)
19 : parameter_count_(parameter_count),
20 bit_vector_(new (zone)
21 BitVector(parameter_count + register_count, zone)) {}
22
23 void BytecodeLoopAssignments::Add(interpreter::Register r) {
24 if (r.is_parameter()) {
25 bit_vector_->Add(r.ToParameterIndex(parameter_count_));
26 } else {
27 bit_vector_->Add(parameter_count_ + r.index());
28 }
29 }
30
31 void BytecodeLoopAssignments::AddPair(interpreter::Register r) {
32 if (r.is_parameter()) {
33 DCHECK(interpreter::Register(r.index() + 1).is_parameter());
34 bit_vector_->Add(r.ToParameterIndex(parameter_count_));
35 bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1);
36 } else {
37 DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
38 bit_vector_->Add(parameter_count_ + r.index());
39 bit_vector_->Add(parameter_count_ + r.index() + 1);
40 }
41 }
42
43 void BytecodeLoopAssignments::AddTriple(interpreter::Register r) {
44 if (r.is_parameter()) {
45 DCHECK(interpreter::Register(r.index() + 1).is_parameter());
46 DCHECK(interpreter::Register(r.index() + 2).is_parameter());
47 bit_vector_->Add(r.ToParameterIndex(parameter_count_));
48 bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1);
49 bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 2);
50 } else {
51 DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
52 DCHECK(!interpreter::Register(r.index() + 2).is_parameter());
53 bit_vector_->Add(parameter_count_ + r.index());
54 bit_vector_->Add(parameter_count_ + r.index() + 1);
55 bit_vector_->Add(parameter_count_ + r.index() + 2);
56 }
57 }
58
59 void BytecodeLoopAssignments::AddAll() { bit_vector_->AddAll(); }
60
61 void BytecodeLoopAssignments::Union(const BytecodeLoopAssignments& other) {
62 bit_vector_->Union(*other.bit_vector_);
63 }
64
65 bool BytecodeLoopAssignments::ContainsParameter(int index) const {
66 DCHECK_GE(index, 0);
67 DCHECK_LT(index, parameter_count());
68 return bit_vector_->Contains(index);
69 }
70
71 bool BytecodeLoopAssignments::ContainsLocal(int index) const {
72 DCHECK_GE(index, 0);
73 DCHECK_LT(index, local_count());
74 return bit_vector_->Contains(parameter_count_ + index);
75 }
76
77 bool BytecodeLoopAssignments::ContainsAccumulator() const {
78 // TODO(leszeks): This assumes the accumulator is always assigned. This is
79 // probably correct, but that assignment is also probably dead, so we should
80 // check liveness.
81 return true;
82 }
83
17 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, 84 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
18 Zone* zone, bool do_liveness_analysis) 85 Zone* zone, bool do_liveness_analysis)
19 : bytecode_array_(bytecode_array), 86 : bytecode_array_(bytecode_array),
20 do_liveness_analysis_(do_liveness_analysis), 87 do_liveness_analysis_(do_liveness_analysis),
21 zone_(zone), 88 zone_(zone),
22 loop_stack_(zone), 89 loop_stack_(zone),
23 loop_end_index_queue_(zone), 90 loop_end_index_queue_(zone),
24 end_to_header_(zone), 91 end_to_header_(zone),
25 header_to_parent_(zone), 92 header_to_info_(zone),
26 liveness_map_(bytecode_array->length(), zone) {} 93 liveness_map_(bytecode_array->length(), zone) {}
27 94
28 namespace { 95 namespace {
29 96
30 void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness, 97 void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness,
31 const BytecodeArrayAccessor& accessor) { 98 const BytecodeArrayAccessor& accessor) {
32 int num_operands = Bytecodes::NumberOfOperands(bytecode); 99 int num_operands = Bytecodes::NumberOfOperands(bytecode);
33 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); 100 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
34 AccumulatorUse accumulator_use = Bytecodes::GetAccumulatorUse(bytecode); 101 AccumulatorUse accumulator_use = Bytecodes::GetAccumulatorUse(bytecode);
35 102
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
138 int handler_offset = 205 int handler_offset =
139 table->LookupRange(current_offset, &handler_context, nullptr); 206 table->LookupRange(current_offset, &handler_context, nullptr);
140 207
141 if (handler_offset != -1) { 208 if (handler_offset != -1) {
142 out_liveness.Union(*liveness_map.GetInLiveness(handler_offset)); 209 out_liveness.Union(*liveness_map.GetInLiveness(handler_offset));
143 out_liveness.MarkRegisterLive(handler_context); 210 out_liveness.MarkRegisterLive(handler_context);
144 } 211 }
145 } 212 }
146 } 213 }
147 214
215 void UpdateAssignments(Bytecode bytecode, BytecodeLoopAssignments& assignments,
216 const BytecodeArrayAccessor& accessor) {
217 int num_operands = Bytecodes::NumberOfOperands(bytecode);
218 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
219
220 for (int i = 0; i < num_operands; ++i) {
221 switch (operand_types[i]) {
222 case OperandType::kRegOut: {
223 assignments.Add(accessor.GetRegisterOperand(i));
224 break;
225 }
226 case OperandType::kRegOutPair: {
227 assignments.AddPair(accessor.GetRegisterOperand(i));
228 break;
229 }
230 case OperandType::kRegOutTriple: {
231 assignments.AddTriple(accessor.GetRegisterOperand(i));
232 break;
233 }
234 default:
235 DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
236 break;
237 }
238 }
239 }
240
148 } // namespace 241 } // namespace
149 242
150 void BytecodeAnalysis::Analyze() { 243 void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
151 loop_stack_.push(-1); 244 loop_stack_.push({-1, nullptr});
152 245
153 BytecodeLivenessState* next_bytecode_in_liveness = nullptr; 246 BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
154 247
248 bool is_osr_loop = false;
249 int osr_loop_end_offset =
250 osr_bailout_id.IsNone() ? -1 : osr_bailout_id.ToInt();
251
155 BytecodeArrayRandomIterator iterator(bytecode_array(), zone()); 252 BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
156 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) { 253 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
157 Bytecode bytecode = iterator.current_bytecode(); 254 Bytecode bytecode = iterator.current_bytecode();
158 int current_offset = iterator.current_offset(); 255 int current_offset = iterator.current_offset();
159 256
160 if (bytecode == Bytecode::kJumpLoop) { 257 if (bytecode == Bytecode::kJumpLoop) {
161 // Every byte up to and including the last byte within the backwards jump 258 // Every byte up to and including the last byte within the backwards jump
162 // instruction is considered part of the loop, set loop end accordingly. 259 // instruction is considered part of the loop, set loop end accordingly.
163 int loop_end = current_offset + iterator.current_bytecode_size(); 260 int loop_end = current_offset + iterator.current_bytecode_size();
164 PushLoop(iterator.GetJumpTargetOffset(), loop_end); 261 PushLoop(iterator.GetJumpTargetOffset(), loop_end);
165 262
263 // Normally prefixed bytecodes are treated as if the prefix's offset was
264 // the actual bytecode's offset. However, the OSR id is the offset of the
265 // actual JumpLoop bytecode, so we need to find the location of that
266 // bytecode ignoring the prefix.
rmcilroy 2016/12/14 10:27:55 Michi question (not for this CL) - should we use t
Michael Starzinger 2016/12/15 13:16:57 Acknowledged. As discussed offline: Doing this bef
267 int jump_loop_offset = current_offset + iterator.current_prefix_offset();
268 is_osr_loop = (jump_loop_offset == osr_loop_end_offset);
269
270 #if DEBUG
271 // Check that is_osr_loop is set iff the osr_loop_end_offset is within
272 // this bytecode.
273 if (is_osr_loop) {
274 DCHECK(current_offset <= osr_loop_end_offset &&
275 osr_loop_end_offset < loop_end);
276 } else {
277 DCHECK(current_offset > osr_loop_end_offset ||
278 osr_loop_end_offset >= loop_end);
279 }
rmcilroy 2016/12/14 10:27:55 Could you just pull this out into a separate helpe
Leszek Swirski 2016/12/14 14:42:19 Done.
280 #endif
281
282 if (is_osr_loop) {
283 loop_stack_.top().loop_info->assignments().AddAll();
rmcilroy 2016/12/14 10:27:55 Could you add a comment on why this is necessary
Leszek Swirski 2016/12/14 14:42:18 Done.
284 }
285
166 // Save the index so that we can do another pass later. 286 // Save the index so that we can do another pass later.
167 if (do_liveness_analysis_) { 287 if (do_liveness_analysis_) {
168 loop_end_index_queue_.push_back(iterator.current_index()); 288 loop_end_index_queue_.push_back(iterator.current_index());
169 } 289 }
170 } else if (current_offset == loop_stack_.top()) { 290 } else if (loop_stack_.size() > 1) {
171 loop_stack_.pop(); 291 LoopStackEntry& current_loop = loop_stack_.top();
292 LoopInfo* current_loop_info = current_loop.loop_info;
293
294 if (!is_osr_loop) {
rmcilroy 2016/12/14 10:27:55 Do we need this check here? If we AddAll for the O
Leszek Swirski 2016/12/14 14:42:19 Sure, it's not performance critical I think.
295 // TODO(leszeks): Ideally, we'd only set values that were assigned in
296 // the loop *and* are live when the loop exits. However, this requires
297 // tracking the out-liveness of *all* loop exits, which is not
298 // information we currently have.
299 UpdateAssignments(bytecode, current_loop_info->assignments(), iterator);
300 }
301
302 if (current_offset == current_loop.header_offset) {
303 loop_stack_.pop();
304 if (loop_stack_.size() > 1) {
305 // Propagate inner loop assignments to outer loop.
306 loop_stack_.top().loop_info->assignments().Union(
307 current_loop_info->assignments());
308 }
309 }
172 } 310 }
173 311
174 if (do_liveness_analysis_) { 312 if (do_liveness_analysis_) {
175 BytecodeLiveness& liveness = liveness_map_.InitializeLiveness( 313 BytecodeLiveness& liveness = liveness_map_.InitializeLiveness(
176 current_offset, bytecode_array()->register_count(), zone()); 314 current_offset, bytecode_array()->register_count(), zone());
177 315
178 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness, 316 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness,
179 iterator, liveness_map_); 317 iterator, liveness_map_);
180 liveness.in->CopyFrom(*liveness.out); 318 liveness.in->CopyFrom(*liveness.out);
181 UpdateInLiveness(bytecode, *liveness.in, iterator); 319 UpdateInLiveness(bytecode, *liveness.in, iterator);
182 320
183 next_bytecode_in_liveness = liveness.in; 321 next_bytecode_in_liveness = liveness.in;
184 } 322 }
185 } 323 }
186 324
187 DCHECK_EQ(loop_stack_.size(), 1u); 325 DCHECK_EQ(loop_stack_.size(), 1u);
188 DCHECK_EQ(loop_stack_.top(), -1); 326 DCHECK_EQ(loop_stack_.top().header_offset, -1);
189 327
190 if (!do_liveness_analysis_) return; 328 if (!do_liveness_analysis_) return;
191 329
192 // At this point, every bytecode has a valid in and out liveness, except for 330 // At this point, every bytecode has a valid in and out liveness, except for
193 // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness 331 // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness
194 // analysis iterations can only add additional liveness bits that are pulled 332 // analysis iterations can only add additional liveness bits that are pulled
195 // across these back edges. 333 // across these back edges.
196 // 334 //
197 // Furthermore, a loop header's in-liveness can only change based on any 335 // Furthermore, a loop header's in-liveness can only change based on any
198 // bytecodes *after* the loop end -- it cannot change as a result of the 336 // bytecodes *after* the loop end -- it cannot change as a result of the
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 // can't change, we need only to update the out-liveness. 390 // can't change, we need only to update the out-liveness.
253 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out, 391 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out,
254 next_bytecode_in_liveness, iterator, liveness_map_); 392 next_bytecode_in_liveness, iterator, liveness_map_);
255 } 393 }
256 394
257 DCHECK(LivenessIsValid()); 395 DCHECK(LivenessIsValid());
258 } 396 }
259 397
260 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) { 398 void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) {
261 DCHECK(loop_header < loop_end); 399 DCHECK(loop_header < loop_end);
262 DCHECK(loop_stack_.top() < loop_header); 400 DCHECK(loop_stack_.top().header_offset < loop_header);
263 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end()); 401 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end());
264 DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end()); 402 DCHECK(header_to_info_.find(loop_header) == header_to_info_.end());
265 403
266 end_to_header_.insert(ZoneMap<int, int>::value_type(loop_end, loop_header)); 404 int parent_offset = loop_stack_.top().header_offset;
267 header_to_parent_.insert( 405
268 ZoneMap<int, int>::value_type(loop_header, loop_stack_.top())); 406 end_to_header_.insert({loop_end, loop_header});
269 loop_stack_.push(loop_header); 407 auto it = header_to_info_.insert(
408 {loop_header, LoopInfo(parent_offset, bytecode_array_->parameter_count(),
409 bytecode_array_->register_count(), zone_)});
410 // Get the loop info pointer from the output of insert.
411 LoopInfo* loop_info = &it.first->second;
412
413 loop_stack_.push({loop_header, loop_info});
270 } 414 }
271 415
272 bool BytecodeAnalysis::IsLoopHeader(int offset) const { 416 bool BytecodeAnalysis::IsLoopHeader(int offset) const {
273 return header_to_parent_.find(offset) != header_to_parent_.end(); 417 return header_to_info_.find(offset) != header_to_info_.end();
274 } 418 }
275 419
276 int BytecodeAnalysis::GetLoopOffsetFor(int offset) const { 420 int BytecodeAnalysis::GetLoopOffsetFor(int offset) const {
277 auto loop_end_to_header = end_to_header_.upper_bound(offset); 421 auto loop_end_to_header = end_to_header_.upper_bound(offset);
278 // If there is no next end => offset is not in a loop. 422 // If there is no next end => offset is not in a loop.
279 if (loop_end_to_header == end_to_header_.end()) { 423 if (loop_end_to_header == end_to_header_.end()) {
280 return -1; 424 return -1;
281 } 425 }
282 // If the header preceeds the offset, this is the loop 426 // If the header preceeds the offset, this is the loop
283 // 427 //
284 // .> header <--loop_end_to_header 428 // .> header <--loop_end_to_header
285 // | 429 // |
286 // | <--offset 430 // | <--offset
287 // | 431 // |
288 // `- end 432 // `- end
289 if (loop_end_to_header->second <= offset) { 433 if (loop_end_to_header->second <= offset) {
290 return loop_end_to_header->second; 434 return loop_end_to_header->second;
291 } 435 }
292 // Otherwise there is a (potentially nested) loop after this offset. 436 // Otherwise there is a (potentially nested) loop after this offset.
293 // 437 //
294 // <--offset 438 // <--offset
295 // 439 //
296 // .> header 440 // .> header
297 // | 441 // |
298 // | .> header <--loop_end_to_header 442 // | .> header <--loop_end_to_header
299 // | | 443 // | |
300 // | `- end 444 // | `- end
301 // | 445 // |
302 // `- end 446 // `- end
303 // We just return the parent of the next loop header (might be -1). 447 // We just return the parent of the next loop (might be -1).
304 DCHECK(header_to_parent_.upper_bound(offset) != header_to_parent_.end()); 448 DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end());
305 449
306 return header_to_parent_.upper_bound(offset)->second; 450 return header_to_info_.upper_bound(offset)->second.parent_offset();
307 } 451 }
308 452
309 int BytecodeAnalysis::GetParentLoopFor(int header_offset) const { 453 const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const {
310 DCHECK(IsLoopHeader(header_offset)); 454 DCHECK(IsLoopHeader(header_offset));
311 455
312 return header_to_parent_.find(header_offset)->second; 456 return header_to_info_.find(header_offset)->second;
313 } 457 }
314 458
315 const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor( 459 const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor(
316 int offset) const { 460 int offset) const {
317 if (!do_liveness_analysis_) return nullptr; 461 if (!do_liveness_analysis_) return nullptr;
318 462
319 return liveness_map_.GetInLiveness(offset); 463 return liveness_map_.GetInLiveness(offset);
320 } 464 }
321 465
322 const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor( 466 const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor(
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
477 } 621 }
478 } 622 }
479 623
480 return invalid_offset == -1; 624 return invalid_offset == -1;
481 } 625 }
482 #endif 626 #endif
483 627
484 } // namespace compiler 628 } // namespace compiler
485 } // namespace internal 629 } // namespace internal
486 } // namespace v8 630 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698