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

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

Issue 10800037: New linear scan allocator. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: refactored liveness computation 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" 9 #include "vm/il_printer.h"
10 #include "vm/flow_graph_builder.h" 10 #include "vm/flow_graph_builder.h"
(...skipping 10 matching lines...) Expand all
21 #define TRACE_ALLOC(m) do { \ 21 #define TRACE_ALLOC(m) do { \
22 if (FLAG_trace_ssa_allocator) OS::Print m ; \ 22 if (FLAG_trace_ssa_allocator) OS::Print m ; \
23 } while (0) 23 } while (0)
24 #else 24 #else
25 #define TRACE_ALLOC(m) 25 #define TRACE_ALLOC(m)
26 #endif 26 #endif
27 27
28 28
29 static const intptr_t kNoVirtualRegister = -1; 29 static const intptr_t kNoVirtualRegister = -1;
30 static const intptr_t kTempVirtualRegister = -2; 30 static const intptr_t kTempVirtualRegister = -2;
31 static UseInterval* const kPermanentlyBlocked =
32 reinterpret_cast<UseInterval*>(-1);
33 static const intptr_t kIllegalPosition = -1; 31 static const intptr_t kIllegalPosition = -1;
34 static const intptr_t kMaxPosition = 0x7FFFFFFF; 32 static const intptr_t kMaxPosition = 0x7FFFFFFF;
35 33
36 34
35 static intptr_t MinPosition(intptr_t a, intptr_t b) {
36 return (a < b) ? a : b;
37 }
38
39
40 static bool IsParallelMovePosition(intptr_t pos) {
41 return (pos & 1) == 0;
42 }
43
44
45 static bool IsInstructionPosition(intptr_t pos) {
46 return (pos & 1) == 1;
47 }
48
49
50 static intptr_t ToParallelMove(intptr_t pos) {
51 return (pos & ~1);
52 }
53
37 FlowGraphAllocator::FlowGraphAllocator( 54 FlowGraphAllocator::FlowGraphAllocator(
38 const GrowableArray<BlockEntryInstr*>& block_order, 55 const GrowableArray<BlockEntryInstr*>& block_order,
39 FlowGraphBuilder* builder) 56 FlowGraphBuilder* builder)
40 : builder_(builder), 57 : builder_(builder),
41 block_order_(block_order), 58 block_order_(block_order),
42 postorder_(builder->postorder_block_entries()), 59 postorder_(builder->postorder_block_entries()),
43 live_out_(block_order.length()), 60 live_out_(block_order.length()),
44 kill_(block_order.length()), 61 kill_(block_order.length()),
45 live_in_(block_order.length()), 62 live_in_(block_order.length()),
46 vreg_count_(builder->current_ssa_temp_index()), 63 vreg_count_(builder->current_ssa_temp_index()),
47 live_ranges_(builder->current_ssa_temp_index()) { 64 live_ranges_(builder->current_ssa_temp_index()) {
48 for (intptr_t i = 0; i < vreg_count_; i++) live_ranges_.Add(NULL); 65 for (intptr_t i = 0; i < vreg_count_; i++) live_ranges_.Add(NULL);
49 66
50 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) { 67 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
51 cpu_regs_[reg] = NULL; 68 blocked_cpu_regs_[reg] = false;
52 } 69 }
53 70
54 cpu_regs_[CTX] = kPermanentlyBlocked; 71 blocked_cpu_regs_[CTX] = true;
55 if (TMP != kNoRegister) { 72 if (TMP != kNoRegister) {
56 cpu_regs_[TMP] = kPermanentlyBlocked; 73 blocked_cpu_regs_[TMP] = true;
57 } 74 }
58 cpu_regs_[SPREG] = kPermanentlyBlocked; 75 blocked_cpu_regs_[SPREG] = true;
59 cpu_regs_[FPREG] = kPermanentlyBlocked; 76 blocked_cpu_regs_[FPREG] = true;
60 } 77 }
61 78
62 79
63 void FlowGraphAllocator::ComputeInitialSets() { 80 void FlowGraphAllocator::ComputeInitialSets() {
64 const intptr_t block_count = postorder_.length(); 81 const intptr_t block_count = postorder_.length();
65 for (intptr_t i = 0; i < block_count; i++) { 82 for (intptr_t i = 0; i < block_count; i++) {
66 BlockEntryInstr* block = postorder_[i]; 83 BlockEntryInstr* block = postorder_[i];
67 84
68 BitVector* kill = kill_[i]; 85 BitVector* kill = kill_[i];
69 BitVector* live_in = live_in_[i]; 86 BitVector* live_in = live_in_[i];
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 } 223 }
207 OS::Print("\n"); 224 OS::Print("\n");
208 225
209 PrintBitVector(" live out", live_out_[i]); 226 PrintBitVector(" live out", live_out_[i]);
210 PrintBitVector(" kill", kill_[i]); 227 PrintBitVector(" kill", kill_[i]);
211 PrintBitVector(" live in", live_in_[i]); 228 PrintBitVector(" live in", live_in_[i]);
212 } 229 }
213 } 230 }
214 231
215 232
216 void UseInterval::Print() { 233 void LiveRange::AddUse(intptr_t pos,
217 OS::Print(" [%d, %d) uses {", start_, end_); 234 Location* location_slot) {
srdjan 2012/07/22 23:27:33 Args should fit into one line.
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
218 for (UsePosition* use_pos = uses_; 235 ASSERT((first_use_interval_->start_ <= pos) &&
219 use_pos != NULL && use_pos->pos() <= end(); 236 (pos <= first_use_interval_->end_));
220 use_pos = use_pos->next()) {
221 if (use_pos != uses_) OS::Print(", ");
222 OS::Print("%d", use_pos->pos());
223 }
224 OS::Print("}\n");
225 }
226
227
228 void UseInterval::AddUse(Instruction* instr,
229 intptr_t pos,
230 Location* location_slot) {
231 ASSERT((start_ <= pos) && (pos <= end_));
232 ASSERT((instr == NULL) || (instr->lifetime_position() == pos));
233 if ((uses_ != NULL) && (uses_->pos() == pos)) { 237 if ((uses_ != NULL) && (uses_->pos() == pos)) {
234 if ((location_slot == NULL) || (uses_->location_slot() == location_slot)) { 238 if ((location_slot == NULL) || (uses_->location_slot() == location_slot)) {
235 return; 239 return;
236 } else if ((uses_->location_slot() == NULL) && (instr == NULL)) { 240 } else if (uses_->location_slot() == NULL) {
237 uses_->set_location_slot(location_slot); 241 uses_->set_location_slot(location_slot);
238 return; 242 return;
239 } 243 }
240 } 244 }
241 uses_ = new UsePosition(instr, pos, uses_, location_slot); 245 uses_ = new UsePosition(pos, uses_, location_slot);
242 } 246 }
243 247
244 248
245 void LiveRange::Print() { 249 void LiveRange::AddUseInterval(intptr_t start, intptr_t end) {
246 OS::Print("vreg %d live intervals:\n", vreg_); 250 if (first_use_interval_ != NULL) {
srdjan 2012/07/22 23:27:33 Add comment: do not add if the first_use_interval_
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Added comment.
247 for (UseInterval* interval = head_; 251 if (first_use_interval_->start_ == start) {
248 interval != NULL; 252 ASSERT(end <= first_use_interval_->end_);
249 interval = interval->next_) { 253 return;
250 interval->Print(); 254 } else if (first_use_interval_->start_ == end) {
255 first_use_interval_->start_ = start;
srdjan 2012/07/22 23:27:33 Do you want to do that even if start < first_use_i
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 This is not shrinking this is expansion. UseInterv
256 return;
257 }
258 }
259
260 first_use_interval_ = new UseInterval(start, end, first_use_interval_);
261 if (last_use_interval_ == NULL) last_use_interval_ = first_use_interval_;
srdjan 2012/07/22 23:27:33 ASSERT(last_use_interval_->next() == NULL) thus ma
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
262 }
263
264
265 void LiveRange::DefineAt(intptr_t pos) {
266 if (first_use_interval_ != NULL) {
srdjan 2012/07/22 23:27:33 I would revert, test for positive first, makes it
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
267 ASSERT(first_use_interval_->start_ <= pos);
268 first_use_interval_->start_ = pos;
269 } else {
270 // Definition without a use.
271 first_use_interval_ = new UseInterval(pos, pos + 1, NULL);
272 last_use_interval_ = first_use_interval_;
251 } 273 }
252 } 274 }
253 275
254 276
255 void LiveRange::AddUseInterval(intptr_t start, intptr_t end) {
256 if ((head_ != NULL) && (head_->start_ == end)) {
257 head_->start_ = start;
258 return;
259 }
260
261 head_ = new UseInterval(vreg_, start, end, head_);
262 }
263
264
265 void LiveRange::DefineAt(Instruction* instr, intptr_t pos, Location* loc) {
266 if (head_ != NULL) {
267 ASSERT(head_->start_ <= pos);
268 head_->start_ = pos;
269 } else {
270 // Definition without a use.
271 head_ = new UseInterval(vreg_, pos, pos + 1, NULL);
272 }
273 head_->AddUse(instr, pos, loc);
274 }
275
276
277 // TODO(vegorov): encode use_at_start vs. use_at_end in the location itself?
278 void LiveRange::UseAt(Instruction* instr,
279 intptr_t def, intptr_t use,
280 bool use_at_end,
281 Location* loc) {
282 if (head_ == NULL || head_->start_ != def) {
283 AddUseInterval(def, use + (use_at_end ? 1 : 0));
284 }
285 head_->AddUse(instr, use, loc);
286 }
287
288
289 LiveRange* FlowGraphAllocator::GetLiveRange(intptr_t vreg) { 277 LiveRange* FlowGraphAllocator::GetLiveRange(intptr_t vreg) {
290 if (live_ranges_[vreg] == NULL) { 278 if (live_ranges_[vreg] == NULL) {
291 live_ranges_[vreg] = new LiveRange(vreg); 279 live_ranges_[vreg] = new LiveRange(vreg);
292 } 280 }
293 return live_ranges_[vreg]; 281 return live_ranges_[vreg];
294 } 282 }
295 283
296 284
297 void FlowGraphAllocator::BlockLocation(Location loc, intptr_t pos) { 285 void FlowGraphAllocator::BlockLocation(Location loc,
286 intptr_t from,
287 intptr_t to) {
298 ASSERT(loc.IsRegister()); 288 ASSERT(loc.IsRegister());
299 const Register reg = loc.reg(); 289 const Register reg = loc.reg();
300 UseInterval* last = cpu_regs_[reg]; 290 if (blocked_cpu_regs_[reg]) return;
srdjan 2012/07/22 23:27:33 == NULL
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 blocked_cpu_regs_[reg] is a bool value.
301 if (last == kPermanentlyBlocked) return; 291 if (cpu_regs_[reg].length() == 0) {
302 if ((last != NULL) && (last->start() == pos)) return; 292 cpu_regs_[reg].Add(new LiveRange(kNoVirtualRegister));
303 cpu_regs_[reg] = new UseInterval(kNoVirtualRegister, pos, pos + 1, last); 293 }
304 } 294 cpu_regs_[reg][0]->AddUseInterval(from, to);
305 295 }
306 296
307 void FlowGraphAllocator::Define(Instruction* instr, 297
308 intptr_t pos, 298 void LiveRange::Print() {
309 intptr_t vreg, 299 OS::Print(" live range v%d [%d, %d)\n", vreg(), Start(), End());
310 Location* loc) { 300 UsePosition* use_pos = uses_;
311 LiveRange* range = GetLiveRange(vreg); 301 for (UseInterval* interval = first_use_interval_;
312 ASSERT(loc != NULL); 302 interval != NULL;
313 if (loc->IsRegister()) { 303 interval = interval->next()) {
314 BlockLocation(*loc, pos); 304 OS::Print(" use interval [%d, %d)\n",
315 range->DefineAt(instr, pos + 1, loc); 305 interval->start(),
316 } else if (loc->IsUnallocated()) { 306 interval->end());
317 range->DefineAt(instr, pos, loc); 307 while ((use_pos != NULL) && (use_pos->pos() <= interval->end())) {
318 } else { 308 OS::Print(" use at %d as %s\n",
319 UNREACHABLE(); 309 use_pos->pos(),
320 } 310 (use_pos->location_slot() == NULL)
321 311 ? "-" : use_pos->location_slot()->Name());
322 AddToUnallocated(range->head()); 312 use_pos = use_pos->next();
323 } 313 }
324 314 }
325 315
326 void FlowGraphAllocator::UseValue(Instruction* instr, 316 if (next_sibling() != NULL) {
327 intptr_t def_pos, 317 next_sibling()->Print();
328 intptr_t use_pos,
329 intptr_t vreg,
330 Location* loc,
331 bool use_at_end) {
332 LiveRange* range = GetLiveRange(vreg);
333 if (loc == NULL) {
334 range->UseAt(NULL, def_pos, use_pos, true, loc);
335 } else if (loc->IsRegister()) {
336 // We have a fixed use.
337 BlockLocation(*loc, use_pos);
338 range->UseAt(instr, def_pos, use_pos, false, loc);
339 } else if (loc->IsUnallocated()) {
340 ASSERT(loc->policy() == Location::kRequiresRegister);
341 range->UseAt(use_at_end ? NULL : instr, def_pos, use_pos, use_at_end, loc);
342 }
343 }
344
345
346 static void PrintChain(UseInterval* chain) {
347 if (chain == kPermanentlyBlocked) {
348 OS::Print(" not for allocation\n");
349 return;
350 }
351
352 while (chain != NULL) {
353 chain->Print();
354 chain = chain->next();
355 } 318 }
356 } 319 }
357 320
358 321
359 void FlowGraphAllocator::PrintLiveRanges() { 322 void FlowGraphAllocator::PrintLiveRanges() {
360 for (intptr_t i = 0; i < unallocated_.length(); i++) { 323 for (intptr_t i = 0; i < unallocated_.length(); i++) {
361 OS::Print("unallocated chain for vr%d\n", unallocated_[i]->vreg()); 324 unallocated_[i]->Print();
362 PrintChain(unallocated_[i]);
363 } 325 }
364 326
365 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) { 327 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
366 OS::Print("blocking chain for %s\n", 328 if (blocked_cpu_regs_[reg]) continue;
329 if (cpu_regs_[reg].length() == 0) continue;
330
331 ASSERT(cpu_regs_[reg].length() == 1);
332 OS::Print("blocking live range for %s\n",
367 Location::RegisterLocation(static_cast<Register>(reg)).Name()); 333 Location::RegisterLocation(static_cast<Register>(reg)).Name());
368 PrintChain(cpu_regs_[reg]); 334 cpu_regs_[reg][0]->Print();
369 } 335 }
336 }
337
338
339 Instruction* FlowGraphAllocator::ConnectOutgoingPhiMoves(
340 BlockEntryInstr* block) {
srdjan 2012/07/22 15:09:24 indent 4 spaces
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
341 Instruction* last = block->last_instruction();
342
343 GotoInstr* goto_instr = last->AsGoto();
344 if (goto_instr == NULL) return last;
srdjan 2012/07/22 23:27:33 What about branches?
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Branches cannot have outgoing phi moves this viola
345
346 // If we have a parallel move here then the successor block must be a
347 // join with phis. The phi inputs contribute uses to each predecessor
348 // block (and the phi outputs contribute definitions in the successor
349 // block).
350 ParallelMoveInstr* parallel_move = goto_instr->previous()->AsParallelMove();
351 if (parallel_move == NULL) return last->previous();
srdjan 2012/07/22 23:27:33 Since last can survive here only as goto_instr, re
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
352
353 // All uses are recorded at the position of parallel move preceding goto.
354 const intptr_t pos = goto_instr->lifetime_position() - 1;
355 ASSERT((pos >= 0) && IsParallelMovePosition(pos));
356
357 JoinEntryInstr* join = goto_instr->successor();
358 ASSERT(join != NULL);
359
360 // Search for the index of the current block in the predecessors of
361 // the join.
362 // TODO(kmillikin): record the predecessor index in the goto when
363 // building the predecessor list to avoid this search.
364 intptr_t pred_idx = 0;
365 for (; pred_idx < join->PredecessorCount(); pred_idx++) {
366 if (join->PredecessorAt(pred_idx) == block) break;
367 }
368 ASSERT(pred_idx < join->PredecessorCount());
srdjan 2012/07/22 23:27:33 class JoinInstr has a method that does the above:
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 I guess this code predates the method. Thanks.
369
370 // Record the corresponding phi input use for each phi.
371 ZoneGrowableArray<PhiInstr*>* phis = join->phis();
372 intptr_t move_idx = 0;
373 for (intptr_t phi_idx = 0; phi_idx < phis->length(); phi_idx++) {
374 PhiInstr* phi = (*phis)[phi_idx];
375 if (phi == NULL) continue;
376
377 Value* val = phi->InputAt(pred_idx);
378 MoveOperands* move = parallel_move->MoveOperandsAt(move_idx);
379 if (val->IsUse()) {
380 // Expected shape of live ranges (m is parallel move, g is goto
381 // instruction):
382 //
383 // m g
384 // value --*
385 //
srdjan 2012/07/22 15:09:24 I do not understand the comment above. I see you a
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
386
387 LiveRange* range = GetLiveRange(
388 val->AsUse()->definition()->ssa_temp_index());
srdjan 2012/07/22 15:09:24 4 spaces indent. 2 spaces is used only when starti
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
389
390 Location* slot = move->src_slot();
391 *slot = Location::PrefersRegister();
392
393 range->AddUseInterval(block->start_pos(), pos);
394 range->AddUse(pos, slot);
srdjan 2012/07/22 23:27:33 Rewrite: move->set_src(Location::Prefersregister(
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
395 } else {
396 ASSERT(val->IsConstant());
397 move->set_src(Location::Constant(val->AsConstant()->value()));
398 }
399 move_idx++;
400 }
401
402 // Begin backward iteration with the instruction before the parallel
403 // move.
404 return parallel_move->previous();
405 }
406
407
408 void FlowGraphAllocator::ConnectIncommingPhiMoves(BlockEntryInstr* block) {
srdjan 2012/07/22 23:27:33 s/Incomming/Incoming/
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
409 // If this block is a join we need to add destinations of phi
410 // resolution moves to phi's live range so that register allocator will
411 // fill them with moves.
412 JoinEntryInstr* join = block->AsJoinEntry();
413 if (join == NULL) return;
414
415 // All uses are recorded at the start position in the block.
416 const intptr_t pos = block->start_pos();
417
srdjan 2012/07/22 23:27:33 s/block/join/
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
418 ZoneGrowableArray<PhiInstr*>* phis = join->phis();
419 if (phis != NULL) {
420 intptr_t move_idx = 0;
421 for (intptr_t j = 0; j < phis->length(); j++) {
422 PhiInstr* phi = (*phis)[j];
423 if (phi == NULL) continue;
424
425 const intptr_t vreg = phi->ssa_temp_index();
426 ASSERT(vreg != -1);
427
428 // Expected shape of live range (B is the block start):
429 //
430 // B
431 // phi [--------
432 //
srdjan 2012/07/22 15:09:24 ditto
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
433
434 LiveRange* range = GetLiveRange(vreg);
435 range->DefineAt(pos); // Shorten live range.
436
437 for (intptr_t k = 0; k < phi->InputCount(); k++) {
438 BlockEntryInstr* pred = block->PredecessorAt(k);
439 ASSERT(pred->last_instruction()->IsGoto());
440 Instruction* move_instr = pred->last_instruction()->previous();
441 ASSERT(move_instr->IsParallelMove());
442
443 MoveOperands* move =
444 move_instr->AsParallelMove()->MoveOperandsAt(move_idx);
srdjan 2012/07/22 23:27:33 indent 4
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
445 move->set_dest(Location::PrefersRegister());
446 range->AddUse(pos, move->dest_slot());
447 }
448
449 // All phi resolution moves are connected. Phi's live range is
450 // complete.
451 AddToUnallocated(range);
452
453 move_idx++;
454 }
455 }
456 }
457
458
459 void FlowGraphAllocator::ProcessOneInstruction(BlockEntryInstr* block,
460 Instruction* current) {
srdjan 2012/07/22 23:27:33 ProcessOneInstruction is not self explanatory, pel
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
461 const intptr_t pos = current->lifetime_position();
462 ASSERT(IsInstructionPosition(pos));
463
464 LocationSummary* locs = current->locs();
465
466 // TODO(vegorov): number of inputs must match number of input locations.
467 if (locs->input_count() != current->InputCount()) {
468 builder_->Bailout("ssa allocator: number of input locations mismatch");
469 }
470
471 const bool output_same_as_first_input =
472 locs->out().IsUnallocated() &&
473 (locs->out().policy() == Location::kSameAsFirstInput);
srdjan 2012/07/22 23:27:33 Indent 4
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
474
475 // Add uses from the deoptimization environment.
476 if (current->env() != NULL) {
477 // Any value mentioned in the deoptimization environment should survive
478 // until the end of instruction but it does not need to be in the register.
479 // Expected shape of live range:
480 //
481 // m i m
482 // value -----*--)
483 //
484
485 const GrowableArray<Value*>& values = current->env()->values();
486 GrowableArray<Location>* locations = current->env()->locations();
487
488 for (intptr_t j = 0; j < values.length(); j++) {
489 Value* val = values[j];
490 if (val->IsUse()) {
491 locations->Add(Location::Any());
492 const intptr_t vreg = val->AsUse()->definition()->ssa_temp_index();
493
494 LiveRange* range = GetLiveRange(vreg);
495 range->AddUseInterval(block->start_pos(), pos + 1);
496 range->AddUse(pos, &(*locations)[j]);
497 } else {
498 ASSERT(val->IsConstant());
499 locations->Add(Location::NoLocation());
500 }
501 }
502 }
503
504 // Process inputs.
505 // Skip the first input if output is specified with kSameAsFirstInput policy,
506 // they will be processed together at the very end.
507 for (intptr_t j = output_same_as_first_input ? 1 : 0;
508 j < current->InputCount();
509 j++) {
510 Value* input = current->InputAt(j);
511 ASSERT(input->IsUse()); // Can not be a constant currently.
512
513 const intptr_t vreg = input->AsUse()->definition()->ssa_temp_index();
514 LiveRange* range = GetLiveRange(vreg);
515
516 Location* in_ref = locs->in_slot(j);
517
518 if (in_ref->IsRegister()) {
519 // Input is expected in a fixed register. Expected shape of
520 // live ranges:
521 //
522 // m i m
523 // value --*
524 // register [-----)
525 //
526 MoveOperands* move =
527 AddMoveAt(pos - 1, *in_ref, Location::PrefersRegister());
srdjan 2012/07/22 23:27:33 4 spaces
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
528 BlockLocation(*in_ref, pos - 1, pos + 1);
529 range->AddUseInterval(block->start_pos(), pos - 1);
530 range->AddUse(pos - 1, move->src_slot());
531 } else {
532 // Normal unallocated input. Expected shape of
533 // live ranges:
534 //
535 // m i m
536 // value -----*--)
537 //
538 ASSERT(in_ref->IsUnallocated());
539 range->AddUseInterval(block->start_pos(), pos + 1);
540 range->AddUse(pos, in_ref);
541 }
542 }
543
544 // Process temps.
545 for (intptr_t j = 0; j < locs->temp_count(); j++) {
546 // Expected shape of live range:
547 //
548 // m i m
549 // [--)
550 //
551
552 Location temp = locs->temp(j);
553 if (temp.IsRegister()) {
554 BlockLocation(temp, pos, pos + 1);
555 } else if (temp.IsUnallocated()) {
556 LiveRange* range = new LiveRange(kTempVirtualRegister);
557 range->AddUseInterval(pos, pos + 1);
558 range->AddUse(pos, locs->temp_slot(j));
559 AddToUnallocated(range);
560 } else {
561 UNREACHABLE();
562 }
563 }
564
565 // Block all allocatable registers for calls.
566 if (locs->is_call()) {
567 // Expected shape of live range:
568 //
569 // m i m
570 // [--)
571 //
572
573 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
574 BlockLocation(Location::RegisterLocation(static_cast<Register>(reg)),
575 pos,
576 pos + 1);
577 }
578
579 #ifdef DEBUG
580 // Verify that temps, inputs and output were specified as fixed
581 // locations. Every register is blocked now so attempt to
582 // allocate will not succeed.
583 for (intptr_t j = 0; j < locs->temp_count(); j++) {
584 ASSERT(!locs->temp(j).IsUnallocated());
585 }
586
587 for (intptr_t j = 0; j < locs->input_count(); j++) {
588 ASSERT(!locs->in(j).IsUnallocated());
589 }
590
591 ASSERT(!locs->out().IsUnallocated());
592 #endif
593 }
594
595 Definition* def = current->AsDefinition();
596 if ((def == NULL) || (def->ssa_temp_index() < 0)) {
597 ASSERT(locs->out().IsInvalid());
598 return;
599 }
600
601 LiveRange* range = GetLiveRange(def->ssa_temp_index());
602 Location* out = locs->out_slot();
603
604 // Process output and finalize its liverange.
605 if (out->IsRegister()) {
606 // Fixed output location. Expected shape of live range:
607 //
608 // m i m
609 // register [--)
610 // output [-------
611 //
612 BlockLocation(*out, pos, pos + 1);
613 // We need to emit move connecting out to some other place.
614 // Interesting case is: fixed output + fixed input last use:
615
616 UsePosition* use = range->first_use();
617 if (use->pos() == (pos + 1)) {
618 // We have a use position on the parallel move.
619 ASSERT(use->location_slot()->IsUnallocated());
620 *(use->location_slot()) = *out;
621 }
622
623 // Shorten live range to the point of definition, this might make the range
624 // empty (if the only use immediately follows). If range is not empty add
625 // move from a fixed register to an unallocated location.
626 range->DefineAt(pos + 1);
627 if (range->Start() == range->End()) return;
628
629 MoveOperands* move = AddMoveAt(pos + 1, Location::PrefersRegister(), *out);
630 range->AddUse(pos - 1, move->dest_slot());
631 } else if (output_same_as_first_input) {
632 // Output register will contain a value of the first input at instruction's
633 // start. Expected shape of live ranges:
634 //
635 // m i m
636 // input #0 --*
637 // output [--*----
638 //
639 ASSERT(locs->in_slot(0)->Equals(Location::RequiresRegister()));
640
641 // Create move that will copy value between input and output.
642 locs->set_out(Location::RequiresRegister());
643 MoveOperands* move = AddMoveAt(pos - 1,
644 Location::RequiresRegister(),
645 Location::PrefersRegister());
646
647 // Add uses to the live range of the input.
648 Value* input = current->InputAt(0);
649 ASSERT(input->IsUse()); // Can not be a constant currently.
650 LiveRange* input_range = GetLiveRange(
651 input->AsUse()->definition()->ssa_temp_index());
652 input_range->AddUseInterval(block->start_pos(), pos - 1);
653 input_range->AddUse(pos - 1, move->src_slot());
654
655 // Shorten output live range to the point of definition and add both input
656 // and output uses slots to be filled by allocator.
657 range->DefineAt(pos - 1);
658 range->AddUse(pos - 1, out);
659 range->AddUse(pos - 1, move->dest_slot());
660 range->AddUse(pos, locs->in_slot(0));
661 } else {
662 // Normal unallocated location that requires a register. Expected shape of
663 // live range:
664 //
665 // m i m
666 // output [-------
667 //
668 ASSERT(out->IsUnallocated() &&
669 (out->policy() == Location::kRequiresRegister));
670
671 // Shorten live range to the point of definition and add use to be filled by
672 // allocator.
673 range->DefineAt(pos);
674 range->AddUse(pos, out);
675 }
676
677 AddToUnallocated(range);
370 } 678 }
371 679
372 680
373 void FlowGraphAllocator::BuildLiveRanges() { 681 void FlowGraphAllocator::BuildLiveRanges() {
374 NumberInstructions(); 682 NumberInstructions();
375 683
376 const intptr_t block_count = postorder_.length(); 684 const intptr_t block_count = postorder_.length();
377 for (intptr_t i = 0; i < block_count; i++) { 685 ASSERT(postorder_[block_count - 1]->IsGraphEntry());
686 for (intptr_t i = 0; i < (block_count - 1); i++) {
378 BlockEntryInstr* block = postorder_[i]; 687 BlockEntryInstr* block = postorder_[i];
379 688
380 // For every SSA value that is live out of this block create an interval 689 // For every SSA value that is live out of this block create an interval
381 // that covers the hole block. It will be shortened if we encounter a 690 // that covers the hole block. It will be shortened if we encounter a
382 // definition of this value in this block. 691 // definition of this value in this block.
383 for (BitVector::Iterator it(live_out_[i]); !it.Done(); it.Advance()) { 692 for (BitVector::Iterator it(live_out_[i]); !it.Done(); it.Advance()) {
384 LiveRange* range = GetLiveRange(it.Current()); 693 LiveRange* range = GetLiveRange(it.Current());
385 range->AddUseInterval(block->start_pos(), block->end_pos()); 694 range->AddUseInterval(block->start_pos(), block->end_pos());
386 } 695 }
387 696
388 // Position corresponding to the beginning of the last instruction in the 697 // Connect outgoing phi-moves that were created in NumberInstructions
389 // block. 698 // and find last instruction that contributes to liveness.
390 intptr_t pos = block->end_pos() - 1; 699 Instruction* current = ConnectOutgoingPhiMoves(block);
391 Instruction* current = block->last_instruction(); 700
392 701 // Now process all instructions in reverse order.
393 // Goto instructions do not contribute liveness information. 702 while (current != block) {
394 GotoInstr* goto_instr = current->AsGoto(); 703 // Skip parallel moves that we insert while processing instructions.
395 if (goto_instr != NULL) { 704 if (!current->IsParallelMove()) {
705 ProcessOneInstruction(block, current);
706 }
396 current = current->previous(); 707 current = current->previous();
397 // If we have a parallel move here then the successor block must be a 708 }
398 // join with phis. The phi inputs contribute uses to each predecessor 709
399 // block (and the phi outputs contribute definitions in the successor 710 ConnectIncommingPhiMoves(block);
400 // block). 711 }
401 // 712 }
402 // We record those uses at the end of the instruction preceding the 713
403 // parallel move. This position is 'pos', because we do not assign 714
404 // instruction numbers to parallel moves. 715 static ParallelMoveInstr* CreateParallelMoveBefore(Instruction* instr,
405 ParallelMoveInstr* parallel_move = current->AsParallelMove(); 716 intptr_t pos) {
406 if (parallel_move != NULL) { 717 Instruction* prev = instr->previous();
407 JoinEntryInstr* join = goto_instr->successor(); 718 ParallelMoveInstr* move = prev->AsParallelMove();
408 ASSERT(join != NULL); 719 if ((move == NULL) || (move->lifetime_position() != pos)) {
409 720 move = new ParallelMoveInstr();
410 // Search for the index of the current block in the predecessors of 721 move->set_next(prev->next());
411 // the join. 722 prev->set_next(move);
412 // TODO(kmillikin): record the predecessor index in the goto when 723 move->next()->set_previous(move);
413 // building the predecessor list to avoid this search. 724 move->set_previous(prev);
414 intptr_t pred_idx = 0; 725 move->set_lifetime_position(pos);
415 for (; pred_idx < join->PredecessorCount(); pred_idx++) { 726 }
416 if (join->PredecessorAt(pred_idx) == block) break; 727 return move;
417 } 728 }
418 ASSERT(pred_idx < join->PredecessorCount()); 729
419 730
420 // Record the corresponding phi input use for each phi. 731 static ParallelMoveInstr* CreateParallelMoveAfter(Instruction* instr,
421 ZoneGrowableArray<PhiInstr*>* phis = join->phis(); 732 intptr_t pos) {
422 intptr_t move_idx = 0; 733 Instruction* next = instr->next();
423 for (intptr_t phi_idx = 0; phi_idx < phis->length(); phi_idx++) { 734 if (next->IsParallelMove() && (next->lifetime_position() == pos)) {
424 PhiInstr* phi = (*phis)[phi_idx]; 735 return next->AsParallelMove();
425 if (phi == NULL) continue; 736 }
426 737 return CreateParallelMoveBefore(next, pos);
427 Value* val = phi->InputAt(pred_idx); 738 }
428 MoveOperands* move = parallel_move->MoveOperandsAt(move_idx); 739
429 if (val->IsUse()) { 740
430 const intptr_t virtual_register =
431 val->AsUse()->definition()->ssa_temp_index();
432 move->set_src(Location::RequiresRegister());
433 GetLiveRange(
434 virtual_register)->head()->AddUse(NULL, pos, move->src_slot());
435 } else {
436 ASSERT(val->IsConstant());
437 move->set_src(Location::Constant(val->AsConstant()->value()));
438 }
439 move_idx++;
440 }
441
442 // Begin backward iteration with the instruction before the parallel
443 // move.
444 current = current->previous();
445 }
446 }
447
448 // Now process all instructions in reverse order.
449 --pos; // 'pos' is now the start position for the current instruction.
450 while (current != block) {
451 LocationSummary* locs = current->locs();
452
453 const bool output_same_as_first_input =
454 locs->out().IsUnallocated() &&
455 locs->out().policy() == Location::kSameAsFirstInput;
456
457 // TODO(vegorov): number of inputs should match number of input locations.
458 // TODO(vegorov): generic support for writable registers?
459 for (intptr_t j = 0; j < current->InputCount(); j++) {
460 Value* input = current->InputAt(j);
461 if (input->IsUse()) {
462 const intptr_t use = input->AsUse()->definition()->ssa_temp_index();
463
464 Location* in_ref = (j < locs->input_count()) ?
465 locs->in_slot(j) : NULL;
466 const bool use_at_end = (j > 0) || (in_ref == NULL) ||
467 !output_same_as_first_input;
468 UseValue(current, block->start_pos(), pos, use, in_ref, use_at_end);
469 }
470 }
471
472 // Add uses from the deoptimization environment.
473 // TODO(vegorov): these uses should _not_ require register but for now
474 // they do because we don't support spilling at all.
475 if (current->env() != NULL) {
476 const GrowableArray<Value*>& values = current->env()->values();
477 GrowableArray<Location>* locations = current->env()->locations();
478
479 for (intptr_t j = 0; j < values.length(); j++) {
480 Value* val = values[j];
481 if (val->IsUse()) {
482 locations->Add(Location::RequiresRegister());
483 const intptr_t use = val->AsUse()->definition()->ssa_temp_index();
484 UseValue(current,
485 block->start_pos(),
486 pos,
487 use,
488 &(*locations)[j],
489 true);
490 } else {
491 locations->Add(Location::NoLocation());
492 }
493 }
494 }
495
496 // Process temps.
497 for (intptr_t j = 0; j < locs->temp_count(); j++) {
498 Location temp = locs->temp(j);
499 if (temp.IsRegister()) {
500 BlockLocation(temp, pos);
501 } else if (temp.IsUnallocated()) {
502 UseInterval* temp_interval = new UseInterval(
503 kTempVirtualRegister, pos, pos + 1, NULL);
504 temp_interval->AddUse(NULL, pos, locs->temp_slot(j));
505 AddToUnallocated(temp_interval);
506 } else {
507 UNREACHABLE();
508 }
509 }
510
511 // Block all allocatable registers for calls.
512 if (locs->is_call()) {
513 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
514 BlockLocation(Location::RegisterLocation(static_cast<Register>(reg)),
515 pos);
516 }
517 }
518
519 if (locs->out().IsRegister()) {
520 builder_->Bailout("ssa allocator: fixed outputs are not supported");
521 }
522
523 Definition* def = current->AsDefinition();
524 if ((def != NULL) && (def->ssa_temp_index() >= 0)) {
525 Define(output_same_as_first_input ? current : NULL,
526 pos,
527 def->ssa_temp_index(),
528 locs->out_slot());
529 }
530
531 current = current->previous();
532 pos -= 2;
533 }
534
535 // If this block is a join we need to add destinations of phi
536 // resolution moves to phi's live range so that register allocator will
537 // fill them with moves.
538 JoinEntryInstr* join = block->AsJoinEntry();
539 if (join != NULL) {
540 ZoneGrowableArray<PhiInstr*>* phis = join->phis();
541 if (phis != NULL) {
542 intptr_t move_idx = 0;
543 for (intptr_t j = 0; j < phis->length(); j++) {
544 PhiInstr* phi = (*phis)[j];
545 if (phi == NULL) continue;
546
547 const intptr_t virtual_register = phi->ssa_temp_index();
548 ASSERT(virtual_register != -1);
549
550 LiveRange* range = GetLiveRange(virtual_register);
551 range->DefineAt(NULL, pos, NULL);
552 UseInterval* interval = GetLiveRange(virtual_register)->head();
553
554 for (intptr_t k = 0; k < phi->InputCount(); k++) {
555 BlockEntryInstr* pred = block->PredecessorAt(k);
556 ASSERT(pred->last_instruction()->IsGoto());
557 Instruction* move_instr = pred->last_instruction()->previous();
558 ParallelMoveInstr* pmove = move_instr->AsParallelMove();
559 ASSERT(pmove != NULL);
560
561 MoveOperands* move_operands = pmove->MoveOperandsAt(move_idx);
562 move_operands->set_dest(Location::RequiresRegister());
563 interval->AddUse(NULL, pos, move_operands->dest_slot());
564 }
565
566 // All phi resolution moves are connected. Phi's live range is
567 // complete.
568 AddToUnallocated(interval);
569
570 move_idx++;
571 }
572 }
573 }
574 }
575 }
576
577
578 // Linearize the control flow graph. The chosen order will be used by the 741 // Linearize the control flow graph. The chosen order will be used by the
579 // linear-scan register allocator. Number most instructions with a pair of 742 // linear-scan register allocator. Number most instructions with a pair of
580 // numbers representing lifetime positions. Introduce explicit parallel 743 // numbers representing lifetime positions. Introduce explicit parallel
581 // move instructions in the predecessors of join nodes. The moves are used 744 // move instructions in the predecessors of join nodes. The moves are used
582 // for phi resolution. 745 // for phi resolution.
583 void FlowGraphAllocator::NumberInstructions() { 746 void FlowGraphAllocator::NumberInstructions() {
584 intptr_t pos = 0; 747 intptr_t pos = 0;
585 748
586 // The basic block order is reverse postorder. 749 // The basic block order is reverse postorder.
587 const intptr_t block_count = postorder_.length(); 750 const intptr_t block_count = postorder_.length();
588 for (intptr_t i = block_count - 1; i >= 0; i--) { 751 for (intptr_t i = block_count - 1; i >= 0; i--) {
589 BlockEntryInstr* block = postorder_[i]; 752 BlockEntryInstr* block = postorder_[i];
753
754 instructions_.Add(block);
590 block->set_start_pos(pos); 755 block->set_start_pos(pos);
591 block->set_lifetime_position(pos); 756 block->set_lifetime_position(pos + 1);
592 pos += 2; 757 pos += 2;
758
593 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { 759 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
594 Instruction* current = it.Current(); 760 Instruction* current = it.Current();
595 // Do not assign numbers to parallel moves or goto instructions. 761 // Do not assign numbers to parallel move instructions.
596 if (!current->IsParallelMove() && !current->IsGoto()) { 762 if (!current->IsParallelMove()) {
597 current->set_lifetime_position(pos); 763 instructions_.Add(current);
764 current->set_lifetime_position(pos + 1);
598 pos += 2; 765 pos += 2;
599 } 766 }
600 } 767 }
601 block->set_end_pos(pos); 768 block->set_end_pos(pos);
602 769
603 // For join entry predecessors create phi resolution moves if 770 // For join entry predecessors create phi resolution moves if
604 // necessary. They will be populated by the register allocator. 771 // necessary. They will be populated by the register allocator.
605 JoinEntryInstr* join = block->AsJoinEntry(); 772 JoinEntryInstr* join = block->AsJoinEntry();
606 if ((join != NULL) && (join->phi_count() > 0)) { 773 if ((join != NULL) && (join->phi_count() > 0)) {
607 const intptr_t phi_count = join->phi_count(); 774 const intptr_t phi_count = join->phi_count();
608 for (intptr_t i = 0; i < block->PredecessorCount(); i++) { 775 for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
609 ParallelMoveInstr* move = new ParallelMoveInstr(); 776 // Insert the move between the last two instructions of the
777 // predecessor block (all such blocks have at least two instructions:
778 // the block entry and goto instructions.)
779 Instruction* last = block->PredecessorAt(i)->last_instruction();
780 ParallelMoveInstr* move =
781 CreateParallelMoveBefore(last, last->lifetime_position() - 1);
782
610 // Populate the ParallelMove with empty moves. 783 // Populate the ParallelMove with empty moves.
611 for (intptr_t j = 0; j < phi_count; j++) { 784 for (intptr_t j = 0; j < phi_count; j++) {
612 move->AddMove(Location::NoLocation(), Location::NoLocation()); 785 move->AddMove(Location::NoLocation(), Location::NoLocation());
613 } 786 }
614
615 // Insert the move between the last two instructions of the
616 // predecessor block (all such blocks have at least two instructions:
617 // the block entry and goto instructions.)
618 BlockEntryInstr* pred = block->PredecessorAt(i);
619 Instruction* next = pred->last_instruction();
620 Instruction* previous = next->previous();
621 ASSERT(next->IsGoto());
622 ASSERT(!previous->IsParallelMove());
623 previous->set_next(move);
624 move->set_previous(previous);
625 move->set_next(next);
626 next->set_previous(move);
627 } 787 }
628 } 788 }
629 } 789 }
630 } 790 }
631 791
632 792
793 Instruction* FlowGraphAllocator::InstructionAt(intptr_t pos) {
794 return instructions_[pos >> 1];
srdjan 2012/07/22 23:27:33 pos / 2 is clearer, IMHO.
Vyacheslav Egorov (Google) 2012/07/24 12:26:42 Done.
795 }
796
797
798 bool FlowGraphAllocator::IsBlockEntry(intptr_t pos) {
799 return InstructionAt(pos)->IsBlockEntry();
800 }
801
802
803 static UsePosition* FirstUseAfter(UsePosition* use, intptr_t after) {
804 while ((use != NULL) && (use->pos() < after)) {
805 use = use->next();
806 }
807 return use;
808 }
809
810
811 Location AllocationFinger::FirstHint() {
812 UsePosition* use = first_hinted_use_;
813
814 while (use != NULL) {
815 if (use->HasHint()) return use->hint();
816 use = use->next();
817 }
818
819 return Location::NoLocation();
820 }
821
822
823 UsePosition* AllocationFinger::FirstRegisterUse(intptr_t after) {
824 for (UsePosition* use = FirstUseAfter(first_register_use_, after);
825 use != NULL;
826 use = use->next()) {
827 Location* loc = use->location_slot();
828 if ((loc != NULL) &&
829 loc->IsUnallocated() &&
830 (loc->policy() == Location::kRequiresRegister)) {
831 first_register_use_ = use;
832 return use;
833 }
834 }
835 return NULL;
836 }
837
838
839 UsePosition* AllocationFinger::FirstRegisterBeneficialUse(intptr_t after) {
840 for (UsePosition* use = FirstUseAfter(first_register_beneficial_use_, after);
841 use != NULL;
842 use = use->next()) {
843 Location* loc = use->location_slot();
844 if ((loc != NULL) &&
845 (loc->IsRegister() ||
846 (loc->IsUnallocated() && loc->IsRegisterBeneficial()))) {
847 first_register_beneficial_use_ = use;
848 return use;
849 }
850 }
851 return NULL;
852 }
853
854
633 intptr_t UseInterval::Intersect(UseInterval* other) { 855 intptr_t UseInterval::Intersect(UseInterval* other) {
634 if (this->start() <= other->start()) { 856 if (this->start() <= other->start()) {
635 if (other->start() < this->end()) return other->start(); 857 if (other->start() < this->end()) return other->start();
636 } else if (this->start() < other->end()) { 858 } else if (this->start() < other->end()) {
637 return this->start(); 859 return this->start();
638 } 860 }
639 return kIllegalPosition; 861 return kIllegalPosition;
640 } 862 }
641 863
642 864
643 static intptr_t FirstIntersection(UseInterval* a, UseInterval* u) { 865 static intptr_t FirstIntersection(UseInterval* a, UseInterval* u) {
644 while (a != NULL && u != NULL) { 866 while (a != NULL && u != NULL) {
645 const intptr_t pos = a->Intersect(u); 867 const intptr_t pos = a->Intersect(u);
646 if (pos != kIllegalPosition) return pos; 868 if (pos != kIllegalPosition) return pos;
647 869
648 if (a->start() < u->start()) { 870 if (a->start() < u->start()) {
649 a = a->next_allocated(); 871 a = a->next();
650 } else { 872 } else {
651 u = u->next(); 873 u = u->next();
652 } 874 }
653 } 875 }
654 876
655 return kMaxPosition; 877 return kMaxPosition;
656 } 878 }
657 879
658 880
659 static Location LookAheadForHint(UseInterval* interval) { 881 LiveRange* LiveRange::MakeTemp(intptr_t pos, Location* location_slot) {
660 UsePosition* use = interval->first_use(); 882 UNREACHABLE();
661 883 return NULL;
662 while (use != NULL) {
663 if (use->HasHint()) return use->hint();
664 use = use->next();
665 }
666
667 return Location::NoLocation();
668 } 884 }
669 885
670 886
671 bool FlowGraphAllocator::AllocateFreeRegister(UseInterval* unallocated) { 887 LiveRange* LiveRange::SplitAt(intptr_t split_pos) {
888 if (Start() == split_pos) return this;
889
890 // Ranges can only be connected by parallel moves.
891 split_pos = ToParallelMove(split_pos);
892
893 UseInterval* interval = finger_.first_pending_use_interval();
894 ASSERT(interval->start() < split_pos);
895
896 // Corner case. We need to start over to find previous interval.
897 if (interval->start() == split_pos) interval = first_use_interval_;
898
899 UseInterval* last_before_split = NULL;
900 while (interval->end() <= split_pos) {
901 last_before_split = interval;
902 interval = interval->next();
903 }
904
905 const bool split_at_start = (interval->start() == split_pos);
906
907 UseInterval* first_after_split = interval;
908 if (!split_at_start && interval->Contains(split_pos)) {
909 first_after_split = new UseInterval(split_pos,
910 interval->end(),
911 interval->next());
912 interval->end_ = split_pos;
913 interval->next_ = first_after_split;
914 last_before_split = interval;
915 }
916
917 ASSERT(last_before_split->next() == first_after_split);
918 ASSERT(last_before_split->end() <= split_pos);
919 ASSERT(split_pos <= first_after_split->start());
920
921 UsePosition* last_use_before_split = NULL;
922 UsePosition* use = uses_;
923 if (split_at_start) {
924 while ((use != NULL) && (use->pos() < split_pos)) {
925 last_use_before_split = use;
926 use = use->next();
927 }
928 } else {
929 while ((use != NULL) && (use->pos() <= split_pos)) {
930 last_use_before_split = use;
931 use = use->next();
932 }
933 }
934 UsePosition* first_use_after_split = use;
935
936 if (last_use_before_split == NULL) {
937 uses_ = NULL;
938 } else {
939 last_use_before_split->set_next(NULL);
940 }
941
942 UseInterval* last_use_interval = (last_before_split == last_use_interval_) ?
943 first_after_split : last_use_interval_;
944 next_sibling_ = new LiveRange(vreg(),
945 first_use_after_split,
946 first_after_split,
947 last_use_interval,
948 next_sibling_);
949
950 TRACE_ALLOC((" split sibling [%d, %d)\n",
951 next_sibling_->Start(), next_sibling_->End()));
952
953 // Split sibling can only start at a parallel move.
954 ASSERT(IsParallelMovePosition(next_sibling_->Start()));
955
956 last_use_interval_ = last_before_split;
957 last_use_interval_->next_ = NULL;
958 return next_sibling_;
959 }
960
961
962 LiveRange* FlowGraphAllocator::SplitBetween(LiveRange* range,
963 intptr_t from,
964 intptr_t to) {
965 // TODO(vegorov): select optimal split position based on loop structure.
966 TRACE_ALLOC(("split %d [%d, %d) between [%d, %d)\n",
967 range->vreg(), range->Start(), range->End(), from, to));
968 return range->SplitAt(to);
969 }
970
971
972 void FlowGraphAllocator::SpillBetween(LiveRange* range,
973 intptr_t from,
974 intptr_t to) {
975 ASSERT(from < to);
976 TRACE_ALLOC(("spill %d [%d, %d) between [%d, %d)\n",
977 range->vreg(), range->Start(), range->End(), from, to));
978 LiveRange* tail = range->SplitAt(from);
979
980 if (tail->Start() < to) {
981 // There is an intersection of tail and [from, to).
982 LiveRange* tail_tail = SplitBetween(tail, tail->Start(), to);
983 Spill(tail);
984 AddToUnallocated(tail_tail);
985 } else {
986 // No intersection between tail and [from, to).
987 AddToUnallocated(tail);
988 }
989 }
990
991
992 void FlowGraphAllocator::SpillAfter(LiveRange* range, intptr_t from) {
993 TRACE_ALLOC(("spill %d [%d, %d) after %d\n",
994 range->vreg(), range->Start(), range->End(), from));
995 LiveRange* tail = range->SplitAt(from);
996 Spill(tail);
997 }
998
999
1000 intptr_t FlowGraphAllocator::AllocateSpillSlotFor(LiveRange* range) {
1001 for (intptr_t i = 0; i < spill_slots_.length(); i++) {
1002 if (spill_slots_[i] <= range->Start()) {
1003 return i;
1004 }
1005 }
1006 spill_slots_.Add(0);
1007 return spill_slots_.length() - 1;
1008 }
1009
1010
1011 void FlowGraphAllocator::Spill(LiveRange* range) {
1012 const intptr_t spill_index = AllocateSpillSlotFor(range);
1013 ASSERT(spill_slots_[spill_index] < range->Start());
1014 spill_slots_[spill_index] = range->End();
1015 range->set_assigned_location(Location::SpillSlot(spill_index));
1016 ConvertAllUses(range);
1017 }
1018
1019
1020 intptr_t FlowGraphAllocator::FirstIntersectionWithAllocated(
1021 Register reg, LiveRange* unallocated) {
1022 intptr_t intersection = kMaxPosition;
1023 for (intptr_t i = 0; i < cpu_regs_[reg].length(); i++) {
1024 LiveRange* allocated = cpu_regs_[reg][i];
1025 if (allocated == NULL) continue;
1026
1027 UseInterval* allocated_head =
1028 allocated->finger()->first_pending_use_interval();
1029 if (allocated_head->start() >= intersection) continue;
1030
1031 const intptr_t pos = FirstIntersection(
1032 unallocated->finger()->first_pending_use_interval(),
1033 allocated_head);
1034 if (pos < intersection) intersection = pos;
1035 }
1036 return intersection;
1037 }
1038
1039
1040
1041 bool FlowGraphAllocator::AllocateFreeRegister(LiveRange* unallocated) {
672 Register candidate = kNoRegister; 1042 Register candidate = kNoRegister;
673 intptr_t free_until = 0; 1043 intptr_t free_until = 0;
674 1044
675 // If hint is available try hint first. 1045 // If hint is available try hint first.
676 // TODO(vegorov): ensure that phis are hinted on the backedge. 1046 // TODO(vegorov): ensure that phis are hinted on the back edge.
677 Location hint = LookAheadForHint(unallocated); 1047 Location hint = unallocated->finger()->FirstHint();
678 if (!hint.IsInvalid()) { 1048 if (!hint.IsInvalid()) {
679 ASSERT(hint.IsRegister()); 1049 ASSERT(hint.IsRegister());
680 1050
681 if (cpu_regs_[hint.reg()] != kPermanentlyBlocked) { 1051 if (!blocked_cpu_regs_[hint.reg()]) {
682 free_until = FirstIntersection(cpu_regs_[hint.reg()], unallocated); 1052 free_until = FirstIntersectionWithAllocated(hint.reg(), unallocated);
683 candidate = hint.reg(); 1053 candidate = hint.reg();
684 } 1054 }
685 1055
686 TRACE_ALLOC(("found hint %s for %d: free until %d\n", 1056 TRACE_ALLOC(("found hint %s for %d: free until %d\n",
687 hint.Name(), unallocated->vreg(), free_until)); 1057 hint.Name(), unallocated->vreg(), free_until));
688 } 1058 }
689 1059
690 if (free_until != kMaxPosition) { 1060 if (free_until != kMaxPosition) {
691 for (int reg = 0; reg < kNumberOfCpuRegisters; ++reg) { 1061 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; ++reg) {
692 if (cpu_regs_[reg] == NULL) { 1062 if (!blocked_cpu_regs_[reg] && cpu_regs_[reg].length() == 0) {
693 candidate = static_cast<Register>(reg); 1063 candidate = static_cast<Register>(reg);
694 free_until = kMaxPosition; 1064 free_until = kMaxPosition;
695 break; 1065 break;
696 } 1066 }
697 } 1067 }
698 } 1068 }
699 1069
700 ASSERT(0 <= kMaxPosition); 1070 ASSERT(0 <= kMaxPosition);
701 if (free_until != kMaxPosition) { 1071 if (free_until != kMaxPosition) {
702 for (int reg = 0; reg < kNumberOfCpuRegisters; ++reg) { 1072 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; ++reg) {
703 if (cpu_regs_[reg] == kPermanentlyBlocked) continue; 1073 if (blocked_cpu_regs_[reg] || (reg == candidate)) continue;
704 if (reg == candidate) continue; 1074
705 1075 const intptr_t intersection =
706 const intptr_t pos = FirstIntersection(cpu_regs_[reg], unallocated); 1076 FirstIntersectionWithAllocated(static_cast<Register>(reg), unallocated);
707 1077
708 if (pos > free_until) { 1078 if (intersection > free_until) {
709 candidate = static_cast<Register>(reg); 1079 candidate = static_cast<Register>(reg);
710 free_until = pos; 1080 free_until = intersection;
711 if (free_until == kMaxPosition) break; 1081 if (free_until == kMaxPosition) break;
712 } 1082 }
713 } 1083 }
714 } 1084 }
715 1085
1086 if (free_until != kMaxPosition) free_until = ToParallelMove(free_until);
1087
716 // All registers are blocked by active ranges. 1088 // All registers are blocked by active ranges.
717 if (free_until <= unallocated->start()) return false; 1089 if (free_until <= unallocated->Start()) return false;
718 1090
719 AssignFreeRegister(unallocated, candidate); 1091 TRACE_ALLOC(("assigning free register %s to %d\n",
1092 Location::RegisterLocation(candidate).Name(),
1093 unallocated->vreg()));
1094
1095 if (free_until != kMaxPosition) {
1096 // There was an intersection. Split unallocated.
1097 TRACE_ALLOC((" splitting at %d\n", free_until));
1098 LiveRange* tail = unallocated->SplitAt(free_until);
1099 AddToUnallocated(tail);
1100 }
1101
1102 cpu_regs_[candidate].Add(unallocated);
1103 unallocated->set_assigned_location(Location::RegisterLocation(candidate));
1104
720 return true; 1105 return true;
721 } 1106 }
722 1107
723 1108
724 UseInterval* UseInterval::Split(intptr_t pos) { 1109 void FlowGraphAllocator::AllocateAnyRegister(LiveRange* unallocated) {
725 if (pos == start()) return this; 1110 UsePosition* register_use =
726 ASSERT(Contains(pos)); 1111 unallocated->finger()->FirstRegisterUse(unallocated->Start());
727 UseInterval* tail = new UseInterval(vreg(), pos, end(), next()); 1112 if (register_use == NULL) {
728 1113 Spill(unallocated);
729 UsePosition* use = uses_; 1114 return;
730 while (use != NULL && use->pos() <= pos) { 1115 }
731 use = use->next(); 1116
732 } 1117 Register candidate = kNoRegister;
733 1118 intptr_t free_until = 0;
734 tail->uses_ = use; 1119 intptr_t blocked_at = kMaxPosition;
735 1120
736 end_ = pos; 1121 for (int reg = 0; reg < kNumberOfCpuRegisters; ++reg) {
737 1122 if (blocked_cpu_regs_[reg]) continue;
738 return tail; 1123 if (UpdateFreeUntil(static_cast<Register>(reg),
739 } 1124 unallocated,
740 1125 &free_until,
741 1126 &blocked_at)) {
742 void FlowGraphAllocator::AssignFreeRegister(UseInterval* unallocated, 1127 candidate = static_cast<Register>(reg);
743 Register reg) { 1128 }
744 TRACE_ALLOC(("assigning free register %s to %d\n", 1129 }
1130
1131 if (free_until < register_use->pos()) {
1132 // Can't acquire free register. Spill until we really need one.
1133 ASSERT(unallocated->Start() < ToParallelMove(register_use->pos()));
1134 SpillBetween(unallocated, unallocated->Start(), register_use->pos());
1135 return;
1136 }
1137
1138 if (blocked_at < unallocated->End()) {
1139 LiveRange* tail = SplitBetween(unallocated,
1140 unallocated->Start(),
1141 blocked_at);
1142 AddToUnallocated(tail);
1143 }
1144
1145 AssignBlockedRegister(unallocated, candidate);
1146 }
1147
1148
1149 bool FlowGraphAllocator::UpdateFreeUntil(Register reg,
1150 LiveRange* unallocated,
1151 intptr_t* cur_free_until,
1152 intptr_t* cur_blocked_at) {
1153 intptr_t free_until = kMaxPosition;
1154 intptr_t blocked_at = kMaxPosition;
1155 const intptr_t start = unallocated->Start();
1156
1157 for (intptr_t i = 0; i < cpu_regs_[reg].length(); i++) {
1158 LiveRange* allocated = cpu_regs_[reg][i];
1159
1160 UseInterval* first_pending_use_interval =
1161 allocated->finger()->first_pending_use_interval();
1162 if (first_pending_use_interval->Contains(start)) {
1163 // This is an active interval.
1164 if (allocated->vreg() <= 0) {
1165 // This register blocked by an interval that
1166 // can't be spilled.
1167 return false;
1168 }
1169
1170 const UsePosition* use =
1171 allocated->finger()->FirstRegisterBeneficialUse(unallocated->Start());
1172
1173 if ((use != NULL) && ((use->pos() - start) <= 1)) {
1174 // This register is blocked by interval that is used
1175 // as register in the current instruction and can't
1176 // be spilled.
1177 return false;
1178 }
1179
1180 const intptr_t use_pos = (use != NULL) ? use->pos()
1181 : allocated->End();
1182
1183 if (use_pos < free_until) free_until = use_pos;
1184 } else {
1185 // This is inactive interval.
1186 const intptr_t intersection = FirstIntersection(
1187 first_pending_use_interval, unallocated->first_use_interval());
1188 if (intersection != kMaxPosition) {
1189 if (intersection < free_until) free_until = intersection;
1190 if (allocated->vreg() == kNoVirtualRegister) blocked_at = intersection;
1191 }
1192 }
1193
1194 if (free_until <= *cur_free_until) {
1195 return false;
1196 }
1197 }
1198
1199 ASSERT(free_until > *cur_free_until);
1200 *cur_free_until = free_until;
1201 *cur_blocked_at = blocked_at;
1202 return true;
1203 }
1204
1205
1206 void FlowGraphAllocator::RemoveEvicted(Register reg, intptr_t first_evicted) {
1207 intptr_t to = first_evicted;
1208 intptr_t from = first_evicted + 1;
1209 while (from < cpu_regs_[reg].length()) {
1210 LiveRange* allocated = cpu_regs_[reg][from++];
1211 if (allocated != NULL) cpu_regs_[reg][to++] = allocated;
1212 }
1213 cpu_regs_[reg].TruncateTo(to);
1214 }
1215
1216
1217 void FlowGraphAllocator::AssignBlockedRegister(LiveRange* unallocated,
1218 Register reg) {
1219 TRACE_ALLOC(("assigning blocked register %s to live range %d\n",
745 Location::RegisterLocation(reg).Name(), 1220 Location::RegisterLocation(reg).Name(),
746 unallocated->vreg())); 1221 unallocated->vreg()));
747 1222
748 UseInterval* a = cpu_regs_[reg]; 1223 intptr_t first_evicted = -1;
749 if (a == NULL) { 1224 for (intptr_t i = cpu_regs_[reg].length() - 1; i >= 0; i--) {
750 // Register is completely free. 1225 LiveRange* allocated = cpu_regs_[reg][i];
751 cpu_regs_[reg] = unallocated; 1226 if (allocated->vreg() < 0) continue; // Can't be evicted.
1227 if (EvictIntersection(allocated,
1228 unallocated)) {
1229 cpu_regs_[reg][i] = NULL;
1230 first_evicted = i;
1231 }
1232 }
1233
1234 // Remove evicted ranges from the array.
1235 if (first_evicted != -1) RemoveEvicted(reg, first_evicted);
1236
1237 cpu_regs_[reg].Add(unallocated);
1238 unallocated->set_assigned_location(Location::RegisterLocation(reg));
1239 }
1240
1241
1242 bool FlowGraphAllocator::EvictIntersection(LiveRange* allocated,
1243 LiveRange* unallocated) {
1244 UseInterval* first_unallocated =
1245 unallocated->finger()->first_pending_use_interval();
1246 const intptr_t intersection = FirstIntersection(
1247 allocated->finger()->first_pending_use_interval(),
1248 first_unallocated);
1249 if (intersection == kMaxPosition) return false;
1250
1251 const intptr_t spill_position = first_unallocated->start();
1252 UsePosition* use = allocated->finger()->FirstRegisterUse(spill_position);
1253 if (use == NULL) {
1254 // No register uses after this point.
1255 SpillAfter(allocated, spill_position);
1256 } else {
1257 const intptr_t restore_position =
1258 (spill_position < intersection) ? MinPosition(intersection, use->pos())
1259 : use->pos();
1260
1261 SpillBetween(allocated, spill_position, restore_position);
1262 }
1263
1264 return true;
1265 }
1266
1267
1268 MoveOperands* FlowGraphAllocator::AddMoveAt(intptr_t pos,
1269 Location to,
1270 Location from) {
1271 ASSERT(IsParallelMovePosition(pos));
1272 Instruction* instr = InstructionAt(pos);
1273 ASSERT(!instr->IsBlockEntry());
1274 return CreateParallelMoveBefore(instr, pos)->AddMove(to, from);
1275 }
1276
1277
1278 void FlowGraphAllocator::ConvertUseTo(UsePosition* use, Location loc) {
1279 ASSERT(use->location_slot() != NULL);
1280 Location* slot = use->location_slot();
1281 ASSERT(slot->IsUnallocated());
1282 ASSERT((slot->policy() == Location::kRequiresRegister) ||
1283 (slot->policy() == Location::kPrefersRegister) ||
1284 (slot->policy() == Location::kAny));
1285 TRACE_ALLOC((" use at %d converted to %s\n", use->pos(), loc.Name()));
1286 *slot = loc;
1287 }
1288
1289
1290 void FlowGraphAllocator::ConvertAllUses(LiveRange* range) {
1291 if (range->vreg() == kNoVirtualRegister) return;
1292 TRACE_ALLOC(("range [%d, %d) for v%d has been allocated to %s:\n",
1293 range->Start(),
1294 range->End(),
1295 range->vreg(),
1296 range->assigned_location().Name()));
1297 ASSERT(!range->assigned_location().IsInvalid());
1298 const Location loc = range->assigned_location();
1299 for (UsePosition* use = range->first_use(); use != NULL; use = use->next()) {
1300 ConvertUseTo(use, loc);
1301 }
1302 }
1303
1304
1305 bool AllocationFinger::Advance(const intptr_t start) {
1306 UseInterval* a = first_pending_use_interval_;
1307 while (a != NULL && a->end() <= start) a = a->next();
1308 first_pending_use_interval_ = a;
1309 if (first_pending_use_interval_ == NULL) {
1310 return true;
1311 }
1312 return false;
1313 }
1314
1315
1316 void FlowGraphAllocator::AdvanceActiveIntervals(const intptr_t start) {
1317 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
1318 if (cpu_regs_[reg].length() == 0) continue;
1319
1320 intptr_t first_evicted = -1;
1321 for (intptr_t i = cpu_regs_[reg].length() - 1; i >= 0; i--) {
1322 LiveRange* range = cpu_regs_[reg][i];
1323 if (range->finger()->Advance(start)) {
1324 ConvertAllUses(range);
1325 cpu_regs_[reg][i] = NULL;
1326 first_evicted = i;
1327 }
1328 }
1329
1330 if (first_evicted != -1) {
1331 RemoveEvicted(static_cast<Register>(reg), first_evicted);
1332 }
1333 }
1334 }
1335
1336
1337 void AllocationFinger::Initialize(LiveRange* range) {
1338 first_pending_use_interval_ = range->first_use_interval();
1339 first_register_use_ = range->first_use();
1340 first_register_beneficial_use_ = range->first_use();
1341 first_hinted_use_ = range->first_use();
1342 }
1343
1344
1345 static inline bool ShouldBeAllocatedBefore(LiveRange* a, LiveRange* b) {
1346 return a->Start() <= b->Start();
1347 }
1348
1349
1350 void FlowGraphAllocator::AddToUnallocated(LiveRange* range) {
1351 range->finger()->Initialize(range);
1352
1353 if (unallocated_.is_empty()) {
1354 unallocated_.Add(range);
752 return; 1355 return;
753 } 1356 }
754 1357
755 UseInterval* u = unallocated; 1358 for (intptr_t i = unallocated_.length() - 1; i >= 0; i--) {
756 ASSERT(u->start() < a->start()); // Register is free. 1359 if (ShouldBeAllocatedBefore(range, unallocated_[i])) {
757 cpu_regs_[reg] = u; 1360 unallocated_.InsertAt(i + 1, range);
758 if (u->next() == NULL || u->next()->start() >= a->start()) {
759 u->set_next_allocated(a);
760 }
761
762 while (a != NULL && u != NULL) {
763 const intptr_t pos = a->Intersect(u);
764 if (pos != kIllegalPosition) {
765 // TODO(vegorov): split live ranges might require control flow resolution
766 // which is not implemented yet.
767 builder_->Bailout("ssa allocator: control flow resolution required");
768
769 TRACE_ALLOC((" splitting at %d\n", pos));
770 // Reached intersection
771 UseInterval* tail = u->Split(pos);
772 AddToUnallocated(tail);
773 ASSERT(tail == u || u->next_allocated() == a);
774 return; 1361 return;
775 } 1362 }
776 1363 }
777 if (a->start() < u->start()) { 1364 unallocated_.InsertAt(0, range);
778 if (a->next_allocated() == NULL) {
779 a->set_next_allocated(u);
780 break;
781 }
782
783 UseInterval* next = a->next_allocated();
784 if (next->start() > u->start()) {
785 a->set_next_allocated(u);
786 u->set_next_allocated(next);
787 }
788
789 a = next;
790 } else {
791 UseInterval* next = u->next();
792
793 if (next == NULL || next->start() >= a->start()) {
794 u->set_next_allocated(a);
795 }
796 u = next;
797 }
798 }
799 }
800
801
802 static void InsertMoveBefore(Instruction* instr, Location to, Location from) {
803 Instruction* prev = instr->previous();
804 ParallelMoveInstr* move = prev->AsParallelMove();
805 if (move == NULL) {
806 move = new ParallelMoveInstr();
807 move->set_next(prev->next());
808 prev->set_next(move);
809 move->next()->set_previous(move);
810 move->set_previous(prev);
811 }
812 move->AddMove(to, from);
813 }
814
815
816 void UsePosition::AssignLocation(Location loc) {
817 if (location_slot_ == NULL) return;
818
819 if (location_slot_->IsUnallocated()) {
820 if (location_slot_->policy() == Location::kSameAsFirstInput) {
821 Instruction* instr = this->instr();
822 LocationSummary* locs = instr->locs();
823 if (!locs->in(0).IsUnallocated()) {
824 InsertMoveBefore(instr, loc, locs->in(0));
825 }
826 locs->set_in(0, loc);
827 }
828 TRACE_ALLOC((" use at %d converted to %s\n", pos(), loc.Name()));
829 *location_slot_ = loc;
830 } else if (location_slot_->IsRegister()) {
831 InsertMoveBefore(this->instr(), *location_slot_, loc);
832 }
833 }
834
835
836 void FlowGraphAllocator::FinalizeInterval(UseInterval* interval, Location loc) {
837 if (interval->vreg() == kNoVirtualRegister) return;
838
839 TRACE_ALLOC(("assigning location %s to interval [%d, %d)\n", loc.Name(),
840 interval->start(), interval->end()));
841
842 for (UsePosition* use = interval->first_use();
843 use != NULL && use->pos() <= interval->end();
844 use = use->next()) {
845 use->AssignLocation(loc);
846 }
847 }
848
849
850 void FlowGraphAllocator::AdvanceActiveIntervals(const intptr_t start) {
851 for (int reg = 0; reg < kNumberOfCpuRegisters; reg++) {
852 if (cpu_regs_[reg] == NULL) continue;
853 if (cpu_regs_[reg] == kPermanentlyBlocked) continue;
854
855 UseInterval* a = cpu_regs_[reg];
856 while (a != NULL && a->end() <= start) {
857 FinalizeInterval(a,
858 Location::RegisterLocation(static_cast<Register>(reg)));
859 a = a->next_allocated();
860 }
861
862 cpu_regs_[reg] = a;
863 }
864 }
865
866
867 static inline bool ShouldBeAllocatedBefore(UseInterval* a, UseInterval* b) {
868 return a->start() <= b->start();
869 }
870
871
872 void FlowGraphAllocator::AddToUnallocated(UseInterval* chain) {
873 if (unallocated_.is_empty()) {
874 unallocated_.Add(chain);
875 return;
876 }
877
878 for (intptr_t i = unallocated_.length() - 1; i >= 0; i--) {
879 if (ShouldBeAllocatedBefore(chain, unallocated_[i])) {
880 unallocated_.InsertAt(i + 1, chain);
881 return;
882 }
883 }
884 unallocated_.InsertAt(0, chain);
885 } 1365 }
886 1366
887 1367
888 bool FlowGraphAllocator::UnallocatedIsSorted() { 1368 bool FlowGraphAllocator::UnallocatedIsSorted() {
889 for (intptr_t i = unallocated_.length() - 1; i >= 1; i--) { 1369 for (intptr_t i = unallocated_.length() - 1; i >= 1; i--) {
890 UseInterval* a = unallocated_[i]; 1370 LiveRange* a = unallocated_[i];
891 UseInterval* b = unallocated_[i - 1]; 1371 LiveRange* b = unallocated_[i - 1];
892 if (!ShouldBeAllocatedBefore(a, b)) return false; 1372 if (!ShouldBeAllocatedBefore(a, b)) return false;
893 } 1373 }
894 return true; 1374 return true;
895 } 1375 }
896 1376
897 1377
898 void FlowGraphAllocator::AllocateCPURegisters() { 1378 void FlowGraphAllocator::AllocateCPURegisters() {
899 ASSERT(UnallocatedIsSorted()); 1379 ASSERT(UnallocatedIsSorted());
900 1380
1381 for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
1382 if (cpu_regs_[i].length() == 1) {
1383 LiveRange* range = cpu_regs_[i][0];
1384 range->finger()->Initialize(range);
1385 }
1386 }
1387
901 while (!unallocated_.is_empty()) { 1388 while (!unallocated_.is_empty()) {
902 UseInterval* range = unallocated_.Last(); 1389 LiveRange* range = unallocated_.Last();
903 unallocated_.RemoveLast(); 1390 unallocated_.RemoveLast();
904 const intptr_t start = range->start(); 1391 const intptr_t start = range->Start();
905 TRACE_ALLOC(("Processing interval chain for vreg %d starting at %d\n", 1392 TRACE_ALLOC(("Processing live range for vreg %d starting at %d\n",
906 range->vreg(), 1393 range->vreg(),
907 start)); 1394 start));
908 1395
909 // TODO(vegorov): eagerly spill liveranges without register uses. 1396 // TODO(vegorov): eagerly spill liveranges without register uses.
910 AdvanceActiveIntervals(start); 1397 AdvanceActiveIntervals(start);
911 1398
912 if (!AllocateFreeRegister(range)) { 1399 if (!AllocateFreeRegister(range)) {
913 builder_->Bailout("ssa allocator: spilling required"); 1400 AllocateAnyRegister(range);
914 return;
915 } 1401 }
916 } 1402 }
917 1403
918 // All allocation decisions were done. 1404 // All allocation decisions were done.
919 ASSERT(unallocated_.is_empty()); 1405 ASSERT(unallocated_.is_empty());
920 1406
921 // Finish allocation. 1407 // Finish allocation.
922 AdvanceActiveIntervals(kMaxPosition); 1408 AdvanceActiveIntervals(kMaxPosition);
923 TRACE_ALLOC(("Allocation completed\n")); 1409 TRACE_ALLOC(("Allocation completed\n"));
924 } 1410 }
925 1411
926 1412
1413 void FlowGraphAllocator::ConnectSplitSiblings(LiveRange* range,
1414 BlockEntryInstr* source_block,
1415 BlockEntryInstr* target_block) {
1416 if (range->next_sibling() == NULL) {
1417 // Nothing to connect. The whole range was allocated to the same location.
1418 TRACE_ALLOC(("range %d has no siblings\n", range->vreg()));
1419 return;
1420 }
1421
1422 const intptr_t source_pos = source_block->end_pos() - 1;
1423 ASSERT(IsInstructionPosition(source_pos));
1424
1425 const intptr_t target_pos = target_block->start_pos();
1426
1427 Location target;
1428 Location source;
1429
1430 #ifdef DEBUG
1431 LiveRange* source_cover = NULL;
1432 LiveRange* target_cover = NULL;
1433 #endif
1434
1435 while ((range != NULL) && (source.IsInvalid() || target.IsInvalid())) {
1436 if (range->CanCover(source_pos)) {
1437 ASSERT(source.IsInvalid());
1438 source = range->assigned_location();
1439 #ifdef DEBUG
1440 source_cover = range;
1441 #endif
1442 }
1443 if (range->CanCover(target_pos)) {
1444 ASSERT(target.IsInvalid());
1445 target = range->assigned_location();
1446 #ifdef DEBUG
1447 target_cover = range;
1448 #endif
1449 }
1450
1451 range = range->next_sibling();
1452 }
1453
1454 TRACE_ALLOC(("connecting [%d, %d) [%s] to [%d, %d) [%s]\n",
1455 source_cover->Start(), source_cover->End(), source.Name(),
1456 target_cover->Start(), target_cover->End(), target.Name()));
1457
1458 // Siblings were allocated to the same register.
1459 if (source.Equals(target)) return;
1460
1461 Instruction* last = source_block->last_instruction();
1462 if (last->SuccessorCount() == 1) {
1463 CreateParallelMoveBefore(last, last->lifetime_position() - 1)->
1464 AddMove(target, source);
1465 } else {
1466 CreateParallelMoveAfter(target_block, target_block->start_pos())->
1467 AddMove(target, source);
1468 }
1469 }
1470
1471
1472 void FlowGraphAllocator::ResolveControlFlow() {
1473 // Resolve linear control flow between touching split siblings
1474 // inside basic blocks.
1475 for (intptr_t vreg = 0; vreg < live_ranges_.length(); vreg++) {
1476 LiveRange* range = live_ranges_[vreg];
1477 if (range == NULL) continue;
1478
1479 while (range->next_sibling() != NULL) {
1480 LiveRange* sibling = range->next_sibling();
1481 if ((range->End() == sibling->Start()) &&
1482 !range->assigned_location().Equals(sibling->assigned_location()) &&
1483 !IsBlockEntry(range->End())) {
1484 AddMoveAt(sibling->Start(),
1485 sibling->assigned_location(),
1486 range->assigned_location());
1487 }
1488 range = sibling;
1489 }
1490 }
1491
1492 // Resolve non-linear control flow across branches.
1493 for (intptr_t i = 1; i < block_order_.length(); i++) {
1494 BlockEntryInstr* block = block_order_[i];
1495 BitVector* live = live_in_[block->postorder_number()];
1496 for (BitVector::Iterator it(live); !it.Done(); it.Advance()) {
1497 LiveRange* range = GetLiveRange(it.Current());
1498 for (intptr_t j = 0; j < block->PredecessorCount(); j++) {
1499 ConnectSplitSiblings(range, block->PredecessorAt(j), block);
1500 }
1501 }
1502 }
1503 }
1504
1505
927 void FlowGraphAllocator::AllocateRegisters() { 1506 void FlowGraphAllocator::AllocateRegisters() {
928 GraphEntryInstr* entry = block_order_[0]->AsGraphEntry(); 1507 GraphEntryInstr* entry = block_order_[0]->AsGraphEntry();
929 ASSERT(entry != NULL); 1508 ASSERT(entry != NULL);
930 1509
931 for (intptr_t i = 0; i < entry->start_env()->values().length(); i++) { 1510 for (intptr_t i = 0; i < entry->start_env()->values().length(); i++) {
932 if (entry->start_env()->values()[i]->IsUse()) { 1511 if (entry->start_env()->values()[i]->IsUse()) {
933 builder_->Bailout("ssa allocator: unsupported start environment"); 1512 builder_->Bailout("ssa allocator: unsupported start environment");
934 } 1513 }
935 } 1514 }
936 1515
937 AnalyzeLiveness(); 1516 AnalyzeLiveness();
938 1517
939 BuildLiveRanges(); 1518 BuildLiveRanges();
940 1519
941 if (FLAG_print_ssa_liveness) { 1520 if (FLAG_print_ssa_liveness) {
942 DumpLiveness(); 1521 DumpLiveness();
943 } 1522 }
944 1523
945 if (FLAG_trace_ssa_allocator) { 1524 if (FLAG_trace_ssa_allocator) {
946 PrintLiveRanges(); 1525 PrintLiveRanges();
947 } 1526 }
948 1527
949 AllocateCPURegisters(); 1528 AllocateCPURegisters();
950 1529
1530 ResolveControlFlow();
1531
951 if (FLAG_trace_ssa_allocator) { 1532 if (FLAG_trace_ssa_allocator) {
952 OS::Print("-- ir after allocation -------------------------\n"); 1533 OS::Print("-- ir after allocation -------------------------\n");
953 FlowGraphPrinter printer(Function::Handle(), block_order_, true); 1534 FlowGraphPrinter printer(Function::Handle(), block_order_, true);
954 printer.PrintBlocks(); 1535 printer.PrintBlocks();
955 } 1536 }
956 } 1537 }
957 1538
958 1539
959 } // namespace dart 1540 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698