[flang-commits] [flang] a398981 - [flang] Add fir.declare operation
Jean Perier via flang-commits
flang-commits at lists.llvm.org
Wed Oct 19 02:08:07 PDT 2022
Author: Jean Perier
Date: 2022-10-19T11:06:58+02:00
New Revision: a398981fb0f01473dbf9833677fe858630c38282
URL: https://github.com/llvm/llvm-project/commit/a398981fb0f01473dbf9833677fe858630c38282
DIFF: https://github.com/llvm/llvm-project/commit/a398981fb0f01473dbf9833677fe858630c38282.diff
LOG: [flang] Add fir.declare operation
Add fir.declare operation whose purpose was described in https://reviews.llvm.org/D134285.
It uses the FortranVariableInterfaceOp for most of its logic (including the verifier).
The rational is that all these aspects/logic will be shared by hlfir.designate and
hlfir.associate.
Its codegen and lowering will be added in later patches.
Differential Revision: https://reviews.llvm.org/D136181
Added:
flang/test/Fir/declare.fir
flang/unittests/Optimizer/FortranVariableTest.cpp
Modified:
flang/include/flang/Optimizer/Dialect/FIROps.h
flang/include/flang/Optimizer/Dialect/FIROps.td
flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp
flang/test/Fir/invalid.fir
flang/unittests/Optimizer/CMakeLists.txt
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.h b/flang/include/flang/Optimizer/Dialect/FIROps.h
index df9bd06a98f23..8229497c8e7e2 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.h
@@ -9,7 +9,9 @@
#ifndef FORTRAN_OPTIMIZER_DIALECT_FIROPS_H
#define FORTRAN_OPTIMIZER_DIALECT_FIROPS_H
+#include "flang/Optimizer/Dialect/FIRAttr.h"
#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Interfaces/LoopLikeInterface.h"
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index c4dd839e035af..cdcfc9a2f2ddd 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -16,6 +16,9 @@
include "flang/Optimizer/Dialect/FIRDialect.td"
include "flang/Optimizer/Dialect/FIRTypes.td"
+include "flang/Optimizer/Dialect/FIRAttr.td"
+include "flang/Optimizer/Dialect/FortranVariableInterface.td"
+include "mlir/IR/BuiltinAttributes.td"
// Base class for FIR operations.
// All operations automatically get a prefix of "fir.".
@@ -2863,4 +2866,61 @@ def fir_IsPresentOp : fir_SimpleOp<"is_present", [NoMemoryEffect]> {
let results = (outs BoolLike);
}
+def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments,
+ DeclareOpInterfaceMethods<FortranVariableOpInterface>]> {
+ let summary = "declare a variable";
+
+ let description = [{
+ Tie the properties of a Fortran variable to an address. The properties
+ include bounds, length parameters, and Fortran attributes.
+
+ The memref argument describes the storage of the variable. It may be a
+ raw address (fir.ref<T>), or a box or class value or address (fir.box<T>,
+ fir.ref<fir.box<T>>, fir.class<T>, fir.ref<fir.class<T>>).
+
+ The shape argument encodes explicit extents and lower bounds. It must be
+ provided if the memref is the raw address of an array.
+ The shape argument must not be provided if memref operand is a box or
+ class value or address, unless the shape is a shift (encodes lower bounds)
+ and the memref if a box value (this covers assumed shapes with local lower
+ bounds).
+
+ The typeparams values are meant to carry the non-deferred length parameters
+ (this includes both Fortran assumed and explicit length parameters).
+ It must always be provided for characters and parametrized derived types
+ when memref is not a box value or address.
+
+ Example:
+
+ CHARACTER(n), OPTIONAL, TARGET :: c(10:, 20:)
+
+ Can be represented as:
+ ```
+ func.func @foo(%arg0: !fir.box<!fir.array<?x?x!fir.char<1,?>>>, %arg1: !fir.ref<i64>) {
+ %c10 = arith.constant 10 : index
+ %c20 = arith.constant 20 : index
+ %1 = fir.load %ag1 : fir.ref<i64>
+ %2 = fir.shift %c10, %c20 : (index, index) -> !fir.shift<2>
+ %3 = fir.declare %arg0(%2) typeparams %1 {fortran_attrs = #fir.var_attrs<optional, target>, uniq_name = "c"}
+ // ... uses %3 as "c"
+ }
+ ```
+ }];
+
+ let arguments = (ins
+ AnyRefOrBox:$memref,
+ Optional<AnyShapeOrShiftType>:$shape,
+ Variadic<AnyIntegerType>:$typeparams,
+ Builtin_StringAttr:$uniq_name,
+ OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs
+ );
+
+ let results = (outs AnyRefOrBox);
+
+ let assemblyFormat = [{
+ $memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)?
+ attr-dict `:` functional-type(operands, results)
+ }];
+}
+
#endif
diff --git a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
index 39e8c9a18ee91..68f192f1b54f9 100644
--- a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
+++ b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
@@ -46,8 +46,8 @@ def FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
}]
>,
InterfaceMethod<
- /*desc=*/"Get the shape of the variable",
- /*retTy=*/"llvm::Optional<mlir::Value>",
+ /*desc=*/"Get the shape of the variable. May be a null value.",
+ /*retTy=*/"mlir::Value",
/*methodName=*/"getShape",
/*args=*/(ins),
/*methodBody=*/[{}],
@@ -74,7 +74,7 @@ def FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
/// Get the sequence type or scalar value type corresponding to this
/// variable.
mlir::Type getElementOrSequenceType() {
- return fir::unwrapPassByRefType(getBase().getType());
+ return fir::unwrapPassByRefType(fir::unwrapRefType(getBase().getType()));
}
/// Get the scalar value type corresponding to this variable.
@@ -87,6 +87,17 @@ def FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
return getElementOrSequenceType().isa<fir::SequenceType>();
}
+ /// Return the rank of the entity if it is known at compile time.
+ llvm::Optional<unsigned> getRank() {
+ if (auto sequenceType =
+ getElementOrSequenceType().dyn_cast<fir::SequenceType>()) {
+ if (sequenceType.hasUnknownShape())
+ return {};
+ return sequenceType.getDimension();
+ }
+ return 0;
+ }
+
/// Is this variable a Fortran pointer ?
bool isPointer() {
auto attrs = getFortranAttrs();
@@ -117,8 +128,32 @@ def FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
return getExplicitTypeParams()[0];
}
+ /// Is this variable represented as a fir.box or fir.class value ?
+ bool isBoxValue() {
+ return getBase().getType().isa<fir::BaseBoxType>();
+ }
+
+ /// Is this variable represented as a fir.box or fir.class address ?
+ bool isBoxAddress() {
+ mlir::Type type = getBase().getType();
+ return fir::isa_ref_type(type) &&
+ fir::unwrapRefType(type).isa<fir::BaseBoxType>();
+ }
+
+ /// Is this variable represented as the value or address of a fir.box or
+ /// fir.class ?
+ bool isBox() {
+ return fir::unwrapRefType(getBase().getType()).isa<fir::BaseBoxType>();
+ }
+
+ /// Interface verifier imlementation.
+ mlir::LogicalResult verifyImpl();
+
}];
+ let verify = [{
+ return ::mlir::cast<::fir::FortranVariableOpInterface>($_op).verifyImpl();
+ }];
}
#endif // FORTRANVARIABLEINTERFACE
diff --git a/flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp b/flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp
index 0091a50d9ec87..12e871a172dab 100644
--- a/flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp
+++ b/flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp
@@ -15,3 +15,52 @@
namespace fir {
#include "flang/Optimizer/Dialect/FortranVariableInterface.cpp.inc"
}
+
+mlir::LogicalResult fir::FortranVariableOpInterface::verifyImpl() {
+ const unsigned numExplicitTypeParams = getExplicitTypeParams().size();
+ if (isCharacter()) {
+ if (numExplicitTypeParams > 1)
+ return emitOpError(
+ "of character entity must have at most one length parameter");
+ if (numExplicitTypeParams == 0 && !isBox())
+ return emitOpError("must be provided exactly one type parameter when its "
+ "base is a character that is not a box");
+
+ } else if (auto recordType = getElementType().dyn_cast<fir::RecordType>()) {
+ if (numExplicitTypeParams < recordType.getNumLenParams() && !isBox())
+ return emitOpError("must be provided all the derived type length "
+ "parameters when the base is not a box");
+ if (numExplicitTypeParams > recordType.getNumLenParams())
+ return emitOpError("has too many length parameters");
+ } else if (numExplicitTypeParams != 0) {
+ return emitOpError("of numeric, logical, or assumed type entity must not "
+ "have length parameters");
+ }
+
+ if (isArray()) {
+ if (mlir::Value shape = getShape()) {
+ if (isBoxAddress())
+ return emitOpError("for box address must not have a shape operand");
+ unsigned shapeRank = 0;
+ if (auto shapeType = shape.getType().dyn_cast<fir::ShapeType>()) {
+ shapeRank = shapeType.getRank();
+ } else if (auto shapeShiftType =
+ shape.getType().dyn_cast<fir::ShapeShiftType>()) {
+ shapeRank = shapeShiftType.getRank();
+ } else {
+ if (!isBoxValue())
+ emitOpError("of array entity with a raw address base must have a "
+ "shape operand that is a shape or shapeshift");
+ shapeRank = shape.getType().cast<fir::ShiftType>().getRank();
+ }
+
+ llvm::Optional<unsigned> rank = getRank();
+ if (!rank || *rank != shapeRank)
+ return emitOpError("has conflicting shape and base operand ranks");
+ } else if (!isBox()) {
+ emitOpError("of array entity with a raw address base must have a shape "
+ "operand that is a shape or shapeshift");
+ }
+ }
+ return mlir::success();
+}
diff --git a/flang/test/Fir/declare.fir b/flang/test/Fir/declare.fir
new file mode 100644
index 0000000000000..f335ae41b6871
--- /dev/null
+++ b/flang/test/Fir/declare.fir
@@ -0,0 +1,145 @@
+// Test fir.declare operation parse, verify (no errors), and unparse.
+
+// RUN: fir-opt %s | fir-opt | FileCheck %s
+
+func.func @numeric_declare(%arg0: !fir.ref<f32>) {
+ %0 = fir.declare %arg0 {uniq_name = "x"} : (!fir.ref<f32>) -> !fir.ref<f32>
+ return
+}
+// CHECK-LABEL: func.func @numeric_declare(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<f32>) {
+// CHECK: %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.ref<f32>) -> !fir.ref<f32>
+
+
+func.func @char_declare(%arg0: !fir.boxchar<1> ) {
+ %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
+ %1 = fir.declare %0#0 typeparams %0#1 {uniq_name = "c"} : (!fir.ref<!fir.char<1,?>>, index) -> !fir.ref<!fir.char<1,?>>
+ return
+}
+// CHECK-LABEL: func.func @char_declare(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.boxchar<1>) {
+// CHECK: %[[VAL_1:.*]]:2 = fir.unboxchar %[[VAL_0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
+// CHECK: %[[VAL_2:.*]] = fir.declare %[[VAL_1]]#0 typeparams %[[VAL_1]]#1 {uniq_name = "c"} : (!fir.ref<!fir.char<1,?>>, index) -> !fir.ref<!fir.char<1,?>>
+
+
+func.func @derived_declare(%arg0: !fir.ref<!fir.type<t{field:i32}>>) {
+ %0 = fir.declare %arg0 {uniq_name = "x"} : (!fir.ref<!fir.type<t{field:i32}>>) -> !fir.ref<!fir.type<t{field:i32}>>
+ return
+}
+// CHECK-LABEL: func.func @derived_declare(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.type<t{field:i32}>>) {
+// CHECK: %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.ref<!fir.type<t{field:i32}>>) -> !fir.ref<!fir.type<t{field:i32}>>
+
+
+func.func @pdt_declare(%arg0: !fir.ref<!fir.type<pdt(param:i32){field:i32}>>) {
+ %c1 = arith.constant 1 : index
+ %0 = fir.declare %arg0 typeparams %c1 {uniq_name = "x"} : (!fir.ref<!fir.type<pdt(param:i32){field:i32}>>, index) -> !fir.ref<!fir.type<pdt(param:i32){field:i32}>>
+ return
+}
+// CHECK-LABEL: func.func @pdt_declare(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.type<pdt(param:i32){field:i32}>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK: %[[VAL_2:.*]] = fir.declare %[[VAL_0]] typeparams %[[VAL_1]] {uniq_name = "x"} : (!fir.ref<!fir.type<pdt(param:i32){field:i32}>>, index) -> !fir.ref<!fir.type<pdt(param:i32){field:i32}>>
+
+
+func.func @array_declare(%arg0: !fir.ref<!fir.array<?x?xf32>>) {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %shape = fir.shape %c1, %c2 : (index, index) -> !fir.shape<2>
+ %0 = fir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.ref<!fir.array<?x?xf32>>
+ return
+}
+// CHECK-LABEL: func.func @array_declare(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<?x?xf32>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK: %[[VAL_2:.*]] = arith.constant 2 : index
+// CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_1]], %[[VAL_2]] : (index, index) -> !fir.shape<2>
+// CHECK: %[[VAL_4:.*]] = fir.declare %[[VAL_0]](%[[VAL_3]]) {uniq_name = "x"} : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.ref<!fir.array<?x?xf32>>
+
+
+func.func @array_declare_2(%arg0: !fir.ref<!fir.array<?x?xf32>>) {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %c3 = arith.constant 3 : index
+ %c4 = arith.constant 4 : index
+ %shape = fir.shape_shift %c1, %c2, %c3, %c4 : (index, index, index, index) -> !fir.shapeshift<2>
+ %0 = fir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref<!fir.array<?x?xf32>>, !fir.shapeshift<2>) -> !fir.ref<!fir.array<?x?xf32>>
+ return
+}
+// CHECK-LABEL: func.func @array_declare_2(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<?x?xf32>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK: %[[VAL_2:.*]] = arith.constant 2 : index
+// CHECK: %[[VAL_3:.*]] = arith.constant 3 : index
+// CHECK: %[[VAL_4:.*]] = arith.constant 4 : index
+// CHECK: %[[VAL_5:.*]] = fir.shape_shift %[[VAL_1]], %[[VAL_2]], %[[VAL_3]], %[[VAL_4]] : (index, index, index, index) -> !fir.shapeshift<2>
+// CHECK: %[[VAL_6:.*]] = fir.declare %[[VAL_0]](%[[VAL_5]]) {uniq_name = "x"} : (!fir.ref<!fir.array<?x?xf32>>, !fir.shapeshift<2>) -> !fir.ref<!fir.array<?x?xf32>>
+
+
+func.func @array_declare_box(%arg0: !fir.box<!fir.array<?x?xf32>>) {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %shape = fir.shift %c1, %c2 : (index, index) -> !fir.shift<2>
+ %0 = fir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.box<!fir.array<?x?xf32>>, !fir.shift<2>) -> !fir.box<!fir.array<?x?xf32>>
+ return
+}
+// CHECK-LABEL: func.func @array_declare_box(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<?x?xf32>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK: %[[VAL_2:.*]] = arith.constant 2 : index
+// CHECK: %[[VAL_3:.*]] = fir.shift %[[VAL_1]], %[[VAL_2]] : (index, index) -> !fir.shift<2>
+// CHECK: %[[VAL_4:.*]] = fir.declare %[[VAL_0]](%[[VAL_3]]) {uniq_name = "x"} : (!fir.box<!fir.array<?x?xf32>>, !fir.shift<2>) -> !fir.box<!fir.array<?x?xf32>>
+
+
+func.func @array_declare_char_box(%arg0: !fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
+ %0 = fir.declare %arg0 {uniq_name = "x"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+ return
+}
+// CHECK-LABEL: func.func @array_declare_char_box(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
+// CHECK: %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+
+
+func.func @array_declare_char_box_2(%arg0: !fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %c3 = arith.constant 3 : index
+ %shape = fir.shift %c1, %c2 : (index, index) -> !fir.shift<2>
+ %0 = fir.declare %arg0(%shape) typeparams %c3 {uniq_name = "x"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.shift<2>, index) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+ return
+}
+// CHECK-LABEL: func.func @array_declare_char_box_2(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<?x?x!fir.char<1,?>>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK: %[[VAL_2:.*]] = arith.constant 2 : index
+// CHECK: %[[VAL_3:.*]] = arith.constant 3 : index
+// CHECK: %[[VAL_4:.*]] = fir.shift %[[VAL_1]], %[[VAL_2]] : (index, index) -> !fir.shift<2>
+// CHECK: %[[VAL_5:.*]] = fir.declare %[[VAL_0]](%[[VAL_4]]) typeparams %[[VAL_3]] {uniq_name = "x"} : (!fir.box<!fir.array<?x?x!fir.char<1,?>>>, !fir.shift<2>, index) -> !fir.box<!fir.array<?x?x!fir.char<1,?>>>
+
+
+func.func @array_declare_char_boxaddr(%arg0: !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>) {
+ %0 = fir.declare %arg0 {uniq_name = "x"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>
+ return
+}
+// CHECK-LABEL: func.func @array_declare_char_boxaddr(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>) {
+// CHECK: %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>
+
+
+func.func @array_declare_char_boxaddr_2(%arg0: !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>) {
+ %c3 = arith.constant 3 : index
+ %0 = fir.declare %arg0 typeparams %c3 {uniq_name = "x"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>, index) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>
+ return
+}
+// CHECK-LABEL: func.func @array_declare_char_boxaddr_2(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>) {
+// CHECK: %[[VAL_1:.*]] = arith.constant 3 : index
+// CHECK: %[[VAL_2:.*]] = fir.declare %[[VAL_0]] typeparams %[[VAL_1]] {uniq_name = "x"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>, index) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>
+
+func.func @array_declare_unlimited_polymorphic_boxaddr(%arg0: !fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>) {
+ %0 = fir.declare %arg0 {uniq_name = "x"} : (!fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>
+ return
+}
+// CHECK-LABEL: func.func @array_declare_unlimited_polymorphic_boxaddr(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>) {
+// CHECK: %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>) -> !fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>
diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir
index f7b23c87b2d2f..5d0ac39184211 100644
--- a/flang/test/Fir/invalid.fir
+++ b/flang/test/Fir/invalid.fir
@@ -808,3 +808,123 @@ func.func @test_fortran_var_attrs() {
// expected-error at +1 {{Unknown fortran variable attribute: volatypo}}
%0 = fir.alloca f32 {fortran_attrs = #fir.var_attrs<volatypo>}
}
+
+// -----
+func.func @bad_numeric_declare(%arg0: !fir.ref<f32>) {
+ %c1 = arith.constant 1 : index
+ // expected-error at +1 {{'fir.declare' op requires attribute 'uniq_name'}}
+ %0 = fir.declare %arg0 typeparams %c1 {uniq_typo = "x"} : (!fir.ref<f32>, index) -> !fir.ref<f32>
+ return
+}
+
+// -----
+func.func @bad_numeric_declare(%arg0: !fir.ref<f32>) {
+ %c1 = arith.constant 1 : index
+ // expected-error at +1 {{'fir.declare' op of numeric, logical, or assumed type entity must not have length parameters}}
+ %0 = fir.declare %arg0 typeparams %c1 {uniq_name = "x"} : (!fir.ref<f32>, index) -> !fir.ref<f32>
+ return
+}
+
+// -----
+func.func @bad_char_declare(%arg0: !fir.boxchar<1> ) {
+ %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
+ // expected-error at +1 {{'fir.declare' op must be provided exactly one type parameter when its base is a character that is not a box}}
+ %1 = fir.declare %0#0 {uniq_name = "c"} : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<!fir.char<1,?>>
+ return
+}
+
+// -----
+func.func @bad_char_declare(%arg0: !fir.boxchar<1> ) {
+ %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
+ // expected-error at +1 {{'fir.declare' op of character entity must have at most one length parameter}}
+ %1 = fir.declare %0#0 typeparams %0#1, %0#1 {uniq_name = "c"} : (!fir.ref<!fir.char<1,?>>, index, index) -> !fir.ref<!fir.char<1,?>>
+ return
+}
+
+// -----
+func.func @bad_derived_declare(%arg0: !fir.ref<!fir.type<t{field:i32}>>) {
+ %c1 = arith.constant 1 : index
+ // expected-error at +1 {{'fir.declare' op has too many length parameters}}
+ %0 = fir.declare %arg0 typeparams %c1 {uniq_name = "x"} : (!fir.ref<!fir.type<t{field:i32}>>, index) -> !fir.ref<!fir.type<t{field:i32}>>
+ return
+}
+
+// -----
+func.func @bad_pdt_declare(%arg0: !fir.ref<!fir.type<pdt(param:i32){field:i32}>>) {
+ // expected-error at +1 {{'fir.declare' op must be provided all the derived type length parameters when the base is not a box}}
+ %0 = fir.declare %arg0 {uniq_name = "x"} : (!fir.ref<!fir.type<pdt(param:i32){field:i32}>>) -> !fir.ref<!fir.type<pdt(param:i32){field:i32}>>
+ return
+}
+
+// -----
+func.func @bad_pdt_declare_2(%arg0: !fir.ref<!fir.type<pdt(param:i32){field:i32}>>) {
+ %c1 = arith.constant 1 : index
+ // expected-error at +1 {{'fir.declare' op has too many length parameters}}
+ %0 = fir.declare %arg0 typeparams %c1, %c1 {uniq_name = "x"} : (!fir.ref<!fir.type<pdt(param:i32){field:i32}>>, index, index) -> !fir.ref<!fir.type<pdt(param:i32){field:i32}>>
+ return
+}
+
+
+// -----
+func.func @bad_array_declare(%arg0: !fir.ref<!fir.array<?x?xf32>>) {
+ // expected-error at +1 {{'fir.declare' op of array entity with a raw address base must have a shape operand that is a shape or shapeshift}}
+ %0 = fir.declare %arg0 {uniq_name = "x"} : (!fir.ref<!fir.array<?x?xf32>>) -> !fir.ref<!fir.array<?x?xf32>>
+ return
+}
+
+// -----
+func.func @bad_array_declare_2(%arg0: !fir.ref<!fir.array<?x?xf32>>) {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %shift = fir.shift %c1, %c2 : (index, index) -> !fir.shift<2>
+ // expected-error at +1 {{'fir.declare' op of array entity with a raw address base must have a shape operand that is a shape or shapeshift}}
+ %0 = fir.declare %arg0(%shift) {uniq_name = "x"} : (!fir.ref<!fir.array<?x?xf32>>, !fir.shift<2>) -> !fir.ref<!fir.array<?x?xf32>>
+ return
+}
+
+// -----
+func.func @bad_array_declare_3(%arg0: !fir.ref<!fir.array<?x?xf32>>) {
+ %c1 = arith.constant 1 : index
+ %shape = fir.shape %c1 : (index) -> !fir.shape<1>
+ // expected-error at +1 {{'fir.declare' op has conflicting shape and base operand ranks}}
+ %0 = fir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<1>) -> !fir.ref<!fir.array<?x?xf32>>
+ return
+}
+
+// -----
+func.func @bad_array_declare_4(%arg0: !fir.ref<!fir.array<?x?xf32>>) {
+ %c1 = arith.constant 1 : index
+ %shape = fir.shape_shift %c1, %c1 : (index, index) -> !fir.shapeshift<1>
+ // expected-error at +1 {{'fir.declare' op has conflicting shape and base operand ranks}}
+ %0 = fir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref<!fir.array<?x?xf32>>, !fir.shapeshift<1>) -> !fir.ref<!fir.array<?x?xf32>>
+ return
+}
+
+// -----
+func.func @bad_array_declare_box(%arg0: !fir.box<!fir.array<?x?xf32>>) {
+ %c1 = arith.constant 1 : index
+ %shape = fir.shift %c1 : (index) -> !fir.shift<1>
+ // expected-error at +1 {{'fir.declare' op has conflicting shape and base operand ranks}}
+ %0 = fir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.box<!fir.array<?x?xf32>>, !fir.shift<1>) -> !fir.box<!fir.array<?x?xf32>>
+ return
+}
+
+// -----
+func.func @bad_array_declare_char_boxaddr(%arg0: !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>) {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %shape = fir.shift %c1, %c2 : (index, index) -> !fir.shift<2>
+ // expected-error at +1 {{'fir.declare' op for box address must not have a shape operand}}
+ %0 = fir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>, !fir.shift<2>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?x!fir.char<1,?>>>>>
+ return
+}
+
+// -----
+func.func @bad_array_declare_unlimited_polymorphic_boxaddr(%arg0: !fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>) {
+ %c1 = arith.constant 1 : index
+ %c2 = arith.constant 2 : index
+ %shape = fir.shift %c1, %c2 : (index, index) -> !fir.shift<2>
+ // expected-error at +1 {{'fir.declare' op for box address must not have a shape operand}}
+ %0 = fir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>, !fir.shift<2>) -> !fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>
+ return
+}
diff --git a/flang/unittests/Optimizer/CMakeLists.txt b/flang/unittests/Optimizer/CMakeLists.txt
index 9f8483d8a1195..e23f0cfe39eee 100644
--- a/flang/unittests/Optimizer/CMakeLists.txt
+++ b/flang/unittests/Optimizer/CMakeLists.txt
@@ -24,6 +24,7 @@ add_flang_unittest(FlangOptimizerTests
Builder/Runtime/TransformationalTest.cpp
FIRContextTest.cpp
FIRTypesTest.cpp
+ FortranVariableTest.cpp
InternalNamesTest.cpp
KindMappingTest.cpp
RTBuilder.cpp
diff --git a/flang/unittests/Optimizer/FortranVariableTest.cpp b/flang/unittests/Optimizer/FortranVariableTest.cpp
new file mode 100644
index 0000000000000..ed248fc72b0ae
--- /dev/null
+++ b/flang/unittests/Optimizer/FortranVariableTest.cpp
@@ -0,0 +1,151 @@
+//===- FortranVariableTest.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Support/InitFIR.h"
+
+struct FortranVariableTest : public testing::Test {
+public:
+ void SetUp() {
+ fir::support::loadDialects(context);
+ builder = std::make_unique<mlir::OpBuilder>(&context);
+ mlir::Location loc = builder->getUnknownLoc();
+
+ // Set up a Module with a dummy function operation inside.
+ // Set the insertion point in the function entry block.
+ mlir::ModuleOp mod = builder->create<mlir::ModuleOp>(loc);
+ mlir::func::FuncOp func =
+ mlir::func::FuncOp::create(loc, "fortran_variable_tests",
+ builder->getFunctionType(llvm::None, llvm::None));
+ auto *entryBlock = func.addEntryBlock();
+ mod.push_back(mod);
+ builder->setInsertionPointToStart(entryBlock);
+ }
+
+ mlir::Location getLoc() { return builder->getUnknownLoc(); }
+ mlir::Value createConstant(std::int64_t cst) {
+ mlir::Type indexType = builder->getIndexType();
+ return builder->create<mlir::arith::ConstantOp>(
+ getLoc(), indexType, builder->getIntegerAttr(indexType, cst));
+ }
+
+ mlir::Value createShape(llvm::ArrayRef<mlir::Value> extents) {
+ mlir::Type shapeType = fir::ShapeType::get(&context, extents.size());
+ return builder->create<fir::ShapeOp>(getLoc(), shapeType, extents);
+ }
+ mlir::MLIRContext context;
+ std::unique_ptr<mlir::OpBuilder> builder;
+};
+
+TEST_F(FortranVariableTest, SimpleScalar) {
+ mlir::Location loc = getLoc();
+ mlir::Type eleType = mlir::FloatType::getF32(&context);
+ mlir::Value addr = builder->create<fir::AllocaOp>(loc, eleType);
+ auto name = mlir::StringAttr::get(&context, "x");
+ auto declare = builder->create<fir::DeclareOp>(loc, addr.getType(), addr,
+ /*shape=*/mlir::Value{}, /*typeParams=*/llvm::None, name,
+ /*fortran_attrs=*/fir::FortranVariableFlagsAttr{});
+
+ fir::FortranVariableOpInterface fortranVariable = declare;
+ EXPECT_FALSE(fortranVariable.isArray());
+ EXPECT_FALSE(fortranVariable.isCharacter());
+ EXPECT_FALSE(fortranVariable.isPointer());
+ EXPECT_FALSE(fortranVariable.isAllocatable());
+ EXPECT_FALSE(fortranVariable.hasExplicitCharLen());
+ EXPECT_EQ(fortranVariable.getElementType(), eleType);
+ EXPECT_EQ(fortranVariable.getElementOrSequenceType(),
+ fortranVariable.getElementType());
+ EXPECT_NE(fortranVariable.getBase(), addr);
+ EXPECT_EQ(fortranVariable.getBase().getType(), addr.getType());
+}
+
+TEST_F(FortranVariableTest, CharacterScalar) {
+ mlir::Location loc = getLoc();
+ mlir::Type eleType = fir::CharacterType::getUnknownLen(&context, 4);
+ mlir::Value len = createConstant(42);
+ llvm::SmallVector<mlir::Value> typeParams{len};
+ mlir::Value addr = builder->create<fir::AllocaOp>(
+ loc, eleType, /*pinned=*/false, typeParams);
+ auto name = mlir::StringAttr::get(&context, "x");
+ auto declare = builder->create<fir::DeclareOp>(loc, addr.getType(), addr,
+ /*shape=*/mlir::Value{}, typeParams, name,
+ /*fortran_attrs=*/fir::FortranVariableFlagsAttr{});
+
+ fir::FortranVariableOpInterface fortranVariable = declare;
+ EXPECT_FALSE(fortranVariable.isArray());
+ EXPECT_TRUE(fortranVariable.isCharacter());
+ EXPECT_FALSE(fortranVariable.isPointer());
+ EXPECT_FALSE(fortranVariable.isAllocatable());
+ EXPECT_TRUE(fortranVariable.hasExplicitCharLen());
+ EXPECT_EQ(fortranVariable.getElementType(), eleType);
+ EXPECT_EQ(fortranVariable.getElementOrSequenceType(),
+ fortranVariable.getElementType());
+ EXPECT_NE(fortranVariable.getBase(), addr);
+ EXPECT_EQ(fortranVariable.getBase().getType(), addr.getType());
+ EXPECT_EQ(fortranVariable.getExplicitCharLen(), len);
+}
+
+TEST_F(FortranVariableTest, SimpleArray) {
+ mlir::Location loc = getLoc();
+ mlir::Type eleType = mlir::FloatType::getF32(&context);
+ llvm::SmallVector<mlir::Value> extents{
+ createConstant(10), createConstant(20), createConstant(30)};
+ fir::SequenceType::Shape typeShape(
+ extents.size(), fir::SequenceType::getUnknownExtent());
+ mlir::Type seqTy = fir::SequenceType::get(typeShape, eleType);
+ mlir::Value addr = builder->create<fir::AllocaOp>(
+ loc, seqTy, /*pinned=*/false, /*typeParams=*/llvm::None, extents);
+ mlir::Value shape = createShape(extents);
+ auto name = mlir::StringAttr::get(&context, "x");
+ auto declare = builder->create<fir::DeclareOp>(loc, addr.getType(), addr,
+ shape, /*typeParams*/ llvm::None, name,
+ /*fortran_attrs=*/fir::FortranVariableFlagsAttr{});
+
+ fir::FortranVariableOpInterface fortranVariable = declare;
+ EXPECT_TRUE(fortranVariable.isArray());
+ EXPECT_FALSE(fortranVariable.isCharacter());
+ EXPECT_FALSE(fortranVariable.isPointer());
+ EXPECT_FALSE(fortranVariable.isAllocatable());
+ EXPECT_FALSE(fortranVariable.hasExplicitCharLen());
+ EXPECT_EQ(fortranVariable.getElementType(), eleType);
+ EXPECT_EQ(fortranVariable.getElementOrSequenceType(), seqTy);
+ EXPECT_NE(fortranVariable.getBase(), addr);
+ EXPECT_EQ(fortranVariable.getBase().getType(), addr.getType());
+}
+
+TEST_F(FortranVariableTest, CharacterArray) {
+ mlir::Location loc = getLoc();
+ mlir::Type eleType = fir::CharacterType::getUnknownLen(&context, 4);
+ mlir::Value len = createConstant(42);
+ llvm::SmallVector<mlir::Value> typeParams{len};
+ llvm::SmallVector<mlir::Value> extents{
+ createConstant(10), createConstant(20), createConstant(30)};
+ fir::SequenceType::Shape typeShape(
+ extents.size(), fir::SequenceType::getUnknownExtent());
+ mlir::Type seqTy = fir::SequenceType::get(typeShape, eleType);
+ mlir::Value addr = builder->create<fir::AllocaOp>(
+ loc, seqTy, /*pinned=*/false, typeParams, extents);
+ mlir::Value shape = createShape(extents);
+ auto name = mlir::StringAttr::get(&context, "x");
+ auto declare = builder->create<fir::DeclareOp>(loc, addr.getType(), addr,
+ shape, typeParams, name,
+ /*fortran_attrs=*/fir::FortranVariableFlagsAttr{});
+
+ fir::FortranVariableOpInterface fortranVariable = declare;
+ EXPECT_TRUE(fortranVariable.isArray());
+ EXPECT_TRUE(fortranVariable.isCharacter());
+ EXPECT_FALSE(fortranVariable.isPointer());
+ EXPECT_FALSE(fortranVariable.isAllocatable());
+ EXPECT_TRUE(fortranVariable.hasExplicitCharLen());
+ EXPECT_EQ(fortranVariable.getElementType(), eleType);
+ EXPECT_EQ(fortranVariable.getElementOrSequenceType(), seqTy);
+ EXPECT_NE(fortranVariable.getBase(), addr);
+ EXPECT_EQ(fortranVariable.getBase().getType(), addr.getType());
+ EXPECT_EQ(fortranVariable.getExplicitCharLen(), len);
+}
More information about the flang-commits
mailing list