diff options
author | John David Anglin <dave.anglin@nrc-cnrc.gc.ca> | 2003-06-29 18:21:57 +0000 |
---|---|---|
committer | John David Anglin <danglin@gcc.gnu.org> | 2003-06-29 18:21:57 +0000 |
commit | 5fad1c24dba769d854f9c3a7d37c359ec84c39eb (patch) | |
tree | 4530b135c43f906ef7b411175cd7fc7732dcb684 /gcc/config/pa | |
parent | 342e2b749d651b51cee1f484966e260c33915575 (diff) | |
download | gcc-5fad1c24dba769d854f9c3a7d37c359ec84c39eb.zip gcc-5fad1c24dba769d854f9c3a7d37c359ec84c39eb.tar.gz gcc-5fad1c24dba769d854f9c3a7d37c359ec84c39eb.tar.bz2 |
pa.c (update_total_code_bytes): New function.
* pa.c (update_total_code_bytes): New function.
(last_address): Number of bytes output for a function and its
associated thunks.
(compute_frame_size): Use BITS_PER_UNIT.
(pa_output_function_epilogue): Compute last_address. Use
update_total_code_bytes.
(output_lbranch): Handle long branch on portable runtime.
(attr_length_millicode_call, attr_length_call,
attr_length_indirect_call): Only use total_code_bytes for calls in
the text section.
(output_call): Only use an indirect call sequence when the target is
not local.
(pa_asm_output_mi_thunk): Handle updating of total_code_bytes. Improve
test to determine when an IA-relative branch can be used. Add various
long branch sequences. Avoid using an indirect branch on all ports
except SOM.
From-SVN: r68677
Diffstat (limited to 'gcc/config/pa')
-rw-r--r-- | gcc/config/pa/pa.c | 375 |
1 files changed, 283 insertions, 92 deletions
diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c index b17d1dc..e7b0b8a 100644 --- a/gcc/config/pa/pa.c +++ b/gcc/config/pa/pa.c @@ -113,6 +113,7 @@ static void store_reg_modify PARAMS ((int, int, int)); static void load_reg PARAMS ((int, int, int)); static void set_reg_plus_d PARAMS ((int, int, int, int)); static void pa_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT)); +static void update_total_code_bytes PARAMS ((int)); static void pa_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT)); static int pa_adjust_cost PARAMS ((rtx, rtx, rtx, int)); static int pa_adjust_priority PARAMS ((rtx, int)); @@ -169,10 +170,15 @@ static int gr_saved, fr_saved; 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 subspace during this compilation so we'll know when to emit inline long-calls. */ unsigned long total_code_bytes; +/* The last address of the previous function plus the number of bytes in + associated thunks that have been output. This is used to determine if + a thunk can use an IA-relative branch to reach its target function. */ +static int last_address; + /* Variables to handle plabels that we discover are necessary at assembly output time. They are output after the current function. */ struct deferred_plabel GTY(()) @@ -3292,8 +3298,8 @@ compute_frame_size (size, fregs_live) size += TARGET_64BIT ? 48 : 32; /* Finally, round to the preferred stack boundary. */ - return ((size + PREFERRED_STACK_BOUNDARY / 8 - 1) - & ~(PREFERRED_STACK_BOUNDARY / 8 - 1)); + return ((size + PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1) + & ~(PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1)); } /* Generate the assembly code for function entry. FILE is a stdio @@ -3681,6 +3687,30 @@ load_reg (reg, disp, base) } } +/* Update the total code bytes output to the text section. */ + +static void +update_total_code_bytes (nbytes) + int nbytes; +{ + if ((TARGET_PORTABLE_RUNTIME || !TARGET_GAS || !TARGET_SOM) + && in_text_section ()) + { + if (INSN_ADDRESSES_SET_P ()) + { + unsigned long old_total = total_code_bytes; + + total_code_bytes += nbytes; + + /* Be prepared to handle overflows. */ + if (old_total > total_code_bytes) + total_code_bytes = -1; + } + else + total_code_bytes = -1; + } +} + /* This function generates the assembly code for function exit. Args are as for output_function_prologue (). @@ -3694,9 +3724,10 @@ pa_output_function_epilogue (file, size) FILE *file; HOST_WIDE_INT size ATTRIBUTE_UNUSED; { - int last_address = 0; rtx insn = get_last_insn (); + last_address = 0; + /* hppa_expand_epilogue does the dirty work now. We just need to output the assembler directives which denote the end of a function. @@ -3724,29 +3755,18 @@ pa_output_function_epilogue (file, size) fputs ("\t.EXIT\n\t.PROCEND\n", file); - /* Finally, update the total number of code bytes output so far. */ - if ((TARGET_PORTABLE_RUNTIME || !TARGET_GAS || !TARGET_SOM) - && !flag_function_sections) + if (INSN_ADDRESSES_SET_P ()) { - if (INSN_ADDRESSES_SET_P ()) - { - unsigned long old_total = total_code_bytes; - - insn = get_last_nonnote_insn (); - last_address += INSN_ADDRESSES (INSN_UID (insn)); - if (INSN_P (insn)) - last_address += insn_default_length (insn); - - total_code_bytes += last_address; - total_code_bytes += FUNCTION_BOUNDARY / BITS_PER_UNIT; - - /* Be prepared to handle overflows. */ - if (old_total > total_code_bytes) - total_code_bytes = -1; - } - else - total_code_bytes = -1; + insn = get_last_nonnote_insn (); + last_address += INSN_ADDRESSES (INSN_UID (insn)); + if (INSN_P (insn)) + last_address += insn_default_length (insn); + last_address = ((last_address + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1) + & ~(FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)); } + + /* Finally, update the total number of code bytes output so far. */ + update_total_code_bytes (last_address); } void @@ -5904,7 +5924,13 @@ output_lbranch (dest, insn) output_asm_insn ("stw %%r1,-12(%%r30)", xoperands); } - if (flag_pic) + if (TARGET_PORTABLE_RUNTIME) + { + output_asm_insn ("ldil L'%0,%%r1", xoperands); + output_asm_insn ("ldo R'%0(%%r1),%%r1", xoperands); + output_asm_insn ("bv %%r0(%%r1)", xoperands); + } + else if (flag_pic) { output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands); if (TARGET_SOM || !TARGET_GAS) @@ -6545,11 +6571,12 @@ attr_length_millicode_call (insn) rtx insn; { unsigned long distance = -1; + unsigned long total = in_text_section () ? total_code_bytes : 0; if (INSN_ADDRESSES_SET_P ()) { - distance = (total_code_bytes + insn_current_reference_address (insn)); - if (distance < total_code_bytes) + distance = (total + insn_current_reference_address (insn)); + if (distance < total) distance = -1; } @@ -6741,11 +6768,12 @@ attr_length_call (insn, sibcall) int sibcall; { unsigned long distance = -1; + unsigned long total = in_text_section ()? total_code_bytes : 0; if (INSN_ADDRESSES_SET_P ()) { - distance = (total_code_bytes + insn_current_reference_address (insn)); - if (distance < total_code_bytes) + distance = (total + insn_current_reference_address (insn)); + if (distance < total) distance = -1; } @@ -6813,12 +6841,15 @@ output_call (insn, call_dest, sibcall) int delay_insn_deleted = 0; int delay_slot_filled = 0; int seq_length = dbr_sequence_length (); + tree call_decl = SYMBOL_REF_DECL (call_dest); + int local_call = call_decl && !TREE_PUBLIC (call_decl); rtx xoperands[2]; xoperands[0] = call_dest; /* Handle the common case where we're sure that the branch will reach - the beginning of the $CODE$ subspace. */ + the beginning of the "$CODE$" subspace. This is the beginning of + the current function if we are in a named section. */ if (!TARGET_LONG_CALLS && attr_length_call (insn, sibcall) == 8) { xoperands[1] = gen_rtx_REG (word_mode, sibcall ? 0 : 2); @@ -6826,7 +6857,7 @@ output_call (insn, call_dest, sibcall) } else { - if (TARGET_64BIT) + if (TARGET_64BIT && !local_call) { /* ??? As far as I can tell, the HP linker doesn't support the long pc-relative sequence described in the 64-bit runtime @@ -6878,9 +6909,10 @@ output_call (insn, call_dest, sibcall) /* Emit a long call. There are several different sequences of increasing length and complexity. In most cases, they don't allow an instruction in the delay slot. */ - if (!(TARGET_LONG_ABS_CALL && !flag_pic) + if (!((TARGET_LONG_ABS_CALL || local_call) && !flag_pic) && !(TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL) - && !(TARGET_GAS && TARGET_LONG_PIC_PCREL_CALL)) + && !(TARGET_GAS && (TARGET_LONG_PIC_PCREL_CALL || local_call)) + && !TARGET_64BIT) indirect_call = 1; if (seq_length != 0 @@ -6900,12 +6932,13 @@ output_call (insn, call_dest, sibcall) delay_insn_deleted = 1; } - if (TARGET_LONG_ABS_CALL && !flag_pic) + if ((TARGET_LONG_ABS_CALL || local_call) && !flag_pic) { /* 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. */ + for this with the SOM linker is buggy. It is safe + to use this for local calls. */ output_asm_insn ("ldil L'%0,%%r1", xoperands); if (sibcall) output_asm_insn ("be R'%0(%%sr4,%%r1)", xoperands); @@ -6923,7 +6956,8 @@ output_call (insn, call_dest, sibcall) } else { - if (TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL) + if ((TARGET_SOM && TARGET_LONG_PIC_SDIFF_CALL) + || (TARGET_64BIT && !TARGET_GAS)) { /* The HP assembler and linker can handle relocations for the difference of two symbols. GAS and the HP @@ -6936,7 +6970,7 @@ output_call (insn, call_dest, sibcall) CODE_LABEL_NUMBER (xoperands[1])); output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands); } - else if (TARGET_GAS && TARGET_LONG_PIC_PCREL_CALL) + else if (TARGET_GAS && (TARGET_LONG_PIC_PCREL_CALL || local_call)) { /* GAS currently can't generate the relocations that are needed for the SOM linker under HP-UX using this @@ -7075,11 +7109,9 @@ output_call (insn, call_dest, sibcall) CODE_LABEL_NUMBER (xoperands[1])); } 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. */ @@ -7101,11 +7133,12 @@ attr_length_indirect_call (insn) rtx insn; { unsigned long distance = -1; + unsigned long total = in_text_section () ? total_code_bytes : 0; if (INSN_ADDRESSES_SET_P ()) { - distance = (total_code_bytes + insn_current_reference_address (insn)); - if (distance < total_code_bytes) + distance = (total + insn_current_reference_address (insn)); + if (distance < total) distance = -1; } @@ -7280,81 +7313,239 @@ pa_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function) HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED; tree function; { - const char *target_name = XSTR (XEXP (DECL_RTL (function), 0), 0); + const char *fname = XSTR (XEXP (DECL_RTL (function), 0), 0); + const char *tname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0); + int val_14 = VAL_14_BITS_P (delta); + int nbytes = 0; static unsigned int current_thunk_number; char label[16]; - const char *lab; - ASM_GENERATE_INTERNAL_LABEL (label, "LTHN", current_thunk_number); - lab = (*targetm.strip_name_encoding) (label); - target_name = (*targetm.strip_name_encoding) (target_name); - /* FIXME: total_code_bytes is not handled correctly in files with - mi thunks. */ - pa_output_function_prologue (file, 0); - if (VAL_14_BITS_P (delta)) - { - if (!TARGET_64BIT && !TARGET_PORTABLE_RUNTIME && flag_pic) - { - fprintf (file, "\taddil LT'%s,%%r19\n", lab); - fprintf (file, "\tldw RT'%s(%%r1),%%r22\n", lab); - fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); - fprintf (file, "\tbb,>=,n %%r22,30,.+16\n"); - fprintf (file, "\tdepi 0,31,2,%%r22\n"); - fprintf (file, "\tldw 4(%%sr0,%%r22),%%r19\n"); - fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); + + ASM_OUTPUT_LABEL (file, tname); + fprintf (file, "\t.PROC\n\t.CALLINFO FRAME=0,NO_CALLS\n\t.ENTRY\n"); + + fname = (*targetm.strip_name_encoding) (fname); + tname = (*targetm.strip_name_encoding) (tname); + + /* Output the thunk. We know that the function is in the same + translation unit (i.e., the same space) as the thunk, and that + thunks are output after their method. Thus, we don't need an + external branch to reach the function. With SOM and GAS, + functions and thunks are effectively in different sections. + Thus, we can always use a IA-relative branch and the linker + will add a long branch stub if necessary. + + However, we have to be careful when generating PIC code on the + SOM port to ensure that the sequence does not transfer to an + import stub for the target function as this could clobber the + return value saved at SP-24. This would also apply to the + 32-bit linux port if the multi-space model is implemented. */ + if ((!TARGET_LONG_CALLS && TARGET_SOM && !TARGET_PORTABLE_RUNTIME + && !(flag_pic && TREE_PUBLIC (function)) + && (TARGET_GAS || last_address < 262132)) + || (!TARGET_LONG_CALLS && !TARGET_SOM && !TARGET_PORTABLE_RUNTIME + && ((targetm.have_named_sections + && DECL_SECTION_NAME (thunk_fndecl) != NULL + /* The GNU 64-bit linker has rather poor stub management. + So, we use a long branch from thunks that aren't in + the same section as the target function. */ + && ((!TARGET_64BIT + && (DECL_SECTION_NAME (thunk_fndecl) + != DECL_SECTION_NAME (function))) + || ((DECL_SECTION_NAME (thunk_fndecl) + == DECL_SECTION_NAME (function)) + && last_address < 262132))) + || (!targetm.have_named_sections && last_address < 262132)))) + { + if (val_14) + { + fprintf (file, "\tb %s\n\tldo " HOST_WIDE_INT_PRINT_DEC + "(%%r26),%%r26\n", fname, delta); + nbytes += 8; + } + else + { + fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC + ",%%r26\n", delta); + fprintf (file, "\tb %s\n\tldo R'" HOST_WIDE_INT_PRINT_DEC + "(%%r1),%%r26\n", fname, delta); + nbytes += 12; + } + } + else if (TARGET_64BIT) + { + /* We only have one call-clobbered scratch register, so we can't + make use of the delay slot if delta doesn't fit in 14 bits. */ + if (!val_14) + fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC + ",%%r26\n\tldo R'" HOST_WIDE_INT_PRINT_DEC + "(%%r1),%%r26\n", delta, delta); + + fprintf (file, "\tb,l .+8,%%r1\n"); + + if (TARGET_GAS) + { + fprintf (file, "\taddil L'%s-$PIC_pcrel$0+4,%%r1\n", fname); + fprintf (file, "\tldo R'%s-$PIC_pcrel$0+8(%%r1),%%r1\n", fname); + } + else + { + int off = val_14 ? 8 : 16; + fprintf (file, "\taddil L'%s-%s-%d,%%r1\n", fname, tname, off); + fprintf (file, "\tldo R'%s-%s-%d(%%r1),%%r1\n", fname, tname, off); + } + + if (val_14) + { + fprintf (file, "\tbv %%r0(%%r1)\n\tldo "); + fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + nbytes += 20; + } + else + { + fprintf (file, "\tbv,n %%r0(%%r1)\n"); + nbytes += 24; + } + } + else if (TARGET_PORTABLE_RUNTIME) + { + fprintf (file, "\tldil L'%s,%%r1\n", fname); + fprintf (file, "\tldo R'%s(%%r1),%%r22\n", fname); + + if (val_14) + { + fprintf (file, "\tbv %%r0(%%r22)\n\tldo "); + fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + nbytes += 16; + } + else + { + fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC + ",%%r26\n", delta); + fprintf (file, "\tbv %%r0(%%r22)\n\tldo "); + fprintf (file, "R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", delta); + nbytes += 20; + } + } + else if (TARGET_SOM && flag_pic && TREE_PUBLIC (function)) + { + /* The function is accessible from outside this module. The only + way to avoid an import stub between the thunk and function is to + call the function directly with an indirect sequence similar to + that used by $$dyncall. This is possible because $$dyncall acts + as the import stub in an indirect call. */ + const char *lab; + + ASM_GENERATE_INTERNAL_LABEL (label, "LTHN", current_thunk_number); + lab = (*targetm.strip_name_encoding) (label); + + fprintf (file, "\taddil LT'%s,%%r19\n", lab); + fprintf (file, "\tldw RT'%s(%%r1),%%r22\n", lab); + fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); + fprintf (file, "\tbb,>=,n %%r22,30,.+16\n"); + fprintf (file, "\tdepi 0,31,2,%%r22\n"); + fprintf (file, "\tldw 4(%%sr0,%%r22),%%r19\n"); + fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); + if (!val_14) + { + fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC + ",%%r26\n", delta); + nbytes += 4; + } + if (TARGET_PA_20) + { + fprintf (file, "\tbve (%%r22)\n\tldo "); + nbytes += 36; + } + else + { if (TARGET_NO_SPACE_REGS) - fprintf (file, "\tbe 0(%%sr4,%%r22)\n\tldo "); + { + fprintf (file, "\tbe 0(%%sr4,%%r22)\n\tldo "); + nbytes += 36; + } else { fprintf (file, "\tldsid (%%sr0,%%r22),%%r1\n"); - fprintf (file, "\tmtsp %%r1,%%sr0\n"); + fprintf (file, "\tmtsp %%r21,%%sr0\n"); fprintf (file, "\tbe 0(%%sr0,%%r22)\n\tldo "); + nbytes += 44; } + } + + if (val_14) + fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + else + fprintf (file, "R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", delta); + } + else if (flag_pic) + { + if (TARGET_PA_20) + fprintf (file, "\tb,l .+8,%%r1\n"); + else + fprintf (file, "\tbl .+8,%%r1\n"); + + if (TARGET_SOM || !TARGET_GAS) + { + fprintf (file, "\taddil L'%s-%s-8,%%r1\n", fname, tname); + fprintf (file, "\tldo R'%s-%s-8(%%r1),%%r22\n", fname, tname); + } + else + { + fprintf (file, "\taddil L'%s-$PIC_pcrel$0+4,%%r1\n", fname); + fprintf (file, "\tldo R'%s-$PIC_pcrel$0+8(%%r1),%%r22\n", fname); + } + + if (val_14) + { + fprintf (file, "\tbv %%r0(%%r22)\n\tldo "); fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + nbytes += 20; } else - fprintf (file, "\tb %s\n\tldo " HOST_WIDE_INT_PRINT_DEC - "(%%r26),%%r26\n", - target_name, delta); + { + fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC + ",%%r26\n", delta); + fprintf (file, "\tbv %%r0(%%r22)\n\tldo "); + fprintf (file, "R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", delta); + nbytes += 24; + } } else { - if (!TARGET_64BIT && !TARGET_PORTABLE_RUNTIME && flag_pic) + if (!val_14) + fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC ",%%r26\n", delta); + + fprintf (file, "\tldil L'%s,%%r22\n", fname); + fprintf (file, "\tbe R'%s(%%sr4,%%r22)\n\tldo ", fname); + + if (val_14) { - fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC - ",%%r26\n\tldo R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", - delta, delta); - fprintf (file, "\taddil LT'%s,%%r19\n", lab); - fprintf (file, "\tldw RT'%s(%%r1),%%r22\n", lab); - fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); - fprintf (file, "\tbb,>=,n %%r22,30,.+16\n"); - fprintf (file, "\tdepi 0,31,2,%%r22\n"); - fprintf (file, "\tldw 4(%%sr0,%%r22),%%r19\n"); - fprintf (file, "\tldw 0(%%sr0,%%r22),%%r22\n"); - if (TARGET_NO_SPACE_REGS) - fprintf (file, "\tbe 0(%%sr4,%%r22)"); - else - { - fprintf (file, "\tldsid (%%sr0,%%r22),%%r1\n"); - fprintf (file, "\tmtsp %%r1,%%sr0\n"); - fprintf (file, "\tbe,n 0(%%sr0,%%r22)\n"); - } + fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%%r26),%%r26\n", delta); + nbytes += 12; } else - fprintf (file, "\taddil L'" HOST_WIDE_INT_PRINT_DEC - ",%%r26\n\tb %s\n\tldo R'" HOST_WIDE_INT_PRINT_DEC - "(%%r1),%%r26\n", delta, target_name, delta); + { + fprintf (file, "R'" HOST_WIDE_INT_PRINT_DEC "(%%r1),%%r26\n", delta); + nbytes += 16; + } } - + fprintf (file, "\t.EXIT\n\t.PROCEND\n"); - if (! TARGET_64BIT && ! TARGET_PORTABLE_RUNTIME && flag_pic) + + if (TARGET_SOM && flag_pic && TREE_PUBLIC (function)) { data_section (); fprintf (file, "\t.align 4\n"); - (*targetm.asm_out.internal_label) (file, "LTHN", current_thunk_number); - fprintf (file, "\t.word P'%s\n", target_name); + ASM_OUTPUT_LABEL (file, label); + fprintf (file, "\t.word P'%s\n", fname); function_section (thunk_fndecl); } + current_thunk_number++; + nbytes = ((nbytes + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1) + & ~(FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)); + last_address += nbytes; + update_total_code_bytes (nbytes); } /* Only direct calls to static functions are allowed to be sibling (tail) |