Skip to content

Commit

Permalink
[SandboxIR] More boilerplate: Function, Argument, Constant, Instructi…
Browse files Browse the repository at this point in the history
…on, OpaqueInst (#97343)

A very basic implementation of sandboxir::
`Fuction`
`Argument`
`Constant`
`Instruction`
`OpaqueInst`
  • Loading branch information
vporpo committed Jul 3, 2024
1 parent edbc0e3 commit d5f5dc9
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 4 deletions.
150 changes: 150 additions & 0 deletions llvm/include/llvm/SandboxIR/SandboxIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#ifndef LLVM_TRANSFORMS_SANDBOXIR_SANDBOXIR_H
#define LLVM_TRANSFORMS_SANDBOXIR_SANDBOXIR_H

#include "llvm/IR/Function.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/raw_ostream.h"
Expand Down Expand Up @@ -129,6 +130,35 @@ class Value {
void dumpCommonPrefix(raw_ostream &OS) const;
void dumpCommonSuffix(raw_ostream &OS) const;
void printAsOperandCommon(raw_ostream &OS) const;
friend raw_ostream &operator<<(raw_ostream &OS, const sandboxir::Value &V) {
V.dump(OS);
return OS;
}
virtual void dump(raw_ostream &OS) const = 0;
LLVM_DUMP_METHOD virtual void dump() const = 0;
#endif
};

/// Argument of a sandboxir::Function.
class Argument : public sandboxir::Value {
public:
Argument(llvm::Argument *Arg, sandboxir::Context &Ctx)
: sandboxir::Value(ClassID::Argument, Arg, Ctx) {}
static bool classof(const sandboxir::Value *From) {
return From->getSubclassID() == ClassID::Argument;
}
#ifndef NDEBUG
void verify() const final {
assert(isa<llvm::Argument>(Val) && "Expected Argument!");
}
friend raw_ostream &operator<<(raw_ostream &OS,
const sandboxir::Argument &TArg) {
TArg.dump(OS);
return OS;
}
void printAsOperand(raw_ostream &OS) const;
void dump(raw_ostream &OS) const final;
LLVM_DUMP_METHOD void dump() const final;
#endif
};

Expand All @@ -142,16 +172,136 @@ class User : public Value {
assert(isa<llvm::User>(Val) && "Expected User!");
}
void dumpCommonHeader(raw_ostream &OS) const final;
void dump(raw_ostream &OS) const override {
// TODO: Remove this tmp implementation once we get the Instruction classes.
}
LLVM_DUMP_METHOD void dump() const override {
// TODO: Remove this tmp implementation once we get the Instruction classes.
}
#endif
};

class Constant : public sandboxir::User {
public:
Constant(llvm::Constant *C, sandboxir::Context &SBCtx)
: sandboxir::User(ClassID::Constant, C, SBCtx) {}
/// For isa/dyn_cast.
static bool classof(const sandboxir::Value *From) {
return From->getSubclassID() == ClassID::Constant ||
From->getSubclassID() == ClassID::Function;
}
sandboxir::Context &getParent() const { return getContext(); }
#ifndef NDEBUG
void verify() const final {
assert(isa<llvm::Constant>(Val) && "Expected Constant!");
}
friend raw_ostream &operator<<(raw_ostream &OS,
const sandboxir::Constant &SBC) {
SBC.dump(OS);
return OS;
}
void dump(raw_ostream &OS) const override;
LLVM_DUMP_METHOD void dump() const override;
#endif
};

/// A sandboxir::User with operands and opcode.
class Instruction : public sandboxir::User {
public:
enum class Opcode {
#define DEF_VALUE(ID, CLASS)
#define DEF_USER(ID, CLASS)
#define OP(OPC) OPC,
#define DEF_INSTR(ID, OPC, CLASS) OPC
#include "llvm/SandboxIR/SandboxIRValues.def"
};

Instruction(ClassID ID, Opcode Opc, llvm::Instruction *I,
sandboxir::Context &SBCtx)
: sandboxir::User(ID, I, SBCtx), Opc(Opc) {}

protected:
Opcode Opc;

public:
static const char *getOpcodeName(Opcode Opc);
#ifndef NDEBUG
friend raw_ostream &operator<<(raw_ostream &OS, Opcode Opc) {
OS << getOpcodeName(Opc);
return OS;
}
#endif
/// For isa/dyn_cast.
static bool classof(const sandboxir::Value *From);

#ifndef NDEBUG
friend raw_ostream &operator<<(raw_ostream &OS,
const sandboxir::Instruction &SBI) {
SBI.dump(OS);
return OS;
}
void dump(raw_ostream &OS) const override;
LLVM_DUMP_METHOD void dump() const override;
#endif
};

/// An LLLVM Instruction that has no SandboxIR equivalent class gets mapped to
/// an OpaqueInstr.
class OpaqueInst : public sandboxir::Instruction {
public:
OpaqueInst(llvm::Instruction *I, sandboxir::Context &Ctx)
: sandboxir::Instruction(ClassID::Opaque, Opcode::Opaque, I, Ctx) {}
OpaqueInst(ClassID SubclassID, llvm::Instruction *I, sandboxir::Context &Ctx)
: sandboxir::Instruction(SubclassID, Opcode::Opaque, I, Ctx) {}
static bool classof(const sandboxir::Value *From) {
return From->getSubclassID() == ClassID::Opaque;
}
#ifndef NDEBUG
void verify() const final {
// Nothing to do
}
friend raw_ostream &operator<<(raw_ostream &OS,
const sandboxir::OpaqueInst &OI) {
OI.dump(OS);
return OS;
}
void dump(raw_ostream &OS) const override;
LLVM_DUMP_METHOD void dump() const override;
#endif
};

class Context {
protected:
LLVMContext &LLVMCtx;
/// Maps LLVM Value to the corresponding sandboxir::Value. Owns all
/// SandboxIR objects.
DenseMap<llvm::Value *, std::unique_ptr<sandboxir::Value>>
LLVMValueToValueMap;

public:
Context(LLVMContext &LLVMCtx) : LLVMCtx(LLVMCtx) {}
sandboxir::Value *getValue(llvm::Value *V) const;
};

class Function : public sandboxir::Value {
public:
Function(llvm::Function *F, sandboxir::Context &Ctx)
: sandboxir::Value(ClassID::Function, F, Ctx) {}
/// For isa/dyn_cast.
static bool classof(const sandboxir::Value *From) {
return From->getSubclassID() == ClassID::Function;
}

#ifndef NDEBUG
void verify() const final {
assert(isa<llvm::Function>(Val) && "Expected Function!");
}
void dumpNameAndArgs(raw_ostream &OS) const;
void dump(raw_ostream &OS) const final;
LLVM_DUMP_METHOD void dump() const final;
#endif
};

} // namespace sandboxir
} // namespace llvm

Expand Down
21 changes: 17 additions & 4 deletions llvm/include/llvm/SandboxIR/SandboxIRValues.def
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,23 @@
//===----------------------------------------------------------------------===//

// ClassID, Class
DEF_USER(User, sandboxir::User)
#ifndef DEF_VALUE
#define DEF_VALUE(ID, CLASS)
#endif
DEF_VALUE(Function, Function)
DEF_VALUE(Argument, Argument)

#ifndef DEF_USER
#define DEF_USER(ID, CLASS)
#endif
DEF_USER(User, User)
DEF_USER(Constant, Constant)

#ifndef DEF_INSTR
#define DEF_INSTR(ID, OPCODE, CLASS)
#endif
// ClassID, Opcode(s), Class
DEF_INSTR(Opaque, OP(Opaque), OpaqueInst)

#ifdef DEF_VALUE
#undef DEF_VALUE
Expand All @@ -18,9 +34,6 @@ DEF_USER(User, sandboxir::User)
#ifdef DEF_INSTR
#undef DEF_INSTR
#endif
#ifdef OPCODES
#undef OPCODES
#endif
#ifdef OP
#undef OP
#endif
114 changes: 114 additions & 0 deletions llvm/lib/SandboxIR/SandboxIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,122 @@ void Value::printAsOperandCommon(raw_ostream &OS) const {
OS << "NULL ";
}

void Argument::printAsOperand(raw_ostream &OS) const {
printAsOperandCommon(OS);
}
void Argument::dump(raw_ostream &OS) const {
dumpCommonPrefix(OS);
dumpCommonSuffix(OS);
}
void Argument::dump() const {
dump(dbgs());
dbgs() << "\n";
}
#endif // NDEBUG

bool User::classof(const Value *From) {
switch (From->getSubclassID()) {
#define DEF_VALUE(ID, CLASS)
#define DEF_USER(ID, CLASS) \
case ClassID::ID: \
return true;
#define DEF_INSTR(ID, OPC, CLASS) \
case ClassID::ID: \
return true;
#include "llvm/SandboxIR/SandboxIRValues.def"
default:
return false;
}
}

#ifndef NDEBUG
void User::dumpCommonHeader(raw_ostream &OS) const {
Value::dumpCommonHeader(OS);
// TODO: This is incomplete
}
#endif // NDEBUG

const char *Instruction::getOpcodeName(Opcode Opc) {
switch (Opc) {
#define DEF_VALUE(ID, CLASS)
#define DEF_USER(ID, CLASS)
#define OP(OPC) \
case Opcode::OPC: \
return #OPC;
#define DEF_INSTR(ID, OPC, CLASS) OPC
#include "llvm/SandboxIR/SandboxIRValues.def"
}
}

bool Instruction::classof(const sandboxir::Value *From) {
switch (From->getSubclassID()) {
#define DEF_INSTR(ID, OPC, CLASS) \
case ClassID::ID: \
return true;
#include "llvm/SandboxIR/SandboxIRValues.def"
default:
return false;
}
}

#ifndef NDEBUG
void Instruction::dump(raw_ostream &OS) const {
OS << "Unimplemented! Please override dump().";
}
void Instruction::dump() const {
dump(dbgs());
dbgs() << "\n";
}

void OpaqueInst::dump(raw_ostream &OS) const {
dumpCommonPrefix(OS);
dumpCommonSuffix(OS);
}

void OpaqueInst::dump() const {
dump(dbgs());
dbgs() << "\n";
}

void Constant::dump(raw_ostream &OS) const {
dumpCommonPrefix(OS);
dumpCommonSuffix(OS);
}

void Constant::dump() const {
dump(dbgs());
dbgs() << "\n";
}

void Function::dumpNameAndArgs(raw_ostream &OS) const {
auto *F = cast<llvm::Function>(Val);
OS << *getType() << " @" << F->getName() << "(";
auto NumArgs = F->arg_size();
for (auto [Idx, Arg] : enumerate(F->args())) {
auto *SBArg = cast_or_null<Argument>(Ctx.getValue(&Arg));
if (SBArg == nullptr)
OS << "NULL";
else
SBArg->printAsOperand(OS);
if (Idx + 1 < NumArgs)
OS << ", ";
}
OS << ")";
}
void Function::dump(raw_ostream &OS) const {
dumpNameAndArgs(OS);
OS << " {\n";
OS << "}\n";
}
void Function::dump() const {
dump(dbgs());
dbgs() << "\n";
}
#endif // NDEBUG

Value *Context::getValue(llvm::Value *V) const {
auto It = LLVMValueToValueMap.find(V);
if (It != LLVMValueToValueMap.end())
return It->second.get();
return nullptr;
}
50 changes: 50 additions & 0 deletions llvm/unittests/SandboxIR/SandboxIRTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,53 @@ define void @foo(i32 %v1) {
sandboxir::Context Ctx(C);
[[maybe_unused]] sandboxir::User U(sandboxir::Value::ClassID::User, Ret, Ctx);
}

TEST_F(SandboxIRTest, FunctionArgumentConstantAndOpaqueInstInstantiation) {
parseIR(C, R"IR(
define void @foo(i32 %v1) {
%add = add i32 %v1, 42
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
llvm::BasicBlock *LLVMBB = &*LLVMF->begin();
llvm::Instruction *LLVMAdd = &*LLVMBB->begin();
auto *LLVMC = cast<llvm::Constant>(LLVMAdd->getOperand(1));
auto *LLVMArg0 = LLVMF->getArg(0);

sandboxir::Context Ctx(C);
sandboxir::Function F(LLVMF, Ctx);
sandboxir::Argument Arg0(LLVMArg0, Ctx);
sandboxir::Constant Const0(LLVMC, Ctx);
sandboxir::OpaqueInst OpaqueI(LLVMAdd, Ctx);

EXPECT_TRUE(isa<sandboxir::Function>(F));
EXPECT_FALSE(isa<sandboxir::Function>(Arg0));
EXPECT_FALSE(isa<sandboxir::Function>(Const0));
EXPECT_FALSE(isa<sandboxir::Function>(OpaqueI));

EXPECT_FALSE(isa<sandboxir::Argument>(F));
EXPECT_TRUE(isa<sandboxir::Argument>(Arg0));
EXPECT_FALSE(isa<sandboxir::Argument>(Const0));
EXPECT_FALSE(isa<sandboxir::Argument>(OpaqueI));

EXPECT_TRUE(isa<sandboxir::Constant>(F));
EXPECT_FALSE(isa<sandboxir::Constant>(Arg0));
EXPECT_TRUE(isa<sandboxir::Constant>(Const0));
EXPECT_FALSE(isa<sandboxir::Constant>(OpaqueI));

EXPECT_FALSE(isa<sandboxir::OpaqueInst>(F));
EXPECT_FALSE(isa<sandboxir::OpaqueInst>(Arg0));
EXPECT_FALSE(isa<sandboxir::OpaqueInst>(Const0));
EXPECT_TRUE(isa<sandboxir::OpaqueInst>(OpaqueI));

EXPECT_FALSE(isa<sandboxir::Instruction>(F));
EXPECT_FALSE(isa<sandboxir::Instruction>(Arg0));
EXPECT_FALSE(isa<sandboxir::Instruction>(Const0));
EXPECT_TRUE(isa<sandboxir::Instruction>(OpaqueI));

EXPECT_FALSE(isa<sandboxir::User>(F));
EXPECT_FALSE(isa<sandboxir::User>(Arg0));
EXPECT_TRUE(isa<sandboxir::User>(Const0));
EXPECT_TRUE(isa<sandboxir::User>(OpaqueI));
}

0 comments on commit d5f5dc9

Please sign in to comment.