http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58192
Jakub Jelinek <jakub at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jakub at gcc dot gnu.org, | |rth at gcc dot gnu.org --- Comment #5 from Jakub Jelinek <jakub at gcc dot gnu.org> --- The difference is that in the S1::<anon struct>::set(Foo) case setup_incoming_promotions in combine.c says that the argument has non-zero mask of 0xff, because the function is local to the TU, while for S2 all caller's aren't guaranteed local and thus it isn't optimized. The argument type is Foo (QImode) passed in SImode register, guess combine.c expects that caller zero-extends in this case, but it doesn't (in neither case). enum class Foo : unsigned char { FOO }; struct S1 { struct { unsigned int i; void __attribute__((noinline)) set (Foo foo) { i = static_cast<unsigned int> (foo); } } x; }; struct S2 { struct X { unsigned int i; void __attribute__((noinline)) set (Foo foo) { i = static_cast<unsigned int> (foo); } }; X x; }; struct T { unsigned short i; Foo f; unsigned char c; }; __attribute__((noinline)) void baz (unsigned int x) { if (x != 0) __builtin_abort (); } void bar (T t) { S1 s1; s1.x.set(t.f); baz (s1.x.i); S2 s2; s2.x.set(t.f); baz (s2.x.i); } int main() { T t = { 0xabcd, Foo::FOO, 0xef}; bar (t); } For: unsigned int v1, v2; __attribute__((noinline, noclone)) static void foo (unsigned char a) { v1 = a; } __attribute__((noinline, noclone)) void bar (unsigned char a) { v2 = a; } void baz (unsigned int a) { foo (a); bar (a); } the situation is similar for foo/bar (foo optimizes and doesn't zero extend from 8 to 32 bits, bar doesn't), but the caller in this case always zero extends.