[Mlir-commits] [mlir] e5a8c8c - [mlir] Refactoring a few Parser APIs

Mehdi Amini llvmlistbot at llvm.org
Mon Nov 22 22:13:04 PST 2021


Author: Sandeep Dasgupta
Date: 2021-11-23T06:11:01Z
New Revision: e5a8c8c883f1f3f91f40c883dd4f613aca0f7105

URL: https://github.com/llvm/llvm-project/commit/e5a8c8c883f1f3f91f40c883dd4f613aca0f7105
DIFF: https://github.com/llvm/llvm-project/commit/e5a8c8c883f1f3f91f40c883dd4f613aca0f7105.diff

LOG: [mlir] Refactoring a few Parser APIs

Refactored two new parser APIs parseGenericOperationAfterOperands and
 parseCustomOperationName out of parseGenericOperation and parseCustomOperation.

Motivation: Sometimes an op can be printed in a special way if certain criteria
is met. While parsing, we need to handle all the versions.
`parseGenericOperationAfterOperands` is handy in situation where we already
parsed the operands and decide to fall back to default parsing.

`parseCustomOperationName` is useful when we need to know details (dialect,
operation name etc.) about a parsed token meant to be an mlir operation.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D113719

Added: 
    mlir/test/IR/pretty_printed_region_op.mlir

Modified: 
    mlir/include/mlir/IR/OpImplementation.h
    mlir/lib/Parser/Parser.cpp
    mlir/test/lib/Dialect/Test/TestDialect.cpp
    mlir/test/lib/Dialect/Test/TestOps.td

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/IR/OpImplementation.h b/mlir/include/mlir/IR/OpImplementation.h
index 34e6cd08ea3c7..dab6e106f9512 100644
--- a/mlir/include/mlir/IR/OpImplementation.h
+++ b/mlir/include/mlir/IR/OpImplementation.h
@@ -907,6 +907,10 @@ class OpAsmParser : public AsmParser {
   virtual Operation *parseGenericOperation(Block *insertBlock,
                                            Block::iterator insertPt) = 0;
 
+  /// Parse the name of an operation, in the custom form. On success, return a
+  /// an object of type 'OperationName'. Otherwise, failure is returned.
+  virtual FailureOr<OperationName> parseCustomOperationName() = 0;
+
   //===--------------------------------------------------------------------===//
   // Operand Parsing
   //===--------------------------------------------------------------------===//
@@ -918,6 +922,20 @@ class OpAsmParser : public AsmParser {
     unsigned number;      // Number, e.g. 12 for an operand like %xyz#12
   };
 
+  /// Parse 
diff erent components, viz., use-info of operand(s), successor(s),
+  /// region(s), attribute(s) and function-type, of the generic form of an
+  /// operation instance and populate the input operation-state 'result' with
+  /// those components. If any of the components is explicitly provided, then
+  /// skip parsing that component.
+  virtual ParseResult parseGenericOperationAfterOpName(
+      OperationState &result,
+      Optional<ArrayRef<OperandType>> parsedOperandType = llvm::None,
+      Optional<ArrayRef<Block *>> parsedSuccessors = llvm::None,
+      Optional<MutableArrayRef<std::unique_ptr<Region>>> parsedRegions =
+          llvm::None,
+      Optional<ArrayRef<NamedAttribute>> parsedAttributes = llvm::None,
+      Optional<FunctionType> parsedFnType = llvm::None) = 0;
+
   /// Parse a single operand.
   virtual ParseResult parseOperand(OperandType &result) = 0;
 

diff  --git a/mlir/lib/Parser/Parser.cpp b/mlir/lib/Parser/Parser.cpp
index a1ba6a8010dbe..1818e420edd01 100644
--- a/mlir/lib/Parser/Parser.cpp
+++ b/mlir/lib/Parser/Parser.cpp
@@ -310,6 +310,20 @@ class OperationParser : public Parser {
   /// Parse an operation instance that is in the generic form.
   Operation *parseGenericOperation();
 
+  /// Parse 
diff erent components, viz., use-info of operand(s), successor(s),
+  /// region(s), attribute(s) and function-type, of the generic form of an
+  /// operation instance and populate the input operation-state 'result' with
+  /// those components. If any of the components is explicitly provided, then
+  /// skip parsing that component.
+  ParseResult parseGenericOperationAfterOpName(
+      OperationState &result,
+      Optional<ArrayRef<SSAUseInfo>> parsedOperandUseInfo = llvm::None,
+      Optional<ArrayRef<Block *>> parsedSuccessors = llvm::None,
+      Optional<MutableArrayRef<std::unique_ptr<Region>>> parsedRegions =
+          llvm::None,
+      Optional<ArrayRef<NamedAttribute>> parsedAttributes = llvm::None,
+      Optional<FunctionType> parsedFnType = llvm::None);
+
   /// Parse an operation instance that is in the generic form and insert it at
   /// the provided insertion point.
   Operation *parseGenericOperation(Block *insertBlock,
@@ -335,6 +349,10 @@ class OperationParser : public Parser {
   /// resultInfo specifies information about the "%name =" specifiers.
   Operation *parseCustomOperation(ArrayRef<ResultRecord> resultIDs);
 
+  /// Parse the name of an operation, in the custom form. On success, return a
+  /// an object of type 'OperationName'. Otherwise, failure is returned.
+  FailureOr<OperationName> parseCustomOperationName();
+
   //===--------------------------------------------------------------------===//
   // Region Parsing
   //===--------------------------------------------------------------------===//
@@ -972,6 +990,105 @@ struct CleanupOpStateRegions {
 };
 } // namespace
 
+ParseResult OperationParser::parseGenericOperationAfterOpName(
+    OperationState &result, Optional<ArrayRef<SSAUseInfo>> parsedOperandUseInfo,
+    Optional<ArrayRef<Block *>> parsedSuccessors,
+    Optional<MutableArrayRef<std::unique_ptr<Region>>> parsedRegions,
+    Optional<ArrayRef<NamedAttribute>> parsedAttributes,
+    Optional<FunctionType> parsedFnType) {
+
+  // Parse the operand list, if not explicitly provided.
+  SmallVector<SSAUseInfo, 8> opInfo;
+  if (!parsedOperandUseInfo) {
+    if (parseToken(Token::l_paren, "expected '(' to start operand list") ||
+        parseOptionalSSAUseList(opInfo) ||
+        parseToken(Token::r_paren, "expected ')' to end operand list")) {
+      return failure();
+    }
+    parsedOperandUseInfo = opInfo;
+  }
+
+  // Parse the successor list, if not explicitly provided.
+  if (!parsedSuccessors) {
+    if (getToken().is(Token::l_square)) {
+      // Check if the operation is not a known terminator.
+      if (!result.name.mightHaveTrait<OpTrait::IsTerminator>())
+        return emitError("successors in non-terminator");
+
+      SmallVector<Block *, 2> successors;
+      if (parseSuccessors(successors))
+        return failure();
+      result.addSuccessors(successors);
+    }
+  } else {
+    result.addSuccessors(*parsedSuccessors);
+  }
+
+  // Parse the region list, if not explicitly provided.
+  if (!parsedRegions) {
+    if (consumeIf(Token::l_paren)) {
+      do {
+        // Create temporary regions with the top level region as parent.
+        result.regions.emplace_back(new Region(topLevelOp));
+        if (parseRegion(*result.regions.back(), /*entryArguments=*/{}))
+          return failure();
+      } while (consumeIf(Token::comma));
+      if (parseToken(Token::r_paren, "expected ')' to end region list"))
+        return failure();
+    }
+  } else {
+    result.addRegions(*parsedRegions);
+  }
+
+  // Parse the attributes, if not explicitly provided.
+  if (!parsedAttributes) {
+    if (getToken().is(Token::l_brace)) {
+      if (parseAttributeDict(result.attributes))
+        return failure();
+    }
+  } else {
+    result.addAttributes(*parsedAttributes);
+  }
+
+  // Parse the operation type, if not explicitly provided.
+  Location typeLoc = result.location;
+  if (!parsedFnType) {
+    if (parseToken(Token::colon, "expected ':' followed by operation type"))
+      return failure();
+
+    typeLoc = getEncodedSourceLocation(getToken().getLoc());
+    auto type = parseType();
+    if (!type)
+      return failure();
+    auto fnType = type.dyn_cast<FunctionType>();
+    if (!fnType)
+      return mlir::emitError(typeLoc, "expected function type");
+
+    parsedFnType = fnType;
+  }
+
+  result.addTypes(parsedFnType->getResults());
+
+  // Check that we have the right number of types for the operands.
+  ArrayRef<Type> operandTypes = parsedFnType->getInputs();
+  if (operandTypes.size() != parsedOperandUseInfo->size()) {
+    auto plural = "s"[parsedOperandUseInfo->size() == 1];
+    return mlir::emitError(typeLoc, "expected ")
+           << parsedOperandUseInfo->size() << " operand type" << plural
+           << " but had " << operandTypes.size();
+  }
+
+  // Resolve all of the operands.
+  for (unsigned i = 0, e = parsedOperandUseInfo->size(); i != e; ++i) {
+    result.operands.push_back(
+        resolveSSAUse((*parsedOperandUseInfo)[i], operandTypes[i]));
+    if (!result.operands.back())
+      return failure();
+  }
+
+  return success();
+}
+
 Operation *OperationParser::parseGenericOperation() {
   // Get location information for the operation.
   auto srcLocation = getEncodedSourceLocation(getToken().getLoc());
@@ -985,6 +1102,7 @@ Operation *OperationParser::parseGenericOperation() {
   consumeToken(Token::string);
 
   OperationState result(srcLocation, name);
+  CleanupOpStateRegions guard{result};
 
   // Lazy load dialects in the context as needed.
   if (!result.name.isRegistered()) {
@@ -1005,74 +1123,9 @@ Operation *OperationParser::parseGenericOperation() {
   if (state.asmState)
     state.asmState->startOperationDefinition(result.name);
 
-  // Parse the operand list.
-  SmallVector<SSAUseInfo, 8> operandInfos;
-  if (parseToken(Token::l_paren, "expected '(' to start operand list") ||
-      parseOptionalSSAUseList(operandInfos) ||
-      parseToken(Token::r_paren, "expected ')' to end operand list")) {
-    return nullptr;
-  }
-
-  // Parse the successor list.
-  if (getToken().is(Token::l_square)) {
-    // Check if the operation is not a known terminator.
-    if (!result.name.mightHaveTrait<OpTrait::IsTerminator>())
-      return emitError("successors in non-terminator"), nullptr;
-
-    SmallVector<Block *, 2> successors;
-    if (parseSuccessors(successors))
-      return nullptr;
-    result.addSuccessors(successors);
-  }
-
-  // Parse the region list.
-  CleanupOpStateRegions guard{result};
-  if (consumeIf(Token::l_paren)) {
-    do {
-      // Create temporary regions with the top level region as parent.
-      result.regions.emplace_back(new Region(topLevelOp));
-      if (parseRegion(*result.regions.back(), /*entryArguments=*/{}))
-        return nullptr;
-    } while (consumeIf(Token::comma));
-    if (parseToken(Token::r_paren, "expected ')' to end region list"))
-      return nullptr;
-  }
-
-  if (getToken().is(Token::l_brace)) {
-    if (parseAttributeDict(result.attributes))
-      return nullptr;
-  }
-
-  if (parseToken(Token::colon, "expected ':' followed by operation type"))
+  if (parseGenericOperationAfterOpName(result))
     return nullptr;
 
-  auto typeLoc = getToken().getLoc();
-  auto type = parseType();
-  if (!type)
-    return nullptr;
-  auto fnType = type.dyn_cast<FunctionType>();
-  if (!fnType)
-    return (emitError(typeLoc, "expected function type"), nullptr);
-
-  result.addTypes(fnType.getResults());
-
-  // Check that we have the right number of types for the operands.
-  auto operandTypes = fnType.getInputs();
-  if (operandTypes.size() != operandInfos.size()) {
-    auto plural = "s"[operandInfos.size() == 1];
-    return (emitError(typeLoc, "expected ")
-                << operandInfos.size() << " operand type" << plural
-                << " but had " << operandTypes.size(),
-            nullptr);
-  }
-
-  // Resolve all of the operands.
-  for (unsigned i = 0, e = operandInfos.size(); i != e; ++i) {
-    result.operands.push_back(resolveSSAUse(operandInfos[i], operandTypes[i]));
-    if (!result.operands.back())
-      return nullptr;
-  }
-
   // Create the operation and try to parse a location for it.
   Operation *op = opBuilder.createOperation(result);
   if (parseTrailingLocationSpecifier(op))
@@ -1133,6 +1186,37 @@ class CustomOpAsmParser : public AsmParserImpl<OpAsmParser> {
     return parser.parseGenericOperation(insertBlock, insertPt);
   }
 
+  FailureOr<OperationName> parseCustomOperationName() final {
+    return parser.parseCustomOperationName();
+  }
+
+  ParseResult parseGenericOperationAfterOpName(
+      OperationState &result,
+      Optional<ArrayRef<OperandType>> parsedOperandTypes,
+      Optional<ArrayRef<Block *>> parsedSuccessors,
+      Optional<MutableArrayRef<std::unique_ptr<Region>>> parsedRegions,
+      Optional<ArrayRef<NamedAttribute>> parsedAttributes,
+      Optional<FunctionType> parsedFnType) final {
+
+    // TODO: The types, OperandType and SSAUseInfo, both share the same members
+    // but in 
diff erent order. It would be cleaner to make one alias of the
+    // other, making the following code redundant.
+    SmallVector<OperationParser::SSAUseInfo> parsedOperandUseInfo;
+    if (parsedOperandTypes) {
+      for (const OperandType &parsedOperandType : *parsedOperandTypes)
+        parsedOperandUseInfo.push_back({
+            parsedOperandType.name,
+            parsedOperandType.number,
+            parsedOperandType.location,
+        });
+    }
+
+    return parser.parseGenericOperationAfterOpName(
+        result,
+        parsedOperandTypes ? llvm::makeArrayRef(parsedOperandUseInfo)
+                           : llvm::None,
+        parsedSuccessors, parsedRegions, parsedAttributes, parsedFnType);
+  }
   //===--------------------------------------------------------------------===//
   // Utilities
   //===--------------------------------------------------------------------===//
@@ -1506,10 +1590,13 @@ class CustomOpAsmParser : public AsmParserImpl<OpAsmParser> {
 };
 } // end anonymous namespace.
 
-Operation *
-OperationParser::parseCustomOperation(ArrayRef<ResultRecord> resultIDs) {
-  llvm::SMLoc opLoc = getToken().getLoc();
+FailureOr<OperationName> OperationParser::parseCustomOperationName() {
   std::string opName = getTokenSpelling().str();
+  if (opName.empty())
+    return (emitError("empty operation name is invalid"), failure());
+
+  consumeToken();
+
   Optional<RegisteredOperationName> opInfo =
       RegisteredOperationName::lookup(opName, getContext());
   StringRef defaultDialect = getState().defaultDialectStack.back();
@@ -1543,13 +1630,28 @@ OperationParser::parseCustomOperation(ArrayRef<ResultRecord> resultIDs) {
     }
   }
 
+  return OperationName(opName, getContext());
+}
+
+Operation *
+OperationParser::parseCustomOperation(ArrayRef<ResultRecord> resultIDs) {
+  llvm::SMLoc opLoc = getToken().getLoc();
+
+  FailureOr<OperationName> opNameInfo = parseCustomOperationName();
+  if (failed(opNameInfo))
+    return nullptr;
+
+  StringRef opName = opNameInfo->getStringRef();
+  Dialect *dialect = opNameInfo->getDialect();
+  Optional<RegisteredOperationName> opInfo = opNameInfo->getRegisteredInfo();
+
   // This is the actual hook for the custom op parsing, usually implemented by
   // the op itself (`Op::parse()`). We retrieve it either from the
   // RegisteredOperationName or from the Dialect.
   function_ref<ParseResult(OpAsmParser &, OperationState &)> parseAssemblyFn;
   bool isIsolatedFromAbove = false;
 
-  defaultDialect = "";
+  StringRef defaultDialect = "";
   if (opInfo) {
     parseAssemblyFn = opInfo->getParseAssemblyFn();
     isIsolatedFromAbove = opInfo->hasTrait<OpTrait::IsIsolatedFromAbove>();
@@ -1570,16 +1672,14 @@ OperationParser::parseCustomOperation(ArrayRef<ResultRecord> resultIDs) {
   auto restoreDefaultDialect = llvm::make_scope_exit(
       [&]() { getState().defaultDialectStack.pop_back(); });
 
-  consumeToken();
-
   // If the custom op parser crashes, produce some indication to help
   // debugging.
   llvm::PrettyStackTraceFormat fmt("MLIR Parser: custom op parser '%s'",
-                                   opName.c_str());
+                                   opNameInfo->getIdentifier().data());
 
   // Get location information for the operation.
   auto srcLocation = getEncodedSourceLocation(opLoc);
-  OperationState opState(srcLocation, opName);
+  OperationState opState(srcLocation, *opNameInfo);
 
   // If we are populating the parser state, start a new operation definition.
   if (state.asmState)

diff  --git a/mlir/test/IR/pretty_printed_region_op.mlir b/mlir/test/IR/pretty_printed_region_op.mlir
new file mode 100644
index 0000000000000..c12b26de4cc6b
--- /dev/null
+++ b/mlir/test/IR/pretty_printed_region_op.mlir
@@ -0,0 +1,35 @@
+// RUN: mlir-opt -allow-unregistered-dialect -split-input-file  %s | FileCheck %s --check-prefixes=CHECK-CUSTOM,CHECK
+// RUN: mlir-opt -allow-unregistered-dialect -mlir-print-op-generic -split-input-file   %s | FileCheck %s --check-prefixes=CHECK,CHECK-GENERIC
+
+// -----
+
+func @pretty_printed_region_op(%arg0 : f32, %arg1 : f32) -> (f32) {
+// CHECK-CUSTOM:  test.pretty_printed_region %arg1, %arg0 start special.op end : (f32, f32) -> f32
+// CHECK-GENERIC: "test.pretty_printed_region"(%arg1, %arg0)
+// CHECK-GENERIC:   ^bb0(%arg[[x:[0-9]+]]: f32, %arg[[y:[0-9]+]]: f32
+// CHECK-GENERIC:     %[[RES:.*]] = "special.op"(%arg[[x]], %arg[[y]]) : (f32, f32) -> f32
+// CHECK-GENERIC:     "test.return"(%[[RES]]) : (f32) -> ()
+// CHECK-GENERIC:  : (f32, f32) -> f32
+
+  %res = test.pretty_printed_region %arg1, %arg0 start special.op end : (f32, f32) -> (f32) loc("some_NameLoc")
+  return %res : f32
+}
+
+// -----
+
+func @pretty_printed_region_op(%arg0 : f32, %arg1 : f32) -> (f32) {
+// CHECK-CUSTOM:  test.pretty_printed_region %arg1, %arg0
+// CHECK-GENERIC: "test.pretty_printed_region"(%arg1, %arg0)
+// CHECK:          ^bb0(%arg[[x:[0-9]+]]: f32, %arg[[y:[0-9]+]]: f32):
+// CHECK:            %[[RES:.*]] = "non.special.op"(%arg[[x]], %arg[[y]]) : (f32, f32) -> f32
+// CHECK:            "test.return"(%[[RES]]) : (f32) -> ()
+// CHECK:          : (f32, f32) -> f32
+
+  %0 = test.pretty_printed_region %arg1, %arg0 ( {
+    ^bb0(%arg2: f32, %arg3: f32):
+      %1 = "non.special.op"(%arg2, %arg3) : (f32, f32) -> f32
+      "test.return"(%1) : (f32) -> ()
+    }) : (f32, f32) -> f32
+    return %0 : f32
+}
+

diff  --git a/mlir/test/lib/Dialect/Test/TestDialect.cpp b/mlir/test/lib/Dialect/Test/TestDialect.cpp
index e045bb91ab18b..cb09ea45795b0 100644
--- a/mlir/test/lib/Dialect/Test/TestDialect.cpp
+++ b/mlir/test/lib/Dialect/Test/TestDialect.cpp
@@ -720,6 +720,107 @@ static void print(OpAsmPrinter &p, WrappingRegionOp op) {
   p.printGenericOp(&op.getRegion().front().front());
 }
 
+//===----------------------------------------------------------------------===//
+// Test PrettyPrintedRegionOp -  exercising the following parser APIs
+//   parseGenericOperationAfterOpName
+//   parseCustomOperationName
+//===----------------------------------------------------------------------===//
+
+static ParseResult parsePrettyPrintedRegionOp(OpAsmParser &parser,
+                                              OperationState &result) {
+
+  llvm::SMLoc loc = parser.getCurrentLocation();
+  Location currLocation = parser.getEncodedSourceLoc(loc);
+
+  // Parse the operands.
+  SmallVector<OpAsmParser::OperandType, 2> operands;
+  if (parser.parseOperandList(operands))
+    return failure();
+
+  // Check if we are parsing the pretty-printed version
+  //  test.pretty_printed_region start <inner-op> end : <functional-type>
+  // Else fallback to parsing the "non pretty-printed" version.
+  if (!succeeded(parser.parseOptionalKeyword("start")))
+    return parser.parseGenericOperationAfterOpName(
+        result, llvm::makeArrayRef(operands));
+
+  FailureOr<OperationName> parseOpNameInfo = parser.parseCustomOperationName();
+  if (failed(parseOpNameInfo))
+    return failure();
+
+  StringRef innerOpName = parseOpNameInfo->getStringRef();
+
+  FunctionType opFntype;
+  Optional<Location> explicitLoc;
+  if (parser.parseKeyword("end") || parser.parseColon() ||
+      parser.parseType(opFntype) ||
+      parser.parseOptionalLocationSpecifier(explicitLoc))
+    return failure();
+
+  // If location of the op is explicitly provided, then use it; Else use
+  // the parser's current location.
+  Location opLoc = explicitLoc.getValueOr(currLocation);
+
+  // Derive the SSA-values for op's operands.
+  if (parser.resolveOperands(operands, opFntype.getInputs(), loc,
+                             result.operands))
+    return failure();
+
+  // Add a region for op.
+  Region &region = *result.addRegion();
+
+  // Create a basic-block inside op's region.
+  Block &block = region.emplaceBlock();
+
+  // Create and insert an "inner-op" operation in the block.
+  // Just for testing purposes, we can assume that inner op is a binary op with
+  // result and operand types all same as the test-op's first operand.
+  Type innerOpType = opFntype.getInput(0);
+  Value lhs = block.addArgument(innerOpType, opLoc);
+  Value rhs = block.addArgument(innerOpType, opLoc);
+
+  OpBuilder builder(parser.getBuilder().getContext());
+  builder.setInsertionPointToStart(&block);
+
+  OperationState innerOpState(opLoc, innerOpName);
+  innerOpState.operands.push_back(lhs);
+  innerOpState.operands.push_back(rhs);
+  innerOpState.addTypes(innerOpType);
+
+  Operation *innerOp = builder.createOperation(innerOpState);
+
+  // Insert a return statement in the block returning the inner-op's result.
+  builder.create<TestReturnOp>(innerOp->getLoc(), innerOp->getResults());
+
+  // Populate the op operation-state with result-type and location.
+  result.addTypes(opFntype.getResults());
+  result.location = innerOp->getLoc();
+
+  return success();
+}
+
+static void print(OpAsmPrinter &p, PrettyPrintedRegionOp op) {
+  p << ' ';
+  p.printOperands(op.getOperands());
+
+  Operation &innerOp = op.getRegion().front().front();
+  // Assuming that region has a single non-terminator inner-op, if the inner-op
+  // meets some criteria (which in this case is a simple one  based on the name
+  // of inner-op), then we can print the entire region in a succinct way.
+  // Here we assume that the prototype of "special.op" can be trivially derived
+  // while parsing it back.
+  if (innerOp.getName().getStringRef().equals("special.op")) {
+    p << " start special.op end";
+  } else {
+    p << " (";
+    p.printRegion(op.getRegion());
+    p << ")";
+  }
+
+  p << " : ";
+  p.printFunctionalType(op);
+}
+
 //===----------------------------------------------------------------------===//
 // Test PolyForOp - parse list of region arguments.
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index 6a06596838db0..8ac9049ee4864 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.td
+++ b/mlir/test/lib/Dialect/Test/TestOps.td
@@ -1630,6 +1630,25 @@ def WrappingRegionOp : TEST_Op<"wrapping_region",
   let printer = [{ return ::print(p, *this); }];
 }
 
+def PrettyPrintedRegionOp : TEST_Op<"pretty_printed_region",
+    [SingleBlockImplicitTerminator<"TestReturnOp">]> {
+  let summary =  "pretty_printed_region operation";
+  let description = [{
+    Test-op can be printed either in a "pretty" or "non-pretty" way based on
+    some criteria. The custom parser parsers both the versions while testing
+    APIs: parseCustomOperationName & parseGenericOperationAfterOpName.
+  }];
+  let arguments = (ins
+    AnyType:$input1,
+    AnyType:$input2
+  );
+
+  let results = (outs AnyType);
+  let regions = (region SizedRegion<1>:$region);
+  let parser = [{ return ::parse$cppClass(parser, result); }];
+  let printer = [{ return ::print(p, *this); }];
+}
+
 def PolyForOp : TEST_Op<"polyfor">
 {
   let summary =  "polyfor operation";


        


More information about the Mlir-commits mailing list