1//===- MemoryModelRelaxationAnnotations.cpp ---------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/IR/MemoryModelRelaxationAnnotations.h"
10#include "llvm/ADT/StringSet.h"
11#include "llvm/IR/Instructions.h"
12#include "llvm/IR/Metadata.h"
13#include "llvm/Support/Debug.h"
14#include "llvm/Support/raw_ostream.h"
15
16using namespace llvm;
17
18//===- MMRAMetadata -------------------------------------------------------===//
19
20MMRAMetadata::MMRAMetadata(const Instruction &I)
21 : MMRAMetadata(I.getMetadata(KindID: LLVMContext::MD_mmra)) {}
22
23MMRAMetadata::MMRAMetadata(MDNode *MD) {
24 if (!MD)
25 return;
26
27 // TODO: Split this into a "tryParse" function that can return an err.
28 // CTor can use the tryParse & just fatal on err.
29
30 MDTuple *Tuple = dyn_cast<MDTuple>(Val: MD);
31 assert(Tuple && "Invalid MMRA structure");
32
33 const auto HandleTagMD = [this](MDNode *TagMD) {
34 Tags.insert(V: {cast<MDString>(Val: TagMD->getOperand(I: 0))->getString(),
35 cast<MDString>(Val: TagMD->getOperand(I: 1))->getString()});
36 };
37
38 if (isTagMD(MD: Tuple)) {
39 HandleTagMD(Tuple);
40 return;
41 }
42
43 for (const MDOperand &Op : Tuple->operands()) {
44 MDNode *MDOp = cast<MDNode>(Val: Op.get());
45 assert(isTagMD(MDOp));
46 HandleTagMD(MDOp);
47 }
48}
49
50bool MMRAMetadata::isTagMD(const Metadata *MD) {
51 if (auto *Tuple = dyn_cast<MDTuple>(Val: MD)) {
52 return Tuple->getNumOperands() == 2 &&
53 isa<MDString>(Val: Tuple->getOperand(I: 0)) &&
54 isa<MDString>(Val: Tuple->getOperand(I: 1));
55 }
56 return false;
57}
58
59MDTuple *MMRAMetadata::getTagMD(LLVMContext &Ctx, StringRef Prefix,
60 StringRef Suffix) {
61 return MDTuple::get(Context&: Ctx,
62 MDs: {MDString::get(Context&: Ctx, Str: Prefix), MDString::get(Context&: Ctx, Str: Suffix)});
63}
64
65MDTuple *MMRAMetadata::getMD(LLVMContext &Ctx,
66 ArrayRef<MMRAMetadata::TagT> Tags) {
67 if (Tags.empty())
68 return nullptr;
69
70 if (Tags.size() == 1)
71 return getTagMD(Ctx, T: Tags.front());
72
73 SmallVector<Metadata *> MMRAs;
74 for (const auto &Tag : Tags)
75 MMRAs.push_back(Elt: getTagMD(Ctx, T: Tag));
76 return MDTuple::get(Context&: Ctx, MDs: MMRAs);
77}
78
79MDNode *MMRAMetadata::combine(LLVMContext &Ctx, const MMRAMetadata &A,
80 const MMRAMetadata &B) {
81 // Let A and B be two tags set, and U be the prefix-wise union of A and B.
82 // For every unique tag prefix P present in A or B:
83 // * If either A or B has no tags with prefix P, no tags with prefix
84 // P are added to U.
85 // * If both A and B have at least one tag with prefix P, all tags with prefix
86 // P from both sets are added to U.
87
88 SmallVector<Metadata *> Result;
89
90 for (const auto &[P, S] : A) {
91 if (B.hasTagWithPrefix(Prefix: P))
92 Result.push_back(Elt: getTagMD(Ctx, Prefix: P, Suffix: S));
93 }
94 for (const auto &[P, S] : B) {
95 if (A.hasTagWithPrefix(Prefix: P))
96 Result.push_back(Elt: getTagMD(Ctx, Prefix: P, Suffix: S));
97 }
98
99 return MDTuple::get(Context&: Ctx, MDs: Result);
100}
101
102bool MMRAMetadata::hasTag(StringRef Prefix, StringRef Suffix) const {
103 return Tags.count(V: {Prefix, Suffix});
104}
105
106bool MMRAMetadata::isCompatibleWith(const MMRAMetadata &Other) const {
107 // Two sets of tags are compatible iff, for every unique tag prefix P
108 // present in at least one set:
109 // - the other set contains no tag with prefix P, or
110 // - at least one tag with prefix P is common to both sets.
111
112 StringMap<bool> PrefixStatuses;
113 for (const auto &[P, S] : Tags)
114 PrefixStatuses[P] |= (Other.hasTag(Prefix: P, Suffix: S) || !Other.hasTagWithPrefix(Prefix: P));
115 for (const auto &[P, S] : Other)
116 PrefixStatuses[P] |= (hasTag(Prefix: P, Suffix: S) || !hasTagWithPrefix(Prefix: P));
117
118 for (auto &[Prefix, Status] : PrefixStatuses) {
119 if (!Status)
120 return false;
121 }
122
123 return true;
124}
125
126bool MMRAMetadata::hasTagWithPrefix(StringRef Prefix) const {
127 for (const auto &[P, S] : Tags)
128 if (P == Prefix)
129 return true;
130 return false;
131}
132
133MMRAMetadata::const_iterator MMRAMetadata::begin() const {
134 return Tags.begin();
135}
136
137MMRAMetadata::const_iterator MMRAMetadata::end() const { return Tags.end(); }
138
139bool MMRAMetadata::empty() const { return Tags.empty(); }
140
141unsigned MMRAMetadata::size() const { return Tags.size(); }
142
143void MMRAMetadata::print(raw_ostream &OS) const {
144 bool IsFirst = true;
145 // TODO: use map_iter + join
146 for (const auto &[P, S] : Tags) {
147 if (IsFirst)
148 IsFirst = false;
149 else
150 OS << ", ";
151 OS << P << ":" << S;
152 }
153}
154
155LLVM_DUMP_METHOD
156void MMRAMetadata::dump() const { print(OS&: dbgs()); }
157
158//===- Helpers ------------------------------------------------------------===//
159
160static bool isReadWriteMemCall(const Instruction &I) {
161 if (const auto *C = dyn_cast<CallBase>(Val: &I))
162 return C->mayReadOrWriteMemory() ||
163 !C->getMemoryEffects().doesNotAccessMemory();
164 return false;
165}
166
167bool llvm::canInstructionHaveMMRAs(const Instruction &I) {
168 return isa<LoadInst>(Val: I) || isa<StoreInst>(Val: I) || isa<AtomicCmpXchgInst>(Val: I) ||
169 isa<AtomicRMWInst>(Val: I) || isa<FenceInst>(Val: I) || isReadWriteMemCall(I);
170}
171