blob: 1dd93bd47a3545b23fb003f96f35c0bb4f343eeb [file]
//===- AMDGPUEventTracking.cpp ----------------------------------*- 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
//
//===----------------------------------------------------------------------===//
#include "AMDGPUEventTracking.h"
#include "AMDGPUHWEvents.h"
#include "AMDGPUWaitcntUtils.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include <algorithm>
#define DEBUG_TYPE "amdgpu-event-tracking"
namespace llvm {
#ifndef NDEBUG
static cl::opt<bool> EventTrackerPrintAll(
"amdgpu-event-tracker-print-all", cl::init(false),
cl::desc("When using -debug, print the updated timeline every time a "
"record is added or removed"));
#endif
namespace AMDGPU {
namespace eventtracking {
void EventTrackerRecord::print(raw_ostream &OS, bool PrintMI,
unsigned Indent) const {
OS.indent(Indent) << "Record Kind=" << Kind << ": ";
if (PrintMI && MI)
OS << *MI;
else
OS << "MI@" << (void *)MI << '\n';
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void EventTrackerRecord::dump() const {
dbgs() << '\n';
print(dbgs(), /*PrintMI=*/true);
dbgs() << '\n';
}
#endif
EventTracker::EventTracker(const MachineBasicBlock &MBB,
ArrayRef<CounterInfo> CounterInfos)
: MBB(&MBB) {
Counters.resize(CounterInfos.size());
for (const CounterInfo &Info : CounterInfos)
Counters[Info.CounterT].CI = &Info;
}
void EventTracker::record(MachineInstr &MI, SingleHWEvent Event) {
LLVM_DEBUG(dbgs() << "[EventTracker] Recording " << Event << ": " << MI);
EventTrackerRecord Rec = EventTrackerRecord(&MI, Event);
[[maybe_unused]] bool FoundMatch = false;
for (CounterData &CD : Counters) {
if (!CD.CI->Events.contains(Event))
continue;
FoundMatch = true;
++CD.Count;
CD.Timeline.push_back(Rec);
#ifndef NDEBUG
LLVM_DEBUG(if (EventTrackerPrintAll) {
dbgs().indent(2) << "Updated Timeline:\n";
print(dbgs(), CD, /*Indent=*/4);
});
#endif
}
assert(FoundMatch && "Event has no matching InstCounterType!");
#ifdef EXPENSIVE_CHECKS
verify();
#endif
}
void EventTracker::drain(InstCounterType T, unsigned N) {
CounterData &CD = Counters[T];
LLVM_DEBUG(dbgs() << "[EventTracker] Drain " << getInstCounterName(T)
<< " at " << N << '\n');
// Fast path for clearing the counter.
if (N == 0) {
CD.Timeline.clear();
CD.Count = 0;
} else {
CD.Count = std::min(CD.Count, N);
// Only keep the N youngest records.
if (CD.Timeline.size() > N) {
auto *RmEnd = CD.Timeline.end() - N;
LLVM_DEBUG(for (const EventTrackerRecord &E
: make_range(CD.Timeline.begin(), RmEnd)) {
dbgs() << " | Removing ";
E.print(dbgs());
});
CD.Timeline.erase(CD.Timeline.begin(), RmEnd);
}
LLVM_DEBUG(dbgs() << " | => Updated Count:" << CD.Count << '\n');
#ifndef NDEBUG
LLVM_DEBUG(if (EventTrackerPrintAll) {
dbgs().indent(2) << "Updated Timeline:\n";
print(dbgs(), CD, /*Indent=*/4);
});
#endif
}
#ifdef EXPENSIVE_CHECKS
verify();
#endif
}
unsigned EventTracker::count(InstCounterType T) const {
return Counters[T].Count;
}
HWEvents EventTracker::getPendingEvents(InstCounterType T) const {
HWEvents Res;
for (const auto &E : Counters[T].Timeline)
Res |= E.getKind();
return Res;
}
ArrayRef<EventTrackerRecord>
EventTracker::getTimeline(InstCounterType T) const {
return Counters[T].Timeline;
}
void EventTracker::print(raw_ostream &OS, InstCounterType T) const {
print(OS, Counters[T]);
}
#if !defined(NDEBUG) || defined(EXPENSIVE_CHECKS)
void EventTracker::verify() const {
if (!MBB)
llvm_unreachable("EventTracker has no MBB!");
for (const CounterData &C : Counters) {
const auto OnError = [&]() {
dbgs() << "EventTracker verification error\n";
print(dbgs(), C);
};
// The timeline cannot have more records than the value of the counter.
if (C.Timeline.size() > C.Count) {
OnError();
llvm_unreachable("timeline has more records than the counter value");
}
}
}
#endif
void EventTracker::print(raw_ostream &OS, bool IgnoreEmpty,
unsigned Indent) const {
OS.indent(Indent) << "EventTracker for ";
MBB->printAsOperand(OS);
OS << ':';
if (IgnoreEmpty) {
if (all_of(Counters, [](const CounterData &CD) { return CD.Count == 0; })) {
OS << " (empty)\n";
return;
}
}
OS << '\n';
for (const CounterData &C : Counters) {
if (IgnoreEmpty && C.Count == 0)
continue;
print(OS, C, Indent + 2);
}
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void EventTracker::dump() const {
dbgs() << '\n';
print(dbgs());
dbgs() << '\n';
}
#endif
void EventTracker::print(raw_ostream &OS, const CounterData &CD,
unsigned Indent) {
OS.indent(Indent) << getInstCounterName(CD.CI->CounterT)
<< " (Count=" << CD.Count
<< ", Timeline=" << CD.Timeline.size() << ")\n";
for (const EventTrackerRecord &E : CD.Timeline) {
OS.indent(Indent + 2);
E.print(OS);
}
}
} // namespace eventtracking
} // namespace AMDGPU
} // namespace llvm