When APX EGPR is active, return false for loops containing indirect
calls to keep them as separate IRA regions.

gcc/ChangeLog:

        * config/i386/i386.cc (ix86_ira_low_pressure_loop_p): New.
        (TARGET_IRA_LOW_PRESSURE_LOOP_P): Define.

gcc/testsuite/ChangeLog:

        * gcc.target/i386/apx-ira-loop-indirect-call.c: New test.
---
 gcc/config/i386/i386.cc                       | 40 ++++++++
 .../i386/apx-ira-loop-indirect-call.c         | 91 +++++++++++++++++++
 2 files changed, 131 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/i386/apx-ira-loop-indirect-call.c

diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index b1fd86c2b32..0776f7e0b14 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -27317,6 +27317,43 @@ ix86_simd_clone_usable (struct cgraph_node *node, 
machine_mode)
     }
 }
 
+/* TARGET_IRA_LOW_PRESSURE_LOOP_P -- when APX EGPR is active, scan the
+   loop for indirect calls.  If found, return false to keep the loop as
+   a separate IRA region so that allocnos crossing those calls receive
+   callee-saved registers.  */
+static bool
+ix86_ira_low_pressure_loop_p (class loop *loop)
+{
+  if (!TARGET_APX_EGPR)
+    return true;
+
+  basic_block *bbs = get_loop_body (loop);
+  unsigned nbbs = loop->num_nodes;
+  bool has_indirect = false;
+
+  for (unsigned i = 0; i < nbbs && !has_indirect; i++)
+    {
+      rtx_insn *insn;
+      FOR_BB_INSNS (bbs[i], insn)
+       if (CALL_P (insn))
+         {
+           rtx call = get_call_rtx_from (insn);
+           if (call)
+             {
+               rtx addr = XEXP (call, 0);
+               if (!MEM_P (addr) || !SYMBOL_REF_P (XEXP (addr, 0)))
+                 {
+                   has_indirect = true;
+                   break;
+                 }
+             }
+         }
+    }
+
+  free (bbs);
+  return !has_indirect;
+}
+
 /* This function adjusts the unroll factor based on
    the hardware capabilities. For ex, bdver3 has
    a loop buffer which makes unrolling of smaller
@@ -28733,6 +28770,9 @@ ix86_libgcc_floating_mode_supported_p
 #undef TARGET_CANONICALIZE_COMPARISON
 #define TARGET_CANONICALIZE_COMPARISON ix86_canonicalize_comparison
 
+#undef TARGET_IRA_LOW_PRESSURE_LOOP_P
+#define TARGET_IRA_LOW_PRESSURE_LOOP_P ix86_ira_low_pressure_loop_p
+
 #undef TARGET_LOOP_UNROLL_ADJUST
 #define TARGET_LOOP_UNROLL_ADJUST ix86_loop_unroll_adjust
 
diff --git a/gcc/testsuite/gcc.target/i386/apx-ira-loop-indirect-call.c 
b/gcc/testsuite/gcc.target/i386/apx-ira-loop-indirect-call.c
new file mode 100644
index 00000000000..1c2d98cb488
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/apx-ira-loop-indirect-call.c
@@ -0,0 +1,91 @@
+/* { dg-do compile  { target { ! ia32 } } } */
+/* { dg-options "-O2 -mapxf" } */
+
+/* Verify that with APX EGPR the TARGET_IRA_LOW_PRESSURE_LOOP_P hook
+   prevents caller-saved spills for pseudos live across indirect calls
+   inside loops with high register pressure.  */
+
+typedef double fp_t;
+typedef struct { void (*process)(const void *, void *); } vtable;
+typedef struct { unsigned int id; void *next; vtable *vt; } item;
+typedef struct item_list { struct item_list *next; item *obj; } item_list;
+
+typedef struct {
+  int dim_x, dim_y;
+  fp_t origin_x, origin_y, step_x, step_y;
+  item_list **buckets;
+} spatial_grid;
+
+typedef struct {
+  fp_t pos_x, pos_y, dir_x, dir_y, limit;
+  unsigned int flags;
+  unsigned long seq;
+  unsigned long *seen;
+} query;
+
+extern int check_bounds(const spatial_grid *, const query *, fp_t *, fp_t *);
+
+void traverse_grid(const spatial_grid *grid, query *q) {
+  fp_t tnear, tfar, tmax_x, tmax_y, tdelta_x, tdelta_y, cx, cy;
+  int cur_x, cur_y, inc_x, inc_y, end_x, end_y;
+  long idx, stride_y;
+  unsigned long seq = q->seq;
+  unsigned long *seen = q->seen;
+  item_list *cur;
+
+  if (!check_bounds(grid, q, &tnear, &tfar))
+    return;
+
+  cx = q->pos_x + q->dir_x * tnear;
+  cy = q->pos_y + q->dir_y * tnear;
+  cur_x = (int)((cx - grid->origin_x) / grid->step_x);
+  cur_y = (int)((cy - grid->origin_y) / grid->step_y);
+
+  tmax_x = tnear + ((cur_x * grid->step_x + grid->origin_x) - cx) / q->dir_x;
+  tdelta_x = grid->step_x / q->dir_x;
+  inc_x = (q->dir_x > 0) ? 1 : -1;
+  end_x = (q->dir_x > 0) ? grid->dim_x : -1;
+
+  tmax_y = tnear + ((cur_y * grid->step_y + grid->origin_y) - cy) / q->dir_y;
+  tdelta_y = grid->step_y / q->dir_y;
+  inc_y = (q->dir_y > 0) ? 1 : -1;
+  end_y = (q->dir_y > 0) ? grid->dim_y : -1;
+
+  stride_y = inc_y * (long)grid->dim_x;
+  idx = cur_y * (long)grid->dim_x + cur_x;
+
+  cur = grid->buckets[idx];
+  while (cur != 0) {
+    if (seen[cur->obj->id] != seq) {
+      seen[cur->obj->id] = seq;
+      cur->obj->vt->process(cur->obj, q);
+    }
+    cur = cur->next;
+  }
+
+  for (;;) {
+    if (tmax_x < tmax_y) {
+      cur_x += inc_x;
+      if (q->limit < tmax_x || cur_x == end_x) break;
+      tmax_x += tdelta_x;
+      idx += inc_x;
+    } else {
+      cur_y += inc_y;
+      if (q->limit < tmax_y || cur_y == end_y) break;
+      tmax_y += tdelta_y;
+      idx += stride_y;
+    }
+
+    cur = grid->buckets[idx];
+    while (cur != 0) {
+      if (seen[cur->obj->id] != seq) {
+        seen[cur->obj->id] = seq;
+        cur->obj->vt->process(cur->obj, q);
+      }
+      cur = cur->next;
+    }
+  }
+}
+
+/* No caller-saved spills around indirect calls in the loop.  */
+/* { dg-final { scan-assembler-not "movq\t\[0-9\]*\\(%rsp\\), 
%(rax|rcx|rdx|rsi|rdi|r8|r9|r10|r11|r1\[6-9\]|r2\[0-9\]|r3\[01\])" } } */
-- 
2.31.1

Reply via email to