Skip to content

Commit 2699f85

Browse files
committed
[SandboxIR] sandboxir::Use Part 1: operands
This patch adds the Use class which is used by the new member functions of the User class that provide access to the operands.
1 parent 2d8b282 commit 2699f85

File tree

3 files changed

+256
-0
lines changed

3 files changed

+256
-0
lines changed

llvm/include/llvm/SandboxIR/SandboxIR.h

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,69 @@ 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 User; // For constructor
92+
friend class OperandUseIterator; // For constructor
93+
94+
public:
95+
operator Value *() const { return get(); }
96+
Value *get() const;
97+
class User *getUser() const { return Usr; }
98+
unsigned getOperandNo() const;
99+
Context *getContext() const { return Ctx; }
100+
bool operator==(const Use &Other) const {
101+
assert(Ctx == Other.Ctx && "Contexts differ!");
102+
return LLVMUse == Other.LLVMUse && Usr == Other.Usr;
103+
}
104+
bool operator!=(const Use &Other) const { return !(*this == Other); }
105+
#ifndef NDEBUG
106+
void dump(raw_ostream &OS) const;
107+
void dump() const;
108+
#endif // NDEBUG
109+
};
110+
111+
/// Returns the operand edge when dereferenced.
112+
class OperandUseIterator {
113+
Use Use;
114+
/// Don't let the user create a non-empty OperandUseIterator.
115+
OperandUseIterator(const class Use &Use) : Use(Use) {}
116+
friend class User; // For constructor
117+
#define DEF_INSTR(ID, OPC, CLASS) friend class CLASS; // For constructor
118+
#include "llvm/SandboxIR/SandboxIRValues.def"
119+
120+
public:
121+
using difference_type = std::ptrdiff_t;
122+
using value_type = sandboxir::Use;
123+
using pointer = value_type *;
124+
using reference = value_type &;
125+
using iterator_category = std::input_iterator_tag;
126+
127+
OperandUseIterator() = default;
128+
value_type operator*() const;
129+
OperandUseIterator &operator++();
130+
bool operator==(const OperandUseIterator &Other) const {
131+
return Use == Other.Use;
132+
}
133+
bool operator!=(const OperandUseIterator &Other) const {
134+
return !(*this == Other);
135+
}
136+
};
74137

75138
/// A SandboxIR Value has users. This is the base class.
76139
class Value {
@@ -174,9 +237,61 @@ class User : public Value {
174237
protected:
175238
User(ClassID ID, llvm::Value *V, Context &Ctx) : Value(ID, V, Ctx) {}
176239

240+
/// \Returns the Use edge that corresponds to \p OpIdx.
241+
/// Note: This is the default implementation that works for instructions that
242+
/// match the underlying LLVM instruction. All others should use a different
243+
/// implementation.
244+
Use getOperandUseDefault(unsigned OpIdx, bool Verify) const;
245+
virtual Use getOperandUseInternal(unsigned OpIdx, bool Verify) const = 0;
246+
friend class OperandUseIterator; // for getOperandUseInternal()
247+
248+
/// The default implementation works only for single-LLVMIR-instruction
249+
/// Users and only if they match exactly the LLVM instruction.
250+
unsigned getUseOperandNoDefault(const Use &Use) const {
251+
return Use.LLVMUse->getOperandNo();
252+
}
253+
/// \Returns the operand index of \p Use.
254+
virtual unsigned getUseOperandNo(const Use &Use) const = 0;
255+
friend unsigned Use::getOperandNo() const; // For getUseOperandNo()
256+
177257
public:
178258
/// For isa/dyn_cast.
179259
static bool classof(const Value *From);
260+
using op_iterator = OperandUseIterator;
261+
using const_op_iterator = OperandUseIterator;
262+
using op_range = iterator_range<op_iterator>;
263+
using const_op_range = iterator_range<const_op_iterator>;
264+
265+
virtual op_iterator op_begin() {
266+
assert(isa<llvm::User>(Val) && "Expect User value!");
267+
return op_iterator(getOperandUseInternal(0, /*Verify=*/false));
268+
}
269+
virtual op_iterator op_end() {
270+
assert(isa<llvm::User>(Val) && "Expect User value!");
271+
return op_iterator(
272+
getOperandUseInternal(getNumOperands(), /*Verify=*/false));
273+
}
274+
virtual const_op_iterator op_begin() const {
275+
return const_cast<User *>(this)->op_begin();
276+
}
277+
virtual const_op_iterator op_end() const {
278+
return const_cast<User *>(this)->op_end();
279+
}
280+
281+
op_range operands() { return make_range<op_iterator>(op_begin(), op_end()); }
282+
const_op_range operands() const {
283+
return make_range<const_op_iterator>(op_begin(), op_end());
284+
}
285+
Value *getOperand(unsigned OpIdx) const { return getOperandUse(OpIdx).get(); }
286+
/// \Returns the operand edge for \p OpIdx. NOTE: This should also work for
287+
/// OpIdx == getNumOperands(), which is used for op_end().
288+
Use getOperandUse(unsigned OpIdx) const {
289+
return getOperandUseInternal(OpIdx, /*Verify=*/true);
290+
}
291+
virtual unsigned getNumOperands() const {
292+
return isa<llvm::User>(Val) ? cast<llvm::User>(Val)->getNumOperands() : 0;
293+
}
294+
180295
#ifndef NDEBUG
181296
void verify() const override {
182297
assert(isa<llvm::User>(Val) && "Expected User!");
@@ -195,6 +310,9 @@ class Constant : public sandboxir::User {
195310
Constant(llvm::Constant *C, sandboxir::Context &SBCtx)
196311
: sandboxir::User(ClassID::Constant, C, SBCtx) {}
197312
friend class Context; // For constructor.
313+
Use getOperandUseInternal(unsigned OpIdx, bool Verify) const final {
314+
return getOperandUseDefault(OpIdx, Verify);
315+
}
198316

199317
public:
200318
/// For isa/dyn_cast.
@@ -203,6 +321,9 @@ class Constant : public sandboxir::User {
203321
From->getSubclassID() == ClassID::Function;
204322
}
205323
sandboxir::Context &getParent() const { return getContext(); }
324+
unsigned getUseOperandNo(const Use &Use) const final {
325+
return getUseOperandNoDefault(Use);
326+
}
206327
#ifndef NDEBUG
207328
void verify() const final {
208329
assert(isa<llvm::Constant>(Val) && "Expected Constant!");
@@ -309,11 +430,17 @@ class OpaqueInst : public sandboxir::Instruction {
309430
OpaqueInst(ClassID SubclassID, llvm::Instruction *I, sandboxir::Context &Ctx)
310431
: sandboxir::Instruction(SubclassID, Opcode::Opaque, I, Ctx) {}
311432
friend class Context; // For constructor.
433+
Use getOperandUseInternal(unsigned OpIdx, bool Verify) const final {
434+
return getOperandUseDefault(OpIdx, Verify);
435+
}
312436

313437
public:
314438
static bool classof(const sandboxir::Value *From) {
315439
return From->getSubclassID() == ClassID::Opaque;
316440
}
441+
unsigned getUseOperandNo(const Use &Use) const final {
442+
return getUseOperandNoDefault(Use);
443+
}
317444
unsigned getNumOfIRInstrs() const final { return 1u; }
318445
#ifndef NDEBUG
319446
void verify() const final {

llvm/lib/SandboxIR/SandboxIR.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,51 @@
1313

1414
using namespace llvm::sandboxir;
1515

16+
Value *Use::get() const { return Ctx->getValue(LLVMUse->get()); }
17+
18+
unsigned Use::getOperandNo() const { return Usr->getUseOperandNo(*this); }
19+
20+
#ifndef NDEBUG
21+
void Use::dump(raw_ostream &OS) const {
22+
Value *Def = nullptr;
23+
if (LLVMUse == nullptr)
24+
OS << "<null> LLVM Use! ";
25+
else
26+
Def = Ctx->getValue(LLVMUse->get());
27+
OS << "Def: ";
28+
if (Def == nullptr)
29+
OS << "NULL";
30+
else
31+
OS << *Def;
32+
OS << "\n";
33+
34+
OS << "User: ";
35+
if (Usr == nullptr)
36+
OS << "NULL";
37+
else
38+
OS << *Usr;
39+
OS << "\n";
40+
41+
OS << "OperandNo: ";
42+
if (Usr == nullptr)
43+
OS << "N/A";
44+
else
45+
OS << getOperandNo();
46+
OS << "\n";
47+
}
48+
49+
void Use::dump() const { dump(dbgs()); }
50+
#endif // NDEBUG
51+
52+
Use OperandUseIterator::operator*() const { return Use; }
53+
54+
OperandUseIterator &OperandUseIterator::operator++() {
55+
assert(Use.LLVMUse != nullptr && "Already at end!");
56+
User *User = Use.getUser();
57+
Use = User->getOperandUseInternal(Use.getOperandNo() + 1, /*Verify=*/false);
58+
return *this;
59+
}
60+
1661
Value::Value(ClassID SubclassID, llvm::Value *Val, Context &Ctx)
1762
: SubclassID(SubclassID), Val(Val), Ctx(Ctx) {
1863
#ifndef NDEBUG
@@ -71,6 +116,17 @@ void Argument::dump() const {
71116
}
72117
#endif // NDEBUG
73118

119+
Use User::getOperandUseDefault(unsigned OpIdx, bool Verify) const {
120+
assert((!Verify || OpIdx < getNumOperands()) && "Out of bounds!");
121+
assert(isa<llvm::User>(Val) && "Non-users have no operands!");
122+
llvm::Use *LLVMUse;
123+
if (OpIdx != getNumOperands())
124+
LLVMUse = &cast<llvm::User>(Val)->getOperandUse(OpIdx);
125+
else
126+
LLVMUse = cast<llvm::User>(Val)->op_end();
127+
return Use(LLVMUse, const_cast<User *>(this), Ctx);
128+
}
129+
74130
bool User::classof(const Value *From) {
75131
switch (From->getSubclassID()) {
76132
#define DEF_VALUE(ID, CLASS)

llvm/unittests/SandboxIR/SandboxIRTest.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,79 @@ define void @foo(i32 %v1) {
109109
#endif
110110
}
111111

112+
TEST_F(SandboxIRTest, Use) {
113+
parseIR(C, R"IR(
114+
define i32 @foo(i32 %v0, i32 %v1) {
115+
%add0 = add i32 %v0, %v1
116+
ret i32 %add0
117+
}
118+
)IR");
119+
Function &LLVMF = *M->getFunction("foo");
120+
sandboxir::Context Ctx(C);
121+
122+
BasicBlock *LLVMBB = &*LLVMF.begin();
123+
auto LLVMBBIt = LLVMBB->begin();
124+
Instruction *LLVMI0 = &*LLVMBBIt++;
125+
126+
auto &F = *Ctx.createFunction(&LLVMF);
127+
auto &BB = *F.begin();
128+
auto *Arg0 = F.getArg(0);
129+
auto *Arg1 = F.getArg(1);
130+
auto It = BB.begin();
131+
auto *I0 = &*It++;
132+
auto *Ret = &*It++;
133+
134+
SmallVector<sandboxir::Argument *> Args{Arg0, Arg1};
135+
unsigned OpIdx = 0;
136+
for (sandboxir::Use Use : I0->operands()) {
137+
// Check Use.getOperandNo().
138+
EXPECT_EQ(Use.getOperandNo(), OpIdx);
139+
// Check Use.getUser().
140+
EXPECT_EQ(Use.getUser(), I0);
141+
// Check Use.getContext().
142+
EXPECT_EQ(Use.getContext(), &Ctx);
143+
// Check Use.get().
144+
sandboxir::Value *Op = Use.get();
145+
EXPECT_EQ(Op, Ctx.getValue(LLVMI0->getOperand(OpIdx)));
146+
// Check implicit cast to Value.
147+
sandboxir::Value *Cast = Use;
148+
EXPECT_EQ(Cast, Op);
149+
// Check that Use points to the correct operand.
150+
EXPECT_EQ(Op, Args[OpIdx]);
151+
// Check getOperand().
152+
EXPECT_EQ(Op, I0->getOperand(OpIdx));
153+
// Check getOperandUse().
154+
EXPECT_EQ(Use, I0->getOperandUse(OpIdx));
155+
++OpIdx;
156+
}
157+
EXPECT_EQ(OpIdx, 2u);
158+
159+
// Check Use.operator==() and Use.operator!=().
160+
sandboxir::Use UseA = I0->getOperandUse(0);
161+
sandboxir::Use UseB = I0->getOperandUse(0);
162+
EXPECT_TRUE(UseA == UseB);
163+
EXPECT_FALSE(UseA != UseB);
164+
165+
// Check getNumOperands().
166+
EXPECT_EQ(I0->getNumOperands(), 2u);
167+
EXPECT_EQ(Ret->getNumOperands(), 1u);
168+
169+
EXPECT_EQ(Ret->getOperand(0), I0);
170+
171+
#ifndef NDEBUG
172+
// Check Use.dump()
173+
std::string Buff;
174+
raw_string_ostream BS(Buff);
175+
BS << "\n";
176+
I0->getOperandUse(0).dump(BS);
177+
EXPECT_EQ(Buff, R"IR(
178+
Def: i32 %v0 ; SB1. (Argument)
179+
User: %add0 = add i32 %v0, %v1 ; SB4. (Opaque)
180+
OperandNo: 0
181+
)IR");
182+
#endif // NDEBUG
183+
}
184+
112185
TEST_F(SandboxIRTest, Function) {
113186
parseIR(C, R"IR(
114187
define void @foo(i32 %arg0, i32 %arg1) {

0 commit comments

Comments
 (0)