diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp index 1d74cf2daf5f3..58d754668000e 100644 --- a/flang/lib/Lower/ConvertVariable.cpp +++ b/flang/lib/Lower/ConvertVariable.cpp @@ -652,26 +652,30 @@ defaultInitializeAtRuntime(Fortran::lower::AbstractConverter &converter, } } -/// Check whether a variable needs to be finalized according to clause 7.5.6.3 -/// point 3. -/// Must be nonpointer, nonallocatable object that is not a dummy argument or -/// function result. -static bool needEndFinalization(const Fortran::lower::pft::Variable &var) { +enum class VariableCleanUp { Finalize, Deallocate }; +/// Check whether a local variable needs to be finalized according to clause +/// 7.5.6.3 point 3 or if it is an allocatable that must be deallocated. Note +/// that deallocation will trigger finalization if the type has any. +static std::optional +needDeallocationOrFinalization(const Fortran::lower::pft::Variable &var) { if (!var.hasSymbol()) - return false; + return std::nullopt; const Fortran::semantics::Symbol &sym = var.getSymbol(); const Fortran::semantics::Scope &owner = sym.owner(); if (owner.kind() == Fortran::semantics::Scope::Kind::MainProgram) { // The standard does not require finalizing main program variables. - return false; + return std::nullopt; } if (!Fortran::semantics::IsPointer(sym) && - !Fortran::semantics::IsAllocatable(sym) && !Fortran::semantics::IsDummy(sym) && !Fortran::semantics::IsFunctionResult(sym) && - !Fortran::semantics::IsSaved(sym)) - return hasFinalization(sym); - return false; + !Fortran::semantics::IsSaved(sym)) { + if (Fortran::semantics::IsAllocatable(sym)) + return VariableCleanUp::Deallocate; + if (hasFinalization(sym)) + return VariableCleanUp::Finalize; + } + return std::nullopt; } /// Check whether a variable needs the be finalized according to clause 7.5.6.3 @@ -779,15 +783,30 @@ static void instantiateLocal(Fortran::lower::AbstractConverter &converter, finalizeAtRuntime(converter, var, symMap); if (mustBeDefaultInitializedAtRuntime(var)) defaultInitializeAtRuntime(converter, var, symMap); - if (needEndFinalization(var)) { + if (std::optional cleanup = + needDeallocationOrFinalization(var)) { auto *builder = &converter.getFirOpBuilder(); mlir::Location loc = converter.getCurrentLocation(); fir::ExtendedValue exv = converter.getSymbolExtendedValue(var.getSymbol(), &symMap); - converter.getFctCtx().attachCleanup([builder, loc, exv]() { - mlir::Value box = builder->createBox(loc, exv); - fir::runtime::genDerivedTypeDestroy(*builder, loc, box); - }); + switch (*cleanup) { + case VariableCleanUp::Finalize: + converter.getFctCtx().attachCleanup([builder, loc, exv]() { + mlir::Value box = builder->createBox(loc, exv); + fir::runtime::genDerivedTypeDestroy(*builder, loc, box); + }); + break; + case VariableCleanUp::Deallocate: + auto *converterPtr = &converter; + converter.getFctCtx().attachCleanup([converterPtr, loc, exv]() { + const fir::MutableBoxValue *mutableBox = + exv.getBoxOf(); + assert(mutableBox && + "trying to deallocate entity not lowered as allocatable"); + Fortran::lower::genDeallocateIfAllocated(*converterPtr, *mutableBox, + loc); + }); + } } } diff --git a/flang/test/Lower/HLFIR/allocatable-end-of-scope-dealloc.f90 b/flang/test/Lower/HLFIR/allocatable-end-of-scope-dealloc.f90 new file mode 100644 index 0000000000000..ad4b015ef9443 --- /dev/null +++ b/flang/test/Lower/HLFIR/allocatable-end-of-scope-dealloc.f90 @@ -0,0 +1,239 @@ +! Test automatic deallocation of local allocatables as described in +! Fortran 2018 standard 9.7.3.2 point 2. and 3. + +! RUN: bbc -emit-hlfir -o - %s | FileCheck %s +module dtypedef + type must_finalize + integer :: i + contains + final :: finalize + end type + type contain_must_finalize + type(must_finalize) :: a + end type + interface + subroutine finalize(a) + import :: must_finalize + type(must_finalize), intent(inout) :: a + end subroutine + end interface + real, allocatable :: x +end module + +subroutine simple() + real, allocatable :: x + allocate(x) + call bar() +end subroutine +! CHECK-LABEL: func.func @_QPsimple() { +! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare {{.*}}"_QFsimpleEx" +! CHECK: fir.call @_QPbar +! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_3]]#1 : !fir.ref>> +! CHECK: %[[VAL_7:.*]] = fir.box_addr %[[VAL_6]] : (!fir.box>) -> !fir.heap +! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_7]] : (!fir.heap) -> i64 +! CHECK: %[[VAL_9:.*]] = arith.constant 0 : i64 +! CHECK: %[[VAL_10:.*]] = arith.cmpi ne, %[[VAL_8]], %[[VAL_9]] : i64 +! CHECK: fir.if %[[VAL_10]] { +! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_3]]#1 : !fir.ref>> +! CHECK: %[[VAL_12:.*]] = fir.box_addr %[[VAL_11]] : (!fir.box>) -> !fir.heap +! CHECK: fir.freemem %[[VAL_12]] : !fir.heap +! CHECK: %[[VAL_13:.*]] = fir.zero_bits !fir.heap +! CHECK: %[[VAL_14:.*]] = fir.embox %[[VAL_13]] : (!fir.heap) -> !fir.box> +! CHECK: fir.store %[[VAL_14]] to %[[VAL_3]]#1 : !fir.ref>> +! CHECK: } + +subroutine multiple_return(cdt) + real, allocatable :: x + logical :: cdt + allocate(x) + if (cdt) return + call bar() +end subroutine +! CHECK-LABEL: func.func @_QPmultiple_return( +! CHECK: cf.cond_br %{{.*}}, ^bb1, ^bb2 +! CHECK: ^bb1: +! CHECK-NOT: fir.freemem +! CHECK: cf.br ^bb3 +! CHECK: ^bb2: +! CHECK: fir.call @_QPbar +! CHECK: cf.br ^bb3 +! CHECK: ^bb3: +! CHECK: fir.if {{.*}} { +! CHECK: fir.freemem +! CHECK: } +! CHECK: return + +subroutine derived() + use dtypedef, only : must_finalize + type(must_finalize), allocatable :: x + allocate(x) + call bar() +end subroutine +! CHECK-LABEL: func.func @_QPderived() { +! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare {{.*}}"_QFderivedEx" +! CHECK: fir.call @_QPbar +! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_3]]#1 : !fir.ref>>> +! CHECK: %[[VAL_12:.*]] = fir.box_addr %[[VAL_11]] : (!fir.box>>) -> !fir.heap> +! CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_12]] : (!fir.heap>) -> i64 +! CHECK: %[[VAL_14:.*]] = arith.constant 0 : i64 +! CHECK: %[[VAL_15:.*]] = arith.cmpi ne, %[[VAL_13]], %[[VAL_14]] : i64 +! CHECK: fir.if %[[VAL_15]] { +! CHECK: %[[VAL_16:.*]] = arith.constant false +! CHECK: %[[VAL_17:.*]] = fir.absent !fir.box +! CHECK: %[[VAL_20:.*]] = fir.convert %[[VAL_3]]#1 : (!fir.ref>>>) -> !fir.ref> +! CHECK: %[[VAL_22:.*]] = fir.call @_FortranAAllocatableDeallocate(%[[VAL_20]], %[[VAL_16]], %[[VAL_17]], %{{.*}}, %{{.*}}) +! CHECK: } + +subroutine derived2() + use dtypedef, only : contain_must_finalize + type(contain_must_finalize), allocatable :: x + allocate(x) +end subroutine +! CHECK-LABEL: func.func @_QPderived2( +! CHECK: fir.if {{.*}} { +! CHECK: fir.call @_FortranAAllocatableDeallocate +! CHECK: } + +subroutine simple_block() + block + real, allocatable :: x + allocate(x) + call bar() + end block + call bar_after_block() +end subroutine +! CHECK-LABEL: func.func @_QPsimple_block( +! CHECK: fir.call @_QPbar +! CHECK: fir.if {{.*}} { +! CHECK: fir.freemem +! CHECK: } +! CHECK: fir.call @_QPbar_after_block + +subroutine mutiple_return_block(cdt) + logical :: cdt + block + real, allocatable :: x + allocate(x) + if (cdt) return + call bar() + end block + call bar_after_block() +end subroutine +! CHECK-LABEL: func.func @_QPmutiple_return_block( +! CHECK: cf.cond_br %{{.*}}, ^bb1, ^bb2 +! CHECK: ^bb1: +! CHECK: fir.if {{.*}} { +! CHECK: fir.freemem +! CHECK: } +! CHECK: cf.br ^bb3 +! CHECK: ^bb2: +! CHECK: fir.call @_QPbar +! CHECK: fir.if {{.*}} { +! CHECK: fir.freemem +! CHECK: } +! CHECK: fir.call @_QPbar_after_block +! CHECK: cf.br ^bb3 +! CHECK: ^bb3: +! CHECK: return + + +subroutine derived_block() + use dtypedef, only : must_finalize + block + type(must_finalize), allocatable :: x + allocate(x) + call bar() + end block + call bar_after_block() +end subroutine +! CHECK-LABEL: func.func @_QPderived_block( +! CHECK: fir.call @_QPbar +! CHECK: fir.if {{.*}} { +! CHECK: fir.call @_FortranAAllocatableDeallocate +! CHECK: } +! CHECK: fir.call @_QPbar_after_block + +subroutine derived_block2() + use dtypedef, only : contain_must_finalize + call bar() + block + type(contain_must_finalize), allocatable :: x + allocate(x) + end block + call bar_after_block() +end subroutine +! CHECK-LABEL: func.func @_QPderived_block2( +! CHECK: fir.call @_QPbar +! CHECK: fir.if {{.*}} { +! CHECK: fir.call @_FortranAAllocatableDeallocate +! CHECK: } +! CHECK: fir.call @_QPbar_after_block + +subroutine no_dealloc_saved() + real, allocatable, save :: x + allocate(x) +end subroutine +! CHECK-LABEL: func.func @_QPno_dealloc_save +! CHECK-NOT: freemem +! CHECK-NOT: Deallocate +! CHECK: return + +subroutine no_dealloc_block_saved() + block + real, allocatable, save :: x + allocate(x) + end block +end subroutine +! CHECK-LABEL: func.func @_QPno_dealloc_block_saved +! CHECK-NOT: freemem +! CHECK-NOT: Deallocate +! CHECK: return + +function no_dealloc_result() result(x) + real, allocatable :: x + allocate(x) +end function +! CHECK-LABEL: func.func @_QPno_dealloc_result +! CHECK-NOT: freemem +! CHECK-NOT: Deallocate +! CHECK: return + +subroutine no_dealloc_dummy(x) + real, allocatable :: x + allocate(x) +end subroutine +! CHECK-LABEL: func.func @_QPno_dealloc_dummy +! CHECK-NOT: freemem +! CHECK-NOT: Deallocate +! CHECK: return + +subroutine no_dealloc_module_var() + use dtypedef, only : x + allocate(x) +end subroutine +! CHECK-LABEL: func.func @_QPno_dealloc_module_var +! CHECK-NOT: freemem +! CHECK-NOT: Deallocate +! CHECK: return + +subroutine no_dealloc_host_assoc() + real, allocatable :: x + call internal() +contains + subroutine internal() + allocate(x) + end subroutine +end subroutine +! CHECK-LABEL: func.func @_QFno_dealloc_host_assocPinternal +! CHECK-NOT: freemem +! CHECK-NOT: Deallocate +! CHECK: return + +subroutine no_dealloc_pointer(x) + real, pointer :: x + allocate(x) +end subroutine +! CHECK-LABEL: func.func @_QPno_dealloc_pointer +! CHECK-NOT: freemem +! CHECK-NOT: Deallocate +! CHECK: return diff --git a/flang/test/Lower/allocatable-polymorphic.f90 b/flang/test/Lower/allocatable-polymorphic.f90 index 148ae3be9f70a..53b257d2eacea 100644 --- a/flang/test/Lower/allocatable-polymorphic.f90 +++ b/flang/test/Lower/allocatable-polymorphic.f90 @@ -656,11 +656,9 @@ program test_alloc ! allocatable. ! LLVM-LABEL: define void @_QMpolyPtest_deallocate() -! LLVM: %[[ALLOCA1:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } -! LLVM: %[[ALLOCA2:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, i64 1 -! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } { ptr null, i64 ptrtoint (ptr getelementptr (%_QMpolyTp1, ptr null, i32 1) to i64), i32 20180515, i8 0, i8 42, i8 2, i8 1, ptr @_QMpolyE.dt.p1, [1 x i64] undef }, ptr %[[ALLOCA1]] +! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } { ptr null, i64 ptrtoint (ptr getelementptr (%_QMpolyTp1, ptr null, i32 1) to i64), i32 20180515, i8 0, i8 42, i8 2, i8 1, ptr @_QMpolyE.dt.p1, [1 x i64] undef }, ptr %[[ALLOCA1:[0-9]*]] ! LLVM: %[[LOAD:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[ALLOCA1]] -! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[LOAD]], ptr %[[ALLOCA2]] +! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[LOAD]], ptr %[[ALLOCA2:[0-9]*]] ! LLVM: %{{.*}} = call {} @_FortranAAllocatableInitDerivedForAllocate(ptr %[[ALLOCA2]], ptr @_QMpolyE.dt.p1, i32 0, i32 0) ! LLVM: %{{.*}} = call i32 @_FortranAAllocatableAllocate(ptr %[[ALLOCA2]], i1 false, ptr null, ptr @_QQcl.{{.*}}, i32 {{.*}}) ! LLVM: %{{.*}} = call i32 @_FortranAAllocatableDeallocatePolymorphic(ptr %[[ALLOCA2]], ptr {{.*}}, i1 false, ptr null, ptr @_QQcl.{{.*}}, i32 {{.*}})