Merged
[Backport] ira: Consider save/restore costs of callee-save registers #397
[Backport] ira: Consider save/restore costs of callee-save registers #397
Merged
haibaooAdd on Aug 23
Total: 24 files changed+752-38
@@ -5359,10 +5359,23 @@ aarch64_hard_regno_call_part_clobbered (unsigned int abi_id,
5359{5359{
5360 if (FP_REGNUM_P (regno) && abi_id != ARM_PCS_SVE)5360 if (FP_REGNUM_P (regno) && abi_id != ARM_PCS_SVE)
5361 {5361 {
5362- poly_int64 per_register_size = GET_MODE_SIZE (mode);5362+ poly_int64 per_register_size;
5363- unsigned int nregs = hard_regno_nregs (regno, mode);5363+ if (aarch64_sve_mode_p (mode))
5364- if (nregs > 1)5364+ /* SVE instructions operate on the full SVE register, even for
5365- per_register_size = exact_div (per_register_size, nregs);5365+ partial modes like VNx2SF whose GET_MODE_SIZE is only 8 bytes
5366+ under -msve-vector-bits=128. The elements of such partial
5367+ modes are strided across the register and can lie outside the
5368+ callee-preserved low 64 bits. Compare against the full SVE
5369+ vector size so that V8-V15 are correctly recognised as
5370+ partially clobbered for any SVE mode under AAPCS64/AAPCS_SIMD. */
5371+ per_register_size = BYTES_PER_SVE_VECTOR;
5372+ else
5373+ {
5374+ per_register_size = GET_MODE_SIZE (mode);
5375+ unsigned int nregs = hard_regno_nregs (regno, mode);
5376+ if (nregs > 1)
5377+ per_register_size = exact_div (per_register_size, nregs);
5378+ }
5366 if (abi_id == ARM_PCS_SIMD || abi_id == ARM_PCS_TLSDESC)5379 if (abi_id == ARM_PCS_SIMD || abi_id == ARM_PCS_TLSDESC)
5367 return maybe_gt (per_register_size, 16);5380 return maybe_gt (per_register_size, 16);
5368 return maybe_gt (per_register_size, 8);5381 return maybe_gt (per_register_size, 8);
@@ -17785,6 +17798,119 @@ aarch64_memory_move_cost (machine_mode mode, reg_class_t rclass_i, bool in)
17785 : aarch64_tune_params.memmov_cost.store_int);17798 : aarch64_tune_params.memmov_cost.store_int);
17786}17799}
17787 17800 
17801+/* CALLEE_SAVED_REGS is the set of callee-saved registers that the
17802+ RA has already decided to use. Return the total number of registers
17803+ in class RCLASS that need to be saved and restored, including the
17804+ frame link registers. */
17805+static int
17806+aarch64_count_saves (const HARD_REG_SET &callee_saved_regs,
17807+ enum reg_class rclass)
17808+{
17809+ auto saved_gprs = callee_saved_regs & reg_class_contents[rclass];
17810+ auto nregs = hard_reg_set_popcount (saved_gprs);
17811+ 
17812+ if (TEST_HARD_REG_BIT (reg_class_contents[rclass], LR_REGNUM))
17813+ {
17814+ if (aarch64_needs_frame_chain ())
17815+ nregs += 2;
17816+ else if (!crtl->is_leaf || df_regs_ever_live_p (LR_REGNUM))
17817+ nregs += 1;
17818+ }
17819+ return nregs;
17820+}
17821+ 
17822+/* CALLEE_SAVED_REGS is the set of callee-saved registers that the
17823+ RA has already decided to use. Return the total number of registers
17824+ that need to be saved above the hard frame pointer, including the
17825+ frame link registers. */
17826+static int
17827+aarch64_count_above_hard_fp_saves (const HARD_REG_SET &callee_saved_regs)
17828+{
17829+ /* FP and Advanced SIMD registers are saved above the frame pointer
17830+ but SVE registers are saved below it. */
17831+ if (known_le (GET_MODE_SIZE (aarch64_reg_save_mode (V8_REGNUM)), 16U))
17832+ return aarch64_count_saves (callee_saved_regs, POINTER_AND_FP_REGS);
17833+ return aarch64_count_saves (callee_saved_regs, POINTER_REGS);
17834+}
17835+ 
17836+/* Implement TARGET_CALLEE_SAVE_COST. */
17837+static int
17838+aarch64_callee_save_cost (spill_cost_type spill_type, unsigned int regno,
17839+ machine_mode mode, unsigned int nregs, int mem_cost,
17840+ const HARD_REG_SET &callee_saved_regs,
17841+ bool existing_spill_p)
17842+{
17843+ /* If we've already committed to saving an odd number of GPRs, assume that
17844+ saving one more will involve turning an STR into an STP and an LDR
17845+ into an LDP. This should still be more expensive than not spilling
17846+ (meaning that the minimum cost is 1), but it should usually be cheaper
17847+ than a separate store or load. */
17848+ if (GP_REGNUM_P (regno)
17849+ && nregs == 1
17850+ && (aarch64_count_saves (callee_saved_regs, GENERAL_REGS) & 1))
17851+ return 1;
17852+ 
17853+ /* Similarly for saving FP registers, if we only need to save the low
17854+ 64 bits. (We can also use STP/LDP instead of STR/LDR for Q registers,
17855+ but that is less likely to be a saving.) */
17856+ if (FP_REGNUM_P (regno)
17857+ && nregs == 1
17858+ && known_eq (GET_MODE_SIZE (aarch64_reg_save_mode (regno)), 8U)
17859+ && (aarch64_count_saves (callee_saved_regs, FP_REGS) & 1))
17860+ return 1;
17861+ 
17862+ /* If this would be the first register that we save, add the cost of
17863+ allocating or deallocating the frame. For GPR, FPR, and Advanced SIMD
17864+ saves, the allocation and deallocation can be folded into the save and
17865+ restore. */
17866+ if (!existing_spill_p
17867+ && !GP_REGNUM_P (regno)
17868+ && !(FP_REGNUM_P (regno)
17869+ && known_le (GET_MODE_SIZE (aarch64_reg_save_mode (regno)), 16U)))
17870+ return default_callee_save_cost (spill_type, regno, mode, nregs, mem_cost,
17871+ callee_saved_regs, existing_spill_p);
17872+ 
17873+ return mem_cost;
17874+}
17875+ 
17876+/* Implement TARGET_FRAME_ALLOCATION_COST. */
17877+static int
17878+aarch64_frame_allocation_cost (frame_cost_type,
17879+ const HARD_REG_SET &callee_saved_regs)
17880+{
17881+ /* The intention is to model the relative costs of different approaches
17882+ to storing data on the stack, rather than to model the cost of saving
17883+ data vs not saving it. This means that we should return 0 if:
17884+ 
17885+ - any frame is going to be allocated with:
17886+ 
17887+ stp x29, x30, [sp, #-...]!
17888+ 
17889+ to create a frame link.
17890+ 
17891+ - any frame is going to be allocated with:
17892+ 
17893+ str x30, [sp, #-...]!
17894+ 
17895+ to save the link register.
17896+ 
17897+ In both cases, the allocation and deallocation instructions are the
17898+ same however we store data to the stack. (In the second case, the STR
17899+ could be converted to an STP by saving an extra call-preserved register,
17900+ but that is modeled by aarch64_callee_save_cost.)
17901+ 
17902+ In other cases, assume that a frame would need to be allocated with a
17903+ separate subtraction and deallocated with a separate addition. Saves
17904+ of call-clobbered registers can then reclaim this cost using a
17905+ predecrement store and a postincrement load.
17906+ 
17907+ For simplicity, give this addition or subtraction the same cost as
17908+ a GPR move. We could parameterize this if necessary. */
17909+ if (aarch64_count_above_hard_fp_saves (callee_saved_regs) == 0)
17910+ return aarch64_tune_params.regmove_cost->GP2GP;
17911+ return 0;
17912+}
17913+ 
17788/* Implement TARGET_INIT_BUILTINS. */17914/* Implement TARGET_INIT_BUILTINS. */
17789static void17915static void
17790aarch64_init_builtins ()17916aarch64_init_builtins ()
@@ -31834,6 +31960,12 @@ aarch64_libgcc_floating_mode_supported_p
31834#undef TARGET_MEMORY_MOVE_COST31960#undef TARGET_MEMORY_MOVE_COST
31835#define TARGET_MEMORY_MOVE_COST aarch64_memory_move_cost31961#define TARGET_MEMORY_MOVE_COST aarch64_memory_move_cost
31836 31962 
31963+#undef TARGET_CALLEE_SAVE_COST
31964+#define TARGET_CALLEE_SAVE_COST aarch64_callee_save_cost
31965+ 
31966+#undef TARGET_FRAME_ALLOCATION_COST
31967+#define TARGET_FRAME_ALLOCATION_COST aarch64_frame_allocation_cost
31968+ 
31837#undef TARGET_MIN_DIVISIONS_FOR_RECIP_MUL31969#undef TARGET_MIN_DIVISIONS_FOR_RECIP_MUL
31838#define TARGET_MIN_DIVISIONS_FOR_RECIP_MUL aarch64_min_divisions_for_recip_mul31970#define TARGET_MIN_DIVISIONS_FOR_RECIP_MUL aarch64_min_divisions_for_recip_mul
31839 31971 
@@ -19493,6 +19493,28 @@ ix86_class_likely_spilled_p (reg_class_t rclass)
19493 return false;19493 return false;
19494}19494}
19495 19495 
19496+/* Implement TARGET_CALLEE_SAVE_COST. */
19497+ 
19498+static int
19499+ix86_callee_save_cost (spill_cost_type, unsigned int hard_regno, machine_mode,
19500+ unsigned int, int mem_cost, const HARD_REG_SET &, bool)
19501+{
19502+ /* Account for the fact that push and pop are shorter and do their
19503+ own allocation and deallocation. */
19504+ if (GENERAL_REGNO_P (hard_regno))
19505+ {
19506+ /* push is 1 byte while typical spill is 4-5 bytes.
19507+ ??? We probably should adjust size costs accordingly.
19508+ Costs are relative to reg-reg move that has 2 bytes for 32bit
19509+ and 3 bytes otherwise. Be sure that no cost table sets cost
19510+ to 2, so we end up with 0. */
19511+ if (mem_cost <= 2 || optimize_function_for_size_p (cfun))
19512+ return 1;
19513+ return mem_cost - 2;
19514+ }
19515+ return mem_cost;
19516+}
19517+ 
19496/* Return true if a set of DST by the expression SRC should be allowed.19518/* Return true if a set of DST by the expression SRC should be allowed.
19497 This prevents complex sets of likely_spilled hard regs before reload. */19519 This prevents complex sets of likely_spilled hard regs before reload. */
19498 19520 
@@ -24683,6 +24705,8 @@ ix86_libgcc_floating_mode_supported_p
24683#define TARGET_PREFERRED_OUTPUT_RELOAD_CLASS ix86_preferred_output_reload_class24705#define TARGET_PREFERRED_OUTPUT_RELOAD_CLASS ix86_preferred_output_reload_class
24684#undef TARGET_CLASS_LIKELY_SPILLED_P24706#undef TARGET_CLASS_LIKELY_SPILLED_P
24685#define TARGET_CLASS_LIKELY_SPILLED_P ix86_class_likely_spilled_p24707#define TARGET_CLASS_LIKELY_SPILLED_P ix86_class_likely_spilled_p
24708+#undef TARGET_CALLEE_SAVE_COST
24709+#define TARGET_CALLEE_SAVE_COST ix86_callee_save_cost
24686 24710 
24687#undef TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST24711#undef TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST
24688#define TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST \24712#define TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST \
@@ -6784,6 +6784,75 @@ value to the result of that function. The arguments to that function
6784are the same as to this target hook.6784are the same as to this target hook.
6785@end deftypefn6785@end deftypefn
6786 6786 
6787+@deftypefn {Target Hook} int TARGET_CALLEE_SAVE_COST (spill_cost_type @var{cost_type}, unsigned int @var{hard_regno}, machine_mode @var{mode}, unsigned int @var{nregs}, int @var{mem_cost}, const HARD_REG_SET @var{&allocated_callee_regs}, bool @var{existing_spills_p})
6788+Return the one-off cost of saving or restoring callee-saved registers
6789+(also known as call-preserved registers or non-volatile registers).
6790+The parameters are as follows:
6791+ 
6792+@itemize
6793+@item
6794+@var{cost_type} is @samp{spill_cost_type::SAVE} for saving a register
6795+and @samp{spill_cost_type::RESTORE} for restoring a register.
6796+ 
6797+@item
6798+@var{hard_regno} and @var{mode} represent the whole register that
6799+the register allocator is considering using; of these,
6800+@var{nregs} registers are fully or partially callee-saved.
6801+ 
6802+@item
6803+@var{mem_cost} is the normal cost for storing (for saves)
6804+or loading (for restores) the @var{nregs} registers.
6805+ 
6806+@item
6807+@var{allocated_callee_regs} is the set of callee-saved registers
6808+that are already in use.
6809+ 
6810+@item
6811+@var{existing_spills_p} is true if the register allocator has
6812+already decided to spill registers to memory.
6813+@end itemize
6814+ 
6815+If @var{existing_spills_p} is false, the cost of a save should account
6816+for frame allocations in a way that is consistent with
6817+@code{TARGET_FRAME_ALLOCATION_COST}'s handling of allocations for spills.
6818+Similarly, the cost of a restore should then account for frame deallocations
6819+in a way that is consistent with @code{TARGET_FRAME_ALLOCATION_COST}'s
6820+handling of deallocations.
6821+ 
6822+Note that this hook should not attempt to apply a frequency scale
6823+to the cost: it is the caller's responsibility to do that where
6824+appropriate.
6825+ 
6826+The default implementation returns @var{mem_cost}, plus the allocation
6827+or deallocation cost returned by @code{TARGET_FRAME_ALLOCATION_COST},
6828+where appropriate.
6829+@end deftypefn
6830+ 
6831+@deftypefn {Target Hook} int TARGET_FRAME_ALLOCATION_COST (frame_cost_type @var{cost_type}, const HARD_REG_SET @var{&allocated_callee_regs})
6832+Return the cost of allocating or deallocating a frame for the sake of
6833+a spill; @var{cost_type} chooses between allocation and deallocation.
6834+The term ``spill'' here includes both forcing a pseudo register to memory
6835+and using caller-saved registers for pseudo registers that are live across
6836+a call.
6837+ 
6838+This hook is only called if the register allocator has not so far
6839+decided to spill. The allocator may have decided to use callee-saved
6840+registers; if so, @var{allocated_callee_regs} is the set of callee-saved
6841+registers that the allocator has used. There might also be other reasons
6842+why a stack frame is already needed; for example, @samp{get_frame_size ()}
6843+might be nonzero, or the target might already require a frame for
6844+target-specific reasons.
6845+ 
6846+When the register allocator uses this hook to cost spills, it also uses
6847+@code{TARGET_CALLEE_SAVE_COST} to cost new callee-saved registers, passing
6848+@samp{false} as the @var{existing_spills_p} argument. The intention is to
6849+allow the target to apply an apples-for-apples comparison between the
6850+cost of using callee-saved registers and using spills in cases where the
6851+allocator has not yet committed to using both strategies.
6852+ 
6853+The default implementation returns 0.
6854+@end deftypefn
6855+ 
6787@defmac BRANCH_COST (@var{speed_p}, @var{predictable_p})6856@defmac BRANCH_COST (@var{speed_p}, @var{predictable_p})
6788A C expression for the cost of a branch instruction. A value of 1 is6857A C expression for the cost of a branch instruction. A value of 1 is
6789the default; other values are interpreted relative to that. Parameter6858the default; other values are interpreted relative to that. Parameter
@@ -4467,6 +4467,10 @@ These macros are obsolete, new ports should use the target hook
4467 4467 
4468@hook TARGET_MEMORY_MOVE_COST4468@hook TARGET_MEMORY_MOVE_COST
4469 4469 
4470+@hook TARGET_CALLEE_SAVE_COST
4471+ 
4472+@hook TARGET_FRAME_ALLOCATION_COST
4473+ 
4470@defmac BRANCH_COST (@var{speed_p}, @var{predictable_p})4474@defmac BRANCH_COST (@var{speed_p}, @var{predictable_p})
4471A C expression for the cost of a branch instruction. A value of 1 is4475A C expression for the cost of a branch instruction. A value of 1 is
4472the default; other values are interpreted relative to that. Parameter4476the default; other values are interpreted relative to that. Parameter
@@ -191,6 +191,12 @@ hard_reg_set_empty_p (const_hard_reg_set x)
191 return x == HARD_CONST (0);191 return x == HARD_CONST (0);
192}192}
193 193 
194+inline int
195+hard_reg_set_popcount (const_hard_reg_set x)
196+{
197+ return popcount_hwi (x);
198+}
199+ 
194#else200#else
195 201 
196inline void202inline void
@@ -254,6 +260,15 @@ hard_reg_set_empty_p (const_hard_reg_set x)
254 bad |= x.elts[i];260 bad |= x.elts[i];
255 return bad == 0;261 return bad == 0;
256}262}
263+ 
264+inline int
265+hard_reg_set_popcount (const_hard_reg_set x)
266+{
267+ int count = 0;
268+ for (unsigned int i = 0; i < ARRAY_SIZE (x.elts); ++i)
269+ count += popcount_hwi (x.elts[i]);
270+ return count;
271+}
257#endif272#endif
258 273 
259/* Iterator for hard register sets. */274/* Iterator for hard register sets. */
@@ -1188,10 +1188,16 @@ finish_update_cost_records (void)
1188 update_cost_record_pool.release ();1188 update_cost_record_pool.release ();
1189}1189}
1190 1190 
1191+/* True if we have allocated memory, or intend to do so. */
1192+static bool allocated_memory_p;
1193+ 
1191/* Array whose element value is TRUE if the corresponding hard1194/* Array whose element value is TRUE if the corresponding hard
1192 register was already allocated for an allocno. */1195 register was already allocated for an allocno. */
1193static bool allocated_hardreg_p[FIRST_PSEUDO_REGISTER];1196static bool allocated_hardreg_p[FIRST_PSEUDO_REGISTER];
1194 1197 
1198+/* Which callee-saved hard registers we've decided to save. */
1199+static HARD_REG_SET allocated_callee_save_regs;
1200+ 
1195/* Describes one element in a queue of allocnos whose costs need to be1201/* Describes one element in a queue of allocnos whose costs need to be
1196 updated. Each allocno in the queue is known to have an allocno1202 updated. Each allocno in the queue is known to have an allocno
1197 class. */1203 class. */
@@ -1733,6 +1739,20 @@ check_hard_reg_p (ira_allocno_t a, int hard_regno,
1733 return j == nregs;1739 return j == nregs;
1734}1740}
1735 1741 
1742+/* Record that we have allocated NREGS registers starting at HARD_REGNO. */
1743+ 
1744+static void
1745+record_allocation (int hard_regno, int nregs)
1746+{
1747+ for (int i = 0; i < nregs; ++i)
1748+ if (!allocated_hardreg_p[hard_regno + i])
1749+ {
1750+ allocated_hardreg_p[hard_regno + i] = true;
1751+ if (!crtl->abi->clobbers_full_reg_p (hard_regno + i))
1752+ SET_HARD_REG_BIT (allocated_callee_save_regs, hard_regno + i);
1753+ }
1754+}
1755+ 
1736/* Return number of registers needed to be saved and restored at1756/* Return number of registers needed to be saved and restored at
1737 function prologue/epilogue if we allocate HARD_REGNO to hold value1757 function prologue/epilogue if we allocate HARD_REGNO to hold value
1738 of MODE. */1758 of MODE. */
@@ -1745,6 +1765,7 @@ calculate_saved_nregs (int hard_regno, machine_mode mode)
1745 ira_assert (hard_regno >= 0);1765 ira_assert (hard_regno >= 0);
1746 for (i = hard_regno_nregs (hard_regno, mode) - 1; i >= 0; i--)1766 for (i = hard_regno_nregs (hard_regno, mode) - 1; i >= 0; i--)
1747 if (!allocated_hardreg_p[hard_regno + i]1767 if (!allocated_hardreg_p[hard_regno + i]
1768+ && ira_hard_regno_nrefs[hard_regno + i] == 0
1748 && !crtl->abi->clobbers_full_reg_p (hard_regno + i)1769 && !crtl->abi->clobbers_full_reg_p (hard_regno + i)
1749 && !LOCAL_REGNO (hard_regno + i))1770 && !LOCAL_REGNO (hard_regno + i))
1750 nregs++;1771 nregs++;
@@ -1953,6 +1974,12 @@ assign_hard_reg (ira_allocno_t a, bool retry_p)
1953#endif1974#endif
1954 auto_bitmap allocnos_to_spill;1975 auto_bitmap allocnos_to_spill;
1955 HARD_REG_SET soft_conflict_regs = {};1976 HARD_REG_SET soft_conflict_regs = {};
1977+ int entry_freq = REG_FREQ_FROM_BB (ENTRY_BLOCK_PTR_FOR_FN (cfun));
1978+ int exit_freq = REG_FREQ_FROM_BB (EXIT_BLOCK_PTR_FOR_FN (cfun));
1979+ int spill_cost = 0;
1980+ /* Whether we have spilled pseudos or used caller-saved registers for values
1981+ that are live across a call. */
1982+ bool existing_spills_p = allocated_memory_p || caller_save_needed;
1956 1983 
1957 ira_assert (! ALLOCNO_ASSIGNED_P (a));1984 ira_assert (! ALLOCNO_ASSIGNED_P (a));
1958 get_conflict_and_start_profitable_regs (a, retry_p,1985 get_conflict_and_start_profitable_regs (a, retry_p,
@@ -1971,7 +1998,19 @@ assign_hard_reg (ira_allocno_t a, bool retry_p)
1971 if (! retry_p)1998 if (! retry_p)
1972 start_update_cost ();1999 start_update_cost ();
1973 mem_cost += ALLOCNO_UPDATED_MEMORY_COST (a);2000 mem_cost += ALLOCNO_UPDATED_MEMORY_COST (a);
1974- 2001+ 
2002+ if (!existing_spills_p)
2003+ {
2004+ auto entry_cost = targetm.frame_allocation_cost
2005+ (frame_cost_type::ALLOCATION, allocated_callee_save_regs);
2006+ spill_cost += entry_cost * entry_freq;
2007+ 
2008+ auto exit_cost = targetm.frame_allocation_cost
2009+ (frame_cost_type::DEALLOCATION, allocated_callee_save_regs);
2010+ spill_cost += exit_cost * exit_freq;
2011+ }
2012+ mem_cost += spill_cost;
2013+ 
1975 ira_allocate_and_copy_costs (&ALLOCNO_UPDATED_HARD_REG_COSTS (a),2014 ira_allocate_and_copy_costs (&ALLOCNO_UPDATED_HARD_REG_COSTS (a),
1976 aclass, ALLOCNO_HARD_REG_COSTS (a));2015 aclass, ALLOCNO_HARD_REG_COSTS (a));
1977 a_costs = ALLOCNO_UPDATED_HARD_REG_COSTS (a);2016 a_costs = ALLOCNO_UPDATED_HARD_REG_COSTS (a);
@@ -2165,15 +2204,37 @@ assign_hard_reg (ira_allocno_t a, bool retry_p)
2165 /* We need to save/restore the hard register in2204 /* We need to save/restore the hard register in
2166 epilogue/prologue. Therefore we increase the cost. */2205 epilogue/prologue. Therefore we increase the cost. */
2167 {2206 {
2207+ int nregs = hard_regno_nregs (hard_regno, mode);
2208+ add_cost = 0;
2168 rclass = REGNO_REG_CLASS (hard_regno);2209 rclass = REGNO_REG_CLASS (hard_regno);
2169- add_cost = ((ira_memory_move_cost[mode][rclass][0]2210+ 
2170- + ira_memory_move_cost[mode][rclass][1])2211+ auto entry_cost = targetm.callee_save_cost
2171- * saved_nregs / hard_regno_nregs (hard_regno,2212+ (spill_cost_type::SAVE, hard_regno, mode, saved_nregs,
2172- mode) - 1);2213+ ira_memory_move_cost[mode][rclass][0] * saved_nregs / nregs,
2214+ allocated_callee_save_regs, existing_spills_p);
2215+ /* In the event of a tie between caller-save and callee-save,
2216+ prefer callee-save. We apply this to the entry cost rather
2217+ than the exit cost since the entry frequency must be at
2218+ least as high as the exit frequency. */
2219+ if (entry_cost > 1)
2220+ entry_cost -= 1;
2221+ add_cost += entry_cost * entry_freq;
2222+ 
2223+ auto exit_cost = targetm.callee_save_cost
2224+ (spill_cost_type::RESTORE, hard_regno, mode, saved_nregs,
2225+ ira_memory_move_cost[mode][rclass][1] * saved_nregs / nregs,
2226+ allocated_callee_save_regs, existing_spills_p);
2227+ add_cost += exit_cost * exit_freq;
2228+ 
2173 cost += add_cost;2229 cost += add_cost;
2174 full_cost += add_cost;2230 full_cost += add_cost;
2175 }2231 }
2176 }2232 }
2233+ if (ira_need_caller_save_p (a, hard_regno))
2234+ {
2235+ cost += spill_cost;
2236+ full_cost += spill_cost;
2237+ }
2177 if (min_cost > cost)2238 if (min_cost > cost)
2178 min_cost = cost;2239 min_cost = cost;
2179 if (min_full_cost > full_cost)2240 if (min_full_cost > full_cost)
@@ -2200,11 +2261,13 @@ assign_hard_reg (ira_allocno_t a, bool retry_p)
2200 fail:2261 fail:
2201 if (best_hard_regno >= 0)2262 if (best_hard_regno >= 0)
2202 {2263 {
2203- for (i = hard_regno_nregs (best_hard_regno, mode) - 1; i >= 0; i--)2264+ record_allocation (best_hard_regno,
2204- allocated_hardreg_p[best_hard_regno + i] = true;2265+ hard_regno_nregs (best_hard_regno, mode));
2205 spill_soft_conflicts (a, allocnos_to_spill, soft_conflict_regs,2266 spill_soft_conflicts (a, allocnos_to_spill, soft_conflict_regs,
2206 best_hard_regno);2267 best_hard_regno);
2207 }2268 }
2269+ else
2270+ allocated_memory_p = true;
2208 if (! retry_p)2271 if (! retry_p)
2209 restore_costs_from_copies (a);2272 restore_costs_from_copies (a);
2210 ALLOCNO_HARD_REGNO (a) = best_hard_regno;2273 ALLOCNO_HARD_REGNO (a) = best_hard_regno;
@@ -3144,13 +3207,15 @@ improve_allocation (void)
3144 int j, k, n, hregno, conflict_hregno, base_cost, class_size, word, nwords;3207 int j, k, n, hregno, conflict_hregno, base_cost, class_size, word, nwords;
3145 int check, spill_cost, min_cost, nregs, conflict_nregs, r, best;3208 int check, spill_cost, min_cost, nregs, conflict_nregs, r, best;
3146 bool try_p;3209 bool try_p;
3147- enum reg_class aclass;3210+ enum reg_class aclass, rclass;
3148 machine_mode mode;3211 machine_mode mode;
3149 int *allocno_costs;3212 int *allocno_costs;
3150 int costs[FIRST_PSEUDO_REGISTER];3213 int costs[FIRST_PSEUDO_REGISTER];
3151 HARD_REG_SET conflicting_regs[2], profitable_hard_regs;3214 HARD_REG_SET conflicting_regs[2], profitable_hard_regs;
3152 ira_allocno_t a;3215 ira_allocno_t a;
3153 bitmap_iterator bi;3216 bitmap_iterator bi;
3217+ int saved_nregs;
3218+ int add_cost;
3154 3219 
3155 /* Don't bother to optimize the code with static chain pointer and3220 /* Don't bother to optimize the code with static chain pointer and
3156 non-local goto in order not to spill the chain pointer3221 non-local goto in order not to spill the chain pointer
@@ -3188,6 +3253,7 @@ improve_allocation (void)
3188 conflicting_regs,3253 conflicting_regs,
3189 &profitable_hard_regs);3254 &profitable_hard_regs);
3190 class_size = ira_class_hard_regs_num[aclass];3255 class_size = ira_class_hard_regs_num[aclass];
3256+ mode = ALLOCNO_MODE (a);
3191 /* Set up cost improvement for usage of each profitable hard3257 /* Set up cost improvement for usage of each profitable hard
3192 register for allocno A. */3258 register for allocno A. */
3193 for (j = 0; j < class_size; j++)3259 for (j = 0; j < class_size; j++)
@@ -3201,6 +3267,22 @@ improve_allocation (void)
3201 costs[hregno] = (allocno_costs == NULL3267 costs[hregno] = (allocno_costs == NULL
3202 ? ALLOCNO_UPDATED_CLASS_COST (a) : allocno_costs[k]);3268 ? ALLOCNO_UPDATED_CLASS_COST (a) : allocno_costs[k]);
3203 costs[hregno] -= allocno_copy_cost_saving (a, hregno);3269 costs[hregno] -= allocno_copy_cost_saving (a, hregno);
3270+ 
3271+ if ((saved_nregs = calculate_saved_nregs (hregno, mode)) != 0)
3272+ {
3273+ /* We need to save/restore the hard register in
3274+ epilogue/prologue. Therefore we increase the cost.
3275+ Since the prolog is placed in the entry BB, the frequency
3276+ of the entry BB is considered while computing the cost. */
3277+ rclass = REGNO_REG_CLASS (hregno);
3278+ add_cost = ((ira_memory_move_cost[mode][rclass][0]
3279+ + ira_memory_move_cost[mode][rclass][1])
3280+ * saved_nregs / hard_regno_nregs (hregno,
3281+ mode) - 1)
3282+ * REG_FREQ_FROM_BB (ENTRY_BLOCK_PTR_FOR_FN (cfun));
3283+ costs[hregno] += add_cost;
3284+ }
3285+ 
3204 costs[hregno] -= base_cost;3286 costs[hregno] -= base_cost;
3205 if (costs[hregno] < 0)3287 if (costs[hregno] < 0)
3206 try_p = true;3288 try_p = true;
@@ -3334,6 +3416,7 @@ improve_allocation (void)
3334 }3416 }
3335 /* Assign the best chosen hard register to A. */3417 /* Assign the best chosen hard register to A. */
3336 ALLOCNO_HARD_REGNO (a) = best;3418 ALLOCNO_HARD_REGNO (a) = best;
3419+ record_allocation (best, nregs);
3337 if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)3420 if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
3338 fprintf (ira_dump_file, "Assigning %d to a%dr%d\n",3421 fprintf (ira_dump_file, "Assigning %d to a%dr%d\n",
3339 best, ALLOCNO_NUM (a), ALLOCNO_REGNO (a));3422 best, ALLOCNO_NUM (a), ALLOCNO_REGNO (a));
@@ -5162,6 +5245,7 @@ color (void)
5162{5245{
5163 allocno_stack_vec.create (ira_allocnos_num);5246 allocno_stack_vec.create (ira_allocnos_num);
5164 memset (allocated_hardreg_p, 0, sizeof (allocated_hardreg_p));5247 memset (allocated_hardreg_p, 0, sizeof (allocated_hardreg_p));
5248+ CLEAR_HARD_REG_SET (allocated_callee_save_regs);
5165 ira_initiate_assign ();5249 ira_initiate_assign ();
5166 do_coloring ();5250 do_coloring ();
5167 ira_finish_assign ();5251 ira_finish_assign ();
@@ -5286,10 +5370,14 @@ ira_color (void)
5286 ira_allocno_iterator ai;5370 ira_allocno_iterator ai;
5287 5371 
5288 /* Setup updated costs. */5372 /* Setup updated costs. */
5373+ allocated_memory_p = false;
5289 FOR_EACH_ALLOCNO (a, ai)5374 FOR_EACH_ALLOCNO (a, ai)
5290 {5375 {
5291 ALLOCNO_UPDATED_MEMORY_COST (a) = ALLOCNO_MEMORY_COST (a);5376 ALLOCNO_UPDATED_MEMORY_COST (a) = ALLOCNO_MEMORY_COST (a);
5292 ALLOCNO_UPDATED_CLASS_COST (a) = ALLOCNO_CLASS_COST (a);5377 ALLOCNO_UPDATED_CLASS_COST (a) = ALLOCNO_CLASS_COST (a);
5378+ if (ALLOCNO_CLASS (a) == NO_REGS
5379+ && !ira_equiv_no_lvalue_p (ALLOCNO_REGNO (a)))
5380+ allocated_memory_p = true;
5293 }5381 }
5294 if (ira_conflicts_p)5382 if (ira_conflicts_p)
5295 color ();5383 color ();
@@ -922,6 +922,9 @@ public:
922 922 
923 /* Flag of that the above array has been initialized. */923 /* Flag of that the above array has been initialized. */
924 bool x_ira_prohibited_mode_move_regs_initialized_p;924 bool x_ira_prohibited_mode_move_regs_initialized_p;
925+ 
926+ /* Number of real occurrences of hard regs before IRA. */
927+ size_t x_ira_hard_regno_nrefs[FIRST_PSEUDO_REGISTER];
925};928};
926 929 
927extern class target_ira_int default_target_ira_int;930extern class target_ira_int default_target_ira_int;
@@ -969,6 +972,8 @@ extern class target_ira_int *this_target_ira_int;
969 (this_target_ira_int->x_ira_reg_class_superunion)972 (this_target_ira_int->x_ira_reg_class_superunion)
970#define ira_prohibited_mode_move_regs \973#define ira_prohibited_mode_move_regs \
971 (this_target_ira_int->x_ira_prohibited_mode_move_regs)974 (this_target_ira_int->x_ira_prohibited_mode_move_regs)
975+#define ira_hard_regno_nrefs \
976+ (this_target_ira_int->x_ira_hard_regno_nrefs)
972 977
973/* ira.cc: */978/* ira.cc: */
974 979 
Mgcc/ira.cc+31-23
@@ -1409,34 +1409,17 @@ find_reg_classes (void)
1409 1409 
1410 1410
1411 1411 
1412-/* Set up the array above. */1412+/* Set up array ira_hard_regno_allocno_class. */
1413static void1413static void
1414setup_hard_regno_aclass (void)1414setup_hard_regno_aclass (void)
1415{1415{
1416 int i;1416 int i;
1417 1417 
1418 for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)1418 for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
1419- {1419+ ira_hard_regno_allocno_class[i]
1420-#if 11420+ = (TEST_HARD_REG_BIT (no_unit_alloc_regs, i)
1421- ira_hard_regno_allocno_class[i]1421+ ? NO_REGS
1422- = (TEST_HARD_REG_BIT (no_unit_alloc_regs, i)1422+ : ira_allocno_class_translate[REGNO_REG_CLASS (i)]);
1423- ? NO_REGS
1424- : ira_allocno_class_translate[REGNO_REG_CLASS (i)]);
1425-#else
1426- int j;
1427- enum reg_class cl;
1428- ira_hard_regno_allocno_class[i] = NO_REGS;
1429- for (j = 0; j < ira_allocno_classes_num; j++)
1430- {
1431- cl = ira_allocno_classes[j];
1432- if (ira_class_hard_reg_index[cl][i] >= 0)
1433- {
1434- ira_hard_regno_allocno_class[i] = cl;
1435- break;
1436- }
1437- }
1438-#endif
1439- }
1440}1423}
1441 1424 
1442 1425
@@ -5517,6 +5500,30 @@ bool ira_conflicts_p;
5517/* Saved between IRA and reload. */5500/* Saved between IRA and reload. */
5518static int saved_flag_ira_share_spill_slots;5501static int saved_flag_ira_share_spill_slots;
5519 5502 
5503+/* Set up array ira_hard_regno_nrefs. */
5504+static void
5505+setup_hard_regno_nrefs (void)
5506+{
5507+ int i;
5508+ 
5509+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
5510+ {
5511+ ira_hard_regno_nrefs[i] = 0;
5512+ for (df_ref use = DF_REG_USE_CHAIN (i);
5513+ use != NULL;
5514+ use = DF_REF_NEXT_REG (use))
5515+ if (DF_REF_CLASS (use) != DF_REF_ARTIFICIAL
5516+ && !(DF_REF_INSN_INFO (use) && DEBUG_INSN_P (DF_REF_INSN (use))))
5517+ ira_hard_regno_nrefs[i]++;
5518+ for (df_ref def = DF_REG_DEF_CHAIN (i);
5519+ def != NULL;
5520+ def = DF_REF_NEXT_REG (def))
5521+ if (DF_REF_CLASS (def) != DF_REF_ARTIFICIAL
5522+ && !(DF_REF_INSN_INFO (def) && DEBUG_INSN_P (DF_REF_INSN (def))))
5523+ ira_hard_regno_nrefs[i]++;
5524+ }
5525+}
5526+ 
5520/* This is the main entry of IRA. */5527/* This is the main entry of IRA. */
5521static void5528static void
5522ira (FILE *f)5529ira (FILE *f)
@@ -5529,7 +5536,8 @@ ira (FILE *f)
5529 edge_iterator ei;5536 edge_iterator ei;
5530 edge e;5537 edge e;
5531 bool output_jump_reload_p = false;5538 bool output_jump_reload_p = false;
5532- 5539+ 
5540+ setup_hard_regno_nrefs ();
5533 if (ira_use_lra_p)5541 if (ira_use_lra_p)
5534 {5542 {
5535 /* First put potential jump output reloads on the output edges5543 /* First put potential jump output reloads on the output edges
@@ -3758,6 +3758,81 @@ are the same as to this target hook.",
3758 int, (machine_mode mode, reg_class_t rclass, bool in),3758 int, (machine_mode mode, reg_class_t rclass, bool in),
3759 default_memory_move_cost)3759 default_memory_move_cost)
3760 3760 
3761+DEFHOOK
3762+(callee_save_cost,
3763+ "Return the one-off cost of saving or restoring callee-saved registers\n\
3764+(also known as call-preserved registers or non-volatile registers).\n\
3765+The parameters are as follows:\n\
3766+\n\
3767+@itemize\n\
3768+@item\n\
3769+@var{cost_type} is @samp{spill_cost_type::SAVE} for saving a register\n\
3770+and @samp{spill_cost_type::RESTORE} for restoring a register.\n\
3771+\n\
3772+@item\n\
3773+@var{hard_regno} and @var{mode} represent the whole register that\n\
3774+the register allocator is considering using; of these,\n\
3775+@var{nregs} registers are fully or partially callee-saved.\n\
3776+\n\
3777+@item\n\
3778+@var{mem_cost} is the normal cost for storing (for saves)\n\
3779+or loading (for restores) the @var{nregs} registers.\n\
3780+\n\
3781+@item\n\
3782+@var{allocated_callee_regs} is the set of callee-saved registers\n\
3783+that are already in use.\n\
3784+\n\
3785+@item\n\
3786+@var{existing_spills_p} is true if the register allocator has\n\
3787+already decided to spill registers to memory.\n\
3788+@end itemize\n\
3789+\n\
3790+If @var{existing_spills_p} is false, the cost of a save should account\n\
3791+for frame allocations in a way that is consistent with\n\
3792+@code{TARGET_FRAME_ALLOCATION_COST}'s handling of allocations for spills.\n\
3793+Similarly, the cost of a restore should then account for frame deallocations\n\
3794+in a way that is consistent with @code{TARGET_FRAME_ALLOCATION_COST}'s\n\
3795+handling of deallocations.\n\
3796+\n\
3797+Note that this hook should not attempt to apply a frequency scale\n\
3798+to the cost: it is the caller's responsibility to do that where\n\
3799+appropriate.\n\
3800+\n\
3801+The default implementation returns @var{mem_cost}, plus the allocation\n\
3802+or deallocation cost returned by @code{TARGET_FRAME_ALLOCATION_COST},\n\
3803+where appropriate.",
3804+ int, (spill_cost_type cost_type, unsigned int hard_regno,
3805+ machine_mode mode, unsigned int nregs, int mem_cost,
3806+ const HARD_REG_SET &allocated_callee_regs, bool existing_spills_p),
3807+ default_callee_save_cost)
3808+ 
3809+DEFHOOK
3810+(frame_allocation_cost,
3811+ "Return the cost of allocating or deallocating a frame for the sake of\n\
3812+a spill; @var{cost_type} chooses between allocation and deallocation.\n\
3813+The term ``spill'' here includes both forcing a pseudo register to memory\n\
3814+and using caller-saved registers for pseudo registers that are live across\n\
3815+a call.\n\
3816+\n\
3817+This hook is only called if the register allocator has not so far\n\
3818+decided to spill. The allocator may have decided to use callee-saved\n\
3819+registers; if so, @var{allocated_callee_regs} is the set of callee-saved\n\
3820+registers that the allocator has used. There might also be other reasons\n\
3821+why a stack frame is already needed; for example, @samp{get_frame_size ()}\n\
3822+might be nonzero, or the target might already require a frame for\n\
3823+target-specific reasons.\n\
3824+\n\
3825+When the register allocator uses this hook to cost spills, it also uses\n\
3826+@code{TARGET_CALLEE_SAVE_COST} to cost new callee-saved registers, passing\n\
3827+@samp{false} as the @var{existing_spills_p} argument. The intention is to\n\
3828+allow the target to apply an apples-for-apples comparison between the\n\
3829+cost of using callee-saved registers and using spills in cases where the\n\
3830+allocator has not yet committed to using both strategies.\n\
3831+\n\
3832+The default implementation returns 0.",
3833+ int, (frame_cost_type cost_type, const HARD_REG_SET &allocated_callee_regs),
3834+ default_frame_allocation_cost)
3835+ 
3761DEFHOOK3836DEFHOOK
3762(use_by_pieces_infrastructure_p,3837(use_by_pieces_infrastructure_p,
3763 "GCC will attempt several strategies when asked to copy between\n\3838 "GCC will attempt several strategies when asked to copy between\n\
@@ -7379,4 +7454,3 @@ DEFHOOKPOD
7379 7454 
7380/* Close the 'struct gcc_target' definition. */7455/* Close the 'struct gcc_target' definition. */
7381HOOK_VECTOR_END (C90_EMPTY_HACK)7456HOOK_VECTOR_END (C90_EMPTY_HACK)
7382- 
@@ -259,6 +259,18 @@ enum poly_value_estimate_kind
259 POLY_VALUE_LIKELY259 POLY_VALUE_LIKELY
260};260};
261 261 
262+enum class spill_cost_type
263+{
264+ SAVE,
265+ RESTORE
266+};
267+ 
268+enum class frame_cost_type
269+{
270+ ALLOCATION,
271+ DEALLOCATION
272+};
273+ 
262extern bool verify_type_context (location_t, type_context_kind, const_tree,274extern bool verify_type_context (location_t, type_context_kind, const_tree,
263 bool = false);275 bool = false);
264 276 
@@ -1903,6 +1903,33 @@ default_register_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
1903#endif1903#endif
1904}1904}
1905 1905 
1906+/* The default implementation of TARGET_CALLEE_SAVE_COST. */
1907+ 
1908+int
1909+default_callee_save_cost (spill_cost_type spill_type, unsigned int,
1910+ machine_mode, unsigned int, int mem_cost,
1911+ const HARD_REG_SET &callee_saved_regs,
1912+ bool existing_spills_p)
1913+{
1914+ if (!existing_spills_p)
1915+ {
1916+ auto frame_type = (spill_type == spill_cost_type::SAVE
1917+ ? frame_cost_type::ALLOCATION
1918+ : frame_cost_type::DEALLOCATION);
1919+ mem_cost += targetm.frame_allocation_cost (frame_type,
1920+ callee_saved_regs);
1921+ }
1922+ return mem_cost;
1923+}
1924+ 
1925+/* The default implementation of TARGET_FRAME_ALLOCATION_COST. */
1926+ 
1927+int
1928+default_frame_allocation_cost (frame_cost_type, const HARD_REG_SET &)
1929+{
1930+ return 0;
1931+}
1932+ 
1906/* The default implementation of TARGET_SLOW_UNALIGNED_ACCESS. */1933/* The default implementation of TARGET_SLOW_UNALIGNED_ACCESS. */
1907 1934 
1908bool1935bool
@@ -220,6 +220,11 @@ extern tree default_builtin_tm_load_store (tree);
220extern int default_memory_move_cost (machine_mode, reg_class_t, bool);220extern int default_memory_move_cost (machine_mode, reg_class_t, bool);
221extern int default_register_move_cost (machine_mode, reg_class_t,221extern int default_register_move_cost (machine_mode, reg_class_t,
222 reg_class_t);222 reg_class_t);
223+extern int default_callee_save_cost (spill_cost_type, unsigned int,
224+ machine_mode, unsigned int, int,
225+ const HARD_REG_SET &, bool);
226+extern int default_frame_allocation_cost (frame_cost_type,
227+ const HARD_REG_SET &);
223extern bool default_slow_unaligned_access (machine_mode, unsigned int);228extern bool default_slow_unaligned_access (machine_mode, unsigned int);
224extern HOST_WIDE_INT default_estimated_poly_value (poly_int64,229extern HOST_WIDE_INT default_estimated_poly_value (poly_int64,
225 poly_value_estimate_kind);230 poly_value_estimate_kind);
@@ -0,0 +1,33 @@
1+/* { dg-do compile { target aarch64-*-* } } */
2+/* { dg-options "-O2 -fno-caller-saves -march=armv8.2-a+sve -msve-vector-bits=128" } */
3+ 
4+/* A VNx2SF value occupies a full Z register even though its data size is
5+ only 64 bits for -msve-vector-bits=128. It therefore cannot be kept in
6+ V8-V15 across an ordinary call, since AAPCS64 only preserves D8-D15. */
7+ 
8+void __RTL (startwith ("ira")) test (void)
9+{
10+(function "test"
11+ (insn-chain
12+ (block 2
13+ (edge-from entry (flags "FALLTHRU"))
14+ (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
15+ (insn 2 (set (reg:VNx2SF <0>) (reg:VNx2SF v0)))
16+ (call_insn 3 (parallel [
17+ (call (mem:DI (symbol_ref:DI ("clobber") [flags 0x41])
18+ [0 clobber S8 A8])
19+ (const_int 0))
20+ (unspec:DI [(const_int 2)] UNSPEC_CALLEE_ABI)
21+ (clobber (reg:DI x30))
22+ ]))
23+ (insn 4 (set (reg:VNx2SF v0) (reg:VNx2SF <0>)))
24+ (insn 5 (use (reg:VNx2SF v0)))
25+ (edge-to exit (flags "FALLTHRU"))
26+ )
27+ )
28+)
29+}
30+ 
31+/* Before PR target/122827, this sequence used a Z8-Z15 register across the
32+ call while saving and restoring only its low 64 bits. */
33+/* { dg-final { scan-assembler-not {mov\tz(8|9|1[0-5])\.d,} } } */
@@ -0,0 +1,12 @@
1+/* { dg-options "-O2" } */
2+ 
3+int test (int x), test2 (int x);
4+ 
5+int foo (int x, int y) {
6+ test (x);
7+ int lhs = test2 (y);
8+ return x + lhs;
9+}
10+ 
11+/* { dg-final { scan-assembler {\tstp\tx19, x20, \[sp,} } } */
12+/* { dg-final { scan-assembler {\tldp\tx19, x20, \[sp,} } } */
@@ -0,0 +1,16 @@
1+/* { dg-options "-O2 -fomit-frame-pointer" } */
2+ 
3+int test (int x), test2 (int x);
4+ 
5+int foo (int x, int y) {
6+ test (x);
7+ int lhs = test2 (y);
8+ return x + lhs;
9+}
10+ 
11+/* { dg-final { scan-assembler {\tstp\tx30, x19, \[sp,} } } */
12+/* { dg-final { scan-assembler {\tldp\tx30, x19, \[sp\],} } } */
13+/* GCC 12 expands incoming arguments through additional pseudos, so the
14+ second value does not retain a profitable preference for x1. */
15+/* { dg-final { scan-assembler {\tstr\tx20, \[sp,} } } */
16+/* { dg-final { scan-assembler {\tldr\tx20, \[sp,} } } */
@@ -0,0 +1,13 @@
1+/* { dg-options "-O2" } */
2+ 
3+float test ();
4+float g;
5+ 
6+float foo (float x, float y) {
7+ g = x + test ();
8+ return (x + test ()) * y;
9+}
10+ 
11+/* GCC 12 allocates FP registers in ascending register-number order. */
12+/* { dg-final { scan-assembler {\tstp\td8, d9, \[sp,} } } */
13+/* { dg-final { scan-assembler {\tldp\td8, d9, \[sp,} } } */
@@ -1,5 +1,5 @@
1/* { dg-do run { target le } } */1/* { dg-do run { target le } } */
2-/* { dg-additional-options "-Os -fno-tree-ter -save-temps -fdump-rtl-ree-all -free -std=c99 -w" } */2+/* { dg-additional-options "-Os -fno-tree-ter -save-temps -fdump-rtl-ree-all -free -std=c99 -w -fno-caller-saves" } */
3 3 
4typedef unsigned char u8;4typedef unsigned char u8;
5typedef unsigned char __attribute__((__vector_size__ (8))) v64u8;5typedef unsigned char __attribute__((__vector_size__ (8))) v64u8;
@@ -0,0 +1,95 @@
1+/* PR target/122827.
2+ 
3+ With -msve-vector-bits=128 and SVE-only autovec, the inlined
4+ madd_v3 calls are vectorised into SVE partial-mode FMLAs that share
5+ a single vec_duplicate of the scalar FAC.
6+ Before the fix, the register allocator placed the VNx2SF
7+ broadcast in V14 across the call to clobber() because IRA thought
8+ V14 was preserved (only the low 64 bits, D14, are callee-saved under
9+ AAPCS64). The SVE FMLA reads the full Z register, so the upper
10+ lanes of the broadcast are corrupted by the call and the result of
11+ the FMLA is wrong. */
12+ 
13+/* { dg-do run { target aarch64_sve128_hw } } */
14+/* { dg-options "-Ofast -msve-vector-bits=128 --param=aarch64-autovec-preference=2" } */
15+/* { dg-require-effective-target aarch64_little_endian } */
16+ 
17+struct R
18+{
19+ float combined[3];
20+ float spec[3];
21+ float diff[3];
22+ float diffshad[3];
23+ float shad[3];
24+};
25+ 
26+struct I
27+{
28+ float fac;
29+ int passflag;
30+};
31+ 
32+/* AAPCS64 only requires callees to preserve the low 64 bits of V8-V15;
33+ the upper lanes are caller-clobbered. The buggy register allocator
34+ in this test places the broadcast in V14 specifically, so clobbering
35+ V14s upper bits here is sufficient to expose the miscompile. The
36+ asm clobber list lets GCC save/restore D14 around the write, so we
37+ honour the ABI contract for the low half while leaving the upper
38+ half trashed, what any normal callee is permitted to do. */
39+__attribute__((noipa)) static void
40+clobber (struct I *si, struct R *r)
41+{
42+ asm volatile ("movi v14.16b, #0xff" : : : "v14");
43+}
44+ 
45+static inline __attribute__((always_inline)) void
46+madd_v3 (float r[3], const float a[3], float f)
47+{
48+ r[0] += a[0] * f;
49+ r[1] += a[1] * f;
50+ r[2] += a[2] * f;
51+}
52+ 
53+__attribute__((noipa)) void
54+test (struct I *si, struct R *r)
55+{
56+ struct R t = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 },
57+ { 10, 11, 12 }, { 13, 14, 15 } };
58+ 
59+ clobber (si, r);
60+ if (si->fac != 0.0f)
61+ {
62+ float fac = si->fac;
63+ clobber (si, &t);
64+ madd_v3 (r->combined, t.combined, fac);
65+ if (si->passflag & 1)
66+ madd_v3 (r->spec, t.spec, fac);
67+ if (si->passflag & 2)
68+ {
69+ madd_v3 (r->diff, t.diff, fac);
70+ madd_v3 (r->diffshad, t.diffshad, fac);
71+ }
72+ if (si->passflag & 4)
73+ madd_v3 (r->shad, t.shad, fac);
74+ }
75+}
76+ 
77+int
78+main (void)
79+{
80+ struct I si = { 2.0f, 7 };
81+ struct R r = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 },
82+ { 0, 0, 0 }, { 0, 0, 0 } };
83+ test (&si, &r);
84+ if (r.combined[0] != 2.f || r.combined[1] != 4.f || r.combined[2] != 6.f)
85+ __builtin_abort ();
86+ if (r.spec[0] != 8.f || r.spec[1] != 10.f || r.spec[2] != 12.f)
87+ __builtin_abort ();
88+ if (r.diff[0] != 14.f || r.diff[1] != 16.f || r.diff[2] != 18.f)
89+ __builtin_abort ();
90+ if (r.diffshad[0] != 20.f || r.diffshad[1] != 22.f || r.diffshad[2] != 24.f)
91+ __builtin_abort ();
92+ if (r.shad[0] != 26.f || r.shad[1] != 28.f || r.shad[2] != 30.f)
93+ __builtin_abort ();
94+ return 0;
95+}
@@ -1,6 +1,6 @@
1/* { dg-do compile } */1/* { dg-do compile } */
2/* { dg-options "-O2 -mforce-indirect-call -fPIC" } */2/* { dg-options "-O2 -mforce-indirect-call -fPIC" } */
3/* { dg-require-effective-target fpic } */3/* { dg-require-effective-target fpic } */
4-/* { dg-final { scan-assembler-times "(?:call|jmp)\[ \\t\]+\\*%" 3 } } */4+/* { dg-final { scan-assembler-times "(?:call|jmp)\[ \\t\]+\\*" 3 } } */
5 5 
6#include "force-indirect-call-1.c"6#include "force-indirect-call-1.c"
@@ -0,0 +1,16 @@
1+/* { dg-do compile { target { ! ia32 } } } */
2+/* { dg-options "-O2 -fdump-rtl-pro_and_epilogue" } */
3+ 
4+/* Verify there is an early return without the prolog and shrink-wrap
5+ the function. */
6+ 
7+void bar ();
8+long
9+foo (long i, long cond)
10+{
11+ if (cond)
12+ bar ();
13+ return i+1;
14+}
15+ 
16+/* { dg-final { scan-rtl-dump-times "Performing shrink-wrapping" 1 "pro_and_epilogue" } } */
@@ -0,0 +1,17 @@
1+/* { dg-do compile { target { ! ia32 } } } */
2+/* { dg-options "-O2 -fdump-rtl-pro_and_epilogue" } */
3+ 
4+/* Verify there is an early return without the prolog and shrink-wrap
5+ the function. */
6+ 
7+int f (int);
8+int
9+advance (int dz)
10+{
11+ if (dz > 0)
12+ return (dz + dz) * dz;
13+ else
14+ return dz * f (dz);
15+}
16+ 
17+/* { dg-final { scan-rtl-dump-times "Performing shrink-wrapping" 1 "pro_and_epilogue" } } */
@@ -0,0 +1,17 @@
1+/* { dg-do compile { target { ia32 } } } */
2+/* { dg-options "-O2 -fpic" } */
3+ 
4+extern void crosscall2 (void (*fn) (void *, int), void *, int);
5+extern void _cgo_panic (void *, int);
6+extern void _cgo_allocate (void *, int);
7+ 
8+void
9+callPanic (void)
10+{
11+ struct { const char *p; } a;
12+ a.p = "panic from C";
13+ crosscall2 (_cgo_panic, &a, sizeof a);
14+ *(int *) 1 = 1;
15+}
16+ 
17+/* { dg-final { scan-assembler "__x86.get_pc_thunk.bx" } } */
@@ -0,0 +1,15 @@
1+/* { dg-do compile { target lp64 } } */
2+/* { dg-options "-O2 -fdump-rtl-pro_and_epilogue" } */
3+ 
4+/* Verify there is an early return without the prolog and shrink-wrap
5+ the function. */
6+void bar ();
7+long
8+foo (long i, long cond)
9+{
10+ if (cond)
11+ bar ();
12+ return i+1;
13+}
14+ 
15+/* { dg-final { scan-rtl-dump-times "Performing shrink-wrapping" 1 "pro_and_epilogue" } } */
@@ -0,0 +1,17 @@
1+/* { dg-do compile { target lp64 } } */
2+/* { dg-options "-O2 -fdump-rtl-pro_and_epilogue" } */
3+ 
4+/* Verify there is an early return without the prolog and shrink-wrap
5+ the function. */
6+ 
7+int f (int);
8+int
9+advance (int dz)
10+{
11+ if (dz > 0)
12+ return (dz + dz) * dz;
13+ else
14+ return dz * f (dz);
15+}
16+ 
17+/* { dg-final { scan-rtl-dump-times "Performing shrink-wrapping" 1 "pro_and_epilogue" } } */