https://github.com/krzysz00 created https://github.com/llvm/llvm-project/pull/132349
This commit improves the `EnumProp` class, causing it to wrap around an `EnumInfo` just like` EnumAttr` does. This EnumProp also has logic for converting to/from an integer attribute and for being read and written as bitcode. The following variants of `EnumProp` are provided: - `EnumPropWithAttrForm` - an EnumProp that can be constructed from (and will be converted to, if `storeInCustomAttribute` is true) a custom attribute, like an `EnumAttr`, instead of a plain integer. This is meant for backwards compatibility with code that uses enum attributes. `NamedEnumProp` adds a "`mnemonic` `<` $enum `>`" syntax around the enum, replicating a common pattern seen in MLIR printers and allowing for reduced ambiguity. `NamedEnumPropWithAttrForm` combines both of these extensions. (Sadly, bitcode auto-upgrade is hampered by the lack of the ability to optionally parse an attribute.) Depends on #132148 >From 17d981b8510f4bf0783edb409a592fe8ee8df5b7 Mon Sep 17 00:00:00 2001 From: Krzysztof Drewniak <krzysdrewn...@gmail.com> Date: Thu, 20 Mar 2025 23:08:31 -0700 Subject: [PATCH] [mlir] Improve EnumProp, making it take an EnumInfo This commit improves the `EnumProp` class, causing it to wrap around an `EnumInfo` just like` EnumAttr` does. This EnumProp also has logic for converting to/from an integer attribute and for being read and written as bitcode. The following variants of `EnumProp` are provided: - `EnumPropWithAttrForm` - an EnumProp that can be constructed from (and will be converted to, if `storeInCustomAttribute` is true) a custom attribute, like an `EnumAttr`, instead of a plain integer. This is meant for backwards compatibility with code that uses enum attributes. `NamedEnumProp` adds a "`mnemonic` `<` $enum `>`" syntax around the enum, replicating a common pattern seen in MLIR printers and allowing for reduced ambiguity. `NamedEnumPropWithAttrForm` combines both of these extensions. (Sadly, bitcode auto-upgrade is hampered by the lack of the ability to optionally parse an attribute.) --- mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td | 14 +- mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 8 +- mlir/include/mlir/IR/EnumAttr.td | 134 ++++++++++++++++++ mlir/include/mlir/IR/Properties.td | 19 --- mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 64 --------- mlir/test/IR/enum-attr-invalid.mlir | 75 ++++++++++ mlir/test/IR/enum-attr-roundtrip.mlir | 45 ++++++ mlir/test/lib/Dialect/Test/TestOps.td | 46 ++++++ 8 files changed, 313 insertions(+), 92 deletions(-) diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td index a9de787806452..34a30a00790ea 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td @@ -485,17 +485,16 @@ def DISubprogramFlags : I32BitEnumAttr< // IntegerOverflowFlags //===----------------------------------------------------------------------===// -def IOFnone : I32BitEnumAttrCaseNone<"none">; -def IOFnsw : I32BitEnumAttrCaseBit<"nsw", 0>; -def IOFnuw : I32BitEnumAttrCaseBit<"nuw", 1>; +def IOFnone : I32BitEnumCaseNone<"none">; +def IOFnsw : I32BitEnumCaseBit<"nsw", 0>; +def IOFnuw : I32BitEnumCaseBit<"nuw", 1>; -def IntegerOverflowFlags : I32BitEnumAttr< +def IntegerOverflowFlags : I32BitEnum< "IntegerOverflowFlags", "LLVM integer overflow flags", [IOFnone, IOFnsw, IOFnuw]> { let separator = ", "; let cppNamespace = "::mlir::LLVM"; - let genSpecializedAttr = 0; let printBitEnumPrimaryGroups = 1; } @@ -504,6 +503,11 @@ def LLVM_IntegerOverflowFlagsAttr : let assemblyFormat = "`<` $value `>`"; } +def LLVM_IntegerOverflowFlagsProp : + NamedEnumPropWithAttrForm<IntegerOverflowFlags, "overflow", LLVM_IntegerOverflowFlagsAttr> { + let defaultValue = enum.cppType # "::" # "none"; +} + //===----------------------------------------------------------------------===// // FastmathFlags //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index 90cc851c0a3b2..75f23e5b46c5f 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -60,7 +60,7 @@ class LLVM_IntArithmeticOpWithOverflowFlag<string mnemonic, string instName, list<Trait> traits = []> : LLVM_ArithmeticOpBase<AnySignlessInteger, mnemonic, instName, !listconcat([DeclareOpInterfaceMethods<IntegerOverflowFlagsInterface>], traits)> { - dag iofArg = (ins EnumProp<"IntegerOverflowFlags", "", "IntegerOverflowFlags::none">:$overflowFlags); + dag iofArg = (ins LLVM_IntegerOverflowFlagsProp:$overflowFlags); let arguments = !con(commonArgs, iofArg); string mlirBuilder = [{ @@ -69,7 +69,7 @@ class LLVM_IntArithmeticOpWithOverflowFlag<string mnemonic, string instName, $res = op; }]; let assemblyFormat = [{ - $lhs `,` $rhs `` custom<OverflowFlags>($overflowFlags) attr-dict `:` type($res) + $lhs `,` $rhs ($overflowFlags^)? attr-dict `:` type($res) }]; string llvmBuilder = "$res = builder.Create" # instName # @@ -563,10 +563,10 @@ class LLVM_CastOpWithOverflowFlag<string mnemonic, string instName, Type type, Type resultType, list<Trait> traits = []> : LLVM_Op<mnemonic, !listconcat([Pure], [DeclareOpInterfaceMethods<IntegerOverflowFlagsInterface>], traits)>, LLVM_Builder<"$res = builder.Create" # instName # "($arg, $_resultType, /*Name=*/\"\", op.hasNoUnsignedWrap(), op.hasNoSignedWrap());"> { - let arguments = (ins type:$arg, EnumProp<"IntegerOverflowFlags", "", "IntegerOverflowFlags::none">:$overflowFlags); + let arguments = (ins type:$arg, LLVM_IntegerOverflowFlagsProp:$overflowFlags); let results = (outs resultType:$res); let builders = [LLVM_OneResultOpBuilder]; - let assemblyFormat = "$arg `` custom<OverflowFlags>($overflowFlags) attr-dict `:` type($arg) `to` type($res)"; + let assemblyFormat = "$arg ($overflowFlags^)? attr-dict `:` type($arg) `to` type($res)"; string llvmInstName = instName; string mlirBuilder = [{ auto op = $_builder.create<$_qualCppClassName>( diff --git a/mlir/include/mlir/IR/EnumAttr.td b/mlir/include/mlir/IR/EnumAttr.td index e5406546b1950..aedda1d952eb6 100644 --- a/mlir/include/mlir/IR/EnumAttr.td +++ b/mlir/include/mlir/IR/EnumAttr.td @@ -10,6 +10,7 @@ #define ENUMATTR_TD include "mlir/IR/AttrTypeBase.td" +include "mlir/IR/Properties.td" //===----------------------------------------------------------------------===// // Enum attribute kinds @@ -551,6 +552,139 @@ class EnumAttr<Dialect dialect, EnumInfo enumInfo, string name = "", let assemblyFormat = "$value"; } +// A property wrapping by a C++ enum. This class will automatically create bytecode +// serialization logic for the given enum, as well as arranging for parser and +// printer calls. +class EnumProp<EnumInfo enumInfo> : Property<enumInfo.cppType, enumInfo.summary> { + EnumInfo enum = enumInfo; + + let description = enum.description; + let predicate = !if( + !isa<BitEnumBase>(enum), + CPred<"(static_cast<" # enum.underlyingType # ">($_self) & ~" # !cast<BitEnumBase>(enum).validBits # ") == 0">, + Or<!foreach(case, enum.enumerants, CPred<"$_self == " # enum.cppType # "::" # case.symbol>)>); + + let convertFromAttribute = [{ + auto intAttr = ::mlir::dyn_cast_if_present<::mlir::IntegerAttr>($_attr); + if (!intAttr) { + return $_diag() << "expected IntegerAttr storage for }] # + enum.cppType # [{"; + } + $_storage = static_cast<}] # enum.cppType # [{>(intAttr.getValue().getZExtValue()); + return ::mlir::success(); + }]; + + let convertToAttribute = [{ + return ::mlir::IntegerAttr::get(::mlir::IntegerType::get($_ctxt, }] # enum.bitwidth + # [{), static_cast<}] # enum.underlyingType #[{>($_storage)); + }]; + + let writeToMlirBytecode = [{ + $_writer.writeVarInt(static_cast<uint64_t>($_storage)); + }]; + + let readFromMlirBytecode = [{ + uint64_t rawValue; + if (::mlir::failed($_reader.readVarInt(rawValue))) + return ::mlir::failure(); + $_storage = static_cast<}] # enum.cppType # [{>(rawValue); + }]; + + let optionalParser = [{ + auto value = ::mlir::FieldParser<std::optional<}] # enum.cppType # [{>>::parse($_parser); + if (::mlir::failed(value)) + return ::mlir::failure(); + if (!(value->has_value())) + return std::nullopt; + $_storage = std::move(**value); + }]; +} + +// Enum property that can have been (or, if `storeInCustomAttribute` is true, will also +// be stored as) an attribute, in addition to being stored as an integer attribute. +class EnumPropWithAttrForm<EnumInfo enumInfo, Attr attributeForm> + : EnumProp<enumInfo> { + Attr attrForm = attributeForm; + bit storeInCustomAttribute = 0; + + let convertFromAttribute = [{ + auto customAttr = ::mlir::dyn_cast_if_present<}] + # attrForm.storageType # [{>($_attr); + if (customAttr) { + $_storage = customAttr.getValue(); + return ::mlir::success(); + } + auto intAttr = ::mlir::dyn_cast_if_present<::mlir::IntegerAttr>($_attr); + if (!intAttr) { + return $_diag() << "expected }] # attrForm.storageType + # [{ or IntegerAttr storage for }] # enum.cppType # [{"; + } + $_storage = static_cast<}] # enum.cppType # [{>(intAttr.getValue().getZExtValue()); + return ::mlir::success(); + }]; + + let convertToAttribute = !if(storeInCustomAttribute, [{ + return }] # attrForm.storageType # [{::get($_ctxt, $_storage); + }], [{ + return ::mlir::IntegerAttr::get(::mlir::IntegerType::get($_ctxt, }] # enumInfo.bitwidth + # [{), static_cast<}] # enum.underlyingType #[{>($_storage)); + }]); +} + +class _namedEnumPropFields<string cppType, string mnemonic> { + code parser = [{ + if ($_parser.parseKeyword("}] # mnemonic # [{") + || $_parser.parseLess()) { + return ::mlir::failure(); + } + auto parseRes = ::mlir::FieldParser<}] # cppType # [{>::parse($_parser); + if (::mlir::failed(parseRes) || + ::mlir::failed($_parser.parseGreater())) { + return ::mlir::failure(); + } + $_storage = *parseRes; + }]; + + code optionalParser = [{ + if ($_parser.parseOptionalKeyword("}] # mnemonic # [{")) { + return std::nullopt; + } + if ($_parser.parseLess()) { + return ::mlir::failure(); + } + auto parseRes = ::mlir::FieldParser<}] # cppType # [{>::parse($_parser); + if (::mlir::failed(parseRes) || + ::mlir::failed($_parser.parseGreater())) { + return ::mlir::failure(); + } + $_storage = *parseRes; + }]; + + code printer = [{ + $_printer << "}] # mnemonic # [{<" << $_storage << ">"; + }]; +} + +// An EnumProp which, when printed, is surrounded by mnemonic<>. +// For example, if the enum can be a, b, or c, and the mnemonic is foo, +// the format of this property will be "foo<a>", "foo<b>", or "foo<c>". +class NamedEnumProp<EnumInfo enumInfo, string name> + : EnumProp<enumInfo> { + string mnemonic = name; + let parser = _namedEnumPropFields<enum.cppType, mnemonic>.parser; + let optionalParser = _namedEnumPropFields<enum.cppType, mnemonic>.optionalParser; + let printer = _namedEnumPropFields<enum.cppType, mnemonic>.printer; +} + +// A `NamedEnumProp` with an attribute form as in `EnumPropWithAttrForm`. +class NamedEnumPropWithAttrForm<EnumInfo enumInfo, string name, Attr attributeForm> + : EnumPropWithAttrForm<enumInfo, attributeForm> { + string mnemonic = name; + let parser = _namedEnumPropFields<enum.cppType, mnemonic>.parser; + let optionalParser = _namedEnumPropFields<enum.cppType, mnemonic>.optionalParser; + let printer = _namedEnumPropFields<enumInfo.cppType, mnemonic>.printer; +} + class _symbolToValue<EnumInfo enumInfo, string case> { defvar cases = !filter(iter, enumInfo.enumerants, !eq(iter.str, case)); diff --git a/mlir/include/mlir/IR/Properties.td b/mlir/include/mlir/IR/Properties.td index 212b85876c8df..ef48d6e4f9d78 100644 --- a/mlir/include/mlir/IR/Properties.td +++ b/mlir/include/mlir/IR/Properties.td @@ -238,25 +238,6 @@ def I64Prop : IntProp<"int64_t">; def I32Property : IntProp<"int32_t">, Deprecated<"moved to shorter name I32Prop">; def I64Property : IntProp<"int64_t">, Deprecated<"moved to shorter name I64Prop">; -class EnumProp<string storageTypeParam, string desc = "", string default = ""> : - Property<storageTypeParam, desc> { - // TODO: implement predicate for enum validity. - let writeToMlirBytecode = [{ - $_writer.writeVarInt(static_cast<uint64_t>($_storage)); - }]; - let readFromMlirBytecode = [{ - uint64_t val; - if (failed($_reader.readVarInt(val))) - return ::mlir::failure(); - $_storage = static_cast<}] # storageTypeParam # [{>(val); - }]; - let defaultValue = default; -} - -class EnumProperty<string storageTypeParam, string desc = "", string default = ""> : - EnumProp<storageTypeParam, desc, default>, - Deprecated<"moved to shorter name EnumProp">; - // Note: only a class so we can deprecate the old name class _cls_StringProp : Property<"std::string", "string"> { let interfaceType = "::llvm::StringRef"; diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index 5370de501a85c..2c28eba198d91 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -49,70 +49,6 @@ using mlir::LLVM::tailcallkind::getMaxEnumValForTailCallKind; #include "mlir/Dialect/LLVMIR/LLVMOpsDialect.cpp.inc" -//===----------------------------------------------------------------------===// -// Property Helpers -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -// IntegerOverflowFlags - -namespace mlir { -static Attribute convertToAttribute(MLIRContext *ctx, - IntegerOverflowFlags flags) { - return IntegerOverflowFlagsAttr::get(ctx, flags); -} - -static LogicalResult -convertFromAttribute(IntegerOverflowFlags &flags, Attribute attr, - function_ref<InFlightDiagnostic()> emitError) { - auto flagsAttr = dyn_cast<IntegerOverflowFlagsAttr>(attr); - if (!flagsAttr) { - return emitError() << "expected 'overflowFlags' attribute to be an " - "IntegerOverflowFlagsAttr, but got " - << attr; - } - flags = flagsAttr.getValue(); - return success(); -} -} // namespace mlir - -static ParseResult parseOverflowFlags(AsmParser &p, - IntegerOverflowFlags &flags) { - if (failed(p.parseOptionalKeyword("overflow"))) { - flags = IntegerOverflowFlags::none; - return success(); - } - if (p.parseLess()) - return failure(); - do { - StringRef kw; - SMLoc loc = p.getCurrentLocation(); - if (p.parseKeyword(&kw)) - return failure(); - std::optional<IntegerOverflowFlags> flag = - symbolizeIntegerOverflowFlags(kw); - if (!flag) - return p.emitError(loc, - "invalid overflow flag: expected nsw, nuw, or none"); - flags = flags | *flag; - } while (succeeded(p.parseOptionalComma())); - return p.parseGreater(); -} - -static void printOverflowFlags(AsmPrinter &p, Operation *op, - IntegerOverflowFlags flags) { - if (flags == IntegerOverflowFlags::none) - return; - p << " overflow<"; - SmallVector<StringRef, 2> strs; - if (bitEnumContainsAny(flags, IntegerOverflowFlags::nsw)) - strs.push_back("nsw"); - if (bitEnumContainsAny(flags, IntegerOverflowFlags::nuw)) - strs.push_back("nuw"); - llvm::interleaveComma(strs, p); - p << ">"; -} - //===----------------------------------------------------------------------===// // Attribute Helpers //===----------------------------------------------------------------------===// diff --git a/mlir/test/IR/enum-attr-invalid.mlir b/mlir/test/IR/enum-attr-invalid.mlir index 923736f28dadb..2f240a56c9874 100644 --- a/mlir/test/IR/enum-attr-invalid.mlir +++ b/mlir/test/IR/enum-attr-invalid.mlir @@ -28,3 +28,78 @@ func.func @test_parse_invalid_attr() -> () { // expected-error@+1 {{failed to parse TestEnumAttr parameter 'value'}} test.op_with_enum 1 : index } + +// ----- + +func.func @test_non_keyword_prop_enum() -> () { + // expected-error@+2 {{expected keyword for a test enum}} + // expected-error@+1 {{invalid value for property value, expected a test enum}} + test.op_with_enum_prop 0 + return +} + +// ----- + +func.func @test_wrong_keyword_prop_enum() -> () { + // expected-error@+2 {{expected one of [first, second, third] for a test enum, got: fourth}} + // expected-error@+1 {{invalid value for property value, expected a test enum}} + test.op_with_enum_prop fourth +} + +// ----- + +func.func @test_bad_integer() -> () { + // expected-error@+1 {{op property 'value' failed to satisfy constraint: a test enum}} + "test.op_with_enum_prop"() <{value = 4 : i32}> {} : () -> () +} + +// ----- + +func.func @test_bit_enum_prop_not_keyword() -> () { + // expected-error@+2 {{expected keyword for a test bit enum}} + // expected-error@+1 {{invalid value for property value1, expected a test bit enum}} + test.op_with_bit_enum_prop 0 + return +} + +// ----- + +func.func @test_bit_enum_prop_wrong_keyword() -> () { + // expected-error@+2 {{expected one of [read, write, execute] for a test bit enum, got: chroot}} + // expected-error@+1 {{invalid value for property value1, expected a test bit enum}} + test.op_with_bit_enum_prop read, chroot : () + return +} + +// ----- + +func.func @test_bit_enum_prop_bad_value() -> () { + // expected-error@+1 {{op property 'value2' failed to satisfy constraint: a test bit enum}} + "test.op_with_bit_enum_prop"() <{value1 = 7 : i32, value2 = 8 : i32}> {} : () -> () + return +} + +// ----- + +func.func @test_bit_enum_prop_named_wrong_keyword() -> () { + // expected-error@+2 {{expected 'bit_enum'}} + // expected-error@+1 {{invalid value for property value1, expected a test bit enum}} + test.op_with_bit_enum_prop_named foo<read, execute> + return +} + +// ----- + +func.func @test_bit_enum_prop_named_not_open() -> () { + // expected-error@+2 {{expected '<'}} + // expected-error@+1 {{invalid value for property value1, expected a test bit enum}} + test.op_with_bit_enum_prop_named bit_enum read, execute> +} + +// ----- + +func.func @test_bit_enum_prop_named_not_closed() -> () { + // expected-error@+2 {{expected '>'}} + // expected-error@+1 {{invalid value for property value1, expected a test bit enum}} + test.op_with_bit_enum_prop_named bit_enum<read, execute + +} diff --git a/mlir/test/IR/enum-attr-roundtrip.mlir b/mlir/test/IR/enum-attr-roundtrip.mlir index 36e605bdbff4d..f1f09f977b7d9 100644 --- a/mlir/test/IR/enum-attr-roundtrip.mlir +++ b/mlir/test/IR/enum-attr-roundtrip.mlir @@ -35,3 +35,48 @@ func.func @test_match_op_with_bit_enum() -> () { test.op_with_bit_enum <execute, write> tag 0 : i32 return } + +// CHECK-LABEL: @test_enum_prop +func.func @test_enum_prop() -> () { + // CHECK: test.op_with_enum_prop first + test.op_with_enum_prop first + + // CHECK: test.op_with_enum_prop first + "test.op_with_enum_prop"() <{value = 0 : i32}> {} : () -> () + + // CHECK: test.op_with_enum_prop_attr_form <{value = 0 : i32}> + test.op_with_enum_prop_attr_form <{value = 0 : i32}> + // CHECK: test.op_with_enum_prop_attr_form <{value = 1 : i32}> + test.op_with_enum_prop_attr_form <{value = #test<enum second>}> + + // CHECK: test.op_with_enum_prop_attr_form_always <{value = #test<enum first>}> + test.op_with_enum_prop_attr_form_always <{value = #test<enum first>}> + // CHECK: test.op_with_enum_prop_attr_form_always <{value = #test<enum second>} + test.op_with_enum_prop_attr_form_always <{value = #test<enum second>}> + + return +} + +// CHECK-LABEL @test_bit_enum_prop() +func.func @test_bit_enum_prop() -> () { + // CHECK: test.op_with_bit_enum_prop read : () + test.op_with_bit_enum_prop read read : () + + // CHECK: test.op_with_bit_enum_prop read, write write, execute + test.op_with_bit_enum_prop read, write write, execute : () + + // CHECK: test.op_with_bit_enum_prop read, execute write + "test.op_with_bit_enum_prop"() <{value1 = 5 : i32, value2 = 2 : i32}> {} : () -> () + + // CHECK: test.op_with_bit_enum_prop read, write, execute + test.op_with_bit_enum_prop read, write, execute : () + + // CHECK: test.op_with_bit_enum_prop_named bit_enum<read>{{$}} + test.op_with_bit_enum_prop_named bit_enum<read> bit_enum<read> + // CHECK: test.op_with_bit_enum_prop_named bit_enum<read, write> bit_enum<write, execute> + test.op_with_bit_enum_prop_named bit_enum<read, write> bit_enum<write, execute> + // CHECK: test.op_with_bit_enum_prop_named bit_enum<read, write, execute> + test.op_with_bit_enum_prop_named bit_enum<read, write, execute> + + return +} diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td index 94c722038f1cc..f314720f7fab5 100644 --- a/mlir/test/lib/Dialect/Test/TestOps.td +++ b/mlir/test/lib/Dialect/Test/TestOps.td @@ -423,6 +423,52 @@ def : Pat<(OpWithEnum ConstantEnumCase<TestEnumAttr, "first">:$value, (OpWithEnum ConstantEnumCase<TestEnumAttr, "second">, ConstantAttr<I32Attr, "1">)>; +//===----------------------------------------------------------------------===// +// Test Enum Properties +//===----------------------------------------------------------------------===// + +// Define the enum property. +def TestEnumProp : EnumProp<TestEnum>; +// Define an op that contains the enum property. +def OpWithEnumProp : TEST_Op<"op_with_enum_prop"> { + let arguments = (ins TestEnumProp:$value); + let assemblyFormat = "$value attr-dict"; +} + +def TestEnumPropAttrForm : EnumPropWithAttrForm<TestEnum, TestEnumAttr>; +def OpWithEnumPropAttrForm : TEST_Op<"op_with_enum_prop_attr_form"> { + let arguments = (ins TestEnumPropAttrForm:$value); + let assemblyFormat = "prop-dict attr-dict"; +} + +def TestEnumPropAttrFormAlways : EnumPropWithAttrForm<TestEnum, TestEnumAttr> { + let storeInCustomAttribute = 1; +} +def OpWithEnumPropAttrFormAlways : TEST_Op<"op_with_enum_prop_attr_form_always"> { + let arguments = (ins TestEnumPropAttrFormAlways:$value); + let assemblyFormat = "prop-dict attr-dict"; +} + +def TestBitEnumProp : EnumProp<TestBitEnum> { + let defaultValue = TestBitEnum.cppType # "::Read"; +} +def OpWithTestBitEnum : TEST_Op<"op_with_bit_enum_prop"> { + let arguments = (ins + TestBitEnumProp:$value1, + TestBitEnumProp:$value2); + let assemblyFormat = "$value1 ($value2^)? attr-dict `:` `(``)`"; +} + +def TestBitEnumPropNamed : NamedEnumProp<TestBitEnum, "bit_enum"> { + let defaultValue = TestBitEnum.cppType # "::Read"; +} +def OpWithBitEnumPropNamed : TEST_Op<"op_with_bit_enum_prop_named"> { + let arguments = (ins + TestBitEnumPropNamed:$value1, + TestBitEnumPropNamed:$value2); + let assemblyFormat = "$value1 ($value2^)? attr-dict"; +} + //===----------------------------------------------------------------------===// // Test Bit Enum Attributes //===----------------------------------------------------------------------===// _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits