OLD | NEW |
---|---|
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/base/adapters.h" | 5 #include "src/base/adapters.h" |
6 #include "src/compiler/linkage.h" | 6 #include "src/compiler/linkage.h" |
7 #include "src/compiler/register-allocator.h" | 7 #include "src/compiler/register-allocator.h" |
8 #include "src/string-stream.h" | 8 #include "src/string-stream.h" |
9 | 9 |
10 namespace v8 { | 10 namespace v8 { |
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
249 first_interval_(nullptr), | 249 first_interval_(nullptr), |
250 first_pos_(nullptr), | 250 first_pos_(nullptr), |
251 parent_(nullptr), | 251 parent_(nullptr), |
252 next_(nullptr), | 252 next_(nullptr), |
253 spill_operand_(nullptr), | 253 spill_operand_(nullptr), |
254 spills_at_definition_(nullptr), | 254 spills_at_definition_(nullptr), |
255 current_interval_(nullptr), | 255 current_interval_(nullptr), |
256 last_processed_use_(nullptr), | 256 last_processed_use_(nullptr), |
257 current_hint_position_(nullptr), | 257 current_hint_position_(nullptr), |
258 size_(kInvalidSize), | 258 size_(kInvalidSize), |
259 weight_(kInvalidWeight) { | 259 weight_(kInvalidWeight), |
260 spilled_in_deferred_block_(false) { | |
260 DCHECK(AllocatedOperand::IsSupportedMachineType(machine_type)); | 261 DCHECK(AllocatedOperand::IsSupportedMachineType(machine_type)); |
261 bits_ = SpillTypeField::encode(SpillType::kNoSpillType) | | 262 bits_ = SpillTypeField::encode(SpillType::kNoSpillType) | |
262 AssignedRegisterField::encode(kUnassignedRegister) | | 263 AssignedRegisterField::encode(kUnassignedRegister) | |
263 MachineTypeField::encode(machine_type); | 264 MachineTypeField::encode(machine_type); |
264 } | 265 } |
265 | 266 |
266 | 267 |
267 void LiveRange::Verify() const { | 268 void LiveRange::Verify() const { |
268 // Walk the positions, verifying that each is in an interval. | 269 // Walk the positions, verifying that each is in an interval. |
269 auto interval = first_interval_; | 270 auto interval = first_interval_; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
312 | 313 |
313 | 314 |
314 void LiveRange::SpillAtDefinition(Zone* zone, int gap_index, | 315 void LiveRange::SpillAtDefinition(Zone* zone, int gap_index, |
315 InstructionOperand* operand) { | 316 InstructionOperand* operand) { |
316 DCHECK(HasNoSpillType()); | 317 DCHECK(HasNoSpillType()); |
317 spills_at_definition_ = new (zone) | 318 spills_at_definition_ = new (zone) |
318 SpillAtDefinitionList(gap_index, operand, spills_at_definition_); | 319 SpillAtDefinitionList(gap_index, operand, spills_at_definition_); |
319 } | 320 } |
320 | 321 |
321 | 322 |
323 bool LiveRange::TryCommitSpillInDeferredBlock( | |
324 InstructionSequence* code, const InstructionOperand& spill_operand) { | |
325 DCHECK(!IsChild()); | |
326 | |
327 if (!FLAG_turbo_preprocess_ranges || IsEmpty() || HasNoSpillType() || | |
328 spill_operand.IsConstant() || spill_operand.IsImmediate()) { | |
329 return false; | |
330 } | |
331 | |
332 int count = 0; | |
333 for (const LiveRange* child = this; child != nullptr; child = child->next()) { | |
334 int first_instr = child->Start().ToInstructionIndex(); | |
335 | |
336 // If the range starts at instruction end, the first instruction index is | |
337 // the next one. | |
338 if (!child->Start().IsGapPosition() && !child->Start().IsStart()) { | |
339 ++first_instr; | |
340 } | |
341 | |
342 // We only look at where the range starts. It doesn't matter where it ends: | |
343 // if it ends past this block, then either there is a phi there already, | |
344 // or ResolveControlFlow will adapt the last instruction gap of this block | |
345 // as if there were a phi. In either case, data flow will be correct. | |
346 const InstructionBlock* block = code->GetInstructionBlock(first_instr); | |
347 | |
348 // If we have slot uses in a subrange, bail out, because we need the value | |
349 // on the stack before that use. | |
350 bool has_slot_use = child->NextSlotPosition(child->Start()) != nullptr; | |
351 if (!block->IsDeferred()) { | |
352 if (child->spilled() || has_slot_use) { | |
353 TRACE( | |
354 "Live Range %d must be spilled at definition: found a " | |
355 "slot-requiring non-deferred child range %d.\n", | |
356 TopLevel()->id(), child->id()); | |
357 return false; | |
358 } | |
359 } else { | |
360 if (child->spilled() || has_slot_use) ++count; | |
361 } | |
362 } | |
363 if (count == 0) return false; | |
364 | |
365 spill_start_index_ = -1; | |
366 spilled_in_deferred_block_ = true; | |
367 | |
368 TRACE("Live Range %d will be spilled only in deferred blocks.\n", id()); | |
369 // If we have ranges that aren't spilled but require the operand on the stack, | |
370 // make sure we insert the spill. | |
371 for (const LiveRange* child = this; child != nullptr; child = child->next()) { | |
372 if (!child->spilled() && | |
373 child->NextSlotPosition(child->Start()) != nullptr) { | |
374 auto instr = code->InstructionAt(child->Start().ToInstructionIndex()); | |
375 // Insert spill at the end to let live range connections happen at START. | |
376 auto move = | |
377 instr->GetOrCreateParallelMove(Instruction::END, code->zone()); | |
378 InstructionOperand assigned = child->GetAssignedOperand(); | |
379 if (TopLevel()->has_slot_use()) { | |
380 bool found = false; | |
381 for (auto move_op : *move) { | |
382 if (move_op->IsEliminated()) continue; | |
383 if (move_op->source().Equals(assigned) && | |
384 move_op->destination().Equals(spill_operand)) { | |
385 found = true; | |
386 break; | |
387 } | |
388 } | |
389 if (found) continue; | |
390 } | |
391 | |
392 move->AddMove(assigned, spill_operand); | |
Jarin
2015/08/04 19:39:43
Still confused about this - where do we actually s
Mircea Trofin
2015/08/04 20:34:46
Correct. It sets up the stage for the register all
Jarin
2015/08/05 14:04:16
I am sorry, that explanation did not really help m
| |
393 } | |
394 } | |
395 | |
396 return true; | |
397 } | |
398 | |
399 | |
322 void LiveRange::CommitSpillsAtDefinition(InstructionSequence* sequence, | 400 void LiveRange::CommitSpillsAtDefinition(InstructionSequence* sequence, |
323 const InstructionOperand& op, | 401 const InstructionOperand& op, |
324 bool might_be_duplicated) { | 402 bool might_be_duplicated) { |
325 DCHECK_IMPLIES(op.IsConstant(), spills_at_definition_ == nullptr); | 403 DCHECK_IMPLIES(op.IsConstant(), spills_at_definition_ == nullptr); |
326 DCHECK(!IsChild()); | 404 DCHECK(!IsChild()); |
327 auto zone = sequence->zone(); | 405 auto zone = sequence->zone(); |
406 | |
407 // If this top level range has a child spilled in a deferred block, we use | |
408 // the range and control flow connection mechanism instead. | |
409 if (TryCommitSpillInDeferredBlock(sequence, op)) return; | |
410 | |
328 for (auto to_spill = spills_at_definition_; to_spill != nullptr; | 411 for (auto to_spill = spills_at_definition_; to_spill != nullptr; |
329 to_spill = to_spill->next) { | 412 to_spill = to_spill->next) { |
330 auto instr = sequence->InstructionAt(to_spill->gap_index); | 413 auto instr = sequence->InstructionAt(to_spill->gap_index); |
331 auto move = instr->GetOrCreateParallelMove(Instruction::START, zone); | 414 auto move = instr->GetOrCreateParallelMove(Instruction::START, zone); |
332 // Skip insertion if it's possible that the move exists already as a | 415 // Skip insertion if it's possible that the move exists already as a |
333 // constraint move from a fixed output register to a slot. | 416 // constraint move from a fixed output register to a slot. |
334 if (might_be_duplicated) { | 417 if (might_be_duplicated) { |
335 bool found = false; | 418 bool found = false; |
336 for (auto move_op : *move) { | 419 for (auto move_op : *move) { |
337 if (move_op->IsEliminated()) continue; | 420 if (move_op->IsEliminated()) continue; |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
409 | 492 |
410 UsePosition* LiveRange::NextRegisterPosition(LifetimePosition start) const { | 493 UsePosition* LiveRange::NextRegisterPosition(LifetimePosition start) const { |
411 UsePosition* pos = NextUsePosition(start); | 494 UsePosition* pos = NextUsePosition(start); |
412 while (pos != nullptr && pos->type() != UsePositionType::kRequiresRegister) { | 495 while (pos != nullptr && pos->type() != UsePositionType::kRequiresRegister) { |
413 pos = pos->next(); | 496 pos = pos->next(); |
414 } | 497 } |
415 return pos; | 498 return pos; |
416 } | 499 } |
417 | 500 |
418 | 501 |
502 UsePosition* LiveRange::NextSlotPosition(LifetimePosition start) const { | |
503 for (UsePosition* pos = NextUsePosition(start); pos != nullptr; | |
504 pos = pos->next()) { | |
505 if (pos->type() != UsePositionType::kRequiresSlot) continue; | |
506 return pos; | |
507 } | |
508 return nullptr; | |
509 } | |
510 | |
511 | |
419 bool LiveRange::CanBeSpilled(LifetimePosition pos) const { | 512 bool LiveRange::CanBeSpilled(LifetimePosition pos) const { |
420 // We cannot spill a live range that has a use requiring a register | 513 // We cannot spill a live range that has a use requiring a register |
421 // at the current or the immediate next position. | 514 // at the current or the immediate next position. |
422 auto use_pos = NextRegisterPosition(pos); | 515 auto use_pos = NextRegisterPosition(pos); |
423 if (use_pos == nullptr) return true; | 516 if (use_pos == nullptr) return true; |
424 return use_pos->pos() > pos.NextStart().End(); | 517 return use_pos->pos() > pos.NextStart().End(); |
425 } | 518 } |
426 | 519 |
427 | 520 |
428 InstructionOperand LiveRange::GetAssignedOperand() const { | 521 InstructionOperand LiveRange::GetAssignedOperand() const { |
(...skipping 1057 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1486 auto output = instr->OutputAt(i); | 1579 auto output = instr->OutputAt(i); |
1487 if (output->IsUnallocated()) { | 1580 if (output->IsUnallocated()) { |
1488 // Unsupported. | 1581 // Unsupported. |
1489 DCHECK(!UnallocatedOperand::cast(output)->HasSlotPolicy()); | 1582 DCHECK(!UnallocatedOperand::cast(output)->HasSlotPolicy()); |
1490 int out_vreg = UnallocatedOperand::cast(output)->virtual_register(); | 1583 int out_vreg = UnallocatedOperand::cast(output)->virtual_register(); |
1491 live->Remove(out_vreg); | 1584 live->Remove(out_vreg); |
1492 } else if (output->IsConstant()) { | 1585 } else if (output->IsConstant()) { |
1493 int out_vreg = ConstantOperand::cast(output)->virtual_register(); | 1586 int out_vreg = ConstantOperand::cast(output)->virtual_register(); |
1494 live->Remove(out_vreg); | 1587 live->Remove(out_vreg); |
1495 } | 1588 } |
1496 Define(curr_position, output); | 1589 if (block->IsHandler() && index == block_start) { |
1590 // The register defined here is blocked from gap start - it is the | |
1591 // exception value. | |
1592 // TODO(mtrofin): should we explore an explicit opcode for | |
1593 // the first instruction in the handler? | |
1594 Define(LifetimePosition::GapFromInstructionIndex(index), output); | |
1595 } else { | |
1596 Define(curr_position, output); | |
1597 } | |
1497 } | 1598 } |
1498 | 1599 |
1499 if (instr->ClobbersRegisters()) { | 1600 if (instr->ClobbersRegisters()) { |
1500 for (int i = 0; i < config()->num_general_registers(); ++i) { | 1601 for (int i = 0; i < config()->num_general_registers(); ++i) { |
1501 if (!IsOutputRegisterOf(instr, i)) { | 1602 if (!IsOutputRegisterOf(instr, i)) { |
1502 auto range = FixedLiveRangeFor(i); | 1603 auto range = FixedLiveRangeFor(i); |
1503 range->AddUseInterval(curr_position, curr_position.End(), | 1604 range->AddUseInterval(curr_position, curr_position.End(), |
1504 allocation_zone()); | 1605 allocation_zone()); |
1505 } | 1606 } |
1506 } | 1607 } |
(...skipping 1066 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2573 auto safe_point_pos = | 2674 auto safe_point_pos = |
2574 LifetimePosition::InstructionFromInstructionIndex(safe_point); | 2675 LifetimePosition::InstructionFromInstructionIndex(safe_point); |
2575 auto cur = range; | 2676 auto cur = range; |
2576 while (cur != nullptr && !cur->Covers(safe_point_pos)) { | 2677 while (cur != nullptr && !cur->Covers(safe_point_pos)) { |
2577 cur = cur->next(); | 2678 cur = cur->next(); |
2578 } | 2679 } |
2579 if (cur == nullptr) continue; | 2680 if (cur == nullptr) continue; |
2580 | 2681 |
2581 // Check if the live range is spilled and the safe point is after | 2682 // Check if the live range is spilled and the safe point is after |
2582 // the spill position. | 2683 // the spill position. |
2583 if (!spill_operand.IsInvalid() && | 2684 int spill_index = range->IsSpilledInSingleDeferredBlock() |
2584 safe_point >= range->spill_start_index()) { | 2685 ? cur->Start().ToInstructionIndex() |
2686 : range->spill_start_index(); | |
2687 | |
2688 if (!spill_operand.IsInvalid() && safe_point >= spill_index) { | |
2585 TRACE("Pointer for range %d (spilled at %d) at safe point %d\n", | 2689 TRACE("Pointer for range %d (spilled at %d) at safe point %d\n", |
2586 range->id(), range->spill_start_index(), safe_point); | 2690 range->id(), spill_index, safe_point); |
2587 map->RecordReference(AllocatedOperand::cast(spill_operand)); | 2691 map->RecordReference(AllocatedOperand::cast(spill_operand)); |
2588 } | 2692 } |
2589 | 2693 |
2590 if (!cur->spilled()) { | 2694 if (!cur->spilled()) { |
2591 TRACE( | 2695 TRACE( |
2592 "Pointer in register for range %d (start at %d) " | 2696 "Pointer in register for range %d (start at %d) " |
2593 "at safe point %d\n", | 2697 "at safe point %d\n", |
2594 cur->id(), cur->Start().value(), safe_point); | 2698 cur->id(), cur->Start().value(), safe_point); |
2595 auto operand = cur->GetAssignedOperand(); | 2699 auto operand = cur->GetAssignedOperand(); |
2596 DCHECK(!operand.IsStackSlot()); | 2700 DCHECK(!operand.IsStackSlot()); |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2773 if (CanEagerlyResolveControlFlow(block)) continue; | 2877 if (CanEagerlyResolveControlFlow(block)) continue; |
2774 auto live = live_in_sets[block->rpo_number().ToInt()]; | 2878 auto live = live_in_sets[block->rpo_number().ToInt()]; |
2775 BitVector::Iterator iterator(live); | 2879 BitVector::Iterator iterator(live); |
2776 while (!iterator.Done()) { | 2880 while (!iterator.Done()) { |
2777 auto* array = finder.ArrayFor(iterator.Current()); | 2881 auto* array = finder.ArrayFor(iterator.Current()); |
2778 for (auto pred : block->predecessors()) { | 2882 for (auto pred : block->predecessors()) { |
2779 FindResult result; | 2883 FindResult result; |
2780 const auto* pred_block = code()->InstructionBlockAt(pred); | 2884 const auto* pred_block = code()->InstructionBlockAt(pred); |
2781 array->Find(block, pred_block, &result); | 2885 array->Find(block, pred_block, &result); |
2782 if (result.cur_cover_ == result.pred_cover_ || | 2886 if (result.cur_cover_ == result.pred_cover_ || |
2783 result.cur_cover_->spilled()) | 2887 (!result.cur_cover_->TopLevel()->IsSpilledInSingleDeferredBlock() && |
2888 result.cur_cover_->spilled())) | |
2784 continue; | 2889 continue; |
2785 auto pred_op = result.pred_cover_->GetAssignedOperand(); | 2890 auto pred_op = result.pred_cover_->GetAssignedOperand(); |
2786 auto cur_op = result.cur_cover_->GetAssignedOperand(); | 2891 auto cur_op = result.cur_cover_->GetAssignedOperand(); |
2787 if (pred_op.Equals(cur_op)) continue; | 2892 if (pred_op.Equals(cur_op)) continue; |
2788 ResolveControlFlow(block, cur_op, pred_block, pred_op); | 2893 ResolveControlFlow(block, cur_op, pred_block, pred_op); |
2789 } | 2894 } |
2790 iterator.Advance(); | 2895 iterator.Advance(); |
2791 } | 2896 } |
2792 } | 2897 } |
2793 } | 2898 } |
(...skipping 18 matching lines...) Expand all Loading... | |
2812 position = Instruction::END; | 2917 position = Instruction::END; |
2813 } | 2918 } |
2814 data()->AddGapMove(gap_index, position, pred_op, cur_op); | 2919 data()->AddGapMove(gap_index, position, pred_op, cur_op); |
2815 } | 2920 } |
2816 | 2921 |
2817 | 2922 |
2818 void LiveRangeConnector::ConnectRanges(Zone* local_zone) { | 2923 void LiveRangeConnector::ConnectRanges(Zone* local_zone) { |
2819 DelayedInsertionMap delayed_insertion_map(local_zone); | 2924 DelayedInsertionMap delayed_insertion_map(local_zone); |
2820 for (auto first_range : data()->live_ranges()) { | 2925 for (auto first_range : data()->live_ranges()) { |
2821 if (first_range == nullptr || first_range->IsChild()) continue; | 2926 if (first_range == nullptr || first_range->IsChild()) continue; |
2927 bool connect_spilled = first_range->IsSpilledInSingleDeferredBlock(); | |
2822 for (auto second_range = first_range->next(); second_range != nullptr; | 2928 for (auto second_range = first_range->next(); second_range != nullptr; |
2823 first_range = second_range, second_range = second_range->next()) { | 2929 first_range = second_range, second_range = second_range->next()) { |
2824 auto pos = second_range->Start(); | 2930 auto pos = second_range->Start(); |
2825 // Add gap move if the two live ranges touch and there is no block | 2931 // Add gap move if the two live ranges touch and there is no block |
2826 // boundary. | 2932 // boundary. |
2827 if (second_range->spilled()) continue; | 2933 if (!connect_spilled && second_range->spilled()) continue; |
2828 if (first_range->End() != pos) continue; | 2934 if (first_range->End() != pos) continue; |
2829 if (data()->IsBlockBoundary(pos) && | 2935 if (data()->IsBlockBoundary(pos) && |
2830 !CanEagerlyResolveControlFlow(GetInstructionBlock(code(), pos))) { | 2936 !CanEagerlyResolveControlFlow(GetInstructionBlock(code(), pos))) { |
2831 continue; | 2937 continue; |
2832 } | 2938 } |
2833 auto prev_operand = first_range->GetAssignedOperand(); | 2939 auto prev_operand = first_range->GetAssignedOperand(); |
2834 auto cur_operand = second_range->GetAssignedOperand(); | 2940 auto cur_operand = second_range->GetAssignedOperand(); |
2835 if (prev_operand.Equals(cur_operand)) continue; | 2941 if (prev_operand.Equals(cur_operand)) continue; |
2836 bool delay_insertion = false; | 2942 bool delay_insertion = false; |
2837 Instruction::GapPosition gap_pos; | 2943 Instruction::GapPosition gap_pos; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2884 auto eliminate = moves->PrepareInsertAfter(move); | 2990 auto eliminate = moves->PrepareInsertAfter(move); |
2885 to_insert.push_back(move); | 2991 to_insert.push_back(move); |
2886 if (eliminate != nullptr) to_eliminate.push_back(eliminate); | 2992 if (eliminate != nullptr) to_eliminate.push_back(eliminate); |
2887 } | 2993 } |
2888 } | 2994 } |
2889 | 2995 |
2890 | 2996 |
2891 } // namespace compiler | 2997 } // namespace compiler |
2892 } // namespace internal | 2998 } // namespace internal |
2893 } // namespace v8 | 2999 } // namespace v8 |
OLD | NEW |