diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td --- a/flang/include/flang/Optimizer/Dialect/FIROps.td +++ b/flang/include/flang/Optimizer/Dialect/FIROps.td @@ -2931,6 +2931,8 @@ $memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)? attr-dict `:` functional-type(operands, results) }]; + + let hasVerifier = 1; } #endif diff --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h --- a/flang/include/flang/Optimizer/Dialect/FIRType.h +++ b/flang/include/flang/Optimizer/Dialect/FIRType.h @@ -335,6 +335,16 @@ return fir::BoxType::get(eleTy); } +/// Is `t` an address to fir.box or class type? +inline bool isBoxAddress(mlir::Type t) { + return fir::isa_ref_type(t) && fir::unwrapRefType(t).isa(); +} + +/// Is `t` a fir.box or class address or value type? +inline bool isBoxAddressOrValue(mlir::Type t) { + return fir::unwrapRefType(t).isa(); +} + } // namespace fir #endif // FORTRAN_OPTIMIZER_DIALECT_FIRTYPE_H diff --git a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td --- a/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td +++ b/flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td @@ -74,7 +74,10 @@ /// Get the sequence type or scalar value type corresponding to this /// variable. mlir::Type getElementOrSequenceType() { - return fir::unwrapPassByRefType(fir::unwrapRefType(getBase().getType())); + mlir::Type type = fir::unwrapPassByRefType(fir::unwrapRefType(getBase().getType())); + if (auto boxCharType = type.dyn_cast()) + return boxCharType.getEleTy(); + return type; } /// Get the scalar value type corresponding to this variable. @@ -135,27 +138,22 @@ /// 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(); + return fir::isBoxAddress(getBase().getType()); } /// Is this variable represented as the value or address of a fir.box or /// fir.class ? bool isBox() { - return fir::unwrapRefType(getBase().getType()).isa(); + return fir::isBoxAddressOrValue(getBase().getType()); } - /// Interface verifier imlementation. - mlir::LogicalResult verifyImpl(); + /// Interface verifier imlementation for declare operations + mlir::LogicalResult verifyDeclareLikeOpImpl(mlir::Value memRef); }]; let cppNamespace = "fir"; - let verify = [{ - return ::mlir::cast<::fir::FortranVariableOpInterface>($_op).verifyImpl(); - }]; } #endif // FORTRANVARIABLEINTERFACE diff --git a/flang/include/flang/Optimizer/HLFIR/CMakeLists.txt b/flang/include/flang/Optimizer/HLFIR/CMakeLists.txt --- a/flang/include/flang/Optimizer/HLFIR/CMakeLists.txt +++ b/flang/include/flang/Optimizer/HLFIR/CMakeLists.txt @@ -5,4 +5,9 @@ mlir_tablegen(HLFIRDialect.cpp.inc -gen-dialect-defs -dialect=hlfir) mlir_tablegen(HLFIRAttributes.h.inc -gen-attrdef-decls -attrdefs-dialect=hlfir) mlir_tablegen(HLFIRAttributes.cpp.inc -gen-attrdef-defs -attrdefs-dialect=hlfir) + +set(LLVM_TARGET_DEFINITIONS HLFIROps.td) +mlir_tablegen(HLFIROps.h.inc -gen-op-decls) +mlir_tablegen(HLFIROps.cpp.inc -gen-op-defs) + add_public_tablegen_target(HLFIROpsIncGen) 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 @@ -16,6 +16,11 @@ #include "mlir/IR/Dialect.h" +namespace hlfir { +/// Is this a type that can be used for an HLFIR variable ? +bool isFortranVariableType(mlir::Type); +} // namespace hlfir + #include "flang/Optimizer/HLFIR/HLFIRDialect.h.inc" #define GET_TYPEDEF_CLASSES 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 @@ -70,4 +70,9 @@ } +def IsFortranVariablePred + : CPred<"::hlfir::isFortranVariableType($_self)">; + +def AnyFortranVariableLike : Type; + #endif // FORTRAN_DIALECT_HLFIR_OP_BASE diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.h b/flang/include/flang/Optimizer/HLFIR/HLFIROps.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.h @@ -0,0 +1,21 @@ +//===-- HLFIROps.h - FIR operations -----------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_OPTIMIZER_HLFIR_HLFIROPS_H +#define FORTRAN_OPTIMIZER_HLFIR_HLFIROPS_H + +#include "flang/Optimizer/Dialect/FIRAttr.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "flang/Optimizer/Dialect/FortranVariableInterface.h" +#include "flang/Optimizer/HLFIR/HLFIRDialect.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" + +#define GET_OP_CLASSES +#include "flang/Optimizer/HLFIR/HLFIROps.h.inc" + +#endif // FORTRAN_OPTIMIZER_HLFIR_HLFIROPS_H diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td new file mode 100644 --- /dev/null +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td @@ -0,0 +1,119 @@ +//===-- HLFIROps.td - HLFIR operation definitions ----------*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Definition of the HLFIR dialect operations +/// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_DIALECT_HLFIR_OPS +#define FORTRAN_DIALECT_HLFIR_OPS + +include "flang/Optimizer/HLFIR/HLFIROpBase.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 "hlfir.". +class hlfir_Op traits> + : Op; + + + +def hlfir_DeclareOp : hlfir_Op<"declare", [AttrSizedOperandSegments, + DeclareOpInterfaceMethods]> { + let summary = "declare a variable and produce an SSA value that can be used as a variable in HLFIR operations"; + + let description = [{ + Tie the properties of a Fortran variable to an address. The properties + include bounds, length parameters, and Fortran attributes. + + The arguments are the same as for fir.declare. + + The main difference with fir.declare is that hlfir.declare returns two + values: + - the first one is an SSA value that allows retrieving the variable + address, bounds, and type parameters at any point without requiring + access to the defining operation. This may be: + - for scalar numerical, logical, or derived type without length + parameters: a fir.ref (e.g. fir.ref) + - for scalar characters: a fir.boxchar or fir.ref> + - for arrays of types without length parameters, without lower bounds, + that are not polymorphic and with a constant shape: + fir.ref> + - for all non pointer/non allocatable entities: fir.box, and + fir.class for polymorphic entities. + - for all pointers/allocatables: + fir.ref>>/fir.ref>> + - the second value has the same type as the input memref, and is the + same. If it is a fir.box or fir.class, it may not contain accurate + local lower bound values. It is intended to be used when generating FIR + from HLFIR in order to avoid descriptor creation for simple entities. + + Example: + + CHARACTER(n) :: c(10:n, 20:n) + + Can be represented as: + ``` + func.func @foo(%arg0: !fir.ref>>, %arg1: !fir.ref) { + %c10 = arith.constant 10 : index + %c20 = arith.constant 20 : index + %1 = fir.load %ag1 : fir.ref + %2 = fir.shape_shift %c10, %1, %c20, %1 : (index, index, index, index) -> !fir.shapeshift<2> + %3 = hfir.declare %arg0(%2) typeparams %1 {uniq_name = "c"} (fir.ref>>, fir.shapeshift<2>, index) -> (fir.box>>, fir.ref>>) + // ... uses %3#0 as "c" + } + ``` + }]; + + let arguments = (ins + AnyRefOrBox:$memref, + Optional:$shape, + Variadic:$typeparams, + Builtin_StringAttr:$uniq_name, + OptionalAttr:$fortran_attrs + ); + + let results = (outs AnyFortranVariableLike, AnyRefOrBoxLike); + + let assemblyFormat = [{ + $memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)? + attr-dict `:` functional-type(operands, results) + }]; + + let builders = [ + OpBuilder<(ins "mlir::Value":$memref, "llvm::StringRef":$uniq_name, + CArg<"mlir::Value", "{}">:$shape, CArg<"mlir::ValueRange", "{}">:$typeparams, + CArg<"fir::FortranVariableFlagsAttr", "{}">:$fortran_attrs)>]; + + let extraClassDeclaration = [{ + /// Get the variable original base (same as input). It lacks + /// any explicit lower bounds and the extents might not be retrievable + /// from it. This matches what is used as a "base" in FIR. + mlir::Value getOriginalBase() { + return getResult(1); + } + + /// Override FortranVariableInterface default implementation + mlir::Value getBase() { + return getResult(0); + } + + /// Given a FIR memory type, and information about non default lower + /// bounds, get the related HLFIR variable type. + static mlir::Type getHLFIRVariableType(mlir::Type type, bool hasLowerBounds); + }]; + + let hasVerifier = 1; +} + +#endif // FORTRAN_DIALECT_HLFIR_OPS diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp --- a/flang/lib/Optimizer/Dialect/FIROps.cpp +++ b/flang/lib/Optimizer/Dialect/FIROps.cpp @@ -3577,6 +3577,12 @@ return eleTy; } +mlir::LogicalResult fir::DeclareOp::verify() { + auto fortranVar = + mlir::cast(this->getOperation()); + return fortranVar.verifyDeclareLikeOpImpl(getMemref()); +} + // Tablegen operators #define GET_OP_CLASSES diff --git a/flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp b/flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp --- a/flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp +++ b/flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp @@ -14,18 +14,23 @@ #include "flang/Optimizer/Dialect/FortranVariableInterface.cpp.inc" -mlir::LogicalResult fir::FortranVariableOpInterface::verifyImpl() { +mlir::LogicalResult +fir::FortranVariableOpInterface::verifyDeclareLikeOpImpl(mlir::Value memref) { const unsigned numExplicitTypeParams = getExplicitTypeParams().size(); + mlir::Type memType = memref.getType(); + const bool sourceIsBoxValue = memType.isa(); + const bool sourceIsBoxAddress = fir::isBoxAddress(memType); + const bool sourceIsBox = sourceIsBoxValue || sourceIsBoxAddress; if (isCharacter()) { if (numExplicitTypeParams > 1) return emitOpError( "of character entity must have at most one length parameter"); - if (numExplicitTypeParams == 0 && !isBox()) + if (numExplicitTypeParams == 0 && !sourceIsBox) 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()) { - if (numExplicitTypeParams < recordType.getNumLenParams() && !isBox()) + if (numExplicitTypeParams < recordType.getNumLenParams() && !sourceIsBox) return emitOpError("must be provided all the derived type length " "parameters when the base is not a box"); if (numExplicitTypeParams > recordType.getNumLenParams()) @@ -37,7 +42,7 @@ if (isArray()) { if (mlir::Value shape = getShape()) { - if (isBoxAddress()) + if (sourceIsBoxAddress) return emitOpError("for box address must not have a shape operand"); unsigned shapeRank = 0; if (auto shapeType = shape.getType().dyn_cast()) { @@ -46,7 +51,7 @@ shape.getType().dyn_cast()) { shapeRank = shapeShiftType.getRank(); } else { - if (!isBoxValue()) + if (!sourceIsBoxValue) emitOpError("of array entity with a raw address base must have a " "shape operand that is a shape or shapeshift"); shapeRank = shape.getType().cast().getRank(); @@ -55,7 +60,7 @@ llvm::Optional rank = getRank(); if (!rank || *rank != shapeRank) return emitOpError("has conflicting shape and base operand ranks"); - } else if (!isBox()) { + } else if (!sourceIsBox) { emitOpError("of array entity with a raw address base must have a shape " "operand that is a shape or shapeshift"); } diff --git a/flang/lib/Optimizer/HLFIR/IR/CMakeLists.txt b/flang/lib/Optimizer/HLFIR/IR/CMakeLists.txt --- a/flang/lib/Optimizer/HLFIR/IR/CMakeLists.txt +++ b/flang/lib/Optimizer/HLFIR/IR/CMakeLists.txt @@ -2,6 +2,7 @@ add_flang_library(HLFIRDialect HLFIRDialect.cpp + HLFIROps.cpp DEPENDS FIRDialect 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 @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "flang/Optimizer/HLFIR/HLFIRDialect.h" +#include "flang/Optimizer/HLFIR/HLFIROps.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/DialectImplementation.h" @@ -31,6 +32,10 @@ #define GET_TYPEDEF_LIST #include "flang/Optimizer/HLFIR/HLFIRTypes.cpp.inc" >(); + addOperations< +#define GET_OP_LIST +#include "flang/Optimizer/HLFIR/HLFIROps.cpp.inc" + >(); } // `expr` `<` `*` | bounds (`x` bounds)* `:` type [`?`] `>` @@ -70,3 +75,13 @@ printer << '?'; printer << '>'; } + +bool hlfir::isFortranVariableType(mlir::Type type) { + return llvm::TypeSwitch(type) + .Case([](auto p) { + mlir::Type eleType = p.getEleTy(); + return eleType.isa() || !fir::hasDynamicSize(eleType); + }) + .Case([](auto) { return true; }) + .Default([](mlir::Type) { return false; }); +} diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp @@ -0,0 +1,83 @@ +//===-- HLFIROps.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 +// +//===----------------------------------------------------------------------===// +// +// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/HLFIR/HLFIROps.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/Matchers.h" +#include "mlir/IR/OpImplementation.h" +#include "llvm/ADT/TypeSwitch.h" +#include + +//===----------------------------------------------------------------------===// +// DeclareOp +//===----------------------------------------------------------------------===// + +/// Given a FIR memory type, and information about non default lower bounds, get +/// the related HLFIR variable type. +mlir::Type hlfir::DeclareOp::getHLFIRVariableType(mlir::Type inputType, + bool hasExplicitLowerBounds) { + mlir::Type type = fir::unwrapRefType(inputType); + if (type.isa()) + return inputType; + if (auto charType = type.dyn_cast()) + if (charType.hasDynamicLen()) + return fir::BoxCharType::get(charType.getContext(), charType.getFKind()); + + auto seqType = type.dyn_cast(); + bool hasDynamicExtents = + seqType && fir::sequenceWithNonConstantShape(seqType); + mlir::Type eleType = seqType ? seqType.getEleTy() : type; + bool hasDynamicLengthParams = fir::characterWithDynamicLen(eleType) || + fir::isRecordWithTypeParameters(eleType); + if (hasExplicitLowerBounds || hasDynamicExtents || hasDynamicLengthParams) + return fir::BoxType::get(type); + return inputType; +} + +static bool hasExplicitLowerBounds(mlir::Value shape) { + return shape && shape.getType().isa(); +} + +void hlfir::DeclareOp::build(mlir::OpBuilder &builder, + mlir::OperationState &result, mlir::Value memref, + llvm::StringRef uniq_name, mlir::Value shape, + mlir::ValueRange typeparams, + fir::FortranVariableFlagsAttr fortran_attrs) { + auto nameAttr = builder.getStringAttr(uniq_name); + mlir::Type inputType = memref.getType(); + bool hasExplicitLbs = hasExplicitLowerBounds(shape); + mlir::Type hlfirVariableType = + getHLFIRVariableType(inputType, hasExplicitLbs); + build(builder, result, {hlfirVariableType, inputType}, memref, shape, + typeparams, nameAttr, fortran_attrs); +} + +mlir::LogicalResult hlfir::DeclareOp::verify() { + if (getMemref().getType() != getResult(1).getType()) + return emitOpError("second result type must match input memref type"); + mlir::Type hlfirVariableType = getHLFIRVariableType( + getMemref().getType(), hasExplicitLowerBounds(getShape())); + if (hlfirVariableType != getResult(0).getType()) + return emitOpError("first result type is inconsistent with variable " + "properties: expected ") + << hlfirVariableType; + // The rest of the argument verification is done by the + // FortranVariableInterface verifier. + auto fortranVar = + mlir::cast(this->getOperation()); + return fortranVar.verifyDeclareLikeOpImpl(getMemref()); +} + +#define GET_OP_CLASSES +#include "flang/Optimizer/HLFIR/HLFIROps.cpp.inc" diff --git a/flang/test/HLFIR/declare.fir b/flang/test/HLFIR/declare.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/declare.fir @@ -0,0 +1,163 @@ +// Test hlfir.declare operation parse, verify (no errors), and unparse. + +// RUN: fir-opt %s | fir-opt | FileCheck %s + +func.func @numeric_declare(%arg0: !fir.ref) { + %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.ref) -> (!fir.ref, !fir.ref) + return +} +// CHECK-LABEL: func.func @numeric_declare( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref) { +// CHECK: %[[VAL_1:.*]] = hlfir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.ref) -> (!fir.ref, !fir.ref) + + +func.func @char_declare(%arg0: !fir.boxchar<1> ) { + %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref>, index) + %1:2 = hlfir.declare %0#0 typeparams %0#1 {uniq_name = "c"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + 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>, index) +// CHECK: %[[VAL_2:.*]] = hlfir.declare %[[VAL_1]]#0 typeparams %[[VAL_1]]#1 {uniq_name = "c"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + + +func.func @derived_declare(%arg0: !fir.ref>) { + %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.ref>) -> (!fir.ref>, !fir.ref>) + return +} +// CHECK-LABEL: func.func @derived_declare( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>) { +// CHECK: %[[VAL_1:.*]] = hlfir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.ref>) -> (!fir.ref>, !fir.ref>) + + +func.func @pdt_declare(%arg0: !fir.ref>) { + %c1 = arith.constant 1 : index + %0:2 = hlfir.declare %arg0 typeparams %c1 {uniq_name = "x"} : (!fir.ref>, index) -> (!fir.box>, !fir.ref>) + return +} +// CHECK-LABEL: func.func @pdt_declare( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>) { +// CHECK: %[[VAL_1:.*]] = arith.constant 1 : index +// CHECK: %[[VAL_2:.*]] = hlfir.declare %[[VAL_0]] typeparams %[[VAL_1]] {uniq_name = "x"} : (!fir.ref>, index) -> (!fir.box>, !fir.ref>) + + +func.func @array_declare(%arg0: !fir.ref>) { + %c1 = arith.constant 1 : index + %c2 = arith.constant 2 : index + %shape = fir.shape %c1, %c2 : (index, index) -> !fir.shape<2> + %0:2 = hlfir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref>, !fir.shape<2>) -> (!fir.box>, !fir.ref>) + return +} +// CHECK-LABEL: func.func @array_declare( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>) { +// 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:.*]] = hlfir.declare %[[VAL_0]](%[[VAL_3]]) {uniq_name = "x"} : (!fir.ref>, !fir.shape<2>) -> (!fir.box>, !fir.ref>) + + +func.func @array_declare_2(%arg0: !fir.ref>) { + %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:2 = hlfir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref>, !fir.shapeshift<2>) -> (!fir.box>, !fir.ref>) + return +} +// CHECK-LABEL: func.func @array_declare_2( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>) { +// 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:.*]] = hlfir.declare %[[VAL_0]](%[[VAL_5]]) {uniq_name = "x"} : (!fir.ref>, !fir.shapeshift<2>) -> (!fir.box>, !fir.ref>) + +func.func @array_declare_constant_extents_with_lower_bounds(%arg0: !fir.ref>) { + %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:2 = hlfir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref>, !fir.shapeshift<2>) -> (!fir.box>, !fir.ref>) + return +} +// CHECK-LABEL: func.func @array_declare_constant_extents_with_lower_bounds( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>) { +// 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:.*]] = hlfir.declare %[[VAL_0]](%[[VAL_5]]) {uniq_name = "x"} : (!fir.ref>, !fir.shapeshift<2>) -> (!fir.box>, !fir.ref>) + + +func.func @array_declare_box(%arg0: !fir.box>) { + %c1 = arith.constant 1 : index + %c2 = arith.constant 2 : index + %shape = fir.shift %c1, %c2 : (index, index) -> !fir.shift<2> + %0:2 = hlfir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.box>, !fir.shift<2>) -> (!fir.box>, !fir.box>) + return +} +// CHECK-LABEL: func.func @array_declare_box( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.box>) { +// 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:.*]] = hlfir.declare %[[VAL_0]](%[[VAL_3]]) {uniq_name = "x"} : (!fir.box>, !fir.shift<2>) -> (!fir.box>, !fir.box>) + + +func.func @array_declare_char_box(%arg0: !fir.box>>) { + %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.box>>) -> (!fir.box>>, !fir.box>>) + return +} +// CHECK-LABEL: func.func @array_declare_char_box( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.box>>) { +// CHECK: %[[VAL_1:.*]] = hlfir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.box>>) -> (!fir.box>>, !fir.box>>) + + +func.func @array_declare_char_box_2(%arg0: !fir.box>>) { + %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:2 = hlfir.declare %arg0(%shape) typeparams %c3 {uniq_name = "x"} : (!fir.box>>, !fir.shift<2>, index) -> (!fir.box>>, !fir.box>>) + return +} +// CHECK-LABEL: func.func @array_declare_char_box_2( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.box>>) { +// 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:.*]] = hlfir.declare %[[VAL_0]](%[[VAL_4]]) typeparams %[[VAL_3]] {uniq_name = "x"} : (!fir.box>>, !fir.shift<2>, index) -> (!fir.box>>, !fir.box>>) + + +func.func @array_declare_char_boxaddr(%arg0: !fir.ref>>>>) { + %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.ref>>>>) -> (!fir.ref>>>>, !fir.ref>>>>) + return +} +// CHECK-LABEL: func.func @array_declare_char_boxaddr( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>>>>) { +// CHECK: %[[VAL_1:.*]] = hlfir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.ref>>>>) -> (!fir.ref>>>>, !fir.ref>>>>) + + +func.func @array_declare_char_boxaddr_2(%arg0: !fir.ref>>>>) { + %c3 = arith.constant 3 : index + %0:2 = hlfir.declare %arg0 typeparams %c3 {uniq_name = "x"} : (!fir.ref>>>>, index) -> (!fir.ref>>>>, !fir.ref>>>>) + return +} +// CHECK-LABEL: func.func @array_declare_char_boxaddr_2( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>>>>) { +// CHECK: %[[VAL_1:.*]] = arith.constant 3 : index +// CHECK: %[[VAL_2:.*]] = hlfir.declare %[[VAL_0]] typeparams %[[VAL_1]] {uniq_name = "x"} : (!fir.ref>>>>, index) -> (!fir.ref>>>>, !fir.ref>>>>) + +func.func @array_declare_unlimited_polymorphic_boxaddr(%arg0: !fir.ref>>>) { + %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.ref>>>) -> (!fir.ref>>>, !fir.ref>>>) + return +} +// CHECK-LABEL: func.func @array_declare_unlimited_polymorphic_boxaddr( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref>>>) { +// CHECK: %[[VAL_1:.*]] = hlfir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.ref>>>) -> (!fir.ref>>>, !fir.ref>>>) diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/invalid.fir @@ -0,0 +1,37 @@ +// HLFIR ops diagnotic tests + +// RUN: fir-opt -split-input-file -verify-diagnostics %s + +func.func @bad_declare(%arg0: !fir.ref) { + // expected-error@+1 {{'hlfir.declare' op first result type is inconsistent with variable properties: expected '!fir.ref'}} + %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.ref) -> (!fir.box, !fir.ref) + return +} + +// ----- +func.func @bad_declare_lower_bounds(%arg0: !fir.ref>) { + %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> + // expected-error@+1 {{'hlfir.declare' op first result type is inconsistent with variable properties: expected '!fir.box>'}} + %0:2 = hlfir.declare %arg0(%shape) {uniq_name = "x"} : (!fir.ref>, !fir.shapeshift<2>) -> (!fir.ref>, !fir.ref>) + return +} + +// ----- +func.func @bad_declare(%arg0: !fir.ref) { + // expected-error@+1 {{'hlfir.declare' op second result type must match input memref type}} + %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.ref) -> (!fir.ref, !fir.ref) + return +} + +// ----- + +// Test that FortranVariableInterface verifier is kicking in. This verifier itself is already tested with fir.declare. +func.func @bad_array_declare(%arg0: !fir.ref>) { + // expected-error@+1 {{'hlfir.declare' op of array entity with a raw address base must have a shape operand that is a shape or shapeshift}} + %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.ref>) -> (!fir.box>, !fir.ref>) + return +}