[flang-commits] [flang] [flang][MLIR][OpenMP] Extend delayed privatization for allocatables (PR #84033)

Kareem Ergawy via flang-commits flang-commits at lists.llvm.org
Tue Mar 5 07:51:26 PST 2024


https://github.com/ergawy updated https://github.com/llvm/llvm-project/pull/84033

>From 7d98b66a9eb6c22cafe8aff43fdefcf59f8fb2fa Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at amd.com>
Date: Fri, 1 Mar 2024 09:55:07 -0600
Subject: [PATCH] [flang][MLIR][OpenMP] Extend delayed privatization for
 allocatables

Adds delayed privatization support for allocatables. In order to explain
the problem this diff is trying to solve, it would be useful to see an
example of `firstprivate` for an alloctable and how it is currently
emitted by flang **without** delayed privatization.

Consider the following Fortran code:
```fortran
subroutine delayed_privatization_allocatable
  implicit none
  integer, allocatable :: var1

!$omp parallel firstprivate(var1)
  var1 = 10
!$omp end parallel
end subroutine
```

You would get something like this (again no delayed privatization yet):
```mlir
    ...
    %3:2 = hlfir.declare %0 {fortran_attrs = #fir.var_attrs<allocatable>, ...} : ...
    omp.parallel {
      // Allocation logic
      ...
      %9 = fir.load %3#1 : !fir.ref<!fir.box<!fir.heap<i32>>>
      ...
      fir.if %12 {
        ...
      } else {
        ...
      }
      %13:2 = hlfir.declare %8 {fortran_attrs = #fir.var_attrs<allocatable>, ...} : ...

      // Copy logic
      %14 = fir.load %13#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
      ...
      fir.if %17 {
        %24 = fir.load %3#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
        ...
      }
      ...
      omp.terminator
    }
```

Note that for both the original and the private declaration, the
allocation and copy logic use both elements (e.g. `%3#0` and `%3#1`).
This poses the following problem for delayed privatization: how can
capture both values in order to pass them to the outlined privatizer?
The main issue is that `hlfir.declare` returns 2 SSA values that not
encapsulated together under a single composite value.

Some possible solutions are:
1. Change the return type of `hlfir.declare`. However, this would be
   very invasive and therefore does not sound like a good idea.
2. Use the built-in `tuple` type to "pack" both values together. This is
   reasonable but not very flexible since we won't be able to express
   other information about the encapsulated values such as whether it is
   alloctable.
3. Introduce a new concept/type that allows us to do the encapsulation
   in a more flexible way. This is the "variable shadow" concept
   introduced by this PR.

A "variable shadow" is a composite value of a special type:
`!fir.shadow` that has some special properties:
* It can be constructed only from `BlockArgument`s.
* It always has the type `!fir.shadow`.
* It packs the required information needed by the delayed privatizer
  that correspond to the result of `hlfir.declare` operation.

We don't introduce any special opertions to deal with shadow values.
Rather we treat them similar to other composites. If you want to get a
certain component, use `fir.extract_value`. If you want to populate a
certain component, use `fir.insert_value`.
---
 flang/include/flang/Lower/SymbolMap.h         |  16 ++-
 .../flang/Optimizer/Builder/BoxValue.h        |  10 +-
 .../flang/Optimizer/Builder/HLFIRTools.h      |  31 ++++-
 .../flang/Optimizer/Builder/VariableShadow.h  |  47 ++++++++
 .../flang/Optimizer/Dialect/FIRTypes.td       |  65 +++++++++-
 flang/lib/Lower/Bridge.cpp                    |  15 +++
 flang/lib/Lower/ConvertExpr.cpp               |   3 +
 .../lib/Lower/OpenMP/DataSharingProcessor.cpp | 111 +++++++++++++++---
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  23 +++-
 flang/lib/Lower/SymbolMap.cpp                 |  22 ++--
 flang/lib/Optimizer/Builder/BoxValue.cpp      |  11 +-
 flang/lib/Optimizer/Builder/HLFIRTools.cpp    |  68 ++++++++++-
 flang/lib/Optimizer/Builder/MutableBox.cpp    |   6 +
 flang/lib/Optimizer/Dialect/FIRType.cpp       |   2 +-
 .../delayed-privatization-allocatable.f90     |  69 +++++++++++
 .../delayed-privatization-firstprivate.f90    |  34 ++++--
 ...yed-privatization-private-firstprivate.f90 |  16 ++-
 .../OpenMP/delayed-privatization-private.f90  |  25 +++-
 .../delayed-privatization-reduction.f90       |   6 +-
 19 files changed, 517 insertions(+), 63 deletions(-)
 create mode 100644 flang/include/flang/Optimizer/Builder/VariableShadow.h
 create mode 100644 flang/test/Lower/OpenMP/delayed-privatization-allocatable.f90

diff --git a/flang/include/flang/Lower/SymbolMap.h b/flang/include/flang/Lower/SymbolMap.h
index a55e4b133fe0a8..6c90a0ccc7e99b 100644
--- a/flang/include/flang/Lower/SymbolMap.h
+++ b/flang/include/flang/Lower/SymbolMap.h
@@ -15,6 +15,7 @@
 
 #include "flang/Common/reference.h"
 #include "flang/Optimizer/Builder/BoxValue.h"
+#include "flang/Optimizer/Builder/VariableShadow.h"
 #include "flang/Optimizer/Dialect/FIRType.h"
 #include "flang/Optimizer/Dialect/FortranVariableInterface.h"
 #include "flang/Optimizer/Support/Matcher.h"
@@ -77,7 +78,8 @@ struct SymbolBox : public fir::details::matcher<SymbolBox> {
 
   using VT =
       std::variant<Intrinsic, FullDim, Char, CharFullDim, PointerOrAllocatable,
-                   Box, fir::FortranVariableOpInterface, None>;
+                   Box, fir::FortranVariableOpInterface,
+                   hlfir::FortranVariableShadow, None>;
 
   //===--------------------------------------------------------------------===//
   // Constructors
@@ -97,11 +99,13 @@ struct SymbolBox : public fir::details::matcher<SymbolBox> {
   /// scalar. For an array, this is the address of the first element in the
   /// array, etc.
   mlir::Value getAddr() const {
-    return match([](const None &) { return mlir::Value{}; },
-                 [](const fir::FortranVariableOpInterface &x) {
-                   return fir::FortranVariableOpInterface(x).getBase();
-                 },
-                 [](const auto &x) { return x.getAddr(); });
+    return match(
+        [](const None &) { return mlir::Value{}; },
+        [](const fir::FortranVariableOpInterface &x) {
+          return fir::FortranVariableOpInterface(x).getBase();
+        },
+        [](const hlfir::FortranVariableShadow &x) { return x.getBase(); },
+        [](const auto &x) { return x.getAddr(); });
   }
 
   std::optional<fir::FortranVariableOpInterface>
diff --git a/flang/include/flang/Optimizer/Builder/BoxValue.h b/flang/include/flang/Optimizer/Builder/BoxValue.h
index 2fed2d48a7a080..151a197cc29b7f 100644
--- a/flang/include/flang/Optimizer/Builder/BoxValue.h
+++ b/flang/include/flang/Optimizer/Builder/BoxValue.h
@@ -13,6 +13,8 @@
 #ifndef FORTRAN_OPTIMIZER_BUILDER_BOXVALUE_H
 #define FORTRAN_OPTIMIZER_BUILDER_BOXVALUE_H
 
+#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/Builder/VariableShadow.h"
 #include "flang/Optimizer/Dialect/FIRType.h"
 #include "flang/Optimizer/Support/FatalError.h"
 #include "flang/Optimizer/Support/Matcher.h"
@@ -479,7 +481,8 @@ class ExtendedValue : public details::matcher<ExtendedValue> {
 public:
   using VT =
       std::variant<UnboxedValue, CharBoxValue, ArrayBoxValue, CharArrayBoxValue,
-                   ProcBoxValue, BoxValue, MutableBoxValue, PolymorphicValue>;
+                   ProcBoxValue, BoxValue, MutableBoxValue, PolymorphicValue,
+                   hlfir::FortranVariableShadow>;
 
   ExtendedValue() : box{UnboxedValue{}} {}
   template <typename A, typename = std::enable_if_t<
@@ -516,6 +519,11 @@ class ExtendedValue : public details::matcher<ExtendedValue> {
                  [](const fir::CharBoxValue &box) -> unsigned { return 0; },
                  [](const fir::ProcBoxValue &box) -> unsigned { return 0; },
                  [](const fir::PolymorphicValue &box) -> unsigned { return 0; },
+                 [](const hlfir::FortranVariableShadow &box) -> unsigned {
+                   TODO(box.getValue().getLoc(),
+                        "rank(): FortranVariableShadow");
+                   return 0;
+                 },
                  [](const auto &box) -> unsigned { return box.rank(); });
   }
 
diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
index 170e134baef614..1de6e6b2d340da 100644
--- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h
+++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
@@ -14,6 +14,7 @@
 #define FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
 
 #include "flang/Optimizer/Builder/BoxValue.h"
+#include "flang/Optimizer/Builder/VariableShadow.h"
 #include "flang/Optimizer/Dialect/FIROps.h"
 #include "flang/Optimizer/Dialect/FortranVariableInterface.h"
 #include "flang/Optimizer/HLFIR/HLFIRDialect.h"
@@ -55,8 +56,12 @@ class Entity : public mlir::Value {
   }
   Entity(fir::FortranVariableOpInterface variable)
       : mlir::Value(variable.getBase()) {}
+  Entity(FortranVariableShadow variableShadow)
+      : mlir::Value(variableShadow.getValue()) {}
+
   bool isValue() const { return isFortranValue(*this); }
   bool isVariable() const { return !isValue(); }
+  bool isMutableShadow() const;
   bool isMutableBox() const { return hlfir::isBoxAddressType(getType()); }
   bool isProcedurePointer() const {
     return fir::isBoxProcAddressType(getType());
@@ -74,6 +79,10 @@ class Entity : public mlir::Value {
   /// Is this an assumed ranked entity?
   bool isAssumedRank() const { return getRank() == -1; }
 
+  bool isFortranVariableShadow() const {
+    return this->getType().isa<fir::ShadowType>();
+  }
+
   /// Return the rank of this entity or -1 if it is an assumed rank.
   int getRank() const {
     mlir::Type type = fir::unwrapPassByRefType(fir::unwrapRefType(getType()));
@@ -152,6 +161,14 @@ class Entity : public mlir::Value {
     return this->getDefiningOp<fir::FortranVariableOpInterface>();
   }
 
+  fir::ShadowType getIfVariableShadow() const {
+    fir::ExtractValueOp op = this->getDefiningOp<fir::ExtractValueOp>();
+    if (!op)
+      return fir::ShadowType();
+
+    return op.getAdt().getType().dyn_cast<fir::ShadowType>();
+  }
+
   // Return a "declaration" operation for this variable if visible,
   // or the "declaration" operation of the allocatable/pointer this
   // variable was dereferenced from (if it is visible).
@@ -174,8 +191,13 @@ class Entity : public mlir::Value {
   }
 
   bool isAllocatable() const {
-    auto varIface = getIfVariableInterface();
-    return varIface ? varIface.isAllocatable() : false;
+    if (auto varIface = getIfVariableInterface())
+      return varIface.isAllocatable();
+
+    if (auto shadowType = getIfVariableShadow())
+      return shadowType.getAllocatable();
+
+    return false;
   }
 
   bool isPointer() const {
@@ -185,7 +207,7 @@ class Entity : public mlir::Value {
 
   // Get the entity as an mlir SSA value containing all the shape, type
   // parameters and dynamic shape information.
-  mlir::Value getBase() const { return *this; }
+  mlir::Value getBase() const;
 
   // Get the entity as a FIR base. This may not carry the shape and type
   // parameters information, and even when it is a box with shape information.
@@ -231,6 +253,9 @@ translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
 fir::ExtendedValue
 translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
                          fir::FortranVariableOpInterface fortranVariable);
+fir::ExtendedValue
+translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
+                         hlfir::FortranVariableShadow fortranVariableShadow);
 
 /// Generate declaration for a fir::ExtendedValue in memory.
 fir::FortranVariableOpInterface
diff --git a/flang/include/flang/Optimizer/Builder/VariableShadow.h b/flang/include/flang/Optimizer/Builder/VariableShadow.h
new file mode 100644
index 00000000000000..af83c4e34abb60
--- /dev/null
+++ b/flang/include/flang/Optimizer/Builder/VariableShadow.h
@@ -0,0 +1,47 @@
+//===-- VariableShadow.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
+#include "mlir/IR/Value.h"
+
+#ifndef FORTRAN_OPTIMIZER_BUILDER_VARIABLESHADOW_H
+#define FORTRAN_OPTIMIZER_BUILDER_VARIABLESHADOW_H
+
+namespace hlfir {
+
+class FortranVariableShadow {
+public:
+  FortranVariableShadow(fir::FirOpBuilder &builder,
+                        mlir::BlockArgument shadowingVal,
+                        fir::FortranVariableOpInterface shadowedVariable);
+
+  mlir::BlockArgument getValue() const { return shadowingVal; }
+
+  mlir::Value getBase() const { return base; }
+
+  mlir::Value getFirBase() const { return firBase; }
+
+  fir::FortranVariableOpInterface getShadowedVariable() const {
+    return shadowedVariable;
+  }
+
+private:
+  mlir::BlockArgument shadowingVal;
+  mlir::Value base;
+  mlir::Value firBase;
+  fir::FortranVariableOpInterface shadowedVariable;
+};
+
+} // namespace hlfir
+
+#endif // FORTRAN_OPTIMIZER_BUILDER_VARIABLESHADOW_H
diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
index 2a2f50720859e7..d83b216bce7b30 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td
@@ -576,6 +576,65 @@ def fir_VoidType : FIR_Type<"Void", "void"> {
   let genStorageClass = 0;
 }
 
+def fir_VariableShadowType : FIR_Type<"Shadow", "shadow"> {
+  let summary = "Type for Fortran-variables shadow values";
+
+  let description = [{
+    A type to encapulate required data for a Fortran-variable shadow value. A
+    shadow type encapsulates the base type as well as the original base type. It
+    also encapsulates information other information about the shadowed value,
+    such as whether it is allocatable or not.
+
+    For example, if we want to shadow a value produced a `hlfir.declare`
+    instruction similar to the following:
+    ```
+    %1:2 = hlfir.declare %0
+      {fortran_attrs = #fir.var_attrs<allocatable>}
+      : (!fir.ref<!fir.box<!fir.heap<i32>>>)
+        -> (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>)
+    ```
+
+    We would need to somehow create a value of the type:
+    ```
+    !fir.shadow<!fir.ref<!fir.box<!fir.heap<i32>>>,
+                !fir.ref<!fir.box<!fir.heap<i32>>>,
+                allocatable : true>
+    ```
+
+    The mechanism to create this value can be, for example, through a
+    combincation `fir.undefined`, `fir.insert_value` instructions similar to what
+    would be done for other composite types like `tuple`s:
+    ```
+    %2 = fir.undefined !fir.shadow<!fir.ref<!fir.box<!fir.heap<i32>>>,
+                                   !fir.ref<!fir.box<!fir.heap<i32>>>,
+                                   allocatable : true>
+    %3 = fir.insert_value %2, %1#0, [0 : index] : <... type info omitted ...>
+    %4 = fir.insert_value %3, %3#1, [1 : index] : <... type info omitted ...>
+    ```
+
+    From that point on, you can use `%4` as the shadow of the originally declared
+    variable being shadowed `%1`.
+
+    For more info about the concept of variable shadows, check `VariableShadow.h`.
+  }];
+
+  let parameters = (ins "mlir::Type":$baseTy,
+                        "mlir::Type":$firBaseTy,
+                        "bool":$allocatable);
+
+  let assemblyFormat = [{
+    `<` $baseTy `,` $firBaseTy `,` `allocatable` `:` $allocatable `>`
+  }];
+
+  let extraClassDeclaration = [{
+    mlir::Type getType(unsigned idx) const {
+      if (idx == 0) return getBaseTy();
+      if (idx == 1) return getFirBaseTy();
+      return mlir::Type();
+    }
+  }];
+}
+
 // Whether a type is a BaseBoxType
 def IsBaseBoxTypePred
         : CPred<"$_self.isa<::fir::BaseBoxType>()">;
@@ -590,13 +649,17 @@ def AnyRealLike : TypeConstraint<Or<[FloatLike.predicate,
     fir_RealType.predicate]>, "any real">;
 def AnyIntegerType : Type<AnyIntegerLike.predicate, "any integer">;
 
+def IsShadowTypePred
+        : CPred<"$_self.isa<::fir::ShadowType>()">;
+
 // Composable types
 // Note that we include both fir_ComplexType and AnyComplex, so we can use both
 // the FIR ComplexType and the MLIR ComplexType (the former is used to represent
 // Fortran complex and the latter for C++ std::complex).
 def AnyCompositeLike : TypeConstraint<Or<[fir_RecordType.predicate,
     fir_SequenceType.predicate, fir_ComplexType.predicate, AnyComplex.predicate,
-    fir_VectorType.predicate, IsTupleTypePred, fir_CharacterType.predicate]>,
+    fir_VectorType.predicate, IsTupleTypePred, fir_CharacterType.predicate,
+    IsShadowTypePred]>,
     "any composite">;
 
 // Reference types
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 153ce0623ab3ae..3d2ff056aed650 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -468,6 +468,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
           return hlfir::translateToExtendedValue(getCurrentLocation(),
                                                  getFirOpBuilder(), x);
         },
+        [&](const hlfir::FortranVariableShadow &x) -> fir::ExtendedValue {
+          return hlfir::translateToExtendedValue(getCurrentLocation(),
+                                                 getFirOpBuilder(), x);
+        },
         [](const auto &box) -> fir::ExtendedValue { return box; });
   }
 
@@ -1009,6 +1013,16 @@ class FirConverter : public Fortran::lower::AbstractConverter {
         return v.match(
             [&](const Fortran::lower::SymbolBox::Intrinsic &)
                 -> Fortran::lower::SymbolBox { return v; },
+            [&](const hlfir::FortranVariableShadow &box)
+                -> Fortran::lower::SymbolBox {
+              auto exv =
+                  hlfir::translateToExtendedValue(toLocation(), *builder, box);
+              return exv.match(
+                  [](mlir::Value x) -> Fortran::lower::SymbolBox {
+                    return Fortran::lower::SymbolBox::Intrinsic{x};
+                  },
+                  [](auto x) -> Fortran::lower::SymbolBox { return x; });
+            },
             [](const auto &) -> Fortran::lower::SymbolBox { return {}; });
 
       return {};
@@ -1066,6 +1080,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     assert(lowerToHighLevelFIR());
     hlfir::Entity lhs{dst};
     hlfir::Entity rhs{src};
+
     // Temporary_lhs is set to true in hlfir.assign below to avoid user
     // assignment to be used and finalization to be called on the LHS.
     // This may or may not be correct but mimics the current behaviour
diff --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp
index d157db2cde4961..9b69bb10967349 100644
--- a/flang/lib/Lower/ConvertExpr.cpp
+++ b/flang/lib/Lower/ConvertExpr.cpp
@@ -981,6 +981,9 @@ class ScalarExprLowering {
           },
           [&](const fir::ProcBoxValue &toBox) {
             TODO(loc, "procedure pointer component in derived type assignment");
+          },
+          [&](const hlfir::FortranVariableShadow &toBox) {
+            TODO(loc, "FortranVariableShadow in derived type assignment");
           });
     }
     return res;
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 717b8cc0276a30..be88d30e12d306 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -15,7 +15,9 @@
 #include "Utils.h"
 #include "flang/Lower/PFTBuilder.h"
 #include "flang/Lower/SymbolMap.h"
+#include "flang/Optimizer/Builder/HLFIRTools.h"
 #include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
 #include "flang/Semantics/tools.h"
 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
 
@@ -357,6 +359,28 @@ void DataSharingProcessor::doPrivatize(const Fortran::semantics::Symbol *sym) {
   bool isFirstPrivate =
       sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate);
 
+  mlir::Type privatizerArgType = hsb.match(
+      [&](const fir::FortranVariableOpInterface &box) -> mlir::Type {
+        hlfir::Entity variable(box);
+        return fir::ShadowType::get(
+            &converter.getMLIRContext(), variable.getBase().getType(),
+            variable.getFirBase().getType(), variable.isAllocatable());
+      },
+      [&](const auto &box) -> mlir::Type { return symType; });
+
+  auto addSymbol = [&](mlir::Region &region, unsigned argIdx, bool force) {
+    hsb.match(
+        [&](const fir::FortranVariableOpInterface &box) {
+          hlfir::Entity variable(box);
+          hlfir::FortranVariableShadow variableShadow(
+              firOpBuilder, region.getArgument(argIdx), box);
+          symTable->addSymbol(*sym, variableShadow, force);
+        },
+        [&](const auto &box) {
+          symTable->addSymbol(*sym, region.getArgument(argIdx), force);
+        });
+  };
+
   mlir::omp::PrivateClauseOp privatizerOp = [&]() {
     auto moduleOp = firOpBuilder.getModule();
     auto uniquePrivatizerName = fir::getTypeAsString(
@@ -373,7 +397,7 @@ void DataSharingProcessor::doPrivatize(const Fortran::semantics::Symbol *sym) {
     firOpBuilder.setInsertionPoint(&moduleOp.getBodyRegion().front(),
                                    moduleOp.getBodyRegion().front().begin());
     auto result = firOpBuilder.create<mlir::omp::PrivateClauseOp>(
-        symLoc, uniquePrivatizerName, symType,
+        symLoc, uniquePrivatizerName, privatizerArgType,
         isFirstPrivate ? mlir::omp::DataSharingClauseType::FirstPrivate
                        : mlir::omp::DataSharingClauseType::Private);
 
@@ -383,15 +407,44 @@ void DataSharingProcessor::doPrivatize(const Fortran::semantics::Symbol *sym) {
     {
       mlir::Region &allocRegion = result.getAllocRegion();
       mlir::Block *allocEntryBlock = firOpBuilder.createBlock(
-          &allocRegion, /*insertPt=*/{}, symType, symLoc);
+          &allocRegion, /*insertPt=*/{}, privatizerArgType, symLoc);
 
       firOpBuilder.setInsertionPointToEnd(allocEntryBlock);
-      symTable->addSymbol(*sym, allocRegion.getArgument(0));
+      addSymbol(allocRegion, 0, false);
       symTable->pushScope();
       cloneSymbol(sym);
-      firOpBuilder.create<mlir::omp::YieldOp>(
-          hsb.getAddr().getLoc(),
-          symTable->shallowLookupSymbol(*sym).getAddr());
+
+      auto genYieldedVal = [&]() -> mlir::Value {
+        auto symBox = symTable->shallowLookupSymbol(*sym);
+        mlir::Value addr = symBox.getAddr();
+        mlir::Operation *definingOp = addr.getDefiningOp();
+        hlfir::DeclareOp declareOp =
+            mlir::dyn_cast<hlfir::DeclareOp>(definingOp);
+
+        if (declareOp == nullptr) {
+          return addr;
+        }
+        declareOp.getBase();
+        declareOp.getOriginalBase();
+        mlir::Location loc = declareOp.getLoc();
+
+        mlir::Value yieldTuple =
+            firOpBuilder.create<fir::UndefOp>(loc, privatizerArgType);
+
+        mlir::Type idxTy = firOpBuilder.getIndexType();
+
+        yieldTuple = firOpBuilder.create<fir::InsertValueOp>(
+            loc, privatizerArgType, yieldTuple, declareOp.getBase(),
+            firOpBuilder.getArrayAttr({firOpBuilder.getIntegerAttr(idxTy, 0)}));
+
+        yieldTuple = firOpBuilder.create<fir::InsertValueOp>(
+            loc, privatizerArgType, yieldTuple, declareOp.getOriginalBase(),
+            firOpBuilder.getArrayAttr({firOpBuilder.getIntegerAttr(idxTy, 1)}));
+        return yieldTuple;
+      };
+
+      firOpBuilder.create<mlir::omp::YieldOp>(hsb.getAddr().getLoc(),
+                                              genYieldedVal());
       symTable->popScope();
     }
 
@@ -401,18 +454,19 @@ void DataSharingProcessor::doPrivatize(const Fortran::semantics::Symbol *sym) {
       // First block argument corresponding to the original/host value while
       // second block argument corresponding to the privatized value.
       mlir::Block *copyEntryBlock = firOpBuilder.createBlock(
-          &copyRegion, /*insertPt=*/{}, {symType, symType}, {symLoc, symLoc});
+          &copyRegion, /*insertPt=*/{}, {privatizerArgType, privatizerArgType},
+          {symLoc, symLoc});
       firOpBuilder.setInsertionPointToEnd(copyEntryBlock);
-      symTable->addSymbol(*sym, copyRegion.getArgument(0),
-                          /*force=*/true);
+      addSymbol(copyRegion, 0, true);
+
       symTable->pushScope();
-      symTable->addSymbol(*sym, copyRegion.getArgument(1));
+      addSymbol(copyRegion, 1, false);
+
       auto ip = firOpBuilder.saveInsertionPoint();
       copyFirstPrivateSymbol(sym, &ip);
 
-      firOpBuilder.create<mlir::omp::YieldOp>(
-          hsb.getAddr().getLoc(),
-          symTable->shallowLookupSymbol(*sym).getAddr());
+      firOpBuilder.create<mlir::omp::YieldOp>(hsb.getAddr().getLoc(),
+                                              copyRegion.getArgument(1));
       symTable->popScope();
     }
 
@@ -423,7 +477,36 @@ void DataSharingProcessor::doPrivatize(const Fortran::semantics::Symbol *sym) {
 
   delayedPrivatizationInfo.privatizers.push_back(
       mlir::SymbolRefAttr::get(privatizerOp));
-  delayedPrivatizationInfo.originalAddresses.push_back(hsb.getAddr());
+
+  hsb.match(
+      [&](const fir::FortranVariableOpInterface &box) {
+        mlir::Location loc = hsb.getAddr().getLoc();
+        mlir::Operation *boxDefiningOp =
+            fir::FortranVariableOpInterface(box).getOperation();
+        if (auto declareOp = mlir::dyn_cast<hlfir::DeclareOp>(boxDefiningOp)) {
+          hlfir::Entity entity{box};
+          mlir::Value originalTuple =
+              firOpBuilder.create<fir::UndefOp>(loc, privatizerArgType);
+
+          mlir::Type idxTy = firOpBuilder.getIndexType();
+
+          originalTuple = firOpBuilder.create<fir::InsertValueOp>(
+              loc, privatizerArgType, originalTuple, entity.getBase(),
+              firOpBuilder.getArrayAttr(
+                  {firOpBuilder.getIntegerAttr(idxTy, 0)}));
+
+          originalTuple = firOpBuilder.create<fir::InsertValueOp>(
+              loc, privatizerArgType, originalTuple, entity.getFirBase(),
+              firOpBuilder.getArrayAttr(
+                  {firOpBuilder.getIntegerAttr(idxTy, 1)}));
+
+          delayedPrivatizationInfo.originalAddresses.push_back(originalTuple);
+        } else
+          TODO(loc, "Unsupported FortranVariableOpInterface operation.");
+      },
+      [&](const auto &box) {
+        delayedPrivatizationInfo.originalAddresses.push_back(hsb.getAddr());
+      });
   delayedPrivatizationInfo.symbols.push_back(sym);
 }
 
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 83aebdaecd3504..ddb5a392919777 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -650,14 +650,31 @@ genParallelOp(Fortran::lower::AbstractConverter &converter,
     llvm::transform(privateVars, std::back_inserter(privateVarLocs),
                     [](mlir::Value v) { return v.getLoc(); });
 
-    converter.getFirOpBuilder().createBlock(&region, /*insertPt=*/{},
-                                            privateVarTypes, privateVarLocs);
+    auto &opBuilder = converter.getFirOpBuilder();
+    opBuilder.createBlock(&region, /*insertPt=*/{}, privateVarTypes,
+                          privateVarLocs);
 
     llvm::SmallVector<const Fortran::semantics::Symbol *> allSymbols =
         reductionSymbols;
     allSymbols.append(delayedPrivatizationInfo.symbols);
     for (auto [arg, prv] : llvm::zip_equal(allSymbols, region.getArguments())) {
-      converter.bindSymbol(*arg, prv);
+      // TODO Another place where a custom type would be more suitable.
+      bool isFortranVariableShadow = [](mlir::Value val) {
+        return val.getType().dyn_cast<fir::ShadowType>() != nullptr;
+      }(prv);
+
+      if (isFortranVariableShadow) {
+        fir::ShadowType tupleType = prv.getType().cast<fir::ShadowType>();
+        auto firBase = opBuilder
+                           .create<fir::ExtractValueOp>(
+                               prv.getLoc(), tupleType.getType(1), prv,
+                               opBuilder.getArrayAttr({opBuilder.getIntegerAttr(
+                                   opBuilder.getIndexType(), 1)}))
+                           .getRes();
+        converter.bindSymbol(*arg, firBase);
+      } else {
+        converter.bindSymbol(*arg, prv);
+      }
     }
 
     return allSymbols;
diff --git a/flang/lib/Lower/SymbolMap.cpp b/flang/lib/Lower/SymbolMap.cpp
index ce078a7fbde651..7c25447f2dae96 100644
--- a/flang/lib/Lower/SymbolMap.cpp
+++ b/flang/lib/Lower/SymbolMap.cpp
@@ -21,16 +21,18 @@
 void Fortran::lower::SymMap::addSymbol(Fortran::semantics::SymbolRef sym,
                                        const fir::ExtendedValue &exv,
                                        bool force) {
-  exv.match([&](const fir::UnboxedValue &v) { addSymbol(sym, v, force); },
-            [&](const fir::CharBoxValue &v) { makeSym(sym, v, force); },
-            [&](const fir::ArrayBoxValue &v) { makeSym(sym, v, force); },
-            [&](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");
-            });
+  exv.match(
+      [&](const fir::UnboxedValue &v) { addSymbol(sym, v, force); },
+      [&](const fir::CharBoxValue &v) { makeSym(sym, v, force); },
+      [&](const fir::ArrayBoxValue &v) { makeSym(sym, v, force); },
+      [&](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); },
+      [&](const hlfir::FortranVariableShadow &v) { makeSym(sym, v, force); },
+      [](auto) {
+        llvm::report_fatal_error("value not added to symbol table");
+      });
 }
 
 Fortran::lower::SymbolBox
diff --git a/flang/lib/Optimizer/Builder/BoxValue.cpp b/flang/lib/Optimizer/Builder/BoxValue.cpp
index 361fa59e204037..b383f476032fb9 100644
--- a/flang/lib/Optimizer/Builder/BoxValue.cpp
+++ b/flang/lib/Optimizer/Builder/BoxValue.cpp
@@ -19,8 +19,10 @@
 #define DEBUG_TYPE "flang-box-value"
 
 mlir::Value fir::getBase(const fir::ExtendedValue &exv) {
-  return exv.match([](const fir::UnboxedValue &x) { return x; },
-                   [](const auto &x) { return x.getAddr(); });
+  return exv.match(
+      [](const fir::UnboxedValue &x) { return x; },
+      [](const hlfir::FortranVariableShadow &x) { return x.getBase(); },
+      [](const auto &x) { return x.getAddr(); });
 }
 
 mlir::Value fir::getLen(const fir::ExtendedValue &exv) {
@@ -40,6 +42,11 @@ fir::ExtendedValue fir::substBase(const fir::ExtendedValue &exv,
                                   mlir::Value base) {
   return exv.match(
       [=](const fir::UnboxedValue &x) { return fir::ExtendedValue(base); },
+      [=](const hlfir::FortranVariableShadow &x) {
+        [[maybe_unused]] auto loc = fir::getBase(exv).getLoc();
+        TODO(loc, "substBase not supported for hlfir::FortranVariableShadow");
+        return fir::ExtendedValue();
+      },
       [=](const auto &x) { return fir::ExtendedValue(x.clone(base)); });
 }
 
diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
index 4ffa303f27103a..e14fae68429750 100644
--- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp
+++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
@@ -183,6 +183,36 @@ static fir::CharBoxValue genUnboxChar(mlir::Location loc,
   return {addr, len};
 }
 
+static bool checkIsIntegerConstant(mlir::Attribute attr, std::int64_t conVal) {
+  if (auto iattr = attr.dyn_cast<mlir::IntegerAttr>())
+    return iattr.getInt() == conVal;
+  return false;
+}
+
+static bool isZero(mlir::Attribute a) { return checkIsIntegerConstant(a, 0); }
+static bool isOne(mlir::Attribute a) { return checkIsIntegerConstant(a, 1); }
+
+bool hlfir::Entity::isMutableShadow() const {
+  if (!isFortranVariableShadow())
+    return false;
+
+  auto tupleType = getType().cast<fir::ShadowType>();
+  return hlfir::isBoxAddressType(tupleType.getType(1));
+}
+
+mlir::Value hlfir::Entity::getBase() const {
+  if (isFortranVariableShadow()) {
+    for (auto *user : getUsers()) {
+      if (auto extractOp = mlir::dyn_cast<fir::ExtractValueOp>(*user)) {
+        if (isZero(extractOp.getCoor()[0]))
+          return extractOp;
+      }
+    }
+  }
+
+  return *this;
+}
+
 mlir::Value hlfir::Entity::getFirBase() const {
   if (fir::FortranVariableOpInterface variable = getIfVariableInterface()) {
     if (auto declareOp =
@@ -192,9 +222,38 @@ mlir::Value hlfir::Entity::getFirBase() const {
             mlir::dyn_cast<hlfir::AssociateOp>(variable.getOperation()))
       return associateOp.getFirBase();
   }
+
+  if (isFortranVariableShadow()) {
+    for (auto *user : getUsers()) {
+      if (auto extractOp = mlir::dyn_cast<fir::ExtractValueOp>(*user)) {
+        if (isOne(extractOp.getCoor()[0])) {
+          return extractOp;
+        }
+      }
+    }
+  }
+
   return getBase();
 }
 
+hlfir::FortranVariableShadow::FortranVariableShadow(
+    fir::FirOpBuilder &builder, mlir::BlockArgument shadowingVal,
+    fir::FortranVariableOpInterface shadowedVariable)
+    : shadowingVal(shadowingVal), shadowedVariable(shadowedVariable) {
+  fir::ShadowType shadowingValType =
+      shadowingVal.getType().cast<fir::ShadowType>();
+  auto baseExtractor = [&](unsigned elementIdx) {
+    return builder.create<fir::ExtractValueOp>(
+        shadowingVal.getLoc(), shadowingValType.getType(elementIdx),
+        shadowingVal,
+        builder.getArrayAttr(
+            {builder.getIntegerAttr(builder.getIndexType(), elementIdx)}));
+  };
+
+  base = baseExtractor(0);
+  firBase = baseExtractor(1);
+}
+
 fir::FortranVariableOpInterface
 hlfir::genDeclare(mlir::Location loc, fir::FirOpBuilder &builder,
                   const fir::ExtendedValue &exv, llvm::StringRef name,
@@ -853,9 +912,10 @@ translateVariableToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
   /// When going towards FIR, use the original base value to avoid
   /// introducing descriptors at runtime when they are not required.
   mlir::Value firBase = variable.getFirBase();
-  if (variable.isMutableBox())
+  if (variable.isMutableBox() || variable.isMutableShadow()) {
     return fir::MutableBoxValue(firBase, getExplicitTypeParams(variable),
                                 fir::MutableProperties{});
+  }
 
   if (firBase.getType().isa<fir::BaseBoxType>()) {
     if (!variable.isSimplyContiguous() || variable.isPolymorphic() ||
@@ -904,6 +964,12 @@ hlfir::translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
   return translateVariableToExtendedValue(loc, builder, var);
 }
 
+fir::ExtendedValue
+hlfir::translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
+                                hlfir::FortranVariableShadow var) {
+  return translateVariableToExtendedValue(loc, builder, var);
+}
+
 std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
 hlfir::translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
                                 hlfir::Entity entity) {
diff --git a/flang/lib/Optimizer/Builder/MutableBox.cpp b/flang/lib/Optimizer/Builder/MutableBox.cpp
index d4012e9c3d9d93..31f65e052db523 100644
--- a/flang/lib/Optimizer/Builder/MutableBox.cpp
+++ b/flang/lib/Optimizer/Builder/MutableBox.cpp
@@ -562,6 +562,9 @@ void fir::factory::associateMutableBox(fir::FirOpBuilder &builder,
       },
       [&](const fir::ProcBoxValue &) {
         TODO(loc, "procedure pointer assignment");
+      },
+      [&](const hlfir::FortranVariableShadow &) {
+        TODO(loc, "FortranVariableShadow assignment");
       });
 }
 
@@ -663,6 +666,9 @@ void fir::factory::associateMutableBoxWithRemap(
       },
       [&](const fir::ProcBoxValue &) {
         TODO(loc, "procedure pointer assignment");
+      },
+      [&](const hlfir::FortranVariableShadow &) {
+        TODO(loc, "FortranVariableShadow assignment");
       });
 }
 
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 8a2c681d958609..0116bb614f62f3 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -1325,7 +1325,7 @@ void FIROpsDialect::registerTypes() {
            fir::ComplexType, FieldType, HeapType, fir::IntegerType, LenType,
            LogicalType, LLVMPointerType, PointerType, RealType, RecordType,
            ReferenceType, SequenceType, ShapeType, ShapeShiftType, ShiftType,
-           SliceType, TypeDescType, fir::VectorType>();
+           SliceType, TypeDescType, fir::VectorType, fir::ShadowType>();
   fir::ReferenceType::attachInterface<
       OpenMPPointerLikeModel<fir::ReferenceType>>(*getContext());
   fir::ReferenceType::attachInterface<
diff --git a/flang/test/Lower/OpenMP/delayed-privatization-allocatable.f90 b/flang/test/Lower/OpenMP/delayed-privatization-allocatable.f90
new file mode 100644
index 00000000000000..9be12c162b4030
--- /dev/null
+++ b/flang/test/Lower/OpenMP/delayed-privatization-allocatable.f90
@@ -0,0 +1,69 @@
+! Test delayed privatization for allocatables.
+
+! RUN: bbc -emit-hlfir -fopenmp --openmp-enable-delayed-privatization -o - %s 2>&1 | FileCheck %s
+
+subroutine delayed_privatization_allocatable
+  implicit none
+  integer, allocatable :: var1
+
+!$omp parallel firstprivate(var1)
+  var1 = 10
+!$omp end parallel
+end subroutine
+
+! CHECK-LABEL: omp.private {type = firstprivate}
+! CHECK-SAME: @[[PRIVATIZER_SYM:.*]] : [[TYPE:!fir.shadow<!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>, allocatable : true>]] alloc {
+
+! CHECK-NEXT: ^bb0(%[[PRIV_ARG:.*]]: [[TYPE]]):
+
+! CHECK-NEXT:  %[[BASE:.*]] = fir.extract_value %[[PRIV_ARG]], [0 : index] : ([[TYPE]])
+! CHECK-NEXT:  %[[FIR_BASE:.*]] = fir.extract_value %[[PRIV_ARG]], [1 : index] : ([[TYPE]])
+
+! CHECK-NEXT:   %[[PRIV_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<i32>> {bindc_name = "var1", pinned, uniq_name = "_QFdelayed_privatization_allocatableEvar1"}
+
+! CHECK-NEXT:   %[[FIR_BASE_VAL:.*]] = fir.load %[[FIR_BASE]] : !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK-NEXT:   %[[FIR_BASE_BOX:.*]] = fir.box_addr %[[FIR_BASE_VAL]] : (!fir.box<!fir.heap<i32>>) -> !fir.heap<i32>
+! CHECK-NEXT:   %[[FIR_BASE_ADDR:.*]] = fir.convert %[[FIR_BASE_BOX]] : (!fir.heap<i32>) -> i64
+! CHECK-NEXT:   %[[C0:.*]] = arith.constant 0 : i64
+! CHECK-NEXT:   %[[ALLOC_COND:.*]] = arith.cmpi ne, %[[FIR_BASE_ADDR]], %[[C0]] : i64
+
+! CHECK-NEXT:   fir.if %[[ALLOC_COND]] {
+! CHECK-NEXT:     %[[PRIV_ALLOCMEM:.*]] = fir.allocmem i32 {fir.must_be_heap = true, uniq_name = "_QFdelayed_privatization_allocatableEvar1.alloc"}
+! CHECK-NEXT:     %[[PRIV_ALLOCMEM_BOX:.*]] = fir.embox %[[PRIV_ALLOCMEM]] : (!fir.heap<i32>) -> !fir.box<!fir.heap<i32>>
+! CHECK-NEXT:     fir.store %[[PRIV_ALLOCMEM_BOX]] to %[[PRIV_ALLOC]] : !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK-NEXT:   } else {
+! CHECK-NEXT:     %[[ZERO_BITS:.*]] = fir.zero_bits !fir.heap<i32>
+! CHECK-NEXT:     %[[ZERO_BOX:.*]] = fir.embox %[[ZERO_BITS]] : (!fir.heap<i32>) -> !fir.box<!fir.heap<i32>>
+! CHECK-NEXT:     fir.store %[[ZERO_BOX]] to %[[PRIV_ALLOC]] : !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK-NEXT:   }
+
+! CHECK-NEXT:   %[[PRIV_DECL:.*]]:2 = hlfir.declare %[[PRIV_ALLOC]]
+
+! CHECK-NEXT:   %[[PRIV_SHADOW:.*]] = fir.undefined [[TYPE]]
+! CHECK-NEXT:   %[[PRIV_SHADOW_2:.*]] = fir.insert_value %[[PRIV_SHADOW]], %[[PRIV_DECL]]#0, [0 : index]
+! CHECK-NEXT:   %[[PRIV_SHADOW_3:.*]] = fir.insert_value %[[PRIV_SHADOW_2]], %[[PRIV_DECL]]#1, [1 : index]
+
+! CHECK-NEXT:   omp.yield(%[[PRIV_SHADOW_3]] : [[TYPE]])
+
+! CHECK-NEXT: } copy {
+! CHECK: ^bb0(%[[PRIV_ORIG_ARG:.*]]: [[TYPE]], %[[PRIV_PRIV_ARG:.*]]: [[TYPE]]):
+! CHECK-NEXT:  %[[ORIG_BASE:.*]] = fir.extract_value %[[PRIV_ORIG_ARG]], [0 : index] : ([[TYPE]])
+! CHECK-NEXT:  %[[ORIG_FIR_BASE:.*]] = fir.extract_value %[[PRIV_ORIG_ARG]], [1 : index] : ([[TYPE]])
+
+! CHECK-NEXT:  %[[PRIV_BASE:.*]] = fir.extract_value %[[PRIV_PRIV_ARG]], [0 : index] : ([[TYPE]])
+! CHECK-NEXT:  %[[PRIV_FIR_BASE:.*]] = fir.extract_value %[[PRIV_PRIV_ARG]], [1 : index] : ([[TYPE]])
+
+! CHECK-NEXT:  %[[PRIV_BASE_VAL:.*]] = fir.load %[[PRIV_BASE]]
+! CHECK-NEXT:  %[[PRIV_BASE_BOX:.*]] = fir.box_addr %[[PRIV_BASE_VAL]]
+! CHECK-NEXT:  %[[PRIV_BASE_ADDR:.*]] = fir.convert %[[PRIV_BASE_BOX]]
+! CHECK-NEXT:  %[[C0:.*]] = arith.constant 0 : i64
+! CHECK-NEXT:  %[[COPY_COND:.*]] = arith.cmpi ne, %[[PRIV_BASE_ADDR]], %[[C0]] : i64
+
+! CHECK-NEXT:  fir.if %[[COPY_COND]] {
+! CHECK-NEXT:    %[[ORIG_BASE_VAL:.*]] = fir.load %[[ORIG_BASE]]
+! CHECK-NEXT:    %[[ORIG_BASE_ADDR:.*]] = fir.box_addr %[[ORIG_BASE_VAL]]
+! CHECK-NEXT:    %[[ORIG_BASE_LD:.*]] = fir.load %[[ORIG_BASE_ADDR]]
+! CHECK-NEXT:    hlfir.assign %[[ORIG_BASE_LD]] to %[[PRIV_BASE_BOX]] temporary_lhs
+! CHECK-NEXT:  }
+
+! CHECK-NEXT:  omp.yield(%[[PRIV_PRIV_ARG]] : [[TYPE]])
diff --git a/flang/test/Lower/OpenMP/delayed-privatization-firstprivate.f90 b/flang/test/Lower/OpenMP/delayed-privatization-firstprivate.f90
index e3d2a5a8af2608..d83df6c334f838 100644
--- a/flang/test/Lower/OpenMP/delayed-privatization-firstprivate.f90
+++ b/flang/test/Lower/OpenMP/delayed-privatization-firstprivate.f90
@@ -12,18 +12,34 @@ subroutine delayed_privatization_firstprivate
 end subroutine
 
 ! CHECK-LABEL: omp.private {type = firstprivate}
-! CHECK-SAME: @[[VAR1_PRIVATIZER_SYM:.*]] : !fir.ref<i32> alloc {
-! CHECK-NEXT: ^bb0(%[[PRIV_ARG:.*]]: !fir.ref<i32>):
+! CHECK-SAME: @[[PRIVATIZER_SYM:.*]] : [[TYPE:!fir.shadow<!fir.ref<i32>, !fir.ref<i32>, allocatable : false>]] alloc {
+! CHECK-NEXT: ^bb0(%[[PRIV_ARG:.*]]: [[TYPE]]):
+
+! CHECK-NEXT:  %[[BASE:.*]] = fir.extract_value %[[PRIV_ARG]], [0 : index] : ([[TYPE]]) -> !fir.ref<i32>
+! CHECK-NEXT:  %[[FIR_BASE:.*]] = fir.extract_value %[[PRIV_ARG]], [1 : index] : ([[TYPE]]) -> !fir.ref<i32>
+
 ! CHECK-NEXT:   %[[PRIV_ALLOC:.*]] = fir.alloca i32 {bindc_name = "var1", pinned, uniq_name = "_QFdelayed_privatization_firstprivateEvar1"}
 ! CHECK-NEXT:   %[[PRIV_DECL:.*]]:2 = hlfir.declare %[[PRIV_ALLOC]] {uniq_name = "_QFdelayed_privatization_firstprivateEvar1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-! CHECK-NEXT:   omp.yield(%[[PRIV_DECL]]#0 : !fir.ref<i32>)
+
+! CHECK-NEXT:  %[[YIELD_VAL:.*]] = fir.undefined [[TYPE]]
+! CHECK-NEXT:  %[[YIELD_VAL_2:.*]] = fir.insert_value %[[YIELD_VAL]], %[[PRIV_DECL]]#0, [0 : index] : ([[TYPE]], !fir.ref<i32>) -> [[TYPE]]
+! CHECK-NEXT:  %[[YIELD_VAL_3:.*]] = fir.insert_value %[[YIELD_VAL_2]], %[[PRIV_DECL]]#1, [1 : index] : ([[TYPE]], !fir.ref<i32>) -> [[TYPE]]
+
+! CHECK-NEXT:   omp.yield(%[[YIELD_VAL_3]] : [[TYPE]])
+
 ! CHECK: } copy {
-! CHECK: ^bb0(%[[PRIV_ORIG_ARG:.*]]: !fir.ref<i32>, %[[PRIV_PRIV_ARG:.*]]: !fir.ref<i32>):
-! CHECK:    %[[ORIG_VAL:.*]] = fir.load %[[PRIV_ORIG_ARG]] : !fir.ref<i32>
-! CHECK:    hlfir.assign %[[ORIG_VAL]] to %[[PRIV_PRIV_ARG]] temporary_lhs : i32, !fir.ref<i32>
-! CHECK:    omp.yield(%[[PRIV_PRIV_ARG]] : !fir.ref<i32>)
-! CHECK: }
+! CHECK: ^bb0(%[[PRIV_ORIG_ARG:.*]]: [[TYPE]], %[[PRIV_PRIV_ARG:.*]]: [[TYPE]]):
+
+! CHECK-NEXT:  %[[ORIG_BASE:.*]] = fir.extract_value %[[PRIV_ORIG_ARG]], [0 : index] : ([[TYPE]]) -> !fir.ref<i32>
+! CHECK-NEXT:  %[[ORIG_FIR_BASE:.*]] = fir.extract_value %[[PRIV_ORIG_ARG]], [1 : index] : ([[TYPE]]) -> !fir.ref<i32>
+
+! CHECK-NEXT:  %[[PRIV_BASE:.*]] = fir.extract_value %[[PRIV_PRIV_ARG]], [0 : index] : ([[TYPE]]) -> !fir.ref<i32>
+! CHECK-NEXT:  %[[PRIV_FIR_BASE:.*]] = fir.extract_value %[[PRIV_PRIV_ARG]], [1 : index] : ([[TYPE]]) -> !fir.ref<i32>
+
+! CHECK-NEXT:  %[[ORIG_VAL:.*]] = fir.load %[[ORIG_BASE]] : !fir.ref<i32>
+! CHECK-NEXT:  hlfir.assign %[[ORIG_VAL]] to %[[PRIV_BASE]] temporary_lhs : i32, !fir.ref<i32>
+! CHECK-NEXT:  omp.yield(%[[PRIV_PRIV_ARG]] : [[TYPE]])
 
 ! CHECK-LABEL: @_QPdelayed_privatization_firstprivate
-! CHECK: omp.parallel private(@[[VAR1_PRIVATIZER_SYM]] %{{.*}} -> %{{.*}} : !fir.ref<i32>) {
+! CHECK: omp.parallel private(@[[PRIVATIZER_SYM]] %{{.*}} -> %{{.*}} : [[TYPE]]) {
 ! CHECK: omp.terminator
diff --git a/flang/test/Lower/OpenMP/delayed-privatization-private-firstprivate.f90 b/flang/test/Lower/OpenMP/delayed-privatization-private-firstprivate.f90
index 46eef6eb3bcf6a..d528004df27c93 100644
--- a/flang/test/Lower/OpenMP/delayed-privatization-private-firstprivate.f90
+++ b/flang/test/Lower/OpenMP/delayed-privatization-private-firstprivate.f90
@@ -14,12 +14,12 @@ subroutine delayed_privatization_private_firstprivate
 end subroutine
 
 ! CHECK-LABEL: omp.private {type = firstprivate}
-! CHECK-SAME: @[[VAR2_PRIVATIZER_SYM:.*]] : !fir.ref<i32> alloc {
+! CHECK-SAME: @[[VAR2_PRIVATIZER_SYM:.*]] : [[TYPE:!fir.shadow<!fir.ref<i32>, !fir.ref<i32>, allocatable : false>]] alloc {
 ! CHECK: } copy {
 ! CHECK: }
 
 ! CHECK-LABEL: omp.private {type = private}
-! CHECK-SAME: @[[VAR1_PRIVATIZER_SYM:.*]] : !fir.ref<i32> alloc {
+! CHECK-SAME: @[[VAR1_PRIVATIZER_SYM:.*]] : [[TYPE]] alloc {
 ! CHECK: }
 
 ! CHECK-LABEL: func.func @_QPdelayed_privatization_private_firstprivate() {
@@ -29,6 +29,14 @@ subroutine delayed_privatization_private_firstprivate
 ! CHECK:  %[[VAR2_ALLOC:.*]] = fir.alloca i32 {bindc_name = "var2"
 ! CHECK:  %[[VAR2_DECL:.*]]:2 = hlfir.declare %[[VAR2_ALLOC]]
 
+! CHECK:  %[[VAR1_VAL:.*]] = fir.undefined [[TYPE]]
+! CHECK:  %[[VAR1_VAL_2:.*]] = fir.insert_value %[[VAR1_VAL]], %[[VAR1_DECL]]#0, [0 : index]
+! CHECK:  %[[VAR1_VAL_3:.*]] = fir.insert_value %[[VAR1_VAL_2]], %[[VAR1_DECL]]#1, [1 : index]
+
+! CHECK:  %[[VAR2_VAL:.*]] = fir.undefined [[TYPE]]
+! CHECK:  %[[VAR2_VAL_2:.*]] = fir.insert_value %[[VAR2_VAL]], %[[VAR2_DECL]]#0, [0 : index]
+! CHECK:  %[[VAR2_VAL_3:.*]] = fir.insert_value %[[VAR2_VAL_2]], %[[VAR2_DECL]]#1, [1 : index]
+
 ! CHECK:  omp.parallel private(
-! CHECK-SAME: @[[VAR1_PRIVATIZER_SYM]] %[[VAR1_DECL]]#0 -> %{{.*}} : !fir.ref<i32>, 
-! CHECK-SAME: @[[VAR2_PRIVATIZER_SYM]] %[[VAR2_DECL]]#0 -> %{{.*}} : !fir.ref<i32>) {
+! CHECK-SAME: @[[VAR1_PRIVATIZER_SYM]] %[[VAR1_VAL_3]] -> %{{.*}} : [[TYPE]],
+! CHECK-SAME: @[[VAR2_PRIVATIZER_SYM]] %[[VAR2_VAL_3]] -> %{{.*}} : [[TYPE]]) {
diff --git a/flang/test/Lower/OpenMP/delayed-privatization-private.f90 b/flang/test/Lower/OpenMP/delayed-privatization-private.f90
index 240e0e71bfcd16..02cc5d307a794a 100644
--- a/flang/test/Lower/OpenMP/delayed-privatization-private.f90
+++ b/flang/test/Lower/OpenMP/delayed-privatization-private.f90
@@ -12,17 +12,32 @@ subroutine delayed_privatization_private
 end subroutine
 
 ! CHECK-LABEL: omp.private {type = private}
-! CHECK-SAME: @[[PRIVATIZER_SYM:.*]] : !fir.ref<i32> alloc {
-! CHECK-NEXT: ^bb0(%[[PRIV_ARG:.*]]: !fir.ref<i32>):
+! CHECK-SAME: @[[PRIVATIZER_SYM:.*]] : [[TYPE:!fir.shadow<!fir.ref<i32>, !fir.ref<i32>, allocatable : false>]] alloc {
+! CHECK-NEXT: ^bb0(%[[PRIV_ARG:.*]]: [[TYPE]]):
+
+! CHECK-NEXT:  %[[BASE:.*]] = fir.extract_value %[[PRIV_ARG]], [0 : index] : ([[TYPE]]) -> !fir.ref<i32>
+! CHECK-NEXT:  %[[FIR_BASE:.*]] = fir.extract_value %[[PRIV_ARG]], [1 : index] : ([[TYPE]]) -> !fir.ref<i32>
+
 ! CHECK-NEXT:   %[[PRIV_ALLOC:.*]] = fir.alloca i32 {bindc_name = "var1", pinned, uniq_name = "_QFdelayed_privatization_privateEvar1"}
 ! CHECK-NEXT:   %[[PRIV_DECL:.*]]:2 = hlfir.declare %[[PRIV_ALLOC]] {uniq_name = "_QFdelayed_privatization_privateEvar1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-! CHECK-NEXT:   omp.yield(%[[PRIV_DECL]]#0 : !fir.ref<i32>)
+
+! CHECK-NEXT:  %[[YIELD_VAL:.*]] = fir.undefined [[TYPE]]
+! CHECK-NEXT:  %[[YIELD_VAL_2:.*]] = fir.insert_value %[[YIELD_VAL]], %[[PRIV_DECL]]#0, [0 : index] : ([[TYPE]], !fir.ref<i32>) -> [[TYPE]]
+! CHECK-NEXT:  %[[YIELD_VAL_3:.*]] = fir.insert_value %[[YIELD_VAL_2]], %[[PRIV_DECL]]#1, [1 : index] : ([[TYPE]], !fir.ref<i32>) -> [[TYPE]]
+
+! CHECK-NEXT:   omp.yield(%[[YIELD_VAL_3]] : [[TYPE]])
 ! CHECK-NOT: } copy {
 
 ! CHECK-LABEL: @_QPdelayed_privatization_private
 ! CHECK: %[[ORIG_ALLOC:.*]] = fir.alloca i32 {bindc_name = "var1", uniq_name = "_QFdelayed_privatization_privateEvar1"}
 ! CHECK: %[[ORIG_DECL:.*]]:2 = hlfir.declare %[[ORIG_ALLOC]] {uniq_name = "_QFdelayed_privatization_privateEvar1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-! CHECK: omp.parallel private(@[[PRIVATIZER_SYM]] %[[ORIG_DECL]]#0 -> %[[PAR_ARG:.*]] : !fir.ref<i32>) {
-! CHECK: %[[PAR_ARG_DECL:.*]]:2 = hlfir.declare %[[PAR_ARG]] {uniq_name = "_QFdelayed_privatization_privateEvar1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+
+! CHECK-NEXT:  %[[ORIG_VAL:.*]] = fir.undefined [[TYPE]]
+! CHECK-NEXT:  %[[ORIG_VAL_2:.*]] = fir.insert_value %[[ORIG_VAL]], %[[ORIG_DECL]]#0, [0 : index] : ([[TYPE]], !fir.ref<i32>) -> [[TYPE]]
+! CHECK-NEXT:  %[[ORIG_VAL_3:.*]] = fir.insert_value %[[ORIG_VAL_2]], %[[ORIG_DECL]]#1, [1 : index] : ([[TYPE]], !fir.ref<i32>) -> [[TYPE]]
+
+! CHECK: omp.parallel private(@[[PRIVATIZER_SYM]] %[[ORIG_VAL_3]] -> %[[PAR_ARG:.*]] : [[TYPE]]) {
+! CHECK: %[[PAR_ARG_FIR_BASE:.*]] = fir.extract_value %[[PAR_ARG]], [1 : index] : ([[TYPE]]) -> !fir.ref<i32>
+! CHECK: %[[PAR_ARG_DECL:.*]]:2 = hlfir.declare %[[PAR_ARG_FIR_BASE]] {uniq_name = "_QFdelayed_privatization_privateEvar1"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
 ! CHECK: hlfir.assign %{{.*}} to %[[PAR_ARG_DECL]]#0 : i32, !fir.ref<i32>
 ! CHECK: omp.terminator
diff --git a/flang/test/Lower/OpenMP/delayed-privatization-reduction.f90 b/flang/test/Lower/OpenMP/delayed-privatization-reduction.f90
index c61f352b9b055a..b2aa9a3448bd4c 100644
--- a/flang/test/Lower/OpenMP/delayed-privatization-reduction.f90
+++ b/flang/test/Lower/OpenMP/delayed-privatization-reduction.f90
@@ -19,12 +19,12 @@ subroutine red_and_delayed_private
 end subroutine
 
 ! CHECK-LABEL: omp.private {type = private}
-! CHECK-SAME: @[[PRIVATIZER_SYM:.*]] : !fir.ref<i32> alloc {
+! CHECK-SAME: @[[PRIVATIZER_SYM:.*]] : [[TYPE:!fir.shadow<!fir.ref<i32>, !fir.ref<i32>, allocatable : false>]] alloc {
 
 ! CHECK-LABEL: omp.reduction.declare
 ! CHECK-SAME: @[[REDUCTION_SYM:.*]] : i32 init
 
 ! CHECK-LABEL: _QPred_and_delayed_private
 ! CHECK: omp.parallel
-! CHECK-SAME: reduction(@[[REDUCTION_SYM]] %{{.*}} -> %arg0 : !fir.ref<i32>)
-! CHECK-SAME: private(@[[PRIVATIZER_SYM]] %{{.*}} -> %arg1 : !fir.ref<i32>) {
+! CHECK-SAME: reduction(@[[REDUCTION_SYM]] %{{.*}} -> %{{.*}} : !fir.ref<i32>)
+! CHECK-SAME: private(@[[PRIVATIZER_SYM]] %{{.*}} -> %{{.*}} : [[TYPE]]) {



More information about the flang-commits mailing list