diff -u gcc-5-5.4.0/debian/changelog gcc-5-5.4.0/debian/changelog --- gcc-5-5.4.0/debian/changelog +++ gcc-5-5.4.0/debian/changelog @@ -1,3 +1,41 @@ +gcc-5 (5.4.0-6ubuntu1~16.04.9) xenial-security; urgency=medium + + * Revert retpoline changes of ppc64el as per the recommendation from + Bill Schmidt of IBM. + - ppc-add-mspeculate-indirect-jumps: drop. + + -- Steve Beattie Mon, 05 Feb 2018 15:48:15 -0800 + +gcc-5 (5.4.0-6ubuntu1~16.04.8) xenial-security; urgency=medium + + * Add retpoline support for x86 via adding -mindirect-branch=, + -mindirect-branch-register, and -mfunction-return= support + - 0001-i386-Move-struct-ix86_frame-to-machine_function.diff, + 0002-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.diff, + 0003-i386-More-use-reference-of-struct-ix86_frame-to-avoi.diff, + 0004-i386-Don-t-use-reference-of-struct-ix86_frame.diff, + 0005-x86-Add-mindirect-branch.diff, + 0006-x86-Add-mfunction-return.diff, + 0007-x86-Add-mindirect-branch-register.diff, + 0008-x86-Add-V-register-operand-modifier.diff, + 0009-x86-Disallow-mindirect-branch-mfunction-return-with-.patch: + implement -mindirect-branch= + with attribute support, -mindirect-branch-register, and + -mfunction-return= with + attribute support. Thanks to H.J. Lu. + * Add retpoline support for ppc64 via adding + -mspeculate-indirect-jumps support + - ppc-add-mspeculate-indirect-jumps: implement + -mspeculate-indirect-jumps. Thanks to Bill Schmidt. + + -- Steve Beattie Fri, 26 Jan 2018 00:08:13 -0800 + +gcc-5 (5.4.0-6ubuntu1~16.04.6) xenial; urgency=medium + + * Add thunderx2 support. LP: #1727540. + + -- dann frazier Tue, 16 Jan 2018 10:27:06 -0700 + gcc-5 (5.4.0-6ubuntu1~16.04.5) xenial-proposed; urgency=medium * SRU diff -u gcc-5-5.4.0/debian/control gcc-5-5.4.0/debian/control --- gcc-5-5.4.0/debian/control +++ gcc-5-5.4.0/debian/control @@ -1,7 +1,7 @@ Source: gcc-5 Section: devel Priority: optional -Maintainer: Ubuntu Core developers +Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Debian GCC Maintainers Uploaders: Matthias Klose Standards-Version: 3.9.8 diff -u gcc-5-5.4.0/debian/rules.patch gcc-5-5.4.0/debian/rules.patch --- gcc-5-5.4.0/debian/rules.patch +++ gcc-5-5.4.0/debian/rules.patch @@ -112,6 +112,20 @@ pr77267 \ sanitizer-Fix-a-crash-in-SizeClassAllocator32-with-a \ asan-Enable-48-bit-VMA-support-on-aarch64 \ + $(if $(with_linaro_branch),vulcan-costs) \ + $(if $(with_linaro_branch),vulcan-to-thunderx2t99) \ + $(if $(with_linaro_branch),thunderx2t99-improve-cost-table) \ + $(if $(with_linaro_branch),thunderx2t99-scheduler) \ + $(if $(with_linaro_branch),thunderx2t99p1) \ + 0001-i386-Move-struct-ix86_frame-to-machine_function \ + 0002-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop \ + 0003-i386-More-use-reference-of-struct-ix86_frame-to-avoi \ + 0004-i386-Don-t-use-reference-of-struct-ix86_frame \ + 0005-x86-Add-mindirect-branch \ + 0006-x86-Add-mfunction-return \ + 0007-x86-Add-mindirect-branch-register \ + 0008-x86-Add-V-register-operand-modifier \ + 0009-x86-Disallow-mindirect-branch-mfunction-return-with- \ # this is still needed on powerpc, e.g. firefox and insighttoolkit4 will ftbfs. ifneq (,$(filter $(DEB_TARGET_ARCH),powerpc)) only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0001-i386-Move-struct-ix86_frame-to-machine_function.diff +++ gcc-5-5.4.0/debian/patches/0001-i386-Move-struct-ix86_frame-to-machine_function.diff @@ -0,0 +1,239 @@ +From 11a3b9034935080b9996caf07fca6353309006f1 Mon Sep 17 00:00:00 2001 +From: hjl +Date: Mon, 15 Jan 2018 11:27:24 +0000 +Subject: [PATCH 1/9] i386: Move struct ix86_frame to machine_function + +Make ix86_frame available to i386 code generation. This is needed to +backport the patch set of -mindirect-branch= to mitigate variant #2 of +the speculative execution vulnerabilities on x86 processors identified +by CVE-2017-5715, aka Spectre. + + Backport from mainline + * config/i386/i386.c (ix86_frame): Moved to ... + * config/i386/i386.h (ix86_frame): Here. + (machine_function): Add frame. + * config/i386/i386.c (ix86_compute_frame_layout): Repace the + frame argument with &cfun->machine->frame. + (ix86_can_use_return_insn_p): Don't pass &frame to + ix86_compute_frame_layout. Copy frame from cfun->machine->frame. + (ix86_can_eliminate): Likewise. + (ix86_expand_prologue): Likewise. + (ix86_expand_epilogue): Likewise. + (ix86_expand_split_stack_prologue): Likewise. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256691 138bc75d-0d04-0410-961f-82ee72b054a4 +--- + src/gcc/config/i386/i386.c | 68 ++++++++------------------------------------- + src/gcc/config/i386/i386.h | 53 ++++++++++++++++++++++++++++++++++- + 2 files changed, 65 insertions(+), 56 deletions(-) + +Index: b/src/gcc/config/i386/i386.c +=================================================================== +--- a/src/gcc/config/i386/i386.c ++++ b/src/gcc/config/i386/i386.c +@@ -2306,53 +2306,6 @@ struct GTY(()) stack_local_entry { + struct stack_local_entry *next; + }; + +-/* Structure describing stack frame layout. +- Stack grows downward: +- +- [arguments] +- <- ARG_POINTER +- saved pc +- +- saved static chain if ix86_static_chain_on_stack +- +- saved frame pointer if frame_pointer_needed +- <- HARD_FRAME_POINTER +- [saved regs] +- <- regs_save_offset +- [padding0] +- +- [saved SSE regs] +- <- sse_regs_save_offset +- [padding1] | +- | <- FRAME_POINTER +- [va_arg registers] | +- | +- [frame] | +- | +- [padding2] | = to_allocate +- <- STACK_POINTER +- */ +-struct ix86_frame +-{ +- int nsseregs; +- int nregs; +- int va_arg_size; +- int red_zone_size; +- int outgoing_arguments_size; +- +- /* The offsets relative to ARG_POINTER. */ +- HOST_WIDE_INT frame_pointer_offset; +- HOST_WIDE_INT hard_frame_pointer_offset; +- HOST_WIDE_INT stack_pointer_offset; +- HOST_WIDE_INT hfp_save_offset; +- HOST_WIDE_INT reg_save_offset; +- HOST_WIDE_INT sse_reg_save_offset; +- +- /* When save_regs_using_mov is set, emit prologue using +- move instead of push instructions. */ +- bool save_regs_using_mov; +-}; +- + /* Which cpu are we scheduling for. */ + enum attr_cpu ix86_schedule; + +@@ -2443,7 +2396,7 @@ static unsigned int ix86_function_arg_bo + const_tree); + static rtx ix86_static_chain (const_tree, bool); + static int ix86_function_regparm (const_tree, const_tree); +-static void ix86_compute_frame_layout (struct ix86_frame *); ++static void ix86_compute_frame_layout (void); + static bool ix86_expand_vector_init_one_nonzero (bool, machine_mode, + rtx, rtx, int); + static void ix86_add_new_builtins (HOST_WIDE_INT); +@@ -9664,7 +9617,8 @@ ix86_can_use_return_insn_p (void) + if (crtl->args.pops_args && crtl->args.size >= 32768) + return 0; + +- ix86_compute_frame_layout (&frame); ++ ix86_compute_frame_layout (); ++ frame = cfun->machine->frame; + return (frame.stack_pointer_offset == UNITS_PER_WORD + && (frame.nregs + frame.nsseregs) == 0); + } +@@ -10073,8 +10027,8 @@ ix86_can_eliminate (const int from, cons + HOST_WIDE_INT + ix86_initial_elimination_offset (int from, int to) + { +- struct ix86_frame frame; +- ix86_compute_frame_layout (&frame); ++ ix86_compute_frame_layout (); ++ struct ix86_frame frame = cfun->machine->frame; + + if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) + return frame.hard_frame_pointer_offset; +@@ -10113,8 +10067,9 @@ ix86_builtin_setjmp_frame_value (void) + /* Fill structure ix86_frame about frame of currently computed function. */ + + static void +-ix86_compute_frame_layout (struct ix86_frame *frame) ++ix86_compute_frame_layout (void) + { ++ struct ix86_frame *frame = &cfun->machine->frame; + unsigned HOST_WIDE_INT stack_alignment_needed; + HOST_WIDE_INT offset; + unsigned HOST_WIDE_INT preferred_alignment; +@@ -11417,7 +11372,8 @@ ix86_expand_prologue (void) + m->fs.sp_offset = INCOMING_FRAME_SP_OFFSET; + m->fs.sp_valid = true; + +- ix86_compute_frame_layout (&frame); ++ ix86_compute_frame_layout (); ++ frame = m->frame; + + if (!TARGET_64BIT && ix86_function_ms_hook_prologue (current_function_decl)) + { +@@ -12083,7 +12039,8 @@ ix86_expand_epilogue (int style) + bool using_drap; + + ix86_finalize_stack_realign_flags (); +- ix86_compute_frame_layout (&frame); ++ ix86_compute_frame_layout (); ++ frame = m->frame; + + m->fs.sp_valid = (!frame_pointer_needed + || (crtl->sp_is_unchanging +@@ -12546,7 +12503,8 @@ ix86_expand_split_stack_prologue (void) + gcc_assert (flag_split_stack && reload_completed); + + ix86_finalize_stack_realign_flags (); +- ix86_compute_frame_layout (&frame); ++ ix86_compute_frame_layout (); ++ frame = cfun->machine->frame; + allocate = frame.stack_pointer_offset - INCOMING_FRAME_SP_OFFSET; + + /* This is the label we will branch to if we have enough stack +Index: b/src/gcc/config/i386/i386.h +=================================================================== +--- a/src/gcc/config/i386/i386.h ++++ b/src/gcc/config/i386/i386.h +@@ -2409,9 +2409,56 @@ enum avx_u128_state + + #define FASTCALL_PREFIX '@' + ++#ifndef USED_FOR_TARGET ++/* Structure describing stack frame layout. ++ Stack grows downward: ++ ++ [arguments] ++ <- ARG_POINTER ++ saved pc ++ ++ saved static chain if ix86_static_chain_on_stack ++ ++ saved frame pointer if frame_pointer_needed ++ <- HARD_FRAME_POINTER ++ [saved regs] ++ <- regs_save_offset ++ [padding0] ++ ++ [saved SSE regs] ++ <- sse_regs_save_offset ++ [padding1] | ++ | <- FRAME_POINTER ++ [va_arg registers] | ++ | ++ [frame] | ++ | ++ [padding2] | = to_allocate ++ <- STACK_POINTER ++ */ ++struct GTY(()) ix86_frame ++{ ++ int nsseregs; ++ int nregs; ++ int va_arg_size; ++ int red_zone_size; ++ int outgoing_arguments_size; ++ ++ /* The offsets relative to ARG_POINTER. */ ++ HOST_WIDE_INT frame_pointer_offset; ++ HOST_WIDE_INT hard_frame_pointer_offset; ++ HOST_WIDE_INT stack_pointer_offset; ++ HOST_WIDE_INT hfp_save_offset; ++ HOST_WIDE_INT reg_save_offset; ++ HOST_WIDE_INT sse_reg_save_offset; ++ ++ /* When save_regs_using_mov is set, emit prologue using ++ move instead of push instructions. */ ++ bool save_regs_using_mov; ++}; ++ + /* Machine specific frame tracking during prologue/epilogue generation. */ + +-#ifndef USED_FOR_TARGET + struct GTY(()) machine_frame_state + { + /* This pair tracks the currently active CFA as reg+offset. When reg +@@ -2457,6 +2504,9 @@ struct GTY(()) machine_function { + int varargs_fpr_size; + int optimize_mode_switching[MAX_386_ENTITIES]; + ++ /* Cached initial frame layout for the current function. */ ++ struct ix86_frame frame; ++ + /* Number of saved registers USE_FAST_PROLOGUE_EPILOGUE + has been computed for. */ + int use_fast_prologue_epilogue_nregs; +@@ -2524,6 +2574,7 @@ struct GTY(()) machine_function { + #define ix86_current_function_calls_tls_descriptor \ + (ix86_tls_descriptor_calls_expanded_in_cfun && df_regs_ever_live_p (SP_REG)) + #define ix86_static_chain_on_stack (cfun->machine->static_chain_on_stack) ++#define ix86_red_zone_size (cfun->machine->frame.red_zone_size) + + /* Control behavior of x86_file_start. */ + #define X86_FILE_START_VERSION_DIRECTIVE false only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0002-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.diff +++ gcc-5-5.4.0/debian/patches/0002-i386-Use-reference-of-struct-ix86_frame-to-avoid-cop.diff @@ -0,0 +1,72 @@ +From 18202ba32cb8de22fc43a5839235a751d0f5c4d9 Mon Sep 17 00:00:00 2001 +From: hjl +Date: Mon, 15 Jan 2018 11:28:44 +0000 +Subject: [PATCH 2/9] i386: Use reference of struct ix86_frame to avoid copy + +When there is no need to make a copy of ix86_frame, we can use reference +of struct ix86_frame to avoid copy. + + Backport from mainline + * config/i386/i386.c (ix86_can_use_return_insn_p): Use reference + of struct ix86_frame. + (ix86_initial_elimination_offset): Likewise. + (ix86_expand_split_stack_prologue): Likewise. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256692 138bc75d-0d04-0410-961f-82ee72b054a4 +--- + gcc/config/i386/i386.c | 9 +++------ + gcc/testsuite/ChangeLog | 8 ++++++++ + 2 files changed, 11 insertions(+), 6 deletions(-) + +diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c +index 8133372..397ef7c 100644 +--- a/src/gcc/config/i386/i386.c ++++ b/src/gcc/config/i386/i386.c +@@ -11843,8 +11843,6 @@ symbolic_reference_mentioned_p (rtx op) + bool + ix86_can_use_return_insn_p (void) + { +- struct ix86_frame frame; +- + if (! reload_completed || frame_pointer_needed) + return 0; + +@@ -11857,7 +11855,7 @@ ix86_can_use_return_insn_p (void) + return 0; + + ix86_compute_frame_layout (); +- frame = cfun->machine->frame; ++ struct ix86_frame &frame = cfun->machine->frame; + return (frame.stack_pointer_offset == UNITS_PER_WORD + && (frame.nregs + frame.nsseregs) == 0); + } +@@ -12344,7 +12342,7 @@ HOST_WIDE_INT + ix86_initial_elimination_offset (int from, int to) + { + ix86_compute_frame_layout (); +- struct ix86_frame frame = cfun->machine->frame; ++ struct ix86_frame &frame = cfun->machine->frame; + + if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM) + return frame.hard_frame_pointer_offset; +@@ -14860,7 +14858,6 @@ static GTY(()) rtx split_stack_fn_large; + void + ix86_expand_split_stack_prologue (void) + { +- struct ix86_frame frame; + HOST_WIDE_INT allocate; + unsigned HOST_WIDE_INT args_size; + rtx_code_label *label; +@@ -14873,7 +14870,7 @@ ix86_expand_split_stack_prologue (void) + + ix86_finalize_stack_realign_flags (); + ix86_compute_frame_layout (); +- frame = cfun->machine->frame; ++ struct ix86_frame &frame = cfun->machine->frame; + allocate = frame.stack_pointer_offset - INCOMING_FRAME_SP_OFFSET; + + /* This is the label we will branch to if we have enough stack +-- +2.7.4 + only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0003-i386-More-use-reference-of-struct-ix86_frame-to-avoi.diff +++ gcc-5-5.4.0/debian/patches/0003-i386-More-use-reference-of-struct-ix86_frame-to-avoi.diff @@ -0,0 +1,59 @@ +From 839ca2d69157ef03c8df0ab912dacdb991738694 Mon Sep 17 00:00:00 2001 +From: hjl +Date: Mon, 15 Jan 2018 11:33:42 +0000 +Subject: [PATCH 3/9] i386: More use reference of struct ix86_frame to avoid copy + +When there is no need to make a copy of ix86_frame, we can use reference +of struct ix86_frame to avoid copy. + + Backport from mainline + * config/i386/i386.c (ix86_expand_prologue): Use reference of + struct ix86_frame. + (ix86_expand_epilogue): Likewise. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256695 138bc75d-0d04-0410-961f-82ee72b054a4 +--- + gcc/ChangeLog | 7 +++++++ + gcc/config/i386/i386.c | 6 ++---- + 2 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c +index 397ef7c..986e6d7 100644 +--- a/src/gcc/config/i386/i386.c ++++ b/src/gcc/config/i386/i386.c +@@ -13667,7 +13667,6 @@ ix86_expand_prologue (void) + { + struct machine_function *m = cfun->machine; + rtx insn, t; +- struct ix86_frame frame; + HOST_WIDE_INT allocate; + bool int_registers_saved; + bool sse_registers_saved; +@@ -13691,7 +13690,7 @@ ix86_expand_prologue (void) + m->fs.sp_valid = true; + + ix86_compute_frame_layout (); +- frame = m->frame; ++ struct ix86_frame &frame = cfun->machine->frame; + + if (!TARGET_64BIT && ix86_function_ms_hook_prologue (current_function_decl)) + { +@@ -14354,13 +14353,12 @@ ix86_expand_epilogue (int style) + { + struct machine_function *m = cfun->machine; + struct machine_frame_state frame_state_save = m->fs; +- struct ix86_frame frame; + bool restore_regs_via_mov; + bool using_drap; + + ix86_finalize_stack_realign_flags (); + ix86_compute_frame_layout (); +- frame = m->frame; ++ struct ix86_frame &frame = cfun->machine->frame; + + m->fs.sp_valid = (!frame_pointer_needed + || (crtl->sp_is_unchanging +-- +2.7.4 + only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0004-i386-Don-t-use-reference-of-struct-ix86_frame.diff +++ gcc-5-5.4.0/debian/patches/0004-i386-Don-t-use-reference-of-struct-ix86_frame.diff @@ -0,0 +1,63 @@ +From c25b81ba01fa9ac0c1baa3aabd64190c47928f03 Mon Sep 17 00:00:00 2001 +From: hjl +Date: Tue, 16 Jan 2018 12:49:29 +0000 +Subject: [PATCH 4/9] i386: Don't use reference of struct ix86_frame + +Use reference of struct ix86_frame in ix86_expand_prologue and +ix86_expand_epilogue caused: + +raised STORAGE_ERROR : stack overflow or erroneous memory access +make[5]: *** [/export/gnu/import/git/sources/gcc/gcc/ada/Make-generated.in:45: ada/sinfo.h] Error 1 + +on trunk when bootstrapping GCC with ada on x86-64. + + * config/i386/i386.c (ix86_expand_prologue): Don't use reference + of struct ix86_frame. + (ix86_expand_epilogue): Likewise. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256742 138bc75d-0d04-0410-961f-82ee72b054a4 +--- + gcc/ChangeLog | 10 ++++++++++ + gcc/config/i386/i386.c | 6 ++++-- + 2 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c +index e758387..ba2abc5 100644 +--- a/src/gcc/config/i386/i386.c ++++ b/src/gcc/config/i386/i386.c +@@ -14061,6 +14061,7 @@ ix86_expand_prologue (void) + { + struct machine_function *m = cfun->machine; + rtx insn, t; ++ struct ix86_frame frame; + HOST_WIDE_INT allocate; + bool int_registers_saved; + bool sse_registers_saved; +@@ -14084,7 +14085,7 @@ ix86_expand_prologue (void) + m->fs.sp_valid = true; + + ix86_compute_frame_layout (); +- struct ix86_frame &frame = cfun->machine->frame; ++ frame = m->frame; + + if (!TARGET_64BIT && ix86_function_ms_hook_prologue (current_function_decl)) + { +@@ -14747,12 +14748,13 @@ ix86_expand_epilogue (int style) + { + struct machine_function *m = cfun->machine; + struct machine_frame_state frame_state_save = m->fs; ++ struct ix86_frame frame; + bool restore_regs_via_mov; + bool using_drap; + + ix86_finalize_stack_realign_flags (); + ix86_compute_frame_layout (); +- struct ix86_frame &frame = cfun->machine->frame; ++ frame = m->frame; + + m->fs.sp_valid = (!frame_pointer_needed + || (crtl->sp_is_unchanging +-- +2.7.4 + only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0005-x86-Add-mindirect-branch.diff +++ gcc-5-5.4.0/debian/patches/0005-x86-Add-mindirect-branch.diff @@ -0,0 +1,1923 @@ +From c6b72be421ded17e0c156070ba6e90aa6c335ed6 Mon Sep 17 00:00:00 2001 +From: hjl +Date: Tue, 16 Jan 2018 10:59:42 +0000 +Subject: [PATCH 5/9] x86: Add -mindirect-branch= + +Add -mindirect-branch= option to convert indirect call and jump to call +and return thunks. The default is 'keep', which keeps indirect call and +jump unmodified. 'thunk' converts indirect call and jump to call and +return thunk. 'thunk-inline' converts indirect call and jump to inlined +call and return thunk. 'thunk-extern' converts indirect call and jump to +external call and return thunk provided in a separate object file. You +can control this behavior for a specific function by using the function +attribute indirect_branch. + +2 kinds of thunks are geneated. Memory thunk where the function address +is at the top of the stack: + +__x86_indirect_thunk: + call L2 +L1: + pause + lfence + jmp L1 +L2: + lea 8(%rsp), %rsp|lea 4(%esp), %esp + ret + +Indirect jmp via memory, "jmp mem", is converted to + + push memory + jmp __x86_indirect_thunk + +Indirect call via memory, "call mem", is converted to + + jmp L2 +L1: + push [mem] + jmp __x86_indirect_thunk +L2: + call L1 + +Register thunk where the function address is in a register, reg: + +__x86_indirect_thunk_reg: + call L2 +L1: + pause + lfence + jmp L1 +L2: + movq %reg, (%rsp)|movl %reg, (%esp) + ret + +where reg is one of (r|e)ax, (r|e)dx, (r|e)cx, (r|e)bx, (r|e)si, (r|e)di, +(r|e)bp, r8, r9, r10, r11, r12, r13, r14 and r15. + +Indirect jmp via register, "jmp reg", is converted to + + jmp __x86_indirect_thunk_reg + +Indirect call via register, "call reg", is converted to + + call __x86_indirect_thunk_reg + +gcc/ + + Backport from mainline + * config/i386/i386-opts.h (indirect_branch): New. + * config/i386/i386-protos.h (ix86_output_indirect_jmp): Likewise. + * config/i386/i386.c (ix86_using_red_zone): Disallow red-zone + with local indirect jump when converting indirect call and jump. + (ix86_set_indirect_branch_type): New. + (ix86_set_current_function): Call ix86_set_indirect_branch_type. + (indirectlabelno): New. + (indirect_thunk_needed): Likewise. + (indirect_thunk_bnd_needed): Likewise. + (indirect_thunks_used): Likewise. + (indirect_thunks_bnd_used): Likewise. + (INDIRECT_LABEL): Likewise. + (indirect_thunk_name): Likewise. + (output_indirect_thunk): Likewise. + (output_indirect_thunk_function): Likewise. + (ix86_output_indirect_branch_via_reg): Likewise. + (ix86_output_indirect_branch_via_push): Likewise. + (ix86_output_indirect_branch): Likewise. + (ix86_output_indirect_jmp): Likewise. + (ix86_code_end): Call output_indirect_thunk_function if needed. + (ix86_output_call_insn): Call ix86_output_indirect_branch if + needed. + (ix86_handle_fndecl_attribute): Handle indirect_branch. + (ix86_attribute_table): Add indirect_branch. + * config/i386/i386.h (machine_function): Add indirect_branch_type + and has_local_indirect_jump. + * config/i386/i386.md (indirect_jump): Set has_local_indirect_jump + to true. + (tablejump): Likewise. + (*indirect_jump): Use ix86_output_indirect_jmp. + (*tablejump_1): Likewise. + (simple_return_indirect_internal): Likewise. + * config/i386/i386.opt (mindirect-branch=): New option. + (indirect_branch): New. + (keep): Likewise. + (thunk): Likewise. + (thunk-inline): Likewise. + (thunk-extern): Likewise. + * doc/extend.texi: Document indirect_branch function attribute. + * doc/invoke.texi: Document -mindirect-branch= option. + +gcc/testsuite/ + + Backport from mainline + * gcc.target/i386/indirect-thunk-1.c: New test. + * gcc.target/i386/indirect-thunk-2.c: Likewise. + * gcc.target/i386/indirect-thunk-3.c: Likewise. + * gcc.target/i386/indirect-thunk-4.c: Likewise. + * gcc.target/i386/indirect-thunk-5.c: Likewise. + * gcc.target/i386/indirect-thunk-6.c: Likewise. + * gcc.target/i386/indirect-thunk-7.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-1.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-2.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-3.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-4.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-5.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-6.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-7.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-8.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-1.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-2.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-3.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-4.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-1.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-2.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-3.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-4.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-5.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-6.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-7.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-1.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-2.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-3.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-4.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-5.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-6.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-7.c: Likewise. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256732 138bc75d-0d04-0410-961f-82ee72b054a4 + +[UBUNTU NOTES: Updated for gcc-5.4 to include defines for + FIRST_INT_REG, LAST_INT_REG, and LEGACY_INT_REGNO_P as defined in + https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=222269. + Dropped indirect-thunk-5.c, indirect-thunk-6.c, + indirect-thunk-bnd-3.c, indirect-thunk-bnd-4.c, + indirect-thunk-extern-5.c, indirect-thunk-extern-6.c, + indirect-thunk-inline-5.c, and indirect-thunk-inline-6.c tests due + to gcc 5.4 and earlier not supporting the -fno-plt option. + --sbeattie, tyhicks] +--- + src/gcc/config/i386/i386-opts.h | 13 + src/gcc/config/i386/i386-protos.h | 1 + src/gcc/config/i386/i386.c | 621 +++++++++++- + src/gcc/config/i386/i386.h | 12 + src/gcc/config/i386/i386.md | 26 + src/gcc/config/i386/i386.opt | 20 + src/gcc/doc/extend.texi | 10 + src/gcc/doc/invoke.texi | 14 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 20 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 20 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 21 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 21 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 44 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c | 23 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c | 21 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c | 23 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c | 22 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c | 22 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c | 21 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c | 44 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c | 42 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c | 20 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c | 21 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c | 19 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c | 19 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c | 20 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c | 20 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c | 43 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c | 20 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c | 20 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c | 21 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c | 21 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c | 44 + 33 files changed, 1334 insertions(+), 15 deletions(-) + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-1.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-2.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-3.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-4.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-5.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-6.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-7.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c + +Index: b/src/gcc/config/i386/i386-opts.h +=================================================================== +--- a/src/gcc/config/i386/i386-opts.h ++++ b/src/gcc/config/i386/i386-opts.h +@@ -99,4 +99,17 @@ enum stack_protector_guard { + SSP_GLOBAL /* global canary */ + }; + ++/* This is used to mitigate variant #2 of the speculative execution ++ vulnerabilities on x86 processors identified by CVE-2017-5715, aka ++ Spectre. They convert indirect branches and function returns to ++ call and return thunks to avoid speculative execution via indirect ++ call, jmp and ret. */ ++enum indirect_branch { ++ indirect_branch_unset = 0, ++ indirect_branch_keep, ++ indirect_branch_thunk, ++ indirect_branch_thunk_inline, ++ indirect_branch_thunk_extern ++}; ++ + #endif +Index: b/src/gcc/config/i386/i386-protos.h +=================================================================== +--- a/src/gcc/config/i386/i386-protos.h ++++ b/src/gcc/config/i386/i386-protos.h +@@ -306,6 +306,7 @@ extern enum attr_cpu ix86_schedule; + #endif + + extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op); ++extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p); + + #ifdef RTX_CODE + /* Target data for multipass lookahead scheduling. +Index: b/src/gcc/config/i386/i386.c +=================================================================== +--- a/src/gcc/config/i386/i386.c ++++ b/src/gcc/config/i386/i386.c +@@ -2554,12 +2554,23 @@ make_pass_insert_vzeroupper (gcc::contex + return new pass_insert_vzeroupper (ctxt); + } + +-/* Return true if a red-zone is in use. */ ++/* Return true if a red-zone is in use. We can't use red-zone when ++ there are local indirect jumps, like "indirect_jump" or "tablejump", ++ which jumps to another place in the function, since "call" in the ++ indirect thunk pushes the return address onto stack, destroying ++ red-zone. ++ ++ TODO: If we can reserve the first 2 WORDs, for PUSH and, another ++ for CALL, in red-zone, we can allow local indirect jumps with ++ indirect thunk. */ + + static inline bool + ix86_using_red_zone (void) + { +- return TARGET_RED_ZONE && !TARGET_64BIT_MS_ABI; ++ return (TARGET_RED_ZONE ++ && !TARGET_64BIT_MS_ABI ++ && (!cfun->machine->has_local_indirect_jump ++ || cfun->machine->indirect_branch_type == indirect_branch_keep)); + } + + /* Return a string that documents the current -m options. The caller is +@@ -5126,6 +5137,37 @@ ix86_reset_previous_fndecl (void) + ix86_previous_fndecl = NULL_TREE; + } + ++/* Set the indirect_branch_type field from the function FNDECL. */ ++ ++static void ++ix86_set_indirect_branch_type (tree fndecl) ++{ ++ if (cfun->machine->indirect_branch_type == indirect_branch_unset) ++ { ++ tree attr = lookup_attribute ("indirect_branch", ++ DECL_ATTRIBUTES (fndecl)); ++ if (attr != NULL) ++ { ++ tree args = TREE_VALUE (attr); ++ if (args == NULL) ++ gcc_unreachable (); ++ tree cst = TREE_VALUE (args); ++ if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0) ++ cfun->machine->indirect_branch_type = indirect_branch_keep; ++ else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0) ++ cfun->machine->indirect_branch_type = indirect_branch_thunk; ++ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0) ++ cfun->machine->indirect_branch_type = indirect_branch_thunk_inline; ++ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0) ++ cfun->machine->indirect_branch_type = indirect_branch_thunk_extern; ++ else ++ gcc_unreachable (); ++ } ++ else ++ cfun->machine->indirect_branch_type = ix86_indirect_branch; ++ } ++} ++ + /* Establish appropriate back-end context for processing the function + FNDECL. The argument might be NULL to indicate processing at top + level, outside of any function scope. */ +@@ -5136,7 +5178,11 @@ ix86_set_current_function (tree fndecl) + several times in the course of compiling a function, and we don't want to + slow things down too much or call target_reinit when it isn't safe. */ + if (fndecl == ix86_previous_fndecl) +- return; ++ { ++ if (fndecl != NULL_TREE) ++ ix86_set_indirect_branch_type (fndecl); ++ return; ++ } + + tree old_tree; + if (ix86_previous_fndecl == NULL_TREE) +@@ -5153,6 +5199,8 @@ ix86_set_current_function (tree fndecl) + return; + } + ++ ix86_set_indirect_branch_type (fndecl); ++ + tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl); + if (new_tree == NULL_TREE) + new_tree = target_option_default_node; +@@ -9681,6 +9729,220 @@ ix86_setup_frame_addresses (void) + # endif + #endif + ++/* Label count for call and return thunks. It is used to make unique ++ labels in call and return thunks. */ ++static int indirectlabelno; ++ ++/* True if call and return thunk functions are needed. */ ++static bool indirect_thunk_needed = false; ++/* True if call and return thunk functions with the BND prefix are ++ needed. */ ++static bool indirect_thunk_bnd_needed = false; ++ ++/* Bit masks of integer registers, which contain branch target, used ++ by call and return thunks functions. */ ++static int indirect_thunks_used; ++/* Bit masks of integer registers, which contain branch target, used ++ by call and return thunks functions with the BND prefix. */ ++static int indirect_thunks_bnd_used; ++ ++#ifndef INDIRECT_LABEL ++# define INDIRECT_LABEL "LIND" ++#endif ++ ++/* Fills in the label name that should be used for the indirect thunk. */ ++ ++static void ++indirect_thunk_name (char name[32], int regno, bool need_bnd_p) ++{ ++ if (USE_HIDDEN_LINKONCE) ++ { ++ const char *bnd = need_bnd_p ? "_bnd" : ""; ++ if (regno >= 0) ++ { ++ const char *reg_prefix; ++ if (LEGACY_INT_REGNO_P (regno)) ++ reg_prefix = TARGET_64BIT ? "r" : "e"; ++ else ++ reg_prefix = ""; ++ sprintf (name, "__x86_indirect_thunk%s_%s%s", ++ bnd, reg_prefix, reg_names[regno]); ++ } ++ else ++ sprintf (name, "__x86_indirect_thunk%s", bnd); ++ } ++ else ++ { ++ if (regno >= 0) ++ { ++ if (need_bnd_p) ++ ASM_GENERATE_INTERNAL_LABEL (name, "LITBR", regno); ++ else ++ ASM_GENERATE_INTERNAL_LABEL (name, "LITR", regno); ++ } ++ else ++ { ++ if (need_bnd_p) ++ ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0); ++ else ++ ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0); ++ } ++ } ++} ++ ++/* Output a call and return thunk for indirect branch. If BND_P is ++ true, the BND prefix is needed. If REGNO != -1, the function ++ address is in REGNO and the call and return thunk looks like: ++ ++ call L2 ++ L1: ++ pause ++ jmp L1 ++ L2: ++ mov %REG, (%sp) ++ ret ++ ++ Otherwise, the function address is on the top of stack and the ++ call and return thunk looks like: ++ ++ call L2 ++ L1: ++ pause ++ jmp L1 ++ L2: ++ lea WORD_SIZE(%sp), %sp ++ ret ++ */ ++ ++static void ++output_indirect_thunk (bool need_bnd_p, int regno) ++{ ++ char indirectlabel1[32]; ++ char indirectlabel2[32]; ++ ++ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, INDIRECT_LABEL, ++ indirectlabelno++); ++ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, INDIRECT_LABEL, ++ indirectlabelno++); ++ ++ /* Call */ ++ if (need_bnd_p) ++ fputs ("\tbnd call\t", asm_out_file); ++ else ++ fputs ("\tcall\t", asm_out_file); ++ assemble_name_raw (asm_out_file, indirectlabel2); ++ fputc ('\n', asm_out_file); ++ ++ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1); ++ ++ /* Pause + lfence. */ ++ fprintf (asm_out_file, "\tpause\n\tlfence\n"); ++ ++ /* Jump. */ ++ fputs ("\tjmp\t", asm_out_file); ++ assemble_name_raw (asm_out_file, indirectlabel1); ++ fputc ('\n', asm_out_file); ++ ++ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2); ++ ++ if (regno >= 0) ++ { ++ /* MOV. */ ++ rtx xops[2]; ++ xops[0] = gen_rtx_MEM (word_mode, stack_pointer_rtx); ++ xops[1] = gen_rtx_REG (word_mode, regno); ++ output_asm_insn ("mov\t{%1, %0|%0, %1}", xops); ++ } ++ else ++ { ++ /* LEA. */ ++ rtx xops[2]; ++ xops[0] = stack_pointer_rtx; ++ xops[1] = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD); ++ output_asm_insn ("lea\t{%E1, %0|%0, %E1}", xops); ++ } ++ ++ if (need_bnd_p) ++ fputs ("\tbnd ret\n", asm_out_file); ++ else ++ fputs ("\tret\n", asm_out_file); ++} ++ ++/* Output a funtion with a call and return thunk for indirect branch. ++ If BND_P is true, the BND prefix is needed. If REGNO != -1, the ++ function address is in REGNO. Otherwise, the function address is ++ on the top of stack. */ ++ ++static void ++output_indirect_thunk_function (bool need_bnd_p, int regno) ++{ ++ char name[32]; ++ tree decl; ++ ++ /* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd. */ ++ indirect_thunk_name (name, regno, need_bnd_p); ++ decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL, ++ get_identifier (name), ++ build_function_type_list (void_type_node, NULL_TREE)); ++ DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL, ++ NULL_TREE, void_type_node); ++ TREE_PUBLIC (decl) = 1; ++ TREE_STATIC (decl) = 1; ++ DECL_IGNORED_P (decl) = 1; ++ ++#if TARGET_MACHO ++ if (TARGET_MACHO) ++ { ++ switch_to_section (darwin_sections[picbase_thunk_section]); ++ fputs ("\t.weak_definition\t", asm_out_file); ++ assemble_name (asm_out_file, name); ++ fputs ("\n\t.private_extern\t", asm_out_file); ++ assemble_name (asm_out_file, name); ++ putc ('\n', asm_out_file); ++ ASM_OUTPUT_LABEL (asm_out_file, name); ++ DECL_WEAK (decl) = 1; ++ } ++ else ++#endif ++ if (USE_HIDDEN_LINKONCE) ++ { ++ cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl)); ++ ++ targetm.asm_out.unique_section (decl, 0); ++ switch_to_section (get_named_section (decl, NULL, 0)); ++ ++ targetm.asm_out.globalize_label (asm_out_file, name); ++ fputs ("\t.hidden\t", asm_out_file); ++ assemble_name (asm_out_file, name); ++ putc ('\n', asm_out_file); ++ ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl); ++ } ++ else ++ { ++ switch_to_section (text_section); ++ ASM_OUTPUT_LABEL (asm_out_file, name); ++ } ++ ++ DECL_INITIAL (decl) = make_node (BLOCK); ++ current_function_decl = decl; ++ allocate_struct_function (decl, false); ++ init_function_start (decl); ++ /* We're about to hide the function body from callees of final_* by ++ emitting it directly; tell them we're a thunk, if they care. */ ++ cfun->is_thunk = true; ++ first_function_block_is_cold = false; ++ /* Make sure unwind info is emitted for the thunk if needed. */ ++ final_start_function (emit_barrier (), asm_out_file, 1); ++ ++ output_indirect_thunk (need_bnd_p, regno); ++ ++ final_end_function (); ++ init_insn_lengths (); ++ free_after_compilation (cfun); ++ set_cfun (NULL); ++ current_function_decl = NULL; ++} ++ + static int pic_labels_used; + + /* Fills in the label name that should be used for a pc thunk for +@@ -9707,11 +9969,32 @@ ix86_code_end (void) + rtx xops[2]; + int regno; + ++ if (indirect_thunk_needed) ++ output_indirect_thunk_function (false, -1); ++ if (indirect_thunk_bnd_needed) ++ output_indirect_thunk_function (true, -1); ++ ++ for (regno = FIRST_REX_INT_REG; regno <= LAST_REX_INT_REG; regno++) ++ { ++ int i = regno - FIRST_REX_INT_REG + LAST_INT_REG + 1; ++ if ((indirect_thunks_used & (1 << i))) ++ output_indirect_thunk_function (false, regno); ++ ++ if ((indirect_thunks_bnd_used & (1 << i))) ++ output_indirect_thunk_function (true, regno); ++ } ++ + for (regno = AX_REG; regno <= SP_REG; regno++) + { + char name[32]; + tree decl; + ++ if ((indirect_thunks_used & (1 << regno))) ++ output_indirect_thunk_function (false, regno); ++ ++ if ((indirect_thunks_bnd_used & (1 << regno))) ++ output_indirect_thunk_function (true, regno); ++ + if (!(pic_labels_used & (1 << regno))) + continue; + +@@ -25805,12 +26088,292 @@ ix86_expand_call (rtx retval, rtx fnaddr + return call; + } + ++/* Output indirect branch via a call and return thunk. CALL_OP is a ++ register which contains the branch target. XASM is the assembly ++ template for CALL_OP. Branch is a tail call if SIBCALL_P is true. ++ A normal call is converted to: ++ ++ call __x86_indirect_thunk_reg ++ ++ and a tail call is converted to: ++ ++ jmp __x86_indirect_thunk_reg ++ */ ++ ++static void ++ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p) ++{ ++ char thunk_name_buf[32]; ++ char *thunk_name; ++ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn); ++ int regno = REGNO (call_op); ++ ++ if (cfun->machine->indirect_branch_type ++ != indirect_branch_thunk_inline) ++ { ++ if (cfun->machine->indirect_branch_type == indirect_branch_thunk) ++ { ++ int i = regno; ++ if (i >= FIRST_REX_INT_REG) ++ i -= (FIRST_REX_INT_REG - LAST_INT_REG - 1); ++ if (need_bnd_p) ++ indirect_thunks_bnd_used |= 1 << i; ++ else ++ indirect_thunks_used |= 1 << i; ++ } ++ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p); ++ thunk_name = thunk_name_buf; ++ } ++ else ++ thunk_name = NULL; ++ ++ if (sibcall_p) ++ { ++ if (thunk_name != NULL) ++ { ++ if (need_bnd_p) ++ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name); ++ else ++ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name); ++ } ++ else ++ output_indirect_thunk (need_bnd_p, regno); ++ } ++ else ++ { ++ if (thunk_name != NULL) ++ { ++ if (need_bnd_p) ++ fprintf (asm_out_file, "\tbnd call\t%s\n", thunk_name); ++ else ++ fprintf (asm_out_file, "\tcall\t%s\n", thunk_name); ++ return; ++ } ++ ++ char indirectlabel1[32]; ++ char indirectlabel2[32]; ++ ++ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, ++ INDIRECT_LABEL, ++ indirectlabelno++); ++ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, ++ INDIRECT_LABEL, ++ indirectlabelno++); ++ ++ /* Jump. */ ++ if (need_bnd_p) ++ fputs ("\tbnd jmp\t", asm_out_file); ++ else ++ fputs ("\tjmp\t", asm_out_file); ++ assemble_name_raw (asm_out_file, indirectlabel2); ++ fputc ('\n', asm_out_file); ++ ++ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1); ++ ++ if (thunk_name != NULL) ++ { ++ if (need_bnd_p) ++ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name); ++ else ++ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name); ++ } ++ else ++ output_indirect_thunk (need_bnd_p, regno); ++ ++ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2); ++ ++ /* Call. */ ++ if (need_bnd_p) ++ fputs ("\tbnd call\t", asm_out_file); ++ else ++ fputs ("\tcall\t", asm_out_file); ++ assemble_name_raw (asm_out_file, indirectlabel1); ++ fputc ('\n', asm_out_file); ++ } ++} ++ ++/* Output indirect branch via a call and return thunk. CALL_OP is ++ the branch target. XASM is the assembly template for CALL_OP. ++ Branch is a tail call if SIBCALL_P is true. A normal call is ++ converted to: ++ ++ jmp L2 ++ L1: ++ push CALL_OP ++ jmp __x86_indirect_thunk ++ L2: ++ call L1 ++ ++ and a tail call is converted to: ++ ++ push CALL_OP ++ jmp __x86_indirect_thunk ++ */ ++ ++static void ++ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm, ++ bool sibcall_p) ++{ ++ char thunk_name_buf[32]; ++ char *thunk_name; ++ char push_buf[64]; ++ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn); ++ int regno = -1; ++ ++ if (cfun->machine->indirect_branch_type ++ != indirect_branch_thunk_inline) ++ { ++ if (cfun->machine->indirect_branch_type == indirect_branch_thunk) ++ { ++ if (need_bnd_p) ++ indirect_thunk_bnd_needed = true; ++ else ++ indirect_thunk_needed = true; ++ } ++ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p); ++ thunk_name = thunk_name_buf; ++ } ++ else ++ thunk_name = NULL; ++ ++ snprintf (push_buf, sizeof (push_buf), "push{%c}\t%s", ++ TARGET_64BIT ? 'q' : 'l', xasm); ++ ++ if (sibcall_p) ++ { ++ output_asm_insn (push_buf, &call_op); ++ if (thunk_name != NULL) ++ { ++ if (need_bnd_p) ++ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name); ++ else ++ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name); ++ } ++ else ++ output_indirect_thunk (need_bnd_p, regno); ++ } ++ else ++ { ++ char indirectlabel1[32]; ++ char indirectlabel2[32]; ++ ++ ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, ++ INDIRECT_LABEL, ++ indirectlabelno++); ++ ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, ++ INDIRECT_LABEL, ++ indirectlabelno++); ++ ++ /* Jump. */ ++ if (need_bnd_p) ++ fputs ("\tbnd jmp\t", asm_out_file); ++ else ++ fputs ("\tjmp\t", asm_out_file); ++ assemble_name_raw (asm_out_file, indirectlabel2); ++ fputc ('\n', asm_out_file); ++ ++ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1); ++ ++ /* An external function may be called via GOT, instead of PLT. */ ++ if (MEM_P (call_op)) ++ { ++ struct ix86_address parts; ++ rtx addr = XEXP (call_op, 0); ++ if (ix86_decompose_address (addr, &parts) ++ && parts.base == stack_pointer_rtx) ++ { ++ /* Since call will adjust stack by -UNITS_PER_WORD, ++ we must convert "disp(stack, index, scale)" to ++ "disp+UNITS_PER_WORD(stack, index, scale)". */ ++ if (parts.index) ++ { ++ addr = gen_rtx_MULT (Pmode, parts.index, ++ GEN_INT (parts.scale)); ++ addr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, ++ addr); ++ } ++ else ++ addr = stack_pointer_rtx; ++ ++ rtx disp; ++ if (parts.disp != NULL_RTX) ++ disp = plus_constant (Pmode, parts.disp, ++ UNITS_PER_WORD); ++ else ++ disp = GEN_INT (UNITS_PER_WORD); ++ ++ addr = gen_rtx_PLUS (Pmode, addr, disp); ++ call_op = gen_rtx_MEM (GET_MODE (call_op), addr); ++ } ++ } ++ ++ output_asm_insn (push_buf, &call_op); ++ ++ if (thunk_name != NULL) ++ { ++ if (need_bnd_p) ++ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name); ++ else ++ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name); ++ } ++ else ++ output_indirect_thunk (need_bnd_p, regno); ++ ++ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2); ++ ++ /* Call. */ ++ if (need_bnd_p) ++ fputs ("\tbnd call\t", asm_out_file); ++ else ++ fputs ("\tcall\t", asm_out_file); ++ assemble_name_raw (asm_out_file, indirectlabel1); ++ fputc ('\n', asm_out_file); ++ } ++} ++ ++/* Output indirect branch via a call and return thunk. CALL_OP is ++ the branch target. XASM is the assembly template for CALL_OP. ++ Branch is a tail call if SIBCALL_P is true. */ ++ ++static void ++ix86_output_indirect_branch (rtx call_op, const char *xasm, ++ bool sibcall_p) ++{ ++ if (REG_P (call_op)) ++ ix86_output_indirect_branch_via_reg (call_op, sibcall_p); ++ else ++ ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p); ++} ++/* Output indirect jump. CALL_OP is the jump target. Jump is a ++ function return if RET_P is true. */ ++ ++const char * ++ix86_output_indirect_jmp (rtx call_op, bool ret_p) ++{ ++ if (cfun->machine->indirect_branch_type != indirect_branch_keep) ++ { ++ /* We can't have red-zone if this isn't a function return since ++ "call" in the indirect thunk pushes the return address onto ++ stack, destroying red-zone. */ ++ if (!ret_p && ix86_red_zone_size != 0) ++ gcc_unreachable (); ++ ++ ix86_output_indirect_branch (call_op, "%0", true); ++ return ""; ++ } ++ else ++ return "%!jmp\t%A0"; ++} ++ + /* Output the assembly for a call instruction. */ + + const char * + ix86_output_call_insn (rtx_insn *insn, rtx call_op) + { + bool direct_p = constant_call_address_operand (call_op, VOIDmode); ++ bool output_indirect_p ++ = (!TARGET_SEH ++ && cfun->machine->indirect_branch_type != indirect_branch_keep); + bool seh_nop_p = false; + const char *xasm; + +@@ -25823,9 +26386,17 @@ ix86_output_call_insn (rtx_insn *insn, r + else if (TARGET_SEH) + xasm = "%!rex.W jmp %A0"; + else +- xasm = "%!jmp\t%A0"; ++ { ++ if (output_indirect_p) ++ xasm = "%0"; ++ else ++ xasm = "%!jmp\t%A0"; ++ } + +- output_asm_insn (xasm, &call_op); ++ if (output_indirect_p && !direct_p) ++ ix86_output_indirect_branch (call_op, xasm, true); ++ else ++ output_asm_insn (xasm, &call_op); + return ""; + } + +@@ -25862,9 +26433,17 @@ ix86_output_call_insn (rtx_insn *insn, r + if (direct_p) + xasm = "%!call\t%P0"; + else +- xasm = "%!call\t%A0"; ++ { ++ if (output_indirect_p) ++ xasm = "%0"; ++ else ++ xasm = "%!call\t%A0"; ++ } + +- output_asm_insn (xasm, &call_op); ++ if (output_indirect_p && !direct_p) ++ ix86_output_indirect_branch (call_op, xasm, false); ++ else ++ output_asm_insn (xasm, &call_op); + + if (seh_nop_p) + return "nop"; +@@ -43014,7 +43593,7 @@ ix86_handle_struct_attribute (tree *node + } + + static tree +-ix86_handle_fndecl_attribute (tree *node, tree name, tree, int, ++ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int, + bool *no_add_attrs) + { + if (TREE_CODE (*node) != FUNCTION_DECL) +@@ -43023,6 +43602,29 @@ ix86_handle_fndecl_attribute (tree *node + name); + *no_add_attrs = true; + } ++ ++ if (is_attribute_p ("indirect_branch", name)) ++ { ++ tree cst = TREE_VALUE (args); ++ if (TREE_CODE (cst) != STRING_CST) ++ { ++ warning (OPT_Wattributes, ++ "%qE attribute requires a string constant argument", ++ name); ++ *no_add_attrs = true; ++ } ++ else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0 ++ && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0 ++ && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0 ++ && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0) ++ { ++ warning (OPT_Wattributes, ++ "argument to %qE attribute is not " ++ "(keep|thunk|thunk-inline|thunk-extern)", name); ++ *no_add_attrs = true; ++ } ++ } ++ + return NULL_TREE; + } + +@@ -46915,6 +47517,9 @@ static const struct attribute_spec ix86_ + false }, + { "callee_pop_aggregate_return", 1, 1, false, true, true, + ix86_handle_callee_pop_aggregate_return, true }, ++ { "indirect_branch", 1, 1, true, false, false, ++ ix86_handle_fndecl_attribute, false }, ++ + /* End element. */ + { NULL, 0, 0, false, false, false, NULL, false } + }; +Index: b/src/gcc/config/i386/i386.h +=================================================================== +--- a/src/gcc/config/i386/i386.h ++++ b/src/gcc/config/i386/i386.h +@@ -1229,6 +1229,9 @@ extern const char *host_detect_local_cpu + /* Base register for access to local variables of the function. */ + #define FRAME_POINTER_REGNUM 20 + ++#define FIRST_INT_REG AX_REG ++#define LAST_INT_REG SP_REG ++ + /* First floating point reg */ + #define FIRST_FLOAT_REG 8 + +@@ -1465,6 +1468,8 @@ enum reg_class + registers. */ + #define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P hook_bool_mode_true + ++#define LEGACY_INT_REGNO_P(N) (IN_RANGE ((N), FIRST_INT_REG, LAST_INT_REG)) ++ + #define QI_REG_P(X) (REG_P (X) && QI_REGNO_P (REGNO (X))) + #define QI_REGNO_P(N) IN_RANGE ((N), AX_REG, BX_REG) + +@@ -2550,6 +2555,13 @@ struct GTY(()) machine_function { + /* If true, it is safe to not save/restore DRAP register. */ + BOOL_BITFIELD no_drap_save_restore : 1; + ++ /* How to generate indirec branch. */ ++ ENUM_BITFIELD(indirect_branch) indirect_branch_type : 3; ++ ++ /* If true, the current function has local indirect jumps, like ++ "indirect_jump" or "tablejump". */ ++ BOOL_BITFIELD has_local_indirect_jump : 1; ++ + /* During prologue/epilogue generation, the current frame state. + Otherwise, the frame state at the end of the prologue. */ + struct machine_frame_state fs; +Index: b/src/gcc/config/i386/i386.md +=================================================================== +--- a/src/gcc/config/i386/i386.md ++++ b/src/gcc/config/i386/i386.md +@@ -11556,13 +11556,18 @@ + { + if (TARGET_X32) + operands[0] = convert_memory_address (word_mode, operands[0]); ++ cfun->machine->has_local_indirect_jump = true; + }) + + (define_insn "*indirect_jump" + [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))] + "" +- "%!jmp\t%A0" +- [(set_attr "type" "ibr") ++ "* return ix86_output_indirect_jmp (operands[0], false);" ++ [(set (attr "type") ++ (if_then_else (match_test "(cfun->machine->indirect_branch_type ++ != indirect_branch_keep)") ++ (const_string "multi") ++ (const_string "ibr"))) + (set_attr "length_immediate" "0")]) + + (define_expand "tablejump" +@@ -11604,14 +11609,19 @@ + + if (TARGET_X32) + operands[0] = convert_memory_address (word_mode, operands[0]); ++ cfun->machine->has_local_indirect_jump = true; + }) + + (define_insn "*tablejump_1" + [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw")) + (use (label_ref (match_operand 1)))] + "" +- "%!jmp\t%A0" +- [(set_attr "type" "ibr") ++ "* return ix86_output_indirect_jmp (operands[0], false);" ++ [(set (attr "type") ++ (if_then_else (match_test "(cfun->machine->indirect_branch_type ++ != indirect_branch_keep)") ++ (const_string "multi") ++ (const_string "ibr"))) + (set_attr "length_immediate" "0")]) + + ;; Convert setcc + movzbl to xor + setcc if operands don't overlap. +@@ -12198,8 +12208,12 @@ + [(simple_return) + (use (match_operand:SI 0 "register_operand" "r"))] + "reload_completed" +- "%!jmp\t%A0" +- [(set_attr "type" "ibr") ++ "* return ix86_output_indirect_jmp (operands[0], true);" ++ [(set (attr "type") ++ (if_then_else (match_test "(cfun->machine->indirect_branch_type ++ != indirect_branch_keep)") ++ (const_string "multi") ++ (const_string "ibr"))) + (set_attr "length_immediate" "0")]) + + (define_insn "nop" +Index: b/src/gcc/config/i386/i386.opt +=================================================================== +--- a/src/gcc/config/i386/i386.opt ++++ b/src/gcc/config/i386/i386.opt +@@ -876,3 +876,23 @@ Enum(stack_protector_guard) String(tls) + + EnumValue + Enum(stack_protector_guard) String(global) Value(SSP_GLOBAL) ++ ++mindirect-branch= ++Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep) ++Convert indirect call and jump to call and return thunks. ++ ++Enum ++Name(indirect_branch) Type(enum indirect_branch) ++Known indirect branch choices (for use with the -mindirect-branch= option): ++ ++EnumValue ++Enum(indirect_branch) String(keep) Value(indirect_branch_keep) ++ ++EnumValue ++Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk) ++ ++EnumValue ++Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline) ++ ++EnumValue ++Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern) +Index: b/src/gcc/doc/extend.texi +=================================================================== +--- a/src/gcc/doc/extend.texi ++++ b/src/gcc/doc/extend.texi +@@ -4119,6 +4119,16 @@ Specify which floating-point unit to use + @code{target("fpmath=sse,387")} option must be specified as + @code{target("fpmath=sse+387")} because the comma would separate + different options. ++ ++@item indirect_branch("@var{choice}") ++@cindex @code{indirect_branch} function attribute, x86 ++On x86 targets, the @code{indirect_branch} attribute causes the compiler ++to convert indirect call and jump with @var{choice}. @samp{keep} ++keeps indirect call and jump unmodified. @samp{thunk} converts indirect ++call and jump to call and return thunk. @samp{thunk-inline} converts ++indirect call and jump to inlined call and return thunk. ++@samp{thunk-extern} converts indirect call and jump to external call ++and return thunk provided in a separate object file. + @end table + + On the PowerPC, the following options are allowed: +Index: b/src/gcc/doc/invoke.texi +=================================================================== +--- a/src/gcc/doc/invoke.texi ++++ b/src/gcc/doc/invoke.texi +@@ -1090,7 +1090,8 @@ See RS/6000 and PowerPC Options. + -m32 -m64 -mx32 -m16 -mlarge-data-threshold=@var{num} @gol + -msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol + -mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol +--malign-data=@var{type} -mstack-protector-guard=@var{guard}} ++-malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol ++-mindirect-branch=@var{choice}} + + @emph{x86 Windows Options} + @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol +@@ -24017,6 +24018,17 @@ The default value of this option is enab + of the option is @option{-fno-sync-libcalls}. This option is used in + the implementation of the @file{libatomic} runtime library. + ++@item -mindirect-branch=@var{choice} ++@opindex -mindirect-branch ++Convert indirect call and jump with @var{choice}. The default is ++@samp{keep}, which keeps indirect call and jump unmodified. ++@samp{thunk} converts indirect call and jump to call and return thunk. ++@samp{thunk-inline} converts indirect call and jump to inlined call ++and return thunk. @samp{thunk-extern} converts indirect call and jump ++to external call and return thunk provided in a separate object file. ++You can control this behavior for a specific function by using the ++function attribute @code{indirect_branch}. @xref{Function Attributes}. ++ + @end table + + @c man end +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c +@@ -0,0 +1,20 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c +@@ -0,0 +1,20 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c +@@ -0,0 +1,21 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++int ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c +@@ -0,0 +1,21 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++int ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c +@@ -0,0 +1,44 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++ ++void func0 (void); ++void func1 (void); ++void func2 (void); ++void func3 (void); ++void func4 (void); ++void func4 (void); ++void func5 (void); ++ ++void ++bar (int i) ++{ ++ switch (i) ++ { ++ default: ++ func0 (); ++ break; ++ case 1: ++ func1 (); ++ break; ++ case 2: ++ func2 (); ++ break; ++ case 3: ++ func3 (); ++ break; ++ case 4: ++ func4 (); ++ break; ++ case 5: ++ func5 (); ++ break; ++ } ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c +@@ -0,0 +1,23 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++extern void male_indirect_jump (long) ++ __attribute__ ((indirect_branch("thunk"))); ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c +@@ -0,0 +1,21 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++__attribute__ ((indirect_branch("thunk"))) ++void ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c +@@ -0,0 +1,23 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++extern int male_indirect_jump (long) ++ __attribute__ ((indirect_branch("thunk-inline"))); ++ ++int ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c +@@ -0,0 +1,22 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++__attribute__ ((indirect_branch("thunk-inline"))) ++int ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c +@@ -0,0 +1,22 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++extern int male_indirect_jump (long) ++ __attribute__ ((indirect_branch("thunk-extern"))); ++ ++int ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c +@@ -0,0 +1,21 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++__attribute__ ((indirect_branch("thunk-extern"))) ++int ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c +@@ -0,0 +1,44 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -fno-pic" } */ ++ ++void func0 (void); ++void func1 (void); ++void func2 (void); ++void func3 (void); ++void func4 (void); ++void func4 (void); ++void func5 (void); ++ ++__attribute__ ((indirect_branch("thunk-extern"))) ++void ++bar (int i) ++{ ++ switch (i) ++ { ++ default: ++ func0 (); ++ break; ++ case 1: ++ func1 (); ++ break; ++ case 2: ++ func2 (); ++ break; ++ case 3: ++ func3 (); ++ break; ++ case 4: ++ func4 (); ++ break; ++ case 5: ++ func5 (); ++ break; ++ } ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c +@@ -0,0 +1,42 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++ ++void func0 (void); ++void func1 (void); ++void func2 (void); ++void func3 (void); ++void func4 (void); ++void func4 (void); ++void func5 (void); ++ ++__attribute__ ((indirect_branch("keep"))) ++void ++bar (int i) ++{ ++ switch (i) ++ { ++ default: ++ func0 (); ++ break; ++ case 1: ++ func1 (); ++ break; ++ case 2: ++ func2 (); ++ break; ++ case 3: ++ func3 (); ++ break; ++ case 4: ++ func4 (); ++ break; ++ case 5: ++ func5 (); ++ break; ++ } ++} ++ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c +@@ -0,0 +1,20 @@ ++/* { dg-do compile { target { ! x32 } } } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ ++ ++void (*dispatch) (char *); ++char buf[10]; ++ ++void ++foo (void) ++{ ++ dispatch (buf); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ ++/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "bnd ret" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c +@@ -0,0 +1,21 @@ ++/* { dg-do compile { target { ! x32 } } } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ ++ ++void (*dispatch) (char *); ++char buf[10]; ++ ++int ++foo (void) ++{ ++ dispatch (buf); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ ++/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */ ++/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "bnd call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "bnd ret" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c +@@ -0,0 +1,19 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c +@@ -0,0 +1,19 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c +@@ -0,0 +1,20 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++int ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c +@@ -0,0 +1,20 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++int ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c +@@ -0,0 +1,43 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++ ++void func0 (void); ++void func1 (void); ++void func2 (void); ++void func3 (void); ++void func4 (void); ++void func4 (void); ++void func5 (void); ++ ++void ++bar (int i) ++{ ++ switch (i) ++ { ++ default: ++ func0 (); ++ break; ++ case 1: ++ func1 (); ++ break; ++ case 2: ++ func2 (); ++ break; ++ case 3: ++ func3 (); ++ break; ++ case 4: ++ func4 (); ++ break; ++ case 5: ++ func5 (); ++ break; ++ } ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c +@@ -0,0 +1,20 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c +@@ -0,0 +1,20 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c +@@ -0,0 +1,21 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++int ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ ++/* { dg-final { scan-assembler-times {\tpause} 1 } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 1 } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c +@@ -0,0 +1,21 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch[256]; ++ ++int ++male_indirect_jump (long offset) ++{ ++ dispatch[offset](offset); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ ++/* { dg-final { scan-assembler-times {\tpause} 1 } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 1 } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c +@@ -0,0 +1,44 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++ ++void func0 (void); ++void func1 (void); ++void func2 (void); ++void func3 (void); ++void func4 (void); ++void func4 (void); ++void func5 (void); ++ ++void ++bar (int i) ++{ ++ switch (i) ++ { ++ default: ++ func0 (); ++ break; ++ case 1: ++ func1 (); ++ break; ++ case 2: ++ func2 (); ++ break; ++ case 3: ++ func3 (); ++ break; ++ case 4: ++ func4 (); ++ break; ++ case 5: ++ func5 (); ++ break; ++ } ++} ++ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0006-x86-Add-mfunction-return.diff +++ gcc-5-5.4.0/debian/patches/0006-x86-Add-mfunction-return.diff @@ -0,0 +1,1460 @@ +From 357311dd400f7f72d2132f2f94161ece39bf08c6 Mon Sep 17 00:00:00 2001 +From: hjl +Date: Tue, 16 Jan 2018 11:10:44 +0000 +Subject: [PATCH 6/9] x86: Add -mfunction-return= + +Add -mfunction-return= option to convert function return to call and +return thunks. The default is 'keep', which keeps function return +unmodified. 'thunk' converts function return to call and return thunk. +'thunk-inline' converts function return to inlined call and return thunk. +'thunk-extern' converts function return to external call and return +thunk provided in a separate object file. You can control this behavior +for a specific function by using the function attribute function_return. + +Function return thunk is the same as memory thunk for -mindirect-branch= +where the return address is at the top of the stack: + +__x86_return_thunk: + call L2 +L1: + pause + lfence + jmp L1 +L2: + lea 8(%rsp), %rsp|lea 4(%esp), %esp + ret + +and function return becomes + + jmp __x86_return_thunk + +-mindirect-branch= tests are updated with -mfunction-return=keep to +avoid false test failures when -mfunction-return=thunk is added to +RUNTESTFLAGS for "make check". + +gcc/ + + Backport from mainline + 2018-01-14 H.J. Lu + + * config/i386/i386-protos.h (ix86_output_function_return): New. + * config/i386/i386.c (ix86_set_indirect_branch_type): Also + set function_return_type. + (indirect_thunk_name): Add ret_p to indicate thunk for function + return. + (output_indirect_thunk_function): Pass false to + indirect_thunk_name. + (ix86_output_indirect_branch_via_reg): Likewise. + (ix86_output_indirect_branch_via_push): Likewise. + (output_indirect_thunk_function): Create alias for function + return thunk if regno < 0. + (ix86_output_function_return): New function. + (ix86_handle_fndecl_attribute): Handle function_return. + (ix86_attribute_table): Add function_return. + * config/i386/i386.h (machine_function): Add + function_return_type. + * config/i386/i386.md (simple_return_internal): Use + ix86_output_function_return. + (simple_return_internal_long): Likewise. + * config/i386/i386.opt (mfunction-return=): New option. + (indirect_branch): Mention -mfunction-return=. + * doc/extend.texi: Document function_return function attribute. + * doc/invoke.texi: Document -mfunction-return= option. + +gcc/testsuite/ + + Backport from mainline + 2018-01-14 H.J. Lu + + * gcc.target/i386/indirect-thunk-1.c (dg-options): Add + -mfunction-return=keep. + * gcc.target/i386/indirect-thunk-2.c: Likewise. + * gcc.target/i386/indirect-thunk-3.c: Likewise. + * gcc.target/i386/indirect-thunk-4.c: Likewise. + * gcc.target/i386/indirect-thunk-5.c: Likewise. + * gcc.target/i386/indirect-thunk-6.c: Likewise. + * gcc.target/i386/indirect-thunk-7.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-1.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-2.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-3.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-4.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-5.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-6.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-7.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-8.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-1.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-2.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-3.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-4.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-1.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-2.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-3.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-4.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-5.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-6.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-7.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-1.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-2.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-3.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-4.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-5.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-6.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-7.c: Likewise. + * gcc.target/i386/ret-thunk-1.c: New test. + * gcc.target/i386/ret-thunk-10.c: Likewise. + * gcc.target/i386/ret-thunk-11.c: Likewise. + * gcc.target/i386/ret-thunk-12.c: Likewise. + * gcc.target/i386/ret-thunk-13.c: Likewise. + * gcc.target/i386/ret-thunk-14.c: Likewise. + * gcc.target/i386/ret-thunk-15.c: Likewise. + * gcc.target/i386/ret-thunk-16.c: Likewise. + * gcc.target/i386/ret-thunk-2.c: Likewise. + * gcc.target/i386/ret-thunk-3.c: Likewise. + * gcc.target/i386/ret-thunk-4.c: Likewise. + * gcc.target/i386/ret-thunk-5.c: Likewise. + * gcc.target/i386/ret-thunk-6.c: Likewise. + * gcc.target/i386/ret-thunk-7.c: Likewise. + * gcc.target/i386/ret-thunk-8.c: Likewise. + * gcc.target/i386/ret-thunk-9.c: Likewise. + +i386: Don't use ASM_OUTPUT_DEF for TARGET_MACHO + +ASM_OUTPUT_DEF isn't defined for TARGET_MACHO. Use ASM_OUTPUT_LABEL to +generate the __x86_return_thunk label, instead of the set directive. +Update testcase to remove the __x86_return_thunk label check. Since +-fno-pic is ignored on Darwin, update testcases to sscan or "push" +only on Linux. + +gcc/ + + Backport from mainline + 2018-01-15 H.J. Lu + + PR target/83839 + * config/i386/i386.c (output_indirect_thunk_function): Use + ASM_OUTPUT_LABEL, instead of ASM_OUTPUT_DEF, for TARGET_MACHO + for __x86.return_thunk. + +gcc/testsuite/ + + Backport from mainline + 2018-01-15 H.J. Lu + + PR target/83839 + * gcc.target/i386/indirect-thunk-1.c: Scan for "push" only on + Linux. + * gcc.target/i386/indirect-thunk-2.c: Likewise. + * gcc.target/i386/indirect-thunk-3.c: Likewise. + * gcc.target/i386/indirect-thunk-4.c: Likewise. + * gcc.target/i386/indirect-thunk-7.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-1.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-2.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-5.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-6.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-7.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-1.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-2.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-3.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-4.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-7.c: Likewise. + * gcc.target/i386/indirect-thunk-register-1.c: Likewise. + * gcc.target/i386/indirect-thunk-register-3.c: Likewise. + * gcc.target/i386/indirect-thunk-register-4.c: Likewise. + * gcc.target/i386/ret-thunk-10.c: Likewise. + * gcc.target/i386/ret-thunk-11.c: Likewise. + * gcc.target/i386/ret-thunk-12.c: Likewise. + * gcc.target/i386/ret-thunk-13.c: Likewise. + * gcc.target/i386/ret-thunk-14.c: Likewise. + * gcc.target/i386/ret-thunk-15.c: Likewise. + * gcc.target/i386/ret-thunk-9.c: Don't check the + __x86_return_thunk label. + Scan for "push" only for Linux. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256734 138bc75d-0d04-0410-961f-82ee72b054a4 + +[Ubuntu note: Dropped indirect-thunk-5.c, indirect-thunk-6.c, + indirect-thunk-bnd-3.c, indirect-thunk-bnd-4.c, + indirect-thunk-extern-5.c, indirect-thunk-extern-6.c, + indirect-thunk-inline-5.c, and indirect-thunk-inline-6.c tests due + to gcc 5.4 and earlier not supporting the -fno-plt option. + --sbeattie,] + +--- + src/gcc/config/i386/i386-protos.h | 1 + src/gcc/config/i386/i386.c | 152 +++++++++++- + src/gcc/config/i386/i386.h | 3 + src/gcc/config/i386/i386.md | 9 + src/gcc/config/i386/i386.opt | 6 + src/gcc/doc/extend.texi | 9 + src/gcc/doc/invoke.texi | 13 - + src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c | 4 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c | 4 + src/gcc/testsuite/gcc.target/i386/ret-thunk-1.c | 13 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-10.c | 23 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-11.c | 23 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-12.c | 22 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-13.c | 22 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-14.c | 22 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-15.c | 22 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-16.c | 18 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-2.c | 13 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-3.c | 12 + src/gcc/testsuite/gcc.target/i386/ret-thunk-4.c | 12 + src/gcc/testsuite/gcc.target/i386/ret-thunk-5.c | 15 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-6.c | 14 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-7.c | 13 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-8.c | 14 + + src/gcc/testsuite/gcc.target/i386/ret-thunk-9.c | 24 + + 48 files changed, 507 insertions(+), 66 deletions(-) + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-1.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-10.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-11.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-12.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-13.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-14.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-15.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-16.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-2.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-3.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-4.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-5.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-6.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-7.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-8.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-9.c + +Index: b/src/gcc/config/i386/i386-protos.h +=================================================================== +--- a/src/gcc/config/i386/i386-protos.h ++++ b/src/gcc/config/i386/i386-protos.h +@@ -307,6 +307,7 @@ extern enum attr_cpu ix86_schedule; + + extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op); + extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p); ++extern const char * ix86_output_function_return (bool long_p); + + #ifdef RTX_CODE + /* Target data for multipass lookahead scheduling. +Index: b/src/gcc/config/i386/i386.c +=================================================================== +--- a/src/gcc/config/i386/i386.c ++++ b/src/gcc/config/i386/i386.c +@@ -5166,6 +5166,31 @@ ix86_set_indirect_branch_type (tree fnde + else + cfun->machine->indirect_branch_type = ix86_indirect_branch; + } ++ ++ if (cfun->machine->function_return_type == indirect_branch_unset) ++ { ++ tree attr = lookup_attribute ("function_return", ++ DECL_ATTRIBUTES (fndecl)); ++ if (attr != NULL) ++ { ++ tree args = TREE_VALUE (attr); ++ if (args == NULL) ++ gcc_unreachable (); ++ tree cst = TREE_VALUE (args); ++ if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0) ++ cfun->machine->function_return_type = indirect_branch_keep; ++ else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0) ++ cfun->machine->function_return_type = indirect_branch_thunk; ++ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0) ++ cfun->machine->function_return_type = indirect_branch_thunk_inline; ++ else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0) ++ cfun->machine->function_return_type = indirect_branch_thunk_extern; ++ else ++ gcc_unreachable (); ++ } ++ else ++ cfun->machine->function_return_type = ix86_function_return; ++ } + } + + /* Establish appropriate back-end context for processing the function +@@ -9753,8 +9778,12 @@ static int indirect_thunks_bnd_used; + /* Fills in the label name that should be used for the indirect thunk. */ + + static void +-indirect_thunk_name (char name[32], int regno, bool need_bnd_p) ++indirect_thunk_name (char name[32], int regno, bool need_bnd_p, ++ bool ret_p) + { ++ if (regno >= 0 && ret_p) ++ gcc_unreachable (); ++ + if (USE_HIDDEN_LINKONCE) + { + const char *bnd = need_bnd_p ? "_bnd" : ""; +@@ -9769,7 +9798,10 @@ indirect_thunk_name (char name[32], int + bnd, reg_prefix, reg_names[regno]); + } + else +- sprintf (name, "__x86_indirect_thunk%s", bnd); ++ { ++ const char *ret = ret_p ? "return" : "indirect"; ++ sprintf (name, "__x86_%s_thunk%s", ret, bnd); ++ } + } + else + { +@@ -9782,10 +9814,20 @@ indirect_thunk_name (char name[32], int + } + else + { +- if (need_bnd_p) +- ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0); ++ if (ret_p) ++ { ++ if (need_bnd_p) ++ ASM_GENERATE_INTERNAL_LABEL (name, "LRTB", 0); ++ else ++ ASM_GENERATE_INTERNAL_LABEL (name, "LRT", 0); ++ } + else +- ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0); ++ { ++ if (need_bnd_p) ++ ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0); ++ else ++ ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0); ++ } + } + } + } +@@ -9880,7 +9922,7 @@ output_indirect_thunk_function (bool nee + tree decl; + + /* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd. */ +- indirect_thunk_name (name, regno, need_bnd_p); ++ indirect_thunk_name (name, regno, need_bnd_p, false); + decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL, + get_identifier (name), + build_function_type_list (void_type_node, NULL_TREE)); +@@ -9923,6 +9965,36 @@ output_indirect_thunk_function (bool nee + ASM_OUTPUT_LABEL (asm_out_file, name); + } + ++ if (regno < 0) ++ { ++ /* Create alias for __x86.return_thunk/__x86.return_thunk_bnd. */ ++ char alias[32]; ++ ++ indirect_thunk_name (alias, regno, need_bnd_p, true); ++#if TARGET_MACHO ++ if (TARGET_MACHO) ++ { ++ fputs ("\t.weak_definition\t", asm_out_file); ++ assemble_name (asm_out_file, alias); ++ fputs ("\n\t.private_extern\t", asm_out_file); ++ assemble_name (asm_out_file, alias); ++ putc ('\n', asm_out_file); ++ ASM_OUTPUT_LABEL (asm_out_file, alias); ++ } ++#else ++ ASM_OUTPUT_DEF (asm_out_file, alias, name); ++ if (USE_HIDDEN_LINKONCE) ++ { ++ fputs ("\t.globl\t", asm_out_file); ++ assemble_name (asm_out_file, alias); ++ putc ('\n', asm_out_file); ++ fputs ("\t.hidden\t", asm_out_file); ++ assemble_name (asm_out_file, alias); ++ putc ('\n', asm_out_file); ++ } ++#endif ++ } ++ + DECL_INITIAL (decl) = make_node (BLOCK); + current_function_decl = decl; + allocate_struct_function (decl, false); +@@ -26121,7 +26193,7 @@ ix86_output_indirect_branch_via_reg (rtx + else + indirect_thunks_used |= 1 << i; + } +- indirect_thunk_name (thunk_name_buf, regno, need_bnd_p); ++ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false); + thunk_name = thunk_name_buf; + } + else +@@ -26230,7 +26302,7 @@ ix86_output_indirect_branch_via_push (rt + else + indirect_thunk_needed = true; + } +- indirect_thunk_name (thunk_name_buf, regno, need_bnd_p); ++ indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false); + thunk_name = thunk_name_buf; + } + else +@@ -26365,6 +26437,46 @@ ix86_output_indirect_jmp (rtx call_op, b + return "%!jmp\t%A0"; + } + ++/* Output function return. CALL_OP is the jump target. Add a REP ++ prefix to RET if LONG_P is true and function return is kept. */ ++ ++const char * ++ix86_output_function_return (bool long_p) ++{ ++ if (cfun->machine->function_return_type != indirect_branch_keep) ++ { ++ char thunk_name[32]; ++ bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn); ++ ++ if (cfun->machine->function_return_type ++ != indirect_branch_thunk_inline) ++ { ++ bool need_thunk = (cfun->machine->function_return_type ++ == indirect_branch_thunk); ++ indirect_thunk_name (thunk_name, -1, need_bnd_p, true); ++ if (need_bnd_p) ++ { ++ indirect_thunk_bnd_needed |= need_thunk; ++ fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name); ++ } ++ else ++ { ++ indirect_thunk_needed |= need_thunk; ++ fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name); ++ } ++ } ++ else ++ output_indirect_thunk (need_bnd_p, -1); ++ ++ return ""; ++ } ++ ++ if (!long_p || ix86_bnd_prefixed_insn_p (current_output_insn)) ++ return "%!ret"; ++ ++ return "rep%; ret"; ++} ++ + /* Output the assembly for a call instruction. */ + + const char * +@@ -43625,6 +43737,28 @@ ix86_handle_fndecl_attribute (tree *node + } + } + ++ if (is_attribute_p ("function_return", name)) ++ { ++ tree cst = TREE_VALUE (args); ++ if (TREE_CODE (cst) != STRING_CST) ++ { ++ warning (OPT_Wattributes, ++ "%qE attribute requires a string constant argument", ++ name); ++ *no_add_attrs = true; ++ } ++ else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0 ++ && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0 ++ && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0 ++ && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0) ++ { ++ warning (OPT_Wattributes, ++ "argument to %qE attribute is not " ++ "(keep|thunk|thunk-inline|thunk-extern)", name); ++ *no_add_attrs = true; ++ } ++ } ++ + return NULL_TREE; + } + +@@ -47519,6 +47653,8 @@ static const struct attribute_spec ix86_ + ix86_handle_callee_pop_aggregate_return, true }, + { "indirect_branch", 1, 1, true, false, false, + ix86_handle_fndecl_attribute, false }, ++ { "function_return", 1, 1, true, false, false, ++ ix86_handle_fndecl_attribute, false }, + + /* End element. */ + { NULL, 0, 0, false, false, false, NULL, false } +Index: b/src/gcc/config/i386/i386.h +=================================================================== +--- a/src/gcc/config/i386/i386.h ++++ b/src/gcc/config/i386/i386.h +@@ -2562,6 +2562,9 @@ struct GTY(()) machine_function { + "indirect_jump" or "tablejump". */ + BOOL_BITFIELD has_local_indirect_jump : 1; + ++ /* How to generate function return. */ ++ ENUM_BITFIELD(indirect_branch) function_return_type : 3; ++ + /* During prologue/epilogue generation, the current frame state. + Otherwise, the frame state at the end of the prologue. */ + struct machine_frame_state fs; +Index: b/src/gcc/config/i386/i386.md +=================================================================== +--- a/src/gcc/config/i386/i386.md ++++ b/src/gcc/config/i386/i386.md +@@ -12169,7 +12169,7 @@ + (define_insn "simple_return_internal" + [(simple_return)] + "reload_completed" +- "%!ret" ++ "* return ix86_output_function_return (false);" + [(set_attr "length_nobnd" "1") + (set_attr "atom_unit" "jeu") + (set_attr "length_immediate" "0") +@@ -12182,12 +12182,7 @@ + [(simple_return) + (unspec [(const_int 0)] UNSPEC_REP)] + "reload_completed" +-{ +- if (ix86_bnd_prefixed_insn_p (insn)) +- return "%!ret"; +- +- return "rep%; ret"; +-} ++ "* return ix86_output_function_return (true);" + [(set_attr "length" "2") + (set_attr "atom_unit" "jeu") + (set_attr "length_immediate" "0") +Index: b/src/gcc/config/i386/i386.opt +=================================================================== +--- a/src/gcc/config/i386/i386.opt ++++ b/src/gcc/config/i386/i386.opt +@@ -881,9 +881,13 @@ mindirect-branch= + Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_indirect_branch) Init(indirect_branch_keep) + Convert indirect call and jump to call and return thunks. + ++mfunction-return= ++Target Report RejectNegative Joined Enum(indirect_branch) Var(ix86_function_return) Init(indirect_branch_keep) ++Convert function return to call and return thunk. ++ + Enum + Name(indirect_branch) Type(enum indirect_branch) +-Known indirect branch choices (for use with the -mindirect-branch= option): ++Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options): + + EnumValue + Enum(indirect_branch) String(keep) Value(indirect_branch_keep) +Index: b/src/gcc/doc/extend.texi +=================================================================== +--- a/src/gcc/doc/extend.texi ++++ b/src/gcc/doc/extend.texi +@@ -4129,6 +4129,15 @@ call and jump to call and return thunk. + indirect call and jump to inlined call and return thunk. + @samp{thunk-extern} converts indirect call and jump to external call + and return thunk provided in a separate object file. ++ ++@item function_return("@var{choice}") ++@cindex @code{function_return} function attribute, x86 ++On x86 targets, the @code{function_return} attribute causes the compiler ++to convert function return with @var{choice}. @samp{keep} keeps function ++return unmodified. @samp{thunk} converts function return to call and ++return thunk. @samp{thunk-inline} converts function return to inlined ++call and return thunk. @samp{thunk-extern} converts function return to ++external call and return thunk provided in a separate object file. + @end table + + On the PowerPC, the following options are allowed: +Index: b/src/gcc/doc/invoke.texi +=================================================================== +--- a/src/gcc/doc/invoke.texi ++++ b/src/gcc/doc/invoke.texi +@@ -1091,7 +1091,7 @@ See RS/6000 and PowerPC Options. + -msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol + -mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol + -malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol +--mindirect-branch=@var{choice}} ++-mindirect-branch=@var{choice} -mfunction-return=@var{choice}} + + @emph{x86 Windows Options} + @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol +@@ -24029,6 +24029,17 @@ to external call and return thunk provid + You can control this behavior for a specific function by using the + function attribute @code{indirect_branch}. @xref{Function Attributes}. + ++@item -mfunction-return=@var{choice} ++@opindex -mfunction-return ++Convert function return with @var{choice}. The default is @samp{keep}, ++which keeps function return unmodified. @samp{thunk} converts function ++return to call and return thunk. @samp{thunk-inline} converts function ++return to inlined call and return thunk. @samp{thunk-extern} converts ++function return to external call and return thunk provided in a separate ++object file. You can control this behavior for a specific function by ++using the function attribute @code{function_return}. ++@xref{Function Attributes}. ++ + @end table + + @c man end +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -11,7 +11,7 @@ male_indirect_jump (long offset) + dispatch(offset); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -11,7 +11,7 @@ male_indirect_jump (long offset) + dispatch[offset](offset); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -12,7 +12,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -12,7 +12,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + void func0 (void); + void func1 (void); +@@ -35,7 +35,7 @@ bar (int i) + } + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -14,7 +14,7 @@ male_indirect_jump (long offset) + dispatch(offset); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -12,7 +12,7 @@ male_indirect_jump (long offset) + dispatch[offset](offset); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -14,7 +14,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ + /* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ + /* { dg-final { scan-assembler {\tpause} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -13,7 +13,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ + /* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ + /* { dg-final { scan-assembler {\tpause} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -14,7 +14,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ + /* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -13,7 +13,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ + /* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ + + void func0 (void); + void func1 (void); +@@ -36,7 +36,7 @@ bar (int i) + } + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */ + /* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + void func0 (void); + void func1 (void); +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile { target { ! x32 } } } */ +-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ + + void (*dispatch) (char *); + char buf[10]; +@@ -10,7 +10,7 @@ foo (void) + dispatch (buf); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ + /* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile { target { ! x32 } } } */ +-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ + + void (*dispatch) (char *); + char buf[10]; +@@ -11,7 +11,7 @@ foo (void) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ + /* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk_bnd" } } */ + /* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -11,7 +11,7 @@ male_indirect_jump (long offset) + dispatch(offset); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -11,7 +11,7 @@ male_indirect_jump (long offset) + dispatch[offset](offset); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -12,7 +12,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ + /* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -12,7 +12,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ + /* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 { target { ! x32 } } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + void func0 (void); + void func1 (void); +@@ -35,7 +35,7 @@ bar (int i) + } + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ + /* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -11,7 +11,7 @@ male_indirect_jump (long offset) + dispatch(offset); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ + /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ + /* { dg-final { scan-assembler {\tpause} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -11,7 +11,7 @@ male_indirect_jump (long offset) + dispatch[offset](offset); + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ + /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ + /* { dg-final { scan-assembler {\tpause} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -12,7 +12,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ + /* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ + /* { dg-final { scan-assembler-times {\tpause} 1 } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +@@ -12,7 +12,7 @@ male_indirect_jump (long offset) + return 0; + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ + /* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ + /* { dg-final { scan-assembler-times {\tpause} 1 } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + void func0 (void); + void func1 (void); +@@ -35,7 +35,7 @@ bar (int i) + } + } + +-/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { { ! x32 } && *-*-linux* } } } } */ + /* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ + /* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ + /* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-1.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-1.c +@@ -0,0 +1,13 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=thunk" } */ ++ ++void ++foo (void) ++{ ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-10.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-10.c +@@ -0,0 +1,23 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */ ++ ++extern void (*bar) (void); ++ ++int ++foo (void) ++{ ++ bar (); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-times {\tpause} 2 } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 2 } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { { ! x32 } && *-*-linux* } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */ ++/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-11.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-11.c +@@ -0,0 +1,23 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */ ++ ++extern void (*bar) (void); ++ ++int ++foo (void) ++{ ++ bar (); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-times {\tpause} 1 } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 1 } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { { ! x32 } && *-*-linux* } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */ ++/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-12.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-12.c +@@ -0,0 +1,22 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ ++ ++extern void (*bar) (void); ++ ++int ++foo (void) ++{ ++ bar (); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-times {\tpause} 1 } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 1 } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "__x86_indirect_thunk:" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */ ++/* { dg-final { scan-assembler "__x86_indirect_thunk_(r|e)ax:" { target { x32 } } } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-13.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-13.c +@@ -0,0 +1,22 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ ++ ++extern void (*bar) (void); ++extern int foo (void) __attribute__ ((function_return("thunk"))); ++ ++int ++foo (void) ++{ ++ bar (); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-times {\tpause} 2 } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 2 } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { { ! x32 } && *-*-linux* } } } } */ ++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 3 } } */ ++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 3 } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-14.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-14.c +@@ -0,0 +1,22 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ ++ ++extern void (*bar) (void); ++ ++__attribute__ ((function_return("thunk-inline"))) ++int ++foo (void) ++{ ++ bar (); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler-times {\tpause} 1 } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 1 } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { { ! x32 } && *-*-linux* } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-15.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-15.c +@@ -0,0 +1,22 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */ ++ ++extern void (*bar) (void); ++ ++__attribute__ ((function_return("thunk-extern"), indirect_branch("thunk"))) ++int ++foo (void) ++{ ++ bar (); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-times {\tpause} 1 } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 1 } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { { ! x32 } && *-*-linux* } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target x32 } } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-16.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-16.c +@@ -0,0 +1,18 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk-extern -fno-pic" } */ ++ ++extern void (*bar) (void); ++ ++__attribute__ ((function_return("keep"), indirect_branch("keep"))) ++int ++foo (void) ++{ ++ bar (); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ ++/* { dg-final { scan-assembler-not "__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-2.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-2.c +@@ -0,0 +1,13 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=thunk-inline" } */ ++ ++void ++foo (void) ++{ ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-3.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-3.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=thunk-extern" } */ ++ ++void ++foo (void) ++{ ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-4.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-4.c +@@ -0,0 +1,12 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=keep" } */ ++ ++void ++foo (void) ++{ ++} ++ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-5.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-5.c +@@ -0,0 +1,15 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=keep" } */ ++ ++extern void foo (void) __attribute__ ((function_return("thunk"))); ++ ++void ++foo (void) ++{ ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-6.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-6.c +@@ -0,0 +1,14 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=keep" } */ ++ ++__attribute__ ((function_return("thunk-inline"))) ++void ++foo (void) ++{ ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler {\tlfence} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-7.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-7.c +@@ -0,0 +1,13 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=keep" } */ ++ ++__attribute__ ((function_return("thunk-extern"))) ++void ++foo (void) ++{ ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-8.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-8.c +@@ -0,0 +1,14 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=thunk-inline" } */ ++ ++extern void foo (void) __attribute__ ((function_return("keep"))); ++ ++void ++foo (void) ++{ ++} ++ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-9.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-9.c +@@ -0,0 +1,24 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */ ++ ++extern void (*bar) (void); ++ ++int ++foo (void) ++{ ++ bar (); ++ return 0; ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk" } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "__x86_indirect_thunk:" } } */ ++/* { dg-final { scan-assembler-times {\tpause} 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 1 { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?bar" { target { { ! x32 } && *-*-linux* } } } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" { target { ! x32 } } } } */ ++/* { dg-final { scan-assembler-times {\tpause} 2 { target { x32 } } } } */ ++/* { dg-final { scan-assembler-times {\tlfence} 2 { target { x32 } } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_(r|e)ax" { target { x32 } } } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" { target x32 } } } */ only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0007-x86-Add-mindirect-branch-register.diff +++ gcc-5-5.4.0/debian/patches/0007-x86-Add-mindirect-branch-register.diff @@ -0,0 +1,837 @@ +From 86118fbdbafe6af54b2da467e1073c49e1742116 Mon Sep 17 00:00:00 2001 +From: hjl +Date: Tue, 16 Jan 2018 11:17:49 +0000 +Subject: [PATCH 7/9] x86: Add -mindirect-branch-register + +Add -mindirect-branch-register to force indirect branch via register. +This is implemented by disabling patterns of indirect branch via memory, +similar to TARGET_X32. + +-mindirect-branch= and -mfunction-return= tests are updated with +-mno-indirect-branch-register to avoid false test failures when +-mindirect-branch-register is added to RUNTESTFLAGS for "make check". + +gcc/ + + Backport from mainline + 2018-01-14 H.J. Lu + + * config/i386/constraints.md (Bs): Disallow memory operand for + -mindirect-branch-register. + (Bw): Likewise. + * config/i386/predicates.md (indirect_branch_operand): Likewise. + (GOT_memory_operand): Likewise. + (call_insn_operand): Likewise. + (sibcall_insn_operand): Likewise. + (GOT32_symbol_operand): Likewise. + * config/i386/i386.md (indirect_jump): Call convert_memory_address + for -mindirect-branch-register. + (tablejump): Likewise. + (*sibcall_memory): Likewise. + (*sibcall_value_memory): Likewise. + Disallow peepholes of indirect call and jump via memory for + -mindirect-branch-register. + (*call_pop): Replace m with Bw. + (*call_value_pop): Likewise. + (*sibcall_pop_memory): Replace m with Bs. + * config/i386/i386.opt (mindirect-branch-register): New option. + * doc/invoke.texi: Document -mindirect-branch-register option. + +gcc/testsuite/ + + Backport from mainline + 2018-01-14 H.J. Lu + + * gcc.target/i386/indirect-thunk-1.c (dg-options): Add + -mno-indirect-branch-register. + * gcc.target/i386/indirect-thunk-2.c: Likewise. + * gcc.target/i386/indirect-thunk-3.c: Likewise. + * gcc.target/i386/indirect-thunk-4.c: Likewise. + * gcc.target/i386/indirect-thunk-5.c: Likewise. + * gcc.target/i386/indirect-thunk-6.c: Likewise. + * gcc.target/i386/indirect-thunk-7.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-1.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-2.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-3.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-4.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-5.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-6.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-7.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-1.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-2.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-3.c: Likewise. + * gcc.target/i386/indirect-thunk-bnd-4.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-1.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-2.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-3.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-4.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-5.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-6.c: Likewise. + * gcc.target/i386/indirect-thunk-extern-7.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-1.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-2.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-3.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-4.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-5.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-6.c: Likewise. + * gcc.target/i386/indirect-thunk-inline-7.c: Likewise. + * gcc.target/i386/ret-thunk-10.c: Likewise. + * gcc.target/i386/ret-thunk-11.c: Likewise. + * gcc.target/i386/ret-thunk-12.c: Likewise. + * gcc.target/i386/ret-thunk-13.c: Likewise. + * gcc.target/i386/ret-thunk-14.c: Likewise. + * gcc.target/i386/ret-thunk-15.c: Likewise. + * gcc.target/i386/ret-thunk-9.c: Likewise. + * gcc.target/i386/indirect-thunk-register-1.c: New test. + * gcc.target/i386/indirect-thunk-register-2.c: Likewise. + * gcc.target/i386/indirect-thunk-register-3.c: Likewise. + +i386: Rename to ix86_indirect_branch_register + +Rename the variable for -mindirect-branch-register to +ix86_indirect_branch_register to match the command-line option name. + + Backport from mainline + 2018-01-15 H.J. Lu + + * config/i386/constraints.md (Bs): Replace + ix86_indirect_branch_thunk_register with + ix86_indirect_branch_register. + (Bw): Likewise. + * config/i386/i386.md (indirect_jump): Likewise. + (tablejump): Likewise. + (*sibcall_memory): Likewise. + (*sibcall_value_memory): Likewise. + Peepholes of indirect call and jump via memory: Likewise. + * config/i386/i386.opt: Likewise. + * config/i386/predicates.md (indirect_branch_operand): Likewise. + (GOT_memory_operand): Likewise. + (call_insn_operand): Likewise. + (sibcall_insn_operand): Likewise. + (GOT32_symbol_operand): Likewise. + +x86: Rewrite ix86_indirect_branch_register logic + +Rewrite ix86_indirect_branch_register logic with + +(and (not (match_test "ix86_indirect_branch_register")) + (original condition before r256662)) + + Backport from mainline + 2018-01-15 H.J. Lu + + * config/i386/predicates.md (constant_call_address_operand): + Rewrite ix86_indirect_branch_register logic. + (sibcall_insn_operand): Likewise. + +Don't check ix86_indirect_branch_register for GOT operand + +Since GOT_memory_operand and GOT32_symbol_operand are simple pattern +matches, don't check ix86_indirect_branch_register here. If needed, +-mindirect-branch= will convert indirect branch via GOT slot to a call +and return thunk. + + Backport from mainline + 2018-01-15 H.J. Lu + + * config/i386/constraints.md (Bs): Update + ix86_indirect_branch_register check. Don't check + ix86_indirect_branch_register with GOT_memory_operand. + (Bw): Likewise. + * config/i386/predicates.md (GOT_memory_operand): Don't check + ix86_indirect_branch_register here. + (GOT32_symbol_operand): Likewise. + +i386: Rewrite indirect_branch_operand logic + + Backport from mainline + 2018-01-15 H.J. Lu + + * config/i386/predicates.md (indirect_branch_operand): Rewrite + ix86_indirect_branch_register logic. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256735 138bc75d-0d04-0410-961f-82ee72b054a4 + +[Ubuntu note: Dropped indirect-thunk-5.c, indirect-thunk-6.c, + indirect-thunk-bnd-3.c, indirect-thunk-bnd-4.c, + indirect-thunk-extern-5.c, indirect-thunk-extern-6.c, + indirect-thunk-inline-5.c, and indirect-thunk-inline-6.c tests due + to gcc 5.4 and earlier not supporting the -fno-plt option. + --sbeattie,] +--- + src/gcc/config/i386/constraints.md | 6 + + src/gcc/config/i386/i386.md | 34 ++++++---- + src/gcc/config/i386/i386.opt | 4 + + src/gcc/config/i386/predicates.md | 9 +- + src/gcc/doc/invoke.texi | 7 +- + src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c | 2 + src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c | 22 ++++++ + src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c | 20 +++++ + src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c | 19 +++++ + src/gcc/testsuite/gcc.target/i386/ret-thunk-10.c | 2 + src/gcc/testsuite/gcc.target/i386/ret-thunk-11.c | 2 + src/gcc/testsuite/gcc.target/i386/ret-thunk-12.c | 2 + src/gcc/testsuite/gcc.target/i386/ret-thunk-13.c | 2 + src/gcc/testsuite/gcc.target/i386/ret-thunk-14.c | 2 + src/gcc/testsuite/gcc.target/i386/ret-thunk-15.c | 2 + src/gcc/testsuite/gcc.target/i386/ret-thunk-9.c | 2 + 39 files changed, 134 insertions(+), 49 deletions(-) + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c + +Index: b/src/gcc/config/i386/constraints.md +=================================================================== +--- a/src/gcc/config/i386/constraints.md ++++ b/src/gcc/config/i386/constraints.md +@@ -157,12 +157,14 @@ + + (define_constraint "Bs" + "@internal Sibcall memory operand." +- (and (not (match_test "TARGET_X32")) ++ (and (not (match_test "ix86_indirect_branch_register")) ++ (not (match_test "TARGET_X32")) + (match_operand 0 "sibcall_memory_operand"))) + + (define_constraint "Bw" + "@internal Call memory operand." +- (and (not (match_test "TARGET_X32")) ++ (and (not (match_test "ix86_indirect_branch_register")) ++ (not (match_test "TARGET_X32")) + (match_operand 0 "memory_operand"))) + + (define_constraint "Bz" +Index: b/src/gcc/config/i386/i386.md +=================================================================== +--- a/src/gcc/config/i386/i386.md ++++ b/src/gcc/config/i386/i386.md +@@ -11554,7 +11554,7 @@ + [(set (pc) (match_operand 0 "indirect_branch_operand"))] + "" + { +- if (TARGET_X32) ++ if (TARGET_X32 || ix86_indirect_branch_register) + operands[0] = convert_memory_address (word_mode, operands[0]); + cfun->machine->has_local_indirect_jump = true; + }) +@@ -11607,7 +11607,7 @@ + OPTAB_DIRECT); + } + +- if (TARGET_X32) ++ if (TARGET_X32 || ix86_indirect_branch_register) + operands[0] = convert_memory_address (word_mode, operands[0]); + cfun->machine->has_local_indirect_jump = true; + }) +@@ -11764,7 +11764,7 @@ + [(call (mem:QI (match_operand:W 0 "memory_operand" "m")) + (match_operand 1)) + (unspec [(const_int 0)] UNSPEC_PEEPSIB)] +- "!TARGET_X32" ++ "!TARGET_X32 && !ix86_indirect_branch_register" + "* return ix86_output_call_insn (insn, operands[0]);" + [(set_attr "type" "call")]) + +@@ -11773,7 +11773,9 @@ + (match_operand:W 1 "memory_operand")) + (call (mem:QI (match_dup 0)) + (match_operand 3))] +- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1)) ++ "!TARGET_X32 ++ && !ix86_indirect_branch_register ++ && SIBLING_CALL_P (peep2_next_insn (1)) + && peep2_reg_dead_p (2, operands[0])" + [(parallel [(call (mem:QI (match_dup 1)) + (match_dup 3)) +@@ -11785,7 +11787,9 @@ + (unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE) + (call (mem:QI (match_dup 0)) + (match_operand 3))] +- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2)) ++ "!TARGET_X32 ++ && !ix86_indirect_branch_register ++ && SIBLING_CALL_P (peep2_next_insn (2)) + && peep2_reg_dead_p (3, operands[0])" + [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE) + (parallel [(call (mem:QI (match_dup 1)) +@@ -11806,7 +11810,7 @@ + }) + + (define_insn "*call_pop" +- [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lmBz")) ++ [(call (mem:QI (match_operand:SI 0 "call_insn_operand" "lBwBz")) + (match_operand 1)) + (set (reg:SI SP_REG) + (plus:SI (reg:SI SP_REG) +@@ -11826,7 +11830,7 @@ + [(set_attr "type" "call")]) + + (define_insn "*sibcall_pop_memory" +- [(call (mem:QI (match_operand:SI 0 "memory_operand" "m")) ++ [(call (mem:QI (match_operand:SI 0 "memory_operand" "Bs")) + (match_operand 1)) + (set (reg:SI SP_REG) + (plus:SI (reg:SI SP_REG) +@@ -11878,7 +11882,9 @@ + [(set (match_operand:W 0 "register_operand") + (match_operand:W 1 "memory_operand")) + (set (pc) (match_dup 0))] +- "!TARGET_X32 && peep2_reg_dead_p (2, operands[0])" ++ "!TARGET_X32 ++ && !ix86_indirect_branch_register ++ && peep2_reg_dead_p (2, operands[0])" + [(set (pc) (match_dup 1))]) + + ;; Call subroutine, returning value in operand 0 +@@ -11928,7 +11934,7 @@ + (call (mem:QI (match_operand:W 1 "memory_operand" "m")) + (match_operand 2))) + (unspec [(const_int 0)] UNSPEC_PEEPSIB)] +- "!TARGET_X32" ++ "!TARGET_X32 && !ix86_indirect_branch_register" + "* return ix86_output_call_insn (insn, operands[1]);" + [(set_attr "type" "callv")]) + +@@ -11938,7 +11944,9 @@ + (set (match_operand 2) + (call (mem:QI (match_dup 0)) + (match_operand 3)))] +- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (1)) ++ "!TARGET_X32 ++ && !ix86_indirect_branch_register ++ && SIBLING_CALL_P (peep2_next_insn (1)) + && peep2_reg_dead_p (2, operands[0])" + [(parallel [(set (match_dup 2) + (call (mem:QI (match_dup 1)) +@@ -11952,7 +11960,9 @@ + (set (match_operand 2) + (call (mem:QI (match_dup 0)) + (match_operand 3)))] +- "!TARGET_X32 && SIBLING_CALL_P (peep2_next_insn (2)) ++ "!TARGET_X32 ++ && !ix86_indirect_branch_register ++ && SIBLING_CALL_P (peep2_next_insn (2)) + && peep2_reg_dead_p (3, operands[0])" + [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE) + (parallel [(set (match_dup 2) +@@ -11976,7 +11986,7 @@ + + (define_insn "*call_value_pop" + [(set (match_operand 0) +- (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lmBz")) ++ (call (mem:QI (match_operand:SI 1 "call_insn_operand" "lBwBz")) + (match_operand 2))) + (set (reg:SI SP_REG) + (plus:SI (reg:SI SP_REG) +Index: b/src/gcc/config/i386/i386.opt +=================================================================== +--- a/src/gcc/config/i386/i386.opt ++++ b/src/gcc/config/i386/i386.opt +@@ -900,3 +900,7 @@ Enum(indirect_branch) String(thunk-inlin + + EnumValue + Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern) ++ ++mindirect-branch-register ++Target Report Var(ix86_indirect_branch_register) Init(0) ++Force indirect call and jump via register. +Index: b/src/gcc/config/i386/predicates.md +=================================================================== +--- a/src/gcc/config/i386/predicates.md ++++ b/src/gcc/config/i386/predicates.md +@@ -607,7 +607,8 @@ + ;; Test for a valid operand for indirect branch. + (define_predicate "indirect_branch_operand" + (ior (match_operand 0 "register_operand") +- (and (not (match_test "TARGET_X32")) ++ (and (not (match_test "ix86_indirect_branch_register")) ++ (not (match_test "TARGET_X32")) + (match_operand 0 "memory_operand")))) + + ;; Test for a valid operand for a call instruction. +@@ -616,7 +617,8 @@ + (ior (match_test "constant_call_address_operand + (op, mode == VOIDmode ? mode : Pmode)") + (match_operand 0 "call_register_no_elim_operand") +- (and (not (match_test "TARGET_X32")) ++ (and (not (match_test "ix86_indirect_branch_register")) ++ (not (match_test "TARGET_X32")) + (match_operand 0 "memory_operand")))) + + ;; Similarly, but for tail calls, in which we cannot allow memory references. +@@ -624,7 +626,8 @@ + (ior (match_test "constant_call_address_operand + (op, mode == VOIDmode ? mode : Pmode)") + (match_operand 0 "register_no_elim_operand") +- (and (not (match_test "TARGET_X32")) ++ (and (not (match_test "ix86_indirect_branch_register")) ++ (not (match_test "TARGET_X32")) + (match_operand 0 "sibcall_memory_operand")))) + + ;; Match exactly zero. +Index: b/src/gcc/doc/invoke.texi +=================================================================== +--- a/src/gcc/doc/invoke.texi ++++ b/src/gcc/doc/invoke.texi +@@ -1091,7 +1091,8 @@ See RS/6000 and PowerPC Options. + -msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol + -mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol + -malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol +--mindirect-branch=@var{choice} -mfunction-return=@var{choice}} ++-mindirect-branch=@var{choice} -mfunction-return=@var{choice} @gol ++-mindirect-branch-register} + + @emph{x86 Windows Options} + @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol +@@ -24040,6 +24041,10 @@ object file. You can control this behav + using the function attribute @code{function_return}. + @xref{Function Attributes}. + ++@item -mindirect-branch-register ++@opindex -mindirect-branch-register ++Force indirect call and jump via register. ++ + @end table + + @c man end +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + void func0 (void); + void func1 (void); +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic" } */ + + void func0 (void); + void func1 (void); +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile { target { ! x32 } } } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ + + void (*dispatch) (char *); + char buf[10]; +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile { target { ! x32 } } } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ + + void (*dispatch) (char *); + char buf[10]; +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + void func0 (void); + void func1 (void); +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + typedef void (*dispatch_t)(long offset); + +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + void func0 (void); + void func1 (void); +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-1.c +@@ -0,0 +1,22 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -mindirect-branch-register -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } */ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk\n" } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk_bnd\n" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-2.c +@@ -0,0 +1,20 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-inline -mindirect-branch-register -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler "mov\[ \t\](%eax|%rax), \\((%esp|%rsp)\\)" } } */ ++/* { dg-final { scan-assembler {\tpause} } } */ ++/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */ ++/* { dg-final { scan-assembler-not "__x86_indirect_thunk" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c +=================================================================== +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-3.c +@@ -0,0 +1,19 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-extern -mindirect-branch-register -fno-pic" } */ ++ ++typedef void (*dispatch_t)(long offset); ++ ++dispatch_t dispatch; ++ ++void ++male_indirect_jump (long offset) ++{ ++ dispatch(offset); ++} ++ ++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_(r|e)ax" } } */ ++/* { dg-final { scan-assembler-not "push(?:l|q)\[ \t\]*_?dispatch" } } */ ++/* { dg-final { scan-assembler-not "pushq\[ \t\]%rax" } } */ ++/* { dg-final { scan-assembler-not {\t(pause|pause|nop)} } } */ ++/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ ++/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-10.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/ret-thunk-10.c ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-10.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk-inline -mindirect-branch=thunk -fno-pic" } */ + + extern void (*bar) (void); + +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-11.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/ret-thunk-11.c ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-11.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk-extern -mindirect-branch=thunk -fno-pic" } */ + + extern void (*bar) (void); + +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-12.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/ret-thunk-12.c ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-12.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk -fno-pic" } */ + + extern void (*bar) (void); + +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-13.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/ret-thunk-13.c ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-13.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-inline -fno-pic" } */ + + extern void (*bar) (void); + extern int foo (void) __attribute__ ((function_return("thunk"))); +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-14.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/ret-thunk-14.c ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-14.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=thunk-extern -fno-pic" } */ + + extern void (*bar) (void); + +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-15.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/ret-thunk-15.c ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-15.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=keep -mindirect-branch=keep -fno-pic" } */ + + extern void (*bar) (void); + +Index: b/src/gcc/testsuite/gcc.target/i386/ret-thunk-9.c +=================================================================== +--- a/src/gcc/testsuite/gcc.target/i386/ret-thunk-9.c ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-9.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */ ++/* { dg-options "-O2 -mno-indirect-branch-register -mno-indirect-branch-register -mfunction-return=thunk -mindirect-branch=thunk -fno-pic" } */ + + extern void (*bar) (void); + only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0008-x86-Add-V-register-operand-modifier.diff +++ gcc-5-5.4.0/debian/patches/0008-x86-Add-V-register-operand-modifier.diff @@ -0,0 +1,139 @@ +From 8a47615dd04a02fdae9691f5ad73fd5a5530c156 Mon Sep 17 00:00:00 2001 +From: hjl +Date: Tue, 16 Jan 2018 11:19:51 +0000 +Subject: [PATCH 8/9] x86: Add 'V' register operand modifier + +Add 'V', a special modifier which prints the name of the full integer +register without '%'. For + +extern void (*func_p) (void); + +void +foo (void) +{ + asm ("call __x86_indirect_thunk_%V0" : : "a" (func_p)); +} + +it generates: + +foo: + movq func_p(%rip), %rax + call __x86_indirect_thunk_rax + ret + +gcc/ + + Backport from mainline + 2018-01-14 H.J. Lu + + * config/i386/i386.c (print_reg): Print the name of the full + integer register without '%'. + (ix86_print_operand): Handle 'V'. + * doc/extend.texi: Document 'V' modifier. + +gcc/testsuite/ + + Backport from mainline + 2018-01-14 H.J. Lu + + * gcc.target/i386/indirect-thunk-register-4.c: New test. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256736 138bc75d-0d04-0410-961f-82ee72b054a4 +--- + gcc/ChangeLog | 10 ++++++++++ + gcc/config/i386/i386.c | 13 ++++++++++++- + gcc/doc/extend.texi | 3 +++ + gcc/testsuite/ChangeLog | 7 +++++++ + gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c | 13 +++++++++++++ + 5 files changed, 45 insertions(+), 1 deletion(-) + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c + +diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c +index 8fb8902..1bbdd0c 100644 +--- a/src/gcc/config/i386/i386.c ++++ b/src/gcc/config/i386/i386.c +@@ -17941,6 +17941,7 @@ put_condition_code (enum rtx_code code, machine_mode mode, bool reverse, + If CODE is 'h', pretend the reg is the 'high' byte register. + If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op. + If CODE is 'd', duplicate the operand for AVX instruction. ++ If CODE is 'V', print naked full integer register name without %. + */ + + void +@@ -17951,7 +17952,7 @@ print_reg (rtx x, int code, FILE *file) + unsigned int regno; + bool duplicated = code == 'd' && TARGET_AVX; + +- if (ASSEMBLER_DIALECT == ASM_ATT) ++ if (ASSEMBLER_DIALECT == ASM_ATT && code != 'V') + putc ('%', file); + + if (x == pc_rtx) +@@ -17999,6 +18000,14 @@ print_reg (rtx x, int code, FILE *file) + else + code = GET_MODE_SIZE (GET_MODE (x)); + ++ if (code == 'V') ++ { ++ if (GENERAL_REGNO_P (regno)) ++ code = GET_MODE_SIZE (word_mode); ++ else ++ error ("'V' modifier on non-integer register"); ++ } ++ + /* Irritatingly, AMD extended registers use different naming convention + from the normal registers: "r%d[bwd]" */ + if (REX_INT_REGNO_P (regno)) +@@ -18118,6 +18127,7 @@ print_reg (rtx x, int code, FILE *file) + & -- print some in-use local-dynamic symbol name. + H -- print a memory address offset by 8; used for sse high-parts + Y -- print condition for XOP pcom* instruction. ++ V -- print naked full integer register name without %. + + -- print a branch hint as 'cs' or 'ds' prefix + ; -- print a semicolon (after prefixes due to bug in older gas). + ~ -- print "i" if TARGET_AVX2, "f" otherwise. +@@ -18342,6 +18352,7 @@ ix86_print_operand (FILE *file, rtx x, int code) + case 'X': + case 'P': + case 'p': ++ case 'V': + break; + + case 's': +diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi +index 46e0a36..9db9e0e 100644 +--- a/src/gcc/doc/extend.texi ++++ b/src/gcc/doc/extend.texi +@@ -8778,6 +8778,9 @@ The table below shows the list of supported modifiers and their effects. + @tab @code{2} + @end multitable + ++@code{V} is a special modifier which prints the name of the full integer ++register without @code{%}. ++ + @anchor{x86floatingpointasmoperands} + @subsubsection x86 Floating-Point @code{asm} Operands + +diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c +new file mode 100644 +index 0000000..f0cd9b7 +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-register-4.c +@@ -0,0 +1,13 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -mindirect-branch=keep -fno-pic" } */ ++ ++extern void (*func_p) (void); ++ ++void ++foo (void) ++{ ++ asm("call __x86_indirect_thunk_%V0" : : "a" (func_p)); ++} ++ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_eax" { target ia32 } } } */ ++/* { dg-final { scan-assembler "call\[ \t\]*__x86_indirect_thunk_rax" { target { ! ia32 } } } } */ +-- +2.7.4 + only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/0009-x86-Disallow-mindirect-branch-mfunction-return-with-.diff +++ gcc-5-5.4.0/debian/patches/0009-x86-Disallow-mindirect-branch-mfunction-return-with-.diff @@ -0,0 +1,304 @@ +From 5d1c53c6fd593de2360c1a2ae44ebf5fa3c5263b Mon Sep 17 00:00:00 2001 +From: hjl +Date: Tue, 16 Jan 2018 11:22:01 +0000 +Subject: [PATCH 9/9] x86: Disallow -mindirect-branch=/-mfunction-return= with + -mcmodel=large + +Since the thunk function may not be reachable in large code model, +-mcmodel=large is incompatible with -mindirect-branch=thunk, +-mindirect-branch=thunk-extern, -mfunction-return=thunk and +-mfunction-return=thunk-extern. Issue an error when they are used with +-mcmodel=large. + +gcc/ + + Backport from mainline + 2018-01-14 H.J. Lu + + * config/i386/i386.c (ix86_set_indirect_branch_type): Disallow + -mcmodel=large with -mindirect-branch=thunk, + -mindirect-branch=thunk-extern, -mfunction-return=thunk and + -mfunction-return=thunk-extern. + * doc/invoke.texi: Document -mcmodel=large is incompatible with + -mindirect-branch=thunk, -mindirect-branch=thunk-extern, + -mfunction-return=thunk and -mfunction-return=thunk-extern. + +gcc/testsuite/ + + Backport from mainline + 2018-01-14 H.J. Lu + + * gcc.target/i386/indirect-thunk-10.c: New test. + * gcc.target/i386/indirect-thunk-8.c: Likewise. + * gcc.target/i386/indirect-thunk-9.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-10.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-11.c: Likewise. + * gcc.target/i386/indirect-thunk-attr-9.c: Likewise. + * gcc.target/i386/ret-thunk-17.c: Likewise. + * gcc.target/i386/ret-thunk-18.c: Likewise. + * gcc.target/i386/ret-thunk-19.c: Likewise. + * gcc.target/i386/ret-thunk-20.c: Likewise. + * gcc.target/i386/ret-thunk-21.c: Likewise. + + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-7-branch@256737 138bc75d-0d04-0410-961f-82ee72b054a4 +--- + gcc/ChangeLog | 13 +++++++++++ + gcc/config/i386/i386.c | 26 ++++++++++++++++++++++ + gcc/doc/invoke.texi | 11 +++++++++ + gcc/testsuite/ChangeLog | 17 ++++++++++++++ + gcc/testsuite/gcc.target/i386/indirect-thunk-10.c | 7 ++++++ + gcc/testsuite/gcc.target/i386/indirect-thunk-8.c | 7 ++++++ + gcc/testsuite/gcc.target/i386/indirect-thunk-9.c | 7 ++++++ + .../gcc.target/i386/indirect-thunk-attr-10.c | 9 ++++++++ + .../gcc.target/i386/indirect-thunk-attr-11.c | 9 ++++++++ + .../gcc.target/i386/indirect-thunk-attr-9.c | 9 ++++++++ + gcc/testsuite/gcc.target/i386/ret-thunk-17.c | 7 ++++++ + gcc/testsuite/gcc.target/i386/ret-thunk-18.c | 8 +++++++ + gcc/testsuite/gcc.target/i386/ret-thunk-19.c | 8 +++++++ + gcc/testsuite/gcc.target/i386/ret-thunk-20.c | 9 ++++++++ + gcc/testsuite/gcc.target/i386/ret-thunk-21.c | 9 ++++++++ + 15 files changed, 156 insertions(+) + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-10.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-8.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-9.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c + create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-17.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-18.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-19.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-20.c + create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-21.c + +diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c +index 1bbdd0c..e758387 100644 +--- a/src/gcc/config/i386/i386.c ++++ b/src/gcc/config/i386/i386.c +@@ -7187,6 +7187,19 @@ ix86_set_indirect_branch_type (tree fndecl) + } + else + cfun->machine->indirect_branch_type = ix86_indirect_branch; ++ ++ /* -mcmodel=large is not compatible with -mindirect-branch=thunk ++ nor -mindirect-branch=thunk-extern. */ ++ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC) ++ && ((cfun->machine->indirect_branch_type ++ == indirect_branch_thunk_extern) ++ || (cfun->machine->indirect_branch_type ++ == indirect_branch_thunk))) ++ error ("%<-mindirect-branch=%s%> and %<-mcmodel=large%> are not " ++ "compatible", ++ ((cfun->machine->indirect_branch_type ++ == indirect_branch_thunk_extern) ++ ? "thunk-extern" : "thunk")); + } + + if (cfun->machine->function_return_type == indirect_branch_unset) +@@ -7212,6 +7225,19 @@ ix86_set_indirect_branch_type (tree fndecl) + } + else + cfun->machine->function_return_type = ix86_function_return; ++ ++ /* -mcmodel=large is not compatible with -mfunction-return=thunk ++ nor -mfunction-return=thunk-extern. */ ++ if ((ix86_cmodel == CM_LARGE || ix86_cmodel == CM_LARGE_PIC) ++ && ((cfun->machine->function_return_type ++ == indirect_branch_thunk_extern) ++ || (cfun->machine->function_return_type ++ == indirect_branch_thunk))) ++ error ("%<-mfunction-return=%s%> and %<-mcmodel=large%> are not " ++ "compatible", ++ ((cfun->machine->function_return_type ++ == indirect_branch_thunk_extern) ++ ? "thunk-extern" : "thunk")); + } + } + +diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi +index 1e572b1..6f3c344 100644 +--- a/src/gcc/doc/invoke.texi ++++ b/src/gcc/doc/invoke.texi +@@ -25699,6 +25699,11 @@ to external call and return thunk provided in a separate object file. + You can control this behavior for a specific function by using the + function attribute @code{indirect_branch}. @xref{Function Attributes}. + ++Note that @option{-mcmodel=large} is incompatible with ++@option{-mindirect-branch=thunk} nor ++@option{-mindirect-branch=thunk-extern} since the thunk function may ++not be reachable in large code model. ++ + @item -mfunction-return=@var{choice} + @opindex -mfunction-return + Convert function return with @var{choice}. The default is @samp{keep}, +@@ -25710,6 +25715,12 @@ object file. You can control this behavior for a specific function by + using the function attribute @code{function_return}. + @xref{Function Attributes}. + ++Note that @option{-mcmodel=large} is incompatible with ++@option{-mfunction-return=thunk} nor ++@option{-mfunction-return=thunk-extern} since the thunk function may ++not be reachable in large code model. ++ ++ + @item -mindirect-branch-register + @opindex -mindirect-branch-register + Force indirect call and jump via register. +diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c +new file mode 100644 +index 0000000..a0674bd +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-10.c +@@ -0,0 +1,7 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-inline -mfunction-return=keep -mcmodel=large" } */ ++ ++void ++bar (void) ++{ ++} +diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c +new file mode 100644 +index 0000000..7a80a89 +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-8.c +@@ -0,0 +1,7 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mindirect-branch=thunk -mfunction-return=keep -mcmodel=large" } */ ++ ++void ++bar (void) ++{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */ ++} +diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c +new file mode 100644 +index 0000000..d4d45c5 +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-9.c +@@ -0,0 +1,7 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mindirect-branch=thunk-extern -mfunction-return=keep -mcmodel=large" } */ ++ ++void ++bar (void) ++{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */ ++} +diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c +new file mode 100644 +index 0000000..3a2aead +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-10.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */ ++/* { dg-additional-options "-fPIC" { target fpic } } */ ++ ++__attribute__ ((indirect_branch("thunk-extern"))) ++void ++bar (void) ++{ /* { dg-error "'-mindirect-branch=thunk-extern' and '-mcmodel=large' are not compatible" } */ ++} +diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c +new file mode 100644 +index 0000000..8e52f03 +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-11.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */ ++/* { dg-additional-options "-fPIC" { target fpic } } */ ++ ++__attribute__ ((indirect_branch("thunk-inline"))) ++void ++bar (void) ++{ ++} +diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c +new file mode 100644 +index 0000000..bdaa4f6 +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-9.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mindirect-branch=keep -mfunction-return=keep -mcmodel=large" } */ ++/* { dg-additional-options "-fPIC" { target fpic } } */ ++ ++__attribute__ ((indirect_branch("thunk"))) ++void ++bar (void) ++{ /* { dg-error "'-mindirect-branch=thunk' and '-mcmodel=large' are not compatible" } */ ++} +diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-17.c b/gcc/testsuite/gcc.target/i386/ret-thunk-17.c +new file mode 100644 +index 0000000..0605e2c +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-17.c +@@ -0,0 +1,7 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mfunction-return=thunk -mindirect-branch=keep -mcmodel=large" } */ ++ ++void ++bar (void) ++{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */ ++} +diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-18.c b/gcc/testsuite/gcc.target/i386/ret-thunk-18.c +new file mode 100644 +index 0000000..307019d +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-18.c +@@ -0,0 +1,8 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mfunction-return=thunk-extern -mindirect-branch=keep -mcmodel=large" } */ ++/* { dg-additional-options "-fPIC" { target fpic } } */ ++ ++void ++bar (void) ++{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */ ++} +diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-19.c b/gcc/testsuite/gcc.target/i386/ret-thunk-19.c +new file mode 100644 +index 0000000..772617f +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-19.c +@@ -0,0 +1,8 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */ ++ ++__attribute__ ((function_return("thunk"))) ++void ++bar (void) ++{ /* { dg-error "'-mfunction-return=thunk' and '-mcmodel=large' are not compatible" } */ ++} +diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-20.c b/gcc/testsuite/gcc.target/i386/ret-thunk-20.c +new file mode 100644 +index 0000000..1e9f9bd +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-20.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */ ++/* { dg-additional-options "-fPIC" { target fpic } } */ ++ ++__attribute__ ((function_return("thunk-extern"))) ++void ++bar (void) ++{ /* { dg-error "'-mfunction-return=thunk-extern' and '-mcmodel=large' are not compatible" } */ ++} +diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-21.c b/gcc/testsuite/gcc.target/i386/ret-thunk-21.c +new file mode 100644 +index 0000000..eea07f7 +--- /dev/null ++++ b/src/gcc/testsuite/gcc.target/i386/ret-thunk-21.c +@@ -0,0 +1,9 @@ ++/* { dg-do compile { target { lp64 } } } */ ++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=keep -mcmodel=large" } */ ++/* { dg-additional-options "-fPIC" { target fpic } } */ ++ ++__attribute__ ((function_return("thunk-inline"))) ++void ++bar (void) ++{ ++} +-- +2.7.4 + only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/thunderx2t99-improve-cost-table.diff +++ gcc-5-5.4.0/debian/patches/thunderx2t99-improve-cost-table.diff @@ -0,0 +1,22 @@ +Description: (thunderx2t99_addrcost_table): Improve cost table. + Backported to gcc-5 by dann frazier +Author: Andrew Pinski +Origin: https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=244851 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1727540 +Applied-Upstream: svn://gcc.gnu.org/svn/gcc/trunk@244851 + +--- a/src/gcc/config/aarch64/aarch64.c 2017-10-19 19:13:38.407610061 -0400 ++++ b/src/gcc/config/aarch64/aarch64.c 2017-10-20 15:37:03.712619817 -0400 +@@ -270,9 +270,9 @@ static const struct cpu_addrcost_table x + static const struct cpu_addrcost_table thunderx2t99_addrcost_table = + { + { +- 0, /* hi */ +- 0, /* si */ +- 0, /* di */ ++ 1, /* hi */ ++ 1, /* si */ ++ 1, /* di */ + 2, /* ti */ + }, + 0, /* pre_modify */ only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/thunderx2t99-scheduler.diff +++ gcc-5-5.4.0/debian/patches/thunderx2t99-scheduler.diff @@ -0,0 +1,485 @@ +Description: Add scheduler for Thunderx2t99 + * config/aarch64/aarch64-cores.def: Change the scheduler + to Thunderx2t99. + * config/aarch64/aarch64.md: Include thunderx2t99.md. + * config/aarch64/thunderx2t99.md: New file. + . + Backported to gcc-5 by dann frazier +Author: Julian Brown +Author: Naveen H.S +Author: Virendra Pathak +Origin: https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=245203 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1727540 +Applied-Upstream: svn://gcc.gnu.org/svn/gcc/trunk@245203 + +--- a/src/gcc/config/aarch64/aarch64-cores.def 2017-10-20 17:19:39.650846958 -0400 ++++ b/src/gcc/config/aarch64/aarch64-cores.def 2017-10-20 18:10:47.523130202 -0400 +@@ -51,8 +51,8 @@ AARCH64_CORE("xgene1", xgene1, x + + /* V8.1 Architecture Processors. */ + +-AARCH64_CORE("thunderx2t99", thunderx2t99, cortexa57, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") +-AARCH64_CORE("vulcan", vulcan, cortexa57, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") ++AARCH64_CORE("thunderx2t99", thunderx2t99, thunderx2t99, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") ++AARCH64_CORE("vulcan", vulcan, thunderx2t99, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") + + /* V8 big.LITTLE implementations. */ + +diff -urpN a/src/gcc/config/aarch64/aarch64.md b/src/gcc/config/aarch64/aarch64.md +--- a/src/gcc/config/aarch64/aarch64.md 2017-10-20 17:04:34.804898636 -0400 ++++ b/src/gcc/config/aarch64/aarch64.md 2017-10-20 18:11:08.241347156 -0400 +@@ -208,6 +208,7 @@ + (include "../arm/cortex-a57.md") + (include "thunderx.md") + (include "../arm/xgene1.md") ++(include "thunderx2t99.md") + + ;; ------------------------------------------------------------------- + ;; Jumps and other miscellaneous insns +diff -urpN a/src/gcc/config/aarch64/thunderx2t99.md b/src/gcc/config/aarch64/thunderx2t99.md +--- a/src/gcc/config/aarch64/thunderx2t99.md 1969-12-31 19:00:00.000000000 -0500 ++++ b/src/gcc/config/aarch64/thunderx2t99.md 2017-10-20 18:11:52.526064571 -0400 +@@ -0,0 +1,443 @@ ++;; Cavium ThunderX 2 CN99xx pipeline description ++;; Copyright (C) 2016-2017 Free Software Foundation, Inc. ++;; ++;; Contributed by Cavium, Broadcom and Mentor Embedded. ++ ++;; This file is part of GCC. ++ ++;; GCC is free software; you can redistribute it and/or modify ++;; it under the terms of the GNU General Public License as published by ++;; the Free Software Foundation; either version 3, or (at your option) ++;; any later version. ++ ++;; GCC is distributed in the hope that it will be useful, ++;; but WITHOUT ANY WARRANTY; without even the implied warranty of ++;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++;; GNU General Public License for more details. ++ ++;; You should have received a copy of the GNU General Public License ++;; along with GCC; see the file COPYING3. If not see ++;; . ++ ++(define_automaton "thunderx2t99, thunderx2t99_advsimd, thunderx2t99_ldst") ++(define_automaton "thunderx2t99_mult") ++ ++(define_cpu_unit "thunderx2t99_i0" "thunderx2t99") ++(define_cpu_unit "thunderx2t99_i1" "thunderx2t99") ++(define_cpu_unit "thunderx2t99_i2" "thunderx2t99") ++ ++(define_cpu_unit "thunderx2t99_ls0" "thunderx2t99_ldst") ++(define_cpu_unit "thunderx2t99_ls1" "thunderx2t99_ldst") ++(define_cpu_unit "thunderx2t99_sd" "thunderx2t99_ldst") ++ ++; Pseudo-units for multiply pipeline. ++ ++(define_cpu_unit "thunderx2t99_i1m1" "thunderx2t99_mult") ++(define_cpu_unit "thunderx2t99_i1m2" "thunderx2t99_mult") ++(define_cpu_unit "thunderx2t99_i1m3" "thunderx2t99_mult") ++ ++; Pseudo-units for load delay (assuming dcache hit). ++ ++(define_cpu_unit "thunderx2t99_ls0d1" "thunderx2t99_ldst") ++(define_cpu_unit "thunderx2t99_ls0d2" "thunderx2t99_ldst") ++(define_cpu_unit "thunderx2t99_ls0d3" "thunderx2t99_ldst") ++ ++(define_cpu_unit "thunderx2t99_ls1d1" "thunderx2t99_ldst") ++(define_cpu_unit "thunderx2t99_ls1d2" "thunderx2t99_ldst") ++(define_cpu_unit "thunderx2t99_ls1d3" "thunderx2t99_ldst") ++ ++; Make some aliases for f0/f1. ++(define_cpu_unit "thunderx2t99_f0" "thunderx2t99_advsimd") ++(define_cpu_unit "thunderx2t99_f1" "thunderx2t99_advsimd") ++ ++(define_reservation "thunderx2t99_i012" "thunderx2t99_i0|thunderx2t99_i1|thunderx2t99_i2") ++(define_reservation "thunderx2t99_ls01" "thunderx2t99_ls0|thunderx2t99_ls1") ++(define_reservation "thunderx2t99_f01" "thunderx2t99_f0|thunderx2t99_f1") ++ ++(define_reservation "thunderx2t99_ls_both" "thunderx2t99_ls0+thunderx2t99_ls1") ++ ++; A load with delay in the ls0/ls1 pipes. ++(define_reservation "thunderx2t99_l0delay" "thunderx2t99_ls0,\ ++ thunderx2t99_ls0d1,thunderx2t99_ls0d2,\ ++ thunderx2t99_ls0d3") ++(define_reservation "thunderx2t99_l1delay" "thunderx2t99_ls1,\ ++ thunderx2t99_ls1d1,thunderx2t99_ls1d2,\ ++ thunderx2t99_ls1d3") ++(define_reservation "thunderx2t99_l01delay" "thunderx2t99_l0delay|thunderx2t99_l1delay") ++ ++;; Branch and call instructions. ++ ++(define_insn_reservation "thunderx2t99_branch" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "call,branch")) ++ "thunderx2t99_i2") ++ ++;; Integer arithmetic/logic instructions. ++ ++; Plain register moves are handled by renaming, and don't create any uops. ++ ++(define_insn_reservation "thunderx2t99_regmove" 0 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "mov_reg")) ++ "nothing") ++ ++(define_insn_reservation "thunderx2t99_alu_basic" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "alu_imm,alu_sreg,alus_imm,alus_sreg,\ ++ adc_reg,adc_imm,adcs_reg,adcs_imm,\ ++ logic_reg,logic_imm,logics_reg,logics_imm,\ ++ csel,adr,mov_imm,shift_reg,shift_imm,bfm,\ ++ rbit,rev,extend,rotate_imm")) ++ "thunderx2t99_i012") ++ ++(define_insn_reservation "thunderx2t99_alu_shift" 2 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "alu_shift_imm,alu_ext,alu_shift_reg,\ ++ alus_shift_imm,alus_ext,alus_shift_reg,\ ++ logic_shift_imm,logics_shift_reg")) ++ "thunderx2t99_i012,thunderx2t99_i012") ++ ++(define_insn_reservation "thunderx2t99_div" 13 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "sdiv,udiv")) ++ "thunderx2t99_i1*3") ++ ++(define_insn_reservation "thunderx2t99_madd" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "mla,smlal,umlal")) ++ "thunderx2t99_i1,thunderx2t99_i1m1,thunderx2t99_i1m2,thunderx2t99_i1m3,\ ++ thunderx2t99_i012") ++ ++; NOTE: smull, umull are used for "high part" multiplies too. ++(define_insn_reservation "thunderx2t99_mul" 4 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "mul,smull,umull")) ++ "thunderx2t99_i1,thunderx2t99_i1m1,thunderx2t99_i1m2,thunderx2t99_i1m3") ++ ++(define_insn_reservation "thunderx2t99_countbits" 3 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "clz")) ++ "thunderx2t99_i1") ++ ++;; Integer loads and stores. ++ ++(define_insn_reservation "thunderx2t99_load_basic" 4 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "load1")) ++ "thunderx2t99_ls01") ++ ++(define_insn_reservation "thunderx2t99_loadpair" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "load2")) ++ "thunderx2t99_i012,thunderx2t99_ls01") ++ ++(define_insn_reservation "thunderx2t99_store_basic" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "store1")) ++ "thunderx2t99_ls01,thunderx2t99_sd") ++ ++(define_insn_reservation "thunderx2t99_storepair_basic" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "store2")) ++ "thunderx2t99_ls01,thunderx2t99_sd") ++ ++;; FP data processing instructions. ++ ++(define_insn_reservation "thunderx2t99_fp_simple" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "ffariths,ffarithd,f_minmaxs,f_minmaxd")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_fp_addsub" 6 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "fadds,faddd")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_fp_cmp" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "fcmps,fcmpd")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_fp_divsqrt_s" 16 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "fdivs,fsqrts")) ++ "thunderx2t99_f0*3|thunderx2t99_f1*3") ++ ++(define_insn_reservation "thunderx2t99_fp_divsqrt_d" 23 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "fdivd,fsqrtd")) ++ "thunderx2t99_f0*5|thunderx2t99_f1*5") ++ ++(define_insn_reservation "thunderx2t99_fp_mul_mac" 6 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "fmuls,fmuld,fmacs,fmacd")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_frint" 7 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "f_rints,f_rintd")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_fcsel" 4 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "fcsel")) ++ "thunderx2t99_f01") ++ ++;; FP miscellaneous instructions. ++ ++(define_insn_reservation "thunderx2t99_fp_cvt" 7 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "f_cvtf2i,f_cvt,f_cvti2f")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_fp_mov" 4 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "fconsts,fconstd,fmov,f_mrc")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_fp_mov_to_gen" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "f_mcr")) ++ "thunderx2t99_f01") ++ ++;; FP loads and stores. ++ ++(define_insn_reservation "thunderx2t99_fp_load_basic" 4 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "f_loads,f_loadd")) ++ "thunderx2t99_ls01") ++ ++(define_insn_reservation "thunderx2t99_fp_loadpair_basic" 4 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_load1_2reg")) ++ "thunderx2t99_ls01*2") ++ ++(define_insn_reservation "thunderx2t99_fp_store_basic" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "f_stores,f_stored")) ++ "thunderx2t99_ls01,thunderx2t99_sd") ++ ++(define_insn_reservation "thunderx2t99_fp_storepair_basic" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_store1_2reg")) ++ "thunderx2t99_ls01,(thunderx2t99_ls01+thunderx2t99_sd),thunderx2t99_sd") ++ ++;; ASIMD integer instructions. ++ ++(define_insn_reservation "thunderx2t99_asimd_int" 7 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_abd,neon_abd_q,\ ++ neon_arith_acc,neon_arith_acc_q,\ ++ neon_abs,neon_abs_q,\ ++ neon_add,neon_add_q,\ ++ neon_neg,neon_neg_q,\ ++ neon_add_long,neon_add_widen,\ ++ neon_add_halve,neon_add_halve_q,\ ++ neon_sub_long,neon_sub_widen,\ ++ neon_sub_halve,neon_sub_halve_q,\ ++ neon_add_halve_narrow_q,neon_sub_halve_narrow_q,\ ++ neon_qabs,neon_qabs_q,\ ++ neon_qadd,neon_qadd_q,\ ++ neon_qneg,neon_qneg_q,\ ++ neon_qsub,neon_qsub_q,\ ++ neon_minmax,neon_minmax_q,\ ++ neon_reduc_minmax,neon_reduc_minmax_q,\ ++ neon_mul_b,neon_mul_h,neon_mul_s,\ ++ neon_mul_b_q,neon_mul_h_q,neon_mul_s_q,\ ++ neon_sat_mul_b,neon_sat_mul_h,neon_sat_mul_s,\ ++ neon_sat_mul_b_q,neon_sat_mul_h_q,neon_sat_mul_s_q,\ ++ neon_mla_b,neon_mla_h,neon_mla_s,\ ++ neon_mla_b_q,neon_mla_h_q,neon_mla_s_q,\ ++ neon_mul_b_long,neon_mul_h_long,\ ++ neon_mul_s_long,neon_mul_d_long,\ ++ neon_sat_mul_b_long,neon_sat_mul_h_long,\ ++ neon_sat_mul_s_long,\ ++ neon_mla_b_long,neon_mla_h_long,neon_mla_s_long,\ ++ neon_sat_mla_b_long,neon_sat_mla_h_long,\ ++ neon_sat_mla_s_long,\ ++ neon_shift_acc,neon_shift_acc_q,\ ++ neon_shift_imm,neon_shift_imm_q,\ ++ neon_shift_reg,neon_shift_reg_q,\ ++ neon_shift_imm_long,neon_shift_imm_narrow_q,\ ++ neon_sat_shift_imm,neon_sat_shift_imm_q,\ ++ neon_sat_shift_reg,neon_sat_shift_reg_q,\ ++ neon_sat_shift_imm_narrow_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_reduc_add" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_reduc_add,neon_reduc_add_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_cmp" 7 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_compare,neon_compare_q,neon_compare_zero,\ ++ neon_tst,neon_tst_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_logic" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_logic,neon_logic_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_polynomial" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_mul_d_long")) ++ "thunderx2t99_f01") ++ ++;; ASIMD floating-point instructions. ++ ++(define_insn_reservation "thunderx2t99_asimd_fp_simple" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_fp_abs_s,neon_fp_abs_d,\ ++ neon_fp_abs_s_q,neon_fp_abs_d_q,\ ++ neon_fp_compare_s,neon_fp_compare_d,\ ++ neon_fp_compare_s_q,neon_fp_compare_d_q,\ ++ neon_fp_minmax_s,neon_fp_minmax_d,\ ++ neon_fp_minmax_s_q,neon_fp_minmax_d_q,\ ++ neon_fp_reduc_minmax_s,neon_fp_reduc_minmax_d,\ ++ neon_fp_reduc_minmax_s_q,neon_fp_reduc_minmax_d_q,\ ++ neon_fp_neg_s,neon_fp_neg_d,\ ++ neon_fp_neg_s_q,neon_fp_neg_d_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_fp_arith" 6 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_fp_abd_s,neon_fp_abd_d,\ ++ neon_fp_abd_s_q,neon_fp_abd_d_q,\ ++ neon_fp_addsub_s,neon_fp_addsub_d,\ ++ neon_fp_addsub_s_q,neon_fp_addsub_d_q,\ ++ neon_fp_reduc_add_s,neon_fp_reduc_add_d,\ ++ neon_fp_reduc_add_s_q,neon_fp_reduc_add_d_q,\ ++ neon_fp_mul_s,neon_fp_mul_d,\ ++ neon_fp_mul_s_q,neon_fp_mul_d_q,\ ++ neon_fp_mla_s,neon_fp_mla_d,\ ++ neon_fp_mla_s_q,neon_fp_mla_d_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_fp_conv" 7 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_fp_cvt_widen_s,neon_fp_cvt_narrow_d_q,\ ++ neon_fp_to_int_s,neon_fp_to_int_d,\ ++ neon_fp_to_int_s_q,neon_fp_to_int_d_q,\ ++ neon_fp_round_s,neon_fp_round_d,\ ++ neon_fp_round_s_q,neon_fp_round_d_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_fp_div_s" 16 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_fp_div_s,neon_fp_div_s_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_fp_div_d" 23 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_fp_div_d,neon_fp_div_d_q")) ++ "thunderx2t99_f01") ++ ++;; ASIMD miscellaneous instructions. ++ ++(define_insn_reservation "thunderx2t99_asimd_misc" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_rbit,\ ++ neon_bsl,neon_bsl_q,\ ++ neon_cls,neon_cls_q,\ ++ neon_cnt,neon_cnt_q,\ ++ neon_from_gp,neon_from_gp_q,\ ++ neon_dup,neon_dup_q,\ ++ neon_ext,neon_ext_q,\ ++ neon_ins,neon_ins_q,\ ++ neon_move,neon_move_q,\ ++ neon_fp_recpe_s,neon_fp_recpe_d,\ ++ neon_fp_recpe_s_q,neon_fp_recpe_d_q,\ ++ neon_fp_recpx_s,neon_fp_recpx_d,\ ++ neon_fp_recpx_s_q,neon_fp_recpx_d_q,\ ++ neon_rev,neon_rev_q,\ ++ neon_dup,neon_dup_q,\ ++ neon_permute,neon_permute_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_recip_step" 6 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_fp_recps_s,neon_fp_recps_s_q,\ ++ neon_fp_recps_d,neon_fp_recps_d_q,\ ++ neon_fp_rsqrts_s, neon_fp_rsqrts_s_q,\ ++ neon_fp_rsqrts_d, neon_fp_rsqrts_d_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_lut" 8 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_tbl1,neon_tbl1_q,neon_tbl2_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_elt_to_gr" 6 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_to_gp,neon_to_gp_q")) ++ "thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_ext" 7 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_shift_imm_narrow_q,neon_sat_shift_imm_narrow_q")) ++ "thunderx2t99_f01") ++ ++;; ASIMD load instructions. ++ ++; NOTE: These reservations attempt to model latency and throughput correctly, ++; but the cycle timing of unit allocation is not necessarily accurate (because ++; insns are split into uops, and those may be issued out-of-order). ++ ++(define_insn_reservation "thunderx2t99_asimd_load1_1_mult" 4 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_load1_1reg,neon_load1_1reg_q")) ++ "thunderx2t99_ls01") ++ ++(define_insn_reservation "thunderx2t99_asimd_load1_2_mult" 4 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_load1_2reg,neon_load1_2reg_q")) ++ "thunderx2t99_ls_both") ++ ++(define_insn_reservation "thunderx2t99_asimd_load1_onelane" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_load1_one_lane,neon_load1_one_lane_q")) ++ "thunderx2t99_l01delay,thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_load1_all" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_load1_all_lanes,neon_load1_all_lanes_q")) ++ "thunderx2t99_l01delay,thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_load2" 5 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_load2_2reg,neon_load2_2reg_q,\ ++ neon_load2_one_lane,neon_load2_one_lane_q,\ ++ neon_load2_all_lanes,neon_load2_all_lanes_q")) ++ "(thunderx2t99_l0delay,thunderx2t99_f01)|(thunderx2t99_l1delay,\ ++ thunderx2t99_f01)") ++ ++;; ASIMD store instructions. ++ ++; Same note applies as for ASIMD load instructions. ++ ++(define_insn_reservation "thunderx2t99_asimd_store1_1_mult" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_store1_1reg,neon_store1_1reg_q")) ++ "thunderx2t99_ls01") ++ ++(define_insn_reservation "thunderx2t99_asimd_store1_2_mult" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_store1_2reg,neon_store1_2reg_q")) ++ "thunderx2t99_ls_both") ++ ++(define_insn_reservation "thunderx2t99_asimd_store1_onelane" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_store1_one_lane,neon_store1_one_lane_q")) ++ "thunderx2t99_ls01,thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_store2_mult" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_store2_2reg,neon_store2_2reg_q")) ++ "thunderx2t99_ls_both,thunderx2t99_f01") ++ ++(define_insn_reservation "thunderx2t99_asimd_store2_onelane" 1 ++ (and (eq_attr "tune" "thunderx2t99") ++ (eq_attr "type" "neon_store2_one_lane,neon_store2_one_lane_q")) ++ "thunderx2t99_ls01,thunderx2t99_f01") only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/thunderx2t99p1.diff +++ gcc-5-5.4.0/debian/patches/thunderx2t99p1.diff @@ -0,0 +1,48 @@ +Description: Change -mcpu=thunderx2t99 's -mcpu=native support + Hi, + When I implemented the -mcpu=thunderx2t99 I did not have the Cavium + partno for ThunderX CN99xx, only the original part no. This patch + adds the new part no for the future versions of the chip. + . + OK? Bootstrapped and tested on aarch64-linux-gnu with no regressions. + . + Thanks, + Andrew + . + ChangeLog: + * config/aarch64/aarch64-cores.def (thunderx2t99): Move to under 'C" + cores and change the partno/implementer to be correct. + (thunderx2t99p1): New core which replaces thunderx2t99 and still has + the 'B" as the implementer. + . + Backported to gcc-5 by dann frazier +Author: Andrew Pinski +Origin: https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=245461 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1727540 +Applied-Upstream: svn://gcc.gnu.org/svn/gcc/trunk@245461 + +--- a/src/gcc/config/aarch64/aarch64-cores.def 2017-10-20 18:10:47.523130202 -0400 ++++ b/src/gcc/config/aarch64/aarch64-cores.def 2017-10-20 18:19:03.407936680 -0400 +@@ -48,10 +48,11 @@ AARCH64_CORE("exynos-m1", exynosm1, c + AARCH64_CORE("qdf24xx", qdf24xx, cortexa57, 8A, AARCH64_FL_FOR_ARCH8 | AARCH64_FL_CRC | AARCH64_FL_CRYPTO, cortexa57, "0x51", "0x800") + AARCH64_CORE("thunderx", thunderx, thunderx, 8A, AARCH64_FL_FOR_ARCH8 | AARCH64_FL_CRC | AARCH64_FL_CRYPTO, thunderx, "0x43", "0x0a1") + AARCH64_CORE("xgene1", xgene1, xgene1, 8A, AARCH64_FL_FOR_ARCH8, xgene1, "0x50", "0x000") ++AARCH64_CORE("thunderx2t99", thunderx2t99, thunderx2t99, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x43", "0x0af") + + /* V8.1 Architecture Processors. */ + +-AARCH64_CORE("thunderx2t99", thunderx2t99, thunderx2t99, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") ++AARCH64_CORE("thunderx2t99p1", thunderx2t99p1, thunderx2t99, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") + AARCH64_CORE("vulcan", vulcan, thunderx2t99, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") + + /* V8 big.LITTLE implementations. */ +diff -urpN a/src/gcc/config/aarch64/aarch64-tune.md b/src/gcc/config/aarch64/aarch64-tune.md +--- a/src/gcc/config/aarch64/aarch64-tune.md 2017-10-20 17:23:44.212332273 -0400 ++++ b/src/gcc/config/aarch64/aarch64-tune.md 2017-10-20 18:19:42.484113735 -0400 +@@ -1,5 +1,5 @@ + ;; -*- buffer-read-only: t -*- + ;; Generated automatically by gentune.sh from aarch64-cores.def + (define_attr "tune" +- "cortexa35,cortexa53,cortexa57,cortexa72,exynosm1,qdf24xx,thunderx,xgene1,thunderx2t99,vulcan,cortexa57cortexa53,cortexa72cortexa53" ++ "cortexa35,cortexa53,cortexa57,cortexa72,exynosm1,qdf24xx,thunderx,xgene1,thunderx2t99,thunderx2t99p1,vulcan,cortexa57cortexa53,cortexa72cortexa53" + (const (symbol_ref "((enum attr_tune) aarch64_tune)"))) only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/vulcan-costs.diff +++ gcc-5-5.4.0/debian/patches/vulcan-costs.diff @@ -0,0 +1,258 @@ +# DP: Add cost model for vulcan CPU + +From: jgreenhalgh +Date: Fri, 15 Jul 2016 11:17:53 +0000 +Subject: [PATCH] [PATCH/AARCH64] Add rtx_costs routine for vulcan. + +gcc/ChangeLog: + +2016-07-15 Virendra Pathak + Julian Brown + + * config/aarch64/aarch64-cores.def: Update vulcan COSTS. + * config/aarch64/aarch64-cost-tables.h + (vulcan_extra_costs): New variable. + * config/aarch64/aarch64.c + (vulcan_addrcost_table): Likewise. + (vulcan_regmove_cost): Likewise. + (vulcan_vector_cost): Likewise. + (vulcan_branch_cost): Likewise. + (vulcan_tunings): Likewise. + +[dannf: backported by removing approx_modes function pointer] + +diff -urpN a/src/gcc/config/aarch64/aarch64.c b/src/gcc/config/aarch64/aarch64.c +--- a/src/gcc/config/aarch64/aarch64.c 2016-07-15 16:14:24.268328586 +0000 ++++ b/src/gcc/config/aarch64/aarch64.c 2016-07-15 16:15:52.603299822 +0000 +@@ -250,6 +250,22 @@ static const struct cpu_addrcost_table x + 0, /* imm_offset */ + }; + ++static const struct cpu_addrcost_table vulcan_addrcost_table = ++{ ++ { ++ 0, /* hi */ ++ 0, /* si */ ++ 0, /* di */ ++ 2, /* ti */ ++ }, ++ 0, /* pre_modify */ ++ 0, /* post_modify */ ++ 2, /* register_offset */ ++ 3, /* register_sextend */ ++ 3, /* register_zextend */ ++ 0, /* imm_offset */ ++}; ++ + static const struct cpu_regmove_cost generic_regmove_cost = + { + 1, /* GP2GP */ +@@ -308,6 +324,15 @@ static const struct cpu_regmove_cost xge + 2 /* FP2FP */ + }; + ++static const struct cpu_regmove_cost vulcan_regmove_cost = ++{ ++ 1, /* GP2GP */ ++ /* Avoid the use of int<->fp moves for spilling. */ ++ 8, /* GP2FP */ ++ 8, /* FP2GP */ ++ 4 /* FP2FP */ ++}; ++ + /* Generic costs for vector insn classes. */ + static const struct cpu_vector_cost generic_vector_cost = + { +@@ -379,6 +404,23 @@ static const struct cpu_vector_cost xgen + 1 /* cond_not_taken_branch_cost */ + }; + ++/* Costs for vector insn classes for Vulcan. */ ++static const struct cpu_vector_cost vulcan_vector_cost = ++{ ++ 6, /* scalar_stmt_cost */ ++ 4, /* scalar_load_cost */ ++ 1, /* scalar_store_cost */ ++ 6, /* vec_stmt_cost */ ++ 6, /* vec_to_scalar_cost */ ++ 5, /* scalar_to_vec_cost */ ++ 8, /* vec_align_load_cost */ ++ 8, /* vec_unalign_load_cost */ ++ 4, /* vec_unalign_store_cost */ ++ 4, /* vec_store_cost */ ++ 2, /* cond_taken_branch_cost */ ++ 1 /* cond_not_taken_branch_cost */ ++}; ++ + /* Generic costs for branch instructions. */ + static const struct cpu_branch_cost generic_branch_cost = + { +@@ -393,6 +436,13 @@ static const struct cpu_branch_cost cort + 3 /* Unpredictable. */ + }; + ++/* Branch costs for Vulcan. */ ++static const struct cpu_branch_cost vulcan_branch_cost = ++{ ++ 1, /* Predictable. */ ++ 3 /* Unpredictable. */ ++}; ++ + static const struct tune_params generic_tunings = + { + &cortexa57_extra_costs, +@@ -589,6 +639,30 @@ static const struct tune_params xgene1_t + (AARCH64_EXTRA_TUNE_APPROX_RSQRT) /* tune_flags. */ + }; + ++static const struct tune_params vulcan_tunings = ++{ ++ &vulcan_extra_costs, ++ &vulcan_addrcost_table, ++ &vulcan_regmove_cost, ++ &vulcan_vector_cost, ++ &vulcan_branch_cost, ++ 4, /* memmov_cost. */ ++ 4, /* issue_rate. */ ++ AARCH64_FUSE_NOTHING, /* fuseable_ops. */ ++ 16, /* function_align. */ ++ 8, /* jump_align. */ ++ 16, /* loop_align. */ ++ 3, /* int_reassoc_width. */ ++ 2, /* fp_reassoc_width. */ ++ 2, /* vec_reassoc_width. */ ++ 2, /* min_div_recip_mul_sf. */ ++ 2, /* min_div_recip_mul_df. */ ++ 0, /* max_case_values. */ ++ 0, /* cache_line_size. */ ++ tune_params::AUTOPREFETCHER_OFF, /* autoprefetcher_model. */ ++ (AARCH64_EXTRA_TUNE_NONE) /* tune_flags. */ ++}; ++ + /* Support for fine-grained override of the tuning structures. */ + struct aarch64_tuning_override_function + { +diff -urpN a/src/gcc/config/aarch64/aarch64-cores.def b/src/gcc/config/aarch64/aarch64-cores.def +--- a/src/gcc/config/aarch64/aarch64-cores.def 2016-07-15 16:14:24.272328721 +0000 ++++ b/src/gcc/config/aarch64/aarch64-cores.def 2016-07-15 16:15:26.730430056 +0000 +@@ -51,7 +51,7 @@ AARCH64_CORE("xgene1", xgene1, x + + /* V8.1 Architecture Processors. */ + +-AARCH64_CORE("vulcan", vulcan, cortexa57, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, cortexa57, "0x42", "0x516") ++AARCH64_CORE("vulcan", vulcan, cortexa57, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, vulcan, "0x42", "0x516") + + /* V8 big.LITTLE implementations. */ + +diff -urpN a/src/gcc/config/aarch64/aarch64-cost-tables.h b/src/gcc/config/aarch64/aarch64-cost-tables.h +--- a/src/gcc/config/aarch64/aarch64-cost-tables.h 2016-07-15 16:14:24.272328721 +0000 ++++ b/src/gcc/config/aarch64/aarch64-cost-tables.h 2016-07-15 16:15:26.730430056 +0000 +@@ -127,6 +127,108 @@ const struct cpu_cost_table thunderx_ext + } + }; + ++const struct cpu_cost_table vulcan_extra_costs = ++{ ++ /* ALU */ ++ { ++ 0, /* Arith. */ ++ 0, /* Logical. */ ++ 0, /* Shift. */ ++ 0, /* Shift_reg. */ ++ COSTS_N_INSNS (1), /* Arith_shift. */ ++ COSTS_N_INSNS (1), /* Arith_shift_reg. */ ++ COSTS_N_INSNS (1), /* Log_shift. */ ++ COSTS_N_INSNS (1), /* Log_shift_reg. */ ++ 0, /* Extend. */ ++ COSTS_N_INSNS (1), /* Extend_arith. */ ++ 0, /* Bfi. */ ++ 0, /* Bfx. */ ++ COSTS_N_INSNS (3), /* Clz. */ ++ 0, /* Rev. */ ++ 0, /* Non_exec. */ ++ true /* Non_exec_costs_exec. */ ++ }, ++ { ++ /* MULT SImode */ ++ { ++ COSTS_N_INSNS (4), /* Simple. */ ++ COSTS_N_INSNS (4), /* Flag_setting. */ ++ COSTS_N_INSNS (4), /* Extend. */ ++ COSTS_N_INSNS (5), /* Add. */ ++ COSTS_N_INSNS (5), /* Extend_add. */ ++ COSTS_N_INSNS (18) /* Idiv. */ ++ }, ++ /* MULT DImode */ ++ { ++ COSTS_N_INSNS (4), /* Simple. */ ++ 0, /* Flag_setting. */ ++ COSTS_N_INSNS (4), /* Extend. */ ++ COSTS_N_INSNS (5), /* Add. */ ++ COSTS_N_INSNS (5), /* Extend_add. */ ++ COSTS_N_INSNS (26) /* Idiv. */ ++ } ++ }, ++ /* LD/ST */ ++ { ++ COSTS_N_INSNS (4), /* Load. */ ++ COSTS_N_INSNS (4), /* Load_sign_extend. */ ++ COSTS_N_INSNS (5), /* Ldrd. */ ++ COSTS_N_INSNS (4), /* Ldm_1st. */ ++ 1, /* Ldm_regs_per_insn_1st. */ ++ 1, /* Ldm_regs_per_insn_subsequent. */ ++ COSTS_N_INSNS (4), /* Loadf. */ ++ COSTS_N_INSNS (4), /* Loadd. */ ++ COSTS_N_INSNS (4), /* Load_unaligned. */ ++ 0, /* Store. */ ++ 0, /* Strd. */ ++ 0, /* Stm_1st. */ ++ 1, /* Stm_regs_per_insn_1st. */ ++ 1, /* Stm_regs_per_insn_subsequent. */ ++ 0, /* Storef. */ ++ 0, /* Stored. */ ++ 0, /* Store_unaligned. */ ++ COSTS_N_INSNS (1), /* Loadv. */ ++ COSTS_N_INSNS (1) /* Storev. */ ++ }, ++ { ++ /* FP SFmode */ ++ { ++ COSTS_N_INSNS (4), /* Div. */ ++ COSTS_N_INSNS (1), /* Mult. */ ++ COSTS_N_INSNS (1), /* Mult_addsub. */ ++ COSTS_N_INSNS (1), /* Fma. */ ++ COSTS_N_INSNS (1), /* Addsub. */ ++ COSTS_N_INSNS (1), /* Fpconst. */ ++ COSTS_N_INSNS (1), /* Neg. */ ++ COSTS_N_INSNS (1), /* Compare. */ ++ COSTS_N_INSNS (2), /* Widen. */ ++ COSTS_N_INSNS (2), /* Narrow. */ ++ COSTS_N_INSNS (2), /* Toint. */ ++ COSTS_N_INSNS (2), /* Fromint. */ ++ COSTS_N_INSNS (2) /* Roundint. */ ++ }, ++ /* FP DFmode */ ++ { ++ COSTS_N_INSNS (6), /* Div. */ ++ COSTS_N_INSNS (1), /* Mult. */ ++ COSTS_N_INSNS (1), /* Mult_addsub. */ ++ COSTS_N_INSNS (1), /* Fma. */ ++ COSTS_N_INSNS (1), /* Addsub. */ ++ COSTS_N_INSNS (1), /* Fpconst. */ ++ COSTS_N_INSNS (1), /* Neg. */ ++ COSTS_N_INSNS (1), /* Compare. */ ++ COSTS_N_INSNS (2), /* Widen. */ ++ COSTS_N_INSNS (2), /* Narrow. */ ++ COSTS_N_INSNS (2), /* Toint. */ ++ COSTS_N_INSNS (2), /* Fromint. */ ++ COSTS_N_INSNS (2) /* Roundint. */ ++ } ++ }, ++ /* Vector */ ++ { ++ COSTS_N_INSNS (1) /* Alu. */ ++ } ++}; + + + #endif only in patch2: unchanged: --- gcc-5-5.4.0.orig/debian/patches/vulcan-to-thunderx2t99.diff +++ gcc-5-5.4.0/debian/patches/vulcan-to-thunderx2t99.diff @@ -0,0 +1,129 @@ +Description: Add -mcpu=thunderx2t99 support + * config/aarch64/aarch64-cores.def: Add thunderx2t99. Change vulcan + to reference thunderx2t99 for the tuning structure + * config/aarch64/aarch64-cost-tables.h (vulcan_extra_costs): Rename to ... + (thunderx2t99_extra_costs): This. + * config/aarch64/aarch64-tune.md: Regenerate. + * config/aarch64/aarch64.c (vulcan_addrcost_table): Rename to ... + (vulcan_addrcost_table): This. + (vulcan_regmove_cost): Rename to ... + (thunderx2t99_regmove_cost): This. + (vulcan_vector_cost): Rename to ... + (thunderx2t99_vector_cost): this. + (vulcan_branch_cost): Rename to ... + (thunderx2t99_branch_cost): This. + (vulcan_tunings): Rename to ... + (thunderx2t99_tunings): This and s/vulcan/thunderx2t99 . + * doc/invoke.texi (AARCH64/mtune): Add thunderx2t99. + . + Backported to gcc-5 by dann frazier +Author: Andrew Pinski +Origin: https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=244235 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1727540 +Applied-Upstream: svn://gcc.gnu.org/svn/gcc/trunk@244235 + +--- a/src/gcc/config/aarch64/aarch64.c 2017-10-20 17:04:37.205144993 -0400 ++++ b/src/gcc/config/aarch64/aarch64.c 2017-10-20 17:19:39.650846958 -0400 +@@ -267,7 +267,7 @@ static const struct cpu_addrcost_table x + 0, /* imm_offset */ + }; + +-static const struct cpu_addrcost_table vulcan_addrcost_table = ++static const struct cpu_addrcost_table thunderx2t99_addrcost_table = + { + { + 0, /* hi */ +@@ -331,7 +331,7 @@ static const struct cpu_regmove_cost xge + 2 /* FP2FP */ + }; + +-static const struct cpu_regmove_cost vulcan_regmove_cost = ++static const struct cpu_regmove_cost thunderx2t99_regmove_cost = + { + 1, /* GP2GP */ + /* Avoid the use of int<->fp moves for spilling. */ +@@ -392,7 +392,7 @@ static const struct cpu_vector_cost xgen + }; + + /* Costs for vector insn classes for Vulcan. */ +-static const struct cpu_vector_cost vulcan_vector_cost = ++static const struct cpu_vector_cost thunderx2t99_vector_cost = + { + 6, /* scalar_stmt_cost */ + 4, /* scalar_load_cost */ +@@ -423,7 +423,7 @@ static const struct cpu_branch_cost cort + }; + + /* Branch costs for Vulcan. */ +-static const struct cpu_branch_cost vulcan_branch_cost = ++static const struct cpu_branch_cost thunderx2t99_branch_cost = + { + 1, /* Predictable. */ + 3 /* Unpredictable. */ +@@ -601,13 +601,13 @@ static const struct tune_params xgene1_t + (AARCH64_EXTRA_TUNE_NONE) /* tune_flags. */ + }; + +-static const struct tune_params vulcan_tunings = ++static const struct tune_params thunderx2t99_tunings = + { +- &vulcan_extra_costs, +- &vulcan_addrcost_table, +- &vulcan_regmove_cost, +- &vulcan_vector_cost, +- &vulcan_branch_cost, ++ &thunderx2t99_extra_costs, ++ &thunderx2t99_addrcost_table, ++ &thunderx2t99_regmove_cost, ++ &thunderx2t99_vector_cost, ++ &thunderx2t99_branch_cost, + 4, /* memmov_cost. */ + 4, /* issue_rate. */ + AARCH64_FUSE_NOTHING, /* fuseable_ops. */ +diff -urpN a/src/gcc/config/aarch64/aarch64-cores.def b/src/gcc/config/aarch64/aarch64-cores.def +--- a/src/gcc/config/aarch64/aarch64-cores.def 2017-10-20 17:04:37.209145404 -0400 ++++ b/src/gcc/config/aarch64/aarch64-cores.def 2017-10-20 17:19:39.650846958 -0400 +@@ -51,7 +51,8 @@ AARCH64_CORE("xgene1", xgene1, x + + /* V8.1 Architecture Processors. */ + +-AARCH64_CORE("vulcan", vulcan, cortexa57, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, vulcan, "0x42", "0x516") ++AARCH64_CORE("thunderx2t99", thunderx2t99, cortexa57, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") ++AARCH64_CORE("vulcan", vulcan, cortexa57, 8_1A, AARCH64_FL_FOR_ARCH8_1 | AARCH64_FL_CRYPTO, thunderx2t99, "0x42", "0x516") + + /* V8 big.LITTLE implementations. */ + +diff -urpN a/src/gcc/config/aarch64/aarch64-cost-tables.h b/src/gcc/config/aarch64/aarch64-cost-tables.h +--- a/src/gcc/config/aarch64/aarch64-cost-tables.h 2017-10-20 17:04:37.209145404 -0400 ++++ b/src/gcc/config/aarch64/aarch64-cost-tables.h 2017-10-20 17:19:39.650846958 -0400 +@@ -127,7 +127,7 @@ const struct cpu_cost_table thunderx_ext + } + }; + +-const struct cpu_cost_table vulcan_extra_costs = ++const struct cpu_cost_table thunderx2t99_extra_costs = + { + /* ALU */ + { +diff -urpN a/src/gcc/config/aarch64/aarch64-tune.md b/src/gcc/config/aarch64/aarch64-tune.md +--- a/src/gcc/config/aarch64/aarch64-tune.md 2017-10-20 17:04:37.173141710 -0400 ++++ b/src/gcc/config/aarch64/aarch64-tune.md 2017-10-20 17:23:44.212332273 -0400 +@@ -1,5 +1,5 @@ + ;; -*- buffer-read-only: t -*- + ;; Generated automatically by gentune.sh from aarch64-cores.def + (define_attr "tune" +- "cortexa35,cortexa53,cortexa57,cortexa72,exynosm1,qdf24xx,thunderx,xgene1,vulcan,cortexa57cortexa53,cortexa72cortexa53" ++ "cortexa35,cortexa53,cortexa57,cortexa72,exynosm1,qdf24xx,thunderx,xgene1,thunderx2t99,vulcan,cortexa57cortexa53,cortexa72cortexa53" + (const (symbol_ref "((enum attr_tune) aarch64_tune)"))) +diff -urpN a/src/gcc/doc/invoke.texi b/src/gcc/doc/invoke.texi +--- a/src/gcc/doc/invoke.texi 2017-10-20 17:04:39.461376437 -0400 ++++ b/src/gcc/doc/invoke.texi 2017-10-20 17:19:39.658847810 -0400 +@@ -12412,7 +12412,7 @@ Specify the name of the target processor + performance of the code. Permissible values for this option are: + @samp{generic}, @samp{cortex-a35}, @samp{cortex-a53}, @samp{cortex-a57}, + @samp{cortex-a72}, @samp{exynos-m1}, @samp{qdf24xx}, @samp{thunderx}, +-@samp{vulcan}, @samp{xgene1}. ++@samp{thunderx2t99}, @samp{vulcan}, @samp{xgene1}. + + Additionally, this option can specify that GCC should tune the performance + of the code for a big.LITTLE system. Permissible values for this