Skip to content

Commit 9d963b7

Browse files
committed
[mlir][OpenMP] Change the definition of omp.private
The intention of this work is to give MLIR->LLVMIR conversion freedom to control how the private variable is allocated so that it can be allocated on the stack in ordinary cases or as part of a structure used to give closure context for tasks which might outlive the current stack frame. See RFC: https://discourse.llvm.org/t/rfc-openmp-supporting-delayed-task-execution-with-firstprivate-variables/83084 In flang, before this patch we hit a TODO with the same wording when generating the copy region for firstprivate polymorphic variables. After this patch the box-like fir.class is passed by reference into the copy region, leading to a different path that didn't hit that old TODO but the generated code still didn't work so I added a new TODO in DataSharingProcessor. --- Please read mlir changes first and then flang changes. In flang lowering I box up all arrays and pass the boxes by reference so that the existing code for reduction init and dealloc regions can be shared. The TODOs for pointers, derived types, characters etc will be resolved in later patches in this same series (to be squashed into this one). I separated it to make it easier to review. Assumed rank was already broken before this patch. I can't find any mention of "assumed rank" in the openmp standard so I guess it is not prohibited. Other than the omp.private operation definition changes, the test changes are mostly down to slightly different codegen from re-using the reduction init region. That code is already well tested so I didn't want to change it.
1 parent f13d6c1 commit 9d963b7

File tree

51 files changed

+659
-853
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+659
-853
lines changed

flang/lib/Lower/OpenMP/DataSharingProcessor.cpp

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212

1313
#include "DataSharingProcessor.h"
1414

15+
#include "PrivateReductionUtils.h"
1516
#include "Utils.h"
1617
#include "flang/Lower/ConvertVariable.h"
1718
#include "flang/Lower/PFTBuilder.h"
1819
#include "flang/Lower/SymbolMap.h"
1920
#include "flang/Optimizer/Builder/HLFIRTools.h"
2021
#include "flang/Optimizer/Builder/Todo.h"
2122
#include "flang/Optimizer/HLFIR/HLFIROps.h"
23+
#include "flang/Semantics/attr.h"
2224
#include "flang/Semantics/tools.h"
2325

2426
namespace Fortran {
@@ -85,28 +87,8 @@ void DataSharingProcessor::insertDeallocs() {
8587
converter.createHostAssociateVarCloneDealloc(*sym);
8688
continue;
8789
}
88-
89-
lower::SymbolBox hsb = converter.lookupOneLevelUpSymbol(*sym);
90-
assert(hsb && "Host symbol box not found");
91-
mlir::Type symType = hsb.getAddr().getType();
92-
mlir::Location symLoc = hsb.getAddr().getLoc();
93-
fir::ExtendedValue symExV = converter.getSymbolExtendedValue(*sym);
94-
mlir::omp::PrivateClauseOp privatizer = symToPrivatizer.at(sym);
95-
96-
lower::SymMapScope scope(symTable);
97-
mlir::OpBuilder::InsertionGuard guard(firOpBuilder);
98-
99-
mlir::Region &deallocRegion = privatizer.getDeallocRegion();
100-
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
101-
mlir::Block *deallocEntryBlock = firOpBuilder.createBlock(
102-
&deallocRegion, /*insertPt=*/{}, symType, symLoc);
103-
104-
firOpBuilder.setInsertionPointToEnd(deallocEntryBlock);
105-
symTable.addSymbol(*sym,
106-
fir::substBase(symExV, deallocRegion.getArgument(0)));
107-
108-
converter.createHostAssociateVarCloneDealloc(*sym);
109-
firOpBuilder.create<mlir::omp::YieldOp>(hsb.getAddr().getLoc());
90+
// For delayed privatization deallocs are created by
91+
// populateByRefInitAndCleanupRegions
11092
}
11193
}
11294

@@ -468,15 +450,47 @@ void DataSharingProcessor::doPrivatize(const semantics::Symbol *sym,
468450
lower::SymbolBox hsb = converter.lookupOneLevelUpSymbol(*sym);
469451
assert(hsb && "Host symbol box not found");
470452

471-
mlir::Type symType = hsb.getAddr().getType();
453+
mlir::Value privVal = hsb.getAddr();
454+
mlir::Type allocType = fir::unwrapRefType(privVal.getType());
472455
mlir::Location symLoc = hsb.getAddr().getLoc();
473456
std::string privatizerName = sym->name().ToString() + ".privatizer";
474457
bool isFirstPrivate = sym->test(semantics::Symbol::Flag::OmpFirstPrivate);
475458

459+
if (mlir::isa<fir::PointerType>(hsb.getAddr().getType()))
460+
TODO(symLoc, "Privatization of pointers");
461+
462+
if (auto poly = mlir::dyn_cast<fir::ClassType>(allocType)) {
463+
if (!mlir::isa<fir::PointerType>(poly.getEleTy()) && isFirstPrivate)
464+
TODO(symLoc, "create polymorphic host associated copy");
465+
}
466+
467+
// fir.array<> cannot be converted to any single llvm type and fir helpers
468+
// are not available in openmp to llvmir translation so we cannot generate
469+
// an alloca for a fir.array type there. Get around this by boxing all
470+
// arrays.
471+
if (mlir::isa<fir::SequenceType>(allocType)) {
472+
hlfir::Entity entity{hsb.getAddr()};
473+
entity = genVariableBox(symLoc, firOpBuilder, entity);
474+
privVal = entity.getBase();
475+
allocType = privVal.getType();
476+
}
477+
478+
if (mlir::isa<fir::BaseBoxType>(privVal.getType())) {
479+
// Boxes should be passed by reference into nested regions:
480+
auto oldIP = firOpBuilder.saveInsertionPoint();
481+
firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
482+
auto alloca = firOpBuilder.create<fir::AllocaOp>(symLoc, privVal.getType());
483+
firOpBuilder.restoreInsertionPoint(oldIP);
484+
firOpBuilder.create<fir::StoreOp>(symLoc, privVal, alloca);
485+
privVal = alloca;
486+
}
487+
488+
mlir::Type argType = privVal.getType();
489+
476490
mlir::omp::PrivateClauseOp privatizerOp = [&]() {
477491
auto moduleOp = firOpBuilder.getModule();
478492
auto uniquePrivatizerName = fir::getTypeAsString(
479-
symType, converter.getKindMap(),
493+
allocType, converter.getKindMap(),
480494
converter.mangleName(*sym) +
481495
(isFirstPrivate ? "_firstprivate" : "_private"));
482496

@@ -488,44 +502,33 @@ void DataSharingProcessor::doPrivatize(const semantics::Symbol *sym,
488502
mlir::OpBuilder::InsertionGuard guard(firOpBuilder);
489503
firOpBuilder.setInsertionPointToStart(moduleOp.getBody());
490504
auto result = firOpBuilder.create<mlir::omp::PrivateClauseOp>(
491-
symLoc, uniquePrivatizerName, symType,
505+
symLoc, uniquePrivatizerName, allocType,
492506
isFirstPrivate ? mlir::omp::DataSharingClauseType::FirstPrivate
493507
: mlir::omp::DataSharingClauseType::Private);
494508
fir::ExtendedValue symExV = converter.getSymbolExtendedValue(*sym);
495509
lower::SymMapScope outerScope(symTable);
496510

497-
// Populate the `alloc` region.
498-
{
499-
mlir::Region &allocRegion = result.getAllocRegion();
500-
mlir::Block *allocEntryBlock = firOpBuilder.createBlock(
501-
&allocRegion, /*insertPt=*/{}, symType, symLoc);
502-
503-
firOpBuilder.setInsertionPointToEnd(allocEntryBlock);
504-
505-
fir::ExtendedValue localExV =
506-
hlfir::translateToExtendedValue(
507-
symLoc, firOpBuilder, hlfir::Entity{allocRegion.getArgument(0)},
508-
/*contiguousHint=*/
509-
evaluate::IsSimplyContiguous(*sym, converter.getFoldingContext()))
510-
.first;
511-
512-
symTable.addSymbol(*sym, localExV);
513-
lower::SymMapScope innerScope(symTable);
514-
cloneSymbol(sym);
515-
mlir::Value cloneAddr = symTable.shallowLookupSymbol(*sym).getAddr();
516-
mlir::Type cloneType = cloneAddr.getType();
517-
518-
// A `convert` op is required for variables that are storage associated
519-
// via `equivalence`. The problem is that these variables are declared as
520-
// `fir.ptr`s while their privatized storage is declared as `fir.ref`,
521-
// therefore we convert to proper symbol type.
522-
mlir::Value yieldedValue =
523-
(symType == cloneType) ? cloneAddr
524-
: firOpBuilder.createConvert(
525-
cloneAddr.getLoc(), symType, cloneAddr);
526-
527-
firOpBuilder.create<mlir::omp::YieldOp>(hsb.getAddr().getLoc(),
528-
yieldedValue);
511+
// Populate the `init` region.
512+
const bool needsInitialization =
513+
!fir::isa_trivial(allocType) ||
514+
Fortran::lower::hasDefaultInitialization(sym->GetUltimate();
515+
if (needsInitialization) {
516+
mlir::Region &initRegion = result.getInitRegion();
517+
mlir::Block *initBlock = firOpBuilder.createBlock(
518+
&initRegion, /*insertPt=*/{}, {argType, argType}, {symLoc, symLoc});
519+
520+
if (fir::isa_char(allocType))
521+
TODO(symLoc, "Privatization init of characters");
522+
if (fir::isa_derived(allocType))
523+
TODO(symLoc, "Privatization init of derived types");
524+
if (Fortran::lower::hasDefaultInitialization(sym->GetUltimate()))
525+
TODO(symLoc,
526+
"Privatization init of symbol with default initialization");
527+
528+
populateByRefInitAndCleanupRegions(
529+
firOpBuilder, symLoc, argType, /*scalarInitValue=*/nullptr, initBlock,
530+
result.getInitPrivateArg(), result.getInitMoldArg(),
531+
result.getDeallocRegion());
529532
}
530533

531534
// Populate the `copy` region if this is a `firstprivate`.
@@ -534,7 +537,7 @@ void DataSharingProcessor::doPrivatize(const semantics::Symbol *sym,
534537
// First block argument corresponding to the original/host value while
535538
// second block argument corresponding to the privatized value.
536539
mlir::Block *copyEntryBlock = firOpBuilder.createBlock(
537-
&copyRegion, /*insertPt=*/{}, {symType, symType}, {symLoc, symLoc});
540+
&copyRegion, /*insertPt=*/{}, {argType, argType}, {symLoc, symLoc});
538541
firOpBuilder.setInsertionPointToEnd(copyEntryBlock);
539542

540543
auto addSymbol = [&](unsigned argIdx, bool force = false) {
@@ -565,7 +568,7 @@ void DataSharingProcessor::doPrivatize(const semantics::Symbol *sym,
565568

566569
if (clauseOps) {
567570
clauseOps->privateSyms.push_back(mlir::SymbolRefAttr::get(privatizerOp));
568-
clauseOps->privateVars.push_back(hsb.getAddr());
571+
clauseOps->privateVars.push_back(privVal);
569572
}
570573

571574
symToPrivatizer[sym] = privatizerOp;

flang/lib/Optimizer/CodeGen/CodeGenOpenMP.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,45 @@ struct MapInfoOpConversion
9090
return mlir::success();
9191
}
9292
};
93+
94+
// FIR op specific conversion for PrivateClauseOp that overwrites the default
95+
// OpenMP Dialect lowering, this allows FIR-aware lowering of types, required
96+
// for boxes because the OpenMP dialect conversion doesn't know anything about
97+
// FIR types.
98+
struct PrivateClauseOpConversion
99+
: public OpenMPFIROpConversion<mlir::omp::PrivateClauseOp> {
100+
using OpenMPFIROpConversion::OpenMPFIROpConversion;
101+
102+
llvm::LogicalResult
103+
matchAndRewrite(mlir::omp::PrivateClauseOp curOp, OpAdaptor adaptor,
104+
mlir::ConversionPatternRewriter &rewriter) const override {
105+
const fir::LLVMTypeConverter &converter = lowerTy();
106+
mlir::Type convertedAllocType;
107+
if (auto box = mlir::dyn_cast<fir::BaseBoxType>(curOp.getType())) {
108+
// In LLVM codegen fir.box<> == fir.ref<fir.box<>> == llvm.ptr
109+
// Here we really do want the actual structure
110+
if (box.isAssumedRank())
111+
TODO(curOp->getLoc(), "Privatize an assumed rank array");
112+
unsigned rank = 0;
113+
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(
114+
fir::unwrapRefType(box.getEleTy())))
115+
rank = seqTy.getShape().size();
116+
convertedAllocType = converter.convertBoxTypeAsStruct(box, rank);
117+
} else {
118+
convertedAllocType = converter.convertType(adaptor.getType());
119+
}
120+
if (!convertedAllocType)
121+
return mlir::failure();
122+
rewriter.startOpModification(curOp);
123+
curOp.setType(convertedAllocType);
124+
rewriter.finalizeOpModification(curOp);
125+
return mlir::success();
126+
}
127+
};
93128
} // namespace
94129

95130
void fir::populateOpenMPFIRToLLVMConversionPatterns(
96131
const LLVMTypeConverter &converter, mlir::RewritePatternSet &patterns) {
97132
patterns.add<MapInfoOpConversion>(converter);
133+
patterns.add<PrivateClauseOpConversion>(converter);
98134
}

flang/test/Analysis/AliasAnalysis/alias-analysis-omp-private-allocatable.mlir

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,7 @@
2020
// CHECK: ar2#0 <-> ar1#1: NoAlias
2121
// CHECK: ar2#1 <-> ar1#1: NoAlias
2222

23-
omp.private {type = private} @_QFmysubEar1_private_ref_box_heap_Uxf64 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>> alloc {
24-
^bb0(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>):
25-
%0 = fir.alloca !fir.box<!fir.heap<!fir.array<?xf64>>> {bindc_name = "ar1", pinned, uniq_name = "_QFmysubEar1"}
26-
%5:2 = hlfir.declare %0 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFmysubEar1"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>)
27-
omp.yield(%5#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>)
28-
} dealloc {
29-
^bb0(%arg0: !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>):
30-
omp.yield
31-
}
23+
omp.private {type = private} @_QFmysubEar1_private_ref_box_heap_Uxf64 : !fir.box<!fir.heap<!fir.array<?xf64>>>
3224
func.func @testPrivateAllocatable(%arg0: !fir.ref<i32> {fir.bindc_name = "ns"}, %arg1: !fir.ref<i32> {fir.bindc_name = "ne"}) {
3325
%0 = fir.dummy_scope : !fir.dscope
3426
%1 = fir.alloca !fir.box<!fir.heap<!fir.array<?xf64>>> {bindc_name = "ar1", uniq_name = "_QFmysubEar1"}

flang/test/Analysis/AliasAnalysis/alias-analysis-omp-teams-distribute-private-ptr.mlir

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,8 @@
1717
// CHECK-LABEL: Testing : "_QQmain"
1818
// CHECK-DAG: ptrA#0 <-> ArrayA#0: MayAlias
1919

20-
omp.private {type = private} @_QFEi_private_ref_i32 : !fir.ref<i32> alloc {
21-
^bb0(%arg0: !fir.ref<i32>):
22-
%0 = fir.alloca i32 {bindc_name = "i", pinned, uniq_name = "_QFEi"}
23-
%1:2 = hlfir.declare %0 {uniq_name = "_QFEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
24-
omp.yield(%1#0 : !fir.ref<i32>)
25-
}
26-
omp.private {type = firstprivate} @_QFEptra_firstprivate_ref_box_ptr_Uxi32 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>> alloc {
27-
^bb0(%arg0: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>):
28-
%0 = fir.alloca !fir.box<!fir.ptr<!fir.array<?xi32>>> {bindc_name = "ptra", pinned, uniq_name = "_QFEptra"}
29-
%1:2 = hlfir.declare %0 {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFEptra"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>)
30-
omp.yield(%1#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>)
31-
} copy {
20+
omp.private {type = private} @_QFEi_private_ref_i32 : i32
21+
omp.private {type = firstprivate} @_QFEptra_firstprivate_ref_box_ptr_Uxi32 : !fir.box<!fir.ptr<!fir.array<?xi32>>> copy {
3222
^bb0(%arg0: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>, %arg1: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>):
3323
%0 = fir.load %arg0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>
3424
fir.store %0 to %arg1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>

flang/test/Analysis/AliasAnalysis/alias-analysis-omp-teams-distribute-private.mlir

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,10 @@
2121
// CHECK-DAG: tmp_private_array#0 <-> unnamed_array#0: NoAlias
2222
// CHECK-DAG: tmp_private_array#1 <-> unnamed_array#0: NoAlias
2323

24-
omp.private {type = private} @_QFEi_private_ref_i32 : !fir.ref<i32> alloc {
25-
^bb0(%arg0: !fir.ref<i32>):
26-
%0 = fir.alloca i32 {bindc_name = "i", pinned, uniq_name = "_QFEi"}
27-
%1:2 = hlfir.declare %0 {uniq_name = "_QFEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
28-
omp.yield(%1#0 : !fir.ref<i32>)
29-
}
30-
omp.private {type = private} @_QFEj_private_ref_i32 : !fir.ref<i32> alloc {
31-
^bb0(%arg0: !fir.ref<i32>):
32-
%0 = fir.alloca i32 {bindc_name = "j", pinned, uniq_name = "_QFEj"}
33-
%1:2 = hlfir.declare %0 {uniq_name = "_QFEj"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
34-
omp.yield(%1#0 : !fir.ref<i32>)
35-
}
36-
omp.private {type = private} @_QFEtmp_private_ref_2xi32 : !fir.ref<!fir.array<2xi32>> alloc {
37-
^bb0(%arg0: !fir.ref<!fir.array<2xi32>>):
38-
%c2 = arith.constant 2 : index
39-
%0 = fir.alloca !fir.array<2xi32> {bindc_name = "tmp", pinned, uniq_name = "_QFEtmp"}
40-
%1 = fir.shape %c2 : (index) -> !fir.shape<1>
41-
%2:2 = hlfir.declare %0(%1) {uniq_name = "_QFEtmp"} : (!fir.ref<!fir.array<2xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2xi32>>, !fir.ref<!fir.array<2xi32>>)
42-
omp.yield(%2#0 : !fir.ref<!fir.array<2xi32>>)
43-
}
24+
omp.private {type = private} @_QFEi_private_ref_i32 : i32
25+
omp.private {type = private} @_QFEj_private_ref_i32 : i32
26+
omp.private {type = private} @_QFEtmp_private_ref_2xi32 : !fir.array<2xi32>
27+
4428
func.func @_QQmain() attributes {fir.bindc_name = "main"} {
4529
%0 = fir.address_of(@_QFEarraya) : !fir.ref<!fir.array<10x10xi32>>
4630
%c10 = arith.constant 10 : index

0 commit comments

Comments
 (0)