[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