/* Subroutines for insn-output.c for Intel 860
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ Copyright (C) 1989, 91, 97, 98, 1999 Free Software Foundation, Inc.
Derived from sparc.c.
Written by Richard Stallman (rms@ai.mit.edu).
- Hacked substantially by Ron Guilmette (rfg@ncd.com) to cater
+ Hacked substantially by Ron Guilmette (rfg@netcom.com) to cater
to the whims of the System V Release 4 assembler.
This file is part of GNU CC.
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. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
#include "config.h"
+#include "system.h"
#include "flags.h"
#include "rtl.h"
+#include "tree.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "output.h"
#include "recog.h"
#include "insn-attr.h"
-
-#include <stdio.h>
+#include "function.h"
+#include "expr.h"
static rtx find_addr_reg ();
case AND:
case IOR:
case XOR:
- case LSHIFT:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if (CONSTANT_P (arg)
&& !(GET_CODE (arg) == CONST_INT
&& (SMALL_INT (arg)
- || INTVAL (arg) & 0xffff == 0)))
+ || (INTVAL (arg) & 0xffff) == 0)))
return 0;
}
case IOR:
if (CONSTANT_P (XEXP (op, 1))
&& !(GET_CODE (XEXP (op, 1)) == CONST_INT
&& (SMALL_INT (XEXP (op, 1))
- || INTVAL (XEXP (op, 1)) & 0xffff == 0)))
+ || (INTVAL (XEXP (op, 1)) & 0xffff) == 0)))
return 0;
- case LSHIFT:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
return 0;
}
}
-
-/* Nonzero only if this *really* is a single insn operand. */
-int
-strict_single_insn_op_p (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (mode == VOIDmode)
- mode = GET_MODE (op);
-
- switch (GET_CODE (op))
- {
- case CC0:
- return 1;
-
- case CONST_INT:
- if (SMALL_INT (op))
- return 1;
- /* We can put this set insn into delay slot, because this is one
- insn; `orh'. */
- if ((INTVAL (op) & 0xffff) == 0)
- return 1;
- return 0;
-
- case SYMBOL_REF:
- return 0;
-
- case REG:
-#if 0
- /* This loses when moving an freg to a general reg. */
- return HARD_REGNO_NREGS (REGNO (op), mode) == 1;
-#endif
- return (mode != DFmode && mode != DImode);
-
- case MEM:
- if (! CONSTANT_ADDRESS_P (XEXP (op, 0)))
- return (mode != DFmode && mode != DImode);
- return 0;
-
- /* We never need to negate or complement constants. */
- case NEG:
- return (mode != DFmode);
- case NOT:
- case ZERO_EXTEND:
- return 1;
-
- case PLUS:
- case MINUS:
- /* Detect cases that require multiple instructions. */
- if (CONSTANT_P (XEXP (op, 1))
- && !(GET_CODE (XEXP (op, 1)) == CONST_INT
- && SMALL_INT (XEXP (op, 1))))
- return 0;
- case EQ:
- case NE:
- case LT:
- case GT:
- case LE:
- case GE:
- case LTU:
- case GTU:
- case LEU:
- case GEU:
- return 1;
-
- case AND:
- if (GET_CODE (XEXP (op, 1)) == NOT)
- {
- rtx arg = XEXP (XEXP (op, 1), 0);
- if (CONSTANT_P (arg)
- && !(GET_CODE (arg) == CONST_INT
- && (SMALL_INT (arg)
- || INTVAL (arg) & 0xffff == 0)))
- return 0;
- }
- case IOR:
- case XOR:
- /* Both small and round numbers take one instruction;
- others take two. */
- if (CONSTANT_P (XEXP (op, 1))
- && !(GET_CODE (XEXP (op, 1)) == CONST_INT
- && (SMALL_INT (XEXP (op, 1))
- || INTVAL (XEXP (op, 1)) & 0xffff == 0)))
- return 0;
-
- case LSHIFT:
- case ASHIFT:
- case ASHIFTRT:
- case LSHIFTRT:
- return 1;
-
- case SUBREG:
- if (SUBREG_WORD (op) != 0)
- return 0;
- return strict_single_insn_op_p (SUBREG_REG (op), mode);
-
- case SIGN_EXTEND:
- if (GET_CODE (XEXP (op, 0)) == MEM
- && ! CONSTANT_ADDRESS_P (XEXP (XEXP (op, 0), 0)))
- return 1;
- return 0;
-
- /* Not doing floating point, since they probably
- take longer than the branch slot they might fill. */
- case FLOAT_EXTEND:
- case FLOAT_TRUNCATE:
- case FLOAT:
- case FIX:
- case UNSIGNED_FLOAT:
- case UNSIGNED_FIX:
- return 0;
-
- default:
- return 0;
- }
-}
-\f
-/* Return truth value of whether OP is a relational operator. */
-int
-relop (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- switch (GET_CODE (op))
- {
- case EQ:
- case NE:
- case GT:
- case GE:
- case LT:
- case LE:
- case GTU:
- case GEU:
- case LTU:
- case LEU:
- return 1;
- }
- return 0;
-}
\f
/* Return non-zero only if OP is a register of mode MODE,
or const0_rtx. */
{
return (GET_CODE (op) == CONST_INT && LOGIC_INT (op));
}
+
+/* Test for a valid operand for a call instruction.
+ Don't allow the arg pointer register or virtual regs
+ since they may change into reg + const, which the patterns
+ can't handle yet. */
+
+int
+call_insn_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == MEM
+ && (CONSTANT_ADDRESS_P (XEXP (op, 0))
+ || (GET_CODE (XEXP (op, 0)) == REG
+ && XEXP (op, 0) != arg_pointer_rtx
+ && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
+ && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
+ return 1;
+ return 0;
+}
\f
/* Return the best assembler insn template
for moving operands[1] into operands[0] as a fullword. */
rtx xoperands[2];
cc_status.flags &= ~CC_F0_IS_0;
- xoperands[0] = gen_rtx (REG, SFmode, 32);
+ xoperands[0] = gen_rtx_REG (SFmode, 32);
xoperands[1] = operands[1];
output_asm_insn (singlemove_string (xoperands), xoperands);
xoperands[1] = xoperands[0];
}
if (GET_CODE (operands[1]) == CONST_INT)
{
+ if (operands[1] == const0_rtx)
+ return "mov %?r0,%0";
if((INTVAL (operands[1]) & 0xffff0000) == 0)
return "or %L1,%?r0,%0";
+ if((INTVAL (operands[1]) & 0xffff8000) == 0xffff8000)
+ return "adds %1,%?r0,%0";
if((INTVAL (operands[1]) & 0x0000ffff) == 0)
return "orh %H1,%?r0,%0";
- if (operands[1] == const0_rtx)
- return "mov %?r0,%0";
}
return "mov %1,%0";
}
enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
rtx addreg0 = 0, addreg1 = 0;
+ int highest_first = 0;
+ int no_addreg1_decrement = 0;
/* First classify both operands. */
operands in OPERANDS to be suitable for the low-numbered word. */
if (optype0 == REGOP)
- latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
else if (optype0 == OFFSOP)
latehalf[0] = adj_offsettable_operand (operands[0], 4);
else
latehalf[0] = operands[0];
if (optype1 == REGOP)
- latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+ latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
else if (optype1 == OFFSOP)
latehalf[1] = adj_offsettable_operand (operands[1], 4);
else if (optype1 == CNSTOP)
else if (optype0 == REGOP && optype1 != REGOP
&& reg_overlap_mentioned_p (operands[0], operands[1]))
{
- /* Do the late half first. */
- output_asm_insn (singlemove_string (latehalf), latehalf);
- /* Then clobber. */
- return singlemove_string (operands);
+ /* If both halves of dest are used in the src memory address,
+ add the two regs and put them in the low reg (operands[0]).
+ Then it works to load latehalf first. */
+ if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
+ && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
+ {
+ rtx xops[2];
+ xops[0] = latehalf[0];
+ xops[1] = operands[0];
+ output_asm_insn ("adds %1,%0,%1", xops);
+ operands[1] = gen_rtx_MEM (DImode, operands[0]);
+ latehalf[1] = adj_offsettable_operand (operands[1], 4);
+ addreg1 = 0;
+ highest_first = 1;
+ }
+ /* Only one register in the dest is used in the src memory address,
+ and this is the first register of the dest, so we want to do
+ the late half first here also. */
+ else if (! reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
+ highest_first = 1;
+ /* Only one register in the dest is used in the src memory address,
+ and this is the second register of the dest, so we want to do
+ the late half last. If addreg1 is set, and addreg1 is the same
+ register as latehalf, then we must suppress the trailing decrement,
+ because it would clobber the value just loaded. */
+ else if (addreg1 && reg_mentioned_p (addreg1, latehalf[0]))
+ no_addreg1_decrement = 1;
}
- /* Normal case: do the two words, low-numbered first. */
+ /* Normal case: do the two words, low-numbered first.
+ Overlap case (highest_first set): do high-numbered word first. */
- output_asm_insn (singlemove_string (operands), operands);
+ if (! highest_first)
+ output_asm_insn (singlemove_string (operands), operands);
CC_STATUS_PARTIAL_INIT;
/* Make any unoffsettable addresses point at high-numbered word. */
/* Undo the adds we just did. */
if (addreg0)
output_asm_insn ("adds -0x4,%0,%0", &addreg0);
- if (addreg1)
+ if (addreg1 && !no_addreg1_decrement)
output_asm_insn ("adds -0x4,%0,%0", &addreg1);
+ if (highest_first)
+ output_asm_insn (singlemove_string (operands), operands);
+
return "";
}
\f
/* If the source operand is any sort of zero, use f0 instead. */
if (operands[1] == CONST0_RTX (GET_MODE (operands[1])))
- operands[1] = gen_rtx (REG, DFmode, F0_REGNUM);
+ operands[1] = gen_rtx_REG (DFmode, F0_REGNUM);
if (FP_REG_P (operands[0]))
{
if (GET_CODE (operands[1]) == REG)
{
output_asm_insn ("ixfr %1,%0", operands);
- operands[0] = gen_rtx (REG, VOIDmode, REGNO (operands[0]) + 1);
- operands[1] = gen_rtx (REG, VOIDmode, REGNO (operands[1]) + 1);
+ operands[0] = gen_rtx_REG (VOIDmode, REGNO (operands[0]) + 1);
+ operands[1] = gen_rtx_REG (VOIDmode, REGNO (operands[1]) + 1);
return "ixfr %1,%0";
}
if (operands[1] == CONST0_RTX (DFmode))
if (GET_CODE (operands[0]) == REG)
{
output_asm_insn ("fxfr %1,%0", operands);
- operands[0] = gen_rtx (REG, VOIDmode, REGNO (operands[0]) + 1);
- operands[1] = gen_rtx (REG, VOIDmode, REGNO (operands[1]) + 1);
+ operands[0] = gen_rtx_REG (VOIDmode, REGNO (operands[0]) + 1);
+ operands[1] = gen_rtx_REG (VOIDmode, REGNO (operands[1]) + 1);
return "fxfr %1,%0";
}
if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
#if 1
cc_status.flags &= ~ CC_KNOW_HI_R31;
- output_asm_insn ("mov %1,%0", xoperands);
+ output_asm_insn (singlemove_string (xoperands), xoperands);
#else
if (GET_CODE (size) == REG)
output_asm_insn ("sub %2,%1,%0", xoperands);
else
{
- xoperands[1]
- = gen_rtx (CONST_INT, VOIDmode, INTVAL (size) - INTVAL (align));
+ xoperands[1] = GEN_INT (INTVAL (size) - INTVAL (align));
cc_status.flags &= ~ CC_KNOW_HI_R31;
output_asm_insn ("mov %1,%0", xoperands);
}
if (align > 4)
{
align = 4;
- alignrtx = gen_rtx (CONST_INT, VOIDmode, 4);
+ alignrtx = GEN_INT (4);
}
/* Recognize special cases of block moves. These occur
/* Generate number for unique label. */
- xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++);
+ xoperands[3] = GEN_INT (movstrsi_label++);
/* Calculate the size of the chunks we will be trying to move first. */
/* Copy the increment (negative) to a register for bla insn. */
- xoperands[4] = gen_rtx (CONST_INT, VOIDmode, - chunk_size);
+ xoperands[4] = GEN_INT (- chunk_size);
xoperands[5] = operands[5];
output_asm_insn ("adds %4,%?r0,%5", xoperands);
return "";
}
\f
+#if 0
/* Output a delayed branch insn with the delay insn in its
branch slot. The delayed branch insn template is in TEMPLATE,
with operands OPERANDS. The insn in its delay slot is INSN.
or l%x,%0,%1
*/
+/* ??? Disabled because this re-recognition is incomplete and causes
+ constrain_operands to segfault. Anyone who cares should fix up
+ the code to use the DBR pass. */
char *
output_delayed_branch (template, operands, insn)
else
{
int insn_code_number;
- rtx pat = gen_rtx (SET, VOIDmode, dest, src);
- rtx delay_insn = gen_rtx (INSN, VOIDmode, 0, 0, 0, pat, -1, 0, 0);
+ rtx pat = gen_rtx_SET (VOIDmode, dest, src);
+ rtx delay_insn = gen_rtx_INSN (VOIDmode, 0, 0, 0, pat, -1, 0, 0);
int i;
/* Output the branch instruction first. */
/* Now recognize the insn which we put in its delay slot.
We must do this after outputting the branch insn,
since operands may just be a pointer to `recog_operand'. */
- INSN_CODE (delay_insn) = insn_code_number = recog (pat, delay_insn);
+ INSN_CODE (delay_insn) = insn_code_number
+ = recog (pat, delay_insn, NULL_PTR);
if (insn_code_number == -1)
abort ();
}
insn_extract (delay_insn);
- if (! constrain_operands (insn_code_number, 1))
+ if (! constrain_operands (1))
fatal_insn_not_found (delay_insn);
template = insn_template[insn_code_number];
}
#ifdef REGISTER_CONSTRAINTS
- if (! constrain_operands (insn_code_number))
+ if (! constrain_operands (1))
abort ();
#endif
output_asm_insn (template, recog_operand);
return "";
}
+#endif
\f
/* Special routine to convert an SFmode value represented as a
CONST_DOUBLE into its equivalent unsigned long bit pattern.
abort ();
#if TARGET_FLOAT_FORMAT != HOST_FLOAT_FORMAT
-# error IEEE emulation needed
+ error IEEE emulation needed
#endif
REAL_VALUE_FROM_CONST_DOUBLE (d, x);
u2.f = d;
instruction, so what the hell.
*/
+/* This corresponds to a version 4 TDESC structure. Lower numbered
+ versions successively omit the last word of the structure. We
+ don't try to handle version 5 here. */
+
+typedef struct TDESC_flags {
+ int version:4;
+ int reg_packing:1;
+ int callable_block:1;
+ int reserved:4;
+ int fregs:6; /* fp regs 2-7 */
+ int iregs:16; /* regs 0-15 */
+} TDESC_flags;
+
+typedef struct TDESC {
+ TDESC_flags flags;
+ int integer_reg_offset; /* same as must_preserve_bytes */
+ int floating_point_reg_offset;
+ unsigned int positive_frame_size; /* same as frame_upper_bytes */
+ unsigned int negative_frame_size; /* same as frame_lower_bytes */
+} TDESC;
+
void
function_epilogue (asm_file, local_bytes)
register FILE *asm_file;
register unsigned local_bytes;
{
register unsigned frame_upper_bytes;
+ register unsigned frame_lower_bytes;
register unsigned preserved_reg_bytes = 0;
register unsigned i;
register unsigned restored_so_far = 0;
+ register unsigned int_restored;
+ register unsigned mask;
+ unsigned intflags=0;
+ register TDESC_flags *flags = (TDESC_flags *) &intflags;
+
+ flags->version = 4;
+ flags->reg_packing = 1;
+ flags->iregs = 8; /* old fp always gets saved */
+
+ /* Round-up the frame_lower_bytes so that it's a multiple of 16. */
+
+ frame_lower_bytes = (local_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT;
/* Count the number of registers that were preserved in the prologue.
Ignore r0. It is never preserved. */
/* Restore all of the "preserved" registers that need restoring. */
- for (i = 1; i < 32; i++)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ mask = 2;
+
+ for (i = 1; i < 32; i++, mask<<=1)
+ if (regs_ever_live[i] && ! call_used_regs[i]) {
fprintf (asm_file, "\tld.l %d(%sfp),%s%s\n",
must_preserve_bytes + (4 * restored_so_far++),
i860_reg_prefix, i860_reg_prefix, reg_names[i]);
+ if (i > 3 && i < 16)
+ flags->iregs |= mask;
+ }
- for (i = 32; i < 64; i++)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ int_restored = restored_so_far;
+ mask = 1;
+
+ for (i = 32; i < 64; i++) {
+ if (regs_ever_live[i] && ! call_used_regs[i]) {
fprintf (asm_file, "\tfld.l %d(%sfp),%s%s\n",
must_preserve_bytes + (4 * restored_so_far++),
i860_reg_prefix, i860_reg_prefix, reg_names[i]);
+ if (i > 33 & i < 40)
+ flags->fregs |= mask;
+ }
+ if (i > 33 && i < 40)
+ mask<<=1;
+ }
/* Get the value we plan to use to restore the stack pointer into r31. */
/* Restore the return address and the old frame pointer. */
- if (must_preserve_r1)
+ if (must_preserve_r1) {
fprintf (asm_file, "\tld.l 4(%sfp),%sr1\n",
i860_reg_prefix, i860_reg_prefix);
+ flags->iregs |= 2;
+ }
fprintf (asm_file, "\tld.l 0(%sfp),%sfp\n",
i860_reg_prefix, i860_reg_prefix);
fprintf (asm_file, "\tbri %sr1\n\tmov %sr31,%ssp\n",
i860_reg_prefix, i860_reg_prefix, i860_reg_prefix);
+
+#ifdef OUTPUT_TDESC /* Output an ABI-compliant TDESC entry */
+ if (! frame_lower_bytes) {
+ flags->version--;
+ if (! frame_upper_bytes) {
+ flags->version--;
+ if (restored_so_far == int_restored) /* No FP saves */
+ flags->version--;
+ }
+ }
+ assemble_name(asm_file,current_function_original_name);
+ fputs(".TDESC:\n", asm_file);
+ fprintf(asm_file, "%s 0x%0x\n", ASM_LONG, intflags);
+ fprintf(asm_file, "%s %d\n", ASM_LONG,
+ int_restored ? must_preserve_bytes : 0);
+ if (flags->version > 1) {
+ fprintf(asm_file, "%s %d\n", ASM_LONG,
+ (restored_so_far == int_restored) ? 0 : must_preserve_bytes +
+ (4 * int_restored));
+ if (flags->version > 2) {
+ fprintf(asm_file, "%s %d\n", ASM_LONG, frame_upper_bytes);
+ if (flags->version > 3)
+ fprintf(asm_file, "%s %d\n", ASM_LONG, frame_lower_bytes);
+ }
+ }
+ tdesc_section();
+ fprintf(asm_file, "%s ", ASM_LONG);
+ assemble_name(asm_file, current_function_original_name);
+ fprintf(asm_file, "\n%s ", ASM_LONG);
+ assemble_name(asm_file, current_function_original_name);
+ fputs(".TDESC\n", asm_file);
+ text_section();
+#endif
+}
+\f
+
+/* Expand a library call to __builtin_saveregs. */
+rtx
+i860_saveregs ()
+{
+ rtx fn = gen_rtx_SYMBOL_REF (Pmode, "__builtin_saveregs");
+ rtx save = gen_reg_rtx (Pmode);
+ rtx valreg = LIBCALL_VALUE (Pmode);
+ rtx ret;
+
+ /* The return value register overlaps the first argument register.
+ Save and restore it around the call. */
+ emit_move_insn (save, valreg);
+ ret = emit_library_call_value (fn, NULL_RTX, 1, Pmode, 0);
+ if (GET_CODE (ret) != REG || REGNO (ret) < FIRST_PSEUDO_REGISTER)
+ ret = copy_to_reg (ret);
+ emit_move_insn (valreg, save);
+
+ return ret;
+}
+
+tree
+i860_build_va_list ()
+{
+ tree field_ireg_used, field_freg_used, field_reg_base, field_mem_ptr;
+ tree record;
+
+ record = make_node (RECORD_TYPE);
+
+ field_ireg_used = build_decl (FIELD_DECL, get_identifier ("__ireg_used"),
+ unsigned_type_node);
+ field_freg_used = build_decl (FIELD_DECL, get_identifier ("__freg_used"),
+ unsigned_type_node);
+ field_reg_base = build_decl (FIELD_DECL, get_identifier ("__reg_base"),
+ ptr_type_node);
+ field_mem_ptr = build_decl (FIELD_DECL, get_identifier ("__mem_ptr"),
+ ptr_type_node);
+
+ DECL_FIELD_CONTEXT (field_ireg_used) = record;
+ DECL_FIELD_CONTEXT (field_freg_used) = record;
+ DECL_FIELD_CONTEXT (field_reg_base) = record;
+ DECL_FIELD_CONTEXT (field_mem_ptr) = record;
+
+#ifdef I860_SVR4_VA_LIST
+ TYPE_FIELDS (record) = field_ireg_used;
+ TREE_CHAIN (field_ireg_used) = field_freg_used;
+ TREE_CHAIN (field_freg_used) = field_reg_base;
+ TREE_CHAIN (field_reg_base) = field_mem_ptr;
+#else
+ TYPE_FIELDS (record) = field_reg_base;
+ TREE_CHAIN (field_reg_base) = field_mem_ptr;
+ TREE_CHAIN (field_mem_ptr) = field_ireg_used;
+ TREE_CHAIN (field_ireg_used) = field_freg_used;
+#endif
+
+ layout_type (record);
+ return record;
+}
+
+void
+i860_va_start (stdarg_p, valist, nextarg)
+ int stdarg_p;
+ tree valist;
+ rtx nextarg;
+{
+ tree saveregs, t;
+
+ saveregs = make_tree (build_pointer_type (va_list_type_node),
+ expand_builtin_saveregs ());
+ saveregs = build1 (INDIRECT_REF, va_list_type_node, saveregs);
+
+ if (stdarg_p)
+ {
+ tree field_ireg_used, field_freg_used, field_reg_base, field_mem_ptr;
+ tree ireg_used, freg_used, reg_base, mem_ptr;
+
+#ifdef I860_SVR4_VA_LIST
+ field_ireg_used = TYPE_FIELDS (va_list_type_node);
+ field_freg_used = TREE_CHAIN (field_ireg_used);
+ field_reg_base = TREE_CHAIN (field_freg_used);
+ field_mem_ptr = TREE_CHAIN (field_reg_base);
+#else
+ field_reg_base = TYPE_FIELDS (va_list_type_node);
+ field_mem_ptr = TREE_CHAIN (field_reg_base);
+ field_ireg_used = TREE_CHAIN (field_mem_ptr);
+ field_freg_used = TREE_CHAIN (field_ireg_used);
+#endif
+
+ ireg_used = build (COMPONENT_REF, TREE_TYPE (field_ireg_used),
+ valist, field_ireg_used);
+ freg_used = build (COMPONENT_REF, TREE_TYPE (field_freg_used),
+ valist, field_freg_used);
+ reg_base = build (COMPONENT_REF, TREE_TYPE (field_reg_base),
+ valist, field_reg_base);
+ mem_ptr = build (COMPONENT_REF, TREE_TYPE (field_mem_ptr),
+ valist, field_mem_ptr);
+
+ t = build_int_2 (current_function_args_info.ints, 0);
+ t = build (MODIFY_EXPR, TREE_TYPE (ireg_used), ireg_used, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ t = build_int_2 (ROUNDUP (current_function_args_info.floats, 8), 0);
+ t = build (MODIFY_EXPR, TREE_TYPE (freg_used), freg_used, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ t = build (COMPONENT_REF, TREE_TYPE (field_reg_base),
+ saveregs, field_reg_base);
+ t = build (MODIFY_EXPR, TREE_TYPE (reg_base), reg_base, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ t = make_tree (ptr_type_node, nextarg);
+ t = build (MODIFY_EXPR, TREE_TYPE (mem_ptr), mem_ptr, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+ else
+ {
+ t = build (MODIFY_EXPR, va_list_type_node, valist, saveregs);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+}
+
+#define NUM_PARM_FREGS 8
+#define NUM_PARM_IREGS 12
+#ifdef I860_SVR4_VARARGS
+#define FREG_OFFSET 0
+#define IREG_OFFSET (NUM_PARM_FREGS * UNITS_PER_WORD)
+#else
+#define FREG_OFFSET (NUM_PARM_IREGS * UNITS_PER_WORD)
+#define IREG_OFFSET 0
+#endif
+
+rtx
+i860_va_arg (valist, type)
+ tree valist, type;
+{
+ tree field_ireg_used, field_freg_used, field_reg_base, field_mem_ptr;
+ tree type_ptr_node, t;
+ rtx lab_over = NULL_RTX;
+ rtx ret, val;
+ HOST_WIDE_INT align;
+
+#ifdef I860_SVR4_VA_LIST
+ field_ireg_used = TYPE_FIELDS (va_list_type_node);
+ field_freg_used = TREE_CHAIN (field_ireg_used);
+ field_reg_base = TREE_CHAIN (field_freg_used);
+ field_mem_ptr = TREE_CHAIN (field_reg_base);
+#else
+ field_reg_base = TYPE_FIELDS (va_list_type_node);
+ field_mem_ptr = TREE_CHAIN (field_reg_base);
+ field_ireg_used = TREE_CHAIN (field_mem_ptr);
+ field_freg_used = TREE_CHAIN (field_ireg_used);
+#endif
+
+ field_ireg_used = build (COMPONENT_REF, TREE_TYPE (field_ireg_used),
+ valist, field_ireg_used);
+ field_freg_used = build (COMPONENT_REF, TREE_TYPE (field_freg_used),
+ valist, field_freg_used);
+ field_reg_base = build (COMPONENT_REF, TREE_TYPE (field_reg_base),
+ valist, field_reg_base);
+ field_mem_ptr = build (COMPONENT_REF, TREE_TYPE (field_mem_ptr),
+ valist, field_mem_ptr);
+
+ ret = gen_reg_rtx (Pmode);
+ type_ptr_node = build_pointer_type (type);
+
+ if (! AGGREGATE_TYPE_P (type))
+ {
+ int nparm, incr, ofs;
+ tree field;
+ rtx lab_false;
+
+ if (FLOAT_TYPE_P (type))
+ {
+ field = field_freg_used;
+ nparm = NUM_PARM_FREGS;
+ incr = 2;
+ ofs = FREG_OFFSET;
+ }
+ else
+ {
+ field = field_ireg_used;
+ nparm = NUM_PARM_IREGS;
+ incr = int_size_in_bytes (type) / UNITS_PER_WORD;
+ ofs = IREG_OFFSET;
+ }
+
+ lab_false = gen_label_rtx ();
+ lab_over = gen_label_rtx ();
+
+ emit_cmp_and_jump_insns (expand_expr (field, NULL_RTX, 0, 0),
+ GEN_INT (nparm - incr), GT, const0_rtx,
+ TYPE_MODE (TREE_TYPE (field)),
+ TREE_UNSIGNED (field), 0, lab_false);
+
+ t = fold (build (POSTINCREMENT_EXPR, TREE_TYPE (field), field,
+ build_int_2 (incr, 0)));
+ TREE_SIDE_EFFECTS (t) = 1;
+
+ t = fold (build (MULT_EXPR, TREE_TYPE (field), field,
+ build_int_2 (UNITS_PER_WORD, 0)));
+ TREE_SIDE_EFFECTS (t) = 1;
+
+ t = fold (build (PLUS_EXPR, ptr_type_node, field_reg_base,
+ fold (build (PLUS_EXPR, TREE_TYPE (field), t,
+ build_int_2 (ofs, 0)))));
+ TREE_SIDE_EFFECTS (t) = 1;
+
+ val = expand_expr (t, ret, VOIDmode, EXPAND_NORMAL);
+ if (val != ret)
+ emit_move_insn (ret, val);
+
+ emit_jump_insn (gen_jump (lab_over));
+ emit_barrier ();
+ emit_label (lab_false);
+ }
+
+ align = TYPE_ALIGN (type);
+ if (align < BITS_PER_WORD)
+ align = BITS_PER_WORD;
+ align /= BITS_PER_UNIT;
+
+ t = build (PLUS_EXPR, ptr_type_node, field_mem_ptr,
+ build_int_2 (align - 1, 0));
+ t = build (BIT_AND_EXPR, ptr_type_node, t, build_int_2 (-align, -1));
+
+ val = expand_expr (t, ret, VOIDmode, EXPAND_NORMAL);
+ if (val != ret)
+ emit_move_insn (ret, val);
+
+ t = fold (build (PLUS_EXPR, ptr_type_node,
+ make_tree (ptr_type_node, ret),
+ build_int_2 (int_size_in_bytes (type), 0)));
+ t = build (MODIFY_EXPR, ptr_type_node, field_mem_ptr, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ if (lab_over)
+ emit_label (lab_over);
+
+ return ret;
}