Privatisation creates local copies of variables in the OpenMP region.
Two functions `createHostAssociateVarClone` and `copyHostAssociateVar`
are added to create a clone of the variable for basic privatisation and to
copy the contents for first-privatisation.
Note: Tests for more data-types will be added when the fir.do_loop is
upstreamed.
This is part of the upstreaming effort from the fir-dev branch in [1].
[1] https://github.com/flang-compiler/f18-llvm-project
Reviewed By: peixin, NimishMishra
Differential Revision: https://reviews.llvm.org/D122595
Co-authored-by: Jean Perier <jperier@nvidia.com>
Co-authored-by: Eric Schweitz <eschweitz@nvidia.com>
Co-authored-by: Peter Klausler <pklausler@nvidia.com>
Co-authored-by: Valentin Clement <clementval@gmail.com>
Co-authored-by: Sourabh Singh Tomar <SourabhSingh.Tomar@amd.com>
Co-authored-by: Nimish Mishra <neelam.nimish@gmail.com>
Co-authored-by: Peixin-Qiao <qiaopeixin@huawei.com>
/// Get the code defined by a label
virtual pft::Evaluation *lookupLabel(pft::Label label) = 0;
+ /// For a given symbol which is host-associated, create a clone using
+ /// parameters from the host-associated symbol.
+ virtual bool
+ createHostAssociateVarClone(const Fortran::semantics::Symbol &sym) = 0;
+
+ virtual void copyHostAssociateVar(const Fortran::semantics::Symbol &sym) = 0;
+
//===--------------------------------------------------------------------===//
// Expressions
//===--------------------------------------------------------------------===//
llvm::None);
}
+ bool createHostAssociateVarClone(
+ const Fortran::semantics::Symbol &sym) override final {
+ mlir::Location loc = genLocation(sym.name());
+ mlir::Type symType = genType(sym);
+ const auto *details = sym.detailsIf<Fortran::semantics::HostAssocDetails>();
+ assert(details && "No host-association found");
+ const Fortran::semantics::Symbol &hsym = details->symbol();
+ Fortran::lower::SymbolBox hsb = lookupSymbol(hsym);
+
+ auto allocate = [&](llvm::ArrayRef<mlir::Value> shape,
+ llvm::ArrayRef<mlir::Value> typeParams) -> mlir::Value {
+ mlir::Value allocVal = builder->allocateLocal(
+ loc, symType, mangleName(sym), toStringRef(sym.GetUltimate().name()),
+ /*pinned=*/true, shape, typeParams,
+ sym.GetUltimate().attrs().test(Fortran::semantics::Attr::TARGET));
+ return allocVal;
+ };
+
+ fir::ExtendedValue hexv = getExtendedValue(hsb);
+ fir::ExtendedValue exv = hexv.match(
+ [&](const fir::BoxValue &box) -> fir::ExtendedValue {
+ const Fortran::semantics::DeclTypeSpec *type = sym.GetType();
+ if (type && type->IsPolymorphic())
+ TODO(loc, "create polymorphic host associated copy");
+ // Create a contiguous temp with the same shape and length as
+ // the original variable described by a fir.box.
+ llvm::SmallVector<mlir::Value> extents =
+ fir::factory::getExtents(*builder, loc, hexv);
+ if (box.isDerivedWithLengthParameters())
+ TODO(loc, "get length parameters from derived type BoxValue");
+ if (box.isCharacter()) {
+ mlir::Value len = fir::factory::readCharLen(*builder, loc, box);
+ mlir::Value temp = allocate(extents, {len});
+ return fir::CharArrayBoxValue{temp, len, extents};
+ }
+ return fir::ArrayBoxValue{allocate(extents, {}), extents};
+ },
+ [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
+ // Allocate storage for a pointer/allocatble descriptor.
+ // No shape/lengths to be passed to the alloca.
+ return fir::MutableBoxValue(allocate({}, {}),
+ box.nonDeferredLenParams(), {});
+ },
+ [&](const auto &) -> fir::ExtendedValue {
+ mlir::Value temp =
+ allocate(fir::factory::getExtents(*builder, loc, hexv),
+ fir::getTypeParams(hexv));
+ return fir::substBase(hexv, temp);
+ });
+
+ return bindIfNewSymbol(sym, exv);
+ }
+
+ void
+ copyHostAssociateVar(const Fortran::semantics::Symbol &sym) override final {
+ // 1) Fetch the original copy of the variable.
+ assert(sym.has<Fortran::semantics::HostAssocDetails>() &&
+ "No host-association found");
+ const Fortran::semantics::Symbol &hsym = sym.GetUltimate();
+ Fortran::lower::SymbolBox hsb = lookupSymbol(hsym);
+ fir::ExtendedValue hexv = getExtendedValue(hsb);
+
+ // 2) Create a copy that will mask the original.
+ createHostAssociateVarClone(sym);
+ Fortran::lower::SymbolBox sb = lookupSymbol(sym);
+ fir::ExtendedValue exv = getExtendedValue(sb);
+
+ // 3) Perform the assignment.
+ mlir::Location loc = genLocation(sym.name());
+ mlir::Type symType = genType(sym);
+ if (auto seqTy = symType.dyn_cast<fir::SequenceType>()) {
+ Fortran::lower::StatementContext stmtCtx;
+ Fortran::lower::createSomeArrayAssignment(*this, exv, hexv, localSymbols,
+ stmtCtx);
+ stmtCtx.finalize();
+ } else if (hexv.getBoxOf<fir::CharBoxValue>()) {
+ fir::factory::CharacterExprHelper{*builder, loc}.createAssign(exv, hexv);
+ } else if (hexv.getBoxOf<fir::MutableBoxValue>()) {
+ TODO(loc, "firstprivatisation of allocatable variables");
+ } else {
+ auto loadVal = builder->create<fir::LoadOp>(loc, fir::getBase(hexv));
+ builder->create<fir::StoreOp>(loc, loadVal, fir::getBase(exv));
+ }
+ }
+
+ //===--------------------------------------------------------------------===//
+ // Utility methods
+ //===--------------------------------------------------------------------===//
+
mlir::Location getCurrentLocation() override final { return toLocation(); }
/// Generate a dummy location.
return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr;
}
-static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
- Fortran::lower::AbstractConverter &converter,
- SmallVectorImpl<Value> &operands) {
- for (const auto &ompObject : objectList.v) {
+template <typename T>
+static void createPrivateVarSyms(Fortran::lower::AbstractConverter &converter,
+ const T *clause) {
+ Fortran::semantics::Symbol *sym = nullptr;
+ const Fortran::parser::OmpObjectList &ompObjectList = clause->v;
+ for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
std::visit(
Fortran::common::visitors{
[&](const Fortran::parser::Designator &designator) {
- if (const auto *name = getDesignatorNameIfDataRef(designator)) {
- const auto variable = converter.getSymbolAddress(*name->symbol);
- operands.push_back(variable);
+ if (const Fortran::parser::Name *name =
+ getDesignatorNameIfDataRef(designator)) {
+ sym = name->symbol;
}
},
- [&](const Fortran::parser::Name &name) {
- const auto variable = converter.getSymbolAddress(*name.symbol);
- operands.push_back(variable);
- }},
+ [&](const Fortran::parser::Name &name) { sym = name.symbol; }},
ompObject.u);
+
+ // Privatization for symbols which are pre-determined (like loop index
+ // variables) happen separately, for everything else privatize here
+ if constexpr (std::is_same_v<T, Fortran::parser::OmpClause::Firstprivate>) {
+ converter.copyHostAssociateVar(*sym);
+ } else {
+ bool success = converter.createHostAssociateVarClone(*sym);
+ (void)success;
+ assert(success && "Privatization failed due to existing binding");
+ }
+ }
+}
+
+static void privatizeVars(Fortran::lower::AbstractConverter &converter,
+ const Fortran::parser::OmpClauseList &opClauseList) {
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+ auto insPt = firOpBuilder.saveInsertionPoint();
+ firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
+ for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
+ if (const auto &privateClause =
+ std::get_if<Fortran::parser::OmpClause::Private>(&clause.u)) {
+ createPrivateVarSyms(converter, privateClause);
+ } else if (const auto &firstPrivateClause =
+ std::get_if<Fortran::parser::OmpClause::Firstprivate>(
+ &clause.u)) {
+ createPrivateVarSyms(converter, firstPrivateClause);
+ }
+ }
+ firOpBuilder.restoreInsertionPoint(insPt);
+}
+
+static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
+ Fortran::lower::AbstractConverter &converter,
+ llvm::SmallVectorImpl<Value> &operands) {
+ auto addOperands = [&](Fortran::lower::SymbolRef sym) {
+ const mlir::Value variable = converter.getSymbolAddress(sym);
+ if (variable) {
+ operands.push_back(variable);
+ } else {
+ if (const auto *details =
+ sym->detailsIf<Fortran::semantics::HostAssocDetails>()) {
+ operands.push_back(converter.getSymbolAddress(details->symbol()));
+ converter.copySymbolBinding(details->symbol(), sym);
+ }
+ }
+ };
+ for (const Fortran::parser::OmpObject &ompObject : objectList.v) {
+ std::visit(Fortran::common::visitors{
+ [&](const Fortran::parser::Designator &designator) {
+ if (const Fortran::parser::Name *name =
+ getDesignatorNameIfDataRef(designator)) {
+ addOperands(*name->symbol);
+ }
+ },
+ [&](const Fortran::parser::Name &name) {
+ addOperands(*name.symbol);
+ }},
+ ompObject.u);
}
}
template <typename Op>
-static void createBodyOfOp(Op &op, fir::FirOpBuilder &firOpBuilder,
- mlir::Location &loc) {
+static void
+createBodyOfOp(Op &op, Fortran::lower::AbstractConverter &converter,
+ mlir::Location &loc,
+ const Fortran::parser::OmpClauseList *clauses = nullptr,
+ bool outerCombined = false) {
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
firOpBuilder.createBlock(&op.getRegion());
auto &block = op.getRegion().back();
firOpBuilder.setInsertionPointToStart(&block);
firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
// Reset the insertion point to the start of the first block.
firOpBuilder.setInsertionPointToStart(&block);
+ // Handle privatization. Do not privatize if this is the outer operation.
+ if (clauses && !outerCombined)
+ privatizeVars(converter, *clauses);
}
static void genOMP(Fortran::lower::AbstractConverter &converter,
std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
const auto &endBlockDirective =
std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t);
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+ mlir::Location currentLocation = converter.getCurrentLocation();
- auto &firOpBuilder = converter.getFirOpBuilder();
- auto currentLocation = converter.getCurrentLocation();
Fortran::lower::StatementContext stmtCtx;
llvm::ArrayRef<mlir::Type> argTy;
mlir::Value ifClauseOperand, numThreadsClauseOperand;
SmallVector<Value> allocateOperands, allocatorOperands;
mlir::UnitAttr nowaitAttr;
- for (const auto &clause :
- std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t).v) {
+ const auto &opClauseList =
+ std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
+ for (const auto &clause : opClauseList.v) {
if (const auto &ifClause =
std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
&clause.u)) {
genAllocateClause(converter, allocateClause->v, allocatorOperands,
allocateOperands);
- } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u)) {
- // TODO: Handle private. This cannot be a hard TODO because testing for
- // allocate clause requires private variables.
+ } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) ||
+ std::get_if<Fortran::parser::OmpClause::Firstprivate>(
+ &clause.u)) {
+ // Privatisation clauses are handled elsewhere.
+ continue;
} else {
TODO(currentLocation, "OpenMP Block construct clauses");
}
currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
/*reductions=*/nullptr, procBindKindAttr);
- createBodyOfOp<omp::ParallelOp>(parallelOp, firOpBuilder, currentLocation);
+ createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation,
+ &opClauseList, /*isCombined=*/false);
} else if (blockDirective.v == llvm::omp::OMPD_master) {
auto masterOp =
firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
- createBodyOfOp<omp::MasterOp>(masterOp, firOpBuilder, currentLocation);
+ createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation);
} else if (blockDirective.v == llvm::omp::OMPD_single) {
auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>(
currentLocation, allocateOperands, allocatorOperands, nowaitAttr);
- createBodyOfOp(singleOp, firOpBuilder, currentLocation);
+ createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation);
+ } else {
+ TODO(converter.getCurrentLocation(), "Unhandled block directive");
}
}
firOpBuilder.getContext(), global.sym_name()));
}
}();
- createBodyOfOp<omp::CriticalOp>(criticalOp, firOpBuilder, currentLocation);
+ createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation);
}
static void
auto currentLocation = converter.getCurrentLocation();
mlir::omp::SectionOp sectionOp =
firOpBuilder.create<mlir::omp::SectionOp>(currentLocation);
- createBodyOfOp<omp::SectionOp>(sectionOp, firOpBuilder, currentLocation);
+ createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation);
}
// TODO: Add support for reduction
currentLocation, /*if_expr_var*/ nullptr, /*num_threads_var*/ nullptr,
allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
/*reductions=*/nullptr, /*proc_bind_val*/ nullptr);
- createBodyOfOp(parallelOp, firOpBuilder, currentLocation);
+ createBodyOfOp(parallelOp, converter, currentLocation);
auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
currentLocation, /*reduction_vars*/ ValueRange(),
/*reductions=*/nullptr, /*allocate_vars*/ ValueRange(),
/*allocators_vars*/ ValueRange(), /*nowait=*/nullptr);
- createBodyOfOp(sectionsOp, firOpBuilder, currentLocation);
+ createBodyOfOp(sectionsOp, converter, currentLocation);
// Sections Construct
} else if (dir == llvm::omp::Directive::OMPD_sections) {
auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
currentLocation, reductionVars, /*reductions = */ nullptr,
allocateOperands, allocatorOperands, noWaitClauseOperand);
- createBodyOfOp<omp::SectionsOp>(sectionsOp, firOpBuilder, currentLocation);
+ createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation);
}
}
/// Get the block for adding Allocas.
mlir::Block *fir::FirOpBuilder::getAllocaBlock() {
- // auto iface =
- // getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>();
- // return iface ? iface.getAllocaBlock() : getEntryBlock();
- return getEntryBlock();
+ auto iface =
+ getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>();
+ return iface ? iface.getAllocaBlock() : getEntryBlock();
}
/// Create a temporary variable on the stack. Anonymous temporaries have no
--- /dev/null
+! This test checks lowering of `FIRSTPRIVATE` clause for scalar types.
+
+! REQUIRES: shell
+! RUN: bbc -fopenmp -emit-fir %s -o - | FileCheck %s --check-prefix=FIRDialect
+
+!FIRDialect: func @_QPfirstprivate_complex(%[[ARG1:.*]]: !fir.ref<!fir.complex<4>>{{.*}}, %[[ARG2:.*]]: !fir.ref<!fir.complex<8>>{{.*}}) {
+!FIRDialect-DAG: omp.parallel {
+!FIRDialect-DAG: %[[ARG1_PVT:.*]] = fir.alloca !fir.complex<4> {bindc_name = "arg1", pinned, uniq_name = "_QFfirstprivate_complexEarg1"}
+!FIRDialect-DAG: %[[ARG1_VAL:.*]] = fir.load %arg0 : !fir.ref<!fir.complex<4>>
+!FIRDialect-DAG: fir.store %[[ARG1_VAL]] to %[[ARG1_PVT]] : !fir.ref<!fir.complex<4>>
+!FIRDialect-DAG: %[[ARG2_PVT:.*]] = fir.alloca !fir.complex<8> {bindc_name = "arg2", pinned, uniq_name = "_QFfirstprivate_complexEarg2"}
+!FIRDialect-DAG: %[[ARG2_VAL:.*]] = fir.load %[[ARG2]] : !fir.ref<!fir.complex<8>>
+!FIRDialect-DAG: fir.store %[[ARG2_VAL]] to %[[ARG2_PVT]] : !fir.ref<!fir.complex<8>>
+!FIRDialect-DAG: %[[LIST_IO:.*]] = fir.call @_FortranAioBeginExternalListOutput
+!FIRDialect-DAG: %[[ARG1_PVT_VAL:.*]] = fir.load %[[ARG1_PVT]] : !fir.ref<!fir.complex<4>>
+!FIRDialect-DAG: %[[ARG1_PVT_REAL:.*]] = fir.extract_value %[[ARG1_PVT_VAL]], [0 : index] : (!fir.complex<4>) -> f32
+!FIRDialect-DAG: %[[ARG1_PVT_IMAG:.*]] = fir.extract_value %[[ARG1_PVT_VAL]], [1 : index] : (!fir.complex<4>) -> f32
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputComplex32(%[[LIST_IO]], %[[ARG1_PVT_REAL]], %[[ARG1_PVT_IMAG]]) : (!fir.ref<i8>, f32, f32) -> i1
+!FIRDialect-DAG: %[[ARG2_PVT_VAL:.*]] = fir.load %[[ARG2_PVT]] : !fir.ref<!fir.complex<8>>
+!FIRDialect-DAG: %[[ARG2_PVT_REAL:.*]] = fir.extract_value %[[ARG2_PVT_VAL]], [0 : index] : (!fir.complex<8>) -> f64
+!FIRDialect-DAG: %[[ARG2_PVT_IMAG:.*]] = fir.extract_value %[[ARG2_PVT_VAL]], [1 : index] : (!fir.complex<8>) -> f64
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputComplex64(%[[LIST_IO]], %[[ARG2_PVT_REAL]], %[[ARG2_PVT_IMAG]]) : (!fir.ref<i8>, f64, f64) -> i1
+!FIRDialect-DAG: omp.terminator
+!FIRDialect-DAG: }
+
+subroutine firstprivate_complex(arg1, arg2)
+ complex(4) :: arg1
+ complex(8) :: arg2
+
+!$OMP PARALLEL FIRSTPRIVATE(arg1, arg2)
+ print *, arg1, arg2
+!$OMP END PARALLEL
+
+end subroutine
+
+!FIRDialect: func @_QPfirstprivate_integer(%[[ARG1:.*]]: !fir.ref<i32>{{.*}}, %[[ARG2:.*]]: !fir.ref<i8>{{.*}}, %[[ARG3:.*]]: !fir.ref<i16>{{.*}}, %[[ARG4:.*]]: !fir.ref<i32>{{.*}}, %[[ARG5:.*]]: !fir.ref<i64>{{.*}}, %[[ARG6:.*]]: !fir.ref<i128>{{.*}}) {
+!FIRDialect-DAG: omp.parallel {
+!FIRDialect-DAG: %[[ARG1_PVT:.*]] = fir.alloca i32 {bindc_name = "arg1", pinned, uniq_name = "_QFfirstprivate_integerEarg1"}
+!FIRDialect-DAG: %[[ARG1_VAL:.*]] = fir.load %[[ARG1]] : !fir.ref<i32>
+!FIRDialect-DAG: fir.store %[[ARG1_VAL]] to %[[ARG1_PVT]] : !fir.ref<i32>
+!FIRDialect-DAG: %[[ARG2_PVT:.*]] = fir.alloca i8 {bindc_name = "arg2", pinned, uniq_name = "_QFfirstprivate_integerEarg2"}
+!FIRDialect-DAG: %[[ARG2_VAL:.*]] = fir.load %[[ARG2]] : !fir.ref<i8>
+!FIRDialect-DAG: fir.store %[[ARG2_VAL]] to %[[ARG2_PVT]] : !fir.ref<i8>
+!FIRDialect-DAG: %[[ARG3_PVT:.*]] = fir.alloca i16 {bindc_name = "arg3", pinned, uniq_name = "_QFfirstprivate_integerEarg3"}
+!FIRDialect-DAG: %[[ARG3_VAL:.*]] = fir.load %[[ARG3]] : !fir.ref<i16>
+!FIRDialect-DAG: fir.store %[[ARG3_VAL]] to %[[ARG3_PVT]] : !fir.ref<i16>
+!FIRDialect-DAG: %[[ARG4_PVT:.*]] = fir.alloca i32 {bindc_name = "arg4", pinned, uniq_name = "_QFfirstprivate_integerEarg4"}
+!FIRDialect-DAG: %[[ARG4_VAL:.*]] = fir.load %[[ARG4]] : !fir.ref<i32>
+!FIRDialect-DAG: fir.store %[[ARG4_VAL]] to %[[ARG4_PVT]] : !fir.ref<i32>
+!FIRDialect-DAG: %[[ARG5_PVT:.*]] = fir.alloca i64 {bindc_name = "arg5", pinned, uniq_name = "_QFfirstprivate_integerEarg5"}
+!FIRDialect-DAG: %[[ARG5_VAL:.*]] = fir.load %[[ARG5]] : !fir.ref<i64>
+!FIRDialect-DAG: fir.store %[[ARG5_VAL]] to %[[ARG5_PVT]] : !fir.ref<i64>
+!FIRDialect-DAG: %[[ARG6_PVT:.*]] = fir.alloca i128 {bindc_name = "arg6", pinned, uniq_name = "_QFfirstprivate_integerEarg6"}
+!FIRDialect-DAG: %[[ARG6_VAL:.*]] = fir.load %[[ARG6]] : !fir.ref<i128>
+!FIRDialect-DAG: fir.store %[[ARG6_VAL]] to %[[ARG6_PVT]] : !fir.ref<i128>
+!FIRDialect-DAG: %[[LIST_IO:.*]] = fir.call @_FortranAioBeginExternalListOutput
+!FIRDialect-DAG: %[[ARG1_PVT_VAL:.*]] = fir.load %[[ARG1_PVT]] : !fir.ref<i32>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputInteger32(%[[LIST_IO]], %[[ARG1_PVT_VAL]]) : (!fir.ref<i8>, i32) -> i1
+!FIRDialect-DAG: %[[ARG2_PVT_VAL:.*]] = fir.load %[[ARG2_PVT]] : !fir.ref<i8>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputInteger8(%[[LIST_IO]], %[[ARG2_PVT_VAL]]) : (!fir.ref<i8>, i8) -> i1
+!FIRDialect-DAG: %[[ARG3_PVT_VAL:.*]] = fir.load %[[ARG3_PVT]] : !fir.ref<i16>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputInteger16(%[[LIST_IO]], %[[ARG3_PVT_VAL]]) : (!fir.ref<i8>, i16) -> i1
+!FIRDialect-DAG: %[[ARG4_PVT_VAL:.*]] = fir.load %[[ARG4_PVT]] : !fir.ref<i32>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputInteger32(%[[LIST_IO]], %[[ARG4_PVT_VAL]]) : (!fir.ref<i8>, i32) -> i1
+!FIRDialect-DAG: %[[ARG5_PVT_VAL:.*]] = fir.load %[[ARG5_PVT]] : !fir.ref<i64>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputInteger64(%[[LIST_IO]], %[[ARG5_PVT_VAL]]) : (!fir.ref<i8>, i64) -> i1
+!FIRDialect-DAG: %[[ARG6_PVT_VAL:.*]] = fir.load %[[ARG6_PVT]] : !fir.ref<i128>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputInteger128(%[[LIST_IO]], %[[ARG6_PVT_VAL]]) : (!fir.ref<i8>, i128) -> i1
+!FIRDialect-DAG: omp.terminator
+!FIRDialect-DAG: }
+
+subroutine firstprivate_integer(arg1, arg2, arg3, arg4, arg5, arg6)
+ integer :: arg1
+ integer(kind=1) :: arg2
+ integer(kind=2) :: arg3
+ integer(kind=4) :: arg4
+ integer(kind=8) :: arg5
+ integer(kind=16) :: arg6
+
+!$OMP PARALLEL FIRSTPRIVATE(arg1, arg2, arg3, arg4, arg5, arg6)
+ print *, arg1, arg2, arg3, arg4, arg5, arg6
+!$OMP END PARALLEL
+
+end subroutine
+
+!FIRDialect: func @_QPfirstprivate_logical(%[[ARG1:.*]]: !fir.ref<!fir.logical<4>>{{.*}}, %[[ARG2:.*]]: !fir.ref<!fir.logical<1>>{{.*}}, %[[ARG3:.*]]: !fir.ref<!fir.logical<2>>{{.*}}, %[[ARG4:.*]]: !fir.ref<!fir.logical<4>>{{.*}}, %[[ARG5:.*]]: !fir.ref<!fir.logical<8>>{{.*}}) {
+!FIRDialect-DAG: omp.parallel {
+!FIRDialect-DAG: %[[ARG1_PVT:.*]] = fir.alloca !fir.logical<4> {bindc_name = "arg1", pinned, uniq_name = "_QFfirstprivate_logicalEarg1"}
+!FIRDialect-DAG: %[[ARG1_VAL:.*]] = fir.load %[[ARG1]] : !fir.ref<!fir.logical<4>>
+!FIRDialect-DAG: fir.store %[[ARG1_VAL]] to %[[ARG1_PVT]] : !fir.ref<!fir.logical<4>>
+!FIRDialect-DAG: %[[ARG2_PVT:.*]] = fir.alloca !fir.logical<1> {bindc_name = "arg2", pinned, uniq_name = "_QFfirstprivate_logicalEarg2"}
+!FIRDialect-DAG: %[[ARG2_VAL:.*]] = fir.load %[[ARG2]] : !fir.ref<!fir.logical<1>>
+!FIRDialect-DAG: fir.store %[[ARG2_VAL]] to %[[ARG2_PVT]] : !fir.ref<!fir.logical<1>>
+!FIRDialect-DAG: %[[ARG3_PVT:.*]] = fir.alloca !fir.logical<2> {bindc_name = "arg3", pinned, uniq_name = "_QFfirstprivate_logicalEarg3"}
+!FIRDialect-DAG: %[[ARG3_VAL:.*]] = fir.load %[[ARG3]] : !fir.ref<!fir.logical<2>>
+!FIRDialect-DAG: fir.store %[[ARG3_VAL]] to %[[ARG3_PVT]] : !fir.ref<!fir.logical<2>>
+!FIRDialect-DAG: %[[ARG4_PVT:.*]] = fir.alloca !fir.logical<4> {bindc_name = "arg4", pinned, uniq_name = "_QFfirstprivate_logicalEarg4"}
+!FIRDialect-DAG: %[[ARG4_VAL:.*]] = fir.load %[[ARG4]] : !fir.ref<!fir.logical<4>>
+!FIRDialect-DAG: fir.store %[[ARG4_VAL]] to %[[ARG4_PVT]] : !fir.ref<!fir.logical<4>>
+!FIRDialect-DAG: %[[ARG5_PVT:.*]] = fir.alloca !fir.logical<8> {bindc_name = "arg5", pinned, uniq_name = "_QFfirstprivate_logicalEarg5"}
+!FIRDialect-DAG: %[[ARG5_VAL:.*]] = fir.load %[[ARG5]] : !fir.ref<!fir.logical<8>>
+!FIRDialect-DAG: fir.store %[[ARG5_VAL]] to %[[ARG5_PVT]] : !fir.ref<!fir.logical<8>>
+!FIRDialect-DAG: %[[LIST_IO:.*]] = fir.call @_FortranAioBeginExternalListOutput
+!FIRDialect-DAG: %[[ARG1_PVT_VAL:.*]] = fir.load %[[ARG1_PVT]] : !fir.ref<!fir.logical<4>>
+!FIRDialect-DAG: %[[ARG1_PVT_CVT:.*]] = fir.convert %[[ARG1_PVT_VAL]] : (!fir.logical<4>) -> i1
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputLogical(%[[LIST_IO]], %[[ARG1_PVT_CVT]]) : (!fir.ref<i8>, i1) -> i1
+!FIRDialect-DAG: %[[ARG2_PVT_VAL:.*]] = fir.load %[[ARG2_PVT]] : !fir.ref<!fir.logical<1>>
+!FIRDialect-DAG: %[[ARG2_PVT_CVT:.*]] = fir.convert %[[ARG2_PVT_VAL]] : (!fir.logical<1>) -> i1
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputLogical(%[[LIST_IO]], %[[ARG2_PVT_CVT]]) : (!fir.ref<i8>, i1) -> i1
+!FIRDialect-DAG: %[[ARG3_PVT_VAL:.*]] = fir.load %[[ARG3_PVT]] : !fir.ref<!fir.logical<2>>
+!FIRDialect-DAG: %[[ARG3_PVT_CVT:.*]] = fir.convert %[[ARG3_PVT_VAL]] : (!fir.logical<2>) -> i1
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputLogical(%[[LIST_IO]], %[[ARG3_PVT_CVT]]) : (!fir.ref<i8>, i1) -> i1
+!FIRDialect-DAG: %[[ARG4_PVT_VAL:.*]] = fir.load %[[ARG4_PVT]] : !fir.ref<!fir.logical<4>>
+!FIRDialect-DAG: %[[ARG4_PVT_CVT:.*]] = fir.convert %[[ARG4_PVT_VAL]] : (!fir.logical<4>) -> i1
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputLogical(%[[LIST_IO]], %[[ARG4_PVT_CVT]]) : (!fir.ref<i8>, i1) -> i1
+!FIRDialect-DAG: %[[ARG5_PVT_VAL:.*]] = fir.load %[[ARG5_PVT]] : !fir.ref<!fir.logical<8>>
+!FIRDialect-DAG: %[[ARG5_PVT_CVT:.*]] = fir.convert %[[ARG5_PVT_VAL]] : (!fir.logical<8>) -> i1
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputLogical(%[[LIST_IO]], %[[ARG5_PVT_CVT]]) : (!fir.ref<i8>, i1) -> i1
+!FIRDialect-DAG: omp.terminator
+!FIRDialect-DAG: }
+
+subroutine firstprivate_logical(arg1, arg2, arg3, arg4, arg5)
+ logical :: arg1
+ logical(kind=1) :: arg2
+ logical(kind=2) :: arg3
+ logical(kind=4) :: arg4
+ logical(kind=8) :: arg5
+
+!$OMP PARALLEL FIRSTPRIVATE(arg1, arg2, arg3, arg4, arg5)
+ print *, arg1, arg2, arg3, arg4, arg5
+!$OMP END PARALLEL
+
+end subroutine
+
+!FIRDialect-DAG: func @_QPfirstprivate_real(%[[ARG1:.*]]: !fir.ref<f32>{{.*}}, %[[ARG2:.*]]: !fir.ref<f16>{{.*}}, %[[ARG3:.*]]: !fir.ref<f32>{{.*}}, %[[ARG4:.*]]: !fir.ref<f64>{{.*}}, %[[ARG5:.*]]: !fir.ref<f80>{{.*}}, %[[ARG6:.*]]: !fir.ref<f128>{{.*}}) {
+!FIRDialect-DAG: omp.parallel {
+!FIRDialect-DAG: %[[ARG1_PVT:.*]] = fir.alloca f32 {bindc_name = "arg1", pinned, uniq_name = "_QFfirstprivate_realEarg1"}
+!FIRDialect-DAG: %[[ARG1_VAL:.*]] = fir.load %[[ARG1]] : !fir.ref<f32>
+!FIRDialect-DAG: fir.store %[[ARG1_VAL]] to %[[ARG1_PVT]] : !fir.ref<f32>
+!FIRDialect-DAG: %[[ARG2_PVT:.*]] = fir.alloca f16 {bindc_name = "arg2", pinned, uniq_name = "_QFfirstprivate_realEarg2"}
+!FIRDialect-DAG: %[[ARG2_VAL:.*]] = fir.load %[[ARG2]] : !fir.ref<f16>
+!FIRDialect-DAG: fir.store %[[ARG2_VAL]] to %[[ARG2_PVT]] : !fir.ref<f16>
+!FIRDialect-DAG: %[[ARG3_PVT:.*]] = fir.alloca f32 {bindc_name = "arg3", pinned, uniq_name = "_QFfirstprivate_realEarg3"}
+!FIRDialect-DAG: %[[ARG3_VAL:.*]] = fir.load %[[ARG3]] : !fir.ref<f32>
+!FIRDialect-DAG: fir.store %[[ARG3_VAL]] to %[[ARG3_PVT]] : !fir.ref<f32>
+!FIRDialect-DAG: %[[ARG4_PVT:.*]] = fir.alloca f64 {bindc_name = "arg4", pinned, uniq_name = "_QFfirstprivate_realEarg4"}
+!FIRDialect-DAG: %[[ARG4_VAL:.*]] = fir.load %[[ARG4]] : !fir.ref<f64>
+!FIRDialect-DAG: fir.store %[[ARG4_VAL]] to %[[ARG4_PVT]] : !fir.ref<f64>
+!FIRDialect-DAG: %[[ARG5_PVT:.*]] = fir.alloca f80 {bindc_name = "arg5", pinned, uniq_name = "_QFfirstprivate_realEarg5"}
+!FIRDialect-DAG: %[[ARG5_VAL:.*]] = fir.load %[[ARG5]] : !fir.ref<f80>
+!FIRDialect-DAG: fir.store %[[ARG5_VAL]] to %[[ARG5_PVT]] : !fir.ref<f80>
+!FIRDialect-DAG: %[[ARG6_PVT:.*]] = fir.alloca f128 {bindc_name = "arg6", pinned, uniq_name = "_QFfirstprivate_realEarg6"}
+!FIRDialect-DAG: %[[ARG6_VAL:.*]] = fir.load %[[ARG6]] : !fir.ref<f128>
+!FIRDialect-DAG: fir.store %[[ARG6_VAL]] to %[[ARG6_PVT]] : !fir.ref<f128>
+!FIRDialect-DAG: %[[LIST_IO:.*]] = fir.call @_FortranAioBeginExternalListOutput
+!FIRDialect-DAG: %[[ARG1_PVT_VAL:.*]] = fir.load %[[ARG1_PVT]] : !fir.ref<f32>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputReal32(%[[LIST_IO]], %[[ARG1_PVT_VAL]]) : (!fir.ref<i8>, f32) -> i1
+!FIRDialect-DAG: %[[ARG2_PVT_VAL:.*]] = fir.embox %[[ARG2_PVT]] : (!fir.ref<f16>) -> !fir.box<f16>
+!FIRDialect-DAG: %[[ARG2_PVT_CVT:.*]] = fir.convert %[[ARG2_PVT_VAL]] : (!fir.box<f16>) -> !fir.box<none>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputDescriptor(%[[LIST_IO]], %[[ARG2_PVT_CVT]]) : (!fir.ref<i8>, !fir.box<none>) -> i1
+!FIRDialect-DAG: %[[ARG3_PVT_VAL:.*]] = fir.load %[[ARG3_PVT]] : !fir.ref<f32>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputReal32(%[[LIST_IO]], %[[ARG3_PVT_VAL]]) : (!fir.ref<i8>, f32) -> i1
+!FIRDialect-DAG: %[[ARG4_PVT_VAL:.*]] = fir.load %[[ARG4_PVT]] : !fir.ref<f64>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputReal64(%[[LIST_IO]], %[[ARG4_PVT_VAL]]) : (!fir.ref<i8>, f64) -> i1
+!FIRDialect-DAG: %[[ARG5_PVT_VAL:.*]] = fir.embox %[[ARG5_PVT]] : (!fir.ref<f80>) -> !fir.box<f80>
+!FIRDialect-DAG: %[[ARG5_PVT_CVT:.*]] = fir.convert %[[ARG5_PVT_VAL]] : (!fir.box<f80>) -> !fir.box<none>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputDescriptor(%[[LIST_IO]], %[[ARG5_PVT_CVT]]) : (!fir.ref<i8>, !fir.box<none>) -> i1
+!FIRDialect-DAG: %[[ARG6_PVT_VAL:.*]] = fir.embox %[[ARG6_PVT]] : (!fir.ref<f128>) -> !fir.box<f128>
+!FIRDialect-DAG: %[[ARG6_PVT_CVT:.*]] = fir.convert %[[ARG6_PVT_VAL]] : (!fir.box<f128>) -> !fir.box<none>
+!FIRDialect-DAG: %{{.*}} = fir.call @_FortranAioOutputDescriptor(%[[LIST_IO]], %[[ARG6_PVT_CVT]]) : (!fir.ref<i8>, !fir.box<none>) -> i1
+!FIRDialect-DAG: omp.terminator
+!FIRDialect-DAG: }
+
+subroutine firstprivate_real(arg1, arg2, arg3, arg4, arg5, arg6)
+ real :: arg1
+ real(kind=2) :: arg2
+ real(kind=4) :: arg3
+ real(kind=8) :: arg4
+ real(kind=10) :: arg5
+ real(kind=16) :: arg6
+
+!$OMP PARALLEL FIRSTPRIVATE(arg1, arg2, arg3, arg4, arg5, arg6)
+ print *, arg1, arg2, arg3, arg4, arg5, arg6
+!$OMP END PARALLEL
+
+end subroutine
--- /dev/null
+! This test checks lowering of OpenMP parallel Directive with
+! `PRIVATE` clause present.
+
+! REQUIRES: shell
+! RUN: bbc -fopenmp -emit-fir %s -o - | \
+! RUN: FileCheck %s --check-prefix=FIRDialect
+
+!FIRDialect: func @_QPprivate_clause(%[[ARG1:.*]]: !fir.ref<i32>{{.*}}, %[[ARG2:.*]]: !fir.ref<!fir.array<10xi32>>{{.*}}, %[[ARG3:.*]]: !fir.boxchar<1>{{.*}}, %[[ARG4:.*]]: !fir.boxchar<1>{{.*}}) {
+!FIRDialect-DAG: %[[ALPHA:.*]] = fir.alloca i32 {{{.*}}, uniq_name = "{{.*}}Ealpha"}
+!FIRDialect-DAG: %[[ALPHA_ARRAY:.*]] = fir.alloca !fir.array<10xi32> {{{.*}}, uniq_name = "{{.*}}Ealpha_array"}
+!FIRDialect-DAG: %[[BETA:.*]] = fir.alloca !fir.char<1,5> {{{.*}}, uniq_name = "{{.*}}Ebeta"}
+!FIRDialect-DAG: %[[BETA_ARRAY:.*]] = fir.alloca !fir.array<10x!fir.char<1,5>> {{{.*}}, uniq_name = "{{.*}}Ebeta_array"}
+
+!FIRDialect-DAG: omp.parallel {
+!FIRDialect-DAG: %[[ALPHA_PRIVATE:.*]] = fir.alloca i32 {{{.*}}, pinned, uniq_name = "{{.*}}Ealpha"}
+!FIRDialect-DAG: %[[ALPHA_ARRAY_PRIVATE:.*]] = fir.alloca !fir.array<10xi32> {{{.*}}, pinned, uniq_name = "{{.*}}Ealpha_array"}
+!FIRDialect-DAG: %[[BETA_PRIVATE:.*]] = fir.alloca !fir.char<1,5> {{{.*}}, pinned, uniq_name = "{{.*}}Ebeta"}
+!FIRDialect-DAG: %[[BETA_ARRAY_PRIVATE:.*]] = fir.alloca !fir.array<10x!fir.char<1,5>> {{{.*}}, pinned, uniq_name = "{{.*}}Ebeta_array"}
+!FIRDialect-DAG: %[[ARG1_PRIVATE:.*]] = fir.alloca i32 {{{.*}}, pinned, uniq_name = "{{.*}}Earg1"}
+!FIRDialect-DAG: %[[ARG2_ARRAY_PRIVATE:.*]] = fir.alloca !fir.array<10xi32> {{{.*}}, pinned, uniq_name = "{{.*}}Earg2"}
+!FIRDialect-DAG: %[[ARG3_PRIVATE:.*]] = fir.alloca !fir.char<1,5> {{{.*}}, pinned, uniq_name = "{{.*}}Earg3"}
+!FIRDialect-DAG: %[[ARG4_ARRAY_PRIVATE:.*]] = fir.alloca !fir.array<10x!fir.char<1,5>> {{{.*}}, pinned, uniq_name = "{{.*}}Earg4"}
+!FIRDialect: omp.terminator
+!FIRDialect: }
+
+subroutine private_clause(arg1, arg2, arg3, arg4)
+
+ integer :: arg1, arg2(10)
+ integer :: alpha, alpha_array(10)
+ character(5) :: arg3, arg4(10)
+ character(5) :: beta, beta_array(10)
+
+!$OMP PARALLEL PRIVATE(alpha, alpha_array, beta, beta_array, arg1, arg2, arg3, arg4)
+ alpha = 1
+ alpha_array = 4
+ beta = "hi"
+ beta_array = "hi"
+ arg1 = 2
+ arg2 = 3
+ arg3 = "world"
+ arg4 = "world"
+!$OMP END PARALLEL
+
+end subroutine
+
+!FIRDialect: func @_QPprivate_clause_scalar() {
+!FIRDialect-DAG: {{.*}} = fir.alloca !fir.complex<4> {bindc_name = "c", uniq_name = "{{.*}}Ec"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i8 {bindc_name = "i1", uniq_name = "{{.*}}Ei1"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i128 {bindc_name = "i16", uniq_name = "{{.*}}Ei16"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i16 {bindc_name = "i2", uniq_name = "{{.*}}Ei2"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i32 {bindc_name = "i4", uniq_name = "{{.*}}Ei4"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i64 {bindc_name = "i8", uniq_name = "{{.*}}Ei8"}
+!FIRDialect-DAG: {{.*}} = fir.alloca !fir.logical<4> {bindc_name = "l", uniq_name = "{{.*}}El"}
+!FIRDialect-DAG: {{.*}} = fir.alloca f32 {bindc_name = "r", uniq_name = "{{.*}}Er"}
+
+!FIRDialect: omp.parallel {
+!FIRDialect-DAG: {{.*}} = fir.alloca i8 {bindc_name = "i1", pinned, uniq_name = "{{.*}}Ei1"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i16 {bindc_name = "i2", pinned, uniq_name = "{{.*}}Ei2"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i32 {bindc_name = "i4", pinned, uniq_name = "{{.*}}Ei4"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i64 {bindc_name = "i8", pinned, uniq_name = "{{.*}}Ei8"}
+!FIRDialect-DAG: {{.*}} = fir.alloca i128 {bindc_name = "i16", pinned, uniq_name = "{{.*}}Ei16"}
+!FIRDialect-DAG: {{.*}} = fir.alloca !fir.complex<4> {bindc_name = "c", pinned, uniq_name = "{{.*}}Ec"}
+!FIRDialect-DAG: {{.*}} = fir.alloca !fir.logical<4> {bindc_name = "l", pinned, uniq_name = "{{.*}}El"}
+!FIRDialect-DAG: {{.*}} = fir.alloca f32 {bindc_name = "r", pinned, uniq_name = "{{.*}}Er"}
+
+subroutine private_clause_scalar()
+
+ integer(kind=1) :: i1
+ integer(kind=2) :: i2
+ integer(kind=4) :: i4
+ integer(kind=8) :: i8
+ integer(kind=16) :: i16
+ complex :: c
+ logical :: l
+ real :: r
+
+!$OMP PARALLEL PRIVATE(i1, i2, i4, i8, i16, c, l, r)
+ print *, i1, i2, i4, i8, i16, c, l, r
+!$OMP END PARALLEL
+
+end subroutine
+
+!FIRDialect: func @_QPprivate_clause_derived_type() {
+!FIRDialect: {{.*}} = fir.alloca !fir.type<{{.*}}{t_i:i32,t_arr:!fir.array<5xi32>}> {bindc_name = "t", uniq_name = "{{.*}}Et"}
+
+!FIRDialect: omp.parallel {
+!FIRDialect: {{.*}} = fir.alloca !fir.type<{{.*}}{t_i:i32,t_arr:!fir.array<5xi32>}> {bindc_name = "t", pinned, uniq_name = "{{.*}}Et"}
+
+subroutine private_clause_derived_type()
+
+ type my_type
+ integer :: t_i
+ integer :: t_arr(5)
+ end type my_type
+ type(my_type) :: t
+
+!$OMP PARALLEL PRIVATE(t)
+ print *, t%t_i
+!$OMP END PARALLEL
+
+end subroutine
+
+!FIRDialect: func @_QPprivate_clause_allocatable() {
+!FIRDialect-DAG: {{.*}} = fir.alloca !fir.box<!fir.heap<i32>> {bindc_name = "x", uniq_name = "{{.*}}Ex"}
+!FIRDialect-DAG: {{.*}} = fir.alloca !fir.heap<i32> {uniq_name = "{{.*}}Ex.addr"}
+!FIRDialect-DAG: {{.*}} = fir.alloca !fir.box<!fir.heap<!fir.array<?xi32>>> {bindc_name = "x2", uniq_name = "{{.*}}Ex2"}
+!FIRDialect-DAG: {{.*}} = fir.alloca !fir.heap<!fir.array<?xi32>> {uniq_name = "{{.*}}Ex2.addr"}
+!FIRDialect-DAG: {{.*}} = fir.address_of(@{{.*}}Ex3) : !fir.ref<!fir.box<!fir.heap<i32>>>
+!FIRDialect-DAG: [[TMP9:%.*]] = fir.address_of(@{{.*}}Ex4) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+
+!FIRDialect: omp.parallel {
+!FIRDialect-DAG: [[TMP37:%.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "{{.*}}Ex"}
+!FIRDialect-DAG: [[TMP40:%.*]] = fir.alloca !fir.array<?xi32>, {{.*}} {bindc_name = "x2", pinned, uniq_name = "{{.*}}Ex2"}
+!FIRDialect-DAG: [[TMP41:%.*]] = fir.alloca i32 {bindc_name = "x3", pinned, uniq_name = "{{.*}}Ex3"}
+!FIRDialect-DAG: [[TMP42:%.*]] = fir.load [[TMP9]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
+!FIRDialect-DAG: [[TMP43:%.*]]:3 = fir.box_dims [[TMP42]], {{.*}} : (!fir.box<!fir.heap<!fir.array<?xi32>>>, index) -> (index, index, index)
+!FIRDialect-DAG: [[TMP44:%.*]] = fir.alloca !fir.array<?xi32>, [[TMP43]]#1 {bindc_name = "x4", pinned, uniq_name = "{{.*}}Ex4"}
+!FIRDialect-DAG: [[TMP52:%.*]] = fir.embox [[TMP40]]({{.*}}) : (!fir.ref<!fir.array<?xi32>>, !fir.shapeshift<1>) -> !fir.box<!fir.array<?xi32>>
+!FIRDialect-DAG: {{.*}} = fir.convert [[TMP52]] : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
+!FIRDialect-DAG: [[TMP58:%.*]] = fir.shape_shift [[TMP43]]#0, [[TMP43]]#1 : (index, index) -> !fir.shapeshift<1>
+!FIRDialect-DAG: [[TMP59:%.*]] = fir.embox [[TMP44]]([[TMP58]]) : (!fir.ref<!fir.array<?xi32>>, !fir.shapeshift<1>) -> !fir.box<!fir.array<?xi32>>
+!FIRDialect-DAG: {{.*}} = fir.convert [[TMP59]] : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
+
+subroutine private_clause_allocatable()
+
+ integer, allocatable :: x, x2(:)
+ integer, allocatable, save :: x3, x4(:)
+
+ print *, x, x2, x3, x4
+
+!$OMP PARALLEL PRIVATE(x, x2, x3, x4)
+ print *, x, x2, x3, x4
+!$OMP END PARALLEL
+
+end subroutine