OSDN Git Service

* ginclude/stdarg.h: Include va-mn10300.h.
authorlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 2 Apr 1997 01:33:45 +0000 (01:33 +0000)
committerlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 2 Apr 1997 01:33:45 +0000 (01:33 +0000)
        * ginclude/varargs.h: Likewise.
        * ginclude/va-mn10300.h: New file.
        * mn10300/mn10300.c (expand_prologue): If current_function_varargs is
        nonzero, then flush d0/d1 back into the stack.
        (mn10300_builtin_saveregs): New function.
        (function_arg, function_arg_partial_nregs): New functions.
        (initial_offset): Tweak now that the RP save area is allocated
        and deallocated around each call again.
        * mn10300/mn10300.h (FIRST_PARM_OFFSET): Now 4.
        (FRAME_POINTER_REQUIRED): Require a frame pointer for all non-leaf
        functions.
        (REG_PARM_STACK_SPACE): Now 8 bytes.
        (FUNCTION_ARG_REGNO_P): Update for new parameter passing conventions.
        (CUMULATIVE_ARGS, INIT_CUMULATIVE_ARGS): Likewise.
        (FUNCTION_ARG_ADVANCE, FUNCTION_ARG): Likewise.
        (FUNCTION_ARG_PARTIAL_NREGS): Likewise.
        (TRAMPOLINE_TEMPLATE): Don't clobber d0 anymore.
        (TRAMPOLINE_SIZE, INITIALIZE_TRAMPOLINE): Corresponding changes.
        (EXPAND_BUILTIN_SAVEREGS): Define.
        * mn10300/mn10300.md (call, call_value patterns): Allocate and
        deallocate a stack slot for the return pointer around each call.

        * mn10300/mn10300.h (RTX_COSTS): Refine.
        (CASE_VALUES_THRESHHOLD, NO_FUNCTION_CSE): Likewise.
        * mn10300/mn10300.c (output_tst): New function.
        * mn10300/mn10300.md (movdi, movdf): Improve code to load constants
        into registers.
        (tst insns): Use output_tst to optimize some cases.  Add versions to
        encourage more zero extensions instead of sign extensions of HImode
        and QImode values.
        (divsi3, udivsi3): Remove patterns.  Replaced by...
        (divmodsi4, udivmodsi4): New expanders/patterns.
        (andsi3): Optimize "and" operations with certain constants.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@13827 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/config/mn10300/mn10300.c
gcc/config/mn10300/mn10300.h
gcc/config/mn10300/mn10300.md
gcc/ginclude/stdarg.h
gcc/ginclude/varargs.h

index 1e4768f..5093351 100644 (file)
@@ -343,6 +343,20 @@ expand_prologue ()
 {
   unsigned int size = get_frame_size ();
 
+  /* If this is an old-style varargs function, then its arguments
+     need to be flushed back to the stack.  */
+  if (current_function_varargs)
+    {
+      emit_move_insn (gen_rtx (MEM, SImode,
+                              gen_rtx (PLUS, Pmode, stack_pointer_rtx,
+                                       GEN_INT (4))),
+                     gen_rtx (REG, SImode, 0));
+      emit_move_insn (gen_rtx (MEM, SImode,
+                              gen_rtx (PLUS, Pmode, stack_pointer_rtx,
+                                       GEN_INT (8))),
+                     gen_rtx (REG, SImode, 1));
+    }
+
   /* And now store all the registers onto the stack with a
      single two byte instruction.  */
   if (regs_ever_live[2] || regs_ever_live[3]
@@ -519,9 +533,9 @@ initial_offset (from, to)
       if (regs_ever_live[2] || regs_ever_live[3]
          || regs_ever_live[6] || regs_ever_live[7]
          || frame_pointer_needed)
-       return 20;
+       return 16;
       else
-       return 4;
+       return 0;
     }
 
   if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
@@ -529,9 +543,9 @@ initial_offset (from, to)
       if (regs_ever_live[2] || regs_ever_live[3]
          || regs_ever_live[6] || regs_ever_live[7]
          || frame_pointer_needed)
-       return get_frame_size () + 20;
+       return get_frame_size () + 16;
       else
-       return get_frame_size () + 4;
+       return get_frame_size ();
     }
 
   if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
@@ -539,3 +553,196 @@ initial_offset (from, to)
 
   abort ();
 }
+
+/* Flush the argument registers to the stack for a stdarg function;
+   return the new argument pointer.  */
+rtx
+mn10300_builtin_saveregs (arglist)
+     tree arglist;
+{
+  rtx offset;
+  tree fntype = TREE_TYPE (current_function_decl);
+  int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
+                   && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+                       != void_type_node)))
+                ? UNITS_PER_WORD : 0);
+
+  if (argadj)
+    offset = plus_constant (current_function_arg_offset_rtx, argadj);
+  else
+    offset = current_function_arg_offset_rtx;
+
+  emit_move_insn (gen_rtx (MEM, SImode, current_function_internal_arg_pointer),
+                 gen_rtx (REG, SImode, 0));
+  emit_move_insn (gen_rtx (MEM, SImode,
+                          plus_constant
+                            (current_function_internal_arg_pointer, 4)),
+                 gen_rtx (REG, SImode, 1));
+  return copy_to_reg (expand_binop (Pmode, add_optab,
+                                   current_function_internal_arg_pointer,
+                                   offset, 0, 0, OPTAB_LIB_WIDEN));
+}
+
+/* Return an RTX to represent where a value with mode MODE will be returned
+   from a function.  If the result is 0, the argument is pushed.  */
+
+rtx
+function_arg (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  rtx result = 0;
+  int size, align;
+
+  /* We only support using 2 data registers as argument registers.  */
+  int nregs = 2;
+
+  /* Figure out the size of the object to be passed.  */
+  if (mode == BLKmode)
+    size = int_size_in_bytes (type);
+  else
+    size = GET_MODE_SIZE (mode);
+
+  /* Figure out the alignment of the object to be passed.  */
+  align = size;
+
+  cum->nbytes = (cum->nbytes + 3) & ~3;
+
+  /* Don't pass this arg via a register if all the argument registers
+     are used up.  */
+  if (cum->nbytes > nregs * UNITS_PER_WORD)
+    return 0;
+
+  /* Don't pass this arg via a register if it would be split between
+     registers and memory.  */
+  if (type == NULL_TREE
+      && cum->nbytes + size > nregs * UNITS_PER_WORD)
+    return 0;
+
+  switch (cum->nbytes / UNITS_PER_WORD)
+    {
+    case 0:
+      result = gen_rtx (REG, mode, 0);
+      break;
+    case 1:
+      result = gen_rtx (REG, mode, 1);
+      break;
+    default:
+      result = 0;
+    }
+
+  return result;
+}
+
+/* Return the number of registers to use for an argument passed partially
+   in registers and partially in memory.  */
+
+int
+function_arg_partial_nregs (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  int size, align;
+
+  /* We only support using 2 data registers as argument registers.  */
+  int nregs = 2;
+
+  /* Figure out the size of the object to be passed.  */
+  if (mode == BLKmode)
+    size = int_size_in_bytes (type);
+  else
+    size = GET_MODE_SIZE (mode);
+
+  /* Figure out the alignment of the object to be passed.  */
+  align = size;
+
+  cum->nbytes = (cum->nbytes + 3) & ~3;
+
+  /* Don't pass this arg via a register if all the argument registers
+     are used up.  */
+  if (cum->nbytes > nregs * UNITS_PER_WORD)
+    return 0;
+
+  if (cum->nbytes + size <= nregs * UNITS_PER_WORD)
+    return 0;
+
+  /* Don't pass this arg via a register if it would be split between
+     registers and memory.  */
+  if (type == NULL_TREE
+      && cum->nbytes + size > nregs * UNITS_PER_WORD)
+    return 0;
+
+  return (nregs * UNITS_PER_WORD - cum->nbytes) / UNITS_PER_WORD;
+}
+
+/* Output a tst insn.  */
+char *
+output_tst (operand, insn)
+     rtx operand, insn;
+{
+  
+  rtx temp;
+  int past_call = 0;
+
+  /* We can save a byte if we can find a register which has the value
+     zero in it.  */
+  temp = PREV_INSN (insn);
+  while (temp)
+    {
+      rtx set;
+
+      /* We allow the search to go through call insns.  We record
+        the fact that we've past a CALL_INSN and reject matches which
+        use call clobbered registers.  */
+      if (GET_CODE (temp) == CODE_LABEL
+         || GET_CODE (temp) == JUMP_INSN
+         || GET_CODE (temp) == BARRIER)
+       break;
+
+      if (GET_CODE (temp) == CALL_INSN)
+       past_call = 1;
+
+      if (GET_CODE (temp) == NOTE)
+       {
+         temp = PREV_INSN (temp);
+         continue;
+       }
+
+      /* It must be an insn, see if it is a simple set. */
+      set = single_set (temp);
+      if (!set)
+       {
+         temp = PREV_INSN (temp);
+         continue;
+       }
+
+      /* Are we setting a data register to zero (this does not win for
+        address registers)? 
+
+        If it's a call clobbered register, have we past a call?
+
+        Make sure the register we find isn't the same as ourself;
+        the mn10300 can't encode that.  */
+      if (REG_P (SET_DEST (set))
+         && SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
+         && !reg_set_between_p (SET_DEST (set), temp, insn)
+         && REGNO_REG_CLASS (REGNO (SET_DEST (set))) == DATA_REGS
+         && REGNO (SET_DEST (set)) != REGNO (operand)
+         && (!past_call 
+             || !call_used_regs[REGNO (SET_DEST (set))]))
+       {
+         rtx xoperands[2];
+         xoperands[0] = operand;
+         xoperands[1] = SET_DEST (set);
+
+         output_asm_insn ("cmp %1,%0", xoperands);
+         return "";
+       }
+      temp = PREV_INSN (temp);
+    }
+  return "cmp 0,%0";
+}
index 1cac33a..f70b6bf 100644 (file)
@@ -351,7 +351,7 @@ enum reg_class {
 /* Is equal to the size of the saved fp + pc, even if an fp isn't
    saved since the value is used before we know.  */
 
-#define FIRST_PARM_OFFSET(FNDECL) -4
+#define FIRST_PARM_OFFSET(FNDECL) 4
 
 /* Specify the registers used for certain standard purposes.
    The values of these macros are register numbers.  */
@@ -404,7 +404,7 @@ enum reg_class {
   OFFSET = initial_offset (FROM, TO)
 
 #define FRAME_POINTER_REQUIRED \
-  !(leaf_function_p () || current_function_outgoing_args_size == 0)
+  !(leaf_function_p ())
 
 #define CAN_DEBUG_WITHOUT_FP
 
@@ -420,21 +420,16 @@ enum reg_class {
 
 #define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
 
-/* On the mn10300, the caller is responsible for allocating and deallocating
-   a stack slot for the "call" and "calls" instructions to save their return
-   pointer.  We used to do this in the "call" and "call_value" expanders,
-   but that generated poor code.
-
-   Now we pretend that we have an outgoing register parameter space so that
-   the generic function calling code will allocate the slot.  */
-   
-#define REG_PARM_STACK_SPACE(FNDECL) 4
+/* We use d0/d1 for passing parameters, so allocate 8 bytes of space
+   for a register flushback area.  */
+#define REG_PARM_STACK_SPACE(DECL) 8
 #define OUTGOING_REG_PARM_STACK_SPACE
 
 /* 1 if N is a possible register number for function argument passing.
    On the MN10300, no registers are used in this way.  */
 
-#define FUNCTION_ARG_REGNO_P(N) 0
+#define FUNCTION_ARG_REGNO_P(N) ((N) <= 1)
+
 \f
 /* Define a data type for recording info about an argument list
    during the scan of that argument list.  This data type should
@@ -445,7 +440,8 @@ enum reg_class {
    On the MN10300, this is a single integer, which is a number of bytes
    of arguments scanned so far.  */
 
-#define CUMULATIVE_ARGS int
+#define CUMULATIVE_ARGS struct cum_arg
+struct cum_arg {int nbytes; };
 
 /* Initialize a variable CUM of type CUMULATIVE_ARGS
    for a call to a function whose data type is FNTYPE.
@@ -454,16 +450,16 @@ enum reg_class {
    On the MN10300, the offset starts at 0.  */
 
 #define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT)      \
- ((CUM) = 0)
+ ((CUM).nbytes = 0)
 
 /* Update the data in CUM to advance over an argument
    of mode MODE and data type TYPE.
    (TYPE is null for libcalls where that information may not be available.)  */
 
 #define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)   \
- ((CUM) += ((MODE) != BLKmode                  \
-           ? (GET_MODE_SIZE (MODE) + 3) & ~3   \
-           : (int_size_in_bytes (TYPE) + 3) & ~3))
+ ((CUM).nbytes += ((MODE) != BLKmode                   \
+                  ? (GET_MODE_SIZE (MODE) + 3) & ~3    \
+                  : (int_size_in_bytes (TYPE) + 3) & ~3))
 
 /* Define where to put the arguments to a function.
    Value is zero to push the argument on the stack,
@@ -480,7 +476,12 @@ enum reg_class {
 
 /* On the MN10300 all args are pushed.  */   
 
-#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) 0
+extern struct rtx_def *function_arg ();
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+  function_arg (&CUM, MODE, TYPE, NAMED)
+
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
+  function_arg_partial_nregs (&CUM, MODE, TYPE, NAMED)
 
 \f
 #define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED)         \
@@ -531,12 +532,11 @@ enum reg_class {
 #define TRAMPOLINE_TEMPLATE(FILE)                      \
   do {                                                 \
     fprintf (FILE, "\tadd -4,sp\n");                   \
-    fprintf (FILE, "\t.long 0x0004fffa\n");                    \
+    fprintf (FILE, "\t.long 0x0004fffa\n");            \
+    fprintf (FILE, "\tmov (0,sp),a0\n");               \
     fprintf (FILE, "\tadd 4,sp\n");                    \
-    fprintf (FILE, "\tmov mdr,d0\n");          \
-    fprintf (FILE, "\tmov d0,a0\n");           \
-    fprintf (FILE, "\tmov (15,a0),a1\n");              \
-    fprintf (FILE, "\tmov (19,a0),a0\n");              \
+    fprintf (FILE, "\tmov (13,a0),a1\n");              \
+    fprintf (FILE, "\tmov (17,a0),a0\n");              \
     fprintf (FILE, "\tjmp (a0)\n");                    \
     fprintf (FILE, "\t.long 0\n");                     \
     fprintf (FILE, "\t.long 0\n");                     \
@@ -544,7 +544,7 @@ enum reg_class {
 
 /* Length in units of the trampoline for entering a nested function.  */
 
-#define TRAMPOLINE_SIZE 0x1d
+#define TRAMPOLINE_SIZE 0x1b
 
 #define TRAMPOLINE_ALIGNMENT 32
 
@@ -554,11 +554,18 @@ enum reg_class {
 
 #define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT)                      \
 {                                                                      \
-  emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((TRAMP), 0x16)),        \
+  emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((TRAMP), 0x14)),        \
                 (CXT));                                                \
-  emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((TRAMP), 0x1a)),        \
+  emit_move_insn (gen_rtx (MEM, SImode, plus_constant ((TRAMP), 0x18)),        \
                 (FNADDR));                                             \
 }
+/* Emit code for a call to builtin_saveregs.  We must emit USE insns which
+   reference the 2 integer arg registers.
+   Ordinarily they are not call used registers, but they are for
+   _builtin_saveregs, so we must make this explicit.  */
+
+extern struct rtx_def *mn10300_builtin_saveregs ();
+#define EXPAND_BUILTIN_SAVEREGS(ARGLIST) mn10300_builtin_saveregs (ARGLIST)
 
 /* Addressing modes, and classification of registers for them.  */
 
@@ -740,22 +747,28 @@ enum reg_class {
 /* A crude cut at RTX_COSTS for the MN10300.  */
 
 /* Provide the costs of a rtl expression.  This is in the body of a
-   switch on CODE. 
-
-   There aren't DImode MOD, DIV or MULT operations, so call them
-   very expensive.  Everything else is pretty much a costant cost.  */
-
+   switch on CODE.  */
 #define RTX_COSTS(RTX,CODE,OUTER_CODE) \
   case MOD:            \
   case DIV:            \
-    return 60;         \
+    return 8;          \
   case MULT:           \
-    return 20;
+    return 8;
 
 /* Nonzero if access to memory by bytes or half words is no faster
    than accessing full words.  */
 #define SLOW_BYTE_ACCESS 1
 
+/* Dispatch tables on the mn10300 are extremely expensive in terms of code
+   and readonly data size.  So we crank up the case threshold value to
+   encourage a series of if/else comparisons to implement many small switch
+   statements.  In theory, this value could be increased much more if we
+   were solely optimizing for space, but we keep it "reasonable" to avoid
+   serious code efficiency lossage.  */
+#define CASE_VALUES_THRESHOLD 6
+
+#define NO_FUNCTION_CSE
+
 /* According expr.c, a value of around 6 should minimize code size, and
    for the MN10300 series, that's our primary concern.  */
 #define MOVE_RATIO 6
@@ -983,3 +996,4 @@ extern void notice_update_cc ();
 extern int call_address_operand ();
 extern enum reg_class secondary_reload_class ();
 extern int initial_offset ();
+extern char *output_tst ();
index c36ee9e..5db2a94 100644 (file)
    || register_operand (operands[1], DImode)"
   "*
 {
+  long val[2];
+  REAL_VALUE_TYPE rv;
+
   switch (which_alternative)
     {
       case 0:
       case 8:
       case 9:
       case 10:
+       if (GET_CODE (operands[1]) == CONST_INT)
+         {
+           val[0] = INTVAL (operands[1]);
+           val[1] = val[0] < 0 ? -1 : 0;
+         }
+       if (GET_CODE (operands[1]) == CONST_DOUBLE)
+         {
+           if (GET_MODE (operands[1]) == DFmode)
+             {
+               REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
+               REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
+             }
+           else if (GET_MODE (operands[1]) == VOIDmode
+                    || GET_MODE (operands[1]) == DImode)
+             {
+               val[0] = CONST_DOUBLE_LOW (operands[1]);
+               val[1] = CONST_DOUBLE_HIGH (operands[1]);
+             }
+         }
+
        if (GET_CODE (operands[1]) == MEM
            && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0)))
          {
              
          }
        else
-         return \"mov %L1,%L0\;mov %H1,%H0\";
+         {
+           if ((GET_CODE (operands[1]) == CONST_INT
+                || GET_CODE (operands[1]) == CONST_DOUBLE)
+               && val[0] == 0
+               && REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
+             output_asm_insn (\"clr %L0\", operands);
+           else
+             output_asm_insn (\"mov %L1,%L0\", operands);
+
+           if ((GET_CODE (operands[1]) == CONST_INT
+                || GET_CODE (operands[1]) == CONST_DOUBLE)
+               && val[1] == 0
+               && REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
+             output_asm_insn (\"clr %H0\", operands);
+           else
+             output_asm_insn (\"mov %H1,%H0\", operands);
+           return \"\";
+         }
     }
 }"
   [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])
    || register_operand (operands[1], DFmode)"
   "*
 {
+  long val[2];
+  REAL_VALUE_TYPE rv;
+
   switch (which_alternative)
     {
       case 0:
       case 8:
       case 9:
       case 10:
+       if (GET_CODE (operands[1]) == CONST_INT)
+         {
+           val[0] = INTVAL (operands[1]);
+           val[1] = val[0] < 0 ? -1 : 0;
+         }
+       if (GET_CODE (operands[1]) == CONST_DOUBLE)
+         {
+           if (GET_MODE (operands[1]) == DFmode)
+             {
+               REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
+               REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
+             }
+           else if (GET_MODE (operands[1]) == VOIDmode
+                    || GET_MODE (operands[1]) == DImode)
+             {
+               val[0] = CONST_DOUBLE_LOW (operands[1]);
+               val[1] = CONST_DOUBLE_HIGH (operands[1]);
+             }
+         }
+
        if (GET_CODE (operands[1]) == MEM
            && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0)))
          {
              
          }
        else
-         return \"mov %L1,%L0\;mov %H1,%H0\";
+         {
+           if ((GET_CODE (operands[1]) == CONST_INT
+                || GET_CODE (operands[1]) == CONST_DOUBLE)
+               && val[0] == 0
+               && REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
+             output_asm_insn (\"clr %L0\", operands);
+           else
+             output_asm_insn (\"mov %L1,%L0\", operands);
+
+           if ((GET_CODE (operands[1]) == CONST_INT
+                || GET_CODE (operands[1]) == CONST_DOUBLE)
+               && val[1] == 0
+               && REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
+             output_asm_insn (\"clr %H0\", operands);
+           else
+             output_asm_insn (\"mov %H1,%H0\", operands);
+           return \"\";
+         }
     }
 }"
   [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])
 (define_insn "tstsi"
   [(set (cc0) (match_operand:SI 0 "register_operand" "da"))]
   ""
-  "cmp 0,%0"
+  "* return output_tst (operands[0], insn);"
+  [(set_attr "cc" "tst")])
+
+(define_insn ""
+  [(set (cc0) (zero_extend:SI (match_operand:QI 0 "memory_operand" "d")))]
+  ""
+  "* return output_tst (operands[0], insn);"
+  [(set_attr "cc" "tst")])
+
+(define_insn ""
+  [(set (cc0) (zero_extend:SI (match_operand:HI 0 "memory_operand" "d")))]
+  ""
+  "* return output_tst (operands[0], insn);"
   [(set_attr "cc" "tst")])
 
+
 (define_insn "cmpsi"
   [(set (cc0)
        (compare (match_operand:SI 0 "register_operand" "!*d*a,da")
   "mul %2,%0"
   [(set_attr "cc" "set_zn_c0")])
 
-(define_insn "divsi3"
-  [(set (match_operand:SI 0 "register_operand" "=d")
-       (div:SI (match_operand:SI 1 "register_operand" "0")
-                (match_operand:SI 2 "register_operand" "d")))]
-  ""
-  "ext %0\;div %2,%0"
-  [(set_attr "cc" "set_zn_c0")])
-
-(define_expand "udivsi3"
-  [(set (match_operand:SI 0 "register_operand" "")
-       (udiv:SI (match_operand:SI 1 "register_operand" "")
-                (match_operand:SI 2 "register_operand" "")))]
+(define_expand "udivmodsi4"
+  [(parallel [(set (match_operand:SI 0 "register_operand" "")
+                  (udiv:SI (match_operand:SI 1 "register_operand" "")
+                           (match_operand:SI 2 "register_operand" "")))
+             (set (match_operand:SI 3 "register_operand" "")
+                  (umod:SI (match_dup 1) (match_dup 2)))])]
   ""
   "
 {
 }")
 
 (define_insn ""
-  [(set (match_operand:SI 0 "register_operand" "=d")
-       (udiv:SI (match_operand:SI 1 "register_operand" "0")
-                (match_operand:SI 2 "register_operand" "d")))]
+  [(set (match_operand:SI 0 "general_operand" "=d")
+       (udiv:SI (match_operand:SI 1 "general_operand" "0")
+                (match_operand:SI 2 "general_operand" "d")))
+   (set (match_operand:SI 3 "general_operand" "=d")
+       (umod:SI (match_dup 1) (match_dup 2)))]
   ""
-  "divu %2,%0"
+  "*
+{
+  if (find_reg_note (insn, REG_UNUSED, operands[3]))
+    return \"divu %2,%0\";
+  else
+    return \"divu %2,%0\;mov mdr,%3\";
+}"
+  [(set_attr "cc" "set_zn_c0")])
+
+(define_expand "divmodsi4"
+  [(parallel [(set (match_operand:SI 0 "register_operand" "")
+                  (div:SI (match_operand:SI 1 "register_operand" "")
+                           (match_operand:SI 2 "register_operand" "")))
+             (set (match_operand:SI 3 "register_operand" "")
+                  (mod:SI (match_dup 1) (match_dup 2)))])]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "general_operand" "=d")
+       (div:SI (match_operand:SI 1 "general_operand" "0")
+                (match_operand:SI 2 "general_operand" "d")))
+   (set (match_operand:SI 3 "general_operand" "=d")
+       (mod:SI (match_dup 1) (match_dup 2)))]
+  ""
+  "*
+{
+  if (find_reg_note (insn, REG_UNUSED, operands[3]))
+    return \"ext %0\;div %2,%0\";
+  else
+    return \"ext %0\;div %2,%0\;mov mdr,%3\";
+}"
   [(set_attr "cc" "set_zn_c0")])
 
 (define_insn "clear_mdr"
     return \"extbu %0\";
   if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xffff)
     return \"exthu %0\";
+  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x7fffffff)
+    return \"add %0,%0\;lsr 1,%0\";
+  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x3fffffff)
+    return \"asl2 %0\;lsr 2,%0\";
+  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x1fffffff)
+    return \"add %0,%0\;asl2 %0\;lsr 3,%0\";
+  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x0fffffff)
+    return \"asl2 %0,%0\;asl2 %0\;lsr 4,%0\";
+  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffe)
+    return \"lsr 1,%0\;add %0,%0\";
+  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffc)
+    return \"lsr 2,%0\;asl2 %0\";
+  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff8)
+    return \"lsr 3,%0\;add %0,%0\;asl2 %0\";
+  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff0)
+    return \"lsr 4,%0\;asl2 %0\;asl2 %0\";
   return \"and %2,%0\";
 }"
   [(set_attr "cc" "none_0hit,set_zn_c0")])
 {
   if (! call_address_operand (XEXP (operands[0], 0)))
     XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0));
+  emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-4)));
   emit_call_insn (gen_call_internal (XEXP (operands[0], 0), operands[1]));
+  emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (4)));
   DONE;
 }")
 
 {
   if (! call_address_operand (XEXP (operands[1], 0)))
     XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0));
+  emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-4)));
   emit_call_insn (gen_call_value_internal (operands[0],
                                           XEXP (operands[1], 0),
                                           operands[2]));
+  emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (4)));
   DONE;
 }")
 
index f817c0b..3b3fdf5 100644 (file)
@@ -44,6 +44,9 @@
 #ifdef __sh__
 #include "va-sh.h"
 #else
+#ifdef __mn10300__
+#include "va-mn10300.h"
+#else
 
 /* Define __gnuc_va_list.  */
 
@@ -81,7 +84,7 @@ void va_end (__gnuc_va_list);         /* Defined in libgcc.a */
 /* We cast to void * and then to TYPE * because this avoids
    a warning about increasing the alignment requirement.  */
 
-#if defined (__arm__) || defined (__i386__) || defined (__i860__) || defined (__ns32000__) || defined (__vax__) || defined (__mn10300__)
+#if defined (__arm__) || defined (__i386__) || defined (__i860__) || defined (__ns32000__) || defined (__vax__)
 /* This is for little-endian machines; small args are padded upward.  */
 #define va_arg(AP, TYPE)                                               \
  (AP = (__gnuc_va_list) ((char *) (AP) + __va_rounded_size (TYPE)),    \
@@ -100,6 +103,7 @@ void va_end (__gnuc_va_list);               /* Defined in libgcc.a */
 
 #endif /* _STDARG_H */
 
+#endif /* not mn10300 */
 #endif /* not sh */
 #endif /* not powerpc with V.4 calling sequence */
 #endif /* not h8300 */
index 4f3b8a3..c8fa205 100644 (file)
@@ -42,6 +42,9 @@
 #ifdef __sh__
 #include "va-sh.h"
 #else
+#ifdef __mn10300__
+#include "va-mn10300.h"
+#else
 
 #ifdef __NeXT__
 
@@ -100,7 +103,7 @@ typedef void *__gnuc_va_list;
   (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
 #endif
 
-#if defined (__arm__) || defined (__i386__) || defined (__i860__) || defined (__ns32000__) || defined (__vax__) || defined (__mn10300__)
+#if defined (__arm__) || defined (__i386__) || defined (__i860__) || defined (__ns32000__) || defined (__vax__)
 /* This is for little-endian machines; small args are padded upward.  */
 #define va_arg(AP, TYPE)                                               \
  (AP = (__gnuc_va_list) ((char *) (AP) + __va_rounded_size (TYPE)),    \
@@ -117,6 +120,7 @@ typedef void *__gnuc_va_list;
 /* Copy __gnuc_va_list into another variable of this type.  */
 #define __va_copy(dest, src) (dest) = (src)
 
+#endif /* not mn10300 */
 #endif /* not sh */
 #endif /* not powerpc with V.4 calling sequence */
 #endif /* not h8300 */