/* Subroutines used for code generation on intel 80960.
- Copyright (C) 1992 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
Contributed by Steven McGeady, Intel Corp.
Additional Work by Glenn Colon-Bonet, Jonathan Shapiro, Andy Wilson
Converted to GCC 2.0 by Jim Wilson and Michael Tiemann, Cygnus Support.
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, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-#include <stdio.h>
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
#include "config.h"
+#include <stdio.h>
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "tree.h"
#include "insn-codes.h"
-#include "assert.h"
#include "expr.h"
+#include "except.h"
#include "function.h"
#include "recog.h"
#include <math.h>
static int ret_label = 0;
-#if 0
+/* This is true if FNDECL is either a varargs or a stdarg function.
+ This is used to help identify functions that use an argument block. */
+
+#define VARARGS_STDARG_FUNCTION(FNDECL) \
+((TYPE_ARG_TYPES (TREE_TYPE (FNDECL)) != 0 \
+ && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (TREE_TYPE (FNDECL)))) != void_type_node)) \
+ || current_function_varargs)
+
/* Handle pragmas for compatibility with Intel's compilers. */
/* ??? This is incomplete, since it does not handle all pragmas that the
- intel compilers understand. Also, it needs to be rewritten to accept
- a stream instead of a string for GCC 2. */
+ intel compilers understand. */
-void
-process_pragma(str)
- char *str;
+int
+process_pragma (finput, t)
+ FILE *finput;
+ tree t;
{
- int align;
int i;
+ register int c;
+ register char *pname;
- if ((i = sscanf (str, " align %d", &align)) == 1)
- switch (align)
- {
- case 0: /* Return to last alignment. */
- align = i960_last_maxbitalignment / 8;
-
- case 16: /* Byte alignments. */
- case 8:
- case 4:
- case 2:
- case 1:
- i960_last_maxbitalignment = i960_maxbitalignment;
- i960_maxbitalignment = align * 8;
- break;
-
- default: /* Unknown, silently ignore. */
- break;
- }
+ if (TREE_CODE (t) != IDENTIFIER_NODE)
+ return 0;
- /* NOTE: ic960 R3.0 pragma align definition:
+ pname = IDENTIFIER_POINTER (t);
- #pragma align [(size)] | (identifier=size[,...])
- #pragma noalign [(identifier)[,...]]
+ if (strcmp (pname, "align") == 0)
+ {
+ char buf[20];
+ char *s = buf;
+ int align;
- (all parens are optional)
+ do {
+ c = getc (finput);
+ } while (c == ' ' || c == '\t');
- - size is [1,2,4,8,16]
- - noalign means size==1
- - applies only to component elements of a struct (and union?)
- - identifier applies to structure tag (only)
- - missing identifier means next struct
+ if (c == '(')
+ c = getc (finput);
+ while (c >= '0' && c <= '9')
+ {
+ if (s < buf + sizeof buf - 1)
+ *s++ = c;
+ c = getc (finput);
+ }
+ *s = '\0';
- - alignment rules for bitfields need more investigation */
+ /* We had to read a non-numerical character to get out of the
+ while loop---often a newline. So, we have to put it back to
+ make sure we continue to parse everything properly. */
+ ungetc (c, finput);
+
+ align = atoi (buf);
+ switch (align)
+ {
+ case 0:
+ /* Return to last alignment. */
+ align = i960_last_maxbitalignment / 8;
+ /* Fall through. */
+ case 16:
+ case 8:
+ case 4:
+ case 2:
+ case 1:
+ i960_last_maxbitalignment = i960_maxbitalignment;
+ i960_maxbitalignment = align * 8;
+ break;
+
+ default:
+ /* Silently ignore bad values. */
+ break;
+ }
+
+ /* NOTE: ic960 R3.0 pragma align definition:
+
+ #pragma align [(size)] | (identifier=size[,...])
+ #pragma noalign [(identifier)[,...]]
+
+ (all parens are optional)
+
+ - size is [1,2,4,8,16]
+ - noalign means size==1
+ - applies only to component elements of a struct (and union?)
+ - identifier applies to structure tag (only)
+ - missing identifier means next struct
+
+ - alignment rules for bitfields need more investigation */
+
+ return 1;
+ }
/* Should be pragma 'far' or equivalent for callx/balx here. */
+
+ return 0;
}
-#endif
/* Initialize variables before compiling any files. */
return (register_operand (op, mode) || literal (op, mode));
}
+/* Return truth value of whether OP can be used as an operands in a three
+ address logic insn, possibly complementing OP, of mode MODE. */
+
+int
+logic_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (register_operand (op, mode)
+ || (GET_CODE (op) == CONST_INT
+ && INTVAL(op) >= -32 && INTVAL(op) < 32));
+}
+
/* Return true if OP is a register or a valid floating point literal. */
int
rtx op;
enum machine_mode mode;
{
- return (TARGET_NUMERICS && (mode == VOIDmode || mode == GET_MODE (op))
- && (op == CONST1_RTX (mode)));
+ return (TARGET_NUMERICS && mode == GET_MODE (op) && op == CONST1_RTX (mode));
}
/* Return true if OP is a float constant of 0. */
rtx op;
enum machine_mode mode;
{
- return (TARGET_NUMERICS && (mode == VOIDmode || mode == GET_MODE (op))
- && (op == CONST0_RTX (mode)));
+ return (TARGET_NUMERICS && mode == GET_MODE (op) && op == CONST0_RTX (mode));
}
/* Return true if OP is a valid floating point literal. */
if (GET_CODE (x) == REG)
return 1;
#endif
+ /* This is a MEMA operand -- it's free. */
+ if (GET_CODE (x) == CONST_INT
+ && INTVAL (x) >= 0
+ && INTVAL (x) < 4096)
+ return 0;
+
if (GET_CODE (x) == PLUS)
{
rtx base = XEXP (x, 0);
rtx *operands;
enum machine_mode mode;
{
- register rtx operand0 = operands[0];
- register rtx operand1 = operands[1];
-
/* We can only store registers to memory. */
- if (GET_CODE (operand0) == MEM && GET_CODE (operand1) != REG)
- operands[1] = force_reg (mode, operand1);
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) != REG)
+ operands[1] = force_reg (mode, operands[1]);
+
+ /* Storing multi-word values in unaligned hard registers to memory may
+ require a scratch since we have to store them a register at a time and
+ adding 4 to the memory address may not yield a valid insn. */
+ /* ??? We don't always need the scratch, but that would complicate things.
+ Maybe later. */
+ /* ??? We must also handle stores to pseudos here, because the pseudo may be
+ replaced with a MEM later. This would be cleaner if we didn't have
+ a separate pattern for unaligned DImode/TImode stores. */
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && (GET_CODE (operands[0]) == MEM
+ || (GET_CODE (operands[0]) == REG
+ && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER))
+ && GET_CODE (operands[1]) == REG
+ && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && ! HARD_REGNO_MODE_OK (REGNO (operands[1]), mode))
+ {
+ emit_insn (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operands[0], operands[1]),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, Pmode)))));
+ return 1;
+ }
return 0;
}
+
+/* Output assembler to move a double word value. */
+
+char *
+i960_output_move_double (dst, src)
+ rtx dst, src;
+{
+ rtx operands[5];
+
+ if (GET_CODE (dst) == REG
+ && GET_CODE (src) == REG)
+ {
+ if ((REGNO (src) & 1)
+ || (REGNO (dst) & 1))
+ {
+ /* We normally copy the low-numbered register first. However, if
+ the second source register is the same as the first destination
+ register, we must copy in the opposite order. */
+ if (REGNO (src) + 1 == REGNO (dst))
+ return "mov %D1,%D0\n\tmov %1,%0";
+ else
+ return "mov %1,%0\n\tmov %D1,%D0";
+ }
+ else
+ return "movl %1,%0";
+ }
+ else if (GET_CODE (dst) == REG
+ && GET_CODE (src) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (src), 'I'))
+ {
+ if (REGNO (dst) & 1)
+ return "mov %1,%0\n\tmov 0,%D0";
+ else
+ return "movl %1,%0";
+ }
+ else if (GET_CODE (dst) == REG
+ && GET_CODE (src) == MEM)
+ {
+ if (REGNO (dst) & 1)
+ {
+ /* One can optimize a few cases here, but you have to be
+ careful of clobbering registers used in the address and
+ edge conditions. */
+ operands[0] = dst;
+ operands[1] = src;
+ operands[2] = gen_rtx (REG, Pmode, REGNO (dst) + 1);
+ operands[3] = gen_rtx (MEM, word_mode, operands[2]);
+ operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD);
+ output_asm_insn ("lda %1,%2\n\tld %3,%0\n\tld %4,%D0", operands);
+ return "";
+ }
+ else
+ return "ldl %1,%0";
+ }
+ else if (GET_CODE (dst) == MEM
+ && GET_CODE (src) == REG)
+ {
+ if (REGNO (src) & 1)
+ {
+ /* This is handled by emit_move_sequence so we shouldn't get here. */
+ abort ();
+ }
+ return "stl %1,%0";
+ }
+ else
+ abort ();
+}
+
+/* Output assembler to move a quad word value. */
+
+char *
+i960_output_move_quad (dst, src)
+ rtx dst, src;
+{
+ rtx operands[7];
+
+ if (GET_CODE (dst) == REG
+ && GET_CODE (src) == REG)
+ {
+ if ((REGNO (src) & 3)
+ || (REGNO (dst) & 3))
+ {
+ /* We normally copy starting with the low numbered register.
+ However, if there is an overlap such that the first dest reg
+ is <= the last source reg but not < the first source reg, we
+ must copy in the opposite order. */
+ if (REGNO (dst) <= REGNO (src) + 3
+ && REGNO (dst) >= REGNO (src))
+ return "mov %F1,%F0\n\tmov %E1,%E0\n\tmov %D1,%D0\n\tmov %1,%0";
+ else
+ return "mov %1,%0\n\tmov %D1,%D0\n\tmov %E1,%E0\n\tmov %F1,%F0";
+ }
+ else
+ return "movq %1,%0";
+ }
+ else if (GET_CODE (dst) == REG
+ && GET_CODE (src) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (src), 'I'))
+ {
+ if (REGNO (dst) & 3)
+ return "mov %1,%0\n\tmov 0,%D0\n\tmov 0,%E0\n\tmov 0,%F0";
+ else
+ return "movq %1,%0";
+ }
+ else if (GET_CODE (dst) == REG
+ && GET_CODE (src) == MEM)
+ {
+ if (REGNO (dst) & 3)
+ {
+ /* One can optimize a few cases here, but you have to be
+ careful of clobbering registers used in the address and
+ edge conditions. */
+ operands[0] = dst;
+ operands[1] = src;
+ operands[2] = gen_rtx (REG, Pmode, REGNO (dst) + 3);
+ operands[3] = gen_rtx (MEM, word_mode, operands[2]);
+ operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD);
+ operands[5] = adj_offsettable_operand (operands[4], UNITS_PER_WORD);
+ operands[6] = adj_offsettable_operand (operands[5], UNITS_PER_WORD);
+ output_asm_insn ("lda %1,%2\n\tld %3,%0\n\tld %4,%D0\n\tld %5,%E0\n\tld %6,%F0", operands);
+ return "";
+ }
+ else
+ return "ldq %1,%0";
+ }
+ else if (GET_CODE (dst) == MEM
+ && GET_CODE (src) == REG)
+ {
+ if (REGNO (src) & 3)
+ {
+ /* This is handled by emit_move_sequence so we shouldn't get here. */
+ abort ();
+ }
+ return "stq %1,%0";
+ }
+ else
+ abort ();
+}
\f
-/* Emit insns to load a constant. Uses several strategies to try to use
- as few insns as possible. */
+/* Emit insns to load a constant to non-floating point registers.
+ Uses several strategies to try to use as few insns as possible. */
char *
i960_output_ldconst (dst, src)
register unsigned rsrc2;
enum machine_mode mode = GET_MODE (dst);
rtx operands[4];
- union { long l[2]; double d; } x;
operands[0] = operands[2] = dst;
operands[1] = operands[3] = src;
output_asm_insn ("ldconst %1,%0", operands);
return "";
}
- else if (mode == DFmode)
+ else if (mode == XFmode)
{
- rtx first, second;
+ REAL_VALUE_TYPE d;
+ long value_long[3];
+ int i;
+
+ if (fp_literal_zero (src, XFmode))
+ return "movt 0,%0";
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, src);
+ REAL_VALUE_TO_TARGET_LONG_DOUBLE (d, value_long);
+
+ output_asm_insn ("# ldconst %1,%0",operands);
- if (fp_literal_zero (src, VOIDmode))
+ for (i = 0; i < 3; i++)
{
- if (FP_REG_P (dst))
- return "movrl %1,%0";
- else
- return "movl 0,%0";
+ operands[0] = gen_rtx (REG, SImode, REGNO (dst) + i);
+ operands[1] = GEN_INT (value_long[i]);
+ output_asm_insn (i960_output_ldconst (operands[0], operands[1]),
+ operands);
}
-#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+ return "";
+ }
+ else if (mode == DFmode)
+ {
+ rtx first, second;
+
+ if (fp_literal_zero (src, DFmode))
+ return "movl 0,%0";
+
split_double (src, &first, &second);
output_asm_insn ("# ldconst %1,%0",operands);
output_asm_insn (i960_output_ldconst (operands[0], operands[1]),
operands);
return "";
-#else
- if (fp_literal_one (src, VOIDmode))
- return "movrl 0f1.0,%0";
- fatal ("inline double constants not supported on this host");
-#endif
+ }
+ else if (mode == SFmode)
+ {
+ REAL_VALUE_TYPE d;
+ long value;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, src);
+ REAL_VALUE_TO_TARGET_SINGLE (d, value);
+
+ output_asm_insn ("# ldconst %1,%0",operands);
+ operands[0] = gen_rtx (REG, SImode, REGNO (dst));
+ operands[1] = GEN_INT (value);
+ output_asm_insn (i960_output_ldconst (operands[0], operands[1]),
+ operands);
+ return "";
}
else if (mode == TImode)
{
else if (mode == DImode)
{
rtx upperhalf, lowerhalf, xoperands[2];
- char *string;
- if (GET_CODE (src) == CONST_DOUBLE)
- {
- upperhalf = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (src));
- lowerhalf = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (src));
- }
- else if (GET_CODE (src) == CONST_INT)
- {
- lowerhalf = src;
- upperhalf = INTVAL (src) < 0 ? constm1_rtx : const0_rtx;
- }
+ if (GET_CODE (src) == CONST_DOUBLE || GET_CODE (src) == CONST_INT)
+ split_double (src, &lowerhalf, &upperhalf);
+
else
abort ();
xoperands);
/* The lower word is emitted as normally. */
}
- else if (mode == SFmode)
- {
-#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
- REAL_VALUE_TYPE d;
- long value;
-
- REAL_VALUE_FROM_CONST_DOUBLE (d, src);
- REAL_VALUE_TO_TARGET_SINGLE (d, value);
-
- output_asm_insn ("# ldconst %1,%0",operands);
- operands[0] = gen_rtx (REG, SImode, REGNO (dst));
- operands[1] = gen_rtx (CONST_INT, VOIDmode, value);
- output_asm_insn (i960_output_ldconst (operands[0], operands[1]),
- operands);
-#else
- if (fp_literal_zero (src, VOIDmode))
- return "movr 0f0.0,%0";
- if (fp_literal_one (src, VOIDmode))
- return "movr 0f1.0,%0";
- fatal ("inline float constants not supported on this host");
-#endif
- return "";
- }
else
{
rsrc1 = INTVAL (src);
{
if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES)
return "lda %1,%0";
- operands[1] = gen_rtx (CONST_INT, VOIDmode, rsrc1 - 31);
+ operands[1] = GEN_INT (rsrc1 - 31);
output_asm_insn ("addo\t31,%1,%0\t# ldconst %3,%0", operands);
return "";
}
if (rsrc1 >= -31)
{
/* return 'sub -(%1),0,%0' */
- operands[1] = gen_rtx (CONST_INT, VOIDmode, - rsrc1);
+ operands[1] = GEN_INT (- rsrc1);
output_asm_insn ("subo\t%1,0,%0\t# ldconst %3,%0", operands);
return "";
}
/* ldconst -32 -> not 31,X */
if (rsrc1 == -32)
{
- operands[1] = gen_rtx (CONST_INT, VOIDmode, ~rsrc1);
+ operands[1] = GEN_INT (~rsrc1);
output_asm_insn ("not\t%1,%0 # ldconst %3,%0", operands);
return "";
}
/* If const is a single bit. */
if (bitpos (rsrc1) >= 0)
{
- operands[1] = gen_rtx (CONST_INT, VOIDmode, bitpos (rsrc1));
+ operands[1] = GEN_INT (bitpos (rsrc1));
output_asm_insn ("setbit\t%1,0,%0\t# ldconst %3,%0", operands);
return "";
}
if (bitstr (rsrc1, &s, &e) < 6)
{
rsrc2 = ((unsigned int) rsrc1) >> s;
- operands[1] = gen_rtx (CONST_INT, VOIDmode, rsrc2);
- operands[2] = gen_rtx (CONST_INT, VOIDmode, s);
+ operands[1] = GEN_INT (rsrc2);
+ operands[2] = GEN_INT (s);
output_asm_insn ("shlo\t%2,%1,%0\t# ldconst %3,%0", operands);
return "";
}
else
leaf_proc_ok = 0;
- /* Even if nobody uses extra parms, can't have leafroc or tail calls if
+ /* Even if nobody uses extra parms, can't have leafproc or tail calls if
argblock, because argblock uses g14 implicitly. */
- if (current_function_args_size != 0)
+ if (current_function_args_size != 0 || VARARGS_STDARG_FUNCTION (fndecl))
{
tail_call_ok = 0;
leaf_proc_ok = 0;
/* Do this after choosing the leaf return register, so it will be listed
if one was chosen. */
- fprintf (file, "\t# Function '%s'\n", name);
+ fprintf (file, "\t# Function '%s'\n", (name[0] == '*' ? &name[1] : name));
fprintf (file, "\t# Registers used: ");
for (i = 0, j = 0; i < FIRST_PSEUDO_REGISTER; i++)
/* Make it a leaf procedure. */
if (TREE_PUBLIC (fndecl))
- fprintf (file,"\t.globl %s.lf\n", name);
+ fprintf (file,"\t.globl\t%s.lf\n", (name[0] == '*' ? &name[1] : name));
- fprintf (file, "\t.leafproc\t_%s,%s.lf\n", name, name);
- fprintf (file, "_%s:\n", name);
+ fprintf (file, "\t.leafproc\t");
+ assemble_name (file, name);
+ fprintf (file, ",%s.lf\n", (name[0] == '*' ? &name[1] : name));
+ ASM_OUTPUT_LABEL (file, name);
fprintf (file, "\tlda LR%d,g14\n", ret_label);
- fprintf (file, "%s.lf:\n", name);
+ fprintf (file, "%s.lf:\n", (name[0] == '*' ? &name[1] : name));
fprintf (file, "\tmov g14,g%d\n", i960_leaf_ret_reg);
if (TARGET_C_SERIES)
int size;
{
int actual_fsize;
- int outgoing_args_size
- = current_function_outgoing_args_size + current_function_pretend_args_size;
+ int outgoing_args_size = current_function_outgoing_args_size;
/* The STARTING_FRAME_OFFSET is totally hidden to us as far
as size is concerned. */
epilogue_string[0] = '\0';
+ if (profile_flag || profile_block_flag)
+ {
+ /* When profiling, we may use registers 20 to 27 to save arguments, so
+ they can't be used here for saving globals. J is the number of
+ argument registers the mcount call will save. */
+ for (j = 7; j >= 0 && ! regs_ever_live[j]; j--)
+ ;
+
+ for (i = 20; i <= j + 20; i++)
+ regs[i] = -1;
+ }
+
/* First look for local registers to save globals in. */
for (i = 0; i < 16; i++)
{
}
/* Take hardware register save area created by the call instruction
- into account. */
- offset = compute_frame_size (size) + 64;
+ into account, but store them before the argument block area. */
+ offset = 64 + actual_fsize - compute_frame_size (0) - rsize;
/* Save registers on stack if needed. */
for (i = 0, j = n_iregs; j > 0 && i < 16; i++)
{
fprintf (file, "\t#End Prologue#\n");
}
+/* Output code for the function profiler. */
+
+void
+output_function_profiler (file, labelno)
+ FILE *file;
+ int labelno;
+{
+ /* The last used parameter register. */
+ int last_parm_reg;
+ int i, j, increment;
+ int varargs_stdarg_function
+ = VARARGS_STDARG_FUNCTION (current_function_decl);
+
+ /* Figure out the last used parameter register. The proper thing to do
+ is to walk incoming args of the function. A function might have live
+ parameter registers even if it has no incoming args. Note that we
+ don't have to save parameter registers g8 to g11 because they are
+ call preserved. */
+
+ /* See also output_function_prologue, which tries to use local registers
+ for preserved call-saved global registers. */
+
+ for (last_parm_reg = 7;
+ last_parm_reg >= 0 && ! regs_ever_live[last_parm_reg];
+ last_parm_reg--)
+ ;
+
+ /* Save parameter registers in regs r4 (20) to r11 (27). */
+
+ for (i = 0, j = 4; i <= last_parm_reg; i += increment, j += increment)
+ {
+ if (i % 4 == 0 && (last_parm_reg - i) >= 3)
+ increment = 4;
+ else if (i % 4 == 0 && (last_parm_reg - i) >= 2)
+ increment = 3;
+ else if (i % 2 == 0 && (last_parm_reg - i) >= 1)
+ increment = 2;
+ else
+ increment = 1;
+
+ fprintf (file, "\tmov%s g%d,r%d\n",
+ (increment == 4 ? "q" : increment == 3 ? "t"
+ : increment == 2 ? "l": ""), i, j);
+ }
+
+ /* If this function uses the arg pointer, then save it in r3 and then
+ set it to zero. */
+
+ if (current_function_args_size != 0 || varargs_stdarg_function)
+ fprintf (file, "\tmov g14,r3\n\tmov 0,g14\n");
+
+ /* Load location address into g0 and call mcount. */
+
+ fprintf (file, "\tlda\tLP%d,g0\n\tcallx\tmcount\n", labelno);
+
+ /* If this function uses the arg pointer, restore it. */
+
+ if (current_function_args_size != 0 || varargs_stdarg_function)
+ fprintf (file, "\tmov r3,g14\n");
+
+ /* Restore parameter registers. */
+
+ for (i = 0, j = 4; i <= last_parm_reg; i += increment, j += increment)
+ {
+ if (i % 4 == 0 && (last_parm_reg - i) >= 3)
+ increment = 4;
+ else if (i % 4 == 0 && (last_parm_reg - i) >= 2)
+ increment = 3;
+ else if (i % 2 == 0 && (last_parm_reg - i) >= 1)
+ increment = 2;
+ else
+ increment = 1;
+
+ fprintf (file, "\tmov%s r%d,g%d\n",
+ (increment == 4 ? "q" : increment == 3 ? "t"
+ : increment == 2 ? "l": ""), j, i);
+ }
+}
+
/* Output code for the function epilogue. */
void
if (epilogue_string[0] != '\0')
fprintf (file, "%s", epilogue_string);
- /* Must clear g14 on return. */
+ /* Must clear g14 on return if this function set it.
+ Only varargs/stdarg functions modify g14. */
- if (current_function_args_size != 0)
+ if (VARARGS_STDARG_FUNCTION (current_function_decl))
fprintf (file, "\tmov 0,g14\n");
fprintf (file, "\tret\n");
int argsize = INTVAL (argsize_rtx);
rtx nexti = next_real_insn (insn);
rtx operands[2];
+ int varargs_stdarg_function
+ = VARARGS_STDARG_FUNCTION (current_function_decl);
operands[0] = target;
operands[1] = arg_pointer;
- if (current_function_args_size != 0)
+ if (current_function_args_size != 0 || varargs_stdarg_function)
output_asm_insn ("mov g14,r3", operands);
if (argsize > 48)
output_asm_insn ("lda %a1,g14", operands);
- else if (current_function_args_size != 0)
+ else if (current_function_args_size != 0 || varargs_stdarg_function)
output_asm_insn ("mov 0,g14", operands);
/* The code used to assume that calls to SYMBOL_REFs could not be more
output_asm_insn ("callx %0", operands);
- if (current_function_args_size != 0)
+ /* If the caller sets g14 to the address of the argblock, then the caller
+ must clear it after the return. */
+
+ if (current_function_args_size != 0 || varargs_stdarg_function)
output_asm_insn ("mov r3,g14", operands);
+ else if (argsize > 48)
+ output_asm_insn ("mov 0,g14", operands);
return "";
}
return lbuf;
}
- if (current_function_args_size != 0)
+ /* Must clear g14 on return if this function set it.
+ Only varargs/stdarg functions modify g14. */
+
+ if (VARARGS_STDARG_FUNCTION (current_function_decl))
output_asm_insn ("mov 0,g14", 0);
if (i960_leaf_ret_reg >= 0)
switch (code)
{
case 'D':
- /* Second reg of a double. */
+ /* Second reg of a double or quad. */
fprintf (file, "%s", reg_names[REGNO (x)+1]);
break;
+ case 'E':
+ /* Third reg of a quad. */
+ fprintf (file, "%s", reg_names[REGNO (x)+2]);
+ break;
+
+ case 'F':
+ /* Fourth reg of a quad. */
+ fprintf (file, "%s", reg_names[REGNO (x)+3]);
+ break;
+
case 0:
fprintf (file, "%s", reg_names[REGNO (x)]);
break;
}
else if (rtxcode == CONST_INT)
{
- if (INTVAL (x) > 9999 || INTVAL (x) < -999)
- fprintf (file, "0x%x", INTVAL (x));
+ HOST_WIDE_INT val = INTVAL (x);
+ if (code == 'C')
+ val = ~val;
+ if (val > 9999 || val < -999)
+ fprintf (file, "0x%x", val);
else
- fprintf (file, "%d", INTVAL (x));
+ fprintf (file, "%d", val);
return;
}
else if (rtxcode == CONST_DOUBLE)
{
- double d;
+ REAL_VALUE_TYPE d;
+ char dstr[30];
- if (x == CONST0_RTX (DFmode) || x == CONST0_RTX (SFmode))
+ if (x == CONST0_RTX (GET_MODE (x)))
{
fprintf (file, "0f0.0");
return;
}
- else if (x == CONST1_RTX (DFmode) || x == CONST1_RTX (SFmode))
+ else if (x == CONST1_RTX (GET_MODE (x)))
{
fprintf (file, "0f1.0");
return;
}
- /* This better be a comment. */
REAL_VALUE_FROM_CONST_DOUBLE (d, x);
- fprintf (file, "%#g", d);
+ REAL_VALUE_TO_DECIMAL (d, "%#g", dstr);
+ fprintf (file, "0f%s", dstr);
return;
}
}
else if (GET_CODE (addr) == MULT)
{
- breg = XEXP (addr, 0);
+ ireg = XEXP (addr, 0);
scale = XEXP (addr, 1);
}
else
}
#endif
\f
-/* Modes for condition codes. */
-#define C_MODES \
- ((1 << (int) CCmode) | (1 << (int) CC_UNSmode) | (1<< (int) CC_CHKmode))
-/* Modes for single-word (and smaller) quantities. */
-#define S_MODES \
- (~C_MODES \
- & ~ ((1 << (int) DImode) | (1 << (int) TImode) \
- | (1 << (int) DFmode) | (1 << (int) TFmode)))
-
-/* Modes for double-word (and smaller) quantities. */
-#define D_MODES \
- (~C_MODES \
- & ~ ((1 << (int) TImode) | (1 << (int) TFmode)))
+int
+hard_regno_mode_ok (regno, mode)
+ int regno;
+ enum machine_mode mode;
+{
+ if (regno < 32)
+ {
+ switch (mode)
+ {
+ case CCmode: case CC_UNSmode: case CC_CHKmode:
+ return 0;
-/* Modes for quad-word quantities. */
-#define T_MODES (~C_MODES)
+ case DImode: case DFmode:
+ return (regno & 1) == 0;
-/* Modes for single-float quantities. */
-#define SF_MODES ((1 << (int) SFmode))
+ case TImode: case XFmode:
+ return (regno & 3) == 0;
-/* Modes for double-float quantities. */
-#define DF_MODES (SF_MODES | (1 << (int) DFmode) | (1 << (int) SCmode))
+ default:
+ return 1;
+ }
+ }
+ else if (regno >= 32 && regno < 36)
+ {
+ switch (mode)
+ {
+ case SFmode: case DFmode: case XFmode:
+ case SCmode: case DCmode:
+ return 1;
-/* Modes for quad-float quantities. */
-#define TF_MODES (DF_MODES | (1 << (int) TFmode) | (1 << (int) DCmode))
+ default:
+ return 0;
+ }
+ }
+ else if (regno == 36)
+ {
+ switch (mode)
+ {
+ case CCmode: case CC_UNSmode: case CC_CHKmode:
+ return 1;
-unsigned int hard_regno_mode_ok[FIRST_PSEUDO_REGISTER] = {
- T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
- T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
- T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
- T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
+ default:
+ return 0;
+ }
+ }
+ else if (regno == 37)
+ return 0;
- TF_MODES, TF_MODES, TF_MODES, TF_MODES, C_MODES};
+ abort ();
+}
\f
/* Return the minimum alignment of an expression rtx X in bytes. This takes
break;
case ASHIFT:
- case LSHIFT:
align = i960_expr_alignment (XEXP (x, 0));
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
else if (mode == VOIDmode)
{
/* End of parm list. */
- assert (type != 0 && TYPE_MODE (type) == VOIDmode);
+ if (type == 0 || TYPE_MODE (type) != VOIDmode)
+ abort ();
size = 1;
}
else
size = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
if (type == 0)
- align = size;
+ {
+ /* ??? This is a hack to properly correct the alignment of XFmode
+ values without affecting anything else. */
+ if (size == 3)
+ align = 4;
+ else
+ align = size;
+ }
else if (TYPE_ALIGN (type) >= BITS_PER_WORD)
align = TYPE_ALIGN (type) / BITS_PER_WORD;
else
subsequent arguments are placed on the stack.
Additionally, parameters with an alignment requirement stronger than
- a word must be be aligned appropriately. */
+ a word must be aligned appropriately. Note that this means that a
+ 64 bit object with a 32 bit alignment is not 64 bit aligned and may be
+ passed in an odd/even register pair. */
/* Update CUM to advance past an argument described by MODE and TYPE. */
i960_arg_size_and_align (mode, type, &size, &align);
- if (named == 0 || size > 4 || cum->ca_nstackparms != 0
+ if (size > 4 || cum->ca_nstackparms != 0
|| (size + ROUND_PARM (cum->ca_nregparms, align)) > NPARM_REGS
|| MUST_PASS_IN_STACK (mode, type))
- cum->ca_nstackparms = ROUND_PARM (cum->ca_nstackparms, align) + size;
+ {
+ /* Indicate that all the registers are in use, even if all are not,
+ so va_start will compute the right value. */
+ cum->ca_nregparms = NPARM_REGS;
+ cum->ca_nstackparms = ROUND_PARM (cum->ca_nstackparms, align) + size;
+ }
else
cum->ca_nregparms = ROUND_PARM (cum->ca_nregparms, align) + size;
}
i960_arg_size_and_align (mode, type, &size, &align);
- if (named == 0 || size > 4 || cum->ca_nstackparms != 0
+ if (size > 4 || cum->ca_nstackparms != 0
|| (size + ROUND_PARM (cum->ca_nregparms, align)) > NPARM_REGS
|| MUST_PASS_IN_STACK (mode, type))
{
/* Floating-point support. */
void
-i960_output_double (file, value)
+i960_output_long_double (file, value)
FILE *file;
- double value;
+ REAL_VALUE_TYPE value;
{
- if (REAL_VALUE_ISINF (value))
- {
- fprintf (file, "\t.word 0\n");
- fprintf (file, "\t.word 0x7ff00000 # Infinity\n");
- }
- else
- fprintf (file, "\t.double 0d%.17e\n", (value));
+ long value_long[3];
+ char dstr[30];
+
+ REAL_VALUE_TO_TARGET_LONG_DOUBLE (value, value_long);
+ REAL_VALUE_TO_DECIMAL (value, "%.20g", dstr);
+
+ fprintf (file,
+ "\t.word\t0x%08lx\t\t# %s\n\t.word\t0x%08lx\n\t.word\t0x%08lx\n",
+ value_long[0], dstr, value_long[1], value_long[2]);
+ fprintf (file, "\t.word\t0x0\n");
}
+void
+i960_output_double (file, value)
+ FILE *file;
+ REAL_VALUE_TYPE value;
+{
+ long value_long[2];
+ char dstr[30];
+
+ REAL_VALUE_TO_TARGET_DOUBLE (value, value_long);
+ REAL_VALUE_TO_DECIMAL (value, "%.20g", dstr);
+
+ fprintf (file, "\t.word\t0x%08lx\t\t# %s\n\t.word\t0x%08lx\n",
+ value_long[0], dstr, value_long[1]);
+}
+
void
i960_output_float (file, value)
FILE *file;
- double value;
+ REAL_VALUE_TYPE value;
{
- if (REAL_VALUE_ISINF (value))
- fprintf (file, "\t.word 0x7f800000 # Infinity\n");
- else
- fprintf (file, "\t.float 0f%.12e\n", (value));
+ long value_long;
+ char dstr[30];
+
+ REAL_VALUE_TO_TARGET_SINGLE (value, value_long);
+ REAL_VALUE_TO_DECIMAL (value, "%.12g", dstr);
+
+ fprintf (file, "\t.word\t0x%08lx\t\t# %s (float)\n", value_long, dstr);
}
\f
/* Return the number of bits that an object of size N bytes is aligned to. */
return n;
}
-/* Compute the size of an aggregate type TSIZE. */
-
-tree
-i960_round_size (tsize)
- tree tsize;
-{
- int size, byte_size, align;
-
- if (TREE_CODE (tsize) != INTEGER_CST)
- return tsize;
-
- size = TREE_INT_CST_LOW (tsize);
- byte_size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
- align = i960_object_bytes_bitalign (byte_size);
-
- /* Handle #pragma align. */
- if (align > i960_maxbitalignment)
- align = i960_maxbitalignment;
-
- if (size % align)
- size = ((size / align) + 1) * align;
-
- return size_int (size);
-}
-
-/* Compute the alignment for an aggregate type TSIZE. */
+/* Compute the alignment for an aggregate type TSIZE.
+ Alignment is MAX (greatest member alignment,
+ MIN (pragma align, structure size alignment)). */
int
i960_round_align (align, tsize)
int align;
tree tsize;
{
- int byte_size;
+ int new_align;
if (TREE_CODE (tsize) != INTEGER_CST)
return align;
- byte_size = (TREE_INT_CST_LOW (tsize) + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
- align = i960_object_bytes_bitalign (byte_size);
+ new_align = i960_object_bytes_bitalign (TREE_INT_CST_LOW (tsize)
+ / BITS_PER_UNIT);
+ /* Handle #pragma align. */
+ if (new_align > i960_maxbitalignment)
+ new_align = i960_maxbitalignment;
+
+ if (align < new_align)
+ align = new_align;
+
return align;
}
\f
int *pretend_size;
int no_rtl;
{
- if (cum->ca_nregparms < NPARM_REGS)
- {
- int first_reg_offset = cum->ca_nregparms;
+ /* Note: for a varargs fn with only a va_alist argument, this is 0. */
+ int first_reg = cum->ca_nregparms;
- if (first_reg_offset > NPARM_REGS)
- first_reg_offset = NPARM_REGS;
+ /* Copy only unnamed register arguments to memory. If there are
+ any stack parms, there are no unnamed arguments in registers, and
+ an argument block was already allocated by the caller.
+ Remember that any arg bigger than 4 words is passed on the stack as
+ are all subsequent args.
- if (! (no_rtl) && first_reg_offset != NPARM_REGS)
- {
- rtx label = gen_label_rtx ();
- emit_insn (gen_cmpsi (arg_pointer_rtx, const0_rtx));
- emit_jump_insn (gen_bne (label));
- emit_insn (gen_rtx (SET, VOIDmode, arg_pointer_rtx,
- stack_pointer_rtx));
- emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx,
- memory_address (SImode,
- plus_constant (stack_pointer_rtx,
- 48))));
- emit_label (label);
- move_block_from_reg
- (first_reg_offset,
- gen_rtx (MEM, BLKmode, virtual_incoming_args_rtx),
- NPARM_REGS - first_reg_offset);
- }
- *pretend_size = (NPARM_REGS - first_reg_offset) * UNITS_PER_WORD;
+ If there are no stack arguments but there are exactly NPARM_REGS
+ registers, either there were no extra arguments or the caller
+ allocated an argument block. */
+
+ if (cum->ca_nstackparms == 0 && first_reg < NPARM_REGS && !no_rtl)
+ {
+ rtx label = gen_label_rtx ();
+ rtx regblock;
+
+ /* If arg_pointer_rtx == 0, no arguments were passed on the stack
+ and we need to allocate a chunk to save the registers (if any
+ arguments were passed on the stack the caller would allocate the
+ 48 bytes as well). We must allocate all 48 bytes (12*4) because
+ va_start assumes it. */
+ emit_insn (gen_cmpsi (arg_pointer_rtx, const0_rtx));
+ emit_jump_insn (gen_bne (label));
+ emit_insn (gen_rtx (SET, VOIDmode, arg_pointer_rtx,
+ stack_pointer_rtx));
+ emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx,
+ memory_address (SImode,
+ plus_constant (stack_pointer_rtx,
+ 48))));
+ emit_label (label);
+
+ /* ??? Note that we unnecessarily store one extra register for stdarg
+ fns. We could optimize this, but it's kept as for now. */
+ regblock = gen_rtx (MEM, BLKmode,
+ plus_constant (arg_pointer_rtx,
+ first_reg * 4));
+ move_block_from_reg (first_reg, regblock,
+ NPARM_REGS - first_reg,
+ (NPARM_REGS - first_reg) * UNITS_PER_WORD);
}
}
/* Otherwise, we have an arg block if the current function has more than
48 bytes of parameters. */
- if (current_function_args_size != 0)
+ if (current_function_args_size != 0 || VARARGS_STDARG_FUNCTION (fndecl))
return 48;
else
return 0;