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