blob: f0d15857f247b0e0c7f97f4b28169ebfe03f6890 [file] [log] [blame]
/* Subroutines for insn-output.c for HPPA.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
2002, 2003, 2004 Free Software Foundation, Inc.
Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c
This file is part of GCC.
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)
any later version.
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 GCC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#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-attr.h"
#include "flags.h"
#include "tree.h"
#include "output.h"
#include "except.h"
#include "expr.h"
#include "optabs.h"
#include "reload.h"
#include "integrate.h"
#include "function.h"
#include "toplev.h"
#include "ggc.h"
#include "recog.h"
#include "predict.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
static int hppa_use_dfa_pipeline_interface (void);
#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE hppa_use_dfa_pipeline_interface
static int
hppa_use_dfa_pipeline_interface (void)
{
return 1;
}
/* Return nonzero if there is a bypass for the output of
OUT_INSN and the fp store IN_INSN. */
int
hppa_fpstore_bypass_p (rtx out_insn, rtx in_insn)
{
enum machine_mode store_mode;
enum machine_mode other_mode;
rtx set;
if (recog_memoized (in_insn) < 0
|| get_attr_type (in_insn) != TYPE_FPSTORE
|| recog_memoized (out_insn) < 0)
return 0;
store_mode = GET_MODE (SET_SRC (PATTERN (in_insn)));
set = single_set (out_insn);
if (!set)
return 0;
other_mode = GET_MODE (SET_SRC (set));
return (GET_MODE_SIZE (store_mode) == GET_MODE_SIZE (other_mode));
}
#ifndef DO_FRAME_NOTES
#ifdef INCOMING_RETURN_ADDR_RTX
#define DO_FRAME_NOTES 1
#else
#define DO_FRAME_NOTES 0
#endif
#endif
static void copy_reg_pointer (rtx, rtx);
static int hppa_address_cost (rtx);
static bool hppa_rtx_costs (rtx, int, int, int *);
static inline rtx force_mode (enum machine_mode, rtx);
static void pa_reorg (void);
static void pa_combine_instructions (void);
static int pa_can_combine_p (rtx, rtx, rtx, int, rtx, rtx, rtx);
static int forward_branch_p (rtx);
static int shadd_constant_p (int);
static void compute_zdepwi_operands (unsigned HOST_WIDE_INT, unsigned *);
static int compute_movstr_length (rtx);
static int compute_clrstr_length (rtx);
static bool pa_assemble_integer (rtx, unsigned int, int);
static void remove_useless_addtr_insns (int);
static void store_reg (int, HOST_WIDE_INT, int);
static void store_reg_modify (int, int, HOST_WIDE_INT);
static void load_reg (int, HOST_WIDE_INT, int);
static void set_reg_plus_d (int, int, HOST_WIDE_INT, int);
static void pa_output_function_prologue (FILE *, HOST_WIDE_INT);
static void update_total_code_bytes (int);
static void pa_output_function_epilogue (FILE *, HOST_WIDE_INT);
static int pa_adjust_cost (rtx, rtx, rtx, int);
static int pa_adjust_priority (rtx, int);
static int pa_issue_rate (void);
static void pa_select_section (tree, int, unsigned HOST_WIDE_INT)
ATTRIBUTE_UNUSED;
static void pa_encode_section_info (tree, rtx, int);
static const char *pa_strip_name_encoding (const char *);
static bool pa_function_ok_for_sibcall (tree, tree);
static void pa_globalize_label (FILE *, const char *)
ATTRIBUTE_UNUSED;
static void pa_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
HOST_WIDE_INT, tree);
#if !defined(USE_COLLECT2)
static void pa_asm_out_constructor (rtx, int);
static void pa_asm_out_destructor (rtx, int);
#endif
static void pa_init_builtins (void);
static void copy_fp_args (rtx) ATTRIBUTE_UNUSED;
static int length_fp_args (rtx) ATTRIBUTE_UNUSED;
static struct deferred_plabel *get_plabel (const char *)
ATTRIBUTE_UNUSED;
static inline void pa_file_start_level (void) ATTRIBUTE_UNUSED;
static inline void pa_file_start_space (int) ATTRIBUTE_UNUSED;
static inline void pa_file_start_file (int) ATTRIBUTE_UNUSED;
static inline void pa_file_start_mcount (const char*) ATTRIBUTE_UNUSED;
static void pa_elf_file_start (void) ATTRIBUTE_UNUSED;
static void pa_som_file_start (void) ATTRIBUTE_UNUSED;
static void pa_linux_file_start (void) ATTRIBUTE_UNUSED;
static void pa_hpux64_gas_file_start (void) ATTRIBUTE_UNUSED;
static void pa_hpux64_hpas_file_start (void) ATTRIBUTE_UNUSED;
static void output_deferred_plabels (void);
#ifdef HPUX_LONG_DOUBLE_LIBRARY
static void pa_hpux_init_libfuncs (void);
#endif
/* Save the operands last given to a compare for use when we
generate a scc or bcc insn. */
rtx hppa_compare_op0, hppa_compare_op1;
enum cmp_type hppa_branch_type;
/* Which cpu we are scheduling for. */
enum processor_type pa_cpu;
/* String to hold which cpu we are scheduling for. */
const char *pa_cpu_string;
/* Which architecture we are generating code for. */
enum architecture_type pa_arch;
/* String to hold which architecture we are generating code for. */
const char *pa_arch_string;
/* Counts for the number of callee-saved general and floating point
registers which were saved by the current function's prologue. */
static int gr_saved, fr_saved;
static rtx find_addr_reg (rtx);
/* Keep track of the number of bytes we have output in the CODE subspace
during this compilation so we'll know when to emit inline long-calls. */
unsigned long total_code_bytes;
/* The last address of the previous function plus the number of bytes in
associated thunks that have been output. This is used to determine if
a thunk can use an IA-relative branch to reach its target function. */
static int last_address;
/* Variables to handle plabels that we discover are necessary at assembly
output time. They are output after the current function. */
struct deferred_plabel GTY(())
{
rtx internal_label;
const char *name;
};
static GTY((length ("n_deferred_plabels"))) struct deferred_plabel *
deferred_plabels;
static size_t n_deferred_plabels = 0;
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
#undef TARGET_ASM_ALIGNED_DI_OP
#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
#undef TARGET_ASM_UNALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_HI_OP TARGET_ASM_ALIGNED_HI_OP
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP TARGET_ASM_ALIGNED_SI_OP
#undef TARGET_ASM_UNALIGNED_DI_OP
#define TARGET_ASM_UNALIGNED_DI_OP TARGET_ASM_ALIGNED_DI_OP
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER pa_assemble_integer
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE pa_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE pa_output_function_epilogue
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST pa_adjust_cost
#undef TARGET_SCHED_ADJUST_PRIORITY
#define TARGET_SCHED_ADJUST_PRIORITY pa_adjust_priority
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE pa_issue_rate
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO pa_encode_section_info
#undef TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING pa_strip_name_encoding
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL pa_function_ok_for_sibcall
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK pa_asm_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
#undef TARGET_ASM_FILE_END
#define TARGET_ASM_FILE_END output_deferred_plabels
#if !defined(USE_COLLECT2)
#undef TARGET_ASM_CONSTRUCTOR
#define TARGET_ASM_CONSTRUCTOR pa_asm_out_constructor
#undef TARGET_ASM_DESTRUCTOR
#define TARGET_ASM_DESTRUCTOR pa_asm_out_destructor
#endif
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS pa_init_builtins
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS hppa_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST hppa_address_cost
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG pa_reorg
#ifdef HPUX_LONG_DOUBLE_LIBRARY
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS pa_hpux_init_libfuncs
#endif
struct gcc_target targetm = TARGET_INITIALIZER;
void
override_options (void)
{
if (pa_cpu_string == NULL)
pa_cpu_string = TARGET_SCHED_DEFAULT;
if (! strcmp (pa_cpu_string, "8000"))
{
pa_cpu_string = "8000";
pa_cpu = PROCESSOR_8000;
}
else if (! strcmp (pa_cpu_string, "7100"))
{
pa_cpu_string = "7100";
pa_cpu = PROCESSOR_7100;
}
else if (! strcmp (pa_cpu_string, "700"))
{
pa_cpu_string = "700";
pa_cpu = PROCESSOR_700;
}
else if (! strcmp (pa_cpu_string, "7100LC"))
{
pa_cpu_string = "7100LC";
pa_cpu = PROCESSOR_7100LC;
}
else if (! strcmp (pa_cpu_string, "7200"))
{
pa_cpu_string = "7200";
pa_cpu = PROCESSOR_7200;
}
else if (! strcmp (pa_cpu_string, "7300"))
{
pa_cpu_string = "7300";
pa_cpu = PROCESSOR_7300;
}
else
{
warning ("unknown -mschedule= option (%s).\nValid options are 700, 7100, 7100LC, 7200, 7300, and 8000\n", pa_cpu_string);
}
/* Set the instruction set architecture. */
if (pa_arch_string && ! strcmp (pa_arch_string, "1.0"))
{
pa_arch_string = "1.0";
pa_arch = ARCHITECTURE_10;
target_flags &= ~(MASK_PA_11 | MASK_PA_20);
}
else if (pa_arch_string && ! strcmp (pa_arch_string, "1.1"))
{
pa_arch_string = "1.1";
pa_arch = ARCHITECTURE_11;
target_flags &= ~MASK_PA_20;
target_flags |= MASK_PA_11;
}
else if (pa_arch_string && ! strcmp (pa_arch_string, "2.0"))
{
pa_arch_string = "2.0";
pa_arch = ARCHITECTURE_20;
target_flags |= MASK_PA_11 | MASK_PA_20;
}
else if (pa_arch_string)
{
warning ("unknown -march= option (%s).\nValid options are 1.0, 1.1, and 2.0\n", pa_arch_string);
}
/* Unconditional branches in the delay slot are not compatible with dwarf2
call frame information. There is no benefit in using this optimization
on PA8000 and later processors. */
if (pa_cpu >= PROCESSOR_8000
|| (! USING_SJLJ_EXCEPTIONS && flag_exceptions)
|| flag_unwind_tables)
target_flags &= ~MASK_JUMP_IN_DELAY;
if (flag_pic && TARGET_PORTABLE_RUNTIME)
{
warning ("PIC code generation is not supported in the portable runtime model\n");
}
if (flag_pic && TARGET_FAST_INDIRECT_CALLS)
{
warning ("PIC code generation is not compatible with fast indirect calls\n");
}
if (! TARGET_GAS && write_symbols != NO_DEBUG)
{
warning ("-g is only supported when using GAS on this processor,");
warning ("-g option disabled");
write_symbols = NO_DEBUG;
}
/* We only support the "big PIC" model now. And we always generate PIC
code when in 64bit mode. */
if (flag_pic == 1 || TARGET_64BIT)
flag_pic = 2;
/* We can't guarantee that .dword is available for 32-bit targets. */
if (UNITS_PER_WORD == 4)
targetm.asm_out.aligned_op.di = NULL;
/* The unaligned ops are only available when using GAS. */
if (!TARGET_GAS)
{
targetm.asm_out.unaligned_op.hi = NULL;
targetm.asm_out.unaligned_op.si = NULL;
targetm.asm_out.unaligned_op.di = NULL;
}
}
static void
pa_init_builtins (void)
{
#ifdef DONT_HAVE_FPUTC_UNLOCKED
built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE;
implicit_built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE;
#endif
}
/* If FROM is a probable pointer register, mark TO as a probable
pointer register with the same pointer alignment as FROM. */
static void
copy_reg_pointer (rtx to, rtx from)
{
if (REG_POINTER (from))
mark_reg_pointer (to, REGNO_POINTER_ALIGN (REGNO (from)));
}
/* Return nonzero only if OP is a register of mode MODE,
or CONST0_RTX. */
int
reg_or_0_operand (rtx op, enum machine_mode mode)
{
return (op == CONST0_RTX (mode) || register_operand (op, mode));
}
/* Return nonzero if OP is suitable for use in a call to a named
function.
For 2.5 try to eliminate either call_operand_address or
function_label_operand, they perform very similar functions. */
int
call_operand_address (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_MODE (op) == word_mode
&& CONSTANT_P (op) && ! TARGET_PORTABLE_RUNTIME);
}
/* Return 1 if X contains a symbolic expression. We know these
expressions will have one of a few well defined forms, so
we need only check those forms. */
int
symbolic_expression_p (rtx x)
{
/* Strip off any HIGH. */
if (GET_CODE (x) == HIGH)
x = XEXP (x, 0);
return (symbolic_operand (x, VOIDmode));
}
int
symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
switch (GET_CODE (op))
{
case SYMBOL_REF:
case LABEL_REF:
return 1;
case CONST:
op = XEXP (op, 0);
return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (op, 0)) == LABEL_REF)
&& GET_CODE (XEXP (op, 1)) == CONST_INT);
default:
return 0;
}
}
/* Return truth value of statement that OP is a symbolic memory
operand of mode MODE. */
int
symbolic_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) != MEM)
return 0;
op = XEXP (op, 0);
return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST
|| GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF);
}
/* Return 1 if the operand is either a register, zero, or a memory operand
that is not symbolic. */
int
reg_or_0_or_nonsymb_mem_operand (rtx op, enum machine_mode mode)
{
if (register_operand (op, mode))
return 1;
if (op == CONST0_RTX (mode))
return 1;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) != MEM)
return 0;
/* Until problems with management of the REG_POINTER flag are resolved,
we need to delay creating move insns with unscaled indexed addresses
until CSE is not expected. */
if (!TARGET_NO_SPACE_REGS
&& !cse_not_expected
&& GET_CODE (XEXP (op, 0)) == PLUS
&& REG_P (XEXP (XEXP (op, 0), 0))
&& REG_P (XEXP (XEXP (op, 0), 1)))
return 0;
return (!symbolic_memory_operand (op, mode)
&& memory_address_p (mode, XEXP (op, 0)));
}
/* Return 1 if the operand is a register operand or a non-symbolic memory
operand after reload. This predicate is used for branch patterns that
internally handle register reloading. We need to accept non-symbolic
memory operands after reload to ensure that the pattern is still valid
if reload didn't find a hard register for the operand. */
int
reg_before_reload_operand (rtx op, enum machine_mode mode)
{
/* Don't accept a SUBREG since it will need a reload. */
if (GET_CODE (op) == SUBREG)
return 0;
if (register_operand (op, mode))
return 1;
if (reload_completed
&& memory_operand (op, mode)
&& !symbolic_memory_operand (op, mode))
return 1;
return 0;
}
/* Accept any constant that can be moved in one instruction into a
general register. */
int
cint_ok_for_move (HOST_WIDE_INT intval)
{
/* OK if ldo, ldil, or zdepi, can be used. */
return (CONST_OK_FOR_LETTER_P (intval, 'J')
|| CONST_OK_FOR_LETTER_P (intval, 'N')
|| CONST_OK_FOR_LETTER_P (intval, 'K'));
}
/* Return 1 iff OP is an indexed memory operand. */
int
indexed_memory_operand (rtx op, enum machine_mode mode)
{
if (GET_MODE (op) != mode)
return 0;
/* Before reload, a (SUBREG (MEM...)) forces reloading into a register. */
if (reload_completed && GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) != MEM || symbolic_memory_operand (op, mode))
return 0;
op = XEXP (op, 0);
return (memory_address_p (mode, op) && IS_INDEX_ADDR_P (op));
}
/* Accept anything that can be used as a destination operand for a
move instruction. We don't accept indexed memory operands since
they are supported only for floating point stores. */
int
move_dest_operand (rtx op, enum machine_mode mode)
{
if (register_operand (op, mode))
return 1;
if (GET_MODE (op) != mode)
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) != MEM || symbolic_memory_operand (op, mode))
return 0;
op = XEXP (op, 0);
return (memory_address_p (mode, op)
&& !IS_INDEX_ADDR_P (op)
&& !IS_LO_SUM_DLT_ADDR_P (op));
}
/* Accept anything that can be used as a source operand for a move
instruction. */
int
move_src_operand (rtx op, enum machine_mode mode)
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONSTANT_P_RTX)
return 1;
if (GET_CODE (op) == CONST_INT)
return cint_ok_for_move (INTVAL (op));
if (GET_MODE (op) != mode)
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) != MEM)
return 0;
/* Until problems with management of the REG_POINTER flag are resolved,
we need to delay creating move insns with unscaled indexed addresses
until CSE is not expected. */
if (!TARGET_NO_SPACE_REGS
&& !cse_not_expected
&& GET_CODE (XEXP (op, 0)) == PLUS
&& REG_P (XEXP (XEXP (op, 0), 0))
&& REG_P (XEXP (XEXP (op, 0), 1)))
return 0;
return memory_address_p (mode, XEXP (op, 0));
}
/* Accept REG and any CONST_INT that can be moved in one instruction into a
general register. */
int
reg_or_cint_move_operand (rtx op, enum machine_mode mode)
{
if (register_operand (op, mode))
return 1;
return (GET_CODE (op) == CONST_INT && cint_ok_for_move (INTVAL (op)));
}
int
pic_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (!flag_pic)
return 0;
switch (GET_CODE (op))
{
case LABEL_REF:
return 1;
case CONST:
op = XEXP (op, 0);
return (GET_CODE (XEXP (op, 0)) == LABEL_REF
&& GET_CODE (XEXP (op, 1)) == CONST_INT);
default:
return 0;
}
}
int
fp_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return reg_renumber && FP_REG_P (op);
}
/* Return truth value of whether OP can be used as an operand in a
three operand arithmetic insn that accepts registers of mode MODE
or 14-bit signed integers. */
int
arith_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && INT_14_BITS (op)));
}
/* Return truth value of whether OP can be used as an operand in a
three operand arithmetic insn that accepts registers of mode MODE
or 11-bit signed integers. */
int
arith11_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && INT_11_BITS (op)));
}
/* Return truth value of whether OP can be used as an operand in a
adddi3 insn. */
int
adddi3_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT
&& (TARGET_64BIT ? INT_14_BITS (op) : INT_11_BITS (op))));
}
/* A constant integer suitable for use in a PRE_MODIFY memory
reference. */
int
pre_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT
&& INTVAL (op) >= -0x2000 && INTVAL (op) < 0x10);
}
/* A constant integer suitable for use in a POST_MODIFY memory
reference. */
int
post_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT
&& INTVAL (op) < 0x2000 && INTVAL (op) >= -0x10);
}
int
arith_double_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_DOUBLE
&& GET_MODE (op) == mode
&& VAL_14_BITS_P (CONST_DOUBLE_LOW (op))
&& ((CONST_DOUBLE_HIGH (op) >= 0)
== ((CONST_DOUBLE_LOW (op) & 0x1000) == 0))));
}
/* Return truth value of whether OP is an integer which fits the
range constraining immediate operands in three-address insns, or
is an integer register. */
int
ireg_or_int5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return ((GET_CODE (op) == CONST_INT && INT_5_BITS (op))
|| (GET_CODE (op) == REG && REGNO (op) > 0 && REGNO (op) < 32));
}
/* Return nonzero if OP is an integer register, else return zero. */
int
ireg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == REG && REGNO (op) > 0 && REGNO (op) < 32);
}
/* Return truth value of whether OP is an integer which fits the
range constraining immediate operands in three-address insns. */
int
int5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && INT_5_BITS (op));
}
int
uint5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && INT_U5_BITS (op));
}
int
int11_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && INT_11_BITS (op));
}
int
uint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
#if HOST_BITS_PER_WIDE_INT > 32
/* All allowed constants will fit a CONST_INT. */
return (GET_CODE (op) == CONST_INT
&& (INTVAL (op) >= 0 && INTVAL (op) < (HOST_WIDE_INT) 1 << 32));
#else
return (GET_CODE (op) == CONST_INT
|| (GET_CODE (op) == CONST_DOUBLE
&& CONST_DOUBLE_HIGH (op) == 0));
#endif
}
int
arith5_operand (rtx op, enum machine_mode mode)
{
return register_operand (op, mode) || int5_operand (op, mode);
}
/* True iff zdepi can be used to generate this CONST_INT.
zdepi first sign extends a 5 bit signed number to a given field
length, then places this field anywhere in a zero. */
int
zdepi_cint_p (unsigned HOST_WIDE_INT x)
{
unsigned HOST_WIDE_INT lsb_mask, t;
/* This might not be obvious, but it's at least fast.
This function is critical; we don't have the time loops would take. */
lsb_mask = x & -x;
t = ((x >> 4) + lsb_mask) & ~(lsb_mask - 1);
/* Return true iff t is a power of two. */
return ((t & (t - 1)) == 0);
}
/* True iff depi or extru can be used to compute (reg & mask).
Accept bit pattern like these:
0....01....1
1....10....0
1..10..01..1 */
int
and_mask_p (unsigned HOST_WIDE_INT mask)
{
mask = ~mask;
mask += mask & -mask;
return (mask & (mask - 1)) == 0;
}
/* True iff depi or extru can be used to compute (reg & OP). */
int
and_operand (rtx op, enum machine_mode mode)
{
return (register_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && and_mask_p (INTVAL (op))));
}
/* True iff depi can be used to compute (reg | MASK). */
int
ior_mask_p (unsigned HOST_WIDE_INT mask)
{
mask += mask & -mask;
return (mask & (mask - 1)) == 0;
}
/* True iff depi can be used to compute (reg | OP). */
int
ior_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && ior_mask_p (INTVAL (op)));
}
int
lhs_lshift_operand (rtx op, enum machine_mode mode)
{
return register_operand (op, mode) || lhs_lshift_cint_operand (op, mode);
}
/* True iff OP is a CONST_INT of the forms 0...0xxxx or 0...01...1xxxx.
Such values can be the left hand side x in (x << r), using the zvdepi
instruction. */
int
lhs_lshift_cint_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
unsigned HOST_WIDE_INT x;
if (GET_CODE (op) != CONST_INT)
return 0;
x = INTVAL (op) >> 4;
return (x & (x + 1)) == 0;
}
int
arith32_operand (rtx op, enum machine_mode mode)
{
return register_operand (op, mode) || GET_CODE (op) == CONST_INT;
}
int
pc_or_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF);
}
/* Legitimize PIC addresses. If the address is already
position-independent, we return ORIG. Newly generated
position-independent addresses go to REG. If we need more
than one register, we lose. */
rtx
legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
{
rtx pic_ref = orig;
/* Labels need special handling. */
if (pic_label_operand (orig, mode))
{
/* We do not want to go through the movXX expanders here since that
would create recursion.
Nor do we really want to call a generator for a named pattern
since that requires multiple patterns if we want to support
multiple word sizes.
So instead we just emit the raw set, which avoids the movXX
expanders completely. */
mark_reg_pointer (reg, BITS_PER_UNIT);
emit_insn (gen_rtx_SET (VOIDmode, reg, orig));
current_function_uses_pic_offset_table = 1;
return reg;
}
if (GET_CODE (orig) == SYMBOL_REF)
{
rtx insn, tmp_reg;
if (reg == 0)
abort ();
/* Before reload, allocate a temporary register for the intermediate
result. This allows the sequence to be deleted when the final
result is unused and the insns are trivially dead. */
tmp_reg = ((reload_in_progress || reload_completed)
? reg : gen_reg_rtx (Pmode));
emit_move_insn (tmp_reg,
gen_rtx_PLUS (word_mode, pic_offset_table_rtx,
gen_rtx_HIGH (word_mode, orig)));
pic_ref
= gen_rtx_MEM (Pmode,
gen_rtx_LO_SUM (Pmode, tmp_reg,
gen_rtx_UNSPEC (Pmode,
gen_rtvec (1, orig),
0)));
current_function_uses_pic_offset_table = 1;
MEM_NOTRAP_P (pic_ref) = 1;
RTX_UNCHANGING_P (pic_ref) = 1;
mark_reg_pointer (reg, BITS_PER_UNIT);
insn = emit_move_insn (reg, pic_ref);
/* Put a REG_EQUAL note on this insn, so that it can be optimized. */
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, REG_NOTES (insn));
return reg;
}
else if (GET_CODE (orig) == CONST)
{
rtx base;
if (GET_CODE (XEXP (orig, 0)) == PLUS
&& XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
return orig;
if (reg == 0)
abort ();
if (GET_CODE (XEXP (orig, 0)) == PLUS)
{
base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
base == reg ? 0 : reg);
}
else
abort ();
if (GET_CODE (orig) == CONST_INT)
{
if (INT_14_BITS (orig))
return plus_constant (base, INTVAL (orig));
orig = force_reg (Pmode, orig);
}
pic_ref = gen_rtx_PLUS (Pmode, base, orig);
/* Likewise, should we set special REG_NOTEs here? */
}
return pic_ref;
}
/* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address.
This macro is used in only one place: `memory_address' in explow.c.
OLDX is the address as it was before break_out_memory_refs was called.
In some cases it is useful to look at this to decide what needs to be done.
MODE and WIN are passed so that this macro can use
GO_IF_LEGITIMATE_ADDRESS.
It is always safe for this macro to do nothing. It exists to recognize
opportunities to optimize the output.
For the PA, transform:
memory(X + <large int>)
into:
if (<large int> & mask) >= 16
Y = (<large int> & ~mask) + mask + 1 Round up.
else
Y = (<large int> & ~mask) Round down.
Z = X + Y
memory (Z + (<large int> - Y));
This is for CSE to find several similar references, and only use one Z.
X can either be a SYMBOL_REF or REG, but because combine can not
perform a 4->2 combination we do nothing for SYMBOL_REF + D where
D will not fit in 14 bits.
MODE_FLOAT references allow displacements which fit in 5 bits, so use
0x1f as the mask.
MODE_INT references allow displacements which fit in 14 bits, so use
0x3fff as the mask.
This relies on the fact that most mode MODE_FLOAT references will use FP
registers and most mode MODE_INT references will use integer registers.
(In the rare case of an FP register used in an integer MODE, we depend
on secondary reloads to clean things up.)
It is also beneficial to handle (plus (mult (X) (Y)) (Z)) in a special
manner if Y is 2, 4, or 8. (allows more shadd insns and shifted indexed
addressing modes to be used).
Put X and Z into registers. Then put the entire expression into
a register. */
rtx
hppa_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
enum machine_mode mode)
{
rtx orig = x;
/* We need to canonicalize the order of operands in unscaled indexed
addresses since the code that checks if an address is valid doesn't
always try both orders. */
if (!TARGET_NO_SPACE_REGS
&& GET_CODE (x) == PLUS
&& GET_MODE (x) == Pmode
&& REG_P (XEXP (x, 0))
&& REG_P (XEXP (x, 1))
&& REG_POINTER (XEXP (x, 0))
&& !REG_POINTER (XEXP (x, 1)))
return gen_rtx_PLUS (Pmode, XEXP (x, 1), XEXP (x, 0));
if (flag_pic)
return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode));
/* Strip off CONST. */
if (GET_CODE (x) == CONST)
x = XEXP (x, 0);
/* Special case. Get the SYMBOL_REF into a register and use indexing.
That should always be safe. */
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == REG
&& GET_CODE (XEXP (x, 1)) == SYMBOL_REF)
{
rtx reg = force_reg (Pmode, XEXP (x, 1));
return force_reg (Pmode, gen_rtx_PLUS (Pmode, reg, XEXP (x, 0)));
}
/* Note we must reject symbols which represent function addresses
since the assembler/linker can't handle arithmetic on plabels. */
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& ((GET_CODE (XEXP (x, 0)) == SYMBOL_REF
&& !FUNCTION_NAME_P (XSTR (XEXP (x, 0), 0)))
|| GET_CODE (XEXP (x, 0)) == REG))
{
rtx int_part, ptr_reg;
int newoffset;
int offset = INTVAL (XEXP (x, 1));
int mask;
mask = (GET_MODE_CLASS (mode) == MODE_FLOAT
? (TARGET_PA_20 ? 0x3fff : 0x1f) : 0x3fff);
/* Choose which way to round the offset. Round up if we
are >= halfway to the next boundary. */
if ((offset & mask) >= ((mask + 1) / 2))
newoffset = (offset & ~ mask) + mask + 1;
else
newoffset = (offset & ~ mask);
/* If the newoffset will not fit in 14 bits (ldo), then
handling this would take 4 or 5 instructions (2 to load
the SYMBOL_REF + 1 or 2 to load the newoffset + 1 to
add the new offset and the SYMBOL_REF.) Combine can
not handle 4->2 or 5->2 combinations, so do not create
them. */
if (! VAL_14_BITS_P (newoffset)
&& GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
{
rtx const_part = plus_constant (XEXP (x, 0), newoffset);
rtx tmp_reg
= force_reg (Pmode,
gen_rtx_HIGH (Pmode, const_part));
ptr_reg
= force_reg (Pmode,
gen_rtx_LO_SUM (Pmode,
tmp_reg, const_part));
}
else
{
if (! VAL_14_BITS_P (newoffset))
int_part = force_reg (Pmode, GEN_INT (newoffset));
else
int_part = GEN_INT (newoffset);
ptr_reg = force_reg (Pmode,
gen_rtx_PLUS (Pmode,
force_reg (Pmode, XEXP (x, 0)),
int_part));
}
return plus_constant (ptr_reg, offset - newoffset);
}
/* Handle (plus (mult (a) (shadd_constant)) (b)). */
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))
&& (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == 'o'
|| GET_CODE (XEXP (x, 1)) == SUBREG)
&& GET_CODE (XEXP (x, 1)) != CONST)
{
int val = INTVAL (XEXP (XEXP (x, 0), 1));
rtx reg1, reg2;
reg1 = XEXP (x, 1);
if (GET_CODE (reg1) != REG)
reg1 = force_reg (Pmode, force_operand (reg1, 0));
reg2 = XEXP (XEXP (x, 0), 0);
if (GET_CODE (reg2) != REG)
reg2 = force_reg (Pmode, force_operand (reg2, 0));
return force_reg (Pmode, gen_rtx_PLUS (Pmode,
gen_rtx_MULT (Pmode,
reg2,
GEN_INT (val)),
reg1));
}
/* Similarly for (plus (plus (mult (a) (shadd_constant)) (b)) (c)).
Only do so for floating point modes since this is more speculative
and we lose if it's an integer store. */
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
&& GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
&& shadd_constant_p (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)))
&& (mode == SFmode || mode == DFmode))
{
/* First, try and figure out what to use as a base register. */
rtx reg1, reg2, base, idx, orig_base;
reg1 = XEXP (XEXP (x, 0), 1);
reg2 = XEXP (x, 1);
base = NULL_RTX;
idx = NULL_RTX;
/* Make sure they're both regs. If one was a SYMBOL_REF [+ const],
then emit_move_sequence will turn on REG_POINTER so we'll know
it's a base register below. */
if (GET_CODE (reg1) != REG)
reg1 = force_reg (Pmode, force_operand (reg1, 0));
if (GET_CODE (reg2) != REG)
reg2 = force_reg (Pmode, force_operand (reg2, 0));
/* Figure out what the base and index are. */
if (GET_CODE (reg1) == REG
&& REG_POINTER (reg1))
{
base = reg1;
orig_base = XEXP (XEXP (x, 0), 1);
idx = gen_rtx_PLUS (Pmode,
gen_rtx_MULT (Pmode,
XEXP (XEXP (XEXP (x, 0), 0), 0),
XEXP (XEXP (XEXP (x, 0), 0), 1)),
XEXP (x, 1));
}
else if (GET_CODE (reg2) == REG
&& REG_POINTER (reg2))
{
base = reg2;
orig_base = XEXP (x, 1);
idx = XEXP (x, 0);
}
if (base == 0)
return orig;
/* If the index adds a large constant, try to scale the
constant so that it can be loaded with only one insn. */
if (GET_CODE (XEXP (idx, 1)) == CONST_INT
&& VAL_14_BITS_P (INTVAL (XEXP (idx, 1))
/ INTVAL (XEXP (XEXP (idx, 0), 1)))
&& INTVAL (XEXP (idx, 1)) % INTVAL (XEXP (XEXP (idx, 0), 1)) == 0)
{
/* Divide the CONST_INT by the scale factor, then add it to A. */
int val = INTVAL (XEXP (idx, 1));
val /= INTVAL (XEXP (XEXP (idx, 0), 1));
reg1 = XEXP (XEXP (idx, 0), 0);
if (GET_CODE (reg1) != REG)
reg1 = force_reg (Pmode, force_operand (reg1, 0));
reg1 = force_reg (Pmode, gen_rtx_PLUS (Pmode, reg1, GEN_INT (val)));
/* We can now generate a simple scaled indexed address. */
return
force_reg
(Pmode, gen_rtx_PLUS (Pmode,
gen_rtx_MULT (Pmode, reg1,
XEXP (XEXP (idx, 0), 1)),
base));
}
/* If B + C is still a valid base register, then add them. */
if (GET_CODE (XEXP (idx, 1)) == CONST_INT
&& INTVAL (XEXP (idx, 1)) <= 4096
&& INTVAL (XEXP (idx, 1)) >= -4096)
{
int val = INTVAL (XEXP (XEXP (idx, 0), 1));
rtx reg1, reg2;
reg1 = force_reg (Pmode, gen_rtx_PLUS (Pmode, base, XEXP (idx, 1)));
reg2 = XEXP (XEXP (idx, 0), 0);
if (GET_CODE (reg2) != CONST_INT)
reg2 = force_reg (Pmode, force_operand (reg2, 0));
return force_reg (Pmode, gen_rtx_PLUS (Pmode,
gen_rtx_MULT (Pmode,
reg2,
GEN_INT (val)),
reg1));
}
/* Get the index into a register, then add the base + index and
return a register holding the result. */
/* First get A into a register. */
reg1 = XEXP (XEXP (idx, 0), 0);
if (GET_CODE (reg1) != REG)
reg1 = force_reg (Pmode, force_operand (reg1, 0));
/* And get B into a register. */
reg2 = XEXP (idx, 1);
if (GET_CODE (reg2) != REG)
reg2 = force_reg (Pmode, force_operand (reg2, 0));
reg1 = force_reg (Pmode,
gen_rtx_PLUS (Pmode,
gen_rtx_MULT (Pmode, reg1,
XEXP (XEXP (idx, 0), 1)),
reg2));
/* Add the result to our base register and return. */
return force_reg (Pmode, gen_rtx_PLUS (Pmode, base, reg1));
}
/* Uh-oh. We might have an address for x[n-100000]. This needs
special handling to avoid creating an indexed memory address
with x-100000 as the base.
If the constant part is small enough, then it's still safe because
there is a guard page at the beginning and end of the data segment.
Scaled references are common enough that we want to try and rearrange the
terms so that we can use indexing for these addresses too. Only
do the optimization for floatint point modes. */
if (GET_CODE (x) == PLUS
&& symbolic_expression_p (XEXP (x, 1)))
{
/* Ugly. We modify things here so that the address offset specified
by the index expression is computed first, then added to x to form
the entire address. */
rtx regx1, regx2, regy1, regy2, y;
/* Strip off any CONST. */
y = XEXP (x, 1);
if (GET_CODE (y) == CONST)
y = XEXP (y, 0);
if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS)
{
/* See if this looks like
(plus (mult (reg) (shadd_const))
(const (plus (symbol_ref) (const_int))))
Where const_int is small. In that case the const
expression is a valid pointer for indexing.
If const_int is big, but can be divided evenly by shadd_const
and added to (reg). This allows more scaled indexed addresses. */
if (GET_CODE (XEXP (y, 0)) == SYMBOL_REF
&& GET_CODE (XEXP (x, 0)) == MULT
&& GET_CODE (XEXP (y, 1)) == CONST_INT
&& INTVAL (XEXP (y, 1)) >= -4096
&& INTVAL (XEXP (y, 1)) <= 4095
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1))))
{
int val = INTVAL (XEXP (XEXP (x, 0), 1));
rtx reg1, reg2;
reg1 = XEXP (x, 1);
if (GET_CODE (reg1) != REG)
reg1 = force_reg (Pmode, force_operand (reg1, 0));
reg2 = XEXP (XEXP (x, 0), 0);
if (GET_CODE (reg2) != REG)
reg2 = force_reg (Pmode, force_operand (reg2, 0));
return force_reg (Pmode,
gen_rtx_PLUS (Pmode,
gen_rtx_MULT (Pmode,
reg2,
GEN_INT (val)),
reg1));
}
else if ((mode == DFmode || mode == SFmode)
&& GET_CODE (XEXP (y, 0)) == SYMBOL_REF
&& GET_CODE (XEXP (x, 0)) == MULT
&& GET_CODE (XEXP (y, 1)) == CONST_INT
&& INTVAL (XEXP (y, 1)) % INTVAL (XEXP (XEXP (x, 0), 1)) == 0
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1))))
{
regx1
= force_reg (Pmode, GEN_INT (INTVAL (XEXP (y, 1))
/ INTVAL (XEXP (XEXP (x, 0), 1))));
regx2 = XEXP (XEXP (x, 0), 0);
if (GET_CODE (regx2) != REG)
regx2 = force_reg (Pmode, force_operand (regx2, 0));
regx2 = force_reg (Pmode, gen_rtx_fmt_ee (GET_CODE (y), Pmode,
regx2, regx1));
return
force_reg (Pmode,
gen_rtx_PLUS (Pmode,
gen_rtx_MULT (Pmode, regx2,
XEXP (XEXP (x, 0), 1)),
force_reg (Pmode, XEXP (y, 0))));
}
else if (GET_CODE (XEXP (y, 1)) == CONST_INT
&& INTVAL (XEXP (y, 1)) >= -4096
&& INTVAL (XEXP (y, 1)) <= 4095)
{
/* This is safe because of the guard page at the
beginning and end of the data space. Just
return the original address. */
return orig;
}
else
{
/* Doesn't look like one we can optimize. */
regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0));
regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0));
regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0));
regx1 = force_reg (Pmode,
gen_rtx_fmt_ee (GET_CODE (y), Pmode,
regx1, regy2));
return force_reg (Pmode, gen_rtx_PLUS (Pmode, regx1, regy1));
}
}
}
return orig;
}
/* For the HPPA, REG and REG+CONST is cost 0
and addresses involving symbolic constants are cost 2.
PIC addresses are very expensive.
It is no coincidence that this has the same structure
as GO_IF_LEGITIMATE_ADDRESS. */
static int
hppa_address_cost (rtx X)
{
switch (GET_CODE (X))
{
case REG:
case PLUS:
case LO_SUM:
return 1;
case HIGH:
return 2;
default:
return 4;
}
}
/* Compute a (partial) cost for rtx X. Return true if the complete
cost has been computed, and false if subexpressions should be
scanned. In either case, *TOTAL contains the cost result. */
static bool
hppa_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
case CONST_INT:
if (INTVAL (x) == 0)
*total = 0;
else if (INT_14_BITS (x))
*total = 1;
else
*total = 2;
return true;
case HIGH:
*total = 2;
return true;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
*total = 4;
return true;
case CONST_DOUBLE:
if ((x == CONST0_RTX (DFmode) || x == CONST0_RTX (SFmode))
&& outer_code != SET)
*total = 0;
else
*total = 8;
return true;
case MULT:
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
*total = COSTS_N_INSNS (3);
else if (TARGET_PA_11 && !TARGET_DISABLE_FPREGS && !TARGET_SOFT_FLOAT)
*total = COSTS_N_INSNS (8);
else
*total = COSTS_N_INSNS (20);
return true;
case DIV:
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
{
*total = COSTS_N_INSNS (14);
return true;
}
/* FALLTHRU */
case UDIV:
case MOD:
case UMOD:
*total = COSTS_N_INSNS (60);
return true;
case PLUS: /* this includes shNadd insns */
case MINUS:
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
*total = COSTS_N_INSNS (3);
else
*total = COSTS_N_INSNS (1);
return true;
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
*total = COSTS_N_INSNS (1);
return true;
default:
return false;
}
}
/* Ensure mode of ORIG, a REG rtx, is MODE. Returns either ORIG or a
new rtx with the correct mode. */
static inline rtx
force_mode (enum machine_mode mode, rtx orig)
{
if (mode == GET_MODE (orig))
return orig;
if (REGNO (orig) >= FIRST_PSEUDO_REGISTER)
abort ();
return gen_rtx_REG (mode, REGNO (orig));
}
/* Emit insns to move operands[1] into operands[0].
Return 1 if we have written out everything that needs to be done to
do the move. Otherwise, return 0 and the caller will emit the move
normally.
Note SCRATCH_REG may not be in the proper mode depending on how it
will be used. This routine is responsible for creating a new copy
of SCRATCH_REG in the proper mode. */
int
emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
{
register rtx operand0 = operands[0];
register rtx operand1 = operands[1];
register rtx tem;
/* We can only handle indexed addresses in the destination operand
of floating point stores. Thus, we need to break out indexed
addresses from the destination operand. */
if (GET_CODE (operand0) == MEM && IS_INDEX_ADDR_P (XEXP (operand0, 0)))
{
/* This is only safe up to the beginning of life analysis. */
if (no_new_pseudos)
abort ();
tem = copy_to_mode_reg (Pmode, XEXP (operand0, 0));
operand0 = replace_equiv_address (operand0, tem);
}
/* On targets with non-equivalent space registers, break out unscaled
indexed addresses from the source operand before the final CSE.
We have to do this because the REG_POINTER flag is not correctly
carried through various optimization passes and CSE may substitute
a pseudo without the pointer set for one with the pointer set. As
a result, we loose various opportunites to create insns with
unscaled indexed addresses. */
if (!TARGET_NO_SPACE_REGS
&& !cse_not_expected
&& GET_CODE (operand1) == MEM
&& GET_CODE (XEXP (operand1, 0)) == PLUS
&& REG_P (XEXP (XEXP (operand1, 0), 0))
&& REG_P (XEXP (XEXP (operand1, 0), 1)))
operand1
= replace_equiv_address (operand1,
copy_to_mode_reg (Pmode, XEXP (operand1, 0)));
if (scratch_reg
&& reload_in_progress && GET_CODE (operand0) == REG
&& REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
operand0 = reg_equiv_mem[REGNO (operand0)];
else if (scratch_reg
&& reload_in_progress && GET_CODE (operand0) == SUBREG
&& GET_CODE (SUBREG_REG (operand0)) == REG
&& REGNO (SUBREG_REG (operand0)) >= FIRST_PSEUDO_REGISTER)
{
/* We must not alter SUBREG_BYTE (operand0) since that would confuse
the code which tracks sets/uses for delete_output_reload. */
rtx temp = gen_rtx_SUBREG (GET_MODE (operand0),
reg_equiv_mem [REGNO (SUBREG_REG (operand0))],
SUBREG_BYTE (operand0));
operand0 = alter_subreg (&temp);
}
if (scratch_reg
&& reload_in_progress && GET_CODE (operand1) == REG
&& REGNO (operand1) >= FIRST_PSEUDO_REGISTER)
operand1 = reg_equiv_mem[REGNO (operand1)];
else if (scratch_reg
&& reload_in_progress && GET_CODE (operand1) == SUBREG
&& GET_CODE (SUBREG_REG (operand1)) == REG
&& REGNO (SUBREG_REG (operand1)) >= FIRST_PSEUDO_REGISTER)
{
/* We must not alter SUBREG_BYTE (operand0) since that would confuse
the code which tracks sets/uses for delete_output_reload. */
rtx temp = gen_rtx_SUBREG (GET_MODE (operand1),
reg_equiv_mem [REGNO (SUBREG_REG (operand1))],
SUBREG_BYTE (operand1));
operand1 = alter_subreg (&temp);
}
if (scratch_reg && reload_in_progress && GET_CODE (operand0) == MEM
&& ((tem = find_replacement (&XEXP (operand0, 0)))
!= XEXP (operand0, 0)))
operand0 = gen_rtx_MEM (GET_MODE (operand0), tem);
if (scratch_reg && reload_in_progress && GET_CODE (operand1) == MEM
&& ((tem = find_replacement (&XEXP (operand1, 0)))
!= XEXP (operand1, 0)))
operand1 = gen_rtx_MEM (GET_MODE (operand1), tem);
/* Handle secondary reloads for loads/stores of FP registers from
REG+D addresses where D does not fit in 5 or 14 bits, including
(subreg (mem (addr))) cases. */
if (scratch_reg
&& fp_reg_operand (operand0, mode)
&& ((GET_CODE (operand1) == MEM
&& !memory_address_p ((GET_MODE_SIZE (mode) == 4 ? SFmode : DFmode),
XEXP (operand1, 0)))
|| ((GET_CODE (operand1) == SUBREG
&& GET_CODE (XEXP (operand1, 0)) == MEM
&& !memory_address_p ((GET_MODE_SIZE (mode) == 4
? SFmode : DFmode),
XEXP (XEXP (operand1, 0), 0))))))
{
if (GET_CODE (operand1) == SUBREG)
operand1 = XEXP (operand1, 0);
/* SCRATCH_REG will hold an address and maybe the actual data. We want
it in WORD_MODE regardless of what mode it was originally given
to us. */
scratch_reg = force_mode (word_mode, scratch_reg);
/* D might not fit in 14 bits either; for such cases load D into
scratch reg. */
if (!memory_address_p (Pmode, XEXP (operand1, 0)))
{
emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1));
emit_move_insn (scratch_reg,
gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)),
Pmode,
XEXP (XEXP (operand1, 0), 0),
scratch_reg));
}
else
emit_move_insn (scratch_reg, XEXP (operand1, 0));
emit_insn (gen_rtx_SET (VOIDmode, operand0,
gen_rtx_MEM (mode, scratch_reg)));
return 1;
}
else if (scratch_reg
&& fp_reg_operand (operand1, mode)
&& ((GET_CODE (operand0) == MEM
&& !memory_address_p ((GET_MODE_SIZE (mode) == 4
? SFmode : DFmode),
XEXP (operand0, 0)))
|| ((GET_CODE (operand0) == SUBREG)
&& GET_CODE (XEXP (operand0, 0)) == MEM
&& !memory_address_p ((GET_MODE_SIZE (mode) == 4
? SFmode : DFmode),
XEXP (XEXP (operand0, 0), 0)))))
{
if (GET_CODE (operand0) == SUBREG)
operand0 = XEXP (operand0, 0);
/* SCRATCH_REG will hold an address and maybe the actual data. We want
it in WORD_MODE regardless of what mode it was originally given
to us. */
scratch_reg = force_mode (word_mode, scratch_reg);
/* D might not fit in 14 bits either; for such cases load D into
scratch reg. */
if (!memory_address_p (Pmode, XEXP (operand0, 0)))
{
emit_move_insn (scratch_reg, XEXP (XEXP (operand0, 0), 1));
emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand0,
0)),
Pmode,
XEXP (XEXP (operand0, 0),
0),
scratch_reg));
}
else
emit_move_insn (scratch_reg, XEXP (operand0, 0));
emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_MEM (mode, scratch_reg),
operand1));
return 1;
}
/* Handle secondary reloads for loads of FP registers from constant
expressions by forcing the constant into memory.
Use scratch_reg to hold the address of the memory location.
The proper fix is to change PREFERRED_RELOAD_CLASS to return
NO_REGS when presented with a const_int and a register class
containing only FP registers. Doing so unfortunately creates
more problems than it solves. Fix this for 2.5. */
else if (scratch_reg
&& CONSTANT_P (operand1)
&& fp_reg_operand (operand0, mode))
{
rtx xoperands[2];
/* SCRATCH_REG will hold an address and maybe the actual data. We want
it in WORD_MODE regardless of what mode it was originally given
to us. */
scratch_reg = force_mode (word_mode, scratch_reg);
/* Force the constant into memory and put the address of the
memory location into scratch_reg. */
xoperands[0] = scratch_reg;
xoperands[1] = XEXP (force_const_mem (mode, operand1), 0);
emit_move_sequence (xoperands, Pmode, 0);
/* Now load the destination register. */
emit_insn (gen_rtx_SET (mode, operand0,
gen_rtx_MEM (mode, scratch_reg)));
return 1;
}
/* Handle secondary reloads for SAR. These occur when trying to load
the SAR from memory, FP register, or with a constant. */
else if (scratch_reg
&& GET_CODE (operand0) == REG
&& REGNO (operand0) < FIRST_PSEUDO_REGISTER
&& REGNO_REG_CLASS (REGNO (operand0)) == SHIFT_REGS
&& (GET_CODE (operand1) == MEM
|| GET_CODE (operand1) == CONST_INT
|| (GET_CODE (operand1) == REG
&& FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1))))))
{
/* D might not fit in 14 bits either; for such cases load D into
scratch reg. */
if (GET_CODE (operand1) == MEM
&& !memory_address_p (Pmode, XEXP (operand1, 0)))
{
/* We are reloading the address into the scratch register, so we
want to make sure the scratch register is a full register. */
scratch_reg = force_mode (word_mode, scratch_reg);
emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1));
emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand1,
0)),
Pmode,
XEXP (XEXP (operand1, 0),
0),
scratch_reg));
/* Now we are going to load the scratch register from memory,
we want to load it in the same width as the original MEM,
which must be the same as the width of the ultimate destination,
OPERAND0. */
scratch_reg = force_mode (GET_MODE (operand0), scratch_reg);
emit_move_insn (scratch_reg, gen_rtx_MEM (GET_MODE (operand0),
scratch_reg));
}
else
{
/* We want to load the scratch register using the same mode as
the ultimate destination. */
scratch_reg = force_mode (GET_MODE (operand0), scratch_reg);
emit_move_insn (scratch_reg, operand1);
}
/* And emit the insn to set the ultimate destination. We know that
the scratch register has the same mode as the destination at this
point. */
emit_move_insn (operand0, scratch_reg);
return 1;
}
/* Handle the most common case: storing into a register. */
else if (register_operand (operand0, mode))
{
if (register_operand (operand1, mode)
|| (GET_CODE (operand1) == CONST_INT
&& cint_ok_for_move (INTVAL (operand1)))
|| (operand1 == CONST0_RTX (mode))
|| (GET_CODE (operand1) == HIGH
&& !symbolic_operand (XEXP (operand1, 0), VOIDmode))
/* Only `general_operands' can come here, so MEM is ok. */
|| GET_CODE (operand1) == MEM)
{
/* Various sets are created during RTL generation which don't
have the REG_POINTER flag correctly set. After the CSE pass,
instruction recognition can fail if we don't consistently
set this flag when performing register copies. This should
also improve the opportunities for creating insns that use
unscaled indexing. */
if (REG_P (operand0) && REG_P (operand1))
{
if (REG_POINTER (operand1)
&& !REG_POINTER (operand0)
&& !HARD_REGISTER_P (operand0))
copy_reg_pointer (operand0, operand1);
else if (REG_POINTER (operand0)
&& !REG_POINTER (operand1)
&& !HARD_REGISTER_P (operand1))
copy_reg_pointer (operand1, operand0);
}
/* When MEMs are broken out, the REG_POINTER flag doesn't
get set. In some cases, we can set the REG_POINTER flag
from the declaration for the MEM. */
if (REG_P (operand0)
&& GET_CODE (operand1) == MEM
&& !REG_POINTER (operand0))
{
tree decl = MEM_EXPR (operand1);
/* Set the register pointer flag and register alignment
if the declaration for this memory reference is a
pointer type. Fortran indirect argument references
are ignored. */
if (decl
&& !(flag_argument_noalias > 1
&& TREE_CODE (decl) == INDIRECT_REF
&& TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL))
{
tree type;
/* If this is a COMPONENT_REF, use the FIELD_DECL from
tree operand 1. */
if (TREE_CODE (decl) == COMPONENT_REF)
decl = TREE_OPERAND (decl, 1);
type = TREE_TYPE (decl);
if (TREE_CODE (type) == ARRAY_TYPE)
type = get_inner_array_type (type);
if (POINTER_TYPE_P (type))
{
int align;
type = TREE_TYPE (type);
/* Using TYPE_ALIGN_OK is rather conservative as
only the ada frontend actually sets it. */
align = (TYPE_ALIGN_OK (type) ? TYPE_ALIGN (type)
: BITS_PER_UNIT);
mark_reg_pointer (operand0, align);
}
}
}
emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1));
return 1;
}
}
else if (GET_CODE (operand0) == MEM)
{
if (mode == DFmode && operand1 == CONST0_RTX (mode)
&& !(reload_in_progress || reload_completed))
{
rtx temp = gen_reg_rtx (DFmode);
emit_insn (gen_rtx_SET (VOIDmode, temp, operand1));
emit_insn (gen_rtx_SET (VOIDmode, operand0, temp));
return 1;
}
if (register_operand (operand1, mode) || operand1 == CONST0_RTX (mode))
{
/* Run this case quickly. */
emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1));
return 1;
}
if (! (reload_in_progress || reload_completed))
{
operands[0] = validize_mem (operand0);
operands[1] = operand1 = force_reg (mode, operand1);
}
}
/* Simplify the source if we need to.
Note we do have to handle function labels here, even though we do
not consider them legitimate constants. Loop optimizations can
call the emit_move_xxx with one as a source. */
if ((GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode))
|| function_label_operand (operand1, mode)
|| (GET_CODE (operand1) == HIGH
&& symbolic_operand (XEXP (operand1, 0), mode)))
{
int ishighonly = 0;
if (GET_CODE (operand1) == HIGH)
{
ishighonly = 1;
operand1 = XEXP (operand1, 0);
}
if (symbolic_operand (operand1, mode))
{
/* Argh. The assembler and linker can't handle arithmetic
involving plabels.
So we force the plabel into memory, load operand0 from
the memory location, then add in the constant part. */
if ((GET_CODE (operand1) == CONST
&& GET_CODE (XEXP (operand1, 0)) == PLUS
&& function_label_operand (XEXP (XEXP (operand1, 0), 0), Pmode))
|| function_label_operand (operand1, mode))
{
rtx temp, const_part;
/* Figure out what (if any) scratch register to use. */
if (reload_in_progress || reload_completed)
{
scratch_reg = scratch_reg ? scratch_reg : operand0;
/* SCRATCH_REG will hold an address and maybe the actual
data. We want it in WORD_MODE regardless of what mode it
was originally given to us. */
scratch_reg = force_mode (word_mode, scratch_reg);
}
else if (flag_pic)
scratch_reg = gen_reg_rtx (Pmode);
if (GET_CODE (operand1) == CONST)
{
/* Save away the constant part of the expression. */
const_part = XEXP (XEXP (operand1, 0), 1);
if (GET_CODE (const_part) != CONST_INT)
abort ();
/* Force the function label into memory. */
temp = force_const_mem (mode, XEXP (XEXP (operand1, 0), 0));
}
else
{
/* No constant part. */
const_part = NULL_RTX;
/* Force the function label into memory. */
temp = force_const_mem (mode, operand1);
}
/* Get the address of the memory location. PIC-ify it if
necessary. */
temp = XEXP (temp, 0);
if (flag_pic)
temp = legitimize_pic_address (temp, mode, scratch_reg);
/* Put the address of the memory location into our destination
register. */
operands[1] = temp;
emit_move_sequence (operands, mode, scratch_reg);
/* Now load from the memory location into our destination
register. */
operands[1] = gen_rtx_MEM (Pmode, operands[0]);
emit_move_sequence (operands, mode, scratch_reg);
/* And add back in the constant part. */
if (const_part != NULL_RTX)
expand_inc (operand0, const_part);
return 1;
}
if (flag_pic)
{
rtx temp;
if (reload_in_progress || reload_completed)
{
temp = scratch_reg ? scratch_reg : operand0;
/* TEMP will hold an address and maybe the actual
data. We want it in WORD_MODE regardless of what mode it
was originally given to us. */
temp = force_mode (word_mode, temp);
}
else
temp = gen_reg_rtx (Pmode);
/* (const (plus (symbol) (const_int))) must be forced to
memory during/after reload if the const_int will not fit
in 14 bits. */
if (GET_CODE (operand1) == CONST
&& GET_CODE (XEXP (operand1, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (operand1, 0), 1)) == CONST_INT
&& !INT_14_BITS (XEXP (XEXP (operand1, 0), 1))
&& (reload_completed || reload_in_progress)
&& flag_pic)
{
operands[1] = force_const_mem (mode, operand1);
operands[1] = legitimize_pic_address (XEXP (operands[1], 0),
mode, temp);
operands[1] = gen_rtx_MEM (mode, operands[1]);
emit_move_sequence (operands, mode, temp);
}
else
{
operands[1] = legitimize_pic_address (operand1, mode, temp);
if (REG_P (operand0) && REG_P (operands[1]))
copy_reg_pointer (operand0, operands[1]);
emit_insn (gen_rtx_SET (VOIDmode, operand0, operands[1]));
}
}
/* On the HPPA, references to data space are supposed to use dp,
register 27, but showing it in the RTL inhibits various cse
and loop optimizations. */
else
{
rtx temp, set;
if (reload_in_progress || reload_completed)
{
temp = scratch_reg ? scratch_reg : operand0;
/* TEMP will hold an address and maybe the actual
data. We want it in WORD_MODE regardless of what mode it
was originally given to us. */
temp = force_mode (word_mode, temp);
}
else
temp = gen_reg_rtx (mode);
/* Loading a SYMBOL_REF into a register makes that register
safe to be used as the base in an indexed address.
Don't mark hard registers though. That loses. */
if (GET_CODE (operand0) == REG
&& REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
mark_reg_pointer (operand0, BITS_PER_UNIT);
if (REGNO (temp) >= FIRST_PSEUDO_REGISTER)
mark_reg_pointer (temp, BITS_PER_UNIT);
if (ishighonly)
set = gen_rtx_SET (mode, operand0, temp);
else
set = gen_rtx_SET (VOIDmode,
operand0,
gen_rtx_LO_SUM (mode, temp, operand1));
emit_insn (gen_rtx_SET (VOIDmode,
temp,
gen_rtx_HIGH (mode, operand1)));
emit_insn (set);
}
return 1;
}
else if (GET_CODE (operand1) != CONST_INT
|| !cint_ok_for_move (INTVAL (operand1)))
{
rtx insn, temp;
rtx op1 = operand1;
HOST_WIDE_INT value = 0;
HOST_WIDE_INT insv = 0;
int insert = 0;
if (GET_CODE (operand1) == CONST_INT)
value = INTVAL (operand1);
if (TARGET_64BIT
&& GET_CODE (operand1) == CONST_INT
&& HOST_BITS_PER_WIDE_INT > 32
&& GET_MODE_BITSIZE (GET_MODE (operand0)) > 32)
{
HOST_WIDE_INT nval;
/* Extract the low order 32 bits of the value and sign extend.
If the new value is the same as the original value, we can
can use the original value as-is. If the new value is
different, we use it and insert the most-significant 32-bits
of the original value into the final result. */
nval = ((value & (((HOST_WIDE_INT) 2 << 31) - 1))
^ ((HOST_WIDE_INT) 1 << 31)) - ((HOST_WIDE_INT) 1 << 31);
if (value != nval)
{
#if HOST_BITS_PER_WIDE_INT > 32
insv = value >= 0 ? value >> 32 : ~(~value >> 32);
#endif
insert = 1;
value = nval;
operand1 = GEN_INT (nval);
}
}
if (reload_in_progress || reload_completed)
temp = scratch_reg ? scratch_reg : operand0;
else
temp = gen_reg_rtx (mode);
/* We don't directly split DImode constants on 32-bit targets
because PLUS uses an 11-bit immediate and the insn sequence
generated is not as efficient as the one using HIGH/LO_SUM. */
if (GET_CODE (operand1) == CONST_INT
&& GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
&& !insert)
{
/* Directly break constant into high and low parts. This
provides better optimization opportunities because various
passes recognize constants split with PLUS but not LO_SUM.
We use a 14-bit signed low part except when the addition
of 0x4000 to the high part might change the sign of the
high part. */
HOST_WIDE_INT low = value & 0x3fff;
HOST_WIDE_INT high = value & ~ 0x3fff;
if (low >= 0x2000)
{
if (high == 0x7fffc000 || (mode == HImode && high == 0x4000))
high += 0x2000;
else
high += 0x4000;
}
low = value - high;
emit_insn (gen_rtx_SET (VOIDmode, temp, GEN_INT (high)));
operands[1] = gen_rtx_PLUS (mode, temp, GEN_INT (low));
}
else
{
emit_insn (gen_rtx_SET (VOIDmode, temp,
gen_rtx_HIGH (mode, operand1)));
operands[1] = gen_rtx_LO_SUM (mode, temp, operand1);
}
insn = emit_move_insn (operands[0], operands[1]);
/* Now insert the most significant 32 bits of the value
into the register. When we don't have a second register
available, it could take up to nine instructions to load
a 64-bit integer constant. Prior to reload, we force
constants that would take more than three instructions
to load to the constant pool. During and after reload,
we have to handle all possible values. */
if (insert)
{
/* Use a HIGH/LO_SUM/INSV sequence if we have a second
register and the value to be inserted is outside the
range that can be loaded with three depdi instructions. */
if (temp != operand0 && (insv >= 16384 || insv < -16384))
{
operand1 = GEN_INT (insv);
emit_insn (gen_rtx_SET (VOIDmode, temp,
gen_rtx_HIGH (mode, operand1)));
emit_move_insn (temp, gen_rtx_LO_SUM (mode, temp, operand1));
emit_insn (gen_insv (operand0, GEN_INT (32),
const0_rtx, temp));
}
else
{
int len = 5, pos = 27;
/* Insert the bits using the depdi instruction. */
while (pos >= 0)
{
HOST_WIDE_INT v5 = ((insv & 31) ^ 16) - 16;
HOST_WIDE_INT sign = v5 < 0;
/* Left extend the insertion. */
insv = (insv >= 0 ? insv >> len : ~(~insv >> len));
while (pos > 0 && (insv & 1) == sign)
{
insv = (insv >= 0 ? insv >> 1 : ~(~insv >> 1));
len += 1;
pos -= 1;
}
emit_insn (gen_insv (operand0, GEN_INT (len),
GEN_INT (pos), GEN_INT (v5)));
len = pos > 0 && pos < 5 ? pos : 5;
pos -= len;
}
}
}
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_EQUAL, op1, REG_NOTES (insn));
return 1;
}
}
/* Now have insn-emit do whatever it normally does. */
return 0;
}
/* Examine EXP and return nonzero if it contains an ADDR_EXPR (meaning
it will need a link/runtime reloc). */
int
reloc_needed (tree exp)
{
int reloc = 0;
switch (TREE_CODE (exp))
{
case ADDR_EXPR:
return 1;
case PLUS_EXPR:
case MINUS_EXPR:
reloc = reloc_needed (TREE_OPERAND (exp, 0));
reloc |= reloc_needed (TREE_OPERAND (exp, 1));
break;
case NOP_EXPR:
case CONVERT_EXPR:
case NON_LVALUE_EXPR:
reloc = reloc_needed (TREE_OPERAND (exp, 0));
break;
case CONSTRUCTOR:
{
register tree link;
for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
if (TREE_VALUE (link) != 0)
reloc |= reloc_needed (TREE_VALUE (link));
}
break;
case ERROR_MARK:
break;
default:
break;
}
return reloc;
}
/* Does operand (which is a symbolic_operand) live in text space?
If so, SYMBOL_REF_FLAG, which is set by pa_encode_section_info,
will be true. */
int
read_only_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (operand) == CONST)
operand = XEXP (XEXP (operand, 0), 0);
if (flag_pic)
{
if (GET_CODE (operand) == SYMBOL_REF)
return SYMBOL_REF_FLAG (operand) && !CONSTANT_POOL_ADDRESS_P (operand);
}
else
{
if (GET_CODE (operand) == SYMBOL_REF)
return SYMBOL_REF_FLAG (operand) || CONSTANT_POOL_ADDRESS_P (operand);
}
return 1;
}
/* Return the best assembler insn template
for moving operands[1] into operands[0] as a fullword. */
const char *
singlemove_string (rtx *operands)
{
HOST_WIDE_INT intval;
if (GET_CODE (operands[0]) == MEM)
return "stw %r1,%0";
if (GET_CODE (operands[1]) == MEM)
return "ldw %1,%0";
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
long i;
REAL_VALUE_TYPE d;
if (GET_MODE (operands[1]) != SFmode)
abort ();
/* Translate the CONST_DOUBLE to a CONST_INT with the same target
bit pattern. */
REAL_VALUE_FROM_CONST_DOUBLE (d, operands[1]);
REAL_VALUE_TO_TARGET_SINGLE (d, i);
operands[1] = GEN_INT (i);
/* Fall through to CONST_INT case. */
}
if (GET_CODE (operands[1]) == CONST_INT)
{
intval = INTVAL (operands[1]);
if (VAL_14_BITS_P (intval))
return "ldi %1,%0";
else if ((intval & 0x7ff) == 0)
return "ldil L'%1,%0";
else if (zdepi_cint_p (intval))
return "{zdepi %Z1,%0|depwi,z %Z1,%0}";
else
return "ldil L'%1,%0\n\tldo R'%1(%0),%0";
}
return "copy %1,%0";
}
/* Compute position (in OP[1]) and width (in OP[2])
useful for copying IMM to a register using the zdepi
instructions. Store the immediate value to insert in OP[0]. */
static void
compute_zdepwi_operands (unsigned HOST_WIDE_INT imm, unsigned *op)
{
int lsb, len;
/* Find the least significant set bit in IMM. */
for (lsb = 0; lsb < 32; lsb++)
{
if ((imm & 1) != 0)
break;
imm >>= 1;
}
/* Choose variants based on *sign* of the 5-bit field. */
if ((imm & 0x10) == 0)
len = (lsb <= 28) ? 4 : 32 - lsb;
else
{
/* Find the width of the bitstring in IMM. */
for (len = 5; len < 32; len++)
{
if ((imm & (1 << len)) == 0)
break;
}
/* Sign extend IMM as a 5-bit value. */
imm = (imm & 0xf) - 0x10;
}
op[0] = imm;
op[1] = 31 - lsb;
op[2] = len;
}
/* Compute position (in OP[1]) and width (in OP[2])
useful for copying IMM to a register using the depdi,z
instructions. Store the immediate value to insert in OP[0]. */
void
compute_zdepdi_operands (unsigned HOST_WIDE_INT imm, unsigned *op)
{
HOST_WIDE_INT lsb, len;
/* Find the least significant set bit in IMM. */
for (lsb = 0; lsb < HOST_BITS_PER_WIDE_INT; lsb++)
{
if ((imm & 1) != 0)
break;
imm >>= 1;
}
/* Choose variants based on *sign* of the 5-bit field. */
if ((imm & 0x10) == 0)
len = ((lsb <= HOST_BITS_PER_WIDE_INT - 4)
? 4 : HOST_BITS_PER_WIDE_INT - lsb);
else
{
/* Find the width of the bitstring in IMM. */
for (len = 5; len < HOST_BITS_PER_WIDE_INT; len++)
{
if ((imm & ((unsigned HOST_WIDE_INT) 1 << len)) == 0)
break;
}
/* Sign extend IMM as a 5-bit value. */
imm = (imm & 0xf) - 0x10;
}
op[0] = imm;
op[1] = 63 - lsb;
op[2] = len;
}
/* Output assembler code to perform a doubleword move insn
with operands OPERANDS. */
const char *
output_move_double (rtx *operands)
{
enum { REGOP, OFFSOP, MEMOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
rtx addreg0 = 0, addreg1 = 0;
/* First classify both operands. */
if (REG_P (operands[0]))
optype0 = REGOP;
else if (offsettable_memref_p (operands[0]))
optype0 = OFFSOP;
else if (GET_CODE (operands[0]) == MEM)
optype0 = MEMOP;
else
optype0 = RNDOP;
if (REG_P (operands[1]))
optype1 = REGOP;
else if (CONSTANT_P (operands[1]))
optype1 = CNSTOP;
else if (offsettable_memref_p (operands[1]))
optype1 = OFFSOP;
else if (GET_CODE (operands[1]) == MEM)
optype1 = MEMOP;
else
optype1 = RNDOP;
/* Check for the cases that the operand constraints are not
supposed to allow to happen. Abort if we get one,
because generating code for these cases is painful. */
if (optype0 != REGOP && optype1 != REGOP)
abort ();
/* Handle auto decrementing and incrementing loads and stores
specifically, since the structure of the function doesn't work
for them without major modification. Do it better when we learn
this port about the general inc/dec addressing of PA.
(This was written by tege. Chide him if it doesn't work.) */
if (optype0 == MEMOP)
{
/* We have to output the address syntax ourselves, since print_operand
doesn't deal with the addresses we want to use. Fix this later. */
rtx addr = XEXP (operands[0], 0);
if (GET_CODE (addr) == POST_INC || GET_CODE (addr) == POST_DEC)
{
rtx high_reg = gen_rtx_SUBREG (SImode, operands[1], 0);
operands[0] = XEXP (addr, 0);
if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG)
abort ();
if (!reg_overlap_mentioned_p (high_reg, addr))
{
/* No overlap between high target register and address
register. (We do this in a non-obvious way to
save a register file writeback) */
if (GET_CODE (addr) == POST_INC)
return "{stws|stw},ma %1,8(%0)\n\tstw %R1,-4(%0)";
return "{stws|stw},ma %1,-8(%0)\n\tstw %R1,12(%0)";
}
else
abort ();
}
else if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC)
{
rtx high_reg = gen_rtx_SUBREG (SImode, operands[1], 0);
operands[0] = XEXP (addr, 0);
if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG)
abort ();
if (!reg_overlap_mentioned_p (high_reg, addr))
{
/* No overlap between high target register and address
register. (We do this in a non-obvious way to
save a register file writeback) */
if (GET_CODE (addr) == PRE_INC)
return "{stws|stw},mb %1,8(%0)\n\tstw %R1,4(%0)";
return "{stws|stw},mb %1,-8(%0)\n\tstw %R1,4(%0)";
}
else
abort ();
}
}
if (optype1 == MEMOP)
{
/* We have to output the address syntax ourselves, since print_operand
doesn't deal with the addresses we want to use. Fix this later. */
rtx addr = XEXP (operands[1], 0);
if (GET_CODE (addr) == POST_INC || GET_CODE (addr) == POST_DEC)
{
rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0);
operands[1] = XEXP (addr, 0);
if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG)
abort ();
if (!reg_overlap_mentioned_p (high_reg, addr))
{
/* No overlap between high target register and address
register. (We do this in a non-obvious way to
save a register file writeback) */
if (GET_CODE (addr) == POST_INC)
return "{ldws|ldw},ma 8(%1),%0\n\tldw -4(%1),%R0";
return "{ldws|ldw},ma -8(%1),%0\n\tldw 12(%1),%R0";
}
else
{
/* This is an undefined situation. We should load into the
address register *and* update that register. Probably
we don't need to handle this at all. */
if (GET_CODE (addr) == POST_INC)
return "ldw 4(%1),%R0\n\t{ldws|ldw},ma 8(%1),%0";
return "ldw 4(%1),%R0\n\t{ldws|ldw},ma -8(%1),%0";
}
}
else if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC)
{
rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0);
operands[1] = XEXP (addr, 0);
if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG)
abort ();
if (!reg_overlap_mentioned_p (high_reg, addr))
{
/* No overlap between high target register and address
register. (We do this in a non-obvious way to
save a register file writeback) */
if (GET_CODE (addr) == PRE_INC)
return "{ldws|ldw},mb 8(%1),%0\n\tldw 4(%1),%R0";
return "{ldws|ldw},mb -8(%1),%0\n\tldw 4(%1),%R0";
}
else
{
/* This is an undefined situation. We should load into the
address register *and* update that register. Probably
we don't need to handle this at all. */
if (GET_CODE (addr) == PRE_INC)
return "ldw 12(%1),%R0\n\t{ldws|ldw},mb 8(%1),%0";
return "ldw -4(%1),%R0\n\t{ldws|ldw},mb -8(%1),%0";
}
}
else if (GET_CODE (addr) == PLUS
&& GET_CODE (XEXP (addr, 0)) == MULT)
{
rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0);
if (!reg_overlap_mentioned_p (high_reg, addr))
{
rtx xoperands[3];
xoperands[0] = high_reg;
xoperands[1] = XEXP (addr, 1);
xoperands[2] = XEXP (XEXP (addr, 0), 0);
xoperands[3] = XEXP (XEXP (addr, 0), 1);
output_asm_insn ("{sh%O3addl %2,%1,%0|shladd,l %2,%O3,%1,%0}",
xoperands);
return "ldw 4(%0),%R0\n\tldw 0(%0),%0";
}
else
{
rtx xoperands[3];
xoperands[0] = high_reg;
xoperands[1] = XEXP (addr, 1);
xoperands[2] = XEXP (XEXP (addr, 0), 0);
xoperands[3] = XEXP (XEXP (addr, 0), 1);
output_asm_insn ("{sh%O3addl %2,%1,%R0|shladd,l %2,%O3,%1,%R0}",
xoperands);
return "ldw 0(%R0),%0\n\tldw 4(%R0),%R0";
}
}
}
/* If an operand is an unoffsettable memory ref, find a register
we can increment temporarily to make it refer to the second word. */
if (optype0 == MEMOP)
addreg0 = find_addr_reg (XEXP (operands[0], 0));
if (optype1 == MEMOP)
addreg1 = find_addr_reg (XEXP (operands[1], 0));
/* Ok, we can do one word at a time.
Normally we do the low-numbered word first.
In either case, set up in LATEHALF the operands to use
for the high-numbered word and in some cases alter the
operands in OPERANDS to be suitable for the low-numbered word. */
if (optype0 == REGOP)
latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
else if (optype0 == OFFSOP)
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]) + 1);
else if (optype1 == OFFSOP)
latehalf[1] = adjust_address (operands[1], SImode, 4);
else if (optype1 == CNSTOP)
split_double (operands[1], &operands[1], &latehalf[1]);
else
latehalf[1] = operands[1];
/* If the first move would clobber the source of the second one,
do them in the other order.
This can happen in two cases:
mem -> register where the first half of the destination register
is the same register used in the memory's address. Reload
can create such insns.
mem in this case will be either register indirect or register
indirect plus a valid offset.
register -> register move where REGNO(dst) == REGNO(src + 1)
someone (Tim/Tege?) claimed this can happen for parameter loads.
Handle mem -> register case first. */
if (optype0 == REGOP
&& (optype1 == MEMOP || optype1 == OFFSOP)
&& refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
operands[1], 0))
{
/* Do the late half first. */
if (addreg1)
output_asm_insn ("ldo 4(%0),%0", &addreg1);
output_asm_insn (singlemove_string (latehalf), latehalf);
/* Then clobber. */
if (addreg1)
output_asm_insn ("ldo -4(%0),%0", &addreg1);
return singlemove_string (operands);
}
/* Now handle register -> register case. */
if (optype0 == REGOP && optype1 == REGOP
&& REGNO (operands[0]) == REGNO (operands[1]) + 1)
{
output_asm_insn (singlemove_string (latehalf), latehalf);
return singlemove_string (operands);
}
/* Normal case: do the two words, low-numbered first. */
output_asm_insn (singlemove_string (operands), operands);
/* Make any unoffsettable addresses point at high-numbered word. */
if (addreg0)
output_asm_insn ("ldo 4(%0),%0", &addreg0);
if (addreg1)
output_asm_insn ("ldo 4(%0),%0", &addreg1);
/* Do that word. */
output_asm_insn (singlemove_string (latehalf), latehalf);
/* Undo the adds we just did. */
if (addreg0)
output_asm_insn ("ldo -4(%0),%0", &addreg0);
if (addreg1)
output_asm_insn ("ldo -4(%0),%0", &addreg1);
return "";
}
const char *
output_fp_move_double (rtx *operands)
{
if (FP_REG_P (operands[0]))
{
if (FP_REG_P (operands[1])
|| operands[1] == CONST0_RTX (GET_MODE (operands[0])))
output_asm_insn ("fcpy,dbl %f1,%0", operands);
else
output_asm_insn ("fldd%F1 %1,%0", operands);
}
else if (FP_REG_P (operands[1]))
{
output_asm_insn ("fstd%F0 %1,%0", operands);
}
else if (operands[1] == CONST0_RTX (GET_MODE (operands[0])))
{
if (GET_CODE (operands[0]) == REG)
{
rtx xoperands[2];
xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
xoperands[0] = operands[0];
output_asm_insn ("copy %%r0,%0\n\tcopy %%r0,%1", xoperands);
}
/* This is a pain. You have to be prepared to deal with an
arbitrary address here including pre/post increment/decrement.
so avoid this in the MD. */
else
abort ();
}
else abort ();
return "";
}
/* Return a REG that occurs in ADDR with coefficient 1.
ADDR can be effectively incremented by incrementing REG. */
static rtx
find_addr_reg (rtx addr)
{
while (GET_CODE (addr) == PLUS)
{
if (GET_CODE (XEXP (addr, 0)) == REG)
addr = XEXP (addr, 0);
else if (GET_CODE (XEXP (addr, 1)) == REG)
addr = XEXP (addr, 1);
else if (CONSTANT_P (XEXP (addr, 0)))
addr = XEXP (addr, 1);
else if (CONSTANT_P (XEXP (addr, 1)))
addr = XEXP (addr, 0);
else
abort ();
}
if (GET_CODE (addr) == REG)
return addr;
abort ();
}
/* Emit code to perform a block move.
OPERANDS[0] is the destination pointer as a REG, clobbered.
OPERANDS[1] is the source pointer as a REG, clobbered.
OPERANDS[2] is a register for temporary storage.
OPERANDS[3] is a register for temporary storage.
OPERANDS[4] is the size as a CONST_INT
OPERANDS[5] is the alignment safe to use, as a CONST_INT.
OPERANDS[6] is another temporary register. */
const char *
output_block_move (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
{
int align = INTVAL (operands[5]);
unsigned long n_bytes = INTVAL (operands[4]);
/* We can't move more than a word at a time because the PA
has no longer integer move insns. (Could use fp mem ops?) */
if (align > (TARGET_64BIT ? 8 : 4))
align = (TARGET_64BIT ? 8 : 4);
/* Note that we know each loop below will execute at least twice
(else we would have open-coded the copy). */
switch (align)
{
case 8:
/* Pre-adjust the loop counter. */
operands[4] = GEN_INT (n_bytes - 16);
output_asm_insn ("ldi %4,%2", operands);
/* Copying loop. */
output_asm_insn ("ldd,ma 8(%1),%3", operands);
output_asm_insn ("ldd,ma 8(%1),%6", operands);
output_asm_insn ("std,ma %3,8(%0)", operands);
output_asm_insn ("addib,>= -16,%2,.-12", operands);
output_asm_insn ("std,ma %6,8(%0)", operands);
/* Handle the residual. There could be up to 7 bytes of
residual to copy! */
if (n_bytes % 16 != 0)
{
operands[4] = GEN_INT (n_bytes % 8);
if (n_bytes % 16 >= 8)
output_asm_insn ("ldd,ma 8(%1),%3", operands);
if (n_bytes % 8 != 0)
output_asm_insn ("ldd 0(%1),%6", operands);
if (n_bytes % 16 >= 8)
output_asm_insn ("std,ma %3,8(%0)", operands);
if (n_bytes % 8 != 0)
output_asm_insn ("stdby,e %6,%4(%0)", operands);
}
return "";
case 4:
/* Pre-adjust the loop counter. */
operands[4] = GEN_INT (n_bytes - 8);
output_asm_insn ("ldi %4,%2", operands);
/* Copying loop. */
output_asm_insn ("{ldws|ldw},ma 4(%1),%3", operands);
output_asm_insn ("{ldws|ldw},ma 4(%1),%6", operands);
output_asm_insn ("{stws|stw},ma %3,4(%0)", operands);
output_asm_insn ("addib,>= -8,%2,.-12", operands);
output_asm_insn ("{stws|stw},ma %6,4(%0)", operands);
/* Handle the residual. There could be up to 7 bytes of
residual to copy! */
if (n_bytes % 8 != 0)
{
operands[4] = GEN_INT (n_bytes % 4);
if (n_bytes % 8 >= 4)
output_asm_insn ("{ldws|ldw},ma 4(%1),%3", operands);
if (n_bytes % 4 != 0)
output_asm_insn ("ldw 0(%1),%6", operands);
if (n_bytes % 8 >= 4)
output_asm_insn ("{stws|stw},ma %3,4(%0)", operands);
if (n_bytes % 4 != 0)
output_asm_insn ("{stbys|stby},e %6,%4(%0)", operands);
}
return "";
case 2:
/* Pre-adjust the loop counter. */
operands[4] = GEN_INT (n_bytes - 4);
output_asm_insn ("ldi %4,%2", operands);
/* Copying loop. */
output_asm_insn ("{ldhs|ldh},ma 2(%1),%3", operands);
output_asm_insn ("{ldhs|ldh},ma 2(%1),%6", operands);
output_asm_insn ("{sths|sth},ma %3,2(%0)", operands);
output_asm_insn ("addib,>= -4,%2,.-12", operands);
output_asm_insn ("{sths|sth},ma %6,2(%0)", operands);
/* Handle the residual. */
if (n_bytes % 4 != 0)
{
if (n_bytes % 4 >= 2)
output_asm_insn ("{ldhs|ldh},ma 2(%1),%3", operands);
if (n_bytes % 2 != 0)
output_asm_insn ("ldb 0(%1),%6", operands);
if (n_bytes % 4 >= 2)
output_asm_insn ("{sths|sth},ma %3,2(%0)", operands);
if (n_bytes % 2 != 0)
output_asm_insn ("stb %6,0(%0)", operands);
}
return "";
case 1:
/* Pre-adjust the loop counter. */
operands[4] = GEN_INT (n_bytes - 2);
output_asm_insn ("ldi %4,%2", operands);
/* Copying loop. */
output_asm_insn ("{ldbs|ldb},ma 1(%1),%3", operands);
output_asm_insn ("{ldbs|ldb},ma 1(%1),%6", operands);
output_asm_insn ("{stbs|stb},ma %3,1(%0)", operands);
output_asm_insn ("addib,>= -2,%2,.-12", operands);
output_asm_insn ("{stbs|stb},ma %6,1(%0)", operands);
/* Handle the residual. */
if (n_bytes % 2 != 0)
{
output_asm_insn ("ldb 0(%1),%3", operands);
output_asm_insn ("stb %3,0(%0)", operands);
}
return "";
default:
abort ();
}
}
/* Count the number of insns necessary to handle this block move.
Basic structure is the same as emit_block_move, except that we
count insns rather than emit them. */
static int
compute_movstr_length (rtx insn)
{
rtx pat = PATTERN (insn);
unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 7), 0));
unsigned long n_bytes = INTVAL (XEXP (XVECEXP (pat, 0, 6), 0));
unsigned int n_insns = 0;
/* We can't move more than four bytes at a time because the PA
has no longer integer move insns. (Could use fp mem ops?) */
if (align > (TARGET_64BIT ? 8 : 4))
align = (TARGET_64BIT ? 8 : 4);
/* The basic copying loop. */
n_insns = 6;
/* Residuals. */
if (n_bytes % (2 * align) != 0)
{
if ((n_bytes % (2 * align)) >= align)
n_insns += 2;
if ((n_bytes % align) != 0)
n_insns += 2;
}
/* Lengths are expressed in bytes now; each insn is 4 bytes. */
return n_insns * 4;
}
/* Emit code to perform a block clear.
OPERANDS[0] is the destination pointer as a REG, clobbered.
OPERANDS[1] is a register for temporary storage.
OPERANDS[2] is the size as a CONST_INT
OPERANDS[3] is the alignment safe to use, as a CONST_INT. */
const char *
output_block_clear (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
{
int align = INTVAL (operands[3]);
unsigned long n_bytes = INTVAL (operands[2]);
/* We can't clear more than a word at a time because the PA
has no longer integer move insns. */
if (align > (TARGET_64BIT ? 8 : 4))
align = (TARGET_64BIT ? 8 : 4);
/* Note that we know each loop below will execute at least twice
(else we would have open-coded the copy). */
switch (align)
{
case 8:
/* Pre-adjust the loop counter. */
operands[2] = GEN_INT (n_bytes - 16);
output_asm_insn ("ldi %2,%1", operands);
/* Loop. */
output_asm_insn ("std,ma %%r0,8(%0)", operands);
output_asm_insn ("addib,>= -16,%1,.-4", operands);
output_asm_insn ("std,ma %%r0,8(%0)", operands);
/* Handle the residual. There could be up to 7 bytes of
residual to copy! */
if (n_bytes % 16 != 0)
{
operands[2] = GEN_INT (n_bytes % 8);
if (n_bytes % 16 >= 8)
output_asm_insn ("std,ma %%r0,8(%0)", operands);
if (n_bytes % 8 != 0)
output_asm_insn ("stdby,e %%r0,%2(%0)", operands);
}
return "";
case 4:
/* Pre-adjust the loop counter. */
operands[2] = GEN_INT (n_bytes - 8);
output_asm_insn ("ldi %2,%1", operands);
/* Loop. */
output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
output_asm_insn ("addib,>= -8,%1,.-4", operands);
output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
/* Handle the residual. There could be up to 7 bytes of
residual to copy! */
if (n_bytes % 8 != 0)
{
operands[2] = GEN_INT (n_bytes % 4);
if (n_bytes % 8 >= 4)
output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
if (n_bytes % 4 != 0)
output_asm_insn ("{stbys|stby},e %%r0,%2(%0)", operands);
}
return "";
case 2:
/* Pre-adjust the loop counter. */
operands[2] = GEN_INT (n_bytes - 4);
output_asm_insn ("ldi %2,%1", operands);
/* Loop. */
output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
output_asm_insn ("addib,>= -4,%1,.-4", operands);
output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
/* Handle the residual. */
if (n_bytes % 4 != 0)
{
if (n_bytes % 4 >= 2)
output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
if (n_bytes % 2 != 0)
output_asm_insn ("stb %%r0,0(%0)", operands);
}
return "";
case 1:
/* Pre-adjust the loop counter. */
operands[2] = GEN_INT (n_bytes - 2);
output_asm_insn ("ldi %2,%1", operands);
/* Loop. */
output_asm_insn ("{stbs|stb},ma %%r0,1(%0)", operands);
output_asm_insn ("addib,>= -2,%1,.-4", operands);
output_asm_insn ("{stbs|stb},ma %%r0,1(%0)", operands);
/* Handle the residual. */
if (n_bytes % 2 != 0)
output_asm_insn ("stb %%r0,0(%0)", operands);
return "";
default:
abort ();
}
}
/* Count the number of insns necessary to handle this block move.
Basic structure is the same as emit_block_move, except that we
count insns rather than emit them. */
static int
compute_clrstr_length (rtx insn)
{
rtx pat = PATTERN (insn);
unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 4), 0));
unsigned long n_bytes = INTVAL (XEXP (XVECEXP (pat, 0, 3), 0));
unsigned int n_insns = 0;
/* We can't clear more than a word at a time because the PA
has no longer integer move insns. */
if (align > (TARGET_64BIT ? 8 : 4))
align = (TARGET_64BIT ? 8 : 4);
/* The basic loop. */
n_insns = 4;
/* Residuals. */
if (n_bytes % (2 * align) != 0)
{
if ((n_bytes % (2 * align)) >= align)
n_insns++;
if ((n_bytes % align) != 0)
n_insns++;
}
/* Lengths are expressed in bytes now; each insn is 4 bytes. */
return n_insns * 4;
}
const char *
output_and (rtx *operands)
{
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0)
{
unsigned HOST_WIDE_INT mask = INTVAL (operands[2]);
int ls0, ls1, ms0, p, len;
for (ls0 = 0; ls0 < 32; ls0++)
if ((mask & (1 << ls0)) == 0)
break;
for (ls1 = ls0; ls1 < 32; ls1++)
if ((mask & (1 << ls1)) != 0)
break;
for (ms0 = ls1; ms0 < 32; ms0++)
if ((mask & (1 << ms0)) == 0)
break;
if (ms0 != 32)
abort ();
if (ls1 == 32)
{
len = ls0;
if (len == 0)
abort ();
operands[2] = GEN_INT (len);
return "{extru|extrw,u} %1,31,%2,%0";
}
else
{
/* We could use this `depi' for the case above as well, but `depi'
requires one more register file access than an `extru'. */
p = 31 - ls0;
len = ls1 - ls0;
operands[2] = GEN_INT (p);
operands[3] = GEN_INT (len);
return "{depi|depwi} 0,%2,%3,%0";
}
}
else
return "and %1,%2,%0";
}
/* Return a string to perform a bitwise-and of operands[1] with operands[2]
storing the result in operands[0]. */
const char *
output_64bit_and (rtx *operands)
{
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0)
{
unsigned HOST_WIDE_INT mask = INTVAL (operands[2]);
int ls0, ls1, ms0, p, len;
for (ls0 = 0; ls0 < HOST_BITS_PER_WIDE_INT; ls0++)
if ((mask & ((unsigned HOST_WIDE_INT) 1 << ls0)) == 0)
break;
for (ls1 = ls0; ls1 < HOST_BITS_PER_WIDE_INT; ls1++)
if ((mask & ((unsigned HOST_WIDE_INT) 1 << ls1)) != 0)
break;
for (ms0 = ls1; ms0 < HOST_BITS_PER_WIDE_INT; ms0++)
if ((mask & ((unsigned HOST_WIDE_INT) 1 << ms0)) == 0)
break;
if (ms0 != HOST_BITS_PER_WIDE_INT)
abort ();
if (ls1 == HOST_BITS_PER_WIDE_INT)
{
len = ls0;
if (len == 0)
abort ();
operands[2] = GEN_INT (len);
return "extrd,u %1,63,%2,%0";
}
else
{
/* We could use this `depi' for the case above as well, but `depi'
requires one more register file access than an `extru'. */