[flang-commits] [flang] 9c84d20 - [flang] Generate TBAA information.

Slava Zakharin via flang-commits flang-commits at lists.llvm.org
Tue Jan 17 10:19:37 PST 2023


Author: Slava Zakharin
Date: 2023-01-17T10:19:11-08:00
New Revision: 9c84d20fa69006adcbbaf424f3ef674298045f78

URL: https://github.com/llvm/llvm-project/commit/9c84d20fa69006adcbbaf424f3ef674298045f78
DIFF: https://github.com/llvm/llvm-project/commit/9c84d20fa69006adcbbaf424f3ef674298045f78.diff

LOG: [flang] Generate TBAA information.

This is initial version of TBAA information generation for Flang
generated IR. The desired behavior is that TBAA type descriptors
are generated for FIR types during FIR to LLVM types conversion,
and then TBAA access tags are attached to memory accessing operations
when they are converted to LLVM IR dialect.

In the initial version the type conversion is not producing
TBAA type descriptors, and all memory accesses are just partitioned
into two sets of box and non-box accesses, which can never alias.

The TBAA generation is enabled by default at >O0 optimization levels.
TBAA generation may also be enabled via `apply-tbaa` option of
`fir-to-llvm-ir` conversion pass. `-mllvm -disable-tbaa` engineering
option allows disabling TBAA generation to override Flang's default
(e.g. when -O1 is used).

SPEC CPU2006/437.leslie3d speeds up by more than 2x on Icelake.

Reviewed By: jeanPerier, clementval

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

Added: 
    flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
    flang/lib/Optimizer/CodeGen/TBAABuilder.h
    flang/test/Fir/tbaa.fir

Modified: 
    flang/include/flang/Optimizer/CodeGen/CGPasses.td
    flang/include/flang/Optimizer/CodeGen/CodeGen.h
    flang/include/flang/Tools/CLOptions.inc
    flang/lib/Optimizer/CodeGen/CMakeLists.txt
    flang/lib/Optimizer/CodeGen/CodeGen.cpp
    flang/lib/Optimizer/CodeGen/DescriptorModel.h
    flang/lib/Optimizer/CodeGen/TypeConverter.h

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/CodeGen/CGPasses.td b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
index c775da00ab246..c215cb647849d 100644
--- a/flang/include/flang/Optimizer/CodeGen/CGPasses.td
+++ b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
@@ -26,7 +26,9 @@ def FIRToLLVMLowering : Pass<"fir-to-llvm-ir", "mlir::ModuleOp"> {
   let dependentDialects = ["mlir::LLVM::LLVMDialect"];
   let options = [
     Option<"forcedTargetTriple", "target", "std::string", /*default=*/"",
-           "Override module's target triple.">
+           "Override module's target triple.">,
+    Option<"applyTBAA", "apply-tbaa", "bool", /*default=*/"false",
+           "Attach TBAA tags to memory accessing operations.">
   ];
 }
 

diff  --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
index a49a82a488328..64747b871de09 100644
--- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h
+++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
@@ -51,6 +51,9 @@ struct FIRToLLVMPassOptions {
   // that such programs would crash at runtime if the derived type descriptors
   // are required by the runtime, so this is only an option to help debugging.
   bool ignoreMissingTypeDescriptors = false;
+
+  // Generate TBAA information for FIR types and memory accessing operations.
+  bool applyTBAA = false;
 };
 
 /// Convert FIR to the LLVM IR dialect with default options.

diff  --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc
index 6dad1c2e93605..3857ea3e7285b 100644
--- a/flang/include/flang/Tools/CLOptions.inc
+++ b/flang/include/flang/Tools/CLOptions.inc
@@ -133,9 +133,11 @@ inline void addDebugFoundationPass(mlir::PassManager &pm) {
       [&]() { return fir::createAddDebugFoundationPass(); });
 }
 
-inline void addFIRToLLVMPass(mlir::PassManager &pm) {
+inline void addFIRToLLVMPass(
+    mlir::PassManager &pm, llvm::OptimizationLevel optLevel = defaultOptLevel) {
   fir::FIRToLLVMPassOptions options;
   options.ignoreMissingTypeDescriptors = ignoreMissingTypeDescriptors;
+  options.applyTBAA = optLevel.isOptimizingForSpeed();
   addPassConditionally(pm, disableFirToLlvmIr,
       [&]() { return fir::createFIRToLLVMPass(options); });
 }
@@ -197,7 +199,8 @@ inline void createDefaultFIROptimizerPassPipeline(
 }
 
 #if !defined(FLANG_EXCLUDE_CODEGEN)
-inline void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm) {
+inline void createDefaultFIRCodeGenPassPipeline(
+    mlir::PassManager &pm, llvm::OptimizationLevel optLevel = defaultOptLevel) {
   fir::addBoxedProcedurePass(pm);
   pm.addNestedPass<mlir::func::FuncOp>(
       fir::createAbstractResultOnFuncOptPass());
@@ -206,7 +209,7 @@ inline void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm) {
   fir::addCodeGenRewritePass(pm);
   fir::addTargetRewritePass(pm);
   fir::addExternalNameConversionPass(pm);
-  fir::addFIRToLLVMPass(pm);
+  fir::addFIRToLLVMPass(pm, optLevel);
 }
 
 /// Create a pass pipeline for lowering from MLIR to LLVM IR
@@ -220,7 +223,7 @@ inline void createMLIRToLLVMPassPipeline(
   fir::createDefaultFIROptimizerPassPipeline(pm, optLevel);
 
   // Add codegen pass pipeline.
-  fir::createDefaultFIRCodeGenPassPipeline(pm);
+  fir::createDefaultFIRCodeGenPassPipeline(pm, optLevel);
 }
 #undef FLANG_EXCLUDE_CODEGEN
 #endif

diff  --git a/flang/lib/Optimizer/CodeGen/CMakeLists.txt b/flang/lib/Optimizer/CodeGen/CMakeLists.txt
index 19c8b1b56125e..58c48479ff375 100644
--- a/flang/lib/Optimizer/CodeGen/CMakeLists.txt
+++ b/flang/lib/Optimizer/CodeGen/CMakeLists.txt
@@ -3,6 +3,7 @@ add_flang_library(FIRCodeGen
   CGOps.cpp
   CodeGen.cpp
   PreCGRewrite.cpp
+  TBAABuilder.cpp
   Target.cpp
   TargetRewrite.cpp
 

diff  --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 73bb0380f1e8f..31b6498931b9b 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -159,16 +159,18 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
     return val;
   }
 
-  /// Construct code sequence to extract the specifc value from a `fir.box`.
-  mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box,
-                              mlir::Type resultTy,
+  /// Construct code sequence to extract the specific value from a `fir.box`.
+  mlir::Value getValueFromBox(mlir::Location loc, mlir::Type boxTy,
+                              mlir::Value box, mlir::Type resultTy,
                               mlir::ConversionPatternRewriter &rewriter,
                               int boxValue) const {
     if (box.getType().isa<mlir::LLVM::LLVMPointerType>()) {
       auto pty = mlir::LLVM::LLVMPointerType::get(resultTy);
       auto p = rewriter.create<mlir::LLVM::GEPOp>(
           loc, pty, box, llvm::ArrayRef<mlir::LLVM::GEPArg>{0, boxValue});
-      return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
+      auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
+      attachTBAATag(loadOp, boxTy, nullptr, p);
+      return loadOp;
     }
     return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, box, boxValue);
   }
@@ -177,27 +179,33 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
   /// from a box.
   llvm::SmallVector<mlir::Value, 3>
   getDimsFromBox(mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys,
-                 mlir::Value box, mlir::Value dim,
+                 mlir::Type boxTy, mlir::Value box, mlir::Value dim,
                  mlir::ConversionPatternRewriter &rewriter) const {
-    mlir::Value l0 = loadDimFieldFromBox(loc, box, dim, 0, retTys[0], rewriter);
-    mlir::Value l1 = loadDimFieldFromBox(loc, box, dim, 1, retTys[1], rewriter);
-    mlir::Value l2 = loadDimFieldFromBox(loc, box, dim, 2, retTys[2], rewriter);
+    mlir::Value l0 =
+        loadDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter);
+    mlir::Value l1 =
+        loadDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter);
+    mlir::Value l2 =
+        loadDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter);
     return {l0, l1, l2};
   }
 
   llvm::SmallVector<mlir::Value, 3>
   getDimsFromBox(mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys,
-                 mlir::Value box, int dim,
+                 mlir::Type boxTy, mlir::Value box, int dim,
                  mlir::ConversionPatternRewriter &rewriter) const {
-    mlir::Value l0 = getDimFieldFromBox(loc, box, dim, 0, retTys[0], rewriter);
-    mlir::Value l1 = getDimFieldFromBox(loc, box, dim, 1, retTys[1], rewriter);
-    mlir::Value l2 = getDimFieldFromBox(loc, box, dim, 2, retTys[2], rewriter);
+    mlir::Value l0 =
+        getDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter);
+    mlir::Value l1 =
+        getDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter);
+    mlir::Value l2 =
+        getDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter);
     return {l0, l1, l2};
   }
 
   mlir::Value
-  loadDimFieldFromBox(mlir::Location loc, mlir::Value box, mlir::Value dim,
-                      int off, mlir::Type ty,
+  loadDimFieldFromBox(mlir::Location loc, mlir::Type boxTy, mlir::Value box,
+                      mlir::Value dim, int off, mlir::Type ty,
                       mlir::ConversionPatternRewriter &rewriter) const {
     assert(box.getType().isa<mlir::LLVM::LLVMPointerType>() &&
            "descriptor inquiry with runtime dim can only be done on descriptor "
@@ -205,41 +213,50 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, 0,
                                  static_cast<int>(kDimsPosInBox), dim, off);
-    return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
+    auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
+    attachTBAATag(loadOp, boxTy, nullptr, p);
+    return loadOp;
   }
 
   mlir::Value
-  getDimFieldFromBox(mlir::Location loc, mlir::Value box, int dim, int off,
-                     mlir::Type ty,
+  getDimFieldFromBox(mlir::Location loc, mlir::Type boxTy, mlir::Value box,
+                     int dim, int off, mlir::Type ty,
                      mlir::ConversionPatternRewriter &rewriter) const {
     if (box.getType().isa<mlir::LLVM::LLVMPointerType>()) {
       auto pty = mlir::LLVM::LLVMPointerType::get(ty);
       mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, 0,
                                    static_cast<int>(kDimsPosInBox), dim, off);
-      return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
+      auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
+      attachTBAATag(loadOp, boxTy, nullptr, p);
+      return loadOp;
     }
     return rewriter.create<mlir::LLVM::ExtractValueOp>(
         loc, box, llvm::ArrayRef<std::int64_t>{kDimsPosInBox, dim, off});
   }
 
   mlir::Value
-  getStrideFromBox(mlir::Location loc, mlir::Value box, unsigned dim,
+  getStrideFromBox(mlir::Location loc, mlir::Type boxTy, mlir::Value box,
+                   unsigned dim,
                    mlir::ConversionPatternRewriter &rewriter) const {
     auto idxTy = lowerTy().indexType();
-    return getDimFieldFromBox(loc, box, dim, kDimStridePos, idxTy, rewriter);
+    return getDimFieldFromBox(loc, boxTy, box, dim, kDimStridePos, idxTy,
+                              rewriter);
   }
 
   /// Read base address from a fir.box. Returned address has type ty.
   mlir::Value
-  getBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
+  getBaseAddrFromBox(mlir::Location loc, mlir::Type resultTy, mlir::Type boxTy,
+                     mlir::Value box,
                      mlir::ConversionPatternRewriter &rewriter) const {
-    return getValueFromBox(loc, box, ty, rewriter, kAddrPosInBox);
+    return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kAddrPosInBox);
   }
 
   mlir::Value
-  getElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
+  getElementSizeFromBox(mlir::Location loc, mlir::Type resultTy,
+                        mlir::Type boxTy, mlir::Value box,
                         mlir::ConversionPatternRewriter &rewriter) const {
-    return getValueFromBox(loc, box, ty, rewriter, kElemLenPosInBox);
+    return getValueFromBox(loc, boxTy, box, resultTy, rewriter,
+                           kElemLenPosInBox);
   }
 
   // Get the element type given an LLVM type that is of the form
@@ -272,21 +289,23 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
 
   /// Read the address of the type descriptor from a box.
   mlir::Value
-  loadTypeDescAddress(mlir::Location loc, mlir::Type ty, mlir::Value box,
+  loadTypeDescAddress(mlir::Location loc, mlir::Type boxTy, mlir::Value box,
                       mlir::ConversionPatternRewriter &rewriter) const {
-    unsigned typeDescFieldId = getTypeDescFieldId(ty);
+    unsigned typeDescFieldId = getTypeDescFieldId(boxTy);
     mlir::Type tdescType = lowerTy().convertTypeDescType(rewriter.getContext());
-    return getValueFromBox(loc, box, tdescType, rewriter, typeDescFieldId);
+    return getValueFromBox(loc, boxTy, box, tdescType, rewriter,
+                           typeDescFieldId);
   }
 
   // Load the attribute from the \p box and perform a check against \p maskValue
   // The final comparison is implemented as `(attribute & maskValue) != 0`.
-  mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box,
+  mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Type boxTy,
+                                   mlir::Value box,
                                    mlir::ConversionPatternRewriter &rewriter,
                                    unsigned maskValue) const {
     mlir::Type attrTy = rewriter.getI32Type();
     mlir::Value attribute =
-        getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox);
+        getValueFromBox(loc, boxTy, box, attrTy, rewriter, kAttributePosInBox);
     mlir::LLVM::ConstantOp attrMask =
         genConstantOffset(loc, rewriter, maskValue);
     auto maskRes =
@@ -333,6 +352,11 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
   }
 
+  void attachTBAATag(mlir::Operation *op, mlir::Type baseFIRType,
+                     mlir::Type accessFIRType, mlir::LLVM::GEPOp gep) const {
+    lowerTy().attachTBAATag(op, baseFIRType, accessFIRType, gep);
+  }
+
   const fir::FIRToLLVMPassOptions &options;
   const BindingTables &bindingTables;
 };
@@ -497,7 +521,8 @@ struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
     auto loc = boxaddr.getLoc();
     mlir::Type ty = convertType(boxaddr.getType());
     if (auto argty = boxaddr.getVal().getType().dyn_cast<fir::BaseBoxType>()) {
-      rewriter.replaceOp(boxaddr, getBaseAddrFromBox(loc, ty, a, rewriter));
+      rewriter.replaceOp(boxaddr,
+                         getBaseAddrFromBox(loc, ty, argty, a, rewriter));
     } else {
       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, a, 0);
     }
@@ -528,7 +553,7 @@ struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
 };
 
 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
-/// dimension infomartion from the boxed value.
+/// dimension information from the boxed value.
 /// Result in a triple set of GEPs and loads.
 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
   using FIROpConversion::FIROpConversion;
@@ -541,9 +566,9 @@ struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
         convertType(boxdims.getResult(1).getType()),
         convertType(boxdims.getResult(2).getType()),
     };
-    auto results =
-        getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
-                       adaptor.getOperands()[1], rewriter);
+    auto results = getDimsFromBox(
+        boxdims.getLoc(), resultTypes, boxdims.getVal().getType(),
+        adaptor.getOperands()[0], adaptor.getOperands()[1], rewriter);
     rewriter.replaceOp(boxdims, results);
     return mlir::success();
   }
@@ -557,10 +582,11 @@ struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
   mlir::LogicalResult
   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
                   mlir::ConversionPatternRewriter &rewriter) const override {
-    mlir::Value a = adaptor.getOperands()[0];
+    mlir::Value box = adaptor.getOperands()[0];
     auto loc = boxelesz.getLoc();
     auto ty = convertType(boxelesz.getType());
-    auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox);
+    auto elemSize = getElementSizeFromBox(loc, ty, boxelesz.getVal().getType(),
+                                          box, rewriter);
     rewriter.replaceOp(boxelesz, elemSize);
     return mlir::success();
   }
@@ -576,8 +602,8 @@ struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> {
                   mlir::ConversionPatternRewriter &rewriter) const override {
     mlir::Value box = adaptor.getOperands()[0];
     auto loc = boxisalloc.getLoc();
-    mlir::Value check =
-        genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable);
+    mlir::Value check = genBoxAttributeCheck(loc, boxisalloc.getVal().getType(),
+                                             box, rewriter, kAttrAllocatable);
     rewriter.replaceOp(boxisalloc, check);
     return mlir::success();
   }
@@ -593,8 +619,8 @@ struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> {
                   mlir::ConversionPatternRewriter &rewriter) const override {
     mlir::Value a = adaptor.getOperands()[0];
     auto loc = boxisarray.getLoc();
-    auto rank =
-        getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox);
+    auto rank = getValueFromBox(loc, boxisarray.getVal().getType(), a,
+                                rewriter.getI32Type(), rewriter, kRankPosInBox);
     auto c0 = genConstantOffset(loc, rewriter, 0);
     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
@@ -612,7 +638,8 @@ struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> {
                   mlir::ConversionPatternRewriter &rewriter) const override {
     mlir::Value box = adaptor.getOperands()[0];
     auto loc = boxisptr.getLoc();
-    mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer);
+    mlir::Value check = genBoxAttributeCheck(loc, boxisptr.getVal().getType(),
+                                             box, rewriter, kAttrPointer);
     rewriter.replaceOp(boxisptr, check);
     return mlir::success();
   }
@@ -629,7 +656,8 @@ struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
     mlir::Value a = adaptor.getOperands()[0];
     auto loc = boxrank.getLoc();
     mlir::Type ty = convertType(boxrank.getType());
-    auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox);
+    auto result = getValueFromBox(loc, boxrank.getVal().getType(), a, ty,
+                                  rewriter, kRankPosInBox);
     rewriter.replaceOp(boxrank, result);
     return mlir::success();
   }
@@ -676,7 +704,8 @@ struct BoxTypeCodeOpConversion : public FIROpConversion<fir::BoxTypeCodeOp> {
     mlir::Value box = adaptor.getOperands()[0];
     auto loc = box.getLoc();
     auto ty = convertType(op.getType());
-    auto typeCode = getValueFromBox(loc, box, ty, rewriter, kTypePosInBox);
+    auto typeCode = getValueFromBox(loc, op.getBox().getType(), box, ty,
+                                    rewriter, kTypePosInBox);
     rewriter.replaceOp(op, typeCode);
     return mlir::success();
   }
@@ -990,6 +1019,14 @@ struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
                        .getType()
                        .dyn_cast<mlir::LLVM::LLVMPointerType>();
 
+    // TODO: the following loads from the type descriptor related
+    // data structures must have proper TBAA access tags.
+    // These loads cannot alias with any real data accesses nor
+    // with any box accesses. Moreover, they can probably be marked
+    // as reading from constant memory (fourth operand of a TBAA
+    // tag may be set to true). These accesses probably deserve
+    // separate sub-root in the TBAA graph.
+
     // Load the descriptor.
     auto desc = rewriter.create<mlir::LLVM::LoadOp>(
         loc, descPtr.getElementType(), adaptor.getOperands()[0]);
@@ -1591,9 +1628,10 @@ struct EmboxCommonConversion : public FIROpConversion<OP> {
       typeDesc =
           this->loadTypeDescAddress(loc, sourceBoxType, sourceBox, rewriter);
       mlir::Type idxTy = this->lowerTy().indexType();
-      eleSize = this->getElementSizeFromBox(loc, idxTy, sourceBox, rewriter);
-      cfiTy = this->getValueFromBox(loc, sourceBox, cfiTy.getType(), rewriter,
-                                    kTypePosInBox);
+      eleSize = this->getElementSizeFromBox(loc, idxTy, sourceBoxType,
+                                            sourceBox, rewriter);
+      cfiTy = this->getValueFromBox(loc, sourceBoxType, sourceBox,
+                                    cfiTy.getType(), rewriter, kTypePosInBox);
     }
     auto mod = box->template getParentOfType<mlir::ModuleOp>();
     mlir::Value descriptor = populateDescriptor(
@@ -1621,9 +1659,10 @@ struct EmboxCommonConversion : public FIROpConversion<OP> {
     if (fir::isPolymorphicType(boxTy) &&
         fir::isPolymorphicType(box.getBox().getType())) {
       mlir::Type idxTy = this->lowerTy().indexType();
-      eleSize = this->getElementSizeFromBox(loc, idxTy, loweredBox, rewriter);
-      cfiTy = this->getValueFromBox(loc, loweredBox, cfiTy.getType(), rewriter,
-                                    kTypePosInBox);
+      eleSize =
+          this->getElementSizeFromBox(loc, idxTy, boxTy, loweredBox, rewriter);
+      cfiTy = this->getValueFromBox(loc, boxTy, loweredBox, cfiTy.getType(),
+                                    rewriter, kTypePosInBox);
       typeDesc = this->loadTypeDescAddress(loc, box.getBox().getType(),
                                            loweredBox, rewriter);
     }
@@ -1726,13 +1765,15 @@ struct EmboxCommonConversion : public FIROpConversion<OP> {
   /// value otherwise.
   mlir::Value
   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
-                               mlir::Location loc, mlir::Value boxValue) const {
+                               mlir::Location loc, mlir::Type boxTy,
+                               mlir::Value boxValue) const {
     if (isInGlobalOp(rewriter))
       return boxValue;
     auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
     auto alloca =
         this->genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
-    rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
+    auto storeOp = rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
+    this->attachTBAATag(storeOp, boxTy, boxTy, nullptr);
     return alloca;
   }
 };
@@ -1778,7 +1819,8 @@ struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
            "fir.embox codegen of derived with length parameters");
       return mlir::failure();
     }
-    auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest);
+    auto result =
+        placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), boxTy, dest);
     rewriter.replaceOp(embox, result);
     return mlir::success();
   }
@@ -1959,7 +2001,8 @@ struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> {
     if (isDerivedTypeWithLenParams(boxTy))
       TODO(loc, "fir.embox codegen of derived with length parameters");
 
-    mlir::Value result = placeInMemoryIfNotGlobalInit(rewriter, loc, dest);
+    mlir::Value result =
+        placeInMemoryIfNotGlobalInit(rewriter, loc, boxTy, dest);
     rewriter.replaceOp(xbox, result);
     return mlir::success();
   }
@@ -2000,7 +2043,8 @@ struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
     llvm::SmallVector<mlir::Value, 2> lenParams;
     mlir::Type inputEleTy = getInputEleTy(rebox);
     if (auto charTy = inputEleTy.dyn_cast<fir::CharacterType>()) {
-      mlir::Value len = getElementSizeFromBox(loc, idxTy, loweredBox, rewriter);
+      mlir::Value len = getElementSizeFromBox(
+          loc, idxTy, rebox.getBox().getType(), loweredBox, rewriter);
       if (charTy.getFKind() != 1) {
         mlir::Value width =
             genConstantIndex(loc, idxTy, rewriter, charTy.getFKind());
@@ -2029,19 +2073,20 @@ struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
     const unsigned inputRank = rebox.getRank();
     for (unsigned dim = 0; dim < inputRank; ++dim) {
       llvm::SmallVector<mlir::Value, 3> dimInfo =
-          getDimsFromBox(loc, {idxTy, idxTy, idxTy}, loweredBox, dim, rewriter);
+          getDimsFromBox(loc, {idxTy, idxTy, idxTy}, rebox.getBox().getType(),
+                         loweredBox, dim, rewriter);
       inputExtents.emplace_back(dimInfo[1]);
       inputStrides.emplace_back(dimInfo[2]);
     }
 
     mlir::Type baseTy = getBaseAddrTypeFromBox(loweredBox.getType());
-    mlir::Value baseAddr =
-        getBaseAddrFromBox(loc, baseTy, loweredBox, rewriter);
+    mlir::Value baseAddr = getBaseAddrFromBox(
+        loc, baseTy, rebox.getBox().getType(), loweredBox, rewriter);
 
     if (!rebox.getSlice().empty() || !rebox.getSubcomponent().empty())
-      return sliceBox(rebox, dest, baseAddr, inputExtents, inputStrides,
+      return sliceBox(rebox, boxTy, dest, baseAddr, inputExtents, inputStrides,
                       operands, rewriter);
-    return reshapeBox(rebox, dest, baseAddr, inputExtents, inputStrides,
+    return reshapeBox(rebox, boxTy, dest, baseAddr, inputExtents, inputStrides,
                       operands, rewriter);
   }
 
@@ -2049,9 +2094,9 @@ struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
   /// Write resulting shape and base address in descriptor, and replace rebox
   /// op.
   mlir::LogicalResult
-  finalizeRebox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
-                mlir::ValueRange lbounds, mlir::ValueRange extents,
-                mlir::ValueRange strides,
+  finalizeRebox(fir::cg::XReboxOp rebox, mlir::Type destBoxTy, mlir::Value dest,
+                mlir::Value base, mlir::ValueRange lbounds,
+                mlir::ValueRange extents, mlir::ValueRange strides,
                 mlir::ConversionPatternRewriter &rewriter) const {
     mlir::Location loc = rebox.getLoc();
     mlir::Value zero =
@@ -2073,16 +2118,16 @@ struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
     }
     dest = insertBaseAddress(rewriter, loc, dest, base);
     mlir::Value result =
-        placeInMemoryIfNotGlobalInit(rewriter, rebox.getLoc(), dest);
+        placeInMemoryIfNotGlobalInit(rewriter, rebox.getLoc(), destBoxTy, dest);
     rewriter.replaceOp(rebox, result);
     return mlir::success();
   }
 
   // Apply slice given the base address, extents and strides of the input box.
   mlir::LogicalResult
-  sliceBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
-           mlir::ValueRange inputExtents, mlir::ValueRange inputStrides,
-           mlir::ValueRange operands,
+  sliceBox(fir::cg::XReboxOp rebox, mlir::Type destBoxTy, mlir::Value dest,
+           mlir::Value base, mlir::ValueRange inputExtents,
+           mlir::ValueRange inputStrides, mlir::ValueRange operands,
            mlir::ConversionPatternRewriter &rewriter) const {
     mlir::Location loc = rebox.getLoc();
     mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext());
@@ -2110,8 +2155,9 @@ struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
     if (rebox.getSlice().empty())
       // The array section is of the form array[%component][substring], keep
       // the input array extents and strides.
-      return finalizeRebox(rebox, dest, base, /*lbounds*/ std::nullopt,
-                           inputExtents, inputStrides, rewriter);
+      return finalizeRebox(rebox, destBoxTy, dest, base,
+                           /*lbounds*/ std::nullopt, inputExtents, inputStrides,
+                           rewriter);
 
     // Strides from the fir.box are in bytes.
     base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
@@ -2160,24 +2206,24 @@ struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
         slicedStrides.emplace_back(stride);
       }
     }
-    return finalizeRebox(rebox, dest, base, /*lbounds*/ std::nullopt,
+    return finalizeRebox(rebox, destBoxTy, dest, base, /*lbounds*/ std::nullopt,
                          slicedExtents, slicedStrides, rewriter);
   }
 
   /// Apply a new shape to the data described by a box given the base address,
   /// extents and strides of the box.
   mlir::LogicalResult
-  reshapeBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
-             mlir::ValueRange inputExtents, mlir::ValueRange inputStrides,
-             mlir::ValueRange operands,
+  reshapeBox(fir::cg::XReboxOp rebox, mlir::Type destBoxTy, mlir::Value dest,
+             mlir::Value base, mlir::ValueRange inputExtents,
+             mlir::ValueRange inputStrides, mlir::ValueRange operands,
              mlir::ConversionPatternRewriter &rewriter) const {
     mlir::ValueRange reboxShifts{operands.begin() + rebox.shiftOffset(),
                                  operands.begin() + rebox.shiftOffset() +
                                      rebox.getShift().size()};
     if (rebox.getShape().empty()) {
       // Only setting new lower bounds.
-      return finalizeRebox(rebox, dest, base, reboxShifts, inputExtents,
-                           inputStrides, rewriter);
+      return finalizeRebox(rebox, destBoxTy, dest, base, reboxShifts,
+                           inputExtents, inputStrides, rewriter);
     }
 
     mlir::Location loc = rebox.getLoc();
@@ -2203,8 +2249,8 @@ struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
       // nextStride = extent * stride;
       stride = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, extent, stride);
     }
-    return finalizeRebox(rebox, dest, base, reboxShifts, newExtents, newStrides,
-                         rewriter);
+    return finalizeRebox(rebox, destBoxTy, dest, base, reboxShifts, newExtents,
+                         newStrides, rewriter);
   }
 
   /// Return scalar element type of the input box.
@@ -2443,7 +2489,8 @@ struct XArrayCoorOpConversion
       // that was just computed.
       if (baseIsBoxed) {
         // Use stride in bytes from the descriptor.
-        mlir::Value stride = getStrideFromBox(loc, operands[0], i, rewriter);
+        mlir::Value stride = getStrideFromBox(loc, coor.getMemref().getType(),
+                                              operands[0], i, rewriter);
         auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, 
diff , stride);
         offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset);
       } else {
@@ -2463,7 +2510,8 @@ struct XArrayCoorOpConversion
       // Working with byte offsets. The base address is read from the fir.box.
       // and need to be casted to i8* to do the pointer arithmetic.
       mlir::Type baseTy = getBaseAddrTypeFromBox(operands[0].getType());
-      mlir::Value base = getBaseAddrFromBox(loc, baseTy, operands[0], rewriter);
+      mlir::Value base = getBaseAddrFromBox(
+          loc, baseTy, coor.getMemref().getType(), operands[0], rewriter);
       mlir::Type voidPtrTy = getVoidPtrType();
       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
       llvm::SmallVector<mlir::LLVM::GEPArg> args{offset};
@@ -2675,7 +2723,7 @@ struct CoordinateOpConversion
     //  implementation might convert.
     mlir::Value resultAddr =
         getBaseAddrFromBox(loc, getBaseAddrTypeFromBox(boxBaseAddr.getType()),
-                           boxBaseAddr, rewriter);
+                           boxObjTy, boxBaseAddr, rewriter);
     // Component Type
     auto cpnTy = fir::dyn_cast_ptrOrBoxEleTy(boxObjTy);
     mlir::Type voidPtrTy = ::getVoidPtrType(coor.getContext());
@@ -2693,7 +2741,7 @@ struct CoordinateOpConversion
         for (unsigned index = i, lastIndex = i + arrTy.getDimension();
              index < lastIndex; ++index) {
           mlir::Value stride =
-              getStrideFromBox(loc, operands[0], index - i, rewriter);
+              getStrideFromBox(loc, boxObjTy, operands[0], index - i, rewriter);
           auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy,
                                                        operands[index], stride);
           off = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, off);
@@ -3022,14 +3070,19 @@ struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
       auto boxValue = rewriter.create<mlir::LLVM::LoadOp>(
           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
           inputBoxStorage);
+      attachTBAATag(boxValue, boxTy, boxTy, nullptr);
       auto newBoxStorage =
           genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
-      rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, newBoxStorage);
+      auto storeOp =
+          rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, newBoxStorage);
+      attachTBAATag(storeOp, boxTy, boxTy, nullptr);
       rewriter.replaceOp(load, newBoxStorage.getResult());
     } else {
-      rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
-          load, convertType(load.getType()), adaptor.getOperands(),
-          load->getAttrs());
+      mlir::Type loadTy = convertType(load.getType());
+      auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(
+          load.getLoc(), loadTy, adaptor.getOperands(), load->getAttrs());
+      attachTBAATag(loadOp, load.getType(), load.getType(), nullptr);
+      rewriter.replaceOp(load, loadOp.getResult());
     }
     return mlir::success();
   }
@@ -3256,19 +3309,24 @@ struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
   mlir::LogicalResult
   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
                   mlir::ConversionPatternRewriter &rewriter) const override {
-    if (store.getValue().getType().isa<fir::BaseBoxType>()) {
+    mlir::Location loc = store.getLoc();
+    mlir::Type storeTy = store.getValue().getType();
+    mlir::LLVM::StoreOp newStoreOp;
+    if (auto boxTy = storeTy.dyn_cast<fir::BaseBoxType>()) {
       // fir.box value is actually in memory, load it first before storing it.
-      mlir::Location loc = store.getLoc();
       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
       auto val = rewriter.create<mlir::LLVM::LoadOp>(
           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
           adaptor.getOperands()[0]);
-      rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
-          store, val, adaptor.getOperands()[1]);
+      attachTBAATag(val, boxTy, boxTy, nullptr);
+      newStoreOp = rewriter.create<mlir::LLVM::StoreOp>(
+          loc, val, adaptor.getOperands()[1]);
     } else {
-      rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
-          store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
+      newStoreOp = rewriter.create<mlir::LLVM::StoreOp>(
+          loc, adaptor.getOperands()[0], adaptor.getOperands()[1]);
     }
+    attachTBAATag(newStoreOp, storeTy, storeTy, nullptr);
+    rewriter.eraseOp(store);
     return mlir::success();
   }
 };
@@ -3708,7 +3766,8 @@ class FIRToLLVMLowering
     }
 
     auto *context = getModule().getContext();
-    fir::LLVMTypeConverter typeConverter{getModule()};
+    fir::LLVMTypeConverter typeConverter{getModule(),
+                                         options.applyTBAA || applyTBAA};
     mlir::RewritePatternSet pattern(context);
     pattern.insert<
         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,

diff  --git a/flang/lib/Optimizer/CodeGen/DescriptorModel.h b/flang/lib/Optimizer/CodeGen/DescriptorModel.h
index 0592fea62d153..964e18885e7fb 100644
--- a/flang/lib/Optimizer/CodeGen/DescriptorModel.h
+++ b/flang/lib/Optimizer/CodeGen/DescriptorModel.h
@@ -25,6 +25,7 @@
 #include "flang/ISO_Fortran_binding.h"
 #include "flang/Runtime/descriptor.h"
 #include "mlir/Dialect/LLVMIR/LLVMTypes.h"
+#include "mlir/IR/BuiltinTypes.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <tuple>
 

diff  --git a/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp b/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
new file mode 100644
index 0000000000000..1bee674f748fe
--- /dev/null
+++ b/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
@@ -0,0 +1,144 @@
+//===-- TBAABuilder.cpp -- TBAA builder definitions -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#include "TBAABuilder.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace mlir;
+using namespace mlir::LLVM;
+
+static llvm::cl::opt<bool> disableTBAA(
+    "disable-tbaa",
+    llvm::cl::desc("disable attaching TBAA tags to memory accessing operations "
+                   "to override default Flang behavior"),
+    llvm::cl::init(false));
+
+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)
+    : 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;
+  }
+
+  // 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);
+
+  // 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);
+
+  // 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);
+
+  // 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);
+}
+
+SymbolRefAttr TBAABuilder::getAccessTag(SymbolRefAttr baseTypeDesc,
+                                        SymbolRefAttr accessTypeDesc,
+                                        int64_t offset) {
+  SymbolRefAttr &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));
+  return tag;
+}
+
+SymbolRefAttr TBAABuilder::getAnyBoxAccessTag() {
+  return getAccessTag(boxMemberTypeDesc, boxMemberTypeDesc, /*offset=*/0);
+}
+
+SymbolRefAttr TBAABuilder::getBoxAccessTag(Type baseFIRType, Type accessFIRType,
+                                           GEPOp gep) {
+  return getAnyBoxAccessTag();
+}
+
+SymbolRefAttr TBAABuilder::getAnyDataAccessTag() {
+  return getAccessTag(anyDataAccessTypeDesc, anyDataAccessTypeDesc,
+                      /*offset=*/0);
+}
+
+SymbolRefAttr TBAABuilder::getDataAccessTag(Type baseFIRType,
+                                            Type accessFIRType, GEPOp gep) {
+  return getAnyDataAccessTag();
+}
+
+void TBAABuilder::attachTBAATag(Operation *op, Type baseFIRType,
+                                Type accessFIRType, GEPOp gep) {
+  if (!enableTBAA)
+    return;
+
+  SymbolRefAttr tbaaTagSym;
+  if (baseFIRType.isa<fir::BaseBoxType>())
+    tbaaTagSym = getBoxAccessTag(baseFIRType, accessFIRType, gep);
+  else
+    tbaaTagSym = getDataAccessTag(baseFIRType, accessFIRType, gep);
+
+  if (tbaaTagSym)
+    op->setAttr(LLVMDialect::getTBAAAttrName(),
+                ArrayAttr::get(op->getContext(), tbaaTagSym));
+}
+
+} // namespace fir

diff  --git a/flang/lib/Optimizer/CodeGen/TBAABuilder.h b/flang/lib/Optimizer/CodeGen/TBAABuilder.h
new file mode 100644
index 0000000000000..dff924bb2a202
--- /dev/null
+++ b/flang/lib/Optimizer/CodeGen/TBAABuilder.h
@@ -0,0 +1,268 @@
+//===-- TBAABuilder.h -- TBAA builder declarations --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_OPTIMIZER_CODEGEN_TBAABUILDER_H
+#define FORTRAN_OPTIMIZER_CODEGEN_TBAABUILDER_H
+
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/BuiltinAttributes.h"
+
+namespace fir {
+
+// TBAA builder provides mapping between FIR types and their TBAA type
+// descriptors, and methods to populate that mapping during FIR to LLVM
+// type conversion and to attach llvm.tbaa attributes to memory access
+// instructions.
+//
+// TBAA type information is represented with LLVM::MetadataOp operation
+// with specific symbol name `TBAABuilder::tbaaMetaOpName`. The basic
+// TBAA tree used for Flang consists of the following nodes:
+//   llvm.metadata @__flang_tbaa {
+//     llvm.tbaa_root @root_0 {id = "Flang Type TBAA Root"}
+//     llvm.tbaa_type_desc @type_desc_1 {id = "any access",
+//                                       members = {<@root_0, 0>}}
+//     llvm.tbaa_type_desc @type_desc_2 {id = "any data access",
+//                                       members = {<@type_desc_1, 0>}}
+//     llvm.tbaa_type_desc @type_desc_3 {id = "descriptor member",
+//                                       members = {<@type_desc_1, 0>}}
+//   }
+//
+// The `<any data access>` and `<descriptor member>` type descriptors
+// are two sub-roots of the basic TBAA tree, and they allow representing
+// box and non-box accesses, which can never alias in the current Flang
+// implementation. The `<any access>` type descriptor is their common parent
+// that can be used for representing accesses that may alias box and non-box
+// accesses if an access cannot be classified strictly as box or non-box.
+// In the current implementation `<any access>` is not used by TBAA access tags,
+// because it is always known whether an operation accesses box or non-box.
+//
+// Given this basic TBAA tree structure, the box/descriptor types may
+// be represented like this:
+//   llvm.tbaa_type_desc @type_desc_4 {
+//       id = "CFI_cdesc_t_dim0",
+//       members = {<@type_desc_3, 0>, // base_addr
+//                  <@type_desc_3, 8>, // elem_len
+//                  <@type_desc_3, 16>, // version
+//                  <@type_desc_3, 20>, // rank
+//                  <@type_desc_3, 21>, // type
+//                  <@type_desc_3, 22>, // attribute
+//                  <@type_desc_3, 23>} // f18Addendum
+//   }
+//   llvm.tbaa_type_desc @type_desc_5 {
+//       id = "CFI_cdesc_t_dim1",
+//       members = {<@type_desc_3, 0>, // base_addr
+//                  <@type_desc_3, 8>, // elem_len
+//                  <@type_desc_3, 16>, // version
+//                  <@type_desc_3, 20>, // rank
+//                  <@type_desc_3, 21>, // type
+//                  <@type_desc_3, 22>, // attribute
+//                  <@type_desc_3, 23>, // f18Addendum
+//                  <@type_desc_3, 24>, // dim[0].lower_bound
+//                  <@type_desc_3, 32>, // dim[0].extent
+//                  <@type_desc_3, 40>} // dim[0].sm
+//   }
+//   llvm.tbaa_type_desc @type_desc_6 {
+//       id = "CFI_cdesc_t_dim2",
+//       members = {<@type_desc_3, 0>, // base_addr
+//                  <@type_desc_3, 8>, // elem_len
+//                  <@type_desc_3, 16>, // version
+//                  <@type_desc_3, 20>, // rank
+//                  <@type_desc_3, 21>, // type
+//                  <@type_desc_3, 22>, // attribute
+//                  <@type_desc_3, 23>, // f18Addendum
+//                  <@type_desc_3, 24>, // dim[0].lower_bound
+//                  <@type_desc_3, 32>, // dim[0].extent
+//                  <@type_desc_3, 40>, // dim[0].sm
+//                  <@type_desc_3, 48>, // dim[1].lower_bound
+//                  <@type_desc_3, 56>, // dim[1].extent
+//                  <@type_desc_3, 64>} // dim[1].sm
+//   }
+// etc.
+//
+// Note that the TBAA type descriptors cannot represent array members
+// of structures, so the `dim` array in the descriptor structure
+// has to be represented as linear set of members.
+//
+// We can use the same TBAA type descriptor for all members of the F18
+// descriptor structure, because the actual accesses of the F18 descriptor
+// members will be disambiguated based on their offset off the beginning
+// of the descriptor. Thus, all members have the same `<descriptor member>`
+// type in the TBAA graph.
+//
+// The TBAA type descriptors have to be created during FIR to LLVM type
+// conversion, so fir::LLVMTypeConverter has to provide the member offsets
+// to TBAABuilder - the offsets must be computed based on the LLVM type
+// to which the FIR type is being converted.
+//
+// TBAABuilder keeps a map between the FIR type and its TBAA type descriptor.
+// The map is used when a TBAA tag needs to be attached to a memory accessing
+// operation given the FIR types identifying the access's base and access type
+// and the offset within the base type, e.g. an access of one dimensional
+// descriptor's `base_addr` member may be defined by:
+//   * base FIR type: !fir.box<!fir.array<?xf32>> - the resulting
+//     access tag will use `<CFI_cdesc_t_dim1>` type descriptor for the base
+//     type.
+//   * access FIR type: <undefined> - all accesses within descriptors
+//     are always represented with `<descriptor member>` type descriptor.
+//   * offset:
+//       llvm.getelementptr %arg0[0, 0] :
+//           (!llvm.ptr<struct<(ptr<f32>, i64, i32, i8, i8, i8, i8,
+//                              array<1 x array<3 x i64>>)>>) ->
+//           !llvm.ptr<ptr<f32>>
+//     The offset is computed based on the LLVM::GEPOp's indices and the LLVM
+//     type layout.
+//
+// Detailed representation of the layout of the F18 descriptors is required
+// to disambiguate accesses of the 
diff erent members of the descriptors,
+// e.g. a read of `base_addr` member (of one box) can never alias with
+// a write of `rank` member (of another box).
+//
+// TODO: define handling of assumed-rank arrays' boxes (they can always
+// be represented with a conservative tag:
+//   < `<descriptor member>`, `<descriptor member>`, 0 >
+// so that they alias with any other box accesses.
+//
+// The same representation can be used for user-defined types, though,
+// strict type aliasing cannot be applied for Fortran programs without
+// additional guarantees from the user. Fortran's storage association
+// constructs provide a way to alias data of 
diff erent types, so using
+// TBAA would be incorrect, e.g.:
+//   subroutine test()
+//     real :: a
+//     integer :: b
+//     equivalence (a, b)
+//     a = 1.0
+//     call test2(b)
+//   end subroutine test
+//
+// The store operation for `a = 1.0` has the base/access type `f32`,
+// while a load from `b` inside `test2` will have base/access type
+// `i32`. Due to the storage association the store and the load alias,
+// so using the access types to create TBAA access tags may result
+// in an incorrect result if `test2` was inlined. Moreover, in the scope
+// of `test2` Flang is not able to indentify whether `b` is part
+// of an equivalence.
+//
+// TBAA may still be applied for programs not using storage association
+// for objects of 
diff erent data types (e.g. under an opt-in compiler option).
+//
+// The initial implementation does not create detailed type descriptors
+// for box types and always uses the conservative box access tag:
+//   < `<descriptor member>`, `<descriptor member>`, 0 >
+//
+// Given the storage association, all non-box accesses are represented
+// with the conservative data access tag:
+//   < `<any data access>`, `<any data access>`, 0 >
+class TBAABuilder {
+public:
+  TBAABuilder(mlir::ModuleOp module, bool applyTBAA);
+  TBAABuilder(TBAABuilder const &) = delete;
+  TBAABuilder &operator=(TBAABuilder const &) = delete;
+
+  // Attach the llvm.tbaa attribute to the given memory accessing operation
+  // based on the provided base/access FIR types and the GEPOp.
+  void attachTBAATag(mlir::Operation *op, mlir::Type baseFIRType,
+                     mlir::Type accessFIRType, 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:
+  //   < <descriptor member>, <descriptor member>, 0 >
+  mlir::SymbolRefAttr getAnyBoxAccessTag();
+  // Returns symbol of TBAATagOp 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);
+
+  // 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;
+  // 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;
+  // 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;
+  // 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;
+  // 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;
+
+  // 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;
+
+  // 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>
+      tagsMap;
+};
+
+} // namespace fir
+
+#endif // FORTRAN_OPTIMIZER_CODEGEN_TBAABUILDER_H

diff  --git a/flang/lib/Optimizer/CodeGen/TypeConverter.h b/flang/lib/Optimizer/CodeGen/TypeConverter.h
index 4ddc073a31ca9..be8843b44f44f 100644
--- a/flang/lib/Optimizer/CodeGen/TypeConverter.h
+++ b/flang/lib/Optimizer/CodeGen/TypeConverter.h
@@ -14,6 +14,7 @@
 #define FORTRAN_OPTIMIZER_CODEGEN_TYPECONVERTER_H
 
 #include "DescriptorModel.h"
+#include "TBAABuilder.h"
 #include "Target.h"
 #include "flang/Optimizer/Builder/Todo.h" // remove when TODO's are done
 #include "flang/Optimizer/Dialect/FIRType.h"
@@ -45,12 +46,13 @@ namespace fir {
 /// This converts FIR types to LLVM types (for now)
 class LLVMTypeConverter : public mlir::LLVMTypeConverter {
 public:
-  LLVMTypeConverter(mlir::ModuleOp module)
+  LLVMTypeConverter(mlir::ModuleOp module, bool applyTBAA)
       : mlir::LLVMTypeConverter(module.getContext()),
         kindMapping(getKindMapping(module)),
         specifics(CodeGenSpecifics::get(module.getContext(),
                                         getTargetTriple(module),
-                                        getKindMapping(module))) {
+                                        getKindMapping(module))),
+        tbaaBuilder(module, applyTBAA) {
     LLVM_DEBUG(llvm::dbgs() << "FIR type converter\n");
 
     // Each conversion should return a value of type mlir::Type.
@@ -273,6 +275,8 @@ class LLVMTypeConverter : public mlir::LLVMTypeConverter {
               mlir::LLVM::LLVMArrayType::get(rowTy, numLenParams));
         }
     }
+    // TODO: send the box type and the converted LLVM structure layout
+    // to tbaaBuilder for proper creation of TBAATypeDescriptorOp.
     return mlir::LLVM::LLVMPointerType::get(
         mlir::LLVM::LLVMStructType::getLiteral(&getContext(), dataDescFields,
                                                /*isPacked=*/false));
@@ -385,9 +389,16 @@ class LLVMTypeConverter : public mlir::LLVMTypeConverter {
 
   KindMapping &getKindMap() { return kindMapping; }
 
+  // Relay TBAA tag attachment to TBAABuilder.
+  void attachTBAATag(mlir::Operation *op, mlir::Type baseFIRType,
+                     mlir::Type accessFIRType, mlir::LLVM::GEPOp gep) {
+    tbaaBuilder.attachTBAATag(op, baseFIRType, accessFIRType, gep);
+  }
+
 private:
   KindMapping kindMapping;
   std::unique_ptr<CodeGenSpecifics> specifics;
+  TBAABuilder tbaaBuilder;
 };
 
 } // namespace fir

diff  --git a/flang/test/Fir/tbaa.fir b/flang/test/Fir/tbaa.fir
new file mode 100644
index 0000000000000..ac94ea94cb3cf
--- /dev/null
+++ b/flang/test/Fir/tbaa.fir
@@ -0,0 +1,373 @@
+// RUN: fir-opt %s --split-input-file --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu apply-tbaa=true" | FileCheck %s
+// RUN: fir-opt %s --split-input-file --fir-to-llvm-ir="target=aarch64-unknown-linux-gnu apply-tbaa=true" | FileCheck %s
+
+module {
+  func.func @tbaa(%arg0: !fir.class<!fir.array<?xnone>> {fir.bindc_name = "a"}) {
+    %c9_i8 = arith.constant 9 : i8
+    %c0_i64 = arith.constant 0 : i64
+    %c10_i32 = arith.constant 10 : i32
+    %0 = fir.coordinate_of %arg0, %c0_i64 : (!fir.class<!fir.array<?xnone>>, i64) -> !fir.ref<none>
+    %1 = fir.embox %0 source_box %arg0 : (!fir.ref<none>, !fir.class<!fir.array<?xnone>>) -> !fir.class<none>
+    %2 = fir.box_typecode %1 : (!fir.class<none>) -> i8
+    %3 = arith.cmpi eq, %2, %c9_i8 : i8
+    cf.cond_br %3, ^bb1, ^bb2
+  ^bb1:  // pred: ^bb0
+    %4 = fir.box_addr %1 : (!fir.class<none>) -> !fir.ref<i32>
+    fir.store %c10_i32 to %4 : !fir.ref<i32>
+    cf.br ^bb2
+  ^bb2:  // 2 preds: ^bb0, ^bb1
+    return
+  }
+}
+
+// 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
+// CHECK:           %[[VAL_2:.*]] = llvm.alloca %[[VAL_1]] x !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)> {alignment = 8 : i64} : (i32) -> !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>>
+// CHECK:           %[[VAL_3:.*]] = llvm.mlir.constant(9 : i8) : i8
+// 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]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !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]] {llvm.tbaa = [@__flang_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>
+// CHECK:           %[[VAL_14:.*]] = llvm.getelementptr %[[VAL_13]]{{\[}}%[[VAL_12]]] : (!llvm.ptr<i8>, i64) -> !llvm.ptr<i8>
+// CHECK:           %[[VAL_15:.*]] = llvm.bitcast %[[VAL_14]] : !llvm.ptr<i8> to !llvm.ptr<struct<()>>
+// 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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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
+// CHECK:           %[[VAL_27:.*]] = llvm.insertvalue %[[VAL_26]], %[[VAL_25]][2] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
+// CHECK:           %[[VAL_28:.*]] = llvm.mlir.constant(0 : i32) : i32
+// CHECK:           %[[VAL_29:.*]] = llvm.trunc %[[VAL_28]] : i32 to i8
+// CHECK:           %[[VAL_30:.*]] = llvm.insertvalue %[[VAL_29]], %[[VAL_27]][3] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
+// CHECK:           %[[VAL_31:.*]] = llvm.trunc %[[VAL_23]] : i32 to i8
+// CHECK:           %[[VAL_32:.*]] = llvm.insertvalue %[[VAL_31]], %[[VAL_30]][4] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
+// CHECK:           %[[VAL_33:.*]] = llvm.mlir.constant(0 : i32) : i32
+// CHECK:           %[[VAL_34:.*]] = llvm.trunc %[[VAL_33]] : i32 to i8
+// CHECK:           %[[VAL_35:.*]] = llvm.insertvalue %[[VAL_34]], %[[VAL_32]][5] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
+// CHECK:           %[[VAL_36:.*]] = llvm.mlir.constant(1 : i32) : i32
+// CHECK:           %[[VAL_37:.*]] = llvm.trunc %[[VAL_36]] : i32 to i8
+// CHECK:           %[[VAL_38:.*]] = llvm.insertvalue %[[VAL_37]], %[[VAL_35]][6] : !llvm.struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>
+// CHECK:           %[[VAL_39:.*]] = llvm.bitcast %[[VAL_19]] : !llvm.ptr<i8> to !llvm.ptr<i8>
+// 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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr<ptr<i32>>
+// CHECK:           llvm.store %[[VAL_5]], %[[VAL_47]] {llvm.tbaa = [@__flang_tbaa::@[[DATAT:tag_[0-9]*]]]} : !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 {
+  func.func @tbaa() {
+    %c0 = arith.constant 0 : index
+    %c8_i32 = arith.constant 8 : i32
+    %c-1_i32 = arith.constant -1 : i32
+    %0 = fir.address_of(@_QFEx) : !fir.ref<!fir.class<!fir.ptr<!fir.array<?xnone>>>>
+    %1 = fir.address_of(@_QQcl.2E2F64756D6D792E66393000) : !fir.ref<!fir.char<1,12>>
+    %2 = fir.convert %1 : (!fir.ref<!fir.char<1,12>>) -> !fir.ref<i8>
+    %3 = fir.call @_FortranAioBeginExternalListOutput(%c-1_i32, %2, %c8_i32) fastmath<contract> : (i32, !fir.ref<i8>, i32) -> !fir.ref<i8>
+    %4 = fir.load %0 : !fir.ref<!fir.class<!fir.ptr<!fir.array<?xnone>>>>
+    %5:3 = fir.box_dims %4, %c0 : (!fir.class<!fir.ptr<!fir.array<?xnone>>>, index) -> (index, index, index)
+    %6 = fircg.ext_rebox %4 origin %5#0 : (!fir.class<!fir.ptr<!fir.array<?xnone>>>, index) -> !fir.class<!fir.array<?xnone>>
+    %7 = fir.convert %6 : (!fir.class<!fir.array<?xnone>>) -> !fir.box<none>
+    %8 = fir.call @_FortranAioOutputDescriptor(%3, %7) fastmath<contract> : (!fir.ref<i8>, !fir.box<none>) -> i1
+    %9 = fir.call @_FortranAioEndIoStatement(%3) fastmath<contract> : (!fir.ref<i8>) -> i32
+    return
+  }
+  func.func private @_FortranAioBeginExternalListOutput(i32, !fir.ref<i8>, i32) -> !fir.ref<i8> attributes {fir.io, fir.runtime}
+  func.func private @_FortranAioOutputDescriptor(!fir.ref<i8>, !fir.box<none>) -> i1 attributes {fir.io, fir.runtime}
+  func.func private @_FortranAioEndIoStatement(!fir.ref<i8>) -> i32 attributes {fir.io, fir.runtime}
+  fir.global linkonce @_QQcl.2E2F64756D6D792E66393000 constant : !fir.char<1,12> {
+    %0 = fir.string_lit "./dummy.f90\00"(12) : !fir.char<1,12>
+    fir.has_value %0 : !fir.char<1,12>
+  }
+  fir.global internal @_QFEx : !fir.class<!fir.ptr<!fir.array<?xnone>>> {
+    %c0 = arith.constant 0 : index
+    %0 = fir.zero_bits !fir.ptr<!fir.array<?xnone>>
+    %1 = fircg.ext_embox %0(%c0) : (!fir.ptr<!fir.array<?xnone>>, index) -> !fir.class<!fir.ptr<!fir.array<?xnone>>>
+    fir.has_value %1 : !fir.class<!fir.ptr<!fir.array<?xnone>>>
+  }
+}
+
+// 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>)>>
+// CHECK:           %[[VAL_2:.*]] = llvm.mlir.constant(1 : i32) : i32
+// CHECK:           %[[VAL_3:.*]] = llvm.alloca %[[VAL_2]] 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>)>>
+// CHECK:           %[[VAL_4:.*]] = llvm.mlir.constant(0 : index) : i64
+// CHECK:           %[[VAL_5:.*]] = llvm.mlir.constant(8 : i32) : i32
+// CHECK:           %[[VAL_6:.*]] = llvm.mlir.constant(-1 : i32) : i32
+// CHECK:           %[[VAL_7:.*]] = llvm.mlir.addressof @_QFEx : !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_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]] {llvm.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]] {llvm.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_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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
+// CHECK:           %[[VAL_31:.*]] = llvm.insertvalue %[[VAL_30]], %[[VAL_29]][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_32:.*]] = llvm.mlir.constant(1 : i32) : i32
+// CHECK:           %[[VAL_33:.*]] = llvm.trunc %[[VAL_32]] : i32 to i8
+// CHECK:           %[[VAL_34:.*]] = llvm.insertvalue %[[VAL_33]], %[[VAL_31]][3] : !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_35:.*]] = llvm.trunc %[[VAL_25]] : i32 to i8
+// CHECK:           %[[VAL_36:.*]] = llvm.insertvalue %[[VAL_35]], %[[VAL_34]][4] : !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_37:.*]] = llvm.mlir.constant(0 : i32) : i32
+// CHECK:           %[[VAL_38:.*]] = llvm.trunc %[[VAL_37]] : i32 to i8
+// CHECK:           %[[VAL_39:.*]] = llvm.insertvalue %[[VAL_38]], %[[VAL_36]][5] : !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_40:.*]] = llvm.mlir.constant(1 : i32) : i32
+// CHECK:           %[[VAL_41:.*]] = llvm.trunc %[[VAL_40]] : i32 to i8
+// CHECK:           %[[VAL_42:.*]] = llvm.insertvalue %[[VAL_41]], %[[VAL_39]][6] : !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_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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]] {llvm.tbaa = [@__flang_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
+// CHECK:           %[[VAL_56:.*]] = llvm.select %[[VAL_55]], %[[VAL_54]], %[[VAL_13]] : i1, i64
+// CHECK:           %[[VAL_57:.*]] = llvm.insertvalue %[[VAL_56]], %[[VAL_44]][7, 0, 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:           %[[VAL_58:.*]] = llvm.insertvalue %[[VAL_48]], %[[VAL_57]][7, 0, 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_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]] {llvm.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_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
+// CHECK:           llvm.return
+// CHECK:         }
+// CHECK:         llvm.func @_FortranAioBeginExternalListOutput(i32, !llvm.ptr<i8>, i32) -> !llvm.ptr<i8> attributes {fir.io, fir.runtime, sym_visibility = "private"}
+// CHECK:         llvm.func @_FortranAioOutputDescriptor(!llvm.ptr<i8>, !llvm.ptr<struct<(ptr<struct<()>>, i64, i32, i8, i8, i8, i8, ptr<i8>, array<1 x i64>)>>) -> i1 attributes {fir.io, fir.runtime, sym_visibility = "private"}
+// CHECK:         llvm.func @_FortranAioEndIoStatement(!llvm.ptr<i8>) -> i32 attributes {fir.io, fir.runtime, sym_visibility = "private"}
+
+// CHECK-LABEL:   llvm.mlir.global linkonce constant @_QQcl.2E2F64756D6D792E66393000() {addr_space = 0 : i32} : !llvm.array<12 x i8> {
+// CHECK:           %[[VAL_0:.*]] = llvm.mlir.constant("./dummy.f90\00") : !llvm.array<12 x i8>
+// CHECK:           llvm.return %[[VAL_0]] : !llvm.array<12 x i8>
+// CHECK:         }
+
+// CHECK-LABEL:   llvm.mlir.global internal @_QFEx() {addr_space = 0 : i32} : !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_0:.*]] = llvm.mlir.constant(0 : index) : i64
+// CHECK:           %[[VAL_1:.*]] = llvm.mlir.null : !llvm.ptr<struct<()>>
+// CHECK:           %[[VAL_2:.*]] = llvm.mlir.constant(0 : i64) : i64
+// CHECK:           %[[VAL_3:.*]] = llvm.mlir.constant(-1 : i32) : i32
+// CHECK:           %[[VAL_4:.*]] = 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_5:.*]] = llvm.insertvalue %[[VAL_2]], %[[VAL_4]][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_6:.*]] = llvm.mlir.constant(20180515 : i32) : i32
+// CHECK:           %[[VAL_7:.*]] = llvm.insertvalue %[[VAL_6]], %[[VAL_5]][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_8:.*]] = llvm.mlir.constant(1 : i32) : i32
+// CHECK:           %[[VAL_9:.*]] = llvm.trunc %[[VAL_8]] : i32 to i8
+// CHECK:           %[[VAL_10:.*]] = llvm.insertvalue %[[VAL_9]], %[[VAL_7]][3] : !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_11:.*]] = llvm.trunc %[[VAL_3]] : i32 to i8
+// CHECK:           %[[VAL_12:.*]] = llvm.insertvalue %[[VAL_11]], %[[VAL_10]][4] : !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_13:.*]] = llvm.mlir.constant(1 : i32) : i32
+// CHECK:           %[[VAL_14:.*]] = llvm.trunc %[[VAL_13]] : i32 to i8
+// CHECK:           %[[VAL_15:.*]] = llvm.insertvalue %[[VAL_14]], %[[VAL_12]][5] : !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_16:.*]] = llvm.mlir.constant(1 : i32) : i32
+// CHECK:           %[[VAL_17:.*]] = llvm.trunc %[[VAL_16]] : i32 to i8
+// CHECK:           %[[VAL_18:.*]] = llvm.insertvalue %[[VAL_17]], %[[VAL_15]][6] : !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_19:.*]] = llvm.mlir.null : !llvm.ptr<i8>
+// CHECK:           %[[VAL_20:.*]] = llvm.bitcast %[[VAL_19]] : !llvm.ptr<i8> to !llvm.ptr<i8>
+// CHECK:           %[[VAL_21:.*]] = llvm.insertvalue %[[VAL_20]], %[[VAL_18]][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_22:.*]] = llvm.mlir.constant(0 : i64) : i64
+// CHECK:           %[[VAL_23:.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK:           %[[VAL_24:.*]] = llvm.insertvalue %[[VAL_23]], %[[VAL_21]][7, 0, 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:           %[[VAL_25:.*]] = llvm.insertvalue %[[VAL_0]], %[[VAL_24]][7, 0, 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_26:.*]] = llvm.insertvalue %[[VAL_2]], %[[VAL_25]][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_27:.*]] = llvm.mul %[[VAL_2]], %[[VAL_0]]  : i64
+// CHECK:           %[[VAL_28:.*]] = llvm.mul %[[VAL_23]], %[[VAL_0]]  : i64
+// CHECK:           %[[VAL_29:.*]] = llvm.bitcast %[[VAL_1]] : !llvm.ptr<struct<()>> to !llvm.ptr<struct<()>>
+// CHECK:           %[[VAL_30:.*]] = llvm.insertvalue %[[VAL_29]], %[[VAL_26]][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.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 {
+  %0 = fir.box_rank %arg0 : (!fir.box<!fir.array<*:f64>>) -> i32
+  return %0 : i32
+}
+
+// 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]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !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 {
+  %0 = fir.box_isarray %arg0 : (!fir.box<!fir.array<*:f64>>) -> i1
+  return %0 : i1
+}
+
+// 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]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !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 {
+  %0 = fir.box_elesize %arg0 : (!fir.box<f32>) -> i32
+  return %0 : i32
+}
+
+// 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]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !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 {
+  %0 = fir.box_isalloc %arg0 : (!fir.box<!fir.array<*:f64>>) -> i1
+  return %0 : i1
+}
+
+// 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]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !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
+// CHECK:           %[[VAL_6:.*]] = llvm.icmp "ne" %[[VAL_4]], %[[VAL_5]] : i32
+// 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>>) {
+  %c0 = arith.constant 0 : i64
+  %1 = fircg.ext_array_coor %arg0(%c0) <%c0> : (!fir.box<!fir.array<?xi32>>, i64, i64) -> !fir.ref<i32>
+  return
+}
+
+// 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
+// CHECK:           %[[VAL_2:.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK:           %[[VAL_3:.*]] = llvm.mlir.constant(0 : i64) : i64
+// 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]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !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]] {llvm.tbaa = [@__flang_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:         }


        


More information about the flang-commits mailing list