[flang-commits] [flang] [mlir] [flang][FIR] Represent fir.global linkage with a typed enum attribute (PR #220243)

Scott Manley via flang-commits flang-commits at lists.llvm.org
Tue Sep 1 06:14:06 PDT 2026


https://github.com/rscottmanley created https://github.com/llvm/llvm-project/pull/220243

Replace the string linkName on fir.global with fir::LinkageAttr / LinkageEnum so only the supported linkages can be used, and CodeGen can map them without string compares.

>From 2d7e9244b5591e5595524ab54ebc91211e83c334 Mon Sep 17 00:00:00 2001
From: Scott Manley <scmanley at nvidia.com>
Date: Tue, 1 Sep 2026 05:57:39 -0700
Subject: [PATCH] [flang][FIR] Represent fir.global linkage with a typed enum
 attribute

Replace the string linkName on fir.global with fir::LinkageAttr /
LinkageEnum so only the supported linkages can be used, and CodeGen
can map them without string compares.
---
 flang/include/flang/Lower/ConvertConstant.h   |  2 +-
 flang/include/flang/Lower/ConvertVariable.h   |  2 +-
 .../flang/Optimizer/Builder/FIRBuilder.h      | 34 +++++++++-----
 .../flang/Optimizer/Dialect/FIRAttr.td        | 47 +++++++++++++++++++
 .../include/flang/Optimizer/Dialect/FIROps.td | 10 ++--
 flang/lib/Lower/ConvertConstant.cpp           |  8 ++--
 flang/lib/Lower/ConvertVariable.cpp           | 24 +++++-----
 flang/lib/Lower/IO.cpp                        |  4 +-
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  2 +-
 flang/lib/Optimizer/Builder/FIRBuilder.cpp    |  4 +-
 .../Builder/Runtime/EnvironmentDefaults.cpp   |  2 +-
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       | 22 +++++----
 flang/lib/Optimizer/Dialect/FIROps.cpp        | 33 +++++++------
 .../Transforms/CUDA/CUFAddConstructor.cpp     |  2 +-
 .../CUFComputeSharedMemoryOffsetsAndSize.cpp  |  2 +-
 .../Transforms/CUDA/CUFDeviceGlobal.cpp       |  8 ++--
 .../Optimizer/Builder/FIRBuilderTest.cpp      | 13 ++---
 .../ACCDeclareGPUModuleInsertion.cpp          |  2 +-
 18 files changed, 137 insertions(+), 84 deletions(-)

diff --git a/flang/include/flang/Lower/ConvertConstant.h b/flang/include/flang/Lower/ConvertConstant.h
index dd19507d4c005..df903783471eb 100644
--- a/flang/include/flang/Lower/ConvertConstant.h
+++ b/flang/include/flang/Lower/ConvertConstant.h
@@ -63,7 +63,7 @@ fir::ExtendedValue convertConstant(Fortran::lower::AbstractConverter &converter,
 fir::GlobalOp tryCreatingDenseGlobal(fir::FirOpBuilder &builder,
                                      mlir::Location loc, mlir::Type symTy,
                                      llvm::StringRef globalName,
-                                     mlir::StringAttr linkage, bool isConst,
+                                     fir::LinkageAttr linkage, bool isConst,
                                      const Fortran::lower::SomeExpr &initExpr,
                                      cuf::DataAttributeAttr dataAttr = {},
                                      bool setDefaultAlignment = true);
diff --git a/flang/include/flang/Lower/ConvertVariable.h b/flang/include/flang/Lower/ConvertVariable.h
index a4ad984579410..c8f117e63040f 100644
--- a/flang/include/flang/Lower/ConvertVariable.h
+++ b/flang/include/flang/Lower/ConvertVariable.h
@@ -136,7 +136,7 @@ mlir::Value genInitialDataTarget(Fortran::lower::AbstractConverter &,
 /// Create the global op and its init if it has one
 fir::GlobalOp defineGlobal(Fortran::lower::AbstractConverter &converter,
                            const Fortran::lower::pft::Variable &var,
-                           llvm::StringRef globalName, mlir::StringAttr linkage,
+                           llvm::StringRef globalName, fir::LinkageAttr linkage,
                            cuf::DataAttributeAttr dataAttr = {});
 
 /// Generate address \p addr inside an initializer.
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index 22d0a6f7febbc..a29e3bf7063a5 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -340,7 +340,7 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
   /// Create a global value.
   fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type,
                              llvm::StringRef name,
-                             mlir::StringAttr linkage = {},
+                             fir::LinkageAttr linkage = {},
                              mlir::Attribute value = {}, bool isConst = false,
                              bool isTarget = false,
                              cuf::DataAttributeAttr dataAttr = {},
@@ -349,14 +349,14 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
   fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type,
                              llvm::StringRef name, bool isConst, bool isTarget,
                              std::function<void(FirOpBuilder &)> bodyBuilder,
-                             mlir::StringAttr linkage = {},
+                             fir::LinkageAttr linkage = {},
                              cuf::DataAttributeAttr dataAttr = {},
                              bool setDefaultAlignment = true);
 
   /// Create a global constant (read-only) value.
   fir::GlobalOp createGlobalConstant(mlir::Location loc, mlir::Type type,
                                      llvm::StringRef name,
-                                     mlir::StringAttr linkage = {},
+                                     fir::LinkageAttr linkage = {},
                                      mlir::Attribute value = {}) {
     return createGlobal(loc, type, name, linkage, value, /*isConst=*/true,
                         /*isTarget=*/false);
@@ -366,7 +366,7 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
   createGlobalConstant(mlir::Location loc, mlir::Type type,
                        llvm::StringRef name,
                        std::function<void(FirOpBuilder &)> bodyBuilder,
-                       mlir::StringAttr linkage = {}) {
+                       fir::LinkageAttr linkage = {}) {
     return createGlobal(loc, type, name, /*isConst=*/true, /*isTarget=*/false,
                         bodyBuilder, linkage);
   }
@@ -383,24 +383,32 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
   // Linkage helpers (inline). The default linkage is external.
   //===--------------------------------------------------------------------===//
 
-  static mlir::StringAttr createCommonLinkage(mlir::MLIRContext *context) {
-    return mlir::StringAttr::get(context, "common");
+  static fir::LinkageAttr createCommonLinkage(mlir::MLIRContext *context) {
+    return fir::LinkageAttr::get(context, fir::LinkageEnum::Common);
   }
-  mlir::StringAttr createCommonLinkage() {
+  fir::LinkageAttr createCommonLinkage() {
     return createCommonLinkage(getContext());
   }
 
-  mlir::StringAttr createExternalLinkage() { return getStringAttr("external"); }
+  fir::LinkageAttr createExternalLinkage() {
+    return fir::LinkageAttr::get(getContext(), fir::LinkageEnum::External);
+  }
 
-  mlir::StringAttr createInternalLinkage() { return getStringAttr("internal"); }
+  fir::LinkageAttr createInternalLinkage() {
+    return fir::LinkageAttr::get(getContext(), fir::LinkageEnum::Internal);
+  }
 
-  mlir::StringAttr createLinkOnceLinkage() { return getStringAttr("linkonce"); }
+  fir::LinkageAttr createLinkOnceLinkage() {
+    return fir::LinkageAttr::get(getContext(), fir::LinkageEnum::Linkonce);
+  }
 
-  mlir::StringAttr createLinkOnceODRLinkage() {
-    return getStringAttr("linkonce_odr");
+  fir::LinkageAttr createLinkOnceODRLinkage() {
+    return fir::LinkageAttr::get(getContext(), fir::LinkageEnum::LinkonceODR);
   }
 
-  mlir::StringAttr createWeakLinkage() { return getStringAttr("weak"); }
+  fir::LinkageAttr createWeakLinkage() {
+    return fir::LinkageAttr::get(getContext(), fir::LinkageEnum::Weak);
+  }
 
   /// Get a function by name. If the function exists in the current module, it
   /// is returned. Otherwise, a null FuncOp is returned.
diff --git a/flang/include/flang/Optimizer/Dialect/FIRAttr.td b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
index c53a2b869e968..cfe8e96e8e4a5 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRAttr.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRAttr.td
@@ -15,10 +15,57 @@
 
 include "flang/Optimizer/Dialect/FIRDialect.td"
 include "mlir/Dialect/OpenACC/OpenACCAttributes.td"
+include "mlir/IR/CommonAttrConstraints.td"
 include "mlir/IR/EnumAttr.td"
 
 class fir_Attr<string name> : AttrDef<FIROpsDialect, name>;
 
+//===----------------------------------------------------------------------===//
+// Linkage
+//===----------------------------------------------------------------------===//
+
+// Linkage attribute used on fir.global. This is a subset of LLVM linkage
+// types currently supported by FIR. The names match the keywords in the
+// custom assembly form of fir.global. External is the default when omitted.
+def FIR_LinkageExternal
+    : I64EnumAttrCase<"External", 0, "external">;
+def FIR_LinkageInternal
+    : I64EnumAttrCase<"Internal", 1, "internal">;
+def FIR_LinkageLinkonce
+    : I64EnumAttrCase<"Linkonce", 2, "linkonce">;
+def FIR_LinkageLinkonceODR
+    : I64EnumAttrCase<"LinkonceODR", 3, "linkonce_odr">;
+def FIR_LinkageWeak
+    : I64EnumAttrCase<"Weak", 4, "weak">;
+def FIR_LinkageCommon
+    : I64EnumAttrCase<"Common", 5, "common">;
+
+def fir_LinkageEnum : I64EnumAttr<
+    "LinkageEnum",
+    "FIR linkage types",
+    [FIR_LinkageExternal, FIR_LinkageInternal, FIR_LinkageLinkonce,
+     FIR_LinkageLinkonceODR, FIR_LinkageWeak, FIR_LinkageCommon]> {
+  let cppNamespace = "::fir";
+  let genSpecializedAttr = 0;
+}
+
+def fir_LinkageAttr : fir_Attr<"Linkage"> {
+  let mnemonic = "linkage";
+  let parameters = (ins "LinkageEnum":$linkage);
+  let assemblyFormat = "`<` $linkage `>`";
+}
+
+def fir_Linkage : DialectAttr<
+    FIROpsDialect,
+    CPred<"::llvm::isa<::fir::LinkageAttr>($_self)">,
+    "FIR linkage specification"> {
+  let storageType = "::fir::LinkageAttr";
+  let returnType = "::fir::LinkageEnum";
+  let convertFromStorage = "$_self.getLinkage()";
+  let constBuilderCall =
+          "::fir::LinkageAttr::get($_builder.getContext(), $0)";
+}
+
 def FIRnoAttributes  : I32BitEnumAttrCaseNone<"None">;
 def FIRallocatable  : I32BitEnumAttrCaseBit<"allocatable", 0>;
 def FIRasynchronous : I32BitEnumAttrCaseBit<"asynchronous", 1>;
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index fd0b5b16c87ad..1a5c29a6e991e 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2920,7 +2920,7 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, SymbolName,
     OptionalAttr<AnyAttr>:$initVal,
     OptionalAttr<UnitAttr>:$constant,
     OptionalAttr<UnitAttr>:$target,
-    OptionalAttr<StrAttr>:$linkName,
+    OptionalAttr<fir_Linkage>:$linkage,
     OptionalAttr<cuf_DataAttributeAttr>:$data_attr,
     OptionalAttr<I64Attr>:$alignment,
     OptionalAttr<StrAttr>:$sym_visibility
@@ -2938,18 +2938,18 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, SymbolName,
       "bool":$isTarget, "mlir::Type":$type,
       CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
     OpBuilder<(ins "llvm::StringRef":$name, "mlir::Type":$type,
-      CArg<"mlir::StringAttr", "{}">:$linkage,
+      CArg<"fir::LinkageAttr", "{}">:$linkage,
       CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
     OpBuilder<(ins "llvm::StringRef":$name, "bool":$isConstant,
       "bool":$isTarget,
-      "mlir::Type":$type, CArg<"mlir::StringAttr", "{}">:$linkage,
+      "mlir::Type":$type, CArg<"fir::LinkageAttr", "{}">:$linkage,
       CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
     OpBuilder<(ins "llvm::StringRef":$name, "mlir::Type":$type,
-      "mlir::Attribute":$initVal, CArg<"mlir::StringAttr", "{}">:$linkage,
+      "mlir::Attribute":$initVal, CArg<"fir::LinkageAttr", "{}">:$linkage,
       CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
     OpBuilder<(ins "llvm::StringRef":$name, "bool":$isConstant,
       "bool":$isTarget, "mlir::Type":$type, "mlir::Attribute":$initVal,
-      CArg<"mlir::StringAttr", "{}">:$linkage,
+      CArg<"fir::LinkageAttr", "{}">:$linkage,
       CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>,
   ];
 
diff --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp
index 6525ce50f9fdc..f48dc9931e627 100644
--- a/flang/lib/Lower/ConvertConstant.cpp
+++ b/flang/lib/Lower/ConvertConstant.cpp
@@ -104,7 +104,7 @@ class DenseGlobalBuilder {
 public:
   static fir::GlobalOp
   tryCreating(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type symTy,
-              llvm::StringRef globalName, mlir::StringAttr linkage,
+              llvm::StringRef globalName, fir::LinkageAttr linkage,
               bool isConst, const Fortran::lower::SomeExpr &initExpr,
               cuf::DataAttributeAttr dataAttr, bool setDefaultAlignment) {
     DenseGlobalBuilder globalBuilder;
@@ -130,7 +130,7 @@ class DenseGlobalBuilder {
   template <Fortran::common::TypeCategory TC, int KIND>
   static fir::GlobalOp tryCreating(
       fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type symTy,
-      llvm::StringRef globalName, mlir::StringAttr linkage, bool isConst,
+      llvm::StringRef globalName, fir::LinkageAttr linkage, bool isConst,
       const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
           &constant,
       cuf::DataAttributeAttr dataAttr, bool setDefaultAlignment = true) {
@@ -202,7 +202,7 @@ class DenseGlobalBuilder {
   fir::GlobalOp tryCreatingGlobal(fir::FirOpBuilder &builder,
                                   mlir::Location loc, mlir::Type symTy,
                                   llvm::StringRef globalName,
-                                  mlir::StringAttr linkage, bool isConst,
+                                  fir::LinkageAttr linkage, bool isConst,
                                   cuf::DataAttributeAttr dataAttr,
                                   bool setDefaultAlignment) const {
     // Not a "trivial" intrinsic constant array, or empty array.
@@ -228,7 +228,7 @@ class DenseGlobalBuilder {
 
 fir::GlobalOp Fortran::lower::tryCreatingDenseGlobal(
     fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type symTy,
-    llvm::StringRef globalName, mlir::StringAttr linkage, bool isConst,
+    llvm::StringRef globalName, fir::LinkageAttr linkage, bool isConst,
     const Fortran::lower::SomeExpr &initExpr, cuf::DataAttributeAttr dataAttr,
     bool setDefaultAlignment) {
   return DenseGlobalBuilder::tryCreating(builder, loc, symTy, globalName,
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index a808905850922..8438b39c990a9 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -198,7 +198,7 @@ static void attachAccDeclareAttribute(fir::FirOpBuilder &builder,
 static fir::GlobalOp declareGlobal(Fortran::lower::AbstractConverter &converter,
                                    const Fortran::lower::pft::Variable &var,
                                    llvm::StringRef globalName,
-                                   mlir::StringAttr linkage) {
+                                   fir::LinkageAttr linkage) {
   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
   if (fir::GlobalOp global = builder.getNamedGlobal(globalName))
     return global;
@@ -497,7 +497,7 @@ createGlobalInitialization(fir::FirOpBuilder &builder, fir::GlobalOp global,
 fir::GlobalOp Fortran::lower::defineGlobal(
     Fortran::lower::AbstractConverter &converter,
     const Fortran::lower::pft::Variable &var, llvm::StringRef globalName,
-    mlir::StringAttr linkage, cuf::DataAttributeAttr dataAttr) {
+    fir::LinkageAttr linkage, cuf::DataAttributeAttr dataAttr) {
   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
   const Fortran::semantics::Symbol &sym = var.getSymbol();
   mlir::Location loc = genLocation(converter, sym);
@@ -628,7 +628,7 @@ fir::GlobalOp Fortran::lower::defineGlobal(
       // with no other definitions, and to never link the resulting module
       // object file.
       if (sym.attrs().test(Fortran::semantics::Attr::BIND_C))
-        global.setLinkName(builder.createCommonLinkage());
+        global.setLinkageAttr(builder.createCommonLinkage());
       createGlobalInitialization(
           builder, global, [&](fir::FirOpBuilder &builder) {
             mlir::Value initValue;
@@ -647,7 +647,7 @@ fir::GlobalOp Fortran::lower::defineGlobal(
 }
 
 /// Return linkage attribute for \p var.
-static mlir::StringAttr
+static fir::LinkageAttr
 getLinkageAttribute(Fortran::lower::AbstractConverter &converter,
                     const Fortran::lower::pft::Variable &var) {
   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
@@ -680,7 +680,7 @@ static void instantiateGlobal(Fortran::lower::AbstractConverter &converter,
   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
   std::string globalName = converter.mangleName(sym);
   mlir::Location loc = genLocation(converter, sym);
-  mlir::StringAttr linkage = getLinkageAttribute(converter, var);
+  fir::LinkageAttr linkage = getLinkageAttribute(converter, var);
   fir::GlobalOp global;
 
   if (Fortran::evaluate::IsCoarray(sym)) {
@@ -905,7 +905,7 @@ genInlinedInitWithMemcpy(Fortran::lower::AbstractConverter &converter,
       (converter.mangleName(*declTy->AsDerived()) + fir::kNameSeparator +
        fir::kDerivedTypeInitSuffix)
           .str());
-  mlir::StringAttr linkage = builder.createInternalLinkage();
+  fir::LinkageAttr linkage = builder.createInternalLinkage();
   fir::GlobalOp global = builder.getNamedGlobal(globalName);
   if (!global && details->init()) {
     global = builder.createGlobal(symLoc, symTy, globalName, linkage,
@@ -1398,7 +1398,7 @@ getAggregateType(Fortran::lower::AbstractConverter &converter,
 static fir::GlobalOp defineGlobalAggregateStore(
     Fortran::lower::AbstractConverter &converter,
     const Fortran::lower::pft::Variable::AggregateStore &aggregate,
-    llvm::StringRef aggName, mlir::StringAttr linkage) {
+    llvm::StringRef aggName, fir::LinkageAttr linkage) {
   assert(aggregate.isGlobal() && "not a global interval");
   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
   fir::GlobalOp global = builder.getNamedGlobal(aggName);
@@ -1443,7 +1443,7 @@ static fir::GlobalOp defineGlobalAggregateStore(
 static fir::GlobalOp declareGlobalAggregateStore(
     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
     const Fortran::lower::pft::Variable::AggregateStore &aggregate,
-    llvm::StringRef aggName, mlir::StringAttr linkage) {
+    llvm::StringRef aggName, fir::LinkageAttr linkage) {
   assert(aggregate.isGlobal() && "not a global interval");
   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
   if (fir::GlobalOp global = builder.getNamedGlobal(aggName))
@@ -1467,7 +1467,7 @@ instantiateAggregateStore(Fortran::lower::AbstractConverter &converter,
   if (var.isGlobal()) {
     fir::GlobalOp global;
     auto &aggregate = var.getAggregateStore();
-    mlir::StringAttr linkage = getLinkageAttribute(converter, var);
+    fir::LinkageAttr linkage = getLinkageAttribute(converter, var);
     if (var.isModuleOrSubmoduleVariable()) {
       // A module global was or will be defined when lowering the module. Emit
       // only a declaration if the global does not exist at that point.
@@ -1668,7 +1668,7 @@ declareCommonBlock(Fortran::lower::AbstractConverter &converter,
   Fortran::semantics::MutableSymbolVector cmnBlkMems =
       getCommonMembersWithInitAliases(common);
   mlir::Location loc = converter.genLocation(common.name());
-  mlir::StringAttr linkage = builder.createCommonLinkage();
+  fir::LinkageAttr linkage = builder.createCommonLinkage();
   const auto *details =
       common.detailsIf<Fortran::semantics::CommonBlockDetails>();
   assert(details && "Expect CommonBlockDetails on the common symbol");
@@ -2708,7 +2708,7 @@ void Fortran::lower::defineModuleVariable(
     AbstractConverter &converter, const Fortran::lower::pft::Variable &var) {
   // Use empty linkage for module variables, which makes them available
   // for use in another unit.
-  mlir::StringAttr linkage = getLinkageAttribute(converter, var);
+  fir::LinkageAttr linkage = getLinkageAttribute(converter, var);
   if (!var.isGlobal())
     fir::emitFatalError(converter.getCurrentLocation(),
                         "attempting to lower module variable as local");
@@ -2845,7 +2845,7 @@ void Fortran::lower::createRuntimeTypeInfoGlobal(
     const Fortran::semantics::Symbol &typeInfoSym) {
   std::string globalName = converter.mangleName(typeInfoSym);
   auto var = Fortran::lower::pft::Variable(typeInfoSym, /*global=*/true);
-  mlir::StringAttr linkage = getLinkageAttribute(converter, var);
+  fir::LinkageAttr linkage = getLinkageAttribute(converter, var);
   defineGlobal(converter, var, globalName, linkage);
 }
 
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 490db8cc1d1ba..611b6918fc46c 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -262,7 +262,7 @@ getNonTbpDefinedIoTableAddr(Fortran::lower::AbstractConverter &converter,
                                                        table.resultType(),
                                                        table.getSymbol()));
 
-  mlir::StringAttr linkOnce = builder.createLinkOnceLinkage();
+  fir::LinkageAttr linkOnce = builder.createLinkOnceLinkage();
   mlir::Type idxTy = builder.getIndexType();
   mlir::Type sizeTy =
       fir::runtime::getModel<std::size_t>()(builder.getContext());
@@ -426,7 +426,7 @@ getNamelistGroup(Fortran::lower::AbstractConverter &converter,
   const auto &details =
       symbol.GetUltimate().get<Fortran::semantics::NamelistDetails>();
   mlir::MLIRContext *context = builder.getContext();
-  mlir::StringAttr linkOnce = builder.createLinkOnceLinkage();
+  fir::LinkageAttr linkOnce = builder.createLinkOnceLinkage();
   mlir::Type idxTy = builder.getIndexType();
   mlir::Type sizeTy =
       fir::runtime::getModel<std::size_t>()(builder.getContext());
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 4563d997b33ae..f2ca5c172d11a 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1220,7 +1220,7 @@ static fir::GlobalOp globalInitialization(lower::AbstractConverter &converter,
                                           const lower::pft::Variable &var,
                                           mlir::Location currentLocation) {
   std::string globalName = converter.mangleName(sym);
-  mlir::StringAttr linkage = firOpBuilder.createInternalLinkage();
+  fir::LinkageAttr linkage = firOpBuilder.createInternalLinkage();
   return Fortran::lower::defineGlobal(converter, var, globalName, linkage);
 }
 
diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
index a84d4fd978e05..ee33c172494e9 100644
--- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp
+++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
@@ -457,7 +457,7 @@ void fir::FirOpBuilder::genStackRestore(mlir::Location loc,
 /// must have a unique name to identify and reference it.
 fir::GlobalOp fir::FirOpBuilder::createGlobal(
     mlir::Location loc, mlir::Type type, llvm::StringRef name,
-    mlir::StringAttr linkage, mlir::Attribute value, bool isConst,
+    fir::LinkageAttr linkage, mlir::Attribute value, bool isConst,
     bool isTarget, cuf::DataAttributeAttr dataAttr, bool setDefaultAlignment) {
   if (auto global = getNamedGlobal(name))
     return global;
@@ -485,7 +485,7 @@ fir::GlobalOp fir::FirOpBuilder::createGlobal(
 fir::GlobalOp fir::FirOpBuilder::createGlobal(
     mlir::Location loc, mlir::Type type, llvm::StringRef name, bool isConst,
     bool isTarget, std::function<void(FirOpBuilder &)> bodyBuilder,
-    mlir::StringAttr linkage, cuf::DataAttributeAttr dataAttr,
+    fir::LinkageAttr linkage, cuf::DataAttributeAttr dataAttr,
     bool setDefaultAlignment) {
   if (auto global = getNamedGlobal(name))
     return global;
diff --git a/flang/lib/Optimizer/Builder/Runtime/EnvironmentDefaults.cpp b/flang/lib/Optimizer/Builder/Runtime/EnvironmentDefaults.cpp
index fa3d00e8b844f..1c98f5066260e 100755
--- a/flang/lib/Optimizer/Builder/Runtime/EnvironmentDefaults.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/EnvironmentDefaults.cpp
@@ -20,7 +20,7 @@ mlir::Value fir::runtime::genEnvironmentDefaults(
       fir::NameUniquer::doGenerated("EnvironmentDefaults");
 
   mlir::MLIRContext *context = builder.getContext();
-  mlir::StringAttr linkOnce = builder.createLinkOnceLinkage();
+  fir::LinkageAttr linkOnce = builder.createLinkOnceLinkage();
   mlir::IntegerType intTy = builder.getIntegerType(8 * sizeof(int));
   fir::ReferenceType charRefTy =
       fir::ReferenceType::get(builder.getIntegerType(8));
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index fb98ca5fb5dcd..10ec85340fde6 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3739,7 +3739,7 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
     auto loc = global.getLoc();
     mlir::Attribute initAttr = global.getInitVal().value_or(mlir::Attribute());
     assert(attributeTypeIsCompatible(global.getContext(), initAttr, tyAttr));
-    auto linkage = convertLinkage(global.getLinkName());
+    auto linkage = convertLinkage(global.getLinkage());
     auto isConst = global.getConstant().has_value();
     mlir::SymbolRefAttr comdat;
     llvm::ArrayRef<mlir::NamedAttribute> attrs;
@@ -3856,22 +3856,24 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
     return mlir::success();
   }
 
-  // TODO: String comparisons should be avoided. Replace linkName with an
-  // enumeration.
   mlir::LLVM::Linkage
-  convertLinkage(std::optional<llvm::StringRef> optLinkage) const {
+  convertLinkage(std::optional<fir::LinkageEnum> optLinkage) const {
     if (optLinkage) {
-      auto name = *optLinkage;
-      if (name == "internal")
+      switch (*optLinkage) {
+      case fir::LinkageEnum::Internal:
         return mlir::LLVM::Linkage::Internal;
-      if (name == "linkonce")
+      case fir::LinkageEnum::Linkonce:
         return mlir::LLVM::Linkage::Linkonce;
-      if (name == "linkonce_odr")
+      case fir::LinkageEnum::LinkonceODR:
         return mlir::LLVM::Linkage::LinkonceODR;
-      if (name == "common")
+      case fir::LinkageEnum::Common:
         return mlir::LLVM::Linkage::Common;
-      if (name == "weak")
+      case fir::LinkageEnum::Weak:
         return mlir::LLVM::Linkage::Weak;
+      case fir::LinkageEnum::External:
+        return mlir::LLVM::Linkage::External;
+      }
+      return mlir::LLVM::Linkage::External;
     }
     return mlir::LLVM::Linkage::External;
   }
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 108f7fc793c61..0aa5fd60e2b52 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -2810,10 +2810,11 @@ mlir::ParseResult fir::GlobalOp::parse(mlir::OpAsmParser &parser,
   auto &builder = parser.getBuilder();
   if (mlir::succeeded(parser.parseOptionalKeyword(&linkage))) {
     if (fir::GlobalOp::verifyValidLinkage(linkage))
-      return mlir::failure();
-    mlir::StringAttr linkAttr = builder.getStringAttr(linkage);
-    result.addAttribute(fir::GlobalOp::getLinkNameAttrName(result.name),
-                        linkAttr);
+      return parser.emitError(parser.getCurrentLocation())
+             << "invalid linkage '" << linkage << "'";
+    auto linkEnum = fir::symbolizeLinkageEnum(linkage);
+    result.addAttribute(fir::GlobalOp::getLinkageAttrName(result.name),
+                        fir::LinkageAttr::get(builder.getContext(), *linkEnum));
   }
 
   // Parse the name as a symbol reference attribute.
@@ -2870,8 +2871,8 @@ mlir::ParseResult fir::GlobalOp::parse(mlir::OpAsmParser &parser,
 void fir::GlobalOp::print(mlir::OpAsmPrinter &p) {
   if (mlir::StringAttr visibility = getSymVisibilityAttr())
     p << ' ' << visibility.getValue();
-  if (getLinkName())
-    p << ' ' << *getLinkName();
+  if (std::optional<fir::LinkageEnum> link = getLinkage())
+    p << ' ' << fir::stringifyLinkageEnum(*link);
   p << ' ';
   p.printAttributeWithoutType(getSymrefAttr());
   if (auto val = getValueOrNull())
@@ -2880,7 +2881,7 @@ void fir::GlobalOp::print(mlir::OpAsmPrinter &p) {
   p.printOptionalAttrDict(
       (*this)->getAttrs(), /*elideAttrs=*/{
           getSymNameAttrName(), getSymrefAttrName(), getTypeAttrName(),
-          getConstantAttrName(), getTargetAttrName(), getLinkNameAttrName(),
+          getConstantAttrName(), getTargetAttrName(), getLinkageAttrName(),
           getInitValAttrName(), getSymVisibilityAttrName()});
   if (getOperation()->getAttr(getConstantAttrName()))
     p << " " << getConstantAttrName().strref();
@@ -2903,7 +2904,7 @@ void fir::GlobalOp::appendInitialValue(mlir::Operation *op) {
 void fir::GlobalOp::build(mlir::OpBuilder &builder,
                           mlir::OperationState &result, llvm::StringRef name,
                           bool isConstant, bool isTarget, mlir::Type type,
-                          mlir::Attribute initialVal, mlir::StringAttr linkage,
+                          mlir::Attribute initialVal, fir::LinkageAttr linkage,
                           llvm::ArrayRef<mlir::NamedAttribute> attrs) {
   result.addRegion();
   result.addAttribute(getTypeAttrName(result.name), mlir::TypeAttr::get(type));
@@ -2919,14 +2920,14 @@ void fir::GlobalOp::build(mlir::OpBuilder &builder,
   if (initialVal)
     result.addAttribute(getInitValAttrName(result.name), initialVal);
   if (linkage)
-    result.addAttribute(getLinkNameAttrName(result.name), linkage);
+    result.addAttribute(getLinkageAttrName(result.name), linkage);
   result.attributes.append(attrs.begin(), attrs.end());
 }
 
 void fir::GlobalOp::build(mlir::OpBuilder &builder,
                           mlir::OperationState &result, llvm::StringRef name,
                           mlir::Type type, mlir::Attribute initialVal,
-                          mlir::StringAttr linkage,
+                          fir::LinkageAttr linkage,
                           llvm::ArrayRef<mlir::NamedAttribute> attrs) {
   build(builder, result, name, /*isConstant=*/false, /*isTarget=*/false, type,
         {}, linkage, attrs);
@@ -2935,14 +2936,14 @@ void fir::GlobalOp::build(mlir::OpBuilder &builder,
 void fir::GlobalOp::build(mlir::OpBuilder &builder,
                           mlir::OperationState &result, llvm::StringRef name,
                           bool isConstant, bool isTarget, mlir::Type type,
-                          mlir::StringAttr linkage,
+                          fir::LinkageAttr linkage,
                           llvm::ArrayRef<mlir::NamedAttribute> attrs) {
   build(builder, result, name, isConstant, isTarget, type, {}, linkage, attrs);
 }
 
 void fir::GlobalOp::build(mlir::OpBuilder &builder,
                           mlir::OperationState &result, llvm::StringRef name,
-                          mlir::Type type, mlir::StringAttr linkage,
+                          mlir::Type type, fir::LinkageAttr linkage,
                           llvm::ArrayRef<mlir::NamedAttribute> attrs) {
   build(builder, result, name, /*isConstant=*/false, /*isTarget=*/false, type,
         {}, linkage, attrs);
@@ -2952,7 +2953,7 @@ void fir::GlobalOp::build(mlir::OpBuilder &builder,
                           mlir::OperationState &result, llvm::StringRef name,
                           bool isConstant, bool isTarget, mlir::Type type,
                           llvm::ArrayRef<mlir::NamedAttribute> attrs) {
-  build(builder, result, name, isConstant, isTarget, type, mlir::StringAttr{},
+  build(builder, result, name, isConstant, isTarget, type, fir::LinkageAttr{},
         attrs);
 }
 
@@ -2965,10 +2966,8 @@ void fir::GlobalOp::build(mlir::OpBuilder &builder,
 }
 
 mlir::ParseResult fir::GlobalOp::verifyValidLinkage(llvm::StringRef linkage) {
-  // Supporting only a subset of the LLVM linkage types for now
-  static const char *validNames[] = {"common", "internal", "linkonce",
-                                     "linkonce_odr", "weak"};
-  return mlir::success(llvm::is_contained(validNames, linkage));
+  // Supporting only a subset of the LLVM linkage types for now.
+  return mlir::success(fir::symbolizeLinkageEnum(linkage).has_value());
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp b/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
index 5c70342d1e0fa..aa393f2d2d9a8 100644
--- a/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
+++ b/flang/lib/Optimizer/Transforms/CUDA/CUFAddConstructor.cpp
@@ -63,7 +63,7 @@ static fir::GlobalOp createManagedPointerGlobal(fir::FirOpBuilder &builder,
   auto ptrGlobal = fir::GlobalOp::create(
       builder, globalOp.getLoc(), ptrGlobalName, /*isConstant=*/false,
       /*isTarget=*/false, ptrTy, initAttr,
-      /*linkName=*/builder.createInternalLinkage(), attrs);
+      /*linkage=*/builder.createInternalLinkage(), attrs);
 
   mlir::Region &region = ptrGlobal.getRegion();
   mlir::Block *block = builder.createBlock(&region);
diff --git a/flang/lib/Optimizer/Transforms/CUDA/CUFComputeSharedMemoryOffsetsAndSize.cpp b/flang/lib/Optimizer/Transforms/CUDA/CUFComputeSharedMemoryOffsetsAndSize.cpp
index bc6df0c9d60b7..dae873fbc0195 100644
--- a/flang/lib/Optimizer/Transforms/CUDA/CUFComputeSharedMemoryOffsetsAndSize.cpp
+++ b/flang/lib/Optimizer/Transforms/CUDA/CUFComputeSharedMemoryOffsetsAndSize.cpp
@@ -54,7 +54,7 @@ static void createSharedMemoryGlobal(fir::FirOpBuilder &builder,
   mlir::OpBuilder::InsertionGuard guard(builder);
   builder.setInsertionPointToEnd(gpuMod.getBody());
 
-  mlir::StringAttr linkage = isDynamic ? builder.createExternalLinkage()
+  fir::LinkageAttr linkage = isDynamic ? builder.createExternalLinkage()
                                        : builder.createInternalLinkage();
   llvm::SmallVector<mlir::NamedAttribute> attrs;
   auto globalOpName = mlir::OperationName(fir::GlobalOp::getOperationName(),
diff --git a/flang/lib/Optimizer/Transforms/CUDA/CUFDeviceGlobal.cpp b/flang/lib/Optimizer/Transforms/CUDA/CUFDeviceGlobal.cpp
index 5e24366c87ba4..a505126c56a93 100644
--- a/flang/lib/Optimizer/Transforms/CUDA/CUFDeviceGlobal.cpp
+++ b/flang/lib/Optimizer/Transforms/CUDA/CUFDeviceGlobal.cpp
@@ -252,7 +252,7 @@ class CUFDeviceGlobal : public fir::impl::CUFDeviceGlobalBase<CUFDeviceGlobal> {
       // Under -gpu=mem:unified, plain host module-scope variables (no
       // explicit CUF data attribute, not a constant) get a no-body
       // declaration in the GPU module: clear the body, init value, and
-      // linkName. With no linkName, the LLVM lowering uses the default
+      // linkage. With no linkage, the LLVM lowering uses the default
       // External linkage (see convertLinkage in CodeGen.cpp), so an
       // initializer-less global emits as `.extern .global ...` in PTX.
       // The host-side definition stays. CUFAddConstructor will emit
@@ -263,15 +263,15 @@ class CUFDeviceGlobal : public fir::impl::CUFDeviceGlobalBase<CUFDeviceGlobal> {
           !globalOp.getDataAttrAttr()) {
         clonedGlobal.getRegion().getBlocks().clear();
         clonedGlobal.removeInitValAttr();
-        clonedGlobal.removeLinkNameAttr();
+        clonedGlobal.removeLinkageAttr();
       }
       // Registered CUDA globals with internal linkage must have a visible
       // device symbol so runtime lookups (cudaGetSymbolAddress) can resolve
       // them. Drop internal linkage from the GPU clone so it uses default
       // external linkage.
       if (cuf::isRegisteredDeviceGlobal(globalOp) &&
-          globalOp.getLinkName() == "internal")
-        clonedGlobal.removeLinkNameAttr();
+          globalOp.getLinkage() == fir::LinkageEnum::Internal)
+        clonedGlobal.removeLinkageAttr();
       gpuSymTable.insert(cloned);
     }
     // Type-info globals for mutually recursive derived types form initializer
diff --git a/flang/unittests/Optimizer/Builder/FIRBuilderTest.cpp b/flang/unittests/Optimizer/Builder/FIRBuilderTest.cpp
index 10a7ddf339133..776ef4b043ef1 100644
--- a/flang/unittests/Optimizer/Builder/FIRBuilderTest.cpp
+++ b/flang/unittests/Optimizer/Builder/FIRBuilderTest.cpp
@@ -191,9 +191,8 @@ TEST_F(FIRBuilderTest, createGlobal1) {
   EXPECT_EQ("global1", global.getSymName());
   EXPECT_TRUE(global.getConstant().has_value());
   EXPECT_EQ(i64Type, global.getType());
-  EXPECT_TRUE(global.getLinkName().has_value());
-  EXPECT_EQ(
-      builder.createInternalLinkage().getValue(), global.getLinkName().value());
+  EXPECT_TRUE(global.getLinkage().has_value());
+  EXPECT_EQ(fir::LinkageEnum::Internal, global.getLinkage().value());
   EXPECT_FALSE(global.getInitVal().has_value());
 
   auto g1 = builder.getNamedGlobal("global1");
@@ -219,9 +218,8 @@ TEST_F(FIRBuilderTest, createGlobal2) {
   EXPECT_TRUE(mlir::isa<mlir::IntegerAttr>(global.getInitVal().value()));
   EXPECT_EQ(16,
       mlir::cast<mlir::IntegerAttr>(global.getInitVal().value()).getValue());
-  EXPECT_TRUE(global.getLinkName().has_value());
-  EXPECT_EQ(
-      builder.createLinkOnceLinkage().getValue(), global.getLinkName().value());
+  EXPECT_TRUE(global.getLinkage().has_value());
+  EXPECT_EQ(fir::LinkageEnum::Linkonce, global.getLinkage().value());
 }
 
 TEST_F(FIRBuilderTest, uniqueCFIdent) {
@@ -310,8 +308,7 @@ TEST_F(FIRBuilderTest, createStringLiteral) {
   auto addrOp = dyn_cast<fir::AddrOfOp>(addr.getDefiningOp());
   auto symbol = addrOp.getSymbol().getRootReference().getValue();
   auto global = builder.getNamedGlobal(symbol);
-  EXPECT_EQ(
-      builder.createLinkOnceLinkage().getValue(), global.getLinkName().value());
+  EXPECT_EQ(fir::LinkageEnum::Linkonce, global.getLinkage().value());
   EXPECT_EQ(fir::CharacterType::get(builder.getContext(), 1, strValue.size()),
       global.getType());
 
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCDeclareGPUModuleInsertion.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCDeclareGPUModuleInsertion.cpp
index f8efb98fb6771..e5b93e053c87d 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCDeclareGPUModuleInsertion.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCDeclareGPUModuleInsertion.cpp
@@ -80,7 +80,7 @@ static bool hasAccDeclareGlobals(ModuleOp mod) {
 static void makeDeviceGlobalDeclaration(Operation &globalOp) {
   globalOp.setInherentAttr(StringAttr::get(globalOp.getContext(), "initVal"),
                            {});
-  globalOp.setInherentAttr(StringAttr::get(globalOp.getContext(), "linkName"),
+  globalOp.setInherentAttr(StringAttr::get(globalOp.getContext(), "linkage"),
                            {});
   for (Region &region : globalOp.getRegions()) {
     region.dropAllReferences();



More information about the flang-commits mailing list