Miscompiled c++ class with packed attribute on ARM with -Os optimizations

http://gcc.gnu.org/PR45070
https://bugs.gentoo.org/331641

--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -3036,7 +3036,7 @@ tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \
     $(TREE_H) $(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_H) $(TREE_INLINE_H) \
     $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) $(GIMPLE_H) \
     $(TREE_PASS_H) $(FLAGS_H) $(EXPR_H) $(BASIC_BLOCK_H) output.h \
-    $(GGC_H) $(OBSTACK_H) $(PARAMS_H) $(CPPLIB_H) $(PARAMS_H)
+    $(GGC_H) $(OBSTACK_H) $(PARAMS_H) $(CPPLIB_H) $(PARAMS_H) langhooks.h
 tree-complex.o : tree-complex.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
     $(TM_H) $(RTL_H) $(REAL_H) $(FLAGS_H) $(TREE_FLOW_H) $(GIMPLE_H) \
     tree-iterator.h $(TREE_PASS_H) tree-ssa-propagate.h $(DIAGNOSTIC_H)
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -13705,7 +13705,8 @@ arm_output_epilogue (rtx sibling)
 		  && !crtl->tail_call_emit)
 		{
 		  unsigned long mask;
-		  mask = (1 << (arm_size_return_regs() / 4)) - 1;
+                  /* Preserve return values, of any size.  */
+		  mask = (1 << ((arm_size_return_regs() + 3) / 4)) - 1;
 		  mask ^= 0xf;
 		  mask &= ~saved_regs_mask;
 		  reg = 0;
--- a/gcc/config/arm/linux-atomic.c
+++ b/gcc/config/arm/linux-atomic.c
@@ -56,7 +56,7 @@ typedef void (__kernel_dmb_t) (void);
 									\
     do {								\
       tmp = *ptr;							\
-      failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr);	\
+      failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
     } while (failure != 0);						\
 									\
     return tmp;								\
@@ -88,8 +88,8 @@ FETCH_AND_OP_WORD (nand, ~, &)
 									\
     do {								\
       oldval = *wordptr;						\
-      newval = ((PFX_OP ((oldval & mask) >> shift)			\
-                 INF_OP (unsigned int) val) << shift) & mask;		\
+      newval = ((PFX_OP (((oldval & mask) >> shift)			\
+			 INF_OP (unsigned int) val)) << shift) & mask;	\
       newval |= oldval & ~mask;						\
       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
     } while (failure != 0);						\
@@ -97,19 +97,19 @@ FETCH_AND_OP_WORD (nand, ~, &)
     return (RETURN & mask) >> shift;					\
   }
 
-SUBWORD_SYNC_OP (add,   , +, short, 2, oldval)
-SUBWORD_SYNC_OP (sub,   , -, short, 2, oldval)
-SUBWORD_SYNC_OP (or,    , |, short, 2, oldval)
-SUBWORD_SYNC_OP (and,   , &, short, 2, oldval)
-SUBWORD_SYNC_OP (xor,   , ^, short, 2, oldval)
-SUBWORD_SYNC_OP (nand, ~, &, short, 2, oldval)
+SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
+SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
 
-SUBWORD_SYNC_OP (add,   , +, char, 1, oldval)
-SUBWORD_SYNC_OP (sub,   , -, char, 1, oldval)
-SUBWORD_SYNC_OP (or,    , |, char, 1, oldval)
-SUBWORD_SYNC_OP (and,   , &, char, 1, oldval)
-SUBWORD_SYNC_OP (xor,   , ^, char, 1, oldval)
-SUBWORD_SYNC_OP (nand, ~, &, char, 1, oldval)
+SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
+SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
 
 #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
   int HIDDEN								\
@@ -119,10 +119,10 @@ SUBWORD_SYNC_OP (nand, ~, &, char, 1, oldval)
 									\
     do {								\
       tmp = *ptr;							\
-      failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr);	\
+      failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
     } while (failure != 0);						\
 									\
-    return PFX_OP tmp INF_OP val;					\
+    return PFX_OP (tmp INF_OP val);					\
   }
 
 OP_AND_FETCH_WORD (add,   , +)
@@ -132,19 +132,19 @@ OP_AND_FETCH_WORD (and,   , &)
 OP_AND_FETCH_WORD (xor,   , ^)
 OP_AND_FETCH_WORD (nand, ~, &)
 
-SUBWORD_SYNC_OP (add,   , +, short, 2, newval)
-SUBWORD_SYNC_OP (sub,   , -, short, 2, newval)
-SUBWORD_SYNC_OP (or,    , |, short, 2, newval)
-SUBWORD_SYNC_OP (and,   , &, short, 2, newval)
-SUBWORD_SYNC_OP (xor,   , ^, short, 2, newval)
-SUBWORD_SYNC_OP (nand, ~, &, short, 2, newval)
+SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
+SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
 
-SUBWORD_SYNC_OP (add,   , +, char, 1, newval)
-SUBWORD_SYNC_OP (sub,   , -, char, 1, newval)
-SUBWORD_SYNC_OP (or,    , |, char, 1, newval)
-SUBWORD_SYNC_OP (and,   , &, char, 1, newval)
-SUBWORD_SYNC_OP (xor,   , ^, char, 1, newval)
-SUBWORD_SYNC_OP (nand, ~, &, char, 1, newval)
+SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
+SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
 
 int HIDDEN
 __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
@@ -194,8 +194,8 @@ __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
       }									\
   }
 
-SUBWORD_VAL_CAS (short, 2)
-SUBWORD_VAL_CAS (char,  1)
+SUBWORD_VAL_CAS (unsigned short, 2)
+SUBWORD_VAL_CAS (unsigned char,  1)
 
 typedef unsigned char bool;
 
@@ -216,8 +216,8 @@ __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
     return (oldval == actual_oldval);					\
   }
 
-SUBWORD_BOOL_CAS (short, 2)
-SUBWORD_BOOL_CAS (char,  1)
+SUBWORD_BOOL_CAS (unsigned short, 2)
+SUBWORD_BOOL_CAS (unsigned char,  1)
 
 void HIDDEN
 __sync_synchronize (void)
@@ -259,8 +259,8 @@ __sync_lock_test_and_set_4 (int *ptr, int val)
     return (oldval & mask) >> shift;					\
   }
 
-SUBWORD_TEST_AND_SET (short, 2)
-SUBWORD_TEST_AND_SET (char,  1)
+SUBWORD_TEST_AND_SET (unsigned short, 2)
+SUBWORD_TEST_AND_SET (unsigned char,  1)
 
 #define SYNC_LOCK_RELEASE(TYPE, WIDTH)					\
   void HIDDEN								\
--- a/gcc/tree-switch-conversion.c
+++ b/gcc/tree-switch-conversion.c
@@ -96,6 +96,7 @@ eight) times the number of the actual switch branches. */
 #include "diagnostic.h"
 #include "tree-dump.h"
 #include "timevar.h"
+#include "langhooks.h"
 
 /* The main structure of the pass.  */
 struct switch_conv_info
@@ -693,9 +694,11 @@ gen_inbound_check (gimple swtch)
 
   /* Make sure we do not generate arithmetics in a subrange.  */
   if (TREE_TYPE (TREE_TYPE (info.index_expr)))
-    utype = unsigned_type_for (TREE_TYPE (TREE_TYPE (info.index_expr)));
+    utype = lang_hooks.types.type_for_mode
+      (TYPE_MODE (TREE_TYPE (TREE_TYPE (info.index_expr))), 1);
   else
-    utype = unsigned_type_for (TREE_TYPE (info.index_expr));
+    utype = lang_hooks.types.type_for_mode
+      (TYPE_MODE (TREE_TYPE (info.index_expr)), 1);
 
   /* (end of) block 0 */
   gsi = gsi_for_stmt (info.arr_ref_first);