1 //===--- PrecompiledPreamble.h - Build precompiled preambles ----*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Helper class to build precompiled preamble.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_CLANG_FRONTEND_PRECOMPILED_PREAMBLE_H
14 #define LLVM_CLANG_FRONTEND_PRECOMPILED_PREAMBLE_H
15 
16 #include "clang/Lex/Lexer.h"
17 #include "clang/Lex/Preprocessor.h"
18 #include "llvm/ADT/IntrusiveRefCntPtr.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/MD5.h"
21 #include <cstddef>
22 #include <memory>
23 #include <system_error>
24 #include <type_traits>
25 
26 namespace llvm {
27 class MemoryBuffer;
28 class MemoryBufferRef;
29 namespace vfs {
30 class FileSystem;
31 }
32 } // namespace llvm
33 
34 namespace clang {
35 class CompilerInstance;
36 class CompilerInvocation;
37 class Decl;
38 class DeclGroupRef;
39 class PCHContainerOperations;
40 
41 /// Runs lexer to compute suggested preamble bounds.
42 PreambleBounds ComputePreambleBounds(const LangOptions &LangOpts,
43                                      const llvm::MemoryBufferRef &Buffer,
44                                      unsigned MaxLines);
45 
46 class PreambleCallbacks;
47 
48 /// A class holding a PCH and all information to check whether it is valid to
49 /// reuse the PCH for the subsequent runs. Use BuildPreamble to create PCH and
50 /// CanReusePreamble + AddImplicitPreamble to make use of it.
51 class PrecompiledPreamble {
52   class PCHStorage;
53   struct PreambleFileHash;
54 
55 public:
56   /// Try to build PrecompiledPreamble for \p Invocation. See
57   /// BuildPreambleError for possible error codes.
58   ///
59   /// \param Invocation Original CompilerInvocation with options to compile the
60   /// file.
61   ///
62   /// \param MainFileBuffer Buffer with the contents of the main file.
63   ///
64   /// \param Bounds Bounds of the preamble, result of calling
65   /// ComputePreambleBounds.
66   ///
67   /// \param Diagnostics Diagnostics engine to be used while building the
68   /// preamble.
69   ///
70   /// \param VFS An instance of vfs::FileSystem to be used for file
71   /// accesses.
72   ///
73   /// \param PCHContainerOps An instance of PCHContainerOperations.
74   ///
75   /// \param StoreInMemory Store PCH in memory. If false, PCH will be stored in
76   /// a temporary file.
77   ///
78   /// \param Callbacks A set of callbacks to be executed when building
79   /// the preamble.
80   static llvm::ErrorOr<PrecompiledPreamble>
81   Build(const CompilerInvocation &Invocation,
82         const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
83         DiagnosticsEngine &Diagnostics,
84         IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
85         std::shared_ptr<PCHContainerOperations> PCHContainerOps,
86         bool StoreInMemory, PreambleCallbacks &Callbacks);
87 
88   PrecompiledPreamble(PrecompiledPreamble &&) = default;
89   PrecompiledPreamble &operator=(PrecompiledPreamble &&) = default;
90 
91   /// PreambleBounds used to build the preamble.
92   PreambleBounds getBounds() const;
93 
94   /// Returns the size, in bytes, that preamble takes on disk or in memory.
95   /// For on-disk preambles returns 0 if filesystem operations fail. Intended to
96   /// be used for logging and debugging purposes only.
97   std::size_t getSize() const;
98 
99   /// Returned string is not null-terminated.
getContents()100   llvm::StringRef getContents() const {
101     return {PreambleBytes.data(), PreambleBytes.size()};
102   }
103 
104   /// Check whether PrecompiledPreamble can be reused for the new contents(\p
105   /// MainFileBuffer) of the main file.
106   bool CanReuse(const CompilerInvocation &Invocation,
107                 const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
108                 llvm::vfs::FileSystem *VFS) const;
109 
110   /// Changes options inside \p CI to use PCH from this preamble. Also remaps
111   /// main file to \p MainFileBuffer and updates \p VFS to ensure the preamble
112   /// is accessible.
113   /// Requires that CanReuse() is true.
114   /// For in-memory preambles, PrecompiledPreamble instance continues to own the
115   /// MemoryBuffer with the Preamble after this method returns. The caller is
116   /// responsible for making sure the PrecompiledPreamble instance outlives the
117   /// compiler run and the AST that will be using the PCH.
118   void AddImplicitPreamble(CompilerInvocation &CI,
119                            IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,
120                            llvm::MemoryBuffer *MainFileBuffer) const;
121 
122   /// Configure \p CI to use this preamble.
123   /// Like AddImplicitPreamble, but doesn't assume CanReuse() is true.
124   /// If this preamble does not match the file, it may parse differently.
125   void OverridePreamble(CompilerInvocation &CI,
126                         IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,
127                         llvm::MemoryBuffer *MainFileBuffer) const;
128 
129 private:
130   PrecompiledPreamble(PCHStorage Storage, std::vector<char> PreambleBytes,
131                       bool PreambleEndsAtStartOfLine,
132                       llvm::StringMap<PreambleFileHash> FilesInPreamble,
133                       llvm::StringSet<> MissingFiles);
134 
135   /// A temp file that would be deleted on destructor call. If destructor is not
136   /// called for any reason, the file will be deleted at static objects'
137   /// destruction.
138   /// An assertion will fire if two TempPCHFiles are created with the same name,
139   /// so it's not intended to be used outside preamble-handling.
140   class TempPCHFile {
141   public:
142     // A main method used to construct TempPCHFile.
143     static llvm::ErrorOr<TempPCHFile> CreateNewPreamblePCHFile();
144 
145   private:
146     TempPCHFile(std::string FilePath);
147 
148   public:
149     TempPCHFile(TempPCHFile &&Other);
150     TempPCHFile &operator=(TempPCHFile &&Other);
151 
152     TempPCHFile(const TempPCHFile &) = delete;
153     ~TempPCHFile();
154 
155     /// A path where temporary file is stored.
156     llvm::StringRef getFilePath() const;
157 
158   private:
159     void RemoveFileIfPresent();
160 
161   private:
162     llvm::Optional<std::string> FilePath;
163   };
164 
165   class InMemoryPreamble {
166   public:
167     std::string Data;
168   };
169 
170   class PCHStorage {
171   public:
172     enum class Kind { Empty, InMemory, TempFile };
173 
174     PCHStorage() = default;
175     PCHStorage(TempPCHFile File);
176     PCHStorage(InMemoryPreamble Memory);
177 
178     PCHStorage(const PCHStorage &) = delete;
179     PCHStorage &operator=(const PCHStorage &) = delete;
180 
181     PCHStorage(PCHStorage &&Other);
182     PCHStorage &operator=(PCHStorage &&Other);
183 
184     ~PCHStorage();
185 
186     Kind getKind() const;
187 
188     TempPCHFile &asFile();
189     const TempPCHFile &asFile() const;
190 
191     InMemoryPreamble &asMemory();
192     const InMemoryPreamble &asMemory() const;
193 
194   private:
195     void destroy();
196     void setEmpty();
197 
198   private:
199     Kind StorageKind = Kind::Empty;
200     std::aligned_union_t<1, TempPCHFile, InMemoryPreamble> Storage = {};
201   };
202 
203   /// Data used to determine if a file used in the preamble has been changed.
204   struct PreambleFileHash {
205     /// All files have size set.
206     off_t Size = 0;
207 
208     /// Modification time is set for files that are on disk.  For memory
209     /// buffers it is zero.
210     time_t ModTime = 0;
211 
212     /// Memory buffers have MD5 instead of modification time.  We don't
213     /// compute MD5 for on-disk files because we hope that modification time is
214     /// enough to tell if the file was changed.
215     llvm::MD5::MD5Result MD5 = {};
216 
217     static PreambleFileHash createForFile(off_t Size, time_t ModTime);
218     static PreambleFileHash
219     createForMemoryBuffer(const llvm::MemoryBufferRef &Buffer);
220 
221     friend bool operator==(const PreambleFileHash &LHS,
222                            const PreambleFileHash &RHS) {
223       return LHS.Size == RHS.Size && LHS.ModTime == RHS.ModTime &&
224              LHS.MD5 == RHS.MD5;
225     }
226     friend bool operator!=(const PreambleFileHash &LHS,
227                            const PreambleFileHash &RHS) {
228       return !(LHS == RHS);
229     }
230   };
231 
232   /// Helper function to set up PCH for the preamble into \p CI and \p VFS to
233   /// with the specified \p Bounds.
234   void configurePreamble(PreambleBounds Bounds, CompilerInvocation &CI,
235                          IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,
236                          llvm::MemoryBuffer *MainFileBuffer) const;
237 
238   /// Sets up the PreprocessorOptions and changes VFS, so that PCH stored in \p
239   /// Storage is accessible to clang. This method is an implementation detail of
240   /// AddImplicitPreamble.
241   static void
242   setupPreambleStorage(const PCHStorage &Storage,
243                        PreprocessorOptions &PreprocessorOpts,
244                        IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS);
245 
246   /// Manages the memory buffer or temporary file that stores the PCH.
247   PCHStorage Storage;
248   /// Keeps track of the files that were used when computing the
249   /// preamble, with both their buffer size and their modification time.
250   ///
251   /// If any of the files have changed from one compile to the next,
252   /// the preamble must be thrown away.
253   llvm::StringMap<PreambleFileHash> FilesInPreamble;
254   /// Files that were not found during preamble building. If any of these now
255   /// exist then the preamble should not be reused.
256   ///
257   /// Storing *all* the missing files that could invalidate the preamble would
258   /// make it too expensive to revalidate (when the include path has many
259   /// entries, each #include will miss half of them on average).
260   /// Instead, we track only files that could have satisfied an #include that
261   /// was ultimately not found.
262   llvm::StringSet<> MissingFiles;
263   /// The contents of the file that was used to precompile the preamble. Only
264   /// contains first PreambleBounds::Size bytes. Used to compare if the relevant
265   /// part of the file has not changed, so that preamble can be reused.
266   std::vector<char> PreambleBytes;
267   /// See PreambleBounds::PreambleEndsAtStartOfLine
268   bool PreambleEndsAtStartOfLine;
269 };
270 
271 /// A set of callbacks to gather useful information while building a preamble.
272 class PreambleCallbacks {
273 public:
274   virtual ~PreambleCallbacks() = default;
275 
276   /// Called before FrontendAction::BeginSourceFile.
277   /// Can be used to store references to various CompilerInstance fields
278   /// (e.g. SourceManager) that may be interesting to the consumers of other
279   /// callbacks.
280   virtual void BeforeExecute(CompilerInstance &CI);
281   /// Called after FrontendAction::Execute(), but before
282   /// FrontendAction::EndSourceFile(). Can be used to transfer ownership of
283   /// various CompilerInstance fields before they are destroyed.
284   virtual void AfterExecute(CompilerInstance &CI);
285   /// Called after PCH has been emitted. \p Writer may be used to retrieve
286   /// information about AST, serialized in PCH.
287   virtual void AfterPCHEmitted(ASTWriter &Writer);
288   /// Called for each TopLevelDecl.
289   /// NOTE: To allow more flexibility a custom ASTConsumer could probably be
290   /// used instead, but having only this method allows a simpler API.
291   virtual void HandleTopLevelDecl(DeclGroupRef DG);
292   /// Creates wrapper class for PPCallbacks so we can also process information
293   /// about includes that are inside of a preamble
294   virtual std::unique_ptr<PPCallbacks> createPPCallbacks();
295   /// The returned CommentHandler will be added to the preprocessor if not null.
296   virtual CommentHandler *getCommentHandler();
297   /// Determines which function bodies are parsed, by default skips everything.
298   /// Only used if FrontendOpts::SkipFunctionBodies is true.
299   /// See ASTConsumer::shouldSkipFunctionBody.
shouldSkipFunctionBody(Decl * D)300   virtual bool shouldSkipFunctionBody(Decl *D) { return true; }
301 };
302 
303 enum class BuildPreambleError {
304   CouldntCreateTempFile = 1,
305   CouldntCreateTargetInfo,
306   BeginSourceFileFailed,
307   CouldntEmitPCH,
308   BadInputs
309 };
310 
311 class BuildPreambleErrorCategory final : public std::error_category {
312 public:
313   const char *name() const noexcept override;
314   std::string message(int condition) const override;
315 };
316 
317 std::error_code make_error_code(BuildPreambleError Error);
318 } // namespace clang
319 
320 namespace std {
321 template <>
322 struct is_error_code_enum<clang::BuildPreambleError> : std::true_type {};
323 } // namespace std
324 
325 #endif
326