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