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,59 @@ 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+ 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+
164223} // anonymous namespace
165224
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) {
170228 const std::string &DPCPPRoot = getDPCPPRoot ();
171229 if (DPCPPRoot == InvalidDPCPPRoot) {
172230 return createStringError (" Could not locate DPCPP root directory" );
@@ -197,6 +255,12 @@ jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
197255 FixedCompilationDatabase DB{" ." , CommandLine};
198256 ClangTool Tool{DB, {SourceFile.Path }};
199257
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+
200264 // Set up in-memory filesystem.
201265 Tool.mapVirtualFile (SourceFile.Path , SourceFile.Contents );
202266 for (const auto &IF : IncludeFiles) {
@@ -222,15 +286,15 @@ jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
222286 return std::move (Action.Module );
223287 }
224288
225- // TODO: Capture compiler errors from the ClangTool.
226- return createStringError (" Unable to obtain LLVM module" );
289+ return createStringError (BuildLog);
227290}
228291
229292// This function is a simplified copy of the device library selection process in
230293// `clang::driver::tools::SYCL::getDeviceLibraries`, assuming a SPIR-V target
231294// (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) {
234298 struct DeviceLibOptInfo {
235299 StringRef DeviceLibName;
236300 StringRef DeviceLibOption;
@@ -247,6 +311,8 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
247311 // libraries cannot be affected via -fno-sycl-device-lib.
248312 bool ExcludeDeviceLibs = false ;
249313
314+ bool FoundUnknownLib = false ;
315+
250316 if (Arg *A = Args.getLastArg (OPT_fsycl_device_lib_EQ,
251317 OPT_fno_sycl_device_lib_EQ)) {
252318 if (A->getValues ().size () == 0 ) {
@@ -268,6 +334,7 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
268334 if (LinkInfoIter == DeviceLibLinkInfo.end () || Val == " internal" ) {
269335 Diags.Report (diag::err_drv_unsupported_option_argument)
270336 << A->getSpelling () << Val;
337+ FoundUnknownLib = true ;
271338 }
272339 DeviceLibLinkInfo[Val] = !ExcludeDeviceLibs;
273340 }
@@ -292,7 +359,6 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
292359 {" libsycl-itt-compiler-wrappers" , " internal" },
293360 {" libsycl-itt-stubs" , " internal" }};
294361
295- SmallVector<std::string, 8 > LibraryList;
296362 StringRef LibSuffix = " .bc" ;
297363 auto AddLibraries = [&](const SYCLDeviceLibsList &LibsList) {
298364 for (const DeviceLibOptInfo &Lib : LibsList) {
@@ -312,37 +378,33 @@ getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
312378 AddLibraries (SYCLDeviceAnnotationLibs);
313379 }
314380
315- return LibraryList ;
381+ return FoundUnknownLib ;
316382}
317383
318384Error jit_compiler::linkDeviceLibraries (llvm::Module &Module,
319- const InputArgList &UserArgList) {
385+ const InputArgList &UserArgList,
386+ std::string &BuildLog) {
320387 const std::string &DPCPPRoot = getDPCPPRoot ();
321388 if (DPCPPRoot == InvalidDPCPPRoot) {
322389 return createStringError (" Could not locate DPCPP root directory" );
323390 }
324391
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.
328392 IntrusiveRefCntPtr<DiagnosticIDs> DiagID{new DiagnosticIDs};
329393 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) {
340401 return createStringError (" Could not determine list of device libraries: %s" ,
341- DiagMsg .c_str ());
402+ BuildLog .c_str ());
342403 }
343- // TODO: Add warnings to build log.
344404
345405 LLVMContext &Context = Module.getContext ();
406+ Context.setDiagnosticHandler (
407+ std::make_unique<LLVMDiagnosticWrapper>(BuildLog));
346408 for (const std::string &LibName : LibNames) {
347409 std::string LibPath = DPCPPRoot + " /lib/" + LibName;
348410
@@ -356,10 +418,8 @@ Error jit_compiler::linkDeviceLibraries(llvm::Module &Module,
356418 }
357419
358420 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 ());
363423 }
364424 }
365425
0 commit comments