Skip to content

Commit

Permalink
Simplify operating on sets and maps of dialect ops.
Browse files Browse the repository at this point in the history
This commit introduces a new type, `OpSet`, that simplifies membership tests of
an instruction on a set of dialect operations. With this PR, applying a
callback on a given set of operations also becomes simpler since a visitor
function `addSet` is introduced.

It also introduces a map-like data structure, `OpMap`, which can be used
to simplify handling of association of dialect operations to certain
values.

With this PR, a new check-llvm-dialects-units target is added which
tests the new GTest ADT tests as well.
  • Loading branch information
tsymalla authored and Thomas Symalla committed Oct 11, 2023
1 parent 300e0c2 commit fa89c71
Show file tree
Hide file tree
Showing 27 changed files with 2,324 additions and 128 deletions.
4 changes: 4 additions & 0 deletions docker/dialects.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ RUN source /vulkandriver/env.sh \
# Run the lit test suite.
RUN source /vulkandriver/env.sh \
&& cmake --build . --target check-llvm-dialects -- -v

# Run the unit tests suite.
RUN source /vulkandriver/env.sh \
&& cmake --build . --target check-llvm-dialects-units -v
26 changes: 26 additions & 0 deletions example/ExampleDialect.td
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,32 @@ def WriteVarArgOp : ExampleOp<"write.vararg",
Longer description of how this operation writes pieces of data.
}];
}

def SetReadOp : ExampleOp<"set.read",
[Memory<[(readwrite InaccessibleMem)]>, NoUnwind]> {
let results = (outs value:$data);
let arguments = (ins);

let defaultBuilderHasExplicitResultType = true;

let summary = "read a piece of data";
let description = [{
Longer description of how this operation reads a piece of data.
}];
}

def SetWriteOp : ExampleOp<"set.write",
[Memory<[(write InaccessibleMem)]>, NoUnwind,
WillReturn]> {
let results = (outs);
let arguments = (ins value:$data);

let summary = "write a data element";
let description = [{
Longer description of how this operation writes pieces of data.
}];
}

def CombineOp : ExampleOp<"combine",
[Memory<[]>, NoUnwind, WillReturn]> {
let results = (outs value:$result);
Expand Down
32 changes: 32 additions & 0 deletions example/ExampleMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "ExampleDialect.h"

#include "llvm-dialects/Dialect/Builder.h"
#include "llvm-dialects/Dialect/OpDescription.h"
#include "llvm-dialects/Dialect/OpSet.h"
#include "llvm-dialects/Dialect/Verifier.h"

#include "llvm/AsmParser/Parser.h"
Expand Down Expand Up @@ -121,6 +123,9 @@ void createFunctionExample(Module &module, const Twine &name) {
b.create<xd::WriteVarArgOp>(p2, varArgs);
b.create<xd::HandleGetOp>();

b.create<xd::SetReadOp>(FixedVectorType::get(b.getInt32Ty(), 2));
b.create<xd::SetWriteOp>(y6);

useUnnamedStructTypes(b);

b.CreateRetVoid();
Expand Down Expand Up @@ -166,12 +171,39 @@ LLVM_DIALECTS_VISITOR_PAYLOAD_PROJECT_FIELD(VisitorNest, inner)
// i.e. if C++ had a strong enough compile-time evaluation (constexpr), it
// should be possible to evaluate the initialization entirely at compile-time.
template <bool rpot> const Visitor<VisitorContainer> &getExampleVisitor() {
static const auto complexSet = OpSet::fromOpDescriptions(
{OpDescription::fromCoreOp(Instruction::Ret),
OpDescription::fromIntrinsic(Intrinsic::umin)});

static const auto visitor =
VisitorBuilder<VisitorContainer>()
.nest<VisitorNest>([](VisitorBuilder<VisitorNest> &b) {
b.add<xd::ReadOp>([](VisitorNest &self, xd::ReadOp &op) {
*self.out << "visiting ReadOp: " << op << '\n';
});
b.addSet<xd::SetReadOp, xd::SetWriteOp>(
[](VisitorNest &self, llvm::Instruction &op) {
if (isa<xd::SetReadOp>(op)) {
*self.out << "visiting SetReadOp (set): " << op << '\n';
} else if (isa<xd::SetWriteOp>(op)) {
*self.out << "visiting SetWriteOp (set): " << op << '\n';
}
});
b.addSet(complexSet, [](VisitorNest &self, llvm::Instruction &op) {
assert((op.getOpcode() == Instruction::Ret ||
(isa<IntrinsicInst>)(&op) &&
cast<IntrinsicInst>(&op)->getIntrinsicID() ==
Intrinsic::umin) &&
"Unexpected operation detected while visiting OpSet!");

if (op.getOpcode() == Instruction::Ret) {
*self.out << "visiting Ret (set): " << op << '\n';
} else if (auto *II = dyn_cast<IntrinsicInst>(&op)) {
if (II->getIntrinsicID() == Intrinsic::umin) {
*self.out << "visiting umin (set): " << op << '\n';
}
}
});
b.add<UnaryInstruction>(
[](VisitorNest &self, UnaryInstruction &inst) {
*self.out << "visiting UnaryInstruction: " << inst << '\n';
Expand Down
13 changes: 8 additions & 5 deletions include/llvm-dialects/Dialect/Dialect.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,14 @@ class DialectExtensionRegistration {

namespace detail {

bool isSimpleOperationDecl(const llvm::Function *fn, llvm::StringRef name);
bool isOverloadedOperationDecl(const llvm::Function *fn, llvm::StringRef name);

bool isSimpleOperation(const llvm::CallInst *i, llvm::StringRef name);
bool isOverloadedOperation(const llvm::CallInst *i, llvm::StringRef name);
bool isSimpleOperationDecl(const llvm::Function *fn, llvm::StringRef mnemonic);
bool isOverloadedOperationDecl(const llvm::Function *fn,
llvm::StringRef mnemonic);

bool isSimpleOperation(const llvm::CallInst *i, llvm::StringRef mnemonic);
bool isOverloadedOperation(const llvm::CallInst *i, llvm::StringRef mnemonic);
bool isOperationDecl(llvm::StringRef fn, bool isOverloaded,
llvm::StringRef mnemonic);

} // namespace detail

Expand Down
31 changes: 28 additions & 3 deletions include/llvm-dialects/Dialect/OpDescription.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "llvm/ADT/StringRef.h"

#include <variant>
#include <vector>

namespace llvm {
class Function;
Expand All @@ -40,19 +39,45 @@ class OpDescription {
};

public:
OpDescription() = default;
OpDescription(bool hasOverloads, llvm::StringRef mnemonic)
: m_kind(hasOverloads ? Kind::DialectWithOverloads : Kind::Dialect),
m_op(mnemonic) {}
OpDescription(Kind kind, unsigned opcode) : m_kind(kind), m_op(opcode) {}
OpDescription(Kind kind, llvm::MutableArrayRef<unsigned> opcodes);

template <typename OpT>
static const OpDescription& get();
static OpDescription fromCoreOp(unsigned op) { return {Kind::Core, op}; }

static OpDescription fromIntrinsic(unsigned op) {
return {Kind::Intrinsic, op};
}

static OpDescription fromDialectOp(bool hasOverloads,
llvm::StringRef mnemonic) {
return {hasOverloads, mnemonic};
}

bool isCoreOp() const { return m_kind == Kind::Core; }
bool isIntrinsic() const { return m_kind == Kind::Intrinsic; }
bool isDialectOp() const {
return m_kind == Kind::Dialect || m_kind == Kind::DialectWithOverloads;
}

template <typename OpT> static const OpDescription &get();

Kind getKind() const { return m_kind; }

unsigned getOpcode() const;

llvm::ArrayRef<unsigned> getOpcodes() const;

llvm::StringRef getMnemonic() const {
assert(m_kind == Kind::Dialect || m_kind == Kind::DialectWithOverloads);
return std::get<llvm::StringRef>(m_op);
}

bool matchInstruction(const llvm::Instruction &inst) const;

bool matchDeclaration(const llvm::Function &decl) const;

bool canMatchDeclaration() const {
Expand Down
Loading

0 comments on commit fa89c71

Please sign in to comment.