[flang-commits] [flang] 1dda134 - [mlir][flang] Convert TBAA metadata to an attribute representation

Markus Böck via flang-commits flang-commits at lists.llvm.org
Wed Jul 19 07:42:58 PDT 2023


Author: Markus Böck
Date: 2023-07-19T16:42:50+02:00
New Revision: 1dda134f85d4fab7ace7b9ec9b57a0319c14de8f

URL: https://github.com/llvm/llvm-project/commit/1dda134f85d4fab7ace7b9ec9b57a0319c14de8f
DIFF: https://github.com/llvm/llvm-project/commit/1dda134f85d4fab7ace7b9ec9b57a0319c14de8f.diff

LOG: [mlir][flang] Convert TBAA metadata to an attribute representation

The current representation of TBAA is the very last in-tree user of the `llvm.metadata` operation.
Using ops to model metadata has a few disadvantages:
* Building a graph has to be done through some weakly typed indirection mechanism such as `SymbolRefAttr`
* Creating the metadata has to be done through a builder within a metadata op.
* It is not multithreading safe as operation insertion into the same block is not thread-safe

This patch therefore converts TBAA metadata into an attribute representation, in a similar manner as it has been done for alias groups and access groups in previous patches.

This additionally has the large benefit of giving us more "correctness by construction" as it makes things like cycles in a TBAA graph, or references to an incorrectly typed metadata node impossible.

Differential Revision: https://reviews.llvm.org/D155444

Added: 
    

Modified: 
    flang/include/flang/Optimizer/CodeGen/TBAABuilder.h
    flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
    flang/lib/Optimizer/CodeGen/TypeConverter.cpp
    flang/test/Fir/tbaa.fir
    mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
    mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
    mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
    mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
    mlir/include/mlir/Target/LLVMIR/ModuleImport.h
    mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
    mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
    mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
    mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
    mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
    mlir/lib/Target/LLVMIR/ModuleImport.cpp
    mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
    mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir
    mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir
    mlir/test/Target/LLVMIR/Import/import-failure.ll
    mlir/test/Target/LLVMIR/Import/metadata-tbaa.ll
    mlir/test/Target/LLVMIR/tbaa.mlir
    mlir/test/mlir-tblgen/llvm-intrinsics.td
    mlir/tools/mlir-tblgen/LLVMIRIntrinsicGen.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/CodeGen/TBAABuilder.h b/flang/include/flang/Optimizer/CodeGen/TBAABuilder.h
index 48c40a4403daee..b94782bb9ea984 100644
--- a/flang/include/flang/Optimizer/CodeGen/TBAABuilder.h
+++ b/flang/include/flang/Optimizer/CodeGen/TBAABuilder.h
@@ -164,7 +164,7 @@ namespace fir {
 //   < `<any data access>`, `<any data access>`, 0 >
 class TBAABuilder {
 public:
-  TBAABuilder(mlir::ModuleOp module, bool applyTBAA);
+  TBAABuilder(mlir::MLIRContext *context, bool applyTBAA);
   TBAABuilder(TBAABuilder const &) = delete;
   TBAABuilder &operator=(TBAABuilder const &) = delete;
 
@@ -175,95 +175,74 @@ class TBAABuilder {
                      mlir::LLVM::GEPOp gep);
 
 private:
-  // Return unique string name based on `basename`.
-  std::string getNewTBAANodeName(llvm::StringRef basename);
-
-  // Find or create TBAATagOp operation (TBAA access tag) with the specified
-  // components and return the symbol it defines.
-  mlir::SymbolRefAttr getAccessTag(mlir::SymbolRefAttr baseTypeDesc,
-                                   mlir::SymbolRefAttr accessTypeDesc,
-                                   int64_t offset);
-  // Returns symbol of TBAATagOp representing access tag:
+  // Find or create TBAATagAttr attribute (TBAA access tag) with the specified
+  // components and return it.
+  mlir::LLVM::TBAATagAttr
+  getAccessTag(mlir::LLVM::TBAATypeDescriptorAttr baseTypeDesc,
+               mlir::LLVM::TBAATypeDescriptorAttr accessTypeDesc,
+               int64_t offset);
+
+  // Returns TBAATagAttr representing access tag:
   //   < <descriptor member>, <descriptor member>, 0 >
-  mlir::SymbolRefAttr getAnyBoxAccessTag();
-  // Returns symbol of TBAATagOp representing access tag:
+  mlir::LLVM::TBAATagAttr getAnyBoxAccessTag();
+  // Returns TBAATagAttr representing access tag:
   //   < <any data access>, <any data access>, 0 >
-  mlir::SymbolRefAttr getAnyDataAccessTag();
-
-  // Returns symbol of TBAATagOp representing access tag
-  // described by the base and access FIR types and the LLVM::GepOp
-  // representing the access in terms of the FIR types converted
-  // to LLVM types. The base type must be derivative of fir::BaseBoxType.
-  mlir::SymbolRefAttr getBoxAccessTag(mlir::Type baseFIRType,
-                                      mlir::Type accessFIRType,
-                                      mlir::LLVM::GEPOp gep);
-
-  // Returns symbol of TBAATagOp representing access tag
-  // described by the base and access FIR types and the LLVM::GepOp
-  // representing the access in terms of the FIR types converted
-  // to LLVM types. The FIR types must describe the "data" access,
-  // i.e. not an access of any box/descriptor member.
-  mlir::SymbolRefAttr getDataAccessTag(mlir::Type baseFIRType,
-                                       mlir::Type accessFIRType,
-                                       mlir::LLVM::GEPOp gep);
+  mlir::LLVM::TBAATagAttr getAnyDataAccessTag();
+
+  // Returns TBAATagAttr representing access tag described by the base and
+  // access FIR types and the LLVM::GepOp representing the access in terms of
+  // the FIR types converted to LLVM types. The base type must be derivative of
+  // fir::BaseBoxType.
+  mlir::LLVM::TBAATagAttr getBoxAccessTag(mlir::Type baseFIRType,
+                                          mlir::Type accessFIRType,
+                                          mlir::LLVM::GEPOp gep);
+
+  // Returns TBAATagAttr representing access tag described by the base and
+  // access FIR types and the LLVM::GepOp representing the access in terms of
+  // the FIR types converted to LLVM types. The FIR types must describe the
+  // "data" access, i.e. not an access of any box/descriptor member.
+  mlir::LLVM::TBAATagAttr getDataAccessTag(mlir::Type baseFIRType,
+                                           mlir::Type accessFIRType,
+                                           mlir::LLVM::GEPOp gep);
 
   // Set to true, if TBAA builder is active, otherwise, all public
   // methods are no-ops.
   bool enableTBAA;
 
-  // LLVM::MetadataOp holding the TBAA operations.
-  mlir::LLVM::MetadataOp tbaaMetaOp;
-  // Symbol name of tbaaMetaOp.
-  static constexpr llvm::StringRef tbaaMetaOpName = "__flang_tbaa";
-
-  // Base names for TBAA operations:
-  //   TBAARootMetadataOp:
-  static constexpr llvm::StringRef kRootSymBasename = "root";
-  //   TBAATypeDescriptorOp:
-  static constexpr llvm::StringRef kTypeDescSymBasename = "type_desc";
-  //   TBAATagOp:
-  static constexpr llvm::StringRef kTagSymBasename = "tag";
-
-  // Symbol defined by the LLVM::TBAARootMetadataOp identifying
-  // Flang's TBAA root.
-  mlir::SymbolRefAttr flangTBAARoot;
+  // LLVM::TBAARootAttr identifying Flang's TBAA root.
+  mlir::LLVM::TBAARootAttr flangTBAARoot;
   // Identity string for Flang's TBAA root.
   static constexpr llvm::StringRef flangTBAARootId = "Flang Type TBAA Root";
 
-  // Symbol defined by LLVM::TBAATypeDescriptorOp identifying
-  // "any access".
-  mlir::SymbolRefAttr anyAccessTypeDesc;
+  // LLVM::TBAATypeDescriptorAttr identifying "any access".
+  mlir::LLVM::TBAATypeDescriptorAttr anyAccessTypeDesc;
   // Identity string for "any access" type descriptor.
   static constexpr llvm::StringRef anyAccessTypeDescId = "any access";
 
-  // Symbol defined by LLVM::TBAATypeDescriptorOp identifying
-  // "any data access" (i.e. non-box memory access).
-  mlir::SymbolRefAttr anyDataAccessTypeDesc;
+  // LLVM::TBAATypeDescriptorAttr identifying "any data access" (i.e. non-box
+  // memory access).
+  mlir::LLVM::TBAATypeDescriptorAttr anyDataAccessTypeDesc;
   // Identity string for "any data access" type descriptor.
   static constexpr llvm::StringRef anyDataAccessTypeDescId = "any data access";
 
-  // Symbol defined by LLVM::TBAATypeDescriptorOp identifying
-  // "descriptor member" access, i.e. any access within the bounds
-  // of a box/descriptor.
-  mlir::SymbolRefAttr boxMemberTypeDesc;
+  // LLVM::TBAATypeDescriptorAttr identifying "descriptor member" access, i.e.
+  // any access within the bounds of a box/descriptor.
+  mlir::LLVM::TBAATypeDescriptorAttr boxMemberTypeDesc;
   // Identity string for "descriptor member" type descriptor.
   static constexpr llvm::StringRef boxMemberTypeDescId = "descriptor member";
 
-  // Counter for unique naming of TBAA operations' symbols.
-  unsigned tbaaNodeCounter = 0;
-
   // Number of attached TBAA tags (used for debugging).
   unsigned tagAttachmentCounter = 0;
 
-  // Mapping from a FIR type to the symbol defined by the corresponding
-  // TBAATypeDescriptorOp. It must be populated during the type conversion.
-  // Currently unused.
-  llvm::DenseMap<mlir::Type, mlir::SymbolRefAttr> typeDescMap;
+  // Mapping from a FIR type to the corresponding TBAATypeDescriptorAttr. It
+  // must be populated during the type conversion. Currently unused.
+  llvm::DenseMap<mlir::Type, mlir::LLVM::TBAATypeDescriptorAttr> typeDescMap;
 
   // Each TBAA tag is a tuple of <baseTypeSym, accessTypeSym, offset>.
-  // This map holds TBAATagOp symbol for each unique tuple.
-  llvm::DenseMap<std::tuple<mlir::SymbolRefAttr, mlir::SymbolRefAttr, int64_t>,
-                 mlir::SymbolRefAttr>
+  // This map holds a TBAATagAttr for each unique tuple.
+  llvm::DenseMap<
+      std::tuple<mlir::LLVM::TBAANodeAttr, mlir::LLVM::TBAANodeAttr, int64_t>,
+      mlir::LLVM::TBAATagAttr>
       tagsMap;
 };
 

diff  --git a/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp b/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
index ffc72c7d2f02e5..04259c0e4c7213 100644
--- a/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
+++ b/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
@@ -37,107 +37,58 @@ static llvm::cl::opt<unsigned>
                        llvm::cl::init(kTagAttachmentUnlimited));
 
 namespace fir {
-std::string TBAABuilder::getNewTBAANodeName(llvm::StringRef basename) {
-  return (llvm::Twine(basename) + llvm::Twine('_') +
-          llvm::Twine(tbaaNodeCounter++))
-      .str();
-}
 
-TBAABuilder::TBAABuilder(mlir::ModuleOp module, bool applyTBAA)
+TBAABuilder::TBAABuilder(MLIRContext *context, bool applyTBAA)
     : enableTBAA(applyTBAA && !disableTBAA) {
   if (!enableTBAA)
     return;
 
-  // In the usual Flang compilation flow, FIRToLLVMPass is run once,
-  // and the MetadataOp holding TBAA operations is created at the beginning
-  // of the pass. With tools like tco it is possible to invoke
-  // FIRToLLVMPass on already converted MLIR, so the MetadataOp
-  // already exists and creating a new one with the same name would
-  // be incorrect. If the TBAA MetadataOp is already present,
-  // we just disable all TBAABuilder actions (e.g. attachTBAATag()
-  // is a no-op).
-  if (llvm::any_of(
-          module.getBodyRegion().getOps<LLVM::MetadataOp>(),
-          [&](auto metaOp) { return metaOp.getSymName() == tbaaMetaOpName; })) {
-    enableTBAA = false;
-    return;
-  }
-
-  LLVM_DEBUG(llvm::dbgs() << "Creating TBAA MetadataOp for module '"
-                          << module.getName().value_or("<unknown>") << "'\n");
-
-  // Create TBAA MetadataOp with the root and basic type descriptors.
-  Location loc = module.getLoc();
-  MLIRContext *context = module.getContext();
-  OpBuilder builder(module.getBody(), module.getBody()->end());
-  tbaaMetaOp = builder.create<MetadataOp>(loc, tbaaMetaOpName);
-  builder.setInsertionPointToStart(&tbaaMetaOp.getBody().front());
-
   // Root node.
-  auto rootOp = builder.create<TBAARootMetadataOp>(
-      loc, getNewTBAANodeName(kRootSymBasename), flangTBAARootId);
-  flangTBAARoot = FlatSymbolRefAttr::get(rootOp);
+  flangTBAARoot =
+      TBAARootAttr::get(context, StringAttr::get(context, flangTBAARootId));
 
   // Any access node.
-  auto anyAccessOp = builder.create<TBAATypeDescriptorOp>(
-      loc, getNewTBAANodeName(kTypeDescSymBasename),
-      StringAttr::get(context, anyAccessTypeDescId),
-      ArrayAttr::get(context, flangTBAARoot), ArrayRef<int64_t>{0});
-  anyAccessTypeDesc = FlatSymbolRefAttr::get(anyAccessOp);
+  anyAccessTypeDesc = TBAATypeDescriptorAttr::get(
+      context, anyAccessTypeDescId, TBAAMemberAttr::get(flangTBAARoot, 0));
 
   // Any data access node.
-  auto anyDataAccessOp = builder.create<TBAATypeDescriptorOp>(
-      loc, getNewTBAANodeName(kTypeDescSymBasename),
-      StringAttr::get(context, anyDataAccessTypeDescId),
-      ArrayAttr::get(context, anyAccessTypeDesc), ArrayRef<int64_t>{0});
-  anyDataAccessTypeDesc = FlatSymbolRefAttr::get(anyDataAccessOp);
+  anyDataAccessTypeDesc =
+      TBAATypeDescriptorAttr::get(context, anyDataAccessTypeDescId,
+                                  TBAAMemberAttr::get(anyAccessTypeDesc, 0));
 
   // Box member access node.
-  auto boxMemberOp = builder.create<TBAATypeDescriptorOp>(
-      loc, getNewTBAANodeName(kTypeDescSymBasename),
-      StringAttr::get(context, boxMemberTypeDescId),
-      ArrayAttr::get(context, anyAccessTypeDesc), ArrayRef<int64_t>{0});
-  boxMemberTypeDesc = FlatSymbolRefAttr::get(boxMemberOp);
+  boxMemberTypeDesc = TBAATypeDescriptorAttr::get(
+      context, boxMemberTypeDescId, TBAAMemberAttr::get(anyAccessTypeDesc, 0));
 }
 
-SymbolRefAttr TBAABuilder::getAccessTag(SymbolRefAttr baseTypeDesc,
-                                        SymbolRefAttr accessTypeDesc,
-                                        int64_t offset) {
-  SymbolRefAttr &tag = tagsMap[{baseTypeDesc, accessTypeDesc, offset}];
+TBAATagAttr TBAABuilder::getAccessTag(TBAATypeDescriptorAttr baseTypeDesc,
+                                      TBAATypeDescriptorAttr accessTypeDesc,
+                                      int64_t offset) {
+  TBAATagAttr &tag = tagsMap[{baseTypeDesc, accessTypeDesc, offset}];
   if (tag)
     return tag;
 
   // Initialize new tag.
-  Location loc = tbaaMetaOp.getLoc();
-  OpBuilder builder(&tbaaMetaOp.getBody().back(),
-                    tbaaMetaOp.getBody().back().end());
-  auto tagOp = builder.create<TBAATagOp>(
-      loc, getNewTBAANodeName(kTagSymBasename), baseTypeDesc.getLeafReference(),
-      accessTypeDesc.getLeafReference(), offset);
-  // TBAATagOp symbols must be referenced by their fully qualified
-  // names, so create a path to TBAATagOp symbol.
-  StringAttr metaOpName = SymbolTable::getSymbolName(tbaaMetaOp);
-  tag = SymbolRefAttr::get(builder.getContext(), metaOpName,
-                           FlatSymbolRefAttr::get(tagOp));
+  tag = TBAATagAttr::get(baseTypeDesc, accessTypeDesc, offset);
   return tag;
 }
 
-SymbolRefAttr TBAABuilder::getAnyBoxAccessTag() {
+TBAATagAttr TBAABuilder::getAnyBoxAccessTag() {
   return getAccessTag(boxMemberTypeDesc, boxMemberTypeDesc, /*offset=*/0);
 }
 
-SymbolRefAttr TBAABuilder::getBoxAccessTag(Type baseFIRType, Type accessFIRType,
-                                           GEPOp gep) {
+TBAATagAttr TBAABuilder::getBoxAccessTag(Type baseFIRType, Type accessFIRType,
+                                         GEPOp gep) {
   return getAnyBoxAccessTag();
 }
 
-SymbolRefAttr TBAABuilder::getAnyDataAccessTag() {
+TBAATagAttr TBAABuilder::getAnyDataAccessTag() {
   return getAccessTag(anyDataAccessTypeDesc, anyDataAccessTypeDesc,
                       /*offset=*/0);
 }
 
-SymbolRefAttr TBAABuilder::getDataAccessTag(Type baseFIRType,
-                                            Type accessFIRType, GEPOp gep) {
+TBAATagAttr TBAABuilder::getDataAccessTag(Type baseFIRType, Type accessFIRType,
+                                          GEPOp gep) {
   return getAnyDataAccessTag();
 }
 
@@ -154,7 +105,7 @@ void TBAABuilder::attachTBAATag(AliasAnalysisOpInterface op, Type baseFIRType,
   LLVM_DEBUG(llvm::dbgs() << "Attaching TBAA tag #" << tagAttachmentCounter
                           << "\n");
 
-  SymbolRefAttr tbaaTagSym;
+  TBAATagAttr tbaaTagSym;
   if (baseFIRType.isa<fir::BaseBoxType>())
     tbaaTagSym = getBoxAccessTag(baseFIRType, accessFIRType, gep);
   else

diff  --git a/flang/lib/Optimizer/CodeGen/TypeConverter.cpp b/flang/lib/Optimizer/CodeGen/TypeConverter.cpp
index 0cac01c4bba507..8de2dbbca3f806 100644
--- a/flang/lib/Optimizer/CodeGen/TypeConverter.cpp
+++ b/flang/lib/Optimizer/CodeGen/TypeConverter.cpp
@@ -37,7 +37,7 @@ LLVMTypeConverter::LLVMTypeConverter(mlir::ModuleOp module, bool applyTBAA)
       specifics(CodeGenSpecifics::get(module.getContext(),
                                       getTargetTriple(module),
                                       getKindMapping(module))),
-      tbaaBuilder(module, applyTBAA) {
+      tbaaBuilder(module->getContext(), applyTBAA) {
   LLVM_DEBUG(llvm::dbgs() << "FIR type converter\n");
 
   // Each conversion should return a value of type mlir::Type.

diff  --git a/flang/test/Fir/tbaa.fir b/flang/test/Fir/tbaa.fir
index 3528f61b5bfccc..66bd41bad18e77 100644
--- a/flang/test/Fir/tbaa.fir
+++ b/flang/test/Fir/tbaa.fir
@@ -20,6 +20,13 @@ module {
   }
 }
 
+// CHECK-DAG:     #[[ROOT:.*]] = #llvm.tbaa_root<id = "Flang Type TBAA Root">
+// CHECK-DAG:     #[[ANYACC:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[ROOT]], 0>}>
+// CHECK-DAG:     #[[ANYDACC:.*]] = #llvm.tbaa_type_desc<id = "any data access", members = {<#[[ANYACC]], 0>}>
+// CHECK-DAG:     #[[BOXMEM:.*]] = #llvm.tbaa_type_desc<id = "descriptor member", members = {<#[[ANYACC]], 0>}>
+// CHECK-DAG:     #[[$BOXT:.*]] = #llvm.tbaa_tag<base_type = #[[BOXMEM]], access_type = #[[BOXMEM]], offset = 0>
+// CHECK-DAG:     #[[$DATAT:.*]] = #llvm.tbaa_tag<base_type = #[[ANYDACC]], access_type = #[[ANYDACC]], offset = 0>
+
 // CHECK-LABEL:   llvm.func @tbaa(
 // CHECK-SAME:                    %[[VAL_0:.*]]: !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>> {fir.bindc_name = "a"}) {
 // CHECK:           %[[VAL_1:.*]] = llvm.mlir.constant(1 : i32) : i32
@@ -28,10 +35,10 @@ module {
 // CHECK:           %[[VAL_4:.*]] = llvm.mlir.constant(0 : i64) : i64
 // CHECK:           %[[VAL_5:.*]] = llvm.mlir.constant(10 : i32) : i32
 // CHECK:           %[[VAL_6:.*]] = llvm.getelementptr %[[VAL_0]][0, 0] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<ptr<struct<()>>>
-// CHECK:           %[[VAL_7:.*]] = llvm.load %[[VAL_6]] {tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr<ptr<struct<()>>>
+// CHECK:           %[[VAL_7:.*]] = llvm.load %[[VAL_6]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<ptr<struct<()>>>
 // CHECK:           %[[VAL_8:.*]] = llvm.mlir.constant(0 : i64) : i64
 // CHECK:           %[[VAL_9:.*]] = llvm.getelementptr %[[VAL_0]][0, 7, 0, 2] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_10:.*]] = llvm.load %[[VAL_9]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_10:.*]] = llvm.load %[[VAL_9]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_11:.*]] = llvm.mul %[[VAL_4]], %[[VAL_10]]  : i64
 // CHECK:           %[[VAL_12:.*]] = llvm.add %[[VAL_11]], %[[VAL_8]]  : i64
 // CHECK:           %[[VAL_13:.*]] = llvm.bitcast %[[VAL_7]] : !llvm.ptr<struct<()>> to !llvm.ptr<i8>
@@ -40,11 +47,11 @@ module {
 // CHECK:           %[[VAL_16:.*]] = llvm.mlir.constant(0 : i64) : i64
 // CHECK:           %[[VAL_17:.*]] = llvm.mlir.constant(-1 : i32) : i32
 // CHECK:           %[[VAL_18:.*]] = llvm.getelementptr %[[VAL_0]][0, 8] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<ptr<i8>>
-// CHECK:           %[[VAL_19:.*]] = llvm.load %[[VAL_18]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<ptr<i8>>
+// CHECK:           %[[VAL_19:.*]] = llvm.load %[[VAL_18]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<ptr<i8>>
 // CHECK:           %[[VAL_20:.*]] = llvm.getelementptr %[[VAL_0]][0, 1] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_21:.*]] = llvm.load %[[VAL_20]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_21:.*]] = llvm.load %[[VAL_20]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_22:.*]] = llvm.getelementptr %[[VAL_0]][0, 4] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i32>
-// CHECK:           %[[VAL_23:.*]] = llvm.load %[[VAL_22]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i32>
+// CHECK:           %[[VAL_23:.*]] = llvm.load %[[VAL_22]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i32>
 // CHECK:           %[[VAL_24:.*]] = llvm.mlir.undef : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
 // CHECK:           %[[VAL_25:.*]] = llvm.insertvalue %[[VAL_21]], %[[VAL_24]][1] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
 // CHECK:           %[[VAL_26:.*]] = llvm.mlir.constant(20180515 : i32) : i32
@@ -64,29 +71,20 @@ module {
 // CHECK:           %[[VAL_40:.*]] = llvm.insertvalue %[[VAL_39]], %[[VAL_38]][7] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
 // CHECK:           %[[VAL_41:.*]] = llvm.bitcast %[[VAL_15]] : !llvm.ptr<struct<()>> to !llvm.ptr<struct<()>>
 // CHECK:           %[[VAL_42:.*]] = llvm.insertvalue %[[VAL_41]], %[[VAL_40]][0] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
-// CHECK:           llvm.store %[[VAL_42]], %[[VAL_2]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>>
+// CHECK:           llvm.store %[[VAL_42]], %[[VAL_2]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>>
 // CHECK:           %[[VAL_43:.*]] = llvm.getelementptr %[[VAL_2]][0, 4] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i8>
-// CHECK:           %[[VAL_44:.*]] = llvm.load %[[VAL_43]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i8>
+// CHECK:           %[[VAL_44:.*]] = llvm.load %[[VAL_43]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i8>
 // CHECK:           %[[VAL_45:.*]] = llvm.icmp "eq" %[[VAL_44]], %[[VAL_3]] : i8
 // CHECK:           llvm.cond_br %[[VAL_45]], ^bb1, ^bb2
 // CHECK:         ^bb1:
 // CHECK:           %[[VAL_46:.*]] = llvm.getelementptr %[[VAL_2]][0, 0] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<ptr<i32>>
-// CHECK:           %[[VAL_47:.*]] = llvm.load %[[VAL_46]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<ptr<i32>>
-// CHECK:           llvm.store %[[VAL_5]], %[[VAL_47]] {tbaa = [@__flang_tbaa::@[[DATAT:tag_[0-9]*]]]} : !llvm.ptr<i32>
+// CHECK:           %[[VAL_47:.*]] = llvm.load %[[VAL_46]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<ptr<i32>>
+// CHECK:           llvm.store %[[VAL_5]], %[[VAL_47]] {tbaa = [#[[$DATAT]]]} : !llvm.ptr<i32>
 // CHECK:           llvm.br ^bb2
 // CHECK:         ^bb2:
 // CHECK:           llvm.return
 // CHECK:         }
 
-// CHECK-LABEL:   llvm.metadata @__flang_tbaa {
-// CHECK:           llvm.tbaa_root @[[ROOT:root_[0-9]*]] {id = "Flang Type TBAA Root"}
-// CHECK:           llvm.tbaa_type_desc @[[ANYACC:type_desc_[0-9]*]] {id = "any access", members = {<@[[ROOT]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[ANYDACC:type_desc_[0-9]*]] {id = "any data access", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[BOXMEM:type_desc_[0-9]*]] {id = "descriptor member", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_tag @[[BOXT]] {access_type = @[[BOXMEM]], base_type = @[[BOXMEM]], offset = 0 : i64}
-// CHECK:           llvm.tbaa_tag @[[DATAT]] {access_type = @[[ANYDACC]], base_type = @[[ANYDACC]], offset = 0 : i64}
-// CHECK:         }
-
 // -----
 
 module {
@@ -121,6 +119,11 @@ module {
   }
 }
 
+// CHECK-DAG:     #[[ROOT:.*]] = #llvm.tbaa_root<id = "Flang Type TBAA Root">
+// CHECK-DAG:     #[[ANYACC:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[ROOT]], 0>}>
+// CHECK-DAG:     #[[BOXMEM:.*]] = #llvm.tbaa_type_desc<id = "descriptor member", members = {<#[[ANYACC]], 0>}>
+// CHECK-DAG:     #[[$BOXT:.*]] = #llvm.tbaa_tag<base_type = #[[BOXMEM]], access_type = #[[BOXMEM]], offset = 0>
+
 // CHECK-LABEL:   llvm.func @tbaa() {
 // CHECK:           %[[VAL_0:.*]] = llvm.mlir.constant(1 : i32) : i32
 // CHECK:           %[[VAL_1:.*]] = llvm.alloca %[[VAL_0]] x !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)> {alignment = 8 : i64} : (i32) -> !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>
@@ -133,24 +136,24 @@ module {
 // CHECK:           %[[VAL_8:.*]] = llvm.mlir.addressof @_QQcl.2E2F64756D6D792E66393000 : !llvm.ptr<array<12 x i8>>
 // CHECK:           %[[VAL_9:.*]] = llvm.bitcast %[[VAL_8]] : !llvm.ptr<array<12 x i8>> to !llvm.ptr<i8>
 // CHECK:           %[[VAL_10:.*]] = llvm.call @_FortranAioBeginExternalListOutput(%[[VAL_6]], %[[VAL_9]], %[[VAL_5]]) {fastmathFlags = #llvm.fastmath<contract>} : (i32, !llvm.ptr<i8>, i32) -> !llvm.ptr<i8>
-// CHECK:           %[[VAL_11:.*]] = llvm.load %[[VAL_7]] {tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>
-// CHECK:           llvm.store %[[VAL_11]], %[[VAL_3]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>
+// CHECK:           %[[VAL_11:.*]] = llvm.load %[[VAL_7]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>
+// CHECK:           llvm.store %[[VAL_11]], %[[VAL_3]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>
 // CHECK:           %[[VAL_12:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, %[[VAL_4]], 0] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>, i64) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_13:.*]] = llvm.load %[[VAL_12]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_13:.*]] = llvm.load %[[VAL_12]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_14:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, %[[VAL_4]], 1] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>, i64) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_15:.*]] = llvm.load %[[VAL_14]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_15:.*]] = llvm.load %[[VAL_14]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_16:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, %[[VAL_4]], 2] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>, i64) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_17:.*]] = llvm.load %[[VAL_16]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_17:.*]] = llvm.load %[[VAL_16]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_18:.*]] = llvm.getelementptr %[[VAL_3]][0, 8] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<ptr<i8>>
-// CHECK:           %[[VAL_19:.*]] = llvm.load %[[VAL_18]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<ptr<i8>>
+// CHECK:           %[[VAL_19:.*]] = llvm.load %[[VAL_18]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<ptr<i8>>
 // CHECK:           %[[VAL_20:.*]] = llvm.mlir.constant(0 : i64) : i64
 // CHECK:           %[[VAL_21:.*]] = llvm.mlir.constant(-1 : i32) : i32
 // CHECK:           %[[VAL_22:.*]] = llvm.getelementptr %[[VAL_3]][0, 1] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_23:.*]] = llvm.load %[[VAL_22]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_23:.*]] = llvm.load %[[VAL_22]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_24:.*]] = llvm.getelementptr %[[VAL_3]][0, 4] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i32>
-// CHECK:           %[[VAL_25:.*]] = llvm.load %[[VAL_24]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i32>
+// CHECK:           %[[VAL_25:.*]] = llvm.load %[[VAL_24]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i32>
 // CHECK:           %[[VAL_26:.*]] = llvm.getelementptr %[[VAL_3]][0, 8] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<ptr<i8>>
-// CHECK:           %[[VAL_27:.*]] = llvm.load %[[VAL_26]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<ptr<i8>>
+// CHECK:           %[[VAL_27:.*]] = llvm.load %[[VAL_26]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<ptr<i8>>
 // CHECK:           %[[VAL_28:.*]] = llvm.mlir.undef : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>
 // CHECK:           %[[VAL_29:.*]] = llvm.insertvalue %[[VAL_23]], %[[VAL_28]][1] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>
 // CHECK:           %[[VAL_30:.*]] = llvm.mlir.constant(20180515 : i32) : i32
@@ -169,13 +172,13 @@ module {
 // CHECK:           %[[VAL_43:.*]] = llvm.bitcast %[[VAL_27]] : !llvm.ptr<i8> to !llvm.ptr<i8>
 // CHECK:           %[[VAL_44:.*]] = llvm.insertvalue %[[VAL_43]], %[[VAL_42]][8] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>
 // CHECK:           %[[VAL_45:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, 0, 0] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_46:.*]] = llvm.load %[[VAL_45]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_46:.*]] = llvm.load %[[VAL_45]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_47:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, 0, 1] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_48:.*]] = llvm.load %[[VAL_47]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_48:.*]] = llvm.load %[[VAL_47]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_49:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, 0, 2] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_50:.*]] = llvm.load %[[VAL_49]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_50:.*]] = llvm.load %[[VAL_49]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_51:.*]] = llvm.getelementptr %[[VAL_3]][0, 0] : (!llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>) -> !llvm.ptr<ptr<struct<()>>>
-// CHECK:           %[[VAL_52:.*]] = llvm.load %[[VAL_51]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<ptr<struct<()>>>
+// CHECK:           %[[VAL_52:.*]] = llvm.load %[[VAL_51]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<ptr<struct<()>>>
 // CHECK:           %[[VAL_53:.*]] = llvm.mlir.constant(0 : i64) : i64
 // CHECK:           %[[VAL_54:.*]] = llvm.mlir.constant(1 : i64) : i64
 // CHECK:           %[[VAL_55:.*]] = llvm.icmp "eq" %[[VAL_48]], %[[VAL_53]] : i64
@@ -185,7 +188,7 @@ module {
 // CHECK:           %[[VAL_59:.*]] = llvm.insertvalue %[[VAL_50]], %[[VAL_58]][7, 0, 2] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>
 // CHECK:           %[[VAL_60:.*]] = llvm.bitcast %[[VAL_52]] : !llvm.ptr<struct<()>> to !llvm.ptr<struct<()>>
 // CHECK:           %[[VAL_61:.*]] = llvm.insertvalue %[[VAL_60]], %[[VAL_59]][0] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>
-// CHECK:           llvm.store %[[VAL_61]], %[[VAL_1]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>
+// CHECK:           llvm.store %[[VAL_61]], %[[VAL_1]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>>
 // CHECK:           %[[VAL_62:.*]] = llvm.bitcast %[[VAL_1]] : !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>> to !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>>
 // CHECK:           %[[VAL_63:.*]] = llvm.call @_FortranAioOutputDescriptor(%[[VAL_10]], %[[VAL_62]]) {fastmathFlags = #llvm.fastmath<contract>} : (!llvm.ptr<i8>, !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>>) -> i1
 // CHECK:           %[[VAL_64:.*]] = llvm.call @_FortranAioEndIoStatement(%[[VAL_10]]) {fastmathFlags = #llvm.fastmath<contract>} : (!llvm.ptr<i8>) -> i32
@@ -235,14 +238,6 @@ module {
 // CHECK:           llvm.return %[[VAL_30]] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr<i8>, array<1 x i64>)>
 // CHECK:         }
 
-// CHECK-LABEL:   llvm.metadata @__flang_tbaa {
-// CHECK:           llvm.tbaa_root @[[ROOT:root_[0-9]*]] {id = "Flang Type TBAA Root"}
-// CHECK:           llvm.tbaa_type_desc @[[ANYACC:type_desc_[0-9]*]] {id = "any access", members = {<@[[ROOT]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[ANYDACC:type_desc_[0-9]*]] {id = "any data access", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[BOXMEM:type_desc_[0-9]*]] {id = "descriptor member", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_tag @[[BOXT]] {access_type = @[[BOXMEM]], base_type = @[[BOXMEM]], offset = 0 : i64}
-// CHECK:         }
-
 // -----
 
 func.func @tbaa(%arg0: !fir.box<!fir.array<*:f64>>) -> i32 {
@@ -250,21 +245,18 @@ func.func @tbaa(%arg0: !fir.box<!fir.array<*:f64>>) -> i32 {
   return %0 : i32
 }
 
+// CHECK-DAG:     #[[ROOT:.*]] = #llvm.tbaa_root<id = "Flang Type TBAA Root">
+// CHECK-DAG:     #[[ANYACC:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[ROOT]], 0>}>
+// CHECK-DAG:     #[[BOXMEM:.*]] = #llvm.tbaa_type_desc<id = "descriptor member", members = {<#[[ANYACC]], 0>}>
+// CHECK-DAG:     #[[$BOXT:.*]] = #llvm.tbaa_tag<base_type = #[[BOXMEM]], access_type = #[[BOXMEM]], offset = 0>
+
 // CHECK-LABEL:   llvm.func @tbaa(
 // CHECK-SAME:                    %[[VAL_0:.*]]: !llvm.ptr<struct<(ptr<f64>, i64, i32, i8, i8, i8, i8)>>) -> i32 {
 // CHECK:           %[[VAL_1:.*]] = llvm.getelementptr %[[VAL_0]][0, 3] : (!llvm.ptr<struct<(ptr<f64>, i64, i32, i8, i8, i8, i8)>>) -> !llvm.ptr<i32>
-// CHECK:           %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr<i32>
+// CHECK:           %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i32>
 // CHECK:           llvm.return %[[VAL_2]] : i32
 // CHECK:         }
 
-// CHECK-LABEL:   llvm.metadata @__flang_tbaa {
-// CHECK:           llvm.tbaa_root @[[ROOT:root_[0-9]*]] {id = "Flang Type TBAA Root"}
-// CHECK:           llvm.tbaa_type_desc @[[ANYACC:type_desc_[0-9]*]] {id = "any access", members = {<@[[ROOT]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[ANYDACC:type_desc_[0-9]*]] {id = "any data access", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[BOXMEM:type_desc_[0-9]*]] {id = "descriptor member", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_tag @[[BOXT]] {access_type = @[[BOXMEM]], base_type = @[[BOXMEM]], offset = 0 : i64}
-// CHECK:         }
-
 // -----
 
 func.func @tbaa(%arg0: !fir.box<!fir.array<*:f64>>) -> i1 {
@@ -272,23 +264,20 @@ func.func @tbaa(%arg0: !fir.box<!fir.array<*:f64>>) -> i1 {
   return %0 : i1
 }
 
+// CHECK-DAG:     #[[ROOT:.*]] = #llvm.tbaa_root<id = "Flang Type TBAA Root">
+// CHECK-DAG:     #[[ANYACC:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[ROOT]], 0>}>
+// CHECK-DAG:     #[[BOXMEM:.*]] = #llvm.tbaa_type_desc<id = "descriptor member", members = {<#[[ANYACC]], 0>}>
+// CHECK-DAG:     #[[$BOXT:.*]] = #llvm.tbaa_tag<base_type = #[[BOXMEM]], access_type = #[[BOXMEM]], offset = 0>
+
 // CHECK-LABEL:   llvm.func @tbaa(
 // CHECK-SAME:                    %[[VAL_0:.*]]: !llvm.ptr<struct<(ptr<f64>, i64, i32, i8, i8, i8, i8)>>) -> i1 {
 // CHECK:           %[[VAL_1:.*]] = llvm.getelementptr %[[VAL_0]][0, 3] : (!llvm.ptr<struct<(ptr<f64>, i64, i32, i8, i8, i8, i8)>>) -> !llvm.ptr<i32>
-// CHECK:           %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr<i32>
+// CHECK:           %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i32>
 // CHECK:           %[[VAL_3:.*]] = llvm.mlir.constant(0 : i32) : i32
 // CHECK:           %[[VAL_4:.*]] = llvm.icmp "ne" %[[VAL_2]], %[[VAL_3]] : i32
 // CHECK:           llvm.return %[[VAL_4]] : i1
 // CHECK:         }
 
-// CHECK-LABEL:   llvm.metadata @__flang_tbaa {
-// CHECK:           llvm.tbaa_root @[[ROOT:root_[0-9]*]] {id = "Flang Type TBAA Root"}
-// CHECK:           llvm.tbaa_type_desc @[[ANYACC:type_desc_[0-9]*]] {id = "any access", members = {<@[[ROOT]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[ANYDACC:type_desc_[0-9]*]] {id = "any data access", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[BOXMEM:type_desc_[0-9]*]] {id = "descriptor member", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_tag @[[BOXT]] {access_type = @[[BOXMEM]], base_type = @[[BOXMEM]], offset = 0 : i64}
-// CHECK:         }
-
 // -----
 
 func.func @tbaa(%arg0: !fir.box<f32>) -> i32 {
@@ -296,21 +285,18 @@ func.func @tbaa(%arg0: !fir.box<f32>) -> i32 {
   return %0 : i32
 }
 
+// CHECK-DAG:     #[[ROOT:.*]] = #llvm.tbaa_root<id = "Flang Type TBAA Root">
+// CHECK-DAG:     #[[ANYACC:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[ROOT]], 0>}>
+// CHECK-DAG:     #[[BOXMEM:.*]] = #llvm.tbaa_type_desc<id = "descriptor member", members = {<#[[ANYACC]], 0>}>
+// CHECK-DAG:     #[[$BOXT:.*]] = #llvm.tbaa_tag<base_type = #[[BOXMEM]], access_type = #[[BOXMEM]], offset = 0>
+
 // CHECK-LABEL:   llvm.func @tbaa(
 // CHECK-SAME:                               %[[VAL_0:.*]]: !llvm.ptr<struct<(ptr<f32>, i64, i32, i8, i8, i8, i8)>>) -> i32 {
 // CHECK:           %[[VAL_1:.*]] = llvm.getelementptr %[[VAL_0]][0, 1] : (!llvm.ptr<struct<(ptr<f32>, i64, i32, i8, i8, i8, i8)>>) -> !llvm.ptr<i32>
-// CHECK:           %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr<i32>
+// CHECK:           %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i32>
 // CHECK:           llvm.return %[[VAL_2]] : i32
 // CHECK:         }
 
-// CHECK-LABEL:   llvm.metadata @__flang_tbaa {
-// CHECK:           llvm.tbaa_root @[[ROOT:root_[0-9]*]] {id = "Flang Type TBAA Root"}
-// CHECK:           llvm.tbaa_type_desc @[[ANYACC:type_desc_[0-9]*]] {id = "any access", members = {<@[[ROOT]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[ANYDACC:type_desc_[0-9]*]] {id = "any data access", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[BOXMEM:type_desc_[0-9]*]] {id = "descriptor member", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_tag @[[BOXT]] {access_type = @[[BOXMEM]], base_type = @[[BOXMEM]], offset = 0 : i64}
-// CHECK:         }
-
 // -----
 
 func.func @tbaa(%arg0: !fir.box<!fir.array<*:f64>>) -> i1 {
@@ -318,10 +304,15 @@ func.func @tbaa(%arg0: !fir.box<!fir.array<*:f64>>) -> i1 {
   return %0 : i1
 }
 
+// CHECK-DAG:     #[[ROOT:.*]] = #llvm.tbaa_root<id = "Flang Type TBAA Root">
+// CHECK-DAG:     #[[ANYACC:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[ROOT]], 0>}>
+// CHECK-DAG:     #[[BOXMEM:.*]] = #llvm.tbaa_type_desc<id = "descriptor member", members = {<#[[ANYACC]], 0>}>
+// CHECK-DAG:     #[[$BOXT:.*]] = #llvm.tbaa_tag<base_type = #[[BOXMEM]], access_type = #[[BOXMEM]], offset = 0>
+
 // CHECK-LABEL:   llvm.func @tbaa(
 // CHECK-SAME:                    %[[VAL_0:.*]]: !llvm.ptr<struct<(ptr<f64>, i64, i32, i8, i8, i8, i8)>>) -> i1 {
 // CHECK:           %[[VAL_1:.*]] = llvm.getelementptr %[[VAL_0]][0, 5] : (!llvm.ptr<struct<(ptr<f64>, i64, i32, i8, i8, i8, i8)>>) -> !llvm.ptr<i32>
-// CHECK:           %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr<i32>
+// CHECK:           %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i32>
 // CHECK:           %[[VAL_3:.*]] = llvm.mlir.constant(2 : i32) : i32
 // CHECK:           %[[VAL_4:.*]] = llvm.and %[[VAL_2]], %[[VAL_3]]  : i32
 // CHECK:           %[[VAL_5:.*]] = llvm.mlir.constant(0 : i32) : i32
@@ -329,14 +320,6 @@ func.func @tbaa(%arg0: !fir.box<!fir.array<*:f64>>) -> i1 {
 // CHECK:           llvm.return %[[VAL_6]] : i1
 // CHECK:         }
 
-// CHECK-LABEL:   llvm.metadata @__flang_tbaa {
-// CHECK:           llvm.tbaa_root @[[ROOT:root_[0-9]*]] {id = "Flang Type TBAA Root"}
-// CHECK:           llvm.tbaa_type_desc @[[ANYACC:type_desc_[0-9]*]] {id = "any access", members = {<@[[ROOT]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[ANYDACC:type_desc_[0-9]*]] {id = "any data access", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[BOXMEM:type_desc_[0-9]*]] {id = "descriptor member", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_tag @[[BOXT]] {access_type = @[[BOXMEM]], base_type = @[[BOXMEM]], offset = 0 : i64}
-// CHECK:         }
-
 // -----
 
 func.func @tbaa(%arg0: !fir.box<!fir.array<?xi32>>) {
@@ -345,6 +328,11 @@ func.func @tbaa(%arg0: !fir.box<!fir.array<?xi32>>) {
   return
 }
 
+// CHECK-DAG:     #[[ROOT:.*]] = #llvm.tbaa_root<id = "Flang Type TBAA Root">
+// CHECK-DAG:     #[[ANYACC:.*]] = #llvm.tbaa_type_desc<id = "any access", members = {<#[[ROOT]], 0>}>
+// CHECK-DAG:     #[[BOXMEM:.*]] = #llvm.tbaa_type_desc<id = "descriptor member", members = {<#[[ANYACC]], 0>}>
+// CHECK-DAG:     #[[$BOXT:.*]] = #llvm.tbaa_tag<base_type = #[[BOXMEM]], access_type = #[[BOXMEM]], offset = 0>
+
 // CHECK-LABEL:   llvm.func @tbaa(
 // CHECK-SAME:                    %[[VAL_0:.*]]: !llvm.ptr<struct<(ptr<i32>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>)>>) {
 // CHECK:           %[[VAL_1:.*]] = llvm.mlir.constant(0 : i64) : i64
@@ -353,21 +341,13 @@ func.func @tbaa(%arg0: !fir.box<!fir.array<?xi32>>) {
 // CHECK:           %[[VAL_4:.*]] = llvm.sub %[[VAL_1]], %[[VAL_2]]  : i64
 // CHECK:           %[[VAL_5:.*]] = llvm.mul %[[VAL_4]], %[[VAL_2]]  : i64
 // CHECK:           %[[VAL_6:.*]] = llvm.getelementptr %[[VAL_0]][0, 7, 0, 2] : (!llvm.ptr<struct<(ptr<i32>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>)>>) -> !llvm.ptr<i64>
-// CHECK:           %[[VAL_7:.*]] = llvm.load %[[VAL_6]] {tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr<i64>
+// CHECK:           %[[VAL_7:.*]] = llvm.load %[[VAL_6]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<i64>
 // CHECK:           %[[VAL_8:.*]] = llvm.mul %[[VAL_5]], %[[VAL_7]]  : i64
 // CHECK:           %[[VAL_9:.*]] = llvm.add %[[VAL_8]], %[[VAL_3]]  : i64
 // CHECK:           %[[VAL_10:.*]] = llvm.getelementptr %[[VAL_0]][0, 0] : (!llvm.ptr<struct<(ptr<i32>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>)>>) -> !llvm.ptr<ptr<i32>>
-// CHECK:           %[[VAL_11:.*]] = llvm.load %[[VAL_10]] {tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<ptr<i32>>
+// CHECK:           %[[VAL_11:.*]] = llvm.load %[[VAL_10]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr<ptr<i32>>
 // CHECK:           %[[VAL_12:.*]] = llvm.bitcast %[[VAL_11]] : !llvm.ptr<i32> to !llvm.ptr<i8>
 // CHECK:           %[[VAL_13:.*]] = llvm.getelementptr %[[VAL_12]]{{\[}}%[[VAL_9]]] : (!llvm.ptr<i8>, i64) -> !llvm.ptr<i8>
 // CHECK:           %[[VAL_14:.*]] = llvm.bitcast %[[VAL_13]] : !llvm.ptr<i8> to !llvm.ptr<i32>
 // CHECK:           llvm.return
 // CHECK:         }
-
-// CHECK-LABEL:   llvm.metadata @__flang_tbaa {
-// CHECK:           llvm.tbaa_root @[[ROOT:root_[0-9]*]] {id = "Flang Type TBAA Root"}
-// CHECK:           llvm.tbaa_type_desc @[[ANYACC:type_desc_[0-9]*]] {id = "any access", members = {<@[[ROOT]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[ANYDACC:type_desc_[0-9]*]] {id = "any data access", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_type_desc @[[BOXMEM:type_desc_[0-9]*]] {id = "descriptor member", members = {<@[[ANYACC]], 0>}}
-// CHECK:           llvm.tbaa_tag @[[BOXT]] {access_type = @[[BOXMEM]], base_type = @[[BOXMEM]], offset = 0 : i64}
-// CHECK:         }

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index e2c2ca578558f3..f58fab881d572a 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -685,4 +685,149 @@ def LLVM_AccessGroupArrayAttr
   let constBuilderCall = ?;
 }
 
+//===----------------------------------------------------------------------===//
+// TBAARootAttr
+//===----------------------------------------------------------------------===//
+
+def LLVM_TBAARootAttr : LLVM_Attr<"TBAARoot", "tbaa_root", [], "TBAANodeAttr"> {
+  let parameters = (ins OptionalParameter<"StringAttr">:$id);
+
+  let summary = "LLVM dialect TBAA root metadata";
+  let description = [{
+    Defines a TBAA root node.
+
+    Example:
+    ```mlir
+    #cpp_root = #llvm.tbaa_root<identity = "Simple C/C++ TBAA">
+    #other_root = #llvm.tbaa_root
+    ```
+
+    See the following link for more details:
+    https://llvm.org/docs/LangRef.html#tbaa-metadata
+  }];
+
+  let assemblyFormat = "(`<` struct(params)^ `>`)?";
+}
+
+//===----------------------------------------------------------------------===//
+// TBAATypeDescriptorAttr
+//===----------------------------------------------------------------------===//
+
+def LLVM_TBAAMemberAttr : LLVM_Attr<"TBAAMember", "tbaa_member"> {
+  let parameters = (ins
+    "TBAANodeAttr":$typeDesc,
+    "int64_t":$offset
+  );
+
+  let builders = [
+    AttrBuilderWithInferredContext<(ins "TBAANodeAttr":$typeDesc,
+                                        "int64_t":$offset), [{
+      return $_get(typeDesc.getContext(), typeDesc, offset);
+    }]>
+  ];
+
+  let assemblyFormat = "`<` params `>`";
+}
+
+def LLVM_TBAAMemberAttrArray : ArrayRefParameter<"TBAAMemberAttr"> {
+  let printer = [{
+    $_printer << '{';
+    llvm::interleaveComma($_self, $_printer, [&](TBAAMemberAttr attr) {
+        $_printer.printStrippedAttrOrType(attr);
+    });
+    $_printer << '}';
+  }];
+
+  let parser = [{
+    [&]() -> FailureOr<SmallVector<TBAAMemberAttr>> {
+        using Result = SmallVector<TBAAMemberAttr>;
+        if ($_parser.parseLBrace())
+            return failure();
+        FailureOr<Result> result = FieldParser<Result>::parse($_parser);
+        if (failed(result))
+            return failure();
+        if ($_parser.parseRBrace())
+            return failure();
+        return result;
+    }()
+  }];
+}
+
+def LLVM_TBAATypeDescriptorAttr : LLVM_Attr<"TBAATypeDescriptor",
+    "tbaa_type_desc", [], "TBAANodeAttr"> {
+  let parameters = (ins
+    StringRefParameter<>:$id,
+    LLVM_TBAAMemberAttrArray:$members
+  );
+
+  let summary = "LLVM dialect TBAA type metadata";
+
+  let description = [{
+    Defines a TBAA node describing a type.
+
+    Example:
+    ```mlir
+    #tbaa_root = #llvm.tbaa_root<identity = "Simple C/C++ TBAA">
+    #tbaa_type_desc1 = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root, 0>}>
+    #tbaa_type_desc2 = #llvm.tbaa_type_desc<id = "long long", members = {<#tbaa_root, 0>}>
+    #tbaa_type_desc3 = #llvm.tbaa_type_desc<id = "agg2_t", members = {<#tbaa_type_desc2, 0>, <#tbaa_type_desc2, 8>}>
+    #tbaa_type_desc4 = #llvm.tbaa_type_desc<id = "int", members = {<#tbaa_type_desc1, 0>}>
+    #tbaa_type_desc5 = #llvm.tbaa_type_desc<id = "agg1_t", members = {<#tbaa_type_desc4, 0>, <#tbaa_type_desc4, 4>}>
+    ```
+
+    See the following link for more details:
+    https://llvm.org/docs/LangRef.html#tbaa-metadata
+  }];
+
+  let assemblyFormat = "`<` struct(params) `>`";
+}
+
+//===----------------------------------------------------------------------===//
+// TBAATagAttr
+//===----------------------------------------------------------------------===//
+
+def LLVM_TBAATagAttr : LLVM_Attr<"TBAATag", "tbaa_tag"> {
+  let parameters = (ins
+    "TBAATypeDescriptorAttr":$base_type,
+    "TBAATypeDescriptorAttr":$access_type,
+    "int64_t":$offset,
+    DefaultValuedParameter<"bool", "false">:$constant
+  );
+
+  let builders = [
+    AttrBuilderWithInferredContext<(ins "TBAATypeDescriptorAttr":$baseType,
+                                        "TBAATypeDescriptorAttr":$accessType,
+                                        "int64_t":$offset), [{
+      return $_get(baseType.getContext(), baseType, accessType, offset,
+                    /*constant=*/false);
+    }]>
+  ];
+
+  let summary = "LLVM dialect TBAA tag metadata";
+
+  let description = [{
+    Defines a TBAA node describing a memory access.
+
+    Example:
+    ```mlir
+    #tbaa_root = #llvm.tbaa_root<identity = "Simple C/C++ TBAA">
+    #tbaa_type_desc1 = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root, 0>}>
+    #tbaa_type_desc2 = #llvm.tbaa_type_desc<id = "int", members = {<#tbaa_type_desc1, 0>}>
+    #tbaa_type_desc3 = #llvm.tbaa_type_desc<id = "agg1_t", members = {<#tbaa_type_desc4, 0>, <#tbaa_type_desc4, 4>}>
+    #tbaa_tag = #llvm.tbaa_tag<base_type = #tbaa_type_desc3, access_type = #tbaa_type_desc2, offset = 0, constant = true>
+    ```
+
+    See the following link for more details:
+    https://llvm.org/docs/LangRef.html#tbaa-metadata
+  }];
+
+  let assemblyFormat = "`<` struct(params) `>`";
+}
+
+def LLVM_TBAATagArrayAttr
+    : TypedArrayAttrBase<LLVM_TBAATagAttr,
+                         LLVM_TBAATagAttr.summary # " array"> {
+  let constBuilderCall = ?;
+}
+
 #endif // LLVMIR_ATTRDEFS

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
index abbfcf415c5471..c370bfa2b733d6 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
@@ -60,6 +60,20 @@ class DITypeAttr : public DINodeAttr {
   static bool classof(Attribute attr);
 };
 
+/// Base class for LLVM attributes participating in the TBAA graph.
+class TBAANodeAttr : public Attribute {
+public:
+  using Attribute::Attribute;
+
+  /// Support LLVM type casting.
+  static bool classof(Attribute attr);
+
+  /// Required by DenseMapInfo to create empty and tombstone key.
+  static TBAANodeAttr getFromOpaquePointer(const void *pointer) {
+    return TBAANodeAttr(reinterpret_cast<const ImplType *>(pointer));
+  }
+};
+
 // Inline the LLVM generated Linkage enum and utility.
 // This is only necessary to isolate the "enum generated code" from the
 // attribute definition itself.

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
index 7750dca4440a7d..2ac98769cf2f59 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
@@ -281,7 +281,7 @@ class LLVM_MemAccessOpBase<string mnemonic, list<Trait> traits = []> :
   dag aliasAttrs = (ins OptionalAttr<LLVM_AccessGroupArrayAttr>:$access_groups,
                     OptionalAttr<LLVM_AliasScopeArrayAttr>:$alias_scopes,
                     OptionalAttr<LLVM_AliasScopeArrayAttr>:$noalias_scopes,
-                    OptionalAttr<SymbolRefArrayAttr>:$tbaa);
+                    OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa);
 }
 
 // Base class for LLVM intrinsics operation. It is similar to LLVM_Op, but
@@ -326,7 +326,7 @@ class LLVM_IntrOpBase<Dialect dialect, string opName, string enumName,
         !if(!gt(requiresAliasAnalysis, 0),
             (ins OptionalAttr<LLVM_AliasScopeArrayAttr>:$alias_scopes,
                  OptionalAttr<LLVM_AliasScopeArrayAttr>:$noalias_scopes,
-                 OptionalAttr<SymbolRefArrayAttr>:$tbaa),
+                 OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa),
             (ins )));
   string resultPattern = !if(!gt(numResults, 1),
                              LLVM_IntrPatterns.structResult,

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 04389f0efdfbae..01070fe74bff9a 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1096,156 +1096,6 @@ def LLVM_MetadataOp : LLVM_Op<"metadata", [
   ];
 
   let hasCustomAssemblyFormat = 1;
-  let hasRegionVerifier = 1;
-}
-
-def LLVM_TBAARootMetadataOp : LLVM_Op<"tbaa_root", [
-  HasParent<"MetadataOp">, Symbol
-]> {
-  let arguments = (ins
-    SymbolNameAttr:$sym_name,
-    StrAttr:$identity
-  );
-  let summary = "LLVM dialect TBAA root node metadata.";
-  let description = [{
-    Defines a TBAA root node.
-
-    Example:
-    ```mlir
-    llvm.metadata @tbaa {
-      llvm.tbaa_root @tbaa_root_0 {identity = "Simple C/C++ TBAA"}
-    }
-    ```
-
-    See the following link for more details:
-    https://llvm.org/docs/LangRef.html#tbaa-metadata
-  }];
-  let assemblyFormat = [{
-    $sym_name ` ` `{` `id` `=` $identity `}` attr-dict
-  }];
-  let hasVerifier = 1;
-}
-
-def LLVM_TBAATypeDescriptorOp : LLVM_Op<"tbaa_type_desc", [
-  HasParent<"MetadataOp">, Symbol
-]> {
-  let arguments = (ins
-    SymbolNameAttr:$sym_name,
-    OptionalAttr<StrAttr>:$identity,
-    FlatSymbolRefArrayAttr:$members,
-    DenseI64ArrayAttr:$offsets
-  );
-  let summary = "LLVM dialect TBAA node describing a type.";
-  let description = [{
-    Defines a TBAA node describing a type.
-
-    Example:
-    ```mlir
-    llvm.metadata @tbaa {
-      llvm.tbaa_root @tbaa_root_0 {identity = "Simple C/C++ TBAA"}
-      llvm.tbaa_type_desc @tbaa_type_desc_1 {
-          identity = "omnipotent char",
-          members = [@tbaa_root_0],
-          offsets = array<i64: 0>
-      }
-      llvm.tbaa_type_desc @tbaa_type_desc_2 {
-          identity = "long long",
-          members = [@tbaa_type_desc_1],
-          offsets = array<i64: 0>
-      }
-      llvm.tbaa_type_desc @tbaa_type_desc_3 {
-          identity = "agg2_t",
-          members = [@tbaa_type_desc_2, @tbaa_type_desc_2],
-          offsets = array<i64: 0, 8>
-      }
-      llvm.tbaa_type_desc @tbaa_type_desc_5 {
-          identity = "int",
-          members = [@tbaa_type_desc_1],
-          offsets = array<i64: 0>
-      }
-      llvm.tbaa_type_desc @tbaa_type_desc_6 {
-          identity = "agg1_t",
-          members = [@tbaa_type_desc_5, @tbaa_type_desc_5],
-          offsets = array<i64: 0, 4>
-      }
-    }
-    ```
-
-    See the following link for more details:
-    https://llvm.org/docs/LangRef.html#tbaa-metadata
-  }];
-
-  // Interleave member types and offsets for better matching
-  // LLVM IR text representation.
-  let assemblyFormat = [{
-    $sym_name ` ` `{`
-      ( `id` `=` $identity^ )? `,`
-      `members` `=` `{` custom<TBAAMembers>($members, $offsets) `}`
-    `}` attr-dict
-  }];
-  let hasVerifier = 1;
-}
-
-def LLVM_TBAATagOp : LLVM_Op<"tbaa_tag", [
-  HasParent<"MetadataOp">, Symbol
-]> {
-  let arguments = (ins
-    SymbolNameAttr:$sym_name,
-    FlatSymbolRefAttr:$base_type,
-    FlatSymbolRefAttr:$access_type,
-    I64Attr:$offset,
-    UnitAttr:$constant
-  );
-  let summary = "LLVM dialect TBAA node describing a memory access.";
-  let description = [{
-    Defines a TBAA node describing a memory access.
-
-    Example:
-    ```mlir
-    llvm.metadata @tbaa {
-      llvm.tbaa_root @tbaa_root_0 {identity = "Simple C/C++ TBAA"}
-      llvm.tbaa_type_desc @tbaa_type_desc_1 {
-          identity = "omnipotent char",
-          members = [@tbaa_root_0],
-          offsets = array<i64: 0>
-      }
-      llvm.tbaa_type_desc @tbaa_type_desc_2 {
-          identity = "long long",
-          members = [@tbaa_type_desc_1],
-          offsets = array<i64: 0>
-      }
-      llvm.tbaa_type_desc @tbaa_type_desc_3 {
-          identity = "agg2_t",
-          members = [@tbaa_type_desc_2, @tbaa_type_desc_2],
-          offsets = array<i64: 0, 8>
-      }
-      llvm.tbaa_tag @tbaa_tag_4 {
-          access_type = @tbaa_type_desc_2,
-          base_type = @tbaa_type_desc_3,
-          offset = 8 : i64
-      }
-      llvm.tbaa_type_desc @tbaa_type_desc_5 {
-          identity = "int",
-          members = [@tbaa_type_desc_1],
-          offsets = array<i64: 0>
-      }
-      llvm.tbaa_type_desc @tbaa_type_desc_6 {
-          identity = "agg1_t",
-          members = [@tbaa_type_desc_5, @tbaa_type_desc_5],
-          offsets = array<i64: 0, 4>
-      }
-      llvm.tbaa_tag @tbaa_tag_7 {
-          access_type = @tbaa_type_desc_5,
-          base_type = @tbaa_type_desc_6,
-          offset = 0 : i64
-      }
-    }
-    ```
-
-    See the following link for more details:
-    https://llvm.org/docs/LangRef.html#tbaa-metadata
-  }];
-  let assemblyFormat = "$sym_name attr-dict";
 }
 
 def LLVM_GlobalOp : LLVM_Op<"mlir.global",

diff  --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
index d86c95ad8e3e76..8b7bf10333cf23 100644
--- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
+++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
@@ -176,18 +176,15 @@ class ModuleImport {
   /// implement the fastmath interface.
   void setFastmathFlagsAttr(llvm::Instruction *inst, Operation *op) const;
 
-  /// Converts all LLVM metadata nodes that translate to operations nested in a
-  /// global metadata operation, such as alias analysis or access group
-  /// metadata, and builds a map from the metadata nodes to the symbols pointing
-  /// to the converted operations. Returns success if all conversions succeed
-  /// and failure otherwise.
-  // Note: All metadata is nested inside a single global metadata operation to
-  // minimize the number of symbols that pollute the global namespace.
+  /// Converts all LLVM metadata nodes that translate to attributes such as
+  /// alias analysis or access group metadata, and builds a map from the
+  /// metadata nodes to the converted attributes.
+  /// Returns success if all conversions succeed and failure otherwise.
   LogicalResult convertMetadata();
 
-  /// Returns the MLIR symbol reference mapped to the given LLVM TBAA
+  /// Returns the MLIR attribute mapped to the given LLVM TBAA
   /// metadata `node`.
-  SymbolRefAttr lookupTBAAAttr(const llvm::MDNode *node) const {
+  Attribute lookupTBAAAttr(const llvm::MDNode *node) const {
     return tbaaMapping.lookup(node);
   }
 
@@ -288,10 +285,6 @@ class ModuleImport {
   /// them fails. All operations are inserted at the start of the current
   /// function entry block.
   FailureOr<Value> convertConstantExpr(llvm::Constant *constant);
-  /// Returns a global metadata operation that serves as a container for LLVM
-  /// metadata that converts to MLIR operations. Creates the global metadata
-  /// operation on the first invocation.
-  MetadataOp getGlobalMetadataOp();
   /// Returns a global comdat operation that serves as a container for LLVM
   /// comdat selectors. Creates the global comdat operation on the first
   /// invocation.
@@ -304,14 +297,13 @@ class ModuleImport {
   LogicalResult processTBAAMetadata(const llvm::MDNode *node);
   /// Converts all LLVM access groups starting from `node` to MLIR access group
   /// operations and stores a mapping from every nested access group node to the
-  /// symbol pointing to the translated operation. Returns success if all
-  /// conversions succeed and failure otherwise.
+  /// translated attribute. Returns success if all conversions succeed and
+  /// failure otherwise.
   LogicalResult processAccessGroupMetadata(const llvm::MDNode *node);
   /// Converts all LLVM alias scopes and domains starting from `node` to MLIR
-  /// alias scope and domain operations and stores a mapping from every nested
-  /// alias scope or alias domain node to the symbol pointing to the translated
-  /// operation. Returns success if all conversions succeed and failure
-  /// otherwise.
+  /// alias scope and domain attributes and stores a mapping from every nested
+  /// alias scope or alias domain node to the translated attribute. Returns
+  /// success if all conversions succeed and failure otherwise.
   LogicalResult processAliasScopeMetadata(const llvm::MDNode *node);
   /// Converts the given LLVM comdat struct to an MLIR comdat selector operation
   /// and stores a mapping from the struct to the symbol pointing to the
@@ -326,8 +318,6 @@ class ModuleImport {
   Operation *constantInsertionOp = nullptr;
   /// Operation to insert the next global after.
   Operation *globalInsertionOp = nullptr;
-  /// Operation to insert metadata operations into.
-  MetadataOp globalMetadataOp = nullptr;
   /// Operation to insert comdat selector operations into.
   ComdatOp globalComdatOp = nullptr;
   /// The current context.
@@ -352,9 +342,9 @@ class ModuleImport {
   /// Mapping between LLVM alias scope and domain metadata nodes and
   /// attributes in the LLVM dialect corresponding to these nodes.
   DenseMap<const llvm::MDNode *, Attribute> aliasScopeMapping;
-  /// Mapping between LLVM TBAA metadata nodes and symbol references to the LLVM
-  /// dialect TBAA operations corresponding to these nodes.
-  DenseMap<const llvm::MDNode *, SymbolRefAttr> tbaaMapping;
+  /// Mapping between LLVM TBAA metadata nodes and LLVM dialect TBAA attributes
+  /// corresponding to these nodes.
+  DenseMap<const llvm::MDNode *, Attribute> tbaaMapping;
   /// Mapping between LLVM comdat structs and symbol references to LLVM dialect
   /// comdat selector operations corresponding to these structs.
   DenseMap<const llvm::Comdat *, SymbolRefAttr> comdatMapping;

diff  --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
index 8f3fcc5284dc2f..da4d43ac9ac844 100644
--- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
+++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
@@ -282,9 +282,9 @@ class ModuleTranslation {
   /// metadata nodes for them and their domains.
   LogicalResult createAliasScopeMetadata();
 
-  /// Returns the LLVM metadata corresponding to a symbol reference to an mlir
-  /// LLVM dialect TBAATagOp operation.
-  llvm::MDNode *getTBAANode(Operation *op, SymbolRefAttr tagRef) const;
+  /// Returns the LLVM metadata corresponding to the given mlir LLVM dialect
+  /// TBAATagAttr.
+  llvm::MDNode *getTBAANode(TBAATagAttr tbaaAttr) const;
 
   /// Process tbaa LLVM Metadata operations and create LLVM
   /// metadata nodes for them.
@@ -332,9 +332,9 @@ class ModuleTranslation {
   /// This map is populated on module entry.
   DenseMap<Attribute, llvm::MDNode *> aliasScopeMetadataMapping;
 
-  /// Mapping from a tbaa metadata operation to its LLVM metadata.
+  /// Mapping from a tbaa attribute to its LLVM metadata.
   /// This map is populated on module entry.
-  DenseMap<const Operation *, llvm::MDNode *> tbaaMetadataMapping;
+  DenseMap<Attribute, llvm::MDNode *> tbaaMetadataMapping;
 
   /// Mapping from a comdat selector operation to its LLVM comdat struct.
   /// This map is populated on module entry.

diff  --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
index 0e0c310a00b440..6e255153f51b24 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
@@ -77,6 +77,14 @@ bool DITypeAttr::classof(Attribute attr) {
                    DIDerivedTypeAttr, DISubroutineTypeAttr>(attr);
 }
 
+//===----------------------------------------------------------------------===//
+// TBAANodeAttr
+//===----------------------------------------------------------------------===//
+
+bool TBAANodeAttr::classof(Attribute attr) {
+  return llvm::isa<TBAATypeDescriptorAttr, TBAARootAttr>(attr);
+}
+
 //===----------------------------------------------------------------------===//
 // MemoryEffectsAttr
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index f06810968d6844..8eee3b2afc8414 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -2828,227 +2828,6 @@ void MetadataOp::print(OpAsmPrinter &printer) {
   printer.printRegion(getBody());
 }
 
-namespace {
-// A node of the TBAA graph.
-struct TBAAGraphNode {
-  // Symbol name defined by a TBAA operation.
-  StringRef symbol;
-  // Operands (if any) of the TBAA operation.
-  SmallVector<TBAAGraphNode *> operands;
-};
-
-// TBAA graph.
-class TBAAGraph {
-public:
-  using iterator = SmallVectorImpl<TBAAGraphNode *>::iterator;
-
-  // Creates a new graph with nodes corresponding to `symbolNames` defined by a
-  // set of TBAA operations.
-  TBAAGraph(ArrayRef<StringAttr> symbolNames) {
-    for (auto symbol : symbolNames) {
-      TBAAGraphNode &node = nodeMap[symbol];
-      assert(node.symbol.empty() && "node is already in the graph");
-      node.symbol = symbol;
-    }
-
-    // Fill the graph operands once all nodes were added. Otherwise,
-    // reallocation can lead to pointer invalidation.
-    for (auto symbol : symbolNames)
-      root.operands.push_back(&nodeMap[symbol]);
-  }
-
-  iterator begin() { return root.operands.begin(); }
-  iterator end() { return root.operands.end(); }
-  TBAAGraphNode *getEntryNode() { return &root; }
-
-  // Get a pointer to TBAAGraphNode corresponding
-  // to `symbol`. The node must be already in the graph.
-  TBAAGraphNode *operator[](StringAttr symbol) {
-    auto it = nodeMap.find(symbol);
-    assert(it != nodeMap.end() && "node must be in the graph");
-    return &it->second;
-  }
-
-private:
-  // Mapping between symbol names defined by TBAA
-  // operations and corresponding TBAAGraphNode's.
-  DenseMap<StringAttr, TBAAGraphNode> nodeMap;
-  // Synthetic root node that has all graph nodes
-  // in its operands list.
-  TBAAGraphNode root;
-};
-} // end anonymous namespace
-
-namespace llvm {
-// GraphTraits definitions for using TBAAGraph with
-// scc_iterator.
-template <>
-struct GraphTraits<TBAAGraphNode *> {
-  using NodeRef = TBAAGraphNode *;
-  using ChildIteratorType = SmallVectorImpl<TBAAGraphNode *>::iterator;
-  static ChildIteratorType child_begin(NodeRef ref) {
-    return ref->operands.begin();
-  }
-  static ChildIteratorType child_end(NodeRef ref) {
-    return ref->operands.end();
-  }
-};
-template <>
-struct GraphTraits<TBAAGraph *> : public GraphTraits<TBAAGraphNode *> {
-  static NodeRef getEntryNode(TBAAGraph *graph) {
-    return graph->getEntryNode();
-  }
-  static ChildIteratorType nodes_begin(TBAAGraph *graph) {
-    return graph->begin();
-  }
-  static ChildIteratorType nodes_end(TBAAGraph *graph) { return graph->end(); }
-};
-} // end namespace llvm
-
-LogicalResult MetadataOp::verifyRegions() {
-  // Verify correctness of TBAA-related symbol references.
-  Region &body = getBody();
-  // Symbol names defined by TBAARootMetadataOp and TBAATypeDescriptorOp.
-  llvm::SmallDenseSet<StringAttr> definedGraphSymbols;
-
-  // Collection of symbol names to ensure a stable ordering of the pointers.
-  // Otherwise, error messages might not be deterministic.
-  SmallVector<StringAttr> symbolNames;
-
-  for (Operation &op : body.getOps()) {
-    if (isa<LLVM::TBAARootMetadataOp>(op) ||
-        isa<LLVM::TBAATypeDescriptorOp>(op)) {
-      StringAttr symbolDef = cast<SymbolOpInterface>(op).getNameAttr();
-      definedGraphSymbols.insert(symbolDef);
-      symbolNames.push_back(symbolDef);
-    } else if (auto tagOp = dyn_cast<LLVM::TBAATagOp>(op)) {
-      symbolNames.push_back(tagOp.getSymNameAttr());
-    }
-  }
-
-  // Complete TBAA graph consisting of TBAARootMetadataOp,
-  // TBAATypeDescriptorOp, and TBAATagOp symbols. It is used
-  // for detecting cycles in the TBAA graph, which is illegal.
-  TBAAGraph tbaaGraph(symbolNames);
-
-  // Verify that TBAA metadata operations refer symbols
-  // from definedGraphSymbols only. Note that TBAATagOp
-  // cannot refer a symbol defined by TBAATagOp.
-  auto verifyReference = [&](Operation &op, StringAttr symbolName,
-                             StringAttr referencingAttr) -> LogicalResult {
-    if (definedGraphSymbols.contains(symbolName))
-      return success();
-    return op.emitOpError()
-           << "expected " << referencingAttr << " to reference a symbol from '"
-           << (*this)->getName() << " @" << getSymName()
-           << "' defined by either '"
-           << LLVM::TBAARootMetadataOp::getOperationName() << "' or '"
-           << LLVM::TBAATypeDescriptorOp::getOperationName()
-           << "' while it references '@" << symbolName.getValue() << "'";
-  };
-  for (Operation &op : body.getOps()) {
-    if (auto tdOp = dyn_cast<LLVM::TBAATypeDescriptorOp>(op)) {
-      SmallVectorImpl<TBAAGraphNode *> &operands =
-          tbaaGraph[tdOp.getSymNameAttr()]->operands;
-      for (Attribute attr : tdOp.getMembers()) {
-        StringAttr symbolRef = llvm::cast<FlatSymbolRefAttr>(attr).getAttr();
-        if (failed(verifyReference(op, symbolRef, tdOp.getMembersAttrName())))
-          return failure();
-
-        // Since the reference is valid, we have to be able
-        // to find TBAAGraphNode corresponding to the operand.
-        operands.push_back(tbaaGraph[symbolRef]);
-      }
-    }
-
-    if (auto tagOp = dyn_cast<LLVM::TBAATagOp>(op)) {
-      SmallVectorImpl<TBAAGraphNode *> &operands =
-          tbaaGraph[tagOp.getSymNameAttr()]->operands;
-      if (failed(verifyReference(op, tagOp.getBaseTypeAttr().getAttr(),
-                                 tagOp.getBaseTypeAttrName())))
-        return failure();
-      if (failed(verifyReference(op, tagOp.getAccessTypeAttr().getAttr(),
-                                 tagOp.getAccessTypeAttrName())))
-        return failure();
-
-      operands.push_back(tbaaGraph[tagOp.getBaseTypeAttr().getAttr()]);
-      operands.push_back(tbaaGraph[tagOp.getAccessTypeAttr().getAttr()]);
-    }
-  }
-
-  // Detect cycles in the TBAA graph.
-  for (llvm::scc_iterator<TBAAGraph *> sccIt = llvm::scc_begin(&tbaaGraph);
-       !sccIt.isAtEnd(); ++sccIt) {
-    if (!sccIt.hasCycle())
-      continue;
-    auto diagOut = emitOpError() << "has cycle in TBAA graph (graph closure: <";
-    llvm::interleaveComma(
-        *sccIt, diagOut, [&](TBAAGraphNode *node) { diagOut << node->symbol; });
-    return diagOut << ">)";
-  }
-  return success();
-}
-
-//===----------------------------------------------------------------------===//
-// Utilities for TBAA related operations/attributes
-//===----------------------------------------------------------------------===//
-
-static ParseResult parseTBAAMembers(OpAsmParser &parser, ArrayAttr &members,
-                                    DenseI64ArrayAttr &offsets) {
-  SmallVector<Attribute> membersVec;
-  SmallVector<int64_t> offsetsVec;
-  auto parseMembers = [&]() {
-    // Parse a pair of `<@tbaa_type_desc_sym, integer-offset>`.
-    FlatSymbolRefAttr member;
-    int64_t offset;
-    if (parser.parseLess() || parser.parseAttribute(member, Type()) ||
-        parser.parseComma() || parser.parseInteger(offset) ||
-        parser.parseGreater())
-      return failure();
-
-    membersVec.push_back(member);
-    offsetsVec.push_back(offset);
-    return success();
-  };
-
-  if (parser.parseCommaSeparatedList(parseMembers))
-    return failure();
-
-  members = ArrayAttr::get(parser.getContext(), membersVec);
-  offsets = DenseI64ArrayAttr::get(parser.getContext(), offsetsVec);
-  return success();
-}
-
-static void printTBAAMembers(OpAsmPrinter &printer,
-                             LLVM::TBAATypeDescriptorOp tdOp, ArrayAttr members,
-                             DenseI64ArrayAttr offsets) {
-  llvm::interleaveComma(
-      llvm::zip(members, offsets.asArrayRef()), printer, [&](auto it) {
-        // Print `<@tbaa_type_desc_sym, integer-offset>`.
-        printer << '<' << std::get<0>(it) << ", " << std::get<1>(it) << '>';
-      });
-}
-
-LogicalResult TBAARootMetadataOp::verify() {
-  if (!getIdentity().empty())
-    return success();
-  return emitOpError() << "expected non-empty " << getIdentityAttrName();
-}
-
-LogicalResult TBAATypeDescriptorOp::verify() {
-  // Verify that the members and offsets arrays have the same
-  // number of elements.
-  ArrayAttr members = getMembers();
-  StringAttr membersName = getMembersAttrName();
-  if (members.size() != getOffsets().size())
-    return emitOpError() << "expected the same number of elements in "
-                         << membersName << " and " << getOffsetsAttrName()
-                         << ": " << members.size()
-                         << " != " << getOffsets().size();
-
-  return success();
-}
-
 //===----------------------------------------------------------------------===//
 // OpAsmDialectInterface
 //===----------------------------------------------------------------------===//
@@ -3067,7 +2846,8 @@ struct LLVMOpAsmDialectInterface : public OpAsmDialectInterface {
               LoopAnnotationAttr, LoopVectorizeAttr, LoopInterleaveAttr,
               LoopUnrollAttr, LoopUnrollAndJamAttr, LoopLICMAttr,
               LoopDistributeAttr, LoopPipelineAttr, LoopPeeledAttr,
-              LoopUnswitchAttr>([&](auto attr) {
+              LoopUnswitchAttr, TBAARootAttr, TBAATagAttr,
+              TBAATypeDescriptorAttr>([&](auto attr) {
           os << decltype(attr)::getMnemonic();
           return AliasResult::OverridableAlias;
         })

diff  --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
index ff6f4287589a3d..a61326059ae05f 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
@@ -16,71 +16,16 @@
 using namespace mlir;
 using namespace mlir::LLVM;
 
-/// Verifies the given array attribute contains symbol references and checks the
-/// referenced symbol types using the provided verification function.
-static LogicalResult
-verifySymbolRefs(Operation *op, StringRef name, ArrayAttr symbolRefs,
-                 llvm::function_ref<LogicalResult(Operation *, SymbolRefAttr)>
-                     verifySymbolType) {
-  assert(symbolRefs && "expected a non-null attribute");
-
-  // Verify that the attribute is a symbol ref array attribute,
-  // because this constraint is not verified for all attribute
-  // names processed here (e.g. 'tbaa'). This verification
-  // is redundant in some cases.
-  if (!llvm::all_of(symbolRefs, [](Attribute attr) {
-        return attr && llvm::isa<SymbolRefAttr>(attr);
-      }))
-    return op->emitOpError() << name
-                             << " attribute failed to satisfy constraint: "
-                                "symbol ref array attribute";
-
-  for (SymbolRefAttr symbolRef : symbolRefs.getAsRange<SymbolRefAttr>()) {
-    StringAttr metadataName = symbolRef.getRootReference();
-    StringAttr symbolName = symbolRef.getLeafReference();
-    // We want @metadata::@symbol, not just @symbol
-    if (metadataName == symbolName) {
-      return op->emitOpError() << "expected '" << symbolRef
-                               << "' to specify a fully qualified reference";
-    }
-    auto metadataOp = SymbolTable::lookupNearestSymbolFrom<LLVM::MetadataOp>(
-        op->getParentOp(), metadataName);
-    if (!metadataOp)
-      return op->emitOpError()
-             << "expected '" << symbolRef << "' to reference a metadata op";
-    Operation *symbolOp =
-        SymbolTable::lookupNearestSymbolFrom(metadataOp, symbolName);
-    if (!symbolOp)
-      return op->emitOpError()
-             << "expected '" << symbolRef << "' to be a valid reference";
-    if (failed(verifySymbolType(symbolOp, symbolRef))) {
-      return failure();
-    }
-  }
-
+/// Verifies that all elements of `array` are instances of `Attr`.
+template <class AttrT>
+static LogicalResult isArrayOf(Operation *op, ArrayAttr array) {
+  for (Attribute iter : array)
+    if (!isa<AttrT>(iter))
+      return op->emitOpError("expected op to return array of ")
+             << AttrT::getMnemonic() << " attributes";
   return success();
 }
 
-/// Verifies the given array attribute contains symbol references that point to
-/// metadata operations of the given type.
-template <typename OpTy>
-LogicalResult verifySymbolRefsPointTo(Operation *op, StringRef name,
-                                      ArrayAttr symbolRefs) {
-  if (!symbolRefs)
-    return success();
-
-  auto verifySymbolType = [op](Operation *symbolOp,
-                               SymbolRefAttr symbolRef) -> LogicalResult {
-    if (!isa<OpTy>(symbolOp)) {
-      return op->emitOpError()
-             << "expected '" << symbolRef << "' to resolve to a "
-             << OpTy::getOperationName();
-    }
-    return success();
-  };
-  return verifySymbolRefs(op, name, symbolRefs, verifySymbolType);
-}
-
 //===----------------------------------------------------------------------===//
 // AccessGroupOpInterface
 //===----------------------------------------------------------------------===//
@@ -90,11 +35,8 @@ LogicalResult mlir::LLVM::detail::verifyAccessGroupOpInterface(Operation *op) {
   ArrayAttr accessGroups = iface.getAccessGroupsOrNull();
   if (!accessGroups)
     return success();
-  for (Attribute iter : accessGroups)
-    if (!isa<AccessGroupAttr>(iter))
-      return op->emitOpError("expected op to return array of ")
-             << AccessGroupAttr::getMnemonic() << " attributes";
-  return success();
+
+  return isArrayOf<AccessGroupAttr>(op, accessGroups);
 }
 
 //===----------------------------------------------------------------------===//
@@ -104,10 +46,20 @@ LogicalResult mlir::LLVM::detail::verifyAccessGroupOpInterface(Operation *op) {
 LogicalResult
 mlir::LLVM::detail::verifyAliasAnalysisOpInterface(Operation *op) {
   auto iface = cast<AliasAnalysisOpInterface>(op);
-  if (failed(verifySymbolRefsPointTo<LLVM::TBAATagOp>(
-          iface, "tbaa tags", iface.getTBAATagsOrNull())))
-    return failure();
-  return success();
+
+  if (auto aliasScopes = iface.getAliasScopesOrNull())
+    if (failed(isArrayOf<AliasScopeAttr>(op, aliasScopes)))
+      return failure();
+
+  if (auto noAliasScopes = iface.getNoAliasScopesOrNull())
+    if (failed(isArrayOf<AliasScopeAttr>(op, noAliasScopes)))
+      return failure();
+
+  ArrayAttr tags = iface.getTBAATagsOrNull();
+  if (!tags)
+    return success();
+
+  return isArrayOf<TBAATagAttr>(op, tags);
 }
 
 #include "mlir/Dialect/LLVMIR/LLVMInterfaces.cpp.inc"

diff  --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index aa67ba78763455..a6f0ebe54aac2f 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -134,12 +134,12 @@ static LogicalResult setProfilingAttr(OpBuilder &builder, llvm::MDNode *node,
       .Default([](auto) { return failure(); });
 }
 
-/// Searches the symbol reference pointing to the metadata operation that
-/// maps to the given TBAA metadata `node` and attaches it to the imported
-/// operation if the lookup succeeds. Returns failure otherwise.
+/// Searches for the attribute that maps to the given TBAA metadata `node` and
+/// attaches it to the imported operation if the lookup succeeds. Returns
+/// failure otherwise.
 static LogicalResult setTBAAAttr(const llvm::MDNode *node, Operation *op,
                                  LLVM::ModuleImport &moduleImport) {
-  SymbolRefAttr tbaaTagSym = moduleImport.lookupTBAAAttr(node);
+  Attribute tbaaTagSym = moduleImport.lookupTBAAAttr(node);
   if (!tbaaTagSym)
     return failure();
 
@@ -151,10 +151,9 @@ static LogicalResult setTBAAAttr(const llvm::MDNode *node, Operation *op,
   return success();
 }
 
-/// Looks up all the symbol references pointing to the access group operations
-/// that map to the access group nodes starting from the access group metadata
-/// `node`, and attaches all of them to the imported operation if the lookups
-/// succeed. Returns failure otherwise.
+/// Looks up all the access group attributes that map to the access group nodes
+/// starting from the access group metadata `node`, and attaches all of them to
+/// the imported operation if the lookups succeed. Returns failure otherwise.
 static LogicalResult setAccessGroupsAttr(const llvm::MDNode *node,
                                          Operation *op,
                                          LLVM::ModuleImport &moduleImport) {
@@ -209,10 +208,9 @@ static LogicalResult setAliasScopesAttr(const llvm::MDNode *node, Operation *op,
   return success();
 }
 
-/// Looks up all the symbol references pointing to the alias scope operations
-/// that map to the alias scope nodes starting from the noalias metadata `node`,
-/// and attaches all of them to the imported operation if the lookups succeed.
-/// Returns failure otherwise.
+/// Looks up all the alias scope attributes that map to the alias scope nodes
+/// starting from the noalias metadata `node`, and attaches all of them to the
+/// imported operation if the lookups succeed. Returns failure otherwise.
 static LogicalResult setNoaliasScopesAttr(const llvm::MDNode *node,
                                           Operation *op,
                                           LLVM::ModuleImport &moduleImport) {

diff  --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 3119aac6161892..9f7ac54b8bcce1 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -76,12 +76,6 @@ static constexpr StringRef getGlobalDtorsVarName() {
   return "llvm.global_dtors";
 }
 
-/// Returns the symbol name for the module-level metadata operation. It must not
-/// conflict with the user namespace.
-static constexpr StringRef getGlobalMetadataOpName() {
-  return "__llvm_global_metadata";
-}
-
 /// Returns the symbol name for the module-level comdat operation. It must not
 /// conflict with the user namespace.
 static constexpr StringRef getGlobalComdatOpName() {
@@ -166,18 +160,6 @@ ModuleImport::ModuleImport(ModuleOp mlirModule,
   builder.setInsertionPointToStart(mlirModule.getBody());
 }
 
-MetadataOp ModuleImport::getGlobalMetadataOp() {
-  if (globalMetadataOp)
-    return globalMetadataOp;
-
-  OpBuilder::InsertionGuard guard(builder);
-  builder.setInsertionPointToEnd(mlirModule.getBody());
-  globalMetadataOp = builder.create<MetadataOp>(mlirModule.getLoc(),
-                                                getGlobalMetadataOpName());
-  globalInsertionOp = globalMetadataOp;
-  return globalMetadataOp;
-}
-
 ComdatOp ModuleImport::getGlobalComdatOp() {
   if (globalComdatOp)
     return globalComdatOp;
@@ -192,34 +174,21 @@ ComdatOp ModuleImport::getGlobalComdatOp() {
 
 LogicalResult ModuleImport::processTBAAMetadata(const llvm::MDNode *node) {
   Location loc = mlirModule.getLoc();
-  SmallVector<const llvm::MDNode *> workList;
-  SetVector<const llvm::MDNode *> nodesToConvert;
-  workList.push_back(node);
-  while (!workList.empty()) {
-    const llvm::MDNode *current = workList.pop_back_val();
-    if (tbaaMapping.contains(current))
-      continue;
-    // Allow cycles in TBAA metadata. Just import it as-is,
-    // and diagnose the problem during LLVMIR dialect verification.
-    if (!nodesToConvert.insert(current))
-      continue;
-    for (const llvm::MDOperand &operand : current->operands())
-      if (auto *opNode = dyn_cast_or_null<const llvm::MDNode>(operand.get()))
-        workList.push_back(opNode);
-  }
 
-  // If `node` is a valid TBAA root node, then return its identity
-  // string, otherwise return std::nullopt.
+  // If `node` is a valid TBAA root node, then return its optional identity
+  // string, otherwise return failure.
   auto getIdentityIfRootNode =
-      [&](const llvm::MDNode *node) -> std::optional<StringRef> {
+      [&](const llvm::MDNode *node) -> FailureOr<std::optional<StringRef>> {
     // Root node, e.g.:
     //   !0 = !{!"Simple C/C++ TBAA"}
-    if (node->getNumOperands() != 1)
-      return std::nullopt;
+    //   !1 = !{}
+    if (node->getNumOperands() > 1)
+      return failure();
     // If the operand is MDString, then assume that this is a root node.
-    if (const auto *op0 = dyn_cast<const llvm::MDString>(node->getOperand(0)))
-      return op0->getString();
-    return std::nullopt;
+    if (node->getNumOperands() == 1)
+      if (const auto *op0 = dyn_cast<const llvm::MDString>(node->getOperand(0)))
+        return std::optional<StringRef>{op0->getString()};
+    return std::optional<StringRef>{};
   };
 
   // If `node` looks like a TBAA type descriptor metadata,
@@ -229,11 +198,10 @@ LogicalResult ModuleImport::processTBAAMetadata(const llvm::MDNode *node) {
   // If `identity` and `memberTypes/Offsets` are non-null, then they will
   // contain the converted metadata operands for a valid TBAA node (i.e. when
   // true is returned).
-  auto isTypeDescriptorNode =
-      [&](const llvm::MDNode *node, StringRef *identity = nullptr,
-          SmallVectorImpl<Attribute> *memberTypes = nullptr,
-          SmallVectorImpl<int64_t> *memberOffsets =
-              nullptr) -> std::optional<bool> {
+  auto isTypeDescriptorNode = [&](const llvm::MDNode *node,
+                                  StringRef *identity = nullptr,
+                                  SmallVectorImpl<TBAAMemberAttr> *members =
+                                      nullptr) -> std::optional<bool> {
     unsigned numOperands = node->getNumOperands();
     // Type descriptor, e.g.:
     //   !1 = !{!"int", !0, /*optional*/i64 0} /* scalar int type */
@@ -280,10 +248,9 @@ LogicalResult ModuleImport::processTBAAMetadata(const llvm::MDNode *node) {
         offset = offsetCI->getZExtValue();
       }
 
-      if (memberTypes)
-        memberTypes->push_back(tbaaMapping.lookup(memberNode));
-      if (memberOffsets)
-        memberOffsets->push_back(offset);
+      if (members)
+        members->push_back(TBAAMemberAttr::get(
+            cast<TBAANodeAttr>(tbaaMapping.lookup(memberNode)), offset));
     }
 
     return true;
@@ -296,10 +263,11 @@ LogicalResult ModuleImport::processTBAAMetadata(const llvm::MDNode *node) {
   // If the other arguments are non-null, then they will contain
   // the converted metadata operands for a valid TBAA node (i.e. when true is
   // returned).
-  auto isTagNode =
-      [&](const llvm::MDNode *node, SymbolRefAttr *baseSymRef = nullptr,
-          SymbolRefAttr *accessSymRef = nullptr, int64_t *offset = nullptr,
-          bool *isConstant = nullptr) -> std::optional<bool> {
+  auto isTagNode = [&](const llvm::MDNode *node,
+                       TBAATypeDescriptorAttr *baseAttr = nullptr,
+                       TBAATypeDescriptorAttr *accessAttr = nullptr,
+                       int64_t *offset = nullptr,
+                       bool *isConstant = nullptr) -> std::optional<bool> {
     // Access tag, e.g.:
     //   !3 = !{!1, !1, i64 0} /* scalar int access */
     //   !4 = !{!2, !1, i64 0} /* agg_t::x access */
@@ -335,10 +303,10 @@ LogicalResult ModuleImport::processTBAAMetadata(const llvm::MDNode *node) {
       }
       isConst = isConstantCI->getValue()[0];
     }
-    if (baseSymRef)
-      *baseSymRef = tbaaMapping.lookup(baseMD);
-    if (accessSymRef)
-      *accessSymRef = tbaaMapping.lookup(accessMD);
+    if (baseAttr)
+      *baseAttr = cast<TBAATypeDescriptorAttr>(tbaaMapping.lookup(baseMD));
+    if (accessAttr)
+      *accessAttr = cast<TBAATypeDescriptorAttr>(tbaaMapping.lookup(accessMD));
     if (offset)
       *offset = offsetCI->getZExtValue();
     if (isConstant)
@@ -346,88 +314,84 @@ LogicalResult ModuleImport::processTBAAMetadata(const llvm::MDNode *node) {
     return true;
   };
 
-  // Helper to compute a unique symbol name that includes the given `baseName`.
-  // Uses the size of the mapping to unique the symbol name.
-  auto getUniqueSymbolName = [&](StringRef baseName) {
-    return (Twine("tbaa_") + Twine(baseName) + Twine('_') +
-            Twine(tbaaMapping.size()))
-        .str();
-  };
+  // Do a post-order walk over the TBAA Graph. Since a correct TBAA Graph is a
+  // DAG, a post-order walk guarantees that we convert any metadata node we
+  // depend on, prior to converting the current node.
+  DenseSet<const llvm::MDNode *> seen;
+  SmallVector<const llvm::MDNode *> workList;
+  workList.push_back(node);
+  while (!workList.empty()) {
+    const llvm::MDNode *current = workList.back();
+    if (tbaaMapping.contains(current)) {
+      // Already converted. Just pop from the worklist.
+      workList.pop_back();
+      continue;
+    }
 
-  // Insert new operations at the end of the MetadataOp.
-  OpBuilder::InsertionGuard guard(builder);
-  builder.setInsertionPointToEnd(&getGlobalMetadataOp().getBody().back());
-  StringAttr metadataOpName = SymbolTable::getSymbolName(getGlobalMetadataOp());
-
-  // On the first walk, create SymbolRefAttr's and map them
-  // to nodes in `nodesToConvert`.
-  for (const auto *current : nodesToConvert) {
-    if (std::optional<StringRef> identity = getIdentityIfRootNode(current)) {
-      if (identity.value().empty())
-        return emitError(loc) << "TBAA root node must have non-empty identity: "
+    // If any child of this node is not yet converted, don't pop the current
+    // node from the worklist but push the not-yet-converted children in the
+    // front of the worklist.
+    bool anyChildNotConverted = false;
+    for (const llvm::MDOperand &operand : current->operands())
+      if (auto *childNode = dyn_cast_or_null<const llvm::MDNode>(operand.get()))
+        if (!tbaaMapping.contains(childNode)) {
+          workList.push_back(childNode);
+          anyChildNotConverted = true;
+        }
+
+    if (anyChildNotConverted) {
+      // If this is the second time we failed to convert an element in the
+      // worklist it must be because a child is dependent on it being converted
+      // and we have a cycle in the graph. Cycles are not allowed in TBAA
+      // graphs.
+      if (!seen.insert(current).second)
+        return emitError(loc) << "has cycle in TBAA graph: "
                               << diagMD(current, llvmModule.get());
 
-      // The root nodes do not have operands, so we can create
-      // the TBAARootMetadataOp on the first walk.
-      auto rootNode = builder.create<TBAARootMetadataOp>(
-          loc, getUniqueSymbolName("root"), identity.value());
-      tbaaMapping.try_emplace(current, FlatSymbolRefAttr::get(rootNode));
-      continue;
-    }
-    if (std::optional<bool> isValid = isTypeDescriptorNode(current)) {
-      if (!isValid.value())
-        return failure();
-      tbaaMapping.try_emplace(
-          current, FlatSymbolRefAttr::get(builder.getContext(),
-                                          getUniqueSymbolName("type_desc")));
       continue;
     }
-    if (std::optional<bool> isValid = isTagNode(current)) {
-      if (!isValid.value())
-        return failure();
-      // TBAATagOp symbols must be referred by their fully qualified
-      // names, so create a path to TBAATagOp symbol.
-      tbaaMapping.try_emplace(
-          current, SymbolRefAttr::get(
-                       builder.getContext(), metadataOpName,
-                       FlatSymbolRefAttr::get(builder.getContext(),
-                                              getUniqueSymbolName("tag"))));
+
+    // Otherwise simply import the current node.
+    workList.pop_back();
+
+    FailureOr<std::optional<StringRef>> rootNodeIdentity =
+        getIdentityIfRootNode(current);
+    if (succeeded(rootNodeIdentity)) {
+      StringAttr stringAttr = *rootNodeIdentity
+                                  ? builder.getStringAttr(**rootNodeIdentity)
+                                  : nullptr;
+      // The root nodes do not have operands, so we can create
+      // the TBAARootMetadataOp on the first walk.
+      tbaaMapping.insert({current, builder.getAttr<TBAARootAttr>(stringAttr)});
       continue;
     }
-    return emitError(loc) << "unsupported TBAA node format: "
-                          << diagMD(current, llvmModule.get());
-  }
 
-  // On the second walk, create TBAA operations using the symbol names from the
-  // map.
-  for (const auto *current : nodesToConvert) {
     StringRef identity;
-    SmallVector<Attribute> memberTypes;
-    SmallVector<int64_t> memberOffsets;
-    if (std::optional<bool> isValid = isTypeDescriptorNode(
-            current, &identity, &memberTypes, &memberOffsets)) {
+    SmallVector<TBAAMemberAttr> members;
+    if (std::optional<bool> isValid =
+            isTypeDescriptorNode(current, &identity, &members)) {
       assert(isValid.value() && "type descriptor node must be valid");
 
-      builder.create<TBAATypeDescriptorOp>(
-          loc, tbaaMapping.lookup(current).getLeafReference(),
-          builder.getStringAttr(identity), builder.getArrayAttr(memberTypes),
-          memberOffsets);
+      tbaaMapping.insert({current, builder.getAttr<TBAATypeDescriptorAttr>(
+                                       identity, members)});
       continue;
     }
-    SymbolRefAttr baseSymRef, accessSymRef;
+
+    TBAATypeDescriptorAttr baseAttr, accessAttr;
     int64_t offset;
     bool isConstant;
-    if (std::optional<bool> isValid = isTagNode(
-            current, &baseSymRef, &accessSymRef, &offset, &isConstant)) {
+    if (std::optional<bool> isValid =
+            isTagNode(current, &baseAttr, &accessAttr, &offset, &isConstant)) {
       assert(isValid.value() && "access tag node must be valid");
-      builder.create<TBAATagOp>(
-          loc, tbaaMapping.lookup(current).getLeafReference(),
-          baseSymRef.getLeafReference(), accessSymRef.getLeafReference(),
-          offset, isConstant);
+      tbaaMapping.insert(
+          {current, builder.getAttr<TBAATagAttr>(baseAttr, accessAttr, offset,
+                                                 isConstant)});
       continue;
     }
-  }
 
+    return emitError(loc) << "unsupported TBAA node format: "
+                          << diagMD(current, llvmModule.get());
+  }
   return success();
 }
 

diff  --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index a767f30c9b50d8..d363fb8d91862d 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -1159,14 +1159,8 @@ void ModuleTranslation::setAliasScopeMetadata(AliasAnalysisOpInterface op,
                         llvm::LLVMContext::MD_noalias);
 }
 
-llvm::MDNode *ModuleTranslation::getTBAANode(Operation *op,
-                                             SymbolRefAttr tagRef) const {
-  StringAttr metadataName = tagRef.getRootReference();
-  StringAttr tagName = tagRef.getLeafReference();
-  auto metadataOp = SymbolTable::lookupNearestSymbolFrom<LLVM::MetadataOp>(
-      op->getParentOp(), metadataName);
-  Operation *tagOp = SymbolTable::lookupNearestSymbolFrom(metadataOp, tagName);
-  return tbaaMetadataMapping.lookup(tagOp);
+llvm::MDNode *ModuleTranslation::getTBAANode(TBAATagAttr tbaaAttr) const {
+  return tbaaMetadataMapping.lookup(tbaaAttr);
 }
 
 void ModuleTranslation::setTBAAMetadata(AliasAnalysisOpInterface op,
@@ -1185,8 +1179,7 @@ void ModuleTranslation::setTBAAMetadata(AliasAnalysisOpInterface op,
     return;
   }
 
-  SymbolRefAttr tagRef = cast<SymbolRefAttr>(tagRefs[0]);
-  llvm::MDNode *node = getTBAANode(op, tagRef);
+  llvm::MDNode *node = getTBAANode(cast<TBAATagAttr>(tagRefs[0]));
   inst->setMetadata(llvm::LLVMContext::MD_tbaa, node);
 }
 
@@ -1194,90 +1187,51 @@ LogicalResult ModuleTranslation::createTBAAMetadata() {
   llvm::LLVMContext &ctx = llvmModule->getContext();
   llvm::IntegerType *offsetTy = llvm::IntegerType::get(ctx, 64);
 
-  // Walk TBAA metadata and create MDNode's with placeholder
-  // operands for the references of other TBAA nodes.
-  for (auto metadata : getModuleBody(mlirModule).getOps<LLVM::MetadataOp>()) {
-    for (auto &op : metadata.getBody().getOps()) {
-      SmallVector<llvm::Metadata *> operands;
-      if (auto rootOp = dyn_cast<LLVM::TBAARootMetadataOp>(op)) {
-        operands.push_back(llvm::MDString::get(ctx, rootOp.getIdentity()));
-      } else if (auto tdOp = dyn_cast<LLVM::TBAATypeDescriptorOp>(op)) {
-        operands.push_back(llvm::MDString::get(
-            ctx, tdOp.getIdentity().value_or(llvm::StringRef{})));
-        for (int64_t offset : tdOp.getOffsets()) {
-          // Use temporary MDNode as the placeholder for the member type
-          // to prevent uniquing the type descriptor nodes until they are
-          // finalized.
-          operands.push_back(
-              llvm::MDNode::getTemporary(ctx, std::nullopt).release());
-          operands.push_back(llvm::ConstantAsMetadata::get(
-              llvm::ConstantInt::get(offsetTy, offset)));
-        }
-      } else if (auto tagOp = dyn_cast<LLVM::TBAATagOp>(op)) {
-        // Use temporary MDNode's as the placeholders for the base and access
-        // types to prevent uniquing the tag nodes until they are finalized.
-        operands.push_back(
-            llvm::MDNode::getTemporary(ctx, std::nullopt).release());
-        operands.push_back(
-            llvm::MDNode::getTemporary(ctx, std::nullopt).release());
-        operands.push_back(llvm::ConstantAsMetadata::get(
-            llvm::ConstantInt::get(offsetTy, tagOp.getOffset())));
-        if (tagOp.getConstant())
-          operands.push_back(llvm::ConstantAsMetadata::get(
-              llvm::ConstantInt::get(offsetTy, 1)));
-      }
-
-      if (operands.empty())
-        continue;
+  // Walk the entire module and create all metadata nodes for the TBAA
+  // attributes. The code below relies on two invariants of the
+  // `AttrTypeWalker`:
+  // 1. Attributes are visited in post-order: Since the attributes create a DAG,
+  //    this ensures that any lookups into `tbaaMetadataMapping` for child
+  //    attributes succeed.
+  // 2. Attributes are only ever visited once: This way we don't leak any
+  //    LLVM metadata instances.
+  AttrTypeWalker walker;
+  walker.addWalk([&](TBAARootAttr root) {
+    tbaaMetadataMapping.insert(
+        {root, llvm::MDNode::get(ctx, llvm::MDString::get(ctx, root.getId()))});
+  });
 
-      tbaaMetadataMapping.insert({&op, llvm::MDNode::get(ctx, operands)});
+  walker.addWalk([&](TBAATypeDescriptorAttr descriptor) {
+    SmallVector<llvm::Metadata *> operands;
+    operands.push_back(llvm::MDString::get(ctx, descriptor.getId()));
+    for (TBAAMemberAttr member : descriptor.getMembers()) {
+      operands.push_back(tbaaMetadataMapping.lookup(member.getTypeDesc()));
+      operands.push_back(llvm::ConstantAsMetadata::get(
+          llvm::ConstantInt::get(offsetTy, member.getOffset())));
     }
-  }
 
-  // Walk TBAA metadata second time and update the placeholder
-  // references.
-  for (auto metadata : getModuleBody(mlirModule).getOps<LLVM::MetadataOp>()) {
-    for (auto &op : metadata.getBody().getOps()) {
-      SmallVector<StringRef> refNames;
-      SmallVector<int64_t> operandIndices;
-      if (auto tdOp = dyn_cast<LLVM::TBAATypeDescriptorOp>(op)) {
-        // The type references are in 1, 3, 5, etc. positions.
-        unsigned opNum = 1;
-        for (Attribute typeAttr : tdOp.getMembers()) {
-          refNames.push_back(cast<FlatSymbolRefAttr>(typeAttr).getValue());
-          operandIndices.push_back(opNum);
-          opNum += 2;
-        }
-      } else if (auto tagOp = dyn_cast<LLVM::TBAATagOp>(op)) {
-        refNames.push_back(tagOp.getBaseType());
-        operandIndices.push_back(0);
-        refNames.push_back(tagOp.getAccessType());
-        operandIndices.push_back(1);
-      }
+    tbaaMetadataMapping.insert({descriptor, llvm::MDNode::get(ctx, operands)});
+  });
 
-      if (refNames.empty())
-        continue;
+  walker.addWalk([&](TBAATagAttr tag) {
+    SmallVector<llvm::Metadata *> operands;
 
-      llvm::MDNode *descNode = tbaaMetadataMapping.lookup(&op);
-      for (auto [refName, opNum] : llvm::zip(refNames, operandIndices)) {
-        // refDef availability in the parent MetadataOp
-        // is checked by module verifier.
-        Operation *refDef = SymbolTable::lookupSymbolIn(metadata, refName);
-        llvm::MDNode *refNode = tbaaMetadataMapping.lookup(refDef);
-        if (!refNode) {
-          op.emitOpError() << "llvm::MDNode missing for the member '@"
-                           << refName << "'";
-          return failure();
-        }
-        auto *tempMD = cast<llvm::MDNode>(descNode->getOperand(opNum).get());
-        descNode->replaceOperandWith(opNum, refNode);
-        // Deallocate temporary MDNode's explicitly.
-        // Note that each temporary node has a single use by creation,
-        // so it is valid to deallocate it here.
-        llvm::MDNode::deleteTemporary(tempMD);
-      }
-    }
-  }
+    operands.push_back(tbaaMetadataMapping.lookup(tag.getBaseType()));
+    operands.push_back(tbaaMetadataMapping.lookup(tag.getAccessType()));
+
+    operands.push_back(llvm::ConstantAsMetadata::get(
+        llvm::ConstantInt::get(offsetTy, tag.getOffset())));
+    if (tag.getConstant())
+      operands.push_back(
+          llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(offsetTy, 1)));
+
+    tbaaMetadataMapping.insert({tag, llvm::MDNode::get(ctx, operands)});
+  });
+
+  mlirModule->walk([&](AliasAnalysisOpInterface analysisOpInterface) {
+    if (auto attr = analysisOpInterface.getTBAATagsOrNull())
+      walker.walk(attr);
+  });
 
   return success();
 }

diff  --git a/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir b/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir
index 8924c9f0410d3a..5a58e2098675be 100644
--- a/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir
@@ -1,169 +1,27 @@
 // RUN: mlir-opt -split-input-file -verify-diagnostics %s
 
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64}
-  }
-  llvm.func @tbaa(%arg0: !llvm.ptr) {
-    %0 = llvm.mlir.constant(1 : i8) : i8
-    // expected-error at below {{expected '@tbaa_tag_1' to specify a fully qualified reference}}
-    llvm.store %0, %arg0 {tbaa = [@tbaa_tag_1]} : i8, !llvm.ptr
-    llvm.return
-  }
-}
+#tbaa_root = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+#tbaa_desc = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root, 0>}>
+#tbaa_tag = #llvm.tbaa_tag<access_type = #tbaa_desc, base_type = #tbaa_desc, offset = 0>
+// expected-error at +2 {{invalid kind of attribute specified}}
+// expected-error at below {{failed to parse LLVM_TBAATagAttr parameter 'access_type' which is to be a `TBAATypeDescriptorAttr`}}
+#tbaa_tag2 = #llvm.tbaa_tag<access_type = #tbaa_tag, base_type = #tbaa_desc, offset = 0>
 
 // -----
 
-llvm.func @tbaa(%arg0: !llvm.ptr) {
-  %0 = llvm.mlir.constant(1 : i8) : i8
-  // expected-error at below {{attribute 'tbaa' failed to satisfy constraint: symbol ref array attribute}}
-  llvm.store %0, %arg0 {tbaa = ["sym"]} : i8, !llvm.ptr
-  llvm.return
-}
+#tbaa_root = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+#tbaa_desc = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root, 0>}>
+#tbaa_tag = #llvm.tbaa_tag<access_type = #tbaa_desc, base_type = #tbaa_desc, offset = 0>
+// expected-error at +2 {{invalid kind of attribute specified}}
+// expected-error at below {{failed to parse LLVM_TBAATagAttr parameter 'base_type' which is to be a `TBAATypeDescriptorAttr`}}
+#tbaa_tag2 = #llvm.tbaa_tag<access_type = #tbaa_desc, base_type = #tbaa_tag, offset = 0>
 
 // -----
 
-module {
-  llvm.func @tbaa(%arg0: !llvm.ptr) {
-    %0 = llvm.mlir.constant(1 : i8) : i8
-    // expected-error at below {{expected '@metadata::@group1' to resolve to a llvm.tbaa_tag}}
-    llvm.store %0, %arg0 {tbaa = [@metadata::@group1]} : i8, !llvm.ptr
-    llvm.return
-  }
-  llvm.metadata @metadata {
-    llvm.func @group1()
-  }
-}
-
-// -----
-
-module {
-  llvm.func @tbaa(%arg0: !llvm.ptr) {
-    %0 = llvm.mlir.constant(1 : i8) : i8
-    // expected-error at below {{expected '@metadata::@sym' to be a valid reference}}
-    llvm.store %0, %arg0 {tbaa = [@metadata::@sym]} : i8, !llvm.ptr
-    llvm.return
-  }
-  llvm.metadata @metadata {
-  }
-}
-
-// -----
-
-llvm.func @tbaa(%arg0: !llvm.ptr) {
-  %0 = llvm.mlir.constant(1 : i8) : i8
-  // expected-error at below {{expected '@tbaa::@sym' to reference a metadata op}}
-  llvm.store %0, %arg0 {tbaa = [@tbaa::@sym]} : i8, !llvm.ptr
-  llvm.return
-}
-
-// -----
-
-llvm.func @tbaa() {
-  // expected-error at below {{expects parent op 'llvm.metadata'}}
-  llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-  llvm.return
-}
-
-// -----
-
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-  }
-
-  llvm.func @tbaa() {
-    // expected-error at below {{expects parent op 'llvm.metadata'}}
-    llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}}
-    llvm.return
-  }
-}
-
-// -----
-
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-  }
-
-  llvm.func @tbaa() {
-    // expected-error at below {{expects parent op 'llvm.metadata'}}
-    llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64}
-    llvm.return
-  }
-}
-
-// -----
-
-module {
-  llvm.metadata @__tbaa {
-    // expected-error at below {{expected non-empty "identity"}}
-    llvm.tbaa_root @tbaa_root_0 {id = ""}
-  }
-}
-
-// -----
-
-  "builtin.module"() ({
-    "llvm.metadata"() ({
-      "llvm.tbaa_root"() {identity = "Simple C/C++ TBAA", sym_name = "tbaa_root_0"} : () -> ()
-      "llvm.tbaa_type_desc"() {identity = "omnipotent char", members = [@tbaa_root_0], offsets = array<i64: 0>, sym_name = "tbaa_type_desc_1"} : () -> ()
-    // expected-error at below {{expected the same number of elements in "members" and "offsets": 2 != 1}}
-      "llvm.tbaa_type_desc"() {identity = "agg_t", members = [@tbaa_type_desc_1, @tbaa_type_desc_1], offsets = array<i64: 0>, sym_name = "tbaa_type_desc_2"} : () -> ()
-    }) {sym_name = "__tbaa"} : () -> ()
-  }) : () -> ()
-
-// -----
-
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    // expected-error at below {{expected "base_type" to reference a symbol from 'llvm.metadata @__tbaa' defined by either 'llvm.tbaa_root' or 'llvm.tbaa_type_desc' while it references '@tbaa_root_2'}}
-    llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_2, offset = 0 : i64}
-  }
-}
-
-// -----
-
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    // expected-error at below {{expected "access_type" to reference a symbol from 'llvm.metadata @__tbaa' defined by either 'llvm.tbaa_root' or 'llvm.tbaa_type_desc' while it references '@tbaa_root_2'}}
-    llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_2, base_type = @tbaa_root_0, offset = 0 : i64}
-  }
-}
-
-// -----
-
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}}
-    // expected-error at below {{expected "members" to reference a symbol from 'llvm.metadata @__tbaa' defined by either 'llvm.tbaa_root' or 'llvm.tbaa_type_desc' while it references '@tbaa_type_desc_4'}}
-    llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_4, 8>}}
-  }
-}
-
-// -----
-
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64}
-    // expected-error at below {{expected "access_type" to reference a symbol from 'llvm.metadata @__tbaa' defined by either 'llvm.tbaa_root' or 'llvm.tbaa_type_desc' while it references '@tbaa_tag_1'}}
-    llvm.tbaa_tag @tbaa_tag_2 {access_type = @tbaa_tag_1, base_type = @tbaa_root_0, offset = 0 : i64}
-  }
-}
-
-// -----
-
-module {
-  // expected-error at below {{has cycle in TBAA graph (graph closure: <tbaa_type_desc_2, tbaa_type_desc_1>)}}
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_type_desc_2, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}}
-  }
-}
+#tbaa_root = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+#tbaa_desc = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root, 0>}>
+#tbaa_tag = #llvm.tbaa_tag<access_type = #tbaa_desc, base_type = #tbaa_desc, offset = 0>
+// expected-error at +3 {{invalid kind of attribute specified}}
+// expected-error at +2 {{failed to parse LLVM_TBAAMemberAttr parameter 'typeDesc' which is to be a `TBAANodeAttr`}}
+// expected-error at below {{failed to parse LLVM_TBAATypeDescriptorAttr parameter 'members' which is to be a `::llvm::ArrayRef<TBAAMemberAttr>`}}
+#tbaa_desc2 = #llvm.tbaa_type_desc<id = "long long", members = {<#tbaa_tag, 0>}>

diff  --git a/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir b/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir
index 5bf38e3199114e..472ae609004c00 100644
--- a/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir
@@ -1,99 +1,94 @@
 // RUN: mlir-opt %s | mlir-opt | FileCheck %s
 
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64}
-    llvm.tbaa_root @tbaa_root_2 {id = "Other language TBAA"}
-    llvm.tbaa_tag @tbaa_tag_3 {access_type = @tbaa_root_2, base_type = @tbaa_root_2, offset = 0 : i64}
-  }
-  llvm.func @tbaa1(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
-    %0 = llvm.mlir.constant(1 : i8) : i8
-    llvm.store %0, %arg0 {tbaa = [@__tbaa::@tbaa_tag_1]} : i8, !llvm.ptr
-    llvm.store %0, %arg1 {tbaa = [@__tbaa::@tbaa_tag_3]} : i8, !llvm.ptr
-    llvm.return
-  }
+#tbaa_root_0 = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+#tbaa_root_1 = #llvm.tbaa_root<id = "Other language TBAA">
+#tbaa_root_2 = #llvm.tbaa_root
+#tbaa_type_desc_0 = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root_0, 0>}>
+#tbaa_tag_0 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_0, base_type = #tbaa_type_desc_0, offset = 0>
+#tbaa_type_desc_1 = #llvm.tbaa_type_desc<id = "long long", members = {<#tbaa_type_desc_0, 0>}>
+#tbaa_type_desc_2 = #llvm.tbaa_type_desc<id = "int", members = {<#tbaa_type_desc_0, 0>}>
+#tbaa_type_desc_3 = #llvm.tbaa_type_desc<id = "agg2_t", members = {<#tbaa_type_desc_1, 0>, <#tbaa_type_desc_1, 8>}>
+#tbaa_type_desc_4 = #llvm.tbaa_type_desc<id = "agg1_t", members = {<#tbaa_type_desc_2, 0>, <#tbaa_type_desc_2, 4>}>
+#tbaa_tag_2 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_1, base_type = #tbaa_type_desc_3, offset = 8>
+#tbaa_tag_3 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_2, base_type = #tbaa_type_desc_4, offset = 0>
+#tbaa_type_desc_5 = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root_1, 0>}>
+#tbaa_type_desc_6 = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root_2, 0>}>
+#tbaa_tag_4 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_6, base_type = #tbaa_type_desc_6, offset = 0>
+#tbaa_tag_1 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_5, base_type = #tbaa_type_desc_5, offset = 0>
+
+// CHECK-DAG: #[[$ROOT_0:.*]] = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+// CHECK-DAG: #[[$ROOT_1:.*]] = #llvm.tbaa_root<id = "Other language TBAA">
+// CHECK-DAG: #[[$ROOT_2:.*]] = #llvm.tbaa_root
+// CHECK-NOT: <{{.*}}>
+// CHECK-DAG: #[[$DESC_0:.*]] = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#[[$ROOT_0]], 0>}>
+// CHECK-DAG: #[[$DESC_1:.*]] = #llvm.tbaa_type_desc<id = "long long", members = {<#[[$DESC_0]], 0>}>
+// CHECK-DAG: #[[$DESC_2:.*]] = #llvm.tbaa_type_desc<id = "int", members = {<#[[$DESC_0]], 0>}>
+// CHECK-DAG: #[[$DESC_3:.*]] = #llvm.tbaa_type_desc<id = "agg2_t", members = {<#[[$DESC_1]], 0>, <#[[$DESC_1]], 8>}>
+// CHECK-DAG: #[[$DESC_4:.*]] = #llvm.tbaa_type_desc<id = "agg1_t", members = {<#[[$DESC_2]], 0>, <#[[$DESC_2]], 4>}>
+// CHECK-DAG: #[[$DESC_5:.*]] = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#[[$ROOT_1]], 0>}>
+// CHECK-DAG: #[[$DESC_6:.*]] = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#[[$ROOT_2]], 0>}>
+// CHECK-DAG: #[[$TAG_0:.*]] = #llvm.tbaa_tag<base_type = #[[$DESC_0]], access_type = #[[$DESC_0]], offset = 0>
+// CHECK-DAG: #[[$TAG_1:.*]] = #llvm.tbaa_tag<base_type = #[[$DESC_5]], access_type = #[[$DESC_5]], offset = 0>
+// CHECK-DAG: #[[$TAG_2:.*]] = #llvm.tbaa_tag<base_type = #[[$DESC_3]], access_type = #[[$DESC_1]], offset = 8>
+// CHECK-DAG: #[[$TAG_3:.*]] = #llvm.tbaa_tag<base_type = #[[$DESC_4]], access_type = #[[$DESC_2]], offset = 0>
+// CHECK-DAG: #[[$TAG_4:.*]] = #llvm.tbaa_tag<base_type = #[[$DESC_6]], access_type = #[[$DESC_6]], offset = 0>
+
+llvm.func @tbaa1(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
+  %0 = llvm.mlir.constant(1 : i8) : i8
+  llvm.store %0, %arg0 {tbaa = [#tbaa_tag_0]} : i8, !llvm.ptr
+  llvm.store %0, %arg1 {tbaa = [#tbaa_tag_1]} : i8, !llvm.ptr
+  llvm.return
 }
 
-// CHECK-LABEL:     llvm.metadata @__tbaa {
-// CHECK:             llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-// CHECK:             llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64}
-// CHECK:             llvm.tbaa_root @tbaa_root_2 {id = "Other language TBAA"}
-// CHECK:             llvm.tbaa_tag @tbaa_tag_3 {access_type = @tbaa_root_2, base_type = @tbaa_root_2, offset = 0 : i64}
-// CHECK:           }
 // CHECK:           llvm.func @tbaa1(%[[VAL_0:.*]]: !llvm.ptr, %[[VAL_1:.*]]: !llvm.ptr) {
 // CHECK:             %[[VAL_2:.*]] = llvm.mlir.constant(1 : i8) : i8
-// CHECK:             llvm.store %[[VAL_2]], %[[VAL_0]] {tbaa = [@__tbaa::@tbaa_tag_1]} : i8, !llvm.ptr
-// CHECK:             llvm.store %[[VAL_2]], %[[VAL_1]] {tbaa = [@__tbaa::@tbaa_tag_3]} : i8, !llvm.ptr
+// CHECK:             llvm.store %[[VAL_2]], %[[VAL_0]] {tbaa = [#[[$TAG_0]]]} : i8, !llvm.ptr
+// CHECK:             llvm.store %[[VAL_2]], %[[VAL_1]] {tbaa = [#[[$TAG_1]]]} : i8, !llvm.ptr
 // CHECK:             llvm.return
 // CHECK:           }
 
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_2, 8>}}
-    llvm.tbaa_tag @tbaa_tag_4 {access_type = @tbaa_type_desc_2, base_type = @tbaa_type_desc_3, offset = 8 : i64}
-    llvm.tbaa_type_desc @tbaa_type_desc_5 {id = "int", members = {<@tbaa_type_desc_1, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_6 {id = "agg1_t", members = {<@tbaa_type_desc_5, 0>, <@tbaa_type_desc_5, 4>}}
-    llvm.tbaa_tag @tbaa_tag_7 {access_type = @tbaa_type_desc_5, base_type = @tbaa_type_desc_6, offset = 0 : i64}
-  }
-  llvm.func @tbaa2(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
-    %0 = llvm.mlir.constant(0 : i32) : i32
-    %1 = llvm.mlir.constant(1 : i32) : i32
-    %2 = llvm.getelementptr inbounds %arg1[%0, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (i64, i64)>
-    %3 = llvm.load %2 {tbaa = [@__tbaa::@tbaa_tag_4]} : !llvm.ptr -> i64
-    %4 = llvm.trunc %3 : i64 to i32
-    %5 = llvm.getelementptr inbounds %arg0[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)>
-    llvm.store %4, %5 {tbaa = [@__tbaa::@tbaa_tag_7]} : i32, !llvm.ptr
-    llvm.return
-  }
+llvm.func @tbaa2(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
+  %0 = llvm.mlir.constant(0 : i32) : i32
+  %1 = llvm.mlir.constant(1 : i32) : i32
+  %2 = llvm.getelementptr inbounds %arg1[%0, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (i64, i64)>
+  %3 = llvm.load %2 {tbaa = [#tbaa_tag_2]} : !llvm.ptr -> i64
+  %4 = llvm.trunc %3 : i64 to i32
+  %5 = llvm.getelementptr inbounds %arg0[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)>
+  llvm.store %4, %5 {tbaa = [#tbaa_tag_3]} : i32, !llvm.ptr
+  llvm.return
 }
 
-// CHECK-LABEL:     llvm.metadata @__tbaa {
-// CHECK:             llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-// CHECK:             llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}}
-// CHECK:             llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}}
-// CHECK:             llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_2, 8>}}
-// CHECK:             llvm.tbaa_tag @tbaa_tag_4 {access_type = @tbaa_type_desc_2, base_type = @tbaa_type_desc_3, offset = 8 : i64}
-// CHECK:             llvm.tbaa_type_desc @tbaa_type_desc_5 {id = "int", members = {<@tbaa_type_desc_1, 0>}}
-// CHECK:             llvm.tbaa_type_desc @tbaa_type_desc_6 {id = "agg1_t", members = {<@tbaa_type_desc_5, 0>, <@tbaa_type_desc_5, 4>}}
-// CHECK:             llvm.tbaa_tag @tbaa_tag_7 {access_type = @tbaa_type_desc_5, base_type = @tbaa_type_desc_6, offset = 0 : i64}
-// CHECK:           }
 // CHECK:           llvm.func @tbaa2(%[[VAL_0:.*]]: !llvm.ptr, %[[VAL_1:.*]]: !llvm.ptr) {
 // CHECK:             %[[VAL_2:.*]] = llvm.mlir.constant(0 : i32) : i32
 // CHECK:             %[[VAL_3:.*]] = llvm.mlir.constant(1 : i32) : i32
 // CHECK:             %[[VAL_4:.*]] = llvm.getelementptr inbounds %[[VAL_1]]{{\[}}%[[VAL_2]], 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (i64, i64)>
-// CHECK:             %[[VAL_5:.*]] = llvm.load %[[VAL_4]] {tbaa = [@__tbaa::@tbaa_tag_4]} : !llvm.ptr -> i64
+// CHECK:             %[[VAL_5:.*]] = llvm.load %[[VAL_4]] {tbaa = [#[[$TAG_2]]]} : !llvm.ptr -> i64
 // CHECK:             %[[VAL_6:.*]] = llvm.trunc %[[VAL_5]] : i64 to i32
 // CHECK:             %[[VAL_7:.*]] = llvm.getelementptr inbounds %[[VAL_0]]{{\[}}%[[VAL_2]], 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)>
-// CHECK:             llvm.store %[[VAL_6]], %[[VAL_7]] {tbaa = [@__tbaa::@tbaa_tag_7]} : i32, !llvm.ptr
+// CHECK:             llvm.store %[[VAL_6]], %[[VAL_7]] {tbaa = [#[[$TAG_3]]]} : i32, !llvm.ptr
 // CHECK:             llvm.return
 // CHECK:           }
 
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64}
-    llvm.tbaa_root @tbaa_root_2 {id = "Other language TBAA"}
-    llvm.tbaa_tag @tbaa_tag_3 {access_type = @tbaa_root_2, base_type = @tbaa_root_2, offset = 0 : i64}
-  }
-  llvm.func @tbaa1(%arg0: !llvm.ptr) {
-    %0 = llvm.mlir.constant(1 : i8) : i8
-    llvm.store %0, %arg0 {tbaa = [@__tbaa::@tbaa_tag_1, @__tbaa::@tbaa_tag_3]} : i8, !llvm.ptr
-    llvm.return
-  }
+llvm.func @tbaa3(%arg0: !llvm.ptr) {
+  %0 = llvm.mlir.constant(1 : i8) : i8
+  llvm.store %0, %arg0 {tbaa = [#tbaa_tag_0, #tbaa_tag_1]} : i8, !llvm.ptr
+  llvm.return
 }
 
-// CHECK-LABEL:     llvm.metadata @__tbaa {
-// CHECK:             llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-// CHECK:             llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64}
-// CHECK:             llvm.tbaa_root @tbaa_root_2 {id = "Other language TBAA"}
-// CHECK:             llvm.tbaa_tag @tbaa_tag_3 {access_type = @tbaa_root_2, base_type = @tbaa_root_2, offset = 0 : i64}
+// CHECK:           llvm.func @tbaa3(%[[VAL_0:.*]]: !llvm.ptr) {
+// CHECK:             %[[VAL_1:.*]] = llvm.mlir.constant(1 : i8) : i8
+// CHECK:             llvm.store %[[VAL_1]], %[[VAL_0]] {tbaa = [#[[$TAG_0]], #[[$TAG_1]]]} : i8, !llvm.ptr
+// CHECK:             llvm.return
 // CHECK:           }
-// CHECK:           llvm.func @tbaa1(%[[VAL_0:.*]]: !llvm.ptr) {
+
+llvm.func @tbaa4(%arg0: !llvm.ptr) {
+  %0 = llvm.mlir.constant(1 : i8) : i8
+  llvm.store %0, %arg0 {tbaa = [#tbaa_tag_4]} : i8, !llvm.ptr
+  llvm.return
+}
+
+// CHECK:           llvm.func @tbaa4(%[[VAL_0:.*]]: !llvm.ptr) {
 // CHECK:             %[[VAL_1:.*]] = llvm.mlir.constant(1 : i8) : i8
-// CHECK:             llvm.store %[[VAL_1]], %[[VAL_0]] {tbaa = [@__tbaa::@tbaa_tag_1, @__tbaa::@tbaa_tag_3]} : i8, !llvm.ptr
+// CHECK:             llvm.store %[[VAL_1]], %[[VAL_0]] {tbaa = [#[[$TAG_4]]]} : i8, !llvm.ptr
 // CHECK:             llvm.return
 // CHECK:           }

diff  --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll
index dbb1d10599da45..b41226b75dacc0 100644
--- a/mlir/test/Target/LLVMIR/Import/import-failure.ll
+++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll
@@ -94,29 +94,28 @@ define void @foo() {
 ; // -----
 
 ; CHECK:      import-failure.ll
-; CHECK-SAME: error: TBAA root node must have non-empty identity: !2 = !{!""}
+; CHECK-SAME: error: unsupported TBAA node format: !{{.*}} = !{!{{.*}}, i64 1, !"omnipotent char"}
 define dso_local void @tbaa(ptr %0) {
-  store i8 1, ptr %0, align 4, !tbaa !2
+  store i32 1, ptr %0, align 4, !tbaa !2
   ret void
 }
 
-!0 = !{!""}
-!1 = !{!"omnipotent char", !0, i64 0}
-!2 = !{!1, !1, i64 0}
+!2 = !{!3, !3, i64 0, i64 4}
+!3 = !{!4, i64 4, !"int"}
+!4 = !{!5, i64 1, !"omnipotent char"}
+!5 = !{!"Simple C++ TBAA"}
 
 ; // -----
 
 ; CHECK:      import-failure.ll
-; CHECK-SAME: error: unsupported TBAA node format: !0 = !{!1, !1, i64 0, i64 4}
+; CHECK-SAME: error: has cycle in TBAA graph: ![[ID:.*]] = distinct !{![[ID]], i64 4, !"int"}
 define dso_local void @tbaa(ptr %0) {
   store i32 1, ptr %0, align 4, !tbaa !2
   ret void
 }
 
 !2 = !{!3, !3, i64 0, i64 4}
-!3 = !{!4, i64 4, !"int"}
-!4 = !{!5, i64 1, !"omnipotent char"}
-!5 = !{!"Simple C++ TBAA"}
+!3 = !{!3, i64 4, !"int"}
 
 ; // -----
 

diff  --git a/mlir/test/Target/LLVMIR/Import/metadata-tbaa.ll b/mlir/test/Target/LLVMIR/Import/metadata-tbaa.ll
index 9477063c857986..2a7685c4e02470 100644
--- a/mlir/test/Target/LLVMIR/Import/metadata-tbaa.ll
+++ b/mlir/test/Target/LLVMIR/Import/metadata-tbaa.ll
@@ -1,19 +1,18 @@
 ; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s
 
-; CHECK-LABEL: llvm.metadata @__llvm_global_metadata {
-; CHECK-DAG:    llvm.tbaa_root @[[R0:tbaa_root_[0-9]+]] {id = "Simple C/C++ TBAA"}
-; CHECK-DAG:    llvm.tbaa_type_desc @[[D0:tbaa_type_desc_[0-9]+]] {id = "scalar type", members = {<@[[R0]], 0>}}
-; CHECK-DAG:    llvm.tbaa_tag @[[T0:tbaa_tag_[0-9]+]] {access_type = @[[D0]], base_type = @[[D0]], offset = 0 : i64}
-; CHECK-DAG:    llvm.tbaa_root @[[R1:tbaa_root_[0-9]+]] {id = "Other language TBAA"}
-; CHECK-DAG:    llvm.tbaa_type_desc @[[D1:tbaa_type_desc_[0-9]+]] {id = "other scalar type", members = {<@[[R1]], 0>}}
-; CHECK-DAG:    llvm.tbaa_tag @[[T1:tbaa_tag_[0-9]+]] {access_type = @[[D1]], base_type = @[[D1]], offset = 0 : i64}
-; CHECK-NEXT:  }
-; CHECK:       llvm.func @tbaa1
+; CHECK-DAG: #[[R0:.*]] = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+; CHECK-DAG: #[[D0:.*]] = #llvm.tbaa_type_desc<id = "scalar type", members = {<#[[R0]], 0>}>
+; CHECK-DAG: #[[$T0:.*]] = #llvm.tbaa_tag<base_type = #[[D0]], access_type = #[[D0]], offset = 0>
+; CHECK-DAG: #[[R1:.*]] = #llvm.tbaa_root<id = "Other language TBAA">
+; CHECK-DAG: #[[D1:.*]] = #llvm.tbaa_type_desc<id = "other scalar type", members = {<#[[R1]], 0>}>
+; CHECK-DAG: #[[$T1:.*]] = #llvm.tbaa_tag<base_type = #[[D1]], access_type = #[[D1]], offset = 0>
+
+; CHECK-LABEL: llvm.func @tbaa1
 ; CHECK:         llvm.store %{{.*}}, %{{.*}} {
-; CHECK-SAME:        tbaa = [@__llvm_global_metadata::@[[T0]]]
+; CHECK-SAME:        tbaa = [#[[$T0]]]
 ; CHECK-SAME:    } : i8, !llvm.ptr
 ; CHECK:         llvm.store %{{.*}}, %{{.*}} {
-; CHECK-SAME:        tbaa = [@__llvm_global_metadata::@[[T1]]]
+; CHECK-SAME:        tbaa = [#[[$T1]]]
 ; CHECK-SAME:    } : i8, !llvm.ptr
 define dso_local void @tbaa1(ptr %0, ptr %1) {
   store i8 1, ptr %0, align 4, !tbaa !0
@@ -31,22 +30,21 @@ define dso_local void @tbaa1(ptr %0, ptr %1) {
 
 ; // -----
 
-; CHECK-LABEL: llvm.metadata @__llvm_global_metadata {
-; CHECK-NEXT:    llvm.tbaa_root @[[R0:tbaa_root_[0-9]+]] {id = "Simple C/C++ TBAA"}
-; CHECK-NEXT:    llvm.tbaa_tag @[[T0:tbaa_tag_[0-9]+]] {access_type = @[[D1:tbaa_type_desc_[0-9]+]], base_type = @[[D2:tbaa_type_desc_[0-9]+]], offset = 8 : i64}
-; CHECK-NEXT:    llvm.tbaa_type_desc @[[D1]] {id = "long long", members = {<@[[D0:tbaa_type_desc_[0-9]+]], 0>}}
-; CHECK-NEXT:    llvm.tbaa_type_desc @[[D0]] {id = "omnipotent char", members = {<@[[R0]], 0>}}
-; CHECK-NEXT:    llvm.tbaa_type_desc @[[D2]] {id = "agg2_t", members = {<@[[D1]], 0>, <@[[D1]], 8>}}
-; CHECK-NEXT:    llvm.tbaa_tag @[[T1:tbaa_tag_[0-9]+]] {access_type = @[[D3:tbaa_type_desc_[0-9]+]], base_type = @[[D4:tbaa_type_desc_[0-9]+]], offset = 0 : i64}
-; CHECK-NEXT:    llvm.tbaa_type_desc @[[D3]] {id = "int", members = {<@[[D0]], 0>}}
-; CHECK-NEXT:    llvm.tbaa_type_desc @[[D4]] {id = "agg1_t", members = {<@[[D3]], 0>, <@[[D3]], 4>}}
-; CHECK-NEXT:  }
-; CHECK:       llvm.func @tbaa2
+; CHECK-DAG: #[[R0:.*]] = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+; CHECK-DAG: #[[$T0:.*]] = #llvm.tbaa_tag<base_type = #[[D2:.*]], access_type = #[[D1:.*]], offset = 8>
+; CHECK-DAG: #[[D1]] = #llvm.tbaa_type_desc<id = "long long", members = {<#[[D0:.*]], 0>}>
+; CHECK-DAG: #[[D0]] = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#[[R0]], 0>}>
+; CHECK-DAG: #[[D2]] = #llvm.tbaa_type_desc<id = "agg2_t", members = {<#[[D1]], 0>, <#[[D1]], 8>}>
+; CHECK-DAG: #[[$T1:.*]] = #llvm.tbaa_tag<base_type = #[[D4:.*]], access_type = #[[D3:.*]], offset = 0>
+; CHECK-DAG: #[[D3]] = #llvm.tbaa_type_desc<id = "int", members = {<#[[D0]], 0>}>
+; CHECK-DAG: #[[D4]] = #llvm.tbaa_type_desc<id = "agg1_t", members = {<#[[D3]], 0>, <#[[D3]], 4>}>
+
+; CHECK-LABEL: llvm.func @tbaa2
 ; CHECK:         llvm.load %{{.*}} {
-; CHECK-SAME:        tbaa = [@__llvm_global_metadata::@[[T0]]]
+; CHECK-SAME:        tbaa = [#[[$T0]]]
 ; CHECK-SAME:    } : !llvm.ptr -> i64
 ; CHECK:         llvm.store %{{.*}}, %{{.*}} {
-; CHECK-SAME:        tbaa = [@__llvm_global_metadata::@[[T1]]]
+; CHECK-SAME:        tbaa = [#[[$T1]]]
 ; CHECK-SAME:    } : i32, !llvm.ptr
 %struct.agg2_t = type { i64, i64 }
 %struct.agg1_t = type { i32, i32 }
@@ -97,3 +95,20 @@ declare void @foo(ptr %arg1)
 !0 = !{!1, !1, i64 0}
 !1 = !{!"scalar type", !2, i64 0}
 !2 = !{!"Simple C/C++ TBAA"}
+
+; // -----
+
+; CHECK: #llvm.tbaa_root
+; CHECK-NOT: <{{.*}}>
+; CHECK: {{[[:space:]]}}
+
+define void @nameless_root(ptr %arg1) {
+  ; CHECK: llvm.load {{.*}}tbaa =
+  %1 = load i32, ptr %arg1, !tbaa !0
+  ret void
+}
+
+!0 = !{!1, !1, i64 0}
+!1 = !{!"scalar type", !2, i64 0}
+!2 = !{}
+

diff  --git a/mlir/test/Target/LLVMIR/tbaa.mlir b/mlir/test/Target/LLVMIR/tbaa.mlir
index 9128599ab04b58..69b666a1ec3538 100644
--- a/mlir/test/Target/LLVMIR/tbaa.mlir
+++ b/mlir/test/Target/LLVMIR/tbaa.mlir
@@ -1,28 +1,25 @@
 // RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s
 
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_2, 8>}}
-    llvm.tbaa_tag @tbaa_tag_4 {access_type = @tbaa_type_desc_2, base_type = @tbaa_type_desc_3, offset = 8 : i64}
-    llvm.tbaa_type_desc @tbaa_type_desc_5 {id = "int", members = {<@tbaa_type_desc_1, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_6 {id = "agg1_t", members = {<@tbaa_type_desc_5, 0>, <@tbaa_type_desc_5, 4>}}
-    llvm.tbaa_tag @tbaa_tag_7 {access_type = @tbaa_type_desc_5, base_type = @tbaa_type_desc_6, offset = 0 : i64, constant}
-  }
-  llvm.func @tbaa2(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
-    %0 = llvm.mlir.constant(0 : i32) : i32
-    %1 = llvm.mlir.constant(1 : i32) : i32
-    %2 = llvm.getelementptr inbounds %arg1[%0, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (i64, i64)>
-    // CHECK: load i64, ptr %{{.*}},{{.*}}!tbaa ![[LTAG:[0-9]*]]
-    %3 = llvm.load %2 {tbaa = [@__tbaa::@tbaa_tag_4]} : !llvm.ptr -> i64
-    %4 = llvm.trunc %3 : i64 to i32
-    %5 = llvm.getelementptr inbounds %arg0[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)>
-    // CHECK: store i32 %{{.*}}, ptr %{{.*}},{{.*}}!tbaa ![[STAG:[0-9]*]]
-    llvm.store %4, %5 {tbaa = [@__tbaa::@tbaa_tag_7]} : i32, !llvm.ptr
-    llvm.return
-  }
+#tbaa_root_0 = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+#tbaa_type_desc_1 = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root_0, 0>}>
+#tbaa_type_desc_2 = #llvm.tbaa_type_desc<id = "long long", members = {<#tbaa_type_desc_1, 0>}>
+#tbaa_type_desc_3 = #llvm.tbaa_type_desc<id = "agg2_t", members = {<#tbaa_type_desc_2, 0>, <#tbaa_type_desc_2, 8>}>
+#tbaa_tag_4 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_2, base_type = #tbaa_type_desc_3, offset = 8>
+#tbaa_type_desc_5 = #llvm.tbaa_type_desc<id = "int", members = {<#tbaa_type_desc_1, 0>}>
+#tbaa_type_desc_6 = #llvm.tbaa_type_desc<id = "agg1_t", members = {<#tbaa_type_desc_5, 0>, <#tbaa_type_desc_5, 4>}>
+#tbaa_tag_7 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_5, base_type = #tbaa_type_desc_6, offset = 0, constant = true>
+
+llvm.func @tbaa2(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
+  %0 = llvm.mlir.constant(0 : i32) : i32
+  %1 = llvm.mlir.constant(1 : i32) : i32
+  %2 = llvm.getelementptr inbounds %arg1[%0, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (i64, i64)>
+  // CHECK: load i64, ptr %{{.*}},{{.*}}!tbaa ![[LTAG:[0-9]*]]
+  %3 = llvm.load %2 {tbaa = [#tbaa_tag_4]} : !llvm.ptr -> i64
+  %4 = llvm.trunc %3 : i64 to i32
+  %5 = llvm.getelementptr inbounds %arg0[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)>
+  // CHECK: store i32 %{{.*}}, ptr %{{.*}},{{.*}}!tbaa ![[STAG:[0-9]*]]
+  llvm.store %4, %5 {tbaa = [#tbaa_tag_7]} : i32, !llvm.ptr
+  llvm.return
 }
 
 // CHECK-DAG: ![[LTAG]] = !{![[AGG2T:[0-9]*]], ![[I64T:[0-9]*]], i64 8}
@@ -39,48 +36,48 @@ module {
 // Verify that the MDNode's created for the access tags are not uniqued
 // before they are finalized. In the process of creating the MDNodes for
 // the tag operations we used to produce incomplete MDNodes like:
-//   @__tbaa::@tbaa_tag_4 => !{!null, !null, i64 0}
-//   @__tbaa::@tbaa_tag_7 => !{!null, !null, i64 0}
+//   #tbaa_tag_4 => !{!null, !null, i64 0}
+//   #tbaa_tag_7 => !{!null, !null, i64 0}
 // This caused the two tags to map to the same incomplete MDNode due to
 // uniquing. To prevent this, we have to use temporary MDNodes
 // instead of !null's.
-module {
-  llvm.metadata @__tbaa {
-    llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"}
-    llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "float", members = {<@tbaa_type_desc_1, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_2, 4>}}
-    llvm.tbaa_tag @tbaa_tag_4 {access_type = @tbaa_type_desc_2, base_type = @tbaa_type_desc_3, offset = 0 : i64}
-    llvm.tbaa_type_desc @tbaa_type_desc_5 {id = "int", members = {<@tbaa_type_desc_1, 0>}}
-    llvm.tbaa_type_desc @tbaa_type_desc_6 {id = "agg1_t", members = {<@tbaa_type_desc_5, 0>, <@tbaa_type_desc_5, 4>}}
-    llvm.tbaa_tag @tbaa_tag_7 {access_type = @tbaa_type_desc_5, base_type = @tbaa_type_desc_6, offset = 0 : i64}
-  }
-  llvm.func @foo(%arg0: !llvm.ptr)
-  llvm.func @tbaa2(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
-    %0 = llvm.mlir.constant(0 : i32) : i32
-    %1 = llvm.mlir.constant(1 : i32) : i32
-    %2 = llvm.getelementptr inbounds %arg1[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (f32, f32)>
-    // CHECK: load float, ptr %{{.*}},{{.*}}!tbaa ![[LTAG:[0-9]*]]
-    %3 = llvm.load %2 {tbaa = [@__tbaa::@tbaa_tag_4]} : !llvm.ptr -> f32
-    %4 = llvm.fptosi %3 : f32 to i32
-    %5 = llvm.getelementptr inbounds %arg0[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)>
-    // CHECK: store i32 %{{.*}}, ptr %{{.*}},{{.*}}!tbaa ![[STAG:[0-9]*]]
-    llvm.store %4, %5 {tbaa = [@__tbaa::@tbaa_tag_7]} : i32, !llvm.ptr
-    // CHECK: atomicrmw add ptr %{{.*}}, i32 %{{.*}} !tbaa ![[STAG]]
-    %6 = llvm.atomicrmw add %5, %4 monotonic {tbaa = [@__tbaa::@tbaa_tag_7]} : !llvm.ptr, i32
-    // CHECK: cmpxchg ptr %{{.*}}, i32 %{{.*}}, i32 %{{.*}} !tbaa ![[STAG]]
-    %7 = llvm.cmpxchg %5, %6, %4 acq_rel monotonic {tbaa = [@__tbaa::@tbaa_tag_7]} : !llvm.ptr, i32
-    %9 = llvm.mlir.constant(42 : i8) : i8
-    // CHECK: llvm.memcpy{{.*}} !tbaa ![[STAG]]
-    "llvm.intr.memcpy"(%arg1, %arg1, %0) <{isVolatile = false}> {tbaa = [@__tbaa::@tbaa_tag_7]} : (!llvm.ptr, !llvm.ptr, i32) -> ()
-    // CHECK: llvm.memset{{.*}} !tbaa ![[STAG]]
-    "llvm.intr.memset"(%arg1, %9, %0) <{isVolatile = false}> {tbaa = [@__tbaa::@tbaa_tag_7]} : (!llvm.ptr, i8, i32) -> ()
-    // CHECK: call void @foo({{.*}} !tbaa ![[STAG]]
-    llvm.call @foo(%arg1) {tbaa = [@__tbaa::@tbaa_tag_7]} : (!llvm.ptr) -> ()
-    llvm.return
-  }
+
+#tbaa_root_0 = #llvm.tbaa_root<id = "Simple C/C++ TBAA">
+#tbaa_type_desc_1 = #llvm.tbaa_type_desc<id = "omnipotent char", members = {<#tbaa_root_0, 0>}>
+#tbaa_type_desc_2 = #llvm.tbaa_type_desc<id = "float", members = {<#tbaa_type_desc_1, 0>}>
+#tbaa_type_desc_3 = #llvm.tbaa_type_desc<id = "agg2_t", members = {<#tbaa_type_desc_2, 0>, <#tbaa_type_desc_2, 4>}>
+#tbaa_tag_4 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_2, base_type = #tbaa_type_desc_3, offset = 0>
+#tbaa_type_desc_5 = #llvm.tbaa_type_desc<id = "int", members = {<#tbaa_type_desc_1, 0>}>
+#tbaa_type_desc_6 = #llvm.tbaa_type_desc<id = "agg1_t", members = {<#tbaa_type_desc_5, 0>, <#tbaa_type_desc_5, 4>}>
+#tbaa_tag_7 = #llvm.tbaa_tag<access_type = #tbaa_type_desc_5, base_type = #tbaa_type_desc_6, offset = 0>
+
+
+llvm.func @foo(%arg0: !llvm.ptr)
+llvm.func @tbaa2(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
+  %0 = llvm.mlir.constant(0 : i32) : i32
+  %1 = llvm.mlir.constant(1 : i32) : i32
+  %2 = llvm.getelementptr inbounds %arg1[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (f32, f32)>
+  // CHECK: load float, ptr %{{.*}},{{.*}}!tbaa ![[LTAG:[0-9]*]]
+  %3 = llvm.load %2 {tbaa = [#tbaa_tag_4]} : !llvm.ptr -> f32
+  %4 = llvm.fptosi %3 : f32 to i32
+  %5 = llvm.getelementptr inbounds %arg0[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)>
+  // CHECK: store i32 %{{.*}}, ptr %{{.*}},{{.*}}!tbaa ![[STAG:[0-9]*]]
+  llvm.store %4, %5 {tbaa = [#tbaa_tag_7]} : i32, !llvm.ptr
+  // CHECK: atomicrmw add ptr %{{.*}}, i32 %{{.*}} !tbaa ![[STAG]]
+  %6 = llvm.atomicrmw add %5, %4 monotonic {tbaa = [#tbaa_tag_7]} : !llvm.ptr, i32
+  // CHECK: cmpxchg ptr %{{.*}}, i32 %{{.*}}, i32 %{{.*}} !tbaa ![[STAG]]
+  %7 = llvm.cmpxchg %5, %6, %4 acq_rel monotonic {tbaa = [#tbaa_tag_7]} : !llvm.ptr, i32
+  %9 = llvm.mlir.constant(42 : i8) : i8
+  // CHECK: llvm.memcpy{{.*}} !tbaa ![[STAG]]
+  "llvm.intr.memcpy"(%arg1, %arg1, %0) <{isVolatile = false}> {tbaa = [#tbaa_tag_7]} : (!llvm.ptr, !llvm.ptr, i32) -> ()
+  // CHECK: llvm.memset{{.*}} !tbaa ![[STAG]]
+  "llvm.intr.memset"(%arg1, %9, %0) <{isVolatile = false}> {tbaa = [#tbaa_tag_7]} : (!llvm.ptr, i8, i32) -> ()
+  // CHECK: call void @foo({{.*}} !tbaa ![[STAG]]
+  llvm.call @foo(%arg1) {tbaa = [#tbaa_tag_7]} : (!llvm.ptr) -> ()
+  llvm.return
 }
 
+
 // CHECK-DAG: ![[LTAG]] = !{![[AGG2T:[0-9]*]], ![[F32T:[0-9]*]], i64 0}
 // CHECK-DAG: ![[AGG2T]] = !{!"agg2_t", ![[F32T]], i64 0, ![[F32T]], i64 4}
 // CHECK-DAG: ![[I64T]] = !{!"float", ![[CHART:[0-9]*]], i64 0}

diff  --git a/mlir/test/mlir-tblgen/llvm-intrinsics.td b/mlir/test/mlir-tblgen/llvm-intrinsics.td
index 6c2ef4eb811e7b..ed2fcf19207974 100644
--- a/mlir/test/mlir-tblgen/llvm-intrinsics.td
+++ b/mlir/test/mlir-tblgen/llvm-intrinsics.td
@@ -74,7 +74,7 @@
 // It has alias scopes, noalias, and tbaa.
 // ALIAS: OptionalAttr<LLVM_AliasScopeArrayAttr>:$alias_scopes
 // ALIAS: OptionalAttr<LLVM_AliasScopeArrayAttr>:$noalias_scopes
-// ALIAS: OptionalAttr<SymbolRefArrayAttr>:$tbaa
+// ALIAS: OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa
 
 //---------------------------------------------------------------------------//
 

diff  --git a/mlir/tools/mlir-tblgen/LLVMIRIntrinsicGen.cpp b/mlir/tools/mlir-tblgen/LLVMIRIntrinsicGen.cpp
index f9b4031f22cece..2c6aa32e2fe2df 100644
--- a/mlir/tools/mlir-tblgen/LLVMIRIntrinsicGen.cpp
+++ b/mlir/tools/mlir-tblgen/LLVMIRIntrinsicGen.cpp
@@ -223,7 +223,7 @@ static bool emitIntrinsic(const llvm::Record &record, llvm::raw_ostream &os) {
     operands.push_back("OptionalAttr<LLVM_AliasScopeArrayAttr>:$alias_scopes");
     operands.push_back(
         "OptionalAttr<LLVM_AliasScopeArrayAttr>:$noalias_scopes");
-    operands.push_back("OptionalAttr<SymbolRefArrayAttr>:$tbaa");
+    operands.push_back("OptionalAttr<LLVM_TBAATagArrayAttr>:$tbaa");
   }
 
   // Emit the definition.


        


More information about the flang-commits mailing list