summaryrefslogtreecommitdiff
path: root/gcc/config/mn10200/mn10200.c
diff options
context:
space:
mode:
authorlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>1997-06-23 17:29:13 +0000
committerlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>1997-06-23 17:29:13 +0000
commitc418d182f7127f60d5d6c1e34fb70003166f05a5 (patch)
tree261e2aa99386dba2f43afbf53c900aea44a53247 /gcc/config/mn10200/mn10200.c
parenteb56b12bde38d4d976fa16f95c4df9081eaa53ab (diff)
downloadgcc-c418d182f7127f60d5d6c1e34fb70003166f05a5.tar.gz
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@14289 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config/mn10200/mn10200.c')
-rw-r--r--gcc/config/mn10200/mn10200.c1542
1 files changed, 1542 insertions, 0 deletions
diff --git a/gcc/config/mn10200/mn10200.c b/gcc/config/mn10200/mn10200.c
new file mode 100644
index 00000000000..6c929c81136
--- /dev/null
+++ b/gcc/config/mn10200/mn10200.c
@@ -0,0 +1,1542 @@
+/* Subroutines for insn-output.c for Matsushita MN10200 series
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Contributed by Jeff Law (law@cygnus.com).
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "recog.h"
+#include "expr.h"
+#include "tree.h"
+#include "obstack.h"
+
+/* Global registers known to hold the value zero.
+
+ Normally we'd depend on CSE and combine to put zero into a
+ register and re-use it.
+
+ However, on the mn10x00 processors we implicitly use the constant
+ zero in tst instructions, so we might be able to do better by
+ loading the value into a register in the prologue, then re-useing
+ that register throughout the function.
+
+ We could perform similar optimizations for other constants, but with
+ gcse due soon, it doesn't seem worth the effort.
+
+ These variables hold a rtx for a register known to hold the value
+ zero throughout the entire function, or NULL if no register of
+ the appropriate class has such a value throughout the life of the
+ function. */
+rtx zero_dreg;
+rtx zero_areg;
+
+/* Note whether or not we need an out of line epilogue. */
+static int out_of_line_epilogue;
+
+/* Indicate this file was compiled by gcc and what optimization
+ level was used. */
+void
+asm_file_start (file)
+ FILE *file;
+{
+ fprintf (file, "#\tGCC For the Matsushita MN10200\n");
+ if (optimize)
+ fprintf (file, "# -O%d\n", optimize);
+ else
+ fprintf (file, "\n\n");
+ output_file_directive (file, main_input_filename);
+}
+
+/* Print operand X using operand code CODE to assembly language output file
+ FILE. */
+
+void
+print_operand (file, x, code)
+ FILE *file;
+ rtx x;
+ int code;
+{
+ switch (code)
+ {
+ case 'b':
+ case 'B':
+ /* These are normal and reversed branches. */
+ switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
+ {
+ case NE:
+ fprintf (file, "ne");
+ break;
+ case EQ:
+ fprintf (file, "eq");
+ break;
+ case GE:
+ fprintf (file, "ge");
+ break;
+ case GT:
+ fprintf (file, "gt");
+ break;
+ case LE:
+ fprintf (file, "le");
+ break;
+ case LT:
+ fprintf (file, "lt");
+ break;
+ case GEU:
+ fprintf (file, "cc");
+ break;
+ case GTU:
+ fprintf (file, "hi");
+ break;
+ case LEU:
+ fprintf (file, "ls");
+ break;
+ case LTU:
+ fprintf (file, "cs");
+ break;
+ default:
+ abort ();
+ }
+ break;
+ case 'C':
+ /* This is used for the operand to a call instruction;
+ if it's a REG, enclose it in parens, else output
+ the operand normally. */
+ if (GET_CODE (x) == REG)
+ {
+ fputc ('(', file);
+ print_operand (file, x, 0);
+ fputc (')', file);
+ }
+ else
+ print_operand (file, x, 0);
+ break;
+
+ /* These are the least significant word in a 32bit value.
+ 'o' allows us to sign extend a constant if doing so
+ makes for more compact code. */
+ case 'L':
+ case 'o':
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ fputc ('(', file);
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ break;
+
+ case SUBREG:
+ fprintf (file, "%s",
+ reg_names[REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)]);
+ break;
+
+ case CONST_DOUBLE:
+ if (code == 'L')
+ {
+ long val;
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, val);
+ print_operand_address (file, GEN_INT (val & 0xffff));
+ }
+ else
+ {
+ long val;
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, val);
+
+ val &= 0xffff;
+ val = (((val) & 0xffff) ^ (~0x7fff)) + 0x8000;
+ print_operand_address (file, GEN_INT (val));
+ }
+ break;
+
+ case CONST_INT:
+ if (code == 'L')
+ print_operand_address (file, GEN_INT ((INTVAL (x) & 0xffff)));
+ else
+ {
+ unsigned int val = INTVAL (x) & 0xffff;
+ val = (((val) & 0xffff) ^ (~0x7fff)) + 0x8000;
+ print_operand_address (file, GEN_INT (val));
+ }
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ /* Similarly, but for the most significant word. */
+ case 'H':
+ case 'h':
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ fputc ('(', file);
+ x = adj_offsettable_operand (x, 2);
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+ break;
+
+ case SUBREG:
+ fprintf (file, "%s",
+ reg_names[REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)] + 1);
+ break;
+
+ case CONST_DOUBLE:
+ if (code == 'H')
+ {
+ long val;
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, val);
+
+ print_operand_address (file, GEN_INT ((val >> 16) & 0xffff));
+ }
+ else
+ {
+ long val;
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, val);
+
+ val = (val >> 16) & 0xffff;
+ val = (((val) & 0xffff) ^ (~0x7fff)) + 0x8000;
+
+ print_operand_address (file, GEN_INT (val));
+ }
+ break;
+
+ case CONST_INT:
+ if (code == 'H')
+ print_operand_address (file,
+ GEN_INT ((INTVAL (x) >> 16) & 0xffff));
+ else
+ {
+ unsigned int val = (INTVAL (x) >> 16) & 0xffff;
+ val = (((val) & 0xffff) ^ (~0x7fff)) + 0x8000;
+
+ print_operand_address (file, GEN_INT (val));
+ }
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ /* Output ~CONST_INT. */
+ case 'N':
+ if (GET_CODE (x) != CONST_INT)
+ abort ();
+ fprintf (file, "%d", ~INTVAL (x));
+ break;
+
+ /* An address which can not be register indirect, if it is
+ register indirect, then turn it into reg + disp. */
+ case 'A':
+ if (GET_CODE (x) != MEM)
+ abort ();
+ if (GET_CODE (XEXP (x, 0)) == REG)
+ x = gen_rtx (PLUS, PSImode, XEXP (x, 0), GEN_INT (0));
+ else
+ x = XEXP (x, 0);
+ fputc ('(', file);
+ output_address (x);
+ fputc (')', file);
+ break;
+
+ case 'Z':
+ print_operand (file, XEXP (x, 1), 0);
+ break;
+
+ /* More cases where we can sign-extend a CONST_INT if it
+ results in more compact code. */
+ case 's':
+ case 'S':
+ if (GET_CODE (x) == CONST_INT)
+ {
+ int val = INTVAL (x);
+
+ if (code == 's')
+ x = GEN_INT (((val & 0xffff) ^ (~0x7fff)) + 0x8000);
+ else
+ x = GEN_INT (((val & 0xff) ^ (~0x7f)) + 0x80);
+ }
+ /* FALL THROUGH */
+ default:
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ fputc ('(', file);
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ break;
+
+ case SUBREG:
+ fprintf (file, "%s",
+ reg_names[REGNO (SUBREG_REG (x)) + SUBREG_WORD (x)]);
+ break;
+
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CONST:
+ case LABEL_REF:
+ case CODE_LABEL:
+ print_operand_address (file, x);
+ break;
+ default:
+ abort ();
+ }
+ break;
+ }
+}
+
+/* Output assembly language output for the address ADDR to FILE. */
+
+void
+print_operand_address (file, addr)
+ FILE *file;
+ rtx addr;
+{
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ print_operand (file, addr, 0);
+ break;
+ case PLUS:
+ {
+ rtx base, index;
+ /* The base and index could be in any order, so we have
+ to figure out which is the base and which is the index.
+ Uses the same code as GO_IF_LEGITIMATE_ADDRESS. */
+ if (REG_P (XEXP (addr, 0))
+ && REG_OK_FOR_BASE_P (XEXP (addr, 0)))
+ base = XEXP (addr, 0), index = XEXP (addr, 1);
+ else if (REG_P (XEXP (addr, 1))
+ && REG_OK_FOR_BASE_P (XEXP (addr, 1)))
+ base = XEXP (addr, 1), index = XEXP (addr, 0);
+ else
+ abort ();
+ print_operand (file, index, 0);
+ fputc (',', file);
+ print_operand (file, base, 0);;
+ break;
+ }
+ case SYMBOL_REF:
+ output_addr_const (file, addr);
+ break;
+ default:
+ output_addr_const (file, addr);
+ break;
+ }
+}
+
+/* Count the number of tst insns which compare an address register
+ with zero. */
+static void
+count_tst_insns (areg_countp)
+ int *areg_countp;
+{
+ rtx insn;
+
+ /* Assume no tst insns exist. */
+ *areg_countp = 0;
+
+ /* If not optimizing, then quit now. */
+ if (!optimize)
+ return;
+
+ /* Walk through all the insns. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ rtx pat;
+
+ /* Ignore anything that is not a normal INSN. */
+ if (GET_CODE (insn) != INSN)
+ continue;
+
+ /* Ignore anything that isn't a SET. */
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) != SET)
+ continue;
+
+ /* Check for a tst insn. */
+ if (SET_DEST (pat) == cc0_rtx
+ && GET_CODE (SET_SRC (pat)) == REG
+ && REGNO_REG_CLASS (REGNO (SET_SRC (pat))) == ADDRESS_REGS)
+ (*areg_countp)++;
+ }
+}
+
+/* Return the total size (in bytes) of the current function's frame.
+ This is the size of the register save area + the size of locals,
+ spills, etc. */
+int
+total_frame_size ()
+{
+ unsigned int size = get_frame_size ();
+ unsigned int outgoing_args_size = current_function_outgoing_args_size;
+ int i;
+
+ /* First figure out if we're going to use an out of line
+ prologue, if so we have to make space for all the
+ registers, even if we don't use them. */
+ if (optimize && !current_function_needs_context && !frame_pointer_needed)
+ {
+ int inline_count, outline_count;
+
+ /* Compute how many bytes an inline prologue would take.
+
+ Each address register store takes two bytes, each data register
+ store takes three bytes. */
+ inline_count = 0;
+ if (regs_ever_live[5])
+ inline_count += 2;
+ if (regs_ever_live[6])
+ inline_count += 2;
+ if (regs_ever_live[2])
+ inline_count += 3;
+ if (regs_ever_live[3])
+ inline_count += 3;
+
+ /* If this function has any stack, then the stack adjustment
+ will take two (or more) bytes. */
+ if (size || outgoing_args_size
+ || regs_ever_live[5] || regs_ever_live[6]
+ || regs_ever_live[2] || regs_ever_live[3])
+ inline_count += 2;
+
+ /* Multiply the current count by two and add one to account for the
+ epilogue insns. */
+ inline_count = inline_count * 2 + 1;
+
+ /* Now compute how many bytes an out of line sequence would take. */
+ /* A relaxed jsr will be three bytes. */
+ outline_count = 3;
+
+ /* If there are outgoing arguments, then we will need a stack
+ pointer adjustment after the call to the prologue, two
+ more bytes. */
+ outline_count += (outgoing_args_size == 0 ? 0 : 2);
+
+ /* If there is some local frame to allocate, it will need to be
+ done before the call to the prologue, two more bytes. */
+ if (get_frame_size () != 0)
+ outline_count += 2;
+
+ /* Now account for the epilogue, multiply the base count by two,
+ then deal with optimizing away the rts instruction. */
+ outline_count = outline_count * 2 + 1;
+
+ if (get_frame_size () == 0 && outgoing_args_size == 0)
+ outline_count -= 1;
+
+ /* If an out of line prologue is smaller, use it. */
+ if (inline_count > outline_count)
+ return size + outgoing_args_size + 16;
+ }
+
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (regs_ever_live[i] && !call_used_regs[i] && ! fixed_regs[i]
+ || (i == FRAME_POINTER_REGNUM && frame_pointer_needed))
+ size += 4;
+ }
+
+ return (size + outgoing_args_size);
+}
+
+/* Expand the prologue into RTL. */
+void
+expand_prologue ()
+{
+ unsigned int size = total_frame_size ();
+ unsigned int outgoing_args_size = current_function_outgoing_args_size;
+ int offset, i;
+
+ zero_areg = NULL_RTX;
+ zero_dreg = NULL_RTX;
+
+ /* If optimizing, see if we should do an out of line prologue/epilogue
+ sequence.
+
+ We don't support out of line prologues if the current function
+ needs a context or frame pointer. */
+ if (optimize && !current_function_needs_context && !frame_pointer_needed)
+ {
+ int inline_count, outline_count, areg_count;
+
+ /* We need to end the current sequence so that count_tst_insns can
+ look at all the insns in this function. Normally this would be
+ unsafe, but it's OK in the prologue/epilogue expanders. */
+ end_sequence ();
+
+ /* Get a count of the number of tst insns which use address
+ registers (it's not profitable to try and improve tst insns
+ which use data registers). */
+ count_tst_insns (&areg_count);
+
+ /* Now start a new sequence. */
+ start_sequence ();
+
+ /* Compute how many bytes an inline prologue would take.
+
+ Each address register store takes two bytes, each data register
+ store takes three bytes. */
+ inline_count = 0;
+ if (regs_ever_live[5])
+ inline_count += 2;
+ if (regs_ever_live[6])
+ inline_count += 2;
+ if (regs_ever_live[2])
+ inline_count += 3;
+ if (regs_ever_live[3])
+ inline_count += 3;
+
+ /* If this function has any stack, then the stack adjustment
+ will take two (or more) bytes. */
+ if (size || outgoing_args_size
+ || regs_ever_live[5] || regs_ever_live[6]
+ || regs_ever_live[2] || regs_ever_live[3])
+ inline_count += 2;
+
+ /* Multiply the current count by two and add one to account for the
+ epilogue insns. */
+ inline_count = inline_count * 2 + 1;
+
+ /* Now compute how many bytes an out of line sequence would take. */
+ /* A relaxed jsr will be three bytes. */
+ outline_count = 3;
+
+ /* If there are outgoing arguments, then we will need a stack
+ pointer adjustment after the call to the prologue, two
+ more bytes. */
+ outline_count += (outgoing_args_size == 0 ? 0 : 2);
+
+ /* If there is some local frame to allocate, it will need to be
+ done before the call to the prologue, two more bytes. */
+ if (get_frame_size () != 0)
+ outline_count += 2;
+
+ /* Now account for the epilogue, multiply the base count by two,
+ then deal with optimizing away the rts instruction. */
+ outline_count = outline_count * 2 + 1;
+
+ if (get_frame_size () == 0 && outgoing_args_size == 0)
+ outline_count -= 1;
+
+ /* If an out of line prologue is smaller, use it. */
+ if (inline_count > outline_count)
+ {
+ if (get_frame_size () != 0)
+ emit_insn (gen_addpsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-size + outgoing_args_size + 16)));
+ emit_insn (gen_outline_prologue_call ());
+
+ if (outgoing_args_size)
+ emit_insn (gen_addpsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-outgoing_args_size)));
+
+ out_of_line_epilogue = 1;
+
+ /* Determine if it is profitable to put the value zero into a register
+ for the entire function. If so, set ZERO_DREG and ZERO_AREG. */
+
+ /* First see if we could load the value into a data register
+ since that's the most efficient way. */
+ if (areg_count > 1
+ && (!regs_ever_live[2] || !regs_ever_live[3]))
+ {
+ if (!regs_ever_live[2])
+ {
+ regs_ever_live[2] = 1;
+ zero_dreg = gen_rtx (REG, HImode, 2);
+ }
+ if (!regs_ever_live[3])
+ {
+ regs_ever_live[3] = 1;
+ zero_dreg = gen_rtx (REG, HImode, 3);
+ }
+ }
+
+ /* Now see if we could load the value into a address register. */
+ if (zero_dreg == NULL_RTX
+ && areg_count > 2
+ && (!regs_ever_live[5] || !regs_ever_live[6]))
+ {
+ if (!regs_ever_live[5])
+ {
+ regs_ever_live[5] = 1;
+ zero_dreg = gen_rtx (REG, HImode, 5);
+ }
+ if (!regs_ever_live[6])
+ {
+ regs_ever_live[6] = 1;
+ zero_dreg = gen_rtx (REG, HImode, 6);
+ }
+ }
+
+ if (zero_dreg)
+ emit_move_insn (zero_dreg, const0_rtx);
+
+ if (zero_areg)
+ emit_move_insn (zero_areg, const0_rtx);
+
+ return;
+ }
+ }
+
+ out_of_line_epilogue = 0;
+
+ /* Temporarily stuff the static chain onto the stack so we can
+ use a0 as a scratch register during the prologue. */
+ if (current_function_needs_context)
+ {
+ emit_insn (gen_addpsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-4)));
+ emit_move_insn (gen_rtx (MEM, PSImode, stack_pointer_rtx),
+ gen_rtx (REG, PSImode, STATIC_CHAIN_REGNUM));
+ }
+
+ if (frame_pointer_needed)
+ {
+ /* Store a2 into a0 temporarily. */
+ emit_move_insn (gen_rtx (REG, PSImode, 4), frame_pointer_rtx);
+
+ /* Set up the frame pointer. */
+ emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+ }
+
+ /* Make any necessary space for the saved registers and local frame. */
+ if (size)
+ emit_insn (gen_addpsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-size)));
+
+ /* Save the callee saved registers. They're saved into the top
+ of the frame, using the stack pointer. */
+ for (i = 0, offset = outgoing_args_size;
+ i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (regs_ever_live[i] && !call_used_regs[i] && ! fixed_regs[i]
+ || (i == FRAME_POINTER_REGNUM && frame_pointer_needed))
+ {
+ int regno;
+
+ /* If we're saving the frame pointer, then it will be found in
+ register 4 (a0). */
+ regno = (i == FRAME_POINTER_REGNUM && frame_pointer_needed) ? 4 : i;
+
+ emit_move_insn (gen_rtx (MEM, PSImode,
+ gen_rtx (PLUS, Pmode,
+ stack_pointer_rtx,
+ GEN_INT (offset))),
+ gen_rtx (REG, PSImode, regno));
+ offset += 4;
+ }
+ }
+
+ /* Now put the static chain back where the rest of the function
+ expects to find it. */
+ if (current_function_needs_context)
+ {
+ emit_move_insn (gen_rtx (REG, PSImode, STATIC_CHAIN_REGNUM),
+ gen_rtx (MEM, PSImode,
+ gen_rtx (PLUS, PSImode, stack_pointer_rtx,
+ GEN_INT (size))));
+ }
+}
+
+/* Expand the epilogue into RTL. */
+void
+expand_epilogue ()
+{
+ unsigned int size;
+ unsigned int outgoing_args_size = current_function_outgoing_args_size;
+ int offset, i, temp_regno;
+ rtx basereg;
+
+ size = total_frame_size ();
+
+ if (DECL_RESULT (current_function_decl)
+ && DECL_RTL (DECL_RESULT (current_function_decl))
+ && REG_P (DECL_RTL (DECL_RESULT (current_function_decl))))
+ temp_regno = (REGNO (DECL_RTL (DECL_RESULT (current_function_decl))) == 4
+ ? 0 : 4);
+ else
+ temp_regno = 4;
+
+ /* Emit an out of line epilogue sequence if it's profitable to do so. */
+ if (out_of_line_epilogue)
+ {
+ /* If there were no outgoing arguments and no local frame, then
+ we will be able to omit the rts at the end of this function,
+ so just jump to the epilogue_noreturn routine. */
+ if (get_frame_size () == 0 && outgoing_args_size == 0)
+ {
+ emit_jump_insn (gen_outline_epilogue_jump ());
+ return;
+ }
+
+ if (outgoing_args_size)
+ emit_insn (gen_addpsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (outgoing_args_size)));
+
+ if (temp_regno == 0)
+ emit_insn (gen_outline_epilogue_call_d0 ());
+ else if (temp_regno == 4)
+ emit_insn (gen_outline_epilogue_call_a0 ());
+
+ if (get_frame_size () != 0)
+ emit_insn (gen_addpsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (size - outgoing_args_size - 16)));
+ emit_jump_insn (gen_return_internal ());
+ return;
+ }
+
+ /* Registers are restored from the frame pointer if we have one,
+ else they're restored from the stack pointer. Figure out
+ the appropriate offset to the register save area for both cases. */
+ if (frame_pointer_needed)
+ {
+ basereg = frame_pointer_rtx;
+ offset = -(size - outgoing_args_size);
+ }
+ else
+ {
+ basereg = stack_pointer_rtx;
+ offset = outgoing_args_size;
+ }
+
+ /* Restore each register. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (regs_ever_live[i] && !call_used_regs[i] && ! fixed_regs[i]
+ || (i == FRAME_POINTER_REGNUM && frame_pointer_needed))
+ {
+ int regno;
+
+ /* Restore the frame pointer (if it exists) into a temporary
+ register. */
+ regno = ((i == FRAME_POINTER_REGNUM && frame_pointer_needed)
+ ? temp_regno : i);
+
+ emit_move_insn (gen_rtx (REG, PSImode, regno),
+ gen_rtx (MEM, PSImode,
+ gen_rtx (PLUS, Pmode,
+ basereg,
+ GEN_INT (offset))));
+ offset += 4;
+ }
+ }
+
+ if (frame_pointer_needed)
+ {
+ /* Deallocate this frame's stack. */
+ emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
+ /* Restore the old frame pointer. */
+ emit_move_insn (frame_pointer_rtx, gen_rtx (REG, PSImode, temp_regno));
+ }
+ else if (size)
+ {
+ /* Deallocate this function's stack. */
+ emit_insn (gen_addpsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (size)));
+ }
+
+ /* If we had to allocate a slot to save the context pointer,
+ then it must be deallocated here. */
+ if (current_function_needs_context)
+ emit_insn (gen_addpsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (4)));
+
+ /* Emit the return insn, if this function had no stack, then we
+ can use the standard return (which allows more optimizations),
+ else we have to use the special one which inhibits optimizations. */
+ if (size == 0 && !current_function_needs_context)
+ emit_jump_insn (gen_return ());
+ else
+ emit_jump_insn (gen_return_internal ());
+}
+
+/* Update the condition code from the insn. */
+
+void
+notice_update_cc (body, insn)
+ rtx body;
+ rtx insn;
+{
+ switch (get_attr_cc (insn))
+ {
+ case CC_NONE:
+ /* Insn does not affect CC at all. */
+ break;
+
+ case CC_NONE_0HIT:
+ /* Insn does not change CC, but the 0'th operand has been changed. */
+ if (cc_status.value1 != 0
+ && reg_overlap_mentioned_p (recog_operand[0], cc_status.value1))
+ cc_status.value1 = 0;
+ break;
+
+ case CC_SET_ZN:
+ /* Insn sets the Z,N flags of CC to recog_operand[0].
+ V,C is in an unusable state. */
+ CC_STATUS_INIT;
+ cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY;
+ cc_status.value1 = recog_operand[0];
+ break;
+
+ case CC_SET_ZNV:
+ /* Insn sets the Z,N,V flags of CC to recog_operand[0].
+ C is in an unusable state. */
+ CC_STATUS_INIT;
+ cc_status.flags |= CC_NO_CARRY;
+ cc_status.value1 = recog_operand[0];
+ break;
+
+ case CC_COMPARE:
+ /* The insn is a compare instruction. */
+ CC_STATUS_INIT;
+ cc_status.value1 = SET_SRC (body);
+ break;
+
+ case CC_CLOBBER:
+ /* Insn doesn't leave CC in a usable state. */
+ CC_STATUS_INIT;
+ break;
+
+ default:
+ CC_STATUS_INIT;
+ break;
+ }
+}
+
+/* Return true if OP is a valid call operand. Valid call operands
+ are SYMBOL_REFs and REGs. */
+int
+call_address_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG);
+}
+
+/* Return true if OP is an indirect memory operand, the "bset" and "bclr"
+ insns use this predicate. */
+int
+indirect_memory_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == REG);
+}
+
+/* Return true if OP is a memory operand with a constant address.
+ A special PSImode move pattern uses this predicate. */
+int
+constant_memory_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return GET_CODE (op) == MEM && CONSTANT_ADDRESS_P (XEXP (op, 0));
+}
+
+/* What (if any) secondary registers are needed to move IN with mode
+ MODE into a register from in register class CLASS.
+
+ We might be able to simplify this. */
+enum reg_class
+secondary_reload_class (class, mode, in, input)
+ enum reg_class class;
+ enum machine_mode mode;
+ rtx in;
+ int input;
+{
+ int regno;
+
+ /* Memory loads less than a full word wide can't have an
+ address or stack pointer destination. They must use
+ a data register as an intermediate register. */
+ if (input
+ && GET_CODE (in) == MEM
+ && (mode == QImode)
+ && class == ADDRESS_REGS)
+ return DATA_REGS;
+
+ /* Address register stores which are not PSImode need a scrach register. */
+ if (! input
+ && GET_CODE (in) == MEM
+ && (mode != PSImode)
+ && class == ADDRESS_REGS)
+ return DATA_REGS;
+
+ /* Otherwise assume no secondary reloads are needed. */
+ return NO_REGS;
+}
+
+
+/* Shifts.
+
+ We devote a fair bit of code to getting efficient shifts since we can only
+ shift one bit at a time, and each single bit shift may take multiple
+ instructions.
+
+ The basic shift methods:
+
+ * loop shifts -- emit a loop using one (or two on H8/S) bit shifts;
+ this is the default. SHIFT_LOOP
+
+ * inlined shifts -- emit straight line code for the shift; this is
+ used when a straight line shift is about the same size or smaller
+ than a loop. We allow the inline version to be slightly longer in
+ some cases as it saves a register. SHIFT_INLINE
+
+ * There other oddballs. Not worth explaining. SHIFT_SPECIAL
+
+
+ HImode shifts:
+
+ 1-4 do them inline
+
+ 5-7 If ashift, then multiply, else loop.
+
+ 8-14 - If ashift, then multiply, if lshiftrt, then divide, else loop.
+ 15 - rotate the bit we want into the carry, clear the destination,
+ (use mov 0,dst, not sub as sub will clobber the carry), then
+ move bit into place.
+
+ Don't Panic, it's not nearly as bad as the H8 shifting code!!! */
+
+int
+nshift_operator (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ switch (GET_CODE (x))
+ {
+ case ASHIFTRT:
+ case LSHIFTRT:
+ case ASHIFT:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Called from the .md file to emit code to do shifts.
+ Returns a boolean indicating success
+ (currently this is always TRUE). */
+
+int
+expand_a_shift (mode, code, operands)
+ enum machine_mode mode;
+ int code;
+ rtx operands[];
+{
+ emit_move_insn (operands[0], operands[1]);
+
+ /* need a loop to get all the bits we want - we generate the
+ code at emit time, but need to allocate a scratch reg now */
+
+ emit_insn (gen_rtx
+ (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode, operands[0],
+ gen_rtx (code, mode,
+ operands[0], operands[2])),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0)))));
+
+ return 1;
+}
+
+/* Shift algorithm determination.
+
+ There are various ways of doing a shift:
+ SHIFT_INLINE: If the amount is small enough, just generate as many one-bit
+ shifts as we need.
+ SHIFT_SPECIAL: Hand crafted assembler.
+ SHIFT_LOOP: If the above methods fail, just loop. */
+
+enum shift_alg
+{
+ SHIFT_INLINE,
+ SHIFT_SPECIAL,
+ SHIFT_LOOP,
+ SHIFT_MAX
+};
+
+/* Symbols of the various shifts which can be used as indices. */
+
+enum shift_type
+ {
+ SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
+ };
+
+/* Symbols of the various modes which can be used as indices. */
+
+enum shift_mode
+ {
+ HIshift,
+ };
+
+/* For single bit shift insns, record assembler and what bits of the
+ condition code are valid afterwards (represented as various CC_FOO
+ bits, 0 means CC isn't left in a usable state). */
+
+struct shift_insn
+{
+ char *assembler;
+ int cc_valid;
+};
+
+/* Assembler instruction shift table.
+
+ These tables are used to look up the basic shifts.
+ They are indexed by cpu, shift_type, and mode.
+*/
+
+static const struct shift_insn shift_one[3][3] =
+{
+ {
+/* SHIFT_ASHIFT */
+ { "add\t%0,%0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY },
+ },
+/* SHIFT_LSHIFTRT */
+ {
+ { "lsr\t%0", CC_NO_CARRY },
+ },
+/* SHIFT_ASHIFTRT */
+ {
+ { "asr\t%0", CC_NO_CARRY },
+ },
+};
+
+/* Given CPU, MODE, SHIFT_TYPE, and shift count COUNT, determine the best
+ algorithm for doing the shift. The assembler code is stored in ASSEMBLER.
+ We don't achieve maximum efficiency in all cases, but the hooks are here
+ to do so.
+
+ For now we just use lots of switch statements. Since we don't even come
+ close to supporting all the cases, this is simplest. If this function ever
+ gets too big, perhaps resort to a more table based lookup. Of course,
+ at this point you may just wish to do it all in rtl. */
+
+static enum shift_alg
+get_shift_alg (shift_type, mode, count, assembler_p, cc_valid_p)
+ enum shift_type shift_type;
+ enum machine_mode mode;
+ int count;
+ const char **assembler_p;
+ int *cc_valid_p;
+{
+ /* The default is to loop. */
+ enum shift_alg alg = SHIFT_LOOP;
+ enum shift_mode shift_mode;
+
+ /* We don't handle negative shifts or shifts greater than the word size,
+ they should have been handled already. */
+
+ if (count < 0 || count > GET_MODE_BITSIZE (mode))
+ abort ();
+
+ switch (mode)
+ {
+ case HImode:
+ shift_mode = HIshift;
+ break;
+ default:
+ abort ();
+ }
+
+ /* Assume either SHIFT_LOOP or SHIFT_INLINE.
+ It is up to the caller to know that looping clobbers cc. */
+ *assembler_p = shift_one[shift_type][shift_mode].assembler;
+ *cc_valid_p = shift_one[shift_type][shift_mode].cc_valid;
+
+ /* Now look for cases we want to optimize. */
+
+ switch (shift_mode)
+ {
+ case HIshift:
+ if (count <= 4)
+ return SHIFT_INLINE;
+ else if (count < 15 && shift_type != SHIFT_ASHIFTRT)
+ {
+ switch (count)
+ {
+ case 5:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 32,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 32,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 6:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 64,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 64,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 7:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 128,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 128,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 8:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 256,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 256,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 9:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 512,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 512,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 10:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 1024,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 1024,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 11:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 2048,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 2048,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 12:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 4096,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 4096,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 13:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 8192,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 8192,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ case 14:
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "mov 16384,%4\n\tmul %4,%0";
+ else if (shift_type == SHIFT_LSHIFTRT)
+ *assembler_p
+ = "sub %4,%4\n\tmov %4,mdr\n\tmov 16384,%4\n\tdivu %4,%0";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ }
+ }
+ else if (count == 15)
+ {
+ if (shift_type == SHIFT_ASHIFTRT)
+ {
+ *assembler_p = "add\t%0,%0\n\tsubc\t%0,%0\n";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ }
+ if (shift_type == SHIFT_LSHIFTRT)
+ {
+ *assembler_p = "add\t%0,%0\n\tmov 0,%0\n\trol %0\n";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ }
+ if (shift_type == SHIFT_ASHIFT)
+ {
+ *assembler_p = "ror\t%0\n\tmov 0,%0\n\tror %0\n";
+ *cc_valid_p = CC_NO_CARRY;
+ return SHIFT_SPECIAL;
+ }
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ return alg;
+}
+
+/* Emit the assembler code for doing shifts. */
+
+char *
+emit_a_shift (insn, operands)
+ rtx insn;
+ rtx *operands;
+{
+ static int loopend_lab;
+ char *assembler;
+ int cc_valid;
+ rtx inside = PATTERN (insn);
+ rtx shift = operands[3];
+ enum machine_mode mode = GET_MODE (shift);
+ enum rtx_code code = GET_CODE (shift);
+ enum shift_type shift_type;
+ enum shift_mode shift_mode;
+
+ loopend_lab++;
+
+ switch (mode)
+ {
+ case HImode:
+ shift_mode = HIshift;
+ break;
+ default:
+ abort ();
+ }
+
+ switch (code)
+ {
+ case ASHIFTRT:
+ shift_type = SHIFT_ASHIFTRT;
+ break;
+ case LSHIFTRT:
+ shift_type = SHIFT_LSHIFTRT;
+ break;
+ case ASHIFT:
+ shift_type = SHIFT_ASHIFT;
+ break;
+ default:
+ abort ();
+ }
+
+ if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ /* Indexing by reg, so have to loop and test at top */
+ output_asm_insn ("mov %2,%4", operands);
+ output_asm_insn ("cmp 0,%4", operands);
+ fprintf (asm_out_file, "\tble .Lle%d\n", loopend_lab);
+
+ /* Get the assembler code to do one shift. */
+ get_shift_alg (shift_type, mode, 1, &assembler, &cc_valid);
+ }
+ else
+ {
+ int n = INTVAL (operands[2]);
+ enum shift_alg alg;
+
+ /* If the count is negative, make it 0. */
+ if (n < 0)
+ n = 0;
+ /* If the count is too big, truncate it.
+ ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
+ do the intuitive thing. */
+ else if (n > GET_MODE_BITSIZE (mode))
+ n = GET_MODE_BITSIZE (mode);
+
+ alg = get_shift_alg (shift_type, mode, n, &assembler, &cc_valid);
+
+
+ switch (alg)
+ {
+ case SHIFT_INLINE:
+ /* Emit one bit shifts. */
+ while (n > 0)
+ {
+ output_asm_insn (assembler, operands);
+ n -= 1;
+ }
+
+ /* Keep track of CC. */
+ if (cc_valid)
+ {
+ cc_status.value1 = operands[0];
+ cc_status.flags |= cc_valid;
+ }
+ return "";
+
+ case SHIFT_SPECIAL:
+ output_asm_insn (assembler, operands);
+
+ /* Keep track of CC. */
+ if (cc_valid)
+ {
+ cc_status.value1 = operands[0];
+ cc_status.flags |= cc_valid;
+ }
+ return "";
+ }
+
+ {
+ fprintf (asm_out_file, "\tmov %d,%s\n", n,
+ reg_names[REGNO (operands[4])]);
+ fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
+ output_asm_insn (assembler, operands);
+ output_asm_insn ("add -1,%4", operands);
+ fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
+ return "";
+ }
+ }
+
+ fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
+ output_asm_insn (assembler, operands);
+ output_asm_insn ("add -1,%4", operands);
+ fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
+ fprintf (asm_out_file, ".Lle%d:\n", loopend_lab);
+
+ return "";
+}
+
+/* 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;
+
+ /* Only pass named arguments in registers. */
+ if (!named)
+ return NULL_RTX;
+
+ /* Figure out the size of the object to be passed. We lie and claim
+ PSImode values are only two bytes since they fit in a single
+ register. */
+ if (mode == BLKmode)
+ size = int_size_in_bytes (type);
+ else if (mode == PSImode)
+ size = 2;
+ else
+ size = GET_MODE_SIZE (mode);
+
+ /* Figure out the alignment of the object to be passed. */
+ align = size;
+
+ cum->nbytes = (cum->nbytes + 1) & ~1;
+
+ /* Don't pass this arg via a register if all the argument registers
+ are used up. */
+ if (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;
+
+ return 0;
+ /* Only pass named arguments in registers. */
+ if (!named)
+ return 0;
+
+ /* Figure out the size of the object to be passed. */
+ if (mode == BLKmode)
+ size = int_size_in_bytes (type);
+ else if (mode == PSImode)
+ size = 2;
+ else
+ size = GET_MODE_SIZE (mode);
+
+ /* Figure out the alignment of the object to be passed. */
+ align = size;
+
+ cum->nbytes = (cum->nbytes + 1) & ~1;
+
+ /* 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;
+}
+
+char *
+output_tst (operand, insn)
+ rtx operand, insn;
+{
+
+ rtx temp;
+ int past_call = 0;
+
+ /* Only tst insns using address registers can be optimized. */
+ if (REGNO_REG_CLASS (REGNO (operand)) != ADDRESS_REGS)
+ return "cmp 0,%0";
+
+ /* If testing an address register against zero, we can do better if
+ we know there's a register already holding the value zero. First
+ see if a global register has been set to zero, else we do a search
+ for a register holding zero, if both of those fail, then we use a
+ compare against zero. */
+ if (zero_dreg || zero_areg)
+ {
+ rtx xoperands[2];
+ xoperands[0] = operand;
+ xoperands[1] = zero_dreg ? zero_dreg : zero_areg;
+
+ output_asm_insn ("cmp %1,%0", xoperands);
+ return "";
+ }
+
+ /* 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 register to zero?
+
+ If it's a call clobbered register, have we past a call? */
+ 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)
+ && (!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";
+}
+
+/* Return nonzero if OP is a valid operand for a {zero,sign}_extendpsisi
+ instruction.
+
+ It accepts anything that is a general operand or the sum of the
+ stack pointer and a general operand. */
+extendpsi_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (general_operand (op, mode)
+ || (GET_CODE (op) == PLUS
+ && XEXP (op, 0) == stack_pointer_rtx
+ && general_operand (XEXP (op, 1), VOIDmode)));
+}