Skip to content

Commit 7a3aa36

Browse files
authored
Merge pull request #68980 from DougGregor/faster-find-buffer-containing-loc-5.10
2 parents a7b5ca8 + ceee85a commit 7a3aa36

File tree

2 files changed

+131
-9
lines changed

2 files changed

+131
-9
lines changed

include/swift/Basic/SourceManager.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/ADT/Optional.h"
2222
#include "llvm/Support/SourceMgr.h"
2323
#include <map>
24+
#include <vector>
2425

2526
namespace swift {
2627

@@ -134,6 +135,28 @@ class SourceManager {
134135
std::map<const char *, VirtualFile> VirtualFiles;
135136
mutable std::pair<const char *, const VirtualFile*> CachedVFile = {nullptr, nullptr};
136137

138+
/// A cache that improves the speed of location -> buffer lookups.
139+
struct BufferLocCache {
140+
/// The set of memory buffers IDs, sorted by the start of their source range.
141+
std::vector<unsigned> sortedBuffers;
142+
143+
/// The number of buffers that were present when sortedBuffers was formed.
144+
///
145+
/// There can be multiple buffers that refer to the same source range,
146+
/// and we remove duplicates as part of the processing of forming the
147+
/// vector of sorted buffers. This number is the number of original buffers,
148+
/// used to determine when the sorted buffers are out of date.
149+
unsigned numBuffersOriginal = 0;
150+
151+
/// The last buffer we looked in. This acts as a one-element MRU cache for
152+
/// lookups based on source locations.
153+
llvm::Optional<unsigned> lastBufferID;
154+
};
155+
156+
/// The cache that's used to quickly map a source location to a particular
157+
/// buffer ID.
158+
mutable BufferLocCache LocCache;
159+
137160
llvm::Optional<unsigned> findBufferContainingLocInternal(SourceLoc Loc) const;
138161

139162
public:

lib/Basic/SourceLoc.cpp

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "swift/Basic/Range.h"
1314
#include "swift/Basic/SourceLoc.h"
1415
#include "swift/Basic/SourceManager.h"
1516
#include "llvm/Support/FileSystem.h"
@@ -407,21 +408,119 @@ SourceManager::getGeneratedSourceInfo(unsigned bufferID) const {
407408
return known->second;
408409
}
409410

411+
namespace {
412+
/// Compare the source location ranges for two buffers, as an ordering to
413+
/// use for fast searches.
414+
struct BufferIDRangeComparison {
415+
const SourceManager *sourceMgr;
416+
417+
bool operator()(unsigned lhsID, unsigned rhsID) const {
418+
auto lhsRange = sourceMgr->getRangeForBuffer(lhsID);
419+
auto rhsRange = sourceMgr->getRangeForBuffer(rhsID);
420+
421+
// If the source buffers are identical, we want the higher-numbered
422+
// source buffers to occur first. This is important when uniquing.
423+
if (lhsRange == rhsRange)
424+
return lhsID > rhsID;
425+
426+
std::less<const char *> pointerCompare;
427+
return pointerCompare(
428+
(const char *)lhsRange.getStart().getOpaquePointerValue(),
429+
(const char *)rhsRange.getStart().getOpaquePointerValue());
430+
}
431+
432+
bool operator()(unsigned lhsID, SourceLoc rhsLoc) const {
433+
auto lhsRange = sourceMgr->getRangeForBuffer(lhsID);
434+
435+
std::less<const char *> pointerCompare;
436+
return pointerCompare(
437+
(const char *)lhsRange.getEnd().getOpaquePointerValue(),
438+
(const char *)rhsLoc.getOpaquePointerValue());
439+
}
440+
441+
bool operator()(SourceLoc lhsLoc, unsigned rhsID) const {
442+
auto rhsRange = sourceMgr->getRangeForBuffer(rhsID);
443+
444+
std::less<const char *> pointerCompare;
445+
return pointerCompare(
446+
(const char *)lhsLoc.getOpaquePointerValue(),
447+
(const char *)rhsRange.getEnd().getOpaquePointerValue());
448+
}
449+
};
450+
451+
/// Determine whether the source ranges for two buffers are equivalent.
452+
struct BufferIDSameRange {
453+
const SourceManager *sourceMgr;
454+
455+
bool operator()(unsigned lhsID, unsigned rhsID) const {
456+
auto lhsRange = sourceMgr->getRangeForBuffer(lhsID);
457+
auto rhsRange = sourceMgr->getRangeForBuffer(rhsID);
458+
459+
return lhsRange == rhsRange;
460+
}
461+
};
462+
}
463+
410464
llvm::Optional<unsigned>
411465
SourceManager::findBufferContainingLocInternal(SourceLoc Loc) const {
412466
assert(Loc.isValid());
413-
// Search the buffers back-to front, so later alias buffers are
414-
// visited first.
415-
auto less_equal = std::less_equal<const char *>();
416-
for (unsigned i = LLVMSourceMgr.getNumBuffers(), e = 1; i >= e; --i) {
417-
auto Buf = LLVMSourceMgr.getMemoryBuffer(i);
418-
if (less_equal(Buf->getBufferStart(), Loc.Value.getPointer()) &&
467+
468+
// If the cache is out-of-date, update it now.
469+
unsigned numBuffers = LLVMSourceMgr.getNumBuffers();
470+
if (numBuffers != LocCache.numBuffersOriginal) {
471+
LocCache.sortedBuffers.assign(
472+
std::begin(range(1, numBuffers+1)), std::end(range(1, numBuffers+1)));
473+
LocCache.numBuffersOriginal = numBuffers;
474+
475+
// Sort the buffer IDs by source range.
476+
std::sort(LocCache.sortedBuffers.begin(),
477+
LocCache.sortedBuffers.end(),
478+
BufferIDRangeComparison{this});
479+
480+
// Remove lower-numbered buffers with the same source ranges as higher-
481+
// numbered buffers. We want later alias buffers to be found first.
482+
auto newEnd = std::unique(
483+
LocCache.sortedBuffers.begin(), LocCache.sortedBuffers.end(),
484+
BufferIDSameRange{this});
485+
LocCache.sortedBuffers.erase(newEnd, LocCache.sortedBuffers.end());
486+
487+
// Forget the last buffer we looked at; it might have been replaced.
488+
LocCache.lastBufferID = llvm::None;
489+
}
490+
491+
// Determine whether the source location we're looking for is within the
492+
// given buffer ID.
493+
auto isInBuffer = [&](unsigned bufferID) {
494+
auto less_equal = std::less_equal<const char *>();
495+
auto buffer = LLVMSourceMgr.getMemoryBuffer(bufferID);
496+
497+
return less_equal(buffer->getBufferStart(), Loc.Value.getPointer()) &&
419498
// Use <= here so that a pointer to the null at the end of the buffer
420499
// is included as part of the buffer.
421-
less_equal(Loc.Value.getPointer(), Buf->getBufferEnd()))
422-
return i;
500+
less_equal(Loc.Value.getPointer(), buffer->getBufferEnd());
501+
};
502+
503+
// Check the last buffer we looked in.
504+
if (auto lastBufferID = LocCache.lastBufferID) {
505+
if (isInBuffer(*lastBufferID))
506+
return *lastBufferID;
423507
}
424-
return llvm::None;
508+
509+
// Search the sorted list of buffer IDs.
510+
auto found = std::lower_bound(LocCache.sortedBuffers.begin(),
511+
LocCache.sortedBuffers.end(),
512+
Loc,
513+
BufferIDRangeComparison{this});
514+
515+
// If the location was past the range covered by source buffers or
516+
// is not within any of the source buffers, fail.
517+
if (found == LocCache.sortedBuffers.end() || !isInBuffer(*found))
518+
return llvm::None;
519+
520+
// Cache the buffer ID we just found, because the next location is likely to
521+
// be close by.
522+
LocCache.lastBufferID = *found;
523+
return *found;
425524
}
426525

427526
unsigned SourceManager::findBufferContainingLoc(SourceLoc Loc) const {

0 commit comments

Comments
 (0)