diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td b/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td index ee0b5aa9760b1..0bddfd85d436b 100644 --- a/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td @@ -95,9 +95,9 @@ def IsFortranValuePred : CPred<"::hlfir::isFortranValueType($_self)">; def AnyFortranValue : TypeConstraint; - -def AnyFortranEntity : TypeConstraint, "any Fortran value or variable type">; +def AnyFortranEntity + : Type, + "any Fortran value or variable type">; def IsFortranScalarCharacterPred : CPred<"::hlfir::isFortranScalarCharacterType($_self)">; diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td index 2f5da720fbe1d..db3fb0b90464d 100644 --- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td @@ -721,6 +721,28 @@ def hlfir_CShiftOp let hasVerifier = 1; } +def hlfir_EOShiftOp + : hlfir_Op< + "eoshift", [AttrSizedOperandSegments, + DeclareOpInterfaceMethods]> { + let summary = "EOSHIFT transformational intrinsic"; + let description = [{ + End-off shift of an array + }]; + + let arguments = (ins AnyFortranArrayObject:$array, + AnyFortranIntegerScalarOrArrayObject:$shift, + Optional:$boundary, Optional:$dim); + + let results = (outs hlfir_ExprType); + + let assemblyFormat = [{ + $array $shift (`boundary` $boundary^)? (`dim` $dim^)? attr-dict `:` functional-type(operands, results) + }]; + + let hasVerifier = 1; +} + def hlfir_ReshapeOp : hlfir_Op< "reshape", [AttrSizedOperandSegments, diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp index ed102db69dae3..93ee94a120aa1 100644 --- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp @@ -1440,44 +1440,46 @@ void hlfir::MatmulTransposeOp::getEffects( } //===----------------------------------------------------------------------===// -// CShiftOp +// Array shifts: CShiftOp/EOShiftOp //===----------------------------------------------------------------------===// -llvm::LogicalResult hlfir::CShiftOp::verify() { - mlir::Value array = getArray(); +template +static llvm::LogicalResult verifyArrayShift(Op op) { + mlir::Value array = op.getArray(); fir::SequenceType arrayTy = mlir::cast( hlfir::getFortranElementOrSequenceType(array.getType())); llvm::ArrayRef inShape = arrayTy.getShape(); std::size_t arrayRank = inShape.size(); mlir::Type eleTy = arrayTy.getEleTy(); - hlfir::ExprType resultTy = mlir::cast(getResult().getType()); + hlfir::ExprType resultTy = + mlir::cast(op.getResult().getType()); llvm::ArrayRef resultShape = resultTy.getShape(); std::size_t resultRank = resultShape.size(); mlir::Type resultEleTy = resultTy.getEleTy(); - mlir::Value shift = getShift(); + mlir::Value shift = op.getShift(); mlir::Type shiftTy = hlfir::getFortranElementOrSequenceType(shift.getType()); - // TODO: turn allowCharacterLenMismatch into true. - if (auto match = areMatchingTypes(*this, eleTy, resultEleTy, - /*allowCharacterLenMismatch=*/false); + if (auto match = areMatchingTypes( + op, eleTy, resultEleTy, + /*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier); match.failed()) - return emitOpError( + return op.emitOpError( "input and output arrays should have the same element type"); if (arrayRank != resultRank) - return emitOpError("input and output arrays should have the same rank"); + return op.emitOpError("input and output arrays should have the same rank"); constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent(); for (auto [inDim, resultDim] : llvm::zip(inShape, resultShape)) if (inDim != unknownExtent && resultDim != unknownExtent && inDim != resultDim) - return emitOpError( + return op.emitOpError( "output array's shape conflicts with the input array's shape"); int64_t dimVal = -1; - if (!getDim()) + if (!op.getDim()) dimVal = 1; - else if (auto dim = fir::getIntIfConstant(getDim())) + else if (auto dim = fir::getIntIfConstant(op.getDim())) dimVal = *dim; // The DIM argument may be statically invalid (e.g. exceed the @@ -1485,44 +1487,79 @@ llvm::LogicalResult hlfir::CShiftOp::verify() { // so avoid some checks unless useStrictIntrinsicVerifier is true. if (useStrictIntrinsicVerifier && dimVal != -1) { if (dimVal < 1) - return emitOpError("DIM must be >= 1"); + return op.emitOpError("DIM must be >= 1"); if (dimVal > static_cast(arrayRank)) - return emitOpError("DIM must be <= input array's rank"); + return op.emitOpError("DIM must be <= input array's rank"); } - if (auto shiftSeqTy = mlir::dyn_cast(shiftTy)) { - // SHIFT is an array. Verify the rank and the shape (if DIM is constant). - llvm::ArrayRef shiftShape = shiftSeqTy.getShape(); - std::size_t shiftRank = shiftShape.size(); - if (shiftRank != arrayRank - 1) - return emitOpError( - "SHIFT's rank must be 1 less than the input array's rank"); - - if (useStrictIntrinsicVerifier && dimVal != -1) { - // SHIFT's shape must be [d(1), d(2), ..., d(DIM-1), d(DIM+1), ..., d(n)], - // where [d(1), d(2), ..., d(n)] is the shape of the ARRAY. - int64_t arrayDimIdx = 0; - int64_t shiftDimIdx = 0; - for (auto shiftDim : shiftShape) { - if (arrayDimIdx == dimVal - 1) + // A helper lambda to verify the shape of the array types of + // certain operands of the array shift (e.g. the SHIFT and BOUNDARY operands). + auto verifyOperandTypeShape = [&](mlir::Type type, + llvm::Twine name) -> llvm::LogicalResult { + if (auto opndSeqTy = mlir::dyn_cast(type)) { + // The operand is an array. Verify the rank and the shape (if DIM is + // constant). + llvm::ArrayRef opndShape = opndSeqTy.getShape(); + std::size_t opndRank = opndShape.size(); + if (opndRank != arrayRank - 1) + return op.emitOpError( + name + "'s rank must be 1 less than the input array's rank"); + + if (useStrictIntrinsicVerifier && dimVal != -1) { + // The operand's shape must be + // [d(1), d(2), ..., d(DIM-1), d(DIM+1), ..., d(n)], + // where [d(1), d(2), ..., d(n)] is the shape of the ARRAY. + int64_t arrayDimIdx = 0; + int64_t opndDimIdx = 0; + for (auto opndDim : opndShape) { + if (arrayDimIdx == dimVal - 1) + ++arrayDimIdx; + + if (inShape[arrayDimIdx] != unknownExtent && + opndDim != unknownExtent && inShape[arrayDimIdx] != opndDim) + return op.emitOpError("SHAPE(ARRAY)(" + + llvm::Twine(arrayDimIdx + 1) + + ") must be equal to SHAPE(" + name + ")(" + + llvm::Twine(opndDimIdx + 1) + + "): " + llvm::Twine(inShape[arrayDimIdx]) + + " != " + llvm::Twine(opndDim)); ++arrayDimIdx; - - if (inShape[arrayDimIdx] != unknownExtent && - shiftDim != unknownExtent && inShape[arrayDimIdx] != shiftDim) - return emitOpError("SHAPE(ARRAY)(" + llvm::Twine(arrayDimIdx + 1) + - ") must be equal to SHAPE(SHIFT)(" + - llvm::Twine(shiftDimIdx + 1) + - "): " + llvm::Twine(inShape[arrayDimIdx]) + - " != " + llvm::Twine(shiftDim)); - ++arrayDimIdx; - ++shiftDimIdx; + ++opndDimIdx; + } } } + return mlir::success(); + }; + + if (failed(verifyOperandTypeShape(shiftTy, "SHIFT"))) + return mlir::failure(); + + if constexpr (std::is_same_v) { + if (mlir::Value boundary = op.getBoundary()) { + mlir::Type boundaryTy = + hlfir::getFortranElementOrSequenceType(boundary.getType()); + if (auto match = areMatchingTypes( + op, eleTy, hlfir::getFortranElementType(boundaryTy), + /*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier); + match.failed()) + return op.emitOpError( + "ARRAY and BOUNDARY operands must have the same element type"); + if (failed(verifyOperandTypeShape(boundaryTy, "BOUNDARY"))) + return mlir::failure(); + } } return mlir::success(); } +//===----------------------------------------------------------------------===// +// CShiftOp +//===----------------------------------------------------------------------===// + +llvm::LogicalResult hlfir::CShiftOp::verify() { + return verifyArrayShift(*this); +} + void hlfir::CShiftOp::getEffects( llvm::SmallVectorImpl< mlir::SideEffects::EffectInstance> @@ -1530,6 +1567,21 @@ void hlfir::CShiftOp::getEffects( getIntrinsicEffects(getOperation(), effects); } +//===----------------------------------------------------------------------===// +// EOShiftOp +//===----------------------------------------------------------------------===// + +llvm::LogicalResult hlfir::EOShiftOp::verify() { + return verifyArrayShift(*this); +} + +void hlfir::EOShiftOp::getEffects( + llvm::SmallVectorImpl< + mlir::SideEffects::EffectInstance> + &effects) { + getIntrinsicEffects(getOperation(), effects); +} + //===----------------------------------------------------------------------===// // ReshapeOp //===----------------------------------------------------------------------===// @@ -1543,7 +1595,8 @@ llvm::LogicalResult hlfir::ReshapeOp::verify() { hlfir::getFortranElementOrSequenceType(array.getType())); if (auto match = areMatchingTypes( *this, hlfir::getFortranElementType(resultType), - arrayType.getElementType(), /*allowCharacterLenMismatch=*/true); + arrayType.getElementType(), + /*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier); match.failed()) return emitOpError("ARRAY and the result must have the same element type"); if (hlfir::isPolymorphicType(resultType) != @@ -1565,9 +1618,9 @@ llvm::LogicalResult hlfir::ReshapeOp::verify() { if (mlir::Value pad = getPad()) { auto padArrayType = mlir::cast( hlfir::getFortranElementOrSequenceType(pad.getType())); - if (auto match = areMatchingTypes(*this, arrayType.getElementType(), - padArrayType.getElementType(), - /*allowCharacterLenMismatch=*/true); + if (auto match = areMatchingTypes( + *this, arrayType.getElementType(), padArrayType.getElementType(), + /*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier); match.failed()) return emitOpError("ARRAY and PAD must be of the same type"); } diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir index d61efe0062e69..0f54a0250294b 100644 --- a/flang/test/HLFIR/invalid.fir +++ b/flang/test/HLFIR/invalid.fir @@ -1555,3 +1555,96 @@ func.func @bad_reshape(%arg0: !hlfir.expr<1x!fir.char<1,2>>, %arg1: !hlfir.expr< %0 = hlfir.reshape %arg0 %arg1 pad %arg2 : (!hlfir.expr<1x!fir.char<1,2>>, !hlfir.expr<1xi32>, !hlfir.expr<1x!fir.char<2,?>>) -> !hlfir.expr> return } + +// ----- + +func.func @bad_eoshift1(%arg0: !hlfir.expr, %arg1: i32) { + // expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}} + %0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr, i32) -> !hlfir.expr + return +} + +// ----- + +func.func @bad_eoshift2(%arg0: !hlfir.expr, %arg1: i32) { + // expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same rank}} + %0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr, i32) -> !hlfir.expr + return +} + +// ----- + +func.func @bad_eoshift3(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) { + // expected-error@+1 {{'hlfir.eoshift' op output array's shape conflicts with the input array's shape}} + %0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<2x2xi32>, i32) -> !hlfir.expr<2x3xi32> + return +} + +// ----- + +func.func @bad_eoshift4(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) { + %c0 = arith.constant 0 : index + // expected-error@+1 {{'hlfir.eoshift' op DIM must be >= 1}} + %0 = hlfir.eoshift %arg0 %arg1 dim %c0 : (!hlfir.expr<2x2xi32>, i32, index) -> !hlfir.expr<2x2xi32> + return +} + +// ----- + +func.func @bad_eoshift5(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) { + %c10 = arith.constant 10 : index + // expected-error@+1 {{'hlfir.eoshift' op DIM must be <= input array's rank}} + %0 = hlfir.eoshift %arg0 %arg1 dim %c10 : (!hlfir.expr<2x2xi32>, i32, index) -> !hlfir.expr<2x2xi32> + return +} + +// ----- + +func.func @bad_eoshift6(%arg0: !hlfir.expr<2x2xi32>, %arg1: !hlfir.expr<2x2xi32>) { + // expected-error@+1 {{'hlfir.eoshift' op SHIFT's rank must be 1 less than the input array's rank}} + %0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<2x2xi32>, !hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32> + return +} + +// ----- + +func.func @bad_eoshift7(%arg0: !hlfir.expr, %arg1: !hlfir.expr<3xi32>) { + %c1 = arith.constant 1 : index + // expected-error@+1 {{'hlfir.eoshift' op SHAPE(ARRAY)(2) must be equal to SHAPE(SHIFT)(1): 2 != 3}} + %0 = hlfir.eoshift %arg0 %arg1 dim %c1 : (!hlfir.expr, !hlfir.expr<3xi32>, index) -> !hlfir.expr<2x2xi32> + return +} + +// ----- + +func.func @bad_eoshift8(%arg0: !hlfir.expr>, %arg1: i32) { + // expected-error@+2 {{'hlfir.eoshift' op character KIND mismatch}} + // expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}} + %0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr>, i32) -> !hlfir.expr> + return +} + +// ----- + +func.func @bad_eoshift9(%arg0: !hlfir.expr>, %arg1: i32) { + // expected-error@+2 {{'hlfir.eoshift' op character LEN mismatch}} + // expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}} + %0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr>, i32) -> !hlfir.expr> + return +} + +// ----- + +func.func @bad_eoshift10(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32, %arg2: f32) { + // expected-error@+1 {{'hlfir.eoshift' op ARRAY and BOUNDARY operands must have the same element type}} + %0 = hlfir.eoshift %arg0 %arg1 boundary %arg2 : (!hlfir.expr<2x2xi32>, i32, f32) -> !hlfir.expr<2x2xi32> + return +} + +// ----- + +func.func @bad_eoshift11(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32, %arg2: !hlfir.expr<2x2xi32>) { + // expected-error@+1 {{'hlfir.eoshift' op BOUNDARY's rank must be 1 less than the input array's rank}} + %0 = hlfir.eoshift %arg0 %arg1 boundary %arg2 : (!hlfir.expr<2x2xi32>, i32, !hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32> + return +}