public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Explicitly record which registers are inaccessible
@ 2011-09-25 19:38 Richard Sandiford
  2011-10-04 10:47 ` Bernd Schmidt
  0 siblings, 1 reply; 5+ messages in thread
From: Richard Sandiford @ 2011-09-25 19:38 UTC (permalink / raw)
  To: gcc-patches

This patch tries to fix one thing that has bugged me for a while:
there's no way of explicitly saying that some hard registers are
only available with certain target options.  The usual approach is
to make TARGET_CONDITIONAL_REGISTER_USAGE fix any registers that
don't exist, but this doesn't in itself stop the register from
being used in things like register variables.  The target really
has to make sure that HARD_REGNO_MODE_OK is false for non-existent
registers too.  And that means duplicating the logic in at least
two places.

If HARD_REGNO_MODE_OK doesn't check for inaccessible registers, those
registers will still seem to be both legitimate register_operands and
legitimate register variables.  If they are then used as register variables,
the likelihood is that we'll either ICE or silently generate code to
access something that doesn't exist.

The patch adds a new HARD_REG_SET, accessible_regs, to say which
registers can be accessed.  It explicitly checks whether register
variables belong to this set, and raises a (hopefully) more user-
friendly error message if not.  Also, to catch internal compiler bugs,
it makes sure that inaccessible registers are never treated as register
operands.

The last bit is indirect, via a new HARD_REG_SET called operand_reg_set.
And this set is the reason why I'm sending the patch now.  The MIPS16 port
has always had a problem with the HI and LO registers: they can only be
set by multiplication and division instructions, and only read by MFHI
and MFLO.  Unlike normal MIPS, there are no MTHI and MTLO instructions.

However, as for normal MIPS, we still exposed HI and LO as allocatable
registers.  This caused real problems if the register allocator wanted
to move something _into_ HI and LO.  In the old days, there was code to
move things into LO by faking an appropriate multiplication, but it
didn't handle the general case of a full HI/LO reload, and it was
something that we never wanted to generate anyway.  But because the
multiplication and division latencies are high, we do still want to
model the multiplication/division and MFHI/MFLO as separately-schedulable
RTL instructions.

For a while we tried to work around this by making the cost of moving
things into LO and HI very high.  But that was always a hack.  Then,
when IRA's cover classes came along, I went for a kind-of get-out: don't
include HI and LO in cover classes.  That was still a bit hackish, but
felt cleaner than relying on costs.  Now that we use pressure classes
instead (a good thing), I'm finally going to try to fix this "properly".
And that means (a) fixing HI and LO and (b) stopping them from being
treated as register operands.  (b) is important because if we start
out with this (valid) instruction before reload:

    (set (reg:SI lo) (mult:SI (reg:SI A) (reg:SI B)))

and we find that the multiplication is a constant, we'll try to match:

    (set (reg:SI lo) (const_int RESULT))

And because this is before reload, that pattern matches the normal
move patterns.  So we end up with unintended (and unimplementable)
moves into the registers even when they are fixed.

(Normally we'd expect the tree-level optimisers to optimise cases like
this, but we shouldn't rely on that, and it seems there are times when
they don't.)

An alternative to (b) would be to only expose HI and LO after reload.
It just feels like that's working around a limitation in the target
description rather than a real fix.

Tested on x86_64-linux-gnu and mips64-linux-gnu.  OK to install?

Richard


gcc/
	* hard-reg-set.h (target_hard_regs): Add x_accessible_reg_set
	and x_operand_reg_set.
	(accessible_reg_set, operand_reg_set): New macros.
	* reginfo.c (init_reg_sets): Initialize accessible_reg_set and
	operand_reg_set.
	(saved_accessible_reg_set, saved_operand_reg_set): New variables.
	(save_register_info): Save them.
	(restore_register_info): Restore them.
	(init_reg_sets_1): Limit operand_reg_set to accessible_reg_set.
	Remove NO_REGS registers from operand_reg_set.  Treat members
	of operand_reg_set as fixed.
	* recog.c (general_operand): Check operand_reg_set rather than
	NO_REGS.
	(register_operand, nonmemory_operand): Likewise.
	* varasm.c (make_decl_rtl): Always use DECL_MODE as the mode of
	register variables.  Check accessible_reg_set and operand_reg_set.
	* config/mips/mips.c (mips_conditional_register_usage): Remove
	inaccessible register from accessible_reg_set, rather than just
	making them fixed.

gcc/testsuite/
	* gcc.target/mips/mips.exp (mips-dg-options): Make -mno-dsp
	imply -mno-dspr2.
	* gcc.target/mips/no-dsp-1.c: New test.
	* gcc.target/mips/soft-float-1.c: Likewise.

Index: gcc/hard-reg-set.h
===================================================================
--- gcc/hard-reg-set.h	2011-09-25 17:36:28.000000000 +0100
+++ gcc/hard-reg-set.h	2011-09-25 18:13:47.000000000 +0100
@@ -583,6 +583,13 @@ #define EXECUTE_IF_SET_IN_HARD_REG_SET(S
 extern char global_regs[FIRST_PSEUDO_REGISTER];
 
 struct target_hard_regs {
+  /* The set of registers that actually exist on the current target.  */
+  HARD_REG_SET x_accessible_reg_set;
+
+  /* The set of registers that should be considered to be register
+     operands.  It is a subset of x_accessible_reg_set.  */
+  HARD_REG_SET x_operand_reg_set;
+
   /* Indexed by hard register number, contains 1 for registers
      that are fixed use (stack pointer, pc, frame pointer, etc.;.
      These are the registers that cannot be used to allocate
@@ -659,6 +666,10 @@ struct target_hard_regs {
 #define this_target_hard_regs (&default_target_hard_regs)
 #endif
 
+#define accessible_reg_set \
+  (this_target_hard_regs->x_accessible_reg_set)
+#define operand_reg_set \
+  (this_target_hard_regs->x_operand_reg_set)
 #define fixed_regs \
   (this_target_hard_regs->x_fixed_regs)
 #define fixed_reg_set \
Index: gcc/reginfo.c
===================================================================
--- gcc/reginfo.c	2011-09-25 17:36:27.000000000 +0100
+++ gcc/reginfo.c	2011-09-25 18:15:18.000000000 +0100
@@ -189,6 +189,9 @@ init_reg_sets (void)
   memcpy (reg_alloc_order, initial_reg_alloc_order, sizeof reg_alloc_order);
 #endif
   memcpy (reg_names, initial_reg_names, sizeof reg_names);
+
+  SET_HARD_REG_SET (accessible_reg_set);
+  SET_HARD_REG_SET (operand_reg_set);
 }
 
 /* Initialize may_move_cost and friends for mode M.  */
@@ -289,6 +292,8 @@ init_move_cost (enum machine_mode m)
 static char saved_call_really_used_regs[FIRST_PSEUDO_REGISTER];
 #endif
 static const char *saved_reg_names[FIRST_PSEUDO_REGISTER];
+static HARD_REG_SET saved_accessible_reg_set;
+static HARD_REG_SET saved_operand_reg_set;
 
 /* Save the register information.  */
 void
@@ -312,6 +317,8 @@ save_register_info (void)
   /* And similarly for reg_names.  */
   gcc_assert (sizeof reg_names == sizeof saved_reg_names);
   memcpy (saved_reg_names, reg_names, sizeof reg_names);
+  COPY_HARD_REG_SET (saved_accessible_reg_set, accessible_reg_set);
+  COPY_HARD_REG_SET (saved_operand_reg_set, operand_reg_set);
 }
 
 /* Restore the register information.  */
@@ -327,6 +334,8 @@ restore_register_info (void)
 #endif
 
   memcpy (reg_names, saved_reg_names, sizeof reg_names);
+  COPY_HARD_REG_SET (accessible_reg_set, saved_accessible_reg_set);
+  COPY_HARD_REG_SET (operand_reg_set, saved_operand_reg_set);
 }
 
 /* After switches have been processed, which perhaps alter
@@ -452,8 +461,27 @@ init_reg_sets_1 (void)
   else
     CLEAR_REG_SET (regs_invalidated_by_call_regset);
 
+  AND_HARD_REG_SET (operand_reg_set, accessible_reg_set);
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     {
+      /* As a special exception, registers whose class is NO_REGS are
+	 not accepted by `register_operand'.  The reason for this change
+	 is to allow the representation of special architecture artifacts
+	 (such as a condition code register) without extending the rtl
+	 definitions.  Since registers of class NO_REGS cannot be used
+	 as registers in any case where register classes are examined,
+	 it is better to apply this exception in a target-independent way.  */
+      if (REGNO_REG_CLASS (i) == NO_REGS)
+	CLEAR_HARD_REG_BIT (operand_reg_set, i);
+
+      /* If a register is too limited to be treated as a register operand,
+	 then it should never be allocated to a psuedo.  */
+      if (!TEST_HARD_REG_BIT (operand_reg_set, i))
+	{
+	  fixed_regs[i] = 1;
+	  call_used_regs[i] = 1;
+	}
+
       /* call_used_regs must include fixed_regs.  */
       gcc_assert (!fixed_regs[i] || call_used_regs[i]);
 #ifdef CALL_REALLY_USED_REGISTERS
Index: gcc/recog.c
===================================================================
--- gcc/recog.c	2011-09-25 17:36:28.000000000 +0100
+++ gcc/recog.c	2011-09-25 18:13:47.000000000 +0100
@@ -925,10 +925,7 @@ next_insn_tests_no_inequality (rtx insn)
    it has.
 
    The main use of this function is as a predicate in match_operand
-   expressions in the machine description.
-
-   For an explanation of this function's behavior for registers of
-   class NO_REGS, see the comment for `register_operand'.  */
+   expressions in the machine description.  */
 
 int
 general_operand (rtx op, enum machine_mode mode)
@@ -998,9 +995,8 @@ general_operand (rtx op, enum machine_mo
     }
 
   if (code == REG)
-    /* A register whose class is NO_REGS is not a general operand.  */
     return (REGNO (op) >= FIRST_PSEUDO_REGISTER
-	    || REGNO_REG_CLASS (REGNO (op)) != NO_REGS);
+	    || in_hard_reg_set_p (operand_reg_set, GET_MODE (op), REGNO (op)));
 
   if (code == MEM)
     {
@@ -1033,15 +1029,7 @@ address_operand (rtx op, enum machine_mo
    If MODE is VOIDmode, accept a register in any mode.
 
    The main use of this function is as a predicate in match_operand
-   expressions in the machine description.
-
-   As a special exception, registers whose class is NO_REGS are
-   not accepted by `register_operand'.  The reason for this change
-   is to allow the representation of special architecture artifacts
-   (such as a condition code register) without extending the rtl
-   definitions.  Since registers of class NO_REGS cannot be used
-   as registers in any case where register classes are examined,
-   it is most consistent to keep this function from accepting them.  */
+   expressions in the machine description.  */
 
 int
 register_operand (rtx op, enum machine_mode mode)
@@ -1080,11 +1068,10 @@ register_operand (rtx op, enum machine_m
       op = sub;
     }
 
-  /* We don't consider registers whose class is NO_REGS
-     to be a register operand.  */
   return (REG_P (op)
 	  && (REGNO (op) >= FIRST_PSEUDO_REGISTER
-	      || REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
+	      || in_hard_reg_set_p (operand_reg_set,
+				    GET_MODE (op), REGNO (op))));
 }
 
 /* Return 1 for a register in Pmode; ignore the tested mode.  */
@@ -1203,11 +1190,10 @@ nonmemory_operand (rtx op, enum machine_
       op = SUBREG_REG (op);
     }
 
-  /* We don't consider registers whose class is NO_REGS
-     to be a register operand.  */
   return (REG_P (op)
 	  && (REGNO (op) >= FIRST_PSEUDO_REGISTER
-	      || REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
+	      || in_hard_reg_set_p (operand_reg_set,
+				    GET_MODE (op), REGNO (op))));
 }
 
 /* Return 1 if OP is a valid operand that stands for pushing a
Index: gcc/varasm.c
===================================================================
--- gcc/varasm.c	2011-09-25 17:36:28.000000000 +0100
+++ gcc/varasm.c	2011-09-25 18:13:47.000000000 +0100
@@ -1198,16 +1198,23 @@ make_decl_rtl (tree decl)
   else if (TREE_CODE (decl) != FUNCTION_DECL && DECL_REGISTER (decl))
     {
       const char *asmspec = name+1;
+      enum machine_mode mode = DECL_MODE (decl);
       reg_number = decode_reg_name (asmspec);
       /* First detect errors in declaring global registers.  */
       if (reg_number == -1)
 	error ("register name not specified for %q+D", decl);
       else if (reg_number < 0)
 	error ("invalid register name for %q+D", decl);
-      else if (TYPE_MODE (TREE_TYPE (decl)) == BLKmode)
+      else if (mode == BLKmode)
 	error ("data type of %q+D isn%'t suitable for a register",
 	       decl);
-      else if (! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl))))
+      else if (!in_hard_reg_set_p (accessible_reg_set, mode, reg_number))
+	error ("the register specified for %q+D cannot be accessed"
+	       " by the current target", decl);
+      else if (!in_hard_reg_set_p (operand_reg_set, mode, reg_number))
+	error ("the register specified for %q+D is not general enough"
+	       " to be used as a register variable", decl);
+      else if (!HARD_REGNO_MODE_OK (reg_number, mode))
 	error ("register specified for %q+D isn%'t suitable for data type",
                decl);
       /* Now handle properly declared static register variables.  */
@@ -1230,7 +1237,7 @@ make_decl_rtl (tree decl)
 	     confused with that register and be eliminated.  This usage is
 	     somewhat suspect...  */
 
-	  SET_DECL_RTL (decl, gen_rtx_raw_REG (DECL_MODE (decl), reg_number));
+	  SET_DECL_RTL (decl, gen_rtx_raw_REG (mode, reg_number));
 	  ORIGINAL_REGNO (DECL_RTL (decl)) = reg_number;
 	  REG_USERVAR_P (DECL_RTL (decl)) = 1;
 
@@ -1242,7 +1249,7 @@ make_decl_rtl (tree decl)
 	      name = IDENTIFIER_POINTER (DECL_NAME (decl));
 	      ASM_DECLARE_REGISTER_GLOBAL (asm_out_file, decl, reg_number, name);
 #endif
-	      nregs = hard_regno_nregs[reg_number][DECL_MODE (decl)];
+	      nregs = hard_regno_nregs[reg_number][mode];
 	      while (nregs > 0)
 		globalize_reg (decl, reg_number + --nregs);
 	    }
Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c	2011-09-25 17:36:27.000000000 +0100
+++ gcc/config/mips/mips.c	2011-09-25 18:13:47.000000000 +0100
@@ -15807,31 +15807,26 @@ mips_conditional_register_usage (void)
       global_regs[CCDSP_PO_REGNUM] = 1;
       global_regs[CCDSP_SC_REGNUM] = 1;
     }
-  else 
-    {
-      int regno;
+  else
+    AND_COMPL_HARD_REG_SET (accessible_reg_set,
+			    reg_class_contents[(int) DSP_ACC_REGS]);
 
-      for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++)
-	fixed_regs[regno] = call_used_regs[regno] = 1;
-    }
   if (!TARGET_HARD_FLOAT)
     {
-      int regno;
-
-      for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
-	fixed_regs[regno] = call_used_regs[regno] = 1;
-      for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
-	fixed_regs[regno] = call_used_regs[regno] = 1;
+      AND_COMPL_HARD_REG_SET (accessible_reg_set,
+			      reg_class_contents[(int) FP_REGS]);
+      AND_COMPL_HARD_REG_SET (accessible_reg_set,
+			      reg_class_contents[(int) ST_REGS]);
     }
-  else if (! ISA_HAS_8CC)
+  else if (!ISA_HAS_8CC)
     {
-      int regno;
-
       /* We only have a single condition-code register.  We implement
 	 this by fixing all the condition-code registers and generating
 	 RTL that refers directly to ST_REG_FIRST.  */
-      for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
-	fixed_regs[regno] = call_used_regs[regno] = 1;
+      AND_COMPL_HARD_REG_SET (accessible_reg_set,
+			      reg_class_contents[(int) ST_REGS]);
+      SET_HARD_REG_BIT (accessible_reg_set, FPSW_REGNUM);
+      fixed_regs[FPSW_REGNUM] = call_used_regs[FPSW_REGNUM] = 1;
     }
   /* In MIPS16 mode, we permit the $t temporary registers to be used
      for reload.  We prohibit the unused $s registers, since they
Index: gcc/testsuite/gcc.target/mips/mips.exp
===================================================================
--- gcc/testsuite/gcc.target/mips/mips.exp	2011-09-25 17:36:27.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/mips.exp	2011-09-25 18:13:47.000000000 +0100
@@ -810,6 +810,10 @@ proc mips-dg-finish {} {
 #            |                           |
 #         -mexplicit-relocs           -mno-explicit-relocs
 #            |                           |
+#         -mdspr2                     -mno-dspr2
+#            |                           |
+#         -mdsp                       -mno-dsp
+#            |                           |
 #            +-- gp, abi & arch ---------+
 #
 # For these purposes, the "gp", "abi" & "arch" option groups are treated
@@ -1092,7 +1096,6 @@ proc mips-dg-options { args } {
 		mips_make_test_option options "-mfp32"
 	    }
 	    mips_make_test_option options "-mno-dsp"
-	    mips_make_test_option options "-mno-dspr2"
 	}
 	unset arch
 	unset isa
@@ -1100,6 +1103,7 @@ proc mips-dg-options { args } {
     }
 
     # Handle dependencies between options on the right of the diagram.
+    mips_option_dependency options "-mno-dsp" "-mno-dspr2"
     mips_option_dependency options "-mno-explicit-relocs" "-mgpopt"
     switch -- [mips_test_option options small-data] {
 	"" -
Index: gcc/testsuite/gcc.target/mips/no-dsp-1.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/no-dsp-1.c	2011-09-25 18:13:47.000000000 +0100
@@ -0,0 +1,7 @@
+/* { dg-options "-mno-dsp" } */
+
+void
+foo (void)
+{
+  register int x asm ("$ac1hi"); /* { dg-error "cannot be accessed" } */
+}
Index: gcc/testsuite/gcc.target/mips/soft-float-1.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/soft-float-1.c	2011-09-25 18:13:47.000000000 +0100
@@ -0,0 +1,7 @@
+/* { dg-options "-msoft-float" } */
+
+void
+foo (void)
+{
+  register float x asm ("$f0"); /* { dg-error "cannot be accessed" } */
+}

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Explicitly record which registers are inaccessible
  2011-09-25 19:38 Explicitly record which registers are inaccessible Richard Sandiford
@ 2011-10-04 10:47 ` Bernd Schmidt
  2011-10-04 12:17   ` Richard Sandiford
  0 siblings, 1 reply; 5+ messages in thread
From: Bernd Schmidt @ 2011-10-04 10:47 UTC (permalink / raw)
  To: gcc-patches, rdsandiford

On 09/25/11 19:16, Richard Sandiford wrote:
> The last bit is indirect, via a new HARD_REG_SET called operand_reg_set.
> And this set is the reason why I'm sending the patch now.  The MIPS16 port
> has always had a problem with the HI and LO registers: they can only be
> set by multiplication and division instructions, and only read by MFHI
> and MFLO.  Unlike normal MIPS, there are no MTHI and MTLO instructions.
[...]
 >  Now that we use pressure classes
> instead (a good thing), I'm finally going to try to fix this "properly".
> And that means (a) fixing HI and LO and (b) stopping them from being
> treated as register operands.  (b) is important because if we start
> out with this (valid) instruction before reload:

The only slightly nonobvious thing about this is that mfhi/mflo can't
have their operand represented using a register_operand. I haven't
looked; I assume that's the case. Ok.


Bernd

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Explicitly record which registers are inaccessible
  2011-10-04 10:47 ` Bernd Schmidt
@ 2011-10-04 12:17   ` Richard Sandiford
  2011-10-04 20:14     ` Richard Sandiford
  0 siblings, 1 reply; 5+ messages in thread
From: Richard Sandiford @ 2011-10-04 12:17 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches

Bernd Schmidt <bernds@codesourcery.com> writes:
> On 09/25/11 19:16, Richard Sandiford wrote:
>> The last bit is indirect, via a new HARD_REG_SET called operand_reg_set.
>> And this set is the reason why I'm sending the patch now.  The MIPS16 port
>> has always had a problem with the HI and LO registers: they can only be
>> set by multiplication and division instructions, and only read by MFHI
>> and MFLO.  Unlike normal MIPS, there are no MTHI and MTLO instructions.
> [...]
>  >  Now that we use pressure classes
>> instead (a good thing), I'm finally going to try to fix this "properly".
>> And that means (a) fixing HI and LO and (b) stopping them from being
>> treated as register operands.  (b) is important because if we start
>> out with this (valid) instruction before reload:
>
> The only slightly nonobvious thing about this is that mfhi/mflo can't
> have their operand represented using a register_operand. I haven't
> looked; I assume that's the case. Ok.

Right.  The follow-up MIPS patch (which I've been sitting on, I suppose
I should post it when I get home, sorry) removes HI and LO from the new
operand_reg_set and extends move_operand to explicitly allow LO.

A lot of the multiplication patterns need rejigging to expose LO early
when required, so it's not pretty...

Richard

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Explicitly record which registers are inaccessible
  2011-10-04 12:17   ` Richard Sandiford
@ 2011-10-04 20:14     ` Richard Sandiford
  2011-11-27 20:26       ` Richard Sandiford
  0 siblings, 1 reply; 5+ messages in thread
From: Richard Sandiford @ 2011-10-04 20:14 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches, richard.sandiford

Richard Sandiford <richard.sandiford@linaro.org> writes:
> Bernd Schmidt <bernds@codesourcery.com> writes:
>> On 09/25/11 19:16, Richard Sandiford wrote:
>>> The last bit is indirect, via a new HARD_REG_SET called operand_reg_set.
>>> And this set is the reason why I'm sending the patch now.  The MIPS16 port
>>> has always had a problem with the HI and LO registers: they can only be
>>> set by multiplication and division instructions, and only read by MFHI
>>> and MFLO.  Unlike normal MIPS, there are no MTHI and MTLO instructions.
>> [...]
>>  >  Now that we use pressure classes
>>> instead (a good thing), I'm finally going to try to fix this "properly".
>>> And that means (a) fixing HI and LO and (b) stopping them from being
>>> treated as register operands.  (b) is important because if we start
>>> out with this (valid) instruction before reload:
>>
>> The only slightly nonobvious thing about this is that mfhi/mflo can't
>> have their operand represented using a register_operand. I haven't
>> looked; I assume that's the case. Ok.
>
> Right.  The follow-up MIPS patch (which I've been sitting on, I suppose
> I should post it when I get home, sorry) removes HI and LO from the new
> operand_reg_set and extends move_operand to explicitly allow LO.
>
> A lot of the multiplication patterns need rejigging to expose LO early
> when required, so it's not pretty...

Here it is.  Like I say, most of it is just exposing LO at expand time
for MIPS16, while not affecting normal mode.  The changes relevant to
this patch are the ones to use muldiv_target_operand and the change
to move_operand.

Richard


gcc/
	* config/mips/mips-protos.h (mips_emit_binary): Declare.
	* config/mips/mips.c (mips_emit_binary): Make global.
	(mips_set_mips16_mode): Turn off -mfix-r4000 in MIPS16 mode.
	(mips_conditional_register_usage): Don't treat LO and HI as
	register operands in MIPS16 mode.
	(mips_mulsidi3_gen_fn): Use {u,}mulsidi3_{32,64}bit_mips16
	for MIPS16 code.
	* config/mips/predicates.md (muldiv_target_operand): New predicate.
	(move_operand): Allow hilo_operand.
	* config/mips/mips.md (mul<mode>3): Explicitly specify LO as the
	target of MIPS16 multiplies, then move it into the target register.
	(mul<mode>3_internal, *macc2, *msac2): Use muldiv_target_operand.
	(<u>mulsidi3_32bit_mips16): New expander.
	(<u>mulsidi3_32bit): Use muldiv_target_operand.
	(<u>mulsidi3_32bit_r4000): Disable for ISA_HAS_DSP.
	(<u>mulsidi3_64bit): Require !TARGET_MIPS16.  Split into
	<u>mulsidi3_64bit_split.
	(<u>mulsidi3_64bit_mips16): New expander.
	(<u>mulsidi3_64bit_split): Likewise, using expansions from
	two previous define_splits.
	(<u>mulsidi3_64bit_hilo, *muls<u>_di, <u>msubsidi4): Use
	muldiv_target_operand.
	(<su>mulsi3_highpart): Use <su>mulsi3_highpart_split for MIPS16 code.
	(<su>mulsi3_highpart_internal): Require !TARGET_MIPS16.
	Split into <su>mulsi3_highpart_split.
	(<su>mulsi3_highpart_split): New expander.
	(<su>muldi3_highpart): Turn into a define_expand.
	Use <su>muldi3_highpart_split for MIPS16 code.
	(<su>muldi3_highpart_internal): Renamed from <su>muldi3_highpart.
	Require !TARGET_MIPS16.  Split into <su>muldi3_highpart_split.
	(<su>muldi3_highpart_split): New expander.
	(<u>mulditi3): Explicitly specify LO as the target of MIPS16
	multiplies, then move it into the target register.
	(<u>mulditi3_internal, <u>maddsidi4): Use muldiv_target_operand.
	(divmod<mode>4, udivmod<mode>4): Turn into define_expands.
	Use <u>divmod<mode>4_split for MIPS16 code, then explicitly
	move LO into operand 0.
	(divmod<mode>4_internal, udivmod<mode>4_internal): Renamed
	from <u>divmod<mode>4.  Use muldiv_target_operand.
	Require !TARGET_MIPS16.  Split into <u>divmod<mode>4_split.
	(<u>divmod<mode>4_split): New expander.
	(<u>divmod<GPR:mode>4_hilo_<HILO:mode>): Use muldiv_target_operand.
	(mfhi<GPR:mode>_<HILO:mode>): Use hilo_operand.

gcc/testsuite/
	* gcc.target/mips/mult-2.c, gcc.target/mips/mult-3.c,
	gcc.target/mips/mult-4.c, gcc.target/mips/mult-5.c,
	gcc.target/mips/mult-6.c, gcc.target/mips/mult-7.c,
	gcc.target/mips/mult-8.c, gcc.target/mips/mult-9.c,
	gcc.target/mips/mult-10.c, gcc.target/mips/mult-11.c,
	gcc.target/mips/mult-12.c, gcc.target/mips/mult-13.c,
	gcc.target/mips/mult-14.c, gcc.target/mips/mult-15.c,
	gcc.target/mips/mult-16.c, gcc.target/mips/mult-17.c,
	gcc.target/mips/mult-18.c, gcc.target/mips/mult-19.c,
	gcc.target/mips/div-1.c, gcc.target/mips/div-2.c,
	gcc.target/mips/div-3.c, gcc.target/mips/div-4.c,
	gcc.target/mips/div-5.c, gcc.target/mips/div-6.c,
	gcc.target/mips/div-7.c, gcc.target/mips/div-8.c,
	gcc.target/mips/div-9.c, gcc.target/mips/div-10.c,
	gcc.target/mips/div-11.c, gcc.target/mips/div-12.c: New tests.
	* gcc.target/mips/fix-r4000-1.c (foo, bar): Add NOMIPS16.
	* gcc.target/mips/fix-r4000-2.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-3.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-4.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-5.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-6.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-7.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-8.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-9.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-10.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-11.c (foo): Likewise.
	* gcc.target/mips/fix-r4000-12.c (foo): Likewise.

Index: gcc/config/mips/mips-protos.h
===================================================================
--- gcc/config/mips/mips-protos.h	2011-09-25 10:11:40.000000000 +0100
+++ gcc/config/mips/mips-protos.h	2011-09-25 10:57:38.000000000 +0100
@@ -191,6 +191,9 @@ extern int mips_split_const_insns (rtx);
 extern int mips_load_store_insns (rtx, rtx);
 extern int mips_idiv_insns (void);
 extern rtx mips_emit_move (rtx, rtx);
+#ifdef RTX_CODE
+extern void mips_emit_binary (enum rtx_code, rtx, rtx, rtx);
+#endif
 extern rtx mips_pic_base_register (rtx);
 extern rtx mips_got_load (rtx, rtx, enum mips_symbol_type);
 extern bool mips_split_symbol (rtx, rtx, enum machine_mode, rtx *);
Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c	2011-09-25 10:52:53.000000000 +0100
+++ gcc/config/mips/mips.c	2011-09-25 11:45:45.000000000 +0100
@@ -2398,7 +2398,7 @@ mips_force_unary (enum machine_mode mode
 
 /* Emit an instruction of the form (set TARGET (CODE OP0 OP1)).  */
 
-static void
+void
 mips_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1)
 {
   emit_insn (gen_rtx_SET (VOIDmode, target,
@@ -15222,6 +15222,11 @@ mips_set_mips16_mode (int mips16_p)
       /* MIPS16 has no BAL instruction.  */
       target_flags &= ~MASK_RELAX_PIC_CALLS;
 
+      /* The R4000 errata don't apply to any known MIPS16 cores.
+	 It's simpler to make the R4000 fixes and MIPS16 mode
+	 mutually exclusive.  */
+      target_flags &= ~MASK_FIX_R4000;
+
       if (flag_pic && !TARGET_OLDABI)
 	sorry ("MIPS16 PIC for ABIs other than o32 and o64");
 
@@ -15828,12 +15833,12 @@ mips_conditional_register_usage (void)
       SET_HARD_REG_BIT (accessible_reg_set, FPSW_REGNUM);
       fixed_regs[FPSW_REGNUM] = call_used_regs[FPSW_REGNUM] = 1;
     }
-  /* In MIPS16 mode, we permit the $t temporary registers to be used
-     for reload.  We prohibit the unused $s registers, since they
-     are call-saved, and saving them via a MIPS16 register would
-     probably waste more time than just reloading the value.  */
   if (TARGET_MIPS16)
     {
+      /* In MIPS16 mode, we permit the $t temporary registers to be used
+	 for reload.  We prohibit the unused $s registers, since they
+	 are call-saved, and saving them via a MIPS16 register would
+	 probably waste more time than just reloading the value.  */
       fixed_regs[18] = call_used_regs[18] = 1;
       fixed_regs[19] = call_used_regs[19] = 1;
       fixed_regs[20] = call_used_regs[20] = 1;
@@ -15843,6 +15848,12 @@ mips_conditional_register_usage (void)
       fixed_regs[26] = call_used_regs[26] = 1;
       fixed_regs[27] = call_used_regs[27] = 1;
       fixed_regs[30] = call_used_regs[30] = 1;
+
+      /* Do not allow HI and LO to be treated as register operands.
+	 There are no MTHI or MTLO instructions (or any real need
+	 for them) and one-way registers cannot easily be reloaded.  */
+      AND_COMPL_HARD_REG_SET (operand_reg_set,
+			      reg_class_contents[(int) MD_REGS]);
     }
   /* $f20-$f23 are call-clobbered for n64.  */
   if (mips_abi == ABI_64)
@@ -16028,12 +16039,20 @@ mips_mulsidi3_gen_fn (enum rtx_code ext_
 	 case we still expand mulsidi3 for DMUL.  */
       if (ISA_HAS_DMUL3)
 	return signed_p ? gen_mulsidi3_64bit_dmul : NULL;
+      if (TARGET_MIPS16)
+	return (signed_p
+		? gen_mulsidi3_64bit_mips16
+		: gen_umulsidi3_64bit_mips16);
       if (TARGET_FIX_R4000)
 	return NULL;
       return signed_p ? gen_mulsidi3_64bit : gen_umulsidi3_64bit;
     }
   else
     {
+      if (TARGET_MIPS16)
+	return (signed_p
+		? gen_mulsidi3_32bit_mips16
+		: gen_umulsidi3_32bit_mips16);
       if (TARGET_FIX_R4000 && !ISA_HAS_DSP)
 	return signed_p ? gen_mulsidi3_32bit_r4000 : gen_umulsidi3_32bit_r4000;
       return signed_p ? gen_mulsidi3_32bit : gen_umulsidi3_32bit;
Index: gcc/config/mips/predicates.md
===================================================================
--- gcc/config/mips/predicates.md	2011-09-25 10:13:25.000000000 +0100
+++ gcc/config/mips/predicates.md	2011-09-25 11:03:48.000000000 +0100
@@ -127,6 +127,11 @@ (define_predicate "fcc_reload_operand"
   (and (match_code "reg,subreg")
        (match_test "ST_REG_P (true_regnum (op))")))
 
+(define_predicate "muldiv_target_operand"
+  (if_then_else (match_test "TARGET_MIPS16")
+		(match_operand 0 "hilo_operand")
+		(match_operand 0 "register_operand")))
+
 (define_special_predicate "pc_or_label_operand"
   (match_code "pc,label_ref"))
 
@@ -189,7 +194,9 @@ (define_predicate "splittable_const_int_
 })
 
 (define_predicate "move_operand"
-  (match_operand 0 "general_operand")
+  ;; Allow HI and LO to be used as the source of a MIPS16 move.
+  (ior (match_operand 0 "general_operand")
+       (match_operand 0 "hilo_operand"))
 {
   enum mips_symbol_type symbol_type;
 
Index: gcc/config/mips/mips.md
===================================================================
--- gcc/config/mips/mips.md	2011-09-25 10:13:25.000000000 +0100
+++ gcc/config/mips/mips.md	2011-09-25 11:36:13.000000000 +0100
@@ -1331,11 +1331,19 @@ (define_expand "mul<mode>3"
 		  (match_operand:GPR 2 "register_operand")))]
   ""
 {
+  rtx lo;
+
   if (TARGET_LOONGSON_2EF || TARGET_LOONGSON_3A)
     emit_insn (gen_mul<mode>3_mul3_loongson (operands[0], operands[1],
                                              operands[2]));
   else if (ISA_HAS_<D>MUL3)
     emit_insn (gen_mul<mode>3_mul3 (operands[0], operands[1], operands[2]));
+  else if (TARGET_MIPS16)
+    {
+      lo = gen_rtx_REG (<MODE>mode, LO_REGNUM);
+      emit_insn (gen_mul<mode>3_internal (lo, operands[1], operands[2]));
+      emit_move_insn (operands[0], lo);
+    }
   else if (TARGET_FIX_R4000)
     emit_insn (gen_mul<mode>3_r4000 (operands[0], operands[1], operands[2]));
   else
@@ -1398,7 +1406,7 @@ (define_peephole2
         (clobber (match_dup 0))])])
 
 (define_insn "mul<mode>3_internal"
-  [(set (match_operand:GPR 0 "register_operand" "=l")
+  [(set (match_operand:GPR 0 "muldiv_target_operand" "=l")
 	(mult:GPR (match_operand:GPR 1 "register_operand" "d")
 		  (match_operand:GPR 2 "register_operand" "d")))]
   "!TARGET_FIX_R4000"
@@ -1575,7 +1583,7 @@ (define_insn_and_split "*msac_using_macc
 ;; Patterns generated by the define_peephole2 below.
 
 (define_insn "*macc2"
-  [(set (match_operand:SI 0 "register_operand" "=l")
+  [(set (match_operand:SI 0 "muldiv_target_operand" "=l")
 	(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d")
 			  (match_operand:SI 2 "register_operand" "d"))
 		 (match_dup 0)))
@@ -1589,7 +1597,7 @@ (define_insn "*macc2"
    (set_attr "mode"	"SI")])
 
 (define_insn "*msac2"
-  [(set (match_operand:SI 0 "register_operand" "=l")
+  [(set (match_operand:SI 0 "muldiv_target_operand" "=l")
 	(minus:SI (match_dup 0)
 		  (mult:SI (match_operand:SI 1 "register_operand" "d")
 			   (match_operand:SI 2 "register_operand" "d"))))
@@ -1744,11 +1752,25 @@ (define_expand "<u>mulsidi3"
   DONE;
 })
 
+(define_expand "<u>mulsidi3_32bit_mips16"
+  [(set (match_operand:DI 0 "register_operand")
+	(mult:DI (any_extend:DI (match_operand:SI 1 "register_operand"))
+		 (any_extend:DI (match_operand:SI 2 "register_operand"))))]
+  "!TARGET_64BIT && TARGET_MIPS16"
+{
+  rtx hilo;
+
+  hilo = gen_rtx_REG (DImode, MD_REG_FIRST);
+  emit_insn (gen_<u>mulsidi3_32bit (hilo, operands[1], operands[2]));
+  emit_move_insn (operands[0], hilo);
+  DONE;
+})
+
 ;; As well as being named patterns, these instructions are used by the
 ;; __builtin_mips_mult<u>() functions.  We must always make those functions
 ;; available if !TARGET_64BIT && ISA_HAS_DSP.
 (define_insn "<u>mulsidi3_32bit"
-  [(set (match_operand:DI 0 "register_operand" "=ka")
+  [(set (match_operand:DI 0 "muldiv_target_operand" "=ka")
 	(mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
 		 (any_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
   "!TARGET_64BIT && (!TARGET_FIX_R4000 || ISA_HAS_DSP)"
@@ -1766,20 +1788,27 @@ (define_insn "<u>mulsidi3_32bit_r4000"
 	(mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
 		 (any_extend:DI (match_operand:SI 2 "register_operand" "d"))))
    (clobber (match_scratch:DI 3 "=x"))]
-  "!TARGET_64BIT && TARGET_FIX_R4000"
+  "!TARGET_64BIT && TARGET_FIX_R4000 && !ISA_HAS_DSP"
   "mult<u>\t%1,%2\;mflo\t%L0\;mfhi\t%M0"
   [(set_attr "type" "imul")
    (set_attr "mode" "SI")
    (set_attr "length" "12")])
 
-(define_insn "<u>mulsidi3_64bit"
+(define_insn_and_split "<u>mulsidi3_64bit"
   [(set (match_operand:DI 0 "register_operand" "=d")
 	(mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
 		 (any_extend:DI (match_operand:SI 2 "register_operand" "d"))))
    (clobber (match_scratch:TI 3 "=x"))
    (clobber (match_scratch:DI 4 "=d"))]
-  "TARGET_64BIT && !TARGET_FIX_R4000 && !ISA_HAS_DMUL3"
+  "TARGET_64BIT && !TARGET_FIX_R4000 && !ISA_HAS_DMUL3 && !TARGET_MIPS16"
   "#"
+  "&& reload_completed"
+  [(const_int 0)]
+{
+  emit_insn (gen_<u>mulsidi3_64bit_split (operands[0], operands[1],
+					  operands[2], operands[4]));
+  DONE;
+}
   [(set_attr "type" "imul")
    (set_attr "mode" "SI")
    (set (attr "length")
@@ -1787,63 +1816,52 @@ (define_insn "<u>mulsidi3_64bit"
 		      (const_int 16)
 		      (const_int 28)))])
 
-(define_split
-  [(set (match_operand:DI 0 "d_operand")
-	(mult:DI (any_extend:DI (match_operand:SI 1 "d_operand"))
-		 (any_extend:DI (match_operand:SI 2 "d_operand"))))
-   (clobber (match_operand:TI 3 "hilo_operand"))
-   (clobber (match_operand:DI 4 "d_operand"))]
-  "TARGET_64BIT && !TARGET_FIX_R4000 && ISA_HAS_EXT_INS && reload_completed"
-  [(set (match_dup 3)
-	(unspec:TI [(mult:DI (any_extend:DI (match_dup 1))
-			     (any_extend:DI (match_dup 2)))]
-		   UNSPEC_SET_HILO))
-
-   ;; OP0 <- LO, OP4 <- HI
-   (set (match_dup 0) (match_dup 5))
-   (set (match_dup 4) (unspec:DI [(match_dup 3)] UNSPEC_MFHI))
-
-   (set (zero_extract:DI (match_dup 0) (const_int 32) (const_int 32))
-	(match_dup 4))]
-  { operands[5] = gen_rtx_REG (DImode, LO_REGNUM); })
+(define_expand "<u>mulsidi3_64bit_mips16"
+  [(set (match_operand:DI 0 "register_operand")
+	(mult:DI (any_extend:DI (match_operand:SI 1 "register_operand"))
+		 (any_extend:DI (match_operand:SI 2 "register_operand"))))]
+  "TARGET_64BIT && TARGET_MIPS16"
+{
+  emit_insn (gen_<u>mulsidi3_64bit_split (operands[0], operands[1],
+					  operands[2], gen_reg_rtx (DImode)));
+  DONE;
+})
 
-(define_split
-  [(set (match_operand:DI 0 "d_operand")
-	(mult:DI (any_extend:DI (match_operand:SI 1 "d_operand"))
-		 (any_extend:DI (match_operand:SI 2 "d_operand"))))
-   (clobber (match_operand:TI 3 "hilo_operand"))
-   (clobber (match_operand:DI 4 "d_operand"))]
-  "TARGET_64BIT && !TARGET_FIX_R4000 && !ISA_HAS_EXT_INS && reload_completed"
-  [(set (match_dup 3)
-	(unspec:TI [(mult:DI (any_extend:DI (match_dup 1))
-			     (any_extend:DI (match_dup 2)))]
-		   UNSPEC_SET_HILO))
-
-   ;; OP0 <- LO, OP4 <- HI
-   (set (match_dup 0) (match_dup 5))
-   (set (match_dup 4) (unspec:DI [(match_dup 3)] UNSPEC_MFHI))
+(define_expand "<u>mulsidi3_64bit_split"
+  [(set (match_operand:DI 0 "register_operand")
+	(mult:DI (any_extend:DI (match_operand:SI 1 "register_operand"))
+		 (any_extend:DI (match_operand:SI 2 "register_operand"))))
+   (clobber (match_operand:DI 3 "register_operand"))]
+  ""
+{
+  rtx hilo;
 
-   ;; Zero-extend OP0.
-   (set (match_dup 0)
-	(ashift:DI (match_dup 0)
-		   (const_int 32)))
-   (set (match_dup 0)
-	(lshiftrt:DI (match_dup 0)
-		     (const_int 32)))
+  hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
+  emit_insn (gen_<u>mulsidi3_64bit_hilo (hilo, operands[1], operands[2]));
 
-   ;; Shift OP4 into place.
-   (set (match_dup 4)
-	(ashift:DI (match_dup 4)
-		   (const_int 32)))
+  emit_move_insn (operands[0], gen_rtx_REG (DImode, LO_REGNUM));
+  emit_insn (gen_mfhidi_ti (operands[3], hilo));
 
-   ;; OR the two halves together
-   (set (match_dup 0)
-	(ior:DI (match_dup 0)
-		(match_dup 4)))]
-  { operands[5] = gen_rtx_REG (DImode, LO_REGNUM); })
+  if (ISA_HAS_EXT_INS)
+    emit_insn (gen_insvdi (operands[0], GEN_INT (32), GEN_INT (32),
+			   operands[3]));
+  else
+    {
+      /* Zero-extend the low part.  */
+      mips_emit_binary (ASHIFT, operands[0], operands[0], GEN_INT (32));
+      mips_emit_binary (LSHIFTRT, operands[0], operands[0], GEN_INT (32));
+
+      /* Shift the high part into place.  */
+      mips_emit_binary (ASHIFT, operands[3], operands[3], GEN_INT (32));
+
+      /* OR the two halves together.  */
+      mips_emit_binary (IOR, operands[0], operands[0], operands[3]);
+    }
+  DONE;
+})
 
 (define_insn "<u>mulsidi3_64bit_hilo"
-  [(set (match_operand:TI 0 "register_operand" "=x")
+  [(set (match_operand:TI 0 "muldiv_target_operand" "=x")
 	(unspec:TI
 	  [(mult:DI
 	     (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
@@ -1867,7 +1885,7 @@ (define_insn "mulsidi3_64bit_dmul"
 
 ;; Widening multiply with negation.
 (define_insn "*muls<u>_di"
-  [(set (match_operand:DI 0 "register_operand" "=x")
+  [(set (match_operand:DI 0 "muldiv_target_operand" "=x")
         (neg:DI
 	 (mult:DI
 	  (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
@@ -1885,9 +1903,9 @@ (define_insn "*muls<u>_di"
 ;; in GENERATE_MADD_MSUB for -mno-dsp, but always ignore them for -mdsp,
 ;; even if !ISA_HAS_DSP_MULT.
 (define_insn "<u>msubsidi4"
-  [(set (match_operand:DI 0 "register_operand" "=ka")
+  [(set (match_operand:DI 0 "muldiv_target_operand" "=ka")
         (minus:DI
-	   (match_operand:DI 3 "register_operand" "0")
+	   (match_operand:DI 3 "muldiv_target_operand" "0")
 	   (mult:DI
 	      (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
 	      (any_extend:DI (match_operand:SI 2 "register_operand" "d")))))]
@@ -1918,6 +1936,9 @@ (define_expand "<su>mulsi3_highpart"
     emit_insn (gen_<su>mulsi3_highpart_mulhi_internal (operands[0],
 						       operands[1],
 						       operands[2]));
+  else if (TARGET_MIPS16)
+    emit_insn (gen_<su>mulsi3_highpart_split (operands[0], operands[1],
+					      operands[2]));
   else
     emit_insn (gen_<su>mulsi3_highpart_internal (operands[0], operands[1],
 					         operands[2]));
@@ -1932,11 +1953,28 @@ (define_insn_and_split "<su>mulsi3_highp
 		   (any_extend:DI (match_operand:SI 2 "register_operand" "d")))
 	  (const_int 32))))
    (clobber (match_scratch:SI 3 "=l"))]
-  "!ISA_HAS_MULHI"
+  "!ISA_HAS_MULHI && !TARGET_MIPS16"
   { return TARGET_FIX_R4000 ? "mult<u>\t%1,%2\n\tmfhi\t%0" : "#"; }
   "&& reload_completed && !TARGET_FIX_R4000"
   [(const_int 0)]
 {
+  emit_insn (gen_<su>mulsi3_highpart_split (operands[0], operands[1],
+					    operands[2]));
+  DONE;
+}
+  [(set_attr "type" "imul")
+   (set_attr "mode" "SI")
+   (set_attr "length" "8")])
+
+(define_expand "<su>mulsi3_highpart_split"
+  [(set (match_operand:SI 0 "register_operand")
+	(truncate:SI
+	 (lshiftrt:DI
+	  (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand"))
+		   (any_extend:DI (match_operand:SI 2 "register_operand")))
+	  (const_int 32))))]
+  ""
+{
   rtx hilo;
 
   if (TARGET_64BIT)
@@ -1952,10 +1990,7 @@ (define_insn_and_split "<su>mulsi3_highp
       emit_insn (gen_mfhisi_di (operands[0], hilo));
     }
   DONE;
-}
-  [(set_attr "type" "imul")
-   (set_attr "mode" "SI")
-   (set_attr "length" "8")])
+})
 
 (define_insn "<su>mulsi3_highpart_mulhi_internal"
   [(set (match_operand:SI 0 "register_operand" "=d")
@@ -1989,7 +2024,25 @@ (define_insn "*<su>mulsi3_highpart_neg_m
 ;; Disable unsigned multiplication for -mfix-vr4120.  This is for VR4120
 ;; errata MD(0), which says that dmultu does not always produce the
 ;; correct result.
-(define_insn_and_split "<su>muldi3_highpart"
+(define_expand "<su>muldi3_highpart"
+  [(set (match_operand:DI 0 "register_operand")
+	(truncate:DI
+	 (lshiftrt:TI
+	  (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand"))
+		   (any_extend:TI (match_operand:DI 2 "register_operand")))
+	  (const_int 64))))]
+  "TARGET_64BIT && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)"
+{
+  if (TARGET_MIPS16)
+    emit_insn (gen_<su>muldi3_highpart_split (operands[0], operands[1],
+					      operands[2]));
+  else
+    emit_insn (gen_<su>muldi3_highpart_internal (operands[0], operands[1],
+						 operands[2]));
+  DONE;
+})
+
+(define_insn_and_split "<su>muldi3_highpart_internal"
   [(set (match_operand:DI 0 "register_operand" "=d")
 	(truncate:DI
 	 (lshiftrt:TI
@@ -1997,21 +2050,37 @@ (define_insn_and_split "<su>muldi3_highp
 		   (any_extend:TI (match_operand:DI 2 "register_operand" "d")))
 	  (const_int 64))))
    (clobber (match_scratch:DI 3 "=l"))]
-  "TARGET_64BIT && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)"
+  "TARGET_64BIT
+   && !TARGET_MIPS16
+   && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)"
   { return TARGET_FIX_R4000 ? "dmult<u>\t%1,%2\n\tmfhi\t%0" : "#"; }
   "&& reload_completed && !TARGET_FIX_R4000"
   [(const_int 0)]
 {
+  emit_insn (gen_<su>muldi3_highpart_split (operands[0], operands[1],
+					    operands[2]));
+  DONE;
+}
+  [(set_attr "type" "imul")
+   (set_attr "mode" "DI")
+   (set_attr "length" "8")])
+
+(define_expand "<su>muldi3_highpart_split"
+  [(set (match_operand:DI 0 "register_operand")
+	(truncate:DI
+	 (lshiftrt:TI
+	  (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand"))
+		   (any_extend:TI (match_operand:DI 2 "register_operand")))
+	  (const_int 64))))]
+  ""
+{
   rtx hilo;
 
   hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
   emit_insn (gen_<u>mulditi3_internal (hilo, operands[1], operands[2]));
   emit_insn (gen_mfhidi_ti (operands[0], hilo));
   DONE;
-}
-  [(set_attr "type" "imul")
-   (set_attr "mode" "DI")
-   (set_attr "length" "8")])
+})
 
 (define_expand "<u>mulditi3"
   [(set (match_operand:TI 0 "register_operand")
@@ -2019,7 +2088,15 @@ (define_expand "<u>mulditi3"
 		 (any_extend:TI (match_operand:DI 2 "register_operand"))))]
   "TARGET_64BIT && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)"
 {
-  if (TARGET_FIX_R4000)
+  rtx hilo;
+
+  if (TARGET_MIPS16)
+    {
+      hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
+      emit_insn (gen_<u>mulditi3_internal (hilo, operands[1], operands[2]));
+      emit_move_insn (operands[0], hilo);
+    }
+  else if (TARGET_FIX_R4000)
     emit_insn (gen_<u>mulditi3_r4000 (operands[0], operands[1], operands[2]));
   else
     emit_insn (gen_<u>mulditi3_internal (operands[0], operands[1],
@@ -2028,7 +2105,7 @@ (define_expand "<u>mulditi3"
 })
 
 (define_insn "<u>mulditi3_internal"
-  [(set (match_operand:TI 0 "register_operand" "=x")
+  [(set (match_operand:TI 0 "muldiv_target_operand" "=x")
 	(mult:TI (any_extend:TI (match_operand:DI 1 "register_operand" "d"))
 		 (any_extend:TI (match_operand:DI 2 "register_operand" "d"))))]
   "TARGET_64BIT
@@ -2067,11 +2144,11 @@ (define_insn "madsi"
 ;; See the comment above <u>msubsidi4 for the relationship between
 ;; ISA_HAS_DSP and ISA_HAS_DSP_MULT.
 (define_insn "<u>maddsidi4"
-  [(set (match_operand:DI 0 "register_operand" "=ka")
+  [(set (match_operand:DI 0 "muldiv_target_operand" "=ka")
 	(plus:DI
 	 (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
 		  (any_extend:DI (match_operand:SI 2 "register_operand" "d")))
-	 (match_operand:DI 3 "register_operand" "0")))]
+	 (match_operand:DI 3 "muldiv_target_operand" "0")))]
   "(TARGET_MAD || ISA_HAS_MACC || GENERATE_MADD_MSUB || ISA_HAS_DSP)
    && !TARGET_64BIT"
 {
@@ -2311,72 +2388,113 @@ (define_insn "*recip<mode>3"
 
 ;; VR4120 errata MD(A1): signed division instructions do not work correctly
 ;; with negative operands.  We use special libgcc functions instead.
-(define_insn_and_split "divmod<mode>4"
-  [(set (match_operand:GPR 0 "register_operand" "=l")
+(define_expand "divmod<mode>4"
+  [(set (match_operand:GPR 0 "register_operand")
+	(div:GPR (match_operand:GPR 1 "register_operand")
+		 (match_operand:GPR 2 "register_operand")))
+   (set (match_operand:GPR 3 "register_operand")
+	(mod:GPR (match_dup 1)
+		 (match_dup 2)))]
+  "!TARGET_FIX_VR4120"
+{
+  if (TARGET_MIPS16)
+    {
+      emit_insn (gen_divmod<mode>4_split (operands[3], operands[1],
+					  operands[2]));
+      emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, LO_REGNUM));
+    }
+  else
+    emit_insn (gen_divmod<mode>4_internal (operands[0], operands[1],
+					   operands[2], operands[3]));
+  DONE;
+})
+
+(define_insn_and_split "divmod<mode>4_internal"
+  [(set (match_operand:GPR 0 "muldiv_target_operand" "=l")
 	(div:GPR (match_operand:GPR 1 "register_operand" "d")
 		 (match_operand:GPR 2 "register_operand" "d")))
    (set (match_operand:GPR 3 "register_operand" "=d")
 	(mod:GPR (match_dup 1)
 		 (match_dup 2)))]
-  "!TARGET_FIX_VR4120"
+  "!TARGET_FIX_VR4120 && !TARGET_MIPS16"
   "#"
   "&& reload_completed"
   [(const_int 0)]
 {
-  rtx hilo;
-
-  if (TARGET_64BIT)
-    {
-      hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
-      emit_insn (gen_divmod<mode>4_hilo_ti (hilo, operands[1], operands[2]));
-      emit_insn (gen_mfhi<mode>_ti (operands[3], hilo));
-    }
-  else
-    {
-      hilo = gen_rtx_REG (DImode, MD_REG_FIRST);
-      emit_insn (gen_divmod<mode>4_hilo_di (hilo, operands[1], operands[2]));
-      emit_insn (gen_mfhi<mode>_di (operands[3], hilo));
-    }
+  emit_insn (gen_divmod<mode>4_split (operands[3], operands[1], operands[2]));
   DONE;
 }
  [(set_attr "type" "idiv")
   (set_attr "mode" "<MODE>")
   (set_attr "length" "8")])
 
-(define_insn_and_split "udivmod<mode>4"
-  [(set (match_operand:GPR 0 "register_operand" "=l")
+(define_expand "udivmod<mode>4"
+  [(set (match_operand:GPR 0 "register_operand")
+	(udiv:GPR (match_operand:GPR 1 "register_operand")
+		  (match_operand:GPR 2 "register_operand")))
+   (set (match_operand:GPR 3 "register_operand")
+	(umod:GPR (match_dup 1)
+		  (match_dup 2)))]
+  ""
+{
+  if (TARGET_MIPS16)
+    {
+      emit_insn (gen_udivmod<mode>4_split (operands[3], operands[1],
+					   operands[2]));
+      emit_move_insn (operands[0], gen_rtx_REG (<MODE>mode, LO_REGNUM));
+    }
+  else
+    emit_insn (gen_udivmod<mode>4_internal (operands[0], operands[1],
+					    operands[2], operands[3]));
+  DONE;
+})
+
+(define_insn_and_split "udivmod<mode>4_internal"
+  [(set (match_operand:GPR 0 "muldiv_target_operand" "=l")
 	(udiv:GPR (match_operand:GPR 1 "register_operand" "d")
 		  (match_operand:GPR 2 "register_operand" "d")))
    (set (match_operand:GPR 3 "register_operand" "=d")
 	(umod:GPR (match_dup 1)
 		  (match_dup 2)))]
-  ""
+  "!TARGET_MIPS16"
   "#"
   "reload_completed"
   [(const_int 0)]
 {
+  emit_insn (gen_udivmod<mode>4_split (operands[3], operands[1], operands[2]));
+  DONE;
+}
+ [(set_attr "type" "idiv")
+  (set_attr "mode" "<MODE>")
+  (set_attr "length" "8")])
+
+(define_expand "<u>divmod<mode>4_split"
+  [(set (match_operand:GPR 0 "register_operand")
+	(any_mod:GPR (match_operand:GPR 1 "register_operand")
+		     (match_operand:GPR 2 "register_operand")))]
+  ""
+{
   rtx hilo;
 
   if (TARGET_64BIT)
     {
       hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
-      emit_insn (gen_udivmod<mode>4_hilo_ti (hilo, operands[1], operands[2]));
-      emit_insn (gen_mfhi<mode>_ti (operands[3], hilo));
+      emit_insn (gen_<u>divmod<mode>4_hilo_ti (hilo, operands[1],
+					       operands[2]));
+      emit_insn (gen_mfhi<mode>_ti (operands[0], hilo));
     }
   else
     {
       hilo = gen_rtx_REG (DImode, MD_REG_FIRST);
-      emit_insn (gen_udivmod<mode>4_hilo_di (hilo, operands[1], operands[2]));
-      emit_insn (gen_mfhi<mode>_di (operands[3], hilo));
+      emit_insn (gen_<u>divmod<mode>4_hilo_di (hilo, operands[1],
+					       operands[2]));
+      emit_insn (gen_mfhi<mode>_di (operands[0], hilo));
     }
   DONE;
-}
- [(set_attr "type" "idiv")
-  (set_attr "mode" "<MODE>")
-  (set_attr "length" "8")])
+})
 
 (define_insn "<u>divmod<GPR:mode>4_hilo_<HILO:mode>"
-  [(set (match_operand:HILO 0 "register_operand" "=x")
+  [(set (match_operand:HILO 0 "muldiv_target_operand" "=x")
 	(unspec:HILO
 	  [(any_div:GPR (match_operand:GPR 1 "register_operand" "d")
 			(match_operand:GPR 2 "register_operand" "d"))]
@@ -4590,7 +4708,7 @@ (define_insn "*movv2sf"
 ;; and the errata related to -mfix-vr4130.
 (define_insn "mfhi<GPR:mode>_<HILO:mode>"
   [(set (match_operand:GPR 0 "register_operand" "=d")
-	(unspec:GPR [(match_operand:HILO 1 "register_operand" "x")]
+	(unspec:GPR [(match_operand:HILO 1 "hilo_operand" "x")]
 		    UNSPEC_MFHI))]
   ""
   { return ISA_HAS_MACCHI ? "<GPR:d>macchi\t%0,%.,%." : "mfhi\t%0"; }
Index: gcc/testsuite/gcc.target/mips/mult-2.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-2.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,13 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdmult\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+
+typedef int TI __attribute__((mode(TI)));
+typedef int DI __attribute__((mode(DI)));
+
+MIPS16 TI
+f (DI x, DI y)
+{
+  return (TI) x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-3.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-3.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,13 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdmultu\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+
+typedef unsigned int TI __attribute__((mode(TI)));
+typedef unsigned int DI __attribute__((mode(DI)));
+
+MIPS16 TI
+f (DI x, DI y)
+{
+  return (TI) x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-4.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-4.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,13 @@
+/* { dg-options "-O2 -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdmult\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+
+typedef int TI __attribute__((mode(TI)));
+typedef int DI __attribute__((mode(DI)));
+
+MIPS16 DI
+f (DI x, DI y)
+{
+  return ((TI) x * y) >> 64;
+}
Index: gcc/testsuite/gcc.target/mips/mult-5.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-5.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,13 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdmultu\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+
+typedef unsigned int TI __attribute__((mode(TI)));
+typedef unsigned int DI __attribute__((mode(DI)));
+
+MIPS16 DI
+f (DI x, DI y)
+{
+  return ((TI) x * y) >> 64;
+}
Index: gcc/testsuite/gcc.target/mips/mult-6.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-6.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdmultu?\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef int DI __attribute__((mode(DI)));
+
+MIPS16 DI
+f (DI x, DI y)
+{
+  return x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-7.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-7.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdmultu?\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef unsigned int DI __attribute__((mode(DI)));
+
+MIPS16 DI
+f (DI x, DI y)
+{
+  return x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-8.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-8.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,15 @@
+/* { dg-options "-O2 -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmult\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+/* { dg-final { scan-assembler-times "\tdsll\t" 2 } } */
+/* { dg-final { scan-assembler "\tdsrl\t" } } */
+
+typedef int DI __attribute__((mode(DI)));
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 DI
+f (SI x, SI y)
+{
+  return (DI) x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-9.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-9.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,15 @@
+/* { dg-options "-O2 -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmultu\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+/* { dg-final { scan-assembler-times "\tdsll\t" 2 } } */
+/* { dg-final { scan-assembler "\tdsrl\t" } } */
+
+typedef unsigned int DI __attribute__((mode(DI)));
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 DI
+f (SI x, SI y)
+{
+  return (DI) x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-10.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-10.c	2011-09-25 11:44:34.000000000 +0100
@@ -0,0 +1,13 @@
+/* { dg-options "-O2 -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmult\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" { xfail *-*-* } } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef int DI __attribute__((mode(DI)));
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return ((DI) x * y) >> 32;
+}
Index: gcc/testsuite/gcc.target/mips/mult-11.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-11.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,13 @@
+/* { dg-options "-O2 -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmultu\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef unsigned int DI __attribute__((mode(DI)));
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return ((DI) x * y) >> 32;
+}
Index: gcc/testsuite/gcc.target/mips/mult-12.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-12.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmultu?\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-13.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-13.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmultu?\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-14.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-14.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,15 @@
+/* { dg-options "-O -mgp32 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmult\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+/* { dg-final { scan-assembler-not "\tdsll\t" } } */
+/* { dg-final { scan-assembler-not "\tdsrl\t" } } */
+
+typedef int DI __attribute__((mode(DI)));
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 DI
+f (SI x, SI y)
+{
+  return (DI) x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-15.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-15.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,15 @@
+/* { dg-options "-O -mgp32 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmultu\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+/* { dg-final { scan-assembler-not "\tdsll\t" } } */
+/* { dg-final { scan-assembler-not "\tdsrl\t" } } */
+
+typedef unsigned int DI __attribute__((mode(DI)));
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 DI
+f (SI x, SI y)
+{
+  return (DI) x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-16.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-16.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,13 @@
+/* { dg-options "-O2 -mgp32 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmult\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef int DI __attribute__((mode(DI)));
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return ((DI) x * y) >> 32;
+}
Index: gcc/testsuite/gcc.target/mips/mult-17.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-17.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,13 @@
+/* { dg-options "-O -mgp32 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmultu\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef unsigned int DI __attribute__((mode(DI)));
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return ((DI) x * y) >> 32;
+}
Index: gcc/testsuite/gcc.target/mips/mult-18.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-18.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp32 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmultu?\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x * y;
+}
Index: gcc/testsuite/gcc.target/mips/mult-19.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/mult-19.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp32 (-mips16)" } */
+/* { dg-final { scan-assembler "\tmultu?\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x * y;
+}
Index: gcc/testsuite/gcc.target/mips/div-1.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-1.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tddiv\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef int DI __attribute__((mode(DI)));
+
+MIPS16 DI
+f (DI x, DI y)
+{
+  return x / y;
+}
Index: gcc/testsuite/gcc.target/mips/div-2.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-2.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tddivu\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef unsigned int DI __attribute__((mode(DI)));
+
+MIPS16 DI
+f (DI x, DI y)
+{
+  return x / y;
+}
Index: gcc/testsuite/gcc.target/mips/div-3.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-3.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tddiv\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef int DI __attribute__((mode(DI)));
+
+MIPS16 DI
+f (DI x, DI y)
+{
+  return x % y;
+}
Index: gcc/testsuite/gcc.target/mips/div-4.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-4.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tddivu\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef unsigned int DI __attribute__((mode(DI)));
+
+MIPS16 DI
+f (DI x, DI y)
+{
+  return x % y;
+}
Index: gcc/testsuite/gcc.target/mips/div-5.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-5.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdiv\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x / y;
+}
Index: gcc/testsuite/gcc.target/mips/div-6.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-6.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdivu\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x / y;
+}
Index: gcc/testsuite/gcc.target/mips/div-7.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-7.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdiv\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x % y;
+}
Index: gcc/testsuite/gcc.target/mips/div-8.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-8.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdivu\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x % y;
+}
Index: gcc/testsuite/gcc.target/mips/div-9.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-9.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdiv\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x / y;
+}
Index: gcc/testsuite/gcc.target/mips/div-10.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-10.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdivu\t" } } */
+/* { dg-final { scan-assembler "\tmflo\t" } } */
+/* { dg-final { scan-assembler-not "\tmfhi\t" } } */
+
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x / y;
+}
Index: gcc/testsuite/gcc.target/mips/div-11.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-11.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdiv\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x % y;
+}
Index: gcc/testsuite/gcc.target/mips/div-12.c
===================================================================
--- /dev/null	2011-09-25 11:09:27.005839516 +0100
+++ gcc/testsuite/gcc.target/mips/div-12.c	2011-09-25 10:57:38.000000000 +0100
@@ -0,0 +1,12 @@
+/* { dg-options "-O -mgp64 (-mips16)" } */
+/* { dg-final { scan-assembler "\tdivu\t" } } */
+/* { dg-final { scan-assembler-not "\tmflo\t" } } */
+/* { dg-final { scan-assembler "\tmfhi\t" } } */
+
+typedef unsigned int SI __attribute__((mode(SI)));
+
+MIPS16 SI
+f (SI x, SI y)
+{
+  return x % y;
+}
Index: gcc/testsuite/gcc.target/mips/fix-r4000-1.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-1.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-1.c	2011-09-25 10:57:38.000000000 +0100
@@ -1,6 +1,6 @@
 /* { dg-options "-march=r4000 -mfix-r4000 -O2 -dp" } */
 typedef int int32_t;
 typedef int uint32_t;
-int32_t foo (int32_t x, int32_t y) { return x * y; }
-uint32_t bar (uint32_t x, uint32_t y) { return x * y; }
+NOMIPS16 int32_t foo (int32_t x, int32_t y) { return x * y; }
+NOMIPS16 uint32_t bar (uint32_t x, uint32_t y) { return x * y; }
 /* { dg-final { scan-assembler-times "[concat {\tmult\t\$[45],\$[45][^\n]+mulsi3_r4000[^\n]+\n\tmflo\t\$2\n}]" 2 } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-2.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-2.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-2.c	2011-09-25 10:57:38.000000000 +0100
@@ -1,7 +1,7 @@
 /* { dg-options "-mips1 -mfix-r4000 -O2 -dp -EB" } */
 typedef int int32_t;
 typedef long long int64_t;
-int32_t foo (int32_t x, int32_t y) { return ((int64_t) x * y) >> 32; }
+NOMIPS16 int32_t foo (int32_t x, int32_t y) { return ((int64_t) x * y) >> 32; }
 /* ??? A highpart pattern would be a better choice, but we currently
    don't use them.  */
 /* { dg-final { scan-assembler "[concat {\tmult\t\$[45],\$[45][^\n]+mulsidi3_32bit_r4000[^\n]+\n\tmflo\t\$3\n\tmfhi\t\$2\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-3.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-3.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-3.c	2011-09-25 10:57:38.000000000 +0100
@@ -1,7 +1,7 @@
 /* { dg-options "-mips1 -mfix-r4000 -O2 -dp -EB" } */
 typedef unsigned int uint32_t;
 typedef unsigned long long uint64_t;
-uint32_t foo (uint32_t x, uint32_t y) { return ((uint64_t) x * y) >> 32; }
+NOMIPS16 uint32_t foo (uint32_t x, uint32_t y) { return ((uint64_t) x * y) >> 32; }
 /* ??? A highpart pattern would be a better choice, but we currently
    don't use them.  */
 /* { dg-final { scan-assembler "[concat {\tmultu\t\$[45],\$[45][^\n]+umulsidi3_32bit_r4000[^\n]+\n\tmflo\t\$3\n\tmfhi\t\$2\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-4.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-4.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-4.c	2011-09-25 10:57:38.000000000 +0100
@@ -4,5 +4,5 @@
 /* { dg-options "-mips1 -mfix-r4000 -O2 -fno-split-wide-types -dp -EL" } */
 typedef int int32_t;
 typedef long long int64_t;
-int64_t foo (int32_t x, int32_t y) { return (int64_t) x * y; }
+NOMIPS16 int64_t foo (int32_t x, int32_t y) { return (int64_t) x * y; }
 /* { dg-final { scan-assembler "[concat {\tmult\t\$[45],\$[45][^\n]+mulsidi3_32bit_r4000[^\n]+\n\tmflo\t\$2\n\tmfhi\t\$3\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-5.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-5.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-5.c	2011-09-25 10:57:38.000000000 +0100
@@ -4,5 +4,5 @@
 /* { dg-options "-mips1 -mfix-r4000 -O2 -fno-split-wide-types -dp -EL" } */
 typedef unsigned int uint32_t;
 typedef unsigned long long uint64_t;
-uint64_t foo (uint32_t x, uint32_t y) { return (uint64_t) x * y; }
+NOMIPS16 uint64_t foo (uint32_t x, uint32_t y) { return (uint64_t) x * y; }
 /* { dg-final { scan-assembler "[concat {\tmultu\t\$[45],\$[45][^\n]+umulsidi3_32bit_r4000[^\n]+\n\tmflo\t\$2\n\tmfhi\t\$3\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-6.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-6.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-6.c	2011-09-25 10:57:38.000000000 +0100
@@ -1,6 +1,6 @@
 /* { dg-options "-march=r4000 -mfix-r4000 -mgp64 -O2 -dp" } */
 typedef long long int64_t;
 typedef unsigned long long uint64_t;
-int64_t foo (int64_t x, int64_t y) { return x * y; }
-uint64_t bar (uint64_t x, uint64_t y) { return x * y; }
+NOMIPS16 int64_t foo (int64_t x, int64_t y) { return x * y; }
+NOMIPS16 uint64_t bar (uint64_t x, uint64_t y) { return x * y; }
 /* { dg-final { scan-assembler-times "[concat {\tdmult\t\$[45],\$[45][^\n]+muldi3_r4000[^\n]+\n\tmflo\t\$2\n}]" 2 } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-7.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-7.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-7.c	2011-09-25 10:57:38.000000000 +0100
@@ -1,7 +1,7 @@
 /* { dg-options "-march=r4000 -mfix-r4000 -O2 -mgp64 -dp -EB" } */
 typedef long long int64_t;
 typedef int int128_t __attribute__((mode(TI)));
-int64_t foo (int64_t x, int64_t y) { return ((int128_t) x * y) >> 64; }
+NOMIPS16 int64_t foo (int64_t x, int64_t y) { return ((int128_t) x * y) >> 64; }
 /* ??? A highpart pattern would be a better choice, but we currently
    don't use them.  */
 /* { dg-final { scan-assembler "[concat {\tdmult\t\$[45],\$[45][^\n]+mulditi3[^\n]+\n\tmflo\t\$3\n\tmfhi\t\$2\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-8.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-8.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-8.c	2011-09-25 10:57:38.000000000 +0100
@@ -1,7 +1,7 @@
 /* { dg-options "-march=r4000 -mfix-r4000 -O2 -mgp64 -dp -EB" } */
 typedef unsigned long long uint64_t;
 typedef unsigned int uint128_t __attribute__((mode(TI)));
-uint64_t foo (uint64_t x, uint64_t y) { return ((uint128_t) x * y) >> 64; }
+NOMIPS16 uint64_t foo (uint64_t x, uint64_t y) { return ((uint128_t) x * y) >> 64; }
 /* ??? A highpart pattern would be a better choice, but we currently
    don't use them.  */
 /* { dg-final { scan-assembler "[concat {\tdmultu\t\$[45],\$[45][^\n]+umulditi3[^\n]+\n\tmflo\t\$3\n\tmfhi\t\$2\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-9.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-9.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-9.c	2011-09-25 10:57:38.000000000 +0100
@@ -4,5 +4,5 @@
 /* { dg-options "-mips3 -mfix-r4000 -mgp64 -O2 -fno-split-wide-types -dp -EL" } */
 typedef long long int64_t;
 typedef int int128_t __attribute__((mode(TI)));
-int128_t foo (int64_t x, int64_t y) { return (int128_t) x * y; }
+NOMIPS16 int128_t foo (int64_t x, int64_t y) { return (int128_t) x * y; }
 /* { dg-final { scan-assembler "[concat {\tdmult\t\$[45],\$[45][^\n]+mulditi3_r4000[^\n]+\n\tmflo\t\$2\n\tmfhi\t\$3\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-10.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-10.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-10.c	2011-09-25 10:57:38.000000000 +0100
@@ -4,5 +4,5 @@
 /* { dg-options "-mips3 -mfix-r4000 -mgp64 -O2 -fno-split-wide-types -dp -EL" } */
 typedef unsigned long long uint64_t;
 typedef unsigned int uint128_t __attribute__((mode(TI)));
-uint128_t foo (uint64_t x, uint64_t y) { return (uint128_t) x * y; }
+NOMIPS16 uint128_t foo (uint64_t x, uint64_t y) { return (uint128_t) x * y; }
 /* { dg-final { scan-assembler "[concat {\tdmultu\t\$[45],\$[45][^\n]+umulditi3_r4000[^\n]+\n\tmflo\t\$2\n\tmfhi\t\$3\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-11.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-11.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-11.c	2011-09-25 10:57:38.000000000 +0100
@@ -1,4 +1,4 @@
 /* { dg-options "-march=r4000 -mfix-r4000 -mgp64 -O2 -dp" } */
 typedef long long int64_t;
-int64_t foo (int64_t x) { return x / 11993; }
+NOMIPS16 int64_t foo (int64_t x) { return x / 11993; }
 /* { dg-final { scan-assembler "[concat {\tdmult\t\$4,\$[0-9]+[^\n]+smuldi3_highpart[^\n]+\n\tmfhi\t\$[0-9]+\n}]" } } */
Index: gcc/testsuite/gcc.target/mips/fix-r4000-12.c
===================================================================
--- gcc/testsuite/gcc.target/mips/fix-r4000-12.c	2011-09-25 10:11:40.000000000 +0100
+++ gcc/testsuite/gcc.target/mips/fix-r4000-12.c	2011-09-25 10:57:38.000000000 +0100
@@ -1,4 +1,4 @@
 /* { dg-options "-march=r4000 -mfix-r4000 -mgp64 -O2 -dp" } */
 typedef unsigned long long uint64_t;
-uint64_t foo (uint64_t x) { return x / 11993; }
+NOMIPS16 uint64_t foo (uint64_t x) { return x / 11993; }
 /* { dg-final { scan-assembler "[concat {\tdmultu\t\$4,\$[0-9]+[^\n]+umuldi3_highpart[^\n]+\n\tmfhi\t\$[0-9]+\n}]" } } */

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Explicitly record which registers are inaccessible
  2011-10-04 20:14     ` Richard Sandiford
@ 2011-11-27 20:26       ` Richard Sandiford
  0 siblings, 0 replies; 5+ messages in thread
From: Richard Sandiford @ 2011-11-27 20:26 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches, richard.sandiford

Richard Sandiford <rdsandiford@googlemail.com> writes:
> Richard Sandiford <richard.sandiford@linaro.org> writes:
>> Bernd Schmidt <bernds@codesourcery.com> writes:
>>> On 09/25/11 19:16, Richard Sandiford wrote:
>>>> The last bit is indirect, via a new HARD_REG_SET called operand_reg_set.
>>>> And this set is the reason why I'm sending the patch now.  The MIPS16 port
>>>> has always had a problem with the HI and LO registers: they can only be
>>>> set by multiplication and division instructions, and only read by MFHI
>>>> and MFLO.  Unlike normal MIPS, there are no MTHI and MTLO instructions.
>>> [...]
>>>  >  Now that we use pressure classes
>>>> instead (a good thing), I'm finally going to try to fix this "properly".
>>>> And that means (a) fixing HI and LO and (b) stopping them from being
>>>> treated as register operands.  (b) is important because if we start
>>>> out with this (valid) instruction before reload:
>>>
>>> The only slightly nonobvious thing about this is that mfhi/mflo can't
>>> have their operand represented using a register_operand. I haven't
>>> looked; I assume that's the case. Ok.

Thanks.

>> Right.  The follow-up MIPS patch (which I've been sitting on, I suppose
>> I should post it when I get home, sorry) removes HI and LO from the new
>> operand_reg_set and extends move_operand to explicitly allow LO.
>>
>> A lot of the multiplication patterns need rejigging to expose LO early
>> when required, so it's not pretty...
>
> Here it is.  Like I say, most of it is just exposing LO at expand time
> for MIPS16, while not affecting normal mode.  The changes relevant to
> this patch are the ones to use muldiv_target_operand and the change
> to move_operand.

Both patches now committed after retesting on x86_64-linux-gnu and
mips64-linux-gnu.

Richard

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-11-27 10:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-09-25 19:38 Explicitly record which registers are inaccessible Richard Sandiford
2011-10-04 10:47 ` Bernd Schmidt
2011-10-04 12:17   ` Richard Sandiford
2011-10-04 20:14     ` Richard Sandiford
2011-11-27 20:26       ` Richard Sandiford

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).