From b3bb4dd3481bac6f6b0df706db2150dd260d0737 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 20 Jan 2023 11:30:39 +0100 Subject: [PATCH] [flang][hlfir] Lower pointer and allocatable sub-part references The previous patches dealt with allocatable and pointer symbol and component whole references. This one deals with the remaining sub-part case where a dereference must be created before applying the sub-part reference on the target. With this patch the support to designate allocatable and pointer in HLFIR is complete, but some use points will need to be updated to use HLFIR designator lowering (at least allocate/deallocate statement and whole allocatable assignment). The partInfo.base had to be turned into an std::optional because loads of allocatable/pointers do create a fir::FortranVariableOpInterface (there is no need to). The optional part comes from the fact that the partInfo.base is not set when creating the partInfo, but later when visiting the designator parts. They are three cases when dereferences must be inserted: - The pointer/allocatable is a symbol followed by a sub-part that is not a component ref. This is done in visit(Symbol). - The pointer/allocatable is a component followed by a sub-part that is not another component ref. This is done in visit(Component). - The pointer/allocatable is followed by a component ref. This case is special since it does not call the above "visit" but instead calls "gen" to break the visit and generate an hlfir.designate for the component base (since one hlfir.designate can only represent one Fortran part-ref, and must be chained to implement a Fortran designator with several part refs). This is done in visitComponentImpl(). Differential Revision: https://reviews.llvm.org/D142124 --- flang/include/flang/Optimizer/Builder/HLFIRTools.h | 3 + flang/lib/Lower/ConvertExprToHLFIR.cpp | 111 +++++++++++++-------- .../HLFIR/allocatable-and-pointer-subparts.f90 | 53 ++++++++++ 3 files changed, 124 insertions(+), 43 deletions(-) create mode 100644 flang/test/Lower/HLFIR/allocatable-and-pointer-subparts.f90 diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h index 3f926d9..20b1f35 100644 --- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h +++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h @@ -103,6 +103,9 @@ public: mlir::Type getFortranElementType() const { return hlfir::getFortranElementType(getType()); } + mlir::Type getElementOrSequenceType() const { + return hlfir::getFortranElementOrSequenceType(getType()); + } bool hasLengthParameters() const { mlir::Type eleTy = getFortranElementType(); diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp index 09c4c7a..da6c9d2 100644 --- a/flang/lib/Lower/ConvertExprToHLFIR.cpp +++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp @@ -82,7 +82,7 @@ private: /// part ref. It contains the lowered pieces of the part-ref that will /// become the operands of an hlfir.declare. struct PartInfo { - fir::FortranVariableOpInterface base; + std::optional base; std::string componentName{}; mlir::Value componentShape; hlfir::DesignateOp::Subscripts subscripts; @@ -144,7 +144,7 @@ private: fir::FortranVariableFlagsAttr attributes) { std::optional complexPart; auto designate = getBuilder().create( - getLoc(), designatorType, partInfo.base.getBase(), + getLoc(), designatorType, partInfo.base.value().getBase(), partInfo.componentName, partInfo.componentShape, partInfo.subscripts, partInfo.substring, complexPart, partInfo.resultShape, partInfo.typeParams, attributes); @@ -162,18 +162,9 @@ private: fir::FortranVariableOpInterface gen(const Fortran::evaluate::Component &component) { + if (Fortran::semantics::IsAllocatableOrPointer(component.GetLastSymbol())) + return genWholeAllocatableOrPointerComponent(component); PartInfo partInfo; - if (Fortran::semantics::IsAllocatableOrPointer(component.GetLastSymbol())) { - // Generate whole allocatable or pointer component reference. The - // hlfir.designate result will be a pointer/allocatable. - auto [_, componentType] = visitComponentImpl( - component, partInfo, /*dereferencePointerAndAllocComponents=*/false); - mlir::Type designatorType = fir::ReferenceType::get(componentType); - fir::FortranVariableFlagsAttr attributes = - Fortran::lower::translateSymbolAttributes(getBuilder().getContext(), - component.GetLastSymbol()); - return genDesignate(designatorType, partInfo, attributes); - } mlir::Type resultType = visit(component, partInfo); return genDesignate(resultType, partInfo, component); } @@ -259,6 +250,19 @@ private: .Default([newEleTy](mlir::Type t) -> mlir::Type { return newEleTy; }); } + fir::FortranVariableOpInterface genWholeAllocatableOrPointerComponent( + const Fortran::evaluate::Component &component) { + // Generate whole allocatable or pointer component reference. The + // hlfir.designate result will be a pointer/allocatable. + PartInfo partInfo; + mlir::Type componentType = visitComponentImpl(component, partInfo).second; + mlir::Type designatorType = fir::ReferenceType::get(componentType); + fir::FortranVariableFlagsAttr attributes = + Fortran::lower::translateSymbolAttributes(getBuilder().getContext(), + component.GetLastSymbol()); + return genDesignate(designatorType, partInfo, attributes); + } + mlir::Type visit(const Fortran::evaluate::DataRef &dataRef, PartInfo &partInfo) { return std::visit([&](const auto &x) { return visit(x, partInfo); }, @@ -282,25 +286,39 @@ private: builder.getContext(), fir::FortranVariableFlagsEnum::parameter); partInfo.base = hlfir::genDeclare(loc, builder, exv, ".stringlit", flags); partInfo.typeParams.push_back(fir::getLen(exv)); - return partInfo.base.getElementOrSequenceType(); + return partInfo.base->getElementOrSequenceType(); } mlir::Type visit(const Fortran::evaluate::SymbolRef &symbolRef, PartInfo &partInfo) { - partInfo.base = gen(symbolRef); - hlfir::genLengthParameters(getLoc(), getBuilder(), partInfo.base, + // A symbol is only visited if there is a following array, substring, or + // complex reference. If the entity is a pointer or allocatable, this + // reference designates the target, so the pointer, allocatable must be + // dereferenced here. + partInfo.base = + hlfir::derefPointersAndAllocatables(loc, getBuilder(), gen(symbolRef)); + hlfir::genLengthParameters(loc, getBuilder(), *partInfo.base, partInfo.typeParams); - return partInfo.base.getElementOrSequenceType(); + return partInfo.base->getElementOrSequenceType(); } mlir::Type visit(const Fortran::evaluate::ArrayRef &arrayRef, PartInfo &partInfo) { mlir::Type baseType; - if (const auto *component = arrayRef.base().UnwrapComponent()) - baseType = hlfir::getFortranElementOrSequenceType( - visitComponentImpl(*component, partInfo).second); - else + if (const auto *component = arrayRef.base().UnwrapComponent()) { + // Pointers and allocatable components must be dereferenced since the + // array ref designates the target (this is done in "visit"). Other + // components need special care to deal with the array%array_comp(indices) + // case. + if (Fortran::semantics::IsAllocatableOrPointer( + component->GetLastSymbol())) + baseType = visit(*component, partInfo); + else + baseType = hlfir::getFortranElementOrSequenceType( + visitComponentImpl(*component, partInfo).second); + } else { baseType = visit(arrayRef.base().GetLastSymbol(), partInfo); + } fir::FirOpBuilder &builder = getBuilder(); mlir::Location loc = getLoc(); @@ -309,7 +327,7 @@ private: auto getBaseBounds = [&](unsigned i) { if (bounds.empty()) { if (partInfo.componentName.empty()) { - bounds = hlfir::genBounds(loc, builder, partInfo.base); + bounds = hlfir::genBounds(loc, builder, partInfo.base.value()); } else { assert( partInfo.componentShape && @@ -378,13 +396,14 @@ private: assert(!partInfo.resultShape && "Fortran designator can only have one ranked part"); partInfo.resultShape = builder.genShape(loc, resultExtents); - } else if (!partInfo.componentName.empty() && partInfo.base.isArray()) { + } else if (!partInfo.componentName.empty() && + partInfo.base.value().isArray()) { // This is an array%array_comp(indices) reference. Keep the // shape of the base array and not the array_comp. - auto compBaseTy = partInfo.base.getElementOrSequenceType(); + auto compBaseTy = partInfo.base->getElementOrSequenceType(); resultType = changeElementType(compBaseTy, resultType); assert(!partInfo.resultShape && "should not have been computed already"); - partInfo.resultShape = hlfir::genShape(loc, builder, partInfo.base); + partInfo.resultShape = hlfir::genShape(loc, builder, *partInfo.base); } return resultType; } @@ -441,19 +460,29 @@ private: mlir::Type visit(const Fortran::evaluate::Component &component, PartInfo &partInfo) { - // Called from contexts where the component is not the base of an ArrayRef. - // In these cases, the component cannot be an array if the base is an - // array. The code below determines the shape of the component reference if - // any. + if (Fortran::semantics::IsAllocatableOrPointer(component.GetLastSymbol())) { + // In a visit, the following reference will address the target. Insert + // the dereference here. + partInfo.base = genWholeAllocatableOrPointerComponent(component); + partInfo.base = hlfir::derefPointersAndAllocatables(loc, getBuilder(), + *partInfo.base); + hlfir::genLengthParameters(loc, getBuilder(), *partInfo.base, + partInfo.typeParams); + return partInfo.base->getElementOrSequenceType(); + } + // This function must be called from contexts where the component is not the + // base of an ArrayRef. In these cases, the component cannot be an array + // if the base is an array. The code below determines the shape of the + // component reference if any. auto [baseType, componentType] = visitComponentImpl(component, partInfo); mlir::Type componentBaseType = hlfir::getFortranElementOrSequenceType(componentType); - if (partInfo.base.isArray()) { + if (partInfo.base.value().isArray()) { // For array%scalar_comp, the result shape is // the one of the base. Compute it here. Note that the lower bounds of the // base are not the ones of the resulting reference (that are default // ones). - partInfo.resultShape = hlfir::genShape(loc, getBuilder(), partInfo.base); + partInfo.resultShape = hlfir::genShape(loc, getBuilder(), *partInfo.base); assert(!partInfo.componentShape && "Fortran designators can only have one ranked part"); return changeElementType(baseType, componentBaseType); @@ -471,8 +500,7 @@ private: // processing a following ArrayRef, if any, and in "visit" otherwise. std::pair visitComponentImpl(const Fortran::evaluate::Component &component, - PartInfo &partInfo, - bool dereferencePointerAndAllocComponents = true) { + PartInfo &partInfo) { fir::FirOpBuilder &builder = getBuilder(); // Break the Designator visit here: if the base is an array-ref, a // coarray-ref, or another component, this creates another hlfir.designate @@ -480,10 +508,15 @@ private: // part-ref. partInfo.base = std::visit([&](const auto &x) { return gen(x); }, component.base().u); + // If the base is an allocatable/pointer, dereference it here since the + // component ref designates its target. + partInfo.base = + hlfir::derefPointersAndAllocatables(loc, builder, *partInfo.base); assert(partInfo.typeParams.empty() && "should not have been computed yet"); - hlfir::genLengthParameters(getLoc(), getBuilder(), partInfo.base, + + hlfir::genLengthParameters(getLoc(), getBuilder(), *partInfo.base, partInfo.typeParams); - mlir::Type baseType = partInfo.base.getElementOrSequenceType(); + mlir::Type baseType = partInfo.base->getElementOrSequenceType(); // Lower the information about the component (type, length parameters and // shape). @@ -514,14 +547,6 @@ private: // Otherwise, the length of the component is deferred and will only // be read when the component is dereferenced. } - - // For pointers and allocatables, if there is a substring, complex part or - // array ref, the designator should be broken here and the pointer or - // allocatable dereferenced. - if (Fortran::semantics::IsAllocatableOrPointer(componentSym) && - dereferencePointerAndAllocComponents) - TODO(loc, "lowering ref to allocatable or pointer component to HLFIR"); - return {baseType, fieldType}; } diff --git a/flang/test/Lower/HLFIR/allocatable-and-pointer-subparts.f90 b/flang/test/Lower/HLFIR/allocatable-and-pointer-subparts.f90 new file mode 100644 index 0000000..842b791 --- /dev/null +++ b/flang/test/Lower/HLFIR/allocatable-and-pointer-subparts.f90 @@ -0,0 +1,53 @@ +! Test lowering of allocatable and pointer sub-part reference to HLFIR +! As opposed to whole reference, a pointer/allocatable dereference must +! be inserted and addressed in a following hlfir.designate to address +! the sub-part. + +! RUN: bbc -emit-fir -hlfir -o - %s -I nw | FileCheck %s + +module m + type t1 + real :: x + end type + type t2 + type(t1), pointer :: p + end type + type t3 + character(:), allocatable :: a(:) + end type +end module + +subroutine test_pointer_component_followed_by_component_ref(x) + use m + type(t2) :: x + call takes_real(x%p%x) +end subroutine +! CHECK-LABEL: func.func @_QPtest_pointer_component_followed_by_component_ref( +! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %{{.*}} {{.*}}Ex +! CHECK: %[[VAL_2:.*]] = hlfir.designate %[[VAL_1]]#0{"p"} {fortran_attrs = #fir.var_attrs} : (!fir.ref>>}>>) -> !fir.ref>>> +! CHECK: %[[VAL_3:.*]] = fir.load %[[VAL_2]] : !fir.ref>>> +! CHECK: %[[VAL_4:.*]] = hlfir.designate %[[VAL_3]]{"x"} : (!fir.box>>) -> !fir.ref + +subroutine test_symbol_followed_by_ref(x) + character(:), allocatable :: x(:) + call test_char(x(10)) +end subroutine +! CHECK-LABEL: func.func @_QPtest_symbol_followed_by_ref( +! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs, uniq_name = {{.*}}Ex" +! CHECK: %[[VAL_2:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref>>>> +! CHECK: %[[VAL_3:.*]] = fir.box_elesize %[[VAL_2]] : (!fir.box>>>) -> index +! CHECK: %[[VAL_4:.*]] = arith.constant 10 : index +! CHECK: %[[VAL_5:.*]] = hlfir.designate %[[VAL_2]] (%[[VAL_4]]) typeparams %[[VAL_3]] : (!fir.box>>>, index, index) -> !fir.boxchar<1> + +subroutine test_component_followed_by_ref(x) + use m + type(t3) :: x + call test_char(x%a(10)) +end subroutine +! CHECK-LABEL: func.func @_QPtest_component_followed_by_ref( +! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %{{.*}} {{.*}}Ex +! CHECK: %[[VAL_2:.*]] = hlfir.designate %[[VAL_1]]#0{"a"} {fortran_attrs = #fir.var_attrs} : (!fir.ref>>>}>>) -> !fir.ref>>>> +! CHECK: %[[VAL_3:.*]] = fir.load %[[VAL_2]] : !fir.ref>>>> +! CHECK: %[[VAL_4:.*]] = fir.box_elesize %[[VAL_3]] : (!fir.box>>>) -> index +! CHECK: %[[VAL_5:.*]] = arith.constant 10 : index +! CHECK: %[[VAL_6:.*]] = hlfir.designate %[[VAL_3]] (%[[VAL_5]]) typeparams %[[VAL_4]] : (!fir.box>>>, index, index) -> !fir.boxchar<1> -- 2.7.4