arm-protos.h (arm_encode_call_attribute): Delete.
gcc/ * config/arm/arm-protos.h (arm_encode_call_attribute): Delete. (arm_is_longcall_p): Rename to... (arm_is_long_call_p): ...this. Take a single tree argument and return a bool. * config/arm/arm.h (CALL_SHORT, CALL_LONG, CALL_NORMAL): Delete. (CUMULATIVE_ARGS): Remove call_cookie. (SHORT_CALL_FLAG_CHAR, LONG_CALL_FLAG_CHAR, ENCODED_SHORT_CALL_ATTR_P) (ENCODED_LONG_CALL_ATTR_P): Delete. (ARM_NAME_ENCODING_LENGTHS): Remove SHORT_CALL_FLAG_CHAR and LONG_CALL_FLAG_CHAR cases. (ARM_DECLARE_FUNCTION_SIZE): Delete. * config/arm/elf.h (ASM_DECLARE_FUNCTION_SIZE): Don't use ARM_DECLARE_FUNCTION_SIZE. * config/arm/arm.c (arm_init_cumulative_args): Don't set call_cookie. (arm_function_arg): Return const0_rtx for VOIDmode arguments. (arm_encode_call_attribute, current_file_function_operand): Delete. (arm_function_in_section_p): New function. (arm_is_longcall_p): Rename to... (arm_is_long_call_p): ...this. Take the target function as a single argument and return a bool. Do not rely on call cookies. Check whether the target symbol is in the same section as the current function, not just the same compilation unit. (arm_function_ok_for_sibcall): Use arm_is_long_call_p. (arm_encode_section_info): Don't encode a call type. * config/arm/arm.md (call, call_value): Update calls to arm_is_long(_)call_p. Simplify logic. (*call_symbol, *call_value_symbol, *call_insn, *call_value_insn): Update calls to arm_is_long(_)call_p. gcc/testsuite/ * gcc.target/arm/long-calls-1.c: New test. * gcc.target/arm/long-calls-2.c: Likewise. * gcc.target/arm/long-calls-3.c: Likewise. * gcc.target/arm/long-calls-4.c: Likewise. From-SVN: r125060
This commit is contained in:
parent
aaee3e893d
commit
25a6519845
11 changed files with 580 additions and 175 deletions
|
@ -1,3 +1,34 @@
|
|||
2007-05-25 Richard Sandiford <richard@codesourcery.com>
|
||||
|
||||
* config/arm/arm-protos.h (arm_encode_call_attribute): Delete.
|
||||
(arm_is_longcall_p): Rename to...
|
||||
(arm_is_long_call_p): ...this. Take a single tree argument and
|
||||
return a bool.
|
||||
* config/arm/arm.h (CALL_SHORT, CALL_LONG, CALL_NORMAL): Delete.
|
||||
(CUMULATIVE_ARGS): Remove call_cookie.
|
||||
(SHORT_CALL_FLAG_CHAR, LONG_CALL_FLAG_CHAR, ENCODED_SHORT_CALL_ATTR_P)
|
||||
(ENCODED_LONG_CALL_ATTR_P): Delete.
|
||||
(ARM_NAME_ENCODING_LENGTHS): Remove SHORT_CALL_FLAG_CHAR and
|
||||
LONG_CALL_FLAG_CHAR cases.
|
||||
(ARM_DECLARE_FUNCTION_SIZE): Delete.
|
||||
* config/arm/elf.h (ASM_DECLARE_FUNCTION_SIZE): Don't use
|
||||
ARM_DECLARE_FUNCTION_SIZE.
|
||||
* config/arm/arm.c (arm_init_cumulative_args): Don't set call_cookie.
|
||||
(arm_function_arg): Return const0_rtx for VOIDmode arguments.
|
||||
(arm_encode_call_attribute, current_file_function_operand): Delete.
|
||||
(arm_function_in_section_p): New function.
|
||||
(arm_is_longcall_p): Rename to...
|
||||
(arm_is_long_call_p): ...this. Take the target function as a single
|
||||
argument and return a bool. Do not rely on call cookies. Check
|
||||
whether the target symbol is in the same section as the current
|
||||
function, not just the same compilation unit.
|
||||
(arm_function_ok_for_sibcall): Use arm_is_long_call_p.
|
||||
(arm_encode_section_info): Don't encode a call type.
|
||||
* config/arm/arm.md (call, call_value): Update calls to
|
||||
arm_is_long(_)call_p. Simplify logic.
|
||||
(*call_symbol, *call_value_symbol, *call_insn, *call_value_insn):
|
||||
Update calls to arm_is_long(_)call_p.
|
||||
|
||||
2007-05-25 Richard Guenther <rguenther@suse.de>
|
||||
|
||||
PR tree-optimization/31982
|
||||
|
|
|
@ -45,7 +45,6 @@ extern void arm_output_fn_unwind (FILE *, bool);
|
|||
|
||||
#ifdef TREE_CODE
|
||||
extern int arm_return_in_memory (tree);
|
||||
extern void arm_encode_call_attribute (tree, int);
|
||||
#endif
|
||||
#ifdef RTX_CODE
|
||||
extern bool arm_vector_mode_supported_p (enum machine_mode);
|
||||
|
@ -121,7 +120,7 @@ extern void arm_print_operand (FILE *, rtx, int);
|
|||
extern void arm_print_operand_address (FILE *, rtx);
|
||||
extern void arm_final_prescan_insn (rtx);
|
||||
extern int arm_debugger_arg_offset (int, rtx);
|
||||
extern int arm_is_longcall_p (rtx, int, int);
|
||||
extern bool arm_is_long_call_p (tree);
|
||||
extern int arm_emit_vector_const (FILE *, rtx);
|
||||
extern const char * arm_output_load_gr (rtx *);
|
||||
extern const char *vfp_output_fstmd (rtx *);
|
||||
|
|
|
@ -104,7 +104,6 @@ static void push_minipool_fix (rtx, HOST_WIDE_INT, rtx *, enum machine_mode,
|
|||
rtx);
|
||||
static void arm_reorg (void);
|
||||
static bool note_invalid_constants (rtx, HOST_WIDE_INT, int);
|
||||
static int current_file_function_operand (rtx);
|
||||
static unsigned long arm_compute_save_reg0_reg12_mask (void);
|
||||
static unsigned long arm_compute_save_reg_mask (void);
|
||||
static unsigned long arm_isr_value (tree);
|
||||
|
@ -2782,21 +2781,6 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
|
|||
pcum->iwmmxt_nregs = 0;
|
||||
pcum->can_split = true;
|
||||
|
||||
pcum->call_cookie = CALL_NORMAL;
|
||||
|
||||
if (TARGET_LONG_CALLS)
|
||||
pcum->call_cookie = CALL_LONG;
|
||||
|
||||
/* Check for long call/short call attributes. The attributes
|
||||
override any command line option. */
|
||||
if (fntype)
|
||||
{
|
||||
if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (fntype)))
|
||||
pcum->call_cookie = CALL_SHORT;
|
||||
else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (fntype)))
|
||||
pcum->call_cookie = CALL_LONG;
|
||||
}
|
||||
|
||||
/* Varargs vectors are treated the same as long long.
|
||||
named_count avoids having to change the way arm handles 'named' */
|
||||
pcum->named_count = 0;
|
||||
|
@ -2867,8 +2851,8 @@ arm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
|
|||
pcum->nregs++;
|
||||
|
||||
if (mode == VOIDmode)
|
||||
/* Compute operand 2 of the call insn. */
|
||||
return GEN_INT (pcum->call_cookie);
|
||||
/* Pick an arbitrary value for operand 2 of the call insn. */
|
||||
return const0_rtx;
|
||||
|
||||
/* Only allow splitting an arg between regs and memory if all preceding
|
||||
args were allocated to regs. For args passed by reference we only count
|
||||
|
@ -3121,27 +3105,6 @@ arm_comp_type_attributes (tree type1, tree type2)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Encode long_call or short_call attribute by prefixing
|
||||
symbol name in DECL with a special character FLAG. */
|
||||
void
|
||||
arm_encode_call_attribute (tree decl, int flag)
|
||||
{
|
||||
const char * str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
|
||||
int len = strlen (str);
|
||||
char * newstr;
|
||||
|
||||
/* Do not allow weak functions to be treated as short call. */
|
||||
if (DECL_WEAK (decl) && flag == SHORT_CALL_FLAG_CHAR)
|
||||
return;
|
||||
|
||||
newstr = alloca (len + 2);
|
||||
newstr[0] = flag;
|
||||
strcpy (newstr + 1, str);
|
||||
|
||||
newstr = (char *) ggc_alloc_string (newstr, len + 1);
|
||||
XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr;
|
||||
}
|
||||
|
||||
/* Assigns default attributes to newly defined type. This is used to
|
||||
set short_call/long_call attributes for function types of
|
||||
functions defined inside corresponding #pragma scopes. */
|
||||
|
@ -3168,92 +3131,79 @@ arm_set_default_type_attributes (tree type)
|
|||
}
|
||||
}
|
||||
|
||||
/* Return 1 if the operand is a SYMBOL_REF for a function known to be
|
||||
defined within the current compilation unit. If this cannot be
|
||||
determined, then 0 is returned. */
|
||||
static int
|
||||
current_file_function_operand (rtx sym_ref)
|
||||
/* Return true if DECL is known to be linked into section SECTION. */
|
||||
|
||||
static bool
|
||||
arm_function_in_section_p (tree decl, section *section)
|
||||
{
|
||||
/* This is a bit of a fib. A function will have a short call flag
|
||||
applied to its name if it has the short call attribute, or it has
|
||||
already been defined within the current compilation unit. */
|
||||
if (ENCODED_SHORT_CALL_ATTR_P (XSTR (sym_ref, 0)))
|
||||
return 1;
|
||||
/* We can only be certain about functions defined in the same
|
||||
compilation unit. */
|
||||
if (!TREE_STATIC (decl))
|
||||
return false;
|
||||
|
||||
/* The current function is always defined within the current compilation
|
||||
unit. If it s a weak definition however, then this may not be the real
|
||||
definition of the function, and so we have to say no. */
|
||||
if (sym_ref == XEXP (DECL_RTL (current_function_decl), 0)
|
||||
&& !DECL_WEAK (current_function_decl))
|
||||
return 1;
|
||||
/* Make sure that SYMBOL always binds to the definition in this
|
||||
compilation unit. */
|
||||
if (!targetm.binds_local_p (decl))
|
||||
return false;
|
||||
|
||||
/* We cannot make the determination - default to returning 0. */
|
||||
return 0;
|
||||
/* If DECL_SECTION_NAME is set, assume it is trustworthy. */
|
||||
if (!DECL_SECTION_NAME (decl))
|
||||
{
|
||||
/* Only cater for unit-at-a-time mode, where we know that the user
|
||||
cannot later specify a section for DECL. */
|
||||
if (!flag_unit_at_a_time)
|
||||
return false;
|
||||
|
||||
/* Make sure that we will not create a unique section for DECL. */
|
||||
if (flag_function_sections || DECL_ONE_ONLY (decl))
|
||||
return false;
|
||||
}
|
||||
|
||||
return function_section (decl) == section;
|
||||
}
|
||||
|
||||
/* Return nonzero if a 32-bit "long_call" should be generated for
|
||||
this call. We generate a long_call if the function:
|
||||
a call from the current function to DECL. We generate a long_call
|
||||
if the function:
|
||||
|
||||
a. has an __attribute__((long call))
|
||||
or b. is within the scope of a #pragma long_calls
|
||||
or c. the -mlong-calls command line switch has been specified
|
||||
. and either:
|
||||
1. -ffunction-sections is in effect
|
||||
or 2. the current function has __attribute__ ((section))
|
||||
or 3. the target function has __attribute__ ((section))
|
||||
|
||||
However we do not generate a long call if the function:
|
||||
|
||||
d. has an __attribute__ ((short_call))
|
||||
or e. is inside the scope of a #pragma no_long_calls
|
||||
or f. is defined within the current compilation unit.
|
||||
or f. is defined in the same section as the current function. */
|
||||
|
||||
This function will be called by C fragments contained in the machine
|
||||
description file. SYM_REF and CALL_COOKIE correspond to the matched
|
||||
rtl operands. CALL_SYMBOL is used to distinguish between
|
||||
two different callers of the function. It is set to 1 in the
|
||||
"call_symbol" and "call_symbol_value" patterns and to 0 in the "call"
|
||||
and "call_value" patterns. This is because of the difference in the
|
||||
SYM_REFs passed by these patterns. */
|
||||
int
|
||||
arm_is_longcall_p (rtx sym_ref, int call_cookie, int call_symbol)
|
||||
bool
|
||||
arm_is_long_call_p (tree decl)
|
||||
{
|
||||
if (!call_symbol)
|
||||
{
|
||||
if (GET_CODE (sym_ref) != MEM)
|
||||
return 0;
|
||||
tree attrs;
|
||||
|
||||
sym_ref = XEXP (sym_ref, 0);
|
||||
}
|
||||
if (!decl)
|
||||
return TARGET_LONG_CALLS;
|
||||
|
||||
if (GET_CODE (sym_ref) != SYMBOL_REF)
|
||||
return 0;
|
||||
attrs = TYPE_ATTRIBUTES (TREE_TYPE (decl));
|
||||
if (lookup_attribute ("short_call", attrs))
|
||||
return false;
|
||||
|
||||
if (call_cookie & CALL_SHORT)
|
||||
return 0;
|
||||
/* For "f", be conservative, and only cater for cases in which the
|
||||
whole of the current function is placed in the same section. */
|
||||
if (!flag_reorder_blocks_and_partition
|
||||
&& arm_function_in_section_p (decl, current_function_section ()))
|
||||
return false;
|
||||
|
||||
if (TARGET_LONG_CALLS)
|
||||
{
|
||||
if (flag_function_sections
|
||||
|| DECL_SECTION_NAME (current_function_decl))
|
||||
/* c.3 is handled by the definition of the
|
||||
ARM_DECLARE_FUNCTION_SIZE macro. */
|
||||
return 1;
|
||||
}
|
||||
if (lookup_attribute ("long_call", attrs))
|
||||
return true;
|
||||
|
||||
if (current_file_function_operand (sym_ref))
|
||||
return 0;
|
||||
|
||||
return (call_cookie & CALL_LONG)
|
||||
|| ENCODED_LONG_CALL_ATTR_P (XSTR (sym_ref, 0))
|
||||
|| TARGET_LONG_CALLS;
|
||||
return TARGET_LONG_CALLS;
|
||||
}
|
||||
|
||||
/* Return nonzero if it is ok to make a tail-call to DECL. */
|
||||
static bool
|
||||
arm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int call_type = TARGET_LONG_CALLS ? CALL_LONG : CALL_NORMAL;
|
||||
unsigned long func_type;
|
||||
|
||||
if (cfun->machine->sibcall_blocked)
|
||||
|
@ -3264,16 +3214,9 @@ arm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
|
|||
if (decl == NULL || TARGET_THUMB)
|
||||
return false;
|
||||
|
||||
/* Get the calling method. */
|
||||
if (lookup_attribute ("short_call", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
|
||||
call_type = CALL_SHORT;
|
||||
else if (lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
|
||||
call_type = CALL_LONG;
|
||||
|
||||
/* Cannot tail-call to long calls, since these are out of range of
|
||||
a branch instruction. However, if not compiling PIC, we know
|
||||
we can reach the symbol if it is in this compilation unit. */
|
||||
if (call_type == CALL_LONG && (flag_pic || !TREE_ASM_WRITTEN (decl)))
|
||||
a branch instruction. */
|
||||
if (arm_is_long_call_p (decl))
|
||||
return false;
|
||||
|
||||
/* If we are interworking and the function is not declared static
|
||||
|
@ -15603,17 +15546,6 @@ arm_encode_section_info (tree decl, rtx rtl, int first)
|
|||
SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
|
||||
#endif
|
||||
|
||||
/* If we are referencing a function that is weak then encode a long call
|
||||
flag in the function name, otherwise if the function is static or
|
||||
or known to be defined in this file then encode a short call flag. */
|
||||
if (first && DECL_P (decl))
|
||||
{
|
||||
if (TREE_CODE (decl) == FUNCTION_DECL && DECL_WEAK (decl))
|
||||
arm_encode_call_attribute (decl, LONG_CALL_FLAG_CHAR);
|
||||
else if (! TREE_PUBLIC (decl))
|
||||
arm_encode_call_attribute (decl, SHORT_CALL_FLAG_CHAR);
|
||||
}
|
||||
|
||||
default_encode_section_info (decl, rtl, first);
|
||||
}
|
||||
#endif /* !ARM_PE */
|
||||
|
|
|
@ -1368,11 +1368,6 @@ do { \
|
|||
than a word, or if they contain elements offset from zero in the struct. */
|
||||
#define DEFAULT_PCC_STRUCT_RETURN 0
|
||||
|
||||
/* Flags for the call/call_value rtl operations set up by function_arg. */
|
||||
#define CALL_NORMAL 0x00000000 /* No special processing. */
|
||||
#define CALL_LONG 0x00000001 /* Always call indirect. */
|
||||
#define CALL_SHORT 0x00000002 /* Never call indirect. */
|
||||
|
||||
/* These bits describe the different types of function supported
|
||||
by the ARM backend. They are exclusive. i.e. a function cannot be both a
|
||||
normal function and an interworked function, for example. Knowing the
|
||||
|
@ -1471,8 +1466,6 @@ typedef struct
|
|||
int iwmmxt_nregs;
|
||||
int named_count;
|
||||
int nargs;
|
||||
/* One of CALL_NORMAL, CALL_LONG or CALL_SHORT. */
|
||||
int call_cookie;
|
||||
int can_split;
|
||||
} CUMULATIVE_ARGS;
|
||||
|
||||
|
@ -1853,18 +1846,6 @@ typedef struct
|
|||
&& (TARGET_32BIT ? ARM_LEGITIMATE_CONSTANT_P (X) \
|
||||
: THUMB_LEGITIMATE_CONSTANT_P (X)))
|
||||
|
||||
/* Special characters prefixed to function names
|
||||
in order to encode attribute like information.
|
||||
Note, '@' and '*' have already been taken. */
|
||||
#define SHORT_CALL_FLAG_CHAR '^'
|
||||
#define LONG_CALL_FLAG_CHAR '#'
|
||||
|
||||
#define ENCODED_SHORT_CALL_ATTR_P(SYMBOL_NAME) \
|
||||
(*(SYMBOL_NAME) == SHORT_CALL_FLAG_CHAR)
|
||||
|
||||
#define ENCODED_LONG_CALL_ATTR_P(SYMBOL_NAME) \
|
||||
(*(SYMBOL_NAME) == LONG_CALL_FLAG_CHAR)
|
||||
|
||||
#ifndef SUBTARGET_NAME_ENCODING_LENGTHS
|
||||
#define SUBTARGET_NAME_ENCODING_LENGTHS
|
||||
#endif
|
||||
|
@ -1874,8 +1855,6 @@ typedef struct
|
|||
be stripped from the start of a function's name, if that
|
||||
name starts with the indicated character. */
|
||||
#define ARM_NAME_ENCODING_LENGTHS \
|
||||
case SHORT_CALL_FLAG_CHAR: return 1; \
|
||||
case LONG_CALL_FLAG_CHAR: return 1; \
|
||||
case '*': return 1; \
|
||||
SUBTARGET_NAME_ENCODING_LENGTHS
|
||||
|
||||
|
@ -1942,15 +1921,6 @@ typedef struct
|
|||
#define TARGET_ARM_DYNAMIC_VAGUE_LINKAGE_P true
|
||||
#endif
|
||||
|
||||
/* Set the short-call flag for any function compiled in the current
|
||||
compilation unit. We skip this for functions with the section
|
||||
attribute when long-calls are in effect as this tells the compiler
|
||||
that the section might be placed a long way from the caller.
|
||||
See arm_is_longcall_p() for more information. */
|
||||
#define ARM_DECLARE_FUNCTION_SIZE(STREAM, NAME, DECL) \
|
||||
if (!TARGET_LONG_CALLS || ! DECL_SECTION_NAME (DECL)) \
|
||||
arm_encode_call_attribute (DECL, SHORT_CALL_FLAG_CHAR)
|
||||
|
||||
#define ARM_OUTPUT_FN_UNWIND(F, PROLOGUE) arm_output_fn_unwind (F, PROLOGUE)
|
||||
|
||||
#ifdef TARGET_UNWIND_INFO
|
||||
|
|
|
@ -8151,22 +8151,13 @@
|
|||
if (operands[2] == NULL_RTX)
|
||||
operands[2] = const0_rtx;
|
||||
|
||||
/* This is to decide if we should generate indirect calls by loading the
|
||||
/* Decide if we should generate indirect calls by loading the
|
||||
32-bit address of the callee into a register before performing the
|
||||
branch and link. operand[2] encodes the long_call/short_call
|
||||
attribute of the function being called. This attribute is set whenever
|
||||
__attribute__((long_call/short_call)) or #pragma long_call/no_long_call
|
||||
is used, and the short_call attribute can also be set if function is
|
||||
declared as static or if it has already been defined in the current
|
||||
compilation unit. See arm.c and arm.h for info about this. The third
|
||||
parameter to arm_is_longcall_p is used to tell it which pattern
|
||||
invoked it. */
|
||||
callee = XEXP (operands[0], 0);
|
||||
|
||||
if ((GET_CODE (callee) == SYMBOL_REF
|
||||
&& arm_is_longcall_p (operands[0], INTVAL (operands[2]), 0))
|
||||
|| (GET_CODE (callee) != SYMBOL_REF
|
||||
&& GET_CODE (callee) != REG))
|
||||
branch and link. */
|
||||
callee = XEXP (operands[0], 0);
|
||||
if (GET_CODE (callee) == SYMBOL_REF
|
||||
? arm_is_long_call_p (SYMBOL_REF_DECL (callee))
|
||||
: !REG_P (callee))
|
||||
XEXP (operands[0], 0) = force_reg (Pmode, callee);
|
||||
}"
|
||||
)
|
||||
|
@ -8248,17 +8239,19 @@
|
|||
"TARGET_EITHER"
|
||||
"
|
||||
{
|
||||
rtx callee = XEXP (operands[1], 0);
|
||||
rtx callee;
|
||||
|
||||
/* In an untyped call, we can get NULL for operand 2. */
|
||||
if (operands[3] == 0)
|
||||
operands[3] = const0_rtx;
|
||||
|
||||
/* See the comment in define_expand \"call\". */
|
||||
if ((GET_CODE (callee) == SYMBOL_REF
|
||||
&& arm_is_longcall_p (operands[1], INTVAL (operands[3]), 0))
|
||||
|| (GET_CODE (callee) != SYMBOL_REF
|
||||
&& GET_CODE (callee) != REG))
|
||||
/* Decide if we should generate indirect calls by loading the
|
||||
32-bit address of the callee into a register before performing the
|
||||
branch and link. */
|
||||
callee = XEXP (operands[1], 0);
|
||||
if (GET_CODE (callee) == SYMBOL_REF
|
||||
? arm_is_long_call_p (SYMBOL_REF_DECL (callee))
|
||||
: !REG_P (callee))
|
||||
XEXP (operands[1], 0) = force_reg (Pmode, callee);
|
||||
}"
|
||||
)
|
||||
|
@ -8345,7 +8338,7 @@
|
|||
(clobber (reg:SI LR_REGNUM))]
|
||||
"TARGET_ARM
|
||||
&& (GET_CODE (operands[0]) == SYMBOL_REF)
|
||||
&& !arm_is_longcall_p (operands[0], INTVAL (operands[2]), 1)"
|
||||
&& !arm_is_long_call_p (SYMBOL_REF_DECL (operands[0]))"
|
||||
"*
|
||||
{
|
||||
return NEED_PLT_RELOC ? \"bl%?\\t%a0(PLT)\" : \"bl%?\\t%a0\";
|
||||
|
@ -8361,7 +8354,7 @@
|
|||
(clobber (reg:SI LR_REGNUM))]
|
||||
"TARGET_ARM
|
||||
&& (GET_CODE (operands[1]) == SYMBOL_REF)
|
||||
&& !arm_is_longcall_p (operands[1], INTVAL (operands[3]), 1)"
|
||||
&& !arm_is_long_call_p (SYMBOL_REF_DECL (operands[1]))"
|
||||
"*
|
||||
{
|
||||
return NEED_PLT_RELOC ? \"bl%?\\t%a1(PLT)\" : \"bl%?\\t%a1\";
|
||||
|
@ -8376,7 +8369,7 @@
|
|||
(clobber (reg:SI LR_REGNUM))]
|
||||
"TARGET_THUMB
|
||||
&& GET_CODE (operands[0]) == SYMBOL_REF
|
||||
&& !arm_is_longcall_p (operands[0], INTVAL (operands[2]), 1)"
|
||||
&& !arm_is_long_call_p (SYMBOL_REF_DECL (operands[0]))"
|
||||
"bl\\t%a0"
|
||||
[(set_attr "length" "4")
|
||||
(set_attr "type" "call")]
|
||||
|
@ -8390,7 +8383,7 @@
|
|||
(clobber (reg:SI LR_REGNUM))]
|
||||
"TARGET_THUMB
|
||||
&& GET_CODE (operands[1]) == SYMBOL_REF
|
||||
&& !arm_is_longcall_p (operands[1], INTVAL (operands[3]), 1)"
|
||||
&& !arm_is_long_call_p (SYMBOL_REF_DECL (operands[1]))"
|
||||
"bl\\t%a1"
|
||||
[(set_attr "length" "4")
|
||||
(set_attr "type" "call")]
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
do \
|
||||
{ \
|
||||
ARM_OUTPUT_FN_UNWIND (FILE, FALSE); \
|
||||
ARM_DECLARE_FUNCTION_SIZE (FILE, FNAME, DECL); \
|
||||
if (!flag_inhibit_size_directive) \
|
||||
ASM_OUTPUT_MEASURED_SIZE (FILE, FNAME); \
|
||||
} \
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
2007-05-25 Richard Sandiford <richard@codesourcery.com>
|
||||
|
||||
* gcc.target/arm/long-calls-1.c: New test.
|
||||
* gcc.target/arm/long-calls-2.c: Likewise.
|
||||
* gcc.target/arm/long-calls-3.c: Likewise.
|
||||
* gcc.target/arm/long-calls-4.c: Likewise.
|
||||
|
||||
2007-05-25 Richard Guenther <rguenther@suse.de>
|
||||
Andrew Pinski <andrew_pinski@playstation.sony.com>
|
||||
|
||||
|
|
126
gcc/testsuite/gcc.target/arm/long-calls-1.c
Normal file
126
gcc/testsuite/gcc.target/arm/long-calls-1.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
/* Check that long calls to different sections are not optimized to "bl". */
|
||||
/* { dg-do compile { target { arm32 && nonpic } } } */
|
||||
/* { dg-options "-O2" } */
|
||||
|
||||
#define section(S) __attribute__((section(S)))
|
||||
#define weak __attribute__((weak))
|
||||
#define noinline __attribute__((noinline))
|
||||
#define long_call __attribute__((long_call))
|
||||
#define short_call __attribute__((short_call))
|
||||
|
||||
#define REMOTE_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
const char *TARGET_ATTRS ID (void); \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; }
|
||||
|
||||
#define EXTERN_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
const char *TARGET_ATTRS noinline ID (void) { return #ID; } \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; } \
|
||||
const char *CALL_ATTRS sibcall_##ID (void) { return ID (); }
|
||||
|
||||
#define STATIC_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
static const char *TARGET_ATTRS noinline ID (void) { return #ID; } \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; } \
|
||||
const char *CALL_ATTRS sibcall_##ID (void) { return ID (); }
|
||||
|
||||
#define DO_TESTS_SECTION(ID, TEST, TARGET_ATTRS) \
|
||||
TEST (ID##1, TARGET_ATTRS, ) \
|
||||
TEST (ID##2, TARGET_ATTRS section (".test.a"), section (".test.b")) \
|
||||
TEST (ID##3, TARGET_ATTRS section (".test.c"), section (".test.c"))
|
||||
|
||||
#define DO_TESTS_CALL_ATTR(ID, TEST, TARGET_ATTRS) \
|
||||
DO_TESTS_SECTION (ID##n, TEST, TARGET_ATTRS) \
|
||||
DO_TESTS_SECTION (ID##l, TEST, TARGET_ATTRS long_call) \
|
||||
DO_TESTS_SECTION (ID##s, TEST, TARGET_ATTRS short_call)
|
||||
|
||||
DO_TESTS_CALL_ATTR (remote_, REMOTE_CALL,)
|
||||
DO_TESTS_CALL_ATTR (strong_, EXTERN_CALL,)
|
||||
DO_TESTS_CALL_ATTR (weak_, EXTERN_CALL, weak)
|
||||
DO_TESTS_CALL_ATTR (static_, STATIC_CALL,)
|
||||
|
||||
|
||||
/* Calls to remote_* should honor the call type sttribute,
|
||||
with "short" being the default. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_n3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l2\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s3\n" } } */
|
||||
|
||||
|
||||
/* Calls to strong_*2 calls should honor the call type attribute,
|
||||
with "short" being the default. Calls to other strong_* functions
|
||||
should be short. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_n3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_n3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_l1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_l1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_l2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_l3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_l3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s3\n" } } */
|
||||
|
||||
|
||||
/* Calls to weak_* should honor the call type sttribute,
|
||||
with "short" being the default. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_n3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_n3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l2\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s3\n" } } */
|
||||
|
||||
|
||||
/* Calls to static_*2 calls should honor the call type attribute,
|
||||
with "short" being the default. Calls to other static_* functions
|
||||
should be short. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_l1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_l1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstatic_l2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_l3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_l3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s3\n" } } */
|
121
gcc/testsuite/gcc.target/arm/long-calls-2.c
Normal file
121
gcc/testsuite/gcc.target/arm/long-calls-2.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
/* Check that long calls to different sections are not optimized to "bl". */
|
||||
/* { dg-do compile { target { arm32 && nonpic } } } */
|
||||
/* { dg-options "-O2 -mlong-calls" } */
|
||||
|
||||
#define section(S) __attribute__((section(S)))
|
||||
#define weak __attribute__((weak))
|
||||
#define noinline __attribute__((noinline))
|
||||
#define long_call __attribute__((long_call))
|
||||
#define short_call __attribute__((short_call))
|
||||
|
||||
#define REMOTE_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
const char *TARGET_ATTRS ID (void); \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; }
|
||||
|
||||
#define EXTERN_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
const char *TARGET_ATTRS noinline ID (void) { return #ID; } \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; } \
|
||||
const char *CALL_ATTRS sibcall_##ID (void) { return ID (); }
|
||||
|
||||
#define STATIC_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
static const char *TARGET_ATTRS noinline ID (void) { return #ID; } \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; } \
|
||||
const char *CALL_ATTRS sibcall_##ID (void) { return ID (); }
|
||||
|
||||
#define DO_TESTS_SECTION(ID, TEST, TARGET_ATTRS) \
|
||||
TEST (ID##1, TARGET_ATTRS, ) \
|
||||
TEST (ID##2, TARGET_ATTRS section (".test.a"), section (".test.b")) \
|
||||
TEST (ID##3, TARGET_ATTRS section (".test.c"), section (".test.c"))
|
||||
|
||||
#define DO_TESTS_CALL_ATTR(ID, TEST, TARGET_ATTRS) \
|
||||
DO_TESTS_SECTION (ID##n, TEST, TARGET_ATTRS) \
|
||||
DO_TESTS_SECTION (ID##l, TEST, TARGET_ATTRS long_call) \
|
||||
DO_TESTS_SECTION (ID##s, TEST, TARGET_ATTRS short_call)
|
||||
|
||||
DO_TESTS_CALL_ATTR (remote_, REMOTE_CALL,)
|
||||
DO_TESTS_CALL_ATTR (strong_, EXTERN_CALL,)
|
||||
DO_TESTS_CALL_ATTR (weak_, EXTERN_CALL, weak)
|
||||
DO_TESTS_CALL_ATTR (static_, STATIC_CALL,)
|
||||
|
||||
|
||||
/* Calls to remote_* should honor the call type sttribute,
|
||||
with "long" being the default. */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_n1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_n2\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_n3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l2\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s3\n" } } */
|
||||
|
||||
|
||||
/* Calls to strong_*2 calls should honor the call type attribute,
|
||||
with "long" being the default. Calls to other strong_* functions
|
||||
should be short. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_n1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tstrong_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_n3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_n3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_l1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_l1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_l2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_l3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_l3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s3\n" } } */
|
||||
|
||||
|
||||
/* Calls to weak_* should honor the call type sttribute,
|
||||
with "long" being the default. */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_n1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_n2\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_n3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l2\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s3\n" } } */
|
||||
|
||||
|
||||
/* Calls to static_*2 calls should honor the call type attribute,
|
||||
with "long" being the default. Calls to other static_* functions
|
||||
should be short. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstatic_n2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_l1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_l1\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstatic_l2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_l3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_l3\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s1\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s2\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s3\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s3\n" } } */
|
117
gcc/testsuite/gcc.target/arm/long-calls-3.c
Normal file
117
gcc/testsuite/gcc.target/arm/long-calls-3.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/* Check that long calls to different sections are not optimized to "bl". */
|
||||
/* { dg-do compile { target { arm32 && fpic } } } */
|
||||
/* { dg-options "-O2 -fpic" } */
|
||||
|
||||
#define section(S) __attribute__((section(S)))
|
||||
#define weak __attribute__((weak))
|
||||
#define noinline __attribute__((noinline))
|
||||
#define long_call __attribute__((long_call))
|
||||
#define short_call __attribute__((short_call))
|
||||
|
||||
#define REMOTE_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
const char *TARGET_ATTRS ID (void); \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; }
|
||||
|
||||
#define EXTERN_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
const char *TARGET_ATTRS noinline ID (void) { return #ID; } \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; } \
|
||||
const char *CALL_ATTRS sibcall_##ID (void) { return ID (); }
|
||||
|
||||
#define STATIC_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
static const char *TARGET_ATTRS noinline ID (void) { return #ID; } \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; } \
|
||||
const char *CALL_ATTRS sibcall_##ID (void) { return ID (); }
|
||||
|
||||
#define DO_TESTS_SECTION(ID, TEST, TARGET_ATTRS) \
|
||||
TEST (ID##1, TARGET_ATTRS, ) \
|
||||
TEST (ID##2, TARGET_ATTRS section (".test.a"), section (".test.b")) \
|
||||
TEST (ID##3, TARGET_ATTRS section (".test.c"), section (".test.c"))
|
||||
|
||||
#define DO_TESTS_CALL_ATTR(ID, TEST, TARGET_ATTRS) \
|
||||
DO_TESTS_SECTION (ID##n, TEST, TARGET_ATTRS) \
|
||||
DO_TESTS_SECTION (ID##l, TEST, TARGET_ATTRS long_call) \
|
||||
DO_TESTS_SECTION (ID##s, TEST, TARGET_ATTRS short_call)
|
||||
|
||||
DO_TESTS_CALL_ATTR (remote_, REMOTE_CALL,)
|
||||
DO_TESTS_CALL_ATTR (strong_, EXTERN_CALL,)
|
||||
DO_TESTS_CALL_ATTR (weak_, EXTERN_CALL, weak)
|
||||
DO_TESTS_CALL_ATTR (static_, STATIC_CALL,)
|
||||
|
||||
|
||||
/* Calls to remote_*, strong_* and weak_* should honor the call type
|
||||
sttribute, with "short" being the default. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_n1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_n2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_n3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s3\\(PLT\\)\n" } } */
|
||||
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_n1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_n1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_n2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_n2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_n3\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_n3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl\tstrong_l1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_l2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tstrong_l3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s3\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s3\\(PLT\\)\n" } } */
|
||||
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_n1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_n1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_n2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_n2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_n3\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_n3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s3\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s3\\(PLT\\)\n" } } */
|
||||
|
||||
|
||||
/* Calls to static_*2 calls should honor the call type attribute,
|
||||
with "short" being the default. Calls to other static_* functions
|
||||
should be short. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n3(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n3(\\(PLT\\))\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_l1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_l1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstatic_l2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_l3(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_l3(\\(PLT\\))\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s3(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s3(\\(PLT\\))\n" } } */
|
110
gcc/testsuite/gcc.target/arm/long-calls-4.c
Normal file
110
gcc/testsuite/gcc.target/arm/long-calls-4.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/* Check that long calls to different sections are not optimized to "bl". */
|
||||
/* { dg-do compile { target { arm32 && fpic } } } */
|
||||
/* { dg-options "-O2 -fpic -mlong-calls" } */
|
||||
|
||||
#define section(S) __attribute__((section(S)))
|
||||
#define weak __attribute__((weak))
|
||||
#define noinline __attribute__((noinline))
|
||||
#define long_call __attribute__((long_call))
|
||||
#define short_call __attribute__((short_call))
|
||||
|
||||
#define REMOTE_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
const char *TARGET_ATTRS ID (void); \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; }
|
||||
|
||||
#define EXTERN_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
const char *TARGET_ATTRS noinline ID (void) { return #ID; } \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; } \
|
||||
const char *CALL_ATTRS sibcall_##ID (void) { return ID (); }
|
||||
|
||||
#define STATIC_CALL(ID, TARGET_ATTRS, CALL_ATTRS) \
|
||||
static const char *TARGET_ATTRS noinline ID (void) { return #ID; } \
|
||||
const char *CALL_ATTRS call_##ID (void) { return ID () + 1; } \
|
||||
const char *CALL_ATTRS sibcall_##ID (void) { return ID (); }
|
||||
|
||||
#define DO_TESTS_SECTION(ID, TEST, TARGET_ATTRS) \
|
||||
TEST (ID##1, TARGET_ATTRS, ) \
|
||||
TEST (ID##2, TARGET_ATTRS section (".test.a"), section (".test.b")) \
|
||||
TEST (ID##3, TARGET_ATTRS section (".test.c"), section (".test.c"))
|
||||
|
||||
#define DO_TESTS_CALL_ATTR(ID, TEST, TARGET_ATTRS) \
|
||||
DO_TESTS_SECTION (ID##n, TEST, TARGET_ATTRS) \
|
||||
DO_TESTS_SECTION (ID##l, TEST, TARGET_ATTRS long_call) \
|
||||
DO_TESTS_SECTION (ID##s, TEST, TARGET_ATTRS short_call)
|
||||
|
||||
DO_TESTS_CALL_ATTR (remote_, REMOTE_CALL,)
|
||||
DO_TESTS_CALL_ATTR (strong_, EXTERN_CALL,)
|
||||
DO_TESTS_CALL_ATTR (weak_, EXTERN_CALL, weak)
|
||||
DO_TESTS_CALL_ATTR (static_, STATIC_CALL,)
|
||||
|
||||
|
||||
/* Calls to remote_*, strong_* and weak_* should honor the call type
|
||||
sttribute, with "long" being the default. */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_n1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_n2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_n3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl\tremote_l3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tremote_s3\\(PLT\\)\n" } } */
|
||||
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_n1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_n2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_n3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_l1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_l2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstrong_l3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstrong_s3\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstrong_s3\\(PLT\\)\n" } } */
|
||||
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_n1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_n2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_n3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tweak_l3\\(PLT\\)\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s1\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s2\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tweak_s3\\(PLT\\)\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tweak_s3\\(PLT\\)\n" } } */
|
||||
|
||||
|
||||
/* Calls to static_*2 calls should honor the call type attribute,
|
||||
with "long" being the default. Calls to other static_* functions
|
||||
should be short. */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstatic_n2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_n3(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_n3(\\(PLT\\))\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_l1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_l1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler-not "\tbl?\tstatic_l2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_l3(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_l3(\\(PLT\\))\n" } } */
|
||||
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s1(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s2(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tbl\tstatic_s3(\\(PLT\\))\n" } } */
|
||||
/* { dg-final { scan-assembler "\tb\tstatic_s3(\\(PLT\\))\n" } } */
|
Loading…
Add table
Reference in a new issue