Hi Harald!
First of all thank you very much for your fast and careful review!
On Sun, 2026-05-31 at 21:55 +0200, Harald Anlauf wrote:
> Hi Henri!
>
> Am 31.05.26 um 8:01 PM schrieb Henri Menke:
> > gfortran already understands the !GCC$ ATTRIBUTES NOINLINE
> > directive to
> > suppress inlining of a procedure, but there was no way to request
> > the
> > opposite, equivalent to the C always_inline attribute.
> >
> > This adds a FORCEINLINE attribute usable as
> >
> > !GCC$ ATTRIBUTES forceinline :: my_procedure
> >
> > Since Fortran has no 'inline' keyword, the translation sets both
> > DECL_DECLARED_INLINE_P and DECL_DISREGARD_INLINE_LIMITS on the
> > function
> > declaration; setting only the latter would make the middle-end warn
> > that
> > the always-inline function "might not be inlinable".
> >
> > FORCEINLINE and NOINLINE are mutually exclusive. In the middle-end
> > the
> > DECL_UNINLINABLE flag set by NOINLINE always takes precedence, so a
> > combination of the two would silently ignore FORCEINLINE. To avoid
> > that
> > surprise the directive parser warns and drops FORCEINLINE when both
> > are
> > applied to the same procedure, whether in one directive or in two.
>
> While I think that options controlling inlining are desirable,
> I wonder if "FORCEINLINE" is a good choice. Also, why is there
> no regular "INLINE"?
>
> A commercial Fortran compiler I use at work supports "INLINE" and
> "ALWAYS_INLINE" with semantics comparable to gcc. My preference
> would be to follow this convention also for gfortran.
>
> (The Intel compiler has INLINE and FORCEINLINE, but according to the
> documentation these are used differently (and at the call site!),
> so there would be potential confusion of users.)
You are of course right. I have to admit that I took the name
FORCEINLINE from the Intel compiler without realizing that Intel's
INLINE/FORCEINLINE are call-site directives with semantics different
from what I implemented here. That mismatch would indeed be a source
of confusion and I fully agree that we should follow the INLINE /
ALWAYS_INLINE convention instead. For the next revision I would
propose to:
- rename FORCEINLINE to ALWAYS_INLINE, keeping the current semantics
(DECL_DECLARED_INLINE_P + DECL_DISREGARD_INLINE_LIMITS, i.e. the
equivalent of __attribute__((always_inline)))
- add INLINE as a plain inlining hint that only sets
DECL_DECLARED_INLINE_P and leaves the inliner's size limits in effect
(in line with the C "inline" keyword)
- keep the mutual-exclusion handling, now between
{INLINE,ALWAYS_INLINE} and NOINLINE.
Does that sound okay?
> A real problem of your patch is that it shifts the enums EXT_ATTR_*,
> after which the module files compiler from previous versions of
> gfortran are incompatible with those after the patch.
> This must be fixed.
Thanks, I did not realize that the EXT_ATTR_* values end up in the
module files (and there also seems to be no source comment warning
about this). As far as I can tell, appending the new values at the end
of the enum (before EXT_ATTR_LAST), and likewise appending the
corresponding rows at the end of ext_attr_list[], leaves the existing
bit positions untouched. Old modules would then continue to read
correctly and only modules that actually use the new attributes would
be unreadable by an older gfortran, which seems unavoidable.
Would that approach be acceptable (append at the end), or would you
rather see the module version bumped? Since I just started
contributing to GCC, I'm not sure what is the appropriate practice.
Kind regards,
Henri
> Harald
>
> > Bootstrapped and regtested on x86_64-pc-linux-gnu
> > (--enable-languages=c,c++,fortran): all three stages built and the
> > stage 2/3 comparison succeeded, and the gfortran testsuite shows no
> > regressions (75375 expected passes, 335 expected failures, 0
> > unexpected
> > failures), with the two new tests passing.
> >
> > gcc/fortran/ChangeLog:
> >
> > * gfortran.h (enum ext_attr_id_t): Add
> > EXT_ATTR_FORCEINLINE.
> > * decl.cc (ext_attr_list): Add "forceinline".
> > (gfc_match_gcc_attributes): Warn about and drop
> > FORCEINLINE when
> > it is combined with NOINLINE on the same symbol.
> > * trans-decl.cc (build_function_decl): Handle
> > EXT_ATTR_FORCEINLINE
> > by setting DECL_DECLARED_INLINE_P and
> > DECL_DISREGARD_INLINE_LIMITS.
> > * gfortran.texi (ATTRIBUTES directive): Document
> > FORCEINLINE.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * gfortran.dg/forceinline_1.f90: New test.
> > * gfortran.dg/forceinline_2.f90: New test.
> >
> > Signed-off-by: Henri Menke <[email protected]>
> > ---
> > gcc/fortran/decl.cc | 13 +++++++++++
> > gcc/fortran/gfortran.h | 1 +
> > gcc/fortran/gfortran.texi | 5 ++++
> > gcc/fortran/trans-decl.cc | 9 +++++++
> > gcc/testsuite/gfortran.dg/forceinline_1.f90 | 26
> > +++++++++++++++++++++
> > gcc/testsuite/gfortran.dg/forceinline_2.f90 | 14 +++++++++++
> > 6 files changed, 68 insertions(+)
> > create mode 100644 gcc/testsuite/gfortran.dg/forceinline_1.f90
> > create mode 100644 gcc/testsuite/gfortran.dg/forceinline_2.f90
> >
> > diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
> > index 166b10d4cd4..aa5b291ac41 100644
> > --- a/gcc/fortran/decl.cc
> > +++ b/gcc/fortran/decl.cc
> > @@ -12847,6 +12847,7 @@ const ext_attr_t ext_attr_list[] = {
> > { "no_arg_check", EXT_ATTR_NO_ARG_CHECK, NULL },
> > { "deprecated", EXT_ATTR_DEPRECATED, NULL },
> > { "noinline", EXT_ATTR_NOINLINE, NULL },
> > + { "forceinline", EXT_ATTR_FORCEINLINE, NULL },
> > { "noreturn", EXT_ATTR_NORETURN, NULL },
> > { "weak", EXT_ATTR_WEAK, NULL },
> > { NULL, EXT_ATTR_LAST, NULL }
> > @@ -12925,6 +12926,18 @@ gfc_match_gcc_attributes (void)
> >
> > sym->attr.ext_attr |= attr.ext_attr;
> >
> > + /* FORCEINLINE and NOINLINE are mutually exclusive. In the
> > middle-end
> > + DECL_UNINLINABLE (set by NOINLINE) always wins, so
> > FORCEINLINE would
> > + be silently ignored. Warn and drop it instead. */
> > + if ((sym->attr.ext_attr & (1 << EXT_ATTR_FORCEINLINE))
> > + && (sym->attr.ext_attr & (1 << EXT_ATTR_NOINLINE)))
> > + {
> > + gfc_warning (0, "Attribute %<FORCEINLINE%> at %C is
> > incompatible "
> > + "with %<NOINLINE%> for %qs and will be
> > ignored",
> > + sym->name);
> > + sym->attr.ext_attr &= ~(1 << EXT_ATTR_FORCEINLINE);
> > + }
> > +
> > if (gfc_match_eos () == MATCH_YES)
> > break;
> >
> > diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
> > index 37a8582e36d..440ad050bcb 100644
> > --- a/gcc/fortran/gfortran.h
> > +++ b/gcc/fortran/gfortran.h
> > @@ -886,6 +886,7 @@ typedef enum
> > EXT_ATTR_NO_ARG_CHECK,
> > EXT_ATTR_DEPRECATED,
> > EXT_ATTR_NOINLINE,
> > + EXT_ATTR_FORCEINLINE,
> > EXT_ATTR_NORETURN,
> > EXT_ATTR_WEAK,
> > EXT_ATTR_LAST, EXT_ATTR_NUM = EXT_ATTR_LAST
> > diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi
> > index a930cc1dc9c..255a9235f60 100644
> > --- a/gcc/fortran/gfortran.texi
> > +++ b/gcc/fortran/gfortran.texi
> > @@ -3397,6 +3397,11 @@ requires an explicit interface.
> > deprecated procedure, variable or parameter; the warning can be
> > suppressed
> > with @option{-Wno-deprecated-declarations}.
> > @item @code{NOINLINE} -- prevent inlining given function.
> > +@item @code{FORCEINLINE} -- force inlining of a given function,
> > ignoring the
> > +inlining size limits. This is the counterpart of @code{NOINLINE}
> > and
> > +corresponds to the C @code{always_inline} attribute.
> > @code{FORCEINLINE} and
> > +@code{NOINLINE} are mutually exclusive; specifying both for the
> > same procedure
> > +makes @code{FORCEINLINE} ignored with a warning.
> > @item @code{NORETURN} -- add a hint that a given function cannot
> > return.
> > @item @code{WEAK} -- emit the declaration of an external symbol
> > as a weak
> > symbol rather than a global. This is primarily useful in
> > defining library
> > diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc
> > index 1bcbfdfd2c9..da291c976ba 100644
> > --- a/gcc/fortran/trans-decl.cc
> > +++ b/gcc/fortran/trans-decl.cc
> > @@ -2652,6 +2652,15 @@ build_function_decl (gfc_symbol * sym, bool
> > global)
> > if (attr.ext_attr & (1 << EXT_ATTR_NOINLINE))
> > DECL_UNINLINABLE (fndecl) = 1;
> >
> > + /* Mark forceinline functions. Fortran has no 'inline' keyword,
> > so set
> > + DECL_DECLARED_INLINE_P as well; otherwise the middle-end
> > warns that the
> > + always-inline function "might not be inlinable". */
> > + if (attr.ext_attr & (1 << EXT_ATTR_FORCEINLINE))
> > + {
> > + DECL_DECLARED_INLINE_P (fndecl) = 1;
> > + DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
> > + }
> > +
> > /* Mark noreturn functions. */
> > if (attr.ext_attr & (1 << EXT_ATTR_NORETURN))
> > TREE_THIS_VOLATILE (fndecl) = 1;
> > diff --git a/gcc/testsuite/gfortran.dg/forceinline_1.f90
> > b/gcc/testsuite/gfortran.dg/forceinline_1.f90
> > new file mode 100644
> > index 00000000000..2058c088662
> > --- /dev/null
> > +++ b/gcc/testsuite/gfortran.dg/forceinline_1.f90
> > @@ -0,0 +1,26 @@
> > +! { dg-do compile }
> > +! { dg-options "-O -fdump-tree-optimized" }
> > +!
> > +! Verify that !GCC$ ATTRIBUTES forceinline forces inlining: the
> > helper
> > +! procedure is inlined into its caller and no standalone call
> > remains.
> > +
> > +subroutine caller(a, n)
> > + implicit none
> > + integer, intent(in) :: n
> > + real, intent(inout) :: a(n)
> > + call helper(a, n)
> > + call helper(a, n)
> > +contains
> > + subroutine helper(x, m)
> > + implicit none
> > + integer, intent(in) :: m
> > + real, intent(inout) :: x(m)
> > +!GCC$ ATTRIBUTES forceinline :: helper
> > + integer :: i
> > + do i = 1, m
> > + x(i) = x(i) + 1.0
> > + end do
> > + end subroutine helper
> > +end subroutine caller
> > +
> > +! { dg-final { scan-tree-dump-not "helper" "optimized" } }
> > diff --git a/gcc/testsuite/gfortran.dg/forceinline_2.f90
> > b/gcc/testsuite/gfortran.dg/forceinline_2.f90
> > new file mode 100644
> > index 00000000000..44e4c107034
> > --- /dev/null
> > +++ b/gcc/testsuite/gfortran.dg/forceinline_2.f90
> > @@ -0,0 +1,14 @@
> > +! { dg-do compile }
> > +!
> > +! FORCEINLINE and NOINLINE are mutually exclusive. Specifying
> > both for the
> > +! same procedure must warn and drop FORCEINLINE, whether they
> > appear in one
> > +! directive or in separate directives.
> > +
> > +subroutine foo()
> > +!GCC$ ATTRIBUTES forceinline, noinline :: foo ! { dg-warning
> > "FORCEINLINE. at .1. is incompatible with .NOINLINE." }
> > +end subroutine foo
> > +
> > +subroutine bar()
> > +!GCC$ ATTRIBUTES forceinline :: bar
> > +!GCC$ ATTRIBUTES noinline :: bar ! { dg-warning "FORCEINLINE. at
> > .1. is incompatible with .NOINLINE." }
> > +end subroutine bar