/* Subroutines for gcc2 for pdp11.
- Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001
- Free Software Foundation, Inc.
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2004, 2005,
+ 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at).
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC is free software; you can redistribute it and/or modify
+GCC 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)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
-GNU CC is distributed in the hope that it will be useful,
+GCC 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. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
-#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "function.h"
#include "recog.h"
#include "tree.h"
#include "expr.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
-
-/*
-#define FPU_REG_P(X) ((X)>=8 && (X)<14)
-#define CPU_REG_P(X) ((X)>=0 && (X)<8)
-*/
+#include "df.h"
/* this is the current value returned by the macro FIRST_PARM_OFFSET
defined in tm.h */
int current_first_parm_offset;
+/* Routines to encode/decode pdp11 floats */
+static void encode_pdp11_f (const struct real_format *fmt,
+ long *, const REAL_VALUE_TYPE *);
+static void decode_pdp11_f (const struct real_format *,
+ REAL_VALUE_TYPE *, const long *);
+static void encode_pdp11_d (const struct real_format *fmt,
+ long *, const REAL_VALUE_TYPE *);
+static void decode_pdp11_d (const struct real_format *,
+ REAL_VALUE_TYPE *, const long *);
+
+/* These two are taken from the corresponding vax descriptors
+ in real.c, changing only the encode/decode routine pointers. */
+const struct real_format pdp11_f_format =
+ {
+ encode_pdp11_f,
+ decode_pdp11_f,
+ 2,
+ 24,
+ 24,
+ -127,
+ 127,
+ 15,
+ 15,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ };
+
+const struct real_format pdp11_d_format =
+ {
+ encode_pdp11_d,
+ decode_pdp11_d,
+ 2,
+ 56,
+ 56,
+ -127,
+ 127,
+ 15,
+ 15,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ };
+
+static void
+encode_pdp11_f (const struct real_format *fmt ATTRIBUTE_UNUSED, long *buf,
+ const REAL_VALUE_TYPE *r)
+{
+ (*vax_f_format.encode) (fmt, buf, r);
+ buf[0] = ((buf[0] >> 16) & 0xffff) | ((buf[0] & 0xffff) << 16);
+}
+
+static void
+decode_pdp11_f (const struct real_format *fmt ATTRIBUTE_UNUSED,
+ REAL_VALUE_TYPE *r, const long *buf)
+{
+ long tbuf;
+ tbuf = ((buf[0] >> 16) & 0xffff) | ((buf[0] & 0xffff) << 16);
+ (*vax_f_format.decode) (fmt, r, &tbuf);
+}
+
+static void
+encode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED, long *buf,
+ const REAL_VALUE_TYPE *r)
+{
+ (*vax_d_format.encode) (fmt, buf, r);
+ buf[0] = ((buf[0] >> 16) & 0xffff) | ((buf[0] & 0xffff) << 16);
+ buf[1] = ((buf[1] >> 16) & 0xffff) | ((buf[1] & 0xffff) << 16);
+}
+
+static void
+decode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED,
+ REAL_VALUE_TYPE *r, const long *buf)
+{
+ long tbuf[2];
+ tbuf[0] = ((buf[0] >> 16) & 0xffff) | ((buf[0] & 0xffff) << 16);
+ tbuf[1] = ((buf[1] >> 16) & 0xffff) | ((buf[1] & 0xffff) << 16);
+ (*vax_d_format.decode) (fmt, r, tbuf);
+}
+
/* This is where the condition code register lives. */
/* rtx cc0_reg_rtx; - no longer needed? */
-static rtx find_addr_reg PARAMS ((rtx));
-static const char *singlemove_string PARAMS ((rtx *));
-static bool pdp11_assemble_integer PARAMS ((rtx, unsigned int, int));
-static void pdp11_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
-static void pdp11_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
+static bool pdp11_handle_option (size_t, const char *, int);
+static void pdp11_option_init_struct (struct gcc_options *);
+static rtx find_addr_reg (rtx);
+static const char *singlemove_string (rtx *);
+static bool pdp11_assemble_integer (rtx, unsigned int, int);
+static void pdp11_output_function_prologue (FILE *, HOST_WIDE_INT);
+static void pdp11_output_function_epilogue (FILE *, HOST_WIDE_INT);
+static bool pdp11_rtx_costs (rtx, int, int, int *, bool);
+static bool pdp11_return_in_memory (const_tree, const_tree);
+static rtx pdp11_function_value (const_tree, const_tree, bool);
+static rtx pdp11_libcall_value (enum machine_mode, const_rtx);
+static bool pdp11_function_value_regno_p (const unsigned int);
+static void pdp11_trampoline_init (rtx, tree, rtx);
+static rtx pdp11_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static void pdp11_function_arg_advance (CUMULATIVE_ARGS *,
+ enum machine_mode, const_tree, bool);
+static void pdp11_conditional_register_usage (void);
+
+/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */
+
+static const struct default_options pdp11_option_optimization_table[] =
+ {
+ { OPT_LEVELS_3_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_NONE, 0, NULL, 0 }
+ };
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_BYTE_OP
#undef TARGET_ASM_CLOSE_PAREN
#define TARGET_ASM_CLOSE_PAREN "]"
-struct gcc_target targetm = TARGET_INITIALIZER;
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS \
+ (MASK_FPU | MASK_45 | TARGET_UNIX_ASM_DEFAULT)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION pdp11_handle_option
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE pdp11_option_optimization_table
+#undef TARGET_OPTION_INIT_STRUCT
+#define TARGET_OPTION_INIT_STRUCT pdp11_option_init_struct
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS pdp11_rtx_costs
+
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG pdp11_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE pdp11_function_arg_advance
+
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY pdp11_return_in_memory
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE pdp11_function_value
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE pdp11_libcall_value
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P pdp11_function_value_regno_p
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT pdp11_trampoline_init
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD pdp11_secondary_reload
+
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST pdp11_register_move_cost
+
+#undef TARGET_PREFERRED_RELOAD_CLASS
+#define TARGET_PREFERRED_RELOAD_CLASS pdp11_preferred_reload_class
+
+#undef TARGET_PREFERRED_OUTPUT_RELOAD_CLASS
+#define TARGET_PREFERRED_OUTPUT_RELOAD_CLASS pdp11_preferred_output_reload_class
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P pdp11_legitimate_address_p
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE pdp11_conditional_register_usage
\f
-/* Nonzero if OP is a valid second operand for an arithmetic insn. */
+/* Implement TARGET_HANDLE_OPTION. */
-int
-arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+pdp11_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
+ int value ATTRIBUTE_UNUSED)
{
- return (register_operand (op, mode) || GET_CODE (op) == CONST_INT);
-}
+ switch (code)
+ {
+ case OPT_m10:
+ target_flags &= ~(MASK_40 | MASK_45);
+ return true;
-int
-const_immediate_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return (GET_CODE (op) == CONST_INT);
+ default:
+ return true;
+ }
}
-int
-immediate15_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return (GET_CODE (op) == CONST_INT && ((INTVAL (op) & 0x8000) == 0x0000));
-}
+/* Implement TARGET_OPTION_INIT_STRUCT. */
-int
-expand_shift_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static void
+pdp11_option_init_struct (struct gcc_options *opts)
{
- return (GET_CODE (op) == CONST_INT
- && abs (INTVAL(op)) > 1
- && abs (INTVAL(op)) <= 4);
+ opts->x_flag_finite_math_only = 0;
+ opts->x_flag_trapping_math = 0;
+ opts->x_flag_signaling_nans = 0;
}
/*
knowing which registers should not be saved even if used.
*/
-#ifdef TWO_BSD
-
static void
-pdp11_output_function_prologue (stream, size)
- FILE *stream;
- HOST_WIDE_INT size;
-{
- fprintf (stream, "\tjsr r5, csv\n");
- if (size)
- {
- fprintf (stream, "\t/*abuse empty parameter slot for locals!*/\n");
- if (size > 2)
- fprintf(stream, "\tsub $%d, sp\n", size - 2);
-
- }
-}
-
-#else /* !TWO_BSD */
-
-static void
-pdp11_output_function_prologue (stream, size)
- FILE *stream;
- HOST_WIDE_INT size;
+pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size)
{
HOST_WIDE_INT fsize = ((size) + 1) & ~1;
int regno;
int via_ac = -1;
fprintf (stream,
- "\n\t; /* function prologue %s*/\n", current_function_name);
+ "\n\t; /* function prologue %s*/\n",
+ current_function_name ());
/* if we are outputting code for main,
the switch FPU to right mode if TARGET_FPU */
if (frame_pointer_needed)
{
- fprintf(stream, "\tmov fp, -(sp)\n");
- fprintf(stream, "\tmov sp, fp\n");
+ fprintf(stream, "\tmov r5, -(sp)\n");
+ fprintf(stream, "\tmov sp, r5\n");
}
else
{
/* make frame */
if (fsize)
- fprintf (stream, "\tsub $%o, sp\n", fsize);
+ asm_fprintf (stream, "\tsub $%#wo, sp\n", fsize);
/* save CPU registers */
- for (regno = 0; regno < 8; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
+ for (regno = R0_REGNUM; regno <= PC_REGNUM; regno++)
+ if (df_regs_ever_live_p (regno) && ! call_used_regs[regno])
if (! ((regno == FRAME_POINTER_REGNUM)
&& frame_pointer_needed))
fprintf (stream, "\tmov %s, -(sp)\n", reg_names[regno]);
/* via_ac specifies the ac to use for saving ac4, ac5 */
via_ac = -1;
- for (regno = 8; regno < FIRST_PSEUDO_REGISTER ; regno++)
+ for (regno = AC0_REGNUM; regno <= AC5_REGNUM ; regno++)
{
/* ac0 - ac3 */
if (LOAD_FPU_REG_P(regno)
- && regs_ever_live[regno]
+ && df_regs_ever_live_p (regno)
&& ! call_used_regs[regno])
{
- fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[regno]);
+ fprintf (stream, "\tstd %s, -(sp)\n", reg_names[regno]);
via_ac = regno;
}
/* maybe make ac4, ac5 call used regs?? */
/* ac4 - ac5 */
if (NO_LOAD_FPU_REG_P(regno)
- && regs_ever_live[regno]
+ && df_regs_ever_live_p (regno)
&& ! call_used_regs[regno])
{
- if (via_ac == -1)
- abort();
-
- fprintf (stream, "\tfldd %s, %s\n", reg_names[regno], reg_names[via_ac]);
- fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[via_ac]);
+ gcc_assert (via_ac != -1);
+ fprintf (stream, "\tldd %s, %s\n",
+ reg_names[regno], reg_names[via_ac]);
+ fprintf (stream, "\tstd %s, -(sp)\n", reg_names[via_ac]);
}
}
fprintf (stream, "\t;/* end of prologue */\n\n");
}
-#endif /* !TWO_BSD */
-
/*
The function epilogue should not depend on the current stack pointer!
It should use the frame pointer only. This is mandatory because
maybe as option if you want to generate code for kernel mode? */
-#ifdef TWO_BSD
-
static void
-pdp11_output_function_epilogue (stream, size)
- FILE *stream;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
-{
- fprintf (stream, "\t/* SP ignored by cret? */\n");
- fprintf (stream, "\tjmp cret\n");
-}
-
-#else /* !TWO_BSD */
-
-static void
-pdp11_output_function_epilogue (stream, size)
- FILE *stream;
- HOST_WIDE_INT size;
+pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size)
{
HOST_WIDE_INT fsize = ((size) + 1) & ~1;
int i, j, k;
if (frame_pointer_needed)
{
/* hope this is safe - m68k does it also .... */
- regs_ever_live[FRAME_POINTER_REGNUM] = 0;
+ df_set_regs_ever_live (FRAME_POINTER_REGNUM, false);
- for (i =7, j = 0 ; i >= 0 ; i--)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ for (i = PC_REGNUM, j = 0 ; i >= 0 ; i--)
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
j++;
/* remember # of pushed bytes for CPU regs */
k = 2*j;
- for (i =7 ; i >= 0 ; i--)
- if (regs_ever_live[i] && ! call_used_regs[i])
- fprintf(stream, "\tmov %o(fp), %s\n",-fsize-2*j--, reg_names[i]);
+ /* change fp -> r5 due to the compile error on libgcc2.c */
+ for (i = PC_REGNUM ; i >= R0_REGNUM ; i--)
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+ fprintf(stream, "\tmov %#" HOST_WIDE_INT_PRINT "o(r5), %s\n",
+ (-fsize-2*j--)&0xffff, reg_names[i]);
/* get ACs */
- via_ac = FIRST_PSEUDO_REGISTER -1;
+ via_ac = AC5_REGNUM;
- for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ for (i = AC5_REGNUM; i >= AC0_REGNUM; i--)
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
{
via_ac = i;
k += 8;
}
- for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
+ for (i = AC5_REGNUM; i >= AC0_REGNUM; i--)
{
if (LOAD_FPU_REG_P(i)
- && regs_ever_live[i]
+ && df_regs_ever_live_p (i)
&& ! call_used_regs[i])
{
- fprintf(stream, "\tfldd %o(fp), %s\n", -fsize-k, reg_names[i]);
+ fprintf(stream, "\tldd %#" HOST_WIDE_INT_PRINT "o(r5), %s\n",
+ (-fsize-k)&0xffff, reg_names[i]);
k -= 8;
}
if (NO_LOAD_FPU_REG_P(i)
- && regs_ever_live[i]
+ && df_regs_ever_live_p (i)
&& ! call_used_regs[i])
{
- if (! LOAD_FPU_REG_P(via_ac))
- abort();
+ gcc_assert (LOAD_FPU_REG_P(via_ac));
- fprintf(stream, "\tfldd %o(fp), %s\n", -fsize-k, reg_names[via_ac]);
- fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]);
+ fprintf(stream, "\tldd %#" HOST_WIDE_INT_PRINT "o(r5), %s\n",
+ (-fsize-k)&0xffff, reg_names[via_ac]);
+ fprintf(stream, "\tstd %s, %s\n", reg_names[via_ac], reg_names[i]);
k -= 8;
}
}
- fprintf(stream, "\tmov fp, sp\n");
- fprintf (stream, "\tmov (sp)+, fp\n");
+ fprintf(stream, "\tmov r5, sp\n");
+ fprintf (stream, "\tmov (sp)+, r5\n");
}
else
{
- via_ac = FIRST_PSEUDO_REGISTER -1;
+ via_ac = AC5_REGNUM;
/* get ACs */
- for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
- if (regs_ever_live[i] && call_used_regs[i])
+ for (i = AC5_REGNUM; i >= AC0_REGNUM; i--)
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
via_ac = i;
- for (i = FIRST_PSEUDO_REGISTER; i > 7; i--)
+ for (i = AC5_REGNUM; i >= AC0_REGNUM; i--)
{
if (LOAD_FPU_REG_P(i)
- && regs_ever_live[i]
+ && df_regs_ever_live_p (i)
&& ! call_used_regs[i])
- fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[i]);
+ fprintf(stream, "\tldd (sp)+, %s\n", reg_names[i]);
if (NO_LOAD_FPU_REG_P(i)
- && regs_ever_live[i]
+ && df_regs_ever_live_p (i)
&& ! call_used_regs[i])
{
- if (! LOAD_FPU_REG_P(via_ac))
- abort();
+ gcc_assert (LOAD_FPU_REG_P(via_ac));
- fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[via_ac]);
- fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]);
+ fprintf(stream, "\tldd (sp)+, %s\n", reg_names[via_ac]);
+ fprintf(stream, "\tstd %s, %s\n", reg_names[via_ac], reg_names[i]);
}
}
- for (i=7; i >= 0; i--)
- if (regs_ever_live[i] && !call_used_regs[i])
+ for (i = PC_REGNUM; i >= 0; i--)
+ if (df_regs_ever_live_p (i) && !call_used_regs[i])
fprintf(stream, "\tmov (sp)+, %s\n", reg_names[i]);
if (fsize)
- fprintf((stream), "\tadd $%o, sp\n", fsize);
+ fprintf((stream), "\tadd $%#" HOST_WIDE_INT_PRINT "o, sp\n",
+ (fsize)&0xffff);
}
fprintf (stream, "\trts pc\n");
fprintf (stream, "\t;/* end of epilogue*/\n\n\n");
}
-#endif /* !TWO_BSD */
-
/* Return the best assembler insn template
for moving operands[1] into operands[0] as a fullword. */
static const char *
-singlemove_string (operands)
- rtx *operands;
+singlemove_string (rtx *operands)
{
if (operands[1] != const0_rtx)
return "mov %1,%0";
with operands OPERANDS. */
const char *
-output_move_double (operands)
- rtx *operands;
+output_move_double (rtx *operands)
{
enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
if (REG_P (operands[1]))
optype1 = REGOP;
- else if (CONSTANT_P (operands[1]))
+ else if (CONSTANT_P (operands[1])
#if 0
- || GET_CODE (operands[1]) == CONST_DOUBLE)
+ || GET_CODE (operands[1]) == CONST_DOUBLE
#endif
+ )
optype1 = CNSTOP;
else if (offsettable_memref_p (operands[1]))
optype1 = OFFSOP;
supposed to allow to happen. Abort if we get one,
because generating code for these cases is painful. */
- if (optype0 == RNDOP || optype1 == RNDOP)
- abort ();
+ gcc_assert (optype0 != RNDOP && optype1 != RNDOP);
/* If one operand is decrementing and one is incrementing
decrement the former register explicitly
latehalf[1] = GEN_INT (INTVAL(operands[1]) >> 16);
operands[1] = GEN_INT (INTVAL(operands[1]) & 0xff);
}
- else if (GET_CODE (operands[1]) == CONST_DOUBLE)
- {
- /* immediate 32 bit values not allowed */
- abort();
- }
+ else
+ /* immediate 32-bit values not allowed */
+ gcc_assert (GET_CODE (operands[1]) != CONST_DOUBLE);
}
else
latehalf[1] = operands[1];
with operands OPERANDS. */
const char *
-output_move_quad (operands)
- rtx *operands;
+output_move_quad (rtx *operands)
{
enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
rtx addreg0 = 0, addreg1 = 0;
- output_asm_insn(";; movdi/df: %1 -> %0", operands);
+ output_asm_insn(";/* movdi/df: %1 -> %0 */", operands);
if (REG_P (operands[0]))
optype0 = REGOP;
supposed to allow to happen. Abort if we get one,
because generating code for these cases is painful. */
- if (optype0 == RNDOP || optype1 == RNDOP)
- abort ();
-
- /* check if we move a CPU reg to an FPU reg, or vice versa! */
- if (optype0 == REGOP && optype1 == REGOP)
- /* bogus - 64 bit cannot reside in CPU! */
- if (CPU_REG_P(REGNO(operands[0]))
- || CPU_REG_P (REGNO(operands[1])))
- abort();
+ gcc_assert (optype0 != RNDOP && optype1 != RNDOP);
if (optype0 == REGOP || optype1 == REGOP)
{
{
if (GET_CODE(operands[1]) == CONST_DOUBLE)
{
- union { double d; int i[2]; } u;
- u.i[0] = CONST_DOUBLE_LOW (operands[1]);
- u.i[1] = CONST_DOUBLE_HIGH (operands[1]);
-
- if (u.d == 0.0)
+ REAL_VALUE_TYPE r;
+ REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
+
+ if (REAL_VALUES_EQUAL (r, dconst0))
return "{clrd|clrf} %0";
}
{
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
- /* floats only. not yet supported!
-
- -- compute it into PDP float format, - internally,
- just use IEEE and ignore possible problems ;-)
-
- we might get away with it !!!! */
-
- abort();
-
-#ifndef HOST_WORDS_BIG_ENDIAN
- latehalf[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
- operands[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
-#else /* HOST_WORDS_BIG_ENDIAN */
- latehalf[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
- operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
-#endif /* HOST_WORDS_BIG_ENDIAN */
+ REAL_VALUE_TYPE r;
+ long dval[2];
+ REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
+ REAL_VALUE_TO_TARGET_DOUBLE (r, dval);
+ latehalf[1] = GEN_INT (dval[1]);
+ operands[1] = GEN_INT (dval[0]);
}
else if (GET_CODE(operands[1]) == CONST_INT)
{
- latehalf[1] = GEN_INT (0);
+ latehalf[1] = const0_rtx;
}
else
- abort();
+ gcc_unreachable ();
}
else
latehalf[1] = operands[1];
ADDR can be effectively incremented by incrementing REG. */
static rtx
-find_addr_reg (addr)
- rtx addr;
+find_addr_reg (rtx addr)
{
while (GET_CODE (addr) == PLUS)
{
\f
/* Output an ascii string. */
void
-output_ascii (file, p, size)
- FILE *file;
- const char *p;
- int size;
+output_ascii (FILE *file, const char *p, int size)
{
int i;
register int c = p[i];
if (c < 0)
c += 256;
- fprintf (file, "%o", c);
+ fprintf (file, "%#o", c);
if (i < size - 1)
putc (',', file);
}
}
-/* --- stole from out-vax, needs changes */
-
void
-print_operand_address (file, addr)
- FILE *file;
- register rtx addr;
+print_operand_address (FILE *file, register rtx addr)
{
- register rtx reg1, reg2, breg, ireg;
+ register rtx breg;
rtx offset;
-
+ int again = 0;
+
retry:
switch (GET_CODE (addr))
else
fprintf (file, "@");
addr = XEXP (addr, 0);
+ again = 1;
goto retry;
case REG:
fprintf (file, "(%s)", reg_names[REGNO (addr)]);
break;
+ case PRE_MODIFY:
case PRE_DEC:
fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]);
break;
+ case POST_MODIFY:
case POST_INC:
fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]);
break;
case PLUS:
- reg1 = 0; reg2 = 0;
- ireg = 0; breg = 0;
+ breg = 0;
offset = 0;
if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
|| GET_CODE (XEXP (addr, 0)) == MEM)
}
if (GET_CODE (addr) != PLUS)
;
- else if (GET_CODE (XEXP (addr, 0)) == MULT)
- {
- reg1 = XEXP (addr, 0);
- addr = XEXP (addr, 1);
- }
- else if (GET_CODE (XEXP (addr, 1)) == MULT)
- {
- reg1 = XEXP (addr, 1);
- addr = XEXP (addr, 0);
- }
else if (GET_CODE (XEXP (addr, 0)) == REG)
{
- reg1 = XEXP (addr, 0);
+ breg = XEXP (addr, 0);
addr = XEXP (addr, 1);
}
else if (GET_CODE (XEXP (addr, 1)) == REG)
{
- reg1 = XEXP (addr, 1);
+ breg = XEXP (addr, 1);
addr = XEXP (addr, 0);
}
- if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
+ if (GET_CODE (addr) == REG)
{
- if (reg1 == 0)
- reg1 = addr;
- else
- reg2 = addr;
+ gcc_assert (breg == 0);
+ breg = addr;
addr = 0;
}
if (offset != 0)
{
- if (addr != 0) abort ();
+ gcc_assert (addr == 0);
addr = offset;
}
- if (reg1 != 0 && GET_CODE (reg1) == MULT)
- {
- breg = reg2;
- ireg = reg1;
- }
- else if (reg2 != 0 && GET_CODE (reg2) == MULT)
- {
- breg = reg1;
- ireg = reg2;
- }
- else if (reg2 != 0 || GET_CODE (addr) == MEM)
- {
- breg = reg2;
- ireg = reg1;
- }
- else
- {
- breg = reg1;
- ireg = reg2;
- }
if (addr != 0)
- output_address (addr);
+ output_addr_const_pdp11 (file, addr);
if (breg != 0)
{
- if (GET_CODE (breg) != REG)
- abort ();
+ gcc_assert (GET_CODE (breg) == REG);
fprintf (file, "(%s)", reg_names[REGNO (breg)]);
}
- if (ireg != 0)
- {
- if (GET_CODE (ireg) == MULT)
- ireg = XEXP (ireg, 0);
- if (GET_CODE (ireg) != REG)
- abort ();
- abort();
- fprintf (file, "[%s]", reg_names[REGNO (ireg)]);
- }
break;
default:
+ if (!again && GET_CODE (addr) == CONST_INT)
+ {
+ /* Absolute (integer number) address. */
+ if (!TARGET_UNIX_ASM)
+ fprintf (file, "@$");
+ }
output_addr_const_pdp11 (file, addr);
}
}
pdp-specific version of output_addr_const. */
static bool
-pdp11_assemble_integer (x, size, aligned_p)
- rtx x;
- unsigned int size;
- int aligned_p;
+pdp11_assemble_integer (rtx x, unsigned int size, int aligned_p)
{
if (aligned_p)
switch (size)
{
case 1:
fprintf (asm_out_file, "\t.byte\t");
- output_addr_const_pdp11 (asm_out_file, x);
+ output_addr_const_pdp11 (asm_out_file, GEN_INT (INTVAL (x) & 0xff));
+;
fprintf (asm_out_file, " /* char */\n");
return true;
/* NO MUL GEN LFPU NLFPU FPU ALL */
/* NO */ { 0, 0, 0, 0, 0, 0, 0},
-/* MUL */ { 0, 2, 2, 10, 22, 22, 22},
-/* GEN */ { 0, 2, 2, 10, 22, 22, 22},
-/* LFPU */ { 0, 10, 10, 2, 2, 2, 10},
-/* NLFPU */ { 0, 22, 22, 2, 2, 2, 22},
-/* FPU */ { 0, 22, 22, 2, 2, 2, 22},
-/* ALL */ { 0, 22, 22, 10, 22, 22, 22}
+/* MUL */ { 0, 2, 2, 22, 22, 22, 22},
+/* GEN */ { 0, 2, 2, 22, 22, 22, 22},
+/* LFPU */ { 0, 22, 22, 2, 2, 2, 22},
+/* NLFPU */ { 0, 22, 22, 2, 10, 10, 22},
+/* FPU */ { 0, 22, 22, 2, 10, 10, 22},
+/* ALL */ { 0, 22, 22, 22, 22, 22, 22}
} ;
/* -- note that some moves are tremendously expensive,
because they require lots of tricks! do we have to
charge the costs incurred by secondary reload class
- -- as we do here with 22 -- or not ? */
+ -- as we do here with 10 -- or not ? */
-int
-register_move_cost(c1, c2)
- enum reg_class c1, c2;
+static int
+pdp11_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t c1, reg_class_t c2)
{
return move_costs[(int)c1][(int)c2];
}
+static bool
+pdp11_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total,
+ bool speed ATTRIBUTE_UNUSED)
+{
+ switch (code)
+ {
+ case CONST_INT:
+ if (INTVAL (x) == 0 || INTVAL (x) == -1 || INTVAL (x) == 1)
+ {
+ *total = 0;
+ return true;
+ }
+ /* FALLTHRU */
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ /* Twice as expensive as REG. */
+ *total = 2;
+ return true;
+
+ case CONST_DOUBLE:
+ /* Twice (or 4 times) as expensive as 16 bit. */
+ *total = 4;
+ return true;
+
+ case MULT:
+ /* ??? There is something wrong in MULT because MULT is not
+ as cheap as total = 2 even if we can shift! */
+ /* If optimizing for size make mult etc cheap, but not 1, so when
+ in doubt the faster insn is chosen. */
+ if (optimize_size)
+ *total = COSTS_N_INSNS (2);
+ else
+ *total = COSTS_N_INSNS (11);
+ return false;
+
+ case DIV:
+ if (optimize_size)
+ *total = COSTS_N_INSNS (2);
+ else
+ *total = COSTS_N_INSNS (25);
+ return false;
+
+ case MOD:
+ if (optimize_size)
+ *total = COSTS_N_INSNS (2);
+ else
+ *total = COSTS_N_INSNS (26);
+ return false;
+
+ case ABS:
+ /* Equivalent to length, so same for optimize_size. */
+ *total = COSTS_N_INSNS (3);
+ return false;
+
+ case ZERO_EXTEND:
+ /* Only used for qi->hi. */
+ *total = COSTS_N_INSNS (1);
+ return false;
+
+ case SIGN_EXTEND:
+ if (GET_MODE (x) == HImode)
+ *total = COSTS_N_INSNS (1);
+ else if (GET_MODE (x) == SImode)
+ *total = COSTS_N_INSNS (6);
+ else
+ *total = COSTS_N_INSNS (2);
+ return false;
+
+ case ASHIFT:
+ case LSHIFTRT:
+ case ASHIFTRT:
+ if (optimize_size)
+ *total = COSTS_N_INSNS (1);
+ else if (GET_MODE (x) == QImode)
+ {
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ *total = COSTS_N_INSNS (8); /* worst case */
+ else
+ *total = COSTS_N_INSNS (INTVAL (XEXP (x, 1)));
+ }
+ else if (GET_MODE (x) == HImode)
+ {
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ if (abs (INTVAL (XEXP (x, 1))) == 1)
+ *total = COSTS_N_INSNS (1);
+ else
+ *total = COSTS_N_INSNS (2.5 + 0.5 * INTVAL (XEXP (x, 1)));
+ }
+ else
+ *total = COSTS_N_INSNS (10); /* worst case */
+ }
+ else if (GET_MODE (x) == SImode)
+ {
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ *total = COSTS_N_INSNS (2.5 + 0.5 * INTVAL (XEXP (x, 1)));
+ else /* worst case */
+ *total = COSTS_N_INSNS (18);
+ }
+ return false;
+
+ default:
+ return false;
+ }
+}
+
const char *
-output_jump(pos, neg, length)
- const char *pos, *neg;
- int length;
+output_jump (enum rtx_code code, int inv, int length)
{
static int x = 0;
static char buf[1000];
+ const char *pos, *neg;
+
+ switch (code)
+ {
+ case EQ: pos = "beq", neg = "bne"; break;
+ case NE: pos = "bne", neg = "beq"; break;
+ case GT: pos = "bgt", neg = "ble"; break;
+ case GTU: pos = "bhi", neg = "blos"; break;
+ case LT: pos = "blt", neg = "bge"; break;
+ case LTU: pos = "blo", neg = "bhis"; break;
+ case GE: pos = "bge", neg = "blt"; break;
+ case GEU: pos = "bhis", neg = "blo"; break;
+ case LE: pos = "ble", neg = "bgt"; break;
+ case LEU: pos = "blos", neg = "bhi"; break;
+ default: gcc_unreachable ();
+ }
#if 0
/* currently we don't need this, because the tstdf and cmpdf
switch (length)
{
- case 1:
+ case 2:
- strcpy(buf, pos);
- strcat(buf, " %l0");
+ sprintf(buf, "%s %%l1", inv ? neg : pos);
return buf;
- case 3:
+ case 6:
- sprintf(buf, "%s JMP_%d\n\tjmp %%l0\nJMP_%d:", neg, x, x);
+ sprintf(buf, "%s JMP_%d\n\tjmp %%l1\nJMP_%d:", inv ? pos : neg, x, x);
x++;
default:
- abort();
+ gcc_unreachable ();
}
}
void
-notice_update_cc_on_set(exp, insn)
- rtx exp;
- rtx insn ATTRIBUTE_UNUSED;
+notice_update_cc_on_set(rtx exp, rtx insn ATTRIBUTE_UNUSED)
{
if (GET_CODE (SET_DEST (exp)) == CC0)
{
int
-simple_memory_operand(op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+simple_memory_operand(rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
rtx addr;
const char *
-output_block_move(operands)
- rtx *operands;
+output_block_move(rtx *operands)
{
static int count = 0;
char buf[200];
return "";
}
-/* for future use */
+/* This function checks whether a real value can be encoded as
+ a literal, i.e., addressing mode 27. In that mode, real values
+ are one word values, so the remaining 48 bits have to be zero. */
int
-comparison_operator_index(op)
- rtx op;
+legitimate_const_double_p (rtx address)
+{
+ REAL_VALUE_TYPE r;
+ long sval[2];
+ REAL_VALUE_FROM_CONST_DOUBLE (r, address);
+ REAL_VALUE_TO_TARGET_DOUBLE (r, sval);
+ if ((sval[0] & 0xffff) == 0 && sval[1] == 0)
+ return 1;
+ return 0;
+}
+
+/* Implement CANNOT_CHANGE_MODE_CLASS. */
+bool
+pdp11_cannot_change_mode_class (enum machine_mode from,
+ enum machine_mode to,
+ enum reg_class rclass)
+{
+ /* Also, FPU registers contain a whole float value and the parts of
+ it are not separately accessible.
+
+ So we disallow all mode changes involving FPRs. */
+ if (FLOAT_MODE_P (from) != FLOAT_MODE_P (to))
+ return true;
+
+ return reg_classes_intersect_p (FPU_REGS, rclass);
+}
+
+/* TARGET_PREFERRED_RELOAD_CLASS
+
+ Given an rtx X being reloaded into a reg required to be
+ in class CLASS, return the class of reg to actually use.
+ In general this is just CLASS; but on some machines
+ in some cases it is preferable to use a more restrictive class.
+
+loading is easier into LOAD_FPU_REGS than FPU_REGS! */
+
+static reg_class_t
+pdp11_preferred_reload_class (rtx x, reg_class_t rclass)
{
- switch (GET_CODE(op))
+ if (rclass == FPU_REGS)
+ return LOAD_FPU_REGS;
+ if (rclass == ALL_REGS)
{
- case NE:
- return 0;
-
- case EQ:
- return 1;
-
- case GE:
- return 2;
-
- case GT:
- return 3;
-
- case LE:
- return 4;
-
- case LT:
- return 5;
-
- case GEU:
- return 6;
-
- case GTU:
- return 7;
+ if (FLOAT_MODE_P (GET_MODE (x)))
+ return LOAD_FPU_REGS;
+ else
+ return GENERAL_REGS;
+ }
+ return rclass;
+}
- case LEU:
- return 8;
-
- case LTU:
- return 9;
-
- default:
- return -1;
+/* TARGET_PREFERRED_OUTPUT_RELOAD_CLASS
+
+ Given an rtx X being reloaded into a reg required to be
+ in class CLASS, return the class of reg to actually use.
+ In general this is just CLASS; but on some machines
+ in some cases it is preferable to use a more restrictive class.
+
+loading is easier into LOAD_FPU_REGS than FPU_REGS! */
+
+static reg_class_t
+pdp11_preferred_output_reload_class (rtx x, reg_class_t rclass)
+{
+ if (rclass == FPU_REGS)
+ return LOAD_FPU_REGS;
+ if (rclass == ALL_REGS)
+ {
+ if (FLOAT_MODE_P (GET_MODE (x)))
+ return LOAD_FPU_REGS;
+ else
+ return GENERAL_REGS;
}
-}
-
-/* tests whether the rtx is a comparison operator */
-int
-comp_operator (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+ return rclass;
+}
+
+
+/* TARGET_SECONDARY_RELOAD.
+
+ FPU registers AC4 and AC5 (class NO_LOAD_FPU_REGS) require an
+ intermediate register (AC0-AC3: LOAD_FPU_REGS). Everything else
+ can be loade/stored directly. */
+static reg_class_t
+pdp11_secondary_reload (bool in_p ATTRIBUTE_UNUSED,
+ rtx x,
+ reg_class_t reload_class,
+ enum machine_mode reload_mode ATTRIBUTE_UNUSED,
+ secondary_reload_info *sri ATTRIBUTE_UNUSED)
{
- return comparison_operator_index(op) >= 0;
+ if (reload_class != NO_LOAD_FPU_REGS || GET_CODE (x) != REG ||
+ REGNO_REG_CLASS (REGNO (x)) == LOAD_FPU_REGS)
+ return NO_REGS;
+
+ return LOAD_FPU_REGS;
}
-
-int
-legitimate_address_p (mode, address)
- enum machine_mode mode;
- rtx address;
+/* Target routine to check if register to register move requires memory.
+
+ The answer is yes if we're going between general register and FPU
+ registers. The mode doesn't matter in making this check.
+*/
+bool
+pdp11_secondary_memory_needed (reg_class_t c1, reg_class_t c2,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ int fromfloat = (c1 == LOAD_FPU_REGS || c1 == NO_LOAD_FPU_REGS ||
+ c1 == FPU_REGS);
+ int tofloat = (c2 == LOAD_FPU_REGS || c2 == NO_LOAD_FPU_REGS ||
+ c2 == FPU_REGS);
+
+ return (fromfloat != tofloat);
+}
+
+/* TARGET_LEGITIMATE_ADDRESS_P recognizes an RTL expression
+ that is a valid memory address for an instruction.
+ The MODE argument is the machine mode for the MEM expression
+ that wants to use this address.
+
+*/
+
+static bool
+pdp11_legitimate_address_p (enum machine_mode mode,
+ rtx operand, bool strict)
{
-/* #define REG_OK_STRICT */
- GO_IF_LEGITIMATE_ADDRESS(mode, address, win);
+ rtx xfoob;
+
+ /* accept @#address */
+ if (CONSTANT_ADDRESS_P (operand))
+ return true;
- return 0;
+ switch (GET_CODE (operand))
+ {
+ case REG:
+ /* accept (R0) */
+ return !strict || REGNO_OK_FOR_BASE_P (REGNO (operand));
- win:
- return 1;
+ case PLUS:
+ /* accept X(R0) */
+ return GET_CODE (XEXP (operand, 0)) == REG
+ && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (operand, 0))))
+ && CONSTANT_ADDRESS_P (XEXP (operand, 1));
+
+ case PRE_DEC:
+ /* accept -(R0) */
+ return GET_CODE (XEXP (operand, 0)) == REG
+ && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (operand, 0))));
-/* #undef REG_OK_STRICT */
+ case POST_INC:
+ /* accept (R0)+ */
+ return GET_CODE (XEXP (operand, 0)) == REG
+ && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (operand, 0))));
+
+ case PRE_MODIFY:
+ /* accept -(SP) -- which uses PRE_MODIFY for byte mode */
+ return GET_CODE (XEXP (operand, 0)) == REG
+ && REGNO (XEXP (operand, 0)) == STACK_POINTER_REGNUM
+ && GET_CODE ((xfoob = XEXP (operand, 1))) == PLUS
+ && GET_CODE (XEXP (xfoob, 0)) == REG
+ && REGNO (XEXP (xfoob, 0)) == STACK_POINTER_REGNUM
+ && CONSTANT_P (XEXP (xfoob, 1))
+ && INTVAL (XEXP (xfoob,1)) == -2;
+
+ case POST_MODIFY:
+ /* accept (SP)+ -- which uses POST_MODIFY for byte mode */
+ return GET_CODE (XEXP (operand, 0)) == REG
+ && REGNO (XEXP (operand, 0)) == STACK_POINTER_REGNUM
+ && GET_CODE ((xfoob = XEXP (operand, 1))) == PLUS
+ && GET_CODE (XEXP (xfoob, 0)) == REG
+ && REGNO (XEXP (xfoob, 0)) == STACK_POINTER_REGNUM
+ && CONSTANT_P (XEXP (xfoob, 1))
+ && INTVAL (XEXP (xfoob,1)) == 2;
+
+ case MEM:
+ /* handle another level of indirection ! */
+ xfoob = XEXP (operand, 0);
+
+ /* (MEM:xx (MEM:xx ())) is not valid for SI, DI and currently
+ also forbidden for float, because we have to handle this
+ in output_move_double and/or output_move_quad() - we could
+ do it, but currently it's not worth it!!!
+ now that DFmode cannot go into CPU register file,
+ maybe I should allow float ...
+ but then I have to handle memory-to-memory moves in movdf ?? */
+ if (GET_MODE_BITSIZE(mode) > 16)
+ return false;
+
+ /* accept @address */
+ if (CONSTANT_ADDRESS_P (xfoob))
+ return true;
+
+ switch (GET_CODE (xfoob))
+ {
+ case REG:
+ /* accept @(R0) - which is @0(R0) */
+ return !strict || REGNO_OK_FOR_BASE_P(REGNO (xfoob));
+
+ case PLUS:
+ /* accept @X(R0) */
+ return GET_CODE (XEXP (xfoob, 0)) == REG
+ && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (xfoob, 0))))
+ && CONSTANT_ADDRESS_P (XEXP (xfoob, 1));
+
+ case PRE_DEC:
+ /* accept @-(R0) */
+ return GET_CODE (XEXP (xfoob, 0)) == REG
+ && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (xfoob, 0))));
+
+ case POST_INC:
+ /* accept @(R0)+ */
+ return GET_CODE (XEXP (xfoob, 0)) == REG
+ && (!strict || REGNO_OK_FOR_BASE_P (REGNO (XEXP (xfoob, 0))));
+
+ default:
+ /* anything else is invalid */
+ return false;
+ }
+
+ default:
+ /* anything else is invalid */
+ return false;
+ }
}
+/* Return the class number of the smallest class containing
+ reg number REGNO. */
+enum reg_class
+pdp11_regno_reg_class (int regno)
+{
+ if (regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)
+ return GENERAL_REGS;
+ else if (regno > AC3_REGNUM)
+ return NO_LOAD_FPU_REGS;
+ else if (regno >= AC0_REGNUM)
+ return LOAD_FPU_REGS;
+ else if (regno & 1)
+ return MUL_REGS;
+ else
+ return GENERAL_REGS;
+}
+
+
+static int
+pdp11_sp_frame_offset (void)
+{
+ int offset = 0, regno;
+ offset = get_frame_size();
+ for (regno = 0; regno <= PC_REGNUM; regno++)
+ if (df_regs_ever_live_p (regno) && ! call_used_regs[regno])
+ offset += 2;
+ for (regno = AC0_REGNUM; regno <= AC5_REGNUM; regno++)
+ if (df_regs_ever_live_p (regno) && ! call_used_regs[regno])
+ offset += 8;
+
+ return offset;
+}
+
+/* Return the offset between two registers, one to be eliminated, and the other
+ its replacement, at the start of a routine. */
+
+int
+pdp11_initial_elimination_offset (int from, int to)
+{
+ int spoff;
+
+ if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+ return 4;
+ else if (from == FRAME_POINTER_REGNUM
+ && to == HARD_FRAME_POINTER_REGNUM)
+ return 0;
+ else
+ {
+ gcc_assert (to == STACK_POINTER_REGNUM);
+
+ /* Get the size of the register save area. */
+ spoff = pdp11_sp_frame_offset ();
+ if (from == FRAME_POINTER_REGNUM)
+ return spoff;
+
+ gcc_assert (from == ARG_POINTER_REGNUM);
+
+ /* If there is a frame pointer, that is saved too. */
+ if (frame_pointer_needed)
+ spoff += 2;
+
+ /* Account for the saved PC in the function call. */
+ return spoff + 2;
+ }
+}
/* A copy of output_addr_const modified for pdp11 expression syntax.
output_addr_const also gets called for %cDIGIT and %nDIGIT, which we don't
So this copy should get called whenever needed.
*/
void
-output_addr_const_pdp11 (file, x)
- FILE *file;
- rtx x;
+output_addr_const_pdp11 (FILE *file, rtx x)
{
char buf[256];
switch (GET_CODE (x))
{
case PC:
- if (flag_pic)
- putc ('.', file);
- else
- abort ();
+ gcc_assert (flag_pic);
+ putc ('.', file);
break;
case SYMBOL_REF:
break;
case CONST_INT:
- /* Should we check for constants which are too big? Maybe cutting
- them off to 16 bits is OK? */
- fprintf (file, "%ho", (unsigned short) INTVAL (x));
+ fprintf (file, "%#o", (int) INTVAL (x) & 0xffff);
break;
case CONST:
if (GET_MODE (x) == VOIDmode)
{
/* We can use %o if the number is one word and positive. */
- if (CONST_DOUBLE_HIGH (x))
- abort (); /* Should we just silently drop the high part? */
- else
- fprintf (file, "%ho", (unsigned short) CONST_DOUBLE_LOW (x));
+ gcc_assert (!CONST_DOUBLE_HIGH (x));
+ fprintf (file, "%#ho", (unsigned short) CONST_DOUBLE_LOW (x));
}
else
/* We can't handle floating point constants;
break;
case PLUS:
- /* Some assemblers need integer constants to appear last (eg masm). */
+ /* Some assemblers need integer constants to appear last (e.g. masm). */
if (GET_CODE (XEXP (x, 0)) == CONST_INT)
{
output_addr_const_pdp11 (file, XEXP (x, 1));
output_operand_lossage ("invalid expression as operand");
}
}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+static bool
+pdp11_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
+{
+ /* Integers 32 bits and under, and scalar floats (if FPU), are returned
+ in registers. The rest go into memory. */
+ return (TYPE_MODE (type) == DImode
+ || (FLOAT_MODE_P (TYPE_MODE (type)) && ! TARGET_AC0)
+ || TREE_CODE (type) == VECTOR_TYPE
+ || COMPLEX_MODE_P (TYPE_MODE (type)));
+}
+
+/* Worker function for TARGET_FUNCTION_VALUE.
+
+ On the pdp11 the value is found in R0 (or ac0??? not without FPU!!!! ) */
+
+static rtx
+pdp11_function_value (const_tree valtype,
+ const_tree fntype_or_decl ATTRIBUTE_UNUSED,
+ bool outgoing ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (TYPE_MODE (valtype),
+ BASE_RETURN_VALUE_REG(TYPE_MODE(valtype)));
+}
+
+/* Worker function for TARGET_LIBCALL_VALUE. */
+
+static rtx
+pdp11_libcall_value (enum machine_mode mode,
+ const_rtx fun ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (mode, BASE_RETURN_VALUE_REG(mode));
+}
+
+/* Worker function for TARGET_FUNCTION_VALUE_REGNO_P.
+
+ On the pdp, the first "output" reg is the only register thus used.
+
+ maybe ac0 ? - as option someday! */
+
+static bool
+pdp11_function_value_regno_p (const unsigned int regno)
+{
+ return (regno == RETVAL_REGNUM) || (TARGET_AC0 && (regno == AC0_REGNUM));
+}
+
+/* Worker function for TARGET_TRAMPOLINE_INIT.
+
+ trampoline - how should i do it in separate i+d ?
+ have some allocate_trampoline magic???
+
+ the following should work for shared I/D:
+
+ MOV #STATIC, $4 01270Y 0x0000 <- STATIC; Y = STATIC_CHAIN_REGNUM
+ JMP @#FUNCTION 000137 0x0000 <- FUNCTION
+*/
+
+static void
+pdp11_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
+{
+ rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+ rtx mem;
+
+ gcc_assert (!TARGET_SPLIT);
+
+ mem = adjust_address (m_tramp, HImode, 0);
+ emit_move_insn (mem, GEN_INT (012700+STATIC_CHAIN_REGNUM));
+ mem = adjust_address (m_tramp, HImode, 2);
+ emit_move_insn (mem, chain_value);
+ mem = adjust_address (m_tramp, HImode, 4);
+ emit_move_insn (mem, GEN_INT (000137));
+ emit_move_insn (mem, fnaddr);
+}
+
+/* Worker function for TARGET_FUNCTION_ARG.
+
+ Determine where to put an argument to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis). */
+
+static rtx
+pdp11_function_arg (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ const_tree type ATTRIBUTE_UNUSED,
+ bool named ATTRIBUTE_UNUSED)
+{
+ return NULL_RTX;
+}
+
+/* Worker function for TARGET_FUNCTION_ARG_ADVANCE.
+
+ Update the data in CUM to advance over an argument of mode MODE and
+ data type TYPE. (TYPE is null for libcalls where that information
+ may not be available.) */
+
+static void
+pdp11_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ *cum += (mode != BLKmode
+ ? GET_MODE_SIZE (mode)
+ : int_size_in_bytes (type));
+}
+
+/* Make sure everything's fine if we *don't* have an FPU.
+ This assumes that putting a register in fixed_regs will keep the
+ compiler's mitts completely off it. We don't bother to zero it out
+ of register classes. Also fix incompatible register naming with
+ the UNIX assembler. */
+
+static void
+pdp11_conditional_register_usage (void)
+{
+ int i;
+ HARD_REG_SET x;
+ if (!TARGET_FPU)
+ {
+ COPY_HARD_REG_SET (x, reg_class_contents[(int)FPU_REGS]);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++ )
+ if (TEST_HARD_REG_BIT (x, i))
+ fixed_regs[i] = call_used_regs[i] = 1;
+ }
+
+ if (TARGET_AC0)
+ call_used_regs[AC0_REGNUM] = 1;
+ if (TARGET_UNIX_ASM)
+ {
+ /* Change names of FPU registers for the UNIX assembler. */
+ reg_names[8] = "fr0";
+ reg_names[9] = "fr1";
+ reg_names[10] = "fr2";
+ reg_names[11] = "fr3";
+ reg_names[12] = "fr4";
+ reg_names[13] = "fr5";
+ }
+}
+
+struct gcc_target targetm = TARGET_INITIALIZER;