Running x86lint, a linter for suboptimal x86-64 instruction encodings, over
GCC's own -O2 binaries flags every 128-bit integer load as one byte larger
than necessary.

TARGET_SSE_TYPELESS_STORES already emits the one-byte-shorter movaps/movups
for 128-bit SSE stores rather than movdqa/movdqu, which differ only by a 66/F3
prefix (the saving does not exist under AVX/AVX512, so it is disabled there).
The identical size win is available for 128-bit loads but has never been taken:
an integer 128-bit load uses movdqa/movdqu for every -mtune.

Add the symmetric X86_TUNE_SSE_TYPELESS_LOADS, enabled for the same processors,
and use it in the 128-bit move patterns so a load emits movaps/movups
(aligned/unaligned respectively).  The substitution is value- and
alignment-exact -- movaps/movdqa both fault on 16-byte-unaligned memory,
movups/movdqu neither do -- and the existing AVX guard in the move patterns
leaves VEX-encoded loads unchanged.  It shrinks every 128-bit struct/array copy
load by a byte, matching the code clang already generates.

The historical asymmetry appears to be an omission rather than a deliberate
choice: typeless stores was introduced as a pure size optimization and no
typeless-loads counterpart was ever proposed; the load mnemonic has otherwise
only been revisited for correctness (e.g. PR target/92904).  On the targeted
cores a 128-bit load feeding an integer-domain consumer has no bypass
("reformatting") penalty, so as with the store the choice is size-only.

gcc/ChangeLog:

        * config/i386/x86-tune.def (X86_TUNE_SSE_TYPELESS_LOADS): New tune
        flag.
        * config/i386/i386.h (TARGET_SSE_TYPELESS_LOADS): New macro.
        * config/i386/sse.md (mov<mode>_internal): Select V4SF mode for a
        128-bit load when TARGET_SSE_TYPELESS_LOADS, so ix86_output_ssemov
        emits the shorter movups/movaps.
        * config/i386/i386.md (*movti_internal): Likewise.

gcc/testsuite/ChangeLog:

        * gcc.target/i386/sse-typeless-loads.c: New test.

Signed-off-by: Andrew Gaul <[email protected]>
---
 gcc/config/i386/i386.h                        |  2 ++
 gcc/config/i386/i386.md                       |  4 +++
 gcc/config/i386/sse.md                        |  4 +++
 gcc/config/i386/x86-tune.def                  |  7 +++++
 .../gcc.target/i386/sse-typeless-loads.c      | 28 +++++++++++++++++++
 5 files changed, 45 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/i386/sse-typeless-loads.c

diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 1b8785d7872..d0717e88794 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -394,6 +394,8 @@ extern unsigned char ix86_tune_features[X86_TUNE_LAST];
 #define TARGET_SSE_SPLIT_REGS  ix86_tune_features[X86_TUNE_SSE_SPLIT_REGS]
 #define TARGET_SSE_TYPELESS_STORES \
        ix86_tune_features[X86_TUNE_SSE_TYPELESS_STORES]
+#define TARGET_SSE_TYPELESS_LOADS \
+       ix86_tune_features[X86_TUNE_SSE_TYPELESS_LOADS]
 #define TARGET_SSE_LOAD0_BY_PXOR ix86_tune_features[X86_TUNE_SSE_LOAD0_BY_PXOR]
 #define TARGET_MEMORY_MISMATCH_STALL \
        ix86_tune_features[X86_TUNE_MEMORY_MISMATCH_STALL]
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index f173423b334..524576b180b 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -2577,6 +2577,10 @@
               (and (eq_attr "alternative" "5")
                    (match_test "TARGET_SSE_TYPELESS_STORES"))
                 (const_string "V4SF")
+              (and (eq_attr "alternative" "4")
+                   (match_test "MEM_P (operands[1])")
+                   (match_test "TARGET_SSE_TYPELESS_LOADS"))
+                (const_string "V4SF")
               ]
               (const_string "TI")))
    (set (attr "preferred_for_speed")
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 7f652a13c87..7b2e785713a 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -1521,6 +1521,10 @@
               (and (eq_attr "alternative" "4")
                    (match_test "TARGET_SSE_TYPELESS_STORES"))
                 (const_string "V4SF")
+              (and (eq_attr "alternative" "3")
+                   (match_test "MEM_P (operands[1])")
+                   (match_test "TARGET_SSE_TYPELESS_LOADS"))
+                (const_string "V4SF")
               (and (eq_attr "alternative" "0")
                    (match_test "TARGET_SSE_LOAD0_BY_PXOR"))
                 (const_string "TI")
diff --git a/gcc/config/i386/x86-tune.def b/gcc/config/i386/x86-tune.def
index d4c27351ad7..fbc5b352ed5 100644
--- a/gcc/config/i386/x86-tune.def
+++ b/gcc/config/i386/x86-tune.def
@@ -472,6 +472,13 @@ DEF_TUNE (X86_TUNE_SSE_TYPELESS_STORES, 
"sse_typeless_stores",
          m_AMD_MULTIPLE | m_ZHAOXIN | m_CORE_ALL | m_TREMONT | m_CORE_HYBRID
          | m_CORE_ATOM | m_C86_4G | m_GENERIC)
 
+/* X86_TUNE_SSE_TYPELESS_LOADS: Always movaps/movups for 128bit loads.  As with
+   typeless stores, the shorter (no 66/F3 prefix) encoding is used; on these
+   cores a load feeding an integer-domain consumer has no bypass penalty.  */
+DEF_TUNE (X86_TUNE_SSE_TYPELESS_LOADS, "sse_typeless_loads",
+         m_AMD_MULTIPLE | m_ZHAOXIN | m_CORE_ALL | m_TREMONT | m_CORE_HYBRID
+         | m_CORE_ATOM | m_C86_4G | m_GENERIC)
+
 /* X86_TUNE_SSE_LOAD0_BY_PXOR: Always use pxor to load0 as opposed to
    xorps/xorpd and other variants.  */
 DEF_TUNE (X86_TUNE_SSE_LOAD0_BY_PXOR, "sse_load0_by_pxor",
diff --git a/gcc/testsuite/gcc.target/i386/sse-typeless-loads.c 
b/gcc/testsuite/gcc.target/i386/sse-typeless-loads.c
new file mode 100644
index 00000000000..ce1b28d2f45
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse-typeless-loads.c
@@ -0,0 +1,28 @@
+/* Verify X86_TUNE_SSE_TYPELESS_LOADS: on tunings that use typeless 128-bit
+   stores, a 128-bit load should likewise use the one-byte-shorter 
movups/movaps
+   rather than movdqu/movdqa (which differ only by a 66/F3 prefix).  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -msse2 -mno-avx -mtune=generic" } */
+
+struct S16 { char c[16]; };
+
+/* Unaligned copy: movdqu load -> movups (matches the movups store).  */
+void
+copy_unaligned (struct S16 *d, const struct S16 *s)
+{
+  *d = *s;
+}
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+/* Aligned copy: movdqa load -> movaps (matches the movaps store).  */
+void
+copy_aligned (v4si *d, const v4si *s)
+{
+  *d = *s;
+}
+
+/* { dg-final { scan-assembler-not "movdqu" } } */
+/* { dg-final { scan-assembler-not "movdqa" } } */
+/* { dg-final { scan-assembler "movups" } } */
+/* { dg-final { scan-assembler "movaps" } } */
-- 
2.55.0

Reply via email to