[llvm-bugs] [Bug 37101] New: [x86] Failure to use both div and mod results of one IDIV in a prime-factor loop while(n%i==0) { n/=i; }

via llvm-bugs llvm-bugs at lists.llvm.org
Thu Apr 12 00:15:19 PDT 2018


https://bugs.llvm.org/show_bug.cgi?id=37101

            Bug ID: 37101
           Summary: [x86] Failure to use both div and mod results of one
                    IDIV in a prime-factor loop while(n%i==0) { n/=i; }
           Product: new-bugs
           Version: trunk
          Hardware: PC
                OS: Linux
            Status: NEW
          Keywords: performance
          Severity: enhancement
          Priority: P
         Component: new bugs
          Assignee: unassignedbugs at nondot.org
          Reporter: peter at cordes.ca
                CC: llvm-bugs at lists.llvm.org

Near-identical code-gen to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85366
for this loop.  From
https://codereview.stackexchange.com/questions/191792/find-prime-factors-in-c/191801#191801,
simplified to use a pointer instead of returning std::vector<int>:

void find_prime_factors_ptr(int n, int *p)
{
    // inefficient to test even numbers > 2, but that's a separate missed
optimization.
    for (int i = 2; i <= n; i++) {
        while (n % i == 0) {
            *p++ = i;
            n /= i;   // reordering the loop body doesn't help
        }
    }
}

https://godbolt.org/g/GLycMM  
clang version 7.0.0 (trunk 329780) 

find_prime_factors_ptr(int, int*):
        movl    %edi, %ecx
        cmpl    $2, %edi
        jl      .LBB0_6
        movl    $2, %edi             # why not keep n in EDI, and use ECX for
i?
.LBB0_2:                                # =>This Loop Header: Depth=1
        movl    %ecx, %eax
        cltd
        idivl   %edi
        testl   %edx, %edx
        jne     .LBB0_5
.LBB0_3:                                #   Parent Loop BB0_2 Depth=1
        movl    %edi, (%rsi)
        movl    %ecx, %eax
        cltd
        idivl   %edi
        movl    %eax, %ecx
        cltd
        idivl   %edi
        addq    $4, %rsi
        testl   %edx, %edx
        je      .LBB0_3
.LBB0_5:                                #   in Loop: Header=BB0_2 Depth=1
        leal    1(%rdi), %eax    # this compare-latency optimization 
        cmpl    %ecx, %edi       # doesn't seem worth it
        movl    %eax, %edi       # 4 uops instead of 2 from defeating
macro-fusion
        jl      .LBB0_2          # vs. inc %rdi / cmp %ecx,%edi / jle
.LBB0_6:
        retq

Using idiv twice inside the loop body is totally pointless.  At either way of
reaching .LBB0_3 (fall through or loop), n/i is in EAX, but LLVM ignores it.

The inner loop can be optimized by dropping movl %ecx, %eax / cltd / idiv with
no other changes.

.LBB0_3:
        movl    %edi, (%rsi)
        addq    $4, %rsi
        movl    %eax, %ecx    # n = tmp
        cltd
        idivl   %edi          # eax = tmp = n/i
        testl   %edx, %edx
        je      .LBB0_3

Unlike for gcc, changing the inner loop like this didn't help:

        int tmp;
        while (tmp = n/i, n % i == 0) {
            *p++ = i;
            n = tmp;
        }

(see the linked gcc bug report for an interesting version that jumps into the
inner loop instead of hoisting the run-zero-times check out of the loop before
the first iteration.)

I think one of the versions on the godbolt link did manage to optimize better
with clang, but I haven't investigated further.  (I think it was one of the
versions using `vector.push_back` inside the loop.)

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20180412/c6e5c0ea/attachment.html>


More information about the llvm-bugs mailing list