| OLD | NEW |
| 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 25 matching lines...) Expand all Loading... |
| 36 static intptr_t MinPosition(intptr_t a, intptr_t b) { | 36 static intptr_t MinPosition(intptr_t a, intptr_t b) { |
| 37 return (a < b) ? a : b; | 37 return (a < b) ? a : b; |
| 38 } | 38 } |
| 39 | 39 |
| 40 | 40 |
| 41 static bool IsParallelMovePosition(intptr_t pos) { | 41 static bool IsParallelMovePosition(intptr_t pos) { |
| 42 return (pos & 1) == 0; | 42 return (pos & 1) == 0; |
| 43 } | 43 } |
| 44 | 44 |
| 45 | 45 |
| 46 static bool IsInstructionPosition(intptr_t pos) { | 46 static bool IsInstructionStartPosition(intptr_t pos) { |
| 47 return (pos & 1) == 0; |
| 48 } |
| 49 |
| 50 |
| 51 static bool IsInstructionEndPosition(intptr_t pos) { |
| 47 return (pos & 1) == 1; | 52 return (pos & 1) == 1; |
| 48 } | 53 } |
| 49 | 54 |
| 50 | 55 |
| 51 static intptr_t ToParallelMove(intptr_t pos) { | 56 static intptr_t ToInstructionStart(intptr_t pos) { |
| 52 return (pos & ~1); | 57 return (pos & ~1); |
| 53 } | 58 } |
| 54 | 59 |
| 60 |
| 55 FlowGraphAllocator::FlowGraphAllocator( | 61 FlowGraphAllocator::FlowGraphAllocator( |
| 56 const GrowableArray<BlockEntryInstr*>& block_order, | 62 const GrowableArray<BlockEntryInstr*>& block_order, |
| 57 FlowGraphBuilder* builder) | 63 FlowGraphBuilder* builder) |
| 58 : builder_(builder), | 64 : builder_(builder), |
| 59 block_order_(block_order), | 65 block_order_(block_order), |
| 60 postorder_(builder->postorder_block_entries()), | 66 postorder_(builder->postorder_block_entries()), |
| 61 live_out_(block_order.length()), | 67 live_out_(block_order.length()), |
| 62 kill_(block_order.length()), | 68 kill_(block_order.length()), |
| 63 live_in_(block_order.length()), | 69 live_in_(block_order.length()), |
| 64 vreg_count_(builder->current_ssa_temp_index()), | 70 vreg_count_(builder->current_ssa_temp_index()), |
| (...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 316 | 322 |
| 317 | 323 |
| 318 LiveRange* FlowGraphAllocator::GetLiveRange(intptr_t vreg) { | 324 LiveRange* FlowGraphAllocator::GetLiveRange(intptr_t vreg) { |
| 319 if (live_ranges_[vreg] == NULL) { | 325 if (live_ranges_[vreg] == NULL) { |
| 320 live_ranges_[vreg] = new LiveRange(vreg); | 326 live_ranges_[vreg] = new LiveRange(vreg); |
| 321 } | 327 } |
| 322 return live_ranges_[vreg]; | 328 return live_ranges_[vreg]; |
| 323 } | 329 } |
| 324 | 330 |
| 325 | 331 |
| 326 void FlowGraphAllocator::BlockLocation(Location loc, | 332 void FlowGraphAllocator::BlockLocation(Location loc, intptr_t pos) { |
| 327 intptr_t from, | |
| 328 intptr_t to) { | |
| 329 ASSERT(loc.IsRegister()); | 333 ASSERT(loc.IsRegister()); |
| 330 const Register reg = loc.reg(); | 334 const Register reg = loc.reg(); |
| 331 if (blocked_cpu_regs_[reg]) return; | 335 if (blocked_cpu_regs_[reg]) return; |
| 332 if (cpu_regs_[reg].length() == 0) { | 336 if (cpu_regs_[reg].length() == 0) { |
| 333 cpu_regs_[reg].Add(new LiveRange(kNoVirtualRegister)); | 337 cpu_regs_[reg].Add(new LiveRange(kNoVirtualRegister)); |
| 334 } | 338 } |
| 335 cpu_regs_[reg][0]->AddUseInterval(from, to); | 339 cpu_regs_[reg][0]->AddUseInterval(pos, pos + 2); |
| 336 } | 340 } |
| 337 | 341 |
| 338 | 342 |
| 339 void LiveRange::Print() { | 343 void LiveRange::Print() { |
| 340 OS::Print(" live range v%d [%d, %d)\n", vreg(), Start(), End()); | 344 OS::Print(" live range v%d [%d, %d)\n", vreg(), Start(), End()); |
| 341 UsePosition* use_pos = uses_; | 345 UsePosition* use_pos = uses_; |
| 342 for (UseInterval* interval = first_use_interval_; | 346 for (UseInterval* interval = first_use_interval_; |
| 343 interval != NULL; | 347 interval != NULL; |
| 344 interval = interval->next()) { | 348 interval = interval->next()) { |
| 345 OS::Print(" use interval [%d, %d)\n", | 349 OS::Print(" use interval [%d, %d)\n", |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 438 } | 442 } |
| 439 ConvertAllUses(range); | 443 ConvertAllUses(range); |
| 440 } | 444 } |
| 441 } | 445 } |
| 442 } | 446 } |
| 443 | 447 |
| 444 // | 448 // |
| 445 // When describing shape of live ranges in comments below we are going to use | 449 // When describing shape of live ranges in comments below we are going to use |
| 446 // the following notation: | 450 // the following notation: |
| 447 // | 451 // |
| 448 // B block entry | 452 // B block entry |
| 449 // g goto instruction | 453 // g g' start and end of goto instruction |
| 450 // m parallel move | 454 // i i' start and end of any other instruction |
| 451 // i any other instruction | 455 // j j' start and end of any other instruction |
| 452 // | 456 |
| 453 // - body of a use interval | 457 // - body of a use interval |
| 454 // [ start of a use interval | 458 // [ start of a use interval |
| 455 // ) end of a use interval | 459 // ) end of a use interval |
| 456 // * use | 460 // * use |
| 457 // | 461 // |
| 458 // For example diagram | 462 // For example diagram |
| 459 // | 463 // |
| 460 // m i | 464 // i i' |
| 461 // value --*-) | 465 // value --*--) |
| 462 // | 466 // |
| 463 // can be read as: use interval for value starts somewhere before parallel move | 467 // can be read as: use interval for value starts somewhere before instruction |
| 464 // and extends until currently processed instruction, there is a use of value | 468 // and extends until currently processed instruction, there is a use of value |
| 465 // at a position of the parallel move. | 469 // at the start of the instruction. |
| 466 // | 470 // |
| 467 | 471 |
| 468 Instruction* FlowGraphAllocator::ConnectOutgoingPhiMoves( | 472 Instruction* FlowGraphAllocator::ConnectOutgoingPhiMoves( |
| 469 BlockEntryInstr* block) { | 473 BlockEntryInstr* block) { |
| 470 Instruction* last = block->last_instruction(); | 474 Instruction* last = block->last_instruction(); |
| 471 | 475 |
| 472 GotoInstr* goto_instr = last->AsGoto(); | 476 GotoInstr* goto_instr = last->AsGoto(); |
| 473 if (goto_instr == NULL) return last; | 477 if (goto_instr == NULL) return last; |
| 474 | 478 |
| 475 // If we have a parallel move here then the successor block must be a | 479 // If we have a parallel move here then the successor block must be a |
| 476 // join with phis. The phi inputs contribute uses to each predecessor | 480 // join with phis. The phi inputs contribute uses to each predecessor |
| 477 // block (and the phi outputs contribute definitions in the successor | 481 // block (and the phi outputs contribute definitions in the successor |
| 478 // block). | 482 // block). |
| 479 ParallelMoveInstr* parallel_move = goto_instr->previous()->AsParallelMove(); | 483 ParallelMoveInstr* parallel_move = goto_instr->previous()->AsParallelMove(); |
| 480 if (parallel_move == NULL) return goto_instr->previous(); | 484 if (parallel_move == NULL) return goto_instr->previous(); |
| 481 | 485 |
| 482 // All uses are recorded at the position of parallel move preceding goto. | 486 // All uses are recorded at the position of parallel move preceding goto. |
| 483 const intptr_t pos = goto_instr->lifetime_position() - 1; | 487 const intptr_t pos = goto_instr->lifetime_position(); |
| 484 ASSERT((pos >= 0) && IsParallelMovePosition(pos)); | 488 ASSERT(parallel_move->lifetime_position() == pos); |
| 485 | 489 |
| 486 JoinEntryInstr* join = goto_instr->successor(); | 490 JoinEntryInstr* join = goto_instr->successor(); |
| 487 ASSERT(join != NULL); | 491 ASSERT(join != NULL); |
| 488 | 492 |
| 489 // Search for the index of the current block in the predecessors of | 493 // Search for the index of the current block in the predecessors of |
| 490 // the join. | 494 // the join. |
| 491 const intptr_t pred_idx = join->IndexOfPredecessor(block); | 495 const intptr_t pred_idx = join->IndexOfPredecessor(block); |
| 492 | 496 |
| 493 // Record the corresponding phi input use for each phi. | 497 // Record the corresponding phi input use for each phi. |
| 494 ZoneGrowableArray<PhiInstr*>* phis = join->phis(); | 498 ZoneGrowableArray<PhiInstr*>* phis = join->phis(); |
| 495 intptr_t move_idx = 0; | 499 intptr_t move_idx = 0; |
| 496 for (intptr_t phi_idx = 0; phi_idx < phis->length(); phi_idx++) { | 500 for (intptr_t phi_idx = 0; phi_idx < phis->length(); phi_idx++) { |
| 497 PhiInstr* phi = (*phis)[phi_idx]; | 501 PhiInstr* phi = (*phis)[phi_idx]; |
| 498 if (phi == NULL) continue; | 502 if (phi == NULL) continue; |
| 499 | 503 |
| 500 Value* val = phi->InputAt(pred_idx); | 504 Value* val = phi->InputAt(pred_idx); |
| 501 MoveOperands* move = parallel_move->MoveOperandsAt(move_idx); | 505 MoveOperands* move = parallel_move->MoveOperandsAt(move_idx); |
| 502 if (val->IsUse()) { | 506 if (val->IsUse()) { |
| 503 // Expected shape of live ranges: | 507 // Expected shape of live ranges: |
| 504 // | 508 // |
| 505 // m g | 509 // g g' |
| 506 // value --* | 510 // value --* |
| 507 // | 511 // |
| 508 | 512 |
| 509 LiveRange* range = GetLiveRange( | 513 LiveRange* range = GetLiveRange( |
| 510 val->AsUse()->definition()->ssa_temp_index()); | 514 val->AsUse()->definition()->ssa_temp_index()); |
| 511 | 515 |
| 512 range->AddUseInterval(block->start_pos(), pos); | 516 range->AddUseInterval(block->start_pos(), pos); |
| 513 range->AddUse(pos, move->src_slot()); | 517 range->AddUse(pos, move->src_slot()); |
| 514 | 518 |
| 515 move->set_src(Location::PrefersRegister()); | 519 move->set_src(Location::PrefersRegister()); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 574 } | 578 } |
| 575 } | 579 } |
| 576 } | 580 } |
| 577 | 581 |
| 578 | 582 |
| 579 // Create and update live ranges corresponding to instruction's inputs, | 583 // Create and update live ranges corresponding to instruction's inputs, |
| 580 // temporaries and output. | 584 // temporaries and output. |
| 581 void FlowGraphAllocator::ProcessOneInstruction(BlockEntryInstr* block, | 585 void FlowGraphAllocator::ProcessOneInstruction(BlockEntryInstr* block, |
| 582 Instruction* current) { | 586 Instruction* current) { |
| 583 const intptr_t pos = current->lifetime_position(); | 587 const intptr_t pos = current->lifetime_position(); |
| 584 ASSERT(IsInstructionPosition(pos)); | 588 ASSERT(IsInstructionStartPosition(pos)); |
| 585 | 589 |
| 586 LocationSummary* locs = current->locs(); | 590 LocationSummary* locs = current->locs(); |
| 587 | 591 |
| 588 // TODO(vegorov): number of inputs must match number of input locations. | 592 // TODO(vegorov): number of inputs must match number of input locations. |
| 589 if (locs->input_count() != current->InputCount()) { | 593 if (locs->input_count() != current->InputCount()) { |
| 590 builder_->Bailout("ssa allocator: number of input locations mismatch"); | 594 builder_->Bailout("ssa allocator: number of input locations mismatch"); |
| 591 } | 595 } |
| 592 | 596 |
| 593 // Normalize same-as-first-input output if input is specified as | 597 // Normalize same-as-first-input output if input is specified as |
| 594 // fixed register. | 598 // fixed register. |
| 595 if (locs->out().IsUnallocated() && | 599 if (locs->out().IsUnallocated() && |
| 596 (locs->out().policy() == Location::kSameAsFirstInput) && | 600 (locs->out().policy() == Location::kSameAsFirstInput) && |
| 597 (locs->in(0).IsRegister())) { | 601 (locs->in(0).IsRegister())) { |
| 598 locs->set_out(locs->in(0)); | 602 locs->set_out(locs->in(0)); |
| 599 } | 603 } |
| 600 | 604 |
| 601 const bool output_same_as_first_input = | 605 const bool output_same_as_first_input = |
| 602 locs->out().IsUnallocated() && | 606 locs->out().IsUnallocated() && |
| 603 (locs->out().policy() == Location::kSameAsFirstInput); | 607 (locs->out().policy() == Location::kSameAsFirstInput); |
| 604 | 608 |
| 605 // Add uses from the deoptimization environment. | 609 // Add uses from the deoptimization environment. |
| 606 if (current->env() != NULL) { | 610 if (current->env() != NULL) { |
| 607 // Any value mentioned in the deoptimization environment should survive | 611 // Any value mentioned in the deoptimization environment should survive |
| 608 // until the end of instruction but it does not need to be in the register. | 612 // until the end of instruction but it does not need to be in the register. |
| 609 // Expected shape of live range: | 613 // Expected shape of live range: |
| 610 // | 614 // |
| 611 // m i m | 615 // i i' |
| 612 // value -----*--) | 616 // value -----* |
| 613 // | 617 // |
| 614 | 618 |
| 615 Environment* env = current->env(); | 619 Environment* env = current->env(); |
| 616 const GrowableArray<Value*>& values = env->values(); | 620 const GrowableArray<Value*>& values = env->values(); |
| 617 | 621 |
| 618 for (intptr_t j = 0; j < values.length(); j++) { | 622 for (intptr_t j = 0; j < values.length(); j++) { |
| 619 Value* val = values[j]; | 623 Value* val = values[j]; |
| 620 if (val->IsUse()) { | 624 if (val->IsUse()) { |
| 621 env->AddLocation(Location::Any()); | 625 env->AddLocation(Location::Any()); |
| 622 const intptr_t vreg = val->AsUse()->definition()->ssa_temp_index(); | 626 const intptr_t vreg = val->AsUse()->definition()->ssa_temp_index(); |
| 623 | 627 |
| 624 LiveRange* range = GetLiveRange(vreg); | 628 LiveRange* range = GetLiveRange(vreg); |
| 625 range->AddUseInterval(block->start_pos(), pos + 1); | 629 range->AddUseInterval(block->start_pos(), pos + 1); |
| 626 range->AddUse(pos, env->LocationSlotAt(j)); | 630 range->AddUse(pos + 1, env->LocationSlotAt(j)); |
| 627 } else { | 631 } else { |
| 628 ASSERT(val->IsConstant()); | 632 ASSERT(val->IsConstant()); |
| 629 env->AddLocation(Location::NoLocation()); | 633 env->AddLocation(Location::NoLocation()); |
| 630 } | 634 } |
| 631 } | 635 } |
| 632 } | 636 } |
| 633 | 637 |
| 634 // Process inputs. | 638 // Process inputs. |
| 635 // Skip the first input if output is specified with kSameAsFirstInput policy, | 639 // Skip the first input if output is specified with kSameAsFirstInput policy, |
| 636 // they will be processed together at the very end. | 640 // they will be processed together at the very end. |
| 637 for (intptr_t j = output_same_as_first_input ? 1 : 0; | 641 for (intptr_t j = output_same_as_first_input ? 1 : 0; |
| 638 j < current->InputCount(); | 642 j < current->InputCount(); |
| 639 j++) { | 643 j++) { |
| 640 Value* input = current->InputAt(j); | 644 Value* input = current->InputAt(j); |
| 641 ASSERT(input->IsUse()); // Can not be a constant currently. | 645 ASSERT(input->IsUse()); // Can not be a constant currently. |
| 642 const intptr_t vreg = input->AsUse()->definition()->ssa_temp_index(); | 646 const intptr_t vreg = input->AsUse()->definition()->ssa_temp_index(); |
| 643 LiveRange* range = GetLiveRange(vreg); | 647 LiveRange* range = GetLiveRange(vreg); |
| 644 | 648 |
| 645 Location* in_ref = locs->in_slot(j); | 649 Location* in_ref = locs->in_slot(j); |
| 646 | 650 |
| 647 if (in_ref->IsRegister()) { | 651 if (in_ref->IsRegister()) { |
| 648 // Input is expected in a fixed register. Expected shape of | 652 // Input is expected in a fixed register. Expected shape of |
| 649 // live ranges: | 653 // live ranges: |
| 650 // | 654 // |
| 651 // m i m | 655 // i i' |
| 652 // value --* | 656 // value --* |
| 653 // register [-----) | 657 // register [--) |
| 654 // | 658 // |
| 655 MoveOperands* move = | 659 MoveOperands* move = |
| 656 AddMoveAt(pos - 1, *in_ref, Location::PrefersRegister()); | 660 AddMoveAt(pos, *in_ref, Location::PrefersRegister()); |
| 657 BlockLocation(*in_ref, pos - 1, pos + 1); | 661 BlockLocation(*in_ref, pos); |
| 658 range->AddUseInterval(block->start_pos(), pos - 1); | 662 range->AddUseInterval(block->start_pos(), pos); |
| 659 range->AddUse(pos - 1, move->src_slot()); | 663 range->AddUse(pos, move->src_slot()); |
| 660 } else { | 664 } else { |
| 661 // Normal unallocated input. Expected shape of | 665 // Normal unallocated input. Expected shape of |
| 662 // live ranges: | 666 // live ranges: |
| 663 // | 667 // |
| 664 // m i m | 668 // i i' |
| 665 // value -----*--) | 669 // value -----* |
| 666 // | 670 // |
| 667 ASSERT(in_ref->IsUnallocated()); | 671 ASSERT(in_ref->IsUnallocated()); |
| 668 range->AddUseInterval(block->start_pos(), pos + 1); | 672 range->AddUseInterval(block->start_pos(), pos + 1); |
| 669 range->AddUse(pos, in_ref); | 673 range->AddUse(pos + 1, in_ref); |
| 670 } | 674 } |
| 671 } | 675 } |
| 672 | 676 |
| 673 // Process temps. | 677 // Process temps. |
| 674 for (intptr_t j = 0; j < locs->temp_count(); j++) { | 678 for (intptr_t j = 0; j < locs->temp_count(); j++) { |
| 675 // Expected shape of live range: | 679 // Expected shape of live range: |
| 676 // | 680 // |
| 677 // m i m | 681 // i i' |
| 678 // [--) | 682 // [-----) |
| 679 // | 683 // |
| 680 | 684 |
| 681 Location temp = locs->temp(j); | 685 Location temp = locs->temp(j); |
| 682 if (temp.IsRegister()) { | 686 if (temp.IsRegister()) { |
| 683 BlockLocation(temp, pos, pos + 1); | 687 BlockLocation(temp, pos); |
| 684 } else if (temp.IsUnallocated()) { | 688 } else if (temp.IsUnallocated()) { |
| 685 LiveRange* range = new LiveRange(kTempVirtualRegister); | 689 LiveRange* range = new LiveRange(kTempVirtualRegister); |
| 686 range->AddUseInterval(pos, pos + 1); | 690 range->AddUseInterval(pos, pos + 1); |
| 687 range->AddUse(pos, locs->temp_slot(j)); | 691 range->AddUse(pos, locs->temp_slot(j)); |
| 688 AddToUnallocated(range); | 692 AddToUnallocated(range); |
| 689 } else { | 693 } else { |
| 690 UNREACHABLE(); | 694 UNREACHABLE(); |
| 691 } | 695 } |
| 692 } | 696 } |
| 693 | 697 |
| 694 // Block all allocatable registers for calls. | 698 // Block all allocatable registers for calls. |
| 695 if (locs->is_call()) { | 699 if (locs->is_call()) { |
| 696 // Expected shape of live range: | 700 // Expected shape of live range: |
| 697 // | 701 // |
| 698 // m i m | 702 // i i' |
| 699 // [--) | 703 // [-----) |
| 700 // | 704 // |
| 701 | 705 |
| 702 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) { | 706 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) { |
| 703 BlockLocation(Location::RegisterLocation(static_cast<Register>(reg)), | 707 BlockLocation(Location::RegisterLocation(static_cast<Register>(reg)), |
| 704 pos, | 708 pos); |
| 705 pos + 1); | |
| 706 } | 709 } |
| 707 | 710 |
| 708 #ifdef DEBUG | 711 #ifdef DEBUG |
| 709 // Verify that temps, inputs and output were specified as fixed | 712 // Verify that temps, inputs and output were specified as fixed |
| 710 // locations. Every register is blocked now so attempt to | 713 // locations. Every register is blocked now so attempt to |
| 711 // allocate will not succeed. | 714 // allocate will not succeed. |
| 712 for (intptr_t j = 0; j < locs->temp_count(); j++) { | 715 for (intptr_t j = 0; j < locs->temp_count(); j++) { |
| 713 ASSERT(!locs->temp(j).IsUnallocated()); | 716 ASSERT(!locs->temp(j).IsUnallocated()); |
| 714 } | 717 } |
| 715 | 718 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 736 // such definitions. | 739 // such definitions. |
| 737 LiveRange* range = (def->ssa_temp_index() >= 0) ? | 740 LiveRange* range = (def->ssa_temp_index() >= 0) ? |
| 738 GetLiveRange(def->ssa_temp_index()) : | 741 GetLiveRange(def->ssa_temp_index()) : |
| 739 new LiveRange(kTempVirtualRegister); | 742 new LiveRange(kTempVirtualRegister); |
| 740 Location* out = locs->out_slot(); | 743 Location* out = locs->out_slot(); |
| 741 | 744 |
| 742 // Process output and finalize its liverange. | 745 // Process output and finalize its liverange. |
| 743 if (out->IsRegister()) { | 746 if (out->IsRegister()) { |
| 744 // Fixed output location. Expected shape of live range: | 747 // Fixed output location. Expected shape of live range: |
| 745 // | 748 // |
| 746 // m i m | 749 // i i' j j' |
| 747 // register [--) | 750 // register [-----) |
| 748 // output [------- | 751 // output [------- |
| 749 // | 752 // |
| 750 BlockLocation(*out, pos, pos + 1); | 753 BlockLocation(*out, pos); |
| 751 | 754 |
| 752 if (range->vreg() == kTempVirtualRegister) return; | 755 if (range->vreg() == kTempVirtualRegister) return; |
| 753 | 756 |
| 754 // We need to emit move connecting fixed register with another location | 757 // We need to emit move connecting fixed register with another location |
| 755 // that will be allocated for this output's live range. | 758 // that will be allocated for this output's live range. |
| 756 // Special case: fixed output followed by a fixed input last use. | 759 // Special case: fixed output followed by a fixed input last use. |
| 757 UsePosition* use = range->first_use(); | 760 UsePosition* use = range->first_use(); |
| 758 if (use->pos() == (pos + 1)) { | 761 if (use->pos() == (pos + 2)) { |
| 759 // We have a use position on the parallel move. | |
| 760 ASSERT(use->location_slot()->IsUnallocated()); | 762 ASSERT(use->location_slot()->IsUnallocated()); |
| 761 *(use->location_slot()) = *out; | 763 *(use->location_slot()) = *out; |
| 762 | 764 |
| 763 // Remove first use. It was allocated. | 765 // Remove first use. It was allocated. |
| 764 range->set_first_use(range->first_use()->next()); | 766 range->set_first_use(range->first_use()->next()); |
| 765 } | 767 } |
| 766 | 768 |
| 767 // Shorten live range to the point of definition, this might make the range | 769 // Shorten live range to the point of definition, this might make the range |
| 768 // empty (if the only use immediately follows). If range is not empty add | 770 // empty (if the only use immediately follows). If range is not empty add |
| 769 // move from a fixed register to an unallocated location. | 771 // move from a fixed register to an unallocated location. |
| 770 range->DefineAt(pos + 1); | 772 range->DefineAt(pos + 2); |
| 771 if (range->Start() == range->End()) return; | 773 if (range->Start() == range->End()) return; |
| 772 | 774 |
| 773 MoveOperands* move = AddMoveAt(pos + 1, Location::PrefersRegister(), *out); | 775 MoveOperands* move = AddMoveAt(pos + 2, Location::PrefersRegister(), *out); |
| 774 range->AddUse(pos + 1, move->dest_slot()); | 776 range->AddUse(pos + 2, move->dest_slot()); |
| 775 } else if (output_same_as_first_input) { | 777 } else if (output_same_as_first_input) { |
| 776 // Output register will contain a value of the first input at instruction's | 778 // Output register will contain a value of the first input at instruction's |
| 777 // start. Expected shape of live ranges: | 779 // start. Expected shape of live ranges: |
| 778 // | 780 // |
| 779 // m i m | 781 // i i' |
| 780 // input #0 --* | 782 // input #0 --* |
| 781 // output [--*---- | 783 // output [---- |
| 782 // | 784 // |
| 783 ASSERT(locs->in_slot(0)->Equals(Location::RequiresRegister())); | 785 ASSERT(locs->in_slot(0)->Equals(Location::RequiresRegister())); |
| 784 | 786 |
| 785 // Create move that will copy value between input and output. | 787 // Create move that will copy value between input and output. |
| 786 locs->set_out(Location::RequiresRegister()); | 788 locs->set_out(Location::RequiresRegister()); |
| 787 MoveOperands* move = AddMoveAt(pos - 1, | 789 MoveOperands* move = AddMoveAt(pos, |
| 788 Location::RequiresRegister(), | 790 Location::RequiresRegister(), |
| 789 Location::PrefersRegister()); | 791 Location::PrefersRegister()); |
| 790 | 792 |
| 791 // Add uses to the live range of the input. | 793 // Add uses to the live range of the input. |
| 792 Value* input = current->InputAt(0); | 794 Value* input = current->InputAt(0); |
| 793 ASSERT(input->IsUse()); // Can not be a constant currently. | 795 ASSERT(input->IsUse()); // Can not be a constant currently. |
| 794 LiveRange* input_range = GetLiveRange( | 796 LiveRange* input_range = GetLiveRange( |
| 795 input->AsUse()->definition()->ssa_temp_index()); | 797 input->AsUse()->definition()->ssa_temp_index()); |
| 796 input_range->AddUseInterval(block->start_pos(), pos - 1); | 798 input_range->AddUseInterval(block->start_pos(), pos); |
| 797 input_range->AddUse(pos - 1, move->src_slot()); | 799 input_range->AddUse(pos, move->src_slot()); |
| 798 | 800 |
| 799 // Shorten output live range to the point of definition and add both input | 801 // Shorten output live range to the point of definition and add both input |
| 800 // and output uses slots to be filled by allocator. | 802 // and output uses slots to be filled by allocator. |
| 801 range->DefineAt(pos - 1); | 803 range->DefineAt(pos); |
| 802 range->AddUse(pos - 1, out); | 804 range->AddUse(pos, out); |
| 803 range->AddUse(pos - 1, move->dest_slot()); | 805 range->AddUse(pos, move->dest_slot()); |
| 804 range->AddUse(pos, locs->in_slot(0)); | 806 range->AddUse(pos, locs->in_slot(0)); |
| 805 } else { | 807 } else { |
| 806 // Normal unallocated location that requires a register. Expected shape of | 808 // Normal unallocated location that requires a register. Expected shape of |
| 807 // live range: | 809 // live range: |
| 808 // | 810 // |
| 809 // m i m | 811 // i i' |
| 810 // output [------- | 812 // output [------- |
| 811 // | 813 // |
| 812 ASSERT(out->IsUnallocated() && | 814 ASSERT(out->IsUnallocated() && |
| 813 (out->policy() == Location::kRequiresRegister)); | 815 (out->policy() == Location::kRequiresRegister)); |
| 814 | 816 |
| 815 // Shorten live range to the point of definition and add use to be filled by | 817 // Shorten live range to the point of definition and add use to be filled by |
| 816 // allocator. | 818 // allocator. |
| 817 range->DefineAt(pos); | 819 range->DefineAt(pos); |
| 818 range->AddUse(pos, out); | 820 range->AddUse(pos, out); |
| 819 } | 821 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 857 void FlowGraphAllocator::NumberInstructions() { | 859 void FlowGraphAllocator::NumberInstructions() { |
| 858 intptr_t pos = 0; | 860 intptr_t pos = 0; |
| 859 | 861 |
| 860 // The basic block order is reverse postorder. | 862 // The basic block order is reverse postorder. |
| 861 const intptr_t block_count = postorder_.length(); | 863 const intptr_t block_count = postorder_.length(); |
| 862 for (intptr_t i = block_count - 1; i >= 0; i--) { | 864 for (intptr_t i = block_count - 1; i >= 0; i--) { |
| 863 BlockEntryInstr* block = postorder_[i]; | 865 BlockEntryInstr* block = postorder_[i]; |
| 864 | 866 |
| 865 instructions_.Add(block); | 867 instructions_.Add(block); |
| 866 block->set_start_pos(pos); | 868 block->set_start_pos(pos); |
| 867 block->set_lifetime_position(pos + 1); | 869 block->set_lifetime_position(pos); |
| 868 pos += 2; | 870 pos += 2; |
| 869 | 871 |
| 870 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 872 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| 871 Instruction* current = it.Current(); | 873 Instruction* current = it.Current(); |
| 872 // Do not assign numbers to parallel move instructions. | 874 // Do not assign numbers to parallel move instructions. |
| 873 if (!current->IsParallelMove()) { | 875 if (!current->IsParallelMove()) { |
| 874 instructions_.Add(current); | 876 instructions_.Add(current); |
| 875 current->set_lifetime_position(pos + 1); | 877 current->set_lifetime_position(pos); |
| 876 pos += 2; | 878 pos += 2; |
| 877 } | 879 } |
| 878 } | 880 } |
| 879 block->set_end_pos(pos); | 881 block->set_end_pos(pos); |
| 880 } | 882 } |
| 881 | 883 |
| 882 // Create parallel moves in join predecessors. This must be done after | 884 // Create parallel moves in join predecessors. This must be done after |
| 883 // all instructions are numbered. | 885 // all instructions are numbered. |
| 884 for (intptr_t i = block_count - 1; i >= 0; i--) { | 886 for (intptr_t i = block_count - 1; i >= 0; i--) { |
| 885 BlockEntryInstr* block = postorder_[i]; | 887 BlockEntryInstr* block = postorder_[i]; |
| 886 | 888 |
| 887 // For join entry predecessors create phi resolution moves if | 889 // For join entry predecessors create phi resolution moves if |
| 888 // necessary. They will be populated by the register allocator. | 890 // necessary. They will be populated by the register allocator. |
| 889 JoinEntryInstr* join = block->AsJoinEntry(); | 891 JoinEntryInstr* join = block->AsJoinEntry(); |
| 890 if ((join != NULL) && (join->phi_count() > 0)) { | 892 if ((join != NULL) && (join->phi_count() > 0)) { |
| 891 const intptr_t phi_count = join->phi_count(); | 893 const intptr_t phi_count = join->phi_count(); |
| 892 for (intptr_t i = 0; i < block->PredecessorCount(); i++) { | 894 for (intptr_t i = 0; i < block->PredecessorCount(); i++) { |
| 893 // Insert the move between the last two instructions of the | 895 // Insert the move between the last two instructions of the |
| 894 // predecessor block (all such blocks have at least two instructions: | 896 // predecessor block (all such blocks have at least two instructions: |
| 895 // the block entry and goto instructions.) | 897 // the block entry and goto instructions.) |
| 896 Instruction* last = block->PredecessorAt(i)->last_instruction(); | 898 Instruction* last = block->PredecessorAt(i)->last_instruction(); |
| 899 ASSERT(last->IsGoto()); |
| 900 |
| 897 ParallelMoveInstr* move = | 901 ParallelMoveInstr* move = |
| 898 CreateParallelMoveBefore(last, last->lifetime_position() - 1); | 902 CreateParallelMoveBefore(last, last->lifetime_position()); |
| 899 | 903 |
| 900 // Populate the ParallelMove with empty moves. | 904 // Populate the ParallelMove with empty moves. |
| 901 for (intptr_t j = 0; j < phi_count; j++) { | 905 for (intptr_t j = 0; j < phi_count; j++) { |
| 902 move->AddMove(Location::NoLocation(), Location::NoLocation()); | 906 move->AddMove(Location::NoLocation(), Location::NoLocation()); |
| 903 } | 907 } |
| 908 |
| 909 // Replace Goto instruction with the corresponding move in |
| 910 // the array of instructions. This is done to ensure that |
| 911 // this parallel move will be treated as a normal instruction |
| 912 // by AddMoveAt for the purpose of live ranges connections (i.e. |
| 913 // a separate move will be inserted by AddMoveAt) |
| 914 // This move can't be reused by AddMoveAt to insert |
| 915 // moves at Goto position because such range connecting moves might |
| 916 // come into a conflict with phi connecting moves due to implicit |
| 917 // interference: phi-value's liferange starts only at successor block |
| 918 // but the move is actually performed at the predecessor. |
| 919 ASSERT(instructions_[last->lifetime_position() / 2] == last); |
| 920 instructions_[last->lifetime_position() / 2] = move; |
| 904 } | 921 } |
| 905 } | 922 } |
| 906 } | 923 } |
| 907 } | 924 } |
| 908 | 925 |
| 909 | 926 |
| 910 Instruction* FlowGraphAllocator::InstructionAt(intptr_t pos) const { | 927 Instruction* FlowGraphAllocator::InstructionAt(intptr_t pos) const { |
| 911 return instructions_[pos / 2]; | 928 return instructions_[pos / 2]; |
| 912 } | 929 } |
| 913 | 930 |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1016 | 1033 |
| 1017 LiveRange* LiveRange::MakeTemp(intptr_t pos, Location* location_slot) { | 1034 LiveRange* LiveRange::MakeTemp(intptr_t pos, Location* location_slot) { |
| 1018 UNREACHABLE(); | 1035 UNREACHABLE(); |
| 1019 return NULL; | 1036 return NULL; |
| 1020 } | 1037 } |
| 1021 | 1038 |
| 1022 | 1039 |
| 1023 LiveRange* LiveRange::SplitAt(intptr_t split_pos) { | 1040 LiveRange* LiveRange::SplitAt(intptr_t split_pos) { |
| 1024 if (Start() == split_pos) return this; | 1041 if (Start() == split_pos) return this; |
| 1025 | 1042 |
| 1026 // Ranges can only be connected by parallel moves. | |
| 1027 split_pos = ToParallelMove(split_pos); | |
| 1028 | |
| 1029 UseInterval* interval = finger_.first_pending_use_interval(); | 1043 UseInterval* interval = finger_.first_pending_use_interval(); |
| 1030 ASSERT(interval->start() < split_pos); | 1044 ASSERT(interval->start() < split_pos); |
| 1031 | 1045 |
| 1032 // Corner case. We need to start over to find previous interval. | 1046 // Corner case. We need to start over to find previous interval. |
| 1033 if (interval->start() == split_pos) interval = first_use_interval_; | 1047 if (interval->start() == split_pos) interval = first_use_interval_; |
| 1034 | 1048 |
| 1035 UseInterval* last_before_split = NULL; | 1049 UseInterval* last_before_split = NULL; |
| 1036 while (interval->end() <= split_pos) { | 1050 while (interval->end() <= split_pos) { |
| 1037 last_before_split = interval; | 1051 last_before_split = interval; |
| 1038 interval = interval->next(); | 1052 interval = interval->next(); |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1094 return next_sibling_; | 1108 return next_sibling_; |
| 1095 } | 1109 } |
| 1096 | 1110 |
| 1097 | 1111 |
| 1098 LiveRange* FlowGraphAllocator::SplitBetween(LiveRange* range, | 1112 LiveRange* FlowGraphAllocator::SplitBetween(LiveRange* range, |
| 1099 intptr_t from, | 1113 intptr_t from, |
| 1100 intptr_t to) { | 1114 intptr_t to) { |
| 1101 // TODO(vegorov): select optimal split position based on loop structure. | 1115 // TODO(vegorov): select optimal split position based on loop structure. |
| 1102 TRACE_ALLOC(("split %d [%d, %d) between [%d, %d)\n", | 1116 TRACE_ALLOC(("split %d [%d, %d) between [%d, %d)\n", |
| 1103 range->vreg(), range->Start(), range->End(), from, to)); | 1117 range->vreg(), range->Start(), range->End(), from, to)); |
| 1118 |
| 1119 // Prefer spliting at instruction starts if possible. |
| 1120 if (from < ToInstructionStart(to)) { |
| 1121 to = ToInstructionStart(to); |
| 1122 } |
| 1123 |
| 1104 return range->SplitAt(to); | 1124 return range->SplitAt(to); |
| 1105 } | 1125 } |
| 1106 | 1126 |
| 1107 | 1127 |
| 1108 void FlowGraphAllocator::SpillBetween(LiveRange* range, | 1128 void FlowGraphAllocator::SpillBetween(LiveRange* range, |
| 1109 intptr_t from, | 1129 intptr_t from, |
| 1110 intptr_t to) { | 1130 intptr_t to) { |
| 1111 ASSERT(from < to); | 1131 ASSERT(from < to); |
| 1112 TRACE_ALLOC(("spill %d [%d, %d) between [%d, %d)\n", | 1132 TRACE_ALLOC(("spill %d [%d, %d) between [%d, %d)\n", |
| 1113 range->vreg(), range->Start(), range->End(), from, to)); | 1133 range->vreg(), range->Start(), range->End(), from, to)); |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1212 FirstIntersectionWithAllocated(static_cast<Register>(reg), unallocated); | 1232 FirstIntersectionWithAllocated(static_cast<Register>(reg), unallocated); |
| 1213 | 1233 |
| 1214 if (intersection > free_until) { | 1234 if (intersection > free_until) { |
| 1215 candidate = static_cast<Register>(reg); | 1235 candidate = static_cast<Register>(reg); |
| 1216 free_until = intersection; | 1236 free_until = intersection; |
| 1217 if (free_until == kMaxPosition) break; | 1237 if (free_until == kMaxPosition) break; |
| 1218 } | 1238 } |
| 1219 } | 1239 } |
| 1220 } | 1240 } |
| 1221 | 1241 |
| 1222 if (free_until != kMaxPosition) free_until = ToParallelMove(free_until); | |
| 1223 | |
| 1224 // All registers are blocked by active ranges. | 1242 // All registers are blocked by active ranges. |
| 1225 if (free_until <= unallocated->Start()) return false; | 1243 if (free_until <= unallocated->Start()) return false; |
| 1226 | 1244 |
| 1227 TRACE_ALLOC(("assigning free register %s to %d\n", | 1245 TRACE_ALLOC(("assigning free register %s to %d\n", |
| 1228 Location::RegisterLocation(candidate).Name(), | 1246 Location::RegisterLocation(candidate).Name(), |
| 1229 unallocated->vreg())); | 1247 unallocated->vreg())); |
| 1230 | 1248 |
| 1231 if (free_until != kMaxPosition) { | 1249 if (free_until != kMaxPosition) { |
| 1232 // There was an intersection. Split unallocated. | 1250 // There was an intersection. Split unallocated. |
| 1233 TRACE_ALLOC((" splitting at %d\n", free_until)); | 1251 TRACE_ALLOC((" splitting at %d\n", free_until)); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 1259 if (UpdateFreeUntil(static_cast<Register>(reg), | 1277 if (UpdateFreeUntil(static_cast<Register>(reg), |
| 1260 unallocated, | 1278 unallocated, |
| 1261 &free_until, | 1279 &free_until, |
| 1262 &blocked_at)) { | 1280 &blocked_at)) { |
| 1263 candidate = static_cast<Register>(reg); | 1281 candidate = static_cast<Register>(reg); |
| 1264 } | 1282 } |
| 1265 } | 1283 } |
| 1266 | 1284 |
| 1267 if (free_until < register_use->pos()) { | 1285 if (free_until < register_use->pos()) { |
| 1268 // Can't acquire free register. Spill until we really need one. | 1286 // Can't acquire free register. Spill until we really need one. |
| 1269 ASSERT(unallocated->Start() < ToParallelMove(register_use->pos())); | 1287 ASSERT(unallocated->Start() < ToInstructionStart(register_use->pos())); |
| 1270 SpillBetween(unallocated, unallocated->Start(), register_use->pos()); | 1288 SpillBetween(unallocated, unallocated->Start(), register_use->pos()); |
| 1271 return; | 1289 return; |
| 1272 } | 1290 } |
| 1273 | 1291 |
| 1274 if (blocked_at < unallocated->End()) { | 1292 if (blocked_at < unallocated->End()) { |
| 1275 LiveRange* tail = SplitBetween(unallocated, | 1293 LiveRange* tail = SplitBetween(unallocated, |
| 1276 unallocated->Start(), | 1294 unallocated->Start(), |
| 1277 blocked_at); | 1295 blocked_at); |
| 1278 AddToUnallocated(tail); | 1296 AddToUnallocated(tail); |
| 1279 } | 1297 } |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1398 SpillBetween(allocated, spill_position, restore_position); | 1416 SpillBetween(allocated, spill_position, restore_position); |
| 1399 } | 1417 } |
| 1400 | 1418 |
| 1401 return true; | 1419 return true; |
| 1402 } | 1420 } |
| 1403 | 1421 |
| 1404 | 1422 |
| 1405 MoveOperands* FlowGraphAllocator::AddMoveAt(intptr_t pos, | 1423 MoveOperands* FlowGraphAllocator::AddMoveAt(intptr_t pos, |
| 1406 Location to, | 1424 Location to, |
| 1407 Location from) { | 1425 Location from) { |
| 1408 ASSERT(IsParallelMovePosition(pos)); | |
| 1409 Instruction* instr = InstructionAt(pos); | 1426 Instruction* instr = InstructionAt(pos); |
| 1410 ASSERT(!instr->IsBlockEntry()); | 1427 ASSERT(!instr->IsBlockEntry()); |
| 1411 return CreateParallelMoveBefore(instr, pos)->AddMove(to, from); | 1428 |
| 1429 ParallelMoveInstr* parallel_move = NULL; |
| 1430 if (IsInstructionStartPosition(pos)) { |
| 1431 parallel_move = CreateParallelMoveBefore(instr, pos); |
| 1432 } else { |
| 1433 parallel_move = CreateParallelMoveAfter(instr, pos); |
| 1434 } |
| 1435 |
| 1436 return parallel_move->AddMove(to, from); |
| 1412 } | 1437 } |
| 1413 | 1438 |
| 1414 | 1439 |
| 1415 void FlowGraphAllocator::ConvertUseTo(UsePosition* use, Location loc) { | 1440 void FlowGraphAllocator::ConvertUseTo(UsePosition* use, Location loc) { |
| 1416 ASSERT(use->location_slot() != NULL); | 1441 ASSERT(use->location_slot() != NULL); |
| 1417 Location* slot = use->location_slot(); | 1442 Location* slot = use->location_slot(); |
| 1418 ASSERT(slot->IsUnallocated()); | 1443 ASSERT(slot->IsUnallocated()); |
| 1419 ASSERT((slot->policy() == Location::kRequiresRegister) || | 1444 ASSERT((slot->policy() == Location::kRequiresRegister) || |
| 1420 (slot->policy() == Location::kPrefersRegister) || | 1445 (slot->policy() == Location::kPrefersRegister) || |
| 1421 (slot->policy() == Location::kAny)); | 1446 (slot->policy() == Location::kAny)); |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1538 TRACE_ALLOC(("Connect source_block=%d, target_block=%d\n", | 1563 TRACE_ALLOC(("Connect source_block=%d, target_block=%d\n", |
| 1539 source_block->block_id(), | 1564 source_block->block_id(), |
| 1540 target_block->block_id())); | 1565 target_block->block_id())); |
| 1541 if (range->next_sibling() == NULL) { | 1566 if (range->next_sibling() == NULL) { |
| 1542 // Nothing to connect. The whole range was allocated to the same location. | 1567 // Nothing to connect. The whole range was allocated to the same location. |
| 1543 TRACE_ALLOC(("range %d has no siblings\n", range->vreg())); | 1568 TRACE_ALLOC(("range %d has no siblings\n", range->vreg())); |
| 1544 return; | 1569 return; |
| 1545 } | 1570 } |
| 1546 | 1571 |
| 1547 const intptr_t source_pos = source_block->end_pos() - 1; | 1572 const intptr_t source_pos = source_block->end_pos() - 1; |
| 1548 ASSERT(IsInstructionPosition(source_pos)); | 1573 ASSERT(IsInstructionEndPosition(source_pos)); |
| 1549 | 1574 |
| 1550 const intptr_t target_pos = target_block->start_pos(); | 1575 const intptr_t target_pos = target_block->start_pos(); |
| 1551 | 1576 |
| 1552 Location target; | 1577 Location target; |
| 1553 Location source; | 1578 Location source; |
| 1554 | 1579 |
| 1555 #ifdef DEBUG | 1580 #ifdef DEBUG |
| 1556 LiveRange* source_cover = NULL; | 1581 LiveRange* source_cover = NULL; |
| 1557 LiveRange* target_cover = NULL; | 1582 LiveRange* target_cover = NULL; |
| 1558 #endif | 1583 #endif |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1578 | 1603 |
| 1579 TRACE_ALLOC(("connecting [%d, %d) [%s] to [%d, %d) [%s]\n", | 1604 TRACE_ALLOC(("connecting [%d, %d) [%s] to [%d, %d) [%s]\n", |
| 1580 source_cover->Start(), source_cover->End(), source.Name(), | 1605 source_cover->Start(), source_cover->End(), source.Name(), |
| 1581 target_cover->Start(), target_cover->End(), target.Name())); | 1606 target_cover->Start(), target_cover->End(), target.Name())); |
| 1582 | 1607 |
| 1583 // Siblings were allocated to the same register. | 1608 // Siblings were allocated to the same register. |
| 1584 if (source.Equals(target)) return; | 1609 if (source.Equals(target)) return; |
| 1585 | 1610 |
| 1586 Instruction* last = source_block->last_instruction(); | 1611 Instruction* last = source_block->last_instruction(); |
| 1587 if (last->SuccessorCount() == 1) { | 1612 if (last->SuccessorCount() == 1) { |
| 1588 CreateParallelMoveBefore(last, last->lifetime_position() - 1)-> | 1613 CreateParallelMoveBefore(last, last->lifetime_position())-> |
| 1589 AddMove(target, source); | 1614 AddMove(target, source); |
| 1590 } else { | 1615 } else { |
| 1591 CreateParallelMoveAfter(target_block, target_block->start_pos())-> | 1616 CreateParallelMoveAfter(target_block, target_block->start_pos())-> |
| 1592 AddMove(target, source); | 1617 AddMove(target, source); |
| 1593 } | 1618 } |
| 1594 } | 1619 } |
| 1595 | 1620 |
| 1596 | 1621 |
| 1597 void FlowGraphAllocator::ResolveControlFlow() { | 1622 void FlowGraphAllocator::ResolveControlFlow() { |
| 1598 // Resolve linear control flow between touching split siblings | 1623 // Resolve linear control flow between touching split siblings |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1651 | 1676 |
| 1652 if (FLAG_trace_ssa_allocator) { | 1677 if (FLAG_trace_ssa_allocator) { |
| 1653 OS::Print("-- ir after allocation -------------------------\n"); | 1678 OS::Print("-- ir after allocation -------------------------\n"); |
| 1654 FlowGraphPrinter printer(Function::Handle(), block_order_, true); | 1679 FlowGraphPrinter printer(Function::Handle(), block_order_, true); |
| 1655 printer.PrintBlocks(); | 1680 printer.PrintBlocks(); |
| 1656 } | 1681 } |
| 1657 } | 1682 } |
| 1658 | 1683 |
| 1659 | 1684 |
| 1660 } // namespace dart | 1685 } // namespace dart |
| OLD | NEW |