[llvm] 822f549 - [IR] Add source location filtering to pass dumps (#203393)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 3 19:16:35 PDT 2026


Author: Yaxun (Sam) Liu
Date: 2026-09-03T22:16:29-04:00
New Revision: 822f549e988f143fc8f035b8407db9dc6f43d13f

URL: https://github.com/llvm/llvm-project/commit/822f549e988f143fc8f035b8407db9dc6f43d13f
DIFF: https://github.com/llvm/llvm-project/commit/822f549e988f143fc8f035b8407db9dc6f43d13f.diff

LOG: [IR] Add source location filtering to pass dumps (#203393)

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 IR and machine-function dumps
to matching debug locations. The option accepts file names or paths with
selected lines and ranges. It also follows inlined locations and works
with legacy and new pass managers.

Added: 
    llvm/test/CodeGen/X86/source-loc-print-filter.ll
    llvm/test/Other/source-loc-print-filter.ll

Modified: 
    llvm/include/llvm/IR/PrintPasses.h
    llvm/include/llvm/Passes/StandardInstrumentations.h
    llvm/lib/Analysis/CallGraphSCCPass.cpp
    llvm/lib/Analysis/LoopPass.cpp
    llvm/lib/Analysis/RegionPass.cpp
    llvm/lib/CodeGen/MachineFunctionPass.cpp
    llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
    llvm/lib/IR/IRPrintingPasses.cpp
    llvm/lib/IR/LegacyPassManager.cpp
    llvm/lib/IR/Pass.cpp
    llvm/lib/IR/PrintPasses.cpp
    llvm/lib/IRPrinter/IRPrintingPasses.cpp
    llvm/lib/Passes/StandardInstrumentations.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/PrintPasses.h b/llvm/include/llvm/IR/PrintPasses.h
index 234c743b798eb..170cb4ba38c97 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,18 @@ LLVM_ABI bool isFilterPassesEmpty();
 // Returns true if we should print the function.
 LLVM_ABI bool isFunctionInPrintList(StringRef FunctionName);
 
+// Returns true if -filter-print-source-locs is empty or contains the source
+// location.
+LLVM_ABI bool isSourceLocInPrintList(const DebugLoc &Loc);
+LLVM_ABI bool isSourceLocFilterEmpty();
+
+// Returns true if the print filters allow all functions.
+LLVM_ABI bool shouldPrintAllFunctions();
+
+// Returns true if the function passes the function-name and source-location
+// print filters.
+LLVM_ABI 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/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h
index cec8ff266a098..beaad14201334 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -248,8 +248,13 @@ template <typename IRUnitT> class LLVM_ABI ChangeReporter {
   // Called when an ignored pass is encountered.
   virtual void handleIgnored(StringRef PassID, std::string &Name) = 0;
 
-  // Stack of IRs before passes.
-  std::vector<IRUnitT> BeforeStack;
+  struct BeforeIR {
+    IRUnitT Data;
+    bool IsInteresting = false;
+  };
+
+  // Stack of IRs before passes and whether they matched the filters.
+  std::vector<BeforeIR> BeforeStack;
   // Is this the first IR seen?
   bool InitialIR = true;
 

diff  --git a/llvm/lib/Analysis/CallGraphSCCPass.cpp b/llvm/lib/Analysis/CallGraphSCCPass.cpp
index 3fd2fe02f6688..b27a7c5e388c2 100644
--- a/llvm/lib/Analysis/CallGraphSCCPass.cpp
+++ b/llvm/lib/Analysis/CallGraphSCCPass.cpp
@@ -683,7 +683,7 @@ namespace {
       };
 
       bool NeedModule = llvm::forcePrintModuleIR();
-      if (isFunctionInPrintList("*") && NeedModule) {
+      if (shouldPrintAllFunctions() && NeedModule) {
         PrintBannerOnce();
         OS << "\n";
         SCC.getCallGraph().getModule().print(OS, nullptr);
@@ -692,14 +692,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 (shouldPrintAllFunctions()) {
           PrintBannerOnce();
           OS << "\nPrinting <null> Function\n";
         }

diff  --git a/llvm/lib/Analysis/LoopPass.cpp b/llvm/lib/Analysis/LoopPass.cpp
index 85c15c5721cd6..56856a6ac4549 100644
--- a/llvm/lib/Analysis/LoopPass.cpp
+++ b/llvm/lib/Analysis/LoopPass.cpp
@@ -31,6 +31,22 @@ using namespace llvm;
 
 namespace {
 
+bool shouldPrintLoop(const Loop &L) {
+  Function *F = L.getHeader()->getParent();
+  bool SourceLocFilterEmpty = isSourceLocFilterEmpty();
+  if (!isFunctionInPrintList(F->getName()))
+    return false;
+
+  if (SourceLocFilterEmpty)
+    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 +64,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..6d8d554c0d7d5 100644
--- a/llvm/lib/Analysis/RegionPass.cpp
+++ b/llvm/lib/Analysis/RegionPass.cpp
@@ -172,6 +172,22 @@ void RGPassManager::dumpPassStructure(unsigned Offset) {
 }
 
 namespace {
+bool shouldPrintRegion(const Region &R) {
+  bool SourceLocFilterEmpty = isSourceLocFilterEmpty();
+  if (!isFunctionInPrintList(R.getEntry()->getParent()->getName()))
+    return false;
+
+  if (SourceLocFilterEmpty)
+    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 +205,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/MachineFunctionPass.cpp b/llvm/lib/CodeGen/MachineFunctionPass.cpp
index 67ae3613263dc..59d6e7f23505a 100644
--- a/llvm/lib/CodeGen/MachineFunctionPass.cpp
+++ b/llvm/lib/CodeGen/MachineFunctionPass.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/Analysis/BasicAliasAnalysis.h"
 #include "llvm/Analysis/BranchProbabilityInfo.h"
 #include "llvm/Analysis/CycleAnalysis.h"
@@ -33,6 +34,7 @@
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/PrintPasses.h"
 #include "llvm/Support/ErrorHandling.h"
 
 using namespace llvm;
@@ -129,7 +131,17 @@ bool MachineFunctionPass::printIRUnit(raw_ostream &OS, Function &F) {
   if (F.hasAvailableExternallyLinkage())
     return false;
   MachineModuleInfo &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
-  MMI.getOrCreateMachineFunction(F).print(OS);
+  MachineFunction &MF = MMI.getOrCreateMachineFunction(F);
+  bool SourceLocFilterEmpty = isSourceLocFilterEmpty();
+  if (!isFunctionInPrintList(MF.getName()))
+    return false;
+  if (!SourceLocFilterEmpty && none_of(MF, [](const MachineBasicBlock &MBB) {
+        return any_of(MBB, [](const MachineInstr &MI) {
+          return isSourceLocInPrintList(MI.getDebugLoc());
+        });
+      }))
+    return false;
+  MF.print(OS);
   return true;
 }
 

diff  --git a/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp b/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
index 5111322023d04..05dee8c3827b1 100644
--- a/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
+++ b/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
@@ -22,6 +22,21 @@
 using namespace llvm;
 
 namespace {
+bool shouldPrintMachineFunction(const MachineFunction &MF) {
+  bool SourceLocFilterEmpty = isSourceLocFilterEmpty();
+  if (!isFunctionInPrintList(MF.getName()))
+    return false;
+
+  if (SourceLocFilterEmpty)
+    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 +59,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 a84f8db98bee4..e5e8fcfaefa72 100644
--- a/llvm/lib/IR/IRPrintingPasses.cpp
+++ b/llvm/lib/IR/IRPrintingPasses.cpp
@@ -28,7 +28,7 @@ namespace {
 
 static void printModule(raw_ostream &OS, StringRef Banner,
                         bool ShouldPreserveUseListOrder, Module &M) {
-  if (llvm::isFunctionInPrintList("*")) {
+  if (shouldPrintAllFunctions()) {
     if (!Banner.empty())
       OS << Banner << "\n";
     M.print(OS, nullptr, ShouldPreserveUseListOrder);
@@ -37,7 +37,7 @@ static void printModule(raw_ostream &OS, StringRef Banner,
 
   bool BannerPrinted = false;
   for (const auto &F : M.functions()) {
-    if (!llvm::isFunctionInPrintList(F.getName()))
+    if (!shouldPrintFunction(F))
       continue;
     if (!BannerPrinted && !Banner.empty()) {
       OS << Banner << "\n";
@@ -91,7 +91,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()->print(OS, nullptr);

diff  --git a/llvm/lib/IR/LegacyPassManager.cpp b/llvm/lib/IR/LegacyPassManager.cpp
index 48310f048f110..d33d6bdd32030 100644
--- a/llvm/lib/IR/LegacyPassManager.cpp
+++ b/llvm/lib/IR/LegacyPassManager.cpp
@@ -1388,18 +1388,19 @@ bool FPPassManager::runOnFunction(Function &F) {
     // unregistered infrastructure passes).
     StringRef PassID;
     bool ReportChanged = PrintChanged != ChangePrinter::None;
-    bool IsInteresting = false, ShouldPrintChanged = false;
+    bool IsInteresting = false, ShouldCaptureChanged = false;
+    bool ShouldPrintChanged = false;
     if (ReportChanged) {
       const PassInfo *PI = Pass::lookupPassInfo(FP->getPassID());
       if (PI && !PI->isAnalysis()) {
         PassID = PI->getPassArgument();
         IsInteresting = isPassInPrintList(PassID);
-        ShouldPrintChanged = IsInteresting && isFunctionInPrintList(Name);
+        ShouldCaptureChanged = IsInteresting;
       } else {
         ReportChanged = false;
       }
     }
-    if (ShouldPrintChanged) {
+    if (ShouldCaptureChanged) {
       BeforeStr.clear();
       AfterStr.clear();
       raw_svector_ostream OS(BeforeStr);
@@ -1439,9 +1440,9 @@ bool FPPassManager::runOnFunction(Function &F) {
       }
     }
 
-    if (ShouldPrintChanged) {
+    if (ShouldCaptureChanged) {
       raw_svector_ostream OS(AfterStr);
-      FP->printIRUnit(OS, F);
+      ShouldPrintChanged |= FP->printIRUnit(OS, F);
     }
     if (ReportChanged)
       reportChangedIR(BeforeStr, AfterStr, FP->getPassName(), PassID, Name,
@@ -1520,6 +1521,22 @@ MPPassManager::runOnModule(Module &M) {
     InstrCount = initSizeRemarkInfo(M, FunctionToInstrCount);
 
   SmallString<0> BeforeStr, AfterStr;
+  auto PrintIR = [&M](raw_ostream &OS) {
+    if (isSourceLocFilterEmpty()) {
+      M.print(OS, /*AAW=*/nullptr);
+      return true;
+    }
+
+    bool Printed = false;
+    for (const Function &F : M) {
+      if (!shouldPrintFunction(F))
+        continue;
+      F.print(OS);
+      Printed = true;
+    }
+    return Printed;
+  };
+
   for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) {
     ModulePass *MP = getContainedPass(Index);
     bool LocalChanged = false;
@@ -1529,8 +1546,8 @@ MPPassManager::runOnModule(Module &M) {
 
     initializeAnalysisImpl(MP);
 
-    // As in FPPassManager, but for module passes. Not subject to
-    // -filter-print-funcs.
+    // As in FPPassManager, but for module passes. A function-name filter by
+    // itself does not filter module passes.
     StringRef PassID;
     bool ReportChanged = PrintChanged != ChangePrinter::None;
     bool IsInteresting = false, ShouldPrintChanged = false;
@@ -1538,16 +1555,16 @@ MPPassManager::runOnModule(Module &M) {
       const PassInfo *PI = Pass::lookupPassInfo(MP->getPassID());
       if (PI && !PI->isAnalysis()) {
         PassID = PI->getPassArgument();
-        IsInteresting = ShouldPrintChanged = isPassInPrintList(PassID);
+        IsInteresting = isPassInPrintList(PassID);
       } else {
         ReportChanged = false;
       }
     }
-    if (ShouldPrintChanged) {
+    if (IsInteresting) {
       BeforeStr.clear();
       AfterStr.clear();
       raw_svector_ostream OS(BeforeStr);
-      M.print(OS, /*AAW=*/nullptr);
+      ShouldPrintChanged = PrintIR(OS);
     }
 
     {
@@ -1578,9 +1595,9 @@ MPPassManager::runOnModule(Module &M) {
       }
     }
 
-    if (ShouldPrintChanged) {
+    if (IsInteresting) {
       raw_svector_ostream OS(AfterStr);
-      M.print(OS, /*AAW=*/nullptr);
+      ShouldPrintChanged |= PrintIR(OS);
     }
     if (ReportChanged)
       reportChangedIR(BeforeStr, AfterStr, MP->getPassName(), PassID,

diff  --git a/llvm/lib/IR/Pass.cpp b/llvm/lib/IR/Pass.cpp
index 08d58379b7e69..4e7090c7ef015 100644
--- a/llvm/lib/IR/Pass.cpp
+++ b/llvm/lib/IR/Pass.cpp
@@ -20,6 +20,7 @@
 #include "llvm/IR/LegacyPassNameParser.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/OptBisect.h"
+#include "llvm/IR/PrintPasses.h"
 #include "llvm/PassInfo.h"
 #include "llvm/PassRegistry.h"
 #include "llvm/Support/Compiler.h"
@@ -178,6 +179,8 @@ Pass *FunctionPass::createPrinterPass(raw_ostream &OS,
 }
 
 bool FunctionPass::printIRUnit(raw_ostream &OS, Function &F) {
+  if (!shouldPrintFunction(F))
+    return false;
   F.print(OS);
   return true;
 }

diff  --git a/llvm/lib/IR/PrintPasses.cpp b/llvm/lib/IR/PrintPasses.cpp
index 643aa69741d13..b0a40dc317522 100644
--- a/llvm/lib/IR/PrintPasses.cpp
+++ b/llvm/lib/IR/PrintPasses.cpp
@@ -8,16 +8,25 @@
 
 #include "llvm/IR/PrintPasses.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/ADT/Twine.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 "llvm/Support/raw_ostream.h"
+#include <vector>
 
 using namespace llvm;
 
@@ -45,23 +54,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 
diff erent 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 
diff erent 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),
@@ -114,6 +124,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.
 
@@ -163,7 +177,158 @@ bool llvm::isFilterPassesEmpty() { return FilterPasses.empty(); }
 
 bool llvm::isFunctionInPrintList(StringRef FunctionName) {
   static const StringSet<> PrintFuncNames(llvm::from_range, PrintFuncsList);
-  return PrintFuncNames.empty() || PrintFuncNames.contains(FunctionName);
+  return PrintFuncNames.empty() || PrintFuncNames.contains(FunctionName) ||
+         PrintFuncNames.contains("*");
+}
+
+namespace {
+
+struct PrintLineRange {
+  unsigned First;
+  unsigned Last;
+};
+
+struct PrintSourceLocFilter {
+  std::string File;
+  SmallVector<PrintLineRange, 4> Lines;
+};
+
+[[noreturn]] void reportBadSourceLocFilter(StringRef Filter) {
+  report_fatal_error(Twine("Invalid -filter-print-source-locs value '") +
+                     Filter + "'. Expected file:line[,line-line][,line].");
+}
+
+std::string normalizeSlashes(StringRef Path) {
+  return sys::path::convert_to_slash(Path, sys::path::Style::windows_backslash);
+}
+
+bool parseLineNumber(StringRef LineText, unsigned &Line) {
+  return !LineText.empty() && !LineText.getAsInteger(10, Line);
+}
+
+PrintLineRange parseLineRange(StringRef RangeText, StringRef FullFilter) {
+  auto [FirstText, LastText] = RangeText.split('-');
+
+  unsigned First;
+  if (!parseLineNumber(FirstText, First))
+    reportBadSourceLocFilter(FullFilter);
+
+  if (!RangeText.contains('-'))
+    return {First, First};
+
+  unsigned Last;
+  if (!parseLineNumber(LastText, Last) || Last < First)
+    reportBadSourceLocFilter(FullFilter);
+
+  return {First, Last};
+}
+
+std::vector<PrintSourceLocFilter> parseSourceLocFilters() {
+  std::vector<PrintSourceLocFilter> Result;
+  for (const std::string &RawFilter : PrintSourceLocs) {
+    StringRef Filter(RawFilter);
+    auto [File, LineList] = Filter.rsplit(':');
+    if (File.empty() || LineList.empty())
+      reportBadSourceLocFilter(Filter);
+
+    PrintSourceLocFilter Parsed;
+    Parsed.File = normalizeSlashes(File);
+    for (StringRef RangeText : llvm::split(LineList, ",")) {
+      Parsed.Lines.push_back(parseLineRange(RangeText, Filter));
+    }
+    Result.push_back(std::move(Parsed));
+  }
+  return Result;
+}
+
+ArrayRef<PrintSourceLocFilter> getSourceLocFilters() {
+  static const std::vector<PrintSourceLocFilter> Filters =
+      parseSourceLocFilters();
+  return Filters;
+}
+
+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 FilterFile, StringRef Directory,
+                 StringRef Filename) {
+  std::string LocFile = normalizeSlashes(Filename);
+  std::string LocPath = makeDebugLocPath(Directory, Filename);
+
+  // Accept an exact filename or path, a basename, or a path suffix so the
+  // filter may omit leading directories.
+  if (FilterFile == LocFile || FilterFile == LocPath)
+    return true;
+
+  StringRef LocFileRef(LocFile);
+  StringRef LocPathRef(LocPath);
+  if (sys::path::filename(LocFileRef) == FilterFile)
+    return true;
+
+  std::string Suffix = (Twine("/") + FilterFile).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 matchesSourceLocFilter(const DebugLoc &Loc,
+                            const PrintSourceLocFilter &Filter) {
+  auto *Scope = dyn_cast_or_null<DIScope>(Loc.getScope());
+  return Scope &&
+         matchesFile(Filter.File, Scope->getDirectory(),
+                     Scope->getFilename()) &&
+         matchesLine(Filter.Lines, Loc.getLine());
+}
+
+} // namespace
+
+bool llvm::isSourceLocInPrintList(const DebugLoc &Loc) {
+  ArrayRef<PrintSourceLocFilter> Filters = getSourceLocFilters();
+  if (Filters.empty())
+    return true;
+
+  for (DebugLoc CurLoc = Loc; CurLoc; CurLoc = CurLoc.getInlinedAt()) {
+    if (any_of(Filters, [&CurLoc](const PrintSourceLocFilter &Filter) {
+          return matchesSourceLocFilter(CurLoc, Filter);
+        }))
+      return true;
+  }
+  return false;
+}
+
+bool llvm::isSourceLocFilterEmpty() { return getSourceLocFilters().empty(); }
+
+bool llvm::shouldPrintAllFunctions() {
+  return isSourceLocFilterEmpty() && isFunctionInPrintList("*");
+}
+
+bool llvm::shouldPrintFunction(const Function &F) {
+  bool SourceLocFilterEmpty = isSourceLocFilterEmpty();
+  if (!isFunctionInPrintList(F.getName()))
+    return false;
+
+  if (SourceLocFilterEmpty)
+    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,
@@ -264,6 +429,16 @@ void llvm::reportChangedIR(StringRef Before, StringRef After,
     return;
 
   if (IsInteresting && Before != After) {
+    if (After.empty() &&
+        llvm::is_contained({ChangePrinter::Quiet, ChangePrinter::Verbose,
+                            ChangePrinter::DotCfgQuiet,
+                            ChangePrinter::DotCfgVerbose},
+                           PrintChanged.getValue())) {
+      errs() << ("*** IR Deleted After " + PassName + " (" + PassID + ") on " +
+                 IRName + " ***\n");
+      return;
+    }
+
     errs() << ("*** IR Dump After " + PassName + " (" + PassID + ") on " +
                IRName + " ***\n");
     switch (PrintChanged) {

diff  --git a/llvm/lib/IRPrinter/IRPrintingPasses.cpp b/llvm/lib/IRPrinter/IRPrintingPasses.cpp
index 8893e34fdeec2..559249d1e5d45 100644
--- a/llvm/lib/IRPrinter/IRPrintingPasses.cpp
+++ b/llvm/lib/IRPrinter/IRPrintingPasses.cpp
@@ -39,14 +39,14 @@ PreservedAnalyses PrintModulePass::run(Module &M, ModuleAnalysisManager &AM) {
   if (ShouldRenumberMetadata)
     M.renumberMetadataForAssembly();
 
-  if (llvm::isFunctionInPrintList("*")) {
+  if (shouldPrintAllFunctions()) {
     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;
@@ -74,7 +74,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()->print(OS, nullptr);

diff  --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 9a2225807145a..882df3d0d6abd 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -19,11 +19,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"
@@ -138,6 +142,9 @@ static cl::opt<bool>
                     cl::desc("Dump dropped debug variables stats"),
                     cl::init(false));
 
+static bool shouldGenerateData(const Function &F);
+static bool shouldGenerateData(const MachineFunction &MF);
+
 namespace {
 
 // An option for specifying an executable that will be called with the IR
@@ -152,6 +159,22 @@ 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();
+  bool SourceLocFilterEmpty = isSourceLocFilterEmpty();
+  if (!isFunctionInPrintList(F->getName()))
+    return false;
+
+  if (SourceLocFilterEmpty)
+    return true;
+
+  for (const BasicBlock *BB : L.blocks())
+    for (const Instruction &I : *BB)
+      if (isSourceLocInPrintList(I.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(IRUnitRef IR, bool Force = false) {
@@ -159,7 +182,7 @@ const Module *unwrapModule(IRUnitRef IR, bool Force = false) {
     return M;
 
   if (const auto *F = dyn_cast<Function>(IR)) {
-    if (!Force && !isFunctionInPrintList(F->getName()))
+    if (!Force && !shouldGenerateData(*F))
       return nullptr;
 
     return F->getParent();
@@ -168,7 +191,7 @@ const Module *unwrapModule(IRUnitRef IR, bool Force = false) {
   if (const auto *C = dyn_cast<LazyCallGraph::SCC>(IR)) {
     for (const LazyCallGraph::Node &N : *C) {
       const Function &F = N.getFunction();
-      if (Force || (!F.isDeclaration() && isFunctionInPrintList(F.getName()))) {
+      if (Force || shouldGenerateData(F)) {
         return F.getParent();
       }
     }
@@ -178,13 +201,13 @@ const Module *unwrapModule(IRUnitRef IR, bool Force = false) {
 
   if (const auto *L = dyn_cast<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 = dyn_cast<MachineFunction>(IR)) {
-    if (!Force && !isFunctionInPrintList(MF->getName()))
+    if (!Force && !shouldGenerateData(*MF))
       return nullptr;
     return MF->getFunction().getParent();
   }
@@ -193,13 +216,13 @@ const Module *unwrapModule(IRUnitRef 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 (shouldPrintAllFunctions() || forcePrintModuleIR()) {
     M->print(OS, nullptr);
   } else {
     for (const auto &F : M->functions()) {
@@ -211,21 +234,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 (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);
 }
@@ -251,19 +273,17 @@ std::string getIRName(IRUnitRef IR) {
 }
 
 bool moduleContainsFilterPrintFunc(const Module &M) {
+  if (shouldPrintAllFunctions())
+    return true;
   return any_of(M.functions(),
-                [](const Function &F) {
-                  return isFunctionInPrintList(F.getName());
-                }) ||
-         isFunctionInPrintList("*");
+                [](const Function &F) { return shouldPrintFunction(F); });
 }
 
 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 shouldGenerateData(F);
+  });
 }
 
 bool shouldPrintIR(IRUnitRef IR) {
@@ -271,16 +291,16 @@ bool shouldPrintIR(IRUnitRef IR) {
     return moduleContainsFilterPrintFunc(*M);
 
   if (const auto *F = dyn_cast<Function>(IR))
-    return isFunctionInPrintList(F->getName());
+    return shouldPrintFunction(*F);
 
   if (const auto *C = dyn_cast<LazyCallGraph::SCC>(IR))
     return sccContainsFilterPrintFunc(*C);
 
   if (const auto *L = dyn_cast<Loop>(IR))
-    return isFunctionInPrintList(L->getHeader()->getParent()->getName());
+    return loopContainsPrintSourceLoc(*L);
 
   if (const auto *MF = dyn_cast<MachineFunction>(IR))
-    return isFunctionInPrintList(MF->getName());
+    return shouldGenerateData(*MF);
   llvm_unreachable("Unknown wrapped IR type");
 }
 
@@ -358,9 +378,7 @@ const Module *getModuleForComparison(IRUnitRef 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.
@@ -392,13 +410,13 @@ void ChangeReporter<T>::saveIRBeforePass(IRUnitRef IR, StringRef PassID,
   // are not given the IR so it cannot be determined whether the pass was for
   // something that was filtered out.
   BeforeStack.emplace_back();
-
-  if (!isInteresting(IR, PassID, PassName))
+  auto &Before = BeforeStack.back();
+  Before.IsInteresting = isInteresting(IR, PassID, PassName);
+  if (!Before.IsInteresting)
     return;
 
   // Save the IR representation on the stack.
-  T &Data = BeforeStack.back();
-  generateIRRepresentation(IR, PassID, Data);
+  generateIRRepresentation(IR, PassID, Before.Data);
 }
 
 template <typename T>
@@ -411,22 +429,24 @@ void ChangeReporter<T>::handleIRAfterPass(IRUnitRef IR, StringRef PassID,
   if (isIgnored(PassID)) {
     if (VerboseMode)
       handleIgnored(PassID, Name);
-  } else if (!isInteresting(IR, PassID, PassName)) {
-    if (VerboseMode)
-      handleFiltered(PassID, Name);
   } else {
-    // Get the before rep from the stack
-    T &Before = BeforeStack.back();
-    // Create the after rep
-    T After;
-    generateIRRepresentation(IR, PassID, After);
-
-    // Was there a change in IR?
-    if (Before == After) {
+    auto &Before = BeforeStack.back();
+    bool AfterIsInteresting = isInteresting(IR, PassID, PassName);
+    if (!Before.IsInteresting && !AfterIsInteresting) {
       if (VerboseMode)
-        omitAfter(PassID, Name);
-    } else
-      handleAfter(PassID, Name, Before, After, IR);
+        handleFiltered(PassID, Name);
+    } else {
+      T After;
+      if (AfterIsInteresting)
+        generateIRRepresentation(IR, PassID, After);
+
+      // Was there a change in IR?
+      if (Before.Data == After) {
+        if (VerboseMode)
+          omitAfter(PassID, Name);
+      } else
+        handleAfter(PassID, Name, Before.Data, After, IR);
+    }
   }
   BeforeStack.pop_back();
 }
@@ -664,10 +684,16 @@ void IRComparer<T>::compare(
         CompareFunc) {
   if (!CompareModule) {
     // Just handle the single function.
-    assert(Before.getData().size() == 1 && After.getData().size() == 1 &&
-           "Expected only one function.");
-    CompareFunc(false, 0, Before.getData().begin()->getValue(),
-                After.getData().begin()->getValue());
+    assert(Before.getData().size() <= 1 && After.getData().size() <= 1 &&
+           (!Before.getData().empty() || !After.getData().empty()) &&
+           "Expected one function in at least one IR unit.");
+    FuncDataT<T> Missing("");
+    const FuncDataT<T> &BeforeFunction =
+        Before.getData().empty() ? Missing
+                                 : Before.getData().begin()->getValue();
+    const FuncDataT<T> &AfterFunction =
+        After.getData().empty() ? Missing : After.getData().begin()->getValue();
+    CompareFunc(false, 0, BeforeFunction, AfterFunction);
     return;
   }
 
@@ -713,11 +739,22 @@ void IRComparer<T>::analyzeIR(IRUnitRef IR, IRDataT<T> &Data) {
 }
 
 static bool shouldGenerateData(const Function &F) {
-  return !F.isDeclaration() && isFunctionInPrintList(F.getName());
+  return !F.isDeclaration() && shouldPrintFunction(F);
 }
 
 static bool shouldGenerateData(const MachineFunction &MF) {
-  return isFunctionInPrintList(MF.getName());
+  bool SourceLocFilterEmpty = isSourceLocFilterEmpty();
+  if (!isFunctionInPrintList(MF.getName()))
+    return false;
+
+  if (SourceLocFilterEmpty)
+    return true;
+
+  for (const MachineBasicBlock &MBB : MF)
+    for (const MachineInstr &MI : MBB)
+      if (isSourceLocInPrintList(MI.getDebugLoc()))
+        return true;
+  return false;
 }
 
 template <typename T>

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..ed07dbf2a00bd
--- /dev/null
+++ b/llvm/test/CodeGen/X86/source-loc-print-filter.ll
@@ -0,0 +1,116 @@
+; 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
+
+; Check source-location filtering in the legacy change printer.
+; RUN: llc -mtriple=x86_64 -O2 -filetype=null -print-changed=quiet \
+; RUN:   -filter-passes=x86-isel -filter-print-source-locs=source.c:10 %s 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=CHANGED
+
+; Check that removing the last matching machine location is reported.
+; RUN: llc -mtriple=x86_64 -O2 -filetype=null -print-changed=quiet \
+; RUN:   -filter-passes=peephole-opt -filter-print-source-locs=source.c:10 %s 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=CHANGED-REMOVED
+
+; Check source-location filtering for legacy module passes.
+; RUN: llc -mtriple=x86_64 -O0 -filetype=null -print-changed=quiet \
+; RUN:   -filter-passes=pre-isel-intrinsic-lowering \
+; RUN:   -filter-print-source-locs=source.c:30 %s 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=CHANGED-MODULE
+
+; Check source-location filtering for legacy IR function passes.
+; RUN: llc -mtriple=x86_64 -O0 -filetype=null -print-changed=quiet \
+; RUN:   -filter-passes=atomic-expand -filter-print-source-locs=source.c:40 \
+; RUN:   %s 2>&1 | FileCheck %s --check-prefix=CHANGED-IR
+
+; RUN: not --crash llc -mtriple=x86_64 -O0 -filetype=null \
+; RUN:   -print-changed=quiet -filter-passes=x86-isel \
+; RUN:   -filter-print-funcs=missing -filter-print-source-locs=source.c: \
+; RUN:   %s 2>&1 | FileCheck %s --check-prefix=INVALID-EMPTY-LINE
+
+; 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
+; MATCH-NOT:  Machine code for function lr
+; MATCH-NOT:  Machine code for function atomic_load
+
+; EMPTY-NOT: IR Dump After
+; EMPTY-NOT: Machine code for function
+
+; CHANGED:      *** IR Dump After X86 DAG->DAG Instruction Selection (x86-isel) on foo ***
+; CHANGED:      Machine code for function foo
+; CHANGED-NOT:  on bar
+
+; CHANGED-REMOVED: *** IR Deleted After Peephole Optimizations (peephole-opt) on foo ***
+; CHANGED-REMOVED-NOT: on bar
+
+; CHANGED-MODULE:      *** IR Dump After Pre-ISel Intrinsic Lowering (pre-isel-intrinsic-lowering) on
+; CHANGED-MODULE-NOT:  define i32 @foo
+; CHANGED-MODULE-NOT:  define i32 @bar
+; CHANGED-MODULE:      define ptr @lr
+
+; CHANGED-IR:      *** IR Dump After Expand Atomic instructions (atomic-expand) on atomic_load ***
+; CHANGED-IR-NEXT: define i128 @atomic_load
+; CHANGED-IR-NOT:  define i32 @foo
+; CHANGED-IR-NOT:  define i32 @bar
+
+; INVALID-EMPTY-LINE: LLVM ERROR: Invalid -filter-print-source-locs value 'source.c:'
+
+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 ptr @lr(ptr %p, i32 %n) !dbg !17 {
+entry:
+  %result = call ptr @llvm.load.relative.i32(ptr %p, i32 %n), !dbg !18
+  ret ptr %result, !dbg !19
+}
+
+declare ptr @llvm.load.relative.i32(ptr, i32)
+
+define i128 @atomic_load(ptr %p) !dbg !20 {
+entry:
+  %value = load atomic i128, ptr %p seq_cst, align 16, !dbg !21
+  ret i128 %value, !dbg !22
+}
+
+!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)
+!17 = distinct !DISubprogram(name: "lr", scope: !1, file: !1, line: 29, type: !4, scopeLine: 29, spFlags: DISPFlagDefinition, unit: !0)
+!18 = !DILocation(line: 30, column: 7, scope: !17)
+!19 = !DILocation(line: 31, column: 3, scope: !17)
+!20 = distinct !DISubprogram(name: "atomic_load", scope: !1, file: !1, line: 39, type: !4, scopeLine: 39, spFlags: DISPFlagDefinition, unit: !0)
+!21 = !DILocation(line: 40, column: 7, scope: !20)
+!22 = !DILocation(line: 41, column: 3, scope: !20)

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..73d29eb204120
--- /dev/null
+++ b/llvm/test/Other/source-loc-print-filter.ll
@@ -0,0 +1,157 @@
+; 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 Windows-style directories in debug info. The filter uses forward
+; slashes so this also checks path separator normalization.
+; RUN: opt < %s 2>&1 -disable-output -passes=forceattrs -print-after-all \
+; RUN:   -filter-print-source-locs=C:/src/dir/windows.c:41 \
+; RUN:   | FileCheck %s --check-prefix=WINDOWS
+
+; 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
+
+; Check that a change which removes the last matching location is reported.
+; RUN: opt < %s 2>&1 -disable-output -passes=instcombine \
+; RUN:   -print-changed=quiet -filter-print-funcs=foo \
+; RUN:   -filter-print-source-locs=source.c:10 \
+; RUN:   | FileCheck %s --check-prefix=CHANGED-REMOVED
+; RUN: %if system-linux %{ opt < %s 2>&1 -disable-output -passes=instcombine -print-changed=
diff -quiet -filter-print-funcs=foo -filter-print-source-locs=source.c:10 | FileCheck %s --check-prefix=CHANGED-DIFF-REMOVED %}
+
+; Check malformed filters, including when the function-name filter matches
+; nothing.
+; RUN: not --crash opt < %s -disable-output -passes=forceattrs \
+; RUN:   -print-after-all -filter-print-funcs=missing \
+; RUN:   -filter-print-source-locs=source.c: 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=INVALID-EMPTY-LINE
+; RUN: not --crash opt < %s -disable-output -passes=forceattrs \
+; RUN:   -print-after-all -filter-print-source-locs=:10 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=INVALID-EMPTY-FILE
+; RUN: not --crash opt < %s -disable-output -passes=forceattrs \
+; RUN:   -print-after-all -filter-print-source-locs=source.c:abc 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=INVALID-LINE
+; RUN: not --crash opt < %s -disable-output -passes=forceattrs \
+; RUN:   -print-after-all -filter-print-source-locs=source.c:20-10 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=INVALID-RANGE
+; RUN: not --crash opt < %s -disable-output -passes=forceattrs \
+; RUN:   -print-after-all -filter-print-source-locs=source.c:10- 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=INVALID-TRAILING-HYPHEN
+
+; 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}}
+
+; WINDOWS:      IR Dump After {{Force set function attributes|ForceFunctionAttrsPass}}
+; WINDOWS-NOT:  define i32 @foo
+; WINDOWS-NOT:  define i32 @bar
+; WINDOWS-NOT:  define i32 @baz
+; WINDOWS:      define i32 @windows
+; WINDOWS-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
+
+; CHANGED-REMOVED: *** IR Deleted After InstCombinePass on foo ***
+
+; CHANGED-DIFF-REMOVED: *** IR Dump After InstCombinePass on foo ***
+; CHANGED-DIFF-REMOVED: -  %sum = add i32 1, 2, !dbg
+
+; INVALID-EMPTY-LINE: LLVM ERROR: Invalid -filter-print-source-locs value 'source.c:'
+; INVALID-EMPTY-FILE: LLVM ERROR: Invalid -filter-print-source-locs value ':10'
+; INVALID-LINE: LLVM ERROR: Invalid -filter-print-source-locs value 'source.c:abc'
+; INVALID-RANGE: LLVM ERROR: Invalid -filter-print-source-locs value 'source.c:20-10'
+; INVALID-TRAILING-HYPHEN: LLVM ERROR: Invalid -filter-print-source-locs value 'source.c:10-'
+
+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
+}
+
+define i32 @windows() !dbg !21 {
+entry:
+  %sum = add i32 7, 8, !dbg !22
+  ret i32 %sum, !dbg !23
+}
+
+!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)
+!20 = !DIFile(filename: "windows.c", directory: "C:\5Csrc\5Cdir")
+!21 = distinct !DISubprogram(name: "windows", scope: !20, file: !20, line: 39, type: !4, scopeLine: 39, spFlags: DISPFlagDefinition, unit: !0)
+!22 = !DILocation(line: 41, column: 7, scope: !21)
+!23 = !DILocation(line: 42, column: 3, scope: !21)


        


More information about the llvm-commits mailing list