[flang-commits] [flang] [flang] Reset lbounds for allocatable function results. (PR #65286)
Slava Zakharin via flang-commits
flang-commits at lists.llvm.org
Mon Sep 4 17:18:40 PDT 2023
https://github.com/vzakhari created https://github.com/llvm/llvm-project/pull/65286:
With HLFIR the lbounds for the ALLOCATABLE result are taken from the mutable box created for the result, so the non-default lbounds might be propagated further causing incorrect result, e.g.:
```
program p
real, allocatable :: p5(:)
allocate(p5, source=real_init())
print *, lbound(p5, 1) ! must print 1, but prints 7
contains
function real_init()
real, allocatable :: real_init(:)
allocate(real_init(7:8))
end function real_init
end program p
```
With FIR lowering the box passed for `source` has explicit lower bound 1 at the call site, but the runtime box initialized by `real_init` call still has lower bound 7. I am not sure if the runtime box initialized b `real_init` will ever be accessed in a debugger via Fortran variable names, but I think that having the right runtime bounds that can be accessible via examining registers/stack might be good in general. So I decided to update the runtime bounds at the point of return.
This change "fixes" the test above for HLFIR.
Reviewed By: jeanPerier
Differential Revision: https://reviews.llvm.org/D156187
>From d461ce0b4b0d007e2be3fee3114e621b61c7fffd Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Mon, 4 Sep 2023 17:09:53 -0700
Subject: [PATCH] [flang] Reset lbounds for allocatable function results.
With HLFIR the lbounds for the ALLOCATABLE result are taken
from the mutable box created for the result, so the non-default
lbounds might be propagated further causing incorrect result, e.g.:
```
program p
real, allocatable :: p5(:)
allocate(p5, source=real_init())
print *, lbound(p5, 1) ! must print 1, but prints 7
contains
function real_init()
real, allocatable :: real_init(:)
allocate(real_init(7:8))
end function real_init
end program p
```
With FIR lowering the box passed for `source` has explicit lower
bound 1 at the call site, but the runtime box initialized by
`real_init` call still has lower bound 7. I am not sure if
the runtime box initialized b `real_init` will ever be accessed
in a debugger via Fortran variable names, but I think that
having the right runtime bounds that can be accessible via
examining registers/stack might be good in general. So I decided
to update the runtime bounds at the point of return.
This change "fixes" the test above for HLFIR.
Reviewed By: jeanPerier
Differential Revision: https://reviews.llvm.org/D156187
---
flang/lib/Lower/Bridge.cpp | 24 ++++++
flang/test/Lower/HLFIR/allocatable-return.f90 | 85 +++++++++++++++++++
flang/test/Lower/allocatable-return.f90 | 77 +++++++++++++++++
3 files changed, 186 insertions(+)
create mode 100644 flang/test/Lower/HLFIR/allocatable-return.f90
create mode 100644 flang/test/Lower/allocatable-return.f90
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index d5de3679cffca3..5d5ac0f274773e 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1308,6 +1308,30 @@ class FirConverter : public Fortran::lower::AbstractConverter {
return fir::factory::CharacterExprHelper{*builder, loc}
.createEmboxChar(x.getBuffer(), x.getLen());
},
+ [&](const fir::MutableBoxValue &x) -> mlir::Value {
+ mlir::Value resultRef = resultSymBox.getAddr();
+ mlir::Value load = builder->create<fir::LoadOp>(loc, resultRef);
+ unsigned rank = x.rank();
+ if (x.isAllocatable() && rank > 0) {
+ // ALLOCATABLE array result must have default lower bounds.
+ // At the call site the result box of a function reference
+ // might be considered having default lower bounds, but
+ // the runtime box should probably comply with this assumption
+ // as well. If the result box has proper lbounds in runtime,
+ // this may improve the debugging experience of Fortran apps.
+ // We may consider removing this, if the overhead of setting
+ // default lower bounds is too big.
+ mlir::Value one =
+ builder->createIntegerConstant(loc, builder->getIndexType(), 1);
+ llvm::SmallVector<mlir::Value> lbounds{rank, one};
+ auto shiftTy = fir::ShiftType::get(builder->getContext(), rank);
+ mlir::Value shiftOp =
+ builder->create<fir::ShiftOp>(loc, shiftTy, lbounds);
+ load = builder->create<fir::ReboxOp>(
+ loc, load.getType(), load, shiftOp, /*slice=*/mlir::Value{});
+ }
+ return load;
+ },
[&](const auto &) -> mlir::Value {
mlir::Value resultRef = resultSymBox.getAddr();
mlir::Type resultType = genType(resultSym);
diff --git a/flang/test/Lower/HLFIR/allocatable-return.f90 b/flang/test/Lower/HLFIR/allocatable-return.f90
new file mode 100644
index 00000000000000..d652b2f9f767dc
--- /dev/null
+++ b/flang/test/Lower/HLFIR/allocatable-return.f90
@@ -0,0 +1,85 @@
+! RUN: bbc -emit-hlfir --polymorphic-type -I nowhere %s -o - | FileCheck %s
+
+! Test allocatable return.
+! Allocatable arrays must have default runtime lbounds after the return.
+
+function test_alloc_return_scalar
+ real, allocatable :: test_alloc_return_scalar
+ allocate(test_alloc_return_scalar)
+end function test_alloc_return_scalar
+! CHECK-LABEL: func.func @_QPtest_alloc_return_scalar() -> !fir.box<!fir.heap<f32>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "test_alloc_return_scalar", uniq_name = "_QFtest_alloc_return_scalarEtest_alloc_return_scalar"}
+! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_scalarEtest_alloc_return_scalar"} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_3]]#1 : !fir.ref<!fir.box<!fir.heap<f32>>>
+! CHECK: return %[[VAL_6]] : !fir.box<!fir.heap<f32>>
+! CHECK: }
+
+function test_alloc_return_array
+ real, allocatable :: test_alloc_return_array(:)
+ allocate(test_alloc_return_array(7:8))
+end function test_alloc_return_array
+! CHECK-LABEL: func.func @_QPtest_alloc_return_array() -> !fir.box<!fir.heap<!fir.array<?xf32>>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "test_alloc_return_array", uniq_name = "_QFtest_alloc_return_arrayEtest_alloc_return_array"}
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_arrayEtest_alloc_return_array"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>)
+! CHECK: %[[VAL_19:.*]] = fir.load %[[VAL_5]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+! CHECK: %[[VAL_20:.*]] = arith.constant 1 : index
+! CHECK: %[[VAL_21:.*]] = fir.shift %[[VAL_20]] : (index) -> !fir.shift<1>
+! CHECK: %[[VAL_22:.*]] = fir.rebox %[[VAL_19]](%[[VAL_21]]) : (!fir.box<!fir.heap<!fir.array<?xf32>>>, !fir.shift<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK: return %[[VAL_22]] : !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK: }
+
+function test_alloc_return_char_scalar
+ character(3), allocatable :: test_alloc_return_char_scalar
+ allocate(test_alloc_return_char_scalar)
+end function test_alloc_return_char_scalar
+! CHECK-LABEL: func.func @_QPtest_alloc_return_char_scalar() -> !fir.box<!fir.heap<!fir.char<1,3>>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,3>>> {bindc_name = "test_alloc_return_char_scalar", uniq_name = "_QFtest_alloc_return_char_scalarEtest_alloc_return_char_scalar"}
+! CHECK: %[[VAL_1:.*]] = arith.constant 3 : index
+! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_0]] typeparams %[[VAL_1]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_char_scalarEtest_alloc_return_char_scalar"} : (!fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>, index) -> (!fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>, !fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>)
+! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_4]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>
+! CHECK: return %[[VAL_7]] : !fir.box<!fir.heap<!fir.char<1,3>>>
+! CHECK: }
+
+function test_alloc_return_char_array
+ character(3), allocatable :: test_alloc_return_char_array(:)
+ allocate(test_alloc_return_char_array(7:8))
+end function test_alloc_return_char_array
+! CHECK-LABEL: func.func @_QPtest_alloc_return_char_array() -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>> {bindc_name = "test_alloc_return_char_array", uniq_name = "_QFtest_alloc_return_char_arrayEtest_alloc_return_char_array"}
+! CHECK: %[[VAL_1:.*]] = arith.constant 3 : index
+! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_0]] typeparams %[[VAL_1]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_char_arrayEtest_alloc_return_char_array"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>, index) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>)
+! CHECK: %[[VAL_20:.*]] = fir.load %[[VAL_6]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>
+! CHECK: %[[VAL_21:.*]] = arith.constant 1 : index
+! CHECK: %[[VAL_22:.*]] = fir.shift %[[VAL_21]] : (index) -> !fir.shift<1>
+! CHECK: %[[VAL_23:.*]] = fir.rebox %[[VAL_20]](%[[VAL_22]]) : (!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>, !fir.shift<1>) -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>
+! CHECK: return %[[VAL_23]] : !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>
+! CHECK: }
+
+function test_alloc_return_poly_scalar
+ type t
+ end type t
+ class(*), allocatable :: test_alloc_return_poly_scalar
+ allocate(t :: test_alloc_return_poly_scalar)
+end function test_alloc_return_poly_scalar
+! CHECK-LABEL: func.func @_QPtest_alloc_return_poly_scalar() -> !fir.class<!fir.heap<none>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.class<!fir.heap<none>> {bindc_name = "test_alloc_return_poly_scalar", uniq_name = "_QFtest_alloc_return_poly_scalarEtest_alloc_return_poly_scalar"}
+! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_poly_scalarEtest_alloc_return_poly_scalar"} : (!fir.ref<!fir.class<!fir.heap<none>>>) -> (!fir.ref<!fir.class<!fir.heap<none>>>, !fir.ref<!fir.class<!fir.heap<none>>>)
+! CHECK: %[[VAL_17:.*]] = fir.load %[[VAL_3]]#1 : !fir.ref<!fir.class<!fir.heap<none>>>
+! CHECK: return %[[VAL_17]] : !fir.class<!fir.heap<none>>
+! CHECK: }
+
+function test_alloc_return_poly_array
+ type t
+ end type t
+ class(*), allocatable :: test_alloc_return_poly_array(:)
+ allocate(t :: test_alloc_return_poly_array(7:8))
+end function test_alloc_return_poly_array
+! CHECK-LABEL: func.func @_QPtest_alloc_return_poly_array() -> !fir.class<!fir.heap<!fir.array<?xnone>>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?xnone>>> {bindc_name = "test_alloc_return_poly_array", uniq_name = "_QFtest_alloc_return_poly_arrayEtest_alloc_return_poly_array"}
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_alloc_return_poly_arrayEtest_alloc_return_poly_array"} : (!fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>) -> (!fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>, !fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>)
+! CHECK: %[[VAL_26:.*]] = fir.load %[[VAL_5]]#1 : !fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>
+! CHECK: %[[VAL_27:.*]] = arith.constant 1 : index
+! CHECK: %[[VAL_28:.*]] = fir.shift %[[VAL_27]] : (index) -> !fir.shift<1>
+! CHECK: %[[VAL_29:.*]] = fir.rebox %[[VAL_26]](%[[VAL_28]]) : (!fir.class<!fir.heap<!fir.array<?xnone>>>, !fir.shift<1>) -> !fir.class<!fir.heap<!fir.array<?xnone>>>
+! CHECK: return %[[VAL_29]] : !fir.class<!fir.heap<!fir.array<?xnone>>>
+! CHECK: }
diff --git a/flang/test/Lower/allocatable-return.f90 b/flang/test/Lower/allocatable-return.f90
new file mode 100644
index 00000000000000..c9c0ef37a78d53
--- /dev/null
+++ b/flang/test/Lower/allocatable-return.f90
@@ -0,0 +1,77 @@
+! RUN: bbc -emit-fir --polymorphic-type -I nowhere %s -o - | FileCheck %s
+
+! Test allocatable return.
+! Allocatable arrays must have default runtime lbounds after the return.
+
+function test_alloc_return_scalar
+ real, allocatable :: test_alloc_return_scalar
+ allocate(test_alloc_return_scalar)
+end function test_alloc_return_scalar
+! CHECK-LABEL: func.func @_QPtest_alloc_return_scalar() -> !fir.box<!fir.heap<f32>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<f32>> {bindc_name = "test_alloc_return_scalar", uniq_name = "_QFtest_alloc_return_scalarEtest_alloc_return_scalar"}
+! CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_0]] : !fir.ref<!fir.box<!fir.heap<f32>>>
+! CHECK: return %[[VAL_5]] : !fir.box<!fir.heap<f32>>
+! CHECK: }
+
+function test_alloc_return_array
+ real, allocatable :: test_alloc_return_array(:)
+ allocate(test_alloc_return_array(7:8))
+end function test_alloc_return_array
+! CHECK-LABEL: func.func @_QPtest_alloc_return_array() -> !fir.box<!fir.heap<!fir.array<?xf32>>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "test_alloc_return_array", uniq_name = "_QFtest_alloc_return_arrayEtest_alloc_return_array"}
+! CHECK: %[[VAL_18:.*]] = fir.load %[[VAL_0]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+! CHECK: %[[VAL_19:.*]] = arith.constant 1 : index
+! CHECK: %[[VAL_20:.*]] = fir.shift %[[VAL_19]] : (index) -> !fir.shift<1>
+! CHECK: %[[VAL_21:.*]] = fir.rebox %[[VAL_18]](%[[VAL_20]]) : (!fir.box<!fir.heap<!fir.array<?xf32>>>, !fir.shift<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK: return %[[VAL_21]] : !fir.box<!fir.heap<!fir.array<?xf32>>>
+! CHECK: }
+
+function test_alloc_return_char_scalar
+ character(3), allocatable :: test_alloc_return_char_scalar
+ allocate(test_alloc_return_char_scalar)
+end function test_alloc_return_char_scalar
+! CHECK-LABEL: func.func @_QPtest_alloc_return_char_scalar() -> !fir.box<!fir.heap<!fir.char<1,3>>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,3>>> {bindc_name = "test_alloc_return_char_scalar", uniq_name = "_QFtest_alloc_return_char_scalarEtest_alloc_return_char_scalar"}
+! CHECK: %[[VAL_5:.*]] = fir.load %[[VAL_0]] : !fir.ref<!fir.box<!fir.heap<!fir.char<1,3>>>>
+! CHECK: return %[[VAL_5]] : !fir.box<!fir.heap<!fir.char<1,3>>>
+! CHECK: }
+
+function test_alloc_return_char_array
+ character(3), allocatable :: test_alloc_return_char_array(:)
+ allocate(test_alloc_return_char_array(7:8))
+end function test_alloc_return_char_array
+! CHECK-LABEL: func.func @_QPtest_alloc_return_char_array() -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>> {bindc_name = "test_alloc_return_char_array", uniq_name = "_QFtest_alloc_return_char_arrayEtest_alloc_return_char_array"}
+! CHECK: %[[VAL_18:.*]] = fir.load %[[VAL_0]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>>
+! CHECK: %[[VAL_19:.*]] = arith.constant 1 : index
+! CHECK: %[[VAL_20:.*]] = fir.shift %[[VAL_19]] : (index) -> !fir.shift<1>
+! CHECK: %[[VAL_21:.*]] = fir.rebox %[[VAL_18]](%[[VAL_20]]) : (!fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>, !fir.shift<1>) -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>
+! CHECK: return %[[VAL_21]] : !fir.box<!fir.heap<!fir.array<?x!fir.char<1,3>>>>
+! CHECK: }
+
+function test_alloc_return_poly_scalar
+ type t
+ end type t
+ class(*), allocatable :: test_alloc_return_poly_scalar
+ allocate(t :: test_alloc_return_poly_scalar)
+end function test_alloc_return_poly_scalar
+! CHECK-LABEL: func.func @_QPtest_alloc_return_poly_scalar() -> !fir.class<!fir.heap<none>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.class<!fir.heap<none>> {bindc_name = "test_alloc_return_poly_scalar", uniq_name = "_QFtest_alloc_return_poly_scalarEtest_alloc_return_poly_scalar"}
+! CHECK: %[[VAL_16:.*]] = fir.load %[[VAL_0]] : !fir.ref<!fir.class<!fir.heap<none>>>
+! CHECK: return %[[VAL_16]] : !fir.class<!fir.heap<none>>
+! CHECK: }
+
+function test_alloc_return_poly_array
+ type t
+ end type t
+ class(*), allocatable :: test_alloc_return_poly_array(:)
+ allocate(t :: test_alloc_return_poly_array(7:8))
+end function test_alloc_return_poly_array
+! CHECK-LABEL: func.func @_QPtest_alloc_return_poly_array() -> !fir.class<!fir.heap<!fir.array<?xnone>>> {
+! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.class<!fir.heap<!fir.array<?xnone>>> {bindc_name = "test_alloc_return_poly_array", uniq_name = "_QFtest_alloc_return_poly_arrayEtest_alloc_return_poly_array"}
+! CHECK: %[[VAL_25:.*]] = fir.load %[[VAL_0]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?xnone>>>>
+! CHECK: %[[VAL_26:.*]] = arith.constant 1 : index
+! CHECK: %[[VAL_27:.*]] = fir.shift %[[VAL_26]] : (index) -> !fir.shift<1>
+! CHECK: %[[VAL_28:.*]] = fir.rebox %[[VAL_25]](%[[VAL_27]]) : (!fir.class<!fir.heap<!fir.array<?xnone>>>, !fir.shift<1>) -> !fir.class<!fir.heap<!fir.array<?xnone>>>
+! CHECK: return %[[VAL_28]] : !fir.class<!fir.heap<!fir.array<?xnone>>>
+! CHECK: }
More information about the flang-commits
mailing list