#pragma omp sections lastprivate(<var>) <BODY>;
This construct is translated into something like:
<last_iter> = alloca i32
<init for lastprivates>;
<last_iter> = 0
; No initializer for simple variables or a default constructor is called for objects.
; For arrays perform element by element initialization by the call of the default constructor.
...
OMP_FOR_START(...,<last_iter>, ..); sets <last_iter> to 1 if this is the last iteration.
<BODY>
...
OMP_FOR_END
if (<last_iter> != 0) {
<final copy for lastprivate>; Update original variable with the lastprivate value.
}
call __kmpc_cancel_barrier() ; an implicit barrier to avoid possible data race.If there is only one section, there is no special code generation, original shared variables are used + barrier is emitted at the end of the directive.
You construct iterators like this so often — and almost always with a single clause kind as the filter — that I think you should probably just add specific API for it on OMPExecutableDirective. Something like
template <Fn> filtered_clause_iterator<Fn> getFilteredClauses(Fn &&fn) const { return filtered_clause_iterator<Fn>(clauses(), std::move(fn)); } struct ClauseKindFilter { OpenMPClauseKind Kind; bool operator()(const OMPClause *clause) const { return clause->getClauseKind() == Kind; } }; filtered_clause_iterator<ClauseKindFilter> getClausesOfKind(OpenMPClauseKind kind) const { return getFilteredClauses(ClauseKindFilter{ kind }); }Also, the operator bool on filtered_clause_iterator should be explicit.
The upshot is that this entire block should turn into: