Skip to content

Commit d8399d5

Browse files
authored
[mlir] Add predicates to tablegen-defined properties (#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.
1 parent 5ca3794 commit d8399d5

File tree

10 files changed

+439
-40
lines changed

10 files changed

+439
-40
lines changed

mlir/include/mlir/IR/Constraints.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ class Or<list<Pred> children> : CombinedPred<PredCombinerOr, children>;
9393
// A predicate that holds if its child does not.
9494
class Neg<Pred child> : CombinedPred<PredCombinerNot, [child]>;
9595

96+
// A predicate that is always true.
97+
defvar TruePred = And<[]>;
98+
99+
// A predicate that is always false.
100+
defvar False = Or<[]>;
101+
96102
// A predicate that substitutes "pat" with "repl" in predicate calls of the
97103
// leaves of the predicate tree (i.e., not CombinedPred).
98104
//

mlir/include/mlir/IR/Properties.td

Lines changed: 100 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#ifndef PROPERTIES
1414
#define PROPERTIES
1515

16+
include "mlir/IR/Constraints.td"
17+
1618
// Base class for defining properties.
1719
class Property<string storageTypeParam = "", string desc = ""> {
1820
// User-readable one line summary used in error reporting messages. If empty,
@@ -63,6 +65,13 @@ class Property<string storageTypeParam = "", string desc = ""> {
6365
return convertFromAttribute($_storage, $_attr, $_diag);
6466
}];
6567

68+
// The verification predicate for this property. Defaults to the true predicate,
69+
// since properties are always their expected type.
70+
// Within the predicate, `$_self` is an instance of the **interface**
71+
// type of the property. Setting this field to ? will also result in a
72+
// true predicate but is not recommended, as it breaks composability.
73+
Pred predicate = TruePred;
74+
6675
// The call expression to hash the property.
6776
//
6877
// Format:
@@ -150,8 +159,8 @@ class Property<string storageTypeParam = "", string desc = ""> {
150159
return ::mlir::failure();
151160
}];
152161

153-
// Base definition for the property. (Will be) used for `OptionalProperty` and
154-
// such cases, analogously to `baseAttr`.
162+
// Base definition for the property. Used to look through `OptionalProperty`
163+
// for some format generation, as with the `baseAttr` field on attributes.
155164
Property baseProperty = ?;
156165

157166
// Default value for the property within its storage. This should be an expression
@@ -224,8 +233,7 @@ def I64Property : IntProperty<"int64_t">;
224233

225234
class EnumProperty<string storageTypeParam, string desc = "", string default = ""> :
226235
Property<storageTypeParam, desc> {
227-
// TODO: take advantage of EnumAttrInfo and the like to make this share nice
228-
// parsing code with EnumAttr.
236+
// TODO: implement predicate for enum validity.
229237
let writeToMlirBytecode = [{
230238
$_writer.writeVarInt(static_cast<uint64_t>($_storage));
231239
}];
@@ -330,6 +338,59 @@ def UnitProperty : Property<"bool", "unit property"> {
330338
}];
331339
}
332340

341+
//===----------------------------------------------------------------------===//
342+
// Property field overwrites
343+
344+
/// Class for giving a property a default value.
345+
/// This doesn't change anything about the property other than giving it a default
346+
/// which can be used by ODS to elide printing.
347+
class DefaultValuedProperty<Property p, string default = "", string storageDefault = ""> : Property<p.storageType, p.summary> {
348+
let defaultValue = default;
349+
let storageTypeValueOverride = storageDefault;
350+
let baseProperty = p;
351+
// Keep up to date with `Property` above.
352+
let summary = p.summary;
353+
let description = p.description;
354+
let storageType = p.storageType;
355+
let interfaceType = p.interfaceType;
356+
let convertFromStorage = p.convertFromStorage;
357+
let assignToStorage = p.assignToStorage;
358+
let convertToAttribute = p.convertToAttribute;
359+
let convertFromAttribute = p.convertFromAttribute;
360+
let predicate = p.predicate;
361+
let hashProperty = p.hashProperty;
362+
let parser = p.parser;
363+
let optionalParser = p.optionalParser;
364+
let printer = p.printer;
365+
let readFromMlirBytecode = p.readFromMlirBytecode;
366+
let writeToMlirBytecode = p.writeToMlirBytecode;
367+
}
368+
369+
/// Apply the predicate `pred` to the property `p`, ANDing it with any
370+
/// predicates it may already have. If `newSummary` is provided, replace the
371+
/// summary of `p` with `newSummary`.
372+
class ConfinedProperty<Property p, Pred pred, string newSummary = "">
373+
: Property<p.storageType, !if(!empty(newSummary), p.summary, newSummary)> {
374+
let predicate = !if(!ne(p.predicate, TruePred), And<[p.predicate, pred]>, pred);
375+
let baseProperty = p;
376+
// Keep up to date with `Property` above.
377+
let description = p.description;
378+
let storageType = p.storageType;
379+
let interfaceType = p.interfaceType;
380+
let convertFromStorage = p.convertFromStorage;
381+
let assignToStorage = p.assignToStorage;
382+
let convertToAttribute = p.convertToAttribute;
383+
let convertFromAttribute = p.convertFromAttribute;
384+
let hashProperty = p.hashProperty;
385+
let parser = p.parser;
386+
let optionalParser = p.optionalParser;
387+
let printer = p.printer;
388+
let readFromMlirBytecode = p.readFromMlirBytecode;
389+
let writeToMlirBytecode = p.writeToMlirBytecode;
390+
let defaultValue = p.defaultValue;
391+
let storageTypeValueOverride = p.storageTypeValueOverride;
392+
}
393+
333394
//===----------------------------------------------------------------------===//
334395
// Primitive property combinators
335396

@@ -342,14 +403,35 @@ class _makePropStorage<Property prop, string name> {
342403
true : "") # ";";
343404
}
344405

406+
/// Construct a `Pred`icate `ret` that wraps the predicate of the underlying
407+
/// property `childProp` with:
408+
///
409+
/// [](childProp.storageType& s) {
410+
/// return [](childProp.interfaceType i) {
411+
/// return leafSubst(childProp.predicate, "$_self" to "i");
412+
/// }(childProp.convertFromStorage(s))
413+
/// }
414+
///
415+
/// and then appends `prefix` and `suffix`.
416+
class _makeStorageWrapperPred<Property wrappedProp> {
417+
Pred ret =
418+
Concat<
419+
"[](" # "const " # wrappedProp.storageType
420+
# "& baseStore) -> bool { return []("
421+
# wrappedProp.interfaceType # " baseIface) -> bool { return (",
422+
SubstLeaves<"$_self", "baseIface", wrappedProp.predicate>,
423+
"); }(" # !subst("$_storage", "baseStore", wrappedProp.convertFromStorage)
424+
# "); }">;
425+
}
426+
345427
/// The generic class for arrays of some other property, which is stored as a
346428
/// `SmallVector` of that property. This uses an `ArrayAttr` as its attribute form
347429
/// though subclasses can override this, as is the case with IntArrayAttr below.
348430
/// Those wishing to use a non-default number of SmallVector elements should
349431
/// subclass `ArrayProperty`.
350-
class ArrayProperty<Property elem = Property<>, string desc = ""> :
351-
Property<"::llvm::SmallVector<" # elem.storageType # ">", desc> {
352-
let summary = "array of " # elem.summary;
432+
class ArrayProperty<Property elem = Property<>, string newSummary = ""> :
433+
Property<"::llvm::SmallVector<" # elem.storageType # ">",
434+
!if(!empty(newSummary), "array of " # elem.summary, newSummary)> {
353435
let interfaceType = "::llvm::ArrayRef<" # elem.storageType # ">";
354436
let convertFromStorage = "::llvm::ArrayRef<" # elem.storageType # ">{$_storage}";
355437
let assignToStorage = "$_storage.assign($_value.begin(), $_value.end())";
@@ -382,6 +464,10 @@ class ArrayProperty<Property elem = Property<>, string desc = ""> :
382464
return ::mlir::ArrayAttr::get($_ctxt, elems);
383465
}];
384466

467+
let predicate = !if(!eq(elem.predicate, TruePred),
468+
TruePred,
469+
Concat<"::llvm::all_of($_self, ", _makeStorageWrapperPred<elem>.ret, ")">);
470+
385471
defvar theParserBegin = [{
386472
auto& storage = $_storage;
387473
auto parseElemFn = [&]() -> ::mlir::ParseResult {
@@ -463,8 +549,8 @@ class ArrayProperty<Property elem = Property<>, string desc = ""> :
463549
}]);
464550
}
465551

466-
class IntArrayProperty<string storageTypeParam = "", string desc = ""> :
467-
ArrayProperty<IntProperty<storageTypeParam, desc>> {
552+
class IntArrayProperty<Property elem, string newSummary=""> :
553+
ArrayProperty<elem, newSummary> {
468554
// Bring back the trivial conversions we don't get in the general case.
469555
let convertFromAttribute = [{
470556
return convertFromAttribute($_storage, $_attr, $_diag);
@@ -474,30 +560,6 @@ class IntArrayProperty<string storageTypeParam = "", string desc = ""> :
474560
}];
475561
}
476562

477-
/// Class for giving a property a default value.
478-
/// This doesn't change anything about the property other than giving it a default
479-
/// which can be used by ODS to elide printing.
480-
class DefaultValuedProperty<Property p, string default = "", string storageDefault = ""> : Property<p.storageType, p.summary> {
481-
let defaultValue = default;
482-
let storageTypeValueOverride = storageDefault;
483-
let baseProperty = p;
484-
// Keep up to date with `Property` above.
485-
let summary = p.summary;
486-
let description = p.description;
487-
let storageType = p.storageType;
488-
let interfaceType = p.interfaceType;
489-
let convertFromStorage = p.convertFromStorage;
490-
let assignToStorage = p.assignToStorage;
491-
let convertToAttribute = p.convertToAttribute;
492-
let convertFromAttribute = p.convertFromAttribute;
493-
let hashProperty = p.hashProperty;
494-
let parser = p.parser;
495-
let optionalParser = p.optionalParser;
496-
let printer = p.printer;
497-
let readFromMlirBytecode = p.readFromMlirBytecode;
498-
let writeToMlirBytecode = p.writeToMlirBytecode;
499-
}
500-
501563
/// An optional property, stored as an std::optional<p.storageType>
502564
/// interfaced with as an std::optional<p.interfaceType>..
503565
/// The syntax is `none` (or empty string if elided) for an absent value or
@@ -575,6 +637,11 @@ class OptionalProperty<Property p, bit canDelegateParsing = 1>
575637
return ::mlir::ArrayAttr::get($_ctxt, {attr});
576638
}];
577639

640+
let predicate = !if(!ne(p.predicate, TruePred),
641+
Or<[CPred<"!$_self.has_value()">,
642+
SubstLeaves<"$_self", "(*($_self))", p.predicate>]>,
643+
TruePred);
644+
578645
defvar delegatedParserBegin = [{
579646
if (::mlir::succeeded($_parser.parseOptionalKeyword("none"))) {
580647
$_storage = std::nullopt;

mlir/include/mlir/TableGen/Property.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ namespace mlir {
2727
namespace tblgen {
2828
class Dialect;
2929
class Type;
30+
class Pred;
3031

3132
// Wrapper class providing helper methods for accessing MLIR Property defined
3233
// in TableGen. This class should closely reflect what is defined as class
@@ -74,6 +75,10 @@ class Property {
7475
return convertFromAttributeCall;
7576
}
7677

78+
// Return the property's predicate. Properties that didn't come from
79+
// tablegen (the hardcoded ones) have the null predicate.
80+
Pred getPredicate() const;
81+
7782
// Returns the method call which parses this property from textual MLIR.
7883
StringRef getParserCall() const { return parserCall; }
7984

mlir/lib/TableGen/Predicate.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,16 @@ propagateGroundTruth(PredNode *node,
235235
return node;
236236
}
237237

238+
if (node->kind == PredCombinerKind::And && node->children.empty()) {
239+
node->kind = PredCombinerKind::True;
240+
return node;
241+
}
242+
243+
if (node->kind == PredCombinerKind::Or && node->children.empty()) {
244+
node->kind = PredCombinerKind::False;
245+
return node;
246+
}
247+
238248
// Otherwise, look at child nodes.
239249

240250
// Move child nodes into some local variable so that they can be optimized

mlir/lib/TableGen/Property.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "mlir/TableGen/Property.h"
1515
#include "mlir/TableGen/Format.h"
1616
#include "mlir/TableGen/Operator.h"
17+
#include "mlir/TableGen/Predicate.h"
1718
#include "llvm/TableGen/Record.h"
1819

1920
using namespace mlir;
@@ -68,8 +69,8 @@ Property::Property(StringRef summary, StringRef description,
6869
StringRef writeToMlirBytecodeCall,
6970
StringRef hashPropertyCall, StringRef defaultValue,
7071
StringRef storageTypeValueOverride)
71-
: summary(summary), description(description), storageType(storageType),
72-
interfaceType(interfaceType),
72+
: def(nullptr), summary(summary), description(description),
73+
storageType(storageType), interfaceType(interfaceType),
7374
convertFromStorageCall(convertFromStorageCall),
7475
assignToStorageCall(assignToStorageCall),
7576
convertToAttributeCall(convertToAttributeCall),
@@ -91,6 +92,15 @@ StringRef Property::getPropertyDefName() const {
9192
return def->getName();
9293
}
9394

95+
Pred Property::getPredicate() const {
96+
if (!def)
97+
return Pred();
98+
const llvm::RecordVal *maybePred = def->getValue("predicate");
99+
if (!maybePred || !maybePred->getValue())
100+
return Pred();
101+
return Pred(maybePred->getValue());
102+
}
103+
94104
Property Property::getBaseProperty() const {
95105
if (const auto *defInit =
96106
llvm::dyn_cast<llvm::DefInit>(def->getValueInit("baseProperty"))) {

0 commit comments

Comments
 (0)