//===----------------------- HWEventListener.h ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines the main interface for hardware event listeners.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_MCA_HWEVENTLISTENER_H
#define LLVM_MCA_HWEVENTLISTENER_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/MCA/Instruction.h"
#include "llvm/MCA/Support.h"

namespace llvm {
namespace mca {

// An HWInstructionEvent represents state changes of instructions that
// listeners might be interested in. Listeners can choose to ignore any event
// they are not interested in.
class HWInstructionEvent {
public:
  // This is the list of event types that are shared by all targets, that
  // generic subtarget-agnostic classes (e.g., Pipeline, HWInstructionEvent,
  // ...) and generic Views can manipulate.
  // Subtargets are free to define additional event types, that are goin to be
  // handled by generic components as opaque values, but can still be
  // emitted by subtarget-specific pipeline stages (e.g., ExecuteStage,
  // DispatchStage, ...) and interpreted by subtarget-specific EventListener
  // implementations.
  enum GenericEventType {
    Invalid = 0,
    // Events generated by the Retire Control Unit.
    Retired,
    // Events generated by the Scheduler.
    Pending,
    Ready,
    Issued,
    Executed,
    // Events generated by the Dispatch logic.
    Dispatched,

    LastGenericEventType,
  };

  HWInstructionEvent(unsigned type, const InstRef &Inst)
      : Type(type), IR(Inst) {}

  // The event type. The exact meaning depends on the subtarget.
  const unsigned Type;

  // The instruction this event was generated for.
  const InstRef &IR;
};

class HWInstructionIssuedEvent : public HWInstructionEvent {
public:
  using ResourceRef = std::pair<uint64_t, uint64_t>;
  HWInstructionIssuedEvent(const InstRef &IR,
                           ArrayRef<std::pair<ResourceRef, ResourceCycles>> UR)
      : HWInstructionEvent(HWInstructionEvent::Issued, IR), UsedResources(UR) {}

  ArrayRef<std::pair<ResourceRef, ResourceCycles>> UsedResources;
};

class HWInstructionDispatchedEvent : public HWInstructionEvent {
public:
  HWInstructionDispatchedEvent(const InstRef &IR, ArrayRef<unsigned> Regs,
                               unsigned UOps)
      : HWInstructionEvent(HWInstructionEvent::Dispatched, IR),
        UsedPhysRegs(Regs), MicroOpcodes(UOps) {}
  // Number of physical register allocated for this instruction. There is one
  // entry per register file.
  ArrayRef<unsigned> UsedPhysRegs;
  // Number of micro opcodes dispatched.
  // This field is often set to the total number of micro-opcodes specified by
  // the instruction descriptor of IR.
  // The only exception is when IR declares a number of micro opcodes
  // which exceeds the processor DispatchWidth, and - by construction - it
  // requires multiple cycles to be fully dispatched. In that particular case,
  // the dispatch logic would generate more than one dispatch event (one per
  // cycle), and each event would declare how many micro opcodes are effectively
  // been dispatched to the schedulers.
  unsigned MicroOpcodes;
};

class HWInstructionRetiredEvent : public HWInstructionEvent {
public:
  HWInstructionRetiredEvent(const InstRef &IR, ArrayRef<unsigned> Regs)
      : HWInstructionEvent(HWInstructionEvent::Retired, IR),
        FreedPhysRegs(Regs) {}
  // Number of register writes that have been architecturally committed. There
  // is one entry per register file.
  ArrayRef<unsigned> FreedPhysRegs;
};

// A HWStallEvent represents a pipeline stall caused by the lack of hardware
// resources.
class HWStallEvent {
public:
  enum GenericEventType {
    Invalid = 0,
    // Generic stall events generated by the DispatchStage.
    RegisterFileStall,
    RetireControlUnitStall,
    // Generic stall events generated by the Scheduler.
    DispatchGroupStall,
    SchedulerQueueFull,
    LoadQueueFull,
    StoreQueueFull,
    LastGenericEvent
  };

  HWStallEvent(unsigned type, const InstRef &Inst) : Type(type), IR(Inst) {}

  // The exact meaning of the stall event type depends on the subtarget.
  const unsigned Type;

  // The instruction this event was generated for.
  const InstRef &IR;
};

// A HWPressureEvent describes an increase in backend pressure caused by
// the presence of data dependencies or unavailability of pipeline resources.
class HWPressureEvent {
public:
  enum GenericReason {
    INVALID = 0,
    // Scheduler was unable to issue all the ready instructions because some
    // pipeline resources were unavailable.
    RESOURCES,
    // Instructions could not be issued because of register data dependencies.
    REGISTER_DEPS,
    // Instructions could not be issued because of memory dependencies.
    MEMORY_DEPS
  };

  HWPressureEvent(GenericReason reason, ArrayRef<InstRef> Insts,
                  uint64_t Mask = 0)
      : Reason(reason), AffectedInstructions(Insts), ResourceMask(Mask) {}

  // Reason for this increase in backend pressure.
  GenericReason Reason;

  // Instructions affected (i.e. delayed) by this increase in backend pressure.
  ArrayRef<InstRef> AffectedInstructions;

  // A mask of unavailable processor resources.
  const uint64_t ResourceMask;
};

class HWEventListener {
public:
  // Generic events generated by the pipeline.
  virtual void onCycleBegin() {}
  virtual void onCycleEnd() {}

  virtual void onEvent(const HWInstructionEvent &Event) {}
  virtual void onEvent(const HWStallEvent &Event) {}
  virtual void onEvent(const HWPressureEvent &Event) {}

  using ResourceRef = std::pair<uint64_t, uint64_t>;
  virtual void onResourceAvailable(const ResourceRef &RRef) {}

  // Events generated by the Scheduler when buffered resources are
  // consumed/freed for an instruction.
  virtual void onReservedBuffers(const InstRef &Inst,
                                 ArrayRef<unsigned> Buffers) {}
  virtual void onReleasedBuffers(const InstRef &Inst,
                                 ArrayRef<unsigned> Buffers) {}

  virtual ~HWEventListener() {}

private:
  virtual void anchor();
};
} // namespace mca
} // namespace llvm

#endif // LLVM_MCA_HWEVENTLISTENER_H
