> + c_register_pragma (0, "long_calls_off", aarch64_pr_long_calls_off); > \ > +} while (0) > + > #define FUNCTION_ARG_PADDING(MODE, TYPE) \ > (aarch64_pad_arg_upward (MODE, TYPE) ? upward : downward)
Hi, I added four testcases to ensure that this patch tested properly. Reg-tested with aarch64-linux-gnu-gcc on qemu. OK for the trunk? Index: gcc/ChangeLog =================================================================== --- gcc/ChangeLog (revision 216558) +++ gcc/ChangeLog (working copy) @@ -1,3 +1,26 @@ +2014-10-28 Felix Yang <felix.y...@huawei.com> + Haijian Zhang <z.zhanghaij...@huawei.com> + + * config/aarch64/aarch64.opt (mlong-calls): New option. + * config/aarch64/aarch64.h (REGISTER_TARGET_PRAGMAS): Define. + * config/aarch64/aarch64.c (aarch64_set_default_type_attributes, + aarch64_attribute_table, aarch64_comp_type_attributes, + aarch64_decl_is_long_call_p, aarch64_function_in_section_p, + aarch64_pr_long_calls, aarch64_pr_no_long_calls, + aarch64_pr_long_calls_off): New functions. + (TARGET_SET_DEFAULT_TYPE_ATTRIBUTES): Define as + aarch64_set_default_type_attributes. + (TARGET_ATTRIBUTE_TABLE): Define as aarch64_attribute_table. + (TARGET_COMP_TYPE_ATTRIBUTES): Define as aarch64_comp_type_attribute. + (aarch64_pragma_enum): New enum. + (aarch64_attribute_table): New attribute table. + * config/aarch64/aarch64-protos.h (aarch64_pr_long_calls, + aarch64_pr_no_long_calls, aarch64_pr_long_calls_off): New declarations. + * config/aarch64/aarch64.md (sibcall, sibcall_value): Modified to + generate indirect call for sibling call when needed. + * config/aarch64/predicate.md (aarch64_call_insn_operand): Modified to + exclude a symbol_ref for an indirect call. + 2014-10-22 Richard Sandiford <richard.sandif...@arm.com> * lra.c (lra): Remove call to recog_init. Index: gcc/testsuite/gcc.target/aarch64/long-calls-1.c =================================================================== --- gcc/testsuite/gcc.target/aarch64/long-calls-1.c (revision 0) +++ gcc/testsuite/gcc.target/aarch64/long-calls-1.c (revision 0) @@ -0,0 +1,133 @@ +/* Check that long calls to different sections are not optimized to "bl". */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +/* This test expects that short calls are the default. */ +/* { dg-skip-if "-mlong-calls in use" { "*-*-*" } { "-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 attribute, + with "short" being the default. + + In the regular expressions below: + + * We allow both "b" and "bl" in some cases to allow for the + possibility of sibling calls. */ + +/* { 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 "\tbl?\tstrong_n1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_n2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_n2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_n3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_n3\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstrong_l1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_l1\n" } } */ +/* { dg-final { scan-assembler-not "\tbl?\tstrong_l2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_l3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_l3\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstrong_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_s3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s3\n" } } */ + + +/* Calls to weak_* should honor the call type attribute, + with "short" being the default. */ + +/* { dg-final { scan-assembler "\tbl\tweak_n1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tweak_n1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_n2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tweak_n2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_n3\n" } } */ +/* { dg-final { scan-assembler "\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 "\tbl?\tweak_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tweak_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_s3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\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 "\tbl?\tstatic_n1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_n2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_n2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_n3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_n3\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstatic_l1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_l1\n" } } */ +/* { dg-final { scan-assembler-not "\tbl?\tstatic_l2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_l3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_l3\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstatic_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_s3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s3\n" } } */ Index: gcc/testsuite/gcc.target/aarch64/long-calls-2.c =================================================================== --- gcc/testsuite/gcc.target/aarch64/long-calls-2.c (revision 0) +++ gcc/testsuite/gcc.target/aarch64/long-calls-2.c (revision 0) @@ -0,0 +1,126 @@ +/* Check that long calls to different sections are not optimized to "bl". */ +/* { dg-do compile } */ +/* { 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 attribute, + with "long" being the default. + + In the regular expressions below: + + * We allow both "b" and "bl" in some cases to allow for the + possibility of sibling calls. */ + +/* { 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 "\tbl?\tstrong_n1\n" } } */ +/* { dg-final { scan-assembler-not "\tbl\tstrong_n2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_n3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_n3\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstrong_l1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_l1\n" } } */ +/* { dg-final { scan-assembler-not "\tbl?\tstrong_l2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_l3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_l3\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstrong_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_s3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s3\n" } } */ + + +/* Calls to weak_* should honor the call type attribute, + 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 "\tbl?\tweak_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tweak_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_s3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\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 "\tbl?\tstatic_n1\n" } } */ +/* { dg-final { scan-assembler-not "\tbl?\tstatic_n2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_n3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_n3\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstatic_l1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_l1\n" } } */ +/* { dg-final { scan-assembler-not "\tbl?\tstatic_l2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_l3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_l3\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstatic_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s1\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s2\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_s3\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s3\n" } } */ Index: gcc/testsuite/gcc.target/aarch64/long-calls-3.c =================================================================== --- gcc/testsuite/gcc.target/aarch64/long-calls-3.c (revision 0) +++ gcc/testsuite/gcc.target/aarch64/long-calls-3.c (revision 0) @@ -0,0 +1,125 @@ +/* Check that long calls to different sections are not optimized to "bl". */ +/* { dg-do compile } */ +/* { 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 + attribute, with "short" being the default. + + In the regular expressions below: + + * The PLT marker is optional, even though we are using -fpic, + because it is not used (or required) on some targets. + + * We allow both "b" and "bl" in some cases to allow for the + possibility of sibling calls. */ + +/* { 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 "\tbl?\tstrong_n1(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_n2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_n2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_n3(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\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 "\tbl?\tstrong_s1(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_s2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_s3(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s3(\\(PLT\\))?\n" } } */ + + +/* { dg-final { scan-assembler "\tbl\tweak_n1(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tweak_n1(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_n2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tweak_n2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_n3(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\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 "\tbl?\tweak_s1(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_s2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tweak_s2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_s3(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\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 "\tbl?\tstatic_n1((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_n2((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_n2((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_n3((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_n3((\\(PLT\\))?)\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstatic_l1((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\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 "\tbl?\tstatic_l3((\\(PLT\\))?)\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstatic_s1((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s1((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_s2((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s2((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_s3((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s3((\\(PLT\\))?)\n" } } */ Index: gcc/testsuite/gcc.target/aarch64/long-calls-4.c =================================================================== --- gcc/testsuite/gcc.target/aarch64/long-calls-4.c (revision 0) +++ gcc/testsuite/gcc.target/aarch64/long-calls-4.c (revision 0) @@ -0,0 +1,118 @@ +/* Check that long calls to different sections are not optimized to "bl". */ +/* { dg-do compile } */ +/* { 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 + attribute, with "long" being the default. + + In the regular expressions below: + + * The PLT marker is optional, even though we are using -fpic, + because it is not used (or required) on some targets. + + * We allow both "b" and "bl" in some cases to allow for the + possibility of sibling calls. */ + +/* { 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 "\tbl?\tstrong_s1(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_s2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstrong_s2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstrong_s3(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\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 "\tbl?\tweak_s1(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_s2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tweak_s2(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl\tweak_s3(\\(PLT\\))?\n" } } */ +/* { dg-final { scan-assembler "\tbl?\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 "\tbl?\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 "\tbl?\tstatic_n3((\\(PLT\\))?)\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstatic_l1((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\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 "\tbl?\tstatic_l3((\\(PLT\\))?)\n" } } */ + +/* { dg-final { scan-assembler "\tbl\tstatic_s1((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s1((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_s2((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s2((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl\tstatic_s3((\\(PLT\\))?)\n" } } */ +/* { dg-final { scan-assembler "\tbl?\tstatic_s3((\\(PLT\\))?)\n" } } */ Index: gcc/testsuite/ChangeLog =================================================================== --- gcc/testsuite/ChangeLog (revision 216558) +++ gcc/testsuite/ChangeLog (working copy) @@ -1,3 +1,11 @@ +2014-10-28 Felix Yang <felix.y...@huawei.com> + Haijian Zhang <z.zhanghaij...@huawei.com> + + * gcc.target/aarch64/long-calls-1.c: New test. + * gcc.target/aarch64/long-calls-2.c: Likewise. + * gcc.target/aarch64/long-calls-3.c: Likewise. + * gcc.target/aarch64/long-calls-4.c: Likewise. + 2014-10-22 Richard Sandiford <richard.sandif...@arm.com> * gcc.target/i386/conversion-2.c: New test. Index: gcc/config/aarch64/predicates.md =================================================================== --- gcc/config/aarch64/predicates.md (revision 216558) +++ gcc/config/aarch64/predicates.md (working copy) @@ -27,7 +27,8 @@ ) (define_predicate "aarch64_call_insn_operand" - (ior (match_code "symbol_ref") + (ior (and (match_code "symbol_ref") + (match_test "!aarch64_is_long_call_p (op)")) (match_operand 0 "register_operand"))) (define_predicate "aarch64_simd_register" Index: gcc/config/aarch64/aarch64.md =================================================================== --- gcc/config/aarch64/aarch64.md (revision 216558) +++ gcc/config/aarch64/aarch64.md (working copy) @@ -581,11 +581,13 @@ (use (match_operand 2 "" ""))])] "" { - rtx pat; + rtx callee, pat; - if (!REG_P (XEXP (operands[0], 0)) - && (GET_CODE (XEXP (operands[0], 0)) != SYMBOL_REF)) - XEXP (operands[0], 0) = force_reg (Pmode, XEXP (operands[0], 0)); + callee = XEXP (operands[0], 0); + if (GET_CODE (callee) == SYMBOL_REF + ? aarch64_is_long_call_p (callee) + : !REG_P (callee)) + XEXP (operands[0], 0) = force_reg (Pmode, callee); if (operands[2] == NULL_RTX) operands[2] = const0_rtx; @@ -611,11 +613,13 @@ (use (match_operand 3 "" ""))])] "" { - rtx pat; + rtx callee, pat; - if (!REG_P (XEXP (operands[1], 0)) - && (GET_CODE (XEXP (operands[1], 0)) != SYMBOL_REF)) - XEXP (operands[1], 0) = force_reg (Pmode, XEXP (operands[1], 0)); + callee = XEXP (operands[1], 0); + if (GET_CODE (callee) == SYMBOL_REF + ? aarch64_is_long_call_p (callee) + : !REG_P (callee)) + XEXP (operands[1], 0) = force_reg (Pmode, callee); if (operands[3] == NULL_RTX) operands[3] = const0_rtx; Index: gcc/config/aarch64/aarch64.opt =================================================================== --- gcc/config/aarch64/aarch64.opt (revision 216558) +++ gcc/config/aarch64/aarch64.opt (working copy) @@ -75,6 +75,10 @@ mlittle-endian Target Report RejectNegative InverseMask(BIG_END) Assume target CPU is configured as little endian +mlong-calls +Target Report Mask(LONG_CALLS) +Generate call insns as indirect calls, if necessary + mcmodel= Target RejectNegative Joined Enum(cmodel) Var(aarch64_cmodel_var) Init(AARCH64_CMODEL_SMALL) Specify the code model Index: gcc/config/aarch64/aarch64-protos.h =================================================================== --- gcc/config/aarch64/aarch64-protos.h (revision 216558) +++ gcc/config/aarch64/aarch64-protos.h (working copy) @@ -217,6 +217,10 @@ bool aarch64_use_return_insn_p (void); const char *aarch64_output_casesi (rtx *); const char *aarch64_rewrite_selected_cpu (const char *name); +extern void aarch64_pr_long_calls (struct cpp_reader *); +extern void aarch64_pr_no_long_calls (struct cpp_reader *); +extern void aarch64_pr_long_calls_off (struct cpp_reader *); + enum aarch64_symbol_type aarch64_classify_symbol (rtx, enum aarch64_symbol_context); enum aarch64_symbol_type aarch64_classify_tls_symbol (rtx); Index: gcc/config/aarch64/aarch64.c =================================================================== --- gcc/config/aarch64/aarch64.c (revision 216558) +++ gcc/config/aarch64/aarch64.c (working copy) @@ -69,6 +69,9 @@ #include "dumpfile.h" #include "builtins.h" +static void aarch64_set_default_type_attributes (tree); +static int aarch64_comp_type_attributes (const_tree, const_tree); + /* Defined for convenience. */ #define POINTER_BYTES (POINTER_SIZE / BITS_PER_UNIT) @@ -530,12 +533,158 @@ aarch64_hard_regno_caller_save_mode (unsigned regn return choose_hard_reg_mode (regno, nregs, false); } +/* Table of machine attributes. */ +static const struct attribute_spec aarch64_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, + affects_type_identity } */ + /* Function calls made to this symbol must be done indirectly, because + it may lie outside of the 26 bit addressing range of a normal function + call. */ + { "long_call", 0, 0, false, true, true, NULL, false }, + /* Whereas these functions are always known to reside within the 26 bit + addressing range. */ + { "short_call", 0, 0, false, true, true, NULL, false }, + { NULL, 0, 0, false, false, false, NULL, false } +}; + +/* Encode the current state of the #pragma [no_]long_calls. */ +typedef enum +{ + OFF, /* No #pragma [no_]long_calls is in effect. */ + LONG, /* #pragma long_calls is in effect. */ + SHORT /* #pragma no_long_calls is in effect. */ +} aarch64_pragma_enum; + +static aarch64_pragma_enum aarch64_pragma_long_calls = OFF; + +void +aarch64_pr_long_calls (struct cpp_reader * pfile ATTRIBUTE_UNUSED) +{ + aarch64_pragma_long_calls = LONG; +} + +void +aarch64_pr_no_long_calls (struct cpp_reader * pfile ATTRIBUTE_UNUSED) +{ + aarch64_pragma_long_calls = SHORT; +} + +void +aarch64_pr_long_calls_off (struct cpp_reader * pfile ATTRIBUTE_UNUSED) +{ + aarch64_pragma_long_calls = OFF; +} + +/* Return 0 if the attributes for two types are incompatible, 1 if they + are compatible, and 2 if they are nearly compatible (which causes a + warning to be generated). */ +static int +aarch64_comp_type_attributes (const_tree type1, const_tree type2) +{ + int l1, l2, s1, s2; + + /* Check for mismatch of non-default calling convention. */ + if (TREE_CODE (type1) != FUNCTION_TYPE) + return 1; + + /* Check for mismatched call attributes. */ + l1 = lookup_attribute ("long_call", TYPE_ATTRIBUTES (type1)) != NULL; + l2 = lookup_attribute ("long_call", TYPE_ATTRIBUTES (type2)) != NULL; + s1 = lookup_attribute ("short_call", TYPE_ATTRIBUTES (type1)) != NULL; + s2 = lookup_attribute ("short_call", TYPE_ATTRIBUTES (type2)) != NULL; + + /* Only bother to check if an attribute is defined. */ + if (l1 | l2 | s1 | s2) + { + /* If one type has an attribute, the other must have the same attribute. */ + if ((l1 != l2) || (s1 != s2)) + return 0; + + /* Disallow mixed attributes. */ + if ((l1 & s2) || (l2 & s1)) + return 0; + } + + return 1; +} + +/* 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. */ +static void +aarch64_set_default_type_attributes (tree type) +{ + /* Add __attribute__ ((long_call)) to all functions, when + inside #pragma long_calls or __attribute__ ((short_call)), + when inside #pragma no_long_calls. */ + if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) + { + tree type_attr_list, attr_name; + type_attr_list = TYPE_ATTRIBUTES (type); + + if (aarch64_pragma_long_calls == LONG) + attr_name = get_identifier ("long_call"); + else if (aarch64_pragma_long_calls == SHORT) + attr_name = get_identifier ("short_call"); + else + return; + + type_attr_list = tree_cons (attr_name, NULL_TREE, type_attr_list); + TYPE_ATTRIBUTES (type) = type_attr_list; + } +} + +/* Return true if DECL is known to be linked into section SECTION. */ +static bool +aarch64_function_in_section_p (tree decl, section *section) +{ + /* We can only be certain about functions defined in the same + compilation unit. */ + if (!TREE_STATIC (decl)) + return false; + + /* Make sure that SYMBOL always binds to the definition in this + compilation unit. */ + if (!targetm.binds_local_p (decl)) + return false; + + /* If DECL_SECTION_NAME is set, assume it is trustworthy. */ + if (!DECL_SECTION_NAME (decl)) + { + /* 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 true if calls to DECL should be treated as long-calls (ie called via a register). */ static bool -aarch64_decl_is_long_call_p (const_tree decl ATTRIBUTE_UNUSED) +aarch64_decl_is_long_call_p (tree decl) { - return false; + tree attrs; + + if (!decl) + return TARGET_LONG_CALLS; + + attrs = TYPE_ATTRIBUTES (TREE_TYPE (decl)); + if (lookup_attribute ("short_call", attrs)) + return false; + + /* 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 + && TREE_CODE (decl) == FUNCTION_DECL + && aarch64_function_in_section_p (decl, current_function_section ())) + return false; + + if (lookup_attribute ("long_call", attrs)) + return true; + + return TARGET_LONG_CALLS; } /* Return true if calls to symbol-ref SYM should be treated as @@ -10231,6 +10380,15 @@ aarch64_asan_shadow_offset (void) #undef TARGET_LEGITIMIZE_ADDRESS #define TARGET_LEGITIMIZE_ADDRESS aarch64_legitimize_address +#undef TARGET_SET_DEFAULT_TYPE_ATTRIBUTES +#define TARGET_SET_DEFAULT_TYPE_ATTRIBUTES aarch64_set_default_type_attributes + +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE aarch64_attribute_table + +#undef TARGET_COMP_TYPE_ATTRIBUTES +#define TARGET_COMP_TYPE_ATTRIBUTES aarch64_comp_type_attributes + struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-aarch64.h" Index: gcc/config/aarch64/aarch64.h =================================================================== --- gcc/config/aarch64/aarch64.h (revision 216558) +++ gcc/config/aarch64/aarch64.h (working copy) @@ -633,6 +633,13 @@ typedef struct stack arg area so far. */ } CUMULATIVE_ARGS; +/* Handle pragmas for compatibility with Intel's compilers. */ +#define REGISTER_TARGET_PRAGMAS() do { \ + c_register_pragma (0, "long_calls", aarch64_pr_long_calls); \ + c_register_pragma (0, "no_long_calls", aarch64_pr_no_long_calls); \ + c_register_pragma (0, "long_calls_off", aarch64_pr_long_calls_off); \ +} while (0) + #define FUNCTION_ARG_PADDING(MODE, TYPE) \ (aarch64_pad_arg_upward (MODE, TYPE) ? upward : downward)
aarch64-long-calls-v3.diff
Description: aarch64-long-calls-v3.diff