| //===- 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/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<MDTuple>(MD); | |
| assert(Tuple && "Invalid MMRA structure"); | |
| const auto HandleTagMD = [this](MDNode *TagMD) { | |
| Tags.insert({cast<MDString>(TagMD->getOperand(0))->getString(), | |
| cast<MDString>(TagMD->getOperand(1))->getString()}); | |
| }; | |
| if (isTagMD(Tuple)) { | |
| HandleTagMD(Tuple); | |
| return; | |
| } | |
| for (const MDOperand &Op : Tuple->operands()) { | |
| MDNode *MDOp = cast<MDNode>(Op.get()); | |
| assert(isTagMD(MDOp)); | |
| HandleTagMD(MDOp); | |
| } | |
| } | |
| bool MMRAMetadata::isTagMD(const Metadata *MD) { | |
| if (auto *Tuple = dyn_cast<MDTuple>(MD)) { | |
| return Tuple->getNumOperands() == 2 && | |
| isa<MDString>(Tuple->getOperand(0)) && | |
| isa<MDString>(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<MMRAMetadata::TagT> Tags) { | |
| if (Tags.empty()) | |
| return nullptr; | |
| if (Tags.size() == 1) | |
| return getTagMD(Ctx, Tags.front()); | |
| SmallVector<Metadata *> 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<Metadata *> 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); | |
| } | |
| void MMRAMetadata::appendTags(Instruction &I, ArrayRef<TagT> Tags) { | |
| if (Tags.empty()) | |
| return; | |
| SmallVector<MMRAMetadata::TagT> MMRAs(Tags); | |
| LLVMContext &Ctx = I.getContext(); | |
| if (MDNode *Existing = I.getMetadata(LLVMContext::MD_mmra)) { | |
| // Merge with existing MMRA tags. | |
| MMRAMetadata Parsed(Existing); | |
| MMRAs.append(Parsed.begin(), Parsed.end()); | |
| } | |
| llvm::sort(MMRAs); | |
| MMRAs.erase(llvm::unique(MMRAs), MMRAs.end()); | |
| I.setMetadata(LLVMContext::MD_mmra, MMRAMetadata::getMD(Ctx, MMRAs)); | |
| } | |
| 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<bool> 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<CallBase>(&I)) | |
| return C->mayReadOrWriteMemory() || | |
| !C->getMemoryEffects().doesNotAccessMemory(); | |
| return false; | |
| } | |
| bool llvm::canInstructionHaveMMRAs(const Instruction &I) { | |
| return isa<LoadInst>(I) || isa<StoreInst>(I) || isa<AtomicCmpXchgInst>(I) || | |
| isa<AtomicRMWInst>(I) || isa<FenceInst>(I) || isReadWriteMemCall(I); | |
| } |