Improvement to signed division of integer constant on x86_64.
authorRoger Sayle <roger@nextmovesoftware.com>
Fri, 9 Jul 2021 16:45:40 +0000 (17:45 +0100)
committerRoger Sayle <roger@nextmovesoftware.com>
Fri, 9 Jul 2021 16:47:55 +0000 (17:47 +0100)
commit59045273cc648e354ba72f9188f69927f00802e2
tree19690437120a0584f9c2f461ea3e767541334e63
parent0d5db79a61af150cba48612c9fbc3267262adb93
Improvement to signed division of integer constant on x86_64.

This patch tweaks the way GCC handles 32-bit integer division on
x86_64, when the numerator is constant.  Currently the function

int foo (int x) {
  return 100/x;
}

generates the code:
foo: movl    $100, %eax
        cltd
        idivl   %edi
        ret

where the sign-extension instruction "cltd" creates a long
dependency chain, as it depends on the "mov" before it, and
is depended upon by "idivl" after it.

With this patch, GCC now matches both icc and LLVM and uses
an xor instead, generating:
foo: xorl    %edx, %edx
        movl    $100, %eax
        idivl   %edi
        ret

Microbenchmarking confirms that this is faster on Intel
processors (Kaby lake), and no worse on AMD processors (Zen2),
which agrees with intuition, but oddly disagrees with the
llvm-mca cycle count prediction on godbolt.org.

The tricky bit is that this sign-extension instruction is only
produced by late (postreload) splitting, and unfortunately none
of the subsequent passes (e.g. cprop_hardreg) is able to
propagate and simplify its constant argument.  The solution
here is to introduce a define_insn_and_split that allows the
constant numerator operand to be captured (by combine) and
then split into an optimal form after reload.

The above microbenchmarking also shows that eliminating the
sign extension of negative values (using movl $-1,%edx) is also
a performance improvement, as performed by icc but not by LLVM.
Both the xor and movl sign-extensions are larger than cltd,
so this transformation is prevented for -Os.

2021-07-09  Roger Sayle  <roger@nextmovesoftware.com>
    Uroš Bizjak  <ubizjak@gmail.com>

gcc/ChangeLog
* config/i386/i386.md (*divmodsi4_const): Optimize SImode
divmod of a constant numerator with new define_insn_and_split.

gcc/testsuite/ChangeLog
* gcc.target/i386/divmod-9.c: New test case.
gcc/config/i386/i386.md
gcc/testsuite/gcc.target/i386/divmod-9.c [new file with mode: 0644]