/* Subroutines for gcc2 for pdp11.
- Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999
- 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 "insn-flags.h"
#include "function.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
+#include "tree.h"
+#include "expr.h"
+#include "diagnostic-core.h"
#include "tm_p.h"
-
-/*
-#define FPU_REG_P(X) ((X)>=8 && (X)<14)
-#define CPU_REG_P(X) ((X)>=0 && (X)<8)
-*/
+#include "target.h"
+#include "target-def.h"
+#include "df.h"
/* this is the current value returned by the macro FIRST_PARM_OFFSET
defined in tm.h */
int current_first_parm_offset;
-/* 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 *));
+/* 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);
+}
-/* Nonzero if OP is a valid second operand for an arithmetic insn. */
+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);
+}
-int
-arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static void
+encode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED, long *buf,
+ const REAL_VALUE_TYPE *r)
{
- return (register_operand (op, mode) || GET_CODE (op) == CONST_INT);
+ (*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);
}
-int
-const_immediate_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static void
+decode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED,
+ REAL_VALUE_TYPE *r, const long *buf)
{
- return (GET_CODE (op) == CONST_INT);
+ 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);
}
-int
-immediate15_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+/* This is where the condition code register lives. */
+/* rtx cc0_reg_rtx; - no longer needed? */
+
+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
+#define TARGET_ASM_BYTE_OP NULL
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP NULL
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP NULL
+#undef TARGET_ASM_INTEGER
+#define TARGET_ASM_INTEGER pdp11_assemble_integer
+
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE pdp11_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE pdp11_output_function_epilogue
+
+#undef TARGET_ASM_OPEN_PAREN
+#define TARGET_ASM_OPEN_PAREN "["
+#undef TARGET_ASM_CLOSE_PAREN
+#define TARGET_ASM_CLOSE_PAREN "]"
+
+#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
+/* Implement TARGET_HANDLE_OPTION. */
+
+static bool
+pdp11_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
+ int value ATTRIBUTE_UNUSED)
{
- return (GET_CODE (op) == CONST_INT && ((INTVAL (op) & 0x8000) == 0x0000));
+ switch (code)
+ {
+ case OPT_m10:
+ target_flags &= ~(MASK_40 | MASK_45);
+ return true;
+
+ default:
+ return true;
+ }
}
-int
-expand_shift_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+/* Implement TARGET_OPTION_INIT_STRUCT. */
+
+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.
*/
-void
-output_function_prologue(stream, size)
- FILE *stream;
- int size;
+static void
+pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size)
{
- int fsize = ((size) + 1) & ~1;
+ HOST_WIDE_INT fsize = ((size) + 1) & ~1;
int regno;
-
int via_ac = -1;
-
- fprintf (stream, "\n\t; /* function prologue %s*/\n", current_function_name);
+
+ fprintf (stream,
+ "\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 ( (strcmp ("main", current_function_name) == 0)
- && TARGET_FPU)
+ if (MAIN_NAME_P (DECL_NAME (current_function_decl)) && TARGET_FPU)
{
- fprintf(stream, "\t;/* switch cpu to double float, single integer */\n");
+ fprintf(stream,
+ "\t;/* switch cpu to double float, single integer */\n");
fprintf(stream, "\tsetd\n");
fprintf(stream, "\tseti\n\n");
}
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]);
}
}
maybe as option if you want to generate code for kernel mode? */
-
-void
-output_function_epilogue(stream, size)
- FILE *stream;
- int size;
+static void
+pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size)
{
- int fsize = ((size) + 1) & ~1;
+ HOST_WIDE_INT fsize = ((size) + 1) & ~1;
int i, j, k;
int via_ac;
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");
}
-
+
/* 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
if (optype0 == REGOP)
latehalf[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
else if (optype0 == OFFSOP)
- latehalf[0] = adj_offsettable_operand (operands[0], 2);
+ latehalf[0] = adjust_address (operands[0], HImode, 2);
else
latehalf[0] = operands[0];
if (optype1 == REGOP)
latehalf[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1);
else if (optype1 == OFFSOP)
- latehalf[1] = adj_offsettable_operand (operands[1], 2);
+ latehalf[1] = adjust_address (operands[1], HImode, 2);
else if (optype1 == CNSTOP)
{
if (CONSTANT_P (operands[1]))
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 (optype0 == REGOP)
latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2);
else if (optype0 == OFFSOP)
- latehalf[0] = adj_offsettable_operand (operands[0], 4);
+ latehalf[0] = adjust_address (operands[0], SImode, 4);
else
latehalf[0] = operands[0];
if (optype1 == REGOP)
latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2);
else if (optype1 == OFFSOP)
- latehalf[1] = adj_offsettable_operand (operands[1], 4);
+ latehalf[1] = adjust_address (operands[1], SImode, 4);
else if (optype1 == CNSTOP)
{
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);
}
}
+/* Target hook to assemble integer objects. We need to use the
+ pdp-specific version of output_addr_const. */
+
+static bool
+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, GEN_INT (INTVAL (x) & 0xff));
+;
+ fprintf (asm_out_file, " /* char */\n");
+ return true;
+
+ case 2:
+ fprintf (asm_out_file, TARGET_UNIX_ASM ? "\t" : "\t.word\t");
+ output_addr_const_pdp11 (asm_out_file, x);
+ fprintf (asm_out_file, " /* short */\n");
+ return true;
+ }
+ return default_assemble_integer (x, size, aligned_p);
+}
+
+
/* register move costs, indexed by regs */
-static int move_costs[N_REG_CLASSES][N_REG_CLASSES] =
+static const int move_costs[N_REG_CLASSES][N_REG_CLASSES] =
{
/* 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)
{
- switch (GET_CODE(op))
+ 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)
+{
+ 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))));
+
+ 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;
+ }
-/* #undef REG_OK_STRICT */
+ 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
use, and for debugging output, which we don't support with this port either.
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));
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < 0)
{
- fprintf (file, ASM_OPEN_PAREN);
+ fprintf (file, targetm.asm_out.open_paren);
output_addr_const_pdp11 (file, XEXP (x, 1));
- fprintf (file, ASM_CLOSE_PAREN);
+ fprintf (file, targetm.asm_out.close_paren);
}
else
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;