diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h b/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h index a0bcd33f57e06..9673804e09f1a 100644 --- a/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h +++ b/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h @@ -72,6 +72,7 @@ inline bool isBoxAddressOrValueType(mlir::Type type) { bool isFortranScalarNumericalType(mlir::Type); bool isFortranNumericalArrayObject(mlir::Type); bool isFortranNumericalOrLogicalArrayObject(mlir::Type); +bool isFortranArrayObject(mlir::Type); bool isPassByRefOrIntegerType(mlir::Type); bool isI1Type(mlir::Type); // scalar i1 or logical, or sequence of logical (via (boxed?) array or expr) diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td b/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td index e210de3ce33eb..2c620af80bfeb 100644 --- a/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td @@ -118,6 +118,11 @@ def IsFortranNumericalOrLogicalArrayObjectPred def AnyFortranNumericalOrLogicalArrayObject : Type; +def IsFortranArrayObjectPred + : CPred<"::hlfir::isFortranArrayObject($_self)">; +def AnyFortranArrayObject : Type; + def IsPassByRefOrIntegerTypePred : CPred<"::hlfir::isPassByRefOrIntegerType($_self)">; def AnyPassByRefOrIntegerType : Type { + let summary = "TRANSPOSE transformational intrinsic"; + let description = [{ + Transpose a rank 2 array + }]; + + let arguments = (ins AnyFortranArrayObject:$array); + + let results = (outs hlfir_ExprType); + + let assemblyFormat = [{ + $array attr-dict `:` functional-type(operands, results) + }]; + + let hasVerifier = 1; +} + def hlfir_AssociateOp : hlfir_Op<"associate", [AttrSizedOperandSegments, DeclareOpInterfaceMethods]> { let summary = "Create a variable from an expression value"; diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp index 3dd757f993848..2cadd6880cb1f 100644 --- a/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp @@ -127,6 +127,12 @@ bool hlfir::isFortranNumericalOrLogicalArrayObject(mlir::Type type) { return false; } +bool hlfir::isFortranArrayObject(mlir::Type type) { + if (isBoxAddressType(type)) + return false; + return !!getFortranElementOrSequenceType(type).dyn_cast(); +} + bool hlfir::isPassByRefOrIntegerType(mlir::Type type) { mlir::Type unwrappedType = fir::unwrapPassByRefType(type); return fir::isa_integer(unwrappedType); diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp index bff5b3ba648e2..53259fe495baa 100644 --- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp @@ -636,6 +636,36 @@ mlir::LogicalResult hlfir::MatmulOp::verify() { return mlir::success(); } +//===----------------------------------------------------------------------===// +// TransposeOp +//===----------------------------------------------------------------------===// + +mlir::LogicalResult hlfir::TransposeOp::verify() { + mlir::Value array = getArray(); + fir::SequenceType arrayTy = + hlfir::getFortranElementOrSequenceType(array.getType()) + .cast(); + llvm::ArrayRef inShape = arrayTy.getShape(); + std::size_t rank = inShape.size(); + mlir::Type eleTy = arrayTy.getEleTy(); + hlfir::ExprType resultTy = getResult().getType().cast(); + llvm::ArrayRef resultShape = resultTy.getShape(); + std::size_t resultRank = resultShape.size(); + mlir::Type resultEleTy = resultTy.getEleTy(); + + if (rank != 2 || resultRank != 2) + return emitOpError("input and output arrays should have rank 2"); + + if (inShape[0] != resultShape[1] || inShape[1] != resultShape[0]) + return emitOpError("output shape does not match input array"); + + if (eleTy != resultEleTy) + return emitOpError( + "input and output arrays should have the same element type"); + + return mlir::success(); +} + //===----------------------------------------------------------------------===// // AssociateOp //===----------------------------------------------------------------------===// diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir index 0ff4bb59c8b2d..2ec7c689b5ea1 100644 --- a/flang/test/HLFIR/invalid.fir +++ b/flang/test/HLFIR/invalid.fir @@ -376,6 +376,27 @@ func.func @bad_matmul8(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2x3xi32>) { return } +// ----- +func.func @bad_transpose1(%arg0: !hlfir.expr<2xi32>) { + // expected-error@+1 {{'hlfir.transpose' op input and output arrays should have rank 2}} + %0 = hlfir.transpose %arg0 : (!hlfir.expr<2xi32>) -> !hlfir.expr<2xi32> + return +} + +// ----- +func.func @bad_transpose2(%arg0: !hlfir.expr<2x3xi32>) { + // expected-error@+1 {{'hlfir.transpose' op output shape does not match input array}} + %0 = hlfir.transpose %arg0 : (!hlfir.expr<2x3xi32>) -> !hlfir.expr<2x2xi32> + return +} + +// ----- +func.func @bad_transpose3(%arg0: !hlfir.expr<2x3xi32>) { + // expected-error@+1 {{'hlfir.transpose' op input and output arrays should have the same element type}} + %0 = hlfir.transpose %arg0 : (!hlfir.expr<2x3xi32>) -> !hlfir.expr<3x2xf64> + return +} + // ----- func.func @bad_assign_1(%arg0: !fir.box>, %arg1: !fir.box>) { // expected-error@+1 {{'hlfir.assign' op lhs must be an allocatable when `realloc` is set}} diff --git a/flang/test/HLFIR/transpose.fir b/flang/test/HLFIR/transpose.fir new file mode 100644 index 0000000000000..14f9383d5775a --- /dev/null +++ b/flang/test/HLFIR/transpose.fir @@ -0,0 +1,69 @@ +// Test hlfir.transpose operation parse, verify (no errors), and unparse + +// RUN: fir-opt %s | fir-opt | FileCheck %s + +// square matrix of known shape +func.func @transpose0(%arg0: !hlfir.expr<2x2xi32>) { + %res = hlfir.transpose %arg0 : (!hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32> + return +} +// CHECK-LABEL: func.func @transpose0 +// CHECK: %[[ARG0:.*]]: !hlfir.expr<2x2xi32> +// CHECK-NEXT: %[[RES:.*]] = hlfir.transpose %[[ARG0]] : (!hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// rectangular matrix of known shape +func.func @transpose1(%arg0: !hlfir.expr<2x3xi32>) { + %res = hlfir.transpose %arg0 : (!hlfir.expr<2x3xi32>) -> !hlfir.expr<3x2xi32> + return +} +// CHECK-LABEL: func.func @transpose1 +// CHECK: %[[ARG0:.*]]: !hlfir.expr<2x3xi32> +// CHECK-NEXT: %[[RES:.*]] = hlfir.transpose %[[ARG0]] : (!hlfir.expr<2x3xi32>) -> !hlfir.expr<3x2xi32> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// matrix of assumed shape +func.func @transpose2(%arg0: !hlfir.expr) { + %res = hlfir.transpose %arg0 : (!hlfir.expr) -> !hlfir.expr + return +} +// CHECK-LABEL: func.func @transpose2 +// CHECK: %[[ARG0:.*]]: !hlfir.expr +// CHECK-NEXT: %[[RES:.*]] = hlfir.transpose %[[ARG0]] : (!hlfir.expr) -> !hlfir.expr +// CHECK-NEXT: return +// CHECK-NEXT: } + +// matrix where only some dimensions are known #1 +func.func @transpose3(%arg0: !hlfir.expr) { + %res = hlfir.transpose %arg0 : (!hlfir.expr) -> !hlfir.expr<2x?xi32> + return +} +// CHECK-LABEL: func.func @transpose3 +// CHECK: %[[ARG0:.*]]: !hlfir.expr +// CHECK-NEXT: %[[RES:.*]] = hlfir.transpose %[[ARG0]] : (!hlfir.expr) -> !hlfir.expr<2x?xi32> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// matrix where only some dimensions are known #2 +func.func @transpose4(%arg0: !hlfir.expr<2x?xi32>) { + %res = hlfir.transpose %arg0 : (!hlfir.expr<2x?xi32>) -> !hlfir.expr + return +} +// CHECK-LABEL: func.func @transpose4 +// CHECK: %[[ARG0:.*]]: !hlfir.expr<2x?xi32> +// CHECK-NEXT: %[[RES:.*]] = hlfir.transpose %[[ARG0]] : (!hlfir.expr<2x?xi32>) -> !hlfir.expr +// CHECK-NEXT: return +// CHECK-NEXT: } + +// matrix is a boxed array +func.func @transpose5(%arg0: !fir.box>) { + %res = hlfir.transpose %arg0 : (!fir.box>) -> !hlfir.expr<2x1xi32> + return +} +// CHECK-LABEL: func.func @transpose5 +// CHECK: %[[ARG0:.*]]: !fir.box> +// CHECK-NEXT: %[[RES:.*]] = hlfir.transpose %[[ARG0]] : (!fir.box>) -> !hlfir.expr<2x1xi32> +// CHECK-NEXT: return +// CHECK-NEXT: }