@@ -1000,6 +1000,26 @@ convertOmpWsLoop(Operation &opInst, llvm::IRBuilderBase &builder,
1000
1000
return success ();
1001
1001
}
1002
1002
1003
+ // / Replace the region arguments of the parallel op (which correspond to private
1004
+ // / variables) with the actual private varibles they correspond to. This
1005
+ // / prepares the parallel op so that it matches what is expected by the
1006
+ // / OMPIRBuilder.
1007
+ static void prepareOmpParallelForPrivatization (omp::ParallelOp opInst) {
1008
+ Region ®ion = opInst.getRegion ();
1009
+ auto privateVars = opInst.getPrivateVars ();
1010
+
1011
+ auto privateVarsIt = privateVars.begin ();
1012
+ // Reduction precede private arguments, so skip them first.
1013
+ unsigned privateArgBeginIdx = opInst.getNumReductionVars ();
1014
+ unsigned privateArgEndIdx = privateArgBeginIdx + privateVars.size ();
1015
+ for (size_t argIdx = privateArgBeginIdx; argIdx < privateArgEndIdx;
1016
+ ++argIdx, ++privateVarsIt)
1017
+ replaceAllUsesInRegionWith (region.getArgument (argIdx), *privateVarsIt,
1018
+ region);
1019
+
1020
+ region.front ().eraseArguments (privateArgBeginIdx, privateVars.size ());
1021
+ }
1022
+
1003
1023
// / Converts the OpenMP parallel operation to LLVM IR.
1004
1024
static LogicalResult
1005
1025
convertOmpParallel (omp::ParallelOp opInst, llvm::IRBuilderBase &builder,
@@ -1008,6 +1028,7 @@ convertOmpParallel(omp::ParallelOp opInst, llvm::IRBuilderBase &builder,
1008
1028
// TODO: support error propagation in OpenMPIRBuilder and use it instead of
1009
1029
// relying on captured variables.
1010
1030
LogicalResult bodyGenStatus = success ();
1031
+ prepareOmpParallelForPrivatization (opInst);
1011
1032
llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder ();
1012
1033
1013
1034
auto bodyGenCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP) {
@@ -1092,6 +1113,86 @@ convertOmpParallel(omp::ParallelOp opInst, llvm::IRBuilderBase &builder,
1092
1113
llvm::Value *&replacementValue) -> InsertPointTy {
1093
1114
replacementValue = &vPtr;
1094
1115
1116
+ // If this is a private value, this lambda will return the corresponding
1117
+ // mlir value and its `PrivateClauseOp`. Otherwise, empty values are
1118
+ // returned.
1119
+ auto [privVar, privatizerClone] =
1120
+ [&]() -> std::pair<mlir::Value, omp::PrivateClauseOp> {
1121
+ if (!opInst.getPrivateVars ().empty ()) {
1122
+ auto privVars = opInst.getPrivateVars ();
1123
+ auto privatizers = opInst.getPrivatizers ();
1124
+ assert (privatizers && privatizers->size () == privVars.size ());
1125
+
1126
+ const auto *privInitIt = privatizers->begin ();
1127
+ for (auto privVarIt = privVars.begin (); privVarIt != privVars.end ();
1128
+ ++privVarIt, ++privInitIt) {
1129
+ // Find the MLIR private variable corresponding to the LLVM value
1130
+ // being privatized.
1131
+ llvm::Value *llvmPrivVar = moduleTranslation.lookupValue (*privVarIt);
1132
+ if (llvmPrivVar != &vPtr)
1133
+ continue ;
1134
+
1135
+ SymbolRefAttr privSym = llvm::cast<SymbolRefAttr>(*privInitIt);
1136
+ omp::PrivateClauseOp privatizer =
1137
+ SymbolTable::lookupNearestSymbolFrom<omp::PrivateClauseOp>(
1138
+ opInst, privSym);
1139
+
1140
+ assert (privatizer);
1141
+ // Clone the privatizer in case it used by more than one parallel
1142
+ // region. The privatizer is processed in-place (see below) before it
1143
+ // gets inlined in the parallel region and therefore processing the
1144
+ // original op is dangerous.
1145
+ return {*privVarIt, privatizer.clone ()};
1146
+ }
1147
+ }
1148
+
1149
+ return {mlir::Value (), omp::PrivateClauseOp ()};
1150
+ }();
1151
+
1152
+ if (privVar) {
1153
+ assert (privatizerClone.getDataSharingType () !=
1154
+ omp::DataSharingClauseType::FirstPrivate &&
1155
+ " TODO: delayed privatization is not supported for `firstprivate` "
1156
+ " clauses yet." );
1157
+ Region &allocRegion = privatizerClone.getAllocRegion ();
1158
+ assert (allocRegion.getNumArguments () == 1 );
1159
+ assert (allocRegion.hasOneBlock () &&
1160
+ " TODO: multi-block alloc regions are not supported yet. Seems "
1161
+ " like there is a difference in `inlineConvertOmpRegions`'s "
1162
+ " pre-conditions for single- and multi-block regions." );
1163
+
1164
+ // Replace the privatizer block argument with mlir value being privatized.
1165
+ // This way, the body of the privatizer will be changed from using the
1166
+ // region/block argument to the value being privatized.
1167
+ auto allocRegionArg = allocRegion.getArgument (0 );
1168
+ replaceAllUsesInRegionWith (allocRegionArg, privVar, allocRegion);
1169
+
1170
+ auto oldIP = builder.saveIP ();
1171
+ builder.restoreIP (allocaIP);
1172
+
1173
+ // Temporarily unlink the terminator from its parent since
1174
+ // `inlineConvertOmpRegions` expects the insertion block to **not**
1175
+ // contain a terminator.
1176
+ llvm::Instruction &allocaTerminator = builder.GetInsertBlock ()->back ();
1177
+ assert (allocaTerminator.isTerminator ());
1178
+ allocaTerminator.removeFromParent ();
1179
+
1180
+ SmallVector<llvm::Value *, 1 > yieldedValues;
1181
+ if (failed (inlineConvertOmpRegions (allocRegion, " omp.privatizer" , builder,
1182
+ moduleTranslation, &yieldedValues))) {
1183
+ opInst.emitError (" failed to inline `alloc` region of an `omp.private` "
1184
+ " op in the parallel region" );
1185
+ bodyGenStatus = failure ();
1186
+ } else {
1187
+ assert (yieldedValues.size () == 1 );
1188
+ replacementValue = yieldedValues.front ();
1189
+ }
1190
+
1191
+ allocaTerminator.insertAfter (&builder.GetInsertBlock ()->back ());
1192
+ privatizerClone.erase ();
1193
+ builder.restoreIP (oldIP);
1194
+ }
1195
+
1095
1196
return codeGenIP;
1096
1197
};
1097
1198
@@ -3009,12 +3110,13 @@ LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation(
3009
3110
.Case ([&](omp::TargetOp) {
3010
3111
return convertOmpTarget (*op, builder, moduleTranslation);
3011
3112
})
3012
- .Case <omp::MapInfoOp, omp::DataBoundsOp>([&](auto op) {
3013
- // No-op, should be handled by relevant owning operations e.g.
3014
- // TargetOp, EnterDataOp, ExitDataOp, DataOp etc. and then
3015
- // discarded
3016
- return success ();
3017
- })
3113
+ .Case <omp::MapInfoOp, omp::DataBoundsOp, omp::PrivateClauseOp>(
3114
+ [&](auto op) {
3115
+ // No-op, should be handled by relevant owning operations e.g.
3116
+ // TargetOp, EnterDataOp, ExitDataOp, DataOp etc. and then
3117
+ // discarded
3118
+ return success ();
3119
+ })
3018
3120
.Default ([&](Operation *inst) {
3019
3121
return inst->emitError (" unsupported OpenMP operation: " )
3020
3122
<< inst->getName ();
0 commit comments