[flang][hlfir] refine hlfir.assign side effects (#113319)

hlfir.assign currently has the `MemoryEffects<[MemWrite]` which makes it
look like it can write to anything. This is good for some cases where
the assign effect cannot be precisely described through the MLIR side
effect API (e.g., when the LHS is a descriptor and it is not possible to
get an OpOperand describing the data address, or when derived type are
involved and finalization could be called, or user defined assignment
for some components). For the most common case of hlfir.assign on
intrinsic types without whole allocatable LHS, this is pessimistic.

This patch implements a finer description of the side effects when
possible, and also adds the proper read/allocate/free effects when
relevant.

The ultimate goal is to suppress the generation of temporary for the LHS
address when dealing with an assignment to a vector subscripted LHS
where the vector subscript is an array constructor that does not refer
to the LHS (as in `x([a,b]) = y`).

Two more patches will follow to enable this.
This commit is contained in:
jeanPerier 2024-10-23 12:33:14 +02:00 committed by GitHub
parent ac5a2010ad
commit d89c1dbaf5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 89 additions and 2 deletions

View File

@ -130,7 +130,7 @@ def hlfir_DeclareOp : hlfir_Op<"declare", [AttrSizedOperandSegments,
let hasVerifier = 1;
}
def fir_AssignOp : hlfir_Op<"assign", [MemoryEffects<[MemWrite]>]> {
def fir_AssignOp : hlfir_Op<"assign", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "Assign an expression or variable value to a Fortran variable";
let description = [{
@ -166,7 +166,7 @@ def fir_AssignOp : hlfir_Op<"assign", [MemoryEffects<[MemWrite]>]> {
}];
let arguments = (ins AnyFortranEntity:$rhs,
Arg<AnyFortranVariable, "", [MemWrite]>:$lhs,
AnyFortranVariable:$lhs,
UnitAttr:$realloc,
UnitAttr:$keep_lhs_length_if_realloc,
UnitAttr:$temporary_lhs);

View File

@ -90,6 +90,62 @@ llvm::LogicalResult hlfir::AssignOp::verify() {
return mlir::success();
}
void hlfir::AssignOp::getEffects(
llvm::SmallVectorImpl<
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
&effects) {
mlir::OpOperand &rhs = getRhsMutable();
mlir::OpOperand &lhs = getLhsMutable();
mlir::Type rhsType = getRhs().getType();
mlir::Type lhsType = getLhs().getType();
if (mlir::isa<fir::RecordType>(hlfir::getFortranElementType(lhsType))) {
// For derived type assignments, set unknown read/write effects since it
// is not known here if user defined finalization is needed, and also
// because allocatable components may lead to "deeper" read/write effects
// that cannot be described with this API.
effects.emplace_back(mlir::MemoryEffects::Read::get(),
mlir::SideEffects::DefaultResource::get());
effects.emplace_back(mlir::MemoryEffects::Write::get(),
mlir::SideEffects::DefaultResource::get());
} else {
// Read effect when RHS is a variable.
if (hlfir::isFortranVariableType(rhsType)) {
if (hlfir::isBoxAddressType(rhsType)) {
// Unknown read effect if the RHS is a descriptor since the read effect
// on the data cannot be described.
effects.emplace_back(mlir::MemoryEffects::Read::get(),
mlir::SideEffects::DefaultResource::get());
} else {
effects.emplace_back(mlir::MemoryEffects::Read::get(), &rhs,
mlir::SideEffects::DefaultResource::get());
}
}
// Write effects on LHS.
if (hlfir::isBoxAddressType(lhsType)) {
// If the LHS is a descriptor, the descriptor will be read and the data
// write cannot be described in this API (and the descriptor may be
// written to in case of realloc, which is covered by the unknown write
// effect.
effects.emplace_back(mlir::MemoryEffects::Read::get(), &lhs,
mlir::SideEffects::DefaultResource::get());
effects.emplace_back(mlir::MemoryEffects::Write::get(),
mlir::SideEffects::DefaultResource::get());
} else {
effects.emplace_back(mlir::MemoryEffects::Write::get(), &lhs,
mlir::SideEffects::DefaultResource::get());
}
}
if (getRealloc()) {
// Reallocation of the data cannot be precisely described by this API.
effects.emplace_back(mlir::MemoryEffects::Free::get(),
mlir::SideEffects::DefaultResource::get());
effects.emplace_back(mlir::MemoryEffects::Allocate::get(),
mlir::SideEffects::DefaultResource::get());
}
}
//===----------------------------------------------------------------------===//
// DeclareOp
//===----------------------------------------------------------------------===//

View File

@ -0,0 +1,31 @@
// Test side effects of hlfir.assign op.
// RUN: fir-opt %s --test-side-effects --verify-diagnostics
func.func @test1(%x: !fir.ref<i32>, %i: i32) {
// expected-remark @below {{found an instance of 'write' on a op operand, on resource '<Default>'}}
hlfir.assign %i to %x : i32, !fir.ref<i32>
return
}
func.func @test2(%x: !fir.ref<i32>, %y: !fir.ref<i32>) {
// expected-remark @below {{found an instance of 'write' on a op operand, on resource '<Default>'}}
// expected-remark @below {{found an instance of 'read' on a op operand, on resource '<Default>'}}
hlfir.assign %y to %x : !fir.ref<i32>, !fir.ref<i32>
return
}
func.func @test3(%x: !fir.ref<!fir.type<t>>, %y: !fir.ref<!fir.type<t>>) {
// expected-remark @below {{found an instance of 'write' on resource '<Default>'}}
// expected-remark @below {{found an instance of 'read' on resource '<Default>'}}
hlfir.assign %y to %x : !fir.ref<!fir.type<t>>, !fir.ref<!fir.type<t>>
return
}
func.func @test4(%x: !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, %y: !fir.box<!fir.array<?xi32>>) {
// expected-remark @below {{found an instance of 'read' on a op operand, on resource '<Default>'}}
// expected-remark @below {{found an instance of 'write' on resource '<Default>'}}
// expected-remark @below {{found an instance of 'free' on resource '<Default>'}}
// expected-remark @below {{found an instance of 'allocate' on resource '<Default>'}}
hlfir.assign %y to %x realloc : !fir.box<!fir.array<?xi32>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
return
}