Skip to content

Commit d2366a8

Browse files
vzakharigithub-actions[bot]
authored andcommitted
Automerge: [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.
2 parents 35567e4 + 199d3d7 commit d2366a8

File tree

15 files changed

+290
-36
lines changed

15 files changed

+290
-36
lines changed

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3178,9 +3178,11 @@ def fir_IsPresentOp : fir_SimpleOp<"is_present", [NoMemoryEffect]> {
31783178
// operations if the values are unused. fir.declare may be used to generate
31793179
// debug information so we would like to keep this around even if the value
31803180
// is not used.
3181-
def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments,
3182-
MemoryEffects<[MemAlloc<DebuggingResource>]>,
3183-
DeclareOpInterfaceMethods<fir_FortranVariableOpInterface>]> {
3181+
def fir_DeclareOp
3182+
: fir_Op<"declare", [AttrSizedOperandSegments,
3183+
MemoryEffects<[MemAlloc<DebuggingResource>]>,
3184+
DeclareOpInterfaceMethods<
3185+
fir_FortranVariableStorageOpInterface>]> {
31843186
let summary = "declare a variable";
31853187

31863188
let description = [{
@@ -3203,6 +3205,11 @@ def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments,
32033205
It must always be provided for characters and parametrized derived types
32043206
when memref is not a box value or address.
32053207

3208+
The storage and storage_offset operands are optional and are required
3209+
for FortranVariableStorageOpInterface, where they are documented.
3210+
If these operands are absent, then the storage of the declared variable
3211+
is only known to start where the memref operand points to.
3212+
32063213
Example:
32073214

32083215
CHARACTER(n), OPTIONAL, TARGET :: c(10:, 20:)
@@ -3220,21 +3227,22 @@ def fir_DeclareOp : fir_Op<"declare", [AttrSizedOperandSegments,
32203227
```
32213228
}];
32223229

3223-
let arguments = (ins
3224-
AnyRefOrBox:$memref,
3225-
Optional<AnyShapeOrShiftType>:$shape,
3226-
Variadic<AnyIntegerType>:$typeparams,
3227-
Optional<fir_DummyScopeType>:$dummy_scope,
3228-
Builtin_StringAttr:$uniq_name,
3229-
OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
3230-
OptionalAttr<cuf_DataAttributeAttr>:$data_attr
3231-
);
3230+
let arguments = (ins AnyRefOrBox:$memref,
3231+
Optional<AnyShapeOrShiftType>:$shape,
3232+
Variadic<AnyIntegerType>:$typeparams,
3233+
Optional<fir_DummyScopeType>:$dummy_scope,
3234+
Optional<AnyReferenceLike>:$storage,
3235+
DefaultValuedAttr<UI64Attr, "0">:$storage_offset,
3236+
Builtin_StringAttr:$uniq_name,
3237+
OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
3238+
OptionalAttr<cuf_DataAttributeAttr>:$data_attr);
32323239

32333240
let results = (outs AnyRefOrBox);
32343241

32353242
let assemblyFormat = [{
32363243
$memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)?
32373244
(`dummy_scope` $dummy_scope^)?
3245+
(`storage` `(` $storage^ `[` $storage_offset `]` `)`)?
32383246
attr-dict `:` functional-type(operands, results)
32393247
}];
32403248

flang/include/flang/Optimizer/Dialect/FIRTypes.td

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,9 +610,10 @@ def AnyCompositeLike : TypeConstraint<Or<[fir_RecordType.predicate,
610610
"any composite">;
611611

612612
// Reference types
613-
def AnyReferenceLike : TypeConstraint<Or<[fir_ReferenceType.predicate,
614-
fir_HeapType.predicate, fir_PointerType.predicate,
615-
fir_LLVMPointerType.predicate]>, "any reference">;
613+
def AnyReferenceLike
614+
: Type<Or<[fir_ReferenceType.predicate, fir_HeapType.predicate,
615+
fir_PointerType.predicate, fir_LLVMPointerType.predicate]>,
616+
"any reference">;
616617

617618
def FuncType : TypeConstraint<FunctionType.predicate, "function type">;
618619

flang/include/flang/Optimizer/Dialect/FortranVariableInterface.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
#include "mlir/IR/BuiltinTypes.h"
2020
#include "mlir/IR/OpDefinition.h"
2121

22+
namespace fir::detail {
23+
/// Verify operations implementing FortranVariableStorageOpInterface.
24+
mlir::LogicalResult verifyFortranVariableStorageOpInterface(mlir::Operation *);
25+
} // namespace fir::detail
26+
2227
#include "flang/Optimizer/Dialect/FortranVariableInterface.h.inc"
2328

2429
#endif // FORTRAN_OPTIMIZER_DIALECT_FORTRANVARIABLEINTERFACE_H

flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,4 +213,56 @@ def fir_FortranVariableOpInterface : OpInterface<"FortranVariableOpInterface"> {
213213

214214
}
215215

216+
def fir_FortranVariableStorageOpInterface
217+
: OpInterface<"FortranVariableStorageOpInterface",
218+
[fir_FortranVariableOpInterface]> {
219+
let description = [{
220+
An extension of FortranVariableOpInterface for operations that provide
221+
information about the physical storage layout of the variable.
222+
The operations provide the raw address of the physical storage
223+
and the byte offset where the variable begins within the physical
224+
storage.
225+
The storage is a reference to an array of known size consisting
226+
of i8 elements. This is how Flang represents COMMON and EQUIVALENCE
227+
storage blocks with the member variables located within the storage
228+
at different offsets. The storage offset for a variable must not
229+
exceed the storage size. Note that the zero-sized variables
230+
may start at the offset that is after the final byte of the storage.
231+
When getStorage() returns nullptr, getStorageOffset() must return 0.
232+
This means that nothing is known about the physical storage
233+
of the variable (beyond the information maybe provided
234+
by the concrete operation itself, e.g. fir.declare defines
235+
the physical storage of a variable via memref operand,
236+
where the variable starts).
237+
}];
238+
239+
let methods =
240+
[InterfaceMethod<
241+
/*desc=*/"Returns the raw address of the physical storage",
242+
/*retTy=*/"mlir::Value",
243+
/*methodName=*/"getStorage",
244+
/*args=*/(ins),
245+
/*methodBody=*/[{}],
246+
/*defaultImplementation=*/[{
247+
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
248+
return op.getStorage();
249+
}]>,
250+
InterfaceMethod<
251+
/*desc=*/"Returns the byte offset where the variable begins "
252+
"within the physical storage",
253+
/*retTy=*/"std::uint64_t",
254+
/*methodName=*/"getStorageOffset",
255+
/*args=*/(ins),
256+
/*methodBody=*/[{}],
257+
/*defaultImplementation=*/[{
258+
ConcreteOp op = mlir::cast<ConcreteOp>(this->getOperation());
259+
return op.getStorageOffset();
260+
}]>,
261+
];
262+
263+
let cppNamespace = "fir";
264+
let verify =
265+
[{ return detail::verifyFortranVariableStorageOpInterface($_op); }];
266+
}
267+
216268
#endif // FORTRANVARIABLEINTERFACE

flang/include/flang/Optimizer/HLFIR/HLFIROps.td

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,22 @@ class hlfir_Op<string mnemonic, list<Trait> traits>
3535
// removed by dead code elimination if the value result is unused. Information
3636
// from the declare operation can be used to generate debug information so we
3737
// don't want to remove it as dead code
38-
def hlfir_DeclareOp : hlfir_Op<"declare", [AttrSizedOperandSegments,
39-
MemoryEffects<[MemAlloc<DebuggingResource>]>,
40-
DeclareOpInterfaceMethods<fir_FortranVariableOpInterface>]> {
38+
def hlfir_DeclareOp
39+
: hlfir_Op<"declare", [AttrSizedOperandSegments,
40+
MemoryEffects<[MemAlloc<DebuggingResource>]>,
41+
DeclareOpInterfaceMethods<
42+
fir_FortranVariableStorageOpInterface>]> {
4143
let summary = "declare a variable and produce an SSA value that can be used as a variable in HLFIR operations";
4244

4345
let description = [{
4446
Tie the properties of a Fortran variable to an address. The properties
4547
include bounds, length parameters, and Fortran attributes.
4648

4749
The arguments are the same as for fir.declare.
50+
The storage and storage_offset operands are optional and are required
51+
for FortranVariableStorageOpInterface, where they are documented.
52+
If these operands are absent, then the storage of the declared variable
53+
is only known to start where the memref operand points to.
4854

4955
The main difference with fir.declare is that hlfir.declare returns two
5056
values:
@@ -84,21 +90,22 @@ def hlfir_DeclareOp : hlfir_Op<"declare", [AttrSizedOperandSegments,
8490
```
8591
}];
8692

87-
let arguments = (ins
88-
AnyRefOrBox:$memref,
89-
Optional<AnyShapeOrShiftType>:$shape,
90-
Variadic<AnyIntegerType>:$typeparams,
91-
Optional<fir_DummyScopeType>:$dummy_scope,
92-
Builtin_StringAttr:$uniq_name,
93-
OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
94-
OptionalAttr<cuf_DataAttributeAttr>:$data_attr
95-
);
93+
let arguments = (ins AnyRefOrBox:$memref,
94+
Optional<AnyShapeOrShiftType>:$shape,
95+
Variadic<AnyIntegerType>:$typeparams,
96+
Optional<fir_DummyScopeType>:$dummy_scope,
97+
Optional<AnyReferenceLike>:$storage,
98+
DefaultValuedAttr<UI64Attr, "0">:$storage_offset,
99+
Builtin_StringAttr:$uniq_name,
100+
OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs,
101+
OptionalAttr<cuf_DataAttributeAttr>:$data_attr);
96102

97103
let results = (outs AnyFortranVariable, AnyRefOrBoxLike);
98104

99105
let assemblyFormat = [{
100106
$memref (`(` $shape^ `)`)? (`typeparams` $typeparams^)?
101107
(`dummy_scope` $dummy_scope^)?
108+
(`storage` `(` $storage^ `[` $storage_offset `]` `)`)?
102109
attr-dict `:` functional-type(operands, results)
103110
}];
104111

flang/lib/Optimizer/Builder/FIRBuilder.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,10 +423,11 @@ mlir::Value fir::FirOpBuilder::genTempDeclareOp(
423423
llvm::ArrayRef<mlir::Value> typeParams,
424424
fir::FortranVariableFlagsAttr fortranAttrs) {
425425
auto nameAttr = mlir::StringAttr::get(builder.getContext(), name);
426-
return fir::DeclareOp::create(builder, loc, memref.getType(), memref, shape,
427-
typeParams,
428-
/*dummy_scope=*/nullptr, nameAttr, fortranAttrs,
429-
cuf::DataAttributeAttr{});
426+
return fir::DeclareOp::create(
427+
builder, loc, memref.getType(), memref, shape, typeParams,
428+
/*dummy_scope=*/nullptr,
429+
/*storage=*/nullptr,
430+
/*storage_offset=*/0, nameAttr, fortranAttrs, cuf::DataAttributeAttr{});
430431
}
431432

432433
mlir::Value fir::FirOpBuilder::genStackSave(mlir::Location loc) {

flang/lib/Optimizer/Dialect/FortranVariableInterface.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,31 @@ fir::FortranVariableOpInterface::verifyDeclareLikeOpImpl(mlir::Value memref) {
6868
}
6969
return mlir::success();
7070
}
71+
72+
mlir::LogicalResult
73+
fir::detail::verifyFortranVariableStorageOpInterface(mlir::Operation *op) {
74+
auto storageIface = mlir::cast<fir::FortranVariableStorageOpInterface>(op);
75+
mlir::Value storage = storageIface.getStorage();
76+
std::uint64_t storageOffset = storageIface.getStorageOffset();
77+
if (!storage) {
78+
if (storageOffset != 0)
79+
return op->emitOpError(
80+
"storage offset specified without the storage reference");
81+
return mlir::success();
82+
}
83+
84+
auto storageType =
85+
mlir::dyn_cast<fir::SequenceType>(fir::unwrapRefType(storage.getType()));
86+
if (!storageType || storageType.getDimension() != 1)
87+
return op->emitOpError("storage must be a vector");
88+
if (storageType.hasDynamicExtents())
89+
return op->emitOpError("storage must have known extent");
90+
if (storageType.getEleTy() != mlir::IntegerType::get(op->getContext(), 8))
91+
return op->emitOpError("storage must be an array of i8 elements");
92+
if (storageOffset > storageType.getConstantArraySize())
93+
return op->emitOpError("storage offset exceeds the storage size");
94+
// TODO: we should probably verify that the (offset + sizeof(var))
95+
// is within the storage object, but this requires mlir::DataLayout.
96+
// Can we make it available during the verification?
97+
return mlir::success();
98+
}

flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ void hlfir::DeclareOp::build(mlir::OpBuilder &builder,
279279
auto [hlfirVariableType, firVarType] =
280280
getDeclareOutputTypes(inputType, hasExplicitLbs);
281281
build(builder, result, {hlfirVariableType, firVarType}, memref, shape,
282-
typeparams, dummy_scope, nameAttr, fortran_attrs, data_attr);
282+
typeparams, dummy_scope, /*storage=*/nullptr, /*storage_offset=*/0,
283+
nameAttr, fortran_attrs, data_attr);
283284
}
284285

285286
llvm::LogicalResult hlfir::DeclareOp::verify() {

flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
305305
auto firDeclareOp = fir::DeclareOp::create(
306306
rewriter, loc, memref.getType(), memref, declareOp.getShape(),
307307
declareOp.getTypeparams(), declareOp.getDummyScope(),
308+
/*storage=*/declareOp.getStorage(),
309+
/*storage_offset=*/declareOp.getStorageOffset(),
308310
declareOp.getUniqName(), fortranAttrs, dataAttr);
309311

310312
// Propagate other attributes from hlfir.declare to fir.declare.

flang/test/Fir/declare.fir

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,22 @@ func.func @array_declare_unlimited_polymorphic_boxaddr(%arg0: !fir.ref<!fir.clas
143143
// CHECK-LABEL: func.func @array_declare_unlimited_polymorphic_boxaddr(
144144
// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.class<!fir.ptr<!fir.array<?x?xnone>>>>) {
145145
// 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>>>>
146+
147+
// CHECK-LABEL: func.func @vars_within_physical_storage() {
148+
// CHECK: %[[VAL_2:.*]] = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
149+
// CHECK: %[[VAL_6:.*]] = fir.declare %{{.*}} storage(%[[VAL_2]][0]) {uniq_name = "a"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
150+
// CHECK: %[[VAL_9:.*]] = fir.declare %{{.*}} storage(%[[VAL_2]][4]) {uniq_name = "b"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
151+
fir.global common @block_(dense<0> : vector<8xi8>) {alignment = 4 : i64} : !fir.array<8xi8>
152+
func.func @vars_within_physical_storage() {
153+
%c4 = arith.constant 4 : index
154+
%c0 = arith.constant 0 : index
155+
%1 = fir.address_of(@block_) : !fir.ref<!fir.array<8xi8>>
156+
%2 = fir.convert %1 : (!fir.ref<!fir.array<8xi8>>) -> !fir.ref<!fir.array<?xi8>>
157+
%3 = fir.coordinate_of %2, %c0 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
158+
%4 = fir.convert %3 : (!fir.ref<i8>) -> !fir.ref<f32>
159+
%5 = fir.declare %4 storage (%1[0]) {uniq_name = "a"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
160+
%6 = fir.coordinate_of %2, %c4 : (!fir.ref<!fir.array<?xi8>>, index) -> !fir.ref<i8>
161+
%7 = fir.convert %6 : (!fir.ref<i8>) -> !fir.ref<f32>
162+
%8 = fir.declare %7 storage (%1[4]) {uniq_name = "b"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> !fir.ref<f32>
163+
return
164+
}

0 commit comments

Comments
 (0)