llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: Konstantinos Parasyris (koparasy) <details> <summary>Changes</summary> This is mostly an NFC. No change to generated LLVM IR or object code. `#cir.lowering_lang_options` gains openmp_has_targets and sycl_is_host, so `-emit-cir` output changes. This is preliminary work towards reusing code when reading `cir` and emitting its output (#<!-- -->227917) --- Full diff: https://github.com/llvm/llvm-project/pull/229441.diff 5 Files Affected: - (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+9-4) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+2) - (modified) clang/lib/CIR/FrontendAction/CIRGenAction.cpp (+174-137) - (modified) clang/test/CIR/CodeGen/lowering-lang-options.cpp (+10) - (modified) clang/test/CIR/IR/lowering-lang-options.cir (+4-2) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index f6aaac5ab4cab..01574522f128a 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -2416,8 +2416,10 @@ def CIR_LoweringLangOptionsAttr Each bool field mirrors the like-named `LangOptions` member: `exceptions`, `threadsafe_statics`, `cuda`, `cuda_is_device`, `hip`, `gpu_rdc`, - `openmp` (`LangOpts.OpenMP != 0`; only its on/off state matters), and - `openmp_is_target_device`. `clang_abi_compat` is an `i32` mirroring + `openmp` (`LangOpts.OpenMP != 0`; only its on/off state matters), + `openmp_is_target_device`, and `sycl_is_host`. `openmp_has_targets` is + `!LangOpts.OMPTargetTriples.empty()`; only whether OpenMP offload targets + were requested matters, not which ones. `clang_abi_compat` is an `i32` mirroring `LangOptions::ClangABI`; it is an `i32` rather than a CIR enum because `ClangABI` gains an enumerator every Clang release (see `clang/Basic/ABIVersions.def`), and the ordinal round-trips fine within a @@ -2428,7 +2430,8 @@ def CIR_LoweringLangOptionsAttr module attributes {cir.lowering_lang_options = #cir.lowering_lang_options< exceptions = false, threadsafe_statics = true, cuda = false, cuda_is_device = false, hip = false, gpu_rdc = false, openmp = false, - openmp_is_target_device = false, clang_abi_compat = 17>} { } + openmp_is_target_device = false, openmp_has_targets = false, + sycl_is_host = false, clang_abi_compat = 17>} { } ``` }]; @@ -2441,13 +2444,15 @@ def CIR_LoweringLangOptionsAttr "bool":$gpu_rdc, "bool":$openmp, "bool":$openmp_is_target_device, + "bool":$openmp_has_targets, + "bool":$sycl_is_host, "int32_t":$clang_abi_compat ); let assemblyFormat = [{ `<` struct($exceptions, $threadsafe_statics, $cuda, $cuda_is_device, $hip, $gpu_rdc, $openmp, $openmp_is_target_device, - $clang_abi_compat) `>` + $openmp_has_targets, $sycl_is_host, $clang_abi_compat) `>` }]; let canHaveIllegalCXXABIType = 0; diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 5063b6a4cc779..b087184bf9cf7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -185,6 +185,8 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, /*gpu_rdc=*/langOpts.GPURelocatableDeviceCode, /*openmp=*/langOpts.OpenMP != 0, /*openmp_is_target_device=*/langOpts.OpenMPIsTargetDevice, + /*openmp_has_targets=*/!langOpts.OMPTargetTriples.empty(), + /*sycl_is_host=*/langOpts.SYCLIsHost, /*clang_abi_compat=*/ static_cast<int32_t>(langOpts.getClangABICompat()))); diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index 627e659f62ba8..91cd47887f528 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -24,6 +24,7 @@ #include "clang/CodeGen/ModuleLinker.h" #include "clang/CodeGenUtils/BackendDiagnosticHandler.h" #include "clang/Frontend/CompilerInstance.h" +#include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSet.h" @@ -82,6 +83,171 @@ static void printCIRModule(mlir::ModuleOp MLIRModule, raw_ostream &OS) { MLIRModule->print(OS, Flags); } +// TODO: share with BackendConsumer::LinkInModules once the rest of the +// linking logic (not just diagnostics) is unified. +static bool linkInModules(CompilerInstance &CI, llvm::Module &M, + SmallVectorImpl<::clang::LinkModule> &LinkModules, + BackendDiagnosticConsumer &DiagConsumer) { + for (auto &LM : LinkModules) { + assert(LM.Module && "LinkModule does not actually have a module"); + + if (LM.PropagateAttrs) + for (llvm::Function &F : *LM.Module) { + if (F.isIntrinsic()) + continue; + clang::CodeGen::mergeDefaultFunctionDefinitionAttributes( + F, CI.getCodeGenOpts(), CI.getLangOpts(), CI.getTargetOpts(), + LM.Internalize); + } + + DiagConsumer.setCurLinkModule(LM.Module.get()); + bool Err; + if (LM.Internalize) { + Err = llvm::Linker::linkModules( + M, std::move(LM.Module), LM.LinkFlags, + [](llvm::Module &M, const llvm::StringSet<> &GVS) { + llvm::internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) { + return !GV.hasName() || (GVS.count(GV.getName()) == 0); + }); + }); + } else { + Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags); + } + + if (Err) + return true; + } + + LinkModules.clear(); + return false; +} + +// Reads the device binary named by -foffload-include-binary and embeds it +// into the host module. wrapSYCLBinaries also appends the registration ctor +// at priority 101 when no registration-function out-param is supplied. +static void embedSYCLDeviceBinary(CompilerInstance &CI, llvm::Module &M) { + StringRef fileName = CI.getCodeGenOpts().OffloadBinaryToEmbedFile; + auto bufferOrErr = CI.getVirtualFileSystem().getBufferForFile(fileName); + if (std::error_code ec = bufferOrErr.getError()) { + CI.getDiagnostics().Report(diag::err_cannot_open_file) + << fileName << ec.message(); + return; + } + std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(bufferOrErr.get()); + if (llvm::Error err = llvm::offloading::wrapSYCLBinaries( + M, ArrayRef<char>(buffer->getBufferStart(), buffer->getBufferSize()), + llvm::offloading::SYCLJITOptions(), /*IsFinalizedImage=*/true)) { + CI.getDiagnostics().Report(diag::err_fe_error_backend) + << llvm::toString(std::move(err)); + return; + } +} + +// Emits the output requested by \p Action for \p MlirModule, a CIR module that +// has been through the CIR-to-CIR pipeline. Language facts are read from the +// module's #cir.lowering_lang_options and target facts from \p CI, so this +// does not need an ASTContext and can also run on CIR loaded from a file. +// \p AddFunctionSourceLocations, if set, runs on the lowered LLVM module +// before linking. +static void emitCIRModuleOutput( + CIRGenAction::OutputType Action, CompilerInstance &CI, + mlir::ModuleOp MlirModule, llvm::LLVMContext &LLVMCtx, + SmallVectorImpl<::clang::LinkModule> &LinkModules, + BackendDiagnosticConsumer &DiagConsumer, + std::unique_ptr<raw_pwrite_stream> OutputStream, + llvm::function_ref<void(llvm::Module &)> AddFunctionSourceLocations = {}) { + CodeGenOptions &CGO = CI.getCodeGenOpts(); + switch (Action) { + case CIRGenAction::OutputType::EmitCIR: + if (OutputStream && MlirModule) + printCIRModule(MlirModule, *OutputStream); + break; + case CIRGenAction::OutputType::EmitLLVM: + case CIRGenAction::OutputType::EmitBC: + case CIRGenAction::OutputType::EmitObj: + case CIRGenAction::OutputType::EmitAssembly: { + StringRef saveTempsPrefix = CGO.SaveTempsFilePrefix; + std::string cirSaveTempsOutFile, mlirSaveTempsOutFile; + if (!saveTempsPrefix.empty()) { + SmallString<128> stem(saveTempsPrefix); + llvm::sys::path::replace_extension(stem, "cir"); + cirSaveTempsOutFile = std::string(stem); + llvm::sys::path::replace_extension(stem, "mlir"); + mlirSaveTempsOutFile = std::string(stem); + } + + if (!cirSaveTempsOutFile.empty()) { + std::error_code ec; + llvm::raw_fd_ostream out(cirSaveTempsOutFile, ec); + if (!ec) + MlirModule->print(out); + } + + // If errors occurred during codegen, stop before running the backend. + if (CI.getDiagnostics().hasErrorOccurred()) + return; + + // Read the language facts before lowering rewrites the module. Without the + // attribute (e.g. hand-written CIR) every fact defaults to off. + auto LangOptsAttr = MlirModule->getAttrOfType<cir::LoweringLangOptionsAttr>( + cir::CIRDialect::getLoweringLangOptionsAttrName()); + const bool EnableOpenMP = LangOptsAttr && LangOptsAttr.getOpenmp(); + const bool SYCLIsHost = LangOptsAttr && LangOptsAttr.getSyclIsHost(); + // LangOptions::CUDA is also set for HIP. + const bool IsCUDAOrOpenMPOffload = + LangOptsAttr && + (LangOptsAttr.getCuda() || LangOptsAttr.getOpenmpHasTargets()); + + // Route LLVM backend diagnostics (optimization remarks, unsupported + // features, inline-asm errors, etc.) through clang diagnostics for + // the remainder of the LLVM-emitting pipeline. + std::unique_ptr<llvm::DiagnosticHandler> OldDiagnosticHandler = + LLVMCtx.getDiagnosticHandler(); + llvm::scope_exit RestoreDiagnosticHandler([&]() { + LLVMCtx.setDiagnosticHandler(std::move(OldDiagnosticHandler)); + }); + LLVMCtx.setDiagnosticHandler(DiagConsumer.createDiagnosticHandler()); + + std::unique_ptr<llvm::Module> LLVMModule = + lowerFromCIRToLLVMIR(MlirModule, LLVMCtx, EnableOpenMP, + mlirSaveTempsOutFile, &CI.getVirtualFileSystem()); + + LLVMModule->setDataLayout(CI.getTarget().getDataLayoutString()); + + if (AddFunctionSourceLocations) + AddFunctionSourceLocations(*LLVMModule); + + if (linkInModules(CI, *LLVMModule, LinkModules, DiagConsumer)) + return; + + // Embed the offloaded SYCL device binary into the host module. + if (SYCLIsHost && !CGO.OffloadBinaryToEmbedFile.empty()) + embedSYCLDeviceBinary(CI, *LLVMModule); + + // CUDA, HIP and OpenMP offloading rely on host-side offload entries that + // are not emitted on the ClangIR path yet, so embedding their device + // objects would produce a host object that cannot be registered. + if (!CGO.OffloadObjects.empty() && IsCUDAOrOpenMPOffload) { + DiagnosticsEngine &Diags = CI.getDiagnostics(); + Diags.Report(Diags.getCustomDiagID( + DiagnosticsEngine::Error, + "ClangIR code gen Not Yet Implemented: embedding offload objects " + "for CUDA, HIP or OpenMP offloading")); + return; + } + + // If there is device offloading code embed it in the host now. + EmbedObject(LLVMModule.get(), CGO, CI.getVirtualFileSystem(), + CI.getDiagnostics()); + + BackendAction BEAction = getBackendActionFromOutputType(Action); + emitBackendOutput(CI, CGO, LLVMModule.get(), BEAction, + CI.getVirtualFileSystemPtr(), std::move(OutputStream)); + break; + } + } +} + class CIRGenConsumer : public clang::ASTConsumer { virtual void anchor(); @@ -189,146 +355,17 @@ class CIRGenConsumer : public clang::ASTConsumer { } } - switch (Action) { - case CIRGenAction::OutputType::EmitCIR: - if (OutputStream && MlirModule) - printCIRModule(MlirModule, *OutputStream); - break; - case CIRGenAction::OutputType::EmitLLVM: - case CIRGenAction::OutputType::EmitBC: - case CIRGenAction::OutputType::EmitObj: - case CIRGenAction::OutputType::EmitAssembly: { - StringRef saveTempsPrefix = CGO.SaveTempsFilePrefix; - std::string cirSaveTempsOutFile, mlirSaveTempsOutFile; - if (!saveTempsPrefix.empty()) { - SmallString<128> stem(saveTempsPrefix); - llvm::sys::path::replace_extension(stem, "cir"); - cirSaveTempsOutFile = std::string(stem); - llvm::sys::path::replace_extension(stem, "mlir"); - mlirSaveTempsOutFile = std::string(stem); - } - - if (!cirSaveTempsOutFile.empty()) { - std::error_code ec; - llvm::raw_fd_ostream out(cirSaveTempsOutFile, ec); - if (!ec) - MlirModule->print(out); - } - - // If errors occurred during codegen, stop before running the backend. - if (CI.getDiagnostics().hasErrorOccurred()) - return; - - // Route LLVM backend diagnostics (optimization remarks, unsupported - // features, inline-asm errors, etc.) through clang diagnostics for - // the remainder of the LLVM-emitting pipeline. - std::unique_ptr<llvm::DiagnosticHandler> OldDiagnosticHandler = - LLVMCtx.getDiagnosticHandler(); - llvm::scope_exit RestoreDiagnosticHandler([&]() { - LLVMCtx.setDiagnosticHandler(std::move(OldDiagnosticHandler)); - }); - LLVMCtx.setDiagnosticHandler(DiagConsumer.createDiagnosticHandler()); - - std::unique_ptr<llvm::Module> LLVMModule = lowerFromCIRToLLVMIR( - MlirModule, LLVMCtx, C.getLangOpts().OpenMP, mlirSaveTempsOutFile, - &CI.getVirtualFileSystem()); - - LLVMModule->setDataLayout(C.getTargetInfo().getDataLayoutString()); - - for (llvm::Function &F : LLVMModule->functions()) + // CIRGen-only step: map the emitted LLVM functions back to their source + // declarations so backend diagnostics point at the original source. + auto AddFunctionSourceLocations = [&](llvm::Module &LLVMModule) { + for (llvm::Function &F : LLVMModule.functions()) if (const Decl *FD = Gen->getDeclForMangledName(F.getName())) DiagConsumer.addFunctionSourceLocation( F.getName(), FD->getASTContext().getFullLoc(FD->getLocation())); - - if (linkInModules(*LLVMModule)) - return; - - // Embed the offloaded SYCL device binary into the host module. - if (C.getLangOpts().SYCLIsHost && !CGO.OffloadBinaryToEmbedFile.empty()) - embedSYCLDeviceBinary(*LLVMModule); - - // CUDA, HIP and OpenMP offloading rely on host-side offload entries that - // are not emitted on the ClangIR path yet, so embedding their device - // objects would produce a host object that cannot be registered. - const LangOptions &LangOpts = C.getLangOpts(); - if (!CGO.OffloadObjects.empty() && - (LangOpts.CUDA || !LangOpts.OMPTargetTriples.empty())) { - DiagnosticsEngine &Diags = CI.getDiagnostics(); - Diags.Report(Diags.getCustomDiagID( - DiagnosticsEngine::Error, - "ClangIR code gen Not Yet Implemented: embedding offload objects " - "for CUDA, HIP or OpenMP offloading")); - return; - } - - // If there is device offloading code embed it in the host now. - EmbedObject(LLVMModule.get(), CGO, CI.getVirtualFileSystem(), - CI.getDiagnostics()); - - BackendAction BEAction = getBackendActionFromOutputType(Action); - emitBackendOutput(CI, CI.getCodeGenOpts(), LLVMModule.get(), BEAction, FS, - std::move(OutputStream)); - break; - } - } - } - - // TODO: share with BackendConsumer::LinkInModules once the rest of the - // linking logic (not just diagnostics) is unified. - bool linkInModules(llvm::Module &M) { - for (auto &LM : LinkModules) { - assert(LM.Module && "LinkModule does not actually have a module"); - - if (LM.PropagateAttrs) - for (llvm::Function &F : *LM.Module) { - if (F.isIntrinsic()) - continue; - clang::CodeGen::mergeDefaultFunctionDefinitionAttributes( - F, CGO, CI.getLangOpts(), CI.getTargetOpts(), LM.Internalize); - } - - DiagConsumer.setCurLinkModule(LM.Module.get()); - bool Err; - if (LM.Internalize) { - Err = llvm::Linker::linkModules( - M, std::move(LM.Module), LM.LinkFlags, - [](llvm::Module &M, const llvm::StringSet<> &GVS) { - llvm::internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) { - return !GV.hasName() || (GVS.count(GV.getName()) == 0); - }); - }); - } else { - Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags); - } - - if (Err) - return true; - } - - LinkModules.clear(); - return false; - } - - // Reads the device binary named by -foffload-include-binary and embeds it - // into the host module. wrapSYCLBinaries also appends the registration ctor - // at priority 101 when no registration-function out-param is supplied. - void embedSYCLDeviceBinary(llvm::Module &M) { - StringRef fileName = CGO.OffloadBinaryToEmbedFile; - auto bufferOrErr = CI.getVirtualFileSystem().getBufferForFile(fileName); - if (std::error_code ec = bufferOrErr.getError()) { - CI.getDiagnostics().Report(diag::err_cannot_open_file) - << fileName << ec.message(); - return; - } - std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(bufferOrErr.get()); - if (llvm::Error err = llvm::offloading::wrapSYCLBinaries( - M, - ArrayRef<char>(buffer->getBufferStart(), buffer->getBufferSize()), - llvm::offloading::SYCLJITOptions(), /*IsFinalizedImage=*/true)) { - CI.getDiagnostics().Report(diag::err_fe_error_backend) - << llvm::toString(std::move(err)); - return; - } + }; + emitCIRModuleOutput(Action, CI, MlirModule, LLVMCtx, LinkModules, + DiagConsumer, std::move(OutputStream), + AddFunctionSourceLocations); } void HandleTagDeclDefinition(TagDecl *D) override { diff --git a/clang/test/CIR/CodeGen/lowering-lang-options.cpp b/clang/test/CIR/CodeGen/lowering-lang-options.cpp index 196577428208a..f1c3683068b87 100644 --- a/clang/test/CIR/CodeGen/lowering-lang-options.cpp +++ b/clang/test/CIR/CodeGen/lowering-lang-options.cpp @@ -3,6 +3,8 @@ // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir \ // RUN: -fexceptions -fcxx-exceptions -emit-cir %s -o - \ // RUN: | FileCheck %s --check-prefix=EXC +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -fsycl-is-host -emit-cir %s -o - | FileCheck %s --check-prefix=SYCL // CIRGen serializes the LangOptions facts that post-CIRGen lowering consumes // onto the ModuleOp as a #cir.lowering_lang_options attribute, unconditionally, so @@ -23,6 +25,8 @@ int x; // PLAIN-SAME: gpu_rdc = false // PLAIN-SAME: openmp = false // PLAIN-SAME: openmp_is_target_device = false +// PLAIN-SAME: openmp_has_targets = false +// PLAIN-SAME: sycl_is_host = false // PLAIN-SAME: clang_abi_compat = {{[0-9]+}}> // -fexceptions -fcxx-exceptions flips exceptions. (OpenMP is exercised in the @@ -34,3 +38,9 @@ int x; // EXC-SAME: openmp = false // EXC-SAME: openmp_is_target_device = false // EXC-SAME: clang_abi_compat = {{[0-9]+}}> + +// -fsycl-is-host flips sycl_is_host, which gates embedding the SYCL device +// binary into the host object. +// SYCL: cir.lowering_lang_options = #cir.lowering_lang_options< +// SYCL-SAME: openmp_has_targets = false +// SYCL-SAME: sycl_is_host = true diff --git a/clang/test/CIR/IR/lowering-lang-options.cir b/clang/test/CIR/IR/lowering-lang-options.cir index 2ef90bd94ed4e..608aad4ab0097 100644 --- a/clang/test/CIR/IR/lowering-lang-options.cir +++ b/clang/test/CIR/IR/lowering-lang-options.cir @@ -7,10 +7,12 @@ module attributes {cir.lowering_lang_options = #cir.lowering_lang_options< exceptions = true, threadsafe_statics = false, cuda = true, cuda_is_device = true, hip = true, gpu_rdc = true, openmp = true, - openmp_is_target_device = true, clang_abi_compat = 9>} { + openmp_is_target_device = true, openmp_has_targets = true, + sycl_is_host = true, clang_abi_compat = 9>} { } // CHECK: cir.lowering_lang_options = #cir.lowering_lang_options< // CHECK-SAME: exceptions = true, threadsafe_statics = false, cuda = true, // CHECK-SAME: cuda_is_device = true, hip = true, gpu_rdc = true, openmp = true, -// CHECK-SAME: openmp_is_target_device = true, clang_abi_compat = 9> +// CHECK-SAME: openmp_is_target_device = true, openmp_has_targets = true, +// CHECK-SAME: sycl_is_host = true, clang_abi_compat = 9> `````````` </details> https://github.com/llvm/llvm-project/pull/229441 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
