[Mlir-commits] [mlir] 62b2b39 - [mlir][irdl] Support variadicity check in operations

Mathieu Fehr llvmlistbot at llvm.org
Sun Aug 20 07:39:38 PDT 2023


Author: Mathieu Fehr
Date: 2023-08-20T16:39:30+01:00
New Revision: 62b2b399924cc98c349979a84f1e375b97ee924c

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

LOG: [mlir][irdl] Support variadicity check in operations

This patch adds support for loading IRDL operations that are
using optional or variadics operands and results.

If an operation declares more than one optional/variadic operand
or result, then it requires the segment sizes in the attribute
dictionary, and otherwise it is computed using the number of
operands or results.

Currently, a variadic operand or result definiton expects all
operands and results in that definition to have the same type.
This restriction will be removed in a following patch.

Depends on D153983

Reviewed By: Mogball, unterumarmung

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

Added: 
    mlir/test/Dialect/IRDL/variadics.mlir

Modified: 
    mlir/lib/Dialect/IRDL/IRDLLoading.cpp
    mlir/test/Dialect/IRDL/variadics.irdl.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/lib/Dialect/IRDL/IRDLLoading.cpp b/mlir/lib/Dialect/IRDL/IRDLLoading.cpp
index 527596705ae8bf..1eef82b8116946 100644
--- a/mlir/lib/Dialect/IRDL/IRDLLoading.cpp
+++ b/mlir/lib/Dialect/IRDL/IRDLLoading.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/Support/SMLoc.h"
+#include <numeric>
 
 using namespace mlir;
 using namespace mlir::irdl;
@@ -49,26 +50,161 @@ irdlAttrOrTypeVerifier(function_ref<InFlightDiagnostic()> emitError,
   return success();
 }
 
+/// Get the operand segment sizes from the attribute dictionary.
+LogicalResult getSegmentSizesFromAttr(Operation *op, StringRef elemName,
+                                      StringRef attrName, unsigned numElements,
+                                      ArrayRef<Variadicity> variadicities,
+                                      SmallVectorImpl<int> &segmentSizes) {
+  // Get the segment sizes attribute, and check that it is of the right type.
+  Attribute segmentSizesAttr = op->getAttr(attrName);
+  if (!segmentSizesAttr) {
+    return op->emitError() << "'" << attrName
+                           << "' attribute is expected but not provided";
+  }
+
+  auto denseSegmentSizes = dyn_cast<DenseI32ArrayAttr>(segmentSizesAttr);
+  if (!denseSegmentSizes) {
+    return op->emitError() << "'" << attrName
+                           << "' attribute is expected to be a dense i32 array";
+  }
+
+  if (denseSegmentSizes.size() != (int64_t)variadicities.size()) {
+    return op->emitError() << "'" << attrName << "' attribute for specifying "
+                           << elemName << " segments must have "
+                           << variadicities.size() << " elements, but got "
+                           << denseSegmentSizes.size();
+  }
+
+  // Check that the segment sizes are corresponding to the given variadicities,
+  for (auto [i, segmentSize, variadicity] :
+       enumerate(denseSegmentSizes.asArrayRef(), variadicities)) {
+    if (segmentSize < 0)
+      return op->emitError()
+             << "'" << attrName << "' attribute for specifying " << elemName
+             << " segments must have non-negative values";
+    if (variadicity == Variadicity::single && segmentSize != 1)
+      return op->emitError() << "element " << i << " in '" << attrName
+                             << "' attribute must be equal to 1";
+
+    if (variadicity == Variadicity::optional && segmentSize > 1)
+      return op->emitError() << "element " << i << " in '" << attrName
+                             << "' attribute must be equal to 0 or 1";
+
+    segmentSizes.push_back(segmentSize);
+  }
+
+  // Check that the sum of the segment sizes is equal to the number of elements.
+  int32_t sum = std::reduce(denseSegmentSizes.asArrayRef().begin(),
+                            denseSegmentSizes.asArrayRef().end(), 0);
+  if (sum != static_cast<int32_t>(numElements))
+    return op->emitError() << "sum of elements in '" << attrName
+                           << "' attribute must be equal to the number of "
+                           << elemName << "s";
+
+  return success();
+}
+
+/// Compute the segment sizes of the given element (operands, results).
+/// If the operation has more than two non-single elements (optional or
+/// variadic), then get the segment sizes from the attribute dictionary.
+/// Otherwise, compute the segment sizes from the number of elements.
+/// `elemName` should be either `"operand"` or `"result"`.
+LogicalResult getSegmentSizes(Operation *op, StringRef elemName,
+                              StringRef attrName, unsigned numElements,
+                              ArrayRef<Variadicity> variadicities,
+                              SmallVectorImpl<int> &segmentSizes) {
+  // If we have more than one non-single variadicity, we need to get the
+  // segment sizes from the attribute dictionary.
+  int numberNonSingle = count_if(
+      variadicities, [](Variadicity v) { return v != Variadicity::single; });
+  if (numberNonSingle > 1)
+    return getSegmentSizesFromAttr(op, elemName, attrName, numElements,
+                                   variadicities, segmentSizes);
+
+  // If we only have single variadicities, the segments sizes are all 1.
+  if (numberNonSingle == 0) {
+    if (numElements != variadicities.size()) {
+      return op->emitError() << "op expects exactly " << variadicities.size()
+                             << " " << elemName << "s, but got " << numElements;
+    }
+    for (size_t i = 0, e = variadicities.size(); i < e; ++i)
+      segmentSizes.push_back(1);
+    return success();
+  }
+
+  assert(numberNonSingle == 1);
+
+  // There is exactly one non-single element, so we can
+  // compute its size and check that it is valid.
+  int nonSingleSegmentSize = static_cast<int>(numElements) -
+                             static_cast<int>(variadicities.size()) + 1;
+
+  if (nonSingleSegmentSize < 0) {
+    return op->emitError() << "op expects at least " << variadicities.size() - 1
+                           << " " << elemName << "s, but got " << numElements;
+  }
+
+  // Add the segment sizes.
+  for (Variadicity variadicity : variadicities) {
+    if (variadicity == Variadicity::single) {
+      segmentSizes.push_back(1);
+      continue;
+    }
+
+    // If we have an optional element, we should check that it represents
+    // zero or one elements.
+    if (nonSingleSegmentSize > 1 && variadicity == Variadicity::optional)
+      return op->emitError() << "op expects at most " << variadicities.size()
+                             << " " << elemName << "s, but got " << numElements;
+
+    segmentSizes.push_back(nonSingleSegmentSize);
+  }
+
+  return success();
+}
+
+/// Compute the segment sizes of the given operands.
+/// If the operation has more than two non-single operands (optional or
+/// variadic), then get the segment sizes from the attribute dictionary.
+/// Otherwise, compute the segment sizes from the number of operands.
+LogicalResult getOperandSegmentSizes(Operation *op,
+                                     ArrayRef<Variadicity> variadicities,
+                                     SmallVectorImpl<int> &segmentSizes) {
+  return getSegmentSizes(op, "operand", "operand_segment_sizes",
+                         op->getNumOperands(), variadicities, segmentSizes);
+}
+
+/// Compute the segment sizes of the given results.
+/// If the operation has more than two non-single results (optional or
+/// variadic), then get the segment sizes from the attribute dictionary.
+/// Otherwise, compute the segment sizes from the number of results.
+LogicalResult getResultSegmentSizes(Operation *op,
+                                    ArrayRef<Variadicity> variadicities,
+                                    SmallVectorImpl<int> &segmentSizes) {
+  return getSegmentSizes(op, "result", "result_segment_sizes",
+                         op->getNumResults(), variadicities, segmentSizes);
+}
+
 /// Verify that the given operation satisfies the given constraints.
 /// This encodes the logic of the verification method for operations defined
 /// with IRDL.
-static LogicalResult
-irdlOpVerifier(Operation *op, ArrayRef<std::unique_ptr<Constraint>> constraints,
-               ArrayRef<size_t> operandConstrs, ArrayRef<size_t> resultConstrs,
-               const DenseMap<StringAttr, size_t> &attributeConstrs) {
-  /// Check that we have the right number of operands.
-  unsigned numOperands = op->getNumOperands();
-  size_t numExpectedOperands = operandConstrs.size();
-  if (numOperands != numExpectedOperands)
-    return op->emitOpError() << numExpectedOperands
-                             << " operands expected, but got " << numOperands;
-
-  /// Check that we have the right number of results.
-  unsigned numResults = op->getNumResults();
-  size_t numExpectedResults = resultConstrs.size();
-  if (numResults != numExpectedResults)
-    return op->emitOpError()
-           << numExpectedResults << " results expected, but got " << numResults;
+static LogicalResult irdlOpVerifier(
+    Operation *op, ArrayRef<std::unique_ptr<Constraint>> constraints,
+    ArrayRef<size_t> operandConstrs, ArrayRef<Variadicity> operandVariadicity,
+    ArrayRef<size_t> resultConstrs, ArrayRef<Variadicity> resultVariadicity,
+    const DenseMap<StringAttr, size_t> &attributeConstrs) {
+  // Get the segment sizes for the operands.
+  // This will check that the number of operands is correct.
+  SmallVector<int> operandSegmentSizes;
+  if (failed(
+          getOperandSegmentSizes(op, operandVariadicity, operandSegmentSizes)))
+    return failure();
+
+  // Get the segment sizes for the results.
+  // This will check that the number of results is correct.
+  SmallVector<int> resultSegmentSizes;
+  if (failed(getResultSegmentSizes(op, resultVariadicity, resultSegmentSizes)))
+    return failure();
 
   auto emitError = [op] { return op->emitError(); };
 
@@ -90,17 +226,29 @@ irdlOpVerifier(Operation *op, ArrayRef<std::unique_ptr<Constraint>> constraints,
       return failure();
   }
 
-  /// Check that all operands satisfy the constraints.
-  for (auto [i, operandType] : enumerate(op->getOperandTypes()))
-    if (failed(verifier.verify({emitError}, TypeAttr::get(operandType),
-                               operandConstrs[i])))
-      return failure();
+  // Check that all operands satisfy the constraints
+  int operandIdx = 0;
+  for (auto [defIndex, segmentSize] : enumerate(operandSegmentSizes)) {
+    for (int i = 0; i < segmentSize; i++) {
+      if (failed(verifier.verify(
+              {emitError}, TypeAttr::get(op->getOperandTypes()[operandIdx]),
+              operandConstrs[defIndex])))
+        return failure();
+      ++operandIdx;
+    }
+  }
 
-  /// Check that all results satisfy the constraints.
-  for (auto [i, resultType] : enumerate(op->getResultTypes()))
-    if (failed(verifier.verify({emitError}, TypeAttr::get(resultType),
-                               resultConstrs[i])))
-      return failure();
+  // Check that all results satisfy the constraints
+  int resultIdx = 0;
+  for (auto [defIndex, segmentSize] : enumerate(resultSegmentSizes)) {
+    for (int i = 0; i < segmentSize; i++) {
+      if (failed(verifier.verify({emitError},
+                                 TypeAttr::get(op->getResultTypes()[resultIdx]),
+                                 resultConstrs[defIndex])))
+        return failure();
+      ++resultIdx;
+    }
+  }
 
   return success();
 }
@@ -135,7 +283,7 @@ static WalkResult loadOperation(
   }
 
   SmallVector<size_t> operandConstraints;
-  SmallVector<size_t> resultConstraints;
+  SmallVector<Variadicity> operandVariadicity;
 
   // Gather which constraint slots correspond to operand constraints
   auto operandsOp = op.getOp<OperandsOp>();
@@ -149,8 +297,15 @@ static WalkResult loadOperation(
         }
       }
     }
+
+    // Gather the variadicities of each operand
+    for (VariadicityAttr attr : operandsOp->getVariadicity())
+      operandVariadicity.push_back(attr.getValue());
   }
 
+  SmallVector<size_t> resultConstraints;
+  SmallVector<Variadicity> resultVariadicity;
+
   // Gather which constraint slots correspond to result constraints
   auto resultsOp = op.getOp<ResultsOp>();
   if (resultsOp.has_value()) {
@@ -163,6 +318,10 @@ static WalkResult loadOperation(
         }
       }
     }
+
+    // Gather the variadicities of each result
+    for (Attribute attr : resultsOp->getVariadicity())
+      resultVariadicity.push_back(attr.cast<VariadicityAttr>().getValue());
   }
 
   // Gather which constraint slots correspond to attributes constraints
@@ -193,10 +352,13 @@ static WalkResult loadOperation(
   auto verifier =
       [constraints{std::move(constraints)},
        operandConstraints{std::move(operandConstraints)},
+       operandVariadicity{std::move(operandVariadicity)},
        resultConstraints{std::move(resultConstraints)},
+       resultVariadicity{std::move(resultVariadicity)},
        attributesContraints{std::move(attributesContraints)}](Operation *op) {
         return irdlOpVerifier(op, constraints, operandConstraints,
-                              resultConstraints, attributesContraints);
+                              operandVariadicity, resultConstraints,
+                              resultVariadicity, attributesContraints);
       };
 
   // IRDL does not support defining regions.

diff  --git a/mlir/test/Dialect/IRDL/variadics.irdl.mlir b/mlir/test/Dialect/IRDL/variadics.irdl.mlir
index 6c051d0eaad487..64c5e1878f08eb 100644
--- a/mlir/test/Dialect/IRDL/variadics.irdl.mlir
+++ b/mlir/test/Dialect/IRDL/variadics.irdl.mlir
@@ -29,13 +29,13 @@ irdl.dialect @testvar {
   // CHECK-NEXT:    %[[v0:[^ ]*]] = irdl.is i16 
   // CHECK-NEXT:    %[[v1:[^ ]*]] = irdl.is i32 
   // CHECK-NEXT:    %[[v2:[^ ]*]] = irdl.is i64 
-  // CHECK-NEXT:    irdl.operands(%[[v0]], variadic %[[v1]], %[[v2]])
+  // CHECK-NEXT:    irdl.operands(%[[v0]], optional %[[v1]], %[[v2]])
   // CHECK-NEXT:  }
   irdl.operation @opt_operand {
     %0 = irdl.is i16
     %1 = irdl.is i32
     %2 = irdl.is i64
-    irdl.operands(%0, variadic %1, %2)
+    irdl.operands(%0, optional %1, %2)
   }
 
   // CHECK-LABEL: irdl.operation @var_and_opt_operand {
@@ -78,13 +78,13 @@ irdl.dialect @testvar {
   // CHECK-NEXT:    %[[v0:[^ ]*]] = irdl.is i16 
   // CHECK-NEXT:    %[[v1:[^ ]*]] = irdl.is i32 
   // CHECK-NEXT:    %[[v2:[^ ]*]] = irdl.is i64 
-  // CHECK-NEXT:    irdl.results(%[[v0]], variadic %[[v1]], %[[v2]])
+  // CHECK-NEXT:    irdl.results(%[[v0]], optional %[[v1]], %[[v2]])
   // CHECK-NEXT:  }
   irdl.operation @opt_result {
     %0 = irdl.is i16
     %1 = irdl.is i32
     %2 = irdl.is i64
-    irdl.results(%0, variadic %1, %2)
+    irdl.results(%0, optional %1, %2)
   }
 
   // CHECK-LABEL: irdl.operation @var_and_opt_result {

diff  --git a/mlir/test/Dialect/IRDL/variadics.mlir b/mlir/test/Dialect/IRDL/variadics.mlir
new file mode 100644
index 00000000000000..a8871fcf5ebd9a
--- /dev/null
+++ b/mlir/test/Dialect/IRDL/variadics.mlir
@@ -0,0 +1,400 @@
+// RUN: mlir-opt %s --irdl-file=%S/variadics.irdl.mlir -split-input-file -verify-diagnostics | FileCheck %s
+
+//===----------------------------------------------------------------------===//
+// Single operand
+//===----------------------------------------------------------------------===//
+
+// Test an operation with a single operand.
+func.func @testSingleOperand(%x: i32) {
+  "testvar.single_operand"(%x) : (i32) -> ()
+  // CHECK: "testvar.single_operand"(%{{.*}}) : (i32) -> ()
+  return
+}
+
+// -----
+
+// Test an operation with a single operand definition and a wrong number of operands.
+func.func @testSingleOperandFail(%x: i32) {
+  // expected-error at +1 {{op expects exactly 1 operands, but got 2}}
+  "testvar.single_operand"(%x, %x) : (i32, i32) -> ()  
+  return
+}
+
+// -----
+
+// Test an operation with a single operand definition and a wrong number of operands.
+func.func @testSingleOperandFail() {
+  // expected-error at +1 {{op expects exactly 1 operands, but got 0}}
+  "testvar.single_operand"() : () -> ()  
+  return
+}
+
+// -----
+
+
+//===----------------------------------------------------------------------===//
+// Variadic operand
+//===----------------------------------------------------------------------===//
+
+// Test an operation with a single variadic operand.
+func.func @testVarOperand(%x: i16, %y: i32, %z: i64) {
+  "testvar.var_operand"(%x, %z) : (i16, i64) -> ()
+  // CHECK: "testvar.var_operand"(%{{.*}}, %{{.*}}) : (i16, i64) -> ()
+  "testvar.var_operand"(%x, %y, %z) : (i16, i32, i64) -> ()
+  // CHECK-NEXT: "testvar.var_operand"(%{{.*}}, %{{.*}}, %{{.*}}) : (i16, i32, i64) -> ()
+  "testvar.var_operand"(%x, %y, %y, %z) : (i16, i32, i32, i64) -> ()
+  // CHECK-NEXT: "testvar.var_operand"(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) : (i16, i32, i32, i64) -> ()
+  "testvar.var_operand"(%x, %y, %y, %y, %z) : (i16, i32, i32, i32, i64) -> ()
+  // CHECK-NEXT: "testvar.var_operand"(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) : (i16, i32, i32, i32, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that the verifier of a variadic operand  fails if the variadic is given
+// a wrong type.
+func.func @testVarOperandFail(%x: i16, %y: i64, %z: i64) {
+  // expected-error at +1 {{expected 'i32' but got 'i64'}}
+  "testvar.var_operand"(%x, %y, %z) : (i16, i64, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that the verifier of a variadic operand fails if the variadic is given
+// a wrong type on the second value.
+func.func @testVarOperandFail(%x: i16, %y1: i32, %y2: i64, %z: i64) {
+  // expected-error at +1 {{expected 'i32' but got 'i64'}}
+  "testvar.var_operand"(%x, %y1, %y2, %z) : (i16, i32, i64, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that if we do not give enough operands, the verifier fails.
+func.func @testVarOperandFail() {
+  // expected-error at +1 {{op expects at least 2 operands, but got 0}}
+  "testvar.var_operand"() : () -> ()
+  return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// Optional operand
+//===----------------------------------------------------------------------===//
+
+
+// Test an operation with a single optional operand.
+func.func @testOptOperand(%x: i16, %y: i32, %z: i64) {
+  "testvar.opt_operand"(%x, %z) : (i16, i64) -> ()
+  // CHECK: "testvar.opt_operand"(%{{.*}}, %{{.*}}) : (i16, i64) -> ()
+  "testvar.opt_operand"(%x, %y, %z) : (i16, i32, i64) -> ()
+  // CHECK-NEXT: "testvar.opt_operand"(%{{.*}}, %{{.*}}, %{{.*}}) : (i16, i32, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that the verifier of an optional operand fails if the variadic is
+// given a wrong type.
+func.func @testOptOperandFail(%x: i16, %y: i64, %z: i64) {
+  // expected-error at +1 {{expected 'i32' but got 'i64'}}
+  "testvar.opt_operand"(%x, %y, %z) : (i16, i64, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that the verifier of an optional operand fails if there are too
+// many operands.
+func.func @testOptOperandFail(%x: i16, %y: i32, %z: i64) {
+  // expected-error at +1 {{op expects at most 3 operands, but got 4}}
+  "testvar.opt_operand"(%x, %y, %y, %z) : (i16, i32, i32, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that the verifier of an optional operand fails if there are not
+// enough operands.
+func.func @testOptOperandFail(%x: i16) {
+  // expected-error at +1 {{op expects at least 2 operands, but got 1}}
+  "testvar.opt_operand"(%x) : (i16) -> ()
+  return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// Multiple variadic
+//===----------------------------------------------------------------------===//
+
+// Check that an operation with multiple variadics expects the segment size
+// attribute
+func.func @testMultOperandsMissingSegment(%x: i16, %z: i64) {
+  // expected-error at +1 {{'operand_segment_sizes' attribute is expected but not provided}}
+  "testvar.var_and_opt_operand"(%x, %x, %z) : (i16, i16, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that an operation with multiple variadics expects the segment size
+// attribute of the right type
+func.func @testMultOperandsWrongSegmentType(%x: i16, %z: i64) {
+  // expected-error at +1 {{'operand_segment_sizes' attribute is expected to be a dense i32 array}}
+  "testvar.var_and_opt_operand"(%x, %x, %z) {operand_segment_sizes = i32} : (i16, i16, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that an operation with multiple variadics with the right segment size
+// verifies.
+func.func @testMultOperands(%x: i16, %y: i32, %z: i64) {
+  "testvar.var_and_opt_operand"(%x, %x, %z) {operand_segment_sizes = array<i32: 2, 0, 1>} : (i16, i16, i64) -> ()
+  // CHECK: "testvar.var_and_opt_operand"(%{{.*}}, %{{.*}}, %{{.*}}) {operand_segment_sizes = array<i32: 2, 0, 1>} : (i16, i16, i64) -> ()
+  "testvar.var_and_opt_operand"(%x, %x, %y, %z) {operand_segment_sizes = array<i32: 2, 1, 1>} : (i16, i16, i32, i64) -> ()
+  // CHECK-NEXT: "testvar.var_and_opt_operand"(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {operand_segment_sizes = array<i32: 2, 1, 1>} : (i16, i16, i32, i64) -> ()
+  "testvar.var_and_opt_operand"(%y, %z) {operand_segment_sizes = array<i32: 0, 1, 1>} : (i32, i64) -> ()
+  // CHECK-NEXT: "testvar.var_and_opt_operand"(%{{.*}}, %{{.*}}) {operand_segment_sizes = array<i32: 0, 1, 1>} : (i32, i64) -> ()
+  return
+}
+
+// -----
+
+// Check that the segment sizes expects non-negative values
+func.func @testMultOperandsSegmentNegative() {
+  // expected-error at +1 {{'operand_segment_sizes' attribute for specifying operand segments must have non-negative values}}
+  "testvar.var_and_opt_operand"() {operand_segment_sizes = array<i32: 2, -1, 1>} : () -> ()
+  return
+}
+
+// -----
+
+// Check that the segment sizes expects 1 for single values
+func.func @testMultOperandsSegmentWrongSingle() {
+  // expected-error at +1 {{element 2 in 'operand_segment_sizes' attribute must be equal to 1}}
+  "testvar.var_and_opt_operand"() {operand_segment_sizes = array<i32: 0, 0, 0>} : () -> ()
+  return
+}
+
+// -----
+
+// Check that the segment sizes expects not more than 1 for optional values
+func.func @testMultOperandsSegmentWrongOptional() {
+  // expected-error at +1 {{element 1 in 'operand_segment_sizes' attribute must be equal to 0 or 1}}
+  "testvar.var_and_opt_operand"() {operand_segment_sizes = array<i32: 0, 2, 0>} : () -> ()
+  return
+}
+
+// -----
+
+// Check that the sum of the segment sizes should be equal to the number of operands
+func.func @testMultOperandsSegmentWrongOptional(%y: i32, %z: i64) {
+  // expected-error at +1 {{sum of elements in 'operand_segment_sizes' attribute must be equal to the number of operands}}
+  "testvar.var_and_opt_operand"(%y, %z) {operand_segment_sizes = array<i32: 0, 0, 1>} : (i32, i64) -> ()
+  return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// Single result
+//===----------------------------------------------------------------------===//
+
+// Test an operation with a single result.
+func.func @testSingleResult() {
+  %x = "testvar.single_result"() : () -> i32
+  // CHECK: %{{.*}} = "testvar.single_result"() : () -> i32
+  return
+}
+
+// -----
+
+// Test an operation with a single result definition and a wrong number of results.
+func.func @testSingleResultFail() {
+  // expected-error at +1 {{op expects exactly 1 results, but got 2}}
+  %x, %y = "testvar.single_result"() : () -> (i32, i32)  
+  return
+}
+
+// -----
+
+// Test an operation with a single result definition and a wrong number of results.
+func.func @testSingleResultFail() {
+  // expected-error at +1 {{op expects exactly 1 results, but got 0}}
+  "testvar.single_result"() : () -> ()  
+  return
+}
+
+// -----
+
+
+//===----------------------------------------------------------------------===//
+// Variadic result
+//===----------------------------------------------------------------------===//
+
+
+// Test an operation with a single variadic result.
+func.func @testVarResult() {
+  "testvar.var_result"() : () -> (i16, i64)
+  // CHECK: "testvar.var_result"() : () -> (i16, i64)
+  "testvar.var_result"() : () -> (i16, i32, i64)
+  // CHECK-NEXT: "testvar.var_result"() : () -> (i16, i32, i64)
+  "testvar.var_result"() : () -> (i16, i32, i32, i64)
+  // CHECK-NEXT: "testvar.var_result"() : () -> (i16, i32, i32, i64)
+  "testvar.var_result"() : () -> (i16, i32, i32, i32, i64)
+  // CHECK-NEXT: "testvar.var_result"() : () -> (i16, i32, i32, i32, i64)
+  return
+}
+
+// -----
+
+// Check that the verifier of a variadic result  fails if the variadic is given
+// a wrong type.
+func.func @testVarResultFail() {
+  // expected-error at +1 {{expected 'i32' but got 'i64'}}
+  "testvar.var_result"() : () -> (i16, i64, i64)
+  return
+}
+
+// -----
+
+// Check that the verifier of a variadic result fails if the variadic is given
+// a wrong type on the second value.
+func.func @testVarResultFail() {
+  // expected-error at +1 {{expected 'i32' but got 'i64'}}
+  "testvar.var_result"() : () -> (i16, i32, i64, i64)
+  return
+}
+
+// -----
+
+// Check that if we do not give enough results, the verifier fails.
+func.func @testVarResultFail() {
+  // expected-error at +1 {{op expects at least 2 results, but got 0}}
+  "testvar.var_result"() : () -> ()
+  return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// Optional result
+//===----------------------------------------------------------------------===//
+
+
+// Test an operation with a single optional result.
+func.func @testOptResult() {
+  "testvar.opt_result"() : () -> (i16, i64)
+  // CHECK: "testvar.opt_result"() : () -> (i16, i64)
+  "testvar.opt_result"() : () -> (i16, i32, i64)
+  // CHECK-NEXT: "testvar.opt_result"() : () -> (i16, i32, i64)
+  return
+}
+
+// -----
+
+// Check that the verifier of an optional result fails if the variadic is
+// given a wrong type.
+func.func @testOptResultFail() {
+  // expected-error at +1 {{expected 'i32' but got 'i64'}}
+  "testvar.opt_result"() : () -> (i16, i64, i64)
+  return
+}
+
+// -----
+
+// Check that the verifier of an optional result fails if there are too
+// many results.
+func.func @testOptResultFail() {
+  // expected-error at +1 {{op expects at most 3 results, but got 4}}
+  "testvar.opt_result"() : () -> (i16, i32, i32, i64)
+  return
+}
+
+// -----
+
+// Check that the verifier of an optional result fails if there are not
+// enough results.
+func.func @testOptResultFail() {
+  // expected-error at +1 {{op expects at least 2 results, but got 1}}
+  "testvar.opt_result"() : () -> (i16)
+  return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// Multiple variadic
+//===----------------------------------------------------------------------===//
+
+// Check that an operation with multiple variadics expects the segment size
+// attribute
+func.func @testMultResultsMissingSegment() {
+  // expected-error at +1 {{'result_segment_sizes' attribute is expected but not provided}}
+  "testvar.var_and_opt_result"() : () -> (i16, i16, i64)
+  return
+}
+
+// -----
+
+// Check that an operation with multiple variadics expects the segment size
+// attribute of the right type
+func.func @testMultResultsWrongSegmentType() {
+  // expected-error at +1 {{'result_segment_sizes' attribute is expected to be a dense i32 array}}
+  "testvar.var_and_opt_result"() {result_segment_sizes = i32} : () -> (i16, i16, i64)
+  return
+}
+
+// -----
+
+// Check that an operation with multiple variadics with the right segment size
+// verifies.
+func.func @testMultResults() {
+  "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 2, 0, 1>} : () -> (i16, i16, i64)
+  // CHECK: "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 2, 0, 1>} : () -> (i16, i16, i64)
+  "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 2, 1, 1>} : () -> (i16, i16, i32, i64)
+  // CHECK-NEXT: "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 2, 1, 1>} : () -> (i16, i16, i32, i64)
+  "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 0, 1, 1>} : () -> (i32, i64)
+  // CHECK-NEXT: "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 0, 1, 1>} : () -> (i32, i64)
+  return
+}
+
+// -----
+
+// Check that the segment sizes expects non-negative values
+func.func @testMultResultsSegmentNegative() {
+  // expected-error at +1 {{'result_segment_sizes' attribute for specifying result segments must have non-negative values}}
+  "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 2, -1, 1>} : () -> ()
+  return
+}
+
+// -----
+
+// Check that the segment sizes expects 1 for single values
+func.func @testMultResultsSegmentWrongSingle() {
+  // expected-error at +1 {{element 2 in 'result_segment_sizes' attribute must be equal to 1}}
+  "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 0, 0, 0>} : () -> ()
+  return
+}
+
+// -----
+
+// Check that the segment sizes expects not more than 1 for optional values
+func.func @testMultResultsSegmentWrongOptional() {
+  // expected-error at +1 {{element 1 in 'result_segment_sizes' attribute must be equal to 0 or 1}}
+  "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 0, 2, 0>} : () -> ()
+  return
+}
+
+// -----
+
+// Check that the sum of the segment sizes should be equal to the number of results
+func.func @testMultResultsSegmentWrongOptional() {
+  // expected-error at +1 {{sum of elements in 'result_segment_sizes' attribute must be equal to the number of results}}
+  "testvar.var_and_opt_result"() {result_segment_sizes = array<i32: 0, 0, 1>} : () -> (i32, i64)
+  return
+}


        


More information about the Mlir-commits mailing list