11cb0ef41Sopenharmony_ci// Copyright 2015 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#ifndef V8_COMPILER_BACKEND_INSTRUCTION_SCHEDULER_H_ 61cb0ef41Sopenharmony_ci#define V8_COMPILER_BACKEND_INSTRUCTION_SCHEDULER_H_ 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ci#include "src/base/optional.h" 91cb0ef41Sopenharmony_ci#include "src/base/utils/random-number-generator.h" 101cb0ef41Sopenharmony_ci#include "src/compiler/backend/instruction.h" 111cb0ef41Sopenharmony_ci#include "src/zone/zone-containers.h" 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_cinamespace v8 { 141cb0ef41Sopenharmony_cinamespace internal { 151cb0ef41Sopenharmony_cinamespace compiler { 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_ci// A set of flags describing properties of the instructions so that the 181cb0ef41Sopenharmony_ci// scheduler is aware of dependencies between instructions. 191cb0ef41Sopenharmony_cienum ArchOpcodeFlags { 201cb0ef41Sopenharmony_ci kNoOpcodeFlags = 0, 211cb0ef41Sopenharmony_ci kHasSideEffect = 1, // The instruction has some side effects (memory 221cb0ef41Sopenharmony_ci // store, function call...) 231cb0ef41Sopenharmony_ci kIsLoadOperation = 2, // The instruction is a memory load. 241cb0ef41Sopenharmony_ci kMayNeedDeoptOrTrapCheck = 4, // The instruction may be associated with a 251cb0ef41Sopenharmony_ci // deopt or trap check which must be run before 261cb0ef41Sopenharmony_ci // instruction e.g. div on Intel platform which 271cb0ef41Sopenharmony_ci // will raise an exception when the divisor is 281cb0ef41Sopenharmony_ci // zero. 291cb0ef41Sopenharmony_ci kIsBarrier = 8, // The instruction can cause GC or it reads/writes registers 301cb0ef41Sopenharmony_ci // that are not explicitly given. Nothing can be reordered 311cb0ef41Sopenharmony_ci // across such an instruction. 321cb0ef41Sopenharmony_ci}; 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ciclass InstructionScheduler final : public ZoneObject { 351cb0ef41Sopenharmony_ci public: 361cb0ef41Sopenharmony_ci V8_EXPORT_PRIVATE InstructionScheduler(Zone* zone, 371cb0ef41Sopenharmony_ci InstructionSequence* sequence); 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ci V8_EXPORT_PRIVATE void StartBlock(RpoNumber rpo); 401cb0ef41Sopenharmony_ci V8_EXPORT_PRIVATE void EndBlock(RpoNumber rpo); 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci V8_EXPORT_PRIVATE void AddInstruction(Instruction* instr); 431cb0ef41Sopenharmony_ci V8_EXPORT_PRIVATE void AddTerminator(Instruction* instr); 441cb0ef41Sopenharmony_ci 451cb0ef41Sopenharmony_ci static bool SchedulerSupported(); 461cb0ef41Sopenharmony_ci 471cb0ef41Sopenharmony_ci private: 481cb0ef41Sopenharmony_ci // A scheduling graph node. 491cb0ef41Sopenharmony_ci // Represent an instruction and their dependencies. 501cb0ef41Sopenharmony_ci class ScheduleGraphNode : public ZoneObject { 511cb0ef41Sopenharmony_ci public: 521cb0ef41Sopenharmony_ci ScheduleGraphNode(Zone* zone, Instruction* instr); 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci // Mark the instruction represented by 'node' as a dependency of this one. 551cb0ef41Sopenharmony_ci // The current instruction will be registered as an unscheduled predecessor 561cb0ef41Sopenharmony_ci // of 'node' (i.e. it must be scheduled before 'node'). 571cb0ef41Sopenharmony_ci void AddSuccessor(ScheduleGraphNode* node); 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci // Check if all the predecessors of this instruction have been scheduled. 601cb0ef41Sopenharmony_ci bool HasUnscheduledPredecessor() { 611cb0ef41Sopenharmony_ci return unscheduled_predecessors_count_ != 0; 621cb0ef41Sopenharmony_ci } 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci // Record that we have scheduled one of the predecessors of this node. 651cb0ef41Sopenharmony_ci void DropUnscheduledPredecessor() { 661cb0ef41Sopenharmony_ci DCHECK_LT(0, unscheduled_predecessors_count_); 671cb0ef41Sopenharmony_ci unscheduled_predecessors_count_--; 681cb0ef41Sopenharmony_ci } 691cb0ef41Sopenharmony_ci 701cb0ef41Sopenharmony_ci Instruction* instruction() { return instr_; } 711cb0ef41Sopenharmony_ci ZoneDeque<ScheduleGraphNode*>& successors() { return successors_; } 721cb0ef41Sopenharmony_ci int latency() const { return latency_; } 731cb0ef41Sopenharmony_ci 741cb0ef41Sopenharmony_ci int total_latency() const { return total_latency_; } 751cb0ef41Sopenharmony_ci void set_total_latency(int latency) { total_latency_ = latency; } 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ci int start_cycle() const { return start_cycle_; } 781cb0ef41Sopenharmony_ci void set_start_cycle(int start_cycle) { start_cycle_ = start_cycle; } 791cb0ef41Sopenharmony_ci 801cb0ef41Sopenharmony_ci private: 811cb0ef41Sopenharmony_ci Instruction* instr_; 821cb0ef41Sopenharmony_ci ZoneDeque<ScheduleGraphNode*> successors_; 831cb0ef41Sopenharmony_ci 841cb0ef41Sopenharmony_ci // Number of unscheduled predecessors for this node. 851cb0ef41Sopenharmony_ci int unscheduled_predecessors_count_; 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci // Estimate of the instruction latency (the number of cycles it takes for 881cb0ef41Sopenharmony_ci // instruction to complete). 891cb0ef41Sopenharmony_ci int latency_; 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ci // The sum of all the latencies on the path from this node to the end of 921cb0ef41Sopenharmony_ci // the graph (i.e. a node with no successor). 931cb0ef41Sopenharmony_ci int total_latency_; 941cb0ef41Sopenharmony_ci 951cb0ef41Sopenharmony_ci // The scheduler keeps a nominal cycle count to keep track of when the 961cb0ef41Sopenharmony_ci // result of an instruction is available. This field is updated by the 971cb0ef41Sopenharmony_ci // scheduler to indicate when the value of all the operands of this 981cb0ef41Sopenharmony_ci // instruction will be available. 991cb0ef41Sopenharmony_ci int start_cycle_; 1001cb0ef41Sopenharmony_ci }; 1011cb0ef41Sopenharmony_ci 1021cb0ef41Sopenharmony_ci // Keep track of all nodes ready to be scheduled (i.e. all their dependencies 1031cb0ef41Sopenharmony_ci // have been scheduled. Note that this class is inteded to be extended by 1041cb0ef41Sopenharmony_ci // concrete implementation of the scheduling queue which define the policy 1051cb0ef41Sopenharmony_ci // to pop node from the queue. 1061cb0ef41Sopenharmony_ci class SchedulingQueueBase { 1071cb0ef41Sopenharmony_ci public: 1081cb0ef41Sopenharmony_ci explicit SchedulingQueueBase(InstructionScheduler* scheduler) 1091cb0ef41Sopenharmony_ci : scheduler_(scheduler), nodes_(scheduler->zone()) {} 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ci void AddNode(ScheduleGraphNode* node); 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci bool IsEmpty() const { return nodes_.empty(); } 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci protected: 1161cb0ef41Sopenharmony_ci InstructionScheduler* scheduler_; 1171cb0ef41Sopenharmony_ci ZoneLinkedList<ScheduleGraphNode*> nodes_; 1181cb0ef41Sopenharmony_ci }; 1191cb0ef41Sopenharmony_ci 1201cb0ef41Sopenharmony_ci // A scheduling queue which prioritize nodes on the critical path (we look 1211cb0ef41Sopenharmony_ci // for the instruction with the highest latency on the path to reach the end 1221cb0ef41Sopenharmony_ci // of the graph). 1231cb0ef41Sopenharmony_ci class CriticalPathFirstQueue : public SchedulingQueueBase { 1241cb0ef41Sopenharmony_ci public: 1251cb0ef41Sopenharmony_ci explicit CriticalPathFirstQueue(InstructionScheduler* scheduler) 1261cb0ef41Sopenharmony_ci : SchedulingQueueBase(scheduler) {} 1271cb0ef41Sopenharmony_ci 1281cb0ef41Sopenharmony_ci // Look for the best candidate to schedule, remove it from the queue and 1291cb0ef41Sopenharmony_ci // return it. 1301cb0ef41Sopenharmony_ci ScheduleGraphNode* PopBestCandidate(int cycle); 1311cb0ef41Sopenharmony_ci }; 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci // A queue which pop a random node from the queue to perform stress tests on 1341cb0ef41Sopenharmony_ci // the scheduler. 1351cb0ef41Sopenharmony_ci class StressSchedulerQueue : public SchedulingQueueBase { 1361cb0ef41Sopenharmony_ci public: 1371cb0ef41Sopenharmony_ci explicit StressSchedulerQueue(InstructionScheduler* scheduler) 1381cb0ef41Sopenharmony_ci : SchedulingQueueBase(scheduler) {} 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci ScheduleGraphNode* PopBestCandidate(int cycle); 1411cb0ef41Sopenharmony_ci 1421cb0ef41Sopenharmony_ci private: 1431cb0ef41Sopenharmony_ci base::RandomNumberGenerator* random_number_generator() { 1441cb0ef41Sopenharmony_ci return scheduler_->random_number_generator(); 1451cb0ef41Sopenharmony_ci } 1461cb0ef41Sopenharmony_ci }; 1471cb0ef41Sopenharmony_ci 1481cb0ef41Sopenharmony_ci // Perform scheduling for the current block specifying the queue type to 1491cb0ef41Sopenharmony_ci // use to determine the next best candidate. 1501cb0ef41Sopenharmony_ci template <typename QueueType> 1511cb0ef41Sopenharmony_ci void Schedule(); 1521cb0ef41Sopenharmony_ci 1531cb0ef41Sopenharmony_ci // Return the scheduling properties of the given instruction. 1541cb0ef41Sopenharmony_ci V8_EXPORT_PRIVATE int GetInstructionFlags(const Instruction* instr) const; 1551cb0ef41Sopenharmony_ci int GetTargetInstructionFlags(const Instruction* instr) const; 1561cb0ef41Sopenharmony_ci 1571cb0ef41Sopenharmony_ci bool IsBarrier(const Instruction* instr) const { 1581cb0ef41Sopenharmony_ci return (GetInstructionFlags(instr) & kIsBarrier) != 0; 1591cb0ef41Sopenharmony_ci } 1601cb0ef41Sopenharmony_ci 1611cb0ef41Sopenharmony_ci // Check whether the given instruction has side effects (e.g. function call, 1621cb0ef41Sopenharmony_ci // memory store). 1631cb0ef41Sopenharmony_ci bool HasSideEffect(const Instruction* instr) const { 1641cb0ef41Sopenharmony_ci return (GetInstructionFlags(instr) & kHasSideEffect) != 0; 1651cb0ef41Sopenharmony_ci } 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ci // Return true if the instruction is a memory load. 1681cb0ef41Sopenharmony_ci bool IsLoadOperation(const Instruction* instr) const { 1691cb0ef41Sopenharmony_ci return (GetInstructionFlags(instr) & kIsLoadOperation) != 0; 1701cb0ef41Sopenharmony_ci } 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci bool CanTrap(const Instruction* instr) const { 1731cb0ef41Sopenharmony_ci return instr->IsTrap() || 1741cb0ef41Sopenharmony_ci (instr->HasMemoryAccessMode() && 1751cb0ef41Sopenharmony_ci instr->memory_access_mode() == kMemoryAccessProtected); 1761cb0ef41Sopenharmony_ci } 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ci // The scheduler will not move the following instructions before the last 1791cb0ef41Sopenharmony_ci // deopt/trap check: 1801cb0ef41Sopenharmony_ci // * loads (this is conservative) 1811cb0ef41Sopenharmony_ci // * instructions with side effect 1821cb0ef41Sopenharmony_ci // * other deopts/traps 1831cb0ef41Sopenharmony_ci // Any other instruction can be moved, apart from those that raise exceptions 1841cb0ef41Sopenharmony_ci // on specific inputs - these are filtered out by the deopt/trap check. 1851cb0ef41Sopenharmony_ci bool MayNeedDeoptOrTrapCheck(const Instruction* instr) const { 1861cb0ef41Sopenharmony_ci return (GetInstructionFlags(instr) & kMayNeedDeoptOrTrapCheck) != 0; 1871cb0ef41Sopenharmony_ci } 1881cb0ef41Sopenharmony_ci 1891cb0ef41Sopenharmony_ci // Return true if the instruction cannot be moved before the last deopt or 1901cb0ef41Sopenharmony_ci // trap point we encountered. 1911cb0ef41Sopenharmony_ci bool DependsOnDeoptOrTrap(const Instruction* instr) const { 1921cb0ef41Sopenharmony_ci return MayNeedDeoptOrTrapCheck(instr) || instr->IsDeoptimizeCall() || 1931cb0ef41Sopenharmony_ci CanTrap(instr) || HasSideEffect(instr) || IsLoadOperation(instr); 1941cb0ef41Sopenharmony_ci } 1951cb0ef41Sopenharmony_ci 1961cb0ef41Sopenharmony_ci // Identify nops used as a definition point for live-in registers at 1971cb0ef41Sopenharmony_ci // function entry. 1981cb0ef41Sopenharmony_ci bool IsFixedRegisterParameter(const Instruction* instr) const { 1991cb0ef41Sopenharmony_ci return (instr->arch_opcode() == kArchNop) && (instr->OutputCount() == 1) && 2001cb0ef41Sopenharmony_ci (instr->OutputAt(0)->IsUnallocated()) && 2011cb0ef41Sopenharmony_ci (UnallocatedOperand::cast(instr->OutputAt(0)) 2021cb0ef41Sopenharmony_ci ->HasFixedRegisterPolicy() || 2031cb0ef41Sopenharmony_ci UnallocatedOperand::cast(instr->OutputAt(0)) 2041cb0ef41Sopenharmony_ci ->HasFixedFPRegisterPolicy()); 2051cb0ef41Sopenharmony_ci } 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci void ComputeTotalLatencies(); 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci static int GetInstructionLatency(const Instruction* instr); 2101cb0ef41Sopenharmony_ci 2111cb0ef41Sopenharmony_ci Zone* zone() { return zone_; } 2121cb0ef41Sopenharmony_ci InstructionSequence* sequence() { return sequence_; } 2131cb0ef41Sopenharmony_ci base::RandomNumberGenerator* random_number_generator() { 2141cb0ef41Sopenharmony_ci return &random_number_generator_.value(); 2151cb0ef41Sopenharmony_ci } 2161cb0ef41Sopenharmony_ci 2171cb0ef41Sopenharmony_ci Zone* zone_; 2181cb0ef41Sopenharmony_ci InstructionSequence* sequence_; 2191cb0ef41Sopenharmony_ci ZoneVector<ScheduleGraphNode*> graph_; 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci friend class InstructionSchedulerTester; 2221cb0ef41Sopenharmony_ci 2231cb0ef41Sopenharmony_ci // Last side effect instruction encountered while building the graph. 2241cb0ef41Sopenharmony_ci ScheduleGraphNode* last_side_effect_instr_; 2251cb0ef41Sopenharmony_ci 2261cb0ef41Sopenharmony_ci // Set of load instructions encountered since the last side effect instruction 2271cb0ef41Sopenharmony_ci // which will be added as predecessors of the next instruction with side 2281cb0ef41Sopenharmony_ci // effects. 2291cb0ef41Sopenharmony_ci ZoneVector<ScheduleGraphNode*> pending_loads_; 2301cb0ef41Sopenharmony_ci 2311cb0ef41Sopenharmony_ci // Live-in register markers are nop instructions which are emitted at the 2321cb0ef41Sopenharmony_ci // beginning of a basic block so that the register allocator will find a 2331cb0ef41Sopenharmony_ci // defining instruction for live-in values. They must not be moved. 2341cb0ef41Sopenharmony_ci // All these nops are chained together and added as a predecessor of every 2351cb0ef41Sopenharmony_ci // other instructions in the basic block. 2361cb0ef41Sopenharmony_ci ScheduleGraphNode* last_live_in_reg_marker_; 2371cb0ef41Sopenharmony_ci 2381cb0ef41Sopenharmony_ci // Last deoptimization or trap instruction encountered while building the 2391cb0ef41Sopenharmony_ci // graph. 2401cb0ef41Sopenharmony_ci ScheduleGraphNode* last_deopt_or_trap_; 2411cb0ef41Sopenharmony_ci 2421cb0ef41Sopenharmony_ci // Keep track of definition points for virtual registers. This is used to 2431cb0ef41Sopenharmony_ci // record operand dependencies in the scheduling graph. 2441cb0ef41Sopenharmony_ci ZoneMap<int32_t, ScheduleGraphNode*> operands_map_; 2451cb0ef41Sopenharmony_ci 2461cb0ef41Sopenharmony_ci base::Optional<base::RandomNumberGenerator> random_number_generator_; 2471cb0ef41Sopenharmony_ci}; 2481cb0ef41Sopenharmony_ci 2491cb0ef41Sopenharmony_ci} // namespace compiler 2501cb0ef41Sopenharmony_ci} // namespace internal 2511cb0ef41Sopenharmony_ci} // namespace v8 2521cb0ef41Sopenharmony_ci 2531cb0ef41Sopenharmony_ci#endif // V8_COMPILER_BACKEND_INSTRUCTION_SCHEDULER_H_ 254