Commit a02aa5b0 by John David Anglin Committed by John David Anglin

pa-linux.h (ASM_OUTPUT_EXTERNAL_LIBCALL): Define.

	* pa-linux.h (ASM_OUTPUT_EXTERNAL_LIBCALL): Define.
	* pa-protos.h (attr_length_millicode_call, attr_length_call,
	pa_init_machine_status): Declare new global functions.
	* pa.c (void copy_fp_args, length_fp_args, get_plabel): Declare and
	implement new functions.
	(attr_length_millicode_call, attr_length_call): Implement.
	(total_code_bytes): Change type to long.
	(pa_output_function_prologue): Compute total_code_bytes on TARGET_64BIT.
	Reset counter if flag_function_sections.
	(output_deferred_plabels): Set output alignment to 3 for TARGET_64BIT.
	(output_cbranch): Move call to gen_label_rtx.
	(output_millicode_call): Rewrite adding long TARGET_64BIT call, expose
	delay slot in all variants, shorten pc-relative calls.
	(output_call): Rewrite adding long TARGET_64BIT call, improved delay
	slot usage and exposure, various new call variants, and shortened
	sequences for some variants on TARGET_PA_20.
	Miscellaneous format changes.
	* pa.h (total_code_bytes): Change type to long.
	(MASK_LONG_CALLS, TARGET_LONG_CALLS, TARGET_LONG_ABS_CALL,
	TARGET_LONG_PIC_SDIFF_CALL, TARGET_LONG_PIC_PCREL_CALL): Define.
	(TARGET_SWITCHES): Add "-mlong-calls" and "-mno-long-calls" options.
	(EXTRA_CONSTRAINT, GO_IF_LEGITIMATE_ADDRESS,
	LEGITIMIZE_RELOAD_ADDRESS): Don't use long floating point loads and
	stores on TARGET_ELF32.
	*pa.md (define_delay): Allow insns in delay on TARGET_PORTABLE_RUNTIME.
	(unnamed patterns for mulsi3, divsi3, udivsi3, modsi3, umodsi3 and
	canonicalize_funcptr_for_compare expanders): Calculate attribute length
	attr_length_millicode_call().
	(call_internal_symref, call_value_internal_symref): Clobber register 1.
	Calculate attribute length using attr_length_call().
	(call_internal_reg_64bit, call_value_internal_reg_64bit): Move gp load
	to delay slot.
	(sibcall, sibcall_value): Rewrite.
	(sibcall_internal_symref, sibcall_value_internal_symref): Clobber
	register 1.  Use attr_length_call().
	(sibcall_internal_symref_64bit, sibcall_value_internal_symref_64bit):
	New patterns.
	(unamed pattern for canonicalize_funcptr_for_compare): Rewrite.
	* som.h (MEMBER_TYPE_FORCES_BLK): Define.
	* t-pa64 (TARGET_LIBGCC2_CFLAGS): Add "-mlong-calls".
	* doc/invoke.texi (mlong-calls): Document.

From-SVN: r58665
parent 8c081e84
2002-10-30 John David Anglin <dave@hiauly.hia.nrc.ca>
* pa-linux.h (ASM_OUTPUT_EXTERNAL_LIBCALL): Define.
* pa-protos.h (attr_length_millicode_call, attr_length_call,
pa_init_machine_status): Declare new global functions.
* pa.c (void copy_fp_args, length_fp_args, get_plabel): Declare and
implement new functions.
(attr_length_millicode_call, attr_length_call): Implement.
(total_code_bytes): Change type to long.
(pa_output_function_prologue): Compute total_code_bytes on TARGET_64BIT.
Reset counter if flag_function_sections.
(output_deferred_plabels): Set output alignment to 3 for TARGET_64BIT.
(output_cbranch): Move call to gen_label_rtx.
(output_millicode_call): Rewrite adding long TARGET_64BIT call, expose
delay slot in all variants, shorten pc-relative calls.
(output_call): Rewrite adding long TARGET_64BIT call, improved delay
slot usage and exposure, various new call variants, and shortened
sequences for some variants on TARGET_PA_20.
Miscellaneous format changes.
* pa.h (total_code_bytes): Change type to long.
(MASK_LONG_CALLS, TARGET_LONG_CALLS, TARGET_LONG_ABS_CALL,
TARGET_LONG_PIC_SDIFF_CALL, TARGET_LONG_PIC_PCREL_CALL): Define.
(TARGET_SWITCHES): Add "-mlong-calls" and "-mno-long-calls" options.
(EXTRA_CONSTRAINT, GO_IF_LEGITIMATE_ADDRESS,
LEGITIMIZE_RELOAD_ADDRESS): Don't use long floating point loads and
stores on TARGET_ELF32.
*pa.md (define_delay): Allow insns in delay on TARGET_PORTABLE_RUNTIME.
(unnamed patterns for mulsi3, divsi3, udivsi3, modsi3, umodsi3 and
canonicalize_funcptr_for_compare expanders): Calculate attribute length
attr_length_millicode_call().
(call_internal_symref, call_value_internal_symref): Clobber register 1.
Calculate attribute length using attr_length_call().
(call_internal_reg_64bit, call_value_internal_reg_64bit): Move gp load
to delay slot.
(sibcall, sibcall_value): Rewrite.
(sibcall_internal_symref, sibcall_value_internal_symref): Clobber
register 1. Use attr_length_call().
(sibcall_internal_symref_64bit, sibcall_value_internal_symref_64bit):
New patterns.
(unamed pattern for canonicalize_funcptr_for_compare): Rewrite.
* som.h (MEMBER_TYPE_FORCES_BLK): Define.
* t-pa64 (TARGET_LIBGCC2_CFLAGS): Add "-mlong-calls".
* doc/invoke.texi (mlong-calls): Document.
2002-10-30 Roger Sayle <roger@eyesopen.com> 2002-10-30 Roger Sayle <roger@eyesopen.com>
* fold-const.c (fold_binary_op_with_conditional_arg): Improve * fold-const.c (fold_binary_op_with_conditional_arg): Improve
......
...@@ -196,6 +196,19 @@ Boston, MA 02111-1307, USA. */ ...@@ -196,6 +196,19 @@ Boston, MA 02111-1307, USA. */
} \ } \
while (0) while (0)
/* As well as globalizing the label, we need to encode the label
to ensure a plabel is generated in an indirect call. */
#undef ASM_OUTPUT_EXTERNAL_LIBCALL
#define ASM_OUTPUT_EXTERNAL_LIBCALL(FILE, FUN) \
do \
{ \
if (!FUNCTION_NAME_P (XSTR (FUN, 0))) \
hppa_encode_label (FUN); \
(*targetm.asm_out.globalize_label) (FILE, XSTR (FUN, 0)); \
} \
while (0)
/* Linux always uses gas. */ /* Linux always uses gas. */
#undef TARGET_GAS #undef TARGET_GAS
#define TARGET_GAS 1 #define TARGET_GAS 1
...@@ -105,6 +105,8 @@ extern int jump_in_call_delay PARAMS ((rtx)); ...@@ -105,6 +105,8 @@ extern int jump_in_call_delay PARAMS ((rtx));
extern enum reg_class secondary_reload_class PARAMS ((enum reg_class, extern enum reg_class secondary_reload_class PARAMS ((enum reg_class,
enum machine_mode, rtx)); enum machine_mode, rtx));
extern int hppa_fpstore_bypass_p PARAMS ((rtx, rtx)); extern int hppa_fpstore_bypass_p PARAMS ((rtx, rtx));
extern int attr_length_millicode_call PARAMS ((rtx, int));
extern int attr_length_call PARAMS ((rtx, int));
/* Declare functions defined in pa.c and used in templates. */ /* Declare functions defined in pa.c and used in templates. */
......
...@@ -121,11 +121,13 @@ static void pa_globalize_label PARAMS ((FILE *, const char *)) ...@@ -121,11 +121,13 @@ static void pa_globalize_label PARAMS ((FILE *, const char *))
ATTRIBUTE_UNUSED; ATTRIBUTE_UNUSED;
static void pa_asm_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT, static void pa_asm_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
HOST_WIDE_INT, tree)); HOST_WIDE_INT, tree));
static void copy_fp_args PARAMS ((rtx)) ATTRIBUTE_UNUSED;
static int length_fp_args PARAMS ((rtx)) ATTRIBUTE_UNUSED;
static struct deferred_plabel *get_plabel PARAMS ((const char *))
ATTRIBUTE_UNUSED;
/* Save the operands last given to a compare for use when we /* Save the operands last given to a compare for use when we
generate a scc or bcc insn. */ generate a scc or bcc insn. */
rtx hppa_compare_op0, hppa_compare_op1; rtx hppa_compare_op0, hppa_compare_op1;
enum cmp_type hppa_branch_type; enum cmp_type hppa_branch_type;
...@@ -149,12 +151,10 @@ static rtx find_addr_reg PARAMS ((rtx)); ...@@ -149,12 +151,10 @@ static rtx find_addr_reg PARAMS ((rtx));
/* Keep track of the number of bytes we have output in the CODE subspaces /* Keep track of the number of bytes we have output in the CODE subspaces
during this compilation so we'll know when to emit inline long-calls. */ during this compilation so we'll know when to emit inline long-calls. */
unsigned long total_code_bytes;
unsigned int total_code_bytes;
/* Variables to handle plabels that we discover are necessary at assembly /* Variables to handle plabels that we discover are necessary at assembly
output time. They are output after the current function. */ output time. They are output after the current function. */
struct deferred_plabel GTY(()) struct deferred_plabel GTY(())
{ {
rtx internal_label; rtx internal_label;
...@@ -3197,14 +3197,14 @@ pa_output_function_prologue (file, size) ...@@ -3197,14 +3197,14 @@ pa_output_function_prologue (file, size)
fputs ("\n\t.ENTRY\n", file); fputs ("\n\t.ENTRY\n", file);
/* If we're using GAS and SOM, and not using the portable runtime model, /* If we're using GAS and SOM, and not using the portable runtime model,
then we don't need to accumulate the total number of code bytes. */ or function sections, then we don't need to accumulate the total number
of code bytes. */
if ((TARGET_GAS && TARGET_SOM && ! TARGET_PORTABLE_RUNTIME) if ((TARGET_GAS && TARGET_SOM && ! TARGET_PORTABLE_RUNTIME)
/* FIXME: we can't handle long calls for TARGET_64BIT. */ || flag_function_sections)
|| TARGET_64BIT)
total_code_bytes = 0; total_code_bytes = 0;
else if (INSN_ADDRESSES_SET_P ()) else if (INSN_ADDRESSES_SET_P ())
{ {
unsigned int old_total = total_code_bytes; unsigned long old_total = total_code_bytes;
total_code_bytes += INSN_ADDRESSES (INSN_UID (get_last_nonnote_insn ())); total_code_bytes += INSN_ADDRESSES (INSN_UID (get_last_nonnote_insn ()));
total_code_bytes += FUNCTION_BOUNDARY / BITS_PER_UNIT; total_code_bytes += FUNCTION_BOUNDARY / BITS_PER_UNIT;
...@@ -4726,6 +4726,47 @@ output_global_address (file, x, round_constant) ...@@ -4726,6 +4726,47 @@ output_global_address (file, x, round_constant)
output_addr_const (file, x); output_addr_const (file, x);
} }
static struct deferred_plabel *
get_plabel (fname)
const char *fname;
{
size_t i;
/* See if we have already put this function on the list of deferred
plabels. This list is generally small, so a liner search is not
too ugly. If it proves too slow replace it with something faster. */
for (i = 0; i < n_deferred_plabels; i++)
if (strcmp (fname, deferred_plabels[i].name) == 0)
break;
/* If the deferred plabel list is empty, or this entry was not found
on the list, create a new entry on the list. */
if (deferred_plabels == NULL || i == n_deferred_plabels)
{
const char *real_name;
if (deferred_plabels == 0)
deferred_plabels = (struct deferred_plabel *)
ggc_alloc (sizeof (struct deferred_plabel));
else
deferred_plabels = (struct deferred_plabel *)
ggc_realloc (deferred_plabels,
((n_deferred_plabels + 1)
* sizeof (struct deferred_plabel)));
i = n_deferred_plabels++;
deferred_plabels[i].internal_label = gen_label_rtx ();
deferred_plabels[i].name = ggc_strdup (fname);
/* Gross. We have just implicitly taken the address of this function,
mark it as such. */
real_name = (*targetm.strip_name_encoding) (fname);
TREE_SYMBOL_REFERENCED (get_identifier (real_name)) = 1;
}
return &deferred_plabels[i];
}
void void
output_deferred_plabels (file) output_deferred_plabels (file)
FILE *file; FILE *file;
...@@ -4737,7 +4778,7 @@ output_deferred_plabels (file) ...@@ -4737,7 +4778,7 @@ output_deferred_plabels (file)
if (n_deferred_plabels) if (n_deferred_plabels)
{ {
data_section (); data_section ();
ASM_OUTPUT_ALIGN (file, 2); ASM_OUTPUT_ALIGN (file, TARGET_64BIT ? 3 : 2);
} }
/* Now output the deferred plabels. */ /* Now output the deferred plabels. */
...@@ -5499,12 +5540,11 @@ output_cbranch (operands, nullify, length, negated, insn) ...@@ -5499,12 +5540,11 @@ output_cbranch (operands, nullify, length, negated, insn)
xoperands[1] = operands[1]; xoperands[1] = operands[1];
xoperands[2] = operands[2]; xoperands[2] = operands[2];
xoperands[3] = operands[3]; xoperands[3] = operands[3];
if (TARGET_SOM || ! TARGET_GAS)
xoperands[4] = gen_label_rtx ();
output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands); output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
if (TARGET_SOM || ! TARGET_GAS) if (TARGET_SOM || !TARGET_GAS)
{ {
xoperands[4] = gen_label_rtx ();
output_asm_insn ("addil L'%l0-%l4,%%r1", xoperands); output_asm_insn ("addil L'%l0-%l4,%%r1", xoperands);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
CODE_LABEL_NUMBER (xoperands[4])); CODE_LABEL_NUMBER (xoperands[4]));
...@@ -6043,9 +6083,120 @@ output_movb (operands, insn, which_alternative, reverse_comparison) ...@@ -6043,9 +6083,120 @@ output_movb (operands, insn, which_alternative, reverse_comparison)
} }
} }
/* Copy any FP arguments in INSN into integer registers. */
static void
copy_fp_args (insn)
rtx insn;
{
rtx link;
rtx xoperands[2];
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
{
int arg_mode, regno;
rtx use = XEXP (link, 0);
if (! (GET_CODE (use) == USE
&& GET_CODE (XEXP (use, 0)) == REG
&& FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0)))))
continue;
arg_mode = GET_MODE (XEXP (use, 0));
regno = REGNO (XEXP (use, 0));
/* Is it a floating point register? */
if (regno >= 32 && regno <= 39)
{
/* Copy the FP register into an integer register via memory. */
if (arg_mode == SFmode)
{
xoperands[0] = XEXP (use, 0);
xoperands[1] = gen_rtx_REG (SImode, 26 - (regno - 32) / 2);
output_asm_insn ("{fstws|fstw} %0,-16(%%sr0,%%r30)", xoperands);
output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands);
}
else
{
xoperands[0] = XEXP (use, 0);
xoperands[1] = gen_rtx_REG (DImode, 25 - (regno - 34) / 2);
output_asm_insn ("{fstds|fstd} %0,-16(%%sr0,%%r30)", xoperands);
output_asm_insn ("ldw -12(%%sr0,%%r30),%R1", xoperands);
output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands);
}
}
}
}
/* Compute length of the FP argument copy sequence for INSN. */
static int
length_fp_args (insn)
rtx insn;
{
int length = 0;
rtx link;
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
{
int arg_mode, regno;
rtx use = XEXP (link, 0);
if (! (GET_CODE (use) == USE
&& GET_CODE (XEXP (use, 0)) == REG
&& FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0)))))
continue;
arg_mode = GET_MODE (XEXP (use, 0));
regno = REGNO (XEXP (use, 0));
/* Is it a floating point register? */
if (regno >= 32 && regno <= 39)
{
if (arg_mode == SFmode)
length += 8;
else
length += 12;
}
}
return length;
}
/* We include the delay slot in the returned length as it is better to
over estimate the length than to under estimate it. */
int
attr_length_millicode_call (insn, length)
rtx insn;
int length;
{
unsigned long distance = total_code_bytes + INSN_ADDRESSES (INSN_UID (insn));
if (distance < total_code_bytes)
distance = -1;
if (TARGET_64BIT)
{
if (!TARGET_LONG_CALLS && distance < 7600000)
return length + 8;
return length + 20;
}
else if (TARGET_PORTABLE_RUNTIME)
return length + 24;
else
{
if (!TARGET_LONG_CALLS && distance < 240000)
return length + 8;
if (TARGET_LONG_ABS_CALL && !flag_pic)
return length + 12;
return length + 24;
}
}
/* INSN is a millicode call. It may have an unconditional jump in its delay /* INSN is a function call. It may have an unconditional jump
slot. in its delay slot.
CALL_DEST is the routine we are calling. */ CALL_DEST is the routine we are calling. */
...@@ -6057,166 +6208,191 @@ output_millicode_call (insn, call_dest) ...@@ -6057,166 +6208,191 @@ output_millicode_call (insn, call_dest)
int attr_length = get_attr_length (insn); int attr_length = get_attr_length (insn);
int seq_length = dbr_sequence_length (); int seq_length = dbr_sequence_length ();
int distance; int distance;
rtx xoperands[4];
rtx seq_insn; rtx seq_insn;
rtx xoperands[3];
xoperands[3] = gen_rtx_REG (Pmode, TARGET_64BIT ? 2 : 31); xoperands[0] = call_dest;
xoperands[2] = gen_rtx_REG (Pmode, TARGET_64BIT ? 2 : 31);
/* Handle common case -- empty delay slot or no jump in the delay slot,
and we're sure that the branch will reach the beginning of the $CODE$ /* Handle the common case where we are sure that the branch will
subspace. The within reach form of the $$sh_func_adrs call has reach the beginning of the $CODE$ subspace. The within reach
a length of 28 and attribute type of multi. This length is the form of the $$sh_func_adrs call has a length of 28. Because
same as the maximum length of an out of reach PIC call to $$div. */ it has an attribute type of multi, it never has a non-zero
if ((seq_length == 0 sequence length. The length of the $$sh_func_adrs is the same
&& (attr_length == 8 as certain out of reach PIC calls to other routines. */
if (!TARGET_LONG_CALLS
&& ((seq_length == 0
&& (attr_length == 12
|| (attr_length == 28 && get_attr_type (insn) == TYPE_MULTI))) || (attr_length == 28 && get_attr_type (insn) == TYPE_MULTI)))
|| (seq_length != 0 || (seq_length != 0 && attr_length == 8)))
&& GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
&& attr_length == 4))
{ {
xoperands[0] = call_dest; output_asm_insn ("{bl|b,l} %0,%2", xoperands);
output_asm_insn ("{bl|b,l} %0,%3%#", xoperands);
return "";
} }
else
/* This call may not reach the beginning of the $CODE$ subspace. */
if (attr_length > 8)
{ {
int delay_insn_deleted = 0; if (TARGET_64BIT)
/* We need to emit an inline long-call branch. */
if (seq_length != 0
&& GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)
{ {
/* A non-jump insn in the delay slot. By definition we can /* It might seem that one insn could be saved by accessing
emit this insn before the call. */ the millicode function using the linkage table. However,
final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0); this doesn't work in shared libraries and other dynamically
loaded objects. Using a pc-relative sequence also avoids
/* Now delete the delay insn. */ problems related to the implicit use of the gp register. */
PUT_CODE (NEXT_INSN (insn), NOTE); output_asm_insn ("b,l .+8,%%r1", xoperands);
NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands);
NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1", xoperands);
delay_insn_deleted = 1; output_asm_insn ("bve,l (%%r1),%%r2", xoperands);
} }
else if (TARGET_PORTABLE_RUNTIME)
/* PIC long millicode call sequence. */
if (flag_pic)
{ {
xoperands[0] = call_dest; /* Pure portable runtime doesn't allow be/ble; we also don't
if (TARGET_SOM || ! TARGET_GAS) have PIC support in the assembler/linker, so this sequence
xoperands[1] = gen_label_rtx (); is needed. */
/* Get our address + 8 into %r1. */ /* Get the address of our target into %r1. */
output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands); output_asm_insn ("ldil L'%0,%%r1", xoperands);
output_asm_insn ("ldo R'%0(%%r1),%%r1", xoperands);
if (TARGET_SOM || ! TARGET_GAS) /* Get our return address into %r31. */
output_asm_insn ("{bl|b,l} .+8,%%r31", xoperands);
output_asm_insn ("addi 8,%%r31,%%r31", xoperands);
/* Jump to our target address in %r1. */
output_asm_insn ("bv %%r0(%%r1)", xoperands);
}
else if (!flag_pic)
{
output_asm_insn ("ldil L'%0,%%r1", xoperands);
if (TARGET_PA_20)
output_asm_insn ("be,l R'%0(%%sr4,%%r1),%%sr0,%%r31", xoperands);
else
output_asm_insn ("ble R'%0(%%sr4,%%r1)", xoperands);
}
else
{ {
/* Add %r1 to the offset of our target from the next insn. */ if (TARGET_SOM || !TARGET_GAS)
output_asm_insn ("addil L%%%0-%1,%%r1", xoperands); {
/* The HP assembler can generate relocations for the
difference of two symbols. GAS can do this for a
millicode symbol but not an arbitrary external
symbol when generating SOM output. */
xoperands[1] = gen_label_rtx ();
output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
output_asm_insn ("addi 16,%%r1,%%r31", xoperands);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
CODE_LABEL_NUMBER (xoperands[1])); CODE_LABEL_NUMBER (xoperands[1]));
output_asm_insn ("ldo R%%%0-%1(%%r1),%%r1", xoperands); output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
} }
else else
{ {
output_asm_insn ("addil L%%%0-$PIC_pcrel$0+4,%%r1", xoperands); output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
output_asm_insn ("ldo R%%%0-$PIC_pcrel$0+8(%%r1),%%r1", output_asm_insn ("addi 16,%%r1,%%r31", xoperands);
output_asm_insn ("addil L'%0-$PIC_pcrel$0+8,%%r1", xoperands);
output_asm_insn ("ldo R'%0-$PIC_pcrel$0+12(%%r1),%%r1",
xoperands); xoperands);
} }
/* Get the return address into %r31. */ /* Jump to our target address in %r1. */
output_asm_insn ("blr 0,%3", xoperands); output_asm_insn ("bv %%r0(%%r1)", xoperands);
}
/* Branch to our target which is in %r1. */ }
output_asm_insn ("bv,n %%r0(%%r1)", xoperands);
/* Empty delay slot. Note this insn gets fetched twice and if (seq_length == 0)
executed once. To be safe we use a nop. */
output_asm_insn ("nop", xoperands); output_asm_insn ("nop", xoperands);
}
/* Pure portable runtime doesn't allow be/ble; we also don't have
PIC support in the assembler/linker, so this sequence is needed. */
else if (TARGET_PORTABLE_RUNTIME)
{
xoperands[0] = call_dest;
/* Get the address of our target into %r29. */
output_asm_insn ("ldil L%%%0,%%r29", xoperands);
output_asm_insn ("ldo R%%%0(%%r29),%%r29", xoperands);
/* Get our return address into %r31. */ /* We are done if there isn't a jump in the delay slot. */
output_asm_insn ("blr %%r0,%3", xoperands); if (seq_length == 0 || GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)
return "";
/* Jump to our target address in %r29. */ /* This call has an unconditional jump in its delay slot. */
output_asm_insn ("bv,n %%r0(%%r29)", xoperands); xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
/* Empty delay slot. Note this insn gets fetched twice and /* See if the return address can be adjusted. Use the containing
executed once. To be safe we use a nop. */ sequence insn's address. */
output_asm_insn ("nop", xoperands); seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0)));
} distance = (INSN_ADDRESSES (INSN_UID (JUMP_LABEL (NEXT_INSN (insn))))
/* If we're allowed to use be/ble instructions, then this is the - INSN_ADDRESSES (INSN_UID (seq_insn)) - 8);
best sequence to use for a long millicode call. */
else
{
xoperands[0] = call_dest;
output_asm_insn ("ldil L%%%0,%3", xoperands);
if (TARGET_PA_20)
output_asm_insn ("be,l R%%%0(%%sr4,%3),%%sr0,%%r31", xoperands);
else
output_asm_insn ("ble R%%%0(%%sr4,%3)", xoperands);
output_asm_insn ("nop", xoperands);
}
/* If we had a jump in the call's delay slot, output it now. */ if (VAL_14_BITS_P (distance))
if (seq_length != 0 && !delay_insn_deleted)
{ {
xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1); xoperands[1] = gen_label_rtx ();
output_asm_insn ("b,n %0", xoperands); output_asm_insn ("ldo %0-%1(%2),%2", xoperands);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
CODE_LABEL_NUMBER (xoperands[3]));
}
else
/* ??? This branch may not reach its target. */
output_asm_insn ("nop\n\tb,n %0", xoperands);
/* Now delete the delay insn. */ /* Delete the jump. */
PUT_CODE (NEXT_INSN (insn), NOTE); PUT_CODE (NEXT_INSN (insn), NOTE);
NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
}
return "";
}
/* This call has an unconditional jump in its delay slot and the return "";
call is known to reach its target or the beginning of the current }
subspace. */
/* Use the containing sequence insn's address. */ /* We include the delay slot in the returned length as it is better to
seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); over estimate the length than to under estimate it. */
distance = INSN_ADDRESSES (INSN_UID (JUMP_LABEL (NEXT_INSN (insn)))) int
- INSN_ADDRESSES (INSN_UID (seq_insn)) - 8; attr_length_call (insn, sibcall)
rtx insn;
int sibcall;
{
unsigned long distance = total_code_bytes + INSN_ADDRESSES (INSN_UID (insn));
/* If the branch was too far away, emit a normal call followed if (distance < total_code_bytes)
by a nop, followed by the unconditional branch. distance = -1;
If the branch is close, then adjust %r2 from within the if (TARGET_64BIT)
call's delay slot. */ {
if (!TARGET_LONG_CALLS
&& ((!sibcall && distance < 7600000) || distance < 240000))
return 8;
xoperands[0] = call_dest; return (sibcall ? 28 : 24);
xoperands[1] = XEXP (PATTERN (NEXT_INSN (insn)), 1); }
if (! VAL_14_BITS_P (distance))
output_asm_insn ("{bl|b,l} %0,%3\n\tnop\n\tb,n %1", xoperands);
else else
{ {
xoperands[2] = gen_label_rtx (); if (!TARGET_LONG_CALLS
output_asm_insn ("\n\t{bl|b,l} %0,%3\n\tldo %1-%2(%3),%3", && ((TARGET_PA_20 && !sibcall && distance < 7600000)
xoperands); || distance < 240000))
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", return 8;
CODE_LABEL_NUMBER (xoperands[2]));
if (TARGET_LONG_ABS_CALL && !flag_pic)
return 12;
if ((TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
|| (TARGET_GAS && TARGET_LONG_PIC_PCREL_CALL))
{
if (TARGET_PA_20)
return 20;
return 28;
} }
else
{
int length = 0;
/* Delete the jump. */ if (TARGET_SOM)
PUT_CODE (NEXT_INSN (insn), NOTE); length += length_fp_args (insn);
NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; if (flag_pic)
return ""; length += 4;
if (TARGET_PA_20)
return (length + 32);
if (!sibcall)
length += 8;
return (length + 40);
}
}
} }
/* INSN is either a function call. It may have an unconditional jump /* INSN is a function call. It may have an unconditional jump
in its delay slot. in its delay slot.
CALL_DEST is the routine we are calling. */ CALL_DEST is the routine we are calling. */
...@@ -6227,47 +6403,44 @@ output_call (insn, call_dest, sibcall) ...@@ -6227,47 +6403,44 @@ output_call (insn, call_dest, sibcall)
rtx call_dest; rtx call_dest;
int sibcall; int sibcall;
{ {
int delay_insn_deleted = 0;
int delay_slot_filled = 0;
int attr_length = get_attr_length (insn); int attr_length = get_attr_length (insn);
int seq_length = dbr_sequence_length (); int seq_length = dbr_sequence_length ();
int distance; rtx xoperands[2];
rtx xoperands[4];
rtx seq_insn;
/* Handle common case -- empty delay slot or no jump in the delay slot,
and we're sure that the branch will reach the beginning of the $CODE$
subspace. */
if ((seq_length == 0 && attr_length == 12)
|| (seq_length != 0
&& GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
&& attr_length == 8))
{
xoperands[0] = call_dest; xoperands[0] = call_dest;
/* Handle the common case where we're sure that the branch will reach
the beginning of the $CODE$ subspace. */
if (!TARGET_LONG_CALLS
&& ((seq_length == 0 && attr_length == 12)
|| (seq_length != 0 && attr_length == 8)))
{
xoperands[1] = gen_rtx_REG (word_mode, sibcall ? 0 : 2); xoperands[1] = gen_rtx_REG (word_mode, sibcall ? 0 : 2);
output_asm_insn ("{bl|b,l} %0,%1%#", xoperands); output_asm_insn ("{bl|b,l} %0,%1", xoperands);
return "";
} }
else
/* This call may not reach the beginning of the $CODE$ subspace. */
if (attr_length > 12)
{ {
int delay_insn_deleted = 0; if (TARGET_64BIT)
rtx xoperands[2]; {
rtx link; /* ??? As far as I can tell, the HP linker doesn't support the
long pc-relative sequence described in the 64-bit runtime
architecture. So, we use a slightly longer indirect call. */
struct deferred_plabel *p = get_plabel (XSTR (call_dest, 0));
/* We need to emit an inline long-call branch. Furthermore, xoperands[0] = p->internal_label;
because we're changing a named function call into an indirect xoperands[1] = gen_label_rtx ();
function call well after the parameters have been set up, we
need to make sure any FP args appear in both the integer /* If this isn't a sibcall, we put the load of %r27 into the
and FP registers. Also, we need move any delay slot insn delay slot. We can't do this in a sibcall as we don't
out of the delay slot. And finally, we can't rely on the linker have a second call-clobbered scratch register available. */
being able to fix the call to $$dyncall! -- Yuk!. */
if (seq_length != 0 if (seq_length != 0
&& GET_CODE (NEXT_INSN (insn)) != JUMP_INSN) && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
&& !sibcall)
{ {
/* A non-jump insn in the delay slot. By definition we can final_scan_insn (NEXT_INSN (insn), asm_out_file,
emit this insn before the call (and in fact before argument optimize, 0, 0);
relocating. */
final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0);
/* Now delete the delay insn. */ /* Now delete the delay insn. */
PUT_CODE (NEXT_INSN (insn), NOTE); PUT_CODE (NEXT_INSN (insn), NOTE);
...@@ -6276,209 +6449,228 @@ output_call (insn, call_dest, sibcall) ...@@ -6276,209 +6449,228 @@ output_call (insn, call_dest, sibcall)
delay_insn_deleted = 1; delay_insn_deleted = 1;
} }
/* Now copy any FP arguments into integer registers. */ output_asm_insn ("addil LT'%0,%%r27", xoperands);
for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) output_asm_insn ("ldd RT'%0(%%r1),%%r1", xoperands);
{ output_asm_insn ("ldd 0(%%r1),%%r1", xoperands);
int arg_mode, regno;
rtx use = XEXP (link, 0);
if (! (GET_CODE (use) == USE
&& GET_CODE (XEXP (use, 0)) == REG
&& FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0)))))
continue;
arg_mode = GET_MODE (XEXP (use, 0)); if (sibcall)
regno = REGNO (XEXP (use, 0));
/* Is it a floating point register? */
if (regno >= 32 && regno <= 39)
{
/* Copy from the FP register into an integer register
(via memory). */
if (arg_mode == SFmode)
{ {
xoperands[0] = XEXP (use, 0); output_asm_insn ("ldd 24(%%r1),%%r27", xoperands);
xoperands[1] = gen_rtx_REG (SImode, 26 - (regno - 32) / 2); output_asm_insn ("ldd 16(%%r1),%%r1", xoperands);
output_asm_insn ("{fstws|fstw} %0,-16(%%sr0,%%r30)", output_asm_insn ("bve (%%r1)", xoperands);
xoperands);
output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands);
} }
else else
{ {
xoperands[0] = XEXP (use, 0); output_asm_insn ("ldd 16(%%r1),%%r2", xoperands);
xoperands[1] = gen_rtx_REG (DImode, 25 - (regno - 34) / 2); output_asm_insn ("bve,l (%%r2),%%r2", xoperands);
output_asm_insn ("{fstds|fstd} %0,-16(%%sr0,%%r30)", output_asm_insn ("ldd 24(%%r1),%%r27", xoperands);
xoperands); delay_slot_filled = 1;
output_asm_insn ("ldw -12(%%sr0,%%r30),%R1", xoperands);
output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands);
}
} }
} }
else
/* Don't have to worry about TARGET_PORTABLE_RUNTIME here since
we don't have any direct calls in that case. */
{ {
size_t i; int indirect_call = 0;
const char *name = XSTR (call_dest, 0);
/* See if we have already put this function on the list /* Emit a long call. There are several different sequences
of deferred plabels. This list is generally small, of increasing length and complexity. In most cases,
so a liner search is not too ugly. If it proves too they don't allow an instruction in the delay slot. */
slow replace it with something faster. */ if (!(TARGET_LONG_ABS_CALL && !flag_pic)
for (i = 0; i < n_deferred_plabels; i++) && !(TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
if (strcmp (name, deferred_plabels[i].name) == 0) && !(TARGET_GAS && TARGET_LONG_PIC_PCREL_CALL))
break; indirect_call = 1;
/* If the deferred plabel list is empty, or this entry was if (seq_length != 0
not found on the list, create a new entry on the list. */ && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
if (deferred_plabels == NULL || i == n_deferred_plabels) && !sibcall
&& (!TARGET_PA_20 || indirect_call))
{ {
const char *real_name; /* A non-jump insn in the delay slot. By definition we can
emit this insn before the call (and in fact before argument
relocating. */
final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0);
if (deferred_plabels == 0) /* Now delete the delay insn. */
deferred_plabels = (struct deferred_plabel *) PUT_CODE (NEXT_INSN (insn), NOTE);
ggc_alloc (sizeof (struct deferred_plabel)); NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
else NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
deferred_plabels = (struct deferred_plabel *) delay_insn_deleted = 1;
ggc_realloc (deferred_plabels, }
((n_deferred_plabels + 1)
* sizeof (struct deferred_plabel)));
i = n_deferred_plabels++; if (TARGET_LONG_ABS_CALL && !flag_pic)
deferred_plabels[i].internal_label = gen_label_rtx (); {
deferred_plabels[i].name = ggc_strdup (name); /* This is the best sequence for making long calls in
non-pic code. Unfortunately, GNU ld doesn't provide
the stub needed for external calls, and GAS's support
for this with the SOM linker is buggy. */
output_asm_insn ("ldil L'%0,%%r1", xoperands);
if (sibcall)
output_asm_insn ("be R'%0(%%sr4,%%r1)", xoperands);
else
{
if (TARGET_PA_20)
output_asm_insn ("be,l R'%0(%%sr4,%%r1),%%sr0,%%r31",
xoperands);
else
output_asm_insn ("ble R'%0(%%sr4,%%r1)", xoperands);
/* Gross. We have just implicitly taken the address of this output_asm_insn ("copy %%r31,%%r2", xoperands);
function, mark it as such. */ delay_slot_filled = 1;
real_name = (*targetm.strip_name_encoding) (name);
TREE_SYMBOL_REFERENCED (get_identifier (real_name)) = 1;
} }
}
/* We have to load the address of the function using a procedure else
label (plabel). Inline plabels can lose for PIC and other
cases, so avoid them by creating a 32bit plabel in the data
segment. */
if (flag_pic)
{ {
xoperands[0] = deferred_plabels[i].internal_label; if (TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL)
if (TARGET_SOM || ! TARGET_GAS) {
/* 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. */
xoperands[1] = gen_label_rtx (); xoperands[1] = gen_label_rtx ();
output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
CODE_LABEL_NUMBER (xoperands[1]));
output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
}
else if (TARGET_GAS && TARGET_LONG_PIC_PCREL_CALL)
{
/* GAS currently can't generate the relocations that
are needed for the SOM linker under HP-UX using this
sequence. The GNU linker doesn't generate the stubs
that are needed for external calls on TARGET_ELF32
with this sequence. For now, we have to use a
longer plabel sequence when using GAS. */
output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1",
xoperands);
output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1",
xoperands);
}
else
{
/* Emit a long plabel-based call sequence. This is
essentially an inline implementation of $$dyncall.
We don't actually try to call $$dyncall as this is
as difficult as calling the function itself. */
struct deferred_plabel *p = get_plabel (XSTR (call_dest, 0));
output_asm_insn ("addil LT%%%0,%%r19", xoperands); xoperands[0] = p->internal_label;
output_asm_insn ("ldw RT%%%0(%%r1),%%r22", xoperands); xoperands[1] = gen_label_rtx ();
output_asm_insn ("ldw 0(%%r22),%%r22", xoperands);
/* Get our address + 8 into %r1. */ /* Since the call is indirect, FP arguments in registers
output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands); need to be copied to the general registers. Then, the
argument relocation stub will copy them back. */
if (TARGET_SOM)
copy_fp_args (insn);
if (TARGET_SOM || ! TARGET_GAS) if (flag_pic)
{ {
/* Add %r1 to the offset of dyncall from the next insn. */ output_asm_insn ("addil LT'%0,%%r19", xoperands);
output_asm_insn ("addil L%%$$dyncall-%1,%%r1", xoperands); output_asm_insn ("ldw RT'%0(%%r1),%%r1", xoperands);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", output_asm_insn ("ldw 0(%%r1),%%r1", xoperands);
CODE_LABEL_NUMBER (xoperands[1]));
output_asm_insn ("ldo R%%$$dyncall-%1(%%r1),%%r1", xoperands);
} }
else else
{ {
output_asm_insn ("addil L%%$$dyncall-$PIC_pcrel$0+4,%%r1", output_asm_insn ("addil LR'%0-$global$,%%r27",
xoperands); xoperands);
output_asm_insn ("ldo R%%$$dyncall-$PIC_pcrel$0+8(%%r1),%%r1", output_asm_insn ("ldw RR'%0-$global$(%%r1),%%r1",
xoperands); xoperands);
} }
/* Get the return address into %r31. */ output_asm_insn ("bb,>=,n %%r1,30,.+16", xoperands);
output_asm_insn ("blr %%r0,%%r31", xoperands); output_asm_insn ("depi 0,31,2,%%r1", xoperands);
output_asm_insn ("ldw 4(%%sr0,%%r1),%%r19", xoperands);
output_asm_insn ("ldw 0(%%sr0,%%r1),%%r1", xoperands);
/* Branch to our target which is in %r1. */ if (!sibcall && !TARGET_PA_20)
output_asm_insn ("bv %%r0(%%r1)", xoperands); {
output_asm_insn ("{bl|b,l} .+8,%%r2", xoperands);
output_asm_insn ("addi 16,%%r2,%%r2", xoperands);
}
}
if (TARGET_PA_20)
{
if (sibcall) if (sibcall)
output_asm_insn ("bve (%%r1)", xoperands);
else
{ {
/* This call never returns, so we do not need to fix the if (indirect_call)
return pointer. */ {
output_asm_insn ("nop", xoperands); output_asm_insn ("bve,l (%%r1),%%r2", xoperands);
output_asm_insn ("stw %%r2,-24(%%sp)", xoperands);
delay_slot_filled = 1;
} }
else else
{ output_asm_insn ("bve,l (%%r1),%%r2", xoperands);
/* Copy the return address into %r2 also. */
output_asm_insn ("copy %%r31,%%r2", xoperands);
} }
} }
else else
{ {
xoperands[0] = deferred_plabels[i].internal_label; output_asm_insn ("ldsid (%%r1),%%r31\n\tmtsp %%r31,%%sr0",
/* Get the address of our target into %r22. */
output_asm_insn ("addil LR%%%0-$global$,%%r27", xoperands);
output_asm_insn ("ldw RR%%%0-$global$(%%r1),%%r22", xoperands);
/* Get the high part of the address of $dyncall into %r2, then
add in the low part in the branch instruction. */
output_asm_insn ("ldil L%%$$dyncall,%%r2", xoperands);
if (TARGET_PA_20)
output_asm_insn ("be,l R%%$$dyncall(%%sr4,%%r2),%%sr0,%%r31",
xoperands); xoperands);
else
output_asm_insn ("ble R%%$$dyncall(%%sr4,%%r2)", xoperands);
if (sibcall) if (sibcall)
{ output_asm_insn ("be 0(%%sr0,%%r1)", xoperands);
/* This call never returns, so we do not need to fix the
return pointer. */
output_asm_insn ("nop", xoperands);
}
else else
{ {
/* Copy the return address into %r2 also. */ output_asm_insn ("ble 0(%%sr0,%%r1)", xoperands);
if (indirect_call)
output_asm_insn ("stw %%r31,-24(%%sp)", xoperands);
else
output_asm_insn ("copy %%r31,%%r2", xoperands); output_asm_insn ("copy %%r31,%%r2", xoperands);
delay_slot_filled = 1;
} }
} }
} }
/* If we had a jump in the call's delay slot, output it now. */
if (seq_length != 0 && !delay_insn_deleted)
{
xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
output_asm_insn ("b,n %0", xoperands);
/* Now delete the delay insn. */
PUT_CODE (NEXT_INSN (insn), NOTE);
NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
} }
return "";
} }
/* This call has an unconditional jump in its delay slot and the if (seq_length == 0 || (delay_insn_deleted && !delay_slot_filled))
call is known to reach its target or the beginning of the current output_asm_insn ("nop", xoperands);
subspace. */
/* Use the containing sequence insn's address. */ /* We are done if there isn't a jump in the delay slot. */
seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); if (seq_length == 0
|| delay_insn_deleted
|| GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)
return "";
distance = INSN_ADDRESSES (INSN_UID (JUMP_LABEL (NEXT_INSN (insn)))) /* A sibcall should never have a branch in the delay slot. */
- INSN_ADDRESSES (INSN_UID (seq_insn)) - 8; if (sibcall)
abort ();
/* If the branch is too far away, emit a normal call followed /* This call has an unconditional jump in its delay slot. */
by a nop, followed by the unconditional branch. If the branch xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
is close, then adjust %r2 in the call's delay slot. */
xoperands[0] = call_dest; if (!delay_slot_filled)
xoperands[1] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
if (! VAL_14_BITS_P (distance))
output_asm_insn ("{bl|b,l} %0,%%r2\n\tnop\n\tb,n %1", xoperands);
else
{ {
xoperands[3] = gen_label_rtx (); /* See if the return address can be adjusted. Use the containing
output_asm_insn ("\n\t{bl|b,l} %0,%%r2\n\tldo %1-%3(%%r2),%%r2", sequence insn's address. */
xoperands); rtx seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0)));
int distance = (INSN_ADDRESSES (INSN_UID (JUMP_LABEL (NEXT_INSN (insn))))
- INSN_ADDRESSES (INSN_UID (seq_insn)) - 8);
if (VAL_14_BITS_P (distance))
{
xoperands[1] = gen_label_rtx ();
output_asm_insn ("ldo %0-%1(%%r2),%%r2", xoperands);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
CODE_LABEL_NUMBER (xoperands[3])); CODE_LABEL_NUMBER (xoperands[3]));
} }
else
/* ??? This branch may not reach its target. */
output_asm_insn ("nop\n\tb,n %0", xoperands);
}
else
/* ??? This branch may not reach its target. */
output_asm_insn ("b,n %0", xoperands);
/* Delete the jump. */ /* Delete the jump. */
PUT_CODE (NEXT_INSN (insn), NOTE); PUT_CODE (NEXT_INSN (insn), NOTE);
NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
return ""; return "";
} }
...@@ -6580,8 +6772,8 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) ...@@ -6580,8 +6772,8 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
{ {
if (! TARGET_64BIT && ! TARGET_PORTABLE_RUNTIME && flag_pic) if (! TARGET_64BIT && ! TARGET_PORTABLE_RUNTIME && flag_pic)
{ {
fprintf (file, "\taddil LT%%%s,%%r19\n", lab); fprintf (file, "\taddil LT'%s,%%r19\n", lab);
fprintf (file, "\tldw RT%%%s(%%r1),%%r22\n", lab); fprintf (file, "\tldw RT'%s(%%r1),%%r22\n", lab);
fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n");
fprintf (file, "\tbb,>=,n %%r22,30,.+16\n"); fprintf (file, "\tbb,>=,n %%r22,30,.+16\n");
fprintf (file, "\tdepi 0,31,2,%%r22\n"); fprintf (file, "\tdepi 0,31,2,%%r22\n");
...@@ -6603,13 +6795,13 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) ...@@ -6603,13 +6795,13 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
{ {
if (! TARGET_64BIT && ! TARGET_PORTABLE_RUNTIME && flag_pic) if (! TARGET_64BIT && ! TARGET_PORTABLE_RUNTIME && flag_pic)
{ {
fprintf (file, "\taddil L%%"); fprintf (file, "\taddil L'");
fprintf (file, HOST_WIDE_INT_PRINT_DEC, delta); fprintf (file, HOST_WIDE_INT_PRINT_DEC, delta);
fprintf (file, ",%%r26\n\tldo R%%"); fprintf (file, ",%%r26\n\tldo R'");
fprintf (file, HOST_WIDE_INT_PRINT_DEC, delta); fprintf (file, HOST_WIDE_INT_PRINT_DEC, delta);
fprintf (file, "(%%r1),%%r26\n"); fprintf (file, "(%%r1),%%r26\n");
fprintf (file, "\taddil LT%%%s,%%r19\n", lab); fprintf (file, "\taddil LT'%s,%%r19\n", lab);
fprintf (file, "\tldw RT%%%s(%%r1),%%r22\n", lab); fprintf (file, "\tldw RT'%s(%%r1),%%r22\n", lab);
fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n");
fprintf (file, "\tbb,>=,n %%r22,30,.+16\n"); fprintf (file, "\tbb,>=,n %%r22,30,.+16\n");
fprintf (file, "\tdepi 0,31,2,%%r22\n"); fprintf (file, "\tdepi 0,31,2,%%r22\n");
...@@ -6620,9 +6812,9 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) ...@@ -6620,9 +6812,9 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
} }
else else
{ {
fprintf (file, "\taddil L%%"); fprintf (file, "\taddil L'");
fprintf (file, HOST_WIDE_INT_PRINT_DEC, delta); fprintf (file, HOST_WIDE_INT_PRINT_DEC, delta);
fprintf (file, ",%%r26\n\tb %s\n\tldo R%%", target_name); fprintf (file, ",%%r26\n\tb %s\n\tldo R'", target_name);
fprintf (file, HOST_WIDE_INT_PRINT_DEC, delta); fprintf (file, HOST_WIDE_INT_PRINT_DEC, delta);
fprintf (file, "(%%r1),%%r26\n"); fprintf (file, "(%%r1),%%r26\n");
} }
...@@ -6634,7 +6826,7 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) ...@@ -6634,7 +6826,7 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
data_section (); data_section ();
fprintf (file, "\t.align 4\n"); fprintf (file, "\t.align 4\n");
ASM_OUTPUT_INTERNAL_LABEL (file, "LTHN", current_thunk_number); ASM_OUTPUT_INTERNAL_LABEL (file, "LTHN", current_thunk_number);
fprintf (file, "\t.word P%%%s\n", target_name); fprintf (file, "\t.word P'%s\n", target_name);
function_section (thunk_fndecl); function_section (thunk_fndecl);
} }
current_thunk_number++; current_thunk_number++;
......
...@@ -31,7 +31,7 @@ enum cmp_type /* comparison type */ ...@@ -31,7 +31,7 @@ enum cmp_type /* comparison type */
}; };
/* For long call handling. */ /* For long call handling. */
extern unsigned int total_code_bytes; extern unsigned long total_code_bytes;
/* Which processor to schedule for. */ /* Which processor to schedule for. */
...@@ -152,6 +152,12 @@ extern int target_flags; ...@@ -152,6 +152,12 @@ extern int target_flags;
#define TARGET_GNU_LD (target_flags & MASK_GNU_LD) #define TARGET_GNU_LD (target_flags & MASK_GNU_LD)
#endif #endif
/* Force generation of long calls. */
#define MASK_LONG_CALLS 32768
#ifndef TARGET_LONG_CALLS
#define TARGET_LONG_CALLS (target_flags & MASK_LONG_CALLS)
#endif
#ifndef TARGET_PA_10 #ifndef TARGET_PA_10
#define TARGET_PA_10 (target_flags & (MASK_PA_11 | MASK_PA_20) == 0) #define TARGET_PA_10 (target_flags & (MASK_PA_11 | MASK_PA_20) == 0)
#endif #endif
...@@ -179,6 +185,27 @@ extern int target_flags; ...@@ -179,6 +185,27 @@ extern int target_flags;
#define TARGET_SOM 0 #define TARGET_SOM 0
#endif #endif
/* The following three defines are potential target switches. The current
defines are optimal given the current capabilities of GAS and GNU ld. */
/* Define to a C expression evaluating to true to use long absolute calls.
Currently, only the HP assembler and SOM linker support long absolute
calls. They are used only in non-pic code. */
#define TARGET_LONG_ABS_CALL (TARGET_SOM && !TARGET_GAS)
/* Define to a C expression evaluating to true to use long pic symbol
difference calls. This is a call variant similar to the long pic
pc-relative call. Long pic symbol difference calls are only used with
the HP SOM linker. Currently, only the HP assembler supports these
calls. GAS doesn't allow an arbritrary difference of two symbols. */
#define TARGET_LONG_PIC_SDIFF_CALL (!TARGET_GAS)
/* Define to a C expression evaluating to true to use long pic
pc-relative calls. Long pic pc-relative calls are only used with
GAS. Currently, they are usable for calls within a module but
not for external calls. */
#define TARGET_LONG_PIC_PCREL_CALL 0
/* Macro to define tables used to set the flags. This is a /* Macro to define tables used to set the flags. This is a
list in braces of target switches with each switch being list in braces of target switches with each switch being
{ "NAME", VALUE, "HELP_STRING" }. VALUE is the bits to set, { "NAME", VALUE, "HELP_STRING" }. VALUE is the bits to set,
...@@ -237,6 +264,10 @@ extern int target_flags; ...@@ -237,6 +264,10 @@ extern int target_flags;
N_("Generate code for huge switch statements") }, \ N_("Generate code for huge switch statements") }, \
{ "no-big-switch", -MASK_BIG_SWITCH, \ { "no-big-switch", -MASK_BIG_SWITCH, \
N_("Do not generate code for huge switch statements") }, \ N_("Do not generate code for huge switch statements") }, \
{ "long-calls", MASK_LONG_CALLS, \
N_("Always generate long calls") }, \
{ "no-long-calls", -MASK_LONG_CALLS, \
N_("Generate long calls only when needed") }, \
{ "linker-opt", 0, \ { "linker-opt", 0, \
N_("Enable linker optimizations") }, \ N_("Enable linker optimizations") }, \
SUBTARGET_SWITCHES \ SUBTARGET_SWITCHES \
...@@ -1193,8 +1224,14 @@ extern int may_call_alloca; ...@@ -1193,8 +1224,14 @@ extern int may_call_alloca;
/* Using DFmode forces only short displacements \ /* Using DFmode forces only short displacements \
to be recognized as valid in reg+d addresses. \ to be recognized as valid in reg+d addresses. \
However, this is not necessary for PA2.0 since\ However, this is not necessary for PA2.0 since\
it has long FP loads/stores. */ \ it has long FP loads/stores. \
\
FIXME: the ELF32 linker clobbers the LSB of \
the FP register number in {fldw,fstw} insns. \
Thus, we only allow long FP loads/stores on \
TARGET_64BIT. */ \
&& memory_address_p ((TARGET_PA_20 \ && memory_address_p ((TARGET_PA_20 \
&& !TARGET_ELF32 \
? GET_MODE (OP) \ ? GET_MODE (OP) \
: DFmode), \ : DFmode), \
XEXP (OP, 0)) \ XEXP (OP, 0)) \
...@@ -1327,6 +1364,7 @@ extern int may_call_alloca; ...@@ -1327,6 +1364,7 @@ extern int may_call_alloca;
/* We can allow symbolic LO_SUM addresses\ /* We can allow symbolic LO_SUM addresses\
for PA2.0. */ \ for PA2.0. */ \
|| (TARGET_PA_20 \ || (TARGET_PA_20 \
&& !TARGET_ELF32 \
&& GET_CODE (XEXP (X, 1)) != CONST_INT)\ && GET_CODE (XEXP (X, 1)) != CONST_INT)\
|| ((MODE) != SFmode \ || ((MODE) != SFmode \
&& (MODE) != DFmode))) \ && (MODE) != DFmode))) \
...@@ -1340,6 +1378,7 @@ extern int may_call_alloca; ...@@ -1340,6 +1378,7 @@ extern int may_call_alloca;
/* We can allow symbolic LO_SUM addresses\ /* We can allow symbolic LO_SUM addresses\
for PA2.0. */ \ for PA2.0. */ \
|| (TARGET_PA_20 \ || (TARGET_PA_20 \
&& !TARGET_ELF32 \
&& GET_CODE (XEXP (X, 1)) != CONST_INT)\ && GET_CODE (XEXP (X, 1)) != CONST_INT)\
|| ((MODE) != SFmode \ || ((MODE) != SFmode \
&& (MODE) != DFmode))) \ && (MODE) != DFmode))) \
...@@ -1354,7 +1393,7 @@ extern int may_call_alloca; ...@@ -1354,7 +1393,7 @@ extern int may_call_alloca;
&& REG_OK_FOR_BASE_P (XEXP (X, 0)) \ && REG_OK_FOR_BASE_P (XEXP (X, 0)) \
&& GET_CODE (XEXP (X, 1)) == UNSPEC \ && GET_CODE (XEXP (X, 1)) == UNSPEC \
&& (TARGET_SOFT_FLOAT \ && (TARGET_SOFT_FLOAT \
|| TARGET_PA_20 \ || (TARGET_PA_20 && !TARGET_ELF32) \
|| ((MODE) != SFmode \ || ((MODE) != SFmode \
&& (MODE) != DFmode))) \ && (MODE) != DFmode))) \
goto ADDR; \ goto ADDR; \
...@@ -1386,7 +1425,7 @@ do { \ ...@@ -1386,7 +1425,7 @@ do { \
rtx new, temp = NULL_RTX; \ rtx new, temp = NULL_RTX; \
\ \
mask = (GET_MODE_CLASS (MODE) == MODE_FLOAT \ mask = (GET_MODE_CLASS (MODE) == MODE_FLOAT \
? (TARGET_PA_20 ? 0x3fff : 0x1f) : 0x3fff); \ ? (TARGET_PA_20 && !TARGET_ELF32 ? 0x3fff : 0x1f) : 0x3fff); \
\ \
if (optimize \ if (optimize \
&& GET_CODE (AD) == PLUS) \ && GET_CODE (AD) == PLUS) \
......
...@@ -105,12 +105,9 @@ ...@@ -105,12 +105,9 @@
(define_delay (eq_attr "type" "call") (define_delay (eq_attr "type" "call")
[(eq_attr "in_call_delay" "true") (nil) (nil)]) [(eq_attr "in_call_delay" "true") (nil) (nil)])
;; millicode call delay slot description. Note it disallows delay slot ;; Millicode call delay slot description.
;; when TARGET_PORTABLE_RUNTIME is true.
(define_delay (eq_attr "type" "milli") (define_delay (eq_attr "type" "milli")
[(and (eq_attr "in_call_delay" "true") [(eq_attr "in_call_delay" "true") (nil) (nil)])
(eq (symbol_ref "TARGET_PORTABLE_RUNTIME") (const_int 0)))
(nil) (nil)])
;; Return and other similar instructions. ;; Return and other similar instructions.
(define_delay (eq_attr "type" "branch,parallel_branch") (define_delay (eq_attr "type" "branch,parallel_branch")
...@@ -4089,27 +4086,7 @@ ...@@ -4089,27 +4086,7 @@
"!TARGET_64BIT" "!TARGET_64BIT"
"* return output_mul_insn (0, insn);" "* return output_mul_insn (0, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(cond [
;; Target (or stub) within reach
(and (lt (plus (symbol_ref "total_code_bytes") (pc))
(const_int 240000))
(eq (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0)))
(const_int 4)
;; Out of reach PIC
(ne (symbol_ref "flag_pic")
(const_int 0))
(const_int 24)
;; Out of reach PORTABLE_RUNTIME
(ne (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0))
(const_int 20)]
;; Out of reach, can use ble
(const_int 12)))])
(define_insn "" (define_insn ""
[(set (reg:SI 29) (mult:SI (reg:SI 26) (reg:SI 25))) [(set (reg:SI 29) (mult:SI (reg:SI 26) (reg:SI 25)))
...@@ -4120,7 +4097,7 @@ ...@@ -4120,7 +4097,7 @@
"TARGET_64BIT" "TARGET_64BIT"
"* return output_mul_insn (0, insn);" "* return output_mul_insn (0, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (const_int 4))]) (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(define_expand "muldi3" (define_expand "muldi3"
[(set (match_operand:DI 0 "register_operand" "") [(set (match_operand:DI 0 "register_operand" "")
...@@ -4211,27 +4188,7 @@ ...@@ -4211,27 +4188,7 @@
"* "*
return output_div_insn (operands, 0, insn);" return output_div_insn (operands, 0, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(cond [
;; Target (or stub) within reach
(and (lt (plus (symbol_ref "total_code_bytes") (pc))
(const_int 240000))
(eq (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0)))
(const_int 4)
;; Out of reach PIC
(ne (symbol_ref "flag_pic")
(const_int 0))
(const_int 24)
;; Out of reach PORTABLE_RUNTIME
(ne (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0))
(const_int 20)]
;; Out of reach, can use ble
(const_int 12)))])
(define_insn "" (define_insn ""
[(set (reg:SI 29) [(set (reg:SI 29)
...@@ -4245,7 +4202,7 @@ ...@@ -4245,7 +4202,7 @@
"* "*
return output_div_insn (operands, 0, insn);" return output_div_insn (operands, 0, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (const_int 4))]) (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(define_expand "udivsi3" (define_expand "udivsi3"
[(set (reg:SI 26) (match_operand:SI 1 "move_operand" "")) [(set (reg:SI 26) (match_operand:SI 1 "move_operand" ""))
...@@ -4261,6 +4218,7 @@ ...@@ -4261,6 +4218,7 @@
" "
{ {
operands[3] = gen_reg_rtx (SImode); operands[3] = gen_reg_rtx (SImode);
if (TARGET_64BIT) if (TARGET_64BIT)
{ {
operands[5] = gen_rtx_REG (SImode, 2); operands[5] = gen_rtx_REG (SImode, 2);
...@@ -4287,27 +4245,7 @@ ...@@ -4287,27 +4245,7 @@
"* "*
return output_div_insn (operands, 1, insn);" return output_div_insn (operands, 1, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(cond [
;; Target (or stub) within reach
(and (lt (plus (symbol_ref "total_code_bytes") (pc))
(const_int 240000))
(eq (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0)))
(const_int 4)
;; Out of reach PIC
(ne (symbol_ref "flag_pic")
(const_int 0))
(const_int 24)
;; Out of reach PORTABLE_RUNTIME
(ne (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0))
(const_int 20)]
;; Out of reach, can use ble
(const_int 12)))])
(define_insn "" (define_insn ""
[(set (reg:SI 29) [(set (reg:SI 29)
...@@ -4321,7 +4259,7 @@ ...@@ -4321,7 +4259,7 @@
"* "*
return output_div_insn (operands, 1, insn);" return output_div_insn (operands, 1, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (const_int 4))]) (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(define_expand "modsi3" (define_expand "modsi3"
[(set (reg:SI 26) (match_operand:SI 1 "move_operand" "")) [(set (reg:SI 26) (match_operand:SI 1 "move_operand" ""))
...@@ -4360,27 +4298,7 @@ ...@@ -4360,27 +4298,7 @@
"* "*
return output_mod_insn (0, insn);" return output_mod_insn (0, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(cond [
;; Target (or stub) within reach
(and (lt (plus (symbol_ref "total_code_bytes") (pc))
(const_int 240000))
(eq (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0)))
(const_int 4)
;; Out of reach PIC
(ne (symbol_ref "flag_pic")
(const_int 0))
(const_int 24)
;; Out of reach PORTABLE_RUNTIME
(ne (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0))
(const_int 20)]
;; Out of reach, can use ble
(const_int 12)))])
(define_insn "" (define_insn ""
[(set (reg:SI 29) (mod:SI (reg:SI 26) (reg:SI 25))) [(set (reg:SI 29) (mod:SI (reg:SI 26) (reg:SI 25)))
...@@ -4393,7 +4311,7 @@ ...@@ -4393,7 +4311,7 @@
"* "*
return output_mod_insn (0, insn);" return output_mod_insn (0, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (const_int 4))]) (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(define_expand "umodsi3" (define_expand "umodsi3"
[(set (reg:SI 26) (match_operand:SI 1 "move_operand" "")) [(set (reg:SI 26) (match_operand:SI 1 "move_operand" ""))
...@@ -4432,27 +4350,7 @@ ...@@ -4432,27 +4350,7 @@
"* "*
return output_mod_insn (1, insn);" return output_mod_insn (1, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
(cond [
;; Target (or stub) within reach
(and (lt (plus (symbol_ref "total_code_bytes") (pc))
(const_int 240000))
(eq (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0)))
(const_int 4)
;; Out of reach PIC
(ne (symbol_ref "flag_pic")
(const_int 0))
(const_int 24)
;; Out of reach PORTABLE_RUNTIME
(ne (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0))
(const_int 20)]
;; Out of reach, can use ble
(const_int 12)))])
(define_insn "" (define_insn ""
[(set (reg:SI 29) (umod:SI (reg:SI 26) (reg:SI 25))) [(set (reg:SI 29) (umod:SI (reg:SI 26) (reg:SI 25)))
...@@ -4465,7 +4363,7 @@ ...@@ -4465,7 +4363,7 @@
"* "*
return output_mod_insn (1, insn);" return output_mod_insn (1, insn);"
[(set_attr "type" "milli") [(set_attr "type" "milli")
(set (attr "length") (const_int 4))]) (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 0)"))])
;;- and instructions ;;- and instructions
;; We define DImode `and` so with DImode `not` we can get ;; We define DImode `and` so with DImode `not` we can get
...@@ -6036,11 +5934,12 @@ ...@@ -6036,11 +5934,12 @@
call_insn = emit_call_insn (gen_call_internal_reg (operands[1])); call_insn = emit_call_insn (gen_call_internal_reg (operands[1]));
} }
if (TARGET_64BIT)
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
if (flag_pic) if (flag_pic)
{ {
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx); use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx);
if (TARGET_64BIT)
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
/* After each call we must restore the PIC register, even if it /* After each call we must restore the PIC register, even if it
doesn't appear to be used. */ doesn't appear to be used. */
...@@ -6052,6 +5951,7 @@ ...@@ -6052,6 +5951,7 @@
(define_insn "call_internal_symref" (define_insn "call_internal_symref"
[(call (mem:SI (match_operand 0 "call_operand_address" "")) [(call (mem:SI (match_operand 0 "call_operand_address" ""))
(match_operand 1 "" "i")) (match_operand 1 "" "i"))
(clobber (reg:SI 1))
(clobber (reg:SI 2)) (clobber (reg:SI 2))
(use (const_int 0))] (use (const_int 0))]
"! TARGET_PORTABLE_RUNTIME" "! TARGET_PORTABLE_RUNTIME"
...@@ -6061,21 +5961,7 @@ ...@@ -6061,21 +5961,7 @@
return output_call (insn, operands[0], 0); return output_call (insn, operands[0], 0);
}" }"
[(set_attr "type" "call") [(set_attr "type" "call")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_call (insn, 0)"))])
;; If we're sure that we can either reach the target or that the
;; linker can use a long-branch stub, then the length is at most
;; 8 bytes.
;;
;; For long-calls the length will be at most 68 bytes (non-pic)
;; or 84 bytes (pic). */
;; Else we have to use a long-call;
(if_then_else (lt (plus (symbol_ref "total_code_bytes") (pc))
(const_int 240000))
(const_int 8)
(if_then_else (eq (symbol_ref "flag_pic")
(const_int 0))
(const_int 68)
(const_int 84))))])
(define_insn "call_internal_reg_64bit" (define_insn "call_internal_reg_64bit"
[(call (mem:SI (match_operand:DI 0 "register_operand" "r")) [(call (mem:SI (match_operand:DI 0 "register_operand" "r"))
...@@ -6086,15 +5972,16 @@ ...@@ -6086,15 +5972,16 @@
"* "*
{ {
/* ??? Needs more work. Length computation, split into multiple insns, /* ??? Needs more work. Length computation, split into multiple insns,
do not use %r22 directly, expose delay slot. */ expose delay slot. */
return \"ldd 16(%0),%%r2\;ldd 24(%0),%%r27\;bve,l (%%r2),%%r2\;nop\"; return \"ldd 16(%0),%%r2\;bve,l (%%r2),%%r2\;ldd 24(%0),%%r27\";
}" }"
[(set_attr "type" "dyncall") [(set_attr "type" "dyncall")
(set (attr "length") (const_int 16))]) (set (attr "length") (const_int 12))])
(define_insn "call_internal_reg" (define_insn "call_internal_reg"
[(call (mem:SI (reg:SI 22)) [(call (mem:SI (reg:SI 22))
(match_operand 0 "" "i")) (match_operand 0 "" "i"))
(clobber (reg:SI 1))
(clobber (reg:SI 2)) (clobber (reg:SI 2))
(use (const_int 1))] (use (const_int 1))]
"" ""
...@@ -6218,11 +6105,13 @@ ...@@ -6218,11 +6105,13 @@
call_insn = emit_call_insn (gen_call_value_internal_reg (operands[0], call_insn = emit_call_insn (gen_call_value_internal_reg (operands[0],
operands[2])); operands[2]));
} }
if (TARGET_64BIT)
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
if (flag_pic) if (flag_pic)
{ {
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx); use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx);
if (TARGET_64BIT)
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
/* After each call we must restore the PIC register, even if it /* After each call we must restore the PIC register, even if it
doesn't appear to be used. */ doesn't appear to be used. */
...@@ -6235,6 +6124,7 @@ ...@@ -6235,6 +6124,7 @@
[(set (match_operand 0 "" "=rf") [(set (match_operand 0 "" "=rf")
(call (mem:SI (match_operand 1 "call_operand_address" "")) (call (mem:SI (match_operand 1 "call_operand_address" ""))
(match_operand 2 "" "i"))) (match_operand 2 "" "i")))
(clobber (reg:SI 1))
(clobber (reg:SI 2)) (clobber (reg:SI 2))
(use (const_int 0))] (use (const_int 0))]
;;- Don't use operand 1 for most machines. ;;- Don't use operand 1 for most machines.
...@@ -6245,21 +6135,7 @@ ...@@ -6245,21 +6135,7 @@
return output_call (insn, operands[1], 0); return output_call (insn, operands[1], 0);
}" }"
[(set_attr "type" "call") [(set_attr "type" "call")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_call (insn, 0)"))])
;; If we're sure that we can either reach the target or that the
;; linker can use a long-branch stub, then the length is at most
;; 8 bytes.
;;
;; For long-calls the length will be at most 68 bytes (non-pic)
;; or 84 bytes (pic). */
;; Else we have to use a long-call;
(if_then_else (lt (plus (symbol_ref "total_code_bytes") (pc))
(const_int 240000))
(const_int 8)
(if_then_else (eq (symbol_ref "flag_pic")
(const_int 0))
(const_int 68)
(const_int 84))))])
(define_insn "call_value_internal_reg_64bit" (define_insn "call_value_internal_reg_64bit"
[(set (match_operand 0 "" "=rf") [(set (match_operand 0 "" "=rf")
...@@ -6271,16 +6147,17 @@ ...@@ -6271,16 +6147,17 @@
"* "*
{ {
/* ??? Needs more work. Length computation, split into multiple insns, /* ??? Needs more work. Length computation, split into multiple insns,
do not use %r22 directly, expose delay slot. */ expose delay slot. */
return \"ldd 16(%1),%%r2\;ldd 24(%1),%%r27\;bve,l (%%r2),%%r2\;nop\"; return \"ldd 16(%1),%%r2\;bve,l (%%r2),%%r2\;ldd 24(%1),%%r27\";
}" }"
[(set_attr "type" "dyncall") [(set_attr "type" "dyncall")
(set (attr "length") (const_int 16))]) (set (attr "length") (const_int 12))])
(define_insn "call_value_internal_reg" (define_insn "call_value_internal_reg"
[(set (match_operand 0 "" "=rf") [(set (match_operand 0 "" "=rf")
(call (mem:SI (reg:SI 22)) (call (mem:SI (reg:SI 22))
(match_operand 1 "" "i"))) (match_operand 1 "" "i")))
(clobber (reg:SI 1))
(clobber (reg:SI 2)) (clobber (reg:SI 2))
(use (const_int 1))] (use (const_int 1))]
"" ""
...@@ -6389,10 +6266,9 @@ ...@@ -6389,10 +6266,9 @@
}") }")
(define_expand "sibcall" (define_expand "sibcall"
[(parallel [(call (match_operand:SI 0 "" "") [(call (match_operand:SI 0 "" "")
(match_operand 1 "" "")) (match_operand 1 "" ""))]
(clobber (reg:SI 0))])] "!TARGET_PORTABLE_RUNTIME"
"! TARGET_PORTABLE_RUNTIME"
" "
{ {
rtx op; rtx op;
...@@ -6400,8 +6276,21 @@ ...@@ -6400,8 +6276,21 @@
op = XEXP (operands[0], 0); op = XEXP (operands[0], 0);
/* We do not allow indirect sibling calls. */ if (TARGET_64BIT)
call_insn = emit_call_insn (gen_sibcall_internal_symref (op, operands[1])); emit_move_insn (arg_pointer_rtx,
gen_rtx_PLUS (word_mode, virtual_outgoing_args_rtx,
GEN_INT (64)));
/* Indirect sibling calls are not allowed. */
if (TARGET_64BIT)
call_insn = gen_sibcall_internal_symref_64bit (op, operands[1]);
else
call_insn = gen_sibcall_internal_symref (op, operands[1]);
call_insn = emit_call_insn (call_insn);
if (TARGET_64BIT)
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
if (flag_pic) if (flag_pic)
{ {
...@@ -6417,38 +6306,39 @@ ...@@ -6417,38 +6306,39 @@
(define_insn "sibcall_internal_symref" (define_insn "sibcall_internal_symref"
[(call (mem:SI (match_operand 0 "call_operand_address" "")) [(call (mem:SI (match_operand 0 "call_operand_address" ""))
(match_operand 1 "" "i")) (match_operand 1 "" "i"))
(clobber (reg:SI 0)) (clobber (reg:SI 1))
(use (reg:SI 2)) (use (reg:SI 2))
(use (const_int 0))] (use (const_int 0))]
"! TARGET_PORTABLE_RUNTIME" "!TARGET_PORTABLE_RUNTIME && !TARGET_64BIT"
"* "*
{ {
output_arg_descriptor (insn); output_arg_descriptor (insn);
return output_call (insn, operands[0], 1); return output_call (insn, operands[0], 1);
}" }"
[(set_attr "type" "call") [(set_attr "type" "call")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_call (insn, 1)"))])
;; If we're sure that we can either reach the target or that the
;; linker can use a long-branch stub, then the length is at most (define_insn "sibcall_internal_symref_64bit"
;; 8 bytes. [(call (mem:SI (match_operand 0 "call_operand_address" ""))
;; (match_operand 1 "" "i"))
;; For long-calls the length will be at most 68 bytes (non-pic) (clobber (reg:SI 1))
;; or 84 bytes (pic). */ (clobber (reg:SI 27))
;; Else we have to use a long-call; (use (reg:SI 2))
(if_then_else (lt (plus (symbol_ref "total_code_bytes") (pc)) (use (const_int 0))]
(const_int 240000)) "TARGET_64BIT"
(const_int 8) "*
(if_then_else (eq (symbol_ref "flag_pic") {
(const_int 0)) output_arg_descriptor (insn);
(const_int 68) return output_call (insn, operands[0], 1);
(const_int 84))))]) }"
[(set_attr "type" "call")
(set (attr "length") (symbol_ref "attr_length_call (insn, 1)"))])
(define_expand "sibcall_value" (define_expand "sibcall_value"
[(parallel [(set (match_operand 0 "" "") [(set (match_operand 0 "" "")
(call (match_operand:SI 1 "" "") (call (match_operand:SI 1 "" "")
(match_operand 2 "" ""))) (match_operand 2 "" "")))]
(clobber (reg:SI 0))])] "!TARGET_PORTABLE_RUNTIME"
"! TARGET_PORTABLE_RUNTIME"
" "
{ {
rtx op; rtx op;
...@@ -6456,10 +6346,24 @@ ...@@ -6456,10 +6346,24 @@
op = XEXP (operands[1], 0); op = XEXP (operands[1], 0);
/* We do not allow indirect sibling calls. */ if (TARGET_64BIT)
call_insn = emit_call_insn (gen_sibcall_value_internal_symref (operands[0], emit_move_insn (arg_pointer_rtx,
op, gen_rtx_PLUS (word_mode, virtual_outgoing_args_rtx,
operands[2])); GEN_INT (64)));
/* Indirect sibling calls are not allowed. */
if (TARGET_64BIT)
call_insn
= gen_sibcall_value_internal_symref_64bit (operands[0], op, operands[2]);
else
call_insn
= gen_sibcall_value_internal_symref (operands[0], op, operands[2]);
call_insn = emit_call_insn (call_insn);
if (TARGET_64BIT)
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
if (flag_pic) if (flag_pic)
{ {
use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx); use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx);
...@@ -6475,32 +6379,34 @@ ...@@ -6475,32 +6379,34 @@
[(set (match_operand 0 "" "=rf") [(set (match_operand 0 "" "=rf")
(call (mem:SI (match_operand 1 "call_operand_address" "")) (call (mem:SI (match_operand 1 "call_operand_address" ""))
(match_operand 2 "" "i"))) (match_operand 2 "" "i")))
(clobber (reg:SI 0)) (clobber (reg:SI 1))
(use (reg:SI 2)) (use (reg:SI 2))
(use (const_int 0))] (use (const_int 0))]
;;- Don't use operand 1 for most machines. "!TARGET_PORTABLE_RUNTIME && !TARGET_64BIT"
"! TARGET_PORTABLE_RUNTIME"
"* "*
{ {
output_arg_descriptor (insn); output_arg_descriptor (insn);
return output_call (insn, operands[1], 1); return output_call (insn, operands[1], 1);
}" }"
[(set_attr "type" "call") [(set_attr "type" "call")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_call (insn, 1)"))])
;; If we're sure that we can either reach the target or that the
;; linker can use a long-branch stub, then the length is at most (define_insn "sibcall_value_internal_symref_64bit"
;; 8 bytes. [(set (match_operand 0 "" "=rf")
;; (call (mem:SI (match_operand 1 "call_operand_address" ""))
;; For long-calls the length will be at most 68 bytes (non-pic) (match_operand 2 "" "i")))
;; or 84 bytes (pic). */ (clobber (reg:SI 1))
;; Else we have to use a long-call; (clobber (reg:SI 27))
(if_then_else (lt (plus (symbol_ref "total_code_bytes") (pc)) (use (reg:SI 2))
(const_int 240000)) (use (const_int 0))]
(const_int 8) "TARGET_64BIT"
(if_then_else (eq (symbol_ref "flag_pic") "*
(const_int 0)) {
(const_int 68) output_arg_descriptor (insn);
(const_int 84))))]) return output_call (insn, operands[1], 1);
}"
[(set_attr "type" "call")
(set (attr "length") (symbol_ref "attr_length_call (insn, 1)"))])
(define_insn "nop" (define_insn "nop"
[(const_int 0)] [(const_int 0)]
...@@ -7392,6 +7298,12 @@ ...@@ -7392,6 +7298,12 @@
"!TARGET_64BIT" "!TARGET_64BIT"
"* "*
{ {
int length = get_attr_length (insn);
rtx xoperands[2];
xoperands[0] = GEN_INT (length - 8);
xoperands[1] = GEN_INT (length - 16);
/* Must import the magic millicode routine. */ /* Must import the magic millicode routine. */
output_asm_insn (\".IMPORT $$sh_func_adrs,MILLICODE\", NULL); output_asm_insn (\".IMPORT $$sh_func_adrs,MILLICODE\", NULL);
...@@ -7400,60 +7312,24 @@ ...@@ -7400,60 +7312,24 @@
First, copy our input parameter into %r29 just in case we don't First, copy our input parameter into %r29 just in case we don't
need to call $$sh_func_adrs. */ need to call $$sh_func_adrs. */
output_asm_insn (\"copy %%r26,%%r29\", NULL); output_asm_insn (\"copy %%r26,%%r29\", NULL);
output_asm_insn (\"{extru|extrw,u} %%r26,31,2,%%r31\", NULL);
/* Next, examine the low two bits in %r26, if they aren't 0x2, then /* Next, examine the low two bits in %r26, if they aren't 0x2, then
we use %r26 unchanged. */ we use %r26 unchanged. */
if (get_attr_length (insn) == 32) output_asm_insn (\"{comib|cmpib},<>,n 2,%%r31,.+%0\", xoperands);
output_asm_insn (\"{extru|extrw,u} %%r26,31,2,%%r31\;{comib|cmpib},<>,n 2,%%r31,.+24\", NULL); output_asm_insn (\"ldi 4096,%%r31\", NULL);
else if (get_attr_length (insn) == 40)
output_asm_insn (\"{extru|extrw,u} %%r26,31,2,%%r31\;{comib|cmpib},<>,n 2,%%r31,.+32\", NULL);
else if (get_attr_length (insn) == 44)
output_asm_insn (\"{extru|extrw,u} %%r26,31,2,%%r31\;{comib|cmpib},<>,n 2,%%r31,.+36\", NULL);
else
output_asm_insn (\"{extru|extrw,u} %%r26,31,2,%%r31\;{comib|cmpib},<>,n 2,%%r31,.+20\", NULL);
/* Next, compare %r26 with 4096, if %r26 is less than or equal to /* Next, compare %r26 with 4096, if %r26 is less than or equal to
4096, then we use %r26 unchanged. */ 4096, then again we use %r26 unchanged. */
if (get_attr_length (insn) == 32) output_asm_insn (\"{comb|cmpb},<<,n %%r26,%%r31,.+%1\", xoperands);
output_asm_insn (\"ldi 4096,%%r31\;{comb|cmpb},<<,n %%r26,%%r31,.+16\",
NULL);
else if (get_attr_length (insn) == 40)
output_asm_insn (\"ldi 4096,%%r31\;{comb|cmpb},<<,n %%r26,%%r31,.+24\",
NULL);
else if (get_attr_length (insn) == 44)
output_asm_insn (\"ldi 4096,%%r31\;{comb|cmpb},<<,n %%r26,%%r31,.+28\",
NULL);
else
output_asm_insn (\"ldi 4096,%%r31\;{comb|cmpb},<<,n %%r26,%%r31,.+12\",
NULL);
/* Else call $$sh_func_adrs to extract the function's real add24. */ /* Finally, call $$sh_func_adrs to extract the function's real add24. */
return output_millicode_call (insn, return output_millicode_call (insn,
gen_rtx_SYMBOL_REF (SImode, gen_rtx_SYMBOL_REF (SImode,
\"$$sh_func_adrs\")); \"$$sh_func_adrs\"));
}" }"
[(set_attr "type" "multi") [(set_attr "type" "multi")
(set (attr "length") (set (attr "length") (symbol_ref "attr_length_millicode_call (insn, 20)"))])
(cond [
;; Target (or stub) within reach
(and (lt (plus (symbol_ref "total_code_bytes") (pc))
(const_int 240000))
(eq (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0)))
(const_int 28)
;; Out of reach PIC
(ne (symbol_ref "flag_pic")
(const_int 0))
(const_int 44)
;; Out of reach PORTABLE_RUNTIME
(ne (symbol_ref "TARGET_PORTABLE_RUNTIME")
(const_int 0))
(const_int 40)]
;; Out of reach, can use ble
(const_int 32)))])
;; On the PA, the PIC register is call clobbered, so it must ;; On the PA, the PIC register is call clobbered, so it must
;; be saved & restored around calls by the caller. If the call ;; be saved & restored around calls by the caller. If the call
......
...@@ -371,3 +371,7 @@ do { \ ...@@ -371,3 +371,7 @@ do { \
on the location of the GCC tool directory. The downside is GCC on the location of the GCC tool directory. The downside is GCC
cannot be moved after installation using a symlink. */ cannot be moved after installation using a symlink. */
#define ALWAYS_STRIP_DOTDOT 1 #define ALWAYS_STRIP_DOTDOT 1
/* Aggregates with a single float or double field should be passed and
returned in the general registers. */
#define MEMBER_TYPE_FORCES_BLK(FIELD, MODE) (MODE==SFmode || MODE==DFmode)
TARGET_LIBGCC2_CFLAGS = -fPIC -Dpa64=1 -DELF=1 TARGET_LIBGCC2_CFLAGS = -fPIC -Dpa64=1 -DELF=1 -mlong-calls
LIB2FUNCS_EXTRA=quadlib.c LIB2FUNCS_EXTRA=quadlib.c
......
...@@ -508,7 +508,7 @@ in the following sections. ...@@ -508,7 +508,7 @@ in the following sections.
-march=@var{architecture-type} @gol -march=@var{architecture-type} @gol
-mbig-switch -mdisable-fpregs -mdisable-indexing @gol -mbig-switch -mdisable-fpregs -mdisable-indexing @gol
-mfast-indirect-calls -mgas -mgnu-ld -mhp-ld @gol -mfast-indirect-calls -mgas -mgnu-ld -mhp-ld @gol
-mjump-in-delay -mlinker-opt @gol -mjump-in-delay -mlinker-opt -mlong-calls @gol
-mlong-load-store -mno-big-switch -mno-disable-fpregs @gol -mlong-load-store -mno-big-switch -mno-disable-fpregs @gol
-mno-disable-indexing -mno-fast-indirect-calls -mno-gas @gol -mno-disable-indexing -mno-fast-indirect-calls -mno-gas @gol
-mno-jump-in-delay -mno-long-load-store @gol -mno-jump-in-delay -mno-long-load-store @gol
...@@ -8094,6 +8094,33 @@ configure option, gcc's program search path, and finally by the user's ...@@ -8094,6 +8094,33 @@ configure option, gcc's program search path, and finally by the user's
@env{PATH}. The linker used by GCC can be printed using @samp{which @env{PATH}. The linker used by GCC can be printed using @samp{which
`gcc -print-prog-name=ld`}. `gcc -print-prog-name=ld`}.
@item -mlong-calls
@opindex mno-long-calls
Generate code that uses long call sequences. This ensures that a call
is always able to reach linker generated stubs. The default is to generate
long calls only when the distance from the call site to the beginning
of the function or translation unit, as the case may be, exceeds a
predefined limit set by the branch type being used. The limits for
normal calls are 7,600,000 and 240,000 bytes, respectively for the
PA 2.0 and PA 1.X architectures. Sibcalls are always limited at
240,000 bytes.
Distances are measured from the beginning of functions when using the
@option{-ffunction-sections} option, or when using the @option{-mgas}
and @option{-mno-portable-runtime} options together under HP-UX with
the SOM linker.
It is normally not desirable to use this option as it will degrade
performance. However, it may be useful in large applications,
particularly when partial linking is used to build the application.
The types of long calls used depends on the capabilities of the
assembler and linker, and the type of code being generated. The
impact on systems that support long absolute calls, and long pic
symbol-difference or pc-relative calls should be relatively small.
However, an indirect call is used on 32-bit ELF systems in pic code
and it is quite long.
@end table @end table
@node Intel 960 Options @node Intel 960 Options
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment