https://github.com/mmha created https://github.com/llvm/llvm-project/pull/129072
This change implements variable linkage types in ClangIR except for common linkage which requires Comdat support. >From b9453787a96008cfe347c2ae2d027d5de12871ae Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Thu, 27 Feb 2025 16:49:57 +0100 Subject: [PATCH] [CIR] Upstream global variable linkage types This change implements variable linkage types in ClangIR except for common linkage which requires Comdat support. --- .../include/clang/CIR/Dialect/IR/CIRDialect.h | 2 + clang/include/clang/CIR/Dialect/IR/CIROps.td | 83 ++++++- .../clang/CIR/Dialect/IR/CIROpsEnums.h | 119 +++++++++++ .../clang/CIR/Dialect/IR/CMakeLists.txt | 4 +- .../clang/CIR/Interfaces/CIROpInterfaces.h | 29 +++ .../clang/CIR/Interfaces/CIROpInterfaces.td | 63 ++++++ .../clang/CIR/Interfaces/CMakeLists.txt | 9 + clang/include/clang/CIR/MissingFeatures.h | 4 + clang/lib/CIR/CodeGen/CIRGenModule.cpp | 202 +++++++++++++++++- clang/lib/CIR/CodeGen/CIRGenModule.h | 12 ++ clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 8 +- clang/lib/CIR/Dialect/IR/CMakeLists.txt | 3 +- clang/lib/CIR/FrontendAction/CMakeLists.txt | 4 + clang/lib/CIR/Interfaces/CIROpInterfaces.cpp | 22 ++ clang/lib/CIR/Interfaces/CMakeLists.txt | 4 +- .../CIR/Lowering/DirectToLLVM/CMakeLists.txt | 5 + clang/test/CIR/global-var-simple.cpp | 64 +++--- clang/test/CIR/global_var_linkage.cpp | 11 + 19 files changed, 608 insertions(+), 41 deletions(-) create mode 100644 clang/include/clang/CIR/Dialect/IR/CIROpsEnums.h create mode 100644 clang/include/clang/CIR/Interfaces/CIROpInterfaces.h create mode 100644 clang/include/clang/CIR/Interfaces/CIROpInterfaces.td create mode 100644 clang/lib/CIR/Interfaces/CIROpInterfaces.cpp create mode 100644 clang/test/CIR/global_var_linkage.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h index 683176b139ca4..0684cf5034f5d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h @@ -28,6 +28,8 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIROpsDialect.h.inc" +#include "clang/CIR/Dialect/IR/CIROpsEnums.h" +#include "clang/CIR/Interfaces/CIROpInterfaces.h" // TableGen'erated files for MLIR dialects require that a macro be defined when // they are included. GET_OP_CLASSES tells the file to define the classes for diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index f9ce38588e436..7c5d339130ec5 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -18,6 +18,8 @@ include "clang/CIR/Dialect/IR/CIRDialect.td" include "clang/CIR/Dialect/IR/CIRTypes.td" include "clang/CIR/Dialect/IR/CIRAttrs.td" +include "clang/CIR/Interfaces/CIROpInterfaces.td" + include "mlir/IR/BuiltinAttributeInterfaces.td" include "mlir/IR/EnumAttr.td" include "mlir/IR/SymbolInterfaces.td" @@ -278,6 +280,59 @@ def ScopeOp : CIR_Op<"scope", [ // GlobalOp //===----------------------------------------------------------------------===// +// Linkage types. This is currently a replay of llvm/IR/GlobalValue.h, this is +// currently handy as part of forwarding appropriate linkage types for LLVM +// lowering, specially useful for C++ support. + +// Externally visible function +def Global_ExternalLinkage : + I32EnumAttrCase<"ExternalLinkage", 0, "external">; +// Available for inspection, not emission. +def Global_AvailableExternallyLinkage : + I32EnumAttrCase<"AvailableExternallyLinkage", 1, "available_externally">; +// Keep one copy of function when linking (inline) +def Global_LinkOnceAnyLinkage : + I32EnumAttrCase<"LinkOnceAnyLinkage", 2, "linkonce">; +// Same, but only replaced by something equivalent. +def Global_LinkOnceODRLinkage : + I32EnumAttrCase<"LinkOnceODRLinkage", 3, "linkonce_odr">; +// Keep one copy of named function when linking (weak) +def Global_WeakAnyLinkage : + I32EnumAttrCase<"WeakAnyLinkage", 4, "weak">; +// Same, but only replaced by something equivalent. +def Global_WeakODRLinkage : + I32EnumAttrCase<"WeakODRLinkage", 5, "weak_odr">; +// TODO: should we add something like appending linkage too? +// Special purpose, only applies to global arrays +// def Global_AppendingLinkage : +// I32EnumAttrCase<"AppendingLinkage", 6, "appending">; +// Rename collisions when linking (static functions). +def Global_InternalLinkage : + I32EnumAttrCase<"InternalLinkage", 7, "internal">; +// Like Internal, but omit from symbol table, prefix it with +// "cir_" to prevent clash with MLIR's symbol "private". +def Global_PrivateLinkage : + I32EnumAttrCase<"PrivateLinkage", 8, "cir_private">; +// ExternalWeak linkage description. +def Global_ExternalWeakLinkage : + I32EnumAttrCase<"ExternalWeakLinkage", 9, "extern_weak">; +// Tentative definitions. +def Global_CommonLinkage : + I32EnumAttrCase<"CommonLinkage", 10, "common">; + +/// An enumeration for the kinds of linkage for global values. +def GlobalLinkageKind : I32EnumAttr< + "GlobalLinkageKind", + "Linkage type/kind", + [Global_ExternalLinkage, Global_AvailableExternallyLinkage, + Global_LinkOnceAnyLinkage, Global_LinkOnceODRLinkage, + Global_WeakAnyLinkage, Global_WeakODRLinkage, + Global_InternalLinkage, Global_PrivateLinkage, + Global_ExternalWeakLinkage, Global_CommonLinkage + ]> { + let cppNamespace = "::cir"; +} + // TODO(CIR): For starters, cir.global has only name and type. The other // properties of a global variable will be added over time as more of ClangIR // is upstreamed. @@ -289,12 +344,19 @@ def GlobalOp : CIR_Op<"global"> { The backing memory for the variable is allocated statically and is described by the type of the variable. + + The `linkage` tracks C/C++ linkage types, currently very similar to LLVM's. }]; - let arguments = (ins SymbolNameAttr:$sym_name, TypeAttr:$sym_type, - OptionalAttr<AnyAttr>:$initial_value); + let arguments = (ins SymbolNameAttr:$sym_name, + TypeAttr:$sym_type, + Arg<GlobalLinkageKind, "linkage type">:$linkage, + OptionalAttr<AnyAttr>:$initial_value, + UnitAttr:$dsolocal); let assemblyFormat = [{ + $linkage + (`dsolocal` $dsolocal^)? $sym_name custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value) attr-dict @@ -303,12 +365,25 @@ def GlobalOp : CIR_Op<"global"> { let extraClassDeclaration = [{ bool isDeclaration() { return !getInitialValue(); } bool hasInitializer() { return !isDeclaration(); } + bool hasAvailableExternallyLinkage() { + return cir::isAvailableExternallyLinkage(getLinkage()); + } + bool hasInternalLinkage() { + return cir::isInternalLinkage(getLinkage()); + } + /// Whether the definition of this global may be replaced at link time. + bool isWeakForLinker() { return cir::isWeakForLinker(getLinkage()); } + bool isDSOLocal() { return getDsolocal(); } }]; let skipDefaultBuilders = 1; - let builders = [OpBuilder<(ins "llvm::StringRef":$sym_name, - "mlir::Type":$sym_type)>]; + let builders = [OpBuilder<(ins + "llvm::StringRef":$sym_name, + "mlir::Type":$sym_type, + // CIR defaults to external linkage. + CArg<"cir::GlobalLinkageKind", + "cir::GlobalLinkageKind::ExternalLinkage">:$linkage)>]; let hasVerifier = 1; } diff --git a/clang/include/clang/CIR/Dialect/IR/CIROpsEnums.h b/clang/include/clang/CIR/Dialect/IR/CIROpsEnums.h new file mode 100644 index 0000000000000..a1e32209555f3 --- /dev/null +++ b/clang/include/clang/CIR/Dialect/IR/CIROpsEnums.h @@ -0,0 +1,119 @@ +//===- CIROpsEnumsDialect.h - MLIR Dialect for CIR ----------------------*- C++ +//-*-===// +// +// 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 file declares the Target dialect for CIR in MLIR. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_CIR_CIROPSENUMS_H_ +#define MLIR_DIALECT_CIR_CIROPSENUMS_H_ + +#include "mlir/IR/BuiltinAttributes.h" +#include "clang/CIR/Dialect/IR/CIROpsEnums.h.inc" + +namespace cir { + +static bool isExternalLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::ExternalLinkage; +} +static bool isAvailableExternallyLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::AvailableExternallyLinkage; +} +static bool isLinkOnceAnyLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::LinkOnceAnyLinkage; +} +static bool isLinkOnceODRLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::LinkOnceODRLinkage; +} +static bool isLinkOnceLinkage(GlobalLinkageKind linkage) { + return isLinkOnceAnyLinkage(linkage) || isLinkOnceODRLinkage(linkage); +} +static bool isWeakAnyLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::WeakAnyLinkage; +} +static bool isWeakODRLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::WeakODRLinkage; +} +static bool isWeakLinkage(GlobalLinkageKind linkage) { + return isWeakAnyLinkage(linkage) || isWeakODRLinkage(linkage); +} +static bool isInternalLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::InternalLinkage; +} +static bool isPrivateLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::PrivateLinkage; +} +static bool isLocalLinkage(GlobalLinkageKind linkage) { + return isInternalLinkage(linkage) || isPrivateLinkage(linkage); +} +static bool isExternalWeakLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::ExternalWeakLinkage; +} +LLVM_ATTRIBUTE_UNUSED static bool isCommonLinkage(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::CommonLinkage; +} +LLVM_ATTRIBUTE_UNUSED static bool +isValidDeclarationLinkage(GlobalLinkageKind linkage) { + return isExternalWeakLinkage(linkage) || isExternalLinkage(linkage); +} + +/// Whether the definition of this global may be replaced by something +/// non-equivalent at link time. For example, if a function has weak linkage +/// then the code defining it may be replaced by different code. +LLVM_ATTRIBUTE_UNUSED static bool +isInterposableLinkage(GlobalLinkageKind linkage) { + switch (linkage) { + case GlobalLinkageKind::WeakAnyLinkage: + case GlobalLinkageKind::LinkOnceAnyLinkage: + case GlobalLinkageKind::CommonLinkage: + case GlobalLinkageKind::ExternalWeakLinkage: + return true; + + case GlobalLinkageKind::AvailableExternallyLinkage: + case GlobalLinkageKind::LinkOnceODRLinkage: + case GlobalLinkageKind::WeakODRLinkage: + // The above three cannot be overridden but can be de-refined. + + case GlobalLinkageKind::ExternalLinkage: + case GlobalLinkageKind::InternalLinkage: + case GlobalLinkageKind::PrivateLinkage: + return false; + } + llvm_unreachable("Fully covered switch above!"); +} + +/// Whether the definition of this global may be discarded if it is not used +/// in its compilation unit. +LLVM_ATTRIBUTE_UNUSED static bool +isDiscardableIfUnused(GlobalLinkageKind linkage) { + return isLinkOnceLinkage(linkage) || isLocalLinkage(linkage) || + isAvailableExternallyLinkage(linkage); +} + +/// Whether the definition of this global may be replaced at link time. NB: +/// Using this method outside of the code generators is almost always a +/// mistake: when working at the IR level use isInterposable instead as it +/// knows about ODR semantics. +LLVM_ATTRIBUTE_UNUSED static bool isWeakForLinker(GlobalLinkageKind linkage) { + return linkage == GlobalLinkageKind::WeakAnyLinkage || + linkage == GlobalLinkageKind::WeakODRLinkage || + linkage == GlobalLinkageKind::LinkOnceAnyLinkage || + linkage == GlobalLinkageKind::LinkOnceODRLinkage || + linkage == GlobalLinkageKind::CommonLinkage || + linkage == GlobalLinkageKind::ExternalWeakLinkage; +} + +LLVM_ATTRIBUTE_UNUSED static bool isValidLinkage(GlobalLinkageKind gl) { + return isExternalLinkage(gl) || isLocalLinkage(gl) || isWeakLinkage(gl) || + isLinkOnceLinkage(gl); +} + +} // namespace cir + +#endif // MLIR_DIALECT_CIR_CIROPSENUMS_H_ diff --git a/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt b/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt index 1fdbc24ba6b4a..39292fb541daa 100644 --- a/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt +++ b/clang/include/clang/CIR/Dialect/IR/CMakeLists.txt @@ -16,4 +16,6 @@ add_dependencies(mlir-headers MLIRCIROpsIncGen) mlir_tablegen(CIROpsAttributes.h.inc -gen-attrdef-decls) mlir_tablegen(CIROpsAttributes.cpp.inc -gen-attrdef-defs) -add_public_tablegen_target(MLIRCIRAttrsEnumsGen) +mlir_tablegen(CIROpsEnums.h.inc -gen-enum-decls) +mlir_tablegen(CIROpsEnums.cpp.inc -gen-enum-defs) +add_public_tablegen_target(MLIRCIREnumsGen) diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.h b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.h new file mode 100644 index 0000000000000..86064619af7db --- /dev/null +++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.h @@ -0,0 +1,29 @@ +//===- CIROpInterfaces.h - CIR Op Interfaces --------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_INTERFACES_CIR_OP_H_ +#define MLIR_INTERFACES_CIR_OP_H_ + +#include "mlir/IR/Attributes.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/Value.h" +#include "mlir/Interfaces/CallInterfaces.h" + +#include "clang/AST/Attr.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Mangle.h" +#include "clang/CIR/Dialect/IR/CIROpsEnums.h" + +namespace cir {} // namespace cir + +/// Include the generated interface declarations. +#include "clang/CIR/Interfaces/CIROpInterfaces.h.inc" + +namespace cir {} // namespace cir + +#endif // MLIR_INTERFACES_CIR_OP_H_ diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td new file mode 100644 index 0000000000000..b765a3e65f70b --- /dev/null +++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td @@ -0,0 +1,63 @@ +//===- CIROpInterfaces.td - CIR Op Interface Definitions --------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_CIR_OP_INTERFACES +#define MLIR_CIR_OP_INTERFACES + +include "mlir/IR/OpBase.td" + +let cppNamespace = "::cir" in { + def CIRGlobalValueInterface + : OpInterface<"CIRGlobalValueInterface"> { + + let methods = [ + InterfaceMethod<"", + "bool", "hasAvailableExternallyLinkage", (ins), [{}], + /*defaultImplementation=*/[{ return false; }] + >, + InterfaceMethod<"", + "bool", "hasLocalLinkage", (ins), [{}], + /*defaultImplementation=*/[{ + return cir::isLocalLinkage($_op.getLinkage()); + }] + >, + InterfaceMethod<"", + "bool", "hasExternalWeakLinkage", (ins), [{}], + /*defaultImplementation=*/[{ + return cir::isExternalWeakLinkage($_op.getLinkage()); + }] + >, + InterfaceMethod<"", + "bool", "isExternalLinkage", (ins), [{}], + /*defaultImplementation=*/[{ + return cir::isExternalLinkage($_op.getLinkage()); + }] + >, + InterfaceMethod<"", + "bool", "isDeclarationForLinker", (ins), [{}], + /*defaultImplementation=*/[{ + if ($_op.hasAvailableExternallyLinkage()) + return true; + return $_op.isDeclaration(); + }] + >, + InterfaceMethod<"", + "void", "setDSOLocal", (ins "bool":$val), [{}], + /*defaultImplementation=*/[{ + $_op.setDsolocal(val); + }] + >, + ]; + let extraClassDeclaration = [{ + bool canBenefitFromLocalAlias(); + }]; + } + +} // namespace cir + +#endif // MLIR_CIR_OP_INTERFACES diff --git a/clang/include/clang/CIR/Interfaces/CMakeLists.txt b/clang/include/clang/CIR/Interfaces/CMakeLists.txt index 1c90b6b5a23cb..e9929f6964605 100644 --- a/clang/include/clang/CIR/Interfaces/CMakeLists.txt +++ b/clang/include/clang/CIR/Interfaces/CMakeLists.txt @@ -3,6 +3,14 @@ # directory which is not the case for CIR (and also FIR, both have similar # workarounds). +function(add_clang_mlir_op_interface interface) + set(LLVM_TARGET_DEFINITIONS ${interface}.td) + mlir_tablegen(${interface}.h.inc -gen-op-interface-decls) + mlir_tablegen(${interface}.cpp.inc -gen-op-interface-defs) + add_public_tablegen_target(MLIR${interface}IncGen) + add_dependencies(mlir-generic-headers MLIR${interface}IncGen) +endfunction() + function(add_clang_mlir_type_interface interface) set(LLVM_TARGET_DEFINITIONS ${interface}.td) mlir_tablegen(${interface}.h.inc -gen-type-interface-decls) @@ -11,4 +19,5 @@ function(add_clang_mlir_type_interface interface) add_dependencies(mlir-generic-headers MLIR${interface}IncGen) endfunction() +add_clang_mlir_op_interface(CIROpInterfaces) add_clang_mlir_type_interface(CIRFPTypeInterface) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index d4fcd52e7e6e3..0d0083850eb70 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -36,6 +36,10 @@ struct MissingFeatures { static bool opGlobalConstant() { return false; } static bool opGlobalAlignment() { return false; } static bool opGlobalLinkage() { return false; } + + static bool supportIFuncAttr() { return false; } + static bool supportVisibility() { return false; } + static bool supportComdat() { return false; } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index d8acc99e550ad..8eb08228075ef 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -18,6 +18,7 @@ #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" @@ -31,7 +32,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, const clang::CodeGenOptions &cgo, DiagnosticsEngine &diags) : builder(mlirContext, *this), astContext(astContext), - langOpts(astContext.getLangOpts()), + langOpts(astContext.getLangOpts()), codeGenOpts(cgo), theModule{mlir::ModuleOp::create(mlir::UnknownLoc::get(&mlirContext))}, diags(diags), target(astContext.getTargetInfo()), genTypes(*this) { @@ -176,6 +177,18 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, } 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(), @@ -210,6 +223,193 @@ void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd, 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) { diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index bf3a4d1130f15..ba81eb9725ee6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -22,8 +22,10 @@ #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" +#include "clang/AST/Decl.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Dialect/IR/CIROpsEnums.h" #include "llvm/ADT/StringRef.h" #include "llvm/TargetParser/Triple.h" @@ -61,6 +63,8 @@ class CIRGenModule : public CIRGenTypeCache { const clang::LangOptions &langOpts; + const clang::CodeGenOptions &codeGenOpts; + /// A "module" matches a c/cpp source file: containing a list of functions. mlir::ModuleOp theModule; @@ -74,6 +78,7 @@ class CIRGenModule : public CIRGenTypeCache { mlir::ModuleOp getModule() const { return theModule; } CIRGenBuilderTy &getBuilder() { return builder; } clang::ASTContext &getASTContext() const { return astContext; } + const clang::CodeGenOptions &getCodeGenOpts() const { return codeGenOpts; } CIRGenTypes &getTypes() { return genTypes; } const clang::LangOptions &getLangOpts() const { return langOpts; } mlir::MLIRContext &getMLIRContext() { return *builder.getContext(); } @@ -118,6 +123,13 @@ class CIRGenModule : public CIRGenTypeCache { const llvm::Triple &getTriple() const { return target.getTriple(); } + cir::GlobalLinkageKind getCIRLinkageForDeclarator(const DeclaratorDecl *dd, + GVALinkage linkage, + bool isConstantVariable); + + cir::GlobalLinkageKind getCIRLinkageVarDefinition(const VarDecl *vd, + bool isConstant); + /// Helpers to emit "not yet implemented" error diagnostics DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef); diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 5602efae1ba41..af473194662c8 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -16,6 +16,7 @@ add_clang_library(clangCIR DEPENDS MLIRCIR + MLIRCIROpInterfacesIncGen ${dialect_libs} LINK_LIBS diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 3f1be930d71e5..539fa9d9518d2 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -18,6 +18,7 @@ #include "mlir/Support/LogicalResult.h" #include "clang/CIR/Dialect/IR/CIROpsDialect.cpp.inc" +#include "clang/CIR/Dialect/IR/CIROpsEnums.cpp.inc" using namespace mlir; using namespace cir; @@ -279,11 +280,16 @@ mlir::LogicalResult cir::GlobalOp::verify() { } void cir::GlobalOp::build(OpBuilder &odsBuilder, OperationState &odsState, - llvm::StringRef sym_name, mlir::Type sym_type) { + llvm::StringRef sym_name, mlir::Type sym_type, + cir::GlobalLinkageKind linkage) { odsState.addAttribute(getSymNameAttrName(odsState.name), odsBuilder.getStringAttr(sym_name)); odsState.addAttribute(getSymTypeAttrName(odsState.name), mlir::TypeAttr::get(sym_type)); + + cir::GlobalLinkageKindAttr linkageAttr = + cir::GlobalLinkageKindAttr::get(odsBuilder.getContext(), linkage); + odsState.addAttribute(getLinkageAttrName(odsState.name), linkageAttr); } static void printGlobalOpTypeAndInitialValue(OpAsmPrinter &p, cir::GlobalOp op, diff --git a/clang/lib/CIR/Dialect/IR/CMakeLists.txt b/clang/lib/CIR/Dialect/IR/CMakeLists.txt index baf8bff185221..4d2f21925119e 100644 --- a/clang/lib/CIR/Dialect/IR/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/IR/CMakeLists.txt @@ -5,7 +5,8 @@ add_clang_library(MLIRCIR DEPENDS MLIRCIROpsIncGen - MLIRCIRAttrsEnumsGen + MLIRCIREnumsGen + MLIRCIROpInterfacesIncGen LINK_LIBS PUBLIC MLIRIR diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt index ac2b857239d07..6d5a8758468f6 100644 --- a/clang/lib/CIR/FrontendAction/CMakeLists.txt +++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt @@ -8,6 +8,10 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) add_clang_library(clangCIRFrontendAction CIRGenAction.cpp + DEPENDS + MLIRCIROpsIncGen + MLIRCIROpInterfacesIncGen + LINK_LIBS clangAST clangFrontend diff --git a/clang/lib/CIR/Interfaces/CIROpInterfaces.cpp b/clang/lib/CIR/Interfaces/CIROpInterfaces.cpp new file mode 100644 index 0000000000000..dfbbdc2fd2382 --- /dev/null +++ b/clang/lib/CIR/Interfaces/CIROpInterfaces.cpp @@ -0,0 +1,22 @@ +//====- CIROpInterfaces.cpp - Interface to AST Attributes ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +#include "clang/CIR/Interfaces/CIROpInterfaces.h" + +using namespace cir; + +/// Include the generated type qualifiers interfaces. +#include "clang/CIR/Interfaces/CIROpInterfaces.cpp.inc" + +#include "clang/CIR/MissingFeatures.h" + +bool CIRGlobalValueInterface::canBenefitFromLocalAlias() { + assert(!cir::MissingFeatures::supportIFuncAttr()); + assert(!cir::MissingFeatures::supportVisibility()); + assert(!cir::MissingFeatures::supportComdat()); + return false; +} diff --git a/clang/lib/CIR/Interfaces/CMakeLists.txt b/clang/lib/CIR/Interfaces/CMakeLists.txt index b826bf612cc35..2fe5714520b74 100644 --- a/clang/lib/CIR/Interfaces/CMakeLists.txt +++ b/clang/lib/CIR/Interfaces/CMakeLists.txt @@ -1,12 +1,14 @@ add_clang_library(MLIRCIRInterfaces + CIROpInterfaces.cpp CIRFPTypeInterface.cpp ADDITIONAL_HEADER_DIRS ${MLIR_MAIN_INCLUDE_DIR}/mlir/Interfaces DEPENDS - MLIRCIRAttrsEnumsGen + MLIRCIREnumsGen MLIRCIRFPTypeInterfaceIncGen + MLIRCIROpInterfacesIncGen LINK_LIBS ${dialect_libs} diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt index 3f74c79249a27..c11ecb82183d0 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt +++ b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt @@ -8,6 +8,11 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) add_clang_library(clangCIRLoweringDirectToLLVM LowerToLLVM.cpp + DEPENDS + MLIRCIREnumsGen + MLIRCIROpsIncGen + MLIRCIROpInterfacesIncGen + LINK_LIBS MLIRIR ${dialect_libs} diff --git a/clang/test/CIR/global-var-simple.cpp b/clang/test/CIR/global-var-simple.cpp index f8e233cd5fe33..585f1ed38390f 100644 --- a/clang/test/CIR/global-var-simple.cpp +++ b/clang/test/CIR/global-var-simple.cpp @@ -2,100 +2,100 @@ // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s char c; -// CHECK: cir.global @c : !cir.int<s, 8> +// CHECK: cir.global external @c : !cir.int<s, 8> signed char sc; -// CHECK: cir.global @sc : !cir.int<s, 8> +// CHECK: cir.global external @sc : !cir.int<s, 8> unsigned char uc; -// CHECK: cir.global @uc : !cir.int<u, 8> +// CHECK: cir.global external @uc : !cir.int<u, 8> short ss; -// CHECK: cir.global @ss : !cir.int<s, 16> +// CHECK: cir.global external @ss : !cir.int<s, 16> unsigned short us = 100; -// CHECK: cir.global @us = #cir.int<100> : !cir.int<u, 16> +// CHECK: cir.global external @us = #cir.int<100> : !cir.int<u, 16> int si = 42; -// CHECK: cir.global @si = #cir.int<42> : !cir.int<s, 32> +// CHECK: cir.global external @si = #cir.int<42> : !cir.int<s, 32> unsigned ui; -// CHECK: cir.global @ui : !cir.int<u, 32> +// CHECK: cir.global external @ui : !cir.int<u, 32> long sl; -// CHECK: cir.global @sl : !cir.int<s, 64> +// CHECK: cir.global external @sl : !cir.int<s, 64> unsigned long ul; -// CHECK: cir.global @ul : !cir.int<u, 64> +// CHECK: cir.global external @ul : !cir.int<u, 64> long long sll; -// CHECK: cir.global @sll : !cir.int<s, 64> +// CHECK: cir.global external @sll : !cir.int<s, 64> unsigned long long ull = 123456; -// CHECK: cir.global @ull = #cir.int<123456> : !cir.int<u, 64> +// CHECK: cir.global external @ull = #cir.int<123456> : !cir.int<u, 64> __int128 s128; -// CHECK: cir.global @s128 : !cir.int<s, 128> +// CHECK: cir.global external @s128 : !cir.int<s, 128> unsigned __int128 u128; -// CHECK: cir.global @u128 : !cir.int<u, 128> +// CHECK: cir.global external @u128 : !cir.int<u, 128> wchar_t wc; -// CHECK: cir.global @wc : !cir.int<s, 32> +// CHECK: cir.global external @wc : !cir.int<s, 32> char8_t c8; -// CHECK: cir.global @c8 : !cir.int<u, 8> +// CHECK: cir.global external @c8 : !cir.int<u, 8> char16_t c16; -// CHECK: cir.global @c16 : !cir.int<u, 16> +// CHECK: cir.global external @c16 : !cir.int<u, 16> char32_t c32; -// CHECK: cir.global @c32 : !cir.int<u, 32> +// CHECK: cir.global external @c32 : !cir.int<u, 32> _BitInt(20) sb20; -// CHECK: cir.global @sb20 : !cir.int<s, 20> +// CHECK: cir.global external @sb20 : !cir.int<s, 20> unsigned _BitInt(48) ub48; -// CHECK: cir.global @ub48 : !cir.int<u, 48> +// CHECK: cir.global external @ub48 : !cir.int<u, 48> bool boolfalse = false; // CHECK: cir.global @boolfalse = #false _Float16 f16; -// CHECK: cir.global @f16 : !cir.f16 +// CHECK: cir.global external @f16 : !cir.f16 __bf16 bf16; -// CHECK: cir.global @bf16 : !cir.bf16 +// CHECK: cir.global external @bf16 : !cir.bf16 float f; -// CHECK: cir.global @f : !cir.float +// CHECK: cir.global external @f : !cir.float double d = 1.25; -// CHECK: cir.global @d = #cir.fp<1.250000e+00> : !cir.double +// CHECK: cir.global external @d = #cir.fp<1.250000e+00> : !cir.double long double ld; -// CHECK: cir.global @ld : !cir.long_double<!cir.f80> +// CHECK: cir.global external @ld : !cir.long_double<!cir.f80> __float128 f128; -// CHECK: cir.global @f128 : !cir.f128 +// CHECK: cir.global external @f128 : !cir.f128 void *vp; -// CHECK: cir.global @vp : !cir.ptr<!cir.void> +// CHECK: cir.global external @vp : !cir.ptr<!cir.void> int *ip = 0; -// CHECK: cir.global @ip = #cir.ptr<null> : !cir.ptr<!cir.int<s, 32>> +// CHECK: cir.global external @ip = #cir.ptr<null> : !cir.ptr<!cir.int<s, 32>> double *dp; -// CHECK: cir.global @dp : !cir.ptr<!cir.double> +// CHECK: cir.global external @dp : !cir.ptr<!cir.double> char **cpp; -// CHECK: cir.global @cpp : !cir.ptr<!cir.ptr<!cir.int<s, 8>>> +// CHECK: cir.global external @cpp : !cir.ptr<!cir.ptr<!cir.int<s, 8>>> void (*fp)(); -// CHECK: cir.global @fp : !cir.ptr<!cir.func<()>> +// CHECK: cir.global external @fp : !cir.ptr<!cir.func<()>> int (*fpii)(int) = 0; -// CHECK: cir.global @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<(!cir.int<s, 32>) -> !cir.int<s, 32>>> +// CHECK: cir.global external @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<(!cir.int<s, 32>) -> !cir.int<s, 32>>> void (*fpvar)(int, ...); -// CHECK: cir.global @fpvar : !cir.ptr<!cir.func<(!cir.int<s, 32>, ...)>> +// CHECK: cir.global external @fpvar : !cir.ptr<!cir.func<(!cir.int<s, 32>, ...)>> diff --git a/clang/test/CIR/global_var_linkage.cpp b/clang/test/CIR/global_var_linkage.cpp new file mode 100644 index 0000000000000..18f17a3c2eb61 --- /dev/null +++ b/clang/test/CIR/global_var_linkage.cpp @@ -0,0 +1,11 @@ +// Linkage types of global variables +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s + +int aaaa; +// CHECK: cir.global external @aaaa +static int bbbb; +// CHECK: cir.global internal @bbbb +inline int cccc; +// CHECK: cir.global linkonce_odr @cccc +[[gnu::selectany]] int dddd; +// CHECK: cir.global weak_odr @dddd _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits