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