diff --git a/flang/include/flang/Optimizer/Analysis/TBAAForest.h b/flang/include/flang/Optimizer/Analysis/TBAAForest.h index 4d2281642b43d..b4932594114a1 100644 --- a/flang/include/flang/Optimizer/Analysis/TBAAForest.h +++ b/flang/include/flang/Optimizer/Analysis/TBAAForest.h @@ -46,6 +46,12 @@ struct TBAATree { mlir::LLVM::TBAATypeDescriptorAttr getRoot() const { return parent; } + /// For the given name, get or create a subtree in the current + /// subtree. For example, this is used for creating subtrees + /// inside the "global data" subtree for the COMMON block variables + /// belonging to the same COMMON block. + SubtreeState &getOrCreateNamedSubtree(mlir::StringAttr name); + private: SubtreeState(mlir::MLIRContext *ctx, std::string name, mlir::LLVM::TBAANodeAttr grandParent) @@ -57,6 +63,9 @@ struct TBAATree { const std::string parentId; mlir::MLIRContext *const context; mlir::LLVM::TBAATypeDescriptorAttr parent; + // A map of named sub-trees, e.g. sub-trees of the COMMON blocks + // placed under the "global data" root. + llvm::DenseMap namedSubtrees; }; /// A subtree for POINTER/TARGET variables data. @@ -131,8 +140,8 @@ class TBAAForrest { // responsibility to provide unique name for the scope. // If the scope string is empty, returns the TBAA tree for the // "root" scope of the given function. - inline const TBAATree &getFuncTreeWithScope(mlir::func::FuncOp func, - llvm::StringRef scope) { + inline TBAATree &getMutableFuncTreeWithScope(mlir::func::FuncOp func, + llvm::StringRef scope) { mlir::StringAttr name = func.getSymNameAttr(); if (!scope.empty()) name = mlir::StringAttr::get(name.getContext(), @@ -140,13 +149,20 @@ class TBAAForrest { return getFuncTree(name); } + inline const TBAATree &getFuncTreeWithScope(mlir::func::FuncOp func, + llvm::StringRef scope) { + return getMutableFuncTreeWithScope(func, scope); + } + private: - const TBAATree &getFuncTree(mlir::StringAttr symName) { + TBAATree &getFuncTree(mlir::StringAttr symName) { if (!separatePerFunction) symName = mlir::StringAttr::get(symName.getContext(), ""); if (!trees.contains(symName)) trees.insert({symName, TBAATree::buildTree(symName)}); - return trees.at(symName); + auto it = trees.find(symName); + assert(it != trees.end()); + return it->second; } // Should each function use a different tree? diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h index e3a44f147b4cd..4b3087ed45788 100644 --- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h +++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h @@ -365,7 +365,12 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener { // Linkage helpers (inline). The default linkage is external. //===--------------------------------------------------------------------===// - mlir::StringAttr createCommonLinkage() { return getStringAttr("common"); } + static mlir::StringAttr createCommonLinkage(mlir::MLIRContext *context) { + return mlir::StringAttr::get(context, "common"); + } + mlir::StringAttr createCommonLinkage() { + return createCommonLinkage(getContext()); + } mlir::StringAttr createInternalLinkage() { return getStringAttr("internal"); } diff --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h index ecab12de55d61..6188c4460dadd 100644 --- a/flang/include/flang/Optimizer/Dialect/FIRType.h +++ b/flang/include/flang/Optimizer/Dialect/FIRType.h @@ -551,6 +551,7 @@ std::optional> getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty, const mlir::DataLayout &dl, const fir::KindMapping &kindMap); + } // namespace fir #endif // FORTRAN_OPTIMIZER_DIALECT_FIRTYPE_H diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td index 54190f09b1ec8..e3001454cdf19 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -326,7 +326,8 @@ def AddAliasTags : Pass<"fir-add-alias-tags", "mlir::ModuleOp"> { theory, each operation could be considered in prallel, so long as there aren't races adding new tags to the mlir context. }]; - let dependentDialects = [ "fir::FIROpsDialect" ]; + // The pass inserts TBAA attributes from LLVM dialect. + let dependentDialects = ["mlir::LLVM::LLVMDialect"]; } def SimplifyRegionLite : Pass<"simplify-region-lite", "mlir::ModuleOp"> { diff --git a/flang/lib/Optimizer/Analysis/TBAAForest.cpp b/flang/lib/Optimizer/Analysis/TBAAForest.cpp index cce50e0de1bc7..44a0348da3a6f 100644 --- a/flang/lib/Optimizer/Analysis/TBAAForest.cpp +++ b/flang/lib/Optimizer/Analysis/TBAAForest.cpp @@ -11,12 +11,23 @@ mlir::LLVM::TBAATagAttr fir::TBAATree::SubtreeState::getTag(llvm::StringRef uniqueName) const { - std::string id = (parentId + "/" + uniqueName).str(); + std::string id = (parentId + '/' + uniqueName).str(); mlir::LLVM::TBAATypeDescriptorAttr type = mlir::LLVM::TBAATypeDescriptorAttr::get( context, id, mlir::LLVM::TBAAMemberAttr::get(parent, 0)); return mlir::LLVM::TBAATagAttr::get(type, type, 0); - // return tag; +} + +fir::TBAATree::SubtreeState & +fir::TBAATree::SubtreeState::getOrCreateNamedSubtree(mlir::StringAttr name) { + auto it = namedSubtrees.find(name); + if (it != namedSubtrees.end()) + return it->second; + + return namedSubtrees + .insert( + {name, SubtreeState(context, parentId + '/' + name.str(), parent)}) + .first->second; } mlir::LLVM::TBAATagAttr fir::TBAATree::SubtreeState::getTag() const { diff --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp index 85403ad257657..d87798ee1c115 100644 --- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp +++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp @@ -14,12 +14,17 @@ #include "flang/Optimizer/Analysis/AliasAnalysis.h" #include "flang/Optimizer/Analysis/TBAAForest.h" +#include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Dialect/FIRDialect.h" #include "flang/Optimizer/Dialect/FirAliasTagOpInterface.h" +#include "flang/Optimizer/Support/DataLayout.h" +#include "flang/Optimizer/Support/Utils.h" #include "flang/Optimizer/Transforms/Passes.h" +#include "mlir/Dialect/DLTI/DLTI.h" #include "mlir/IR/Dominance.h" #include "mlir/Pass/Pass.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/CommandLine.h" @@ -57,12 +62,131 @@ static llvm::cl::opt localAllocsThreshold( namespace { +// Return the size and alignment (in bytes) for the given type. +// TODO: this must be combined with DebugTypeGenerator::getFieldSizeAndAlign(). +// We'd better move fir::LLVMTypeConverter out of the FIRCodeGen component. +static std::pair +getTypeSizeAndAlignment(mlir::Type type, + fir::LLVMTypeConverter &llvmTypeConverter) { + mlir::Type llvmTy; + if (auto boxTy = mlir::dyn_cast_if_present(type)) + llvmTy = llvmTypeConverter.convertBoxTypeAsStruct(boxTy, getBoxRank(boxTy)); + else + llvmTy = llvmTypeConverter.convertType(type); + + const mlir::DataLayout &dataLayout = llvmTypeConverter.getDataLayout(); + uint64_t byteSize = dataLayout.getTypeSize(llvmTy); + unsigned short byteAlign = dataLayout.getTypeABIAlignment(llvmTy); + return std::pair{byteSize, byteAlign}; +} + +// IntervalTy class describes a range of bytes addressed by a variable +// within some storage. Zero-sized intervals are not allowed. +class IntervalTy { +public: + IntervalTy() = delete; + IntervalTy(std::uint64_t start, std::size_t size) + : start(start), end(start + (size - 1)) { + assert(size != 0 && "empty intervals should not be created"); + } + constexpr bool operator<(const IntervalTy &rhs) const { + if (start < rhs.start) + return true; + if (rhs.start < start) + return false; + return end < rhs.end; + } + bool overlaps(const IntervalTy &other) const { + return end >= other.start && other.end >= start; + } + bool contains(const IntervalTy &other) const { + return start <= other.start && end >= other.end; + } + void merge(const IntervalTy &other) { + start = std::min(start, other.start); + end = std::max(end, other.end); + assert(start <= end); + } + void print(llvm::raw_ostream &os) const { + os << "[" << start << "," << end << "]"; + } + std::uint64_t getStart() const { return start; } + std::uint64_t getEnd() const { return end; } + +private: + std::uint64_t start; + std::uint64_t end; +}; + +// IntervalSetTy is an ordered set of IntervalTy entities. +class IntervalSetTy : public std::set { +public: + // Find an interval from the set that contain the given interval. + // The complexity is O(log(N)), where N is the size of the set. + std::optional getContainingInterval(const IntervalTy &interval) { + if (empty()) + return std::nullopt; + + auto it = lower_bound(interval); + // The iterator points to the first interval that is not less than + // the given interval. The given interval may belong to the one + // pointed out by the iterator or to the previous one. + // + // In the following cases there might be no interval that is not less + // than the given interval, e.g.: + // Case 1: + // interval: [5,5] + // set: {[4,6]} + // Case 2: + // interval: [5,5] + // set: {[4,5]} + // We have to look starting from the last interval in the set. + if (it == end()) + --it; + + // The loop must finish in two iterator max. + do { + if (it->contains(interval)) + return *it; + // If the current interval from the set is less than the given + // interval and there is no overlap, we should not look further. + if ((!it->overlaps(interval) && *it < interval) || it == begin()) + break; + + --it; + } while (true); + + return std::nullopt; + } +}; + +// Stream operators for IntervalTy and IntervalSetTy. +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const IntervalTy &interval) { + interval.print(os); + return os; +} +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const IntervalSetTy &set) { + if (set.empty()) { + os << " "; + return os; + } + for (const auto &interval : set) + os << ' ' << interval; + return os; +} + /// Shared state per-module class PassState { public: - PassState(mlir::DominanceInfo &domInfo, + PassState(mlir::ModuleOp module, const mlir::DataLayout &dl, + mlir::DominanceInfo &domInfo, std::optional localAllocsThreshold) - : domInfo(domInfo), localAllocsThreshold(localAllocsThreshold) {} + : domInfo(domInfo), localAllocsThreshold(localAllocsThreshold), + symTab(module.getOperation()), + llvmTypeConverter(module, /*applyTBAA=*/false, + /*forceUnifiedTBAATree=*/false, dl) {} /// memoised call to fir::AliasAnalysis::getSource inline const fir::AliasAnalysis::Source &getSource(mlir::Value value) { if (!analysisCache.contains(value)) @@ -72,13 +196,14 @@ class PassState { } /// get the per-function TBAATree for this function - inline const fir::TBAATree &getFuncTree(mlir::func::FuncOp func) { - return forrest[func]; + inline fir::TBAATree &getMutableFuncTreeWithScope(mlir::func::FuncOp func, + fir::DummyScopeOp scope) { + auto &scopeMap = scopeNames.at(func); + return forrest.getMutableFuncTreeWithScope(func, scopeMap.lookup(scope)); } inline const fir::TBAATree &getFuncTreeWithScope(mlir::func::FuncOp func, fir::DummyScopeOp scope) { - auto &scopeMap = scopeNames.at(func); - return forrest.getFuncTreeWithScope(func, scopeMap.lookup(scope)); + return getMutableFuncTreeWithScope(func, scope); } void processFunctionScopes(mlir::func::FuncOp func); @@ -98,8 +223,82 @@ class PassState { // attachment. bool attachLocalAllocTag(); + // Return fir.global for the given name. + fir::GlobalOp getGlobalDefiningOp(mlir::StringAttr name) const { + return symTab.lookup(name); + } + + // Process fir::FortranVariableStorageOpInterface operations within + // the given op, and fill in declToStorageMap with the information + // about their physical storages and layouts. + void collectPhysicalStorageAliasSets(mlir::Operation *op); + + // Return the byte size of the given declaration. + std::size_t getDeclarationSize(fir::FortranVariableStorageOpInterface decl) { + mlir::Type memType = fir::unwrapRefType(decl.getBase().getType()); + auto [size, alignment] = + getTypeSizeAndAlignment(memType, llvmTypeConverter); + return llvm::alignTo(size, alignment); + } + + // A StorageDesc specifies an operation that defines a physical storage + // and the pair within that physical storage where + // a variable resides. + struct StorageDesc { + StorageDesc() = delete; + StorageDesc(mlir::Operation *storageDef, std::uint64_t start, + std::size_t size) + : storageDef(storageDef), interval(start, size) {} + + // Return a string representing the byte range of the variable within + // its storage, e.g. bytes_0_to_0 for a 1-byte variable starting + // at offset 0. + std::string getByteRangeStr() const { + return ("bytes_" + llvm::Twine(interval.getStart()) + "_to_" + + llvm::Twine(interval.getEnd())) + .str(); + } + + mlir::Operation *storageDef; + IntervalTy interval; + }; + + // Fills in declToStorageMap on the first invocation. + // Returns a storage descriptor for the given op (if registered + // in declToStorageMap). + const StorageDesc *computeStorageDesc(mlir::Operation *op) { + if (!op) + return nullptr; + + // TODO: it should be safe to run collectPhysicalStorageAliasSets() + // on the parent func.func instead of the module, since the TBAA + // tags use different roots per function. This may provide better + // results for storages that have members with descriptors + // in one function but not the others. + if (!declToStorageMapComputed) + collectPhysicalStorageAliasSets(op->getParentOfType()); + return getStorageDesc(op); + } + +private: + const StorageDesc *getStorageDesc(mlir::Operation *op) const { + auto it = declToStorageMap.find(op); + return it == declToStorageMap.end() ? nullptr : &it->second; + } + + StorageDesc &getMutableStorageDesc(mlir::Operation *op) { + auto it = declToStorageMap.find(op); + assert(it != declToStorageMap.end()); + return it->second; + } + private: mlir::DominanceInfo &domInfo; + std::optional localAllocsThreshold; + // Symbol table cache for the module. + mlir::SymbolTable symTab; + // Type converter to compute the size of declarations. + fir::LLVMTypeConverter llvmTypeConverter; fir::AliasAnalysis analysis; llvm::DenseMap analysisCache; fir::TBAAForrest forrest; @@ -118,7 +317,12 @@ class PassState { // member(s), to avoid the cost of isRecordWithDescriptorMember(). llvm::DenseSet typesContainingDescriptors; - std::optional localAllocsThreshold; + // A map between fir::FortranVariableStorageOpInterface operations + // and their storage descriptors. + llvm::DenseMap declToStorageMap; + // declToStorageMapComputed is set to true after declToStorageMap + // is initialized by collectPhysicalStorageAliasSets(). + bool declToStorageMapComputed = false; }; // Process fir.dummy_scope operations in the given func: @@ -198,6 +402,202 @@ bool PassState::attachLocalAllocTag() { return true; } +static mlir::Value getStorageDefinition(mlir::Value storageRef) { + while (auto convert = + mlir::dyn_cast_or_null(storageRef.getDefiningOp())) + storageRef = convert.getValue(); + return storageRef; +} + +void PassState::collectPhysicalStorageAliasSets(mlir::Operation *op) { + // A map between fir::FortranVariableStorageOpInterface operations + // and the intervals describing their layout within their physical + // storages. + llvm::DenseMap memberIntervals; + // A map between operations defining physical storages (e.g. fir.global) + // and sets of fir::FortranVariableStorageOpInterface operations + // declaring their member variables. + llvm::DenseMap> + storageDecls; + + bool seenUnknownStorage = false; + bool seenDeclWithDescriptor = false; + op->walk([&](fir::FortranVariableStorageOpInterface decl) { + mlir::Value storageRef = decl.getStorage(); + if (!storageRef) + return mlir::WalkResult::advance(); + + // If we have seen a declaration of a variable containing + // a descriptor, and we have not been able to identify + // a storage of any variable, then any variable may + // potentially overlap with the variable containing + // a descriptor. In this case, it is hard to make any + // assumptions about any variable with physical + // storage. Exit early. + if (seenUnknownStorage && seenDeclWithDescriptor) + return mlir::WalkResult::interrupt(); + + if (typeReferencesDescriptor(decl.getBase().getType())) + seenDeclWithDescriptor = true; + + mlir::Operation *storageDef = + getStorageDefinition(storageRef).getDefiningOp(); + // All physical storages that are defined by non-global + // objects (e.g. via fir.alloca) indicate an EQUIVALENCE. + // Inside an EQUIVALENCE each variable overlaps + // with at least one another variable. So all EQUIVALENCE + // variables belong to the same alias set, and there is + // no reason to investigate them further. + // Note that, in general, the storage may be defined by a block + // argument. + auto addrOfOp = mlir::dyn_cast_or_null(storageDef); + if (!storageDef || + (!addrOfOp && !mlir::dyn_cast(storageDef))) { + seenUnknownStorage = true; + return mlir::WalkResult::advance(); + } + if (!addrOfOp) + return mlir::WalkResult::advance(); + fir::GlobalOp globalDef = + getGlobalDefiningOp(addrOfOp.getSymbol().getRootReference()); + std::uint64_t storageOffset = decl.getStorageOffset(); + std::size_t declSize = getDeclarationSize(decl); + LLVM_DEBUG(llvm::dbgs() + << "Found variable with storage:\n" + << "Declaration: " << decl << "\n" + << "Storage: " << (globalDef ? globalDef : nullptr) << "\n" + << "Offset: " << storageOffset << "\n" + << "Size: " << declSize << "\n"); + if (!globalDef) { + seenUnknownStorage = true; + return mlir::WalkResult::advance(); + } + // Zero-sized variables do not need any TBAA tags, because + // they cannot be accessed. + if (declSize == 0) + return mlir::WalkResult::advance(); + + declToStorageMap.try_emplace(decl.getOperation(), globalDef.getOperation(), + storageOffset, declSize); + storageDecls.try_emplace(globalDef.getOperation()) + .first->second.push_back(decl.getOperation()); + + auto &set = + memberIntervals.try_emplace(globalDef.getOperation()).first->second; + set.insert(IntervalTy(storageOffset, declSize)); + return mlir::WalkResult::advance(); + }); + + // Mark the map as computed before any early exits below. + declToStorageMapComputed = true; + + if (seenUnknownStorage && seenDeclWithDescriptor) { + declToStorageMap.clear(); + return; + } + + // Process each physical storage. + for (auto &map : memberIntervals) { + mlir::Operation *storageDef = map.first; + const IntervalSetTy &originalSet = map.second; + LLVM_DEBUG( + llvm::dbgs() << "Merging " << originalSet.size() + << " member intervals for: "; + storageDef->print(llvm::dbgs(), mlir::OpPrintingFlags{}.skipRegions()); + llvm::dbgs() << "\nIntervals: " << originalSet << "\n"); + // Ordered set of merged overlapping intervals. + // Since the intervals in originalSet are sorted, the merged + // intervals are always added at the end of the mergedIntervals set. + IntervalSetTy mergedIntervals; + if (originalSet.size() > 1) { + auto intervalIt = originalSet.begin(); + IntervalTy mergedInterval = *intervalIt; + while (++intervalIt != originalSet.end()) { + if (mergedInterval.overlaps(*intervalIt)) { + mergedInterval.merge(*intervalIt); + } else { + mergedIntervals.insert(mergedIntervals.end(), mergedInterval); + mergedInterval = *intervalIt; + } + } + mergedIntervals.insert(mergedIntervals.end(), mergedInterval); + } else { + // 0 or 1 total interval requires no merging. + mergedIntervals = originalSet; + } + LLVM_DEBUG(llvm::dbgs() << "Merged intervals:" << mergedIntervals << "\n"); + + bool wasMerged = originalSet.size() != mergedIntervals.size(); + + // Go through all the declarations within the storage, and assign + // them to their final intervals (if some merging happened), + // and collect information about "poisoned" intervals (see below). + // invalidIntervals set will contain the "poisoned" intervals. + IntervalSetTy invalidIntervals; + for (auto *decl : storageDecls.at(storageDef)) { + StorageDesc &declStorageDesc = getMutableStorageDesc(decl); + + if (wasMerged) { + // Some intervals were merged, so we have to modify the intervals + // for some declarations. + + auto containingInterval = + mergedIntervals.getContainingInterval(declStorageDesc.interval); + assert(containingInterval && "did not find the containing interval"); + LLVM_DEBUG(llvm::dbgs() << "Placing: " << *decl << " into interval " + << *containingInterval); + declStorageDesc.interval = *containingInterval; + } + if (typeReferencesDescriptor( + mlir::cast(decl) + .getBase() + .getType())) { + // If a variable contains a descriptor within it. + // We cannot attach any data tag to it, because it will + // conflict with the late TBBA tags attachment for + // the descriptor data. This also applies to all + // variables overlapping with this one, thus we should + // remove any storage descriptors for their declarations. + LLVM_DEBUG(llvm::dbgs() << " (poisoned)"); + invalidIntervals.insert(declStorageDesc.interval); + } + LLVM_DEBUG(llvm::dbgs() << "\n"); + } + + if (invalidIntervals.empty()) + continue; + + // Now that all the declarations are assigned to their intervals, + // go through the "poisoned" intervals and remove all declarations + // belonging to them from declToStorageMap, so that they do not + // have any tags attached. + LLVM_DEBUG(llvm::dbgs() + << "Invalid intervals:" << invalidIntervals << "\n"); + if (invalidIntervals.size() == mergedIntervals.size()) { + // All variables are "poisoned". Save the O(log(N)) lookups + // in invalidIntervals set, and poison them all. + for (auto *decl : storageDecls.at(storageDef)) { + LLVM_DEBUG(llvm::dbgs() + << "Removing storage descriptor for: " << *decl << "\n"); + declToStorageMap.erase(decl); + } + continue; + } + + // Some variables are "poisoned". + for (auto *decl : storageDecls.at(storageDef)) { + const StorageDesc *declStorageDesc = getStorageDesc(decl); + assert(declStorageDesc && "declaration must have a storage descriptor"); + if (auto containingInterval = invalidIntervals.getContainingInterval( + declStorageDesc->interval)) { + LLVM_DEBUG(llvm::dbgs() + << "Removing storage descriptor for: " << *decl << "\n"); + declToStorageMap.erase(decl); + } + } + } +} + class AddAliasTagsPass : public fir::impl::AddAliasTagsBase { public: void runOnOperation() override; @@ -310,14 +710,62 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op, source.kind == fir::AliasAnalysis::SourceKind::Global && !source.isBoxData()) { mlir::SymbolRefAttr glbl = llvm::cast(source.origin.u); - const char *name = glbl.getRootReference().data(); - LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to global " << name - << " at " << *op << "\n"); - if (source.isPointer()) + mlir::StringAttr globalName = glbl.getRootReference(); + LLVM_DEBUG(llvm::dbgs().indent(2) + << "Found reference to global " << globalName.str() << " at " + << *op << "\n"); + if (source.isPointer()) { tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag(); - else - tag = - state.getFuncTreeWithScope(func, scopeOp).globalDataTree.getTag(name); + } else { + // In general, place the tags under the "global data" root. + fir::TBAATree::SubtreeState *subTree = + &state.getMutableFuncTreeWithScope(func, scopeOp).globalDataTree; + + mlir::Operation *instantiationPoint = source.origin.instantiationPoint; + auto storageIface = + mlir::dyn_cast_or_null( + instantiationPoint); + const PassState::StorageDesc *storageDesc = + state.computeStorageDesc(instantiationPoint); + + if (storageDesc) { + // This is a variable that is part of a known physical storage + // that may contain multiple and maybe overlapping variables. + // We have may assign it with a tag that relates + // to the byte range within the physical storage. + assert(instantiationPoint && "cannot be null"); + assert(storageDesc->storageDef && "cannot be null"); + assert(storageDesc->storageDef == + state.getGlobalDefiningOp(globalName) && + "alias analysis reached a different storage"); + std::string aliasSetName = storageDesc->getByteRangeStr(); + subTree = &subTree->getOrCreateNamedSubtree(globalName); + tag = subTree->getTag(aliasSetName); + LLVM_DEBUG(llvm::dbgs() + << "Variable instantiated by " << *instantiationPoint + << " tagged with '" << aliasSetName << "' under '" + << globalName << "' root\n"); + } else if (storageIface && storageIface.getStorage()) { + // This is a variable that is: + // * aliasing a descriptor, or + // * part of an unknown physical storage, or + // * zero-sized. + // If it aliases a descriptor or the storage is unknown + // (i.e. it *may* alias a descriptor), then we cannot assign any tag to + // it, because we cannot use any tag from the "any data accesses" tree. + // If it is a zero-sized variable, we do not care about + // attaching a tag, because the access is invalid. + LLVM_DEBUG(llvm::dbgs() << "WARNING: poisoned or unknown storage or " + "zero-sized variable access\n"); + } else { + // This is a variable defined by the global symbol, + // and it is the only variable that belong to that global storage. + // Tag it using the global's name. + tag = subTree->getTag(globalName); + LLVM_DEBUG(llvm::dbgs() + << "Tagged under '" << globalName << "' root\n"); + } + } // TBAA for global variables with descriptors } else if (enableDirect && @@ -401,12 +849,15 @@ void AddAliasTagsPass::runOnOperation() { // thinks the pass operates on), then the real work of the pass is done in // runOnAliasInterface auto &domInfo = getAnalysis(); - PassState state(domInfo, localAllocsThreshold.getPosition() - ? std::optional(localAllocsThreshold) - : std::nullopt); - - mlir::ModuleOp mod = getOperation(); - mod.walk( + mlir::ModuleOp module = getOperation(); + mlir::DataLayout dl = *fir::support::getOrSetMLIRDataLayout( + module, /*allowDefaultLayout=*/false); + PassState state(module, dl, domInfo, + localAllocsThreshold.getPosition() + ? std::optional(localAllocsThreshold) + : std::nullopt); + + module.walk( [&](fir::FirAliasTagOpInterface op) { runOnAliasInterface(op, state); }); LLVM_DEBUG(llvm::dbgs() << "=== End " DEBUG_TYPE " ===\n"); diff --git a/flang/test/Transforms/tbaa-derived-with-descriptor.fir b/flang/test/Transforms/tbaa-derived-with-descriptor.fir index 18b9a801911f7..2e238ca788ca4 100644 --- a/flang/test/Transforms/tbaa-derived-with-descriptor.fir +++ b/flang/test/Transforms/tbaa-derived-with-descriptor.fir @@ -20,6 +20,7 @@ // end subroutine test // RUN: fir-opt --fir-add-alias-tags %s | FileCheck %s +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { func.func @_QPtest() { %c0 = arith.constant 0 : index %c1 = arith.constant 1 : index @@ -62,3 +63,4 @@ func.func @_QPtest() { fir.call @_FortranADestroyWithoutFinalization(%26) : (!fir.box) -> () return } +} diff --git a/flang/test/Transforms/tbaa-for-common-vars.fir b/flang/test/Transforms/tbaa-for-common-vars.fir new file mode 100644 index 0000000000000..a8dd86bff72ed --- /dev/null +++ b/flang/test/Transforms/tbaa-for-common-vars.fir @@ -0,0 +1,436 @@ +// RUN: fir-opt --split-input-file --fir-add-alias-tags %s | FileCheck %s + +// Fortran source: +// subroutine test1 +// real :: a, b +// common /common1/ a, b +// a = b +// end subroutine test1 +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + fir.global common @common1_(dense<0> : vector<8xi8>) {alignment = 4 : i64} : !fir.array<8xi8> + func.func @_QPtest1() { + %c4 = arith.constant 4 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %addr1 = fir.address_of(@common1_) : !fir.ref> + %addr2 = fir.address_of(@common1_) : !fir.ref> + %2 = fir.coordinate_of %addr1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ref + %4 = fir.declare %3 storage(%addr1[0]) {uniq_name = "_QFtest1Ea"} : (!fir.ref, !fir.ref>) -> !fir.ref + %5 = fir.coordinate_of %addr2, %c4 : (!fir.ref>, index) -> !fir.ref + %6 = fir.convert %5 : (!fir.ref) -> !fir.ref + %7 = fir.declare %6 storage(%addr2[4]) {uniq_name = "_QFtest1Eb"} : (!fir.ref, !fir.ref>) -> !fir.ref + %8 = fir.load %7 : !fir.ref + fir.store %8 to %4 : !fir.ref + return + } +} +// CHECK: #[[$ATTR_0:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_1:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_2:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_4:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_5:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_6:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_7:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_8:.+]] = #llvm.tbaa_tag +// CHECK: #[[$ATTR_9:.+]] = #llvm.tbaa_tag +// CHECK-LABEL: func.func @_QPtest1() { +// CHECK: fir.load{{.*}}{tbaa = [#[[$ATTR_8]]]} : !fir.ref +// CHECK: fir.store{{.*}}{tbaa = [#[[$ATTR_9]]]} : !fir.ref + +// ----- + +// Fortran source: +// subroutine test2 +// real :: a, b +// common /common2/ a +// equivalence (a, b) +// a = b +// end subroutine test2 +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + fir.global common @common2_(dense<0> : vector<4xi8>) {alignment = 4 : i64} : !fir.array<4xi8> + func.func @_QPtest2() { + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@common2_) : !fir.ref> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ptr + %4 = fir.declare %3 storage(%1[0]) {uniq_name = "_QFtest2Ea"} : (!fir.ptr, !fir.ref>) -> !fir.ptr + %5 = fir.declare %3 storage(%1[0]) {uniq_name = "_QFtest2Eb"} : (!fir.ptr, !fir.ref>) -> !fir.ptr + %6 = fir.load %5 : !fir.ptr + fir.store %6 to %4 : !fir.ptr + return + } +} +// CHECK: #[[$ATTR_10:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_11:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_12:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_13:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_14:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_15:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_16:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_18:.+]] = #llvm.tbaa_tag +// CHECK-LABEL: func.func @_QPtest2() { +// CHECK: fir.load{{.*}}{tbaa = [#[[$ATTR_18]]]} : !fir.ptr +// CHECK: fir.store{{.*}}{tbaa = [#[[$ATTR_18]]]} : !fir.ptr + +// ----- + +// Fortran source compiled with -mmlir -inline-all: +// subroutine test3 +// real :: a, b +// common /common3/ a, b +// a = b +// call inner(a, b) +// contains +// subroutine inner(c, d) +// real :: c, d +// c = d +// end subroutine inner +// end subroutine test3 +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + fir.global common @common3_(dense<0> : vector<8xi8>) {alignment = 4 : i64} : !fir.array<8xi8> + func.func @_QPtest3() { + %c4 = arith.constant 4 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@common3_) : !fir.ref> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ref + %4 = fir.declare %3 storage(%1[0]) {uniq_name = "_QFtest3Ea"} : (!fir.ref, !fir.ref>) -> !fir.ref + %5 = fir.coordinate_of %1, %c4 : (!fir.ref>, index) -> !fir.ref + %6 = fir.convert %5 : (!fir.ref) -> !fir.ref + %7 = fir.declare %6 storage(%1[4]) {uniq_name = "_QFtest3Eb"} : (!fir.ref, !fir.ref>) -> !fir.ref + %8 = fir.load %7 : !fir.ref + fir.store %8 to %4 : !fir.ref + %9 = fir.dummy_scope : !fir.dscope + %10 = fir.declare %4 dummy_scope %9 {uniq_name = "_QFtest3FinnerEc"} : (!fir.ref, !fir.dscope) -> !fir.ref + %11 = fir.declare %7 dummy_scope %9 {uniq_name = "_QFtest3FinnerEd"} : (!fir.ref, !fir.dscope) -> !fir.ref + %12 = fir.load %11 : !fir.ref + fir.store %12 to %10 : !fir.ref + return + } +} +// CHECK: #[[ROOT3:.+]] = #llvm.tbaa_root +// CHECK: #[[ROOT3INNER:.+]] = #llvm.tbaa_root +// CHECK: #[[ANYACC3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[ANYACC3INNER:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[ANYDATA3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[ANYDATA3INNER:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TARGETDATA3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[DUMMYARG3INNER:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOBALDATA3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[DUMMYD:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[DUMMYC:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[DUMMYDTAG:.+]] = #llvm.tbaa_tag +// CHECK: #[[DUMMYCTAG:.+]] = #llvm.tbaa_tag +// CHECK: #[[GLOBALDATA3COMMON3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOBALB:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOBALA:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOBALBTAG:.+]] = #llvm.tbaa_tag +// CHECK: #[[GLOBALATAG:.+]] = #llvm.tbaa_tag +// CHECK-LABEL: func.func @_QPtest3() { +// CHECK: fir.load{{.*}}{tbaa = [#[[GLOBALBTAG]]]} : !fir.ref +// CHECK: fir.store{{.*}}{tbaa = [#[[GLOBALATAG]]]} : !fir.ref +// CHECK: fir.load{{.*}}{tbaa = [#[[DUMMYDTAG]]]} : !fir.ref +// CHECK: fir.store{{.*}}{tbaa = [#[[DUMMYCTAG]]]} : !fir.ref + +// ----- + +// Fortran source compiled with -mmlir -inline-all: +// subroutine test4 +// real :: a, b +// common /common4/ a, b +// a = b +// call inner +// contains +// subroutine inner +// real :: c, d +// common /common4/ c, d +// c = d +// end subroutine inner +// end subroutine test4 +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + fir.global common @common4_(dense<0> : vector<8xi8>) {alignment = 4 : i64} : !fir.array<8xi8> + func.func @_QPtest4() { + %c4 = arith.constant 4 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@common4_) : !fir.ref> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ref + %4 = fir.declare %3 storage(%1[0]) {uniq_name = "_QFtest4Ea"} : (!fir.ref, !fir.ref>) -> !fir.ref + %5 = fir.coordinate_of %1, %c4 : (!fir.ref>, index) -> !fir.ref + %6 = fir.convert %5 : (!fir.ref) -> !fir.ref + %7 = fir.declare %6 storage(%1[4]) {uniq_name = "_QFtest4Eb"} : (!fir.ref, !fir.ref>) -> !fir.ref + %8 = fir.load %7 : !fir.ref + fir.store %8 to %4 : !fir.ref + %9 = fir.dummy_scope : !fir.dscope + %10 = fir.declare %3 storage(%1[0]) {uniq_name = "_QFtest4FinnerEc"} : (!fir.ref, !fir.ref>) -> !fir.ref + %11 = fir.declare %6 storage(%1[4]) {uniq_name = "_QFtest4FinnerEd"} : (!fir.ref, !fir.ref>) -> !fir.ref + %12 = fir.load %11 : !fir.ref + fir.store %12 to %10 : !fir.ref + return + } +} +// CHECK: #[[TEST4ROOT:.+]] = #llvm.tbaa_root +// CHECK: #[[INNER4ROOT:.+]] = #llvm.tbaa_root +// CHECK: #[[TEST4ANYCC:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[INNER4ANYACC:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST4ANYDATA:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[INNER4ANYDATA:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST4TARGET:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[INNER4TARGET:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST4GLOBAL:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[INNER4GLOBAL:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST4COMMON:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[INNER4COMMON:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST4B:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST4A:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[INNER4D:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[INNER4C:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST4BTAG:.+]] = #llvm.tbaa_tag +// CHECK: #[[TEST4ATAG:.+]] = #llvm.tbaa_tag +// CHECK: #[[INNER4DTAG:.+]] = #llvm.tbaa_tag +// CHECK: #[[INNER4CTAG:.+]] = #llvm.tbaa_tag +// CHECK-LABEL: func.func @_QPtest4() { +// CHECK: fir.load{{.*}}{tbaa = [#[[TEST4BTAG]]]} : !fir.ref +// CHECK: fir.store{{.*}}{tbaa = [#[[TEST4ATAG]]]} : !fir.ref +// CHECK: fir.load{{.*}}{tbaa = [#[[INNER4DTAG]]]} : !fir.ref +// CHECK: fir.store{{.*}}{tbaa = [#[[INNER4CTAG]]]} : !fir.ref + +// ----- + +// Fortran source with manually removed fir.declare for 'b': +// subroutine test5 +// real :: a, b +// common /common5/ a, b +// a = b +// end subroutine test5 +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + fir.global common @common5_(dense<0> : vector<8xi8>) {alignment = 4 : i64} : !fir.array<8xi8> + func.func @_QPtest5() { + %c4 = arith.constant 4 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@common5_) : !fir.ref> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ref + %4 = fir.declare %3 storage(%1[0]) {uniq_name = "_QFtest5Ea"} : (!fir.ref, !fir.ref>) -> !fir.ref + %5 = fir.coordinate_of %1, %c4 : (!fir.ref>, index) -> !fir.ref + %6 = fir.convert %5 : (!fir.ref) -> !fir.ref +// %7 = fir.declare %6 storage(%1[4]) {uniq_name = "_QFtest5Eb"} : (!fir.ref, !fir.ref>) -> !fir.ref + %8 = fir.load %6 : !fir.ref + fir.store %8 to %4 : !fir.ref + return + } +} +// CHECK: #[[TEST5ROOT:.+]] = #llvm.tbaa_root +// CHECK: #[[TEST5ANYACC:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST5ANYDATA:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST5TARGET:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST5GLOBAL:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST5COMMON5:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST5COMMON5TAG:.+]] = #llvm.tbaa_tag +// CHECK: #[[TEST5A:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TEST5ATAG:.+]] = #llvm.tbaa_tag +// CHECK-LABEL: func.func @_QPtest5() { +// CHECK: fir.load{{.*}}{tbaa = [#[[TEST5COMMON5TAG]]]} : !fir.ref +// CHECK: fir.store{{.*}}{tbaa = [#[[TEST5ATAG]]]} : !fir.ref + +// ----- + +// Fortran source: +// module m1 +// common /block/ a(10), b(10) +// end +// module m2 +// common /block/ c(20) +// end +// subroutine test6 +// use m1 +// use m2 +// a(1) = c(1) + b(1) +// end subroutine test6 +// +// Test that all accesses are using the same TBAA tag. +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + fir.global common @block_(dense<0> : vector<80xi8>) {alignment = 4 : i64} : !fir.array<80xi8> + func.func @_QPtest6() { + %c1 = arith.constant 1 : index + %c20 = arith.constant 20 : index + %c40 = arith.constant 40 : index + %c10 = arith.constant 10 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@block_) : !fir.ref> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ref> + %4 = fir.shape %c10 : (index) -> !fir.shape<1> + %5 = fir.declare %3(%4) storage(%1[0]) {uniq_name = "_QMm1Ea"} : (!fir.ref>, !fir.shape<1>, !fir.ref>) -> !fir.ref> + %6 = fir.coordinate_of %1, %c40 : (!fir.ref>, index) -> !fir.ref + %7 = fir.convert %6 : (!fir.ref) -> !fir.ref> + %8 = fir.declare %7(%4) storage(%1[40]) {uniq_name = "_QMm1Eb"} : (!fir.ref>, !fir.shape<1>, !fir.ref>) -> !fir.ref> + %9 = fir.convert %2 : (!fir.ref) -> !fir.ref> + %10 = fir.shape %c20 : (index) -> !fir.shape<1> + %11 = fir.declare %9(%10) storage(%1[0]) {uniq_name = "_QMm2Ec"} : (!fir.ref>, !fir.shape<1>, !fir.ref>) -> !fir.ref> + %12 = fir.array_coor %11(%10) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + %13 = fir.load %12 : !fir.ref + %14 = fir.array_coor %8(%4) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + %15 = fir.load %14 : !fir.ref + %16 = arith.addf %13, %15 fastmath : f32 + %17 = fir.array_coor %5(%4) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + fir.store %16 to %17 : !fir.ref + return + } +} +// CHECK: #[[$ATTR_0:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_1:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_2:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_4:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_5:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_6:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_7:.+]] = #llvm.tbaa_tag +// CHECK-LABEL: func.func @_QPtest6() { +// CHECK: fir.load{{.*}}{tbaa = [#[[$ATTR_7]]]} : !fir.ref +// CHECK: fir.load{{.*}}{tbaa = [#[[$ATTR_7]]]} : !fir.ref +// CHECK: fir.store{{.*}}{tbaa = [#[[$ATTR_7]]]} : !fir.ref + +// ----- + +// Fortran source: +// module m1 +// integer :: b +// common /block/ a(10), b +// end +// module m2 +// real, pointer :: p +// common /block/ p +// end +// subroutine test7 +// use m1 +// use m2 +// a(1) = p + b +// end subroutine test7 +// +// Test that: +// * access to 'p' is tagged with "target data", +// * access to 'b' is tagged with global data/block_/bytes_40_to_43 +// * access to 'a' is not tagged, because it overlaps with +// a descriptor. +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { fir.global common @block_(dense<0> : vector<44xi8>) {alignment = 4 : i64} : !fir.array<44xi8> + func.func @_QPtest7() { + %c1 = arith.constant 1 : index + %c40 = arith.constant 40 : index + %c10 = arith.constant 10 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@block_) : !fir.ref> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ref> + %4 = fir.shape %c10 : (index) -> !fir.shape<1> + %5 = fir.declare %3(%4) storage(%1[0]) {uniq_name = "_QMm1Ea"} : (!fir.ref>, !fir.shape<1>, !fir.ref>) -> !fir.ref> + %6 = fir.coordinate_of %1, %c40 : (!fir.ref>, index) -> !fir.ref + %7 = fir.convert %6 : (!fir.ref) -> !fir.ref + %8 = fir.declare %7 storage(%1[40]) {uniq_name = "_QMm1Eb"} : (!fir.ref, !fir.ref>) -> !fir.ref + %9 = fir.convert %1 : (!fir.ref>) -> !fir.ref> + %10 = fir.coordinate_of %9, %c0 : (!fir.ref>, index) -> !fir.ref + %11 = fir.convert %10 : (!fir.ref) -> !fir.ref>> + %12 = fir.declare %11 storage(%9[0]) {fortran_attrs = #fir.var_attrs, uniq_name = "_QMm2Ep"} : (!fir.ref>>, !fir.ref>) -> !fir.ref>> + %13 = fir.load %12 : !fir.ref>> + %14 = fir.box_addr %13 : (!fir.box>) -> !fir.ptr + %15 = fir.load %14 : !fir.ptr + %16 = fir.load %8 : !fir.ref + %17 = fir.convert %16 : (i32) -> f32 + %18 = arith.addf %15, %17 fastmath : f32 + %19 = fir.array_coor %5(%4) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + fir.store %18 to %19 : !fir.ref + return + } +} +// CHECK: #[[$ATTR_73:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_74:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_75:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_76:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_77:.+]] = #llvm.tbaa_tag +// CHECK: #[[$ATTR_78:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_79:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_80:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_81:.+]] = #llvm.tbaa_tag +// CHECK-LABEL: func.func @_QPtest7() { +// CHECK: fir.load %{{[0-9]+}} : !fir.ref>> +// CHECK: fir.load{{.*}} {tbaa = [#[[$ATTR_77]]]} : !fir.ptr +// CHECK: fir.load{{.*}}{tbaa = [#[[$ATTR_81]]]} : !fir.ref +// CHECK: fir.store %{{[0-9]+}} to %{{[0-9]+}} : !fir.ref + +// ----- + +// Fortran source: +// module m1 +// integer :: b +// common /block/ a(10), b +// end +// module m2 +// real, pointer :: p +// common /block/ p +// end +// subroutine test8 +// use m1 +// use m2 +// a(1) = p + b +// end subroutine test8 +// +// The storage for 'b' is manually made ambiguous. +// Test that none of the global data accesses is tagged, +// because there is member containing descriptor in 'block', +// and we cannot reliably trace the storage for 'b'. +// In most cases, the storage should be traceable. +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { fir.global common @block_(dense<0> : vector<44xi8>) {alignment = 4 : i64} : !fir.array<44xi8> + func.func @_QPtest8() { + %c1 = arith.constant 1 : index + %c40 = arith.constant 40 : index + %c10 = arith.constant 10 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@block_) : !fir.ref> + %ptrcast = fir.convert %1 : (!fir.ref>) -> !fir.llvm_ptr> + %tmpmem = fir.alloca !fir.llvm_ptr> + fir.store %ptrcast to %tmpmem : !fir.ref>> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ref> + %4 = fir.shape %c10 : (index) -> !fir.shape<1> + %5 = fir.declare %3(%4) storage(%1[0]) {uniq_name = "_QMm1Ea"} : (!fir.ref>, !fir.shape<1>, !fir.ref>) -> !fir.ref> + %6 = fir.coordinate_of %1, %c40 : (!fir.ref>, index) -> !fir.ref + %7 = fir.convert %6 : (!fir.ref) -> !fir.ref + %addr = fir.load %tmpmem : !fir.ref>> + %addrcast = fir.convert %addr : (!fir.llvm_ptr>) -> !fir.ref> + %8 = fir.declare %7 storage(%addrcast[40]) {uniq_name = "_QMm1Eb"} : (!fir.ref, !fir.ref>) -> !fir.ref + %9 = fir.convert %1 : (!fir.ref>) -> !fir.ref> + %10 = fir.coordinate_of %9, %c0 : (!fir.ref>, index) -> !fir.ref + %11 = fir.convert %10 : (!fir.ref) -> !fir.ref>> + %12 = fir.declare %11 storage(%9[0]) {fortran_attrs = #fir.var_attrs, uniq_name = "_QMm2Ep"} : (!fir.ref>>, !fir.ref>) -> !fir.ref>> + %13 = fir.load %12 : !fir.ref>> + %14 = fir.box_addr %13 : (!fir.box>) -> !fir.ptr + %15 = fir.load %14 : !fir.ptr + %16 = fir.load %8 : !fir.ref + %17 = fir.convert %16 : (i32) -> f32 + %18 = arith.addf %15, %17 fastmath : f32 + %19 = fir.array_coor %5(%4) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref + fir.store %18 to %19 : !fir.ref + return + } +} +// CHECK: #[[$ATTR_82:.+]] = #llvm.tbaa_root +// CHECK: #[[$ATTR_83:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_84:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_85:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_86:.+]] = #llvm.tbaa_tag +// CHECK: #[[$ATTR_87:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[$ATTR_88:.+]] = #llvm.tbaa_tag +// CHECK-LABEL: func.func @_QPtest8() { +// CHECK: fir.load %{{[0-9]+}} : !fir.ref>> +// CHECK: fir.load %{{[0-9]+}} {tbaa = [#[[$ATTR_86]]]} : !fir.ptr +// CHECK: fir.load %{{[0-9]+}} : !fir.ref +// CHECK: fir.store %{{[0-9]+}} to %{{[0-9]+}} : !fir.ref diff --git a/flang/test/Transforms/tbaa-for-global-equiv-vars.fir b/flang/test/Transforms/tbaa-for-global-equiv-vars.fir new file mode 100644 index 0000000000000..dbefa3f8e3f5f --- /dev/null +++ b/flang/test/Transforms/tbaa-for-global-equiv-vars.fir @@ -0,0 +1,86 @@ +// RUN: fir-opt --split-input-file --fir-add-alias-tags %s | FileCheck %s + +// Fortran source: +// module data1 +// real :: glob1, glob2 +// equivalence (glob1, glob2) +// end module data1 +// subroutine test1 +// use data1 +// glob1 = glob2 +// end subroutine test1 +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + fir.global @_QMdata1Eglob1 : !fir.array<4xi8> { + %0 = fir.zero_bits !fir.array<4xi8> + fir.has_value %0 : !fir.array<4xi8> + } + func.func @_QPtest1() { + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@_QMdata1Eglob1) : !fir.ref> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ptr + %4 = fir.declare %3 storage(%1[0]) {uniq_name = "_QMdata1Eglob1"} : (!fir.ptr, !fir.ref>) -> !fir.ptr + %5 = fir.declare %3 storage(%1[0]) {uniq_name = "_QMdata1Eglob2"} : (!fir.ptr, !fir.ref>) -> !fir.ptr + %6 = fir.load %5 : !fir.ptr + fir.store %6 to %4 : !fir.ptr + return + } +} +// CHECK: #[[ROOT1:.+]] = #llvm.tbaa_root +// CHECK: #[[ANYACC1:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[ANYDATA1:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TARGETDATA1:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOBALDATA1:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOB1COMMON:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOB1:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TAG:.+]] = #llvm.tbaa_tag +// CHECK: %[[VAL_7:.*]] = fir.load{{.*}}{tbaa = [#[[TAG]]]} : !fir.ptr +// CHECK: fir.store{{.*}}{tbaa = [#[[TAG]]]} : !fir.ptr + +// ----- + +// Fortran source: +// module data2 +// real :: glob1, glob2, glob3 +// equivalence (glob1, glob2) +// common /glob1/ glob1, glob3 +// end module data2 +// subroutine test2 +// use data2 +// glob1 = glob2 + glob3 +// end subroutine test2 +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + fir.global common @glob1_(dense<0> : vector<8xi8>) {alignment = 4 : i64} : !fir.array<8xi8> + func.func @_QPtest2() { + %c4 = arith.constant 4 : index + %c0 = arith.constant 0 : index + %0 = fir.dummy_scope : !fir.dscope + %1 = fir.address_of(@glob1_) : !fir.ref> + %2 = fir.coordinate_of %1, %c0 : (!fir.ref>, index) -> !fir.ref + %3 = fir.convert %2 : (!fir.ref) -> !fir.ptr + %4 = fir.declare %3 storage(%1[0]) {uniq_name = "_QMdata2Eglob1"} : (!fir.ptr, !fir.ref>) -> !fir.ptr + %5 = fir.declare %3 storage(%1[0]) {uniq_name = "_QMdata2Eglob2"} : (!fir.ptr, !fir.ref>) -> !fir.ptr + %6 = fir.coordinate_of %1, %c4 : (!fir.ref>, index) -> !fir.ref + %7 = fir.convert %6 : (!fir.ref) -> !fir.ref + %8 = fir.declare %7 storage(%1[4]) {uniq_name = "_QMdata2Eglob3"} : (!fir.ref, !fir.ref>) -> !fir.ref + %9 = fir.load %5 : !fir.ptr + %10 = fir.load %8 : !fir.ref + %11 = arith.addf %9, %10 fastmath : f32 + fir.store %11 to %4 : !fir.ptr + return + } +} +// CHECK: #[[ROOT2:.+]] = #llvm.tbaa_root +// CHECK: #[[ANYACC2:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[ANYDATA2:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[TARGETDATA2:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOBALDATA2:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOB1COMMON:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOB1GLOB2:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOB3:.+]] = #llvm.tbaa_type_desc}> +// CHECK: #[[GLOB1GLOB2TAG:.+]] = #llvm.tbaa_tag +// CHECK: #[[GLOB3TAG:.+]] = #llvm.tbaa_tag +// CHECK: fir.load{{.*}}{tbaa = [#[[GLOB1GLOB2TAG]]]} : !fir.ptr +// CHECK: fir.load{{.*}}{tbaa = [#[[GLOB3TAG]]]} : !fir.ref +// CHECK: fir.store{{.*}}{tbaa = [#[[GLOB1GLOB2TAG]]]} : !fir.ptr diff --git a/flang/test/Transforms/tbaa-for-local-vars.fir b/flang/test/Transforms/tbaa-for-local-vars.fir index 82058ffef290a..4eb6b2ecf31c4 100644 --- a/flang/test/Transforms/tbaa-for-local-vars.fir +++ b/flang/test/Transforms/tbaa-for-local-vars.fir @@ -65,6 +65,7 @@ // CHECK: %[[VAL_13:.*]] = fir.declare %[[VAL_1]] {uniq_name = ".tmp.func_result"} : (!fir.ref>) -> !fir.ref> // CHECK: %[[VAL_14:.*]] = fir.coordinate_of %[[VAL_13]], x : (!fir.ref>) -> !fir.ref // CHECK: %[[VAL_16:.*]] = fir.load %[[VAL_14]] {tbaa = [#[[$ATTR_13]]]} : !fir.ref +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { func.func @_QMmPtest(%arg0: !fir.ref {fir.bindc_name = "arg"}) { %cst = arith.constant 1.000000e+00 : f32 %0 = fir.alloca !fir.type<_QMmTt{x:f32}> {bindc_name = ".result"} @@ -89,4 +90,4 @@ func.func @_QMmPtest(%arg0: !fir.ref {fir.bindc_name = "arg"}) { fir.store %19 to %2 : !fir.ref return } - +} diff --git a/flang/test/Transforms/tbaa-local-alloc-threshold.fir b/flang/test/Transforms/tbaa-local-alloc-threshold.fir index 27c19a6e23095..d9c6ad3ef096c 100644 --- a/flang/test/Transforms/tbaa-local-alloc-threshold.fir +++ b/flang/test/Transforms/tbaa-local-alloc-threshold.fir @@ -11,6 +11,7 @@ // COUNT1-NOT: fir.store{{.*}}{tbaa = // COUNT0-NOT: fir.load{{.*}}{tbaa = // COUNT0-NOT: fir.store{{.*}}{tbaa = +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { func.func @_QPtest() { %0 = fir.dummy_scope : !fir.dscope %1 = fir.alloca f32 {bindc_name = "x", uniq_name = "_QFtestEx"} @@ -21,3 +22,4 @@ func.func @_QPtest() { fir.store %5 to %2 : !fir.ref return } +} diff --git a/flang/test/Transforms/tbaa-with-dummy-scope.fir b/flang/test/Transforms/tbaa-with-dummy-scope.fir index 7624de9431e08..4ae2b8efe2581 100644 --- a/flang/test/Transforms/tbaa-with-dummy-scope.fir +++ b/flang/test/Transforms/tbaa-with-dummy-scope.fir @@ -42,6 +42,7 @@ // CHECK: %[[VAL_10:.*]] = fir.dummy_scope : !fir.dscope // CHECK: %[[VAL_13:.*]] = fir.load %{{.*}} {tbaa = [#[[$ATTR_14]]]} : !fir.ref // CHECK: fir.store %{{.*}} {tbaa = [#[[$ATTR_15]]]} : !fir.ref +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { func.func @test1(%arg0: !fir.ref {fir.bindc_name = "x", fir.target}, %arg1: !fir.ref {fir.bindc_name = "y", fir.target}) { %scope_out = fir.dummy_scope : !fir.dscope %0 = fir.declare %arg0 dummy_scope %scope_out {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestEx"} : (!fir.ref, !fir.dscope) -> !fir.ref @@ -60,6 +61,7 @@ func.func @test1(%arg0: !fir.ref {fir.bindc_name = "x", fir.target}, %arg1: fir.store %8 to %6 : !fir.ref return } +} // ----- @@ -118,6 +120,7 @@ func.func @test1(%arg0: !fir.ref {fir.bindc_name = "x", fir.target}, %arg1: // CHECK: fir.store %[[VAL_14]] to %[[VAL_10]] {tbaa = [#[[$ATTR_49]]]} : !fir.ref // CHECK: %[[VAL_15:.*]] = fir.load %[[VAL_13]] {tbaa = [#[[$ATTR_50]]]} : !fir.ref // CHECK: fir.store %[[VAL_15]] to %[[VAL_12]] {tbaa = [#[[$ATTR_48]]]} : !fir.ref +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { func.func @_QMtestPcaller(%arg0: !fir.ref {fir.bindc_name = "z"}) { %0 = fir.dummy_scope : !fir.dscope %1 = fir.address_of(@_QMtestEx) : !fir.ref @@ -147,3 +150,4 @@ fir.global @_QMtestEy : f32 { %0 = fir.zero_bits f32 fir.has_value %0 : f32 } +} diff --git a/flang/test/Transforms/tbaa-with-dummy-scope2.fir b/flang/test/Transforms/tbaa-with-dummy-scope2.fir index fd711a4d70eb4..54902ca7d41e1 100644 --- a/flang/test/Transforms/tbaa-with-dummy-scope2.fir +++ b/flang/test/Transforms/tbaa-with-dummy-scope2.fir @@ -25,6 +25,7 @@ // are placed into the same TBAA root. Since glob is a global // and x is a dummy argument, TBAA ends up reporting no-alias // for them, which is incorrect. +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { func.func @_QPtest1() attributes {noinline} { %c1_i32 = arith.constant 1 : i32 %c2_i32 = arith.constant 2 : i32 @@ -40,6 +41,7 @@ func.func @_QPtest1() attributes {noinline} { fir.store %c2_i32 to %2 : !fir.ref return } +} // CHECK: #[[$ATTR_0:.+]] = #llvm.tbaa_root // CHECK: #[[$ATTR_1:.+]] = #llvm.tbaa_type_desc}> // CHECK: #[[$ATTR_2:.+]] = #llvm.tbaa_type_desc}> @@ -68,6 +70,7 @@ func.func @_QPtest1() attributes {noinline} { // ----- // This test has fir.dummy_scope in place, and TBAA is correct. +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { func.func @_QPtest2() attributes {noinline} { %c1_i32 = arith.constant 1 : i32 %c2_i32 = arith.constant 2 : i32 @@ -84,6 +87,7 @@ func.func @_QPtest2() attributes {noinline} { fir.store %c2_i32 to %2 : !fir.ref return } +} // CHECK: #[[$ATTR_0:.+]] = #llvm.tbaa_root // CHECK: #[[$ATTR_1:.+]] = #llvm.tbaa_root // CHECK: #[[$ATTR_2:.+]] = #llvm.tbaa_type_desc}> diff --git a/flang/test/Transforms/tbaa.fir b/flang/test/Transforms/tbaa.fir index 88e200f43b4ef..bbc0d235bef50 100644 --- a/flang/test/Transforms/tbaa.fir +++ b/flang/test/Transforms/tbaa.fir @@ -1,5 +1,6 @@ // RUN: fir-opt --split-input-file --fir-add-alias-tags %s | FileCheck %s +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { // subroutine oneArg(a) // integer :: a(:) // a(1) = a(2) @@ -37,9 +38,11 @@ // CHECK: fir.store %[[VAL_6]] to %[[VAL_7]] {tbaa = [#[[ONE_ARG_A_TAG]]]} : !fir.ref // CHECK: return // CHECK: } +} // ----- +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { // subroutine twoArg(a, b) // integer :: a(:), b(:) // a(1) = b(1) @@ -82,9 +85,11 @@ // CHECK: fir.store %[[VAL_8]] to %[[VAL_9]] {tbaa = [#[[TWO_ARG_A_TAG]]]} : !fir.ref // CHECK: return // CHECK: } +} // ----- +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { // subroutine targetArg(a, b) // integer, target :: a(:) // integer :: b(:) @@ -128,9 +133,11 @@ // CHECK: fir.store %[[VAL_8]] to %[[VAL_9]] {tbaa = [#[[TARGET_A_TAG]]]} : !fir.ref // CHECK: return // CHECK: } +} // ----- +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { // subroutine pointerArg(a, b) // integer, pointer :: a(:) // integer :: b(:) @@ -181,9 +188,12 @@ // CHECK: fir.store %[[VAL_8]] to %[[VAL_12]] {tbaa = [#[[POINTER_A_TAG]]]} : !fir.ref // CHECK: return // CHECK: } +} // ----- +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { + // Make sure we don't mistake other block arguments as dummy arguments: omp.declare_reduction @add_reduction_i32 : i32 init { @@ -218,3 +228,4 @@ fir.global internal @_QFEi : i32 { %c0_i32 = arith.constant 0 : i32 fir.has_value %c0_i32 : i32 } +} diff --git a/flang/test/Transforms/tbaa2.fir b/flang/test/Transforms/tbaa2.fir index 1429d0b420766..a594e6b32fdac 100644 --- a/flang/test/Transforms/tbaa2.fir +++ b/flang/test/Transforms/tbaa2.fir @@ -1,6 +1,7 @@ // Test fir alias analysis pass on a larger real life code example (from the RFC) // RUN: fir-opt --fir-add-alias-tags %s | FileCheck %s +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { fir.global @_QMmodEa : !fir.box>> { %c0 = arith.constant 0 : index %0 = fir.zero_bits !fir.heap> @@ -397,3 +398,4 @@ // CHECK: fir.store %[[VAL_144:.*]]#1 to %[[VAL_34]] {tbaa = [#[[LOCAL1_ALLOC_TAG]]]} : !fir.ref // CHECK: return // CHECK: } +} diff --git a/flang/test/Transforms/tbaa3.fir b/flang/test/Transforms/tbaa3.fir index 97bf69da1b99c..abcb7e000bac1 100644 --- a/flang/test/Transforms/tbaa3.fir +++ b/flang/test/Transforms/tbaa3.fir @@ -107,7 +107,7 @@ // LOCAL: #[[LOCALATAG:.+]] = #llvm.tbaa_tag // LOCAL: #[[LOCALATTAG:.+]] = #llvm.tbaa_tag -module { +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { fir.global @_QMdataEglob : !fir.array<10xf32> { %0 = fir.zero_bits !fir.array<10xf32> fir.has_value %0 : !fir.array<10xf32> diff --git a/flang/test/Transforms/tbaa4.fir b/flang/test/Transforms/tbaa4.fir index 6fa8fff02b6a6..c368a3d06c2ba 100644 --- a/flang/test/Transforms/tbaa4.fir +++ b/flang/test/Transforms/tbaa4.fir @@ -8,16 +8,21 @@ // ALL: #[[TARGETDATA:.+]] = #llvm.tbaa_type_desc}> // ALL: #[[GLOBALDATA:.+]] = #llvm.tbaa_type_desc}> // ALL: #[[BLK:.+]] = #llvm.tbaa_type_desc}> -// ALL: #[[TAG:.+]] = #llvm.tbaa_tag +// ALL: #[[BLK_A:.+]] = #llvm.tbaa_type_desc}> +// ALL: #[[BLK_C:.+]] = #llvm.tbaa_type_desc}> +// ALL: #[[BLK_B:.+]] = #llvm.tbaa_type_desc}> +// ALL: #[[BLK_A_TAG:.+]] = #llvm.tbaa_tag +// ALL: #[[BLK_C_TAG:.+]] = #llvm.tbaa_tag +// ALL: #[[BLK_B_TAG:.+]] = #llvm.tbaa_tag -module { +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { // ALL-LABEL: fir.global common @blk_(dense<0> : vector<48xi8>) {alignment = 4 : i64} : !fir.array<48xi8> fir.global common @blk_(dense<0> : vector<48xi8>) {alignment = 4 : i64} : !fir.array<48xi8> // ALL-LABEL: func.func @_QPtest_common() { -// ALL: fir.store{{.*}}{tbaa = [#[[TAG]]]} : !fir.ref -// ALL: fir.store{{.*}}{tbaa = [#[[TAG]]]} : !fir.ref -// ALL: fir.store{{.*}}{tbaa = [#[[TAG]]]} : !fir.ref +// ALL: fir.store{{.*}}{tbaa = [#[[BLK_A_TAG]]]} : !fir.ref +// ALL: fir.store{{.*}}{tbaa = [#[[BLK_C_TAG]]]} : !fir.ref +// ALL: fir.store{{.*}}{tbaa = [#[[BLK_B_TAG]]]} : !fir.ref func.func @_QPtest_common() { %c1 = arith.constant 1 : index %c1_i32 = arith.constant 1 : i32 @@ -31,14 +36,14 @@ module { %2 = fir.convert %1 : (!fir.ref>) -> !fir.ref> %3 = fir.coordinate_of %2, %c0 : (!fir.ref>, index) -> !fir.ref %4 = fir.convert %3 : (!fir.ref) -> !fir.ref - %5 = fir.declare %4 {uniq_name = "_QFtest_commonEa"} : (!fir.ref) -> !fir.ref + %5 = fir.declare %4 storage(%1[0]) {uniq_name = "_QFtest_commonEa"} : (!fir.ref, !fir.ref>) -> !fir.ref %6 = fir.coordinate_of %2, %c4 : (!fir.ref>, index) -> !fir.ref %7 = fir.convert %6 : (!fir.ref) -> !fir.ref - %8 = fir.declare %7 {uniq_name = "_QFtest_commonEb"} : (!fir.ref) -> !fir.ref + %8 = fir.declare %7 storage(%1[4]) {uniq_name = "_QFtest_commonEb"} : (!fir.ref, !fir.ref>) -> !fir.ref %9 = fir.coordinate_of %2, %c8 : (!fir.ref>, index) -> !fir.ref %10 = fir.convert %9 : (!fir.ref) -> !fir.ref> %11 = fir.shape %c10 : (index) -> !fir.shape<1> - %12 = fir.declare %10(%11) {uniq_name = "_QFtest_commonEc"} : (!fir.ref>, !fir.shape<1>) -> !fir.ref> + %12 = fir.declare %10(%11) storage(%1[8]) {uniq_name = "_QFtest_commonEc"} : (!fir.ref>, !fir.shape<1>, !fir.ref>) -> !fir.ref> fir.store %cst to %5 : !fir.ref %13 = fir.array_coor %12(%11) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref fir.store %c1_i32 to %13 : !fir.ref @@ -55,13 +60,14 @@ module { // LOCAL: #[[TARGETDATA:.+]] = #llvm.tbaa_type_desc}> // LOCAL: #[[ALLOCATEDDATA:.+]] = #llvm.tbaa_type_desc}> // LOCAL: #[[EQUIV:.+]] = #llvm.tbaa_type_desc}> -// LOCAL: #[[$ATTR_13:.+]] = #llvm.tbaa_tag +// LOCAL: #[[TAG:.+]] = #llvm.tbaa_tag // ALL-LABEL: func.func @_QPtest_local_equiv() { // LOCAL: fir.store{{.*}}{tbaa = [#[[TAG]]]} : !fir.ptr // LOCAL: fir.store{{.*}}{tbaa = [#[[TAG]]]} : !fir.ref // LOCAL: fir.store{{.*}}{tbaa = [#[[TAG]]]} : !fir.ptr // DEFAULT-NOT: fir.store{{.}}tbaa +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { func.func @_QPtest_local_equiv() { %c1 = arith.constant 1 : index %c1_i32 = arith.constant 1 : i32 @@ -85,6 +91,7 @@ func.func @_QPtest_local_equiv() { fir.store %cst to %5 : !fir.ptr return } +} // ----- @@ -95,7 +102,7 @@ func.func @_QPtest_local_equiv() { // ALL: #[[GLOBALDATA:.+]] = #llvm.tbaa_type_desc}> // ALL: #[[EQUIV:.+]] = #llvm.tbaa_type_desc}> // ALL: #[[TAG:.+]] = #llvm.tbaa_tag -module { +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { // ALL-LABEL: fir.global internal @_QFtest_save_equivEa : !fir.array<40xi8> { fir.global internal @_QFtest_save_equivEa : !fir.array<40xi8> { %0 = fir.zero_bits !fir.array<40xi8> @@ -141,7 +148,7 @@ module { // ALL: #[[EQUIV:.+]] = #llvm.tbaa_type_desc}> // ALL: #[[TAG:.+]] = #llvm.tbaa_tag -module { +module attributes {dlti.dl_spec = #dlti.dl_spec : vector<4xi64>, i1 = dense<8> : vector<2xi64>, i8 = dense<8> : vector<2xi64>, i16 = dense<16> : vector<2xi64>, i32 = dense<32> : vector<2xi64>, i64 = dense<[32, 64]> : vector<2xi64>, f16 = dense<16> : vector<2xi64>, f64 = dense<64> : vector<2xi64>, f128 = dense<128> : vector<2xi64>, "dlti.endianness" = "little">, llvm.data_layout = ""} { // ALL-LABEL: fir.global @_QMdataEa : !fir.array<40xi8> { fir.global @_QMdataEa : !fir.array<40xi8> { %0 = fir.zero_bits !fir.array<40xi8>