[Mlir-commits] [clang] [flang] [mlir] [mlir][IR] Require inherent symbol attributes for Symbol operations (PR #218920)

Mehdi Amini llvmlistbot at llvm.org
Wed Aug 26 16:57:14 PDT 2026


https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/218920

>From 0ac17e73645d88f62949dd0ed60638da2420bf4e Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Wed, 26 Aug 2026 04:48:51 -0700
Subject: [PATCH] [mlir][IR] Require inherent symbol attributes

Require SymbolTable operations to implement SymbolOpInterface and store symbol
names and visibility as inherent attributes.

Add missing symbol properties/interfaces to GPU, OpenACC, OpenMP, EmitC, and
Toy operations, and update affected tests and the GPU Python builder.

Assisted-by: Codex
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |   4 +-
 clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp   |   6 +-
 clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h   |   6 +-
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       |   4 +-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |   4 +-
 clang/test/CodeGen/AArch64/neon/intrinsics.c  |   2 +-
 .../include/flang/Optimizer/Dialect/FIROps.td |  30 ++++--
 flang/lib/Lower/OpenACC.cpp                   |   5 +-
 flang/lib/Lower/OpenMP/OpenMP.cpp             |   2 +-
 .../lib/Lower/Support/ReductionProcessor.cpp  |   4 +-
 flang/lib/Lower/Support/Utils.cpp             |   6 +-
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       |   2 +-
 flang/lib/Optimizer/Dialect/FIROps.cpp        |  20 ++--
 .../OpenACC/Support/FIROpenACCUtils.cpp       |   4 +-
 .../OpenMP/DoConcurrentConversion.cpp         |   4 +-
 .../Transforms/CompilerGeneratedNames.cpp     |   3 +-
 .../Transforms/ExternalNameConversion.cpp     |   4 +-
 flang/lib/Utils/OpenMP.cpp                    |   2 +-
 mlir/docs/SymbolsAndSymbolTables.md           |  13 +--
 mlir/examples/toy/Ch2/include/toy/Ops.td      |   3 +-
 mlir/examples/toy/Ch3/include/toy/Ops.td      |   3 +-
 mlir/examples/toy/Ch4/include/toy/Ops.td      |   3 +-
 mlir/examples/toy/Ch5/include/toy/Ops.td      |   3 +-
 mlir/examples/toy/Ch6/include/toy/Ops.td      |   3 +-
 mlir/examples/toy/Ch7/include/toy/Ops.td      |   3 +-
 .../include/mlir/Dialect/Async/IR/AsyncOps.td |   3 +-
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td   |  24 +++--
 mlir/include/mlir/Dialect/Func/IR/FuncOps.td  |   3 +-
 mlir/include/mlir/Dialect/GPU/IR/GPUOps.td    |  20 ++--
 mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td  |  35 +++---
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   |  41 ++++---
 .../mlir/Dialect/MLProgram/IR/MLProgramOps.td |   6 +-
 .../mlir/Dialect/MemRef/IR/MemRefOps.td       |   2 +-
 .../mlir/Dialect/OpenACC/OpenACCOps.td        |  50 +++++----
 .../mlir/Dialect/OpenMP/OpenMPClauses.td      |   7 +-
 mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td |  18 +++-
 mlir/include/mlir/Dialect/PDL/IR/PDLOps.td    |   8 +-
 .../mlir/Dialect/PDLInterp/IR/PDLInterpOps.td |   5 +-
 .../mlir/Dialect/SPIRV/IR/SPIRVGraphOps.td    |   6 +-
 .../Dialect/SPIRV/IR/SPIRVStructureOps.td     |  36 ++++---
 .../include/mlir/Dialect/Shape/IR/ShapeOps.td |   6 +-
 .../include/mlir/Dialect/Shard/IR/ShardOps.td |   7 +-
 mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td  |  11 +-
 .../mlir/Dialect/Transform/IR/TransformOps.td |   2 +-
 .../mlir/Dialect/WasmSSA/IR/WasmSSAOps.td     |  86 ++++++++++++---
 mlir/include/mlir/IR/BuiltinOps.td            |   3 +-
 mlir/include/mlir/IR/SymbolInterfaces.td      |  71 +++++--------
 mlir/include/mlir/IR/SymbolTable.h            |  67 +++++++++---
 .../mlir/Interfaces/FunctionInterfaces.td     |   4 +-
 mlir/include/mlir/Target/SMTLIB/Namespace.h   |   5 +-
 mlir/lib/CAPI/IR/IR.cpp                       |   2 +-
 .../Conversion/FuncToEmitC/FuncToEmitC.cpp    |  23 ++--
 mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.cpp |  11 +-
 .../MemRefToEmitC/MemRefToEmitC.cpp           |   4 +-
 .../Conversion/SCFToOpenMP/SCFToOpenMP.cpp    |   3 +-
 .../TosaToSPIRVTosa/TosaToSPIRVTosa.cpp       |   8 +-
 mlir/lib/Dialect/Async/IR/Async.cpp           |   3 +-
 .../Async/Transforms/AsyncToAsyncRuntime.cpp  |  10 +-
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp           |   2 +-
 .../EmitC/Transforms/MLGOAddReflectionMap.cpp |   2 +-
 .../EmitC/Transforms/WrapFuncInClass.cpp      |  11 +-
 mlir/lib/Dialect/Func/IR/FuncOps.cpp          |   3 +-
 mlir/lib/Dialect/GPU/IR/GPUDialect.cpp        |   8 +-
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    |  29 +++--
 .../Dialect/LLVMIR/Transforms/AddComdats.cpp  |   2 +-
 mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp       |   9 +-
 .../OpenACC/Transforms/ACCImplicitRoutine.cpp |   1 +
 .../Transforms/ACCRoutineToGPUFunc.cpp        |   3 +-
 mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp  |   5 +-
 mlir/lib/Dialect/PDL/IR/PDL.cpp               |   3 +-
 mlir/lib/Dialect/SPIRV/IR/ArmGraphOps.cpp     |  18 ++--
 mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp        |  55 +++++++---
 .../Linking/ModuleCombiner/ModuleCombiner.cpp |   8 +-
 ...nvertToReplicatedConstantCompositePass.cpp |   3 +-
 .../SPIRV/Transforms/SPIRVConversion.cpp      |  10 +-
 mlir/lib/Dialect/Shape/IR/Shape.cpp           |   8 +-
 .../lib/Dialect/Transform/IR/TransformOps.cpp |   4 +-
 mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp    |   8 +-
 mlir/lib/IR/BuiltinAttributes.cpp             |   6 +-
 mlir/lib/IR/BuiltinDialect.cpp                |   4 +-
 mlir/lib/IR/SymbolTable.cpp                   | 100 ++++++------------
 .../lib/Interfaces/FunctionImplementation.cpp |  18 ++--
 mlir/lib/Pass/IRPrinting.cpp                  |  11 +-
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       |   6 +-
 .../SPIRV/Deserialization/Deserializer.cpp    |   8 +-
 .../SPIRV/Serialization/SerializeOps.cpp      |   2 +-
 mlir/python/mlir/dialects/gpu/__init__.py     |  10 +-
 .../TosaToSPIRVTosa/op-nesting.mlir           |   4 +-
 mlir/test/Dialect/LLVMIR/alias.mlir           |   8 ++
 .../Dialect/OpenMP/cli-canonical_loop.mlir    |   9 +-
 .../replicated-const-composites.mlir          |   4 +-
 .../Shard/backward-sharding-propagation.mlir  |   2 +-
 ...forward-backward-sharding-propagation.mlir |   2 +-
 .../Shard/forward-sharding-propagation.mlir   |   2 +-
 .../Shard/sharding-propagation-failed.mlir    |   2 +-
 mlir/test/lib/Dialect/Test/TestOpDefs.cpp     |   8 ++
 mlir/test/lib/Dialect/Test/TestOps.td         |  11 +-
 .../tools/tblgen-to-irdl/OpDefinitionsGen.cpp |  12 ++-
 .../Dialect/OpenACC/OpenACCUtilsTest.cpp      |   6 +-
 .../Dialect/SPIRV/SerializationTest.cpp       |   9 +-
 mlir/unittests/IR/SymbolTableTest.cpp         |  30 ++++++
 mlir/unittests/Transforms/Canonicalizer.cpp   |   5 +-
 102 files changed, 721 insertions(+), 483 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 7cf6516de64d9..552d8c09553c0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3336,6 +3336,7 @@ def CIR_TLSModelAttr: CIR_EnumAttr<CIR_TLSModel, "tls_model"> {
 
 def CIR_GlobalOp : CIR_Op<"global", [
   DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
+  SymbolName, SymbolVisibility,
   DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
   NoRegionArguments
 ]> {
@@ -4171,7 +4172,8 @@ def CIR_CallingConv : CIR_I32EnumAttr<"CallingConv", "calling convention", [
 ]>;
 
 def CIR_FuncOp : CIR_Op<"func", [
-  AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface,
+  AutomaticAllocationScope, CallableOpInterface, SymbolName, SymbolVisibility,
+  FunctionOpInterface,
   DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
   HasAtMostOneOfAttrs<["global_ctor_priority", "global_dtor_priority"]>,
   IsolatedFromAbove
diff --git a/clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp b/clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp
index 87b6596eb6773..90eae72f70a30 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp
@@ -156,7 +156,8 @@ void CIRGenModule::emitGlobalOpenACCDeclareDataOperands(
   {
     mlir::OpBuilder::InsertionGuard guardCase(builder);
     auto ctorOp = mlir::acc::GlobalConstructorOp::create(
-        builder, exprLoc, (varName + "_acc_ctor").str());
+        builder, exprLoc, (varName + "_acc_ctor").str(),
+        /*sym_visibility=*/nullptr);
     getModule().push_back(ctorOp);
     mlir::Block *block = builder.createBlock(&ctorOp.getRegion(),
                                              ctorOp.getRegion().end(), {}, {});
@@ -191,7 +192,8 @@ void CIRGenModule::emitGlobalOpenACCDeclareDataOperands(
   if (requiresDtor) {
     mlir::OpBuilder::InsertionGuard guardCase(builder);
     auto ctorOp = mlir::acc::GlobalDestructorOp::create(
-        builder, exprLoc, (varName + "_acc_dtor").str());
+        builder, exprLoc, (varName + "_acc_dtor").str(),
+        /*sym_visibility=*/nullptr);
     getModule().push_back(ctorOp);
     mlir::Block *block = builder.createBlock(&ctorOp.getRegion(),
                                              ctorOp.getRegion().end(), {}, {});
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
index e709380be0d1f..8d400c721ea77 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
@@ -195,10 +195,12 @@ class OpenACCRecipeBuilder : OpenACCRecipeBuilderBase {
     RecipeTy recipe;
 
     if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
-      recipe = RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType(),
+      recipe = RecipeTy::create(modBuilder, loc, recipeName,
+                                /*sym_visibility=*/nullptr, mainOp.getType(),
                                 convertReductionOp(reductionOp));
     } else {
-      recipe = RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType());
+      recipe = RecipeTy::create(modBuilder, loc, recipeName,
+                                /*sym_visibility=*/nullptr, mainOp.getType());
     }
     insertLocation = modBuilder.saveInsertionPoint();
 
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 21864cfa63691..8f7bf8062e2e8 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2416,7 +2416,7 @@ void cir::FuncOp::build(OpBuilder &builder, OperationState &result,
                         StringRef name, FuncType type,
                         GlobalLinkageKind linkage, CallingConv callingConv) {
   result.addRegion();
-  result.addAttribute(SymbolTable::getSymbolAttrName(),
+  result.addAttribute(getSymNameAttrName(result.name),
                       builder.getStringAttr(name));
   result.addAttribute(getFunctionTypeAttrName(result.name),
                       TypeAttr::get(type));
@@ -2500,7 +2500,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
     state.addAttribute(dsoLocalNameAttr, parser.getBuilder().getUnitAttr());
 
   StringAttr nameAttr;
-  if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(state.name),
                              state.attributes))
     return failure();
   llvm::SmallVector<OpAsmParser::Argument, 8> arguments;
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index d2f0b9eb16fc2..9ee597b81df24 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2705,7 +2705,7 @@ void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
     cir::FuncOp func, bool filterArgAndResAttrs,
     SmallVectorImpl<mlir::NamedAttribute> &result) const {
   for (mlir::NamedAttribute attr : func->getAttrs()) {
-    if (attr.getName() == mlir::SymbolTable::getSymbolAttrName() ||
+    if (attr.getName() == func.getSymNameAttrName() ||
         attr.getName() == func.getFunctionTypeAttrName() ||
         attr.getName() == getLinkageAttrNameString() ||
         attr.getName() == func.getCallingConvAttrName() ||
@@ -3163,7 +3163,7 @@ CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp &op,
   builder.setInsertionPointToStart(&comdatOp.getBody().back());
   auto selectorOp = mlir::LLVM::ComdatSelectorOp::create(
       builder, comdatOp.getLoc(), op.getSymName(),
-      mlir::LLVM::comdat::Comdat::Any);
+      mlir::LLVM::comdat::Comdat::Any, /*sym_visibility=*/nullptr);
   return mlir::SymbolRefAttr::get(
       builder.getContext(), comdatName,
       mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr()));
diff --git a/clang/test/CodeGen/AArch64/neon/intrinsics.c b/clang/test/CodeGen/AArch64/neon/intrinsics.c
index 4f8443abd86a6..2eeb33d5d99cb 100644
--- a/clang/test/CodeGen/AArch64/neon/intrinsics.c
+++ b/clang/test/CodeGen/AArch64/neon/intrinsics.c
@@ -5623,7 +5623,7 @@ float16x4_t test_vcvt_f16_f32(float32x4_t a) {
 }
 
 // LLVM-LABEL: @test_vcvt_high_f16_f32(
-// CIR-LABEL: @vcvt_f16_f32(
+// CIR-LABEL: @vcvt_high_f16_f32(
 float16x8_t test_vcvt_high_f16_f32(float16x4_t a, float32x4_t b) {
 // CIR:  cir.call @vcvt_f16_f32
 // CIR: cir.call @vcombine_f16
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 9f9d45776b120..fd0b5b16c87ad 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2889,7 +2889,8 @@ class AtMostRegion<int numBlocks> : Region<
   CPred<"$_self.getBlocks().size() <= " # numBlocks>,
   "region with " # numBlocks # " blocks">;
 
-def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> {
+def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, SymbolName,
+                                     SymbolVisibility, Symbol]> {
   let summary = "Global data";
   let description = [{
     A global variable or constant with initial values.
@@ -2921,7 +2922,8 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> {
     OptionalAttr<UnitAttr>:$target,
     OptionalAttr<StrAttr>:$linkName,
     OptionalAttr<cuf_DataAttributeAttr>:$data_attr,
-    OptionalAttr<I64Attr>:$alignment
+    OptionalAttr<I64Attr>:$alignment,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let regions = (region AtMostRegion<1>:$region);
@@ -2975,8 +2977,7 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> {
 
     mlir::FlatSymbolRefAttr getSymbol() {
       return mlir::FlatSymbolRefAttr::get(getContext(),
-          (*this)->getAttrOfType<mlir::StringAttr>(
-              mlir::SymbolTable::getSymbolAttrName()).getValue());
+          getNameAttr().getValue());
     }
 
     bool isInitialized() {
@@ -3094,7 +3095,8 @@ def fir_ModuleDebugImportsOp : fir_Op<"module_debug_imports", [
 }
 
 def fir_TypeInfoOp : fir_Op<"type_info",
-    [IsolatedFromAbove, Symbol, ImplicitFirTerminator]> {
+    [IsolatedFromAbove, SymbolName, SymbolVisibility, Symbol,
+     ImplicitFirTerminator]> {
   let summary = "Derived type information";
 
   let description = [{
@@ -3130,6 +3132,7 @@ def fir_TypeInfoOp : fir_Op<"type_info",
 
   let arguments = (ins
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     TypeAttr:$type,
     OptionalAttr<TypeAttr>:$parent_type,
     UnitAttr:$abstract,
@@ -3151,7 +3154,8 @@ def fir_TypeInfoOp : fir_Op<"type_info",
   ];
 
   let assemblyFormat = [{
-    $sym_name (`abstract` $abstract^)? (`noinit` $no_init^)?
+    ($sym_visibility^)? $sym_name (`abstract` $abstract^)?
+    (`noinit` $no_init^)?
     (`nodestroy` $no_destroy^)? (`nofinal` $no_final^)?
     (`extends` $parent_type^)? attr-dict `:` $type
     (`dispatch_table` $dispatch_table^)?
@@ -3679,7 +3683,10 @@ def YieldOp : fir_Op<"yield",
   let assemblyFormat = "( `(` $results^ `:` type($results) `)` )? attr-dict";
 }
 
-def fir_LocalitySpecifierOp : fir_Op<"local", [IsolatedFromAbove]> {
+def fir_LocalitySpecifierOp : fir_Op<"local", [IsolatedFromAbove,
+                                                SymbolName,
+                                                SymbolVisibility,
+                                                Symbol]> {
   let summary = "Provides declaration of local and local_init logic.";
   let description = [{
     This operation provides a declaration of how to implement the
@@ -3753,6 +3760,7 @@ def fir_LocalitySpecifierOp : fir_Op<"local", [IsolatedFromAbove]> {
   }];
 
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttrOf<AnyType>:$type,
                        LocalitySpecifierTypeAttr:$locality_specifier_type);
 
@@ -3761,7 +3769,7 @@ def fir_LocalitySpecifierOp : fir_Op<"local", [IsolatedFromAbove]> {
                         AnyRegion:$dealloc_region);
 
   let assemblyFormat = [{
-    $locality_specifier_type $sym_name `:` $type
+    $locality_specifier_type ($sym_visibility^)? $sym_name `:` $type
       (`init` $init_region^)?
       (`copy` $copy_region^)?
       (`dealloc` $dealloc_region^)?
@@ -3801,6 +3809,8 @@ def fir_LocalitySpecifierOp : fir_Op<"local", [IsolatedFromAbove]> {
 }
 
 def fir_DeclareReductionOp : fir_Op<"declare_reduction", [IsolatedFromAbove,
+                                                         SymbolName,
+                                                         SymbolVisibility,
                                                          Symbol]> {
   let summary = "declares a reduction kind";
   let description = [{
@@ -3854,6 +3864,7 @@ def fir_DeclareReductionOp : fir_Op<"declare_reduction", [IsolatedFromAbove,
   }];
 
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttr:$type,
                        OptionalAttr<TypeAttr>:$byref_element_type);
 
@@ -3864,7 +3875,8 @@ def fir_DeclareReductionOp : fir_Op<"declare_reduction", [IsolatedFromAbove,
                         AnyRegion:$cleanupRegion,
                         AnyRegion:$dataPtrPtrRegion);
 
-  let assemblyFormat = "$sym_name `:` $type attr-dict-with-keyword "
+  let assemblyFormat = "($sym_visibility^)? $sym_name `:` $type "
+                       "attr-dict-with-keyword "
                        "( `alloc` $allocRegion^ )? "
                        "`init` $initializerRegion "
                        "`combiner` $reductionRegion "
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 2e4b36178748b..30364d0ae9231 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -912,7 +912,8 @@ static void createDeclareGlobalOp(mlir::OpBuilder &modBuilder,
                                   const std::string &declareGlobalName,
                                   bool implicit, std::stringstream &asFortran) {
   GlobalCtorOrDtorOp declareGlobalOp =
-      GlobalCtorOrDtorOp::create(modBuilder, loc, declareGlobalName);
+      GlobalCtorOrDtorOp::create(modBuilder, loc, declareGlobalName,
+                                 /*sym_visibility=*/nullptr);
   builder.createBlock(&declareGlobalOp.getRegion(),
                       declareGlobalOp.getRegion().end(), {}, {});
   builder.setInsertionPointToEnd(&declareGlobalOp.getRegion().back());
@@ -4752,7 +4753,7 @@ void createOpenACCRoutineConstruct(
   mlir::OpBuilder modBuilder(mod.getBodyRegion());
   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
   mlir::acc::RoutineOp::create(
-      modBuilder, loc, routineOpStr,
+      modBuilder, loc, routineOpStr, /*sym_visibility=*/nullptr,
       mlir::SymbolRefAttr::get(builder.getContext(), funcName),
       getArrayAttrOrNull(builder, bindIdNames),
       getArrayAttrOrNull(builder, bindStrNames),
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 7503d33c8df38..3b121a4673c23 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -6749,7 +6749,7 @@ genOpenMPDeclareMapperImpl(lower::AbstractConverter &converter,
   firOpBuilder.setInsertionPointToStart(converter.getModuleOp().getBody());
   auto mlirType = converter.genType(varType.declTypeSpec->derivedTypeSpec());
   auto declMapperOp = mlir::omp::DeclareMapperOp::create(
-      firOpBuilder, loc, mapperNameStr, mlirType);
+      firOpBuilder, loc, mapperNameStr, /*sym_visibility=*/nullptr, mlirType);
   auto &region = declMapperOp.getRegion();
   firOpBuilder.createBlock(&region);
   auto varVal = region.addArgument(firOpBuilder.getRefType(mlirType), loc);
diff --git a/flang/lib/Lower/Support/ReductionProcessor.cpp b/flang/lib/Lower/Support/ReductionProcessor.cpp
index 7b502880f50f3..f36834fecc914 100644
--- a/flang/lib/Lower/Support/ReductionProcessor.cpp
+++ b/flang/lib/Lower/Support/ReductionProcessor.cpp
@@ -680,8 +680,8 @@ DeclareRedType ReductionProcessor::createDeclareReductionHelper(
   } else
     type = valTy;
 
-  decl = DeclareRedType::create(modBuilder, loc, reductionOpName, type,
-                                boxedTyAttr);
+  decl = DeclareRedType::create(modBuilder, loc, reductionOpName,
+                                /*sym_visibility=*/nullptr, type, boxedTyAttr);
   createReductionAllocAndInitRegions(converter, loc, decl, genInitValueCB, type,
                                      isByRef, sym);
   builder.createBlock(&decl.getReductionRegion(),
diff --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp
index 9431daaddf1aa..e6b6e923308af 100644
--- a/flang/lib/Lower/Support/Utils.cpp
+++ b/flang/lib/Lower/Support/Utils.cpp
@@ -787,12 +787,14 @@ void privatizeSymbol(
 
     if constexpr (std::is_same_v<OpType, mlir::omp::PrivateClauseOp>) {
       result = OpType::create(
-          firOpBuilder, symLoc, uniquePrivatizerName, allocType,
+          firOpBuilder, symLoc, uniquePrivatizerName,
+          /*sym_visibility=*/nullptr, allocType,
           emitCopyRegion ? mlir::omp::DataSharingClauseType::FirstPrivate
                          : mlir::omp::DataSharingClauseType::Private);
     } else {
       result =
-          OpType::create(firOpBuilder, symLoc, uniquePrivatizerName, allocType,
+          OpType::create(firOpBuilder, symLoc, uniquePrivatizerName,
+                         /*sym_visibility=*/nullptr, allocType,
                          emitCopyRegion ? fir::LocalitySpecifierType::LocalInit
                                         : fir::LocalitySpecifierType::Local);
     }
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index cb3435a63767c..c5360d468bdd7 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3854,7 +3854,7 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
     rewriter.setInsertionPointToEnd(&comdatOp.getBody().back());
     auto selectorOp = mlir::LLVM::ComdatSelectorOp::create(
         rewriter, comdatOp.getLoc(), global.getSymName(),
-        mlir::LLVM::comdat::Comdat::Any);
+        mlir::LLVM::comdat::Comdat::Any, /*sym_visibility=*/nullptr);
     global.setComdatAttr(mlir::SymbolRefAttr::get(
         rewriter.getContext(), comdatName,
         mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr())));
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index c681c03fba777..108f7fc793c61 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -2431,7 +2431,7 @@ void fir::TypeInfoOp::build(mlir::OpBuilder &builder,
                             llvm::ArrayRef<mlir::NamedAttribute> attrs) {
   result.addRegion();
   result.addRegion();
-  result.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
+  result.addAttribute(getSymNameAttrName(result.name),
                       builder.getStringAttr(type.getName()));
   result.addAttribute(getTypeAttrName(result.name), mlir::TypeAttr::get(type));
   if (parentType)
@@ -2803,6 +2803,8 @@ mlir::Type fir::GlobalOp::resultType() {
 
 mlir::ParseResult fir::GlobalOp::parse(mlir::OpAsmParser &parser,
                                        mlir::OperationState &result) {
+  (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
+
   // Parse the optional linkage
   llvm::StringRef linkage;
   auto &builder = parser.getBuilder();
@@ -2820,7 +2822,7 @@ mlir::ParseResult fir::GlobalOp::parse(mlir::OpAsmParser &parser,
                             fir::GlobalOp::getSymrefAttrName(result.name),
                             result.attributes))
     return mlir::failure();
-  result.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
+  result.addAttribute(getSymNameAttrName(result.name),
                       nameAttr.getRootReference());
 
   bool simpleInitializer = false;
@@ -2866,6 +2868,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();
   p << ' ';
@@ -2873,11 +2877,11 @@ void fir::GlobalOp::print(mlir::OpAsmPrinter &p) {
   if (auto val = getValueOrNull())
     p << '(' << val << ')';
   // Print all other attributes that are not pretty printed here.
-  p.printOptionalAttrDict((*this)->getAttrs(), /*elideAttrs=*/{
-                              getSymNameAttrName(), getSymrefAttrName(),
-                              getTypeAttrName(), getConstantAttrName(),
-                              getTargetAttrName(), getLinkNameAttrName(),
-                              getInitValAttrName()});
+  p.printOptionalAttrDict(
+      (*this)->getAttrs(), /*elideAttrs=*/{
+          getSymNameAttrName(), getSymrefAttrName(), getTypeAttrName(),
+          getConstantAttrName(), getTargetAttrName(), getLinkNameAttrName(),
+          getInitValAttrName(), getSymVisibilityAttrName()});
   if (getOperation()->getAttr(getConstantAttrName()))
     p << " " << getConstantAttrName().strref();
   if (getOperation()->getAttr(getTargetAttrName()))
@@ -2903,7 +2907,7 @@ void fir::GlobalOp::build(mlir::OpBuilder &builder,
                           llvm::ArrayRef<mlir::NamedAttribute> attrs) {
   result.addRegion();
   result.addAttribute(getTypeAttrName(result.name), mlir::TypeAttr::get(type));
-  result.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
+  result.addAttribute(getSymNameAttrName(result.name),
                       builder.getStringAttr(name));
   result.addAttribute(getSymrefAttrName(result.name),
                       mlir::SymbolRefAttr::get(builder.getContext(), name));
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index 4417b769fa314..f0ce7bb4e57c5 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -441,9 +441,11 @@ static RecipeOp genRecipeOp(
   RecipeOp recipe;
   if constexpr (std::is_same_v<RecipeOp, mlir::acc::ReductionRecipeOp>) {
     recipe = mlir::acc::ReductionRecipeOp::create(modBuilder, loc, recipeName,
+                                                  /*sym_visibility=*/nullptr,
                                                   ty, op);
   } else {
-    recipe = RecipeOp::create(modBuilder, loc, recipeName, ty);
+    recipe = RecipeOp::create(modBuilder, loc, recipeName,
+                              /*sym_visibility=*/nullptr, ty);
   }
 
   assert(hlfir::isFortranVariableType(ty) && "expect Fortran variable type");
diff --git a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
index b50f5ccf6a323..4dc6aa4750eaa 100644
--- a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
+++ b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
@@ -856,7 +856,7 @@ class DoConcurrentConversion
 
         auto privatizer = mlir::omp::PrivateClauseOp::create(
             rewriter, localizer.getLoc(), sym.getLeafReference().str() + ".omp",
-            localizer.getTypeAttr().getValue(),
+            /*sym_visibility=*/nullptr, localizer.getTypeAttr().getValue(),
             mlir::omp::DataSharingClauseType::Private);
 
         cloneFIRRegionToOMP(rewriter, localizer.getInitRegion(),
@@ -895,7 +895,7 @@ class DoConcurrentConversion
         if (!ompReducer) {
           ompReducer = mlir::omp::DeclareReductionOp::create(
               rewriter, firReducer.getLoc(), ompReducerName,
-              firReducer.getTypeAttr().getValue(),
+              /*sym_visibility=*/nullptr, firReducer.getTypeAttr().getValue(),
               firReducer.getByrefElementTypeAttr());
 
           cloneFIRRegionToOMP(rewriter, firReducer.getAllocRegion(),
diff --git a/flang/lib/Optimizer/Transforms/CompilerGeneratedNames.cpp b/flang/lib/Optimizer/Transforms/CompilerGeneratedNames.cpp
index 6cda116071f55..7a173da514b16 100644
--- a/flang/lib/Optimizer/Transforms/CompilerGeneratedNames.cpp
+++ b/flang/lib/Optimizer/Transforms/CompilerGeneratedNames.cpp
@@ -44,8 +44,7 @@ void CompilerGeneratedNamesConversionPass::runOnOperation() {
   llvm::DenseMap<mlir::StringAttr, mlir::FlatSymbolRefAttr> remappings;
 
   auto processOp = [&](mlir::Operation &op) {
-    auto symName = op.getAttrOfType<mlir::StringAttr>(
-        mlir::SymbolTable::getSymbolAttrName());
+    auto symName = mlir::cast<mlir::SymbolOpInterface>(&op).getNameAttr();
     auto deconstructedName = fir::NameUniquer::deconstruct(symName);
     if (deconstructedName.first != fir::NameUniquer::NameKind::NOT_UNIQUED &&
         !fir::NameUniquer::isExternalFacingUniquedName(deconstructedName)) {
diff --git a/flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp b/flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp
index 10e631e5aec1e..dae471175599e 100644
--- a/flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp
+++ b/flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp
@@ -78,8 +78,8 @@ void ExternalNameConversionPass::runOnOperation() {
   mlir::SymbolTable symbolTable(op);
 
   auto processFctOrGlobal = [&](mlir::Operation &funcOrGlobal) {
-    auto symName = funcOrGlobal.getAttrOfType<mlir::StringAttr>(
-        mlir::SymbolTable::getSymbolAttrName());
+    auto symName =
+        mlir::cast<mlir::SymbolOpInterface>(&funcOrGlobal).getNameAttr();
     auto deconstructedName = fir::NameUniquer::deconstruct(symName);
     if (fir::NameUniquer::isExternalFacingUniquedName(deconstructedName)) {
       // Check if this is a private function that would conflict with a common
diff --git a/flang/lib/Utils/OpenMP.cpp b/flang/lib/Utils/OpenMP.cpp
index 745b54005fc0b..92f92ed914842 100644
--- a/flang/lib/Utils/OpenMP.cpp
+++ b/flang/lib/Utils/OpenMP.cpp
@@ -259,7 +259,7 @@ mlir::FlatSymbolRefAttr getOrGenImplicitDefaultDeclareMapper(
 
   firOpBuilder.setInsertionPointToStart(moduleOp.getBody());
   auto declMapperOp = mlir::omp::DeclareMapperOp::create(
-      firOpBuilder, loc, mapperNameStr, recordType);
+      firOpBuilder, loc, mapperNameStr, /*sym_visibility=*/nullptr, recordType);
   auto &region = declMapperOp.getRegion();
   firOpBuilder.createBlock(&region);
   auto mapperArg = region.addArgument(firOpBuilder.getRefType(recordType), loc);
diff --git a/mlir/docs/SymbolsAndSymbolTables.md b/mlir/docs/SymbolsAndSymbolTables.md
index 4288b7bd66a02..c30cc38845c0a 100644
--- a/mlir/docs/SymbolsAndSymbolTables.md
+++ b/mlir/docs/SymbolsAndSymbolTables.md
@@ -42,15 +42,16 @@ necessary verification and accessors; it also supports operations, such as
 `builtin.module`, that conditionally define a symbol. `Symbol`s must have the
 following properties:
 
-*   A `StringAttr` attribute named
-    'SymbolTable::getSymbolAttrName()'(`sym_name`).
-    -   This attribute defines the symbolic 'name' of the operation.
+*   A `StringAttr` name exposed by `SymbolOpInterface::getNameAttr`. The
+    `SymbolName` trait provides the conventional implementation backed by an
+    inherent `StringAttr` named `sym_name`.
 *   A [visibility](#symbol-visibility) (`getVisibility`/`setVisibility`)
     -   The visibility defines in which scopes the symbol may be accessed.
-    -   By default this is stored in an optional inherent `StringAttr` named
-        'SymbolOpInterface::getDefaultVisibilityAttrName()'(`sym_visibility`),
+    -   The `SymbolVisibility` trait provides the conventional implementation,
+        stored in an optional inherent `StringAttr` named
+        `SymbolOpInterface::getDefaultVisibilityAttrName()` (`sym_visibility`),
         where the absence of the attribute means public visibility. Operations
-        may override `getVisibility`/`setVisibility` to use a different
+        may instead implement `getVisibility`/`setVisibility` using a different
         representation. Client code should not make any assumptions about the
         presence of a visibility attribute.
 *   No SSA results
diff --git a/mlir/examples/toy/Ch2/include/toy/Ops.td b/mlir/examples/toy/Ch2/include/toy/Ops.td
index 91bf83a54df1a..59106aa1622c9 100644
--- a/mlir/examples/toy/Ch2/include/toy/Ops.td
+++ b/mlir/examples/toy/Ch2/include/toy/Ops.td
@@ -113,7 +113,7 @@ def AddOp : Toy_Op<"add"> {
 //===----------------------------------------------------------------------===//
 
 def FuncOp : Toy_Op<"func", [
-    FunctionOpInterface, IsolatedFromAbove
+    SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove
   ]> {
   let summary = "user defined function operation";
   let description = [{
@@ -134,6 +134,7 @@ def FuncOp : Toy_Op<"func", [
 
   let arguments = (ins
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     TypeAttrOf<FunctionType>:$function_type,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
     OptionalAttr<DictArrayAttr>:$res_attrs
diff --git a/mlir/examples/toy/Ch3/include/toy/Ops.td b/mlir/examples/toy/Ch3/include/toy/Ops.td
index 027b076af9e63..e0e973a96ed88 100644
--- a/mlir/examples/toy/Ch3/include/toy/Ops.td
+++ b/mlir/examples/toy/Ch3/include/toy/Ops.td
@@ -112,7 +112,7 @@ def AddOp : Toy_Op<"add", [Pure]> {
 //===----------------------------------------------------------------------===//
 
 def FuncOp : Toy_Op<"func", [
-    FunctionOpInterface, IsolatedFromAbove
+    SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove
   ]> {
   let summary = "user defined function operation";
   let description = [{
@@ -133,6 +133,7 @@ def FuncOp : Toy_Op<"func", [
 
   let arguments = (ins
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     TypeAttrOf<FunctionType>:$function_type,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
     OptionalAttr<DictArrayAttr>:$res_attrs
diff --git a/mlir/examples/toy/Ch4/include/toy/Ops.td b/mlir/examples/toy/Ch4/include/toy/Ops.td
index 6c6b73937aaf8..e43cca419d847 100644
--- a/mlir/examples/toy/Ch4/include/toy/Ops.td
+++ b/mlir/examples/toy/Ch4/include/toy/Ops.td
@@ -141,7 +141,7 @@ def CastOp : Toy_Op<"cast", [
 //===----------------------------------------------------------------------===//
 
 def FuncOp : Toy_Op<"func", [
-    FunctionOpInterface, IsolatedFromAbove
+    SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove
   ]> {
   let summary = "user defined function operation";
   let description = [{
@@ -162,6 +162,7 @@ def FuncOp : Toy_Op<"func", [
 
   let arguments = (ins
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     TypeAttrOf<FunctionType>:$function_type,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
     OptionalAttr<DictArrayAttr>:$res_attrs
diff --git a/mlir/examples/toy/Ch5/include/toy/Ops.td b/mlir/examples/toy/Ch5/include/toy/Ops.td
index 6a136ec76e3d1..3aecebd98bd46 100644
--- a/mlir/examples/toy/Ch5/include/toy/Ops.td
+++ b/mlir/examples/toy/Ch5/include/toy/Ops.td
@@ -141,7 +141,7 @@ def CastOp : Toy_Op<"cast", [
 //===----------------------------------------------------------------------===//
 
 def FuncOp : Toy_Op<"func", [
-    FunctionOpInterface, IsolatedFromAbove
+    SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove
   ]> {
   let summary = "user defined function operation";
   let description = [{
@@ -162,6 +162,7 @@ def FuncOp : Toy_Op<"func", [
 
   let arguments = (ins
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     TypeAttrOf<FunctionType>:$function_type,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
     OptionalAttr<DictArrayAttr>:$res_attrs
diff --git a/mlir/examples/toy/Ch6/include/toy/Ops.td b/mlir/examples/toy/Ch6/include/toy/Ops.td
index 897b36d6135f4..b7818aafb7253 100644
--- a/mlir/examples/toy/Ch6/include/toy/Ops.td
+++ b/mlir/examples/toy/Ch6/include/toy/Ops.td
@@ -141,7 +141,7 @@ def CastOp : Toy_Op<"cast", [
 //===----------------------------------------------------------------------===//
 
 def FuncOp : Toy_Op<"func", [
-    FunctionOpInterface, IsolatedFromAbove
+    SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove
   ]> {
   let summary = "user defined function operation";
   let description = [{
@@ -162,6 +162,7 @@ def FuncOp : Toy_Op<"func", [
 
   let arguments = (ins
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     TypeAttrOf<FunctionType>:$function_type,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
     OptionalAttr<DictArrayAttr>:$res_attrs
diff --git a/mlir/examples/toy/Ch7/include/toy/Ops.td b/mlir/examples/toy/Ch7/include/toy/Ops.td
index 9151396c8aac7..3f6c77dd79726 100644
--- a/mlir/examples/toy/Ch7/include/toy/Ops.td
+++ b/mlir/examples/toy/Ch7/include/toy/Ops.td
@@ -165,7 +165,7 @@ def CastOp : Toy_Op<"cast", [
 //===----------------------------------------------------------------------===//
 
 def FuncOp : Toy_Op<"func", [
-    FunctionOpInterface, IsolatedFromAbove
+    SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove
   ]> {
   let summary = "user defined function operation";
   let description = [{
@@ -186,6 +186,7 @@ def FuncOp : Toy_Op<"func", [
 
   let arguments = (ins
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     TypeAttrOf<FunctionType>:$function_type,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
     OptionalAttr<DictArrayAttr>:$res_attrs
diff --git a/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td b/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
index d20ad0d9971e7..cc21bc1b7fd33 100644
--- a/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
+++ b/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
@@ -107,7 +107,8 @@ def Async_ExecuteOp :
 }
 
 def Async_FuncOp : Async_Op<"func",
-    [FunctionOpInterface, IsolatedFromAbove, OpAsmOpInterface]> {
+    [SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove,
+     OpAsmOpInterface]> {
   let summary = "async function operation";
   let description = [{
     An async function is like a normal function, but supports non-blocking
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 0e7ea45d6d091..96770e9dd86f7 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -856,7 +856,8 @@ def EmitC_DeclareFuncOp : EmitC_Op<"declare_func", [
 
 def EmitC_FuncOp : EmitC_Op<"func", [
   AutomaticAllocationScope,
-  FunctionOpInterface, IsolatedFromAbove, OpAsmOpInterface
+  SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove,
+  OpAsmOpInterface
 ]> {
   let summary = "An operation with a name containing a single `SSACFG` region";
   let description = [{
@@ -898,6 +899,7 @@ def EmitC_FuncOp : EmitC_Op<"func", [
     ```
   }];
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttrOf<FunctionType>:$function_type,
                        OptionalAttr<StrArrayAttr>:$specifiers,
                        OptionalAttr<DictArrayAttr>:$arg_attrs,
@@ -1426,7 +1428,8 @@ def EmitC_VariableOp : EmitC_Op<"variable", []> {
   let hasVerifier = 1;
 }
 
-def EmitC_GlobalOp : EmitC_Op<"global", [Symbol]> {
+def EmitC_GlobalOp
+    : EmitC_Op<"global", [SymbolName, SymbolVisibility, Symbol]> {
   let summary = "A global variable";
   let description = [{
     The `emitc.global` operation declares or defines a named global variable.
@@ -1461,6 +1464,7 @@ def EmitC_GlobalOp : EmitC_Op<"global", [Symbol]> {
   }];
 
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttr:$type,
                        OptionalAttr<EmitC_OpaqueOrTypedAttr>:$initial_value,
                        UnitAttr:$extern_specifier,
@@ -1471,6 +1475,7 @@ def EmitC_GlobalOp : EmitC_Op<"global", [Symbol]> {
        (`extern` $extern_specifier^)?
        (`static` $static_specifier^)?
        (`const` $const_specifier^)?
+       ($sym_visibility^)?
        $sym_name
        `:` custom<EmitCGlobalOpTypeAndInitialValue>($type, $initial_value)
        attr-dict
@@ -1917,6 +1922,7 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
 def EmitC_ClassOp
     : EmitC_Op<"class", [AutomaticAllocationScope, IsolatedFromAbove,
                          OpAsmOpInterface, SymbolTable,
+                         SymbolName, SymbolVisibility,
                          Symbol]#GraphRegionNoTerminator.traits> {
   let summary =
       "Represents a C++ class definition, encapsulating fields and methods.";
@@ -1952,7 +1958,9 @@ def EmitC_ClassOp
     ```
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name, UnitAttr:$final_specifier,
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                   OptionalAttr<StrAttr>:$sym_visibility,
+                   UnitAttr:$final_specifier,
                    DefaultValuedAttr<EmitC_ClassTypeAttr,
                                      "ClassType::class_">:$class_type);
 
@@ -1964,10 +1972,11 @@ def EmitC_ClassOp
   }];
 
   let assemblyFormat =
-      [{ ($class_type^)? (`final` $final_specifier^)? $sym_name attr-dict-with-keyword $body }];
+      [{ ($class_type^)? (`final` $final_specifier^)? ($sym_visibility^)? $sym_name attr-dict-with-keyword $body }];
 }
 
-def EmitC_FieldOp : EmitC_Op<"field", [Symbol]> {
+def EmitC_FieldOp
+    : EmitC_Op<"field", [SymbolName, SymbolVisibility, Symbol]> {
   let summary = "A field within a class";
   let description = [{
     The `emitc.field` operation declares a named field within an `emitc.class`
@@ -1988,11 +1997,12 @@ def EmitC_FieldOp : EmitC_Op<"field", [Symbol]> {
     ```
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name, TypeAttr:$type,
+  let arguments = (ins SymbolNameAttr:$sym_name,
+      OptionalAttr<StrAttr>:$sym_visibility, TypeAttr:$type,
       OptionalAttr<EmitC_OpaqueOrTypedAttr>:$initial_value);
 
   let assemblyFormat = [{
-       $sym_name
+       ($sym_visibility^)? $sym_name
        `:` custom<EmitCFieldOpTypeAndInitialValue>($type, $initial_value)
        attr-dict
   }];
diff --git a/mlir/include/mlir/Dialect/Func/IR/FuncOps.td b/mlir/include/mlir/Dialect/Func/IR/FuncOps.td
index f86dac3a772f1..7d984092f5e33 100644
--- a/mlir/include/mlir/Dialect/Func/IR/FuncOps.td
+++ b/mlir/include/mlir/Dialect/Func/IR/FuncOps.td
@@ -249,7 +249,8 @@ def ConstantOp : Func_Op<"constant",
 
 def FuncOp : Func_Op<"func", [
   AffineScope, AutomaticAllocationScope,
-  FunctionOpInterface, IsolatedFromAbove, OpAsmOpInterface
+  SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove,
+  OpAsmOpInterface
 ]> {
   let summary = "An operation with a name containing a single `SSACFG` region";
   let description = [{
diff --git a/mlir/include/mlir/Dialect/GPU/IR/GPUOps.td b/mlir/include/mlir/Dialect/GPU/IR/GPUOps.td
index 1066dcb3b7308..13fe6d3f25c12 100644
--- a/mlir/include/mlir/Dialect/GPU/IR/GPUOps.td
+++ b/mlir/include/mlir/Dialect/GPU/IR/GPUOps.td
@@ -346,7 +346,8 @@ def GPU_OptionalDimSizeHintAttr : ConfinedAttr<OptionalAttr<DenseI32ArrayAttr>,
 >;
 
 def GPU_GPUFuncOp : GPU_Op<"func", [
-    HasParent<"GPUModuleOp">, AutomaticAllocationScope, FunctionOpInterface,
+    HasParent<"GPUModuleOp">, AutomaticAllocationScope, SymbolName,
+    SymbolVisibility, FunctionOpInterface,
     IsolatedFromAbove, AffineScope
   ]> {
   let summary = "Function executable on a GPU";
@@ -419,7 +420,9 @@ def GPU_GPUFuncOp : GPU_Op<"func", [
     attribution.
   }];
 
-  let arguments = (ins TypeAttrOf<FunctionType>:$function_type,
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
+                       TypeAttrOf<FunctionType>:$function_type,
                        OptionalAttr<DictArrayAttr>:$arg_attrs,
                        OptionalAttr<DictArrayAttr>:$res_attrs,
                        OptionalAttr<DictArrayAttr>:$workgroup_attrib_attrs,
@@ -1543,7 +1546,8 @@ def GPU_InitializeNamedBarrierOp
 
 def GPU_GPUModuleOp : GPU_Op<"module", [
       IsolatedFromAbove, DataLayoutOpInterface, HasDefaultDLTIDataLayout,
-      NoRegionArguments, SymbolTable, Symbol] # GraphRegionNoTerminator.traits> {
+      NoRegionArguments, SymbolTable, SymbolName, SymbolVisibility,
+      Symbol] # GraphRegionNoTerminator.traits> {
   let summary = "A top level compilation unit containing code to be run on a GPU.";
   let description = [{
     GPU module contains code that is intended to be run on a GPU. A host device
@@ -1593,11 +1597,12 @@ def GPU_GPUModuleOp : GPU_Op<"module", [
 
   let arguments = (ins
       SymbolNameAttr:$sym_name,
+      OptionalAttr<StrAttr>:$sym_visibility,
       OptionalAttr<GPUNonEmptyTargetArrayAttr>:$targets,
       OptionalAttr<OffloadingTranslationAttr>:$offloadingHandler);
   let regions = (region SizedRegion<1>:$bodyRegion);
   let assemblyFormat = [{
-    $sym_name
+    ($sym_visibility^)? $sym_name
     (`<` $offloadingHandler^ `>`)?
     ($targets^)? attr-dict-with-keyword $bodyRegion
   }];
@@ -1617,8 +1622,10 @@ def GPU_GPUModuleOp : GPU_Op<"module", [
   let hasVerifier = 1;
 }
 
-def GPU_BinaryOp : GPU_Op<"binary", [Symbol]>, Arguments<(ins
+def GPU_BinaryOp
+    : GPU_Op<"binary", [SymbolName, SymbolVisibility, Symbol]>, Arguments<(ins
       SymbolNameAttr:$sym_name,
+      OptionalAttr<StrAttr>:$sym_visibility,
       OptionalAttr<OffloadingTranslationAttr>:$offloadingHandler,
       ConfinedAttr<GPUObjectArrayAttr, [ArrayMinCount<1>]>:$objects)
     > {
@@ -1657,7 +1664,8 @@ def GPU_BinaryOp : GPU_Op<"binary", [Symbol]>, Arguments<(ins
   ];
   let skipDefaultBuilders = 1;
   let assemblyFormat = [{
-    $sym_name custom<OffloadingHandler>($offloadingHandler) attr-dict $objects
+    ($sym_visibility^)? $sym_name
+    custom<OffloadingHandler>($offloadingHandler) attr-dict $objects
   }];
 }
 
diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
index 688ec9855fa38..d47d1be4d181d 100644
--- a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
+++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
@@ -32,7 +32,8 @@ class AtMostOneChildOf<string op> : ParamNativeOpTrait<"AtMostOneChildOf", op>;
 //===----------------------------------------------------------------------===//
 
 def IRDL_DialectOp : IRDL_Op<"dialect",
-    [IsolatedFromAbove, NoTerminator, Symbol, SymbolTable]> {
+    [IsolatedFromAbove, NoTerminator, SymbolName, SymbolVisibility, Symbol,
+     SymbolTable]> {
   let summary = "Define a new dialect";
   let description = [{
     The `irdl.dialect` operation defines a dialect. All operations, attributes,
@@ -49,10 +50,12 @@ def IRDL_DialectOp : IRDL_Op<"dialect",
     The above program defines a `cmath` dialect.
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name);
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility);
   let regions = (region SizedRegion<1>:$body);
   let assemblyFormat =
-    "$sym_name attr-dict-with-keyword custom<SingleBlockRegion>($body)";
+    "($sym_visibility^)? $sym_name attr-dict-with-keyword "
+    "custom<SingleBlockRegion>($body)";
   let hasVerifier = 1;
 }
 
@@ -62,7 +65,8 @@ def IRDL_DialectOp : IRDL_Op<"dialect",
 
 def IRDL_TypeOp : IRDL_Op<"type",
     [HasParent<"DialectOp">, NoTerminator, NoRegionArguments,
-     AtMostOneChildOf<"ParametersOp">, Symbol, IsolatedFromAbove]> {
+     AtMostOneChildOf<"ParametersOp">, SymbolName, Symbol,
+     SymbolVisibility, IsolatedFromAbove]> {
   let summary = "Define a new type";
   let description = [{
     `irdl.type` defines a new type belonging to the `irdl.dialect` parent.
@@ -87,16 +91,19 @@ def IRDL_TypeOp : IRDL_Op<"type",
     type has a single parameter that should be either `i32` or `i64`.
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name);
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility);
   let regions = (region SizedRegion<1>:$body);
   let assemblyFormat =
-    "$sym_name attr-dict-with-keyword custom<SingleBlockRegion>($body)";
+    "($sym_visibility^)? $sym_name attr-dict-with-keyword "
+    "custom<SingleBlockRegion>($body)";
   let hasVerifier = 1;
 }
 
 def IRDL_AttributeOp : IRDL_Op<"attribute",
     [HasParent<"DialectOp">, NoTerminator, NoRegionArguments,
-     AtMostOneChildOf<"ParametersOp">, Symbol, IsolatedFromAbove]> {
+     AtMostOneChildOf<"ParametersOp">, SymbolName, Symbol,
+     SymbolVisibility, IsolatedFromAbove]> {
   let summary = "Define a new attribute";
   let description = [{
     `irdl.attribute` defines a new attribute belonging to the `irdl.dialect`
@@ -123,10 +130,12 @@ def IRDL_AttributeOp : IRDL_Op<"attribute",
     either a `"foo"` or a `"bar"`.
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name);
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility);
   let regions = (region SizedRegion<1>:$body);
   let assemblyFormat =
-    "$sym_name attr-dict-with-keyword custom<SingleBlockRegion>($body)";
+    "($sym_visibility^)? $sym_name attr-dict-with-keyword "
+    "custom<SingleBlockRegion>($body)";
 
   let hasVerifier = 1;
 }
@@ -169,7 +178,7 @@ def IRDL_ParametersOp : IRDL_Op<"parameters",
 def IRDL_OperationOp : IRDL_Op<"operation",
     [HasParent<"DialectOp">, NoTerminator, NoRegionArguments,
     AtMostOneChildOf<"OperandsOp, ResultsOp, AttributesOp, RegionsOp">,
-    Symbol, IsolatedFromAbove]> {
+    SymbolName, SymbolVisibility, Symbol, IsolatedFromAbove]> {
   let summary = "Define a new operation";
   let description = [{
     `irdl.operation` defines a new operation belonging to the `irdl.dialect`
@@ -200,10 +209,12 @@ def IRDL_OperationOp : IRDL_Op<"operation",
     returns a single result of the element type of the operand.
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name);
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility);
   let regions = (region SizedRegion<1>:$body);
   let assemblyFormat =
-    "$sym_name attr-dict-with-keyword custom<SingleBlockRegion>($body)";
+    "($sym_visibility^)? $sym_name attr-dict-with-keyword "
+    "custom<SingleBlockRegion>($body)";
   let hasRegionVerifier = true;
   let hasVerifier = 1;
 }
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index faeab607d6846..d5373d4bc6186 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1401,7 +1401,8 @@ def LLVM_AddressOfOp : LLVM_Op<"mlir.addressof",
 }
 
 def LLVM_GlobalOp : LLVM_Op<"mlir.global",
-    [IsolatedFromAbove, SingleBlockImplicitTerminator<"ReturnOp">, Symbol]> {
+    [IsolatedFromAbove, SingleBlockImplicitTerminator<"ReturnOp">, SymbolName,
+     SymbolVisibility, Symbol]> {
   let arguments = (ins
     TypeAttr:$global_type,
     UnitAttr:$constant,
@@ -1418,7 +1419,8 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global",
     OptionalAttr<SymbolRefAttr>:$comdat,
     OptionalAttr<DIGlobalVariableExpressionArrayAttr>:$dbg_exprs,
     DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_,
-    OptionalAttr<ArrayAttr>:$target_specific_attrs
+    OptionalAttr<ArrayAttr>:$target_specific_attrs,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
   let summary = "LLVM dialect global.";
   let description = [{
@@ -1647,7 +1649,8 @@ def LLVM_GlobalDtorsOp : LLVM_Op<"mlir.global_dtors", [
 }
 
 def LLVM_AliasOp : LLVM_Op<"mlir.alias",
-    [IsolatedFromAbove, SingleBlockImplicitTerminator<"ReturnOp">, Symbol]> {
+    [IsolatedFromAbove, SingleBlockImplicitTerminator<"ReturnOp">, SymbolName,
+     SymbolVisibility, Symbol]> {
   let arguments = (ins
     TypeAttr:$alias_type,
     StrAttr:$sym_name,
@@ -1655,7 +1658,8 @@ def LLVM_AliasOp : LLVM_Op<"mlir.alias",
     UnitAttr:$dso_local,
     DefaultValuedAttr<LLVM_TLSMode, "mlir::LLVM::ThreadLocalMode::NotThreadLocal">:$tls_mode,
     OptionalAttr<UnnamedAddr>:$unnamed_addr,
-    DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_
+    DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
   let summary = "LLVM dialect alias.";
   let description = [{
@@ -1726,7 +1730,8 @@ def LLVM_AliasOp : LLVM_Op<"mlir.alias",
 }
 
 def LLVM_IFuncOp : LLVM_Op<"mlir.ifunc",
-    [IsolatedFromAbove, Symbol, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
+    [IsolatedFromAbove, SymbolName, SymbolVisibility, Symbol,
+     DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
   let arguments = (ins
     SymbolNameAttr:$sym_name,
     TypeAttr:$i_func_type,
@@ -1736,7 +1741,8 @@ def LLVM_IFuncOp : LLVM_Op<"mlir.ifunc",
     UnitAttr:$dso_local,
     DefaultValuedAttr<ConfinedAttr<I32Attr, [IntNonNegative]>, "0">:$address_space,
     DefaultValuedAttr<UnnamedAddr, "mlir::LLVM::UnnamedAddr::None">:$unnamed_addr,
-    DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_
+    DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
   let summary = "LLVM dialect ifunc";
   let description = [{
@@ -1779,7 +1785,8 @@ def LLVM_IFuncOp : LLVM_Op<"mlir.ifunc",
   ];
 
   let assemblyFormat = [{
-    custom<LLVMLinkage>($linkage) ($visibility_^)? ($unnamed_addr^)?
+    ($sym_visibility^)? custom<LLVMLinkage>($linkage)
+    ($visibility_^)? ($unnamed_addr^)?
     $sym_name `:` $i_func_type `,` $resolver_type $resolver attr-dict
   }];
   let hasVerifier = 1;
@@ -1967,10 +1974,12 @@ def LLVM_IndirectBrOp : LLVM_TerminatorOp<"indirectbr",
   }];
 }
 
-def LLVM_ComdatSelectorOp : LLVM_Op<"comdat_selector", [Symbol]> {
+def LLVM_ComdatSelectorOp
+    : LLVM_Op<"comdat_selector", [SymbolName, SymbolVisibility, Symbol]> {
   let arguments = (ins
     SymbolNameAttr:$sym_name,
-    Comdat:$comdat
+    Comdat:$comdat,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let summary = "LLVM dialect comdat selector declaration";
@@ -1986,12 +1995,15 @@ def LLVM_ComdatSelectorOp : LLVM_Op<"comdat_selector", [Symbol]> {
     llvm.mlir.global internal constant @has_any_comdat(1 : i64) comdat(@__llvm_comdat::@any) : i64
     ```
   }];
-  let assemblyFormat = "$sym_name $comdat attr-dict";
+  let assemblyFormat = "($sym_visibility^)? $sym_name $comdat attr-dict";
 }
 
-def LLVM_ComdatOp : LLVM_Op<"comdat", [NoTerminator, NoRegionArguments, SymbolTable, Symbol]> {
+def LLVM_ComdatOp
+    : LLVM_Op<"comdat", [NoTerminator, NoRegionArguments, SymbolTable,
+                          SymbolName, SymbolVisibility, Symbol]> {
   let arguments = (ins
-    SymbolNameAttr:$sym_name
+    SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
   let summary = "LLVM dialect comdat region";
 
@@ -2012,12 +2024,13 @@ def LLVM_ComdatOp : LLVM_Op<"comdat", [NoTerminator, NoRegionArguments, SymbolTa
   let skipDefaultBuilders = 1;
   let builders = [OpBuilder<(ins "StringRef":$symName)>];
 
-  let assemblyFormat = "$sym_name $body attr-dict";
+  let assemblyFormat = "($sym_visibility^)? $sym_name $body attr-dict";
   let hasRegionVerifier = 1;
 }
 
 def LLVM_LLVMFuncOp : LLVM_Op<"func", [
-    AffineScope, AutomaticAllocationScope, IsolatedFromAbove, FunctionOpInterface
+    AffineScope, AutomaticAllocationScope, IsolatedFromAbove, SymbolName,
+    SymbolVisibility, FunctionOpInterface
   ]> {
   let summary = "LLVM dialect function.";
 
diff --git a/mlir/include/mlir/Dialect/MLProgram/IR/MLProgramOps.td b/mlir/include/mlir/Dialect/MLProgram/IR/MLProgramOps.td
index 095f6bee2c345..32e4d859f1ac2 100644
--- a/mlir/include/mlir/Dialect/MLProgram/IR/MLProgramOps.td
+++ b/mlir/include/mlir/Dialect/MLProgram/IR/MLProgramOps.td
@@ -28,7 +28,7 @@ class MLProgram_Op<string mnemonic, list<Trait> traits = []> :
 //===----------------------------------------------------------------------===//
 
 def MLProgram_FuncOp : MLProgram_Op<"func", [
-    FunctionOpInterface, IsolatedFromAbove,
+    SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove,
     RegionKindInterface, Symbol
   ]> {
   let summary = "Function containing a single `SSACFG` region";
@@ -98,7 +98,7 @@ def MLProgram_FuncOp : MLProgram_Op<"func", [
 //===----------------------------------------------------------------------===//
 
 def MLProgram_GlobalOp : MLProgram_Op<"global", [
-    Symbol
+    SymbolName, SymbolVisibility, Symbol
   ]> {
   let summary = "Module level declaration of a global variable";
   let description = [{
@@ -387,7 +387,7 @@ def MLProgram_GlobalStoreGraphOp : MLProgram_Op<"global_store_graph", [
 //===----------------------------------------------------------------------===//
 
 def MLProgram_SubgraphOp : MLProgram_Op<"subgraph", [
-    FunctionOpInterface, HasOnlyGraphRegion,
+    SymbolName, SymbolVisibility, FunctionOpInterface, HasOnlyGraphRegion,
     IsolatedFromAbove, RegionKindInterface, SingleBlock, Symbol
   ]> {
   let summary = "An function containing a single `Graph` region";
diff --git a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
index 5b2794a8aa540..810eda17268ac 100644
--- a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
+++ b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
@@ -1190,7 +1190,7 @@ def MemRef_GetGlobalOp : MemRef_Op<"get_global",
 // GlobalOp
 //===----------------------------------------------------------------------===//
 
-def MemRef_GlobalOp : MemRef_Op<"global", [Symbol,
+def MemRef_GlobalOp : MemRef_Op<"global", [SymbolName, SymbolVisibility, Symbol,
     DeclareOpInterfaceMethods<AlignmentAttrOpInterface>]> {
   let summary = "declare or define a global memref variable";
   let description = [{
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index aab852d735042..375d2517da6c6 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -1391,7 +1391,8 @@ def OpenACC_UpdateHostOp : OpenACC_DataExitOpWithVarPtr<"update_host",
 //===----------------------------------------------------------------------===//
 
 def OpenACC_PrivateRecipeOp
-    : OpenACC_Op<"private.recipe", [IsolatedFromAbove, Symbol, RecipeInterface,
+    : OpenACC_Op<"private.recipe", [IsolatedFromAbove, SymbolName,
+                                    SymbolVisibility, Symbol, RecipeInterface,
                                     AutomaticAllocationScope]> {
   let summary = "privatization recipe";
 
@@ -1449,13 +1450,15 @@ def OpenACC_PrivateRecipeOp
   }];
 
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttr:$type);
 
   let regions = (region AnyRegion:$initRegion,
                         AnyRegion:$destroyRegion);
 
   let assemblyFormat = [{
-    $sym_name `:` $type attr-dict-with-keyword `init` $initRegion
+    ($sym_visibility^)? $sym_name `:` $type attr-dict-with-keyword
+    `init` $initRegion
     (`destroy` $destroyRegion^)?
   }];
 
@@ -1500,8 +1503,8 @@ def OpenACC_PrivateRecipeOp
 //===----------------------------------------------------------------------===//
 
 def OpenACC_FirstprivateRecipeOp
-    : OpenACC_Op<"firstprivate.recipe", [IsolatedFromAbove, Symbol,
-                                         RecipeInterface,
+    : OpenACC_Op<"firstprivate.recipe", [IsolatedFromAbove, SymbolName,
+                                         SymbolVisibility, Symbol, RecipeInterface,
                                          AutomaticAllocationScope]> {
   let summary = "privatization recipe";
 
@@ -1575,13 +1578,15 @@ def OpenACC_FirstprivateRecipeOp
   }];
 
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttr:$type);
 
   let regions = (region AnyRegion:$initRegion, AnyRegion:$copyRegion,
                         AnyRegion:$destroyRegion);
 
   let assemblyFormat = [{
-    $sym_name `:` $type attr-dict-with-keyword `init` $initRegion
+    ($sym_visibility^)? $sym_name `:` $type attr-dict-with-keyword
+    `init` $initRegion
     `copy` $copyRegion
     (`destroy` $destroyRegion^)?
   }];
@@ -1614,8 +1619,8 @@ def OpenACC_FirstprivateRecipeOp
 //===----------------------------------------------------------------------===//
 
 def OpenACC_ReductionRecipeOp
-    : OpenACC_Op<"reduction.recipe", [IsolatedFromAbove, Symbol,
-                                      RecipeInterface,
+    : OpenACC_Op<"reduction.recipe", [IsolatedFromAbove, SymbolName,
+                                      SymbolVisibility, Symbol, RecipeInterface,
                                       AutomaticAllocationScope]> {
   let summary = "reduction recipe";
 
@@ -1718,6 +1723,7 @@ def OpenACC_ReductionRecipeOp
   }];
 
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttr:$type,
                        OpenACC_ReductionOperatorAttr:$reductionOperator);
 
@@ -1726,7 +1732,7 @@ def OpenACC_ReductionRecipeOp
                         AnyRegion:$destroyRegion);
 
   let assemblyFormat = [{
-    $sym_name `:` $type attr-dict-with-keyword
+    ($sym_visibility^)? $sym_name `:` $type attr-dict-with-keyword
     `reduction_operator` $reductionOperator
     `init` $initRegion `combiner` $combinerRegion
     (`destroy` $destroyRegion^)?
@@ -3226,7 +3232,8 @@ def OpenACC_DeclareExitOp : OpenACC_Op<"declare_exit",
 }
 
 def OpenACC_GlobalConstructorOp : OpenACC_Op<"global_ctor",
-                                             [IsolatedFromAbove, Symbol]> {
+                                             [IsolatedFromAbove, SymbolName,
+                                              SymbolVisibility, Symbol]> {
   let summary = "Used to hold construction operations associated with globals such as declare";
 
   let description = [{
@@ -3249,18 +3256,20 @@ def OpenACC_GlobalConstructorOp : OpenACC_Op<"global_ctor",
     ```
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name);
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility);
   let regions = (region AnyRegion:$region);
 
   let assemblyFormat = [{
-    $sym_name $region attr-dict-with-keyword
+    ($sym_visibility^)? $sym_name $region attr-dict-with-keyword
   }];
 
   let hasVerifier = 0;
 }
 
 def OpenACC_GlobalDestructorOp : OpenACC_Op<"global_dtor",
-                                            [IsolatedFromAbove, Symbol]> {
+                                            [IsolatedFromAbove, SymbolName,
+                                             SymbolVisibility, Symbol]> {
   let summary = "Used to hold destruction operations associated with globals such as declare";
 
   let description = [{
@@ -3284,11 +3293,12 @@ def OpenACC_GlobalDestructorOp : OpenACC_Op<"global_dtor",
     ```
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name);
+  let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility);
   let regions = (region AnyRegion:$region);
 
   let assemblyFormat = [{
-    $sym_name $region attr-dict-with-keyword
+    ($sym_visibility^)? $sym_name $region attr-dict-with-keyword
   }];
 
   let hasVerifier = 0;
@@ -3330,7 +3340,9 @@ def OpenACC_DeclareOp : OpenACC_Op<"declare",
 // 2.15.1 Routine Directive
 //===----------------------------------------------------------------------===//
 
-def OpenACC_RoutineOp : OpenACC_Op<"routine", [IsolatedFromAbove]> {
+def OpenACC_RoutineOp
+    : OpenACC_Op<"routine", [IsolatedFromAbove, SymbolName, SymbolVisibility,
+                             Symbol]> {
   let summary = "acc routine operation";
 
   let description = [{
@@ -3355,7 +3367,8 @@ def OpenACC_RoutineOp : OpenACC_Op<"routine", [IsolatedFromAbove]> {
     must be modified as well.
   }];
 
-  let arguments = (ins SymbolNameAttr:$sym_name, SymbolRefAttr:$func_name,
+  let arguments = (ins SymbolNameAttr:$sym_name,
+      OptionalAttr<StrAttr>:$sym_visibility, SymbolRefAttr:$func_name,
       OptionalAttr<SymbolRefArrayAttr>:$bindIdName,
       OptionalAttr<StrArrayAttr>:$bindStrName,
       OptionalAttr<DeviceTypeArrayAttr>:$bindIdNameDeviceType,
@@ -3373,7 +3386,8 @@ def OpenACC_RoutineOp : OpenACC_Op<"routine", [IsolatedFromAbove]> {
                             ::mlir::Location location,
                             ::llvm::StringRef sym_name,
                             mlir::SymbolRefAttr func_name, bool implicit) {
-      return create(builder, location, sym_name, func_name, /*bindIDName=*/{},
+      return create(builder, location, sym_name, /*sym_visibility=*/nullptr,
+                    func_name, /*bindIDName=*/{},
                     /*bindStrName=*/{}, /*bindIdNameDeviceType=*/{},
                     /*bindStrnameDeviceType=*/{}, /*worker=*/{}, /*vector=*/{},
                     /*seq=*/{}, /*nohost=*/false, implicit, /*gang=*/{},
@@ -3438,7 +3452,7 @@ def OpenACC_RoutineOp : OpenACC_Op<"routine", [IsolatedFromAbove]> {
   }];
 
   let assemblyFormat = [{
-    $sym_name `func` `(` $func_name `)`
+    ($sym_visibility^)? $sym_name `func` `(` $func_name `)`
     oilist (
         `bind` `(` custom<BindName>($bindIdName, $bindStrName ,$bindIdNameDeviceType, $bindStrNameDeviceType) `)`
       | `gang` `` custom<RoutineGangClause>($gang, $gangDim, $gangDimDeviceType)
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
index f90f201a150a1..ca091cf8729b2 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
@@ -274,14 +274,15 @@ class OpenMP_CriticalNameClauseSkip<
   > : OpenMP_Clause<traits, arguments, assemblyFormat, description,
                     extraClassDeclaration> {
   let traits = [
-    Symbol
+    SymbolName, SymbolVisibility, Symbol
   ];
 
   let arguments = (ins
-    SymbolNameAttr:$sym_name
+    SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
-  let reqAssemblyFormat = "$sym_name";
+  let reqAssemblyFormat = "($sym_visibility^)? $sym_name";
 
   let description = [{
     The `sym_name` can be used in `omp.critical` constructs in the dialect.
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 024c6aa2fc1e3..963aece310af2 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -30,7 +30,9 @@ include "mlir/IR/SymbolInterfaces.td"
 // 2.19.4 Data-Sharing Attribute Clauses
 //===----------------------------------------------------------------------===//
 
-def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove, RecipeInterface]> {
+def PrivateClauseOp
+    : OpenMP_Op<"private", [IsolatedFromAbove, RecipeInterface, SymbolName,
+                            SymbolVisibility, Symbol]> {
   let summary = "Provides declaration of [first]private logic.";
   let description = [{
     This operation provides a declaration of how to implement the
@@ -104,6 +106,7 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove, RecipeInterface]>
   }];
 
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttrOf<AnyType>:$type,
                        DataSharingClauseTypeAttr:$data_sharing_type);
 
@@ -112,7 +115,7 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove, RecipeInterface]>
                         AnyRegion:$dealloc_region);
 
   let assemblyFormat = [{
-    $data_sharing_type $sym_name `:` $type
+    $data_sharing_type ($sym_visibility^)? $sym_name `:` $type
       (`init` $init_region^)?
       (`copy` $copy_region^)?
       (`dealloc` $dealloc_region^)? attr-dict
@@ -2221,7 +2224,7 @@ def DeclareMapperOp : OpenMP_Op<"declare_mapper", [
     IsolatedFromAbove,
     RecipeInterface,
     SingleBlock,
-    Symbol
+    SymbolName, SymbolVisibility, Symbol
   ]> {
   let summary = "declare mapper directive";
   let description = [{
@@ -2230,11 +2233,13 @@ def DeclareMapperOp : OpenMP_Op<"declare_mapper", [
   }] # clausesDescription;
 
   let arguments = (ins  SymbolNameAttr:$sym_name,
+                        OptionalAttr<StrAttr>:$sym_visibility,
                         TypeAttr:$type);
 
   let regions = (region AnyRegion:$body);
 
-  let assemblyFormat = "$sym_name `:` $type $body attr-dict";
+  let assemblyFormat =
+      "($sym_visibility^)? $sym_name `:` $type $body attr-dict";
 
   let extraClassDeclaration = [{
     /// Get DeclareMapperInfoOp.
@@ -2281,6 +2286,8 @@ def DeclareMapperInfoOp : OpenMP_Op<"declare_mapper.info",
 
 def DeclareReductionOp : OpenMP_Op<"declare_reduction", [IsolatedFromAbove,
                                                          RecipeInterface,
+                                                         SymbolName,
+                                                         SymbolVisibility,
                                                          Symbol]> {
   let summary = "declares a reduction kind";
   let description = [{
@@ -2331,6 +2338,7 @@ def DeclareReductionOp : OpenMP_Op<"declare_reduction", [IsolatedFromAbove,
   }];
 
   let arguments = (ins SymbolNameAttr:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility,
                        TypeAttr:$type,
                        OptionalAttr<TypeAttr>:$byref_element_type
                        );
@@ -2342,7 +2350,7 @@ def DeclareReductionOp : OpenMP_Op<"declare_reduction", [IsolatedFromAbove,
                         AnyRegion:$cleanupRegion,
                         MaxSizedRegion<1>:$dataPtrPtrRegion);
 
-  let assemblyFormat = "$sym_name "
+  let assemblyFormat = "($sym_visibility^)? $sym_name "
                        "( `byref_element_type` `(` $byref_element_type^ `)` )? "
                        "`:` $type attr-dict-with-keyword "
                        "( `alloc` $allocRegion^ )? "
diff --git a/mlir/include/mlir/Dialect/PDL/IR/PDLOps.td b/mlir/include/mlir/Dialect/PDL/IR/PDLOps.td
index 71f060cc80019..f2279d328a5eb 100644
--- a/mlir/include/mlir/Dialect/PDL/IR/PDLOps.td
+++ b/mlir/include/mlir/Dialect/PDL/IR/PDLOps.td
@@ -393,7 +393,7 @@ def PDL_OperationOp : PDL_Op<"operation", [AttrSizedOperandSegments]> {
 //===----------------------------------------------------------------------===//
 
 def PDL_PatternOp : PDL_Op<"pattern", [
-    IsolatedFromAbove, SingleBlock, Symbol,
+    IsolatedFromAbove, SingleBlock, SymbolName, SymbolVisibility, Symbol,
     DeclareOpInterfaceMethods<OpAsmOpInterface, ["getDefaultDialect"]>
   ]> {
   let summary = "Define a rewrite pattern";
@@ -421,10 +421,12 @@ def PDL_PatternOp : PDL_Op<"pattern", [
   }];
 
   let arguments = (ins ConfinedAttr<I16Attr, [IntNonNegative]>:$benefit,
-                       OptionalAttr<SymbolNameAttr>:$sym_name);
+                       OptionalAttr<SymbolNameAttr>:$sym_name,
+                       OptionalAttr<StrAttr>:$sym_visibility);
   let regions = (region SizedRegion<1>:$bodyRegion);
   let assemblyFormat = [{
-    ($sym_name^)? `:` `benefit` `(` $benefit `)` attr-dict-with-keyword $bodyRegion
+    ($sym_visibility^)? ($sym_name^)? `:` `benefit` `(` $benefit `)`
+    attr-dict-with-keyword $bodyRegion
   }];
 
   let builders = [
diff --git a/mlir/include/mlir/Dialect/PDLInterp/IR/PDLInterpOps.td b/mlir/include/mlir/Dialect/PDLInterp/IR/PDLInterpOps.td
index e72b9293de7ca..752ba8e3c4e97 100644
--- a/mlir/include/mlir/Dialect/PDLInterp/IR/PDLInterpOps.td
+++ b/mlir/include/mlir/Dialect/PDLInterp/IR/PDLInterpOps.td
@@ -641,7 +641,7 @@ def PDLInterp_ForEachOp
 //===----------------------------------------------------------------------===//
 
 def PDLInterp_FuncOp : PDLInterp_Op<"func", [
-    FunctionOpInterface, IsolatedFromAbove
+    SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove
   ]> {
   let summary = "PDL Interpreter Function Operation";
   let description = [{
@@ -665,7 +665,8 @@ def PDLInterp_FuncOp : PDLInterp_Op<"func", [
     SymbolNameAttr:$sym_name,
     TypeAttrOf<FunctionType>:$function_type,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
-    OptionalAttr<DictArrayAttr>:$res_attrs
+    OptionalAttr<DictArrayAttr>:$res_attrs,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
   let regions = (region MinSizedRegion<1>:$body);
 
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGraphOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGraphOps.td
index f0a543d20630c..ae36961698140 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGraphOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGraphOps.td
@@ -36,7 +36,8 @@ class SPIRV_GraphARMOp<string mnemonic, list<Trait> traits = []> :
 
 def SPIRV_GraphARMOp : SPIRV_GraphARMOp<"Graph", [
     AutomaticAllocationScope, DeclareOpInterfaceMethods<CallableOpInterface>,
-    FunctionOpInterface, InModuleScope, IsolatedFromAbove
+    SymbolName, SymbolVisibility, FunctionOpInterface, InModuleScope,
+    IsolatedFromAbove
   ]> {
 
   let summary = "Declare or define a SPIR-V graph";
@@ -77,7 +78,8 @@ def SPIRV_GraphARMOp : SPIRV_GraphARMOp<"Graph", [
     OptionalAttr<DictArrayAttr>:$arg_attrs,
     OptionalAttr<DictArrayAttr>:$res_attrs,
     OptionalAttr<BoolAttr>:$entry_point,
-    StrAttr:$sym_name
+    StrAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let results = (outs);
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td
index 43a0dbac17e66..c8cd881f138c5 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td
@@ -356,7 +356,7 @@ def SPIRV_ExecutionModeIdOp : SPIRV_Op<"ExecutionModeId", []> {
 // -----
 
 def SPIRV_FuncOp : SPIRV_Op<"func", [
-    AutomaticAllocationScope, FunctionOpInterface,
+    AutomaticAllocationScope, SymbolName, SymbolVisibility, FunctionOpInterface,
     InModuleScope, IsolatedFromAbove
   ]> {
   let summary = "Declare or define a function";
@@ -418,7 +418,8 @@ def SPIRV_FuncOp : SPIRV_Op<"func", [
     OptionalAttr<DictArrayAttr>:$res_attrs,
     StrAttr:$sym_name,
     SPIRV_FunctionControlAttr:$function_control,
-    OptionalAttr<SPIRV_LinkageAttributesAttr>:$linkage_attributes
+    OptionalAttr<SPIRV_LinkageAttributesAttr>:$linkage_attributes,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let results = (outs);
@@ -464,7 +465,9 @@ def SPIRV_FuncOp : SPIRV_Op<"func", [
 
 // -----
 
-def SPIRV_GlobalVariableOp : SPIRV_Op<"GlobalVariable", [InModuleScope, Symbol]> {
+def SPIRV_GlobalVariableOp
+    : SPIRV_Op<"GlobalVariable", [InModuleScope, SymbolName, SymbolVisibility,
+                                  Symbol]> {
   let summary = [{
     Allocate an object in memory at module scope. The object is
     referenced using a symbol name.
@@ -517,7 +520,8 @@ def SPIRV_GlobalVariableOp : SPIRV_Op<"GlobalVariable", [InModuleScope, Symbol]>
     OptionalAttr<I32Attr>:$binding,
     OptionalAttr<I32Attr>:$descriptor_set,
     OptionalAttr<StrAttr>:$built_in,
-    OptionalAttr<SPIRV_LinkageAttributesAttr>:$linkage_attributes
+    OptionalAttr<SPIRV_LinkageAttributesAttr>:$linkage_attributes,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let results = (outs);
@@ -567,7 +571,7 @@ def SPIRV_GlobalVariableOp : SPIRV_Op<"GlobalVariable", [InModuleScope, Symbol]>
 
 def SPIRV_ModuleOp : SPIRV_Op<"module",
     [IsolatedFromAbove, NoRegionArguments, NoTerminator,
-     SingleBlock, SymbolTable, Symbol]> {
+     SingleBlock, SymbolTable, SymbolName, SymbolVisibility, Symbol]> {
   let summary = "The top-level op that defines a SPIR-V module";
 
   let description = [{
@@ -616,7 +620,8 @@ def SPIRV_ModuleOp : SPIRV_Op<"module",
     SPIRV_AddressingModelAttr:$addressing_model,
     SPIRV_MemoryModelAttr:$memory_model,
     OptionalAttr<SPIRV_VerCapExtAttr>:$vce_triple,
-    OptionalAttr<StrAttr>:$sym_name
+    OptionalAttr<StrAttr>:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let results = (outs);
@@ -691,7 +696,9 @@ def SPIRV_ReferenceOfOp : SPIRV_Op<"mlir.referenceof", [Pure]> {
 
 // -----
 
-def SPIRV_SpecConstantOp : SPIRV_Op<"SpecConstant", [InModuleScope, Symbol]> {
+def SPIRV_SpecConstantOp
+    : SPIRV_Op<"SpecConstant", [InModuleScope, SymbolName, SymbolVisibility,
+                                Symbol]> {
   let summary = [{
     Declare a new integer-type or floating-point-type scalar specialization
     constant.
@@ -729,7 +736,8 @@ def SPIRV_SpecConstantOp : SPIRV_Op<"SpecConstant", [InModuleScope, Symbol]> {
 
   let arguments = (ins
     StrAttr:$sym_name,
-    TypedAttrInterface:$default_value
+    TypedAttrInterface:$default_value,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let results = (outs);
@@ -742,7 +750,7 @@ def SPIRV_SpecConstantOp : SPIRV_Op<"SpecConstant", [InModuleScope, Symbol]> {
 // -----
 
 def SPIRV_SpecConstantCompositeOp : SPIRV_Op<"SpecConstantComposite", [
-    InModuleScope, Symbol]> {
+    InModuleScope, SymbolName, SymbolVisibility, Symbol]> {
   let summary = "Declare a new composite specialization constant.";
 
   let description = [{
@@ -783,7 +791,8 @@ def SPIRV_SpecConstantCompositeOp : SPIRV_Op<"SpecConstantComposite", [
   let arguments = (ins
     TypeAttr:$type,
     StrAttr:$sym_name,
-    SymbolRefArrayAttr:$constituents
+    SymbolRefArrayAttr:$constituents,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let results = (outs);
@@ -795,7 +804,9 @@ def SPIRV_SpecConstantCompositeOp : SPIRV_Op<"SpecConstantComposite", [
 
 // -----
 
-def SPIRV_EXTSpecConstantCompositeReplicateOp : SPIRV_ExtVendorOp<"SpecConstantCompositeReplicate", [InModuleScope, Symbol]> {
+def SPIRV_EXTSpecConstantCompositeReplicateOp
+    : SPIRV_ExtVendorOp<"SpecConstantCompositeReplicate",
+                        [InModuleScope, SymbolName, SymbolVisibility, Symbol]> {
   let summary = "Declare a new replicated composite specialization constant op.";
 
   let description = [{
@@ -822,7 +833,8 @@ def SPIRV_EXTSpecConstantCompositeReplicateOp : SPIRV_ExtVendorOp<"SpecConstantC
   let arguments = (ins
     TypeAttr:$type,
     StrAttr:$sym_name,
-    SymbolRefAttr:$constituent
+    SymbolRefAttr:$constituent,
+    OptionalAttr<StrAttr>:$sym_visibility
   );
 
   let results = (outs);
diff --git a/mlir/include/mlir/Dialect/Shape/IR/ShapeOps.td b/mlir/include/mlir/Dialect/Shape/IR/ShapeOps.td
index 5ec130c7e2e4a..9adb702af3449 100644
--- a/mlir/include/mlir/Dialect/Shape/IR/ShapeOps.td
+++ b/mlir/include/mlir/Dialect/Shape/IR/ShapeOps.td
@@ -979,7 +979,8 @@ def Shape_CstrRequireOp : Shape_Op<"cstr_require", []> {
 //===----------------------------------------------------------------------===//
 
 def Shape_FunctionLibraryOp : Shape_Op<"function_library",
-    [AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, Symbol,
+    [AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, SymbolName,
+     SymbolVisibility, Symbol,
      NoTerminator, OpAsmOpInterface, SingleBlock]> {
   let summary = "Represents shape functions and corresponding ops";
   let description = [{
@@ -1025,7 +1026,8 @@ def Shape_FunctionLibraryOp : Shape_Op<"function_library",
 
 def Shape_FuncOp : Shape_Op<"func",
     [AffineScope, AutomaticAllocationScope,
-     FunctionOpInterface, IsolatedFromAbove, OpAsmOpInterface]> {
+     SymbolName, SymbolVisibility, FunctionOpInterface, IsolatedFromAbove,
+     OpAsmOpInterface]> {
   let summary = "Shape function";
   let description = [{
     An operation with a name containing a single `SSACFG` region which
diff --git a/mlir/include/mlir/Dialect/Shard/IR/ShardOps.td b/mlir/include/mlir/Dialect/Shard/IR/ShardOps.td
index 60f6d6fc1ffe4..95b7f5624a246 100644
--- a/mlir/include/mlir/Dialect/Shard/IR/ShardOps.td
+++ b/mlir/include/mlir/Dialect/Shard/IR/ShardOps.td
@@ -28,7 +28,8 @@ class Shard_Op<string mnemonic, list<Trait> traits = []> :
     Op<Shard_Dialect, mnemonic, traits> {
 }
 
-def Shard_GridOp : Shard_Op<"grid", [Symbol, Pure]> {
+def Shard_GridOp
+    : Shard_Op<"grid", [SymbolName, SymbolVisibility, Symbol, Pure]> {
   let summary = "Description of a device/process grid.";
   let description = [{
     The shard.grid operation is a symbol operation that identifies a specific
@@ -67,10 +68,12 @@ def Shard_GridOp : Shard_Op<"grid", [Symbol, Pure]> {
   }];
   let arguments = (ins
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     DenseI64ArrayAttr:$shape
   );
   let assemblyFormat = [{
-    $sym_name `(` `shape` `=` custom<DimensionList>($shape) `)`
+    ($sym_visibility^)? $sym_name
+      `(` `shape` `=` custom<DimensionList>($shape) `)`
       attr-dict
   }];
   let extraClassDeclaration = [{
diff --git a/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td b/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
index 2edacfa02b576..c86997790d32e 100644
--- a/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
+++ b/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
@@ -2749,7 +2749,8 @@ def Tosa_WhileOp : Tosa_Op<"while_loop", [
 //===----------------------------------------------------------------------===//
 // Operator: variable
 //===----------------------------------------------------------------------===//
-def Tosa_VariableOp : Tosa_Op<"variable", [Symbol]> {
+def Tosa_VariableOp
+    : Tosa_Op<"variable", [SymbolName, SymbolVisibility, Symbol]> {
   let summary = "Defines a variable";
 
   let description = [{
@@ -2758,10 +2759,10 @@ def Tosa_VariableOp : Tosa_Op<"variable", [Symbol]> {
   }];
 
   let arguments = (ins
-    // Note: "sym_name" is used as opposed to "name" in the specification,
-    // since a Symbol must be named "sym_name" for it to be recognised by
-    // the containing SymbolTable.
+    // Note: "sym_name" is used as opposed to "name" in the specification to
+    // use the conventional representation provided by the SymbolName trait.
     SymbolNameAttr:$sym_name,
+    OptionalAttr<StrAttr>:$sym_visibility,
     IndexElementsAttr:$var_shape,
     TypeAttr:$type,
     OptionalAttr<AnyAttr>:$initial_value
@@ -2770,7 +2771,7 @@ def Tosa_VariableOp : Tosa_Op<"variable", [Symbol]> {
   let hasCustomAssemblyFormat = 1;
 
   let assemblyFormat = [{
-    $sym_name
+    ($sym_visibility^)? $sym_name
     attr-dict
     custom<VariableOpTypeOrInitialValue>($var_shape, $type, $initial_value)
   }];
diff --git a/mlir/include/mlir/Dialect/Transform/IR/TransformOps.td b/mlir/include/mlir/Dialect/Transform/IR/TransformOps.td
index 2daa2ad655e34..97a4f5dee679f 100644
--- a/mlir/include/mlir/Dialect/Transform/IR/TransformOps.td
+++ b/mlir/include/mlir/Dialect/Transform/IR/TransformOps.td
@@ -1007,7 +1007,7 @@ def MergeHandlesOp : TransformDialectOp<"merge_handles",
 }
 
 def NamedSequenceOp : TransformDialectOp<"named_sequence",
-    [FunctionOpInterface,
+    [SymbolName, SymbolVisibility, FunctionOpInterface,
      IsolatedFromAbove,
      DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
      DeclareOpInterfaceMethods<TransformOpInterface>]> {
diff --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
index 8961f6d7dc7f0..2df567c393e59 100644
--- a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
@@ -150,6 +150,7 @@ def WasmSSA_ConstOp : WasmSSA_Op<"const", [
 
 def WasmSSA_FuncOp : WasmSSA_Op<"func", [
     AffineScope, AutomaticAllocationScope,
+    SymbolName,
     DeclareOpInterfaceMethods<FunctionOpInterface, ["verifyBody"]>,
     IsolatedFromAbove,
     Symbol]> {
@@ -213,7 +214,15 @@ def WasmSSA_FuncOp : WasmSSA_Op<"func", [
       return getExported() ?
         ::mlir::SymbolTable::Visibility::Public :
         ::mlir::SymbolTable::Visibility::Nested;
-    };
+    }
+
+    void setVisibility(::mlir::SymbolTable::Visibility visibility) {
+      if (visibility == ::mlir::SymbolTable::Visibility::Private) {
+        emitOpError("Wasm symbols do not support private visibility");
+        return;
+      }
+      setExported(visibility == ::mlir::SymbolTable::Visibility::Public);
+    }
   }];
 
   let builders = [
@@ -240,7 +249,7 @@ def WasmSSA_FuncCallOp : WasmSSA_Op<"call"> {
 }
 
 def WasmSSA_FuncImportOp : WasmSSA_Op<"import_func", [
-    Symbol,
+    SymbolName, Symbol,
     CallableOpInterface,
     ImportOpInterface]> {
   let summary = "Importing a function variable";
@@ -275,7 +284,12 @@ def WasmSSA_FuncImportOp : WasmSSA_Op<"import_func", [
 
     ::mlir::SymbolTable::Visibility getVisibility() {
       return ::mlir::SymbolTable::Visibility::Nested;
-    };
+    }
+
+    void setVisibility(::mlir::SymbolTable::Visibility visibility) {
+      if (visibility != ::mlir::SymbolTable::Visibility::Nested)
+        emitOpError("Wasm imports must have nested visibility");
+    }
   }];
   let builders = [
     OpBuilder<(ins "StringRef":$symbol,
@@ -290,7 +304,8 @@ def WasmSSA_FuncImportOp : WasmSSA_Op<"import_func", [
 
 def WasmSSA_GlobalOp : WasmSSA_Op<"global", [
   AffineScope, AutomaticAllocationScope,
-  IsolatedFromAbove, Symbol, ConstantExpressionInitializerOpTrait]> {
+  IsolatedFromAbove, SymbolName, Symbol,
+  ConstantExpressionInitializerOpTrait]> {
   let summary= "WebAssembly global value";
   let arguments = (ins SymbolNameAttr: $sym_name,
                      WasmSSA_ValTypeAttr: $type,
@@ -328,7 +343,15 @@ def WasmSSA_GlobalOp : WasmSSA_Op<"global", [
       return getExported() ?
         ::mlir::SymbolTable::Visibility::Public :
         ::mlir::SymbolTable::Visibility::Nested;
-    };
+    }
+
+    void setVisibility(::mlir::SymbolTable::Visibility visibility) {
+      if (visibility == ::mlir::SymbolTable::Visibility::Private) {
+        emitOpError("Wasm symbols do not support private visibility");
+        return;
+      }
+      setExported(visibility == ::mlir::SymbolTable::Visibility::Public);
+    }
 
     wasmssa::ReturnOp getInitTerminator();
   }];
@@ -337,7 +360,7 @@ def WasmSSA_GlobalOp : WasmSSA_Op<"global", [
 }
 
 def WasmSSA_GlobalImportOp : WasmSSA_Op<"import_global", [
-    Symbol,
+    SymbolName, Symbol,
     ImportOpInterface]> {
   let summary = "Importing a global variable";
   let description = [{
@@ -360,7 +383,12 @@ def WasmSSA_GlobalImportOp : WasmSSA_Op<"import_global", [
 
     ::mlir::SymbolTable::Visibility getVisibility() {
       return ::mlir::SymbolTable::Visibility::Nested;
-    };
+    }
+
+    void setVisibility(::mlir::SymbolTable::Visibility visibility) {
+      if (visibility != ::mlir::SymbolTable::Visibility::Nested)
+        emitOpError("Wasm imports must have nested visibility");
+    }
   }];
   let hasCustomAssemblyFormat = 1;
 }
@@ -522,7 +550,7 @@ def WasmSSA_LocalTeeOp : WasmSSA_Op<"local_tee", [
   let assemblyFormat = "$localVar `:` type($localVar) `to` $value `:` type($value) attr-dict";
 }
 
-def WasmSSA_MemOp : WasmSSA_Op<"memory", [Symbol]> {
+def WasmSSA_MemOp : WasmSSA_Op<"memory", [SymbolName, Symbol]> {
   let summary= "WebAssembly memory definition";
   let description = [{
     Define a memory to be used by the program.
@@ -551,13 +579,22 @@ def WasmSSA_MemOp : WasmSSA_Op<"memory", [Symbol]> {
       return getExported() ?
         ::mlir::SymbolTable::Visibility::Public :
         ::mlir::SymbolTable::Visibility::Nested;
-    };
+    }
+
+    void setVisibility(::mlir::SymbolTable::Visibility visibility) {
+      if (visibility == ::mlir::SymbolTable::Visibility::Private) {
+        emitOpError("Wasm symbols do not support private visibility");
+        return;
+      }
+      setExported(visibility == ::mlir::SymbolTable::Visibility::Public);
+    }
   }];
 
   let assemblyFormat = "(`exported` $exported^)? $sym_name $limits attr-dict";
 }
 
-def WasmSSA_MemImportOp : WasmSSA_Op<"import_mem", [Symbol, ImportOpInterface]> {
+def WasmSSA_MemImportOp
+    : WasmSSA_Op<"import_mem", [SymbolName, Symbol, ImportOpInterface]> {
   let summary = "Importing a memory";
   let description = [{
     Import a memory from another module.
@@ -577,12 +614,17 @@ def WasmSSA_MemImportOp : WasmSSA_Op<"import_mem", [Symbol, ImportOpInterface]>
       bool isDeclaration() const { return true; }
       ::mlir::SymbolTable::Visibility getVisibility() {
        return ::mlir::SymbolTable::Visibility::Nested;
-      };
+      }
+
+      void setVisibility(::mlir::SymbolTable::Visibility visibility) {
+        if (visibility != ::mlir::SymbolTable::Visibility::Nested)
+          emitOpError("Wasm imports must have nested visibility");
+      }
    }];
   let assemblyFormat = "$importName `from` $moduleName `as` $sym_name $limits attr-dict";
 }
 
-def WasmSSA_TableOp : WasmSSA_Op<"table", [Symbol]> {
+def WasmSSA_TableOp : WasmSSA_Op<"table", [SymbolName, Symbol]> {
   let summary= "WebAssembly table value";
   let arguments = (ins SymbolNameAttr: $sym_name,
                      WasmSSA_TableTypeAttr: $type,
@@ -592,12 +634,21 @@ def WasmSSA_TableOp : WasmSSA_Op<"table", [Symbol]> {
       return getExported() ?
         ::mlir::SymbolTable::Visibility::Public :
         ::mlir::SymbolTable::Visibility::Nested;
-    };
+    }
+
+    void setVisibility(::mlir::SymbolTable::Visibility visibility) {
+      if (visibility == ::mlir::SymbolTable::Visibility::Private) {
+        emitOpError("Wasm symbols do not support private visibility");
+        return;
+      }
+      setExported(visibility == ::mlir::SymbolTable::Visibility::Public);
+    }
   }];
   let assemblyFormat = "(`exported` $exported^)? $sym_name $type attr-dict";
 }
 
-def WasmSSA_TableImportOp : WasmSSA_Op<"import_table", [Symbol, ImportOpInterface]> {
+def WasmSSA_TableImportOp
+    : WasmSSA_Op<"import_table", [SymbolName, Symbol, ImportOpInterface]> {
   let summary = "Importing a table";
   let description = [{
     Import a table from another module.
@@ -617,7 +668,12 @@ def WasmSSA_TableImportOp : WasmSSA_Op<"import_table", [Symbol, ImportOpInterfac
     bool isDeclaration() const { return true; }
     ::mlir::SymbolTable::Visibility getVisibility() {
        return ::mlir::SymbolTable::Visibility::Nested;
-    };
+    }
+
+    void setVisibility(::mlir::SymbolTable::Visibility visibility) {
+      if (visibility != ::mlir::SymbolTable::Visibility::Nested)
+        emitOpError("Wasm imports must have nested visibility");
+    }
   }];
   let assemblyFormat = "$importName `from` $moduleName `as` $sym_name $type attr-dict";
 }
diff --git a/mlir/include/mlir/IR/BuiltinOps.td b/mlir/include/mlir/IR/BuiltinOps.td
index cdc09afe0b67e..f16b686762b48 100644
--- a/mlir/include/mlir/IR/BuiltinOps.td
+++ b/mlir/include/mlir/IR/BuiltinOps.td
@@ -31,7 +31,8 @@ class Builtin_Op<string mnemonic, list<Trait> traits = []> :
 //===----------------------------------------------------------------------===//
 
 def ModuleOp : Builtin_Op<"module", [
-    AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, Symbol,
+    AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, SymbolName,
+    SymbolVisibility, Symbol,
     OpAsmOpInterface
   ] # GraphRegionNoTerminator.traits> {
   let summary = "A top level container operation";
diff --git a/mlir/include/mlir/IR/SymbolInterfaces.td b/mlir/include/mlir/IR/SymbolInterfaces.td
index 75b47a99fcd9e..1a6190c41a075 100644
--- a/mlir/include/mlir/IR/SymbolInterfaces.td
+++ b/mlir/include/mlir/IR/SymbolInterfaces.td
@@ -31,41 +31,16 @@ def Symbol : OpInterface<"SymbolOpInterface"> {
 
   let methods = [
     InterfaceMethod<"Returns the name of this symbol.",
-      "::mlir::StringAttr", "getNameAttr", (ins), [{
-        // Don't rely on the trait implementation as optional symbol operations
-        // may override this.
-        return mlir::SymbolTable::getSymbolName($_op);
-      }], /*defaultImplementation=*/[{
-        return mlir::SymbolTable::getSymbolName(this->getOperation());
-      }]
+      "::mlir::StringAttr", "getNameAttr", (ins)
     >,
     InterfaceMethod<"Sets the name of this symbol.",
-      "void", "setName", (ins "::mlir::StringAttr":$name), [{}],
-      /*defaultImplementation=*/[{
-        this->getOperation()->setAttr(
-            mlir::SymbolTable::getSymbolAttrName(), name);
-      }]
+      "void", "setName", (ins "::mlir::StringAttr":$name)
     >,
-    InterfaceMethod<[{
-        Gets the visibility of this symbol. The default implementation reads
-        the `getDefaultVisibilityAttrName()` attribute and assumes public
-        visibility if it is absent.
-      }],
-      "mlir::SymbolTable::Visibility", "getVisibility", (ins), [{}],
-      /*defaultImplementation=*/[{
-        return ::mlir::detail::defaultGetSymbolVisibility(
-            this->getOperation());
-      }]
+    InterfaceMethod<"Gets the visibility of this symbol.",
+      "mlir::SymbolTable::Visibility", "getVisibility", (ins)
     >,
-    InterfaceMethod<[{
-        Sets the visibility of this symbol. The default implementation writes
-        the `getDefaultVisibilityAttrName()` attribute and drops it for public
-        visibility.
-      }],
-      "void", "setVisibility", (ins "mlir::SymbolTable::Visibility":$vis), [{}],
-      /*defaultImplementation=*/[{
-        ::mlir::detail::defaultSetSymbolVisibility(this->getOperation(), vis);
-      }]
+    InterfaceMethod<"Sets the visibility of this symbol.",
+      "void", "setVisibility", (ins "mlir::SymbolTable::Visibility":$vis)
     >,
     InterfaceMethod<[{
         Get all of the uses of the current symbol that are nested within the
@@ -136,7 +111,7 @@ def Symbol : OpInterface<"SymbolOpInterface"> {
     // If this is an optional symbol, bail out early if possible.
     auto concreteOp = cast<ConcreteOp>($_op);
     if (concreteOp.isOptionalSymbol()) {
-      if(!concreteOp->getInherentAttr(::mlir::SymbolTable::getSymbolAttrName()).value_or(Attribute{}))
+      if (!concreteOp.getNameAttr())
         return success();
     }
     if (::mlir::failed(::mlir::detail::verifySymbol($_op)))
@@ -155,26 +130,26 @@ def Symbol : OpInterface<"SymbolOpInterface"> {
   }];
 
   let extraClassDeclaration = [{
-    /// Return the name of the attribute used for symbol visibility by the
-    /// default implementations of `getVisibility` and `setVisibility`.
-    static ::mlir::StringRef getDefaultVisibilityAttrName() {
-      return "sym_visibility";
-    }
-  }];
-
-  let extraSharedClassDeclaration = [{
-    using Visibility = mlir::SymbolTable::Visibility;
-
     /// Convenience version of `getNameAttr` that returns a StringRef.
     ::mlir::StringRef getName() {
       return getNameAttr().getValue();
     }
 
-    /// Convenience version of `setName` that take a StringRef.
+    /// Convenience version of `setName` that takes a StringRef.
     void setName(::mlir::StringRef name) {
-      setName(::mlir::StringAttr::get($_op->getContext(), name));
+      setName(::mlir::StringAttr::get(getOperation()->getContext(), name));
+    }
+
+    /// Return the conventional symbol visibility attribute name.
+    static ::mlir::StringRef getDefaultVisibilityAttrName() {
+      return "sym_visibility";
     }
 
+  }];
+
+  let extraSharedClassDeclaration = [{
+    using Visibility = mlir::SymbolTable::Visibility;
+
     /// Returns true if this symbol has nested visibility.
     bool isNested() {
       return $_op.getVisibility() == mlir::SymbolTable::Visibility::Nested;
@@ -208,7 +183,7 @@ def Symbol : OpInterface<"SymbolOpInterface"> {
 
   // Add additional classof checks to properly handle "optional" symbols.
   let extraClassOf = [{
-    return $_op->hasAttr(::mlir::SymbolTable::getSymbolAttrName());
+    return static_cast<bool>($_op.getNameAttr());
   }];
 }
 
@@ -256,6 +231,12 @@ def SymbolUserAttrInterface : AttrInterface<"SymbolUserAttrInterface"> {
 // Symbol Traits
 //===----------------------------------------------------------------------===//
 
+// Op stores its symbol name in a `sym_name` inherent attribute.
+def SymbolName : NativeOpTrait<"SymbolName">;
+
+// Op stores its symbol visibility in a `sym_visibility` inherent attribute.
+def SymbolVisibility : NativeOpTrait<"SymbolVisibility">;
+
 // Op defines a symbol table.
 def SymbolTable : NativeOpTrait<"SymbolTable">;
 
diff --git a/mlir/include/mlir/IR/SymbolTable.h b/mlir/include/mlir/IR/SymbolTable.h
index 9d3fdb98248d5..702031e2e95d7 100644
--- a/mlir/include/mlir/IR/SymbolTable.h
+++ b/mlir/include/mlir/IR/SymbolTable.h
@@ -72,9 +72,6 @@ class SymbolTable {
   FailureOr<StringAttr> renameToUnique(Operation *op,
                                        ArrayRef<SymbolTable *> others);
 
-  /// Return the name of the attribute used for symbol names.
-  static StringRef getSymbolAttrName() { return "sym_name"; }
-
   /// Returns the associated operation.
   Operation *getOp() const { return symbolTableOp; }
 
@@ -438,22 +435,66 @@ namespace detail {
 LogicalResult verifySymbolTable(Operation *op);
 LogicalResult verifySymbol(Operation *op);
 
-/// Default implementations of `SymbolOpInterface::getVisibility` and
-/// `SymbolOpInterface::setVisibility`, which keep the visibility in the
-/// `SymbolOpInterface::getDefaultVisibilityAttrName()` attribute. Public
-/// visibility is represented by the absence of that attribute.
-SymbolTable::Visibility defaultGetSymbolVisibility(Operation *symbol);
-void defaultSetSymbolVisibility(Operation *symbol, SymbolTable::Visibility vis);
 } // namespace detail
 
 namespace OpTrait {
+/// A trait that provides the name accessors for symbol operations that store
+/// their name in the conventional `sym_name` inherent attribute.
+template <typename ConcreteType>
+class SymbolName : public TraitBase<ConcreteType, SymbolName> {
+public:
+  StringAttr getNameAttr() {
+    return cast<ConcreteType>(this->getOperation()).getSymNameAttr();
+  }
+
+  StringRef getName() { return getNameAttr().getValue(); }
+
+  void setName(StringAttr name) {
+    cast<ConcreteType>(this->getOperation()).setSymNameAttr(name);
+  }
+
+  void setName(StringRef name) {
+    setName(StringAttr::get(this->getOperation()->getContext(), name));
+  }
+};
+
+/// A trait that provides visibility accessors for symbol operations that store
+/// their visibility in the conventional `sym_visibility` inherent attribute.
+template <typename ConcreteType>
+class SymbolVisibility : public TraitBase<ConcreteType, SymbolVisibility> {
+public:
+  SymbolTable::Visibility getVisibility() {
+    auto concrete = cast<ConcreteType>(this->getOperation());
+    StringAttr visibility = concrete.getSymVisibilityAttr();
+    if (!visibility || visibility.getValue() == "public")
+      return SymbolTable::Visibility::Public;
+    if (visibility.getValue() == "private")
+      return SymbolTable::Visibility::Private;
+    assert(visibility.getValue() == "nested" && "invalid symbol visibility");
+    return SymbolTable::Visibility::Nested;
+  }
+
+  void setVisibility(SymbolTable::Visibility visibility) {
+    auto concrete = cast<ConcreteType>(this->getOperation());
+    if (visibility == SymbolTable::Visibility::Public) {
+      concrete.setSymVisibilityAttr({});
+      return;
+    }
+    assert((visibility == SymbolTable::Visibility::Private ||
+            visibility == SymbolTable::Visibility::Nested) &&
+           "invalid symbol visibility");
+    StringRef value =
+        visibility == SymbolTable::Visibility::Private ? "private" : "nested";
+    concrete.setSymVisibilityAttr(
+        StringAttr::get(this->getOperation()->getContext(), value));
+  }
+};
+
 /// A trait used to provide symbol table functionalities to a region operation.
 /// This operation must hold exactly 1 region. Once attached, all operations
 /// that are directly within the region, i.e not including those within child
-/// regions, that contain a 'SymbolTable::getSymbolAttrName()' StringAttr will
-/// be verified to ensure that the names are uniqued. These operations must also
-/// adhere to the constraints defined by the `Symbol` trait, even if they do not
-/// inherit from it.
+/// regions, and implement `SymbolOpInterface` will be verified to ensure that
+/// their names are uniqued.
 template <typename ConcreteType>
 class SymbolTable : public TraitBase<ConcreteType, SymbolTable> {
 public:
diff --git a/mlir/include/mlir/Interfaces/FunctionInterfaces.td b/mlir/include/mlir/Interfaces/FunctionInterfaces.td
index f701e828ed641..4f312a6210831 100644
--- a/mlir/include/mlir/Interfaces/FunctionInterfaces.td
+++ b/mlir/include/mlir/Interfaces/FunctionInterfaces.td
@@ -132,8 +132,8 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
         OpBuilder &builder, OperationState &state, StringRef name, Type type,
         ArrayRef<NamedAttribute> attrs, TypeRange inputTypes) {
       OpBuilder::InsertionGuard g(builder);
-      state.addAttribute(SymbolTable::getSymbolAttrName(),
-                        builder.getStringAttr(name));
+      state.getOrAddProperties<typename ConcreteOp::Properties>().sym_name =
+          builder.getStringAttr(name);
       state.addAttribute(ConcreteOp::getFunctionTypeAttrName(state.name),
                         TypeAttr::get(type));
       state.attributes.append(attrs.begin(), attrs.end());
diff --git a/mlir/include/mlir/Target/SMTLIB/Namespace.h b/mlir/include/mlir/Target/SMTLIB/Namespace.h
index 09bd5cd2d407b..61364f881a6aa 100644
--- a/mlir/include/mlir/Target/SMTLIB/Namespace.h
+++ b/mlir/include/mlir/Target/SMTLIB/Namespace.h
@@ -47,9 +47,8 @@ class Namespace {
   void add(mlir::ModuleOp module) {
     assert(module->getNumRegions() == 1);
     for (auto &op : module.getBody(0)->getOperations())
-      if (auto symbol = op.getAttrOfType<mlir::StringAttr>(
-              mlir::SymbolTable::getSymbolAttrName()))
-        nextIndex.insert({symbol.getValue(), 0});
+      if (auto symbol = mlir::dyn_cast<mlir::SymbolOpInterface>(&op))
+        nextIndex.insert({symbol.getName(), 0});
   }
 
   /// SymbolCache initializer; initialize from every key that is convertible to
diff --git a/mlir/lib/CAPI/IR/IR.cpp b/mlir/lib/CAPI/IR/IR.cpp
index fcde3dfabd2a4..fdd4c9e52f9ba 100644
--- a/mlir/lib/CAPI/IR/IR.cpp
+++ b/mlir/lib/CAPI/IR/IR.cpp
@@ -1403,7 +1403,7 @@ MlirStringRef mlirIdentifierStr(MlirIdentifier ident) {
 //===----------------------------------------------------------------------===//
 
 MlirStringRef mlirSymbolTableGetSymbolAttributeName() {
-  return wrap(SymbolTable::getSymbolAttrName());
+  return wrap(llvm::StringRef("sym_name"));
 }
 
 MlirStringRef mlirSymbolTableGetDefaultVisibilityAttributeName() {
diff --git a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
index 96a5a77bce341..24e66e835c98d 100644
--- a/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
+++ b/mlir/lib/Conversion/FuncToEmitC/FuncToEmitC.cpp
@@ -93,15 +93,16 @@ getOrCreateMultiReturnType(ConversionPatternRewriter &rewriter, Location loc,
     auto savedIP = rewriter.saveInsertionPoint();
     rewriter.setInsertionPoint(insertBefore);
 
-    emitc::ClassOp classOp = emitc::ClassOp::create(rewriter, loc, structName,
-                                                    /*final_specifier=*/false,
-                                                    emitc::ClassType::struct_);
+    emitc::ClassOp classOp = emitc::ClassOp::create(
+        rewriter, loc, structName, /*sym_visibility=*/nullptr,
+        /*final_specifier=*/false, emitc::ClassType::struct_);
     rewriter.createBlock(&classOp.getBody());
     rewriter.setInsertionPointToStart(&classOp.getBody().front());
 
     for (auto [i, type] : llvm::enumerate(types)) {
       auto fieldName = rewriter.getStringAttr("field" + std::to_string(i));
-      emitc::FieldOp::create(rewriter, loc, fieldName, TypeAttr::get(type),
+      emitc::FieldOp::create(rewriter, loc, fieldName,
+                             /*sym_visibility=*/nullptr, TypeAttr::get(type),
                              nullptr);
     }
 
@@ -297,18 +298,12 @@ class FuncOpConversion final : public OpConversionPattern<func::FuncOp> {
 
     newFuncOp.setArgAttrsAttr(funcOp.getArgAttrsAttr());
     newFuncOp.setResAttrsAttr(funcOp.getResAttrsAttr());
-    if (StringAttr visibility = funcOp.getSymVisibilityAttr())
-      newFuncOp->setDiscardableAttr(
-          SymbolOpInterface::getDefaultVisibilityAttrName(), visibility);
+    newFuncOp.setVisibility(funcOp.getVisibility());
 
-    // Copy over all attributes other than the function name and type.
+    // Copy over the discardable attributes.
     for (const auto &namedAttr :
-         funcOp->getDiscardableAttrDictionary().getValue()) {
-      if (namedAttr.getName() != funcOp.getFunctionTypeAttrName() &&
-          namedAttr.getName() != SymbolTable::getSymbolAttrName())
-        newFuncOp->setDiscardableAttr(namedAttr.getName(),
-                                      namedAttr.getValue());
-    }
+         funcOp->getDiscardableAttrDictionary().getValue())
+      newFuncOp->setDiscardableAttr(namedAttr.getName(), namedAttr.getValue());
 
     // Add `extern` to specifiers if `func.func` is declaration only.
     if (funcOp.isDeclaration()) {
diff --git a/mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.cpp b/mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.cpp
index d254e4b5878e7..8337b2aa558fb 100644
--- a/mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.cpp
+++ b/mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.cpp
@@ -317,13 +317,9 @@ lowerAsEntryFunction(gpu::GPUFuncOp funcOp, const TypeConverter &typeConverter,
   copyGPUProperty(funcOp.getWorkgroupAttributionsAttrName(),
                   funcOp.getWorkgroupAttributionsAttr());
   for (const auto &discardableAttr :
-       funcOp->getDiscardableAttrDictionary().getValue()) {
-    if (discardableAttr.getName() == funcOp.getFunctionTypeAttrName() ||
-        discardableAttr.getName() == SymbolTable::getSymbolAttrName())
-      continue;
+       funcOp->getDiscardableAttrDictionary().getValue())
     newFuncOp->setDiscardableAttr(discardableAttr.getName(),
                                   discardableAttr.getValue());
-  }
 
   rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
                               newFuncOp.end());
@@ -964,7 +960,8 @@ LogicalResult GPUPrintfConversion::matchAndRewrite(
         makeVarName(moduleOp, llvm::Twine(globalVarName) + "_sc");
 
     return spirv::SpecConstantOp::create(
-        rewriter, loc, rewriter.getStringAttr(specCstName), attr);
+        rewriter, loc, rewriter.getStringAttr(specCstName), attr,
+        /*sym_visibility=*/nullptr);
   };
   {
     Operation *parent =
@@ -1000,7 +997,7 @@ LogicalResult GPUPrintfConversion::matchAndRewrite(
     specCstComposite = spirv::SpecConstantCompositeOp::create(
         rewriter, loc, TypeAttr::get(globalType),
         rewriter.getStringAttr(specCstCompositeName),
-        rewriter.getArrayAttr(constituents));
+        rewriter.getArrayAttr(constituents), /*sym_visibility=*/nullptr);
 
     auto ptrType = spirv::PointerType::get(
         globalType, spirv::StorageClass::UniformConstant);
diff --git a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
index 9ac49e2eb1a14..42439c1e00693 100644
--- a/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
+++ b/mlir/lib/Conversion/MemRefToEmitC/MemRefToEmitC.cpp
@@ -403,8 +403,8 @@ struct ConvertGlobal final : public OpConversionPattern<memref::GlobalOp> {
       initialValue = {};
 
     rewriter.replaceOpWithNewOp<emitc::GlobalOp>(
-        op, operands.getSymName(), resultTy, initialValue, externSpecifier,
-        staticSpecifier, operands.getConstant());
+        op, operands.getSymName(), /*sym_visibility=*/nullptr, resultTy,
+        initialValue, externSpecifier, staticSpecifier, operands.getConstant());
     return success();
   }
 };
diff --git a/mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp b/mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp
index 37a2b5046096a..99333f8837704 100644
--- a/mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp
+++ b/mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp
@@ -204,7 +204,8 @@ createDecl(PatternRewriter &builder, SymbolTable &symbolTable,
   OpBuilder::InsertionGuard guard(builder);
   Type type = reduce.getOperands()[reductionIndex].getType();
   auto decl = omp::DeclareReductionOp::create(builder, reduce.getLoc(),
-                                              "__scf_reduction", type,
+                                              "__scf_reduction",
+                                              /*sym_visibility=*/nullptr, type,
                                               /*byref_element_type=*/{});
   symbolTable.insert(decl);
 
diff --git a/mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosa.cpp b/mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosa.cpp
index 45a7b0e68b793..789912d1077c6 100644
--- a/mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosa.cpp
+++ b/mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosa.cpp
@@ -26,7 +26,7 @@ void copyFuncAttrsToGraph(func::FuncOp funcOp, func::FuncOpAdaptor adaptor,
                           spirv::GraphARMOp graphOp) {
   for (NamedAttribute attr : adaptor.getAttributes()) {
     StringRef attrName = attr.getName().getValue();
-    if (llvm::is_contained({SymbolTable::getSymbolAttrName(),
+    if (llvm::is_contained({funcOp.getSymNameAttrName().getValue(),
                             funcOp.getFunctionTypeAttrName().getValue(),
                             funcOp.getArgAttrsAttrName().getValue(),
                             funcOp.getResAttrsAttrName().getValue(),
@@ -135,9 +135,9 @@ struct FuncGraphConvert final : OpConversionPattern<func::FuncOp> {
     auto entryPointAttr = BoolAttr::get(context, true);
     auto graphTy = GraphType::get(
         context, signatureConverter.getConvertedTypes(), newResultTypes);
-    auto graphOp =
-        spirv::GraphARMOp::create(rewriter, funcOp.getLoc(), graphTy, argAttrs,
-                                  resAttrs, entryPointAttr, name);
+    auto graphOp = spirv::GraphARMOp::create(
+        rewriter, funcOp.getLoc(), graphTy, argAttrs, resAttrs, entryPointAttr,
+        name, funcOp.getSymVisibilityAttr());
     copyFuncAttrsToGraph(funcOp, adaptor, graphOp);
 
     rewriter.inlineRegionBefore(funcOp.getBody(), graphOp.getBody(),
diff --git a/mlir/lib/Dialect/Async/IR/Async.cpp b/mlir/lib/Dialect/Async/IR/Async.cpp
index 4d533e1579480..027c7637e025e 100644
--- a/mlir/lib/Dialect/Async/IR/Async.cpp
+++ b/mlir/lib/Dialect/Async/IR/Async.cpp
@@ -302,8 +302,7 @@ LogicalResult AwaitOp::verify() {
 void FuncOp::build(OpBuilder &builder, OperationState &state, StringRef name,
                    FunctionType type, ArrayRef<NamedAttribute> attrs,
                    ArrayRef<DictionaryAttr> argAttrs) {
-  state.addAttribute(SymbolTable::getSymbolAttrName(),
-                     builder.getStringAttr(name));
+  state.getOrAddProperties<Properties>().sym_name = builder.getStringAttr(name);
   state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
 
   state.attributes.append(attrs.begin(), attrs.end());
diff --git a/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp b/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
index 401851dd594de..fc7c26e86cfee 100644
--- a/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
+++ b/mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp
@@ -475,13 +475,9 @@ class AsyncFuncOpLowering : public OpConversionPattern<async::FuncOp> {
 
     SymbolTable::setSymbolVisibility(newFuncOp,
                                      SymbolTable::getSymbolVisibility(op));
-    // Copy over all attributes other than the name.
-    for (const auto &namedAttr :
-         op->getDiscardableAttrDictionary().getValue()) {
-      if (namedAttr.getName() != SymbolTable::getSymbolAttrName())
-        newFuncOp->setDiscardableAttr(namedAttr.getName(),
-                                      namedAttr.getValue());
-    }
+    // Copy over the discardable attributes.
+    for (const auto &namedAttr : op->getDiscardableAttrDictionary().getValue())
+      newFuncOp->setDiscardableAttr(namedAttr.getName(), namedAttr.getValue());
 
     rewriter.inlineRegionBefore(op.getBody(), newFuncOp.getBody(),
                                 newFuncOp.end());
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 4c1db1ff45306..1fb16235b444d 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -781,7 +781,7 @@ DeclareFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 void FuncOp::build(OpBuilder &builder, OperationState &state, StringRef name,
                    FunctionType type, ArrayRef<NamedAttribute> attrs,
                    ArrayRef<DictionaryAttr> argAttrs) {
-  state.addAttribute(SymbolTable::getSymbolAttrName(),
+  state.addAttribute(getSymNameAttrName(state.name),
                      builder.getStringAttr(name));
   state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
   state.attributes.append(attrs.begin(), attrs.end());
diff --git a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
index 1caf379dc5f37..0686fbcde9511 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/MLGOAddReflectionMap.cpp
@@ -197,7 +197,7 @@ class MLGOAddReflectionMapClass : public OpRewritePattern<ClassOp> {
     // attribute string identifying the field
     FieldOp reflectionMapField = FieldOp::create(
         rewriter, classOp.getLoc(), rewriter.getStringAttr("reflectionMap"),
-        TypeAttr::get(mapType),
+        /*sym_visibility=*/nullptr, TypeAttr::get(mapType),
         emitc::OpaqueAttr::get(context, reflectionMapContents));
 
     // Create getBufferForName method
diff --git a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
index 2b24dcac32b8b..894dcaab9305b 100644
--- a/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
+++ b/mlir/lib/Dialect/EmitC/Transforms/WrapFuncInClass.cpp
@@ -79,7 +79,8 @@ class WrapFuncInClass : public OpRewritePattern<FuncOp> {
 
     std::string className = llvm::formatv(
         /*Validate=*/false, classNameFormat.c_str(), funcOp.getName());
-    ClassOp newClassOp = ClassOp::create(rewriter, funcOp.getLoc(), className);
+    ClassOp newClassOp = ClassOp::create(rewriter, funcOp.getLoc(), className,
+                                         /*sym_visibility=*/nullptr);
 
     SmallVector<std::pair<StringAttr, TypeAttr>> fields;
     rewriter.createBlock(&newClassOp.getBody());
@@ -93,8 +94,9 @@ class WrapFuncInClass : public OpRewritePattern<FuncOp> {
       TypeAttr typeAttr = TypeAttr::get(val.getType());
       fields.push_back({fieldName, typeAttr});
 
-      FieldOp fieldop = FieldOp::create(rewriter, funcOp->getLoc(), fieldName,
-                                        typeAttr, nullptr);
+      FieldOp fieldop =
+          FieldOp::create(rewriter, funcOp->getLoc(), fieldName,
+                          /*sym_visibility=*/nullptr, typeAttr, nullptr);
 
       if (argAttrs && idx < argAttrs->size()) {
         fieldop->setDiscardableAttrs(funcOp.getArgAttrDict(idx));
@@ -105,7 +107,8 @@ class WrapFuncInClass : public OpRewritePattern<FuncOp> {
     if (globalsIt != globalsToMove.end()) {
       for (auto global : globalsIt->second) {
         FieldOp::create(rewriter, funcOp->getLoc(), global.getSymNameAttr(),
-                        global.getTypeAttr(), global.getInitialValueAttr());
+                        /*sym_visibility=*/nullptr, global.getTypeAttr(),
+                        global.getInitialValueAttr());
       }
     }
 
diff --git a/mlir/lib/Dialect/Func/IR/FuncOps.cpp b/mlir/lib/Dialect/Func/IR/FuncOps.cpp
index 2493243e24b8d..be31234f98d1f 100644
--- a/mlir/lib/Dialect/Func/IR/FuncOps.cpp
+++ b/mlir/lib/Dialect/Func/IR/FuncOps.cpp
@@ -159,8 +159,7 @@ FuncOp FuncOp::create(Location location, StringRef name, FunctionType type,
 void FuncOp::build(OpBuilder &builder, OperationState &state, StringRef name,
                    FunctionType type, ArrayRef<NamedAttribute> attrs,
                    ArrayRef<DictionaryAttr> argAttrs) {
-  state.addAttribute(SymbolTable::getSymbolAttrName(),
-                     builder.getStringAttr(name));
+  state.getOrAddProperties<Properties>().sym_name = builder.getStringAttr(name);
   state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
   state.attributes.append(attrs.begin(), attrs.end());
   state.addRegion();
diff --git a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
index 22c5e3c9b86ad..f3855229e8fbd 100644
--- a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
+++ b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
@@ -1640,8 +1640,8 @@ void GPUFuncOp::build(OpBuilder &builder, OperationState &result,
                       ArrayRef<NamedAttribute> attrs) {
   OpBuilder::InsertionGuard g(builder);
 
-  result.addAttribute(SymbolTable::getSymbolAttrName(),
-                      builder.getStringAttr(name));
+  result.getOrAddProperties<Properties>().sym_name =
+      builder.getStringAttr(name);
   result.addAttribute(getFunctionTypeAttrName(result.name),
                       TypeAttr::get(type));
   result.addAttribute(getWorkgroupAttributionsAttrName(result.name),
@@ -1715,7 +1715,7 @@ ParseResult GPUFuncOp::parse(OpAsmParser &parser, OperationState &result) {
 
   // Parse the function name.
   StringAttr nameAttr;
-  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
                              result.attributes))
     return failure();
 
@@ -2049,7 +2049,7 @@ void BinaryOp::build(OpBuilder &builder, OperationState &result, StringRef name,
                      Attribute offloadingHandler, ArrayAttr objects) {
   auto &properties = result.getOrAddProperties<Properties>();
   result.attributes.push_back(builder.getNamedAttr(
-      SymbolTable::getSymbolAttrName(), builder.getStringAttr(name)));
+      getSymNameAttrName(result.name), builder.getStringAttr(name)));
   properties.objects = objects;
   if (offloadingHandler)
     properties.offloadingHandler = offloadingHandler;
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 374ad4a9dcb83..c97b61d142d32 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -2405,8 +2405,8 @@ void GlobalOp::build(OpBuilder &builder, OperationState &result, Type type,
                      bool dsoLocal, ThreadLocalMode threadModel,
                      SymbolRefAttr comdat, ArrayRef<NamedAttribute> attrs,
                      ArrayRef<Attribute> dbgExprs) {
-  result.addAttribute(getSymNameAttrName(result.name),
-                      builder.getStringAttr(name));
+  result.getOrAddProperties<Properties>().sym_name =
+      builder.getStringAttr(name);
   result.addAttribute(getGlobalTypeAttrName(result.name), TypeAttr::get(type));
   result.addAttribute(
       getTlsModeAttrName(result.name),
@@ -2480,12 +2480,11 @@ void GlobalOp::print(OpAsmPrinter &p) {
   // Note that the alignment attribute is printed using the
   // default syntax here, even though it is an inherent attribute
   // (as defined in https://mlir.llvm.org/docs/LangRef/#attributes)
-  p.printOptionalAttrDict((*this)->getAttrs(),
-                          {SymbolTable::getSymbolAttrName(),
-                           getGlobalTypeAttrName(), getConstantAttrName(),
-                           getValueAttrName(), getLinkageAttrName(),
-                           getUnnamedAddrAttrName(), getTlsModeAttrName(),
-                           getVisibility_AttrName(), getComdatAttrName()});
+  p.printOptionalAttrDict(
+      (*this)->getAttrs(),
+      {getSymNameAttrName(), getGlobalTypeAttrName(), getConstantAttrName(),
+       getValueAttrName(), getLinkageAttrName(), getUnnamedAddrAttrName(),
+       getTlsModeAttrName(), getVisibility_AttrName(), getComdatAttrName()});
 
   // Print the trailing type unless it's a string global.
   if (llvm::dyn_cast_or_null<StringAttr>(getValueOrNull()))
@@ -2536,6 +2535,7 @@ template <typename OpType>
 static ParseResult parseCommonGlobalAndAlias(OpAsmParser &parser,
                                              OperationState &result) {
   MLIRContext *ctx = parser.getContext();
+
   // Parse optional linkage, default to External.
   result.addAttribute(
       OpType::getLinkageAttrName(result.name),
@@ -2842,10 +2842,9 @@ void AliasOp::print(OpAsmPrinter &p) {
 
   p.printSymbolName(getSymName());
   p.printOptionalAttrDict((*this)->getAttrs(),
-                          {SymbolTable::getSymbolAttrName(),
-                           getAliasTypeAttrName(), getLinkageAttrName(),
-                           getUnnamedAddrAttrName(), getTlsModeAttrName(),
-                           getVisibility_AttrName()});
+                          {getSymNameAttrName(), getAliasTypeAttrName(),
+                           getLinkageAttrName(), getUnnamedAddrAttrName(),
+                           getTlsModeAttrName(), getVisibility_AttrName()});
 
   // Print the trailing type.
   p << " : " << getType() << ' ';
@@ -2950,7 +2949,7 @@ void IFuncOp::build(OpBuilder &builder, OperationState &result, StringRef name,
                     Linkage linkage, LLVM::Visibility visibility) {
   return build(builder, result, name, iFuncType, resolverName, resolverType,
                linkage, /*dso_local=*/false, /*address_space=*/0,
-               UnnamedAddr::None, visibility);
+               UnnamedAddr::None, visibility, /*sym_visibility=*/nullptr);
 }
 
 LogicalResult IFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
@@ -3085,7 +3084,7 @@ void LLVMFuncOp::build(OpBuilder &builder, OperationState &result,
                        ArrayRef<DictionaryAttr> argAttrs,
                        std::optional<uint64_t> functionEntryCount) {
   result.addRegion();
-  result.addAttribute(SymbolTable::getSymbolAttrName(),
+  result.addAttribute(getSymNameAttrName(result.name),
                       builder.getStringAttr(name));
   result.addAttribute(getFunctionTypeAttrName(result.name),
                       TypeAttr::get(type));
@@ -3200,7 +3199,7 @@ ParseResult LLVMFuncOp::parse(OpAsmParser &parser, OperationState &result) {
   bool isVariadic;
 
   auto signatureLocation = parser.getCurrentLocation();
-  if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
                              result.attributes) ||
       function_interface_impl::parseFunctionSignatureWithArguments(
           parser, /*allowVariadic=*/true, entryArgs, isVariadic, resultTypes,
diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/AddComdats.cpp b/mlir/lib/Dialect/LLVMIR/Transforms/AddComdats.cpp
index 1fb482b63670f..685e35a9ef55d 100644
--- a/mlir/lib/Dialect/LLVMIR/Transforms/AddComdats.cpp
+++ b/mlir/lib/Dialect/LLVMIR/Transforms/AddComdats.cpp
@@ -36,7 +36,7 @@ static void addComdat(LLVM::LLVMFuncOp &op, OpBuilder &builder,
   builder.setInsertionPointToStart(&comdatOp.getBody().back());
   auto selectorOp = mlir::LLVM::ComdatSelectorOp::create(
       builder, comdatOp.getLoc(), op.getSymName(),
-      mlir::LLVM::comdat::Comdat::Any);
+      mlir::LLVM::comdat::Comdat::Any, /*sym_visibility=*/nullptr);
   op.setComdatAttr(mlir::SymbolRefAttr::get(
       builder.getContext(), comdatName,
       mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr())));
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index 380ddd061bc06..360eac356cceb 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -1878,7 +1878,8 @@ PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
   OpBuilder::InsertionGuard guard(builder);
 
   // Create the recipe operation first so regions have proper parent context
-  auto recipe = PrivateRecipeOp::create(builder, loc, recipeName, varType);
+  auto recipe = PrivateRecipeOp::create(builder, loc, recipeName,
+                                        /*sym_visibility=*/nullptr, varType);
 
   // Populate the init region
   bool needsFree = false;
@@ -1913,7 +1914,8 @@ PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
   // Create the private.recipe op with the same type as the firstprivate.recipe.
   OpBuilder::InsertionGuard guard(builder);
   auto varType = firstprivRecipe.getType();
-  auto recipe = PrivateRecipeOp::create(builder, loc, recipeName, varType);
+  auto recipe = PrivateRecipeOp::create(builder, loc, recipeName,
+                                        /*sym_visibility=*/nullptr, varType);
 
   // Clone the init region
   IRMapping mapping;
@@ -1975,7 +1977,8 @@ FirstprivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
   OpBuilder::InsertionGuard guard(builder);
 
   // Create the recipe operation first so regions have proper parent context
-  auto recipe = FirstprivateRecipeOp::create(builder, loc, recipeName, varType);
+  auto recipe = FirstprivateRecipeOp::create(
+      builder, loc, recipeName, /*sym_visibility=*/nullptr, varType);
 
   // Populate the init region
   bool needsFree = false;
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
index 1ac1d023e1ff4..bd2ab4df75606 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
@@ -102,6 +102,7 @@ class ACCImplicitRoutine
     auto routineOp = acc::RoutineOp::create(
         builder, loc,
         /* sym_name=*/builder.getStringAttr(routineName),
+        /* sym_visibility=*/nullptr,
         /* func_name=*/
         mlir::SymbolRefAttr::get(builder.getContext(),
                                  builder.getStringAttr(callee.getName())),
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCRoutineToGPUFunc.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCRoutineToGPUFunc.cpp
index c98a7544d5af7..a83f4ac988408 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCRoutineToGPUFunc.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCRoutineToGPUFunc.cpp
@@ -254,8 +254,7 @@ static LogicalResult cloneFuncsToGPUModule(
                            "cannot replace symbol for acc routine");
         return failure();
       }
-      deviceFuncOp->setDiscardableAttr(SymbolTable::getSymbolAttrName(),
-                                       funcName);
+      deviceFuncOp.setName(funcName);
     }
     if (auto specAttr =
             srcFunc->getDiscardableAttrOfType<SpecializedRoutineAttr>(
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 3d2f7ef86753c..85ff885f4a8f3 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -4866,7 +4866,8 @@ std::pair<unsigned, unsigned> FuseOp::getGenerateesODSOperandIndexAndLength() {
 
 void CriticalDeclareOp::build(OpBuilder &builder, OperationState &state,
                               const CriticalDeclareOperands &clauses) {
-  CriticalDeclareOp::build(builder, state, clauses.symName, clauses.hint);
+  CriticalDeclareOp::build(builder, state, clauses.symName,
+                           clauses.symVisibility, clauses.hint);
 }
 
 LogicalResult CriticalDeclareOp::verify() {
@@ -5312,7 +5313,7 @@ void PrivateClauseOp::build(OpBuilder &odsBuilder, OperationState &odsState,
                             TypeRange /*result_types*/, StringAttr symName,
                             TypeAttr type) {
   PrivateClauseOp::build(
-      odsBuilder, odsState, symName, type,
+      odsBuilder, odsState, symName, /*sym_visibility=*/nullptr, type,
       DataSharingClauseTypeAttr::get(odsBuilder.getContext(),
                                      DataSharingClauseType::Private));
 }
diff --git a/mlir/lib/Dialect/PDL/IR/PDL.cpp b/mlir/lib/Dialect/PDL/IR/PDL.cpp
index a28b365cdb4f5..3caee12bbf471 100644
--- a/mlir/lib/Dialect/PDL/IR/PDL.cpp
+++ b/mlir/lib/Dialect/PDL/IR/PDL.cpp
@@ -387,7 +387,8 @@ void PatternOp::build(OpBuilder &builder, OperationState &state,
                       std::optional<uint16_t> benefit,
                       std::optional<StringRef> name) {
   build(builder, state, builder.getI16IntegerAttr(benefit.value_or(0)),
-        name ? builder.getStringAttr(*name) : StringAttr());
+        name ? builder.getStringAttr(*name) : StringAttr(),
+        /*sym_visibility=*/nullptr);
   state.regions[0]->emplaceBlock();
 }
 
diff --git a/mlir/lib/Dialect/SPIRV/IR/ArmGraphOps.cpp b/mlir/lib/Dialect/SPIRV/IR/ArmGraphOps.cpp
index 47fe4d9c5b21c..97bbde4d4cb11 100644
--- a/mlir/lib/Dialect/SPIRV/IR/ArmGraphOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/ArmGraphOps.cpp
@@ -34,9 +34,11 @@ ParseResult spirv::GraphARMOp::parse(OpAsmParser &parser,
                                      OperationState &result) {
   Builder &builder = parser.getBuilder();
 
+  (void)impl::parseOptionalVisibilityKeyword(parser, result.attributes);
+
   // Parse the name as a symbol.
   StringAttr nameAttr;
-  if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
                              result.attributes))
     return failure();
 
@@ -75,16 +77,18 @@ ParseResult spirv::GraphARMOp::parse(OpAsmParser &parser,
 
 void spirv::GraphARMOp::print(OpAsmPrinter &printer) {
   // Print graph name, signature, and control.
-  printer << " ";
+  printer << ' ';
+  if (StringAttr visibility = getSymVisibilityAttr())
+    printer << visibility.getValue() << ' ';
   printer.printSymbolName(getSymName());
   GraphType grType = getFunctionType();
   function_interface_impl::printFunctionSignature(
       printer, *this, grType.getInputs(),
       /*isVariadic=*/false, grType.getResults());
-  function_interface_impl::printFunctionAttributes(printer, *this,
-                                                   {getFunctionTypeAttrName(),
-                                                    getArgAttrsAttrName(),
-                                                    getResAttrsAttrName()});
+  function_interface_impl::printFunctionAttributes(
+      printer, *this,
+      {getFunctionTypeAttrName(), getArgAttrsAttrName(), getResAttrsAttrName(),
+       getSymVisibilityAttrName()});
 
   // Print the body.
   Region &body = this->getBody();
@@ -159,7 +163,7 @@ LogicalResult spirv::GraphARMOp::verifyBody() {
 void spirv::GraphARMOp::build(OpBuilder &builder, OperationState &state,
                               StringRef name, GraphType type,
                               ArrayRef<NamedAttribute> attrs, bool entryPoint) {
-  state.addAttribute(SymbolTable::getSymbolAttrName(),
+  state.addAttribute(getSymNameAttrName(state.name),
                      builder.getStringAttr(name));
   state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
   state.attributes.append(attrs);
diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
index 907e671c43b79..37ae6a942e491 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
@@ -1129,9 +1129,11 @@ ParseResult spirv::FuncOp::parse(OpAsmParser &parser, OperationState &result) {
   SmallVector<Type> resultTypes;
   auto &builder = parser.getBuilder();
 
+  (void)impl::parseOptionalVisibilityKeyword(parser, result.attributes);
+
   // Parse the name as a symbol.
   StringAttr nameAttr;
-  if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
                              result.attributes))
     return failure();
 
@@ -1173,7 +1175,9 @@ ParseResult spirv::FuncOp::parse(OpAsmParser &parser, OperationState &result) {
 
 void spirv::FuncOp::print(OpAsmPrinter &printer) {
   // Print function name, signature, and control.
-  printer << " ";
+  printer << ' ';
+  if (StringAttr visibility = getSymVisibilityAttr())
+    printer << visibility.getValue() << ' ';
   printer.printSymbolName(getSymName());
   auto fnType = getFunctionType();
   function_interface_impl::printFunctionSignature(
@@ -1185,7 +1189,7 @@ void spirv::FuncOp::print(OpAsmPrinter &printer) {
       printer, *this,
       {spirv::attributeName<spirv::FunctionControl>(),
        getFunctionTypeAttrName(), getArgAttrsAttrName(), getResAttrsAttrName(),
-       getFunctionControlAttrName()});
+       getFunctionControlAttrName(), getSymVisibilityAttrName()});
 
   // Print the body if this is not an external function.
   Region &body = this->getBody();
@@ -1317,7 +1321,7 @@ void spirv::FuncOp::build(OpBuilder &builder, OperationState &state,
                           StringRef name, FunctionType type,
                           spirv::FunctionControl control,
                           ArrayRef<NamedAttribute> attrs) {
-  state.addAttribute(SymbolTable::getSymbolAttrName(),
+  state.addAttribute(getSymNameAttrName(state.name),
                      builder.getStringAttr(name));
   state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
   state.addAttribute(spirv::attributeName<spirv::FunctionControl>(),
@@ -1414,11 +1418,13 @@ void spirv::GlobalVariableOp::build(OpBuilder &builder, OperationState &state,
 
 ParseResult spirv::GlobalVariableOp::parse(OpAsmParser &parser,
                                            OperationState &result) {
+  (void)impl::parseOptionalVisibilityKeyword(parser, result.attributes);
+
   // Parse variable name.
   StringAttr nameAttr;
   StringRef initializerAttrName =
       spirv::GlobalVariableOp::getInitializerAttrName(result.name);
-  if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
                              result.attributes)) {
     return failure();
   }
@@ -1458,8 +1464,11 @@ void spirv::GlobalVariableOp::print(OpAsmPrinter &printer) {
 
   // Print variable name.
   printer << ' ';
+  if (StringAttr visibility = getSymVisibilityAttr())
+    printer << visibility.getValue() << ' ';
   printer.printSymbolName(getSymName());
-  elidedAttrs.push_back(SymbolTable::getSymbolAttrName());
+  elidedAttrs.push_back(getSymNameAttrName());
+  elidedAttrs.push_back(getSymVisibilityAttrName());
 
   StringRef initializerAttrName = this->getInitializerAttrName();
   // Print optional initializer
@@ -1675,7 +1684,7 @@ void spirv::ModuleOp::build(OpBuilder &builder, OperationState &state,
   OpBuilder::InsertionGuard guard(builder);
   builder.createBlock(state.addRegion());
   if (name) {
-    state.attributes.append(mlir::SymbolTable::getSymbolAttrName(),
+    state.attributes.append(getSymNameAttrName(state.name),
                             builder.getStringAttr(*name));
   }
 }
@@ -1695,7 +1704,7 @@ void spirv::ModuleOp::build(OpBuilder &builder, OperationState &state,
   if (vceTriple)
     state.addAttribute(getVCETripleAttrName(), *vceTriple);
   if (name)
-    state.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
+    state.addAttribute(getSymNameAttrName(state.name),
                        builder.getStringAttr(*name));
 }
 
@@ -1703,10 +1712,12 @@ ParseResult spirv::ModuleOp::parse(OpAsmParser &parser,
                                    OperationState &result) {
   Region *body = result.addRegion();
 
+  (void)impl::parseOptionalVisibilityKeyword(parser, result.attributes);
+
   // If the name is present, parse it.
   StringAttr nameAttr;
   (void)parser.parseOptionalSymbolName(
-      nameAttr, mlir::SymbolTable::getSymbolAttrName(), result.attributes);
+      nameAttr, getSymNameAttrName(result.name), result.attributes);
 
   // Parse attributes
   spirv::AddressingModel addrModel;
@@ -1737,6 +1748,8 @@ ParseResult spirv::ModuleOp::parse(OpAsmParser &parser,
 }
 
 void spirv::ModuleOp::print(OpAsmPrinter &printer) {
+  if (StringAttr visibility = getSymVisibilityAttr())
+    printer << ' ' << visibility.getValue();
   if (std::optional<StringRef> name = getName()) {
     printer << ' ';
     printer.printSymbolName(*name);
@@ -1749,7 +1762,7 @@ void spirv::ModuleOp::print(OpAsmPrinter &printer) {
   auto addressingModelAttrName = spirv::attributeName<spirv::AddressingModel>();
   auto memoryModelAttrName = spirv::attributeName<spirv::MemoryModel>();
   elidedAttrs.assign({addressingModelAttrName, memoryModelAttrName,
-                      mlir::SymbolTable::getSymbolAttrName()});
+                      getSymNameAttrName(), getSymVisibilityAttrName()});
 
   if (std::optional<spirv::VerCapExtAttr> triple = getVceTriple()) {
     printer << " requires " << *triple;
@@ -1865,12 +1878,14 @@ LogicalResult spirv::ReferenceOfOp::verify() {
 
 ParseResult spirv::SpecConstantOp::parse(OpAsmParser &parser,
                                          OperationState &result) {
+  (void)impl::parseOptionalVisibilityKeyword(parser, result.attributes);
+
   StringAttr nameAttr;
   Attribute valueAttr;
   StringRef defaultValueAttrName =
       spirv::SpecConstantOp::getDefaultValueAttrName(result.name);
 
-  if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
                              result.attributes))
     return failure();
 
@@ -1892,6 +1907,8 @@ ParseResult spirv::SpecConstantOp::parse(OpAsmParser &parser,
 
 void spirv::SpecConstantOp::print(OpAsmPrinter &printer) {
   printer << ' ';
+  if (StringAttr visibility = getSymVisibilityAttr())
+    printer << visibility.getValue() << ' ';
   printer.printSymbolName(getSymName());
   if (auto specID =
           (*this)->getDiscardableAttrOfType<IntegerAttr>(kSpecIdAttrName))
@@ -1952,8 +1969,10 @@ LogicalResult spirv::VectorShuffleOp::verify() {
 ParseResult spirv::SpecConstantCompositeOp::parse(OpAsmParser &parser,
                                                   OperationState &result) {
 
+  (void)impl::parseOptionalVisibilityKeyword(parser, result.attributes);
+
   StringAttr compositeName;
-  if (parser.parseSymbolName(compositeName, SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(compositeName, getSymNameAttrName(result.name),
                              result.attributes))
     return failure();
 
@@ -1994,7 +2013,9 @@ ParseResult spirv::SpecConstantCompositeOp::parse(OpAsmParser &parser,
 }
 
 void spirv::SpecConstantCompositeOp::print(OpAsmPrinter &printer) {
-  printer << " ";
+  printer << ' ';
+  if (StringAttr visibility = getSymVisibilityAttr())
+    printer << visibility.getValue() << ' ';
   printer.printSymbolName(getSymName());
   printer << " (" << llvm::interleaved(this->getConstituents().getValue())
           << ") : " << getType();
@@ -2053,13 +2074,15 @@ LogicalResult spirv::SpecConstantCompositeOp::verify() {
 ParseResult
 spirv::EXTSpecConstantCompositeReplicateOp::parse(OpAsmParser &parser,
                                                   OperationState &result) {
+  (void)impl::parseOptionalVisibilityKeyword(parser, result.attributes);
+
   StringAttr compositeName;
   FlatSymbolRefAttr specConstRef;
   const char *attrName = "spec_const";
   NamedAttrList attrs;
   Type type;
 
-  if (parser.parseSymbolName(compositeName, SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(compositeName, getSymNameAttrName(result.name),
                              result.attributes) ||
       parser.parseLParen() ||
       parser.parseAttribute(specConstRef, Type(), attrName, attrs) ||
@@ -2079,7 +2102,9 @@ spirv::EXTSpecConstantCompositeReplicateOp::parse(OpAsmParser &parser,
 }
 
 void spirv::EXTSpecConstantCompositeReplicateOp::print(OpAsmPrinter &printer) {
-  printer << " ";
+  printer << ' ';
+  if (StringAttr visibility = getSymVisibilityAttr())
+    printer << visibility.getValue() << ' ';
   printer.printSymbolName(getSymName());
   printer << " (" << this->getConstituent() << ") : " << getType();
 }
diff --git a/mlir/lib/Dialect/SPIRV/Linking/ModuleCombiner/ModuleCombiner.cpp b/mlir/lib/Dialect/SPIRV/Linking/ModuleCombiner/ModuleCombiner.cpp
index f4467afa1a32c..8068344a02a89 100644
--- a/mlir/lib/Dialect/SPIRV/Linking/ModuleCombiner/ModuleCombiner.cpp
+++ b/mlir/lib/Dialect/SPIRV/Linking/ModuleCombiner/ModuleCombiner.cpp
@@ -72,12 +72,12 @@ static LogicalResult updateSymbolAndAllUses(SymbolOpInterface op,
 static llvm::hash_code computeHash(SymbolOpInterface symbolOp) {
   NamedAttrList attrs(symbolOp->getDiscardableAttrDictionary());
   symbolOp->getName().populateInherentAttrs(symbolOp, attrs);
-  auto range = llvm::make_filter_range(attrs, [](NamedAttribute attr) {
-    return attr.getName() != SymbolTable::getSymbolAttrName();
-  });
+  // `populateInherentAttrs` adds the name property back to the list. Remove it
+  // so otherwise-identical symbols still hash equally after being renamed.
+  attrs.erase("sym_name");
 
   return llvm::hash_combine(symbolOp->getName(),
-                            llvm::hash_combine_range(range));
+                            llvm::hash_combine_range(attrs));
 }
 
 namespace mlir {
diff --git a/mlir/lib/Dialect/SPIRV/Transforms/ConvertToReplicatedConstantCompositePass.cpp b/mlir/lib/Dialect/SPIRV/Transforms/ConvertToReplicatedConstantCompositePass.cpp
index 739c3092d36a7..4bc4dfab7a57f 100644
--- a/mlir/lib/Dialect/SPIRV/Transforms/ConvertToReplicatedConstantCompositePass.cpp
+++ b/mlir/lib/Dialect/SPIRV/Transforms/ConvertToReplicatedConstantCompositePass.cpp
@@ -109,7 +109,8 @@ struct SpecConstantCompositeOpConversion final
           op, "expected flat symbol reference for splat constituent");
 
     rewriter.replaceOpWithNewOp<spirv::EXTSpecConstantCompositeReplicateOp>(
-        op, TypeAttr::get(op.getType()), op.getSymNameAttr(), splatConstituent);
+        op, TypeAttr::get(op.getType()), op.getSymNameAttr(), splatConstituent,
+        op.getSymVisibilityAttr());
 
     return success();
   }
diff --git a/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp b/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp
index 413ebbbe78548..7d15d40de3b1e 100644
--- a/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp
+++ b/mlir/lib/Dialect/SPIRV/Transforms/SPIRVConversion.cpp
@@ -1027,14 +1027,10 @@ struct FuncOpConversion final : OpConversionPattern<func::FuncOp> {
         .setVisibility(
             cast<SymbolOpInterface>(funcOp.getOperation()).getVisibility());
 
-    // Copy over all attributes other than the function name and type.
+    // Copy over the discardable attributes.
     for (NamedAttribute namedAttr :
-         funcOp->getDiscardableAttrDictionary().getValue()) {
-      if (namedAttr.getName() != funcOp.getFunctionTypeAttrName() &&
-          namedAttr.getName() != SymbolTable::getSymbolAttrName())
-        newFuncOp->setDiscardableAttr(namedAttr.getName(),
-                                      namedAttr.getValue());
-    }
+         funcOp->getDiscardableAttrDictionary().getValue())
+      newFuncOp->setDiscardableAttr(namedAttr.getName(), namedAttr.getValue());
 
     rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
                                 newFuncOp.end());
diff --git a/mlir/lib/Dialect/Shape/IR/Shape.cpp b/mlir/lib/Dialect/Shape/IR/Shape.cpp
index 0bb130eac1bff..4604786da7881 100644
--- a/mlir/lib/Dialect/Shape/IR/Shape.cpp
+++ b/mlir/lib/Dialect/Shape/IR/Shape.cpp
@@ -1220,8 +1220,8 @@ OpFoldResult FromExtentsOp::fold(FoldAdaptor adaptor) {
 
 void FunctionLibraryOp::build(OpBuilder &builder, OperationState &result,
                               StringRef name) {
-  result.attributes.push_back(builder.getNamedAttr(
-      ::mlir::SymbolTable::getSymbolAttrName(), builder.getStringAttr(name)));
+  result.getOrAddProperties<Properties>().sym_name =
+      builder.getStringAttr(name);
 }
 
 FuncOp FunctionLibraryOp::getShapeFunction(Operation *op) {
@@ -1236,7 +1236,7 @@ ParseResult FunctionLibraryOp::parse(OpAsmParser &parser,
                                      OperationState &result) {
   // Parse the op name.
   StringAttr nameAttr;
-  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),
+  if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
                              result.attributes))
     return failure();
 
@@ -1262,7 +1262,7 @@ void FunctionLibraryOp::print(OpAsmPrinter &p) {
   p << ' ';
   p.printSymbolName(getName());
   p.printOptionalAttrDictWithKeyword(
-      (*this)->getAttrs(), {mlir::SymbolTable::getSymbolAttrName(), "mapping"});
+      (*this)->getDiscardableAttrDictionary().getValue());
   p << ' ';
   p.printRegion(getRegion(), /*printEntryBlockArgs=*/false,
                 /*printBlockTerminators=*/false);
diff --git a/mlir/lib/Dialect/Transform/IR/TransformOps.cpp b/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
index 2f1648a0b0c30..e40f3b7885c27 100644
--- a/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
+++ b/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
@@ -2550,8 +2550,8 @@ void transform::NamedSequenceOp::build(OpBuilder &builder,
                                        SequenceBodyBuilderFn bodyBuilder,
                                        ArrayRef<NamedAttribute> attrs,
                                        ArrayRef<DictionaryAttr> argAttrs) {
-  state.addAttribute(SymbolTable::getSymbolAttrName(),
-                     builder.getStringAttr(symName));
+  state.getOrAddProperties<Properties>().sym_name =
+      builder.getStringAttr(symName);
   state.addAttribute(getFunctionTypeAttrName(state.name),
                      TypeAttr::get(FunctionType::get(builder.getContext(),
                                                      rootType, resultTypes)));
diff --git a/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
index eab9c7a9d53a7..e0e4b085bc21e 100644
--- a/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
+++ b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
@@ -87,8 +87,10 @@ ParseResult parseImportOp(OpAsmParser &parser, OperationState &result) {
     return failure();
 
   StringAttr symbolName;
-  res = parser.parseSymbolName(symbolName, SymbolTable::getSymbolAttrName(),
-                               result.attributes);
+  res = parser.parseSymbolName(symbolName);
+  if (succeeded(res))
+    result.getOrAddProperties<GlobalImportOp::Properties>().sym_name =
+        symbolName;
   return res;
 }
 } // namespace
@@ -272,7 +274,7 @@ ParseResult GlobalOp::parse(OpAsmParser &parser, OperationState &result) {
     result.addAttribute(getExportedAttrName(result.name), UnitAttr::get(ctx));
   }
 
-  res = parser.parseSymbolName(symbolName, SymbolTable::getSymbolAttrName(),
+  res = parser.parseSymbolName(symbolName, getSymNameAttrName(result.name),
                                result.attributes);
   res = parser.parseType(globalType);
   result.addAttribute(getTypeAttrName(result.name), TypeAttr::get(globalType));
diff --git a/mlir/lib/IR/BuiltinAttributes.cpp b/mlir/lib/IR/BuiltinAttributes.cpp
index 0f84f7a682fcf..876f5f878fdea 100644
--- a/mlir/lib/IR/BuiltinAttributes.cpp
+++ b/mlir/lib/IR/BuiltinAttributes.cpp
@@ -348,10 +348,8 @@ FlatSymbolRefAttr SymbolRefAttr::get(StringAttr value) {
 }
 
 FlatSymbolRefAttr SymbolRefAttr::get(Operation *symbol) {
-  auto symName =
-      symbol->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
-  assert(symName && "value does not have a valid symbol name");
-  return SymbolRefAttr::get(symName);
+  auto symbolOp = cast<SymbolOpInterface>(symbol);
+  return SymbolRefAttr::get(symbolOp.getNameAttr());
 }
 
 StringAttr SymbolRefAttr::getLeafReference() const {
diff --git a/mlir/lib/IR/BuiltinDialect.cpp b/mlir/lib/IR/BuiltinDialect.cpp
index 0d6389ce2e5d4..c692a4dda518c 100644
--- a/mlir/lib/IR/BuiltinDialect.cpp
+++ b/mlir/lib/IR/BuiltinDialect.cpp
@@ -125,7 +125,7 @@ void ModuleOp::build(OpBuilder &builder, OperationState &state,
   state.addRegion()->emplaceBlock();
   if (name) {
     state.attributes.push_back(builder.getNamedAttr(
-        mlir::SymbolTable::getSymbolAttrName(), builder.getStringAttr(*name)));
+        getSymNameAttrName(state.name), builder.getStringAttr(*name)));
   }
 }
 
@@ -164,7 +164,7 @@ LogicalResult ModuleOp::verify() {
     if (!attr.getName().strref().contains('.') &&
         !llvm::is_contained(
             ArrayRef<StringRef>{
-                mlir::SymbolTable::getSymbolAttrName(),
+                getSymNameAttrName().getValue(),
                 mlir::SymbolOpInterface::getDefaultVisibilityAttrName()},
             attr.getName().strref()))
       return emitOpError() << "can only contain attributes with "
diff --git a/mlir/lib/IR/SymbolTable.cpp b/mlir/lib/IR/SymbolTable.cpp
index cdf5a7d7c1469..31e9ae5c5dfe2 100644
--- a/mlir/lib/IR/SymbolTable.cpp
+++ b/mlir/lib/IR/SymbolTable.cpp
@@ -11,7 +11,6 @@
 #include "mlir/IR/OpImplementation.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringSwitch.h"
 #include <optional>
 
 using namespace mlir;
@@ -25,10 +24,10 @@ static bool isPotentiallyUnknownSymbolTable(Operation *op) {
 /// Returns the string name of the given symbol, or null if this is not a
 /// symbol.
 static StringAttr getNameIfSymbol(Operation *op) {
-  return op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
-}
-static StringAttr getNameIfSymbol(Operation *op, StringAttr symbolAttrNameId) {
-  return op->getAttrOfType<StringAttr>(symbolAttrNameId);
+  auto symbol = dyn_cast<SymbolOpInterface>(op);
+  if (!symbol)
+    return {};
+  return symbol.getNameAttr();
 }
 
 /// Computes the nested symbol reference attribute for the symbol 'symbolName'
@@ -40,7 +39,6 @@ collectValidReferencesFor(Operation *symbol, StringAttr symbolName,
                           Operation *within,
                           SmallVectorImpl<SymbolRefAttr> &results) {
   assert(within->isAncestor(symbol) && "expected 'within' to be an ancestor");
-  MLIRContext *ctx = symbol->getContext();
 
   auto leafRef = FlatSymbolRefAttr::get(symbolName);
   results.push_back(leafRef);
@@ -52,14 +50,12 @@ collectValidReferencesFor(Operation *symbol, StringAttr symbolName,
 
   // Collect references until 'symbolTableOp' reaches 'within'.
   SmallVector<FlatSymbolRefAttr, 1> nestedRefs(1, leafRef);
-  StringAttr symbolNameId =
-      StringAttr::get(ctx, SymbolTable::getSymbolAttrName());
   do {
     // Each parent of 'symbol' should define a symbol table.
     if (!symbolTableOp->hasTrait<OpTrait::SymbolTable>())
       return failure();
     // Each parent of 'symbol' should also be a symbol.
-    StringAttr symbolTableName = getNameIfSymbol(symbolTableOp, symbolNameId);
+    StringAttr symbolTableName = getNameIfSymbol(symbolTableOp);
     if (!symbolTableName)
       return failure();
     results.push_back(SymbolRefAttr::get(symbolTableName, nestedRefs));
@@ -123,10 +119,8 @@ SymbolTable::SymbolTable(Operation *symbolTableOp)
   assert(symbolTableOp->getRegion(0).hasOneBlock() &&
          "expected operation to have a single block");
 
-  StringAttr symbolNameId = StringAttr::get(symbolTableOp->getContext(),
-                                            SymbolTable::getSymbolAttrName());
   for (auto &op : symbolTableOp->getRegion(0).front()) {
-    StringAttr name = getNameIfSymbol(&op, symbolNameId);
+    StringAttr name = getNameIfSymbol(&op);
     if (!name)
       continue;
 
@@ -292,14 +286,16 @@ SymbolTable::renameToUnique(Operation *op, ArrayRef<SymbolTable *> others) {
 
 /// Returns the name of the given symbol operation.
 StringAttr SymbolTable::getSymbolName(Operation *symbol) {
-  StringAttr name = getNameIfSymbol(symbol);
+  auto symbolOp = cast<SymbolOpInterface>(symbol);
+  StringAttr name = symbolOp.getNameAttr();
   assert(name && "expected valid symbol name");
   return name;
 }
 
 /// Sets the name of the given symbol operation.
 void SymbolTable::setSymbolName(Operation *symbol, StringAttr name) {
-  symbol->setAttr(getSymbolAttrName(), name);
+  auto symbolOp = cast<SymbolOpInterface>(symbol);
+  symbolOp.setName(name);
 }
 
 /// Returns the visibility of the given symbol operation.
@@ -373,10 +369,8 @@ Operation *SymbolTable::lookupSymbolIn(Operation *symbolTableOp,
     return nullptr;
 
   // Look for a symbol with the given name.
-  StringAttr symbolNameId = StringAttr::get(symbolTableOp->getContext(),
-                                            SymbolTable::getSymbolAttrName());
   for (auto &op : region.front())
-    if (getNameIfSymbol(&op, symbolNameId) == symbol)
+    if (getNameIfSymbol(&op) == symbol)
       return &op;
   return nullptr;
 }
@@ -472,8 +466,7 @@ LogicalResult detail::verifySymbolTable(Operation *op) {
   for (auto &block : op->getRegion(0)) {
     for (auto &op : block) {
       // Check for a symbol name attribute.
-      auto nameAttr =
-          op.getAttrOfType<StringAttr>(mlir::SymbolTable::getSymbolAttrName());
+      StringAttr nameAttr = getNameIfSymbol(&op);
       if (!nameAttr)
         continue;
 
@@ -493,7 +486,7 @@ LogicalResult detail::verifySymbolTable(Operation *op) {
     if (SymbolUserOpInterface user = dyn_cast<SymbolUserOpInterface>(op))
       if (failed(user.verifySymbolUses(symbolTable)))
         return WalkResult::interrupt();
-    for (auto &attr : op->getDiscardableAttrs()) {
+    for (auto &attr : op->getDiscardableAttrDictionary().getValue()) {
       if (auto user = dyn_cast<SymbolUserAttrInterface>(attr.getValue())) {
         if (failed(user.verifySymbolUses(op, symbolTable)))
           return WalkResult::interrupt();
@@ -509,14 +502,13 @@ LogicalResult detail::verifySymbolTable(Operation *op) {
 
 LogicalResult detail::verifySymbol(Operation *op) {
   // Verify the name attribute.
-  if (!op->getAttrOfType<StringAttr>(mlir::SymbolTable::getSymbolAttrName()))
-    return op->emitOpError() << "requires string attribute '"
-                             << mlir::SymbolTable::getSymbolAttrName() << "'";
+  if (!cast<SymbolOpInterface>(op).getNameAttr())
+    return op->emitOpError("requires a symbol name");
 
   // Verify the visibility attribute.
   StringRef visAttrName =
       mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
-  if (Attribute vis = op->getAttr(visAttrName)) {
+  if (Attribute vis = op->getInherentAttr(visAttrName).value_or(Attribute{})) {
     StringAttr visStrAttr = llvm::dyn_cast<StringAttr>(vis);
     if (!visStrAttr)
       return op->emitOpError()
@@ -533,41 +525,6 @@ LogicalResult detail::verifySymbol(Operation *op) {
   return success();
 }
 
-SymbolTable::Visibility detail::defaultGetSymbolVisibility(Operation *symbol) {
-  StringAttr vis = symbol->getAttrOfType<StringAttr>(
-      SymbolOpInterface::getDefaultVisibilityAttrName());
-  // If the attribute doesn't exist, assume public.
-  if (!vis)
-    return SymbolTable::Visibility::Public;
-
-  // Otherwise, switch on the string value.
-  return StringSwitch<SymbolTable::Visibility>(vis.getValue())
-      .Case("private", SymbolTable::Visibility::Private)
-      .Case("nested", SymbolTable::Visibility::Nested)
-      .Case("public", SymbolTable::Visibility::Public);
-}
-
-void detail::defaultSetSymbolVisibility(Operation *symbol,
-                                        SymbolTable::Visibility vis) {
-  StringRef attrName = SymbolOpInterface::getDefaultVisibilityAttrName();
-
-  // If the visibility is public, just drop the attribute as this is the
-  // default.
-  if (vis == SymbolTable::Visibility::Public) {
-    symbol->removeAttr(attrName);
-    return;
-  }
-
-  // Otherwise, update the attribute.
-  assert((vis == SymbolTable::Visibility::Private ||
-          vis == SymbolTable::Visibility::Nested) &&
-         "unknown symbol visibility kind");
-
-  StringRef visName =
-      vis == SymbolTable::Visibility::Private ? "private" : "nested";
-  symbol->setAttr(attrName, StringAttr::get(symbol->getContext(), visName));
-}
-
 //===----------------------------------------------------------------------===//
 // Symbol Use Lists
 //===----------------------------------------------------------------------===//
@@ -578,14 +535,23 @@ void detail::defaultSetSymbolVisibility(Operation *symbol,
 static WalkResult
 walkSymbolRefs(Operation *op,
                function_ref<WalkResult(SymbolTable::SymbolUse)> callback) {
-  return op->getAttrDictionary().walk<WalkOrder::PreOrder>(
-      [&](SymbolRefAttr symbolRef) {
-        if (callback({op, symbolRef}).wasInterrupted())
-          return WalkResult::interrupt();
-
-        // Don't walk nested references.
-        return WalkResult::skip();
-      });
+  bool interrupted = false;
+  auto walk = [&](Attribute attr) {
+    if (interrupted)
+      return;
+    interrupted = attr.walk<WalkOrder::PreOrder>([&](SymbolRefAttr symbolRef) {
+                        if (callback({op, symbolRef}).wasInterrupted())
+                          return WalkResult::interrupt();
+
+                        // Don't walk nested references.
+                        return WalkResult::skip();
+                      })
+                      .wasInterrupted();
+  };
+  walk(op->getRawDictionaryAttrs());
+  op->getName().walkInherentAttrs(
+      op, [&](StringRef, Attribute &attr) { walk(attr); });
+  return interrupted ? WalkResult::interrupt() : WalkResult::advance();
 }
 
 /// Walk all of the uses, for any symbol, that are nested within the given
diff --git a/mlir/lib/Interfaces/FunctionImplementation.cpp b/mlir/lib/Interfaces/FunctionImplementation.cpp
index 94d4cc08aa0eb..3913081b58383 100644
--- a/mlir/lib/Interfaces/FunctionImplementation.cpp
+++ b/mlir/lib/Interfaces/FunctionImplementation.cpp
@@ -97,8 +97,7 @@ ParseResult function_interface_impl::parseFunctionOp(
 
   // Parse the name as a symbol.
   StringAttr nameAttr;
-  if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
-                             result.attributes))
+  if (parser.parseSymbolName(nameAttr, "sym_name", result.attributes))
     return failure();
 
   // Parse the function signature.
@@ -132,7 +131,7 @@ ParseResult function_interface_impl::parseFunctionOp(
   // dictionary.
   for (StringRef disallowed :
        {SymbolOpInterface::getDefaultVisibilityAttrName(),
-        SymbolTable::getSymbolAttrName(), typeAttrName.getValue()}) {
+        StringRef("sym_name"), typeAttrName.getValue()}) {
     if (parsedAttributes.get(disallowed))
       return parser.emitError(attributeDictLocation, "'")
              << disallowed
@@ -166,7 +165,7 @@ ParseResult function_interface_impl::parseFunctionOp(
 void function_interface_impl::printFunctionAttributes(
     OpAsmPrinter &p, Operation *op, ArrayRef<StringRef> elided) {
   // Print out function attributes, if present.
-  SmallVector<StringRef, 8> ignoredAttrs = {SymbolTable::getSymbolAttrName()};
+  SmallVector<StringRef, 8> ignoredAttrs = {"sym_name"};
   ignoredAttrs.append(elided.begin(), elided.end());
 
   NamedAttrList attrs(op->getDiscardableAttrDictionary().getValue());
@@ -179,15 +178,16 @@ void function_interface_impl::printFunctionOp(
     OpAsmPrinter &p, FunctionOpInterface op, bool isVariadic,
     StringRef typeAttrName, StringAttr argAttrsName, StringAttr resAttrsName) {
   // Print the operation and the function name.
-  auto funcName =
-      op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
-          .getValue();
+  auto symbol = cast<SymbolOpInterface>(op.getOperation());
+  StringRef funcName = symbol.getName();
   p << ' ';
 
   StringRef visibilityAttrName =
       SymbolOpInterface::getDefaultVisibilityAttrName();
-  if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
-    p << visibility.getValue() << ' ';
+  Attribute visibility =
+      op->getInherentAttr(visibilityAttrName).value_or(Attribute{});
+  if (auto value = dyn_cast_or_null<StringAttr>(visibility))
+    p << value.getValue() << ' ';
   p.printSymbolName(funcName);
 
   ArrayRef<Type> argTypes = op.getArgumentTypes();
diff --git a/mlir/lib/Pass/IRPrinting.cpp b/mlir/lib/Pass/IRPrinting.cpp
index 006ce3cbe794a..61a6adb0f1a24 100644
--- a/mlir/lib/Pass/IRPrinting.cpp
+++ b/mlir/lib/Pass/IRPrinting.cpp
@@ -70,9 +70,8 @@ static void printIRHeader(raw_ostream &out, StringRef title, Pass *pass,
   pass->printAsTextualPipeline(out);
   if (printModuleScope) {
     out << " ('" << op->getName() << "' operation";
-    if (auto symbolName =
-            op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
-      out << ": @" << symbolName.getValue();
+    if (auto symbol = dyn_cast<SymbolOpInterface>(op))
+      out << ": @" << symbol.getName();
     out << ")";
   }
   out << " //----- //\n";
@@ -230,10 +229,8 @@ getOpAndSymbolNames(Operation *op, StringRef passName,
   ++counters.try_emplace(op, -1).first->second;
   while (iter) {
     countPrefix.push_back(counters[iter]);
-    StringAttr symbolNameAttr =
-        iter->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
-    std::string symbolName =
-        symbolNameAttr ? symbolNameAttr.str() : "no-symbol-name";
+    auto symbol = dyn_cast<SymbolOpInterface>(iter);
+    std::string symbolName = symbol ? symbol.getName().str() : "no-symbol-name";
     llvm::replace(symbolName, '/', '_');
     llvm::replace(symbolName, '\\', '_');
 
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 08b23c0bbdfa4..02cdb6088b6ac 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1113,7 +1113,8 @@ void ModuleImport::processComdat(const llvm::Comdat *comdat) {
   builder.setInsertionPointToEnd(&comdatOp.getBody().back());
   auto selectorOp = ComdatSelectorOp::create(
       builder, mlirModule.getLoc(), comdat->getName(),
-      convertComdatFromLLVM(comdat->getSelectionKind()));
+      convertComdatFromLLVM(comdat->getSelectionKind()),
+      /*sym_visibility=*/nullptr);
   auto symbolRef =
       SymbolRefAttr::get(builder.getContext(), getGlobalComdatOpName(),
                          FlatSymbolRefAttr::get(selectorOp.getSymNameAttr()));
@@ -1547,7 +1548,8 @@ LogicalResult ModuleImport::convertIFunc(llvm::GlobalIFunc *ifunc) {
                   convertLinkageFromLLVM(ifunc->getLinkage()),
                   ifunc->isDSOLocal(), ifunc->getAddressSpace(),
                   convertUnnamedAddrFromLLVM(ifunc->getUnnamedAddr()),
-                  convertVisibilityFromLLVM(ifunc->getVisibility()));
+                  convertVisibilityFromLLVM(ifunc->getVisibility()),
+                  /*sym_visibility=*/nullptr);
   return success();
 }
 
diff --git a/mlir/lib/Target/SPIRV/Deserialization/Deserializer.cpp b/mlir/lib/Target/SPIRV/Deserialization/Deserializer.cpp
index 8e02d070c0cfa..ff8f6acbc3990 100644
--- a/mlir/lib/Target/SPIRV/Deserialization/Deserializer.cpp
+++ b/mlir/lib/Target/SPIRV/Deserialization/Deserializer.cpp
@@ -986,7 +986,8 @@ spirv::Deserializer::createSpecConstant(Location loc, uint32_t resultID,
                                         TypedAttr defaultValue) {
   auto symName = opBuilder.getStringAttr(getSpecConstantSymbol(resultID));
   auto op = spirv::SpecConstantOp::create(opBuilder, unknownLoc, symName,
-                                          defaultValue);
+                                          defaultValue,
+                                          /*sym_visibility=*/nullptr);
   if (decorations.count(resultID)) {
     for (auto attr : decorations[resultID].getAttrs())
       setInherentOrDiscardableAttr(op, attr.getName(), attr.getValue());
@@ -2013,7 +2014,7 @@ spirv::Deserializer::processSpecConstantComposite(ArrayRef<uint32_t> operands) {
 
   auto op = spirv::SpecConstantCompositeOp::create(
       opBuilder, unknownLoc, TypeAttr::get(resultType), symName,
-      opBuilder.getArrayAttr(elements));
+      opBuilder.getArrayAttr(elements), /*sym_visibility=*/nullptr);
   specConstCompositeMap[resultID] = op;
 
   return success();
@@ -2047,7 +2048,8 @@ LogicalResult spirv::Deserializer::processSpecConstantCompositeReplicateEXT(
       getSpecConstant(operands[2]);
   auto op = spirv::EXTSpecConstantCompositeReplicateOp::create(
       opBuilder, unknownLoc, TypeAttr::get(resultType), symName,
-      SymbolRefAttr::get(constituentSpecConstantOp));
+      SymbolRefAttr::get(constituentSpecConstantOp),
+      /*sym_visibility=*/nullptr);
 
   specConstCompositeReplicateMap[resultID] = op;
 
diff --git a/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp b/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
index 9ed2401ac4099..442064fcb6fe5 100644
--- a/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
+++ b/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
@@ -712,7 +712,7 @@ Serializer::processGlobalVariableOp(spirv::GlobalVariableOp varOp) {
 
   // Encode the name.
   auto varName = varOp.getSymName();
-  elidedAttrs.push_back(SymbolTable::getSymbolAttrName());
+  elidedAttrs.push_back(varOp.getSymNameAttrName());
   if (failed(processName(resultID, varName))) {
     return failure();
   }
diff --git a/mlir/python/mlir/dialects/gpu/__init__.py b/mlir/python/mlir/dialects/gpu/__init__.py
index b75bd525c9487..2ce4e3a24bf6b 100644
--- a/mlir/python/mlir/dialects/gpu/__init__.py
+++ b/mlir/python/mlir/dialects/gpu/__init__.py
@@ -86,7 +86,10 @@ def __init__(
             if not isinstance(function_type, TypeAttr)
             else function_type
         )
+        if not isinstance(sym_name, (str, StringAttr)):
+            raise ValueError("sym_name must be a string or a StringAttr")
         super().__init__(
+            sym_name,
             function_type,
             arg_attrs=arg_attrs,
             res_attrs=res_attrs,
@@ -96,13 +99,6 @@ def __init__(
             ip=ip,
         )
 
-        if isinstance(sym_name, str):
-            self.attributes[self.SYM_NAME_ATTR_NAME] = StringAttr.get(sym_name)
-        elif isinstance(sym_name, StringAttr):
-            self.attributes[self.SYM_NAME_ATTR_NAME] = sym_name
-        else:
-            raise ValueError("sym_name must be a string or a StringAttr")
-
         if kernel:
             self.attributes[self.KERNEL_ATTR_NAME] = UnitAttr.get()
 
diff --git a/mlir/test/Conversion/TosaToSPIRVTosa/op-nesting.mlir b/mlir/test/Conversion/TosaToSPIRVTosa/op-nesting.mlir
index 11e76d7df1637..0a4f5f730d51e 100644
--- a/mlir/test/Conversion/TosaToSPIRVTosa/op-nesting.mlir
+++ b/mlir/test/Conversion/TosaToSPIRVTosa/op-nesting.mlir
@@ -3,8 +3,8 @@
 // CHECK: gpu.module @random_container
 gpu.module @random_container {
   // CHECK: spirv.module @_spirv_tosa_nested Logical Vulkan attributes {spirv.target_env = #spirv.target_env<
-  // CHECK: spirv.ARM.Graph @nested(%[[ARG0:.*]]: !spirv.arm.tensor<1xi8> {spirv.interface_var_abi = #spirv.interface_var_abi<(0, 0)>}) -> (!spirv.arm.tensor<1xi8> {spirv.interface_var_abi = #spirv.interface_var_abi<(0, 1)>}) attributes {entry_point = true} {
-  func.func @nested(%arg0: tensor<1xi8>) -> tensor<1xi8> {
+  // CHECK: spirv.ARM.Graph private @nested(%[[ARG0:.*]]: !spirv.arm.tensor<1xi8> {spirv.interface_var_abi = #spirv.interface_var_abi<(0, 0)>}) -> (!spirv.arm.tensor<1xi8> {spirv.interface_var_abi = #spirv.interface_var_abi<(0, 1)>}) attributes {entry_point = true} {
+  func.func private @nested(%arg0: tensor<1xi8>) -> tensor<1xi8> {
     // CHECK: spirv.ARM.GraphOutputs %[[ARG0]] : !spirv.arm.tensor<1xi8>
     return %arg0 : tensor<1xi8>
   }
diff --git a/mlir/test/Dialect/LLVMIR/alias.mlir b/mlir/test/Dialect/LLVMIR/alias.mlir
index 32c795113a035..4b7d8e073aa29 100644
--- a/mlir/test/Dialect/LLVMIR/alias.mlir
+++ b/mlir/test/Dialect/LLVMIR/alias.mlir
@@ -15,6 +15,12 @@ llvm.mlir.alias external @_ZTV1D : !llvm.struct<(array<3 x ptr>)> {
   llvm.return %0 : !llvm.ptr
 }
 
+llvm.mlir.global external @private_symbol_global() {sym_visibility = "private"} : i32
+llvm.mlir.alias external @nested_alias {sym_visibility = "nested"} : i32 {
+  %0 = llvm.mlir.addressof @private_symbol_global : !llvm.ptr
+  llvm.return %0 : !llvm.ptr
+}
+
 // CHECK: llvm.mlir.alias external @foo_alias : !llvm.ptr {
 // CHECK:   %[[ADDR:.*]] = llvm.mlir.addressof @callee : !llvm.ptr
 // CHECK:   llvm.return %[[ADDR]] : !llvm.ptr
@@ -23,6 +29,8 @@ llvm.mlir.alias external @_ZTV1D : !llvm.struct<(array<3 x ptr>)> {
 // CHECK:   %[[ADDR:.*]] = llvm.mlir.addressof @callee : !llvm.ptr
 // CHECK:   llvm.return %[[ADDR]] : !llvm.ptr
 // CHECK: }
+// CHECK: llvm.mlir.global external @private_symbol_global() {addr_space = 0 : i32, sym_visibility = "private"} : i32
+// CHECK: llvm.mlir.alias external @nested_alias {sym_visibility = "nested"} : i32 {
 
 // -----
 
diff --git a/mlir/test/Dialect/OpenMP/cli-canonical_loop.mlir b/mlir/test/Dialect/OpenMP/cli-canonical_loop.mlir
index 0e9385ee75c47..76d450424ffda 100644
--- a/mlir/test/Dialect/OpenMP/cli-canonical_loop.mlir
+++ b/mlir/test/Dialect/OpenMP/cli-canonical_loop.mlir
@@ -236,9 +236,8 @@ func.func @omp_newcli_unused() -> () {
 }
 
 
-// CHECK-LABEL: @omp_canonloop_multiregion_isolatedfromabove(
-func.func @omp_canonloop_multiregion_isolatedfromabove() -> () {
-  omp.private {type = firstprivate} @x.privatizer : !llvm.ptr init {
+// CHECK-LABEL: omp.private {{.*}} @x.privatizer
+omp.private {type = firstprivate} @x.privatizer : !llvm.ptr init {
     ^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr):
       %c42_i32 = arith.constant 42: i32
       // CHECK: omp.canonical_loop %iv : i32 in range(%c42_i32) {
@@ -273,10 +272,6 @@ func.func @omp_canonloop_multiregion_isolatedfromabove() -> () {
       }
       // CHECK: omp.yield
       omp.yield
-  }
-
-  // CHECK: return
-  return
 }
 
 
diff --git a/mlir/test/Dialect/SPIRV/Transforms/replicated-const-composites.mlir b/mlir/test/Dialect/SPIRV/Transforms/replicated-const-composites.mlir
index f0c0248bf694d..b27529eee97e1 100644
--- a/mlir/test/Dialect/SPIRV/Transforms/replicated-const-composites.mlir
+++ b/mlir/test/Dialect/SPIRV/Transforms/replicated-const-composites.mlir
@@ -200,8 +200,8 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, ReplicatedCompos
 
   spirv.SpecConstant @sc_i32_1 = 1 : i32
 
-  // CHECK: spirv.EXT.SpecConstantCompositeReplicate @scc_splat_array_of_i32 (@sc_i32_1) : !spirv.array<3 x i32>
-  spirv.SpecConstantComposite @scc_splat_array_of_i32 (@sc_i32_1, @sc_i32_1, @sc_i32_1) : !spirv.array<3 x i32>
+  // CHECK: spirv.EXT.SpecConstantCompositeReplicate private @scc_splat_array_of_i32 (@sc_i32_1) : !spirv.array<3 x i32>
+  spirv.SpecConstantComposite private @scc_splat_array_of_i32 (@sc_i32_1, @sc_i32_1, @sc_i32_1) : !spirv.array<3 x i32>
 
   // CHECK: spirv.EXT.SpecConstantCompositeReplicate @scc_splat_struct_of_i32 (@sc_i32_1) : !spirv.struct<(i32, i32, i32)>
   spirv.SpecConstantComposite @scc_splat_struct_of_i32 (@sc_i32_1, @sc_i32_1, @sc_i32_1) : !spirv.struct<(i32, i32, i32)>
diff --git a/mlir/test/Dialect/Shard/backward-sharding-propagation.mlir b/mlir/test/Dialect/Shard/backward-sharding-propagation.mlir
index 8894c4aee49c0..f88e38ecb8bff 100644
--- a/mlir/test/Dialect/Shard/backward-sharding-propagation.mlir
+++ b/mlir/test/Dialect/Shard/backward-sharding-propagation.mlir
@@ -2,7 +2,7 @@
 
 #map = affine_map<(d0, d1) -> (d0, d1)>
 module {
-  shard.grid @grid(shape = 1) {sym_visibility = "private"}
+  shard.grid "private" @grid(shape = 1)
   func.func @test_forward() -> tensor<6x6xi32> {
     %c1_i32 = arith.constant 1 : i32
     // CHECK: tensor.empty()
diff --git a/mlir/test/Dialect/Shard/forward-backward-sharding-propagation.mlir b/mlir/test/Dialect/Shard/forward-backward-sharding-propagation.mlir
index 0d8d99752620a..3faaff28c6920 100644
--- a/mlir/test/Dialect/Shard/forward-backward-sharding-propagation.mlir
+++ b/mlir/test/Dialect/Shard/forward-backward-sharding-propagation.mlir
@@ -2,7 +2,7 @@
 
 #map = affine_map<(d0, d1) -> (d0, d1)>
 module {
-  shard.grid @grid(shape = 1) {sym_visibility = "private"}
+  shard.grid "private" @grid(shape = 1)
   func.func @test_forward() -> tensor<6x6xi32> {
     %c1_i32 = arith.constant 1 : i32
     // CHECK: tensor.empty()
diff --git a/mlir/test/Dialect/Shard/forward-sharding-propagation.mlir b/mlir/test/Dialect/Shard/forward-sharding-propagation.mlir
index 3cda9eaa365fd..a5faafb438da0 100644
--- a/mlir/test/Dialect/Shard/forward-sharding-propagation.mlir
+++ b/mlir/test/Dialect/Shard/forward-sharding-propagation.mlir
@@ -2,7 +2,7 @@
 
 #map = affine_map<(d0, d1) -> (d0, d1)>
 module attributes {dlti.map = #dlti.map<"MPI:Implementation" = "mpich", "MPI:comm_world_rank" = 0 : i32>} {
-  shard.grid @grid(shape = 1) {sym_visibility = "private"}
+  shard.grid "private" @grid(shape = 1)
   func.func @test_forward() -> (tensor<6x6xi32>, tensor<6x6xi32>, tensor<i32>) attributes {llvm.emit_c_interface} {
     %c1_i32 = arith.constant 1 : i32
     // CHECK: [[v3:%.*]] = tensor.empty() : tensor<6x6xi32>
diff --git a/mlir/test/Dialect/Shard/sharding-propagation-failed.mlir b/mlir/test/Dialect/Shard/sharding-propagation-failed.mlir
index 3459c1c9f6edc..c80bf754a7bb3 100644
--- a/mlir/test/Dialect/Shard/sharding-propagation-failed.mlir
+++ b/mlir/test/Dialect/Shard/sharding-propagation-failed.mlir
@@ -1,6 +1,6 @@
 // RUN: mlir-opt --pass-pipeline="builtin.module(func.func(sharding-propagation))" %s -verify-diagnostics
 
-shard.grid @grid(shape = 1) {sym_visibility = "private"}
+shard.grid "private" @grid(shape = 1)
 // expected-error @+1 {{'func.func' op only one block is supported!}}
 func.func @multi_block_function(%arg0 : tensor<6x6xi32>) -> tensor<6x6xi32> {
     %sharding = shard.sharding @grid split_axes = [[0]] : !shard.sharding
diff --git a/mlir/test/lib/Dialect/Test/TestOpDefs.cpp b/mlir/test/lib/Dialect/Test/TestOpDefs.cpp
index a6988ce0c27d4..06871fd0ea835 100644
--- a/mlir/test/lib/Dialect/Test/TestOpDefs.cpp
+++ b/mlir/test/lib/Dialect/Test/TestOpDefs.cpp
@@ -26,6 +26,14 @@ SymbolTable::Visibility OverriddenSymbolVisibilityOp::getVisibility() {
   return SymbolTable::Visibility::Private;
 }
 
+StringAttr OverriddenSymbolVisibilityOp::getNameAttr() {
+  return getSymNameAttr();
+}
+
+void OverriddenSymbolVisibilityOp::setName(StringAttr name) {
+  setSymNameAttr(name);
+}
+
 static StringLiteral getVisibilityString(SymbolTable::Visibility visibility) {
   switch (visibility) {
   case SymbolTable::Visibility::Private:
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index 1aaecdcb885b9..dc0da7b54c90b 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.td
+++ b/mlir/test/lib/Dialect/Test/TestOps.td
@@ -169,13 +169,15 @@ def TestTokenRegionWithoutTraitOp
 // Test Symbols
 //===----------------------------------------------------------------------===//
 
-def SymbolOp : TEST_Op<"symbol", [NoMemoryEffect, Symbol]> {
+def SymbolOp
+    : TEST_Op<"symbol", [NoMemoryEffect, SymbolName, SymbolVisibility, Symbol]> {
   let summary =  "operation which defines a new symbol";
   let arguments = (ins StrAttr:$sym_name,
                        OptionalAttr<StrAttr>:$sym_visibility);
 }
 
-def SymbolWithResultOp : TEST_Op<"symbol_with_result", [Symbol]> {
+def SymbolWithResultOp
+    : TEST_Op<"symbol_with_result", [SymbolName, SymbolVisibility, Symbol]> {
   let summary = "invalid symbol operation that produces an SSA result";
   let arguments = (ins StrAttr:$sym_name,
                        OptionalAttr<StrAttr>:$sym_visibility);
@@ -183,6 +185,7 @@ def SymbolWithResultOp : TEST_Op<"symbol_with_result", [Symbol]> {
 }
 
 def OverriddenSymbolVisibilityOp : TEST_Op<"overridden_symbol_visibility", [
+  SymbolName,
   DeclareOpInterfaceMethods<Symbol, ["getVisibility", "setVisibility"]>,
 ]> {
   let summary =  "operation overridden symbol visibility accessors";
@@ -729,7 +732,9 @@ def ConversionCallOp : TEST_Op<"conversion_call_op",
   }];
 }
 
-def ConversionFuncOp : TEST_Op<"conversion_func_op", [FunctionOpInterface]> {
+def ConversionFuncOp
+    : TEST_Op<"conversion_func_op",
+              [SymbolName, SymbolVisibility, FunctionOpInterface]> {
   let arguments = (ins SymbolNameAttr:$sym_name,
                        TypeAttrOf<FunctionType>:$function_type,
                        OptionalAttr<DictArrayAttr>:$arg_attrs,
diff --git a/mlir/tools/tblgen-to-irdl/OpDefinitionsGen.cpp b/mlir/tools/tblgen-to-irdl/OpDefinitionsGen.cpp
index c3b1de0dff18d..15418cac2fcc9 100644
--- a/mlir/tools/tblgen-to-irdl/OpDefinitionsGen.cpp
+++ b/mlir/tools/tblgen-to-irdl/OpDefinitionsGen.cpp
@@ -393,7 +393,8 @@ static irdl::OperationOp createIRDLOperation(OpBuilder &builder,
   StringRef opName = getOperatorName(tblgenOp);
 
   irdl::OperationOp op = irdl::OperationOp::create(
-      builder, UnknownLoc::get(ctx), StringAttr::get(ctx, opName));
+      builder, UnknownLoc::get(ctx), StringAttr::get(ctx, opName),
+      /*sym_visibility=*/nullptr);
 
   // Add the block in the region.
   Block &opBlock = op.getBody().emplaceBlock();
@@ -499,7 +500,8 @@ static irdl::TypeOp createIRDLType(OpBuilder &builder,
   std::string combined = ("!" + typeName).str();
 
   irdl::TypeOp op = irdl::TypeOp::create(builder, UnknownLoc::get(ctx),
-                                         StringAttr::get(ctx, combined));
+                                         StringAttr::get(ctx, combined),
+                                         /*sym_visibility=*/nullptr);
 
   op.getBody().emplaceBlock();
 
@@ -513,7 +515,8 @@ static irdl::AttributeOp createIRDLAttr(OpBuilder &builder,
   std::string combined = ("#" + attrName).str();
 
   irdl::AttributeOp op = irdl::AttributeOp::create(
-      builder, UnknownLoc::get(ctx), StringAttr::get(ctx, combined));
+      builder, UnknownLoc::get(ctx), StringAttr::get(ctx, combined),
+      /*sym_visibility=*/nullptr);
 
   op.getBody().emplaceBlock();
 
@@ -523,7 +526,8 @@ static irdl::AttributeOp createIRDLAttr(OpBuilder &builder,
 static irdl::DialectOp createIRDLDialect(OpBuilder &builder) {
   MLIRContext *ctx = builder.getContext();
   return irdl::DialectOp::create(builder, UnknownLoc::get(ctx),
-                                 StringAttr::get(ctx, selectedDialect));
+                                 StringAttr::get(ctx, selectedDialect),
+                                 /*sym_visibility=*/nullptr);
 }
 
 static bool emitDialectIRDLDefs(const RecordKeeper &records, raw_ostream &os) {
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
index caa22fa6d9e10..4f519135e5111 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsTest.cpp
@@ -734,7 +734,8 @@ TEST_F(OpenACCUtilsTest, isValidSymbolUseRecipe) {
   auto i32Type = b.getI32Type();
   llvm::StringRef recipeName = "test_recipe";
   OwningOpRef<PrivateRecipeOp> recipeOp =
-      PrivateRecipeOp::create(b, loc, recipeName, i32Type);
+      PrivateRecipeOp::create(b, loc, recipeName,
+                              /*sym_visibility=*/nullptr, i32Type);
 
   // Create a value to privatize
   auto memrefTy = MemRefType::get({10}, b.getI32Type());
@@ -918,7 +919,8 @@ TEST_F(OpenACCUtilsTest, isValidSymbolUseNullDefiningOpPtr) {
   auto i32Type = b.getI32Type();
   llvm::StringRef recipeName = "test_recipe";
   OwningOpRef<PrivateRecipeOp> recipeOp =
-      PrivateRecipeOp::create(b, loc, recipeName, i32Type);
+      PrivateRecipeOp::create(b, loc, recipeName,
+                              /*sym_visibility=*/nullptr, i32Type);
 
   // Create a value to privatize
   auto memrefTy = MemRefType::get({10}, b.getI32Type());
diff --git a/mlir/unittests/Dialect/SPIRV/SerializationTest.cpp b/mlir/unittests/Dialect/SPIRV/SerializationTest.cpp
index 626a0f4002641..0d04e6ff2407f 100644
--- a/mlir/unittests/Dialect/SPIRV/SerializationTest.cpp
+++ b/mlir/unittests/Dialect/SPIRV/SerializationTest.cpp
@@ -412,14 +412,15 @@ TEST_F(SerializationTest, LongSpecConstantCompositeIsSplit) {
   constituents.reserve(kLongCompositeSize);
   for (unsigned i = 0; i < kLongCompositeSize; ++i) {
     std::string name = ("sc" + Twine(i)).str();
-    auto sc =
-        spirv::SpecConstantOp::create(builder, loc, builder.getStringAttr(name),
-                                      builder.getI32IntegerAttr(0));
+    auto sc = spirv::SpecConstantOp::create(
+        builder, loc, builder.getStringAttr(name), builder.getI32IntegerAttr(0),
+        /*sym_visibility=*/nullptr);
     constituents.push_back(SymbolRefAttr::get(sc));
   }
   spirv::SpecConstantCompositeOp::create(builder, loc, TypeAttr::get(arrayType),
                                          builder.getStringAttr("long_scc"),
-                                         builder.getArrayAttr(constituents));
+                                         builder.getArrayAttr(constituents),
+                                         /*sym_visibility=*/nullptr);
 
   ASSERT_TRUE(succeeded(spirv::serialize(module.get(), binary)));
   EXPECT_TRUE(allInstructionsWithinWordLimit(binary));
diff --git a/mlir/unittests/IR/SymbolTableTest.cpp b/mlir/unittests/IR/SymbolTableTest.cpp
index ee3e726b3855b..f18cf85ef0568 100644
--- a/mlir/unittests/IR/SymbolTableTest.cpp
+++ b/mlir/unittests/IR/SymbolTableTest.cpp
@@ -75,6 +75,36 @@ class ReplaceAllSymbolUsesTest : public ::testing::Test {
 
 namespace {
 
+TEST(SymbolOpInterface, NativeSymbolTraits) {
+  DialectRegistry registry;
+  ::test::registerTestDialect(registry);
+  MLIRContext context(registry);
+
+  constexpr static StringLiteral kInput = R"MLIR(
+    "test.symbol"() <{sym_name = "symbol_name"}> : () -> ()
+  )MLIR";
+  OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(kInput, &context);
+  auto symOp = cast<SymbolOpInterface>(module->getBody()->front());
+
+  EXPECT_EQ(symOp.getName(), "symbol_name");
+  EXPECT_TRUE(symOp.isPublic());
+
+  symOp.setName("new_name");
+  EXPECT_EQ(symOp.getName(), "new_name");
+  EXPECT_EQ(symOp->getInherentAttr("sym_name").value_or(Attribute{}),
+            symOp.getNameAttr());
+
+  symOp.setPrivate();
+  EXPECT_TRUE(symOp.isPrivate());
+  symOp.setNested();
+  EXPECT_TRUE(symOp.isNested());
+  symOp.setPublic();
+  EXPECT_TRUE(symOp.isPublic());
+  EXPECT_FALSE(
+      symOp->getInherentAttr(SymbolOpInterface::getDefaultVisibilityAttrName())
+          .value_or(Attribute{}));
+}
+
 TEST_F(ReplaceAllSymbolUsesTest, OperationInModuleOp) {
   // Symbol as `Operation *`, rename within module.
   testReplaceAllSymbolUses([&](const auto &symbolTable, auto module, auto fooOp,
diff --git a/mlir/unittests/Transforms/Canonicalizer.cpp b/mlir/unittests/Transforms/Canonicalizer.cpp
index 4b94e0602b509..74144ea39603d 100644
--- a/mlir/unittests/Transforms/Canonicalizer.cpp
+++ b/mlir/unittests/Transforms/Canonicalizer.cpp
@@ -79,8 +79,9 @@ TEST(CanonicalizerTest, TestDisablePatterns) {
   OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(code, &context);
   ASSERT_TRUE(succeeded(mgr.run(*module)));
 
-  EXPECT_TRUE(module->lookupSymbol("B"));
-  EXPECT_FALSE(module->lookupSymbol("A"));
+  Block &body = module->getBodyRegion().front();
+  ASSERT_EQ(body.getOperations().size(), 1u);
+  EXPECT_EQ(body.front().getResult(0).getType(), Float32Type::get(&context));
 }
 
 } // end anonymous namespace



More information about the Mlir-commits mailing list