//===- MemoryModelRelaxationAnnotations.cpp ---------------------*- 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 // //===----------------------------------------------------------------------===// #include "llvm/IR/MemoryModelRelaxationAnnotations.h" #include "llvm/ADT/StringSet.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Metadata.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; //===- MMRAMetadata -------------------------------------------------------===// MMRAMetadata::MMRAMetadata(const Instruction &I) : MMRAMetadata(I.getMetadata(LLVMContext::MD_mmra)) {} MMRAMetadata::MMRAMetadata(MDNode *MD) { if (!MD) return; // TODO: Split this into a "tryParse" function that can return an err. // CTor can use the tryParse & just fatal on err. MDTuple *Tuple = dyn_cast(MD); assert(Tuple && "Invalid MMRA structure"); const auto HandleTagMD = [this](MDNode *TagMD) { Tags.insert({cast(TagMD->getOperand(0))->getString(), cast(TagMD->getOperand(1))->getString()}); }; if (isTagMD(Tuple)) { HandleTagMD(Tuple); return; } for (const MDOperand &Op : Tuple->operands()) { MDNode *MDOp = cast(Op.get()); assert(isTagMD(MDOp)); HandleTagMD(MDOp); } } bool MMRAMetadata::isTagMD(const Metadata *MD) { if (auto *Tuple = dyn_cast(MD)) { return Tuple->getNumOperands() == 2 && isa(Tuple->getOperand(0)) && isa(Tuple->getOperand(1)); } return false; } MDTuple *MMRAMetadata::getTagMD(LLVMContext &Ctx, StringRef Prefix, StringRef Suffix) { return MDTuple::get(Ctx, {MDString::get(Ctx, Prefix), MDString::get(Ctx, Suffix)}); } MDTuple *MMRAMetadata::getMD(LLVMContext &Ctx, ArrayRef Tags) { if (Tags.empty()) return nullptr; if (Tags.size() == 1) return getTagMD(Ctx, Tags.front()); SmallVector MMRAs; for (const auto &Tag : Tags) MMRAs.push_back(getTagMD(Ctx, Tag)); return MDTuple::get(Ctx, MMRAs); } MDNode *MMRAMetadata::combine(LLVMContext &Ctx, const MMRAMetadata &A, const MMRAMetadata &B) { // Let A and B be two tags set, and U be the prefix-wise union of A and B. // For every unique tag prefix P present in A or B: // * If either A or B has no tags with prefix P, no tags with prefix // P are added to U. // * If both A and B have at least one tag with prefix P, all tags with prefix // P from both sets are added to U. SmallVector Result; for (const auto &[P, S] : A) { if (B.hasTagWithPrefix(P)) Result.push_back(getTagMD(Ctx, P, S)); } for (const auto &[P, S] : B) { if (A.hasTagWithPrefix(P)) Result.push_back(getTagMD(Ctx, P, S)); } return MDTuple::get(Ctx, Result); } bool MMRAMetadata::hasTag(StringRef Prefix, StringRef Suffix) const { return Tags.count({Prefix, Suffix}); } bool MMRAMetadata::isCompatibleWith(const MMRAMetadata &Other) const { // Two sets of tags are compatible iff, for every unique tag prefix P // present in at least one set: // - the other set contains no tag with prefix P, or // - at least one tag with prefix P is common to both sets. StringMap PrefixStatuses; for (const auto &[P, S] : Tags) PrefixStatuses[P] |= (Other.hasTag(P, S) || !Other.hasTagWithPrefix(P)); for (const auto &[P, S] : Other) PrefixStatuses[P] |= (hasTag(P, S) || !hasTagWithPrefix(P)); for (auto &[Prefix, Status] : PrefixStatuses) { if (!Status) return false; } return true; } bool MMRAMetadata::hasTagWithPrefix(StringRef Prefix) const { for (const auto &[P, S] : Tags) if (P == Prefix) return true; return false; } MMRAMetadata::const_iterator MMRAMetadata::begin() const { return Tags.begin(); } MMRAMetadata::const_iterator MMRAMetadata::end() const { return Tags.end(); } bool MMRAMetadata::empty() const { return Tags.empty(); } unsigned MMRAMetadata::size() const { return Tags.size(); } void MMRAMetadata::print(raw_ostream &OS) const { bool IsFirst = true; // TODO: use map_iter + join for (const auto &[P, S] : Tags) { if (IsFirst) IsFirst = false; else OS << ", "; OS << P << ":" << S; } } LLVM_DUMP_METHOD void MMRAMetadata::dump() const { print(dbgs()); } //===- Helpers ------------------------------------------------------------===// static bool isReadWriteMemCall(const Instruction &I) { if (const auto *C = dyn_cast(&I)) return C->mayReadOrWriteMemory() || !C->getMemoryEffects().doesNotAccessMemory(); return false; } bool llvm::canInstructionHaveMMRAs(const Instruction &I) { return isa(I) || isa(I) || isa(I) || isa(I) || isa(I) || isReadWriteMemCall(I); }