diff --git a/mlir/lib/Dialect/IRDL/IRDLLoading.cpp b/mlir/lib/Dialect/IRDL/IRDLLoading.cpp --- 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 using namespace mlir; using namespace mlir::irdl; @@ -49,26 +50,161 @@ return success(); } +/// Get the operand segment sizes from the attribute dictionary. +LogicalResult getSegmentSizesFromAttr(Operation *op, StringRef elemName, + StringRef attrName, unsigned numElements, + ArrayRef variadicities, + SmallVectorImpl &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(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(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 variadicities, + SmallVectorImpl &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(numElements) - + static_cast(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 variadicities, + SmallVectorImpl &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 variadicities, + SmallVectorImpl &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> constraints, - ArrayRef operandConstrs, ArrayRef resultConstrs, - const DenseMap &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> constraints, + ArrayRef operandConstrs, ArrayRef operandVariadicity, + ArrayRef resultConstrs, ArrayRef resultVariadicity, + const DenseMap &attributeConstrs) { + // Get the segment sizes for the operands. + // This will check that the number of operands is correct. + SmallVector 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 resultSegmentSizes; + if (failed(getResultSegmentSizes(op, resultVariadicity, resultSegmentSizes))) + return failure(); auto emitError = [op] { return op->emitError(); }; @@ -90,17 +226,29 @@ 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 @@ } SmallVector operandConstraints; - SmallVector resultConstraints; + SmallVector operandVariadicity; // Gather which constraint slots correspond to operand constraints auto operandsOp = op.getOp(); @@ -149,8 +297,15 @@ } } } + + // Gather the variadicities of each operand + for (VariadicityAttr attr : operandsOp->getVariadicity()) + operandVariadicity.push_back(attr.getValue()); } + SmallVector resultConstraints; + SmallVector resultVariadicity; + // Gather which constraint slots correspond to result constraints auto resultsOp = op.getOp(); if (resultsOp.has_value()) { @@ -163,6 +318,10 @@ } } } + + // Gather the variadicities of each result + for (Attribute attr : resultsOp->getVariadicity()) + resultVariadicity.push_back(attr.cast().getValue()); } // Gather which constraint slots correspond to attributes constraints @@ -193,10 +352,13 @@ 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 --- a/mlir/test/Dialect/IRDL/variadics.irdl.mlir +++ b/mlir/test/Dialect/IRDL/variadics.irdl.mlir @@ -29,13 +29,13 @@ // 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 @@ // 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 --- /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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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} : (i16, i16, i64) -> () + // CHECK: "testvar.var_and_opt_operand"(%{{.*}}, %{{.*}}, %{{.*}}) {operand_segment_sizes = array} : (i16, i16, i64) -> () + "testvar.var_and_opt_operand"(%x, %x, %y, %z) {operand_segment_sizes = array} : (i16, i16, i32, i64) -> () + // CHECK-NEXT: "testvar.var_and_opt_operand"(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {operand_segment_sizes = array} : (i16, i16, i32, i64) -> () + "testvar.var_and_opt_operand"(%y, %z) {operand_segment_sizes = array} : (i32, i64) -> () + // CHECK-NEXT: "testvar.var_and_opt_operand"(%{{.*}}, %{{.*}}) {operand_segment_sizes = array} : (i32, i64) -> () + return +} + +// ----- + +// Check that the segment sizes expects non-negative values +func.func @testMultOperandsSegmentNegative() { + // expected-error@+1 {{'operand_segment_sizes' attribute for specifying operand segments must have non-negative values}} + "testvar.var_and_opt_operand"() {operand_segment_sizes = array} : () -> () + return +} + +// ----- + +// Check that the segment sizes expects 1 for single values +func.func @testMultOperandsSegmentWrongSingle() { + // expected-error@+1 {{element 2 in 'operand_segment_sizes' attribute must be equal to 1}} + "testvar.var_and_opt_operand"() {operand_segment_sizes = array} : () -> () + return +} + +// ----- + +// Check that the segment sizes expects not more than 1 for optional values +func.func @testMultOperandsSegmentWrongOptional() { + // expected-error@+1 {{element 1 in 'operand_segment_sizes' attribute must be equal to 0 or 1}} + "testvar.var_and_opt_operand"() {operand_segment_sizes = array} : () -> () + 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@+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, 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@+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@+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@+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@+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@+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@+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@+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@+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@+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@+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} : () -> (i16, i16, i64) + // CHECK: "testvar.var_and_opt_result"() {result_segment_sizes = array} : () -> (i16, i16, i64) + "testvar.var_and_opt_result"() {result_segment_sizes = array} : () -> (i16, i16, i32, i64) + // CHECK-NEXT: "testvar.var_and_opt_result"() {result_segment_sizes = array} : () -> (i16, i16, i32, i64) + "testvar.var_and_opt_result"() {result_segment_sizes = array} : () -> (i32, i64) + // CHECK-NEXT: "testvar.var_and_opt_result"() {result_segment_sizes = array} : () -> (i32, i64) + return +} + +// ----- + +// Check that the segment sizes expects non-negative values +func.func @testMultResultsSegmentNegative() { + // expected-error@+1 {{'result_segment_sizes' attribute for specifying result segments must have non-negative values}} + "testvar.var_and_opt_result"() {result_segment_sizes = array} : () -> () + return +} + +// ----- + +// Check that the segment sizes expects 1 for single values +func.func @testMultResultsSegmentWrongSingle() { + // expected-error@+1 {{element 2 in 'result_segment_sizes' attribute must be equal to 1}} + "testvar.var_and_opt_result"() {result_segment_sizes = array} : () -> () + return +} + +// ----- + +// Check that the segment sizes expects not more than 1 for optional values +func.func @testMultResultsSegmentWrongOptional() { + // expected-error@+1 {{element 1 in 'result_segment_sizes' attribute must be equal to 0 or 1}} + "testvar.var_and_opt_result"() {result_segment_sizes = array} : () -> () + return +} + +// ----- + +// Check that the sum of the segment sizes should be equal to the number of results +func.func @testMultResultsSegmentWrongOptional() { + // expected-error@+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, i64) + return +}