Skip to content

Commit 38d72b9

Browse files
committed
[SandboxIR] sandboxir::Use Part 2: uses/users
This patch implements uses/users for sandboxir::Value, including functions like getNumUses(), hasNUsesOrMore() etc.
1 parent c3125ad commit 38d72b9

File tree

3 files changed

+181
-1
lines changed

3 files changed

+181
-1
lines changed

llvm/include/llvm/SandboxIR/SandboxIR.h

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@ class Use {
8888
: LLVMUse(LLVMUse), User(User), Ctx(&Ctx) {}
8989
Use() : LLVMUse(nullptr), Ctx(nullptr) {}
9090

91+
friend class Value; // For constructor
9192
friend class User; // For constructor
9293
friend class OperandUseIterator; // For constructor
94+
friend class UserUseIterator; // For accessing members
9395

9496
public:
9597
operator Value *() const { return get(); }
@@ -137,6 +139,31 @@ class OperandUseIterator {
137139
}
138140
};
139141

142+
/// Returns user edge when dereferenced.
143+
class UserUseIterator {
144+
Use Use;
145+
/// Don't let the user create a non-empty UserUseIterator.
146+
UserUseIterator(const class Use &Use) : Use(Use) {}
147+
friend class Value; // For constructor
148+
149+
public:
150+
using difference_type = std::ptrdiff_t;
151+
using value_type = sandboxir::Use;
152+
using pointer = value_type *;
153+
using reference = value_type &;
154+
using iterator_category = std::input_iterator_tag;
155+
156+
UserUseIterator() = default;
157+
value_type operator*() const { return Use; }
158+
UserUseIterator &operator++();
159+
bool operator==(const UserUseIterator &Other) const {
160+
return Use == Other.Use;
161+
}
162+
bool operator!=(const UserUseIterator &Other) const {
163+
return !(*this == Other);
164+
}
165+
};
166+
140167
/// A SandboxIR Value has users. This is the base class.
141168
class Value {
142169
public:
@@ -188,9 +215,77 @@ class Value {
188215
virtual ~Value() = default;
189216
ClassID getSubclassID() const { return SubclassID; }
190217

218+
using use_iterator = UserUseIterator;
219+
using const_use_iterator = UserUseIterator;
220+
221+
use_iterator use_begin();
222+
const_use_iterator use_begin() const {
223+
return const_cast<Value *>(this)->use_begin();
224+
}
225+
use_iterator use_end() { return use_iterator(Use(nullptr, nullptr, Ctx)); }
226+
const_use_iterator use_end() const {
227+
return const_cast<Value *>(this)->use_end();
228+
}
229+
230+
iterator_range<use_iterator> uses() {
231+
return make_range<use_iterator>(use_begin(), use_end());
232+
}
233+
iterator_range<const_use_iterator> uses() const {
234+
return make_range<const_use_iterator>(use_begin(), use_end());
235+
}
236+
237+
/// Helper for mapped_iterator.
238+
struct UseToUser {
239+
User *operator()(const Use &Use) const { return &*Use.getUser(); }
240+
};
241+
242+
using user_iterator = mapped_iterator<sandboxir::UserUseIterator, UseToUser>;
243+
using const_user_iterator = user_iterator;
244+
245+
user_iterator user_begin();
246+
user_iterator user_end() {
247+
return user_iterator(Use(nullptr, nullptr, Ctx), UseToUser());
248+
}
249+
const_user_iterator user_begin() const {
250+
return const_cast<Value *>(this)->user_begin();
251+
}
252+
const_user_iterator user_end() const {
253+
return const_cast<Value *>(this)->user_end();
254+
}
255+
256+
iterator_range<user_iterator> users() {
257+
return make_range<user_iterator>(user_begin(), user_end());
258+
}
259+
iterator_range<const_user_iterator> users() const {
260+
return make_range<const_user_iterator>(user_begin(), user_end());
261+
}
262+
/// \Returns the number of user edges (not necessarily to unique users).
263+
/// WARNING: This is a linear-time operation.
264+
unsigned getNumUses() const;
265+
/// Return true if this value has N uses or more.
266+
/// This is logically equivalent to getNumUses() >= N.
267+
/// WARNING: This can be expensive, as it is linear to the number of users.
268+
bool hasNUsesOrMore(unsigned Num) const {
269+
unsigned Cnt = 0;
270+
for (auto It = use_begin(), ItE = use_end(); It != ItE; ++It) {
271+
if (++Cnt >= Num)
272+
return true;
273+
}
274+
return false;
275+
}
276+
/// Return true if this Value has exactly N uses.
277+
bool hasNUses(unsigned Num) const {
278+
unsigned Cnt = 0;
279+
for (auto It = use_begin(), ItE = use_end(); It != ItE; ++It) {
280+
if (++Cnt > Num)
281+
return false;
282+
}
283+
return Cnt == Num;
284+
}
285+
191286
Type *getType() const { return Val->getType(); }
192287

193-
Context &getContext() const;
288+
Context &getContext() const { return Ctx; }
194289
#ifndef NDEBUG
195290
/// Should crash if there is something wrong with the instruction.
196291
virtual void verify() const = 0;

llvm/lib/SandboxIR/SandboxIR.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/SandboxIR/SandboxIR.h"
10+
#include "llvm/ADT/SmallPtrSet.h"
1011
#include "llvm/IR/Constants.h"
1112
#include "llvm/Support/Debug.h"
1213
#include <sstream>
@@ -58,13 +59,50 @@ OperandUseIterator &OperandUseIterator::operator++() {
5859
return *this;
5960
}
6061

62+
UserUseIterator &UserUseIterator::operator++() {
63+
llvm::Use *&LLVMUse = Use.LLVMUse;
64+
assert(LLVMUse != nullptr && "Already at end!");
65+
LLVMUse = LLVMUse->getNext();
66+
if (LLVMUse == nullptr) {
67+
Use.User = nullptr;
68+
return *this;
69+
}
70+
auto *Ctx = Use.Ctx;
71+
auto *LLVMUser = LLVMUse->getUser();
72+
Use.User = cast_or_null<sandboxir::User>(Ctx->getValue(LLVMUser));
73+
return *this;
74+
}
75+
6176
Value::Value(ClassID SubclassID, llvm::Value *Val, Context &Ctx)
6277
: SubclassID(SubclassID), Val(Val), Ctx(Ctx) {
6378
#ifndef NDEBUG
6479
UID = Ctx.getNumValues();
6580
#endif
6681
}
6782

83+
Value::use_iterator Value::use_begin() {
84+
llvm::Use *LLVMUse = nullptr;
85+
if (Val->use_begin() != Val->use_end())
86+
LLVMUse = &*Val->use_begin();
87+
User *User = LLVMUse != nullptr ? cast_or_null<sandboxir::User>(Ctx.getValue(
88+
Val->use_begin()->getUser()))
89+
: nullptr;
90+
return use_iterator(Use(LLVMUse, User, Ctx));
91+
}
92+
93+
Value::user_iterator Value::user_begin() {
94+
auto UseBegin = Val->use_begin();
95+
auto UseEnd = Val->use_end();
96+
bool AtEnd = UseBegin == UseEnd;
97+
llvm::Use *LLVMUse = AtEnd ? nullptr : &*UseBegin;
98+
User *User =
99+
AtEnd ? nullptr
100+
: cast_or_null<sandboxir::User>(Ctx.getValue(&*LLVMUse->getUser()));
101+
return user_iterator(Use(LLVMUse, User, Ctx), UseToUser());
102+
}
103+
104+
unsigned Value::getNumUses() const { return range_size(Val->users()); }
105+
68106
#ifndef NDEBUG
69107
std::string Value::getName() const {
70108
std::stringstream SS;

llvm/unittests/SandboxIR/SandboxIRTest.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ define i32 @foo(i32 %v0, i32 %v1) {
143143
// Check Use.get().
144144
sandboxir::Value *Op = Use.get();
145145
EXPECT_EQ(Op, Ctx.getValue(LLVMI0->getOperand(OpIdx)));
146+
// Check Use.getUser().
147+
EXPECT_EQ(Use.getUser(), I0);
146148
// Check implicit cast to Value.
147149
sandboxir::Value *Cast = Use;
148150
EXPECT_EQ(Cast, Op);
@@ -180,6 +182,51 @@ User: %add0 = add i32 %v0, %v1 ; SB4. (Opaque)
180182
OperandNo: 0
181183
)IR");
182184
#endif // NDEBUG
185+
186+
// Check Value.user_begin().
187+
sandboxir::Value::user_iterator UIt = I0->user_begin();
188+
sandboxir::User *U = *UIt;
189+
EXPECT_EQ(U, Ret);
190+
// Check Value.uses().
191+
EXPECT_EQ(range_size(I0->uses()), 1u);
192+
EXPECT_EQ((*I0->uses().begin()).getUser(), Ret);
193+
// Check Value.users().
194+
EXPECT_EQ(range_size(I0->users()), 1u);
195+
EXPECT_EQ(*I0->users().begin(), Ret);
196+
// Check Value.getNumUses().
197+
EXPECT_EQ(I0->getNumUses(), 1u);
198+
// Check Value.hasNUsesOrMore().
199+
EXPECT_TRUE(I0->hasNUsesOrMore(0u));
200+
EXPECT_TRUE(I0->hasNUsesOrMore(1u));
201+
EXPECT_FALSE(I0->hasNUsesOrMore(2u));
202+
// Check Value.hasNUses().
203+
EXPECT_FALSE(I0->hasNUses(0u));
204+
EXPECT_TRUE(I0->hasNUses(1u));
205+
EXPECT_FALSE(I0->hasNUses(2u));
206+
}
207+
208+
// Check that the operands/users are counted correctly.
209+
// I1
210+
// / \
211+
// \ /
212+
// I2
213+
TEST_F(SandboxIRTest, DuplicateUses) {
214+
parseIR(C, R"IR(
215+
define void @foo(i8 %v) {
216+
%I1 = add i8 %v, %v
217+
%I2 = add i8 %I1, %I1
218+
ret void
219+
}
220+
)IR");
221+
Function &LLVMF = *M->getFunction("foo");
222+
sandboxir::Context Ctx(C);
223+
auto *F = Ctx.createFunction(&LLVMF);
224+
auto *BB = &*F->begin();
225+
auto It = BB->begin();
226+
auto *I1 = &*It++;
227+
auto *I2 = &*It++;
228+
EXPECT_EQ(range_size(I1->users()), 2u);
229+
EXPECT_EQ(range_size(I2->operands()), 2u);
183230
}
184231

185232
TEST_F(SandboxIRTest, Function) {

0 commit comments

Comments
 (0)