[packages/gcc] - rel 5; add retpoline support (used for spectre mitigation)
arekm
arekm at pld-linux.org
Sun Jan 14 19:17:45 CET 2018
commit 95542190f2ac42f1619b8dd94026fe8b720a8687
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Sun Jan 14 19:17:36 2018 +0100
- rel 5; add retpoline support (used for spectre mitigation)
Revert-eeb6872bf.patch | 82 -
gcc-retpoline.patch | 4863 ++++++++++++++++++++++++++++++++++++++++++++++++
gcc.spec | 14 +-
3 files changed, 4872 insertions(+), 87 deletions(-)
---
diff --git a/gcc.spec b/gcc.spec
index 9b9829b..6512115 100644
--- a/gcc.spec
+++ b/gcc.spec
@@ -105,7 +105,7 @@ Summary(pl.UTF-8): Kolekcja kompilatorów GNU: kompilator C i pliki współdziel
Summary(pt_BR.UTF-8): Coleção dos compiladores GNU: o compilador C e arquivos compartilhados
Name: gcc
Version: %{major_ver}.%{minor_ver}
-Release: 4
+Release: 5
Epoch: 6
License: GPL v3+
Group: Development/Languages
@@ -117,12 +117,15 @@ Source3: libffi.pc.in
Source4: branch.sh
# use branch.sh to update glibc-branch.diff
Patch100: %{name}-branch.diff
-# Patch100-md5: 29b75338857af540acf861b49e306285
+# Patch100-md5: 4a58511a43a1387d3f2e004ff67d6fb6
+# https://github.com/hjl-tools/gcc.git
+# git log -p --reverse origin/gcc-7-branch..hjl/indirect/gcc-7-branch/master > ~/rpm/packages/gcc/gcc-retpoline.patch
+Patch101: %{name}-retpoline.patch
Patch0: %{name}-info.patch
Patch2: %{name}-nodebug.patch
Patch3: %{name}-ada-link.patch
Patch4: %{name}-ada-x32.patch
-Patch5: Revert-eeb6872bf.patch
+
Patch6: %{name}-pr81481.patch
Patch10: %{name}-moresparcs.patch
@@ -2798,12 +2801,13 @@ Extensions dla języka C.
%prep
%setup -q
-#%patch100 -p0
+%patch100 -p0
+%patch101 -p1
%patch0 -p1
%patch2 -p1
%patch3 -p1
%patch4 -p1
-%patch5 -p1
+
%patch6 -p0
%patch10 -p1
diff --git a/Revert-eeb6872bf.patch b/Revert-eeb6872bf.patch
deleted file mode 100644
index 1688c14..0000000
--- a/Revert-eeb6872bf.patch
+++ /dev/null
@@ -1,82 +0,0 @@
-commit 43d83a70267a9e5c456d28de8e7348820446b712
-Author: Bartłomiej Piotrowski <bpiotrowski at archlinux.org>
-Date: Tue May 16 07:50:42 2017 +0200
-
- Revert "Prevent LTO wrappers to process a recursive execution"
-
- This reverts commit eeb6872bfdfd1e71b27de6f62a5f5c08a2efb015.
-
-diff --git a/gcc/file-find.c b/gcc/file-find.c
-index b072a4993d76..b5a1fe8494e8 100644
---- a/gcc/file-find.c
-+++ b/gcc/file-find.c
-@@ -208,38 +208,3 @@ prefix_from_string (const char *p, struct path_prefix *pprefix)
- }
- free (nstore);
- }
--
--void
--remove_prefix (const char *prefix, struct path_prefix *pprefix)
--{
-- struct prefix_list *remove, **prev, **remove_prev = NULL;
-- int max_len = 0;
--
-- if (pprefix->plist)
-- {
-- prev = &pprefix->plist;
-- for (struct prefix_list *pl = pprefix->plist; pl->next; pl = pl->next)
-- {
-- if (strcmp (prefix, pl->prefix) == 0)
-- {
-- remove = pl;
-- remove_prev = prev;
-- continue;
-- }
--
-- int l = strlen (pl->prefix);
-- if (l > max_len)
-- max_len = l;
--
-- prev = &pl;
-- }
--
-- if (remove_prev)
-- {
-- *remove_prev = remove->next;
-- free (remove);
-- }
--
-- pprefix->max_len = max_len;
-- }
--}
-diff --git a/gcc/file-find.h b/gcc/file-find.h
-index 8f49a3af273e..407feba26e74 100644
---- a/gcc/file-find.h
-+++ b/gcc/file-find.h
-@@ -41,7 +41,6 @@ extern void find_file_set_debug (bool);
- extern char *find_a_file (struct path_prefix *, const char *, int);
- extern void add_prefix (struct path_prefix *, const char *);
- extern void add_prefix_begin (struct path_prefix *, const char *);
--extern void remove_prefix (const char *prefix, struct path_prefix *);
- extern void prefix_from_env (const char *, struct path_prefix *);
- extern void prefix_from_string (const char *, struct path_prefix *);
-
-diff --git a/gcc/gcc-ar.c b/gcc/gcc-ar.c
-index 78d2fc1ad306..d5d80e042e5a 100644
---- a/gcc/gcc-ar.c
-+++ b/gcc/gcc-ar.c
-@@ -194,14 +194,6 @@ main (int ac, char **av)
- #ifdef CROSS_DIRECTORY_STRUCTURE
- real_exe_name = concat (target_machine, "-", PERSONALITY, NULL);
- #endif
-- /* Do not search original location in the same folder. */
-- char *exe_folder = lrealpath (av[0]);
-- exe_folder[strlen (exe_folder) - strlen (lbasename (exe_folder))] = '\0';
-- char *location = concat (exe_folder, PERSONALITY, NULL);
--
-- if (access (location, X_OK) == 0)
-- remove_prefix (exe_folder, &path);
--
- exe_name = find_a_file (&path, real_exe_name, X_OK);
- if (!exe_name)
- {
diff --git a/gcc-retpoline.patch b/gcc-retpoline.patch
new file mode 100644
index 0000000..0f79e83
--- /dev/null
+++ b/gcc-retpoline.patch
@@ -0,0 +1,4863 @@
+commit 9005adea32ef0cc14b3ef7ceacf5b67bf0862194
+Author: H.J. Lu <hjl.tools at gmail.com>
+Date: Mon Nov 6 09:11:08 2017 -0800
+
+ i386: Move struct ix86_frame to machine_function
+
+ Make ix86_frame available to i386 code generation.
+
+ * 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.
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index 8a3782c0298..813337242d8 100644
+--- a/gcc/config/i386/i386.c
++++ b/gcc/config/i386/i386.c
+@@ -2444,53 +2444,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;
+
+@@ -2582,7 +2535,7 @@ static unsigned int ix86_function_arg_boundary (machine_mode,
+ 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, HOST_WIDE_INT);
+@@ -11903,7 +11856,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);
+ }
+@@ -12389,8 +12343,8 @@ ix86_can_eliminate (const int from, const int to)
+ 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;
+@@ -12429,8 +12383,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;
+@@ -13737,7 +13692,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))
+ {
+@@ -14405,7 +14361,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
+@@ -14915,7 +14872,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
+diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
+index 9c776dc5172..f9b91286a01 100644
+--- a/gcc/config/i386/i386.h
++++ b/gcc/config/i386/i386.h
+@@ -2451,9 +2451,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
+@@ -2512,6 +2559,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;
+@@ -2594,6 +2644,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
+
+commit b721283e4f4ff378a0bee2255b7d62163eab9f1e
+Author: hjl <hjl at 138bc75d-0d04-0410-961f-82ee72b054a4>
+Date: Mon Nov 6 23:04:15 2017 +0000
+
+ 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.
+
+ Tested on x86-64.
+
+ * 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/trunk@254480 138bc75d-0d04-0410-961f-82ee72b054a4
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index 813337242d8..397ef7cac26 100644
+--- a/gcc/config/i386/i386.c
++++ b/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;
+-
+ /* Don't use `ret' instruction in interrupt handler. */
+ if (! reload_completed
+ || frame_pointer_needed
+@@ -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
+
+commit 3b89cfddd6276d3f13c210ed11ef638515392a04
+Author: H.J. Lu <hjl.tools at gmail.com>
+Date: Tue Nov 28 10:26:35 2017 -0800
+
+ 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.
+
+ * config/i386/i386.c (ix86_expand_prologue): Use reference of
+ struct ix86_frame.
+ (ix86_expand_epilogue): Likewise.
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index 397ef7cac26..986e6d79584 100644
+--- a/gcc/config/i386/i386.c
++++ b/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
+
+commit c89890ab6730606ecc8d3f5937fe352341f0f713
+Author: H.J. Lu <hjl.tools at gmail.com>
+Date: Sat Jan 6 22:29:55 2018 -0800
+
+ 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/
+
+ * 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): 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/
+
+ * 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.
+
+diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h
+index 542cd0f3d67..efcdc3b1a14 100644
+--- a/gcc/config/i386/i386-opts.h
++++ b/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
+diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
+index d2cccf14735..bcdd9872db9 100644
+--- a/gcc/config/i386/i386-protos.h
++++ b/gcc/config/i386/i386-protos.h
+@@ -313,6 +313,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);
+ extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
+ enum machine_mode mode);
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index 986e6d79584..f1c58faa035 100644
+--- a/gcc/config/i386/i386.c
++++ b/gcc/config/i386/i386.c
+@@ -4212,12 +4212,23 @@ make_pass_stv (gcc::context *ctxt)
+ return new pass_stv (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. */
+
+ 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
+@@ -7148,6 +7159,37 @@ ix86_set_func_type (tree fndecl)
+ }
+ }
+
++/* 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. */
+@@ -7163,7 +7205,10 @@ ix86_set_current_function (tree fndecl)
+ one is extern inline and one isn't. Call ix86_set_func_type
+ to set the func_type field. */
+ if (fndecl != NULL_TREE)
+- ix86_set_func_type (fndecl);
++ {
++ ix86_set_func_type (fndecl);
++ ix86_set_indirect_branch_type (fndecl);
++ }
+ return;
+ }
+
+@@ -7183,6 +7228,7 @@ ix86_set_current_function (tree fndecl)
+ }
+
+ ix86_set_func_type (fndecl);
++ ix86_set_indirect_branch_type (fndecl);
+
+ tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl);
+ if (new_tree == NULL_TREE)
+@@ -11920,6 +11966,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
+@@ -11946,11 +12206,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;
+
+@@ -28446,12 +28727,292 @@ ix86_nopic_noplt_attribute_p (rtx call_op)
+ return false;
+ }
+
++/* 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;
+
+@@ -28461,10 +29022,21 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
+ {
+ if (ix86_nopic_noplt_attribute_p (call_op))
+ {
++ direct_p = false;
+ if (TARGET_64BIT)
+- xasm = "%!jmp\t{*%p0 at GOTPCREL(%%rip)|[QWORD PTR %p0 at GOTPCREL[rip]]}";
++ {
++ if (output_indirect_p)
++ xasm = "{%p0 at GOTPCREL(%%rip)|[QWORD PTR %p0 at GOTPCREL[rip]]}";
++ else
++ xasm = "%!jmp\t{*%p0 at GOTPCREL(%%rip)|[QWORD PTR %p0 at GOTPCREL[rip]]}";
++ }
+ else
+- xasm = "%!jmp\t{*%p0 at GOT|[DWORD PTR %p0 at GOT]}";
++ {
++ if (output_indirect_p)
++ xasm = "{%p0 at GOT|[DWORD PTR %p0 at GOT]}";
++ else
++ xasm = "%!jmp\t{*%p0 at GOT|[DWORD PTR %p0 at GOT]}";
++ }
+ }
+ else
+ xasm = "%!jmp\t%P0";
+@@ -28474,9 +29046,17 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
+ else if (TARGET_SEH)
+ xasm = "%!rex.W jmp\t%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 "";
+ }
+
+@@ -28514,18 +29094,37 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
+ {
+ if (ix86_nopic_noplt_attribute_p (call_op))
+ {
++ direct_p = false;
+ if (TARGET_64BIT)
+- xasm = "%!call\t{*%p0 at GOTPCREL(%%rip)|[QWORD PTR %p0 at GOTPCREL[rip]]}";
++ {
++ if (output_indirect_p)
++ xasm = "{%p0 at GOTPCREL(%%rip)|[QWORD PTR %p0 at GOTPCREL[rip]]}";
++ else
++ xasm = "%!call\t{*%p0 at GOTPCREL(%%rip)|[QWORD PTR %p0 at GOTPCREL[rip]]}";
++ }
+ else
+- xasm = "%!call\t{*%p0 at GOT|[DWORD PTR %p0 at GOT]}";
++ {
++ if (output_indirect_p)
++ xasm = "{%p0 at GOT|[DWORD PTR %p0 at GOT]}";
++ else
++ xasm = "%!call\t{*%p0 at GOT|[DWORD PTR %p0 at GOT]}";
++ }
+ }
+ else
+ 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";
+@@ -41444,7 +42043,7 @@ ix86_handle_struct_attribute (tree *node, tree name, tree, int,
+ }
+
+ 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)
+@@ -41453,6 +42052,29 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int,
+ 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;
+ }
+
+@@ -45761,6 +46383,8 @@ static const struct attribute_spec ix86_attribute_table[] =
+ ix86_handle_interrupt_attribute, false },
+ { "no_caller_saved_registers", 0, 0, false, true, true,
+ ix86_handle_no_caller_saved_registers_attribute, false },
++ { "indirect_branch", 1, 1, true, false, false,
++ ix86_handle_fndecl_attribute, false },
+
+ /* End element. */
+ { NULL, 0, 0, false, false, false, NULL, false }
+diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
+index f9b91286a01..9d2209e605b 100644
+--- a/gcc/config/i386/i386.h
++++ b/gcc/config/i386/i386.h
+@@ -2609,6 +2609,13 @@ struct GTY(()) machine_function {
+ /* Function type. */
+ ENUM_BITFIELD(function_type) func_type : 2;
+
++ /* 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;
++
+ /* If true, the current function is a function specified with
+ the "interrupt" or "no_caller_saved_registers" attribute. */
+ BOOL_BITFIELD no_caller_saved_registers : 1;
+diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
+index 0281bb5f06c..e32f2311065 100644
+--- a/gcc/config/i386/i386.md
++++ b/gcc/config/i386/i386.md
+@@ -11625,13 +11625,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")
+ (set_attr "maybe_prefix_bnd" "1")])
+
+@@ -11674,14 +11679,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")
+ (set_attr "maybe_prefix_bnd" "1")])
+
+@@ -12352,8 +12362,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")
+ (set_attr "maybe_prefix_bnd" "1")])
+
+diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
+index 9384e29b1de..c076d9c70ab 100644
+--- a/gcc/config/i386/i386.opt
++++ b/gcc/config/i386/i386.opt
+@@ -927,3 +927,23 @@ Attempt to avoid generating instruction sequences containing ret bytes.
+ mgeneral-regs-only
+ Target Report RejectNegative Mask(GENERAL_REGS_ONLY) Var(ix86_target_flags) Save
+ Generate code which uses only the general registers.
++
++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)
+diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
+index ba309d01a9b..935381da6fa 100644
+--- a/gcc/doc/extend.texi
++++ b/gcc/doc/extend.texi
+@@ -5540,6 +5540,16 @@ Specify which floating-point unit to use. You must specify the
+ @code{target("fpmath=sse,387")} option as
+ @code{target("fpmath=sse+387")} because the comma would separate
+ different options.
++
++ at item indirect_branch("@var{choice}")
++ at 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.
++ at samp{thunk-extern} converts indirect call and jump to external call
++and return thunk provided in a separate object file.
+ @end table
+
+ On the x86, the inliner does not inline a
+diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
+index 7311c10a754..4979c8c939d 100644
+--- a/gcc/doc/invoke.texi
++++ b/gcc/doc/invoke.texi
+@@ -1210,7 +1210,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
+--mmitigate-rop -mgeneral-regs-only}
++-mmitigate-rop -mgeneral-regs-only @gol
++-mindirect-branch=@var{choice}}
+
+ @emph{x86 Windows Options}
+ @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
+@@ -25686,6 +25687,17 @@ Generate code that uses only the general-purpose registers. This
+ prevents the compiler from using floating-point, vector, mask and bound
+ registers.
+
++ at item -mindirect-branch=@var{choice}
++ at opindex -mindirect-branch
++Convert indirect call and jump with @var{choice}. The default is
++ at samp{keep}, which keeps indirect call and jump unmodified.
++ at samp{thunk} converts indirect call and jump to call and return thunk.
++ at 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
+
+ These @samp{-m} switches are supported in addition to the above
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+new file mode 100644
+index 00000000000..d983e1c3e26
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+new file mode 100644
+index 00000000000..58f09b42d8a
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+new file mode 100644
+index 00000000000..f20d35c19b6
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+new file mode 100644
+index 00000000000..0eff8fb658a
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+new file mode 100644
+index 00000000000..a25b20dd808
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+@@ -0,0 +1,17 @@
++/* { dg-do compile { target *-*-linux* } } */
++/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
++
++extern void bar (void);
++
++void
++foo (void)
++{
++ bar ();
++}
++
++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar at GOT" } } */
++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+new file mode 100644
+index 00000000000..cff114a6c29
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+@@ -0,0 +1,18 @@
++/* { dg-do compile { target *-*-linux* } } */
++/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
++
++extern void bar (void);
++
++int
++foo (void)
++{
++ bar ();
++ return 0;
++}
++
++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar at GOT" } } */
++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
++/* { 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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+new file mode 100644
+index 00000000000..afdb6007986
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
+new file mode 100644
+index 00000000000..d64d978b699
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
+new file mode 100644
+index 00000000000..93067454d3d
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
+new file mode 100644
+index 00000000000..97744d65729
+--- /dev/null
++++ b/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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
+new file mode 100644
+index 00000000000..bfce3ea5cb2
+--- /dev/null
++++ b/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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
+new file mode 100644
+index 00000000000..0833606046b
+--- /dev/null
++++ b/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)} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
+new file mode 100644
+index 00000000000..2eba0fbd9b2
+--- /dev/null
++++ b/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)} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
+new file mode 100644
+index 00000000000..f58427eae11
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
+new file mode 100644
+index 00000000000..564ed39547c
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
+new file mode 100644
+index 00000000000..50fbee20a5a
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
+new file mode 100644
+index 00000000000..2976e67adce
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+new file mode 100644
+index 00000000000..da4bc98ef23
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+@@ -0,0 +1,19 @@
++/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
++/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
++
++void bar (char *);
++char buf[10];
++
++void
++foo (void)
++{
++ bar (buf);
++}
++
++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar at GOT" } } */
++/* { 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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+new file mode 100644
+index 00000000000..c64d12ef989
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+@@ -0,0 +1,20 @@
++/* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
++/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
++
++void bar (char *);
++char buf[10];
++
++int
++foo (void)
++{
++ bar (buf);
++ return 0;
++}
++
++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar at GOT" } } */
++/* { dg-final { scan-assembler "bnd jmp\[ \t\]*__x86_indirect_thunk" } } */
++/* { dg-final { scan-assembler "bnd jmp\[ \t\]*\.LIND" } } */
++/* { dg-final { scan-assembler-times "bnd call\[ \t\]*\.LIND" 2 } } */
++/* { dg-final { scan-assembler "bnd ret" } } */
++/* { dg-final { scan-assembler {\tpause} } } */
++/* { dg-final { scan-assembler {\tlfence} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
+new file mode 100644
+index 00000000000..49f27b49465
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
+new file mode 100644
+index 00000000000..a1e3eb6fc74
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
+new file mode 100644
+index 00000000000..395634e7e5c
+--- /dev/null
++++ b/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)} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
+new file mode 100644
+index 00000000000..fd3f63379a1
+--- /dev/null
++++ b/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)} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+new file mode 100644
+index 00000000000..ba2f92b6f34
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+@@ -0,0 +1,16 @@
++/* { dg-do compile { target *-*-linux* } } */
++/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
++
++extern void bar (void);
++
++void
++foo (void)
++{
++ bar ();
++}
++
++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar at GOT" } } */
++/* { 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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+new file mode 100644
+index 00000000000..0c5a2d472c6
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+@@ -0,0 +1,17 @@
++/* { dg-do compile { target *-*-linux* } } */
++/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
++
++extern void bar (void);
++
++int
++foo (void)
++{
++ bar ();
++ return 0;
++}
++
++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar at GOT" } } */
++/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */
++/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */
++/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
++/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
+new file mode 100644
+index 00000000000..665252327aa
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
+new file mode 100644
+index 00000000000..68c0ff713b3
+--- /dev/null
++++ b/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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
+new file mode 100644
+index 00000000000..e2da1fcb683
+--- /dev/null
++++ b/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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
+new file mode 100644
+index 00000000000..244fec708d6
+--- /dev/null
++++ b/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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
+new file mode 100644
+index 00000000000..107ebe32f54
+--- /dev/null
++++ b/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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+new file mode 100644
+index 00000000000..17b04ef2229
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+@@ -0,0 +1,17 @@
++/* { dg-do compile { target *-*-linux* } } */
++/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
++
++extern void bar (void);
++
++void
++foo (void)
++{
++ bar ();
++}
++
++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar at GOT" } } */
++/* { 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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+new file mode 100644
+index 00000000000..d9eb11285aa
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+@@ -0,0 +1,18 @@
++/* { dg-do compile { target *-*-linux* } } */
++/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
++
++extern void bar (void);
++
++int
++foo (void)
++{
++ bar ();
++ return 0;
++}
++
++/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*bar at GOT" } } */
++/* { 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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
+new file mode 100644
+index 00000000000..d02b1dcb1b9
+--- /dev/null
++++ b/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" } } */
+
+commit ad272ea92bdadd7f94bc1dafb35529959d3de1f0
+Author: H.J. Lu <hjl.tools at gmail.com>
+Date: Sat Jan 6 22:29:56 2018 -0800
+
+ 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/
+
+ * 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): 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/
+
+ * 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.
+
+diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
+index bcdd9872db9..42eece35766 100644
+--- a/gcc/config/i386/i386-protos.h
++++ b/gcc/config/i386/i386-protos.h
+@@ -314,6 +314,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);
+ extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
+ enum machine_mode mode);
+
+diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
+index f1c58faa035..4bfe2fa8c1d 100644
+--- a/gcc/config/i386/i386.c
++++ b/gcc/config/i386/i386.c
+@@ -7188,6 +7188,31 @@ ix86_set_indirect_branch_type (tree fndecl)
+ 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
+@@ -11990,8 +12015,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" : "";
+@@ -12006,7 +12035,10 @@ indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
+ 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
+ {
+@@ -12019,10 +12051,20 @@ indirect_thunk_name (char name[32], int regno, bool need_bnd_p)
+ }
+ 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);
++ }
+ }
+ }
+ }
+@@ -12117,7 +12159,7 @@ output_indirect_thunk_function (bool need_bnd_p, int regno)
+ 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));
+@@ -12160,6 +12202,35 @@ output_indirect_thunk_function (bool need_bnd_p, int regno)
+ 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);
++ ASM_OUTPUT_DEF (asm_out_file, alias, name);
++#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);
++ }
++#else
++ 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);
+@@ -28760,7 +28831,7 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
+ 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
+@@ -28869,7 +28940,7 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
+ 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
+@@ -29004,6 +29075,46 @@ ix86_output_indirect_jmp (rtx call_op, bool ret_p)
+ 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 *
+@@ -42075,6 +42186,28 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
+ }
+ }
+
++ 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;
+ }
+
+@@ -46385,6 +46518,8 @@ static const struct attribute_spec ix86_attribute_table[] =
+ ix86_handle_no_caller_saved_registers_attribute, false },
+ { "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 }
+diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
+index 9d2209e605b..45593068905 100644
+--- a/gcc/config/i386/i386.h
++++ b/gcc/config/i386/i386.h
+@@ -2616,6 +2616,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;
++
+ /* If true, the current function is a function specified with
+ the "interrupt" or "no_caller_saved_registers" attribute. */
+ BOOL_BITFIELD no_caller_saved_registers : 1;
+diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
+index e32f2311065..3ac79ff6ee6 100644
+--- a/gcc/config/i386/i386.md
++++ b/gcc/config/i386/i386.md
+@@ -12313,7 +12313,7 @@
+ (define_insn "simple_return_internal"
+ [(simple_return)]
+ "reload_completed"
+- "%!ret"
++ "* return ix86_output_function_return (false);"
+ [(set_attr "length" "1")
+ (set_attr "atom_unit" "jeu")
+ (set_attr "length_immediate" "0")
+@@ -12335,12 +12335,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")
+diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
+index c076d9c70ab..b07388d95a9 100644
+--- a/gcc/config/i386/i386.opt
++++ b/gcc/config/i386/i386.opt
+@@ -932,9 +932,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)
+diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
+index 935381da6fa..46e0a3623a6 100644
+--- a/gcc/doc/extend.texi
++++ b/gcc/doc/extend.texi
+@@ -5550,6 +5550,15 @@ 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.
++
++ at item function_return("@var{choice}")
++ at 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 x86, the inliner does not inline a
+diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
+index 4979c8c939d..f3eb54b1668 100644
+--- a/gcc/doc/invoke.texi
++++ b/gcc/doc/invoke.texi
+@@ -1211,7 +1211,7 @@ See RS/6000 and PowerPC Options.
+ -mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol
+ -malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol
+ -mmitigate-rop -mgeneral-regs-only @gol
+--mindirect-branch=@var{choice}}
++-mindirect-branch=@var{choice} -mfunction-return==@var{choice}}
+
+ @emph{x86 Windows Options}
+ @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
+@@ -25698,6 +25698,17 @@ 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}.
+
++ at item -mfunction-return=@var{choice}
++ at 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}.
++ at xref{Function Attributes}.
++
+ @end table
+
+ These @samp{-m} switches are supported in addition to the above
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
+index d983e1c3e26..f076155c91a 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-1.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
+index 58f09b42d8a..d7984f592fe 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-2.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
+index f20d35c19b6..3257d0a2e16 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-3.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
+index 0eff8fb658a..7cab2df6474 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-4.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+index a25b20dd808..b4836c38d6c 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-5.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile { target *-*-linux* } } */
+-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
++/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
+
+ extern void bar (void);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+index cff114a6c29..1f06bd1af74 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-6.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile { target *-*-linux* } } */
+-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
++/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk" } */
+
+ extern void bar (void);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
+index afdb6007986..0b3fef86a20 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-7.c
++++ b/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);
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
+index d64d978b699..5f6cfc17b56 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
+index 93067454d3d..b256160ec80 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
+index 97744d65729..567c95051d6 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
+index bfce3ea5cb2..3b662af7d5d 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
+index 0833606046b..98785a38248 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
+index 2eba0fbd9b2..a498a39e404 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
+index f58427eae11..66f295d1eb6 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c
++++ b/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);
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
+index 564ed39547c..d730d31bda1 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c
++++ b/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);
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
+index 50fbee20a5a..aacb814d737 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-1.c
++++ b/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];
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
+index 2976e67adce..7b44dda23df 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-2.c
++++ b/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];
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+index da4bc98ef23..70b4fb36eea 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-3.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+
+ void bar (char *);
+ char buf[10];
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+index c64d12ef989..3baf03ee77c 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-bnd-4.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile { target { *-*-linux* && { ! x32 } } } } */
+-/* { dg-options "-O2 -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
++/* { dg-options "-O2 -mfunction-return=keep -mindirect-branch=thunk -fcheck-pointer-bounds -mmpx -fpic -fno-plt" } */
+
+ void bar (char *);
+ char buf[10];
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
+index 49f27b49465..637fc3d3f4e 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-1.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
+index a1e3eb6fc74..ff9efe03fe6 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-2.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
+index 395634e7e5c..2686a5f2db4 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-3.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
+index fd3f63379a1..f07f6b214ad 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-4.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+index ba2f92b6f34..21740ac5b7f 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-5.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile { target *-*-linux* } } */
+-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
++/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+ extern void bar (void);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+index 0c5a2d472c6..a77c1f470b8 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-6.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile { target *-*-linux* } } */
+-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
++/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+ extern void bar (void);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
+index 665252327aa..e64910fd4aa 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-7.c
++++ b/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);
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
+index 68c0ff713b3..365cf2ee226 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-1.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
+index e2da1fcb683..72646a4960b 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-2.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
+index 244fec708d6..f48945e3dfc 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-3.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
+index 107ebe32f54..4b1d558fc4e 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-4.c
++++ b/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);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+index 17b04ef2229..0f687c3b027 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-5.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile { target *-*-linux* } } */
+-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
++/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+ extern void bar (void);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+index d9eb11285aa..b27c6fc96a2 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
++++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-6.c
+@@ -1,5 +1,5 @@
+ /* { dg-do compile { target *-*-linux* } } */
+-/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
++/* { dg-options "-O2 -mfunction-return=keep -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+ extern void bar (void);
+
+diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
+index d02b1dcb1b9..2c496492eaa 100644
+--- a/gcc/testsuite/gcc.target/i386/indirect-thunk-inline-7.c
++++ b/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);
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-1.c b/gcc/testsuite/gcc.target/i386/ret-thunk-1.c
+new file mode 100644
+index 00000000000..7223f67ba5e
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-10.c b/gcc/testsuite/gcc.target/i386/ret-thunk-10.c
+new file mode 100644
+index 00000000000..1630e2fa2b5
+--- /dev/null
++++ b/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 } } } } */
++/* { 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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-11.c b/gcc/testsuite/gcc.target/i386/ret-thunk-11.c
+new file mode 100644
+index 00000000000..876159cf783
+--- /dev/null
++++ b/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 } } } } */
++/* { 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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-12.c b/gcc/testsuite/gcc.target/i386/ret-thunk-12.c
+new file mode 100644
+index 00000000000..01b0a02f80b
+--- /dev/null
++++ b/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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-13.c b/gcc/testsuite/gcc.target/i386/ret-thunk-13.c
+new file mode 100644
+index 00000000000..e028c2b6a99
+--- /dev/null
++++ b/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 } } } } */
++/* { 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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-14.c b/gcc/testsuite/gcc.target/i386/ret-thunk-14.c
+new file mode 100644
+index 00000000000..c14ee3ae4c0
+--- /dev/null
++++ b/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 } } } } */
++/* { 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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-15.c b/gcc/testsuite/gcc.target/i386/ret-thunk-15.c
+new file mode 100644
+index 00000000000..2f21e138ec2
+--- /dev/null
++++ b/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 } } } } */
++/* { 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 } } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-16.c b/gcc/testsuite/gcc.target/i386/ret-thunk-16.c
+new file mode 100644
+index 00000000000..a16cad16aaa
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-2.c b/gcc/testsuite/gcc.target/i386/ret-thunk-2.c
+new file mode 100644
+index 00000000000..c6659e3ad09
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-3.c b/gcc/testsuite/gcc.target/i386/ret-thunk-3.c
+new file mode 100644
+index 00000000000..0f7f388f459
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-4.c b/gcc/testsuite/gcc.target/i386/ret-thunk-4.c
+new file mode 100644
+index 00000000000..9ae37e835a0
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-5.c b/gcc/testsuite/gcc.target/i386/ret-thunk-5.c
+new file mode 100644
+index 00000000000..4bd0d2a27bc
+--- /dev/null
++++ b/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} } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-6.c b/gcc/testsuite/gcc.target/i386/ret-thunk-6.c
+new file mode 100644
+index 00000000000..053841f6f7d
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-7.c b/gcc/testsuite/gcc.target/i386/ret-thunk-7.c
+new file mode 100644
+index 00000000000..262e6780112
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-8.c b/gcc/testsuite/gcc.target/i386/ret-thunk-8.c
+new file mode 100644
+index 00000000000..c1658e96673
+--- /dev/null
++++ b/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" } } */
+diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-9.c b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+new file mode 100644
+index 00000000000..f6ccad98da7
+--- /dev/null
++++ b/gcc/testsuite/gcc.target/i386/ret-thunk-9.c
+@@ -0,0 +1,25 @@
++/* { 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-not "__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 } } } } */
++/* { 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 } } } */
+
+commit 443f274c129f9a4da28d1f796744d1179ec2fdc4
+Author: H.J. Lu <hjl.tools at gmail.com>
+Date: Sat Jan 6 22:29:56 2018 -0800
+
+ 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/
+
+ * 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/
+
+ * 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.
+
+diff --git a/gcc/config/i386/constraints.md b/gcc/config/i386/constraints.md
+index 38d604fdace..697caf704dd 100644
+--- a/gcc/config/i386/constraints.md
++++ b/gcc/config/i386/constraints.md
+@@ -198,16 +198,20 @@
+
+ (define_constraint "Bs"
+ "@internal Sibcall memory operand."
+- (ior (and (not (match_test "TARGET_X32"))
++ (ior (and (not (match_test "TARGET_X32
++ || ix86_indirect_branch_thunk_register"))
+ (match_operand 0 "sibcall_memory_operand"))
+- (and (match_test "TARGET_X32 && Pmode == DImode")
++ (and (match_test "TARGET_X32 && Pmode == DImode
++ && !ix86_indirect_branch_thunk_register")
+ (match_operand 0 "GOT_memory_operand"))))
+
+ (define_constraint "Bw"
+ "@internal Call memory operand."
+- (ior (and (not (match_test "TARGET_X32"))
++ (ior (and (not (match_test "TARGET_X32
++ || ix86_indirect_branch_thunk_register"))
+ (match_operand 0 "memory_operand"))
+- (and (match_test "TARGET_X32 && Pmode == DImode")
++ (and (match_test "TARGET_X32 && Pmode == DImode
++ && !ix86_indirect_branch_thunk_register")
+ (match_operand 0 "GOT_memory_operand"))))
+
+ (define_constraint "Bz"
+diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
+index 3ac79ff6ee6..02ab4d45fa6 100644
+--- a/gcc/config/i386/i386.md
++++ b/gcc/config/i386/i386.md
+@@ -11623,7 +11623,7 @@
+ [(set (pc) (match_operand 0 "indirect_branch_operand"))]
+ ""
+ {
+- if (TARGET_X32)
++ if (TARGET_X32 || ix86_indirect_branch_thunk_register)
+ operands[0] = convert_memory_address (word_mode, operands[0]);
+ cfun->machine->has_local_indirect_jump = true;
+ })
+@@ -11677,7 +11677,7 @@
+ OPTAB_DIRECT);
+ }
+
+- if (TARGET_X32)
++ if (TARGET_X32 || ix86_indirect_branch_thunk_register)
+ operands[0] = convert_memory_address (word_mode, operands[0]);
+ cfun->machine->has_local_indirect_jump = true;
+ })
+@@ -11869,7 +11869,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_thunk_register"
+ "* return ix86_output_call_insn (insn, operands[0]);"
+ [(set_attr "type" "call")])
+
+@@ -11878,7 +11878,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_thunk_register
++ && SIBLING_CALL_P (peep2_next_insn (1))
+ && !reg_mentioned_p (operands[0],
+ CALL_INSN_FUNCTION_USAGE (peep2_next_insn (1)))"
+ [(parallel [(call (mem:QI (match_dup 1))
+@@ -11891,7 +11893,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_thunk_register
++ && SIBLING_CALL_P (peep2_next_insn (2))
+ && !reg_mentioned_p (operands[0],
+ CALL_INSN_FUNCTION_USAGE (peep2_next_insn (2)))"
+ [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)
+@@ -11913,7 +11917,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)
+@@ -11933,7 +11937,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)
+@@ -11987,7 +11991,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_thunk_register
++ && peep2_reg_dead_p (2, operands[0])"
+ [(set (pc) (match_dup 1))])
+
<Skipped 1030 lines>
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/gcc.git/commitdiff/95542190f2ac42f1619b8dd94026fe8b720a8687
More information about the pld-cvs-commit
mailing list