Index: src/compiler/instruction-scheduler.cc |
diff --git a/src/compiler/instruction-scheduler.cc b/src/compiler/instruction-scheduler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2f329ead41569959a6db4799f79b03a549d4f31a |
--- /dev/null |
+++ b/src/compiler/instruction-scheduler.cc |
@@ -0,0 +1,280 @@ |
+// Copyright 2015 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "src/compiler/instruction-scheduler.h" |
+ |
+#include "src/base/adapters.h" |
+ |
+namespace v8 { |
+namespace internal { |
+namespace compiler { |
+ |
+InstructionScheduler::ScheduleGraphNode::ScheduleGraphNode( |
+ Zone* zone, |
+ Instruction* instr) |
+ : instr_(instr), |
+ successors_(zone), |
+ unscheduled_predecessors_count_(0), |
+ latency_(GetInstructionLatency(instr)), |
+ total_latency_(-1), |
+ start_cycle_(-1) { |
+} |
+ |
+ |
+void InstructionScheduler::ScheduleGraphNode::AddSuccessor( |
+ ScheduleGraphNode* node) { |
+ successors_.push_back(node); |
+ node->unscheduled_predecessors_count_++; |
+} |
+ |
+ |
+InstructionScheduler::InstructionScheduler(Zone* zone, |
+ InstructionSequence* sequence) |
+ : zone_(zone), |
+ sequence_(sequence), |
+ graph_(zone), |
+ last_side_effect_instr_(nullptr), |
+ pending_loads_(zone), |
+ last_live_in_reg_marker_(nullptr) { |
+} |
+ |
+ |
+void InstructionScheduler::StartBlock(RpoNumber rpo) { |
+ DCHECK(graph_.empty()); |
+ DCHECK(last_side_effect_instr_ == nullptr); |
+ DCHECK(pending_loads_.empty()); |
+ DCHECK(last_live_in_reg_marker_ == nullptr); |
+ sequence()->StartBlock(rpo); |
+} |
+ |
+ |
+void InstructionScheduler::EndBlock(RpoNumber rpo) { |
+ ScheduleBlock(); |
+ sequence()->EndBlock(rpo); |
+ graph_.clear(); |
+ last_side_effect_instr_ = nullptr; |
+ pending_loads_.clear(); |
+ last_live_in_reg_marker_ = nullptr; |
+} |
+ |
+ |
+void InstructionScheduler::AddInstruction(Instruction* instr) { |
+ ScheduleGraphNode* new_node = new (zone()) ScheduleGraphNode(zone(), instr); |
+ |
+ if (IsBlockTerminator(instr)) { |
+ // Make sure that basic block terminators are not moved by adding them |
+ // as successor of every instruction. |
+ for (auto node : graph_) { |
+ node->AddSuccessor(new_node); |
+ } |
+ } else if (IsFixedRegisterParameter(instr)) { |
+ if (last_live_in_reg_marker_ != nullptr) { |
+ last_live_in_reg_marker_->AddSuccessor(new_node); |
+ } |
+ last_live_in_reg_marker_ = new_node; |
+ } else { |
+ if (last_live_in_reg_marker_ != nullptr) { |
+ last_live_in_reg_marker_->AddSuccessor(new_node); |
+ } |
+ |
+ // Instructions with side effects and memory operations can't be |
+ // reordered with respect to each other. |
+ if (HasSideEffect(instr)) { |
+ if (last_side_effect_instr_ != nullptr) { |
+ last_side_effect_instr_->AddSuccessor(new_node); |
+ } |
+ for (auto load : pending_loads_) { |
+ load->AddSuccessor(new_node); |
+ } |
+ pending_loads_.clear(); |
+ last_side_effect_instr_ = new_node; |
+ } else if (IsLoadOperation(instr)) { |
+ // Load operations can't be reordered with side effects instructions but |
+ // independent loads can be reordered with respect to each other. |
+ if (last_side_effect_instr_ != nullptr) { |
+ last_side_effect_instr_->AddSuccessor(new_node); |
+ } |
+ pending_loads_.push_back(new_node); |
+ } |
+ |
+ // Look for operand dependencies. |
+ for (auto node : graph_) { |
+ if (HasOperandDependency(node->instruction(), instr)) { |
+ node->AddSuccessor(new_node); |
+ } |
+ } |
+ } |
+ |
+ graph_.push_back(new_node); |
+} |
+ |
+ |
+bool InstructionScheduler::CompareNodes(ScheduleGraphNode *node1, |
+ ScheduleGraphNode *node2) const { |
+ return node1->total_latency() > node2->total_latency(); |
+} |
+ |
+ |
+void InstructionScheduler::ScheduleBlock() { |
+ ZoneLinkedList<ScheduleGraphNode*> ready_list(zone()); |
+ |
+ // Compute total latencies so that we can schedule the critical path first. |
+ ComputeTotalLatencies(); |
+ |
+ // Add nodes which don't have dependencies to the ready list. |
+ for (auto node : graph_) { |
+ if (!node->HasUnscheduledPredecessor()) { |
+ ready_list.push_back(node); |
+ } |
+ } |
+ |
+ // Go through the ready list and schedule the instructions. |
+ int cycle = 0; |
+ while (!ready_list.empty()) { |
+ auto candidate = ready_list.end(); |
+ for (auto iterator = ready_list.begin(); iterator != ready_list.end(); |
+ ++iterator) { |
+ // Look for the best candidate to schedule. |
+ // We only consider instructions that have all their operands ready and |
+ // we try to schedule the critical path first (we look for the instruction |
+ // with the highest latency on the path to reach the end of the graph). |
+ if (cycle >= (*iterator)->start_cycle()) { |
+ if ((candidate == ready_list.end()) || |
+ CompareNodes(*iterator, *candidate)) { |
+ candidate = iterator; |
+ } |
+ } |
+ } |
+ |
+ if (candidate != ready_list.end()) { |
+ sequence()->AddInstruction((*candidate)->instruction()); |
+ |
+ for (auto successor : (*candidate)->successors()) { |
+ successor->DropUnscheduledPredecessor(); |
+ successor->set_start_cycle( |
+ std::max(successor->start_cycle(), |
+ cycle + (*candidate)->latency())); |
+ |
+ if (!successor->HasUnscheduledPredecessor()) { |
+ ready_list.push_back(successor); |
+ } |
+ } |
+ |
+ ready_list.erase(candidate); |
+ } |
+ |
+ cycle++; |
+ } |
+} |
+ |
+ |
+int InstructionScheduler::GetInstructionFlags(const Instruction* instr) const { |
+ switch (instr->arch_opcode()) { |
+ case kArchNop: |
+ case kArchStackPointer: |
+ case kArchFramePointer: |
+ case kArchTruncateDoubleToI: |
+ return kNoOpcodeFlags; |
+ |
+ case kArchPrepareCallCFunction: |
+ case kArchPrepareTailCall: |
+ case kArchCallCFunction: |
+ case kArchCallCodeObject: |
+ case kArchCallJSFunction: |
+ case kArchLazyBailout: |
+ return kHasSideEffect; |
+ |
+ case kArchTailCallCodeObject: |
+ case kArchTailCallJSFunction: |
+ return kHasSideEffect | kIsBlockTerminator; |
+ |
+ case kArchDeoptimize: |
+ case kArchJmp: |
+ case kArchLookupSwitch: |
+ case kArchTableSwitch: |
+ case kArchRet: |
+ case kArchThrowTerminator: |
+ return kIsBlockTerminator; |
+ |
+ case kCheckedLoadInt8: |
+ case kCheckedLoadUint8: |
+ case kCheckedLoadInt16: |
+ case kCheckedLoadUint16: |
+ case kCheckedLoadWord32: |
+ case kCheckedLoadWord64: |
+ case kCheckedLoadFloat32: |
+ case kCheckedLoadFloat64: |
+ return kIsLoadOperation; |
+ |
+ case kCheckedStoreWord8: |
+ case kCheckedStoreWord16: |
+ case kCheckedStoreWord32: |
+ case kCheckedStoreWord64: |
+ case kCheckedStoreFloat32: |
+ case kCheckedStoreFloat64: |
+ case kArchStoreWithWriteBarrier: |
+ return kHasSideEffect; |
+ |
+#define CASE(Name) case k##Name: |
+ TARGET_ARCH_OPCODE_LIST(CASE) |
+#undef CASE |
+ return GetTargetInstructionFlags(instr); |
+ } |
+ |
+ UNREACHABLE(); |
+ return kNoOpcodeFlags; |
+} |
+ |
+ |
+bool InstructionScheduler::HasOperandDependency( |
+ const Instruction* instr1, const Instruction* instr2) const { |
+ for (size_t i = 0; i < instr1->OutputCount(); ++i) { |
+ for (size_t j = 0; j < instr2->InputCount(); ++j) { |
+ const InstructionOperand* output = instr1->OutputAt(i); |
+ const InstructionOperand* input = instr2->InputAt(j); |
+ |
+ if (output->IsUnallocated() && input->IsUnallocated() && |
+ (UnallocatedOperand::cast(output)->virtual_register() == |
+ UnallocatedOperand::cast(input)->virtual_register())) { |
+ return true; |
+ } |
+ |
+ if (output->IsConstant() && input->IsUnallocated() && |
+ (ConstantOperand::cast(output)->virtual_register() == |
+ UnallocatedOperand::cast(input)->virtual_register())) { |
+ return true; |
+ } |
+ } |
+ } |
+ |
+ // TODO(bafsa): Do we need to look for anti-dependencies/output-dependencies? |
+ |
+ return false; |
+} |
+ |
+ |
+bool InstructionScheduler::IsBlockTerminator(const Instruction* instr) const { |
+ return ((GetInstructionFlags(instr) & kIsBlockTerminator) || |
+ (instr->flags_mode() == kFlags_branch)); |
+} |
+ |
+ |
+void InstructionScheduler::ComputeTotalLatencies() { |
+ for (auto node : base::Reversed(graph_)) { |
+ int max_latency = 0; |
+ |
+ for (auto successor : node->successors()) { |
+ DCHECK(successor->total_latency() != -1); |
+ if (successor->total_latency() > max_latency) { |
+ max_latency = successor->total_latency(); |
+ } |
+ } |
+ |
+ node->set_total_latency(max_latency + node->latency()); |
+ } |
+} |
+ |
+} // namespace compiler |
+} // namespace internal |
+} // namespace v8 |