Skip to content
Open
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
61 changes: 53 additions & 8 deletions flang/include/flang/Optimizer/Dialect/FIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,8 @@ def fir_HasValueOp : fir_Op<"has_value", [Terminator, HasParent<"GlobalOp">]> {
// Operations on !fir.box<T> type objects
//===----------------------------------------------------------------------===//

def fir_EmboxOp : fir_Op<"embox", [NoMemoryEffect, AttrSizedOperandSegments]> {
def fir_EmboxOp : fir_Op<"embox", [NoMemoryEffect, AttrSizedOperandSegments,
fir_FortranObjectViewOpInterface]> {
let summary = "boxes a given reference and (optional) dimension information";

let description = [{
Expand Down Expand Up @@ -870,10 +871,14 @@ def fir_EmboxOp : fir_Op<"embox", [NoMemoryEffect, AttrSizedOperandSegments]> {
return 1 + (getShape() ? 1 : 0) + (getSlice() ? 1 : 0)
+ numLenParams();
}
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getMemref(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
}];
}

def fir_ReboxOp : fir_Op<"rebox", [NoMemoryEffect, AttrSizedOperandSegments]> {
def fir_ReboxOp : fir_Op<"rebox", [NoMemoryEffect, AttrSizedOperandSegments,
fir_FortranObjectViewOpInterface]> {
let summary =
"create a box given another box and (optional) dimension information";

Expand Down Expand Up @@ -923,6 +928,12 @@ def fir_ReboxOp : fir_Op<"rebox", [NoMemoryEffect, AttrSizedOperandSegments]> {
}];

let hasVerifier = 1;

let extraClassDeclaration = [{
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getBox(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
}];
}

def fir_ReboxAssumedRankOp : fir_Op<"rebox_assumed_rank",
Expand Down Expand Up @@ -1071,7 +1082,9 @@ def fir_UnboxProcOp : fir_SimpleOp<"unboxproc", [NoMemoryEffect]> {
let results = (outs FunctionType, fir_ReferenceType:$refTuple);
}

def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoMemoryEffect]> {
def fir_BoxAddrOp
: fir_SimpleOneResultOp<"box_addr", [NoMemoryEffect,
fir_FortranObjectViewOpInterface]> {
let summary = "return a memory reference to the boxed value";

let description = [{
Expand All @@ -1094,6 +1107,12 @@ def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoMemoryEffect]> {
let hasFolder = 1;

let builders = [OpBuilder<(ins "mlir::Value":$val)>];

let extraClassDeclaration = [{
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getVal(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
}];
}

def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoMemoryEffect]> {
Expand Down Expand Up @@ -1766,8 +1785,9 @@ def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
// Record and array type operations
//===----------------------------------------------------------------------===//

def fir_ArrayCoorOp : fir_Op<"array_coor",
[NoMemoryEffect, AttrSizedOperandSegments]> {
def fir_ArrayCoorOp
: fir_Op<"array_coor", [NoMemoryEffect, AttrSizedOperandSegments,
fir_FortranObjectViewOpInterface]> {

let summary = "Find the coordinate of an element of an array";

Expand Down Expand Up @@ -1810,9 +1830,16 @@ def fir_ArrayCoorOp : fir_Op<"array_coor",

let hasVerifier = 1;
let hasCanonicalizer = 1;
let extraClassDeclaration = [{
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getMemref(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
}];
}

def fir_CoordinateOp : fir_Op<"coordinate_of", [NoMemoryEffect]> {
def fir_CoordinateOp
: fir_Op<"coordinate_of", [NoMemoryEffect,
fir_FortranObjectViewOpInterface]> {

let summary = "Finds the coordinate (location) of a value in memory";

Expand Down Expand Up @@ -1864,6 +1891,10 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoMemoryEffect]> {
let extraClassDeclaration = [{
constexpr static int32_t kDynamicIndex = std::numeric_limits<int32_t>::min();
CoordinateIndicesAdaptor getIndices();

// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getRef(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
}];
}

Expand Down Expand Up @@ -2830,7 +2861,8 @@ def fir_VolatileCastOp : fir_SimpleOneResultOp<"volatile_cast", [Pure]> {
}

def fir_ConvertOp
: fir_SimpleOneResultOp<"convert", [NoMemoryEffect, ViewLikeOpInterface]> {
: fir_SimpleOneResultOp<"convert", [NoMemoryEffect, ViewLikeOpInterface,
fir_FortranObjectViewOpInterface]> {
let summary = "encapsulates all Fortran entity type conversions";

let description = [{
Expand Down Expand Up @@ -2868,7 +2900,13 @@ def fir_ConvertOp
static bool isPointerCompatible(mlir::Type ty);
static bool canBeConverted(mlir::Type inType, mlir::Type outType);
static bool areVectorsCompatible(mlir::Type inTy, mlir::Type outTy);

// ViewLikeOpInterface methods:
mlir::Value getViewSource() { return getValue(); }

// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getValue(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult) { return 0; }
}];
let hasCanonicalizer = 1;
}
Expand Down Expand Up @@ -3221,7 +3259,8 @@ def fir_DeclareOp
: fir_Op<"declare", [AttrSizedOperandSegments,
MemoryEffects<[MemAlloc<DebuggingResource>]>,
DeclareOpInterfaceMethods<
fir_FortranVariableStorageOpInterface>]> {
fir_FortranVariableStorageOpInterface>,
fir_FortranObjectViewOpInterface]> {
let summary = "declare a variable";

let description = [{
Expand Down Expand Up @@ -3285,6 +3324,12 @@ def fir_DeclareOp
attr-dict `:` functional-type(operands, results)
}];

let extraClassDeclaration = [{
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getMemref(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult) { return 0; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct for common block variables?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! Yes, it should be valid.

For example:

program main
  common /blk/ a, b
  real :: a, b
  a = 1.0
  b = 2.0
  print *, a, b
end program main
    %1 = fir.address_of(@blk_) : !fir.ref<!fir.array<8xi8>>
    %c0 = arith.constant 0 : index
    %2 = fir.coordinate_of %1, %c0 : (!fir.ref<!fir.array<8xi8>>, index) -> !fir.ref<i8>
    %3 = fir.convert %2 : (!fir.ref<i8>) -> !fir.ref<f32>
    %4:2 = hlfir.declare %3 storage(%1[0]) {uniq_name = "_QFEa"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<f32>, !fir.ref<f32>)
    %c4 = arith.constant 4 : index
    %5 = fir.coordinate_of %1, %c4 : (!fir.ref<!fir.array<8xi8>>, index) -> !fir.ref<i8>
    %6 = fir.convert %5 : (!fir.ref<i8>) -> !fir.ref<f32>
    %7:2 = hlfir.declare %6 storage(%1[4]) {uniq_name = "_QFEb"} : (!fir.ref<f32>, !fir.ref<!fir.array<8xi8>>) -> (!fir.ref<f32>, !fir.ref<f32>)

Basically, [hl]fir.declare does not apply any offset to its operand on its own. The offset is applied to the operands of [hl]fir.declare by fir.coordinate_of. It is also the case for EQUIVALENCE variables.

}];

let hasVerifier = 1;
}

Expand Down
55 changes: 55 additions & 0 deletions flang/include/flang/Optimizer/Dialect/FortranVariableInterface.td
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,59 @@ def fir_FortranVariableStorageOpInterface
[{ return detail::verifyFortranVariableStorageOpInterface($_op); }];
}

def fir_FortranObjectViewOpInterface
: OpInterface<"FortranObjectViewOpInterface"> {
let description = [{
Interface for operations that may produce results that allow accessing
objects in memory based on the operands of these operations.
For example:
```
%0 = fir.convert %x : (!llvm.ptr) -> !fir.llvm_ptr<i32>
```
When the result of `fir.convert` is used to access an object in memory,
FIR alias analysis may want to pass through `fir.convert` to be able
to find the original Fortran object that is being accessed.
This interface provides methods that allow discovering information
about the accessed object and the characteristics of the resulting
"view" of the object, e.g. whether the result and the input
access the object from the same location within the object or
whether they are offsetted (which may happen, for example, in case of
`fir.array_coor` operation).
}];
let cppNamespace = "::fir";

let methods =
[InterfaceMethod<
/*desc=*/
[{ Returns the operand which the given OpResult is based on. }],
/*retTy=*/"::mlir::Value",
/*methodName=*/"getViewSource",
/*args=*/(ins "::mlir::OpResult":$resultView),
/*methodBody=*/"",
/*defaultImplementation=*/[{
assert($_op.getOperation() == resultView.getOwner() &&
"resultView must be a result of this operation");
return $_op.getViewSource(resultView);
}]>,
InterfaceMethod<
/*desc=*/[{
Returns the constant offset (in bytes) applied to the corresponding
operand to produce the resulting view.
If it is not possible to identify the constant offset,
the result in nullopt.
Note that the offset may be negative, e.g. when addressing
an array section with a negative stride.
}],
/*retTy=*/"std::optional<std::int64_t>",
/*methodName=*/"getViewOffset",
/*args=*/(ins "::mlir::OpResult":$resultView),
/*methodBody=*/"",
/*defaultImplementation=*/[{
assert($_op.getOperation() == resultView.getOwner() &&
"resultView must be a result of this operation");
return $_op.getViewOffset(resultView);
}]>,
];
}

#endif // FORTRANVARIABLEINTERFACE
29 changes: 22 additions & 7 deletions flang/include/flang/Optimizer/HLFIR/HLFIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def hlfir_DeclareOp
: hlfir_Op<"declare", [AttrSizedOperandSegments,
MemoryEffects<[MemAlloc<DebuggingResource>]>,
DeclareOpInterfaceMethods<
fir_FortranVariableStorageOpInterface>]> {
fir_FortranVariableStorageOpInterface>,
fir_FortranObjectViewOpInterface]> {
let summary = "declare a variable and produce an SSA value that can be used as a variable in HLFIR operations";

let description = [{
Expand Down Expand Up @@ -140,6 +141,10 @@ def hlfir_DeclareOp
/// 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);

// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getMemref(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult) { return 0; }
}];

let hasVerifier = 1;
Expand Down Expand Up @@ -213,8 +218,11 @@ def fir_AssignOp : hlfir_Op<"assign", [DeclareOpInterfaceMethods<MemoryEffectsOp
let hasVerifier = 1;
}

def hlfir_DesignateOp : hlfir_Op<"designate", [AttrSizedOperandSegments,
DeclareOpInterfaceMethods<fir_FortranVariableOpInterface>, NoMemoryEffect]> {
def hlfir_DesignateOp
: hlfir_Op<"designate",
[AttrSizedOperandSegments,
DeclareOpInterfaceMethods<fir_FortranVariableOpInterface>,
NoMemoryEffect, fir_FortranObjectViewOpInterface]> {
let summary = "Designate a Fortran variable";

let description = [{
Expand Down Expand Up @@ -278,6 +286,9 @@ def hlfir_DesignateOp : hlfir_Op<"designate", [AttrSizedOperandSegments,
void setFortranAttrs(fir::FortranVariableFlagsEnum flags) {
this->setFortranAttrs(std::optional<fir::FortranVariableFlagsEnum>(flags));
}
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getMemref(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult);
}];

let builders = [
Expand Down Expand Up @@ -938,8 +949,9 @@ def hlfir_EndAssociateOp : hlfir_Op<"end_associate", [MemoryEffects<[MemFree]>]>
let hasVerifier = 1;
}

def hlfir_AsExprOp : hlfir_Op<"as_expr",
[DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
def hlfir_AsExprOp
: hlfir_Op<"as_expr", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
fir_FortranObjectViewOpInterface]> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

I do not think AsExpr is a view op, It result should be regarded as a value/tensor with no memory connection to the input.

When it has not been eliminated, it may end-up being implemented as a copy or as use the original storage if it is proven safe.

I think as_expr may have been given some handling in aliasing so that it could have an easier code generation.

I expect this was done so that hlfir.assign optimization would not optimize hlfir.asssign of hlfir.elemental using hlfir.as_expr in case the as_expr source overlaps with the LHS assuming that as_expr codegen could end up using the source.

But I feel the problem should be seen in the other direction, and it is up to the as_expr codegen to prove that it can safely replace the copy by its source.

If possible, I would prefer keeping the ad-hoc handling in FIR aliasing so that we can try to fix this instead of enshrining the assumption that as_expr result is based on its source in the operation definition.

let summary = "Take the value of an array, character or derived variable";

let description = [{
Expand All @@ -961,8 +973,11 @@ def hlfir_AsExprOp : hlfir_Op<"as_expr",
let results = (outs hlfir_ExprType);

let extraClassDeclaration = [{
// Is this a "move" ?
bool isMove() { return getMustFree() != mlir::Value{}; }
// Is this a "move" ?
bool isMove() { return getMustFree() != mlir::Value{}; }
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getVar(); }
std::optional<std::int64_t> getViewOffset(mlir::OpResult) { return 0; }
}];

let assemblyFormat = [{
Expand Down
Loading