#include "OpClass.h"
#include "OpFormatGen.h"
#include "OpGenHelpers.h"
#include "mlir/TableGen/Argument.h"
#include "mlir/TableGen/Attribute.h"
#include "mlir/TableGen/Class.h"
#include "mlir/TableGen/CodeGenHelpers.h"
#include "mlir/TableGen/Format.h"
#include "mlir/TableGen/GenInfo.h"
#include "mlir/TableGen/Interfaces.h"
#include "mlir/TableGen/Operator.h"
#include "mlir/TableGen/Property.h"
#include "mlir/TableGen/SideEffects.h"
#include "mlir/TableGen/Trait.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#define DEBUG_TYPE "mlir-tblgen-opdefgen"
using namespace llvm;
using namespace mlir;
using namespace mlir::tblgen;
static const char *const tblgenNamePrefix = "tblgen_";
static const char *const generatedArgName = "odsArg";
static const char *const odsBuilder = "odsBuilder";
static const char *const builderOpState = "odsState";
static const char *const propertyStorage = "propStorage";
static const char *const propertyValue = "propValue";
static const char *const propertyAttr = "propAttr";
static const char *const propertyDiag = "emitError";
static const char *const operandSegmentAttrName = "operandSegmentSizes";
static const char *const resultSegmentAttrName = "resultSegmentSizes";
static const char *const subrangeGetAttr =
"::mlir::impl::get{4}AttrFromSortedRange({3}.begin() + {1}, {3}.end() - "
"{2}, {0})";
static const char *const sameVariadicSizeValueRangeCalcCode = R"(
bool isVariadic[] = {{{0}};
int prevVariadicCount = 0;
for (unsigned i = 0; i < index; ++i)
if (isVariadic[i]) ++prevVariadicCount;
// Calculate how many dynamic values a static variadic {4} corresponds to.
// This assumes all static variadic {4}s have the same dynamic value count.
int variadicSize = ({3} - {1}) / {2};
// `index` passed in as the parameter is the static index which counts each
// {4} (variadic or not) as size 1. So here for each previous static variadic
// {4}, we need to offset by (variadicSize - 1) to get where the dynamic
// value pack for this static {4} starts.
int start = index + (variadicSize - 1) * prevVariadicCount;
int size = isVariadic[index] ? variadicSize : 1;
return {{start, size};
)";
static const char *const attrSizedSegmentValueRangeCalcCode = R"(
unsigned start = 0;
for (unsigned i = 0; i < index; ++i)
start += sizeAttr[i];
return {start, sizeAttr[index]};
)";
static const char *const adapterSegmentSizeAttrInitCode = R"(
assert({0} && "missing segment size attribute for op");
auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>({0});
)";
static const char *const adapterSegmentSizeAttrInitCodeProperties = R"(
::llvm::ArrayRef<int32_t> sizeAttr = {0};
)";
static const char *const opSegmentSizeAttrInitCode = R"(
auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>({0});
)";
static const char *const variadicOfVariadicAdaptorCalcCode = R"(
auto tblgenTmpOperands = getODSOperands({1});
auto sizes = {0}();
::llvm::SmallVector<{2}> tblgenTmpOperandGroups;
for (int i = 0, e = sizes.size(); i < e; ++i) {{
tblgenTmpOperandGroups.push_back(tblgenTmpOperands.take_front(sizes[i]));
tblgenTmpOperands = tblgenTmpOperands.drop_front(sizes[i]);
}
return tblgenTmpOperandGroups;
)";
static const char *const valueRangeReturnCode = R"(
auto valueRange = {1};
return {{std::next({0}, valueRange.first),
std::next({0}, valueRange.first + valueRange.second)};
)";
static const char *const readBytecodeSegmentSizeNative = R"(
if ($_reader.getBytecodeVersion() >= /*kNativePropertiesODSSegmentSize=*/6)
return $_reader.readSparseArray(::llvm::MutableArrayRef($_storage));
)";
static const char *const readBytecodeSegmentSizeLegacy = R"(
if ($_reader.getBytecodeVersion() < /*kNativePropertiesODSSegmentSize=*/6) {
auto &$_storage = prop.$_propName;
::mlir::DenseI32ArrayAttr attr;
if (::mlir::failed($_reader.readAttribute(attr))) return ::mlir::failure();
if (attr.size() > static_cast<int64_t>(sizeof($_storage) / sizeof(int32_t))) {
$_reader.emitError("size mismatch for operand/result_segment_size");
return ::mlir::failure();
}
::llvm::copy(::llvm::ArrayRef<int32_t>(attr), $_storage.begin());
}
)";
static const char *const writeBytecodeSegmentSizeNative = R"(
if ($_writer.getBytecodeVersion() >= /*kNativePropertiesODSSegmentSize=*/6)
$_writer.writeSparseArray(::llvm::ArrayRef($_storage));
)";
static const char *const writeBytecodeSegmentSizeLegacy = R"(
if ($_writer.getBytecodeVersion() < /*kNativePropertiesODSSegmentSize=*/6) {
auto &$_storage = prop.$_propName;
$_writer.writeAttribute(::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage));
}
)";
static const char *const opCommentHeader = R"(
//===----------------------------------------------------------------------===//
// {0} {1}
//===----------------------------------------------------------------------===//
)";
static std::string replaceAllSubstrs(std::string str, const std::string &match,
const std::string &substitute) {
std::string::size_type scanLoc = 0, matchLoc = std::string::npos;
while ((matchLoc = str.find(match, scanLoc)) != std::string::npos) {
str = str.replace(matchLoc, match.size(), substitute);
scanLoc = matchLoc + substitute.size();
}
return str;
}
static inline bool hasStringAttribute(const Record &record,
StringRef fieldName) {
auto *valueInit = record.getValueInit(fieldName);
return isa<StringInit>(valueInit);
}
static std::string getArgumentName(const Operator &op, int index) {
const auto &operand = op.getOperand(index);
if (!operand.name.empty())
return std::string(operand.name);
return std::string(formatv("{0}_{1}", generatedArgName, index));
}
static bool canUseUnwrappedRawValue(const tblgen::Attribute &attr) {
return attr.getReturnType() != attr.getStorageType() &&
!attr.getConstBuilderTemplate().empty();
}
static std::string constBuildAttrFromParam(const tblgen::Attribute &attr,
FmtContext &fctx,
StringRef paramName) {
std::string builderTemplate = attr.getConstBuilderTemplate().str();
if (StringRef(builderTemplate).contains("\"$0\""))
builderTemplate = replaceAllSubstrs(builderTemplate, "\"$0\"", "$0");
return tgfmt(builderTemplate, &fctx, paramName).str();
}
namespace {
struct AttributeMetadata {
StringRef attrName;
bool isRequired;
std::optional<Attribute> constraint;
unsigned lowerBound = 0;
unsigned upperBound = 0;
};
class OpOrAdaptorHelper {
public:
OpOrAdaptorHelper(const Operator &op, bool emitForOp)
: op(op), emitForOp(emitForOp) {
computeAttrMetadata();
}
class Formatter {
public:
template <typename Functor>
Formatter(Functor &&func) : func(std::forward<Functor>(func)) {}
std::string str() const {
std::string result;
llvm::raw_string_ostream os(result);
os << *this;
return os.str();
}
private:
std::function<raw_ostream &(raw_ostream &)> func;
friend raw_ostream &operator<<(raw_ostream &os, const Formatter &fmt) {
return fmt.func(os);
}
};
Formatter getAttr(StringRef attrName, bool isNamed = false) const {
assert(attrMetadata.count(attrName) && "expected attribute metadata");
return [this, attrName, isNamed](raw_ostream &os) -> raw_ostream & {
const AttributeMetadata &attr = attrMetadata.find(attrName)->second;
if (hasProperties()) {
assert(!isNamed);
return os << "getProperties()." << attrName;
}
return os << formatv(subrangeGetAttr, getAttrName(attrName),
attr.lowerBound, attr.upperBound, getAttrRange(),
isNamed ? "Named" : "");
};
}
Formatter getAttrName(StringRef attrName) const {
return [this, attrName](raw_ostream &os) -> raw_ostream & {
if (emitForOp)
return os << op.getGetterName(attrName) << "AttrName()";
return os << formatv("{0}::{1}AttrName(*odsOpName)", op.getCppClassName(),
op.getGetterName(attrName));
};
}
StringRef getAttrRange() const {
return emitForOp ? "(*this)->getAttrs()" : "odsAttrs";
}
Formatter emitErrorPrefix() const {
return [this](raw_ostream &os) -> raw_ostream & {
if (emitForOp)
return os << "emitOpError(";
return os << formatv("emitError(loc, \"'{0}' op \"",
op.getOperationName());
};
}
Formatter getOperand(unsigned index) const {
return [this, index](raw_ostream &os) -> raw_ostream & {
return os << formatv(op.getOperand(index).isVariadic()
? "this->getODSOperands({0})"
: "(*this->getODSOperands({0}).begin())",
index);
};
}
Formatter getResult(unsigned index) const {
return [this, index](raw_ostream &os) -> raw_ostream & {
if (!emitForOp)
return os << "<no results should be generated>";
return os << formatv(op.getResult(index).isVariadic()
? "this->getODSResults({0})"
: "(*this->getODSResults({0}).begin())",
index);
};
}
bool isEmittingForOp() const { return emitForOp; }
const Operator &getOp() const { return op; }
const llvm::MapVector<StringRef, AttributeMetadata> &getAttrMetadata() const {
return attrMetadata;
}
bool hasProperties() const {
if (!op.getProperties().empty())
return true;
if (!op.getDialect().usePropertiesForAttributes())
return false;
if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments") ||
op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"))
return true;
return llvm::any_of(getAttrMetadata(),
[](const std::pair<StringRef, AttributeMetadata> &it) {
return !it.second.constraint ||
!it.second.constraint->isDerivedAttr();
});
}
std::optional<NamedProperty> &getOperandSegmentsSize() {
return operandSegmentsSize;
}
std::optional<NamedProperty> &getResultSegmentsSize() {
return resultSegmentsSize;
}
uint32_t getOperandSegmentSizesLegacyIndex() {
return operandSegmentSizesLegacyIndex;
}
uint32_t getResultSegmentSizesLegacyIndex() {
return resultSegmentSizesLegacyIndex;
}
private:
void computeAttrMetadata();
const Operator &op;
const bool emitForOp;
llvm::MapVector<StringRef, AttributeMetadata> attrMetadata;
std::optional<NamedProperty> operandSegmentsSize;
std::string operandSegmentsSizeStorage;
std::optional<NamedProperty> resultSegmentsSize;
std::string resultSegmentsSizeStorage;
uint32_t operandSegmentSizesLegacyIndex = 0;
uint32_t resultSegmentSizesLegacyIndex = 0;
unsigned numRequired;
};
}
void OpOrAdaptorHelper::computeAttrMetadata() {
for (const NamedAttribute &namedAttr : op.getAttributes()) {
Attribute attr = namedAttr.attr;
bool isOptional =
attr.hasDefaultValue() || attr.isOptional() || attr.isDerivedAttr();
attrMetadata.insert(
{namedAttr.name, AttributeMetadata{namedAttr.name, !isOptional, attr}});
}
auto makeProperty = [&](StringRef storageType) {
return Property(
storageType,
"::llvm::ArrayRef<int32_t>",
"$_storage",
"::llvm::copy($_value, $_storage.begin())",
"::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage)",
"return convertFromAttribute($_storage, $_attr, $_diag);",
readBytecodeSegmentSizeNative,
writeBytecodeSegmentSizeNative,
"::llvm::hash_combine_range(std::begin($_storage), "
"std::end($_storage));",
"");
};
if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
if (op.getDialect().usePropertiesForAttributes()) {
operandSegmentsSizeStorage =
llvm::formatv("std::array<int32_t, {0}>", op.getNumOperands());
operandSegmentsSize = {"operandSegmentSizes",
makeProperty(operandSegmentsSizeStorage)};
} else {
attrMetadata.insert(
{operandSegmentAttrName, AttributeMetadata{operandSegmentAttrName,
true,
std::nullopt}});
}
}
if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {
if (op.getDialect().usePropertiesForAttributes()) {
resultSegmentsSizeStorage =
llvm::formatv("std::array<int32_t, {0}>", op.getNumResults());
resultSegmentsSize = {"resultSegmentSizes",
makeProperty(resultSegmentsSizeStorage)};
} else {
attrMetadata.insert(
{resultSegmentAttrName,
AttributeMetadata{resultSegmentAttrName, true,
std::nullopt}});
}
}
SmallVector<AttributeMetadata> sortedAttrMetadata =
llvm::to_vector(llvm::make_second_range(attrMetadata.takeVector()));
llvm::sort(sortedAttrMetadata,
[](const AttributeMetadata &lhs, const AttributeMetadata &rhs) {
return lhs.attrName < rhs.attrName;
});
StringRef legacyOperandSegmentSizeName =
StringLiteral("operand_segment_sizes");
StringRef legacyResultSegmentSizeName = StringLiteral("result_segment_sizes");
operandSegmentSizesLegacyIndex = 0;
resultSegmentSizesLegacyIndex = 0;
for (auto item : sortedAttrMetadata) {
if (item.attrName < legacyOperandSegmentSizeName)
++operandSegmentSizesLegacyIndex;
if (item.attrName < legacyResultSegmentSizeName)
++resultSegmentSizesLegacyIndex;
}
numRequired = 0;
for (AttributeMetadata &attr : sortedAttrMetadata) {
attr.lowerBound = numRequired;
numRequired += attr.isRequired;
};
for (AttributeMetadata &attr : sortedAttrMetadata)
attr.upperBound = numRequired - attr.lowerBound - attr.isRequired;
for (const AttributeMetadata &attr : sortedAttrMetadata)
attrMetadata.insert({attr.attrName, attr});
}
namespace {
class OpEmitter {
using ConstArgument =
llvm::PointerUnion<const AttributeMetadata *, const NamedProperty *>;
public:
static void
emitDecl(const Operator &op, raw_ostream &os,
const StaticVerifierFunctionEmitter &staticVerifierEmitter);
static void
emitDef(const Operator &op, raw_ostream &os,
const StaticVerifierFunctionEmitter &staticVerifierEmitter);
private:
OpEmitter(const Operator &op,
const StaticVerifierFunctionEmitter &staticVerifierEmitter);
void emitDecl(raw_ostream &os);
void emitDef(raw_ostream &os);
void genAttrNameGetters();
void genOpAsmInterface();
void genOpNameGetter();
void genPropertiesSupport();
void
genPropertiesSupportForBytecode(ArrayRef<ConstArgument> attrOrProperties);
void genAttrGetters();
void genAttrSetters();
void genOptionalAttrRemovers();
void genNamedOperandGetters();
void genNamedOperandSetters();
void genNamedResultGetters();
void genNamedRegionGetters();
void genNamedSuccessorGetters();
void genPopulateDefaultAttributes();
void genBuilder();
void genSeparateArgParamBuilder();
void genUseOperandAsResultTypeSeparateParamBuilder();
void genUseOperandAsResultTypeCollectiveParamBuilder();
void genInferredTypeCollectiveParamBuilder();
void genUseAttrAsResultTypeBuilder();
void genCollectiveParamBuilder();
enum class TypeParamKind {
None,
Separate,
Collective,
};
enum class AttrParamKind {
WrappedAttr,
UnwrappedValue,
};
void buildParamList(SmallVectorImpl<MethodParameter> ¶mList,
llvm::StringSet<> &inferredAttributes,
SmallVectorImpl<std::string> &resultTypeNames,
TypeParamKind typeParamKind,
AttrParamKind attrParamKind = AttrParamKind::WrappedAttr);
void
genCodeForAddingArgAndRegionForBuilder(MethodBody &body,
llvm::StringSet<> &inferredAttributes,
bool isRawValueAttr = false);
void genCanonicalizerDecls();
void genFolderDecls();
void genParser();
void genPrinter();
void genVerifier();
void genCustomVerifier();
void genOperandResultVerifier(MethodBody &body,
Operator::const_value_range values,
StringRef valueKind);
void genRegionVerifier(MethodBody &body);
void genSuccessorVerifier(MethodBody &body);
void genTraits();
void genOpInterfaceMethods();
void genOpInterfaceMethods(const tblgen::InterfaceTrait *trait);
Method *genOpInterfaceMethod(const tblgen::InterfaceMethod &method,
bool declaration = true);
void genSideEffectInterfaceMethods();
void genTypeInterfaceMethods();
private:
const Record &def;
const Operator &op;
OpClass opClass;
FmtContext verifyCtx;
const StaticVerifierFunctionEmitter &staticVerifierEmitter;
OpOrAdaptorHelper emitHelper;
};
}
static void populateSubstitutions(const OpOrAdaptorHelper &emitHelper,
FmtContext &ctx) {
auto &op = emitHelper.getOp();
for (const auto &namedAttr : op.getAttributes())
ctx.addSubst(namedAttr.name,
emitHelper.getOp().getGetterName(namedAttr.name) + "()");
for (int i = 0, e = op.getNumOperands(); i < e; ++i) {
auto &value = op.getOperand(i);
if (!value.name.empty())
ctx.addSubst(value.name, emitHelper.getOperand(i).str());
}
for (int i = 0, e = op.getNumResults(); i < e; ++i) {
auto &value = op.getResult(i);
if (!value.name.empty())
ctx.addSubst(value.name, emitHelper.getResult(i).str());
}
}
static void genNativeTraitAttrVerifier(MethodBody &body,
const OpOrAdaptorHelper &emitHelper) {
const char *const checkAttrSizedValueSegmentsCode = R"(
{
auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>(tblgen_{0});
auto numElements = sizeAttr.asArrayRef().size();
if (numElements != {1})
return {3}"'{0}' attribute for specifying {2} segments must have {1} "
"elements, but got ") << numElements;
}
)";
auto &op = emitHelper.getOp();
if (!op.getDialect().usePropertiesForAttributes()) {
if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
body << formatv(checkAttrSizedValueSegmentsCode, operandSegmentAttrName,
op.getNumOperands(), "operand",
emitHelper.emitErrorPrefix());
}
if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {
body << formatv(checkAttrSizedValueSegmentsCode, resultSegmentAttrName,
op.getNumResults(), "result",
emitHelper.emitErrorPrefix());
}
}
}
static bool canEmitAttrVerifier(Attribute attr, bool isEmittingForOp) {
if (attr.isDerivedAttr())
return false;
Pred pred = attr.getPredicate();
if (pred.isNull())
return false;
std::string condition = pred.getCondition();
return !condition.empty() &&
(!StringRef(condition).contains("$_op") || isEmittingForOp);
}
static void
genAttributeVerifier(const OpOrAdaptorHelper &emitHelper, FmtContext &ctx,
MethodBody &body,
const StaticVerifierFunctionEmitter &staticVerifierEmitter,
bool useProperties) {
if (emitHelper.getAttrMetadata().empty())
return;
const char *const verifyAttrInline = R"(
if ({0} && !({1}))
return {2}"attribute '{3}' failed to satisfy constraint: {4}");
)";
const char *const verifyAttrUnique = R"(
if (::mlir::failed({0}(*this, {1}, "{2}")))
return ::mlir::failure();
)";
const char *const findRequiredAttr = R"(
while (true) {{
if (namedAttrIt == namedAttrRange.end())
return {1}"requires attribute '{2}'");
if (namedAttrIt->getName() == {0}) {{
tblgen_{2} = namedAttrIt->getValue();
break;
})";
const char *const checkOptionalAttr = R"(
else if (namedAttrIt->getName() == {0}) {{
tblgen_{1} = namedAttrIt->getValue();
})";
const char *const checkTrailingAttrs = R"(while (true) {
if (namedAttrIt == namedAttrRange.end()) {
break;
})";
const auto emitVerifier = [&](Attribute attr, StringRef attrName,
StringRef varName) {
std::string condition = attr.getPredicate().getCondition();
std::optional<StringRef> constraintFn;
if (emitHelper.isEmittingForOp() &&
(constraintFn = staticVerifierEmitter.getAttrConstraintFn(attr))) {
body << formatv(verifyAttrUnique, *constraintFn, varName, attrName);
} else {
body << formatv(verifyAttrInline, varName,
tgfmt(condition, &ctx.withSelf(varName)),
emitHelper.emitErrorPrefix(), attrName,
escapeString(attr.getSummary()));
}
};
const auto getVarName = [&](StringRef attrName) {
return (tblgenNamePrefix + attrName).str();
};
body.indent();
if (useProperties) {
for (const std::pair<StringRef, AttributeMetadata> &it :
emitHelper.getAttrMetadata()) {
const AttributeMetadata &metadata = it.second;
if (metadata.constraint && metadata.constraint->isDerivedAttr())
continue;
body << formatv(
"auto tblgen_{0} = getProperties().{0}; (void)tblgen_{0};\n",
it.first);
if (metadata.isRequired)
body << formatv(
"if (!tblgen_{0}) return {1}\"requires attribute '{0}'\");\n",
it.first, emitHelper.emitErrorPrefix());
}
} else {
body << formatv("auto namedAttrRange = {0};\n", emitHelper.getAttrRange());
body << "auto namedAttrIt = namedAttrRange.begin();\n";
SmallVector<const AttributeMetadata *> optionalAttrs;
for (const std::pair<StringRef, AttributeMetadata> &it :
emitHelper.getAttrMetadata()) {
const AttributeMetadata &metadata = it.second;
if (!metadata.isRequired) {
optionalAttrs.push_back(&metadata);
continue;
}
body << formatv("::mlir::Attribute {0};\n", getVarName(it.first));
for (const AttributeMetadata *optional : optionalAttrs) {
body << formatv("::mlir::Attribute {0};\n",
getVarName(optional->attrName));
}
body << formatv(findRequiredAttr, emitHelper.getAttrName(it.first),
emitHelper.emitErrorPrefix(), it.first);
for (const AttributeMetadata *optional : optionalAttrs) {
body << formatv(checkOptionalAttr,
emitHelper.getAttrName(optional->attrName),
optional->attrName);
}
body << "\n ++namedAttrIt;\n}\n";
optionalAttrs.clear();
}
if (!optionalAttrs.empty()) {
for (const AttributeMetadata *optional : optionalAttrs) {
body << formatv("::mlir::Attribute {0};\n",
getVarName(optional->attrName));
}
body << checkTrailingAttrs;
for (const AttributeMetadata *optional : optionalAttrs) {
body << formatv(checkOptionalAttr,
emitHelper.getAttrName(optional->attrName),
optional->attrName);
}
body << "\n ++namedAttrIt;\n}\n";
}
}
body.unindent();
genNativeTraitAttrVerifier(body, emitHelper);
bool isEmittingForOp = emitHelper.isEmittingForOp();
for (const auto &namedAttr : emitHelper.getOp().getAttributes())
if (canEmitAttrVerifier(namedAttr.attr, isEmittingForOp))
emitVerifier(namedAttr.attr, namedAttr.name, getVarName(namedAttr.name));
}
static std::string formatExtraDeclarations(const Operator &op) {
SmallVector<StringRef> extraDeclarations;
for (const auto &trait : op.getTraits()) {
if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {
StringRef value = opTrait->getExtraConcreteClassDeclaration();
if (value.empty())
continue;
extraDeclarations.push_back(value);
}
}
extraDeclarations.push_back(op.getExtraClassDeclaration());
return llvm::join(extraDeclarations, "\n");
}
static std::string formatExtraDefinitions(const Operator &op) {
SmallVector<StringRef> extraDefinitions;
for (const auto &trait : op.getTraits()) {
if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {
StringRef value = opTrait->getExtraConcreteClassDefinition();
if (value.empty())
continue;
extraDefinitions.push_back(value);
}
}
extraDefinitions.push_back(op.getExtraClassDefinition());
FmtContext ctx = FmtContext().addSubst("cppClass", op.getCppClassName());
return tgfmt(llvm::join(extraDefinitions, "\n"), &ctx).str();
}
OpEmitter::OpEmitter(const Operator &op,
const StaticVerifierFunctionEmitter &staticVerifierEmitter)
: def(op.getDef()), op(op),
opClass(op.getCppClassName(), formatExtraDeclarations(op),
formatExtraDefinitions(op)),
staticVerifierEmitter(staticVerifierEmitter),
emitHelper(op, true) {
verifyCtx.addSubst("_op", "(*this->getOperation())");
verifyCtx.addSubst("_ctxt", "this->getOperation()->getContext()");
genTraits();
genAttrNameGetters();
genOpAsmInterface();
genOpNameGetter();
genNamedOperandGetters();
genNamedOperandSetters();
genNamedResultGetters();
genNamedRegionGetters();
genNamedSuccessorGetters();
genPropertiesSupport();
genAttrGetters();
genAttrSetters();
genOptionalAttrRemovers();
genBuilder();
genPopulateDefaultAttributes();
genParser();
genPrinter();
genVerifier();
genCustomVerifier();
genCanonicalizerDecls();
genFolderDecls();
genTypeInterfaceMethods();
genOpInterfaceMethods();
generateOpFormat(op, opClass);
genSideEffectInterfaceMethods();
}
void OpEmitter::emitDecl(
const Operator &op, raw_ostream &os,
const StaticVerifierFunctionEmitter &staticVerifierEmitter) {
OpEmitter(op, staticVerifierEmitter).emitDecl(os);
}
void OpEmitter::emitDef(
const Operator &op, raw_ostream &os,
const StaticVerifierFunctionEmitter &staticVerifierEmitter) {
OpEmitter(op, staticVerifierEmitter).emitDef(os);
}
void OpEmitter::emitDecl(raw_ostream &os) {
opClass.finalize();
opClass.writeDeclTo(os);
}
void OpEmitter::emitDef(raw_ostream &os) {
opClass.finalize();
opClass.writeDefTo(os);
}
static void errorIfPruned(size_t line, Method *m, const Twine &methodName,
const Operator &op) {
if (m)
return;
PrintFatalError(op.getLoc(), "Unexpected overlap when generating `" +
methodName + "` for " +
op.getOperationName() + " (from line " +
Twine(line) + ")");
}
#define ERROR_IF_PRUNED(M, N, O) errorIfPruned(__LINE__, M, N, O)
void OpEmitter::genAttrNameGetters() {
const llvm::MapVector<StringRef, AttributeMetadata> &attributes =
emitHelper.getAttrMetadata();
bool hasOperandSegmentsSize =
op.getDialect().usePropertiesForAttributes() &&
op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");
{
auto *method = opClass.addStaticInlineMethod(
"::llvm::ArrayRef<::llvm::StringRef>", "getAttributeNames");
ERROR_IF_PRUNED(method, "getAttributeNames", op);
auto &body = method->body();
if (!hasOperandSegmentsSize && attributes.empty()) {
body << " return {};";
return;
}
body << " static ::llvm::StringRef attrNames[] = {";
llvm::interleaveComma(llvm::make_first_range(attributes), body,
[&](StringRef attrName) {
body << "::llvm::StringRef(\"" << attrName << "\")";
});
if (hasOperandSegmentsSize) {
if (!attributes.empty())
body << ", ";
body << "::llvm::StringRef(\"" << operandSegmentAttrName << "\")";
}
body << "};\n return ::llvm::ArrayRef(attrNames);";
}
{
auto *method = opClass.addInlineMethod<Method::Private>(
"::mlir::StringAttr", "getAttributeNameForIndex",
MethodParameter("unsigned", "index"));
ERROR_IF_PRUNED(method, "getAttributeNameForIndex", op);
method->body()
<< " return getAttributeNameForIndex((*this)->getName(), index);";
}
{
auto *method = opClass.addStaticInlineMethod<Method::Private>(
"::mlir::StringAttr", "getAttributeNameForIndex",
MethodParameter("::mlir::OperationName", "name"),
MethodParameter("unsigned", "index"));
ERROR_IF_PRUNED(method, "getAttributeNameForIndex", op);
if (attributes.empty()) {
method->body() << " return {};";
} else {
const char *const getAttrName = R"(
assert(index < {0} && "invalid attribute index");
assert(name.getStringRef() == getOperationName() && "invalid operation name");
assert(name.isRegistered() && "Operation isn't registered, missing a "
"dependent dialect loading?");
return name.getAttributeNames()[index];
)";
method->body() << formatv(getAttrName, attributes.size());
}
}
const char *attrNameMethodBody = " return getAttributeNameForIndex({0});";
for (auto [index, attr] :
llvm::enumerate(llvm::make_first_range(attributes))) {
std::string name = op.getGetterName(attr);
std::string methodName = name + "AttrName";
{
auto *method = opClass.addInlineMethod("::mlir::StringAttr", methodName);
ERROR_IF_PRUNED(method, methodName, op);
method->body() << llvm::formatv(attrNameMethodBody, index);
}
{
auto *method = opClass.addStaticInlineMethod(
"::mlir::StringAttr", methodName,
MethodParameter("::mlir::OperationName", "name"));
ERROR_IF_PRUNED(method, methodName, op);
method->body() << llvm::formatv(attrNameMethodBody,
"name, " + Twine(index));
}
}
if (hasOperandSegmentsSize) {
std::string name = op.getGetterName(operandSegmentAttrName);
std::string methodName = name + "AttrName";
{
auto *method = opClass.addInlineMethod("::mlir::StringAttr", methodName);
ERROR_IF_PRUNED(method, methodName, op);
method->body()
<< " return (*this)->getName().getAttributeNames().back();";
}
{
auto *method = opClass.addStaticInlineMethod(
"::mlir::StringAttr", methodName,
MethodParameter("::mlir::OperationName", "name"));
ERROR_IF_PRUNED(method, methodName, op);
method->body() << " return name.getAttributeNames().back();";
}
}
}
template <typename OpClassOrAdaptor>
static void emitAttrGetterWithReturnType(FmtContext &fctx,
OpClassOrAdaptor &opClass,
const Operator &op, StringRef name,
Attribute attr) {
auto *method = opClass.addMethod(attr.getReturnType(), name);
ERROR_IF_PRUNED(method, name, op);
auto &body = method->body();
body << " auto attr = " << name << "Attr();\n";
if (attr.hasDefaultValue() && attr.isOptional()) {
if (!attr.isConstBuildable()) {
PrintFatalError("DefaultValuedAttr of type " + attr.getAttrDefName() +
" must have a constBuilder");
}
std::string defaultValue = std::string(
tgfmt(attr.getConstBuilderTemplate(), &fctx, attr.getDefaultValue()));
body << " if (!attr)\n return "
<< tgfmt(attr.getConvertFromStorageCall(),
&fctx.withSelf(defaultValue))
<< ";\n";
}
body << " return "
<< tgfmt(attr.getConvertFromStorageCall(), &fctx.withSelf("attr"))
<< ";\n";
}
void OpEmitter::genPropertiesSupport() {
if (!emitHelper.hasProperties())
return;
SmallVector<ConstArgument> attrOrProperties;
for (const std::pair<StringRef, AttributeMetadata> &it :
emitHelper.getAttrMetadata()) {
if (!it.second.constraint || !it.second.constraint->isDerivedAttr())
attrOrProperties.push_back(&it.second);
}
for (const NamedProperty &prop : op.getProperties())
attrOrProperties.push_back(&prop);
if (emitHelper.getOperandSegmentsSize())
attrOrProperties.push_back(&emitHelper.getOperandSegmentsSize().value());
if (emitHelper.getResultSegmentsSize())
attrOrProperties.push_back(&emitHelper.getResultSegmentsSize().value());
if (attrOrProperties.empty())
return;
auto &setPropMethod =
opClass
.addStaticMethod(
"::llvm::LogicalResult", "setPropertiesFromAttr",
MethodParameter("Properties &", "prop"),
MethodParameter("::mlir::Attribute", "attr"),
MethodParameter(
"::llvm::function_ref<::mlir::InFlightDiagnostic()>",
"emitError"))
->body();
auto &getPropMethod =
opClass
.addStaticMethod("::mlir::Attribute", "getPropertiesAsAttr",
MethodParameter("::mlir::MLIRContext *", "ctx"),
MethodParameter("const Properties &", "prop"))
->body();
auto &hashMethod =
opClass
.addStaticMethod("llvm::hash_code", "computePropertiesHash",
MethodParameter("const Properties &", "prop"))
->body();
auto &getInherentAttrMethod =
opClass
.addStaticMethod("std::optional<mlir::Attribute>", "getInherentAttr",
MethodParameter("::mlir::MLIRContext *", "ctx"),
MethodParameter("const Properties &", "prop"),
MethodParameter("llvm::StringRef", "name"))
->body();
auto &setInherentAttrMethod =
opClass
.addStaticMethod("void", "setInherentAttr",
MethodParameter("Properties &", "prop"),
MethodParameter("llvm::StringRef", "name"),
MethodParameter("mlir::Attribute", "value"))
->body();
auto &populateInherentAttrsMethod =
opClass
.addStaticMethod("void", "populateInherentAttrs",
MethodParameter("::mlir::MLIRContext *", "ctx"),
MethodParameter("const Properties &", "prop"),
MethodParameter("::mlir::NamedAttrList &", "attrs"))
->body();
auto &verifyInherentAttrsMethod =
opClass
.addStaticMethod(
"::llvm::LogicalResult", "verifyInherentAttrs",
MethodParameter("::mlir::OperationName", "opName"),
MethodParameter("::mlir::NamedAttrList &", "attrs"),
MethodParameter(
"llvm::function_ref<::mlir::InFlightDiagnostic()>",
"emitError"))
->body();
opClass.declare<UsingDeclaration>("Properties", "FoldAdaptor::Properties");
setPropMethod << R"decl(
::mlir::DictionaryAttr dict = ::llvm::dyn_cast<::mlir::DictionaryAttr>(attr);
if (!dict) {
emitError() << "expected DictionaryAttr to set properties";
return ::mlir::failure();
}
)decl";
const char *propFromAttrFmt = R"decl(
auto setFromAttr = [] (auto &propStorage, ::mlir::Attribute propAttr,
::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
{0}
};
{2};
)decl";
const char *attrGetNoDefaultFmt = R"decl(;
if (attr && ::mlir::failed(setFromAttr(prop.{0}, attr, emitError)))
return ::mlir::failure();
)decl";
const char *attrGetDefaultFmt = R"decl(;
if (attr) {{
if (::mlir::failed(setFromAttr(prop.{0}, attr, emitError)))
return ::mlir::failure();
} else {{
prop.{0} = {1};
}
)decl";
for (const auto &attrOrProp : attrOrProperties) {
if (const auto *namedProperty =
llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {
StringRef name = namedProperty->name;
auto &prop = namedProperty->prop;
FmtContext fctx;
std::string getAttr;
llvm::raw_string_ostream os(getAttr);
os << " auto attr = dict.get(\"" << name << "\");";
if (name == operandSegmentAttrName) {
os << " if (!attr) attr = dict.get(\"operand_segment_sizes\");";
}
if (name == resultSegmentAttrName) {
os << " if (!attr) attr = dict.get(\"result_segment_sizes\");";
}
os.flush();
setPropMethod << "{\n"
<< formatv(propFromAttrFmt,
tgfmt(prop.getConvertFromAttributeCall(),
&fctx.addSubst("_attr", propertyAttr)
.addSubst("_storage", propertyStorage)
.addSubst("_diag", propertyDiag)),
name, getAttr);
if (prop.hasDefaultValue()) {
setPropMethod << formatv(attrGetDefaultFmt, name,
prop.getDefaultValue());
} else {
setPropMethod << formatv(attrGetNoDefaultFmt, name);
}
setPropMethod << " }\n";
} else {
const auto *namedAttr =
llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);
StringRef name = namedAttr->attrName;
std::string getAttr;
llvm::raw_string_ostream os(getAttr);
os << " auto attr = dict.get(\"" << name << "\");";
if (name == operandSegmentAttrName) {
os << " if (!attr) attr = dict.get(\"operand_segment_sizes\");";
}
if (name == resultSegmentAttrName) {
os << " if (!attr) attr = dict.get(\"result_segment_sizes\");";
}
os.flush();
setPropMethod << formatv(R"decl(
{{
auto &propStorage = prop.{0};
{1}
if (attr) {{
auto convertedAttr = ::llvm::dyn_cast<std::remove_reference_t<decltype(propStorage)>>(attr);
if (convertedAttr) {{
propStorage = convertedAttr;
} else {{
emitError() << "Invalid attribute `{0}` in property conversion: " << attr;
return ::mlir::failure();
}
}
}
)decl",
name, getAttr);
}
}
setPropMethod << " return ::mlir::success();\n";
getPropMethod << " ::mlir::SmallVector<::mlir::NamedAttribute> attrs;\n"
<< " ::mlir::Builder odsBuilder{ctx};\n";
const char *propToAttrFmt = R"decl(
{
const auto &propStorage = prop.{0};
attrs.push_back(odsBuilder.getNamedAttr("{0}",
{1}));
}
)decl";
for (const auto &attrOrProp : attrOrProperties) {
if (const auto *namedProperty =
llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {
StringRef name = namedProperty->name;
auto &prop = namedProperty->prop;
FmtContext fctx;
getPropMethod << formatv(
propToAttrFmt, name,
tgfmt(prop.getConvertToAttributeCall(),
&fctx.addSubst("_ctxt", "ctx")
.addSubst("_storage", propertyStorage)));
continue;
}
const auto *namedAttr =
llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);
StringRef name = namedAttr->attrName;
getPropMethod << formatv(R"decl(
{{
const auto &propStorage = prop.{0};
if (propStorage)
attrs.push_back(odsBuilder.getNamedAttr("{0}",
propStorage));
}
)decl",
name);
}
getPropMethod << R"decl(
if (!attrs.empty())
return odsBuilder.getDictionaryAttr(attrs);
return {};
)decl";
const char *propHashFmt = R"decl(
auto hash_{0} = [] (const auto &propStorage) -> llvm::hash_code {
return {1};
};
)decl";
for (const auto &attrOrProp : attrOrProperties) {
if (const auto *namedProperty =
llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {
StringRef name = namedProperty->name;
auto &prop = namedProperty->prop;
FmtContext fctx;
hashMethod << formatv(propHashFmt, name,
tgfmt(prop.getHashPropertyCall(),
&fctx.addSubst("_storage", propertyStorage)));
}
}
hashMethod << " return llvm::hash_combine(";
llvm::interleaveComma(
attrOrProperties, hashMethod, [&](const ConstArgument &attrOrProp) {
if (const auto *namedProperty =
llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {
hashMethod << "\n hash_" << namedProperty->name << "(prop."
<< namedProperty->name << ")";
return;
}
const auto *namedAttr =
llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);
StringRef name = namedAttr->attrName;
hashMethod << "\n llvm::hash_value(prop." << name
<< ".getAsOpaquePointer())";
});
hashMethod << ");\n";
const char *getInherentAttrMethodFmt = R"decl(
if (name == "{0}")
return prop.{0};
)decl";
const char *setInherentAttrMethodFmt = R"decl(
if (name == "{0}") {{
prop.{0} = ::llvm::dyn_cast_or_null<std::remove_reference_t<decltype(prop.{0})>>(value);
return;
}
)decl";
const char *populateInherentAttrsMethodFmt = R"decl(
if (prop.{0}) attrs.append("{0}", prop.{0});
)decl";
for (const auto &attrOrProp : attrOrProperties) {
if (const auto *namedAttr =
llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp)) {
StringRef name = namedAttr->attrName;
getInherentAttrMethod << formatv(getInherentAttrMethodFmt, name);
setInherentAttrMethod << formatv(setInherentAttrMethodFmt, name);
populateInherentAttrsMethod
<< formatv(populateInherentAttrsMethodFmt, name);
continue;
}
const auto *namedProperty = cast<const NamedProperty *>(attrOrProp);
StringRef name = namedProperty->name;
if (name != operandSegmentAttrName && name != resultSegmentAttrName)
continue;
auto &prop = namedProperty->prop;
FmtContext fctx;
fctx.addSubst("_ctxt", "ctx");
fctx.addSubst("_storage", Twine("prop.") + name);
if (name == operandSegmentAttrName) {
getInherentAttrMethod
<< formatv(" if (name == \"operand_segment_sizes\" || name == "
"\"{0}\") return ",
operandSegmentAttrName);
} else {
getInherentAttrMethod
<< formatv(" if (name == \"result_segment_sizes\" || name == "
"\"{0}\") return ",
resultSegmentAttrName);
}
getInherentAttrMethod << tgfmt(prop.getConvertToAttributeCall(), &fctx)
<< ";\n";
if (name == operandSegmentAttrName) {
setInherentAttrMethod
<< formatv(" if (name == \"operand_segment_sizes\" || name == "
"\"{0}\") {{",
operandSegmentAttrName);
} else {
setInherentAttrMethod
<< formatv(" if (name == \"result_segment_sizes\" || name == "
"\"{0}\") {{",
resultSegmentAttrName);
}
setInherentAttrMethod << formatv(R"decl(
auto arrAttr = ::llvm::dyn_cast_or_null<::mlir::DenseI32ArrayAttr>(value);
if (!arrAttr) return;
if (arrAttr.size() != sizeof(prop.{0}) / sizeof(int32_t))
return;
llvm::copy(arrAttr.asArrayRef(), prop.{0}.begin());
return;
}
)decl",
name);
if (name == operandSegmentAttrName) {
populateInherentAttrsMethod
<< formatv(" attrs.append(\"{0}\", {1});\n", operandSegmentAttrName,
tgfmt(prop.getConvertToAttributeCall(), &fctx));
} else {
populateInherentAttrsMethod
<< formatv(" attrs.append(\"{0}\", {1});\n", resultSegmentAttrName,
tgfmt(prop.getConvertToAttributeCall(), &fctx));
}
}
getInherentAttrMethod << " return std::nullopt;\n";
for (const auto &attrOrProp : attrOrProperties) {
const auto *namedAttr =
llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);
if (!namedAttr || !namedAttr->constraint)
continue;
Attribute attr = *namedAttr->constraint;
std::optional<StringRef> constraintFn =
staticVerifierEmitter.getAttrConstraintFn(attr);
if (!constraintFn)
continue;
if (canEmitAttrVerifier(attr,
false)) {
std::string name = op.getGetterName(namedAttr->attrName);
verifyInherentAttrsMethod
<< formatv(R"(
{{
::mlir::Attribute attr = attrs.get({0}AttrName(opName));
if (attr && ::mlir::failed({1}(attr, "{2}", emitError)))
return ::mlir::failure();
}
)",
name, constraintFn, namedAttr->attrName);
}
}
verifyInherentAttrsMethod << " return ::mlir::success();";
genPropertiesSupportForBytecode(attrOrProperties);
}
void OpEmitter::genPropertiesSupportForBytecode(
ArrayRef<ConstArgument> attrOrProperties) {
if (op.useCustomPropertiesEncoding()) {
opClass.declareStaticMethod(
"::llvm::LogicalResult", "readProperties",
MethodParameter("::mlir::DialectBytecodeReader &", "reader"),
MethodParameter("::mlir::OperationState &", "state"));
opClass.declareMethod(
"void", "writeProperties",
MethodParameter("::mlir::DialectBytecodeWriter &", "writer"));
return;
}
auto &readPropertiesMethod =
opClass
.addStaticMethod(
"::llvm::LogicalResult", "readProperties",
MethodParameter("::mlir::DialectBytecodeReader &", "reader"),
MethodParameter("::mlir::OperationState &", "state"))
->body();
auto &writePropertiesMethod =
opClass
.addMethod(
"void", "writeProperties",
MethodParameter("::mlir::DialectBytecodeWriter &", "writer"))
->body();
readPropertiesMethod
<< " auto &prop = state.getOrAddProperties<Properties>(); (void)prop;";
writePropertiesMethod << " auto &prop = getProperties(); (void)prop;\n";
for (const auto &item : llvm::enumerate(attrOrProperties)) {
auto &attrOrProp = item.value();
FmtContext fctx;
fctx.addSubst("_reader", "reader")
.addSubst("_writer", "writer")
.addSubst("_storage", propertyStorage)
.addSubst("_ctxt", "this->getContext()");
if (emitHelper.getOperandSegmentsSize().has_value() &&
item.index() == emitHelper.getOperandSegmentSizesLegacyIndex()) {
FmtContext fmtCtxt(fctx);
fmtCtxt.addSubst("_propName", operandSegmentAttrName);
readPropertiesMethod << tgfmt(readBytecodeSegmentSizeLegacy, &fmtCtxt);
writePropertiesMethod << tgfmt(writeBytecodeSegmentSizeLegacy, &fmtCtxt);
}
if (emitHelper.getResultSegmentsSize().has_value() &&
item.index() == emitHelper.getResultSegmentSizesLegacyIndex()) {
FmtContext fmtCtxt(fctx);
fmtCtxt.addSubst("_propName", resultSegmentAttrName);
readPropertiesMethod << tgfmt(readBytecodeSegmentSizeLegacy, &fmtCtxt);
writePropertiesMethod << tgfmt(writeBytecodeSegmentSizeLegacy, &fmtCtxt);
}
if (const auto *namedProperty =
attrOrProp.dyn_cast<const NamedProperty *>()) {
StringRef name = namedProperty->name;
readPropertiesMethod << formatv(
R"(
{{
auto &propStorage = prop.{0};
auto readProp = [&]() {
{1};
return ::mlir::success();
};
if (::mlir::failed(readProp()))
return ::mlir::failure();
}
)",
name,
tgfmt(namedProperty->prop.getReadFromMlirBytecodeCall(), &fctx));
writePropertiesMethod << formatv(
R"(
{{
auto &propStorage = prop.{0};
{1};
}
)",
name, tgfmt(namedProperty->prop.getWriteToMlirBytecodeCall(), &fctx));
continue;
}
const auto *namedAttr = attrOrProp.dyn_cast<const AttributeMetadata *>();
StringRef name = namedAttr->attrName;
if (namedAttr->isRequired) {
readPropertiesMethod << formatv(R"(
if (::mlir::failed(reader.readAttribute(prop.{0})))
return ::mlir::failure();
)",
name);
writePropertiesMethod
<< formatv(" writer.writeAttribute(prop.{0});\n", name);
} else {
readPropertiesMethod << formatv(R"(
if (::mlir::failed(reader.readOptionalAttribute(prop.{0})))
return ::mlir::failure();
)",
name);
writePropertiesMethod << formatv(R"(
writer.writeOptionalAttribute(prop.{0});
)",
name);
}
}
readPropertiesMethod << " return ::mlir::success();";
}
void OpEmitter::genAttrGetters() {
FmtContext fctx;
fctx.withBuilder("::mlir::Builder((*this)->getContext())");
auto emitDerivedAttr = [&](StringRef name, Attribute attr) {
if (auto *method = opClass.addMethod(attr.getReturnType(), name))
method->body() << " " << attr.getDerivedCodeBody() << "\n";
};
auto emitAttrWithStorageType = [&](StringRef name, StringRef attrName,
Attribute attr) {
auto *method =
opClass.addInlineMethod(attr.getStorageType(), name + "Attr");
if (!method)
return;
method->body() << formatv(
" return ::llvm::{1}<{2}>({0});", emitHelper.getAttr(attrName),
attr.isOptional() || attr.hasDefaultValue() ? "dyn_cast_or_null"
: "cast",
attr.getStorageType());
};
for (const NamedAttribute &namedAttr : op.getAttributes()) {
std::string name = op.getGetterName(namedAttr.name);
if (namedAttr.attr.isDerivedAttr()) {
emitDerivedAttr(name, namedAttr.attr);
} else {
emitAttrWithStorageType(name, namedAttr.name, namedAttr.attr);
emitAttrGetterWithReturnType(fctx, opClass, op, name, namedAttr.attr);
}
}
auto derivedAttrs = make_filter_range(op.getAttributes(),
[](const NamedAttribute &namedAttr) {
return namedAttr.attr.isDerivedAttr();
});
if (derivedAttrs.empty())
return;
opClass.addTrait("::mlir::DerivedAttributeOpInterface::Trait");
{
auto *method =
opClass.addStaticMethod("bool", "isDerivedAttribute",
MethodParameter("::llvm::StringRef", "name"));
ERROR_IF_PRUNED(method, "isDerivedAttribute", op);
auto &body = method->body();
for (auto namedAttr : derivedAttrs)
body << " if (name == \"" << namedAttr.name << "\") return true;\n";
body << " return false;";
}
{
auto *method = opClass.addMethod("::mlir::DictionaryAttr",
"materializeDerivedAttributes");
ERROR_IF_PRUNED(method, "materializeDerivedAttributes", op);
auto &body = method->body();
auto nonMaterializable =
make_filter_range(derivedAttrs, [](const NamedAttribute &namedAttr) {
return namedAttr.attr.getConvertFromStorageCall().empty();
});
if (!nonMaterializable.empty()) {
std::string attrs;
llvm::raw_string_ostream os(attrs);
interleaveComma(nonMaterializable, os, [&](const NamedAttribute &attr) {
os << op.getGetterName(attr.name);
});
PrintWarning(
op.getLoc(),
formatv(
"op has non-materializable derived attributes '{0}', skipping",
os.str()));
body << formatv(" emitOpError(\"op has non-materializable derived "
"attributes '{0}'\");\n",
attrs);
body << " return nullptr;";
return;
}
body << " ::mlir::MLIRContext* ctx = getContext();\n";
body << " ::mlir::Builder odsBuilder(ctx); (void)odsBuilder;\n";
body << " return ::mlir::DictionaryAttr::get(";
body << " ctx, {\n";
interleave(
derivedAttrs, body,
[&](const NamedAttribute &namedAttr) {
auto tmpl = namedAttr.attr.getConvertFromStorageCall();
std::string name = op.getGetterName(namedAttr.name);
body << " {" << name << "AttrName(),\n"
<< tgfmt(tmpl, &fctx.withSelf(name + "()")
.withBuilder("odsBuilder")
.addSubst("_ctxt", "ctx")
.addSubst("_storage", "ctx"))
<< "}";
},
",\n");
body << "});";
}
}
void OpEmitter::genAttrSetters() {
bool useProperties = op.getDialect().usePropertiesForAttributes();
auto emitSetAttr = [&](Method *method, StringRef getterName,
StringRef attrName, StringRef attrVar) {
if (useProperties) {
method->body() << formatv(" getProperties().{0} = {1};", attrName,
attrVar);
} else {
method->body() << formatv(" (*this)->setAttr({0}AttrName(), {1});",
getterName, attrVar);
}
};
auto emitAttrWithStorageType = [&](StringRef setterName, StringRef getterName,
StringRef attrName, Attribute attr) {
auto *method =
opClass.addInlineMethod("void", setterName + "Attr",
MethodParameter(attr.getStorageType(), "attr"));
if (method)
emitSetAttr(method, getterName, attrName, "attr");
};
auto emitAttrWithReturnType = [&](StringRef setterName, StringRef getterName,
StringRef attrName, Attribute attr) {
Attribute baseAttr = attr.getBaseAttr();
if (!canUseUnwrappedRawValue(baseAttr))
return;
FmtContext fctx;
fctx.withBuilder("::mlir::Builder((*this)->getContext())");
bool isUnitAttr = attr.getAttrDefName() == "UnitAttr";
bool isOptional = attr.isOptional();
auto createMethod = [&](const Twine ¶mType) {
return opClass.addMethod("void", setterName,
MethodParameter(paramType.str(), "attrValue"));
};
Method *method = nullptr;
if (isUnitAttr)
method = createMethod("bool");
else if (isOptional)
method =
createMethod("::std::optional<" + baseAttr.getReturnType() + ">");
else
method = createMethod(attr.getReturnType());
if (!method)
return;
if (!isOptional) {
emitSetAttr(method, getterName, attrName,
constBuildAttrFromParam(attr, fctx, "attrValue"));
return;
}
StringRef paramStr = isUnitAttr ? "attrValue" : "*attrValue";
if (!useProperties) {
const char *optionalCodeBody = R"(
if (attrValue)
return (*this)->setAttr({0}AttrName(), {1});
(*this)->removeAttr({0}AttrName());)";
method->body() << formatv(
optionalCodeBody, getterName,
constBuildAttrFromParam(baseAttr, fctx, paramStr));
} else {
const char *optionalCodeBody = R"(
auto &odsProp = getProperties().{0};
if (attrValue)
odsProp = {1};
else
odsProp = nullptr;)";
method->body() << formatv(
optionalCodeBody, attrName,
constBuildAttrFromParam(baseAttr, fctx, paramStr));
}
};
for (const NamedAttribute &namedAttr : op.getAttributes()) {
if (namedAttr.attr.isDerivedAttr())
continue;
std::string setterName = op.getSetterName(namedAttr.name);
std::string getterName = op.getGetterName(namedAttr.name);
emitAttrWithStorageType(setterName, getterName, namedAttr.name,
namedAttr.attr);
emitAttrWithReturnType(setterName, getterName, namedAttr.name,
namedAttr.attr);
}
}
void OpEmitter::genOptionalAttrRemovers() {
auto emitRemoveAttr = [&](StringRef name, bool useProperties) {
auto upperInitial = name.take_front().upper();
auto *method = opClass.addInlineMethod("::mlir::Attribute",
op.getRemoverName(name) + "Attr");
if (!method)
return;
if (useProperties) {
method->body() << formatv(R"(
auto &attr = getProperties().{0};
attr = {{};
return attr;
)",
name);
return;
}
method->body() << formatv("return (*this)->removeAttr({0}AttrName());",
op.getGetterName(name));
};
for (const NamedAttribute &namedAttr : op.getAttributes())
if (namedAttr.attr.isOptional())
emitRemoveAttr(namedAttr.name,
op.getDialect().usePropertiesForAttributes());
}
template <typename RangeT>
static void generateValueRangeStartAndEnd(
Class &opClass, bool isGenericAdaptorBase, StringRef methodName,
int numVariadic, int numNonVariadic, StringRef rangeSizeCall,
bool hasAttrSegmentSize, StringRef sizeAttrInit, RangeT &&odsValues) {
SmallVector<MethodParameter> parameters{MethodParameter("unsigned", "index")};
if (isGenericAdaptorBase) {
parameters.emplace_back("unsigned", "odsOperandsSize");
rangeSizeCall = "odsOperandsSize";
}
auto *method = opClass.addMethod("std::pair<unsigned, unsigned>", methodName,
numVariadic == 0 ? Method::Properties::Inline
: Method::Properties::None,
parameters);
if (!method)
return;
auto &body = method->body();
if (numVariadic == 0) {
body << " return {index, 1};\n";
} else if (hasAttrSegmentSize) {
body << sizeAttrInit << attrSizedSegmentValueRangeCalcCode;
} else {
SmallVector<StringRef, 4> isVariadic;
isVariadic.reserve(llvm::size(odsValues));
for (auto &it : odsValues)
isVariadic.push_back(it.isVariableLength() ? "true" : "false");
std::string isVariadicList = llvm::join(isVariadic, ", ");
body << formatv(sameVariadicSizeValueRangeCalcCode, isVariadicList,
numNonVariadic, numVariadic, rangeSizeCall, "operand");
}
}
static std::string generateTypeForGetter(const NamedTypeConstraint &value) {
std::string str = "::mlir::Value";
if (value.constraint.getCPPClassName() != "::mlir::Type" &&
StringRef(value.constraint.getCPPClassName()).starts_with("::"))
str = llvm::formatv("::mlir::TypedValue<{0}>",
value.constraint.getCPPClassName())
.str();
return str;
}
static void
generateNamedOperandGetters(const Operator &op, Class &opClass,
Class *genericAdaptorBase, StringRef sizeAttrInit,
StringRef rangeType, StringRef rangeElementType,
StringRef rangeBeginCall, StringRef rangeSizeCall,
StringRef getOperandCallPattern) {
const int numOperands = op.getNumOperands();
const int numVariadicOperands = op.getNumVariableLengthOperands();
const int numNormalOperands = numOperands - numVariadicOperands;
const auto *sameVariadicSize =
op.getTrait("::mlir::OpTrait::SameVariadicOperandSize");
const auto *attrSizedOperands =
op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");
if (numVariadicOperands > 1 && !sameVariadicSize && !attrSizedOperands) {
PrintFatalError(op.getLoc(), "op has multiple variadic operands but no "
"specification over their sizes");
}
if (numVariadicOperands < 2 && attrSizedOperands) {
PrintFatalError(op.getLoc(), "op must have at least two variadic operands "
"to use 'AttrSizedOperandSegments' trait");
}
if (attrSizedOperands && sameVariadicSize) {
PrintFatalError(op.getLoc(),
"op cannot have both 'AttrSizedOperandSegments' and "
"'SameVariadicOperandSize' traits");
}
bool isGenericAdaptorBase = genericAdaptorBase != nullptr;
generateValueRangeStartAndEnd(
isGenericAdaptorBase ? *genericAdaptorBase : opClass,
isGenericAdaptorBase,
"getODSOperandIndexAndLength", numVariadicOperands,
numNormalOperands, rangeSizeCall, attrSizedOperands, sizeAttrInit,
const_cast<Operator &>(op).getOperands());
if (isGenericAdaptorBase) {
Method *method = opClass.addInlineMethod(
"std::pair<unsigned, unsigned>", "getODSOperandIndexAndLength",
MethodParameter("unsigned", "index"));
ERROR_IF_PRUNED(method, "getODSOperandIndexAndLength", op);
MethodBody &body = method->body();
body.indent() << formatv(
"return Base::getODSOperandIndexAndLength(index, {0});", rangeSizeCall);
}
auto *m = opClass.addInlineMethod(rangeType, "getODSOperands",
MethodParameter("unsigned", "index"));
ERROR_IF_PRUNED(m, "getODSOperands", op);
auto &body = m->body();
body << formatv(valueRangeReturnCode, rangeBeginCall,
"getODSOperandIndexAndLength(index)");
for (int i = 0; i != numOperands; ++i) {
const auto &operand = op.getOperand(i);
if (operand.name.empty())
continue;
std::string name = op.getGetterName(operand.name);
if (operand.isOptional()) {
m = opClass.addInlineMethod(isGenericAdaptorBase
? rangeElementType
: generateTypeForGetter(operand),
name);
ERROR_IF_PRUNED(m, name, op);
m->body().indent() << formatv("auto operands = getODSOperands({0});\n"
"return operands.empty() ? {1}{{} : ",
i, m->getReturnType());
if (!isGenericAdaptorBase)
m->body() << llvm::formatv("::llvm::cast<{0}>", m->getReturnType());
m->body() << "(*operands.begin());";
} else if (operand.isVariadicOfVariadic()) {
std::string segmentAttr = op.getGetterName(
operand.constraint.getVariadicOfVariadicSegmentSizeAttr());
if (genericAdaptorBase) {
m = opClass.addMethod("::llvm::SmallVector<" + rangeType + ">", name);
ERROR_IF_PRUNED(m, name, op);
m->body() << llvm::formatv(variadicOfVariadicAdaptorCalcCode,
segmentAttr, i, rangeType);
continue;
}
m = opClass.addInlineMethod("::mlir::OperandRangeRange", name);
ERROR_IF_PRUNED(m, name, op);
m->body() << " return getODSOperands(" << i << ").split(" << segmentAttr
<< "Attr());";
} else if (operand.isVariadic()) {
m = opClass.addInlineMethod(rangeType, name);
ERROR_IF_PRUNED(m, name, op);
m->body() << " return getODSOperands(" << i << ");";
} else {
m = opClass.addInlineMethod(isGenericAdaptorBase
? rangeElementType
: generateTypeForGetter(operand),
name);
ERROR_IF_PRUNED(m, name, op);
m->body().indent() << "return ";
if (!isGenericAdaptorBase)
m->body() << llvm::formatv("::llvm::cast<{0}>", m->getReturnType());
m->body() << llvm::formatv("(*getODSOperands({0}).begin());", i);
}
}
}
void OpEmitter::genNamedOperandGetters() {
std::string attrSizeInitCode;
if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
if (op.getDialect().usePropertiesForAttributes())
attrSizeInitCode = formatv(adapterSegmentSizeAttrInitCodeProperties,
"getProperties().operandSegmentSizes");
else
attrSizeInitCode = formatv(opSegmentSizeAttrInitCode,
emitHelper.getAttr(operandSegmentAttrName));
}
generateNamedOperandGetters(
op, opClass,
nullptr,
attrSizeInitCode,
"::mlir::Operation::operand_range",
"::mlir::Value",
"getOperation()->operand_begin()",
"getOperation()->getNumOperands()",
"getOperation()->getOperand({0})");
}
void OpEmitter::genNamedOperandSetters() {
auto *attrSizedOperands =
op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");
for (int i = 0, e = op.getNumOperands(); i != e; ++i) {
const auto &operand = op.getOperand(i);
if (operand.name.empty())
continue;
std::string name = op.getGetterName(operand.name);
StringRef returnType;
if (operand.isVariadicOfVariadic()) {
returnType = "::mlir::MutableOperandRangeRange";
} else if (operand.isVariableLength()) {
returnType = "::mlir::MutableOperandRange";
} else {
returnType = "::mlir::OpOperand &";
}
bool isVariadicOperand =
operand.isVariadicOfVariadic() || operand.isVariableLength();
auto *m = opClass.addMethod(returnType, name + "Mutable",
isVariadicOperand ? Method::Properties::None
: Method::Properties::Inline);
ERROR_IF_PRUNED(m, name, op);
auto &body = m->body();
body << " auto range = getODSOperandIndexAndLength(" << i << ");\n";
if (!isVariadicOperand) {
body << " return getOperation()->getOpOperand(range.first);\n";
continue;
}
body << " auto mutableRange = "
"::mlir::MutableOperandRange(getOperation(), "
"range.first, range.second";
if (attrSizedOperands) {
if (emitHelper.hasProperties())
body << formatv(", ::mlir::MutableOperandRange::OperandSegment({0}u, "
"{{getOperandSegmentSizesAttrName(), "
"::mlir::DenseI32ArrayAttr::get(getContext(), "
"getProperties().operandSegmentSizes)})",
i);
else
body << formatv(
", ::mlir::MutableOperandRange::OperandSegment({0}u, *{1})", i,
emitHelper.getAttr(operandSegmentAttrName, true));
}
body << ");\n";
if (operand.isVariadicOfVariadic()) {
body << " return "
"mutableRange.split(*(*this)->getAttrDictionary().getNamed("
<< op.getGetterName(
operand.constraint.getVariadicOfVariadicSegmentSizeAttr())
<< "AttrName()));\n";
} else {
body << " return mutableRange;\n";
}
}
}
void OpEmitter::genNamedResultGetters() {
const int numResults = op.getNumResults();
const int numVariadicResults = op.getNumVariableLengthResults();
const int numNormalResults = numResults - numVariadicResults;
const auto *sameVariadicSize =
op.getTrait("::mlir::OpTrait::SameVariadicResultSize");
const auto *attrSizedResults =
op.getTrait("::mlir::OpTrait::AttrSizedResultSegments");
if (numVariadicResults > 1 && !sameVariadicSize && !attrSizedResults) {
PrintFatalError(op.getLoc(), "op has multiple variadic results but no "
"specification over their sizes");
}
if (numVariadicResults < 2 && attrSizedResults) {
PrintFatalError(op.getLoc(), "op must have at least two variadic results "
"to use 'AttrSizedResultSegments' trait");
}
if (attrSizedResults && sameVariadicSize) {
PrintFatalError(op.getLoc(),
"op cannot have both 'AttrSizedResultSegments' and "
"'SameVariadicResultSize' traits");
}
std::string attrSizeInitCode;
if (attrSizedResults) {
if (op.getDialect().usePropertiesForAttributes())
attrSizeInitCode = formatv(adapterSegmentSizeAttrInitCodeProperties,
"getProperties().resultSegmentSizes");
else
attrSizeInitCode = formatv(opSegmentSizeAttrInitCode,
emitHelper.getAttr(resultSegmentAttrName));
}
generateValueRangeStartAndEnd(
opClass, false, "getODSResultIndexAndLength",
numVariadicResults, numNormalResults, "getOperation()->getNumResults()",
attrSizedResults, attrSizeInitCode, op.getResults());
auto *m = opClass.addInlineMethod("::mlir::Operation::result_range",
"getODSResults",
MethodParameter("unsigned", "index"));
ERROR_IF_PRUNED(m, "getODSResults", op);
m->body() << formatv(valueRangeReturnCode, "getOperation()->result_begin()",
"getODSResultIndexAndLength(index)");
for (int i = 0; i != numResults; ++i) {
const auto &result = op.getResult(i);
if (result.name.empty())
continue;
std::string name = op.getGetterName(result.name);
if (result.isOptional()) {
m = opClass.addInlineMethod(generateTypeForGetter(result), name);
ERROR_IF_PRUNED(m, name, op);
m->body() << " auto results = getODSResults(" << i << ");\n"
<< llvm::formatv(" return results.empty()"
" ? {0}()"
" : ::llvm::cast<{0}>(*results.begin());",
m->getReturnType());
} else if (result.isVariadic()) {
m = opClass.addInlineMethod("::mlir::Operation::result_range", name);
ERROR_IF_PRUNED(m, name, op);
m->body() << " return getODSResults(" << i << ");";
} else {
m = opClass.addInlineMethod(generateTypeForGetter(result), name);
ERROR_IF_PRUNED(m, name, op);
m->body() << llvm::formatv(
" return ::llvm::cast<{0}>(*getODSResults({1}).begin());",
m->getReturnType(), i);
}
}
}
void OpEmitter::genNamedRegionGetters() {
unsigned numRegions = op.getNumRegions();
for (unsigned i = 0; i < numRegions; ++i) {
const auto ®ion = op.getRegion(i);
if (region.name.empty())
continue;
std::string name = op.getGetterName(region.name);
if (region.isVariadic()) {
auto *m = opClass.addInlineMethod(
"::mlir::MutableArrayRef<::mlir::Region>", name);
ERROR_IF_PRUNED(m, name, op);
m->body() << formatv(" return (*this)->getRegions().drop_front({0});",
i);
continue;
}
auto *m = opClass.addInlineMethod("::mlir::Region &", name);
ERROR_IF_PRUNED(m, name, op);
m->body() << formatv(" return (*this)->getRegion({0});", i);
}
}
void OpEmitter::genNamedSuccessorGetters() {
unsigned numSuccessors = op.getNumSuccessors();
for (unsigned i = 0; i < numSuccessors; ++i) {
const NamedSuccessor &successor = op.getSuccessor(i);
if (successor.name.empty())
continue;
std::string name = op.getGetterName(successor.name);
if (successor.isVariadic()) {
auto *m = opClass.addInlineMethod("::mlir::SuccessorRange", name);
ERROR_IF_PRUNED(m, name, op);
m->body() << formatv(
" return {std::next((*this)->successor_begin(), {0}), "
"(*this)->successor_end()};",
i);
continue;
}
auto *m = opClass.addInlineMethod("::mlir::Block *", name);
ERROR_IF_PRUNED(m, name, op);
m->body() << formatv(" return (*this)->getSuccessor({0});", i);
}
}
static bool canGenerateUnwrappedBuilder(const Operator &op) {
if (op.getNumNativeAttributes() == 0)
return false;
bool canGenerate = false;
for (int i = 0, e = op.getNumNativeAttributes(); i < e; ++i) {
const NamedAttribute &namedAttr = op.getAttribute(i);
if (canUseUnwrappedRawValue(namedAttr.attr)) {
canGenerate = true;
break;
}
}
return canGenerate;
}
static bool canInferType(const Operator &op) {
return op.getTrait("::mlir::InferTypeOpInterface::Trait");
}
void OpEmitter::genSeparateArgParamBuilder() {
SmallVector<AttrParamKind, 2> attrBuilderType;
attrBuilderType.push_back(AttrParamKind::WrappedAttr);
if (canGenerateUnwrappedBuilder(op))
attrBuilderType.push_back(AttrParamKind::UnwrappedValue);
auto emit = [&](AttrParamKind attrType, TypeParamKind paramKind,
bool inferType) {
SmallVector<MethodParameter> paramList;
SmallVector<std::string, 4> resultNames;
llvm::StringSet<> inferredAttributes;
buildParamList(paramList, inferredAttributes, resultNames, paramKind,
attrType);
auto *m = opClass.addStaticMethod("void", "build", std::move(paramList));
if (!m)
return;
auto &body = m->body();
genCodeForAddingArgAndRegionForBuilder(body, inferredAttributes,
attrType ==
AttrParamKind::UnwrappedValue);
if (inferType) {
body << formatv(R"(
::llvm::SmallVector<::mlir::Type, 2> inferredReturnTypes;
if (::mlir::succeeded({0}::inferReturnTypes(odsBuilder.getContext(),
{1}.location, {1}.operands,
{1}.attributes.getDictionary({1}.getContext()),
{1}.getRawProperties(),
{1}.regions, inferredReturnTypes)))
{1}.addTypes(inferredReturnTypes);
else
::llvm::report_fatal_error("Failed to infer result type(s).");)",
opClass.getClassName(), builderOpState);
return;
}
switch (paramKind) {
case TypeParamKind::None:
return;
case TypeParamKind::Separate:
for (int i = 0, e = op.getNumResults(); i < e; ++i) {
if (op.getResult(i).isOptional())
body << " if (" << resultNames[i] << ")\n ";
body << " " << builderOpState << ".addTypes(" << resultNames[i]
<< ");\n";
}
if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {
if (op.getDialect().usePropertiesForAttributes()) {
body << " ::llvm::copy(::llvm::ArrayRef<int32_t>({";
} else {
std::string getterName = op.getGetterName(resultSegmentAttrName);
body << " " << builderOpState << ".addAttribute(" << getterName
<< "AttrName(" << builderOpState << ".name), "
<< "odsBuilder.getDenseI32ArrayAttr({";
}
interleaveComma(
llvm::seq<int>(0, op.getNumResults()), body, [&](int i) {
const NamedTypeConstraint &result = op.getResult(i);
if (!result.isVariableLength()) {
body << "1";
} else if (result.isOptional()) {
body << "(" << resultNames[i] << " ? 1 : 0)";
} else {
assert(result.isVariadic());
body << "static_cast<int32_t>(" << resultNames[i] << ".size())";
}
});
if (op.getDialect().usePropertiesForAttributes()) {
body << "}), " << builderOpState
<< ".getOrAddProperties<Properties>()."
"resultSegmentSizes.begin());\n";
} else {
body << "}));\n";
}
}
return;
case TypeParamKind::Collective: {
int numResults = op.getNumResults();
int numVariadicResults = op.getNumVariableLengthResults();
int numNonVariadicResults = numResults - numVariadicResults;
bool hasVariadicResult = numVariadicResults != 0;
if (!hasVariadicResult || numNonVariadicResults != 0)
body << " "
<< "assert(resultTypes.size() "
<< (hasVariadicResult ? ">=" : "==") << " "
<< numNonVariadicResults
<< "u && \"mismatched number of results\");\n";
body << " " << builderOpState << ".addTypes(resultTypes);\n";
}
return;
}
llvm_unreachable("unhandled TypeParamKind");
};
for (auto attrType : attrBuilderType) {
emit(attrType, TypeParamKind::Separate, false);
if (canInferType(op))
emit(attrType, TypeParamKind::None, true);
emit(attrType, TypeParamKind::Collective, false);
}
}
void OpEmitter::genUseOperandAsResultTypeCollectiveParamBuilder() {
int numResults = op.getNumResults();
SmallVector<MethodParameter> paramList;
paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");
paramList.emplace_back("::mlir::OperationState &", builderOpState);
paramList.emplace_back("::mlir::ValueRange", "operands");
StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";
paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",
"attributes", attributesDefaultValue);
if (op.getNumVariadicRegions())
paramList.emplace_back("unsigned", "numRegions");
auto *m = opClass.addStaticMethod("void", "build", std::move(paramList));
if (!m)
return;
auto &body = m->body();
body << " " << builderOpState << ".addOperands(operands);\n";
body << " " << builderOpState << ".addAttributes(attributes);\n";
if (int numRegions = op.getNumRegions()) {
body << llvm::formatv(
" for (unsigned i = 0; i != {0}; ++i)\n",
(op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));
body << " (void)" << builderOpState << ".addRegion();\n";
}
SmallVector<std::string, 2> resultTypes(numResults, "operands[0].getType()");
body << " " << builderOpState << ".addTypes({"
<< llvm::join(resultTypes, ", ") << "});\n\n";
}
void OpEmitter::genPopulateDefaultAttributes() {
if (llvm::all_of(op.getAttributes(), [](const NamedAttribute &named) {
return !named.attr.hasDefaultValue() || named.attr.isOptional();
}))
return;
if (emitHelper.hasProperties()) {
SmallVector<MethodParameter> paramList;
paramList.emplace_back("::mlir::OperationName", "opName");
paramList.emplace_back("Properties &", "properties");
auto *m =
opClass.addStaticMethod("void", "populateDefaultProperties", paramList);
ERROR_IF_PRUNED(m, "populateDefaultProperties", op);
auto &body = m->body();
body.indent();
body << "::mlir::Builder " << odsBuilder << "(opName.getContext());\n";
for (const NamedAttribute &namedAttr : op.getAttributes()) {
auto &attr = namedAttr.attr;
if (!attr.hasDefaultValue() || attr.isOptional())
continue;
StringRef name = namedAttr.name;
FmtContext fctx;
fctx.withBuilder(odsBuilder);
body << "if (!properties." << name << ")\n"
<< " properties." << name << " = "
<< std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,
tgfmt(attr.getDefaultValue(), &fctx)))
<< ";\n";
}
return;
}
SmallVector<MethodParameter> paramList;
paramList.emplace_back("const ::mlir::OperationName &", "opName");
paramList.emplace_back("::mlir::NamedAttrList &", "attributes");
auto *m = opClass.addStaticMethod("void", "populateDefaultAttrs", paramList);
ERROR_IF_PRUNED(m, "populateDefaultAttrs", op);
auto &body = m->body();
body.indent();
body << "auto attrNames = opName.getAttributeNames();\n";
body << "::mlir::Builder " << odsBuilder
<< "(attrNames.front().getContext());\n";
StringMap<int> attrIndex;
for (const auto &it : llvm::enumerate(emitHelper.getAttrMetadata())) {
attrIndex[it.value().first] = it.index();
}
for (const NamedAttribute &namedAttr : op.getAttributes()) {
auto &attr = namedAttr.attr;
if (!attr.hasDefaultValue() || attr.isOptional())
continue;
auto index = attrIndex[namedAttr.name];
body << "if (!attributes.get(attrNames[" << index << "])) {\n";
FmtContext fctx;
fctx.withBuilder(odsBuilder);
std::string defaultValue =
std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,
tgfmt(attr.getDefaultValue(), &fctx)));
body.indent() << formatv("attributes.append(attrNames[{0}], {1});\n", index,
defaultValue);
body.unindent() << "}\n";
}
}
void OpEmitter::genInferredTypeCollectiveParamBuilder() {
SmallVector<MethodParameter> paramList;
paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");
paramList.emplace_back("::mlir::OperationState &", builderOpState);
paramList.emplace_back("::mlir::ValueRange", "operands");
StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";
paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",
"attributes", attributesDefaultValue);
if (op.getNumVariadicRegions())
paramList.emplace_back("unsigned", "numRegions");
auto *m = opClass.addStaticMethod("void", "build", std::move(paramList));
if (!m)
return;
auto &body = m->body();
int numResults = op.getNumResults();
int numVariadicResults = op.getNumVariableLengthResults();
int numNonVariadicResults = numResults - numVariadicResults;
int numOperands = op.getNumOperands();
int numVariadicOperands = op.getNumVariableLengthOperands();
int numNonVariadicOperands = numOperands - numVariadicOperands;
if (numVariadicOperands == 0 || numNonVariadicOperands != 0)
body << " assert(operands.size()"
<< (numVariadicOperands != 0 ? " >= " : " == ")
<< numNonVariadicOperands
<< "u && \"mismatched number of parameters\");\n";
body << " " << builderOpState << ".addOperands(operands);\n";
body << " " << builderOpState << ".addAttributes(attributes);\n";
if (int numRegions = op.getNumRegions()) {
body << llvm::formatv(
" for (unsigned i = 0; i != {0}; ++i)\n",
(op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));
body << " (void)" << builderOpState << ".addRegion();\n";
}
if (emitHelper.hasProperties()) {
body << formatv(R"(
if (!attributes.empty()) {
::mlir::OpaqueProperties properties =
&{1}.getOrAddProperties<{0}::Properties>();
std::optional<::mlir::RegisteredOperationName> info =
{1}.name.getRegisteredInfo();
if (failed(info->setOpPropertiesFromAttribute({1}.name, properties,
{1}.attributes.getDictionary({1}.getContext()), nullptr)))
::llvm::report_fatal_error("Property conversion failed.");
})",
opClass.getClassName(), builderOpState);
}
body << formatv(R"(
::llvm::SmallVector<::mlir::Type, 2> inferredReturnTypes;
if (::mlir::succeeded({0}::inferReturnTypes(odsBuilder.getContext(),
{1}.location, operands,
{1}.attributes.getDictionary({1}.getContext()),
{1}.getRawProperties(),
{1}.regions, inferredReturnTypes))) {{)",
opClass.getClassName(), builderOpState);
if (numVariadicResults == 0 || numNonVariadicResults != 0)
body << "\n assert(inferredReturnTypes.size()"
<< (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults
<< "u && \"mismatched number of return types\");";
body << "\n " << builderOpState << ".addTypes(inferredReturnTypes);";
body << formatv(R"(
} else {{
::llvm::report_fatal_error("Failed to infer result type(s).");
})",
opClass.getClassName(), builderOpState);
}
void OpEmitter::genUseOperandAsResultTypeSeparateParamBuilder() {
auto emit = [&](AttrParamKind attrType) {
SmallVector<MethodParameter> paramList;
SmallVector<std::string, 4> resultNames;
llvm::StringSet<> inferredAttributes;
buildParamList(paramList, inferredAttributes, resultNames,
TypeParamKind::None, attrType);
auto *m = opClass.addStaticMethod("void", "build", std::move(paramList));
if (!m)
return;
auto &body = m->body();
genCodeForAddingArgAndRegionForBuilder(body, inferredAttributes,
attrType ==
AttrParamKind::UnwrappedValue);
auto numResults = op.getNumResults();
if (numResults == 0)
return;
const char *index = op.getOperand(0).isVariadic() ? ".front()" : "";
std::string resultType =
formatv("{0}{1}.getType()", getArgumentName(op, 0), index).str();
body << " " << builderOpState << ".addTypes({" << resultType;
for (int i = 1; i != numResults; ++i)
body << ", " << resultType;
body << "});\n\n";
};
emit(AttrParamKind::WrappedAttr);
if (canGenerateUnwrappedBuilder(op))
emit(AttrParamKind::UnwrappedValue);
}
void OpEmitter::genUseAttrAsResultTypeBuilder() {
SmallVector<MethodParameter> paramList;
paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");
paramList.emplace_back("::mlir::OperationState &", builderOpState);
paramList.emplace_back("::mlir::ValueRange", "operands");
paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",
"attributes", "{}");
auto *m = opClass.addStaticMethod("void", "build", std::move(paramList));
if (!m)
return;
auto &body = m->body();
std::string resultType;
const auto &namedAttr = op.getAttribute(0);
body << " auto attrName = " << op.getGetterName(namedAttr.name)
<< "AttrName(" << builderOpState
<< ".name);\n"
" for (auto attr : attributes) {\n"
" if (attr.getName() != attrName) continue;\n";
if (namedAttr.attr.isTypeAttr()) {
resultType = "::llvm::cast<::mlir::TypeAttr>(attr.getValue()).getValue()";
} else {
resultType = "::llvm::cast<::mlir::TypedAttr>(attr.getValue()).getType()";
}
body << " " << builderOpState << ".addOperands(operands);\n";
body << " " << builderOpState << ".addAttributes(attributes);\n";
SmallVector<std::string, 2> resultTypes(op.getNumResults(), resultType);
body << " " << builderOpState << ".addTypes({"
<< llvm::join(resultTypes, ", ") << "});\n";
body << " }\n";
}
static SmallVector<MethodParameter>
getBuilderSignature(const Builder &builder) {
ArrayRef<Builder::Parameter> params(builder.getParameters());
SmallVector<MethodParameter> arguments;
arguments.reserve(params.size() + 2);
arguments.emplace_back("::mlir::OpBuilder &", odsBuilder);
arguments.emplace_back("::mlir::OperationState &", builderOpState);
for (unsigned i = 0, e = params.size(); i < e; ++i) {
std::optional<StringRef> paramName = params[i].getName();
std::string name =
paramName ? paramName->str() : "odsArg" + std::to_string(i);
StringRef defaultValue;
if (std::optional<StringRef> defaultParamValue =
params[i].getDefaultValue())
defaultValue = *defaultParamValue;
arguments.emplace_back(params[i].getCppType(), std::move(name),
defaultValue);
}
return arguments;
}
void OpEmitter::genBuilder() {
for (const Builder &builder : op.getBuilders()) {
SmallVector<MethodParameter> arguments = getBuilderSignature(builder);
std::optional<StringRef> body = builder.getBody();
auto properties = body ? Method::Static : Method::StaticDeclaration;
auto *method =
opClass.addMethod("void", "build", properties, std::move(arguments));
if (body)
ERROR_IF_PRUNED(method, "build", op);
if (method)
method->setDeprecated(builder.getDeprecatedMessage());
FmtContext fctx;
fctx.withBuilder(odsBuilder);
fctx.addSubst("_state", builderOpState);
if (body)
method->body() << tgfmt(*body, &fctx);
}
if (op.skipDefaultBuilders())
return;
genSeparateArgParamBuilder();
genCollectiveParamBuilder();
if (op.getNumVariableLengthResults() == 0) {
if (op.getTrait("::mlir::OpTrait::SameOperandsAndResultType")) {
genUseOperandAsResultTypeSeparateParamBuilder();
genUseOperandAsResultTypeCollectiveParamBuilder();
}
if (op.getTrait("::mlir::OpTrait::FirstAttrDerivedResultType"))
genUseAttrAsResultTypeBuilder();
}
}
void OpEmitter::genCollectiveParamBuilder() {
int numResults = op.getNumResults();
int numVariadicResults = op.getNumVariableLengthResults();
int numNonVariadicResults = numResults - numVariadicResults;
int numOperands = op.getNumOperands();
int numVariadicOperands = op.getNumVariableLengthOperands();
int numNonVariadicOperands = numOperands - numVariadicOperands;
SmallVector<MethodParameter> paramList;
paramList.emplace_back("::mlir::OpBuilder &", "");
paramList.emplace_back("::mlir::OperationState &", builderOpState);
paramList.emplace_back("::mlir::TypeRange", "resultTypes");
paramList.emplace_back("::mlir::ValueRange", "operands");
StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";
paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",
"attributes", attributesDefaultValue);
if (op.getNumVariadicRegions())
paramList.emplace_back("unsigned", "numRegions");
auto *m = opClass.addStaticMethod("void", "build", std::move(paramList));
if (!m)
return;
auto &body = m->body();
if (numVariadicOperands == 0 || numNonVariadicOperands != 0)
body << " assert(operands.size()"
<< (numVariadicOperands != 0 ? " >= " : " == ")
<< numNonVariadicOperands
<< "u && \"mismatched number of parameters\");\n";
body << " " << builderOpState << ".addOperands(operands);\n";
body << " " << builderOpState << ".addAttributes(attributes);\n";
if (int numRegions = op.getNumRegions()) {
body << llvm::formatv(
" for (unsigned i = 0; i != {0}; ++i)\n",
(op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));
body << " (void)" << builderOpState << ".addRegion();\n";
}
if (numVariadicResults == 0 || numNonVariadicResults != 0)
body << " assert(resultTypes.size()"
<< (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults
<< "u && \"mismatched number of return types\");\n";
body << " " << builderOpState << ".addTypes(resultTypes);\n";
if (emitHelper.hasProperties()) {
body << formatv(R"(
if (!attributes.empty()) {
::mlir::OpaqueProperties properties =
&{1}.getOrAddProperties<{0}::Properties>();
std::optional<::mlir::RegisteredOperationName> info =
{1}.name.getRegisteredInfo();
if (failed(info->setOpPropertiesFromAttribute({1}.name, properties,
{1}.attributes.getDictionary({1}.getContext()), nullptr)))
::llvm::report_fatal_error("Property conversion failed.");
})",
opClass.getClassName(), builderOpState);
}
if (canInferType(op) && op.getNumSuccessors() == 0)
genInferredTypeCollectiveParamBuilder();
}
void OpEmitter::buildParamList(SmallVectorImpl<MethodParameter> ¶mList,
llvm::StringSet<> &inferredAttributes,
SmallVectorImpl<std::string> &resultTypeNames,
TypeParamKind typeParamKind,
AttrParamKind attrParamKind) {
resultTypeNames.clear();
auto numResults = op.getNumResults();
resultTypeNames.reserve(numResults);
paramList.emplace_back("::mlir::OpBuilder &", odsBuilder);
paramList.emplace_back("::mlir::OperationState &", builderOpState);
switch (typeParamKind) {
case TypeParamKind::None:
break;
case TypeParamKind::Separate: {
for (int i = 0; i < numResults; ++i) {
const auto &result = op.getResult(i);
std::string resultName = std::string(result.name);
if (resultName.empty())
resultName = std::string(formatv("resultType{0}", i));
StringRef type =
result.isVariadic() ? "::mlir::TypeRange" : "::mlir::Type";
paramList.emplace_back(type, resultName, result.isOptional());
resultTypeNames.emplace_back(std::move(resultName));
}
} break;
case TypeParamKind::Collective: {
paramList.emplace_back("::mlir::TypeRange", "resultTypes");
resultTypeNames.push_back("resultTypes");
} break;
}
int defaultValuedAttrStartIndex = op.getNumArgs();
bool hasTrailingParams = op.getNumSuccessors() || op.getNumVariadicRegions();
if (!hasTrailingParams) {
for (int i = op.getNumArgs() - 1; i >= 0; --i) {
auto *namedAttr =
llvm::dyn_cast_if_present<tblgen::NamedAttribute *>(op.getArg(i));
if (!namedAttr)
break;
Attribute attr = namedAttr->attr;
if (!attr.hasDefaultValue() && !attr.isDerivedAttr())
break;
StringRef retType = namedAttr->attr.getReturnType();
if (retType == "::llvm::APInt" || retType == "::llvm::APFloat")
break;
defaultValuedAttrStartIndex = i;
}
}
if (defaultValuedAttrStartIndex < op.getNumArgs()) {
auto *namedAttr =
cast<NamedAttribute *>(op.getArg(defaultValuedAttrStartIndex));
Attribute attr = namedAttr->attr;
if ((attrParamKind == AttrParamKind::WrappedAttr &&
canUseUnwrappedRawValue(attr)) ||
(attrParamKind == AttrParamKind::UnwrappedValue &&
!canUseUnwrappedRawValue(attr)))
++defaultValuedAttrStartIndex;
}
for (const NamedTypeConstraint &operand : op.getOperands()) {
if (operand.isVariadicOfVariadic()) {
inferredAttributes.insert(
operand.constraint.getVariadicOfVariadicSegmentSizeAttr());
}
}
for (int i = 0, e = op.getNumArgs(), numOperands = 0; i < e; ++i) {
Argument arg = op.getArg(i);
if (const auto *operand =
llvm::dyn_cast_if_present<NamedTypeConstraint *>(arg)) {
StringRef type;
if (operand->isVariadicOfVariadic())
type = "::llvm::ArrayRef<::mlir::ValueRange>";
else if (operand->isVariadic())
type = "::mlir::ValueRange";
else
type = "::mlir::Value";
paramList.emplace_back(type, getArgumentName(op, numOperands++),
operand->isOptional());
continue;
}
if (llvm::isa_and_present<NamedProperty *>(arg)) {
continue;
}
const NamedAttribute &namedAttr = *arg.get<NamedAttribute *>();
const Attribute &attr = namedAttr.attr;
if (inferredAttributes.contains(namedAttr.name))
continue;
StringRef type;
switch (attrParamKind) {
case AttrParamKind::WrappedAttr:
type = attr.getStorageType();
break;
case AttrParamKind::UnwrappedValue:
if (canUseUnwrappedRawValue(attr))
type = attr.getReturnType();
else
type = attr.getStorageType();
break;
}
std::string defaultValue;
if (i >= defaultValuedAttrStartIndex) {
if (attrParamKind == AttrParamKind::UnwrappedValue &&
canUseUnwrappedRawValue(attr))
defaultValue += attr.getDefaultValue();
else
defaultValue += "nullptr";
}
paramList.emplace_back(type, namedAttr.name, StringRef(defaultValue),
attr.isOptional());
}
for (const NamedSuccessor &succ : op.getSuccessors()) {
StringRef type =
succ.isVariadic() ? "::mlir::BlockRange" : "::mlir::Block *";
paramList.emplace_back(type, succ.name);
}
for (const NamedRegion ®ion : op.getRegions())
if (region.isVariadic())
paramList.emplace_back("unsigned",
llvm::formatv("{0}Count", region.name).str());
}
void OpEmitter::genCodeForAddingArgAndRegionForBuilder(
MethodBody &body, llvm::StringSet<> &inferredAttributes,
bool isRawValueAttr) {
for (int i = 0, e = op.getNumOperands(); i < e; ++i) {
std::string argName = getArgumentName(op, i);
const NamedTypeConstraint &operand = op.getOperand(i);
if (operand.constraint.isVariadicOfVariadic()) {
body << " for (::mlir::ValueRange range : " << argName << ")\n "
<< builderOpState << ".addOperands(range);\n";
body << " {\n"
<< " ::llvm::SmallVector<int32_t> rangeSegments;\n"
<< " for (::mlir::ValueRange range : " << argName << ")\n"
<< " rangeSegments.push_back(range.size());\n"
<< " auto rangeAttr = " << odsBuilder
<< ".getDenseI32ArrayAttr(rangeSegments);\n";
if (op.getDialect().usePropertiesForAttributes()) {
body << " " << builderOpState << ".getOrAddProperties<Properties>()."
<< operand.constraint.getVariadicOfVariadicSegmentSizeAttr()
<< " = rangeAttr;";
} else {
body << " " << builderOpState << ".addAttribute("
<< op.getGetterName(
operand.constraint.getVariadicOfVariadicSegmentSizeAttr())
<< "AttrName(" << builderOpState << ".name), rangeAttr);";
}
body << " }\n";
continue;
}
if (operand.isOptional())
body << " if (" << argName << ")\n ";
body << " " << builderOpState << ".addOperands(" << argName << ");\n";
}
auto emitSegment = [&]() {
interleaveComma(llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {
const NamedTypeConstraint &operand = op.getOperand(i);
if (!operand.isVariableLength()) {
body << "1";
return;
}
std::string operandName = getArgumentName(op, i);
if (operand.isOptional()) {
body << "(" << operandName << " ? 1 : 0)";
} else if (operand.isVariadicOfVariadic()) {
body << llvm::formatv(
"static_cast<int32_t>(std::accumulate({0}.begin(), {0}.end(), 0, "
"[](int32_t curSum, ::mlir::ValueRange range) {{ return curSum + "
"static_cast<int32_t>(range.size()); }))",
operandName);
} else {
body << "static_cast<int32_t>(" << getArgumentName(op, i) << ".size())";
}
});
};
if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
std::string sizes = op.getGetterName(operandSegmentAttrName);
if (op.getDialect().usePropertiesForAttributes()) {
body << " ::llvm::copy(::llvm::ArrayRef<int32_t>({";
emitSegment();
body << "}), " << builderOpState
<< ".getOrAddProperties<Properties>()."
"operandSegmentSizes.begin());\n";
} else {
body << " " << builderOpState << ".addAttribute(" << sizes << "AttrName("
<< builderOpState << ".name), "
<< "odsBuilder.getDenseI32ArrayAttr({";
emitSegment();
body << "}));\n";
}
}
for (const auto &namedAttr : op.getAttributes()) {
auto &attr = namedAttr.attr;
if (attr.isDerivedAttr() || inferredAttributes.contains(namedAttr.name))
continue;
bool emitNotNullCheck =
(attr.isOptional() && !attr.hasDefaultValue()) ||
(attr.hasDefaultValue() && !isRawValueAttr) ||
(isRawValueAttr && attr.getAttrDefName() == "UnitAttr");
if (emitNotNullCheck)
body.indent() << formatv("if ({0}) ", namedAttr.name) << "{\n";
if (isRawValueAttr && canUseUnwrappedRawValue(attr)) {
FmtContext fctx;
fctx.withBuilder("odsBuilder");
if (op.getDialect().usePropertiesForAttributes()) {
body << formatv(" {0}.getOrAddProperties<Properties>().{1} = {2};\n",
builderOpState, namedAttr.name,
constBuildAttrFromParam(attr, fctx, namedAttr.name));
} else {
body << formatv(" {0}.addAttribute({1}AttrName({0}.name), {2});\n",
builderOpState, op.getGetterName(namedAttr.name),
constBuildAttrFromParam(attr, fctx, namedAttr.name));
}
} else {
if (op.getDialect().usePropertiesForAttributes()) {
body << formatv(" {0}.getOrAddProperties<Properties>().{1} = {1};\n",
builderOpState, namedAttr.name);
} else {
body << formatv(" {0}.addAttribute({1}AttrName({0}.name), {2});\n",
builderOpState, op.getGetterName(namedAttr.name),
namedAttr.name);
}
}
if (emitNotNullCheck)
body.unindent() << " }\n";
}
for (const NamedRegion ®ion : op.getRegions()) {
if (region.isVariadic())
body << formatv(" for (unsigned i = 0; i < {0}Count; ++i)\n ",
region.name);
body << " (void)" << builderOpState << ".addRegion();\n";
}
for (const NamedSuccessor &namedSuccessor : op.getSuccessors()) {
body << formatv(" {0}.addSuccessors({1});\n", builderOpState,
namedSuccessor.name);
}
}
void OpEmitter::genCanonicalizerDecls() {
bool hasCanonicalizeMethod = def.getValueAsBit("hasCanonicalizeMethod");
if (hasCanonicalizeMethod) {
SmallVector<MethodParameter> paramList;
paramList.emplace_back(op.getCppClassName(), "op");
paramList.emplace_back("::mlir::PatternRewriter &", "rewriter");
auto *m = opClass.declareStaticMethod("::llvm::LogicalResult",
"canonicalize", std::move(paramList));
ERROR_IF_PRUNED(m, "canonicalize", op);
}
bool hasCanonicalizer = def.getValueAsBit("hasCanonicalizer");
if (!hasCanonicalizeMethod && !hasCanonicalizer)
return;
bool hasBody = hasCanonicalizeMethod && !hasCanonicalizer;
SmallVector<MethodParameter> paramList;
paramList.emplace_back("::mlir::RewritePatternSet &", "results");
paramList.emplace_back("::mlir::MLIRContext *", "context");
auto kind = hasBody ? Method::Static : Method::StaticDeclaration;
auto *method = opClass.addMethod("void", "getCanonicalizationPatterns", kind,
std::move(paramList));
if (hasBody) {
ERROR_IF_PRUNED(method, "getCanonicalizationPatterns", op);
method->body() << " results.add(canonicalize);\n";
}
}
void OpEmitter::genFolderDecls() {
if (!op.hasFolder())
return;
SmallVector<MethodParameter> paramList;
paramList.emplace_back("FoldAdaptor", "adaptor");
StringRef retType;
bool hasSingleResult =
op.getNumResults() == 1 && op.getNumVariableLengthResults() == 0;
if (hasSingleResult) {
retType = "::mlir::OpFoldResult";
} else {
paramList.emplace_back("::llvm::SmallVectorImpl<::mlir::OpFoldResult> &",
"results");
retType = "::llvm::LogicalResult";
}
auto *m = opClass.declareMethod(retType, "fold", std::move(paramList));
ERROR_IF_PRUNED(m, "fold", op);
}
void OpEmitter::genOpInterfaceMethods(const tblgen::InterfaceTrait *opTrait) {
Interface interface = opTrait->getInterface();
auto alwaysDeclaredMethodsVec = opTrait->getAlwaysDeclaredMethods();
llvm::StringSet<> alwaysDeclaredMethods;
alwaysDeclaredMethods.insert(alwaysDeclaredMethodsVec.begin(),
alwaysDeclaredMethodsVec.end());
for (const InterfaceMethod &method : interface.getMethods()) {
if (method.getBody())
continue;
if (method.getDefaultImplementation() &&
!alwaysDeclaredMethods.count(method.getName()))
continue;
(void)genOpInterfaceMethod(method);
}
}
Method *OpEmitter::genOpInterfaceMethod(const InterfaceMethod &method,
bool declaration) {
SmallVector<MethodParameter> paramList;
for (const InterfaceMethod::Argument &arg : method.getArguments())
paramList.emplace_back(arg.type, arg.name);
auto props = (method.isStatic() ? Method::Static : Method::None) |
(declaration ? Method::Declaration : Method::None);
return opClass.addMethod(method.getReturnType(), method.getName(), props,
std::move(paramList));
}
void OpEmitter::genOpInterfaceMethods() {
for (const auto &trait : op.getTraits()) {
if (const auto *opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait))
if (opTrait->shouldDeclareMethods())
genOpInterfaceMethods(opTrait);
}
}
void OpEmitter::genSideEffectInterfaceMethods() {
enum EffectKind { Operand, Result, Symbol, Static };
struct EffectLocation {
SideEffect effect;
unsigned index;
unsigned kind;
};
StringMap<SmallVector<EffectLocation, 1>> interfaceEffects;
auto resolveDecorators = [&](Operator::var_decorator_range decorators,
unsigned index, unsigned kind) {
for (auto decorator : decorators)
if (SideEffect *effect = dyn_cast<SideEffect>(&decorator)) {
opClass.addTrait(effect->getInterfaceTrait());
interfaceEffects[effect->getBaseEffectName()].push_back(
EffectLocation{*effect, index, kind});
}
};
for (const auto &trait : op.getTraits()) {
const auto *opTrait = dyn_cast<tblgen::SideEffectTrait>(&trait);
if (!opTrait)
continue;
auto &effects = interfaceEffects[opTrait->getBaseEffectName()];
for (auto decorator : opTrait->getEffects())
effects.push_back(EffectLocation{cast<SideEffect>(decorator),
0, EffectKind::Static});
}
for (unsigned i = 0, operandIt = 0, e = op.getNumArgs(); i != e; ++i) {
Argument arg = op.getArg(i);
if (arg.is<NamedTypeConstraint *>()) {
resolveDecorators(op.getArgDecorators(i), operandIt, EffectKind::Operand);
++operandIt;
continue;
}
if (arg.is<NamedProperty *>())
continue;
const NamedAttribute *attr = arg.get<NamedAttribute *>();
if (attr->attr.getBaseAttr().isSymbolRefAttr())
resolveDecorators(op.getArgDecorators(i), i, EffectKind::Symbol);
}
for (unsigned i = 0, e = op.getNumResults(); i != e; ++i)
resolveDecorators(op.getResultDecorators(i), i, EffectKind::Result);
const char *addEffectCode =
" effects.emplace_back({0}::get(), {1}{2}, {3}, {4}::get());\n";
for (auto &it : interfaceEffects) {
std::string type = llvm::formatv("::llvm::SmallVectorImpl<::mlir::"
"SideEffects::EffectInstance<{0}>> &",
it.first())
.str();
auto *getEffects = opClass.addMethod("void", "getEffects",
MethodParameter(type, "effects"));
ERROR_IF_PRUNED(getEffects, "getEffects", op);
auto &body = getEffects->body();
for (auto &location : it.second) {
StringRef effect = location.effect.getName();
StringRef resource = location.effect.getResource();
int stage = (int)location.effect.getStage();
bool effectOnFullRegion = (int)location.effect.getEffectOnfullRegion();
if (location.kind == EffectKind::Static) {
body << llvm::formatv(addEffectCode, effect, "", stage,
effectOnFullRegion, resource)
.str();
} else if (location.kind == EffectKind::Symbol) {
const auto *attr = op.getArg(location.index).get<NamedAttribute *>();
std::string argName = op.getGetterName(attr->name);
if (attr->attr.isOptional()) {
body << " if (auto symbolRef = " << argName << "Attr())\n "
<< llvm::formatv(addEffectCode, effect, "symbolRef, ", stage,
effectOnFullRegion, resource)
.str();
} else {
body << llvm::formatv(addEffectCode, effect, argName + "Attr(), ",
stage, effectOnFullRegion, resource)
.str();
}
} else {
body << " {\n auto valueRange = getODS"
<< (location.kind == EffectKind::Operand ? "Operand" : "Result")
<< "IndexAndLength(" << location.index << ");\n"
<< " for (unsigned idx = valueRange.first; idx < "
"valueRange.first"
<< " + valueRange.second; idx++) {\n "
<< llvm::formatv(addEffectCode, effect,
(location.kind == EffectKind::Operand
? "&getOperation()->getOpOperand(idx), "
: "getOperation()->getOpResult(idx), "),
stage, effectOnFullRegion, resource)
<< " }\n }\n";
}
}
}
}
void OpEmitter::genTypeInterfaceMethods() {
if (!op.allResultTypesKnown())
return;
const auto *trait =
cast<InterfaceTrait>(op.getTrait("::mlir::InferTypeOpInterface::Trait"));
Interface interface = trait->getInterface();
Method *method = [&]() -> Method * {
for (const InterfaceMethod &interfaceMethod : interface.getMethods()) {
if (interfaceMethod.getName() == "inferReturnTypes") {
return genOpInterfaceMethod(interfaceMethod, false);
}
}
assert(0 && "unable to find inferReturnTypes interface method");
return nullptr;
}();
ERROR_IF_PRUNED(method, "inferReturnTypes", op);
auto &body = method->body();
body << " inferredReturnTypes.resize(" << op.getNumResults() << ");\n";
FmtContext fctx;
fctx.withBuilder("odsBuilder");
fctx.addSubst("_ctxt", "context");
body << " ::mlir::Builder odsBuilder(context);\n";
SmallVector<int> constructedIndices(op.getNumResults(), -1);
int inferredTypeIdx = 0;
for (int numResults = op.getNumResults(); inferredTypeIdx != numResults;) {
for (int i = 0, e = op.getNumResults(); i != e; ++i) {
if (constructedIndices[i] >= 0)
continue;
const InferredResultType &infer = op.getInferredResultType(i);
std::string typeStr;
if (infer.isArg()) {
auto arg = op.getArgToOperandOrAttribute(infer.getIndex());
if (arg.kind() == Operator::OperandOrAttribute::Kind::Operand) {
typeStr = ("operands[" + Twine(arg.operandOrAttributeIndex()) +
"].getType()")
.str();
} else {
auto *attr =
op.getArg(arg.operandOrAttributeIndex()).get<NamedAttribute *>();
body << " ::mlir::TypedAttr odsInferredTypeAttr" << inferredTypeIdx
<< " = ";
if (op.getDialect().usePropertiesForAttributes()) {
body << "(properties ? properties.as<Properties *>()->"
<< attr->name
<< " : "
"::llvm::dyn_cast_or_null<::mlir::TypedAttr>(attributes."
"get(\"" +
attr->name + "\")));\n";
} else {
body << "::llvm::dyn_cast_or_null<::mlir::TypedAttr>(attributes."
"get(\"" +
attr->name + "\"));\n";
}
body << " if (!odsInferredTypeAttr" << inferredTypeIdx
<< ") return ::mlir::failure();\n";
typeStr =
("odsInferredTypeAttr" + Twine(inferredTypeIdx) + ".getType()")
.str();
}
} else if (std::optional<StringRef> builder =
op.getResult(infer.getResultIndex())
.constraint.getBuilderCall()) {
typeStr = tgfmt(*builder, &fctx).str();
} else if (int index = constructedIndices[infer.getResultIndex()];
index >= 0) {
typeStr = ("odsInferredType" + Twine(index)).str();
} else {
continue;
}
body << " ::mlir::Type odsInferredType" << inferredTypeIdx++ << " = "
<< tgfmt(infer.getTransformer(), &fctx.withSelf(typeStr)) << ";\n";
constructedIndices[i] = inferredTypeIdx - 1;
}
}
for (auto [i, index] : llvm::enumerate(constructedIndices))
body << " inferredReturnTypes[" << i << "] = odsInferredType" << index
<< ";\n";
body << " return ::mlir::success();";
}
void OpEmitter::genParser() {
if (hasStringAttribute(def, "assemblyFormat"))
return;
if (!def.getValueAsBit("hasCustomAssemblyFormat"))
return;
SmallVector<MethodParameter> paramList;
paramList.emplace_back("::mlir::OpAsmParser &", "parser");
paramList.emplace_back("::mlir::OperationState &", "result");
auto *method = opClass.declareStaticMethod("::mlir::ParseResult", "parse",
std::move(paramList));
ERROR_IF_PRUNED(method, "parse", op);
}
void OpEmitter::genPrinter() {
if (hasStringAttribute(def, "assemblyFormat"))
return;
if (!def.getValueAsBit("hasCustomAssemblyFormat"))
return;
auto *method = opClass.declareMethod(
"void", "print", MethodParameter("::mlir::OpAsmPrinter &", "p"));
ERROR_IF_PRUNED(method, "print", op);
}
void OpEmitter::genVerifier() {
auto *implMethod =
opClass.addMethod("::llvm::LogicalResult", "verifyInvariantsImpl");
ERROR_IF_PRUNED(implMethod, "verifyInvariantsImpl", op);
auto &implBody = implMethod->body();
bool useProperties = emitHelper.hasProperties();
populateSubstitutions(emitHelper, verifyCtx);
genAttributeVerifier(emitHelper, verifyCtx, implBody, staticVerifierEmitter,
useProperties);
genOperandResultVerifier(implBody, op.getOperands(), "operand");
genOperandResultVerifier(implBody, op.getResults(), "result");
for (auto &trait : op.getTraits()) {
if (auto *t = dyn_cast<tblgen::PredTrait>(&trait)) {
implBody << tgfmt(" if (!($0))\n "
"return emitOpError(\"failed to verify that $1\");\n",
&verifyCtx, tgfmt(t->getPredTemplate(), &verifyCtx),
t->getSummary());
}
}
genRegionVerifier(implBody);
genSuccessorVerifier(implBody);
implBody << " return ::mlir::success();\n";
auto *method = opClass.addMethod("::llvm::LogicalResult", "verifyInvariants");
ERROR_IF_PRUNED(method, "verifyInvariants", op);
auto &body = method->body();
if (def.getValueAsBit("hasVerifier")) {
body << " if(::mlir::succeeded(verifyInvariantsImpl()) && "
"::mlir::succeeded(verify()))\n";
body << " return ::mlir::success();\n";
body << " return ::mlir::failure();";
} else {
body << " return verifyInvariantsImpl();";
}
}
void OpEmitter::genCustomVerifier() {
if (def.getValueAsBit("hasVerifier")) {
auto *method = opClass.declareMethod("::llvm::LogicalResult", "verify");
ERROR_IF_PRUNED(method, "verify", op);
}
if (def.getValueAsBit("hasRegionVerifier")) {
auto *method =
opClass.declareMethod("::llvm::LogicalResult", "verifyRegions");
ERROR_IF_PRUNED(method, "verifyRegions", op);
}
}
void OpEmitter::genOperandResultVerifier(MethodBody &body,
Operator::const_value_range values,
StringRef valueKind) {
const char *const verifyOptional = R"(
if (valueGroup{0}.size() > 1) {
return emitOpError("{1} group starting at #") << index
<< " requires 0 or 1 element, but found " << valueGroup{0}.size();
}
)";
const char *const verifyValues = R"(
for (auto v : valueGroup{0}) {
if (::mlir::failed({1}(*this, v.getType(), "{2}", index++)))
return ::mlir::failure();
}
)";
const auto canSkip = [](const NamedTypeConstraint &value) {
return !value.hasPredicate() && !value.isOptional() &&
!value.isVariadicOfVariadic();
};
if (values.empty() || llvm::all_of(values, canSkip))
return;
FmtContext fctx;
body << " {\n unsigned index = 0; (void)index;\n";
for (const auto &staticValue : llvm::enumerate(values)) {
const NamedTypeConstraint &value = staticValue.value();
bool hasPredicate = value.hasPredicate();
bool isOptional = value.isOptional();
bool isVariadicOfVariadic = value.isVariadicOfVariadic();
if (!hasPredicate && !isOptional && !isVariadicOfVariadic)
continue;
body << formatv(" auto valueGroup{2} = getODS{0}{1}s({2});\n",
valueKind.substr(0, 1).upper(), valueKind.substr(1),
staticValue.index());
if (isOptional) {
body << formatv(verifyOptional, staticValue.index(), valueKind);
} else if (isVariadicOfVariadic) {
body << formatv(
" if (::mlir::failed(::mlir::OpTrait::impl::verifyValueSizeAttr("
"*this, \"{0}\", \"{1}\", valueGroup{2}.size())))\n"
" return ::mlir::failure();\n",
value.constraint.getVariadicOfVariadicSegmentSizeAttr(), value.name,
staticValue.index());
}
if (!hasPredicate)
continue;
StringRef constraintFn =
staticVerifierEmitter.getTypeConstraintFn(value.constraint);
body << formatv(verifyValues, staticValue.index(), constraintFn, valueKind);
}
body << " }\n";
}
void OpEmitter::genRegionVerifier(MethodBody &body) {
const char *const verifyRegion = R"(
for (auto ®ion : {0})
if (::mlir::failed({1}(*this, region, "{2}", index++)))
return ::mlir::failure();
)";
const char *const getSingleRegion =
"::llvm::MutableArrayRef((*this)->getRegion({0}))";
const auto canSkip = [](const NamedRegion ®ion) {
return region.constraint.getPredicate().isNull();
};
auto regions = op.getRegions();
if (regions.empty() && llvm::all_of(regions, canSkip))
return;
body << " {\n unsigned index = 0; (void)index;\n";
for (const auto &it : llvm::enumerate(regions)) {
const auto ®ion = it.value();
if (canSkip(region))
continue;
auto getRegion = region.isVariadic()
? formatv("{0}()", op.getGetterName(region.name)).str()
: formatv(getSingleRegion, it.index()).str();
auto constraintFn =
staticVerifierEmitter.getRegionConstraintFn(region.constraint);
body << formatv(verifyRegion, getRegion, constraintFn, region.name);
}
body << " }\n";
}
void OpEmitter::genSuccessorVerifier(MethodBody &body) {
const char *const verifySuccessor = R"(
for (auto *successor : {0})
if (::mlir::failed({1}(*this, successor, "{2}", index++)))
return ::mlir::failure();
)";
const char *const getSingleSuccessor = "::llvm::MutableArrayRef({0}())";
const auto canSkip = [](const NamedSuccessor &successor) {
return successor.constraint.getPredicate().isNull();
};
auto successors = op.getSuccessors();
if (successors.empty() && llvm::all_of(successors, canSkip))
return;
body << " {\n unsigned index = 0; (void)index;\n";
for (auto it : llvm::enumerate(successors)) {
const auto &successor = it.value();
if (canSkip(successor))
continue;
auto getSuccessor =
formatv(successor.isVariadic() ? "{0}()" : getSingleSuccessor,
successor.name, it.index())
.str();
auto constraintFn =
staticVerifierEmitter.getSuccessorConstraintFn(successor.constraint);
body << formatv(verifySuccessor, getSuccessor, constraintFn,
successor.name);
}
body << " }\n";
}
static void addSizeCountTrait(OpClass &opClass, StringRef traitKind,
int numTotal, int numVariadic) {
if (numVariadic != 0) {
if (numTotal == numVariadic)
opClass.addTrait("::mlir::OpTrait::Variadic" + traitKind + "s");
else
opClass.addTrait("::mlir::OpTrait::AtLeastN" + traitKind + "s<" +
Twine(numTotal - numVariadic) + ">::Impl");
return;
}
switch (numTotal) {
case 0:
opClass.addTrait("::mlir::OpTrait::Zero" + traitKind + "s");
break;
case 1:
opClass.addTrait("::mlir::OpTrait::One" + traitKind);
break;
default:
opClass.addTrait("::mlir::OpTrait::N" + traitKind + "s<" + Twine(numTotal) +
">::Impl");
break;
}
}
void OpEmitter::genTraits() {
unsigned numRegions = op.getNumRegions();
unsigned numVariadicRegions = op.getNumVariadicRegions();
addSizeCountTrait(opClass, "Region", numRegions, numVariadicRegions);
int numResults = op.getNumResults();
int numVariadicResults = op.getNumVariableLengthResults();
addSizeCountTrait(opClass, "Result", numResults, numVariadicResults);
if (numResults == 1 && numVariadicResults == 0) {
auto cppName = op.getResults().begin()->constraint.getCPPClassName();
opClass.addTrait("::mlir::OpTrait::OneTypedResult<" + cppName + ">::Impl");
}
unsigned numSuccessors = op.getNumSuccessors();
unsigned numVariadicSuccessors = op.getNumVariadicSuccessors();
addSizeCountTrait(opClass, "Successor", numSuccessors, numVariadicSuccessors);
int numOperands = op.getNumOperands();
int numVariadicOperands = op.getNumVariableLengthOperands();
addSizeCountTrait(opClass, "Operand", numOperands, numVariadicOperands);
for (const auto &trait : op.getTraits()) {
if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {
if (opTrait->isStructuralOpTrait())
opClass.addTrait(opTrait->getFullyQualifiedTraitName());
}
}
opClass.addTrait("::mlir::OpTrait::OpInvariants");
if (emitHelper.hasProperties())
opClass.addTrait("::mlir::BytecodeOpInterface::Trait");
for (const auto &trait : op.getTraits()) {
if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {
if (!opTrait->isStructuralOpTrait())
opClass.addTrait(opTrait->getFullyQualifiedTraitName());
} else if (auto *opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait)) {
opClass.addTrait(opTrait->getFullyQualifiedTraitName());
}
}
}
void OpEmitter::genOpNameGetter() {
auto *method = opClass.addStaticMethod<Method::Constexpr>(
"::llvm::StringLiteral", "getOperationName");
ERROR_IF_PRUNED(method, "getOperationName", op);
method->body() << " return ::llvm::StringLiteral(\"" << op.getOperationName()
<< "\");";
}
void OpEmitter::genOpAsmInterface() {
int numResults = op.getNumResults();
if (numResults <= 1 || op.getTrait("::mlir::OpAsmOpInterface::Trait"))
return;
SmallVector<StringRef, 4> resultNames(numResults);
for (int i = 0; i != numResults; ++i)
resultNames[i] = op.getResultName(i);
if (llvm::all_of(resultNames, [](StringRef name) { return name.empty(); }))
return;
opClass.addTrait("::mlir::OpAsmOpInterface::Trait");
auto *method = opClass.addMethod(
"void", "getAsmResultNames",
MethodParameter("::mlir::OpAsmSetValueNameFn", "setNameFn"));
ERROR_IF_PRUNED(method, "getAsmResultNames", op);
auto &body = method->body();
for (int i = 0; i != numResults; ++i) {
body << " auto resultGroup" << i << " = getODSResults(" << i << ");\n"
<< " if (!resultGroup" << i << ".empty())\n"
<< " setNameFn(*resultGroup" << i << ".begin(), \""
<< resultNames[i] << "\");\n";
}
}
namespace {
class OpOperandAdaptorEmitter {
public:
static void
emitDecl(const Operator &op,
const StaticVerifierFunctionEmitter &staticVerifierEmitter,
raw_ostream &os);
static void
emitDef(const Operator &op,
const StaticVerifierFunctionEmitter &staticVerifierEmitter,
raw_ostream &os);
private:
explicit OpOperandAdaptorEmitter(
const Operator &op,
const StaticVerifierFunctionEmitter &staticVerifierEmitter);
void addVerification();
const Operator &op;
Class genericAdaptorBase;
Class genericAdaptor;
Class adaptor;
const StaticVerifierFunctionEmitter &staticVerifierEmitter;
OpOrAdaptorHelper emitHelper;
};
}
OpOperandAdaptorEmitter::OpOperandAdaptorEmitter(
const Operator &op,
const StaticVerifierFunctionEmitter &staticVerifierEmitter)
: op(op), genericAdaptorBase(op.getGenericAdaptorName() + "Base"),
genericAdaptor(op.getGenericAdaptorName()), adaptor(op.getAdaptorName()),
staticVerifierEmitter(staticVerifierEmitter),
emitHelper(op, false) {
genericAdaptorBase.declare<VisibilityDeclaration>(Visibility::Public);
bool useProperties = emitHelper.hasProperties();
if (useProperties) {
using ConstArgument =
llvm::PointerUnion<const AttributeMetadata *, const NamedProperty *>;
SmallVector<ConstArgument> attrOrProperties;
for (const std::pair<StringRef, AttributeMetadata> &it :
emitHelper.getAttrMetadata()) {
if (!it.second.constraint || !it.second.constraint->isDerivedAttr())
attrOrProperties.push_back(&it.second);
}
for (const NamedProperty &prop : op.getProperties())
attrOrProperties.push_back(&prop);
if (emitHelper.getOperandSegmentsSize())
attrOrProperties.push_back(&emitHelper.getOperandSegmentsSize().value());
if (emitHelper.getResultSegmentsSize())
attrOrProperties.push_back(&emitHelper.getResultSegmentsSize().value());
assert(!attrOrProperties.empty());
std::string declarations = " struct Properties {\n";
llvm::raw_string_ostream os(declarations);
std::string comparator =
" bool operator==(const Properties &rhs) const {\n"
" return \n";
llvm::raw_string_ostream comparatorOs(comparator);
for (const auto &attrOrProp : attrOrProperties) {
if (const auto *namedProperty =
llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {
StringRef name = namedProperty->name;
if (name.empty())
report_fatal_error("missing name for property");
std::string camelName =
convertToCamelFromSnakeCase(name, true);
auto &prop = namedProperty->prop;
os << " using " << name << "Ty = " << prop.getStorageType() << ";\n"
<< " " << name << "Ty " << name;
if (prop.hasDefaultValue())
os << " = " << prop.getDefaultValue();
comparatorOs << " rhs." << name << " == this->" << name
<< " &&\n";
const char *accessorFmt = R"decl(;
{0} get{1}() {
auto &propStorage = this->{2};
return {3};
}
void set{1}(const {0} &propValue) {
auto &propStorage = this->{2};
{4};
}
)decl";
FmtContext fctx;
os << formatv(accessorFmt, prop.getInterfaceType(), camelName, name,
tgfmt(prop.getConvertFromStorageCall(),
&fctx.addSubst("_storage", propertyStorage)),
tgfmt(prop.getAssignToStorageCall(),
&fctx.addSubst("_value", propertyValue)
.addSubst("_storage", propertyStorage)));
continue;
}
const auto *namedAttr =
llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);
const Attribute *attr = nullptr;
if (namedAttr->constraint)
attr = &*namedAttr->constraint;
StringRef name = namedAttr->attrName;
if (name.empty())
report_fatal_error("missing name for property attr");
std::string camelName =
convertToCamelFromSnakeCase(name, true);
StringRef storageType;
if (attr) {
storageType = attr->getStorageType();
} else {
if (name != operandSegmentAttrName && name != resultSegmentAttrName) {
report_fatal_error("unexpected AttributeMetadata");
}
storageType = "::mlir::DenseI32ArrayAttr";
}
os << " using " << name << "Ty = " << storageType << ";\n"
<< " " << name << "Ty " << name << ";\n";
comparatorOs << " rhs." << name << " == this->" << name << " &&\n";
if (attr) {
const char *accessorFmt = R"decl(
auto get{0}() {
auto &propStorage = this->{1};
return ::llvm::{2}<{3}>(propStorage);
}
void set{0}(const {3} &propValue) {
this->{1} = propValue;
}
)decl";
os << formatv(accessorFmt, camelName, name,
attr->isOptional() || attr->hasDefaultValue()
? "dyn_cast_or_null"
: "cast",
storageType);
}
}
comparatorOs << " true;\n }\n"
" bool operator!=(const Properties &rhs) const {\n"
" return !(*this == rhs);\n"
" }\n";
comparatorOs.flush();
os << comparator;
os << " };\n";
os.flush();
genericAdaptorBase.declare<ExtraClassDeclaration>(std::move(declarations));
}
genericAdaptorBase.declare<VisibilityDeclaration>(Visibility::Protected);
genericAdaptorBase.declare<Field>("::mlir::DictionaryAttr", "odsAttrs");
genericAdaptorBase.declare<Field>("::std::optional<::mlir::OperationName>",
"odsOpName");
if (useProperties)
genericAdaptorBase.declare<Field>("Properties", "properties");
genericAdaptorBase.declare<Field>("::mlir::RegionRange", "odsRegions");
genericAdaptor.addTemplateParam("RangeT");
genericAdaptor.addField("RangeT", "odsOperands");
genericAdaptor.addParent(
ParentClass("detail::" + genericAdaptorBase.getClassName()));
genericAdaptor.declare<UsingDeclaration>(
"ValueT", "::llvm::detail::ValueOfRange<RangeT>");
genericAdaptor.declare<UsingDeclaration>(
"Base", "detail::" + genericAdaptorBase.getClassName());
const auto *attrSizedOperands =
op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");
{
SmallVector<MethodParameter> paramList;
if (useProperties) {
paramList.emplace_back("::mlir::DictionaryAttr", "attrs");
paramList.emplace_back("const Properties &", "properties");
} else {
paramList.emplace_back("::mlir::DictionaryAttr", "attrs", "{}");
paramList.emplace_back("const ::mlir::EmptyProperties &", "properties",
"{}");
}
paramList.emplace_back("::mlir::RegionRange", "regions", "{}");
auto *baseConstructor =
genericAdaptorBase.addConstructor<Method::Inline>(paramList);
baseConstructor->addMemberInitializer("odsAttrs", "attrs");
if (useProperties)
baseConstructor->addMemberInitializer("properties", "properties");
baseConstructor->addMemberInitializer("odsRegions", "regions");
MethodBody &body = baseConstructor->body();
body.indent() << "if (odsAttrs)\n";
body.indent() << formatv(
"odsOpName.emplace(\"{0}\", odsAttrs.getContext());\n",
op.getOperationName());
paramList.insert(paramList.begin(), MethodParameter("RangeT", "values"));
auto *constructor = genericAdaptor.addConstructor(paramList);
constructor->addMemberInitializer("Base", "attrs, properties, regions");
constructor->addMemberInitializer("odsOperands", "values");
paramList[1] = MethodParameter("::mlir::DictionaryAttr", "attrs");
paramList[2] = MethodParameter("::mlir::OpaqueProperties", "properties");
auto *opaquePropertiesConstructor =
genericAdaptor.addConstructor(std::move(paramList));
if (useProperties) {
opaquePropertiesConstructor->addMemberInitializer(
genericAdaptor.getClassName(),
"values, "
"attrs, "
"(properties ? *properties.as<Properties *>() : Properties{}), "
"regions");
} else {
opaquePropertiesConstructor->addMemberInitializer(
genericAdaptor.getClassName(),
"values, "
"attrs, "
"(properties ? *properties.as<::mlir::EmptyProperties *>() : "
"::mlir::EmptyProperties{}), "
"regions");
}
if (useProperties) {
SmallVector<MethodParameter> paramList;
paramList.emplace_back("RangeT", "values");
paramList.emplace_back("::mlir::DictionaryAttr", "attrs",
attrSizedOperands ? "" : "nullptr");
auto *noPropertiesConstructor =
genericAdaptor.addConstructor(std::move(paramList));
noPropertiesConstructor->addMemberInitializer(
genericAdaptor.getClassName(), "values, "
"attrs, "
"Properties{}, "
"{}");
}
}
{
Constructor *constructor;
if (useProperties) {
constructor = genericAdaptorBase.addConstructor(
MethodParameter(op.getCppClassName(), "op"));
} else {
constructor = genericAdaptorBase.addConstructor<Method::Inline>(
MethodParameter("::mlir::Operation *", "op"));
}
constructor->addMemberInitializer("odsAttrs",
"op->getRawDictionaryAttrs()");
constructor->addMemberInitializer("odsOpName", "op->getName()");
if (useProperties)
constructor->addMemberInitializer("properties", "op.getProperties()");
constructor->addMemberInitializer("odsRegions", "op->getRegions()");
constructor = genericAdaptor.addConstructor(
MethodParameter("RangeT", "values"), MethodParameter("LateInst", "op"));
constructor->addTemplateParam("LateInst = " + op.getCppClassName());
constructor->addTemplateParam(
"= std::enable_if_t<std::is_same_v<LateInst, " + op.getCppClassName() +
">>");
constructor->addMemberInitializer("Base", "op");
constructor->addMemberInitializer("odsOperands", "values");
}
std::string sizeAttrInit;
if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
if (op.getDialect().usePropertiesForAttributes())
sizeAttrInit =
formatv(adapterSegmentSizeAttrInitCodeProperties,
llvm::formatv("getProperties().operandSegmentSizes"));
else
sizeAttrInit = formatv(adapterSegmentSizeAttrInitCode,
emitHelper.getAttr(operandSegmentAttrName));
}
generateNamedOperandGetters(op, genericAdaptor,
&genericAdaptorBase,
sizeAttrInit,
"RangeT",
"ValueT",
"odsOperands.begin()",
"odsOperands.size()",
"odsOperands[{0}]");
if (auto *m = genericAdaptor.addMethod("RangeT", "getOperands"))
m->body() << " return odsOperands;";
FmtContext fctx;
fctx.withBuilder("::mlir::Builder(odsAttrs.getContext())");
auto emitAttrWithStorageType = [&](StringRef name, StringRef emitName,
Attribute attr) {
auto *method = genericAdaptorBase.addMethod(
attr.getStorageType(), emitName + "Attr",
attr.hasDefaultValue() || !useProperties ? Method::Properties::None
: Method::Properties::Inline);
ERROR_IF_PRUNED(method, "Adaptor::" + emitName + "Attr", op);
auto &body = method->body().indent();
if (!useProperties)
body << "assert(odsAttrs && \"no attributes when constructing "
"adapter\");\n";
body << formatv(
"auto attr = ::llvm::{1}<{2}>({0});\n", emitHelper.getAttr(name),
attr.hasDefaultValue() || attr.isOptional() ? "dyn_cast_or_null"
: "cast",
attr.getStorageType());
if (attr.hasDefaultValue() && attr.isOptional()) {
std::string defaultValue = std::string(
tgfmt(attr.getConstBuilderTemplate(), &fctx, attr.getDefaultValue()));
body << "if (!attr)\n attr = " << defaultValue << ";\n";
}
body << "return attr;\n";
};
if (useProperties) {
auto *m = genericAdaptorBase.addInlineMethod("const Properties &",
"getProperties");
ERROR_IF_PRUNED(m, "Adaptor::getProperties", op);
m->body() << " return properties;";
}
{
auto *m = genericAdaptorBase.addInlineMethod("::mlir::DictionaryAttr",
"getAttributes");
ERROR_IF_PRUNED(m, "Adaptor::getAttributes", op);
m->body() << " return odsAttrs;";
}
for (auto &namedAttr : op.getAttributes()) {
const auto &name = namedAttr.name;
const auto &attr = namedAttr.attr;
if (attr.isDerivedAttr())
continue;
std::string emitName = op.getGetterName(name);
emitAttrWithStorageType(name, emitName, attr);
emitAttrGetterWithReturnType(fctx, genericAdaptorBase, op, emitName, attr);
}
unsigned numRegions = op.getNumRegions();
for (unsigned i = 0; i < numRegions; ++i) {
const auto ®ion = op.getRegion(i);
if (region.name.empty())
continue;
std::string name = op.getGetterName(region.name);
if (region.isVariadic()) {
auto *m = genericAdaptorBase.addInlineMethod("::mlir::RegionRange", name);
ERROR_IF_PRUNED(m, "Adaptor::" + name, op);
m->body() << formatv(" return odsRegions.drop_front({0});", i);
continue;
}
auto *m = genericAdaptorBase.addInlineMethod("::mlir::Region &", name);
ERROR_IF_PRUNED(m, "Adaptor::" + name, op);
m->body() << formatv(" return *odsRegions[{0}];", i);
}
if (numRegions > 0) {
if (auto *m = genericAdaptorBase.addInlineMethod("::mlir::RegionRange",
"getRegions"))
m->body() << " return odsRegions;";
}
StringRef genericAdaptorClassName = genericAdaptor.getClassName();
adaptor.addParent(ParentClass(genericAdaptorClassName))
.addTemplateParam("::mlir::ValueRange");
adaptor.declare<VisibilityDeclaration>(Visibility::Public);
adaptor.declare<UsingDeclaration>(genericAdaptorClassName +
"::" + genericAdaptorClassName);
{
auto *constructor =
adaptor.addConstructor(MethodParameter(op.getCppClassName(), "op"));
constructor->addMemberInitializer(genericAdaptorClassName,
"op->getOperands(), op");
}
addVerification();
genericAdaptorBase.finalize();
genericAdaptor.finalize();
adaptor.finalize();
}
void OpOperandAdaptorEmitter::addVerification() {
auto *method = adaptor.addMethod("::llvm::LogicalResult", "verify",
MethodParameter("::mlir::Location", "loc"));
ERROR_IF_PRUNED(method, "verify", op);
auto &body = method->body();
bool useProperties = emitHelper.hasProperties();
FmtContext verifyCtx;
populateSubstitutions(emitHelper, verifyCtx);
genAttributeVerifier(emitHelper, verifyCtx, body, staticVerifierEmitter,
useProperties);
body << " return ::mlir::success();";
}
void OpOperandAdaptorEmitter::emitDecl(
const Operator &op,
const StaticVerifierFunctionEmitter &staticVerifierEmitter,
raw_ostream &os) {
OpOperandAdaptorEmitter emitter(op, staticVerifierEmitter);
{
NamespaceEmitter ns(os, "detail");
emitter.genericAdaptorBase.writeDeclTo(os);
}
emitter.genericAdaptor.writeDeclTo(os);
emitter.adaptor.writeDeclTo(os);
}
void OpOperandAdaptorEmitter::emitDef(
const Operator &op,
const StaticVerifierFunctionEmitter &staticVerifierEmitter,
raw_ostream &os) {
OpOperandAdaptorEmitter emitter(op, staticVerifierEmitter);
{
NamespaceEmitter ns(os, "detail");
emitter.genericAdaptorBase.writeDefTo(os);
}
emitter.genericAdaptor.writeDefTo(os);
emitter.adaptor.writeDefTo(os);
}
static void
emitOpClasses(const RecordKeeper &recordKeeper,
const std::vector<Record *> &defs, raw_ostream &os,
const StaticVerifierFunctionEmitter &staticVerifierEmitter,
bool emitDecl) {
if (defs.empty())
return;
for (auto *def : defs) {
Operator op(*def);
if (emitDecl) {
{
NamespaceEmitter emitter(os, op.getCppNamespace());
os << formatv(opCommentHeader, op.getQualCppClassName(),
"declarations");
OpOperandAdaptorEmitter::emitDecl(op, staticVerifierEmitter, os);
OpEmitter::emitDecl(op, os, staticVerifierEmitter);
}
if (!op.getCppNamespace().empty())
os << "MLIR_DECLARE_EXPLICIT_TYPE_ID(" << op.getCppNamespace()
<< "::" << op.getCppClassName() << ")\n\n";
} else {
{
NamespaceEmitter emitter(os, op.getCppNamespace());
os << formatv(opCommentHeader, op.getQualCppClassName(), "definitions");
OpOperandAdaptorEmitter::emitDef(op, staticVerifierEmitter, os);
OpEmitter::emitDef(op, os, staticVerifierEmitter);
}
if (!op.getCppNamespace().empty())
os << "MLIR_DEFINE_EXPLICIT_TYPE_ID(" << op.getCppNamespace()
<< "::" << op.getCppClassName() << ")\n\n";
}
}
}
static void emitOpClassDecls(const RecordKeeper &recordKeeper,
const std::vector<Record *> &defs,
raw_ostream &os) {
for (auto *def : defs) {
Operator op(*def);
NamespaceEmitter emitter(os, op.getCppNamespace());
os << "class " << op.getCppClassName() << ";\n";
}
IfDefScope scope("GET_OP_CLASSES", os);
if (defs.empty())
return;
StaticVerifierFunctionEmitter staticVerifierEmitter(os, recordKeeper);
staticVerifierEmitter.collectOpConstraints(defs);
emitOpClasses(recordKeeper, defs, os, staticVerifierEmitter,
true);
}
static void emitOpClassDefs(const RecordKeeper &recordKeeper,
ArrayRef<Record *> defs, raw_ostream &os,
StringRef constraintPrefix = "") {
if (defs.empty())
return;
StaticVerifierFunctionEmitter staticVerifierEmitter(os, recordKeeper,
constraintPrefix);
os << formatv(opCommentHeader, "Local Utility Method", "Definitions");
staticVerifierEmitter.collectOpConstraints(defs);
staticVerifierEmitter.emitOpConstraints(defs);
emitOpClasses(recordKeeper, defs, os, staticVerifierEmitter,
false);
}
static bool emitOpDecls(const RecordKeeper &recordKeeper, raw_ostream &os) {
emitSourceFileHeader("Op Declarations", os, recordKeeper);
std::vector<Record *> defs = getRequestedOpDefinitions(recordKeeper);
emitOpClassDecls(recordKeeper, defs, os);
SmallVector<ArrayRef<Record *>, 4> shardedDefs;
shardOpDefinitions(defs, shardedDefs);
if (defs.empty() || shardedDefs.size() <= 1)
return false;
Dialect dialect = Operator(defs.front()).getDialect();
NamespaceEmitter ns(os, dialect);
const char *const opRegistrationHook =
"void register{0}Operations{1}({2}::{0} *dialect);\n";
os << formatv(opRegistrationHook, dialect.getCppClassName(), "",
dialect.getCppNamespace());
for (unsigned i = 0; i < shardedDefs.size(); ++i) {
os << formatv(opRegistrationHook, dialect.getCppClassName(), i,
dialect.getCppNamespace());
}
return false;
}
static void emitOpDefShard(const RecordKeeper &recordKeeper,
ArrayRef<Record *> defs, const Dialect &dialect,
unsigned shardIndex, unsigned shardCount,
raw_ostream &os) {
std::string shardGuard = "GET_OP_DEFS_";
std::string indexStr = std::to_string(shardIndex);
shardGuard += indexStr;
IfDefScope scope(shardGuard, os);
const char *const opRegistrationHook =
"void {0}::register{1}Operations{2}({0}::{1} *dialect) {{\n";
if (shardIndex == 0) {
os << formatv(opRegistrationHook, dialect.getCppNamespace(),
dialect.getCppClassName(), "");
for (unsigned i = 0; i < shardCount; ++i) {
os << formatv(" {0}::register{1}Operations{2}(dialect);\n",
dialect.getCppNamespace(), dialect.getCppClassName(), i);
}
os << "}\n";
}
os << formatv(opCommentHeader, dialect.getCppClassName(),
"Op Registration Hook")
<< formatv(opRegistrationHook, dialect.getCppNamespace(),
dialect.getCppClassName(), shardIndex);
for (Record *def : defs) {
os << formatv(" ::mlir::RegisteredOperationName::insert<{0}>(*dialect);\n",
Operator(def).getQualCppClassName());
}
os << "}\n";
emitOpClassDefs(recordKeeper, defs, os, indexStr);
}
static bool emitOpDefs(const RecordKeeper &recordKeeper, raw_ostream &os) {
emitSourceFileHeader("Op Definitions", os, recordKeeper);
std::vector<Record *> defs = getRequestedOpDefinitions(recordKeeper);
SmallVector<ArrayRef<Record *>, 4> shardedDefs;
shardOpDefinitions(defs, shardedDefs);
if (shardedDefs.size() == 1) {
{
IfDefScope scope("GET_OP_LIST", os);
interleave(
defs, os,
[&](Record *def) { os << Operator(def).getQualCppClassName(); },
",\n");
}
{
IfDefScope scope("GET_OP_CLASSES", os);
emitOpClassDefs(recordKeeper, defs, os);
}
return false;
}
if (defs.empty())
return false;
Dialect dialect = Operator(defs.front()).getDialect();
for (auto [idx, value] : llvm::enumerate(shardedDefs)) {
emitOpDefShard(recordKeeper, value, dialect, idx, shardedDefs.size(), os);
}
return false;
}
static mlir::GenRegistration
genOpDecls("gen-op-decls", "Generate op declarations",
[](const RecordKeeper &records, raw_ostream &os) {
return emitOpDecls(records, os);
});
static mlir::GenRegistration genOpDefs("gen-op-defs", "Generate op definitions",
[](const RecordKeeper &records,
raw_ostream &os) {
return emitOpDefs(records, os);
});