Hongtao did warn me there might be corner cases in STV when
converting DImode SUBREGs to use V2DImode vector registers.
https://gcc.gnu.org/pipermail/gcc-patches/2026-May/718615.html
is exactly such a case, where with -m32 -O2 -march=cascadelake
we attempt to convert a comparison containing (subreg:DI (reg:DD) 0)
where the resulting call to gen_lowpart (V2DImode, ...) triggers
an ICE. It's the lack of transitivity: DDmode may be SUBREGed to
DImode, and DImode may be SUBREGed to V2DImode, but DDmode can't
(directly) be SUBREGed to V2DImode.
Alas my attempts to reduce a testcase (simpler than the one
already in the testsuite) haven't be successful... The example
below contains the problematic SUBREG in a comparison, but in
this case, STV doesn't consider converting the chain profitable.
typedef float __decfloat64 __attribute__((mode(DD)));
__decfloat64 ext();
__decfloat64 x,y,z;
void foo()
{
__decfloat64 t = ext();
if (__builtin_memcmp((void*)&t,(void*)&x,sizeof(__decfloat64)) != 0
&& __builtin_memcmp((void*)&t,(void*)&y,sizeof(__decfloat64)) != 0
&& __builtin_memcmp((void*)&t,(void*)&z,sizeof(__decfloat64)) != 0) {
x = t;
y = t;
z = t;
}
}
Fortunately, the proposed fix is relatively straight forward, using
modes_tieable_p to check that the source pseudo is representable in
an SSE register. When originally written, this code was expecting
V1TImode, V2DImode or V4SImode. If this isn't a simple SUBREG, STV
can emit a move instruction (to preserve it), then operate on the
resulting pseudo (as usual).
This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with no new failures. Ok for mainline?
2026-06-14 Roger Sayle <[email protected]>
gcc/ChangeLog
* config/i386/i386-features.cc (scalar_chain::convert_op): Check
if the (DImode) SUBREG being converted by STV has an original mode
that's tieable to the vector mode (V2DImode), if not (e.g. DDmode)
emit this "conversion" as a separate move for reload to handle.
Thanks in advance (and apologies for any inconvenience),
Roger
--
diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc
index 0694811e9da..1ad705cb356 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -1169,7 +1169,16 @@ scalar_chain::convert_op (rtx *op, rtx_insn *insn)
{
gcc_assert (SUBREG_P (*op));
if (GET_MODE (*op) != vmode)
- *op = gen_lowpart (vmode, *op);
+ {
+ if (!targetm.modes_tieable_p (vmode, GET_MODE (SUBREG_REG (*op))))
+ {
+ tmp = gen_reg_rtx (GET_MODE (*op));
+ emit_insn_before (gen_rtx_SET (tmp, *op), insn);
+ *op = gen_rtx_SUBREG (vmode, tmp, 0);
+ }
+ else
+ *op = gen_lowpart (vmode, *op);
+ }
}
}