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.">
];
}
// 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.
[&]() { 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); });
}
}
#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());
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
fir::createDefaultFIROptimizerPassPipeline(pm, optLevel);
// Add codegen pass pipeline.
- fir::createDefaultFIRCodeGenPassPipeline(pm);
+ fir::createDefaultFIRCodeGenPassPipeline(pm, optLevel);
}
#undef FLANG_EXCLUDE_CODEGEN
#endif
CGOps.cpp
CodeGen.cpp
PreCGRewrite.cpp
+ TBAABuilder.cpp
Target.cpp
TargetRewrite.cpp
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);
}
/// 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 "
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
/// 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 =
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;
};
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);
}
};
/// 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;
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();
}
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();
}
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();
}
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);
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();
}
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();
}
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();
}
.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]);
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(
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);
}
/// 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;
}
};
"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();
}
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();
}
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());
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);
}
/// 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 =
}
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());
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);
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();
// 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.
// 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 {
// 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};
// 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());
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);
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();
}
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();
}
};
}
auto *context = getModule().getContext();
- fir::LLVMTypeConverter typeConverter{getModule()};
+ fir::LLVMTypeConverter typeConverter{getModule(),
+ options.applyTBAA || applyTBAA};
mlir::RewritePatternSet pattern(context);
pattern.insert<
AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
#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>
--- /dev/null
+//===-- 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
--- /dev/null
+//===-- 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 different 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 different 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 different 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
#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"
/// 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.
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));
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
--- /dev/null
+// 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: }