Skip to content

Commit c3125ad

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 c3125ad

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

llvm/include/llvm/SandboxIR/SandboxIR.h

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,71 @@ 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 *User;
84+
Context *Ctx;
85+
86+
/// Don't allow the user to create a sandboxir::Use directly.
87+
Use(llvm::Use *LLVMUse, class User *User, Context &Ctx)
88+
: LLVMUse(LLVMUse), User(User), 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 User; }
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 && User == Other.User;
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_VALUE(ID, CLASS)
118+
#define DEF_USER(ID, CLASS)
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+
};
74139

75140
/// A SandboxIR Value has users. This is the base class.
76141
class Value {
@@ -174,9 +239,61 @@ class User : public Value {
174239
protected:
175240
User(ClassID ID, llvm::Value *V, Context &Ctx) : Value(ID, V, Ctx) {}
176241

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

199319
public:
200320
/// For isa/dyn_cast.
@@ -203,6 +323,9 @@ class Constant : public sandboxir::User {
203323
From->getSubclassID() == ClassID::Function;
204324
}
205325
sandboxir::Context &getParent() const { return getContext(); }
326+
unsigned getUseOperandNo(const Use &Use) const final {
327+
return getUseOperandNoDefault(Use);
328+
}
206329
#ifndef NDEBUG
207330
void verify() const final {
208331
assert(isa<llvm::Constant>(Val) && "Expected Constant!");
@@ -309,11 +432,17 @@ class OpaqueInst : public sandboxir::Instruction {
309432
OpaqueInst(ClassID SubclassID, llvm::Instruction *I, sandboxir::Context &Ctx)
310433
: sandboxir::Instruction(SubclassID, Opcode::Opaque, I, Ctx) {}
311434
friend class Context; // For constructor.
435+
Use getOperandUseInternal(unsigned OpIdx, bool Verify) const final {
436+
return getOperandUseDefault(OpIdx, Verify);
437+
}
312438

313439
public:
314440
static bool classof(const sandboxir::Value *From) {
315441
return From->getSubclassID() == ClassID::Opaque;
316442
}
443+
unsigned getUseOperandNo(const Use &Use) const final {
444+
return getUseOperandNoDefault(Use);
445+
}
317446
unsigned getNumOfIRInstrs() const final { return 1u; }
318447
#ifndef NDEBUG
319448
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 User->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 (User == nullptr)
36+
OS << "NULL";
37+
else
38+
OS << *User;
39+
OS << "\n";
40+
41+
OS << "OperandNo: ";
42+
if (User == 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)