Skip to content

Commit eb97761

Browse files
authored
[SandboxIR] sandboxir::Use operands (part 1) and uses (part 2) (#98251)
This PR adds the Use class and several operands-related functions to the User class (part 1) and several uses-related functions to the Value class (part 2).
1 parent 5f1bb62 commit eb97761

File tree

3 files changed

+437
-1
lines changed

3 files changed

+437
-1
lines changed

llvm/include/llvm/SandboxIR/SandboxIR.h

Lines changed: 223 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,96 @@ namespace sandboxir {
7171
class Function;
7272
class Context;
7373
class Instruction;
74+
class User;
75+
class Value;
76+
77+
/// Represents a Def-use/Use-def edge in SandboxIR.
78+
/// NOTE: Unlike llvm::Use, this is not an integral part of the use-def chains.
79+
/// It is also not uniqued and is currently passed by value, so you can have
80+
/// more than one sandboxir::Use objects for the same use-def edge.
81+
class Use {
82+
llvm::Use *LLVMUse;
83+
User *Usr;
84+
Context *Ctx;
85+
86+
/// Don't allow the user to create a sandboxir::Use directly.
87+
Use(llvm::Use *LLVMUse, User *Usr, Context &Ctx)
88+
: LLVMUse(LLVMUse), Usr(Usr), Ctx(&Ctx) {}
89+
Use() : LLVMUse(nullptr), Ctx(nullptr) {}
90+
91+
friend class Value; // For constructor
92+
friend class User; // For constructor
93+
friend class OperandUseIterator; // For constructor
94+
friend class UserUseIterator; // For accessing members
95+
96+
public:
97+
operator Value *() const { return get(); }
98+
Value *get() const;
99+
class User *getUser() const { return Usr; }
100+
unsigned getOperandNo() const;
101+
Context *getContext() const { return Ctx; }
102+
bool operator==(const Use &Other) const {
103+
assert(Ctx == Other.Ctx && "Contexts differ!");
104+
return LLVMUse == Other.LLVMUse && Usr == Other.Usr;
105+
}
106+
bool operator!=(const Use &Other) const { return !(*this == Other); }
107+
#ifndef NDEBUG
108+
void dump(raw_ostream &OS) const;
109+
void dump() const;
110+
#endif // NDEBUG
111+
};
112+
113+
/// Returns the operand edge when dereferenced.
114+
class OperandUseIterator {
115+
Use Use;
116+
/// Don't let the user create a non-empty OperandUseIterator.
117+
OperandUseIterator(const class Use &Use) : Use(Use) {}
118+
friend class User; // For constructor
119+
#define DEF_INSTR(ID, OPC, CLASS) friend class CLASS; // For constructor
120+
#include "llvm/SandboxIR/SandboxIRValues.def"
121+
122+
public:
123+
using difference_type = std::ptrdiff_t;
124+
using value_type = sandboxir::Use;
125+
using pointer = value_type *;
126+
using reference = value_type &;
127+
using iterator_category = std::input_iterator_tag;
128+
129+
OperandUseIterator() = default;
130+
value_type operator*() const;
131+
OperandUseIterator &operator++();
132+
bool operator==(const OperandUseIterator &Other) const {
133+
return Use == Other.Use;
134+
}
135+
bool operator!=(const OperandUseIterator &Other) const {
136+
return !(*this == Other);
137+
}
138+
};
139+
140+
/// Returns user edge when dereferenced.
141+
class UserUseIterator {
142+
Use Use;
143+
/// Don't let the user create a non-empty UserUseIterator.
144+
UserUseIterator(const class Use &Use) : Use(Use) {}
145+
friend class Value; // For constructor
146+
147+
public:
148+
using difference_type = std::ptrdiff_t;
149+
using value_type = sandboxir::Use;
150+
using pointer = value_type *;
151+
using reference = value_type &;
152+
using iterator_category = std::input_iterator_tag;
153+
154+
UserUseIterator() = default;
155+
value_type operator*() const { return Use; }
156+
UserUseIterator &operator++();
157+
bool operator==(const UserUseIterator &Other) const {
158+
return Use == Other.Use;
159+
}
160+
bool operator!=(const UserUseIterator &Other) const {
161+
return !(*this == Other);
162+
}
163+
};
74164

75165
/// A SandboxIR Value has users. This is the base class.
76166
class Value {
@@ -123,9 +213,77 @@ class Value {
123213
virtual ~Value() = default;
124214
ClassID getSubclassID() const { return SubclassID; }
125215

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

128-
Context &getContext() const;
286+
Context &getContext() const { return Ctx; }
129287
#ifndef NDEBUG
130288
/// Should crash if there is something wrong with the instruction.
131289
virtual void verify() const = 0;
@@ -174,9 +332,61 @@ class User : public Value {
174332
protected:
175333
User(ClassID ID, llvm::Value *V, Context &Ctx) : Value(ID, V, Ctx) {}
176334

335+
/// \Returns the Use edge that corresponds to \p OpIdx.
336+
/// Note: This is the default implementation that works for instructions that
337+
/// match the underlying LLVM instruction. All others should use a different
338+
/// implementation.
339+
Use getOperandUseDefault(unsigned OpIdx, bool Verify) const;
340+
virtual Use getOperandUseInternal(unsigned OpIdx, bool Verify) const = 0;
341+
friend class OperandUseIterator; // for getOperandUseInternal()
342+
343+
/// The default implementation works only for single-LLVMIR-instruction
344+
/// Users and only if they match exactly the LLVM instruction.
345+
unsigned getUseOperandNoDefault(const Use &Use) const {
346+
return Use.LLVMUse->getOperandNo();
347+
}
348+
/// \Returns the operand index of \p Use.
349+
virtual unsigned getUseOperandNo(const Use &Use) const = 0;
350+
friend unsigned Use::getOperandNo() const; // For getUseOperandNo()
351+
177352
public:
178353
/// For isa/dyn_cast.
179354
static bool classof(const Value *From);
355+
using op_iterator = OperandUseIterator;
356+
using const_op_iterator = OperandUseIterator;
357+
using op_range = iterator_range<op_iterator>;
358+
using const_op_range = iterator_range<const_op_iterator>;
359+
360+
virtual op_iterator op_begin() {
361+
assert(isa<llvm::User>(Val) && "Expect User value!");
362+
return op_iterator(getOperandUseInternal(0, /*Verify=*/false));
363+
}
364+
virtual op_iterator op_end() {
365+
assert(isa<llvm::User>(Val) && "Expect User value!");
366+
return op_iterator(
367+
getOperandUseInternal(getNumOperands(), /*Verify=*/false));
368+
}
369+
virtual const_op_iterator op_begin() const {
370+
return const_cast<User *>(this)->op_begin();
371+
}
372+
virtual const_op_iterator op_end() const {
373+
return const_cast<User *>(this)->op_end();
374+
}
375+
376+
op_range operands() { return make_range<op_iterator>(op_begin(), op_end()); }
377+
const_op_range operands() const {
378+
return make_range<const_op_iterator>(op_begin(), op_end());
379+
}
380+
Value *getOperand(unsigned OpIdx) const { return getOperandUse(OpIdx).get(); }
381+
/// \Returns the operand edge for \p OpIdx. NOTE: This should also work for
382+
/// OpIdx == getNumOperands(), which is used for op_end().
383+
Use getOperandUse(unsigned OpIdx) const {
384+
return getOperandUseInternal(OpIdx, /*Verify=*/true);
385+
}
386+
virtual unsigned getNumOperands() const {
387+
return isa<llvm::User>(Val) ? cast<llvm::User>(Val)->getNumOperands() : 0;
388+
}
389+
180390
#ifndef NDEBUG
181391
void verify() const override {
182392
assert(isa<llvm::User>(Val) && "Expected User!");
@@ -195,6 +405,9 @@ class Constant : public sandboxir::User {
195405
Constant(llvm::Constant *C, sandboxir::Context &SBCtx)
196406
: sandboxir::User(ClassID::Constant, C, SBCtx) {}
197407
friend class Context; // For constructor.
408+
Use getOperandUseInternal(unsigned OpIdx, bool Verify) const final {
409+
return getOperandUseDefault(OpIdx, Verify);
410+
}
198411

199412
public:
200413
/// For isa/dyn_cast.
@@ -203,6 +416,9 @@ class Constant : public sandboxir::User {
203416
From->getSubclassID() == ClassID::Function;
204417
}
205418
sandboxir::Context &getParent() const { return getContext(); }
419+
unsigned getUseOperandNo(const Use &Use) const final {
420+
return getUseOperandNoDefault(Use);
421+
}
206422
#ifndef NDEBUG
207423
void verify() const final {
208424
assert(isa<llvm::Constant>(Val) && "Expected Constant!");
@@ -309,11 +525,17 @@ class OpaqueInst : public sandboxir::Instruction {
309525
OpaqueInst(ClassID SubclassID, llvm::Instruction *I, sandboxir::Context &Ctx)
310526
: sandboxir::Instruction(SubclassID, Opcode::Opaque, I, Ctx) {}
311527
friend class Context; // For constructor.
528+
Use getOperandUseInternal(unsigned OpIdx, bool Verify) const final {
529+
return getOperandUseDefault(OpIdx, Verify);
530+
}
312531

313532
public:
314533
static bool classof(const sandboxir::Value *From) {
315534
return From->getSubclassID() == ClassID::Opaque;
316535
}
536+
unsigned getUseOperandNo(const Use &Use) const final {
537+
return getUseOperandNoDefault(Use);
538+
}
317539
unsigned getNumOfIRInstrs() const final { return 1u; }
318540
#ifndef NDEBUG
319541
void verify() const final {

llvm/lib/SandboxIR/SandboxIR.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,102 @@
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>
1314

1415
using namespace llvm::sandboxir;
1516

17+
Value *Use::get() const { return Ctx->getValue(LLVMUse->get()); }
18+
19+
unsigned Use::getOperandNo() const { return Usr->getUseOperandNo(*this); }
20+
21+
#ifndef NDEBUG
22+
void Use::dump(raw_ostream &OS) const {
23+
Value *Def = nullptr;
24+
if (LLVMUse == nullptr)
25+
OS << "<null> LLVM Use! ";
26+
else
27+
Def = Ctx->getValue(LLVMUse->get());
28+
OS << "Def: ";
29+
if (Def == nullptr)
30+
OS << "NULL";
31+
else
32+
OS << *Def;
33+
OS << "\n";
34+
35+
OS << "User: ";
36+
if (Usr == nullptr)
37+
OS << "NULL";
38+
else
39+
OS << *Usr;
40+
OS << "\n";
41+
42+
OS << "OperandNo: ";
43+
if (Usr == nullptr)
44+
OS << "N/A";
45+
else
46+
OS << getOperandNo();
47+
OS << "\n";
48+
}
49+
50+
void Use::dump() const { dump(dbgs()); }
51+
#endif // NDEBUG
52+
53+
Use OperandUseIterator::operator*() const { return Use; }
54+
55+
OperandUseIterator &OperandUseIterator::operator++() {
56+
assert(Use.LLVMUse != nullptr && "Already at end!");
57+
User *User = Use.getUser();
58+
Use = User->getOperandUseInternal(Use.getOperandNo() + 1, /*Verify=*/false);
59+
return *this;
60+
}
61+
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.Usr = nullptr;
68+
return *this;
69+
}
70+
auto *Ctx = Use.Ctx;
71+
auto *LLVMUser = LLVMUse->getUser();
72+
Use.Usr = cast_or_null<sandboxir::User>(Ctx->getValue(LLVMUser));
73+
return *this;
74+
}
75+
1676
Value::Value(ClassID SubclassID, llvm::Value *Val, Context &Ctx)
1777
: SubclassID(SubclassID), Val(Val), Ctx(Ctx) {
1878
#ifndef NDEBUG
1979
UID = Ctx.getNumValues();
2080
#endif
2181
}
2282

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+
23106
#ifndef NDEBUG
24107
std::string Value::getName() const {
25108
std::stringstream SS;
@@ -71,6 +154,17 @@ void Argument::dump() const {
71154
}
72155
#endif // NDEBUG
73156

157+
Use User::getOperandUseDefault(unsigned OpIdx, bool Verify) const {
158+
assert((!Verify || OpIdx < getNumOperands()) && "Out of bounds!");
159+
assert(isa<llvm::User>(Val) && "Non-users have no operands!");
160+
llvm::Use *LLVMUse;
161+
if (OpIdx != getNumOperands())
162+
LLVMUse = &cast<llvm::User>(Val)->getOperandUse(OpIdx);
163+
else
164+
LLVMUse = cast<llvm::User>(Val)->op_end();
165+
return Use(LLVMUse, const_cast<User *>(this), Ctx);
166+
}
167+
74168
bool User::classof(const Value *From) {
75169
switch (From->getSubclassID()) {
76170
#define DEF_VALUE(ID, CLASS)

0 commit comments

Comments
 (0)