llvm-project/clang/lib/CIR/CodeGen/CIRGenerator.cpp
Erich Keane bb55c4bcfe
[CIR] Implement member-pointer members lowering/CXX ABI lowering (#187327)
Record types with a member pointer as a member require quite a bit of
work to get to function properly. First, we have to wire them through
the AST->CIR lowering to make sure we properly represent them, and
represent them when they're zero initializable. We also have to properly
initialize elements when we're NOT zero initializable.

More importantly, we have to implement the CXXABILowering of record
types. Before this patch, we just assumed that all RecordTypes were
legal, since we didn't have the above lowering. A vast majority of this
patch is around getting RecordTypes to lower properly. There isn't
really a good way to test this without the FE changes, so it wasn't
split off.

We accomplish this in 2 phases: First, we transform each individual
record type along the way, giving it a new cxx-abi specific name. We
have to ensure that recursive evaluation works correctly, so we pulled
the solution from the LLVM-IR dialect for that. Secondly, we rename all
of the RecordType's back. This is safe only after we have 'removed' all
references to the untransformed RecordType due to the way RecordStorage
works, however it'll assert if we get this wrong. We do this second
stage to minimize the impact on tests as well as the IR: otherwise EVERY
RecordType would have their names changed. In addition to massive test
changes, this would be an ugly change everywhere.

One additional concern with RecordType lowering, is that it makes most
of RecordTypes potentially 'illegal'. Thus, any attribute that
'references' a record type is required to be transformed as well, so
this patch implements attribute legalization and transformation.

This quite a few NYIs around lowering of these, though most are probably
not really accessible.
2026-03-31 07:29:33 -07:00

209 lines
7.2 KiB
C++

//===--- CIRGenerator.cpp - Emit CIR from ASTs ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This builds an AST and converts it to CIR.
//
//===----------------------------------------------------------------------===//
#include "CIRGenModule.h"
#include "mlir/Dialect/OpenACC/OpenACCOpsDialect.h.inc"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/Target/LLVMIR/Import.h"
#include "clang/AST/DeclGroup.h"
#include "clang/CIR/CIRGenerator.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/OpenACC/RegisterOpenACCExtensions.h"
#include "llvm/IR/DataLayout.h"
using namespace cir;
using namespace clang;
void CIRGenerator::anchor() {}
CIRGenerator::CIRGenerator(clang::DiagnosticsEngine &diags,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,
const CodeGenOptions &cgo)
: diags(diags), fs(std::move(vfs)), codeGenOpts{cgo},
handlingTopLevelDecls{0} {}
CIRGenerator::~CIRGenerator() {
// There should normally not be any leftover inline method definitions.
assert(deferredInlineMemberFuncDefs.empty() || diags.hasErrorOccurred());
}
static void setMLIRDataLayout(mlir::ModuleOp &mod, const llvm::DataLayout &dl) {
mlir::MLIRContext *mlirContext = mod.getContext();
mlir::DataLayoutSpecInterface dlSpec =
mlir::translateDataLayout(dl, mlirContext);
mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName, dlSpec);
}
void CIRGenerator::Initialize(ASTContext &astContext) {
using namespace llvm;
this->astContext = &astContext;
mlirContext = std::make_unique<mlir::MLIRContext>();
mlirContext->loadDialect<mlir::DLTIDialect>();
mlirContext->loadDialect<cir::CIRDialect>();
mlirContext->getOrLoadDialect<mlir::acc::OpenACCDialect>();
mlirContext->getOrLoadDialect<mlir::omp::OpenMPDialect>();
// Register extensions to integrate CIR types with OpenACC.
mlir::DialectRegistry registry;
cir::acc::registerOpenACCExtensions(registry);
mlirContext->appendDialectRegistry(registry);
cgm = std::make_unique<clang::CIRGen::CIRGenModule>(
*mlirContext.get(), astContext, codeGenOpts, diags);
mlir::ModuleOp mod = cgm->getModule();
llvm::DataLayout layout =
llvm::DataLayout(astContext.getTargetInfo().getDataLayoutString());
setMLIRDataLayout(mod, layout);
}
bool CIRGenerator::verifyModule() const { return cgm->verifyModule(); }
mlir::ModuleOp CIRGenerator::getModule() const { return cgm->getModule(); }
bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) {
if (diags.hasUnrecoverableErrorOccurred())
return true;
HandlingTopLevelDeclRAII handlingDecl(*this);
for (Decl *decl : group)
cgm->emitTopLevelDecl(decl);
return true;
}
void CIRGenerator::HandleTranslationUnit(ASTContext &astContext) {
// Release the Builder when there is no error.
if (!diags.hasErrorOccurred() && cgm)
cgm->release();
// If there are errors before or when releasing the cgm, reset the module to
// stop here before invoking the backend.
assert(!cir::MissingFeatures::cleanupAfterErrorDiags());
}
void CIRGenerator::HandleInlineFunctionDefinition(FunctionDecl *d) {
if (diags.hasErrorOccurred())
return;
assert(d->doesThisDeclarationHaveABody());
// We may want to emit this definition. However, that decision might be
// based on computing the linkage, and we have to defer that in case we are
// inside of something that will chagne the method's final linkage, e.g.
// typedef struct {
// void bar();
// void foo() { bar(); }
// } A;
deferredInlineMemberFuncDefs.push_back(d);
// Provide some coverage mapping even for methods that aren't emitted.
// Don't do this for templated classes though, as they may not be
// instantiable.
assert(!cir::MissingFeatures::coverageMapping());
}
void CIRGenerator::emitDeferredDecls() {
if (deferredInlineMemberFuncDefs.empty())
return;
// Emit any deferred inline method definitions. Note that more deferred
// methods may be added during this loop, since ASTConsumer callbacks can be
// invoked if AST inspection results in declarations being added. Therefore,
// we use an index to loop over the deferredInlineMemberFuncDefs rather than
// a range.
HandlingTopLevelDeclRAII handlingDecls(*this);
for (unsigned i = 0; i != deferredInlineMemberFuncDefs.size(); ++i)
cgm->emitTopLevelDecl(deferredInlineMemberFuncDefs[i]);
deferredInlineMemberFuncDefs.clear();
}
/// HandleTagDeclDefinition - This callback is invoked each time a TagDecl to
/// (e.g. struct, union, enum, class) is completed. This allows the client to
/// hack on the type, which can occur at any point in the file (because these
/// can be defined in declspecs).
void CIRGenerator::HandleTagDeclDefinition(TagDecl *d) {
if (diags.hasErrorOccurred())
return;
// Don't allow re-entrant calls to CIRGen triggered by PCH deserialization to
// emit deferred decls.
HandlingTopLevelDeclRAII handlingDecl(*this, /*EmitDeferred=*/false);
cgm->updateCompletedType(d);
// For MSVC compatibility, treat declarations of static data members with
// inline initializers as definitions.
if (astContext->getTargetInfo().getCXXABI().isMicrosoft())
cgm->errorNYI(d->getSourceRange(), "HandleTagDeclDefinition: MSABI");
// For OpenMP emit declare reduction functions or declare mapper, if
// required.
if (astContext->getLangOpts().OpenMP) {
for (Decl *member : d->decls()) {
if (auto *drd = dyn_cast<OMPDeclareReductionDecl>(member)) {
if (astContext->DeclMustBeEmitted(drd))
cgm->errorNYI(d->getSourceRange(),
"HandleTagDeclDefinition: OMPDeclareReductionDecl");
} else if (auto *dmd = dyn_cast<OMPDeclareMapperDecl>(member)) {
if (astContext->DeclMustBeEmitted(dmd))
cgm->errorNYI(d->getSourceRange(),
"HandleTagDeclDefinition: OMPDeclareMapperDecl");
}
}
}
}
void CIRGenerator::HandleTagDeclRequiredDefinition(const TagDecl *D) {
if (diags.hasErrorOccurred())
return;
assert(!cir::MissingFeatures::generateDebugInfo());
}
void CIRGenerator::HandleCXXStaticMemberVarInstantiation(VarDecl *D) {
if (diags.hasErrorOccurred())
return;
cgm->handleCXXStaticMemberVarInstantiation(D);
}
void CIRGenerator::HandleOpenACCRoutineReference(const FunctionDecl *FD,
const OpenACCRoutineDecl *RD) {
llvm::StringRef mangledName = cgm->getMangledName(FD);
cir::FuncOp entry =
mlir::dyn_cast_if_present<cir::FuncOp>(cgm->getGlobalValue(mangledName));
// if this wasn't generated, don't force it to be.
if (!entry)
return;
cgm->emitOpenACCRoutineDecl(FD, entry, RD->getBeginLoc(), RD->clauses());
}
void CIRGenerator::CompleteTentativeDefinition(VarDecl *d) {
if (diags.hasErrorOccurred())
return;
cgm->emitTentativeDefinition(d);
}
void CIRGenerator::HandleVTable(CXXRecordDecl *rd) {
if (diags.hasErrorOccurred())
return;
cgm->emitVTable(rd);
}