Skip to content

Commit 193d044

Browse files
[DRAFT][SYCL RTC] Implement --auto-pch support
1 parent 86451eb commit 193d044

File tree

10 files changed

+388
-17
lines changed

10 files changed

+388
-17
lines changed

clang/include/clang/Driver/Options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ enum ClangVisibility {
4141
FlangOption = (1 << 4),
4242
FC1Option = (1 << 5),
4343
DXCOption = (1 << 6),
44+
SYCLRTCOnlyOption = (1 << 7),
4445
};
4546

4647
enum ID {

clang/include/clang/Driver/Options.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ def FC1Option : OptionVisibility;
107107
// are made available when the driver is running in DXC compatibility mode.
108108
def DXCOption : OptionVisibility;
109109

110+
def SYCLRTCOnlyOption : OptionVisibility;
111+
110112
/////////
111113
// Docs
112114

@@ -195,6 +197,11 @@ def sycl_Group : OptionGroup<"<SYCL group>">, Group<f_Group>,
195197
DocName<"SYCL options">,
196198
Visibility<[ClangOption, CLOption]>;
197199

200+
def sycl_rtc_only_Group : OptionGroup<"<SYCL RTC only group">,
201+
Group<f_Group>,
202+
DocName<"SYCL RTC specific options">,
203+
Visibility<[SYCLRTCOnlyOption]>;
204+
198205
def cuda_Group : OptionGroup<"<CUDA group>">, Group<f_Group>,
199206
DocName<"CUDA options">,
200207
Visibility<[ClangOption, CLOption]>;
@@ -7511,6 +7518,13 @@ def fsyclbin : Flag<["-"], "fsyclbin">, Alias<fsyclbin_EQ>,
75117518
AliasArgs<["executable"]>;
75127519
} // let Group = sycl_Group
75137520

7521+
let Visibility = [SYCLRTCOnlyOption] in {
7522+
let Group = sycl_rtc_only_Group in {
7523+
def auto_pch : Flag<["--"], "auto-pch">,
7524+
HelpText<"Enable Auto-PCH for SYCL RTC Compilation">;
7525+
} // let Group = sycl_rtc_only_Group
7526+
} // let Visibility = [SYCLRTCOnlyOption]
7527+
75147528
// FIXME: -fsycl-explicit-simd is deprecated. remove it when support is dropped.
75157529
def : Flag<["-"], "fsycl-explicit-simd">, Flags<[Deprecated]>,
75167530
Group<clang_ignored_legacy_options_Group>,

clang/include/clang/Frontend/PrecompiledPreamble.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ class PrecompiledPreamble {
8787
IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics,
8888
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
8989
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
90-
bool StoreInMemory, StringRef StoragePath,
91-
PreambleCallbacks &Callbacks);
90+
bool StoreInMemory, StringRef StoragePath, PreambleCallbacks &Callbacks,
91+
bool AllowASTWithErrors = true);
9292

9393
PrecompiledPreamble(PrecompiledPreamble &&);
9494
PrecompiledPreamble &operator=(PrecompiledPreamble &&);

clang/lib/Frontend/PrecompiledPreamble.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,10 @@ class TempPCHFile {
247247
class PrecompilePreambleAction : public ASTFrontendAction {
248248
public:
249249
PrecompilePreambleAction(std::shared_ptr<PCHBuffer> Buffer, bool WritePCHFile,
250-
PreambleCallbacks &Callbacks)
250+
PreambleCallbacks &Callbacks,
251+
bool AllowASTWithErrors = true)
251252
: Buffer(std::move(Buffer)), WritePCHFile(WritePCHFile),
252-
Callbacks(Callbacks) {}
253+
Callbacks(Callbacks), AllowASTWithErrors(AllowASTWithErrors) {}
253254

254255
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
255256
StringRef InFile) override;
@@ -285,17 +286,19 @@ class PrecompilePreambleAction : public ASTFrontendAction {
285286
bool WritePCHFile; // otherwise the PCH is written into the PCHBuffer only.
286287
std::unique_ptr<llvm::raw_pwrite_stream> FileOS; // null if in-memory
287288
PreambleCallbacks &Callbacks;
289+
bool AllowASTWithErrors;
288290
};
289291

290292
class PrecompilePreambleConsumer : public PCHGenerator {
291293
public:
292294
PrecompilePreambleConsumer(PrecompilePreambleAction &Action, Preprocessor &PP,
293295
ModuleCache &ModCache, StringRef isysroot,
294296
std::shared_ptr<PCHBuffer> Buffer,
295-
const CodeGenOptions &CodeGenOpts)
297+
const CodeGenOptions &CodeGenOpts,
298+
bool AllowASTWithErrors = true)
296299
: PCHGenerator(PP, ModCache, "", isysroot, std::move(Buffer), CodeGenOpts,
297300
ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
298-
/*AllowASTWithErrors=*/true),
301+
AllowASTWithErrors),
299302
Action(Action) {}
300303

301304
bool HandleTopLevelDecl(DeclGroupRef DG) override {
@@ -337,7 +340,7 @@ PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI,
337340

338341
return std::make_unique<PrecompilePreambleConsumer>(
339342
*this, CI.getPreprocessor(), CI.getModuleCache(), Sysroot, Buffer,
340-
CI.getCodeGenOpts());
343+
CI.getCodeGenOpts(), AllowASTWithErrors);
341344
}
342345

343346
template <class T> bool moveOnNoError(llvm::ErrorOr<T> Val, T &Output) {
@@ -415,7 +418,8 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
415418
IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics,
416419
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
417420
std::shared_ptr<PCHContainerOperations> PCHContainerOps, bool StoreInMemory,
418-
StringRef StoragePath, PreambleCallbacks &Callbacks) {
421+
StringRef StoragePath, PreambleCallbacks &Callbacks,
422+
bool AllowASTWithErrors) {
419423
assert(VFS && "VFS is null");
420424

421425
auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);
@@ -512,7 +516,7 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
512516
auto Act = std::make_unique<PrecompilePreambleAction>(
513517
std::move(Buffer),
514518
/*WritePCHFile=*/Storage->getKind() == PCHStorage::Kind::TempFile,
515-
Callbacks);
519+
Callbacks, AllowASTWithErrors);
516520
if (!Act->BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
517521
return BuildPreambleError::BeginSourceFileFailed;
518522

clang/test/Driver/sycl-unsupported.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@
6464
// UNSUPPORTED_OPT-NOT: clang{{.*}} "-fsycl-is-device"{{.*}} "[[OPT_CC1]]{{.*}}"
6565
// UNSUPPORTED_OPT: clang{{.*}} "-fsycl-is-host"{{.*}} "[[OPT_CC1]]{{.*}}"
6666

67+
// "--auto-pch" should only be enabled for SYCL RTC compilations, regular driver
68+
// shouldn't know about it:
69+
//
70+
// RUN: not %clangxx -### %s --auto-pch 2>&1 | FileCheck %s --check-prefix AUTO_PCH
71+
// RUN: not %clangxx -fsycl-device-only -### %s --auto-pch 2>&1 | FileCheck %s --check-prefix AUTO_PCH
72+
// RUN: not %clangxx -fsycl -### %s --auto-pch 2>&1 | FileCheck %s --check-prefix AUTO_PCH
73+
//
74+
// AUTO_PCH: error: unknown argument: '--auto-pch'
75+
6776
// FPGA support has been removed, usage of any FPGA specific options and any
6877
// options that have FPGA specific arguments should emit a specific error
6978
// diagnostic.

sycl-jit/jit-compiler/lib/rtc/DeviceCompilation.cpp

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <clang/Frontend/ChainedDiagnosticConsumer.h>
2626
#include <clang/Frontend/CompilerInstance.h>
2727
#include <clang/Frontend/FrontendActions.h>
28+
#include <clang/Frontend/PrecompiledPreamble.h>
2829
#include <clang/Frontend/TextDiagnosticBuffer.h>
2930
#include <clang/Frontend/TextDiagnosticPrinter.h>
3031
#include <clang/Frontend/Utils.h>
@@ -78,6 +79,12 @@ class SYCLToolchain {
7879
}
7980
}
8081

82+
struct PrecompiledPreambles {
83+
using key = std::pair<std::string /*Opts*/, std::string /*Preamble*/>;
84+
std::mutex Mutex;
85+
std::map<key, std::shared_ptr<PrecompiledPreamble>> PreamblesMap;
86+
};
87+
8188
// Similar to FrontendActionFactory, but we don't take ownership of
8289
// `FrontendAction`, nor do we create copies of it as we only perform a single
8390
// `ToolInvocation`.
@@ -140,9 +147,15 @@ class SYCLToolchain {
140147
}
141148

142149
ArgStringList ASL;
143-
for_each(DAL, [&DAL, &ASL](Arg *A) { A->render(DAL, ASL); });
144-
for_each(UserArgList,
145-
[&UserArgList, &ASL](Arg *A) { A->render(UserArgList, ASL); });
150+
for (Arg *A : DAL)
151+
A->render(DAL, ASL);
152+
for (Arg *A : UserArgList) {
153+
Option Group = A->getOption().getGroup();
154+
if (Group.isValid() && Group.getID() == OPT_sycl_rtc_only_Group)
155+
continue;
156+
157+
A->render(UserArgList, ASL);
158+
}
146159

147160
std::vector<std::string> CommandLine;
148161
CommandLine.reserve(ASL.size() + 2);
@@ -153,6 +166,79 @@ class SYCLToolchain {
153166
return CommandLine;
154167
}
155168

169+
class ActionWithPCHPreamble : public Action {
170+
std::string CmdLineOpts;
171+
172+
public:
173+
ActionWithPCHPreamble(FrontendAction &FEAction, std::string &&CmdLineOpts)
174+
: Action(FEAction), CmdLineOpts(std::move(CmdLineOpts)) {}
175+
176+
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
177+
FileManager *Files,
178+
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
179+
DiagnosticConsumer *DiagConsumer) override {
180+
auto MainFilePath = Invocation->getFrontendOpts().Inputs[0].getFile();
181+
auto MainFileBuffer = Files->getBufferForFile(MainFilePath);
182+
assert(MainFileBuffer && "Can't get memory buffer for in-memory source?");
183+
184+
PreambleBounds Bounds = ComputePreambleBounds(
185+
Invocation->getLangOpts(), **MainFileBuffer, 100 /* MaxLines */);
186+
187+
PrecompiledPreambles::key key{
188+
std::move(CmdLineOpts),
189+
(*MainFileBuffer)->getBuffer().substr(0, Bounds.Size).str()};
190+
191+
std::shared_ptr<PrecompiledPreamble> Preamble;
192+
{
193+
PrecompiledPreambles &Preambles = SYCLToolchain::instance().Preambles;
194+
std::lock_guard<std::mutex> Lock{Preambles.Mutex};
195+
auto [It, Inserted] = Preambles.PreamblesMap.try_emplace(key);
196+
197+
if (Inserted) {
198+
PreambleCallbacks Callbacks;
199+
auto DiagIds = llvm::makeIntrusiveRefCnt<DiagnosticIDs>();
200+
auto DiagOpts = Invocation->getDiagnosticOpts();
201+
auto Diags = llvm::makeIntrusiveRefCnt<DiagnosticsEngine>(
202+
DiagIds, DiagOpts, DiagConsumer, false);
203+
204+
static std::string StoragePath =
205+
(SYCLToolchain::instance().getPrefix() + "/preambles").str();
206+
llvm::ErrorOr<PrecompiledPreamble> NewPreamble =
207+
PrecompiledPreamble::Build(
208+
*Invocation, MainFileBuffer->get(), Bounds, Diags,
209+
Files->getVirtualFileSystemPtr(), PCHContainerOps,
210+
/*StorePreamblesInMemory*/ true, StoragePath, Callbacks,
211+
/*AllowASTWithErrors=*/false);
212+
213+
if (!NewPreamble)
214+
return false;
215+
216+
It->second = std::make_shared<PrecompiledPreamble>(
217+
std::move(NewPreamble.get()));
218+
}
219+
220+
Preamble = It->second;
221+
} // End lock
222+
223+
assert(Preamble);
224+
assert(Preamble->CanReuse(*Invocation, **MainFileBuffer, Bounds,
225+
Files->getVirtualFileSystem()));
226+
227+
// FIXME: WHY release????
228+
auto Buf = llvm::MemoryBuffer::getMemBufferCopy(
229+
(*MainFileBuffer)->getBuffer(), MainFilePath)
230+
.release();
231+
232+
auto VFS = Files->getVirtualFileSystemPtr();
233+
Preamble->AddImplicitPreamble(*Invocation, VFS, Buf);
234+
auto NewFiles = makeIntrusiveRefCnt<FileManager>(
235+
Files->getFileSystemOpts(), std::move(VFS));
236+
237+
return Action::runInvocation(std::move(Invocation), NewFiles.get(),
238+
std::move(PCHContainerOps), DiagConsumer);
239+
}
240+
};
241+
156242
public:
157243
static SYCLToolchain &instance() {
158244
static SYCLToolchain Instance;
@@ -162,7 +248,8 @@ class SYCLToolchain {
162248
bool run(const InputArgList &UserArgList, BinaryFormat Format,
163249
const char *SourceFilePath, FrontendAction &FEAction,
164250
IntrusiveRefCntPtr<FileSystem> FSOverlay = nullptr,
165-
DiagnosticConsumer *DiagConsumer = nullptr) {
251+
DiagnosticConsumer *DiagConsumer = nullptr,
252+
bool UseAutoPCH = false) {
166253
std::vector<std::string> CommandLine =
167254
createCommandLine(UserArgList, Format, SourceFilePath);
168255

@@ -175,9 +262,21 @@ class SYCLToolchain {
175262
auto Files = llvm::makeIntrusiveRefCnt<clang::FileManager>(
176263
clang::FileSystemOptions{"." /* WorkingDir */}, FS);
177264

178-
Action A{FEAction};
179-
ToolInvocation TI{CommandLine, &A, Files.get(),
180-
std::make_shared<PCHContainerOperations>()};
265+
Action Normal{FEAction};
266+
267+
// User compilation options must be part of the key in the preambles map. We
268+
// can either use "raw" user options or the "processed" from
269+
// `createCommandLine` as long as we're consistent in what we're using.
270+
// Current internal APIs pass `InputArgList` around instead of a single
271+
// `std::string`, so it's easier to use `CommandLine`. Just make sure to
272+
// drop `rtc_N.cpp` that is always different:
273+
ActionWithPCHPreamble WithPreamble{FEAction,
274+
join(drop_end(CommandLine, 1), " ")};
275+
ToolInvocation TI{CommandLine,
276+
UseAutoPCH ? static_cast<Action *>(&WithPreamble)
277+
: &Normal,
278+
Files.get(), std::make_shared<PCHContainerOperations>()};
279+
181280
TI.setDiagnosticConsumer(DiagConsumer ? DiagConsumer : &IgnoreDiag);
182281

183282
return TI.run();
@@ -217,6 +316,8 @@ class SYCLToolchain {
217316
std::string ClangXXExe = (Prefix + "/bin/clang++").str();
218317
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> ToolchainFS =
219318
llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
319+
320+
PrecompiledPreambles Preambles;
220321
};
221322

222323
class ClangDiagnosticWrapper {
@@ -348,9 +449,11 @@ Expected<ModuleUPtr> jit_compiler::compileDeviceCode(
348449
DiagnosticOptions DiagOpts;
349450
ClangDiagnosticWrapper Wrapper(BuildLog, &DiagOpts);
350451

452+
bool AutoPCH = UserArgList.hasArg(OPT_auto_pch);
453+
351454
if (SYCLToolchain::instance().run(UserArgList, Format, SourceFile.Path, ELOA,
352455
getInMemoryFS(SourceFile, IncludeFiles),
353-
Wrapper.consumer())) {
456+
Wrapper.consumer(), AutoPCH)) {
354457
return ELOA.takeModule();
355458
} else {
356459
return createStringError(BuildLog);

sycl/doc/extensions/experimental/sycl_ext_oneapi_kernel_compiler.asciidoc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,57 @@ build_options{{
11271127
Relax the requirement that parameter types for free-function kernels must be
11281128
forward-declarable.
11291129

1130+
===== `--auto-pch`
1131+
1132+
Enable auto-detection of the preamble and use it as a pre-compiled header to
1133+
speed up subsequent compilations of TUs matching the preamble/compilation
1134+
options. Example of the code that can benefit from this:
1135+
1136+
[source,c++]
1137+
----
1138+
#include <sycl/sycl.hpp>
1139+
1140+
// Auto-detected preamble ends before next line:
1141+
namespace syclext = sycl::ext::oneapi;
1142+
namespace syclexp = sycl::ext::oneapi::experimental;
1143+
1144+
extern "C"
1145+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
1146+
void iota(float start, float *ptr) {
1147+
size_t id = syclext::this_work_item::get_nd_item<1>().get_global_linear_id();
1148+
ptr[id] = start + static_cast<float>(id);
1149+
}
1150+
----
1151+
1152+
Limitations:
1153+
1154+
* Preamble detection is done at the Lexer level and can't handle code like
1155+
1156+
[source,c++]
1157+
----
1158+
#if 1
1159+
#include <sycl/sycl.hpp>
1160+
#else
1161+
// Auto-detected preamble ends in the middle of `#else` and would fail to compile.
1162+
void foo() {}
1163+
#endif
1164+
----
1165+
1166+
* Any changes in either preamble or compilation options (including
1167+
`-DSOMETHING`!) result in a creation of a new pre-compiled header/preamble.
1168+
1169+
* No support (including not reporting any errors) for `+__DATE__+`/`+__TIME__+`
1170+
macros inside auto-detected preamble (transitively in regards to the
1171+
includes).
1172+
1173+
* Files used inside preamble must not change between different compilations (at
1174+
least for the same auto-detected preamble).
1175+
1176+
* Auto-generated pre-compiled headers/preambles are stored in memory only. That means:
1177+
- No persistency between invocations
1178+
- Currently there is no eviction mechanism, so application is expected to use
1179+
the option only when number of preambles is limited.
1180+
11301181
=== Known issues and limitations when the language is `sycl`
11311182

11321183
==== Changing the compiler action or output

0 commit comments

Comments
 (0)