[Mlir-commits] [mlir] 2f66c89 - [mlir] Support TBAA metadata in LLVMIR dialect.

Slava Zakharin llvmlistbot at llvm.org
Fri Jan 6 11:17:39 PST 2023


Author: Slava Zakharin
Date: 2023-01-06T11:16:31-08:00
New Revision: 2f66c891307cbf1e506b04ac2f7add4dbd64c9c5

URL: https://github.com/llvm/llvm-project/commit/2f66c891307cbf1e506b04ac2f7add4dbd64c9c5
DIFF: https://github.com/llvm/llvm-project/commit/2f66c891307cbf1e506b04ac2f7add4dbd64c9c5.diff

LOG: [mlir] Support TBAA metadata in LLVMIR dialect.

This change introduces new LLVMIR dialect operations to represent
TBAA root, type descriptor and access tag metadata nodes.

For the purpose of importing TBAA metadata from LLVM IR it only
supports the current version of TBAA format described in
https://llvm.org/docs/LangRef.html#tbaa-metadata (i.e. size-aware
representation introduced in D41501 is not supported).

TBAA attribute support is only added for LLVM::LoadOp and LLVM::StoreOp.
Support for intrinsics operations (e.g. LLVM::MemcpyOp) may be added later.

The TBAA attribute is represented as an array of access tags, though,
LLVM IR supports only single access tag per memory accessing instruction.
I implemented it as an array anticipating similar support in LLVM IR
to combine TBAA graphs with different roots for Flang - one of the options
described in https://docs.google.com/document/d/16kKZVmI585wth01VSaJAqZMZpoX68rcdBmgfj0kNAt0/edit#heading=h.jzzheaz9vqac

It should be easy to restrict MLIR operation to a single access tag,
if we end up using a different approach for Flang.

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

Added: 
    mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir
    mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir
    mlir/test/Target/LLVMIR/Import/tbaa.ll
    mlir/test/Target/LLVMIR/tbaa.mlir

Modified: 
    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/LLVMDialect.cpp
    mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
    mlir/lib/Target/LLVMIR/ModuleImport.cpp
    mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
    mlir/test/Target/LLVMIR/Import/import-failure.ll

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
index c85df7ddacbae..80f82650a10f8 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
@@ -52,6 +52,7 @@ def LLVM_Dialect : Dialect {
     static StringRef getNoUndefAttrName() { return "llvm.noundef"; }
     static StringRef getSExtAttrName() { return "llvm.signext"; }
     static StringRef getZExtAttrName() { return "llvm.zeroext"; }
+    static StringRef getTBAAAttrName() { return "llvm.tbaa"; }
 
     /// Verifies if the attribute is a well-formed value for "llvm.struct_attrs"
     static LogicalResult verifyStructAttr(

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 94e5d9b5aeb2f..ca4cc4853543d 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -205,6 +205,10 @@ class MemoryOpWithAlignmentAndAttributes : MemoryOpWithAlignmentBase {
   code setAliasScopeMetadataCode = [{
     moduleTranslation.setAliasScopeMetadata(op, inst);
   }];
+
+  code setTBAAMetadataCode = [{
+    moduleTranslation.setTBAAMetadata(op, inst);
+  }];
 }
 
 // Memory-related operations.
@@ -360,6 +364,7 @@ def LLVM_LoadOp : LLVM_Op<"load">, MemoryOpWithAlignmentAndAttributes {
      # setNonTemporalMetadataCode
      # setAccessGroupsMetadataCode
      # setAliasScopeMetadataCode
+     # setTBAAMetadataCode
      # [{
     $res = inst;
   }];
@@ -397,7 +402,8 @@ def LLVM_StoreOp : LLVM_Op<"store">, MemoryOpWithAlignmentAndAttributes {
   }] # setAlignmentCode
      # setNonTemporalMetadataCode
      # setAccessGroupsMetadataCode
-     # setAliasScopeMetadataCode;
+     # setAliasScopeMetadataCode
+     # setTBAAMetadataCode;
   // FIXME: Import attributes.
   string mlirBuilder = [{
     $_op = $_builder.create<LLVM::StoreOp>($_location, $value, $addr);
@@ -992,6 +998,7 @@ def LLVM_MetadataOp : LLVM_Op<"metadata", [
   }];
   let regions = (region SizedRegion<1>:$body);
   let assemblyFormat = "$sym_name attr-dict-with-keyword $body";
+  let hasRegionVerifier = 1;
 }
 
 def LLVM_AliasScopeDomainMetadataOp : LLVM_Op<"alias_scope_domain", [
@@ -1074,6 +1081,152 @@ def LLVM_AccessGroupMetadataOp : LLVM_Op<"access_group", [
   let assemblyFormat = "$sym_name attr-dict";
 }
 
+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:
+      llvm.metadata @tbaa {
+        llvm.tbaa_root @tbaa_root_0 {identity = "Simple C/C++ TBAA"}
+        llvm.return
+      }
+
+    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:
+      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>
+        }
+        llvm.return
+      }
+
+    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:
+      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
+        }
+        llvm.return
+      }
+
+    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",
     [IsolatedFromAbove, SingleBlockImplicitTerminator<"ReturnOp">, Symbol]> {
   let arguments = (ins

diff  --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
index 05094ddc75462..5dc1f23ce26e7 100644
--- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
+++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
@@ -149,6 +149,17 @@ class ModuleImport {
   /// implement the fastmath interface.
   void setFastmathFlagsAttr(llvm::Instruction *inst, Operation *op) const;
 
+  /// Converts LLVM metadata to corresponding MLIR representation,
+  /// e.g. metadata nodes referenced via !tbaa are converted to
+  /// TBAA operations hosted inside a MetadataOp.
+  LogicalResult convertMetadata();
+
+  /// Returns SymbolRefAttr representing TBAA metadata `node`
+  /// in `tbaaMapping`.
+  SymbolRefAttr lookupTBAAAttr(const llvm::MDNode *node) {
+    return tbaaMapping.lookup(node);
+  }
+
 private:
   /// Clears the block and value mapping before processing a new region.
   void clearBlockAndValueMapping() {
@@ -219,6 +230,25 @@ class ModuleImport {
   /// them fails. All operations are inserted at the start of the current
   /// function entry block.
   FailureOr<Value> convertConstantExpr(llvm::Constant *constant);
+  /// Returns symbol name to be used for MetadataOp containing
+  /// TBAA metadata operations. It must not conflict with the user
+  /// name space.
+  StringRef getTBAAMetadataOpName() const { return "__tbaa"; }
+  /// Returns a terminated MetadataOp into which TBAA metadata
+  /// operations can be placed. The MetadataOp is created
+  /// on the first invocation of this function.
+  MetadataOp getTBAAMetadataOp();
+  /// Performs conversion of LLVM TBAA metadata starting from
+  /// `node`. On exit from this function all nodes reachable
+  /// from `node` are converted, and tbaaMapping map is updated
+  /// (unless all dependencies have been converted by a previous
+  /// invocation of this function).
+  LogicalResult processTBAAMetadata(const llvm::MDNode *node);
+  /// Returns unique string name of a symbol that may be used
+  /// for a TBAA metadata operation. The name will contain
+  /// the provided `basename` and will be uniqued via
+  /// tbaaNodeCounter (see below).
+  std::string getNewTBAANodeName(StringRef basename);
 
   /// Builder pointing at where the next instruction should be generated.
   OpBuilder builder;
@@ -251,6 +281,16 @@ class ModuleImport {
   LLVM::TypeFromLLVMIRTranslator typeTranslator;
   /// Stateful debug information importer.
   std::unique_ptr<detail::DebugImporter> debugImporter;
+  /// A terminated MetadataOp where TBAA metadata operations
+  /// can be inserted.
+  MetadataOp tbaaMetadataOp{};
+  /// Mapping between LLVM TBAA metadata nodes and symbol references
+  /// to the LLVMIR dialect TBAA operations corresponding to these
+  /// nodes.
+  DenseMap<const llvm::MDNode *, SymbolRefAttr> tbaaMapping;
+  /// A counter to be used as a unique suffix for symbols
+  /// defined by TBAA operations.
+  unsigned tbaaNodeCounter = 0;
 };
 
 } // namespace LLVM

diff  --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
index af84f8a2d7f67..33f8ab7d066df 100644
--- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
+++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
@@ -148,6 +148,10 @@ class ModuleTranslation {
   // Sets LLVM metadata for memory operations that have alias scope information.
   void setAliasScopeMetadata(Operation *op, llvm::Instruction *inst);
 
+  /// Sets LLVM TBAA metadata for memory operations that have
+  /// TBAA attributes.
+  void setTBAAMetadata(Operation *op, llvm::Instruction *inst);
+
   /// Converts the type from MLIR LLVM dialect to LLVM.
   llvm::Type *convertType(Type type);
 
@@ -291,6 +295,14 @@ class ModuleTranslation {
   /// metadata nodes for them and their domains.
   LogicalResult createAliasScopeMetadata();
 
+  /// Returns the LLVM metadata corresponding to a reference to an mlir LLVM
+  /// dialect TBAATagOp operation.
+  llvm::MDNode *getTBAANode(Operation &memOp, SymbolRefAttr tagRef) const;
+
+  /// Process tbaa LLVM Metadata operations and create LLVM
+  /// metadata nodes for them.
+  LogicalResult createTBAAMetadata();
+
   /// Translates dialect attributes attached to the given operation.
   LogicalResult convertDialectAttributes(Operation *op);
 
@@ -333,10 +345,14 @@ class ModuleTranslation {
   /// attribute.
   DenseMap<Attribute, llvm::MDNode *> loopOptionsMetadataMapping;
 
-  /// Mapping from an access scope metadata operation to its LLVM metadata.
+  /// Mapping from an alias scope metadata operation to its LLVM metadata.
   /// This map is populated on module entry.
   DenseMap<Operation *, llvm::MDNode *> aliasScopeMetadataMapping;
 
+  /// Mapping from a tbaa metadata operation to its LLVM metadata.
+  /// This map is populated on module entry.
+  DenseMap<const Operation *, llvm::MDNode *> tbaaMetadataMapping;
+
   /// Stack of user-specified state elements, useful when translating operations
   /// with regions.
   SmallVector<std::unique_ptr<StackFrame>> stack;

diff  --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index f77251b1257e9..58abf02d7a3ed 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -22,6 +22,7 @@
 #include "mlir/IR/Matchers.h"
 #include "mlir/Transforms/InliningUtils.h"
 
+#include "llvm/ADT/SCCIterator.h"
 #include "llvm/ADT/TypeSwitch.h"
 #include "llvm/AsmParser/Parser.h"
 #include "llvm/Bitcode/BitcodeReader.h"
@@ -681,8 +682,18 @@ LogicalResult verifySymbolAttribute(
     llvm::function_ref<LogicalResult(Operation *, SymbolRefAttr)>
         verifySymbolType) {
   if (Attribute attribute = op->getAttr(attributeName)) {
-    // The attribute is already verified to be a symbol ref array attribute via
-    // a constraint in the operation definition.
+    // 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 (!(attribute.isa<ArrayAttr>() &&
+          llvm::all_of(attribute.cast<ArrayAttr>(), [&](Attribute attr) {
+            return attr && attr.isa<SymbolRefAttr>();
+          })))
+      return op->emitOpError("attribute '")
+             << attributeName
+             << "' failed to satisfy constraint: symbol ref array attribute";
+
     for (SymbolRefAttr symbolRef :
          attribute.cast<ArrayAttr>().getAsRange<SymbolRefAttr>()) {
       StringAttr metadataName = symbolRef.getRootReference();
@@ -742,6 +753,11 @@ static LogicalResult verifyMemoryOpMetadata(Operation *op) {
           op, LLVMDialect::getNoAliasScopesAttrName())))
     return failure();
 
+  // tbaa
+  if (failed(verifyOpMetadata<LLVM::TBAATagOp>(op,
+                                               LLVMDialect::getTBAAAttrName())))
+    return failure();
+
   return success();
 }
 
@@ -2566,6 +2582,217 @@ OpFoldResult LLVM::GEPOp::fold(ArrayRef<Attribute> operands) {
   return {};
 }
 
+//===----------------------------------------------------------------------===//
+// Utilities for LLVM::MetadataOp
+//===----------------------------------------------------------------------===//
+
+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 {
+  // 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;
+
+public:
+  using iterator = SmallVectorImpl<TBAAGraphNode *>::iterator;
+
+  iterator begin() { return root.operands.begin(); }
+  iterator end() { return root.operands.end(); }
+  TBAAGraphNode *getEntryNode() { return &root; }
+
+  // Add new graph node corresponding to `symbol`
+  // defined by a TBAA operation.
+  void addNodeDefinition(StringAttr symbol) {
+    TBAAGraphNode &node = nodeMap[symbol];
+    assert(node.symbol.empty() && "node is already in the graph");
+    node.symbol = symbol;
+    root.operands.push_back(&node);
+  }
+
+  // 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;
+  }
+};
+} // 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;
+  // 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;
+
+  for (Operation &op : body.getOps())
+    if (isa<LLVM::TBAARootMetadataOp>(op) ||
+        isa<LLVM::TBAATypeDescriptorOp>(op)) {
+      StringAttr symbolDef = cast<SymbolOpInterface>(op).getNameAttr();
+      definedGraphSymbols.insert(symbolDef);
+      tbaaGraph.addNodeDefinition(symbolDef);
+    } else if (auto tagOp = dyn_cast<LLVM::TBAATagOp>(op)) {
+      tbaaGraph.addNodeDefinition(tagOp.getSymNameAttr());
+    }
+
+  // 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 = attr.cast<FlatSymbolRefAttr>().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
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index faa580cd1122b..aed832173ef02 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -69,8 +69,8 @@ static LogicalResult convertIntrinsicImpl(OpBuilder &odsBuilder,
 /// dialect attributes.
 static ArrayRef<unsigned> getSupportedMetadataImpl() {
   static const SmallVector<unsigned> convertibleMetadata = {
-      llvm::LLVMContext::MD_prof // profiling metadata
-  };
+      llvm::LLVMContext::MD_prof, // profiling metadata
+      llvm::LLVMContext::MD_tbaa};
   return convertibleMetadata;
 }
 
@@ -127,6 +127,20 @@ static LogicalResult setProfilingAttrs(OpBuilder &builder, llvm::MDNode *node,
   return failure();
 }
 
+/// Attaches the given TBAA metadata `node` to the imported operation.
+/// Returns success, if the metadata has been converted and the attachment
+/// succeeds, failure - otherwise.
+static LogicalResult setTBAAAttrs(const llvm::MDNode *node, Operation *op,
+                                  LLVM::ModuleImport &moduleImport) {
+  SymbolRefAttr tbaaTagSym = moduleImport.lookupTBAAAttr(node);
+  if (!tbaaTagSym)
+    return failure();
+
+  op->setAttr(LLVMDialect::getTBAAAttrName(),
+              ArrayAttr::get(op->getContext(), tbaaTagSym));
+  return success();
+}
+
 namespace {
 
 /// Implementation of the dialect interface that converts operations belonging
@@ -151,6 +165,8 @@ class LLVMDialectLLVMIRImportInterface : public LLVMImportDialectInterface {
     // Call metadata specific handlers.
     if (kind == llvm::LLVMContext::MD_prof)
       return setProfilingAttrs(builder, node, op, moduleImport);
+    if (kind == llvm::LLVMContext::MD_tbaa)
+      return setTBAAAttrs(node, op, moduleImport);
 
     // A handler for a supported metadata kind is missing.
     llvm_unreachable("unknown metadata type");

diff  --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index fb58e34962e8b..ac828e7cc2805 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -27,6 +27,7 @@
 #include "llvm/ADT/StringSet.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/InstIterator.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Metadata.h"
@@ -41,13 +42,24 @@ using namespace mlir::LLVM::detail;
 // Utility to print an LLVM value as a string for passing to emitError().
 // FIXME: Diagnostic should be able to natively handle types that have
 // operator << (raw_ostream&) defined.
-static std::string diag(llvm::Value &value) {
+static std::string diag(const llvm::Value &value) {
   std::string str;
   llvm::raw_string_ostream os(str);
   os << value;
   return os.str();
 }
 
+// Utility to print an LLVM metadata node as a string for passing
+// to emitError(). The module argument is needed to print the nodes
+// canonically numbered.
+static std::string diagMD(const llvm::Metadata *node,
+                          const llvm::Module *module) {
+  std::string str;
+  llvm::raw_string_ostream os(str);
+  node->print(os, module, /*IsForDebug=*/true);
+  return os.str();
+}
+
 /// Returns the name of the global_ctors global variables.
 static constexpr StringRef getGlobalCtorsVarName() {
   return "llvm.global_ctors";
@@ -328,6 +340,277 @@ ModuleImport::ModuleImport(ModuleOp mlirModule,
   builder.setInsertionPointToStart(mlirModule.getBody());
 }
 
+MetadataOp ModuleImport::getTBAAMetadataOp() {
+  if (tbaaMetadataOp)
+    return tbaaMetadataOp;
+
+  OpBuilder::InsertionGuard guard(builder);
+  Location loc = mlirModule.getLoc();
+
+  builder.setInsertionPointToEnd(mlirModule.getBody());
+  tbaaMetadataOp = builder.create<MetadataOp>(loc, getTBAAMetadataOpName());
+  builder.createBlock(&tbaaMetadataOp.getBody());
+  builder.create<ReturnOp>(loc, Value{});
+
+  return tbaaMetadataOp;
+}
+
+std::string ModuleImport::getNewTBAANodeName(StringRef basename) {
+  return (Twine("tbaa_") + Twine(basename) + Twine('_') +
+          Twine(tbaaNodeCounter++))
+      .str();
+}
+
+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.count(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.
+  auto getIdentityIfRootNode =
+      [&](const llvm::MDNode *node) -> std::optional<StringRef> {
+    // Root node, e.g.:
+    //   !0 = !{!"Simple C/C++ TBAA"}
+    if (node->getNumOperands() != 1)
+      return std::nullopt;
+    // If the operand is MDString, then assume that this is a root node.
+    if (auto op0 = dyn_cast<const llvm::MDString>(node->getOperand(0)))
+      return op0->getString();
+    return std::nullopt;
+  };
+
+  // If `node` looks like a TBAA type descriptor metadata,
+  // then return true, if it is a valid node, and false otherwise.
+  // If it does not look like a TBAA type descriptor metadata, then
+  // return std::nullopt.
+  // 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> {
+    unsigned numOperands = node->getNumOperands();
+    // Type descriptor, e.g.:
+    //   !1 = !{!"int", !0, /*optional*/i64 0} /* scalar int type */
+    //   !2 = !{!"agg_t", !1, i64 0} /* struct agg_t { int x; } */
+    if (numOperands < 2)
+      return std::nullopt;
+
+    // TODO: support "new" format (D41501) for type descriptors,
+    //       where the first operand is an MDNode.
+    auto identityNode = dyn_cast<const llvm::MDString>(node->getOperand(0));
+    if (!identityNode)
+      return std::nullopt;
+
+    // This should be a type descriptor node.
+    if (identity)
+      *identity = identityNode->getString();
+
+    for (unsigned pairNum = 0, e = numOperands / 2; pairNum < e; ++pairNum) {
+      const auto *memberNode =
+          dyn_cast<const llvm::MDNode>(node->getOperand(2 * pairNum + 1));
+      if (!memberNode) {
+        emitError(loc) << "operand '" << 2 * pairNum + 1 << "' must be MDNode: "
+                       << diagMD(node, llvmModule.get());
+        return false;
+      }
+      int64_t offset = 0;
+      if (2 * pairNum + 2 >= numOperands) {
+        // Allow for optional 0 offset in 2-operand nodes.
+        if (numOperands != 2) {
+          emitError(loc) << "missing member offset: "
+                         << diagMD(node, llvmModule.get());
+          return false;
+        }
+      } else {
+        auto *offsetCI = llvm::mdconst::dyn_extract<llvm::ConstantInt>(
+            node->getOperand(2 * pairNum + 2));
+        if (!offsetCI) {
+          emitError(loc) << "operand '" << 2 * pairNum + 2
+                         << "' must be ConstantInt: "
+                         << diagMD(node, llvmModule.get());
+          return false;
+        }
+        offset = offsetCI->getZExtValue();
+      }
+
+      if (memberTypes)
+        memberTypes->push_back(tbaaMapping.lookup(memberNode));
+      if (memberOffsets)
+        memberOffsets->push_back(offset);
+    }
+
+    return true;
+  };
+
+  // If `node` looks like a TBAA access tag metadata,
+  // then return true, if it is a valid node, and false otherwise.
+  // If it does not look like a TBAA access tag metadata, then
+  // return std::nullopt.
+  // 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> {
+    // Access tag, e.g.:
+    //   !3 = !{!1, !1, i64 0} /* scalar int access */
+    //   !4 = !{!2, !1, i64 0} /* agg_t::x access */
+    //
+    // Optional 4th argument is ConstantInt 0/1 identifying whether
+    // the location being accessed is "constant" (see for details:
+    // https://llvm.org/docs/LangRef.html#representation).
+    unsigned numOperands = node->getNumOperands();
+    if (numOperands != 3 && numOperands != 4)
+      return std::nullopt;
+    auto baseMD = dyn_cast<const llvm::MDNode>(node->getOperand(0));
+    auto accessMD = dyn_cast<const llvm::MDNode>(node->getOperand(1));
+    auto offsetCI =
+        llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(2));
+    if (!baseMD || !accessMD || !offsetCI)
+      return std::nullopt;
+    // TODO: support "new" TBAA format, if needed (see D41501).
+    // In the "old" format the first operand of the access type
+    // metadata is MDString. We have to distinguish the formats,
+    // because access tags have the same structure, but 
diff erent
+    // meaning for the operands.
+    if (accessMD->getNumOperands() < 1 ||
+        !isa<llvm::MDString>(accessMD->getOperand(0)))
+      return std::nullopt;
+    bool isConst = false;
+    if (numOperands == 4) {
+      auto isConstantCI =
+          llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(3));
+      if (!isConstantCI) {
+        emitError(loc) << "operand '3' must be ConstantInt: "
+                       << diagMD(node, llvmModule.get());
+        return false;
+      }
+      isConst = isConstantCI->getValue()[0];
+    }
+    if (baseSymRef)
+      *baseSymRef = tbaaMapping.lookup(baseMD);
+    if (accessSymRef)
+      *accessSymRef = tbaaMapping.lookup(accessMD);
+    if (offset)
+      *offset = offsetCI->getZExtValue();
+    if (isConstant)
+      *isConstant = isConst;
+    return true;
+  };
+
+  // Insert new operations before the terminator.
+  OpBuilder::InsertionGuard guard(builder);
+  builder.setInsertionPoint(&getTBAAMetadataOp().getBody().back().back());
+  StringAttr metadataOpName = SymbolTable::getSymbolName(getTBAAMetadataOp());
+
+  // 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: "
+                              << 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, getNewTBAANodeName("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(),
+                                          getNewTBAANodeName("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(),
+                                              getNewTBAANodeName("tag"))));
+      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)) {
+      assert(isValid.value() && "type descriptor node must be valid");
+
+      builder.create<TBAATypeDescriptorOp>(
+          loc, tbaaMapping.lookup(current).getLeafReference(),
+          builder.getStringAttr(identity), builder.getArrayAttr(memberTypes),
+          memberOffsets);
+      continue;
+    }
+    SymbolRefAttr baseSymRef, accessSymRef;
+    int64_t offset;
+    bool isConstant;
+    if (std::optional<bool> isValid = isTagNode(
+            current, &baseSymRef, &accessSymRef, &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);
+      continue;
+    }
+  }
+
+  return success();
+}
+
+LogicalResult ModuleImport::convertMetadata() {
+  OpBuilder::InsertionGuard guard(builder);
+  builder.setInsertionPoint(mlirModule.getBody(), mlirModule.getBody()->end());
+  for (const llvm::Function &func : llvmModule->functions())
+    for (const llvm::Instruction &inst : llvm::instructions(func)) {
+      llvm::AAMDNodes nodes = inst.getAAMetadata();
+      if (!nodes)
+        continue;
+
+      if (const llvm::MDNode *tbaaMD = nodes.TBAA)
+        if (failed(processTBAAMetadata(tbaaMD)))
+          return failure();
+      // TODO: only TBAA metadata is currently supported.
+    }
+
+  return success();
+}
+
 LogicalResult ModuleImport::convertGlobals() {
   for (llvm::GlobalVariable &globalVar : llvmModule->globals()) {
     if (globalVar.getName() == getGlobalCtorsVarName() ||
@@ -1275,6 +1558,8 @@ mlir::translateLLVMIRToModule(std::unique_ptr<llvm::Module> llvmModule,
   ModuleImport moduleImport(module.get(), std::move(llvmModule));
   if (failed(moduleImport.initializeImportInterface()))
     return {};
+  if (failed(moduleImport.convertMetadata()))
+    return {};
   if (failed(moduleImport.convertGlobals()))
     return {};
   if (failed(moduleImport.convertFunctions()))

diff  --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index a214ee5c28c30..c72f29be3f9c3 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -1175,6 +1175,114 @@ void ModuleTranslation::setAliasScopeMetadata(Operation *op,
   populateScopeMetadata(LLVMDialect::getNoAliasScopesAttrName(), "noalias");
 }
 
+llvm::MDNode *ModuleTranslation::getTBAANode(Operation &memOp,
+                                             SymbolRefAttr tagRef) const {
+  StringAttr metadataName = tagRef.getRootReference();
+  StringAttr tagName = tagRef.getLeafReference();
+  auto metadataOp = SymbolTable::lookupNearestSymbolFrom<LLVM::MetadataOp>(
+      memOp.getParentOp(), metadataName);
+  Operation *tagOp = SymbolTable::lookupNearestSymbolFrom(metadataOp, tagName);
+  return tbaaMetadataMapping.lookup(tagOp);
+}
+
+void ModuleTranslation::setTBAAMetadata(Operation *op,
+                                        llvm::Instruction *inst) {
+  auto tbaa = op->getAttrOfType<ArrayAttr>(LLVMDialect::getTBAAAttrName());
+  if (!tbaa || tbaa.empty())
+    return;
+  // LLVM IR currently does not support attaching more than one
+  // TBAA access tag to a memory accessing instruction.
+  // It may be useful to support this in future, but for the time being
+  // just ignore the metadata if MLIR operation has multiple access tags.
+  if (tbaa.size() > 1) {
+    op->emitWarning() << "TBAA access tags were not translated, because LLVM "
+                         "IR only supports a single tag per instruction";
+    return;
+  }
+  SymbolRefAttr tagRef = tbaa[0].cast<SymbolRefAttr>();
+  llvm::MDNode *tagNode = getTBAANode(*op, tagRef);
+  inst->setMetadata(llvm::LLVMContext::MD_tbaa, tagNode);
+}
+
+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()) {
+          operands.push_back(nullptr); // Placeholder for the member type.
+          operands.push_back(llvm::ConstantAsMetadata::get(
+              llvm::ConstantInt::get(offsetTy, offset)));
+        }
+      } else if (auto tagOp = dyn_cast<LLVM::TBAATagOp>(op)) {
+        operands.push_back(nullptr); // Placeholder for the base type.
+        operands.push_back(nullptr); // Placeholder for the access type.
+        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;
+
+      tbaaMetadataMapping.insert({&op, llvm::MDNode::get(ctx, operands)});
+    }
+  }
+
+  // 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(typeAttr.cast<FlatSymbolRefAttr>().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);
+      }
+
+      if (refNames.empty())
+        continue;
+
+      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();
+        }
+        descNode->replaceOperandWith(opNum, refNode);
+      }
+    }
+  }
+
+  return success();
+}
+
 llvm::Type *ModuleTranslation::convertType(Type type) {
   return typeTranslator.translateType(type);
 }
@@ -1268,6 +1376,8 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext,
     return nullptr;
   if (failed(translator.createAliasScopeMetadata()))
     return nullptr;
+  if (failed(translator.createTBAAMetadata()))
+    return nullptr;
   if (failed(translator.convertFunctions()))
     return nullptr;
 

diff  --git a/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir b/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir
new file mode 100644
index 0000000000000..6e7f6682cb6b5
--- /dev/null
+++ b/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir
@@ -0,0 +1,181 @@
+// 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.return
+  }
+  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 {llvm.tbaa = [@tbaa_tag_1]} : i8, !llvm.ptr
+    llvm.return
+  }
+}
+
+// -----
+
+llvm.func @tbaa(%arg0: !llvm.ptr) {
+  %0 = llvm.mlir.constant(1 : i8) : i8
+  // expected-error at below {{attribute 'llvm.tbaa' failed to satisfy constraint: symbol ref array attribute}}
+  llvm.store %0, %arg0 {llvm.tbaa = ["sym"]} : i8, !llvm.ptr
+  llvm.return
+}
+
+// -----
+
+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 {llvm.tbaa = [@metadata::@group1]} : i8, !llvm.ptr
+    llvm.return
+  }
+  llvm.metadata @metadata {
+    llvm.access_group @group1
+    llvm.return
+  }
+}
+
+// -----
+
+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 {llvm.tbaa = [@metadata::@sym]} : i8, !llvm.ptr
+    llvm.return
+  }
+  llvm.metadata @metadata {
+    llvm.return
+  }
+}
+
+// -----
+
+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 {llvm.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.return
+  }
+
+  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.return
+  }
+
+  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 = ""}
+    llvm.return
+  }
+}
+
+// -----
+
+  "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"} : () -> ()
+      "llvm.return"() : () -> ()
+    }) {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}
+    llvm.return
+  }
+}
+
+// -----
+
+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}
+    llvm.return
+  }
+}
+
+// -----
+
+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>}}
+    llvm.return
+  }
+}
+
+// -----
+
+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}
+    llvm.return
+  }
+}
+
+// -----
+
+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>}}
+    llvm.return
+  }
+}

diff  --git a/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir b/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir
new file mode 100644
index 0000000000000..748cbbea8099f
--- /dev/null
+++ b/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir
@@ -0,0 +1,105 @@
+// 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.return
+  }
+  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
+  }
+}
+
+// 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.return
+// 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.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.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::@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
+  }
+}
+
+// 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:             llvm.return
+// 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_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.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.return
+  }
+  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
+  }
+}
+
+// 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.return
+// CHECK:           }
+// CHECK:           llvm.func @tbaa1(%[[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.return
+// CHECK:           }

diff  --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll
index c4dffe9c2d514..99a35afa1fd50 100644
--- a/mlir/test/Target/LLVMIR/Import/import-failure.ll
+++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll
@@ -118,3 +118,90 @@ define void @foo() {
 define void @foo() {
   ret void
 }
+
+; // -----
+
+; CHECK: error: TBAA root node must have non-empty identity: !2 = !{!""}
+define dso_local void @tbaa(ptr %0) {
+  store i8 1, ptr %0, align 4, !tbaa !2
+  ret void
+}
+
+!0 = !{!""}
+!1 = !{!"omnipotent char", !0, i64 0}
+!2 = !{!1, !1, i64 0}
+
+; // -----
+
+; CHECK: error: unsupported TBAA node format: !0 = !{!1, i64 0, i64 0}
+define dso_local void @tbaa(ptr %0) {
+  store i8 1, ptr %0, align 4, !tbaa !2
+  ret void
+}
+
+!0 = !{!"Simple C/C++ TBAA"}
+!1 = !{!"omnipotent char", !0, i64 0}
+!2 = !{!1, i64 0, i64 0}
+
+; // -----
+
+; CHECK: error: operand '1' must be MDNode: !1 = !{!"omnipotent char", i64 0, i64 0}
+define dso_local void @tbaa(ptr %0) {
+  store i8 1, ptr %0, align 4, !tbaa !2
+  ret void
+}
+
+!0 = !{!"Simple C/C++ TBAA"}
+!1 = !{!"omnipotent char", i64 0, i64 0}
+!2 = !{!1, !1, i64 0}
+
+; // -----
+
+; CHECK: error: missing member offset: !1 = !{!"agg_t", !2, i64 0, !2}
+define dso_local void @tbaa(ptr %0) {
+  store i8 1, ptr %0, align 4, !tbaa !3
+  ret void
+}
+
+!0 = !{!"Simple C/C++ TBAA"}
+!1 = !{!"omnipotent char", !0, i64 0}
+!2 = !{!"agg_t", !1, i64 0, !1}
+!3 = !{!2, !1, i64 0}
+
+; // -----
+
+; CHECK: error: operand '4' must be ConstantInt: !1 = !{!"agg_t", !2, i64 0, !2, double 1.000000e+00}
+define dso_local void @tbaa(ptr %0) {
+  store i8 1, ptr %0, align 4, !tbaa !3
+  ret void
+}
+
+!0 = !{!"Simple C/C++ TBAA"}
+!1 = !{!"omnipotent char", !0, i64 0}
+!2 = !{!"agg_t", !1, i64 0, !1, double 1.0}
+!3 = !{!2, !1, i64 0}
+
+; // -----
+
+; CHECK: error: operand '3' must be ConstantInt: !0 = !{!1, !1, i64 0, double 1.000000e+00}
+define dso_local void @tbaa(ptr %0) {
+  store i8 1, ptr %0, align 4, !tbaa !2
+  ret void
+}
+
+!0 = !{!"Simple C/C++ TBAA"}
+!1 = !{!"omnipotent char", !0, i64 0}
+!2 = !{!1, !1, i64 0, double 1.0}
+
+; // -----
+
+; CHECK: error: unsupported TBAA node format: !0 = !{!1, !1, i64 0, i64 4}
+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"}

diff  --git a/mlir/test/Target/LLVMIR/Import/tbaa.ll b/mlir/test/Target/LLVMIR/Import/tbaa.ll
new file mode 100644
index 0000000000000..8d23b56ca4732
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/Import/tbaa.ll
@@ -0,0 +1,69 @@
+; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s
+
+// -----
+
+; CHECK-LABEL: llvm.metadata @__tbaa {
+; 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 = @[[R0]], base_type = @[[R0]], offset = 0 : i64}
+; CHECK-NEXT:    llvm.tbaa_root @[[R1:tbaa_root_[0-9]+]] {id = "Other language TBAA"}
+; CHECK-NEXT:    llvm.tbaa_tag @[[T1:tbaa_tag_[0-9]+]] {access_type = @[[R1]], base_type = @[[R1]], offset = 0 : i64}
+; CHECK-NEXT:    llvm.return
+; CHECK-NEXT:  }
+; CHECK:       llvm.func @tbaa1
+; CHECK:         llvm.store %{{.*}}, %{{.*}} {
+; CHECK-SAME:        tbaa = [@__tbaa::@[[T0]]]
+; CHECK-SAME:    } : i8, !llvm.ptr
+; CHECK:         llvm.store %{{.*}}, %{{.*}} {
+; CHECK-SAME:        tbaa = [@__tbaa::@[[T1]]]
+; CHECK-SAME:    } : i8, !llvm.ptr
+define dso_local void @tbaa1(ptr %0, ptr %1) {
+  store i8 1, ptr %0, align 4, !tbaa !1
+  store i8 1, ptr %1, align 4, !tbaa !3
+  ret void
+}
+
+!0 = !{!"Simple C/C++ TBAA"}
+!1 = !{!0, !0, i64 0}
+!2 = !{!"Other language TBAA"}
+!3 = !{!2, !2, i64 0}
+
+// -----
+
+; CHECK-LABEL: llvm.metadata @__tbaa {
+; 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:    llvm.return
+; CHECK-NEXT:  }
+; CHECK:       llvm.func @tbaa2
+; CHECK:         llvm.load %{{.*}} {
+; CHECK-SAME:        tbaa = [@__tbaa::@[[T0]]]
+; CHECK-SAME:    } : !llvm.ptr -> i64
+; CHECK:         llvm.store %{{.*}}, %{{.*}} {
+; CHECK-SAME:        tbaa = [@__tbaa::@[[T1]]]
+; CHECK-SAME:    } : i32, !llvm.ptr
+%struct.agg2_t = type { i64, i64 }
+%struct.agg1_t = type { i32, i32 }
+
+define dso_local void @tbaa2(ptr %0, ptr %1) {
+  %3 = getelementptr inbounds %struct.agg2_t, ptr %1, i32 0, i32 1
+  %4 = load i64, ptr %3, align 8, !tbaa !6
+  %5 = trunc i64 %4 to i32
+  %6 = getelementptr inbounds %struct.agg1_t, ptr %0, i32 0, i32 0
+  store i32 %5, ptr %6, align 4, !tbaa !11
+  ret void
+}
+
+!6 = !{!7, !8, i64 8}
+!7 = !{!"agg2_t", !8, i64 0, !8, i64 8}
+!8 = !{!"long long", !9, i64 0}
+!9 = !{!"omnipotent char", !10, i64 0}
+!10 = !{!"Simple C/C++ TBAA"}
+!11 = !{!12, !13, i64 0}
+!12 = !{!"agg1_t", !13, i64 0, !13, i64 4}
+!13 = !{!"int", !9, i64 0}

diff  --git a/mlir/test/Target/LLVMIR/tbaa.mlir b/mlir/test/Target/LLVMIR/tbaa.mlir
new file mode 100644
index 0000000000000..83474c14c8663
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/tbaa.mlir
@@ -0,0 +1,36 @@
+// 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.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)>
+    // CHECK: load i64, ptr %{{.*}},{{.*}}!tbaa ![[LTAG:[0-9]*]]
+    %3 = llvm.load %2 {llvm.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 {llvm.tbaa = [@__tbaa::@tbaa_tag_7]} : i32, !llvm.ptr
+    llvm.return
+  }
+}
+
+// CHECK-DAG: ![[LTAG]] = !{![[AGG2T:[0-9]*]], ![[I64T:[0-9]*]], i64 8}
+// CHECK-DAG: ![[AGG2T]] = !{!"agg2_t", ![[I64T]], i64 0, ![[I64T]], i64 8}
+// CHECK-DAG: ![[I64T]] = !{!"long long", ![[CHART:[0-9]*]], i64 0}
+// CHECK-DAG: ![[CHART]] = !{!"omnipotent char", ![[ROOT:[0-9]*]], i64 0}
+// CHECK-DAG: ![[ROOT]] = !{!"Simple C/C++ TBAA"}
+// CHECK-DAG: ![[STAG]] = !{![[AGG1T:[0-9]*]], ![[I32T:[0-9]*]], i64 0, i64 1}
+// CHECK-DAG: ![[AGG1T]] = !{!"agg1_t", ![[I32T]], i64 0, ![[I32T]], i64 4}
+// CHECK-DAG: ![[I32T]] = !{!"int", ![[CHART]], i64 0}


        


More information about the Mlir-commits mailing list