[llvm] [Support] Faster line and column lookup in SourceMgr (PR #195881)
via llvm-commits
llvm-commits at lists.llvm.org
Tue May 5 09:37:37 PDT 2026
Albert =?utf-8?q?Havliček?= <havlialb at fit.cvut.cz>,
Albert =?utf-8?q?Havliček?= <havlialb at fit.cvut.cz>
Message-ID: <llvm.org/llvm/llvm-project/pull/195881 at github.com>
In-Reply-To:
https://github.com/Bertik23 created https://github.com/llvm/llvm-project/pull/195881
Previously line and column lookup worked by finding the line number by utilizing a cache of offsets of line ends. And finding the column number by linear search of a previous newline. This is quite slow and caused some issues after merging #174566 and was fixed by wrapping all calls of the slow function in a `if (ParserContext)` in #180068. This is not ideal, since when the parser context would be supplied the parsing of files with long lines will take ages.
This PR implements a fix to the problem by utilizing the computed offsets for line ends to calculate the position on the current line. Thus improving the asymptotic complexity of the getLineAndColumn method from O(log l + c) to O(log l) (l is number of lines, c is column position of the pointer). While not adding any overhead.
The second commit in this PR just updates two comments that I thought were misleading.
Since the getLineNumber methods are now unused I removed them in the third commit.
>From fc946c71697982aff25b5ec9accbfacdfc080036 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <havlialb at fit.cvut.cz>
Date: Tue, 5 May 2026 17:59:49 +0200
Subject: [PATCH 1/3] [Support] Faster line and column lookup
---
llvm/include/llvm/Support/SourceMgr.h | 9 ++++++
llvm/lib/Support/SourceMgr.cpp | 46 +++++++++++++++++++++++----
2 files changed, 49 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/Support/SourceMgr.h b/llvm/include/llvm/Support/SourceMgr.h
index 02e694cad8697..ec598336dc9b1 100644
--- a/llvm/include/llvm/Support/SourceMgr.h
+++ b/llvm/include/llvm/Support/SourceMgr.h
@@ -71,6 +71,15 @@ class SourceMgr {
template <typename T>
unsigned getLineNumberSpecialized(const char *Ptr) const;
+ /// Look up a given \p Ptr in the buffer, determining which line and column
+ /// it came from. This method has O(log n) complexity, where n is the number
+ /// of lines in the buffer.
+ LLVM_ABI std::pair<unsigned, unsigned>
+ getLineAndColumn(const char *Ptr) const;
+ template <typename T>
+ std::pair<unsigned, unsigned>
+ getLineAndColumnSpecialized(const char *Ptr) const;
+
/// Return a pointer to the first character of the specified line number or
/// null if the line number is invalid.
LLVM_ABI const char *getPointerForLineNumber(unsigned LineNo) const;
diff --git a/llvm/lib/Support/SourceMgr.cpp b/llvm/lib/Support/SourceMgr.cpp
index 299615a6c8041..85428403626fb 100644
--- a/llvm/lib/Support/SourceMgr.cpp
+++ b/llvm/lib/Support/SourceMgr.cpp
@@ -151,6 +151,45 @@ unsigned SourceMgr::SrcBuffer::getLineNumber(const char *Ptr) const {
return getLineNumberSpecialized<uint64_t>(Ptr);
}
+template <typename T>
+std::pair<unsigned, unsigned>
+SourceMgr::SrcBuffer::getLineAndColumnSpecialized(const char *Ptr) const {
+ std::vector<T> &Offsets =
+ GetOrCreateOffsetCache<T>(OffsetCache, Buffer.get());
+
+ const char *BufStart = Buffer->getBufferStart();
+ assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd());
+ ptrdiff_t PtrDiff = Ptr - BufStart;
+ assert(PtrDiff >= 0 &&
+ static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max());
+ T PtrOffset = static_cast<T>(PtrDiff);
+
+ // llvm::lower_bound gives the number of EOL before PtrOffset. Add 1 to get
+ // the line number.
+ auto I = llvm::lower_bound(Offsets, PtrOffset);
+ unsigned LineNo = I - Offsets.begin() + 1;
+
+ // The column number is the distance from the previous EOL (or the start of
+ // the buffer) to the pointer.
+ T LineStartOffs = (I == Offsets.begin()) ? 0 : (I[-1] + 1);
+ unsigned ColNo = PtrOffset - LineStartOffs + 1;
+ return {LineNo, ColNo};
+}
+
+/// Look up a given \p Ptr in the buffer, determining which line and column
+/// it came from.
+LLVM_ABI std::pair<unsigned, unsigned>
+SourceMgr::SrcBuffer::getLineAndColumn(const char *Ptr) const {
+ size_t Sz = Buffer->getBufferSize();
+ if (Sz <= std::numeric_limits<uint8_t>::max())
+ return getLineAndColumnSpecialized<uint8_t>(Ptr);
+ if (Sz <= std::numeric_limits<uint16_t>::max())
+ return getLineAndColumnSpecialized<uint16_t>(Ptr);
+ if (Sz <= std::numeric_limits<uint32_t>::max())
+ return getLineAndColumnSpecialized<uint32_t>(Ptr);
+ return getLineAndColumnSpecialized<uint64_t>(Ptr);
+}
+
template <typename T>
const char *SourceMgr::SrcBuffer::getPointerForLineNumberSpecialized(
unsigned LineNo) const {
@@ -217,12 +256,7 @@ SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const {
auto &SB = getBufferInfo(BufferID);
const char *Ptr = Loc.getPointer();
- unsigned LineNo = SB.getLineNumber(Ptr);
- const char *BufStart = SB.Buffer->getBufferStart();
- size_t NewlineOffs = StringRef(BufStart, Ptr - BufStart).find_last_of("\n\r");
- if (NewlineOffs == StringRef::npos)
- NewlineOffs = ~(size_t)0;
- return {LineNo, Ptr - BufStart - NewlineOffs};
+ return SB.getLineAndColumn(Ptr);
}
// FIXME: Note that the formatting of source locations is spread between
>From 50b2f2aba230f77bf6574548356e06e13aaeeb58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <havlialb at fit.cvut.cz>
Date: Tue, 5 May 2026 18:03:51 +0200
Subject: [PATCH 2/3] [Support] Update comments about time complexity of
getLineAndColumn
---
llvm/include/llvm/Support/SourceMgr.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/Support/SourceMgr.h b/llvm/include/llvm/Support/SourceMgr.h
index ec598336dc9b1..842163cd96e06 100644
--- a/llvm/include/llvm/Support/SourceMgr.h
+++ b/llvm/include/llvm/Support/SourceMgr.h
@@ -217,13 +217,15 @@ class SourceMgr {
LLVM_ABI unsigned FindBufferContainingLoc(SMLoc Loc) const;
/// Find the line number for the specified location in the specified file.
- /// This is not a fast method.
+ /// This method has O(log n) complexity, where n is the number of lines in the
+ /// buffer.
unsigned FindLineNumber(SMLoc Loc, unsigned BufferID = 0) const {
return getLineAndColumn(Loc, BufferID).first;
}
/// Find the line and column number for the specified location in the
- /// specified file. This is not a fast method.
+ /// specified file. This method has O(log n) complexity, where n is the number
+ /// of lines in the
LLVM_ABI std::pair<unsigned, unsigned>
getLineAndColumn(SMLoc Loc, unsigned BufferID = 0) const;
>From f15d187b62a1104d66064cad5e9c0c0774a30684 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Albert=20Havli=C4=8Dek?= <havlialb at fit.cvut.cz>
Date: Tue, 5 May 2026 18:36:16 +0200
Subject: [PATCH 3/3] [Support] Remove now unused functions
---
llvm/include/llvm/Support/SourceMgr.h | 6 ------
llvm/lib/Support/SourceMgr.cpp | 31 ---------------------------
2 files changed, 37 deletions(-)
diff --git a/llvm/include/llvm/Support/SourceMgr.h b/llvm/include/llvm/Support/SourceMgr.h
index 842163cd96e06..ec0b4edb295d4 100644
--- a/llvm/include/llvm/Support/SourceMgr.h
+++ b/llvm/include/llvm/Support/SourceMgr.h
@@ -65,12 +65,6 @@ class SourceMgr {
/// dynamically based on the size of Buffer.
mutable void *OffsetCache = nullptr;
- /// Look up a given \p Ptr in the buffer, determining which line it came
- /// from.
- LLVM_ABI unsigned getLineNumber(const char *Ptr) const;
- template <typename T>
- unsigned getLineNumberSpecialized(const char *Ptr) const;
-
/// Look up a given \p Ptr in the buffer, determining which line and column
/// it came from. This method has O(log n) complexity, where n is the number
/// of lines in the buffer.
diff --git a/llvm/lib/Support/SourceMgr.cpp b/llvm/lib/Support/SourceMgr.cpp
index 85428403626fb..2704025060e03 100644
--- a/llvm/lib/Support/SourceMgr.cpp
+++ b/llvm/lib/Support/SourceMgr.cpp
@@ -120,37 +120,6 @@ static std::vector<T> &GetOrCreateOffsetCache(void *&OffsetCache,
return *Offsets;
}
-template <typename T>
-unsigned SourceMgr::SrcBuffer::getLineNumberSpecialized(const char *Ptr) const {
- std::vector<T> &Offsets =
- GetOrCreateOffsetCache<T>(OffsetCache, Buffer.get());
-
- const char *BufStart = Buffer->getBufferStart();
- assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd());
- ptrdiff_t PtrDiff = Ptr - BufStart;
- assert(PtrDiff >= 0 &&
- static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max());
- T PtrOffset = static_cast<T>(PtrDiff);
-
- // llvm::lower_bound gives the number of EOL before PtrOffset. Add 1 to get
- // the line number.
- return llvm::lower_bound(Offsets, PtrOffset) - Offsets.begin() + 1;
-}
-
-/// Look up a given \p Ptr in the buffer, determining which line it came
-/// from.
-unsigned SourceMgr::SrcBuffer::getLineNumber(const char *Ptr) const {
- size_t Sz = Buffer->getBufferSize();
- if (Sz <= std::numeric_limits<uint8_t>::max())
- return getLineNumberSpecialized<uint8_t>(Ptr);
- else if (Sz <= std::numeric_limits<uint16_t>::max())
- return getLineNumberSpecialized<uint16_t>(Ptr);
- else if (Sz <= std::numeric_limits<uint32_t>::max())
- return getLineNumberSpecialized<uint32_t>(Ptr);
- else
- return getLineNumberSpecialized<uint64_t>(Ptr);
-}
-
template <typename T>
std::pair<unsigned, unsigned>
SourceMgr::SrcBuffer::getLineAndColumnSpecialized(const char *Ptr) const {
More information about the llvm-commits
mailing list