https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118481
--- Comment #2 from Richard Biener <rguenth at gcc dot gnu.org> --- I think we should be able to VN them the same so that PRE discovers the full redundancy. Oh we do: Value numbering stmt = _4 = VIEW_CONVERT_EXPR<unsigned char>(a.0_1); Setting value number of _4 to _4 (changed) ... Value numbering stmt = iftmp.1_6 = BIT_FIELD_REF <a, 8, 0>; Setting value number of iftmp.1_6 to _4 (changed) ... Value numbering stmt = iftmp.1_2 = PHI <iftmp.1_6(3), _4(4)> Setting value number of iftmp.1_2 to _4 (changed) I think what's missing for PRE to work is BIT_INSERT_EXPR <@0, V_C_E <@0>, ...> simplification to @0 - IIRC we only have BIT_INSERT <..., BIT_FIELD_REF ..> simplification here. That said, we fail to apply code hoisting because a.0_1 = a is AVAIL_OUT in BB 2. We can't eliminate iftmp.1_2 as theres no value available for replacement and we don't consider inserting the conversion from a.0_1 here. It works for typedef unsigned char v1 __attribute__((__vector_size__(1))); unsigned char vext_p64(v1 a, unsigned n) { unsigned char r = (n == 0) ? a[n] : a[0]; return r; } because we have two BIT_FIELD_REF from memory in this case. It also works for typedef unsigned char v1 __attribute__((__vector_size__(1))); unsigned char vext_p64(v1 a, unsigned n) { v1 x = a; unsigned char r = (n == 0) ? x[n] : x[0]; return r; } because we have two V_C_E from register in this case.