Hi Guys,

  A bug reported against the RL78 port has a uncovered a generic problem
  with expand_expr_real_1() function - part of the code their computes
  the offset of an address for an array reference, and if necessary
  makes sure that it is in the correct mode:

        if (offset)
          {
            machine_mode address_mode;
            rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode,
                                          EXPAND_SUM);

            gcc_assert (MEM_P (op0));

            address_mode = get_address_mode (op0);
            if (GET_MODE (offset_rtx) != address_mode)
              offset_rtx = convert_to_mode (address_mode, offset_rtx, 0);

  The problem here is that convert_to_mode() may decide to offload
  offset_rtx into another reg and then extend the reg to address_mode,
  but it assumes that offset_rtx is a valid rtl expression for the
  target.

  Offset_rtx probably looks like this:

    (plus (mult (reg) (const_int)) (const_int))

  For the RL78, and possibly other targets, this is valid as an address,
  but not as standalone expression.  (The RL78 does not have a
  multiply-and-add instruction, but it does have register+offset indexed
  addressing).  The end result then is an ICE like this:

    error: unrecognizable insn:
      (insn 10 9 11 2 (set (subreg:HI (reg:SI 48) 0)
                           (plus:HI (mult:HI (reg:HI 44)
                                             (const_int 2))
                                    (reg:HI 46))))

  I don't think that convert_to_mode is at fault here however - it is
  written assuming that its input is valid.  So instead I am asking for
  comments on the patch below.  (I realise that the sources are in stage
  4 and so this patch is not suitable for application at this time).

  The patch adds a second call to expand_expr(), giving the address mode
  as the suggested mode, and using a normal expansion, rather than
  EXPAND_SUM.  This might work, but even if it does not the rtl in
  offset_rtx will be valid in a non-address context so that whatever
  convert_to_mode does will still remain valid.

  What do people think ?  Is this the right approach ?

  I have tested the patch with an rl78-elf toolchain as well as an
  x86_64-pc-linux-gnu toolchain and there were no regressions in the gcc
  testsuite for either of these targets.

Cheers
  Nick

Index: gcc/expr.c
===================================================================
--- gcc/expr.c  (revision 221374)
+++ gcc/expr.c  (working copy)
@@ -10257,6 +10257,15 @@
 
            address_mode = get_address_mode (op0);
+ 
+           if (GET_MODE (offset_rtx) != address_mode)
+             /* Before calling convert_to_mode, try expanding the offset as a
+                normal expression in the address mode.  Even if this fails to
+                choose that mode, it has the benefit that any RTL generated
+                will be valid even outside of an address computation, a
+                result that is not achieved with EXPAND_SUM.  */
+               offset_rtx = expand_expr (offset, NULL_RTX, get_address_mode 
(op0),
+                                         EXPAND_NORMAL);
+
            if (GET_MODE (offset_rtx) != address_mode)
              offset_rtx = convert_to_mode (address_mode, offset_rtx, 0);
 
            /* See the comment in expand_assignment for the rationale.  */

Reply via email to