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

Side by Side Diff: runtime/vm/flow_graph_allocator.cc

Issue 10696151: Skeleton of a linear scan register allocator. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 months 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/flow_graph_allocator.h" 5 #include "vm/flow_graph_allocator.h"
6 6
7 #include "vm/bit_vector.h" 7 #include "vm/bit_vector.h"
8 #include "vm/intermediate_language.h" 8 #include "vm/intermediate_language.h"
9 #include "vm/il_printer.h"
10 #include "vm/flow_graph_builder.h"
9 #include "vm/flow_graph_compiler.h" 11 #include "vm/flow_graph_compiler.h"
10 12
11 namespace dart { 13 namespace dart {
12 14
13 DEFINE_FLAG(bool, print_ssa_liveness, false, 15 DEFINE_FLAG(bool, print_ssa_liveness, false,
14 "Print liveness for ssa variables."); 16 "Print liveness for ssa variables.");
17 DEFINE_FLAG(bool, trace_ssa_allocator, false,
18 "Trace register allocation over SSA.");
19
20 #ifdef DEBUG
21 #define TRACE_ALLOC(m) do { \
22 if (FLAG_trace_ssa_allocator) OS::Print m ; \
23 } while (0)
24 #endif
15 25
16 FlowGraphAllocator::FlowGraphAllocator( 26 FlowGraphAllocator::FlowGraphAllocator(
17 const GrowableArray<BlockEntryInstr*>& postorder, 27 const GrowableArray<BlockEntryInstr*>& block_order,
18 intptr_t max_ssa_temp_index) 28 FlowGraphBuilder* builder)
19 : live_out_(postorder.length()), 29 : builder_(builder),
20 kill_(postorder.length()), 30 live_out_(builder->postorder_block_entries().length()),
srdjan 2012/07/10 23:27:54 builder->postorder_block_entries().length() can be
Vyacheslav Egorov (Google) 2012/07/11 13:27:58 Done.
21 live_in_(postorder.length()), 31 kill_(builder->postorder_block_entries().length()),
22 postorder_(postorder), 32 live_in_(builder->postorder_block_entries().length()),
23 vreg_count_(max_ssa_temp_index) { 33 block_order_(block_order),
34 postorder_(builder->postorder_block_entries()),
35 vreg_count_(builder->current_ssa_temp_index()),
36 live_ranges_(builder->current_ssa_temp_index()) {
37 for (intptr_t i = 0; i < vreg_count_; i++) live_ranges_.Add(NULL);
24 } 38 }
25 39
26 40
27 void FlowGraphAllocator::ResolveConstraints() {
28 // TODO(fschneider): Resolve register constraints.
29 }
30
31
32 void FlowGraphAllocator::ComputeInitialSets() { 41 void FlowGraphAllocator::ComputeInitialSets() {
33 const intptr_t block_count = postorder_.length(); 42 const intptr_t block_count = postorder_.length();
34 for (intptr_t i = 0; i < block_count; i++) { 43 for (intptr_t i = 0; i < block_count; i++) {
35 BlockEntryInstr* block = postorder_[i]; 44 BlockEntryInstr* block = postorder_[i];
36 45
37 BitVector* kill = kill_[i]; 46 BitVector* kill = kill_[i];
38 BitVector* live_in = live_in_[i]; 47 BitVector* live_in = live_in_[i];
39 48
40 if (block->IsJoinEntry()) { 49 if (block->IsJoinEntry()) {
41 JoinEntryInstr* join = block->AsJoinEntry(); 50 JoinEntryInstr* join = block->AsJoinEntry();
(...skipping 19 matching lines...) Expand all
61 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { 70 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
62 Instruction* current = it.Current(); 71 Instruction* current = it.Current();
63 for (intptr_t j = 0; j < current->InputCount(); j++) { 72 for (intptr_t j = 0; j < current->InputCount(); j++) {
64 Value* input = current->InputAt(j); 73 Value* input = current->InputAt(j);
65 if (input->IsUse()) { 74 if (input->IsUse()) {
66 const intptr_t use = input->AsUse()->definition()->ssa_temp_index(); 75 const intptr_t use = input->AsUse()->definition()->ssa_temp_index();
67 if (!kill->Contains(use)) live_in->Add(use); 76 if (!kill->Contains(use)) live_in->Add(use);
68 } 77 }
69 } 78 }
70 79
80 // Add uses from the deoptimization environment.
81 if (current->env() != NULL) {
82 const ZoneGrowableArray<Value*>& values = current->env()->values();
83 for (intptr_t j = 0; j < values.length(); j++) {
84 Value* val = values[j];
85 if (val->IsUse()) {
86 const intptr_t use = val->AsUse()->definition()->ssa_temp_index();
87 if (!kill->Contains(use)) live_in->Add(use);
88 }
89 }
90 }
91
71 Definition* current_def = current->AsDefinition(); 92 Definition* current_def = current->AsDefinition();
72 if ((current_def != NULL) && (current_def->ssa_temp_index() >= 0)) { 93 if ((current_def != NULL) && (current_def->ssa_temp_index() >= 0)) {
73 kill->Add(current_def->ssa_temp_index()); 94 kill->Add(current_def->ssa_temp_index());
74 } 95 }
75 } 96 }
76 } 97 }
77 98
78 // Update initial live_in sets to match live_out sets. Has to be 99 // Update initial live_in sets to match live_out sets. Has to be
79 // done in a separate path because of backwards branches. 100 // done in a separate path because of backwards branches.
80 for (intptr_t i = 0; i < block_count; i++) { 101 for (intptr_t i = 0; i < block_count; i++) {
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
131 void FlowGraphAllocator::AnalyzeLiveness() { 152 void FlowGraphAllocator::AnalyzeLiveness() {
132 const intptr_t block_count = postorder_.length(); 153 const intptr_t block_count = postorder_.length();
133 for (intptr_t i = 0; i < block_count; i++) { 154 for (intptr_t i = 0; i < block_count; i++) {
134 live_out_.Add(new BitVector(vreg_count_)); 155 live_out_.Add(new BitVector(vreg_count_));
135 kill_.Add(new BitVector(vreg_count_)); 156 kill_.Add(new BitVector(vreg_count_));
136 live_in_.Add(new BitVector(vreg_count_)); 157 live_in_.Add(new BitVector(vreg_count_));
137 } 158 }
138 159
139 ComputeInitialSets(); 160 ComputeInitialSets();
140 ComputeLiveInAndLiveOutSets(); 161 ComputeLiveInAndLiveOutSets();
141
142 if (FLAG_print_ssa_liveness) {
143 DumpLiveness();
144 }
145 } 162 }
146 163
147 164
148 static void PrintBitVector(const char* tag, BitVector* v) { 165 static void PrintBitVector(const char* tag, BitVector* v) {
149 OS::Print("%s:", tag); 166 OS::Print("%s:", tag);
150 for (BitVector::Iterator it(v); !it.Done(); it.Advance()) { 167 for (BitVector::Iterator it(v); !it.Done(); it.Advance()) {
151 OS::Print(" %d", it.Current()); 168 OS::Print(" %d", it.Current());
152 } 169 }
153 OS::Print("\n"); 170 OS::Print("\n");
154 } 171 }
(...skipping 12 matching lines...) Expand all
167 } 184 }
168 OS::Print("\n"); 185 OS::Print("\n");
169 186
170 PrintBitVector(" live out", live_out_[i]); 187 PrintBitVector(" live out", live_out_[i]);
171 PrintBitVector(" kill", kill_[i]); 188 PrintBitVector(" kill", kill_[i]);
172 PrintBitVector(" live in", live_in_[i]); 189 PrintBitVector(" live in", live_in_[i]);
173 } 190 }
174 } 191 }
175 192
176 193
194 void UseInterval::Print() {
195 OS::Print(" [%d, %d) uses {", start_, end_);
196 for (UsePosition* use_pos = uses_;
197 use_pos != NULL && use_pos->pos() <= end();
198 use_pos = use_pos->next()) {
199 if (use_pos != uses_) OS::Print(", ");
200 OS::Print("%d", use_pos->pos());
201 }
202 OS::Print("}\n");
203 }
204
205
206 void UseInterval::AddUse(Instruction* instr,
207 intptr_t pos,
208 Location* location_slot) {
209 ASSERT((start_ <= pos) && (pos <= end_)); // TODO(vegorov) uses inclusion?
srdjan 2012/07/10 23:27:54 In comment you wrote [start, end) ?
Vyacheslav Egorov (Google) 2012/07/11 13:27:58 Last use is still attributed to the UseInterval be
210 ASSERT((instr == NULL) || (instr->pos() == pos));
211 if ((uses_ != NULL) && (uses_->pos() == pos)) {
212 if ((location_slot == NULL) || (uses_->location_slot() == location_slot)) {
213 return;
214 } else if ((uses_->location_slot() == NULL) && (instr == NULL)) {
215 uses_->set_location_slot(location_slot);
216 return;
217 }
218 }
219 uses_ = new UsePosition(instr, pos, uses_, location_slot);
220 }
221
222
223 void LiveRange::Print() {
224 OS::Print("vreg %d live intervals:\n", vreg_);
225 for (UseInterval* interval = head_;
226 interval != NULL;
227 interval = interval->next_) {
228 interval->Print();
229 }
230 }
231
232
233 void LiveRange::AddUseInterval(intptr_t start, intptr_t end) {
234 if (head_ != NULL && (head_->start_ == end)) {
srdjan 2012/07/10 23:27:54 parenthesis
Vyacheslav Egorov (Google) 2012/07/11 13:27:58 Done.
235 head_->start_ = start;
236 return;
237 }
238
239 head_ = new UseInterval(vreg_, start, end, head_);
240 }
241
242
243 void LiveRange::DefineAt(Instruction* instr, intptr_t pos, Location* loc) {
244 if (head_ != NULL) {
245 ASSERT(head_->start_ <= pos);
246 head_->start_ = pos;
247 } else {
248 // Definition without a use.
249 head_ = new UseInterval(vreg_, pos, pos + 1, NULL);
250 }
251 head_->AddUse(instr, pos, loc);
252 }
253
254
255 // TODO(vegorov): encode use_at_start vs. use_at_end in the location itself?
256 void LiveRange::UseAt(Instruction* instr,
257 intptr_t def, intptr_t use,
258 bool use_at_end,
259 Location* loc) {
260 if (head_ == NULL || head_->start_ != def) {
261 AddUseInterval(def, use + (use_at_end ? 1 : 0));
262 }
263 head_->AddUse(instr, use, loc);
264 }
265
266
267 LiveRange* FlowGraphAllocator::GetLiveRange(intptr_t vreg) {
268 if (live_ranges_[vreg] == NULL) {
269 live_ranges_[vreg] = new LiveRange(vreg);
270 }
271 return live_ranges_[vreg];
272 }
273
274
275 static const intptr_t kNoVirtualRegister = -1;
276 static const intptr_t kTempVirtualRegister = -2;
277 static UseInterval* const kPermanentlyBlocked =
278 reinterpret_cast<UseInterval*>(-1);
279 static const intptr_t kIllegalPosition = -1;
280 static const intptr_t kMaxPosition = 0x7FFFFFFF;
281
282
283 void FlowGraphAllocator::BlockLocation(Location loc, intptr_t pos) {
284 ASSERT(loc.IsRegister());
285 const Register reg = loc.reg();
286 UseInterval* last = cpu_regs_[reg];
287 if (last == kPermanentlyBlocked) return;
288 if ((last != NULL) && (last->start() == pos)) return;
289 cpu_regs_[reg] = new UseInterval(kNoVirtualRegister, pos, pos + 1, last);
290 }
291
292
293 void FlowGraphAllocator::Define(Instruction* instr,
294 intptr_t pos,
295 intptr_t vreg,
296 Location* loc) {
297 LiveRange* range = GetLiveRange(vreg);
298 ASSERT(loc != NULL);
299 if (loc->IsRegister()) {
300 BlockLocation(*loc, pos);
301 range->DefineAt(instr, pos + 1, loc);
302 } else if (loc->IsUnallocated()) {
303 range->DefineAt(instr, pos, loc);
304 } else {
305 UNREACHABLE();
306 }
307
308 AddToUnallocated(range->head());
309 }
310
311
312 void FlowGraphAllocator::UseValue(Instruction* instr,
313 intptr_t def_pos,
314 intptr_t use_pos,
315 intptr_t vreg,
316 Location* loc,
317 bool use_at_end) {
318 LiveRange* range = GetLiveRange(vreg);
319 if (loc == NULL) {
320 range->UseAt(NULL, def_pos, use_pos, true, loc);
321 } else if (loc->IsRegister()) {
322 // We have a fixed use.
323 BlockLocation(*loc, use_pos);
324 range->UseAt(instr, def_pos, use_pos, false, loc);
325 } else if (loc->IsUnallocated()) {
326 ASSERT(loc->policy() == Location::kRequiresRegister);
327 range->UseAt(use_at_end ? NULL : instr, def_pos, use_pos, use_at_end, loc);
328 }
329 }
330
331
332 static void PrintChain(UseInterval* chain) {
333 if (chain == kPermanentlyBlocked) {
334 OS::Print(" not for allocation\n");
335 return;
336 }
337
338 while (chain != NULL) {
339 chain->Print();
340 chain = chain->next();
341 }
342 }
343
344
345 void FlowGraphAllocator::PrintLiveRanges() {
346 for (intptr_t i = 0; i < unallocated_.length(); i++) {
347 OS::Print("unallocated chain for vr%d\n", unallocated_[i]->vreg());
348 PrintChain(unallocated_[i]);
349 }
350
351 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
352 OS::Print("blocking chain for %s\n",
353 Location::RegisterLocation(static_cast<Register>(reg)).Name());
354 PrintChain(cpu_regs_[reg]);
355 }
356 }
357
358
359 void FlowGraphAllocator::BuildLiveRanges() {
360 NumberInstructions();
361
362 const intptr_t block_count = postorder_.length();
363 for (intptr_t i = 0; i < block_count; i++) {
364 BlockEntryInstr* block = postorder_[i];
365
366 // For every SSA value that is live out of this block create an interval
367 // that covers the hole block. It will be shortened if we encounter a
368 // definition of this value in this block.
369 for (BitVector::Iterator it(live_out_[i]); !it.Done(); it.Advance()) {
370 LiveRange* range = GetLiveRange(it.Current());
371 range->AddUseInterval(block->start_pos(), block->end_pos());
372 }
373
374 intptr_t pos = block->end_pos() - 1;
375
376 Instruction* current = block->last_instruction();
377
378 // If last instruction is a parallel move we need to perform phi resolution.
379 if (current->IsParallelMove()) {
380 ParallelMoveInstr* parallel_move = current->AsParallelMove();
381 JoinEntryInstr* join = current->next()->AsJoinEntry();
382 ASSERT(join != NULL);
383
384 // Find index of the current block in predecessors of join.
385 intptr_t pred_idx = -1;
386 for (intptr_t j = 0; j < join->PredecessorCount(); j++) {
387 BlockEntryInstr* pred = join->PredecessorAt(j);
388 if (pred == block) {
389 pred_idx = j;
390 break;
391 }
392 }
393 ASSERT(pred_idx != -1);
394
395 // For every phi we have a reserved phi resolution move and we need
396 // to either initialize its source with constant or to register a use, so
397 // that register allocator will populate source slot with location of
398 // the appropriate SSA value.
399 ZoneGrowableArray<PhiInstr*>* phis = join->phis();
400 intptr_t move_idx = 0;
401 for (intptr_t j = 0; j < phis->length(); j++) {
402 PhiInstr* phi = (*phis)[j];
403 if (phi == NULL) continue;
404
405 Value* val = phi->InputAt(pred_idx);
406
407 MoveOperands move = parallel_move->moves()[move_idx];
408 if (val->IsUse()) {
409 const intptr_t use = val->AsUse()->definition()->ssa_temp_index();
410 Location* slot = move.src_slot();
411 *slot = Location::RequiresRegister();
412 GetLiveRange(use)->head()->AddUse(NULL, pos, slot);
413 } else {
414 ASSERT(val->IsConstant());
415 move.set_src(Location::Constant(val->AsConstant()->value()));
416 }
417
418 move_idx++;
419 }
420
421 current = current->previous();
422 }
423
424 // Now process all instructions in reverse order.
425 pos -= 1;
426 while (current != block) {
427 LocationSummary* locs = current->locs();
428
429 const bool output_same_as_first_input =
430 locs->out().IsUnallocated() &&
431 locs->out().policy() == Location::kSameAsFirstInput;
432
433 // TODO(vegorov): number of inputs should match number of input locations.
434 // TODO(vegorov): generic support for writable registers?
435 for (intptr_t j = 0; j < current->InputCount(); j++) {
436 Value* input = current->InputAt(j);
437 if (input->IsUse()) {
438 const intptr_t use = input->AsUse()->definition()->ssa_temp_index();
439
440 Location* in_ref = (j < locs->input_count()) ?
441 locs->in_slot(j) : NULL;
442 const bool use_at_end = (j > 0) || (in_ref == NULL) ||
443 !output_same_as_first_input;
444 UseValue(current, block->start_pos(), pos, use, in_ref, use_at_end);
445 }
446 }
447
448 // Add uses from the deoptimization environment.
449 // TODO(vegorov): these uses should _not_ require register but for now
450 // they do because we don't support spilling at all.
451 if (current->env() != NULL) {
452 const ZoneGrowableArray<Value*>& values = current->env()->values();
453 GrowableArray<Location>* locations = current->env()->locations();
454
455 for (intptr_t j = 0; j < values.length(); j++) {
456 Value* val = values[j];
457 if (val->IsUse()) {
458 locations->Add(Location::RequiresRegister());
459 const intptr_t use = val->AsUse()->definition()->ssa_temp_index();
460 UseValue(current,
461 block->start_pos(),
462 pos,
463 use,
464 &(*locations)[j],
465 true);
466 } else {
467 locations->Add(Location::NoLocation());
468 }
469 }
470 }
471
472 // Process temps.
473 for (intptr_t j = 0; j < locs->temp_count(); j++) {
474 Location temp = locs->temp(j);
475 if (temp.IsRegister()) {
476 BlockLocation(temp, pos);
477 } else if (temp.IsUnallocated()) {
478 UseInterval* temp_interval = new UseInterval(
479 kTempVirtualRegister, pos, pos + 1, NULL);
480 temp_interval->AddUse(NULL, pos, locs->temp_slot(j));
481 AddToUnallocated(temp_interval);
482 } else {
483 UNREACHABLE();
484 }
485 }
486
487 // Block all allocatable registers for calls.
488 if (locs->is_call()) {
489 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
490 BlockLocation(Location::RegisterLocation(static_cast<Register>(reg)),
491 pos);
492 }
493 }
494
495 if (locs->out().IsRegister()) {
496 builder_->Bailout("ssa allocator: fixed outputs are not supported");
497 }
498
499 Definition* def = current->AsDefinition();
500 if ((def != NULL) && (def->ssa_temp_index() >= 0)) {
501 Define(output_same_as_first_input ? current : NULL,
502 pos,
503 def->ssa_temp_index(),
504 locs->out_slot());
505 }
506
507 current = current->previous();
508 pos -= 2;
srdjan 2012/07/10 23:27:54 Why -2?
Vyacheslav Egorov (Google) 2012/07/11 13:27:58 Each instruction occupies two positions. See comme
srdjan 2012/07/11 17:22:31 Using -2, -1 is more readable in this case.
509 }
510
511 // If this block is a join we need to add destinations of phi
512 // resolution moves to phi's live range so that register allocator will
513 // fill them with moves.
514 if (block->IsJoinEntry() && block->AsJoinEntry()->phis() != NULL) {
515 ZoneGrowableArray<PhiInstr*>* phis = block->AsJoinEntry()->phis();
516
517 intptr_t move_idx = 0;
518 for (intptr_t j = 0; j < phis->length(); j++) {
519 PhiInstr* phi = (*phis)[j];
520 if (phi == NULL) continue;
521
522 const intptr_t def = phi->ssa_temp_index();
523 ASSERT(def != -1);
524
525 LiveRange* range = GetLiveRange(def);
526 range->DefineAt(NULL, pos, NULL);
527 UseInterval* interval = GetLiveRange(def)->head();
528
529 for (intptr_t k = 0; k < phi->InputCount(); k++) {
530 BlockEntryInstr* pred = block->PredecessorAt(k);
531 ASSERT(pred->last_instruction()->IsParallelMove());
532
533 Location* slot = pred->last_instruction()->AsParallelMove()->
534 moves()[move_idx].dest_slot();
535 *slot = Location::RequiresRegister();
536 interval->AddUse(NULL, pos, slot);
537 }
538
539 // All phi resolution moves are connected. Phi's live range is complete.
540 AddToUnallocated(interval);
541
542 move_idx++;
543 }
544 }
545 }
546 }
547
548
srdjan 2012/07/10 23:27:54 Add comment, something like: Computes positions of
Vyacheslav Egorov (Google) 2012/07/11 13:27:58 Added comment in the header file.
549 void FlowGraphAllocator::NumberInstructions() {
550 intptr_t pos = 0;
551
552 const intptr_t block_count = postorder_.length();
553 for (intptr_t i = block_count - 1; i >= 0; i--) {
554 BlockEntryInstr* block = postorder_[i];
555
556 block->set_start_pos(pos);
557 pos += 2;
558 Instruction* current = block->next();
559
560 Instruction* last = block->last_instruction();
561 if (!last->IsParallelMove()) last = last->next();
562
563 while (current != last) {
564 current->set_pos(pos);
565 current = current->next();
566 pos += 2;
srdjan 2012/07/10 23:27:54 Why +2?
Vyacheslav Egorov (Google) 2012/07/11 13:27:58 see above.
567 }
568 block->set_end_pos(pos);
569
570 // For join entry predecessors create phi resolution moves if
571 // necessary. They will be populated by the register allocator.
572 if (block->IsJoinEntry() && block->AsJoinEntry()->phi_count() > 0) {
srdjan 2012/07/10 23:27:54 parenthesis
Vyacheslav Egorov (Google) 2012/07/11 13:27:58 Done.
573 const intptr_t phi_count = block->AsJoinEntry()->phi_count();
574 for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
575 BlockEntryInstr* pred = block->PredecessorAt(i);
576 ASSERT(!pred->last_instruction()->IsParallelMove());
577
578 ParallelMoveInstr* move = new ParallelMoveInstr(phi_count);
579 move->set_next(block);
580 move->set_previous(pred->last_instruction());
581 pred->last_instruction()->set_next(move);
582 pred->set_last_instruction(move);
583
584 // Populate ParallelMove with empty moves.
585 for (intptr_t j = 0; j < phi_count; j++) {
586 move->AddMove(Location::NoLocation(), Location::NoLocation());
587 }
588 }
589 }
590 }
591 }
592
593
594 intptr_t UseInterval::Intersect(UseInterval* other) {
595 if (this->start() <= other->start()) {
596 if (other->start() < this->end()) return other->start();
597 } else if (this->start() < other->end()) {
598 return this->start();
599 }
600 return kIllegalPosition;
601 }
602
603
604 static intptr_t FirstIntersection(UseInterval* a, UseInterval* u) {
605 while (a != NULL && u != NULL) {
606 const intptr_t pos = a->Intersect(u);
607 if (pos != kIllegalPosition) return pos;
608
609 if (a->start() < u->start()) {
610 a = a->next_allocated();
611 } else {
612 u = u->next();
613 }
614 }
615
616 return kMaxPosition;
617 }
618
619
620 static Location LookAheadForHint(UseInterval* interval) {
621 UsePosition* use = interval->first_use();
622
623 while (use != NULL) {
624 if (use->HasHint()) return use->hint();
625 use = use->next();
626 }
627
628 return Location::NoLocation();
629 }
630
631
632 bool FlowGraphAllocator::AllocateFreeRegister(UseInterval* unallocated) {
633 Register candidate = kNoRegister;
634 intptr_t free_until = 0;
635
636 // If hint is available try hint first.
637 // TODO(vegorov): ensure that phis are hinted on the backedge.
638 Location hint = LookAheadForHint(unallocated);
639 if (!hint.IsInvalid()) {
640 ASSERT(hint.IsRegister());
641
642 if (cpu_regs_[hint.reg()] != kPermanentlyBlocked) {
643 free_until = FirstIntersection(cpu_regs_[hint.reg()], unallocated);
644 candidate = hint.reg();
645 }
646
647 TRACE_ALLOC(("found hint %s for %d: free until %d\n",
648 hint.Name(), unallocated->vreg(), free_until));
649 }
650
651 if (free_until != kMaxPosition) {
652 for (int reg = 0; reg < kNumberOfCpuRegisters; ++reg) {
653 if (cpu_regs_[reg] == NULL) {
654 candidate = static_cast<Register>(reg);
655 free_until = kMaxPosition;
656 break;
657 }
658 }
659 }
660
661 ASSERT(0 <= kMaxPosition);
662 if (free_until != kMaxPosition) {
663 for (int reg = 0; reg < kNumberOfCpuRegisters; ++reg) {
664 if (cpu_regs_[reg] == kPermanentlyBlocked) continue;
665 if (reg == candidate) continue;
666
667 const intptr_t pos = FirstIntersection(cpu_regs_[reg], unallocated);
668
669 if (pos > free_until) {
670 candidate = static_cast<Register>(reg);
671 free_until = pos;
672 if (free_until == kMaxPosition) break;
673 }
674 }
675 }
676
677 // All registers are blocked by active ranges.
678 if (free_until <= unallocated->start()) return false;
679
680 AssignFreeRegister(unallocated, candidate);
681 return true;
682 }
683
684
685 UseInterval* UseInterval::Split(intptr_t pos) {
686 if (pos == start()) return this;
687 ASSERT(Contains(pos));
688 UseInterval* tail = new UseInterval(vreg(), pos, end(), next());
689
690 UsePosition* use = uses_;
691 while (use != NULL && use->pos() <= pos) {
692 use = use->next();
693 }
694
695 tail->uses_ = use;
696
697 end_ = pos;
698
699 return tail;
700 }
701
702
703 void FlowGraphAllocator::AssignFreeRegister(UseInterval* unallocated,
704 Register reg) {
705 TRACE_ALLOC(("assigning free register %s to %d\n",
706 Location::RegisterLocation(reg).Name(),
707 unallocated->vreg()));
708
709 UseInterval* a = cpu_regs_[reg];
710 if (a == NULL) {
711 // Register is completely free.
712 cpu_regs_[reg] = unallocated;
713 return;
714 }
715
716 UseInterval* u = unallocated;
717 ASSERT(u->start() < a->start()); // Register is free.
718 cpu_regs_[reg] = u;
719 if (u->next() == NULL || u->next()->start() >= a->start()) {
720 u->set_next_allocated(a);
721 }
722
723 while (a != NULL && u != NULL) {
724 const intptr_t pos = a->Intersect(u);
725 if (pos != kIllegalPosition) {
726 // TODO(vegorov): split live ranges might require control flow resolution
727 // which is not implemented yet.
728 builder_->Bailout("ssa allocator: control flow resolution required");
729
730 TRACE_ALLOC((" splitting at %d\n", pos));
731 // Reached intersection
732 UseInterval* tail = u->Split(pos);
733 AddToUnallocated(tail);
734 ASSERT(tail == u || u->next_allocated() == a);
735 return;
736 }
737
738 if (a->start() < u->start()) {
739 if (a->next_allocated() == NULL) {
740 a->set_next_allocated(u);
741 break;
742 }
743
744 UseInterval* next = a->next_allocated();
745 if (next->start() > u->start()) {
746 a->set_next_allocated(u);
747 u->set_next_allocated(next);
748 }
749
750 a = next;
751 } else {
752 UseInterval* next = u->next();
753
754 if (next == NULL || next->start() >= a->start()) {
755 u->set_next_allocated(a);
756 }
757 u = next;
758 }
759 }
760 }
761
762
763 static void InsertMoveBefore(Instruction* instr, Location to, Location from) {
764 Instruction* prev = instr->previous();
765 ParallelMoveInstr* move = prev->AsParallelMove();
766 if (move == NULL) {
767 move = new ParallelMoveInstr(1);
768 move->set_next(prev->next());
769 prev->set_next(move);
770 move->next()->set_previous(move);
771 move->set_previous(prev);
772 }
773 move->AddMove(to, from);
774 }
775
776
777 void UsePosition::AssignLocation(Location loc) {
778 if (location_slot_ == NULL) return;
779
780 if (location_slot_->IsUnallocated()) {
781 if (location_slot_->policy() == Location::kSameAsFirstInput) {
782 Instruction* instr = this->instr();
783 LocationSummary* locs = instr->locs();
784 if (!locs->in(0).IsUnallocated()) {
785 InsertMoveBefore(instr, loc, locs->in(0));
786 }
787 locs->set_in(0, loc);
788 }
789 TRACE_ALLOC((" use at %d converted to %s\n", pos(), loc.Name()));
790 *location_slot_ = loc;
791 } else if (location_slot_->IsRegister()) {
792 InsertMoveBefore(this->instr(), *location_slot_, loc);
793 }
794 }
795
796
797 void FlowGraphAllocator::FinalizeInterval(UseInterval* interval, Location loc) {
798 if (interval->vreg() == kNoVirtualRegister) return;
799
800 TRACE_ALLOC(("assigning location %s to interval [%d, %d)\n", loc.Name(),
801 interval->start(), interval->end()));
802
803 for (UsePosition* use = interval->first_use();
804 use != NULL && use->pos() <= interval->end();
805 use = use->next()) {
806 use->AssignLocation(loc);
807 }
808 }
809
810
811 void FlowGraphAllocator::AdvanceActiveIntervals(const intptr_t start) {
812 for (int reg = 0; reg < kNumberOfCpuRegisters; reg++) {
813 if (cpu_regs_[reg] == NULL) continue;
814 if (cpu_regs_[reg] == kPermanentlyBlocked) continue;
815
816 UseInterval* a = cpu_regs_[reg];
817 while (a != NULL && a->end() <= start) {
818 FinalizeInterval(a,
819 Location::RegisterLocation(static_cast<Register>(reg)));
820 a = a->next_allocated();
821 }
822
823 cpu_regs_[reg] = a;
824 }
825 }
826
827
828 static inline bool ShouldBeAllocatedBefore(UseInterval* a, UseInterval* b) {
829 return a->start() <= b->start();
830 }
831
832
833 void FlowGraphAllocator::AddToUnallocated(UseInterval* chain) {
834 if (unallocated_.is_empty()) {
835 unallocated_.Add(chain);
836 return;
837 }
838
839 for (intptr_t i = unallocated_.length() - 1; i >= 0; i--) {
840 if (ShouldBeAllocatedBefore(chain, unallocated_[i])) {
841 unallocated_.InsertAt(i + 1, chain);
842 return;
843 }
844 }
845 unallocated_.InsertAt(0, chain);
846 }
847
848
849 bool FlowGraphAllocator::UnallocatedIsSorted() {
850 for (intptr_t i = unallocated_.length() - 1; i >= 1; i--) {
851 UseInterval* a = unallocated_[i];
852 UseInterval* b = unallocated_[i - 1];
853 if (!ShouldBeAllocatedBefore(a, b)) return false;
854 }
855 return true;
856 }
857
858
859 void FlowGraphAllocator::AllocateCPURegisters() {
860 ASSERT(UnallocatedIsSorted());
861
862 while (!unallocated_.is_empty()) {
863 UseInterval* range = unallocated_.Last();
864 unallocated_.RemoveLast();
865 const intptr_t start = range->start();
866 TRACE_ALLOC(("Processing interval chain for vreg %d starting at %d\n",
867 range->vreg(),
868 start));
869
870 // TODO(vegorov): eagerly spill liveranges without register uses.
871 AdvanceActiveIntervals(start);
872
873 if (!AllocateFreeRegister(range)) {
874 builder_->Bailout("ssa allocator: spilling required");
875 return;
876 }
877 }
878
879 // All allocation decisions were done.
880 ASSERT(unallocated_.is_empty());
881
882 // Finish allocation.
883 AdvanceActiveIntervals(kMaxPosition);
884 TRACE_ALLOC(("Allocation completed\n"));
885 }
886
887
888 void FlowGraphAllocator::AllocateRegisters() {
889 GraphEntryInstr* entry = block_order_[0]->AsGraphEntry();
890 ASSERT(entry != NULL);
891
892 for (intptr_t i = 0; i < entry->start_env()->values().length(); i++) {
893 if (entry->start_env()->values()[i]->IsUse()) {
894 builder_->Bailout("ssa allocator: unsupported start environment");
895 }
896 }
897
898 AnalyzeLiveness();
899
900 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
901 cpu_regs_[reg] = NULL;
902 }
903
904 cpu_regs_[CTX] = kPermanentlyBlocked;
905 cpu_regs_[TMP] = kPermanentlyBlocked;
906 cpu_regs_[RSP] = kPermanentlyBlocked;
907 cpu_regs_[RBP] = kPermanentlyBlocked;
srdjan 2012/07/10 23:27:54 Use SPREG and FPREG instead of RSP and RBP.
Vyacheslav Egorov (Google) 2012/07/11 13:27:58 Done.
908
909 BuildLiveRanges();
910
911 if (FLAG_print_ssa_liveness) {
912 DumpLiveness();
913 }
914
915 if (FLAG_trace_ssa_allocator) {
916 PrintLiveRanges();
917 }
918
919 AllocateCPURegisters();
920
921 if (FLAG_trace_ssa_allocator) {
922 OS::Print("-- ir after allocation -------------------------\n");
923 FlowGraphPrinter printer(Function::Handle(), block_order_, true);
924 printer.PrintBlocks();
925 }
926 }
927
928
177 } // namespace dart 929 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698