13
13
#include < clang/CodeGen/CodeGenAction.h>
14
14
#include < clang/Driver/Compilation.h>
15
15
#include < clang/Driver/Options.h>
16
+ #include < clang/Frontend/ChainedDiagnosticConsumer.h>
16
17
#include < clang/Frontend/CompilerInstance.h>
17
18
#include < clang/Frontend/TextDiagnosticBuffer.h>
19
+ #include < clang/Frontend/TextDiagnosticPrinter.h>
18
20
#include < clang/Tooling/CompilationDatabase.h>
19
21
#include < clang/Tooling/Tooling.h>
20
22
23
+ #include < llvm/IR/DiagnosticInfo.h>
24
+ #include < llvm/IR/DiagnosticPrinter.h>
21
25
#include < llvm/IR/PassInstrumentation.h>
22
26
#include < llvm/IR/PassManager.h>
23
27
#include < llvm/IRReader/IRReader.h>
27
31
#include < llvm/SYCLLowerIR/SYCLJointMatrixTransform.h>
28
32
#include < llvm/Support/PropertySetIO.h>
29
33
34
+ #include < algorithm>
35
+ #include < array>
36
+ #include < sstream>
37
+
30
38
using namespace clang ;
31
39
using namespace clang ::tooling;
32
40
using namespace clang ::driver;
@@ -132,6 +140,9 @@ struct GetLLVMModuleAction : public ToolAction {
132
140
CompilerInstance Compiler (std::move (PCHContainerOps));
133
141
Compiler.setInvocation (std::move (Invocation));
134
142
Compiler.setFileManager (Files);
143
+ // Suppress summary with number of warnings and errors being printed to
144
+ // stdout.
145
+ Compiler.setVerboseOutputStream (std::make_unique<llvm::raw_null_ostream>());
135
146
136
147
// Create the compiler's actual diagnostics engine.
137
148
Compiler.createDiagnostics (DiagConsumer, /* ShouldOwnClient=*/ false );
@@ -161,12 +172,59 @@ struct GetLLVMModuleAction : public ToolAction {
161
172
std::unique_ptr<llvm::Module> Module;
162
173
};
163
174
175
+ class ClangDiagnosticWrapper {
176
+
177
+ llvm::raw_string_ostream LogStream;
178
+
179
+ std::unique_ptr<clang::TextDiagnosticPrinter> LogPrinter;
180
+
181
+ public:
182
+ ClangDiagnosticWrapper (std::string &LogString, DiagnosticOptions *DiagOpts)
183
+ : LogStream(LogString),
184
+ LogPrinter (
185
+ std::make_unique<TextDiagnosticPrinter>(LogStream, DiagOpts)) {}
186
+
187
+ clang::TextDiagnosticPrinter *consumer () { return LogPrinter.get (); }
188
+
189
+ llvm::raw_ostream &stream () { return LogStream; }
190
+ };
191
+
192
+ class LLVMDiagnosticWrapper : public llvm ::DiagnosticHandler {
193
+ llvm::raw_string_ostream LogStream;
194
+
195
+ DiagnosticPrinterRawOStream LogPrinter;
196
+
197
+ public:
198
+ LLVMDiagnosticWrapper (std::string &BuildLog)
199
+ : LogStream(BuildLog), LogPrinter(LogStream) {}
200
+
201
+ bool handleDiagnostics (const DiagnosticInfo &DI) override {
202
+ auto Prefix = [](DiagnosticSeverity Severity) -> llvm::StringLiteral {
203
+ switch (Severity) {
204
+ case llvm::DiagnosticSeverity::DS_Error:
205
+ return " ERROR" ;
206
+ case llvm::DiagnosticSeverity::DS_Warning:
207
+ return " WARNING" ;
208
+ case llvm::DiagnosticSeverity::DS_Note:
209
+ return " NOTE:" ;
210
+ case llvm::DiagnosticSeverity::DS_Remark:
211
+ return " REMARK:" ;
212
+ default :
213
+ llvm_unreachable (" Unhandled case" );
214
+ }
215
+ }(DI.getSeverity ());
216
+ LogPrinter << Prefix;
217
+ DI.print (LogPrinter);
218
+ LogPrinter << " \n " ;
219
+ return true ;
220
+ }
221
+ };
222
+
164
223
} // anonymous namespace
165
224
166
- Expected<std::unique_ptr<llvm::Module>>
167
- jit_compiler::compileDeviceCode (InMemoryFile SourceFile,
168
- View<InMemoryFile> IncludeFiles,
169
- const InputArgList &UserArgList) {
225
+ Expected<std::unique_ptr<llvm::Module>> jit_compiler::compileDeviceCode (
226
+ InMemoryFile SourceFile, View<InMemoryFile> IncludeFiles,
227
+ const InputArgList &UserArgList, std::string &BuildLog) {
170
228
const std::string &DPCPPRoot = getDPCPPRoot ();
171
229
if (DPCPPRoot == InvalidDPCPPRoot) {
172
230
return createStringError (" Could not locate DPCPP root directory" );
@@ -197,6 +255,12 @@ jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
197
255
FixedCompilationDatabase DB{" ." , CommandLine};
198
256
ClangTool Tool{DB, {SourceFile.Path }};
199
257
258
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts{new DiagnosticOptions};
259
+ ClangDiagnosticWrapper Wrapper (BuildLog, DiagOpts.get ());
260
+ Tool.setDiagnosticConsumer (Wrapper.consumer ());
261
+ // Suppress message "Error while processing" being printed to stdout.
262
+ Tool.setPrintErrorMessage (false );
263
+
200
264
// Set up in-memory filesystem.
201
265
Tool.mapVirtualFile (SourceFile.Path , SourceFile.Contents );
202
266
for (const auto &IF : IncludeFiles) {
@@ -222,15 +286,15 @@ jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
222
286
return std::move (Action.Module );
223
287
}
224
288
225
- // TODO: Capture compiler errors from the ClangTool.
226
- return createStringError (" Unable to obtain LLVM module" );
289
+ return createStringError (BuildLog);
227
290
}
228
291
229
292
// This function is a simplified copy of the device library selection process in
230
293
// `clang::driver::tools::SYCL::getDeviceLibraries`, assuming a SPIR-V target
231
294
// (no AoT, no third-party GPUs, no native CPU). Keep in sync!
232
- static SmallVector<std::string, 8 >
233
- getDeviceLibraries (const ArgList &Args, DiagnosticsEngine &Diags) {
295
+ static bool getDeviceLibraries (const ArgList &Args,
296
+ SmallVectorImpl<std::string> &LibraryList,
297
+ DiagnosticsEngine &Diags) {
234
298
struct DeviceLibOptInfo {
235
299
StringRef DeviceLibName;
236
300
StringRef DeviceLibOption;
@@ -247,6 +311,8 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
247
311
// libraries cannot be affected via -fno-sycl-device-lib.
248
312
bool ExcludeDeviceLibs = false ;
249
313
314
+ bool FoundUnknownLib = false ;
315
+
250
316
if (Arg *A = Args.getLastArg (OPT_fsycl_device_lib_EQ,
251
317
OPT_fno_sycl_device_lib_EQ)) {
252
318
if (A->getValues ().size () == 0 ) {
@@ -268,6 +334,7 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
268
334
if (LinkInfoIter == DeviceLibLinkInfo.end () || Val == " internal" ) {
269
335
Diags.Report (diag::err_drv_unsupported_option_argument)
270
336
<< A->getSpelling () << Val;
337
+ FoundUnknownLib = true ;
271
338
}
272
339
DeviceLibLinkInfo[Val] = !ExcludeDeviceLibs;
273
340
}
@@ -292,7 +359,6 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
292
359
{" libsycl-itt-compiler-wrappers" , " internal" },
293
360
{" libsycl-itt-stubs" , " internal" }};
294
361
295
- SmallVector<std::string, 8 > LibraryList;
296
362
StringRef LibSuffix = " .bc" ;
297
363
auto AddLibraries = [&](const SYCLDeviceLibsList &LibsList) {
298
364
for (const DeviceLibOptInfo &Lib : LibsList) {
@@ -312,37 +378,33 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
312
378
AddLibraries (SYCLDeviceAnnotationLibs);
313
379
}
314
380
315
- return LibraryList ;
381
+ return FoundUnknownLib ;
316
382
}
317
383
318
384
Error jit_compiler::linkDeviceLibraries (llvm::Module &Module,
319
- const InputArgList &UserArgList) {
385
+ const InputArgList &UserArgList,
386
+ std::string &BuildLog) {
320
387
const std::string &DPCPPRoot = getDPCPPRoot ();
321
388
if (DPCPPRoot == InvalidDPCPPRoot) {
322
389
return createStringError (" Could not locate DPCPP root directory" );
323
390
}
324
391
325
- // TODO: Seems a bit excessive to set up this machinery for one warning and
326
- // one error. Rethink when implementing the build log/error reporting as
327
- // mandated by the extension.
328
392
IntrusiveRefCntPtr<DiagnosticIDs> DiagID{new DiagnosticIDs};
329
393
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts{new DiagnosticOptions};
330
- TextDiagnosticBuffer *DiagBuffer = new TextDiagnosticBuffer;
331
- DiagnosticsEngine Diags (DiagID, DiagOpts, DiagBuffer);
332
-
333
- auto LibNames = getDeviceLibraries (UserArgList, Diags);
334
- if (std::distance (DiagBuffer->err_begin (), DiagBuffer->err_end ()) > 0 ) {
335
- std::string DiagMsg;
336
- raw_string_ostream SOS{DiagMsg};
337
- interleave (
338
- DiagBuffer->err_begin (), DiagBuffer->err_end (),
339
- [&](const auto &D) { SOS << D.second ; }, [&]() { SOS << ' \n ' ; });
394
+ ClangDiagnosticWrapper Wrapper (BuildLog, DiagOpts.get ());
395
+ DiagnosticsEngine Diags (DiagID, DiagOpts, Wrapper.consumer (),
396
+ /* ShouldOwnClient=*/ false );
397
+
398
+ SmallVector<std::string> LibNames;
399
+ bool FoundUnknownLib = getDeviceLibraries (UserArgList, LibNames, Diags);
400
+ if (FoundUnknownLib) {
340
401
return createStringError (" Could not determine list of device libraries: %s" ,
341
- DiagMsg .c_str ());
402
+ BuildLog .c_str ());
342
403
}
343
- // TODO: Add warnings to build log.
344
404
345
405
LLVMContext &Context = Module.getContext ();
406
+ Context.setDiagnosticHandler (
407
+ std::make_unique<LLVMDiagnosticWrapper>(BuildLog));
346
408
for (const std::string &LibName : LibNames) {
347
409
std::string LibPath = DPCPPRoot + " /lib/" + LibName;
348
410
@@ -356,10 +418,8 @@ Error jit_compiler::linkDeviceLibraries(llvm::Module &Module,
356
418
}
357
419
358
420
if (Linker::linkModules (Module, std::move (Lib), Linker::LinkOnlyNeeded)) {
359
- // TODO: Obtain detailed error message from the context's diagnostics
360
- // handler.
361
- return createStringError (" Unable to link device library: %s" ,
362
- LibPath.c_str ());
421
+ return createStringError (" Unable to link device library %s: %s" ,
422
+ LibPath.c_str (), BuildLog.c_str ());
363
423
}
364
424
}
365
425
0 commit comments