Skip to content

Commit 7d98b66

Browse files
committed
[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`.
1 parent 894f52f commit 7d98b66

19 files changed

+517
-63
lines changed

flang/include/flang/Lower/SymbolMap.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "flang/Common/reference.h"
1717
#include "flang/Optimizer/Builder/BoxValue.h"
18+
#include "flang/Optimizer/Builder/VariableShadow.h"
1819
#include "flang/Optimizer/Dialect/FIRType.h"
1920
#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
2021
#include "flang/Optimizer/Support/Matcher.h"
@@ -77,7 +78,8 @@ struct SymbolBox : public fir::details::matcher<SymbolBox> {
7778

7879
using VT =
7980
std::variant<Intrinsic, FullDim, Char, CharFullDim, PointerOrAllocatable,
80-
Box, fir::FortranVariableOpInterface, None>;
81+
Box, fir::FortranVariableOpInterface,
82+
hlfir::FortranVariableShadow, None>;
8183

8284
//===--------------------------------------------------------------------===//
8385
// Constructors
@@ -97,11 +99,13 @@ struct SymbolBox : public fir::details::matcher<SymbolBox> {
9799
/// scalar. For an array, this is the address of the first element in the
98100
/// array, etc.
99101
mlir::Value getAddr() const {
100-
return match([](const None &) { return mlir::Value{}; },
101-
[](const fir::FortranVariableOpInterface &x) {
102-
return fir::FortranVariableOpInterface(x).getBase();
103-
},
104-
[](const auto &x) { return x.getAddr(); });
102+
return match(
103+
[](const None &) { return mlir::Value{}; },
104+
[](const fir::FortranVariableOpInterface &x) {
105+
return fir::FortranVariableOpInterface(x).getBase();
106+
},
107+
[](const hlfir::FortranVariableShadow &x) { return x.getBase(); },
108+
[](const auto &x) { return x.getAddr(); });
105109
}
106110

107111
std::optional<fir::FortranVariableOpInterface>

flang/include/flang/Optimizer/Builder/BoxValue.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#ifndef FORTRAN_OPTIMIZER_BUILDER_BOXVALUE_H
1414
#define FORTRAN_OPTIMIZER_BUILDER_BOXVALUE_H
1515

16+
#include "flang/Optimizer/Builder/Todo.h"
17+
#include "flang/Optimizer/Builder/VariableShadow.h"
1618
#include "flang/Optimizer/Dialect/FIRType.h"
1719
#include "flang/Optimizer/Support/FatalError.h"
1820
#include "flang/Optimizer/Support/Matcher.h"
@@ -479,7 +481,8 @@ class ExtendedValue : public details::matcher<ExtendedValue> {
479481
public:
480482
using VT =
481483
std::variant<UnboxedValue, CharBoxValue, ArrayBoxValue, CharArrayBoxValue,
482-
ProcBoxValue, BoxValue, MutableBoxValue, PolymorphicValue>;
484+
ProcBoxValue, BoxValue, MutableBoxValue, PolymorphicValue,
485+
hlfir::FortranVariableShadow>;
483486

484487
ExtendedValue() : box{UnboxedValue{}} {}
485488
template <typename A, typename = std::enable_if_t<
@@ -516,6 +519,11 @@ class ExtendedValue : public details::matcher<ExtendedValue> {
516519
[](const fir::CharBoxValue &box) -> unsigned { return 0; },
517520
[](const fir::ProcBoxValue &box) -> unsigned { return 0; },
518521
[](const fir::PolymorphicValue &box) -> unsigned { return 0; },
522+
[](const hlfir::FortranVariableShadow &box) -> unsigned {
523+
TODO(box.getValue().getLoc(),
524+
"rank(): FortranVariableShadow");
525+
return 0;
526+
},
519527
[](const auto &box) -> unsigned { return box.rank(); });
520528
}
521529

flang/include/flang/Optimizer/Builder/HLFIRTools.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define FORTRAN_OPTIMIZER_BUILDER_HLFIRTOOLS_H
1515

1616
#include "flang/Optimizer/Builder/BoxValue.h"
17+
#include "flang/Optimizer/Builder/VariableShadow.h"
1718
#include "flang/Optimizer/Dialect/FIROps.h"
1819
#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
1920
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
@@ -55,8 +56,12 @@ class Entity : public mlir::Value {
5556
}
5657
Entity(fir::FortranVariableOpInterface variable)
5758
: mlir::Value(variable.getBase()) {}
59+
Entity(FortranVariableShadow variableShadow)
60+
: mlir::Value(variableShadow.getValue()) {}
61+
5862
bool isValue() const { return isFortranValue(*this); }
5963
bool isVariable() const { return !isValue(); }
64+
bool isMutableShadow() const;
6065
bool isMutableBox() const { return hlfir::isBoxAddressType(getType()); }
6166
bool isProcedurePointer() const {
6267
return fir::isBoxProcAddressType(getType());
@@ -74,6 +79,10 @@ class Entity : public mlir::Value {
7479
/// Is this an assumed ranked entity?
7580
bool isAssumedRank() const { return getRank() == -1; }
7681

82+
bool isFortranVariableShadow() const {
83+
return this->getType().isa<fir::ShadowType>();
84+
}
85+
7786
/// Return the rank of this entity or -1 if it is an assumed rank.
7887
int getRank() const {
7988
mlir::Type type = fir::unwrapPassByRefType(fir::unwrapRefType(getType()));
@@ -152,6 +161,14 @@ class Entity : public mlir::Value {
152161
return this->getDefiningOp<fir::FortranVariableOpInterface>();
153162
}
154163

164+
fir::ShadowType getIfVariableShadow() const {
165+
fir::ExtractValueOp op = this->getDefiningOp<fir::ExtractValueOp>();
166+
if (!op)
167+
return fir::ShadowType();
168+
169+
return op.getAdt().getType().dyn_cast<fir::ShadowType>();
170+
}
171+
155172
// Return a "declaration" operation for this variable if visible,
156173
// or the "declaration" operation of the allocatable/pointer this
157174
// variable was dereferenced from (if it is visible).
@@ -174,8 +191,13 @@ class Entity : public mlir::Value {
174191
}
175192

176193
bool isAllocatable() const {
177-
auto varIface = getIfVariableInterface();
178-
return varIface ? varIface.isAllocatable() : false;
194+
if (auto varIface = getIfVariableInterface())
195+
return varIface.isAllocatable();
196+
197+
if (auto shadowType = getIfVariableShadow())
198+
return shadowType.getAllocatable();
199+
200+
return false;
179201
}
180202

181203
bool isPointer() const {
@@ -185,7 +207,7 @@ class Entity : public mlir::Value {
185207

186208
// Get the entity as an mlir SSA value containing all the shape, type
187209
// parameters and dynamic shape information.
188-
mlir::Value getBase() const { return *this; }
210+
mlir::Value getBase() const;
189211

190212
// Get the entity as a FIR base. This may not carry the shape and type
191213
// parameters information, and even when it is a box with shape information.
@@ -231,6 +253,9 @@ translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
231253
fir::ExtendedValue
232254
translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
233255
fir::FortranVariableOpInterface fortranVariable);
256+
fir::ExtendedValue
257+
translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
258+
hlfir::FortranVariableShadow fortranVariableShadow);
234259

235260
/// Generate declaration for a fir::ExtendedValue in memory.
236261
fir::FortranVariableOpInterface
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===-- VariableShadow.h ----------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "flang/Optimizer/Builder/FIRBuilder.h"
14+
#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
15+
#include "mlir/IR/Value.h"
16+
17+
#ifndef FORTRAN_OPTIMIZER_BUILDER_VARIABLESHADOW_H
18+
#define FORTRAN_OPTIMIZER_BUILDER_VARIABLESHADOW_H
19+
20+
namespace hlfir {
21+
22+
class FortranVariableShadow {
23+
public:
24+
FortranVariableShadow(fir::FirOpBuilder &builder,
25+
mlir::BlockArgument shadowingVal,
26+
fir::FortranVariableOpInterface shadowedVariable);
27+
28+
mlir::BlockArgument getValue() const { return shadowingVal; }
29+
30+
mlir::Value getBase() const { return base; }
31+
32+
mlir::Value getFirBase() const { return firBase; }
33+
34+
fir::FortranVariableOpInterface getShadowedVariable() const {
35+
return shadowedVariable;
36+
}
37+
38+
private:
39+
mlir::BlockArgument shadowingVal;
40+
mlir::Value base;
41+
mlir::Value firBase;
42+
fir::FortranVariableOpInterface shadowedVariable;
43+
};
44+
45+
} // namespace hlfir
46+
47+
#endif // FORTRAN_OPTIMIZER_BUILDER_VARIABLESHADOW_H

flang/include/flang/Optimizer/Dialect/FIRTypes.td

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,65 @@ def fir_VoidType : FIR_Type<"Void", "void"> {
576576
let genStorageClass = 0;
577577
}
578578

579+
def fir_VariableShadowType : FIR_Type<"Shadow", "shadow"> {
580+
let summary = "Type for Fortran-variables shadow values";
581+
582+
let description = [{
583+
A type to encapulate required data for a Fortran-variable shadow value. A
584+
shadow type encapsulates the base type as well as the original base type. It
585+
also encapsulates information other information about the shadowed value,
586+
such as whether it is allocatable or not.
587+
588+
For example, if we want to shadow a value produced a `hlfir.declare`
589+
instruction similar to the following:
590+
```
591+
%1:2 = hlfir.declare %0
592+
{fortran_attrs = #fir.var_attrs<allocatable>}
593+
: (!fir.ref<!fir.box<!fir.heap<i32>>>)
594+
-> (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>)
595+
```
596+
597+
We would need to somehow create a value of the type:
598+
```
599+
!fir.shadow<!fir.ref<!fir.box<!fir.heap<i32>>>,
600+
!fir.ref<!fir.box<!fir.heap<i32>>>,
601+
allocatable : true>
602+
```
603+
604+
The mechanism to create this value can be, for example, through a
605+
combincation `fir.undefined`, `fir.insert_value` instructions similar to what
606+
would be done for other composite types like `tuple`s:
607+
```
608+
%2 = fir.undefined !fir.shadow<!fir.ref<!fir.box<!fir.heap<i32>>>,
609+
!fir.ref<!fir.box<!fir.heap<i32>>>,
610+
allocatable : true>
611+
%3 = fir.insert_value %2, %1#0, [0 : index] : <... type info omitted ...>
612+
%4 = fir.insert_value %3, %3#1, [1 : index] : <... type info omitted ...>
613+
```
614+
615+
From that point on, you can use `%4` as the shadow of the originally declared
616+
variable being shadowed `%1`.
617+
618+
For more info about the concept of variable shadows, check `VariableShadow.h`.
619+
}];
620+
621+
let parameters = (ins "mlir::Type":$baseTy,
622+
"mlir::Type":$firBaseTy,
623+
"bool":$allocatable);
624+
625+
let assemblyFormat = [{
626+
`<` $baseTy `,` $firBaseTy `,` `allocatable` `:` $allocatable `>`
627+
}];
628+
629+
let extraClassDeclaration = [{
630+
mlir::Type getType(unsigned idx) const {
631+
if (idx == 0) return getBaseTy();
632+
if (idx == 1) return getFirBaseTy();
633+
return mlir::Type();
634+
}
635+
}];
636+
}
637+
579638
// Whether a type is a BaseBoxType
580639
def IsBaseBoxTypePred
581640
: CPred<"$_self.isa<::fir::BaseBoxType>()">;
@@ -590,13 +649,17 @@ def AnyRealLike : TypeConstraint<Or<[FloatLike.predicate,
590649
fir_RealType.predicate]>, "any real">;
591650
def AnyIntegerType : Type<AnyIntegerLike.predicate, "any integer">;
592651

652+
def IsShadowTypePred
653+
: CPred<"$_self.isa<::fir::ShadowType>()">;
654+
593655
// Composable types
594656
// Note that we include both fir_ComplexType and AnyComplex, so we can use both
595657
// the FIR ComplexType and the MLIR ComplexType (the former is used to represent
596658
// Fortran complex and the latter for C++ std::complex).
597659
def AnyCompositeLike : TypeConstraint<Or<[fir_RecordType.predicate,
598660
fir_SequenceType.predicate, fir_ComplexType.predicate, AnyComplex.predicate,
599-
fir_VectorType.predicate, IsTupleTypePred, fir_CharacterType.predicate]>,
661+
fir_VectorType.predicate, IsTupleTypePred, fir_CharacterType.predicate,
662+
IsShadowTypePred]>,
600663
"any composite">;
601664

602665
// Reference types

flang/lib/Lower/Bridge.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
468468
return hlfir::translateToExtendedValue(getCurrentLocation(),
469469
getFirOpBuilder(), x);
470470
},
471+
[&](const hlfir::FortranVariableShadow &x) -> fir::ExtendedValue {
472+
return hlfir::translateToExtendedValue(getCurrentLocation(),
473+
getFirOpBuilder(), x);
474+
},
471475
[](const auto &box) -> fir::ExtendedValue { return box; });
472476
}
473477

@@ -1009,6 +1013,16 @@ class FirConverter : public Fortran::lower::AbstractConverter {
10091013
return v.match(
10101014
[&](const Fortran::lower::SymbolBox::Intrinsic &)
10111015
-> Fortran::lower::SymbolBox { return v; },
1016+
[&](const hlfir::FortranVariableShadow &box)
1017+
-> Fortran::lower::SymbolBox {
1018+
auto exv =
1019+
hlfir::translateToExtendedValue(toLocation(), *builder, box);
1020+
return exv.match(
1021+
[](mlir::Value x) -> Fortran::lower::SymbolBox {
1022+
return Fortran::lower::SymbolBox::Intrinsic{x};
1023+
},
1024+
[](auto x) -> Fortran::lower::SymbolBox { return x; });
1025+
},
10121026
[](const auto &) -> Fortran::lower::SymbolBox { return {}; });
10131027

10141028
return {};
@@ -1066,6 +1080,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
10661080
assert(lowerToHighLevelFIR());
10671081
hlfir::Entity lhs{dst};
10681082
hlfir::Entity rhs{src};
1083+
10691084
// Temporary_lhs is set to true in hlfir.assign below to avoid user
10701085
// assignment to be used and finalization to be called on the LHS.
10711086
// This may or may not be correct but mimics the current behaviour

flang/lib/Lower/ConvertExpr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,9 @@ class ScalarExprLowering {
981981
},
982982
[&](const fir::ProcBoxValue &toBox) {
983983
TODO(loc, "procedure pointer component in derived type assignment");
984+
},
985+
[&](const hlfir::FortranVariableShadow &toBox) {
986+
TODO(loc, "FortranVariableShadow in derived type assignment");
984987
});
985988
}
986989
return res;

0 commit comments

Comments
 (0)