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