https://gcc.gnu.org/g:a3de59859472872f17563fb68d1c142c281da240

commit r17-1274-ga3de59859472872f17563fb68d1c142c281da240
Author: Henri Menke <[email protected]>
Date:   Fri May 29 18:09:05 2026 +0000

    fortran: add INLINE and ALWAYS_INLINE attributes
    
    gfortran already understands the !GCC$ ATTRIBUTES NOINLINE directive to
    suppress inlining of a procedure, but there was no way to request the
    opposite.  This adds two attributes:
    
        !GCC$ ATTRIBUTES inline :: my_procedure
        !GCC$ ATTRIBUTES always_inline :: my_procedure
    
    INLINE is a plain hint equivalent to the C inline keyword and only sets
    DECL_DECLARED_INLINE_P, so the inliner's size limits still apply.
    ALWAYS_INLINE corresponds to the C always_inline attribute and
    additionally sets DECL_DISREGARD_INLINE_LIMITS.  Since Fortran has no
    inline keyword, DECL_DECLARED_INLINE_P is set explicitly.  Setting only
    DECL_DISREGARD_INLINE_LIMITS would make the middle-end warn that the
    always-inline function might not be inlinable.
    
    INLINE and ALWAYS_INLINE are incompatible with NOINLINE.  In the
    middle-end the DECL_UNINLINABLE flag set by NOINLINE always wins, so the
    inline request would be silently ignored.  To avoid that surprise the
    directive parser warns and drops the inline attribute when both are
    applied to the same procedure, whether in one directive or in two.
    
    The new EXT_ATTR_* values are appended at the end of the enum because the
    symbol_attribute.ext_attr bitmask is written to module files.  Appending
    preserves the existing bit positions and keeps modules written by older
    gfortran readable.
    
    Bootstrapped and regtested on x86_64-pc-linux-gnu with
    --enable-languages=c,c++,fortran.  All three stages built and the
    stage 2 and 3 comparison succeeded.  The gfortran testsuite shows no
    regressions (75660 expected passes, 339 expected failures, 0 unexpected
    failures) and the four new tests pass.
    
    gcc/fortran/ChangeLog:
    
            * gfortran.h (enum ext_attr_id_t): Add EXT_ATTR_INLINE and
            EXT_ATTR_ALWAYS_INLINE at the end.  Document that new attributes
            must be appended to preserve module compatibility.
            * decl.cc (ext_attr_list): Add "inline" and "always_inline".
            (gfc_match_gcc_attributes): Warn about and drop INLINE or
            ALWAYS_INLINE when combined with NOINLINE on the same symbol.
            * trans-decl.cc (build_function_decl): Handle EXT_ATTR_INLINE and
            EXT_ATTR_ALWAYS_INLINE by setting DECL_DECLARED_INLINE_P, and
            DECL_DISREGARD_INLINE_LIMITS for ALWAYS_INLINE.
            * gfortran.texi (ATTRIBUTES directive): Document INLINE and
            ALWAYS_INLINE.
    
    gcc/testsuite/ChangeLog:
    
            * gfortran.dg/always_inline_1.f90: New test.
            * gfortran.dg/always_inline_2.f90: New test.
            * gfortran.dg/inline_1.f90: New test.
            * gfortran.dg/inline_2.f90: New test.
    
    Signed-off-by: Henri Menke <[email protected]>

Diff:
---
 gcc/fortran/decl.cc                           | 25 ++++++++++++++++++++++++-
 gcc/fortran/gfortran.h                        | 10 +++++++++-
 gcc/fortran/gfortran.texi                     |  8 ++++++++
 gcc/fortran/trans-decl.cc                     | 10 ++++++++++
 gcc/testsuite/gfortran.dg/always_inline_1.f90 | 26 ++++++++++++++++++++++++++
 gcc/testsuite/gfortran.dg/always_inline_2.f90 | 14 ++++++++++++++
 gcc/testsuite/gfortran.dg/inline_1.f90        | 27 +++++++++++++++++++++++++++
 gcc/testsuite/gfortran.dg/inline_2.f90        | 14 ++++++++++++++
 8 files changed, 132 insertions(+), 2 deletions(-)

diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
index b81d81b2dd11..b14463ddfe7b 100644
--- a/gcc/fortran/decl.cc
+++ b/gcc/fortran/decl.cc
@@ -12844,11 +12844,13 @@ const ext_attr_t ext_attr_list[] = {
   { "cdecl",        EXT_ATTR_CDECL,        "cdecl"     },
   { "stdcall",      EXT_ATTR_STDCALL,      "stdcall"   },
   { "fastcall",     EXT_ATTR_FASTCALL,     "fastcall"  },
-  { "no_arg_check", EXT_ATTR_NO_ARG_CHECK, NULL        },
+  { "no_arg_check", EXT_ATTR_NO_ARG_CHECK, NULL               },
   { "deprecated",   EXT_ATTR_DEPRECATED,   NULL               },
   { "noinline",     EXT_ATTR_NOINLINE,     NULL               },
   { "noreturn",     EXT_ATTR_NORETURN,     NULL               },
   { "weak",        EXT_ATTR_WEAK,         NULL        },
+  { "inline",       EXT_ATTR_INLINE,       NULL               },
+  { "always_inline",EXT_ATTR_ALWAYS_INLINE,NULL               },
   { NULL,           EXT_ATTR_LAST,         NULL        }
 };
 
@@ -12925,6 +12927,27 @@ gfc_match_gcc_attributes (void)
 
       sym->attr.ext_attr |= attr.ext_attr;
 
+      /* INLINE and ALWAYS_INLINE are incompatible with NOINLINE.  In the
+        middle-end the DECL_UNINLINABLE flag set by NOINLINE always wins, so
+        the inline request would be silently ignored.  Warn and drop it.  */
+      if (sym->attr.ext_attr & (1 << EXT_ATTR_NOINLINE))
+       {
+         if (sym->attr.ext_attr & (1 << EXT_ATTR_ALWAYS_INLINE))
+           {
+             gfc_warning (0, "Attribute %<ALWAYS_INLINE%> at %C is "
+                          "incompatible with %<NOINLINE%> for %qs and will "
+                          "be ignored", sym->name);
+             sym->attr.ext_attr &= ~(1 << EXT_ATTR_ALWAYS_INLINE);
+           }
+         if (sym->attr.ext_attr & (1 << EXT_ATTR_INLINE))
+           {
+             gfc_warning (0, "Attribute %<INLINE%> at %C is incompatible "
+                          "with %<NOINLINE%> for %qs and will be ignored",
+                          sym->name);
+             sym->attr.ext_attr &= ~(1 << EXT_ATTR_INLINE);
+           }
+       }
+
       if (gfc_match_eos () == MATCH_YES)
        break;
 
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 8328ff8b34bf..d8d8f3faae24 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -877,7 +877,13 @@ enum gfc_omp_at_type
   OMP_AT_EXECUTION
 };
 
-/* Structure and list of supported extension attributes.  */
+/* Structure and list of supported extension attributes.
+
+   The bitmask formed from these values (symbol_attribute.ext_attr) is
+   written to and read from module files, see mio_symbol_attribute.  New
+   attributes must therefore be appended at the end (before EXT_ATTR_LAST)
+   so that the existing bit positions, and thus module compatibility, are
+   preserved.  */
 typedef enum
 {
   EXT_ATTR_DLLIMPORT = 0,
@@ -890,6 +896,8 @@ typedef enum
   EXT_ATTR_NOINLINE,
   EXT_ATTR_NORETURN,
   EXT_ATTR_WEAK,
+  EXT_ATTR_INLINE,
+  EXT_ATTR_ALWAYS_INLINE,
   EXT_ATTR_LAST, EXT_ATTR_NUM = EXT_ATTR_LAST
 }
 ext_attr_id_t;
diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi
index 12b95f4b19b7..c9f45df1882f 100644
--- a/gcc/fortran/gfortran.texi
+++ b/gcc/fortran/gfortran.texi
@@ -3404,6 +3404,14 @@ 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{INLINE} -- hint that the given function should be inlined, while
+still respecting the inlining size limits, corresponding to the C @code{inline}
+keyword.
+@item @code{ALWAYS_INLINE} -- 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{INLINE} and
+@code{ALWAYS_INLINE} are incompatible with @code{NOINLINE}.  Specifying both
+for the same procedure makes the inline attribute 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 ea12be5dd258..42a5c06d5f0c 100644
--- a/gcc/fortran/trans-decl.cc
+++ b/gcc/fortran/trans-decl.cc
@@ -2673,6 +2673,16 @@ build_function_decl (gfc_symbol * sym, bool global)
   if (attr.ext_attr & (1 << EXT_ATTR_NOINLINE))
     DECL_UNINLINABLE (fndecl) = 1;
 
+  /* Mark inline functions.  Fortran has no 'inline' keyword, so both INLINE
+     and ALWAYS_INLINE set DECL_DECLARED_INLINE_P explicitly.  ALWAYS_INLINE
+     additionally disregards the inliner's size limits.  Setting only that
+     would make the middle-end warn that the always-inline function "might
+     not be inlinable".  */
+  if (attr.ext_attr & ((1 << EXT_ATTR_INLINE) | (1 << EXT_ATTR_ALWAYS_INLINE)))
+    DECL_DECLARED_INLINE_P (fndecl) = 1;
+  if (attr.ext_attr & (1 << EXT_ATTR_ALWAYS_INLINE))
+    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/always_inline_1.f90 
b/gcc/testsuite/gfortran.dg/always_inline_1.f90
new file mode 100644
index 000000000000..fce9ce0781e8
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/always_inline_1.f90
@@ -0,0 +1,26 @@
+! { dg-do compile }
+! { dg-options "-O -fdump-tree-optimized" }
+!
+! Verify that !GCC$ ATTRIBUTES always_inline 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 always_inline :: 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/always_inline_2.f90 
b/gcc/testsuite/gfortran.dg/always_inline_2.f90
new file mode 100644
index 000000000000..2f6040865e6c
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/always_inline_2.f90
@@ -0,0 +1,14 @@
+! { dg-do compile }
+!
+! ALWAYS_INLINE is incompatible with NOINLINE.  Specifying both for the same
+! procedure must warn and drop ALWAYS_INLINE, whether they appear in one
+! directive or in separate directives.
+
+subroutine foo()
+!GCC$ ATTRIBUTES always_inline, noinline :: foo  ! { dg-warning 
"ALWAYS_INLINE. at .1. is incompatible with .NOINLINE." }
+end subroutine foo
+
+subroutine bar()
+!GCC$ ATTRIBUTES always_inline :: bar
+!GCC$ ATTRIBUTES noinline :: bar  ! { dg-warning "ALWAYS_INLINE. at .1. is 
incompatible with .NOINLINE." }
+end subroutine bar
diff --git a/gcc/testsuite/gfortran.dg/inline_1.f90 
b/gcc/testsuite/gfortran.dg/inline_1.f90
new file mode 100644
index 000000000000..6dc59bf6e2a1
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/inline_1.f90
@@ -0,0 +1,27 @@
+! { dg-do compile }
+! { dg-options "-O2 -fno-inline-small-functions -fno-inline-functions 
-fno-inline-functions-called-once -fdump-tree-optimized" }
+!
+! With all automatic inlining disabled the helper procedure would not be
+! inlined, but the INLINE attribute marks it as declared-inline, so it is
+! inlined anyway 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 inline :: 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/inline_2.f90 
b/gcc/testsuite/gfortran.dg/inline_2.f90
new file mode 100644
index 000000000000..60da68c947b7
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/inline_2.f90
@@ -0,0 +1,14 @@
+! { dg-do compile }
+!
+! INLINE is incompatible with NOINLINE.  Specifying both for the same
+! procedure must warn and drop INLINE, whether they appear in one directive
+! or in separate directives.
+
+subroutine foo()
+!GCC$ ATTRIBUTES inline, noinline :: foo  ! { dg-warning "INLINE. at .1. is 
incompatible with .NOINLINE." }
+end subroutine foo
+
+subroutine bar()
+!GCC$ ATTRIBUTES inline :: bar
+!GCC$ ATTRIBUTES noinline :: bar  ! { dg-warning "INLINE. at .1. is 
incompatible with .NOINLINE." }
+end subroutine bar

Reply via email to