[Bug c/84646] New: Missed optimisation for hoisting conditions outside nested loops

david at westcontrol dot com gcc-bugzilla@gcc.gnu.org
Thu Mar 1 10:40:00 GMT 2018


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84646

            Bug ID: 84646
           Summary: Missed optimisation for hoisting conditions outside
                    nested loops
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: david at westcontrol dot com
  Target Milestone: ---

This is a missed optimisation opportunity.  In a discussion about the "best"
way to break out of a nested loop, I tested this code with gcc:

int foo(const int * p, const int * q, int m, int n) {
    int sum = 0;
    bool running = true;
    const int max = 20000;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (running) {
                sum += (p[i] * q[j]);
                if (sum >= max) {
                    running = false;
                    sum = max;
                }
            }
        }
    }
    return sum;
}


The test for "running" is hoisted outside the inner loop, so that the generated
code is changed to approximately:

int foo(const int * p, const int * q, int m, int n) {
    int sum = 0;
    bool running = true;
    const int max = 20000;
    for (int i = 0; i < n; i++) {
loop:
        if (running) {
            for (int j = 0; j < m; j++) {
                sum += (p[i] * q[j]);
                if (sum >= max) {
                    running = false;
                    sum = max;
                    goto loop;
                }
            }
        }
    }
    return sum;
}

This is definitely a good step - avoiding the check for "running" in the inner
loop, and breaking out of it when the max condition is reached is a clear win. 
But it would be even nicer if the check could be hoisted further - the
"running" flag could be completely eliminated to give the transformation:

int foo(const int * p, const int * q, int m, int n) {
    int sum = 0;
    //bool running = true;
    const int max = 20000;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (running) {
                sum += (p[i] * q[j]);
                if (sum >= max) {
                    //running = false;
                    sum = max;
                    goto exit;
                }
            }
        }
    }
exit:
    return sum;
}


Testing was done with -O2 and -O3, on a variety of gcc versions and targets
(thanks, godbolt.org!) up to version 8.0 (trunk at this time).  The generated
code showed approximately the same transformations and optimisations.


More information about the Gcc-bugs mailing list