1313#include < clang/CodeGen/CodeGenAction.h>
1414#include < clang/Driver/Compilation.h>
1515#include < clang/Driver/Options.h>
16+ #include < clang/Frontend/ChainedDiagnosticConsumer.h>
1617#include < clang/Frontend/CompilerInstance.h>
1718#include < clang/Frontend/TextDiagnosticBuffer.h>
19+ #include < clang/Frontend/TextDiagnosticPrinter.h>
1820#include < clang/Tooling/CompilationDatabase.h>
1921#include < clang/Tooling/Tooling.h>
2022
23+ #include < llvm/IR/DiagnosticInfo.h>
24+ #include < llvm/IR/DiagnosticPrinter.h>
2125#include < llvm/IR/PassInstrumentation.h>
2226#include < llvm/IR/PassManager.h>
2327#include < llvm/IRReader/IRReader.h>
2731#include < llvm/SYCLLowerIR/SYCLJointMatrixTransform.h>
2832#include < llvm/Support/PropertySetIO.h>
2933
34+ #include < algorithm>
35+ #include < array>
36+ #include < sstream>
37+
3038using namespace clang ;
3139using namespace clang ::tooling;
3240using namespace clang ::driver;
@@ -132,6 +140,9 @@ struct GetLLVMModuleAction : public ToolAction {
132140 CompilerInstance Compiler (std::move (PCHContainerOps));
133141 Compiler.setInvocation (std::move (Invocation));
134142 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>());
135146
136147 // Create the compiler's actual diagnostics engine.
137148 Compiler.createDiagnostics (DiagConsumer, /* ShouldOwnClient=*/ false );
@@ -161,12 +172,56 @@ struct GetLLVMModuleAction : public ToolAction {
161172 std::unique_ptr<llvm::Module> Module;
162173};
163174
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+
185+ LogPrinter = std::make_unique<TextDiagnosticPrinter>(LogStream, DiagOpts);
186+ }
187+
188+ clang::TextDiagnosticPrinter *consumer () { return LogPrinter.get (); }
189+
190+ llvm::raw_ostream &stream () { return LogStream; }
191+ };
192+
193+ class LLVMDiagnosticWrapper : public llvm ::DiagnosticHandler {
194+ llvm::raw_string_ostream LogStream;
195+
196+ DiagnosticPrinterRawOStream LogPrinter;
197+
198+ public:
199+ LLVMDiagnosticWrapper (std::string &BuildLog)
200+ : LogStream(BuildLog), LogPrinter(LogStream) {}
201+
202+ bool handleDiagnostics (const DiagnosticInfo &DI) override {
203+ auto Prefix = [](DiagnosticSeverity Severity) -> llvm::StringLiteral {
204+ switch (Severity) {
205+ case llvm::DiagnosticSeverity::DS_Error:
206+ return " ERROR" ;
207+ case llvm::DiagnosticSeverity::DS_Warning:
208+ return " WARNING" ;
209+ default :
210+ return " NOTE:" ;
211+ }
212+ }(DI.getSeverity ());
213+ LogPrinter << Prefix;
214+ DI.print (LogPrinter);
215+ LogPrinter << " \n " ;
216+ return true ;
217+ }
218+ };
219+
164220} // anonymous namespace
165221
166- Expected<std::unique_ptr<llvm::Module>>
167- jit_compiler::compileDeviceCode (InMemoryFile SourceFile,
168- View<InMemoryFile> IncludeFiles,
169- const InputArgList &UserArgList) {
222+ Expected<std::unique_ptr<llvm::Module>> jit_compiler::compileDeviceCode (
223+ InMemoryFile SourceFile, View<InMemoryFile> IncludeFiles,
224+ const InputArgList &UserArgList, std::string &BuildLog) {
170225 const std::string &DPCPPRoot = getDPCPPRoot ();
171226 if (DPCPPRoot == InvalidDPCPPRoot) {
172227 return createStringError (" Could not locate DPCPP root directory" );
@@ -197,12 +252,19 @@ jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
197252 FixedCompilationDatabase DB{" ." , CommandLine};
198253 ClangTool Tool{DB, {SourceFile.Path }};
199254
255+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts{new DiagnosticOptions};
256+ ClangDiagnosticWrapper Wrapper (BuildLog, DiagOpts.get ());
257+ Tool.setDiagnosticConsumer (Wrapper.consumer ());
258+
200259 // Set up in-memory filesystem.
201260 Tool.mapVirtualFile (SourceFile.Path , SourceFile.Contents );
202261 for (const auto &IF : IncludeFiles) {
203262 Tool.mapVirtualFile (IF.Path , IF.Contents );
204263 }
205264
265+ // Suppress message "Error while processing" being printed to stdout.
266+ Tool.setPrintErrorMessage (false );
267+
206268 // Reset argument adjusters to drop the `-fsyntax-only` flag which is added by
207269 // default by this API.
208270 Tool.clearArgumentsAdjusters ();
@@ -222,15 +284,15 @@ jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
222284 return std::move (Action.Module );
223285 }
224286
225- // TODO: Capture compiler errors from the ClangTool.
226- return createStringError (" Unable to obtain LLVM module" );
287+ return createStringError (BuildLog);
227288}
228289
229290// This function is a simplified copy of the device library selection process in
230291// `clang::driver::tools::SYCL::getDeviceLibraries`, assuming a SPIR-V target
231292// (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) {
293+ static bool getDeviceLibraries (const ArgList &Args,
294+ SmallVectorImpl<std::string> &LibraryList,
295+ DiagnosticsEngine &Diags) {
234296 struct DeviceLibOptInfo {
235297 StringRef DeviceLibName;
236298 StringRef DeviceLibOption;
@@ -247,6 +309,8 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
247309 // libraries cannot be affected via -fno-sycl-device-lib.
248310 bool ExcludeDeviceLibs = false ;
249311
312+ bool FoundUnknownLib = false ;
313+
250314 if (Arg *A = Args.getLastArg (OPT_fsycl_device_lib_EQ,
251315 OPT_fno_sycl_device_lib_EQ)) {
252316 if (A->getValues ().size () == 0 ) {
@@ -268,6 +332,7 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
268332 if (LinkInfoIter == DeviceLibLinkInfo.end () || Val == " internal" ) {
269333 Diags.Report (diag::err_drv_unsupported_option_argument)
270334 << A->getSpelling () << Val;
335+ FoundUnknownLib = true ;
271336 }
272337 DeviceLibLinkInfo[Val] = !ExcludeDeviceLibs;
273338 }
@@ -292,7 +357,6 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
292357 {" libsycl-itt-compiler-wrappers" , " internal" },
293358 {" libsycl-itt-stubs" , " internal" }};
294359
295- SmallVector<std::string, 8 > LibraryList;
296360 StringRef LibSuffix = " .bc" ;
297361 auto AddLibraries = [&](const SYCLDeviceLibsList &LibsList) {
298362 for (const DeviceLibOptInfo &Lib : LibsList) {
@@ -312,37 +376,33 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
312376 AddLibraries (SYCLDeviceAnnotationLibs);
313377 }
314378
315- return LibraryList ;
379+ return FoundUnknownLib ;
316380}
317381
318382Error jit_compiler::linkDeviceLibraries (llvm::Module &Module,
319- const InputArgList &UserArgList) {
383+ const InputArgList &UserArgList,
384+ std::string &BuildLog) {
320385 const std::string &DPCPPRoot = getDPCPPRoot ();
321386 if (DPCPPRoot == InvalidDPCPPRoot) {
322387 return createStringError (" Could not locate DPCPP root directory" );
323388 }
324389
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.
328390 IntrusiveRefCntPtr<DiagnosticIDs> DiagID{new DiagnosticIDs};
329391 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 ' ; });
392+ ClangDiagnosticWrapper Wrapper (BuildLog, DiagOpts.get ());
393+ DiagnosticsEngine Diags (DiagID, DiagOpts, Wrapper.consumer (),
394+ /* ShouldOwnClient=*/ false );
395+
396+ SmallVector<std::string> LibNames;
397+ bool FoundUnknownLib = getDeviceLibraries (UserArgList, LibNames, Diags);
398+ if (FoundUnknownLib) {
340399 return createStringError (" Could not determine list of device libraries: %s" ,
341- DiagMsg .c_str ());
400+ BuildLog .c_str ());
342401 }
343- // TODO: Add warnings to build log.
344402
345403 LLVMContext &Context = Module.getContext ();
404+ Context.setDiagnosticHandler (
405+ std::make_unique<LLVMDiagnosticWrapper>(BuildLog));
346406 for (const std::string &LibName : LibNames) {
347407 std::string LibPath = DPCPPRoot + " /lib/" + LibName;
348408
@@ -356,10 +416,8 @@ Error jit_compiler::linkDeviceLibraries(llvm::Module &Module,
356416 }
357417
358418 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 ());
419+ return createStringError (" Unable to link device library %s: %s" ,
420+ LibPath.c_str (), BuildLog.c_str ());
363421 }
364422 }
365423
0 commit comments