1//===-- User.cpp - Implement the User class -------------------------------===//
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/User.h"
10#include "llvm/IR/Constant.h"
11#include "llvm/IR/Constants.h"
12#include "llvm/IR/GlobalValue.h"
13#include "llvm/IR/IntrinsicInst.h"
14
15using namespace llvm;
16
17namespace llvm {
18class BasicBlock;
19}
20
21//===----------------------------------------------------------------------===//
22// User Class
23//===----------------------------------------------------------------------===//
24
25bool User::replaceUsesOfWith(Value *From, Value *To) {
26 bool Changed = false;
27 if (From == To) return Changed; // Duh what?
28
29 assert((!isa<Constant>(this) || isa<GlobalValue>(this)) &&
30 "Cannot call User::replaceUsesOfWith on a constant!");
31
32 for (unsigned i = 0, E = getNumOperands(); i != E; ++i)
33 if (getOperand(i) == From) { // Is This operand is pointing to oldval?
34 // The side effects of this setOperand call include linking to
35 // "To", adding "this" to the uses list of To, and
36 // most importantly, removing "this" from the use list of "From".
37 setOperand(i, Val: To);
38 Changed = true;
39 }
40
41 return Changed;
42}
43
44//===----------------------------------------------------------------------===//
45// User allocHungoffUses Implementation
46//===----------------------------------------------------------------------===//
47
48void User::allocHungoffUses(unsigned N, bool WithExtraValues) {
49 assert(HasHungOffUses && "alloc must have hung off uses");
50
51 static_assert(alignof(Use) >= alignof(Value *),
52 "Alignment is insufficient for 'hung-off-uses' pieces");
53
54 // Allocate the array of Uses
55 size_t size = N * sizeof(Use);
56 if (WithExtraValues)
57 size += N * sizeof(Value *);
58 Use *Begin = static_cast<Use*>(::operator new(size));
59 Use *End = Begin + N;
60 setOperandList(Begin);
61 for (; Begin != End; Begin++)
62 new (Begin) Use(this);
63}
64
65void User::growHungoffUses(unsigned NewNumUses, bool WithExtraValues) {
66 assert(HasHungOffUses && "realloc must have hung off uses");
67
68 unsigned OldNumUses = getNumOperands();
69
70 // We don't support shrinking the number of uses. We wouldn't have enough
71 // space to copy the old uses in to the new space.
72 assert(NewNumUses > OldNumUses && "realloc must grow num uses");
73
74 Use *OldOps = getOperandList();
75 allocHungoffUses(N: NewNumUses, WithExtraValues);
76 Use *NewOps = getOperandList();
77
78 // Now copy from the old operands list to the new one.
79 std::copy(first: OldOps, last: OldOps + OldNumUses, result: NewOps);
80
81 // If the User has extra values (phi basic blocks, switch case values), then
82 // we need to copy these, too.
83 if (WithExtraValues) {
84 auto *OldPtr = reinterpret_cast<char *>(OldOps + OldNumUses);
85 auto *NewPtr = reinterpret_cast<char *>(NewOps + NewNumUses);
86 std::copy(first: OldPtr, last: OldPtr + (OldNumUses * sizeof(Value *)), result: NewPtr);
87 }
88 Use::zap(Start: OldOps, Stop: OldOps + OldNumUses, del: true);
89}
90
91// This is a private struct used by `User` to track the co-allocated descriptor
92// section.
93struct DescriptorInfo {
94 intptr_t SizeInBytes;
95};
96
97ArrayRef<const uint8_t> User::getDescriptor() const {
98 auto MutableARef = const_cast<User *>(this)->getDescriptor();
99 return {MutableARef.begin(), MutableARef.end()};
100}
101
102MutableArrayRef<uint8_t> User::getDescriptor() {
103 assert(HasDescriptor && "Don't call otherwise!");
104 assert(!HasHungOffUses && "Invariant!");
105
106 auto *DI = reinterpret_cast<DescriptorInfo *>(getIntrusiveOperands()) - 1;
107 assert(DI->SizeInBytes != 0 && "Should not have had a descriptor otherwise!");
108
109 return MutableArrayRef<uint8_t>(
110 reinterpret_cast<uint8_t *>(DI) - DI->SizeInBytes, DI->SizeInBytes);
111}
112
113bool User::isDroppable() const {
114 if (auto *II = dyn_cast<IntrinsicInst>(Val: this)) {
115 switch (II->getIntrinsicID()) {
116 default:
117 return false;
118 case Intrinsic::assume:
119 case Intrinsic::pseudoprobe:
120 case Intrinsic::experimental_noalias_scope_decl:
121 return true;
122 }
123 }
124 return false;
125}
126
127//===----------------------------------------------------------------------===//
128// User operator new Implementations
129//===----------------------------------------------------------------------===//
130
131void *User::allocateFixedOperandUser(size_t Size, unsigned Us,
132 unsigned DescBytes) {
133 assert(Us < (1u << NumUserOperandsBits) && "Too many operands");
134
135 static_assert(sizeof(DescriptorInfo) % sizeof(void *) == 0, "Required below");
136
137 unsigned DescBytesToAllocate =
138 DescBytes == 0 ? 0 : (DescBytes + sizeof(DescriptorInfo));
139 assert(DescBytesToAllocate % sizeof(void *) == 0 &&
140 "We need this to satisfy alignment constraints for Uses");
141
142 size_t LeadingSize = DescBytesToAllocate + sizeof(Use) * Us;
143
144 // Ensure we allocate at least one pointer's worth of space before the main
145 // user allocation. We use this memory to pass information from the destructor
146 // to the deletion operator, so it can recover the true allocation start.
147 LeadingSize = std::max(a: LeadingSize, b: sizeof(void *));
148
149 uint8_t *Storage = static_cast<uint8_t *>(::operator new(LeadingSize + Size));
150 User *Obj = reinterpret_cast<User *>(Storage + LeadingSize);
151 Use *Operands = reinterpret_cast<Use *>(Obj) - Us;
152 Obj->NumUserOperands = Us;
153 Obj->HasHungOffUses = false;
154 Obj->HasDescriptor = DescBytes != 0;
155
156 if (DescBytes != 0) {
157 auto *DescInfo = reinterpret_cast<DescriptorInfo *>(Operands) - 1;
158 DescInfo->SizeInBytes = DescBytes;
159 }
160
161 return Obj;
162}
163
164void *User::operator new(size_t Size, IntrusiveOperandsAllocMarker allocTrait) {
165 return allocateFixedOperandUser(Size, Us: allocTrait.NumOps, DescBytes: 0);
166}
167
168void *User::operator new(size_t Size,
169 IntrusiveOperandsAndDescriptorAllocMarker allocTrait) {
170 return allocateFixedOperandUser(Size, Us: allocTrait.NumOps,
171 DescBytes: allocTrait.DescBytes);
172}
173
174void *User::operator new(size_t Size, HungOffOperandsAllocMarker) {
175 // Allocate space for a single Use*
176 void *Storage = ::operator new(Size + sizeof(Use *));
177 Use **HungOffOperandList = static_cast<Use **>(Storage);
178 User *Obj = reinterpret_cast<User *>(HungOffOperandList + 1);
179 Obj->NumUserOperands = 0;
180 Obj->HasHungOffUses = true;
181 Obj->HasDescriptor = false;
182 *HungOffOperandList = nullptr;
183 return Obj;
184}
185
186//===----------------------------------------------------------------------===//
187// User operator delete Implementation
188//===----------------------------------------------------------------------===//
189
190User::~User() {
191 // Hung off uses use a single Use* before the User, while other subclasses
192 // use a Use[] allocated prior to the user.
193 void *AllocStart = nullptr;
194 if (HasHungOffUses) {
195 assert(!HasDescriptor && "not supported!");
196
197 Use **HungOffOperandList = reinterpret_cast<Use **>(this) - 1;
198 // drop the hung off uses.
199 Use::zap(Start: *HungOffOperandList, Stop: *HungOffOperandList + NumUserOperands,
200 /* Delete */ del: true);
201 AllocStart = HungOffOperandList;
202 } else if (HasDescriptor) {
203 Use *UseBegin = reinterpret_cast<Use *>(this) - NumUserOperands;
204 Use::zap(Start: UseBegin, Stop: UseBegin + NumUserOperands, /* Delete */ del: false);
205
206 auto *DI = reinterpret_cast<DescriptorInfo *>(UseBegin) - 1;
207 AllocStart = reinterpret_cast<uint8_t *>(DI) - DI->SizeInBytes;
208 } else if (NumUserOperands > 0) {
209 Use *Storage = reinterpret_cast<Use *>(this) - NumUserOperands;
210 Use::zap(Start: Storage, Stop: Storage + NumUserOperands,
211 /* Delete */ del: false);
212 AllocStart = Storage;
213 } else {
214 // Handle the edge case where there are no operands and no descriptor.
215 AllocStart = (void **)(this) - 1;
216 }
217
218 // Operator delete needs to know where the allocation started. To avoid
219 // use-after-destroy, we have to store the allocation start outside the User
220 // object memory. The `User` new operator always allocates least one pointer
221 // before the User, so we can use that to store the allocation start. As a
222 // special case, we avoid this extra prefix allocation for ConstantData
223 // instances, since those are extremely common.
224 if (!isa<ConstantData>(Val: this))
225 ((void **)this)[-1] = AllocStart;
226}
227
228void User::operator delete(void *Usr) { ::operator delete(((void **)Usr)[-1]); }
229
230void User::operator delete(void *Usr, HungOffOperandsAllocMarker) {
231 Use **HungOffOperandList = static_cast<Use **>(Usr) - 1;
232 ::operator delete(HungOffOperandList);
233}
234
235void User::operator delete(void *Usr,
236 IntrusiveOperandsAndDescriptorAllocMarker Marker) {
237 unsigned NumOps = Marker.NumOps;
238 Use *UseBegin = static_cast<Use *>(Usr) - NumOps;
239 auto *DI = reinterpret_cast<DescriptorInfo *>(UseBegin) - 1;
240 uint8_t *Storage = reinterpret_cast<uint8_t *>(DI) - DI->SizeInBytes;
241 ::operator delete(Storage);
242}
243
244void User::operator delete(void *Usr, IntrusiveOperandsAllocMarker Marker) {
245 unsigned NumOps = Marker.NumOps;
246 size_t LeadingSize = sizeof(Use) * NumOps;
247 // Handle the edge case where there are no operands and no descriptor.
248 LeadingSize = std::max(a: LeadingSize, b: sizeof(void *));
249 uint8_t *Storage = static_cast<uint8_t *>(Usr) - LeadingSize;
250 ::operator delete(Storage);
251}
252