aboutsummaryrefslogtreecommitdiff
path: root/gcc/ira-int.h
diff options
context:
space:
mode:
authorRichard Sandiford <richard.sandiford@arm.com>2022-01-10 14:47:08 +0000
committerRichard Sandiford <richard.sandiford@arm.com>2022-01-10 14:47:08 +0000
commit8e7a23728f66d2da88b47e34224410457fdefbf5 (patch)
tree5ce6592a88faae7ccb7c4156714217ca74a684de /gcc/ira-int.h
parentd54565d87ff79b882208dfb29af50232033c233d (diff)
downloadgcc-8e7a23728f66d2da88b47e34224410457fdefbf5.zip
gcc-8e7a23728f66d2da88b47e34224410457fdefbf5.tar.gz
gcc-8e7a23728f66d2da88b47e34224410457fdefbf5.tar.bz2
ira: Try to avoid propagating conflicts
Suppose that: - an inner loop L contains an allocno A - L clobbers hard register R while A is live - A's parent allocno is AP Previously, propagate_allocno_info would propagate conflict sets up the loop tree, so that the conflict between A and R would become a conflict between AP and R (and so on for ancestors of AP). However, when IRA treats loops as separate allocation regions, it can decide on a loop-by-loop basis whether to allocate a register or spill to memory. Conflicts in inner loops therefore don't need to become hard conflicts in parent loops. Instead we can record that using the “conflicting” registers for the parent allocnos has a higher cost. In the example above, this higher cost is the sum of: - the cost of saving R on entry to L - the cost of keeping the pseudo register in memory throughout L - the cost of reloading R on exit from L This value is also a cap on the hard register cost that A can contribute to AP in general (not just for conflicts). Whatever allocation we pick for AP, there is always the option of spilling that register to memory throughout L, so the cost to A of allocating a register to AP can't be more than the cost of spilling A. To take an extreme example: if allocating a register R2 to A is more expensive than spilling A to memory, ALLOCNO_HARD_REG_COSTS (A)[R2] could be (say) 2 times greater than ALLOCNO_MEMORY_COST (A) or 100 times greater than ALLOCNO_MEMORY_COST (A). But this scale factor doesn't matter to AP. All that matters is that R2 is more expensive than memory for A, so that allocating R2 to AP should be costed as spilling A to memory (again assuming that A and AP are in different allocation regions). Propagating a factor of 100 would distort the register costs for AP. move_spill_restore tries to undo the propagation done by propagate_allocno_info, so we need some extra processing there. gcc/ PR rtl-optimization/98782 * ira-int.h (ira_allocno::might_conflict_with_parent_p): New field. (ALLOCNO_MIGHT_CONFLICT_WITH_PARENT_P): New macro. (ira_single_region_allocno_p): New function. (ira_total_conflict_hard_regs): Likewise. * ira-build.c (ira_create_allocno): Initialize ALLOCNO_MIGHT_CONFLICT_WITH_PARENT_P. (ira_propagate_hard_reg_costs): New function. (propagate_allocno_info): Use it. Try to avoid propagating hard register conflicts to parent allocnos if we can handle the conflicts by spilling instead. Limit the propagated register costs to the cost of spilling throughout the child loop. * ira-color.c (color_pass): Use ira_single_region_allocno_p to test whether a child and parent allocno can share the same register. (move_spill_restore): Adjust for the new behavior of propagate_allocno_info. gcc/testsuite/ * gcc.target/aarch64/reg-alloc-2.c: New test.
Diffstat (limited to 'gcc/ira-int.h')
-rw-r--r--gcc/ira-int.h37
1 files changed, 37 insertions, 0 deletions
diff --git a/gcc/ira-int.h b/gcc/ira-int.h
index c5b1a13..8b87498 100644
--- a/gcc/ira-int.h
+++ b/gcc/ira-int.h
@@ -314,6 +314,13 @@ struct ira_allocno
vector where a bit with given index represents allocno with the
same number. */
unsigned int conflict_vec_p : 1;
+ /* True if the parent loop has an allocno for the same register and
+ if the parent allocno's assignment might not be valid in this loop.
+ This means that we cannot merge this allocno and the parent allocno
+ together.
+
+ This is only ever true for non-cap allocnos. */
+ unsigned int might_conflict_with_parent_p : 1;
/* Hard register assigned to given allocno. Negative value means
that memory was allocated to the allocno. During the reload,
spilled allocno has value equal to the corresponding stack slot
@@ -423,6 +430,8 @@ struct ira_allocno
#define ALLOCNO_CAP_MEMBER(A) ((A)->cap_member)
#define ALLOCNO_NREFS(A) ((A)->nrefs)
#define ALLOCNO_FREQ(A) ((A)->freq)
+#define ALLOCNO_MIGHT_CONFLICT_WITH_PARENT_P(A) \
+ ((A)->might_conflict_with_parent_p)
#define ALLOCNO_HARD_REGNO(A) ((A)->hard_regno)
#define ALLOCNO_CALL_FREQ(A) ((A)->call_freq)
#define ALLOCNO_CALLS_CROSSED_NUM(A) ((A)->calls_crossed_num)
@@ -1623,4 +1632,32 @@ ira_subloop_allocnos_can_differ_p (ira_allocno_t a, bool allocated_p = true)
return true;
}
+/* Return true if we should treat A and SUBLOOP_A as belonging to a
+ single region. */
+inline bool
+ira_single_region_allocno_p (ira_allocno_t a, ira_allocno_t subloop_a)
+{
+ if (flag_ira_region != IRA_REGION_MIXED)
+ return false;
+
+ if (ALLOCNO_MIGHT_CONFLICT_WITH_PARENT_P (subloop_a))
+ return false;
+
+ auto rclass = ALLOCNO_CLASS (a);
+ auto pclass = ira_pressure_class_translate[rclass];
+ auto loop_used_regs = ALLOCNO_LOOP_TREE_NODE (a)->reg_pressure[pclass];
+ return loop_used_regs <= ira_class_hard_regs_num[pclass];
+}
+
+/* Return the set of all hard registers that conflict with A. */
+inline HARD_REG_SET
+ira_total_conflict_hard_regs (ira_allocno_t a)
+{
+ auto obj_0 = ALLOCNO_OBJECT (a, 0);
+ HARD_REG_SET conflicts = OBJECT_TOTAL_CONFLICT_HARD_REGS (obj_0);
+ for (int i = 1; i < ALLOCNO_NUM_OBJECTS (a); i++)
+ conflicts |= OBJECT_TOTAL_CONFLICT_HARD_REGS (ALLOCNO_OBJECT (a, i));
+ return conflicts;
+}
+
#endif /* GCC_IRA_INT_H */