Index: flang/include/flang/Optimizer/CodeGen/CGPasses.td =================================================================== --- flang/include/flang/Optimizer/CodeGen/CGPasses.td +++ flang/include/flang/Optimizer/CodeGen/CGPasses.td @@ -26,7 +26,9 @@ 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."> ]; } Index: flang/include/flang/Optimizer/CodeGen/CodeGen.h =================================================================== --- flang/include/flang/Optimizer/CodeGen/CodeGen.h +++ flang/include/flang/Optimizer/CodeGen/CodeGen.h @@ -51,6 +51,9 @@ // 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. Index: flang/include/flang/Tools/CLOptions.inc =================================================================== --- flang/include/flang/Tools/CLOptions.inc +++ flang/include/flang/Tools/CLOptions.inc @@ -133,9 +133,11 @@ [&]() { 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 @@ } #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( fir::createAbstractResultOnFuncOptPass()); @@ -206,7 +209,7 @@ 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 @@ fir::createDefaultFIROptimizerPassPipeline(pm, optLevel); // Add codegen pass pipeline. - fir::createDefaultFIRCodeGenPassPipeline(pm); + fir::createDefaultFIRCodeGenPassPipeline(pm, optLevel); } #undef FLANG_EXCLUDE_CODEGEN #endif Index: flang/lib/Optimizer/CodeGen/CMakeLists.txt =================================================================== --- flang/lib/Optimizer/CodeGen/CMakeLists.txt +++ flang/lib/Optimizer/CodeGen/CMakeLists.txt @@ -3,6 +3,7 @@ CGOps.cpp CodeGen.cpp PreCGRewrite.cpp + TBAABuilder.cpp Target.cpp TargetRewrite.cpp Index: flang/lib/Optimizer/CodeGen/CodeGen.cpp =================================================================== --- flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -159,16 +159,18 @@ 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()) { auto pty = mlir::LLVM::LLVMPointerType::get(resultTy); auto p = rewriter.create( loc, pty, box, llvm::ArrayRef{0, boxValue}); - return rewriter.create(loc, resultTy, p); + auto loadOp = rewriter.create(loc, resultTy, p); + attachTBAATag(loadOp, boxTy, nullptr, p); + return loadOp; } return rewriter.create(loc, box, boxValue); } @@ -177,27 +179,33 @@ /// from a box. llvm::SmallVector getDimsFromBox(mlir::Location loc, llvm::ArrayRef 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 getDimsFromBox(mlir::Location loc, llvm::ArrayRef 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() && "descriptor inquiry with runtime dim can only be done on descriptor " @@ -205,41 +213,50 @@ auto pty = mlir::LLVM::LLVMPointerType::get(ty); mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, 0, static_cast(kDimsPosInBox), dim, off); - return rewriter.create(loc, ty, p); + auto loadOp = rewriter.create(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()) { auto pty = mlir::LLVM::LLVMPointerType::get(ty); mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, 0, static_cast(kDimsPosInBox), dim, off); - return rewriter.create(loc, ty, p); + auto loadOp = rewriter.create(loc, ty, p); + attachTBAATag(loadOp, boxTy, nullptr, p); + return loadOp; } return rewriter.create( loc, box, llvm::ArrayRef{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 @@ /// 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 @@ return *static_cast(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 @@ auto loc = boxaddr.getLoc(); mlir::Type ty = convertType(boxaddr.getType()); if (auto argty = boxaddr.getVal().getType().dyn_cast()) { - rewriter.replaceOp(boxaddr, getBaseAddrFromBox(loc, ty, a, rewriter)); + rewriter.replaceOp(boxaddr, + getBaseAddrFromBox(loc, ty, argty, a, rewriter)); } else { rewriter.replaceOpWithNewOp(boxaddr, a, 0); } @@ -528,7 +553,7 @@ }; /// 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 { using FIROpConversion::FIROpConversion; @@ -541,9 +566,9 @@ 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 @@ 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 @@ 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 @@ 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( boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0); @@ -612,7 +638,8 @@ 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 @@ 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 @@ 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 @@ .getType() .dyn_cast(); + // 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( loc, descPtr.getElementType(), adaptor.getOperands()[0]); @@ -1591,9 +1628,10 @@ 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::Value descriptor = populateDescriptor( @@ -1621,9 +1659,10 @@ 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 @@ /// 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(loc, boxValue, alloca); + auto storeOp = rewriter.create(loc, boxValue, alloca); + this->attachTBAATag(storeOp, boxTy, boxTy, nullptr); return alloca; } }; @@ -1778,7 +1819,8 @@ "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 @@ 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 @@ llvm::SmallVector lenParams; mlir::Type inputEleTy = getInputEleTy(rebox); if (auto charTy = inputEleTy.dyn_cast()) { - 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 @@ const unsigned inputRank = rebox.getRank(); for (unsigned dim = 0; dim < inputRank; ++dim) { llvm::SmallVector 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 @@ /// 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 @@ } 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 @@ 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(loc, voidPtrTy, base); @@ -2160,24 +2206,24 @@ 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 @@ // nextStride = extent * stride; stride = rewriter.create(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 @@ // 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(loc, idxTy, diff, stride); offset = rewriter.create(loc, idxTy, sc, offset); } else { @@ -2463,7 +2510,8 @@ // 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(loc, voidPtrTy, base); llvm::SmallVector args{offset}; @@ -2675,7 +2723,7 @@ // 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 @@ 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(loc, idxTy, operands[index], stride); off = rewriter.create(loc, idxTy, sc, off); @@ -3022,14 +3070,19 @@ auto boxValue = rewriter.create( loc, boxPtrTy.cast().getElementType(), inputBoxStorage); + attachTBAATag(boxValue, boxTy, boxTy, nullptr); auto newBoxStorage = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter); - rewriter.create(loc, boxValue, newBoxStorage); + auto storeOp = + rewriter.create(loc, boxValue, newBoxStorage); + attachTBAATag(storeOp, boxTy, boxTy, nullptr); rewriter.replaceOp(load, newBoxStorage.getResult()); } else { - rewriter.replaceOpWithNewOp( - load, convertType(load.getType()), adaptor.getOperands(), - load->getAttrs()); + mlir::Type loadTy = convertType(load.getType()); + auto loadOp = rewriter.create( + 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 @@ mlir::LogicalResult matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const override { - if (store.getValue().getType().isa()) { + mlir::Location loc = store.getLoc(); + mlir::Type storeTy = store.getValue().getType(); + mlir::LLVM::StoreOp newStoreOp; + if (auto boxTy = storeTy.dyn_cast()) { // 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( loc, boxPtrTy.cast().getElementType(), adaptor.getOperands()[0]); - rewriter.replaceOpWithNewOp( - store, val, adaptor.getOperands()[1]); + attachTBAATag(val, boxTy, boxTy, nullptr); + newStoreOp = rewriter.create( + loc, val, adaptor.getOperands()[1]); } else { - rewriter.replaceOpWithNewOp( - store, adaptor.getOperands()[0], adaptor.getOperands()[1]); + newStoreOp = rewriter.create( + loc, adaptor.getOperands()[0], adaptor.getOperands()[1]); } + attachTBAATag(newStoreOp, storeTy, storeTy, nullptr); + rewriter.eraseOp(store); return mlir::success(); } }; @@ -3708,7 +3766,8 @@ } auto *context = getModule().getContext(); - fir::LLVMTypeConverter typeConverter{getModule()}; + fir::LLVMTypeConverter typeConverter{getModule(), + options.applyTBAA || applyTBAA}; mlir::RewritePatternSet pattern(context); pattern.insert< AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, Index: flang/lib/Optimizer/CodeGen/DescriptorModel.h =================================================================== --- flang/lib/Optimizer/CodeGen/DescriptorModel.h +++ 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 Index: flang/lib/Optimizer/CodeGen/TBAABuilder.h =================================================================== --- /dev/null +++ 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 +// types conversion and to attach llvm.tbaa attributes to memory accessing +// 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 `` and `` 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 `` type descriptor is their common parrent +// 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 `` is not used by TBAA access tags, +// because it is always known whether 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 `` +// type in the TBAA graph. +// +// The TBAA type descriptors have to be created during FIR to LLVM type +// conversion, so fir::LLVMTypeConverter have 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' 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> - the resulting +// access tag will use `` type descriptor for the base +// type. +// * access FIR type: - all accesses within descriptors +// are always represented with `` type descriptor. +// * offset: +// llvm.getelementptr %arg0[0, 0] : +// (!llvm.ptr, i64, i32, i8, i8, i8, i8, +// array<1 x array<3 x i64>>)>>) -> +// !llvm.ptr> +// 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: +// < ``, ``, 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 incorrect result if `test2` was inlined. Moreover, in 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 opt-in compiler option). +// +// The initial implementation does not create detailed type descriptors +// for box types and always uses the conservative box access tag: +// < ``, ``, 0 > +// +// Given the storage association, all non-box accesses are represented +// with the conservative data access tag: +// < ``, ``, 0 > +class TBAABuilder { +public: + TBAABuilder(mlir::ModuleOp module, bool applyTBAA); + TBAABuilder(TBAABuilder const &) = delete; + TBAABuilder &operator=(TBAABuilder const &) = delete; + + // Attach 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: + // < , , 0 > + mlir::SymbolRefAttr getAnyBoxAccessTag(); + // Returns symbol of TBAATagOp representing access tag: + // < , , 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 typeDescMap; + + // Each TBAA tag is a tuple of . + // This map holds TBAATagOp symbol for each unique tuple. + llvm::DenseMap, + mlir::SymbolRefAttr> + tagsMap; +}; + +} // namespace fir + +#endif // FORTRAN_OPTIMIZER_CODEGEN_TBAABUILDER_H Index: flang/lib/Optimizer/CodeGen/TBAABuilder.cpp =================================================================== --- /dev/null +++ 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 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 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(), + [&](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(loc, tbaaMetaOpName); + builder.setInsertionPointToStart(&tbaaMetaOp.getBody().front()); + + // Root node. + auto rootOp = builder.create( + loc, getNewTBAANodeName(kRootSymBasename), flangTBAARootId); + flangTBAARoot = FlatSymbolRefAttr::get(rootOp); + + // Any access node. + auto anyAccessOp = builder.create( + loc, getNewTBAANodeName(kTypeDescSymBasename), + StringAttr::get(context, anyAccessTypeDescId), + ArrayAttr::get(context, flangTBAARoot), ArrayRef{0}); + anyAccessTypeDesc = FlatSymbolRefAttr::get(anyAccessOp); + + // Any data access node. + auto anyDataAccessOp = builder.create( + loc, getNewTBAANodeName(kTypeDescSymBasename), + StringAttr::get(context, anyDataAccessTypeDescId), + ArrayAttr::get(context, anyAccessTypeDesc), ArrayRef{0}); + anyDataAccessTypeDesc = FlatSymbolRefAttr::get(anyDataAccessOp); + + // Box member access node. + auto boxMemberOp = builder.create( + loc, getNewTBAANodeName(kTypeDescSymBasename), + StringAttr::get(context, boxMemberTypeDescId), + ArrayAttr::get(context, anyAccessTypeDesc), ArrayRef{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( + 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()) + tbaaTagSym = getBoxAccessTag(baseFIRType, accessFIRType, gep); + else + tbaaTagSym = getDataAccessTag(baseFIRType, accessFIRType, gep); + + if (tbaaTagSym) + op->setAttr(LLVMDialect::getTBAAAttrName(), + ArrayAttr::get(op->getContext(), tbaaTagSym)); +} + +} // namespace fir Index: flang/lib/Optimizer/CodeGen/TypeConverter.h =================================================================== --- flang/lib/Optimizer/CodeGen/TypeConverter.h +++ 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 @@ /// 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 @@ 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 @@ 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 specifics; + TBAABuilder tbaaBuilder; }; } // namespace fir Index: flang/test/Fir/tbaa.fir =================================================================== --- /dev/null +++ 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.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>, i64) -> !fir.ref + %1 = fir.embox %0 source_box %arg0 : (!fir.ref, !fir.class>) -> !fir.class + %2 = fir.box_typecode %1 : (!fir.class) -> 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) -> !fir.ref + fir.store %c10_i32 to %4 : !fir.ref + cf.br ^bb2 + ^bb2: // 2 preds: ^bb0, ^bb1 + return + } +} + +// CHECK-LABEL: llvm.func @tbaa( +// CHECK-SAME: %[[VAL_0:.*]]: !llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)> {alignment = 8 : i64} : (i32) -> !llvm.ptr>, i64, i32, i8, i8, i8, i8, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr>> +// CHECK: %[[VAL_7:.*]] = llvm.load %[[VAL_6]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr>> +// CHECK: %[[VAL_8:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK: %[[VAL_9:.*]] = llvm.getelementptr %[[VAL_0]][0, 7, 0, 2] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_10:.*]] = llvm.load %[[VAL_9]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// 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> to !llvm.ptr +// CHECK: %[[VAL_14:.*]] = llvm.getelementptr %[[VAL_13]]{{\[}}%[[VAL_12]]] : (!llvm.ptr, i64) -> !llvm.ptr +// CHECK: %[[VAL_15:.*]] = llvm.bitcast %[[VAL_14]] : !llvm.ptr to !llvm.ptr> +// 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr> +// CHECK: %[[VAL_19:.*]] = llvm.load %[[VAL_18]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr> +// CHECK: %[[VAL_20:.*]] = llvm.getelementptr %[[VAL_0]][0, 1] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_21:.*]] = llvm.load %[[VAL_20]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_22:.*]] = llvm.getelementptr %[[VAL_0]][0, 4] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_23:.*]] = llvm.load %[[VAL_22]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_24:.*]] = llvm.mlir.undef : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)> +// CHECK: %[[VAL_25:.*]] = llvm.insertvalue %[[VAL_21]], %[[VAL_24]][1] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, ptr, 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>, i64, i32, i8, i8, i8, i8, ptr, 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>, i64, i32, i8, i8, i8, i8, ptr, 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>, i64, i32, i8, i8, i8, i8, ptr, 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>, i64, i32, i8, i8, i8, i8, ptr, 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>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)> +// CHECK: %[[VAL_39:.*]] = llvm.bitcast %[[VAL_19]] : !llvm.ptr to !llvm.ptr +// CHECK: %[[VAL_40:.*]] = llvm.insertvalue %[[VAL_39]], %[[VAL_38]][7] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)> +// CHECK: %[[VAL_41:.*]] = llvm.bitcast %[[VAL_15]] : !llvm.ptr> to !llvm.ptr> +// CHECK: %[[VAL_42:.*]] = llvm.insertvalue %[[VAL_41]], %[[VAL_40]][0] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)> +// CHECK: llvm.store %[[VAL_42]], %[[VAL_2]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)>> +// CHECK: %[[VAL_43:.*]] = llvm.getelementptr %[[VAL_2]][0, 4] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_44:.*]] = llvm.load %[[VAL_43]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// 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>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)>>) -> !llvm.ptr> +// CHECK: %[[VAL_47:.*]] = llvm.load %[[VAL_46]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr> +// CHECK: llvm.store %[[VAL_5]], %[[VAL_47]] {llvm.tbaa = [@__flang_tbaa::@[[DATAT:tag_[0-9]*]]]} : !llvm.ptr +// 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>>> + %1 = fir.address_of(@_QQcl.2E2F64756D6D792E66393000) : !fir.ref> + %2 = fir.convert %1 : (!fir.ref>) -> !fir.ref + %3 = fir.call @_FortranAioBeginExternalListOutput(%c-1_i32, %2, %c8_i32) fastmath : (i32, !fir.ref, i32) -> !fir.ref + %4 = fir.load %0 : !fir.ref>>> + %5:3 = fir.box_dims %4, %c0 : (!fir.class>>, index) -> (index, index, index) + %6 = fircg.ext_rebox %4 origin %5#0 : (!fir.class>>, index) -> !fir.class> + %7 = fir.convert %6 : (!fir.class>) -> !fir.box + %8 = fir.call @_FortranAioOutputDescriptor(%3, %7) fastmath : (!fir.ref, !fir.box) -> i1 + %9 = fir.call @_FortranAioEndIoStatement(%3) fastmath : (!fir.ref) -> i32 + return + } + func.func private @_FortranAioBeginExternalListOutput(i32, !fir.ref, i32) -> !fir.ref attributes {fir.io, fir.runtime} + func.func private @_FortranAioOutputDescriptor(!fir.ref, !fir.box) -> i1 attributes {fir.io, fir.runtime} + func.func private @_FortranAioEndIoStatement(!fir.ref) -> 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>> { + %c0 = arith.constant 0 : index + %0 = fir.zero_bits !fir.ptr> + %1 = fircg.ext_embox %0(%c0) : (!fir.ptr>, index) -> !fir.class>> + fir.has_value %1 : !fir.class>> + } +} + +// 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> {alignment = 8 : i64} : (i32) -> !llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>> +// CHECK: %[[VAL_2:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[VAL_3:.*]] = llvm.alloca %[[VAL_2]] x !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> {alignment = 8 : i64} : (i32) -> !llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>> +// CHECK: %[[VAL_8:.*]] = llvm.mlir.addressof @_QQcl.2E2F64756D6D792E66393000 : !llvm.ptr> +// CHECK: %[[VAL_9:.*]] = llvm.bitcast %[[VAL_8]] : !llvm.ptr> to !llvm.ptr +// CHECK: %[[VAL_10:.*]] = llvm.call @_FortranAioBeginExternalListOutput(%[[VAL_6]], %[[VAL_9]], %[[VAL_5]]) {fastmathFlags = #llvm.fastmath} : (i32, !llvm.ptr, i32) -> !llvm.ptr +// CHECK: %[[VAL_11:.*]] = llvm.load %[[VAL_7]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>> +// CHECK: llvm.store %[[VAL_11]], %[[VAL_3]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>> +// CHECK: %[[VAL_12:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, %[[VAL_4]], 0] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>, i64) -> !llvm.ptr +// CHECK: %[[VAL_13:.*]] = llvm.load %[[VAL_12]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_14:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, %[[VAL_4]], 1] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>, i64) -> !llvm.ptr +// CHECK: %[[VAL_15:.*]] = llvm.load %[[VAL_14]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_16:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, %[[VAL_4]], 2] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>, i64) -> !llvm.ptr +// CHECK: %[[VAL_17:.*]] = llvm.load %[[VAL_16]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_18:.*]] = llvm.getelementptr %[[VAL_3]][0, 8] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr> +// CHECK: %[[VAL_19:.*]] = llvm.load %[[VAL_18]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr> +// 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_23:.*]] = llvm.load %[[VAL_22]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_24:.*]] = llvm.getelementptr %[[VAL_3]][0, 4] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_25:.*]] = llvm.load %[[VAL_24]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_26:.*]] = llvm.getelementptr %[[VAL_3]][0, 8] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr> +// CHECK: %[[VAL_27:.*]] = llvm.load %[[VAL_26]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr> +// CHECK: %[[VAL_28:.*]] = llvm.mlir.undef : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_29:.*]] = llvm.insertvalue %[[VAL_23]], %[[VAL_28]][1] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_43:.*]] = llvm.bitcast %[[VAL_27]] : !llvm.ptr to !llvm.ptr +// CHECK: %[[VAL_44:.*]] = llvm.insertvalue %[[VAL_43]], %[[VAL_42]][8] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_45:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, 0, 0] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_46:.*]] = llvm.load %[[VAL_45]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_47:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, 0, 1] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_48:.*]] = llvm.load %[[VAL_47]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_49:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, 0, 2] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr +// CHECK: %[[VAL_50:.*]] = llvm.load %[[VAL_49]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr +// CHECK: %[[VAL_51:.*]] = llvm.getelementptr %[[VAL_3]][0, 0] : (!llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>>) -> !llvm.ptr>> +// CHECK: %[[VAL_52:.*]] = llvm.load %[[VAL_51]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr>> +// 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_58:.*]] = llvm.insertvalue %[[VAL_48]], %[[VAL_57]][7, 0, 1] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_59:.*]] = llvm.insertvalue %[[VAL_50]], %[[VAL_58]][7, 0, 2] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_60:.*]] = llvm.bitcast %[[VAL_52]] : !llvm.ptr> to !llvm.ptr> +// CHECK: %[[VAL_61:.*]] = llvm.insertvalue %[[VAL_60]], %[[VAL_59]][0] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: llvm.store %[[VAL_61]], %[[VAL_1]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>> +// CHECK: %[[VAL_62:.*]] = llvm.bitcast %[[VAL_1]] : !llvm.ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>> to !llvm.ptr>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)>> +// CHECK: %[[VAL_63:.*]] = llvm.call @_FortranAioOutputDescriptor(%[[VAL_10]], %[[VAL_62]]) {fastmathFlags = #llvm.fastmath} : (!llvm.ptr, !llvm.ptr>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)>>) -> i1 +// CHECK: %[[VAL_64:.*]] = llvm.call @_FortranAioEndIoStatement(%[[VAL_10]]) {fastmathFlags = #llvm.fastmath} : (!llvm.ptr) -> i32 +// CHECK: llvm.return +// CHECK: } +// CHECK: llvm.func @_FortranAioBeginExternalListOutput(i32, !llvm.ptr, i32) -> !llvm.ptr attributes {fir.io, fir.runtime, sym_visibility = "private"} +// CHECK: llvm.func @_FortranAioOutputDescriptor(!llvm.ptr, !llvm.ptr>, i64, i32, i8, i8, i8, i8, ptr, array<1 x i64>)>>) -> i1 attributes {fir.io, fir.runtime, sym_visibility = "private"} +// CHECK: llvm.func @_FortranAioEndIoStatement(!llvm.ptr) -> 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> { +// CHECK: %[[VAL_0:.*]] = llvm.mlir.constant(0 : index) : i64 +// CHECK: %[[VAL_1:.*]] = llvm.mlir.null : !llvm.ptr> +// 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_5:.*]] = llvm.insertvalue %[[VAL_2]], %[[VAL_4]][1] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_19:.*]] = llvm.mlir.null : !llvm.ptr +// CHECK: %[[VAL_20:.*]] = llvm.bitcast %[[VAL_19]] : !llvm.ptr to !llvm.ptr +// CHECK: %[[VAL_21:.*]] = llvm.insertvalue %[[VAL_20]], %[[VAL_18]][8] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_25:.*]] = llvm.insertvalue %[[VAL_0]], %[[VAL_24]][7, 0, 1] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: %[[VAL_26:.*]] = llvm.insertvalue %[[VAL_2]], %[[VAL_25]][7, 0, 2] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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> to !llvm.ptr> +// CHECK: %[[VAL_30:.*]] = llvm.insertvalue %[[VAL_29]], %[[VAL_26]][0] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)> +// CHECK: llvm.return %[[VAL_30]] : !llvm.struct<(ptr>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, 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>) -> i32 { + %0 = fir.box_rank %arg0 : (!fir.box>) -> i32 + return %0 : i32 +} + +// CHECK-LABEL: llvm.func @tbaa( +// CHECK-SAME: %[[VAL_0:.*]]: !llvm.ptr, i64, i32, i8, i8, i8, i8)>>) -> i32 { +// CHECK: %[[VAL_1:.*]] = llvm.getelementptr %[[VAL_0]][0, 3] : (!llvm.ptr, i64, i32, i8, i8, i8, i8)>>) -> !llvm.ptr +// CHECK: %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr +// 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>) -> i1 { + %0 = fir.box_isarray %arg0 : (!fir.box>) -> i1 + return %0 : i1 +} + +// CHECK-LABEL: llvm.func @tbaa( +// CHECK-SAME: %[[VAL_0:.*]]: !llvm.ptr, i64, i32, i8, i8, i8, i8)>>) -> i1 { +// CHECK: %[[VAL_1:.*]] = llvm.getelementptr %[[VAL_0]][0, 3] : (!llvm.ptr, i64, i32, i8, i8, i8, i8)>>) -> !llvm.ptr +// CHECK: %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr +// 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) -> i32 { + %0 = fir.box_elesize %arg0 : (!fir.box) -> i32 + return %0 : i32 +} + +// CHECK-LABEL: llvm.func @tbaa( +// CHECK-SAME: %[[VAL_0:.*]]: !llvm.ptr, i64, i32, i8, i8, i8, i8)>>) -> i32 { +// CHECK: %[[VAL_1:.*]] = llvm.getelementptr %[[VAL_0]][0, 1] : (!llvm.ptr, i64, i32, i8, i8, i8, i8)>>) -> !llvm.ptr +// CHECK: %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr +// 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>) -> i1 { + %0 = fir.box_isalloc %arg0 : (!fir.box>) -> i1 + return %0 : i1 +} + +// CHECK-LABEL: llvm.func @tbaa( +// CHECK-SAME: %[[VAL_0:.*]]: !llvm.ptr, i64, i32, i8, i8, i8, i8)>>) -> i1 { +// CHECK: %[[VAL_1:.*]] = llvm.getelementptr %[[VAL_0]][0, 5] : (!llvm.ptr, i64, i32, i8, i8, i8, i8)>>) -> !llvm.ptr +// CHECK: %[[VAL_2:.*]] = llvm.load %[[VAL_1]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr +// 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>) { + %c0 = arith.constant 0 : i64 + %1 = fircg.ext_array_coor %arg0(%c0) <%c0> : (!fir.box>, i64, i64) -> !fir.ref + return +} + +// CHECK-LABEL: llvm.func @tbaa( +// CHECK-SAME: %[[VAL_0:.*]]: !llvm.ptr, 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, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>)>>) -> !llvm.ptr +// CHECK: %[[VAL_7:.*]] = llvm.load %[[VAL_6]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT:tag_[0-9]*]]]} : !llvm.ptr +// 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, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>)>>) -> !llvm.ptr> +// CHECK: %[[VAL_11:.*]] = llvm.load %[[VAL_10]] {llvm.tbaa = [@__flang_tbaa::@[[BOXT]]]} : !llvm.ptr> +// CHECK: %[[VAL_12:.*]] = llvm.bitcast %[[VAL_11]] : !llvm.ptr to !llvm.ptr +// CHECK: %[[VAL_13:.*]] = llvm.getelementptr %[[VAL_12]]{{\[}}%[[VAL_9]]] : (!llvm.ptr, i64) -> !llvm.ptr +// CHECK: %[[VAL_14:.*]] = llvm.bitcast %[[VAL_13]] : !llvm.ptr to !llvm.ptr +// 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: }