aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorRichard Sandiford <richard.sandiford@arm.com>2025-08-18 11:12:57 +0100
committerRichard Sandiford <richard.sandiford@arm.com>2025-08-18 11:12:57 +0100
commit4a56ba8c8ec281ef794a598f64a5707204ca9088 (patch)
tree0c833a7c1de73185cfee3842176780f96c492b1f /gcc
parentcc54f2f47e63c9d404a44f618cf114ae63e81b40 (diff)
downloadgcc-4a56ba8c8ec281ef794a598f64a5707204ca9088.zip
gcc-4a56ba8c8ec281ef794a598f64a5707204ca9088.tar.gz
gcc-4a56ba8c8ec281ef794a598f64a5707204ca9088.tar.bz2
gcse: Fix handling of partial clobbers [PR97497]
This patch fixes an internal disagreement in gcse about how to handle partial clobbers. Like many passes, gcse doesn't track the modes of live values, so if a call clobbers only part of a register, the pass has to make conservative assumptions. As the comment in the patch says, this means: (1) ignoring partial clobbers when computing liveness and reaching definitions (2) treating partial clobbers as full clobbers when computing availability DF is mostly concerned with (1), so ignores partial clobbers. compute_hash_table_work did (2) when calculating kill sets, but compute_transp didn't do (2) when computing transparency. This led to a nonsensical situation of a register being in both the transparency and kill sets. gcc/ PR rtl-optimization/97497 * function-abi.h (predefined_function_abi::only_partial_reg_clobbers) (function_abi::only_partial_reg_clobbers): New member functions. * gcse-common.cc: Include regs.h and function-abi.h. (compute_transp): Check for partially call-clobbered registers and treat them as not being transparent in blocks with calls.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/function-abi.h39
-rw-r--r--gcc/gcse-common.cc40
2 files changed, 71 insertions, 8 deletions
diff --git a/gcc/function-abi.h b/gcc/function-abi.h
index edf83c1..6cb552f 100644
--- a/gcc/function-abi.h
+++ b/gcc/function-abi.h
@@ -99,6 +99,39 @@ public:
return m_full_and_partial_reg_clobbers;
}
+ /* Return the set of registers that a function call is allowed to alter
+ partially but not fully; i.e. those in full_and_partial_reg_clobbers ()
+ but not in full_reg_clobbers ().
+
+ If a register X is in this set and if we don't know which parts of
+ X are live (typically because we don't bother to track X's mode),
+ then the conservative assumptions are:
+
+ - to ignore the call when computing reaching definitions
+ - to treat X as clobbered when computing availability
+
+ For example, if we have:
+
+ A: X := Y
+ B: call that partially clobbers X
+ C: ... := ... X ...
+
+ and don't track the mode of X when computing reaching definitions,
+ then the conservative assumption is that A's definition survives
+ until C. But if we have:
+
+ A: X := Y
+ B: call that partially clobbers X
+ C: ... := ... Y ...
+
+ and don't track the mode of X when computing availability, then the
+ conservative assumption is that Y is not available in X at C. */
+ HARD_REG_SET
+ only_partial_reg_clobbers () const
+ {
+ return full_and_partial_reg_clobbers () & ~full_reg_clobbers ();
+ }
+
/* Return the set of registers that cannot be used to hold a value of
mode MODE across a function call. That is:
@@ -166,6 +199,12 @@ public:
}
HARD_REG_SET
+ only_partial_reg_clobbers () const
+ {
+ return m_mask & m_base_abi->only_partial_reg_clobbers ();
+ }
+
+ HARD_REG_SET
mode_clobbers (machine_mode mode) const
{
return m_mask & m_base_abi->mode_clobbers (mode);
diff --git a/gcc/gcse-common.cc b/gcc/gcse-common.cc
index 888e4d6..9ce9bb5 100644
--- a/gcc/gcse-common.cc
+++ b/gcc/gcse-common.cc
@@ -27,7 +27,8 @@
#include "rtl.h"
#include "df.h"
#include "gcse-common.h"
-
+#include "regs.h"
+#include "function-abi.h"
/* Record all of the canonicalized MEMs of record_last_mem_set_info's insn.
Note we store a pair of elements in the list, so they have to be
@@ -129,13 +130,36 @@ compute_transp (const_rtx x, int indx, sbitmap *bmap,
switch (code)
{
case REG:
- {
- df_ref def;
- for (def = DF_REG_DEF_CHAIN (REGNO (x));
- def;
- def = DF_REF_NEXT_REG (def))
- bitmap_clear_bit (bmap[DF_REF_BB (def)->index], indx);
- }
+ {
+ df_ref def;
+ for (def = DF_REG_DEF_CHAIN (REGNO (x));
+ def;
+ def = DF_REF_NEXT_REG (def))
+ bitmap_clear_bit (bmap[DF_REF_BB (def)->index], indx);
+
+ /* Check for hard registers that are partially clobbered (but not
+ fully clobbered) by calls. Such partial clobbers do not have
+ an associated definition or use in the DF representation,
+ but they do prevent the register from being transparent.
+
+ ??? The check here is fairly crude. We could instead maintain
+ separate blocks_with_calls bitmaps for each ABI. */
+ if (HARD_REGISTER_P (x))
+ for (unsigned int i = 0; i < NUM_ABI_IDS; ++i)
+ {
+ const predefined_function_abi &abi = function_abis[i];
+ if (abi.initialized_p ()
+ && overlaps_hard_reg_set_p (abi.only_partial_reg_clobbers (),
+ GET_MODE (x), REGNO (x)))
+ {
+ bitmap_iterator bi;
+ unsigned bb_index;
+ EXECUTE_IF_SET_IN_BITMAP (blocks_with_calls, 0, bb_index, bi)
+ bitmap_clear_bit (bmap[bb_index], indx);
+ break;
+ }
+ }
+ }
return;