blob: adbfd5d10d755e022e6fc76f79d1b80a9694e63f [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/compiler/instruction-scheduler.h"
6
7#include "src/base/adapters.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +01008#include "src/base/utils/random-number-generator.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00009
10namespace v8 {
11namespace internal {
12namespace compiler {
13
Ben Murdoch097c5b22016-05-18 11:27:45 +010014// Compare the two nodes and return true if node1 is a better candidate than
15// node2 (i.e. node1 should be scheduled before node2).
16bool InstructionScheduler::CriticalPathFirstQueue::CompareNodes(
17 ScheduleGraphNode *node1, ScheduleGraphNode *node2) const {
18 return node1->total_latency() > node2->total_latency();
19}
20
21
22InstructionScheduler::ScheduleGraphNode*
23InstructionScheduler::CriticalPathFirstQueue::PopBestCandidate(int cycle) {
24 DCHECK(!IsEmpty());
25 auto candidate = nodes_.end();
26 for (auto iterator = nodes_.begin(); iterator != nodes_.end(); ++iterator) {
27 // We only consider instructions that have all their operands ready and
28 // we try to schedule the critical path first.
29 if (cycle >= (*iterator)->start_cycle()) {
30 if ((candidate == nodes_.end()) || CompareNodes(*iterator, *candidate)) {
31 candidate = iterator;
32 }
33 }
34 }
35
36 if (candidate != nodes_.end()) {
37 ScheduleGraphNode *result = *candidate;
38 nodes_.erase(candidate);
39 return result;
40 }
41
42 return nullptr;
43}
44
45
46InstructionScheduler::ScheduleGraphNode*
47InstructionScheduler::StressSchedulerQueue::PopBestCandidate(int cycle) {
48 DCHECK(!IsEmpty());
49 // Choose a random element from the ready list.
50 auto candidate = nodes_.begin();
51 std::advance(candidate, isolate()->random_number_generator()->NextInt(
52 static_cast<int>(nodes_.size())));
53 ScheduleGraphNode *result = *candidate;
54 nodes_.erase(candidate);
55 return result;
56}
57
58
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000059InstructionScheduler::ScheduleGraphNode::ScheduleGraphNode(
60 Zone* zone,
61 Instruction* instr)
62 : instr_(instr),
63 successors_(zone),
64 unscheduled_predecessors_count_(0),
65 latency_(GetInstructionLatency(instr)),
66 total_latency_(-1),
67 start_cycle_(-1) {
68}
69
70
71void InstructionScheduler::ScheduleGraphNode::AddSuccessor(
72 ScheduleGraphNode* node) {
73 successors_.push_back(node);
74 node->unscheduled_predecessors_count_++;
75}
76
77
78InstructionScheduler::InstructionScheduler(Zone* zone,
79 InstructionSequence* sequence)
80 : zone_(zone),
81 sequence_(sequence),
82 graph_(zone),
83 last_side_effect_instr_(nullptr),
84 pending_loads_(zone),
85 last_live_in_reg_marker_(nullptr) {
86}
87
88
89void InstructionScheduler::StartBlock(RpoNumber rpo) {
90 DCHECK(graph_.empty());
91 DCHECK(last_side_effect_instr_ == nullptr);
92 DCHECK(pending_loads_.empty());
93 DCHECK(last_live_in_reg_marker_ == nullptr);
94 sequence()->StartBlock(rpo);
95}
96
97
98void InstructionScheduler::EndBlock(RpoNumber rpo) {
Ben Murdoch097c5b22016-05-18 11:27:45 +010099 if (FLAG_turbo_stress_instruction_scheduling) {
100 ScheduleBlock<StressSchedulerQueue>();
101 } else {
102 ScheduleBlock<CriticalPathFirstQueue>();
103 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000104 sequence()->EndBlock(rpo);
105 graph_.clear();
106 last_side_effect_instr_ = nullptr;
107 pending_loads_.clear();
108 last_live_in_reg_marker_ = nullptr;
109}
110
111
112void InstructionScheduler::AddInstruction(Instruction* instr) {
113 ScheduleGraphNode* new_node = new (zone()) ScheduleGraphNode(zone(), instr);
114
115 if (IsBlockTerminator(instr)) {
116 // Make sure that basic block terminators are not moved by adding them
117 // as successor of every instruction.
118 for (auto node : graph_) {
119 node->AddSuccessor(new_node);
120 }
121 } else if (IsFixedRegisterParameter(instr)) {
122 if (last_live_in_reg_marker_ != nullptr) {
123 last_live_in_reg_marker_->AddSuccessor(new_node);
124 }
125 last_live_in_reg_marker_ = new_node;
126 } else {
127 if (last_live_in_reg_marker_ != nullptr) {
128 last_live_in_reg_marker_->AddSuccessor(new_node);
129 }
130
131 // Instructions with side effects and memory operations can't be
132 // reordered with respect to each other.
133 if (HasSideEffect(instr)) {
134 if (last_side_effect_instr_ != nullptr) {
135 last_side_effect_instr_->AddSuccessor(new_node);
136 }
137 for (auto load : pending_loads_) {
138 load->AddSuccessor(new_node);
139 }
140 pending_loads_.clear();
141 last_side_effect_instr_ = new_node;
142 } else if (IsLoadOperation(instr)) {
143 // Load operations can't be reordered with side effects instructions but
144 // independent loads can be reordered with respect to each other.
145 if (last_side_effect_instr_ != nullptr) {
146 last_side_effect_instr_->AddSuccessor(new_node);
147 }
148 pending_loads_.push_back(new_node);
149 }
150
151 // Look for operand dependencies.
152 for (auto node : graph_) {
153 if (HasOperandDependency(node->instruction(), instr)) {
154 node->AddSuccessor(new_node);
155 }
156 }
157 }
158
159 graph_.push_back(new_node);
160}
161
162
Ben Murdoch097c5b22016-05-18 11:27:45 +0100163template <typename QueueType>
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000164void InstructionScheduler::ScheduleBlock() {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100165 QueueType ready_list(this);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000166
167 // Compute total latencies so that we can schedule the critical path first.
168 ComputeTotalLatencies();
169
170 // Add nodes which don't have dependencies to the ready list.
171 for (auto node : graph_) {
172 if (!node->HasUnscheduledPredecessor()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100173 ready_list.AddNode(node);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000174 }
175 }
176
177 // Go through the ready list and schedule the instructions.
178 int cycle = 0;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100179 while (!ready_list.IsEmpty()) {
180 auto candidate = ready_list.PopBestCandidate(cycle);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000181
Ben Murdoch097c5b22016-05-18 11:27:45 +0100182 if (candidate != nullptr) {
183 sequence()->AddInstruction(candidate->instruction());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000184
Ben Murdoch097c5b22016-05-18 11:27:45 +0100185 for (auto successor : candidate->successors()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000186 successor->DropUnscheduledPredecessor();
187 successor->set_start_cycle(
188 std::max(successor->start_cycle(),
Ben Murdoch097c5b22016-05-18 11:27:45 +0100189 cycle + candidate->latency()));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000190
191 if (!successor->HasUnscheduledPredecessor()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100192 ready_list.AddNode(successor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000193 }
194 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000195 }
196
197 cycle++;
198 }
199}
200
201
202int InstructionScheduler::GetInstructionFlags(const Instruction* instr) const {
203 switch (instr->arch_opcode()) {
204 case kArchNop:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000205 case kArchFramePointer:
Ben Murdoch097c5b22016-05-18 11:27:45 +0100206 case kArchParentFramePointer:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000207 case kArchTruncateDoubleToI:
Ben Murdoch097c5b22016-05-18 11:27:45 +0100208 case kArchStackSlot:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000209 return kNoOpcodeFlags;
210
Ben Murdoch097c5b22016-05-18 11:27:45 +0100211 case kArchStackPointer:
212 // ArchStackPointer instruction loads the current stack pointer value and
213 // must not be reordered with instruction with side effects.
214 return kIsLoadOperation;
215
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000216 case kArchPrepareCallCFunction:
217 case kArchPrepareTailCall:
218 case kArchCallCFunction:
219 case kArchCallCodeObject:
220 case kArchCallJSFunction:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000221 return kHasSideEffect;
222
223 case kArchTailCallCodeObject:
224 case kArchTailCallJSFunction:
225 return kHasSideEffect | kIsBlockTerminator;
226
227 case kArchDeoptimize:
228 case kArchJmp:
229 case kArchLookupSwitch:
230 case kArchTableSwitch:
231 case kArchRet:
232 case kArchThrowTerminator:
233 return kIsBlockTerminator;
234
235 case kCheckedLoadInt8:
236 case kCheckedLoadUint8:
237 case kCheckedLoadInt16:
238 case kCheckedLoadUint16:
239 case kCheckedLoadWord32:
240 case kCheckedLoadWord64:
241 case kCheckedLoadFloat32:
242 case kCheckedLoadFloat64:
243 return kIsLoadOperation;
244
245 case kCheckedStoreWord8:
246 case kCheckedStoreWord16:
247 case kCheckedStoreWord32:
248 case kCheckedStoreWord64:
249 case kCheckedStoreFloat32:
250 case kCheckedStoreFloat64:
251 case kArchStoreWithWriteBarrier:
252 return kHasSideEffect;
253
254#define CASE(Name) case k##Name:
255 TARGET_ARCH_OPCODE_LIST(CASE)
256#undef CASE
257 return GetTargetInstructionFlags(instr);
258 }
259
260 UNREACHABLE();
261 return kNoOpcodeFlags;
262}
263
264
265bool InstructionScheduler::HasOperandDependency(
266 const Instruction* instr1, const Instruction* instr2) const {
267 for (size_t i = 0; i < instr1->OutputCount(); ++i) {
268 for (size_t j = 0; j < instr2->InputCount(); ++j) {
269 const InstructionOperand* output = instr1->OutputAt(i);
270 const InstructionOperand* input = instr2->InputAt(j);
271
272 if (output->IsUnallocated() && input->IsUnallocated() &&
273 (UnallocatedOperand::cast(output)->virtual_register() ==
274 UnallocatedOperand::cast(input)->virtual_register())) {
275 return true;
276 }
277
278 if (output->IsConstant() && input->IsUnallocated() &&
279 (ConstantOperand::cast(output)->virtual_register() ==
280 UnallocatedOperand::cast(input)->virtual_register())) {
281 return true;
282 }
283 }
284 }
285
286 // TODO(bafsa): Do we need to look for anti-dependencies/output-dependencies?
287
288 return false;
289}
290
291
292bool InstructionScheduler::IsBlockTerminator(const Instruction* instr) const {
293 return ((GetInstructionFlags(instr) & kIsBlockTerminator) ||
294 (instr->flags_mode() == kFlags_branch));
295}
296
297
298void InstructionScheduler::ComputeTotalLatencies() {
299 for (auto node : base::Reversed(graph_)) {
300 int max_latency = 0;
301
302 for (auto successor : node->successors()) {
303 DCHECK(successor->total_latency() != -1);
304 if (successor->total_latency() > max_latency) {
305 max_latency = successor->total_latency();
306 }
307 }
308
309 node->set_total_latency(max_latency + node->latency());
310 }
311}
312
313} // namespace compiler
314} // namespace internal
315} // namespace v8