[llvm] [llvm-calc-occupancy] Add an AMDGPU occupancy calculator tool (PR #208727)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 13 00:57:44 PDT 2026
================
@@ -0,0 +1,410 @@
+//===-- llvm-calc-occupancy.cpp - AMDGPU occupancy calculator -------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// A small standalone utility that answers "what occupancy do I get?" for an
+// AMDGPU kernel, given some subset of its resource usage: workgroup size,
+// VGPRs, SGPRs and LDS. Fields that are left unspecified are treated as
+// unconstrained, and the result is reported as a range (waves per EU).
+//
+// It reuses the compiler's own occupancy math (GCNSubtarget) so the numbers
+// match what the backend would compute for the same inputs.
+//
+// Example:
+// llvm-calc-occupancy -mcpu=gfx90a --wg-size=512 --vgprs=50 --sgprs=30 \
+// --lds=103kb
+//
+//===----------------------------------------------------------------------===//
+
+#include "AMDGPUTargetMachine.h"
+#include "GCNSubtarget.h"
+#include "Utils/AMDGPUBaseInfo.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/Triple.h"
+#include <optional>
+
+using namespace llvm;
+
+namespace {
+cl::OptionCategory OccCategory("llvm-calc-occupancy options");
+
+cl::opt<std::string> TripleName("mtriple", cl::desc("Target triple"),
+ cl::init("amdgcn-amd-amdhsa"),
+ cl::cat(OccCategory));
+
+cl::opt<std::string> MCPU("mcpu", cl::desc("Target GPU (e.g. gfx90a)"),
+ cl::init(""), cl::cat(OccCategory));
+
+cl::opt<std::string> MAttr("mattr",
+ cl::desc("Comma-separated subtarget features "
+ "(e.g. +wavefrontsize32)"),
+ cl::init(""), cl::cat(OccCategory));
+
+cl::opt<std::string>
+ WGSizeStr("wg-size",
+ cl::desc("Flat workgroup size: single value 'N' or range "
+ "'MIN:MAX' (default: 1:1024)"),
+ cl::init(""), cl::cat(OccCategory));
+cl::alias WGSizeAlias("flat-workgroup-size", cl::aliasopt(WGSizeStr));
+
+cl::opt<int> NumVGPRs("vgprs", cl::desc("VGPRs used per lane (default: none)"),
+ cl::init(-1), cl::cat(OccCategory));
+
+cl::opt<int> NumSGPRs("sgprs", cl::desc("SGPRs used per wave (default: none)"),
+ cl::init(-1), cl::cat(OccCategory));
+
+cl::opt<std::string> LDSStr("lds",
+ cl::desc("LDS bytes per workgroup, accepts k/kb/m "
+ "suffixes (default: 0)"),
+ cl::init(""), cl::cat(OccCategory));
+
+cl::opt<unsigned>
+ DynVGPRBlockSize("dynamic-vgpr-block-size",
+ cl::desc("Dynamic VGPR block size (0 = disabled)"),
+ cl::init(0), cl::cat(OccCategory));
+
+cl::opt<bool> ShowLimits("limits",
+ cl::desc("Print the per-occupancy VGPR/SGPR limit "
+ "table for this GPU"),
+ cl::init(false), cl::cat(OccCategory));
+} // namespace
+
+// Parse a byte size with an optional binary suffix (k/kb/kib/m/mb/mib), all
+// base 1024. A bare number is interpreted as bytes.
+static bool parseSize(StringRef S, uint64_t &Out) {
+ S = S.trim();
+ if (S.empty())
+ return false;
+ uint64_t Mult = 1;
+ StringRef Lower = S.lower();
+ static const std::pair<StringRef, uint64_t> Suffixes[] = {
+ {"kib", 1024}, {"kb", 1024}, {"k", 1024},
+ {"mib", 1024 * 1024}, {"mb", 1024 * 1024}, {"m", 1024 * 1024}};
+ for (const auto &[Suf, M] : Suffixes) {
+ if (Lower.ends_with(Suf)) {
+ Mult = M;
+ S = S.drop_back(Suf.size()).rtrim();
+ break;
+ }
+ }
+ uint64_t Value;
+ if (S.getAsInteger(10, Value))
+ return false;
+ Out = Value * Mult;
+ return true;
+}
+
+// Parse "N" or "MIN:MAX" (also accepts "MIN-MAX") into a flat workgroup range.
+static bool parseWGRange(StringRef S, unsigned &Min, unsigned &Max) {
+ S = S.trim();
+ StringRef LHS, RHS;
+ if (S.contains(':'))
+ std::tie(LHS, RHS) = S.split(':');
+ else if (S.contains('-'))
+ std::tie(LHS, RHS) = S.split('-');
+ else
+ LHS = RHS = S;
+
+ unsigned Lo, Hi;
+ if (LHS.trim().getAsInteger(10, Lo) || RHS.trim().getAsInteger(10, Hi))
+ return false;
+ if (Lo == 0 || Hi == 0 || Lo > Hi)
+ return false;
+ Min = Lo;
+ Max = Hi;
+ return true;
+}
+
+static std::string formatBytes(uint64_t Bytes) {
----------------
michaelselehov wrote:
Checked both: `format_bytes`/`format_bytes_with_ascii` (Support/Format.h)
produce a hex dump of an `ArrayRef<uint8_t>`, not a human-readable size. I also
went looking for `humanReadableSize` and couldn't find it: no such symbol in
current `main`, and `git log -S humanReadableSize --all -- llvm/` finds no
commit that ever added one, so I don't think it exists (the only
"human-readable size" helpers in-tree are a Python one in
`llvm/utils/git/github-automation.py` and third-party benchmark's
`HumanReadableNumber`). I searched for any spelling (KiB/KB/kB, MiB, GiB) and
the usual divide-by-1024 idiom too, not just the exact string, so there's no
drop-in public C++ helper for a "N bytes (K KiB)"-style summary. Kept the tiny
local `formatBytes`.
https://github.com/llvm/llvm-project/pull/208727
More information about the llvm-commits
mailing list