mips.md (divmodsi4*, [...]): Add -mcheck-range-division/ -mcheck-zero-division checking.
authorStan Cox <scox@cygnus.com>
Tue, 1 Dec 1998 16:52:56 +0000 (16:52 +0000)
committerStan Cox <scox@gcc.gnu.org>
Tue, 1 Dec 1998 16:52:56 +0000 (16:52 +0000)
* mips.md (divmodsi4*, divmoddi4*, udivmodsi4*, udivmoddi4): Add -mcheck-range-division/
-mcheck-zero-division checking.  Avoid as macro expansion.  Use hi/lo as destination
register.
(div_trap): New.
(divsi3*, divdi3*, modsi3*, moddi3*, udivsi3*, udivdi3*, umodsi3*,
umoddi3*): Add -mcheck-range-division/-mcheck-zero-division checking.
Avoid as macro expansion.  Use hi/lo as destination register.
* mips.h (MASK_CHECK_RANGE_DIV): New.
(MASK_NO_CHECK_ZERO_DIV): New.
(ELIMINABLE_REGS): Added GP_REG_FIRST + 31.
(CAN_ELIMINATE, INITIAL_ELIMINATION_OFFSET): Allow for getting
return address for leaf functions out of r31 to support
builtin_return_address.

From-SVN: r24047

gcc/ChangeLog
gcc/config/mips/mips.h
gcc/config/mips/mips.md

index 67856df3c88e4486f2cdbdc4e919744b74017511..c239f45e906fdbd194b288ec6b6977ca4bb1b827 100644 (file)
@@ -1,3 +1,20 @@
+Tue Dec  1 16:45:49 1998  Stan Cox  <scox@cygnus.com>
+
+       * mips.md (divmodsi4*, divmoddi4*, udivmodsi4*, udivmoddi4): Add
+       -mcheck-range-division/-mcheck-zero-division checking.  Avoid as macro
+       expansion.  Use hi/lo as destination register.
+       (div_trap): New.
+       (divsi3*, divdi3*, modsi3*, moddi3*, udivsi3*, udivdi3*, umodsi3*,
+       umoddi3*): Add -mcheck-range-division/-mcheck-zero-division checking.
+       Avoid as macro expansion.  Use hi/lo as destination register. 
+
+       * mips.h (MASK_CHECK_RANGE_DIV): New.
+       (MASK_NO_CHECK_ZERO_DIV): New.
+       (ELIMINABLE_REGS): Added GP_REG_FIRST + 31.
+       (CAN_ELIMINATE, INITIAL_ELIMINATION_OFFSET): Allow for getting
+       return address for leaf functions out of r31 to support
+       builtin_return_address. 
+       
 Tue Dec  1 15:03:30 1998  Herman A.J. ten Brugge <Haj.Ten.Brugge@net.HCC.nl>
 
        * jump.c (jump_optimize): Call regs_set_between_p with PREV_INSN(x),
index f36398a8862080144b7c185771897b7ff3c522c8..13f1b7bd227db9a22cf8d8758f9527eee95b4134 100644 (file)
@@ -336,16 +336,18 @@ extern void               mips_select_section ();
 #define MASK_4300_MUL_FIX 0x00080000    /* Work-around early Vr4300 CPU bug */
 #define MASK_MIPS3900  0x00100000      /* like -mips1 only 3900 */
 #define MASK_MIPS16    0x01000000      /* Generate mips16 code */
+#define MASK_NO_CHECK_ZERO_DIV 0x04000000      /* divide by zero checking */
+#define MASK_CHECK_RANGE_DIV 0x08000000        /* divide result range checking */
 
                                        /* Dummy switches used only in spec's*/
 #define MASK_MIPS_TFILE        0x00000000      /* flag for mips-tfile usage */
 
                                        /* Debug switches, not documented */
-#define MASK_DEBUG     0x40000000      /* Eliminate version # in .s file */
-#define MASK_DEBUG_A   0x20000000      /* don't allow <label>($reg) addrs */
-#define MASK_DEBUG_B   0x10000000      /* GO_IF_LEGITIMATE_ADDRESS debug */
-#define MASK_DEBUG_C   0x08000000      /* don't expand seq, etc. */
-#define MASK_DEBUG_D   0x04000000      /* don't do define_split's */
+#define MASK_DEBUG     0               /* Eliminate version # in .s file */
+#define MASK_DEBUG_A   0x40000000      /* don't allow <label>($reg) addrs */
+#define MASK_DEBUG_B   0x20000000      /* GO_IF_LEGITIMATE_ADDRESS debug */
+#define MASK_DEBUG_C   0x10000000      /* don't expand seq, etc. */
+#define MASK_DEBUG_D   0               /* don't do define_split's */
 #define MASK_DEBUG_E   0               /* function_arg debug */
 #define MASK_DEBUG_F   0
 #define MASK_DEBUG_G   0               /* don't support 64 bit arithmetic */
@@ -425,6 +427,9 @@ extern void         mips_select_section ();
 
 #define TARGET_4300_MUL_FIX     (target_flags & MASK_4300_MUL_FIX)
 
+#define TARGET_NO_CHECK_ZERO_DIV (target_flags & MASK_NO_CHECK_ZERO_DIV)
+#define TARGET_CHECK_RANGE_DIV  (target_flags & MASK_CHECK_RANGE_DIV)
+
 /* This is true if we must enable the assembly language file switching
    code.  */
 
@@ -490,6 +495,10 @@ extern void                mips_select_section ();
   {"no-fix4300",         -MASK_4300_MUL_FIX},                          \
   {"4650",               MASK_MAD | MASK_SINGLE_FLOAT},                \
   {"3900",               MASK_MIPS3900},                               \
+  {"check-zero-division",-MASK_NO_CHECK_ZERO_DIV},                     \
+  {"no-check-zero-division", MASK_NO_CHECK_ZERO_DIV},                  \
+  {"check-range-division",MASK_CHECK_RANGE_DIV},                       \
+  {"no-check-range-division",-MASK_CHECK_RANGE_DIV},                   \
   {"debug",              MASK_DEBUG},                                  \
   {"debuga",             MASK_DEBUG_A},                                \
   {"debugb",             MASK_DEBUG_B},                                \
@@ -2144,6 +2153,7 @@ extern struct mips_frame_info current_frame_info;
  { RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM},               \
  { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 30},                  \
  { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 17},                  \
+ { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 31},                  \
  { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM},                                \
  { FRAME_POINTER_REGNUM, GP_REG_FIRST + 30},                           \
  { FRAME_POINTER_REGNUM, GP_REG_FIRST + 17}}
@@ -2169,11 +2179,14 @@ extern struct mips_frame_info current_frame_info;
    */
 
 #define CAN_ELIMINATE(FROM, TO)                                                \
-  ((TO) == HARD_FRAME_POINTER_REGNUM                                   \
+  (((FROM) == RETURN_ADDRESS_POINTER_REGNUM && (! leaf_function_p ()   \
+   || TO == GP_REG_FIRST + 31 && leaf_function_p))                     \
+  || ((FROM) != RETURN_ADDRESS_POINTER_REGNUM                          \
+   && ((TO) == HARD_FRAME_POINTER_REGNUM                               \
    || ((TO) == STACK_POINTER_REGNUM && ! frame_pointer_needed          \
        && ! (TARGET_MIPS16 && TARGET_64BIT)                             \
        && (! TARGET_MIPS16                                             \
-          || compute_frame_size (get_frame_size ()) < 32768)))
+          || compute_frame_size (get_frame_size ()) < 32768)))))
 
 /* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'.  It
    specifies the initial difference between the specified pair of
@@ -2206,7 +2219,9 @@ extern struct mips_frame_info current_frame_info;
   /* Some ABIs store 64 bits to the stack, but Pmode is 32 bits,        \
      so we must add 4 bytes to the offset to get the right value.  */   \
   else if ((FROM) == RETURN_ADDRESS_POINTER_REGNUM)                     \
-    (OFFSET) = current_frame_info.gp_sp_offset                          \
+   if (leaf_function_p ())                                              \
+      (OFFSET) = 0;                                                     \
+   else (OFFSET) = current_frame_info.gp_sp_offset                      \
               + ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT))      \
                  * (BYTES_BIG_ENDIAN != 0));                            \
 }
index 5282fe0939d67e253aefae326482bba589bf4a80..14c4457a76e997599b35ab5543fadd88ae70d6af 100644 (file)
 ;; a divide by power of 2 with a shift, and then the remainder is no longer
 ;; available.
 
-(define_insn "divmodsi4"
+(define_expand "divmodsi4"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (div:SI (match_operand:SI 1 "register_operand" "d")
                (match_operand:SI 2 "register_operand" "d")))
    (clobber (match_scratch:SI 5 "=h"))
    (clobber (match_scratch:SI 6 "=a"))]
   "optimize"
-  "*
+  "
 {
-  if (find_reg_note (insn, REG_UNUSED, operands[3]))
-    return \"div\\t%0,%1,%2\";
+  rtx label;
 
-  if (find_reg_note (insn, REG_UNUSED, operands[0]))
-    return \"rem\\t%3,%1,%2\";
+  emit_insn (gen_divmodsi4_internal (operands[0], operands[1], operands[2],
+            operands[3]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  if (TARGET_CHECK_RANGE_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (-1)),
+                              GEN_INT (0x6)));
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0x80000000)),
+                              GEN_INT (0x6)));
+    }
+  
+  DONE;
+}")
 
-  return \"div\\t%0,%1,%2\;mfhi\\t%3\";
-}"
+(define_insn "divmodsi4_internal"
+  [(set (match_operand:SI 0 "register_operand" "=l")
+       (div:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "register_operand" "d")))
+   (set (match_operand:SI 3 "register_operand" "=h")
+       (mod:SI (match_dup 1)
+               (match_dup 2)))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "optimize"
+  "div\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "14")])         ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
-(define_insn "divmoddi4"
+(define_expand "divmoddi4"
   [(set (match_operand:DI 0 "register_operand" "=d")
        (div:DI (match_operand:DI 1 "se_register_operand" "d")
                (match_operand:DI 2 "se_register_operand" "d")))
    (clobber (match_scratch:DI 5 "=h"))
    (clobber (match_scratch:DI 6 "=a"))]
   "TARGET_64BIT && optimize"
-  "*
+  "
 {
-  if (find_reg_note (insn, REG_UNUSED, operands[3]))
-    return \"ddiv\\t%0,%1,%2\";
+  rtx label;
 
-  if (find_reg_note (insn, REG_UNUSED, operands[0]))
-    return \"drem\\t%3,%1,%2\";
+  emit_insn (gen_divmoddi4_internal (operands[0], operands[1], operands[2],
+             operands[3]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  if (TARGET_CHECK_RANGE_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (-1)),
+                              GEN_INT (0x6)));
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0x80000000)),
+                              GEN_INT (0x6)));
+    }
+  
+  DONE;
+}")
 
-  return \"ddiv\\t%0,%1,%2\;mfhi\\t%3\";
-}"
+(define_insn "divmoddi4_internal"
+  [(set (match_operand:DI 0 "register_operand" "=l")
+       (div:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))
+   (set (match_operand:DI 3 "register_operand" "=h")
+       (mod:DI (match_dup 1)
+               (match_dup 2)))
+   (clobber (match_scratch:DI 6 "=a"))]
+  "TARGET_64BIT && optimize"
+  "ddiv\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
-   (set_attr "mode"    "DI")
-   (set_attr "length"  "15")])         ;; various tests for dividing by 0 and such
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
 
-(define_insn "udivmodsi4"
+(define_expand "udivmodsi4"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (udiv:SI (match_operand:SI 1 "register_operand" "d")
                 (match_operand:SI 2 "register_operand" "d")))
    (clobber (match_scratch:SI 5 "=h"))
    (clobber (match_scratch:SI 6 "=a"))]
   "optimize"
-  "*
+  "
 {
-  if (find_reg_note (insn, REG_UNUSED, operands[3]))
-    return \"divu\\t%0,%1,%2\";
+  rtx label;
 
-  if (find_reg_note (insn, REG_UNUSED, operands[0]))
-    return \"remu\\t%3,%1,%2\";
+  emit_insn (gen_udivmodsi4_internal (operands[0], operands[1], operands[2],
+                                      operands[3]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  
+  DONE;
+}")
 
-  return \"divu\\t%0,%1,%2\;mfhi\\t%3\";
-}"
+(define_insn "udivmodsi4_internal"
+  [(set (match_operand:SI 0 "register_operand" "=l")
+       (udiv:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "register_operand" "d")))
+   (set (match_operand:SI 3 "register_operand" "=h")
+       (umod:SI (match_dup 1)
+                (match_dup 2)))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "optimize"
+  "divu\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "8")])          ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
-(define_insn "udivmoddi4"
+(define_expand "udivmoddi4"
   [(set (match_operand:DI 0 "register_operand" "=d")
        (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
                 (match_operand:DI 2 "se_register_operand" "d")))
    (clobber (match_scratch:DI 5 "=h"))
    (clobber (match_scratch:DI 6 "=a"))]
   "TARGET_64BIT && optimize"
+  "
+{
+  rtx label;
+
+  emit_insn (gen_udivmoddi4_internal (operands[0], operands[1], operands[2],
+                                      operands[3]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  
+  DONE;
+}")
+
+(define_insn "udivmoddi4_internal"
+  [(set (match_operand:DI 0 "register_operand" "=l")
+       (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "se_register_operand" "d")))
+   (set (match_operand:DI 3 "register_operand" "=h")
+       (umod:DI (match_dup 1)
+                (match_dup 2)))
+   (clobber (match_scratch:DI 6 "=a"))]
+  "TARGET_64BIT && optimize"
+  "ddivu\\t$0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+;; Division trap
+
+(define_insn "div_trap"
+  [(trap_if (eq (match_operand 0 "register_operand" "d")
+               (match_operand 1 "reg_or_0_operand" "dJ"))
+            (match_operand 2 "immediate_operand" ""))]
+  ""
   "*
 {
-  if (find_reg_note (insn, REG_UNUSED, operands[3]))
-    return \"ddivu\\t%0,%1,%2\";
+  rtx link;
+  int have_dep_anti = 0;
 
-  if (find_reg_note (insn, REG_UNUSED, operands[0]))
-    return \"dremu\\t%3,%1,%2\";
+  /* For divmod if one division is not needed then we don't need an extra
+     divide by zero trap, which is anti dependent on previous trap */
+  for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
 
-  return \"ddivu\\t%0,%1,%2\;mfhi\\t%3\";
+    if ((int) REG_DEP_ANTI == (int) REG_NOTE_KIND (link)
+        && GET_CODE (PATTERN (XEXP (link, 0))) == TRAP_IF
+       && REGNO (operands[1]) == 0)
+      have_dep_anti = 1;
+  if (! have_dep_anti)
+    if (GENERATE_BRANCHLIKELY)
+      return \"%(beql\\t%0,%1,.+8\\n\\tbreak\\t%2%)\";
+    else
+      return \"%(bne\\t%0,%1,.+12\\n\\tnop\\n\\tbreak\\t%2%)\";
 }"
-  [(set_attr "type"    "idiv")
-   (set_attr "mode"    "DI")
-   (set_attr "length"  "8")])          ;; various tests for dividing by 0 and such
+  [(set_attr "type" "unknown")
+   (set_attr "length" "2")])
 
 (define_expand "divsi3"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+  [(set (match_operand:SI 0 "register_operand" "=l")
        (div:SI (match_operand:SI 1 "register_operand" "d")
-               (match_operand:SI 2 "nonmemory_operand" "di")))
-   (clobber (match_scratch:SI 3 "=l"))
-   (clobber (match_scratch:SI 4 "=h"))
-   (clobber (match_scratch:SI 6 "=a"))]
+               (match_operand:SI 2 "register_operand" "d")))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "!optimize"
   "
 {
-  /* MIPS16 needs div/rem ops in registers. */
-  if (TARGET_MIPS16)
-    operands[2] = force_reg (SImode, operands[2]);
+  rtx label;
+
+  emit_insn (gen_divsi3_internal (operands[0], operands[1], operands[2]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  if (TARGET_CHECK_RANGE_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (-1)),
+                              GEN_INT (0x6)));
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0x80000000)),
+                              GEN_INT (0x6)));
+    }
+  
+  DONE;
 }")
 
 (define_insn "divsi3_internal"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+  [(set (match_operand:SI 0 "register_operand" "=l")
        (div:SI (match_operand:SI 1 "register_operand" "d")
-               (match_operand:SI 2 "nonmemory_operand" "di")))]
+               (match_operand:SI 2 "nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "!optimize"
-  "div\\t%0,%1,%2"
+  "div\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "13")]) ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
 (define_expand "divdi3"
-  [(set (match_operand:DI 0 "register_operand" "=d")
+  [(set (match_operand:DI 0 "register_operand" "=l")
        (div:DI (match_operand:DI 1 "se_register_operand" "d")
-               (match_operand:DI 2 "se_nonmemory_operand" "di")))
-   (clobber (match_scratch:DI 3 "=l"))
-   (clobber (match_scratch:DI 4 "=h"))
-   (clobber (match_scratch:DI 6 "=a"))]
+               (match_operand:DI 2 "se_register_operand" "d"))) 
+   (clobber (match_scratch:DI 3 "=h"))
+   (clobber (match_scratch:DI 4 "=a"))]
   "TARGET_64BIT && !optimize"
   "
 {
-  /* MIPS16 needs div/rem ops in registers. */
-  if (TARGET_MIPS16)
-    operands[2] = force_reg (DImode, operands[2]);
+  rtx label;
+
+  emit_insn (gen_divdi3_internal (operands[0], operands[1], operands[2]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  if (TARGET_CHECK_RANGE_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (-1)),
+                              GEN_INT (0x6)));
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0x80000000)),
+                              GEN_INT (0x6)));
+    }
+  
+  DONE;
 }")
 
 (define_insn "divdi3_internal"
-  [(set (match_operand:DI 0 "register_operand" "=d")
+  [(set (match_operand:DI 0 "register_operand" "=l")
        (div:DI (match_operand:DI 1 "se_register_operand" "d")
-               (match_operand:DI 2 "se_nonmemory_operand" "di")))]
+               (match_operand:DI 2 "se_nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "TARGET_64BIT && !optimize"
-  "ddiv\\t%0,%1,%2"
+  "ddiv\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "DI")
-   (set_attr "length"  "14")]) ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
 (define_expand "modsi3"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+  [(set (match_operand:SI 0 "register_operand" "=h")
        (mod:SI (match_operand:SI 1 "register_operand" "d")
-               (match_operand:SI 2 "nonmemory_operand" "di")))
+               (match_operand:SI 2 "register_operand" "d")))
    (clobber (match_scratch:SI 3 "=l"))
-   (clobber (match_scratch:SI 4 "=h"))
-   (clobber (match_scratch:SI 6 "=a"))]
+   (clobber (match_scratch:SI 4 "=a"))]
   "!optimize"
   "
 {
-  /* MIPS16 needs div/rem ops in registers. */
-  if (TARGET_MIPS16)
-    operands[2] = force_reg (SImode, operands[2]);
+  rtx label;
+
+  emit_insn (gen_modsi3_internal (operands[0], operands[1], operands[2]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  if (TARGET_CHECK_RANGE_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (-1)),
+                              GEN_INT (0x6)));
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0x80000000)),
+                              GEN_INT (0x6)));
+    }
+  
+  DONE;
 }")
 
 (define_insn "modsi3_internal"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+  [(set (match_operand:SI 0 "register_operand" "=h")
        (mod:SI (match_operand:SI 1 "register_operand" "d")
-               (match_operand:SI 2 "nonmemory_operand" "di")))]
+               (match_operand:SI 2 "nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "!optimize"
-  "rem\\t%0,%1,%2"
+  "div\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "13")]) ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
 (define_expand "moddi3"
-  [(set (match_operand:DI 0 "register_operand" "=d")
+  [(set (match_operand:DI 0 "register_operand" "=h")
        (mod:DI (match_operand:DI 1 "se_register_operand" "d")
-               (match_operand:DI 2 "se_nonmemory_operand" "di")))
+               (match_operand:DI 2 "se_register_operand" "d")))
    (clobber (match_scratch:DI 3 "=l"))
-   (clobber (match_scratch:DI 4 "=h"))
-   (clobber (match_scratch:DI 6 "=a"))]
+   (clobber (match_scratch:DI 4 "=a"))]
   "TARGET_64BIT && !optimize"
   "
 {
-  /* MIPS16 needs div/rem ops in registers. */
-  if (TARGET_MIPS16)
-    operands[2] = force_reg (DImode, operands[2]);
+  rtx label;
+
+  emit_insn (gen_moddi3_internal (operands[0], operands[1], operands[2]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  if (TARGET_CHECK_RANGE_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (-1)),
+                              GEN_INT (0x6)));
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0x80000000)),
+                              GEN_INT (0x6)));
+    }
+  
+  DONE;
 }")
 
 (define_insn "moddi3_internal"
-  [(set (match_operand:DI 0 "register_operand" "=d")
+  [(set (match_operand:DI 0 "register_operand" "=h")
        (mod:DI (match_operand:DI 1 "se_register_operand" "d")
-               (match_operand:DI 2 "se_nonmemory_operand" "di")))]
+               (match_operand:DI 2 "se_nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "TARGET_64BIT && !optimize"
-  "drem\\t%0,%1,%2"
+  "ddiv\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "DI")
-   (set_attr "length"  "14")]) ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
 (define_expand "udivsi3"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+  [(set (match_operand:SI 0 "register_operand" "=l")
        (udiv:SI (match_operand:SI 1 "register_operand" "d")
-                (match_operand:SI 2 "nonmemory_operand" "di")))
-   (clobber (match_scratch:SI 3 "=l"))
-   (clobber (match_scratch:SI 4 "=h"))
-   (clobber (match_scratch:SI 6 "=a"))]
+                (match_operand:SI 2 "register_operand" "d")))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "!optimize"
   "
 {
-  /* MIPS16 needs div/rem ops in registers. */
-  if (TARGET_MIPS16)
-    operands[2] = force_reg (SImode, operands[2]);
+  rtx label;
+
+  emit_insn (gen_udivsi3_internal (operands[0], operands[1], operands[2]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  
+  DONE;
 }")
 
 (define_insn "udivsi3_internal"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+  [(set (match_operand:SI 0 "register_operand" "=l")
        (udiv:SI (match_operand:SI 1 "register_operand" "d")
-                (match_operand:SI 2 "nonmemory_operand" "di")))]
+                (match_operand:SI 2 "nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "!optimize"
-  "divu\\t%0,%1,%2"
+  "divu\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "7")])  ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
 (define_expand "udivdi3"
-  [(set (match_operand:DI 0 "register_operand" "=d")
+  [(set (match_operand:DI 0 "register_operand" "=l")
        (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
-                (match_operand:DI 2 "se_nonmemory_operand" "di")))
-   (clobber (match_scratch:DI 3 "=l"))
-   (clobber (match_scratch:DI 4 "=h"))
-   (clobber (match_scratch:DI 6 "=a"))]
+                (match_operand:DI 2 "se_register_operand" "di")))
+   (clobber (match_scratch:DI 3 "=h"))
+   (clobber (match_scratch:DI 4 "=a"))]
   "TARGET_64BIT && !optimize"
   "
 {
-  /* MIPS16 needs div/rem ops in registers. */
-  if (TARGET_MIPS16)
-    operands[2] = force_reg (DImode, operands[2]);
+  rtx label;
+
+  emit_insn (gen_udivdi3_internal (operands[0], operands[1], operands[2]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  
+  DONE;
 }")
 
 (define_insn "udivdi3_internal"
-  [(set (match_operand:DI 0 "register_operand" "=d")
+  [(set (match_operand:DI 0 "register_operand" "=l")
        (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
-                (match_operand:DI 2 "se_nonmemory_operand" "di")))]
+                (match_operand:DI 2 "se_nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "TARGET_64BIT && !optimize"
-  "ddivu\\t%0,%1,%2"
+  "ddivu\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "DI")
-   (set_attr "length"  "7")])  ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
 (define_expand "umodsi3"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+  [(set (match_operand:SI 0 "register_operand" "=h")
        (umod:SI (match_operand:SI 1 "register_operand" "d")
-                (match_operand:SI 2 "nonmemory_operand" "di")))
+                (match_operand:SI 2 "register_operand" "d")))
    (clobber (match_scratch:SI 3 "=l"))
-   (clobber (match_scratch:SI 4 "=h"))
-   (clobber (match_scratch:SI 6 "=a"))]
+   (clobber (match_scratch:SI 4 "=a"))]
   "!optimize"
   "
 {
-  /* MIPS16 needs div/rem ops in registers. */
-  if (TARGET_MIPS16)
-    operands[2] = force_reg (SImode, operands[2]);
+  rtx label;
+
+  emit_insn (gen_umodsi3_internal (operands[0], operands[1], operands[2]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (SImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  
+  DONE;
 }")
 
 (define_insn "umodsi3_internal"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+  [(set (match_operand:SI 0 "register_operand" "=h")
        (umod:SI (match_operand:SI 1 "register_operand" "d")
-                (match_operand:SI 2 "nonmemory_operand" "di")))]
+                (match_operand:SI 2 "nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "!optimize"
-  "remu\\t%0,%1,%2"
+  "divu\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "7")])  ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
 (define_expand "umoddi3"
-  [(set (match_operand:DI 0 "register_operand" "=d")
+  [(set (match_operand:DI 0 "register_operand" "=h")
        (umod:DI (match_operand:DI 1 "se_register_operand" "d")
-                (match_operand:DI 2 "se_nonmemory_operand" "di")))
+                (match_operand:DI 2 "se_register_operand" "di")))
    (clobber (match_scratch:DI 3 "=l"))
-   (clobber (match_scratch:DI 4 "=h"))
-   (clobber (match_scratch:DI 6 "=a"))]
+   (clobber (match_scratch:DI 4 "=a"))]
   "TARGET_64BIT && !optimize"
   "
 {
-  /* MIPS16 needs div/rem ops in registers. */
-  if (TARGET_MIPS16)
-    operands[2] = force_reg (DImode, operands[2]);
+  rtx label;
+
+  emit_insn (gen_umoddi3_internal (operands[0], operands[1], operands[2]));
+  if (!TARGET_NO_CHECK_ZERO_DIV)
+    {
+      emit_insn (gen_div_trap (operands[2],
+                              copy_to_mode_reg (DImode, GEN_INT (0)),
+                              GEN_INT (0x7)));
+    }
+  
+  DONE;
 }")
 
 (define_insn "umoddi3_internal"
-  [(set (match_operand:DI 0 "register_operand" "=d")
+  [(set (match_operand:DI 0 "register_operand" "=h")
        (umod:DI (match_operand:DI 1 "se_register_operand" "d")
-                (match_operand:DI 2 "se_nonmemory_operand" "di")))]
+                (match_operand:DI 2 "se_nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=a"))]
   "TARGET_64BIT && !optimize"
-  "dremu\\t%0,%1,%2"
+  "ddivu\\t$0,%1,%2"
   [(set_attr "type"    "idiv")
    (set_attr "mode"    "DI")
-   (set_attr "length"  "7")])  ;; various tests for dividing by 0 and such
-
+   (set_attr "length"  "1")])
 \f
 ;;
 ;;  ....................