On 2021/10/26 21:20, Richard Biener wrote:
> On Mon, Oct 18, 2021 at 6:29 AM Xionghu Luo <luo...@linux.ibm.com> wrote:
>>
>>
>>
>> On 2021/10/15 16:11, Richard Biener wrote:
>>> On Sat, Oct 9, 2021 at 5:45 AM Xionghu Luo <luo...@linux.ibm.com> wrote:
>>>>
>>>> Hi,
>>>>
>>>> On 2021/9/28 20:09, Richard Biener wrote:
>>>>> On Fri, Sep 24, 2021 at 8:29 AM Xionghu Luo <luo...@linux.ibm.com> wrote:
>>>>>>
>>>>>> Update the patch to v3, not sure whether you prefer the paste style
>>>>>> and continue to link the previous thread as Segher dislikes this...
>>>>>>
>>>>>>
>>>>>> [PATCH v3] Don't move cold code out of loop by checking bb count
>>>>>>
>>>>>>
>>>>>> Changes:
>>>>>> 1. Handle max_loop in determine_max_movement instead of
>>>>>> outermost_invariant_loop.
>>>>>> 2. Remove unnecessary changes.
>>>>>> 3. Add for_all_locs_in_loop (loop, ref, ref_in_loop_hot_body) in
>>>>>> can_sm_ref_p.
>>>>>> 4. "gsi_next (&bsi);" in move_computations_worker is kept since it caused
>>>>>> infinite loop when implementing v1 and the iteration is missed to be
>>>>>> updated actually.
>>>>>>
>>>>>> v1: https://gcc.gnu.org/pipermail/gcc-patches/2021-August/576488.html
>>>>>> v2: https://gcc.gnu.org/pipermail/gcc-patches/2021-September/579086.html
>>>>>>
>>>>>> There was a patch trying to avoid move cold block out of loop:
>>>>>>
>>>>>> https://gcc.gnu.org/pipermail/gcc/2014-November/215551.html
>>>>>>
>>>>>> Richard suggested to "never hoist anything from a bb with lower execution
>>>>>> frequency to a bb with higher one in LIM invariantness_dom_walker
>>>>>> before_dom_children".
>>>>>>
>>>>>> In gimple LIM analysis, add find_coldest_out_loop to move invariants to
>>>>>> expected target loop, if profile count of the loop bb is colder
>>>>>> than target loop preheader, it won't be hoisted out of loop.
>>>>>> Likely for store motion, if all locations of the REF in loop is cold,
>>>>>> don't do store motion of it.
>>>>>>
>>>>>> SPEC2017 performance evaluation shows 1% performance improvement for
>>>>>> intrate GEOMEAN and no obvious regression for others. Especially,
>>>>>> 500.perlbench_r +7.52% (Perf shows function S_regtry of perlbench is
>>>>>> largely improved.), and 548.exchange2_r+1.98%, 526.blender_r +1.00%
>>>>>> on P8LE.
>>>>>>
>>>>>> gcc/ChangeLog:
>>>>>>
>>>>>> * loop-invariant.c (find_invariants_bb): Check profile count
>>>>>> before motion.
>>>>>> (find_invariants_body): Add argument.
>>>>>> * tree-ssa-loop-im.c (find_coldest_out_loop): New function.
>>>>>> (determine_max_movement): Use find_coldest_out_loop.
>>>>>> (move_computations_worker): Adjust and fix iteration udpate.
>>>>>> (execute_sm_exit): Check pointer validness.
>>>>>> (class ref_in_loop_hot_body): New functor.
>>>>>> (ref_in_loop_hot_body::operator): New.
>>>>>> (can_sm_ref_p): Use for_all_locs_in_loop.
>>>>>>
>>>>>> gcc/testsuite/ChangeLog:
>>>>>>
>>>>>> * gcc.dg/tree-ssa/recip-3.c: Adjust.
>>>>>> * gcc.dg/tree-ssa/ssa-lim-18.c: New test.
>>>>>> * gcc.dg/tree-ssa/ssa-lim-19.c: New test.
>>>>>> * gcc.dg/tree-ssa/ssa-lim-20.c: New test.
>>>>>> ---
>>>>>> gcc/loop-invariant.c | 10 ++--
>>>>>> gcc/tree-ssa-loop-im.c | 61 ++++++++++++++++++++--
>>>>>> gcc/testsuite/gcc.dg/tree-ssa/recip-3.c | 2 +-
>>>>>> gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-18.c | 20 +++++++
>>>>>> gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-19.c | 27 ++++++++++
>>>>>> gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-20.c | 25 +++++++++
>>>>>> gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-21.c | 28 ++++++++++
>>>>>> 7 files changed, 165 insertions(+), 8 deletions(-)
>>>>>> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-18.c
>>>>>> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-19.c
>>>>>> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-20.c
>>>>>> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-21.c
>>>>>>
>>>>>> diff --git a/gcc/loop-invariant.c b/gcc/loop-invariant.c
>>>>>> index fca0c2b24be..5c3be7bf0eb 100644
>>>>>> --- a/gcc/loop-invariant.c
>>>>>> +++ b/gcc/loop-invariant.c
>>>>>> @@ -1183,9 +1183,14 @@ find_invariants_insn (rtx_insn *insn, bool
>>>>>> always_reached, bool always_executed)
>>>>>> call. */
>>>>>>
>>>>>> static void
>>>>>> -find_invariants_bb (basic_block bb, bool always_reached, bool
>>>>>> always_executed)
>>>>>> +find_invariants_bb (class loop *loop, basic_block bb, bool
>>>>>> always_reached,
>>>>>> + bool always_executed)
>>>>>> {
>>>>>> rtx_insn *insn;
>>>>>> + basic_block preheader = loop_preheader_edge (loop)->src;
>>>>>> +
>>>>>> + if (preheader->count > bb->count)
>>>>>> + return;
>>>>>>
>>>>>> FOR_BB_INSNS (bb, insn)
>>>>>> {
>>>>>> @@ -1214,8 +1219,7 @@ find_invariants_body (class loop *loop,
>>>>>> basic_block *body,
>>>>>> unsigned i;
>>>>>>
>>>>>> for (i = 0; i < loop->num_nodes; i++)
>>>>>> - find_invariants_bb (body[i],
>>>>>> - bitmap_bit_p (always_reached, i),
>>>>>> + find_invariants_bb (loop, body[i], bitmap_bit_p (always_reached, i),
>>>>>> bitmap_bit_p (always_executed, i));
>>>>>> }
>>>>>>
>>>>>> diff --git a/gcc/tree-ssa-loop-im.c b/gcc/tree-ssa-loop-im.c
>>>>>> index 4b187c2cdaf..655fab03442 100644
>>>>>> --- a/gcc/tree-ssa-loop-im.c
>>>>>> +++ b/gcc/tree-ssa-loop-im.c
>>>>>> @@ -417,6 +417,28 @@ movement_possibility (gimple *stmt)
>>>>>> return ret;
>>>>>> }
>>>>>>
>>>>>> +/* Find coldest loop between outmost_loop and loop by comapring profile
>>>>>> count. */
>>>>>> +
>>>>>> +static class loop *
>>>>>> +find_coldest_out_loop (class loop *outmost_loop, class loop *loop,
>>>>>> + basic_block curr_bb)
>>>>>> +{
>>>>>> + class loop *cold_loop, *min_loop;
>>>>>> + cold_loop = min_loop = outmost_loop;
>>>>>> + profile_count min_count = loop_preheader_edge (min_loop)->src->count;
>>>>>> +
>>>>>> + if (curr_bb && curr_bb->count < loop_preheader_edge
>>>>>> (loop)->src->count)
>>>>>
>>>>> Honza - can you comment on whether we should compare BB counts this way?
>>>>>
>>>>> I would suspect that for, say,
>>>>>
>>>>> for (...)
>>>>> if (a)
>>>>> X;
>>>>> else
>>>>> Y;
>>>>>
>>>>> that the counts for X and Y will be less than that of the preheader of
>>>>> the loop
>>>>> only when the loop is estimated to run once. That is, should we really
>>>>> compare
>>>>> the to the preheader count or maybe better to the _header_ count which
>>>>> would keep the number of iterations out of the equation?
>>>>
>>>> I quickly tried to replace all the loop_preheader_edge (loop)->src with
>>>> loop_preheader_edge (loop)->dest, it will cause many failures in
>>>> gcc.dg/tree-ssa/ssa-lim-*.c, I didn't go deep to investigate, but it seems
>>>> reasonable to compare the bb count with preheader count as both gimple lim
>>>> and RTL loop-invariant move instructions to *preheader* instead of *header*
>>>> after analysis?
>>>
>>> Hmm, yeah - guess I was confused here.
>>>
>>>>>
>>>>> If we look at maybe_hot_count_p that's a quite sophisticated thing to
>>>>> compare a count to the "IPA hot", here we're comparing two counts
>>>>> within a function where it actually matters whether we use a<b or
>>>>> !(a>=b) since 'unordered' is mapped to false (but there's no ordered_p
>>>>> function).
>>>>>
>>>>> Xionghu, you error on the side of not hoisting for unordered counts here
>>>>>
>>>>>> + return NULL;
>>>>>> +
>>>>>> + while (min_loop != loop)
>>>>>> + {
>>>>>> + min_loop = superloop_at_depth (loop, loop_depth (min_loop) + 1);
>>>>>> + if (loop_preheader_edge (min_loop)->src->count < min_count)
>>>>>
>>>>> but in the other direction here and on the side of not hoisting
>>>>> in ref_in_loop_hot_body.
>>>>>
>>>>> The three-state relational operator overloads are probably not the
>>>>> very best idea...
>>>>> (see profile-count.h for them)
>>>>>
>>>> Added new function bb_colder_than_loop_preheader to encapsulate the
>>>> comparision,
>>>> if FALSE is returned due to three-state inequality, find_coldest_out_loop
>>>> will return the original input to lim->max_loop, and
>>>> ref_in_loop_hot_body::operator ()
>>>> will return true to continue perform store motion, both preserve the
>>>> previous
>>>> behavior.
>>>
>>> Thanks. But I don't think the abstraction as written is useful:
>>>
>>> +/* Compare the profile count inequality of COUNT1 and COUNT2, it is
>>> three-state
>>> + as stated in profile-count.h, FALSE is returned if inequality cannot be
>>> + decided. */
>>> +bool bb_colder_than_loop_preheader (profile_count count1, profile_count
>>> count2)
>>> +{
>>> + if (count1 < count2)
>>> + return true;
>>> + else
>>> + return false;
>>> +}
>>>
>>> given the following seems to pass the preheader count in place of the BB
>>> count.
>>>
>>> + if (bb_colder_than_loop_preheader (
>>> + loop_preheader_edge (min_loop)->src->count, min_count))
>>> + cold_loop = min_loop;
>>>
>>> find_coldest_out_loop is also a bit weird, I think we want to find
>>> the outermost loop between outmost_loop and loop that has a
>>> lower count than the curr_bb count but
>>>
>>> + while (min_loop != loop)
>>> + {
>>> + min_loop = superloop_at_depth (loop, loop_depth (min_loop) + 1);
>>> + if (bb_colder_than_loop_preheader (
>>> + loop_preheader_edge (min_loop)->src->count, min_count))
>>> + cold_loop = min_loop;
>>>
>>> compares the outermost loops count (min_count) against the preheader
>>> count? So we're searching for a cold loop with respect to its enclosing
>>> loop
>>> here?
>>
>> Let me try to explain how it works :)
>>
>> find_coldest_out_loop does two steps check:
>> 1) Check whether curr_bb is cold in it's own loop_father, if it is cold,
>> just return NULL which means it should not be moved out at all;
>> 2) curr_bb is NOT cold, assuming the current loop L[m] is the coldest first,
>> than try to find a cold loop to be hoisted to from {L[1], L[2], ... L[m]},
>> if L[i]->count < L[m]->count, set the cold_loop to L[i] until find the loop
>> that has smallest profile_count.
>>
>>
>> Take the updated ssa-lim-19.c as example, check whether curr_bb(bb 5) is
>> cold in
>> loop 3, if it is cold, just return NULL, otherwise select the coldest loop in
>> {loop1, loop2, loop3} and find that loop2 is colder than loop3, return loop2
>> to
>> be the target hoist loop. The first check could AVOID hoist if curr_bb is
>> colder
>> than loop3, but it is still hot than loop1 and loop2. Not sure whether it
>> is possible
>> to construct such cases?
>>
>>
>> gcc/testsuite/gcc.dg/tree-ssa/ssa-lim-19.c
>>
>> volatile int x;
>> void
>> bar (int, char *, char *);
>> void
>> foo (int *a, int n, int m, int s, int t)
>> {
>> int i;
>> int j;
>> int k;
>>
>> for (i = 0; i < m; i++) // loop 1
>> {
>> if (__builtin_expect (x, 0))
>> for (j = 0; j < n; j++) // loop 2
>> for (k = 0; k < n; k++) // loop 3
>> {
>> bar (s / 5, "one", "two"); // curr_bb
>> a[t] = s;
>> }
>> a[t] = t; // curr_bb2
>> }
>> }
>>
>> The 4 invariant statements are moved to bb 11(loop2) instead of bb 10(loop1)
>> with this patch.
>> There are totally 6 combinations when curr_bb is hotter than loop 3. We need
>> to compare the "Loop preheader hotness" instead of "every Loop[i] and
>> curr_bb hotness",
>> returning the coldest loop for this function find_coldest_out_loop, otherwise
>> unexpected behavior happens.
>>
>> L1 > L2 > L3 => return L3
>> L1 > L3 > L2 => return L2
>> L2 > L1 > L3 => return L3
>> L2 > L3 > L1 => return L1
>> L3 > L1 > L2 => return L2
>> L3 > L2 > L1 => return L1
>>
>> So bb_colder_than_loop_preheader does two kind of checks, one is checking
>> L3 preheader count with curr_bb count, another is checking L3 preheader count
>> with L1 preheader count, L2 preheader count, etc...
>>
>>
>> ssa-lim-19.c.138t.lim2:
>> ...
>> <bb 10> [local count: 16057869]: // L1 preheader
>> - _4 = s_22(D) / 5;
>> - _5 = (long unsigned int) t_24(D);
>> - _6 = _5 * 4;
>> - _7 = a_25(D) + _6;
>> _8 = (long unsigned int) t_24(D);
>> _9 = _8 * 4;
>> _10 = a_25(D) + _9;
>>
>> <bb 3> [local count: 145980626]:
>> # i_34 = PHI <i_29(12), 0(10)>
>> x.0_1 ={v} x;
>> if (x.0_1 != 0)
>> goto <bb 4>; [10.00%]
>> else
>> goto <bb 8>; [90.00%]
>>
>> <bb 4> [local count: 14598063]:
>> if (n_20(D) > 0)
>> goto <bb 11>; [89.00%]
>> else
>> goto <bb 8>; [11.00%]
>>
>> <bb 11> [local count: 12992276]: // L2 preheader
>> + _4 = s_22(D) / 5;
>> + _5 = (long unsigned int) t_24(D);
>> + _6 = _5 * 4;
>> + _7 = a_25(D) + _6;
>> goto <bb 7>; [100.00%]
>>
>> <bb 14> [local count: 850510901]:
>>
>> <bb 5> [local count: 955630225]: // curr_bb
>> # k_36 = PHI <k_27(14), 0(7)>
>> bar (_4, "one", "two");
>> *_7 = s_22(D);
>> k_27 = k_36 + 1;
>> if (n_20(D) > k_27)
>> goto <bb 14>; [89.00%]
>> else
>> goto <bb 6>; [11.00%]
>>
>> <bb 6> [local count: 118111600]:
>> j_21 = j_35 + 1;
>> if (n_20(D) > j_21)
>> goto <bb 13>; [89.00%]
>> else
>> goto <bb 8>; [11.00%]
>>
>> <bb 13> [local count: 105119324]:
>>
>> <bb 7> [local count: 118111600]: // L3 preheader
>> # j_35 = PHI <j_21(13), 0(11)>
>> goto <bb 5>; [100.00%]
>>
>> <bb 8> [local count: 145980626]:
>> *_10 = t_24(D);
>> i_29 = i_34 + 1;
>>
>> Re-paste the bb_colder_than_loop_preheader and find_coldest_out_loop:
>>
>> +/* Compare the profile count inequality of COUNT1 and COUNT2, it is
>> three-state
>> + as stated in profile-count.h, FALSE is returned if inequality cannot be
>> + decided. */
>> +bool bb_colder_than_loop_preheader (profile_count count1, profile_count
>> count2)
>> +{
>> + if (count1 < count2)
>> + return true;
>> + else
>> + return false;
>> +}
>> +
>> +/* Find coldest loop between OUTMOST_LOOP and LOOP by comapring profile
>> count.
>> + */
>> +
>> +static class loop *
>> +find_coldest_out_loop (class loop *outmost_loop, class loop *loop,
>> + basic_block curr_bb)
>> +{
>> + class loop *cold_loop, *min_loop;
>> + cold_loop = min_loop = outmost_loop;
>> + profile_count min_count = loop_preheader_edge (min_loop)->src->count;
>> +
>> + /* If bb_colder_than_loop_preheader returns false due to three-state
>> + comparision, OUTMOST_LOOP is returned finally to preserve the behavior.
>> + Otherwise, return the coldest loop between OUTMOST_LOOP and LOOP. */
>> + if (curr_bb
>> + && bb_colder_than_loop_preheader (curr_bb->count,
>> + loop_preheader_edge
>> (loop)->src->count))
>> + return NULL;
>> +
>> + while (min_loop != loop)
>> + {
>> + min_loop = superloop_at_depth (loop, loop_depth (min_loop) + 1);
>> + if (bb_colder_than_loop_preheader (
>> + loop_preheader_edge (min_loop)->src->count, min_count))
>> + cold_loop = min_loop;
>> + }
>> + return cold_loop;
>> +}
>> +
>>
>>
>>>
>>> Why is this function not simply
>>>
>>> +static class loop *
>>> +find_coldest_out_loop (class loop *outmost_loop, class loop *loop,
>>> + basic_block curr_bb)
>>> +{
>>> while (bb_colder_than_loop_preheader (curr_bb->count,
>>> loop_preheader_edge (outermost_loop)->src->count))
>>> {
>>> if (outermost_loop == loop)
>>> return NULL;
>>> outermost_loop = superloop_at_depth (loop, loop_depth
>>> (outermost_loop) + 1);
>>> }
>>> return outermost_loop;
>>> }
>>
>> If change like this, when processing curr_bb(5), outermost_loop will
>> return loop 1 since curr_bb->count > Loop1_prehead->count, the while
>> loop stopped. This doesn't meet what we want.
>
> Why? curr_bb is executed at least as often as loop1 preheader if
> we look at the counts? So either the counts do not really tell us
> anything of help or I am missing something. Are you merely
> looking for a block with a lower count on the path from the outermost
> loop entry to the block in question and deciding you do not want to
> hoist further than that? So it's not about not hoisting to a hot place
> but instead hoist to the coldest place within a loop nest?
>
> So we have
>
> for (i = 0; i < m; i++) // loop 1
> {
> if (__builtin_expect (x, 0))
> for (j = 0; j < n; j++) // loop 2
>
>
> <bb 10> [local count: 16057869]: // L1 preheader
> ...
> <bb 3> [local count: 145980626]:
> # i_34 = PHI <i_29(12), 0(10)>
> ...
> <bb 11> [local count: 12992276]: // L2 preheader
> ...
> <bb 7> [local count: 118111600]: // L3 preheader
> # j_35 = PHI <j_21(13), 0(11)>
> goto <bb 5>; [100.00%]
>
> and we want to hoist to the L2 preheader because that's less frequently
> executed than the L1 preheader (which is less frequently executed
> than the L3 preheader or the block we are hoisting from).
Yes, this is exactly what I want, sorry for not describe it clear before ;(
The updated patch[1] may reflect find_coldest_out_loop better:
It first check whether curr_bb is hotter than it's preheader, if false, return
NULL
which means no need hoist at all; Then find a *coldest* preheader to hoist
within a loop nest from outmost_loop.
[1] https://gcc.gnu.org/pipermail/gcc-patches/2021-October/581961.html
+/* Find coldest loop between OUTMOST_LOOP and LOOP by comparing profile count.
+ It does two steps check:
+ 1) Check whether CURR_BB is cold in it's own loop_father, if it is cold,
just
+ return NULL which means it should not be moved out at all;
+ 2) CURR_BB is NOT cold, set LOOP to cold_loop, then iteratively search
loops
+ from {L[outmost_loop], L[outmost_loop+1], ... L[loop]}, if L[i] is colder
+ than L[cold_loop], reset cold_loop to L[i] until get the loop that has
+ smallest profile_count. */
+
+static class loop *
+find_coldest_out_loop (class loop *outmost_loop, class loop *loop,
+ basic_block curr_bb)
+{
+ class loop *cold_loop;
+
+ /* If bb_colder_than_loop_preheader returns false due to three-state
+ comparision, OUTMOST_LOOP is returned finally to preserve the behavior.
+ Otherwise, return the coldest loop between OUTMOST_LOOP and LOOP. */
+ if (curr_bb
+ && bb_colder_than_loop_preheader (curr_bb,
+ loop_preheader_edge (loop)->src))
+ return NULL;
+
+ cold_loop = loop;
+ while (outmost_loop != loop)
+ {
+ if (bb_colder_than_loop_preheader (loop_preheader_edge
(outmost_loop)->src,
+ loop_preheader_edge (cold_loop)->src))
+ cold_loop = outmost_loop;
+ outmost_loop = superloop_at_depth (loop, loop_depth (outmost_loop) + 1);
+ }
+ return cold_loop;
+}
>
> I'm concerned with compile-time complexity re-evaluating counts on the
> loop nest many times. So it looks to me that we can pre-compute
> this lowest-preheader-count loop for a loop nest at least for the
> store-motion case where we know the outermost loop?
>
>
But the lowest-preheader-count loop may change for a loop/bb with different
outermost loop. For example if,
L1_preheader_count < L2_preheader_count < L3_preheader_count <
L4_preheader_count < curr_bb_count
then,
find_coldest_out_loop (L1, loop, curr_bb) => coldest preheader loop is L1
find_coldest_out_loop (L2, loop, curr_bb) => coldest preheader loop is L2
So it will be a 1:N map? Pre-compute it in find_coldest_out_loop
and save it also in lim_data with a new variable
coldest_preheader_loop[outmost_loop][coldest_preheader_loop]?
each call of find_coldest_out_loop will check whether that variable is set
already, only continue the search if
coldest_preheader_loop[outmost_loop][coldest_preheader_loop] is not set?
Seems a bit complicated and not sure whether it helps to reduce
compile-time complexity or I am misunderstanding...
--
Thanks,
Xionghu