• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 #ifndef V8_COMPILER_BACKEND_INSTRUCTION_SCHEDULER_H_
6 #define V8_COMPILER_BACKEND_INSTRUCTION_SCHEDULER_H_
7 
8 #include "src/base/optional.h"
9 #include "src/compiler/backend/instruction.h"
10 #include "src/zone/zone-containers.h"
11 
12 namespace v8 {
13 
14 namespace base {
15 class RandomNumberGenerator;
16 }  // namespace base
17 
18 namespace internal {
19 namespace compiler {
20 
21 // A set of flags describing properties of the instructions so that the
22 // scheduler is aware of dependencies between instructions.
23 enum ArchOpcodeFlags {
24   kNoOpcodeFlags = 0,
25   kHasSideEffect = 1,    // The instruction has some side effects (memory
26                          // store, function call...)
27   kIsLoadOperation = 2,  // The instruction is a memory load.
28   kMayNeedDeoptOrTrapCheck = 4,  // The instruction may be associated with a
29                                  // deopt or trap check which must be run before
30                                  // instruction e.g. div on Intel platform which
31                                  // will raise an exception when the divisor is
32                                  // zero.
33   kIsBarrier = 8,  // The instruction can cause GC or it reads/writes registers
34                    // that are not explicitly given. Nothing can be reordered
35                    // across such an instruction.
36 };
37 
38 class InstructionScheduler final : public ZoneObject {
39  public:
40   V8_EXPORT_PRIVATE InstructionScheduler(Zone* zone,
41                                          InstructionSequence* sequence);
42 
43   V8_EXPORT_PRIVATE void StartBlock(RpoNumber rpo);
44   V8_EXPORT_PRIVATE void EndBlock(RpoNumber rpo);
45 
46   V8_EXPORT_PRIVATE void AddInstruction(Instruction* instr);
47   V8_EXPORT_PRIVATE void AddTerminator(Instruction* instr);
48 
49   static bool SchedulerSupported();
50 
51  private:
52   // A scheduling graph node.
53   // Represent an instruction and their dependencies.
54   class ScheduleGraphNode : public ZoneObject {
55    public:
56     ScheduleGraphNode(Zone* zone, Instruction* instr);
57 
58     // Mark the instruction represented by 'node' as a dependency of this one.
59     // The current instruction will be registered as an unscheduled predecessor
60     // of 'node' (i.e. it must be scheduled before 'node').
61     void AddSuccessor(ScheduleGraphNode* node);
62 
63     // Check if all the predecessors of this instruction have been scheduled.
HasUnscheduledPredecessor()64     bool HasUnscheduledPredecessor() {
65       return unscheduled_predecessors_count_ != 0;
66     }
67 
68     // Record that we have scheduled one of the predecessors of this node.
DropUnscheduledPredecessor()69     void DropUnscheduledPredecessor() {
70       DCHECK_LT(0, unscheduled_predecessors_count_);
71       unscheduled_predecessors_count_--;
72     }
73 
instruction()74     Instruction* instruction() { return instr_; }
successors()75     ZoneDeque<ScheduleGraphNode*>& successors() { return successors_; }
latency()76     int latency() const { return latency_; }
77 
total_latency()78     int total_latency() const { return total_latency_; }
set_total_latency(int latency)79     void set_total_latency(int latency) { total_latency_ = latency; }
80 
start_cycle()81     int start_cycle() const { return start_cycle_; }
set_start_cycle(int start_cycle)82     void set_start_cycle(int start_cycle) { start_cycle_ = start_cycle; }
83 
84    private:
85     Instruction* instr_;
86     ZoneDeque<ScheduleGraphNode*> successors_;
87 
88     // Number of unscheduled predecessors for this node.
89     int unscheduled_predecessors_count_;
90 
91     // Estimate of the instruction latency (the number of cycles it takes for
92     // instruction to complete).
93     int latency_;
94 
95     // The sum of all the latencies on the path from this node to the end of
96     // the graph (i.e. a node with no successor).
97     int total_latency_;
98 
99     // The scheduler keeps a nominal cycle count to keep track of when the
100     // result of an instruction is available. This field is updated by the
101     // scheduler to indicate when the value of all the operands of this
102     // instruction will be available.
103     int start_cycle_;
104   };
105 
106   // Keep track of all nodes ready to be scheduled (i.e. all their dependencies
107   // have been scheduled. Note that this class is inteded to be extended by
108   // concrete implementation of the scheduling queue which define the policy
109   // to pop node from the queue.
110   class SchedulingQueueBase {
111    public:
SchedulingQueueBase(InstructionScheduler * scheduler)112     explicit SchedulingQueueBase(InstructionScheduler* scheduler)
113         : scheduler_(scheduler), nodes_(scheduler->zone()) {}
114 
115     void AddNode(ScheduleGraphNode* node);
116 
IsEmpty()117     bool IsEmpty() const { return nodes_.empty(); }
118 
119    protected:
120     InstructionScheduler* scheduler_;
121     ZoneLinkedList<ScheduleGraphNode*> nodes_;
122   };
123 
124   // A scheduling queue which prioritize nodes on the critical path (we look
125   // for the instruction with the highest latency on the path to reach the end
126   // of the graph).
127   class CriticalPathFirstQueue : public SchedulingQueueBase {
128    public:
CriticalPathFirstQueue(InstructionScheduler * scheduler)129     explicit CriticalPathFirstQueue(InstructionScheduler* scheduler)
130         : SchedulingQueueBase(scheduler) {}
131 
132     // Look for the best candidate to schedule, remove it from the queue and
133     // return it.
134     ScheduleGraphNode* PopBestCandidate(int cycle);
135   };
136 
137   // A queue which pop a random node from the queue to perform stress tests on
138   // the scheduler.
139   class StressSchedulerQueue : public SchedulingQueueBase {
140    public:
StressSchedulerQueue(InstructionScheduler * scheduler)141     explicit StressSchedulerQueue(InstructionScheduler* scheduler)
142         : SchedulingQueueBase(scheduler) {}
143 
144     ScheduleGraphNode* PopBestCandidate(int cycle);
145 
146    private:
random_number_generator()147     base::RandomNumberGenerator* random_number_generator() {
148       return scheduler_->random_number_generator();
149     }
150   };
151 
152   // Perform scheduling for the current block specifying the queue type to
153   // use to determine the next best candidate.
154   template <typename QueueType>
155   void Schedule();
156 
157   // Return the scheduling properties of the given instruction.
158   V8_EXPORT_PRIVATE int GetInstructionFlags(const Instruction* instr) const;
159   int GetTargetInstructionFlags(const Instruction* instr) const;
160 
IsBarrier(const Instruction * instr)161   bool IsBarrier(const Instruction* instr) const {
162     return (GetInstructionFlags(instr) & kIsBarrier) != 0;
163   }
164 
165   // Check whether the given instruction has side effects (e.g. function call,
166   // memory store).
HasSideEffect(const Instruction * instr)167   bool HasSideEffect(const Instruction* instr) const {
168     return (GetInstructionFlags(instr) & kHasSideEffect) != 0;
169   }
170 
171   // Return true if the instruction is a memory load.
IsLoadOperation(const Instruction * instr)172   bool IsLoadOperation(const Instruction* instr) const {
173     return (GetInstructionFlags(instr) & kIsLoadOperation) != 0;
174   }
175 
176   // The scheduler will not move the following instructions before the last
177   // deopt/trap check:
178   //  * loads (this is conservative)
179   //  * instructions with side effect
180   //  * other deopts/traps
181   // Any other instruction can be moved, apart from those that raise exceptions
182   // on specific inputs - these are filtered out by the deopt/trap check.
MayNeedDeoptOrTrapCheck(const Instruction * instr)183   bool MayNeedDeoptOrTrapCheck(const Instruction* instr) const {
184     return (GetInstructionFlags(instr) & kMayNeedDeoptOrTrapCheck) != 0;
185   }
186 
187   // Return true if the instruction cannot be moved before the last deopt or
188   // trap point we encountered.
DependsOnDeoptOrTrap(const Instruction * instr)189   bool DependsOnDeoptOrTrap(const Instruction* instr) const {
190     return MayNeedDeoptOrTrapCheck(instr) || instr->IsDeoptimizeCall() ||
191            instr->IsTrap() || HasSideEffect(instr) || IsLoadOperation(instr);
192   }
193 
194   // Identify nops used as a definition point for live-in registers at
195   // function entry.
IsFixedRegisterParameter(const Instruction * instr)196   bool IsFixedRegisterParameter(const Instruction* instr) const {
197     return (instr->arch_opcode() == kArchNop) && (instr->OutputCount() == 1) &&
198            (instr->OutputAt(0)->IsUnallocated()) &&
199            (UnallocatedOperand::cast(instr->OutputAt(0))
200                 ->HasFixedRegisterPolicy() ||
201             UnallocatedOperand::cast(instr->OutputAt(0))
202                 ->HasFixedFPRegisterPolicy());
203   }
204 
205   void ComputeTotalLatencies();
206 
207   static int GetInstructionLatency(const Instruction* instr);
208 
zone()209   Zone* zone() { return zone_; }
sequence()210   InstructionSequence* sequence() { return sequence_; }
random_number_generator()211   base::RandomNumberGenerator* random_number_generator() {
212     return &random_number_generator_.value();
213   }
214 
215   Zone* zone_;
216   InstructionSequence* sequence_;
217   ZoneVector<ScheduleGraphNode*> graph_;
218 
219   friend class InstructionSchedulerTester;
220 
221   // Last side effect instruction encountered while building the graph.
222   ScheduleGraphNode* last_side_effect_instr_;
223 
224   // Set of load instructions encountered since the last side effect instruction
225   // which will be added as predecessors of the next instruction with side
226   // effects.
227   ZoneVector<ScheduleGraphNode*> pending_loads_;
228 
229   // Live-in register markers are nop instructions which are emitted at the
230   // beginning of a basic block so that the register allocator will find a
231   // defining instruction for live-in values. They must not be moved.
232   // All these nops are chained together and added as a predecessor of every
233   // other instructions in the basic block.
234   ScheduleGraphNode* last_live_in_reg_marker_;
235 
236   // Last deoptimization or trap instruction encountered while building the
237   // graph.
238   ScheduleGraphNode* last_deopt_or_trap_;
239 
240   // Keep track of definition points for virtual registers. This is used to
241   // record operand dependencies in the scheduling graph.
242   ZoneMap<int32_t, ScheduleGraphNode*> operands_map_;
243 
244   base::Optional<base::RandomNumberGenerator> random_number_generator_;
245 };
246 
247 }  // namespace compiler
248 }  // namespace internal
249 }  // namespace v8
250 
251 #endif  // V8_COMPILER_BACKEND_INSTRUCTION_SCHEDULER_H_
252