[flang] Added storage specification for [hl]fir.declare. (#155325)

As proposed in
https://discourse.llvm.org/t/rfc-flang-representation-for-objects-inside-physical-storage/88026,
this patch adds a `storage` Value operand and a `storage_offset`
Integer attribute for `[hl]fir.declare` operations.

The `storage` operand indicates the raw address of the physical storage
a variable belongs to. This is the beginning address of the physical
storage.
The `storage_offset` specifies a byte offset within the physical storage
where the variable object starts.
This commit is contained in:
Slava Zakharin 2025-08-27 19:48:44 -07:00 committed by GitHub
parent a0af7b8fc3
commit 199d3d77dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 290 additions and 36 deletions

View File

@ -3178,9 +3178,11 @@ def fir_IsPresentOp : fir_SimpleOp<"is_present", [NoMemoryEffect]> {
// operations if the values are unused. fir.declare may be used to generate
// debug information so we would like to keep this around even if the value
// is not used.
def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments,
MemoryEffects<[MemAlloc<DebuggingResource>]>,
DeclareOpInterfaceMethods<fir_FortranVariableOpInterface>]> {
def fir_DeclareOp
: fir_Op<"declare", [AttrSizedOperandSegments,
MemoryEffects<[MemAlloc<DebuggingResource>]>,
DeclareOpInterfaceMethods<
fir_FortranVariableStorageOpInterface>]> {
let summary = "declare a variable";
let description = [{
@ -3203,6 +3205,11 @@ def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments,
It must always be provided for characters and parametrized derived types
when memref is not a box value or address.
The storage and storage_offset operands are optional and are required
for FortranVariableStorageOpInterface, where they are documented.
If these operands are absent, then the storage of the declared variable
is only known to start where the memref operand points to.
Example:
CHARACTER(n), OPTIONAL, TARGET :: c(10:, 20:)
@ -3220,21 +3227,22 @@ def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments,
```
}];
let arguments = (ins
AnyRefOrBox:$memref,
Optional<AnyShapeOrShiftType>:$shape,
Variadic<AnyIntegerType>:$typeparams,
Optional<fir_DummyScopeType>:$dummy_scope,
Builtin_StringAttr:$uniq_name,
OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
OptionalAttr<cuf_DataAttributeAttr>:$data_attr
);
let arguments = (ins AnyRefOrBox:$memref,
Optional<AnyShapeOrShiftType>:$shape,
Variadic<AnyIntegerType>:$typeparams,
Optional<fir_DummyScopeType>:$dummy_scope,
Optional<AnyReferenceLike>:$storage,
DefaultValuedAttr<UI64Attr, "0">:$storage_offset,
Builtin_StringAttr:$uniq_name,
OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
OptionalAttr<cuf_DataAttributeAttr>:$data_attr);
let results = (outs AnyRefOrBox);
let assemblyFormat = [{
$memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)?
(`dummy_scope` $dummy_scope^)?
(`storage` `(` $storage^ `[` $storage_offset `]` `)`)?
attr-dict `:` functional-type(operands, results)
}];

View File

@ -610,9 +610,10 @@ def AnyCompositeLike : TypeConstraint<Or<[fir_RecordType.predicate,
"any composite">;
// Reference types
def AnyReferenceLike : TypeConstraint<Or<[fir_ReferenceType.predicate,
fir_HeapType.predicate, fir_PointerType.predicate,
fir_LLVMPointerType.predicate]>, "any reference">;
def AnyReferenceLike
: Type<Or<[fir_ReferenceType.predicate, fir_HeapType.predicate,
fir_PointerType.predicate, fir_LLVMPointerType.predicate]>,
"any reference">;
def FuncType : TypeConstraint<FunctionType.predicate, "function type">;

View File

@ -19,6 +19,11 @@
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/OpDefinition.h"
namespace fir::detail {
/// Verify operations implementing FortranVariableStorageOpInterface.
mlir::LogicalResult verifyFortranVariableStorageOpInterface(mlir::Operation *);
} // namespace fir::detail
#include "flang/Optimizer/Dialect/FortranVariableInterface.h.inc"
#endif // FORTRAN_OPTIMIZER_DIALECT_FORTRANVARIABLEINTERFACE_H

View File

@ -213,4 +213,56 @@ def fir_FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
}
def fir_FortranVariableStorageOpInterface
: OpInterface<"FortranVariableStorageOpInterface",
[fir_FortranVariableOpInterface]> {
let description = [{
An extension of FortranVariableOpInterface for operations that provide
information about the physical storage layout of the variable.
The operations provide the raw address of the physical storage
and the byte offset where the variable begins within the physical
storage.
The storage is a reference to an array of known size consisting
of i8 elements. This is how Flang represents COMMON and EQUIVALENCE
storage blocks with the member variables located within the storage
at different offsets. The storage offset for a variable must not
exceed the storage size. Note that the zero-sized variables
may start at the offset that is after the final byte of the storage.
When getStorage() returns nullptr, getStorageOffset() must return 0.
This means that nothing is known about the physical storage
of the variable (beyond the information maybe provided
by the concrete operation itself, e.g. fir.declare defines
the physical storage of a variable via memref operand,
where the variable starts).
}];
let methods =
[InterfaceMethod<
/*desc=*/"Returns the raw address of the physical storage",
/*retTy=*/"mlir::Value",
/*methodName=*/"getStorage",
/*args=*/(ins),
/*methodBody=*/[{}],
/*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getStorage();
}]>,
InterfaceMethod<
/*desc=*/"Returns the byte offset where the variable begins "
"within the physical storage",
/*retTy=*/"std::uint64_t",
/*methodName=*/"getStorageOffset",
/*args=*/(ins),
/*methodBody=*/[{}],
/*defaultImplementation=*/[{
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
return op.getStorageOffset();
}]>,
];
let cppNamespace = "fir";
let verify =
[{ return detail::verifyFortranVariableStorageOpInterface($_op); }];
}
#endif // FORTRANVARIABLEINTERFACE

View File

@ -35,9 +35,11 @@ class hlfir_Op<string mnemonic, list<Trait> traits>
// removed by dead code elimination if the value result is unused. Information
// from the declare operation can be used to generate debug information so we
// don't want to remove it as dead code
def hlfir_DeclareOp : hlfir_Op<"declare", [AttrSizedOperandSegments,
MemoryEffects<[MemAlloc<DebuggingResource>]>,
DeclareOpInterfaceMethods<fir_FortranVariableOpInterface>]> {
def hlfir_DeclareOp
: hlfir_Op<"declare", [AttrSizedOperandSegments,
MemoryEffects<[MemAlloc<DebuggingResource>]>,
DeclareOpInterfaceMethods<
fir_FortranVariableStorageOpInterface>]> {
let summary = "declare a variable and produce an SSA value that can be used as a variable in HLFIR operations";
let description = [{
@ -45,6 +47,10 @@ def hlfir_DeclareOp : hlfir_Op<"declare", [AttrSizedOperandSegments,
include bounds, length parameters, and Fortran attributes.
The arguments are the same as for fir.declare.
The storage and storage_offset operands are optional and are required
for FortranVariableStorageOpInterface, where they are documented.
If these operands are absent, then the storage of the declared variable
is only known to start where the memref operand points to.
The main difference with fir.declare is that hlfir.declare returns two
values:
@ -84,21 +90,22 @@ def hlfir_DeclareOp : hlfir_Op<"declare", [AttrSizedOperandSegments,
```
}];
let arguments = (ins
AnyRefOrBox:$memref,
Optional<AnyShapeOrShiftType>:$shape,
Variadic<AnyIntegerType>:$typeparams,
Optional<fir_DummyScopeType>:$dummy_scope,
Builtin_StringAttr:$uniq_name,
OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
OptionalAttr<cuf_DataAttributeAttr>:$data_attr
);
let arguments = (ins AnyRefOrBox:$memref,
Optional<AnyShapeOrShiftType>:$shape,
Variadic<AnyIntegerType>:$typeparams,
Optional<fir_DummyScopeType>:$dummy_scope,
Optional<AnyReferenceLike>:$storage,
DefaultValuedAttr<UI64Attr, "0">:$storage_offset,
Builtin_StringAttr:$uniq_name,
OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
OptionalAttr<cuf_DataAttributeAttr>:$data_attr);
let results = (outs AnyFortranVariable, AnyRefOrBoxLike);
let assemblyFormat = [{
$memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)?
(`dummy_scope` $dummy_scope^)?
(`storage` `(` $storage^ `[` $storage_offset `]` `)`)?
attr-dict `:` functional-type(operands, results)
}];

View File

@ -423,10 +423,11 @@ mlir::Value fir::FirOpBuilder::genTempDeclareOp(
llvm::ArrayRef<mlir::Value> typeParams,
fir::FortranVariableFlagsAttr fortranAttrs) {
auto nameAttr = mlir::StringAttr::get(builder.getContext(), name);
return fir::DeclareOp::create(builder, loc, memref.getType(), memref, shape,
typeParams,
/*dummy_scope=*/nullptr, nameAttr, fortranAttrs,
cuf::DataAttributeAttr{});
return fir::DeclareOp::create(
builder, loc, memref.getType(), memref, shape, typeParams,
/*dummy_scope=*/nullptr,
/*storage=*/nullptr,
/*storage_offset=*/0, nameAttr, fortranAttrs, cuf::DataAttributeAttr{});
}
mlir::Value fir::FirOpBuilder::genStackSave(mlir::Location loc) {

View File

@ -68,3 +68,31 @@ fir::FortranVariableOpInterface::verifyDeclareLikeOpImpl(mlir::Value memref) {
}
return mlir::success();
}
mlir::LogicalResult
fir::detail::verifyFortranVariableStorageOpInterface(mlir::Operation *op) {
auto storageIface = mlir::cast<fir::FortranVariableStorageOpInterface>(op);
mlir::Value storage = storageIface.getStorage();
std::uint64_t storageOffset = storageIface.getStorageOffset();
if (!storage) {
if (storageOffset != 0)
return op->emitOpError(
"storage offset specified without the storage reference");
return mlir::success();
}
auto storageType =
mlir::dyn_cast<fir::SequenceType>(fir::unwrapRefType(storage.getType()));
if (!storageType || storageType.getDimension() != 1)
return op->emitOpError("storage must be a vector");
if (storageType.hasDynamicExtents())
return op->emitOpError("storage must have known extent");
if (storageType.getEleTy() != mlir::IntegerType::get(op->getContext(), 8))
return op->emitOpError("storage must be an array of i8 elements");
if (storageOffset > storageType.getConstantArraySize())
return op->emitOpError("storage offset exceeds the storage size");
// TODO: we should probably verify that the (offset + sizeof(var))
// is within the storage object, but this requires mlir::DataLayout.
// Can we make it available during the verification?
return mlir::success();
}

View File

@ -279,7 +279,8 @@ void hlfir::DeclareOp::build(mlir::OpBuilder &builder,
auto [hlfirVariableType, firVarType] =
getDeclareOutputTypes(inputType, hasExplicitLbs);
build(builder, result, {hlfirVariableType, firVarType}, memref, shape,
typeparams, dummy_scope, nameAttr, fortran_attrs, data_attr);
typeparams, dummy_scope, /*storage=*/nullptr, /*storage_offset=*/0,
nameAttr, fortran_attrs, data_attr);
}
llvm::LogicalResult hlfir::DeclareOp::verify() {

View File

@ -305,6 +305,8 @@ public:
auto firDeclareOp = fir::DeclareOp::create(
rewriter, loc, memref.getType(), memref, declareOp.getShape(),
declareOp.getTypeparams(), declareOp.getDummyScope(),
/*storage=*/declareOp.getStorage(),
/*storage_offset=*/declareOp.getStorageOffset(),
declareOp.getUniqName(), fortranAttrs, dataAttr);
// Propagate other attributes from hlfir.declare to fir.declare.

View File

@ -143,3 +143,22 @@ func.func @array_declare_unlimited_polymorphic_boxaddr(%arg0: !fir.ref<!fir.clas
// 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>>>>
// CHECK-LABEL: func.func @vars_within_physical_storage() {
// CHECK: %[[VAL_2:.*]] = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
// CHECK: %[[VAL_6:.*]] = fir.declare %{{.*}} storage(%[[VAL_2]][0]) {uniq_name = "a"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
// CHECK: %[[VAL_9:.*]] = fir.declare %{{.*}} storage(%[[VAL_2]][4]) {uniq_name = "b"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
fir.global common @block_(dense<0> : vector<8xi8>) {alignment = 4 : i64} : !fir.array<8xi8>
func.func @vars_within_physical_storage() {
%c4 = arith.constant 4 : index
%c0 = arith.constant 0 : index
%1 = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
%2 = fir.convert %1 : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
%3 = fir.coordinate_of %2, %c0 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
%4 = fir.convert %3 : (!fir.ref<i8>) -> !fir.ref<f32>
%5 = fir.declare %4 storage (%1[0]) {uniq_name = "a"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
%6 = fir.coordinate_of %2, %c4 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
%7 = fir.convert %6 : (!fir.ref<i8>) -> !fir.ref<f32>
%8 = fir.declare %7 storage (%1[4]) {uniq_name = "b"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
return
}

View File

@ -1426,3 +1426,60 @@ func.func @wrong_weights_number_in_if_then_else(%cond: i1) {
}
return
}
// -----
func.func @fir_declare_bad_storage_offset(%arg0: !fir.ref<!fir.array<8xi8>>) {
%c0 = arith.constant 0 : index
%addr = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
%2 = fir.convert %addr : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
%var = fir.coordinate_of %2, %c0 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
// expected-error@+1 {{negative integer literal not valid for unsigned integer type}}
%decl = fir.declare %var storage (%addr[-1]) {uniq_name = "a"} : (!fir.ref<i8>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<i8>
return
}
// -----
"func.func"() <{function_type = (!fir.ref<!fir.array<8xi8>>) -> (), sym_name = "fir_declare_bad_storage_offset"}> ({
^bb0(%arg0: !fir.ref<!fir.array<8xi8>>):
%0 = "arith.constant"() <{value = 0 : index}> : () -> index
%1 = "fir.address_of"() <{symbol = @block_}> : () -> !fir.ref<!fir.array<8xi8>>
%2 = "fir.convert"(%1) : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
%3 = "fir.coordinate_of"(%2, %0) <{baseType = !fir.ref<!fir.array<?xi8>>}> : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
// expected-error@+1 {{storage offset specified without the storage reference}}
%4 = "fir.declare"(%3) <{operandSegmentSizes = array<i32: 1, 0, 0, 0, 0>, storage_offset = 1 : ui64, uniq_name = "a"}> : (!fir.ref<i8>) -> !fir.ref<i8>
"func.return"() : () -> ()
}) : () -> ()
// -----
func.func @fir_declare_bad_storage(%arg0: !fir.ref<i8>) {
// expected-error@+1 {{storage must be a vector}}
%decl = fir.declare %arg0 storage (%arg0[0]) {uniq_name = "a"} : (!fir.ref<i8>, !fir.ref<i8>) -> !fir.ref<i8>
return
}
// -----
func.func @fir_declare_bad_storage(%arg0: !fir.ref<i8>, %arg1: !fir.ref<!fir.array<?xi8>>) {
// expected-error@+1 {{storage must have known extent}}
%decl = fir.declare %arg0 storage (%arg1[0]) {uniq_name = "a"} : (!fir.ref<i8>, !fir.ref<!fir.array<?xi8>>) -> !fir.ref<i8>
return
}
// -----
func.func @fir_declare_bad_storage(%arg0: !fir.ref<i8>, %arg1: !fir.ref<!fir.array<1xi32>>) {
// expected-error@+1 {{storage must be an array of i8 elements}}
%decl = fir.declare %arg0 storage (%arg1[0]) {uniq_name = "a"} : (!fir.ref<i8>, !fir.ref<!fir.array<1xi32>>) -> !fir.ref<i8>
return
}
// -----
func.func @fir_declare_bad_storage_offset(%arg0: !fir.ref<i8>, %arg1: !fir.ref<!fir.array<1xi8>>) {
// expected-error@+1 {{storage offset exceeds the storage size}}
%decl = fir.declare %arg0 storage (%arg1[2]) {uniq_name = "a"} : (!fir.ref<i8>, !fir.ref<!fir.array<1xi8>>) -> !fir.ref<i8>
return
}

View File

@ -237,3 +237,30 @@ func.func @rebox_scalar_attrs(%arg0: !fir.class<!fir.ptr<!fir.type<sometype{i:i3
// CHECK-LABEL: @rebox_scalar_attrs
// CHECK: fir.rebox %{{.*}} : (!fir.class<!fir.ptr<!fir.type<sometype{i:i32}>>>) -> !fir.class<!fir.type<sometype{i:i32}>>
// CHECK: return
func.func @vars_within_physical_storage() {
%c4 = arith.constant 4 : index
%c0 = arith.constant 0 : index
%1 = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
%2 = fir.convert %1 : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
%3 = fir.coordinate_of %2, %c0 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
%4 = fir.convert %3 : (!fir.ref<i8>) -> !fir.ref<f32>
%5:2 = hlfir.declare %4 storage (%1[0]) {uniq_name = "a"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<f32>, !fir.ref<f32>)
%6 = fir.coordinate_of %2, %c4 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
%7 = fir.convert %6 : (!fir.ref<i8>) -> !fir.ref<f32>
%8:2 = hlfir.declare %7 storage (%1[4]) {uniq_name = "b"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<f32>, !fir.ref<f32>)
return
}
// CHECK-LABEL: func.func @vars_within_physical_storage() {
// CHECK: %[[VAL_0:.*]] = arith.constant 4 : index
// CHECK: %[[VAL_1:.*]] = arith.constant 0 : index
// CHECK: %[[VAL_2:.*]] = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
// CHECK: %[[VAL_3:.*]] = fir.convert %[[VAL_2]] : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
// CHECK: %[[VAL_4:.*]] = fir.coordinate_of %[[VAL_3]], %[[VAL_1]] : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_4]] : (!fir.ref<i8>) -> !fir.ref<f32>
// CHECK: %[[VAL_6:.*]] = fir.declare %[[VAL_5]] storage(%[[VAL_2]][0]) {uniq_name = "a"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
// CHECK: %[[VAL_7:.*]] = fir.coordinate_of %[[VAL_3]], %[[VAL_0]] : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
// CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_7]] : (!fir.ref<i8>) -> !fir.ref<f32>
// CHECK: %[[VAL_9:.*]] = fir.declare %[[VAL_8]] storage(%[[VAL_2]][4]) {uniq_name = "b"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
// CHECK: return
// CHECK: }

View File

@ -161,3 +161,21 @@ func.func @array_declare_unlimited_polymorphic_boxaddr(%arg0: !fir.ref<!fir.clas
// 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:.*]] = hlfir.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>>>>, !fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>)
func.func @vars_within_physical_storage() {
%c4 = arith.constant 4 : index
%c0 = arith.constant 0 : index
%1 = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
%2 = fir.convert %1 : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
%3 = fir.coordinate_of %2, %c0 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
%4 = fir.convert %3 : (!fir.ref<i8>) -> !fir.ref<f32>
%5:2 = hlfir.declare %4 storage (%1[0]) {uniq_name = "a"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<f32>, !fir.ref<f32>)
%6 = fir.coordinate_of %2, %c4 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
%7 = fir.convert %6 : (!fir.ref<i8>) -> !fir.ref<f32>
%8:2 = hlfir.declare %7 storage (%1[4]) {uniq_name = "b"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<f32>, !fir.ref<f32>)
return
}
// CHECK-LABEL: func.func @vars_within_physical_storage() {
// CHECK: %[[VAL_2:.*]] = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
// CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %{{.*}} storage(%[[VAL_2]][0]) {uniq_name = "a"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<f32>, !fir.ref<f32>)
// CHECK: %[[VAL_9:.*]]:2 = hlfir.declare %{{.*}} storage(%[[VAL_2]][4]) {uniq_name = "b"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<f32>, !fir.ref<f32>)

View File

@ -1659,3 +1659,28 @@ func.func @bad_eoshift11(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32, %arg2: !hlfir.
%0 = hlfir.eoshift %arg0 %arg1 boundary %arg2 : (!hlfir.expr<2x2xi32>, i32, !hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32>
return
}
// -----
func.func @fir_declare_bad_storage_offset(%arg0: !fir.ref<!fir.array<8xi8>>) {
%c0 = arith.constant 0 : index
%addr = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
%2 = fir.convert %addr : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
%var = fir.coordinate_of %2, %c0 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
// expected-error@+1 {{negative integer literal not valid for unsigned integer type}}
%decl:2 = hlfir.declare %var storage (%addr[-1]) {uniq_name = "a"} : (!fir.ref<i8>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<i8>, !fir.ref<i8>)
return
}
// -----
"func.func"() <{function_type = (!fir.ref<!fir.array<8xi8>>) -> (), sym_name = "fir_declare_bad_storage_offset"}> ({
^bb0(%arg0: !fir.ref<!fir.array<8xi8>>):
%0 = "arith.constant"() <{value = 0 : index}> : () -> index
%1 = "fir.address_of"() <{symbol = @block_}> : () -> !fir.ref<!fir.array<8xi8>>
%2 = "fir.convert"(%1) : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
%3 = "fir.coordinate_of"(%2, %0) <{baseType = !fir.ref<!fir.array<?xi8>>}> : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
// expected-error@+1 {{storage offset specified without the storage reference}}
%4:2 = "hlfir.declare"(%3) <{operandSegmentSizes = array<i32: 1, 0, 0, 0, 0>, storage_offset = 1 : ui64, uniq_name = "a"}> : (!fir.ref<i8>) -> (!fir.ref<i8>, !fir.ref<i8>)
"func.return"() : () -> ()
}) : () -> ()

View File

@ -49,7 +49,7 @@ TEST_F(FortranVariableTest, SimpleScalar) {
auto name = mlir::StringAttr::get(&context, "x");
auto declare = fir::DeclareOp::create(*builder, loc, addr.getType(), addr,
/*shape=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{},
/*dummy_scope=*/nullptr, name,
/*dummy_scope=*/nullptr, /*storage=*/nullptr, /*storage_offset=*/0, name,
/*fortran_attrs=*/fir::FortranVariableFlagsAttr{},
/*data_attr=*/cuf::DataAttributeAttr{});
@ -75,7 +75,8 @@ TEST_F(FortranVariableTest, CharacterScalar) {
*builder, loc, eleType, /*pinned=*/false, typeParams);
auto name = mlir::StringAttr::get(&context, "x");
auto declare = fir::DeclareOp::create(*builder, loc, addr.getType(), addr,
/*shape=*/mlir::Value{}, typeParams, /*dummy_scope=*/nullptr, name,
/*shape=*/mlir::Value{}, typeParams, /*dummy_scope=*/nullptr,
/*storage=*/nullptr, /*storage_offset=*/0, name,
/*fortran_attrs=*/fir::FortranVariableFlagsAttr{},
/*data_attr=*/cuf::DataAttributeAttr{});
@ -106,7 +107,8 @@ TEST_F(FortranVariableTest, SimpleArray) {
mlir::Value shape = createShape(extents);
auto name = mlir::StringAttr::get(&context, "x");
auto declare = fir::DeclareOp::create(*builder, loc, addr.getType(), addr,
shape, /*typeParams=*/mlir::ValueRange{}, /*dummy_scope=*/nullptr, name,
shape, /*typeParams=*/mlir::ValueRange{}, /*dummy_scope=*/nullptr,
/*storage=*/nullptr, /*storage_offset=*/0, name,
/*fortran_attrs=*/fir::FortranVariableFlagsAttr{},
/*data_attr=*/cuf::DataAttributeAttr{});
@ -137,7 +139,8 @@ TEST_F(FortranVariableTest, CharacterArray) {
mlir::Value shape = createShape(extents);
auto name = mlir::StringAttr::get(&context, "x");
auto declare = fir::DeclareOp::create(*builder, loc, addr.getType(), addr,
shape, typeParams, /*dummy_scope=*/nullptr, name,
shape, typeParams, /*dummy_scope=*/nullptr, /*storage=*/nullptr,
/*storage_offset=*/0, name,
/*fortran_attrs=*/fir::FortranVariableFlagsAttr{},
/*data_attr=*/cuf::DataAttributeAttr{});