https://github.com/krzysz00 created https://github.com/llvm/llvm-project/pull/120176
Give the properties from tablegen a `predicate` field that holds the predicate that the property needs to satisfy, if one exists, and hook that field up to verifier generation. >From e8b571f614adbc68ad551798c8e4b6b7df09c315 Mon Sep 17 00:00:00 2001 From: Krzysztof Drewniak <krzysdrewn...@gmail.com> Date: Wed, 27 Nov 2024 18:54:40 -0800 Subject: [PATCH] [mlir] Add predicates to tablegen-defined properties Give the properties from tablegen a `predicate` field that holds the predicate that the property needs to satisfy, if one exists, and hook that field up to verifier generation. --- mlir/include/mlir/IR/Properties.td | 129 +++++++++++---- mlir/include/mlir/TableGen/Property.h | 5 + mlir/lib/TableGen/Property.cpp | 14 +- mlir/test/IR/test-op-property-predicates.mlir | 148 ++++++++++++++++++ mlir/test/lib/Dialect/Test/TestOps.td | 32 +++- .../mlir-tblgen/op-properties-predicates.td | 72 +++++++++ mlir/test/mlir-tblgen/op-properties.td | 2 +- mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp | 55 +++++++ 8 files changed, 417 insertions(+), 40 deletions(-) create mode 100644 mlir/test/IR/test-op-property-predicates.mlir create mode 100644 mlir/test/mlir-tblgen/op-properties-predicates.td diff --git a/mlir/include/mlir/IR/Properties.td b/mlir/include/mlir/IR/Properties.td index 0becf7d0098356..63ba0fda2ac979 100644 --- a/mlir/include/mlir/IR/Properties.td +++ b/mlir/include/mlir/IR/Properties.td @@ -13,6 +13,8 @@ #ifndef PROPERTIES #define PROPERTIES +include "mlir/IR/Constraints.td" + // Base class for defining properties. class Property<string storageTypeParam = "", string desc = ""> { // User-readable one line summary used in error reporting messages. If empty, @@ -63,6 +65,12 @@ class Property<string storageTypeParam = "", string desc = ""> { return convertFromAttribute($_storage, $_attr, $_diag); }]; + // The verification predicate for this property. Defaults to And<[]>, + // which is trivially true, since properties are always their expected type. + // Within the predicate, `$_self` is an instance of the **interface** + // type of the property. + Pred predicate = ?; + // The call expression to hash the property. // // Format: @@ -150,8 +158,8 @@ class Property<string storageTypeParam = "", string desc = ""> { return ::mlir::failure(); }]; - // Base definition for the property. (Will be) used for `OptionalProperty` and - // such cases, analogously to `baseAttr`. + // Base definition for the property. Used to look through `OptionalProperty` + // for some format generation, as with the `baseAttr` field on attributes. Property baseProperty = ?; // Default value for the property within its storage. This should be an expression @@ -224,8 +232,7 @@ def I64Property : IntProperty<"int64_t">; class EnumProperty<string storageTypeParam, string desc = "", string default = ""> : Property<storageTypeParam, desc> { - // TODO: take advantage of EnumAttrInfo and the like to make this share nice - // parsing code with EnumAttr. + // TODO: implement predicate for enum validity. let writeToMlirBytecode = [{ $_writer.writeVarInt(static_cast<uint64_t>($_storage)); }]; @@ -330,6 +337,56 @@ def UnitProperty : Property<"bool", "unit property"> { }]; } +//===----------------------------------------------------------------------===// +// Property field overwrites + +/// Class for giving a property a default value. +/// This doesn't change anything about the property other than giving it a default +/// which can be used by ODS to elide printing. +class DefaultValuedProperty<Property p, string default = "", string storageDefault = ""> : Property<p.storageType, p.summary> { + let defaultValue = default; + let storageTypeValueOverride = storageDefault; + let baseProperty = p; + // Keep up to date with `Property` above. + let summary = p.summary; + let description = p.description; + let storageType = p.storageType; + let interfaceType = p.interfaceType; + let convertFromStorage = p.convertFromStorage; + let assignToStorage = p.assignToStorage; + let convertToAttribute = p.convertToAttribute; + let convertFromAttribute = p.convertFromAttribute; + let predicate = p.predicate; + let hashProperty = p.hashProperty; + let parser = p.parser; + let optionalParser = p.optionalParser; + let printer = p.printer; + let readFromMlirBytecode = p.readFromMlirBytecode; + let writeToMlirBytecode = p.writeToMlirBytecode; +} + +class ConfinedProperty<Property p, Pred pred, string newSummary = ""> + : Property<p.storageType, !if(!empty(newSummary), p.summary, newSummary)> { + let predicate = !if(!initialized(p.predicate), And<[p.predicate, pred]>, pred); + let baseProperty = p; + // Keep up to date with `Property` above. + let description = p.description; + let storageType = p.storageType; + let interfaceType = p.interfaceType; + let convertFromStorage = p.convertFromStorage; + let assignToStorage = p.assignToStorage; + let convertToAttribute = p.convertToAttribute; + let convertFromAttribute = p.convertFromAttribute; + let hashProperty = p.hashProperty; + let parser = p.parser; + let optionalParser = p.optionalParser; + let printer = p.printer; + let readFromMlirBytecode = p.readFromMlirBytecode; + let writeToMlirBytecode = p.writeToMlirBytecode; + let defaultValue = p.defaultValue; + let storageTypeValueOverride = p.storageTypeValueOverride; +} + //===----------------------------------------------------------------------===// // Primitive property combinators @@ -342,14 +399,37 @@ class _makePropStorage<Property prop, string name> { true : "") # ";"; } +/// Construct a `Pred`icate `ret` that wraps the predicate of the underlying +/// property `childProp` with: +/// +/// [](childProp.storageType& s) { +/// return [](childProp.interfaceType i) { +/// return leafSubst(childProp.predicate, "$_self" to "i"); +/// }(childProp.convertFromStorage(s)) +/// } +/// +/// and then appends `prefix` and `suffix`. +class _makeChildWrapperPred<string prefix, Property wrappedProp, string suffix> { + Pred ret = + !if(!initialized(wrappedProp.predicate), + Concat< + prefix # "[](" + # "const " # wrappedProp.storageType # "& baseStore) -> bool { return [](" + # wrappedProp.interfaceType # " baseIface) -> bool { return (", + SubstLeaves<"$_self", "baseIface", wrappedProp.predicate>, + "); }(" # !subst("$_storage", "baseStore", wrappedProp.convertFromStorage) + # "); }" # suffix + >, ?); +} + /// The generic class for arrays of some other property, which is stored as a /// `SmallVector` of that property. This uses an `ArrayAttr` as its attribute form /// though subclasses can override this, as is the case with IntArrayAttr below. /// Those wishing to use a non-default number of SmallVector elements should /// subclass `ArrayProperty`. -class ArrayProperty<Property elem = Property<>, string desc = ""> : - Property<"::llvm::SmallVector<" # elem.storageType # ">", desc> { - let summary = "array of " # elem.summary; +class ArrayProperty<Property elem = Property<>, string newSummary = ""> : + Property<"::llvm::SmallVector<" # elem.storageType # ">", + !if(!empty(newSummary), "array of " # elem.summary, newSummary)> { let interfaceType = "::llvm::ArrayRef<" # elem.storageType # ">"; let convertFromStorage = "::llvm::ArrayRef<" # elem.storageType # ">{$_storage}"; let assignToStorage = "$_storage.assign($_value.begin(), $_value.end())"; @@ -382,6 +462,8 @@ class ArrayProperty<Property elem = Property<>, string desc = ""> : return ::mlir::ArrayAttr::get($_ctxt, elems); }]; + let predicate = _makeChildWrapperPred<"::llvm::all_of($_self, ", elem, ")">.ret; + defvar theParserBegin = [{ auto& storage = $_storage; auto parseElemFn = [&]() -> ::mlir::ParseResult { @@ -463,8 +545,8 @@ class ArrayProperty<Property elem = Property<>, string desc = ""> : }]); } -class IntArrayProperty<string storageTypeParam = "", string desc = ""> : - ArrayProperty<IntProperty<storageTypeParam, desc>> { +class IntArrayProperty<Property elem, string newSummary=""> : + ArrayProperty<elem, newSummary> { // Bring back the trivial conversions we don't get in the general case. let convertFromAttribute = [{ return convertFromAttribute($_storage, $_attr, $_diag); @@ -474,30 +556,6 @@ class IntArrayProperty<string storageTypeParam = "", string desc = ""> : }]; } -/// Class for giving a property a default value. -/// This doesn't change anything about the property other than giving it a default -/// which can be used by ODS to elide printing. -class DefaultValuedProperty<Property p, string default = "", string storageDefault = ""> : Property<p.storageType, p.summary> { - let defaultValue = default; - let storageTypeValueOverride = storageDefault; - let baseProperty = p; - // Keep up to date with `Property` above. - let summary = p.summary; - let description = p.description; - let storageType = p.storageType; - let interfaceType = p.interfaceType; - let convertFromStorage = p.convertFromStorage; - let assignToStorage = p.assignToStorage; - let convertToAttribute = p.convertToAttribute; - let convertFromAttribute = p.convertFromAttribute; - let hashProperty = p.hashProperty; - let parser = p.parser; - let optionalParser = p.optionalParser; - let printer = p.printer; - let readFromMlirBytecode = p.readFromMlirBytecode; - let writeToMlirBytecode = p.writeToMlirBytecode; -} - /// An optional property, stored as an std::optional<p.storageType> /// interfaced with as an std::optional<p.interfaceType>.. /// The syntax is `none` (or empty string if elided) for an absent value or @@ -575,6 +633,11 @@ class OptionalProperty<Property p, bit canDelegateParsing = 1> return ::mlir::ArrayAttr::get($_ctxt, {attr}); }]; + let predicate = !if(!initialized(p.predicate), + Or<[CPred<"!$_self.has_value()">, + SubstLeaves<"$_self", "(*($_self))", p.predicate>]>, + ?); + defvar delegatedParserBegin = [{ if (::mlir::succeeded($_parser.parseOptionalKeyword("none"))) { $_storage = std::nullopt; diff --git a/mlir/include/mlir/TableGen/Property.h b/mlir/include/mlir/TableGen/Property.h index 702e6756e6a95c..386159191ef1f4 100644 --- a/mlir/include/mlir/TableGen/Property.h +++ b/mlir/include/mlir/TableGen/Property.h @@ -27,6 +27,7 @@ namespace mlir { namespace tblgen { class Dialect; class Type; +class Pred; // Wrapper class providing helper methods for accessing MLIR Property defined // in TableGen. This class should closely reflect what is defined as class @@ -74,6 +75,10 @@ class Property { return convertFromAttributeCall; } + // Return the property's predicate. Properties that didn't come from + // tablegen (the hardcoded ones) have the null predicate. + Pred getPredicate() const; + // Returns the method call which parses this property from textual MLIR. StringRef getParserCall() const { return parserCall; } diff --git a/mlir/lib/TableGen/Property.cpp b/mlir/lib/TableGen/Property.cpp index b86b87df91c607..13851167ddaf75 100644 --- a/mlir/lib/TableGen/Property.cpp +++ b/mlir/lib/TableGen/Property.cpp @@ -14,6 +14,7 @@ #include "mlir/TableGen/Property.h" #include "mlir/TableGen/Format.h" #include "mlir/TableGen/Operator.h" +#include "mlir/TableGen/Predicate.h" #include "llvm/TableGen/Record.h" using namespace mlir; @@ -68,8 +69,8 @@ Property::Property(StringRef summary, StringRef description, StringRef writeToMlirBytecodeCall, StringRef hashPropertyCall, StringRef defaultValue, StringRef storageTypeValueOverride) - : summary(summary), description(description), storageType(storageType), - interfaceType(interfaceType), + : def(nullptr), summary(summary), description(description), + storageType(storageType), interfaceType(interfaceType), convertFromStorageCall(convertFromStorageCall), assignToStorageCall(assignToStorageCall), convertToAttributeCall(convertToAttributeCall), @@ -91,6 +92,15 @@ StringRef Property::getPropertyDefName() const { return def->getName(); } +Pred Property::getPredicate() const { + if (!def) + return Pred(); + const llvm::RecordVal *maybePred = def->getValue("predicate"); + if (!maybePred || !maybePred->getValue()) + return Pred(); + return Pred(maybePred->getValue()); +} + Property Property::getBaseProperty() const { if (const auto *defInit = llvm::dyn_cast<llvm::DefInit>(def->getValueInit("baseProperty"))) { diff --git a/mlir/test/IR/test-op-property-predicates.mlir b/mlir/test/IR/test-op-property-predicates.mlir new file mode 100644 index 00000000000000..4dba420bc8f825 --- /dev/null +++ b/mlir/test/IR/test-op-property-predicates.mlir @@ -0,0 +1,148 @@ +// RUN: mlir-opt -split-input-file -verify-diagnostics %s + +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [2 : i64], + defaulted = 3 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [6 : i64], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +test.op_with_property_predicates <{ + scalar = 1 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [6 : i64], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'scalar' failed to satiisfy constraint: non-negative int64_t}} +test.op_with_property_predicates <{ + scalar = -1 : i64, + optional = [2 : i64], + defaulted = 3 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [6 : i64], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'optional' failed to satiisfy constraint: optional non-negative int64_t}} +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [-1 : i64], + defaulted = 3 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [6 : i64], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'defaulted' failed to satiisfy constraint: non-negative int64_t}} +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [2 : i64], + defaulted = -1 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [6 : i64], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'more_constrained' failed to satiisfy constraint: between 0 and 5}} +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [2 : i64], + defaulted = 3 : i64, + more_constrained = 100 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [6 : i64], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'array' failed to satiisfy constraint: array of non-negative int64_t}} +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [2 : i64], + defaulted = 3 : i64, + more_constrained = 4 : i64, + array = [-1 : i64], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [6 : i64], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'non_empty_unconstrained' failed to satiisfy constraint: non-empty array of int64_t}} +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [2 : i64], + defaulted = 3 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [], + non_empty_constrained = [6 : i64], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'non_empty_constrained' failed to satiisfy constraint: non-empty array of non-negative int64_t}} +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [2 : i64], + defaulted = 3 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'non_empty_constrained' failed to satiisfy constraint: non-empty array of non-negative int64_t}} +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [2 : i64], + defaulted = 3 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [-1 : i64], + non_empty_optional = [[7 : i64]], + unconstrained = 8 : i64}> + +// ----- + +// expected-error @+1 {{'test.op_with_property_predicates' op property 'non_empty_optional' failed to satiisfy constraint: optional non-empty array of non-negative int64_t}} +test.op_with_property_predicates <{ + scalar = 1 : i64, + optional = [2 : i64], + defaulted = 3 : i64, + more_constrained = 4 : i64, + array = [], + non_empty_unconstrained = [5: i64], + non_empty_constrained = [6 : i64], + non_empty_optional = [[]], + unconstrained = 8 : i64}> diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td index d24d52f356d88f..384139d9c32c02 100644 --- a/mlir/test/lib/Dialect/Test/TestOps.td +++ b/mlir/test/lib/Dialect/Test/TestOps.td @@ -2998,7 +2998,7 @@ def TestOpWithProperties : TEST_Op<"with_properties"> { StrAttr:$b, // Attributes can directly be used here. StringProperty:$c, BoolProperty:$flag, - IntArrayProperty<"int64_t">:$array // example of an array + IntArrayProperty<I64Property>:$array // example of an array ); } @@ -3040,15 +3040,15 @@ def TestOpWithEmptyProperties : TEST_Op<"empty_properties"> { def TestOpUsingPropertyInCustom : TEST_Op<"using_property_in_custom"> { let assemblyFormat = "custom<UsingPropertyInCustom>($prop) attr-dict"; - let arguments = (ins IntArrayProperty<"int64_t">:$prop); + let arguments = (ins IntArrayProperty<I64Property>:$prop); } def TestOpUsingPropertyInCustomAndOther : TEST_Op<"using_property_in_custom_and_other"> { let assemblyFormat = "custom<UsingPropertyInCustom>($prop) prop-dict attr-dict"; let arguments = (ins - IntArrayProperty<"int64_t">:$prop, - IntProperty<"int64_t">:$other + IntArrayProperty<I64Property>:$prop, + I64Property:$other ); } @@ -3253,6 +3253,30 @@ def TestOpWithArrayProperties : TEST_Op<"with_array_properties"> { ); } +def NonNegativeI64Property : ConfinedProperty<I64Property, + CPred<"$_self >= 0">, "non-negative int64_t">; + +class NonEmptyArray<Property p> : ConfinedProperty + <ArrayProperty<p>, Neg<CPred<"$_self.empty()">>, + "non-empty array of " # p.summary>; + +def OpWithPropertyPredicates : TEST_Op<"op_with_property_predicates"> { + let arguments = (ins + NonNegativeI64Property:$scalar, + OptionalProperty<NonNegativeI64Property>:$optional, + DefaultValuedProperty<NonNegativeI64Property, "0">:$defaulted, + ConfinedProperty<NonNegativeI64Property, + CPred<"$_self <= 5">, "between 0 and 5">:$more_constrained, + ArrayProperty<NonNegativeI64Property>:$array, + NonEmptyArray<I64Property>:$non_empty_unconstrained, + NonEmptyArray<NonNegativeI64Property>:$non_empty_constrained, + // Test applying predicates when the fromStorage() on the optional<> isn't trivial. + OptionalProperty<NonEmptyArray<NonNegativeI64Property>>:$non_empty_optional, + I64Property:$unconstrained + ); + let assemblyFormat = "attr-dict prop-dict"; +} + //===----------------------------------------------------------------------===// // Test Dataflow //===----------------------------------------------------------------------===// diff --git a/mlir/test/mlir-tblgen/op-properties-predicates.td b/mlir/test/mlir-tblgen/op-properties-predicates.td new file mode 100644 index 00000000000000..0a036e9f0cae51 --- /dev/null +++ b/mlir/test/mlir-tblgen/op-properties-predicates.td @@ -0,0 +1,72 @@ +// RUN: mlir-tblgen -gen-op-defs -I %S/../../include %s | FileCheck %s + +include "mlir/IR/Constraints.td" +include "mlir/IR/EnumAttr.td" +include "mlir/IR/OpBase.td" +include "mlir/IR/Properties.td" + +def Test_Dialect : Dialect { + let name = "test"; + let cppNamespace = "foobar"; +} +class NS_Op<string mnemonic, list<Trait> traits = []> : + Op<Test_Dialect, mnemonic, traits>; + +def NonNegativeI64Property : ConfinedProperty<I64Property, + CPred<"$_self >= 0">, "non-negative int64_t">; + +class NonEmptyArray<Property p> : ConfinedProperty + <ArrayProperty<p>, Neg<CPred<"$_self.empty()">>, + "non-empty array of " # p.summary>; + +def OpWithPredicates : NS_Op<"op_with_predicates"> { + let arguments = (ins + NonNegativeI64Property:$scalar, + OptionalProperty<NonNegativeI64Property>:$optional, + DefaultValuedProperty<NonNegativeI64Property, "0">:$defaulted, + ConfinedProperty<NonNegativeI64Property, + CPred<"$_self <= 5">, "between 0 and 5">:$moreConstrained, + ArrayProperty<NonNegativeI64Property>:$array, + NonEmptyArray<I64Property>:$non_empty_unconstrained, + NonEmptyArray<NonNegativeI64Property>:$non_empty_constrained, + // Test applying predicates when the fromStorage() on the optional<> isn't trivial. + OptionalProperty<NonEmptyArray<NonNegativeI64Property>>:$non_empty_optional, + I64Property:$unconstrained + ); +} + +// CHECK-LABEL: OpWithPredicates::verifyInvariantsImpl() +// CHECK: int64_t tblgen_scalar = this->getScalar(); (void)tblgen_scalar; +// CHECK: if (!((tblgen_scalar >= 0))) +// CHECK-NEXT: return emitOpError("property 'scalar' failed to satiisfy constraint: non-negative int64_t"); + +// CHECK: std::optional<int64_t> tblgen_optional = this->getOptional(); (void)tblgen_optional; +// CHECK: if (!(((!tblgen_optional.has_value())) || (((*(tblgen_optional)) >= 0)))) +// CHECK-NEXT: return emitOpError("property 'optional' failed to satiisfy constraint: optional non-negative int64_t"); + +// CHECK: int64_t tblgen_defaulted = this->getDefaulted(); (void)tblgen_defaulted; +// CHECK: if (!((tblgen_defaulted >= 0))) +// CHECK-NEXT: return emitOpError("property 'defaulted' failed to satiisfy constraint: non-negative int64_t"); + +// CHECK: int64_t tblgen_moreConstrained = this->getMoreConstrained(); (void)tblgen_moreConstrained; +// CHECK: if (!(((tblgen_moreConstrained >= 0)) && ((tblgen_moreConstrained <= 5)))) +// CHECK-NEXT: return emitOpError("property 'moreConstrained' failed to satiisfy constraint: between 0 and 5"); + +// CHECK: ::llvm::ArrayRef<int64_t> tblgen_array = this->getArray(); (void)tblgen_array; +// CHECK: if (!(::llvm::all_of(tblgen_array, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); }))) +// CHECK-NEXT: return emitOpError("property 'array' failed to satiisfy constraint: array of non-negative int64_t"); + +// CHECK: ::llvm::ArrayRef<int64_t> tblgen_nonEmptyUnconstrained = this->getNonEmptyUnconstrained(); (void)tblgen_nonEmptyUnconstrained; +// CHECK: if (!(!((tblgen_nonEmptyUnconstrained.empty())))) +// CHECK-NEXT: return emitOpError("property 'non_empty_unconstrained' failed to satiisfy constraint: non-empty array of int64_t"); + +// CHECK: ::llvm::ArrayRef<int64_t> tblgen_nonEmptyConstrained = this->getNonEmptyConstrained(); (void)tblgen_nonEmptyConstrained; +// CHECK: if (!((::llvm::all_of(tblgen_nonEmptyConstrained, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })) && (!((tblgen_nonEmptyConstrained.empty()))))) +// CHECK-NEXT: return emitOpError("property 'non_empty_constrained' failed to satiisfy constraint: non-empty array of non-negative int64_t"); + +// CHECK: std::optional<::llvm::ArrayRef<int64_t>> tblgen_nonEmptyOptional = this->getNonEmptyOptional(); (void)tblgen_nonEmptyOptional; +// CHECK: (!(((!tblgen_nonEmptyOptional.has_value())) || ((::llvm::all_of((*(tblgen_nonEmptyOptional)), [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })) && (!(((*(tblgen_nonEmptyOptional)).empty())))))) +// CHECK-NEXT: return emitOpError("property 'non_empty_optional' failed to satiisfy constraint: optional non-empty array of non-negative int64_t"); + +// CHECK-NOT: int64_t tblgen_unconstrained +// CHECK: return ::mlir::success(); diff --git a/mlir/test/mlir-tblgen/op-properties.td b/mlir/test/mlir-tblgen/op-properties.td index 918583c9f4ed4b..bf32ce42cf6fbf 100644 --- a/mlir/test/mlir-tblgen/op-properties.td +++ b/mlir/test/mlir-tblgen/op-properties.td @@ -20,7 +20,7 @@ def OpWithAttr : NS_Op<"op_with_attr">{ // Test required and optional properties // --- -def DefaultI64Array : IntArrayProperty<"int64_t"> { +def DefaultI64Array : IntArrayProperty<I64Property> { let defaultValue = "::llvm::ArrayRef<int64_t>{}"; let storageTypeValueOverride = "::llvm::SmallVector<int64_t>{}"; } diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp index 30a2d4da573dc5..6a93d5e9c397dc 100644 --- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp +++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp @@ -1033,6 +1033,60 @@ while (true) {{ emitVerifier(namedAttr.attr, namedAttr.name, getVarName(namedAttr.name)); } +static void genPropertyVerifier(const OpOrAdaptorHelper &emitHelper, + FmtContext &ctx, MethodBody &body) { + + // Code to get a reference to a property into a variable to avoid multiple + // evaluations while verifying a property. + // {0}: Property variable name. + // {1}: Property name, with the first letter capitalized, to find the getter. + // {2}: Property interface type. + const char *const fetchProperty = R"( + {2} {0} = this->get{1}(); (void){0}; +)"; + + // Code to verify that the predicate of a property holds. Embeds the + // condition inline. + // {0}: Property condition code, pre-tgfmt()'d. + // {1}: Emit error prefix. + // {2}: Property name. + // {3}: Property description. + const char *const verifyProperty = R"( + if (!({0})) + return {1}"property '{2}' failed to satiisfy constraint: {3}"); +)"; + + // Prefix variables with `tblgen_` to avoid hiding the attribute accessor. + const auto getVarName = [&](const NamedProperty &prop) { + std::string varName = + convertToCamelFromSnakeCase(prop.name, /*capitalizeFirst=*/false); + return (tblgenNamePrefix + Twine(varName)).str(); + }; + + body.indent(); + for (const NamedProperty &prop : emitHelper.getOp().getProperties()) { + Pred predicate = prop.prop.getPredicate(); + // Null predicate, nothing to verify. + if (predicate == Pred()) + continue; + + std::string rawCondition = predicate.getCondition(); + bool needsOp = StringRef(rawCondition).contains("$_op"); + if (needsOp && !emitHelper.isEmittingForOp()) + continue; + + std::string varName = getVarName(prop); + std::string getterName = + convertToCamelFromSnakeCase(prop.name, /*capitalizeFirst=*/true); + body << formatv(fetchProperty, varName, getterName, + prop.prop.getInterfaceType()); + body << formatv(verifyProperty, tgfmt(rawCondition, &ctx.withSelf(varName)), + emitHelper.emitErrorPrefix(), prop.name, + prop.prop.getSummary()); + } + body.unindent(); +} + /// Include declarations specified on NativeTrait static std::string formatExtraDeclarations(const Operator &op) { SmallVector<StringRef> extraDeclarations; @@ -3726,6 +3780,7 @@ void OpEmitter::genVerifier() { bool useProperties = emitHelper.hasProperties(); populateSubstitutions(emitHelper, verifyCtx); + genPropertyVerifier(emitHelper, verifyCtx, implBody); genAttributeVerifier(emitHelper, verifyCtx, implBody, staticVerifierEmitter, useProperties); genOperandResultVerifier(implBody, op.getOperands(), "operand"); _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits