https://gcc.gnu.org/g:2f8a386c305711c987b82baeb9b8ba7fe281941b

commit 2f8a386c305711c987b82baeb9b8ba7fe281941b
Author: Paul-Antoine Arras <[email protected]>
Date:   Fri Oct 16 11:42:56 2026 +0200

    amdgcn: Use the real register budget for non-kernel functions
    
    The loop optimizers (ivopts, loop-invariant, predcom) estimate the number of
    available registers from target_avail_regs, which init_set_costs computes 
once
    from the non-fixed GENERAL_REGS, with no function in context.  GCN only
    restricts the registers of non-kernel functions (to MAX_NORMAL_*) in
    gcn_conditional_register_usage once a function is current, so the count was
    always the one for kernels: 78 SGPRs, while IRA only gets 44 in a non-kernel
    function such as an outlined OpenMP body.  Nothing recomputes the count
    afterwards, since reinit_regs does not touch the loop cost tables.
    
    Fix this by making GCN a SWITCHABLE_TARGET, as MIPS does for MIPS16.  
Kernels
    (and code with no current function) use default_target_globals; non-kernel
    functions use a second set, created lazily with
    save_target_globals_default_opts and selected from
    TARGET_SET_CURRENT_FUNCTION.  The register-usage hook runs with the 
non-kernel
    function current when that set is created, so the restricted fixed_regs end 
up
    in it, and init_set_costs, run by initialize_rtl before the first GIMPLE 
pass
    of the first function expanded in that set, computes target_avail_regs from
    them.  Switching between the two sets afterwards is a pointer swap.
    
    gcn_conditional_register_usage now decides kernel-ness from the declaration
    (new gcn_is_kernel) rather than from cfun->machine->normal_function, which 
is
    only set at expand time.  The cached set mixes GC and non-GC data, so it is
    dropped before a PCH is written, as MIPS does.
    
    This also changes what loop-invariant's estimate_reg_pressure_cost and 
predcom's
    MAX_DISTANCE see for non-kernel functions, which is the intended behaviour.
    
    Known limitation: target_avail_regs only counts GENERAL_REGS, i.e. SGPRs on
    GCN; VGPRs are not accounted for.
    
    gcc/ChangeLog:
    
            * config/gcn/gcn.h (SWITCHABLE_TARGET): Define.
            * config/gcn/gcn.cc: Include target-globals.h.
            (normal_function_globals, old_is_kernel): New static variables.
            (gcn_is_kernel): New function.
            (gcn_set_current_function): New function.
            (gcn_prepare_pch_save): New function.
            (gcn_conditional_register_usage): Use gcn_is_kernel to detect
            non-kernel functions and do not require cfun->machine for them.
            (TARGET_SET_CURRENT_FUNCTION, TARGET_PREPARE_PCH_SAVE): Define.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/gcn/ivopts-reg-budget-1.c: New test.
    
    (cherry picked from commit 6cdf9af5c4dae269be023e0493448bb00c98251a)

Diff:
---
 gcc/config/gcn/gcn.cc                              | 73 +++++++++++++++++++++-
 gcc/config/gcn/gcn.h                               |  3 +
 gcc/testsuite/gcc.target/gcn/ivopts-reg-budget-1.c | 35 +++++++++++
 3 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/gcc/config/gcn/gcn.cc b/gcc/config/gcn/gcn.cc
index 9c305398c782..bf25abed321d 100644
--- a/gcc/config/gcn/gcn.cc
+++ b/gcc/config/gcn/gcn.cc
@@ -55,6 +55,7 @@
 #include "cgraph.h"
 #include "case-cfn-macros.h"
 #include "opts.h"
+#include "target-globals.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -109,6 +110,13 @@ const struct gcn_device_def gcn_devices[] = {
 #include "gcn-devices.def"
 };
 
+/* Target state for non-kernel, normal functions. (Kernels use
+   default_target_globals.)  */
+static GTY (()) struct target_globals *normal_function_globals;
+
+/* Whether the last function passed to gcn_set_current_function was a kernel. 
*/
+static bool old_is_kernel = true;
+
 /* }}}  */
 /* {{{ Initialization and options.  */
 
@@ -1492,6 +1500,26 @@ gcn_stepped_zero_int_parallel_p (rtx op, int step)
   return true;
 }
 
+/* Return true if FNDECL is a kernel function.  A function is treated as a
+   kernel if it has the "amdgpu_hsa_kernel" attribute, or it is the main
+   function, or it has the "omp target entrypoint" attribute.  */
+
+static bool
+gcn_is_kernel (tree fndecl)
+{
+  if (!fndecl)
+    return true;
+
+  tree attr = lookup_attribute ("amdgpu_hsa_kernel", DECL_ATTRIBUTES (fndecl));
+  if (!attr)
+    attr = lookup_attribute ("amdgpu_hsa_kernel",
+                            TYPE_ATTRIBUTES (TREE_TYPE (fndecl)));
+
+  return attr || (DECL_NAME (fndecl) && MAIN_NAME_P (DECL_NAME (fndecl)))
+        || lookup_attribute ("omp target entrypoint", DECL_ATTRIBUTES (fndecl))
+             != NULL_TREE;
+}
+
 /* }}}  */
 /* {{{ Addresses, pointers and moves.  */
 
@@ -2604,10 +2632,11 @@ gcn_conditional_register_usage (void)
        i++)
     fixed_regs[VGPR_REGNO (i)] = call_used_regs[VGPR_REGNO (i)] = 1;
 
-  if (!cfun || !cfun->machine)
+  if (!cfun)
     return;
 
-  if (cfun->machine->normal_function)
+  if (!gcn_is_kernel (cfun->decl)
+      || (cfun->machine && cfun->machine->normal_function))
     {
       /* Restrict the set of SGPRs, VGPRs and AVGPRs used by non-kernel
         functions.  */
@@ -2625,6 +2654,9 @@ gcn_conditional_register_usage (void)
       return;
     }
 
+  if (!cfun->machine)
+    return;
+
   /* If the set of requested args is the default set, nothing more needs to
      be done.  */
   if (cfun->machine->args.requested == default_requested_args)
@@ -4110,6 +4142,39 @@ gcn_expand_divmod_libfunc (rtx libfunc, machine_mode 
mode, rtx op0, rtx op1,
                      RETURN_VALUE_REG + (wideinnermode == TImode ? 2 : 1));
 }
 
+/* Implement TARGET_SET_CURRENT_FUNCTION.  Set up the target-dependent global
+   state depending on whether the current function is a kernel.  */
+
+static void
+gcn_set_current_function (tree fndecl)
+{
+  bool is_kernel = gcn_is_kernel (fndecl);
+  if (is_kernel == old_is_kernel)
+    return;
+
+  if (!is_kernel)
+    {
+      if (!normal_function_globals)
+       normal_function_globals = save_target_globals_default_opts ();
+      else
+       restore_target_globals (normal_function_globals);
+    }
+  else
+    restore_target_globals (&default_target_globals);
+
+  old_is_kernel = is_kernel;
+}
+
+/* Implement TARGET_PREPARE_PCH_SAVE.  */
+
+static void
+gcn_prepare_pch_save (void)
+{
+  restore_target_globals (&default_target_globals);
+  normal_function_globals = NULL;
+  old_is_kernel = true;
+}
+
 /* }}}  */
 /* {{{ Miscellaneous.  */
 
@@ -8738,6 +8803,8 @@ gcn_output_ensure_scalar (rtx *operands, machine_mode 
mode)
 #define TARGET_MODES_TIEABLE_P gcn_modes_tieable_p
 #undef  TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE gcn_option_override
+#undef  TARGET_PREPARE_PCH_SAVE
+#define TARGET_PREPARE_PCH_SAVE gcn_prepare_pch_save
 #undef  TARGET_PRETEND_OUTGOING_VARARGS_NAMED
 #define TARGET_PRETEND_OUTGOING_VARARGS_NAMED \
   gcn_pretend_outgoing_varargs_named
@@ -8755,6 +8822,8 @@ gcn_output_ensure_scalar (rtx *operands, machine_mode 
mode)
 #define TARGET_SECTION_TYPE_FLAGS gcn_section_type_flags
 #undef  TARGET_SCALAR_MODE_SUPPORTED_P
 #define TARGET_SCALAR_MODE_SUPPORTED_P gcn_scalar_mode_supported_p
+#undef  TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION gcn_set_current_function
 #undef  TARGET_SIMD_CLONE_ADJUST
 #define TARGET_SIMD_CLONE_ADJUST gcn_simd_clone_adjust
 #undef  TARGET_SIMD_CLONE_COMPUTE_VECSIZE_AND_SIMDLEN
diff --git a/gcc/config/gcn/gcn.h b/gcc/config/gcn/gcn.h
index a758280a8403..fcc9728d3e26 100644
--- a/gcc/config/gcn/gcn.h
+++ b/gcc/config/gcn/gcn.h
@@ -919,3 +919,6 @@ enum gcn_builtin_codes
       || M == V2SFmode || M == V2DImode || M == V2DFmode) \
    ? 2 \
    : 1)
+
+/* For switching between kernel and regular function.  */
+#define SWITCHABLE_TARGET 1
diff --git a/gcc/testsuite/gcc.target/gcn/ivopts-reg-budget-1.c 
b/gcc/testsuite/gcc.target/gcn/ivopts-reg-budget-1.c
new file mode 100644
index 000000000000..378f93e1a069
--- /dev/null
+++ b/gcc/testsuite/gcc.target/gcn/ivopts-reg-budget-1.c
@@ -0,0 +1,35 @@
+/* Non-kernel functions may only use the first MAX_NORMAL_SGPR_COUNT SGPRs
+   (see gcn_conditional_register_usage), so the register budget seen by the
+   loop optimizers must be smaller for them than for kernels.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-ivopts-details" } */
+
+/* A normal function.  It is expanded before main, i.e. it is the first
+   function for which RTL is initialized.  */
+
+__attribute__((noinline)) void
+f (double *a, double *b, int n)
+{
+  for (int i = 0; i < n; i++)
+    a[i] = b[i] * 2.0;
+}
+
+double *x, *y;
+int n;
+
+/* main is compiled as a kernel.  */
+
+int
+main ()
+{
+  f (x, y, n);
+  for (int i = 0; i < n; i++)
+    x[i] += y[i];
+  return 0;
+}
+
+/* { dg-final { scan-assembler "\\.amdhsa_kernel\\s+main" } } */
+/* { dg-final { scan-assembler-not "\\.amdhsa_kernel\\s+f\\M" } } */
+/* { dg-final { scan-tree-dump-times "target_avail_regs 44" 1 "ivopts" } } */
+/* { dg-final { scan-tree-dump-times "target_avail_regs 78" 1 "ivopts" } } */

Reply via email to