aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJeff Law <law@gcc.gnu.org>1997-04-01 18:33:45 -0700
committerJeff Law <law@gcc.gnu.org>1997-04-01 18:33:45 -0700
commit22ef4e9b59f8df4b3a8e37d178426b2cfe059657 (patch)
treedd8d664c5789730a9a8a40c022140026db3891e9 /gcc
parente8adce1747d6a07982425c014a7e1ae2639ce04b (diff)
downloadgcc-22ef4e9b59f8df4b3a8e37d178426b2cfe059657.zip
gcc-22ef4e9b59f8df4b3a8e37d178426b2cfe059657.tar.gz
gcc-22ef4e9b59f8df4b3a8e37d178426b2cfe059657.tar.bz2
stdarg.h: Include va-mn10300.h.
* ginclude/stdarg.h: Include va-mn10300.h. * 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. From-SVN: r13827
Diffstat (limited to 'gcc')
-rw-r--r--gcc/config/mn10300/mn10300.c215
-rw-r--r--gcc/config/mn10300/mn10300.h80
-rw-r--r--gcc/config/mn10300/mn10300.md178
-rw-r--r--gcc/ginclude/stdarg.h6
-rw-r--r--gcc/ginclude/varargs.h6
5 files changed, 427 insertions, 58 deletions
diff --git a/gcc/config/mn10300/mn10300.c b/gcc/config/mn10300/mn10300.c
index 1e4768f..5093351 100644
--- a/gcc/config/mn10300/mn10300.c
+++ b/gcc/config/mn10300/mn10300.c
@@ -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";
+}
diff --git a/gcc/config/mn10300/mn10300.h b/gcc/config/mn10300/mn10300.h
index 1cac33a..f70b6bf 100644
--- a/gcc/config/mn10300/mn10300.h
+++ b/gcc/config/mn10300/mn10300.h
@@ -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)
+
/* 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)
#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 ();
diff --git a/gcc/config/mn10300/mn10300.md b/gcc/config/mn10300/mn10300.md
index c36ee9e..5db2a94 100644
--- a/gcc/config/mn10300/mn10300.md
+++ b/gcc/config/mn10300/mn10300.md
@@ -185,6 +185,9 @@
|| register_operand (operands[1], DImode)"
"*
{
+ long val[2];
+ REAL_VALUE_TYPE rv;
+
switch (which_alternative)
{
case 0:
@@ -202,6 +205,26 @@
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)))
{
@@ -221,7 +244,24 @@
}
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")])
@@ -245,6 +285,9 @@
|| register_operand (operands[1], DFmode)"
"*
{
+ long val[2];
+ REAL_VALUE_TYPE rv;
+
switch (which_alternative)
{
case 0:
@@ -262,6 +305,26 @@
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)))
{
@@ -281,7 +344,24 @@
}
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")])
@@ -297,9 +377,22 @@
(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")
@@ -441,18 +534,12 @@
"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)))])]
""
"
{
@@ -462,11 +549,44 @@
}")
(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"
@@ -491,6 +611,22 @@
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")])
@@ -826,7 +962,9 @@
{
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;
}")
@@ -849,9 +987,11 @@
{
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;
}")
diff --git a/gcc/ginclude/stdarg.h b/gcc/ginclude/stdarg.h
index f817c0b..3b3fdf5 100644
--- a/gcc/ginclude/stdarg.h
+++ b/gcc/ginclude/stdarg.h
@@ -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 */
diff --git a/gcc/ginclude/varargs.h b/gcc/ginclude/varargs.h
index 4f3b8a3..c8fa205 100644
--- a/gcc/ginclude/varargs.h
+++ b/gcc/ginclude/varargs.h
@@ -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 */