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