Skip to content

Commit f9bdca3

Browse files
authored
Merge pull request #9818 from github/redsun82/swift-file-label-caching
Swift: cache file labels
2 parents edc8f6f + 42f4625 commit f9bdca3

File tree

7 files changed

+106
-71
lines changed

7 files changed

+106
-71
lines changed

swift/extractor/SwiftExtractor.cpp

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,29 +104,19 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config,
104104
dumpArgs(*trapTarget, config);
105105
TrapDomain trap{*trapTarget};
106106

107-
// TODO: move default location emission elsewhere, possibly in a separate global trap file
107+
// TODO: remove this and recreate it with IPA when we have that
108108
// the following cannot conflict with actual files as those have an absolute path starting with /
109-
auto unknownFileLabel = trap.createLabel<FileTag>("unknown");
110-
auto unknownLocationLabel = trap.createLabel<LocationTag>("unknown");
111-
trap.emit(FilesTrap{unknownFileLabel});
112-
trap.emit(LocationsTrap{unknownLocationLabel, unknownFileLabel});
109+
File unknownFileEntry{trap.createLabel<FileTag>("unknown")};
110+
Location unknownLocationEntry{trap.createLabel<LocationTag>("unknown")};
111+
unknownLocationEntry.file = unknownFileEntry.id;
112+
trap.emit(unknownFileEntry);
113+
trap.emit(unknownLocationEntry);
113114

114115
SwiftVisitor visitor(compiler.getSourceMgr(), trap, module, primaryFile);
115116
auto topLevelDecls = getTopLevelDecls(module, primaryFile);
116117
for (auto decl : topLevelDecls) {
117118
visitor.extract(decl);
118119
}
119-
// TODO the following will be moved to the dispatcher when we start caching swift file objects
120-
// for the moment, topLevelDecls always contains the current module, which does not have a file
121-
// associated with it, so we need a special case when there are no top level declarations
122-
if (topLevelDecls.size() == 1) {
123-
// In the case of empty files, the dispatcher is not called, but we still want to 'record' the
124-
// fact that the file was extracted
125-
llvm::SmallString<PATH_MAX> name(filename);
126-
llvm::sys::fs::make_absolute(name);
127-
auto fileLabel = trap.createLabel<FileTag>(name.str().str());
128-
trap.emit(FilesTrap{fileLabel, name.str().str()});
129-
}
130120
}
131121

132122
static std::unordered_set<std::string> collectInputFilenames(swift::CompilerInstance& compiler) {

swift/extractor/infra/FilePath.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <string>
4+
namespace codeql {
5+
6+
// wrapper around `std::string` mainly intended to unambiguously go into an `std::variant`
7+
// TODO probably not needed once we can use `std::filesystem::path`
8+
struct FilePath {
9+
FilePath() = default;
10+
FilePath(const std::string& path) : path{path} {}
11+
FilePath(std::string&& path) : path{std::move(path)} {}
12+
13+
std::string path;
14+
15+
bool operator==(const FilePath& other) const { return path == other.path; }
16+
};
17+
} // namespace codeql
18+
19+
namespace std {
20+
template <>
21+
struct hash<codeql::FilePath> {
22+
size_t operator()(const codeql::FilePath& value) { return hash<string>{}(value.path); }
23+
};
24+
} // namespace std

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 60 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "swift/extractor/trap/TrapDomain.h"
99
#include "swift/extractor/infra/SwiftTagTraits.h"
1010
#include "swift/extractor/trap/generated/TrapClasses.h"
11+
#include "swift/extractor/infra/FilePath.h"
1112

1213
namespace codeql {
1314

@@ -17,6 +18,25 @@ namespace codeql {
1718
// Since SwiftDispatcher sees all the AST nodes, it also attaches a location to every 'locatable'
1819
// node (AST nodes that are not types: declarations, statements, expressions, etc.).
1920
class SwiftDispatcher {
21+
// types to be supported by assignNewLabel/fetchLabel need to be listed here
22+
using Store = TrapLabelStore<const swift::Decl*,
23+
const swift::Stmt*,
24+
const swift::StmtCondition*,
25+
const swift::StmtConditionElement*,
26+
const swift::CaseLabelItem*,
27+
const swift::Expr*,
28+
const swift::Pattern*,
29+
const swift::TypeRepr*,
30+
const swift::TypeBase*,
31+
const swift::IfConfigClause*,
32+
FilePath>;
33+
34+
template <typename E>
35+
static constexpr bool IsStorable = std::is_constructible_v<Store::Handle, const E&>;
36+
37+
template <typename E>
38+
static constexpr bool IsLocatable = std::is_base_of_v<LocatableTag, TrapTagOf<E>>;
39+
2040
public:
2141
// all references and pointers passed as parameters to this constructor are supposed to outlive
2242
// the SwiftDispatcher
@@ -27,7 +47,12 @@ class SwiftDispatcher {
2747
: sourceManager{sourceManager},
2848
trap{trap},
2949
currentModule{currentModule},
30-
currentPrimarySourceFile{currentPrimarySourceFile} {}
50+
currentPrimarySourceFile{currentPrimarySourceFile} {
51+
if (currentPrimarySourceFile) {
52+
// we make sure the file is in the trap output even if the source is empty
53+
fetchLabel(getFilePath(currentPrimarySourceFile->getFilename()));
54+
}
55+
}
3156

3257
template <typename Entry>
3358
void emit(const Entry& entry) {
@@ -61,9 +86,11 @@ class SwiftDispatcher {
6186
// This method gives a TRAP label for already emitted AST node.
6287
// If the AST node was not emitted yet, then the emission is dispatched to a corresponding
6388
// visitor (see `visit(T *)` methods below).
64-
template <typename E, typename... Args>
65-
TrapLabelOf<E> fetchLabel(E* e, Args&&... args) {
66-
assert(e && "trying to fetch a label on nullptr, maybe fetchOptionalLabel is to be used?");
89+
template <typename E, typename... Args, std::enable_if_t<IsStorable<E>>* = nullptr>
90+
TrapLabelOf<E> fetchLabel(const E& e, Args&&... args) {
91+
if constexpr (std::is_constructible_v<bool, const E&>) {
92+
assert(e && "fetching a label on a null entity, maybe fetchOptionalLabel is to be used?");
93+
}
6794
// this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might
6895
// end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel`
6996
// only after having called `assignNewLabel` on `e`.
@@ -76,7 +103,7 @@ class SwiftDispatcher {
76103
visit(e, std::forward<Args>(args)...);
77104
// TODO when everything is moved to structured C++ classes, this should be moved to createEntry
78105
if (auto l = store.get(e)) {
79-
if constexpr (!std::is_base_of_v<swift::TypeBase, E>) {
106+
if constexpr (IsLocatable<E>) {
80107
attachLocation(e, *l);
81108
}
82109
return *l;
@@ -93,40 +120,37 @@ class SwiftDispatcher {
93120
return fetchLabelFromUnion<AstNodeTag>(node);
94121
}
95122

96-
TrapLabel<IfConfigClauseTag> fetchLabel(const swift::IfConfigClause& clause) {
97-
return fetchLabel(&clause);
98-
}
99-
100-
TrapLabel<ConditionElementTag> fetchLabel(const swift::StmtConditionElement& element) {
101-
return fetchLabel(&element);
123+
template <typename E, std::enable_if_t<IsStorable<E*>>* = nullptr>
124+
TrapLabelOf<E> fetchLabel(const E& e) {
125+
return fetchLabel(&e);
102126
}
103127

104128
// Due to the lazy emission approach, we must assign a label to a corresponding AST node before
105129
// it actually gets emitted to handle recursive cases such as recursive calls, or recursive type
106130
// declarations
107-
template <typename E, typename... Args>
108-
TrapLabelOf<E> assignNewLabel(E* e, Args&&... args) {
131+
template <typename E, typename... Args, std::enable_if_t<IsStorable<E>>* = nullptr>
132+
TrapLabelOf<E> assignNewLabel(const E& e, Args&&... args) {
109133
assert(waitingForNewLabel == Store::Handle{e} && "assignNewLabel called on wrong entity");
110134
auto label = trap.createLabel<TrapTagOf<E>>(std::forward<Args>(args)...);
111135
store.insert(e, label);
112136
waitingForNewLabel = std::monostate{};
113137
return label;
114138
}
115139

116-
template <typename E, typename... Args, std::enable_if_t<!std::is_pointer_v<E>>* = nullptr>
140+
template <typename E, typename... Args, std::enable_if_t<IsStorable<E*>>* = nullptr>
117141
TrapLabelOf<E> assignNewLabel(const E& e, Args&&... args) {
118142
return assignNewLabel(&e, std::forward<Args>(args)...);
119143
}
120144

121145
// convenience methods for structured C++ creation
122-
template <typename E, typename... Args, std::enable_if_t<!std::is_pointer_v<E>>* = nullptr>
146+
template <typename E, typename... Args>
123147
auto createEntry(const E& e, Args&&... args) {
124-
return TrapClassOf<E>{assignNewLabel(&e, std::forward<Args>(args)...)};
148+
return TrapClassOf<E>{assignNewLabel(e, std::forward<Args>(args)...)};
125149
}
126150

127151
// used to create a new entry for entities that should not be cached
128152
// an example is swift::Argument, that are created on the fly and thus have no stable pointer
129-
template <typename E, typename... Args, std::enable_if_t<!std::is_pointer_v<E>>* = nullptr>
153+
template <typename E, typename... Args>
130154
auto createUncachedEntry(const E& e, Args&&... args) {
131155
auto label = trap.createLabel<TrapTagOf<E>>(std::forward<Args>(args)...);
132156
attachLocation(&e, label);
@@ -219,35 +243,23 @@ class SwiftDispatcher {
219243
}
220244

221245
private:
222-
// types to be supported by assignNewLabel/fetchLabel need to be listed here
223-
using Store = TrapLabelStore<swift::Decl,
224-
swift::Stmt,
225-
swift::StmtCondition,
226-
swift::StmtConditionElement,
227-
swift::CaseLabelItem,
228-
swift::Expr,
229-
swift::Pattern,
230-
swift::TypeRepr,
231-
swift::TypeBase,
232-
swift::IfConfigClause>;
233-
234246
void attachLocation(swift::SourceLoc start,
235247
swift::SourceLoc end,
236248
TrapLabel<LocatableTag> locatableLabel) {
237249
if (!start.isValid() || !end.isValid()) {
238250
// invalid locations seem to come from entities synthesized by the compiler
239251
return;
240252
}
241-
std::string filepath = getFilepath(start);
242-
auto fileLabel = trap.createLabel<FileTag>(filepath);
243-
// TODO: do not emit duplicate trap entries for Files
244-
trap.emit(FilesTrap{fileLabel, filepath});
245-
auto [startLine, startColumn] = sourceManager.getLineAndColumnInBuffer(start);
246-
auto [endLine, endColumn] = sourceManager.getLineAndColumnInBuffer(end);
247-
auto locLabel = trap.createLabel<LocationTag>('{', fileLabel, "}:", startLine, ':', startColumn,
248-
':', endLine, ':', endColumn);
249-
trap.emit(LocationsTrap{locLabel, fileLabel, startLine, startColumn, endLine, endColumn});
250-
trap.emit(LocatableLocationsTrap{locatableLabel, locLabel});
253+
auto file = getFilePath(sourceManager.getDisplayNameForLoc(start));
254+
Location entry{{}};
255+
entry.file = fetchLabel(file);
256+
std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start);
257+
std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end);
258+
entry.id = trap.createLabel<LocationTag>('{', entry.file, "}:", entry.start_line, ':',
259+
entry.start_column, ':', entry.end_line, ':',
260+
entry.end_column);
261+
emit(entry);
262+
emit(LocatableLocationsTrap{locatableLabel, entry.id});
251263
}
252264

253265
template <typename Tag, typename... Ts>
@@ -276,14 +288,13 @@ class SwiftDispatcher {
276288
return false;
277289
}
278290

279-
std::string getFilepath(swift::SourceLoc loc) {
291+
static FilePath getFilePath(llvm::StringRef path) {
280292
// TODO: this needs more testing
281293
// TODO: check canonicaliztion of names on a case insensitive filesystems
282294
// TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true
283-
auto displayName = sourceManager.getDisplayNameForLoc(loc);
284295
llvm::SmallString<PATH_MAX> realPath;
285-
if (std::error_code ec = llvm::sys::fs::real_path(displayName, realPath)) {
286-
std::cerr << "Cannot get real path: '" << displayName.str() << "': " << ec.message() << "\n";
296+
if (std::error_code ec = llvm::sys::fs::real_path(path, realPath)) {
297+
std::cerr << "Cannot get real path: '" << path.str() << "': " << ec.message() << "\n";
287298
return {};
288299
}
289300
return realPath.str().str();
@@ -303,6 +314,12 @@ class SwiftDispatcher {
303314
virtual void visit(swift::TypeRepr* typeRepr, swift::Type type) = 0;
304315
virtual void visit(swift::TypeBase* type) = 0;
305316

317+
void visit(const FilePath& file) {
318+
auto entry = createEntry(file);
319+
entry.name = file.path;
320+
emit(entry);
321+
}
322+
306323
const swift::SourceManager& sourceManager;
307324
TrapDomain& trap;
308325
Store store;

swift/extractor/infra/SwiftTagTraits.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <swift/AST/ASTVisitor.h>
66
#include "swift/extractor/trap/TrapTagTraits.h"
77
#include "swift/extractor/trap/generated/TrapTags.h"
8+
#include "swift/extractor/infra/FilePath.h"
89

910
namespace codeql {
1011

@@ -15,12 +16,12 @@ using SILBoxTypeTag = SilBoxTypeTag;
1516
using SILFunctionTypeTag = SilFunctionTypeTag;
1617
using SILTokenTypeTag = SilTokenTypeTag;
1718

18-
#define MAP_TYPE_TO_TAG(TYPE, TAG) \
19-
template <> \
20-
struct detail::ToTagFunctor<swift::TYPE> { \
21-
using type = TAG; \
19+
#define MAP_TYPE_TO_TAG(TYPE, TAG) \
20+
template <> \
21+
struct detail::ToTagFunctor<TYPE> { \
22+
using type = TAG; \
2223
}
23-
#define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(TYPE, TYPE##Tag)
24+
#define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(swift::TYPE, TYPE##Tag)
2425
#define MAP_SUBTAG(TYPE, PARENT) \
2526
MAP_TAG(TYPE); \
2627
static_assert(std::is_base_of_v<PARENT##Tag, TYPE##Tag>, \
@@ -35,7 +36,7 @@ using SILTokenTypeTag = SilTokenTypeTag;
3536

3637
MAP_TAG(Stmt);
3738
MAP_TAG(StmtCondition);
38-
MAP_TYPE_TO_TAG(StmtConditionElement, ConditionElementTag);
39+
MAP_TYPE_TO_TAG(swift::StmtConditionElement, ConditionElementTag);
3940
MAP_TAG(CaseLabelItem);
4041
#define ABSTRACT_STMT(CLASS, PARENT) MAP_SUBTAG(CLASS##Stmt, PARENT)
4142
#define STMT(CLASS, PARENT) ABSTRACT_STMT(CLASS, PARENT)
@@ -60,14 +61,16 @@ MAP_TAG(Pattern);
6061

6162
MAP_TAG(TypeRepr);
6263

63-
MAP_TYPE_TO_TAG(TypeBase, TypeTag);
64+
MAP_TYPE_TO_TAG(swift::TypeBase, TypeTag);
6465
#define ABSTRACT_TYPE(CLASS, PARENT) MAP_SUBTAG(CLASS##Type, PARENT)
6566
#define TYPE(CLASS, PARENT) ABSTRACT_TYPE(CLASS, PARENT)
6667
#include <swift/AST/TypeNodes.def>
6768

6869
OVERRIDE_TAG(FuncDecl, ConcreteFuncDeclTag);
6970
OVERRIDE_TAG(VarDecl, ConcreteVarDeclTag);
7071

72+
MAP_TYPE_TO_TAG(FilePath, FileTag);
73+
7174
#undef MAP_TAG
7275
#undef MAP_SUBTAG
7376
#undef MAP_TYPE_TO_TAG

swift/extractor/trap/TrapLabelStore.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ namespace codeql {
2020
template <typename... Ts>
2121
class TrapLabelStore {
2222
public:
23-
using Handle = std::variant<std::monostate, const Ts*...>;
23+
using Handle = std::variant<std::monostate, Ts...>;
2424

2525
template <typename T>
26-
std::optional<TrapLabelOf<T>> get(const T* e) {
26+
std::optional<TrapLabelOf<T>> get(const T& e) {
2727
if (auto found = store_.find(e); found != store_.end()) {
2828
return TrapLabelOf<T>::unsafeCreateFromUntyped(found->second);
2929
}
3030
return std::nullopt;
3131
}
3232

3333
template <typename T>
34-
void insert(const T* e, TrapLabelOf<T> l) {
34+
void insert(const T& e, TrapLabelOf<T> l) {
3535
auto [_, inserted] = store_.emplace(e, l);
3636
assert(inserted && "already inserted");
3737
}

swift/extractor/trap/TrapTagTraits.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ struct ToTrapClassFunctor;
2626
} // namespace detail
2727

2828
template <typename T>
29-
using TrapTagOf = typename detail::ToTagOverride<std::remove_const_t<T>>::type;
29+
using TrapTagOf =
30+
typename detail::ToTagOverride<std::remove_const_t<std::remove_pointer_t<T>>>::type;
3031

3132
template <typename T>
3233
using TrapLabelOf = TrapLabel<TrapTagOf<T>>;

swift/extractor/visitors/SwiftVisitor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class SwiftVisitor : private SwiftDispatcher {
1414
using SwiftDispatcher::SwiftDispatcher;
1515

1616
template <typename T>
17-
void extract(T* entity) {
17+
void extract(const T& entity) {
1818
fetchLabel(entity);
1919
}
2020

0 commit comments

Comments
 (0)