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