[llvm] [IR] Filter dumps by source location (PR #203393)

Yaxun Liu via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 11 13:48:34 PDT 2026


https://github.com/yxsamliu created https://github.com/llvm/llvm-project/pull/203393

[IR] Filter dumps by source location

Large dumps from `-print-after-all` can be hard to use when only a few source lines matter. Function filtering can still print too much and can miss code after inlining.

Add `-filter-print-source-locs` to limit dumps to IR or machine code with matching debug locations. The option accepts selected lines and ranges, and works with legacy and new print paths.



>From 3b4ba261fa51b49eb18175354700753e9b906fc5 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Wed, 10 Jun 2026 21:49:36 -0400
Subject: [PATCH] [IR] Filter dumps by source location

Large dumps from `-print-after-all` can be hard to use when only a few source lines matter. Function filtering can still print too much and can miss code after inlining.

Add `-filter-print-source-locs` to limit dumps to IR or machine code with matching debug locations. The option accepts selected lines and ranges, and works with legacy and new print paths.
---
 llvm/include/llvm/IR/PrintPasses.h            |  12 ++
 llvm/lib/Analysis/CallGraphSCCPass.cpp        |   7 +-
 llvm/lib/Analysis/LoopPass.cpp                |  20 +-
 llvm/lib/Analysis/RegionPass.cpp              |  17 +-
 .../CodeGen/MachineFunctionPrinterPass.cpp    |  16 +-
 llvm/lib/IR/IRPrintingPasses.cpp              |   6 +-
 llvm/lib/IR/PrintPasses.cpp                   | 185 ++++++++++++++++--
 llvm/lib/IRPrinter/IRPrintingPasses.cpp       |   6 +-
 llvm/lib/Passes/StandardInstrumentations.cpp  |  90 +++++----
 .../CodeGen/X86/source-loc-print-filter.ll    |  49 +++++
 llvm/test/Other/source-loc-print-filter.ll    |  97 +++++++++
 11 files changed, 444 insertions(+), 61 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/source-loc-print-filter.ll
 create mode 100644 llvm/test/Other/source-loc-print-filter.ll

diff --git a/llvm/include/llvm/IR/PrintPasses.h b/llvm/include/llvm/IR/PrintPasses.h
index 0aa1b379c35cf..c5c3b43871467 100644
--- a/llvm/include/llvm/IR/PrintPasses.h
+++ b/llvm/include/llvm/IR/PrintPasses.h
@@ -15,6 +15,9 @@
 
 namespace llvm {
 
+class DebugLoc;
+class Function;
+
 enum class ChangePrinter {
   None,
   Verbose,
@@ -61,6 +64,15 @@ bool isFilterPassesEmpty();
 // Returns true if we should print the function.
 bool isFunctionInPrintList(StringRef FunctionName);
 
+// Returns true if -filter-print-source-locs is empty or contains the source
+// location.
+bool isSourceLocInPrintList(const DebugLoc &Loc);
+bool isSourceLocFilterEmpty();
+
+// Returns true if the function passes the function-name and source-location
+// print filters.
+bool shouldPrintFunction(const Function &F);
+
 // Ensure temporary files exist, creating or re-using them.  \p FD contains
 // file descriptors (-1 indicates that the file should be created) and
 // \p SR contains the corresponding initial content.  \p FileName will have
diff --git a/llvm/lib/Analysis/CallGraphSCCPass.cpp b/llvm/lib/Analysis/CallGraphSCCPass.cpp
index 3fd2fe02f6688..75eb2c2e0a70d 100644
--- a/llvm/lib/Analysis/CallGraphSCCPass.cpp
+++ b/llvm/lib/Analysis/CallGraphSCCPass.cpp
@@ -683,7 +683,8 @@ namespace {
       };
 
       bool NeedModule = llvm::forcePrintModuleIR();
-      if (isFunctionInPrintList("*") && NeedModule) {
+      if (isFunctionInPrintList("*") && isSourceLocFilterEmpty() &&
+          NeedModule) {
         PrintBannerOnce();
         OS << "\n";
         SCC.getCallGraph().getModule().print(OS, nullptr);
@@ -692,14 +693,14 @@ namespace {
       bool FoundFunction = false;
       for (CallGraphNode *CGN : SCC) {
         if (Function *F = CGN->getFunction()) {
-          if (!F->isDeclaration() && isFunctionInPrintList(F->getName())) {
+          if (!F->isDeclaration() && shouldPrintFunction(*F)) {
             FoundFunction = true;
             if (!NeedModule) {
               PrintBannerOnce();
               F->print(OS);
             }
           }
-        } else if (isFunctionInPrintList("*")) {
+        } else if (isFunctionInPrintList("*") && isSourceLocFilterEmpty()) {
           PrintBannerOnce();
           OS << "\nPrinting <null> Function\n";
         }
diff --git a/llvm/lib/Analysis/LoopPass.cpp b/llvm/lib/Analysis/LoopPass.cpp
index 85c15c5721cd6..d6525643f9e3a 100644
--- a/llvm/lib/Analysis/LoopPass.cpp
+++ b/llvm/lib/Analysis/LoopPass.cpp
@@ -31,6 +31,21 @@ using namespace llvm;
 
 namespace {
 
+bool shouldPrintLoop(const Loop &L) {
+  Function *F = L.getHeader()->getParent();
+  if (!isFunctionInPrintList(F->getName()))
+    return false;
+
+  if (isSourceLocFilterEmpty())
+    return true;
+
+  for (const BasicBlock *BB : L.blocks())
+    for (const Instruction &I : *BB)
+      if (isSourceLocInPrintList(I.getDebugLoc()))
+        return true;
+  return false;
+}
+
 /// PrintLoopPass - Print a Function corresponding to a Loop.
 ///
 class PrintLoopPassWrapper : public LoopPass {
@@ -48,11 +63,8 @@ class PrintLoopPassWrapper : public LoopPass {
   }
 
   bool runOnLoop(Loop *L, LPPassManager &) override {
-    auto BBI = llvm::find_if(L->blocks(), [](BasicBlock *BB) { return BB; });
-    if (BBI != L->blocks().end() &&
-        isFunctionInPrintList((*BBI)->getParent()->getName())) {
+    if (shouldPrintLoop(*L))
       printLoop(*L, OS, Banner);
-    }
     return false;
   }
 
diff --git a/llvm/lib/Analysis/RegionPass.cpp b/llvm/lib/Analysis/RegionPass.cpp
index ae1d84659de86..63b7f8c6fc6d2 100644
--- a/llvm/lib/Analysis/RegionPass.cpp
+++ b/llvm/lib/Analysis/RegionPass.cpp
@@ -172,6 +172,21 @@ void RGPassManager::dumpPassStructure(unsigned Offset) {
 }
 
 namespace {
+bool shouldPrintRegion(const Region &R) {
+  if (!isFunctionInPrintList(R.getEntry()->getParent()->getName()))
+    return false;
+
+  if (isSourceLocFilterEmpty())
+    return true;
+
+  for (const BasicBlock *BB : R.blocks())
+    if (BB)
+      for (const Instruction &I : *BB)
+        if (isSourceLocInPrintList(I.getDebugLoc()))
+          return true;
+  return false;
+}
+
 //===----------------------------------------------------------------------===//
 // PrintRegionPass
 class PrintRegionPass : public RegionPass {
@@ -189,7 +204,7 @@ class PrintRegionPass : public RegionPass {
   }
 
   bool runOnRegion(Region *R, RGPassManager &RGM) override {
-    if (!isFunctionInPrintList(R->getEntry()->getParent()->getName()))
+    if (!shouldPrintRegion(*R))
       return false;
     Out << Banner;
     for (const auto *BB : R->blocks()) {
diff --git a/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp b/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
index 5111322023d04..e42f12db1aa8d 100644
--- a/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
+++ b/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
@@ -22,6 +22,20 @@
 using namespace llvm;
 
 namespace {
+bool shouldPrintMachineFunction(const MachineFunction &MF) {
+  if (!isFunctionInPrintList(MF.getName()))
+    return false;
+
+  if (isSourceLocFilterEmpty())
+    return true;
+
+  for (const MachineBasicBlock &MBB : MF)
+    for (const MachineInstr &MI : MBB)
+      if (isSourceLocInPrintList(MI.getDebugLoc()))
+        return true;
+  return false;
+}
+
 /// MachineFunctionPrinterPass - This is a pass to dump the IR of a
 /// MachineFunction.
 ///
@@ -44,7 +58,7 @@ struct MachineFunctionPrinterPass : public MachineFunctionPass {
   }
 
   bool runOnMachineFunction(MachineFunction &MF) override {
-    if (!isFunctionInPrintList(MF.getName()))
+    if (!shouldPrintMachineFunction(MF))
       return false;
     OS << "# " << Banner << ":\n";
     auto *SIWrapper = getAnalysisIfAvailable<SlotIndexesWrapperPass>();
diff --git a/llvm/lib/IR/IRPrintingPasses.cpp b/llvm/lib/IR/IRPrintingPasses.cpp
index 43671b0b9a1a3..d00f57a63e6c4 100644
--- a/llvm/lib/IR/IRPrintingPasses.cpp
+++ b/llvm/lib/IR/IRPrintingPasses.cpp
@@ -40,14 +40,14 @@ class PrintModulePassWrapper : public ModulePass {
         ShouldPreserveUseListOrder(ShouldPreserveUseListOrder) {}
 
   bool runOnModule(Module &M) override {
-    if (llvm::isFunctionInPrintList("*")) {
+    if (llvm::isFunctionInPrintList("*") && isSourceLocFilterEmpty()) {
       if (!Banner.empty())
         OS << Banner << "\n";
       M.print(OS, nullptr, ShouldPreserveUseListOrder);
     } else {
       bool BannerPrinted = false;
       for (const auto &F : M.functions()) {
-        if (llvm::isFunctionInPrintList(F.getName())) {
+        if (shouldPrintFunction(F)) {
           if (!BannerPrinted && !Banner.empty()) {
             OS << Banner << "\n";
             BannerPrinted = true;
@@ -79,7 +79,7 @@ class PrintFunctionPassWrapper : public FunctionPass {
 
   // This pass just prints a banner followed by the function as it's processed.
   bool runOnFunction(Function &F) override {
-    if (isFunctionInPrintList(F.getName())) {
+    if (shouldPrintFunction(F)) {
       if (forcePrintModuleIR())
         OS << Banner << " (function: " << F.getName() << ")\n"
            << *F.getParent();
diff --git a/llvm/lib/IR/PrintPasses.cpp b/llvm/lib/IR/PrintPasses.cpp
index 74d97d1fe5835..f3006bfee20bd 100644
--- a/llvm/lib/IR/PrintPasses.cpp
+++ b/llvm/lib/IR/PrintPasses.cpp
@@ -7,13 +7,23 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/IR/PrintPasses.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instruction.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/IOSandbox.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"
 #include <unordered_set>
+#include <vector>
 
 using namespace llvm;
 
@@ -41,23 +51,24 @@ static cl::opt<bool> PrintAfterAll("print-after-all",
 // this option. -filter-passes will limit the output to the named passes that
 // actually change the IR and other passes are reported as filtered out. The
 // specified passes will either be reported as making no changes (with no IR
-// reported) or the changed IR will be reported. Also, the -filter-print-funcs
-// and -print-module-scope options will do similar filtering based on function
-// name, reporting changed IRs as functions(or modules if -print-module-scope is
-// specified) for a particular function or indicating that the IR has been
-// filtered out. The extra options can be combined, allowing only changed IRs
-// for certain passes on certain functions to be reported in different formats,
-// with the rest being reported as filtered out.  The -print-before-changed
+// reported) or the changed IR will be reported. Also, the -filter-print-funcs,
+// -filter-print-source-locs and -print-module-scope options will do similar
+// filtering based on function name or source location, reporting changed IRs as
+// functions(or modules if -print-module-scope is specified) for a particular
+// function or indicating that the IR has been filtered out. The extra options
+// can be combined, allowing only changed IRs for certain passes on certain
+// functions or source locations to be reported in different formats, with the
+// rest being reported as filtered out.  The -print-before-changed
 // option will print the IR as it was before each pass that changed it. The
 // optional value of quiet will only report when the IR changes, suppressing all
 // other messages, including the initial IR. The values "diff" and "diff-quiet"
 // will present the changes in a form similar to a patch, in either verbose or
 // quiet mode, respectively. The lines that are removed and added are prefixed
-// with '-' and '+', respectively. The -filter-print-funcs and -filter-passes
-// can be used to filter the output.  This reporter relies on the linux diff
-// utility to do comparisons and insert the prefixes. For systems that do not
-// have the necessary facilities, the error message will be shown in place of
-// the expected output.
+// with '-' and '+', respectively. The -filter-print-funcs,
+// -filter-print-source-locs and -filter-passes can be used to filter the
+// output. This reporter relies on the linux diff utility to do comparisons and
+// insert the prefixes. For systems that do not have the necessary facilities,
+// the error message will be shown in place of the expected output.
 cl::opt<ChangePrinter> llvm::PrintChanged(
     "print-changed", cl::desc("Print changed IRs"), cl::Hidden,
     cl::ValueOptional, cl::init(ChangePrinter::None),
@@ -110,6 +121,10 @@ static cl::list<std::string>
                             "options"),
                    cl::CommaSeparated, cl::Hidden);
 
+static cl::list<std::string> PrintSourceLocs(
+    "filter-print-source-locs", cl::value_desc("file:line[,line-line][,line]"),
+    cl::desc("Only print IR containing matching source locations"), cl::Hidden);
+
 /// This is a helper to determine whether to print IR before or
 /// after a pass.
 
@@ -162,7 +177,151 @@ bool llvm::isFunctionInPrintList(StringRef FunctionName) {
   static std::unordered_set<std::string> PrintFuncNames(PrintFuncsList.begin(),
                                                         PrintFuncsList.end());
   return PrintFuncNames.empty() ||
-         PrintFuncNames.count(std::string(FunctionName));
+         PrintFuncNames.count(std::string(FunctionName)) ||
+         PrintFuncNames.count("*");
+}
+
+namespace {
+
+struct PrintLineRange {
+  unsigned First;
+  unsigned Last;
+};
+
+struct PrintSourceLocSpec {
+  std::string File;
+  SmallVector<PrintLineRange, 4> Lines;
+};
+
+[[noreturn]] void reportBadLocSpec(StringRef Spec) {
+  report_fatal_error(Twine("Invalid -filter-print-source-locs value '") + Spec +
+                     "'. Expected file:line[,line-line][,line].");
+}
+
+std::string normalizeSlashes(StringRef Path) {
+  std::string Result = Path.str();
+  for (char &C : Result)
+    if (C == '\\')
+      C = '/';
+  return Result;
+}
+
+bool parseLine(StringRef LineText, unsigned &Line) {
+  return !LineText.empty() && !LineText.getAsInteger(10, Line);
+}
+
+PrintLineRange parseLineRange(StringRef RangeText, StringRef FullSpec) {
+  auto [FirstText, LastText] = RangeText.split('-');
+
+  unsigned First;
+  if (!parseLine(FirstText, First))
+    reportBadLocSpec(FullSpec);
+
+  if (LastText.empty())
+    return {First, First};
+
+  unsigned Last;
+  if (!parseLine(LastText, Last) || Last < First)
+    reportBadLocSpec(FullSpec);
+
+  return {First, Last};
+}
+
+std::vector<PrintSourceLocSpec> parseSourceLocSpecs() {
+  std::vector<PrintSourceLocSpec> Result;
+  for (const std::string &RawSpec : PrintSourceLocs) {
+    StringRef Spec(RawSpec);
+    auto [File, LineSpec] = Spec.rsplit(':');
+    if (File.empty() || LineSpec.empty())
+      reportBadLocSpec(Spec);
+
+    PrintSourceLocSpec Parsed;
+    Parsed.File = normalizeSlashes(File);
+    for (StringRef RangeText : llvm::split(LineSpec, ",")) {
+      Parsed.Lines.push_back(parseLineRange(RangeText, Spec));
+    }
+    Result.push_back(std::move(Parsed));
+  }
+  return Result;
+}
+
+ArrayRef<PrintSourceLocSpec> getSourceLocSpecs() {
+  static const std::vector<PrintSourceLocSpec> Specs = parseSourceLocSpecs();
+  return Specs;
+}
+
+std::string makeDebugLocPath(StringRef Directory, StringRef Filename) {
+  std::string NormalizedFilename = normalizeSlashes(Filename);
+  if (Directory.empty() || sys::path::is_absolute(NormalizedFilename))
+    return NormalizedFilename;
+
+  std::string NormalizedDirectory = normalizeSlashes(Directory);
+  if (NormalizedDirectory.empty())
+    return NormalizedFilename;
+  if (NormalizedDirectory.back() == '/')
+    return NormalizedDirectory + NormalizedFilename;
+  return NormalizedDirectory + "/" + NormalizedFilename;
+}
+
+bool matchesFile(StringRef SpecFile, StringRef Directory, StringRef Filename) {
+  std::string LocFile = normalizeSlashes(Filename);
+  std::string LocPath = makeDebugLocPath(Directory, Filename);
+
+  if (SpecFile == LocFile || SpecFile == LocPath)
+    return true;
+
+  StringRef LocFileRef(LocFile);
+  StringRef LocPathRef(LocPath);
+  if (sys::path::filename(LocFileRef) == SpecFile)
+    return true;
+
+  std::string Suffix = (Twine("/") + SpecFile).str();
+  return LocFileRef.ends_with(Suffix) || LocPathRef.ends_with(Suffix);
+}
+
+bool matchesLine(ArrayRef<PrintLineRange> Ranges, unsigned Line) {
+  return any_of(Ranges, [Line](const PrintLineRange &Range) {
+    return Range.First <= Line && Line <= Range.Last;
+  });
+}
+
+bool matchesSourceLocSpec(const DebugLoc &Loc, const PrintSourceLocSpec &Spec) {
+  auto *Scope = dyn_cast_or_null<DIScope>(Loc.getScope());
+  return Scope &&
+         matchesFile(Spec.File, Scope->getDirectory(), Scope->getFilename()) &&
+         matchesLine(Spec.Lines, Loc.getLine());
+}
+
+} // namespace
+
+bool llvm::isSourceLocInPrintList(const DebugLoc &Loc) {
+  ArrayRef<PrintSourceLocSpec> Specs = getSourceLocSpecs();
+  if (Specs.empty())
+    return true;
+
+  for (DebugLoc CurLoc = Loc; CurLoc; CurLoc = CurLoc.getInlinedAt()) {
+    if (any_of(Specs, [&CurLoc](const PrintSourceLocSpec &Spec) {
+          return matchesSourceLocSpec(CurLoc, Spec);
+        }))
+      return true;
+  }
+  return false;
+}
+
+bool llvm::isSourceLocFilterEmpty() { return PrintSourceLocs.empty(); }
+
+bool llvm::shouldPrintFunction(const Function &F) {
+  if (!isFunctionInPrintList(F.getName()))
+    return false;
+
+  if (isSourceLocFilterEmpty())
+    return true;
+
+  for (const BasicBlock &BB : F)
+    for (const Instruction &I : BB)
+      if (isSourceLocInPrintList(I.getDebugLoc()))
+        return true;
+  return false;
 }
 
 std::error_code cleanUpTempFilesImpl(ArrayRef<std::string> FileName,
diff --git a/llvm/lib/IRPrinter/IRPrintingPasses.cpp b/llvm/lib/IRPrinter/IRPrintingPasses.cpp
index adb192ac4a916..343d8b2b6d74b 100644
--- a/llvm/lib/IRPrinter/IRPrintingPasses.cpp
+++ b/llvm/lib/IRPrinter/IRPrintingPasses.cpp
@@ -32,14 +32,14 @@ PrintModulePass::PrintModulePass(raw_ostream &OS, const std::string &Banner,
       EmitSummaryIndex(EmitSummaryIndex) {}
 
 PreservedAnalyses PrintModulePass::run(Module &M, ModuleAnalysisManager &AM) {
-  if (llvm::isFunctionInPrintList("*")) {
+  if (llvm::isFunctionInPrintList("*") && isSourceLocFilterEmpty()) {
     if (!Banner.empty())
       OS << Banner << "\n";
     M.print(OS, nullptr, ShouldPreserveUseListOrder);
   } else {
     bool BannerPrinted = false;
     for (const auto &F : M.functions()) {
-      if (llvm::isFunctionInPrintList(F.getName())) {
+      if (shouldPrintFunction(F)) {
         if (!BannerPrinted && !Banner.empty()) {
           OS << Banner << "\n";
           BannerPrinted = true;
@@ -67,7 +67,7 @@ PrintFunctionPass::PrintFunctionPass(raw_ostream &OS, const std::string &Banner)
 
 PreservedAnalyses PrintFunctionPass::run(Function &F,
                                          FunctionAnalysisManager &) {
-  if (isFunctionInPrintList(F.getName())) {
+  if (shouldPrintFunction(F)) {
     if (forcePrintModuleIR())
       OS << Banner << " (function: " << F.getName() << ")\n" << *F.getParent();
     else
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 19e72a8612c4a..9732323d7e77e 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -18,11 +18,15 @@
 #include "llvm/Analysis/LazyCallGraph.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/CodeGen/MIRPrinter.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineInstr.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/MachineVerifier.h"
+#include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/Function.h"
+#include "llvm/IR/Instruction.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/PassInstrumentation.h"
 #include "llvm/IR/PassManager.h"
@@ -159,6 +163,39 @@ static cl::opt<std::string>
                 cl::desc("exe called with module IR after each pass that "
                          "changes it"));
 
+bool loopContainsPrintSourceLoc(const Loop &L) {
+  const Function *F = L.getHeader()->getParent();
+  if (!isFunctionInPrintList(F->getName()))
+    return false;
+
+  if (isSourceLocFilterEmpty())
+    return true;
+
+  for (const BasicBlock *BB : L.blocks())
+    for (const Instruction &I : *BB)
+      if (isSourceLocInPrintList(I.getDebugLoc()))
+        return true;
+  return false;
+}
+
+bool shouldGenerateData(const Function &F) {
+  return !F.isDeclaration() && shouldPrintFunction(F);
+}
+
+bool shouldGenerateData(const MachineFunction &MF) {
+  if (!isFunctionInPrintList(MF.getName()))
+    return false;
+
+  if (isSourceLocFilterEmpty())
+    return true;
+
+  for (const MachineBasicBlock &MBB : MF)
+    for (const MachineInstr &MI : MBB)
+      if (isSourceLocInPrintList(MI.getDebugLoc()))
+        return true;
+  return false;
+}
+
 /// Extract Module out of \p IR unit. May return nullptr if \p IR does not match
 /// certain global filters. Will never return nullptr if \p Force is true.
 const Module *unwrapModule(Any IR, bool Force = false) {
@@ -166,7 +203,7 @@ const Module *unwrapModule(Any IR, bool Force = false) {
     return M;
 
   if (const auto *F = unwrapIR<Function>(IR)) {
-    if (!Force && !isFunctionInPrintList(F->getName()))
+    if (!Force && !shouldGenerateData(*F))
       return nullptr;
 
     return F->getParent();
@@ -175,7 +212,7 @@ const Module *unwrapModule(Any IR, bool Force = false) {
   if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR)) {
     for (const LazyCallGraph::Node &N : *C) {
       const Function &F = N.getFunction();
-      if (Force || (!F.isDeclaration() && isFunctionInPrintList(F.getName()))) {
+      if (Force || (!F.isDeclaration() && shouldGenerateData(F))) {
         return F.getParent();
       }
     }
@@ -185,13 +222,13 @@ const Module *unwrapModule(Any IR, bool Force = false) {
 
   if (const auto *L = unwrapIR<Loop>(IR)) {
     const Function *F = L->getHeader()->getParent();
-    if (!Force && !isFunctionInPrintList(F->getName()))
+    if (!Force && !loopContainsPrintSourceLoc(*L))
       return nullptr;
     return F->getParent();
   }
 
   if (const auto *MF = unwrapIR<MachineFunction>(IR)) {
-    if (!Force && !isFunctionInPrintList(MF->getName()))
+    if (!Force && !shouldGenerateData(*MF))
       return nullptr;
     return MF->getFunction().getParent();
   }
@@ -200,13 +237,14 @@ const Module *unwrapModule(Any IR, bool Force = false) {
 }
 
 void printIR(raw_ostream &OS, const Function *F) {
-  if (!isFunctionInPrintList(F->getName()))
+  if (!shouldPrintFunction(*F))
     return;
   OS << *F;
 }
 
 void printIR(raw_ostream &OS, const Module *M) {
-  if (isFunctionInPrintList("*") || forcePrintModuleIR()) {
+  if ((isFunctionInPrintList("*") && isSourceLocFilterEmpty()) ||
+      forcePrintModuleIR()) {
     M->print(OS, nullptr);
   } else {
     for (const auto &F : M->functions()) {
@@ -218,21 +256,20 @@ void printIR(raw_ostream &OS, const Module *M) {
 void printIR(raw_ostream &OS, const LazyCallGraph::SCC *C) {
   for (const LazyCallGraph::Node &N : *C) {
     const Function &F = N.getFunction();
-    if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) {
+    if (!F.isDeclaration() && shouldGenerateData(F)) {
       F.print(OS);
     }
   }
 }
 
 void printIR(raw_ostream &OS, const Loop *L) {
-  const Function *F = L->getHeader()->getParent();
-  if (!isFunctionInPrintList(F->getName()))
+  if (!loopContainsPrintSourceLoc(*L))
     return;
   printLoop(const_cast<Loop &>(*L), OS);
 }
 
 void printIR(raw_ostream &OS, const MachineFunction *MF) {
-  if (!isFunctionInPrintList(MF->getName()))
+  if (!shouldGenerateData(*MF))
     return;
   MF->print(OS);
 }
@@ -259,18 +296,15 @@ std::string getIRName(Any IR) {
 
 bool moduleContainsFilterPrintFunc(const Module &M) {
   return any_of(M.functions(),
-                [](const Function &F) {
-                  return isFunctionInPrintList(F.getName());
-                }) ||
-         isFunctionInPrintList("*");
+                [](const Function &F) { return shouldPrintFunction(F); }) ||
+         (isFunctionInPrintList("*") && isSourceLocFilterEmpty());
 }
 
 bool sccContainsFilterPrintFunc(const LazyCallGraph::SCC &C) {
-  return any_of(C,
-                [](const LazyCallGraph::Node &N) {
-                  return isFunctionInPrintList(N.getName());
-                }) ||
-         isFunctionInPrintList("*");
+  return any_of(C, [](const LazyCallGraph::Node &N) {
+    const Function &F = N.getFunction();
+    return !F.isDeclaration() && shouldGenerateData(F);
+  });
 }
 
 bool shouldPrintIR(Any IR) {
@@ -278,16 +312,16 @@ bool shouldPrintIR(Any IR) {
     return moduleContainsFilterPrintFunc(*M);
 
   if (const auto *F = unwrapIR<Function>(IR))
-    return isFunctionInPrintList(F->getName());
+    return shouldPrintFunction(*F);
 
   if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR))
     return sccContainsFilterPrintFunc(*C);
 
   if (const auto *L = unwrapIR<Loop>(IR))
-    return isFunctionInPrintList(L->getHeader()->getParent()->getName());
+    return loopContainsPrintSourceLoc(*L);
 
   if (const auto *MF = unwrapIR<MachineFunction>(IR))
-    return isFunctionInPrintList(MF->getName());
+    return shouldGenerateData(*MF);
   llvm_unreachable("Unknown wrapped IR type");
 }
 
@@ -364,9 +398,7 @@ const Module *getModuleForComparison(Any IR) {
   return nullptr;
 }
 
-bool isInterestingFunction(const Function &F) {
-  return isFunctionInPrintList(F.getName());
-}
+bool isInterestingFunction(const Function &F) { return shouldGenerateData(F); }
 
 // Return true when this is a pass on IR for which printing
 // of changes is desired.
@@ -715,14 +747,6 @@ template <typename T> void IRComparer<T>::analyzeIR(Any IR, IRDataT<T> &Data) {
   llvm_unreachable("Unknown IR unit");
 }
 
-static bool shouldGenerateData(const Function &F) {
-  return !F.isDeclaration() && isFunctionInPrintList(F.getName());
-}
-
-static bool shouldGenerateData(const MachineFunction &MF) {
-  return isFunctionInPrintList(MF.getName());
-}
-
 template <typename T>
 template <typename FunctionT>
 bool IRComparer<T>::generateFunctionData(IRDataT<T> &Data, const FunctionT &F) {
diff --git a/llvm/test/CodeGen/X86/source-loc-print-filter.ll b/llvm/test/CodeGen/X86/source-loc-print-filter.ll
new file mode 100644
index 0000000000000..8d20fa90e028e
--- /dev/null
+++ b/llvm/test/CodeGen/X86/source-loc-print-filter.ll
@@ -0,0 +1,49 @@
+; RUN: llc -mtriple=x86_64 -O2 -print-after-all \
+; RUN:   -filter-print-source-locs=source.c:10 -o /dev/null < %s 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=MATCH
+
+; RUN: llc -mtriple=x86_64 -O2 -print-after-all \
+; RUN:   -filter-print-funcs=* -filter-print-source-locs=source.c:10 \
+; RUN:   -o /dev/null < %s 2>&1 | FileCheck %s --check-prefix=MATCH
+
+; RUN: llc -mtriple=x86_64 -O2 -print-after-all \
+; RUN:   -filter-print-source-locs=source.c:999 -o /dev/null < %s 2>&1 \
+; RUN:   | FileCheck %s --allow-empty --check-prefix=EMPTY
+
+; MATCH:      IR Dump After
+; MATCH:      define i32 @foo
+; MATCH-NOT:  define i32 @bar
+; MATCH:      Machine code for function foo
+; MATCH-NOT:  Machine code for function bar
+
+; EMPTY-NOT: IR Dump After
+; EMPTY-NOT: Machine code for function
+
+define i32 @foo() !dbg !5 {
+entry:
+  %sum = add i32 1, 2, !dbg !10
+  ret i32 %sum, !dbg !11
+}
+
+define i32 @bar() !dbg !12 {
+entry:
+  %sum = add i32 3, 4, !dbg !13
+  ret i32 %sum, !dbg !14
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "test", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "source.c", directory: "/tmp")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !DISubroutineType(types: !15)
+!5 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 9, type: !4, scopeLine: 9, spFlags: DISPFlagDefinition, unit: !0)
+!10 = !DILocation(line: 10, column: 7, scope: !5)
+!11 = !DILocation(line: 11, column: 3, scope: !5)
+!12 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 19, type: !4, scopeLine: 19, spFlags: DISPFlagDefinition, unit: !0)
+!13 = !DILocation(line: 20, column: 7, scope: !12)
+!14 = !DILocation(line: 21, column: 3, scope: !12)
+!15 = !{!16}
+!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
diff --git a/llvm/test/Other/source-loc-print-filter.ll b/llvm/test/Other/source-loc-print-filter.ll
new file mode 100644
index 0000000000000..022fa58753111
--- /dev/null
+++ b/llvm/test/Other/source-loc-print-filter.ll
@@ -0,0 +1,97 @@
+; Check separated lines and ranges.
+; RUN: opt < %s 2>&1 -disable-output -passes=forceattrs -print-after-all \
+; RUN:   -filter-print-source-locs=source.c:10,30-32 \
+; RUN:   | FileCheck %s --check-prefix=RANGE
+
+; Check path suffix matching.
+; RUN: opt < %s 2>&1 -disable-output -passes=forceattrs -print-after-all \
+; RUN:   -filter-print-source-locs=dir/source.c:31 \
+; RUN:   | FileCheck %s --check-prefix=SUFFIX
+
+; Check function pass filtering.
+; RUN: opt < %s 2>&1 -disable-output -passes='function(no-op-function)' \
+; RUN:   -print-after-all -filter-print-source-locs=source.c:10 \
+; RUN:   | FileCheck %s --check-prefix=FUNCTION
+
+; Check that an explicit function wildcard still respects the source location
+; filter.
+; RUN: opt < %s 2>&1 -disable-output -passes=forceattrs -print-after-all \
+; RUN:   -filter-print-funcs=* -filter-print-source-locs=source.c:10 \
+; RUN:   | FileCheck %s --check-prefix=WILDCARD
+
+; Check that a missing source location suppresses the dump.
+; RUN: opt < %s 2>&1 -disable-output -passes=forceattrs -print-after-all \
+; RUN:   -filter-print-source-locs=missing.c:10 \
+; RUN:   | FileCheck %s --allow-empty --check-prefix=EMPTY
+
+; Check that the explicit print pass uses the same filter.
+; RUN: opt < %s 2>&1 -disable-output -passes=print \
+; RUN:   -filter-print-funcs=* -filter-print-source-locs=source.c:10 \
+; RUN:   | FileCheck %s --check-prefix=PRINT-PASS
+
+; RANGE:      IR Dump After {{Force set function attributes|ForceFunctionAttrsPass}}
+; RANGE:      define i32 @foo
+; RANGE-NOT:  define i32 @bar
+; RANGE:      define i32 @baz
+; RANGE-NOT:  IR Dump After {{Force set function attributes|ForceFunctionAttrsPass}}
+
+; SUFFIX:      IR Dump After {{Force set function attributes|ForceFunctionAttrsPass}}
+; SUFFIX-NOT:  define i32 @foo
+; SUFFIX-NOT:  define i32 @bar
+; SUFFIX:      define i32 @baz
+; SUFFIX-NOT:  IR Dump After {{Force set function attributes|ForceFunctionAttrsPass}}
+
+; FUNCTION:      IR Dump After NoOpFunctionPass on foo
+; FUNCTION-NEXT: define i32 @foo
+; FUNCTION-NOT:  IR Dump After NoOpFunctionPass on bar
+; FUNCTION-NOT:  IR Dump After NoOpFunctionPass on baz
+
+; WILDCARD:      IR Dump After {{Force set function attributes|ForceFunctionAttrsPass}}
+; WILDCARD:      define i32 @foo
+; WILDCARD-NOT:  define i32 @bar
+; WILDCARD-NOT:  define i32 @baz
+; WILDCARD-NOT:  IR Dump After {{Force set function attributes|ForceFunctionAttrsPass}}
+
+; EMPTY-NOT: IR Dump After {{Force set function attributes|ForceFunctionAttrsPass}}
+
+; PRINT-PASS:      define i32 @foo
+; PRINT-PASS-NOT:  define i32 @bar
+; PRINT-PASS-NOT:  define i32 @baz
+
+define i32 @foo() !dbg !5 {
+entry:
+  %sum = add i32 1, 2, !dbg !10
+  ret i32 %sum, !dbg !11
+}
+
+define i32 @bar() !dbg !12 {
+entry:
+  %sum = add i32 3, 4, !dbg !13
+  ret i32 %sum, !dbg !14
+}
+
+define i32 @baz() !dbg !15 {
+entry:
+  %sum = add i32 5, 6, !dbg !16
+  ret i32 %sum, !dbg !17
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "test", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "source.c", directory: "/tmp/dir")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !DISubroutineType(types: !18)
+!5 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 9, type: !4, scopeLine: 9, spFlags: DISPFlagDefinition, unit: !0)
+!10 = !DILocation(line: 10, column: 7, scope: !5)
+!11 = !DILocation(line: 11, column: 3, scope: !5)
+!12 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 19, type: !4, scopeLine: 19, spFlags: DISPFlagDefinition, unit: !0)
+!13 = !DILocation(line: 20, column: 7, scope: !12)
+!14 = !DILocation(line: 21, column: 3, scope: !12)
+!15 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 29, type: !4, scopeLine: 29, spFlags: DISPFlagDefinition, unit: !0)
+!16 = !DILocation(line: 31, column: 7, scope: !15)
+!17 = !DILocation(line: 32, column: 3, scope: !15)
+!18 = !{!19}
+!19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)



More information about the llvm-commits mailing list