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.
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.
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