diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h b/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h --- a/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h +++ b/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h @@ -73,6 +73,7 @@ bool isFortranNumericalArrayObject(mlir::Type); bool isFortranNumericalOrLogicalArrayObject(mlir::Type); bool isFortranArrayObject(mlir::Type); +bool isFortranLogicalArrayObject(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 --- a/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td @@ -133,4 +133,9 @@ def AnyFortranLogicalOrI1ArrayObject : Type; +def IsFortranLogicalArrayPred + : CPred<"::hlfir::isFortranLogicalArrayObject($_self)">; +def AnyFortranLogicalArrayObject : Type; + #endif // FORTRAN_DIALECT_HLFIR_OP_BASE diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td --- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td @@ -317,6 +317,28 @@ let hasVerifier = 1; } +def hlfir_AnyOp : hlfir_Op<"any", []> { + let summary = "ANY transformational intrinsic"; + let description = [{ + Takes a logical array MASK as argument, optionally along a particular dimension, + and returns true if any element of MASK is true. + }]; + + let arguments = (ins + AnyFortranLogicalArrayObject:$mask, + Optional:$dim + ); + + let results = (outs hlfir_ExprType); + + let assemblyFormat = [{ + $mask (`dim` $dim^)? attr-dict `:` functional-type(operands, results) + }]; + + let hasVerifier = 1; +} + + def hlfir_ProductOp : hlfir_Op<"product", [AttrSizedOperandSegments, DeclareOpInterfaceMethods]> { let summary = "PRODUCT transformational intrinsic"; diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp --- a/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIRDialect.cpp @@ -148,6 +148,17 @@ return false; } +bool hlfir::isFortranLogicalArrayObject(mlir::Type type) { + if (isBoxAddressType(type)) + return false; + if (auto arrayTy = + getFortranElementOrSequenceType(type).dyn_cast()) { + mlir::Type eleTy = arrayTy.getEleTy(); + return mlir::isa(eleTy); + } + return false; +} + bool hlfir::isMaskArgument(mlir::Type type) { if (isBoxAddressType(type)) return false; diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp --- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp @@ -441,6 +441,44 @@ return mlir::success(); } +//===----------------------------------------------------------------------===// +// AnyOp +//===----------------------------------------------------------------------===// +mlir::LogicalResult hlfir::AnyOp::verify() { + mlir::Operation *op = getOperation(); + + auto results = op->getResultTypes(); + assert(results.size() == 1); + + mlir::Value mask = getMask(); + fir::SequenceType maskTy = + hlfir::getFortranElementOrSequenceType(mask.getType()) + .cast(); + mlir::Type logicalTy = maskTy.getEleTy(); + llvm::ArrayRef maskShape = maskTy.getShape(); + hlfir::ExprType resultTy = results[0].cast(); + + // Result is of the same type as MASK + if (resultTy.getElementType() != logicalTy) + return emitOpError( + "result must have the same element type as MASK argument"); + + if (resultTy.isArray()) { + // Result is of the same type as MASK + if (resultTy.getEleTy() != logicalTy) + return emitOpError( + "result must have the same element type as MASK argument"); + + llvm::ArrayRef resultShape = resultTy.getShape(); + + // Result has rank n-1 + if (resultShape.size() != (maskShape.size() - 1)) + return emitOpError("result rank must be one less than MASK"); + } + + return mlir::success(); +} + //===----------------------------------------------------------------------===// // ConcatOp //===----------------------------------------------------------------------===// diff --git a/flang/test/HLFIR/any.fir b/flang/test/HLFIR/any.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/any.fir @@ -0,0 +1,113 @@ +// Test hlfir.product operation parse, verify (no errors), and unparse + +// RUN: fir-opt %s | fir-opt | FileCheck %s + +// mask is an expression of known shape +func.func @any0(%arg0: !hlfir.expr<2x!fir.logical<4>>) { + %any = hlfir.any %arg0 : (!hlfir.expr<2x!fir.logical<4>>) -> !hlfir.expr> + return +} +// CHECK: func.func @any0(%[[ARRAY:.*]]: !hlfir.expr<2x!fir.logical<4>>) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] : (!hlfir.expr<2x!fir.logical<4>>) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// mask is an expression of assumed shape +func.func @any1(%arg0: !hlfir.expr>) { + %any = hlfir.any %arg0 : (!hlfir.expr>) -> !hlfir.expr> + return +} +// CHECK: func.func @any1(%[[ARRAY:.*]]: !hlfir.expr>) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] : (!hlfir.expr>) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// mask is a boxed array +func.func @any2(%arg0: !fir.box>>) { + %any = hlfir.any %arg0 : (!fir.box>>) -> !hlfir.expr> + return +} +// CHECK: func.func @any2(%[[ARRAY:.*]]: !fir.box>>) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] : (!fir.box>>) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// mask is an assumed shape boxed array +func.func @any3(%arg0: !fir.box>>){ + %any = hlfir.any %arg0 : (!fir.box>>) -> !hlfir.expr> + return +} +// CHECK: func.func @any3(%[[ARRAY:.*]]: !fir.box>>) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] : (!fir.box>>) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// mask is a 2-dimensional array +func.func @any4(%arg0: !fir.box>>){ + %any = hlfir.any %arg0 : (!fir.box>>) -> !hlfir.expr> + return +} +// CHECK: func.func @any4(%[[ARRAY:.*]]: !fir.box>>) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] : (!fir.box>>) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// mask and dim argument +func.func @any5(%arg0: !fir.box>>, %arg1: i32) { + %any = hlfir.any %arg0 dim %arg1 : (!fir.box>>, i32) -> !hlfir.expr> + return +} +// CHECK: func.func @any5(%[[ARRAY:.*]]: !fir.box>>, %[[DIM:.*]]: i32) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] dim %[[DIM]] : (!fir.box>>, i32) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// hlfir.any with dim argument with an unusual type +func.func @any6(%arg0: !fir.box>>, %arg1: index) { + %any = hlfir.any %arg0 dim %arg1 : (!fir.box>>, index) -> !hlfir.expr> + return +} +// CHECK: func.func @any6(%[[ARRAY:.*]]: !fir.box>>, %[[DIM:.*]]: index) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] dim %[[DIM]] : (!fir.box>>, index) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// mask is a 2 dimensional array with dim +func.func @any7(%arg0: !fir.box>>, %arg1: i32) { + %any = hlfir.any %arg0 dim %arg1 : (!fir.box>>, i32) -> !hlfir.expr> + return +} +// CHECK: func.func @any7(%[[ARRAY:.*]]: !fir.box>>, %[[DIM:.*]]: i32) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] dim %[[DIM]] : (!fir.box>>, i32) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// known shape expr return +func.func @any8(%arg0: !fir.box>>, %arg1: i32) { + %any = hlfir.any %arg0 dim %arg1 : (!fir.box>>, i32) -> !hlfir.expr<2x!fir.logical<4>> + return +} +// CHECK: func.func @any8(%[[ARRAY:.*]]: !fir.box>>, %[[DIM:.*]]: i32) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] dim %[[DIM]] : (!fir.box>>, i32) -> !hlfir.expr<2x!fir.logical<4>> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// hlfir.any with mask argument of ref> type +func.func @any9(%arg0: !fir.ref>>) { + %any = hlfir.any %arg0 : (!fir.ref>>) -> !hlfir.expr> + return +} +// CHECK: func.func @any9(%[[ARRAY:.*]]: !fir.ref>>) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] : (!fir.ref>>) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } + +// hlfir.any with fir.logical<8> type +func.func @any10(%arg0: !fir.box>>) { + %any = hlfir.any %arg0 : (!fir.box>>) -> !hlfir.expr> + return +} +// CHECK: func.func @any10(%[[ARRAY:.*]]: !fir.box>>) { +// CHECK-NEXT: %[[ANY:.*]] = hlfir.any %[[ARRAY]] : (!fir.box>>) -> !hlfir.expr> +// CHECK-NEXT: return +// CHECK-NEXT: } diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir --- a/flang/test/HLFIR/invalid.fir +++ b/flang/test/HLFIR/invalid.fir @@ -296,6 +296,25 @@ return } +// ----- +func.func @bad_any1(%arg0: !hlfir.expr>) { + // expected-error@+1 {{'hlfir.any' op result must have the same element type as MASK argument}} + %0 = hlfir.any %arg0 : (!hlfir.expr>) -> !hlfir.expr> +} + +// ----- +func.func @bad_any2(%arg0: !hlfir.expr>) { + // expected-error@+1 {{'hlfir.any' op result must have the same element type as MASK argument}} + %0 = hlfir.any %arg0 : (!hlfir.expr>) -> !hlfir.expr> +} + +// ----- +func.func @bad_any3(%arg0: !hlfir.expr>, %arg1: i32){ + // expected-error@+1 {{'hlfir.any' op result rank must be one less than MASK}} + %0 = hlfir.any %arg0 dim %arg1 : (!hlfir.expr>, i32) -> !hlfir.expr> +} + + // ----- func.func @bad_product1(%arg0: !hlfir.expr, %arg1: i32, %arg2: !fir.box>) { // expected-error@+1 {{'hlfir.product' op result must have the same element type as ARRAY argument}}