clang 24.0.0git
CIRGenAction.cpp
Go to the documentation of this file.
1//===--- CIRGenAction.cpp - LLVM Code generation Frontend Action ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "mlir/IR/MLIRContext.h"
12#include "mlir/IR/OwningOpRef.h"
23#include "llvm/ADT/ScopeExit.h"
24#include "llvm/ADT/SmallString.h"
25#include "llvm/ADT/StringSet.h"
26#include "llvm/Frontend/Offloading/OffloadWrapper.h"
27#include "llvm/IR/DiagnosticHandler.h"
28#include "llvm/IR/Function.h"
29#include "llvm/IR/GlobalValue.h"
30#include "llvm/IR/LLVMContext.h"
31#include "llvm/IR/Module.h"
32#include "llvm/Linker/Linker.h"
33#include "llvm/Support/MemoryBuffer.h"
34#include "llvm/Support/Path.h"
35#include "llvm/Support/raw_ostream.h"
36#include "llvm/Transforms/IPO/Internalize.h"
37
38using namespace cir;
39using namespace clang;
40
41namespace cir {
42
43static BackendAction
45 switch (Action) {
47 assert(false &&
48 "Unsupported output type for getBackendActionFromOutputType!");
49 break; // Unreachable, but fall through to report that
58 }
59 // We should only get here if a non-enum value is passed in or we went through
60 // the assert(false) case above
61 llvm_unreachable("Unsupported output type!");
62}
63
64static std::unique_ptr<llvm::Module>
65lowerFromCIRToLLVMIR(mlir::ModuleOp MLIRModule, llvm::LLVMContext &LLVMCtx,
66 bool EnableOpenMP,
67 llvm::StringRef mlirSaveTempsOutFile = {},
68 llvm::vfs::FileSystem *fs = nullptr) {
69 return direct::lowerDirectlyFromCIRToLLVMIR(MLIRModule, LLVMCtx, EnableOpenMP,
70 mlirSaveTempsOutFile, fs);
71}
72
74
75 virtual void anchor();
76
78
80
81 std::unique_ptr<raw_pwrite_stream> OutputStream;
82
83 ASTContext *Context{nullptr};
85 std::unique_ptr<CIRGenerator> Gen;
86 const FrontendOptions &FEOptions;
87 CodeGenOptions &CGO;
88
89 llvm::LLVMContext &LLVMCtx;
91
92 std::optional<CIRDiagnosticHandler> MLIRDiagHandler;
93
94 // Translates LLVM backend diagnostics (raised while lowering CIR to LLVM
95 // IR and while running emitBackendOutput) into clang diagnostics; shared
96 // with classic CodeGen's BackendConsumer.
97 BackendDiagnosticConsumer DiagConsumer;
98
99public:
101 CodeGenOptions &CGO, std::unique_ptr<raw_pwrite_stream> OS,
102 llvm::LLVMContext &LLVMCtx,
104 : Action(Action), CI(CI), OutputStream(std::move(OS)),
105 FS(&CI.getVirtualFileSystem()),
106 Gen(std::make_unique<CIRGenerator>(CI.getDiagnostics(), std::move(FS),
107 CI.getCodeGenOpts())),
108 FEOptions(CI.getFrontendOpts()), CGO(CGO), LLVMCtx(LLVMCtx),
109 LinkModules(LinkModules),
110 DiagConsumer(CI.getDiagnostics(), CI.getCodeGenOpts()) {}
111
112 void Initialize(ASTContext &Ctx) override {
113 assert(!Context && "initialized multiple times");
114 Context = &Ctx;
115 DiagConsumer.setSourceManager(&Ctx.getSourceManager());
116 Gen->Initialize(Ctx);
117 // Install the MLIR diagnostic handler now that CIRGenerator owns its
118 // MLIRContext. Lifetime is tied to this consumer, which spans CIRGen,
119 // CIR-to-CIR passes, and CIR-to-LLVM lowering.
120 MLIRDiagHandler.emplace(&Gen->getMLIRContext(), CI.getDiagnostics(),
121 CI.getSourceManager(), CI.getFileManager());
122 }
123
125 Gen->HandleTopLevelDecl(D);
126 return true;
127 }
128
130 Gen->HandleCXXStaticMemberVarInstantiation(VD);
131 }
132
134 const OpenACCRoutineDecl *RD) override {
135 Gen->HandleOpenACCRoutineReference(FD, RD);
136 }
137
139 Gen->HandleInlineFunctionDefinition(D);
140 }
141
143 Gen->HandleTranslationUnit(C);
144
145 if (!FEOptions.ClangIRDisableCIRVerifier) {
146 if (!Gen->verifyModule()) {
147 // Verifier output already routed through ClangIRDiagnosticHandler.
148 // Only emit the generic fatal if nothing more specific was reported.
149 if (!CI.getDiagnostics().hasErrorOccurred())
150 CI.getDiagnostics().Report(
151 diag::err_cir_verification_failed_pre_passes);
152 llvm::report_fatal_error(
153 "CIR codegen: module verification error before running CIR passes");
154 return;
155 }
156 }
157
158 mlir::ModuleOp MlirModule = Gen->getModule();
159 mlir::MLIRContext &MlirCtx = Gen->getMLIRContext();
160
161 if (!FEOptions.ClangIRDisablePasses) {
162 std::string LibOptOptions = FEOptions.ClangIRLibOptOptions;
163
164 // Setup and run CIR pipeline.
165 const bool EnableLibOpt =
166 FEOptions.ClangIRLibOptEnabled && (CGO.OptimizationLevel > 0);
168 MlirModule, MlirCtx, !FEOptions.ClangIRDisableCIRVerifier,
169 FEOptions.ClangIREnableIdiomRecognizer, CGO.OptimizationLevel > 0,
170 EnableLibOpt, LibOptOptions, FEOptions.ClangIRCallConvLowering)
171 .failed()) {
172 // Pass-side errors already routed through ClangIRDiagnosticHandler.
173 // Skip the generic catch-all if a specific diagnostic was emitted.
174 if (!CI.getDiagnostics().hasErrorOccurred())
175 CI.getDiagnostics().Report(diag::err_cir_to_cir_transform_failed);
176 return;
177 }
178 }
179
180 switch (Action) {
182 if (OutputStream && MlirModule) {
183 mlir::OpPrintingFlags Flags;
184 Flags.enableDebugInfo(/*enable=*/true, /*prettyForm=*/false);
185 MlirModule->print(*OutputStream, Flags);
186 }
187 break;
192 StringRef saveTempsPrefix = CGO.SaveTempsFilePrefix;
193 std::string cirSaveTempsOutFile, mlirSaveTempsOutFile;
194 if (!saveTempsPrefix.empty()) {
195 SmallString<128> stem(saveTempsPrefix);
196 llvm::sys::path::replace_extension(stem, "cir");
197 cirSaveTempsOutFile = std::string(stem);
198 llvm::sys::path::replace_extension(stem, "mlir");
199 mlirSaveTempsOutFile = std::string(stem);
200 }
201
202 if (!cirSaveTempsOutFile.empty()) {
203 std::error_code ec;
204 llvm::raw_fd_ostream out(cirSaveTempsOutFile, ec);
205 if (!ec)
206 MlirModule->print(out);
207 }
208
209 // If errors occurred during codegen, stop before running the backend.
210 if (CI.getDiagnostics().hasErrorOccurred())
211 return;
212
213 // Route LLVM backend diagnostics (optimization remarks, unsupported
214 // features, inline-asm errors, etc.) through clang diagnostics for
215 // the remainder of the LLVM-emitting pipeline.
216 std::unique_ptr<llvm::DiagnosticHandler> OldDiagnosticHandler =
217 LLVMCtx.getDiagnosticHandler();
218 llvm::scope_exit RestoreDiagnosticHandler([&]() {
219 LLVMCtx.setDiagnosticHandler(std::move(OldDiagnosticHandler));
220 });
221 LLVMCtx.setDiagnosticHandler(DiagConsumer.createDiagnosticHandler());
222
223 std::unique_ptr<llvm::Module> LLVMModule = lowerFromCIRToLLVMIR(
224 MlirModule, LLVMCtx, C.getLangOpts().OpenMP, mlirSaveTempsOutFile,
225 &CI.getVirtualFileSystem());
226
227 LLVMModule->setDataLayout(C.getTargetInfo().getDataLayoutString());
228
229 for (llvm::Function &F : LLVMModule->functions())
230 if (const Decl *FD = Gen->getDeclForMangledName(F.getName()))
231 DiagConsumer.addFunctionSourceLocation(
232 F.getName(), FD->getASTContext().getFullLoc(FD->getLocation()));
233
234 if (linkInModules(*LLVMModule))
235 return;
236
237 // Embed the offloaded SYCL device binary into the host module.
238 if (C.getLangOpts().SYCLIsHost && !CGO.OffloadBinaryToEmbedFile.empty())
239 embedSYCLDeviceBinary(*LLVMModule);
240
241 // CUDA, HIP and OpenMP offloading rely on host-side offload entries that
242 // are not emitted on the ClangIR path yet, so embedding their device
243 // objects would produce a host object that cannot be registered.
244 const LangOptions &LangOpts = C.getLangOpts();
245 if (!CGO.OffloadObjects.empty() &&
246 (LangOpts.CUDA || !LangOpts.OMPTargetTriples.empty())) {
247 DiagnosticsEngine &Diags = CI.getDiagnostics();
248 Diags.Report(Diags.getCustomDiagID(
250 "ClangIR code gen Not Yet Implemented: embedding offload objects "
251 "for CUDA, HIP or OpenMP offloading"));
252 return;
253 }
254
255 // If there is device offloading code embed it in the host now.
256 EmbedObject(LLVMModule.get(), CGO, CI.getVirtualFileSystem(),
257 CI.getDiagnostics());
258
260 emitBackendOutput(CI, CI.getCodeGenOpts(), LLVMModule.get(), BEAction, FS,
261 std::move(OutputStream));
262 break;
263 }
264 }
265 }
266
267 // TODO: share with BackendConsumer::LinkInModules once the rest of the
268 // linking logic (not just diagnostics) is unified.
269 bool linkInModules(llvm::Module &M) {
270 for (auto &LM : LinkModules) {
271 assert(LM.Module && "LinkModule does not actually have a module");
272
273 if (LM.PropagateAttrs)
274 for (llvm::Function &F : *LM.Module) {
275 if (F.isIntrinsic())
276 continue;
278 F, CGO, CI.getLangOpts(), CI.getTargetOpts(), LM.Internalize);
279 }
280
281 DiagConsumer.setCurLinkModule(LM.Module.get());
282 bool Err;
283 if (LM.Internalize) {
284 Err = llvm::Linker::linkModules(
285 M, std::move(LM.Module), LM.LinkFlags,
286 [](llvm::Module &M, const llvm::StringSet<> &GVS) {
287 llvm::internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
288 return !GV.hasName() || (GVS.count(GV.getName()) == 0);
289 });
290 });
291 } else {
292 Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags);
293 }
294
295 if (Err)
296 return true;
297 }
298
299 LinkModules.clear();
300 return false;
301 }
302
303 // Reads the device binary named by -foffload-include-binary and embeds it
304 // into the host module. wrapSYCLBinaries also appends the registration ctor
305 // at priority 101 when no registration-function out-param is supplied.
306 void embedSYCLDeviceBinary(llvm::Module &M) {
307 StringRef fileName = CGO.OffloadBinaryToEmbedFile;
308 auto bufferOrErr = CI.getVirtualFileSystem().getBufferForFile(fileName);
309 if (std::error_code ec = bufferOrErr.getError()) {
310 CI.getDiagnostics().Report(diag::err_cannot_open_file)
311 << fileName << ec.message();
312 return;
313 }
314 std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(bufferOrErr.get());
315 if (llvm::Error err = llvm::offloading::wrapSYCLBinaries(
316 M,
317 ArrayRef<char>(buffer->getBufferStart(), buffer->getBufferSize()),
318 llvm::offloading::SYCLJITOptions(), /*IsFinalizedImage=*/true)) {
319 CI.getDiagnostics().Report(diag::err_fe_error_backend)
320 << llvm::toString(std::move(err));
321 return;
322 }
323 }
324
327 Context->getSourceManager(),
328 "CIR generation of declaration");
329 Gen->HandleTagDeclDefinition(D);
330 }
331
332 void HandleTagDeclRequiredDefinition(const TagDecl *D) override {
333 Gen->HandleTagDeclRequiredDefinition(D);
334 }
335
337 Gen->CompleteTentativeDefinition(D);
338 }
339
340 void HandleVTable(CXXRecordDecl *RD) override { Gen->HandleVTable(RD); }
341};
342} // namespace cir
343
344void CIRGenConsumer::anchor() {}
345
346CIRGenAction::CIRGenAction(OutputType Act, mlir::MLIRContext *MLIRCtx)
347 : MLIRCtx(MLIRCtx ? MLIRCtx : new mlir::MLIRContext),
348 Ctx(std::make_unique<llvm::LLVMContext>()), Action(Act) {}
349
350CIRGenAction::~CIRGenAction() { MLIRMod.release(); }
351
353 if (clang::loadLinkModules(CI, *Ctx, LinkModules))
354 return false;
356}
357
359 if (getCurrentFileKind().getLanguage() != Language::CIR) {
361 return;
362 }
363
364 // TODO: Parse the ClangIR input and emit the requested output.
366 diag::err_fe_cir_input_unsupported);
367}
368
369static std::unique_ptr<raw_pwrite_stream>
370getOutputStream(CompilerInstance &CI, StringRef InFile,
372 switch (Action) {
374 return CI.createDefaultOutputFile(false, InFile, "s");
376 return CI.createDefaultOutputFile(false, InFile, "cir");
378 return CI.createDefaultOutputFile(false, InFile, "ll");
380 return CI.createDefaultOutputFile(true, InFile, "bc");
382 return CI.createDefaultOutputFile(true, InFile, "o");
383 }
384 llvm_unreachable("Invalid CIRGenAction::OutputType");
385}
386
387std::unique_ptr<ASTConsumer>
389 std::unique_ptr<llvm::raw_pwrite_stream> Out = CI.takeOutputStream();
390
391 if (!Out)
392 Out = getOutputStream(CI, InFile, Action);
393
394 auto Result = std::make_unique<cir::CIRGenConsumer>(
395 Action, CI, CI.getCodeGenOpts(), std::move(Out), *Ctx, LinkModules);
396
397 return Result;
398}
399
400void EmitAssemblyAction::anchor() {}
401EmitAssemblyAction::EmitAssemblyAction(mlir::MLIRContext *MLIRCtx)
402 : CIRGenAction(OutputType::EmitAssembly, MLIRCtx) {}
403
404void EmitCIRAction::anchor() {}
405EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx)
406 : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {}
407
408void EmitLLVMAction::anchor() {}
409EmitLLVMAction::EmitLLVMAction(mlir::MLIRContext *MLIRCtx)
410 : CIRGenAction(OutputType::EmitLLVM, MLIRCtx) {}
411
412void EmitBCAction::anchor() {}
413EmitBCAction::EmitBCAction(mlir::MLIRContext *MLIRCtx)
414 : CIRGenAction(OutputType::EmitBC, MLIRCtx) {}
415
416void EmitObjAction::anchor() {}
417EmitObjAction::EmitObjAction(mlir::MLIRContext *MLIRCtx)
418 : CIRGenAction(OutputType::EmitObj, MLIRCtx) {}
Defines the clang::ASTContext interface.
static std::unique_ptr< raw_pwrite_stream > getOutputStream(CompilerInstance &CI, StringRef InFile, CIRGenAction::OutputType Action)
CIRGenAction(OutputType Action, mlir::MLIRContext *MLIRCtx=nullptr)
OutputType Action
void ExecuteAction() override
Implement the ExecuteAction interface by running Sema on the already-initialized AST consumer.
bool BeginSourceFileAction(clang::CompilerInstance &CI) override
Callback at the start of processing a single input.
~CIRGenAction() override
std::unique_ptr< clang::ASTConsumer > CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef InFile) override
void Initialize(ASTContext &Ctx) override
Initialize - This is called to initialize the consumer, providing the ASTContext.
bool HandleTopLevelDecl(DeclGroupRef D) override
HandleTopLevelDecl - Handle the specified top-level declaration.
void HandleTranslationUnit(ASTContext &C) override
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
bool linkInModules(llvm::Module &M)
void HandleInlineFunctionDefinition(FunctionDecl *D) override
This callback is invoked each time an inline (method or friend) function definition in a class is com...
void CompleteTentativeDefinition(VarDecl *D) override
CompleteTentativeDefinition - Callback invoked at the end of a translation unit to notify the consume...
void HandleTagDeclRequiredDefinition(const TagDecl *D) override
This callback is invoked the first time each TagDecl is required to be complete.
CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI, CodeGenOptions &CGO, std::unique_ptr< raw_pwrite_stream > OS, llvm::LLVMContext &LLVMCtx, SmallVectorImpl<::clang::LinkModule > &LinkModules)
void embedSYCLDeviceBinary(llvm::Module &M)
void HandleVTable(CXXRecordDecl *RD) override
Callback involved at the end of a translation unit to notify the consumer that a vtable for the given...
void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *VD) override
HandleCXXStaticMemberVarInstantiation - Tell the consumer that this.
void HandleOpenACCRoutineReference(const FunctionDecl *FD, const OpenACCRoutineDecl *RD) override
Callback to handle the end-of-translation unit attachment of OpenACC routine declaration information.
void HandleTagDeclDefinition(TagDecl *D) override
HandleTagDeclDefinition - This callback is invoked each time a TagDecl (e.g.
EmitAssemblyAction(mlir::MLIRContext *MLIRCtx=nullptr)
EmitBCAction(mlir::MLIRContext *MLIRCtx=nullptr)
EmitCIRAction(mlir::MLIRContext *MLIRCtx=nullptr)
EmitLLVMAction(mlir::MLIRContext *MLIRCtx=nullptr)
EmitObjAction(mlir::MLIRContext *MLIRCtx=nullptr)
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs.
Definition ASTConsumer.h:35
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:239
SourceManager & getSourceManager()
Definition ASTContext.h:911
void ExecuteAction() override
Implement the ExecuteAction interface by running Sema on the already-initialized AST consumer.
Owns the state needed to translate LLVM backend diagnostics into clang diagnostics,...
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
CodeGenOptions - Track various options which control how the code is optimized and passed to the back...
CompilerInstance - Helper class for managing a single instance of the Clang compiler.
std::unique_ptr< raw_pwrite_stream > createDefaultOutputFile(bool Binary=true, StringRef BaseInput="", StringRef Extension="", bool RemoveFileOnSignal=true, bool CreateMissingDirectories=false, bool ForceUseTemporary=false, bool SetOnlyIfDifferent=false)
Create the default output file (from the invocation's options) and add it to the list of tracked outp...
DiagnosticsEngine & getDiagnostics() const
Get the current diagnostics engine.
std::unique_ptr< llvm::raw_pwrite_stream > takeOutputStream()
CodeGenOptions & getCodeGenOpts()
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:241
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
unsigned getCustomDiagID(Level L, const char(&FormatString)[N])
Return an ID for a diagnostic with the specified format string and level.
Definition Diagnostic.h:939
InputKind getCurrentFileKind() const
virtual bool BeginSourceFileAction(CompilerInstance &CI)
Callback at the start of processing a single input.
CompilerInstance & getCompilerInstance() const
FrontendOptions - Options for controlling the behavior of the frontend.
Represents a function declaration or definition.
Definition Decl.h:2059
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
std::vector< llvm::Triple > OMPTargetTriples
Triples of the OpenMP targets that the host code codegen should take into account in order to generat...
PrettyStackTraceDecl - If a crash occurs, indicate that it happened when doing something to a specifi...
Definition DeclBase.h:1317
Encodes a location in the source.
Represents the declaration of a struct/union/class/enum.
Definition Decl.h:3852
Represents a variable declaration or definition.
Definition Decl.h:933
std::unique_ptr< llvm::Module > lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, llvm::LLVMContext &llvmCtx, bool enableOpenMP, llvm::StringRef mlirSaveTempsOutFile={}, llvm::vfs::FileSystem *fs=nullptr)
static BackendAction getBackendActionFromOutputType(CIRGenAction::OutputType Action)
mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule, mlir::MLIRContext &mlirCtx, bool enableVerifier, bool enableIdiomRecognizer, bool enableCIRSimplify, bool enableLibOpt, llvm::StringRef libOptOptions, bool enableCallConvLowering)
Definition CIRPasses.cpp:74
static std::unique_ptr< llvm::Module > lowerFromCIRToLLVMIR(mlir::ModuleOp MLIRModule, llvm::LLVMContext &LLVMCtx, bool EnableOpenMP, llvm::StringRef mlirSaveTempsOutFile={}, llvm::vfs::FileSystem *fs=nullptr)
void mergeDefaultFunctionDefinitionAttributes(llvm::Function &F, const CodeGenOptions &CodeGenOpts, const LangOptions &LangOpts, const TargetOptions &TargetOpts, bool WillInternalize)
Adds attributes to F according to our CodeGenOpts and LangOpts, as though we had emitted it ourselves...
Definition CGCall.cpp:2498
Top level wrappers for InstallAPI frontend operations.
@ CIR
LLVM IR & CIR: we accept these so that we can run the optimizer on them, and compile them to assembly...
void EmbedObject(llvm::Module *M, const CodeGenOptions &CGOpts, llvm::vfs::FileSystem &VFS, DiagnosticsEngine &Diags)
void emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts, llvm::Module *M, BackendAction Action, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, std::unique_ptr< raw_pwrite_stream > OS, BackendConsumer *BC=nullptr)
BackendAction
Definition BackendUtil.h:31
@ Backend_EmitAssembly
Emit native assembly files.
Definition BackendUtil.h:32
@ Backend_EmitLL
Emit human-readable LLVM assembly.
Definition BackendUtil.h:34
@ Backend_EmitBC
Emit LLVM bitcode files.
Definition BackendUtil.h:33
@ Backend_EmitObj
Emit native object files.
Definition BackendUtil.h:37
bool loadLinkModules(CompilerInstance &CI, llvm::LLVMContext &Ctx, llvm::SmallVectorImpl< LinkModule > &LinkModules)
Load every bitcode file listed in CodeGenOpts.LinkBitcodeFiles into LinkModules.
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30