let arguments = (ins
<type-constraint>:$<operand-name>,
...
-
<attr-constraint>:$<attr-name>,
...
);
information.
There is no requirements on the relative order of operands and attributes; they
-can mix freely. But it is recommended to put all operands ahead of attributes,
-and use an empty line to separate them to make it more visually distinguishable
-if possible. The relative order of operands themselves matters.
+can mix freely. The relative order of operands themselves matters. From each
+named argument a named getter will be generated that returns the argument with
+the return type (in the case of attributes the return type will be
+constructed from the storage type, while for operands it will be `Value`). Each
+attribute's raw value (e.g., as stored) can also be accessed via generated
+`<name>Attr` getters for use in transformation passes where the more user
+friendly return type is less suitable.
All the arguments should be named to 1) provide documentation, 2) drive
auto-generation of getter methods, 3) provide a handle to reference for other
places like constraints.
-> * Place attributes after operands if possible
-> * Give operands and attribute proper names
-
#### Variadic operands
To declare a variadic operand, wrap the `TypeConstraint` for the operand with
-// RUN: mlir-tblgen -gen-op-decls -I %S/../../include %s | FileCheck %s --check-prefix=DECL
-// RUN: mlir-tblgen -gen-op-defs -I %S/../../include %s | FileCheck %s --check-prefix=DEF
+// RUN: mlir-tblgen -gen-op-decls -I %S/../../include %s | FileCheck %s --check-prefix=DECL --dump-input-on-failure
+// RUN: mlir-tblgen -gen-op-defs -I %S/../../include %s | FileCheck %s --check-prefix=DEF --dump-input-on-failure
include "mlir/IR/OpBase.td"
// Test getter methods
// ---
+// DEF: some-attr-kind AOp::aAttrAttr()
+// DEF-NEXT: this->getAttr("aAttr").cast<some-attr-kind>()
// DEF: some-return-type AOp::aAttr() {
-// DEF-NEXT: auto attr = this->getAttr("aAttr").cast<some-attr-kind>();
+// DEF-NEXT: auto attr = aAttrAttr()
// DEF-NEXT: return attr.some-convert-from-storage();
+// DEF: some-attr-kind AOp::bAttrAttr()
+// DEF-NEXT: return this->getAttr("bAttr").dyn_cast_or_null<some-attr-kind>()
// DEF: some-return-type AOp::bAttr() {
-// DEF-NEXT: auto attr = this->getAttr("bAttr").dyn_cast_or_null<some-attr-kind>();
+// DEF-NEXT: auto attr = bAttrAttr();
// DEF-NEXT: if (!attr)
// DEF-NEXT: return some-const-builder-call(mlir::Builder(this->getContext()), 4.2).some-convert-from-storage();
// DEF-NEXT: return attr.some-convert-from-storage();
+// DEF: some-attr-kind AOp::cAttrAttr()
+// DEF-NEXT: return this->getAttr("cAttr").dyn_cast_or_null<some-attr-kind>()
// DEF: Optional<some-return-type> AOp::cAttr() {
-// DEF-NEXT: auto attr = this->getAttr("cAttr").dyn_cast_or_null<some-attr-kind>();
+// DEF-NEXT: auto attr = cAttrAttr()
// DEF-NEXT: return attr ? Optional<some-return-type>(attr.some-convert-from-storage()) : (llvm::None);
// Test build methods
// CHECK-LABEL: NS::AOp declarations
-// CHECK: class AOpOperandAdaptor {
-// CHECK: public:
-// CHECK: AOpOperandAdaptor(ArrayRef<Value *> values);
-// CHECK: ArrayRef<Value *> getODSOperands(unsigned index);
-// CHECK: Value *a();
-// CHECK: ArrayRef<Value *> b();
-// CHECK: private:
-// CHECK: ArrayRef<Value *> tblgen_operands;
-// CHECK: };
+// CHECK: class AOpOperandAdaptor {
+// CHECK: public:
+// CHECK: AOpOperandAdaptor(ArrayRef<Value *> values);
+// CHECK: ArrayRef<Value *> getODSOperands(unsigned index);
+// CHECK: Value *a();
+// CHECK: ArrayRef<Value *> b();
+// CHECK: private:
+// CHECK: ArrayRef<Value *> tblgen_operands;
+// CHECK: };
// CHECK: class AOp : public Op<AOp, OpTrait::AtLeastNResults<1>::Impl, OpTrait::HasNoSideEffect, OpTrait::AtLeastNOperands<1>::Impl
// CHECK: public:
// CHECK: Operation::result_range getODSResults(unsigned index);
// CHECK: Value *r();
// CHECK: Region &someRegion();
+// CHECK: IntegerAttr attr1Attr()
// CHECK: APInt attr1();
+// CHECK: FloatAttr attr2Attr()
// CHECK: Optional< APFloat > attr2();
// CHECK: static void build(Value *val);
// CHECK: static void build(Builder *tblgen_builder, OperationState &tblgen_state, Type r, ArrayRef<Type> s, Value *a, ValueRange b, IntegerAttr attr1, /*optional*/FloatAttr attr2);
os << extraClassDeclaration << "\n";
if (hasPrivateMethod) {
- os << '\n';
- os << "private:\n";
+ os << "\nprivate:\n";
for (const auto &method : methods) {
if (method.isPrivate()) {
method.writeDeclTo(os);
void OpEmitter::genAttrGetters() {
FmtContext fctx;
fctx.withBuilder("mlir::Builder(this->getContext())");
- for (auto &namedAttr : op.getAttributes()) {
- const auto &name = namedAttr.name;
- const auto &attr = namedAttr.attr;
+ // Emit the derived attribute body.
+ auto emitDerivedAttr = [&](StringRef name, Attribute attr) {
auto &method = opClass.newMethod(attr.getReturnType(), name);
auto &body = method.body();
+ body << " " << attr.getDerivedCodeBody() << "\n";
+ };
- // Emit the derived attribute body.
- if (attr.isDerivedAttr()) {
- body << " " << attr.getDerivedCodeBody() << "\n";
- continue;
- }
-
- // Emit normal emitter.
-
- // Return the queried attribute with the correct return type.
- auto attrVal =
- (attr.hasDefaultValue() || attr.isOptional())
- ? formatv("this->getAttr(\"{0}\").dyn_cast_or_null<{1}>()", name,
- attr.getStorageType())
- : formatv("this->getAttr(\"{0}\").cast<{1}>()", name,
- attr.getStorageType());
- body << " auto attr = " << attrVal << ";\n";
+ // Emit with return type specified.
+ auto emitAttrWithReturnType = [&](StringRef name, Attribute attr) {
+ auto &method = opClass.newMethod(attr.getReturnType(), name);
+ auto &body = method.body();
+ body << " auto attr = " << name << "Attr();\n";
if (attr.hasDefaultValue()) {
// Returns the default value if not set.
// TODO: this is inefficient, we are recreating the attribute for every
body << " return "
<< tgfmt(attr.getConvertFromStorageCall(), &fctx.withSelf("attr"))
<< ";\n";
+ };
+
+ // Generate raw named accessor type. This is a wrapper class that allows
+ // referring to the attributes via accessors instead of having to use
+ // the string interface for better compile time verification.
+ auto emitAttrWithStorageType = [&](StringRef name, Attribute attr) {
+ auto &method =
+ opClass.newMethod(attr.getStorageType(), (name + "Attr").str());
+ auto &body = method.body();
+ body << " return this->getAttr(\"" << name << "\").";
+ if (attr.isOptional() || attr.hasDefaultValue())
+ body << "dyn_cast_or_null<";
+ else
+ body << "cast<";
+ body << attr.getStorageType() << ">();";
+ };
+
+ for (auto &namedAttr : op.getAttributes()) {
+ const auto &name = namedAttr.name;
+ const auto &attr = namedAttr.attr;
+ if (attr.isDerivedAttr()) {
+ emitDerivedAttr(name, attr);
+ } else {
+ emitAttrWithStorageType(name, attr);
+ emitAttrWithReturnType(name, attr);
+ }
}
}