Skip to content

Commit e5f9817

Browse files
committed
[flang][OpenMP] Extend do concurrent mapping to multi-range loops (llvm#127634)
Adds support for converting mulit-range loops to OpenMP (on the host only for now). The changes here "prepare" a loop nest for collapsing by sinking iteration variables to the innermost `fir.do_loop` op in the nest. PR stack: - llvm#126026 - llvm#127595 - llvm#127633 - llvm#127634 (this PR) - llvm#127635
1 parent 28b3143 commit e5f9817

File tree

2 files changed

+96
-6
lines changed

2 files changed

+96
-6
lines changed

flang/docs/DoConcurrentConversionToOpenMP.md

+29
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,35 @@ omp.parallel {
173173

174174
<!-- TODO -->
175175

176+
### Multi-range loops
177+
178+
The pass currently supports multi-range loops as well. Given the following
179+
example:
180+
181+
```fortran
182+
do concurrent(i=1:n, j=1:m)
183+
a(i,j) = i * j
184+
end do
185+
```
186+
187+
The generated `omp.loop_nest` operation look like:
188+
189+
```
190+
omp.loop_nest (%arg0, %arg1)
191+
: index = (%17, %19) to (%18, %20)
192+
inclusive step (%c1_2, %c1_4) {
193+
fir.store %arg0 to %private_i#1 : !fir.ref<i32>
194+
fir.store %arg1 to %private_j#1 : !fir.ref<i32>
195+
...
196+
omp.yield
197+
}
198+
```
199+
200+
It is worth noting that we have privatized versions for both iteration
201+
variables: `i` and `j`. These are locally allocated inside the parallel/target
202+
OpenMP region similar to what the single-range example in previous section
203+
shows.
204+
176205
<!--
177206
More details about current status will be added along with relevant parts of the
178207
implementation in later upstreaming patches.

flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp

+67-6
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,77 @@ namespace looputils {
163163
/// Stores info needed about the induction/iteration variable for each `do
164164
/// concurrent` in a loop nest.
165165
struct InductionVariableInfo {
166+
InductionVariableInfo(fir::DoLoopOp doLoop) { populateInfo(doLoop); }
167+
166168
/// The operation allocating memory for iteration variable.
167169
mlir::Operation *iterVarMemDef;
168170

169171
/// the operation(s) updating the iteration variable with the current
170172
/// iteration number.
171-
llvm::SetVector<mlir::Operation *> indVarUpdateOps;
173+
llvm::SmallVector<mlir::Operation *> indVarUpdateOps;
174+
175+
private:
176+
/// For the \p doLoop parameter, find the following:
177+
///
178+
/// 1. The operation that declares its iteration variable or allocates memory
179+
/// for it. For example, give the following loop:
180+
/// ```
181+
/// ...
182+
/// %i:2 = hlfir.declare %0 {uniq_name = "_QFEi"} : ...
183+
/// ...
184+
/// fir.do_loop %ind_var = %lb to %ub step %s unordered {
185+
/// %ind_var_conv = fir.convert %ind_var : (index) -> i32
186+
/// fir.store %ind_var_conv to %i#1 : !fir.ref<i32>
187+
/// ...
188+
/// }
189+
/// ```
190+
///
191+
/// This function sets the `iterVarMemDef` member to the `hlfir.declare` op
192+
/// for `%i`.
193+
///
194+
/// 2. The operation(s) that update the loop's iteration variable from its
195+
/// induction variable. For the above example, the `indVarUpdateOps` is
196+
/// populated with the first 2 ops in the loop's body.
197+
///
198+
/// Note: The current implementation is dependent on how flang emits loop
199+
/// bodies; which is sufficient for the current simple test/use cases. If this
200+
/// proves to be insufficient, this should be made more generic.
201+
void populateInfo(fir::DoLoopOp doLoop) {
202+
mlir::Value result = nullptr;
203+
204+
// Checks if a StoreOp is updating the memref of the loop's iteration
205+
// variable.
206+
auto isStoringIV = [&](fir::StoreOp storeOp) {
207+
// Direct store into the IV memref.
208+
if (storeOp.getValue() == doLoop.getInductionVar()) {
209+
indVarUpdateOps.push_back(storeOp);
210+
return true;
211+
}
212+
213+
// Indirect store into the IV memref.
214+
if (auto convertOp = mlir::dyn_cast<fir::ConvertOp>(
215+
storeOp.getValue().getDefiningOp())) {
216+
if (convertOp.getOperand() == doLoop.getInductionVar()) {
217+
indVarUpdateOps.push_back(convertOp);
218+
indVarUpdateOps.push_back(storeOp);
219+
return true;
220+
}
221+
}
222+
223+
return false;
224+
};
225+
226+
for (mlir::Operation &op : doLoop) {
227+
if (auto storeOp = mlir::dyn_cast<fir::StoreOp>(op))
228+
if (isStoringIV(storeOp)) {
229+
result = storeOp.getMemref();
230+
break;
231+
}
232+
}
233+
234+
assert(result != nullptr && result.getDefiningOp() != nullptr);
235+
iterVarMemDef = result.getDefiningOp();
236+
}
172237
};
173238

174239
using LoopNestToIndVarMap =
@@ -462,11 +527,7 @@ mlir::LogicalResult collectLoopNest(fir::DoLoopOp currentLoop,
462527
assert(currentLoop.getUnordered());
463528

464529
while (true) {
465-
loopNest.insert(
466-
{currentLoop,
467-
InductionVariableInfo{
468-
findLoopIterationVarMemDecl(currentLoop),
469-
std::move(looputils::extractIndVarUpdateOps(currentLoop))}});
530+
loopNest.insert({currentLoop, InductionVariableInfo(currentLoop)});
470531
llvm::SmallVector<fir::DoLoopOp> unorderedLoops;
471532

472533
for (auto nestedLoop : currentLoop.getRegion().getOps<fir::DoLoopOp>())

0 commit comments

Comments
 (0)