diff --git a/example/ExampleDialect.td b/example/ExampleDialect.td index 1b2a2bf..4c2e507 100644 --- a/example/ExampleDialect.td +++ b/example/ExampleDialect.td @@ -320,3 +320,19 @@ def StringAttrOp : Op { The argument should not have a setter method }]; } + +def NoSummaryOp : Op { + let results = (outs); + let arguments = (ins); + + let description = [{ + Some description + }]; +} + +def NoDescriptionOp : Op { + let results = (outs); + let arguments = (ins); + + let summary = "Some summary"; +} diff --git a/include/llvm-dialects/TableGen/Common.h b/include/llvm-dialects/TableGen/Common.h index f08075b..a63907e 100644 --- a/include/llvm-dialects/TableGen/Common.h +++ b/include/llvm-dialects/TableGen/Common.h @@ -20,6 +20,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Record.h" +#include #if !defined(LLVM_MAIN_REVISION) || LLVM_MAIN_REVISION >= 513628 using RecordKeeperTy = const llvm::RecordKeeper; @@ -35,4 +36,8 @@ void emitHeader(llvm::raw_ostream& out); bool shouldEmitComments(); +/// Prefix an incoming multi-line string with a single-line comment string line +/// by line. +std::string createCommentFromString(const std::string &input); + } // namespace llvm_dialects diff --git a/include/llvm-dialects/TableGen/Operations.h b/include/llvm-dialects/TableGen/Operations.h index bf82f51..8bddc4d 100644 --- a/include/llvm-dialects/TableGen/Operations.h +++ b/include/llvm-dialects/TableGen/Operations.h @@ -103,6 +103,8 @@ class Operation : public OperationBase { public: std::string name; std::string mnemonic; + std::string summary; + std::string description; std::vector traits; std::vector results; diff --git a/lib/TableGen/Common.cpp b/lib/TableGen/Common.cpp index 78e7508..7ad5108 100644 --- a/lib/TableGen/Common.cpp +++ b/lib/TableGen/Common.cpp @@ -17,6 +17,8 @@ */ #include "llvm-dialects/TableGen/Common.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" @@ -33,3 +35,18 @@ void llvm_dialects::emitHeader(raw_ostream& out) { } bool llvm_dialects::shouldEmitComments() { return g_emitComments; } + +std::string llvm_dialects::createCommentFromString(const std::string &input) { + StringRef inRef{input}; + if (inRef.trim().empty()) + return input; + + SmallVector lines; + inRef.split(lines, '\n'); + + std::string outStr; + for (auto line : lines) + outStr += "/// " + line.str() + '\n'; + + return outStr; +} diff --git a/lib/TableGen/GenDialect.cpp b/lib/TableGen/GenDialect.cpp index 34bea86..2d6a465 100644 --- a/lib/TableGen/GenDialect.cpp +++ b/lib/TableGen/GenDialect.cpp @@ -172,7 +172,16 @@ class Builder; fmt.withOp(op.name); fmt.addSubst("mnemonic", op.mnemonic); + std::string description = "/// " + op.name + '\n'; + + if (!op.summary.empty()) + description += createCommentFromString(op.summary); + + if (!op.description.empty()) + description += createCommentFromString(op.description); + out << tgfmt(R"( + $2 class $_op : public $0 { static const ::llvm::StringLiteral s_name; //{"$dialect.$mnemonic"}; @@ -188,7 +197,8 @@ class Builder; &fmt, op.superclass() ? op.superclass()->name : "::llvm::CallInst", !op.haveResultOverloads() ? "isSimpleOperation" - : "isOverloadedOperation"); + : "isOverloadedOperation", + description); for (const auto &builder : op.builders()) builder.emitDeclaration(out, fmt); diff --git a/lib/TableGen/Operations.cpp b/lib/TableGen/Operations.cpp index b0c5eb6..54e1643 100644 --- a/lib/TableGen/Operations.cpp +++ b/lib/TableGen/Operations.cpp @@ -360,6 +360,11 @@ bool Operation::parse(raw_ostream &errs, GenDialectsContext *context, op->name = record->getName(); op->mnemonic = record->getValueAsString("mnemonic"); + if (!record->isValueUnset("summary")) + op->summary = record->getValueAsString("summary"); + + if (!record->isValueUnset("description")) + op->description = record->getValueAsString("description"); for (RecordTy *traitRec : record->getValueAsListOfDefs("traits")) op->traits.push_back(context->getTrait(traitRec)); diff --git a/test/example/generated/ExampleDialect.cpp.inc b/test/example/generated/ExampleDialect.cpp.inc index 3c2d603..06f313a 100644 --- a/test/example/generated/ExampleDialect.cpp.inc +++ b/test/example/generated/ExampleDialect.cpp.inc @@ -112,6 +112,16 @@ namespace xd::cpp { state.setError(); }); + builder.add([](::llvm_dialects::VerifierState &state, NoDescriptionOp &op) { + if (!op.verifier(state.out())) + state.setError(); + }); + + builder.add([](::llvm_dialects::VerifierState &state, NoSummaryOp &op) { + if (!op.verifier(state.out())) + state.setError(); + }); + builder.add([](::llvm_dialects::VerifierState &state, ReadOp &op) { if (!op.verifier(state.out())) state.setError(); @@ -1519,6 +1529,108 @@ instName + const ::llvm::StringLiteral NoDescriptionOp::s_name{"xd.ir.no.description.op"}; + + NoDescriptionOp* NoDescriptionOp::create(llvm_dialects::Builder& b, const llvm::Twine &instName) { + ::llvm::LLVMContext& context = b.getContext(); + (void)context; + ::llvm::Module& module = *b.GetInsertBlock()->getModule(); + + + const ::llvm::AttributeList attrs + = ExampleDialect::get(context).getAttributeList(4); + auto fnType = ::llvm::FunctionType::get(::llvm::Type::getVoidTy(context), { +}, false); + + auto fn = module.getOrInsertFunction(s_name, fnType, attrs); + ::llvm::SmallString<32> newName; + for (unsigned i = 0; !::llvm::isa<::llvm::Function>(fn.getCallee()) || + ::llvm::cast<::llvm::Function>(fn.getCallee())->getFunctionType() != fn.getFunctionType(); i++) { + // If a function with the same name but a different types already exists, + // we get a bitcast of a function or a function with the wrong type. + // Try new names until we get one with the correct type. + newName = ""; + ::llvm::raw_svector_ostream newNameStream(newName); + newNameStream << s_name << "_" << i; + fn = module.getOrInsertFunction(newNameStream.str(), fnType, attrs); + } + assert(::llvm::isa<::llvm::Function>(fn.getCallee())); + assert(fn.getFunctionType() == fnType); + assert(::llvm::cast<::llvm::Function>(fn.getCallee())->getFunctionType() == fn.getFunctionType()); + + return ::llvm::cast(b.CreateCall(fn, std::nullopt, instName)); +} + + + bool NoDescriptionOp::verifier(::llvm::raw_ostream &errs) { + ::llvm::LLVMContext &context = getModule()->getContext(); + (void)context; + + using ::llvm_dialects::printable; + + if (arg_size() != 0) { + errs << " wrong number of arguments: " << arg_size() + << ", expected 0\n"; + return false; + } + return true; +} + + + + + + const ::llvm::StringLiteral NoSummaryOp::s_name{"xd.ir.no.summary.op"}; + + NoSummaryOp* NoSummaryOp::create(llvm_dialects::Builder& b, const llvm::Twine &instName) { + ::llvm::LLVMContext& context = b.getContext(); + (void)context; + ::llvm::Module& module = *b.GetInsertBlock()->getModule(); + + + const ::llvm::AttributeList attrs + = ExampleDialect::get(context).getAttributeList(4); + auto fnType = ::llvm::FunctionType::get(::llvm::Type::getVoidTy(context), { +}, false); + + auto fn = module.getOrInsertFunction(s_name, fnType, attrs); + ::llvm::SmallString<32> newName; + for (unsigned i = 0; !::llvm::isa<::llvm::Function>(fn.getCallee()) || + ::llvm::cast<::llvm::Function>(fn.getCallee())->getFunctionType() != fn.getFunctionType(); i++) { + // If a function with the same name but a different types already exists, + // we get a bitcast of a function or a function with the wrong type. + // Try new names until we get one with the correct type. + newName = ""; + ::llvm::raw_svector_ostream newNameStream(newName); + newNameStream << s_name << "_" << i; + fn = module.getOrInsertFunction(newNameStream.str(), fnType, attrs); + } + assert(::llvm::isa<::llvm::Function>(fn.getCallee())); + assert(fn.getFunctionType() == fnType); + assert(::llvm::cast<::llvm::Function>(fn.getCallee())->getFunctionType() == fn.getFunctionType()); + + return ::llvm::cast(b.CreateCall(fn, std::nullopt, instName)); +} + + + bool NoSummaryOp::verifier(::llvm::raw_ostream &errs) { + ::llvm::LLVMContext &context = getModule()->getContext(); + (void)context; + + using ::llvm_dialects::printable; + + if (arg_size() != 0) { + errs << " wrong number of arguments: " << arg_size() + << ", expected 0\n"; + return false; + } + return true; +} + + + + + const ::llvm::StringLiteral ReadOp::s_name{"xd.ir.read"}; ReadOp* ReadOp::create(llvm_dialects::Builder& b, ::llvm::Type* dataType, const llvm::Twine &instName) { @@ -2357,6 +2469,22 @@ data } + template <> + const ::llvm_dialects::OpDescription & + ::llvm_dialects::OpDescription::get() { + static const ::llvm_dialects::OpDescription desc{false, "xd.ir.no.description.op"}; + return desc; + } + + + template <> + const ::llvm_dialects::OpDescription & + ::llvm_dialects::OpDescription::get() { + static const ::llvm_dialects::OpDescription desc{false, "xd.ir.no.summary.op"}; + return desc; + } + + template <> const ::llvm_dialects::OpDescription & ::llvm_dialects::OpDescription::get() { diff --git a/test/example/generated/ExampleDialect.h.inc b/test/example/generated/ExampleDialect.h.inc index 5b317a6..0553f53 100644 --- a/test/example/generated/ExampleDialect.h.inc +++ b/test/example/generated/ExampleDialect.h.inc @@ -111,6 +111,13 @@ Initial = 2, };}; }; + /// Add32Op +/// add two numbers, and a little extra +/// +/// For those times when you want a little extra, this operation adds two +/// numbers and puts a constant on top. +/// + class Add32Op : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.add32"}; @@ -142,6 +149,12 @@ Extra = 2, }; + /// CombineOp +/// combine two pieces of data +/// +/// Longer description of... well, you know by now how this goes. +/// + class CombineOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.combine"}; @@ -170,6 +183,12 @@ Rhs = 1, }; + /// ExtractElementOp +/// extract an element from a vector +/// +/// Return the element of `vector` with the given `index`. +/// + class ExtractElementOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.extractelement"}; @@ -198,6 +217,12 @@ Index = 1, }; + /// FromFixedVectorOp +/// convert to our custom vector type +/// +/// Demonstrate a more complex unification case. +/// + class FromFixedVectorOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.fromfixedvector"}; @@ -223,6 +248,12 @@ Source = 0, }; + /// HandleGetOp +/// obtain a handle +/// +/// Use a dialect type without type arguments. +/// + class HandleGetOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.handle.get"}; @@ -244,6 +275,13 @@ bool verifier(::llvm::raw_ostream &errs); }; + /// IExtOp +/// custom ext operation that only works on integers +/// +/// Demonstrates the use of the same unevaluatable `le` predicate in a valued +/// position. +/// + class IExtOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.iext"}; @@ -269,6 +307,12 @@ Source = 0, }; + /// ITruncOp +/// custom trunc operation that only works on integers +/// +/// Demonstrates the use of a predicate in an unvalued position. +/// + class ITruncOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.itrunc"}; @@ -294,6 +338,12 @@ Source = 0, }; + /// ImmutableOp +/// demonstrate how an argument will not get a setter method +/// +/// Make an argument immutable +/// + class ImmutableOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.immutable.op"}; @@ -316,6 +366,13 @@ Val = 0, }; + /// InsertElementOp +/// insert an element into a vector +/// +/// Insert the given `value` into the given `vector` at the given `index` and +/// returns the result. +/// + class InsertElementOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.insertelement"}; @@ -347,6 +404,13 @@ Index = 2, }; + /// InstNameConflictDoubleOp +/// demonstrate how name conflict will be avoided +/// +/// Like InstNameConflictOp but this has a second parameter named like the +/// dialect compiler's first choice +/// + class InstNameConflictDoubleOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.inst.name.conflict.double"}; @@ -375,6 +439,14 @@ InstName_0 = 1, }; + /// InstNameConflictOp +/// demonstrate how name conflict will be avoided +/// +/// The builder accepts an additional argument to set the name of the created +/// value like IRBuilder methods. This op produces a conflict so the parameter +/// will be renamed. +/// + class InstNameConflictOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.inst.name.conflict"}; @@ -400,6 +472,12 @@ InstName = 0, }; + /// InstNameConflictVarargsOp +/// demonstrate how name conflict will be avoided +/// +/// Like InstNameConflictOp but with varargs +/// + class InstNameConflictVarargsOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.inst.name.conflict.varargs"}; @@ -427,6 +505,60 @@ InstName_0Start = 0, }; + /// NoDescriptionOp +/// Some summary + + class NoDescriptionOp : public ::llvm::CallInst { + static const ::llvm::StringLiteral s_name; //{"xd.ir.no.description.op"}; + + public: + static bool classof(const ::llvm::CallInst* i) { + return ::llvm_dialects::detail::isSimpleOperation(i, s_name); + } + static bool classof(const ::llvm::Value* v) { + return ::llvm::isa<::llvm::CallInst>(v) && + classof(::llvm::cast<::llvm::CallInst>(v)); + } + static NoDescriptionOp* create(::llvm_dialects::Builder& b, const llvm::Twine &instName = ""); + +bool verifier(::llvm::raw_ostream &errs); + + + + + }; + + /// NoSummaryOp +/// +/// Some description +/// + + class NoSummaryOp : public ::llvm::CallInst { + static const ::llvm::StringLiteral s_name; //{"xd.ir.no.summary.op"}; + + public: + static bool classof(const ::llvm::CallInst* i) { + return ::llvm_dialects::detail::isSimpleOperation(i, s_name); + } + static bool classof(const ::llvm::Value* v) { + return ::llvm::isa<::llvm::CallInst>(v) && + classof(::llvm::cast<::llvm::CallInst>(v)); + } + static NoSummaryOp* create(::llvm_dialects::Builder& b, const llvm::Twine &instName = ""); + +bool verifier(::llvm::raw_ostream &errs); + + + + + }; + + /// ReadOp +/// read a piece of data +/// +/// Longer description of how this operation reads a piece of data. +/// + class ReadOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.read"}; @@ -448,6 +580,12 @@ bool verifier(::llvm::raw_ostream &errs); }; + /// SetReadOp +/// read a piece of data +/// +/// Longer description of how this operation reads a piece of data. +/// + class SetReadOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.set.read"}; @@ -469,6 +607,12 @@ bool verifier(::llvm::raw_ostream &errs); }; + /// SetWriteOp +/// write a data element +/// +/// Longer description of how this operation writes pieces of data. +/// + class SetWriteOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.set.write"}; @@ -493,6 +637,12 @@ Data = 0, }; + /// SizeOfOp +/// size of a given type +/// +/// Returns the store size of the given type in bytes. +/// + class SizeOfOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.sizeof"}; @@ -518,6 +668,12 @@ SizeofType = 0, }; + /// StreamAddOp +/// perform the add operation streaming from memory +/// +/// Illustrate the use of the OpClass feature. +/// + class StreamAddOp : public StreamReduceOp { static const ::llvm::StringLiteral s_name; //{"xd.ir.stream.add"}; @@ -539,6 +695,12 @@ bool verifier(::llvm::raw_ostream &errs); }; + /// StreamMaxOp +/// perform the max operation streaming from memory +/// +/// Illustrate the use of the OpClass feature. +/// + class StreamMaxOp : public StreamReduceOp { static const ::llvm::StringLiteral s_name; //{"xd.ir.stream.max"}; @@ -560,6 +722,12 @@ bool verifier(::llvm::raw_ostream &errs); }; + /// StreamMinOp +/// perform the min operation streaming from memory +/// +/// Illustrate the use of the OpClass feature. +/// + class StreamMinOp : public StreamReduceOp { static const ::llvm::StringLiteral s_name; //{"xd.ir.stream.min"}; @@ -581,6 +749,12 @@ bool verifier(::llvm::raw_ostream &errs); }; + /// StringAttrOp +/// demonstrate an argument that takes in a StringRef +/// +/// The argument should not have a setter method +/// + class StringAttrOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.string.attr.op"}; @@ -603,6 +777,12 @@ Val = 0, }; + /// WriteOp +/// write a piece of data +/// +/// Longer description of how this operation writes a piece of data. +/// + class WriteOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.write"}; @@ -627,6 +807,12 @@ Data = 0, }; + /// WriteVarArgOp +/// write one or more data elements +/// +/// Longer description of how this operation writes pieces of data. +/// + class WriteVarArgOp : public ::llvm::CallInst { static const ::llvm::StringLiteral s_name; //{"xd.ir.write.vararg"};