(mips_split_addresses): New variable.
authorJim Wilson <wilson@gcc.gnu.org>
Mon, 17 Jun 1996 18:58:25 +0000 (11:58 -0700)
committerJim Wilson <wilson@gcc.gnu.org>
Mon, 17 Jun 1996 18:58:25 +0000 (11:58 -0700)
(simple_memory_operand): Add comment about mode check.  Add check
for LO_SUM.
(call_insn_operand): OP is now an addresses instead of a MEM.
(move_operand, mips_check_split): New functions.
(mips_count_memory_refs): Add check for LO_SUM.
(mips_move_1word): Add HIGH support.
(mips_address_cost): Delete check for HIGH.
(output_block_move): Handle LO_SUM addresses.
(override_options): Set mips_split_addresses.
(print_operand_address): Add LO_SUM support.

From-SVN: r12285

gcc/config/mips/mips.c

index 783d93cae9976b6bab87e698b4969203269014af..64c6ce790047d5967d63c168450fad13da8c87b4 100644 (file)
@@ -203,6 +203,9 @@ char *mips_cpu_string;              /* for -mcpu=<xxx> */
 char *mips_isa_string;         /* for -mips{1,2,3,4} */
 char *mips_abi_string;         /* for -mabi={32,n32,64} */
 
+/* If TRUE, we split addresses into their high and low parts in the RTL.  */
+int mips_split_addresses;
+
 /* Generating calls to position independent functions?  */
 enum mips_abicalls_type mips_abicalls;
 
@@ -536,6 +539,9 @@ simple_memory_operand (op, mode)
     return FALSE;
 
   /* dword operations really put out 2 instructions, so eliminate them.  */
+  /* ??? This isn't strictly correct.  It is OK to accept multiword modes
+     here, since the length attributes are being set correctly, but only
+     if the address is offsettable.  LO_SUM is not offsettable.  */
   if (GET_MODE_SIZE (GET_MODE (op)) > UNITS_PER_WORD)
     return FALSE;
 
@@ -547,6 +553,7 @@ simple_memory_operand (op, mode)
       break;
 
     case REG:
+    case LO_SUM:
       return TRUE;
 
     case CONST_INT:
@@ -655,15 +662,61 @@ call_insn_operand (op, mode)
      rtx op;
      enum machine_mode mode;
 {
-  if (GET_CODE (op) == MEM
-      && (CONSTANT_ADDRESS_P (XEXP (op, 0))
-         || (GET_CODE (XEXP (op, 0)) == REG
-             && XEXP (op, 0) != arg_pointer_rtx
-             && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
-                  && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
+  if (CONSTANT_ADDRESS_P (op)
+      || (GET_CODE (op) == REG && op != arg_pointer_rtx
+         && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER
+               && REGNO (op) <= LAST_VIRTUAL_REGISTER)))
     return 1;
   return 0;
 }
+
+/* Return true if OPERAND is valid as a source operand for a move
+   instruction.  */
+
+int
+move_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (general_operand (op, mode)
+         && ! (mips_split_addresses && mips_check_split (op, mode)));
+}
+
+/* Return true if we split the address into high and low parts.  */
+
+/* ??? We should also handle reg+array somewhere.  We get four
+   instructions currently, lui %hi/addui %lo/addui reg/lw.  Better is
+   lui %hi/addui reg/lw %lo.  Fixing GO_IF_LEGITIMATE_ADDRESS to accept
+   (plus (reg) (symbol_ref)) doesn't work because the SYMBOL_REF is broken
+   out of the address, then we have 4 instructions to combine.  Perhaps
+   add a 3->2 define_split for combine.  */
+
+/* ??? We could also split a CONST_INT here if it is a large_int().
+   However, it doesn't seem to be very useful to have %hi(constant).
+   We would be better off by doing the masking ourselves and then putting
+   the explicit high part of the constant in the RTL.  This will give better
+   optimization.  Also, %hi(constant) needs assembler changes to work.
+   There is already a define_split that does this.  */
+
+int
+mips_check_split (address, mode)
+     rtx address;
+     enum machine_mode mode;
+{     
+  /* ??? This is the same check used in simple_memory_operand.
+     We use it here because LO_SUM is not offsettable.  */
+  if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+    return 0;
+
+  if ((GET_CODE (address) == SYMBOL_REF && ! SYMBOL_REF_FLAG (address))
+      || (GET_CODE (address) == CONST
+         && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF
+         && ! SYMBOL_REF_FLAG (XEXP (XEXP (address, 0), 0)))
+      || GET_CODE (address) == LABEL_REF)
+    return 1;
+
+  return 0;
+}
 \f
 /* Returns an operand string for the given instruction's delay slot,
    after updating filled delay slot statistics.
@@ -787,6 +840,7 @@ mips_count_memory_refs (op, num)
 
        case REG:
        case CONST_INT:
+       case LO_SUM:
          break;
 
        case PLUS:
@@ -1179,6 +1233,12 @@ mips_move_1word (operands, insn, unsignedp)
          operands[3] = add_op1;
          ret = "add%:\t%0,%2,%3";
        }
+
+      else if (code1 == HIGH)
+       {
+         operands[1] = XEXP (op1, 0);
+         ret = "lui\t%0,%%hi(%1)";
+       }
     }
 
   else if (code0 == MEM)
@@ -1622,7 +1682,6 @@ mips_address_cost (addr)
       break;
 
     case LO_SUM:
-    case HIGH:
       return 1;
 
     case LABEL_REF:
@@ -2554,6 +2613,42 @@ output_block_move (insn, operands, num_regs, move_type)
        }
     }
 
+  /* ??? We really shouldn't get any LO_SUM addresses here, because they
+     are not offsettable, however, offsettable_address_p says they are
+     offsettable. I think this is a bug in offsettable_address_p.
+     For expediency, we fix this by just loading the address into a register
+     if we happen to get one.  */
+
+  if (GET_CODE (src_reg) == LO_SUM)
+    {
+      src_reg = operands[ 3 + num_regs-- ];
+      if (move_type != BLOCK_MOVE_LAST)
+       {
+         xoperands[2] = XEXP (XEXP (operands[1], 0), 1);
+         xoperands[1] = XEXP (XEXP (operands[1], 0), 0);
+         xoperands[0] = src_reg;
+         if (Pmode == DImode)
+           output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
+         else
+           output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
+       }
+    }
+
+  if (GET_CODE (dest_reg) == LO_SUM)
+    {
+      dest_reg = operands[ 3 + num_regs-- ];
+      if (move_type != BLOCK_MOVE_LAST)
+       {
+         xoperands[2] = XEXP (XEXP (operands[0], 0), 1);
+         xoperands[1] = XEXP (XEXP (operands[0], 0), 0);
+         xoperands[0] = dest_reg;
+         if (Pmode == DImode)
+           output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
+         else
+           output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
+       }
+    }
+
   if (num_regs > (sizeof (load_store) / sizeof (load_store[0])))
     num_regs = (sizeof (load_store) / sizeof (load_store[0]));
 
@@ -3436,6 +3531,14 @@ override_options ()
       mips_section_threshold = 0x7fffffff;
     }
 
+  /* ??? This does not work when target addresses are DImode.
+     This is because we are missing DImode high/lo_sum patterns.  */
+
+  if (TARGET_GAS && optimize && ! flag_pic && Pmode == SImode)
+    mips_split_addresses = 1;
+  else
+    mips_split_addresses = 0;
+
   /* -mrnames says to use the MIPS software convention for register
      names instead of the hardware names (ie, $a0 instead of $4).
      We do this by switching the names in mips_reg_names, which the
@@ -3918,6 +4021,23 @@ print_operand_address (file, addr)
        fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
        break;
 
+      case LO_SUM:
+       {
+         register rtx arg0   = XEXP (addr, 0);
+         register rtx arg1   = XEXP (addr, 1);
+
+         if (! mips_split_addresses)
+           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM.");
+
+         if (GET_CODE (arg0) != REG)
+           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG.");
+
+         fprintf (file, "%%lo(");
+         print_operand_address (file, arg1);
+         fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
+       }
+       break;
+
       case PLUS:
        {
          register rtx reg    = (rtx)0;
@@ -3948,8 +4068,8 @@ print_operand_address (file, addr)
          if (!CONSTANT_P (offset))
            abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
 
-       if (REGNO (reg) == ARG_POINTER_REGNUM)
-         abort_with_insn (addr, "Arg pointer not eliminated.");
+         if (REGNO (reg) == ARG_POINTER_REGNUM)
+           abort_with_insn (addr, "Arg pointer not eliminated.");
 
          output_addr_const (file, offset);
          fprintf (file, "(%s)", reg_names [REGNO (reg)]);