On Wed, Jun 17, 2026 at 10:57 AM Dhruv Chawla <[email protected]> wrote: > > On 17/06/26 14:03, Kyrylo Tkachov wrote: > > > > > >> On 17 Jun 2026, at 10:22, Richard Biener <[email protected]> > >> wrote: > >> > >> On Wed, Jun 17, 2026 at 8:27 AM <[email protected]> wrote: > >>> > >>> From: Dhruv Chawla <[email protected]> > >>> > >>> This flag causes the frontend to emit annot_expr_parallel_kind for > >>> do-concurrent loops, bypassing the PR83064 fix which would emit > >>> annot_expr_ivdep_kind. This flag is meant to be a temporary fix, and > >>> it along with patch 1 of this series recovers most of the performance gap > >>> between flang and gfortran for SPEC2026 pot3d_s (speeding it up roughly > >>> 5x), modulo libgomp performance issues. > >>> > >>> Bootstrapped and regtested on aarch64-linux-gnu. > >> > > Hi Richi and Kyrill, > > > We generally do not what to have user-facing flags that say > > "it might generate wrong-code". Instead we'd have asserting > > flags from the user, like -fdoconcurrent-are-parallelizable. But > > as the underlying issue is not visible to the user this doesn't > > work in this case. > > > So please do not add such flag (flags need to be preserved > > forever). Iff you really agree that you want it, use a > > --parm instead that we can freely remove. > > That makes sense, I will respin this to be a --param instead. My > apologies, that is effectively what I meant originally. This is just > meant to be a temporary fix for autopar's issues. > > But then, why not do this at the optimization phase of > > fortran and detect the problematic setup (temporaries > > it was, IIRC)? Or even, when you detect them, mark > > them as to be localized? Possibly both are not easily > > possible if there's function calls involved that might be > > inlined, exposing additional locals? > > Yes, that is what it turned out to be - a caller-allocated stack array > used as return storage for a callee (`compute` in the test case) got > hoisted outside the loop (as its address was loop-invariant for a > single thread), so multiple threads ended up writing to the same address > thus leading to garbage output. This doesn't occur in the pot3d function, > so it is safe to ignore this bug.
But it must be possible to classify a conservatively safe subset of do concurrent loops from inside the frontend? I'd really prefer that. > Unfortunately I do not know how the auto-parallelizer exactly works so > I had to ask an LLM to make me a patch that fixed it (which did > actually work and seemed to pass the testsuite!), but I don't trust it > enough to generally post as a patch. Thus the simple flag to bypass it. autopar is essentially unmaintained ... but clearly the can_be_parallel flag isn't correct for the IL as-is? While I looked into autpar years ago I don't remember much in what guarantees it makes, but I think all of the safety is with the dependence analysis we skip with can_be_parallel annotated loops. > > Drive by comment from me, but is the "wrong-code” risk here > > non-deterministic reassociation of FP operations? > > So is this a fast-math-style wrong-code? Or does is it a more serious risk? > > I initially thought it was similar to fast-math, but doing some digging > exposed the actual bug in autopar. > > > Thanks, > > Kyrill > > > >> > >>> Signed-off-by: Dhruv Chawla <[email protected]> > >>> > >>> gcc/fortran/ChangeLog: > >>> > >>> PR fortran/125717 > >>> * invoke.texi: Document the option. > >>> * lang.opt: Add -fdoconcurrent-force-parallel. > >>> * trans-stmt.cc (gfc_trans_forall_loop): Emit > >>> annot_expr_parallel_kind when flag_doconcurrent_force_parallel > >>> is set. > >>> > >>> gcc/testsuite/ChangeLog: > >>> > >>> * gfortran.dg/lto/do_concurrent_16_0.f90: New test. > >>> --- > >>> gcc/fortran/invoke.texi | 9 +++- > >>> gcc/fortran/lang.opt | 4 ++ > >>> gcc/fortran/trans-stmt.cc | 9 ++-- > >>> .../gfortran.dg/lto/do_concurrent_16_0.f90 | 53 +++++++++++++++++++ > >>> 4 files changed, 71 insertions(+), 4 deletions(-) > >>> create mode 100644 gcc/testsuite/gfortran.dg/lto/do_concurrent_16_0.f90 > >>> > >>> diff --git a/gcc/fortran/invoke.texi b/gcc/fortran/invoke.texi > >>> index 69c4fa69cc6..55a3fe3487b 100644 > >>> --- a/gcc/fortran/invoke.texi > >>> +++ b/gcc/fortran/invoke.texi > >>> @@ -191,7 +191,8 @@ and warnings}. > >>> -fbounds-check -ftail-call-workaround -ftail-call-workaround=@var{n} > >>> -fcheck-array-temporaries > >>> -fcheck=<all|array-temps|bits|bounds|do|mem|pointer|recursion> > >>> --fcoarray=<none|single|shared|lib> -fexternal-blas -fexternal-blas64 > >>> -ff2c > >>> +-fcoarray=<none|single|shared|lib> -fdoconcurrent-force-parallel > >>> +-fexternal-blas -fexternal-blas64 -ff2c > >>> -ffrontend-loop-interchange -ffrontend-optimize > >>> -finit-character=@var{n} -finit-integer=@var{n} -finit-local-zero > >>> -finit-derived -finit-logical=<true|false> > >>> @@ -2070,6 +2071,12 @@ size and also compilation time may become > >>> excessive. If that is the > >>> case, it may be better to disable this option. Instances of packing > >>> can be found by using @option{-Warray-temporaries}. > >>> > >>> +@opindex fdoconcurrent-force-parallel > >>> +@item -fdoconcurrent-force-parallel > >>> +This option forces parallelization of do-concurrent loops, bypassing the > >>> +usual validity analysis done by the compiler. This can introduce > >>> correctness > >>> +issues, so it should be used with caution. > >>> + > >>> @opindex fexternal-blas > >>> @item -fexternal-blas > >>> This option makes @command{gfortran} generate calls to BLAS functions > >>> diff --git a/gcc/fortran/lang.opt b/gcc/fortran/lang.opt > >>> index 6266d7f71bd..f90128a2cf3 100644 > >>> --- a/gcc/fortran/lang.opt > >>> +++ b/gcc/fortran/lang.opt > >>> @@ -554,6 +554,10 @@ fdefault-real-16 > >>> Fortran Var(flag_default_real_16) > >>> Set the default real kind to an 16 byte wide type. > >>> > >>> +fdoconcurrent-force-parallel > >>> +Fortran Var(flag_doconcurrent_force_parallel) > >>> +Force auto-parallelization of do-concurrent loops. > >>> + > >>> fdollar-ok > >>> Fortran Var(flag_dollar_ok) > >>> Allow dollar signs in entity names. > >>> diff --git a/gcc/fortran/trans-stmt.cc b/gcc/fortran/trans-stmt.cc > >>> index 0b1a8fa6b14..b89a9347394 100644 > >>> --- a/gcc/fortran/trans-stmt.cc > >>> +++ b/gcc/fortran/trans-stmt.cc > >>> @@ -4390,11 +4390,14 @@ gfc_trans_forall_loop (forall_info *forall_tmp, > >>> tree body, > >>> count, build_int_cst (TREE_TYPE (count), > >>> 0)); > >>> > >>> /* PR 83064 means that we cannot use annot_expr_parallel_kind until > >>> - the autoparallelizer can handle this. */ > >>> + the autoparallelizer can handle this. For now, use a flag to > >>> bypass > >>> + the ivdep analysis for cases where the bug doesn't manifest > >>> itself. */ > >>> + annot_expr_kind kind = annot_expr_ivdep_kind; > >>> + if (flag_doconcurrent_force_parallel && forall_tmp->do_concurrent) > >>> + kind = annot_expr_parallel_kind; > >>> if (forall_tmp->do_concurrent || iter->annot.ivdep) > >>> cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, > >>> - build_int_cst (integer_type_node, > >>> - annot_expr_ivdep_kind), > >>> + build_int_cst (integer_type_node, kind), > >>> integer_zero_node); > >>> > >>> if (iter->annot.unroll && cond != error_mark_node) > >>> diff --git a/gcc/testsuite/gfortran.dg/lto/do_concurrent_16_0.f90 > >>> b/gcc/testsuite/gfortran.dg/lto/do_concurrent_16_0.f90 > >>> new file mode 100644 > >>> index 00000000000..7b0419854cd > >>> --- /dev/null > >>> +++ b/gcc/testsuite/gfortran.dg/lto/do_concurrent_16_0.f90 > >>> @@ -0,0 +1,53 @@ > >>> +! { dg-lto-do link } > >>> +! { dg-require-effective-target pthread } > >>> +! { dg-lto-options { { -O2 -flto -fdoconcurrent-force-parallel > >>> -ftree-parallelize-loops=2 -fdump-tree-parloops2-details } } } > >>> +! > >>> +! Testcase taken from PR fortran/83064 > >>> + > >>> +program main > >>> + use, intrinsic :: iso_fortran_env > >>> + implicit none > >>> + > >>> + integer, parameter :: nsplit = 4 > >>> + integer(int64), parameter :: ne = 100000 > >>> + integer(int64) :: stride, low(nsplit), high(nsplit), edof(ne), i > >>> + real(real64), dimension(nsplit) :: pi > >>> + > >>> + edof(1::4) = 1 > >>> + edof(2::4) = 2 > >>> + edof(3::4) = 3 > >>> + edof(4::4) = 4 > >>> + > >>> + stride = ceiling(real(ne)/nsplit) > >>> + do i = 1, nsplit > >>> + high(i) = stride*i > >>> + end do > >>> + do i = 2, nsplit > >>> + low(i) = high(i-1) + 1 > >>> + end do > >>> + low(1) = 1 > >>> + high(nsplit) = ne > >>> + > >>> + pi = 0 > >>> + do concurrent (i = 1:nsplit) > >>> + pi(i) = sum(compute( low(i), high(i) )) > >>> + end do > >>> + print *, "PI", 4*sum(pi) > >>> + > >>> +contains > >>> + > >>> + pure function compute( low, high ) result( tmp ) > >>> + integer(int64), intent(in) :: low, high > >>> + real(real64), dimension(nsplit) :: tmp > >>> + integer(int64) :: j, k > >>> + > >>> + tmp = 0 > >>> + do j = low, high > >>> + k = edof(j) > >>> + tmp(k) = tmp(k) + (-1.0_real64)**(j+1) / real( 2*j-1 ) > >>> + end do > >>> + end function > >>> + > >>> +end program main > >>> + > >>> +! { dg-final { scan-ltrans-tree-dump "parallelizing \[^\n\r\]*loop" > >>> "parloops2" } } > >>> -- > >>> 2.43.0 > > > > > > -- > Regards, > Dhruv
