blob: 512d058e41a9b9f25c4541cf505e7b190bb3f481 [file] [edit]
//===- AMDGPUEventTracking.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
/// Minimal API to track in-flight events that increment hardware instruction
/// counters (\ref InstCounterType, e.g. LOAD_CNT, DS_CNT). Intended as a
/// building block for waitcnt insertion: it tracks which events are
/// outstanding and how far back they are, but does not directly compute waits.
///
/// - \ref CounterInfo: constant per-counter description (events, limit).
/// - \ref EventTrackerRecord: a (MachineInstr, SingleHWEvent) pair.
/// - \ref EventTracker: per-MBB state; for each counter, a count and a
/// timeline of records ordered from oldest to youngest. Driven by the
/// client while visiting the MBB: \ref EventTracker::record adds events,
/// and \ref EventTracker::drain models waits.
///
/// This is a low-level API. It can be used directly for trivial waitcnt
/// calculation (e.g. wait on a specific MI), but the typical usage is for
/// clients to build helpers on top of it that combine the timeline with
/// additional information (e.g. alias analysis, subtarget-specific behavior)
/// to compute more precise waits.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_AMDGPU_AMDGPUEVENTTRACKING_H
#define LLVM_LIB_TARGET_AMDGPU_AMDGPUEVENTTRACKING_H
#include "AMDGPUHWEvents.h"
#include "AMDGPUWaitcntUtils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
namespace llvm {
class raw_ostream;
class MachineBasicBlock;
namespace AMDGPU {
namespace eventtracking {
/// FIXME: Move this to a common file and make it a generic utility.
///
/// Represents target-specific information about an available
/// \ref InstCounterType. This information is constant and inferred from the
/// subtarget.
struct CounterInfo {
CounterInfo() = default;
constexpr CounterInfo(InstCounterType T, HWEvents Events, unsigned Limit)
: CounterT(T), Events(Events), Limit(Limit) {}
/// Type of counter this is.
InstCounterType CounterT;
/// HWEvents for this counter.
HWEvents Events;
/// Hardware limit for this counter.
unsigned Limit;
};
/// Represents a record within the \ref EventTracker's timeline
/// for one \ref InstCounterType. A record is a pair of \ref MachineInstr
/// and \ref SingleHWEvent.
class EventTrackerRecord {
public:
EventTrackerRecord(MachineInstr *MI, SingleHWEvent Kind)
: MI(MI), Kind(Kind) {}
/// \returns the MachineInstr that originated this record.
MachineInstr *getMI() const { return MI; }
/// \returns the kind of record this is, as a \ref SingleHWEvent.
SingleHWEvent getKind() const { return Kind; }
void print(raw_ostream &OS, bool PrintMI = true, unsigned Indent = 0) const;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void dump() const;
#endif
private:
MachineInstr *MI;
SingleHWEvent Kind;
};
/// This assert serves as a reminder to be mindful of the size of the object.
static_assert(sizeof(EventTrackerRecord) == 16,
"EventTrackerRecord should remain small to optimize its layout "
"within cache lines, for maximum iteration speed");
/// Per-MBB tracking context which tracks the current value (count) of each
/// instruction counter and the timeline of \ref EventTrackerRecord
/// for each instruction counter.
///
/// This class is only responsible for tracking records for every
/// \ref InstCounterType. It does not deal with calculating the waitcnts needed,
/// or doing more advanced reasoning over the timeline for specific queries
/// (e.g. finding an aliasing store). These responsibilities are left to
/// helpers built on top of this class (see the file header).
///
/// The expected usage pattern is to visit the MBB in instruction order, call
/// \ref EventTracker::record whenever an interesting event occurs, and call
/// \ref EventTracker::drain whenever a wait on a counter is performed.
/// In between those operations, the user of the class can use all other methods
/// to query the current state of a counter, iterate its timeline, etc.
///
/// Allocation/storage/mapping of EventTrackers to MBBs is left to the user of
/// the class.
class EventTracker {
public:
/// \p MBB is the MachineBasicBlock for this tracker.
/// \p CounterInfos describes the available \ref InstCounterType. It must
/// contain exactly one entry for each counter type in
/// [0, CounterInfos.size()), and must outlive this class as its elements are
/// stored by reference.
EventTracker(const MachineBasicBlock &MBB,
ArrayRef<CounterInfo> CounterInfos);
/// Record an event of type \p Event at a MachineInstr \p MI, which will
/// affect all counters that have \p Event in their event set.
/// \pre \p Event belongs to at least one \ref InstCounterType.
void record(MachineInstr &MI, SingleHWEvent Event);
/// Drains the timeline of \p T such that:
/// - The \ref count of \p T is no higher than \p N.
/// - Only the \p N youngest records remain in the timeline.
void drain(InstCounterType T, unsigned N = 0);
/// \returns a conservative estimate of the current value of the counter \p T.
unsigned count(InstCounterType T) const;
/// \returns the set of pending HWEvents for \p T.
HWEvents getPendingEvents(InstCounterType T) const;
/// \returns the current timeline for \p T, which contains all known in-flight
/// (not drained) records, ordered from oldest to youngest.
/// The returned ArrayRef is invalidated whenever the timeline changes (e.g.
/// \ref record, \ref drain).
ArrayRef<EventTrackerRecord> getTimeline(InstCounterType T) const;
/// Prints a dump of the internal tracking state of this class for \p T to the
/// stream \p OS.
void print(raw_ostream &OS, InstCounterType T) const;
/// Prints a dump of all internal tracking state of this class to the stream
/// \p OS. If \p IgnoreEmpty is true, do not print counters with a count of 0.
void print(raw_ostream &OS, bool IgnoreEmpty = false,
unsigned Indent = 0) const;
#if !defined(NDEBUG) || defined(EXPENSIVE_CHECKS)
/// Verifies that the invariants of this class hold.
void verify() const;
#endif
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void dump() const;
#endif
private:
/// FIXME: I do not think it is relevant to expose this class like we expose
/// \ref EventTrackerRecord at this time. However, if this becomes complex
/// enough, we could clean it up and expose it if it leads to a better API.
struct CounterData {
const CounterInfo *CI = nullptr;
SmallVector<EventTrackerRecord, 16> Timeline;
/// The current value of the counter.
///
/// FIXME: We shouldn't need it for correctness so perhaps it should be
/// removed entirely, or be marked as debug.
uint32_t Count = 0;
};
static void print(raw_ostream &OS, const CounterData &CD,
unsigned Indent = 0);
const MachineBasicBlock *MBB;
// NB: This, combined with the inline storage of Timeline, can lead to this
// class becoming quite big - verify the size of this object whenever a change
// is made.
SmallVector<CounterData, InstCounterType::NUM_INST_CNTS> Counters;
};
} // namespace eventtracking
} // namespace AMDGPU
} // namespace llvm
#endif