Skip to content

[flang] Defined SafeTempArrayCopyAttrInterface for array repacking. #134346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions flang/docs/ArrayRepacking.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ Arguments:
* `max-element-size` - constant integer attribute specifying the maximum byte element-size of an array that is eligible for repacking.
* `min-stride` - constant integer attribute specifying the minimum byte stride of the innermost dimension of an array that is eligible for repacking.
* `typeparams` - type parameters of the element type.
* `*.temp_copy_is_safe`: a list of attributes implementing `TempCopyIsSafe` attribute interface for generating a boolean value indicating whether using a temporary copy instead of the original array is safe in the current context.
* `is-safe`: a list of attributes implementing `fir::SafeTempArrayCopyAttrInterface` attribute interface for generating a boolean value indicating whether using a temporary copy instead of the original array is safe in the current context.

Memory effects are conservative, assuming that an allocation and copy may happen:

Expand Down Expand Up @@ -243,7 +243,7 @@ Memory effects are conservative, assuming that a copy and deallocation may happe

### New attribute interface

The `TempCopyIsSafe` attribute interface provides means to generate programming model specific predicates saying whether repacking is safe or not at the point where it needs to be done. For example the OpenMP MLIR dialect may provide an attribute implementing this interface to generate a runtime check at the point of packing array `x` inside subroutine `repacking`. A conservative implementation might look like this:
The `fir::SafeTempArrayCopyAttrInterface` attribute interface provides means to generate programming model specific predicates saying whether repacking is safe or not at the point where it needs to be done. For example the OpenMP MLIR dialect may provide an attribute implementing this interface to generate a runtime check at the point of packing array `x` inside subroutine `repacking`. A conservative implementation might look like this:

```C
repacking_is_safe = omp_get_num_team() == 1 && omp_get_num_threads() == 1;
Expand Down Expand Up @@ -297,15 +297,15 @@ end program main

So the most conservative implementation of the predicate generator may be to always produce `false` value. Flang lowering may attach any number of such attributes to `fir.pack_array` depending on the compilation context and options.

[TBD] define `TempCopyIsSafe` attribute interface so that OpenACC/OpenMP dialects can provide their specific attributes, which can be used to generate static/runtime checks for safety of the temporary copy in particular context.
`fir::SafeTempArrayCopyAttrInterface` is defined in `flang/include/flang/Optimizer/Dialect/SafeTempArrayCopyAttrInterface.td`. The OpenACC-specific implementation is defined in `lib/Optimizer/OpenACC/FIROpenACCAttributes.cpp`. The OpenMP-specific implementation is defined in `lib/Optimizer/OpenMP/Support/FIROpenMPAttributes.cpp`

#### Alternatives/additions to the attribute interface

The following ideas were expressed during the review, and they are worth considering.

| |
| - |
| For OpenACC/OpenMP runtime to be able to detect/handle the presence of the original array and the temporary copy in the device data environment, descriptor flags/properties might be used to mark the copy's descriptor as such and provide a link to the original array (or its descriptor). It may be problematic to maintain such flags/properties, in general, because of the repacking that may happen, especially, in C code, where the flags/properties might be dropped. Moreover, a copy array might be repacked into another copy array multiple times, so a descriptor might need to keep a chain of associated arrays and it will have to be maintained as well.<br>An alternative to tracking the original-copy "association" might be compiler generated code notifying the OpenACC/OpenMP offload runtime about the copy being created/deleted for the original array, so that the offload runtime can disallow repacking or report an error when the repacking is definitely causing the program to behave incorrectly. The compiler may report the "association" to the runtime through callbacks provided by `TempCopyIsSafe` attribute interface, and this will require proper bookkeeping in the runtime specific to the array repacking. |
| For OpenACC/OpenMP runtime to be able to detect/handle the presence of the original array and the temporary copy in the device data environment, descriptor flags/properties might be used to mark the copy's descriptor as such and provide a link to the original array (or its descriptor). It may be problematic to maintain such flags/properties, in general, because of the repacking that may happen, especially, in C code, where the flags/properties might be dropped. Moreover, a copy array might be repacked into another copy array multiple times, so a descriptor might need to keep a chain of associated arrays and it will have to be maintained as well.<br>An alternative to tracking the original-copy "association" might be compiler generated code notifying the OpenACC/OpenMP offload runtime about the copy being created/deleted for the original array, so that the offload runtime can disallow repacking or report an error when the repacking is definitely causing the program to behave incorrectly. The compiler may report the "association" to the runtime through callbacks provided by `fir::SafeTempArrayCopyAttrInterface` attribute interface, and this will require proper bookkeeping in the runtime specific to the array repacking. |
| There may be some uses for an API allowing to statically determine whether a given descriptor (SSA value) represent the repacked copy of the original array. For example, it may be in the form of an API in the OpenACC `MappableType` interface. This can be done with some limitations for the values produced by `fir.pack_array` that are dynamic (i.e. the copy is created conditionally based on the runtime checks). |
| The compiler can also try to statically determine the conditions where the array repacking might be unsafe, e.g. a presence of memory barriers or operations carrying implicit memory barriers, presence of atomic operations between the `pack/unpack` operations may indicate non-trivial handling of the array memory. Such checks may result in the removal of `pack/unpack` operations, and they can probably be done in a mandatory pass (not an optimization pass). At the same time, the result of the checks may depend on other optimization passes (e.g. inlining), so the behavior may be inconsistent between different optimization levels. |

Expand Down Expand Up @@ -373,7 +373,7 @@ Lowering of the new operations (after all the optimizations) might be done in a
`fir.pack_array` lowering might be done in the following steps:

* If there are dynamic constraints, generate a boolean `p1` that is set to true if repacking has to be done (depending on the constraints values and the original array descriptor). The IR might be cleaner if we generate a Fortran runtime call here.
* If there are attributes implementing `TempCopyIsSafe` attribute interface, then use the interface method to generate boolean predicates for each such attribute: `p2`, ..., `pn`.
* If there are attributes implementing `fir::SafeTempArrayCopyAttrInterface` attribute interface, then use the interface method to generate boolean predicates for each such attribute: `p2`, ..., `pn`.
* [TBD] it seems that the runtime checks for the target offload programs will require a pure `PointerLike` value for the array start and the total byte size of the array (e.g. as `index` value).
* Compute `p = p1 && p2 && ... && pn`.
* Compute the total size of the temporary `required_size` (in elements).
Expand Down
4 changes: 2 additions & 2 deletions flang/include/flang/Lower/ConvertVariable.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ fir::ExtendedValue genPackArray(Fortran::lower::AbstractConverter &converter,
/// to the given symbol, generate fir.unpack_array operation
/// that reverts the effect of fir.pack_array.
/// \p def is expected to be hlfir.declare operation.
void genUnpackArray(fir::FirOpBuilder &builder, mlir::Location loc,
fir::FortranVariableOpInterface def,
void genUnpackArray(Fortran::lower::AbstractConverter &converter,
mlir::Location loc, fir::FortranVariableOpInterface def,
const Fortran::semantics::Symbol &sym);

} // namespace lower
Expand Down
5 changes: 5 additions & 0 deletions flang/include/flang/Optimizer/Dialect/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ set(LLVM_TARGET_DEFINITIONS FirAliasTagOpInterface.td)
mlir_tablegen(FirAliasTagOpInterface.h.inc -gen-op-interface-decls)
mlir_tablegen(FirAliasTagOpInterface.cpp.inc -gen-op-interface-defs)

set(LLVM_TARGET_DEFINITIONS SafeTempArrayCopyAttrInterface.td)
mlir_tablegen(SafeTempArrayCopyAttrInterface.h.inc -gen-attr-interface-decls)
mlir_tablegen(SafeTempArrayCopyAttrInterface.cpp.inc -gen-attr-interface-defs)
add_public_tablegen_target(FIRSafeTempArrayCopyAttrInterfaceIncGen)

set(LLVM_TARGET_DEFINITIONS CanonicalizationPatterns.td)
mlir_tablegen(CanonicalizationPatterns.inc -gen-rewriters)
add_public_tablegen_target(CanonicalizationPatternsIncGen)
Expand Down
2 changes: 2 additions & 0 deletions flang/include/flang/Optimizer/Dialect/FIRAttr.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,6 @@ void printFirAttribute(FIROpsDialect *dialect, mlir::Attribute attr,
#define GET_ATTRDEF_CLASSES
#include "flang/Optimizer/Dialect/FIRAttr.h.inc"

#include "flang/Optimizer/Dialect/SafeTempArrayCopyAttrInterface.h"

#endif // FORTRAN_OPTIMIZER_DIALECT_FIRATTR_H
22 changes: 22 additions & 0 deletions flang/include/flang/Optimizer/Dialect/FIRAttr.td
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,26 @@ def fir_PackArrayHeuristicsAttr
let assemblyFormat = "`<` $value `>`";
}

def fir_OpenACCSafeTempArrayCopyAttr : fir_Attr<"OpenACCSafeTempArrayCopy"> {
let mnemonic = "acc_safe_temp_array_copy";
let description = [{
An attribute implementing SafeTempArrayCopyAttrInterface.
It specifies whether it is possible to dynamically check
if creating a temporary copy of a Fortran array is safe
in the context of OpenACC.
It also provides the methods to generate those dynamic checks.
}];
}

def fir_OpenMPSafeTempArrayCopyAttr : fir_Attr<"OpenMPSafeTempArrayCopy"> {
let mnemonic = "omp_safe_temp_array_copy";
let description = [{
An attribute implementing SafeTempArrayCopyAttrInterface.
It specifies whether it is possible to dynamically check
if creating a temporary copy of a Fortran array is safe
in the context of OpenMP.
It also provides the methods to generate those dynamic checks.
}];
}

#endif // FIR_DIALECT_FIR_ATTRS
3 changes: 3 additions & 0 deletions flang/include/flang/Optimizer/Dialect/FIRDialect.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ void addFIRInlinerExtension(mlir::DialectRegistry &registry);
// Register implementation of LLVMTranslationDialectInterface.
void addFIRToLLVMIRExtension(mlir::DialectRegistry &registry);

void registerFortranTempArrayCopyIsSafeExternalModels(
mlir::DialectRegistry &registry);

} // namespace fir

#endif // FORTRAN_OPTIMIZER_DIALECT_FIRDIALECT_H
1 change: 1 addition & 0 deletions flang/include/flang/Optimizer/Dialect/FIROps.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Dialect/FirAliasTagOpInterface.h"
#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
#include "flang/Optimizer/Dialect/SafeTempArrayCopyAttrInterface.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
Expand Down
29 changes: 25 additions & 4 deletions flang/include/flang/Optimizer/Dialect/FIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ include "flang/Optimizer/Dialect/FIRTypes.td"
include "flang/Optimizer/Dialect/FIRAttr.td"
include "flang/Optimizer/Dialect/FortranVariableInterface.td"
include "flang/Optimizer/Dialect/FirAliasTagOpInterface.td"
include "flang/Optimizer/Dialect/SafeTempArrayCopyAttrInterface.td"
include "mlir/IR/BuiltinAttributes.td"

// Base class for FIR operations.
Expand Down Expand Up @@ -3378,6 +3379,14 @@ def fir_PackArrayOp
that is eligible for repacking.
- heuristics attribute specifies conditions when the array repacking
may be optimized.
- is_safe is an optional non-empty array of SafeTempArrayCopyAttr
attributes. Each attribute implements SafeTempArrayCopyAttrInterface
that is used to generate a dynamic predicate value identifying
whether the creation of the temporary array copy is safe.
For example, if omp.fortran_safe_temp_array_copy attribute
is attached, its implementation may generate special code
to check if fir.pack_array may be executed by multiple
threads, and disallow repacking in this case.
}];

let arguments = (ins AnyBoxedArray:$array, UnitAttr:$stack,
Expand All @@ -3386,7 +3395,8 @@ def fir_PackArrayOp
OptionalAttr<UI64Attr>:$min_stride,
DefaultValuedAttr<fir_PackArrayHeuristicsAttr,
"::fir::PackArrayHeuristics::None">:$heuristics,
Variadic<AnyIntegerType>:$typeparams);
Variadic<AnyIntegerType>:$typeparams,
OptionalAttr<NonEmptySafeTempArrayCopyArrayAttr>:$is_safe);

let results = (outs AnyBoxedArray:$result);
let assemblyFormat = [{
Expand All @@ -3395,7 +3405,7 @@ def fir_PackArrayOp
(`no_copy` $no_copy^)?
(`constraints` custom<PackArrayConstraints>($max_size, $max_element_size, $min_stride)^)?
(`heuristics` $heuristics^)?
(`typeparams` $typeparams^)?
(`typeparams` $typeparams^)? (`is_safe` $is_safe^)?
attr-dict `:` functional-type(operands, results)
}];

Expand All @@ -3420,15 +3430,26 @@ def fir_UnpackArrayOp
was allocated.
- no_copy attribute indicates that the temporary array
is not copied into the original temporary array.
- is_safe is an optional non-empty array of SafeTempArrayCopyAttr
attributes. Each attribute implements SafeTempArrayCopyAttrInterface
that is used to generate extra code before any copy-out or
deallocation of the temporary happens.
For example, if acc.fortran_safe_temp_array_copy attribute
is attached, its implementation may generate special code
to check if the temporary has been transferred to OpenACC device
data environment, and issue a runtime error that the temporary
is present on the device while it is about to be deallocated
on the host.
}];

let arguments = (ins AnyBoxedArray:$temp, AnyBoxedArray:$original,
UnitAttr:$stack, UnitAttr:$no_copy);
UnitAttr:$stack, UnitAttr:$no_copy,
OptionalAttr<NonEmptySafeTempArrayCopyArrayAttr>:$is_safe);

let assemblyFormat = [{
$temp `to` $original
(`stack` $stack^):(`heap`)?
(`no_copy` $no_copy^)?
(`no_copy` $no_copy^)? (`is_safe` $is_safe^)?
attr-dict `:` type($original)
}];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===- SafeTempArrayCopyAttrInterface.h -------------------------*- 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_DIALECT_SAFETEMPARRAYCOPYATTRINTERFACE_H
#define FORTRAN_OPTIMIZER_DIALECT_SAFETEMPARRAYCOPYATTRINTERFACE_H

#include "flang/Optimizer/Dialect/FIRAttr.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/OpDefinition.h"

namespace fir {
class FirOpBuilder;
}

#include "flang/Optimizer/Dialect/SafeTempArrayCopyAttrInterface.h.inc"

#endif // FORTRAN_OPTIMIZER_DIALECT_SAFETEMPARRAYCOPYATTRINTERFACE_H
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===- SafeTempArrayCopyAttrInterface.td -------------------*- 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
/// This file defines SafeTempArrayCopyAttrInterface and a generic attribute
/// SafeTempArrayCopyAttr promising the SafeTempArrayCopyAttrInterface.
///
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_SAFETEMPARRAYCOPYATTRINTERFACE_TD
#define FORTRAN_SAFETEMPARRAYCOPYATTRINTERFACE_TD

include "mlir/IR/OpBase.td"

def SafeTempArrayCopyAttrInterface
: AttrInterface<"SafeTempArrayCopyAttrInterface"> {
let cppNamespace = "::fir";
let description = [{
Interface for attributes defining whether creation of a temporary
copy of a Fortran array is safe and/or how to produce proper
dynamic checks to avoid it, if it is unsafe.
}];

let methods =
[StaticInterfaceMethod<
/*desc=*/[{
Returns true iff the usage of the temporary array copy
can be made safe applying some dynamic checks.
}],
/*retTy=*/"bool",
/*methodName=*/"isDynamicallySafe",
/*args=*/(ins)>,
StaticInterfaceMethod<
/*desc=*/[{
Generate FIR that produces an i1 Value indicating
whether the creation of the temporary array copy is safe.
\p array is a definition of the original array.
The implementation may assume that \p array is present
(though, it may be empty).
}],
/*retTy=*/"mlir::Value",
/*methodName=*/"genDynamicCheck",
/*args=*/
(ins "::mlir::Location":$loc, "::fir::FirOpBuilder &":$builder,
"::mlir::Value":$array)>,
StaticInterfaceMethod<
/*desc=*/[{
This method allows inserting any FIR right before the optional
copy-out (from \p temp to \p array) and the deallocation
of the temporary array (implying that the temporary copy was
actually created).
}],
/*retTy=*/"void",
/*methodName=*/"registerTempDeallocation",
/*args=*/
(ins "::mlir::Location":$loc, "::fir::FirOpBuilder &":$builder,
"::mlir::Value":$array, "::mlir::Value":$temp)>,
];
}

def SafeTempArrayCopyAttr
: ConfinedAttr<
AnyAttr, [PromisedAttrInterface<SafeTempArrayCopyAttrInterface>]> {
let description = [{
Generic attribute implementing or promising
the `SafeTempArrayCopyAttrInterface` interface.
}];
}

def SafeTempArrayCopyArrayAttr
: TypedArrayAttrBase<SafeTempArrayCopyAttr,
"array of SafeTempArrayCopyAttr attributes">;

def NonEmptySafeTempArrayCopyArrayAttr
: ConfinedAttr<SafeTempArrayCopyArrayAttr, [ArrayMinCount<1>]>;

#endif // FORTRAN_SAFETEMPARRAYCOPYATTRINTERFACE_TD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ namespace fir::acc {

void registerOpenACCExtensions(mlir::DialectRegistry &registry);

/// Register external models for FIR attributes related to OpenACC.
void registerAttrsExtensions(mlir::DialectRegistry &registry);

/// Register all dialects whose operations may be created
/// by the transformational attributes.
void registerTransformationalAttrsDependentDialects(
mlir::DialectRegistry &registry);

} // namespace fir::acc

#endif // FLANG_OPTIMIZER_OPENACC_REGISTEROPENACCEXTENSIONS_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===- RegisterOpenMPExtensions.h - OpenMP Extension Registration -*- 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 FLANG_OPTIMIZER_OPENMP_SUPPORT_REGISTEROPENMPEXTENSIONS_H_
#define FLANG_OPTIMIZER_OPENMP_SUPPORT_REGISTEROPENMPEXTENSIONS_H_

namespace mlir {
class DialectRegistry;
} // namespace mlir

namespace fir::omp {

void registerOpenMPExtensions(mlir::DialectRegistry &registry);

/// Register external models for FIR attributes related to OpenMP.
void registerAttrsExtensions(mlir::DialectRegistry &registry);

/// Register all dialects whose operations may be created
/// by the transformational attributes.
void registerTransformationalAttrsDependentDialects(
mlir::DialectRegistry &registry);

} // namespace fir::omp

#endif // FLANG_OPTIMIZER_OPENMP_SUPPORT_REGISTEROPENMPEXTENSIONS_H_
Loading