[llvm] [Passes] Add IR-only SQLite IR tracker (PR #191852)
Yaxun Liu via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 14 07:06:58 PDT 2026
https://github.com/yxsamliu updated https://github.com/llvm/llvm-project/pull/191852
>From a7cccba9892bf90026328bf563a67824a5ad32cc Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Mon, 13 Apr 2026 10:43:22 -0400
Subject: [PATCH 1/2] [Passes] Add IR-only SQLite IR tracker
LLVM developers often need a quick way to see how IR tied to a specific source
line changes as the middle-end pass pipeline runs. The common workflow today is
-print-after-all, but dumping the whole module after every pass is slow on large
inputs, does not provide a practical way to filter by source location, and makes
it awkward to relate the same logical program point across passes.
This commit adds an IR-only tracker that records those snapshots into a SQLite
database so developers can focus on one file:line (and column) instead of
whole-module text.
Typical use: run opt or clang with the hidden LLVM option
-ir-tracker-database=/absolute/path.db alongside the usual -passes= or -O
pipeline so the run produces a database; follow-on tooling (for example the
small Python driver in a separate patch) can then list passes and show how the
IR at a chosen source line evolved.
The database is built from the existing New pass manager instrumentation in
StandardInstrumentations: an initial snapshot at pipeline entry, then one row
per pass, with each tracked instruction storing its printed IR text and its
DILocation-derived file, line, and column. In representative runs, enabling the
tracker adds on the order of 10% wall time versus the same compilation without
it, which is much less than the cost of -print-after-all on large modules.
---
llvm/include/llvm/IR/PrintPasses.h | 3 +
.../llvm/Passes/StandardInstrumentations.h | 8 +
llvm/lib/IR/PrintPasses.cpp | 8 +
llvm/lib/Passes/CMakeLists.txt | 21 ++
llvm/lib/Passes/StandardInstrumentations.cpp | 329 ++++++++++++++++++
llvm/test/Other/Inputs/check-ir-tracker.py | 40 +++
llvm/test/Other/ir-tracker-db.ll | 32 ++
7 files changed, 441 insertions(+)
create mode 100644 llvm/test/Other/Inputs/check-ir-tracker.py
create mode 100644 llvm/test/Other/ir-tracker-db.ll
diff --git a/llvm/include/llvm/IR/PrintPasses.h b/llvm/include/llvm/IR/PrintPasses.h
index 0aa1b379c35cf..95e0879da9bfb 100644
--- a/llvm/include/llvm/IR/PrintPasses.h
+++ b/llvm/include/llvm/IR/PrintPasses.h
@@ -81,6 +81,9 @@ std::string doSystemDiff(StringRef Before, StringRef After,
StringRef OldLineFormat, StringRef NewLineFormat,
StringRef UnchangedLineFormat);
+/// Non-empty when \c -ir-tracker-database is set.
+StringRef getIRTrackerDatabasePath();
+
} // namespace llvm
#endif // LLVM_IR_PRINTPASSES_H
diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h
index 4ee5ab2554868..48ad6c1aba84f 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -145,6 +145,13 @@ class PrintPassInstrumentation {
int Indent = 0;
};
+/// IR tracker instrumentation that records instructions with debug locations
+/// after each pass into a SQLite database when \c -ir-tracker-database is set.
+class IRTrackerInstrumentation {
+public:
+ LLVM_ABI void registerCallbacks(PassInstrumentationCallbacks &PIC);
+};
+
class PreservedCFGCheckerInstrumentation {
public:
// Keeps sticky poisoned flag for the given basic block once it has been
@@ -599,6 +606,7 @@ class PrintCrashIRInstrumentation {
class StandardInstrumentations {
PrintIRInstrumentation PrintIR;
PrintPassInstrumentation PrintPass;
+ IRTrackerInstrumentation IRTracker;
TimePassesHandler TimePasses;
TimeProfilingPassesHandler TimeProfilingPasses;
OptNoneInstrumentation OptNone;
diff --git a/llvm/lib/IR/PrintPasses.cpp b/llvm/lib/IR/PrintPasses.cpp
index 610411a3cf978..9815872406f4c 100644
--- a/llvm/lib/IR/PrintPasses.cpp
+++ b/llvm/lib/IR/PrintPasses.cpp
@@ -109,6 +109,12 @@ static cl::list<std::string>
"options"),
cl::CommaSeparated, cl::Hidden);
+static cl::opt<std::string> IRTrackerDatabase(
+ "ir-tracker-database",
+ cl::desc("IR tracker: SQLite database path; record instructions with "
+ "debug locations after each pass"),
+ cl::value_desc("file"), cl::init(""), cl::Hidden);
+
/// This is a helper to determine whether to print IR before or
/// after a pass.
@@ -164,6 +170,8 @@ bool llvm::isFunctionInPrintList(StringRef FunctionName) {
PrintFuncNames.count(std::string(FunctionName));
}
+StringRef llvm::getIRTrackerDatabasePath() { return IRTrackerDatabase; }
+
std::error_code cleanUpTempFilesImpl(ArrayRef<std::string> FileName,
unsigned N) {
std::error_code RC;
diff --git a/llvm/lib/Passes/CMakeLists.txt b/llvm/lib/Passes/CMakeLists.txt
index 5d7cd3689f3ff..bf2a9f2b551c5 100644
--- a/llvm/lib/Passes/CMakeLists.txt
+++ b/llvm/lib/Passes/CMakeLists.txt
@@ -33,3 +33,24 @@ add_llvm_component_library(LLVMPasses
Vectorize
Instrumentation
)
+
+# Optional: IR tracker SQLite backend (-ir-tracker-database).
+find_package(SQLite3 QUIET)
+if(SQLite3_FOUND)
+ set(IR_TRACKER_SQLITE_LIB SQLite::SQLite3)
+ set(IR_TRACKER_HAVE_SQLITE 1)
+else()
+ find_path(LLVM_SQLITE3_INCLUDE_DIR sqlite3.h)
+ find_library(LLVM_SQLITE3_LIBRARY sqlite3)
+ if(LLVM_SQLITE3_INCLUDE_DIR AND LLVM_SQLITE3_LIBRARY)
+ set(IR_TRACKER_SQLITE_LIB "${LLVM_SQLITE3_LIBRARY}")
+ set(IR_TRACKER_HAVE_SQLITE 1)
+ endif()
+endif()
+if(IR_TRACKER_HAVE_SQLITE)
+ target_compile_definitions(LLVMPasses PRIVATE LLVM_ENABLE_IR_TRACKER_SQLITE=1)
+ if(LLVM_SQLITE3_INCLUDE_DIR)
+ target_include_directories(LLVMPasses PRIVATE "${LLVM_SQLITE3_INCLUDE_DIR}")
+ endif()
+ target_link_libraries(LLVMPasses PRIVATE ${IR_TRACKER_SQLITE_LIB})
+endif()
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 19e72a8612c4a..5e6d16dfbb19d 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -14,6 +14,7 @@
#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/ADT/Any.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/LazyCallGraph.h"
#include "llvm/Analysis/LoopInfo.h"
@@ -22,8 +23,10 @@
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineVerifier.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/ModuleSlotTracker.h"
#include "llvm/IR/PassInstrumentation.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/PrintPasses.h"
@@ -40,6 +43,10 @@
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/xxhash.h"
+#ifdef LLVM_ENABLE_IR_TRACKER_SQLITE
+#include <sqlite3.h>
+#endif
+#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <utility>
@@ -368,6 +375,306 @@ bool isInterestingFunction(const Function &F) {
return isFunctionInPrintList(F.getName());
}
+#ifdef LLVM_ENABLE_IR_TRACKER_SQLITE
+static std::string getIRTrackerFilePath(const DILocation *Loc) {
+ if (!Loc)
+ return {};
+
+ StringRef Dir = Loc->getDirectory();
+ StringRef File = Loc->getFilename();
+ if (File.empty())
+ return {};
+ if (Dir.empty())
+ return File.str();
+
+ SmallString<256> Path(Dir);
+ sys::path::append(Path, File);
+ return std::string(Path);
+}
+
+static std::string getIRTrackerInstructionText(const Instruction &I,
+ ModuleSlotTracker &MST) {
+ std::string Text;
+ raw_string_ostream OS(Text);
+ I.print(OS, MST);
+ OS.flush();
+ return Text;
+}
+
+class IRTrackerState {
+ sqlite3 *DB = nullptr;
+ sqlite3_stmt *StmtInsertPass = nullptr;
+ sqlite3_stmt *StmtInsertFileIgnore = nullptr;
+ sqlite3_stmt *StmtSelectFileId = nullptr;
+ sqlite3_stmt *StmtInsertInst = nullptr;
+ unsigned NextSeq = 1;
+ bool InitialCaptured = false;
+ std::unordered_map<std::string, int> FileIdCache;
+
+ void check(int RC, const char *Ctx) const {
+ if (RC == SQLITE_OK || RC == SQLITE_DONE || RC == SQLITE_ROW)
+ return;
+ StringRef Msg = DB ? sqlite3_errmsg(DB) : sqlite3_errstr(RC);
+ report_fatal_error(Twine(Ctx) + ": " + Msg);
+ }
+
+ int getOrCreateFileId(StringRef Path) {
+ std::string Key = Path.str();
+ auto It = FileIdCache.find(Key);
+ if (It != FileIdCache.end())
+ return It->second;
+
+ check(sqlite3_bind_text(StmtInsertFileIgnore, 1, Key.data(), (int)Key.size(),
+ SQLITE_TRANSIENT),
+ "sqlite3_bind_text(insert file)");
+ check(sqlite3_step(StmtInsertFileIgnore), "sqlite3_step(insert file)");
+ check(sqlite3_reset(StmtInsertFileIgnore), "sqlite3_reset(insert file)");
+
+ check(sqlite3_bind_text(StmtSelectFileId, 1, Key.data(), (int)Key.size(),
+ SQLITE_TRANSIENT),
+ "sqlite3_bind_text(select file)");
+ if (sqlite3_step(StmtSelectFileId) != SQLITE_ROW)
+ report_fatal_error("ir-tracker: missing file row after insert");
+ int FileId = sqlite3_column_int(StmtSelectFileId, 0);
+ check(sqlite3_reset(StmtSelectFileId), "sqlite3_reset(select file)");
+ FileIdCache[Key] = FileId;
+ return FileId;
+ }
+
+ sqlite3_int64 insertPassRow(unsigned Seq, StringRef Phase, StringRef PassName,
+ StringRef IRUnit) {
+ check(sqlite3_bind_int(StmtInsertPass, 1, (int)Seq), "bind seq");
+ check(sqlite3_bind_text(StmtInsertPass, 2, Phase.data(), (int)Phase.size(),
+ SQLITE_TRANSIENT),
+ "bind phase");
+ check(sqlite3_bind_text(StmtInsertPass, 3, PassName.data(),
+ (int)PassName.size(), SQLITE_TRANSIENT),
+ "bind pass class");
+ check(sqlite3_bind_text(StmtInsertPass, 4, IRUnit.data(), (int)IRUnit.size(),
+ SQLITE_TRANSIENT),
+ "bind ir unit");
+ check(sqlite3_step(StmtInsertPass), "sqlite3_step(insert pass)");
+ check(sqlite3_reset(StmtInsertPass), "sqlite3_reset(insert pass)");
+ return sqlite3_last_insert_rowid(DB);
+ }
+
+ void insertInstructionRow(sqlite3_int64 PassRowId, StringRef FunctionName,
+ StringRef BBLabel, unsigned InstSeq,
+ StringRef Opcode, StringRef InstText, int FileId,
+ unsigned Line, unsigned Col) {
+ check(sqlite3_bind_int64(StmtInsertInst, 1, PassRowId), "bind pass id");
+ check(sqlite3_bind_text(StmtInsertInst, 2, FunctionName.data(),
+ (int)FunctionName.size(), SQLITE_TRANSIENT),
+ "bind function");
+ check(sqlite3_bind_text(StmtInsertInst, 3, BBLabel.data(),
+ (int)BBLabel.size(), SQLITE_TRANSIENT),
+ "bind basic block");
+ check(sqlite3_bind_int(StmtInsertInst, 4, (int)InstSeq), "bind inst seq");
+ check(sqlite3_bind_text(StmtInsertInst, 5, Opcode.data(),
+ (int)Opcode.size(), SQLITE_TRANSIENT),
+ "bind opcode");
+ check(sqlite3_bind_text(StmtInsertInst, 6, InstText.data(),
+ (int)InstText.size(), SQLITE_TRANSIENT),
+ "bind inst text");
+ check(sqlite3_bind_int(StmtInsertInst, 7, FileId), "bind file id");
+ check(sqlite3_bind_int(StmtInsertInst, 8, (int)Line), "bind line");
+ check(sqlite3_bind_int(StmtInsertInst, 9, (int)Col), "bind col");
+ check(sqlite3_step(StmtInsertInst), "sqlite3_step(insert instruction)");
+ check(sqlite3_reset(StmtInsertInst), "sqlite3_reset(insert instruction)");
+ }
+
+ void indexInstructionsInFunction(const Function &F, sqlite3_int64 PassRowId) {
+ if (F.isDeclaration() || !isFunctionInPrintList(F.getName()))
+ return;
+
+ std::string FunctionName = F.getName().str();
+ ModuleSlotTracker MST(F.getParent());
+ MST.incorporateFunction(F);
+ for (const BasicBlock &BB : F) {
+ std::string BBLabel =
+ BB.hasName() ? BB.getName().str() : std::string("<unnamed>");
+ unsigned InstSeq = 0;
+ for (const Instruction &I : BB) {
+ const DebugLoc &DL = I.getDebugLoc();
+ if (!DL)
+ continue;
+ const DILocation *Loc = DL.get();
+ if (!Loc || Loc->getLine() == 0)
+ continue;
+
+ std::string FilePath = getIRTrackerFilePath(Loc);
+ if (FilePath.empty())
+ continue;
+
+ insertInstructionRow(PassRowId, FunctionName, BBLabel, InstSeq++,
+ I.getOpcodeName(),
+ getIRTrackerInstructionText(I, MST),
+ getOrCreateFileId(FilePath), Loc->getLine(),
+ Loc->getColumn());
+ }
+ }
+ }
+
+ void indexIR(Any IR, sqlite3_int64 PassRowId) {
+ if (const auto *M = unwrapIR<Module>(IR)) {
+ for (const Function &F : *M)
+ indexInstructionsInFunction(F, PassRowId);
+ return;
+ }
+ if (const auto *F = unwrapIR<Function>(IR)) {
+ indexInstructionsInFunction(*F, PassRowId);
+ return;
+ }
+ if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR)) {
+ for (const LazyCallGraph::Node &N : *C)
+ indexInstructionsInFunction(N.getFunction(), PassRowId);
+ return;
+ }
+ if (const auto *L = unwrapIR<Loop>(IR))
+ indexInstructionsInFunction(*L->getHeader()->getParent(), PassRowId);
+ }
+
+ void captureInitialIR(Any IR) {
+ if (InitialCaptured)
+ return;
+ InitialCaptured = true;
+ sqlite3_int64 PassRowId =
+ insertPassRow(0, "initial", "<initial>", getIRName(IR));
+ indexIR(IR, PassRowId);
+ }
+
+public:
+ explicit IRTrackerState(StringRef Path) {
+ check(sqlite3_open_v2(Path.str().c_str(), &DB,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr),
+ "sqlite3_open_v2");
+
+ const char *ResetSchema = R"SQL(
+DROP TABLE IF EXISTS ir_tracker_instructions;
+DROP TABLE IF EXISTS ir_tracker_passes;
+DROP TABLE IF EXISTS ir_tracker_files;
+DROP TABLE IF EXISTS ir_tracker_meta;
+)SQL";
+ char *Err = nullptr;
+ if (sqlite3_exec(DB, ResetSchema, nullptr, nullptr, &Err) != SQLITE_OK) {
+ std::string Msg = Err ? Err : "unknown error";
+ sqlite3_free(Err);
+ report_fatal_error(Twine("ir-tracker reset: ") + Msg);
+ }
+
+ const char *Schema = R"SQL(
+PRAGMA foreign_keys = ON;
+CREATE TABLE ir_tracker_meta (
+ key TEXT PRIMARY KEY,
+ value TEXT NOT NULL
+);
+CREATE TABLE ir_tracker_files (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ path TEXT NOT NULL UNIQUE
+);
+CREATE TABLE ir_tracker_passes (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ seq INTEGER NOT NULL,
+ phase TEXT NOT NULL,
+ pass_class TEXT NOT NULL,
+ ir_unit TEXT NOT NULL
+);
+CREATE UNIQUE INDEX ir_tracker_idx_passes_seq
+ ON ir_tracker_passes(seq);
+CREATE TABLE ir_tracker_instructions (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ pass_id INTEGER NOT NULL REFERENCES ir_tracker_passes(id),
+ function TEXT NOT NULL,
+ basicblock TEXT NOT NULL,
+ inst_seq INTEGER NOT NULL,
+ opcode TEXT NOT NULL,
+ inst_text TEXT NOT NULL,
+ file_id INTEGER NOT NULL REFERENCES ir_tracker_files(id),
+ line INTEGER NOT NULL,
+ col INTEGER NOT NULL
+);
+CREATE INDEX ir_tracker_idx_instr_file_loc
+ ON ir_tracker_instructions(file_id, line, col);
+CREATE INDEX ir_tracker_idx_instr_pass
+ ON ir_tracker_instructions(pass_id);
+)SQL";
+ if (sqlite3_exec(DB, Schema, nullptr, nullptr, &Err) != SQLITE_OK) {
+ std::string Msg = Err ? Err : "unknown error";
+ sqlite3_free(Err);
+ report_fatal_error(Twine("ir-tracker schema: ") + Msg);
+ }
+ if (sqlite3_exec(DB,
+ "INSERT INTO ir_tracker_meta(key, value) "
+ "VALUES('schema_version', '1')",
+ nullptr, nullptr, &Err) != SQLITE_OK) {
+ std::string Msg = Err ? Err : "unknown error";
+ sqlite3_free(Err);
+ report_fatal_error(Twine("ir-tracker metadata: ") + Msg);
+ }
+
+ check(sqlite3_prepare_v2(
+ DB,
+ "INSERT INTO ir_tracker_passes(seq, phase, pass_class, ir_unit) "
+ "VALUES(?, ?, ?, ?)",
+ -1, &StmtInsertPass, nullptr),
+ "sqlite3_prepare(insert pass)");
+ check(sqlite3_prepare_v2(
+ DB, "INSERT OR IGNORE INTO ir_tracker_files(path) VALUES(?)", -1,
+ &StmtInsertFileIgnore, nullptr),
+ "sqlite3_prepare(insert file)");
+ check(sqlite3_prepare_v2(DB,
+ "SELECT id FROM ir_tracker_files WHERE path = ?",
+ -1, &StmtSelectFileId, nullptr),
+ "sqlite3_prepare(select file)");
+ check(sqlite3_prepare_v2(
+ DB,
+ "INSERT INTO ir_tracker_instructions("
+ "pass_id, function, basicblock, inst_seq, opcode, inst_text, "
+ "file_id, line, col) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ -1, &StmtInsertInst, nullptr),
+ "sqlite3_prepare(insert instruction)");
+
+ check(sqlite3_exec(DB, "BEGIN IMMEDIATE", nullptr, nullptr, nullptr),
+ "sqlite3_exec(begin transaction)");
+ }
+
+ ~IRTrackerState() {
+ if (StmtInsertInst)
+ sqlite3_finalize(StmtInsertInst);
+ if (StmtSelectFileId)
+ sqlite3_finalize(StmtSelectFileId);
+ if (StmtInsertFileIgnore)
+ sqlite3_finalize(StmtInsertFileIgnore);
+ if (StmtInsertPass)
+ sqlite3_finalize(StmtInsertPass);
+ if (!DB)
+ return;
+ if (sqlite3_get_autocommit(DB) == 0)
+ check(sqlite3_exec(DB, "COMMIT", nullptr, nullptr, nullptr),
+ "sqlite3_exec(commit transaction)");
+ sqlite3_close(DB);
+ }
+
+ void beforePass(StringRef PassID, Any IR) {
+ if (InitialCaptured || isIgnored(PassID) || !shouldPrintIR(IR))
+ return;
+ captureInitialIR(IR);
+ }
+
+ void afterPass(StringRef PassID, Any IR, PassInstrumentationCallbacks &PIC) {
+ if (isIgnored(PassID) || !shouldPrintIR(IR))
+ return;
+
+ StringRef PassName = PIC.getPassNameForClassName(PassID);
+ if (PassName.empty())
+ PassName = PassID;
+ sqlite3_int64 PassRowId =
+ insertPassRow(NextSeq++, "after", PassName, getIRName(IR));
+ indexIR(IR, PassRowId);
+ }
+};
+#endif
+
// Return true when this is a pass on IR for which printing
// of changes is desired.
bool isInteresting(Any IR, StringRef PassID, StringRef PassName) {
@@ -2506,10 +2813,32 @@ void PrintCrashIRInstrumentation::registerCallbacks(
});
}
+void IRTrackerInstrumentation::registerCallbacks(
+ PassInstrumentationCallbacks &PIC) {
+#ifdef LLVM_ENABLE_IR_TRACKER_SQLITE
+ StringRef Path = getIRTrackerDatabasePath();
+ if (Path.empty())
+ return;
+
+ auto State = std::make_shared<IRTrackerState>(Path);
+ PIC.registerBeforeNonSkippedPassCallback(
+ [State](StringRef PassID, Any IR) { State->beforePass(PassID, IR); });
+ PIC.registerAfterPassCallback(
+ [State, &PIC](StringRef PassID, Any IR, const PreservedAnalyses &) {
+ State->afterPass(PassID, IR, PIC);
+ });
+#else
+ if (!getIRTrackerDatabasePath().empty())
+ report_fatal_error(
+ "-ir-tracker-database requires an LLVM build linked against SQLite3");
+#endif
+}
+
void StandardInstrumentations::registerCallbacks(
PassInstrumentationCallbacks &PIC, ModuleAnalysisManager *MAM) {
PrintIR.registerCallbacks(PIC);
PrintPass.registerCallbacks(PIC);
+ IRTracker.registerCallbacks(PIC);
TimePasses.registerCallbacks(PIC);
OptNone.registerCallbacks(PIC);
OptPassGate.registerCallbacks(PIC);
diff --git a/llvm/test/Other/Inputs/check-ir-tracker.py b/llvm/test/Other/Inputs/check-ir-tracker.py
new file mode 100644
index 0000000000000..4fe2f70bf6c4d
--- /dev/null
+++ b/llvm/test/Other/Inputs/check-ir-tracker.py
@@ -0,0 +1,40 @@
+import sqlite3
+import sys
+
+
+def main() -> int:
+ con = sqlite3.connect(sys.argv[1])
+ cur = con.cursor()
+
+ schema = cur.execute(
+ "SELECT value FROM ir_tracker_meta WHERE key = 'schema_version'"
+ ).fetchone()
+ assert schema == ("1",), schema
+
+ passes = cur.execute(
+ "SELECT seq, phase FROM ir_tracker_passes ORDER BY seq"
+ ).fetchall()
+ assert passes, passes
+ assert passes[0] == (0, "initial"), passes
+ assert any(phase == "after" for _, phase in passes), passes
+
+ expected = set(sys.argv[2:])
+ functions = {
+ row[0]
+ for row in cur.execute(
+ "SELECT DISTINCT function FROM ir_tracker_instructions ORDER BY function"
+ )
+ }
+ assert functions == expected, (functions, expected)
+
+ rows = cur.execute(
+ "SELECT function, opcode FROM ir_tracker_instructions ORDER BY function, inst_seq"
+ ).fetchall()
+ for function in expected:
+ assert any(row[0] == function for row in rows), rows
+
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/llvm/test/Other/ir-tracker-db.ll b/llvm/test/Other/ir-tracker-db.ll
new file mode 100644
index 0000000000000..9ff1c0ec4e35c
--- /dev/null
+++ b/llvm/test/Other/ir-tracker-db.ll
@@ -0,0 +1,32 @@
+; RUN: opt -disable-output -passes=instcombine -ir-tracker-database=%t.db %s
+; RUN: %python %S/Inputs/check-ir-tracker.py %t.db f g
+; RUN: opt -disable-output -passes=instcombine -filter-print-funcs=f -ir-tracker-database=%t-filter.db %s
+; RUN: %python %S/Inputs/check-ir-tracker.py %t-filter.db f
+
+define i32 @f(i32 %x) !dbg !6 {
+entry:
+ %add = add i32 %x, 1, !dbg !8
+ ret i32 %add, !dbg !9
+}
+
+define i32 @g(i32 %x) !dbg !7 {
+entry:
+ %mul = mul i32 %x, 2, !dbg !10
+ ret i32 %mul, !dbg !11
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "ir-tracker-test", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "ir-tracker.c", directory: "/tmp")
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!4 = !DISubroutineType(types: !5)
+!5 = !{!3, !3}
+!6 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 7, type: !4, scopeLine: 7, spFlags: DISPFlagDefinition, unit: !0)
+!7 = distinct !DISubprogram(name: "g", scope: !1, file: !1, line: 13, type: !4, scopeLine: 13, spFlags: DISPFlagDefinition, unit: !0)
+!8 = !DILocation(line: 8, column: 3, scope: !6)
+!9 = !DILocation(line: 9, column: 3, scope: !6)
+!10 = !DILocation(line: 14, column: 3, scope: !7)
+!11 = !DILocation(line: 15, column: 3, scope: !7)
>From d950944cbb80aed88dd966de6e9c75519599cfc8 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Tue, 14 Apr 2026 10:06:26 -0400
Subject: [PATCH 2/2] Apply clang-format to IR tracker instrumentation (NFC)
---
llvm/lib/Passes/StandardInstrumentations.cpp | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 5e6d16dfbb19d..4e29811ab2136 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -424,8 +424,8 @@ class IRTrackerState {
if (It != FileIdCache.end())
return It->second;
- check(sqlite3_bind_text(StmtInsertFileIgnore, 1, Key.data(), (int)Key.size(),
- SQLITE_TRANSIENT),
+ check(sqlite3_bind_text(StmtInsertFileIgnore, 1, Key.data(),
+ (int)Key.size(), SQLITE_TRANSIENT),
"sqlite3_bind_text(insert file)");
check(sqlite3_step(StmtInsertFileIgnore), "sqlite3_step(insert file)");
check(sqlite3_reset(StmtInsertFileIgnore), "sqlite3_reset(insert file)");
@@ -450,8 +450,8 @@ class IRTrackerState {
check(sqlite3_bind_text(StmtInsertPass, 3, PassName.data(),
(int)PassName.size(), SQLITE_TRANSIENT),
"bind pass class");
- check(sqlite3_bind_text(StmtInsertPass, 4, IRUnit.data(), (int)IRUnit.size(),
- SQLITE_TRANSIENT),
+ check(sqlite3_bind_text(StmtInsertPass, 4, IRUnit.data(),
+ (int)IRUnit.size(), SQLITE_TRANSIENT),
"bind ir unit");
check(sqlite3_step(StmtInsertPass), "sqlite3_step(insert pass)");
check(sqlite3_reset(StmtInsertPass), "sqlite3_reset(insert pass)");
@@ -506,11 +506,10 @@ class IRTrackerState {
if (FilePath.empty())
continue;
- insertInstructionRow(PassRowId, FunctionName, BBLabel, InstSeq++,
- I.getOpcodeName(),
- getIRTrackerInstructionText(I, MST),
- getOrCreateFileId(FilePath), Loc->getLine(),
- Loc->getColumn());
+ insertInstructionRow(
+ PassRowId, FunctionName, BBLabel, InstSeq++, I.getOpcodeName(),
+ getIRTrackerInstructionText(I, MST), getOrCreateFileId(FilePath),
+ Loc->getLine(), Loc->getColumn());
}
}
}
More information about the llvm-commits
mailing list