/* Subroutines for insn-output.c for HPPA.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c
This file is part of GCC.
static void copy_reg_pointer (rtx, rtx);
static void fix_range (const char *);
static bool pa_handle_option (size_t, const char *, int);
-static int hppa_address_cost (rtx);
-static bool hppa_rtx_costs (rtx, int, int, int *);
+static int hppa_address_cost (rtx, bool);
+static bool hppa_rtx_costs (rtx, int, int, int *, bool);
static inline rtx force_mode (enum machine_mode, rtx);
static void pa_reorg (void);
static void pa_combine_instructions (void);
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 update_total_code_bytes (unsigned 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 void pa_init_builtins (void);
static rtx hppa_builtin_saveregs (void);
static void hppa_va_start (tree, rtx);
-static tree hppa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
+static tree hppa_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
static bool pa_scalar_mode_supported_p (enum machine_mode);
static bool pa_commutative_p (const_rtx x, int outer_code);
static void copy_fp_args (rtx) ATTRIBUTE_UNUSED;
/* 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;
+static unsigned int last_address;
/* Variables to handle plabels that we discover are necessary at assembly
output time. They are output after the current function. */
static struct machine_function *
pa_init_machine_status (void)
{
- return ggc_alloc_cleared (sizeof (machine_function));
+ return GGC_CNEW (machine_function);
}
/* If FROM is a probable pointer register, mark TO as a probable
/* Labels need special handling. */
if (pic_label_operand (orig, mode))
{
+ rtx insn;
+
/* We do not want to go through the movXX expanders here since that
would create recursion.
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;
+ insn = emit_insn (gen_rtx_SET (VOIDmode, reg, orig));
+
+ /* 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));
+
+ /* During and after reload, we need to generate a REG_LABEL_OPERAND note
+ and update LABEL_NUSES because this is not done automatically. */
+ if (reload_in_progress || reload_completed)
+ {
+ /* Extract LABEL_REF. */
+ if (GET_CODE (orig) == CONST)
+ orig = XEXP (XEXP (orig, 0), 0);
+ /* Extract CODE_LABEL. */
+ orig = XEXP (orig, 0);
+ add_reg_note (insn, REG_LABEL_OPERAND, orig);
+ LABEL_NUSES (orig)++;
+ }
+ crtl->uses_pic_offset_table = 1;
return reg;
}
if (GET_CODE (orig) == SYMBOL_REF)
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_const_mem (Pmode,
- gen_rtx_LO_SUM (Pmode, tmp_reg,
- gen_rtx_UNSPEC (Pmode,
+ if (function_label_operand (orig, mode))
+ {
+ /* Force function label into memory in word mode. */
+ orig = XEXP (force_const_mem (word_mode, orig), 0);
+ /* Load plabel address from DLT. */
+ emit_move_insn (tmp_reg,
+ gen_rtx_PLUS (word_mode, pic_offset_table_rtx,
+ gen_rtx_HIGH (word_mode, orig)));
+ pic_ref
+ = gen_const_mem (Pmode,
+ gen_rtx_LO_SUM (Pmode, tmp_reg,
+ gen_rtx_UNSPEC (Pmode,
gen_rtvec (1, orig),
UNSPEC_DLTIND14R)));
+ emit_move_insn (reg, pic_ref);
+ /* Now load address of function descriptor. */
+ pic_ref = gen_rtx_MEM (Pmode, reg);
+ }
+ else
+ {
+ /* Load symbol reference from DLT. */
+ emit_move_insn (tmp_reg,
+ gen_rtx_PLUS (word_mode, pic_offset_table_rtx,
+ gen_rtx_HIGH (word_mode, orig)));
+ pic_ref
+ = gen_const_mem (Pmode,
+ gen_rtx_LO_SUM (Pmode, tmp_reg,
+ gen_rtx_UNSPEC (Pmode,
+ gen_rtvec (1, orig),
+ UNSPEC_DLTIND14R)));
+ }
- current_function_uses_pic_offset_table = 1;
+ crtl->uses_pic_offset_table = 1;
mark_reg_pointer (reg, BITS_PER_UNIT);
insn = emit_move_insn (reg, pic_ref);
int mask;
mask = (GET_MODE_CLASS (mode) == MODE_FLOAT
- ? (TARGET_PA_20 ? 0x3fff : 0x1f) : 0x3fff);
+ ? (INT14_OK_STRICT ? 0x3fff : 0x1f) : 0x3fff);
/* Choose which way to round the offset. Round up if we
are >= halfway to the next boundary. */
as GO_IF_LEGITIMATE_ADDRESS. */
static int
-hppa_address_cost (rtx X)
+hppa_address_cost (rtx X,
+ bool speed ATTRIBUTE_UNUSED)
{
switch (GET_CODE (X))
{
scanned. In either case, *TOTAL contains the cost result. */
static bool
-hppa_rtx_costs (rtx x, int code, int outer_code, int *total)
+hppa_rtx_costs (rtx x, int code, int outer_code, int *total,
+ bool speed ATTRIBUTE_UNUSED)
{
switch (code)
{
decl = TREE_OPERAND (decl, 1);
type = TREE_TYPE (decl);
- if (TREE_CODE (type) == ARRAY_TYPE)
- type = get_inner_array_type (type);
+ type = strip_array_types (type);
if (POINTER_TYPE_P (type))
{
reloc |= reloc_needed (TREE_OPERAND (exp, 1));
break;
- case NOP_EXPR:
- case CONVERT_EXPR:
+ CASE_CONVERT:
case NON_LVALUE_EXPR:
reloc = reloc_needed (TREE_OPERAND (exp, 0));
break;
/* If the current function calls __builtin_eh_return, then we need
to allocate stack space for registers that will hold data for
the exception handler. */
- if (DO_FRAME_NOTES && current_function_calls_eh_return)
+ if (DO_FRAME_NOTES && crtl->calls_eh_return)
{
unsigned int i;
size of the current function's stack frame. We don't need to align
for the outgoing arguments as their alignment is set by the final
rounding for the frame as a whole. */
- size += current_function_outgoing_args_size;
+ size += crtl->outgoing_args_size;
/* Allocate space for the fixed frame marker. This space must be
allocated for any function that makes calls or allocates
/* Save RP first. The calling conventions manual states RP will
always be stored into the caller's frame at sp - 20 or sp - 16
depending on which ABI is in use. */
- if (df_regs_ever_live_p (2) || current_function_calls_eh_return)
+ if (df_regs_ever_live_p (2) || crtl->calls_eh_return)
{
store_reg (2, TARGET_64BIT ? -16 : -20, STACK_POINTER_REGNUM);
rp_saved = true;
/* Saving the EH return data registers in the frame is the simplest
way to get the frame unwind information emitted. We put them
just before the general registers. */
- if (DO_FRAME_NOTES && current_function_calls_eh_return)
+ if (DO_FRAME_NOTES && crtl->calls_eh_return)
{
unsigned int i, regno;
/* Saving the EH return data registers in the frame is the simplest
way to get the frame unwind information emitted. */
- if (DO_FRAME_NOTES && current_function_calls_eh_return)
+ if (DO_FRAME_NOTES && crtl->calls_eh_return)
{
unsigned int i, regno;
/* Update the total code bytes output to the text section. */
static void
-update_total_code_bytes (int nbytes)
+update_total_code_bytes (unsigned int nbytes)
{
if ((TARGET_PORTABLE_RUNTIME || !TARGET_GAS || !TARGET_SOM)
&& !IN_NAMED_SECTION_P (cfun->decl))
{
- if (INSN_ADDRESSES_SET_P ())
- {
- unsigned long old_total = total_code_bytes;
+ unsigned int old_total = total_code_bytes;
- total_code_bytes += nbytes;
+ total_code_bytes += nbytes;
- /* Be prepared to handle overflows. */
- if (old_total > total_code_bytes)
- total_code_bytes = -1;
- }
- else
- total_code_bytes = -1;
+ /* Be prepared to handle overflows. */
+ if (old_total > total_code_bytes)
+ total_code_bytes = UINT_MAX;
}
}
last_address = ((last_address + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)
& ~(FUNCTION_BOUNDARY / BITS_PER_UNIT - 1));
}
+ else
+ last_address = UINT_MAX;
/* Finally, update the total number of code bytes output so far. */
update_total_code_bytes (last_address);
/* If the current function calls __builtin_eh_return, then we need
to restore the saved EH data registers. */
- if (DO_FRAME_NOTES && current_function_calls_eh_return)
+ if (DO_FRAME_NOTES && crtl->calls_eh_return)
{
unsigned int i, regno;
/* If the current function calls __builtin_eh_return, then we need
to restore the saved EH data registers. */
- if (DO_FRAME_NOTES && current_function_calls_eh_return)
+ if (DO_FRAME_NOTES && crtl->calls_eh_return)
{
unsigned int i, regno;
if (ret_off != 0)
load_reg (2, ret_off, STACK_POINTER_REGNUM);
- if (DO_FRAME_NOTES && current_function_calls_eh_return)
+ if (DO_FRAME_NOTES && crtl->calls_eh_return)
{
rtx sa = EH_RETURN_STACKADJ_RTX;
}
\f
static enum reg_class
-pa_secondary_reload (bool in_p, rtx x, enum reg_class class,
+pa_secondary_reload (bool in_p, rtx x, enum reg_class rclass,
enum machine_mode mode, secondary_reload_info *sri)
{
int is_symbolic, regno;
/* Handle the easy stuff first. */
- if (class == R1_REGS)
+ if (rclass == R1_REGS)
return NO_REGS;
if (REG_P (x))
{
regno = REGNO (x);
- if (class == BASE_REG_CLASS && regno < FIRST_PSEUDO_REGISTER)
+ if (rclass == BASE_REG_CLASS && regno < FIRST_PSEUDO_REGISTER)
return NO_REGS;
}
else
generation requires %r1 as a scratch register. */
if (flag_pic
&& (mode == SImode || mode == DImode)
- && FP_REG_CLASS_P (class)
+ && FP_REG_CLASS_P (rclass)
&& (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE))
{
sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1
memory loads and stores. */
if ((regno >= FIRST_PSEUDO_REGISTER || regno == -1)
&& GET_MODE_CLASS (mode) == MODE_INT
- && FP_REG_CLASS_P (class))
+ && FP_REG_CLASS_P (rclass))
{
/* Reload passes (mem:SI (reg/f:DI 30 %r30) when it wants to check
the secondary reload needed for a pseudo. It never passes a
/* We need a secondary register (GPR) for copies between the SAR
and anything other than a general register. */
- if (class == SHIFT_REGS && (regno <= 0 || regno >= 32))
+ if (rclass == SHIFT_REGS && (regno <= 0 || regno >= 32))
{
sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
return NO_REGS;
well as secondary memory. */
if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER
&& (REGNO_REG_CLASS (regno) == SHIFT_REGS
- && FP_REG_CLASS_P (class)))
+ && FP_REG_CLASS_P (rclass)))
{
sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
return NO_REGS;
function_arg_padding (enum machine_mode mode, const_tree type)
{
if (mode == BLKmode
- || (TARGET_64BIT && type && AGGREGATE_TYPE_P (type)))
+ || (TARGET_64BIT
+ && type
+ && (AGGREGATE_TYPE_P (type)
+ || TREE_CODE (type) == COMPLEX_TYPE
+ || TREE_CODE (type) == VECTOR_TYPE)))
{
/* Return none if justification is not required. */
if (type
? UNITS_PER_WORD : 0);
if (argadj)
- offset = plus_constant (current_function_arg_offset_rtx, argadj);
+ offset = plus_constant (crtl->args.arg_offset_rtx, argadj);
else
- offset = current_function_arg_offset_rtx;
+ offset = crtl->args.arg_offset_rtx;
if (TARGET_64BIT)
{
/* Adjust for varargs/stdarg differences. */
if (argadj)
- offset = plus_constant (current_function_arg_offset_rtx, -argadj);
+ offset = plus_constant (crtl->args.arg_offset_rtx, -argadj);
else
- offset = current_function_arg_offset_rtx;
+ offset = crtl->args.arg_offset_rtx;
/* We need to save %r26 .. %r19 inclusive starting at offset -64
from the incoming arg pointer and growing to larger addresses. */
/* Store general registers on the stack. */
dest = gen_rtx_MEM (BLKmode,
- plus_constant (current_function_internal_arg_pointer,
+ plus_constant (crtl->args.internal_arg_pointer,
-16));
set_mem_alias_set (dest, get_varargs_alias_set ());
set_mem_align (dest, BITS_PER_WORD);
emit_insn (gen_blockage ());
return copy_to_reg (expand_binop (Pmode, add_optab,
- current_function_internal_arg_pointer,
+ crtl->args.internal_arg_pointer,
offset, 0, 0, OPTAB_LIB_WIDEN));
}
}
static tree
-hppa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
+hppa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p)
{
if (TARGET_64BIT)
{
attr_length_call (rtx insn, int sibcall)
{
int local_call;
- rtx call_dest;
+ rtx call, call_dest;
tree call_decl;
int length = 0;
rtx pat = PATTERN (insn);
unsigned long distance = -1;
+ gcc_assert (GET_CODE (insn) == CALL_INSN);
+
if (INSN_ADDRESSES_SET_P ())
{
unsigned long total;
distance = -1;
}
- /* Determine if this is a local call. */
- if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL)
- call_dest = XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0);
- else
- call_dest = XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0);
+ gcc_assert (GET_CODE (pat) == PARALLEL);
+
+ /* Get the call rtx. */
+ call = XVECEXP (pat, 0, 0);
+ if (GET_CODE (call) == SET)
+ call = SET_SRC (call);
+
+ gcc_assert (GET_CODE (call) == CALL);
+ /* Determine if this is a local call. */
+ call_dest = XEXP (XEXP (call, 0), 0);
call_decl = SYMBOL_REF_DECL (call_dest);
local_call = call_decl && targetm.binds_local_p (call_decl);
length += 12;
/* long pc-relative branch sequence. */
- else if ((TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
- || (TARGET_64BIT && !TARGET_GAS)
+ else if (TARGET_LONG_PIC_SDIFF_CALL
|| (TARGET_GAS && !TARGET_SOM
&& (TARGET_LONG_PIC_PCREL_CALL || local_call)))
{
length += 20;
- if (!TARGET_PA_20 && !TARGET_NO_SPACE_REGS)
+ if (!TARGET_PA_20 && !TARGET_NO_SPACE_REGS && flag_pic)
length += 8;
}
if (!sibcall)
length += 8;
- if (!TARGET_NO_SPACE_REGS)
+ if (!TARGET_NO_SPACE_REGS && flag_pic)
length += 8;
}
}
of increasing length and complexity. In most cases,
they don't allow an instruction in the delay slot. */
if (!((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)
- && !(TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
+ && !TARGET_LONG_PIC_SDIFF_CALL
&& !(TARGET_GAS && !TARGET_SOM
&& (TARGET_LONG_PIC_PCREL_CALL || local_call))
&& !TARGET_64BIT)
if (seq_length != 0
&& GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
&& !sibcall
- && (!TARGET_PA_20 || indirect_call))
+ && (!TARGET_PA_20
+ || indirect_call
+ || ((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)))
{
/* A non-jump insn in the delay slot. By definition we can
emit this insn before the call (and in fact before argument
}
else
{
- if ((TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
- || (TARGET_64BIT && !TARGET_GAS))
+ if (TARGET_LONG_PIC_SDIFF_CALL)
{
/* The HP assembler and linker can handle relocations
- for the difference of two symbols. GAS and the HP
- linker can't do this when one of the symbols is
- external. */
+ for the difference of two symbols. The HP assembler
+ recognizes the sequence as a pc-relative call and
+ the linker provides stubs when needed. */
xoperands[1] = gen_label_rtx ();
output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
}
else
{
- if (!TARGET_NO_SPACE_REGS)
+ if (!TARGET_NO_SPACE_REGS && flag_pic)
output_asm_insn ("ldsid (%%r1),%%r31\n\tmtsp %%r31,%%sr0",
xoperands);
if (sibcall)
{
- if (TARGET_NO_SPACE_REGS)
+ if (TARGET_NO_SPACE_REGS || !flag_pic)
output_asm_insn ("be 0(%%sr4,%%r1)", xoperands);
else
output_asm_insn ("be 0(%%sr0,%%r1)", xoperands);
}
else
{
- if (TARGET_NO_SPACE_REGS)
+ if (TARGET_NO_SPACE_REGS || !flag_pic)
output_asm_insn ("ble 0(%%sr4,%%r1)", xoperands);
else
output_asm_insn ("ble 0(%%sr0,%%r1)", xoperands);
int len = strlen (str) + 1;
char *newstr, *p;
- p = newstr = alloca (len + 1);
+ p = newstr = XALLOCAVEC (char, len + 1);
*p++ = '@';
strcpy (p, str);
{
static unsigned int current_thunk_number;
int val_14 = VAL_14_BITS_P (delta);
- int nbytes = 0;
+ unsigned int old_last_address = last_address, nbytes = 0;
char label[16];
rtx xoperands[4];
nbytes = ((nbytes + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)
& ~(FUNCTION_BOUNDARY / BITS_PER_UNIT - 1));
last_address += nbytes;
+ if (old_last_address > last_address)
+ last_address = UINT_MAX;
update_total_code_bytes (nbytes);
}
profitable to do so when the frame pointer is being eliminated. */
if (!reload_completed
&& flag_omit_frame_pointer
- && !current_function_calls_alloca
+ && !cfun->calls_alloca
&& op == frame_pointer_rtx)
return 0;
static void
pa_combine_instructions (void)
{
- rtx anchor, new;
+ rtx anchor, new_rtx;
/* This can get expensive since the basic algorithm is on the
order of O(n^2) (or worse). Only do it for -O2 or higher
may be combined with "floating" insns. As the name implies,
"anchor" instructions don't move, while "floating" insns may
move around. */
- new = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, NULL_RTX, NULL_RTX));
- new = make_insn_raw (new);
+ new_rtx = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, NULL_RTX, NULL_RTX));
+ new_rtx = make_insn_raw (new_rtx);
for (anchor = get_insns (); anchor; anchor = NEXT_INSN (anchor))
{
{
/* If ANCHOR and FLOATER can be combined, then we're
done with this pass. */
- if (pa_can_combine_p (new, anchor, floater, 0,
+ if (pa_can_combine_p (new_rtx, anchor, floater, 0,
SET_DEST (PATTERN (floater)),
XEXP (SET_SRC (PATTERN (floater)), 0),
XEXP (SET_SRC (PATTERN (floater)), 1)))
{
if (GET_CODE (SET_SRC (PATTERN (floater))) == PLUS)
{
- if (pa_can_combine_p (new, anchor, floater, 0,
+ if (pa_can_combine_p (new_rtx, anchor, floater, 0,
SET_DEST (PATTERN (floater)),
XEXP (SET_SRC (PATTERN (floater)), 0),
XEXP (SET_SRC (PATTERN (floater)), 1)))
}
else
{
- if (pa_can_combine_p (new, anchor, floater, 0,
+ if (pa_can_combine_p (new_rtx, anchor, floater, 0,
SET_DEST (PATTERN (floater)),
SET_SRC (PATTERN (floater)),
SET_SRC (PATTERN (floater))))
{
/* If ANCHOR and FLOATER can be combined, then we're
done with this pass. */
- if (pa_can_combine_p (new, anchor, floater, 1,
+ if (pa_can_combine_p (new_rtx, anchor, floater, 1,
SET_DEST (PATTERN (floater)),
XEXP (SET_SRC (PATTERN (floater)),
0),
}
static int
-pa_can_combine_p (rtx new, rtx anchor, rtx floater, int reversed, rtx dest,
+pa_can_combine_p (rtx new_rtx, rtx anchor, rtx floater, int reversed, rtx dest,
rtx src1, rtx src2)
{
int insn_code_number;
If the pattern doesn't match or the constraints
aren't met keep searching for a suitable floater
insn. */
- XVECEXP (PATTERN (new), 0, 0) = PATTERN (anchor);
- XVECEXP (PATTERN (new), 0, 1) = PATTERN (floater);
- INSN_CODE (new) = -1;
- insn_code_number = recog_memoized (new);
+ XVECEXP (PATTERN (new_rtx), 0, 0) = PATTERN (anchor);
+ XVECEXP (PATTERN (new_rtx), 0, 1) = PATTERN (floater);
+ INSN_CODE (new_rtx) = -1;
+ insn_code_number = recog_memoized (new_rtx);
if (insn_code_number < 0
- || (extract_insn (new), ! constrain_operands (1)))
+ || (extract_insn (new_rtx), ! constrain_operands (1)))
return 0;
if (reversed)
#endif
/* Return true if a change from mode FROM to mode TO for a register
- in register class CLASS is invalid. */
+ in register class RCLASS is invalid. */
bool
pa_cannot_change_mode_class (enum machine_mode from, enum machine_mode to,
- enum reg_class class)
+ enum reg_class rclass)
{
if (from == to)
return false;
On the 64-bit target, this conflicts with the definition of
LOAD_EXTEND_OP. Thus, we can't allow changing between modes
with different sizes in the floating-point registers. */
- if (MAYBE_FP_REG_CLASS_P (class))
+ if (MAYBE_FP_REG_CLASS_P (rclass))
return true;
/* HARD_REGNO_MODE_OK places modes with sizes larger than a word