+2004-09-28  Richard Henderson  <rth@redhat.com>
+
+       PR 17531
+       * expr.c (expand_expr_addr_expr_1): Only assemble_external for decls.
+       Don't check VOIDmode here.  Force PLUS operands to common type.
+       (expand_expr_addr_expr): Do VOIDmode check earlier.  Force use of
+       Pmode if given a non pointer type.
+
+2004-09-28  Zdenek Dvorak  <rakdver@atrey.karlin.mff.cuni.cz>
+
+       PR 17531
+       * optabs.c (expand_binop): Force constants to the correct mode.
+
 2004-09-28  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * config/s390/s390.c (s390_adjust_cost): Remove.
 
     case CONST_DECL:
       /* Recurse and make the output_constant_def clause above handle this.  */
       return expand_expr_addr_expr_1 (DECL_INITIAL (exp), target,
-                                   tmode, modifier);
+                                     tmode, modifier);
 
     case REALPART_EXPR:
       /* The real part of the complex number is always first, therefore
          result = XEXP (result, 0);
 
          /* ??? Is this needed anymore?  */
-         if (!TREE_USED (exp) == 0)
+         if (DECL_P (exp) && !TREE_USED (exp) == 0)
            {
              assemble_external (exp);
              TREE_USED (exp) = 1;
   subtarget = offset || bitpos ? NULL_RTX : target;
   result = expand_expr_addr_expr_1 (inner, subtarget, tmode, modifier);
 
-  if (tmode == VOIDmode)
-    {
-      tmode = GET_MODE (result);
-      if (tmode == VOIDmode)
-       tmode = Pmode;
-    }
-
   if (offset)
     {
       rtx tmp;
        result = force_operand (result, NULL);
       tmp = expand_expr (offset, NULL, tmode, EXPAND_NORMAL);
 
+      result = convert_memory_address (tmode, result);
+      tmp = convert_memory_address (tmode, tmp);
+
       if (modifier == EXPAND_SUM)
        result = gen_rtx_PLUS (tmode, result, tmp);
       else
     {
       /* Someone beforehand should have rejected taking the address
         of such an object.  */
-      gcc_assert (!(bitpos % BITS_PER_UNIT));
+      gcc_assert ((bitpos % BITS_PER_UNIT) == 0);
 
       result = plus_constant (result, bitpos / BITS_PER_UNIT);
       if (modifier < EXPAND_SUM)
   enum machine_mode rmode;
   rtx result;
 
+  /* Target mode of VOIDmode says "whatever's natural".  */
+  if (tmode == VOIDmode)
+    tmode = TYPE_MODE (TREE_TYPE (exp));
+
+  /* We can get called with some Weird Things if the user does silliness
+     like "(short) &a".  In that case, convert_memory_address won't do
+     the right thing, so ignore the given target mode.  */
+  if (!targetm.valid_pointer_mode (tmode))
+    tmode = Pmode;
+
   result = expand_expr_addr_expr_1 (TREE_OPERAND (exp, 0), target,
                                    tmode, modifier);
 
   /* Despite expand_expr claims concerning ignoring TMODE when not
-     strictly convenient, stuff breaks if we don't honor it.  */
-  if (tmode == VOIDmode)
-    tmode = TYPE_MODE (TREE_TYPE (exp));
+     strictly convenient, stuff breaks if we don't honor it.  Note
+     that combined with the above, we only do this for pointer modes.  */
   rmode = GET_MODE (result);
   if (rmode == VOIDmode)
     rmode = tmode;
   if (rmode != tmode)
     result = convert_memory_address (tmode, result);
- 
+
   return result;
 }
 
 
      force expensive constants into a register.  */
   if (CONSTANT_P (op0) && optimize
       && rtx_cost (op0, binoptab->code) > COSTS_N_INSNS (1))
-    op0 = force_reg (mode, op0);
+    {
+      if (GET_MODE (op0) != VOIDmode)
+       op0 = convert_modes (mode, VOIDmode, op0, unsignedp);
+      op0 = force_reg (mode, op0);
+    }
 
   if (CONSTANT_P (op1) && optimize
       && ! shift_op && rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1))
-    op1 = force_reg (mode, op1);
+    {
+      if (GET_MODE (op1) != VOIDmode)
+       op1 = convert_modes (mode, VOIDmode, op1, unsignedp);
+      op1 = force_reg (mode, op1);
+    }
 
   /* Record where to delete back to if we backtrack.  */
   last = get_last_insn ();