[flang-commits] [flang] f677c5e - [flang] Initial lowering of SELECT TYPE construct to fir.select_type operation
Valentin Clement via flang-commits
flang-commits at lists.llvm.org
Mon Nov 14 01:48:47 PST 2022
Author: Valentin Clement
Date: 2022-11-14T10:48:41+01:00
New Revision: f677c5ee97911561c9948684029aef15b1f5cdd0
URL: https://github.com/llvm/llvm-project/commit/f677c5ee97911561c9948684029aef15b1f5cdd0
DIFF: https://github.com/llvm/llvm-project/commit/f677c5ee97911561c9948684029aef15b1f5cdd0.diff
LOG: [flang] Initial lowering of SELECT TYPE construct to fir.select_type operation
This patch is the initial path to lower the SELECT TYPE construct to the
fir.select_type operation. More work is required in the AssocEntity
mapping but it will be done in a follow up patch to ease the review.
Reviewed By: jeanPerier
Differential Revision: https://reviews.llvm.org/D137728
Added:
flang/test/Lower/select-type.f90
Modified:
flang/include/flang/Optimizer/Builder/BoxValue.h
flang/lib/Lower/Bridge.cpp
flang/lib/Lower/ConvertExpr.cpp
flang/lib/Lower/SymbolMap.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/Builder/BoxValue.h b/flang/include/flang/Optimizer/Builder/BoxValue.h
index 94a72f0750e6a..bffb4924d0009 100644
--- a/flang/include/flang/Optimizer/Builder/BoxValue.h
+++ b/flang/include/flang/Optimizer/Builder/BoxValue.h
@@ -517,6 +517,14 @@ class ExtendedValue : public details::matcher<ExtendedValue> {
[](const auto &box) -> unsigned { return box.rank(); });
}
+ bool isPolymorphic() const {
+ return match([](const fir::PolymorphicValue &box) -> bool { return true; },
+ [](const fir::ArrayBoxValue &box) -> bool {
+ return box.getTdesc() ? true : false;
+ },
+ [](const auto &box) -> bool { return false; });
+ }
+
/// Is this an assumed size array ?
bool isAssumedSize() const;
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 00a0a8dec0be1..1553b1fc26910 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -2100,14 +2100,146 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
void genFIR(const Fortran::parser::SelectTypeConstruct &selectTypeConstruct) {
- setCurrentPositionAt(selectTypeConstruct);
- TODO(toLocation(), "SelectTypeConstruct implementation");
- }
- void genFIR(const Fortran::parser::SelectTypeStmt &) {
- TODO(toLocation(), "SelectTypeStmt implementation");
- }
- void genFIR(const Fortran::parser::TypeGuardStmt &) {
- TODO(toLocation(), "TypeGuardStmt implementation");
+ mlir::Location loc = toLocation();
+ mlir::MLIRContext *context = builder->getContext();
+ Fortran::lower::StatementContext stmtCtx;
+ fir::ExtendedValue selector;
+ llvm::SmallVector<mlir::Attribute> attrList;
+ llvm::SmallVector<mlir::Block *> blockList;
+ unsigned typeGuardIdx = 0;
+ bool hasLocalScope = false;
+
+ for (Fortran::lower::pft::Evaluation &eval :
+ getEval().getNestedEvaluations()) {
+ if (auto *selectTypeStmt =
+ eval.getIf<Fortran::parser::SelectTypeStmt>()) {
+ // Retrieve the selector
+ const auto &s = std::get<Fortran::parser::Selector>(selectTypeStmt->t);
+ if (const auto *v = std::get_if<Fortran::parser::Variable>(&s.u))
+ selector = genExprBox(loc, *Fortran::semantics::GetExpr(*v), stmtCtx);
+ else
+ fir::emitFatalError(
+ loc, "selector with expr not expected in select type statement");
+
+ // Going through the controlSuccessor first to create the
+ // fir.select_type operation.
+ mlir::Block *defaultBlock = eval.parentConstruct->constructExit->block;
+ for (Fortran::lower::pft::Evaluation *e = eval.controlSuccessor; e;
+ e = e->controlSuccessor) {
+ const auto &typeGuardStmt =
+ e->getIf<Fortran::parser::TypeGuardStmt>();
+ const auto &guard =
+ std::get<Fortran::parser::TypeGuardStmt::Guard>(typeGuardStmt->t);
+ assert(e->block && "missing TypeGuardStmt block");
+ // CLASS DEFAULT
+ if (std::holds_alternative<Fortran::parser::Default>(guard.u)) {
+ defaultBlock = e->block;
+ continue;
+ }
+
+ blockList.push_back(e->block);
+ if (const auto *typeSpec =
+ std::get_if<Fortran::parser::TypeSpec>(&guard.u)) {
+ // TYPE IS
+ mlir::Type ty;
+ if (std::holds_alternative<Fortran::parser::IntrinsicTypeSpec>(
+ typeSpec->u)) {
+ const Fortran::semantics::IntrinsicTypeSpec *intrinsic =
+ typeSpec->declTypeSpec->AsIntrinsic();
+ int kind =
+ Fortran::evaluate::ToInt64(intrinsic->kind()).value_or(kind);
+ llvm::SmallVector<Fortran::lower::LenParameterTy> params;
+ if (intrinsic->category() ==
+ Fortran::common::TypeCategory::Character ||
+ intrinsic->category() ==
+ Fortran::common::TypeCategory::Derived)
+ TODO(loc, "typeSpec with length parameters");
+ ty = genType(intrinsic->category(), kind, params);
+ } else {
+ const Fortran::semantics::DerivedTypeSpec *derived =
+ typeSpec->declTypeSpec->AsDerived();
+ ty = genType(*derived);
+ }
+ attrList.push_back(fir::ExactTypeAttr::get(ty));
+ } else if (const auto *derived =
+ std::get_if<Fortran::parser::DerivedTypeSpec>(
+ &guard.u)) {
+ // CLASS IS
+ assert(derived->derivedTypeSpec && "derived type spec is null");
+ mlir::Type ty = genType(*(derived->derivedTypeSpec));
+ attrList.push_back(fir::SubclassAttr::get(ty));
+ }
+ }
+ attrList.push_back(mlir::UnitAttr::get(context));
+ blockList.push_back(defaultBlock);
+ builder->create<fir::SelectTypeOp>(loc, fir::getBase(selector),
+ attrList, blockList);
+ } else if (auto *typeGuardStmt =
+ eval.getIf<Fortran::parser::TypeGuardStmt>()) {
+ // Map the type guard local symbol for the selector to a more precise
+ // typed entity in the TypeGuardStmt when necessary.
+ const auto &guard =
+ std::get<Fortran::parser::TypeGuardStmt::Guard>(typeGuardStmt->t);
+ if (hasLocalScope)
+ localSymbols.popScope();
+ localSymbols.pushScope();
+ hasLocalScope = true;
+ assert(attrList.size() >= typeGuardIdx &&
+ "TypeGuard attribute missing");
+ mlir::Attribute typeGuardAttr = attrList[typeGuardIdx];
+ mlir::Block *typeGuardBlock = blockList[typeGuardIdx];
+ const Fortran::semantics::Scope &guardScope =
+ bridge.getSemanticsContext().FindScope(eval.position);
+ mlir::OpBuilder::InsertPoint crtInsPt = builder->saveInsertionPoint();
+ builder->setInsertionPointToStart(typeGuardBlock);
+
+ auto addAssocEntitySymbol = [&](fir::ExtendedValue exv) {
+ for (auto &symbol : guardScope.GetSymbols()) {
+ if (symbol->GetUltimate()
+ .detailsIf<Fortran::semantics::AssocEntityDetails>()) {
+ localSymbols.addSymbol(symbol, exv);
+ break;
+ }
+ }
+ };
+
+ if (std::holds_alternative<Fortran::parser::Default>(guard.u)) {
+ // CLASS DEFAULT
+ addAssocEntitySymbol(selector);
+ } else if (const auto *typeSpec =
+ std::get_if<Fortran::parser::TypeSpec>(&guard.u)) {
+ // TYPE IS
+ fir::ExactTypeAttr attr =
+ typeGuardAttr.dyn_cast<fir::ExactTypeAttr>();
+ mlir::Value exactValue;
+ if (std::holds_alternative<Fortran::parser::IntrinsicTypeSpec>(
+ typeSpec->u)) {
+ exactValue = builder->create<fir::BoxAddrOp>(
+ loc, fir::ReferenceType::get(attr.getType()),
+ fir::getBase(selector));
+ } else if (std::holds_alternative<Fortran::parser::DerivedTypeSpec>(
+ typeSpec->u)) {
+ exactValue = builder->create<fir::ConvertOp>(
+ loc, fir::BoxType::get(attr.getType()), fir::getBase(selector));
+ }
+ addAssocEntitySymbol(exactValue);
+ } else if (std::holds_alternative<Fortran::parser::DerivedTypeSpec>(
+ guard.u)) {
+ // CLASS IS
+ fir::SubclassAttr attr = typeGuardAttr.dyn_cast<fir::SubclassAttr>();
+ mlir::Value derived = builder->create<fir::ConvertOp>(
+ loc, fir::ClassType::get(attr.getType()), fir::getBase(selector));
+ addAssocEntitySymbol(derived);
+ }
+ builder->restoreInsertionPoint(crtInsPt);
+ ++typeGuardIdx;
+ } else if (eval.getIf<Fortran::parser::EndSelectStmt>()) {
+ if (hasLocalScope)
+ localSymbols.popScope();
+ stmtCtx.finalize();
+ }
+ genFIR(eval);
+ }
}
//===--------------------------------------------------------------------===//
@@ -2755,6 +2887,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
void genFIR(const Fortran::parser::IfThenStmt &) {} // nop
void genFIR(const Fortran::parser::NonLabelDoStmt &) {} // nop
void genFIR(const Fortran::parser::OmpEndLoopDirective &) {} // nop
+ void genFIR(const Fortran::parser::SelectTypeStmt &) {} // nop
+ void genFIR(const Fortran::parser::TypeGuardStmt &) {} // nop
void genFIR(const Fortran::parser::NamelistStmt &) {
TODO(toLocation(), "NamelistStmt lowering");
diff --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp
index a509bab44af72..580e48478a52c 100644
--- a/flang/lib/Lower/ConvertExpr.cpp
+++ b/flang/lib/Lower/ConvertExpr.cpp
@@ -4162,7 +4162,7 @@ class ArrayExprLowering {
mlir::Value convertElementForUpdate(mlir::Location loc, mlir::Type eleTy,
mlir::Value origVal) {
if (auto origEleTy = fir::dyn_cast_ptrEleTy(origVal.getType()))
- if (origEleTy.isa<fir::BoxType>()) {
+ if (origEleTy.isa<fir::BaseBoxType>()) {
// If origVal is a box variable, load it so it is in the value domain.
origVal = builder.create<fir::LoadOp>(loc, origVal);
}
@@ -7645,8 +7645,8 @@ fir::ExtendedValue Fortran::lower::createBoxValue(
}
fir::ExtendedValue addr = Fortran::lower::createSomeExtendedAddress(
loc, converter, expr, symMap, stmtCtx);
- fir::ExtendedValue result =
- fir::BoxValue(converter.getFirOpBuilder().createBox(loc, addr));
+ fir::ExtendedValue result = fir::BoxValue(
+ converter.getFirOpBuilder().createBox(loc, addr, addr.isPolymorphic()));
if (isParentComponent(expr))
result = updateBoxForParentComponent(converter, result, expr);
return result;
diff --git a/flang/lib/Lower/SymbolMap.cpp b/flang/lib/Lower/SymbolMap.cpp
index 73986c220fc29..84f07c88b9379 100644
--- a/flang/lib/Lower/SymbolMap.cpp
+++ b/flang/lib/Lower/SymbolMap.cpp
@@ -26,6 +26,7 @@ void Fortran::lower::SymMap::addSymbol(Fortran::semantics::SymbolRef sym,
[&](const fir::CharArrayBoxValue &v) { makeSym(sym, v, force); },
[&](const fir::BoxValue &v) { makeSym(sym, v, force); },
[&](const fir::MutableBoxValue &v) { makeSym(sym, v, force); },
+ [&](const fir::PolymorphicValue &v) { makeSym(sym, v, force); },
[](auto) {
llvm::report_fatal_error("value not added to symbol table");
});
diff --git a/flang/test/Lower/select-type.f90 b/flang/test/Lower/select-type.f90
new file mode 100644
index 0000000000000..ee42c485831b2
--- /dev/null
+++ b/flang/test/Lower/select-type.f90
@@ -0,0 +1,177 @@
+! RUN: bbc -polymorphic-type -emit-fir %s -o - | FileCheck %s
+
+module select_type_lower_test
+ type p1
+ integer :: a
+ integer :: b
+ end type
+
+ type, extends(p1) :: p2
+ integer :: c
+ end type
+
+ type, extends(p1) :: p3(k)
+ integer, kind :: k
+ real(k) :: r
+ end type
+
+contains
+
+ function get_class()
+ class(p1), pointer :: get_class
+ end function
+
+ subroutine select_type1(a)
+ class(p1), intent(in) :: a
+
+ select type (a)
+ type is (p1)
+ print*, 'type is p1'
+ class is (p1)
+ print*, 'class is p1'
+ class is (p2)
+ print*, 'class is p2', a%c
+ class default
+ print*,'default'
+ end select
+ end subroutine
+
+! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type1(
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"})
+
+! CHECK: fir.select_type %[[ARG0]] : !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
+! CHECK-SAME: [#fir.type_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[TYPE_IS_BLK:.*]], #fir.class_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[CLASS_IS_P1_BLK:.*]], #fir.class_is<!fir.type<_QMselect_type_lower_testTp2{a:i32,b:i32,c:i32}>>, ^[[CLASS_IS_P2_BLK:.*]], unit, ^[[DEFAULT_BLOCK:.*]]]
+! CHECK: ^[[TYPE_IS_BLK]]
+! CHECK: ^[[CLASS_IS_P1_BLK]]
+! CHECK: ^[[CLASS_IS_P2_BLK]]
+! CHECK: %[[P2:.*]] = fir.convert %[[ARG0:.*]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.class<!fir.type<_QMselect_type_lower_testTp2{a:i32,b:i32,c:i32}>>
+! CHECK: %[[FIELD:.*]] = fir.field_index c, !fir.type<_QMselect_type_lower_testTp2{a:i32,b:i32,c:i32}>
+! CHECK: %{{.*}} = fir.coordinate_of %[[P2]], %[[FIELD]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp2{a:i32,b:i32,c:i32}>>, !fir.field) -> !fir.ref<i32>
+! CHECK: ^[[DEFAULT_BLOCK]]
+
+ subroutine select_type2()
+ select type (a => get_class())
+ type is (p1)
+ print*, 'type is p1'
+ class is (p1)
+ print*, 'class is p1'
+ class default
+ print*,'default'
+ end select
+ end subroutine
+
+! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type2()
+! CHECK: %[[RESULT:.*]] = fir.alloca !fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>> {bindc_name = ".result"}
+! CHECK: %[[FCTCALL:.*]] = fir.call @_QMselect_type_lower_testPget_class() : () -> !fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>
+! CHECK: fir.save_result %[[FCTCALL]] to %[[RESULT]] : !fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>, !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>
+! CHECK: %[[SELECTOR:.*]] = fir.load %[[RESULT]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>
+! CHECK: fir.select_type %[[SELECTOR]] : !fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>
+! CHECK-SAME: [#fir.type_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[TYPE_IS_BLK:.*]], #fir.class_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[CLASS_IS_BLK:.*]], unit, ^[[DEFAULT_BLK:.*]]]
+! CHECK: ^[[TYPE_IS_BLK]]
+! CHECK: ^[[CLASS_IS_BLK]]
+! CHECK: ^[[DEFAULT_BLK]]
+
+ subroutine select_type3(a)
+ class(p1), pointer, intent(in) :: a(:)
+
+ select type (x => a(1))
+ type is (p1)
+ print*, 'type is p1'
+ class is (p1)
+ print*, 'class is p1'
+ class default
+ print*,'default'
+ end select
+ end subroutine
+
+! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type3(
+! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>> {fir.bindc_name = "a"})
+! CHECK: %[[ARG0_LOAD:.*]] = fir.load %[[ARG0]] : !fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>>
+! CHECK: %[[COORD:.*]] = fir.coordinate_of %[[ARG0_LOAD]], %{{.*}} : (!fir.class<!fir.ptr<!fir.array<?x!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>, i64) -> !fir.ref<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
+! CHECK: %[[TDESC:.*]] = fir.box_tdesc %[[ARG0_LOAD]] : (!fir.class<!fir.ptr<!fir.array<?x!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>) -> !fir.tdesc<none>
+! CHECK: %[[SELECTOR:.*]] = fir.embox %[[COORD]] tdesc %[[TDESC]] : (!fir.ref<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, !fir.tdesc<none>) -> !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
+! CHECK: fir.select_type %[[SELECTOR]] : !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
+! CHECK-SAME: [#fir.type_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[TYPE_IS_BLK:.*]], #fir.class_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[CLASS_IS_BLK:.*]], unit, ^[[DEFAULT_BLK:.*]]]
+! CHECK: ^[[TYPE_IS_BLK]]
+! CHECK: ^[[CLASS_IS_BLK]]
+! CHECK: ^[[DEFAULT_BLK]]
+
+ subroutine select_type4(a)
+ class(p1), intent(in) :: a
+ select type(a)
+ type is(p3(8))
+ print*, 'type is p3(8)'
+ type is(p3(4))
+ print*, 'type is p3(4)'
+ class is (p1)
+ print*, 'class is p1'
+ end select
+ end subroutine
+
+! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type4(
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"})
+! CHECK: fir.select_type %[[ARG0]] : !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
+! CHECK-SAME: [#fir.type_is<!fir.type<_QMselect_type_lower_testTp3K8{a:i32,b:i32,r:f64}>>, ^[[P3_8:.*]], #fir.type_is<!fir.type<_QMselect_type_lower_testTp3K4{a:i32,b:i32,r:f32}>>, ^[[P3_4:.*]], #fir.class_is<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, ^[[P1:.*]], unit, ^[[EXIT:.*]]]
+! CHECK: ^[[P3_8]]
+! CHECK: ^[[P3_4]]
+! CHECK: ^[[P1]]
+! CHECK: ^[[EXIT]]
+
+ subroutine select_type5(a)
+ class(*), intent(in) :: a
+
+ select type (x => a)
+ type is (integer(1))
+ print*, 'type is integer(1)'
+ type is (integer(4))
+ print*, 'type is integer(4)'
+ type is (real(4))
+ print*, 'type is real'
+ type is (logical)
+ print*, 'type is logical'
+ class default
+ print*,'default'
+ end select
+ end subroutine
+
+! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type5(
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a"})
+! CHECK: fir.select_type %[[ARG0]] : !fir.class<none>
+! CHECK-SAME: [#fir.type_is<i8>, ^[[I8_BLK:.*]], #fir.type_is<i32>, ^[[I32_BLK:.*]], #fir.type_is<f32>, ^[[F32_BLK:.*]], #fir.type_is<!fir.logical<4>>, ^[[LOG_BLK:.*]], unit, ^[[DEFAULT:.*]]]
+! CHECK: ^[[I8_BLK]]
+! CHECK: ^[[I32_BLK]]
+! CHECK: ^[[F32_BLK]]
+! CHECK: ^[[LOG_BLK]]
+! CHECK: ^[[DEFAULT_BLOCK]]
+
+
+ subroutine select_type6(a)
+ class(*), intent(out) :: a
+
+ select type(a)
+ type is (integer)
+ a = 100
+ type is (real)
+ a = 2.0
+ class default
+ stop 'error'
+ end select
+ end subroutine
+
+! CHECK-LABEL: func.func @_QMselect_type_lower_testPselect_type6(
+! CHECK-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a"})
+
+! CHECK: fir.select_type %[[ARG0]] : !fir.class<none> [#fir.type_is<i32>, ^[[INT_BLK:.*]], #fir.type_is<f32>, ^[[REAL_BLK:.*]], unit, ^[[DEFAULT_BLK:.*]]]
+! CHECK: ^[[INT_BLK]]
+! CHECK: %[[BOX_ADDR:.*]] = fir.box_addr %[[ARG0]] : (!fir.class<none>) -> !fir.ref<i32>
+! CHECK: %[[C100:.*]] = arith.constant 100 : i32
+! CHECK: fir.store %[[C100]] to %[[BOX_ADDR]] : !fir.ref<i32>
+
+! CHECK: ^[[REAL_BLK]]: // pred: ^bb0
+! CHECK: %[[BOX_ADDR:.*]] = fir.box_addr %[[ARG0]] : (!fir.class<none>) -> !fir.ref<f32>
+! CHECK: %[[C2:.*]] = arith.constant 2.000000e+00 : f32
+! CHECK: fir.store %[[C2]] to %[[BOX_ADDR]] : !fir.ref<f32>
+
+end module
+
+
More information about the flang-commits
mailing list