485 lines
18 KiB
C++
485 lines
18 KiB
C++
//===- CIRGenModule.cpp - Per-Module state for CIR generation -------------===//
|
|
//
|
|
// 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 is the internal per-translation-unit state used for CIR translation.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CIRGenModule.h"
|
|
#include "CIRGenConstantEmitter.h"
|
|
#include "CIRGenFunction.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/DeclBase.h"
|
|
#include "clang/AST/GlobalDecl.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/CIR/Dialect/IR/CIRDialect.h"
|
|
#include "clang/CIR/MissingFeatures.h"
|
|
|
|
#include "mlir/IR/BuiltinOps.h"
|
|
#include "mlir/IR/Location.h"
|
|
#include "mlir/IR/MLIRContext.h"
|
|
#include "mlir/IR/Verifier.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::CIRGen;
|
|
|
|
CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
|
|
clang::ASTContext &astContext,
|
|
const clang::CodeGenOptions &cgo,
|
|
DiagnosticsEngine &diags)
|
|
: builder(mlirContext, *this), astContext(astContext),
|
|
langOpts(astContext.getLangOpts()), codeGenOpts(cgo),
|
|
theModule{mlir::ModuleOp::create(mlir::UnknownLoc::get(&mlirContext))},
|
|
diags(diags), target(astContext.getTargetInfo()), genTypes(*this) {
|
|
|
|
// Initialize cached types
|
|
VoidTy = cir::VoidType::get(&getMLIRContext());
|
|
SInt8Ty = cir::IntType::get(&getMLIRContext(), 8, /*isSigned=*/true);
|
|
SInt16Ty = cir::IntType::get(&getMLIRContext(), 16, /*isSigned=*/true);
|
|
SInt32Ty = cir::IntType::get(&getMLIRContext(), 32, /*isSigned=*/true);
|
|
SInt64Ty = cir::IntType::get(&getMLIRContext(), 64, /*isSigned=*/true);
|
|
SInt128Ty = cir::IntType::get(&getMLIRContext(), 128, /*isSigned=*/true);
|
|
UInt8Ty = cir::IntType::get(&getMLIRContext(), 8, /*isSigned=*/false);
|
|
UInt16Ty = cir::IntType::get(&getMLIRContext(), 16, /*isSigned=*/false);
|
|
UInt32Ty = cir::IntType::get(&getMLIRContext(), 32, /*isSigned=*/false);
|
|
UInt64Ty = cir::IntType::get(&getMLIRContext(), 64, /*isSigned=*/false);
|
|
UInt128Ty = cir::IntType::get(&getMLIRContext(), 128, /*isSigned=*/false);
|
|
FP16Ty = cir::FP16Type::get(&getMLIRContext());
|
|
BFloat16Ty = cir::BF16Type::get(&getMLIRContext());
|
|
FloatTy = cir::SingleType::get(&getMLIRContext());
|
|
DoubleTy = cir::DoubleType::get(&getMLIRContext());
|
|
FP80Ty = cir::FP80Type::get(&getMLIRContext());
|
|
FP128Ty = cir::FP128Type::get(&getMLIRContext());
|
|
|
|
theModule->setAttr(cir::CIRDialect::getTripleAttrName(),
|
|
builder.getStringAttr(getTriple().str()));
|
|
}
|
|
|
|
mlir::Location CIRGenModule::getLoc(SourceLocation cLoc) {
|
|
assert(cLoc.isValid() && "expected valid source location");
|
|
const SourceManager &sm = astContext.getSourceManager();
|
|
PresumedLoc pLoc = sm.getPresumedLoc(cLoc);
|
|
StringRef filename = pLoc.getFilename();
|
|
return mlir::FileLineColLoc::get(builder.getStringAttr(filename),
|
|
pLoc.getLine(), pLoc.getColumn());
|
|
}
|
|
|
|
mlir::Location CIRGenModule::getLoc(SourceRange cRange) {
|
|
assert(cRange.isValid() && "expected a valid source range");
|
|
mlir::Location begin = getLoc(cRange.getBegin());
|
|
mlir::Location end = getLoc(cRange.getEnd());
|
|
mlir::Attribute metadata;
|
|
return mlir::FusedLoc::get({begin, end}, metadata, builder.getContext());
|
|
}
|
|
|
|
void CIRGenModule::emitGlobal(clang::GlobalDecl gd) {
|
|
const auto *global = cast<ValueDecl>(gd.getDecl());
|
|
|
|
if (const auto *fd = dyn_cast<FunctionDecl>(global)) {
|
|
// Update deferred annotations with the latest declaration if the function
|
|
// was already used or defined.
|
|
if (fd->hasAttr<AnnotateAttr>())
|
|
errorNYI(fd->getSourceRange(), "deferredAnnotations");
|
|
if (!fd->doesThisDeclarationHaveABody()) {
|
|
if (!fd->doesDeclarationForceExternallyVisibleDefinition())
|
|
return;
|
|
|
|
errorNYI(fd->getSourceRange(),
|
|
"function declaration that forces code gen");
|
|
return;
|
|
}
|
|
} else {
|
|
assert(cast<VarDecl>(global)->isFileVarDecl() &&
|
|
"Cannot emit local var decl as global");
|
|
}
|
|
|
|
// TODO(CIR): Defer emitting some global definitions until later
|
|
emitGlobalDefinition(gd);
|
|
}
|
|
|
|
void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
|
|
mlir::Operation *op) {
|
|
auto const *funcDecl = cast<FunctionDecl>(gd.getDecl());
|
|
if (funcDecl->getIdentifier() == nullptr) {
|
|
errorNYI(funcDecl->getSourceRange().getBegin(),
|
|
"function definition with a non-identifier for a name");
|
|
return;
|
|
}
|
|
cir::FuncType funcType =
|
|
cast<cir::FuncType>(convertType(funcDecl->getType()));
|
|
|
|
cir::FuncOp funcOp = dyn_cast_if_present<cir::FuncOp>(op);
|
|
if (!funcOp || funcOp.getFunctionType() != funcType) {
|
|
funcOp = getAddrOfFunction(gd, funcType, /*ForVTable=*/false,
|
|
/*DontDefer=*/true, ForDefinition);
|
|
}
|
|
|
|
CIRGenFunction cgf(*this, builder);
|
|
{
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
cgf.generateCode(gd, funcOp, funcType);
|
|
}
|
|
}
|
|
|
|
void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
|
|
bool isTentative) {
|
|
const QualType astTy = vd->getType();
|
|
const mlir::Type type = convertType(vd->getType());
|
|
if (clang::IdentifierInfo *identifier = vd->getIdentifier()) {
|
|
auto varOp = builder.create<cir::GlobalOp>(getLoc(vd->getSourceRange()),
|
|
identifier->getName(), type);
|
|
// TODO(CIR): This code for processing initial values is a placeholder
|
|
// until class ConstantEmitter is upstreamed and the code for processing
|
|
// constant expressions is filled out. Only the most basic handling of
|
|
// certain constant expressions is implemented for now.
|
|
const VarDecl *initDecl;
|
|
const Expr *initExpr = vd->getAnyInitializer(initDecl);
|
|
mlir::Attribute initializer;
|
|
if (initExpr) {
|
|
if (APValue *value = initDecl->evaluateValue()) {
|
|
ConstantEmitter emitter(*this);
|
|
initializer = emitter.tryEmitPrivateForMemory(*value, astTy);
|
|
} else {
|
|
errorNYI(initExpr->getSourceRange(), "non-constant initializer");
|
|
}
|
|
} else {
|
|
initializer = builder.getZeroInitAttr(convertType(astTy));
|
|
}
|
|
|
|
varOp.setInitialValueAttr(initializer);
|
|
|
|
// Set CIR's linkage type as appropriate.
|
|
cir::GlobalLinkageKind linkage =
|
|
getCIRLinkageVarDefinition(vd, /*IsConstant=*/false);
|
|
|
|
// Set CIR linkage and DLL storage class.
|
|
varOp.setLinkage(linkage);
|
|
|
|
if (linkage == cir::GlobalLinkageKind::CommonLinkage)
|
|
errorNYI(initExpr->getSourceRange(), "common linkage");
|
|
|
|
theModule.push_back(varOp);
|
|
} else {
|
|
errorNYI(vd->getSourceRange().getBegin(),
|
|
"variable definition with a non-identifier for a name");
|
|
}
|
|
}
|
|
|
|
void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd,
|
|
mlir::Operation *op) {
|
|
const auto *decl = cast<ValueDecl>(gd.getDecl());
|
|
if (const auto *fd = dyn_cast<FunctionDecl>(decl)) {
|
|
// TODO(CIR): Skip generation of CIR for functions with available_externally
|
|
// linkage at -O0.
|
|
|
|
if (const auto *method = dyn_cast<CXXMethodDecl>(decl)) {
|
|
// Make sure to emit the definition(s) before we emit the thunks. This is
|
|
// necessary for the generation of certain thunks.
|
|
(void)method;
|
|
errorNYI(method->getSourceRange(), "member function");
|
|
return;
|
|
}
|
|
|
|
if (fd->isMultiVersion())
|
|
errorNYI(fd->getSourceRange(), "multiversion functions");
|
|
emitGlobalFunctionDefinition(gd, op);
|
|
return;
|
|
}
|
|
|
|
if (const auto *vd = dyn_cast<VarDecl>(decl))
|
|
return emitGlobalVarDefinition(vd, !vd->hasDefinition());
|
|
|
|
llvm_unreachable("Invalid argument to CIRGenModule::emitGlobalDefinition");
|
|
}
|
|
|
|
static bool shouldBeInCOMDAT(CIRGenModule &cgm, const Decl &d) {
|
|
assert(!cir::MissingFeatures::supportComdat());
|
|
|
|
if (d.hasAttr<SelectAnyAttr>())
|
|
return true;
|
|
|
|
GVALinkage linkage;
|
|
if (auto *vd = dyn_cast<VarDecl>(&d))
|
|
linkage = cgm.getASTContext().GetGVALinkageForVariable(vd);
|
|
else
|
|
linkage =
|
|
cgm.getASTContext().GetGVALinkageForFunction(cast<FunctionDecl>(&d));
|
|
|
|
switch (linkage) {
|
|
case clang::GVA_Internal:
|
|
case clang::GVA_AvailableExternally:
|
|
case clang::GVA_StrongExternal:
|
|
return false;
|
|
case clang::GVA_DiscardableODR:
|
|
case clang::GVA_StrongODR:
|
|
return true;
|
|
}
|
|
llvm_unreachable("No such linkage");
|
|
}
|
|
|
|
// TODO(CIR): this could be a common method between LLVM codegen.
|
|
static bool isVarDeclStrongDefinition(const ASTContext &astContext,
|
|
CIRGenModule &cgm, const VarDecl *vd,
|
|
bool noCommon) {
|
|
// Don't give variables common linkage if -fno-common was specified unless it
|
|
// was overridden by a NoCommon attribute.
|
|
if ((noCommon || vd->hasAttr<NoCommonAttr>()) && !vd->hasAttr<CommonAttr>())
|
|
return true;
|
|
|
|
// C11 6.9.2/2:
|
|
// A declaration of an identifier for an object that has file scope without
|
|
// an initializer, and without a storage-class specifier or with the
|
|
// storage-class specifier static, constitutes a tentative definition.
|
|
if (vd->getInit() || vd->hasExternalStorage())
|
|
return true;
|
|
|
|
// A variable cannot be both common and exist in a section.
|
|
if (vd->hasAttr<SectionAttr>())
|
|
return true;
|
|
|
|
// A variable cannot be both common and exist in a section.
|
|
// We don't try to determine which is the right section in the front-end.
|
|
// If no specialized section name is applicable, it will resort to default.
|
|
if (vd->hasAttr<PragmaClangBSSSectionAttr>() ||
|
|
vd->hasAttr<PragmaClangDataSectionAttr>() ||
|
|
vd->hasAttr<PragmaClangRelroSectionAttr>() ||
|
|
vd->hasAttr<PragmaClangRodataSectionAttr>())
|
|
return true;
|
|
|
|
// Thread local vars aren't considered common linkage.
|
|
if (vd->getTLSKind())
|
|
return true;
|
|
|
|
// Tentative definitions marked with WeakImportAttr are true definitions.
|
|
if (vd->hasAttr<WeakImportAttr>())
|
|
return true;
|
|
|
|
// A variable cannot be both common and exist in a comdat.
|
|
if (shouldBeInCOMDAT(cgm, *vd))
|
|
return true;
|
|
|
|
// Declarations with a required alignment do not have common linkage in MSVC
|
|
// mode.
|
|
if (astContext.getTargetInfo().getCXXABI().isMicrosoft()) {
|
|
if (vd->hasAttr<AlignedAttr>())
|
|
return true;
|
|
QualType varType = vd->getType();
|
|
if (astContext.isAlignmentRequired(varType))
|
|
return true;
|
|
|
|
if (const auto *rt = varType->getAs<RecordType>()) {
|
|
const RecordDecl *rd = rt->getDecl();
|
|
for (const FieldDecl *fd : rd->fields()) {
|
|
if (fd->isBitField())
|
|
continue;
|
|
if (fd->hasAttr<AlignedAttr>())
|
|
return true;
|
|
if (astContext.isAlignmentRequired(fd->getType()))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Microsoft's link.exe doesn't support alignments greater than 32 bytes for
|
|
// common symbols, so symbols with greater alignment requirements cannot be
|
|
// common.
|
|
// Other COFF linkers (ld.bfd and LLD) support arbitrary power-of-two
|
|
// alignments for common symbols via the aligncomm directive, so this
|
|
// restriction only applies to MSVC environments.
|
|
if (astContext.getTargetInfo().getTriple().isKnownWindowsMSVCEnvironment() &&
|
|
astContext.getTypeAlignIfKnown(vd->getType()) >
|
|
astContext.toBits(CharUnits::fromQuantity(32)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator(
|
|
const DeclaratorDecl *dd, GVALinkage linkage, bool isConstantVariable) {
|
|
if (linkage == GVA_Internal)
|
|
return cir::GlobalLinkageKind::InternalLinkage;
|
|
|
|
if (dd->hasAttr<WeakAttr>()) {
|
|
if (isConstantVariable)
|
|
return cir::GlobalLinkageKind::WeakODRLinkage;
|
|
return cir::GlobalLinkageKind::WeakAnyLinkage;
|
|
}
|
|
|
|
if (const auto *fd = dd->getAsFunction())
|
|
if (fd->isMultiVersion() && linkage == GVA_AvailableExternally)
|
|
return cir::GlobalLinkageKind::LinkOnceAnyLinkage;
|
|
|
|
// We are guaranteed to have a strong definition somewhere else,
|
|
// so we can use available_externally linkage.
|
|
if (linkage == GVA_AvailableExternally)
|
|
return cir::GlobalLinkageKind::AvailableExternallyLinkage;
|
|
|
|
// Note that Apple's kernel linker doesn't support symbol
|
|
// coalescing, so we need to avoid linkonce and weak linkages there.
|
|
// Normally, this means we just map to internal, but for explicit
|
|
// instantiations we'll map to external.
|
|
|
|
// In C++, the compiler has to emit a definition in every translation unit
|
|
// that references the function. We should use linkonce_odr because
|
|
// a) if all references in this translation unit are optimized away, we
|
|
// don't need to codegen it. b) if the function persists, it needs to be
|
|
// merged with other definitions. c) C++ has the ODR, so we know the
|
|
// definition is dependable.
|
|
if (linkage == GVA_DiscardableODR)
|
|
return !astContext.getLangOpts().AppleKext
|
|
? cir::GlobalLinkageKind::LinkOnceODRLinkage
|
|
: cir::GlobalLinkageKind::InternalLinkage;
|
|
|
|
// An explicit instantiation of a template has weak linkage, since
|
|
// explicit instantiations can occur in multiple translation units
|
|
// and must all be equivalent. However, we are not allowed to
|
|
// throw away these explicit instantiations.
|
|
//
|
|
// CUDA/HIP: For -fno-gpu-rdc case, device code is limited to one TU,
|
|
// so say that CUDA templates are either external (for kernels) or internal.
|
|
// This lets llvm perform aggressive inter-procedural optimizations. For
|
|
// -fgpu-rdc case, device function calls across multiple TU's are allowed,
|
|
// therefore we need to follow the normal linkage paradigm.
|
|
if (linkage == GVA_StrongODR) {
|
|
if (getLangOpts().AppleKext)
|
|
return cir::GlobalLinkageKind::ExternalLinkage;
|
|
if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice &&
|
|
!getLangOpts().GPURelocatableDeviceCode)
|
|
return dd->hasAttr<CUDAGlobalAttr>()
|
|
? cir::GlobalLinkageKind::ExternalLinkage
|
|
: cir::GlobalLinkageKind::InternalLinkage;
|
|
return cir::GlobalLinkageKind::WeakODRLinkage;
|
|
}
|
|
|
|
// C++ doesn't have tentative definitions and thus cannot have common
|
|
// linkage.
|
|
if (!getLangOpts().CPlusPlus && isa<VarDecl>(dd) &&
|
|
!isVarDeclStrongDefinition(astContext, *this, cast<VarDecl>(dd),
|
|
getCodeGenOpts().NoCommon)) {
|
|
errorNYI(dd->getBeginLoc(), "common linkage", dd->getDeclKindName());
|
|
return cir::GlobalLinkageKind::CommonLinkage;
|
|
}
|
|
|
|
// selectany symbols are externally visible, so use weak instead of
|
|
// linkonce. MSVC optimizes away references to const selectany globals, so
|
|
// all definitions should be the same and ODR linkage should be used.
|
|
// http://msdn.microsoft.com/en-us/library/5tkz6s71.aspx
|
|
if (dd->hasAttr<SelectAnyAttr>())
|
|
return cir::GlobalLinkageKind::WeakODRLinkage;
|
|
|
|
// Otherwise, we have strong external linkage.
|
|
assert(linkage == GVA_StrongExternal);
|
|
return cir::GlobalLinkageKind::ExternalLinkage;
|
|
}
|
|
|
|
cir::GlobalLinkageKind
|
|
CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant) {
|
|
assert(!isConstant && "constant variables NYI");
|
|
GVALinkage linkage = astContext.GetGVALinkageForVariable(vd);
|
|
return getCIRLinkageForDeclarator(vd, linkage, isConstant);
|
|
}
|
|
|
|
// Emit code for a single top level declaration.
|
|
void CIRGenModule::emitTopLevelDecl(Decl *decl) {
|
|
|
|
// Ignore dependent declarations.
|
|
if (decl->isTemplated())
|
|
return;
|
|
|
|
switch (decl->getKind()) {
|
|
default:
|
|
errorNYI(decl->getBeginLoc(), "declaration of kind",
|
|
decl->getDeclKindName());
|
|
break;
|
|
|
|
case Decl::Function: {
|
|
auto *fd = cast<FunctionDecl>(decl);
|
|
// Consteval functions shouldn't be emitted.
|
|
if (!fd->isConsteval())
|
|
emitGlobal(fd);
|
|
break;
|
|
}
|
|
|
|
case Decl::Var: {
|
|
auto *vd = cast<VarDecl>(decl);
|
|
emitGlobal(vd);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd,
|
|
mlir::Type funcType, bool forVTable,
|
|
bool dontDefer,
|
|
ForDefinition_t isForDefinition) {
|
|
assert(!cast<FunctionDecl>(gd.getDecl())->isConsteval() &&
|
|
"consteval function should never be emitted");
|
|
|
|
if (!funcType) {
|
|
const auto *fd = cast<FunctionDecl>(gd.getDecl());
|
|
funcType = convertType(fd->getType());
|
|
}
|
|
|
|
cir::FuncOp func = getOrCreateCIRFunction(
|
|
cast<NamedDecl>(gd.getDecl())->getIdentifier()->getName(), funcType, gd,
|
|
forVTable, dontDefer, /*isThunk=*/false, isForDefinition);
|
|
return func;
|
|
}
|
|
|
|
cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
|
|
StringRef mangledName, mlir::Type funcType, GlobalDecl gd, bool forVTable,
|
|
bool dontDefer, bool isThunk, ForDefinition_t isForDefinition,
|
|
mlir::ArrayAttr extraAttrs) {
|
|
auto *funcDecl = llvm::cast_or_null<FunctionDecl>(gd.getDecl());
|
|
bool invalidLoc = !funcDecl ||
|
|
funcDecl->getSourceRange().getBegin().isInvalid() ||
|
|
funcDecl->getSourceRange().getEnd().isInvalid();
|
|
cir::FuncOp funcOp = createCIRFunction(
|
|
invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()),
|
|
mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
|
|
return funcOp;
|
|
}
|
|
|
|
cir::FuncOp
|
|
CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
|
|
cir::FuncType funcType,
|
|
const clang::FunctionDecl *funcDecl) {
|
|
cir::FuncOp func;
|
|
{
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
|
|
func = builder.create<cir::FuncOp>(loc, name, funcType);
|
|
theModule.push_back(func);
|
|
}
|
|
return func;
|
|
}
|
|
|
|
mlir::Type CIRGenModule::convertType(QualType type) {
|
|
return genTypes.convertType(type);
|
|
}
|
|
|
|
bool CIRGenModule::verifyModule() const {
|
|
// Verify the module after we have finished constructing it, this will
|
|
// check the structural properties of the IR and invoke any specific
|
|
// verifiers we have on the CIR operations.
|
|
return mlir::verify(theModule).succeeded();
|
|
}
|
|
|
|
DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc,
|
|
llvm::StringRef feature) {
|
|
unsigned diagID = diags.getCustomDiagID(
|
|
DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0");
|
|
return diags.Report(loc, diagID) << feature;
|
|
}
|
|
|
|
DiagnosticBuilder CIRGenModule::errorNYI(SourceRange loc,
|
|
llvm::StringRef feature) {
|
|
return errorNYI(loc.getBegin(), feature) << loc;
|
|
}
|