[llvm] Introduce UnpredictableProfileLoader for PMU branch-miss profiles (PR #99027)

Wei Xiao via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 21 00:52:50 PDT 2024


================
@@ -0,0 +1,220 @@
+//=== UnpredictableProfileLoader.cpp - Unpredictable Profile Loader -------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass reads a sample profile containing mispredict counts and a sample
+// profile containing execution counts and computes branch mispredict ratios for
+// each conditional instruction. If a sufficiently high mispredict ratio is
+// found !unpredictable metadata is added.
+//
+// Note that this requires that the mispredict and frequency profiles have
+// comparable magnitudes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/IPO/UnpredictableProfileLoader.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/ProfileData/SampleProf.h"
+#include "llvm/ProfileData/SampleProfReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/Transforms/IPO.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "unpredictable-profile-loader"
+
+static cl::opt<std::string> UnpredictableHintsFile(
+    "unpredictable-hints-file",
+    cl::desc("Path to the unpredictability hints profile"), cl::Hidden);
+
+// Typically this file will be provided via PGOOpt. This option is provided
+// primarily for debugging and testing.
+static cl::opt<std::string>
+    FrequencyProfileOption("unpredictable-hints-frequency-profile",
+                           cl::desc("Path to an execution frequency profile to "
+                                    "use as a baseline for unpredictability"),
+                           cl::Hidden);
+
+// This determines the minimum apparent mispredict ratio which should earn a
+// mispredict metadata annotation.
+static cl::opt<double> MinimumRatio(
+    "unpredictable-hints-min-ratio",
+    cl::desc(
+        "Absolute minimum branch miss ratio to apply MD_unpredictable from"),
+    cl::init(0.2), cl::Hidden);
+
+// This option is useful for dealing with two different sampling frequencies.
+static cl::opt<double>
+    RatioFactor("unpredictable-hints-factor",
+                cl::desc("Multiply all ratios by this factor"), cl::init(1.0),
+                cl::ReallyHidden);
+
+// Lookup samples for an Instruction's corresponding location in a
+// FunctionSamples profile. The count returned is directly from the profile
+// representing the number of samples seen.
+ErrorOr<double> UnpredictableProfileLoaderPass::getMispredictRatio(
+    const FunctionSamples *FuncFreqSamples,
+    const FunctionSamples *FuncMispSamples, const Instruction *I) {
+
+  const auto &Loc = I->getDebugLoc();
+  if (!Loc)
+    return std::error_code();
+
+  const FunctionSamples *FreqSamples =
+      FuncFreqSamples->findFunctionSamples(Loc, FreqReader->getRemapper());
+  if (!FreqSamples)
+    return std::error_code();
+  const ErrorOr<uint64_t> FreqCount = FreqSamples->findSamplesAt(
+      FunctionSamples::getOffset(Loc), Loc->getBaseDiscriminator());
+  if (!FreqCount)
+    return std::error_code();
+
+  const FunctionSamples *MispSamples =
+      FuncMispSamples->findFunctionSamples(Loc, MispReader->getRemapper());
+  if (!MispSamples)
+    return std::error_code();
+  const ErrorOr<uint64_t> MispCount = MispSamples->findSamplesAt(
+      FunctionSamples::getOffset(Loc), Loc->getBaseDiscriminator());
+  if (!MispCount)
+    return std::error_code();
+
+  const double Freq = FreqCount.get();
+  if (!Freq)
+    return std::error_code();
+
+  const double Misp = MispCount.get();
+  const double MissRatio = (Misp * RatioFactor) / Freq;
+
+  LLVM_DEBUG(dbgs() << "Computing mispredict ratio of " << format("%0.2f", Misp)
+                    << "/" << format("%0.2f", Freq) << " * "
+                    << format("%0.2f", RatioFactor.getValue()) << " = "
+                    << format("%0.2f", MissRatio) << " for instruction\n"
+                    << *I << "\n");
+  return MissRatio;
+}
+
+// Examine all Select and BranchInsts in a function, adding !unpredictable
+// metadata if they appear in the mispredict profile with sufficient weight.
+bool UnpredictableProfileLoaderPass::addUpredictableMetadata(Function &F) {
+
+  const FunctionSamples *FreqSamples = FreqReader->getSamplesFor(F);
+  if (!FreqSamples)
+    return false;
+
+  const FunctionSamples *MispSamples = MispReader->getSamplesFor(F);
+  if (!MispSamples)
+    return false;
+
+  bool MadeChange = false;
+  for (BasicBlock &BB : F) {
+    for (Instruction &I : BB) {
+      if (!isa<BranchInst>(&I) && !isa<SelectInst>(&I) && !isa<SwitchInst>(&I))
+        continue;
+      if (I.hasMetadata(LLVMContext::MD_unpredictable))
+        continue;
+
+      const ErrorOr<double> RatioOrError =
+          getMispredictRatio(FreqSamples, MispSamples, &I);
+      if (!RatioOrError)
+        continue;
+      const double MissRatio = RatioOrError.get();
+
+      if (MissRatio < MinimumRatio) {
+        LLVM_DEBUG(dbgs() << "\tRatio " << format("%0.2f", MissRatio)
+                          << " is below threshold of "
+                          << format("%0.2f", MinimumRatio.getValue())
+                          << "; ignoring.\n");
+        continue;
+      }
+
+      // In the future we probably want to attach more information here, such as
+      // the mispredict count or ratio.
+      MDNode *MD = MDNode::get(I.getContext(), std::nullopt);
+      I.setMetadata(LLVMContext::MD_unpredictable, MD);
+      MadeChange = true;
+    }
+  }
+
+  return MadeChange;
+}
+
+bool UnpredictableProfileLoaderPass::addUpredictableMetadata(Module &M) {
+  bool MadeChange = false;
+
+  for (Function &F : M)
+    MadeChange |= addUpredictableMetadata(F);
+
+  // Return an indication of whether we changed anything or not.
+  return MadeChange;
+}
+
+bool UnpredictableProfileLoaderPass::loadSampleProfile(Module &M) {
+  if (MispReader && FreqReader)
+    return true;
+
+  assert(!MispReader && !FreqReader &&
+         "Expected both or neither profile readers");
+
+  LLVMContext &Ctx = M.getContext();
+  auto FS = vfs::getRealFileSystem();
+
+  auto ReadProfile = [&Ctx,
+                      &FS](const std::string ProfileFile,
+                           std::unique_ptr<SampleProfileReader> &ReaderPtr) {
+    if (ProfileFile.empty())
+      return false;
+
+    ErrorOr<std::unique_ptr<SampleProfileReader>> ReaderOrErr =
+        SampleProfileReader::create(ProfileFile, Ctx, *FS);
+    if (std::error_code EC = ReaderOrErr.getError()) {
+      std::string Msg = "Could not open profile: " + EC.message();
+      Ctx.diagnose(DiagnosticInfoSampleProfile(ProfileFile, Msg,
+                                               DiagnosticSeverity::DS_Warning));
+      return false;
+    }
+
+    ReaderPtr = std::move(ReaderOrErr.get());
+    ReaderPtr->read();
----------------
williamweixiao wrote:

do we need to check the return value of "ReaderPtr->read()"?

https://github.com/llvm/llvm-project/pull/99027


More information about the llvm-commits mailing list